SpecialPowerModule.cpp 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745
  1. /*
  2. ** Command & Conquer Generals(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: SpecialPowerModule.cpp ///////////////////////////////////////////////////////////////////
  24. // Author: Colin Day, April 2002
  25. // Desc: Special power module interface
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #include "Common/GameAudio.h"
  30. #include "Common/GlobalData.h"
  31. #include "Common/INI.h"
  32. #include "Common/Player.h"
  33. #include "Common/PlayerList.h"
  34. #include "Common/Science.h"
  35. #include "Common/SpecialPower.h"
  36. #include "Common/ThingFactory.h"
  37. #include "Common/ThingTemplate.h"
  38. #include "Common/Xfer.h"
  39. #include "GameLogic/GameLogic.h"
  40. #include "GameLogic/Object.h"
  41. #include "GameLogic/Module/DeletionUpdate.h"
  42. #include "GameLogic/Module/UpdateModule.h"
  43. #include "GameLogic/Module/SpecialPowerModule.h"
  44. #include "GameLogic/ScriptEngine.h"
  45. #include "GameClient/Eva.h"
  46. #include "GameClient/InGameUI.h"
  47. #include "GameClient/ControlBar.h"
  48. #ifdef _INTERNAL
  49. // for occasional debugging...
  50. //#pragma optimize("", off)
  51. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  52. #endif
  53. //-------------------------------------------------------------------------------------------------
  54. //-------------------------------------------------------------------------------------------------
  55. SpecialPowerModuleData::SpecialPowerModuleData()
  56. {
  57. m_specialPowerTemplate = NULL;
  58. m_updateModuleStartsAttack = false;
  59. m_startsPaused = FALSE;
  60. } // end SpecialPowerModuleData
  61. //-------------------------------------------------------------------------------------------------
  62. //-------------------------------------------------------------------------------------------------
  63. /* static */ void SpecialPowerModuleData::buildFieldParse(MultiIniFieldParse& p)
  64. {
  65. BehaviorModuleData::buildFieldParse( p );
  66. static const FieldParse dataFieldParse[] =
  67. {
  68. { "SpecialPowerTemplate", INI::parseSpecialPowerTemplate, NULL, offsetof( SpecialPowerModuleData, m_specialPowerTemplate ) },
  69. { "UpdateModuleStartsAttack", INI::parseBool, NULL, offsetof( SpecialPowerModuleData, m_updateModuleStartsAttack ) },
  70. { "StartsPaused", INI::parseBool, NULL, offsetof( SpecialPowerModuleData, m_startsPaused ) },
  71. { "InitiateSound", INI::parseAudioEventRTS, NULL, offsetof( SpecialPowerModuleData, m_initiateSound ) },
  72. { 0, 0, 0, 0 }
  73. };
  74. p.add(dataFieldParse);
  75. } // end buildFieldParse
  76. ///////////////////////////////////////////////////////////////////////////////////////////////////
  77. ///////////////////////////////////////////////////////////////////////////////////////////////////
  78. ///////////////////////////////////////////////////////////////////////////////////////////////////
  79. //-------------------------------------------------------------------------------------------------
  80. //-------------------------------------------------------------------------------------------------
  81. SpecialPowerModule::SpecialPowerModule( Thing *thing, const ModuleData *moduleData )
  82. : BehaviorModule( thing, moduleData )
  83. {
  84. m_availableOnFrame = 0;
  85. m_pausedCount = 0;
  86. m_pausedOnFrame = 0;
  87. m_pausedPercent = 0.0f;
  88. // we won't be able to use the power for X number of frames now
  89. // if we're pre-built, start counting down
  90. if( BitTest( getObject()->getStatusBits(), OBJECT_STATUS_UNDER_CONSTRUCTION ) == FALSE )
  91. {
  92. //A sharedNSync special only startPowerRecharges when first scienced or when executed,
  93. //Since a new modue with same SPTemplates may construct at any time.
  94. if ( getSpecialPowerTemplate()->isSharedNSync() == FALSE )
  95. startPowerRecharge();
  96. }
  97. // WE USED TO DO THE POLL-EVERYBODY-AND-VOTE-ON-WHO-TO-SYNC-TO THING HERE,
  98. // BUT NO MORE, NOW IT IS HANDLED IN PLAYER
  99. // Some Special powers need to be activated by an Upgrade, so prevent the timer from going until then
  100. const SpecialPowerModuleData *md = (const SpecialPowerModuleData *)moduleData;
  101. if( md->m_startsPaused )
  102. pauseCountdown( TRUE );
  103. resolveSpecialPower();
  104. // Now, if we find that we have just come into being,
  105. // but there is already a science granted for our shared superweapon,
  106. // lets make sure TheIngameUI knows about our public timer
  107. // add this weapon to the UI if it has a public timer for all to see
  108. if( m_pausedCount == 0 &&
  109. getSpecialPowerTemplate()->isSharedNSync() == TRUE &&
  110. getSpecialPowerTemplate()->hasPublicTimer() == TRUE &&
  111. getObject()->getControllingPlayer() &&
  112. getObject()->isKindOf( KINDOF_STRUCTURE ) )
  113. {
  114. TheInGameUI->addSuperweapon( getObject()->getControllingPlayer()->getPlayerIndex(),
  115. getPowerName(),
  116. getObject()->getID(),
  117. getSpecialPowerModuleData()->m_specialPowerTemplate );
  118. }
  119. } // end SpecialPowerModule
  120. //-------------------------------------------------------------------------------------------------
  121. const AudioEventRTS& SpecialPowerModule::getInitiateSound() const
  122. {
  123. return getSpecialPowerModuleData()->m_initiateSound;
  124. }
  125. //-------------------------------------------------------------------------------------------------
  126. //-------------------------------------------------------------------------------------------------
  127. SpecialPowerModule::~SpecialPowerModule()
  128. {
  129. if( getSpecialPowerModuleData()->m_specialPowerTemplate->hasPublicTimer() == TRUE &&
  130. getObject()->getControllingPlayer() )
  131. TheInGameUI->removeSuperweapon( getObject()->getControllingPlayer()->getPlayerIndex(),
  132. getPowerName(),
  133. getObject()->getID(),
  134. getSpecialPowerModuleData()->m_specialPowerTemplate );
  135. } // end ~SpecialPowerModule
  136. //-------------------------------------------------------------------------------------------------
  137. //-------------------------------------------------------------------------------------------------
  138. void SpecialPowerModule::resolveSpecialPower( void )
  139. {
  140. /*
  141. // if we're pre-built, and not from a command center, and a building register us with the UI
  142. if( getSpecialPowerModuleData()->m_specialPowerTemplate->hasPublicTimer() == TRUE &&
  143. TheGameLogic->getFrame() == 0 && getObject()->getControllingPlayer() &&
  144. getObject()->isKindOf( KINDOF_COMMANDCENTER ) == FALSE &&
  145. getObject()->isKindOf( KINDOF_STRUCTURE ) )
  146. {
  147. //KM: The KINDOF_STRUCTURE check was made to prevent scripted bombers from registering their
  148. // special powers as public timers.
  149. TheInGameUI->addSuperweapon( getObject()->getControllingPlayer()->getPlayerIndex(),
  150. getPowerName(),
  151. getObject()->getID(),
  152. getSpecialPowerModuleData()->m_specialPowerTemplate );
  153. }
  154. */
  155. }
  156. //-------------------------------------------------------------------------------------------------
  157. //-------------------------------------------------------------------------------------------------
  158. void SpecialPowerModule::onSpecialPowerCreation( void )
  159. {
  160. // THIS gets called by addScience(), that is, when the General has purchased a new special power,
  161. // and this module is thus activated.
  162. // start a power recharge going
  163. startPowerRecharge();
  164. // Dustin wants these special powers to start ready to fire,
  165. // so here (and only here) we will expressly set them to ready-now.
  166. if ( getSpecialPowerTemplate()->isSharedNSync())
  167. {
  168. Player *player = getObject()->getControllingPlayer();
  169. if ( player )
  170. {
  171. player->expressSpecialPowerReadyFrame( getSpecialPowerTemplate(), TheGameLogic->getFrame() );
  172. m_availableOnFrame = player->getOrStartSpecialPowerReadyFrame( getSpecialPowerTemplate() );
  173. }
  174. }
  175. // Some Special powers need to be activated by an Upgrade, so prevent the timer from going until then
  176. const SpecialPowerModuleData *md = getSpecialPowerModuleData();
  177. if( md->m_startsPaused )
  178. pauseCountdown( TRUE );
  179. // add this weapon to the UI if it has a public timer for all to see
  180. if( getSpecialPowerModuleData()->m_specialPowerTemplate->hasPublicTimer() == TRUE &&
  181. getObject()->getControllingPlayer() &&
  182. getObject()->isKindOf( KINDOF_STRUCTURE ) )
  183. {
  184. TheInGameUI->addSuperweapon( getObject()->getControllingPlayer()->getPlayerIndex(),
  185. getPowerName(),
  186. getObject()->getID(),
  187. getSpecialPowerModuleData()->m_specialPowerTemplate );
  188. }
  189. }
  190. //-------------------------------------------------------------------------------------------------
  191. //-------------------------------------------------------------------------------------------------
  192. ScienceType SpecialPowerModule::getRequiredScience( void ) const
  193. {
  194. return getSpecialPowerModuleData()->m_specialPowerTemplate->getRequiredScience();
  195. } // end ~SpecialPowerModule
  196. //-------------------------------------------------------------------------------------------------
  197. //-------------------------------------------------------------------------------------------------
  198. const SpecialPowerTemplate * SpecialPowerModule::getSpecialPowerTemplate( void ) const
  199. {
  200. return getSpecialPowerModuleData()->m_specialPowerTemplate;
  201. } // end ~SpecialPowerModule
  202. //-------------------------------------------------------------------------------------------------
  203. //-------------------------------------------------------------------------------------------------
  204. AsciiString SpecialPowerModule::getPowerName( void ) const
  205. {
  206. return getSpecialPowerModuleData()->m_specialPowerTemplate->getName();
  207. } // end ~SpecialPowerModule
  208. //-------------------------------------------------------------------------------------------------
  209. /** Is this module designed for the power identier template passed in? */
  210. //-------------------------------------------------------------------------------------------------
  211. Bool SpecialPowerModule::isModuleForPower( const SpecialPowerTemplate *specialPowerTemplate ) const
  212. {
  213. // get the module data
  214. const SpecialPowerModuleData *modData = getSpecialPowerModuleData();
  215. //
  216. // if the special power template defined in the module data matches the template we want
  217. // to check then we are for it!
  218. //
  219. return modData->m_specialPowerTemplate == specialPowerTemplate;
  220. } // end canExecutePower
  221. //-------------------------------------------------------------------------------------------------
  222. /** Is this special power ready to use */
  223. //-------------------------------------------------------------------------------------------------
  224. Bool SpecialPowerModule::isReady() const
  225. {
  226. #if defined(_DEBUG) || defined(_INTERNAL)
  227. // this is a cheat ... remove this for release!
  228. if( TheGlobalData->m_specialPowerUsesDelay == FALSE )
  229. return TRUE;
  230. #endif
  231. const Object* obj = getObject();
  232. const SpecialPowerModuleData *modData = getSpecialPowerModuleData();
  233. if ( obj && modData )
  234. {
  235. Player *player = getObject()->getControllingPlayer();
  236. if ( player )
  237. {
  238. if ( modData->m_specialPowerTemplate->isSharedNSync())
  239. return (TheGameLogic->getFrame() >= player->getOrStartSpecialPowerReadyFrame( modData->m_specialPowerTemplate ) );
  240. }
  241. }
  242. return (m_pausedCount == 0) && (TheGameLogic->getFrame() >= m_availableOnFrame);
  243. } // end isReady
  244. //-------------------------------------------------------------------------------------------------
  245. /** Get the percentage ready a special power is to use
  246. * 1.0f = ready now
  247. * 0.5f = 50% ready
  248. * 0.2f = 20% ready
  249. * etc ... */
  250. //-------------------------------------------------------------------------------------------------
  251. Real SpecialPowerModule::getPercentReady() const
  252. {
  253. #if defined(_DEBUG) || defined(_INTERNAL)
  254. if( TheGlobalData->m_specialPowerUsesDelay == FALSE )
  255. return 1.0f;
  256. #endif
  257. // easy case ... is ready
  258. if( isReady() )
  259. return 1.0f;
  260. if ( m_pausedCount > 0 )
  261. return m_pausedPercent;
  262. // get the module data
  263. const SpecialPowerModuleData *modData = getSpecialPowerModuleData();
  264. // sanity
  265. if( modData->m_specialPowerTemplate == NULL )
  266. return 0.0f;
  267. UnsignedInt readyFrame = m_availableOnFrame;
  268. //unless
  269. const Object* obj = getObject();
  270. if ( obj )
  271. {
  272. Player *player = getObject()->getControllingPlayer();
  273. if ( player )
  274. {
  275. if ( modData->m_specialPowerTemplate->isSharedNSync())
  276. {
  277. readyFrame = player->getOrStartSpecialPowerReadyFrame( getSpecialPowerTemplate() );
  278. }
  279. }
  280. }
  281. // calculate the percent
  282. Real percent = 1.0f - ((readyFrame - TheGameLogic->getFrame()) /
  283. (Real)modData->m_specialPowerTemplate->getReloadTime());
  284. return percent;
  285. }
  286. //-------------------------------------------------------------------------------------------------
  287. /** A special power has been used ... start the recharge process by computing the frame
  288. * we will become fully available on in the future again */
  289. //-------------------------------------------------------------------------------------------------
  290. void SpecialPowerModule::startPowerRecharge()
  291. {
  292. #if defined(_DEBUG) || defined(_INTERNAL)
  293. // this is a cheat ... remove this for release!
  294. if( TheGlobalData->m_specialPowerUsesDelay == FALSE )
  295. return;
  296. #endif
  297. const SpecialPowerModuleData *modData = getSpecialPowerModuleData();
  298. // sanity
  299. if( modData->m_specialPowerTemplate == NULL )
  300. {
  301. DEBUG_CRASH(("special power not found"));
  302. return;
  303. }
  304. Object* obj = getObject();
  305. if (!obj)
  306. return;
  307. Player* player = getObject()->getControllingPlayer();
  308. if (!player)
  309. return;
  310. //Here, we make sure that general specials work as one between command centers
  311. // only factory type faction buildings should do this, and only with generals powers (in general)
  312. // but there are no restrictions on the use of SharedNSync on specialPowerTemplates at large
  313. if ( modData->m_specialPowerTemplate->isSharedNSync() )
  314. {
  315. player->resetOrStartSpecialPowerReadyFrame( modData->m_specialPowerTemplate );
  316. }
  317. else
  318. {
  319. // set the frame we will be 100% available on now
  320. m_availableOnFrame = TheGameLogic->getFrame() + getSpecialPowerTemplate()->getReloadTime();
  321. }
  322. }
  323. //-------------------------------------------------------------------------------------------------
  324. //-------------------------------------------------------------------------------------------------
  325. void SpecialPowerModule::initiateIntentToDoSpecialPower( const Object *targetObj, const Coord3D *targetPos, UnsignedInt commandOptions, Int locationCount )
  326. {
  327. Bool valid = false;
  328. // tell our update modules that we intend to do this special power.
  329. for( BehaviorModule** u = getObject()->getBehaviorModules(); *u; ++u )
  330. {
  331. SpecialPowerUpdateInterface* spu = (*u)->getSpecialPowerUpdateInterface();
  332. if( spu )
  333. {
  334. //Validate that we are calling the correct module!
  335. if( isModuleForPower( getSpecialPowerModuleData()->m_specialPowerTemplate ) )
  336. {
  337. spu->initiateIntentToDoSpecialPower( getSpecialPowerModuleData()->m_specialPowerTemplate, targetObj, targetPos, commandOptions, locationCount );
  338. valid = true;
  339. }
  340. }
  341. }
  342. //If we depend on our update module to trigger the special power, make sure we have the
  343. //appropriate update module!
  344. if( !valid && getSpecialPowerModuleData()->m_updateModuleStartsAttack )
  345. {
  346. DEBUG_CRASH( ("Object does not contain a special power module to execute. Did you forget to add it to the object INI?\n"));
  347. //DEBUG_CRASH(( "Object does not contain special power module (%s) to execute. Did you forget to add it to the object INI?\n",
  348. // command->m_specialPower->getName().str() ));
  349. }
  350. }
  351. //-------------------------------------------------------------------------------------------------
  352. //-------------------------------------------------------------------------------------------------
  353. void SpecialPowerModule::triggerSpecialPower( const Coord3D *location )
  354. {
  355. aboutToDoSpecialPower( location ); // do BEFORE recharge
  356. createViewObject(location);
  357. // we won't be able to use the power for X number of frames now
  358. startPowerRecharge();
  359. }
  360. //-------------------------------------------------------------------------------------------------
  361. //-------------------------------------------------------------------------------------------------
  362. void SpecialPowerModule::createViewObject( const Coord3D *location )
  363. {
  364. const SpecialPowerModuleData *modData = getSpecialPowerModuleData();
  365. const SpecialPowerTemplate *powerTemplate = modData->m_specialPowerTemplate;
  366. if( modData == NULL || powerTemplate == NULL )
  367. return;
  368. Real visionRange = powerTemplate->getViewObjectRange();
  369. UnsignedInt visionDuration = powerTemplate->getViewObjectDuration();
  370. if( visionRange == 0 || visionDuration == 0 )
  371. return; // We don't want a view object at all.
  372. AsciiString objectName = TheGlobalData->m_specialPowerViewObjectName;
  373. if( objectName.isEmpty() )
  374. return;
  375. const ThingTemplate *viewObjectTemplate = TheThingFactory->findTemplate( objectName );
  376. if( viewObjectTemplate == NULL )
  377. return;
  378. Object *viewObject = TheThingFactory->newObject( viewObjectTemplate, getObject()->getControllingPlayer()->getDefaultTeam() );
  379. if( viewObject == NULL )
  380. return;
  381. viewObject->setPosition( location );
  382. viewObject->setShroudClearingRange( visionRange );
  383. static NameKeyType key_DeletionUpdate = NAMEKEY("DeletionUpdate");
  384. DeletionUpdate* dup = (DeletionUpdate*)viewObject->findUpdateModule(key_DeletionUpdate);
  385. if( dup )
  386. {
  387. dup->setLifetimeRange( visionDuration, visionDuration );
  388. }
  389. }
  390. //-------------------------------------------------------------------------------------------------
  391. //-------------------------------------------------------------------------------------------------
  392. void SpecialPowerModule::markSpecialPowerTriggered( const Coord3D *location )
  393. {
  394. triggerSpecialPower(location);
  395. }
  396. //-------------------------------------------------------------------------------------------------
  397. //-------------------------------------------------------------------------------------------------
  398. void SpecialPowerModule::aboutToDoSpecialPower( const Coord3D *location )
  399. {
  400. // Tell the scripting engine!
  401. TheScriptEngine->notifyOfTriggeredSpecialPower(
  402. getObject()->getControllingPlayer()->getPlayerIndex(),
  403. getSpecialPowerModuleData()->m_specialPowerTemplate->getName(),
  404. getObject()->getID());
  405. // Let EVA do her thing
  406. SpecialPowerType type = getSpecialPowerModuleData()->m_specialPowerTemplate->getSpecialPowerType();
  407. // Only play the EVA sounds if this is not the local player, and the local player doesn't consider the
  408. // person an enemy.
  409. // Kris: Actually, all players need to hear these warnings.
  410. //Player *localPlayer = ThePlayerList->getLocalPlayer();
  411. //if (localPlayer != getObject()->getControllingPlayer() && localPlayer->getRelationship(getObject()->getTeam()) != ENEMIES) {
  412. if (type == SPECIAL_PARTICLE_UPLINK_CANNON)
  413. TheEva->setShouldPlay(EVA_SuperweaponLaunched_ParticleCannon);
  414. else if (type == SPECIAL_NEUTRON_MISSILE)
  415. TheEva->setShouldPlay(EVA_SuperweaponLaunched_Nuke);
  416. else if (type == SPECIAL_SCUD_STORM)
  417. TheEva->setShouldPlay(EVA_SuperweaponLaunched_ScudStorm);
  418. //}
  419. // get module data
  420. const SpecialPowerModuleData *modData = getSpecialPowerModuleData();
  421. // play our initiate sound if we have one
  422. AudioEventRTS audioEvent = *modData->m_specialPowerTemplate->getInitiateSound();
  423. audioEvent.setObjectID(getObject()->getID());
  424. TheAudio->addAudioEvent( &audioEvent );
  425. // play sound at target location if specified
  426. if( location )
  427. {
  428. AudioEventRTS soundAtLocation = *modData->m_specialPowerTemplate->getInitiateAtTargetSound();
  429. soundAtLocation.setPosition( location );
  430. soundAtLocation.setPlayerIndex(getObject()->getControllingPlayer()->getPlayerIndex());
  431. TheAudio->addAudioEvent( &soundAtLocation );
  432. } // end if
  433. }
  434. //-------------------------------------------------------------------------------------------------
  435. //By default, special powers are not triggered by it's update module -- in which case
  436. //it triggers it and resets its timer immediately. When the update module triggers it,
  437. //then all we do is initiate the special power, and trust that the update module will
  438. //do the rest.
  439. //-------------------------------------------------------------------------------------------------
  440. void SpecialPowerModule::doSpecialPower( UnsignedInt commandOptions )
  441. {
  442. if (m_pausedCount > 0 || getObject()->isDisabled()) {
  443. return;
  444. }
  445. //This tells the update module that we want to do our special power. The update modules
  446. //will then start processing each frame.
  447. initiateIntentToDoSpecialPower( NULL, NULL, commandOptions );
  448. //Only trigger the special power immediately if the updatemodule doesn't start the attack.
  449. //An example of a case that wouldn't trigger immediately is for a unit that needs to
  450. //close to range before firing the special attack. A case that would trigger immediately
  451. //is the napalm strike. If we don't call this now, it's up to the update module to do so.
  452. if( !getSpecialPowerModuleData()->m_updateModuleStartsAttack )
  453. {
  454. triggerSpecialPower( NULL );// Location-less trigger
  455. }
  456. }
  457. //-------------------------------------------------------------------------------------------------
  458. //-------------------------------------------------------------------------------------------------
  459. void SpecialPowerModule::doSpecialPowerAtObject( Object *obj, UnsignedInt commandOptions )
  460. {
  461. if (m_pausedCount > 0 || getObject()->isDisabled()) {
  462. return;
  463. }
  464. //This tells the update module that we want to do our special power. The update modules
  465. //will then start processing each frame.
  466. initiateIntentToDoSpecialPower( obj, NULL, commandOptions );
  467. //Only trigger the special power immediately if the updatemodule doesn't start the attack.
  468. //An example of a case that wouldn't trigger immediately is for a unit that needs to
  469. //close to range before firing the special attack. A case that would trigger immediately
  470. //is the napalm strike. If we don't call this now, it's up to the update module to do so.
  471. if( !getSpecialPowerModuleData()->m_updateModuleStartsAttack )
  472. {
  473. triggerSpecialPower( obj->getPosition() );
  474. }
  475. }
  476. //-------------------------------------------------------------------------------------------------
  477. //-------------------------------------------------------------------------------------------------
  478. void SpecialPowerModule::doSpecialPowerAtLocation( const Coord3D *loc, UnsignedInt commandOptions )
  479. {
  480. if (m_pausedCount > 0 || getObject()->isDisabled()) {
  481. return;
  482. }
  483. //This tells the update module that we want to do our special power. The update modules
  484. //will then start processing each frame.
  485. initiateIntentToDoSpecialPower( NULL, loc, commandOptions );
  486. //Only trigger the special power immediately if the updatemodule doesn't start the attack.
  487. //An example of a case that wouldn't trigger immediately is for a unit that needs to
  488. //close to range before firing the special attack. A case that would trigger immediately
  489. //is the napalm strike. If we don't call this now, it's up to the update module to do so.
  490. if( !getSpecialPowerModuleData()->m_updateModuleStartsAttack )
  491. {
  492. triggerSpecialPower( loc );
  493. }
  494. }
  495. //-------------------------------------------------------------------------------------------------
  496. //-------------------------------------------------------------------------------------------------
  497. void SpecialPowerModule::doSpecialPowerAtMultipleLocations( const Coord3D *locations, Int locCount, UnsignedInt commandOptions )
  498. {
  499. if (m_pausedCount > 0 || getObject()->isDisabled()) {
  500. return;
  501. }
  502. //This tells the update module that we want to do our special power. The update modules
  503. //will then start processing each frame.
  504. initiateIntentToDoSpecialPower( NULL, locations, commandOptions, locCount );
  505. //Only trigger the special power immediately if the updatemodule doesn't start the attack.
  506. //An example of a case that wouldn't trigger immediately is for a unit that needs to
  507. //close to range before firing the special attack. A case that would trigger immediately
  508. //is the napalm strike. If we don't call this now, it's up to the update module to do so.
  509. if( !getSpecialPowerModuleData()->m_updateModuleStartsAttack )
  510. {
  511. triggerSpecialPower( NULL );// This type doesn't create view objects
  512. }
  513. }
  514. //-------------------------------------------------------------------------------------------------
  515. //-------------------------------------------------------------------------------------------------
  516. void SpecialPowerModule::pauseCountdown( Bool pause )
  517. {
  518. if (pause)// If pausing
  519. {
  520. if( m_pausedCount == 0 )
  521. {
  522. // Only record this with the first pausing, otherwise upon final unpausing you would get credited the time
  523. // between pauses as time served.
  524. m_pausedOnFrame = TheGameLogic->getFrame();
  525. m_pausedPercent = getPercentReady();
  526. }
  527. ++m_pausedCount;
  528. }
  529. else if( m_pausedCount > 0 )//Else if unpausing, but only if I am in fact paused, so multiple unpauses don't break.
  530. {
  531. --m_pausedCount;
  532. // And only update the ready time if we are fully unpaused now.
  533. if( m_pausedCount == 0 )
  534. {
  535. m_availableOnFrame += (TheGameLogic->getFrame() - m_pausedOnFrame);
  536. }
  537. }
  538. } // end pauseCountdown
  539. //-------------------------------------------------------------------------------------------------
  540. //-------------------------------------------------------------------------------------------------
  541. UnsignedInt SpecialPowerModule::getReadyFrame( void ) const
  542. {
  543. if ( getSpecialPowerTemplate()->isSharedNSync() )
  544. {
  545. const Object* obj = getObject();
  546. if ( obj )
  547. {
  548. Player *player = getObject()->getControllingPlayer();
  549. if ( player )
  550. return player->getOrStartSpecialPowerReadyFrame( getSpecialPowerTemplate() );
  551. }
  552. }
  553. if (m_pausedCount > 0 || getObject()->isDisabled())
  554. {
  555. Int pausedFrames = TheGameLogic->getFrame() - m_pausedOnFrame;
  556. return m_availableOnFrame + pausedFrames;
  557. }
  558. else
  559. {
  560. return m_availableOnFrame;
  561. }
  562. }
  563. // ------------------------------------------------------------------------------------------------
  564. /** CRC */
  565. // ------------------------------------------------------------------------------------------------
  566. void SpecialPowerModule::crc( Xfer *xfer )
  567. {
  568. // extend base class
  569. BehaviorModule::crc( xfer );
  570. } // end crc
  571. // ------------------------------------------------------------------------------------------------
  572. /** Xfer method
  573. * Version Info:
  574. * 1: Initial version */
  575. // ------------------------------------------------------------------------------------------------
  576. void SpecialPowerModule::xfer( Xfer *xfer )
  577. {
  578. // version
  579. XferVersion currentVersion = 1;
  580. XferVersion version = currentVersion;
  581. xfer->xferVersion( &version, currentVersion );
  582. // extend base class
  583. BehaviorModule::xfer( xfer );
  584. // available on frame
  585. xfer->xferUnsignedInt( &m_availableOnFrame );
  586. // paused by script
  587. xfer->xferInt( &m_pausedCount );
  588. // paused on frame
  589. xfer->xferUnsignedInt( &m_pausedOnFrame );
  590. // paused percent
  591. xfer->xferReal( &m_pausedPercent );
  592. } // end xfer
  593. // ------------------------------------------------------------------------------------------------
  594. /** Load post process */
  595. // ------------------------------------------------------------------------------------------------
  596. void SpecialPowerModule::loadPostProcess( void )
  597. {
  598. // extend base class
  599. BehaviorModule::loadPostProcess();
  600. // Now, if we find that we have just come into being,
  601. // but there is already a science granted for our shared superweapon,
  602. // lets make sure TheIngameUI knows about our public timer
  603. // add this weapon to the UI if it has a public timer for all to see
  604. if( m_pausedCount == 0 &&
  605. getSpecialPowerTemplate()->isSharedNSync() == TRUE &&
  606. getSpecialPowerTemplate()->hasPublicTimer() == TRUE &&
  607. getObject()->getControllingPlayer() &&
  608. getObject()->isKindOf( KINDOF_STRUCTURE ) )
  609. {
  610. TheInGameUI->addSuperweapon( getObject()->getControllingPlayer()->getPlayerIndex(),
  611. getPowerName(),
  612. getObject()->getID(),
  613. getSpecialPowerModuleData()->m_specialPowerTemplate );
  614. }
  615. } // end loadPostProcess