damageablestaticphys.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494
  1. /*
  2. ** Command & Conquer Renegade(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. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : Combat *
  23. * *
  24. * $Archive:: /Commando/Code/Combat/damageablestaticphys.cpp $*
  25. * *
  26. * Original Author:: Greg Hjelstrom *
  27. * *
  28. * $Author:: Greg_h $*
  29. * *
  30. * $Modtime:: 8/17/01 7:15p $*
  31. * *
  32. * $Revision:: 19 $*
  33. * *
  34. *---------------------------------------------------------------------------------------------*
  35. * Functions: *
  36. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  37. #include "damageablestaticphys.h"
  38. #include "simpledefinitionfactory.h"
  39. #include "persistfactory.h"
  40. #include "wwphysids.h"
  41. #include "hanim.h"
  42. #include "wwdebug.h"
  43. #include "wwhack.h"
  44. #include "combat.h"
  45. #include "explosion.h"
  46. DECLARE_FORCE_LINK( damageablestaticphys );
  47. /*
  48. ** Chunk Id's used by DamageableStaticPhysDefClass
  49. */
  50. enum
  51. {
  52. DAMAGEABLESTATICPHYSDEF_CHUNK_STATICANIMPHYSDEF = 7311734,
  53. DAMAGEABLESTATICPHYSDEF_CHUNK_VARIABLES,
  54. DAMAGEABLESTATICPHYSDEF_CHUNK_DEFENSEOBJECTDEF,
  55. DAMAGEABLESTATICPHYSDEF_VARIABLE_KILLEDEXPLOSION = 0x00,
  56. DAMAGEABLESTATICPHYSDEF_VARIABLE_RESETAFTERANIM,
  57. DAMAGEABLESTATICPHYSDEF_VARIABLE_LIVELOOPSTART,
  58. DAMAGEABLESTATICPHYSDEF_VARIABLE_LIVELOOPEND,
  59. DAMAGEABLESTATICPHYSDEF_VARIABLE_LIVETWITCHSTART,
  60. DAMAGEABLESTATICPHYSDEF_VARIABLE_LIVETWITCHEND,
  61. DAMAGEABLESTATICPHYSDEF_VARIABLE_DEATHTRANSITIONSTART,
  62. DAMAGEABLESTATICPHYSDEF_VARIABLE_DEATHTRANSITIONEND,
  63. DAMAGEABLESTATICPHYSDEF_VARIABLE_DEADLOOPSTART,
  64. DAMAGEABLESTATICPHYSDEF_VARIABLE_DEADLOOPEND,
  65. DAMAGEABLESTATICPHYSDEF_VARIABLE_DEADTWITCHSTART,
  66. DAMAGEABLESTATICPHYSDEF_VARIABLE_DEADTWITCHEND,
  67. DAMAGEABLESTATICPHYSDEF_VARIABLE_PLAYTWITCHESTOCOMPLETION,
  68. };
  69. /*
  70. ** Chunk Id's used by DamageableStaticPhysClass
  71. */
  72. enum
  73. {
  74. DAMAGEABLESTATICPHYS_CHUNK_STATICANIMPHYS = 7311740,
  75. DAMAGEABLESTATICPHYS_CHUNK_DEFENSEOBJECT,
  76. DAMAGEABLESTATICPHYS_CHUNK_VARIABLES,
  77. DAMAGEABLESTATICPHYS_VARIABLE_CURSTATE = 0x00,
  78. };
  79. /************************************************************************************************
  80. **
  81. ** DamageableStaticPhysDefClass Implementation
  82. **
  83. ************************************************************************************************/
  84. SimplePersistFactoryClass<DamageableStaticPhysDefClass, PHYSICS_CHUNKID_DAMAGEABLESTATICPHYSDEF> _DamageableStaticPhysDefPersistFactory;
  85. DECLARE_DEFINITION_FACTORY(DamageableStaticPhysDefClass, CLASSID_DAMAGEABLESTATICPHYSDEF, "DamageableStaticPhys") _DamageableStaticPhysDefDefFactory;
  86. DamageableStaticPhysDefClass::DamageableStaticPhysDefClass(void) :
  87. StaticAnimPhysDefClass(),
  88. KilledExplosion(0),
  89. LiveLoopStart(0),
  90. LiveLoopEnd(0),
  91. LiveTwitchStart(0),
  92. LiveTwitchEnd(0),
  93. DeathTransitionStart(0),
  94. DeathTransitionEnd(0),
  95. DeadLoopStart(0),
  96. DeadLoopEnd(0),
  97. DeadTwitchStart(0),
  98. DeadTwitchEnd(0),
  99. PlayTwitchesToCompletion(false)
  100. {
  101. PARAM_SEPARATOR(DamageableStaticPhysDefClass, "Damage Behavior Controls");
  102. EDITABLE_PARAM(DamageableStaticPhysDefClass, ParameterClass::TYPE_EXPLOSIONDEFINITIONID, KilledExplosion);
  103. INT_EDITABLE_PARAM(DamageableStaticPhysDefClass, LiveLoopStart, 0, 999);
  104. INT_EDITABLE_PARAM(DamageableStaticPhysDefClass, LiveLoopEnd, 0, 999);
  105. INT_EDITABLE_PARAM(DamageableStaticPhysDefClass, LiveTwitchStart, 0, 999);
  106. INT_EDITABLE_PARAM(DamageableStaticPhysDefClass, LiveTwitchEnd,0, 999);
  107. INT_EDITABLE_PARAM(DamageableStaticPhysDefClass, DeathTransitionStart,0, 999);
  108. INT_EDITABLE_PARAM(DamageableStaticPhysDefClass, DeathTransitionEnd,0, 999);
  109. INT_EDITABLE_PARAM(DamageableStaticPhysDefClass, DeadLoopStart,0, 999);
  110. INT_EDITABLE_PARAM(DamageableStaticPhysDefClass, DeadLoopEnd,0, 999);
  111. INT_EDITABLE_PARAM(DamageableStaticPhysDefClass, DeadTwitchStart,0, 999);
  112. INT_EDITABLE_PARAM(DamageableStaticPhysDefClass, DeadTwitchEnd,0, 999);
  113. EDITABLE_PARAM(DamageableStaticPhysDefClass,ParameterClass::TYPE_BOOL,PlayTwitchesToCompletion);
  114. DEFENSEOBJECTDEF_EDITABLE_PARAMS(DamageableStaticPhysDefClass, DefenseObjectDef);
  115. }
  116. uint32 DamageableStaticPhysDefClass::Get_Class_ID(void) const
  117. {
  118. return CLASSID_DAMAGEABLESTATICPHYSDEF;
  119. }
  120. bool DamageableStaticPhysDefClass::Is_Type(const char * type_name)
  121. {
  122. if (stricmp(type_name,DamageableStaticPhysDefClass::Get_Type_Name()) == 0) {
  123. return true;
  124. } else {
  125. return StaticAnimPhysDefClass::Is_Type(type_name);
  126. }
  127. }
  128. PersistClass * DamageableStaticPhysDefClass::Create(void) const
  129. {
  130. DamageableStaticPhysClass * obj = new DamageableStaticPhysClass;
  131. obj->Init(*this);
  132. return obj;
  133. }
  134. bool DamageableStaticPhysDefClass::Save(ChunkSaveClass & csave)
  135. {
  136. csave.Begin_Chunk(DAMAGEABLESTATICPHYSDEF_CHUNK_STATICANIMPHYSDEF);
  137. StaticAnimPhysDefClass::Save(csave);
  138. csave.End_Chunk();
  139. csave.Begin_Chunk(DAMAGEABLESTATICPHYSDEF_CHUNK_VARIABLES);
  140. WRITE_MICRO_CHUNK(csave, DAMAGEABLESTATICPHYSDEF_VARIABLE_KILLEDEXPLOSION, KilledExplosion);
  141. WRITE_MICRO_CHUNK(csave, DAMAGEABLESTATICPHYSDEF_VARIABLE_LIVELOOPSTART,LiveLoopStart);
  142. WRITE_MICRO_CHUNK(csave, DAMAGEABLESTATICPHYSDEF_VARIABLE_LIVELOOPEND,LiveLoopEnd);
  143. WRITE_MICRO_CHUNK(csave, DAMAGEABLESTATICPHYSDEF_VARIABLE_LIVETWITCHSTART,LiveTwitchStart);
  144. WRITE_MICRO_CHUNK(csave, DAMAGEABLESTATICPHYSDEF_VARIABLE_LIVETWITCHEND,LiveTwitchEnd);
  145. WRITE_MICRO_CHUNK(csave, DAMAGEABLESTATICPHYSDEF_VARIABLE_DEATHTRANSITIONSTART,DeathTransitionStart);
  146. WRITE_MICRO_CHUNK(csave, DAMAGEABLESTATICPHYSDEF_VARIABLE_DEATHTRANSITIONEND,DeathTransitionEnd);
  147. WRITE_MICRO_CHUNK(csave, DAMAGEABLESTATICPHYSDEF_VARIABLE_DEADLOOPSTART,DeadLoopStart);
  148. WRITE_MICRO_CHUNK(csave, DAMAGEABLESTATICPHYSDEF_VARIABLE_DEADLOOPEND,DeadLoopEnd);
  149. WRITE_MICRO_CHUNK(csave, DAMAGEABLESTATICPHYSDEF_VARIABLE_DEADTWITCHSTART,DeadTwitchStart);
  150. WRITE_MICRO_CHUNK(csave, DAMAGEABLESTATICPHYSDEF_VARIABLE_DEADTWITCHEND,DeadTwitchEnd);
  151. WRITE_MICRO_CHUNK(csave, DAMAGEABLESTATICPHYSDEF_VARIABLE_PLAYTWITCHESTOCOMPLETION,PlayTwitchesToCompletion);
  152. csave.End_Chunk();
  153. csave.Begin_Chunk(DAMAGEABLESTATICPHYSDEF_CHUNK_DEFENSEOBJECTDEF);
  154. DefenseObjectDef.Save(csave);
  155. csave.End_Chunk();
  156. return true;
  157. }
  158. bool DamageableStaticPhysDefClass::Load( ChunkLoadClass &cload )
  159. {
  160. while (cload.Open_Chunk()) {
  161. switch(cload.Cur_Chunk_ID()) {
  162. case DAMAGEABLESTATICPHYSDEF_CHUNK_STATICANIMPHYSDEF:
  163. StaticAnimPhysDefClass::Load( cload );
  164. break;
  165. case DAMAGEABLESTATICPHYSDEF_CHUNK_VARIABLES:
  166. while (cload.Open_Micro_Chunk()) {
  167. switch(cload.Cur_Micro_Chunk_ID()) {
  168. READ_MICRO_CHUNK(cload, DAMAGEABLESTATICPHYSDEF_VARIABLE_KILLEDEXPLOSION, KilledExplosion);
  169. READ_MICRO_CHUNK(cload, DAMAGEABLESTATICPHYSDEF_VARIABLE_LIVELOOPSTART,LiveLoopStart);
  170. READ_MICRO_CHUNK(cload, DAMAGEABLESTATICPHYSDEF_VARIABLE_LIVELOOPEND,LiveLoopEnd);
  171. READ_MICRO_CHUNK(cload, DAMAGEABLESTATICPHYSDEF_VARIABLE_LIVETWITCHSTART,LiveTwitchStart);
  172. READ_MICRO_CHUNK(cload, DAMAGEABLESTATICPHYSDEF_VARIABLE_LIVETWITCHEND,LiveTwitchEnd);
  173. READ_MICRO_CHUNK(cload, DAMAGEABLESTATICPHYSDEF_VARIABLE_DEATHTRANSITIONSTART,DeathTransitionStart);
  174. READ_MICRO_CHUNK(cload, DAMAGEABLESTATICPHYSDEF_VARIABLE_DEATHTRANSITIONEND,DeathTransitionEnd);
  175. READ_MICRO_CHUNK(cload, DAMAGEABLESTATICPHYSDEF_VARIABLE_DEADLOOPSTART,DeadLoopStart);
  176. READ_MICRO_CHUNK(cload, DAMAGEABLESTATICPHYSDEF_VARIABLE_DEADLOOPEND,DeadLoopEnd);
  177. READ_MICRO_CHUNK(cload, DAMAGEABLESTATICPHYSDEF_VARIABLE_DEADTWITCHSTART,DeadTwitchStart);
  178. READ_MICRO_CHUNK(cload, DAMAGEABLESTATICPHYSDEF_VARIABLE_DEADTWITCHEND,DeadTwitchEnd);
  179. READ_MICRO_CHUNK(cload, DAMAGEABLESTATICPHYSDEF_VARIABLE_PLAYTWITCHESTOCOMPLETION,PlayTwitchesToCompletion);
  180. default:
  181. WWDEBUG_SAY(( "Unrecognized DamageableStaticPhysDef Variable chunkID\n" ));
  182. break;
  183. }
  184. cload.Close_Micro_Chunk();
  185. }
  186. break;
  187. case DAMAGEABLESTATICPHYSDEF_CHUNK_DEFENSEOBJECTDEF:
  188. DefenseObjectDef.Load(cload);
  189. break;
  190. default:
  191. WWDEBUG_SAY(( "Unrecognized DamageableStaticPhysDef chunkID\n" ));
  192. break;
  193. }
  194. cload.Close_Chunk();
  195. }
  196. return true;
  197. }
  198. const PersistFactoryClass & DamageableStaticPhysDefClass::Get_Factory (void) const
  199. {
  200. return _DamageableStaticPhysDefPersistFactory;
  201. }
  202. /************************************************************************************************
  203. **
  204. ** DamageableStaticPhysClass Implementation
  205. **
  206. ************************************************************************************************/
  207. SimplePersistFactoryClass<DamageableStaticPhysClass, PHYSICS_CHUNKID_DAMAGEABLESTATICPHYS> _DamageableStaticPhysPersistFactory;
  208. const PersistFactoryClass & DamageableStaticPhysClass::Get_Factory (void) const
  209. {
  210. return _DamageableStaticPhysPersistFactory;
  211. }
  212. DamageableStaticPhysClass::DamageableStaticPhysClass(void) :
  213. CurState(STATE_LIVE_LOOP)
  214. {
  215. }
  216. DamageableStaticPhysClass::~DamageableStaticPhysClass( void )
  217. {
  218. }
  219. void DamageableStaticPhysClass::Init( const DamageableStaticPhysDefClass & def )
  220. {
  221. // DamageableStaticPhys are not PhysicalGameObjs, so pass in a NULL owner
  222. DefenseObject.Init(def.DefenseObjectDef, NULL);
  223. StaticAnimPhysClass::Init(def);
  224. Start_Loop();
  225. }
  226. void DamageableStaticPhysClass::Timestep(float dt)
  227. {
  228. StaticAnimPhysClass::Timestep(dt);
  229. /*
  230. ** If we just reached a target, then transition into the loop for our current health state
  231. */
  232. if ( (Get_Animation_Manager().Get_Animation_Mode() == AnimCollisionManagerClass::ANIMATE_TARGET) &&
  233. (Get_Animation_Manager().Is_At_Target()) )
  234. {
  235. Start_Loop();
  236. }
  237. }
  238. void DamageableStaticPhysClass::Apply_Damage_Static( const OffenseObjectClass & offense )
  239. {
  240. /*
  241. ** If we're already completely damaged, possibly start a twitch and then return
  242. */
  243. if (DefenseObject.Get_Health() <= 0) {
  244. Play_Twitch();
  245. return;
  246. }
  247. /*
  248. ** Apply the damage
  249. */
  250. float new_health = DefenseObject.Apply_Damage(offense);
  251. /*
  252. ** If we just lost all of our health, implement all of the
  253. ** destruction effects.
  254. */
  255. if (new_health <= 0) {
  256. /*
  257. ** Play our animation to the last frame
  258. */
  259. Play_Death_Transition();
  260. /*
  261. ** Set off an explosion if the user defined one
  262. */
  263. const DamageableStaticPhysDefClass * def = Get_DamageableStaticPhysDef();
  264. if (def->KilledExplosion != 0) {
  265. ExplosionManager::Create_Explosion_At(def->KilledExplosion,Get_Transform(),NULL);
  266. }
  267. } else {
  268. /*
  269. ** We were hit but didn't lose all of our health, so play a twitch
  270. */
  271. Play_Twitch();
  272. }
  273. }
  274. void DamageableStaticPhysClass::Reset_Health(void)
  275. {
  276. DefenseObject.Set_Health(DefenseObject.Get_Health_Max());
  277. Start_Loop();
  278. }
  279. void DamageableStaticPhysClass::Start_Loop(void)
  280. {
  281. const DamageableStaticPhysDefClass * def = Get_DamageableStaticPhysDef();
  282. if (def != NULL) {
  283. int frame0,frame1;
  284. if (DefenseObject.Get_Health() > 0) {
  285. frame0 = def->LiveLoopStart;
  286. frame1 = def->LiveLoopEnd;
  287. CurState = STATE_LIVE_LOOP;
  288. } else {
  289. frame0 = def->DeadLoopStart;
  290. frame1 = def->DeadLoopEnd;
  291. CurState = STATE_DEAD_LOOP;
  292. }
  293. if (frame0 != frame1) {
  294. Get_Animation_Manager().Set_Animation_Mode(AnimCollisionManagerClass::ANIMATE_LOOP);
  295. Get_Animation_Manager().Set_Current_Frame(frame0);
  296. Get_Animation_Manager().Set_Loop_Start(frame0);
  297. Get_Animation_Manager().Set_Loop_End(frame1);
  298. } else {
  299. Get_Animation_Manager().Set_Animation_Mode(AnimCollisionManagerClass::ANIMATE_MANUAL);
  300. Get_Animation_Manager().Set_Current_Frame(frame0);
  301. }
  302. } else {
  303. WWDEBUG_SAY(("ERROR: Missing definition for damageable object: %s\n",Model->Get_Name()));
  304. }
  305. }
  306. void DamageableStaticPhysClass::Play_Twitch(void)
  307. {
  308. /*
  309. ** Never interrupt death transitions.
  310. */
  311. if (CurState == STATE_DEATH_TRANSITION) {
  312. return;
  313. }
  314. /*
  315. ** Play the appropriate twitch depending on whether we are alive or dead
  316. */
  317. const DamageableStaticPhysDefClass * def = Get_DamageableStaticPhysDef();
  318. if (def != NULL) {
  319. /*
  320. ** Don't interrupt other twitches if the designer doesn't want us to
  321. */
  322. if ((def->PlayTwitchesToCompletion == false) ||
  323. ((CurState != STATE_LIVE_TWITCH) && (CurState != STATE_DEAD_TWITCH)))
  324. {
  325. int frame0,frame1;
  326. if (DefenseObject.Get_Health() > 0) {
  327. frame0 = def->LiveTwitchStart;
  328. frame1 = def->LiveTwitchEnd;
  329. CurState = STATE_LIVE_TWITCH;
  330. } else {
  331. frame0 = def->DeadTwitchStart;
  332. frame1 = def->DeadTwitchEnd;
  333. CurState = STATE_DEAD_TWITCH;
  334. }
  335. if (frame0 != frame1) {
  336. Get_Animation_Manager().Set_Animation_Mode(AnimCollisionManagerClass::ANIMATE_TARGET);
  337. Get_Animation_Manager().Set_Current_Frame(frame0);
  338. Get_Animation_Manager().Set_Target_Frame(frame1);
  339. }
  340. }
  341. } else {
  342. WWDEBUG_SAY(("ERROR: Missing definition for damageable object: %s\n",Model->Get_Name()));
  343. }
  344. }
  345. void DamageableStaticPhysClass::Play_Death_Transition(void)
  346. {
  347. const DamageableStaticPhysDefClass * def = Get_DamageableStaticPhysDef();
  348. if (def != NULL) {
  349. if (def->DeathTransitionStart != def->DeathTransitionEnd) {
  350. CurState = STATE_DEATH_TRANSITION;
  351. Get_Animation_Manager().Set_Animation_Mode(AnimCollisionManagerClass::ANIMATE_TARGET);
  352. Get_Animation_Manager().Set_Current_Frame(def->DeathTransitionStart);
  353. Get_Animation_Manager().Set_Target_Frame(def->DeathTransitionEnd);
  354. } else {
  355. Start_Loop();
  356. }
  357. } else {
  358. WWDEBUG_SAY(("ERROR: Missing definition for damageable object: %s\n",Model->Get_Name()));
  359. }
  360. }
  361. bool DamageableStaticPhysClass::Save( ChunkSaveClass & csave )
  362. {
  363. /*
  364. ** Save the parent class data
  365. */
  366. csave.Begin_Chunk(DAMAGEABLESTATICPHYS_CHUNK_STATICANIMPHYS);
  367. StaticAnimPhysClass::Save( csave );
  368. csave.End_Chunk();
  369. /*
  370. ** Save the contents of our damage object
  371. */
  372. csave.Begin_Chunk(DAMAGEABLESTATICPHYS_CHUNK_DEFENSEOBJECT);
  373. DefenseObject.Save(csave);
  374. csave.End_Chunk();
  375. /*
  376. ** Save our variables
  377. */
  378. csave.Begin_Chunk(DAMAGEABLESTATICPHYS_CHUNK_VARIABLES);
  379. WRITE_MICRO_CHUNK(csave, DAMAGEABLESTATICPHYS_VARIABLE_CURSTATE, CurState);
  380. csave.End_Chunk();
  381. return true;
  382. }
  383. bool DamageableStaticPhysClass::Load(ChunkLoadClass & cload)
  384. {
  385. while (cload.Open_Chunk()) {
  386. switch(cload.Cur_Chunk_ID()) {
  387. case DAMAGEABLESTATICPHYS_CHUNK_STATICANIMPHYS:
  388. StaticAnimPhysClass::Load( cload );
  389. break;
  390. case DAMAGEABLESTATICPHYS_CHUNK_DEFENSEOBJECT:
  391. DefenseObject.Load(cload);
  392. break;
  393. case DAMAGEABLESTATICPHYS_CHUNK_VARIABLES:
  394. while (cload.Open_Micro_Chunk()) {
  395. switch(cload.Cur_Micro_Chunk_ID()) {
  396. READ_MICRO_CHUNK(cload, DAMAGEABLESTATICPHYS_VARIABLE_CURSTATE, CurState);
  397. default:
  398. WWDEBUG_SAY(( "Unrecognized DamageableStaticPhys Variable chunkID\n" ));
  399. break;
  400. }
  401. cload.Close_Micro_Chunk();
  402. }
  403. break;
  404. default:
  405. WWDEBUG_SAY(( "Unrecognized DamageableStaticPhys chunkID\n" ));
  406. break;
  407. }
  408. cload.Close_Chunk();
  409. }
  410. Start_Loop();
  411. return true;
  412. }