Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 67 additions & 0 deletions GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIGuard.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -454,7 +454,53 @@ StateReturnType AIGuardInnerState::update()
m_exitConditions.m_center = *targetToGuard->getPosition();
}

// TheSuperHackers @bugfix CookieLandProjects 31/07/2026 - This keeps deployed units from moving/undeploying immediately

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

a more concise comment would be easier to read and understand

// after killing a target if there are still more enemies available in the guard area.
// The original code would delete the attack state and create a new one, which would cause the unit to undeploy and move to the next target.
#if RETAIL_COMPATIBLE_CRC
return m_attackState->update();
#else
StateReturnType ret = m_attackState->update();

if (ret == STATE_CONTINUE || IS_STATE_SLEEP(ret))
return ret;

// Attack sub-state finished (success or failure).
// Clean it up.
m_attackState->onExit(EXIT_NORMAL);
deleteInstance(m_attackState);
m_attackState = nullptr;

// If attack finished successfully (target died), check instantly for another enemy in guard range.
if (ret == STATE_SUCCESS)
{
// If we find another enemy in guard range. Recreate the attack state and continue attacking.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

imperative mood is preferred over first person plural ("we")

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Shouldn't the cannon be deployed anyways in guard or stop mode,, even if there is no enemy?

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.

Yes. But on every kill the nuke cannon decides to redeploy then retarget. Check the mentioned issue for reference.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Yes, I'm aware of that. But what if the nuke cannon has a kill, but there are no other targets in range. What I make up from your code is that it will still redeploy - as you only seem to stop the redeploy if there is another target. Or am I reading that wrong?

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 nuke cannon will ONLY redeploy if he is not near the guard center (expected behavior)
Since if u set to guard a specific area, he has to first clear out every enemy within that guard area before going to center and deploying for the last time, never to undeploy again (unless something gets in its MinimumAttackRange).

if (getGuardMachine()->lookForInnerTarget())
{
Object* newTargetToGuard = getGuardMachine()->findTargetToGuardByID();
Coord3D pos = newTargetToGuard ? *newTargetToGuard->getPosition() : *getGuardMachine()->getPositionToGuard();
Object* nemesis = TheGameLogic->findObjectByID(getGuardMachine()->getNemesisID());
if (nemesis)
{
m_exitConditions.m_center = pos;
m_exitConditions.m_radiusSqr = sqr(AIGuardMachine::getStdGuardRange(getMachineOwner()));
m_exitConditions.m_conditionsToConsider = (ExitConditions::ATTACK_ExitIfOutsideRadius |
ExitConditions::ATTACK_ExitIfNoUnitFound);

m_attackState = newInstance(AIAttackState)(getMachine(), false, true, false, &m_exitConditions);
m_attackState->getMachine()->setGoalObject(nemesis);

StateReturnType enterRet = m_attackState->onEnter();
if (enterRet == STATE_CONTINUE)
return STATE_CONTINUE;
return enterRet;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Add whiteline for readability.

}
}
}

// No new target found, state the attack states result (success/failure).
return ret;
#endif
}
else if (m_enterState)
{
Expand Down Expand Up @@ -656,6 +702,27 @@ StateReturnType AIGuardReturnState::onEnter()
{
area->getCenterPoint(&m_goalPosition);
}

// TheSuperHackers @bugfix CookieLandProjects 31/07/2026 - This keeps deployed units from moving/undeploying even though we are already

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Don't understand the comment

// at the guard position.
#if !RETAIL_COMPATIBLE_CRC
Object* owner = getMachineOwner();
if (owner && owner->getPosition())
{
Coord3D myPos = *owner->getPosition();
Coord3D delta;
delta.x = myPos.x - m_goalPosition.x;
delta.y = myPos.y - m_goalPosition.y;
delta.z = myPos.z - m_goalPosition.z;
Real closeSqr = sqr(CLOSE_ENOUGH);
if (delta.lengthSqr() <= closeSqr)
{
// Already close enough so dont move.
return STATE_SUCCESS;
}
}
#endif

AIUpdateInterface *ai = getMachineOwner()->getAIUpdateInterface();
if (ai && ai->isDoingGroundMovement())
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,24 @@ UpdateSleepTime DeployStyleAIUpdate::update()
}
}

// TheSuperHackers @bugfix CookieLandProjects 31/07/2026 - This keeps deployed units from fully deploying even though their target exited the range,

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

The comment is confusing. "This keeps deployed units from fully deploying"

// instead reverse the deploy.
#if !RETAIL_COMPATIBLE_CRC
if (m_state == DEPLOY && isTryingToAttack && !isInRange)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Deployment reversal misses expiry frame

When the target leaves range on the update where the deployment timer expires, the preceding timer block changes m_state from DEPLOY to READY_TO_ATTACK before this condition runs. The m_state == DEPLOY check then prevents reversal, causing the unit to finish deploying despite its target already being out of range.

Knowledge Base Used: GameLogic simulation

Prompt To Fix With AI
This is a comment left during a code review.
Path: GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DeployStyleAIUpdate.cpp
Line: 149

Comment:
**Deployment reversal misses expiry frame**

When the target leaves range on the update where the deployment timer expires, the preceding timer block changes `m_state` from `DEPLOY` to `READY_TO_ATTACK` before this condition runs. The `m_state == DEPLOY` check then prevents reversal, causing the unit to finish deploying despite its target already being out of range.

**Knowledge Base Used:** [GameLogic simulation](https://app.greptile.com/thesuperhackers/-/custom-context/knowledge-base/thesuperhackers/generalsgamecode/-/docs/gamelogic-simulation.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

{
if (m_frameToWaitForDeploy != 0)
{
// Reverse the deploy at its current frame so we dont finish deploying fully.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

use imperative mood for comments

setMyState(UNDEPLOY, TRUE);
}
else
{
// No pending deploy timer? Make sure we transition to undeploy state.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

use imperative mood for comments

setMyState(UNDEPLOY);
}
}
#endif

if( isInRange || isInGuardIdleState )
{
switch( m_state )
Expand Down
Loading