ParticleUplinkCannonUpdate.cpp 50 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395
  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: ParticleUplinkCannonUpdate.cpp //////////////////////////////////////////////////////////////////////////
  24. // Author: Kris Morness, September 2002
  25. // Desc: Update module to handle building states and weapon firing of the particle uplink cannon.
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  28. #define DEFINE_DAMAGE_NAMES
  29. #define DEFINE_DEATH_NAMES
  30. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  31. #include "Common\ThingTemplate.h"
  32. #include "Common\ThingFactory.h"
  33. #include "Common\Player.h"
  34. #include "Common\PlayerList.h"
  35. #include "Common\Xfer.h"
  36. #include "Common\ClientUpdateModule.h"
  37. #include "GameClient\ControlBar.h"
  38. #include "GameClient\GameClient.h"
  39. #include "GameClient\Drawable.h"
  40. #include "GameClient\ParticleSys.h"
  41. #include "GameClient\FXList.h"
  42. #include "GameLogic\GameLogic.h"
  43. #include "GameLogic\PartitionManager.h"
  44. #include "GameLogic\Object.h"
  45. #include "GameLogic\ObjectIter.h"
  46. #include "GameLogic\Weaponset.h"
  47. #include "GameLogic\Weapon.h"
  48. #include "GameLogic\TerrainLogic.h"
  49. #include "GameLogic\Module\SpecialPowerModule.h"
  50. #include "GameLogic\Module\ParticleUplinkCannonUpdate.h"
  51. #include "GameLogic\Module\PhysicsUpdate.h"
  52. #include "GameLogic\Module\LaserUpdate.h"
  53. #include "GameLogic\Module\ActiveBody.h"
  54. #ifdef _INTERNAL
  55. // for occasional debugging...
  56. //#pragma optimize("", off)
  57. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  58. #endif
  59. //-------------------------------------------------------------------------------------------------
  60. //-------------------------------------------------------------------------------------------------
  61. ParticleUplinkCannonUpdateModuleData::ParticleUplinkCannonUpdateModuleData()
  62. {
  63. m_specialPowerTemplate = NULL;
  64. m_beginChargeFrames = 0;
  65. m_raiseAntennaFrames = 0;
  66. m_readyDelayFrames = 0;
  67. m_outerEffectNumBones = 0;
  68. m_beamTravelFrames = 0;
  69. m_widthGrowFrames = 0;
  70. m_totalFiringFrames = 0;
  71. m_totalScorchMarks = 0;
  72. m_scorchMarkScalar = 1.0f;
  73. m_damageRadiusScalar = 1.0f;
  74. m_groundHitFX = NULL;
  75. m_beamLaunchFX = NULL;
  76. m_framesBetweenLaunchFXRefresh = 30;
  77. m_totalDamagePulses = 0;
  78. m_damagePerSecond = 0.0f;
  79. m_damageType = DAMAGE_LASER;
  80. m_deathType = DEATH_LASERED;
  81. m_revealRange = 0.0f;
  82. m_manualDrivingSpeed = 0.0f;
  83. m_manualFastDrivingSpeed = 0.0f;
  84. m_doubleClickToFastDriveDelay = 500;
  85. //Added by Sadullah Nader
  86. //Initializations inserted
  87. m_swathOfDeathAmplitude = 0.0f;
  88. m_swathOfDeathDistance = 0.0f;
  89. //
  90. }
  91. //-------------------------------------------------------------------------------------------------
  92. /*static*/ void ParticleUplinkCannonUpdateModuleData::buildFieldParse(MultiIniFieldParse& p)
  93. {
  94. ModuleData::buildFieldParse(p);
  95. static const FieldParse dataFieldParse[] =
  96. {
  97. { "SpecialPowerTemplate", INI::parseSpecialPowerTemplate, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_specialPowerTemplate ) },
  98. { "BeginChargeTime", INI::parseDurationUnsignedInt, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_beginChargeFrames ) },
  99. { "RaiseAntennaTime", INI::parseDurationUnsignedInt, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_raiseAntennaFrames ) },
  100. { "ReadyDelayTime", INI::parseDurationUnsignedInt, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_readyDelayFrames ) },
  101. { "WidthGrowTime", INI::parseDurationUnsignedInt, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_widthGrowFrames ) },
  102. { "BeamTravelTime", INI::parseDurationUnsignedInt, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_beamTravelFrames ) },
  103. { "TotalFiringTime", INI::parseDurationUnsignedInt, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_totalFiringFrames ) },
  104. { "RevealRange", INI::parseReal, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_revealRange ) },
  105. { "OuterEffectBoneName", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_outerEffectBaseBoneName ) },
  106. { "OuterEffectNumBones", INI::parseUnsignedInt, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_outerEffectNumBones ) },
  107. { "OuterNodesLightFlareParticleSystem", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_outerNodesLightFlareParticleSystemName ) },
  108. { "OuterNodesMediumFlareParticleSystem", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_outerNodesMediumFlareParticleSystemName ) },
  109. { "OuterNodesIntenseFlareParticleSystem", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_outerNodesIntenseFlareParticleSystemName ) },
  110. { "ConnectorBoneName", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_connectorBoneName ) },
  111. { "ConnectorMediumLaserName", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_connectorMediumLaserNameName ) },
  112. { "ConnectorIntenseLaserName", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_connectorIntenseLaserNameName ) },
  113. { "ConnectorMediumFlare", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_connectorMediumFlareParticleSystemName ) },
  114. { "ConnectorIntenseFlare", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_connectorIntenseFlareParticleSystemName ) },
  115. { "FireBoneName", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_fireBoneName ) },
  116. { "LaserBaseLightFlareParticleSystemName", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_laserBaseLightFlareParticleSystemName ) },
  117. { "LaserBaseMediumFlareParticleSystemName", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_laserBaseMediumFlareParticleSystemName ) },
  118. { "LaserBaseIntenseFlareParticleSystemName", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_laserBaseIntenseFlareParticleSystemName ) },
  119. { "ParticleBeamLaserName", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_particleBeamLaserName ) },
  120. { "SwathOfDeathDistance", INI::parseReal, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_swathOfDeathDistance ) },
  121. { "SwathOfDeathAmplitude", INI::parseReal, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_swathOfDeathAmplitude ) },
  122. { "TotalScorchMarks", INI::parseUnsignedInt, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_totalScorchMarks ) },
  123. { "ScorchMarkScalar", INI::parseReal, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_scorchMarkScalar ) },
  124. { "BeamLaunchFX", INI::parseFXList, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_beamLaunchFX ) },
  125. { "DelayBetweenLaunchFX", INI::parseDurationUnsignedInt, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_framesBetweenLaunchFXRefresh ) },
  126. { "GroundHitFX", INI::parseFXList, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_groundHitFX ) },
  127. { "DamagePerSecond", INI::parseReal, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_damagePerSecond ) },
  128. { "TotalDamagePulses", INI::parseUnsignedInt, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_totalDamagePulses ) },
  129. { "DamageType", INI::parseIndexList, TheDamageNames, offsetof( ParticleUplinkCannonUpdateModuleData, m_damageType ) },
  130. { "DeathType", INI::parseIndexList, TheDeathNames, offsetof( ParticleUplinkCannonUpdateModuleData, m_deathType ) },
  131. { "DamageRadiusScalar", INI::parseReal, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_damageRadiusScalar ) },
  132. { "PoweringUpSoundLoop", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_powerupSoundName ) },
  133. { "UnpackToIdleSoundLoop", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_unpackToReadySoundName ) },
  134. { "FiringToPackSoundLoop", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_firingToIdleSoundName ) },
  135. { "GroundAnnihilationSoundLoop", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_annihilationSoundName ) },
  136. { "DamagePulseRemnantObjectName", INI::parseAsciiString, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_damagePulseRemnantObjectName ) },
  137. { "ManualDrivingSpeed", INI::parseReal, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_manualDrivingSpeed ) },
  138. { "ManualFastDrivingSpeed", INI::parseReal, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_manualFastDrivingSpeed ) },
  139. { "DoubleClickToFastDriveDelay", INI::parseDurationUnsignedInt, NULL, offsetof( ParticleUplinkCannonUpdateModuleData, m_doubleClickToFastDriveDelay ) },
  140. { 0, 0, 0, 0 }
  141. };
  142. p.add(dataFieldParse);
  143. }
  144. //-------------------------------------------------------------------------------------------------
  145. ParticleUplinkCannonUpdate::ParticleUplinkCannonUpdate( Thing *thing, const ModuleData* moduleData ) : UpdateModule( thing, moduleData )
  146. {
  147. m_status = STATUS_IDLE;
  148. m_laserStatus = LASERSTATUS_NONE;
  149. m_frames = 0;
  150. m_specialPowerModule = NULL;
  151. m_groundToOrbitBeamID = INVALID_DRAWABLE_ID;
  152. m_orbitToTargetBeamID = INVALID_DRAWABLE_ID;
  153. m_connectorSystemID = INVALID_PARTICLE_SYSTEM_ID;
  154. m_laserBaseSystemID = INVALID_PARTICLE_SYSTEM_ID;
  155. m_connectorNodePosition.zero();
  156. m_laserOriginPosition.zero();
  157. m_upBonesCached = false;
  158. m_defaultInfoCached = false;
  159. m_invalidSettings = false;
  160. m_manualTargetMode = false;
  161. m_initialTargetPosition.zero();
  162. m_currentTargetPosition.zero();
  163. m_overrideTargetDestination.zero();
  164. m_scorchMarksMade= 0;
  165. m_nextScorchMarkFrame = 0;
  166. m_nextLaunchFXFrame = 0;
  167. m_damagePulsesMade = 0;
  168. m_nextDamagePulseFrame = 0;
  169. m_startAttackFrame = 0;
  170. m_startDecayFrame = 0;
  171. m_lastDrivingClickFrame = 0;
  172. m_2ndLastDrivingClickFrame = 0;
  173. m_clientShroudedLastFrame = FALSE;
  174. for( Int i = 0; i < MAX_OUTER_NODES; i++ )
  175. {
  176. m_outerSystemIDs[ i ] = INVALID_PARTICLE_SYSTEM_ID;
  177. m_laserBeamIDs[ i ] = INVALID_DRAWABLE_ID;
  178. //Initializing (even though they will get cached properly).
  179. m_outerNodePositions[ i ].zero();
  180. m_outerNodeOrientations[ i ].Make_Identity();
  181. }
  182. }
  183. //-------------------------------------------------------------------------------------------------
  184. //-------------------------------------------------------------------------------------------------
  185. void ParticleUplinkCannonUpdate::killEverything()
  186. {
  187. removeAllEffects();
  188. //This laser is independent from the other effects and needs to be specially handled.
  189. if( m_orbitToTargetBeamID )
  190. {
  191. Drawable *beam = TheGameClient->findDrawableByID( m_orbitToTargetBeamID );
  192. if( beam )
  193. {
  194. TheGameClient->destroyDrawable( beam );
  195. }
  196. m_orbitToTargetBeamID = INVALID_DRAWABLE_ID;
  197. }
  198. TheAudio->removeAudioEvent( m_powerupSound.getPlayingHandle() );
  199. TheAudio->removeAudioEvent( m_unpackToReadySound.getPlayingHandle() );
  200. TheAudio->removeAudioEvent( m_firingToIdleSound.getPlayingHandle() );
  201. TheAudio->removeAudioEvent( m_annihilationSound.getPlayingHandle() );
  202. }
  203. //-------------------------------------------------------------------------------------------------
  204. //-------------------------------------------------------------------------------------------------
  205. ParticleUplinkCannonUpdate::~ParticleUplinkCannonUpdate( void )
  206. {
  207. killEverything();
  208. }
  209. //-------------------------------------------------------------------------------------------------
  210. // Validate that we have the necessary data from the ini file.
  211. //-------------------------------------------------------------------------------------------------
  212. void ParticleUplinkCannonUpdate::onObjectCreated()
  213. {
  214. const ParticleUplinkCannonUpdateModuleData *data = getParticleUplinkCannonUpdateModuleData();
  215. Object *obj = getObject();
  216. if( !data->m_specialPowerTemplate )
  217. {
  218. DEBUG_CRASH( ("%s object's ParticleUplinkCannonUpdate lacks access to the SpecialPowerTemplate. Needs to be specified in ini.", obj->getTemplate()->getName().str() ) );
  219. m_invalidSettings = true;
  220. return;
  221. }
  222. m_specialPowerModule = obj->getSpecialPowerModule( data->m_specialPowerTemplate );
  223. m_connectorNodePosition.set( obj->getPosition() );
  224. m_laserOriginPosition.set( obj->getPosition() );
  225. //Create instances of the sounds required.
  226. m_powerupSound.setEventName( data->m_powerupSoundName );
  227. m_unpackToReadySound.setEventName( data->m_unpackToReadySoundName );
  228. m_firingToIdleSound.setEventName( data->m_firingToIdleSoundName );
  229. m_annihilationSound.setEventName( data->m_annihilationSoundName );
  230. TheAudio->getInfoForAudioEvent( &m_powerupSound );
  231. TheAudio->getInfoForAudioEvent( &m_unpackToReadySound );
  232. TheAudio->getInfoForAudioEvent( &m_firingToIdleSound );
  233. TheAudio->getInfoForAudioEvent( &m_annihilationSound );
  234. }
  235. //-------------------------------------------------------------------------------------------------
  236. void ParticleUplinkCannonUpdate::initiateIntentToDoSpecialPower(const SpecialPowerTemplate *specialPowerTemplate, const Object *targetObj, const Coord3D *targetPos, UnsignedInt commandOptions, Int locationCount )
  237. {
  238. const ParticleUplinkCannonUpdateModuleData *data = getParticleUplinkCannonUpdateModuleData();
  239. if( m_specialPowerModule->getSpecialPowerTemplate() != specialPowerTemplate )
  240. {
  241. //Check to make sure our modules are connected.
  242. return;
  243. }
  244. if( !BitTest( commandOptions, COMMAND_FIRED_BY_SCRIPT ) )
  245. {
  246. //All human players have manual control and must "drive" the beam around!
  247. m_startAttackFrame = TheGameLogic->getFrame();
  248. m_laserStatus = LASERSTATUS_NONE;
  249. m_manualTargetMode = true;
  250. m_initialTargetPosition.set( targetPos );
  251. m_overrideTargetDestination.set( targetPos );
  252. m_currentTargetPosition.set( targetPos );
  253. }
  254. else
  255. {
  256. //All computer controlled players have automatic control -- the "S" curve.
  257. UnsignedInt now = TheGameLogic->getFrame();
  258. m_initialTargetPosition.set( targetPos );
  259. m_startAttackFrame = max( now, (UnsignedInt)1 );
  260. m_laserStatus = LASERSTATUS_NONE;
  261. setLogicalStatus( STATUS_READY_TO_FIRE );
  262. m_specialPowerModule->setReadyFrame( now );
  263. }
  264. m_startDecayFrame = m_startAttackFrame + data->m_totalFiringFrames;
  265. SpecialPowerModuleInterface *spmInterface = getObject()->getSpecialPowerModule( specialPowerTemplate );
  266. if( spmInterface )
  267. {
  268. SpecialPowerModule *spModule = (SpecialPowerModule*)spmInterface;
  269. spModule->markSpecialPowerTriggered( &m_initialTargetPosition );
  270. }
  271. }
  272. //-------------------------------------------------------------------------------------------------
  273. Bool ParticleUplinkCannonUpdate::isPowerCurrentlyInUse( const CommandButton *command ) const
  274. {
  275. if( m_startAttackFrame != 0 && m_startAttackFrame <= TheGameLogic->getFrame() )
  276. {
  277. return true;
  278. }
  279. return false;
  280. }
  281. //-------------------------------------------------------------------------------------------------
  282. void ParticleUplinkCannonUpdate::setSpecialPowerOverridableDestination( const Coord3D *loc )
  283. {
  284. if( !getObject()->isDisabled() )
  285. {
  286. m_overrideTargetDestination = *loc;
  287. m_manualTargetMode = true;
  288. m_2ndLastDrivingClickFrame = m_lastDrivingClickFrame;
  289. m_lastDrivingClickFrame = TheGameLogic->getFrame();
  290. }
  291. }
  292. //-------------------------------------------------------------------------------------------------
  293. /** The update callback. */
  294. //-------------------------------------------------------------------------------------------------
  295. UpdateSleepTime ParticleUplinkCannonUpdate::update()
  296. {
  297. if( m_invalidSettings )
  298. {
  299. // can't return UPDATE_SLEEP_FOREVER unless we are sleepy...
  300. return UPDATE_SLEEP_NONE;
  301. ///return UPDATE_SLEEP_FOREVER;
  302. }
  303. const ParticleUplinkCannonUpdateModuleData *data = getParticleUplinkCannonUpdateModuleData();
  304. Object *me = getObject();
  305. // must test SOLD *before* the others.
  306. if( me->testStatus(OBJECT_STATUS_SOLD))
  307. {
  308. if (m_status != STATUS_IDLE)
  309. {
  310. setLogicalStatus( STATUS_IDLE );
  311. killEverything();
  312. }
  313. return UPDATE_SLEEP_NONE;
  314. }
  315. if( me->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION) )
  316. {
  317. return UPDATE_SLEEP_NONE;
  318. }
  319. if( me->isEffectivelyDead() )
  320. {
  321. return UPDATE_SLEEP_NONE;
  322. }
  323. //Check to see what our status is -- there are a couple:
  324. //1) Idle while waiting to get near the time when we should be preparing to be ready.
  325. //2) If superweapon delay is nuked (cheat) then we need to make the building ready now.
  326. UnsignedInt now = TheGameLogic->getFrame();
  327. UnsignedInt readyToFireFrame = m_specialPowerModule->isReady() ? now : m_specialPowerModule->getReadyFrame();
  328. UnsignedInt almostReadyFrame = readyToFireFrame - data->m_readyDelayFrames;
  329. UnsignedInt raiseAntennaFrame = almostReadyFrame - data->m_raiseAntennaFrames;
  330. UnsignedInt beginChargeFrame = raiseAntennaFrame - data->m_beginChargeFrames;
  331. if( m_startAttackFrame != 0 && m_startAttackFrame <= now )
  332. {
  333. if( m_startDecayFrame > now )
  334. {
  335. if( me->isDisabledByType( DISABLED_UNDERPOWERED ) ||
  336. me->isDisabledByType( DISABLED_EMP ) ||
  337. me->isDisabledByType( DISABLED_HACKED ) )
  338. {
  339. //We must end the special power early! ABORT! ABORT!
  340. m_startDecayFrame = now;
  341. }
  342. }
  343. UnsignedInt endDecayFrame = m_startDecayFrame + data->m_widthGrowFrames;
  344. UnsignedInt orbitalBirthFrame = m_startAttackFrame + data->m_beamTravelFrames;
  345. UnsignedInt orbitalDecayStart = m_startDecayFrame + data->m_beamTravelFrames;
  346. UnsignedInt orbitalDeathFrame = orbitalDecayStart + data->m_widthGrowFrames;
  347. switch( m_laserStatus )
  348. {
  349. case LASERSTATUS_NONE:
  350. //Means we are eligible to fire!
  351. if( orbitalBirthFrame <= now )
  352. {
  353. createOrbitToTargetLaser( data->m_widthGrowFrames );
  354. m_laserStatus = LASERSTATUS_BORN;
  355. m_scorchMarksMade = 0;
  356. m_nextScorchMarkFrame = now;
  357. m_damagePulsesMade = 0;
  358. m_nextDamagePulseFrame = now;
  359. }
  360. break;
  361. case LASERSTATUS_BORN:
  362. {
  363. Drawable *beam = TheGameClient->findDrawableByID( m_orbitToTargetBeamID );
  364. if( beam )
  365. {
  366. //m_annihilationSound.setPosition( beam->getPosition() );
  367. if( orbitalDecayStart <= now )
  368. {
  369. static NameKeyType nameKeyClientUpdate = NAMEKEY( "LaserUpdate" );
  370. LaserUpdate *update = (LaserUpdate*)beam->findClientUpdateModule( nameKeyClientUpdate );
  371. if( update )
  372. {
  373. update->setDecayFrames( data->m_widthGrowFrames );
  374. }
  375. m_laserStatus = LASERSTATUS_DECAYING;
  376. }
  377. }
  378. break;
  379. }
  380. case LASERSTATUS_DECAYING:
  381. {
  382. Drawable *beam = TheGameClient->findDrawableByID( m_orbitToTargetBeamID );
  383. if( beam )
  384. {
  385. //m_annihilationSound.setPosition( beam->getPosition() );
  386. TheAudio->removeAudioEvent( m_annihilationSound.getPlayingHandle() );
  387. if( orbitalDeathFrame <= now )
  388. {
  389. TheGameClient->destroyDrawable( beam );
  390. m_orbitToTargetBeamID = INVALID_DRAWABLE_ID;
  391. m_laserStatus = LASERSTATUS_DEAD;
  392. m_startAttackFrame = 0;
  393. setLogicalStatus( STATUS_IDLE );
  394. }
  395. }
  396. break;
  397. }
  398. case LASERSTATUS_DEAD:
  399. //Nothing... this is just a wait state.
  400. break;
  401. }
  402. Drawable *beam = TheGameClient->findDrawableByID( m_orbitToTargetBeamID );
  403. if( beam && orbitalBirthFrame <= now && now <= orbitalDeathFrame )
  404. {
  405. if( !m_manualTargetMode )
  406. {
  407. //Calculate the position of the beam because it swaths -- a nice S curve centering at the target location!
  408. //First determine the factor of time completed (ranges between 0.0 and 1.0)
  409. Real factor = (Real)(now - orbitalBirthFrame) / (Real)(orbitalDeathFrame - orbitalBirthFrame);
  410. //We're generating a swath that travels the points between sin( -1PI ) and sin( 1PI )
  411. Real radians = (factor * TWO_PI) - PI;
  412. Real cxDistance = (factor * data->m_swathOfDeathDistance ) - (data->m_swathOfDeathDistance * 0.5f); //cx is cartesian x
  413. //Now calculate the amplitude value.
  414. Real height = sin( radians );
  415. Real cxHeight = height * data->m_swathOfDeathAmplitude;
  416. Coord3D buildingToInitialTargetVector;
  417. buildingToInitialTargetVector.set( &m_initialTargetPosition );
  418. buildingToInitialTargetVector.sub( me->getPosition() );
  419. Real targetDistance = buildingToInitialTargetVector.length();
  420. //Calculate the point position assuming the target position is on the x axis relative to the building.
  421. m_currentTargetPosition.x = cxDistance + targetDistance;
  422. m_currentTargetPosition.y = cxHeight;
  423. m_currentTargetPosition.z = 0.0f;
  424. targetDistance = m_currentTargetPosition.length();
  425. //Now that we have our cartesian offset relative to the target coordinate, we need to rotate that offset
  426. //so it's aligned to along the building -> target vector.
  427. Vector2 buildingToTargetVector( m_initialTargetPosition.x - me->getPosition()->x, m_initialTargetPosition.y - me->getPosition()->y );
  428. buildingToTargetVector.Normalize();
  429. Vector2 cartesianTargetVector( m_currentTargetPosition.x, m_currentTargetPosition.y );
  430. cartesianTargetVector.Normalize();
  431. Real dotProduct = Vector2::Dot_Product( buildingToTargetVector, cartesianTargetVector );
  432. dotProduct = __min( 0.99999f, __max( -0.99999f, dotProduct ) ); //Account for numerical errors. Also, acos(-1.00000) is coming out QNAN on the superweapon general map. Heh.
  433. Real angle = (Real)ACos( dotProduct );
  434. if( buildingToTargetVector.Y >= 0 )
  435. {
  436. angle = -angle;
  437. }
  438. Matrix3D mtx( Vector3( 1.0f, 0.0f, 0.0f ) );
  439. mtx.Scale( targetDistance );
  440. mtx.Rotate_Z( -angle );
  441. Vector3 v = mtx.Get_X_Vector();
  442. m_currentTargetPosition.x = me->getPosition()->x + v.X;
  443. m_currentTargetPosition.y = me->getPosition()->y + v.Y;
  444. }
  445. else
  446. {
  447. Real speed = data->m_manualDrivingSpeed;
  448. if( m_lastDrivingClickFrame - m_2ndLastDrivingClickFrame < data->m_doubleClickToFastDriveDelay )
  449. {
  450. //Because we double clicked, use the faster driving speed.
  451. speed = data->m_manualFastDrivingSpeed;
  452. }
  453. //Convert speed to speed per frame.
  454. speed /= LOGICFRAMES_PER_SECOND;
  455. //Calculate the distance from our current position to our target dest.
  456. Coord3D vector = m_overrideTargetDestination;
  457. vector.sub( &m_currentTargetPosition );
  458. Real distance = vector.length();
  459. if( distance < speed )
  460. {
  461. //Don't allow the speed to overshoot the target point if close.
  462. speed = distance;
  463. }
  464. //Unitize the vector then apply the distance we will move.
  465. vector.normalize();
  466. vector.scale( speed );
  467. //Move the current target position in the desired direction and speed.
  468. m_currentTargetPosition.x += vector.x;
  469. m_currentTargetPosition.y += vector.y;
  470. }
  471. //Regardless of which method we used to set the target position, make sure the z position is at the terrain.
  472. m_currentTargetPosition.z = TheTerrainLogic->getGroundHeight( m_currentTargetPosition.x, m_currentTargetPosition.y );
  473. Coord3D orbitPosition;
  474. orbitPosition.set( &m_currentTargetPosition );
  475. orbitPosition.z += 500.0f;
  476. Real scorchRadius = 0.0f;
  477. Real damageRadius = 0.0f;
  478. //Reset the laser position
  479. static NameKeyType nameKeyClientUpdate = NAMEKEY( "LaserUpdate" );
  480. LaserUpdate *update = (LaserUpdate*)beam->findClientUpdateModule( nameKeyClientUpdate );
  481. if( update )
  482. {
  483. update->initLaser( NULL, &orbitPosition, &m_currentTargetPosition );
  484. scorchRadius = update->getCurrentLaserRadius() * data->m_scorchMarkScalar;
  485. damageRadius = update->getCurrentLaserRadius() * data->m_damageRadiusScalar;
  486. }
  487. //Create scorch marks periodically
  488. if( m_nextScorchMarkFrame <= now )
  489. {
  490. m_scorchMarksMade++;
  491. //Create the scorch mark now!
  492. Scorches scorchID = (Scorches)GameClientRandomValue( SCORCH_1, SCORCH_4 ); //Yes, this is just client fluff!
  493. TheGameClient->addScorch( &m_currentTargetPosition, scorchRadius, scorchID );
  494. //Calculate next scorch mark frame.
  495. Real nextFactor = (Real)m_scorchMarksMade / (Real)data->m_totalScorchMarks;
  496. m_nextScorchMarkFrame = orbitalBirthFrame + nextFactor * (orbitalDeathFrame - orbitalBirthFrame);
  497. //Generate iteration of fxlist for beam hitting ground.
  498. if( data->m_groundHitFX )
  499. {
  500. FXList::doFXPos( data->m_groundHitFX, &m_currentTargetPosition, NULL );
  501. }
  502. //Also reveal vision because the owning player has full rights to watch the carnage he created!
  503. ThePartitionManager->doShroudReveal( m_currentTargetPosition.x, m_currentTargetPosition.y, data->m_revealRange, me->getControllingPlayer()->getPlayerMask() );
  504. ThePartitionManager->undoShroudReveal( m_currentTargetPosition.x, m_currentTargetPosition.y, data->m_revealRange, me->getControllingPlayer()->getPlayerMask() );
  505. }
  506. //Handle damage pulses
  507. if( m_nextDamagePulseFrame <= now )
  508. {
  509. m_damagePulsesMade++;
  510. DamageInfo damageInfo;
  511. Real totalFiringSeconds = data->m_totalFiringFrames / LOGICFRAMES_PER_SECOND;
  512. Real damagePerPulse = (Real)(totalFiringSeconds * data->m_damagePerSecond) / (Real)data->m_totalDamagePulses;
  513. damageInfo.in.m_amount = damagePerPulse;
  514. damageInfo.in.m_sourceID = me->getID();
  515. damageInfo.in.m_damageType = data->m_damageType;
  516. damageInfo.in.m_deathType = data->m_deathType;
  517. PartitionFilterAlive filterAlive;
  518. PartitionFilter *filters[] = { &filterAlive, NULL };
  519. ObjectIterator *iter = ThePartitionManager->iterateObjectsInRange( &m_currentTargetPosition, damageRadius, FROM_CENTER_2D, filters );
  520. MemoryPoolObjectHolder hold( iter );
  521. for( Object *obj = iter->first(); obj; obj = iter->next() )
  522. {
  523. BodyModuleInterface *body = obj->getBodyModule();
  524. if( body )
  525. {
  526. body->attemptDamage( &damageInfo );
  527. }
  528. }
  529. if( data->m_damagePulseRemnantObjectName.isNotEmpty() )
  530. {
  531. //Create a remnant damaging object that will fade over time to represent the burning trail.
  532. const ThingTemplate *thing = TheThingFactory->findTemplate( data->m_damagePulseRemnantObjectName );
  533. if( thing )
  534. {
  535. //Fire and forget
  536. Object *remnant = TheThingFactory->newObject( thing, me->getTeam() );
  537. if( remnant )
  538. {
  539. remnant->setPosition( &m_currentTargetPosition );
  540. }
  541. }
  542. }
  543. //Calculate next damage pulse frame.
  544. Real nextFactor = (Real)m_damagePulsesMade / (Real)data->m_totalDamagePulses;
  545. m_nextDamagePulseFrame = orbitalBirthFrame + nextFactor * (orbitalDeathFrame - orbitalBirthFrame);
  546. }
  547. }
  548. if( endDecayFrame <= now )
  549. {
  550. setLogicalStatus( STATUS_PACKING );
  551. }
  552. else if( m_startDecayFrame <= now )
  553. {
  554. setLogicalStatus( STATUS_POSTFIRE );
  555. }
  556. else
  557. {
  558. setLogicalStatus( STATUS_FIRING );
  559. }
  560. }
  561. else if( readyToFireFrame <= now )
  562. {
  563. setLogicalStatus( STATUS_READY_TO_FIRE );
  564. }
  565. else if( almostReadyFrame <= now )
  566. {
  567. setLogicalStatus( STATUS_ALMOST_READY );
  568. }
  569. else if( raiseAntennaFrame <= now )
  570. {
  571. setLogicalStatus( STATUS_PREPARING );
  572. }
  573. else if( beginChargeFrame <= now )
  574. {
  575. setLogicalStatus( STATUS_CHARGING );
  576. }
  577. if( m_status == STATUS_FIRING )
  578. {
  579. if( m_nextLaunchFXFrame <= now )
  580. {
  581. //Generate iteration of fxlist for beam launching
  582. if( data->m_beamLaunchFX )
  583. {
  584. FXList::doFXPos( data->m_beamLaunchFX, &m_laserOriginPosition, NULL );
  585. }
  586. m_nextLaunchFXFrame = now + data->m_framesBetweenLaunchFXRefresh;
  587. }
  588. }
  589. //Handle shrouded status changes for the client player.
  590. Player *localPlayer = ThePlayerList->getLocalPlayer();
  591. if( localPlayer )
  592. {
  593. Bool shrouded = me->getShroudedStatus( localPlayer->getPlayerIndex() ) != OBJECTSHROUD_CLEAR;
  594. if( shrouded )
  595. {
  596. //We can't see it so any client effects that have been added logically needs to be removed!
  597. removeAllEffects();
  598. }
  599. else
  600. {
  601. //We can see it -- we only want to do anything if we just started seeing it, which means
  602. //we want to add client effects again.
  603. Bool revealThisFrame = m_clientShroudedLastFrame != shrouded;
  604. if( revealThisFrame )
  605. {
  606. //Only if we reveal this frame, will we add client effects. The logic can take it from
  607. //here on... unless of course we lose sight again.
  608. setClientStatus( m_status, revealThisFrame );
  609. }
  610. }
  611. m_clientShroudedLastFrame = shrouded;
  612. }
  613. return UPDATE_SLEEP_NONE;
  614. }
  615. //-------------------------------------------------------------------------------------------------
  616. void ParticleUplinkCannonUpdate::createOuterNodeParticleSystems( IntensityTypes intensity )
  617. {
  618. const ParticleUplinkCannonUpdateModuleData *data = getParticleUplinkCannonUpdateModuleData();
  619. AsciiString str;
  620. switch( intensity )
  621. {
  622. case IT_LIGHT:
  623. str = data->m_outerNodesLightFlareParticleSystemName;
  624. break;
  625. case IT_MEDIUM:
  626. str = data->m_outerNodesMediumFlareParticleSystemName;
  627. break;
  628. case IT_INTENSE:
  629. str = data->m_outerNodesIntenseFlareParticleSystemName;
  630. break;
  631. case IT_FINISH:
  632. break;
  633. }
  634. if( str.isNotEmpty() )
  635. {
  636. const ParticleSystemTemplate *tmp = TheParticleSystemManager->findTemplate( str );
  637. if( tmp )
  638. {
  639. ParticleSystem *system;
  640. for( int i = 0; i < data->m_outerEffectNumBones; i++ )
  641. {
  642. system = TheParticleSystemManager->createParticleSystem( tmp );
  643. if( system )
  644. {
  645. m_outerSystemIDs[ i ] = system->getSystemID();
  646. system->setPosition( &m_outerNodePositions[ i ] );
  647. system->setLocalTransform( &m_outerNodeOrientations[ i ] );
  648. }
  649. }
  650. }
  651. }
  652. }
  653. //-------------------------------------------------------------------------------------------------
  654. void ParticleUplinkCannonUpdate::createConnectorLasers( IntensityTypes intensity )
  655. {
  656. //Cache bone positions for the laser when it is ready to fire
  657. if( !m_upBonesCached )
  658. {
  659. calculateUpBonePositions();
  660. m_upBonesCached = true;
  661. }
  662. const ParticleUplinkCannonUpdateModuleData *data = getParticleUplinkCannonUpdateModuleData();
  663. AsciiString str;
  664. switch( intensity )
  665. {
  666. case IT_LIGHT:
  667. break;
  668. case IT_MEDIUM:
  669. str = data->m_connectorMediumLaserNameName;;
  670. break;
  671. case IT_INTENSE:
  672. str = data->m_connectorIntenseLaserNameName;
  673. break;
  674. case IT_FINISH:
  675. break;
  676. }
  677. if( str.isNotEmpty() )
  678. {
  679. const ThingTemplate *thingTemplate = TheThingFactory->findTemplate( str );
  680. if( thingTemplate )
  681. {
  682. for( int i = 0; i < data->m_outerEffectNumBones; i++ )
  683. {
  684. Drawable *beam = TheThingFactory->newDrawable( thingTemplate );
  685. if( beam )
  686. {
  687. m_laserBeamIDs[ i ] = beam->getID();
  688. static NameKeyType nameKeyClientUpdate = NAMEKEY( "LaserUpdate" );
  689. LaserUpdate *update = (LaserUpdate*)beam->findClientUpdateModule( nameKeyClientUpdate );
  690. if( update )
  691. {
  692. update->initLaser( NULL, &m_outerNodePositions[ i ], &m_connectorNodePosition );
  693. }
  694. }
  695. }
  696. }
  697. }
  698. }
  699. //-------------------------------------------------------------------------------------------------
  700. void ParticleUplinkCannonUpdate::createConnectorFlare( IntensityTypes intensity )
  701. {
  702. const ParticleUplinkCannonUpdateModuleData *data = getParticleUplinkCannonUpdateModuleData();
  703. AsciiString str;
  704. switch( intensity )
  705. {
  706. case IT_LIGHT:
  707. break;
  708. case IT_MEDIUM:
  709. str = data->m_connectorMediumFlareParticleSystemName;
  710. break;
  711. case IT_INTENSE:
  712. str = data->m_connectorIntenseFlareParticleSystemName;
  713. break;
  714. case IT_FINISH:
  715. break;
  716. }
  717. if( str.isNotEmpty() )
  718. {
  719. const ParticleSystemTemplate *tmp = TheParticleSystemManager->findTemplate( str );
  720. ParticleSystem *system;
  721. if( tmp )
  722. {
  723. system = TheParticleSystemManager->createParticleSystem( tmp );
  724. if( system )
  725. {
  726. m_connectorSystemID = system->getSystemID();
  727. system->setPosition( &m_connectorNodePosition );
  728. }
  729. }
  730. }
  731. }
  732. //-------------------------------------------------------------------------------------------------
  733. void ParticleUplinkCannonUpdate::createLaserBaseFlare( IntensityTypes intensity )
  734. {
  735. const ParticleUplinkCannonUpdateModuleData *data = getParticleUplinkCannonUpdateModuleData();
  736. AsciiString str;
  737. switch( intensity )
  738. {
  739. case IT_LIGHT:
  740. str = data->m_laserBaseLightFlareParticleSystemName;
  741. break;
  742. case IT_MEDIUM:
  743. str = data->m_laserBaseMediumFlareParticleSystemName;
  744. break;
  745. case IT_INTENSE:
  746. str = data->m_laserBaseIntenseFlareParticleSystemName;
  747. break;
  748. case IT_FINISH:
  749. break;
  750. }
  751. if( str.isNotEmpty() )
  752. {
  753. const ParticleSystemTemplate *tmp = TheParticleSystemManager->findTemplate( str );
  754. if( tmp )
  755. {
  756. ParticleSystem *system = TheParticleSystemManager->createParticleSystem( tmp );
  757. if( system )
  758. {
  759. m_laserBaseSystemID = system->getSystemID();
  760. system->setPosition( &m_laserOriginPosition );
  761. }
  762. }
  763. }
  764. }
  765. //-------------------------------------------------------------------------------------------------
  766. void ParticleUplinkCannonUpdate::createGroundToOrbitLaser( UnsignedInt growthFrames )
  767. {
  768. const ParticleUplinkCannonUpdateModuleData *data = getParticleUplinkCannonUpdateModuleData();
  769. Drawable *beam = TheGameClient->findDrawableByID( m_groundToOrbitBeamID );
  770. if( beam )
  771. {
  772. TheGameClient->destroyDrawable( beam );
  773. m_orbitToTargetBeamID = INVALID_DRAWABLE_ID;
  774. }
  775. if( data->m_particleBeamLaserName.isNotEmpty() )
  776. {
  777. const ThingTemplate *thingTemplate = TheThingFactory->findTemplate( data->m_particleBeamLaserName );
  778. if( thingTemplate )
  779. {
  780. Drawable *beam = TheThingFactory->newDrawable( thingTemplate );
  781. if( beam )
  782. {
  783. m_groundToOrbitBeamID = beam->getID();
  784. static NameKeyType nameKeyClientUpdate = NAMEKEY( "LaserUpdate" );
  785. LaserUpdate *update = (LaserUpdate*)beam->findClientUpdateModule( nameKeyClientUpdate );
  786. if( update )
  787. {
  788. Coord3D orbitPosition;
  789. orbitPosition.set( &m_laserOriginPosition );
  790. orbitPosition.z += 500.0f;
  791. update->initLaser( NULL, &m_laserOriginPosition, &orbitPosition, growthFrames );
  792. }
  793. }
  794. }
  795. }
  796. }
  797. //-------------------------------------------------------------------------------------------------
  798. void ParticleUplinkCannonUpdate::createOrbitToTargetLaser( UnsignedInt growthFrames )
  799. {
  800. const ParticleUplinkCannonUpdateModuleData *data = getParticleUplinkCannonUpdateModuleData();
  801. Drawable *beam = TheGameClient->findDrawableByID( m_orbitToTargetBeamID );
  802. if( beam )
  803. {
  804. TheAudio->removeAudioEvent( m_annihilationSound.getPlayingHandle() );
  805. TheGameClient->destroyDrawable( beam );
  806. m_orbitToTargetBeamID = INVALID_DRAWABLE_ID;
  807. }
  808. if( data->m_particleBeamLaserName.isNotEmpty() )
  809. {
  810. const ThingTemplate *thingTemplate = TheThingFactory->findTemplate( data->m_particleBeamLaserName );
  811. if( thingTemplate )
  812. {
  813. Drawable *beam = TheThingFactory->newDrawable( thingTemplate );
  814. if( beam )
  815. {
  816. m_orbitToTargetBeamID = beam->getID();
  817. static NameKeyType nameKeyClientUpdate = NAMEKEY( "LaserUpdate" );
  818. LaserUpdate *update = (LaserUpdate*)beam->findClientUpdateModule( nameKeyClientUpdate );
  819. if( update )
  820. {
  821. Coord3D orbitPosition;
  822. orbitPosition.set( &m_initialTargetPosition );
  823. orbitPosition.z += 500.0f;
  824. update->initLaser( NULL, &orbitPosition, &m_initialTargetPosition, growthFrames );
  825. }
  826. }
  827. }
  828. if( m_annihilationSound.getEventName().isNotEmpty() )
  829. {
  830. m_annihilationSound.setDrawableID( m_orbitToTargetBeamID );
  831. //m_annihilationSound.setPosition( &m_initialTargetPosition );
  832. m_annihilationSound.setPlayingHandle( TheAudio->addAudioEvent( &m_annihilationSound ) );
  833. }
  834. }
  835. }
  836. //-------------------------------------------------------------------------------------------------
  837. void ParticleUplinkCannonUpdate::createGroundHitParticleSystem( IntensityTypes intensity )
  838. {
  839. }
  840. //-------------------------------------------------------------------------------------------------
  841. void ParticleUplinkCannonUpdate::removeAllEffects()
  842. {
  843. const ParticleUplinkCannonUpdateModuleData *data = getParticleUplinkCannonUpdateModuleData();
  844. for( int i = 0; i < data->m_outerEffectNumBones; i++ )
  845. {
  846. if( m_outerSystemIDs && m_outerSystemIDs[ i ] )
  847. {
  848. TheParticleSystemManager->destroyParticleSystemByID( m_outerSystemIDs[ i ] );
  849. m_outerSystemIDs[ i ] = INVALID_PARTICLE_SYSTEM_ID;
  850. }
  851. Drawable *beam = TheGameClient->findDrawableByID( m_laserBeamIDs[ i ] );
  852. if( beam )
  853. {
  854. TheGameClient->destroyDrawable( beam );
  855. m_laserBeamIDs[ i ] = INVALID_DRAWABLE_ID;
  856. }
  857. }
  858. if( m_connectorSystemID )
  859. {
  860. TheParticleSystemManager->destroyParticleSystemByID( m_connectorSystemID );
  861. m_connectorSystemID = INVALID_PARTICLE_SYSTEM_ID;
  862. }
  863. if( m_laserBaseSystemID )
  864. {
  865. TheParticleSystemManager->destroyParticleSystemByID( m_laserBaseSystemID );
  866. m_laserBaseSystemID = INVALID_PARTICLE_SYSTEM_ID;
  867. }
  868. Drawable *beam = TheGameClient->findDrawableByID( m_groundToOrbitBeamID );
  869. if( beam )
  870. {
  871. TheGameClient->destroyDrawable( beam );
  872. m_groundToOrbitBeamID = INVALID_DRAWABLE_ID;
  873. }
  874. //Remove all sound hooks
  875. }
  876. //-------------------------------------------------------------------------------------------------
  877. Bool ParticleUplinkCannonUpdate::calculateDefaultInformation()
  878. {
  879. const ParticleUplinkCannonUpdateModuleData *data = getParticleUplinkCannonUpdateModuleData();
  880. Object *obj = getObject();
  881. //Get the local bone positions for each of the outer nodes.
  882. Coord3D bonePositions[ MAX_OUTER_NODES ];
  883. Matrix3D boneMatrices[ MAX_OUTER_NODES ];
  884. int numBones = obj->getMultiLogicalBonePosition( data->m_outerEffectBaseBoneName.str(), data->m_outerEffectNumBones, bonePositions, boneMatrices, FALSE );
  885. if( numBones != data->m_outerEffectNumBones )
  886. {
  887. DEBUG_CRASH( ("Particle cannon requires %d outer node bones, but can only find %d bones.", data->m_outerEffectNumBones, numBones ) );
  888. m_invalidSettings = true;
  889. return false;
  890. }
  891. for( int i = 0; i < data->m_outerEffectNumBones; i++ )
  892. {
  893. m_laserBeamIDs[ i ] = INVALID_DRAWABLE_ID;
  894. m_outerSystemIDs[ i ] = INVALID_PARTICLE_SYSTEM_ID;
  895. //Convert the local bone position into world space.
  896. Matrix3D nodeMatrix;
  897. getObject()->convertBonePosToWorldPos( &bonePositions[i], &boneMatrices[i], &m_outerNodePositions[ i ], &m_outerNodeOrientations[ i ] );
  898. }
  899. return true;
  900. }
  901. //-------------------------------------------------------------------------------------------------
  902. Bool ParticleUplinkCannonUpdate::calculateUpBonePositions()
  903. {
  904. const ParticleUplinkCannonUpdateModuleData *data = getParticleUplinkCannonUpdateModuleData();
  905. Object *obj = getObject();
  906. //Due to instant special weapons (or scripting)... it's possible to be in many states that are ready to fire (or firing)
  907. Drawable *draw = obj->getDrawable();
  908. Matrix3D mtx;
  909. Coord3D pos;
  910. if( draw )
  911. {
  912. if( data->m_connectorBoneName.isNotEmpty() && draw->getCurrentClientBonePositions( data->m_connectorBoneName.str(), 0, &pos, &mtx, 1 ) )
  913. {
  914. obj->convertBonePosToWorldPos( &pos, &mtx, &m_connectorNodePosition, &mtx );
  915. }
  916. if( data->m_connectorBoneName.isNotEmpty() && draw->getCurrentClientBonePositions( data->m_fireBoneName.str(), 0, &pos, &mtx, 1 ) )
  917. {
  918. obj->convertBonePosToWorldPos( &pos, &mtx, &m_laserOriginPosition, &mtx );
  919. }
  920. }
  921. return true;
  922. }
  923. //-------------------------------------------------------------------------------------------------
  924. void ParticleUplinkCannonUpdate::setLogicalStatus( PUCStatus newStatus )
  925. {
  926. Object *obj = getObject();
  927. if( m_status == newStatus )
  928. {
  929. return;
  930. }
  931. //Handle entering new status
  932. switch( newStatus )
  933. {
  934. case STATUS_IDLE:
  935. {
  936. //Set unpacked animation
  937. obj->clearModelConditionFlags( MAKE_MODELCONDITION_MASK3( MODELCONDITION_PACKING, MODELCONDITION_UNPACKING, MODELCONDITION_DEPLOYED ) );
  938. TheAudio->removeAudioEvent( m_powerupSound.getPlayingHandle() );
  939. TheAudio->removeAudioEvent( m_unpackToReadySound.getPlayingHandle() );
  940. TheAudio->removeAudioEvent( m_firingToIdleSound.getPlayingHandle() );
  941. TheAudio->removeAudioEvent( m_annihilationSound.getPlayingHandle() );
  942. break;
  943. }
  944. case STATUS_CHARGING:
  945. {
  946. m_laserStatus = LASERSTATUS_NONE;
  947. m_powerupSound.setObjectID( obj->getID() );
  948. m_powerupSound.setPlayingHandle( TheAudio->addAudioEvent( &m_powerupSound ) );
  949. TheAudio->removeAudioEvent( m_unpackToReadySound.getPlayingHandle() );
  950. TheAudio->removeAudioEvent( m_firingToIdleSound.getPlayingHandle() );
  951. TheAudio->removeAudioEvent( m_annihilationSound.getPlayingHandle() );
  952. break;
  953. }
  954. case STATUS_PREPARING:
  955. {
  956. //Set unpacking animation
  957. obj->clearAndSetModelConditionFlags( MAKE_MODELCONDITION_MASK2( MODELCONDITION_PACKING, MODELCONDITION_DEPLOYED ),
  958. MAKE_MODELCONDITION_MASK( MODELCONDITION_UNPACKING ) );
  959. if( m_unpackToReadySound.getEventName().isNotEmpty() )
  960. {
  961. m_unpackToReadySound.setObjectID( obj->getID() );
  962. m_unpackToReadySound.setPlayingHandle( TheAudio->addAudioEvent( &m_unpackToReadySound ) );
  963. }
  964. TheAudio->removeAudioEvent( m_firingToIdleSound.getPlayingHandle() );
  965. TheAudio->removeAudioEvent( m_annihilationSound.getPlayingHandle() );
  966. m_laserStatus = LASERSTATUS_NONE;
  967. break;
  968. }
  969. case STATUS_ALMOST_READY:
  970. {
  971. //Set deployed animation
  972. obj->clearAndSetModelConditionFlags( MAKE_MODELCONDITION_MASK2( MODELCONDITION_PACKING, MODELCONDITION_UNPACKING ),
  973. MAKE_MODELCONDITION_MASK( MODELCONDITION_DEPLOYED ) );
  974. m_laserStatus = LASERSTATUS_NONE;
  975. break;
  976. }
  977. case STATUS_READY_TO_FIRE:
  978. {
  979. obj->clearAndSetModelConditionFlags( MAKE_MODELCONDITION_MASK2( MODELCONDITION_PACKING, MODELCONDITION_UNPACKING ),
  980. MAKE_MODELCONDITION_MASK( MODELCONDITION_DEPLOYED ) );
  981. TheAudio->removeAudioEvent( m_powerupSound.getPlayingHandle() );
  982. TheAudio->removeAudioEvent( m_firingToIdleSound.getPlayingHandle() );
  983. TheAudio->removeAudioEvent( m_annihilationSound.getPlayingHandle() );
  984. m_laserStatus = LASERSTATUS_NONE;
  985. break;
  986. }
  987. case STATUS_PREFIRE:
  988. {
  989. break;
  990. }
  991. case STATUS_FIRING:
  992. {
  993. obj->clearAndSetModelConditionFlags( MAKE_MODELCONDITION_MASK2( MODELCONDITION_PACKING, MODELCONDITION_UNPACKING ),
  994. MAKE_MODELCONDITION_MASK( MODELCONDITION_DEPLOYED ) );
  995. if( m_firingToIdleSound.getEventName().isNotEmpty() )
  996. {
  997. m_firingToIdleSound.setObjectID( obj->getID() );
  998. m_firingToIdleSound.setPlayingHandle( TheAudio->addAudioEvent( &m_firingToIdleSound ) );
  999. }
  1000. TheAudio->removeAudioEvent( m_powerupSound.getPlayingHandle() );
  1001. TheAudio->removeAudioEvent( m_unpackToReadySound.getPlayingHandle() );
  1002. TheAudio->removeAudioEvent( m_annihilationSound.getPlayingHandle() );
  1003. m_nextLaunchFXFrame = 0;
  1004. break;
  1005. }
  1006. case STATUS_POSTFIRE:
  1007. {
  1008. break;
  1009. }
  1010. case STATUS_PACKING:
  1011. {
  1012. //Set packing animation
  1013. obj->clearAndSetModelConditionFlags( MAKE_MODELCONDITION_MASK2( MODELCONDITION_UNPACKING, MODELCONDITION_DEPLOYED ),
  1014. MAKE_MODELCONDITION_MASK( MODELCONDITION_PACKING ) );
  1015. break;
  1016. }
  1017. }
  1018. //Set new status.
  1019. m_status = newStatus;
  1020. setClientStatus( m_status, FALSE );
  1021. }
  1022. //-------------------------------------------------------------------------------------------------
  1023. void ParticleUplinkCannonUpdate::setClientStatus( PUCStatus newStatus, Bool revealThisFrame )
  1024. {
  1025. const ParticleUplinkCannonUpdateModuleData *data = getParticleUplinkCannonUpdateModuleData();
  1026. //Caches bone positions for positioning the client beams.
  1027. if( !m_defaultInfoCached )
  1028. {
  1029. if( !calculateDefaultInformation() )
  1030. {
  1031. m_invalidSettings = true;
  1032. return;
  1033. }
  1034. m_defaultInfoCached = true;
  1035. }
  1036. //This isn't the most efficient way to do this. Various effects can live in more than
  1037. //one status state, but keeping track of them properly is error-prone and hard to read.
  1038. //Therefore, the new philosophy is to delete EVERYTHING between status changes and
  1039. //create the ones we want!
  1040. removeAllEffects();
  1041. //Handle entering new status
  1042. switch( newStatus )
  1043. {
  1044. case STATUS_IDLE:
  1045. {
  1046. break;
  1047. }
  1048. case STATUS_CHARGING:
  1049. {
  1050. createOuterNodeParticleSystems( IT_LIGHT );
  1051. break;
  1052. }
  1053. case STATUS_PREPARING:
  1054. {
  1055. createOuterNodeParticleSystems( IT_MEDIUM );
  1056. break;
  1057. }
  1058. case STATUS_ALMOST_READY:
  1059. {
  1060. createOuterNodeParticleSystems( IT_MEDIUM );
  1061. createConnectorLasers( IT_MEDIUM );
  1062. createConnectorFlare( IT_MEDIUM );
  1063. break;
  1064. }
  1065. case STATUS_READY_TO_FIRE:
  1066. {
  1067. createOuterNodeParticleSystems( IT_MEDIUM );
  1068. createConnectorLasers( IT_MEDIUM );
  1069. createConnectorFlare( IT_MEDIUM );
  1070. createLaserBaseFlare( IT_LIGHT );
  1071. break;
  1072. }
  1073. case STATUS_PREFIRE:
  1074. {
  1075. break;
  1076. }
  1077. case STATUS_FIRING:
  1078. {
  1079. if( revealThisFrame )
  1080. {
  1081. createGroundToOrbitLaser( 0 );
  1082. }
  1083. else
  1084. {
  1085. createGroundToOrbitLaser( data->m_widthGrowFrames );
  1086. }
  1087. createOuterNodeParticleSystems( IT_INTENSE );
  1088. createConnectorLasers( IT_INTENSE );
  1089. createConnectorFlare( IT_INTENSE );
  1090. createLaserBaseFlare( IT_INTENSE );
  1091. break;
  1092. }
  1093. case STATUS_POSTFIRE:
  1094. {
  1095. createOuterNodeParticleSystems( IT_MEDIUM );
  1096. createConnectorLasers( IT_MEDIUM );
  1097. createConnectorFlare( IT_MEDIUM );
  1098. createLaserBaseFlare( IT_MEDIUM );
  1099. createGroundToOrbitLaser( 0 );
  1100. Drawable *beam = TheGameClient->findDrawableByID( m_groundToOrbitBeamID );
  1101. if( beam )
  1102. {
  1103. static NameKeyType nameKeyClientUpdate = NAMEKEY( "LaserUpdate" );
  1104. LaserUpdate *update = (LaserUpdate*)beam->findClientUpdateModule( nameKeyClientUpdate );
  1105. if( update )
  1106. {
  1107. if( revealThisFrame )
  1108. {
  1109. update->setDecayFrames( 0 );
  1110. }
  1111. else
  1112. {
  1113. update->setDecayFrames( data->m_widthGrowFrames );
  1114. }
  1115. }
  1116. }
  1117. break;
  1118. }
  1119. case STATUS_PACKING:
  1120. {
  1121. break;
  1122. }
  1123. }
  1124. }
  1125. //-------------------------------------------------------------------------------------------------
  1126. Bool ParticleUplinkCannonUpdate::doesSpecialPowerHaveOverridableDestinationActive() const
  1127. {
  1128. return m_status == STATUS_PREFIRE || m_status == STATUS_FIRING || m_status == STATUS_POSTFIRE;
  1129. }
  1130. // ------------------------------------------------------------------------------------------------
  1131. /** CRC */
  1132. // ------------------------------------------------------------------------------------------------
  1133. void ParticleUplinkCannonUpdate::crc( Xfer *xfer )
  1134. {
  1135. // extend base class
  1136. UpdateModule::crc( xfer );
  1137. } // end crc
  1138. // ------------------------------------------------------------------------------------------------
  1139. /** Xfer method
  1140. * Version Info:
  1141. * 1: Initial version */
  1142. // ------------------------------------------------------------------------------------------------
  1143. void ParticleUplinkCannonUpdate::xfer( Xfer *xfer )
  1144. {
  1145. const ParticleUplinkCannonUpdateModuleData *data = getParticleUplinkCannonUpdateModuleData();
  1146. // version
  1147. XferVersion currentVersion = 2;
  1148. XferVersion version = currentVersion;
  1149. xfer->xferVersion( &version, currentVersion );
  1150. // extend base class
  1151. UpdateModule::xfer( xfer );
  1152. // status
  1153. xfer->xferUser( &m_status, sizeof( PUCStatus ) );
  1154. // laser status
  1155. xfer->xferUser( &m_laserStatus, sizeof( LaserStatus ) );
  1156. // frames
  1157. xfer->xferUnsignedInt( &m_frames );
  1158. // we do not need to tie up the special power module pointer, it is done on object creation
  1159. // SpecialPowerModuleInterface *m_specialPowerModule;
  1160. // outer system ids
  1161. xfer->xferUser( m_outerSystemIDs, sizeof( ParticleSystemID ) * MAX_OUTER_NODES );
  1162. // laser beam ids
  1163. xfer->xferUser( m_laserBeamIDs, sizeof( DrawableID ) * MAX_OUTER_NODES );
  1164. // ground to orbit beam id
  1165. xfer->xferDrawableID( &m_groundToOrbitBeamID );
  1166. // orbit to target beam
  1167. xfer->xferDrawableID( &m_orbitToTargetBeamID );
  1168. // connector system ID
  1169. xfer->xferUser( &m_connectorSystemID, sizeof( ParticleSystemID ) );
  1170. // laser base system id
  1171. xfer->xferUser( &m_laserBaseSystemID, sizeof( ParticleSystemID ) );
  1172. // outer node positions
  1173. xfer->xferUser( m_outerNodePositions, sizeof( Coord3D ) * MAX_OUTER_NODES );
  1174. // outer node orientations
  1175. xfer->xferUser( m_outerNodeOrientations, sizeof( Matrix3D ) * MAX_OUTER_NODES );
  1176. // connector node position
  1177. xfer->xferCoord3D( &m_connectorNodePosition );
  1178. // laser origin position
  1179. xfer->xferCoord3D( &m_laserOriginPosition );
  1180. // The manual override target destination.
  1181. xfer->xferCoord3D( &m_overrideTargetDestination );
  1182. // up bones cached
  1183. xfer->xferBool( &m_upBonesCached );
  1184. // default info cached
  1185. xfer->xferBool( &m_defaultInfoCached );
  1186. // invalid settings
  1187. xfer->xferBool( &m_invalidSettings );
  1188. // initial target position
  1189. xfer->xferCoord3D( &m_initialTargetPosition );
  1190. // current target position
  1191. xfer->xferCoord3D( &m_currentTargetPosition );
  1192. // scorch marks make
  1193. xfer->xferUnsignedInt( &m_scorchMarksMade );
  1194. // next scorch mark frame
  1195. xfer->xferUnsignedInt( &m_nextScorchMarkFrame );
  1196. // next launch FX frame
  1197. xfer->xferUnsignedInt( &m_nextLaunchFXFrame );
  1198. // damage pluses made
  1199. xfer->xferUnsignedInt( &m_damagePulsesMade );
  1200. // next damage pulse frame
  1201. xfer->xferUnsignedInt( &m_nextDamagePulseFrame );
  1202. // start attack frame
  1203. xfer->xferUnsignedInt( &m_startAttackFrame );
  1204. // start attack frame
  1205. if( xfer->getXferMode() == XFER_SAVE || version >= 2 )
  1206. {
  1207. xfer->xferUnsignedInt( &m_startDecayFrame );
  1208. }
  1209. else
  1210. {
  1211. m_startDecayFrame = m_startAttackFrame + data->m_totalFiringFrames;
  1212. }
  1213. // the time of last manual target click
  1214. xfer->xferUnsignedInt( & m_lastDrivingClickFrame );
  1215. // the time of the 2nd last manual target click
  1216. xfer->xferUnsignedInt( &m_2ndLastDrivingClickFrame );
  1217. } // end xfer
  1218. // ------------------------------------------------------------------------------------------------
  1219. /** Load post process */
  1220. // ------------------------------------------------------------------------------------------------
  1221. void ParticleUplinkCannonUpdate::loadPostProcess( void )
  1222. {
  1223. // extend base class
  1224. UpdateModule::loadPostProcess();
  1225. } // end loadPostProcess