diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/SpecialPowerModule.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/SpecialPowerModule.h index e731c516fa9..c670abb51de 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/SpecialPowerModule.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Module/SpecialPowerModule.h @@ -49,7 +49,7 @@ class SpecialPowerModuleInterface virtual Bool isModuleForPower( const SpecialPowerTemplate *specialPowerTemplate ) const = 0; virtual Bool isReady() const = 0; -// This is the althernate way to one-at-a-time BlackLotus' specials; we'll keep it commented her until Dustin decides, or until 12/10/02 +// This is the alternate way to one-at-a-time BlackLotus' specials; we'll keep it commented her until Dustin decides, or until 12/10/02 // virtual Bool isBusy() const = 0; virtual Real getPercentReady() const = 0; virtual UnsignedInt getReadyFrame() const = 0; @@ -70,6 +70,7 @@ class SpecialPowerModuleInterface //If the special power launches a construction site, we need to know the final product for placement purposes. virtual const ThingTemplate* getReferenceThingTemplate() const = 0; + virtual void onConstructionCompleted() = 0; }; //------------------------------------------------------------------------------------------------- @@ -162,8 +163,12 @@ class SpecialPowerModule : public BehaviorModule, //If the special power launches a construction site, we need to know the final product for placement purposes. virtual const ThingTemplate* getReferenceThingTemplate() const { return nullptr; } + virtual void onConstructionCompleted(); + protected: + void init(); + void initCountdown(); Bool initiateIntentToDoSpecialPower( const Object *targetObj, const Coord3D *targetPos, const Waypoint *way, UnsignedInt commandOptions ); void triggerSpecialPower( const Coord3D *location ); void createViewObject( const Coord3D *location ); diff --git a/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h b/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h index 42d92543f71..69d1874ab63 100644 --- a/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h +++ b/GeneralsMD/Code/GameEngine/Include/GameLogic/Object.h @@ -208,6 +208,8 @@ class Object : public Thing, public Snapshot Bool isAirborneTarget() const { return m_status.test( OBJECT_STATUS_AIRBORNE_TARGET ); } ///< Our locomotor will control marking us as a valid target for anti air weapons or not Bool isUsingAirborneLocomotor() const; ///< returns true if the current locomotor is an airborne one + void onConstructionCompleted(); + /// central place for us to put any additional capture logic void onCapture( Player *oldOwner, Player *newOwner ); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIPlayer.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIPlayer.cpp index 72de14856e0..82c4ab22abf 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIPlayer.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/AI/AIPlayer.cpp @@ -127,8 +127,8 @@ void AIPlayer::onStructureProduced( Object *factory, Object *bldg ) info->setUnderConstruction(false); bldg->updateObjValuesFromMapProperties(&d); - // clear the under construction status - bldg->clearStatus( MAKE_OBJECT_STATUS_MASK2( OBJECT_STATUS_UNDER_CONSTRUCTION, OBJECT_STATUS_RECONSTRUCTING ) ); + + bldg->onConstructionCompleted(); // UnderConstruction just cleared, so update our upgrades bldg->updateUpgradeModules(); @@ -470,8 +470,7 @@ Object *AIPlayer::buildStructureNow(const ThingTemplate *bldgPlan, BuildListInfo info->setObjectID( bldg->getID() ); info->setObjectTimestamp( TheGameLogic->getFrame()+1 ); // has to be non-zero, so just add 1. - // clear the under construction status - bldg->clearStatus( MAKE_OBJECT_STATUS_MASK2( OBJECT_STATUS_UNDER_CONSTRUCTION, OBJECT_STATUS_RECONSTRUCTING ) ); + bldg->onConstructionCompleted(); // UnderConstruction just cleared, so update our upgrades bldg->updateUpgradeModules(); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp index 19ff1670fce..721bf639323 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Object.cpp @@ -4565,6 +4565,35 @@ void Object::removeUpgrade( const UpgradeTemplate *upgradeT ) } } +void Object::onConstructionCompleted() +{ + if (!testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION)) + { + // Was already signaled completed. + DEBUG_ASSERTCRASH(!testStatus(OBJECT_STATUS_RECONSTRUCTING), ("Unexpected status")); + return; + } + + DEBUG_ASSERTCRASH( + getConstructionPercent() == (Real)CONSTRUCTION_COMPLETE || + getConstructionPercent() >= 100.0f, + ("Is the construction really completed yet?")); + + // clear the under construction status + clearStatus(MAKE_OBJECT_STATUS_MASK2( + OBJECT_STATUS_UNDER_CONSTRUCTION, + OBJECT_STATUS_RECONSTRUCTING + )); + + // tell all special powers that the construction has completed + for(BehaviorModule** m = getBehaviorModules(); *m; ++m) + { + SpecialPowerModuleInterface* sp = (*m)->getSpecialPower(); + if (sp) + sp->onConstructionCompleted(); + } +} + //------------------------------------------------------------------------------------------------- /** Central point for onCapture logic */ //------------------------------------------------------------------------------------------------- diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/SpecialPower/OCLSpecialPower.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/SpecialPower/OCLSpecialPower.cpp index 01dbad565f9..98c9e49cd73 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/SpecialPower/OCLSpecialPower.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/SpecialPower/OCLSpecialPower.cpp @@ -177,8 +177,8 @@ void OCLSpecialPower::doSpecialPowerAtLocation( const Coord3D *loc, Real angle, SpecialPowerModule::doSpecialPowerAtLocation( &targetCoord, angle, commandOptions ); #if RETAIL_COMPATIBLE_CRC - // TheSuperHackers @info we need to leave early if we are in the MissileLauncherBuildingUpdate crash fix codepath - if (m_availableOnFrame == 0xFFFFFFFF) + // TheSuperHackers @info Leave early if we are in the special power crash fix code path + if (m_availableOnFrame == ~0u) return; #endif @@ -244,6 +244,12 @@ void OCLSpecialPower::doSpecialPower( UnsignedInt commandOptions ) // call the base class action cause we are *EXTENDING* functionality SpecialPowerModule::doSpecialPowerAtLocation( &creationCoord, INVALID_ANGLE, commandOptions ); +#if RETAIL_COMPATIBLE_CRC + // TheSuperHackers @info Leave early if we are in the special power crash fix code path + if (m_availableOnFrame == ~0u) + return; +#endif + const ObjectCreationList* ocl = findOCL(); ObjectCreationList::create( ocl, getObject(), &creationCoord, &creationCoord, false ); } diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/SpecialPower/SpecialPowerModule.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/SpecialPower/SpecialPowerModule.cpp index 6f88eff654d..d1fabc4edab 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/SpecialPower/SpecialPowerModule.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/SpecialPower/SpecialPowerModule.cpp @@ -96,12 +96,7 @@ SpecialPowerModuleData::SpecialPowerModuleData() SpecialPowerModule::SpecialPowerModule( Thing *thing, const ModuleData *moduleData ) : BehaviorModule( thing, moduleData ) { - -#if RETAIL_COMPATIBLE_CRC m_availableOnFrame = 0; -#else - m_availableOnFrame = 0xFFFFFFFF; -#endif m_pausedCount = 0; m_pausedOnFrame = 0; m_pausedPercent = 0.0f; @@ -111,16 +106,36 @@ SpecialPowerModule::SpecialPowerModule( Thing *thing, const ModuleData *moduleDa // if we're pre-built, start counting down if( !getObject()->getStatusBits().test( OBJECT_STATUS_UNDER_CONSTRUCTION ) ) { - //A sharedNSync special only startPowerRecharges when first scienced or when executed, - //Since a new module with same SPTemplates may construct at any time. - if ( getSpecialPowerTemplate()->isSharedNSync() == FALSE ) - startPowerRecharge(); + init(); } + else + { +#if RETAIL_COMPATIBLE_CRC + initCountdown(); +#else + // TheSuperHackers @bugfix The Special Power will not be available until construction is done. + m_availableOnFrame = ~0u; +#endif + } +} + +void SpecialPowerModule::init() +{ + //A sharedNSync special only startPowerRecharges when first scienced or when executed, + //Since a new module with same SPTemplates may construct at any time. + if ( getSpecialPowerTemplate()->isSharedNSync() == FALSE ) + startPowerRecharge(); + + initCountdown(); +} + +void SpecialPowerModule::initCountdown() +{ // WE USED TO DO THE POLL-EVERYBODY-AND-VOTE-ON-WHO-TO-SYNC-TO THING HERE, // BUT NO MORE, NOW IT IS HANDLED IN PLAYER // Some Special powers need to be activated by an Upgrade, so prevent the timer from going until then - const SpecialPowerModuleData *md = (const SpecialPowerModuleData *)moduleData; + const SpecialPowerModuleData *md = getSpecialPowerModuleData(); if( md->m_startsPaused ) pauseCountdown( TRUE ); @@ -128,7 +143,7 @@ SpecialPowerModule::SpecialPowerModule( Thing *thing, const ModuleData *moduleDa // Now, if we find that we have just come into being, // but there is already a science granted for our shared superweapon, - // lets make sure TheIngameUI knows about our public timer + // lets make sure TheInGameUI knows about our public timer // add this weapon to the UI if it has a public timer for all to see if( m_pausedCount == 0 && getSpecialPowerTemplate()->isSharedNSync() == TRUE && @@ -142,7 +157,6 @@ SpecialPowerModule::SpecialPowerModule( Thing *thing, const ModuleData *moduleDa getSpecialPowerModuleData()->m_specialPowerTemplate ); } - } //------------------------------------------------------------------------------------------------- @@ -381,6 +395,17 @@ Bool SpecialPowerModule::isScriptOnly() const return modData->m_scriptedSpecialPowerOnly; } +//------------------------------------------------------------------------------------------------- +void SpecialPowerModule::onConstructionCompleted() +{ +#if !RETAIL_COMPATIBLE_CRC + DEBUG_ASSERTCRASH(m_availableOnFrame == ~0u, + ("Unexpected state. Function must be called only after OBJECT_STATUS_UNDER_CONSTRUCTION was removed")); + + m_availableOnFrame = 0; + init(); +#endif +} //------------------------------------------------------------------------------------------------- /** A special power has been used ... start the recharge process by computing the frame @@ -429,6 +454,13 @@ void SpecialPowerModule::startPowerRecharge() //------------------------------------------------------------------------------------------------- Bool SpecialPowerModule::initiateIntentToDoSpecialPower( const Object *targetObj, const Coord3D *targetPos, const Waypoint *way, UnsignedInt commandOptions ) { +#if !RETAIL_COMPATIBLE_CRC + if( getObject()->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION) ) + { + return false; + } +#endif + Bool valid = false; // tell our update modules that we intend to do this special power. for( BehaviorModule** u = getObject()->getBehaviorModules(); *u; ++u ) @@ -456,26 +488,20 @@ Bool SpecialPowerModule::initiateIntentToDoSpecialPower( const Object *targetObj } #if RETAIL_COMPATIBLE_CRC - // TheSuperHackers @info we need to leave early if we are in the MissileLauncherBuildingUpdate crash fix codepath - if (m_availableOnFrame == 0xFFFFFFFF) + // TheSuperHackers @info Leave early if we are in the special power crash fix code path + if (m_availableOnFrame == ~0u) { - DEBUG_ASSERTCRASH(!valid, ("Using MissileLauncherBuildingUpdate escape path when valid is set to true")); + DEBUG_ASSERTCRASH(!valid, ("Using special power escape path when valid is set to true")); return false; } #endif - getObject()->getControllingPlayer()->getAcademyStats()->recordSpecialPowerUsed( getSpecialPowerModuleData()->m_specialPowerTemplate ); + //If we depend on our update module to trigger the special power, make sure we have the appropriate update module! + DEBUG_ASSERTCRASH(valid || !getSpecialPowerModuleData()->m_updateModuleStartsAttack, + ("Object does not contain a special power module to execute. Did you forget to add it to the object INI?")); - //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 ) - { - DEBUG_CRASH( ("Object does not contain a special power module to execute. Did you forget to add it to the object INI?")); - //DEBUG_CRASH(( "Object does not contain special power module (%s) to execute. Did you forget to add it to the object INI?", - // command->m_specialPower->getName().str() )); - } - - return valid; + getObject()->getControllingPlayer()->getAcademyStats()->recordSpecialPowerUsed( getSpecialPowerModuleData()->m_specialPowerTemplate ); + return true; } //------------------------------------------------------------------------------------------------- @@ -679,15 +705,16 @@ void SpecialPowerModule::doSpecialPower( UnsignedInt commandOptions ) //This tells the update module that we want to do our special power. The update modules //will then start processing each frame. - initiateIntentToDoSpecialPower( nullptr, nullptr, nullptr, commandOptions ); - - //Only trigger the special power immediately if the updatemodule doesn't start the attack. - //An example of a case that wouldn't trigger immediately is for a unit that needs to - //close to range before firing the special attack. A case that would trigger immediately - //is the napalm strike. If we don't call this now, it's up to the update module to do so. - if( !getSpecialPowerModuleData()->m_updateModuleStartsAttack ) + if (initiateIntentToDoSpecialPower( nullptr, nullptr, nullptr, commandOptions )) { - triggerSpecialPower( nullptr );// Location-less trigger + //Only trigger the special power immediately if the update module doesn't start the attack. + //An example of a case that wouldn't trigger immediately is for a unit that needs to + //close to range before firing the special attack. A case that would trigger immediately + //is the napalm strike. If we don't call this now, it's up to the update module to do so. + if( !getSpecialPowerModuleData()->m_updateModuleStartsAttack ) + { + triggerSpecialPower( nullptr );// Location-less trigger + } } } @@ -701,15 +728,16 @@ void SpecialPowerModule::doSpecialPowerAtObject( Object *obj, UnsignedInt comman //This tells the update module that we want to do our special power. The update modules //will then start processing each frame. - initiateIntentToDoSpecialPower( obj, nullptr, nullptr, commandOptions ); - - //Only trigger the special power immediately if the updatemodule doesn't start the attack. - //An example of a case that wouldn't trigger immediately is for a unit that needs to - //close to range before firing the special attack. A case that would trigger immediately - //is the napalm strike. If we don't call this now, it's up to the update module to do so. - if( !getSpecialPowerModuleData()->m_updateModuleStartsAttack ) + if (initiateIntentToDoSpecialPower( obj, nullptr, nullptr, commandOptions )) { - triggerSpecialPower( obj->getPosition() ); + //Only trigger the special power immediately if the update module doesn't start the attack. + //An example of a case that wouldn't trigger immediately is for a unit that needs to + //close to range before firing the special attack. A case that would trigger immediately + //is the napalm strike. If we don't call this now, it's up to the update module to do so. + if( !getSpecialPowerModuleData()->m_updateModuleStartsAttack ) + { + triggerSpecialPower( obj->getPosition() ); + } } } @@ -723,21 +751,16 @@ void SpecialPowerModule::doSpecialPowerAtLocation( const Coord3D *loc, Real angl //This tells the update module that we want to do our special power. The update modules //will then start processing each frame. - initiateIntentToDoSpecialPower( nullptr, loc, nullptr, commandOptions ); - -#if RETAIL_COMPATIBLE_CRC - // TheSuperHackers @info we need to leave early if we are in the MissileLauncherBuildingUpdate crash fix codepath - if (m_availableOnFrame == 0xFFFFFFFF) - return; -#endif - - //Only trigger the special power immediately if the updatemodule doesn't start the attack. - //An example of a case that wouldn't trigger immediately is for a unit that needs to - //close to range before firing the special attack. A case that would trigger immediately - //is the napalm strike. If we don't call this now, it's up to the update module to do so. - if( !getSpecialPowerModuleData()->m_updateModuleStartsAttack ) + if (initiateIntentToDoSpecialPower( nullptr, loc, nullptr, commandOptions )) { - triggerSpecialPower( loc ); + //Only trigger the special power immediately if the update module doesn't start the attack. + //An example of a case that wouldn't trigger immediately is for a unit that needs to + //close to range before firing the special attack. A case that would trigger immediately + //is the napalm strike. If we don't call this now, it's up to the update module to do so. + if( !getSpecialPowerModuleData()->m_updateModuleStartsAttack ) + { + triggerSpecialPower( loc ); + } } } @@ -751,15 +774,16 @@ void SpecialPowerModule::doSpecialPowerUsingWaypoints( const Waypoint *way, Unsi //This tells the update module that we want to do our special power. The update modules //will then start processing each frame. - initiateIntentToDoSpecialPower( nullptr, nullptr, way, commandOptions ); - - //Only trigger the special power immediately if the updatemodule doesn't start the attack. - //An example of a case that wouldn't trigger immediately is for a unit that needs to - //close to range before firing the special attack. A case that would trigger immediately - //is the napalm strike. If we don't call this now, it's up to the update module to do so. - if( !getSpecialPowerModuleData()->m_updateModuleStartsAttack ) + if (initiateIntentToDoSpecialPower( nullptr, nullptr, way, commandOptions )) { - triggerSpecialPower( nullptr );// This type doesn't create view objects + //Only trigger the special power immediately if the update module doesn't start the attack. + //An example of a case that wouldn't trigger immediately is for a unit that needs to + //close to range before firing the special attack. A case that would trigger immediately + //is the napalm strike. If we don't call this now, it's up to the update module to do so. + if( !getSpecialPowerModuleData()->m_updateModuleStartsAttack ) + { + triggerSpecialPower( nullptr );// This type doesn't create view objects + } } } diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cpp index db8a7093347..2aa3b1e8234 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/AIUpdate/DozerAIUpdate.cpp @@ -553,9 +553,7 @@ StateReturnType DozerActionDoActionState::update() if( goalObject->getConstructionPercent() >= 100.0f ) { - // clear the under construction status - goalObject->clearStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_UNDER_CONSTRUCTION ) ); - goalObject->clearStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_RECONSTRUCTING ) ); + goalObject->onConstructionCompleted(); // stop playing the construction sound! dozerAI->finishBuildingSound(); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/BattlePlanUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/BattlePlanUpdate.cpp index ce703d4fd79..ea322c6174c 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/BattlePlanUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/BattlePlanUpdate.cpp @@ -256,6 +256,15 @@ void BattlePlanUpdate::onObjectCreated() //------------------------------------------------------------------------------------------------- Bool BattlePlanUpdate::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 + if (!m_specialPowerModule) { + Object* us = getObject(); + us->getSpecialPowerModule(specialPowerTemplate)->setReadyFrame(0xFFFFFFFF); + return FALSE; + } +#endif + if( m_specialPowerModule->getSpecialPowerTemplate() != specialPowerTemplate ) { //Check to make sure our modules are connected. diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/MissileLauncherBuildingUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/MissileLauncherBuildingUpdate.cpp index 552b57d79c8..31a7c39734d 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/MissileLauncherBuildingUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/MissileLauncherBuildingUpdate.cpp @@ -46,11 +46,15 @@ //------------------------------------------------------------------------------------------------- MissileLauncherBuildingUpdate::MissileLauncherBuildingUpdate( Thing *thing, const ModuleData* moduleData ) : SpecialPowerUpdateModule( thing, moduleData ) { + const MissileLauncherBuildingUpdateModuleData* d = getMissileLauncherBuildingUpdateModuleData(); + m_doorState = DOOR_CLOSED; m_timeoutState = DOOR_CLOSED; m_timeoutFrame = 0; - m_openIdleAudio = getMissileLauncherBuildingUpdateModuleData()->m_openIdleAudio; + m_openIdleAudio = d->m_openIdleAudio; m_openIdleAudio.setObjectID(getObject()->getID()); + m_specialPowerModule = getObject()->getSpecialPowerModule(d->m_specialPowerTemplate); + DEBUG_ASSERTCRASH(m_specialPowerModule, ("Missing special power")); } //------------------------------------------------------------------------------------------------- @@ -236,23 +240,16 @@ Bool MissileLauncherBuildingUpdate::isPowerCurrentlyInUse( const CommandButton * //------------------------------------------------------------------------------------------------- UpdateSleepTime MissileLauncherBuildingUpdate::update() { - const MissileLauncherBuildingUpdateModuleData* d = getMissileLauncherBuildingUpdateModuleData(); - - UnsignedInt now = TheGameLogic->getFrame(); - // If we are under construction, any decision we make about door status could be wrong. // Our special power module is randomly going to be initialized or not (which would result // in him reporting a 0 frame ready, which means we will start open). if( getObject()->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION) ) return UPDATE_SLEEP_NONE; - if (!m_specialPowerModule) - { - m_specialPowerModule = getObject()->getSpecialPowerModule(d->m_specialPowerTemplate); - DEBUG_ASSERTCRASH(m_specialPowerModule, ("Missing special power")); - } + const MissileLauncherBuildingUpdateModuleData* d = getMissileLauncherBuildingUpdateModuleData(); + + UnsignedInt now = TheGameLogic->getFrame(); - if (m_specialPowerModule) { UnsignedInt readyFrame = m_specialPowerModule->getReadyFrame(); UnsignedInt whenToStartOpening = (readyFrame >= d->m_doorOpenTime) ? (readyFrame - d->m_doorOpenTime) : 0; @@ -313,9 +310,6 @@ void MissileLauncherBuildingUpdate::xfer( Xfer *xfer ) // extend base class UpdateModule::xfer( xfer ); - // do not need to tie the m_specialPowerModule pointer cause it gets tied - // SpecialPowerModuleInterface *m_specialPowerModule; - // door state xfer->xferUser( &m_doorState, sizeof( DoorStateType ) ); diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp index 34ed6c5e8db..387dea6ef76 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/ParticleUplinkCannonUpdate.cpp @@ -267,6 +267,15 @@ void ParticleUplinkCannonUpdate::onObjectCreated() //------------------------------------------------------------------------------------------------- Bool ParticleUplinkCannonUpdate::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 + if (!m_specialPowerModule) { + Object* us = getObject(); + us->getSpecialPowerModule(specialPowerTemplate)->setReadyFrame(0xFFFFFFFF); + return FALSE; + } +#endif + const ParticleUplinkCannonUpdateModuleData *data = getParticleUplinkCannonUpdateModuleData(); if( m_specialPowerModule->getSpecialPowerTemplate() != specialPowerTemplate ) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/SpectreGunshipDeploymentUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/SpectreGunshipDeploymentUpdate.cpp index 0a9a5e7bcb4..5d744eddff1 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/SpectreGunshipDeploymentUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/SpectreGunshipDeploymentUpdate.cpp @@ -138,6 +138,15 @@ void SpectreGunshipDeploymentUpdate::onObjectCreated() //------------------------------------------------------------------------------------------------- Bool SpectreGunshipDeploymentUpdate::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 + if (!m_specialPowerModule) { + Object* us = getObject(); + us->getSpecialPowerModule(specialPowerTemplate)->setReadyFrame(0xFFFFFFFF); + return FALSE; + } +#endif + const SpectreGunshipDeploymentUpdateModuleData *data = getSpectreGunshipDeploymentUpdateModuleData(); if( m_specialPowerModule->getSpecialPowerTemplate() != specialPowerTemplate ) diff --git a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/SpectreGunshipUpdate.cpp b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/SpectreGunshipUpdate.cpp index f72d2ee6c3b..8d95fbcab17 100644 --- a/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/SpectreGunshipUpdate.cpp +++ b/GeneralsMD/Code/GameEngine/Source/GameLogic/Object/Update/SpectreGunshipUpdate.cpp @@ -183,6 +183,15 @@ void SpectreGunshipUpdate::onObjectCreated() //------------------------------------------------------------------------------------------------- Bool SpectreGunshipUpdate::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 + if (!m_specialPowerModule) { + Object* us = getObject(); + us->getSpecialPowerModule(specialPowerTemplate)->setReadyFrame(0xFFFFFFFF); + return FALSE; + } +#endif + const SpectreGunshipUpdateModuleData *data = getSpectreGunshipUpdateModuleData(); if( m_specialPowerModule->getSpecialPowerTemplate() != specialPowerTemplate )