SpecialAbilityUpdate.cpp 69 KB

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