| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408 |
- /*
- ** Command & Conquer Generals(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // FILE: StructureCollapseUpdate.cpp ///////////////////////////////////////////////////////////////////////
- // Author:
- // Desc:
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #include "Common/Thing.h"
- #include "Common/ThingTemplate.h"
- #include "Common/INI.h"
- #include "Common/RandomValue.h"
- #include "Common/GlobalData.h"
- #include "Common/Xfer.h"
- #include "GameClient/FXList.h"
- #include "GameLogic/GameLogic.h"
- #include "GameLogic/Module/BoneFXUpdate.h"
- #include "GameLogic/Module/StructureCollapseUpdate.h"
- #include "GameLogic/Module/AIUpdate.h"
- #include "GameLogic/Object.h"
- #include "GameLogic/ObjectCreationList.h"
- #include "GameClient/Drawable.h"
- #include "GameClient/InGameUI.h"
- const Int MAX_IDX = 32;
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- static const char *TheStructureCollapsePhaseNames[] =
- {
- "INITIAL",
- "DELAY",
- "BURST",
- "FINAL",
- NULL
- };
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- StructureCollapseUpdate::StructureCollapseUpdate( Thing *thing, const ModuleData* moduleData ) : UpdateModule( thing, moduleData )
- {
- m_collapseFrame = 0;
- m_collapseState = COLLAPSESTATE_STANDING;
- m_collapseVelocity = 0.0f;
- //Added By Sadullah Nader
- //Initialization(s) inserted
- m_burstFrame = 0;
- m_currentHeight = 0.0f;
- //
- setWakeFrame(getObject(), UPDATE_SLEEP_FOREVER);
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- StructureCollapseUpdate::~StructureCollapseUpdate( void )
- {
- }
- //-------------------------------------------------------------------------------------------------
- static void parseFX( INI* ini, void *instance, void * /*store*/, const void* /*userData*/ )
- {
- StructureCollapseUpdateModuleData* self = (StructureCollapseUpdateModuleData*)instance;
- StructureCollapsePhaseType scphase = (StructureCollapsePhaseType)INI::scanIndexList(ini->getNextToken(), TheStructureCollapsePhaseNames);
- for (const char* token = ini->getNextToken(); token != NULL; token = ini->getNextTokenOrNull())
- {
- const FXList *fxl = TheFXListStore->findFXList((token)); // could be null! this is OK!
- self->m_fxs[scphase].push_back(fxl);
- }
- }
- //-------------------------------------------------------------------------------------------------
- static void parseOCL( INI* ini, void *instance, void * /*store*/, const void* /*userData*/ )
- {
- StructureCollapseUpdateModuleData* self = (StructureCollapseUpdateModuleData*)instance;
- StructureCollapsePhaseType stphase = (StructureCollapsePhaseType)INI::scanIndexList(ini->getNextToken(), TheStructureCollapsePhaseNames);
- for (const char* token = ini->getNextToken(); token != NULL; token = ini->getNextTokenOrNull())
- {
- const ObjectCreationList *ocl = TheObjectCreationListStore->findObjectCreationList(token); // could be null! this is OK!
- self->m_ocls[stphase].push_back(ocl);
- }
- }
- //-------------------------------------------------------------------------------------------------
- /*static*/ void StructureCollapseUpdateModuleData::buildFieldParse(MultiIniFieldParse& p)
- {
- UpdateModuleData::buildFieldParse(p);
- static const FieldParse dataFieldParse[] =
- {
- { "MinCollapseDelay", INI::parseDurationUnsignedInt, NULL, offsetof( StructureCollapseUpdateModuleData, m_minCollapseDelay ) },
- { "MaxCollapseDelay", INI::parseDurationUnsignedInt, NULL, offsetof( StructureCollapseUpdateModuleData, m_maxCollapseDelay ) },
- { "MinBurstDelay", INI::parseDurationUnsignedInt, NULL, offsetof( StructureCollapseUpdateModuleData, m_minBurstDelay ) },
- { "MaxBurstDelay", INI::parseDurationUnsignedInt, NULL, offsetof( StructureCollapseUpdateModuleData, m_maxBurstDelay ) },
- { "CollapseDamping", INI::parseReal, NULL, offsetof( StructureCollapseUpdateModuleData, m_collapseDamping ) },
- { "MaxShudder", INI::parseReal, NULL, offsetof( StructureCollapseUpdateModuleData, m_maxShudder ) },
- { "BigBurstFrequency", INI::parseInt, NULL, offsetof( StructureCollapseUpdateModuleData, m_bigBurstFrequency ) },
- { "OCL", parseOCL, NULL, 0 },
- { "FXList", parseFX, NULL, 0 },
- { 0, 0, 0, 0 }
- };
- p.add(dataFieldParse);
- p.add(DieMuxData::getFieldParse(), offsetof( StructureCollapseUpdateModuleData, m_dieMuxData ));
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void StructureCollapseUpdate::beginStructureCollapse(const DamageInfo *damageInfo)
- {
- const StructureCollapseUpdateModuleData *d = getStructureCollapseUpdateModuleData();
- Object *building = getObject();
- UnsignedInt now = TheGameLogic->getFrame();
- // This has to use a game logic random value since the bursts can spawn debris, and debris is sync'd.
- m_collapseFrame = now + GameLogicRandomValue(d->m_minCollapseDelay, d->m_maxCollapseDelay);
- doPhaseStuff(SCPHASE_INITIAL, building->getPosition());
- m_collapseState = COLLAPSESTATE_WAITINGFORCOLLAPSESTART;
- m_currentHeight = 0.0f;
- setWakeFrame(getObject(), UPDATE_SLEEP_NONE);
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void StructureCollapseUpdate::onDie( const DamageInfo *damageInfo )
- {
- const StructureCollapseUpdateModuleData* d = getStructureCollapseUpdateModuleData();
- if (!d->m_dieMuxData.isDieApplicable(getObject(), damageInfo))
- return;
- AIUpdateInterface *ai = getObject()->getAIUpdateInterface();
- if (ai)
- ai->markAsDead();
- // deselect this object for all players.
- TheGameLogic->deselectObject(getObject(), PLAYERMASK_ALL, TRUE);
- beginStructureCollapse(damageInfo);
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- UpdateSleepTime StructureCollapseUpdate::update( void )
- {
- static const Real COLLAPSE_ACCELERATION_FACTOR = 0.02f;
- const StructureCollapseUpdateModuleData *d = getStructureCollapseUpdateModuleData();
- if (m_collapseState == COLLAPSESTATE_STANDING)
- {
- DEBUG_CRASH(("hmm, what?"));
- return UPDATE_SLEEP_FOREVER;
- }
- // We are in the dramatic pause between when the building has lost all its hit points and
- // when it starts toppling over.
- if (m_collapseState == COLLAPSESTATE_WAITINGFORCOLLAPSESTART)
- {
- UnsignedInt now = TheGameLogic->getFrame();
- Object *building = getObject();
- const Coord3D *currentPosition = building->getPosition();
- Vector3 shudder;
- shudder.Set(GameClientRandomValueReal(-(d->m_maxShudder), d->m_maxShudder), GameClientRandomValueReal(-(d->m_maxShudder), d->m_maxShudder), 0);
- const Matrix3D *instMatrix = building->getDrawable()->getInstanceMatrix();
- Matrix3D newInstMatrix;
- newInstMatrix = *instMatrix;
- newInstMatrix.Set_Translation(shudder);
- building->getDrawable()->setInstanceMatrix(&newInstMatrix);
- if (now >= m_collapseFrame)
- {
- m_collapseState = COLLAPSESTATE_COLLAPSING;
- doPhaseStuff(SCPHASE_BURST, currentPosition);
- // This has to use a game logic random value since the bursts can spawn debris, and debris is sync'd.
- m_burstFrame = now + GameLogicRandomValue(d->m_minBurstDelay, d->m_maxBurstDelay);
- }
- }
- // The building is in the process of falling over.
- if (m_collapseState == COLLAPSESTATE_COLLAPSING)
- {
- Object *building = getObject();
- UnsignedInt now = TheGameLogic->getFrame();
- m_currentHeight -= m_collapseVelocity;
- m_collapseVelocity -= TheGlobalData->m_gravity * (1.0 - d->m_collapseDamping);
- const Coord3D *currentPosition = building->getPosition();
- Vector3 shudder;
- shudder.Set(GameClientRandomValueReal(-(d->m_maxShudder), d->m_maxShudder), GameClientRandomValueReal(-(d->m_maxShudder), d->m_maxShudder), m_currentHeight);
- const Matrix3D *instMatrix = building->getDrawable()->getInstanceMatrix();
- Matrix3D newInstMatrix;
- newInstMatrix = *instMatrix;
- newInstMatrix.Set_Translation(shudder);
- building->getDrawable()->setInstanceMatrix(&newInstMatrix);
- if (now >= m_burstFrame)
- {
- if (GameLogicRandomValue(1, d->m_bigBurstFrequency) == 1)
- {
- doPhaseStuff(SCPHASE_BURST, currentPosition);
- }
- else
- {
- doPhaseStuff(SCPHASE_DELAY, currentPosition);
- }
- // This has to use a game logic random value since the bursts can spawn debris, and debris is sync'd.
- m_burstFrame += GameLogicRandomValue(d->m_minBurstDelay, d->m_maxBurstDelay);
- }
- // if ((m_currentHeight + building->getGeometryInfo().getMaxHeightAbovePosition()) <= 0)
- if ((m_currentHeight + building->getTemplate()->getTemplateGeometryInfo().getMaxHeightAbovePosition()) <= 0)
- {
- m_collapseState = COLLAPSESTATE_DONE;
- doPhaseStuff(SCPHASE_FINAL, building->getPosition());
- Drawable *drawable = building->getDrawable();
- doCollapseDoneStuff();
- drawable->clearModelConditionState(MODELCONDITION_RUBBLE);
- drawable->setModelConditionState(MODELCONDITION_POST_COLLAPSE);
- building->setOrientation(building->getOrientation());
-
- // Need to update body particle systems, now
- BodyModuleInterface *body = building->getBodyModule();
- body->updateBodyParticleSystems();
- Vector3 shudder;
- shudder.Set(0, 0, 0);
- const Matrix3D *instMatrix = building->getDrawable()->getInstanceMatrix();
- Matrix3D newInstMatrix;
- newInstMatrix = *instMatrix;
- newInstMatrix.Set_Translation(shudder);
- building->getDrawable()->setInstanceMatrix(&newInstMatrix);
- return UPDATE_SLEEP_FOREVER;
- }
- }
- return UPDATE_SLEEP_NONE;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- inline Bool inList(Int value, Int count, const Int idxList[])
- {
- for (Int j = 0; j < count; ++j)
- {
- if (idxList[j] == value)
- return true;
- }
- return false;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- static void buildNonDupRandomIndexList(Int range, Int count, Int idxList[])
- {
- for (Int i = 0; i < count; ++i)
- {
- Int idx;
- do
- {
- idx = GameLogicRandomValue(0, range-1);
- }
- while (inList(idx, i, idxList));
- idxList[i] = idx;
- }
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void StructureCollapseUpdate::doPhaseStuff(StructureCollapsePhaseType scphase, const Coord3D *target)
- {
- DEBUG_LOG(("Firing phase %d on frame %d\n", scphase, TheGameLogic->getFrame()));
- const StructureCollapseUpdateModuleData* d = getStructureCollapseUpdateModuleData();
- Int i, idx, count, listSize;
- Int idxList[MAX_IDX];
- listSize = d->m_fxs[scphase].size();
- if (listSize > 0)
- {
- count = d->m_fxCount[scphase];
- buildNonDupRandomIndexList(listSize, count, idxList);
- for (i = 0; i < count; ++i)
- {
- idx = idxList[i];
- const FXVec& v = d->m_fxs[scphase];
- DEBUG_ASSERTCRASH(idx>=0&&idx<v.size(),("bad idx"));
- const FXList* fxl = v[idx];
- FXList::doFXPos(fxl, target);
- }
- }
- listSize = d->m_ocls[scphase].size();
- if (listSize > 0)
- {
- count = d->m_oclCount[scphase];
- buildNonDupRandomIndexList(listSize, count, idxList);
- for (i = 0; i < count; ++i)
- {
- idx = idxList[i];
- const OCLVec& v = d->m_ocls[scphase];
- DEBUG_ASSERTCRASH(idx>=0&&idx<v.size(),("bad idx"));
- const ObjectCreationList* ocl = v[idx];
- ObjectCreationList::create(ocl, getObject(), target, NULL);
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void StructureCollapseUpdate::doCollapseDoneStuff()
- {
- static NameKeyType key_BoneFXUpdate = NAMEKEY("BoneFXUpdate");
- BoneFXUpdate *bfxu = (BoneFXUpdate *)getObject()->findUpdateModule(key_BoneFXUpdate);
- if (bfxu != NULL)
- {
- bfxu->stopAllBoneFX();
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void StructureCollapseUpdate::crc( Xfer *xfer )
- {
- // extend base class
- UpdateModule::crc( xfer );
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer method
- * Version Info:
- * 1: Initial version */
- // ------------------------------------------------------------------------------------------------
- void StructureCollapseUpdate::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- // extend base class
- UpdateModule::xfer( xfer );
- // collapse frame
- xfer->xferUnsignedInt( &m_collapseFrame );
- // burst frame
- xfer->xferUnsignedInt( &m_burstFrame );
- // collapse state
- xfer->xferUser( &m_collapseState, sizeof( StructureCollapseStateType ) );
- // collapse velocity
- xfer->xferReal( &m_collapseVelocity );
- // current height
- xfer->xferReal( &m_currentHeight );
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void StructureCollapseUpdate::loadPostProcess( void )
- {
- // extend base class
- UpdateModule::loadPostProcess();
- } // end loadPostProcess
|