Skip to content

bugfix(ai): Fix bugged unit behavior when attacked during guard mode#2197

Merged
xezon merged 1 commit into
TheSuperHackers:mainfrom
CookieLandProjects:a
Jun 19, 2026
Merged

bugfix(ai): Fix bugged unit behavior when attacked during guard mode#2197
xezon merged 1 commit into
TheSuperHackers:mainfrom
CookieLandProjects:a

Conversation

@CookieLandProjects

@CookieLandProjects CookieLandProjects commented Jan 26, 2026

Copy link
Copy Markdown

Made Guard do what its supposed to instead of retaliating when its not supposed to.

EDIT: Most of the bug description was moved from the code to here:

// This fixes the conflicting movement and fire behavior in Guard mode when the unit is under attack
//
// Root cause: In retail, both AI_GUARD_INNER and AI_GUARD_RETURN had the attackAggressors
// handler attached.
// While the INNER state was issuing attack orders or RETURN state was
// issuing movement orders back to the guard position,
// the aggressor handler kept firing attack commands on every shot.
// This resulted in confliction in orders, causing the unit to do unexpected behavior.
//
// Fix: Remove the attackAggressors handler from AI_GUARD_INNER and AI_GUARD_RETURN.
// The inner guard engagement logic is still handled within the AI_GUARD_INNER state,
// and once the unit decides to return (RETURN state), it performs a clean movement without
// competing attack orders (unless something enters its Inner guard range).

@greptile-apps

greptile-apps Bot commented Jan 26, 2026

Copy link
Copy Markdown

Greptile Summary

This PR fixes a conflicting movement and fire behavior in Guard mode by removing attackAggressors condition handlers from the AI_GUARD_RETURN and AI_GUARD_INNER states across Generals and GeneralsMD. The fix is gated behind #if !RETAIL_COMPATIBLE_CRC preprocessor guards so the retail code path is preserved.

  • In Generals/AIGuard.cpp, the attackAggressors handler is removed from AI_GUARD_RETURN; AI_GUARD_INNER already lacked it in the retail build.
  • In GeneralsMD/AIGuard.cpp, both AI_GUARD_INNER and AI_GUARD_RETURN have their attackAggressors handler removed, matching the description in the PR. AIGuardRetaliate.cpp mirrors this for AI_GUARD_RETALIATE_RETURN.

Confidence Score: 5/5

The changes are minimal and surgical — each state definition is narrowly wrapped in a preprocessor guard, leaving the retail code path completely intact and activating the fix only under the non-retail build.

All three files make consistent, parallel changes: the attackAggressors handler is removed only from states whose internal logic already manages engagement or movement, while states that legitimately need to respond to attacks (IDLE) retain the handler. The diff is small, easy to audit, and the logic matches the documented root cause.

No files require special attention — each change is self-contained within the state machine constructor and the fix pattern is applied consistently across all three files.

Important Files Changed

Filename Overview
Generals/Code/GameEngine/Source/GameLogic/AI/AIGuard.cpp Wraps AI_GUARD_RETURN state definition with #if RETAIL_COMPATIBLE_CRC to remove the attackAggressors handler on the fix path; AI_GUARD_INNER already had no such handler in the Generals codebase.
GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIGuard.cpp Both AI_GUARD_INNER and AI_GUARD_RETURN are wrapped; the attackAggressors handler is stripped from both states on the fix path, matching the ZH-specific retail behavior described in the PR.
GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIGuardRetaliate.cpp Only AI_GUARD_RETALIATE_RETURN is wrapped; AI_GUARD_RETALIATE_INNER already had no attackAggressors in the original code, so the fix is correctly scoped.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    subgraph RETAIL["RETAIL_COMPATIBLE_CRC = 1 (original retail)"]
        R1[AI_GUARD_RETURN + attackAggressors] -->|success| R2[AI_GUARD_IDLE + attackAggressors]
        R1 -->|attacked| R_ATK[AI_GUARD_ATTACK_AGGRESSOR]
        R2 -->|enemy in range| R3[AI_GUARD_INNER + attackAggressors]
        R3 -->|attacked| R_ATK
        R_ATK --> R1
    end
    subgraph FIX["RETAIL_COMPATIBLE_CRC = 0 (fix)"]
        F1[AI_GUARD_RETURN - no attackAggressors] -->|success| F2[AI_GUARD_IDLE + attackAggressors]
        F2 -->|enemy in range| F3[AI_GUARD_INNER - no attackAggressors]
        F2 -->|attacked| F_ATK[AI_GUARD_ATTACK_AGGRESSOR]
        F_ATK --> F1
    end
Loading
%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%%
flowchart TD
    subgraph RETAIL["RETAIL_COMPATIBLE_CRC = 1 (original retail)"]
        R1[AI_GUARD_RETURN + attackAggressors] -->|success| R2[AI_GUARD_IDLE + attackAggressors]
        R1 -->|attacked| R_ATK[AI_GUARD_ATTACK_AGGRESSOR]
        R2 -->|enemy in range| R3[AI_GUARD_INNER + attackAggressors]
        R3 -->|attacked| R_ATK
        R_ATK --> R1
    end
    subgraph FIX["RETAIL_COMPATIBLE_CRC = 0 (fix)"]
        F1[AI_GUARD_RETURN - no attackAggressors] -->|success| F2[AI_GUARD_IDLE + attackAggressors]
        F2 -->|enemy in range| F3[AI_GUARD_INNER - no attackAggressors]
        F2 -->|attacked| F_ATK[AI_GUARD_ATTACK_AGGRESSOR]
        F_ATK --> F1
    end
Loading

Reviews (7): Last reviewed commit: "Fixed guard (retaliation) states." | Re-trigger Greptile

Comment thread GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIGuard.cpp
Comment thread GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIGuardRetaliate.cpp
@xezon xezon added Bug Something is not working right, typically is user facing Minor Severity: Minor < Major < Critical < Blocker Unit AI Is related to unit behavior Gen Relates to Generals ZH Relates to Zero Hour labels Feb 11, 2026
@xezon

xezon commented Feb 11, 2026

Copy link
Copy Markdown

This needs more work.

Can you explain in more technical detail why and how this change is correcting the bug?

@Caball009

Caball009 commented Jun 7, 2026

Copy link
Copy Markdown

@CookieLandProjects We don't include issue links or ids (anymore). I think the comments can be reduced to something much more terse:

// TheSuperHackers @bugfix 09/04/2026 This fixes the conflicting movement and fire behavior in Guard mode when the unit is under attack.
defineState(AI_GUARD_INNER, newInstance(AIGuardInnerState)(this), AI_GUARD_OUTER, AI_GUARD_OUTER);
defineState(AI_GUARD_RETURN, newInstance(AIGuardReturnState)(this), AI_GUARD_IDLE, AI_GUARD_INNER);

...

// TheSuperHackers @bugfix 09/04/2026 This fixes the conflicting movement and fire behavior in Guard mode when the unit is under attack.
defineState(AI_GUARD_RETALIATE_RETURN, newInstance(AIGuardRetaliateReturnState)(this), AI_GUARD_RETALIATE_IDLE, AI_GUARD_RETALIATE_INNER);

and then put the rest of the explanation in a comment here and / or the first post.

Please also rebase so this can be tested with the latest main branch.

@Caball009 Caball009 added the NoRetail This fix or change is not applicable with Retail game compatibility label Jun 7, 2026
xezon
xezon previously approved these changes Jun 8, 2026

@xezon xezon left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Comment text can be polished.

Pull title needs polishing.

Needs replication to Generals.

Comment thread GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIGuard.cpp Outdated
Comment thread GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIGuard.cpp Outdated
@xezon xezon requested a review from Stubbjax June 8, 2026 19:34
@Caball009 Caball009 changed the title Fixed Guard Retaliation bugfix(ai): Fix bugged unit behavior when using guard mode when unit is under attack Jun 17, 2026
@Caball009

Copy link
Copy Markdown

I took the liberty of making changes to the code comments, so that this PR can move forward. I think the only thing that remains is testing, and I'll ask around if testers can do that.

Comment thread Core/GameEngine/Include/Common/GameDefines.h Outdated
@Caball009 Caball009 changed the title bugfix(ai): Fix bugged unit behavior when using guard mode when unit is under attack bugfix(ai): Fix bugged unit behavior when attacked during guard mode Jun 17, 2026
@Stubbjax Stubbjax dismissed their stale review June 17, 2026 05:17

Feedback has been addressed.

@DrGoldFish1

Copy link
Copy Markdown

I have tested the issue before and after the fix and tested it for air, tank and infantry units and the bug seem to be fully fixed for all unit types with guardmode. No conflicting behavior is being seen after the fix.

Comment thread GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIGuard.cpp Outdated
@xezon xezon merged commit 78edd87 into TheSuperHackers:main Jun 19, 2026
17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Bug Something is not working right, typically is user facing Gen Relates to Generals Minor Severity: Minor < Major < Critical < Blocker NoRetail This fix or change is not applicable with Retail game compatibility Unit AI Is related to unit behavior ZH Relates to Zero Hour

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants