From 110aa2d9c73765d042f3b524bcadf73d6f06d08f Mon Sep 17 00:00:00 2001 From: Bobby Battista Date: Fri, 31 Oct 2025 21:12:14 -0400 Subject: [PATCH] bugfix(ai): fix crash when AI fires China Nuclear Missile while another is under construction --- .../Source/GameLogic/AI/AIGroup.cpp | 18 ++++++++++++++++++ .../SpecialPower/SpecialPowerModule.cpp | 10 ++++++++++ .../Update/MissileLauncherBuildingUpdate.cpp | 14 ++++++++++++++ .../Source/GameLogic/AI/AIGroup.cpp | 19 +++++++++++++++++++ .../Update/MissileLauncherBuildingUpdate.cpp | 5 +++-- 5 files changed, 64 insertions(+), 2 deletions(-) diff --git a/Generals/Code/GameEngine/Source/GameLogic/AI/AIGroup.cpp b/Generals/Code/GameEngine/Source/GameLogic/AI/AIGroup.cpp index e6e3d42f69..c413d934dd 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/AI/AIGroup.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/AI/AIGroup.cpp @@ -2620,6 +2620,12 @@ void AIGroup::groupDoSpecialPower( UnsignedInt specialPowerID, UnsignedInt comma //Special powers do a lot of different things, but the top level stuff doesn't use //ai interface code. It finds the special power module and calls it directly for each object. Object *object = (*i); +#if !RETAIL_COMPATIBLE_AIGROUP + // TheSuperHackers @bugfix bobtista 01/11/2025 Prevents AI from trying to fire special powers from buildings under construction. + if (object->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION)) { + continue; + } +#endif const SpecialPowerTemplate *spTemplate = TheSpecialPowerStore->findSpecialPowerTemplateByID( specialPowerID ); if( spTemplate ) { @@ -2659,6 +2665,12 @@ void AIGroup::groupDoSpecialPowerAtLocation( UnsignedInt specialPowerID, const C //ai interface code. It finds the special power module and calls it directly for each object. Object *object = (*i); +#if !RETAIL_COMPATIBLE_AIGROUP + // TheSuperHackers @bugfix bobtista 01/11/2025 Prevents AI from trying to fire special powers from buildings under construction. + if (object->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION)) { + continue; + } +#endif const SpecialPowerTemplate *spTemplate = TheSpecialPowerStore->findSpecialPowerTemplateByID( specialPowerID ); if( spTemplate ) { @@ -2698,6 +2710,12 @@ void AIGroup::groupDoSpecialPowerAtObject( UnsignedInt specialPowerID, Object *t //ai interface code. It finds the special power module and calls it directly for each object. Object *object = (*i); +#if !RETAIL_COMPATIBLE_AIGROUP + // TheSuperHackers @bugfix bobtista 01/11/2025 Prevents AI from trying to fire special powers from buildings under construction. + if (object->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION)) { + continue; + } +#endif const SpecialPowerTemplate *spTemplate = TheSpecialPowerStore->findSpecialPowerTemplateByID( specialPowerID ); if( spTemplate ) { diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/SpecialPower/SpecialPowerModule.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/SpecialPower/SpecialPowerModule.cpp index 4d164ee577..be1f028c72 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/SpecialPower/SpecialPowerModule.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/SpecialPower/SpecialPowerModule.cpp @@ -406,6 +406,16 @@ Bool SpecialPowerModule::initiateIntentToDoSpecialPower( const Object *targetObj } } + // TheSuperHackers @bugfix bobtista 01/11/2025 Prevents AI from consuming special power timer when MissileLauncherBuildingUpdate is not ready. +#if RETAIL_COMPATIBLE_CRC + // TheSuperHackers @info we need to leave early if we are in the MissileLauncherBuildingUpdate crash fix codepath + if (m_availableOnFrame == 0xFFFFFFFF) + { + DEBUG_ASSERTCRASH(!valid, ("Using MissileLauncherBuildingUpdate escape path when valid is set to true")); + return false; + } +#endif + //If we depend on our update module to trigger the special power, make sure we have the //appropriate update module! if( !valid && getSpecialPowerModuleData()->m_updateModuleStartsAttack ) diff --git a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/MissileLauncherBuildingUpdate.cpp b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/MissileLauncherBuildingUpdate.cpp index e3eb7351ef..650d2e83f8 100644 --- a/Generals/Code/GameEngine/Source/GameLogic/Object/Update/MissileLauncherBuildingUpdate.cpp +++ b/Generals/Code/GameEngine/Source/GameLogic/Object/Update/MissileLauncherBuildingUpdate.cpp @@ -200,6 +200,20 @@ void MissileLauncherBuildingUpdate::switchToState(DoorStateType dst) //------------------------------------------------------------------------------------------------- Bool MissileLauncherBuildingUpdate::initiateIntentToDoSpecialPower( const SpecialPowerTemplate *specialPowerTemplate, const Object *targetObj, const Coord3D *targetPos, const Waypoint *way, UnsignedInt commandOptions ) { + // TheSuperHackers @bugfix bobtista 01/11/2025 Prevents crash when AI fires China Nuclear Missile while another is under construction. + if (!m_specialPowerModule) { +#if RETAIL_COMPATIBLE_CRC + Object* us = getObject(); + us->getSpecialPowerModule(specialPowerTemplate)->setReadyFrame(0xFFFFFFFF); +#endif + return FALSE; + } + + if( m_specialPowerModule->getSpecialPowerTemplate() != specialPowerTemplate ) + { + return FALSE; + } + #if defined(RTS_DEBUG) DEBUG_ASSERTCRASH(!TheGlobalData->m_specialPowerUsesDelay || m_doorState == DOOR_OPEN, ("door is not fully open when specialpower is fired!")); #endif diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIGroup.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIGroup.cpp index 755cb89ef4..6dc994272d 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIGroup.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIGroup.cpp @@ -2683,6 +2683,12 @@ void AIGroup::groupDoSpecialPower( UnsignedInt specialPowerID, UnsignedInt comma //Special powers do a lot of different things, but the top level stuff doesn't use //ai interface code. It finds the special power module and calls it directly for each object. Object *object = (*i); +#if !RETAIL_COMPATIBLE_AIGROUP + // TheSuperHackers @bugfix bobtista 01/11/2025 Prevents AI from trying to fire special powers from buildings under construction. + if (object->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION)) { + continue; + } +#endif const SpecialPowerTemplate *spTemplate = TheSpecialPowerStore->findSpecialPowerTemplateByID( specialPowerID ); if( spTemplate ) { @@ -2731,6 +2737,13 @@ void AIGroup::groupDoSpecialPowerAtLocation( UnsignedInt specialPowerID, const C // destroys the AIGroup list, in order to keep the selection sync'ed with the group. // M Lorenzen... 8/23/03 +#if !RETAIL_COMPATIBLE_AIGROUP + // TheSuperHackers @bugfix bobtista 01/11/2025 Prevents AI from trying to fire special powers from buildings under construction. + if (object->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION)) { + continue; + } +#endif + const SpecialPowerTemplate *spTemplate = TheSpecialPowerStore->findSpecialPowerTemplateByID( specialPowerID ); if( spTemplate ) { @@ -2771,6 +2784,12 @@ void AIGroup::groupDoSpecialPowerAtObject( UnsignedInt specialPowerID, Object *t //ai interface code. It finds the special power module and calls it directly for each object. Object *object = (*i); +#if !RETAIL_COMPATIBLE_AIGROUP + // TheSuperHackers @bugfix bobtista 01/11/2025 Prevents AI from trying to fire special powers from buildings under construction. + if (object->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION)) { + continue; + } +#endif const SpecialPowerTemplate *spTemplate = TheSpecialPowerStore->findSpecialPowerTemplateByID( specialPowerID ); if( spTemplate ) { diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/MissileLauncherBuildingUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/MissileLauncherBuildingUpdate.cpp index e1c641345f..2e1f5b1564 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/MissileLauncherBuildingUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/MissileLauncherBuildingUpdate.cpp @@ -201,14 +201,15 @@ void MissileLauncherBuildingUpdate::switchToState(DoorStateType dst) //------------------------------------------------------------------------------------------------- Bool MissileLauncherBuildingUpdate::initiateIntentToDoSpecialPower( const SpecialPowerTemplate *specialPowerTemplate, const Object *targetObj, const Coord3D *targetPos, const Waypoint *way, UnsignedInt commandOptions ) { -#if RETAIL_COMPATIBLE_CRC // TheSuperHackers @bugfix Mauller 29/06/2025 prevent a game crash when told to launch before ready to do so + // TheSuperHackers @bugfix bobtista 01/11/2025 Prevents crash when AI fires China Nuclear Missile while another is under construction. if (!m_specialPowerModule) { +#if RETAIL_COMPATIBLE_CRC Object* us = getObject(); us->getSpecialPowerModule(specialPowerTemplate)->setReadyFrame(0xFFFFFFFF); +#endif return FALSE; } -#endif if( m_specialPowerModule->getSpecialPowerTemplate() != specialPowerTemplate ) {