From b2dfd6ad02e49f254a75a29e29d52efeffab5c4e Mon Sep 17 00:00:00 2001 From: Stubbjax Date: Tue, 4 Nov 2025 02:25:17 +1100 Subject: [PATCH 1/4] bugfix: Prevent manually ejecting rappelling units during combat drops --- .../Object/Update/AIUpdate/ChinookAIUpdate.cpp | 18 ++++++++++++++++-- .../Object/Update/AIUpdate/ChinookAIUpdate.cpp | 18 ++++++++++++++++-- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp index 7c13cab791..4cf534d025 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp @@ -367,6 +367,9 @@ class ChinookCombatDropState : public State if (rappeller == NULL || rappeller->isEffectivelyDead() || !rappeller->isAboveTerrain() || rappeller->isContained()) #endif { + if (rappeller) + rappeller->clearStatus(MAKE_OBJECT_STATUS_MASK(OBJECT_STATUS_IS_USING_ABILITY)); + oit = it->rappellerIDs.erase(oit); } else @@ -587,6 +590,8 @@ class ChinookCombatDropState : public State Object* rappeller = getPotentialRappeller(obj); if (rappeller != NULL) { + rappeller->setStatus(MAKE_OBJECT_STATUS_MASK(OBJECT_STATUS_IS_USING_ABILITY)); + ExitInterface *exitInterface = obj->getObjectExitInterface(); ExitDoorType exitDoor = exitInterface ? exitInterface->reserveDoorForExit(rappeller->getTemplate(), rappeller) : DOOR_NONE_AVAILABLE; if(exitDoor != DOOR_NONE_AVAILABLE) @@ -983,8 +988,17 @@ ObjectID ChinookAIUpdate::getBuildingToNotPathAround() const //------------------------------------------------------------------------------------------------- AIFreeToExitType ChinookAIUpdate::getAiFreeToExit(const Object* exiter) const { - if (m_flightStatus == CHINOOK_LANDED - || (m_flightStatus == CHINOOK_DOING_COMBAT_DROP && exiter->isKindOf(KINDOF_CAN_RAPPEL))) + if (m_flightStatus == CHINOOK_DOING_COMBAT_DROP && exiter->isKindOf(KINDOF_CAN_RAPPEL)) + { +#if !RETAIL_COMPATIBLE_CRC + if (!exiter->testStatus(OBJECT_STATUS_IS_USING_ABILITY)) + return WAIT_TO_EXIT; +#endif + + return FREE_TO_EXIT; + } + + if (m_flightStatus == CHINOOK_LANDED) return FREE_TO_EXIT; return WAIT_TO_EXIT; diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp index c6fca75821..ca18081889 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp @@ -368,6 +368,9 @@ class ChinookCombatDropState : public State if (rappeller == NULL || rappeller->isEffectivelyDead() || !rappeller->isAboveTerrain() || rappeller->isContained()) #endif { + if (rappeller) + rappeller->clearStatus(MAKE_OBJECT_STATUS_MASK(OBJECT_STATUS_IS_USING_ABILITY)); + oit = it->rappellerIDs.erase(oit); } else @@ -588,6 +591,8 @@ class ChinookCombatDropState : public State Object* rappeller = getPotentialRappeller(obj); if (rappeller != NULL) { + rappeller->setStatus(MAKE_OBJECT_STATUS_MASK(OBJECT_STATUS_IS_USING_ABILITY)); + ExitInterface *exitInterface = obj->getObjectExitInterface(); ExitDoorType exitDoor = exitInterface ? exitInterface->reserveDoorForExit(rappeller->getTemplate(), rappeller) : DOOR_NONE_AVAILABLE; if(exitDoor != DOOR_NONE_AVAILABLE) @@ -1045,8 +1050,17 @@ ObjectID ChinookAIUpdate::getBuildingToNotPathAround() const //------------------------------------------------------------------------------------------------- AIFreeToExitType ChinookAIUpdate::getAiFreeToExit(const Object* exiter) const { - if (m_flightStatus == CHINOOK_LANDED - || (m_flightStatus == CHINOOK_DOING_COMBAT_DROP && exiter->isKindOf(KINDOF_CAN_RAPPEL))) + if (m_flightStatus == CHINOOK_DOING_COMBAT_DROP && exiter->isKindOf(KINDOF_CAN_RAPPEL)) + { +#if !RETAIL_COMPATIBLE_CRC + if (!exiter->testStatus(OBJECT_STATUS_IS_USING_ABILITY)) + return WAIT_TO_EXIT; +#endif + + return FREE_TO_EXIT; + } + + if (m_flightStatus == CHINOOK_LANDED) return FREE_TO_EXIT; return WAIT_TO_EXIT; From f5f9e77ed0a5c02351dd80e148b770e0ea165b80 Mon Sep 17 00:00:00 2001 From: Stubbjax Date: Tue, 4 Nov 2025 11:43:51 +1100 Subject: [PATCH 2/4] refactor: Simplify logic flow --- .../GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp | 4 +--- .../GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp index 4cf534d025..e6f8d8bc1f 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp @@ -367,9 +367,6 @@ class ChinookCombatDropState : public State if (rappeller == NULL || rappeller->isEffectivelyDead() || !rappeller->isAboveTerrain() || rappeller->isContained()) #endif { - if (rappeller) - rappeller->clearStatus(MAKE_OBJECT_STATUS_MASK(OBJECT_STATUS_IS_USING_ABILITY)); - oit = it->rappellerIDs.erase(oit); } else @@ -603,6 +600,7 @@ class ChinookCombatDropState : public State DEBUG_CRASH(("rappeller is not free to exit... what?")); } + rappeller->clearStatus(MAKE_OBJECT_STATUS_MASK(OBJECT_STATUS_IS_USING_ABILITY)); rappeller->setTransformMatrix(&it->dropStartMtx); AIUpdateInterface* rappellerAI = rappeller ? rappeller->getAIUpdateInterface() : NULL; diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp index ca18081889..70ed150539 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp @@ -368,9 +368,6 @@ class ChinookCombatDropState : public State if (rappeller == NULL || rappeller->isEffectivelyDead() || !rappeller->isAboveTerrain() || rappeller->isContained()) #endif { - if (rappeller) - rappeller->clearStatus(MAKE_OBJECT_STATUS_MASK(OBJECT_STATUS_IS_USING_ABILITY)); - oit = it->rappellerIDs.erase(oit); } else @@ -604,6 +601,7 @@ class ChinookCombatDropState : public State DEBUG_CRASH(("rappeller is not free to exit... what?")); } + rappeller->clearStatus(MAKE_OBJECT_STATUS_MASK(OBJECT_STATUS_IS_USING_ABILITY)); rappeller->setTransformMatrix(&it->dropStartMtx); AIUpdateInterface* rappellerAI = rappeller ? rappeller->getAIUpdateInterface() : NULL; From ba31afa7bd9d8d9931f75670c9dbea018cc992aa Mon Sep 17 00:00:00 2001 From: Stubbjax Date: Tue, 4 Nov 2025 11:56:42 +1100 Subject: [PATCH 3/4] tweak: Add CRC flags --- .../GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp | 4 ++++ .../GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp index e6f8d8bc1f..ee8bb90c5a 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp @@ -587,7 +587,9 @@ class ChinookCombatDropState : public State Object* rappeller = getPotentialRappeller(obj); if (rappeller != NULL) { +#if !RETAIL_COMPATIBLE_CRC rappeller->setStatus(MAKE_OBJECT_STATUS_MASK(OBJECT_STATUS_IS_USING_ABILITY)); +#endif ExitInterface *exitInterface = obj->getObjectExitInterface(); ExitDoorType exitDoor = exitInterface ? exitInterface->reserveDoorForExit(rappeller->getTemplate(), rappeller) : DOOR_NONE_AVAILABLE; @@ -600,7 +602,9 @@ class ChinookCombatDropState : public State DEBUG_CRASH(("rappeller is not free to exit... what?")); } +#if !RETAIL_COMPATIBLE_CRC rappeller->clearStatus(MAKE_OBJECT_STATUS_MASK(OBJECT_STATUS_IS_USING_ABILITY)); +#endif rappeller->setTransformMatrix(&it->dropStartMtx); AIUpdateInterface* rappellerAI = rappeller ? rappeller->getAIUpdateInterface() : NULL; diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp index 70ed150539..214608fc8b 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp @@ -588,7 +588,9 @@ class ChinookCombatDropState : public State Object* rappeller = getPotentialRappeller(obj); if (rappeller != NULL) { +#if !RETAIL_COMPATIBLE_CRC rappeller->setStatus(MAKE_OBJECT_STATUS_MASK(OBJECT_STATUS_IS_USING_ABILITY)); +#endif ExitInterface *exitInterface = obj->getObjectExitInterface(); ExitDoorType exitDoor = exitInterface ? exitInterface->reserveDoorForExit(rappeller->getTemplate(), rappeller) : DOOR_NONE_AVAILABLE; @@ -601,7 +603,9 @@ class ChinookCombatDropState : public State DEBUG_CRASH(("rappeller is not free to exit... what?")); } +#if !RETAIL_COMPATIBLE_CRC rappeller->clearStatus(MAKE_OBJECT_STATUS_MASK(OBJECT_STATUS_IS_USING_ABILITY)); +#endif rappeller->setTransformMatrix(&it->dropStartMtx); AIUpdateInterface* rappellerAI = rappeller ? rappeller->getAIUpdateInterface() : NULL; From 03d2bf07de5c6126ee0be47bbda4ce1c0a310bca Mon Sep 17 00:00:00 2001 From: Stubbjax Date: Tue, 4 Nov 2025 11:58:25 +1100 Subject: [PATCH 4/4] docs: Add comments --- .../Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp | 1 + .../Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp index ee8bb90c5a..dd931b1dfe 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp @@ -588,6 +588,7 @@ class ChinookCombatDropState : public State if (rappeller != NULL) { #if !RETAIL_COMPATIBLE_CRC + // ChinookAIUpdate::getAiFreeToExit is dependent on this status. rappeller->setStatus(MAKE_OBJECT_STATUS_MASK(OBJECT_STATUS_IS_USING_ABILITY)); #endif diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp index 214608fc8b..f68f9e8c93 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/ChinookAIUpdate.cpp @@ -589,6 +589,7 @@ class ChinookCombatDropState : public State if (rappeller != NULL) { #if !RETAIL_COMPATIBLE_CRC + // ChinookAIUpdate::getAiFreeToExit is dependent on this status. rappeller->setStatus(MAKE_OBJECT_STATUS_MASK(OBJECT_STATUS_IS_USING_ABILITY)); #endif