GarrisonContain.cpp 69 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: 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_isEnclosingContainer = TRUE; ///< a sensible default for a garrison container... few exceptions, firebase is one
  70. m_initialRoster.count = 0;
  71. } // end if
  72. //-----------------------------------------------------------------------------
  73. inline Real calcDistSqr(const Coord3D& a, const Coord3D& b)
  74. {
  75. return sqr(a.x - b.x) + sqr(a.y - b.y) + sqr(a.z - b.z);
  76. }
  77. // ------------------------------------------------------------------------------------------------
  78. /** Given the target position, find the garrison point that is closest to it */
  79. // ------------------------------------------------------------------------------------------------
  80. Int GarrisonContain::findClosestFreeGarrisonPointIndex( Int conditionIndex,
  81. const Coord3D *targetPos )
  82. {
  83. DEBUG_ASSERTCRASH(m_garrisonPointsInitialized, ("garrisonPoints are not inited"));
  84. // sanity
  85. if( targetPos == NULL || m_garrisonPointsInUse == MAX_GARRISON_POINTS )
  86. return GARRISON_INDEX_INVALID;
  87. Int closestIndex = GARRISON_INDEX_INVALID;
  88. Real closestDistSq = -1.0f;
  89. Real distSq;
  90. for( Int i = 0; i < MAX_GARRISON_POINTS; ++i )
  91. {
  92. // only consider free garrison points
  93. if( m_garrisonPointData[ i ].object == NULL )
  94. {
  95. // compute the squared distance between these two points
  96. distSq = calcDistSqr(*targetPos, m_garrisonPoint[ conditionIndex ][ i ]);
  97. if( distSq < closestDistSq || closestDistSq == -1.0f )
  98. {
  99. closestDistSq = distSq;
  100. closestIndex = i;
  101. } // end if
  102. } // end if
  103. } // end for i
  104. return closestIndex;
  105. } // end findClosestFreeGarrisonPointIndex
  106. // ------------------------------------------------------------------------------------------------
  107. /** Given the object, return the garrison point index the object is placed at ... if any */
  108. // ------------------------------------------------------------------------------------------------
  109. Int GarrisonContain::getObjectGarrisonPointIndex( Object *obj )
  110. {
  111. // sanity
  112. if( obj == NULL )
  113. return GARRISON_INDEX_INVALID;
  114. for( Int i = 0; i < MAX_GARRISON_POINTS; ++i )
  115. if( m_garrisonPointData[ i ].object == obj )
  116. return i;
  117. return GARRISON_INDEX_INVALID;
  118. } // end getObjectGarrisonPointIndex
  119. // ------------------------------------------------------------------------------------------------
  120. /** Put the object at the specified garrison point by index */
  121. // ------------------------------------------------------------------------------------------------
  122. void GarrisonContain::putObjectAtGarrisonPoint( Object *obj,
  123. ObjectID targetID,
  124. Int conditionIndex,
  125. Int pointIndex )
  126. {
  127. DEBUG_ASSERTCRASH(m_garrisonPointsInitialized, ("garrisonPoints are not inited"));
  128. // sanity
  129. if( obj == NULL || pointIndex < 0 || pointIndex >= MAX_GARRISON_POINTS ||
  130. conditionIndex < 0 || conditionIndex >= MAX_GARRISON_POINT_CONDITIONS )
  131. {
  132. DEBUG_CRASH(( "GarrisionContain::putObjectAtGarrisionPoint - Invalid arguments\n" ));
  133. return;
  134. } // end if
  135. // make sure this point is empty
  136. if( m_garrisonPointData[ pointIndex ].object != NULL )
  137. {
  138. DEBUG_CRASH(( "GarrisonContain::putObjectAtGarrisonPoint - Garrison Point '%d' is not empty\n",
  139. pointIndex ));
  140. return;
  141. } // end if
  142. // get the position we're going to use
  143. Coord3D pos = m_garrisonPoint[ conditionIndex ][ pointIndex ];
  144. // set the object position
  145. obj->setPosition( &pos );
  146. // save the data for being place at this point
  147. m_garrisonPointData[ pointIndex ].object = obj;
  148. m_garrisonPointData[ pointIndex ].targetID = targetID;
  149. m_garrisonPointData[ pointIndex ].placeFrame = TheGameLogic->getFrame();
  150. ++m_garrisonPointsInUse;
  151. //
  152. // create a drawable that has a gun barrel which will show there is an object at this
  153. // garrison point ready to shoot
  154. //
  155. static const ThingTemplate *muzzle = TheThingFactory->findTemplate( "GarrisonGun" );
  156. DEBUG_ASSERTCRASH( muzzle, ("Warning, Object 'GarrisonGun' not found and is need for Garrison gun effects\n") );
  157. if( muzzle && isEnclosingContainerFor( obj ) )// If we are showing the contained, we need no gun barrel drawable added
  158. {
  159. Drawable *draw = TheThingFactory->newDrawable( muzzle );
  160. if( draw )
  161. {
  162. // set position of the drawable at the garrison fire point
  163. draw->setPosition( &pos );
  164. // record the drawable in our data array
  165. m_garrisonPointData[ pointIndex ].effect = draw;
  166. m_garrisonPointData[ pointIndex ].lastEffectFrame = 0;
  167. //Copy shroud status from our container.
  168. Drawable *containerDrawable=getObject()->getDrawable();
  169. if (containerDrawable)
  170. draw->setFullyObscuredByShroud(containerDrawable->getFullyObscuredByShroud());
  171. } // end if
  172. } // end if
  173. /*
  174. UnicodeString msg;
  175. msg.format( L"Added object '%S'(%d) to point '%d'",
  176. obj->getTemplate()->getName().str(),
  177. obj->getID(),
  178. pointIndex );
  179. TheInGameUI->message( msg );
  180. */
  181. } // end putObjectAtGarrisonPoint
  182. // ------------------------------------------------------------------------------------------------
  183. /** Given the current state of the structure, return the condition index we are to use
  184. * from the garrison point position arrays */
  185. // ------------------------------------------------------------------------------------------------
  186. Int GarrisonContain::findConditionIndex( void )
  187. {
  188. BodyModuleInterface *body = getObject()->getBodyModule();
  189. BodyDamageType bodyDamage = body->getDamageState();
  190. Int index = GARRISON_INDEX_INVALID;
  191. switch( bodyDamage )
  192. {
  193. // --------------------------------------------------------------------------------------------
  194. case BODY_PRISTINE:
  195. index = GARRISON_POINT_PRISTINE;
  196. break;
  197. // --------------------------------------------------------------------------------------------
  198. case BODY_DAMAGED:
  199. index = GARRISON_POINT_DAMAGED;
  200. break;
  201. // --------------------------------------------------------------------------------------------
  202. case BODY_REALLYDAMAGED:
  203. case BODY_RUBBLE:
  204. index = GARRISON_POINT_REALLY_DAMAGED;
  205. break;
  206. // --------------------------------------------------------------------------------------------
  207. default:
  208. DEBUG_CRASH(( "GarrisonContain::findConditionIndex - Unknown body damage type '%d'\n",
  209. bodyDamage ));
  210. break;
  211. } // end switch
  212. return index;
  213. } // end findConditionIndex
  214. //-------------------------------------------------------------------------------------------------
  215. //The weapon system would like to perform a range check assuming the object is placed in the
  216. //best possible available garrison position. If found, we change sourcePos to that position.
  217. //-------------------------------------------------------------------------------------------------
  218. Bool GarrisonContain::calcBestGarrisonPosition( Coord3D *sourcePos, const Coord3D *targetPos )
  219. {
  220. // sanity
  221. if( !sourcePos || !targetPos )
  222. return FALSE;
  223. #if defined __DEBUG || defined _INTERNAL
  224. const GarrisonContainModuleData *modData = getGarrisonContainModuleData();
  225. DEBUG_ASSERTCRASH(modData->m_isEnclosingContainer, ("calcBestGarrisonPosition... SHOULD NOT GET HERE, since this container is non-enclosing") );
  226. #endif
  227. // find which garrison point position array we will used based on body condition
  228. Int conditionIndex = findConditionIndex();
  229. // get the index of the garrison point that is closest to the target position
  230. Int placeIndex = findClosestFreeGarrisonPointIndex( conditionIndex, targetPos );
  231. if( placeIndex == GARRISON_INDEX_INVALID )
  232. {
  233. DEBUG_CRASH( ("GarrisonContain::calcBestGarrisonPosition - Unable to find suitable garrison point.\n") );
  234. return FALSE;
  235. }
  236. sourcePos->set( &(m_garrisonPoint[ conditionIndex ][ placeIndex ]) );
  237. return TRUE;
  238. }
  239. //-------------------------------------------------------------------------------------------------
  240. //The AI is entering the aim state and would like to move the unit to the best position, perform
  241. //a range check, and if it succeeds, leave him there -- otherwise, remove him immediately.
  242. //-------------------------------------------------------------------------------------------------
  243. Bool GarrisonContain::attemptBestFirePointPosition( Object *source, Weapon *weapon, Object *victim )
  244. {
  245. //Sanity
  246. if( !source || !victim || !weapon )
  247. {
  248. return FALSE;
  249. }
  250. #if defined __DEBUG || defined _INTERNAL
  251. const GarrisonContainModuleData *modData = getGarrisonContainModuleData();
  252. DEBUG_ASSERTCRASH(modData->m_isEnclosingContainer, ("calcBestGarrisonPosition... SHOULD NOT GET HERE, since this container is non-enclosing") );
  253. #endif
  254. //If this object is already at a garrison point, remove him.
  255. Int existingIndex = getObjectGarrisonPointIndex( source );
  256. if( existingIndex != GARRISON_INDEX_INVALID )
  257. {
  258. removeObjectFromGarrisonPoint( source, existingIndex );
  259. }
  260. putObjectAtBestGarrisonPoint( source, victim, NULL );
  261. //Okay, now we have positioned the object in the best position for the victim.
  262. //Now check if we are able to fire on our victim.
  263. if( weapon->isWithinAttackRange( source, victim ) )
  264. {
  265. return TRUE;
  266. }
  267. //Crap, we failed... so remove the object from the garrison point.
  268. existingIndex = getObjectGarrisonPointIndex( source );
  269. if( existingIndex != GARRISON_INDEX_INVALID )
  270. {
  271. removeObjectFromGarrisonPoint( source, existingIndex );
  272. }
  273. return FALSE;
  274. }
  275. //-------------------------------------------------------------------------------------------------
  276. //The AI is entering the aim state and would like to move the unit to the best position, perform
  277. //a range check, and if it succeeds, leave him there -- otherwise, remove him immediately.
  278. //-------------------------------------------------------------------------------------------------
  279. Bool GarrisonContain::attemptBestFirePointPosition( Object *source, Weapon *weapon, const Coord3D *targetPos )
  280. {
  281. //Sanity
  282. if( !source || !targetPos || !weapon )
  283. {
  284. return FALSE;
  285. }
  286. #if defined __DEBUG || defined _INTERNAL
  287. const GarrisonContainModuleData *modData = getGarrisonContainModuleData();
  288. DEBUG_ASSERTCRASH(modData->m_isEnclosingContainer, ("calcBestGarrisonPosition... SHOULD NOT GET HERE, since this container is non-enclosing") );
  289. #endif
  290. //If this object is already at a garrison point, remove him.
  291. Int existingIndex = getObjectGarrisonPointIndex( source );
  292. if( existingIndex != GARRISON_INDEX_INVALID )
  293. {
  294. removeObjectFromGarrisonPoint( source, existingIndex );
  295. }
  296. putObjectAtBestGarrisonPoint( source, NULL, targetPos );
  297. //Okay, now we have positioned the object in the best position for the targetPos.
  298. //Now check if we are able to fire on our targetPos.
  299. if( weapon->isWithinAttackRange( source, targetPos ) )
  300. {
  301. return TRUE;
  302. }
  303. //Crap, we failed... so remove the object from the garrison point.
  304. existingIndex = getObjectGarrisonPointIndex( source );
  305. if( existingIndex != GARRISON_INDEX_INVALID )
  306. {
  307. removeObjectFromGarrisonPoint( source, existingIndex );
  308. }
  309. return FALSE;
  310. }
  311. //-------------------------------------------------------------------------------------------------
  312. /** Place the object at the "best" garrison point position so it's on the same "side" of
  313. * the structure that its target is */
  314. //-------------------------------------------------------------------------------------------------
  315. void GarrisonContain::putObjectAtBestGarrisonPoint( Object *obj, Object *target, const Coord3D *targetPos )
  316. {
  317. // sanity
  318. if( obj == NULL || (target == NULL && targetPos == NULL) )
  319. return;
  320. #if defined __DEBUG || defined _INTERNAL
  321. const GarrisonContainModuleData *modData = getGarrisonContainModuleData();
  322. DEBUG_ASSERTCRASH(modData->m_isEnclosingContainer, ("calcBestGarrisonPosition... SHOULD NOT GET HERE, since this container is non-enclosing") );
  323. #endif
  324. // if obj target, override pos
  325. if (target != NULL)
  326. targetPos = target->getPosition();
  327. // if this object is already at a garrison point do nothing
  328. if( getObjectGarrisonPointIndex( obj ) != GARRISON_INDEX_INVALID )
  329. return;
  330. // find which garrison point position array we will used based on body condition
  331. Int conditionIndex = findConditionIndex();
  332. // get the index of the garrison point that is closest to the target position
  333. Int placeIndex = findClosestFreeGarrisonPointIndex( conditionIndex, targetPos );
  334. DEBUG_ASSERTCRASH( placeIndex != GARRISON_INDEX_INVALID,
  335. ("GarrisonContain::putObjectAtBestGarrisonPoint - Unable to find suitable garrison point for '%s'\n",
  336. obj->getTemplate()->getName().str()) );
  337. // put it here
  338. putObjectAtGarrisonPoint( obj, target ? target->getID() : INVALID_ID, conditionIndex, placeIndex );
  339. } // end putObjectAtBestGarrisonPoint
  340. // ------------------------------------------------------------------------------------------------
  341. /** Remove the object from the garrison point position and replace at the center of the building */
  342. // ------------------------------------------------------------------------------------------------
  343. void GarrisonContain::removeObjectFromGarrisonPoint( Object *obj, Int index )
  344. {
  345. if ( ! isEnclosingContainerFor(obj) )
  346. return;// since I am not enclosed, I am not at a garrison point!
  347. #if defined __DEBUG || defined _INTERNAL
  348. const GarrisonContainModuleData *modData = getGarrisonContainModuleData();
  349. DEBUG_ASSERTCRASH(modData->m_isEnclosingContainer, ("calcBestGarrisonPosition... SHOULD NOT GET HERE, since this container is non-enclosing") );
  350. #endif
  351. // sanity
  352. if( obj == NULL )
  353. return;
  354. // search for the object in the garrison point data, if found, remove it
  355. Int removeIndex = index;
  356. if( removeIndex == SEARCH_FOR_REMOVE )
  357. {
  358. for( Int i = 0; i < MAX_GARRISON_POINTS; ++i )
  359. {
  360. if( m_garrisonPointData[ i ].object == obj )
  361. {
  362. removeIndex = i;
  363. break;
  364. } // end if
  365. } // end for i
  366. } // end if
  367. // validate the index slot to remove
  368. if( removeIndex < 0 || removeIndex >= MAX_GARRISON_POINTS )
  369. {
  370. //
  371. // this is not an error, if a search was ordered, we may very well not find the
  372. // object at a garrison point and therefore can't remove it
  373. //
  374. return;
  375. } // end if
  376. // remove from this spot
  377. m_garrisonPointData[ removeIndex ].object = NULL;
  378. m_garrisonPointData[ removeIndex ].targetID = INVALID_ID;
  379. m_garrisonPointData[ removeIndex ].placeFrame = 0;
  380. m_garrisonPointData[ removeIndex ].lastEffectFrame = 0;
  381. --m_garrisonPointsInUse;
  382. // destroy drawable for gun barrel and effects if present
  383. if( m_garrisonPointData[ removeIndex ].effect )
  384. TheGameClient->destroyDrawable( m_garrisonPointData[ removeIndex ].effect );
  385. m_garrisonPointData[ removeIndex ].effect = NULL;
  386. // set the position of the object to back to the center of the garrisoned building
  387. obj->setPosition( getObject()->getPosition() );
  388. /*
  389. UnicodeString msg;
  390. msg.format( L"Removed object '%S'(%d) from point '%d'",
  391. obj->getTemplate()->getName().str(),
  392. obj->getID(),
  393. removeIndex );
  394. TheInGameUI->message( msg );
  395. */
  396. } // end removeObjectFromGarrisonPoint
  397. //-------------------------------------------------------------------------------------------------
  398. //-------------------------------------------------------------------------------------------------
  399. GarrisonContain::GarrisonContain( Thing *thing, const ModuleData *moduleData ) :
  400. OpenContain( thing, moduleData )
  401. {
  402. Int i, j;
  403. m_originalTeam = NULL;
  404. m_hideGarrisonedStateFromNonallies = FALSE;
  405. m_garrisonPointsInUse = 0;
  406. m_garrisonPointsInitialized = FALSE;
  407. m_stationGarrisonPointsInitialized = FALSE;
  408. for( i = 0; i < MAX_GARRISON_POINTS; i++ )
  409. {
  410. m_garrisonPointData[ i ].object = NULL;
  411. m_garrisonPointData[ i ].targetID = INVALID_ID;
  412. m_garrisonPointData[ i ].placeFrame = 0;
  413. m_garrisonPointData[ i ].lastEffectFrame = 0;
  414. m_garrisonPointData[ i ].effect = NULL;
  415. for( j = 0; j < MAX_GARRISON_POINT_CONDITIONS; ++j )
  416. m_garrisonPoint[ j ][ i ].zero();
  417. } // end for i
  418. m_rallyValid = FALSE;
  419. m_exitRallyPoint.zero();
  420. m_evacDisposition = EVAC_BURST_FROM_CENTER; // default, anyway
  421. m_stationPointList.clear();
  422. } // end GarrisonContain
  423. //-------------------------------------------------------------------------------------------------
  424. //-------------------------------------------------------------------------------------------------
  425. GarrisonContain::~GarrisonContain( void )
  426. {
  427. } // end ~GarrisonContain
  428. //-------------------------------------------------------------------------------------------------
  429. /** can this container contain this kind of object?
  430. and, if checkCapacity is TRUE, does this container have enough space
  431. left to hold the given unit? */
  432. // ------------------------------------------------------------------------------------------------
  433. Bool GarrisonContain::isValidContainerFor(const Object* obj, Bool checkCapacity) const
  434. {
  435. // extend functionality // this just tests kindof masks
  436. if( OpenContain::isValidContainerFor( obj, checkCapacity ) == false )
  437. return false;
  438. // zero-health buildings are not garrisonable.
  439. if (getObject()->getBodyModule()->getHealth() <= 0.0f)
  440. return false;
  441. // ReallyDamaged buildings are not garrisonable as well.
  442. if( getObject()->getBodyModule()->getDamageState() == BODY_REALLYDAMAGED && !getObject()->isKindOf( KINDOF_GARRISONABLE_UNTIL_DESTROYED ) )
  443. return false;
  444. if (obj && !obj->isKindOf(KINDOF_NO_GARRISON))
  445. {
  446. if (checkCapacity)
  447. {
  448. Int garrisonMax = getContainMax();
  449. Int containCount = getContainCount();
  450. return ( containCount < garrisonMax );
  451. }
  452. else
  453. {
  454. return true;
  455. }
  456. }
  457. return false;
  458. }
  459. // ------------------------------------------------------------------------------------------------
  460. /** Any objects that are sitting at the garrison points which no longer have targets need
  461. * to be moved to the center of the building and taken off the garrison point */
  462. // ------------------------------------------------------------------------------------------------
  463. void GarrisonContain::removeInvalidObjectsFromGarrisonPoints( void )
  464. {
  465. #if defined __DEBUG || defined _INTERNAL
  466. const GarrisonContainModuleData *modData = getGarrisonContainModuleData();
  467. DEBUG_ASSERTCRASH(modData->m_isEnclosingContainer, ("removeinvalidobjFromGarrisonPoint... SHOULD NOT GET HERE, since this container is non-enclosing") );
  468. #endif
  469. Object *obj;
  470. if (m_garrisonPointsInUse == 0)
  471. return; // my, that was easy
  472. for( Int i = 0; i < MAX_GARRISON_POINTS; ++i )
  473. {
  474. obj = m_garrisonPointData[ i ].object;
  475. if( obj )
  476. {
  477. AIUpdateInterface *ai = obj->getAIUpdateInterface();
  478. Bool targetIsValid = true; // assume true for now...
  479. Object *goalObject = ai->getGoalObject();
  480. if( goalObject )
  481. {
  482. Weapon *weapon = obj->getCurrentWeapon();
  483. if( !weapon || !weapon->isWithinAttackRange( obj, goalObject ) )
  484. {
  485. //As a garrisoned member, if our target is out of range,
  486. //then get out of the space, because someone else might
  487. //be able to shoot it.
  488. targetIsValid = false;
  489. }
  490. }
  491. // note that we can be attacking a position, rather than an object...
  492. if( !obj->testStatus(OBJECT_STATUS_IS_ATTACKING) || !targetIsValid )
  493. {
  494. removeObjectFromGarrisonPoint( obj, i );
  495. }
  496. } // end if
  497. } // end for i
  498. } // end removeInvalidObjectsFromGarrisonPoints
  499. // ------------------------------------------------------------------------------------------------
  500. /** Are there any objects in the center that have now obtained targets and need to move to
  501. * a garrison point */
  502. // ------------------------------------------------------------------------------------------------
  503. void GarrisonContain::addValidObjectsToGarrisonPoints( void )
  504. {
  505. #if defined __DEBUG || defined _INTERNAL
  506. const GarrisonContainModuleData *modData = getGarrisonContainModuleData();
  507. DEBUG_ASSERTCRASH(modData->m_isEnclosingContainer, ("addvalidobjtoGarrisonPoint... SHOULD NOT GET HERE, since this container is non-enclosing") );
  508. #endif
  509. const ContainedItemsList& containList = getContainList();
  510. if (containList.empty())
  511. return;
  512. for( ContainedItemsList::const_iterator it = containList.begin(); it != containList.end(); ++it )
  513. {
  514. Object* obj = *it;
  515. AIUpdateInterface* ai = obj->getAIUpdateInterface();
  516. if( ai )
  517. {
  518. Object *victim = ai->getCurrentVictim();
  519. const Coord3D *victimPos = ai->getCurrentVictimPos();
  520. //
  521. // add this object to the garrison point that is closest to its target if it's not
  522. // already in there
  523. //
  524. if( victim )
  525. putObjectAtBestGarrisonPoint( obj, victim, NULL );
  526. else if( victimPos )
  527. putObjectAtBestGarrisonPoint( obj, NULL, victimPos );
  528. } // end if
  529. } // end for it
  530. } // end addValidObjectsToGarrisonPoints
  531. // ------------------------------------------------------------------------------------------------
  532. /** Every frame this method is called. It keeps any of the attacking units at any of the
  533. * fire points closest to their active target and shuffles them around to any open garrison
  534. * points that are available if they are closer. We will also track our targets position
  535. * and orient any effect stuff we need to (gun barrel / muzzle flash) */
  536. // ------------------------------------------------------------------------------------------------
  537. void GarrisonContain::trackTargets( void )
  538. {
  539. if ( ! isEnclosingContainerFor( 0 ) )
  540. return; // since ina non-enclosing container, objects fire from their station points, instead of being juggled around between garrison firepoints
  541. Int conditionIndex = findConditionIndex();
  542. const ContainedItemsList& containList = getContainList();
  543. AIUpdateInterface *ai;
  544. Object *obj;
  545. for( ContainedItemsList::const_iterator it = containList.begin(); it != containList.end(); ++it )
  546. {
  547. DEBUG_ASSERTCRASH(m_garrisonPointsInitialized, ("garrisonPoints are not inited"));
  548. // get the object
  549. obj = *it;
  550. // only consider objects that are actually at garrison points for re-shuffling
  551. Int ourIndex = getObjectGarrisonPointIndex( obj );
  552. if( ourIndex != GARRISON_INDEX_INVALID )
  553. {
  554. // does this object have a target?
  555. ai = obj->getAIUpdateInterface();
  556. if( ai )
  557. {
  558. Object *victim = ai->getCurrentVictim();
  559. // even though the target position can't change in some cases, still must do this code at least once.
  560. const Coord3D *victimPos = ai->getCurrentVictimPos();
  561. if( victim || victimPos )
  562. {
  563. if (victim)
  564. victimPos = victim->getPosition();
  565. const Coord3D *ourPos = obj->getPosition();
  566. // find the closest free (of all remaining) garrison points to our target
  567. Int newIndex = findClosestFreeGarrisonPointIndex( conditionIndex,
  568. victimPos );
  569. // if unable to find another garrison point, don't bother
  570. if( newIndex != GARRISON_INDEX_INVALID )
  571. {
  572. // get the distance from our current index to the target
  573. Real currentDistSq = calcDistSqr(*victimPos, *ourPos );
  574. // get the distance from the newly chosen index
  575. Real newDistSq = calcDistSqr(*victimPos, m_garrisonPoint[ conditionIndex ][ newIndex ] );
  576. // if the newly chosen index is closer than our current index, switch
  577. if( newDistSq < currentDistSq )
  578. {
  579. // remove from the old index
  580. removeObjectFromGarrisonPoint( obj, ourIndex );
  581. // place at the new index
  582. putObjectAtGarrisonPoint( obj, victim ? victim->getID() : INVALID_ID, conditionIndex, newIndex );
  583. } // end if, new index is closer
  584. } // end if, possible closer index was found
  585. //
  586. // we are now either at a new garrison fire point, or we have remained at our
  587. // existing point still tracking our target. Orient the effect drawable which
  588. // shows the gun barrel and muzzle flash towards our target position
  589. //
  590. if( m_garrisonPointData[ ourIndex ].effect )
  591. {
  592. Coord2D v;
  593. v.x = victimPos->x - ourPos->x;
  594. v.y = victimPos->y - ourPos->y;
  595. // v.z = victomPos->z - ourPos.z;
  596. // orient the effect object towards the victim position
  597. m_garrisonPointData[ ourIndex ].effect->setOrientation( v.toAngle() );
  598. } // end if
  599. } // end if, victim present
  600. } // end if, ai
  601. } // end if, we're at a garrison point
  602. } // end for it
  603. } // end trackTargets
  604. // ------------------------------------------------------------------------------------------------
  605. /** Remove all the objects at garrison points back to the center and redeploy them among the
  606. * garrison points. NOTE that we are preserving the frame in which the object was put
  607. * at the garrison point originally as this method is used when the model condition changes
  608. * which could shuffle the garrison point positions but that shouldn't logically change
  609. * when an object was placed at the point */
  610. // ------------------------------------------------------------------------------------------------
  611. void GarrisonContain::redeployOccupants( void )
  612. {
  613. GarrisonPointData garrisonPointDataCopy[ MAX_GARRISON_POINTS ];
  614. Int i;
  615. // copy the current set of garrison point data sets
  616. for( i = 0; i < MAX_GARRISON_POINTS; ++i )
  617. garrisonPointDataCopy[ i ] = m_garrisonPointData[ i ];
  618. // Lorenzen changed, 6/11/03, so that garrisoncontains that are not enclosing will keep units at their assigned stations,
  619. // rather than Bamphing them all over the building as they fire.
  620. // // remove the occupants
  621. // removeInvalidObjectsFromGarrisonPoints();
  622. // // redeploy them
  623. // addValidObjectsToGarrisonPoints();
  624. // ATTENTION... setting this false allows each redeployOccupants() call to create fresh station points, based on the new transform
  625. // if anything wierd ever happens, like rotating buildings and such, we will need a way of transforming the points without clearing the
  626. // list (and thus forgetting where everyone contained was stationed)... just a handy reminder.
  627. m_stationGarrisonPointsInitialized = FALSE;
  628. matchObjectsToGarrisonPoints();
  629. // restore the frame markers that things were recorded as entering their point
  630. Int index;
  631. for( i = 0; i < MAX_GARRISON_POINTS; ++i )
  632. {
  633. if( garrisonPointDataCopy[ i ].object )
  634. {
  635. // where was this object redeployed
  636. index = getObjectGarrisonPointIndex( garrisonPointDataCopy[ i ].object );
  637. if( index != GARRISON_INDEX_INVALID )
  638. m_garrisonPointData[ index ].placeFrame = garrisonPointDataCopy[ i ].placeFrame;
  639. } // end if
  640. } // end for i
  641. } // end redeployOccupants
  642. // ------------------------------------------------------------------------------------------------
  643. /** Do any effects during an update cycle that we need to */
  644. // ------------------------------------------------------------------------------------------------
  645. void GarrisonContain::updateEffects( void )
  646. {
  647. #if defined __DEBUG || defined _INTERNAL
  648. const GarrisonContainModuleData *modData = getGarrisonContainModuleData();
  649. DEBUG_ASSERTCRASH(modData->m_isEnclosingContainer, ("updateeffects... SHOULD NOT GET HERE, since this container is non-enclosing") );
  650. #endif
  651. UnsignedInt currentFrame = TheGameLogic->getFrame();
  652. const ContainedItemsList& containList = getContainList();
  653. for( ContainedItemsList::const_iterator it = containList.begin(); it != containList.end(); ++it )
  654. {
  655. Object *obj;
  656. // get the object
  657. obj = *it;
  658. //
  659. // did the object fire last frame, if so make a muzzle flash if needed at the
  660. // garrison point
  661. //
  662. if( obj->getLastShotFiredFrame() == currentFrame - 1 )
  663. {
  664. Int garrisonIndex = getObjectGarrisonPointIndex( obj );
  665. // only consider doing muzzle flash logic if the object is actually at a garrison point
  666. if( garrisonIndex != GARRISON_INDEX_INVALID )
  667. {
  668. // set the model condition for the effect object to show the muzzle flash
  669. Drawable *effect = m_garrisonPointData[ garrisonIndex ].effect;
  670. if( effect )
  671. {
  672. const Weapon *passengerWeapon = obj->getCurrentWeapon();
  673. if( passengerWeapon && passengerWeapon->getDamageType() != DAMAGE_POISON )// No muzzle flash with poison weapon
  674. {
  675. // set the model condition
  676. effect->setModelConditionState( MODELCONDITION_FIRING_A );
  677. // mark this "fire frame" so we can turn it off in a little while
  678. m_garrisonPointData[ garrisonIndex ].lastEffectFrame = currentFrame;
  679. }
  680. } // end if
  681. } // end if, object is at garrision point
  682. } // end if, object shot last frame
  683. } // end for containment iterator
  684. // remove any firing effects for time that has passed
  685. for( Int i = 0; i < MAX_GARRISON_POINTS; ++i )
  686. {
  687. if( m_garrisonPointData[ i ].effect &&
  688. m_garrisonPointData[ i ].lastEffectFrame != 0 &&
  689. currentFrame - m_garrisonPointData[ i ].lastEffectFrame > MUZZLE_FLASH_LIFETIME )
  690. {
  691. // clear the model condition
  692. m_garrisonPointData[ i ].effect->clearModelConditionState( MODELCONDITION_FIRING_A );
  693. // clear the last effect frame
  694. m_garrisonPointData[ i ].lastEffectFrame = 0;
  695. } // end if
  696. } // end for i
  697. } // end updateEffects
  698. //-------------------------------------------------------------------------------------------------
  699. //-------------------------------------------------------------------------------------------------
  700. UpdateSleepTime GarrisonContain::update( void )
  701. {
  702. const GarrisonContainModuleData *modData = getGarrisonContainModuleData();
  703. // extend functionality
  704. UpdateSleepTime result;
  705. result = OpenContain::update();
  706. // remove effectively dead objects from this garrison container
  707. const ContainedItemsList& containList = getContainList();
  708. Object *contained;
  709. for( ContainedItemsList::const_iterator it = containList.begin(); it != containList.end(); /*empty*/ )
  710. {
  711. // get object
  712. contained = *it;
  713. // increment iterator, we may delete the object
  714. ++it;
  715. // remove if dead
  716. if( contained->isEffectivelyDead() )
  717. {
  718. // remove from container
  719. removeFromContain( contained );
  720. // set the safe occlusion frame to way way way in the future so we never see it during death
  721. #define HUGE_FRAME_IN_FUTURE (LOGICFRAMES_PER_SECOND * 1000)
  722. contained->setSafeOcclusionFrame( TheGameLogic->getFrame() + HUGE_FRAME_IN_FUTURE );
  723. } // end if
  724. } // end for, it
  725. // Lorenzen changed, 6/11/03, so that garrisoncontains that are not enclosing will keep units at their assigned stations,
  726. // rather than Bamphing them all over the building as they fire.
  727. // // are there any objects at the garrison points who now need to go back to the center of the structure
  728. // removeInvalidObjectsFromGarrisonPoints();
  729. //
  730. // //
  731. // // are there any objects in the center that have now obtained targets and need to move to
  732. // // a garrison point
  733. // //
  734. // addValidObjectsToGarrisonPoints();
  735. matchObjectsToGarrisonPoints();
  736. healObjects();
  737. if (modData->m_mobileGarrison && (getObject()->isMobile() == TRUE) )
  738. {
  739. moveObjectsWithMe();
  740. }
  741. else
  742. {
  743. // sanity information
  744. DEBUG_ASSERTCRASH( getObject()->isMobile() == FALSE,
  745. ("GarrisonContain::update - Objects with garrison contain can be spec'd as 'mobile' in the INI. Do you really want to do this? \n") );
  746. }
  747. return UPDATE_SLEEP_NONE;
  748. } // end update
  749. //-------------------------------------------------------------------------------------------------
  750. /** Every frame, and whenever anyone enters or leaves */
  751. // ------------------------------------------------------------------------------------------------
  752. void GarrisonContain::matchObjectsToGarrisonPoints( void )
  753. {
  754. if ( isEnclosingContainerFor( NULL ) == FALSE )
  755. {
  756. // enforce that everybody stays at their pre-assigned space
  757. positionObjectsAtStationGarrisonPoints();
  758. }
  759. else
  760. {
  761. // are there any objects at the garrison points who now need to go back to the center of the structure
  762. removeInvalidObjectsFromGarrisonPoints();
  763. // are there any objects in the center that have now obtained targets and need to move to
  764. // a garrison point
  765. addValidObjectsToGarrisonPoints();
  766. // any units that have just fired need to have a muzzle flash display out of the fire point
  767. updateEffects();
  768. // given all the objects that are at the garrison points shooting at something, if their
  769. // target moves around the structure and closer to another open garrison point we want
  770. // to shuffle our object to the new closest garrison point. We'll also track the target
  771. // here and set orientation for any effects we need to
  772. trackTargets();
  773. }
  774. }
  775. //-------------------------------------------------------------------------------------------------
  776. /** enforce that everybody stays at their pre-assigned space */
  777. // ------------------------------------------------------------------------------------------------
  778. void GarrisonContain::positionObjectsAtStationGarrisonPoints()
  779. {
  780. if ( ! m_stationGarrisonPointsInitialized )
  781. {
  782. loadStationGarrisonPoints();
  783. }
  784. const ContainedItemsList& containList = getContainList();
  785. Object *contained;
  786. for( ContainedItemsList::const_iterator it = containList.begin(); it != containList.end(); ++it )
  787. {
  788. contained = *it;
  789. Bool foundHisSpot = FALSE;
  790. // now lets find him in our station point list, and make sure he stays put there.
  791. for( std::vector<StationPointData>::const_iterator pt = m_stationPointList.begin();
  792. pt != m_stationPointList.end();
  793. ++pt)
  794. {
  795. const StationPointData *spd = &*pt;
  796. if( spd->occupantID == contained->getID() )
  797. {
  798. contained->setPosition( &spd->position );
  799. foundHisSpot = TRUE;
  800. break;
  801. }
  802. }
  803. if ( ! foundHisSpot && ! pickAStationForMe( contained ))
  804. {
  805. DEBUG_ASSERTCRASH( foundHisSpot, ("GarrisonContain::positionObjectsAtStationGarrisonPoints found something terribly wrong... \nthere is either a station point shortage, or some other bug."));
  806. }
  807. } // end for, it
  808. }
  809. //-------------------------------------------------------------------------------------------------
  810. /** When a new guy enters a non-enclosing garrison container */
  811. // ------------------------------------------------------------------------------------------------
  812. Bool GarrisonContain::pickAStationForMe( const Object *obj )
  813. {
  814. Bool foundVacancy = FALSE;
  815. for( std::vector<StationPointData>::iterator pt = m_stationPointList.begin(); pt != m_stationPointList.end(); ++pt)
  816. {
  817. StationPointData *spd = &*pt; // non const
  818. if ( spd->occupantID == INVALID_ID ) // found a vacancy
  819. {
  820. spd->occupantID = obj->getID();
  821. foundVacancy = TRUE;
  822. return TRUE;
  823. }
  824. }
  825. DEBUG_ASSERTCRASH(foundVacancy, ("GarrisonContain::pickAStationForMe is all kinds of bad... \n there was no vacancy found for a newly contained object."));
  826. return FALSE;
  827. }
  828. void GarrisonContain::removeObjectFromStationPoint( const Object *obj )
  829. {
  830. //sanity
  831. if ( obj == NULL )
  832. return;
  833. Bool foundOccupant = FALSE;
  834. for( std::vector<StationPointData>::iterator pt = m_stationPointList.begin(); pt != m_stationPointList.end(); ++pt)
  835. {
  836. StationPointData *spd = &*pt; // non const
  837. if ( spd->occupantID == obj->getID() ) // found him sitting there
  838. {
  839. spd->occupantID = INVALID_ID;// give up your space
  840. foundOccupant = TRUE;
  841. return;
  842. }
  843. }
  844. DEBUG_ASSERTCRASH(foundOccupant, ("GarrisonContain::removeObjectFromStationPoint is all kinds of bad... \n the contained object was not found in station point list."));
  845. }
  846. //-------------------------------------------------------------------------------------------------
  847. /** When I become damaged */
  848. // ------------------------------------------------------------------------------------------------
  849. void GarrisonContain::onDamage( DamageInfo * /*info*/ )
  850. {
  851. // const ContainedItemsList& containList = getContainList();
  852. // for( ContainedItemsList::const_iterator it = containList.begin(); it != containList.end(); ++it )
  853. // {
  854. // Object *obj;
  855. //
  856. // // get the object
  857. // obj = *it;
  858. //
  859. // healSingleObject(obj, modData->m_framesForFullHeal);
  860. // }
  861. }
  862. //-------------------------------------------------------------------------------------------------
  863. //-------------------------------------------------------------------------------------------------
  864. void GarrisonContain::healObjects( void )
  865. {
  866. const GarrisonContainModuleData *modData = getGarrisonContainModuleData();
  867. if (!modData->m_doIHealObjects)
  868. return;
  869. const ContainedItemsList& containList = getContainList();
  870. for( ContainedItemsList::const_iterator it = containList.begin(); it != containList.end(); ++it )
  871. {
  872. Object *obj;
  873. // get the object
  874. obj = *it;
  875. healSingleObject(obj, modData->m_framesForFullHeal);
  876. }
  877. }
  878. //-------------------------------------------------------------------------------------------------
  879. //-------------------------------------------------------------------------------------------------
  880. void GarrisonContain::healSingleObject( Object *obj, Real framesForFullHeal)
  881. {
  882. // setup the healing damageInfo structure with all but the amount
  883. DamageInfo healInfo;
  884. healInfo.in.m_damageType = DAMAGE_HEALING;
  885. healInfo.in.m_deathType = DEATH_NONE;
  886. //healInfo.in.m_sourceID = getObject()->getID();
  887. // get body module of the thing to heal
  888. BodyModuleInterface *body = obj->getBodyModule();
  889. // if we've been in here long enough ... set our health to max
  890. if( TheGameLogic->getFrame() - obj->getContainedByFrame() >= framesForFullHeal )
  891. {
  892. // set the amount to max just to be sure we're at the top
  893. healInfo.in.m_amount = body->getMaxHealth();
  894. // set max health
  895. body->attemptHealing( &healInfo );
  896. } // end if
  897. else
  898. {
  899. //
  900. // given the *whole* time it would take to heal this object, lets pretend that the
  901. // object is at zero health ... and give it a sliver of health as if it were at 0 health
  902. // and would be fully healed at 'framesForFullHeal'
  903. //
  904. healInfo.in.m_amount = body->getMaxHealth() / framesForFullHeal;
  905. // do the healing
  906. body->attemptHealing( &healInfo );
  907. } // end else
  908. }
  909. //-------------------------------------------------------------------------------------------------
  910. /** return the player that *appears* to control this unit. if null,
  911. use getObject()->getControllingPlayer() instead. */
  912. // ------------------------------------------------------------------------------------------------
  913. const Player* GarrisonContain::getApparentControllingPlayer( const Player* observingPlayer ) const
  914. {
  915. const Player* myPlayer = getObject()->getControllingPlayer();
  916. if ( m_hideGarrisonedStateFromNonallies && m_originalTeam && myPlayer && observingPlayer )
  917. {
  918. Relationship r = myPlayer->getRelationship(observingPlayer->getDefaultTeam());
  919. if (r != ALLIES)
  920. return m_originalTeam->getControllingPlayer();
  921. }
  922. return myPlayer;
  923. }
  924. //-------------------------------------------------------------------------------------------------
  925. //-------------------------------------------------------------------------------------------------
  926. void GarrisonContain::recalcApparentControllingPlayer( void )
  927. {
  928. //Record original team first time through.
  929. if( m_originalTeam == NULL )
  930. {
  931. m_originalTeam = getObject()->getTeam();
  932. }
  933. // (hokey trick: if our team is null, nuke originalTeam -- this
  934. // usually means we are being called during game-teardown and
  935. // the teams are no longer valid...)
  936. if (getObject()->getTeam() == NULL)
  937. m_originalTeam = NULL;
  938. // Check to see if we have any units contained in our object
  939. if( getContainCount() > 0 )
  940. {
  941. ContainedItemsList::const_iterator it = getContainList().begin();
  942. Object *rider = *it;
  943. // Check to see if all the contained units are stealthy. Need to set this flag before the capture,
  944. // since the Radar refresh in setTeam will want to use it to decide our color.
  945. Bool detected = rider->getStatusBits().test( OBJECT_STATUS_DETECTED );
  946. m_hideGarrisonedStateFromNonallies = ( !detected && ( getStealthUnitsContained() == getContainCount() ) );
  947. Player* controller = rider->getControllingPlayer();
  948. Team *team = controller ? controller->getDefaultTeam() : NULL;
  949. if( team )
  950. {
  951. getObject()->setTeam( team );
  952. }
  953. }
  954. else
  955. {
  956. //Nothing in object, so set team to original team.
  957. getObject()->setTeam( m_originalTeam );
  958. m_hideGarrisonedStateFromNonallies = false;
  959. }
  960. //Only allow the garrison state to be set if the client team knows that it is garrisoned.
  961. Drawable *draw = getObject()->getDrawable();
  962. if( draw )
  963. {
  964. Bool setModelGarrisoned = FALSE;
  965. if ( getContainCount() > 0 )
  966. {
  967. ContainedItemsList::const_iterator it = getContainList().begin();
  968. Object *occupant = *it;
  969. Bool detected = occupant->getStatusBits().test( OBJECT_STATUS_DETECTED );
  970. if( detected || (getApparentControllingPlayer(ThePlayerList->getLocalPlayer()) == getObject()->getControllingPlayer()) )
  971. {
  972. setModelGarrisoned = TRUE;
  973. }
  974. }
  975. if ( setModelGarrisoned )
  976. draw->setModelConditionState( MODELCONDITION_GARRISONED );
  977. else
  978. draw->clearModelConditionState( MODELCONDITION_GARRISONED );
  979. // Handle the team color that is rendered
  980. const Player* controller = getApparentControllingPlayer(ThePlayerList->getLocalPlayer());
  981. if (controller)
  982. {
  983. if (TheGlobalData->m_timeOfDay == TIME_OF_DAY_NIGHT)
  984. draw->setIndicatorColor( controller->getPlayerNightColor() );
  985. else
  986. draw->setIndicatorColor( controller->getPlayerColor() );
  987. }
  988. // now that we have an object inside us, we need to get all the garrison point positions
  989. // if we don't already have them.
  990. if( getContainCount() > 0 )
  991. {
  992. if ( isEnclosingContainerFor( 0 ) )
  993. {
  994. if ( m_garrisonPointsInitialized == FALSE )
  995. {
  996. loadGarrisonPoints();
  997. }
  998. }
  999. else // must need station points instead
  1000. {
  1001. if ( m_stationGarrisonPointsInitialized == FALSE )
  1002. {
  1003. loadStationGarrisonPoints();
  1004. }
  1005. }
  1006. }
  1007. }
  1008. }
  1009. // ------------------------------------------------------------------------------------------------
  1010. /** Load the garrison point position data and save for use later */
  1011. // ------------------------------------------------------------------------------------------------
  1012. void GarrisonContain::loadGarrisonPoints( void )
  1013. {
  1014. const GarrisonContainModuleData *modData = getGarrisonContainModuleData();
  1015. DEBUG_ASSERTCRASH(modData->m_isEnclosingContainer, ("loadGarrisonPoints... SHOULD NOT GET HERE, since this container is non-enclosing") );
  1016. Object *structure = getObject();
  1017. Int i, j;
  1018. Bool gBonesFound = FALSE;
  1019. //
  1020. // initialize all the garrison points to the center of the object, this assumes that we
  1021. // will never move (these points are cached)
  1022. //
  1023. for( i = 0; i < MAX_GARRISON_POINT_CONDITIONS; ++i )
  1024. for( j = 0; j < MAX_GARRISON_POINTS; ++j )
  1025. m_garrisonPoint[ i ][ j ] = *(structure->getPosition());
  1026. //
  1027. // in order to get all the garrison point positions we will actually switch the model
  1028. // condition through the garrisoned pristine, damaged, and really damaged states. This
  1029. // is especially important because at the time or "garrisoning" a model condition may
  1030. // not actually switch to a garrisoned state because of units that garrison things
  1031. // all stealthy. By caching all the bone positions once we will have access to
  1032. // any of the points for any condition state at any time
  1033. //
  1034. {
  1035. Int conditionIndex;
  1036. Int count = 0;
  1037. // save the original paramters for the model condition
  1038. Drawable* draw = structure->getDrawable();
  1039. const ModelConditionFlags originalFlags = draw->getModelConditionFlags();
  1040. ModelConditionFlags clearFlags;
  1041. ModelConditionFlags setFlags;
  1042. // pristine garrisoned
  1043. clearFlags.clear();
  1044. setFlags.clear();
  1045. clearFlags.set( MODELCONDITION_REALLY_DAMAGED );
  1046. clearFlags.set( MODELCONDITION_RUBBLE );
  1047. clearFlags.set( MODELCONDITION_SPECIAL_DAMAGED );
  1048. clearFlags.set( MODELCONDITION_DAMAGED );
  1049. setFlags.set( MODELCONDITION_GARRISONED );
  1050. structure->clearAndSetModelConditionFlags( clearFlags, setFlags );
  1051. conditionIndex = GARRISON_POINT_PRISTINE;
  1052. count = structure->getMultiLogicalBonePosition("FIREPOINT", MAX_GARRISON_POINTS, m_garrisonPoint[ conditionIndex ], NULL);
  1053. if ( count > 0) gBonesFound = TRUE;
  1054. // damaged garrisoned
  1055. clearFlags.clear();
  1056. setFlags.clear();
  1057. clearFlags.set( MODELCONDITION_REALLY_DAMAGED );
  1058. clearFlags.set( MODELCONDITION_RUBBLE );
  1059. clearFlags.set( MODELCONDITION_SPECIAL_DAMAGED );
  1060. setFlags.set( MODELCONDITION_DAMAGED );
  1061. structure->clearAndSetModelConditionFlags( clearFlags, setFlags );
  1062. conditionIndex = GARRISON_POINT_DAMAGED;
  1063. count = structure->getMultiLogicalBonePosition("FIREPOINT", MAX_GARRISON_POINTS, m_garrisonPoint[ conditionIndex ], NULL);
  1064. if ( count > 0) gBonesFound = TRUE;
  1065. // really damaged garrisoned
  1066. clearFlags.clear();
  1067. setFlags.clear();
  1068. clearFlags.set( MODELCONDITION_RUBBLE );
  1069. clearFlags.set( MODELCONDITION_SPECIAL_DAMAGED );
  1070. clearFlags.set( MODELCONDITION_DAMAGED );
  1071. setFlags.set( MODELCONDITION_REALLY_DAMAGED );
  1072. structure->clearAndSetModelConditionFlags( clearFlags, setFlags );
  1073. conditionIndex = GARRISON_POINT_REALLY_DAMAGED;
  1074. count = structure->getMultiLogicalBonePosition("FIREPOINT", MAX_GARRISON_POINTS, m_garrisonPoint[ conditionIndex ], NULL);
  1075. if ( count > 0) gBonesFound = TRUE;
  1076. // restore the original condition flags
  1077. draw->replaceModelConditionFlags( originalFlags );
  1078. } // end if, draw
  1079. // garrison points are now initialized
  1080. m_garrisonPointsInitialized = TRUE;
  1081. if (gBonesFound && modData->m_mobileGarrison && (getObject()->isMobile() == TRUE) )
  1082. {
  1083. DEBUG_ASSERTCRASH( getObject()->isMobile() == FALSE,
  1084. ("GarrisonContain::update - You have specified this garrisonContain as mobile,\n yet you want garrison point placement bones... \n what are you thinking?") );
  1085. }
  1086. } // end loadGarrisonPoints
  1087. // ------------------------------------------------------------------------------------------------
  1088. /** Validate any exit rally point that has been chosen (if any). If it's not valid,
  1089. * try to find a new one */
  1090. // ------------------------------------------------------------------------------------------------
  1091. void GarrisonContain::validateRallyPoint( void )
  1092. {
  1093. // if we have a rally point already picked, make sure it's valid
  1094. if( m_rallyValid == TRUE )
  1095. {
  1096. Coord3D result;
  1097. FindPositionOptions options;
  1098. // ask for a valid position exactly at the rally point
  1099. options.flags = FPF_IGNORE_ALLY_OR_NEUTRAL_UNITS;
  1100. options.minRadius = 0.0f;
  1101. options.maxRadius = 0.0f;
  1102. options.ignoreObject = getObject();
  1103. options.relationshipObject = getObject();
  1104. if( ThePartitionManager->findPositionAround( &m_exitRallyPoint, &options, &result ) == FALSE )
  1105. m_rallyValid = FALSE;
  1106. } // end if
  1107. // if no rally point is present, try to find one
  1108. if( m_rallyValid == FALSE )
  1109. {
  1110. FindPositionOptions options;
  1111. // pick a location for everybody to rally at
  1112. options.flags = FPF_IGNORE_ALLY_OR_NEUTRAL_UNITS;
  1113. options.minRadius = getObject()->getGeometryInfo().getBoundingCircleRadius();
  1114. options.maxRadius = options.minRadius * 1.8f; // arbitrary max distance away, change as needed
  1115. options.ignoreObject = getObject();
  1116. options.relationshipObject = getObject();
  1117. m_rallyValid = ThePartitionManager->findPositionAround( getObject()->getPosition(),
  1118. &options,
  1119. &m_exitRallyPoint );
  1120. } // end if
  1121. } // end validateRallyPoint
  1122. //-------------------------------------------------------------------------------------------------
  1123. void GarrisonContain::onSelling( void )
  1124. {
  1125. removeAllContained( FALSE );
  1126. OpenContain::onSelling();
  1127. }
  1128. // ------------------------------------------------------------------------------------------------
  1129. /** Remove all contents of this container. We will try to do so with intelligent garrison
  1130. * logic, but if all else fails no matter, we need to get all things out after this
  1131. * call is complete */
  1132. // ------------------------------------------------------------------------------------------------
  1133. void GarrisonContain::removeAllContained( Bool exposeStealthUnits )
  1134. {
  1135. //
  1136. // we will call this when we are destroying the object (either normally or through a game
  1137. // reset/exit etc) *and* also when we have received an "evacuate" command from the player.
  1138. // We will attempt to find a spot for all the contents to scatter to that is around the
  1139. // structure, but if such a spot is not found, we'll just scatter in a random direction
  1140. // from the building.
  1141. //
  1142. // only even bother doing this if we have contents inside us just cause it's a waste
  1143. if( getContainCount() > 0 )
  1144. {
  1145. // validate the current rally point is still a good one, or pick a new one
  1146. validateRallyPoint();
  1147. } // end if
  1148. // call the base class to extend functionality and do the actual removal
  1149. OpenContain::removeAllContained( exposeStealthUnits );
  1150. recalcApparentControllingPlayer();
  1151. } // end removeAllContained
  1152. // ------------------------------------------------------------------------------------------------
  1153. /** 'exitObj' is one of the things we contain, it needs to 'exit' us */
  1154. // ------------------------------------------------------------------------------------------------
  1155. void GarrisonContain::exitObjectViaDoor( Object *exitObj, ExitDoorType exitDoor )
  1156. {
  1157. DEBUG_ASSERTCRASH(exitDoor == DOOR_1, ("multiple exit doors not supported here"));
  1158. // We don't use the ExitPath system of the general OpenContain, we just send people out. The
  1159. // direction of outing has been picked by Design to be the Screen Down at the default camera angle.
  1160. removeFromContain( exitObj );
  1161. Coord3D startPosition;
  1162. Coord3D endPosition;
  1163. Real exitAngle = getObject()->getOrientation();
  1164. // Garrison doesn't have reserveDoor or exitDelay, so if we do nothing, everyone will appear on top
  1165. // of each other and get stuck inside each others' extent (except for the first guy). So we'll
  1166. // scatter the start point around a little to make it better.
  1167. startPosition = *getObject()->getPosition();
  1168. // In the case of cliff bunkers, the units start in a cliff. So we want to adjust.
  1169. AIUpdateInterface *ai = exitObj->getAI();
  1170. if (ai) {
  1171. Locomotor *loco = ai->getCurLocomotor();
  1172. if (loco && !TheAI->pathfinder()->validMovementTerrain( LAYER_GROUND, loco, &startPosition)) {
  1173. // try front & back.
  1174. Real offset = getObject()->getGeometryInfo().getMajorRadius();
  1175. startPosition.x -= offset*Cos(exitAngle);
  1176. startPosition.y -= offset*Sin(exitAngle);
  1177. if (!TheAI->pathfinder()->validMovementTerrain(LAYER_GROUND, loco, &startPosition)) {
  1178. startPosition.x += 2*offset*Cos(exitAngle);
  1179. startPosition.y += 2*offset*Sin(exitAngle);
  1180. if (!TheAI->pathfinder()->validMovementTerrain(LAYER_GROUND, loco, &startPosition)) {
  1181. startPosition = *getObject()->getPosition();
  1182. }
  1183. }
  1184. }
  1185. }
  1186. if ( m_evacDisposition == EVAC_TO_LEFT || m_evacDisposition == EVAC_TO_RIGHT )
  1187. {
  1188. Real EVAC__SCALAR = ( m_evacDisposition == EVAC_TO_LEFT ? 1.0f : -1.0f );
  1189. Real containerHalfLength = getObject()->getGeometryInfo().getMajorRadius() ;
  1190. Real containerHalfWidth = getObject()->getGeometryInfo().getMinorRadius() ;
  1191. Vector3 doorPosition;
  1192. doorPosition.X = GameLogicRandomValueReal( -containerHalfLength/4, containerHalfLength/4 );// a rectangular pocket to act as the "doorway"
  1193. doorPosition.Y = GameLogicRandomValueReal( containerHalfWidth/2, containerHalfWidth * 2) * EVAC__SCALAR;
  1194. doorPosition.Z = 0;
  1195. Vector3 walkToPosition;
  1196. walkToPosition.X = GameLogicRandomValueReal( -containerHalfLength, containerHalfLength );
  1197. walkToPosition.Y = containerHalfWidth * 10 * EVAC__SCALAR;// spread-out!
  1198. walkToPosition.Z = 0;
  1199. const Matrix3D *mtx = getObject()->getTransformMatrix();
  1200. mtx->Transform_Vector( *mtx, doorPosition, &doorPosition );
  1201. startPosition.x = doorPosition.X;
  1202. startPosition.y = doorPosition.Y;
  1203. startPosition.z = doorPosition.Z;
  1204. mtx->Transform_Vector( *mtx, walkToPosition, &walkToPosition );
  1205. endPosition.x = walkToPosition.X;
  1206. endPosition.y = walkToPosition.Y;
  1207. endPosition.z = walkToPosition.Z;
  1208. exitObj->setPosition( &startPosition );
  1209. exitObj->setOrientation( exitAngle );
  1210. ///< @todo This really should be automatically wrapped up in an activation sequence for objects in general
  1211. // tell the AI about it
  1212. TheAI->pathfinder()->addObjectToPathfindMap( exitObj );
  1213. if( ai )
  1214. {
  1215. TheAI->pathfinder()->adjustToPossibleDestination(exitObj, ai->getLocomotorSet(), &endPosition);
  1216. std::vector<Coord3D> exitPath;
  1217. exitPath.push_back(endPosition);
  1218. ai->aiFollowPath( &exitPath, getObject(), CMD_FROM_AI );
  1219. TheAI->pathfinder()->updateGoal(exitObj, &endPosition, TheTerrainLogic->getLayerForDestination(&endPosition));
  1220. }
  1221. }
  1222. else // must be EVAC_BURST_FROM_CENTER. then!
  1223. {
  1224. // if we are not enclosed, then just walk away from where we "are."
  1225. if ( isEnclosingContainerFor( exitObj ))
  1226. {
  1227. exitObj->setPosition( &startPosition ); // correct for non-ground-level station points
  1228. exitObj->setPositionZ( TheTerrainLogic->getGroundHeight( startPosition.x, startPosition.y ) );
  1229. }
  1230. exitObj->setOrientation( exitAngle );
  1231. ///< @todo This really should be automatically wrapped up in an activation sequence for objects in general
  1232. // tell the AI about it
  1233. TheAI->pathfinder()->addObjectToPathfindMap( exitObj );
  1234. endPosition = startPosition;
  1235. if( ai )
  1236. {
  1237. TheAI->pathfinder()->adjustToPossibleDestination(exitObj, ai->getLocomotorSet(), &endPosition);
  1238. std::vector<Coord3D> exitPath;
  1239. exitPath.push_back(endPosition);
  1240. ai->aiFollowPath( &exitPath, getObject(), CMD_FROM_AI );
  1241. TheAI->pathfinder()->updateGoal(exitObj, &endPosition, TheTerrainLogic->getLayerForDestination(&endPosition));
  1242. }
  1243. }
  1244. recalcApparentControllingPlayer();
  1245. }
  1246. //-------------------------------------------------------------------------------------------------
  1247. //-------------------------------------------------------------------------------------------------
  1248. void GarrisonContain::onContaining( Object *obj, Bool wasSelected )
  1249. {
  1250. // extend base class
  1251. OpenContain::onContaining( obj, wasSelected );
  1252. // get the structure object
  1253. Object *structure = getObject();
  1254. // objects inside a building are held
  1255. obj->setDisabled( DISABLED_HELD );
  1256. // the building can now attack, since it has soldiers inside of it
  1257. structure->setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_CAN_ATTACK ) );
  1258. // give the object a garrisoned version of its weapon
  1259. obj->setWeaponBonusCondition( WEAPONBONUSCONDITION_GARRISONED );
  1260. // put the object in the center of the building
  1261. if (isEnclosingContainerFor( obj ))
  1262. obj->setPosition( structure->getPosition() );
  1263. obj->getControllingPlayer()->getAcademyStats()->recordBuildingGarrisoned();
  1264. //
  1265. // the team of the building is now the same as those that have garrisoned it, be sure
  1266. // to save our original team tho so that we can revert back to it when all the
  1267. // occupants are gone
  1268. //
  1269. recalcApparentControllingPlayer();
  1270. Drawable *draw = obj->getDrawable();
  1271. if ( draw && draw->isSelected() )
  1272. TheInGameUI->deselectDrawable( draw );
  1273. } // end onContaining
  1274. //-------------------------------------------------------------------------------------------------
  1275. //-------------------------------------------------------------------------------------------------
  1276. void GarrisonContain::onRemoving( Object *obj )
  1277. {
  1278. OpenContain::onRemoving(obj);
  1279. if (isEnclosingContainerFor( obj ))
  1280. // first remove the object from any garrison fire point if it's at one
  1281. removeObjectFromGarrisonPoint( obj );
  1282. else
  1283. {
  1284. removeObjectFromStationPoint( obj );
  1285. //Kris: Patch 1.01 -- Passing in correct argument for Y (instead of X) fixes cases where selling firebases
  1286. //were dropping contained infantry to incorrect altitudes.
  1287. obj->setPositionZ( TheTerrainLogic->getGroundHeight( obj->getPosition()->x, obj->getPosition()->y ) );
  1288. }
  1289. // give the object back a regular weapon
  1290. obj->clearWeaponBonusCondition( WEAPONBONUSCONDITION_GARRISONED );
  1291. // object is no longer held inside a garrisoned building
  1292. obj->clearDisabled( DISABLED_HELD );
  1293. // if we have nothing left inside of us then we are once again back to our original team
  1294. if( getContainCount() == 0 )
  1295. {
  1296. // put us back on our original team
  1297. // (hokey exception: if our team is null, don't bother -- this
  1298. // usually means we are being called during game-teardown and
  1299. // the teams are no longer valid...)
  1300. if (getObject()->getTeam() != NULL)
  1301. {
  1302. getObject()->setTeam( m_originalTeam );
  1303. m_originalTeam = NULL;
  1304. }
  1305. // we also lose our transient attack ability
  1306. getObject()->clearStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_CAN_ATTACK ) );
  1307. m_hideGarrisonedStateFromNonallies = false;
  1308. // change the state back from garrisoned
  1309. Drawable *draw = getObject()->getDrawable();
  1310. if( draw )
  1311. {
  1312. draw->clearModelConditionState( MODELCONDITION_GARRISONED );
  1313. }
  1314. } // end if
  1315. else if( getStealthUnitsContained() != getContainCount() )
  1316. {
  1317. m_hideGarrisonedStateFromNonallies = false;
  1318. }
  1319. // disable occlusion while the unit walks out of the building
  1320. ///@todo: we should probably not draw the unit to begin with and just have them pop out.
  1321. obj->setSafeOcclusionFrame(TheGameLogic->getFrame()+obj->getTemplate()->getOcclusionDelay());
  1322. recalcApparentControllingPlayer();
  1323. } // end onRemoving
  1324. // ------------------------------------------------------------------------------------------------
  1325. /** A GarrisonContain always lets people shoot out */
  1326. // ------------------------------------------------------------------------------------------------
  1327. Bool GarrisonContain::isPassengerAllowedToFire( ObjectID id ) const
  1328. {
  1329. const Object *self = getObject();
  1330. if ( self && self->isDisabledByType( DISABLED_SUBDUED ) )
  1331. return FALSE;
  1332. return TRUE;
  1333. } // end isPassengerAllowedToFire
  1334. // ------------------------------------------------------------------------------------------------
  1335. /** A Mobile garrison keeps its occupants with it when it moves */
  1336. //-------------------------------------------------------------------------------------------------
  1337. void GarrisonContain::moveObjectsWithMe( void )
  1338. {
  1339. const GarrisonContainModuleData *modData = getGarrisonContainModuleData();
  1340. if (!modData->m_mobileGarrison)
  1341. return;
  1342. const ContainedItemsList& containList = getContainList();
  1343. for( ContainedItemsList::const_iterator it = containList.begin(); it != containList.end(); ++it )
  1344. {
  1345. Object *obj;
  1346. // get the object
  1347. obj = *it;
  1348. obj->setPosition(getObject()->getPosition());
  1349. }
  1350. }
  1351. //-------------------------------------------------------------------------------------------------
  1352. // ------------------------------------------------------------------------------------------------
  1353. void GarrisonContain::onBodyDamageStateChange( const DamageInfo* , BodyDamageType , BodyDamageType newState )
  1354. {
  1355. // This is an event triggered on edge, so we just need to look at the newState. We know a change happened.
  1356. // And we don't need to check Rubble, since death exiting is already handled. This is Garrison specific.
  1357. if( newState == BODY_REALLYDAMAGED && !getObject()->isKindOf( KINDOF_GARRISONABLE_UNTIL_DESTROYED ) )
  1358. {
  1359. if( getContainCount() > 0 )
  1360. orderAllPassengersToExit( CMD_FROM_AI, FALSE );
  1361. }
  1362. }
  1363. //-------------------------------------------------------------------------------------------------
  1364. // ------------------------------------------------------------------------------------------------
  1365. void GarrisonContain::onObjectCreated()
  1366. {
  1367. GarrisonContainModuleData* self = (GarrisonContainModuleData*)getGarrisonContainModuleData();
  1368. Int count = self->m_initialRoster.count;
  1369. const ThingTemplate* rosterTemplate = TheThingFactory->findTemplate( self->m_initialRoster.templateName );
  1370. Object* object = getObject();
  1371. for( int i = 0; i < count; i++ )
  1372. {
  1373. //We are creating a garrison that comes with an initial roster, so add it now!
  1374. Object* payload = TheThingFactory->newObject( rosterTemplate, object->getControllingPlayer()->getDefaultTeam() );
  1375. if( object->getContain() && object->getContain()->isValidContainerFor( payload, true ) )
  1376. {
  1377. object->getContain()->addToContain( payload );
  1378. }
  1379. else
  1380. {
  1381. DEBUG_CRASH( ( "DeliverPayload: PutInContainer %s is full, or not valid for the payload %s!",
  1382. object->getName().str(), self->m_initialRoster.templateName.str() ) );
  1383. }
  1384. }
  1385. }
  1386. // ------------------------------------------------------------------------------------------------
  1387. /** CRC */
  1388. // ------------------------------------------------------------------------------------------------
  1389. void GarrisonContain::crc( Xfer *xfer )
  1390. {
  1391. // extend base class
  1392. OpenContain::crc( xfer );
  1393. } // end crc
  1394. // ------------------------------------------------------------------------------------------------
  1395. /** Xfer method
  1396. * Version Info:
  1397. * 1: Initial version */
  1398. // ------------------------------------------------------------------------------------------------
  1399. void GarrisonContain::xfer( Xfer *xfer )
  1400. {
  1401. Int i;
  1402. // version
  1403. XferVersion currentVersion = 1;
  1404. XferVersion version = currentVersion;
  1405. xfer->xferVersion( &version, currentVersion );
  1406. // extend base class
  1407. OpenContain::xfer( xfer );
  1408. // original team
  1409. TeamID teamID = m_originalTeam ? m_originalTeam->getID() : TEAM_ID_INVALID;
  1410. xfer->xferUser( &teamID, sizeof( TeamID ) );
  1411. if( xfer->getXferMode() == XFER_LOAD )
  1412. {
  1413. if( teamID != TEAM_ID_INVALID )
  1414. {
  1415. m_originalTeam = TheTeamFactory->findTeamByID( teamID );
  1416. if( m_originalTeam == NULL )
  1417. {
  1418. DEBUG_CRASH(( "GarrisonContain::xfer - Unable to find original team by id\n" ));
  1419. throw SC_INVALID_DATA;
  1420. } // end if
  1421. } // end if
  1422. else
  1423. m_originalTeam = NULL;
  1424. } // end if
  1425. xfer->xferBool( &m_hideGarrisonedStateFromNonallies );
  1426. // garrison point data
  1427. UnsignedShort pointDataCount = MAX_GARRISON_POINTS;
  1428. xfer->xferUnsignedShort( &pointDataCount );
  1429. for( i = 0; i < pointDataCount; ++i )
  1430. {
  1431. if( xfer->getXferMode() == XFER_SAVE )
  1432. {
  1433. // object at this point
  1434. Object *obj = m_garrisonPointData[ i ].object;
  1435. ObjectID objectID = obj ? obj->getID() : INVALID_ID;
  1436. xfer->xferObjectID( &objectID );
  1437. // target
  1438. xfer->xferObjectID( &m_garrisonPointData[ i ].targetID );
  1439. // placement frame
  1440. xfer->xferUnsignedInt( &m_garrisonPointData[ i ].placeFrame );
  1441. // last effect frame
  1442. xfer->xferUnsignedInt( &m_garrisonPointData[ i ].lastEffectFrame );
  1443. // effect drawable id
  1444. Drawable *draw = m_garrisonPointData[ i ].effect;
  1445. DrawableID drawableID = draw ? draw->getID() : INVALID_DRAWABLE_ID;
  1446. xfer->xferDrawableID( &drawableID );
  1447. } // end if, save
  1448. else
  1449. {
  1450. // objectID
  1451. ObjectID objectID;
  1452. xfer->xferObjectID( &objectID );
  1453. // target
  1454. ObjectID targetID;
  1455. xfer->xferObjectID( &targetID );
  1456. // placement frame
  1457. UnsignedInt placeFrame;
  1458. xfer->xferUnsignedInt( &placeFrame );
  1459. // last effect frame
  1460. UnsignedInt lastEffectFrame;
  1461. xfer->xferUnsignedInt( &lastEffectFrame );
  1462. // effect drawable id
  1463. DrawableID drawableID;
  1464. xfer->xferDrawableID( &drawableID );
  1465. // store
  1466. if( i < MAX_GARRISON_POINTS )
  1467. {
  1468. m_garrisonPointData[ i ].objectID = objectID;
  1469. m_garrisonPointData[ i ].targetID = targetID;
  1470. m_garrisonPointData[ i ].placeFrame = placeFrame;
  1471. m_garrisonPointData[ i ].lastEffectFrame = lastEffectFrame;
  1472. m_garrisonPointData[ i ].effectID = drawableID;
  1473. } // end if
  1474. } // end else, load
  1475. } // end for i
  1476. // garrison points in use
  1477. xfer->xferInt( &m_garrisonPointsInUse );
  1478. // garrison points
  1479. xfer->xferUser( m_garrisonPoint, sizeof( Coord3D ) * MAX_GARRISON_POINT_CONDITIONS * MAX_GARRISON_POINTS );
  1480. // garrison points initialized
  1481. xfer->xferBool( &m_garrisonPointsInitialized );
  1482. // rally valid
  1483. xfer->xferBool( &m_rallyValid );
  1484. // exit rally point
  1485. xfer->xferCoord3D( &m_exitRallyPoint );
  1486. } // end xfer
  1487. // ------------------------------------------------------------------------------------------------
  1488. /** Load post process */
  1489. // ------------------------------------------------------------------------------------------------
  1490. void GarrisonContain::loadPostProcess( void )
  1491. {
  1492. // extend base class
  1493. OpenContain::loadPostProcess();
  1494. // connect up pointers needed
  1495. for( Int i = 0; i < MAX_GARRISON_POINTS; i++ )
  1496. {
  1497. // object pointer
  1498. if( m_garrisonPointData[ i ].objectID != INVALID_ID )
  1499. {
  1500. m_garrisonPointData[ i ].object = TheGameLogic->findObjectByID( m_garrisonPointData[ i ].objectID );
  1501. if( m_garrisonPointData[ i ].object == NULL )
  1502. {
  1503. DEBUG_CRASH(( "GarrisonContain::loadPostProcess - Unable to find object for point data\n" ));
  1504. throw SC_INVALID_DATA;
  1505. } // end if
  1506. } // end if
  1507. else
  1508. m_garrisonPointData[ i ].object = NULL;
  1509. // drawable effect pointer
  1510. if( m_garrisonPointData[ i ].effectID != INVALID_ID )
  1511. {
  1512. m_garrisonPointData[ i ].effect = TheGameClient->findDrawableByID( m_garrisonPointData[ i ].effectID );
  1513. if( m_garrisonPointData[ i ].effect == NULL )
  1514. {
  1515. DEBUG_CRASH(( "GarrisonContain::loadPostProcess - Unable to find effect for point data\n" ));
  1516. throw SC_INVALID_DATA;
  1517. } // end if
  1518. } // end if
  1519. else
  1520. m_garrisonPointData[ i ].effect = NULL;
  1521. } // end for i
  1522. } // end loadPostProcess
  1523. // ------------------------------------------------------------------------------------------------
  1524. /** Load the loadStationGarrisonPoints data and save for use later */
  1525. // ------------------------------------------------------------------------------------------------
  1526. void GarrisonContain::loadStationGarrisonPoints( void )
  1527. {
  1528. const GarrisonContainModuleData *modData = getGarrisonContainModuleData();
  1529. Object *structure = getObject();
  1530. Bool stationBonesFound = FALSE;
  1531. //
  1532. // in order to get all the station point positions we will actually switch the model
  1533. // condition to garrisoned pristine, and use these for any modelcondition
  1534. {
  1535. Int conditionIndex;
  1536. Int count = 0;
  1537. // save the original paramters for the model condition
  1538. Drawable* draw = structure->getDrawable();
  1539. const ModelConditionFlags originalFlags = draw->getModelConditionFlags();
  1540. ModelConditionFlags clearFlags;
  1541. ModelConditionFlags setFlags;
  1542. // pristine garrisoned
  1543. clearFlags.clear();
  1544. setFlags.clear();
  1545. clearFlags.set( MODELCONDITION_REALLY_DAMAGED );
  1546. clearFlags.set( MODELCONDITION_RUBBLE );
  1547. clearFlags.set( MODELCONDITION_SPECIAL_DAMAGED );
  1548. clearFlags.set( MODELCONDITION_DAMAGED );
  1549. setFlags.set( MODELCONDITION_GARRISONED );
  1550. structure->clearAndSetModelConditionFlags( clearFlags, setFlags );
  1551. conditionIndex = GARRISON_POINT_PRISTINE;
  1552. Coord3D tempBuffer[MAX_GARRISON_POINTS];
  1553. for( int t = 0; t < MAX_GARRISON_POINTS; ++t )
  1554. tempBuffer[ t ] = *(structure->getPosition());
  1555. count = structure->getMultiLogicalBonePosition("STATION", modData->m_containMax, tempBuffer, NULL);
  1556. if ( count > 0) stationBonesFound = TRUE;
  1557. m_stationPointList.clear();// we are starting over... forget everything
  1558. for( t = 0; t < count; ++t )
  1559. {
  1560. StationPointData tempStationPointData;
  1561. tempStationPointData.position = tempBuffer[ t ];
  1562. tempStationPointData.occupantID = INVALID_ID;
  1563. m_stationPointList.push_back( tempStationPointData ); // store for later use
  1564. }
  1565. // restore the original condition flags
  1566. draw->replaceModelConditionFlags( originalFlags );
  1567. //tempBuffer pops
  1568. }
  1569. // garrison points are now initialized
  1570. m_stationGarrisonPointsInitialized = TRUE;
  1571. if (stationBonesFound && modData->m_mobileGarrison && (getObject()->isMobile() == TRUE) )
  1572. {
  1573. DEBUG_ASSERTCRASH( getObject()->isMobile() == FALSE,
  1574. ("GarrisonContain::update - You have specified this garrisonContain as mobile,\n yet you want station garrison point placement bones... \n what are you thinking?") );
  1575. }
  1576. } // end loadStationGarrisonPoints