Skip to content

bugfix: Attacking infantry no longer attempt to path to their targets when force-evacuated from a vehicle - #3098

Open
Stubbjax wants to merge 2 commits into
TheSuperHackers:mainfrom
Stubbjax:fix-evacuated-attacking-infantry-behaviour
Open

bugfix: Attacking infantry no longer attempt to path to their targets when force-evacuated from a vehicle#3098
Stubbjax wants to merge 2 commits into
TheSuperHackers:mainfrom
Stubbjax:fix-evacuated-attacking-infantry-behaviour

Conversation

@Stubbjax

@Stubbjax Stubbjax commented Aug 11, 2026

Copy link
Copy Markdown

This change fixes an issue where infantry would attempt to path to their targets when force-evacuated from a vehicle.

Force-evacuated infantry would incorrectly retain their goal object reference, which would override the path/destination the infantry were given upon evacuation to that of their attack target instead of their scatter position. Furthermore, temporary AI states would not be cleared when the state machine was cleared upon receiving new commands, causing subsequent commands to effectively provide the temporary state with new data (i.e. giving an attack command during the scatter state would just assign a goal object and cause the unit to path to the target).

The fix is to clear the goal object when infantry are scattered as well as any temporary states when the main state machine is cleared (usually when receiving a new command). As a result, evacuation behaviour is consistent across all vehicles under all circumstances and units feel nice and responsive.

Before

The force-evacuated Tank Hunters attempt to path to the Emperor Overlord

BEFORE.mp4

After

The force-evacuated Tank Hunters are correctly scattered

AFTER.mp4

@Stubbjax Stubbjax self-assigned this Aug 11, 2026
@Stubbjax Stubbjax added Bug Something is not working right, typically is user facing Major Severity: Minor < Major < Critical < Blocker Gen Relates to Generals ZH Relates to Zero Hour NoRetail This fix or change is not applicable with Retail game compatibility labels Aug 11, 2026
@Caball009

Caball009 commented Aug 11, 2026

Copy link
Copy Markdown

Can you check if there's issue for this? 1337 perhaps.

@WWB2-account

Copy link
Copy Markdown

Yeah, this looks to be very similar to 1337 indeed. If this pull also fixes the unresponsiveness to player commands it should be identical. From your description it seems like it is.

Based on that, I would highly recommend putting this change behind the Controversial flag so the game design comittee can squabble over it; it's a buff to high level Inf Gen play.

@xezon xezon added China Affects China faction Buff Makes a thing more powerful Controversial Is controversial labels Aug 11, 2026
m_goalWaypoint = nullptr;
m_goalSquad = nullptr;

#if !RETAIL_COMPATIBLE_CRC

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Better also put it behind a PRESERVE_... macro.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't do this because the temporary state reset logic is not necessarily exclusive to the issue at hand. I could make it more broad/generic and have something like PRESERVE_UNINTERRUPTIBLE_TEMP_AI_STATES, but that's not really indicative of the bug it's targeting.


#if !RETAIL_COMPATIBLE_CRC
if (m_temporaryState)
m_temporaryState->onExit(EXIT_RESET);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any idea if this could also affect some other ai behaviors? State is really vast, used by many different things.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only other setTemporaryState cases are AI_FOLLOW_EXITPRODUCTION_PATH (only when AI player exits an attacked Tunnel), AI_MOVE_OUT_OF_THE_WAY, and several AI_BUSY cases in the deploy logic. I've been unable to find any issues or regressions with these states.

@Stubbjax Stubbjax added Committee2026 ★ Approved by the 2026 elected Game Design Committee ★ and removed Buff Makes a thing more powerful China Affects China faction Controversial Is controversial labels Aug 12, 2026
@Stubbjax

Copy link
Copy Markdown
Author

I accidentally just discovered that exiting the temporary state when given new commands also seems to fix #199.

Before

BEFORE.mp4

After

AFTER.mp4

@tintinhamans

Copy link
Copy Markdown

/agentic_review

@Stubbjax Stubbjax added Buff Makes a thing more powerful China Affects China faction Controversial Is controversial labels Aug 12, 2026
@qodo-free-for-open-source-projects

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (1) 📘 Rule violations (0) 📎 Requirement gaps (1) 📜 Skill insights (0)

Grey Divider


Action required

1. Evacuation fix behind RETAIL_COMPATIBLE_CRC 📎 Requirement gap ≡ Correctness
Description
The new evac/command-responsiveness cleanup that clears m_temporaryState / the AI goal object is
wrapped in #if !RETAIL_COMPATIBLE_CRC, but this repo defaults RETAIL_COMPATIBLE_CRC to 1, so
default (retail-compatible) builds won’t include the fix and the reported bug may remain. This risks
failing the requirement that infantry immediately obey attack commands after emerging from destroyed
fireport vehicles.
Code

GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIStates.cpp[R1024-1027]

+#if !RETAIL_COMPATIBLE_CRC
+	if (m_temporaryState)
+		m_temporaryState->onExit(EXIT_RESET);
+
Evidence
PR Compliance ID 2 requires infantry to respond immediately to player attack commands after emerging
from destroyed fireport vehicles, and the diff implements that via new cleanup/goal-reset logic
(temporary state exit/reset and clearing the goal object during scatter). However, the added logic
is placed under #if !RETAIL_COMPATIBLE_CRC while RETAIL_COMPATIBLE_CRC is defined to default to
(1) in the repo, and the referenced CMake configuration does not override it, meaning the new
behavior is excluded from default compilation (including both Generals and GeneralsMD codepaths
where the guarded logic was added).

Infantry emerging from destroyed fireport vehicles respond immediately to player attack commands
GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIStates.cpp[1024-1029]
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp[771-774]
Core/GameEngine/Include/Common/GameDefines.h[90-92]
Generals/Code/GameEngine/Source/GameLogic/AI/AIStates.cpp[1012-1029]
Generals/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp[669-679]
GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIStates.cpp[1021-1033]
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp[768-775]
Core/GameEngine/Include/Common/GameDefines.h[90-96]
cmake/config-build.cmake[70-74]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The responsiveness bugfix is currently compiled out in the default configuration because the new cleanup/goal-reset logic is guarded by `#if !RETAIL_COMPATIBLE_CRC` while `RETAIL_COMPATIBLE_CRC` defaults to `1` in this repo, so retail-compatible/default builds won’t contain the fix.

## Issue Context
PR Compliance ID 2 requires infantry to respond immediately to player attack commands after emerging from destroyed fireport vehicles. The PR introduces new cleanup behavior (clearing `m_temporaryState` / resetting temporary state exit and clearing the AI goal object during `scatter`) intended to restore responsiveness, but because it is behind `!RETAIL_COMPATIBLE_CRC` it only takes effect when explicitly building with retail CRC compatibility disabled. This affects both Generals and GeneralsMD implementations of `AIStateMachine::clear()` and `OpenContain::scatterToNearbyPosition()`, and the CMake config referenced does not appear to override the default macro value.

## Fix Focus Areas
- Core/GameEngine/Include/Common/GameDefines.h[90-96]
- cmake/config-build.cmake[70-74]
- Generals/Code/GameEngine/Source/GameLogic/AI/AIStates.cpp[1012-1029]
- GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIStates.cpp[1021-1033]
- GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIStates.cpp[1024-1029]
- Generals/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp[669-679]
- GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp[768-775]
- GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp[771-774]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Temporary state double-exit 🐞 Bug ☼ Reliability
Description
When !RETAIL_COMPATIBLE_CRC is enabled, AIStateMachine::clear() calls
m_temporaryState->onExit(EXIT_RESET) after StateMachine::clear() already exits the current
state; because states are singletons per StateID, setting a temporary state to the current StateID
would invoke onExit() twice on the same State instance.
Code

Generals/Code/GameEngine/Source/GameLogic/AI/AIStates.cpp[R1020-1023]

+	if (m_temporaryState)
+		m_temporaryState->onExit(EXIT_RESET);
+
+	m_temporaryState = nullptr;
Evidence
StateMachine::clear() already calls onExit(EXIT_RESET) on the current state.
StateMachine::defineState()/internalGetState() prove there is a single State* per StateID, and
AIStateMachine::setTemporaryState() uses internalGetState(), so a temporary state set to the
current StateID will reference the same object that StateMachine::clear() just exited.

Generals/Code/GameEngine/Source/GameLogic/AI/AIStates.cpp[1012-1029]
Generals/Code/GameEngine/Source/Common/StateMachine.cpp[344-363]
Generals/Code/GameEngine/Source/Common/StateMachine.cpp[483-525]
Generals/Code/GameEngine/Source/GameLogic/AI/AIStates.cpp[914-961]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`AIStateMachine::clear()` now exits the temporary state after calling `StateMachine::clear()`. If `m_temporaryState` aliases the state that was current (same StateID), the same `State` object can receive `onExit(EXIT_RESET)` twice.

## Issue Context
`StateMachine` stores exactly one `State*` per `StateID` and returns that same pointer from `internalGetState()`. Both `setState()` and `AIStateMachine::setTemporaryState()` use this mechanism, so same-ID implies same instance.

## Fix Focus Areas
- Generals/Code/GameEngine/Source/GameLogic/AI/AIStates.cpp[1012-1029]
- GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIStates.cpp[1021-1033]
- Generals/Code/GameEngine/Source/Common/StateMachine.cpp[344-363]
- Generals/Code/GameEngine/Source/Common/StateMachine.cpp[483-525]
- Generals/Code/GameEngine/Source/GameLogic/AI/AIStates.cpp[914-961]

## Suggested change
- Capture `StateID oldCurrent = getCurrentStateID();` before calling `StateMachine::clear()`.
- After `StateMachine::clear()`, only call `m_temporaryState->onExit(EXIT_RESET)` if `m_temporaryState != nullptr` AND `m_temporaryState->getID() != oldCurrent`.
- Always set `m_temporaryState = nullptr` (and optionally reset `m_temporaryStateFramEnd = 0`) afterward.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context
Review mode: ⚖️ Balanced: This is a localized but behavior-changing AI/state-machine fix across mirrored game-engine paths, with lifecycle and command-handling interactions that merit a complete single-pass review.

Grey Divider

Tip of the day
💡 Did you know, you can enable the Remediation agent and Qodo fixes findings in a dedicated fix PR

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment on lines +1024 to +1027
#if !RETAIL_COMPATIBLE_CRC
if (m_temporaryState)
m_temporaryState->onExit(EXIT_RESET);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Evacuation fix behind retail_compatible_crc 📎 Requirement gap ≡ Correctness

The new evac/command-responsiveness cleanup that clears m_temporaryState / the AI goal object is
wrapped in #if !RETAIL_COMPATIBLE_CRC, but this repo defaults RETAIL_COMPATIBLE_CRC to 1, so
default (retail-compatible) builds won’t include the fix and the reported bug may remain. This risks
failing the requirement that infantry immediately obey attack commands after emerging from destroyed
fireport vehicles.
Agent Prompt
## Issue description
The responsiveness bugfix is currently compiled out in the default configuration because the new cleanup/goal-reset logic is guarded by `#if !RETAIL_COMPATIBLE_CRC` while `RETAIL_COMPATIBLE_CRC` defaults to `1` in this repo, so retail-compatible/default builds won’t contain the fix.

## Issue Context
PR Compliance ID 2 requires infantry to respond immediately to player attack commands after emerging from destroyed fireport vehicles. The PR introduces new cleanup behavior (clearing `m_temporaryState` / resetting temporary state exit and clearing the AI goal object during `scatter`) intended to restore responsiveness, but because it is behind `!RETAIL_COMPATIBLE_CRC` it only takes effect when explicitly building with retail CRC compatibility disabled. This affects both Generals and GeneralsMD implementations of `AIStateMachine::clear()` and `OpenContain::scatterToNearbyPosition()`, and the CMake config referenced does not appear to override the default macro value.

## Fix Focus Areas
- Core/GameEngine/Include/Common/GameDefines.h[90-96]
- cmake/config-build.cmake[70-74]
- Generals/Code/GameEngine/Source/GameLogic/AI/AIStates.cpp[1012-1029]
- GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIStates.cpp[1021-1033]
- GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIStates.cpp[1024-1029]
- Generals/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp[669-679]
- GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp[768-775]
- GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp[771-774]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +1020 to +1023
if (m_temporaryState)
m_temporaryState->onExit(EXIT_RESET);

m_temporaryState = nullptr;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

2. Temporary state double-exit 🐞 Bug ☼ Reliability

When !RETAIL_COMPATIBLE_CRC is enabled, AIStateMachine::clear() calls
m_temporaryState->onExit(EXIT_RESET) after StateMachine::clear() already exits the current
state; because states are singletons per StateID, setting a temporary state to the current StateID
would invoke onExit() twice on the same State instance.
Agent Prompt
## Issue description
`AIStateMachine::clear()` now exits the temporary state after calling `StateMachine::clear()`. If `m_temporaryState` aliases the state that was current (same StateID), the same `State` object can receive `onExit(EXIT_RESET)` twice.

## Issue Context
`StateMachine` stores exactly one `State*` per `StateID` and returns that same pointer from `internalGetState()`. Both `setState()` and `AIStateMachine::setTemporaryState()` use this mechanism, so same-ID implies same instance.

## Fix Focus Areas
- Generals/Code/GameEngine/Source/GameLogic/AI/AIStates.cpp[1012-1029]
- GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIStates.cpp[1021-1033]
- Generals/Code/GameEngine/Source/Common/StateMachine.cpp[344-363]
- Generals/Code/GameEngine/Source/Common/StateMachine.cpp[483-525]
- Generals/Code/GameEngine/Source/GameLogic/AI/AIStates.cpp[914-961]

## Suggested change
- Capture `StateID oldCurrent = getCurrentStateID();` before calling `StateMachine::clear()`.
- After `StateMachine::clear()`, only call `m_temporaryState->onExit(EXIT_RESET)` if `m_temporaryState != nullptr` AND `m_temporaryState->getID() != oldCurrent`.
- Always set `m_temporaryState = nullptr` (and optionally reset `m_temporaryStateFramEnd = 0`) afterward.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Buff Makes a thing more powerful Bug Something is not working right, typically is user facing China Affects China faction Committee2026 ★ Approved by the 2026 elected Game Design Committee ★ Controversial Is controversial Gen Relates to Generals Major Severity: Minor < Major < Critical < Blocker NoRetail This fix or change is not applicable with Retail game compatibility ZH Relates to Zero Hour

Projects

None yet

5 participants