FlammableUpdate.cpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326
  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: FlammableUpdate.cpp /////////////////////////////////////////////////////////////////////////
  24. // Author: Graham Smallwood, April 2002
  25. // Desc: Update that manages Aflame and Burned statuses and their effects
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #include "Common/AudioEventRTS.h"
  30. #include "Common/GameAudio.h"
  31. #include "Common/Xfer.h"
  32. #include "GameLogic/GameLogic.h"
  33. #include "GameLogic/Object.h"
  34. #include "GameLogic/Module/BodyModule.h"
  35. #include "GameLogic/Module/FlammableUpdate.h"
  36. #include "GameLogic/Module/FireSpreadUpdate.h"
  37. //-------------------------------------------------------------------------------------------------
  38. FlammableUpdateModuleData::FlammableUpdateModuleData()
  39. {
  40. m_burnedDelay = 0;
  41. m_aflameDuration = 0;
  42. m_aflameDamageDelay = 0;
  43. m_aflameDamageAmount = 0;
  44. // Enabled By Sadullah Nader
  45. // Initialization needed
  46. m_burningSoundName.clear();
  47. //
  48. m_flameDamageLimitData = 20.0f;
  49. m_flameDamageExpirationDelay = LOGICFRAMES_PER_SECOND * 2;
  50. }
  51. //-------------------------------------------------------------------------------------------------
  52. /*static*/ void FlammableUpdateModuleData::buildFieldParse(MultiIniFieldParse& p)
  53. {
  54. UpdateModuleData::buildFieldParse(p);
  55. static const FieldParse dataFieldParse[] =
  56. {
  57. { "BurnedDelay", INI::parseDurationUnsignedInt, NULL, offsetof( FlammableUpdateModuleData, m_burnedDelay ) },
  58. { "AflameDuration", INI::parseDurationUnsignedInt, NULL, offsetof( FlammableUpdateModuleData, m_aflameDuration ) },
  59. { "AflameDamageDelay", INI::parseDurationUnsignedInt, NULL, offsetof( FlammableUpdateModuleData, m_aflameDamageDelay ) },
  60. { "AflameDamageAmount", INI::parseInt, NULL, offsetof( FlammableUpdateModuleData, m_aflameDamageAmount ) },
  61. { "BurningSoundName", INI::parseAsciiString, NULL, offsetof( FlammableUpdateModuleData, m_burningSoundName) },
  62. { "FlameDamageLimit", INI::parseReal, NULL, offsetof( FlammableUpdateModuleData, m_flameDamageLimitData ) },
  63. { "FlameDamageExpiration", INI::parseDurationUnsignedInt, NULL, offsetof( FlammableUpdateModuleData, m_flameDamageExpirationDelay ) },
  64. { 0, 0, 0, 0 }
  65. };
  66. p.add(dataFieldParse);
  67. }
  68. //-------------------------------------------------------------------------------------------------
  69. //-------------------------------------------------------------------------------------------------
  70. FlammableUpdate::FlammableUpdate( Thing *thing, const ModuleData* moduleData ) : UpdateModule( thing, moduleData )
  71. {
  72. m_status = FS_NORMAL;
  73. m_aflameEndFrame = 0;
  74. m_burnedEndFrame = 0;
  75. m_damageEndFrame = 0;
  76. m_audioHandle = NULL;
  77. m_flameDamageLimit = getFlammableUpdateModuleData()->m_flameDamageLimitData;
  78. m_lastFlameDamageDealt = 0;
  79. setWakeFrame(getObject(), UPDATE_SLEEP_FOREVER);
  80. }
  81. //-------------------------------------------------------------------------------------------------
  82. //-------------------------------------------------------------------------------------------------
  83. FlammableUpdate::~FlammableUpdate( void )
  84. {
  85. stopBurningSound();
  86. }
  87. //-------------------------------------------------------------------------------------------------
  88. /** Damage has been dealt, this is an opportunity to reach to that damage */
  89. //-------------------------------------------------------------------------------------------------
  90. void FlammableUpdate::onDamage( DamageInfo *damageInfo )
  91. {
  92. if( damageInfo->in.m_damageType == DAMAGE_FLAME || damageInfo->in.m_damageType == DAMAGE_PARTICLE_BEAM )
  93. {
  94. UnsignedInt now = TheGameLogic->getFrame();
  95. if( now - getFlammableUpdateModuleData()->m_flameDamageExpirationDelay > m_lastFlameDamageDealt )
  96. {
  97. // If it has been a long time since our last flame damage, reset the threshold
  98. m_flameDamageLimit = getFlammableUpdateModuleData()->m_flameDamageLimitData;
  99. }
  100. m_lastFlameDamageDealt = now;
  101. Object *me = getObject();
  102. if( !me->getStatusBits().test( OBJECT_STATUS_AFLAME ) && !me->getStatusBits().test( OBJECT_STATUS_BURNED ) )
  103. {
  104. // If I'm not on fire, and I haven't burned up, see if I should try to catch fire.
  105. m_flameDamageLimit -= damageInfo->out.m_actualDamageDealt;
  106. if( m_flameDamageLimit <= 0 )
  107. {
  108. tryToIgnite();
  109. }
  110. }
  111. }
  112. }
  113. //-------------------------------------------------------------------------------------------------
  114. //-------------------------------------------------------------------------------------------------
  115. UpdateSleepTime FlammableUpdate::update( void )
  116. {
  117. Object *me = getObject();
  118. DEBUG_ASSERTCRASH(m_status == FS_AFLAME, ("hmm, should be aflame"));
  119. UnsignedInt now = TheGameLogic->getFrame();
  120. const FlammableUpdateModuleData *data = getFlammableUpdateModuleData();
  121. if( m_damageEndFrame != 0 && now >= m_damageEndFrame )
  122. {
  123. m_damageEndFrame = now + data->m_aflameDamageDelay;
  124. doAflameDamage();
  125. }
  126. if( m_burnedEndFrame != 0 && now >= m_burnedEndFrame )
  127. {
  128. // So this status is set, but I am still aflame on an independent timer.
  129. me->setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_BURNED ) );
  130. me->setModelConditionState( MODELCONDITION_SMOLDERING );
  131. }
  132. if( m_aflameEndFrame != 0 && now >= m_aflameEndFrame )
  133. {
  134. // This is the important one. I am no longer on fire.
  135. if( me->getStatusBits().test( OBJECT_STATUS_BURNED ) )
  136. {
  137. // If I am burned, then I will never catch fire again.
  138. m_status = FS_BURNED;
  139. }
  140. else
  141. {
  142. // otherwise I am free to burn again
  143. m_status = FS_NORMAL;
  144. }
  145. stopBurningSound();
  146. me->clearStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_AFLAME ) );
  147. me->getBodyModule()->setAflame( FALSE );
  148. me->clearModelConditionState( MODELCONDITION_AFLAME );
  149. }
  150. return calcSleepTime();
  151. }
  152. // ------------------------------------------------------------------------------------------------
  153. // ------------------------------------------------------------------------------------------------
  154. UpdateSleepTime FlammableUpdate::calcSleepTime()
  155. {
  156. UnsignedInt now = TheGameLogic->getFrame();
  157. if (m_status == FS_AFLAME && m_aflameEndFrame != 0 && m_aflameEndFrame > now)
  158. {
  159. UnsignedInt soonest = m_aflameEndFrame;
  160. if (m_burnedEndFrame != 0 && m_burnedEndFrame < soonest && m_burnedEndFrame > now) soonest = m_burnedEndFrame;
  161. if (m_damageEndFrame != 0 && m_damageEndFrame < soonest && m_damageEndFrame > now) soonest = m_damageEndFrame;
  162. DEBUG_ASSERTCRASH(soonest - now > 0, ("hmm"));
  163. // UPDATE_SLEEP requires a count-of-frames, not an absolute-frame, so subtract 'now'
  164. return UPDATE_SLEEP(soonest - now);
  165. }
  166. else
  167. {
  168. return UPDATE_SLEEP_FOREVER;
  169. }
  170. }
  171. //-------------------------------------------------------------------------------------------------
  172. //-------------------------------------------------------------------------------------------------
  173. void FlammableUpdate::tryToIgnite()
  174. {
  175. if( m_status == FS_NORMAL )
  176. {
  177. Object *me = getObject();
  178. me->setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_AFLAME ) );
  179. me->getBodyModule()->setAflame( TRUE );
  180. me->setModelConditionState( MODELCONDITION_AFLAME );
  181. startBurningSound();
  182. // bleah. this sucks. (srj)
  183. static const NameKeyType key_FireSpreadUpdate = NAMEKEY("FireSpreadUpdate");
  184. FireSpreadUpdate* fu = (FireSpreadUpdate*)getObject()->findUpdateModule(key_FireSpreadUpdate);
  185. if (fu != NULL)
  186. {
  187. fu->startFireSpreading();
  188. }
  189. m_status = FS_AFLAME;
  190. const FlammableUpdateModuleData *data = getFlammableUpdateModuleData();
  191. UnsignedInt now = TheGameLogic->getFrame();
  192. m_aflameEndFrame = now + data->m_aflameDuration;
  193. m_burnedEndFrame = data->m_burnedDelay ? now + data->m_burnedDelay : 0;
  194. m_damageEndFrame = data->m_aflameDamageDelay ? now + data->m_aflameDamageDelay : 0;
  195. setWakeFrame(getObject(), calcSleepTime());
  196. }
  197. }
  198. //-------------------------------------------------------------------------------------------------
  199. //-------------------------------------------------------------------------------------------------
  200. void FlammableUpdate::doAflameDamage()
  201. {
  202. const FlammableUpdateModuleData *data = getFlammableUpdateModuleData();
  203. DamageInfo info;
  204. info.in.m_amount = data->m_aflameDamageAmount;
  205. info.in.m_sourceID = getObject()->getID();
  206. info.in.m_damageType = DAMAGE_FLAME;
  207. info.in.m_deathType = DEATH_BURNED;
  208. getObject()->attemptDamage( &info );
  209. }
  210. //-------------------------------------------------------------------------------------------------
  211. //-------------------------------------------------------------------------------------------------
  212. void FlammableUpdate::startBurningSound()
  213. {
  214. const FlammableUpdateModuleData *data = getFlammableUpdateModuleData();
  215. AudioEventRTS audio(data->m_burningSoundName, getObject()->getID());
  216. m_audioHandle = TheAudio->addAudioEvent( &audio );
  217. }
  218. //-------------------------------------------------------------------------------------------------
  219. //-------------------------------------------------------------------------------------------------
  220. void FlammableUpdate::stopBurningSound()
  221. {
  222. if (m_audioHandle)
  223. {
  224. TheAudio->removeAudioEvent( m_audioHandle );
  225. m_audioHandle = NULL;
  226. }
  227. }
  228. //-------------------------------------------------------------------------------------------------
  229. //-------------------------------------------------------------------------------------------------
  230. Bool FlammableUpdate::wouldIgnite()
  231. {
  232. if( m_status == FS_NORMAL )
  233. return TRUE;
  234. return FALSE;
  235. }
  236. // ------------------------------------------------------------------------------------------------
  237. /** CRC */
  238. // ------------------------------------------------------------------------------------------------
  239. void FlammableUpdate::crc( Xfer *xfer )
  240. {
  241. // extend base class
  242. UpdateModule::crc( xfer );
  243. } // end crc
  244. // ------------------------------------------------------------------------------------------------
  245. /** Xfer method
  246. * Version Info:
  247. * 1: Initial version */
  248. // ------------------------------------------------------------------------------------------------
  249. void FlammableUpdate::xfer( Xfer *xfer )
  250. {
  251. // version
  252. XferVersion currentVersion = 1;
  253. XferVersion version = currentVersion;
  254. xfer->xferVersion( &version, currentVersion );
  255. // extend base class
  256. UpdateModule::xfer( xfer );
  257. // flammability status
  258. xfer->xferUser( &m_status, sizeof( FlammabilityStatusType ) );
  259. // aflame end frame
  260. xfer->xferUnsignedInt( &m_aflameEndFrame );
  261. // burned end frame
  262. xfer->xferUnsignedInt( &m_burnedEndFrame );
  263. // damage end frame
  264. xfer->xferUnsignedInt( &m_damageEndFrame );
  265. // flame damage limit
  266. xfer->xferReal( &m_flameDamageLimit );
  267. // last flame damage dealt
  268. xfer->xferUnsignedInt( &m_lastFlameDamageDealt );
  269. } // end xfer
  270. // ------------------------------------------------------------------------------------------------
  271. /** Load post process */
  272. // ------------------------------------------------------------------------------------------------
  273. void FlammableUpdate::loadPostProcess( void )
  274. {
  275. // extend base class
  276. UpdateModule::loadPostProcess();
  277. } // end loadPostProcess