ActiveBody.cpp 43 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312
  1. /*
  2. ** Command & Conquer Generals(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: ActiveBody.cpp ///////////////////////////////////////////////////////////////////////////
  24. // Author: Colin Day, November 2001
  25. // Desc: Active bodies have health, they can die and are affected by health
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #include "Common/BitFlagsIO.h"
  30. #include "Common/CRCDebug.h"
  31. #include "Common/DamageFX.h"
  32. #include "Common/Player.h"
  33. #include "Common/GameState.h"
  34. #include "Common/GlobalData.h"
  35. #include "Common/PlayerList.h"
  36. #include "Common/Team.h"
  37. #include "Common/Thing.h"
  38. #include "Common/ThingTemplate.h"
  39. #include "Common/Xfer.h"
  40. #include "GameClient/ControlBar.h"
  41. #include "GameClient/Drawable.h"
  42. #include "GameClient/InGameUI.h"
  43. #include "GameClient/ParticleSys.h"
  44. #include "GameLogic/AIPathfind.h"
  45. #include "GameLogic/Armor.h"
  46. #include "GameLogic/GameLogic.h"
  47. #include "GameLogic/Object.h"
  48. #include "GameLogic/Damage.h"
  49. #include "GameLogic/TerrainLogic.h"
  50. #include "GameLogic/Module/AIUpdate.h"
  51. #include "GameLogic/Module/ActiveBody.h"
  52. #include "GameLogic/Module/BridgeBehavior.h"
  53. #include "GameLogic/Module/DamageModule.h"
  54. #include "GameLogic/Module/DieModule.h"
  55. #ifdef _INTERNAL
  56. // for occasional debugging...
  57. //#pragma optimize("", off)
  58. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  59. #endif
  60. #define YELLOW_DAMAGE_PERCENT (0.25f)
  61. // FORWARD REFERENCES /////////////////////////////////////////////////////////////////////////////
  62. // ------------------------------------------------------------------------------------------------
  63. /** Body particle systems are particle systems that are automatically created and attached
  64. * to an object as the damage state changes for that object. We keep a list of these
  65. * so that when we transition from one state to another we can kill any old particle
  66. * systems that we need to before we create new ones */
  67. // ------------------------------------------------------------------------------------------------
  68. class BodyParticleSystem : public MemoryPoolObject
  69. {
  70. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( BodyParticleSystem, "BodyParticleSystem" )
  71. public:
  72. ParticleSystemID m_particleSystemID; ///< the particle system ID
  73. BodyParticleSystem *m_next; ///< next particle system in this body module
  74. };
  75. // ------------------------------------------------------------------------------------------------
  76. // ------------------------------------------------------------------------------------------------
  77. BodyParticleSystem::~BodyParticleSystem( void )
  78. {
  79. } // end ~BodyParticleSystem
  80. ///////////////////////////////////////////////////////////////////////////////////////////////////
  81. // PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
  82. ///////////////////////////////////////////////////////////////////////////////////////////////////
  83. //-------------------------------------------------------------------------------------------------
  84. // ------------------------------------------------------------------------------------------------
  85. static BodyDamageType calcDamageState(Real health, Real maxHealth)
  86. {
  87. if (!TheGlobalData)
  88. return BODY_PRISTINE;
  89. Real ratio = health / maxHealth;
  90. if (ratio > TheGlobalData->m_unitDamagedThresh)
  91. {
  92. return BODY_PRISTINE;
  93. }
  94. else if (ratio > TheGlobalData->m_unitReallyDamagedThresh)
  95. {
  96. return BODY_DAMAGED;
  97. }
  98. else if (ratio > 0.0f)
  99. {
  100. return BODY_REALLYDAMAGED;
  101. }
  102. else
  103. {
  104. return BODY_RUBBLE;
  105. }
  106. }
  107. //-------------------------------------------------------------------------------------------------
  108. //-------------------------------------------------------------------------------------------------
  109. ActiveBodyModuleData::ActiveBodyModuleData()
  110. {
  111. m_maxHealth = 0;
  112. m_initialHealth = 0;
  113. }
  114. //-------------------------------------------------------------------------------------------------
  115. //-------------------------------------------------------------------------------------------------
  116. void ActiveBodyModuleData::buildFieldParse(MultiIniFieldParse& p)
  117. {
  118. ModuleData::buildFieldParse(p);
  119. static const FieldParse dataFieldParse[] =
  120. {
  121. { "MaxHealth", INI::parseReal, NULL, offsetof( ActiveBodyModuleData, m_maxHealth ) },
  122. { "InitialHealth", INI::parseReal, NULL, offsetof( ActiveBodyModuleData, m_initialHealth ) },
  123. { 0, 0, 0, 0 }
  124. };
  125. p.add(dataFieldParse);
  126. }
  127. //-------------------------------------------------------------------------------------------------
  128. //-------------------------------------------------------------------------------------------------
  129. ActiveBody::ActiveBody( Thing *thing, const ModuleData* moduleData ) :
  130. BodyModule(thing, moduleData),
  131. m_curDamageFX(NULL),
  132. m_curArmorSet(NULL),
  133. m_frontCrushed(false),
  134. m_backCrushed(false),
  135. m_lastDamageTimestamp(0xffffffff),// So we don't think we just got damaged on the first frame
  136. m_lastHealingTimestamp(0xffffffff),// So we don't think we just got healed on the first frame
  137. m_curDamageState(BODY_PRISTINE),
  138. m_nextDamageFXTime(0),
  139. m_lastDamageFXDone((DamageType)-1),
  140. m_lastDamageCleared(false),
  141. m_particleSystems(NULL),
  142. m_indestructible(false)
  143. {
  144. m_currentHealth = getActiveBodyModuleData()->m_initialHealth;
  145. m_prevHealth = getActiveBodyModuleData()->m_initialHealth;
  146. m_maxHealth = getActiveBodyModuleData()->m_maxHealth;
  147. m_initialHealth = getActiveBodyModuleData()->m_initialHealth;
  148. // force an initially-valid armor setup
  149. validateArmorAndDamageFX();
  150. // start us in the right state
  151. setCorrectDamageState();
  152. }
  153. //-------------------------------------------------------------------------------------------------
  154. //-------------------------------------------------------------------------------------------------
  155. ActiveBody::~ActiveBody( void )
  156. {
  157. }
  158. // ------------------------------------------------------------------------------------------------
  159. // ------------------------------------------------------------------------------------------------
  160. void ActiveBody::onDelete( void )
  161. {
  162. // delete all particle systems
  163. deleteAllParticleSystems();
  164. } // end onDelete
  165. //-------------------------------------------------------------------------------------------------
  166. //-------------------------------------------------------------------------------------------------
  167. void ActiveBody::setCorrectDamageState()
  168. {
  169. m_curDamageState = calcDamageState(m_currentHealth, m_maxHealth);
  170. /// @todo srj -- bleah, this is an icky way to do it. oh well.
  171. if (m_curDamageState == BODY_RUBBLE && getObject()->isKindOf(KINDOF_STRUCTURE))
  172. {
  173. Real rubbleHeight = getObject()->getTemplate()->getStructureRubbleHeight();
  174. if (rubbleHeight <= 0.0f)
  175. rubbleHeight = TheGlobalData->m_defaultStructureRubbleHeight;
  176. /** @todo I had to change this to a Z only version to keep it from disappearing from the
  177. PartitionManager for a frame. That didn't used to happen.
  178. */
  179. getObject()->setGeometryInfoZ(rubbleHeight);
  180. // Have to tell pathfind as well, as rubble pathfinds differently.
  181. TheAI->pathfinder()->removeObjectFromPathfindMap(getObject());
  182. TheAI->pathfinder()->addObjectToPathfindMap(getObject());
  183. // here we make sure nobody collides with us, ever again... //Lorenzen
  184. //THis allows projectiles shot from infantry that are inside rubble to get out of said rubble safely
  185. getObject()->setStatus( OBJECT_STATUS_NO_COLLISIONS );
  186. }
  187. }
  188. //-------------------------------------------------------------------------------------------------
  189. //-------------------------------------------------------------------------------------------------
  190. void ActiveBody::setDamageState( BodyDamageType newState )
  191. {
  192. Real ratio = 1.0f;
  193. if( newState == BODY_PRISTINE )
  194. {
  195. ratio = 1.0f;
  196. }
  197. else if( newState == BODY_DAMAGED )
  198. {
  199. ratio = TheGlobalData->m_unitDamagedThresh;
  200. }
  201. else if( newState == BODY_REALLYDAMAGED )
  202. {
  203. ratio = TheGlobalData->m_unitReallyDamagedThresh;
  204. }
  205. else if( newState == BODY_RUBBLE )
  206. {
  207. ratio = 0.0f;
  208. }
  209. Real desiredHealth = m_maxHealth * ratio - 1;// -1 because < not <= in calcState
  210. desiredHealth = max( desiredHealth, 0.0f );
  211. internalChangeHealth( desiredHealth - m_currentHealth );
  212. setCorrectDamageState();
  213. }
  214. //-------------------------------------------------------------------------------------------------
  215. //-------------------------------------------------------------------------------------------------
  216. void ActiveBody::validateArmorAndDamageFX() const
  217. {
  218. const ArmorTemplateSet* set = getObject()->getTemplate()->findArmorTemplateSet(m_curArmorSetFlags);
  219. DEBUG_ASSERTCRASH(set, ("findArmorSet should never return null"));
  220. if (set && set != m_curArmorSet)
  221. {
  222. if (set->getArmorTemplate())
  223. {
  224. m_curArmor = TheArmorStore->makeArmor(set->getArmorTemplate());
  225. }
  226. else
  227. {
  228. m_curArmor.clear();
  229. }
  230. m_curDamageFX = set->getDamageFX();
  231. m_curArmorSet = set;
  232. }
  233. }
  234. //-------------------------------------------------------------------------------------------------
  235. //-------------------------------------------------------------------------------------------------
  236. Real ActiveBody::estimateDamage( DamageInfoInput& damageInfo ) const
  237. {
  238. validateArmorAndDamageFX();
  239. Real amount = m_curArmor.adjustDamage(damageInfo.m_damageType, damageInfo.m_amount);
  240. return amount;
  241. }
  242. //-------------------------------------------------------------------------------------------------
  243. //-------------------------------------------------------------------------------------------------
  244. void ActiveBody::doDamageFX( const DamageInfo *damageInfo )
  245. {
  246. DamageType damageTypeToUse = damageInfo->in.m_damageType;
  247. if (damageInfo->in.m_damageType == DAMAGE_UNRESISTABLE &&
  248. (damageInfo->in.m_deathType == DEATH_POISONED || damageInfo->in.m_deathType == DEATH_POISONED_BETA))
  249. {
  250. // PoisonedBehavior sends DAMAGE_UNRESISTABLE (rather that DAMAGE_POISON) to avoid reinfection,
  251. // but it prevents us from using the appropriate DamageFX. This is pretty greasy; I should really
  252. // augment DamageInfo to have some "visible damage type" field, but that's too risky at this point.
  253. // oh well, the carnage continues. (srj)
  254. damageTypeToUse = DAMAGE_POISON;
  255. }
  256. if (damageTypeToUse == DAMAGE_RADIATION)
  257. {
  258. Object* obj = getObject();
  259. if ( obj )
  260. {
  261. Drawable *draw = obj->getDrawable();
  262. if ( draw )
  263. {
  264. draw->setTintStatus( TINT_STATUS_IRRADIATED );
  265. // We allow the Tinting Class in Drawable to turn this effect off, which it does every update
  266. }
  267. }
  268. }
  269. if (m_curDamageFX)
  270. {
  271. UnsignedInt now = TheGameLogic->getFrame();
  272. if (damageTypeToUse == m_lastDamageFXDone && m_nextDamageFXTime > now)
  273. return;
  274. Object *source = TheGameLogic->findObjectByID(damageInfo->in.m_sourceID); // might be null, I guess
  275. m_lastDamageFXDone = damageTypeToUse;
  276. m_nextDamageFXTime = now + m_curDamageFX->getDamageFXThrottleTime(damageTypeToUse, source);
  277. m_curDamageFX->doDamageFX(damageTypeToUse, damageInfo->out.m_actualDamageDealt, source, getObject());
  278. }
  279. }
  280. //-------------------------------------------------------------------------------------------------
  281. //-------------------------------------------------------------------------------------------------
  282. void ActiveBody::attemptDamage( DamageInfo *damageInfo )
  283. {
  284. validateArmorAndDamageFX();
  285. // sanity
  286. if( damageInfo == NULL )
  287. return;
  288. if ( m_indestructible )
  289. return;
  290. // initialize these, just in case we bail out early
  291. damageInfo->out.m_actualDamageDealt = 0.0f;
  292. damageInfo->out.m_actualDamageClipped = 0.0f;
  293. // we cannot damage again objects that are already dead
  294. Object* obj = getObject();
  295. if( obj->isEffectivelyDead() )
  296. return;
  297. Bool alreadyHandled = FALSE;
  298. Bool allowModifier = TRUE;
  299. Real amount = m_curArmor.adjustDamage(damageInfo->in.m_damageType, damageInfo->in.m_amount);
  300. switch( damageInfo->in.m_damageType )
  301. {
  302. case DAMAGE_HEALING:
  303. {
  304. // Healing and Damage are separate, so this shouldn't happen
  305. attemptHealing( damageInfo );
  306. return;
  307. }
  308. case DAMAGE_KILLPILOT:
  309. {
  310. // This type of damage doesn't actually damage the unit, but it does kill it's
  311. // pilot, in the case of a vehicle.
  312. if( obj->isKindOf( KINDOF_VEHICLE ) )
  313. {
  314. // Make it unmanned, so units can easily check the ability to "take control of it"
  315. obj->setDisabled( DISABLED_UNMANNED );
  316. TheGameLogic->deselectObject(obj, PLAYERMASK_ALL, TRUE);
  317. // Convert it to the neutral team so it renders gray giving visual representation that it is unmanned.
  318. obj->setTeam( ThePlayerList->getNeutralPlayer()->getDefaultTeam() );
  319. }
  320. alreadyHandled = TRUE;
  321. allowModifier = FALSE;
  322. break;
  323. }
  324. }
  325. if (allowModifier)
  326. {
  327. if( damageInfo->in.m_damageType != DAMAGE_UNRESISTABLE )
  328. {
  329. // Apply the damage scalar (extra bonuses -- like strategy center defensive battle plan)
  330. // And remember not to adjust unresistable damage, just like the armor code can't.
  331. amount *= m_damageScalar;
  332. }
  333. }
  334. // sanity check the damage value -- can't apply negative damage
  335. if( amount > 0.0f )
  336. {
  337. BodyDamageType oldState = m_curDamageState;
  338. if (!alreadyHandled)
  339. {
  340. // do the damage simplistic damage subtraction
  341. internalChangeHealth( -amount );
  342. }
  343. #ifdef ALLOW_SURRENDER
  344. //*****************************************************************************************
  345. //*****************************************************************************************
  346. //THIS CODE HAS BEEN DISABLED FOR THE MULTIPLAYER PLAY TEST!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!**
  347. //*****************************************************************************************
  348. // // if we were "killed" by surrender damage...
  349. // if (damageInfo->in.m_damageType == DAMAGE_SURRENDER && m_currentHealth <= 0.0f && obj->isKindOf(KINDOF_CAN_SURRENDER))
  350. // {
  351. // AIUpdateInterface* ai = obj->getAIUpdateInterface();
  352. // if (ai)
  353. // {
  354. // // do no damage, but make it surrender instead.
  355. // m_currentHealth = m_prevHealth;
  356. // const Object* killer = TheGameLogic->findObjectByID( damageInfo->in.m_sourceID );
  357. // ai->setSurrendered(killer, true);
  358. // return;
  359. // }
  360. // }
  361. //*****************************************************************************************
  362. //*****************************************************************************************
  363. #endif
  364. // record the actual damage done from this, and when it happened
  365. damageInfo->out.m_actualDamageDealt = amount;
  366. damageInfo->out.m_actualDamageClipped = m_prevHealth - m_currentHealth;
  367. // then copy the whole DamageInfo struct for easy lookup
  368. // (object pointer loses scope as soon as atteptdamage's caller ends)
  369. // m_lastDamageTimestamp is initialized to FFFFFFFFFF, so doing a < compare is problematic.
  370. // jba.
  371. if (m_lastDamageTimestamp!=TheGameLogic->getFrame() && m_lastDamageTimestamp != TheGameLogic->getFrame()-1) {
  372. m_lastDamageInfo = *damageInfo;
  373. m_lastDamageCleared = false;
  374. m_lastDamageTimestamp = TheGameLogic->getFrame();
  375. } else {
  376. // Multiple damages applied in one/next frame. We prefer the one that tells who the attacker is.
  377. Object *srcObj1 = TheGameLogic->findObjectByID(m_lastDamageInfo.in.m_sourceID);
  378. Object *srcObj2 = TheGameLogic->findObjectByID(damageInfo->in.m_sourceID);
  379. if (srcObj2) {
  380. if (srcObj1) {
  381. if (srcObj2->isKindOf(KINDOF_VEHICLE) || srcObj2->isKindOf(KINDOF_INFANTRY) ||
  382. srcObj2->isFactionStructure()) {
  383. m_lastDamageInfo = *damageInfo;
  384. m_lastDamageCleared = false;
  385. m_lastDamageTimestamp = TheGameLogic->getFrame();
  386. }
  387. } else {
  388. m_lastDamageInfo = *damageInfo;
  389. m_lastDamageCleared = false;
  390. m_lastDamageTimestamp = TheGameLogic->getFrame();
  391. }
  392. } else {
  393. // no change.
  394. }
  395. }
  396. // Notify the player that they have been attacked by this player
  397. if (m_lastDamageInfo.in.m_sourceID != INVALID_ID)
  398. {
  399. Object *srcObj = TheGameLogic->findObjectByID(m_lastDamageInfo.in.m_sourceID);
  400. if (srcObj)
  401. {
  402. Player *srcPlayer = srcObj->getControllingPlayer();
  403. obj->getControllingPlayer()->setAttackedBy(srcPlayer->getPlayerIndex());
  404. }
  405. }
  406. // if our health has gone down then do run the damage module callback
  407. if( m_currentHealth < m_prevHealth )
  408. {
  409. for (BehaviorModule** m = obj->getBehaviorModules(); *m; ++m)
  410. {
  411. DamageModuleInterface* d = (*m)->getDamage();
  412. if (!d)
  413. continue;
  414. d->onDamage( damageInfo );
  415. }
  416. }
  417. if (m_curDamageState != oldState)
  418. {
  419. for (BehaviorModule** m = obj->getBehaviorModules(); *m; ++m)
  420. {
  421. DamageModuleInterface* d = (*m)->getDamage();
  422. if (!d)
  423. continue;
  424. d->onBodyDamageStateChange( damageInfo, oldState, m_curDamageState );
  425. }
  426. // @todo: This really feels like it should be in the TransitionFX lists.
  427. if (m_curDamageState == BODY_DAMAGED)
  428. {
  429. AudioEventRTS damaged = *obj->getTemplate()->getSoundOnDamaged();
  430. damaged.setObjectID(obj->getID());
  431. TheAudio->addAudioEvent(&damaged);
  432. }
  433. else if (m_curDamageState == BODY_REALLYDAMAGED)
  434. {
  435. AudioEventRTS reallyDamaged = *obj->getTemplate()->getSoundOnReallyDamaged();
  436. reallyDamaged.setObjectID(obj->getID());
  437. TheAudio->addAudioEvent(&reallyDamaged);
  438. }
  439. }
  440. // Should we play our fear sound?
  441. if( (m_prevHealth / m_maxHealth) > YELLOW_DAMAGE_PERCENT &&
  442. (m_currentHealth / m_maxHealth) < YELLOW_DAMAGE_PERCENT &&
  443. (m_currentHealth > 0) )
  444. {
  445. // 25% chance to play
  446. if (GameLogicRandomValue(0, 99) < 25)
  447. {
  448. AudioEventRTS fearSound = *obj->getTemplate()->getVoiceFear();
  449. fearSound.setPosition( obj->getPosition() );
  450. fearSound.setPlayerIndex( obj->getControllingPlayer()->getPlayerIndex() );
  451. TheAudio->addAudioEvent(&fearSound);
  452. }
  453. }
  454. // check to see if we died
  455. if( m_currentHealth <= 0 && m_prevHealth > 0 )
  456. {
  457. // Give our killer credit for killing us, if there is one.
  458. Object *killer = TheGameLogic->findObjectByID( damageInfo->in.m_sourceID );
  459. if( killer )
  460. {
  461. killer->scoreTheKill( obj );
  462. }
  463. obj->onDie( damageInfo );
  464. }
  465. }
  466. doDamageFX(damageInfo);
  467. // Damaged repulsable civilians scare (repulse) other civs. jba.
  468. if (TheAI->getAiData()->m_enableRepulsors) {
  469. if (obj->isKindOf(KINDOF_CAN_BE_REPULSED)) {
  470. obj->setStatus(OBJECT_STATUS_REPULSOR, true);
  471. }
  472. }
  473. }
  474. //-------------------------------------------------------------------------------------------------
  475. //-------------------------------------------------------------------------------------------------
  476. void ActiveBody::attemptHealing( DamageInfo *damageInfo )
  477. {
  478. validateArmorAndDamageFX();
  479. // sanity
  480. if( damageInfo == NULL )
  481. return;
  482. if( damageInfo->in.m_damageType != DAMAGE_HEALING )
  483. {
  484. // Healing and Damage are separate, so this shouldn't happen
  485. attemptDamage( damageInfo );
  486. return;
  487. }
  488. Object* obj = getObject();
  489. // srj sez: sorry, once yer dead, yer dead.
  490. // Special case for bridges, cause the system now things they're dead
  491. ///@todo we need to figure out what has changed so we don't have to hack this (CBD 11-1-2002)
  492. if( obj->isKindOf( KINDOF_BRIDGE ) == FALSE &&
  493. obj->isKindOf( KINDOF_BRIDGE_TOWER ) == FALSE &&
  494. obj->isEffectivelyDead())
  495. return;
  496. // initialize these, just in case we bail out early
  497. damageInfo->out.m_actualDamageDealt = 0.0f;
  498. damageInfo->out.m_actualDamageClipped = 0.0f;
  499. Real amount = m_curArmor.adjustDamage(damageInfo->in.m_damageType, damageInfo->in.m_amount);
  500. // sanity check the damage value -- can't apply negative healing
  501. if( amount > 0.0f )
  502. {
  503. BodyDamageType oldState = m_curDamageState;
  504. // do the damage simplistic damage ADDITION
  505. internalChangeHealth( amount );
  506. // record the actual damage done from this, and when it happened
  507. damageInfo->out.m_actualDamageDealt = amount;
  508. damageInfo->out.m_actualDamageClipped = m_prevHealth - m_currentHealth;
  509. //then copy the whole DamageInfo struct for easy lookup
  510. //(object pointer loses scope as soon as atteptdamage's caller ends)
  511. m_lastDamageInfo = *damageInfo;
  512. m_lastDamageCleared = false;
  513. m_lastDamageTimestamp = TheGameLogic->getFrame();
  514. m_lastHealingTimestamp = TheGameLogic->getFrame();
  515. // if our health has gone UP then do run the damage module callback
  516. if( m_currentHealth > m_prevHealth )
  517. {
  518. for (BehaviorModule** m = obj->getBehaviorModules(); *m; ++m)
  519. {
  520. DamageModuleInterface* d = (*m)->getDamage();
  521. if (!d)
  522. continue;
  523. d->onHealing( damageInfo );
  524. }
  525. }
  526. if (m_curDamageState != oldState)
  527. {
  528. for (BehaviorModule** m = obj->getBehaviorModules(); *m; ++m)
  529. {
  530. DamageModuleInterface* d = (*m)->getDamage();
  531. if (!d)
  532. continue;
  533. d->onBodyDamageStateChange( damageInfo, oldState, m_curDamageState );
  534. }
  535. }
  536. }
  537. doDamageFX(damageInfo);
  538. }
  539. //-------------------------------------------------------------------------------------------------
  540. /** Simple setting of the health value, it does *NOT* track any transition
  541. * states for the event of "damage" or the event of "death". */
  542. //-------------------------------------------------------------------------------------------------
  543. void ActiveBody::setInitialHealth(Int initialPercent)
  544. {
  545. // save the current health as the previous health
  546. m_prevHealth = m_currentHealth;
  547. Real factor = initialPercent/100.0f;
  548. Real newHealth = factor * m_initialHealth;
  549. // change the health to the requested percentage.
  550. internalChangeHealth(newHealth - m_currentHealth);
  551. }
  552. //-------------------------------------------------------------------------------------------------
  553. /** Simple setting of the health value, it does *NOT* track any transition
  554. * states for the event of "damage" or the event of "death". */
  555. //-------------------------------------------------------------------------------------------------
  556. void ActiveBody::setMaxHealth( Real maxHealth, MaxHealthChangeType healthChangeType )
  557. {
  558. Real prevMaxHealth = m_maxHealth;
  559. m_maxHealth = maxHealth;
  560. m_initialHealth = maxHealth;
  561. switch( healthChangeType )
  562. {
  563. case PRESERVE_RATIO:
  564. {
  565. //400/500 (80%) + 100 becomes 480/600 (80%)
  566. //200/500 (40%) - 100 becomes 160/400 (40%)
  567. Real ratio = m_currentHealth / prevMaxHealth;
  568. Real newHealth = maxHealth * ratio;
  569. internalChangeHealth( newHealth - m_currentHealth );
  570. break;
  571. }
  572. case ADD_CURRENT_HEALTH_TOO:
  573. {
  574. //Add the same amount that we are adding to the max health.
  575. //This could kill you if max health is reduced (if we ever have that ability to add buffer health like in D&D)
  576. //400/500 (80%) + 100 becomes 500/600 (83%)
  577. //200/500 (40%) - 100 becomes 100/400 (25%)
  578. internalChangeHealth( maxHealth - prevMaxHealth );
  579. break;
  580. }
  581. case SAME_CURRENTHEALTH:
  582. //do nothing
  583. break;
  584. }
  585. //
  586. // when max health is getting clipped to a lower value, if our current health
  587. // value is now outside of the max health range we will set it back down to the
  588. // new cap. Note that we are *NOT* going through any healing or damage methods here
  589. // and are doing a direct set
  590. //
  591. if( m_currentHealth > maxHealth )
  592. {
  593. internalChangeHealth( maxHealth - m_currentHealth );
  594. }
  595. }
  596. // ------------------------------------------------------------------------------------------------
  597. /** Given the current damage state of the object, evaluate the visual model conditions
  598. * that have a visual impact on the object */
  599. // ------------------------------------------------------------------------------------------------
  600. void ActiveBody::evaluateVisualCondition()
  601. {
  602. Drawable* draw = getObject()->getDrawable();
  603. if (draw)
  604. {
  605. draw->reactToBodyDamageStateChange(m_curDamageState);
  606. }
  607. //
  608. // destroy any particle systems that were attached to our body for the old state
  609. // and create new particle systems for the new state
  610. //
  611. updateBodyParticleSystems();
  612. }
  613. // ------------------------------------------------------------------------------------------------
  614. /** Create up to maxSystems particle systems of type particleSystemName and attach to bones
  615. * specified by the bone base name. If there are more bones than maxSystems then the
  616. * bones will be randomly selected */
  617. // ------------------------------------------------------------------------------------------------
  618. void ActiveBody::createParticleSystems( const AsciiString &boneBaseName,
  619. const ParticleSystemTemplate *systemTemplate,
  620. Int maxSystems )
  621. {
  622. Object *us = getObject();
  623. // sanity
  624. if( systemTemplate == NULL )
  625. return;
  626. // get the bones
  627. enum { MAX_BONES = 16 };
  628. Coord3D bonePositions[ MAX_BONES ];
  629. Int numBones = us->getMultiLogicalBonePosition( boneBaseName.str(),
  630. MAX_BONES,
  631. bonePositions,
  632. NULL,
  633. FALSE );
  634. // if no bones found nothing else to do
  635. if( numBones == 0 )
  636. return;
  637. //
  638. // if we don't have enough bones to go up to maxSystems, we will change maxSystems to be
  639. // the number of bones we actually have (we don't want systems doubling up on bones)
  640. //
  641. if( numBones < maxSystems )
  642. maxSystems = numBones;
  643. //
  644. // create an array that we'll use to mark which bone positions have already been used,
  645. // this is necessary when we have more bones than particle systems we're going to
  646. // create, in which case we place the particle systems at random bone locations
  647. // but don't want to repeat any
  648. //
  649. Bool usedBoneIndices[ MAX_BONES ] = { FALSE };
  650. // create the particle systems
  651. const Coord3D *pos;
  652. for( Int i = 0; i < maxSystems; ++i )
  653. {
  654. // pick a bone index to place this particle system at
  655. // MDC: moving to GameLogicRandomValue. This does not need to be synced, but having it so makes searches *so* much nicer.
  656. //Int boneIndex = GameLogicRandomValue( 0, maxSystems - i - 1 );
  657. // DTEH: Moved back to GameClientRandomValue because of desync problems. July 27th 2003.
  658. Int boneIndex = GameClientRandomValue( 0, maxSystems - i - 1 );
  659. // find the actual bone location to use and mark that bone index as used
  660. Int count = 0;
  661. for( Int j = 0; j < numBones; j++ )
  662. {
  663. // ignore bone positions that have already been used
  664. if( usedBoneIndices[ j ] == TRUE )
  665. continue;
  666. // this spot is available, if count == boneIndex then use this index
  667. if( count == boneIndex )
  668. {
  669. pos = &bonePositions[ j ];
  670. usedBoneIndices[ j ] = TRUE;
  671. break; // exit for j
  672. } // end if
  673. else
  674. {
  675. // we won't use this index, increment count until we find a suitable index to use
  676. ++count;
  677. } // end else
  678. } // end for, j
  679. // sanity
  680. DEBUG_ASSERTCRASH( j != numBones,
  681. ("ActiveBody::createParticleSystems, Unable to select particle system index\n") );
  682. // create particle system here
  683. ParticleSystem *particleSystem = TheParticleSystemManager->createParticleSystem( systemTemplate );
  684. if( particleSystem )
  685. {
  686. // set the position of the particle system in local object space
  687. particleSystem->setPosition( pos );
  688. // attach particle system to object
  689. particleSystem->attachToObject( us );
  690. // create a new body particle system entry and keep this particle system in it
  691. BodyParticleSystem *newEntry = newInstance(BodyParticleSystem);
  692. newEntry->m_particleSystemID = particleSystem->getSystemID();
  693. newEntry->m_next = m_particleSystems;
  694. m_particleSystems = newEntry;
  695. } // end if
  696. } // end for, i
  697. } // end createParticleSystems
  698. // ------------------------------------------------------------------------------------------------
  699. /** Delete all the body particle systems */
  700. // ------------------------------------------------------------------------------------------------
  701. void ActiveBody::deleteAllParticleSystems( void )
  702. {
  703. BodyParticleSystem *nextBodySystem;
  704. ParticleSystem *particleSystem;
  705. while( m_particleSystems )
  706. {
  707. // get this particle system
  708. particleSystem = TheParticleSystemManager->findParticleSystem( m_particleSystems->m_particleSystemID );
  709. if( particleSystem )
  710. particleSystem->destroy();
  711. // get next system in the body
  712. nextBodySystem = m_particleSystems->m_next;
  713. // destroy this entry
  714. m_particleSystems->deleteInstance();
  715. // set the body systems head to the next
  716. m_particleSystems = nextBodySystem;
  717. } // end while
  718. } // end deleteAllParticleSystems
  719. // ------------------------------------------------------------------------------------------------
  720. /* This function is called on state changes only. Body Type or Aflameness. */
  721. // ------------------------------------------------------------------------------------------------
  722. void ActiveBody::updateBodyParticleSystems( void )
  723. {
  724. static const ParticleSystemTemplate *fireSmallTemplate = TheParticleSystemManager->findTemplate( TheGlobalData->m_autoFireParticleSmallSystem );
  725. static const ParticleSystemTemplate *fireMediumTemplate = TheParticleSystemManager->findTemplate( TheGlobalData->m_autoFireParticleMediumSystem );
  726. static const ParticleSystemTemplate *fireLargeTemplate = TheParticleSystemManager->findTemplate( TheGlobalData->m_autoFireParticleLargeSystem );
  727. static const ParticleSystemTemplate *smokeSmallTemplate = TheParticleSystemManager->findTemplate( TheGlobalData->m_autoSmokeParticleSmallSystem );
  728. static const ParticleSystemTemplate *smokeMediumTemplate = TheParticleSystemManager->findTemplate( TheGlobalData->m_autoSmokeParticleMediumSystem );
  729. static const ParticleSystemTemplate *smokeLargeTemplate = TheParticleSystemManager->findTemplate( TheGlobalData->m_autoSmokeParticleLargeSystem );
  730. static const ParticleSystemTemplate *aflameTemplate = TheParticleSystemManager->findTemplate( TheGlobalData->m_autoAflameParticleSystem );
  731. Int countModifier;
  732. const ParticleSystemTemplate *fireSmall;
  733. const ParticleSystemTemplate *fireMedium;
  734. const ParticleSystemTemplate *fireLarge;
  735. const ParticleSystemTemplate *smokeSmall;
  736. const ParticleSystemTemplate *smokeMedium;
  737. const ParticleSystemTemplate *smokeLarge;
  738. //
  739. // when we're aflame, we use a slightly different set of particle systems that are
  740. // auto created that lends itself to more fire and bigger fire
  741. //
  742. if( getObject()->testStatus( OBJECT_STATUS_AFLAME ) )
  743. {
  744. fireSmall = fireMediumTemplate; // small fire becomes medium fire
  745. fireMedium = fireLargeTemplate; // medium fire becomes large fire
  746. fireLarge = fireLargeTemplate; // large fire stays large
  747. smokeSmall = fireSmallTemplate; // small smoke becomes small fire
  748. smokeMedium = fireSmallTemplate; // medium smoke becomes small fire
  749. smokeLarge = fireSmallTemplate; // large smoke becomes small fire
  750. // we get to make more of them all too
  751. countModifier = 2;
  752. } // end if
  753. else
  754. {
  755. // use regular templates
  756. fireSmall = fireSmallTemplate;
  757. fireMedium = fireMediumTemplate;
  758. fireLarge = fireLargeTemplate;
  759. smokeSmall = smokeSmallTemplate;
  760. smokeMedium = smokeMediumTemplate;
  761. smokeLarge = smokeLargeTemplate;
  762. // we make just the normal amount of these
  763. countModifier = 1;
  764. } // end else
  765. //
  766. // remove any particle systems we have currently in the list in favor of any new ones
  767. // that we're going to autopopulate ourselves with
  768. //
  769. deleteAllParticleSystems();
  770. //
  771. // create particle systems for the new body state
  772. //
  773. // small fire bones
  774. createParticleSystems( TheGlobalData->m_autoFireParticleSmallPrefix,
  775. fireSmall, TheGlobalData->m_autoFireParticleSmallMax * countModifier );
  776. // medium fire bones
  777. createParticleSystems( TheGlobalData->m_autoFireParticleMediumPrefix,
  778. fireMedium, TheGlobalData->m_autoFireParticleMediumMax * countModifier );
  779. // large fire bones
  780. createParticleSystems( TheGlobalData->m_autoFireParticleLargePrefix,
  781. fireLarge, TheGlobalData->m_autoFireParticleLargeMax * countModifier );
  782. // small smoke bones
  783. createParticleSystems( TheGlobalData->m_autoSmokeParticleSmallPrefix,
  784. smokeSmall, TheGlobalData->m_autoSmokeParticleSmallMax * countModifier );
  785. // medium smoke bones
  786. createParticleSystems( TheGlobalData->m_autoSmokeParticleMediumPrefix,
  787. smokeMedium, TheGlobalData->m_autoSmokeParticleMediumMax * countModifier );
  788. // large smoke bones
  789. createParticleSystems( TheGlobalData->m_autoSmokeParticleLargePrefix,
  790. smokeLarge, TheGlobalData->m_autoSmokeParticleLargeMax * countModifier );
  791. // actively on fire
  792. if( getObject()->testStatus( OBJECT_STATUS_AFLAME ) )
  793. createParticleSystems( TheGlobalData->m_autoAflameParticlePrefix,
  794. aflameTemplate, TheGlobalData->m_autoAflameParticleMax * countModifier );
  795. } // end updatebodyParticleSystems
  796. //-------------------------------------------------------------------------------------------------
  797. /** Simple changing of the health value, it does *NOT* track any transition
  798. * states for the event of "damage" or the event of "death". If you
  799. * with to kill an object and give these modules a chance to react
  800. * to that event use the proper damage method calls.
  801. * No game logic should go in here. This is the low level math and flag maintenance.
  802. * Game stuff goes in attemptDamage and attemptHealing.
  803. */
  804. //-------------------------------------------------------------------------------------------------
  805. void ActiveBody::internalChangeHealth( Real delta )
  806. {
  807. // save the current health as the previous health
  808. m_prevHealth = m_currentHealth;
  809. // change the health by the delta, it can be positive or negative
  810. m_currentHealth += delta;
  811. // high end cap
  812. Real maxHealth = m_maxHealth;
  813. if( m_currentHealth > maxHealth )
  814. m_currentHealth = maxHealth;
  815. // low end cap
  816. const Real lowEndCap = 0.0f; // low end cap for health, don't go below this
  817. if( m_currentHealth < lowEndCap )
  818. m_currentHealth = lowEndCap;
  819. // recalc the damage state
  820. BodyDamageType oldState = m_curDamageState;
  821. setCorrectDamageState();
  822. // if our state has changed
  823. if( m_curDamageState != oldState )
  824. {
  825. //
  826. // show a visual change in the model for the damage state, we do not show visual changes
  827. // for damage states when things are under construction because we just don't have
  828. // all the art states for that during buildup animation
  829. //
  830. if( BitTest( getObject()->getStatusBits(), OBJECT_STATUS_UNDER_CONSTRUCTION ) == FALSE)
  831. evaluateVisualCondition();
  832. } // end if
  833. // mark the bit according to our health. (if our AI is dead but our health improves, it will
  834. // still re-flag this bit in the AIDeadState every frame.)
  835. getObject()->setEffectivelyDead(m_currentHealth <= 0);
  836. }
  837. //-------------------------------------------------------------------------------------------------
  838. //-------------------------------------------------------------------------------------------------
  839. Real ActiveBody::getHealth() const
  840. {
  841. return m_currentHealth;
  842. }
  843. //-------------------------------------------------------------------------------------------------
  844. //-------------------------------------------------------------------------------------------------
  845. BodyDamageType ActiveBody::getDamageState() const
  846. {
  847. return m_curDamageState;
  848. }
  849. //-------------------------------------------------------------------------------------------------
  850. //-------------------------------------------------------------------------------------------------
  851. Real ActiveBody::getMaxHealth() const
  852. {
  853. return m_maxHealth;
  854. } ///< return max health
  855. //-------------------------------------------------------------------------------------------------
  856. //-------------------------------------------------------------------------------------------------
  857. Real ActiveBody::getInitialHealth() const
  858. {
  859. return m_initialHealth;
  860. } // return initial health
  861. // ------------------------------------------------------------------------------------------------
  862. /** Set or unset the overridable indestructible flag in the body */
  863. // ------------------------------------------------------------------------------------------------
  864. void ActiveBody::setIndestructible( Bool indestructible )
  865. {
  866. m_indestructible = indestructible;
  867. // for bridges, we mirror this state on its towers
  868. Object *us = getObject();
  869. if( us->isKindOf( KINDOF_BRIDGE ) )
  870. {
  871. BridgeBehaviorInterface *bbi = BridgeBehavior::getBridgeBehaviorInterfaceFromObject( us );
  872. if( bbi )
  873. {
  874. Object *tower;
  875. // get tower
  876. for( Int i = 0; i < BRIDGE_MAX_TOWERS; ++i )
  877. {
  878. tower = TheGameLogic->findObjectByID( bbi->getTowerID( BridgeTowerType(i) ) );
  879. if( tower )
  880. {
  881. BodyModuleInterface *body = tower->getBodyModule();
  882. if( body )
  883. body->setIndestructible( indestructible );
  884. } // end if
  885. } // end for, i
  886. } // end if
  887. } // end if
  888. } // end setIndestructible
  889. //-------------------------------------------------------------------------------------------------
  890. //-------------------------------------------------------------------------------------------------
  891. void ActiveBody::onVeterancyLevelChanged( VeterancyLevel oldLevel, VeterancyLevel newLevel )
  892. {
  893. if (oldLevel == newLevel)
  894. return;
  895. if (oldLevel < newLevel)
  896. {
  897. AudioEventRTS veterancyChanged;
  898. switch (newLevel)
  899. {
  900. case LEVEL_VETERAN:
  901. veterancyChanged = *getObject()->getTemplate()->getSoundPromotedVeteran();
  902. break;
  903. case LEVEL_ELITE:
  904. veterancyChanged = *getObject()->getTemplate()->getSoundPromotedElite();
  905. break;
  906. case LEVEL_HEROIC:
  907. veterancyChanged = *getObject()->getTemplate()->getSoundPromotedHero();
  908. break;
  909. }
  910. veterancyChanged.setObjectID(getObject()->getID());
  911. TheAudio->addAudioEvent(&veterancyChanged);
  912. //Also mark the UI dirty -- incase the object is selected or contained.
  913. Object *obj = getObject();
  914. Drawable *draw = TheInGameUI->getFirstSelectedDrawable();
  915. if( draw )
  916. {
  917. Object *checkOwner = draw->getObject();
  918. if( checkOwner == obj )
  919. {
  920. //Our selected object has been promoted!
  921. TheControlBar->markUIDirty();
  922. }
  923. else
  924. {
  925. const Object *containedBy = obj->getContainedBy();
  926. if( containedBy && TheInGameUI->getSelectCount() == 1 )
  927. {
  928. Object *checkOwner = draw->getObject();
  929. if( checkOwner == containedBy )
  930. {
  931. //But only if the contained by object is containing me!
  932. TheControlBar->markUIDirty();
  933. }
  934. }
  935. }
  936. }
  937. }
  938. Real oldBonus = TheGlobalData->m_healthBonus[oldLevel];
  939. Real newBonus = TheGlobalData->m_healthBonus[newLevel];
  940. Real mult = newBonus / oldBonus;
  941. // get this before calling setMaxHealth, since it can clip curHealth
  942. //Real newHealth = m_currentHealth * mult;
  943. // change the max
  944. setMaxHealth(m_maxHealth * mult, PRESERVE_RATIO );
  945. // now change the cur (setMaxHealth now handles it)
  946. //internalChangeHealth( newHealth - m_currentHealth );
  947. switch (newLevel)
  948. {
  949. case LEVEL_REGULAR:
  950. clearArmorSetFlag(ARMORSET_VETERAN);
  951. clearArmorSetFlag(ARMORSET_ELITE);
  952. clearArmorSetFlag(ARMORSET_HERO);
  953. break;
  954. case LEVEL_VETERAN:
  955. setArmorSetFlag(ARMORSET_VETERAN);
  956. clearArmorSetFlag(ARMORSET_ELITE);
  957. clearArmorSetFlag(ARMORSET_HERO);
  958. break;
  959. case LEVEL_ELITE:
  960. clearArmorSetFlag(ARMORSET_VETERAN);
  961. setArmorSetFlag(ARMORSET_ELITE);
  962. clearArmorSetFlag(ARMORSET_HERO);
  963. break;
  964. case LEVEL_HEROIC:
  965. clearArmorSetFlag(ARMORSET_VETERAN);
  966. clearArmorSetFlag(ARMORSET_ELITE);
  967. setArmorSetFlag(ARMORSET_HERO);
  968. break;
  969. }
  970. }
  971. // ------------------------------------------------------------------------------------------------
  972. // ------------------------------------------------------------------------------------------------
  973. void ActiveBody::setAflame( Bool )
  974. {
  975. //
  976. // All this does now is act like a major body state change. It is called after Aflame has been
  977. // set or cleared as an Object Status
  978. //
  979. updateBodyParticleSystems();
  980. }
  981. // ------------------------------------------------------------------------------------------------
  982. /** CRC */
  983. // ------------------------------------------------------------------------------------------------
  984. void ActiveBody::crc( Xfer *xfer )
  985. {
  986. // extend base class
  987. BodyModule::crc( xfer );
  988. } // end crc
  989. // ------------------------------------------------------------------------------------------------
  990. /** Xfer method
  991. * Version Info:
  992. * 1: Initial version */
  993. // ------------------------------------------------------------------------------------------------
  994. void ActiveBody::xfer( Xfer *xfer )
  995. {
  996. // version
  997. XferVersion currentVersion = 1;
  998. XferVersion version = currentVersion;
  999. xfer->xferVersion( &version, currentVersion );
  1000. // base class
  1001. BodyModule::xfer( xfer );
  1002. // current health
  1003. xfer->xferReal( &m_currentHealth );
  1004. // previous health
  1005. xfer->xferReal( &m_prevHealth );
  1006. // max health
  1007. xfer->xferReal( &m_maxHealth );
  1008. // initial health
  1009. xfer->xferReal( &m_initialHealth );
  1010. // current damage state
  1011. xfer->xferUser( &m_curDamageState, sizeof( BodyDamageType ) );
  1012. // next damage fx time
  1013. xfer->xferUnsignedInt( &m_nextDamageFXTime );
  1014. // last damage fx done
  1015. xfer->xferUser( &m_lastDamageFXDone, sizeof( DamageType ) );
  1016. // last damage info
  1017. xfer->xferSnapshot( &m_lastDamageInfo );
  1018. // last damage timestamp
  1019. xfer->xferUnsignedInt( &m_lastDamageTimestamp );
  1020. // last damage timestamp
  1021. xfer->xferUnsignedInt( &m_lastHealingTimestamp );
  1022. // front crushed
  1023. xfer->xferBool( &m_frontCrushed );
  1024. // back crushed
  1025. xfer->xferBool( &m_backCrushed );
  1026. // last damaged cleared
  1027. xfer->xferBool( &m_lastDamageCleared );
  1028. // indestructible
  1029. xfer->xferBool( &m_indestructible );
  1030. // particle system count
  1031. BodyParticleSystem *system;
  1032. UnsignedShort particleSystemCount = 0;
  1033. for( system = m_particleSystems; system; system = system->m_next )
  1034. particleSystemCount++;
  1035. xfer->xferUnsignedShort( &particleSystemCount );
  1036. // particle systems
  1037. if( xfer->getXferMode() == XFER_SAVE )
  1038. {
  1039. // walk the particle systems
  1040. for( system = m_particleSystems; system; system = system->m_next )
  1041. {
  1042. // write particle system ID
  1043. xfer->xferUser( &system->m_particleSystemID, sizeof( ParticleSystemID ) );
  1044. } // end for, system
  1045. } // end if, save
  1046. else
  1047. {
  1048. ParticleSystemID particleSystemID;
  1049. // the list should be empty at this time
  1050. if( m_particleSystems != NULL )
  1051. {
  1052. DEBUG_CRASH(( "ActiveBody::xfer - m_particleSystems should be empty, but is not\n" ));
  1053. throw SC_INVALID_DATA;
  1054. } // end if
  1055. // read all data elements
  1056. BodyParticleSystem *newEntry;
  1057. for( UnsignedShort i = 0; i < particleSystemCount; ++i )
  1058. {
  1059. // read particle system ID
  1060. xfer->xferUser( &particleSystemID, sizeof( ParticleSystemID ) );
  1061. // allocate entry and add to list
  1062. newEntry = newInstance(BodyParticleSystem);
  1063. newEntry->m_particleSystemID = particleSystemID;
  1064. newEntry->m_next = m_particleSystems; // the list will be reversed, but we don't care
  1065. m_particleSystems = newEntry;
  1066. } // end for, i
  1067. } // end else, load
  1068. // armor set flags
  1069. m_curArmorSetFlags.xfer( xfer );
  1070. } // end xfer
  1071. // ------------------------------------------------------------------------------------------------
  1072. /** Load post process */
  1073. // ------------------------------------------------------------------------------------------------
  1074. void ActiveBody::loadPostProcess( void )
  1075. {
  1076. // extend base class
  1077. BodyModule::loadPostProcess();
  1078. } // end loadPostProcess