bugfix: Make healed units follow building rally point on exit#1822
bugfix: Make healed units follow building rally point on exit#1822tintinhamans wants to merge 3 commits intoTheSuperHackers:mainfrom
Conversation
Generals/Code/GameEngine/Source/GameLogic/Object/Contain/HealContain.cpp
Outdated
Show resolved
Hide resolved
Generals/Code/GameEngine/Source/GameLogic/Object/Contain/HealContain.cpp
Outdated
Show resolved
Hide resolved
Generals/Code/GameEngine/Source/GameLogic/Object/Contain/HealContain.cpp
Outdated
Show resolved
Hide resolved
Generals/Code/GameEngine/Source/GameLogic/Object/Contain/HealContain.cpp
Outdated
Show resolved
Hide resolved
Generals/Code/GameEngine/Source/GameLogic/Object/Contain/HealContain.cpp
Outdated
Show resolved
Hide resolved
610b09f to
8fe5984
Compare
|
| Filename | Overview |
|---|---|
| Generals/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp | Refactors exitObjectViaDoor to use getRallyPoint() instead of checking m_rallyPointExists directly; adds a #if !RETAIL_COMPATIBLE_CRC-guarded fallback in getRallyPoint() to delegate to the primary exit interface's rally point, enabling HealContain to propagate the building's rally point. Self-recursion guard (primaryExit != this) is correct. |
| Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp | Adds #if !RETAIL_COMPATIBLE_CRC-guarded rally point move after healing is complete and takeoff begins. The pattern follows established aiDoCommand pending-command mechanics: calling aiMoveToPosition while m_flightStatus != CHINOOK_FLYING causes aiDoCommand to store the command and set m_hasPendingCommand = true, dispatched when the Chinook becomes idle post-takeoff. Note: aiDoCommand also clears m_airfieldForHealing (to INVALID_ID) via the non-evacuate path, but pp->setHealee(false) was already called so this is benign. |
| Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/JetAIUpdate.cpp | Adds #if !RETAIL_COMPATIBLE_CRC-guarded rally point move after helipad-produced aircraft finish healing. Since friend_setAllowAirLoco(true) and setState(TAKING_OFF_AWAIT_CLEARANCE) are called first (setting TAKEOFF_IN_PROGRESS = true via onEnter), the subsequent aiMoveToPosition correctly routes through the TAKEOFF_IN_PROGRESS guard in aiDoCommand, storing the command as pending for dispatch after takeoff completes. The same-frame else if (HAS_PENDING_COMMAND) re-dispatch re-queues the command safely. |
| GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp | Mirror of the Generals/ counterpart — identical changes applied correctly to the Zero Hour codebase. |
| GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp | Mirror of the Generals/ counterpart — identical healing rally point logic applied to the Zero Hour Chinook. Same behavioral analysis applies. |
| GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/JetAIUpdate.cpp | Mirror of the Generals/ counterpart — identical healing rally point logic applied to the Zero Hour jet/helicopter. Same behavioral analysis applies. |
Sequence Diagram
sequenceDiagram
participant Update as ChinookAIUpdate / JetAIUpdate
participant SM as StateMachine
participant PP as ParkingPlace
participant AI as AIInterface
participant Exit as ExitInterface (Airfield)
Update->>PP: setHealee(unit, false)
Update->>SM: setState(TAKING_OFF / TAKING_OFF_AWAIT_CLEARANCE)
Note over SM: onEnter() sets TAKEOFF_IN_PROGRESS=true
Update->>Exit: getRallyPoint()
Exit-->>Update: Coord3D* rallyPoint (if set)
Update->>AI: aiMoveToPosition(rallyPoint)
AI->>AI: aiDoCommand(AICMD_MOVE_TO_POSITION)
Note over AI: TAKEOFF_IN_PROGRESS=true →<br/>store as m_mostRecentCommand,<br/>set HAS_PENDING_COMMAND=true, return
Note over SM: Takeoff animation completes,<br/>TAKEOFF_IN_PROGRESS=false
SM-->>Update: unit is now idle (flying)
Update->>AI: dispatch m_mostRecentCommand (move to rally)
AI->>AI: aiDoCommand(AICMD_MOVE_TO_POSITION)
Note over AI: ALLOW_AIR_LOCO=true, not landing/takeoff →<br/>AIUpdateInterface::aiDoCommand executes move
Last reviewed commit: 7c656b2
8fe5984 to
faf8a09
Compare
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/HealContain.cpp
Outdated
Show resolved
Hide resolved
faf8a09 to
4e9ef5f
Compare
4e9ef5f to
e87b99b
Compare
Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp
Outdated
Show resolved
Hide resolved
Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/JetAIUpdate.cpp
Outdated
Show resolved
Hide resolved
Additional Comments (2)
Prompt To Fix With AIThis is a comment left during a code review.
Path: GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp
Line: 1038:1039
Comment:
`airfield` could be `nullptr` if the airfield was destroyed or doesn't exist. Need null check before calling `getObjectExitInterface()`.
```suggestion
Object *airfield = TheGameLogic->findObjectByID( m_airfieldForHealing );
if (airfield)
{
const Coord3D *rp = airfield->getObjectExitInterface()->getRallyPoint();
if( rp )
{
AICommandParms parms( AICMD_MOVE_TO_POSITION, CMD_FROM_AI );
parms.m_pos = *rp;
m_pendingCommand.store( parms );
m_hasPendingCommand = true;
}
}
```
How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix With AIThis is a comment left during a code review.
Path: GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/JetAIUpdate.cpp
Line: 1782:1783
Comment:
`airfield` could be `nullptr` if the airfield was destroyed or doesn't exist (see line 1717-1718 in same file for precedent). Need null check before calling `getObjectExitInterface()`.
```suggestion
Object *airfield = TheGameLogic->findObjectByID( jet->getProducerID() );
if (airfield)
{
const Coord3D *rp = airfield->getObjectExitInterface()->getRallyPoint();
if( rp )
{
AICommandParms parms( AICMD_MOVE_TO_POSITION, CMD_FROM_AI );
parms.m_pos = *rp;
m_mostRecentCommand.store( parms );
setFlag(HAS_PENDING_COMMAND, true);
}
}
```
How can I resolve this? If you propose a fix, please make it concise. |
e87b99b to
ffa98ad
Compare
Generals/Code/GameEngine/Source/GameLogic/Object/Contain/HealContain.cpp
Show resolved
Hide resolved
Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp
Outdated
Show resolved
Hide resolved
Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/JetAIUpdate.cpp
Outdated
Show resolved
Hide resolved
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/JetAIUpdate.cpp
Outdated
Show resolved
Hide resolved
Generals/Code/GameEngine/Source/GameLogic/Object/Contain/HealContain.cpp
Outdated
Show resolved
Hide resolved
Generals/Code/GameEngine/Source/GameLogic/Object/Contain/HealContain.cpp
Outdated
Show resolved
Hide resolved
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/HealContain.cpp
Outdated
Show resolved
Hide resolved
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp
Outdated
Show resolved
Hide resolved
| const Coord3D* rp = buildingExit->getRallyPoint(); | ||
| if (rp) | ||
| { | ||
| setRallyPoint(rp); |
There was a problem hiding this comment.
This looks strange. Basically the Rally Point of the HealContain is now set/updated after every heal. Which begs the question, why is the Rally Point not updated for the Heal Contain when the Rally Point of the Object Exit Interface is updated? That would be the most logical and efficient way (and maybe that is what was supposed to happen but is broken somewhere).
There was a problem hiding this comment.
I changed approach on this and made fallback on OpenContain instead.
| const Coord3D *rp = exitInterface ? exitInterface->getRallyPoint() : nullptr; | ||
| if( rp ) | ||
| { | ||
| AICommandParms parms( AICMD_MOVE_TO_POSITION, CMD_FROM_AI ); |
There was a problem hiding this comment.
Is this the correct way to do the movement for this purpose?
For reference
OpenContain::exitObjectViaDoor uses ai->aiFollowPath( &exitPath, me, CMD_FROM_AI ) to go to the Rally Point.
DefaultProductionExitUpdate::exitObjectViaDoor uses ai->aiFollowExitProductionPath( &exitPath, creationObject, CMD_FROM_AI ) to go to the Rally Point.
Helicopter looks special, so perhaps that is fine. Unclear.
There was a problem hiding this comment.
I think this is correct. The Chinook/helicopter is landed when healing and needs to take off first - it cannot follow a path directly. This matches how ParkingPlaceBehavior::exitObjectViaDoor handles helipad-produced aircraft:
I did see from above that I can use aiMoveToPosition instead of storing pending command manually since aiDoCommand already handles takeoff in-progress and queues move for us.
ffa98ad to
e4eac2e
Compare
GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp
Outdated
Show resolved
Hide resolved
2c1be60 to
5a3b849
Compare
xezon
left a comment
There was a problem hiding this comment.
Can you explain the difference between the waypointing in OpenContain and in ChinookAIUpdate? Which one is used in what event?
| if (getObject()) | ||
| { | ||
| ExitInterface *primaryExit = getObject()->getObjectExitInterface(); | ||
| if (primaryExit && primaryExit != static_cast<ExitInterface *>(const_cast<OpenContain *>(this))) |
There was a problem hiding this comment.
!= static_cast<const ExitInterface *>(this)
Generals/Code/GameEngine/Source/GameLogic/Object/Contain/OpenContain.cpp
Show resolved
Hide resolved
| exitPath.push_back(endPosition); | ||
| exitPath.push_back(endPosition); // Do it twice, in case units stack up due to brief flying. jba. | ||
| #if !RETAIL_COMPATIBLE_CRC | ||
| // TheSuperHackers @bugfix arcticdolphin 02/03/2026 Use primary exit interface rally point if available. |
War factory repair is unrelated - it uses the dock system ( |
5a3b849 to
c1204d1
Compare
c1204d1 to
7c656b2
Compare
Units will now follow building rally point after healing.
Closes #221