ActiveBody.cpp 55 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654
  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: 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/AI.h"
  45. #include "GameLogic/AIPathfind.h"
  46. #include "GameLogic/Armor.h"
  47. #include "GameLogic/GameLogic.h"
  48. #include "GameLogic/Object.h"
  49. #include "GameLogic/Damage.h"
  50. #include "GameLogic/PartitionManager.h"
  51. #include "GameLogic/TerrainLogic.h"
  52. #include "GameLogic/Weapon.h"
  53. #include "GameLogic/Module/AIUpdate.h"
  54. #include "GameLogic/Module/ActiveBody.h"
  55. #include "GameLogic/Module/BridgeBehavior.h"
  56. #include "GameLogic/Module/ContainModule.h"
  57. #include "GameLogic/Module/DamageModule.h"
  58. #include "GameLogic/Module/DieModule.h"
  59. #ifdef _INTERNAL
  60. // for occasional debugging...
  61. //#pragma optimize("", off)
  62. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  63. #endif
  64. #define YELLOW_DAMAGE_PERCENT (0.25f)
  65. // FORWARD REFERENCES /////////////////////////////////////////////////////////////////////////////
  66. // ------------------------------------------------------------------------------------------------
  67. /** Body particle systems are particle systems that are automatically created and attached
  68. * to an object as the damage state changes for that object. We keep a list of these
  69. * so that when we transition from one state to another we can kill any old particle
  70. * systems that we need to before we create new ones */
  71. // ------------------------------------------------------------------------------------------------
  72. class BodyParticleSystem : public MemoryPoolObject
  73. {
  74. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( BodyParticleSystem, "BodyParticleSystem" )
  75. public:
  76. ParticleSystemID m_particleSystemID; ///< the particle system ID
  77. BodyParticleSystem *m_next; ///< next particle system in this body module
  78. };
  79. // ------------------------------------------------------------------------------------------------
  80. // ------------------------------------------------------------------------------------------------
  81. BodyParticleSystem::~BodyParticleSystem( void )
  82. {
  83. } // end ~BodyParticleSystem
  84. ///////////////////////////////////////////////////////////////////////////////////////////////////
  85. // PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
  86. ///////////////////////////////////////////////////////////////////////////////////////////////////
  87. //-------------------------------------------------------------------------------------------------
  88. // ------------------------------------------------------------------------------------------------
  89. static BodyDamageType calcDamageState(Real health, Real maxHealth)
  90. {
  91. if (!TheGlobalData)
  92. return BODY_PRISTINE;
  93. Real ratio = health / maxHealth;
  94. if (ratio > TheGlobalData->m_unitDamagedThresh)
  95. {
  96. return BODY_PRISTINE;
  97. }
  98. else if (ratio > TheGlobalData->m_unitReallyDamagedThresh)
  99. {
  100. return BODY_DAMAGED;
  101. }
  102. else if (ratio > 0.0f)
  103. {
  104. return BODY_REALLYDAMAGED;
  105. }
  106. else
  107. {
  108. return BODY_RUBBLE;
  109. }
  110. }
  111. //-------------------------------------------------------------------------------------------------
  112. //-------------------------------------------------------------------------------------------------
  113. ActiveBodyModuleData::ActiveBodyModuleData()
  114. {
  115. m_maxHealth = 0;
  116. m_initialHealth = 0;
  117. m_subdualDamageCap = 0;
  118. m_subdualDamageHealRate = 0;
  119. m_subdualDamageHealAmount = 0;
  120. }
  121. //-------------------------------------------------------------------------------------------------
  122. //-------------------------------------------------------------------------------------------------
  123. void ActiveBodyModuleData::buildFieldParse(MultiIniFieldParse& p)
  124. {
  125. ModuleData::buildFieldParse(p);
  126. static const FieldParse dataFieldParse[] =
  127. {
  128. { "MaxHealth", INI::parseReal, NULL, offsetof( ActiveBodyModuleData, m_maxHealth ) },
  129. { "InitialHealth", INI::parseReal, NULL, offsetof( ActiveBodyModuleData, m_initialHealth ) },
  130. { "SubdualDamageCap", INI::parseReal, NULL, offsetof( ActiveBodyModuleData, m_subdualDamageCap ) },
  131. { "SubdualDamageHealRate", INI::parseDurationUnsignedInt, NULL, offsetof( ActiveBodyModuleData, m_subdualDamageHealRate ) },
  132. { "SubdualDamageHealAmount", INI::parseReal, NULL, offsetof( ActiveBodyModuleData, m_subdualDamageHealAmount ) },
  133. { 0, 0, 0, 0 }
  134. };
  135. p.add(dataFieldParse);
  136. }
  137. //-------------------------------------------------------------------------------------------------
  138. //-------------------------------------------------------------------------------------------------
  139. ActiveBody::ActiveBody( Thing *thing, const ModuleData* moduleData ) :
  140. BodyModule(thing, moduleData),
  141. m_curDamageFX(NULL),
  142. m_curArmorSet(NULL),
  143. m_frontCrushed(false),
  144. m_backCrushed(false),
  145. m_lastDamageTimestamp(0xffffffff),// So we don't think we just got damaged on the first frame
  146. m_lastHealingTimestamp(0xffffffff),// So we don't think we just got healed on the first frame
  147. m_curDamageState(BODY_PRISTINE),
  148. m_nextDamageFXTime(0),
  149. m_lastDamageFXDone((DamageType)-1),
  150. m_lastDamageCleared(false),
  151. m_particleSystems(NULL),
  152. m_currentSubdualDamage(0),
  153. m_indestructible(false)
  154. {
  155. m_currentHealth = getActiveBodyModuleData()->m_initialHealth;
  156. m_prevHealth = getActiveBodyModuleData()->m_initialHealth;
  157. m_maxHealth = getActiveBodyModuleData()->m_maxHealth;
  158. m_initialHealth = getActiveBodyModuleData()->m_initialHealth;
  159. // force an initially-valid armor setup
  160. validateArmorAndDamageFX();
  161. // start us in the right state
  162. setCorrectDamageState();
  163. }
  164. //-------------------------------------------------------------------------------------------------
  165. //-------------------------------------------------------------------------------------------------
  166. ActiveBody::~ActiveBody( void )
  167. {
  168. }
  169. // ------------------------------------------------------------------------------------------------
  170. // ------------------------------------------------------------------------------------------------
  171. void ActiveBody::onDelete( void )
  172. {
  173. // delete all particle systems
  174. deleteAllParticleSystems();
  175. } // end onDelete
  176. //-------------------------------------------------------------------------------------------------
  177. //-------------------------------------------------------------------------------------------------
  178. void ActiveBody::setCorrectDamageState()
  179. {
  180. m_curDamageState = calcDamageState(m_currentHealth, m_maxHealth);
  181. /// @todo srj -- bleah, this is an icky way to do it. oh well.
  182. if (m_curDamageState == BODY_RUBBLE && getObject()->isKindOf(KINDOF_STRUCTURE))
  183. {
  184. Real rubbleHeight = getObject()->getTemplate()->getStructureRubbleHeight();
  185. if (rubbleHeight <= 0.0f)
  186. rubbleHeight = TheGlobalData->m_defaultStructureRubbleHeight;
  187. /** @todo I had to change this to a Z only version to keep it from disappearing from the
  188. PartitionManager for a frame. That didn't used to happen.
  189. */
  190. getObject()->setGeometryInfoZ(rubbleHeight);
  191. // Have to tell pathfind as well, as rubble pathfinds differently.
  192. TheAI->pathfinder()->removeObjectFromPathfindMap(getObject());
  193. TheAI->pathfinder()->addObjectToPathfindMap(getObject());
  194. // here we make sure nobody collides with us, ever again... //Lorenzen
  195. //THis allows projectiles shot from infantry that are inside rubble to get out of said rubble safely
  196. getObject()->setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_NO_COLLISIONS ) );
  197. }
  198. }
  199. //-------------------------------------------------------------------------------------------------
  200. //-------------------------------------------------------------------------------------------------
  201. void ActiveBody::setDamageState( BodyDamageType newState )
  202. {
  203. Real ratio = 1.0f;
  204. if( newState == BODY_PRISTINE )
  205. {
  206. ratio = 1.0f;
  207. }
  208. else if( newState == BODY_DAMAGED )
  209. {
  210. ratio = TheGlobalData->m_unitDamagedThresh;
  211. }
  212. else if( newState == BODY_REALLYDAMAGED )
  213. {
  214. ratio = TheGlobalData->m_unitReallyDamagedThresh;
  215. }
  216. else if( newState == BODY_RUBBLE )
  217. {
  218. ratio = 0.0f;
  219. }
  220. Real desiredHealth = m_maxHealth * ratio - 1;// -1 because < not <= in calcState
  221. desiredHealth = max( desiredHealth, 0.0f );
  222. internalChangeHealth( desiredHealth - m_currentHealth );
  223. setCorrectDamageState();
  224. }
  225. //-------------------------------------------------------------------------------------------------
  226. //-------------------------------------------------------------------------------------------------
  227. void ActiveBody::validateArmorAndDamageFX() const
  228. {
  229. const ArmorTemplateSet* set = getObject()->getTemplate()->findArmorTemplateSet(m_curArmorSetFlags);
  230. DEBUG_ASSERTCRASH(set, ("findArmorSet should never return null"));
  231. if (set && set != m_curArmorSet)
  232. {
  233. if (set->getArmorTemplate())
  234. {
  235. m_curArmor = TheArmorStore->makeArmor(set->getArmorTemplate());
  236. }
  237. else
  238. {
  239. m_curArmor.clear();
  240. }
  241. m_curDamageFX = set->getDamageFX();
  242. m_curArmorSet = set;
  243. }
  244. }
  245. //-------------------------------------------------------------------------------------------------
  246. //-------------------------------------------------------------------------------------------------
  247. Real ActiveBody::estimateDamage( DamageInfoInput& damageInfo ) const
  248. {
  249. validateArmorAndDamageFX();
  250. //Subdual damage can't affect you if you can't be subdued
  251. if( IsSubdualDamage(damageInfo.m_damageType) && !canBeSubdued() )
  252. return 0.0f;
  253. if( damageInfo.m_damageType == DAMAGE_KILL_GARRISONED )
  254. {
  255. ContainModuleInterface* contain = getObject()->getContain();
  256. if( contain && contain->getContainCount() > 0 && contain->isGarrisonable() && !contain->isImmuneToClearBuildingAttacks() )
  257. return 1.0f;
  258. else
  259. return 0.0f;
  260. }
  261. if( damageInfo.m_damageType == DAMAGE_SNIPER )
  262. {
  263. if( getObject()->isKindOf( KINDOF_STRUCTURE ) && getObject()->testStatus( OBJECT_STATUS_UNDER_CONSTRUCTION ) )
  264. {
  265. //If we're a pathfinder shooting a stinger site under construction... don't. Special case code.
  266. return 0.0f;
  267. }
  268. }
  269. Real amount = m_curArmor.adjustDamage(damageInfo.m_damageType, damageInfo.m_amount);
  270. return amount;
  271. }
  272. //-------------------------------------------------------------------------------------------------
  273. //-------------------------------------------------------------------------------------------------
  274. void ActiveBody::doDamageFX( const DamageInfo *damageInfo )
  275. {
  276. DamageType damageTypeToUse = damageInfo->in.m_damageType;
  277. if (damageInfo->in.m_damageFXOverride != DAMAGE_UNRESISTABLE )
  278. {
  279. // Just the visual aspect of damage can be overridden in some cases.
  280. // Unresistable is the default to mean no override, as we are out of bits.
  281. damageTypeToUse = damageInfo->in.m_damageFXOverride;
  282. }
  283. if (m_curDamageFX)
  284. {
  285. UnsignedInt now = TheGameLogic->getFrame();
  286. if (damageTypeToUse == m_lastDamageFXDone && m_nextDamageFXTime > now)
  287. return;
  288. Object *source = TheGameLogic->findObjectByID(damageInfo->in.m_sourceID); // might be null, I guess
  289. m_lastDamageFXDone = damageTypeToUse;
  290. m_nextDamageFXTime = now + m_curDamageFX->getDamageFXThrottleTime(damageTypeToUse, source);
  291. m_curDamageFX->doDamageFX(damageTypeToUse, damageInfo->out.m_actualDamageDealt, source, getObject());
  292. }
  293. }
  294. //-------------------------------------------------------------------------------------------------
  295. //-------------------------------------------------------------------------------------------------
  296. void ActiveBody::attemptDamage( DamageInfo *damageInfo )
  297. {
  298. validateArmorAndDamageFX();
  299. // sanity
  300. if( damageInfo == NULL )
  301. return;
  302. if ( m_indestructible )
  303. return;
  304. // initialize these, just in case we bail out early
  305. damageInfo->out.m_actualDamageDealt = 0.0f;
  306. damageInfo->out.m_actualDamageClipped = 0.0f;
  307. // we cannot damage again objects that are already dead
  308. Object* obj = getObject();
  309. if( obj->isEffectivelyDead() )
  310. return;
  311. Object *damager = TheGameLogic->findObjectByID( damageInfo->in.m_sourceID );
  312. if( damager )
  313. {
  314. //Store the template so later if the attacking object dies, we use script conditions to look at the
  315. //damager's template inside evaluateTeamAttackedByType or evaluateNameAttackedByType.
  316. damageInfo->in.m_sourceTemplate = damager->getTemplate();
  317. }
  318. Bool alreadyHandled = FALSE;
  319. Bool allowModifier = TRUE;
  320. Real amount = m_curArmor.adjustDamage(damageInfo->in.m_damageType, damageInfo->in.m_amount);
  321. switch( damageInfo->in.m_damageType )
  322. {
  323. case DAMAGE_HEALING:
  324. {
  325. if( !damageInfo->in.m_kill )
  326. {
  327. // Healing and Damage are separate, so this shouldn't happen
  328. attemptHealing( damageInfo );
  329. }
  330. return;
  331. }
  332. case DAMAGE_KILLPILOT:
  333. {
  334. // This type of damage doesn't actually damage the unit, but it does kill it's
  335. // pilot, in the case of a vehicle.
  336. if( obj->isKindOf( KINDOF_VEHICLE ) )
  337. {
  338. //Handle special case for combat bike. We actually will kill the bike by
  339. //forcing the rider to leave the bike. That way the bike will automatically
  340. //scuttle and be unusable.
  341. ContainModuleInterface *contain = obj->getContain();
  342. if( contain && contain->isRiderChangeContain() )
  343. {
  344. AIUpdateInterface *ai = obj->getAI();
  345. if( ai->isMoving() )
  346. {
  347. //Bike is moving, so just blow it up instead.
  348. if (damager)
  349. damager->scoreTheKill( obj );
  350. obj->kill();
  351. }
  352. else
  353. {
  354. //Removing the rider will scuttle the bike.
  355. Object *rider = *(contain->getContainedItemsList()->begin());
  356. ai->aiEvacuateInstantly( TRUE, CMD_FROM_AI );
  357. //Kill the rider.
  358. if (damager)
  359. damager->scoreTheKill( rider );
  360. rider->kill();
  361. }
  362. }
  363. else
  364. {
  365. // Make it unmanned, so units can easily check the ability to "take control of it"
  366. obj->setDisabled( DISABLED_UNMANNED );
  367. TheGameLogic->deselectObject(obj, PLAYERMASK_ALL, TRUE);
  368. if ( obj->getAI() )
  369. obj->getAI()->aiIdle( CMD_FROM_AI );
  370. // Convert it to the neutral team so it renders gray giving visual representation that it is unmanned.
  371. obj->setTeam( ThePlayerList->getNeutralPlayer()->getDefaultTeam() );
  372. }
  373. //We don't care which team sniped the vehicle... we use this information to flag whether or not
  374. //we captured a vehicle.
  375. ThePlayerList->getNeutralPlayer()->getAcademyStats()->recordVehicleSniped();
  376. }
  377. alreadyHandled = TRUE;
  378. allowModifier = FALSE;
  379. break;
  380. }
  381. case DAMAGE_KILL_GARRISONED:
  382. {
  383. // KRIS: READ THIS!!!
  384. // This code is very misleading (but in a good way). One would think this is
  385. // an excellent place to add the hook to kill garrisoned troops. And that is
  386. // a correct assumption. Unfortunately, the vast majority of garrison slayings
  387. // are performed in DumbProjectileBehavior::projectileHandleCollision(), so my
  388. // hope is that this message will save you some research time!
  389. Int killsToMake = REAL_TO_INT_FLOOR(damageInfo->in.m_amount);
  390. ContainModuleInterface* contain = obj->getContain();
  391. if( contain && contain->getContainCount() > 0 && contain->isGarrisonable() && !contain->isImmuneToClearBuildingAttacks() )
  392. {
  393. Int numKilled = 0;
  394. // garrisonable buildings subvert the normal process here.
  395. const ContainedItemsList* items = contain->getContainedItemsList();
  396. if (items)
  397. {
  398. for( ContainedItemsList::const_iterator it = items->begin(); (it != items->end()) && (numKilled < killsToMake); it++ )
  399. {
  400. Object* thingToKill = *it;
  401. if (!thingToKill->isEffectivelyDead() )
  402. {
  403. if (damager)
  404. damager->scoreTheKill( thingToKill );
  405. thingToKill->kill();
  406. ++numKilled;
  407. thingToKill->getControllingPlayer()->getAcademyStats()->recordClearedGarrisonedBuilding();
  408. }
  409. } // next contained item
  410. } // if items
  411. } // if a garrisonable thing
  412. alreadyHandled = TRUE;
  413. allowModifier = FALSE;
  414. break;
  415. }
  416. case DAMAGE_STATUS:
  417. {
  418. // Damage amount is msec time we set the status given in damageStatusType
  419. Real realFramesToStatusFor = ConvertDurationFromMsecsToFrames(amount);
  420. obj->doStatusDamage( damageInfo->in.m_damageStatusType , REAL_TO_INT_CEIL(realFramesToStatusFor) );
  421. alreadyHandled = TRUE;
  422. allowModifier = FALSE;
  423. break;
  424. }
  425. }
  426. if( IsSubdualDamage(damageInfo->in.m_damageType) )
  427. {
  428. if( !canBeSubdued() )
  429. return;
  430. Bool wasSubdued = isSubdued();
  431. internalAddSubdualDamage(amount);
  432. Bool nowSubdued = isSubdued();
  433. alreadyHandled = TRUE;
  434. allowModifier = FALSE;
  435. if( wasSubdued != nowSubdued )
  436. {
  437. onSubdualChange(nowSubdued);
  438. }
  439. getObject()->notifySubdualDamage(amount);
  440. }
  441. if (allowModifier)
  442. {
  443. if( damageInfo->in.m_damageType != DAMAGE_UNRESISTABLE )
  444. {
  445. // Apply the damage scalar (extra bonuses -- like strategy center defensive battle plan)
  446. // And remember not to adjust unresistable damage, just like the armor code can't.
  447. amount *= m_damageScalar;
  448. }
  449. }
  450. // sanity check the damage value -- can't apply negative damage
  451. if( amount > 0.0f || damageInfo->in.m_kill )
  452. {
  453. BodyDamageType oldState = m_curDamageState;
  454. //If the object is going to die, make sure we damage all remaining health.
  455. if( damageInfo->in.m_kill )
  456. {
  457. amount = m_currentHealth;
  458. }
  459. if (!alreadyHandled)
  460. {
  461. // do the damage simplistic damage subtraction
  462. internalChangeHealth( -amount );
  463. }
  464. #ifdef ALLOW_SURRENDER
  465. //*****************************************************************************************
  466. //*****************************************************************************************
  467. //THIS CODE HAS BEEN DISABLED FOR THE MULTIPLAYER PLAY TEST!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!**
  468. //*****************************************************************************************
  469. // // if we were "killed" by surrender damage...
  470. // if (damageInfo->in.m_damageType == DAMAGE_SURRENDER && m_currentHealth <= 0.0f && obj->isKindOf(KINDOF_CAN_SURRENDER))
  471. // {
  472. // AIUpdateInterface* ai = obj->getAIUpdateInterface();
  473. // if (ai)
  474. // {
  475. // // do no damage, but make it surrender instead.
  476. // m_currentHealth = m_prevHealth;
  477. // const Object* killer = TheGameLogic->findObjectByID( damageInfo->in.m_sourceID );
  478. // ai->setSurrendered(killer, true);
  479. // return;
  480. // }
  481. // }
  482. //*****************************************************************************************
  483. //*****************************************************************************************
  484. #endif
  485. // record the actual damage done from this, and when it happened
  486. damageInfo->out.m_actualDamageDealt = amount;
  487. damageInfo->out.m_actualDamageClipped = m_prevHealth - m_currentHealth;
  488. // then copy the whole DamageInfo struct for easy lookup
  489. // (object pointer loses scope as soon as atteptdamage's caller ends)
  490. // m_lastDamageTimestamp is initialized to FFFFFFFFFF, so doing a < compare is problematic.
  491. // jba.
  492. if (m_lastDamageTimestamp!=TheGameLogic->getFrame() && m_lastDamageTimestamp != TheGameLogic->getFrame()-1) {
  493. m_lastDamageInfo = *damageInfo;
  494. m_lastDamageCleared = false;
  495. m_lastDamageTimestamp = TheGameLogic->getFrame();
  496. } else {
  497. // Multiple damages applied in one/next frame. We prefer the one that tells who the attacker is.
  498. Object *srcObj1 = TheGameLogic->findObjectByID(m_lastDamageInfo.in.m_sourceID);
  499. Object *srcObj2 = TheGameLogic->findObjectByID(damageInfo->in.m_sourceID);
  500. if (srcObj2) {
  501. if (srcObj1) {
  502. if (srcObj2->isKindOf(KINDOF_VEHICLE) || srcObj2->isKindOf(KINDOF_INFANTRY) ||
  503. srcObj2->isFactionStructure()) {
  504. m_lastDamageInfo = *damageInfo;
  505. m_lastDamageCleared = false;
  506. m_lastDamageTimestamp = TheGameLogic->getFrame();
  507. }
  508. } else {
  509. m_lastDamageInfo = *damageInfo;
  510. m_lastDamageCleared = false;
  511. m_lastDamageTimestamp = TheGameLogic->getFrame();
  512. }
  513. } else {
  514. // no change.
  515. }
  516. }
  517. // Notify the player that they have been attacked by this player
  518. if (m_lastDamageInfo.in.m_sourceID != INVALID_ID)
  519. {
  520. Object *srcObj = TheGameLogic->findObjectByID(m_lastDamageInfo.in.m_sourceID);
  521. if (srcObj)
  522. {
  523. Player *srcPlayer = srcObj->getControllingPlayer();
  524. obj->getControllingPlayer()->setAttackedBy(srcPlayer->getPlayerIndex());
  525. }
  526. }
  527. // if our health has gone down then do run the damage module callback
  528. if( m_currentHealth < m_prevHealth )
  529. {
  530. for (BehaviorModule** m = obj->getBehaviorModules(); *m; ++m)
  531. {
  532. DamageModuleInterface* d = (*m)->getDamage();
  533. if (!d)
  534. continue;
  535. d->onDamage( damageInfo );
  536. }
  537. }
  538. if (m_curDamageState != oldState)
  539. {
  540. for (BehaviorModule** m = obj->getBehaviorModules(); *m; ++m)
  541. {
  542. DamageModuleInterface* d = (*m)->getDamage();
  543. if (!d)
  544. continue;
  545. d->onBodyDamageStateChange( damageInfo, oldState, m_curDamageState );
  546. }
  547. // @todo: This really feels like it should be in the TransitionFX lists.
  548. if (m_curDamageState == BODY_DAMAGED)
  549. {
  550. AudioEventRTS damaged = *obj->getTemplate()->getSoundOnDamaged();
  551. damaged.setObjectID(obj->getID());
  552. TheAudio->addAudioEvent(&damaged);
  553. }
  554. else if (m_curDamageState == BODY_REALLYDAMAGED)
  555. {
  556. AudioEventRTS reallyDamaged = *obj->getTemplate()->getSoundOnReallyDamaged();
  557. reallyDamaged.setObjectID(obj->getID());
  558. TheAudio->addAudioEvent(&reallyDamaged);
  559. }
  560. }
  561. // Should we play our fear sound?
  562. if( (m_prevHealth / m_maxHealth) > YELLOW_DAMAGE_PERCENT &&
  563. (m_currentHealth / m_maxHealth) < YELLOW_DAMAGE_PERCENT &&
  564. (m_currentHealth > 0) )
  565. {
  566. // 25% chance to play
  567. if (GameLogicRandomValue(0, 99) < 25)
  568. {
  569. AudioEventRTS fearSound = *obj->getTemplate()->getVoiceFear();
  570. fearSound.setPosition( obj->getPosition() );
  571. fearSound.setPlayerIndex( obj->getControllingPlayer()->getPlayerIndex() );
  572. TheAudio->addAudioEvent(&fearSound);
  573. }
  574. }
  575. // check to see if we died
  576. if( m_currentHealth <= 0 && m_prevHealth > 0 )
  577. {
  578. // Give our killer credit for killing us, if there is one.
  579. if( damager )
  580. {
  581. damager->scoreTheKill( obj );
  582. }
  583. obj->onDie( damageInfo );
  584. }
  585. }
  586. doDamageFX(damageInfo);
  587. // Damaged repulsable civilians scare (repulse) other civs. jba.
  588. if( TheAI->getAiData()->m_enableRepulsors )
  589. {
  590. if( obj->isKindOf( KINDOF_CAN_BE_REPULSED ) )
  591. {
  592. obj->setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_REPULSOR ) );
  593. }
  594. }
  595. //Retaliate, even if I'm dead -- we'll still get my nearby friends to get revenge!!!
  596. //Also only retaliate if we're controlled by a human player and the thing that attacked me
  597. //is an enemy.
  598. Player *controllingPlayer = obj->getControllingPlayer();
  599. if( controllingPlayer && controllingPlayer->isLogicalRetaliationModeEnabled() && controllingPlayer->getPlayerType() == PLAYER_HUMAN )
  600. {
  601. if( shouldRetaliateAgainstAggressor(obj, damager))
  602. {
  603. PartitionFilterPlayerAffiliation f1( controllingPlayer, ALLOW_ALLIES, true );
  604. PartitionFilterOnMap filterMapStatus;
  605. PartitionFilter *filters[] = { &f1, &filterMapStatus, 0 };
  606. Real distance = TheAI->getAiData()->m_retaliateFriendsRadius + obj->getGeometryInfo().getBoundingCircleRadius();
  607. SimpleObjectIterator *iter = ThePartitionManager->iterateObjectsInRange( obj->getPosition(), distance, FROM_CENTER_2D, filters, ITER_FASTEST );
  608. MemoryPoolObjectHolder hold( iter );
  609. for( Object *them = iter->first(); them; them = iter->next() )
  610. {
  611. if (!shouldRetaliate(them)) {
  612. continue;
  613. }
  614. AIUpdateInterface *ai = them->getAI();
  615. if (ai==NULL) {
  616. continue;
  617. }
  618. //If we have AI and we're mobile, then assist!
  619. if( !them->isKindOf( KINDOF_IMMOBILE ))
  620. {
  621. //But only if we can attack it!
  622. CanAttackResult result = them->getAbleToAttackSpecificObject( ATTACK_NEW_TARGET, damager, CMD_FROM_AI );
  623. if( result == ATTACKRESULT_POSSIBLE_AFTER_MOVING || result == ATTACKRESULT_POSSIBLE )
  624. {
  625. ai->aiGuardRetaliate( damager, them->getPosition(), NO_MAX_SHOTS_LIMIT, CMD_FROM_AI );
  626. }
  627. }
  628. }
  629. }
  630. }
  631. }
  632. //-------------------------------------------------------------------------------------------------
  633. //-------------------------------------------------------------------------------------------------
  634. Bool ActiveBody::shouldRetaliateAgainstAggressor(Object *obj, Object *damager)
  635. {
  636. /* This considers whether obj should invoke his friends to retaliate against damager.
  637. Note that obj could be a structure, so we don't actually check whether obj will
  638. retaliate, as in many cases he wouldn't. */
  639. if (damager==NULL) {
  640. return false;
  641. }
  642. if (damager->isAirborneTarget()) {
  643. return false; // Don't retaliate against aircraft. [8/25/2003]
  644. }
  645. if (damager->getRelationship( obj ) != ENEMIES) {
  646. return false; // only retaliate against enemies.
  647. }
  648. Real distSqr = ThePartitionManager->getDistanceSquared(obj, damager, FROM_BOUNDINGSPHERE_2D);
  649. if (distSqr > sqr(TheAI->getAiData()->m_maxRetaliateDistance)) {
  650. return false;
  651. }
  652. // Only human players retaliate. [8/25/2003]
  653. if (obj->getControllingPlayer()->getPlayerType() != PLAYER_HUMAN) {
  654. return false;
  655. }
  656. // Drones never retaliate. [8/25/2003]
  657. if (obj->isKindOf(KINDOF_DRONE)) {
  658. return false;
  659. }
  660. return true;
  661. }
  662. //-------------------------------------------------------------------------------------------------
  663. //-------------------------------------------------------------------------------------------------
  664. Bool ActiveBody::shouldRetaliate(Object *obj)
  665. {
  666. // Cannot retaliate objects dont. [8/25/2003]
  667. if (obj->isKindOf(KINDOF_CANNOT_RETALIATE)) {
  668. return false;
  669. }
  670. if (obj->isKindOf( KINDOF_IMMOBILE )) {
  671. return false;
  672. }
  673. // Drones never retaliate. [8/25/2003]
  674. if (obj->isKindOf(KINDOF_DRONE)) {
  675. return false;
  676. }
  677. // Any unit that isn't idle won't retaliate. [8/25/2003]
  678. if (obj->getAI()) {
  679. if (!obj->getAI()->isIdle()) {
  680. return false;
  681. }
  682. } else {
  683. return false; // Non-ai can't retaliate. [8/26/2003]
  684. }
  685. // Stealthed units don't retaliate unless they're detected. [8/25/2003]
  686. if ( obj->getStatusBits().test( OBJECT_STATUS_STEALTHED ) &&
  687. !obj->getStatusBits().test( OBJECT_STATUS_DETECTED ) ) {
  688. return false;
  689. }
  690. // If we're using an ability, don't stop. [8/25/2003]
  691. if (obj->testStatus(OBJECT_STATUS_IS_USING_ABILITY)) {
  692. return false;
  693. }
  694. return true;
  695. }
  696. //-------------------------------------------------------------------------------------------------
  697. //-------------------------------------------------------------------------------------------------
  698. void ActiveBody::attemptHealing( DamageInfo *damageInfo )
  699. {
  700. validateArmorAndDamageFX();
  701. // sanity
  702. if( damageInfo == NULL )
  703. return;
  704. if( damageInfo->in.m_damageType != DAMAGE_HEALING )
  705. {
  706. // Healing and Damage are separate, so this shouldn't happen
  707. attemptDamage( damageInfo );
  708. return;
  709. }
  710. Object* obj = getObject();
  711. // srj sez: sorry, once yer dead, yer dead.
  712. // Special case for bridges, cause the system now things they're dead
  713. ///@todo we need to figure out what has changed so we don't have to hack this (CBD 11-1-2002)
  714. if( obj->isKindOf( KINDOF_BRIDGE ) == FALSE &&
  715. obj->isKindOf( KINDOF_BRIDGE_TOWER ) == FALSE &&
  716. obj->isEffectivelyDead())
  717. return;
  718. // initialize these, just in case we bail out early
  719. damageInfo->out.m_actualDamageDealt = 0.0f;
  720. damageInfo->out.m_actualDamageClipped = 0.0f;
  721. Real amount = m_curArmor.adjustDamage(damageInfo->in.m_damageType, damageInfo->in.m_amount);
  722. // sanity check the damage value -- can't apply negative healing
  723. if( amount > 0.0f )
  724. {
  725. BodyDamageType oldState = m_curDamageState;
  726. // do the damage simplistic damage ADDITION
  727. internalChangeHealth( amount );
  728. // record the actual damage done from this, and when it happened
  729. damageInfo->out.m_actualDamageDealt = amount;
  730. damageInfo->out.m_actualDamageClipped = m_prevHealth - m_currentHealth;
  731. //then copy the whole DamageInfo struct for easy lookup
  732. //(object pointer loses scope as soon as atteptdamage's caller ends)
  733. m_lastDamageInfo = *damageInfo;
  734. m_lastDamageCleared = false;
  735. m_lastDamageTimestamp = TheGameLogic->getFrame();
  736. m_lastHealingTimestamp = TheGameLogic->getFrame();
  737. // if our health has gone UP then do run the damage module callback
  738. if( m_currentHealth > m_prevHealth )
  739. {
  740. for (BehaviorModule** m = obj->getBehaviorModules(); *m; ++m)
  741. {
  742. DamageModuleInterface* d = (*m)->getDamage();
  743. if (!d)
  744. continue;
  745. d->onHealing( damageInfo );
  746. }
  747. }
  748. if (m_curDamageState != oldState)
  749. {
  750. for (BehaviorModule** m = obj->getBehaviorModules(); *m; ++m)
  751. {
  752. DamageModuleInterface* d = (*m)->getDamage();
  753. if (!d)
  754. continue;
  755. d->onBodyDamageStateChange( damageInfo, oldState, m_curDamageState );
  756. }
  757. }
  758. }
  759. doDamageFX(damageInfo);
  760. }
  761. //-------------------------------------------------------------------------------------------------
  762. /** Simple setting of the health value, it does *NOT* track any transition
  763. * states for the event of "damage" or the event of "death". */
  764. //-------------------------------------------------------------------------------------------------
  765. void ActiveBody::setInitialHealth(Int initialPercent)
  766. {
  767. // save the current health as the previous health
  768. m_prevHealth = m_currentHealth;
  769. Real factor = initialPercent/100.0f;
  770. Real newHealth = factor * m_initialHealth;
  771. // change the health to the requested percentage.
  772. internalChangeHealth(newHealth - m_currentHealth);
  773. }
  774. //-------------------------------------------------------------------------------------------------
  775. /** Simple setting of the health value, it does *NOT* track any transition
  776. * states for the event of "damage" or the event of "death". */
  777. //-------------------------------------------------------------------------------------------------
  778. void ActiveBody::setMaxHealth( Real maxHealth, MaxHealthChangeType healthChangeType )
  779. {
  780. Real prevMaxHealth = m_maxHealth;
  781. m_maxHealth = maxHealth;
  782. m_initialHealth = maxHealth;
  783. switch( healthChangeType )
  784. {
  785. case PRESERVE_RATIO:
  786. {
  787. //400/500 (80%) + 100 becomes 480/600 (80%)
  788. //200/500 (40%) - 100 becomes 160/400 (40%)
  789. Real ratio = m_currentHealth / prevMaxHealth;
  790. Real newHealth = maxHealth * ratio;
  791. internalChangeHealth( newHealth - m_currentHealth );
  792. break;
  793. }
  794. case ADD_CURRENT_HEALTH_TOO:
  795. {
  796. //Add the same amount that we are adding to the max health.
  797. //This could kill you if max health is reduced (if we ever have that ability to add buffer health like in D&D)
  798. //400/500 (80%) + 100 becomes 500/600 (83%)
  799. //200/500 (40%) - 100 becomes 100/400 (25%)
  800. internalChangeHealth( maxHealth - prevMaxHealth );
  801. break;
  802. }
  803. case SAME_CURRENTHEALTH:
  804. //do nothing
  805. break;
  806. case FULLY_HEAL:
  807. {
  808. // Set current to the new Max.
  809. //400/500 (80%) + 100 becomes 600/600 (100%)
  810. //200/500 (40%) - 100 becomes 400/400 (100%)
  811. internalChangeHealth(m_maxHealth - m_currentHealth);
  812. break;
  813. }
  814. }
  815. //
  816. // when max health is getting clipped to a lower value, if our current health
  817. // value is now outside of the max health range we will set it back down to the
  818. // new cap. Note that we are *NOT* going through any healing or damage methods here
  819. // and are doing a direct set
  820. //
  821. if( m_currentHealth > maxHealth )
  822. {
  823. internalChangeHealth( maxHealth - m_currentHealth );
  824. }
  825. }
  826. // ------------------------------------------------------------------------------------------------
  827. /** Given the current damage state of the object, evaluate the visual model conditions
  828. * that have a visual impact on the object */
  829. // ------------------------------------------------------------------------------------------------
  830. void ActiveBody::evaluateVisualCondition()
  831. {
  832. Drawable* draw = getObject()->getDrawable();
  833. if (draw)
  834. {
  835. draw->reactToBodyDamageStateChange(m_curDamageState);
  836. }
  837. //
  838. // destroy any particle systems that were attached to our body for the old state
  839. // and create new particle systems for the new state
  840. //
  841. updateBodyParticleSystems();
  842. }
  843. // ------------------------------------------------------------------------------------------------
  844. /** Create up to maxSystems particle systems of type particleSystemName and attach to bones
  845. * specified by the bone base name. If there are more bones than maxSystems then the
  846. * bones will be randomly selected */
  847. // ------------------------------------------------------------------------------------------------
  848. void ActiveBody::createParticleSystems( const AsciiString &boneBaseName,
  849. const ParticleSystemTemplate *systemTemplate,
  850. Int maxSystems )
  851. {
  852. Object *us = getObject();
  853. // sanity
  854. if( systemTemplate == NULL )
  855. return;
  856. // get the bones
  857. enum { MAX_BONES = 16 };
  858. Coord3D bonePositions[ MAX_BONES ];
  859. Int numBones = us->getMultiLogicalBonePosition( boneBaseName.str(),
  860. MAX_BONES,
  861. bonePositions,
  862. NULL,
  863. FALSE );
  864. // if no bones found nothing else to do
  865. if( numBones == 0 )
  866. return;
  867. //
  868. // if we don't have enough bones to go up to maxSystems, we will change maxSystems to be
  869. // the number of bones we actually have (we don't want systems doubling up on bones)
  870. //
  871. if( numBones < maxSystems )
  872. maxSystems = numBones;
  873. //
  874. // create an array that we'll use to mark which bone positions have already been used,
  875. // this is necessary when we have more bones than particle systems we're going to
  876. // create, in which case we place the particle systems at random bone locations
  877. // but don't want to repeat any
  878. //
  879. Bool usedBoneIndices[ MAX_BONES ] = { FALSE };
  880. // create the particle systems
  881. const Coord3D *pos;
  882. for( Int i = 0; i < maxSystems; ++i )
  883. {
  884. // pick a bone index to place this particle system at
  885. // MDC: moving to GameLogicRandomValue. This does not need to be synced, but having it so makes searches *so* much nicer.
  886. // DTEH: Moved back to GameClientRandomValue because of desync problems. July 27th 2003.
  887. Int boneIndex = GameClientRandomValue( 0, maxSystems - i - 1 );
  888. // find the actual bone location to use and mark that bone index as used
  889. Int count = 0;
  890. for( Int j = 0; j < numBones; j++ )
  891. {
  892. // ignore bone positions that have already been used
  893. if( usedBoneIndices[ j ] == TRUE )
  894. continue;
  895. // this spot is available, if count == boneIndex then use this index
  896. if( count == boneIndex )
  897. {
  898. pos = &bonePositions[ j ];
  899. usedBoneIndices[ j ] = TRUE;
  900. break; // exit for j
  901. } // end if
  902. else
  903. {
  904. // we won't use this index, increment count until we find a suitable index to use
  905. ++count;
  906. } // end else
  907. } // end for, j
  908. // sanity
  909. DEBUG_ASSERTCRASH( j != numBones,
  910. ("ActiveBody::createParticleSystems, Unable to select particle system index\n") );
  911. // create particle system here
  912. ParticleSystem *particleSystem = TheParticleSystemManager->createParticleSystem( systemTemplate );
  913. if( particleSystem )
  914. {
  915. // set the position of the particle system in local object space
  916. particleSystem->setPosition( pos );
  917. // attach particle system to object
  918. particleSystem->attachToObject( us );
  919. // create a new body particle system entry and keep this particle system in it
  920. BodyParticleSystem *newEntry = newInstance(BodyParticleSystem);
  921. newEntry->m_particleSystemID = particleSystem->getSystemID();
  922. newEntry->m_next = m_particleSystems;
  923. m_particleSystems = newEntry;
  924. } // end if
  925. } // end for, i
  926. } // end createParticleSystems
  927. // ------------------------------------------------------------------------------------------------
  928. /** Delete all the body particle systems */
  929. // ------------------------------------------------------------------------------------------------
  930. void ActiveBody::deleteAllParticleSystems( void )
  931. {
  932. BodyParticleSystem *nextBodySystem;
  933. ParticleSystem *particleSystem;
  934. while( m_particleSystems )
  935. {
  936. // get this particle system
  937. particleSystem = TheParticleSystemManager->findParticleSystem( m_particleSystems->m_particleSystemID );
  938. if( particleSystem )
  939. particleSystem->destroy();
  940. // get next system in the body
  941. nextBodySystem = m_particleSystems->m_next;
  942. // destroy this entry
  943. m_particleSystems->deleteInstance();
  944. // set the body systems head to the next
  945. m_particleSystems = nextBodySystem;
  946. } // end while
  947. } // end deleteAllParticleSystems
  948. // ------------------------------------------------------------------------------------------------
  949. /* This function is called on state changes only. Body Type or Aflameness. */
  950. // ------------------------------------------------------------------------------------------------
  951. void ActiveBody::updateBodyParticleSystems( void )
  952. {
  953. static const ParticleSystemTemplate *fireSmallTemplate = TheParticleSystemManager->findTemplate( TheGlobalData->m_autoFireParticleSmallSystem );
  954. static const ParticleSystemTemplate *fireMediumTemplate = TheParticleSystemManager->findTemplate( TheGlobalData->m_autoFireParticleMediumSystem );
  955. static const ParticleSystemTemplate *fireLargeTemplate = TheParticleSystemManager->findTemplate( TheGlobalData->m_autoFireParticleLargeSystem );
  956. static const ParticleSystemTemplate *smokeSmallTemplate = TheParticleSystemManager->findTemplate( TheGlobalData->m_autoSmokeParticleSmallSystem );
  957. static const ParticleSystemTemplate *smokeMediumTemplate = TheParticleSystemManager->findTemplate( TheGlobalData->m_autoSmokeParticleMediumSystem );
  958. static const ParticleSystemTemplate *smokeLargeTemplate = TheParticleSystemManager->findTemplate( TheGlobalData->m_autoSmokeParticleLargeSystem );
  959. static const ParticleSystemTemplate *aflameTemplate = TheParticleSystemManager->findTemplate( TheGlobalData->m_autoAflameParticleSystem );
  960. Int countModifier;
  961. const ParticleSystemTemplate *fireSmall;
  962. const ParticleSystemTemplate *fireMedium;
  963. const ParticleSystemTemplate *fireLarge;
  964. const ParticleSystemTemplate *smokeSmall;
  965. const ParticleSystemTemplate *smokeMedium;
  966. const ParticleSystemTemplate *smokeLarge;
  967. //
  968. // when we're aflame, we use a slightly different set of particle systems that are
  969. // auto created that lends itself to more fire and bigger fire
  970. //
  971. if( getObject()->testStatus( OBJECT_STATUS_AFLAME ) )
  972. {
  973. fireSmall = fireMediumTemplate; // small fire becomes medium fire
  974. fireMedium = fireLargeTemplate; // medium fire becomes large fire
  975. fireLarge = fireLargeTemplate; // large fire stays large
  976. smokeSmall = fireSmallTemplate; // small smoke becomes small fire
  977. smokeMedium = fireSmallTemplate; // medium smoke becomes small fire
  978. smokeLarge = fireSmallTemplate; // large smoke becomes small fire
  979. // we get to make more of them all too
  980. countModifier = 2;
  981. } // end if
  982. else
  983. {
  984. // use regular templates
  985. fireSmall = fireSmallTemplate;
  986. fireMedium = fireMediumTemplate;
  987. fireLarge = fireLargeTemplate;
  988. smokeSmall = smokeSmallTemplate;
  989. smokeMedium = smokeMediumTemplate;
  990. smokeLarge = smokeLargeTemplate;
  991. // we make just the normal amount of these
  992. countModifier = 1;
  993. } // end else
  994. //
  995. // remove any particle systems we have currently in the list in favor of any new ones
  996. // that we're going to autopopulate ourselves with
  997. //
  998. deleteAllParticleSystems();
  999. //
  1000. // create particle systems for the new body state
  1001. //
  1002. // small fire bones
  1003. createParticleSystems( TheGlobalData->m_autoFireParticleSmallPrefix,
  1004. fireSmall, TheGlobalData->m_autoFireParticleSmallMax * countModifier );
  1005. // medium fire bones
  1006. createParticleSystems( TheGlobalData->m_autoFireParticleMediumPrefix,
  1007. fireMedium, TheGlobalData->m_autoFireParticleMediumMax * countModifier );
  1008. // large fire bones
  1009. createParticleSystems( TheGlobalData->m_autoFireParticleLargePrefix,
  1010. fireLarge, TheGlobalData->m_autoFireParticleLargeMax * countModifier );
  1011. // small smoke bones
  1012. createParticleSystems( TheGlobalData->m_autoSmokeParticleSmallPrefix,
  1013. smokeSmall, TheGlobalData->m_autoSmokeParticleSmallMax * countModifier );
  1014. // medium smoke bones
  1015. createParticleSystems( TheGlobalData->m_autoSmokeParticleMediumPrefix,
  1016. smokeMedium, TheGlobalData->m_autoSmokeParticleMediumMax * countModifier );
  1017. // large smoke bones
  1018. createParticleSystems( TheGlobalData->m_autoSmokeParticleLargePrefix,
  1019. smokeLarge, TheGlobalData->m_autoSmokeParticleLargeMax * countModifier );
  1020. // actively on fire
  1021. if( getObject()->testStatus( OBJECT_STATUS_AFLAME ) )
  1022. createParticleSystems( TheGlobalData->m_autoAflameParticlePrefix,
  1023. aflameTemplate, TheGlobalData->m_autoAflameParticleMax * countModifier );
  1024. } // end updatebodyParticleSystems
  1025. //-------------------------------------------------------------------------------------------------
  1026. /** Simple changing of the health value, it does *NOT* track any transition
  1027. * states for the event of "damage" or the event of "death". If you
  1028. * with to kill an object and give these modules a chance to react
  1029. * to that event use the proper damage method calls.
  1030. * No game logic should go in here. This is the low level math and flag maintenance.
  1031. * Game stuff goes in attemptDamage and attemptHealing.
  1032. */
  1033. //-------------------------------------------------------------------------------------------------
  1034. void ActiveBody::internalChangeHealth( Real delta )
  1035. {
  1036. // save the current health as the previous health
  1037. m_prevHealth = m_currentHealth;
  1038. // change the health by the delta, it can be positive or negative
  1039. m_currentHealth += delta;
  1040. // high end cap
  1041. Real maxHealth = m_maxHealth;
  1042. if( m_currentHealth > maxHealth )
  1043. m_currentHealth = maxHealth;
  1044. // low end cap
  1045. const Real lowEndCap = 0.0f; // low end cap for health, don't go below this
  1046. if( m_currentHealth < lowEndCap )
  1047. m_currentHealth = lowEndCap;
  1048. // recalc the damage state
  1049. BodyDamageType oldState = m_curDamageState;
  1050. setCorrectDamageState();
  1051. // if our state has changed
  1052. if( m_curDamageState != oldState )
  1053. {
  1054. //
  1055. // show a visual change in the model for the damage state, we do not show visual changes
  1056. // for damage states when things are under construction because we just don't have
  1057. // all the art states for that during buildup animation
  1058. //
  1059. if( !getObject()->getStatusBits().test( OBJECT_STATUS_UNDER_CONSTRUCTION ) )
  1060. evaluateVisualCondition();
  1061. } // end if
  1062. // mark the bit according to our health. (if our AI is dead but our health improves, it will
  1063. // still re-flag this bit in the AIDeadState every frame.)
  1064. getObject()->setEffectivelyDead(m_currentHealth <= 0);
  1065. }
  1066. //-------------------------------------------------------------------------------------------------
  1067. //-------------------------------------------------------------------------------------------------
  1068. void ActiveBody::internalAddSubdualDamage( Real delta )
  1069. {
  1070. const ActiveBodyModuleData *data = getActiveBodyModuleData();
  1071. m_currentSubdualDamage += delta;
  1072. m_currentSubdualDamage = min(m_currentSubdualDamage, data->m_subdualDamageCap);
  1073. }
  1074. //-------------------------------------------------------------------------------------------------
  1075. //-------------------------------------------------------------------------------------------------
  1076. Bool ActiveBody::canBeSubdued() const
  1077. {
  1078. // Any body with subdue listings can be subdued.
  1079. return getActiveBodyModuleData()->m_subdualDamageCap > 0;
  1080. }
  1081. //-------------------------------------------------------------------------------------------------
  1082. //-------------------------------------------------------------------------------------------------
  1083. void ActiveBody::onSubdualChange( Bool isNowSubdued )
  1084. {
  1085. if( !getObject()->isKindOf(KINDOF_PROJECTILE) )
  1086. {
  1087. Object *me = getObject();
  1088. if( isNowSubdued )
  1089. {
  1090. me->setDisabled(DISABLED_SUBDUED);
  1091. ContainModuleInterface *contain = me->getContain();
  1092. if ( contain )
  1093. contain->orderAllPassengersToIdle( CMD_FROM_AI );
  1094. }
  1095. else
  1096. {
  1097. me->clearDisabled(DISABLED_SUBDUED);
  1098. if( me->isKindOf( KINDOF_FS_INTERNET_CENTER ) )
  1099. {
  1100. //Kris: October 20, 2003 - Patch 1.01
  1101. //Any unit inside an internet center is a hacker! Order
  1102. //them to start hacking again.
  1103. ContainModuleInterface *contain = me->getContain();
  1104. if ( contain )
  1105. contain->orderAllPassengersToHackInternet( CMD_FROM_AI );
  1106. }
  1107. }
  1108. }
  1109. else if( isNowSubdued )// There is no coming back from being jammed, and projectiles can't even heal, but this makes it clear.
  1110. {
  1111. ProjectileUpdateInterface *pui = getObject()->getProjectileUpdateInterface();
  1112. if( pui )
  1113. {
  1114. pui->projectileNowJammed();
  1115. }
  1116. }
  1117. }
  1118. //-------------------------------------------------------------------------------------------------
  1119. //-------------------------------------------------------------------------------------------------
  1120. Bool ActiveBody::isSubdued() const
  1121. {
  1122. return m_maxHealth <= m_currentSubdualDamage;
  1123. }
  1124. //-------------------------------------------------------------------------------------------------
  1125. //-------------------------------------------------------------------------------------------------
  1126. Real ActiveBody::getHealth() const
  1127. {
  1128. return m_currentHealth;
  1129. }
  1130. //-------------------------------------------------------------------------------------------------
  1131. //-------------------------------------------------------------------------------------------------
  1132. BodyDamageType ActiveBody::getDamageState() const
  1133. {
  1134. return m_curDamageState;
  1135. }
  1136. //-------------------------------------------------------------------------------------------------
  1137. //-------------------------------------------------------------------------------------------------
  1138. Real ActiveBody::getMaxHealth() const
  1139. {
  1140. return m_maxHealth;
  1141. } ///< return max health
  1142. //-------------------------------------------------------------------------------------------------
  1143. //-------------------------------------------------------------------------------------------------
  1144. UnsignedInt ActiveBody::getSubdualDamageHealRate() const
  1145. {
  1146. return getActiveBodyModuleData()->m_subdualDamageHealRate;
  1147. }
  1148. //-------------------------------------------------------------------------------------------------
  1149. //-------------------------------------------------------------------------------------------------
  1150. Real ActiveBody::getSubdualDamageHealAmount() const
  1151. {
  1152. return getActiveBodyModuleData()->m_subdualDamageHealAmount;
  1153. }
  1154. //-------------------------------------------------------------------------------------------------
  1155. //-------------------------------------------------------------------------------------------------
  1156. Bool ActiveBody::hasAnySubdualDamage() const
  1157. {
  1158. return m_currentSubdualDamage > 0;
  1159. }
  1160. //-------------------------------------------------------------------------------------------------
  1161. //-------------------------------------------------------------------------------------------------
  1162. Real ActiveBody::getInitialHealth() const
  1163. {
  1164. return m_initialHealth;
  1165. } // return initial health
  1166. // ------------------------------------------------------------------------------------------------
  1167. /** Set or unset the overridable indestructible flag in the body */
  1168. // ------------------------------------------------------------------------------------------------
  1169. void ActiveBody::setIndestructible( Bool indestructible )
  1170. {
  1171. m_indestructible = indestructible;
  1172. // for bridges, we mirror this state on its towers
  1173. Object *us = getObject();
  1174. if( us->isKindOf( KINDOF_BRIDGE ) )
  1175. {
  1176. BridgeBehaviorInterface *bbi = BridgeBehavior::getBridgeBehaviorInterfaceFromObject( us );
  1177. if( bbi )
  1178. {
  1179. Object *tower;
  1180. // get tower
  1181. for( Int i = 0; i < BRIDGE_MAX_TOWERS; ++i )
  1182. {
  1183. tower = TheGameLogic->findObjectByID( bbi->getTowerID( BridgeTowerType(i) ) );
  1184. if( tower )
  1185. {
  1186. BodyModuleInterface *body = tower->getBodyModule();
  1187. if( body )
  1188. body->setIndestructible( indestructible );
  1189. } // end if
  1190. } // end for, i
  1191. } // end if
  1192. } // end if
  1193. } // end setIndestructible
  1194. //-------------------------------------------------------------------------------------------------
  1195. //-------------------------------------------------------------------------------------------------
  1196. void ActiveBody::onVeterancyLevelChanged( VeterancyLevel oldLevel, VeterancyLevel newLevel, Bool provideFeedback )
  1197. {
  1198. if (oldLevel == newLevel)
  1199. return;
  1200. if (oldLevel < newLevel)
  1201. {
  1202. if( provideFeedback )
  1203. {
  1204. AudioEventRTS veterancyChanged;
  1205. switch (newLevel)
  1206. {
  1207. case LEVEL_VETERAN:
  1208. veterancyChanged = *getObject()->getTemplate()->getSoundPromotedVeteran();
  1209. break;
  1210. case LEVEL_ELITE:
  1211. veterancyChanged = *getObject()->getTemplate()->getSoundPromotedElite();
  1212. break;
  1213. case LEVEL_HEROIC:
  1214. veterancyChanged = *getObject()->getTemplate()->getSoundPromotedHero();
  1215. break;
  1216. }
  1217. veterancyChanged.setObjectID(getObject()->getID());
  1218. TheAudio->addAudioEvent(&veterancyChanged);
  1219. }
  1220. //Also mark the UI dirty -- incase the object is selected or contained.
  1221. Object *obj = getObject();
  1222. Drawable *draw = TheInGameUI->getFirstSelectedDrawable();
  1223. if( draw )
  1224. {
  1225. Object *checkOwner = draw->getObject();
  1226. if( checkOwner == obj )
  1227. {
  1228. //Our selected object has been promoted!
  1229. TheControlBar->markUIDirty();
  1230. }
  1231. else
  1232. {
  1233. const Object *containedBy = obj->getContainedBy();
  1234. if( containedBy && TheInGameUI->getSelectCount() == 1 )
  1235. {
  1236. Object *checkOwner = draw->getObject();
  1237. if( checkOwner == containedBy )
  1238. {
  1239. //But only if the contained by object is containing me!
  1240. TheControlBar->markUIDirty();
  1241. }
  1242. }
  1243. }
  1244. }
  1245. }
  1246. Real oldBonus = TheGlobalData->m_healthBonus[oldLevel];
  1247. Real newBonus = TheGlobalData->m_healthBonus[newLevel];
  1248. Real mult = newBonus / oldBonus;
  1249. // get this before calling setMaxHealth, since it can clip curHealth
  1250. //Real newHealth = m_currentHealth * mult;
  1251. // change the max
  1252. setMaxHealth(m_maxHealth * mult, PRESERVE_RATIO );
  1253. // now change the cur (setMaxHealth now handles it)
  1254. //internalChangeHealth( newHealth - m_currentHealth );
  1255. switch (newLevel)
  1256. {
  1257. case LEVEL_REGULAR:
  1258. clearArmorSetFlag(ARMORSET_VETERAN);
  1259. clearArmorSetFlag(ARMORSET_ELITE);
  1260. clearArmorSetFlag(ARMORSET_HERO);
  1261. break;
  1262. case LEVEL_VETERAN:
  1263. setArmorSetFlag(ARMORSET_VETERAN);
  1264. clearArmorSetFlag(ARMORSET_ELITE);
  1265. clearArmorSetFlag(ARMORSET_HERO);
  1266. break;
  1267. case LEVEL_ELITE:
  1268. clearArmorSetFlag(ARMORSET_VETERAN);
  1269. setArmorSetFlag(ARMORSET_ELITE);
  1270. clearArmorSetFlag(ARMORSET_HERO);
  1271. break;
  1272. case LEVEL_HEROIC:
  1273. clearArmorSetFlag(ARMORSET_VETERAN);
  1274. clearArmorSetFlag(ARMORSET_ELITE);
  1275. setArmorSetFlag(ARMORSET_HERO);
  1276. break;
  1277. }
  1278. }
  1279. // ------------------------------------------------------------------------------------------------
  1280. // ------------------------------------------------------------------------------------------------
  1281. void ActiveBody::setAflame( Bool )
  1282. {
  1283. //
  1284. // All this does now is act like a major body state change. It is called after Aflame has been
  1285. // set or cleared as an Object Status
  1286. //
  1287. updateBodyParticleSystems();
  1288. }
  1289. // ------------------------------------------------------------------------------------------------
  1290. /** CRC */
  1291. // ------------------------------------------------------------------------------------------------
  1292. void ActiveBody::crc( Xfer *xfer )
  1293. {
  1294. // extend base class
  1295. BodyModule::crc( xfer );
  1296. } // end crc
  1297. // ------------------------------------------------------------------------------------------------
  1298. /** Xfer method
  1299. * Version Info:
  1300. * 1: Initial version */
  1301. // ------------------------------------------------------------------------------------------------
  1302. void ActiveBody::xfer( Xfer *xfer )
  1303. {
  1304. // version
  1305. XferVersion currentVersion = 1;
  1306. XferVersion version = currentVersion;
  1307. xfer->xferVersion( &version, currentVersion );
  1308. // base class
  1309. BodyModule::xfer( xfer );
  1310. // current health
  1311. xfer->xferReal( &m_currentHealth );
  1312. xfer->xferReal( &m_currentSubdualDamage );
  1313. // previous health
  1314. xfer->xferReal( &m_prevHealth );
  1315. // max health
  1316. xfer->xferReal( &m_maxHealth );
  1317. // initial health
  1318. xfer->xferReal( &m_initialHealth );
  1319. // current damage state
  1320. xfer->xferUser( &m_curDamageState, sizeof( BodyDamageType ) );
  1321. // next damage fx time
  1322. xfer->xferUnsignedInt( &m_nextDamageFXTime );
  1323. // last damage fx done
  1324. xfer->xferUser( &m_lastDamageFXDone, sizeof( DamageType ) );
  1325. // last damage info
  1326. xfer->xferSnapshot( &m_lastDamageInfo );
  1327. // last damage timestamp
  1328. xfer->xferUnsignedInt( &m_lastDamageTimestamp );
  1329. // last damage timestamp
  1330. xfer->xferUnsignedInt( &m_lastHealingTimestamp );
  1331. // front crushed
  1332. xfer->xferBool( &m_frontCrushed );
  1333. // back crushed
  1334. xfer->xferBool( &m_backCrushed );
  1335. // last damaged cleared
  1336. xfer->xferBool( &m_lastDamageCleared );
  1337. // indestructible
  1338. xfer->xferBool( &m_indestructible );
  1339. // particle system count
  1340. BodyParticleSystem *system;
  1341. UnsignedShort particleSystemCount = 0;
  1342. for( system = m_particleSystems; system; system = system->m_next )
  1343. particleSystemCount++;
  1344. xfer->xferUnsignedShort( &particleSystemCount );
  1345. // particle systems
  1346. if( xfer->getXferMode() == XFER_SAVE )
  1347. {
  1348. // walk the particle systems
  1349. for( system = m_particleSystems; system; system = system->m_next )
  1350. {
  1351. // write particle system ID
  1352. xfer->xferUser( &system->m_particleSystemID, sizeof( ParticleSystemID ) );
  1353. } // end for, system
  1354. } // end if, save
  1355. else
  1356. {
  1357. ParticleSystemID particleSystemID;
  1358. // the list should be empty at this time
  1359. if( m_particleSystems != NULL )
  1360. {
  1361. DEBUG_CRASH(( "ActiveBody::xfer - m_particleSystems should be empty, but is not\n" ));
  1362. throw SC_INVALID_DATA;
  1363. } // end if
  1364. // read all data elements
  1365. BodyParticleSystem *newEntry;
  1366. for( UnsignedShort i = 0; i < particleSystemCount; ++i )
  1367. {
  1368. // read particle system ID
  1369. xfer->xferUser( &particleSystemID, sizeof( ParticleSystemID ) );
  1370. // allocate entry and add to list
  1371. newEntry = newInstance(BodyParticleSystem);
  1372. newEntry->m_particleSystemID = particleSystemID;
  1373. newEntry->m_next = m_particleSystems; // the list will be reversed, but we don't care
  1374. m_particleSystems = newEntry;
  1375. } // end for, i
  1376. } // end else, load
  1377. // armor set flags
  1378. m_curArmorSetFlags.xfer( xfer );
  1379. } // end xfer
  1380. // ------------------------------------------------------------------------------------------------
  1381. /** Load post process */
  1382. // ------------------------------------------------------------------------------------------------
  1383. void ActiveBody::loadPostProcess( void )
  1384. {
  1385. // extend base class
  1386. BodyModule::loadPostProcess();
  1387. } // end loadPostProcess