RebuildHoleBehavior.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488
  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: RebuildHoleBehavior.cpp //////////////////////////////////////////////////////////////////
  24. // Author: Colin Day, June 2002
  25. // Desc: GLA Hole behavior that reconstructs a building after death
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #include "Common/GameState.h"
  30. #include "Common/ThingFactory.h"
  31. #include "Common/ThingTemplate.h"
  32. #include "Common/Xfer.h"
  33. #include "GameClient/Drawable.h"
  34. #include "GameLogic/GameLogic.h"
  35. #include "GameLogic/Object.h"
  36. #include "GameLogic/ScriptEngine.h"
  37. #include "GameLogic/Module/AIUpdate.h"
  38. #include "GameLogic/Module/BodyModule.h"
  39. #include "GameLogic/Module/RebuildHoleBehavior.h"
  40. #include "GameLogic/Module/StickyBombUpdate.h"
  41. #ifdef _INTERNAL
  42. // for occasional debugging...
  43. //#pragma optimize("", off)
  44. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  45. #endif
  46. //-------------------------------------------------------------------------------------------------
  47. // ------------------------------------------------------------------------------------------------
  48. RebuildHoleBehaviorModuleData::RebuildHoleBehaviorModuleData( void )
  49. {
  50. m_workerRespawnDelay = 0.0f;
  51. m_holeHealthRegenPercentPerSecond = 0.1f;
  52. } // end RebuildHoleBehaviorModuleData
  53. //-------------------------------------------------------------------------------------------------
  54. // ------------------------------------------------------------------------------------------------
  55. /*static*/ void RebuildHoleBehaviorModuleData::buildFieldParse( MultiIniFieldParse &p )
  56. {
  57. UpdateModuleData::buildFieldParse( p );
  58. static const FieldParse dataFieldParse[] =
  59. {
  60. { "WorkerObjectName", INI::parseAsciiString, NULL, offsetof( RebuildHoleBehaviorModuleData, m_workerTemplateName ) },
  61. { "WorkerRespawnDelay", INI::parseDurationReal, NULL, offsetof( RebuildHoleBehaviorModuleData, m_workerRespawnDelay ) },
  62. { "HoleHealthRegen%PerSecond", INI::parsePercentToReal, NULL, offsetof( RebuildHoleBehaviorModuleData, m_holeHealthRegenPercentPerSecond ) },
  63. { 0, 0, 0, 0 }
  64. };
  65. p.add( dataFieldParse );
  66. } // end buildFieldParse
  67. ///////////////////////////////////////////////////////////////////////////////////////////////////
  68. ///////////////////////////////////////////////////////////////////////////////////////////////////
  69. ///////////////////////////////////////////////////////////////////////////////////////////////////
  70. //-------------------------------------------------------------------------------------------------
  71. //-------------------------------------------------------------------------------------------------
  72. RebuildHoleBehavior::RebuildHoleBehavior( Thing *thing, const ModuleData* moduleData )
  73. : UpdateModule( thing, moduleData )
  74. {
  75. m_workerID = INVALID_ID;
  76. m_reconstructingID = INVALID_ID;
  77. m_spawnerObjectID = INVALID_ID;
  78. m_workerWaitCounter = 0;
  79. m_workerTemplate = NULL;
  80. m_rebuildTemplate = NULL;
  81. } // end RebuildHoleBehavior
  82. //-------------------------------------------------------------------------------------------------
  83. //-------------------------------------------------------------------------------------------------
  84. RebuildHoleBehavior::~RebuildHoleBehavior( void )
  85. {
  86. // ensure that our generated worker is destroyed,
  87. // just in case someone decides to destroy (not kill) us...
  88. if( m_workerID != INVALID_ID )
  89. {
  90. Object *worker = TheGameLogic->findObjectByID(m_workerID);
  91. if( worker )
  92. {
  93. TheGameLogic->destroyObject(worker);
  94. m_workerID = INVALID_ID;
  95. }
  96. }
  97. } // end ~RebuildHoleBehavior
  98. // ------------------------------------------------------------------------------------------------
  99. /** we need to start all the timers and ID ties to make a new worker at the correct time */
  100. // ------------------------------------------------------------------------------------------------
  101. void RebuildHoleBehavior::newWorkerRespawnProcess( Object *existingWorker )
  102. {
  103. const RebuildHoleBehaviorModuleData *modData = getRebuildHoleBehaviorModuleData();
  104. // if we have an existing worker, get rid of it
  105. if( existingWorker )
  106. {
  107. DEBUG_ASSERTCRASH(existingWorker->getID() == m_workerID, ("m_workerID mismatch in RebuildHole"));
  108. TheGameLogic->destroyObject( existingWorker );
  109. }
  110. m_workerID = INVALID_ID;
  111. // set the timer for the next worker respawn
  112. m_workerWaitCounter = modData->m_workerRespawnDelay;
  113. //
  114. // this method is called when a worker needs to be respawned from the hole. One of those
  115. // situations is where the building was killed. Since during building reconstruction
  116. // we made the hole "effectively not here" we will always want to make the hole
  117. // "here again" (able to be selected, targeted by the AI etc) because it's the
  118. // "focus" of this small area again
  119. //
  120. getObject()->maskObject( FALSE );
  121. } // end newWorkerRespawnProcess
  122. // ------------------------------------------------------------------------------------------------
  123. // ------------------------------------------------------------------------------------------------
  124. void RebuildHoleBehavior::startRebuildProcess( const ThingTemplate *rebuild, ObjectID spawnerID )
  125. {
  126. // save what we're gonna do
  127. m_rebuildTemplate = rebuild;
  128. // store the object that spawned this hole (even though it's likely being destroyed)
  129. m_spawnerObjectID = spawnerID;
  130. // start the spawning process for a worker
  131. newWorkerRespawnProcess( NULL );
  132. } /// end startRebuildProcess
  133. //----------------------------------------------------------------------------------------------
  134. void RebuildHoleBehavior::transferBombs( Object *reconstruction )
  135. {
  136. Object *self = getObject();
  137. Object *obj = TheGameLogic->getFirstObject();
  138. while( obj )
  139. {
  140. if( obj->isKindOf( KINDOF_MINE ) )
  141. {
  142. static NameKeyType key_StickyBombUpdate = NAMEKEY( "StickyBombUpdate" );
  143. StickyBombUpdate *update = (StickyBombUpdate*)obj->findUpdateModule( key_StickyBombUpdate );
  144. if( update && update->getTargetObject() == self )
  145. {
  146. update->setTargetObject( reconstruction );
  147. }
  148. }
  149. obj = obj->getNextObject();
  150. }
  151. }
  152. //-------------------------------------------------------------------------------------------------
  153. //-------------------------------------------------------------------------------------------------
  154. UpdateSleepTime RebuildHoleBehavior::update( void )
  155. {
  156. const RebuildHoleBehaviorModuleData *modData = getRebuildHoleBehaviorModuleData();
  157. Object *hole = getObject();
  158. Object *reconstructing = NULL;
  159. Object *worker = NULL;
  160. // get the worker object if we have one
  161. if( m_workerID != 0 )
  162. {
  163. // get the worker
  164. worker = TheGameLogic->findObjectByID( m_workerID );
  165. // if the worker is no longer there, start the respawning process for a worker again
  166. if( worker == NULL )
  167. newWorkerRespawnProcess( NULL );
  168. } // end if
  169. // if we have a reconstructing object built, get the actual object pointer
  170. if( m_reconstructingID != 0 )
  171. {
  172. // get object pointer
  173. reconstructing = TheGameLogic->findObjectByID( m_reconstructingID );
  174. //
  175. // if that object does not exist anymore, we need to kill a worker if we have one
  176. // and start the spawning process over again
  177. //
  178. if( reconstructing == NULL )
  179. {
  180. newWorkerRespawnProcess( worker );
  181. m_reconstructingID = INVALID_ID;
  182. } // end if
  183. } // end if
  184. // see if it's time for us to spawn a worker
  185. if( worker == NULL && m_workerWaitCounter > 0 )
  186. {
  187. // decrement counter and respawn if it's time
  188. if( --m_workerWaitCounter == 0 )
  189. {
  190. // resolve the worker template pointer if necessary
  191. if( m_workerTemplate == NULL )
  192. m_workerTemplate = TheThingFactory->findTemplate( modData->m_workerTemplateName );
  193. // create a worker
  194. worker = TheThingFactory->newObject( m_workerTemplate, hole->getTeam() );
  195. if( worker )
  196. {
  197. // set the position of the worker to that of the hole
  198. worker->setPosition( hole->getPosition() );
  199. // save the ID of the worker spawned
  200. m_workerID = worker->getID();
  201. //
  202. // tell the worker to begin construction of a building if one does not
  203. // exist yet. If one does, have construction resume
  204. //
  205. AIUpdateInterface *ai = worker->getAIUpdateInterface();
  206. if( ai )
  207. {
  208. if( reconstructing == NULL )
  209. reconstructing = ai->construct( m_rebuildTemplate,
  210. hole->getPosition(),
  211. hole->getOrientation(),
  212. hole->getControllingPlayer(),
  213. TRUE );
  214. else
  215. ai->aiResumeConstruction( reconstructing, CMD_FROM_AI );
  216. for ( Object *obj = TheGameLogic->getFirstObject(); obj; obj = obj->getNextObject() )
  217. {
  218. // Just like the building transfers attackers to the hole when it creates us, we need to transfer
  219. // attackers to our replacement building before we mask ourselves.
  220. AIUpdateInterface* ai = obj->getAI();
  221. if (!ai)
  222. continue;
  223. ai->transferAttack(hole->getID(), reconstructing->getID());
  224. }
  225. // save the id of what we are reconstructing
  226. m_reconstructingID = reconstructing->getID();
  227. // we want to prevent the player from selecting and doing things with this worker
  228. worker->setStatus( OBJECT_STATUS_UNSELECTABLE );
  229. //
  230. // we want to prevent the player and the AI from selecting or targeting the hole
  231. // cause the focus in this area (while reconstruction is happening) is the
  232. // actual reconstructing building
  233. //
  234. hole->maskObject( TRUE );
  235. transferBombs( reconstructing );
  236. } // end if
  237. } // end if, worker
  238. } // end if, time to spawn a worker
  239. } // end if, check for working respawn
  240. // holes get auto-healed when they're sittin around
  241. BodyModuleInterface *body = hole->getBodyModule();
  242. if( body->getHealth() < body->getMaxHealth() )
  243. {
  244. DamageInfo healingInfo;
  245. // do some healing
  246. healingInfo.in.m_amount = (modData->m_holeHealthRegenPercentPerSecond / LOGICFRAMES_PER_SECOND) *
  247. body->getMaxHealth();
  248. healingInfo.in.m_sourceID = hole->getID();
  249. healingInfo.in.m_damageType = DAMAGE_HEALING;
  250. healingInfo.in.m_deathType = DEATH_NONE;
  251. body->attemptHealing( &healingInfo );
  252. } // end if
  253. // when re-construction is complete, we remove this hole and worker
  254. if( reconstructing &&
  255. BitTest( reconstructing->getStatusBits(), OBJECT_STATUS_UNDER_CONSTRUCTION ) == FALSE )
  256. {
  257. // Transfer hole name to new building
  258. TheScriptEngine->transferObjectName( hole->getName(), reconstructing );
  259. // make the worker go away
  260. if( worker )
  261. TheGameLogic->destroyObject( worker );
  262. // make the hole go away
  263. TheGameLogic->destroyObject( hole );
  264. } // end if
  265. return UPDATE_SLEEP_NONE;
  266. } // end update
  267. // ------------------------------------------------------------------------------------------------
  268. //-------------------------------------------------------------------------------------------------
  269. void RebuildHoleBehavior::onDie( const DamageInfo *damageInfo )
  270. {
  271. if( m_workerID != INVALID_ID )
  272. {
  273. // Our rebuilding building and us the hole can be killed in the same frame, which means we may not have
  274. // deleted our generated worker since we do that in our update.
  275. Object *worker = TheGameLogic->findObjectByID(m_workerID);
  276. if( worker )
  277. {
  278. TheGameLogic->destroyObject(worker);
  279. m_workerID = INVALID_ID;
  280. }
  281. }
  282. Object *obj = getObject();
  283. // destroy us
  284. TheGameLogic->destroyObject( obj );
  285. } // end onDie
  286. // ------------------------------------------------------------------------------------------------
  287. /** Helper method to get interface given an object */
  288. // ------------------------------------------------------------------------------------------------
  289. /*static*/ RebuildHoleBehaviorInterface* RebuildHoleBehavior::getRebuildHoleBehaviorInterfaceFromObject( Object *obj )
  290. {
  291. RebuildHoleBehaviorInterface *rhbi = NULL;
  292. if( obj )
  293. {
  294. for( BehaviorModule **i = obj->getBehaviorModules(); *i; ++i )
  295. {
  296. rhbi = (*i)->getRebuildHoleBehaviorInterface();
  297. if( rhbi )
  298. break; // exit for
  299. } // end for i
  300. } // end if, obj
  301. return rhbi;
  302. } // end getRebuildHoleBehaviorInterfaceFromObject
  303. // ------------------------------------------------------------------------------------------------
  304. /** CRC */
  305. // ------------------------------------------------------------------------------------------------
  306. void RebuildHoleBehavior::crc( Xfer *xfer )
  307. {
  308. // extend base class
  309. UpdateModule::crc( xfer );
  310. } // end crc
  311. // ------------------------------------------------------------------------------------------------
  312. /** Xfer method
  313. * Version Info:
  314. * 1: Initial version,
  315. * 2: Added spawner id */
  316. // ------------------------------------------------------------------------------------------------
  317. void RebuildHoleBehavior::xfer( Xfer *xfer )
  318. {
  319. // version
  320. XferVersion currentVersion = 2;
  321. XferVersion version = currentVersion;
  322. xfer->xferVersion( &version, currentVersion );
  323. // extend base class
  324. UpdateModule::xfer( xfer );
  325. // worker ID
  326. xfer->xferObjectID( &m_workerID );
  327. // reconstructing id
  328. xfer->xferObjectID( &m_reconstructingID );
  329. // spawner ID
  330. if( version >= 2 )
  331. xfer->xferObjectID( &m_spawnerObjectID );
  332. // worker wait counter
  333. xfer->xferUnsignedInt( &m_workerWaitCounter );
  334. // worker template
  335. AsciiString workerName = m_workerTemplate ? m_workerTemplate->getName() : AsciiString::TheEmptyString;
  336. xfer->xferAsciiString( &workerName );
  337. if( xfer->getXferMode() == XFER_LOAD )
  338. {
  339. if( workerName != AsciiString::TheEmptyString )
  340. {
  341. m_workerTemplate = TheThingFactory->findTemplate( workerName );
  342. if( m_workerTemplate == NULL )
  343. {
  344. DEBUG_CRASH(( "RebuildHoleBehavior::xfer - Unable to find template '%s'\n",
  345. workerName.str() ));
  346. throw SC_INVALID_DATA;
  347. } // end if
  348. } // end if
  349. else
  350. m_workerTemplate = NULL;
  351. } // end if
  352. // rebuild template
  353. AsciiString rebuildName = m_rebuildTemplate ? m_rebuildTemplate->getName() : AsciiString::TheEmptyString;
  354. xfer->xferAsciiString( &rebuildName );
  355. if( xfer->getXferMode() == XFER_LOAD )
  356. {
  357. if( rebuildName != AsciiString::TheEmptyString )
  358. {
  359. m_rebuildTemplate = TheThingFactory->findTemplate( rebuildName );
  360. if( m_rebuildTemplate == NULL )
  361. {
  362. DEBUG_CRASH(( "RebuildHoleBehavior::xfer - Unable to find template '%s'\n",
  363. rebuildName.str() ));
  364. throw SC_INVALID_DATA;
  365. } // end if
  366. } // end if
  367. else
  368. m_rebuildTemplate = NULL;
  369. } // end if
  370. } // end xfer
  371. // ------------------------------------------------------------------------------------------------
  372. /** Load post process */
  373. // ------------------------------------------------------------------------------------------------
  374. void RebuildHoleBehavior::loadPostProcess( void )
  375. {
  376. // extend base class
  377. UpdateModule::loadPostProcess();
  378. } // end loadPostProcess