StealthUpdate.cpp 38 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229
  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: StealthUpdate.cpp ////////////////////////////////////////////////////////////////////////
  24. // Author: Kris Morness, May 2002
  25. // Desc: An update that checks for a status bit to stealth the owning object
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  28. #define DEFINE_STEALTHLEVEL_NAMES
  29. #define DEFINE_OBJECT_STATUS_NAMES
  30. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  31. #include "Common/GameState.h"
  32. #include "Common/Player.h"
  33. #include "Common/PlayerList.h"
  34. #include "Common/Radar.h"
  35. #include "Common/Team.h"
  36. #include "Common/ThingTemplate.h"
  37. #include "Common/ThingFactory.h"
  38. #include "Common/Xfer.h"
  39. #include "GameClient/ControlBar.h"
  40. #include "GameClient/Drawable.h"
  41. #include "GameClient/FXList.h"
  42. #include "GameClient/GameClient.h"
  43. #include "GameClient/Eva.h"
  44. #include "GameLogic/Damage.h"
  45. #include "GameLogic/Object.h"
  46. #include "GameLogic/PartitionManager.h"
  47. #include "GameLogic/Weapon.h"
  48. #include "GameLogic/Module/AIUpdate.h"
  49. #include "GameLogic/Module/StealthUpdate.h"
  50. #include "GameLogic/Module/PhysicsUpdate.h"
  51. #include "GameLogic/Module/ContainModule.h"
  52. #include "GameLogic/Module/SpawnBehavior.h"
  53. #ifdef _INTERNAL
  54. // for occasional debugging...
  55. //#pragma optimize("", off)
  56. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  57. #endif
  58. StealthUpdateModuleData::StealthUpdateModuleData()
  59. {
  60. //Added By Sadullah Nader
  61. //Initialization(s) inserted
  62. m_disguiseFX = NULL;
  63. m_disguiseRevealFX = NULL;
  64. //
  65. m_stealthDelay = UINT_MAX;
  66. m_stealthLevel = 0;
  67. m_stealthSpeed = 0.0f;
  68. m_friendlyOpacityMin = 0.5f;
  69. m_friendlyOpacityMax = 1.0f;
  70. m_pulseFrames = 30;
  71. m_teamDisguised = false;
  72. m_revealDistanceFromTarget = 0.0f;
  73. m_orderIdleEnemiesToAttackMeUponReveal = false;
  74. m_innateStealth = true;
  75. m_disguiseTransitionFrames = 0;
  76. m_disguiseRevealTransitionFrames = 0;
  77. m_blackMarketCheckFrames = 0;
  78. m_enemyDetectionEvaEvent = EVA_Invalid;
  79. m_ownDetectionEvaEvent = EVA_Invalid;
  80. m_grantedBySpecialPower = FALSE;
  81. }
  82. //-------------------------------------------------------------------------------------------------
  83. void StealthUpdateModuleData::buildFieldParse(MultiIniFieldParse& p)
  84. {
  85. UpdateModuleData::buildFieldParse(p);
  86. static const FieldParse dataFieldParse[] =
  87. {
  88. { "StealthDelay", INI::parseDurationUnsignedInt, NULL, offsetof( StealthUpdateModuleData, m_stealthDelay ) },
  89. { "MoveThresholdSpeed", INI::parseVelocityReal, NULL, offsetof( StealthUpdateModuleData, m_stealthSpeed ) },
  90. { "StealthForbiddenConditions", INI::parseBitString32, TheStealthLevelNames, offsetof( StealthUpdateModuleData, m_stealthLevel) },
  91. { "HintDetectableConditions", ObjectStatusMaskType::parseFromINI, NULL, offsetof( StealthUpdateModuleData, m_hintDetectableStates) },
  92. { "RequiredStatus", ObjectStatusMaskType::parseFromINI, NULL, offsetof( StealthUpdateModuleData, m_requiredStatus ) },
  93. { "ForbiddenStatus", ObjectStatusMaskType::parseFromINI, NULL, offsetof( StealthUpdateModuleData, m_forbiddenStatus ) },
  94. { "FriendlyOpacityMin", INI::parsePercentToReal, NULL, offsetof( StealthUpdateModuleData, m_friendlyOpacityMin ) },
  95. { "FriendlyOpacityMax", INI::parsePercentToReal, NULL, offsetof( StealthUpdateModuleData, m_friendlyOpacityMax ) },
  96. { "PulseFrequency", INI::parseDurationUnsignedInt, NULL, offsetof( StealthUpdateModuleData, m_pulseFrames ) },
  97. { "DisguisesAsTeam", INI::parseBool, NULL, offsetof( StealthUpdateModuleData, m_teamDisguised ) },
  98. { "RevealDistanceFromTarget", INI::parseReal, NULL, offsetof( StealthUpdateModuleData, m_revealDistanceFromTarget ) },
  99. { "OrderIdleEnemiesToAttackMeUponReveal", INI::parseBool, NULL, offsetof( StealthUpdateModuleData, m_orderIdleEnemiesToAttackMeUponReveal ) },
  100. { "DisguiseFX", INI::parseFXList, NULL, offsetof( StealthUpdateModuleData, m_disguiseFX ) },
  101. { "DisguiseRevealFX", INI::parseFXList, NULL, offsetof( StealthUpdateModuleData, m_disguiseRevealFX ) },
  102. { "DisguiseTransitionTime", INI::parseDurationUnsignedInt, NULL, offsetof( StealthUpdateModuleData, m_disguiseTransitionFrames ) },
  103. { "DisguiseRevealTransitionTime", INI::parseDurationUnsignedInt, NULL, offsetof( StealthUpdateModuleData, m_disguiseRevealTransitionFrames ) },
  104. { "InnateStealth", INI::parseBool, NULL, offsetof( StealthUpdateModuleData, m_innateStealth ) },
  105. { "UseRiderStealth", INI::parseBool, NULL, offsetof( StealthUpdateModuleData, m_useRiderStealth ) },
  106. { "EnemyDetectionEvaEvent", Eva::parseEvaMessageFromIni, NULL, offsetof( StealthUpdateModuleData, m_enemyDetectionEvaEvent ) },
  107. { "OwnDetectionEvaEvent", Eva::parseEvaMessageFromIni, NULL, offsetof( StealthUpdateModuleData, m_ownDetectionEvaEvent ) },
  108. { "BlackMarketCheckDelay", INI::parseDurationUnsignedInt, NULL, offsetof( StealthUpdateModuleData, m_blackMarketCheckFrames ) },
  109. { "GrantedBySpecialPower", INI::parseBool, NULL, offsetof( StealthUpdateModuleData, m_grantedBySpecialPower ) },
  110. { 0, 0, 0, 0 }
  111. };
  112. p.add(dataFieldParse);
  113. }
  114. //-------------------------------------------------------------------------------------------------
  115. //-------------------------------------------------------------------------------------------------
  116. StealthUpdate::StealthUpdate( Thing *thing, const ModuleData* moduleData ) : UpdateModule( thing, moduleData )
  117. {
  118. const StealthUpdateModuleData *data = getStealthUpdateModuleData();
  119. m_stealthAllowedFrame = TheGameLogic->getFrame() + data->m_stealthDelay;
  120. //Must be enabled manually if using disguise system (bomb truck uses)
  121. m_enabled = !data->m_teamDisguised;
  122. //Added By Sadullah Nader
  123. //Initialization(s) inserted
  124. m_detectionExpiresFrame = 0;
  125. //
  126. m_pulsePhaseRate = 0.2f;
  127. m_pulsePhase = GameClientRandomValueReal(0, PI);
  128. m_disguiseAsPlayerIndex = -1;
  129. m_disguiseAsTemplate = NULL;
  130. m_transitioningToDisguise = false;
  131. m_disguised = false;
  132. m_disguiseTransitionFrames = 0;
  133. m_disguiseHalfpointReached = false;
  134. m_nextBlackMarketCheckFrame = 0;
  135. m_framesGranted = 0;
  136. if( data->m_innateStealth )
  137. {
  138. //Giving innate stealth units this status bit allows other code to easily check the status bit.
  139. getObject()->setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_CAN_STEALTH ) );
  140. }
  141. // start active, since some stealths start enabled from the get-go
  142. if ( data->m_grantedBySpecialPower )
  143. setWakeFrame( getObject(), UPDATE_SLEEP_FOREVER );
  144. else
  145. setWakeFrame( getObject(), UPDATE_SLEEP_NONE );
  146. // we do not need to restore a disguise
  147. m_xferRestoreDisguise = FALSE;
  148. }
  149. //-------------------------------------------------------------------------------------------------
  150. //-------------------------------------------------------------------------------------------------
  151. StealthUpdate::~StealthUpdate( void )
  152. {
  153. }
  154. //-------------------------------------------------------------------------------------------------
  155. void isBlackMarket( Object *obj, void *userData )
  156. {
  157. if( obj && obj->isKindOf( KINDOF_FS_BLACK_MARKET ) )
  158. {
  159. if( obj->isEffectivelyDead() )
  160. {
  161. return;
  162. }
  163. if( obj->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION) )
  164. {
  165. return;
  166. }
  167. if( obj->testStatus( OBJECT_STATUS_SOLD ) )
  168. {
  169. return;
  170. }
  171. *(Bool*)userData = TRUE;
  172. }
  173. }
  174. //---------------------------------------------------------------------------------------~-_-~-_-~-
  175. void StealthUpdate::receiveGrant( Bool active, UnsignedInt frames )
  176. {
  177. Object *obj = getObject();
  178. if ( obj == NULL )
  179. return;
  180. if (this->canDisguise())
  181. return; //so bombtrucks and stuff do not get foiled by this.
  182. //Kris: Turn it off if we pass in FALSE for active.
  183. if( !active && m_enabled )
  184. {
  185. //markAsDetected();
  186. }
  187. m_enabled = active;
  188. if( m_enabled )
  189. {
  190. //On
  191. obj->setStatus( MAKE_OBJECT_STATUS_MASK2( OBJECT_STATUS_CAN_STEALTH, OBJECT_STATUS_STEALTHED ) );
  192. m_stealthAllowedFrame = TheGameLogic->getFrame();
  193. setWakeFrame( obj, UPDATE_SLEEP_NONE );
  194. m_framesGranted = frames;
  195. }
  196. else
  197. {
  198. //Off
  199. obj->clearStatus( MAKE_OBJECT_STATUS_MASK2( OBJECT_STATUS_CAN_STEALTH, OBJECT_STATUS_STEALTHED ) );
  200. m_stealthAllowedFrame = FOREVER;
  201. m_framesGranted = 0;
  202. Drawable *draw = obj->getDrawable();
  203. if( draw )
  204. {
  205. draw->setEffectiveOpacity( 1.0f );
  206. }
  207. }
  208. const ContainModuleInterface *contain = obj->getContain();
  209. if ( contain && contain->isRiderChangeContain() )
  210. {
  211. const Object *rider = contain->friend_getRider();
  212. if ( rider )
  213. {
  214. StealthUpdate *riderStealth = rider->getStealth();
  215. if ( riderStealth )
  216. riderStealth->receiveGrant( active, frames );
  217. }
  218. }
  219. }
  220. //-------------------------------------------------------------------------------------------------
  221. Bool StealthUpdate::allowedToStealth( Object *stealthOwner ) const
  222. {
  223. const Object *self = getObject();
  224. const StealthUpdateModuleData *data = getStealthUpdateModuleData();
  225. UnsignedInt now = TheGameLogic->getFrame();
  226. UnsignedInt flags = data->m_stealthLevel;
  227. if( self != stealthOwner )
  228. {
  229. //Extract the rules from the rider's stealthupdate module data instead
  230. //of our own, because the rider determines if the container can stealth or not.
  231. const StealthUpdate *stealthUpdate = stealthOwner->getStealth();
  232. if( stealthUpdate )
  233. {
  234. flags = stealthUpdate->getStealthLevel();
  235. }
  236. }
  237. //With regards to slaves that stealth with us, we need to all be stealthed or not at all. If
  238. //any of the slaves can't stealth, then reveal everyone!
  239. if( self->isKindOf( KINDOF_SPAWNS_ARE_THE_WEAPONS ) )
  240. {
  241. SpawnBehaviorInterface *sbInterface = self->getSpawnBehaviorInterface();
  242. if( sbInterface )
  243. {
  244. if( !sbInterface->areAllSlavesStealthed() )
  245. {
  246. sbInterface->revealSlaves();
  247. return FALSE;
  248. }
  249. }
  250. }
  251. if( flags & STEALTH_NOT_WHILE_ATTACKING && self->getStatusBits().test( OBJECT_STATUS_IS_FIRING_WEAPON ) )
  252. {
  253. //Doesn't stealth while aggressive (includes approaching).
  254. return FALSE;
  255. }
  256. if( flags & STEALTH_NOT_WHILE_USING_ABILITY && self->getStatusBits().test( OBJECT_STATUS_IS_USING_ABILITY ) )
  257. {
  258. //Doesn't stealth while using a special ability (starting with preparation, which takes place after unpacking).
  259. return FALSE;
  260. }
  261. if( flags & STEALTH_ONLY_WITH_BLACK_MARKET && m_nextBlackMarketCheckFrame < now )
  262. {
  263. //randomize timer a little incase we have a whole bunch on the same frame.
  264. m_nextBlackMarketCheckFrame += data->m_blackMarketCheckFrames + GameLogicRandomValue( 0, 10 );
  265. //If we can't find an active black market, then we can't stealth.
  266. Bool blackMarket = FALSE;
  267. self->getControllingPlayer()->iterateObjects( isBlackMarket, &blackMarket );
  268. if( !blackMarket )
  269. {
  270. return FALSE;
  271. }
  272. }
  273. if( !stealthOwner->getStatusBits().test( OBJECT_STATUS_CAN_STEALTH ) )
  274. {
  275. return FALSE;
  276. }
  277. if( flags & STEALTH_NOT_WHILE_TAKING_DAMAGE && self->getBodyModule()->getLastDamageTimestamp() >= now - 1 )
  278. {
  279. //Only if it's not healing damage.
  280. if( self->getBodyModule()->getLastDamageInfo()->in.m_damageType != DAMAGE_HEALING )
  281. {
  282. //Can't stealth if we just took damage in the last frame or two.
  283. if( self->getBodyModule()->getLastDamageTimestamp() != 0xffffffff )
  284. {
  285. //But it's initialized to 0xffffffff so we don't think we took damage on the first frame.
  286. return FALSE;
  287. }
  288. }
  289. }
  290. //We need all required status or else we fail
  291. // If we have any requirements
  292. if( data->m_requiredStatus.any() && !self->getStatusBits().testForAll( data->m_requiredStatus ) )
  293. return FALSE;
  294. //If we have any forbidden statii, then fail
  295. if( self->getStatusBits().testForAny( data->m_forbiddenStatus ) )
  296. return FALSE;
  297. //Do a quick preliminary test to see if we are restricted by firing particular weapons and we fired a shot last frame or this frame.
  298. if( flags & STEALTH_NOT_WHILE_FIRING_WEAPON && self->getStatusBits().test( OBJECT_STATUS_IS_FIRING_WEAPON ) )
  299. {
  300. if( (flags & STEALTH_NOT_WHILE_FIRING_WEAPON) == STEALTH_NOT_WHILE_FIRING_WEAPON )
  301. {
  302. //Not allowed to stealth while firing ANY weapon!
  303. return FALSE;
  304. }
  305. //Now do weapon specific checks.
  306. Weapon *weapon;
  307. UnsignedInt lastFrame = TheGameLogic->getFrame() - 1;
  308. if( flags & STEALTH_NOT_WHILE_FIRING_PRIMARY )
  309. {
  310. //Check primary weapon status
  311. weapon = self->getWeaponInWeaponSlot( PRIMARY_WEAPON );
  312. if( weapon && weapon->getLastShotFrame() >= lastFrame )
  313. {
  314. return FALSE;
  315. }
  316. }
  317. if( flags & STEALTH_NOT_WHILE_FIRING_SECONDARY )
  318. {
  319. //Check secondary weapon status
  320. weapon = self->getWeaponInWeaponSlot( SECONDARY_WEAPON );
  321. if( weapon && weapon->getLastShotFrame() >= lastFrame )
  322. {
  323. return FALSE;
  324. }
  325. }
  326. if( flags & STEALTH_NOT_WHILE_FIRING_TERTIARY )
  327. {
  328. //Check tertiary weapon status
  329. weapon = self->getWeaponInWeaponSlot( TERTIARY_WEAPON );
  330. if( weapon && weapon->getLastShotFrame() >= lastFrame )
  331. {
  332. return FALSE;
  333. }
  334. }
  335. }
  336. const Object *containedBy = self->getContainedBy();
  337. if( containedBy )
  338. {
  339. ContainModuleInterface *contain = containedBy->getContain();
  340. if( contain && !contain->isGarrisonable() )
  341. {
  342. return FALSE;
  343. }
  344. }
  345. //new past-alpha feature, grr...
  346. if( flags & STEALTH_NOT_WHILE_RIDERS_ATTACKING )
  347. {
  348. ContainModuleInterface *myContain = self->getContain();
  349. if ( myContain && myContain->isPassengerAllowedToFire() )
  350. {
  351. if ( myContain->isAnyRiderAttacking() )
  352. return FALSE;
  353. }
  354. }
  355. const PhysicsBehavior *physics = self->getPhysics();
  356. if ((flags & STEALTH_NOT_WHILE_MOVING) && physics != NULL &&
  357. physics->getVelocityMagnitude() > getStealthUpdateModuleData()->m_stealthSpeed)
  358. return FALSE;
  359. if( self->testScriptStatusBit(OBJECT_STATUS_SCRIPT_UNSTEALTHED))
  360. {
  361. //We can't stealth because a script disabled this ability for this object!
  362. return FALSE;
  363. }
  364. return TRUE;
  365. }
  366. //---------------------------------------------------------------------------
  367. //-------------------------------------------------------------------------------------------------
  368. void StealthUpdate::hintDetectableWhileUnstealthed()
  369. {
  370. Object *self = getObject();
  371. const StealthUpdateModuleData *md = getStealthUpdateModuleData();
  372. if( self && md->m_hintDetectableStates.testForAny( self->getStatusBits() ) )
  373. {
  374. if ( self->getControllingPlayer() == ThePlayerList->getLocalPlayer() )
  375. {
  376. Drawable *selfDraw = self->getDrawable();
  377. if ( selfDraw )
  378. selfDraw->setSecondMaterialPassOpacity( 1.0f );
  379. }
  380. }
  381. }
  382. //-------------------------------------------------------------------------------
  383. Real StealthUpdate::getFriendlyOpacity() const
  384. {
  385. return getStealthUpdateModuleData()->m_friendlyOpacityMin;
  386. }
  387. //=============================================================================
  388. // indicate how the given unit is "stealthed" with respect to a given player.
  389. StealthLookType StealthUpdate::calcStealthedStatusForPlayer(const Object* obj, const Player* player)
  390. {
  391. /*
  392. for stealthy things, there are these distinct "logical" states:
  393. -- not stealthed at all (ie, totally visible)
  394. -- stealthed
  395. -- stealthed-but-detected
  396. and the following visual states:
  397. -- normal (n)
  398. -- invisible (i)
  399. -- stealthed-but-visible-to-friendly-folks (sv)
  400. -- stealthed-but-visible-to-everyone-due-to-being-detected (sd)
  401. Let's be ubergeeks and make a matrix of the possibilities:
  402. Ally Nonally
  403. normal: (n) (n)
  404. stealthed: (sv) (i)
  405. detected: (sd) (sd)
  406. Or, to put it another way:
  407. If normal, you always appear normal.
  408. If stealthed (and not detected), you appear as (sv) to allies and (i) to others.
  409. If detected, you always appears as (sd).
  410. Sorry, there is one more condition, stealthed, but visible to friendly folks, YET detected
  411. In this state we render outselves visible and we ovlerlay the detection effect as a warning
  412. we'll call this STEALTHLOOK_VISIBLE_FRIENDLY_DETECTED
  413. */
  414. if (obj->isEffectivelyDead())
  415. return STEALTHLOOK_NONE; // making sure he turns visible when he dies
  416. if( obj->getStatusBits().test( OBJECT_STATUS_STEALTHED ) )
  417. {
  418. const Team* team = obj->getTeam();
  419. Relationship r = team ? team->getRelationship(player->getDefaultTeam()) : NEUTRAL;
  420. if( !player->isPlayerActive() )
  421. {
  422. //Observer players are friends to everyone!
  423. r = ALLIES;
  424. }
  425. // srj sez: disguised stuff doesn't work well when combined with the normal "detected" stuff.
  426. // so special case it here.
  427. if (canDisguise())
  428. {
  429. if (r != ALLIES && isDisguised())
  430. return STEALTHLOOK_DISGUISED_ENEMY;
  431. else
  432. return STEALTHLOOK_NONE;
  433. }
  434. if( obj->getStatusBits().test( OBJECT_STATUS_DETECTED ) ) // we're detected.
  435. {
  436. if (r == ALLIES)// if we're friendly to the given player, detection DOES matter though.
  437. return STEALTHLOOK_VISIBLE_FRIENDLY_DETECTED;
  438. else
  439. return STEALTHLOOK_VISIBLE_DETECTED;
  440. }
  441. else
  442. {
  443. if (r == ALLIES)
  444. {
  445. // if we're friendly to the given player, detection doesn't matter.
  446. return STEALTHLOOK_VISIBLE_FRIENDLY;
  447. }
  448. else
  449. {
  450. // srj sez: disguised stuff doesn't work well when combined with the normal "detected" stuff.
  451. // so special case it above.
  452. // if( getStealthUpdateModuleData()->m_teamDisguised )
  453. // {
  454. // return STEALTHLOOK_DISGUISED_ENEMY;
  455. // }
  456. // we're effectively hidden.
  457. return STEALTHLOOK_INVISIBLE;
  458. }
  459. }
  460. }
  461. else
  462. {
  463. return STEALTHLOOK_NONE;
  464. }
  465. }
  466. //-------------------------------------------------------------------------------------------------
  467. Object* StealthUpdate::calcStealthOwner()
  468. {
  469. const StealthUpdateModuleData *data = getStealthUpdateModuleData();
  470. //If we are going to use the rider for stealth rules, then we need to separate the
  471. //rider and the container. The rider will determine if the container is stealthed or
  472. //not.
  473. if( data->m_useRiderStealth )
  474. {
  475. //We're actually going to logically check the rider as the stealth owner, but the
  476. //stealth effects will go on the container.
  477. ContainModuleInterface *contain = getObject()->getContain();
  478. if( contain )
  479. {
  480. const ContainedItemsList *riderList = contain->getContainedItemsList();
  481. ContainedItemsList::const_iterator riderIterator;
  482. riderIterator = riderList->begin();
  483. if( riderIterator != riderList->end() )
  484. {
  485. //Return this rider!
  486. return *riderIterator;
  487. }
  488. }
  489. }
  490. //Not applicable, return ourself.
  491. return getObject();
  492. }
  493. //-------------------------------------------------------------------------------------------------
  494. //-------------------------------------------------------------------------------------------------
  495. UpdateSleepTime StealthUpdate::calcSleepTime() const
  496. {
  497. return m_enabled ? UPDATE_SLEEP_NONE : UPDATE_SLEEP_FOREVER;
  498. }
  499. //-------------------------------------------------------------------------------------------------
  500. /** The update callback. */
  501. //-------------------------------------------------------------------------------------------------
  502. UpdateSleepTime StealthUpdate::update( void )
  503. {
  504. // restore disguise if we need to from a game load
  505. if( m_xferRestoreDisguise == TRUE )
  506. {
  507. Drawable *draw = getObject()->getDrawable();
  508. Bool wasHidden = FALSE;
  509. // hack! if drawable was hidden (such as if we're inside a container) we must keep that state
  510. if( draw && draw->isDrawableEffectivelyHidden() )
  511. wasHidden = TRUE;
  512. // do the change (we get a new drawable from this)
  513. changeVisualDisguise();
  514. // restore hidden state in the new drawable
  515. draw = getObject()->getDrawable();
  516. if( wasHidden && draw )
  517. draw->setDrawableHidden( TRUE );
  518. } // end if
  519. Object *self = getObject();
  520. Object *stealthOwner = calcStealthOwner();
  521. UnsignedInt stealthDelay;
  522. if( self == stealthOwner )
  523. {
  524. const StealthUpdateModuleData *data = getStealthUpdateModuleData();
  525. stealthDelay = data->m_stealthDelay;
  526. }
  527. else
  528. {
  529. //Extract the rules from the rider's stealthupdate module data instead
  530. //of our own, because the rider determines if the container can stealth or not.
  531. const StealthUpdate *stealthUpdate = stealthOwner->getStealth();
  532. if( stealthUpdate )
  533. {
  534. stealthDelay = stealthUpdate->getStealthDelay();
  535. }
  536. }
  537. UnsignedInt now = TheGameLogic->getFrame();
  538. /// @todo srj -- improve sleeping behavior. we currently just sleep when not enabled,
  539. // and demand every-frame attention when enabled. this could probably be smartened.
  540. if( !m_enabled )
  541. {
  542. return calcSleepTime();
  543. }
  544. Drawable* draw = self->getDrawable();
  545. if( draw )
  546. {
  547. const StealthUpdateModuleData *data = getStealthUpdateModuleData();
  548. //Are we disguise transitioning (either gaining or losing disguise look?)
  549. /** @todo srj -- evil hack here... this whole heat-vision thing is fucked.
  550. don't want it on mines but no good way to do that. hack for now. */
  551. if (self->isKindOf(KINDOF_MINE))
  552. {
  553. // special case for mines
  554. draw->setEffectiveOpacity( 0.0f, 0.0f );
  555. }
  556. else if( m_disguiseTransitionFrames )
  557. {
  558. m_disguiseTransitionFrames--;
  559. Real factor;
  560. if( m_transitioningToDisguise )
  561. {
  562. factor = 1.0f - ( (Real)m_disguiseTransitionFrames / (Real)data->m_disguiseTransitionFrames );
  563. }
  564. else
  565. {
  566. factor = 1.0f - ( (Real)m_disguiseTransitionFrames / (Real)data->m_disguiseRevealTransitionFrames );
  567. }
  568. if( factor >= 0.5f && !m_disguiseHalfpointReached )
  569. {
  570. //Switch models at the halfway point
  571. changeVisualDisguise();
  572. m_disguiseHalfpointReached = true;
  573. }
  574. //Opacity ranges from full to none at midpoint and full again at the end
  575. Real opacity = fabs( 1.0f - (factor * 2.0f) );
  576. Real overrideOpacity = opacity < 1.0f ? 0.0f : 1.0f;
  577. draw->setEffectiveOpacity( opacity, overrideOpacity );
  578. if( !m_disguiseTransitionFrames && !m_transitioningToDisguise )
  579. {
  580. //We're finished removing disguise so turn off stealth update.
  581. m_enabled = false;
  582. self->clearStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_STEALTHED ) );
  583. self->clearStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_DETECTED ) );
  584. return calcSleepTime();
  585. }
  586. }
  587. else
  588. {
  589. draw->setEffectiveOpacity( 0.5f + ( Sin( m_pulsePhase ) * 0.5f ) );
  590. // between one half and full opacity
  591. m_pulsePhase += m_pulsePhaseRate;
  592. }
  593. }
  594. /// @todo srj -- do we need to do this EVERY frame?
  595. Real revealDistance = getRevealDistanceFromTarget();
  596. if( revealDistance > 0.0f )
  597. {
  598. AIUpdateInterface *ai = self->getAI();
  599. if( ai )
  600. {
  601. Object *target = ai->getCurrentVictim();
  602. if( target )
  603. {
  604. Real distSqrd = ThePartitionManager->getDistanceSquared( self, target, FROM_CENTER_2D );
  605. if( distSqrd <= revealDistance * revealDistance )
  606. {
  607. //We're close enough to reveal ourselves
  608. markAsDetected();
  609. return calcSleepTime();
  610. }
  611. }
  612. }
  613. }
  614. //Deal with temporary stealth.
  615. if( m_framesGranted > 0 )
  616. {
  617. m_framesGranted--;
  618. //If the last AI command given was by the player... then LOSE the stealth now!
  619. AIUpdateInterface *ai = self->getAI();
  620. if( ai )
  621. {
  622. if( ai->getLastCommandSource() == CMD_FROM_PLAYER )
  623. {
  624. //No exploits :)
  625. receiveGrant( FALSE );
  626. }
  627. }
  628. if( m_framesGranted == 0 )
  629. {
  630. //Disable it now that it has officially expired.
  631. receiveGrant( FALSE );
  632. }
  633. }
  634. if( allowedToStealth( stealthOwner ) )
  635. {
  636. // If I can stealth, don't attempt to Stealth until the timer is zero.
  637. if( m_stealthAllowedFrame > now )
  638. {
  639. return calcSleepTime();
  640. }
  641. // If we haven't stealthed yet( still destealthed ), play stealthOn here
  642. //if ( ( self->getStatusBits() && OBJECT_STATUS_STEALTHED ) == 0 )
  643. if( !self->getStatusBits().test( OBJECT_STATUS_STEALTHED ) )
  644. {
  645. AudioEventRTS soundEvent = *self->getTemplate()->getSoundStealthOn();
  646. soundEvent.setObjectID(self->getID());
  647. TheAudio->addAudioEvent( &soundEvent );
  648. }
  649. // The timer is zero, so if we aren't stealthed, do so now!
  650. self->setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_STEALTHED ) );
  651. }
  652. else
  653. {
  654. m_stealthAllowedFrame = now + stealthDelay;
  655. // if you are destealthing on your own free will, play sound for all to hear
  656. if( self->getStatusBits().test( OBJECT_STATUS_STEALTHED ) )
  657. {
  658. AudioEventRTS soundEvent = *self->getTemplate()->getSoundStealthOn();
  659. soundEvent.setObjectID(self->getID());
  660. TheAudio->addAudioEvent( &soundEvent );
  661. }
  662. self->clearStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_STEALTHED ) );
  663. hintDetectableWhileUnstealthed();
  664. }
  665. Bool detectedStatusChangedThisFrame = FALSE;
  666. if (m_detectionExpiresFrame > now)
  667. {
  668. // if this is the first time being detected, play stealth off sound
  669. if( !self->getStatusBits().test( OBJECT_STATUS_DETECTED ) )
  670. {
  671. detectedStatusChangedThisFrame = TRUE;
  672. AudioEventRTS soundEvent = *self->getTemplate()->getSoundStealthOff();
  673. soundEvent.setObjectID(self->getID());
  674. TheAudio->addAudioEvent( &soundEvent );
  675. }
  676. self->setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_DETECTED ) );
  677. }
  678. else
  679. {
  680. // if this is the first time your clearing the detected status, play the stealth on sound
  681. if( self->getStatusBits().test( OBJECT_STATUS_DETECTED ) )
  682. {
  683. detectedStatusChangedThisFrame = TRUE;
  684. //Only play sound effect if the selected object is controllable.
  685. if( self->isLocallyControlled() )
  686. {
  687. AudioEventRTS soundEvent = *self->getTemplate()->getSoundStealthOn();
  688. soundEvent.setObjectID(self->getID());
  689. TheAudio->addAudioEvent( &soundEvent );
  690. }
  691. }
  692. self->clearStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_DETECTED ) );
  693. }
  694. if ( detectedStatusChangedThisFrame )
  695. {
  696. //do the trick where we tell our container to recals his apparent controlling player
  697. //since I may have just become either detected or undetected
  698. if ( self->isContained() )
  699. {
  700. Object *container = self->getContainedBy();
  701. if ( container )
  702. {
  703. ContainModuleInterface *contain = container->getContain();
  704. if( contain && contain->isGarrisonable() )
  705. {
  706. contain->recalcApparentControllingPlayer();
  707. }
  708. }
  709. }
  710. }
  711. if (draw)
  712. {
  713. StealthLookType stealthLook = calcStealthedStatusForPlayer( self, ThePlayerList->getLocalPlayer() );
  714. draw->setStealthLook( stealthLook );
  715. }
  716. return calcSleepTime();
  717. }
  718. //-------------------------------------------------------------------------------------------------
  719. void setWakeupIfInRange( Object *obj, void *userData)
  720. {
  721. Object *victim = (Object *)userData;
  722. AIUpdateInterface *ai = obj->getAI();
  723. if (!ai) {
  724. return;
  725. }
  726. Real vision = obj->getVisionRange();
  727. Coord3D srcpos = *obj->getPosition();
  728. Coord3D dstpos = *victim->getPosition();
  729. srcpos.sub(&dstpos);
  730. if (srcpos.length() > vision)
  731. return;
  732. ai->wakeUpAndAttemptToTarget();
  733. // if( obj->isKindOf( KINDOF_SELECTABLE ) && ( obj->isAbleToAttack() || !obj->isKindOf( KINDOF_STRUCTURE ) )) {
  734. // Drawable *draw = obj->getDrawable();
  735. // if( draw ) {
  736. // draw->setEmoticon( "Emoticon_Alarm", 5000 );
  737. // }
  738. // }
  739. }
  740. //-------------------------------------------------------------------------------------------------
  741. void StealthUpdate::markAsDetected(UnsignedInt numFrames)
  742. {
  743. Object *self = getObject();
  744. Object *stealthOwner = calcStealthOwner();
  745. UnsignedInt stealthDelay, orderIdlesToAttack;
  746. if( self == stealthOwner )
  747. {
  748. const StealthUpdateModuleData *data = getStealthUpdateModuleData();
  749. //Use the standard module data information (because we stealth ourself)
  750. stealthDelay = data->m_stealthDelay;
  751. orderIdlesToAttack = data->m_orderIdleEnemiesToAttackMeUponReveal;
  752. }
  753. else
  754. {
  755. //Extract the rules from the rider's stealthupdate module data instead
  756. //of our own, because the rider determines if the container can stealth or not.
  757. const StealthUpdate *stealthUpdate = stealthOwner->getStealth();
  758. if( stealthUpdate )
  759. {
  760. stealthDelay = stealthUpdate->getStealthDelay();
  761. orderIdlesToAttack = stealthUpdate->getOrderIdleEnemiesToAttackMeUponReveal();
  762. }
  763. }
  764. Player *thisPlayer = self->getControllingPlayer();
  765. //If we are disguised, remove the disguise permanently!
  766. if( isDisguised() )
  767. {
  768. disguiseAsObject( NULL );
  769. }
  770. UnsignedInt now = TheGameLogic->getFrame();
  771. if( !numFrames )
  772. {
  773. //Kris:
  774. //If numFrames is zero (the default value), use the stealth delay specified in the ini file.
  775. m_detectionExpiresFrame = now + stealthDelay;
  776. }
  777. else if ( m_detectionExpiresFrame < now + numFrames )
  778. {
  779. m_detectionExpiresFrame = now + numFrames;
  780. }
  781. if( orderIdlesToAttack )
  782. {
  783. // This can't be a partitionmanager thing, because we need to know which objects can see
  784. // us. Therefore, walk the play list, and for each player that considers us an enemy,
  785. // check if any of their units can see us.
  786. Int numPlayers = ThePlayerList->getPlayerCount();
  787. for (Int n = 0; n < numPlayers; ++n)
  788. {
  789. Player *player = ThePlayerList->getNthPlayer(n);
  790. if (!player)
  791. continue;
  792. if (player->getRelationship(thisPlayer->getDefaultTeam()) != ENEMIES)
  793. continue;
  794. player->iterateObjects(setWakeupIfInRange, self);
  795. }
  796. }
  797. }
  798. //-------------------------------------------------------------------------------------------------
  799. void StealthUpdate::disguiseAsObject( const Object *target )
  800. {
  801. Object *self = getObject();
  802. const StealthUpdateModuleData *data = getStealthUpdateModuleData();
  803. if( target && target->getControllingPlayer() )
  804. {
  805. StealthUpdate* stealth = target->getStealth();
  806. if( stealth && stealth->getDisguisedTemplate() )
  807. {
  808. m_disguiseAsTemplate = stealth->getDisguisedTemplate();
  809. m_disguiseAsPlayerIndex = stealth->getDisguisedPlayerIndex();
  810. }
  811. else
  812. {
  813. m_disguiseAsTemplate = target->getTemplate();
  814. m_disguiseAsPlayerIndex = target->getControllingPlayer()->getPlayerIndex();
  815. }
  816. m_enabled = true;
  817. m_transitioningToDisguise = true; //Means we are gaining disguise over time.
  818. m_disguiseTransitionFrames = data->m_disguiseTransitionFrames;
  819. m_disguiseHalfpointReached = false;
  820. //Wake up so I can process!
  821. setWakeFrame( getObject(), UPDATE_SLEEP_NONE );
  822. }
  823. else if( m_disguised )
  824. {
  825. m_disguiseAsTemplate = NULL;
  826. m_disguiseAsPlayerIndex = 0;
  827. m_disguiseTransitionFrames = data->m_disguiseRevealTransitionFrames;
  828. m_transitioningToDisguise = false; //Means we are losing the disguise over time.
  829. m_disguiseHalfpointReached = false;
  830. }
  831. Drawable *draw = self->getDrawable();
  832. if( draw && draw->isSelected() )
  833. {
  834. TheControlBar->markUIDirty();
  835. }
  836. }
  837. //-------------------------------------------------------------------------------------------------
  838. void StealthUpdate::changeVisualDisguise()
  839. {
  840. Object *self = getObject();
  841. const StealthUpdateModuleData *data = getStealthUpdateModuleData();
  842. Drawable *draw = self->getDrawable();
  843. // We need to maintain our selection across the un/disguise, so pull selected out here.
  844. Bool selected = draw->isSelected();
  845. if( m_disguiseAsTemplate )
  846. {
  847. Player *player = ThePlayerList->getNthPlayer( m_disguiseAsPlayerIndex );
  848. ModelConditionFlags flags = draw->getModelConditionFlags();
  849. //Get rid of the old instance!
  850. TheGameClient->destroyDrawable( draw );
  851. draw = TheThingFactory->newDrawable( m_disguiseAsTemplate );
  852. if( draw )
  853. {
  854. TheGameLogic->bindObjectAndDrawable(self, draw);
  855. draw->setPosition( self->getPosition() );
  856. draw->setOrientation( self->getOrientation() );
  857. draw->setModelConditionFlags( flags );
  858. draw->updateDrawable();
  859. self->getPhysics()->resetDynamicPhysics();
  860. if( selected )
  861. {
  862. TheInGameUI->selectDrawable( draw );
  863. }
  864. Player *clientPlayer = ThePlayerList->getLocalPlayer();
  865. if( self->getControllingPlayer()->getRelationship( clientPlayer->getDefaultTeam() ) != ALLIES && clientPlayer->isPlayerActive() )
  866. {
  867. //Neutrals and enemies will see this disguised unit as the team it's disguised as.
  868. if (TheGlobalData->m_timeOfDay == TIME_OF_DAY_NIGHT)
  869. draw->setIndicatorColor( player->getPlayerNightColor() );
  870. else
  871. draw->setIndicatorColor( player->getPlayerColor() );
  872. }
  873. else
  874. {
  875. //If it's on our team or our ally's team, then show it's true colors.
  876. if (TheGlobalData->m_timeOfDay == TIME_OF_DAY_NIGHT)
  877. draw->setIndicatorColor( self->getNightIndicatorColor() );
  878. else
  879. draw->setIndicatorColor( self->getIndicatorColor() );
  880. }
  881. }
  882. //Play a disguise sound!
  883. AudioEventRTS sound = *self->getTemplate()->getPerUnitSound( "DisguiseStarted" );
  884. sound.setObjectID( self->getID() );
  885. TheAudio->addAudioEvent( &sound );
  886. FXList::doFXPos( data->m_disguiseFX, self->getPosition() );
  887. m_disguised = true;
  888. self->setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_DISGUISED ) );
  889. self->setModelConditionState( MODELCONDITION_DISGUISED );
  890. //33) Did the player ever build a "disguisable" unit and never used the disguise ability?
  891. self->getControllingPlayer()->getAcademyStats()->recordVehicleDisguised();
  892. }
  893. else if( m_disguiseAsPlayerIndex != -1 )
  894. {
  895. m_disguiseAsPlayerIndex = -1;
  896. ModelConditionFlags flags = draw->getModelConditionFlags();
  897. //Get rid of the old instance!
  898. TheGameClient->destroyDrawable( draw );
  899. const ThingTemplate *tTemplate = self->getTemplate();
  900. TheThingFactory->newDrawable( tTemplate );
  901. if( draw )
  902. {
  903. TheGameLogic->bindObjectAndDrawable(self, draw);
  904. draw->setPosition( self->getPosition() );
  905. draw->setOrientation( self->getOrientation() );
  906. draw->setModelConditionFlags( flags );
  907. draw->updateDrawable();
  908. self->getPhysics()->resetDynamicPhysics();
  909. if (TheGlobalData->m_timeOfDay == TIME_OF_DAY_NIGHT)
  910. draw->setIndicatorColor( self->getNightIndicatorColor() );
  911. else
  912. draw->setIndicatorColor( self->getIndicatorColor() );
  913. if( selected )
  914. {
  915. TheInGameUI->selectDrawable( draw );
  916. }
  917. //UGH!
  918. //A concrete example is the bomb truck. Different payloads are displayed based on which upgrades have been
  919. //made. When the bomb truck disguises as something else, these subobjects are lost because the vector is
  920. //stored in W3DDrawModule. When we revert back to the original bomb truck, we call this function to
  921. //recalculate those upgraded subobjects.
  922. self->forceRefreshSubObjectUpgradeStatus();
  923. }
  924. Bool successfulReveal = false;
  925. AIUpdateInterface *ai = self->getAI();
  926. if( ai )
  927. {
  928. Object *currTarget = ai->getCurrentVictim();
  929. if( currTarget )
  930. {
  931. successfulReveal = true;
  932. }
  933. }
  934. //Play a reveal sound!
  935. AudioEventRTS sound;
  936. if( successfulReveal )
  937. {
  938. sound = *self->getTemplate()->getPerUnitSound( "DisguiseRevealedSuccess" );
  939. }
  940. else
  941. {
  942. sound = *self->getTemplate()->getPerUnitSound( "DisguiseRevealedFailure" );
  943. }
  944. sound.setObjectID( self->getID() );
  945. TheAudio->addAudioEvent( &sound );
  946. FXList::doFXPos( data->m_disguiseRevealFX, self->getPosition() );
  947. m_disguised = false;
  948. self->clearStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_DISGUISED ) );
  949. self->clearModelConditionState( MODELCONDITION_DISGUISED );
  950. }
  951. //Reset the radar (determines color on add)
  952. TheRadar->removeObject( self );
  953. TheRadar->addObject( self );
  954. // couldn't possibly need to restore a disguise now :)
  955. m_xferRestoreDisguise = FALSE;
  956. }
  957. // ------------------------------------------------------------------------------------------------
  958. /** CRC */
  959. // ------------------------------------------------------------------------------------------------
  960. void StealthUpdate::crc( Xfer *xfer )
  961. {
  962. // extend base class
  963. UpdateModule::crc( xfer );
  964. } // end crc
  965. // ------------------------------------------------------------------------------------------------
  966. /** Xfer method
  967. * Version Info:
  968. * 1: Initial version */
  969. // ------------------------------------------------------------------------------------------------
  970. void StealthUpdate::xfer( Xfer *xfer )
  971. {
  972. // version
  973. XferVersion currentVersion = 2;
  974. XferVersion version = currentVersion;
  975. xfer->xferVersion( &version, currentVersion );
  976. // extend base class
  977. UpdateModule::xfer( xfer );
  978. // stealth allowed frame
  979. xfer->xferUnsignedInt( &m_stealthAllowedFrame );
  980. // detection expires frame
  981. xfer->xferUnsignedInt( &m_detectionExpiresFrame );
  982. // enabled
  983. xfer->xferBool( &m_enabled );
  984. // pulse phase rate
  985. xfer->xferReal( &m_pulsePhaseRate );
  986. // pulse phase
  987. xfer->xferReal( &m_pulsePhase );
  988. // disguise as player index
  989. xfer->xferInt( &m_disguiseAsPlayerIndex );
  990. // disguise as template
  991. AsciiString name = m_disguiseAsTemplate ? m_disguiseAsTemplate->getName() : AsciiString::TheEmptyString;
  992. xfer->xferAsciiString( &name );
  993. if( xfer->getXferMode() == XFER_LOAD )
  994. {
  995. m_disguiseAsTemplate = NULL;
  996. if( name.isEmpty() == FALSE )
  997. {
  998. m_disguiseAsTemplate = TheThingFactory->findTemplate( name );
  999. if( m_disguiseAsTemplate == NULL )
  1000. {
  1001. DEBUG_CRASH(( "StealthUpdate::xfer - Unknown template '%s'\n", name.str() ));
  1002. throw SC_INVALID_DATA;
  1003. } // end if
  1004. } // end if
  1005. } // end if
  1006. // disguise transition frames
  1007. xfer->xferUnsignedInt( &m_disguiseTransitionFrames );
  1008. // disguise halfpoint reached
  1009. xfer->xferBool( &m_disguiseHalfpointReached );
  1010. // transitioning to disguise
  1011. xfer->xferBool( &m_transitioningToDisguise );
  1012. // disguised
  1013. xfer->xferBool( &m_disguised );
  1014. if( version >= 2 )
  1015. {
  1016. xfer->xferUnsignedInt( &m_framesGranted );
  1017. }
  1018. } // end xfer
  1019. // ------------------------------------------------------------------------------------------------
  1020. /** Load post process */
  1021. // ------------------------------------------------------------------------------------------------
  1022. void StealthUpdate::loadPostProcess( void )
  1023. {
  1024. // extend base class
  1025. UpdateModule::loadPostProcess();
  1026. //
  1027. // we will need to restore our disguise when the game is ready to run ... NOTE that we
  1028. // cannot restore it here because if we called changeVisualDisguise() it would
  1029. // destroy our drawable and create new stuff. The destruction of a drawable during
  1030. // a load is *very* bad ... it has a snapshot instance in the game state, other things
  1031. // may be pointing at it etc.
  1032. //
  1033. if( isDisguised() )
  1034. m_xferRestoreDisguise = TRUE;
  1035. } // end loadPostProcess