StructureCollapseUpdate.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408
  1. /*
  2. ** Command & Conquer Generals(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: StructureCollapseUpdate.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/RandomValue.h"
  33. #include "Common/GlobalData.h"
  34. #include "Common/Xfer.h"
  35. #include "GameClient/FXList.h"
  36. #include "GameLogic/GameLogic.h"
  37. #include "GameLogic/Module/BoneFXUpdate.h"
  38. #include "GameLogic/Module/StructureCollapseUpdate.h"
  39. #include "GameLogic/Module/AIUpdate.h"
  40. #include "GameLogic/Object.h"
  41. #include "GameLogic/ObjectCreationList.h"
  42. #include "GameClient/Drawable.h"
  43. #include "GameClient/InGameUI.h"
  44. const Int MAX_IDX = 32;
  45. //-------------------------------------------------------------------------------------------------
  46. //-------------------------------------------------------------------------------------------------
  47. static const char *TheStructureCollapsePhaseNames[] =
  48. {
  49. "INITIAL",
  50. "DELAY",
  51. "BURST",
  52. "FINAL",
  53. NULL
  54. };
  55. //-------------------------------------------------------------------------------------------------
  56. //-------------------------------------------------------------------------------------------------
  57. StructureCollapseUpdate::StructureCollapseUpdate( Thing *thing, const ModuleData* moduleData ) : UpdateModule( thing, moduleData )
  58. {
  59. m_collapseFrame = 0;
  60. m_collapseState = COLLAPSESTATE_STANDING;
  61. m_collapseVelocity = 0.0f;
  62. //Added By Sadullah Nader
  63. //Initialization(s) inserted
  64. m_burstFrame = 0;
  65. m_currentHeight = 0.0f;
  66. //
  67. setWakeFrame(getObject(), UPDATE_SLEEP_FOREVER);
  68. }
  69. //-------------------------------------------------------------------------------------------------
  70. //-------------------------------------------------------------------------------------------------
  71. StructureCollapseUpdate::~StructureCollapseUpdate( void )
  72. {
  73. }
  74. //-------------------------------------------------------------------------------------------------
  75. static void parseFX( INI* ini, void *instance, void * /*store*/, const void* /*userData*/ )
  76. {
  77. StructureCollapseUpdateModuleData* self = (StructureCollapseUpdateModuleData*)instance;
  78. StructureCollapsePhaseType scphase = (StructureCollapsePhaseType)INI::scanIndexList(ini->getNextToken(), TheStructureCollapsePhaseNames);
  79. for (const char* token = ini->getNextToken(); token != NULL; token = ini->getNextTokenOrNull())
  80. {
  81. const FXList *fxl = TheFXListStore->findFXList((token)); // could be null! this is OK!
  82. self->m_fxs[scphase].push_back(fxl);
  83. }
  84. }
  85. //-------------------------------------------------------------------------------------------------
  86. static void parseOCL( INI* ini, void *instance, void * /*store*/, const void* /*userData*/ )
  87. {
  88. StructureCollapseUpdateModuleData* self = (StructureCollapseUpdateModuleData*)instance;
  89. StructureCollapsePhaseType stphase = (StructureCollapsePhaseType)INI::scanIndexList(ini->getNextToken(), TheStructureCollapsePhaseNames);
  90. for (const char* token = ini->getNextToken(); token != NULL; token = ini->getNextTokenOrNull())
  91. {
  92. const ObjectCreationList *ocl = TheObjectCreationListStore->findObjectCreationList(token); // could be null! this is OK!
  93. self->m_ocls[stphase].push_back(ocl);
  94. }
  95. }
  96. //-------------------------------------------------------------------------------------------------
  97. /*static*/ void StructureCollapseUpdateModuleData::buildFieldParse(MultiIniFieldParse& p)
  98. {
  99. UpdateModuleData::buildFieldParse(p);
  100. static const FieldParse dataFieldParse[] =
  101. {
  102. { "MinCollapseDelay", INI::parseDurationUnsignedInt, NULL, offsetof( StructureCollapseUpdateModuleData, m_minCollapseDelay ) },
  103. { "MaxCollapseDelay", INI::parseDurationUnsignedInt, NULL, offsetof( StructureCollapseUpdateModuleData, m_maxCollapseDelay ) },
  104. { "MinBurstDelay", INI::parseDurationUnsignedInt, NULL, offsetof( StructureCollapseUpdateModuleData, m_minBurstDelay ) },
  105. { "MaxBurstDelay", INI::parseDurationUnsignedInt, NULL, offsetof( StructureCollapseUpdateModuleData, m_maxBurstDelay ) },
  106. { "CollapseDamping", INI::parseReal, NULL, offsetof( StructureCollapseUpdateModuleData, m_collapseDamping ) },
  107. { "MaxShudder", INI::parseReal, NULL, offsetof( StructureCollapseUpdateModuleData, m_maxShudder ) },
  108. { "BigBurstFrequency", INI::parseInt, NULL, offsetof( StructureCollapseUpdateModuleData, m_bigBurstFrequency ) },
  109. { "OCL", parseOCL, NULL, 0 },
  110. { "FXList", parseFX, NULL, 0 },
  111. { 0, 0, 0, 0 }
  112. };
  113. p.add(dataFieldParse);
  114. p.add(DieMuxData::getFieldParse(), offsetof( StructureCollapseUpdateModuleData, m_dieMuxData ));
  115. }
  116. //-------------------------------------------------------------------------------------------------
  117. //-------------------------------------------------------------------------------------------------
  118. void StructureCollapseUpdate::beginStructureCollapse(const DamageInfo *damageInfo)
  119. {
  120. const StructureCollapseUpdateModuleData *d = getStructureCollapseUpdateModuleData();
  121. Object *building = getObject();
  122. UnsignedInt now = TheGameLogic->getFrame();
  123. // This has to use a game logic random value since the bursts can spawn debris, and debris is sync'd.
  124. m_collapseFrame = now + GameLogicRandomValue(d->m_minCollapseDelay, d->m_maxCollapseDelay);
  125. doPhaseStuff(SCPHASE_INITIAL, building->getPosition());
  126. m_collapseState = COLLAPSESTATE_WAITINGFORCOLLAPSESTART;
  127. m_currentHeight = 0.0f;
  128. setWakeFrame(getObject(), UPDATE_SLEEP_NONE);
  129. }
  130. //-------------------------------------------------------------------------------------------------
  131. //-------------------------------------------------------------------------------------------------
  132. void StructureCollapseUpdate::onDie( const DamageInfo *damageInfo )
  133. {
  134. const StructureCollapseUpdateModuleData* d = getStructureCollapseUpdateModuleData();
  135. if (!d->m_dieMuxData.isDieApplicable(getObject(), damageInfo))
  136. return;
  137. AIUpdateInterface *ai = getObject()->getAIUpdateInterface();
  138. if (ai)
  139. ai->markAsDead();
  140. // deselect this object for all players.
  141. TheGameLogic->deselectObject(getObject(), PLAYERMASK_ALL, TRUE);
  142. beginStructureCollapse(damageInfo);
  143. }
  144. //-------------------------------------------------------------------------------------------------
  145. //-------------------------------------------------------------------------------------------------
  146. UpdateSleepTime StructureCollapseUpdate::update( void )
  147. {
  148. static const Real COLLAPSE_ACCELERATION_FACTOR = 0.02f;
  149. const StructureCollapseUpdateModuleData *d = getStructureCollapseUpdateModuleData();
  150. if (m_collapseState == COLLAPSESTATE_STANDING)
  151. {
  152. DEBUG_CRASH(("hmm, what?"));
  153. return UPDATE_SLEEP_FOREVER;
  154. }
  155. // We are in the dramatic pause between when the building has lost all its hit points and
  156. // when it starts toppling over.
  157. if (m_collapseState == COLLAPSESTATE_WAITINGFORCOLLAPSESTART)
  158. {
  159. UnsignedInt now = TheGameLogic->getFrame();
  160. Object *building = getObject();
  161. const Coord3D *currentPosition = building->getPosition();
  162. Vector3 shudder;
  163. shudder.Set(GameClientRandomValueReal(-(d->m_maxShudder), d->m_maxShudder), GameClientRandomValueReal(-(d->m_maxShudder), d->m_maxShudder), 0);
  164. const Matrix3D *instMatrix = building->getDrawable()->getInstanceMatrix();
  165. Matrix3D newInstMatrix;
  166. newInstMatrix = *instMatrix;
  167. newInstMatrix.Set_Translation(shudder);
  168. building->getDrawable()->setInstanceMatrix(&newInstMatrix);
  169. if (now >= m_collapseFrame)
  170. {
  171. m_collapseState = COLLAPSESTATE_COLLAPSING;
  172. doPhaseStuff(SCPHASE_BURST, currentPosition);
  173. // This has to use a game logic random value since the bursts can spawn debris, and debris is sync'd.
  174. m_burstFrame = now + GameLogicRandomValue(d->m_minBurstDelay, d->m_maxBurstDelay);
  175. }
  176. }
  177. // The building is in the process of falling over.
  178. if (m_collapseState == COLLAPSESTATE_COLLAPSING)
  179. {
  180. Object *building = getObject();
  181. UnsignedInt now = TheGameLogic->getFrame();
  182. m_currentHeight -= m_collapseVelocity;
  183. m_collapseVelocity -= TheGlobalData->m_gravity * (1.0 - d->m_collapseDamping);
  184. const Coord3D *currentPosition = building->getPosition();
  185. Vector3 shudder;
  186. shudder.Set(GameClientRandomValueReal(-(d->m_maxShudder), d->m_maxShudder), GameClientRandomValueReal(-(d->m_maxShudder), d->m_maxShudder), m_currentHeight);
  187. const Matrix3D *instMatrix = building->getDrawable()->getInstanceMatrix();
  188. Matrix3D newInstMatrix;
  189. newInstMatrix = *instMatrix;
  190. newInstMatrix.Set_Translation(shudder);
  191. building->getDrawable()->setInstanceMatrix(&newInstMatrix);
  192. if (now >= m_burstFrame)
  193. {
  194. if (GameLogicRandomValue(1, d->m_bigBurstFrequency) == 1)
  195. {
  196. doPhaseStuff(SCPHASE_BURST, currentPosition);
  197. }
  198. else
  199. {
  200. doPhaseStuff(SCPHASE_DELAY, currentPosition);
  201. }
  202. // This has to use a game logic random value since the bursts can spawn debris, and debris is sync'd.
  203. m_burstFrame += GameLogicRandomValue(d->m_minBurstDelay, d->m_maxBurstDelay);
  204. }
  205. // if ((m_currentHeight + building->getGeometryInfo().getMaxHeightAbovePosition()) <= 0)
  206. if ((m_currentHeight + building->getTemplate()->getTemplateGeometryInfo().getMaxHeightAbovePosition()) <= 0)
  207. {
  208. m_collapseState = COLLAPSESTATE_DONE;
  209. doPhaseStuff(SCPHASE_FINAL, building->getPosition());
  210. Drawable *drawable = building->getDrawable();
  211. doCollapseDoneStuff();
  212. drawable->clearModelConditionState(MODELCONDITION_RUBBLE);
  213. drawable->setModelConditionState(MODELCONDITION_POST_COLLAPSE);
  214. building->setOrientation(building->getOrientation());
  215. // Need to update body particle systems, now
  216. BodyModuleInterface *body = building->getBodyModule();
  217. body->updateBodyParticleSystems();
  218. Vector3 shudder;
  219. shudder.Set(0, 0, 0);
  220. const Matrix3D *instMatrix = building->getDrawable()->getInstanceMatrix();
  221. Matrix3D newInstMatrix;
  222. newInstMatrix = *instMatrix;
  223. newInstMatrix.Set_Translation(shudder);
  224. building->getDrawable()->setInstanceMatrix(&newInstMatrix);
  225. return UPDATE_SLEEP_FOREVER;
  226. }
  227. }
  228. return UPDATE_SLEEP_NONE;
  229. }
  230. //-------------------------------------------------------------------------------------------------
  231. //-------------------------------------------------------------------------------------------------
  232. inline Bool inList(Int value, Int count, const Int idxList[])
  233. {
  234. for (Int j = 0; j < count; ++j)
  235. {
  236. if (idxList[j] == value)
  237. return true;
  238. }
  239. return false;
  240. }
  241. //-------------------------------------------------------------------------------------------------
  242. //-------------------------------------------------------------------------------------------------
  243. static void buildNonDupRandomIndexList(Int range, Int count, Int idxList[])
  244. {
  245. for (Int i = 0; i < count; ++i)
  246. {
  247. Int idx;
  248. do
  249. {
  250. idx = GameLogicRandomValue(0, range-1);
  251. }
  252. while (inList(idx, i, idxList));
  253. idxList[i] = idx;
  254. }
  255. }
  256. //-------------------------------------------------------------------------------------------------
  257. //-------------------------------------------------------------------------------------------------
  258. void StructureCollapseUpdate::doPhaseStuff(StructureCollapsePhaseType scphase, const Coord3D *target)
  259. {
  260. DEBUG_LOG(("Firing phase %d on frame %d\n", scphase, TheGameLogic->getFrame()));
  261. const StructureCollapseUpdateModuleData* d = getStructureCollapseUpdateModuleData();
  262. Int i, idx, count, listSize;
  263. Int idxList[MAX_IDX];
  264. listSize = d->m_fxs[scphase].size();
  265. if (listSize > 0)
  266. {
  267. count = d->m_fxCount[scphase];
  268. buildNonDupRandomIndexList(listSize, count, idxList);
  269. for (i = 0; i < count; ++i)
  270. {
  271. idx = idxList[i];
  272. const FXVec& v = d->m_fxs[scphase];
  273. DEBUG_ASSERTCRASH(idx>=0&&idx<v.size(),("bad idx"));
  274. const FXList* fxl = v[idx];
  275. FXList::doFXPos(fxl, target);
  276. }
  277. }
  278. listSize = d->m_ocls[scphase].size();
  279. if (listSize > 0)
  280. {
  281. count = d->m_oclCount[scphase];
  282. buildNonDupRandomIndexList(listSize, count, idxList);
  283. for (i = 0; i < count; ++i)
  284. {
  285. idx = idxList[i];
  286. const OCLVec& v = d->m_ocls[scphase];
  287. DEBUG_ASSERTCRASH(idx>=0&&idx<v.size(),("bad idx"));
  288. const ObjectCreationList* ocl = v[idx];
  289. ObjectCreationList::create(ocl, getObject(), target, NULL);
  290. }
  291. }
  292. }
  293. //-------------------------------------------------------------------------------------------------
  294. //-------------------------------------------------------------------------------------------------
  295. void StructureCollapseUpdate::doCollapseDoneStuff()
  296. {
  297. static NameKeyType key_BoneFXUpdate = NAMEKEY("BoneFXUpdate");
  298. BoneFXUpdate *bfxu = (BoneFXUpdate *)getObject()->findUpdateModule(key_BoneFXUpdate);
  299. if (bfxu != NULL)
  300. {
  301. bfxu->stopAllBoneFX();
  302. }
  303. }
  304. // ------------------------------------------------------------------------------------------------
  305. /** CRC */
  306. // ------------------------------------------------------------------------------------------------
  307. void StructureCollapseUpdate::crc( Xfer *xfer )
  308. {
  309. // extend base class
  310. UpdateModule::crc( xfer );
  311. } // end crc
  312. // ------------------------------------------------------------------------------------------------
  313. /** Xfer method
  314. * Version Info:
  315. * 1: Initial version */
  316. // ------------------------------------------------------------------------------------------------
  317. void StructureCollapseUpdate::xfer( Xfer *xfer )
  318. {
  319. // version
  320. XferVersion currentVersion = 1;
  321. XferVersion version = currentVersion;
  322. xfer->xferVersion( &version, currentVersion );
  323. // extend base class
  324. UpdateModule::xfer( xfer );
  325. // collapse frame
  326. xfer->xferUnsignedInt( &m_collapseFrame );
  327. // burst frame
  328. xfer->xferUnsignedInt( &m_burstFrame );
  329. // collapse state
  330. xfer->xferUser( &m_collapseState, sizeof( StructureCollapseStateType ) );
  331. // collapse velocity
  332. xfer->xferReal( &m_collapseVelocity );
  333. // current height
  334. xfer->xferReal( &m_currentHeight );
  335. } // end xfer
  336. // ------------------------------------------------------------------------------------------------
  337. /** Load post process */
  338. // ------------------------------------------------------------------------------------------------
  339. void StructureCollapseUpdate::loadPostProcess( void )
  340. {
  341. // extend base class
  342. UpdateModule::loadPostProcess();
  343. } // end loadPostProcess