diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..04bc145 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +_* diff --git a/lua/ge/extensions/career/retrievedamaged.lua b/lua/ge/extensions/career/vehicleRetrieval.lua similarity index 52% rename from lua/ge/extensions/career/retrievedamaged.lua rename to lua/ge/extensions/career/vehicleRetrieval.lua index 56ef5d0..35fde5e 100644 --- a/lua/ge/extensions/career/retrievedamaged.lua +++ b/lua/ge/extensions/career/vehicleRetrieval.lua @@ -15,57 +15,10 @@ local M = {} -M.dependencies = { 'career_career', 'career_modules_computer', 'career_modules_inventory', 'career_modules_payment', - 'career_modules_playerAttributes' } +M.dependencies = { 'career_modules_inventory', 'freeroam_facilities', "career_vehicleSaveSystem" } -local function getVehicleSavesFolder(savePath) - return savePath .. "/career/_vehicle_saves" -end - -local function getInventoryIdFolder(vehicleSavesPath, inventoryId) - return vehicleSavesPath .. "/" .. inventoryId -end - -local function getInventoryIdFile(inventoryIdPath) - return inventoryIdPath .. "/save.json" -end - -local function createFolderIfNeeded(path) - if not FS:directoryExists(path) then - FS:directoryCreate(path) - end -end - -local function prepareFolders(inventoryId) - local _, savePath = career_saveSystem.getCurrentSaveSlot() - local vehicleSavesFolder = getVehicleSavesFolder(savePath) - createFolderIfNeeded(vehicleSavesFolder) - - local inventoryIdFolder = getInventoryIdFolder(vehicleSavesFolder, inventoryId) - createFolderIfNeeded(inventoryIdFolder) - - return getInventoryIdFile(inventoryIdFolder) -end - -local function onSave(inventoryId) - local fileName = prepareFolders(inventoryId) - - local vehicleId = career_modules_inventory.getVehicleIdFromInventoryId(inventoryId) - if vehicleId then - local object = be:getObjectByID(vehicleId) - object:queueLuaCommand('beamstate.save("' .. fileName .. '")') - end -end - -local function onLoad(inventoryId) - local fileName = prepareFolders(inventoryId) - - local vehicleId = career_modules_inventory.getVehicleIdFromInventoryId(inventoryId) - if vehicleId then - local object = be:getObjectByID(vehicleId) - object:queueLuaCommand('beamstate.load("' .. fileName .. '")') - end -end +local career_modules_inventory_removeVehicleObject +local vehicleObjectsToRemove = {} local function spawnVehicle(inventoryId, callback) if career_modules_inventory.getVehicleIdFromInventoryId(inventoryId) then @@ -75,20 +28,17 @@ local function spawnVehicle(inventoryId, callback) end end -local function retrieve_favourite() +local function Retrieve() local fav_veh_id = career_modules_inventory.getFavoriteVehicle() extensions.core_jobsystem.create( function(job) - onSave(fav_veh_id) - job.sleep(1) spawnVehicle(fav_veh_id, function() local veh = be:getObjectByID(career_modules_inventory.getVehicleIdFromInventoryId(fav_veh_id)) local location = { pos = veh:getPosition(), rot = quat(0, 0, 1, 0) * quat(veh:getRefNodeRotation()) } local garage = career_modules_inventory.getClosestGarage(location.pos) - - onLoad(fav_veh_id) job.sleep(1) + career_vehicleSaveSystem.LoadVehicle(fav_veh_id) freeroam_facilities.teleportToGarage(garage.id, veh, false) end) end @@ -100,7 +50,7 @@ local function onComputerAddFunctions(menuData, computerFunctions) local computerFunctionData = { id = "retrieve_damaged", label = "Retrieve Favourite Damaged", - callback = retrieve_favourite, + callback = Retrieve, order = 1 } if menuData.tutorialPartShoppingActive or menuData.tutorialTuningActive then @@ -111,14 +61,21 @@ local function onComputerAddFunctions(menuData, computerFunctions) end end -local function onSaveCurrentSaveSlot(currentSavePath, oldSaveDate, vehiclesThumbnailUpdate) - local vs = career_modules_inventory.getVehicles() - for id, _ in pairs(vs) do - onSave(id) - end +local function RemoveVehicleObject(inventoryId) + table.insert(vehicleObjectsToRemove, inventoryId) + career_vehicleSaveSystem.QueueVehicleToSave(inventoryId) + career_vehicleSaveSystem.SaveVehicle(inventoryId) + career_vehicleSaveSystem.CheckSavedAsync(function() + career_modules_inventory_removeVehicleObject(inventoryId) + end) +end + +local function onCareerActive() + career_modules_inventory_removeVehicleObject = career_modules_inventory.removeVehicleObject + career_modules_inventory.removeVehicleObject = RemoveVehicleObject end -M.onSaveCurrentSaveSlot = onSaveCurrentSaveSlot M.onComputerAddFunctions = onComputerAddFunctions +M.onCareerActive = onCareerActive return M diff --git a/lua/ge/extensions/career/vehicleSaveSystem.lua b/lua/ge/extensions/career/vehicleSaveSystem.lua new file mode 100644 index 0000000..bde17cb --- /dev/null +++ b/lua/ge/extensions/career/vehicleSaveSystem.lua @@ -0,0 +1,176 @@ +-- Copyright (C) 2025 snoutie +-- Authors: snoutie (copyright@achtarmig.org) +-- This program is free software: you can redistribute it and/or modify +-- it under the terms of the GNU Affero General Public License as published +-- by the Free Software Foundation, either version 3 of the License, or +-- (at your option) any later version. + +-- This program is distributed in the hope that it will be useful, +-- but WITHOUT ANY WARRANTY; without even the implied warranty of +-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +-- GNU Affero General Public License for more details. + +-- You should have received a copy of the GNU Affero General Public License +-- along with this program. If not, see . + +local M = {} + +M.dependencies = { 'career_career', 'career_saveSystem', 'career_modules_inventory' } + +local extensionName = "career_vehicleSaveSystem" +local vehicleSaves_temp = "/temp/career/vehicle_saves" + +local function CreateDirectories(dir) + local p = path.dirname(dir) + if not FS:directoryExists(p) then + CreateDirectories(string.sub(p, 1, #p - 1)) + end + FS:directoryCreate(dir) +end + +local function ClearDirectory(dir) + local files = FS:findFiles(dir .. "/", '*', 0, false, true) + for i = 1, tableSize(files) do + FS:remove(files[i]) + end +end + +local function CopySaves(from, to) + local save_folders = FS:directoryList(from, false, true) + for i = 1, tableSize(save_folders) do + local folder_dir, folder_name, _ = path.split(save_folders[i]) + FS:directoryCreate(to .. "/" .. folder_name) + local save_files = FS:findFiles(folder_dir .. folder_name .. "/", '*.json', 0, false, false) + for n = 1, tableSize(save_files) do + local file_dir, file_name, file_ext = path.split(save_files[n]) + FS:remove(to .. "/" .. folder_name .. "/" .. file_name) + FS:copyFile(file_dir .. file_name, to .. "/" .. folder_name .. "/" .. file_name) + end + end +end + +local function LoadFiles(from) + ClearDirectory(vehicleSaves_temp) + CopySaves(from, vehicleSaves_temp) +end + +local function SaveFiles(to) + CopySaves(vehicleSaves_temp, to) +end + +local function GetVehicleSaveFile(root, inventoryId) + local path = root .. "/" .. inventoryId + CreateDirectories(path) + + return path .. "/save.json" +end + +local function QueueVehicleToSave(inventoryId) + M.queuedVehicleSaves[inventoryId] = true +end + +local function QueuedVehicleSaved(inventoryId) + log("I", "saving", "saved vehicle " .. inventoryId) + table.remove(M.queuedVehicleSaves, inventoryId) +end + +local function VehiclesSaved() + if next(M.queuedVehicleSaves) == nil then + log('I', 'saving', 'all vehicles saved') + return true + else + log('I', 'saving', 'still saving vehicles') + end + return false +end + +local function CheckSavedAsync(callback) + extensions.core_jobsystem.create( + function(job) + while true do + if VehiclesSaved() then + break + end + job.sleep(0.1) + end + if callback then + callback() + end + end) +end + +local function SaveVehicle(inventoryId) + local saveFile = GetVehicleSaveFile(vehicleSaves_temp, inventoryId) + + local vehicleId = career_modules_inventory.getVehicleIdFromInventoryId(inventoryId) + if vehicleId then + log('I', 'saving', 'saving vehicle ' .. inventoryId .. " to " .. saveFile) + local object = be:getObjectByID(vehicleId) + object:queueLuaCommand("beamstate.save(\"" .. + saveFile .. + "\"); obj:queueGameEngineLua('career_vehicleSaveSystem.QueuedVehicleSaved(\"" .. inventoryId .. "\")')") + return true + else + return false + end +end + +local function LoadVehicle(inventoryId) + local saveFile = GetVehicleSaveFile(vehicleSaves_temp, inventoryId) + + local vehicleId = career_modules_inventory.getVehicleIdFromInventoryId(inventoryId) + if vehicleId then + log("I", "loading", "loading vehicle from " .. saveFile) + local object = be:getObjectByID(vehicleId) + object:queueLuaCommand("beamstate.load(\"" .. + saveFile .. "\")") + end +end + +local function onSaveCurrentSaveSlotAsyncStart() + career_saveSystem.registerAsyncSaveExtension(extensionName) +end + +local function onSaveCurrentSaveSlot(currentSavePath, oldSaveDate, vehiclesThumbnailUpdate) + local vehicles = career_modules_inventory.getVehicles() + + for id, _ in pairs(vehicles) do + QueueVehicleToSave(id) + end + + for id, _ in pairs(M.queuedVehicleSaves) do + if not SaveVehicle(id) then + QueuedVehicleSaved(id) + end + end + CheckSavedAsync(function() + SaveFiles(currentSavePath .. "/career/vehicle_saves") + career_saveSystem.asyncSaveExtensionFinished(extensionName) + end) +end + +local function onCareerActive() + local _, saveSlot = career_saveSystem.getCurrentSaveSlot() + local vehicleSaves_saveSlot = saveSlot .. "/career/vehicle_saves" + + CreateDirectories(vehicleSaves_saveSlot) + CreateDirectories(vehicleSaves_temp) + + LoadFiles(vehicleSaves_saveSlot) +end + +M.queuedVehicleSaves = {} + +M.SaveVehicle = SaveVehicle +M.LoadVehicle = LoadVehicle + +M.CheckSavedAsync = CheckSavedAsync + +M.QueueVehicleToSave = QueueVehicleToSave +M.QueuedVehicleSaved = QueuedVehicleSaved + +M.onSaveCurrentSaveSlotAsyncStart = onSaveCurrentSaveSlotAsyncStart +M.onSaveCurrentSaveSlot = onSaveCurrentSaveSlot +M.onCareerActive = onCareerActive + +return M diff --git a/scripts/retrievedamagedcareer/modScript.lua b/scripts/retrievedamagedcareer/modScript.lua index 2717b23..1fe623b 100644 --- a/scripts/retrievedamagedcareer/modScript.lua +++ b/scripts/retrievedamagedcareer/modScript.lua @@ -15,12 +15,14 @@ local M = {} -M.dependencies = { 'career_career', 'career_modules_payment', 'career_modules_playerAttributes' } +M.dependencies = { 'career_career' } -extensions.load("career_retrievedamaged") +extensions.load("career_vehicleSaveSystem") +extensions.load("career_vehicleRetrieval") M.onInit = function() setExtensionUnloadMode(M, "manual") end + return M