From ac59d4032c8a140ef59aa4a4c79f4660f420d65b Mon Sep 17 00:00:00 2001 From: Stubbjax Date: Mon, 17 Nov 2025 16:16:41 +1100 Subject: [PATCH 1/2] bugfix: Builders now resume their task after having been disabled --- .../Include/GameLogic/Module/DozerAIUpdate.h | 4 ++++ .../Include/GameLogic/Module/WorkerAIUpdate.h | 3 +++ .../Object/Update/AIUpdate/DozerAIUpdate.cpp | 12 ++++++++++++ .../Object/Update/AIUpdate/WorkerAIUpdate.cpp | 12 ++++++++++++ .../Include/GameLogic/Module/DozerAIUpdate.h | 4 ++++ .../Include/GameLogic/Module/WorkerAIUpdate.h | 3 +++ .../Source/GameLogic/Object/Object.cpp | 17 +++++++++++++---- .../Object/Update/AIUpdate/DozerAIUpdate.cpp | 12 ++++++++++++ .../Object/Update/AIUpdate/WorkerAIUpdate.cpp | 12 ++++++++++++ 9 files changed, 75 insertions(+), 4 deletions(-) diff --git a/Generals/Code/GameEngine/Include/GameLogic/Module/DozerAIUpdate.h b/Generals/Code/GameEngine/Include/GameLogic/Module/DozerAIUpdate.h index 1488eecb0d..b41311bd72 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 @@ -282,6 +284,8 @@ class DozerAIUpdate : public AIUpdateInterface, public DozerAIInterface 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 a124c77aff..cc8726d970 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 @@ -218,6 +219,8 @@ class WorkerAIUpdate : public AIUpdateInterface, public DozerAIInterface, public 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 d5371bd9a3..c2b963455c 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cpp @@ -2037,6 +2037,15 @@ 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)); +} + //------------------------------------------------------------------------------------------------- /** Is there a given task waiting to be done */ //------------------------------------------------------------------------------------------------- @@ -2115,6 +2124,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; 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 7b2dfd9eec..3f9e2f8066 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/WorkerAIUpdate.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/WorkerAIUpdate.cpp @@ -695,6 +695,15 @@ 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)); +} + //------------------------------------------------------------------------------------------------- /** Is there a given task waiting to be done */ //------------------------------------------------------------------------------------------------- @@ -773,6 +782,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; diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/DozerAIUpdate.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/DozerAIUpdate.h index 2d5d4bd399..bb3c891930 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 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 @@ -282,6 +284,8 @@ class DozerAIUpdate : public AIUpdateInterface, public DozerAIInterface 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 afc308ccf6..099638a89e 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 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 @@ -223,6 +224,8 @@ class WorkerAIUpdate : public AIUpdateInterface, public DozerAIInterface, public 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 df4f4c66a2..fede2ffa04 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp @@ -3809,11 +3809,20 @@ void Object::onDisabledEdge(Bool becomingDisabled) (*module)->onDisabledEdge( becomingDisabled ); DozerAIInterface *dozerAI = getAI() ? getAI()->getDozerAIInterface() : NULL; - 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 + 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 ea9d64c9a2..f84cd0a92c 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cpp @@ -2042,6 +2042,15 @@ 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)); +} + //------------------------------------------------------------------------------------------------- /** Is there a given task waiting to be done */ //------------------------------------------------------------------------------------------------- @@ -2120,6 +2129,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; 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 a3bdd0628a..e3dbf7b3ba 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/WorkerAIUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/WorkerAIUpdate.cpp @@ -695,6 +695,15 @@ 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)); +} + //------------------------------------------------------------------------------------------------- /** Is there a given task waiting to be done */ //------------------------------------------------------------------------------------------------- @@ -773,6 +782,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; From 1155347505d0cb41b587918ca9932362af629b0d Mon Sep 17 00:00:00 2001 From: Stubbjax Date: Mon, 17 Nov 2025 19:26:56 +1100 Subject: [PATCH 2/2] tweak: Add new data to the xfer logic --- .../Object/Update/AIUpdate/DozerAIUpdate.cpp | 12 +++++++++++- .../Object/Update/AIUpdate/WorkerAIUpdate.cpp | 12 +++++++++++- .../Object/Update/AIUpdate/DozerAIUpdate.cpp | 12 +++++++++++- .../Object/Update/AIUpdate/WorkerAIUpdate.cpp | 12 +++++++++++- 4 files changed, 44 insertions(+), 4 deletions(-) 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 c2b963455c..64e3c5b2bf 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cpp @@ -2450,7 +2450,11 @@ void DozerAIUpdate::crc( Xfer *xfer ) 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 ); @@ -2471,6 +2475,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 3f9e2f8066..c6d723dfa8 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/WorkerAIUpdate.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/WorkerAIUpdate.cpp @@ -1417,7 +1417,11 @@ void WorkerAIUpdate::crc( Xfer *xfer ) // ------------------------------------------------------------------------------------------------ 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 ); @@ -1441,6 +1445,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/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cpp index f84cd0a92c..f9a0be58b4 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cpp @@ -2462,7 +2462,11 @@ void DozerAIUpdate::crc( Xfer *xfer ) 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 ); @@ -2483,6 +2487,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 e3dbf7b3ba..13ca49327c 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/WorkerAIUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/WorkerAIUpdate.cpp @@ -1427,7 +1427,11 @@ void WorkerAIUpdate::crc( Xfer *xfer ) // ------------------------------------------------------------------------------------------------ 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 ); @@ -1451,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) {