bugfix: Fix issue where builders could resume completed tasks after being disabled#2793
bugfix: Fix issue where builders could resume completed tasks after being disabled#2793Stubbjax wants to merge 8 commits into
Conversation
|
| Filename | Overview |
|---|---|
| Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cpp | Adds setPreviousTask (correctly guards against INVALID) and refactors resumePreviousTask, but the build-resume guard !testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION) is inverted — newTask fires for complete buildings and is skipped for incomplete ones. |
| Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/WorkerAIUpdate.cpp | Mirrors DozerAIUpdate changes; carries the same inverted !testStatus guard in resumePreviousTask. |
| GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cpp | Zero Hour mirror of the Generals Dozer fix; same inverted !testStatus condition present. |
| GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/WorkerAIUpdate.cpp | Zero Hour mirror of the Generals Worker fix; same inverted !testStatus condition present. |
| Generals/Code/GameEngine/Source/GameLogic/Object/Object.cpp | Correctly captures the current task via setPreviousTask at the moment of first EMP/underpowered/hacked disable, guarded against double-fire by the existing !isDisabledByType check. |
| GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp | Identical, correct change to Generals/Object.cpp for Zero Hour. |
| Generals/Code/GameEngine/Include/GameLogic/Module/DozerAIUpdate.h | Adds setPreviousTask pure virtual and override declarations; no issues. |
| Generals/Code/GameEngine/Include/GameLogic/Module/WorkerAIUpdate.h | Adds setPreviousTask override declaration; no issues. |
| GeneralsMD/Code/GameEngine/Include/GameLogic/Module/DozerAIUpdate.h | Zero Hour mirror of DozerAIUpdate.h changes; no issues. |
| GeneralsMD/Code/GameEngine/Include/GameLogic/Module/WorkerAIUpdate.h | Zero Hour mirror of WorkerAIUpdate.h changes; no issues. |
Sequence Diagram
sequenceDiagram
participant OBJ as Object::setDisabledUntil
participant DAU as DozerAIUpdate
participant ICT as internalCancelTask
participant RPT as resumePreviousTask
Note over OBJ: Builder hit by EMP/underpowered/hacked
OBJ->>DAU: setPreviousTask(getCurrentTask())
Note over DAU: Guards DOZER_TASK_INVALID,<br/>saves m_previousTask + m_task[task]
Note over ICT: Task cancelled due to disable
ICT->>ICT: internalTaskCompleteOrCancelled(task)
Note over ICT: m_previousTask no longer set here<br/>(removed by this PR)
Note over RPT: Builder re-enables
RPT->>RPT: "m_previousTask == INVALID? return"
RPT->>RPT: "m_previousTask == DOZER_TASK_BUILD?"
RPT->>RPT: findObjectByID(target)
Note over RPT: ⚠ Inverted guard: !testStatus(UNDER_CONSTRUCTION)<br/>true=complete → calls newTask (wrong)<br/>false=incomplete → skips newTask (wrong)
RPT->>DAU: newTask(BUILD, target)
RPT->>RPT: "Reset m_previousTask = INVALID"
Reviews (6): Last reviewed commit: "fix: Reverse condition" | Re-trigger Greptile
| if( task == DOZER_TASK_BUILD ) | ||
| { | ||
| // TheSuperHackers @bugfix Stubbjax 15/06/2026 Ignore the build task if the building is already complete. | ||
| if (target->getConstructionPercent() == CONSTRUCTION_COMPLETE) |
There was a problem hiding this comment.
Should this perhaps go into DozerAIUpdate::resumePreviousTask and here it should be an assert?
There was a problem hiding this comment.
Good point. I suppose it could seeing as this is the only way it can happen. Having to resolve the target from the task info does make the code a bit more complex though.
657688e to
a0fbee6
Compare
de2f3f1 to
bdf1ab1
Compare
| return; | ||
|
|
||
| newTask(m_previousTask, TheGameLogic->findObjectByID(m_previousTaskInfo.m_targetObjectID)); | ||
| m_previousTask = DOZER_TASK_INVALID; |
There was a problem hiding this comment.
Now m_previousTask is no longer cleared if not DOZER_TASK_BUILD or already completed.
There was a problem hiding this comment.
It is still not cleared if task is not DOZER_TASK_BUILD
|
|
||
| DozerAIInterface* dozerAI = getAI() ? getAI()->getDozerAIInterface() : nullptr; | ||
| if (dozerAI) | ||
| dozerAI->setPreviousTask(dozerAI->getCurrentTask()); |
There was a problem hiding this comment.
This looks a bit strange. Wasn't cancelTask supposed to take of this? For example from Object::onDisabledEdge.
There was a problem hiding this comment.
Yes, but there was no distinction on how the disabling happened here. A unit entering a transport causes it to become disabled, and thus it would attempt to resume the task after exiting the transport.
74beb35 to
a4ffdb5
Compare
| { | ||
| newTask(m_previousTask, TheGameLogic->findObjectByID(m_previousTaskInfo.m_targetObjectID)); | ||
| Object* target = TheGameLogic->findObjectByID(m_previousTaskInfo.m_targetObjectID); | ||
| if (!target || target->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION)) |
There was a problem hiding this comment.
Why is !target a valid condition for new task?
This change fixes an issue introduced by #1870 that allows builders to resume an already completed task after being disabled.
When assigning a new build task to a builder, if the target building is an already-completed building, then the new task is ignored.
Before
BEFORE.mp4
After
AFTER.mp4