AutoHealBehavior.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  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: AutoHealBehavior.cpp ///////////////////////////////////////////////////////////////////////
  24. // Author:
  25. // Desc:
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #include "Common/Thing.h"
  30. #include "Common/ThingTemplate.h"
  31. #include "Common/INI.h"
  32. #include "Common/Player.h"
  33. #include "Common/Xfer.h"
  34. #include "GameClient/ParticleSys.h"
  35. #include "GameClient/Anim2D.h"
  36. #include "GameClient/InGameUI.h"
  37. #include "GameLogic/Module/AutoHealBehavior.h"
  38. #include "GameLogic/Module/BodyModule.h"
  39. #include "GameLogic/GameLogic.h"
  40. #include "GameLogic/Object.h"
  41. #include "GameLogic/PartitionManager.h"
  42. #ifdef _INTERNAL
  43. // for occasional debugging...
  44. //#pragma optimize("", off)
  45. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  46. #endif
  47. //-------------------------------------------------------------------------------------------------
  48. //-------------------------------------------------------------------------------------------------
  49. struct AutoHealPlayerScanHelper
  50. {
  51. KindOfMaskType m_kindOfToTest;
  52. KindOfMaskType m_forbiddenKindOf;
  53. Object *m_theHealer;
  54. ObjectPointerList *m_objectList;
  55. Bool m_skipSelfForHealing;
  56. };
  57. static void checkForAutoHeal( Object *testObj, void *userData )
  58. {
  59. AutoHealPlayerScanHelper *helper = (AutoHealPlayerScanHelper*)userData;
  60. ObjectPointerList *listToAddTo = helper->m_objectList;
  61. if( testObj->isEffectivelyDead() )
  62. return;
  63. if( testObj->getControllingPlayer() != helper->m_theHealer->getControllingPlayer() )
  64. return;
  65. if( testObj->isOffMap() )
  66. return;
  67. if( helper->m_skipSelfForHealing && testObj == helper->m_theHealer )
  68. return;
  69. if( !testObj->isAnyKindOf(helper->m_kindOfToTest) )
  70. return;
  71. if( testObj->isAnyKindOf( helper->m_forbiddenKindOf ) )
  72. return;
  73. if( testObj->getBodyModule()->getHealth() >= testObj->getBodyModule()->getMaxHealth() )
  74. return;
  75. listToAddTo->push_back(testObj);
  76. }
  77. //-------------------------------------------------------------------------------------------------
  78. //-------------------------------------------------------------------------------------------------
  79. AutoHealBehavior::AutoHealBehavior( Thing *thing, const ModuleData* moduleData ) : UpdateModule( thing, moduleData )
  80. {
  81. const AutoHealBehaviorModuleData *d = getAutoHealBehaviorModuleData();
  82. m_radiusParticleSystemID = INVALID_PARTICLE_SYSTEM_ID;
  83. m_soonestHealFrame = 0;
  84. m_stopped = false;
  85. Object *obj = getObject();
  86. {
  87. if( d->m_radiusParticleSystemTmpl )
  88. {
  89. ParticleSystem *particleSystem;
  90. particleSystem = TheParticleSystemManager->createParticleSystem( d->m_radiusParticleSystemTmpl );
  91. if( particleSystem )
  92. {
  93. particleSystem->setPosition( obj->getPosition() );
  94. m_radiusParticleSystemID = particleSystem->getSystemID();
  95. }
  96. }
  97. }
  98. if (d->m_initiallyActive)
  99. {
  100. giveSelfUpgrade();
  101. // start these guys with random phasings so that we don't
  102. // have all of 'em check on the same frame.
  103. UnsignedInt delay = getAutoHealBehaviorModuleData()->m_healingDelay;
  104. setWakeFrame(getObject(), UPDATE_SLEEP(GameLogicRandomValue(1, delay)));
  105. }
  106. else
  107. {
  108. setWakeFrame(getObject(), UPDATE_SLEEP_FOREVER);
  109. }
  110. }
  111. //-------------------------------------------------------------------------------------------------
  112. //-------------------------------------------------------------------------------------------------
  113. AutoHealBehavior::~AutoHealBehavior( void )
  114. {
  115. if( m_radiusParticleSystemID != INVALID_PARTICLE_SYSTEM_ID )
  116. TheParticleSystemManager->destroyParticleSystemByID( m_radiusParticleSystemID );
  117. }
  118. //-------------------------------------------------------------------------------------------------
  119. void AutoHealBehavior::stopHealing()
  120. {
  121. m_stopped = true;
  122. m_soonestHealFrame = FOREVER;
  123. setWakeFrame(getObject(), UPDATE_SLEEP_FOREVER);
  124. }
  125. //-------------------------------------------------------------------------------------------------
  126. void AutoHealBehavior::undoUpgrade()
  127. {
  128. m_soonestHealFrame = 0;
  129. setUpgradeExecuted( FALSE );
  130. }
  131. //-------------------------------------------------------------------------------------------------
  132. /** Damage has been dealt, this is an opportunity to reach to that damage */
  133. //-------------------------------------------------------------------------------------------------
  134. void AutoHealBehavior::onDamage( DamageInfo *damageInfo )
  135. {
  136. if (m_stopped)
  137. return;
  138. const AutoHealBehaviorModuleData *d = getAutoHealBehaviorModuleData();
  139. if (isUpgradeActive() && d->m_radius == 0.0f)
  140. {
  141. // if this is nonzero, getting damaged resets our healing process. so go to
  142. // sleep for this long.
  143. if (d->m_startHealingDelay > 0)
  144. {
  145. setWakeFrame(getObject(), UPDATE_SLEEP(d->m_startHealingDelay));
  146. }
  147. else if( TheGameLogic->getFrame() > m_soonestHealFrame )
  148. {
  149. // We can only force an immediate wake if we are ready to heal. Otherwise we will
  150. // heal on a timer AND at every damage input.
  151. setWakeFrame(getObject(), UPDATE_SLEEP_NONE);
  152. }
  153. }
  154. }
  155. //-------------------------------------------------------------------------------------------------
  156. /** The update callback. */
  157. //-------------------------------------------------------------------------------------------------
  158. UpdateSleepTime AutoHealBehavior::update( void )
  159. {
  160. if (m_stopped)
  161. return UPDATE_SLEEP_FOREVER;
  162. Object *obj = getObject();
  163. const AutoHealBehaviorModuleData *d = getAutoHealBehaviorModuleData();
  164. // do not heal if our status bit is not on.
  165. // do not heal if our status is effectively dead. There ain't no coming back, man!
  166. if (!isUpgradeActive() || obj->isEffectivelyDead())
  167. {
  168. DEBUG_ASSERTCRASH(isUpgradeActive(), ("hmm, this should not be possible"));
  169. return UPDATE_SLEEP_FOREVER;
  170. }
  171. //DEBUG_LOG(("doing auto heal %d\n",TheGameLogic->getFrame()));
  172. if( d->m_affectsWholePlayer )
  173. {
  174. // Even newer system, I can ignore radius and iterate objects on the owning player. Faster than scanning range 10,000,000
  175. ObjectPointerList objectsToHeal;
  176. Player *owningPlayer = getObject()->getControllingPlayer();
  177. if( owningPlayer )
  178. {
  179. AutoHealPlayerScanHelper helper;
  180. helper.m_kindOfToTest = d->m_kindOf;
  181. helper.m_forbiddenKindOf = d->m_forbiddenKindOf;
  182. helper.m_objectList = &objectsToHeal;
  183. helper.m_theHealer = getObject();
  184. helper.m_skipSelfForHealing = d->m_skipSelfForHealing;
  185. // Smack all objects with this function, and we will end up with a list of Objects deserving of pulseHealObject
  186. owningPlayer->iterateObjects( checkForAutoHeal, &helper );
  187. for( ObjectPointerListIterator iter = objectsToHeal.begin(); iter != objectsToHeal.end(); ++iter )
  188. {
  189. pulseHealObject(*iter);
  190. }
  191. objectsToHeal.clear();
  192. }
  193. return UPDATE_SLEEP(d->m_healingDelay);
  194. }
  195. else if( d->m_radius == 0.0f )
  196. {
  197. //ORIGINAL SYSTEM -- JUST HEAL SELF!
  198. // do not heal if we are at max health already
  199. BodyModuleInterface *body = obj->getBodyModule();
  200. if( body->getHealth() < body->getMaxHealth() )
  201. {
  202. pulseHealObject( obj );
  203. return UPDATE_SLEEP(d->m_healingDelay);
  204. }
  205. else
  206. {
  207. // go to sleep forever -- we'll wake back up when we are damaged again
  208. return UPDATE_SLEEP_FOREVER;
  209. }
  210. }
  211. else
  212. {
  213. //EXPANDED SYSTEM -- HEAL FRIENDLIES IN RADIUS
  214. // setup scan filters
  215. PartitionFilterRelationship relationship( obj, PartitionFilterRelationship::ALLOW_ALLIES );
  216. PartitionFilterSameMapStatus filterMapStatus(obj);
  217. PartitionFilterAlive filterAlive;
  218. PartitionFilter *filters[] = { &relationship, &filterAlive, &filterMapStatus, NULL };
  219. // scan objects in our region
  220. ObjectIterator *iter = ThePartitionManager->iterateObjectsInRange( obj->getPosition(), d->m_radius, FROM_CENTER_2D, filters );
  221. MemoryPoolObjectHolder hold( iter );
  222. for( obj = iter->first(); obj; obj = iter->next() )
  223. {
  224. // do not heal if we are at max health already
  225. BodyModuleInterface *body = obj->getBodyModule();
  226. if( body->getHealth() < body->getMaxHealth() )
  227. {
  228. if( obj->isAnyKindOf( d->m_kindOf ) && !obj->isAnyKindOf( d->m_forbiddenKindOf ) )
  229. {
  230. if( !d->m_skipSelfForHealing || obj != getObject() )
  231. {
  232. pulseHealObject( obj );
  233. if( d->m_singleBurst && TheGameLogic->getDrawIconUI() )
  234. {
  235. if( TheAnim2DCollection && TheGlobalData->m_getHealedAnimationName.isEmpty() == FALSE )
  236. {
  237. Anim2DTemplate *animTemplate = TheAnim2DCollection->findTemplate( TheGlobalData->m_getHealedAnimationName );
  238. if ( animTemplate )
  239. {
  240. Coord3D iconPosition;
  241. iconPosition.set(obj->getPosition()->x,
  242. obj->getPosition()->y,
  243. obj->getPosition()->z + obj->getGeometryInfo().getMaxHeightAbovePosition() );
  244. TheInGameUI->addWorldAnimation( animTemplate, &iconPosition, WORLD_ANIM_FADE_ON_EXPIRE,
  245. TheGlobalData->m_getHealedAnimationDisplayTimeInSeconds,
  246. TheGlobalData->m_getHealedAnimationZRisePerSecond);
  247. }
  248. }
  249. }
  250. }
  251. }
  252. }
  253. } // end for obj
  254. return UPDATE_SLEEP( d->m_singleBurst ? UPDATE_SLEEP_FOREVER : d->m_healingDelay );
  255. }
  256. }
  257. //-------------------------------------------------------------------------------------------------
  258. //-------------------------------------------------------------------------------------------------
  259. void AutoHealBehavior::pulseHealObject( Object *obj )
  260. {
  261. if (m_stopped)
  262. return;
  263. const AutoHealBehaviorModuleData *data = getAutoHealBehaviorModuleData();
  264. if ( data->m_radius == 0.0f )
  265. obj->attemptHealing(data->m_healingAmount, getObject());
  266. else
  267. obj->attemptHealingFromSoleBenefactor( data->m_healingAmount, getObject(), data->m_healingDelay );
  268. if( data->m_unitHealPulseParticleSystemTmpl )
  269. {
  270. ParticleSystem *system = TheParticleSystemManager->createParticleSystem( data->m_unitHealPulseParticleSystemTmpl );
  271. if( system )
  272. {
  273. system->setPosition( obj->getPosition() );
  274. }
  275. }
  276. m_soonestHealFrame = TheGameLogic->getFrame() + data->m_healingDelay;// In case onDamage tries to wake us up early
  277. }
  278. // ------------------------------------------------------------------------------------------------
  279. /** CRC */
  280. // ------------------------------------------------------------------------------------------------
  281. void AutoHealBehavior::crc( Xfer *xfer )
  282. {
  283. // extend base class
  284. UpdateModule::crc( xfer );
  285. // extend base class
  286. UpgradeMux::upgradeMuxCRC( xfer );
  287. } // end crc
  288. // ------------------------------------------------------------------------------------------------
  289. /** Xfer method
  290. * Version Info:
  291. * 1: Initial version */
  292. // ------------------------------------------------------------------------------------------------
  293. void AutoHealBehavior::xfer( Xfer *xfer )
  294. {
  295. // version
  296. XferVersion currentVersion = 1;
  297. XferVersion version = currentVersion;
  298. xfer->xferVersion( &version, currentVersion );
  299. // extend base class
  300. UpdateModule::xfer( xfer );
  301. // extend base class
  302. UpgradeMux::upgradeMuxXfer( xfer );
  303. // particle system id
  304. xfer->xferUser( &m_radiusParticleSystemID, sizeof( ParticleSystemID ) );
  305. // Timer safety
  306. xfer->xferUnsignedInt( &m_soonestHealFrame );
  307. // stopped
  308. xfer->xferBool( &m_stopped );
  309. } // end xfer
  310. // ------------------------------------------------------------------------------------------------
  311. /** Load post process */
  312. // ------------------------------------------------------------------------------------------------
  313. void AutoHealBehavior::loadPostProcess( void )
  314. {
  315. // extend base class
  316. UpdateModule::loadPostProcess();
  317. // extend base class
  318. UpgradeMux::upgradeMuxLoadPostProcess();
  319. } // end loadPostProcess