/* ** Command & Conquer Generals Zero Hour(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ //////////////////////////////////////////////////////////////////////////////// // // // (c) 2001-2003 Electronic Arts Inc. // // // //////////////////////////////////////////////////////////////////////////////// // FILE: SpecialPowerModule.cpp /////////////////////////////////////////////////////////////////// // Author: Colin Day, April 2002 // Desc: Special power module interface /////////////////////////////////////////////////////////////////////////////////////////////////// // USER INCLUDES ////////////////////////////////////////////////////////////////////////////////// #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine #include "Common/GameAudio.h" #include "Common/GlobalData.h" #include "Common/INI.h" #include "Common/Player.h" #include "Common/PlayerList.h" #include "Common/Science.h" #include "Common/SpecialPower.h" #include "Common/ThingFactory.h" #include "Common/ThingTemplate.h" #include "Common/Xfer.h" #include "GameLogic/GameLogic.h" #include "GameLogic/Object.h" #include "GameLogic/Module/DeletionUpdate.h" #include "GameLogic/Module/UpdateModule.h" #include "GameLogic/Module/SpecialPowerModule.h" #include "GameLogic/Module/SpecialPowerUpdateModule.h" #include "GameLogic/ScriptEngine.h" #include "GameClient/Eva.h" #include "GameClient/InGameUI.h" #include "GameClient/ControlBar.h" #ifdef _INTERNAL // for occasional debugging... //#pragma optimize("", off) //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes") #endif //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- SpecialPowerModuleData::SpecialPowerModuleData() { m_specialPowerTemplate = NULL; m_updateModuleStartsAttack = false; m_startsPaused = FALSE; m_scriptedSpecialPowerOnly = FALSE; } // end SpecialPowerModuleData //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- /* static */ void SpecialPowerModuleData::buildFieldParse(MultiIniFieldParse& p) { BehaviorModuleData::buildFieldParse( p ); static const FieldParse dataFieldParse[] = { { "SpecialPowerTemplate", INI::parseSpecialPowerTemplate, NULL, offsetof( SpecialPowerModuleData, m_specialPowerTemplate ) }, { "UpdateModuleStartsAttack", INI::parseBool, NULL, offsetof( SpecialPowerModuleData, m_updateModuleStartsAttack ) }, { "StartsPaused", INI::parseBool, NULL, offsetof( SpecialPowerModuleData, m_startsPaused ) }, { "InitiateSound", INI::parseAudioEventRTS, NULL, offsetof( SpecialPowerModuleData, m_initiateSound ) }, { "ScriptedSpecialPowerOnly", INI::parseBool, NULL, offsetof( SpecialPowerModuleData, m_scriptedSpecialPowerOnly ) }, { 0, 0, 0, 0 } }; p.add(dataFieldParse); } // end buildFieldParse /////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////////////////////////// //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- SpecialPowerModule::SpecialPowerModule( Thing *thing, const ModuleData *moduleData ) : BehaviorModule( thing, moduleData ) { m_availableOnFrame = 0; m_pausedCount = 0; m_pausedOnFrame = 0; m_pausedPercent = 0.0f; // we won't be able to use the power for X number of frames now // 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 modue with same SPTemplates may construct at any time. if ( getSpecialPowerTemplate()->isSharedNSync() == FALSE ) startPowerRecharge(); } // 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; if( md->m_startsPaused ) pauseCountdown( TRUE ); resolveSpecialPower(); // 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 // add this weapon to the UI if it has a public timer for all to see if( m_pausedCount == 0 && getSpecialPowerTemplate()->isSharedNSync() == TRUE && getSpecialPowerTemplate()->hasPublicTimer() == TRUE && getObject()->getControllingPlayer() && getObject()->isKindOf( KINDOF_STRUCTURE ) ) { TheInGameUI->addSuperweapon( getObject()->getControllingPlayer()->getPlayerIndex(), getPowerName(), getObject()->getID(), getSpecialPowerModuleData()->m_specialPowerTemplate ); } } // end SpecialPowerModule //------------------------------------------------------------------------------------------------- const AudioEventRTS& SpecialPowerModule::getInitiateSound() const { return getSpecialPowerModuleData()->m_initiateSound; } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- SpecialPowerModule::~SpecialPowerModule() { if( getSpecialPowerModuleData()->m_specialPowerTemplate->hasPublicTimer() == TRUE && getObject()->getControllingPlayer() ) TheInGameUI->removeSuperweapon( getObject()->getControllingPlayer()->getPlayerIndex(), getPowerName(), getObject()->getID(), getSpecialPowerModuleData()->m_specialPowerTemplate ); } // end ~SpecialPowerModule //------------------------------------------------------------------------------------------------- void SpecialPowerModule::setReadyFrame( UnsignedInt frame ) { m_availableOnFrame = frame; //If a script should change the ready frame, we need to update the paused frame. This value isn't //used directly to determine if paused or not... it uses m_pausedCount. m_pausedOnFrame = TheGameLogic->getFrame(); } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- void SpecialPowerModule::resolveSpecialPower( void ) { /* // if we're pre-built, and not from a command center, and a building register us with the UI if( getSpecialPowerModuleData()->m_specialPowerTemplate->hasPublicTimer() == TRUE && TheGameLogic->getFrame() == 0 && getObject()->getControllingPlayer() && getObject()->isKindOf( KINDOF_COMMANDCENTER ) == FALSE && getObject()->isKindOf( KINDOF_STRUCTURE ) ) { //KM: The KINDOF_STRUCTURE check was made to prevent scripted bombers from registering their // special powers as public timers. TheInGameUI->addSuperweapon( getObject()->getControllingPlayer()->getPlayerIndex(), getPowerName(), getObject()->getID(), getSpecialPowerModuleData()->m_specialPowerTemplate ); } */ } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- void SpecialPowerModule::onSpecialPowerCreation( void ) { // THIS gets called by addScience(), that is, when the General has purchased a new special power, // and this module is thus activated. // start a power recharge going startPowerRecharge(); // Dustin wants these special powers to start ready to fire, // so here (and only here) we will expressly set them to ready-now. if ( getSpecialPowerTemplate()->isSharedNSync()) { Player *player = getObject()->getControllingPlayer(); if ( player ) { player->expressSpecialPowerReadyFrame( getSpecialPowerTemplate(), TheGameLogic->getFrame() ); m_availableOnFrame = player->getOrStartSpecialPowerReadyFrame( getSpecialPowerTemplate() ); } } // Some Special powers need to be activated by an Upgrade, so prevent the timer from going until then const SpecialPowerModuleData *md = getSpecialPowerModuleData(); if( md->m_startsPaused ) pauseCountdown( TRUE ); // add this weapon to the UI if it has a public timer for all to see if( getSpecialPowerModuleData()->m_specialPowerTemplate->hasPublicTimer() == TRUE && getObject()->getControllingPlayer() && getObject()->isKindOf( KINDOF_STRUCTURE ) ) { TheInGameUI->addSuperweapon( getObject()->getControllingPlayer()->getPlayerIndex(), getPowerName(), getObject()->getID(), getSpecialPowerModuleData()->m_specialPowerTemplate ); } } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- ScienceType SpecialPowerModule::getRequiredScience( void ) const { return getSpecialPowerModuleData()->m_specialPowerTemplate->getRequiredScience(); } // end ~SpecialPowerModule //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- const SpecialPowerTemplate * SpecialPowerModule::getSpecialPowerTemplate( void ) const { return getSpecialPowerModuleData()->m_specialPowerTemplate; } // end ~SpecialPowerModule //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- AsciiString SpecialPowerModule::getPowerName( void ) const { return getSpecialPowerModuleData()->m_specialPowerTemplate->getName(); } // end ~SpecialPowerModule //------------------------------------------------------------------------------------------------- /** Is this module designed for the power identier template passed in? */ //------------------------------------------------------------------------------------------------- Bool SpecialPowerModule::isModuleForPower( const SpecialPowerTemplate *specialPowerTemplate ) const { // get the module data const SpecialPowerModuleData *modData = getSpecialPowerModuleData(); // // if the special power template defined in the module data matches the template we want // to check then we are for it! // if( modData->m_specialPowerTemplate == specialPowerTemplate ) { //We match templates. return TRUE; } //We don't match templates. return FALSE; } // end canExecutePower //------------------------------------------------------------------------------------------------- /** Is this special power ready to use */ //------------------------------------------------------------------------------------------------- Bool SpecialPowerModule::isReady() const { #if defined(_DEBUG) || defined(_INTERNAL) || defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE) // this is a cheat ... remove this for release! if( TheGlobalData->m_specialPowerUsesDelay == FALSE ) return TRUE; #endif const Object* obj = getObject(); const SpecialPowerModuleData *modData = getSpecialPowerModuleData(); if ( obj && modData ) { Player *player = getObject()->getControllingPlayer(); if ( player ) { if ( modData->m_specialPowerTemplate->isSharedNSync()) return (TheGameLogic->getFrame() >= player->getOrStartSpecialPowerReadyFrame( modData->m_specialPowerTemplate ) ); } } return (m_pausedCount == 0) && (TheGameLogic->getFrame() >= m_availableOnFrame); } // end isReady //------------------------------------------------------------------------------------------------- /** Get the percentage ready a special power is to use * 1.0f = ready now * 0.5f = 50% ready * 0.2f = 20% ready * etc ... */ //------------------------------------------------------------------------------------------------- Real SpecialPowerModule::getPercentReady() const { if( m_pausedCount > 0 && m_pausedPercent == 1.0f ) { //Don't consider it ready if paused. return 0.99999f; } #if defined(_DEBUG) || defined(_INTERNAL) || defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE) if( TheGlobalData->m_specialPowerUsesDelay == FALSE ) return 1.0f; #endif // easy case ... is ready if( isReady() ) return 1.0f; if( m_pausedCount > 0 ) { return m_pausedPercent; } // get the module data const SpecialPowerModuleData *modData = getSpecialPowerModuleData(); // sanity if( modData->m_specialPowerTemplate == NULL ) return 0.0f; UnsignedInt readyFrame = m_availableOnFrame; //unless const Object* obj = getObject(); if ( obj ) { Player *player = getObject()->getControllingPlayer(); if ( player ) { if ( modData->m_specialPowerTemplate->isSharedNSync()) { readyFrame = player->getOrStartSpecialPowerReadyFrame( getSpecialPowerTemplate() ); } } } // calculate the percent Real percent = 1.0f - ((readyFrame - TheGameLogic->getFrame()) / (Real)modData->m_specialPowerTemplate->getReloadTime()); return percent; } //------------------------------------------------------------------------------------------------- // A special power module that is only supposed to be fired via scripts. An example of this // are the various cargo plane units we have. Scripters can launch specials from them after // specifying a waypoint path for them to follow them. //------------------------------------------------------------------------------------------------- Bool SpecialPowerModule::isScriptOnly() const { const SpecialPowerModuleData *modData = getSpecialPowerModuleData(); return modData->m_scriptedSpecialPowerOnly; } //------------------------------------------------------------------------------------------------- /** A special power has been used ... start the recharge process by computing the frame * we will become fully available on in the future again */ //------------------------------------------------------------------------------------------------- void SpecialPowerModule::startPowerRecharge() { #if defined(_DEBUG) || defined(_INTERNAL) || defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE) // this is a cheat ... remove this for release! if( TheGlobalData->m_specialPowerUsesDelay == FALSE ) return; #endif const SpecialPowerModuleData *modData = getSpecialPowerModuleData(); // sanity if( modData->m_specialPowerTemplate == NULL ) { DEBUG_CRASH(("special power not found")); return; } Object* obj = getObject(); if (!obj) return; Player* player = getObject()->getControllingPlayer(); if (!player) return; //Here, we make sure that general specials work as one between command centers // only factory type faction buildings should do this, and only with generals powers (in general) // but there are no restrictions on the use of SharedNSync on specialPowerTemplates at large if ( modData->m_specialPowerTemplate->isSharedNSync() ) { player->resetOrStartSpecialPowerReadyFrame( modData->m_specialPowerTemplate ); } else { // set the frame we will be 100% available on now m_availableOnFrame = TheGameLogic->getFrame() + getSpecialPowerTemplate()->getReloadTime(); } } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- Bool SpecialPowerModule::initiateIntentToDoSpecialPower( const Object *targetObj, const Coord3D *targetPos, const Waypoint *way, UnsignedInt commandOptions ) { Bool valid = false; // tell our update modules that we intend to do this special power. for( BehaviorModule** u = getObject()->getBehaviorModules(); *u; ++u ) { SpecialPowerUpdateInterface* spu = (*u)->getSpecialPowerUpdateInterface(); if( spu ) { //Validate that we are calling the correct module! if( isModuleForPower( getSpecialPowerModuleData()->m_specialPowerTemplate ) ) { if( spu->doesSpecialPowerUpdatePassScienceTest() ) { if( spu->initiateIntentToDoSpecialPower( getSpecialPowerModuleData()->m_specialPowerTemplate, targetObj, targetPos, way, commandOptions ) ) { //Kris: Aug 2003 //We have executed the special power, so don't try to execute any more. This logic //was changed for multi-level spectres. Before, multiple modules would get launched //causing 2 or 3 spectres to be created. valid = true; break; } } } } } 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! 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?\n")); //DEBUG_CRASH(( "Object does not contain special power module (%s) to execute. Did you forget to add it to the object INI?\n", // command->m_specialPower->getName().str() )); } return valid; } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- void SpecialPowerModule::triggerSpecialPower( const Coord3D *location ) { aboutToDoSpecialPower( location ); // do BEFORE recharge createViewObject(location); // we won't be able to use the power for X number of frames now startPowerRecharge(); } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- void SpecialPowerModule::createViewObject( const Coord3D *location ) { const SpecialPowerModuleData *modData = getSpecialPowerModuleData(); const SpecialPowerTemplate *powerTemplate = modData->m_specialPowerTemplate; if( modData == NULL || powerTemplate == NULL ) return; Real visionRange = powerTemplate->getViewObjectRange(); UnsignedInt visionDuration = powerTemplate->getViewObjectDuration(); if( visionRange == 0 || visionDuration == 0 ) return; // We don't want a view object at all. AsciiString objectName = TheGlobalData->m_specialPowerViewObjectName; if( objectName.isEmpty() ) return; const ThingTemplate *viewObjectTemplate = TheThingFactory->findTemplate( objectName ); if( viewObjectTemplate == NULL ) return; Object *viewObject = TheThingFactory->newObject( viewObjectTemplate, getObject()->getControllingPlayer()->getDefaultTeam() ); if( viewObject == NULL ) return; viewObject->setPosition( location ); viewObject->setShroudClearingRange( visionRange ); static NameKeyType key_DeletionUpdate = NAMEKEY("DeletionUpdate"); DeletionUpdate* dup = (DeletionUpdate*)viewObject->findUpdateModule(key_DeletionUpdate); if( dup ) { dup->setLifetimeRange( visionDuration, visionDuration ); } } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- void SpecialPowerModule::markSpecialPowerTriggered( const Coord3D *location ) { triggerSpecialPower( location ); } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- void SpecialPowerModule::aboutToDoSpecialPower( const Coord3D *location ) { // Tell the scripting engine! TheScriptEngine->notifyOfTriggeredSpecialPower( getObject()->getControllingPlayer()->getPlayerIndex(), getSpecialPowerModuleData()->m_specialPowerTemplate->getName(), getObject()->getID()); // Let EVA do her thing SpecialPowerType type = getSpecialPowerModuleData()->m_specialPowerTemplate->getSpecialPowerType(); Player *localPlayer = ThePlayerList->getLocalPlayer(); // Only play the EVA sounds if this is not the local player, and the local player doesn't consider the // person an enemy. // Kris: Actually, all players need to hear these warnings. // Ian: But now there are different Eva messages depending on who launched //if (localPlayer != getObject()->getControllingPlayer() && localPlayer->getRelationship(getObject()->getTeam()) != ENEMIES) { if( type == SPECIAL_PARTICLE_UPLINK_CANNON || type == SUPW_SPECIAL_PARTICLE_UPLINK_CANNON || type == LAZR_SPECIAL_PARTICLE_UPLINK_CANNON ) { if ( localPlayer == getObject()->getControllingPlayer() ) { TheEva->setShouldPlay(EVA_SuperweaponLaunched_Own_ParticleCannon); } else if ( localPlayer->getRelationship(getObject()->getTeam()) != ENEMIES ) { // Note: counting relationship NEUTRAL as ally. Not sure if this makes a difference??? TheEva->setShouldPlay(EVA_SuperweaponLaunched_Ally_ParticleCannon); } else { TheEva->setShouldPlay(EVA_SuperweaponLaunched_Enemy_ParticleCannon); } } else if( type == SPECIAL_NEUTRON_MISSILE || type == NUKE_SPECIAL_NEUTRON_MISSILE || type == SUPW_SPECIAL_NEUTRON_MISSILE ) { if ( localPlayer == getObject()->getControllingPlayer() ) { TheEva->setShouldPlay(EVA_SuperweaponLaunched_Own_Nuke); } else if ( localPlayer->getRelationship(getObject()->getTeam()) != ENEMIES ) { // Note: counting relationship NEUTRAL as ally. Not sure if this makes a difference??? TheEva->setShouldPlay(EVA_SuperweaponLaunched_Ally_Nuke); } else { TheEva->setShouldPlay(EVA_SuperweaponLaunched_Enemy_Nuke); } } else if (type == SPECIAL_SCUD_STORM) { if ( localPlayer == getObject()->getControllingPlayer() ) { TheEva->setShouldPlay(EVA_SuperweaponLaunched_Own_ScudStorm); } else if ( localPlayer->getRelationship(getObject()->getTeam()) != ENEMIES ) { // Note: counting relationship NEUTRAL as ally. Not sure if this makes a difference??? TheEva->setShouldPlay(EVA_SuperweaponLaunched_Ally_ScudStorm); } else { TheEva->setShouldPlay(EVA_SuperweaponLaunched_Enemy_ScudStorm); } } else if (type == SPECIAL_GPS_SCRAMBLER || type == SLTH_SPECIAL_GPS_SCRAMBLER ) { // This is Ghetto. Voices should be ini lines in the special power entry. You shouldn't have to // add to an enum to get a new voice if ( localPlayer == getObject()->getControllingPlayer() ) { TheEva->setShouldPlay(EVA_SuperweaponLaunched_Own_GPS_Scrambler); } else if ( localPlayer->getRelationship(getObject()->getTeam()) != ENEMIES ) { // Note: counting relationship NEUTRAL as ally. Not sure if this makes a difference??? TheEva->setShouldPlay(EVA_SuperweaponLaunched_Ally_GPS_Scrambler); } else { TheEva->setShouldPlay(EVA_SuperweaponLaunched_Enemy_GPS_Scrambler); } } else if (type == SPECIAL_SNEAK_ATTACK) { if ( localPlayer == getObject()->getControllingPlayer() ) { TheEva->setShouldPlay(EVA_SuperweaponLaunched_Own_Sneak_Attack); } else if ( localPlayer->getRelationship(getObject()->getTeam()) != ENEMIES ) { // Note: counting relationship NEUTRAL as ally. Not sure if this makes a difference??? TheEva->setShouldPlay(EVA_SuperweaponLaunched_Ally_Sneak_Attack); } else { TheEva->setShouldPlay(EVA_SuperweaponLaunched_Enemy_Sneak_Attack); } } } // get module data const SpecialPowerModuleData *modData = getSpecialPowerModuleData(); // play our initiate sound if we have one AudioEventRTS audioEvent = *modData->m_specialPowerTemplate->getInitiateSound(); audioEvent.setObjectID(getObject()->getID()); TheAudio->addAudioEvent( &audioEvent ); // play sound at target location if specified if( location ) { AudioEventRTS soundAtLocation = *modData->m_specialPowerTemplate->getInitiateAtTargetSound(); soundAtLocation.setPosition( location ); soundAtLocation.setPlayerIndex(getObject()->getControllingPlayer()->getPlayerIndex()); TheAudio->addAudioEvent( &soundAtLocation ); } // end if } //------------------------------------------------------------------------------------------------- //By default, special powers are not triggered by it's update module -- in which case //it triggers it and resets its timer immediately. When the update module triggers it, //then all we do is initiate the special power, and trust that the update module will //do the rest. //------------------------------------------------------------------------------------------------- void SpecialPowerModule::doSpecialPower( UnsignedInt commandOptions ) { if (m_pausedCount > 0 || getObject()->isDisabled()) { return; } //This tells the update module that we want to do our special power. The update modules //will then start processing each frame. initiateIntentToDoSpecialPower( NULL, NULL, NULL, 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 ) { triggerSpecialPower( NULL );// Location-less trigger } } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- void SpecialPowerModule::doSpecialPowerAtObject( Object *obj, UnsignedInt commandOptions ) { if (m_pausedCount > 0 || getObject()->isDisabled()) { return; } //This tells the update module that we want to do our special power. The update modules //will then start processing each frame. initiateIntentToDoSpecialPower( obj, NULL, NULL, 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 ) { triggerSpecialPower( obj->getPosition() ); } } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- void SpecialPowerModule::doSpecialPowerAtLocation( const Coord3D *loc, Real angle, UnsignedInt commandOptions ) { if (m_pausedCount > 0 || getObject()->isDisabled()) { return; } //This tells the update module that we want to do our special power. The update modules //will then start processing each frame. initiateIntentToDoSpecialPower( NULL, loc, NULL, 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 ) { triggerSpecialPower( loc ); } } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- void SpecialPowerModule::doSpecialPowerUsingWaypoints( const Waypoint *way, UnsignedInt commandOptions ) { if (m_pausedCount > 0 || getObject()->isDisabled()) { return; } //This tells the update module that we want to do our special power. The update modules //will then start processing each frame. initiateIntentToDoSpecialPower( NULL, NULL, 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 ) { triggerSpecialPower( NULL );// This type doesn't create view objects } } //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- void SpecialPowerModule::pauseCountdown( Bool pause ) { if (pause)// If pausing { if( m_pausedCount == 0 ) { // Only record this with the first pausing, otherwise upon final unpausing you would get credited the time // between pauses as time served. m_pausedOnFrame = TheGameLogic->getFrame(); m_pausedPercent = getPercentReady(); } ++m_pausedCount; } else if( m_pausedCount > 0 )//Else if unpausing, but only if I am in fact paused, so multiple unpauses don't break. { --m_pausedCount; // And only update the ready time if we are fully unpaused now. if( m_pausedCount == 0 ) { m_availableOnFrame += (TheGameLogic->getFrame() - m_pausedOnFrame); } } } // end pauseCountdown //------------------------------------------------------------------------------------------------- //------------------------------------------------------------------------------------------------- UnsignedInt SpecialPowerModule::getReadyFrame( void ) const { if ( getSpecialPowerTemplate()->isSharedNSync() ) { const Object* obj = getObject(); if ( obj ) { Player *player = getObject()->getControllingPlayer(); if ( player ) return player->getOrStartSpecialPowerReadyFrame( getSpecialPowerTemplate() ); } } if (m_pausedCount > 0 || getObject()->isDisabled()) { Int pausedFrames = TheGameLogic->getFrame() - m_pausedOnFrame; return m_availableOnFrame + pausedFrames; } else { return m_availableOnFrame; } } // ------------------------------------------------------------------------------------------------ /** CRC */ // ------------------------------------------------------------------------------------------------ void SpecialPowerModule::crc( Xfer *xfer ) { // extend base class BehaviorModule::crc( xfer ); } // end crc // ------------------------------------------------------------------------------------------------ /** Xfer method * Version Info: * 1: Initial version */ // ------------------------------------------------------------------------------------------------ void SpecialPowerModule::xfer( Xfer *xfer ) { // version XferVersion currentVersion = 1; XferVersion version = currentVersion; xfer->xferVersion( &version, currentVersion ); // extend base class BehaviorModule::xfer( xfer ); // available on frame xfer->xferUnsignedInt( &m_availableOnFrame ); // paused by script xfer->xferInt( &m_pausedCount ); // paused on frame xfer->xferUnsignedInt( &m_pausedOnFrame ); // paused percent xfer->xferReal( &m_pausedPercent ); } // end xfer // ------------------------------------------------------------------------------------------------ /** Load post process */ // ------------------------------------------------------------------------------------------------ void SpecialPowerModule::loadPostProcess( void ) { // extend base class BehaviorModule::loadPostProcess(); // 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 // add this weapon to the UI if it has a public timer for all to see if( m_pausedCount == 0 && getSpecialPowerTemplate()->isSharedNSync() == TRUE && getSpecialPowerTemplate()->hasPublicTimer() == TRUE && getObject()->getControllingPlayer() && getObject()->isKindOf( KINDOF_STRUCTURE ) ) { TheInGameUI->addSuperweapon( getObject()->getControllingPlayer()->getPlayerIndex(), getPowerName(), getObject()->getID(), getSpecialPowerModuleData()->m_specialPowerTemplate ); } } // end loadPostProcess