-
Notifications
You must be signed in to change notification settings - Fork 230
Fix for Deployed units (Nuke Cannon) #3035
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 | ||
| // 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. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. imperative mood is preferred over first person plural ("we") There was a problem hiding this comment. Choose a reason for hiding this commentThe 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?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. There was a problem hiding this comment. Choose a reason for hiding this commentThe 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?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) |
||
| 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; | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
| { | ||
|
|
@@ -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 | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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()) | ||
| { | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When the target leaves range on the update where the deployment timer expires, the preceding timer block changes Knowledge Base Used: GameLogic simulation Prompt To Fix With AIThis 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. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 ) | ||
|
|
||
There was a problem hiding this comment.
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