diff --git a/Generals/Code/GameEngine/Include/Common/Module.h b/Generals/Code/GameEngine/Include/Common/Module.h index ca254e755f0..084a44f82cf 100644 --- a/Generals/Code/GameEngine/Include/Common/Module.h +++ b/Generals/Code/GameEngine/Include/Common/Module.h @@ -248,6 +248,7 @@ class ObjectModule : public Module // virtual destructor prototype defined by MemoryPoolObject virtual void onCapture( Player *oldOwner, Player *newOwner ) { } + virtual void onDisabledEdge( Bool nowDisabled ) { } protected: diff --git a/Generals/Code/GameEngine/Include/GameLogic/Module/DozerAIUpdate.h b/Generals/Code/GameEngine/Include/GameLogic/Module/DozerAIUpdate.h index 16681f0864b..b8a8dca3ead 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/Module/DozerAIUpdate.h +++ b/Generals/Code/GameEngine/Include/GameLogic/Module/DozerAIUpdate.h @@ -139,9 +139,8 @@ 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 cancelTask( DozerTask task, Bool rememberTask = false ) = 0; ///< cancel this task from the queue, if it's the current task the dozer will stop working on it. Can remember the cancelled task for resumption. virtual void cancelAllTasks() = 0; ///< cancel all tasks from the queue, if it's the current task the dozer will stop working on it - virtual void resumePreviousTask() = 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 @@ -211,6 +210,7 @@ class DozerAIUpdate : public AIUpdateInterface, public DozerAIInterface virtual const DozerAIInterface* getDozerAIInterface() const override {return this;} virtual void onDelete() override; + virtual void onDisabledEdge(Bool nowDisabled) override; // // module data methods ... this is LAME, multiple inheritance off an interface with replicated @@ -240,9 +240,8 @@ class DozerAIUpdate : public AIUpdateInterface, public DozerAIInterface // task actions virtual void newTask( DozerTask task, Object *target ) override; ///< set a desire to do the requrested task - virtual void cancelTask( DozerTask task ) override; ///< cancel this task from the queue, if it's the current task the dozer will stop working on it + virtual void cancelTask( DozerTask task, Bool rememberTask = false ) override; ///< cancel this task from the queue, if it's the current task the dozer will stop working on it virtual void cancelAllTasks() override; ///< cancel all tasks from the queue, if it's the current task the dozer will stop working on it - virtual void resumePreviousTask() override; ///< resume the previous task if there was one // internal methods to manage behavior from within the dozer state machine virtual void internalTaskComplete( DozerTask task ) override; ///< set a dozer task as successfully completed @@ -278,6 +277,10 @@ class DozerAIUpdate : public AIUpdateInterface, public DozerAIInterface virtual void privateRepair( Object *obj, CommandSourceType cmdSource ) override; ///< repair the target virtual void privateResumeConstruction( Object *obj, CommandSourceType cmdSource ) override; ///< resume construction on obj + virtual void setPreviousTask(DozerTask task); ///< set the previous task + virtual void resumePreviousTask(); ///< resume the previous task if there was one + virtual void clearPreviousTask(); ///< clear the previous task + struct DozerTaskInfo { DozerTaskInfo() diff --git a/Generals/Code/GameEngine/Include/GameLogic/Module/WorkerAIUpdate.h b/Generals/Code/GameEngine/Include/GameLogic/Module/WorkerAIUpdate.h index 9b15198c754..54eb5917c25 100644 --- a/Generals/Code/GameEngine/Include/GameLogic/Module/WorkerAIUpdate.h +++ b/Generals/Code/GameEngine/Include/GameLogic/Module/WorkerAIUpdate.h @@ -132,6 +132,7 @@ class WorkerAIUpdate : public AIUpdateInterface, public DozerAIInterface, public // Dozer side virtual void onDelete() override; + virtual void onDisabledEdge(Bool nowDisabled) override; virtual Real getRepairHealthPerSecond() const override; ///< get health to repair per second virtual Real getBoredTime() const override; ///< how long till we're bored @@ -154,9 +155,8 @@ class WorkerAIUpdate : public AIUpdateInterface, public DozerAIInterface, public // task actions virtual void newTask( DozerTask task, Object* target ) override; ///< set a desire to do the requrested task - virtual void cancelTask( DozerTask task ) override; ///< cancel this task from the queue, if it's the current task the dozer will stop working on it + virtual void cancelTask( DozerTask task, Bool rememberTask = false ) override; ///< cancel this task from the queue, if it's the current task the dozer will stop working on it. Can remember the cancelled task for resumption. virtual void cancelAllTasks() override; ///< cancel all tasks from the queue, if it's the current task the dozer will stop working on it - virtual void resumePreviousTask() override; ///< resume the previous task if there was one // internal methods to manage behavior from within the dozer state machine virtual void internalTaskComplete( DozerTask task ) override; ///< set a dozer task as successfully completed @@ -264,6 +264,10 @@ class WorkerAIUpdate : public AIUpdateInterface, public DozerAIInterface, public virtual void privateDock( Object *obj, CommandSourceType cmdSource ) override; virtual void privateIdle(CommandSourceType cmdSource) override; ///< Enter idle state. + virtual void setPreviousTask(DozerTask task); ///< set the previous task + virtual void resumePreviousTask(); ///< resume the previous task if there was one + virtual void clearPreviousTask(); ///< clear the previous task + private: void createMachines(); ///< create our behavior machines we need diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Object.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Object.cpp index d5748b002e4..4e37a99ec2e 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Object.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Object.cpp @@ -3415,6 +3415,10 @@ void Object::friend_adjustPowerForPlayer( Bool incoming ) //------------------------------------------------------------------------------------------------- void Object::onDisabledEdge(Bool becomingDisabled) { + // rip through the behavior modules and call the onDisabledEdge for any modules that care + for( BehaviorModule **module = m_behaviors; *module; ++module ) + (*module)->onDisabledEdge( becomingDisabled ); + Player* controller = getControllingPlayer(); // can be called during game teardown, thus controller can be null if (controller) 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 c5a3eb001f2..c60801ae013 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cpp @@ -2045,8 +2045,10 @@ void DozerAIUpdate::newTask( DozerTask task, Object *target ) * re-evaluate what it wants to do if it was working on the task being * cancelled */ //------------------------------------------------------------------------------------------------- -void DozerAIUpdate::cancelTask( DozerTask task ) +void DozerAIUpdate::cancelTask( DozerTask task, Bool rememberTask ) { + if (rememberTask) + setPreviousTask(task); // clear the order internalCancelTask( task ); @@ -2061,20 +2063,51 @@ void DozerAIUpdate::cancelAllTasks() for (UnsignedInt task = DOZER_TASK_FIRST; task < DOZER_NUM_TASKS; ++task) internalCancelTask((DozerTask)task); + clearPreviousTask(); + m_dozerMachine->resetToDefaultState(); } +//------------------------------------------------------------------------------------------------- +/** Set the previous task so that we may return to it if we become temporarily incapacitated */ +//------------------------------------------------------------------------------------------------- +void DozerAIUpdate::setPreviousTask(DozerTask task) +{ + if (task == DOZER_TASK_INVALID) + return; + + m_previousTask = task; + m_previousTaskInfo = m_task[task]; +} + //------------------------------------------------------------------------------------------------- /** Attempt to resume the previous task */ //------------------------------------------------------------------------------------------------- void DozerAIUpdate::resumePreviousTask() { - if (m_previousTask != DOZER_TASK_INVALID) + if (m_previousTask == DOZER_TASK_INVALID) + return; + + if (m_previousTask == DOZER_TASK_BUILD) { - newTask(m_previousTask, TheGameLogic->findObjectByID(m_previousTaskInfo.m_targetObjectID)); - m_previousTask = DOZER_TASK_INVALID; - m_previousTaskInfo = DozerTaskInfo(); + Object* target = TheGameLogic->findObjectByID(m_previousTaskInfo.m_targetObjectID); + if (target && target->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION)) + newTask(m_previousTask, target); } + else if (m_previousTask == DOZER_TASK_REPAIR || m_previousTask == DOZER_TASK_FORTIFY) + { + Object* target = TheGameLogic->findObjectByID(m_previousTaskInfo.m_targetObjectID); + if (target) + newTask(m_previousTask, target); + } + + clearPreviousTask(); +} + +void DozerAIUpdate::clearPreviousTask() +{ + m_previousTask = DOZER_TASK_INVALID; + m_previousTaskInfo = DozerTaskInfo(); } //------------------------------------------------------------------------------------------------- @@ -2133,8 +2166,7 @@ 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(); + clearPreviousTask(); // remove dock point info for this task for( Int i = 0; i < DOZER_NUM_DOCK_POINTS; i++ ) @@ -2158,9 +2190,6 @@ 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; @@ -2297,6 +2326,32 @@ void DozerAIUpdate::onDelete() } } +void DozerAIUpdate::onDisabledEdge(Bool nowDisabled) +{ + if (nowDisabled) + { + // Have to say goodbye to the thing we might be building or repairing so someone else can do it. + if (getCurrentTask() != DOZER_TASK_INVALID) + { + // TheSuperHackers @info We want to explicitly define what types to resume from as some types + // are undesirable (e.g. DISABLED_HELD via entering/exiting a container). + Bool rememberTask = getObject()->isDisabledByType(DISABLED_EMP) || + getObject()->isDisabledByType(DISABLED_HACKED) || + getObject()->isDisabledByType(DISABLED_SUBDUED) || + getObject()->isDisabledByType(DISABLED_UNDERPOWERED); + + cancelTask(getCurrentTask(), rememberTask); + } + } + else + { +#if !RETAIL_COMPATIBLE_CRC + // TheSuperHackers @bugfix Stubbjax 17/11/2025 Resume previous task when re-enabled. + resumePreviousTask(); +#endif + } +} + //------------------------------------------------------------------------------------------------- /** Get the most recently issued task */ //------------------------------------------------------------------------------------------------- @@ -2511,7 +2566,7 @@ void DozerAIUpdate::xfer( Xfer *xfer ) xfer->xferSnapshot(m_dozerMachine); xfer->xferUser(&m_currentTask, sizeof(m_currentTask)); - if (currentVersion >= 2) + if (version >= 2) { xfer->xferUser(&m_previousTask, sizeof(m_previousTask)); xfer->xferUser(&m_previousTaskInfo, sizeof(m_previousTaskInfo)); 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 c6798af48f3..dcfa81a50c4 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/WorkerAIUpdate.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/WorkerAIUpdate.cpp @@ -685,8 +685,10 @@ void WorkerAIUpdate::newTask( DozerTask task, Object* target ) * re-evaluate what it wants to do if it was working on the task being * cancelled */ //------------------------------------------------------------------------------------------------- -void WorkerAIUpdate::cancelTask( DozerTask task ) +void WorkerAIUpdate::cancelTask( DozerTask task, Bool rememberTask ) { + if (rememberTask) + setPreviousTask(task); // clear the order internalCancelTask( task ); @@ -701,20 +703,51 @@ void WorkerAIUpdate::cancelAllTasks() for (UnsignedInt task = DOZER_TASK_FIRST; task < DOZER_NUM_TASKS; ++task) internalCancelTask((DozerTask)task); + clearPreviousTask(); + m_dozerMachine->resetToDefaultState(); } +//------------------------------------------------------------------------------------------------- +/** Set the previous task so that we may return to it if we become temporarily incapacitated */ +//------------------------------------------------------------------------------------------------- +void WorkerAIUpdate::setPreviousTask(DozerTask task) +{ + if (task == DOZER_TASK_INVALID) + return; + + m_previousTask = task; + m_previousTaskInfo = m_task[task]; +} + //------------------------------------------------------------------------------------------------- /** Attempt to resume the previous task */ //------------------------------------------------------------------------------------------------- void WorkerAIUpdate::resumePreviousTask() { - if (m_previousTask != DOZER_TASK_INVALID) + if (m_previousTask == DOZER_TASK_INVALID) + return; + + if (m_previousTask == DOZER_TASK_BUILD) { - newTask(m_previousTask, TheGameLogic->findObjectByID(m_previousTaskInfo.m_targetObjectID)); - m_previousTask = DOZER_TASK_INVALID; - m_previousTaskInfo = DozerTaskInfo(); + Object* target = TheGameLogic->findObjectByID(m_previousTaskInfo.m_targetObjectID); + if (target && target->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION)) + newTask(m_previousTask, target); } + else if (m_previousTask == DOZER_TASK_REPAIR || m_previousTask == DOZER_TASK_FORTIFY) + { + Object* target = TheGameLogic->findObjectByID(m_previousTaskInfo.m_targetObjectID); + if (target) + newTask(m_previousTask, target); + } + + clearPreviousTask(); +} + +void WorkerAIUpdate::clearPreviousTask() +{ + m_previousTask = DOZER_TASK_INVALID; + m_previousTaskInfo = DozerTaskInfo(); } //------------------------------------------------------------------------------------------------- @@ -773,8 +806,7 @@ 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(); + clearPreviousTask(); // remove dock point info for this task for( Int i = 0; i < DOZER_NUM_DOCK_POINTS; i++ ) @@ -798,9 +830,6 @@ 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; @@ -925,6 +954,32 @@ void WorkerAIUpdate::onDelete() } } +void WorkerAIUpdate::onDisabledEdge(Bool nowDisabled) +{ + if (nowDisabled) + { + // Have to say goodbye to the thing we might be building or repairing so someone else can do it. + if (getCurrentTask() != DOZER_TASK_INVALID) + { + // TheSuperHackers @info We want to explicitly define what types to resume from as some types + // are undesirable (e.g. DISABLED_HELD via entering/exiting a container). + Bool rememberTask = getObject()->isDisabledByType(DISABLED_EMP) || + getObject()->isDisabledByType(DISABLED_HACKED) || + getObject()->isDisabledByType(DISABLED_SUBDUED) || + getObject()->isDisabledByType(DISABLED_UNDERPOWERED); + + cancelTask(getCurrentTask(), rememberTask); + } + } + else + { +#if !RETAIL_COMPATIBLE_CRC + // TheSuperHackers @bugfix Stubbjax 17/11/2025 Resume previous task when re-enabled. + resumePreviousTask(); +#endif + } +} + //------------------------------------------------------------------------------------------------- /** Get the most recently issued task */ //------------------------------------------------------------------------------------------------- @@ -1463,7 +1518,7 @@ void WorkerAIUpdate::xfer( Xfer *xfer ) xfer->xferSnapshot(m_dozerMachine); xfer->xferUser(&m_currentTask, sizeof(m_currentTask)); - if (currentVersion >= 2) + if (version >= 2) { xfer->xferUser(&m_previousTask, sizeof(m_previousTask)); xfer->xferUser(&m_previousTaskInfo, sizeof(m_previousTaskInfo)); diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/DozerAIUpdate.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/DozerAIUpdate.h index 150e893762d..d7113be4a1d 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/DozerAIUpdate.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/DozerAIUpdate.h @@ -139,9 +139,8 @@ 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 cancelTask( DozerTask task, Bool rememberTask = false ) = 0; ///< cancel this task from the queue, if it's the current task the dozer will stop working on it. Can remember the cancelled task for resumption. virtual void cancelAllTasks() = 0; ///< cancel all tasks from the queue, if it's the current task the dozer will stop working on it - virtual void resumePreviousTask() = 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 @@ -211,6 +210,7 @@ class DozerAIUpdate : public AIUpdateInterface, public DozerAIInterface virtual const DozerAIInterface* getDozerAIInterface() const override {return this;} virtual void onDelete() override; + virtual void onDisabledEdge(Bool nowDisabled) override; // // module data methods ... this is LAME, multiple inheritance off an interface with replicated @@ -240,9 +240,8 @@ class DozerAIUpdate : public AIUpdateInterface, public DozerAIInterface // task actions virtual void newTask( DozerTask task, Object *target ) override; ///< set a desire to do the requested task - virtual void cancelTask( DozerTask task ) override; ///< cancel this task from the queue, if it's the current task the dozer will stop working on it + virtual void cancelTask( DozerTask task, Bool rememberTask = false ) override; ///< cancel this task from the queue, if it's the current task the dozer will stop working on it virtual void cancelAllTasks() override; ///< cancel all tasks from the queue, if it's the current task the dozer will stop working on it - virtual void resumePreviousTask() override; ///< resume the previous task if there was one // internal methods to manage behavior from within the dozer state machine virtual void internalTaskComplete( DozerTask task ) override; ///< set a dozer task as successfully completed @@ -278,6 +277,10 @@ class DozerAIUpdate : public AIUpdateInterface, public DozerAIInterface virtual void privateRepair( Object *obj, CommandSourceType cmdSource ) override; ///< repair the target virtual void privateResumeConstruction( Object *obj, CommandSourceType cmdSource ) override; ///< resume construction on obj + virtual void setPreviousTask(DozerTask task); ///< set the previous task + virtual void resumePreviousTask(); ///< resume the previous task if there was one + virtual void clearPreviousTask(); ///< clear the previous task + struct DozerTaskInfo { DozerTaskInfo() diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/WorkerAIUpdate.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/WorkerAIUpdate.h index 5d0faa343f6..7b9deabdc08 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/WorkerAIUpdate.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/WorkerAIUpdate.h @@ -135,6 +135,7 @@ class WorkerAIUpdate : public AIUpdateInterface, public DozerAIInterface, public // Dozer side virtual void onDelete() override; + virtual void onDisabledEdge(Bool nowDisabled) override; virtual Real getRepairHealthPerSecond() const override; ///< get health to repair per second virtual Real getBoredTime() const override; ///< how long till we're bored @@ -157,9 +158,8 @@ class WorkerAIUpdate : public AIUpdateInterface, public DozerAIInterface, public // task actions virtual void newTask( DozerTask task, Object* target ) override; ///< set a desire to do the requested task - virtual void cancelTask( DozerTask task ) override; ///< cancel this task from the queue, if it's the current task the dozer will stop working on it + virtual void cancelTask( DozerTask task, Bool rememberTask = false ) override; ///< cancel this task from the queue, if it's the current task the dozer will stop working on it. Can remember the cancelled task for resumption. virtual void cancelAllTasks() override; ///< cancel all tasks from the queue, if it's the current task the dozer will stop working on it - virtual void resumePreviousTask() override; ///< resume the previous task if there was one // internal methods to manage behavior from within the dozer state machine virtual void internalTaskComplete( DozerTask task ) override; ///< set a dozer task as successfully completed @@ -270,6 +270,10 @@ class WorkerAIUpdate : public AIUpdateInterface, public DozerAIInterface, public virtual void privateDock( Object *obj, CommandSourceType cmdSource ) override; virtual void privateIdle(CommandSourceType cmdSource) override; ///< Enter idle state. + virtual void setPreviousTask(DozerTask task); ///< set the previous task + virtual void resumePreviousTask(); ///< resume the previous task if there was one + virtual void clearPreviousTask(); ///< clear the previous task + private: void createMachines(); ///< create our behavior machines we need diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp index 629cf11f4fe..ba7b7264b77 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp @@ -3908,24 +3908,6 @@ void Object::onDisabledEdge(Bool becomingDisabled) for( BehaviorModule **module = m_behaviors; *module; ++module ) (*module)->onDisabledEdge( becomingDisabled ); - DozerAIInterface *dozerAI = getAI() ? getAI()->getDozerAIInterface() : nullptr; - if (dozerAI) - { - 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(); // can be called during game teardown, thus controller can be null if (controller) 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 2ccaaa00d38..1b01957d1de 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cpp @@ -2050,8 +2050,10 @@ void DozerAIUpdate::newTask( DozerTask task, Object *target ) * re-evaluate what it wants to do if it was working on the task being * cancelled */ //------------------------------------------------------------------------------------------------- -void DozerAIUpdate::cancelTask( DozerTask task ) +void DozerAIUpdate::cancelTask( DozerTask task, Bool rememberTask ) { + if (rememberTask) + setPreviousTask(task); // clear the order internalCancelTask( task ); @@ -2066,20 +2068,51 @@ void DozerAIUpdate::cancelAllTasks() for (UnsignedInt task = DOZER_TASK_FIRST; task < DOZER_NUM_TASKS; ++task) internalCancelTask((DozerTask)task); + clearPreviousTask(); + m_dozerMachine->resetToDefaultState(); } +//------------------------------------------------------------------------------------------------- +/** Set the previous task so that we may return to it if we become temporarily incapacitated */ +//------------------------------------------------------------------------------------------------- +void DozerAIUpdate::setPreviousTask(DozerTask task) +{ + if (task == DOZER_TASK_INVALID) + return; + + m_previousTask = task; + m_previousTaskInfo = m_task[task]; +} + //------------------------------------------------------------------------------------------------- /** Attempt to resume the previous task */ //------------------------------------------------------------------------------------------------- void DozerAIUpdate::resumePreviousTask() { - if (m_previousTask != DOZER_TASK_INVALID) + if (m_previousTask == DOZER_TASK_INVALID) + return; + + if (m_previousTask == DOZER_TASK_BUILD) + { + Object* target = TheGameLogic->findObjectByID(m_previousTaskInfo.m_targetObjectID); + if (target && target->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION)) + newTask(m_previousTask, target); + } + else if (m_previousTask == DOZER_TASK_REPAIR || m_previousTask == DOZER_TASK_FORTIFY) { - newTask(m_previousTask, TheGameLogic->findObjectByID(m_previousTaskInfo.m_targetObjectID)); - m_previousTask = DOZER_TASK_INVALID; - m_previousTaskInfo = DozerTaskInfo(); + Object* target = TheGameLogic->findObjectByID(m_previousTaskInfo.m_targetObjectID); + if (target) + newTask(m_previousTask, target); } + + clearPreviousTask(); +} + +void DozerAIUpdate::clearPreviousTask() +{ + m_previousTask = DOZER_TASK_INVALID; + m_previousTaskInfo = DozerTaskInfo(); } //------------------------------------------------------------------------------------------------- @@ -2138,8 +2171,7 @@ 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(); + clearPreviousTask(); // remove dock point info for this task for( Int i = 0; i < DOZER_NUM_DOCK_POINTS; i++ ) @@ -2163,9 +2195,6 @@ 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; @@ -2302,6 +2331,32 @@ void DozerAIUpdate::onDelete() } } +void DozerAIUpdate::onDisabledEdge(Bool nowDisabled) +{ + if (nowDisabled) + { + // Have to say goodbye to the thing we might be building or repairing so someone else can do it. + if (getCurrentTask() != DOZER_TASK_INVALID) + { + // TheSuperHackers @info We want to explicitly define what types to resume from as some types + // are undesirable (e.g. DISABLED_HELD via entering/exiting a container). + Bool rememberTask = getObject()->isDisabledByType(DISABLED_EMP) || + getObject()->isDisabledByType(DISABLED_HACKED) || + getObject()->isDisabledByType(DISABLED_SUBDUED) || + getObject()->isDisabledByType(DISABLED_UNDERPOWERED); + + cancelTask(getCurrentTask(), rememberTask); + } + } + else + { +#if !RETAIL_COMPATIBLE_CRC + // TheSuperHackers @bugfix Stubbjax 17/11/2025 Resume previous task when re-enabled. + resumePreviousTask(); +#endif + } +} + //------------------------------------------------------------------------------------------------- /** Get the most recently issued task */ //------------------------------------------------------------------------------------------------- @@ -2523,7 +2578,7 @@ void DozerAIUpdate::xfer( Xfer *xfer ) xfer->xferSnapshot(m_dozerMachine); xfer->xferUser(&m_currentTask, sizeof(m_currentTask)); - if (currentVersion >= 2) + if (version >= 2) { xfer->xferUser(&m_previousTask, sizeof(m_previousTask)); xfer->xferUser(&m_previousTaskInfo, sizeof(m_previousTaskInfo)); 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 ba7313535c5..47a17d182bc 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/WorkerAIUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/WorkerAIUpdate.cpp @@ -685,8 +685,10 @@ void WorkerAIUpdate::newTask( DozerTask task, Object* target ) * re-evaluate what it wants to do if it was working on the task being * cancelled */ //------------------------------------------------------------------------------------------------- -void WorkerAIUpdate::cancelTask( DozerTask task ) +void WorkerAIUpdate::cancelTask( DozerTask task, Bool rememberTask ) { + if (rememberTask) + setPreviousTask(task); // clear the order internalCancelTask( task ); @@ -701,20 +703,51 @@ void WorkerAIUpdate::cancelAllTasks() for (UnsignedInt task = DOZER_TASK_FIRST; task < DOZER_NUM_TASKS; ++task) internalCancelTask((DozerTask)task); + clearPreviousTask(); + m_dozerMachine->resetToDefaultState(); } +//------------------------------------------------------------------------------------------------- +/** Set the previous task so that we may return to it if we become temporarily incapacitated */ +//------------------------------------------------------------------------------------------------- +void WorkerAIUpdate::setPreviousTask(DozerTask task) +{ + if (task == DOZER_TASK_INVALID) + return; + + m_previousTask = task; + m_previousTaskInfo = m_task[task]; +} + //------------------------------------------------------------------------------------------------- /** Attempt to resume the previous task */ //------------------------------------------------------------------------------------------------- void WorkerAIUpdate::resumePreviousTask() { - if (m_previousTask != DOZER_TASK_INVALID) + if (m_previousTask == DOZER_TASK_INVALID) + return; + + if (m_previousTask == DOZER_TASK_BUILD) { - newTask(m_previousTask, TheGameLogic->findObjectByID(m_previousTaskInfo.m_targetObjectID)); - m_previousTask = DOZER_TASK_INVALID; - m_previousTaskInfo = DozerTaskInfo(); + Object* target = TheGameLogic->findObjectByID(m_previousTaskInfo.m_targetObjectID); + if (target && target->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION)) + newTask(m_previousTask, target); } + else if (m_previousTask == DOZER_TASK_REPAIR || m_previousTask == DOZER_TASK_FORTIFY) + { + Object* target = TheGameLogic->findObjectByID(m_previousTaskInfo.m_targetObjectID); + if (target) + newTask(m_previousTask, target); + } + + clearPreviousTask(); +} + +void WorkerAIUpdate::clearPreviousTask() +{ + m_previousTask = DOZER_TASK_INVALID; + m_previousTaskInfo = DozerTaskInfo(); } //------------------------------------------------------------------------------------------------- @@ -773,8 +806,7 @@ 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(); + clearPreviousTask(); // remove dock point info for this task for( Int i = 0; i < DOZER_NUM_DOCK_POINTS; i++ ) @@ -798,9 +830,6 @@ 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; @@ -925,6 +954,32 @@ void WorkerAIUpdate::onDelete() } } +void WorkerAIUpdate::onDisabledEdge(Bool nowDisabled) +{ + if (nowDisabled) + { + // Have to say goodbye to the thing we might be building or repairing so someone else can do it. + if (getCurrentTask() != DOZER_TASK_INVALID) + { + // TheSuperHackers @info We want to explicitly define what types to resume from as some types + // are undesirable (e.g. DISABLED_HELD via entering/exiting a container). + Bool rememberTask = getObject()->isDisabledByType(DISABLED_EMP) || + getObject()->isDisabledByType(DISABLED_HACKED) || + getObject()->isDisabledByType(DISABLED_SUBDUED) || + getObject()->isDisabledByType(DISABLED_UNDERPOWERED); + + cancelTask(getCurrentTask(), rememberTask); + } + } + else + { +#if !RETAIL_COMPATIBLE_CRC + // TheSuperHackers @bugfix Stubbjax 17/11/2025 Resume previous task when re-enabled. + resumePreviousTask(); +#endif + } +} + //------------------------------------------------------------------------------------------------- /** Get the most recently issued task */ //------------------------------------------------------------------------------------------------- @@ -1473,7 +1528,7 @@ void WorkerAIUpdate::xfer( Xfer *xfer ) xfer->xferSnapshot(m_dozerMachine); xfer->xferUser(&m_currentTask, sizeof(m_currentTask)); - if (currentVersion >= 2) + if (version >= 2) { xfer->xferUser(&m_previousTask, sizeof(m_previousTask)); xfer->xferUser(&m_previousTaskInfo, sizeof(m_previousTaskInfo));