GarrisonContain.cpp 55 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656
  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: GarrisonContain.cpp //////////////////////////////////////////////////////////////////////
  24. // Author: Colin Day, February 2002
  25. // Desc: Contain module for structures that can be garrisoned
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #include "Common/GameState.h"
  30. #include "Common/PerfTimer.h"
  31. #include "Common/Player.h"
  32. #include "Common/PlayerList.h"
  33. #include "Common/RandomValue.h"
  34. #include "Common/Team.h"
  35. #include "Common/ThingFactory.h"
  36. #include "Common/ThingTemplate.h"
  37. #include "Common/Xfer.h"
  38. #include "GameLogic/AIPathfind.h"
  39. #include "GameLogic/GameLogic.h"
  40. #include "GameLogic/Object.h"
  41. #include "GameLogic/Module/AIUpdate.h"
  42. #include "GameLogic/Module/GarrisonContain.h"
  43. #include "GameLogic/Module/BodyModule.h"
  44. #include "GameLogic/PartitionManager.h"
  45. #include "GameLogic/Weapon.h"
  46. #include "GameClient/Drawable.h"
  47. #include "GameClient/GameClient.h"
  48. #include "GameClient/InGameUI.h"
  49. #include "GameClient/View.h"
  50. #ifdef _INTERNAL
  51. //#pragma optimize("", off)
  52. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  53. #endif
  54. ///////////////////////////////////////////////////////////////////////////////////////////////////
  55. enum { MUZZLE_FLASH_LIFETIME = LOGICFRAMES_PER_SECOND / 7 };
  56. // ------------------------------------------------------------------------------------------------
  57. // ------------------------------------------------------------------------------------------------
  58. GarrisonContainModuleData::GarrisonContainModuleData( void )
  59. {
  60. //
  61. // by default we say that transports can have infantry inside them, this will be totally
  62. // overwritten by any data provided from the INI entry tho
  63. //
  64. m_allowInsideKindOf = MAKE_KINDOF_MASK( KINDOF_INFANTRY );
  65. m_mobileGarrison = FALSE;
  66. m_doIHealObjects = false; ///< if T, then I heal objects that are inside of me
  67. m_framesForFullHeal = 1.0f; ///< the number of frames something inside of me takes to heal
  68. m_immuneToClearBuildingAttacks = false;
  69. m_initialRoster.count = 0;
  70. } // end if
  71. //-----------------------------------------------------------------------------
  72. inline Real calcDistSqr(const Coord3D& a, const Coord3D& b)
  73. {
  74. return sqr(a.x - b.x) + sqr(a.y - b.y) + sqr(a.z - b.z);
  75. }
  76. // ------------------------------------------------------------------------------------------------
  77. /** Given the target position, find the garrison point that is closest to it */
  78. // ------------------------------------------------------------------------------------------------
  79. Int GarrisonContain::findClosestFreeGarrisonPointIndex( Int conditionIndex,
  80. const Coord3D *targetPos )
  81. {
  82. DEBUG_ASSERTCRASH(m_garrisonPointsInitialized, ("garrisonPoints are not inited"));
  83. // sanity
  84. if( targetPos == NULL || m_garrisonPointsInUse == MAX_GARRISON_POINTS )
  85. return GARRISON_INDEX_INVALID;
  86. Int closestIndex = GARRISON_INDEX_INVALID;
  87. Real closestDistSq = -1.0f;
  88. Real distSq;
  89. for( Int i = 0; i < MAX_GARRISON_POINTS; ++i )
  90. {
  91. // only consider free garrison points
  92. if( m_garrisonPointData[ i ].object == NULL )
  93. {
  94. // compute the squared distance between these two points
  95. distSq = calcDistSqr(*targetPos, m_garrisonPoint[ conditionIndex ][ i ]);
  96. if( distSq < closestDistSq || closestDistSq == -1.0f )
  97. {
  98. closestDistSq = distSq;
  99. closestIndex = i;
  100. } // end if
  101. } // end if
  102. } // end for i
  103. return closestIndex;
  104. } // end findClosestFreeGarrisonPointIndex
  105. // ------------------------------------------------------------------------------------------------
  106. /** Given the object, return the garrison point index the object is placed at ... if any */
  107. // ------------------------------------------------------------------------------------------------
  108. Int GarrisonContain::getObjectGarrisonPointIndex( Object *obj )
  109. {
  110. // sanity
  111. if( obj == NULL )
  112. return GARRISON_INDEX_INVALID;
  113. for( Int i = 0; i < MAX_GARRISON_POINTS; ++i )
  114. if( m_garrisonPointData[ i ].object == obj )
  115. return i;
  116. return GARRISON_INDEX_INVALID;
  117. } // end getObjectGarrisonPointIndex
  118. // ------------------------------------------------------------------------------------------------
  119. /** Put the object at the specified garrison point by index */
  120. // ------------------------------------------------------------------------------------------------
  121. void GarrisonContain::putObjectAtGarrisonPoint( Object *obj,
  122. ObjectID targetID,
  123. Int conditionIndex,
  124. Int pointIndex )
  125. {
  126. DEBUG_ASSERTCRASH(m_garrisonPointsInitialized, ("garrisonPoints are not inited"));
  127. // sanity
  128. if( obj == NULL || pointIndex < 0 || pointIndex >= MAX_GARRISON_POINTS ||
  129. conditionIndex < 0 || conditionIndex >= MAX_GARRISON_POINT_CONDITIONS )
  130. {
  131. DEBUG_CRASH(( "GarrisionContain::putObjectAtGarrisionPoint - Invalid arguments\n" ));
  132. return;
  133. } // end if
  134. // make sure this point is empty
  135. if( m_garrisonPointData[ pointIndex ].object != NULL )
  136. {
  137. DEBUG_CRASH(( "GarrisonContain::putObjectAtGarrisonPoint - Garrison Point '%d' is not empty\n",
  138. pointIndex ));
  139. return;
  140. } // end if
  141. // get the position we're going to use
  142. Coord3D pos = m_garrisonPoint[ conditionIndex ][ pointIndex ];
  143. // set the object position
  144. obj->setPosition( &pos );
  145. // save the data for being place at this point
  146. m_garrisonPointData[ pointIndex ].object = obj;
  147. m_garrisonPointData[ pointIndex ].targetID = targetID;
  148. m_garrisonPointData[ pointIndex ].placeFrame = TheGameLogic->getFrame();
  149. ++m_garrisonPointsInUse;
  150. //
  151. // create a drawable that has a gun barrel which will show there is an object at this
  152. // garrison point ready to shoot
  153. //
  154. static const ThingTemplate *muzzle = TheThingFactory->findTemplate( "GarrisonGun" );
  155. DEBUG_ASSERTCRASH( muzzle, ("Warning, Object 'GarrisonGun' not found and is need for Garrison gun effects\n") );
  156. if( muzzle )
  157. {
  158. Drawable *draw = TheThingFactory->newDrawable( muzzle );
  159. if( draw )
  160. {
  161. // set position of the drawable at the garrison fire point
  162. draw->setPosition( &pos );
  163. // record the drawable in our data array
  164. m_garrisonPointData[ pointIndex ].effect = draw;
  165. m_garrisonPointData[ pointIndex ].lastEffectFrame = 0;
  166. //Copy shroud status from our container.
  167. Drawable *containerDrawable=getObject()->getDrawable();
  168. if (containerDrawable)
  169. draw->setFullyObscuredByShroud(containerDrawable->getFullyObscuredByShroud());
  170. } // end if
  171. } // end if
  172. /*
  173. UnicodeString msg;
  174. msg.format( L"Added object '%S'(%d) to point '%d'",
  175. obj->getTemplate()->getName().str(),
  176. obj->getID(),
  177. pointIndex );
  178. TheInGameUI->message( msg );
  179. */
  180. } // end putObjectAtGarrisonPoint
  181. // ------------------------------------------------------------------------------------------------
  182. /** Given the current state of the structure, return the condition index we are to use
  183. * from the garrison point position arrays */
  184. // ------------------------------------------------------------------------------------------------
  185. Int GarrisonContain::findConditionIndex( void )
  186. {
  187. BodyModuleInterface *body = getObject()->getBodyModule();
  188. BodyDamageType bodyDamage = body->getDamageState();
  189. Int index = GARRISON_INDEX_INVALID;
  190. switch( bodyDamage )
  191. {
  192. // --------------------------------------------------------------------------------------------
  193. case BODY_PRISTINE:
  194. index = GARRISON_POINT_PRISTINE;
  195. break;
  196. // --------------------------------------------------------------------------------------------
  197. case BODY_DAMAGED:
  198. index = GARRISON_POINT_DAMAGED;
  199. break;
  200. // --------------------------------------------------------------------------------------------
  201. case BODY_REALLYDAMAGED:
  202. case BODY_RUBBLE:
  203. index = GARRISON_POINT_REALLY_DAMAGED;
  204. break;
  205. // --------------------------------------------------------------------------------------------
  206. default:
  207. DEBUG_CRASH(( "GarrisonContain::findConditionIndex - Unknown body damage type '%d'\n",
  208. bodyDamage ));
  209. break;
  210. } // end switch
  211. return index;
  212. } // end findConditionIndex
  213. //-------------------------------------------------------------------------------------------------
  214. //The weapon system would like to perform a range check assuming the object is placed in the
  215. //best possible available garrison position. If found, we change sourcePos to that position.
  216. //-------------------------------------------------------------------------------------------------
  217. Bool GarrisonContain::calcBestGarrisonPosition( Coord3D *sourcePos, const Coord3D *targetPos )
  218. {
  219. // sanity
  220. if( !sourcePos || !targetPos )
  221. return FALSE;
  222. // find which garrison point position array we will used based on body condition
  223. Int conditionIndex = findConditionIndex();
  224. // get the index of the garrison point that is closest to the target position
  225. Int placeIndex = findClosestFreeGarrisonPointIndex( conditionIndex, targetPos );
  226. if( placeIndex == GARRISON_INDEX_INVALID )
  227. {
  228. DEBUG_CRASH( ("GarrisonContain::calcBestGarrisonPosition - Unable to find suitable garrison point.\n") );
  229. return FALSE;
  230. }
  231. sourcePos->set( &(m_garrisonPoint[ conditionIndex ][ placeIndex ]) );
  232. return TRUE;
  233. }
  234. //-------------------------------------------------------------------------------------------------
  235. //The AI is entering the aim state and would like to move the unit to the best position, perform
  236. //a range check, and if it succeeds, leave him there -- otherwise, remove him immediately.
  237. //-------------------------------------------------------------------------------------------------
  238. Bool GarrisonContain::attemptBestFirePointPosition( Object *source, Weapon *weapon, Object *victim )
  239. {
  240. //Sanity
  241. if( !source || !victim || !weapon )
  242. {
  243. return FALSE;
  244. }
  245. //If this object is already at a garrison point, remove him.
  246. Int existingIndex = getObjectGarrisonPointIndex( source );
  247. if( existingIndex != GARRISON_INDEX_INVALID )
  248. {
  249. removeObjectFromGarrisonPoint( source, existingIndex );
  250. }
  251. putObjectAtBestGarrisonPoint( source, victim, NULL );
  252. //Okay, now we have positioned the object in the best position for the victim.
  253. //Now check if we are able to fire on our victim.
  254. if( weapon->isWithinAttackRange( source, victim ) )
  255. {
  256. return TRUE;
  257. }
  258. //Crap, we failed... so remove the object from the garrison point.
  259. existingIndex = getObjectGarrisonPointIndex( source );
  260. if( existingIndex != GARRISON_INDEX_INVALID )
  261. {
  262. removeObjectFromGarrisonPoint( source, existingIndex );
  263. }
  264. return FALSE;
  265. }
  266. //-------------------------------------------------------------------------------------------------
  267. //The AI is entering the aim state and would like to move the unit to the best position, perform
  268. //a range check, and if it succeeds, leave him there -- otherwise, remove him immediately.
  269. //-------------------------------------------------------------------------------------------------
  270. Bool GarrisonContain::attemptBestFirePointPosition( Object *source, Weapon *weapon, const Coord3D *targetPos )
  271. {
  272. //Sanity
  273. if( !source || !targetPos || !weapon )
  274. {
  275. return FALSE;
  276. }
  277. //If this object is already at a garrison point, remove him.
  278. Int existingIndex = getObjectGarrisonPointIndex( source );
  279. if( existingIndex != GARRISON_INDEX_INVALID )
  280. {
  281. removeObjectFromGarrisonPoint( source, existingIndex );
  282. }
  283. putObjectAtBestGarrisonPoint( source, NULL, targetPos );
  284. //Okay, now we have positioned the object in the best position for the targetPos.
  285. //Now check if we are able to fire on our targetPos.
  286. if( weapon->isWithinAttackRange( source, targetPos ) )
  287. {
  288. return TRUE;
  289. }
  290. //Crap, we failed... so remove the object from the garrison point.
  291. existingIndex = getObjectGarrisonPointIndex( source );
  292. if( existingIndex != GARRISON_INDEX_INVALID )
  293. {
  294. removeObjectFromGarrisonPoint( source, existingIndex );
  295. }
  296. return FALSE;
  297. }
  298. //-------------------------------------------------------------------------------------------------
  299. /** Place the object at the "best" garrison point position so it's on the same "side" of
  300. * the structure that its target is */
  301. //-------------------------------------------------------------------------------------------------
  302. void GarrisonContain::putObjectAtBestGarrisonPoint( Object *obj, Object *target, const Coord3D *targetPos )
  303. {
  304. // sanity
  305. if( obj == NULL || (target == NULL && targetPos == NULL) )
  306. return;
  307. // if obj target, override pos
  308. if (target != NULL)
  309. targetPos = target->getPosition();
  310. // if this object is already at a garrison point do nothing
  311. if( getObjectGarrisonPointIndex( obj ) != GARRISON_INDEX_INVALID )
  312. return;
  313. // find which garrison point position array we will used based on body condition
  314. Int conditionIndex = findConditionIndex();
  315. // get the index of the garrison point that is closest to the target position
  316. Int placeIndex = findClosestFreeGarrisonPointIndex( conditionIndex, targetPos );
  317. DEBUG_ASSERTCRASH( placeIndex != GARRISON_INDEX_INVALID,
  318. ("GarrisonContain::putObjectAtBestGarrisonPoint - Unable to find suitable garrison point for '%s'\n",
  319. obj->getTemplate()->getName().str()) );
  320. // put it here
  321. putObjectAtGarrisonPoint( obj, target ? target->getID() : INVALID_ID, conditionIndex, placeIndex );
  322. } // end putObjectAtBestGarrisonPoint
  323. // ------------------------------------------------------------------------------------------------
  324. /** Remove the object from the garrison point position and replace at the center of the building */
  325. // ------------------------------------------------------------------------------------------------
  326. void GarrisonContain::removeObjectFromGarrisonPoint( Object *obj, Int index )
  327. {
  328. // sanity
  329. if( obj == NULL )
  330. return;
  331. // search for the object in the garrison point data, if found, remove it
  332. Int removeIndex = index;
  333. if( removeIndex == SEARCH_FOR_REMOVE )
  334. {
  335. for( Int i = 0; i < MAX_GARRISON_POINTS; ++i )
  336. {
  337. if( m_garrisonPointData[ i ].object == obj )
  338. {
  339. removeIndex = i;
  340. break;
  341. } // end if
  342. } // end for i
  343. } // end if
  344. // validate the index slot to remove
  345. if( removeIndex < 0 || removeIndex >= MAX_GARRISON_POINTS )
  346. {
  347. //
  348. // this is not an error, if a search was ordered, we may very well not find the
  349. // object at a garrison point and therefore can't remove it
  350. //
  351. return;
  352. } // end if
  353. // remove from this spot
  354. m_garrisonPointData[ removeIndex ].object = NULL;
  355. m_garrisonPointData[ removeIndex ].targetID = INVALID_ID;
  356. m_garrisonPointData[ removeIndex ].placeFrame = 0;
  357. m_garrisonPointData[ removeIndex ].lastEffectFrame = 0;
  358. --m_garrisonPointsInUse;
  359. // destroy drawable for gun barrel and effects if present
  360. if( m_garrisonPointData[ removeIndex ].effect )
  361. TheGameClient->destroyDrawable( m_garrisonPointData[ removeIndex ].effect );
  362. m_garrisonPointData[ removeIndex ].effect = NULL;
  363. // set the position of the object to back to the center of the garrisoned building
  364. obj->setPosition( getObject()->getPosition() );
  365. /*
  366. UnicodeString msg;
  367. msg.format( L"Removed object '%S'(%d) from point '%d'",
  368. obj->getTemplate()->getName().str(),
  369. obj->getID(),
  370. removeIndex );
  371. TheInGameUI->message( msg );
  372. */
  373. } // end removeObjectFromGarrisonPoint
  374. //-------------------------------------------------------------------------------------------------
  375. //-------------------------------------------------------------------------------------------------
  376. GarrisonContain::GarrisonContain( Thing *thing, const ModuleData *moduleData ) :
  377. OpenContain( thing, moduleData )
  378. {
  379. Int i, j;
  380. m_originalTeam = NULL;
  381. m_hideGarrisonedStateFromNonallies = FALSE;
  382. m_garrisonPointsInUse = 0;
  383. m_garrisonPointsInitialized = FALSE;
  384. for( i = 0; i < MAX_GARRISON_POINTS; i++ )
  385. {
  386. m_garrisonPointData[ i ].object = NULL;
  387. m_garrisonPointData[ i ].targetID = INVALID_ID;
  388. m_garrisonPointData[ i ].placeFrame = 0;
  389. m_garrisonPointData[ i ].lastEffectFrame = 0;
  390. m_garrisonPointData[ i ].effect = NULL;
  391. for( j = 0; j < MAX_GARRISON_POINT_CONDITIONS; ++j )
  392. m_garrisonPoint[ j ][ i ].zero();
  393. } // end for i
  394. m_rallyValid = FALSE;
  395. m_exitRallyPoint.zero();
  396. } // end GarrisonContain
  397. //-------------------------------------------------------------------------------------------------
  398. //-------------------------------------------------------------------------------------------------
  399. GarrisonContain::~GarrisonContain( void )
  400. {
  401. } // end ~GarrisonContain
  402. //-------------------------------------------------------------------------------------------------
  403. /** can this container contain this kind of object?
  404. and, if checkCapacity is TRUE, does this container have enough space
  405. left to hold the given unit? */
  406. // ------------------------------------------------------------------------------------------------
  407. Bool GarrisonContain::isValidContainerFor(const Object* obj, Bool checkCapacity) const
  408. {
  409. // extend functionality // this just tests kindof masks
  410. if( OpenContain::isValidContainerFor( obj, checkCapacity ) == false )
  411. return false;
  412. // zero-health buildings are not garrisonable.
  413. if (getObject()->getBodyModule()->getHealth() <= 0.0f)
  414. return false;
  415. // ReallyDamaged buildings are not garrisonable as well.
  416. if( getObject()->getBodyModule()->getDamageState() == BODY_REALLYDAMAGED && !getObject()->isKindOf( KINDOF_GARRISONABLE_UNTIL_DESTROYED ) )
  417. return false;
  418. if (obj && !obj->isKindOf(KINDOF_NO_GARRISON))
  419. {
  420. if (checkCapacity)
  421. {
  422. Int garrisonMax = getContainMax();
  423. Int containCount = getContainCount();
  424. return ( containCount < garrisonMax );
  425. }
  426. else
  427. {
  428. return true;
  429. }
  430. }
  431. return false;
  432. }
  433. // ------------------------------------------------------------------------------------------------
  434. /** Any objects that are sitting at the garrison points which no longer have targets need
  435. * to be moved to the center of the building and taken off the garrison point */
  436. // ------------------------------------------------------------------------------------------------
  437. void GarrisonContain::removeInvalidObjectsFromGarrisonPoints( void )
  438. {
  439. Object *obj;
  440. if (m_garrisonPointsInUse == 0)
  441. return; // my, that was easy
  442. for( Int i = 0; i < MAX_GARRISON_POINTS; ++i )
  443. {
  444. obj = m_garrisonPointData[ i ].object;
  445. if( obj )
  446. {
  447. AIUpdateInterface *ai = obj->getAIUpdateInterface();
  448. Bool targetIsValid = true; // assume true for now...
  449. Object *goalObject = ai->getGoalObject();
  450. if( goalObject )
  451. {
  452. Weapon *weapon = obj->getCurrentWeapon();
  453. if( !weapon || !weapon->isWithinAttackRange( obj, goalObject ) )
  454. {
  455. //As a garrisoned member, if our target is out of range,
  456. //then get out of the space, because someone else might
  457. //be able to shoot it.
  458. targetIsValid = false;
  459. }
  460. }
  461. // note that we can be attacking a position, rather than an object...
  462. if( !obj->testStatus(OBJECT_STATUS_IS_ATTACKING) || !targetIsValid )
  463. {
  464. removeObjectFromGarrisonPoint( obj, i );
  465. }
  466. } // end if
  467. } // end for i
  468. } // end removeInvalidObjectsFromGarrisonPoints
  469. // ------------------------------------------------------------------------------------------------
  470. /** Are there any objects in the center that have now obtained targets and need to move to
  471. * a garrison point */
  472. // ------------------------------------------------------------------------------------------------
  473. void GarrisonContain::addValidObjectsToGarrisonPoints( void )
  474. {
  475. const ContainedItemsList& containList = getContainList();
  476. if (containList.empty())
  477. return;
  478. for( ContainedItemsList::const_iterator it = containList.begin(); it != containList.end(); ++it )
  479. {
  480. Object* obj = *it;
  481. AIUpdateInterface* ai = obj->getAIUpdateInterface();
  482. if( ai )
  483. {
  484. Object *victim = ai->getCurrentVictim();
  485. const Coord3D *victimPos = ai->getCurrentVictimPos();
  486. //
  487. // add this object to the garrison point that is closest to its target if it's not
  488. // already in there
  489. //
  490. if( victim )
  491. putObjectAtBestGarrisonPoint( obj, victim, NULL );
  492. else if( victimPos )
  493. putObjectAtBestGarrisonPoint( obj, NULL, victimPos );
  494. } // end if
  495. } // end for it
  496. } // end addValidObjectsToGarrisonPoints
  497. // ------------------------------------------------------------------------------------------------
  498. /** Every frame this method is called. It keeps any of the attacking units at any of the
  499. * fire points closest to their active target and shuffles them around to any open garrison
  500. * points that are available if they are closer. We will also track our targets position
  501. * and orient any effect stuff we need to (gun barrel / muzzle flash) */
  502. // ------------------------------------------------------------------------------------------------
  503. void GarrisonContain::trackTargets( void )
  504. {
  505. Int conditionIndex = findConditionIndex();
  506. const ContainedItemsList& containList = getContainList();
  507. AIUpdateInterface *ai;
  508. Object *obj;
  509. for( ContainedItemsList::const_iterator it = containList.begin(); it != containList.end(); ++it )
  510. {
  511. DEBUG_ASSERTCRASH(m_garrisonPointsInitialized, ("garrisonPoints are not inited"));
  512. // get the object
  513. obj = *it;
  514. // only consider objects that are actually at garrison points for re-shuffling
  515. Int ourIndex = getObjectGarrisonPointIndex( obj );
  516. if( ourIndex != GARRISON_INDEX_INVALID )
  517. {
  518. // does this object have a target?
  519. ai = obj->getAIUpdateInterface();
  520. if( ai )
  521. {
  522. Object *victim = ai->getCurrentVictim();
  523. // even though the target position can't change in some cases, still must do this code at least once.
  524. const Coord3D *victimPos = ai->getCurrentVictimPos();
  525. if( victim || victimPos )
  526. {
  527. if (victim)
  528. victimPos = victim->getPosition();
  529. const Coord3D *ourPos = obj->getPosition();
  530. // find the closest free (of all remaining) garrison points to our target
  531. Int newIndex = findClosestFreeGarrisonPointIndex( conditionIndex,
  532. victimPos );
  533. // if unable to find another garrison point, don't bother
  534. if( newIndex != GARRISON_INDEX_INVALID )
  535. {
  536. // get the distance from our current index to the target
  537. Real currentDistSq = calcDistSqr(*victimPos, *ourPos );
  538. // get the distance from the newly chosen index
  539. Real newDistSq = calcDistSqr(*victimPos, m_garrisonPoint[ conditionIndex ][ newIndex ] );
  540. // if the newly chosen index is closer than our current index, switch
  541. if( newDistSq < currentDistSq )
  542. {
  543. // remove from the old index
  544. removeObjectFromGarrisonPoint( obj, ourIndex );
  545. // place at the new index
  546. putObjectAtGarrisonPoint( obj, victim ? victim->getID() : INVALID_ID, conditionIndex, newIndex );
  547. } // end if, new index is closer
  548. } // end if, possible closer index was found
  549. //
  550. // we are now either at a new garrison fire point, or we have remained at our
  551. // existing point still tracking our target. Orient the effect drawable which
  552. // shows the gun barrel and muzzle flash towards our target position
  553. //
  554. if( m_garrisonPointData[ ourIndex ].effect )
  555. {
  556. Coord2D v;
  557. v.x = victimPos->x - ourPos->x;
  558. v.y = victimPos->y - ourPos->y;
  559. // v.z = victomPos->z - ourPos.z;
  560. // orient the effect object towards the victim position
  561. m_garrisonPointData[ ourIndex ].effect->setOrientation( v.toAngle() );
  562. } // end if
  563. } // end if, victim present
  564. } // end if, ai
  565. } // end if, we're at a garrison point
  566. } // end for it
  567. } // end trackTargets
  568. // ------------------------------------------------------------------------------------------------
  569. /** Remove all the objects at garrison points back to the center and redeploy them among the
  570. * garrison points. NOTE that we are preserving the frame in which the object was put
  571. * at the garrison point originally as this method is used when the model condition changes
  572. * which could shuffle the garrison point positions but that shouldn't logically change
  573. * when an object was placed at the point */
  574. // ------------------------------------------------------------------------------------------------
  575. void GarrisonContain::redeployOccupants( void )
  576. {
  577. GarrisonPointData garrisonPointDataCopy[ MAX_GARRISON_POINTS ];
  578. Int i;
  579. // copy the current set of garrison point data sets
  580. for( i = 0; i < MAX_GARRISON_POINTS; ++i )
  581. garrisonPointDataCopy[ i ] = m_garrisonPointData[ i ];
  582. // remove the occupants
  583. removeInvalidObjectsFromGarrisonPoints();
  584. // redeploy them
  585. addValidObjectsToGarrisonPoints();
  586. // restore the frame markers that things were recorded as entering their point
  587. Int index;
  588. for( i = 0; i < MAX_GARRISON_POINTS; ++i )
  589. {
  590. if( garrisonPointDataCopy[ i ].object )
  591. {
  592. // where was this object redeployed
  593. index = getObjectGarrisonPointIndex( garrisonPointDataCopy[ i ].object );
  594. if( index != GARRISON_INDEX_INVALID )
  595. m_garrisonPointData[ index ].placeFrame = garrisonPointDataCopy[ i ].placeFrame;
  596. } // end if
  597. } // end for i
  598. } // end redeployOccupants
  599. // ------------------------------------------------------------------------------------------------
  600. /** Do any effects during an update cycle that we need to */
  601. // ------------------------------------------------------------------------------------------------
  602. void GarrisonContain::updateEffects( void )
  603. {
  604. UnsignedInt currentFrame = TheGameLogic->getFrame();
  605. const ContainedItemsList& containList = getContainList();
  606. for( ContainedItemsList::const_iterator it = containList.begin(); it != containList.end(); ++it )
  607. {
  608. Object *obj;
  609. // get the object
  610. obj = *it;
  611. //
  612. // did the object fire last frame, if so make a muzzle flash if needed at the
  613. // garrison point
  614. //
  615. if( obj->getLastShotFiredFrame() == currentFrame - 1 )
  616. {
  617. Int garrisonIndex = getObjectGarrisonPointIndex( obj );
  618. // only consider doing muzzle flash logic if the object is actually at a garrison point
  619. if( garrisonIndex != GARRISON_INDEX_INVALID )
  620. {
  621. // set the model condition for the effect object to show the muzzle flash
  622. Drawable *effect = m_garrisonPointData[ garrisonIndex ].effect;
  623. if( effect )
  624. {
  625. // set the model condition
  626. effect->setModelConditionState( MODELCONDITION_FIRING_A );
  627. // mark this "fire frame" so we can turn it off in a little while
  628. m_garrisonPointData[ garrisonIndex ].lastEffectFrame = currentFrame;
  629. } // end if
  630. } // end if, object is at garrision point
  631. } // end if, object shot last frame
  632. } // end for containment iterator
  633. // remove any firing effects for time that has passed
  634. for( Int i = 0; i < MAX_GARRISON_POINTS; ++i )
  635. {
  636. if( m_garrisonPointData[ i ].effect &&
  637. m_garrisonPointData[ i ].lastEffectFrame != 0 &&
  638. currentFrame - m_garrisonPointData[ i ].lastEffectFrame > MUZZLE_FLASH_LIFETIME )
  639. {
  640. // clear the model condition
  641. m_garrisonPointData[ i ].effect->clearModelConditionState( MODELCONDITION_FIRING_A );
  642. // clear the last effect frame
  643. m_garrisonPointData[ i ].lastEffectFrame = 0;
  644. } // end if
  645. } // end for i
  646. } // end updateEffects
  647. //-------------------------------------------------------------------------------------------------
  648. //-------------------------------------------------------------------------------------------------
  649. UpdateSleepTime GarrisonContain::update( void )
  650. {
  651. const GarrisonContainModuleData *modData = getGarrisonContainModuleData();
  652. // extend functionality
  653. UpdateSleepTime result;
  654. result = OpenContain::update();
  655. // remove effectively dead objects from this garrison container
  656. const ContainedItemsList& containList = getContainList();
  657. Object *contained;
  658. for( ContainedItemsList::const_iterator it = containList.begin(); it != containList.end(); /*empty*/ )
  659. {
  660. // get object
  661. contained = *it;
  662. // increment iterator, we may delete the object
  663. ++it;
  664. // remove if dead
  665. if( contained->isEffectivelyDead() )
  666. {
  667. // remove from container
  668. removeFromContain( contained );
  669. // set the safe occlusion frame to way way way in the future so we never see it during death
  670. #define HUGE_FRAME_IN_FUTURE (LOGICFRAMES_PER_SECOND * 1000)
  671. contained->setSafeOcclusionFrame( TheGameLogic->getFrame() + HUGE_FRAME_IN_FUTURE );
  672. } // end if
  673. } // end for, it
  674. // are there any objects at the garrison points who now need to go back to the center of the structure
  675. removeInvalidObjectsFromGarrisonPoints();
  676. //
  677. // are there any objects in the center that have now obtained targets and need to move to
  678. // a garrison point
  679. //
  680. addValidObjectsToGarrisonPoints();
  681. // any units that have just fired need to have a muzzle flash display out of the fire point
  682. updateEffects();
  683. //
  684. // given all the objects that are at the garrison points shooting at something, if their
  685. // target moves around the structure and closer to another open garrison point we want
  686. // to shuffle our object to the new closest garrison point. We'll also track the target
  687. // here and set orientation for any effects we need to
  688. //
  689. trackTargets();
  690. healObjects();
  691. if (modData->m_mobileGarrison && (getObject()->isMobile() == TRUE) )
  692. {
  693. moveObjectsWithMe();
  694. }
  695. else
  696. {
  697. // sanity information
  698. DEBUG_ASSERTCRASH( getObject()->isMobile() == FALSE,
  699. ("GarrisonContain::update - Objects with garrison contain can be spec'd as 'mobile' in the INI. Do you really want to do this? \n") );
  700. }
  701. return UPDATE_SLEEP_NONE;
  702. } // end update
  703. //-------------------------------------------------------------------------------------------------
  704. //-------------------------------------------------------------------------------------------------
  705. void GarrisonContain::healObjects( void )
  706. {
  707. const GarrisonContainModuleData *modData = getGarrisonContainModuleData();
  708. if (!modData->m_doIHealObjects)
  709. return;
  710. const ContainedItemsList& containList = getContainList();
  711. for( ContainedItemsList::const_iterator it = containList.begin(); it != containList.end(); ++it )
  712. {
  713. Object *obj;
  714. // get the object
  715. obj = *it;
  716. healSingleObject(obj, modData->m_framesForFullHeal);
  717. }
  718. }
  719. //-------------------------------------------------------------------------------------------------
  720. //-------------------------------------------------------------------------------------------------
  721. void GarrisonContain::healSingleObject( Object *obj, Real framesForFullHeal)
  722. {
  723. // setup the healing damageInfo structure with all but the amount
  724. DamageInfo healInfo;
  725. healInfo.in.m_damageType = DAMAGE_HEALING;
  726. healInfo.in.m_deathType = DEATH_NONE;
  727. //healInfo.in.m_sourceID = getObject()->getID();
  728. // get body module of the thing to heal
  729. BodyModuleInterface *body = obj->getBodyModule();
  730. // if we've been in here long enough ... set our health to max
  731. if( TheGameLogic->getFrame() - obj->getContainedByFrame() >= framesForFullHeal )
  732. {
  733. // set the amount to max just to be sure we're at the top
  734. healInfo.in.m_amount = body->getMaxHealth();
  735. // set max health
  736. body->attemptHealing( &healInfo );
  737. } // end if
  738. else
  739. {
  740. //
  741. // given the *whole* time it would take to heal this object, lets pretend that the
  742. // object is at zero health ... and give it a sliver of health as if it were at 0 health
  743. // and would be fully healed at 'framesForFullHeal'
  744. //
  745. healInfo.in.m_amount = body->getMaxHealth() / framesForFullHeal;
  746. // do the healing
  747. body->attemptHealing( &healInfo );
  748. } // end else
  749. }
  750. //-------------------------------------------------------------------------------------------------
  751. /** return the player that *appears* to control this unit. if null,
  752. use getObject()->getControllingPlayer() instead. */
  753. // ------------------------------------------------------------------------------------------------
  754. const Player* GarrisonContain::getApparentControllingPlayer( const Player* observingPlayer ) const
  755. {
  756. const Player* myPlayer = getObject()->getControllingPlayer();
  757. if ( m_hideGarrisonedStateFromNonallies && m_originalTeam && myPlayer && observingPlayer )
  758. {
  759. Relationship r = myPlayer->getRelationship(observingPlayer->getDefaultTeam());
  760. if (r != ALLIES)
  761. return m_originalTeam->getControllingPlayer();
  762. }
  763. return myPlayer;
  764. }
  765. //-------------------------------------------------------------------------------------------------
  766. //-------------------------------------------------------------------------------------------------
  767. void GarrisonContain::recalcApparentControllingPlayer( void )
  768. {
  769. //Record original team first time through.
  770. if( m_originalTeam == NULL )
  771. {
  772. m_originalTeam = getObject()->getTeam();
  773. }
  774. // (hokey trick: if our team is null, nuke originalTeam -- this
  775. // usually means we are being called during game-teardown and
  776. // the teams are no longer valid...)
  777. if (getObject()->getTeam() == NULL)
  778. m_originalTeam = NULL;
  779. // Check to see if we have any units contained in our object
  780. if( getContainCount() > 0 )
  781. {
  782. ContainedItemsList::const_iterator it = getContainList().begin();
  783. Object *rider = *it;
  784. // Check to see if all the contained units are stealthy. Need to set this flag before the capture,
  785. // since the Radar refresh in setTeam will want to use it to decide our color.
  786. Bool detected = ( rider->getStatusBits() & OBJECT_STATUS_DETECTED );
  787. m_hideGarrisonedStateFromNonallies = ( !detected && ( getStealthUnitsContained() == getContainCount() ) );
  788. Player* controller = rider->getControllingPlayer();
  789. Team *team = controller ? controller->getDefaultTeam() : NULL;
  790. if( team )
  791. {
  792. getObject()->setTeam( team );
  793. }
  794. }
  795. else
  796. {
  797. //Nothing in object, so set team to original team.
  798. getObject()->setTeam( m_originalTeam );
  799. m_hideGarrisonedStateFromNonallies = false;
  800. }
  801. //Only allow the garrison state to be set if the client team knows that it is garrisoned.
  802. Drawable *draw = getObject()->getDrawable();
  803. if( draw )
  804. {
  805. Bool setModelGarrisoned = FALSE;
  806. if ( getContainCount() > 0 )
  807. {
  808. ContainedItemsList::const_iterator it = getContainList().begin();
  809. Object *occupant = *it;
  810. Bool detected = ( occupant->getStatusBits() & OBJECT_STATUS_DETECTED );
  811. if( detected || (getApparentControllingPlayer(ThePlayerList->getLocalPlayer()) == getObject()->getControllingPlayer()) )
  812. {
  813. setModelGarrisoned = TRUE;
  814. }
  815. }
  816. if ( setModelGarrisoned )
  817. draw->setModelConditionState( MODELCONDITION_GARRISONED );
  818. else
  819. draw->clearModelConditionState( MODELCONDITION_GARRISONED );
  820. // Handle the team color that is rendered
  821. const Player* controller = getApparentControllingPlayer(ThePlayerList->getLocalPlayer());
  822. if (controller)
  823. {
  824. if (TheGlobalData->m_timeOfDay == TIME_OF_DAY_NIGHT)
  825. draw->setIndicatorColor( controller->getPlayerNightColor() );
  826. else
  827. draw->setIndicatorColor( controller->getPlayerColor() );
  828. }
  829. // now that we have an object inside us, we need to get all the garrison point positions
  830. // if we don't already have them.
  831. if( getContainCount() > 0 && m_garrisonPointsInitialized == FALSE )
  832. {
  833. loadGarrisonPoints();
  834. }
  835. }
  836. }
  837. // ------------------------------------------------------------------------------------------------
  838. /** Load the garrison point position data and save for use later */
  839. // ------------------------------------------------------------------------------------------------
  840. void GarrisonContain::loadGarrisonPoints( void )
  841. {
  842. const GarrisonContainModuleData *modData = getGarrisonContainModuleData();
  843. Object *structure = getObject();
  844. Int i, j;
  845. Bool gBonesFound = FALSE;
  846. //
  847. // initialize all the garrison points to the center of the object, this assumes that we
  848. // will never move (these points are cached)
  849. //
  850. for( i = 0; i < MAX_GARRISON_POINT_CONDITIONS; ++i )
  851. for( j = 0; j < MAX_GARRISON_POINTS; ++j )
  852. m_garrisonPoint[ i ][ j ] = *(structure->getPosition());
  853. //
  854. // in order to get all the garrison point positions we will actually switch the model
  855. // condition through the garrisoned pristine, damaged, and really damaged states. This
  856. // is especially important because at the time or "garrisoning" a model condition may
  857. // not actually switch to a garrisoned state because of units that garrison things
  858. // all stealthy. By caching all the bone positions once we will have access to
  859. // any of the points for any condition state at any time
  860. //
  861. {
  862. Int conditionIndex;
  863. Int count = 0;
  864. // save the original paramters for the model condition
  865. Drawable* draw = structure->getDrawable();
  866. const ModelConditionFlags originalFlags = draw->getModelConditionFlags();
  867. ModelConditionFlags clearFlags;
  868. ModelConditionFlags setFlags;
  869. // pristine garrisoned
  870. clearFlags.clear();
  871. setFlags.clear();
  872. clearFlags.set( MODELCONDITION_REALLY_DAMAGED );
  873. clearFlags.set( MODELCONDITION_RUBBLE );
  874. clearFlags.set( MODELCONDITION_SPECIAL_DAMAGED );
  875. clearFlags.set( MODELCONDITION_DAMAGED );
  876. setFlags.set( MODELCONDITION_GARRISONED );
  877. structure->clearAndSetModelConditionFlags( clearFlags, setFlags );
  878. conditionIndex = GARRISON_POINT_PRISTINE;
  879. count = structure->getMultiLogicalBonePosition("FIREPOINT", MAX_GARRISON_POINTS, m_garrisonPoint[ conditionIndex ], NULL);
  880. if ( count > 0) gBonesFound = TRUE;
  881. // damaged garrisoned
  882. clearFlags.clear();
  883. setFlags.clear();
  884. clearFlags.set( MODELCONDITION_REALLY_DAMAGED );
  885. clearFlags.set( MODELCONDITION_RUBBLE );
  886. clearFlags.set( MODELCONDITION_SPECIAL_DAMAGED );
  887. setFlags.set( MODELCONDITION_DAMAGED );
  888. structure->clearAndSetModelConditionFlags( clearFlags, setFlags );
  889. conditionIndex = GARRISON_POINT_DAMAGED;
  890. count = structure->getMultiLogicalBonePosition("FIREPOINT", MAX_GARRISON_POINTS, m_garrisonPoint[ conditionIndex ], NULL);
  891. if ( count > 0) gBonesFound = TRUE;
  892. // really damaged garrisoned
  893. clearFlags.clear();
  894. setFlags.clear();
  895. clearFlags.set( MODELCONDITION_RUBBLE );
  896. clearFlags.set( MODELCONDITION_SPECIAL_DAMAGED );
  897. clearFlags.set( MODELCONDITION_DAMAGED );
  898. setFlags.set( MODELCONDITION_REALLY_DAMAGED );
  899. structure->clearAndSetModelConditionFlags( clearFlags, setFlags );
  900. conditionIndex = GARRISON_POINT_REALLY_DAMAGED;
  901. count = structure->getMultiLogicalBonePosition("FIREPOINT", MAX_GARRISON_POINTS, m_garrisonPoint[ conditionIndex ], NULL);
  902. if ( count > 0) gBonesFound = TRUE;
  903. // restore the original condition flags
  904. draw->replaceModelConditionFlags( originalFlags );
  905. } // end if, draw
  906. // garrison points are now initialized
  907. m_garrisonPointsInitialized = TRUE;
  908. if (gBonesFound && modData->m_mobileGarrison && (getObject()->isMobile() == TRUE) )
  909. {
  910. DEBUG_ASSERTCRASH( getObject()->isMobile() == FALSE,
  911. ("GarrisonContain::update - You have specified this garrisonContain as mobile,\n yet you want garrison point placement bones... \n what are you thinking?") );
  912. }
  913. } // end loadGarrisonPoints
  914. // ------------------------------------------------------------------------------------------------
  915. /** Validate any exit rally point that has been chosen (if any). If it's not valid,
  916. * try to find a new one */
  917. // ------------------------------------------------------------------------------------------------
  918. void GarrisonContain::validateRallyPoint( void )
  919. {
  920. // if we have a rally point already picked, make sure it's valid
  921. if( m_rallyValid == TRUE )
  922. {
  923. Coord3D result;
  924. FindPositionOptions options;
  925. // ask for a valid position exactly at the rally point
  926. options.flags = FPF_IGNORE_ALLY_OR_NEUTRAL_UNITS;
  927. options.minRadius = 0.0f;
  928. options.maxRadius = 0.0f;
  929. options.ignoreObject = getObject();
  930. options.relationshipObject = getObject();
  931. if( ThePartitionManager->findPositionAround( &m_exitRallyPoint, &options, &result ) == FALSE )
  932. m_rallyValid = FALSE;
  933. } // end if
  934. // if no rally point is present, try to find one
  935. if( m_rallyValid == FALSE )
  936. {
  937. FindPositionOptions options;
  938. // pick a location for everybody to rally at
  939. options.flags = FPF_IGNORE_ALLY_OR_NEUTRAL_UNITS;
  940. options.minRadius = getObject()->getGeometryInfo().getBoundingCircleRadius();
  941. options.maxRadius = options.minRadius * 1.8f; // arbitrary max distance away, change as needed
  942. options.ignoreObject = getObject();
  943. options.relationshipObject = getObject();
  944. m_rallyValid = ThePartitionManager->findPositionAround( getObject()->getPosition(),
  945. &options,
  946. &m_exitRallyPoint );
  947. } // end if
  948. } // end validateRallyPoint
  949. // ------------------------------------------------------------------------------------------------
  950. /** Remove all contents of this container. We will try to do so with intelligent garrison
  951. * logic, but if all else fails no matter, we need to get all things out after this
  952. * call is complete */
  953. // ------------------------------------------------------------------------------------------------
  954. void GarrisonContain::removeAllContained( Bool exposeStealthUnits )
  955. {
  956. //
  957. // we will call this when we are destroying the object (either normally or through a game
  958. // reset/exit etc) *and* also when we have received an "evacuate" command from the player.
  959. // We will attempt to find a spot for all the contents to scatter to that is around the
  960. // structure, but if such a spot is not found, we'll just scatter in a random direction
  961. // from the building.
  962. //
  963. // only even bother doing this if we have contents inside us just cause it's a waste
  964. if( getContainCount() > 0 )
  965. {
  966. // validate the current rally point is still a good one, or pick a new one
  967. validateRallyPoint();
  968. } // end if
  969. // call the base class to extend functionality and do the actual removal
  970. OpenContain::removeAllContained( exposeStealthUnits );
  971. recalcApparentControllingPlayer();
  972. } // end removeAllContained
  973. // ------------------------------------------------------------------------------------------------
  974. /** 'exitObj' is one of the things we contain, it needs to 'exit' us */
  975. // ------------------------------------------------------------------------------------------------
  976. void GarrisonContain::exitObjectViaDoor( Object *exitObj, ExitDoorType exitDoor )
  977. {
  978. DEBUG_ASSERTCRASH(exitDoor == DOOR_1, ("multiple exit doors not supported here"));
  979. // We don't use the ExitPath system of the general OpenContain, we just send people out. The
  980. // direction of outing has been picked by Design to be the Screen Down at the default camera angle.
  981. removeFromContain( exitObj );
  982. Coord3D startPosition;
  983. Coord3D endPosition;
  984. Real exitAngle = getObject()->getOrientation();
  985. // Garrison doesn't have reserveDoor or exitDelay, so if we do nothing, everyone will appear on top
  986. // of each other and get stuck inside each others' extent (except for the first guy). So we'll
  987. // scatter the start point around a little to make it better.
  988. startPosition = *getObject()->getPosition();
  989. // In the case of cliff bunkers, the units start in a cliff. So we want to adjust.
  990. AIUpdateInterface *ai = exitObj->getAI();
  991. if (ai) {
  992. Locomotor *loco = ai->getCurLocomotor();
  993. if (loco && !TheAI->pathfinder()->validMovementTerrain( LAYER_GROUND, loco, &startPosition)) {
  994. // try front & back.
  995. Real offset = getObject()->getGeometryInfo().getMajorRadius();
  996. startPosition.x -= offset*Cos(exitAngle);
  997. startPosition.y -= offset*Sin(exitAngle);
  998. if (!TheAI->pathfinder()->validMovementTerrain(LAYER_GROUND, loco, &startPosition)) {
  999. startPosition.x += 2*offset*Cos(exitAngle);
  1000. startPosition.y += 2*offset*Sin(exitAngle);
  1001. if (!TheAI->pathfinder()->validMovementTerrain(LAYER_GROUND, loco, &startPosition)) {
  1002. startPosition = *getObject()->getPosition();
  1003. }
  1004. }
  1005. }
  1006. }
  1007. exitObj->setPosition( &startPosition );
  1008. exitObj->setOrientation( exitAngle );
  1009. ///< @todo This really should be automatically wrapped up in an activation sequence for objects in general
  1010. // tell the AI about it
  1011. TheAI->pathfinder()->addObjectToPathfindMap( exitObj );
  1012. endPosition = startPosition;
  1013. if( ai )
  1014. {
  1015. TheAI->pathfinder()->adjustToPossibleDestination(exitObj, ai->getLocomotorSet(), &endPosition);
  1016. std::vector<Coord3D> exitPath;
  1017. exitPath.push_back(endPosition);
  1018. ai->aiFollowPath( &exitPath, getObject(), CMD_FROM_AI );
  1019. TheAI->pathfinder()->updateGoal(exitObj, &endPosition, TheTerrainLogic->getLayerForDestination(&endPosition));
  1020. }
  1021. recalcApparentControllingPlayer();
  1022. }
  1023. //-------------------------------------------------------------------------------------------------
  1024. //-------------------------------------------------------------------------------------------------
  1025. void GarrisonContain::onContaining( Object *obj )
  1026. {
  1027. // extend base class
  1028. OpenContain::onContaining( obj );
  1029. // get the structure object
  1030. Object *structure = getObject();
  1031. // objects inside a building are held
  1032. obj->setDisabled( DISABLED_HELD );
  1033. // the building can now attack, since it has soldiers inside of it
  1034. structure->setStatus( OBJECT_STATUS_CAN_ATTACK );
  1035. // give the object a garrisoned version of its weapon
  1036. obj->setWeaponBonusCondition( WEAPONBONUSCONDITION_GARRISONED );
  1037. // put the object in the center of the building
  1038. obj->setPosition( structure->getPosition() );
  1039. //
  1040. // the team of the building is now the same as those that have garrisoned it, be sure
  1041. // to save our original team tho so that we can revert back to it when all the
  1042. // occupants are gone
  1043. //
  1044. recalcApparentControllingPlayer();
  1045. } // end onContaining
  1046. //-------------------------------------------------------------------------------------------------
  1047. //-------------------------------------------------------------------------------------------------
  1048. void GarrisonContain::onRemoving( Object *obj )
  1049. {
  1050. OpenContain::onRemoving(obj);
  1051. // first remove the object from any garrison fire point if it's at one
  1052. removeObjectFromGarrisonPoint( obj );
  1053. // give the object back a regular weapon
  1054. obj->clearWeaponBonusCondition( WEAPONBONUSCONDITION_GARRISONED );
  1055. // object is no longer held inside a garrisoned building
  1056. obj->clearDisabled( DISABLED_HELD );
  1057. // if we have nothing left inside of us then we are once again back to our original team
  1058. if( getContainCount() == 0 )
  1059. {
  1060. // put us back on our original team
  1061. // (hokey exception: if our team is null, don't bother -- this
  1062. // usually means we are being called during game-teardown and
  1063. // the teams are no longer valid...)
  1064. if (getObject()->getTeam() != NULL)
  1065. {
  1066. getObject()->setTeam( m_originalTeam );
  1067. m_originalTeam = NULL;
  1068. }
  1069. // we also lose our transient attack ability
  1070. getObject()->clearStatus( OBJECT_STATUS_CAN_ATTACK );
  1071. m_hideGarrisonedStateFromNonallies = false;
  1072. // change the state back from garrisoned
  1073. Drawable *draw = getObject()->getDrawable();
  1074. if( draw )
  1075. {
  1076. draw->clearModelConditionState( MODELCONDITION_GARRISONED );
  1077. }
  1078. } // end if
  1079. else if( getStealthUnitsContained() != getContainCount() )
  1080. {
  1081. m_hideGarrisonedStateFromNonallies = false;
  1082. }
  1083. // disable occlusion while the unit walks out of the building
  1084. ///@todo: we should probably not draw the unit to begin with and just have them pop out.
  1085. obj->setSafeOcclusionFrame(TheGameLogic->getFrame()+obj->getTemplate()->getOcclusionDelay());
  1086. recalcApparentControllingPlayer();
  1087. } // end onRemoving
  1088. // ------------------------------------------------------------------------------------------------
  1089. /** A GarrisonContain always lets people shoot out */
  1090. // ------------------------------------------------------------------------------------------------
  1091. Bool GarrisonContain::isPassengerAllowedToFire() const
  1092. {
  1093. return TRUE;
  1094. } // end isPassengerAllowedToFire
  1095. // ------------------------------------------------------------------------------------------------
  1096. /** A Mobile garrison keeps its occupants with it when it moves */
  1097. //-------------------------------------------------------------------------------------------------
  1098. void GarrisonContain::moveObjectsWithMe( void )
  1099. {
  1100. const GarrisonContainModuleData *modData = getGarrisonContainModuleData();
  1101. if (!modData->m_mobileGarrison)
  1102. return;
  1103. const ContainedItemsList& containList = getContainList();
  1104. for( ContainedItemsList::const_iterator it = containList.begin(); it != containList.end(); ++it )
  1105. {
  1106. Object *obj;
  1107. // get the object
  1108. obj = *it;
  1109. obj->setPosition(getObject()->getPosition());
  1110. }
  1111. }
  1112. //-------------------------------------------------------------------------------------------------
  1113. // ------------------------------------------------------------------------------------------------
  1114. void GarrisonContain::onBodyDamageStateChange( const DamageInfo* , BodyDamageType , BodyDamageType newState )
  1115. {
  1116. // This is an event triggered on edge, so we just need to look at the newState. We know a change happened.
  1117. // And we don't need to check Rubble, since death exiting is already handled. This is Garrison specific.
  1118. if( newState == BODY_REALLYDAMAGED && !getObject()->isKindOf( KINDOF_GARRISONABLE_UNTIL_DESTROYED ) )
  1119. {
  1120. if( getContainCount() > 0 )
  1121. orderAllPassengersToExit(CMD_FROM_AI);
  1122. }
  1123. }
  1124. //-------------------------------------------------------------------------------------------------
  1125. // ------------------------------------------------------------------------------------------------
  1126. void GarrisonContain::onObjectCreated()
  1127. {
  1128. GarrisonContainModuleData* self = (GarrisonContainModuleData*)getGarrisonContainModuleData();
  1129. Int count = self->m_initialRoster.count;
  1130. const ThingTemplate* rosterTemplate = TheThingFactory->findTemplate( self->m_initialRoster.templateName );
  1131. Object* object = getObject();
  1132. for( int i = 0; i < count; i++ )
  1133. {
  1134. //We are creating a garrison that comes with an initial roster, so add it now!
  1135. Object* payload = TheThingFactory->newObject( rosterTemplate, object->getControllingPlayer()->getDefaultTeam() );
  1136. if( object->getContain() && object->getContain()->isValidContainerFor( payload, true ) )
  1137. {
  1138. object->getContain()->addToContain( payload );
  1139. }
  1140. else
  1141. {
  1142. DEBUG_CRASH( ( "DeliverPayload: PutInContainer %s is full, or not valid for the payload %s!",
  1143. object->getName().str(), self->m_initialRoster.templateName.str() ) );
  1144. }
  1145. }
  1146. }
  1147. // ------------------------------------------------------------------------------------------------
  1148. /** CRC */
  1149. // ------------------------------------------------------------------------------------------------
  1150. void GarrisonContain::crc( Xfer *xfer )
  1151. {
  1152. // extend base class
  1153. OpenContain::crc( xfer );
  1154. } // end crc
  1155. // ------------------------------------------------------------------------------------------------
  1156. /** Xfer method
  1157. * Version Info:
  1158. * 1: Initial version */
  1159. // ------------------------------------------------------------------------------------------------
  1160. void GarrisonContain::xfer( Xfer *xfer )
  1161. {
  1162. Int i;
  1163. // version
  1164. XferVersion currentVersion = 1;
  1165. XferVersion version = currentVersion;
  1166. xfer->xferVersion( &version, currentVersion );
  1167. // extend base class
  1168. OpenContain::xfer( xfer );
  1169. // original team
  1170. TeamID teamID = m_originalTeam ? m_originalTeam->getID() : TEAM_ID_INVALID;
  1171. xfer->xferUser( &teamID, sizeof( TeamID ) );
  1172. if( xfer->getXferMode() == XFER_LOAD )
  1173. {
  1174. if( teamID != TEAM_ID_INVALID )
  1175. {
  1176. m_originalTeam = TheTeamFactory->findTeamByID( teamID );
  1177. if( m_originalTeam == NULL )
  1178. {
  1179. DEBUG_CRASH(( "GarrisonContain::xfer - Unable to find original team by id\n" ));
  1180. throw SC_INVALID_DATA;
  1181. } // end if
  1182. } // end if
  1183. else
  1184. m_originalTeam = NULL;
  1185. } // end if
  1186. xfer->xferBool( &m_hideGarrisonedStateFromNonallies );
  1187. // garrison point data
  1188. UnsignedShort pointDataCount = MAX_GARRISON_POINTS;
  1189. xfer->xferUnsignedShort( &pointDataCount );
  1190. for( i = 0; i < pointDataCount; ++i )
  1191. {
  1192. if( xfer->getXferMode() == XFER_SAVE )
  1193. {
  1194. // object at this point
  1195. Object *obj = m_garrisonPointData[ i ].object;
  1196. ObjectID objectID = obj ? obj->getID() : INVALID_ID;
  1197. xfer->xferObjectID( &objectID );
  1198. // target
  1199. xfer->xferObjectID( &m_garrisonPointData[ i ].targetID );
  1200. // placement frame
  1201. xfer->xferUnsignedInt( &m_garrisonPointData[ i ].placeFrame );
  1202. // last effect frame
  1203. xfer->xferUnsignedInt( &m_garrisonPointData[ i ].lastEffectFrame );
  1204. // effect drawable id
  1205. Drawable *draw = m_garrisonPointData[ i ].effect;
  1206. DrawableID drawableID = draw ? draw->getID() : INVALID_DRAWABLE_ID;
  1207. xfer->xferDrawableID( &drawableID );
  1208. } // end if, save
  1209. else
  1210. {
  1211. // objectID
  1212. ObjectID objectID;
  1213. xfer->xferObjectID( &objectID );
  1214. // target
  1215. ObjectID targetID;
  1216. xfer->xferObjectID( &targetID );
  1217. // placement frame
  1218. UnsignedInt placeFrame;
  1219. xfer->xferUnsignedInt( &placeFrame );
  1220. // last effect frame
  1221. UnsignedInt lastEffectFrame;
  1222. xfer->xferUnsignedInt( &lastEffectFrame );
  1223. // effect drawable id
  1224. DrawableID drawableID;
  1225. xfer->xferDrawableID( &drawableID );
  1226. // store
  1227. if( i < MAX_GARRISON_POINTS )
  1228. {
  1229. m_garrisonPointData[ i ].objectID = objectID;
  1230. m_garrisonPointData[ i ].targetID = targetID;
  1231. m_garrisonPointData[ i ].placeFrame = placeFrame;
  1232. m_garrisonPointData[ i ].lastEffectFrame = lastEffectFrame;
  1233. m_garrisonPointData[ i ].effectID = drawableID;
  1234. } // end if
  1235. } // end else, load
  1236. } // end for i
  1237. // garrison points in use
  1238. xfer->xferInt( &m_garrisonPointsInUse );
  1239. // garrison points
  1240. xfer->xferUser( m_garrisonPoint, sizeof( Coord3D ) * MAX_GARRISON_POINT_CONDITIONS * MAX_GARRISON_POINTS );
  1241. // garrison points initialized
  1242. xfer->xferBool( &m_garrisonPointsInitialized );
  1243. // rally valid
  1244. xfer->xferBool( &m_rallyValid );
  1245. // exit rally point
  1246. xfer->xferCoord3D( &m_exitRallyPoint );
  1247. } // end xfer
  1248. // ------------------------------------------------------------------------------------------------
  1249. /** Load post process */
  1250. // ------------------------------------------------------------------------------------------------
  1251. void GarrisonContain::loadPostProcess( void )
  1252. {
  1253. // extend base class
  1254. OpenContain::loadPostProcess();
  1255. // connect up pointers needed
  1256. for( Int i = 0; i < MAX_GARRISON_POINTS; i++ )
  1257. {
  1258. // object pointer
  1259. if( m_garrisonPointData[ i ].objectID != INVALID_ID )
  1260. {
  1261. m_garrisonPointData[ i ].object = TheGameLogic->findObjectByID( m_garrisonPointData[ i ].objectID );
  1262. if( m_garrisonPointData[ i ].object == NULL )
  1263. {
  1264. DEBUG_CRASH(( "GarrisonContain::loadPostProcess - Unable to find object for point data\n" ));
  1265. throw SC_INVALID_DATA;
  1266. } // end if
  1267. } // end if
  1268. else
  1269. m_garrisonPointData[ i ].object = NULL;
  1270. // drawable effect pointer
  1271. if( m_garrisonPointData[ i ].effectID != INVALID_ID )
  1272. {
  1273. m_garrisonPointData[ i ].effect = TheGameClient->findDrawableByID( m_garrisonPointData[ i ].effectID );
  1274. if( m_garrisonPointData[ i ].effect == NULL )
  1275. {
  1276. DEBUG_CRASH(( "GarrisonContain::loadPostProcess - Unable to find effect for point data\n" ));
  1277. throw SC_INVALID_DATA;
  1278. } // end if
  1279. } // end if
  1280. else
  1281. m_garrisonPointData[ i ].effect = NULL;
  1282. } // end for i
  1283. } // end loadPostProcess