SpawnBehavior.cpp 31 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078
  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: SpawnBehavior.cpp ////////////////////////////////////////////////////////////////////////
  24. // Author: Graham Smallwood, January 2002
  25. // Desc: Update will create and monitor a group of spawned units and replace as needed
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  28. #include "Common/GameState.h"
  29. #include "Common/ThingFactory.h"
  30. #include "Common/ThingTemplate.h"
  31. #include "Common/Player.h"
  32. #include "Common/Xfer.h"
  33. #include "GameLogic/GameLogic.h"
  34. #include "GameLogic/Object.h"
  35. #include "GameLogic/PartitionManager.h"
  36. #include "GameLogic/Module/AIUpdate.h"
  37. #include "GameLogic/Module/SpawnBehavior.h"
  38. #include "GameLogic/Module/BodyModule.h"
  39. #include "GameClient/Drawable.h" //selection logic
  40. #include "GameClient/InGameUI.h" // selection logic
  41. #include "GameLogic/ExperienceTracker.h" //veterancy logic
  42. #define NONE_SPAWNED_YET (0xffffffff)
  43. #ifdef _INTERNAL
  44. // for occasional debugging...
  45. //#pragma optimize("", off)
  46. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  47. #endif
  48. #define SPAWN_DELAY_MIN_FRAMES (16) // about as rapidly as you'd expect people to successively exit through the same door
  49. //-------------------------------------------------------------------------------------------------
  50. SpawnBehavior::SpawnBehavior( Thing *thing, const ModuleData* moduleData )
  51. : UpdateModule( thing, moduleData )
  52. {
  53. const SpawnBehaviorModuleData* md = getSpawnBehaviorModuleData();
  54. // GEE, THIS IS NEW...
  55. // NOW, WE CAN HAVE A LIST OF TEMPLATE NAMES
  56. m_templateNameIterator = md->m_spawnTemplateNameData.begin();
  57. m_spawnTemplate = TheThingFactory->findTemplate( *m_templateNameIterator );
  58. //each time m_spawn template is used, it will increment m_templateNameIterator,
  59. //thus scanning through the ordered list of template names
  60. //looping back to the beginning
  61. m_framesToWait = 0;
  62. //Added By Sadullah Nader
  63. //Initialization(s) inserted
  64. m_firstBatchCount = 0;
  65. //
  66. if( md->m_isOneShotData )
  67. m_oneShotCountdown = md->m_spawnNumberData;
  68. else
  69. m_oneShotCountdown = -1;
  70. m_active = TRUE;
  71. m_replacementTimes.clear();
  72. // The initializing of the initial bursters is handled in the first update @todo invent an object::postConstructionProcess() some day
  73. m_initialBurstCountdown = md->m_initialBurst;
  74. m_initialBurstTimesInited = FALSE;
  75. m_aggregateHealth = md->m_aggregateHealth;
  76. m_spawnCount = NONE_SPAWNED_YET;
  77. m_active = TRUE;
  78. m_selfTaskingSpawnCount = 0;
  79. }
  80. //-------------------------------------------------------------------------------------------------
  81. SpawnBehavior::~SpawnBehavior( void )
  82. {
  83. m_replacementTimes.clear();
  84. }
  85. // ------------------------------------------------------------------------------------------------
  86. void SpawnBehavior::onDelete()
  87. {
  88. const SpawnBehaviorModuleData *modData = getSpawnBehaviorModuleData();
  89. // destroy anything that we have spawned that is not already dead
  90. if( modData->m_spawnedRequireSpawner )
  91. {
  92. Object *obj;
  93. for( objectIDListIterator it = m_spawnIDs.begin(); it != m_spawnIDs.end(); /*empty*/ )
  94. {
  95. // get object
  96. obj = TheGameLogic->findObjectByID( *it );
  97. // increment iterator incase the id list is alterd because what we're about to do
  98. ++it;
  99. //
  100. // destroy this if it's alive, in the usual case this object "dies" and is
  101. // not "destroyed". on *death* we *KILL* our spawned things, but this is here
  102. // and will *DESTROY* our spawned things if we ourselves are destroyed and they
  103. // are still alive (such a case would be calling destroy object on us directly)
  104. //
  105. if( obj && obj->isEffectivelyDead() == FALSE )
  106. TheGameLogic->destroyObject( obj );
  107. } // end for, it
  108. } // end if
  109. }
  110. // ------------------------------------------------------------------------------------------------
  111. void SpawnBehavior::onDie( const DamageInfo *damageInfo )
  112. {
  113. const SpawnBehaviorModuleData *modData = getSpawnBehaviorModuleData();
  114. ///@todo isDieApplicable should be called outside of the onDie call
  115. if( modData->m_dieMuxData.isDieApplicable( getObject(), damageInfo ) == FALSE )
  116. return;
  117. for( objectIDListIterator iter = m_spawnIDs.begin();
  118. iter != m_spawnIDs.end();
  119. iter++
  120. )
  121. {
  122. Object *currentSpawn = TheGameLogic->findObjectByID( (*iter) );
  123. if( currentSpawn )
  124. {
  125. // Go through all my spawns and see if they have a SlavedUpdate I can tell I was killed to
  126. for (BehaviorModule** update = currentSpawn->getBehaviorModules(); *update; ++update)
  127. {
  128. SlavedUpdateInterface* sdu = (*update)->getSlavedUpdateInterface();
  129. if (sdu != NULL)
  130. {
  131. sdu->onSlaverDie( damageInfo );
  132. break;
  133. }
  134. }
  135. // our spawner has died, we must invalidate the ID now in the spawned object
  136. currentSpawn->setProducer( NULL );
  137. }
  138. }
  139. // kill anything that we have spawned if our module data directs us to do so
  140. if( modData->m_spawnedRequireSpawner )
  141. {
  142. Object *obj;
  143. for( objectIDListIterator it = m_spawnIDs.begin(); it != m_spawnIDs.end(); /*emtpy*/ )
  144. {
  145. // get object
  146. obj = TheGameLogic->findObjectByID( *it );
  147. // increment iterator incase the id list is alterd because what we're about to do
  148. ++it;
  149. // kill it if not already dead
  150. if( obj && obj->isEffectivelyDead() == FALSE )
  151. obj->kill();
  152. } // end for, it
  153. } // end if
  154. }
  155. //-------------------------------------------------------------------------------------------------
  156. UpdateSleepTime SpawnBehavior::update( void )
  157. {
  158. /// @todo srj use SLEEPY_UPDATE here
  159. //EVERY FRAME
  160. if ( m_aggregateHealth )
  161. {
  162. computeAggregateStates();
  163. }
  164. const SpawnBehaviorModuleData* md = getSpawnBehaviorModuleData();
  165. if ( ! m_initialBurstTimesInited )
  166. {
  167. m_initialBurstTimesInited = TRUE;
  168. Bool runtimeProduced = getObject()->getProducerID()!=INVALID_ID; //this was produced by a production module, rather than a script or worldbuilder
  169. Int now = TheGameLogic->getFrame();
  170. Int burstInitCount = m_initialBurstCountdown;
  171. for( int listIndex = 0; listIndex < md->m_spawnNumberData; listIndex++ )
  172. {
  173. if ( md->m_initialBurst > 0 )
  174. {
  175. UnsignedInt birthFrame = now;
  176. if ( runtimeProduced && burstInitCount > 0)
  177. {
  178. --burstInitCount;
  179. birthFrame += (listIndex*SPAWN_DELAY_MIN_FRAMES);
  180. }
  181. m_replacementTimes.push_back( runtimeProduced );// Set all spawns to be created in rapid succession
  182. }
  183. else
  184. m_replacementTimes.push_back( listIndex );// Set all spawns to be created as soon as possible
  185. }
  186. }
  187. //SPARSELY SOLVED
  188. if (--m_framesToWait > 0)
  189. {
  190. return UPDATE_SLEEP_NONE;
  191. }
  192. m_framesToWait = SPAWN_UPDATE_RATE;
  193. //Go through the list and make a spawn for each number that is less than now's frame
  194. if( shouldTryToSpawn() )
  195. {
  196. // Due to STL's list vomiting when you erase in a for loop,
  197. // Start at the beginning, and if you erase one, use the return
  198. // value to manually step to the next. Otherwise, step by yourself.
  199. // begin == end means empty list, so no special check is needed.
  200. intListIterator iterator = m_replacementTimes.begin();
  201. while( iterator != m_replacementTimes.end() )
  202. {
  203. Int replacementTime = *iterator;
  204. UnsignedInt currentTime = TheGameLogic->getFrame();
  205. if( currentTime > replacementTime )
  206. {
  207. //If you create one, you pop the number off the list
  208. if( createSpawn() )
  209. iterator = m_replacementTimes.erase( iterator );
  210. else
  211. iterator++;
  212. }
  213. else
  214. iterator++;
  215. }
  216. if( md->m_isOneShotData && (m_oneShotCountdown <= 0) )
  217. stopSpawning(); //I only trigger one batch. ie on Creation.
  218. }
  219. return UPDATE_SLEEP_NONE;
  220. }
  221. // ------------------------------------------------------------------------------------------------
  222. Bool SpawnBehavior::maySpawnSelfTaskAI( Real maxSelfTaskersRatio )
  223. {
  224. if ( m_spawnCount == 0)
  225. return FALSE;
  226. if ( maxSelfTaskersRatio == 0)
  227. return FALSE;
  228. //if my last attack command was from player or script, I need to forbid my spawn from disobeying that command
  229. //otherwise (since my attack state was autoacquired my ny own ai), let them deviate by the ratio specified.
  230. Object* obj = getObject();
  231. if ( ! obj )
  232. return FALSE;
  233. AIUpdateInterface *ai = obj->getAI();
  234. if ( ! ai )
  235. return FALSE;
  236. CommandSourceType lastAttackCommandSource = ai->getLastCommandSource();
  237. if ( lastAttackCommandSource != CMD_FROM_AI )
  238. return FALSE;
  239. Real curSelfTaskersRatio = (Real)m_selfTaskingSpawnCount / (Real)m_spawnCount;
  240. return ( curSelfTaskersRatio < maxSelfTaskersRatio );
  241. }
  242. // ------------------------------------------------------------------------------------------------
  243. Object* SpawnBehavior::getClosestSlave( const Coord3D *pos )
  244. {
  245. Object *closest = NULL;
  246. Real closestDistance;
  247. for( objectIDListIterator it = m_spawnIDs.begin(); it != m_spawnIDs.end(); ++it )
  248. {
  249. Object *obj = TheGameLogic->findObjectByID( *it );
  250. if( obj )
  251. {
  252. Real distance = ThePartitionManager->getDistanceSquared( obj, pos, FROM_CENTER_2D );
  253. if( !closest || closestDistance > distance )
  254. {
  255. closest = obj;
  256. closestDistance = distance;
  257. }
  258. }
  259. }
  260. return closest; //Could be null!
  261. }
  262. // ------------------------------------------------------------------------------------------------
  263. void SpawnBehavior::orderSlavesToAttackTarget( Object *target, Int maxShotsToFire, CommandSourceType cmdSource )
  264. {
  265. for( objectIDListIterator it = m_spawnIDs.begin(); it != m_spawnIDs.end(); ++it )
  266. {
  267. Object *obj = TheGameLogic->findObjectByID( *it );
  268. if( obj )
  269. {
  270. AIUpdateInterface *ai = obj->getAI();
  271. if( ai )
  272. {
  273. ai->aiForceAttackObject( target, maxShotsToFire, cmdSource );
  274. }
  275. }
  276. }
  277. }
  278. // ------------------------------------------------------------------------------------------------
  279. void SpawnBehavior::orderSlavesToAttackPosition( const Coord3D *pos, Int maxShotsToFire, CommandSourceType cmdSource )
  280. {
  281. for( objectIDListIterator it = m_spawnIDs.begin(); it != m_spawnIDs.end(); ++it )
  282. {
  283. Object *obj = TheGameLogic->findObjectByID( *it );
  284. if( obj )
  285. {
  286. AIUpdateInterface *ai = obj->getAI();
  287. if( ai )
  288. {
  289. ai->aiAttackPosition( pos, maxShotsToFire, cmdSource );
  290. }
  291. }
  292. }
  293. }
  294. // ------------------------------------------------------------------------------------------------
  295. void SpawnBehavior::orderSlavesToGoIdle( CommandSourceType cmdSource )
  296. {
  297. for( objectIDListIterator it = m_spawnIDs.begin(); it != m_spawnIDs.end(); ++it )
  298. {
  299. Object *obj = TheGameLogic->findObjectByID( *it );
  300. if( obj )
  301. {
  302. AIUpdateInterface *ai = obj->getAI();
  303. if( ai )
  304. {
  305. ai->aiIdle( cmdSource );
  306. }
  307. }
  308. }
  309. }
  310. // ------------------------------------------------------------------------------------------------
  311. CanAttackResult SpawnBehavior::getCanAnySlavesAttackSpecificTarget( AbleToAttackType attackType, const Object *target, CommandSourceType cmdSource )
  312. {
  313. Bool invalidShot = FALSE;
  314. for( objectIDListIterator it = m_spawnIDs.begin(); it != m_spawnIDs.end(); ++it )
  315. {
  316. Object *obj = TheGameLogic->findObjectByID( *it );
  317. if( obj )
  318. {
  319. CanAttackResult result = obj->getAbleToAttackSpecificObject( attackType, target, cmdSource );
  320. switch( result )
  321. {
  322. case ATTACKRESULT_POSSIBLE:
  323. case ATTACKRESULT_POSSIBLE_AFTER_MOVING:
  324. return result;
  325. case ATTACKRESULT_NOT_POSSIBLE:
  326. break;
  327. case ATTACKRESULT_INVALID_SHOT:
  328. invalidShot = TRUE;
  329. break;
  330. default:
  331. DEBUG_CRASH( ("SpawnBehavior::getCanAnySlavesAttackSpecificTarget encountered unhandled CanAttackResult of %d. Treating as not possible...", result) );
  332. break;
  333. }
  334. }
  335. }
  336. //Prioritize the reasonings!
  337. if( invalidShot )
  338. {
  339. return ATTACKRESULT_INVALID_SHOT;
  340. }
  341. return ATTACKRESULT_NOT_POSSIBLE;
  342. }
  343. // ------------------------------------------------------------------------------------------------
  344. CanAttackResult SpawnBehavior::getCanAnySlavesUseWeaponAgainstTarget( AbleToAttackType attackType, const Object *victim, const Coord3D *pos, CommandSourceType cmdSource )
  345. {
  346. Bool invalidShot = FALSE;
  347. for( objectIDListIterator it = m_spawnIDs.begin(); it != m_spawnIDs.end(); ++it )
  348. {
  349. Object *obj = TheGameLogic->findObjectByID( *it );
  350. if( obj )
  351. {
  352. CanAttackResult result = obj->getAbleToUseWeaponAgainstTarget( attackType, victim, pos, cmdSource );
  353. switch( result )
  354. {
  355. case ATTACKRESULT_POSSIBLE:
  356. case ATTACKRESULT_POSSIBLE_AFTER_MOVING:
  357. return result;
  358. case ATTACKRESULT_NOT_POSSIBLE:
  359. break;
  360. case ATTACKRESULT_INVALID_SHOT:
  361. invalidShot = TRUE;
  362. break;
  363. default:
  364. DEBUG_CRASH( ("SpawnBehavior::getCanAnySlavesUseWeaponAgainstTarget encountered unhandled CanAttackResult of %d. Treating as not possible...", result) );
  365. break;
  366. }
  367. }
  368. }
  369. //Prioritize the reasonings!
  370. if( invalidShot )
  371. {
  372. return ATTACKRESULT_INVALID_SHOT;
  373. }
  374. return ATTACKRESULT_NOT_POSSIBLE;
  375. }
  376. // ------------------------------------------------------------------------------------------------
  377. Bool SpawnBehavior::canAnySlavesAttack()
  378. {
  379. for( objectIDListIterator it = m_spawnIDs.begin(); it != m_spawnIDs.end(); ++it )
  380. {
  381. Object *obj = TheGameLogic->findObjectByID( *it );
  382. if( obj )
  383. {
  384. if( obj->isAbleToAttack() )
  385. {
  386. return true;
  387. }
  388. }
  389. }
  390. return false;
  391. }
  392. // ------------------------------------------------------------------------------------------------
  393. // ------------------------------------------------------------------------------------------------
  394. class OrphanData
  395. {
  396. public:
  397. OrphanData( void );
  398. const ThingTemplate *m_matchTemplate;
  399. Object *m_source;
  400. Object *m_closest;
  401. Real m_closestDistSq;
  402. };
  403. #define BIG_DISTANCE 99999999.9f
  404. // ------------------------------------------------------------------------------------------------
  405. // ------------------------------------------------------------------------------------------------
  406. OrphanData::OrphanData( void )
  407. {
  408. m_matchTemplate = NULL;
  409. m_source = NULL;
  410. m_closest = NULL;
  411. m_closestDistSq = BIG_DISTANCE;
  412. }
  413. // ------------------------------------------------------------------------------------------------
  414. static void findClosestOrphan( Object *obj, void *userData )
  415. {
  416. OrphanData *orphanData = (OrphanData *)userData;
  417. // if template doesn't match do nothing
  418. if( obj->getTemplate()->isEquivalentTo( orphanData->m_matchTemplate ) == FALSE )
  419. return;
  420. // this object must be orphaned
  421. if( obj->getProducerID() != INVALID_ID )
  422. return;
  423. // is this the closest one so far
  424. Real distSq = ThePartitionManager->getDistanceSquared( orphanData->m_source, obj, FROM_CENTER_2D );
  425. if( distSq < orphanData->m_closestDistSq )
  426. {
  427. orphanData->m_closest = obj;
  428. orphanData->m_closestDistSq = distSq;
  429. } // end if
  430. } // findClosestOrphan
  431. // ------------------------------------------------------------------------------------------------
  432. Object *SpawnBehavior::reclaimOrphanSpawn( void )
  433. {
  434. Player *player = getObject()->getControllingPlayer();
  435. const SpawnBehaviorModuleData *md = getSpawnBehaviorModuleData();
  436. //
  437. // iterate all the objects my controlling player has and look for any orphaned things
  438. // that we would normally spawn, if found we'll just make it our own
  439. //
  440. // EVEN MORE NEW AND DIFFERENT
  441. // This block scans the list for matchTemplates
  442. //
  443. OrphanData orphanData;
  444. AsciiString prevName = "";
  445. for (std::vector<AsciiString>::const_iterator tempName = md->m_spawnTemplateNameData.begin();
  446. tempName != md->m_spawnTemplateNameData.end();
  447. ++tempName)
  448. {
  449. if (prevName.compare(*tempName)) // the list may have redundancy, this will skip some of it
  450. continue;
  451. orphanData.m_matchTemplate = TheThingFactory->findTemplate( *tempName );;
  452. orphanData.m_source = getObject();
  453. orphanData.m_closest = NULL;
  454. orphanData.m_closestDistSq = BIG_DISTANCE;
  455. player->iterateObjects( findClosestOrphan, &orphanData );
  456. prevName = *tempName;
  457. }
  458. return orphanData.m_closest;
  459. }
  460. //-------------------------------------------------------------------------------------------------
  461. Bool SpawnBehavior::createSpawn()
  462. {
  463. Object *parent = getObject();
  464. const SpawnBehaviorModuleData *md = getSpawnBehaviorModuleData();
  465. ExitInterface* exitInterface = parent->getObjectExitInterface();
  466. if( exitInterface == NULL )
  467. {
  468. DEBUG_ASSERTCRASH( exitInterface != NULL, ("Something cannot have SpawnBehavior without an exit interface") );
  469. return FALSE;
  470. }
  471. ExitDoorType exitDoor = exitInterface->reserveDoorForExit(NULL, NULL);
  472. if (exitDoor == DOOR_NONE_AVAILABLE)
  473. return FALSE;
  474. Object *newSpawn = NULL;
  475. // try to reclaim orphaned objects if possible
  476. Bool reclaimedOrphan = FALSE;
  477. if( md->m_canReclaimOrphans && md->m_isOneShotData == FALSE )
  478. {
  479. newSpawn = reclaimOrphanSpawn();
  480. if( newSpawn )
  481. reclaimedOrphan = TRUE;
  482. }
  483. // This assures that an orphan has not just been reclaimed,
  484. if( newSpawn == NULL ) // and that we really want a new spawn, here.
  485. {
  486. m_spawnTemplate = TheThingFactory->findTemplate( *m_templateNameIterator );
  487. //newSpawn = TheThingFactory->newObject( m_spawnTemplate, parent->getControllingPlayer()->getDefaultTeam() );
  488. newSpawn = TheThingFactory->newObject( m_spawnTemplate, parent->getTeam() ); // just a little worried about this...
  489. // Count this unit towards our score.
  490. newSpawn->getControllingPlayer()->onUnitCreated(parent, newSpawn);
  491. // Gee, this is new...
  492. // Now, we can have a list of template names.
  493. // Each time m_spawn template is used, it will increment m_templateNameIterator,
  494. // thus scanning through the ordered list of template names, and
  495. // looping back to the beginning
  496. ++m_templateNameIterator;
  497. if ( m_templateNameIterator == md->m_spawnTemplateNameData.end())
  498. {
  499. m_templateNameIterator = md->m_spawnTemplateNameData.begin();
  500. }
  501. }
  502. newSpawn->setProducer(parent);
  503. // If they have a SlavedUpdate, then I have to tell them who their daddy is from now on.
  504. for (BehaviorModule** update = newSpawn->getBehaviorModules(); *update; ++update)
  505. {
  506. SlavedUpdateInterface* sdu = (*update)->getSlavedUpdateInterface();
  507. if (sdu != NULL)
  508. {
  509. sdu->onEnslave( parent );
  510. break;
  511. }
  512. }
  513. m_spawnIDs.push_back( newSpawn->getID() );
  514. if( reclaimedOrphan == FALSE )
  515. {
  516. if ( md->m_exitByBudding )
  517. {
  518. Bool barracksExitSuccess = FALSE;
  519. if ( m_initialBurstCountdown > 0 )
  520. {
  521. Object *barracks = TheGameLogic->findObjectByID( parent->getProducerID() );
  522. if ( barracks && barracks->isKindOf( KINDOF_STRUCTURE ) )
  523. {
  524. ExitInterface* barracksExitInterface = barracks->getObjectExitInterface();
  525. if ( barracksExitInterface )
  526. {
  527. ExitDoorType barracksDoor = barracksExitInterface->reserveDoorForExit(NULL, NULL);
  528. barracksExitInterface->exitObjectViaDoor( newSpawn, barracksDoor );
  529. newSpawn->setProducer(parent);//let parents producer exit him, but he thinks it was me
  530. --m_initialBurstCountdown;
  531. barracksExitSuccess = TRUE;
  532. }
  533. }
  534. }
  535. if ( ! barracksExitSuccess )
  536. {
  537. // find the closest spawn to the nexus...
  538. //there is probably a more elegant way to choose the budHost, but oh well
  539. Object *budHost = NULL;
  540. Object *curSpawn = NULL;
  541. Real tapeMeasure = 99999;
  542. Real closest = 999999.9f; // 1000 * 1000
  543. objectIDListIterator iter;
  544. for( iter = m_spawnIDs.begin(); iter != m_spawnIDs.end(); iter++)
  545. {
  546. curSpawn = TheGameLogic->findObjectByID( *iter );
  547. if ( curSpawn )
  548. {
  549. if (curSpawn == newSpawn )
  550. continue;
  551. tapeMeasure = ThePartitionManager->getDistanceSquared( curSpawn, parent, FROM_CENTER_2D );
  552. if ( tapeMeasure < closest )
  553. {
  554. closest = tapeMeasure;
  555. budHost = curSpawn;
  556. }
  557. }
  558. }
  559. exitInterface->exitObjectByBudding( newSpawn, budHost );// also handles the NULL pointer okay
  560. }
  561. }
  562. else
  563. {
  564. exitInterface->exitObjectViaDoor( newSpawn, exitDoor );
  565. }
  566. }
  567. else
  568. exitInterface->unreserveDoorForExit( exitDoor );
  569. if( md->m_isOneShotData )
  570. m_oneShotCountdown--;
  571. if ( m_spawnCount == NONE_SPAWNED_YET )
  572. {
  573. m_spawnCount = 1;
  574. }
  575. else
  576. {
  577. ++ m_spawnCount;
  578. }
  579. return TRUE;
  580. }
  581. //-------------------------------------------------------------------------------------------------
  582. void SpawnBehavior::onSpawnDeath( ObjectID deadSpawn, DamageInfo *damageInfo )
  583. {
  584. objectIDListIterator it = std::find(m_spawnIDs.begin(), m_spawnIDs.end(), deadSpawn);
  585. // If the iterator is at the end, we didn't find deadSpawn, so bail out.
  586. // Otherwise, bad crash stuff will happen.
  587. if (it == m_spawnIDs.end())
  588. return;
  589. //When one dies, you push (now + delay) as the time a new one should be made
  590. const SpawnBehaviorModuleData* md = getSpawnBehaviorModuleData();
  591. Int replacementTime = md->m_spawnReplaceDelayData + TheGameLogic->getFrame();
  592. m_replacementTimes.push_back( replacementTime );
  593. m_spawnIDs.erase( it );
  594. --m_spawnCount;
  595. if ( (m_spawnCount == 0) && m_aggregateHealth) // I'm dead without my spawn
  596. {
  597. Object *killer = TheGameLogic->findObjectByID(damageInfo->in.m_sourceID);
  598. if (killer != NULL) {
  599. killer->scoreTheKill(getObject());
  600. }
  601. TheGameLogic->destroyObject(getObject());
  602. //getObject()->kill();
  603. return;
  604. }
  605. }
  606. //-------------------------------------------------------------------------------------------------
  607. void SpawnBehavior::stopSpawning()
  608. {
  609. m_active = FALSE;
  610. }
  611. //-------------------------------------------------------------------------------------------------
  612. void SpawnBehavior::startSpawning()
  613. {
  614. m_active = TRUE;
  615. }
  616. //-------------------------------------------------------------------------------------------------
  617. /** When I become damaged */
  618. // ------------------------------------------------------------------------------------------------
  619. void SpawnBehavior::onDamage( DamageInfo *info )
  620. {
  621. for( objectIDListIterator iter = m_spawnIDs.begin();
  622. iter != m_spawnIDs.end();
  623. iter++
  624. )
  625. {
  626. Object *currentSpawn = TheGameLogic->findObjectByID( (*iter) );
  627. if( currentSpawn )
  628. {
  629. // Go through all my spawns and see if they have a SlavedUpdate I can tell I was hurt to
  630. for (BehaviorModule** update = currentSpawn->getBehaviorModules(); *update; ++update)
  631. {
  632. SlavedUpdateInterface* sdu = (*update)->getSlavedUpdateInterface();
  633. if (sdu != NULL)
  634. {
  635. sdu->onSlaverDamage( info );
  636. break;
  637. }
  638. }
  639. }
  640. }
  641. }
  642. // ------------------------------------------------------------------------------------------------
  643. // ------------------------------------------------------------------------------------------------
  644. Bool SpawnBehavior::shouldTryToSpawn()
  645. {
  646. const SpawnBehaviorModuleData *modData = getSpawnBehaviorModuleData();
  647. // Not if we are turned off
  648. if( !m_active )
  649. return FALSE;
  650. if( BitTest( getObject()-> getStatusBits(), OBJECT_STATUS_RECONSTRUCTING ) &&
  651. modData->m_isOneShotData == TRUE )
  652. {
  653. // If we are a Hole rebuild, not only should we not, but we should never ask again.
  654. stopSpawning();
  655. return FALSE;
  656. }
  657. // Not if we are under construction or being sold
  658. if( getObject()->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION) || getObject()->testStatus(OBJECT_STATUS_SOLD) )
  659. return FALSE;
  660. // Not if we are civilian controlled
  661. if( getObject()->isNeutralControlled() )
  662. return FALSE;
  663. return TRUE;
  664. }
  665. //********************************************************************
  666. //* This function allows various object states to be either inherited
  667. //* from the spawner to the spawn, or vice versa
  668. //* Selection is distributed across all spawn and spawner at once
  669. //* Health for the spawner is calc'd by aggregating the sum of all
  670. //* spawn health, and dividing by optimal health of full population
  671. //* (that is, the max spawn, SpawnBehaviorModuleData::m_spawnNumberData)
  672. //* at full health.
  673. //* Veterancy is sucked out of any unit that has any and put into the
  674. //* Spawner, scaled by 1/SpawnBehaviorModuleData::m_spawnNumberData;
  675. //* The HealthBoxPosition (maybe to include moodicon, vet icon) is calc'd
  676. //* as an average position of all the spawn.
  677. //********************************************************************
  678. void SpawnBehavior::computeAggregateStates(void)
  679. {
  680. if ( ! m_aggregateHealth ) // sanity
  681. return;
  682. Object* obj = getObject();
  683. const SpawnBehaviorModuleData* md = getSpawnBehaviorModuleData();
  684. Int spawnCount = 0;
  685. Int spawnCountMax = md->m_spawnNumberData;
  686. Coord3D avgSpawnPos;
  687. avgSpawnPos.set(0,0,0);
  688. Real acrHealth = 0.0f;
  689. Real avgHealthMax = 0.0f;
  690. ExperienceTracker *expTracker = obj->getExperienceTracker();
  691. VeterancyLevel vetLevel = expTracker->getVeterancyLevel();
  692. Bool SomebodyIsSelected = FALSE;
  693. Bool SomebodyIsNotSelected = FALSE;
  694. Drawable *spawnDraw = NULL;
  695. Object *currentSpawn = NULL;
  696. WeaponBonusConditionFlags spawnWeaponBonus;
  697. m_selfTaskingSpawnCount = 0;
  698. for( objectIDListIterator iter = m_spawnIDs.begin(); iter != m_spawnIDs.end(); iter++)
  699. {
  700. currentSpawn = TheGameLogic->findObjectByID( (*iter) );
  701. if( currentSpawn )
  702. {
  703. //m_selfTaskingSpawnCount += ( currentSpawn->isSelf);
  704. for (BehaviorModule** update = currentSpawn->getBehaviorModules(); *update; ++update)
  705. {
  706. SlavedUpdateInterface* sdu = (*update)->getSlavedUpdateInterface();
  707. if (sdu != NULL)
  708. {
  709. m_selfTaskingSpawnCount += ( sdu->isSelfTasking());;
  710. break;
  711. }
  712. }
  713. // VETERANCY LEVEL *************************************
  714. VeterancyLevel spawnVetLevel = currentSpawn->getVeterancyLevel();
  715. if ( spawnVetLevel > vetLevel) // whoever has more, set the other guy
  716. {
  717. expTracker->setVeterancyLevel(spawnVetLevel);
  718. }
  719. else if ( spawnVetLevel < vetLevel) // whoever has more, set the other guy
  720. {
  721. currentSpawn->getExperienceTracker()->setVeterancyLevel(vetLevel);
  722. }
  723. spawnWeaponBonus = currentSpawn->getWeaponBonusCondition();
  724. avgSpawnPos.add(currentSpawn->getPosition());
  725. BodyModuleInterface *body = currentSpawn->getBodyModule();
  726. acrHealth += body->getHealth();
  727. avgHealthMax += body->getMaxHealth();
  728. spawnDraw = currentSpawn->getDrawable();
  729. if ( spawnDraw->isSelected() )
  730. {
  731. SomebodyIsSelected = TRUE;
  732. }
  733. else
  734. {
  735. SomebodyIsNotSelected = TRUE;
  736. }
  737. spawnCount++;
  738. }
  739. } // next iter
  740. // SELECTION STATE *****************************************
  741. // THIS LOGIC IS SIMPLE
  742. // IF ANY ONE OF MY SPAWN ARE SELECTED RIGHT NOW,
  743. // I JUST SET THEM ALL SELECTED
  744. // THIS WAY WE ARE ALWAYS ONE BIG HAPPY MOB
  745. // BUT I ONLY PROCEED IF THERE IS ONE AMONG ALL OF US WHO IS NOT SELECTED
  746. if ( SomebodyIsSelected && ( ! obj->getDrawable()->isSelected() || SomebodyIsNotSelected ) )
  747. {
  748. GameMessage *teamMsg = TheMessageStream->appendMessage( GameMessage::MSG_CREATE_SELECTED_GROUP );
  749. teamMsg->appendBooleanArgument( FALSE );// not creating new team so pass false
  750. if ( SomebodyIsNotSelected ) // lets select everybody
  751. {
  752. for( objectIDListIterator iter = m_spawnIDs.begin(); iter != m_spawnIDs.end(); iter++)
  753. {
  754. currentSpawn = TheGameLogic->findObjectByID( (*iter) );
  755. if( currentSpawn )
  756. {
  757. spawnDraw = currentSpawn->getDrawable();
  758. if ( ! spawnDraw->isSelected())
  759. {
  760. TheInGameUI->selectDrawable( spawnDraw );
  761. TheInGameUI->setDisplayedMaxWarning( FALSE );
  762. teamMsg->appendBooleanArgument( FALSE );// not creating new team so pass false
  763. teamMsg->appendObjectIDArgument( currentSpawn->getID() );
  764. }
  765. }
  766. } // next iter
  767. }
  768. // if somebody is selected then I sure need to be!
  769. if ( ! obj->getDrawable()->isSelected())
  770. {
  771. TheInGameUI->selectDrawable( obj->getDrawable() );
  772. TheInGameUI->setDisplayedMaxWarning( FALSE );
  773. teamMsg->appendBooleanArgument( FALSE );// not creating new team so pass false
  774. teamMsg->appendObjectIDArgument( obj->getID() );
  775. }
  776. }
  777. // HEALTH BOX POSITION *****************************
  778. // pick a centered, average spot to draw the health box
  779. avgSpawnPos.scale(1.0f / spawnCount);
  780. avgSpawnPos.sub(obj->getPosition());
  781. obj->setHealthBoxOffset(avgSpawnPos);
  782. // HEALTH STATE *************************************
  783. // make my health an aggregate of all my spawns' healths
  784. if ( spawnCount )
  785. {
  786. avgHealthMax /= spawnCount;
  787. Real perfectTotalHealth = avgHealthMax * spawnCountMax;
  788. Real actualHealth = acrHealth / perfectTotalHealth;
  789. obj->getBodyModule()->setInitialHealth(100.0f * actualHealth);
  790. }
  791. else
  792. {
  793. obj->getBodyModule()->setInitialHealth(0);// I been sick <
  794. }
  795. // HOUSEKEEPING *************************************
  796. //make sure no enemies are shooting at the nexus, since it doesn't 'exist'
  797. obj->setStatus(OBJECT_STATUS_MASKED);
  798. }
  799. // ------------------------------------------------------------------------------------------------
  800. /** CRC */
  801. // ------------------------------------------------------------------------------------------------
  802. void SpawnBehavior::crc( Xfer *xfer )
  803. {
  804. // extend base class
  805. BehaviorModule::crc( xfer );
  806. } // end crc
  807. // ------------------------------------------------------------------------------------------------
  808. /** Xfer method
  809. * Version Info:
  810. * 1: Initial version
  811. * 2: Added m_initialBurstTimesInited to the save. jba.
  812. */
  813. // ------------------------------------------------------------------------------------------------
  814. void SpawnBehavior::xfer( Xfer *xfer )
  815. {
  816. AsciiString name;
  817. // version
  818. XferVersion currentVersion = 2;
  819. XferVersion version = currentVersion;
  820. xfer->xferVersion( &version, currentVersion );
  821. // extend base class
  822. BehaviorModule::xfer( xfer );
  823. if (version >= 2) {
  824. xfer->xferBool(&m_initialBurstTimesInited);
  825. }
  826. // spawn template
  827. name = m_spawnTemplate ? m_spawnTemplate->getName() : AsciiString::TheEmptyString;
  828. xfer->xferAsciiString( &name );
  829. if( xfer->getXferMode() == XFER_LOAD )
  830. {
  831. m_spawnTemplate = NULL;
  832. if( name.isEmpty() == FALSE )
  833. {
  834. m_spawnTemplate = TheThingFactory->findTemplate( name );
  835. if( m_spawnTemplate == NULL )
  836. {
  837. DEBUG_CRASH(( "SpawnBehavior::xfer - Unable to find template '%s'\n", name.str() ));
  838. throw SC_INVALID_DATA;
  839. } // end if
  840. } // end if
  841. } // end if
  842. // one shot countdown
  843. xfer->xferInt( &m_oneShotCountdown );
  844. // frames to wait
  845. xfer->xferInt( &m_framesToWait );
  846. // first batch count
  847. xfer->xferInt( &m_firstBatchCount );
  848. // replacement times
  849. if( xfer->getXferMode() == XFER_LOAD )
  850. m_replacementTimes.clear();
  851. xfer->xferSTLIntList( &m_replacementTimes );
  852. // spawn ids
  853. xfer->xferSTLObjectIDList( &m_spawnIDs );
  854. // active
  855. xfer->xferBool( &m_active );
  856. // aggregate health
  857. xfer->xferBool( &m_aggregateHealth );
  858. // spawn count
  859. xfer->xferInt( &m_spawnCount );
  860. // self tasking spawn count
  861. xfer->xferUnsignedInt( &m_selfTaskingSpawnCount );
  862. } // end xfer
  863. // ------------------------------------------------------------------------------------------------
  864. /** Load post process */
  865. // ------------------------------------------------------------------------------------------------
  866. void SpawnBehavior::loadPostProcess( void )
  867. {
  868. // extend base class
  869. BehaviorModule::loadPostProcess();
  870. } // end loadPostProcess