SpecialAbilityUpdate.cpp 60 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903
  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: SpecialAbilityUpdate.cpp /////////////////////////////////////////////////////////////////////////
  24. // Author: Kris Morness, July 2002
  25. // Desc: Handles processing of unit special abilities.
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  28. #include "Common/GameAudio.h"
  29. #include "Common/GlobalData.h"
  30. #include "Common/Player.h"
  31. #include "Common/PlayerList.h"
  32. #include "Common/SpecialPower.h"
  33. #include "Common/Team.h"
  34. #include "Common/ThingFactory.h"
  35. #include "Common/ThingTemplate.h"
  36. #include "Common/MiscAudio.h"
  37. #include "Common/Xfer.h"
  38. #include "GameClient/Drawable.h"
  39. #include "GameClient/FXList.h"
  40. #include "GameClient/Eva.h"
  41. #include "GameClient/InGameUI.h"
  42. #include "GameClient/ControlBar.h"
  43. #include "GameClient/GameText.h"
  44. #include "GameLogic/AIPathfind.h"
  45. #include "GameLogic/GameLogic.h"
  46. #include "GameLogic/Object.h"
  47. #include "GameLogic/PartitionManager.h"
  48. #include "GameLogic/Weapon.h"
  49. #include "GameLogic/ExperienceTracker.h"
  50. #include "GameLogic/Module/AIUpdate.h"
  51. #include "GameLogic/Module/LaserUpdate.h"
  52. #include "GameLogic/Module/PhysicsUpdate.h"
  53. #include "GameLogic/Module/SpecialAbilityUpdate.h"
  54. #include "GameLogic/Module/SpecialPowerModule.h"
  55. #include "GameLogic/Module/StickyBombUpdate.h"
  56. #include "GameLogic/Module/StealthUpdate.h"
  57. #include "GameLogic/Module/ContainModule.h"
  58. #ifdef _INTERNAL
  59. // for occasional debugging...
  60. //#pragma optimize("", off)
  61. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  62. #endif
  63. //-------------------------------------------------------------------------------------------------
  64. SpecialAbilityUpdate::SpecialAbilityUpdate( Thing *thing, const ModuleData* moduleData ) : UpdateModule( thing, moduleData )
  65. {
  66. //Added By Sadullah Nader
  67. //Initialization(s) inserted
  68. m_captureFlashPhase = 0.0f;
  69. //
  70. m_active = false;
  71. m_prepFrames = 0;
  72. m_animFrames = 0;
  73. m_targetID = INVALID_ID;
  74. m_targetPos.zero();
  75. m_locationCount = 0;
  76. m_specialObjectEntries = 0;
  77. m_noTargetCommand = false;
  78. m_packingState = STATE_NONE;
  79. m_facingInitiated = false;
  80. m_facingComplete = false;
  81. m_withinStartAbilityRange = false;
  82. m_doDisableFXParticles = TRUE;// true always, unless small unit causes it to toggle on-off
  83. setWakeFrame(getObject(), UPDATE_SLEEP_FOREVER);
  84. // This is the althernate way to one-at-a-time BlackLotus' specials; we'll keep it commented her until Dustin decides, or until 12/10/02
  85. // setBusy( FALSE );
  86. }
  87. //-------------------------------------------------------------------------------------------------
  88. SpecialAbilityUpdate::~SpecialAbilityUpdate( void )
  89. {
  90. onExit( true );
  91. }
  92. /*------------------------------------------------------------------------------------------------
  93. void SpecialAbilityUpdate::update( void )
  94. This is the brains of the entire special ability update. There are several optional steps and
  95. variations that can be processed for any particular type of special ability. A special ability
  96. that has every option will do the following in order:
  97. 1 -- APPROACH: If I'm not close enough to the target, then approach it
  98. 2 -- UNPACK: If I need to unpack before I can prepare, then do so now (this uses the model
  99. condition unpack).
  100. 3 -- PREPARE: If I need to perform a task for a period of time before I can trigger my special
  101. ability, then do so now. A good example is aiming with a targetting laser for a few
  102. seconds before firing your special weapon.
  103. 4 -- TRIGGER: Once preparation is complete, fire your special ability now.
  104. 5 -- PACK: If I need to pack after finishing my attack, do so now.
  105. 6 -- FINISH: Clean up the states, and turn off the update.
  106. Variations:
  107. Persistent Specials -- A persistent special will continually trigger it's effect every so often
  108. and never end. A good example of this is the disable building hack. The hacker will run up
  109. to the target building, unpack, prepare (firing hack stream), then after a period of time,
  110. the building becomes disabled. But because it's persistent, we reset the preparation and
  111. trigger the building disabled code over and over again -- which is on a timer.
  112. No Target Specials -- You can link two different main specials together. Colonel Burton has the
  113. ability to lay C4 charges on multiple targets. Activating these specials require a target.
  114. The non-target version is actually the detonator, which goes through all the special objects
  115. and detonates each one of them.
  116. Options:
  117. SpecialPowerTemplate -- Defines the special power template that links to a command.
  118. StartAbilityRange -- Specify how far you want your unit to be from the target before
  119. start your special ability.
  120. AbilityAbortRange -- After starting an attack, it'll allow preparation unless the target goes
  121. beyond this range. If this happens, the ability is aborted outright.
  122. PreparationTime -- How long it takes to prepare your special once in position and unpacked.
  123. PersistentPrepTime -- This value defines whether or not you are using a persistent special.
  124. Once the special ability is triggered, it'll wait until this specified
  125. delay occurs and it'll trigger it again, for ever until the unit dies,
  126. the target dies, or the unit decides to do something else.
  127. PackTime -- How long it takes to pack up the unit after triggering a non persistent
  128. special ability or after ordering the unit to do something else, or the
  129. target dies (if applicable).
  130. PackUnpackVariationFactor -- Randomizes the pack and unpack time by specified range. The closer
  131. to zero, the smaller the variation. 0.2 would have a pack or unpack range
  132. of xTime +/- 20%. This is important because it represents averages.
  133. UnpackTime -- Same as PackTime, except used once entering range of target.
  134. SkipPackingWithNoTarget -- This option is used by the No Target Special variation, and only uses
  135. packing/unpacking when you have a specific target. In the case of Colonel
  136. Burton, this value is set to true. When he plants a C4 charge, he has a
  137. target, therefore he requires unpacking (laying the charge). When he runs
  138. away and decides to detonate the charge, he calls the same special
  139. ability, but without a target -- therefore the packing is skipped and
  140. detonates it right away.
  141. SpecialObject -- Defines the special object the unit will create via the special ability.
  142. In one case, it creates and maintains the laser or binary stream during
  143. preparation. In Colonel Burton's case, it keeps track of the C4 charges
  144. that he has placed.
  145. MaxSpecialObjects -- Defines the max number of special objects that can exist at any given
  146. time. The laser example only has one, but the C4 charges can have more.
  147. SpecialObjectsPersistent -- If this flag is set, then the objects will remain should the owner
  148. decides to do something else... C4 charges are a good example.
  149. EffectDuration -- Defines the duration of the special ability. In the case of disabling
  150. the building (hacker), this value will dictate how long the building
  151. will be disabled should the hacker die or stop the attack.
  152. UniqueSpecialObjectTargets -- Prevents the owner from placing multiple special objects on the
  153. same target. C4 charges once again.
  154. SpecialObjectsPersistWhenOwnerDies -- If the owner dies, the special objects will remain in
  155. the world. Timed C4 charges is a good example, but remote C4 charges
  156. is a bad example -- because it requires the owner to detonate them.
  157. FlipObjectAfterPacking -- Simply rotates the object 180 degrees after packing (due to special
  158. animation case).
  159. FlipObjectAfterUnPacking -- Simply rotates the object 180 degrees after unpacking (due to
  160. special animation case). Used by colonel burton after planting charge.
  161. Lets use the Age of Kings Trebuchet as a simple example:
  162. Variation -- PersistentPrepTime (set to true in ini file)
  163. 1 -- APPROACH: Get within range before unpacking
  164. 2 -- UNPACK: Unpack the treb at this location.
  165. 3 -- PREPARE: Aim at the target
  166. 4 -- TRIGGER: Fire the treb.
  167. 5 -- RESET PREPARATION: Go to step 3 until target dead.
  168. 6 -- PACK: Assuming we are done... pack up
  169. 7 -- FINISH: Stop the special ability
  170. -------------------------------------------------------------------------------------------------*/
  171. UpdateSleepTime SpecialAbilityUpdate::update( void )
  172. {
  173. /// @todo srj -- this could probably sleep more between stages. maybe someday.
  174. // Note: This will be complicated because one difficult example is handling Burton when he dies while
  175. // bombs are in the world.
  176. //Validation of special objects makes sure that they still exist. Sometimes special
  177. //objects can be destroyed or removed, or detonate, etc! When they go missing,
  178. //they are removed from the special object list and free up slots for those units
  179. //that can have multiple.
  180. //We need to do this now because it's possible the special objects need to be checked while
  181. //the the special ability is over (thus inactive).
  182. validateSpecialObjects();
  183. //Important! This check will see if there has been any commands issued by either the player
  184. //or script. When told to do something else, we need to immediately cleanup our special ability.
  185. //This also means some things might be left around like timed charges to detonate.
  186. if( getObject()->isEffectivelyDead() )
  187. {
  188. onExit( TRUE );
  189. return calcSleepTime();
  190. }
  191. if( !m_active ) // Not active.
  192. return calcSleepTime();
  193. AIUpdateInterface *ai = getObject()->getAIUpdateInterface();
  194. if( !ai )
  195. {
  196. onExit( false );
  197. return calcSleepTime();
  198. }
  199. if( ai->getLastCommandSource() != CMD_FROM_AI )
  200. {
  201. onExit( false );
  202. return calcSleepTime();
  203. }
  204. //STEP 2 & 5(6) -- Handles packing and unpacking in progress. If packing
  205. //then ends the special ability once complete. Things that don't pack
  206. //will never be handled, nor do things that aren't in a packing state.
  207. if( handlePackingProcessing() )
  208. {
  209. return calcSleepTime();
  210. }
  211. Bool shouldAbort = false;
  212. // A dead target will end our special (if we are using a target).
  213. if (m_targetID != INVALID_ID)
  214. {
  215. Object* target = TheGameLogic->findObjectByID(m_targetID);
  216. if (target != NULL)
  217. {
  218. const SpecialAbilityUpdateModuleData* data = getSpecialAbilityUpdateModuleData();
  219. if (target->isEffectivelyDead())
  220. shouldAbort = TRUE;
  221. else switch (data->m_specialPowerTemplate->getSpecialPowerType())
  222. {
  223. case SPECIAL_INFANTRY_CAPTURE_BUILDING:
  224. case SPECIAL_BLACKLOTUS_CAPTURE_BUILDING:
  225. case SPECIAL_HACKER_DISABLE_BUILDING:
  226. if (target->getTeam() == getObject()->getTeam())
  227. {
  228. // it's been captured by a colleague! we should stop.
  229. shouldAbort = TRUE;
  230. }
  231. break;
  232. }
  233. }
  234. }
  235. if (shouldAbort)
  236. {
  237. // doh, a colleague has already captured it. just stop.
  238. ai->aiIdle( CMD_FROM_AI );
  239. onExit( false );
  240. return calcSleepTime();
  241. }
  242. //DETERMINE OUR PHASE! BRAIN LOGIC
  243. if( !isPreparationComplete() )
  244. {
  245. //The special ability has fired, now continue to process the special ability
  246. //until it expires
  247. m_prepFrames--;
  248. if( isPreparationComplete() )
  249. {
  250. //STEP 4 -- TRIGGER (with preparation)
  251. triggerAbilityEffect();
  252. if( isPersistentAbility() )
  253. {
  254. //VARIATION -- PERSISTENCE (repeats preparation)
  255. resetPreparation();
  256. }
  257. else
  258. {
  259. endPreparation();
  260. if( needToPack() )
  261. {
  262. //STEP 5 -- PACK
  263. //Note: If we actually do pack, then cleanup will be handled in
  264. //handlePackingProcess(), near the top of this function.
  265. startPacking(true);
  266. }
  267. else
  268. {
  269. //STEP 6 -- FINISH
  270. finishAbility();
  271. }
  272. }
  273. }
  274. else
  275. {
  276. //Process the preparation if it's still not complete.
  277. Bool continuePrep = continuePreparation();
  278. if( !continuePrep )
  279. {
  280. //We failed so abort!
  281. endPreparation();
  282. if( needToPack() )
  283. {
  284. //STEP 5 -- PACK
  285. //Note: If we actually do pack, then cleanup will be handled in
  286. //handlePackingProcess(), near the top of this function.
  287. startPacking(false);
  288. }
  289. else
  290. {
  291. //STEP 6 -- FINISH
  292. finishAbility();
  293. }
  294. }
  295. }
  296. }
  297. else if( isWithinStartAbilityRange() )
  298. {
  299. m_withinStartAbilityRange = true;
  300. if( !isFacing() && needToFace() )
  301. {
  302. startFacing();
  303. return calcSleepTime();
  304. }
  305. if( needToUnpack() )
  306. {
  307. //STEP 2 -- UNPACK
  308. startUnpacking();
  309. return calcSleepTime();
  310. }
  311. if( m_packingState == STATE_UNPACKED )
  312. {
  313. //STEP 3 -- PREPARE
  314. startPreparation();
  315. if( isPreparationComplete() )
  316. {
  317. //STEP 4 -- TRIGGER (skipping preparation)
  318. triggerAbilityEffect();
  319. endPreparation();
  320. if( needToPack() )
  321. {
  322. //STEP 5 -- PACK
  323. //Note: If we actually do pack, then cleanup will be handled in
  324. //handlePackingProcess(), near the top of this function.
  325. startPacking(true);
  326. }
  327. else
  328. {
  329. //STEP 6 -- FINISH
  330. finishAbility();
  331. }
  332. }
  333. }
  334. }
  335. else if( ai->isIdle() )
  336. {
  337. //STEP 1 -- APPROACH
  338. approachTarget();
  339. }
  340. return calcSleepTime();
  341. }
  342. //-------------------------------------------------------------------------------------------------
  343. void SpecialAbilityUpdate::initiateIntentToDoSpecialPower( const SpecialPowerTemplate *specialPowerTemplate,
  344. const Object *targetObj,
  345. const Coord3D *targetPos,
  346. UnsignedInt commandOptions,
  347. Int locationCount )
  348. {
  349. const SpecialAbilityUpdateModuleData* data = getSpecialAbilityUpdateModuleData();
  350. const SpecialPowerTemplate *spTemplate = data->m_specialPowerTemplate;
  351. if( spTemplate != specialPowerTemplate )
  352. {
  353. //Check to make sure our modules are connected.
  354. return;
  355. }
  356. //Clear target values
  357. m_targetID = INVALID_ID;
  358. m_targetPos.zero();
  359. m_locationCount = 0;
  360. m_prepFrames = 0;
  361. m_animFrames = 0;
  362. m_packingState = STATE_PACKED;
  363. m_facingInitiated = false;
  364. m_facingComplete = false;
  365. m_withinStartAbilityRange = false;
  366. getObject()->clearModelConditionFlags(
  367. MAKE_MODELCONDITION_MASK4( MODELCONDITION_UNPACKING, MODELCONDITION_PACKING, MODELCONDITION_FIRING_A, MODELCONDITION_RAISING_FLAG ) );
  368. if( targetObj )
  369. {
  370. //Get the target!
  371. m_targetID = targetObj ? targetObj->getID() : INVALID_ID;
  372. }
  373. else if( targetPos )
  374. {
  375. //Get the position!
  376. m_targetPos = *targetPos;
  377. m_locationCount = locationCount;
  378. }
  379. //Clear any old AI before starting this special ability.
  380. if( !getObject()->getAIUpdateInterface() )
  381. {
  382. return;
  383. }
  384. getObject()->getAIUpdateInterface()->aiIdle( CMD_FROM_AI );
  385. //Determine whether we are triggering a command (rather than executing special at location or target)
  386. m_noTargetCommand = !targetObj && !targetPos;
  387. if( data->m_unpackTime == 0 || m_noTargetCommand && data->m_skipPackingWithNoTarget )
  388. {
  389. //Only unpack if we need to -- setting it to unpacked will skip step 2 in the update
  390. m_packingState = STATE_UNPACKED;
  391. }
  392. m_active = true;
  393. //Prevent other mutually exclusive specials from running (kill them now if we're starting something else)
  394. SpecialAbilityUpdate *disableSA;
  395. disableSA = getObject()->findSpecialAbilityUpdate( SPECIAL_BLACKLOTUS_DISABLE_VEHICLE_HACK );
  396. if( disableSA && disableSA != this )
  397. disableSA->onExit( FALSE );
  398. disableSA = getObject()->findSpecialAbilityUpdate( SPECIAL_BLACKLOTUS_STEAL_CASH_HACK );
  399. if( disableSA && disableSA != this )
  400. disableSA->onExit( FALSE );
  401. disableSA = getObject()->findSpecialAbilityUpdate( SPECIAL_BLACKLOTUS_CAPTURE_BUILDING );
  402. if( disableSA && disableSA != this )
  403. disableSA->onExit( FALSE );
  404. disableSA = getObject()->findSpecialAbilityUpdate( SPECIAL_REMOTE_CHARGES );
  405. if( disableSA && disableSA != this )
  406. disableSA->onExit( FALSE );
  407. disableSA = getObject()->findSpecialAbilityUpdate( SPECIAL_TIMED_CHARGES );
  408. if( disableSA && disableSA != this )
  409. disableSA->onExit( FALSE );
  410. setWakeFrame(getObject(), UPDATE_SLEEP_NONE);
  411. }
  412. //-------------------------------------------------------------------------------------------------
  413. Bool SpecialAbilityUpdate::isPowerCurrentlyInUse( const CommandButton *command ) const
  414. {
  415. if( command )
  416. {
  417. if( command->getSpecialPowerTemplate() && command->getSpecialPowerTemplate()->getSpecialPowerType() == SPECIAL_REMOTE_CHARGES )
  418. {
  419. if( !BitTest( command->getOptions(), CONTEXTMODE_COMMAND ) )
  420. {
  421. //This is the detonate charge button. Treat it backwards saying it's in use when we don't have any special objects (charges).
  422. //That way, the button will be grayed out.
  423. return getSpecialObjectCount() == 0;
  424. }
  425. }
  426. }
  427. if( m_packingState != STATE_NONE )
  428. {
  429. //exception for powers with zero reload time... they are ready to use immediately!
  430. if ( (m_packingState == STATE_PACKING || m_packingState == STATE_PACKED) &&
  431. command && command->getSpecialPowerTemplate()->getReloadTime() == 0 )
  432. return false;
  433. if ( m_withinStartAbilityRange )
  434. {
  435. return true;
  436. }
  437. }
  438. return false;
  439. }
  440. //-------------------------------------------------------------------------------------------------
  441. void SpecialAbilityUpdate::onExit( Bool cleanup )
  442. {
  443. const SpecialAbilityUpdateModuleData* data = getSpecialAbilityUpdateModuleData();
  444. getObject()->clearModelConditionFlags(
  445. MAKE_MODELCONDITION_MASK4( MODELCONDITION_UNPACKING, MODELCONDITION_PACKING, MODELCONDITION_FIRING_A, MODELCONDITION_RAISING_FLAG ) );
  446. getObject()->clearStatus( OBJECT_STATUS_IS_USING_ABILITY );
  447. TheAudio->removeAudioEvent( m_prepSoundLoop.getPlayingHandle() );
  448. endPreparation();
  449. if( !data->m_specialObjectsPersistent || cleanup && !data->m_specialObjectsPersistWhenOwnerDies )
  450. {
  451. //Delete special objects that aren't considered persistent whenever we turn off
  452. //leave the special ability update.
  453. killSpecialObjects();
  454. }
  455. m_active = false;
  456. m_withinStartAbilityRange = false;
  457. m_packingState = STATE_NONE;
  458. // This is the althernate way to one-at-a-time BlackLotus' specials; we'll keep it commented her until Dustin decides, or until 12/10/02
  459. // setBusy( FALSE );// My owner is no longer using me
  460. // no, actually, we DON'T want to call this here, since onExit is always called
  461. // (directly or indirectly) from update()... and calling setWakeFrame() from your
  462. // own update() method is a no-no (since it would just be ignored in favor
  463. // of the return value from update() anyway). just set m_active to false,
  464. // and we'll put ourselves to sleep.
  465. // setWakeFrame(getObject(), UPDATE_SLEEP_FOREVER);
  466. }
  467. //-------------------------------------------------------------------------------------------------
  468. //Some special abilities requires packing or unpacking. When setup, all this function does is
  469. //decrement the counter and clear the model state when finished.
  470. //Returns TRUE if we are still packing or unpacking.
  471. //-------------------------------------------------------------------------------------------------
  472. Bool SpecialAbilityUpdate::handlePackingProcessing()
  473. {
  474. const SpecialAbilityUpdateModuleData* data = getSpecialAbilityUpdateModuleData();
  475. if( m_animFrames > 0 )
  476. {
  477. m_animFrames--;
  478. if( m_animFrames == 0 )
  479. {
  480. // We're done, so clear the states.
  481. getObject()->clearModelConditionFlags( MAKE_MODELCONDITION_MASK2( MODELCONDITION_UNPACKING, MODELCONDITION_PACKING ) );
  482. if( m_packingState == STATE_UNPACKING )
  483. {
  484. if( data->m_flipObjectAfterUnpacking )
  485. {
  486. getObject()->setOrientation( getObject()->getOrientation() + PI );
  487. }
  488. m_packingState = STATE_UNPACKED;
  489. }
  490. else if( m_packingState == STATE_PACKING )
  491. {
  492. if( data->m_flipObjectAfterPacking )
  493. {
  494. getObject()->setOrientation( getObject()->getOrientation() + PI );
  495. }
  496. //We just finished packing up, therefore
  497. //we have completed our special ability.
  498. m_packingState = STATE_PACKED;
  499. //Do exit preparation.
  500. finishAbility();
  501. //Complete the special ability now
  502. return true;
  503. }
  504. //We're finished processing
  505. return false;
  506. }
  507. //This is new... the ability to disable stealth before triggering
  508. if( getSpecialAbilityUpdateModuleData()->m_loseStealthOnTrigger &&
  509. m_animFrames < getSpecialAbilityUpdateModuleData()->m_preTriggerUnstealthFrames)
  510. {
  511. static NameKeyType key_StealthUpdate = NAMEKEY( "StealthUpdate" );
  512. StealthUpdate* stealth = (StealthUpdate*)getObject()->findUpdateModule( key_StealthUpdate );
  513. if( stealth )
  514. {
  515. stealth->markAsDetected();
  516. }
  517. }
  518. //We're still processing
  519. return true;
  520. }
  521. //We're not processing
  522. return false;
  523. }
  524. //-------------------------------------------------------------------------------------------------
  525. Bool SpecialAbilityUpdate::needToPack() const
  526. {
  527. const SpecialAbilityUpdateModuleData* data = getSpecialAbilityUpdateModuleData();
  528. if( m_packingState == STATE_UNPACKED )
  529. {
  530. if( data->m_skipPackingWithNoTarget && m_noTargetCommand )
  531. {
  532. return false;
  533. }
  534. if( data->m_packTime )
  535. {
  536. return true;
  537. }
  538. }
  539. return false;
  540. }
  541. //-------------------------------------------------------------------------------------------------
  542. Bool SpecialAbilityUpdate::needToUnpack() const
  543. {
  544. const SpecialAbilityUpdateModuleData* data = getSpecialAbilityUpdateModuleData();
  545. if( m_packingState == STATE_PACKED )
  546. {
  547. if( data->m_skipPackingWithNoTarget && m_noTargetCommand )
  548. {
  549. return false;
  550. }
  551. if( data->m_unpackTime )
  552. {
  553. return true;
  554. }
  555. }
  556. return false;
  557. }
  558. //-------------------------------------------------------------------------------------------------
  559. void SpecialAbilityUpdate::startPacking(Bool success)
  560. {
  561. const SpecialAbilityUpdateModuleData* data = getSpecialAbilityUpdateModuleData();
  562. m_packingState = STATE_PACKING;
  563. Real variation = GameLogicRandomValueReal( 1.0f - data->m_packUnpackVariationFactor, 1.0f + data->m_packUnpackVariationFactor );
  564. m_animFrames = data->m_packTime * variation;
  565. //Set the animation state
  566. getObject()->clearAndSetModelConditionFlags(
  567. MAKE_MODELCONDITION_MASK2( MODELCONDITION_UNPACKING, MODELCONDITION_RAISING_FLAG ),
  568. MAKE_MODELCONDITION_MASK( MODELCONDITION_PACKING ) );
  569. AudioEventRTS sound = data->m_packSound;
  570. sound.setObjectID( getObject()->getID() );
  571. TheAudio->addAudioEvent( &sound );
  572. //Sync the animation length to the time it'll take to pack.
  573. Drawable* draw = getObject()->getDrawable();
  574. if (draw)
  575. draw->setAnimationCompletionTime( m_animFrames );
  576. AIUpdateInterface *ai = getObject()->getAIUpdateInterface();
  577. if (ai)
  578. ai->aiBusy(CMD_FROM_AI);
  579. if (success)
  580. {
  581. AudioEventRTS event;
  582. switch( data->m_specialPowerTemplate->getSpecialPowerType() )
  583. {
  584. //case SPECIAL_HACKER_DISABLE_BUILDING:// Awaiting Mical's new sound
  585. // event = *getObject()->getTemplate()->getPerUnitSound( "VoiceDisableBuildingComplete" );
  586. // break;
  587. case SPECIAL_BLACKLOTUS_CAPTURE_BUILDING:
  588. event = *getObject()->getTemplate()->getPerUnitSound( "VoiceCaptureBuildingComplete" );
  589. break;
  590. case SPECIAL_BLACKLOTUS_DISABLE_VEHICLE_HACK:
  591. event = *getObject()->getTemplate()->getPerUnitSound( "VoiceDisableVehicleComplete" );
  592. break;
  593. case SPECIAL_BLACKLOTUS_STEAL_CASH_HACK:
  594. event = *getObject()->getTemplate()->getPerUnitSound( "VoiceStealCashComplete" );
  595. break;
  596. default:
  597. event = *getObject()->getTemplate()->getVoiceTaskComplete();
  598. break;
  599. }
  600. event.setObjectID(getObject()->getID());
  601. TheAudio->addAudioEvent(&event);
  602. }
  603. }
  604. //-------------------------------------------------------------------------------------------------
  605. void SpecialAbilityUpdate::startUnpacking()
  606. {
  607. const SpecialAbilityUpdateModuleData* data = getSpecialAbilityUpdateModuleData();
  608. m_packingState = STATE_UNPACKING;
  609. Real variation = GameLogicRandomValueReal( 1.0f - data->m_packUnpackVariationFactor, 1.0f + data->m_packUnpackVariationFactor );
  610. m_animFrames = data->m_unpackTime * variation;
  611. //Set the animation state
  612. getObject()->clearAndSetModelConditionFlags(
  613. MAKE_MODELCONDITION_MASK( MODELCONDITION_PACKING ),
  614. MAKE_MODELCONDITION_MASK( MODELCONDITION_UNPACKING ) );
  615. AudioEventRTS sound = data->m_unpackSound;
  616. sound.setObjectID( getObject()->getID() );
  617. TheAudio->addAudioEvent( &sound );
  618. //Sync the animation length to the time it'll take to unpack.
  619. Drawable* draw = getObject()->getDrawable();
  620. if (draw)
  621. draw->setAnimationCompletionTime( m_animFrames );
  622. AIUpdateInterface *ai = getObject()->getAIUpdateInterface();
  623. if (ai)
  624. ai->aiBusy(CMD_FROM_AI);
  625. }
  626. //-------------------------------------------------------------------------------------------------
  627. Bool SpecialAbilityUpdate::isWithinStartAbilityRange() const
  628. {
  629. const SpecialAbilityUpdateModuleData* data = getSpecialAbilityUpdateModuleData();
  630. const Object *self = getObject();
  631. //Quickly convert very short range approachs to "contact" class requiring collision before
  632. //stopping.
  633. Real range = data->m_startAbilityRange;
  634. const Real UNDERSIZE = PATHFIND_CELL_SIZE_F * 0.25f;
  635. range = __max( 0.0f, range - UNDERSIZE );
  636. if( m_withinStartAbilityRange )
  637. {
  638. //Only get within range once.
  639. return true;
  640. }
  641. Real fDistSquared = 0.0f;
  642. Object *target = NULL;
  643. if( m_targetID != INVALID_ID )
  644. {
  645. target = TheGameLogic->findObjectByID( m_targetID );
  646. if( target )
  647. {
  648. fDistSquared = ThePartitionManager->getDistanceSquared( self, target, FROM_BOUNDINGSPHERE_2D );
  649. }
  650. }
  651. else if( m_targetPos.x || m_targetPos.y || m_targetPos.z ) //It's zero if not used...
  652. {
  653. fDistSquared = ThePartitionManager->getDistanceSquared( self, &m_targetPos, FROM_BOUNDINGSPHERE_2D );
  654. }
  655. else
  656. {
  657. //No position, so this step is useless
  658. return true;
  659. }
  660. //Check to see how far we are from the target!
  661. Real fStartRangeSquared = data->m_startAbilityRange * data->m_startAbilityRange;
  662. if( fDistSquared <= fStartRangeSquared )
  663. {
  664. if( range == 0.0f && m_targetID != INVALID_ID )
  665. {
  666. //We want to ensure we collided with our target first!
  667. ObjectIterator *iter = ThePartitionManager->iteratePotentialCollisions( self->getPosition(), self->getGeometryInfo(), 0.0f );
  668. MemoryPoolObjectHolder hold(iter);
  669. for( Object *them = iter->first(); them; them = iter->next() )
  670. {
  671. if( target == them )
  672. {
  673. return true;
  674. }
  675. }
  676. return false;
  677. }
  678. if( data->m_approachRequiresLOS )
  679. {
  680. //Make sure we can see the target!
  681. PartitionFilterLineOfSight filterLOS( self );
  682. PartitionFilter *filters[] = { &filterLOS, NULL };
  683. ObjectIterator *iter = ThePartitionManager->iterateObjectsInRange( self, range, FROM_BOUNDINGSPHERE_2D, filters, ITER_SORTED_NEAR_TO_FAR );
  684. for( Object *theTarget = iter->first(); theTarget; theTarget = iter->next() )
  685. {
  686. //LOS check succeeded.
  687. if( target == theTarget )
  688. {
  689. return true;
  690. }
  691. }
  692. }
  693. else
  694. {
  695. return true;
  696. }
  697. }
  698. return false;
  699. }
  700. //-------------------------------------------------------------------------------------------------
  701. Bool SpecialAbilityUpdate::isWithinAbilityAbortRange() const
  702. {
  703. const SpecialAbilityUpdateModuleData* data = getSpecialAbilityUpdateModuleData();
  704. const Object *self = getObject();
  705. //Quickly convert very short range approachs to "contact" class requiring collision before
  706. //stopping.
  707. Real range = data->m_startAbilityRange;
  708. const Real UNDERSIZE = PATHFIND_CELL_SIZE_F * 0.25f;
  709. range = __max( 0.0f, range - UNDERSIZE );
  710. Real fDistSquared = 0.0f;
  711. Object *target = NULL;
  712. if( m_targetID != INVALID_ID )
  713. {
  714. target = TheGameLogic->findObjectByID( m_targetID );
  715. if( target )
  716. {
  717. fDistSquared = ThePartitionManager->getDistanceSquared( self, target, FROM_BOUNDINGSPHERE_2D );
  718. }
  719. }
  720. else if( m_targetPos.x || m_targetPos.y || m_targetPos.z ) //It's zero if not used...
  721. {
  722. fDistSquared = ThePartitionManager->getDistanceSquared( self, &m_targetPos, FROM_BOUNDINGSPHERE_2D );
  723. }
  724. else
  725. {
  726. //No position, so this step is useless
  727. return true;
  728. }
  729. //Check to see how far we are from the target!
  730. Real fStartRangeSquared = data->m_abilityAbortRange * data->m_abilityAbortRange;
  731. if( fDistSquared <= fStartRangeSquared )
  732. {
  733. if( range == 0.0f && m_targetID != INVALID_ID )
  734. {
  735. //We want to ensure we collided with our target first!
  736. ObjectIterator *iter = ThePartitionManager->iteratePotentialCollisions( self->getPosition(), self->getGeometryInfo(), 0.0f );
  737. MemoryPoolObjectHolder hold(iter);
  738. for( Object *them = iter->first(); them; them = iter->next() )
  739. {
  740. if( target == them )
  741. {
  742. return true;
  743. }
  744. }
  745. return false;
  746. }
  747. return true;
  748. }
  749. return false;
  750. }
  751. //-------------------------------------------------------------------------------------------------
  752. Bool SpecialAbilityUpdate::approachTarget()
  753. {
  754. Object *self = getObject();
  755. if( m_targetID != INVALID_ID )
  756. {
  757. Object *target = TheGameLogic->findObjectByID( m_targetID );
  758. if( target )
  759. {
  760. AIUpdateInterface *ai = self->getAIUpdateInterface();
  761. if( ai )
  762. {
  763. ai->ignoreObstacle( target );
  764. ai->aiMoveToObject( target, CMD_FROM_AI );
  765. return true;
  766. }
  767. }
  768. }
  769. else if( m_targetPos.x || m_targetPos.y || m_targetPos.z ) //It's zero if not used...
  770. {
  771. AIUpdateInterface *ai = self->getAIUpdateInterface();
  772. if( ai )
  773. {
  774. ai->aiMoveToPosition( &m_targetPos, CMD_FROM_AI );
  775. return true;
  776. }
  777. }
  778. return false;
  779. }
  780. //-------------------------------------------------------------------------------------------------
  781. void SpecialAbilityUpdate::startPreparation()
  782. {
  783. const SpecialAbilityUpdateModuleData* data = getSpecialAbilityUpdateModuleData();
  784. const SpecialPowerTemplate *spTemplate = data->m_specialPowerTemplate;
  785. //Set the preparation timer
  786. m_prepFrames = data->m_preparationFrames;
  787. switch( spTemplate->getSpecialPowerType() )
  788. {
  789. case SPECIAL_MISSILE_DEFENDER_LASER_GUIDED_MISSILES:
  790. {
  791. Object *target = TheGameLogic->findObjectByID( m_targetID );
  792. if( target )
  793. {
  794. //Specialized code that specifically creates and looks up a laser update.
  795. Object *specialObject = createSpecialObject();
  796. if( specialObject )
  797. {
  798. if (!initLaser(specialObject, target))
  799. return;
  800. }
  801. }
  802. break;
  803. }
  804. case SPECIAL_INFANTRY_CAPTURE_BUILDING:
  805. {
  806. Object *target = TheGameLogic->findObjectByID( m_targetID );
  807. if (target)
  808. {
  809. if (target->getTeam() == getObject()->getTeam())
  810. {
  811. // it's been captured by a colleague! we should stop.
  812. return;
  813. }
  814. }
  815. getObject()->clearAndSetModelConditionFlags( MAKE_MODELCONDITION_MASK( MODELCONDITION_UNPACKING ),
  816. MAKE_MODELCONDITION_MASK( MODELCONDITION_RAISING_FLAG ) );
  817. Drawable* draw = getObject()->getDrawable();
  818. if (draw)
  819. draw->setAnimationCompletionTime(data->m_preparationFrames);
  820. //Warn the victim so he might have a chance to react!
  821. if( target && target->isLocallyControlled() )
  822. {
  823. TheEva->setShouldPlay( EVA_BuildingBeingStolen );
  824. }
  825. break;
  826. }
  827. case SPECIAL_HACKER_DISABLE_BUILDING:
  828. case SPECIAL_BLACKLOTUS_CAPTURE_BUILDING:
  829. case SPECIAL_BLACKLOTUS_DISABLE_VEHICLE_HACK:
  830. case SPECIAL_BLACKLOTUS_STEAL_CASH_HACK:
  831. {
  832. Object *target = TheGameLogic->findObjectByID( m_targetID );
  833. if( target )
  834. {
  835. Relationship r = getObject()->getRelationship(target);
  836. if( r == ALLIES )
  837. return;
  838. //Specialized code that specifically creates and looks up a laser update.
  839. Object *specialObject = createSpecialObject();
  840. if( specialObject )
  841. {
  842. if (!initLaser(specialObject, target))
  843. return;
  844. //For the hacker this sets up the looping typing animation.
  845. getObject()->clearAndSetModelConditionFlags( MAKE_MODELCONDITION_MASK( MODELCONDITION_UNPACKING ),
  846. MAKE_MODELCONDITION_MASK( MODELCONDITION_FIRING_A ) );
  847. }
  848. //Warn the victim so he might have a chance to react!
  849. if( spTemplate->getSpecialPowerType() == SPECIAL_BLACKLOTUS_CAPTURE_BUILDING && target && target->isLocallyControlled() )
  850. {
  851. TheEva->setShouldPlay( EVA_BuildingBeingStolen );
  852. }
  853. }
  854. break;
  855. }
  856. }
  857. //Trigger the reset timer on the special power because we're officially starting it now!
  858. SpecialPowerModuleInterface *spmInterface = getMySPM();
  859. if( spmInterface )
  860. {
  861. spmInterface->markSpecialPowerTriggered(NULL);// Null for not creating a view object
  862. }
  863. if (getObject()->getAI()) {
  864. getObject()->getAI()->aiIdle( CMD_FROM_AI ); // just in case. jba.
  865. }
  866. getObject()->setStatus( OBJECT_STATUS_IS_USING_ABILITY );
  867. m_prepSoundLoop = data->m_prepSoundLoop;
  868. m_prepSoundLoop.setObjectID( getObject()->getID() );
  869. m_prepSoundLoop.setPlayingHandle( TheAudio->addAudioEvent( &m_prepSoundLoop ) );
  870. }
  871. //-------------------------------------------------------------------------------------------------
  872. SpecialPowerModuleInterface* SpecialAbilityUpdate::getMySPM()
  873. {
  874. const SpecialAbilityUpdateModuleData* d = getSpecialAbilityUpdateModuleData();
  875. return getObject()->getSpecialPowerModule(d->m_specialPowerTemplate);
  876. }
  877. //-------------------------------------------------------------------------------------------------
  878. Bool SpecialAbilityUpdate::initLaser(Object* specialObject, Object* target )
  879. {
  880. const SpecialAbilityUpdateModuleData* data = getSpecialAbilityUpdateModuleData();
  881. static NameKeyType key_LaserUpdate = NAMEKEY( "LaserUpdate" );
  882. Drawable *draw = specialObject->getDrawable();
  883. if( !draw )
  884. {
  885. killSpecialObjects();
  886. return false;
  887. }
  888. LaserUpdate *update = (LaserUpdate*)draw->findClientUpdateModule( key_LaserUpdate );
  889. if( !update )
  890. {
  891. killSpecialObjects();
  892. return false;
  893. }
  894. Coord3D startPos;
  895. if( !getObject()->getSingleLogicalBonePosition( data->m_specialObjectAttachToBoneName.str(), &startPos, NULL ) )
  896. {
  897. //If we can't find the bone, then set it to our current position.
  898. startPos.set( getObject()->getPosition() );
  899. }
  900. Coord3D endPos;
  901. if (target)
  902. {
  903. target->getGeometryInfo().getCenterPosition( *target->getPosition(), endPos );
  904. }
  905. else
  906. {
  907. endPos = startPos;
  908. }
  909. update->initLaser( NULL, &startPos, &endPos );
  910. return true;
  911. }
  912. //-------------------------------------------------------------------------------------------------
  913. Bool SpecialAbilityUpdate::continuePreparation()
  914. {
  915. const SpecialAbilityUpdateModuleData* data = getSpecialAbilityUpdateModuleData();
  916. const SpecialPowerTemplate *spTemplate = data->m_specialPowerTemplate;
  917. //Check if we are within the abort distance, otherwise abort!
  918. if( data->m_abilityAbortRange < SPECIAL_ABILITY_HUGE_DISTANCE )
  919. {
  920. if( !isWithinAbilityAbortRange() )
  921. {
  922. return false;
  923. }
  924. }
  925. switch( spTemplate->getSpecialPowerType() )
  926. {
  927. case SPECIAL_MISSILE_DEFENDER_LASER_GUIDED_MISSILES:
  928. case SPECIAL_BLACKLOTUS_DISABLE_VEHICLE_HACK:
  929. {
  930. Object *target = TheGameLogic->findObjectByID( m_targetID );
  931. if( !target )
  932. {
  933. //Target is dead, stop.
  934. return false;
  935. }
  936. Relationship r = getObject()->getRelationship(target);
  937. if( r == ALLIES )
  938. {
  939. //It's been captured by a colleague, so cancel!
  940. return false;
  941. }
  942. //Specialized code that specifically creates and looks up a laser update.
  943. for( std::list<ObjectID>::iterator it = m_specialObjectIDList.begin(); it != m_specialObjectIDList.end(); ++it )
  944. {
  945. Object* specialObject = TheGameLogic->findObjectByID( *it );
  946. if( specialObject )
  947. {
  948. if( !initLaser( specialObject, target ) )
  949. {
  950. return false;
  951. }
  952. }
  953. }
  954. break;
  955. }
  956. case SPECIAL_INFANTRY_CAPTURE_BUILDING:
  957. case SPECIAL_BLACKLOTUS_CAPTURE_BUILDING:
  958. // PROCESS THE FLASHING WHILE GETTING CAPTURED FX HERE
  959. Object* target = TheGameLogic->findObjectByID( m_targetID );
  960. if( !target )
  961. {
  962. //Target is dead, stop.
  963. return false;
  964. }
  965. Relationship r = getObject()->getRelationship(target);
  966. if( r == ALLIES )
  967. {
  968. //It's been captured by a colleague, so cancel!
  969. return false;
  970. }
  971. if (data->m_doCaptureFX)
  972. {
  973. Drawable *targetDraw = target->getDrawable();
  974. if (targetDraw) // skip fx if merely 'invulnerable'
  975. {
  976. Bool lastPhase = ( ((Int)m_captureFlashPhase) & 1 );// were we in a flashy phase last frame?
  977. Real denominator = MAX(1, data->m_preparationFrames);
  978. Real increment = 1.0f - ((Real)m_prepFrames / denominator );
  979. m_captureFlashPhase += increment / 3.0f;
  980. Bool thisPhase = ( ((Int)m_captureFlashPhase) & 1 );// are we in a flashy phase this frame?
  981. if ( lastPhase && ( ! thisPhase ) )
  982. {
  983. RGBColor myHouseColor;
  984. myHouseColor.setFromInt( getObject()->getIndicatorColor() );
  985. Real saturation = TheGlobalData->m_selectionFlashSaturationFactor;
  986. targetDraw->saturateRGB( myHouseColor, saturation );
  987. targetDraw->flashAsSelected( &myHouseColor ); //In MY house color, not his!
  988. AudioEventRTS defectorTimerSound = TheAudio->getMiscAudio()->m_defectorTimerTickSound;
  989. defectorTimerSound.setObjectID( m_targetID );
  990. TheAudio->addAudioEvent(&defectorTimerSound);
  991. }
  992. }
  993. }
  994. SpecialPowerModuleInterface *spmInterface = getMySPM();
  995. if (spmInterface && spTemplate->getSpecialPowerType() == SPECIAL_INFANTRY_CAPTURE_BUILDING )
  996. {
  997. // these keep resetting the recharge timer while in use. (srj) // only for infantry capture, not black lotus which resets in triggerAbilityEffect()
  998. spmInterface->startPowerRecharge();
  999. }
  1000. break;
  1001. }
  1002. return true;
  1003. }
  1004. //-------------------------------------------------------------------------------------------------
  1005. void SpecialAbilityUpdate::triggerAbilityEffect()
  1006. {
  1007. const SpecialAbilityUpdateModuleData* data = getSpecialAbilityUpdateModuleData();
  1008. const SpecialPowerTemplate *spTemplate = data->m_specialPowerTemplate;
  1009. Object *object = getObject();
  1010. //Award experience to units for triggering the ability (optional and ini specified).
  1011. //NOTE: Be award of persistant abilities that call trigger over and over again!
  1012. if( data->m_awardXPForTriggering )
  1013. {
  1014. ExperienceTracker *xpTracker = object->getExperienceTracker();
  1015. if( xpTracker )
  1016. {
  1017. xpTracker->addExperiencePoints( data->m_awardXPForTriggering );
  1018. }
  1019. }
  1020. //Also add skill points. If unspecified, it'll use the award experience to units for triggering value.
  1021. Int skillPoints = data->m_skillPointsForTriggering != -1 ? data->m_skillPointsForTriggering : data->m_awardXPForTriggering;
  1022. if( skillPoints > 0 )
  1023. {
  1024. Player *player = object->getControllingPlayer();
  1025. if( player )
  1026. {
  1027. player->addSkillPoints( skillPoints );
  1028. }
  1029. }
  1030. AudioEventRTS sound = data->m_triggerSound;
  1031. sound.setObjectID( object->getID() );
  1032. TheAudio->addAudioEvent( &sound );
  1033. Bool okToLoseStealth = TRUE;
  1034. switch( spTemplate->getSpecialPowerType() )
  1035. {
  1036. case SPECIAL_MISSILE_DEFENDER_LASER_GUIDED_MISSILES:
  1037. {
  1038. Object *target = TheGameLogic->findObjectByID( m_targetID );
  1039. if( target )
  1040. {
  1041. const Weapon *weapon = object->getWeaponInWeaponSlot( SECONDARY_WEAPON );
  1042. if( weapon )
  1043. {
  1044. // lock it just till the weapon is empty or the attack is "done"
  1045. object->setWeaponLock( SECONDARY_WEAPON, LOCKED_TEMPORARILY );
  1046. AIUpdateInterface *ai = object->getAIUpdateInterface();
  1047. if( ai )
  1048. {
  1049. ai->aiAttackObject( target, NO_MAX_SHOTS_LIMIT, CMD_FROM_AI );
  1050. }
  1051. }
  1052. }
  1053. break;
  1054. }
  1055. case SPECIAL_TANKHUNTER_TNT_ATTACK:
  1056. case SPECIAL_TIMED_CHARGES:
  1057. {
  1058. //Place new tnt.
  1059. Object *target = TheGameLogic->findObjectByID( m_targetID );
  1060. //sanity
  1061. if( !target )
  1062. {
  1063. return;
  1064. }
  1065. Object *charge = createSpecialObject();
  1066. if( charge )
  1067. {
  1068. static NameKeyType key_StickyBombUpdate = NAMEKEY( "StickyBombUpdate" );
  1069. StickyBombUpdate *update = (StickyBombUpdate*)charge->findUpdateModule( key_StickyBombUpdate );
  1070. if( !update )
  1071. {
  1072. DEBUG_ASSERTCRASH( 0,
  1073. ("Unit '%s' attempted to place %s on %s but the bomb requires a StickyBombUpdate module.",
  1074. object->getTemplate()->getName().str(),
  1075. charge->getTemplate()->getName().str(),
  1076. target->getTemplate()->getName().str() ) );
  1077. killSpecialObjects();
  1078. return;
  1079. }
  1080. //Setting the producer ID allows the sticky bomb update module to initialize
  1081. //and setup timers, etc.
  1082. update->init( target, object );
  1083. }
  1084. break;
  1085. }
  1086. case SPECIAL_HACKER_DISABLE_BUILDING:
  1087. case SPECIAL_BLACKLOTUS_DISABLE_VEHICLE_HACK:
  1088. {
  1089. //Disable the target temporarily.
  1090. Object *target = TheGameLogic->findObjectByID( m_targetID );
  1091. //sanity
  1092. if( !target )
  1093. {
  1094. return;
  1095. }
  1096. Relationship r = object->getRelationship(target);
  1097. if ( r == ALLIES)
  1098. return;
  1099. //Disable the target for a specified amount of time.
  1100. target->setDisabledUntil( DISABLED_HACKED, TheGameLogic->getFrame() + data->m_effectDuration );
  1101. UnsignedInt durationInterleaveFactor = 1;
  1102. Real targetFootprintArea = target->getGeometryInfo().getFootprintArea();
  1103. if ( ( targetFootprintArea < 300) && target->isKindOf( KINDOF_STRUCTURE ))
  1104. {
  1105. m_doDisableFXParticles = !m_doDisableFXParticles; // toggle if small building
  1106. durationInterleaveFactor = 2;
  1107. }
  1108. if ( m_doDisableFXParticles )
  1109. {
  1110. const ParticleSystemTemplate *tmp = data->m_disableFXParticleSystem;
  1111. if (tmp)
  1112. {
  1113. ParticleSystem *sys = TheParticleSystemManager->createParticleSystem(tmp);
  1114. if (sys)
  1115. {
  1116. Coord3D offs = {0,0,0};
  1117. target->getGeometryInfo().makeRandomOffsetWithinFootprint( offs );
  1118. sys->attachToObject(target);
  1119. sys->setPosition( &offs );
  1120. sys->setSystemLifetime( data->m_effectDuration * durationInterleaveFactor ); //lifetime of the system, not the particles
  1121. }
  1122. }
  1123. }
  1124. break;
  1125. }
  1126. case SPECIAL_INFANTRY_CAPTURE_BUILDING:
  1127. case SPECIAL_BLACKLOTUS_CAPTURE_BUILDING:
  1128. {
  1129. Object *target = TheGameLogic->findObjectByID( m_targetID );
  1130. //sanity
  1131. if( !target )
  1132. {
  1133. return;
  1134. }
  1135. if (target->getTeam() == object->getTeam())
  1136. {
  1137. // it's been captured by a colleague! we should stop.
  1138. return;
  1139. }
  1140. // Just in case we are capturing a building which is already garrisoned by other
  1141. ContainModuleInterface * contain = target->getContain();
  1142. if ( contain && contain->isGarrisonable() )
  1143. {
  1144. contain->removeAllContained( TRUE );
  1145. break; // we do not want to set a neutral building to our team if we are not in it, that would be confusing!
  1146. }
  1147. //Play the "building stolen" EVA event if the local player is the victim!
  1148. if( target && target->isLocallyControlled() )
  1149. {
  1150. TheEva->setShouldPlay( EVA_BuildingStolen );
  1151. }
  1152. target->defect( object->getControllingPlayer()->getDefaultTeam(), 1); // one frame of flash!
  1153. SpecialPowerModuleInterface *spmInterface = getMySPM();
  1154. if (spmInterface && spTemplate->getSpecialPowerType() == SPECIAL_BLACKLOTUS_CAPTURE_BUILDING )
  1155. {
  1156. // only for black lotus, not infantry capture which resets in contunueprep()
  1157. spmInterface->startPowerRecharge();
  1158. }
  1159. break;
  1160. break;
  1161. }
  1162. case SPECIAL_BLACKLOTUS_STEAL_CASH_HACK:
  1163. {
  1164. Object *target = TheGameLogic->findObjectByID( m_targetID );
  1165. //sanity
  1166. if( !target )
  1167. {
  1168. return;
  1169. }
  1170. //Steal a thousand cash from the other team!
  1171. Money *targetMoney = target->getControllingPlayer()->getMoney();
  1172. Money *objectMoney = object->getControllingPlayer()->getMoney();
  1173. if( targetMoney && objectMoney )
  1174. {
  1175. UnsignedInt cash = targetMoney->countMoney();
  1176. UnsignedInt desiredAmount = 1000;
  1177. //Check to see if they have 1000 cash, otherwise, take the remainder!
  1178. cash = min( desiredAmount, cash );
  1179. if( cash > 0 )
  1180. {
  1181. //Steal the cash
  1182. targetMoney->withdraw( cash );
  1183. objectMoney->deposit( cash );
  1184. Player* controller = object->getControllingPlayer();
  1185. if (controller)
  1186. controller->getScoreKeeper()->addMoneyEarned( cash );
  1187. //Play the "cash stolen" EVA event if the local player is the victim!
  1188. if( target && target->isLocallyControlled() )
  1189. {
  1190. TheEva->setShouldPlay( EVA_CashStolen );
  1191. }
  1192. //Display cash income floating over the blacklotus
  1193. UnicodeString moneyString;
  1194. moneyString.format( TheGameText->fetch( "GUI:AddCash" ), cash );
  1195. Coord3D pos;
  1196. pos.set( object->getPosition() );
  1197. pos.z += 20.0f; //add a little z to make it show up above the unit.
  1198. TheInGameUI->addFloatingText( moneyString, &pos, GameMakeColor( 0, 255, 0, 255 ) );
  1199. //Display cash lost floating over the target
  1200. moneyString.format( TheGameText->fetch( "GUI:LoseCash" ), cash );
  1201. pos.set( target->getPosition() );
  1202. pos.z += 30.0f; //add a little z to make it show up above the unit.
  1203. TheInGameUI->addFloatingText( moneyString, &pos, GameMakeColor( 255, 0, 0, 255 ) );
  1204. }
  1205. }
  1206. break;
  1207. }
  1208. case SPECIAL_REMOTE_CHARGES:
  1209. {
  1210. static NameKeyType key_StickyBombUpdate = NAMEKEY( "StickyBombUpdate" );
  1211. if( m_targetID == INVALID_ID && !m_targetPos.x && !m_targetPos.y && !m_targetPos.z )
  1212. {
  1213. //If there is no target object nor position, then we are detonating the existing charges.
  1214. std::list<ObjectID>::iterator i;
  1215. for( i = m_specialObjectIDList.begin(); i != m_specialObjectIDList.end(); ++i )
  1216. {
  1217. Object *specialObject = TheGameLogic->findObjectByID( *i );
  1218. if( specialObject )
  1219. {
  1220. StickyBombUpdate *update = (StickyBombUpdate*)specialObject->findUpdateModule( key_StickyBombUpdate );
  1221. if( update )
  1222. {
  1223. //Blow it up!!!
  1224. update->detonate();
  1225. okToLoseStealth = FALSE;
  1226. //Note: while the objects are detonating, they will still exist in the game.
  1227. //Our update will be responsible for validating their existance and removing them..
  1228. //in case either the enemy player cleans one up, or after it's gone.
  1229. }
  1230. }
  1231. }
  1232. }
  1233. else
  1234. {
  1235. //Place a new charge.
  1236. Object *target = TheGameLogic->findObjectByID( m_targetID );
  1237. //sanity
  1238. if( !target )
  1239. {
  1240. return;
  1241. }
  1242. Object *charge = createSpecialObject();
  1243. if( charge )
  1244. {
  1245. StickyBombUpdate *update = (StickyBombUpdate*)charge->findUpdateModule( key_StickyBombUpdate );
  1246. if( !update )
  1247. {
  1248. DEBUG_ASSERTCRASH( 0,
  1249. ("Unit '%s' attempted to place remote charge but the charge '%s' requires a StickyBombUpdate module.",
  1250. object->getTemplate()->getName().str(),
  1251. charge->getTemplate()->getName().str() ) );
  1252. killSpecialObjects();
  1253. return;
  1254. }
  1255. //Setting the producer ID allows the sticky bomb update module to initialize
  1256. //and setup timers, etc.
  1257. update->init( target, object );
  1258. }
  1259. }
  1260. break;
  1261. }
  1262. case SPECIAL_DISGUISE_AS_VEHICLE:
  1263. {
  1264. Object *target = TheGameLogic->findObjectByID( m_targetID );
  1265. static NameKeyType key_StealthUpdate = NAMEKEY( "StealthUpdate" );
  1266. if( target )
  1267. {
  1268. StealthUpdate *update = (StealthUpdate*)object->findUpdateModule( key_StealthUpdate );
  1269. if( update )
  1270. {
  1271. update->disguiseAsObject( target );
  1272. }
  1273. }
  1274. break;
  1275. }
  1276. }
  1277. if( data->m_loseStealthOnTrigger && okToLoseStealth)
  1278. {
  1279. static NameKeyType key_StealthUpdate = NAMEKEY( "StealthUpdate" );
  1280. StealthUpdate* stealth = (StealthUpdate*)object->findUpdateModule( key_StealthUpdate );
  1281. if( stealth )
  1282. {
  1283. stealth->markAsDetected();
  1284. }
  1285. }
  1286. }
  1287. //-------------------------------------------------------------------------------------------------
  1288. Object* SpecialAbilityUpdate::createSpecialObject()
  1289. {
  1290. const SpecialAbilityUpdateModuleData* data = getSpecialAbilityUpdateModuleData();
  1291. Object *specialObject = NULL;
  1292. if( m_specialObjectEntries == data->m_maxSpecialObjects )
  1293. {
  1294. if( data->m_specialObjectsPersistent )
  1295. {
  1296. //If we are dealing with persistent objects, and we have reached our
  1297. //limit we can have, then don't allow any more to be created....
  1298. //We could add recycling code if need be.. but the logic that handles
  1299. //canDoSpecialPowerXXX should prevent this triggering.
  1300. return NULL;
  1301. }
  1302. else
  1303. {
  1304. //We've reached our limit with non-persistent object(s), so remove them.
  1305. killSpecialObjects();
  1306. }
  1307. }
  1308. //Get the template of the special object
  1309. const ThingTemplate* thingTemplate = TheThingFactory->findTemplate( data->m_specialObjectName );
  1310. if( thingTemplate )
  1311. {
  1312. //Create a new special object
  1313. specialObject = TheThingFactory->newObject( thingTemplate, getObject()->getTeam() );
  1314. if( specialObject )
  1315. {
  1316. m_specialObjectIDList.push_back( specialObject->getID() );
  1317. m_specialObjectEntries++;
  1318. specialObject->setPosition( getObject()->getPosition() );
  1319. //So we can get experience from it when it blows up (if applicable)
  1320. //specialObject->setProducer( getObject() ); --This causes it to be an enemy which is naughty.
  1321. ExperienceTracker *xpTracker = specialObject->getExperienceTracker();
  1322. if( xpTracker )
  1323. {
  1324. xpTracker->setExperienceSink( getObject()->getID() );
  1325. }
  1326. }
  1327. }
  1328. return specialObject;
  1329. }
  1330. //-------------------------------------------------------------------------------------------------
  1331. void SpecialAbilityUpdate::killSpecialObjects()
  1332. {
  1333. const SpecialAbilityUpdateModuleData* data = getSpecialAbilityUpdateModuleData();
  1334. const SpecialPowerTemplate *spTemplate = data->m_specialPowerTemplate;
  1335. Object *specialObject;
  1336. std::list<ObjectID>::iterator i;
  1337. for( i = m_specialObjectIDList.begin(); i != m_specialObjectIDList.end(); ++i )
  1338. {
  1339. specialObject = TheGameLogic->findObjectByID( *i );
  1340. if( specialObject )
  1341. {
  1342. //Delete the old one!
  1343. TheGameLogic->destroyObject( specialObject );
  1344. }
  1345. }
  1346. //Reset the list
  1347. m_specialObjectIDList.clear();
  1348. m_specialObjectEntries = 0;
  1349. switch( spTemplate->getSpecialPowerType() )
  1350. {
  1351. case SPECIAL_MISSILE_DEFENDER_LASER_GUIDED_MISSILES:
  1352. //The instant we remove the laser, reset the weaponset!
  1353. Object *object = getObject();
  1354. const Weapon *weapon = object->getWeaponInWeaponSlot( PRIMARY_WEAPON );
  1355. if( weapon )
  1356. {
  1357. // lock it just till the weapon is empty or the attack is "done"
  1358. object->setWeaponLock( PRIMARY_WEAPON, LOCKED_TEMPORARILY );
  1359. }
  1360. break;
  1361. }
  1362. }
  1363. //-------------------------------------------------------------------------------------------------
  1364. UnsignedInt SpecialAbilityUpdate::getSpecialObjectCount() const
  1365. {
  1366. return m_specialObjectEntries;
  1367. }
  1368. //-------------------------------------------------------------------------------------------------
  1369. UnsignedInt SpecialAbilityUpdate::getSpecialObjectMax() const
  1370. {
  1371. const SpecialAbilityUpdateModuleData* data = getSpecialAbilityUpdateModuleData();
  1372. return data->m_maxSpecialObjects;
  1373. }
  1374. //-------------------------------------------------------------------------------------------------
  1375. void SpecialAbilityUpdate::validateSpecialObjects()
  1376. {
  1377. Object *specialObject;
  1378. std::list<ObjectID>::iterator i = m_specialObjectIDList.begin();
  1379. while( i != m_specialObjectIDList.end() )
  1380. {
  1381. std::list<ObjectID>::iterator prev = i;
  1382. specialObject = TheGameLogic->findObjectByID( *i );
  1383. i++;
  1384. if( !specialObject )
  1385. {
  1386. //Remove it from the list, and decrement our counter.
  1387. m_specialObjectIDList.erase( prev );
  1388. --m_specialObjectEntries;
  1389. }
  1390. }
  1391. }
  1392. //-------------------------------------------------------------------------------------------------
  1393. void SpecialAbilityUpdate::finishAbility()
  1394. {
  1395. const SpecialAbilityUpdateModuleData* data = getSpecialAbilityUpdateModuleData();
  1396. m_withinStartAbilityRange = false;
  1397. m_packingState = STATE_NONE;
  1398. //The flee range code is used only if the unit has completed a special ability against a target.
  1399. //If we don't have a valid target, then we don't flee -- the specific example is when Colonel Burton
  1400. //uses his remote charge special ability. With a target, he will plant the charge then flee. Without
  1401. //a target, he simply detonates it without fleeing.
  1402. Bool validTarget = m_targetPos.x || m_targetPos.y || m_targetPos.z || m_targetID != INVALID_ID;
  1403. if( data->m_fleeRangeAfterCompletion && validTarget )
  1404. {
  1405. Coord3D pos;
  1406. pos.set( getObject()->getPosition() );
  1407. AIUpdateInterface *ai = getObject()->getAIUpdateInterface();
  1408. if( ai )
  1409. {
  1410. Coord3D dir;
  1411. dir.set( getObject()->getUnitDirectionVector2D() );
  1412. dir.normalize();
  1413. dir.scale( data->m_fleeRangeAfterCompletion );
  1414. if( data->m_flipObjectAfterUnpacking || data->m_flipObjectAfterPacking )
  1415. {
  1416. pos.add( &dir );
  1417. }
  1418. else
  1419. {
  1420. pos.sub( &dir );
  1421. }
  1422. // Now check for mines. Normally we are fleeing from a bomb we just planted.
  1423. // It is not good to run back towards the previous mine we just planted about
  1424. // the time it explodes. jba.
  1425. Object *obj = getObject();
  1426. if (obj) {
  1427. Player *contPlayer = obj->getControllingPlayer();
  1428. if (contPlayer) {
  1429. PartitionFilterSamePlayer filterPlayer( contPlayer ); // Look for our own mines.
  1430. PartitionFilterAcceptByKindOf filterKind(MAKE_KINDOF_MASK(KINDOF_MINE), KINDOFMASK_NONE);
  1431. PartitionFilter *filters[] = { &filterKind, &filterPlayer, NULL };
  1432. Object *mine = ThePartitionManager->getClosestObject( &pos, data->m_fleeRangeAfterCompletion, FROM_CENTER_2D, filters );// could be null. this is ok.
  1433. if (mine) {
  1434. dir.set(pos.x-mine->getPosition()->x, pos.y-mine->getPosition()->y, 0);
  1435. dir.normalize();
  1436. dir.scale(data->m_fleeRangeAfterCompletion);
  1437. pos = *mine->getPosition();
  1438. pos.add(&dir);
  1439. }
  1440. }
  1441. }
  1442. // this is just a trick: apply a motive force (even zero!) so that
  1443. // AI will think we were already "moving". this causes us to go directly
  1444. // to the moving anim rather than popping into idle for 1 frame (srj)
  1445. PhysicsBehavior* phys = getObject()->getPhysics();
  1446. if (phys)
  1447. {
  1448. Coord3D bogusForce;
  1449. bogusForce.zero();
  1450. phys->applyMotiveForce(&bogusForce);
  1451. }
  1452. ai->aiMoveToPosition( &pos, CMD_FROM_AI );
  1453. Object *target = TheGameLogic->findObjectByID( m_targetID );
  1454. if( target )
  1455. {
  1456. ai->ignoreObstacle( target );
  1457. }
  1458. }
  1459. }
  1460. else
  1461. {
  1462. //Clear any old AI before ending this special ability.
  1463. AIUpdateInterface *ai = getObject()->getAIUpdateInterface();
  1464. if (ai)
  1465. ai->aiIdle(CMD_FROM_AI);
  1466. }
  1467. //// Emit finished sound //Moved to StartPacking(), thank you, ML
  1468. //AudioEventRTS event = *getObject()->getTemplate()->getVoiceTaskComplete();
  1469. //event.setObjectID(getObject()->getID());
  1470. //TheAudio->addAudioEvent(&event);
  1471. onExit( false );
  1472. }
  1473. //-------------------------------------------------------------------------------------------------
  1474. Bool SpecialAbilityUpdate::isFacing()
  1475. {
  1476. AIUpdateInterface *ai = getObject()->getAIUpdateInterface();
  1477. if( ai )
  1478. {
  1479. if( !m_facingComplete && m_facingInitiated)
  1480. {
  1481. if( ai->isIdle() )
  1482. {
  1483. //We finished facing the target
  1484. m_facingComplete = true;
  1485. return false;
  1486. }
  1487. //We are still in the process of facing the target
  1488. return true;
  1489. }
  1490. }
  1491. else
  1492. {
  1493. //Something without AI always faces target...
  1494. return true;
  1495. }
  1496. //Either the facing hasn't initiated yet, or we are complete facing.
  1497. return false;
  1498. }
  1499. //-------------------------------------------------------------------------------------------------
  1500. Bool SpecialAbilityUpdate::needToFace() const
  1501. {
  1502. const AIUpdateInterface *ai = getObject()->getAIUpdateInterface();
  1503. if( !ai )
  1504. {
  1505. //If we don't have AI, we can't face target!
  1506. return false;
  1507. }
  1508. //Return true if we haven't initiated facing or if we haven't completed it.
  1509. return !m_facingInitiated || !m_facingComplete;
  1510. }
  1511. //-------------------------------------------------------------------------------------------------
  1512. void SpecialAbilityUpdate::startFacing()
  1513. {
  1514. Object *target = TheGameLogic->findObjectByID( m_targetID );
  1515. AIUpdateInterface *ai = getObject()->getAIUpdateInterface();
  1516. if( !ai )
  1517. {
  1518. return;
  1519. }
  1520. // First kill it's physics.
  1521. ai->aiIdle( CMD_FROM_AI );
  1522. if (getObject()->getPhysics())
  1523. getObject()->getPhysics()->resetDynamicPhysics();
  1524. // NO, do not do this; we promise Update modules that they will be
  1525. // called *exactly* once per frame... no more, no less! (srj)
  1526. //ai->update();
  1527. m_facingInitiated = true;
  1528. if( target )
  1529. {
  1530. ai->aiFaceObject( target, CMD_FROM_AI );
  1531. }
  1532. else if( m_targetPos.x || m_targetPos.y || m_targetPos.z ) //It's zero if not used...
  1533. {
  1534. ai->aiFacePosition( &m_targetPos, CMD_FROM_AI );
  1535. }
  1536. }
  1537. //-------------------------------------------------------------------------------------------------
  1538. const SpecialPowerTemplate* SpecialAbilityUpdate::getTemplate() const
  1539. {
  1540. const SpecialAbilityUpdateModuleData* data = getSpecialAbilityUpdateModuleData();
  1541. return data->m_specialPowerTemplate;
  1542. }
  1543. //-------------------------------------------------------------------------------------------------
  1544. SpecialPowerType SpecialAbilityUpdate::getSpecialPowerType() const
  1545. {
  1546. return getTemplate()->getSpecialPowerType();
  1547. }
  1548. //-------------------------------------------------------------------------------------------------
  1549. // This looks for a special object with specified producer ID
  1550. //-------------------------------------------------------------------------------------------------
  1551. Object* SpecialAbilityUpdate::findSpecialObjectWithProducerID( const Object *target )
  1552. {
  1553. Object *specialObject;
  1554. std::list<ObjectID>::iterator i;
  1555. for( i = m_specialObjectIDList.begin(); i != m_specialObjectIDList.end(); ++i )
  1556. {
  1557. specialObject = TheGameLogic->findObjectByID( *i );
  1558. if( specialObject )
  1559. {
  1560. if( specialObject->getProducerID() == target->getID() )
  1561. {
  1562. return specialObject;
  1563. }
  1564. }
  1565. }
  1566. return NULL;
  1567. }
  1568. //-------------------------------------------------------------------------------------------------
  1569. void SpecialAbilityUpdate::endPreparation()
  1570. {
  1571. getObject()->clearStatus( OBJECT_STATUS_IS_USING_ABILITY );
  1572. TheAudio->removeAudioEvent( m_prepSoundLoop.getPlayingHandle() );
  1573. // Based on the special that we just finished preparing (either by failure or success),
  1574. // do we want to keep the "special objects" created? Some specials will -- others won't.
  1575. // Note that persistant specials will not call this until preparation is complete (not
  1576. // recycling).
  1577. const SpecialAbilityUpdateModuleData* data = getSpecialAbilityUpdateModuleData();
  1578. const SpecialPowerTemplate *spTemplate = data->m_specialPowerTemplate;
  1579. switch( spTemplate->getSpecialPowerType() )
  1580. {
  1581. case SPECIAL_TANKHUNTER_TNT_ATTACK:
  1582. case SPECIAL_TIMED_CHARGES:
  1583. case SPECIAL_REMOTE_CHARGES:
  1584. case SPECIAL_DISGUISE_AS_VEHICLE:
  1585. // No, don't delete placed charges.
  1586. // -OR- Not applicable (doesn't use special objects).
  1587. break;
  1588. case SPECIAL_MISSILE_DEFENDER_LASER_GUIDED_MISSILES:
  1589. case SPECIAL_HACKER_DISABLE_BUILDING:
  1590. case SPECIAL_BLACKLOTUS_DISABLE_VEHICLE_HACK:
  1591. case SPECIAL_BLACKLOTUS_CAPTURE_BUILDING:
  1592. case SPECIAL_BLACKLOTUS_STEAL_CASH_HACK:
  1593. case SPECIAL_INFANTRY_CAPTURE_BUILDING:
  1594. killSpecialObjects();
  1595. break;
  1596. }
  1597. }
  1598. // ------------------------------------------------------------------------------------------------
  1599. /** CRC */
  1600. // ------------------------------------------------------------------------------------------------
  1601. void SpecialAbilityUpdate::crc( Xfer *xfer )
  1602. {
  1603. // extend base class
  1604. UpdateModule::crc( xfer );
  1605. } // end crc
  1606. // ------------------------------------------------------------------------------------------------
  1607. /** Xfer method
  1608. * Version Info:
  1609. * 1: Initial version */
  1610. // ------------------------------------------------------------------------------------------------
  1611. void SpecialAbilityUpdate::xfer( Xfer *xfer )
  1612. {
  1613. // version
  1614. XferVersion currentVersion = 1;
  1615. XferVersion version = currentVersion;
  1616. xfer->xferVersion( &version, currentVersion );
  1617. // extend base class
  1618. UpdateModule::xfer( xfer );
  1619. // active
  1620. xfer->xferBool( &m_active );
  1621. // prep frames
  1622. xfer->xferUnsignedInt( &m_prepFrames );
  1623. // anim frames
  1624. xfer->xferUnsignedInt( &m_animFrames );
  1625. // target ID
  1626. xfer->xferObjectID( &m_targetID );
  1627. // target position
  1628. xfer->xferCoord3D( &m_targetPos );
  1629. // location count
  1630. xfer->xferInt( &m_locationCount );
  1631. // special object id list
  1632. xfer->xferSTLObjectIDList( &m_specialObjectIDList );
  1633. // special object entries
  1634. xfer->xferUnsignedInt( &m_specialObjectEntries );
  1635. // no target command
  1636. xfer->xferBool( &m_noTargetCommand );
  1637. // packing state
  1638. xfer->xferUser( &m_packingState, sizeof( PackingState ) );
  1639. // facing initiated
  1640. xfer->xferBool( &m_facingInitiated );
  1641. // facing complete
  1642. xfer->xferBool( &m_facingComplete );
  1643. // within start ability range
  1644. xfer->xferBool( &m_withinStartAbilityRange );
  1645. // do disable fx particles
  1646. xfer->xferBool( &m_doDisableFXParticles );
  1647. // capture flash phase
  1648. xfer->xferReal( &m_captureFlashPhase );
  1649. } // end xfer
  1650. // ------------------------------------------------------------------------------------------------
  1651. /** Load post process */
  1652. // ------------------------------------------------------------------------------------------------
  1653. void SpecialAbilityUpdate::loadPostProcess( void )
  1654. {
  1655. // extend base class
  1656. UpdateModule::loadPostProcess();
  1657. } // end loadPostProcess