SpecialPowerModule.cpp 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877
  1. /*
  2. ** Command & Conquer Generals Zero Hour(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/Module/SpecialPowerUpdateModule.h"
  45. #include "GameLogic/ScriptEngine.h"
  46. #include "GameClient/Eva.h"
  47. #include "GameClient/InGameUI.h"
  48. #include "GameClient/ControlBar.h"
  49. #ifdef _INTERNAL
  50. // for occasional debugging...
  51. //#pragma optimize("", off)
  52. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  53. #endif
  54. //-------------------------------------------------------------------------------------------------
  55. //-------------------------------------------------------------------------------------------------
  56. SpecialPowerModuleData::SpecialPowerModuleData()
  57. {
  58. m_specialPowerTemplate = NULL;
  59. m_updateModuleStartsAttack = false;
  60. m_startsPaused = FALSE;
  61. m_scriptedSpecialPowerOnly = FALSE;
  62. } // end SpecialPowerModuleData
  63. //-------------------------------------------------------------------------------------------------
  64. //-------------------------------------------------------------------------------------------------
  65. /* static */ void SpecialPowerModuleData::buildFieldParse(MultiIniFieldParse& p)
  66. {
  67. BehaviorModuleData::buildFieldParse( p );
  68. static const FieldParse dataFieldParse[] =
  69. {
  70. { "SpecialPowerTemplate", INI::parseSpecialPowerTemplate, NULL, offsetof( SpecialPowerModuleData, m_specialPowerTemplate ) },
  71. { "UpdateModuleStartsAttack", INI::parseBool, NULL, offsetof( SpecialPowerModuleData, m_updateModuleStartsAttack ) },
  72. { "StartsPaused", INI::parseBool, NULL, offsetof( SpecialPowerModuleData, m_startsPaused ) },
  73. { "InitiateSound", INI::parseAudioEventRTS, NULL, offsetof( SpecialPowerModuleData, m_initiateSound ) },
  74. { "ScriptedSpecialPowerOnly", INI::parseBool, NULL, offsetof( SpecialPowerModuleData, m_scriptedSpecialPowerOnly ) },
  75. { 0, 0, 0, 0 }
  76. };
  77. p.add(dataFieldParse);
  78. } // end buildFieldParse
  79. ///////////////////////////////////////////////////////////////////////////////////////////////////
  80. ///////////////////////////////////////////////////////////////////////////////////////////////////
  81. ///////////////////////////////////////////////////////////////////////////////////////////////////
  82. //-------------------------------------------------------------------------------------------------
  83. //-------------------------------------------------------------------------------------------------
  84. SpecialPowerModule::SpecialPowerModule( Thing *thing, const ModuleData *moduleData )
  85. : BehaviorModule( thing, moduleData )
  86. {
  87. m_availableOnFrame = 0;
  88. m_pausedCount = 0;
  89. m_pausedOnFrame = 0;
  90. m_pausedPercent = 0.0f;
  91. // we won't be able to use the power for X number of frames now
  92. // if we're pre-built, start counting down
  93. if( !getObject()->getStatusBits().test( OBJECT_STATUS_UNDER_CONSTRUCTION ) )
  94. {
  95. //A sharedNSync special only startPowerRecharges when first scienced or when executed,
  96. //Since a new modue with same SPTemplates may construct at any time.
  97. if ( getSpecialPowerTemplate()->isSharedNSync() == FALSE )
  98. startPowerRecharge();
  99. }
  100. // WE USED TO DO THE POLL-EVERYBODY-AND-VOTE-ON-WHO-TO-SYNC-TO THING HERE,
  101. // BUT NO MORE, NOW IT IS HANDLED IN PLAYER
  102. // Some Special powers need to be activated by an Upgrade, so prevent the timer from going until then
  103. const SpecialPowerModuleData *md = (const SpecialPowerModuleData *)moduleData;
  104. if( md->m_startsPaused )
  105. pauseCountdown( TRUE );
  106. resolveSpecialPower();
  107. // Now, if we find that we have just come into being,
  108. // but there is already a science granted for our shared superweapon,
  109. // lets make sure TheIngameUI knows about our public timer
  110. // add this weapon to the UI if it has a public timer for all to see
  111. if( m_pausedCount == 0 &&
  112. getSpecialPowerTemplate()->isSharedNSync() == TRUE &&
  113. getSpecialPowerTemplate()->hasPublicTimer() == TRUE &&
  114. getObject()->getControllingPlayer() &&
  115. getObject()->isKindOf( KINDOF_STRUCTURE ) )
  116. {
  117. TheInGameUI->addSuperweapon( getObject()->getControllingPlayer()->getPlayerIndex(),
  118. getPowerName(),
  119. getObject()->getID(),
  120. getSpecialPowerModuleData()->m_specialPowerTemplate );
  121. }
  122. } // end SpecialPowerModule
  123. //-------------------------------------------------------------------------------------------------
  124. const AudioEventRTS& SpecialPowerModule::getInitiateSound() const
  125. {
  126. return getSpecialPowerModuleData()->m_initiateSound;
  127. }
  128. //-------------------------------------------------------------------------------------------------
  129. //-------------------------------------------------------------------------------------------------
  130. SpecialPowerModule::~SpecialPowerModule()
  131. {
  132. if( getSpecialPowerModuleData()->m_specialPowerTemplate->hasPublicTimer() == TRUE &&
  133. getObject()->getControllingPlayer() )
  134. TheInGameUI->removeSuperweapon( getObject()->getControllingPlayer()->getPlayerIndex(),
  135. getPowerName(),
  136. getObject()->getID(),
  137. getSpecialPowerModuleData()->m_specialPowerTemplate );
  138. } // end ~SpecialPowerModule
  139. //-------------------------------------------------------------------------------------------------
  140. void SpecialPowerModule::setReadyFrame( UnsignedInt frame )
  141. {
  142. m_availableOnFrame = frame;
  143. //If a script should change the ready frame, we need to update the paused frame. This value isn't
  144. //used directly to determine if paused or not... it uses m_pausedCount.
  145. m_pausedOnFrame = TheGameLogic->getFrame();
  146. }
  147. //-------------------------------------------------------------------------------------------------
  148. //-------------------------------------------------------------------------------------------------
  149. void SpecialPowerModule::resolveSpecialPower( void )
  150. {
  151. /*
  152. // if we're pre-built, and not from a command center, and a building register us with the UI
  153. if( getSpecialPowerModuleData()->m_specialPowerTemplate->hasPublicTimer() == TRUE &&
  154. TheGameLogic->getFrame() == 0 && getObject()->getControllingPlayer() &&
  155. getObject()->isKindOf( KINDOF_COMMANDCENTER ) == FALSE &&
  156. getObject()->isKindOf( KINDOF_STRUCTURE ) )
  157. {
  158. //KM: The KINDOF_STRUCTURE check was made to prevent scripted bombers from registering their
  159. // special powers as public timers.
  160. TheInGameUI->addSuperweapon( getObject()->getControllingPlayer()->getPlayerIndex(),
  161. getPowerName(),
  162. getObject()->getID(),
  163. getSpecialPowerModuleData()->m_specialPowerTemplate );
  164. }
  165. */
  166. }
  167. //-------------------------------------------------------------------------------------------------
  168. //-------------------------------------------------------------------------------------------------
  169. void SpecialPowerModule::onSpecialPowerCreation( void )
  170. {
  171. // THIS gets called by addScience(), that is, when the General has purchased a new special power,
  172. // and this module is thus activated.
  173. // start a power recharge going
  174. startPowerRecharge();
  175. // Dustin wants these special powers to start ready to fire,
  176. // so here (and only here) we will expressly set them to ready-now.
  177. if ( getSpecialPowerTemplate()->isSharedNSync())
  178. {
  179. Player *player = getObject()->getControllingPlayer();
  180. if ( player )
  181. {
  182. player->expressSpecialPowerReadyFrame( getSpecialPowerTemplate(), TheGameLogic->getFrame() );
  183. m_availableOnFrame = player->getOrStartSpecialPowerReadyFrame( getSpecialPowerTemplate() );
  184. }
  185. }
  186. // Some Special powers need to be activated by an Upgrade, so prevent the timer from going until then
  187. const SpecialPowerModuleData *md = getSpecialPowerModuleData();
  188. if( md->m_startsPaused )
  189. pauseCountdown( TRUE );
  190. // add this weapon to the UI if it has a public timer for all to see
  191. if( getSpecialPowerModuleData()->m_specialPowerTemplate->hasPublicTimer() == TRUE &&
  192. getObject()->getControllingPlayer() &&
  193. getObject()->isKindOf( KINDOF_STRUCTURE ) )
  194. {
  195. TheInGameUI->addSuperweapon( getObject()->getControllingPlayer()->getPlayerIndex(),
  196. getPowerName(),
  197. getObject()->getID(),
  198. getSpecialPowerModuleData()->m_specialPowerTemplate );
  199. }
  200. }
  201. //-------------------------------------------------------------------------------------------------
  202. //-------------------------------------------------------------------------------------------------
  203. ScienceType SpecialPowerModule::getRequiredScience( void ) const
  204. {
  205. return getSpecialPowerModuleData()->m_specialPowerTemplate->getRequiredScience();
  206. } // end ~SpecialPowerModule
  207. //-------------------------------------------------------------------------------------------------
  208. //-------------------------------------------------------------------------------------------------
  209. const SpecialPowerTemplate * SpecialPowerModule::getSpecialPowerTemplate( void ) const
  210. {
  211. return getSpecialPowerModuleData()->m_specialPowerTemplate;
  212. } // end ~SpecialPowerModule
  213. //-------------------------------------------------------------------------------------------------
  214. //-------------------------------------------------------------------------------------------------
  215. AsciiString SpecialPowerModule::getPowerName( void ) const
  216. {
  217. return getSpecialPowerModuleData()->m_specialPowerTemplate->getName();
  218. } // end ~SpecialPowerModule
  219. //-------------------------------------------------------------------------------------------------
  220. /** Is this module designed for the power identier template passed in? */
  221. //-------------------------------------------------------------------------------------------------
  222. Bool SpecialPowerModule::isModuleForPower( const SpecialPowerTemplate *specialPowerTemplate ) const
  223. {
  224. // get the module data
  225. const SpecialPowerModuleData *modData = getSpecialPowerModuleData();
  226. //
  227. // if the special power template defined in the module data matches the template we want
  228. // to check then we are for it!
  229. //
  230. if( modData->m_specialPowerTemplate == specialPowerTemplate )
  231. {
  232. //We match templates.
  233. return TRUE;
  234. }
  235. //We don't match templates.
  236. return FALSE;
  237. } // end canExecutePower
  238. //-------------------------------------------------------------------------------------------------
  239. /** Is this special power ready to use */
  240. //-------------------------------------------------------------------------------------------------
  241. Bool SpecialPowerModule::isReady() const
  242. {
  243. #if defined(_DEBUG) || defined(_INTERNAL) || defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE)
  244. // this is a cheat ... remove this for release!
  245. if( TheGlobalData->m_specialPowerUsesDelay == FALSE )
  246. return TRUE;
  247. #endif
  248. const Object* obj = getObject();
  249. const SpecialPowerModuleData *modData = getSpecialPowerModuleData();
  250. if ( obj && modData )
  251. {
  252. Player *player = getObject()->getControllingPlayer();
  253. if ( player )
  254. {
  255. if ( modData->m_specialPowerTemplate->isSharedNSync())
  256. return (TheGameLogic->getFrame() >= player->getOrStartSpecialPowerReadyFrame( modData->m_specialPowerTemplate ) );
  257. }
  258. }
  259. return (m_pausedCount == 0) && (TheGameLogic->getFrame() >= m_availableOnFrame);
  260. } // end isReady
  261. //-------------------------------------------------------------------------------------------------
  262. /** Get the percentage ready a special power is to use
  263. * 1.0f = ready now
  264. * 0.5f = 50% ready
  265. * 0.2f = 20% ready
  266. * etc ... */
  267. //-------------------------------------------------------------------------------------------------
  268. Real SpecialPowerModule::getPercentReady() const
  269. {
  270. if( m_pausedCount > 0 && m_pausedPercent == 1.0f )
  271. {
  272. //Don't consider it ready if paused.
  273. return 0.99999f;
  274. }
  275. #if defined(_DEBUG) || defined(_INTERNAL) || defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE)
  276. if( TheGlobalData->m_specialPowerUsesDelay == FALSE )
  277. return 1.0f;
  278. #endif
  279. // easy case ... is ready
  280. if( isReady() )
  281. return 1.0f;
  282. if( m_pausedCount > 0 )
  283. {
  284. return m_pausedPercent;
  285. }
  286. // get the module data
  287. const SpecialPowerModuleData *modData = getSpecialPowerModuleData();
  288. // sanity
  289. if( modData->m_specialPowerTemplate == NULL )
  290. return 0.0f;
  291. UnsignedInt readyFrame = m_availableOnFrame;
  292. //unless
  293. const Object* obj = getObject();
  294. if ( obj )
  295. {
  296. Player *player = getObject()->getControllingPlayer();
  297. if ( player )
  298. {
  299. if ( modData->m_specialPowerTemplate->isSharedNSync())
  300. {
  301. readyFrame = player->getOrStartSpecialPowerReadyFrame( getSpecialPowerTemplate() );
  302. }
  303. }
  304. }
  305. // calculate the percent
  306. Real percent = 1.0f - ((readyFrame - TheGameLogic->getFrame()) /
  307. (Real)modData->m_specialPowerTemplate->getReloadTime());
  308. return percent;
  309. }
  310. //-------------------------------------------------------------------------------------------------
  311. // A special power module that is only supposed to be fired via scripts. An example of this
  312. // are the various cargo plane units we have. Scripters can launch specials from them after
  313. // specifying a waypoint path for them to follow them.
  314. //-------------------------------------------------------------------------------------------------
  315. Bool SpecialPowerModule::isScriptOnly() const
  316. {
  317. const SpecialPowerModuleData *modData = getSpecialPowerModuleData();
  318. return modData->m_scriptedSpecialPowerOnly;
  319. }
  320. //-------------------------------------------------------------------------------------------------
  321. /** A special power has been used ... start the recharge process by computing the frame
  322. * we will become fully available on in the future again */
  323. //-------------------------------------------------------------------------------------------------
  324. void SpecialPowerModule::startPowerRecharge()
  325. {
  326. #if defined(_DEBUG) || defined(_INTERNAL) || defined(_ALLOW_DEBUG_CHEATS_IN_RELEASE)
  327. // this is a cheat ... remove this for release!
  328. if( TheGlobalData->m_specialPowerUsesDelay == FALSE )
  329. return;
  330. #endif
  331. const SpecialPowerModuleData *modData = getSpecialPowerModuleData();
  332. // sanity
  333. if( modData->m_specialPowerTemplate == NULL )
  334. {
  335. DEBUG_CRASH(("special power not found"));
  336. return;
  337. }
  338. Object* obj = getObject();
  339. if (!obj)
  340. return;
  341. Player* player = getObject()->getControllingPlayer();
  342. if (!player)
  343. return;
  344. //Here, we make sure that general specials work as one between command centers
  345. // only factory type faction buildings should do this, and only with generals powers (in general)
  346. // but there are no restrictions on the use of SharedNSync on specialPowerTemplates at large
  347. if ( modData->m_specialPowerTemplate->isSharedNSync() )
  348. {
  349. player->resetOrStartSpecialPowerReadyFrame( modData->m_specialPowerTemplate );
  350. }
  351. else
  352. {
  353. // set the frame we will be 100% available on now
  354. m_availableOnFrame = TheGameLogic->getFrame() + getSpecialPowerTemplate()->getReloadTime();
  355. }
  356. }
  357. //-------------------------------------------------------------------------------------------------
  358. //-------------------------------------------------------------------------------------------------
  359. Bool SpecialPowerModule::initiateIntentToDoSpecialPower( const Object *targetObj, const Coord3D *targetPos, const Waypoint *way, UnsignedInt commandOptions )
  360. {
  361. Bool valid = false;
  362. // tell our update modules that we intend to do this special power.
  363. for( BehaviorModule** u = getObject()->getBehaviorModules(); *u; ++u )
  364. {
  365. SpecialPowerUpdateInterface* spu = (*u)->getSpecialPowerUpdateInterface();
  366. if( spu )
  367. {
  368. //Validate that we are calling the correct module!
  369. if( isModuleForPower( getSpecialPowerModuleData()->m_specialPowerTemplate ) )
  370. {
  371. if( spu->doesSpecialPowerUpdatePassScienceTest() )
  372. {
  373. if( spu->initiateIntentToDoSpecialPower( getSpecialPowerModuleData()->m_specialPowerTemplate, targetObj, targetPos, way, commandOptions ) )
  374. {
  375. //Kris: Aug 2003
  376. //We have executed the special power, so don't try to execute any more. This logic
  377. //was changed for multi-level spectres. Before, multiple modules would get launched
  378. //causing 2 or 3 spectres to be created.
  379. valid = true;
  380. break;
  381. }
  382. }
  383. }
  384. }
  385. }
  386. getObject()->getControllingPlayer()->getAcademyStats()->recordSpecialPowerUsed( getSpecialPowerModuleData()->m_specialPowerTemplate );
  387. //If we depend on our update module to trigger the special power, make sure we have the
  388. //appropriate update module!
  389. if( !valid && getSpecialPowerModuleData()->m_updateModuleStartsAttack )
  390. {
  391. DEBUG_CRASH( ("Object does not contain a special power module to execute. Did you forget to add it to the object INI?\n"));
  392. //DEBUG_CRASH(( "Object does not contain special power module (%s) to execute. Did you forget to add it to the object INI?\n",
  393. // command->m_specialPower->getName().str() ));
  394. }
  395. return valid;
  396. }
  397. //-------------------------------------------------------------------------------------------------
  398. //-------------------------------------------------------------------------------------------------
  399. void SpecialPowerModule::triggerSpecialPower( const Coord3D *location )
  400. {
  401. aboutToDoSpecialPower( location ); // do BEFORE recharge
  402. createViewObject(location);
  403. // we won't be able to use the power for X number of frames now
  404. startPowerRecharge();
  405. }
  406. //-------------------------------------------------------------------------------------------------
  407. //-------------------------------------------------------------------------------------------------
  408. void SpecialPowerModule::createViewObject( const Coord3D *location )
  409. {
  410. const SpecialPowerModuleData *modData = getSpecialPowerModuleData();
  411. const SpecialPowerTemplate *powerTemplate = modData->m_specialPowerTemplate;
  412. if( modData == NULL || powerTemplate == NULL )
  413. return;
  414. Real visionRange = powerTemplate->getViewObjectRange();
  415. UnsignedInt visionDuration = powerTemplate->getViewObjectDuration();
  416. if( visionRange == 0 || visionDuration == 0 )
  417. return; // We don't want a view object at all.
  418. AsciiString objectName = TheGlobalData->m_specialPowerViewObjectName;
  419. if( objectName.isEmpty() )
  420. return;
  421. const ThingTemplate *viewObjectTemplate = TheThingFactory->findTemplate( objectName );
  422. if( viewObjectTemplate == NULL )
  423. return;
  424. Object *viewObject = TheThingFactory->newObject( viewObjectTemplate, getObject()->getControllingPlayer()->getDefaultTeam() );
  425. if( viewObject == NULL )
  426. return;
  427. viewObject->setPosition( location );
  428. viewObject->setShroudClearingRange( visionRange );
  429. static NameKeyType key_DeletionUpdate = NAMEKEY("DeletionUpdate");
  430. DeletionUpdate* dup = (DeletionUpdate*)viewObject->findUpdateModule(key_DeletionUpdate);
  431. if( dup )
  432. {
  433. dup->setLifetimeRange( visionDuration, visionDuration );
  434. }
  435. }
  436. //-------------------------------------------------------------------------------------------------
  437. //-------------------------------------------------------------------------------------------------
  438. void SpecialPowerModule::markSpecialPowerTriggered( const Coord3D *location )
  439. {
  440. triggerSpecialPower( location );
  441. }
  442. //-------------------------------------------------------------------------------------------------
  443. //-------------------------------------------------------------------------------------------------
  444. void SpecialPowerModule::aboutToDoSpecialPower( const Coord3D *location )
  445. {
  446. // Tell the scripting engine!
  447. TheScriptEngine->notifyOfTriggeredSpecialPower(
  448. getObject()->getControllingPlayer()->getPlayerIndex(),
  449. getSpecialPowerModuleData()->m_specialPowerTemplate->getName(),
  450. getObject()->getID());
  451. // Let EVA do her thing
  452. SpecialPowerType type = getSpecialPowerModuleData()->m_specialPowerTemplate->getSpecialPowerType();
  453. Player *localPlayer = ThePlayerList->getLocalPlayer();
  454. // Only play the EVA sounds if this is not the local player, and the local player doesn't consider the
  455. // person an enemy.
  456. // Kris: Actually, all players need to hear these warnings.
  457. // Ian: But now there are different Eva messages depending on who launched
  458. //if (localPlayer != getObject()->getControllingPlayer() && localPlayer->getRelationship(getObject()->getTeam()) != ENEMIES)
  459. {
  460. if( type == SPECIAL_PARTICLE_UPLINK_CANNON || type == SUPW_SPECIAL_PARTICLE_UPLINK_CANNON || type == LAZR_SPECIAL_PARTICLE_UPLINK_CANNON )
  461. {
  462. if ( localPlayer == getObject()->getControllingPlayer() )
  463. {
  464. TheEva->setShouldPlay(EVA_SuperweaponLaunched_Own_ParticleCannon);
  465. }
  466. else if ( localPlayer->getRelationship(getObject()->getTeam()) != ENEMIES )
  467. {
  468. // Note: counting relationship NEUTRAL as ally. Not sure if this makes a difference???
  469. TheEva->setShouldPlay(EVA_SuperweaponLaunched_Ally_ParticleCannon);
  470. }
  471. else
  472. {
  473. TheEva->setShouldPlay(EVA_SuperweaponLaunched_Enemy_ParticleCannon);
  474. }
  475. }
  476. else if( type == SPECIAL_NEUTRON_MISSILE || type == NUKE_SPECIAL_NEUTRON_MISSILE || type == SUPW_SPECIAL_NEUTRON_MISSILE )
  477. {
  478. if ( localPlayer == getObject()->getControllingPlayer() )
  479. {
  480. TheEva->setShouldPlay(EVA_SuperweaponLaunched_Own_Nuke);
  481. }
  482. else if ( localPlayer->getRelationship(getObject()->getTeam()) != ENEMIES )
  483. {
  484. // Note: counting relationship NEUTRAL as ally. Not sure if this makes a difference???
  485. TheEva->setShouldPlay(EVA_SuperweaponLaunched_Ally_Nuke);
  486. }
  487. else
  488. {
  489. TheEva->setShouldPlay(EVA_SuperweaponLaunched_Enemy_Nuke);
  490. }
  491. }
  492. else if (type == SPECIAL_SCUD_STORM)
  493. {
  494. if ( localPlayer == getObject()->getControllingPlayer() )
  495. {
  496. TheEva->setShouldPlay(EVA_SuperweaponLaunched_Own_ScudStorm);
  497. }
  498. else if ( localPlayer->getRelationship(getObject()->getTeam()) != ENEMIES )
  499. {
  500. // Note: counting relationship NEUTRAL as ally. Not sure if this makes a difference???
  501. TheEva->setShouldPlay(EVA_SuperweaponLaunched_Ally_ScudStorm);
  502. }
  503. else
  504. {
  505. TheEva->setShouldPlay(EVA_SuperweaponLaunched_Enemy_ScudStorm);
  506. }
  507. }
  508. else if (type == SPECIAL_GPS_SCRAMBLER || type == SLTH_SPECIAL_GPS_SCRAMBLER )
  509. {
  510. // This is Ghetto. Voices should be ini lines in the special power entry. You shouldn't have to
  511. // add to an enum to get a new voice
  512. if ( localPlayer == getObject()->getControllingPlayer() )
  513. {
  514. TheEva->setShouldPlay(EVA_SuperweaponLaunched_Own_GPS_Scrambler);
  515. }
  516. else if ( localPlayer->getRelationship(getObject()->getTeam()) != ENEMIES )
  517. {
  518. // Note: counting relationship NEUTRAL as ally. Not sure if this makes a difference???
  519. TheEva->setShouldPlay(EVA_SuperweaponLaunched_Ally_GPS_Scrambler);
  520. }
  521. else
  522. {
  523. TheEva->setShouldPlay(EVA_SuperweaponLaunched_Enemy_GPS_Scrambler);
  524. }
  525. }
  526. else if (type == SPECIAL_SNEAK_ATTACK)
  527. {
  528. if ( localPlayer == getObject()->getControllingPlayer() )
  529. {
  530. TheEva->setShouldPlay(EVA_SuperweaponLaunched_Own_Sneak_Attack);
  531. }
  532. else if ( localPlayer->getRelationship(getObject()->getTeam()) != ENEMIES )
  533. {
  534. // Note: counting relationship NEUTRAL as ally. Not sure if this makes a difference???
  535. TheEva->setShouldPlay(EVA_SuperweaponLaunched_Ally_Sneak_Attack);
  536. }
  537. else
  538. {
  539. TheEva->setShouldPlay(EVA_SuperweaponLaunched_Enemy_Sneak_Attack);
  540. }
  541. }
  542. }
  543. // get module data
  544. const SpecialPowerModuleData *modData = getSpecialPowerModuleData();
  545. // play our initiate sound if we have one
  546. AudioEventRTS audioEvent = *modData->m_specialPowerTemplate->getInitiateSound();
  547. audioEvent.setObjectID(getObject()->getID());
  548. TheAudio->addAudioEvent( &audioEvent );
  549. // play sound at target location if specified
  550. if( location )
  551. {
  552. AudioEventRTS soundAtLocation = *modData->m_specialPowerTemplate->getInitiateAtTargetSound();
  553. soundAtLocation.setPosition( location );
  554. soundAtLocation.setPlayerIndex(getObject()->getControllingPlayer()->getPlayerIndex());
  555. TheAudio->addAudioEvent( &soundAtLocation );
  556. } // end if
  557. }
  558. //-------------------------------------------------------------------------------------------------
  559. //By default, special powers are not triggered by it's update module -- in which case
  560. //it triggers it and resets its timer immediately. When the update module triggers it,
  561. //then all we do is initiate the special power, and trust that the update module will
  562. //do the rest.
  563. //-------------------------------------------------------------------------------------------------
  564. void SpecialPowerModule::doSpecialPower( UnsignedInt commandOptions )
  565. {
  566. if (m_pausedCount > 0 || getObject()->isDisabled()) {
  567. return;
  568. }
  569. //This tells the update module that we want to do our special power. The update modules
  570. //will then start processing each frame.
  571. initiateIntentToDoSpecialPower( NULL, NULL, NULL, commandOptions );
  572. //Only trigger the special power immediately if the updatemodule doesn't start the attack.
  573. //An example of a case that wouldn't trigger immediately is for a unit that needs to
  574. //close to range before firing the special attack. A case that would trigger immediately
  575. //is the napalm strike. If we don't call this now, it's up to the update module to do so.
  576. if( !getSpecialPowerModuleData()->m_updateModuleStartsAttack )
  577. {
  578. triggerSpecialPower( NULL );// Location-less trigger
  579. }
  580. }
  581. //-------------------------------------------------------------------------------------------------
  582. //-------------------------------------------------------------------------------------------------
  583. void SpecialPowerModule::doSpecialPowerAtObject( Object *obj, UnsignedInt commandOptions )
  584. {
  585. if (m_pausedCount > 0 || getObject()->isDisabled()) {
  586. return;
  587. }
  588. //This tells the update module that we want to do our special power. The update modules
  589. //will then start processing each frame.
  590. initiateIntentToDoSpecialPower( obj, NULL, NULL, commandOptions );
  591. //Only trigger the special power immediately if the updatemodule doesn't start the attack.
  592. //An example of a case that wouldn't trigger immediately is for a unit that needs to
  593. //close to range before firing the special attack. A case that would trigger immediately
  594. //is the napalm strike. If we don't call this now, it's up to the update module to do so.
  595. if( !getSpecialPowerModuleData()->m_updateModuleStartsAttack )
  596. {
  597. triggerSpecialPower( obj->getPosition() );
  598. }
  599. }
  600. //-------------------------------------------------------------------------------------------------
  601. //-------------------------------------------------------------------------------------------------
  602. void SpecialPowerModule::doSpecialPowerAtLocation( const Coord3D *loc, Real angle, UnsignedInt commandOptions )
  603. {
  604. if (m_pausedCount > 0 || getObject()->isDisabled()) {
  605. return;
  606. }
  607. //This tells the update module that we want to do our special power. The update modules
  608. //will then start processing each frame.
  609. initiateIntentToDoSpecialPower( NULL, loc, NULL, commandOptions );
  610. //Only trigger the special power immediately if the updatemodule doesn't start the attack.
  611. //An example of a case that wouldn't trigger immediately is for a unit that needs to
  612. //close to range before firing the special attack. A case that would trigger immediately
  613. //is the napalm strike. If we don't call this now, it's up to the update module to do so.
  614. if( !getSpecialPowerModuleData()->m_updateModuleStartsAttack )
  615. {
  616. triggerSpecialPower( loc );
  617. }
  618. }
  619. //-------------------------------------------------------------------------------------------------
  620. //-------------------------------------------------------------------------------------------------
  621. void SpecialPowerModule::doSpecialPowerUsingWaypoints( const Waypoint *way, UnsignedInt commandOptions )
  622. {
  623. if (m_pausedCount > 0 || getObject()->isDisabled()) {
  624. return;
  625. }
  626. //This tells the update module that we want to do our special power. The update modules
  627. //will then start processing each frame.
  628. initiateIntentToDoSpecialPower( NULL, NULL, way, commandOptions );
  629. //Only trigger the special power immediately if the updatemodule doesn't start the attack.
  630. //An example of a case that wouldn't trigger immediately is for a unit that needs to
  631. //close to range before firing the special attack. A case that would trigger immediately
  632. //is the napalm strike. If we don't call this now, it's up to the update module to do so.
  633. if( !getSpecialPowerModuleData()->m_updateModuleStartsAttack )
  634. {
  635. triggerSpecialPower( NULL );// This type doesn't create view objects
  636. }
  637. }
  638. //-------------------------------------------------------------------------------------------------
  639. //-------------------------------------------------------------------------------------------------
  640. void SpecialPowerModule::pauseCountdown( Bool pause )
  641. {
  642. if (pause)// If pausing
  643. {
  644. if( m_pausedCount == 0 )
  645. {
  646. // Only record this with the first pausing, otherwise upon final unpausing you would get credited the time
  647. // between pauses as time served.
  648. m_pausedOnFrame = TheGameLogic->getFrame();
  649. m_pausedPercent = getPercentReady();
  650. }
  651. ++m_pausedCount;
  652. }
  653. else if( m_pausedCount > 0 )//Else if unpausing, but only if I am in fact paused, so multiple unpauses don't break.
  654. {
  655. --m_pausedCount;
  656. // And only update the ready time if we are fully unpaused now.
  657. if( m_pausedCount == 0 )
  658. {
  659. m_availableOnFrame += (TheGameLogic->getFrame() - m_pausedOnFrame);
  660. }
  661. }
  662. } // end pauseCountdown
  663. //-------------------------------------------------------------------------------------------------
  664. //-------------------------------------------------------------------------------------------------
  665. UnsignedInt SpecialPowerModule::getReadyFrame( void ) const
  666. {
  667. if ( getSpecialPowerTemplate()->isSharedNSync() )
  668. {
  669. const Object* obj = getObject();
  670. if ( obj )
  671. {
  672. Player *player = getObject()->getControllingPlayer();
  673. if ( player )
  674. return player->getOrStartSpecialPowerReadyFrame( getSpecialPowerTemplate() );
  675. }
  676. }
  677. if (m_pausedCount > 0 || getObject()->isDisabled())
  678. {
  679. Int pausedFrames = TheGameLogic->getFrame() - m_pausedOnFrame;
  680. return m_availableOnFrame + pausedFrames;
  681. }
  682. else
  683. {
  684. return m_availableOnFrame;
  685. }
  686. }
  687. // ------------------------------------------------------------------------------------------------
  688. /** CRC */
  689. // ------------------------------------------------------------------------------------------------
  690. void SpecialPowerModule::crc( Xfer *xfer )
  691. {
  692. // extend base class
  693. BehaviorModule::crc( xfer );
  694. } // end crc
  695. // ------------------------------------------------------------------------------------------------
  696. /** Xfer method
  697. * Version Info:
  698. * 1: Initial version */
  699. // ------------------------------------------------------------------------------------------------
  700. void SpecialPowerModule::xfer( Xfer *xfer )
  701. {
  702. // version
  703. XferVersion currentVersion = 1;
  704. XferVersion version = currentVersion;
  705. xfer->xferVersion( &version, currentVersion );
  706. // extend base class
  707. BehaviorModule::xfer( xfer );
  708. // available on frame
  709. xfer->xferUnsignedInt( &m_availableOnFrame );
  710. // paused by script
  711. xfer->xferInt( &m_pausedCount );
  712. // paused on frame
  713. xfer->xferUnsignedInt( &m_pausedOnFrame );
  714. // paused percent
  715. xfer->xferReal( &m_pausedPercent );
  716. } // end xfer
  717. // ------------------------------------------------------------------------------------------------
  718. /** Load post process */
  719. // ------------------------------------------------------------------------------------------------
  720. void SpecialPowerModule::loadPostProcess( void )
  721. {
  722. // extend base class
  723. BehaviorModule::loadPostProcess();
  724. // Now, if we find that we have just come into being,
  725. // but there is already a science granted for our shared superweapon,
  726. // lets make sure TheIngameUI knows about our public timer
  727. // add this weapon to the UI if it has a public timer for all to see
  728. if( m_pausedCount == 0 &&
  729. getSpecialPowerTemplate()->isSharedNSync() == TRUE &&
  730. getSpecialPowerTemplate()->hasPublicTimer() == TRUE &&
  731. getObject()->getControllingPlayer() &&
  732. getObject()->isKindOf( KINDOF_STRUCTURE ) )
  733. {
  734. TheInGameUI->addSuperweapon( getObject()->getControllingPlayer()->getPlayerIndex(),
  735. getPowerName(),
  736. getObject()->getID(),
  737. getSpecialPowerModuleData()->m_specialPowerTemplate );
  738. }
  739. } // end loadPostProcess