diff --git a/Generals/Code/GameEngine/Include/GameLogic/Module/DozerAIUpdate.h b/Generals/Code/GameEngine/Include/GameLogic/Module/DozerAIUpdate.h index 63b325b5818..69ed020e4e4 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/Module/DozerAIUpdate.h +++ b/Generals/Code/GameEngine/Include/GameLogic/Module/DozerAIUpdate.h @@ -140,6 +140,7 @@ class DozerAIInterface // task actions virtual void newTask( DozerTask task, Object *target ) = 0; ///< set a desire to do the requrested task virtual void cancelTask( DozerTask task ) = 0; ///< cancel this task from the queue, if it's the current task the dozer will stop working on it + virtual void resumePreviousTask(void) = 0; ///< resume the previous task if there was one // internal methods to manage behavior from within the dozer state machine virtual void internalTaskComplete( DozerTask task ) = 0; ///< set a dozer task as successfully completed @@ -239,6 +240,7 @@ class DozerAIUpdate : public AIUpdateInterface, public DozerAIInterface // task actions virtual void newTask( DozerTask task, Object *target ); ///< set a desire to do the requrested task virtual void cancelTask( DozerTask task ); ///< cancel this task from the queue, if it's the current task the dozer will stop working on it + virtual void resumePreviousTask(void); ///< resume the previous task if there was one // internal methods to manage behavior from within the dozer state machine virtual void internalTaskComplete( DozerTask task ); ///< set a dozer task as successfully completed @@ -276,12 +278,20 @@ class DozerAIUpdate : public AIUpdateInterface, public DozerAIInterface struct DozerTaskInfo { + DozerTaskInfo() + { + m_targetObjectID = INVALID_ID; + m_taskOrderFrame = 0; + } + ObjectID m_targetObjectID; ///< target object ID of task UnsignedInt m_taskOrderFrame; ///< logic frame we decided we wanted to do this task } m_task[ DOZER_NUM_TASKS ]; ///< tasks we want to do indexed by DozerTask DozerPrimaryStateMachine *m_dozerMachine; ///< the custom state machine for Dozer behavior DozerTask m_currentTask; ///< current task the dozer is attending to (if any) + DozerTask m_previousTask; ///< previous task the dozer was attending to (if any) + DozerTaskInfo m_previousTaskInfo; ///< info on the previous task the dozer was attending to (if any) AudioEventRTS m_buildingSound; ///< sound is pulled from the object we are building! Bool m_isRebuild; ///< is this a rebuild of a previous building? diff --git a/Generals/Code/GameEngine/Include/GameLogic/Module/WorkerAIUpdate.h b/Generals/Code/GameEngine/Include/GameLogic/Module/WorkerAIUpdate.h index 67f2f9e4a9c..d042a4c4fb2 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/Module/WorkerAIUpdate.h +++ b/Generals/Code/GameEngine/Include/GameLogic/Module/WorkerAIUpdate.h @@ -155,6 +155,7 @@ class WorkerAIUpdate : public AIUpdateInterface, public DozerAIInterface, public // task actions virtual void newTask( DozerTask task, Object* target ); ///< set a desire to do the requrested task virtual void cancelTask( DozerTask task ); ///< cancel this task from the queue, if it's the current task the dozer will stop working on it + virtual void resumePreviousTask(void); ///< resume the previous task if there was one // internal methods to manage behavior from within the dozer state machine virtual void internalTaskComplete( DozerTask task ); ///< set a dozer task as successfully completed @@ -212,12 +213,20 @@ class WorkerAIUpdate : public AIUpdateInterface, public DozerAIInterface, public // Dozer data struct DozerTaskInfo { + DozerTaskInfo() + { + m_targetObjectID = INVALID_ID; + m_taskOrderFrame = 0; + } + ObjectID m_targetObjectID; ///< target object ID of task UnsignedInt m_taskOrderFrame; ///< logic frame we decided we wanted to do this task } m_task[ DOZER_NUM_TASKS ]; ///< tasks we want to do indexed by DozerTask DozerTask m_currentTask; ///< current task the dozer is attending to (if any) + DozerTask m_previousTask; ///< previous task the dozer was attending to (if any) + DozerTaskInfo m_previousTaskInfo; ///< info on the previous task the dozer was attending to (if any) // // the following info array can be used if we want to have more complicated approaches diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cpp index f422f22de08..13478282f35 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cpp @@ -1452,6 +1452,7 @@ DozerAIUpdate::DozerAIUpdate( Thing *thing, const ModuleData* moduleData ) : } m_currentTask = DOZER_TASK_INVALID; + m_previousTask = DOZER_TASK_INVALID; m_buildSubTask = DOZER_SELECT_BUILD_DOCK_LOCATION; // irrelavant, but I want non-garbage value @@ -2040,6 +2041,19 @@ void DozerAIUpdate::cancelTask( DozerTask task ) } +//------------------------------------------------------------------------------------------------- +/** Attempt to resume the previous task */ +//------------------------------------------------------------------------------------------------- +void DozerAIUpdate::resumePreviousTask(void) +{ + if (m_previousTask != DOZER_TASK_INVALID) + { + newTask(m_previousTask, TheGameLogic->findObjectByID(m_previousTaskInfo.m_targetObjectID)); + m_previousTask = DOZER_TASK_INVALID; + m_previousTaskInfo = DozerTaskInfo(); + } +} + //------------------------------------------------------------------------------------------------- /** Is there a given task waiting to be done */ //------------------------------------------------------------------------------------------------- @@ -2096,6 +2110,9 @@ void DozerAIUpdate::internalTaskComplete( DozerTask task ) m_task[ task ].m_targetObjectID = INVALID_ID; m_task[ task ].m_taskOrderFrame = 0; + m_previousTask = DOZER_TASK_INVALID; + m_previousTaskInfo = DozerTaskInfo(); + // remove dock point info for this task for( Int i = 0; i < DOZER_NUM_DOCK_POINTS; i++ ) m_dockPoint[ task ][ i ].valid = FALSE; @@ -2118,6 +2135,9 @@ void DozerAIUpdate::internalCancelTask( DozerTask task ) // call the single method that gets called for completing and canceling tasks internalTaskCompleteOrCancelled( task ); + m_previousTask = task; + m_previousTaskInfo = m_task[task]; + // remove the info for this task m_task[ task ].m_targetObjectID = INVALID_ID; m_task[ task ].m_taskOrderFrame = 0; @@ -2436,12 +2456,18 @@ void DozerAIUpdate::crc( Xfer *xfer ) // ------------------------------------------------------------------------------------------------ /** Xfer method * Version Info: - * 1: Initial version */ + * 1: Initial version + * 2: TheSuperHackers @tweak Stubbjax 17/11/2025 Save the dozer's previous task + */ // ------------------------------------------------------------------------------------------------ void DozerAIUpdate::xfer( Xfer *xfer ) { // version - XferVersion currentVersion = 1; +#if RETAIL_COMPATIBLE_XFER_SAVE + XferVersion currentVersion = 1; +#else + XferVersion currentVersion = 2; +#endif XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); @@ -2462,6 +2488,12 @@ void DozerAIUpdate::xfer( Xfer *xfer ) xfer->xferSnapshot(m_dozerMachine); xfer->xferUser(&m_currentTask, sizeof(m_currentTask)); + if (currentVersion >= 2) + { + xfer->xferUser(&m_previousTask, sizeof(m_previousTask)); + xfer->xferUser(&m_previousTaskInfo, sizeof(m_previousTaskInfo)); + } + Int dockPoints = DOZER_NUM_DOCK_POINTS; xfer->xferInt(&dockPoints); if (dockPoints!=DOZER_NUM_DOCK_POINTS) { diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/WorkerAIUpdate.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/WorkerAIUpdate.cpp index bd67b782cd2..5615f8d7794 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/WorkerAIUpdate.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/WorkerAIUpdate.cpp @@ -100,6 +100,7 @@ WorkerAIUpdate::WorkerAIUpdate( Thing *thing, const ModuleData* moduleData ) : } } m_currentTask = DOZER_TASK_INVALID; + m_previousTask = DOZER_TASK_INVALID; m_buildSubTask = DOZER_SELECT_BUILD_DOCK_LOCATION; // irrelavant, but I want non-garbage value m_supplyTruckStateMachine = nullptr; @@ -695,6 +696,19 @@ void WorkerAIUpdate::cancelTask( DozerTask task ) } +//------------------------------------------------------------------------------------------------- +/** Attempt to resume the previous task */ +//------------------------------------------------------------------------------------------------- +void WorkerAIUpdate::resumePreviousTask(void) +{ + if (m_previousTask != DOZER_TASK_INVALID) + { + newTask(m_previousTask, TheGameLogic->findObjectByID(m_previousTaskInfo.m_targetObjectID)); + m_previousTask = DOZER_TASK_INVALID; + m_previousTaskInfo = DozerTaskInfo(); + } +} + //------------------------------------------------------------------------------------------------- /** Is there a given task waiting to be done */ //------------------------------------------------------------------------------------------------- @@ -751,6 +765,9 @@ void WorkerAIUpdate::internalTaskComplete( DozerTask task ) m_task[ task ].m_targetObjectID = INVALID_ID; m_task[ task ].m_taskOrderFrame = 0; + m_previousTask = DOZER_TASK_INVALID; + m_previousTaskInfo = DozerTaskInfo(); + // remove dock point info for this task for( Int i = 0; i < DOZER_NUM_DOCK_POINTS; i++ ) m_dockPoint[ task ][ i ].valid = FALSE; @@ -773,6 +790,9 @@ void WorkerAIUpdate::internalCancelTask( DozerTask task ) // call the single method that gets called for completing and canceling tasks internalTaskCompleteOrCancelled( task ); + m_previousTask = task; + m_previousTaskInfo = m_task[task]; + // remove the info for this task m_task[ task ].m_targetObjectID = INVALID_ID; m_task[ task ].m_taskOrderFrame = 0; @@ -1401,11 +1421,17 @@ void WorkerAIUpdate::crc( Xfer *xfer ) // ------------------------------------------------------------------------------------------------ /** Xfer method * Version Info: - * 1: Initial version */ + * 1: Initial version + * 2: TheSuperHackers @tweak Stubbjax 17/11/2025 Save the worker's previous task + */ // ------------------------------------------------------------------------------------------------ void WorkerAIUpdate::xfer( Xfer *xfer ) { - XferVersion currentVersion = 1; +#if RETAIL_COMPATIBLE_XFER_SAVE + XferVersion currentVersion = 1; +#else + XferVersion currentVersion = 2; +#endif XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); @@ -1429,6 +1455,12 @@ void WorkerAIUpdate::xfer( Xfer *xfer ) xfer->xferSnapshot(m_dozerMachine); xfer->xferUser(&m_currentTask, sizeof(m_currentTask)); + if (currentVersion >= 2) + { + xfer->xferUser(&m_previousTask, sizeof(m_previousTask)); + xfer->xferUser(&m_previousTaskInfo, sizeof(m_previousTaskInfo)); + } + Int dockPoints = DOZER_NUM_DOCK_POINTS; xfer->xferInt(&dockPoints); if (dockPoints!=DOZER_NUM_DOCK_POINTS) { diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/DozerAIUpdate.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/DozerAIUpdate.h index dbf4e87eee8..20aff0af941 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/DozerAIUpdate.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/DozerAIUpdate.h @@ -140,6 +140,7 @@ class DozerAIInterface // task actions virtual void newTask( DozerTask task, Object *target ) = 0; ///< set a desire to do the requested task virtual void cancelTask( DozerTask task ) = 0; ///< cancel this task from the queue, if it's the current task the dozer will stop working on it + virtual void resumePreviousTask(void) = 0; ///< resume the previous task if there was one // internal methods to manage behavior from within the dozer state machine virtual void internalTaskComplete( DozerTask task ) = 0; ///< set a dozer task as successfully completed @@ -239,6 +240,7 @@ class DozerAIUpdate : public AIUpdateInterface, public DozerAIInterface // task actions virtual void newTask( DozerTask task, Object *target ); ///< set a desire to do the requested task virtual void cancelTask( DozerTask task ); ///< cancel this task from the queue, if it's the current task the dozer will stop working on it + virtual void resumePreviousTask(void); ///< resume the previous task if there was one // internal methods to manage behavior from within the dozer state machine virtual void internalTaskComplete( DozerTask task ); ///< set a dozer task as successfully completed @@ -276,12 +278,20 @@ class DozerAIUpdate : public AIUpdateInterface, public DozerAIInterface struct DozerTaskInfo { + DozerTaskInfo() + { + m_targetObjectID = INVALID_ID; + m_taskOrderFrame = 0; + } + ObjectID m_targetObjectID; ///< target object ID of task UnsignedInt m_taskOrderFrame; ///< logic frame we decided we wanted to do this task } m_task[ DOZER_NUM_TASKS ]; ///< tasks we want to do indexed by DozerTask DozerPrimaryStateMachine *m_dozerMachine; ///< the custom state machine for Dozer behavior DozerTask m_currentTask; ///< current task the dozer is attending to (if any) + DozerTask m_previousTask; ///< previous task the dozer was attending to (if any) + DozerTaskInfo m_previousTaskInfo; ///< info on the previous task the dozer was attending to (if any) AudioEventRTS m_buildingSound; ///< sound is pulled from the object we are building! Bool m_isRebuild; ///< is this a rebuild of a previous building? diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/WorkerAIUpdate.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/WorkerAIUpdate.h index 793843bff40..062e752a97c 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/WorkerAIUpdate.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/WorkerAIUpdate.h @@ -158,6 +158,7 @@ class WorkerAIUpdate : public AIUpdateInterface, public DozerAIInterface, public // task actions virtual void newTask( DozerTask task, Object* target ); ///< set a desire to do the requested task virtual void cancelTask( DozerTask task ); ///< cancel this task from the queue, if it's the current task the dozer will stop working on it + virtual void resumePreviousTask(void); ///< resume the previous task if there was one // internal methods to manage behavior from within the dozer state machine virtual void internalTaskComplete( DozerTask task ); ///< set a dozer task as successfully completed @@ -217,12 +218,20 @@ class WorkerAIUpdate : public AIUpdateInterface, public DozerAIInterface, public // Dozer data struct DozerTaskInfo { + DozerTaskInfo() + { + m_targetObjectID = INVALID_ID; + m_taskOrderFrame = 0; + } + ObjectID m_targetObjectID; ///< target object ID of task UnsignedInt m_taskOrderFrame; ///< logic frame we decided we wanted to do this task } m_task[ DOZER_NUM_TASKS ]; ///< tasks we want to do indexed by DozerTask DozerTask m_currentTask; ///< current task the dozer is attending to (if any) + DozerTask m_previousTask; ///< previous task the dozer was attending to (if any) + DozerTaskInfo m_previousTaskInfo; ///< info on the previous task the dozer was attending to (if any) // // the following info array can be used if we want to have more complicated approaches diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp index e11db7d4c53..67a5765e2d1 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp @@ -3853,11 +3853,21 @@ void Object::onDisabledEdge(Bool becomingDisabled) (*module)->onDisabledEdge( becomingDisabled ); DozerAIInterface *dozerAI = getAI() ? getAI()->getDozerAIInterface() : nullptr; - if( becomingDisabled && dozerAI ) + if (dozerAI) { - // Have to say goodbye to the thing we might be building or repairing so someone else can do it. - if( dozerAI->getCurrentTask() != DOZER_TASK_INVALID ) - dozerAI->cancelTask( dozerAI->getCurrentTask() ); + if (becomingDisabled) + { + // Have to say goodbye to the thing we might be building or repairing so someone else can do it. + if (dozerAI->getCurrentTask() != DOZER_TASK_INVALID) + dozerAI->cancelTask(dozerAI->getCurrentTask()); + } + else + { +#if !RETAIL_COMPATIBLE_CRC + // TheSuperHackers @bugfix Stubbjax 17/11/2025 Resume previous task when re-enabled. + dozerAI->resumePreviousTask(); +#endif + } } Player* controller = getControllingPlayer(); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cpp index 9b723e932ef..fb5da2d4373 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cpp @@ -1457,6 +1457,7 @@ DozerAIUpdate::DozerAIUpdate( Thing *thing, const ModuleData* moduleData ) : } m_currentTask = DOZER_TASK_INVALID; + m_previousTask = DOZER_TASK_INVALID; m_buildSubTask = DOZER_SELECT_BUILD_DOCK_LOCATION; // irrelevant, but I want non-garbage value @@ -2045,6 +2046,19 @@ void DozerAIUpdate::cancelTask( DozerTask task ) } +//------------------------------------------------------------------------------------------------- +/** Attempt to resume the previous task */ +//------------------------------------------------------------------------------------------------- +void DozerAIUpdate::resumePreviousTask(void) +{ + if (m_previousTask != DOZER_TASK_INVALID) + { + newTask(m_previousTask, TheGameLogic->findObjectByID(m_previousTaskInfo.m_targetObjectID)); + m_previousTask = DOZER_TASK_INVALID; + m_previousTaskInfo = DozerTaskInfo(); + } +} + //------------------------------------------------------------------------------------------------- /** Is there a given task waiting to be done */ //------------------------------------------------------------------------------------------------- @@ -2101,6 +2115,9 @@ void DozerAIUpdate::internalTaskComplete( DozerTask task ) m_task[ task ].m_targetObjectID = INVALID_ID; m_task[ task ].m_taskOrderFrame = 0; + m_previousTask = DOZER_TASK_INVALID; + m_previousTaskInfo = DozerTaskInfo(); + // remove dock point info for this task for( Int i = 0; i < DOZER_NUM_DOCK_POINTS; i++ ) m_dockPoint[ task ][ i ].valid = FALSE; @@ -2123,6 +2140,9 @@ void DozerAIUpdate::internalCancelTask( DozerTask task ) // call the single method that gets called for completing and canceling tasks internalTaskCompleteOrCancelled( task ); + m_previousTask = task; + m_previousTaskInfo = m_task[task]; + // remove the info for this task m_task[ task ].m_targetObjectID = INVALID_ID; m_task[ task ].m_taskOrderFrame = 0; @@ -2448,12 +2468,18 @@ void DozerAIUpdate::crc( Xfer *xfer ) // ------------------------------------------------------------------------------------------------ /** Xfer method * Version Info: - * 1: Initial version */ + * 1: Initial version + * 2: TheSuperHackers @tweak Stubbjax 17/11/2025 Save the dozer's previous task + */ // ------------------------------------------------------------------------------------------------ void DozerAIUpdate::xfer( Xfer *xfer ) { // version - XferVersion currentVersion = 1; +#if RETAIL_COMPATIBLE_XFER_SAVE + XferVersion currentVersion = 1; +#else + XferVersion currentVersion = 2; +#endif XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); @@ -2474,6 +2500,12 @@ void DozerAIUpdate::xfer( Xfer *xfer ) xfer->xferSnapshot(m_dozerMachine); xfer->xferUser(&m_currentTask, sizeof(m_currentTask)); + if (currentVersion >= 2) + { + xfer->xferUser(&m_previousTask, sizeof(m_previousTask)); + xfer->xferUser(&m_previousTaskInfo, sizeof(m_previousTaskInfo)); + } + Int dockPoints = DOZER_NUM_DOCK_POINTS; xfer->xferInt(&dockPoints); if (dockPoints!=DOZER_NUM_DOCK_POINTS) { diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/WorkerAIUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/WorkerAIUpdate.cpp index 8e9a753daab..5a7af64055a 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/WorkerAIUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/WorkerAIUpdate.cpp @@ -100,6 +100,7 @@ WorkerAIUpdate::WorkerAIUpdate( Thing *thing, const ModuleData* moduleData ) : } } m_currentTask = DOZER_TASK_INVALID; + m_previousTask = DOZER_TASK_INVALID; m_buildSubTask = DOZER_SELECT_BUILD_DOCK_LOCATION; // irrelevant, but I want non-garbage value m_supplyTruckStateMachine = nullptr; @@ -695,6 +696,19 @@ void WorkerAIUpdate::cancelTask( DozerTask task ) } +//------------------------------------------------------------------------------------------------- +/** Attempt to resume the previous task */ +//------------------------------------------------------------------------------------------------- +void WorkerAIUpdate::resumePreviousTask(void) +{ + if (m_previousTask != DOZER_TASK_INVALID) + { + newTask(m_previousTask, TheGameLogic->findObjectByID(m_previousTaskInfo.m_targetObjectID)); + m_previousTask = DOZER_TASK_INVALID; + m_previousTaskInfo = DozerTaskInfo(); + } +} + //------------------------------------------------------------------------------------------------- /** Is there a given task waiting to be done */ //------------------------------------------------------------------------------------------------- @@ -751,6 +765,9 @@ void WorkerAIUpdate::internalTaskComplete( DozerTask task ) m_task[ task ].m_targetObjectID = INVALID_ID; m_task[ task ].m_taskOrderFrame = 0; + m_previousTask = DOZER_TASK_INVALID; + m_previousTaskInfo = DozerTaskInfo(); + // remove dock point info for this task for( Int i = 0; i < DOZER_NUM_DOCK_POINTS; i++ ) m_dockPoint[ task ][ i ].valid = FALSE; @@ -773,6 +790,9 @@ void WorkerAIUpdate::internalCancelTask( DozerTask task ) // call the single method that gets called for completing and canceling tasks internalTaskCompleteOrCancelled( task ); + m_previousTask = task; + m_previousTaskInfo = m_task[task]; + // remove the info for this task m_task[ task ].m_targetObjectID = INVALID_ID; m_task[ task ].m_taskOrderFrame = 0; @@ -1411,11 +1431,17 @@ void WorkerAIUpdate::crc( Xfer *xfer ) // ------------------------------------------------------------------------------------------------ /** Xfer method * Version Info: - * 1: Initial version */ + * 1: Initial version + * 2: TheSuperHackers @tweak Stubbjax 17/11/2025 Save the worker's previous task + */ // ------------------------------------------------------------------------------------------------ void WorkerAIUpdate::xfer( Xfer *xfer ) { - XferVersion currentVersion = 1; +#if RETAIL_COMPATIBLE_XFER_SAVE + XferVersion currentVersion = 1; +#else + XferVersion currentVersion = 2; +#endif XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); @@ -1439,6 +1465,12 @@ void WorkerAIUpdate::xfer( Xfer *xfer ) xfer->xferSnapshot(m_dozerMachine); xfer->xferUser(&m_currentTask, sizeof(m_currentTask)); + if (currentVersion >= 2) + { + xfer->xferUser(&m_previousTask, sizeof(m_previousTask)); + xfer->xferUser(&m_previousTaskInfo, sizeof(m_previousTaskInfo)); + } + Int dockPoints = DOZER_NUM_DOCK_POINTS; xfer->xferInt(&dockPoints); if (dockPoints!=DOZER_NUM_DOCK_POINTS) {