RebuildHoleBehavior.cpp 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  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: 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. Object *worker = TheGameLogic->findObjectByID(m_workerID);
  132. newWorkerRespawnProcess( worker ); //Kill the worker if we have one.
  133. } /// end startRebuildProcess
  134. //----------------------------------------------------------------------------------------------
  135. void RebuildHoleBehavior::transferBombs( Object *reconstruction )
  136. {
  137. Object *self = getObject();
  138. Object *obj = TheGameLogic->getFirstObject();
  139. while( obj )
  140. {
  141. if( obj->isKindOf( KINDOF_MINE ) )
  142. {
  143. static NameKeyType key_StickyBombUpdate = NAMEKEY( "StickyBombUpdate" );
  144. StickyBombUpdate *update = (StickyBombUpdate*)obj->findUpdateModule( key_StickyBombUpdate );
  145. if( update && update->getTargetObject() == self )
  146. {
  147. update->setTargetObject( reconstruction );
  148. }
  149. }
  150. obj = obj->getNextObject();
  151. }
  152. }
  153. //-------------------------------------------------------------------------------------------------
  154. //-------------------------------------------------------------------------------------------------
  155. UpdateSleepTime RebuildHoleBehavior::update( void )
  156. {
  157. const RebuildHoleBehaviorModuleData *modData = getRebuildHoleBehaviorModuleData();
  158. Object *hole = getObject();
  159. Object *reconstructing = NULL;
  160. Object *worker = NULL;
  161. // get the worker object if we have one
  162. if( m_workerID != 0 )
  163. {
  164. // get the worker
  165. worker = TheGameLogic->findObjectByID( m_workerID );
  166. // if the worker is no longer there, start the respawning process for a worker again
  167. if( worker == NULL )
  168. newWorkerRespawnProcess( NULL );
  169. } // end if
  170. // if we have a reconstructing object built, get the actual object pointer
  171. if( m_reconstructingID != 0 )
  172. {
  173. // get object pointer
  174. reconstructing = TheGameLogic->findObjectByID( m_reconstructingID );
  175. //
  176. // if that object does not exist anymore, we need to kill a worker if we have one
  177. // and start the spawning process over again
  178. //
  179. if( reconstructing == NULL )
  180. {
  181. newWorkerRespawnProcess( worker );
  182. m_reconstructingID = INVALID_ID;
  183. } // end if
  184. } // end if
  185. // see if it's time for us to spawn a worker
  186. if( worker == NULL && m_workerWaitCounter > 0 )
  187. {
  188. // decrement counter and respawn if it's time
  189. if( --m_workerWaitCounter == 0 )
  190. {
  191. // resolve the worker template pointer if necessary
  192. if( m_workerTemplate == NULL )
  193. m_workerTemplate = TheThingFactory->findTemplate( modData->m_workerTemplateName );
  194. // create a worker
  195. worker = TheThingFactory->newObject( m_workerTemplate, hole->getTeam() );
  196. if( worker )
  197. {
  198. // set the position of the worker to that of the hole
  199. worker->setPosition( hole->getPosition() );
  200. // save the ID of the worker spawned
  201. m_workerID = worker->getID();
  202. //
  203. // tell the worker to begin construction of a building if one does not
  204. // exist yet. If one does, have construction resume
  205. //
  206. AIUpdateInterface *ai = worker->getAIUpdateInterface();
  207. if( ai )
  208. {
  209. if( reconstructing == NULL )
  210. reconstructing = ai->construct( m_rebuildTemplate,
  211. hole->getPosition(),
  212. hole->getOrientation(),
  213. hole->getControllingPlayer(),
  214. TRUE );
  215. else
  216. ai->aiResumeConstruction( reconstructing, CMD_FROM_AI );
  217. for ( Object *obj = TheGameLogic->getFirstObject(); obj; obj = obj->getNextObject() )
  218. {
  219. // Just like the building transfers attackers to the hole when it creates us, we need to transfer
  220. // attackers to our replacement building before we mask ourselves.
  221. AIUpdateInterface* ai = obj->getAI();
  222. if (!ai)
  223. continue;
  224. ai->transferAttack(hole->getID(), reconstructing->getID());
  225. }
  226. // save the id of what we are reconstructing
  227. m_reconstructingID = reconstructing->getID();
  228. //Kris: Hacking the building to set the hole as the producer... so if the site dies, we
  229. //can transfer the attack back to the hole. The object has OBJECT_STATUS_RECONSTRUCTING
  230. //which we check when the object dies.
  231. reconstructing->setProducer( hole );
  232. // we want to prevent the player from selecting and doing things with this worker
  233. worker->setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_UNSELECTABLE ) );
  234. //
  235. // we want to prevent the player and the AI from selecting or targeting the hole
  236. // cause the focus in this area (while reconstruction is happening) is the
  237. // actual reconstructing building
  238. //
  239. hole->maskObject( TRUE );
  240. transferBombs( reconstructing );
  241. } // end if
  242. } // end if, worker
  243. } // end if, time to spawn a worker
  244. } // end if, check for working respawn
  245. // holes get auto-healed when they're sittin around
  246. BodyModuleInterface *body = hole->getBodyModule();
  247. if( body->getHealth() < body->getMaxHealth() )
  248. {
  249. DamageInfo healingInfo;
  250. // do some healing
  251. healingInfo.in.m_amount = (modData->m_holeHealthRegenPercentPerSecond / LOGICFRAMES_PER_SECOND) *
  252. body->getMaxHealth();
  253. healingInfo.in.m_sourceID = hole->getID();
  254. healingInfo.in.m_damageType = DAMAGE_HEALING;
  255. healingInfo.in.m_deathType = DEATH_NONE;
  256. body->attemptHealing( &healingInfo );
  257. } // end if
  258. // when re-construction is complete, we remove this hole and worker
  259. if( reconstructing && !reconstructing->getStatusBits().test( OBJECT_STATUS_UNDER_CONSTRUCTION ) )
  260. {
  261. // Transfer hole name to new building
  262. TheScriptEngine->transferObjectName( hole->getName(), reconstructing );
  263. // make the worker go away
  264. if( worker )
  265. TheGameLogic->destroyObject( worker );
  266. // make the hole go away
  267. TheGameLogic->destroyObject( hole );
  268. } // end if
  269. return UPDATE_SLEEP_NONE;
  270. } // end update
  271. // ------------------------------------------------------------------------------------------------
  272. //-------------------------------------------------------------------------------------------------
  273. void RebuildHoleBehavior::onDie( const DamageInfo *damageInfo )
  274. {
  275. if( m_workerID != INVALID_ID )
  276. {
  277. // Our rebuilding building and us the hole can be killed in the same frame, which means we may not have
  278. // deleted our generated worker since we do that in our update.
  279. Object *worker = TheGameLogic->findObjectByID(m_workerID);
  280. if( worker )
  281. {
  282. TheGameLogic->destroyObject(worker);
  283. m_workerID = INVALID_ID;
  284. }
  285. }
  286. Object *obj = getObject();
  287. // destroy us
  288. TheGameLogic->destroyObject( obj );
  289. } // end onDie
  290. // ------------------------------------------------------------------------------------------------
  291. /** Helper method to get interface given an object */
  292. // ------------------------------------------------------------------------------------------------
  293. /*static*/ RebuildHoleBehaviorInterface* RebuildHoleBehavior::getRebuildHoleBehaviorInterfaceFromObject( Object *obj )
  294. {
  295. RebuildHoleBehaviorInterface *rhbi = NULL;
  296. if( obj )
  297. {
  298. for( BehaviorModule **i = obj->getBehaviorModules(); *i; ++i )
  299. {
  300. rhbi = (*i)->getRebuildHoleBehaviorInterface();
  301. if( rhbi )
  302. break; // exit for
  303. } // end for i
  304. } // end if, obj
  305. return rhbi;
  306. } // end getRebuildHoleBehaviorInterfaceFromObject
  307. // ------------------------------------------------------------------------------------------------
  308. /** CRC */
  309. // ------------------------------------------------------------------------------------------------
  310. void RebuildHoleBehavior::crc( Xfer *xfer )
  311. {
  312. // extend base class
  313. UpdateModule::crc( xfer );
  314. } // end crc
  315. // ------------------------------------------------------------------------------------------------
  316. /** Xfer method
  317. * Version Info:
  318. * 1: Initial version,
  319. * 2: Added spawner id */
  320. // ------------------------------------------------------------------------------------------------
  321. void RebuildHoleBehavior::xfer( Xfer *xfer )
  322. {
  323. // version
  324. XferVersion currentVersion = 2;
  325. XferVersion version = currentVersion;
  326. xfer->xferVersion( &version, currentVersion );
  327. // extend base class
  328. UpdateModule::xfer( xfer );
  329. // worker ID
  330. xfer->xferObjectID( &m_workerID );
  331. // reconstructing id
  332. xfer->xferObjectID( &m_reconstructingID );
  333. // spawner ID
  334. if( version >= 2 )
  335. xfer->xferObjectID( &m_spawnerObjectID );
  336. // worker wait counter
  337. xfer->xferUnsignedInt( &m_workerWaitCounter );
  338. // worker template
  339. AsciiString workerName = m_workerTemplate ? m_workerTemplate->getName() : AsciiString::TheEmptyString;
  340. xfer->xferAsciiString( &workerName );
  341. if( xfer->getXferMode() == XFER_LOAD )
  342. {
  343. if( workerName != AsciiString::TheEmptyString )
  344. {
  345. m_workerTemplate = TheThingFactory->findTemplate( workerName );
  346. if( m_workerTemplate == NULL )
  347. {
  348. DEBUG_CRASH(( "RebuildHoleBehavior::xfer - Unable to find template '%s'\n",
  349. workerName.str() ));
  350. throw SC_INVALID_DATA;
  351. } // end if
  352. } // end if
  353. else
  354. m_workerTemplate = NULL;
  355. } // end if
  356. // rebuild template
  357. AsciiString rebuildName = m_rebuildTemplate ? m_rebuildTemplate->getName() : AsciiString::TheEmptyString;
  358. xfer->xferAsciiString( &rebuildName );
  359. if( xfer->getXferMode() == XFER_LOAD )
  360. {
  361. if( rebuildName != AsciiString::TheEmptyString )
  362. {
  363. m_rebuildTemplate = TheThingFactory->findTemplate( rebuildName );
  364. if( m_rebuildTemplate == NULL )
  365. {
  366. DEBUG_CRASH(( "RebuildHoleBehavior::xfer - Unable to find template '%s'\n",
  367. rebuildName.str() ));
  368. throw SC_INVALID_DATA;
  369. } // end if
  370. } // end if
  371. else
  372. m_rebuildTemplate = NULL;
  373. } // end if
  374. } // end xfer
  375. // ------------------------------------------------------------------------------------------------
  376. /** Load post process */
  377. // ------------------------------------------------------------------------------------------------
  378. void RebuildHoleBehavior::loadPostProcess( void )
  379. {
  380. // extend base class
  381. UpdateModule::loadPostProcess();
  382. } // end loadPostProcess