OpenContain.cpp 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597
  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: OpenContain.cpp //////////////////////////////////////////////////////////////////////////
  24. // Author: Colin Day, November 2001
  25. // Desc: The OpenContain ContainModule allows objects to be contained inside of other
  26. // objects. There is a set of functionality that will be common to
  27. // all container modules that provides the actual containment
  28. // implementations, those implementations are found here
  29. ///////////////////////////////////////////////////////////////////////////////////////////////////
  30. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  31. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  32. #include "Common/BitFlagsIO.h"
  33. #include "Common/GameAudio.h"
  34. #include "Common/GameState.h"
  35. #include "Common/Module.h"
  36. #include "Common/Player.h"
  37. #include "Common/RandomValue.h"
  38. #include "Common/ThingTemplate.h"
  39. #include "Common/Xfer.h"
  40. #include "GameClient/Drawable.h"
  41. #include "GameClient/InGameUI.h"
  42. #include "GameClient/ControlBar.h"
  43. #include "GameLogic/AIPathfind.h"
  44. #include "GameLogic/GameLogic.h"
  45. #include "GameLogic/Module/OpenContain.h"
  46. #include "GameLogic/Module/AIUpdate.h"
  47. #include "GameLogic/Module/PhysicsUpdate.h"
  48. #include "GameLogic/Module/StealthUpdate.h"
  49. #include "GameLogic/Module/BodyModule.h"
  50. #include "GameLogic/Object.h"
  51. #include "GameLogic/PartitionManager.h"
  52. #include "GameLogic/Weapon.h"
  53. #ifdef _INTERNAL
  54. // for occasional debugging...
  55. //#pragma optimize("", off)
  56. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  57. #endif
  58. ///////////////////////////////////////////////////////////////////////////////////////////////////
  59. // PUBLIC FUNCTIONS ///////////////////////////////////////////////////////////////////////////////
  60. ///////////////////////////////////////////////////////////////////////////////////////////////////
  61. // ------------------------------------------------------------------------------------------------
  62. // ------------------------------------------------------------------------------------------------
  63. OpenContainModuleData::OpenContainModuleData( void )
  64. {
  65. m_containMax = CONTAIN_MAX_UNKNOWN; // means we don't care, infinite, unassigned, whatever
  66. m_passengersAllowedToFire = FALSE;
  67. m_passengersInTurret = FALSE;
  68. m_numberOfExitPaths = 1;
  69. m_damagePercentageToUnits = 0;
  70. m_doorOpenTime = 1;
  71. m_allowInsideKindOf.clear(); m_allowInsideKindOf.flip(); // everything is allowed
  72. m_forbidInsideKindOf.clear(); // nothing is forbidden
  73. m_allowAlliesInside = TRUE;
  74. m_allowEnemiesInside = TRUE;
  75. m_allowNeutralInside = TRUE;
  76. } // end OpenContainModuleData
  77. // ------------------------------------------------------------------------------------------------
  78. // ------------------------------------------------------------------------------------------------
  79. /*static*/ void OpenContainModuleData::buildFieldParse(MultiIniFieldParse& p)
  80. {
  81. UpdateModuleData::buildFieldParse(p);
  82. static const FieldParse dataFieldParse[] =
  83. {
  84. { "ContainMax", INI::parseInt, NULL, offsetof( OpenContainModuleData, m_containMax ) },
  85. { "EnterSound", INI::parseAudioEventRTS, NULL, offsetof( OpenContainModuleData, m_enterSound ) },
  86. { "ExitSound", INI::parseAudioEventRTS, NULL, offsetof( OpenContainModuleData, m_exitSound ) },
  87. { "DamagePercentToUnits", INI::parsePercentToReal, NULL, offsetof( OpenContainModuleData, m_damagePercentageToUnits ) },
  88. { "AllowInsideKindOf", KindOfMaskType::parseFromINI, NULL, offsetof( OpenContainModuleData, m_allowInsideKindOf ) },
  89. { "ForbidInsideKindOf", KindOfMaskType::parseFromINI, NULL, offsetof( OpenContainModuleData, m_forbidInsideKindOf ) },
  90. { "PassengersAllowedToFire", INI::parseBool, NULL, offsetof( OpenContainModuleData, m_passengersAllowedToFire ) },
  91. { "PassengersInTurret", INI::parseBool, NULL, offsetof( OpenContainModuleData, m_passengersInTurret ) },
  92. { "NumberOfExitPaths", INI::parseInt, NULL, offsetof( OpenContainModuleData, m_numberOfExitPaths ) },
  93. { "DoorOpenTime", INI::parseDurationUnsignedInt, NULL, offsetof( OpenContainModuleData, m_doorOpenTime ) },
  94. { "AllowAlliesInside", INI::parseBool, NULL, offsetof( OpenContainModuleData, m_allowAlliesInside ) },
  95. { "AllowEnemiesInside", INI::parseBool, NULL, offsetof( OpenContainModuleData, m_allowEnemiesInside ) },
  96. { "AllowNeutralInside", INI::parseBool, NULL, offsetof( OpenContainModuleData, m_allowNeutralInside ) },
  97. { 0, 0, 0, 0 }
  98. };
  99. p.add(dataFieldParse);
  100. p.add(DieMuxData::getFieldParse(), offsetof( OpenContainModuleData, m_dieMuxData ));
  101. }
  102. ///////////////////////////////////////////////////////////////////////////////////////////////////
  103. ///////////////////////////////////////////////////////////////////////////////////////////////////
  104. ///////////////////////////////////////////////////////////////////////////////////////////////////
  105. //-------------------------------------------------------------------------------------------------
  106. //-------------------------------------------------------------------------------------------------
  107. OpenContain::OpenContain( Thing *thing, const ModuleData* moduleData ) : UpdateModule( thing, moduleData )
  108. {
  109. // initialize our lists
  110. m_containList.clear();
  111. m_objectEnterExitInfo.clear();
  112. m_playerEnteredMask = 0;
  113. m_lastUnloadSoundFrame = 0;
  114. m_lastLoadSoundFrame = 0;
  115. m_containListSize = 0;
  116. m_stealthUnitsContained = 0;
  117. m_doorCloseCountdown = 0;
  118. //Added By Sadullah Nader
  119. //Initializations inserted
  120. m_rallyPoint.zero();
  121. m_rallyPointExists = FALSE;
  122. //
  123. m_conditionState.clear();
  124. m_firePointStart = -1;
  125. m_firePointNext = 0;
  126. m_firePointSize = 0;
  127. m_noFirePointsInArt = false;
  128. m_whichExitPath = 1;
  129. m_loadSoundsEnabled = TRUE;
  130. for( Int i = 0; i < MAX_FIRE_POINTS; i++ )
  131. {
  132. m_firePoints[ i ].Make_Identity();
  133. } // end for i
  134. }
  135. // ------------------------------------------------------------------------------------------------
  136. // ------------------------------------------------------------------------------------------------
  137. Int OpenContain::getContainMax( void ) const
  138. {
  139. const OpenContainModuleData *modData = getOpenContainModuleData();
  140. return modData->m_containMax;
  141. } // end getContainMax
  142. //-------------------------------------------------------------------------------------------------
  143. //-------------------------------------------------------------------------------------------------
  144. OpenContain::~OpenContain()
  145. {
  146. // sanity, the system should be cleaning these up itself if all is going well
  147. DEBUG_ASSERTCRASH( m_containList.empty(),
  148. ("OpenContain %s: destroying a container that still has items in it!\n",
  149. getObject()->getTemplate()->getName().str() ) );
  150. // sanity
  151. DEBUG_ASSERTCRASH( m_xferContainIDList.empty(),
  152. ("OpenContain %s: m_xferContainIDList is not empty but should be\n",
  153. getObject()->getTemplate()->getName().str() ) );
  154. }
  155. //-------------------------------------------------------------------------------------------------
  156. //-------------------------------------------------------------------------------------------------
  157. // our object changed position... react as appropriate.
  158. void OpenContain::containReactToTransformChange()
  159. {
  160. // Our transform changed, which means our bones moved, and we keep people positioned on bones.
  161. redeployOccupants();
  162. }
  163. //-------------------------------------------------------------------------------------------------
  164. //-------------------------------------------------------------------------------------------------
  165. UpdateSleepTime OpenContain::update( void )
  166. {
  167. m_playerEnteredMask = 0;
  168. // we need to monitor changes in the art and position
  169. monitorConditionChanges();
  170. if( m_doorCloseCountdown )
  171. {
  172. /// @todo srj -- for now, OpenContain assumes at most one door
  173. --m_doorCloseCountdown;
  174. if( m_doorCloseCountdown == 0 )
  175. getObject()->clearAndSetModelConditionState( MODELCONDITION_DOOR_1_OPENING, MODELCONDITION_DOOR_1_CLOSING );
  176. }
  177. if (!m_objectEnterExitInfo.empty())
  178. pruneDeadWanters();
  179. return UPDATE_SLEEP_NONE;
  180. }
  181. //-------------------------------------------------------------------------------------------------
  182. //-------------------------------------------------------------------------------------------------
  183. void OpenContain::addOrRemoveObjFromWorld(Object* obj, Bool add)
  184. {
  185. //
  186. // it's expensive to add and remove structures from the pathfinder, but we really shouldn't
  187. // be doing it anyway as it doesn't make much sense to contain a structure, but we'll
  188. // check for it here and print a warning
  189. //
  190. if( obj->isKindOf( KINDOF_STRUCTURE ) )
  191. DEBUG_LOG(( "WARNING: Containing/Removing structures like '%s' is potentially a very expensive and slow operation\n",
  192. obj->getTemplate()->getName().str() ));
  193. if (add)
  194. {
  195. ThePartitionManager->registerObject( obj );
  196. if( obj->getDrawable() )
  197. {
  198. obj->getDrawable()->setDrawableHidden( false );
  199. }
  200. // add object to pathfind map
  201. TheAI->pathfinder()->addObjectToPathfindMap( obj );
  202. }
  203. else
  204. {
  205. // remove object from its group (if any)
  206. obj->leaveGroup();
  207. // remove rider from partition manager
  208. ThePartitionManager->unRegisterObject( obj );
  209. // hide the drawable associated with rider
  210. if( obj->getDrawable() )
  211. obj->getDrawable()->setDrawableHidden( true );
  212. // remove object from pathfind map
  213. TheAI->pathfinder()->removeObjectFromPathfindMap( obj );
  214. }
  215. // if we're a non-enclosing container (eg, parachute), and we're inside an
  216. // enclosing container (eg, b52), we need to be able to show/hide the contained riders...
  217. if( obj->getContain() )
  218. {
  219. const ContainedItemsList* items = obj->getContain()->getContainedItemsList();
  220. if (items)
  221. {
  222. for(ContainedItemsList::const_iterator it = items->begin(); it != items->end(); ++it)
  223. {
  224. if( !obj->getContain()->isEnclosingContainerFor(*it) )
  225. addOrRemoveObjFromWorld(*it, add);
  226. }
  227. }
  228. }
  229. }
  230. //-------------------------------------------------------------------------------------------------
  231. /** Add 'rider' to the m_containList of objects in this module.
  232. * This will trigger an onContaining event for the object that this module
  233. * is a part of and an onContainedBy event for the object being contained */
  234. //-------------------------------------------------------------------------------------------------
  235. void OpenContain::addToContain( Object *rider )
  236. {
  237. // sanity
  238. if( rider == NULL )
  239. return;
  240. #if defined(_DEBUG) || defined(_INTERNAL)
  241. if( !isValidContainerFor( rider, false ) )
  242. {
  243. Object *reportObject = rider;
  244. if( rider->getContain() )
  245. {
  246. //Report the first thing inside it!
  247. const ContainedItemsList *items = rider->getContain()->getContainedItemsList();
  248. if( items )
  249. {
  250. reportObject = *items->begin();
  251. }
  252. }
  253. DEBUG_CRASH( ("OpenContain::addToContain() - Object %s not valid for container %s!", reportObject?reportObject->getTemplate()->getName().str():"NULL", getObject()->getTemplate()->getName().str() ) );
  254. }
  255. #endif
  256. // this object cannot be contained by this module if it is already contained in something
  257. if( rider->getContainedBy() != NULL )
  258. {
  259. DEBUG_LOG(( "'%s' is trying to contain '%s', but '%s' is already contained by '%s'\n",
  260. getObject()->getTemplate()->getName().str(),
  261. rider->getTemplate()->getName().str(),
  262. rider->getTemplate()->getName().str(),
  263. rider->getContainedBy()->getTemplate()->getName().str() ));
  264. return;
  265. }
  266. // Which list to physically add to needs to be overridable
  267. addToContainList(rider);
  268. m_playerEnteredMask = rider->getControllingPlayer()->getPlayerMask();
  269. if (isEnclosingContainerFor( rider ))
  270. {
  271. addOrRemoveObjFromWorld(rider, false);
  272. }
  273. // ensure our contents are positions correctly.
  274. redeployOccupants();
  275. // trigger an onContaining event for the object that just "ate" something
  276. if( getObject()->getContain() )
  277. {
  278. getObject()->getContain()->onContaining( rider );
  279. }
  280. // trigger an onContainedBy event for the object that just got "eaten" by us
  281. rider->onContainedBy( getObject() );
  282. doLoadSound();
  283. }
  284. //-------------------------------------------------------------------------------------------------
  285. //-------------------------------------------------------------------------------------------------
  286. void OpenContain::addToContainList( Object *rider )
  287. {
  288. m_containList.push_back(rider);
  289. m_containListSize++;
  290. if( rider->isKindOf( KINDOF_STEALTH_GARRISON ) )
  291. {
  292. m_stealthUnitsContained++;
  293. }
  294. }
  295. //-------------------------------------------------------------------------------------------------
  296. /** Remove 'rider' from the m_containList of objects in this module.
  297. * This will trigger an onRemoving event for the object that this module
  298. * is a part of and an onRemovedFrom event for the object being removed */
  299. //-------------------------------------------------------------------------------------------------
  300. void OpenContain::removeFromContain( Object *rider, Bool exposeStealthUnits )
  301. {
  302. // sanity
  303. if( rider == NULL )
  304. return;
  305. //
  306. // we can only remove this object from the contains list of this module if
  307. // it is actually contained by this module
  308. //
  309. Object *containedBy = rider->getContainedBy();
  310. if( containedBy != getObject() )
  311. {
  312. DEBUG_LOG(( "'%s' is trying to un-contain '%s', but '%s' is really contained by '%s'\n",
  313. getObject()->getTemplate()->getName().str(),
  314. rider->getTemplate()->getName().str(),
  315. rider->getTemplate()->getName().str(),
  316. containedBy ? containedBy->getTemplate()->getName().str() : "Nothing" ));
  317. return;
  318. }
  319. ContainedItemsList::iterator it = std::find(m_containList.begin(), m_containList.end(), rider);
  320. if (it != m_containList.end())
  321. {
  322. // note that this invalidates the iterator!
  323. removeFromContainViaIterator( it, exposeStealthUnits );
  324. }
  325. }
  326. //-------------------------------------------------------------------------------------------------
  327. /** Remove all contained objects from the contained list */
  328. //-------------------------------------------------------------------------------------------------
  329. void OpenContain::removeAllContained( Bool exposeStealthUnits )
  330. {
  331. ContainedItemsList::iterator it;
  332. while ((it = m_containList.begin()) != m_containList.end())
  333. {
  334. // note that this invalidates the iterator!
  335. removeFromContainViaIterator( it, exposeStealthUnits );
  336. } // end while
  337. } // end removeAllContained
  338. //-------------------------------------------------------------------------------------------------
  339. //-------------------------------------------------------------------------------------------------
  340. void OpenContain::doLoadSound()
  341. {
  342. //
  343. // play a sound for loading someone into a building
  344. //
  345. if( m_loadSoundsEnabled )
  346. {
  347. UnsignedInt now = TheGameLogic->getFrame();
  348. if( now != m_lastLoadSoundFrame )
  349. {
  350. if (getOpenContainModuleData())
  351. {
  352. AudioEventRTS enterSound(getOpenContainModuleData()->m_enterSound);
  353. enterSound.setObjectID(getObject()->getID());
  354. TheAudio->addAudioEvent(&enterSound);
  355. }
  356. // save this frame as the last time we did this sound
  357. m_lastLoadSoundFrame = now;
  358. }
  359. }
  360. }
  361. //-------------------------------------------------------------------------------------------------
  362. //-------------------------------------------------------------------------------------------------
  363. void OpenContain::doUnloadSound()
  364. {
  365. //
  366. // play a sound for unloading someone from a building ... but don't play more
  367. // than one per frame (we can do meta unload all commands)
  368. //
  369. UnsignedInt now = TheGameLogic->getFrame();
  370. if( now != m_lastUnloadSoundFrame )
  371. {
  372. if (getOpenContainModuleData())
  373. {
  374. AudioEventRTS exitSound(getOpenContainModuleData()->m_exitSound);
  375. exitSound.setObjectID(getObject()->getID());
  376. TheAudio->addAudioEvent(&exitSound);
  377. }
  378. // save this frame as the last time we did this sound
  379. m_lastUnloadSoundFrame = now;
  380. }
  381. }
  382. //-------------------------------------------------------------------------------------------------
  383. /** Iterate the contained list and call the callback on each of the objects */
  384. //-------------------------------------------------------------------------------------------------
  385. void OpenContain::iterateContained( ContainIterateFunc func, void *userData, Bool reverse )
  386. {
  387. if (reverse)
  388. {
  389. // note that this has to be smart enough to handle items in the list being deleted
  390. // via the callback function.
  391. for(ContainedItemsList::reverse_iterator it = m_containList.rbegin(); it != m_containList.rend(); )
  392. {
  393. // save the rider...
  394. Object* rider = *it;
  395. // incr the iterator BEFORE calling the func (if the func removes the rider,
  396. // the iterator becomes invalid)
  397. ++it;
  398. // call it
  399. (*func)( rider, userData );
  400. }
  401. }
  402. else
  403. {
  404. // note that this has to be smart enough to handle items in the list being deleted
  405. // via the callback function.
  406. for(ContainedItemsList::iterator it = m_containList.begin(); it != m_containList.end(); )
  407. {
  408. // save the rider...
  409. Object* rider = *it;
  410. // incr the iterator BEFORE calling the func (if the func removes the rider,
  411. // the iterator becomes invalid)
  412. ++it;
  413. // call it
  414. (*func)( rider, userData );
  415. }
  416. }
  417. }
  418. //-------------------------------------------------------------------------------------------------
  419. struct DropData
  420. {
  421. Real minRadius;
  422. Real maxRadius;
  423. Object *container;
  424. };
  425. ///////////////////////////////////////////////////////////////////////////////////////////////////
  426. // PRIVATE FUNCTIONS //////////////////////////////////////////////////////////////////////////////
  427. ///////////////////////////////////////////////////////////////////////////////////////////////////
  428. //-------------------------------------------------------------------------------------------------
  429. /** Remove an object from the containment of this module given the item
  430. * to remove and trigger the proper callback events */
  431. //-------------------------------------------------------------------------------------------------
  432. void OpenContain::removeFromContainViaIterator( ContainedItemsList::iterator it, Bool exposeStealthUnits )
  433. {
  434. /*
  435. #ifdef _DEBUG
  436. TheInGameUI->message( UnicodeString( L"'%S(%d)' no longer contains '%S(%d)'" ),
  437. getObject()->getTemplate()->getName().str(),
  438. getObject()->getID(),
  439. itemToRemove->m_object->getTemplate()->getName().str(),
  440. itemToRemove->m_object->getID() );
  441. #endif
  442. */
  443. Object *rider = *it;
  444. // remove item from list
  445. m_containList.erase(it);
  446. m_containListSize--;
  447. if( rider->isKindOf( KINDOF_STEALTH_GARRISON ) )
  448. {
  449. m_stealthUnitsContained--;
  450. if( exposeStealthUnits )
  451. {
  452. static NameKeyType key_StealthUpdate = NAMEKEY( "StealthUpdate" );
  453. StealthUpdate* stealth = (StealthUpdate*)rider->findUpdateModule( key_StealthUpdate );
  454. if( stealth )
  455. {
  456. stealth->markAsDetected();
  457. }
  458. }
  459. }
  460. if (isEnclosingContainerFor( rider ))
  461. {
  462. addOrRemoveObjFromWorld(rider, true);
  463. }
  464. /// place the object in the world at position of the container m_object
  465. rider->setPosition( getObject()->getPosition() );
  466. rider->setLayer( getObject()->getLayer() );
  467. doUnloadSound();
  468. // trigger an onRemoving event for 'm_object' no longer containing 'itemToRemove->m_object'
  469. DEBUG_ASSERTCRASH(getObject()->getContain() == this, ("hmm, wrong container"));
  470. if( getObject()->getContain() )
  471. {
  472. getObject()->getContain()->onRemoving( rider );
  473. }
  474. // trigger an onRemovedFrom event for 'remove'
  475. DEBUG_ASSERTCRASH(getObject()->getContain() == this, ("hmm, wrong container 2"));
  476. rider->onRemovedFrom( getObject() );
  477. }
  478. //-------------------------------------------------------------------------------------------------
  479. void OpenContain::scatterToNearbyPosition(Object* rider)
  480. {
  481. Object *theContainer = getObject();
  482. //
  483. // for now we will just set the position of the object that is being removed from us
  484. // at a random angle away from our center out some distance
  485. //
  486. //
  487. // pick an angle that is in the view of the current camera position so that
  488. // the thing will come out "toward" the player and they can see it
  489. // NOPE, can't do that ... all players screen angles will be different, unless
  490. // we maintain the angle of each players screen in the player structure or something
  491. //
  492. Real angle = GameLogicRandomValueReal( 0.0f, 2.0f * PI );
  493. // angle = TheTacticalView->getAngle();
  494. // angle -= GameLogicRandomValueReal( PI / 3.0f, 2.0f * (PI / 3.0F) );
  495. Real minRadius = theContainer->getGeometryInfo().getBoundingCircleRadius();
  496. Real maxRadius = minRadius + minRadius / 2.0f;
  497. const Coord3D *containerPos = theContainer->getPosition();
  498. Real dist = GameLogicRandomValueReal( minRadius, maxRadius );
  499. Coord3D pos;
  500. pos.x = dist * Cos( angle ) + containerPos->x;
  501. pos.y = dist * Sin( angle ) + containerPos->y;
  502. pos.z = TheTerrainLogic->getLayerHeight( pos.x, pos.y, theContainer->getLayer() );
  503. // set orientation
  504. rider->setOrientation( angle );
  505. AIUpdateInterface *ai = rider->getAI();
  506. if( ai )
  507. {
  508. // set position of the object at center of building and move them toward pos
  509. rider->setPosition( theContainer->getPosition() );
  510. ai->ignoreObstacle(theContainer);
  511. ai->aiMoveToPosition( &pos, CMD_FROM_AI );
  512. } // end if
  513. else
  514. {
  515. // no ai, just set position at the target pos
  516. rider->setPosition( &pos );
  517. } // end else
  518. }
  519. //-------------------------------------------------------------------------------------------------
  520. void OpenContain::onContaining( Object * /*rider*/ )
  521. {
  522. // Play audio
  523. if( m_loadSoundsEnabled )
  524. {
  525. AudioEventRTS enterSound = *getObject()->getTemplate()->getSoundEnter();
  526. enterSound.setObjectID(getObject()->getID());
  527. TheAudio->addAudioEvent(&enterSound);
  528. }
  529. }
  530. //-------------------------------------------------------------------------------------------------
  531. void OpenContain::onRemoving( Object *rider)
  532. {
  533. // Play audio
  534. AudioEventRTS exitSound = *getObject()->getTemplate()->getSoundExit();
  535. exitSound.setObjectID(getObject()->getID());
  536. TheAudio->addAudioEvent(&exitSound);
  537. if (rider) {
  538. // This is a misnomer, but it makes it clearer for the user.
  539. AudioEventRTS fallingSound = *rider->getTemplate()->getSoundFalling();
  540. fallingSound.setObjectID(rider->getID());
  541. TheAudio->addAudioEvent(&fallingSound);
  542. }
  543. }
  544. //-------------------------------------------------------------------------------------------------
  545. Real OpenContain::getContainedItemsMass() const
  546. {
  547. /// @todo srj -- may want to cache this information.
  548. Real mass = 0;
  549. for(ContainedItemsList::const_iterator it = m_containList.begin(); it != m_containList.end(); ++it)
  550. {
  551. PhysicsBehavior* phys = (*it)->getPhysics();
  552. if (phys)
  553. mass += phys->getMass();
  554. }
  555. return mass;
  556. }
  557. //-------------------------------------------------------------------------------------------------
  558. //-------------------------------------------------------------------------------------------------
  559. void OpenContain::onCollide( Object *other, const Coord3D *loc, const Coord3D *normal )
  560. {
  561. // colliding with nothing? we don't care.
  562. if( other == NULL )
  563. return;
  564. // ok, step two: only contain stuff that wants us to contain it.
  565. // (it would suck to have accidental collisions contain things...)
  566. // must be an AI object....
  567. AIUpdateInterface *ai = other->getAI();
  568. if (ai == NULL)
  569. return;
  570. // ...and must be trying to enter our object.
  571. // (huh huh, he said "enter")
  572. if (ai->getEnterTarget() != getObject())
  573. return;
  574. // last-minute change: don't allow units from multiple (different) players to occupy the same
  575. // unit. so eject everyone else if they aren't controlled by the same player. (srj)
  576. for( ContainedItemsList::iterator it = m_containList.begin(); it != m_containList.end(); )
  577. {
  578. // save the rider...
  579. Object* rider = *it;
  580. // incr the iterator BEFORE calling the func (if the func removes the rider,
  581. // the iterator becomes invalid)
  582. ++it;
  583. // call it
  584. if( rider->getControllingPlayer() != other->getControllingPlayer() )
  585. {
  586. if( rider->getAI() )
  587. {
  588. if( rider->isKindOf( KINDOF_STEALTH_GARRISON ) )
  589. {
  590. // aiExit is needed to walk away from the building well, but it doesn't take the Unstealth flag
  591. static const NameKeyType key_StealthUpdate = NAMEKEY( "StealthUpdate" );
  592. StealthUpdate* stealth = (StealthUpdate*)rider->findUpdateModule( key_StealthUpdate );
  593. if( stealth )
  594. {
  595. stealth->markAsDetected();
  596. }
  597. }
  598. rider->getAI()->aiExit( getObject(), CMD_FROM_AI );
  599. }
  600. else
  601. removeFromContain( rider, TRUE );
  602. }
  603. }
  604. // finally, we must have space to contain it.
  605. if( !isValidContainerFor( other, TRUE ) )
  606. return;
  607. addToContain(other);
  608. }
  609. //-------------------------------------------------------------------------------------------------
  610. //-------------------------------------------------------------------------------------------------
  611. void OpenContain::onDelete( void ) ///< Last possible moment cleanup
  612. {
  613. // This uses my literal list, and not the gettor, because we don't want to get redirected some place fancy.
  614. for(ContainedItemsList::iterator it = m_containList.begin(); it != m_containList.end(); )
  615. {
  616. Object* rider = *it;
  617. ++it;
  618. TheGameLogic->destroyObject( rider );
  619. }
  620. }
  621. //-------------------------------------------------------------------------------------------------
  622. /** The die callback. */
  623. //-------------------------------------------------------------------------------------------------
  624. void OpenContain::onDie( const DamageInfo * damageInfo )
  625. {
  626. if (!getOpenContainModuleData()->m_dieMuxData.isDieApplicable(getObject(), damageInfo))
  627. return;
  628. //Check to see if we are going to inflict damage on contained units.
  629. if( getOpenContainModuleData()->m_damagePercentageToUnits > 0 )
  630. {
  631. //Cycle through the units and apply damage to them!
  632. processDamageToContained();
  633. }
  634. killRidersWhoAreNotFreeToExit();
  635. // Leaving this commented out to show it can't work. We are about to die, so they will have zero
  636. // chance to hit an exitState::Update. At least we would clean them up in onDelete.
  637. // orderAllPassengersToExit( CMD_FROM_AI );
  638. removeAllContained();
  639. }
  640. // ------------------------------------------------------------------------------------------------
  641. /** Check to see if we are a valid container for 'obj' */
  642. // ------------------------------------------------------------------------------------------------
  643. Bool OpenContain::isValidContainerFor(const Object* obj, Bool checkCapacity) const
  644. {
  645. const Object *us = getObject();
  646. const OpenContainModuleData *modData = getOpenContainModuleData();
  647. // if we have any kind of masks set then we must make that check
  648. if (obj->isAnyKindOf( modData->m_allowInsideKindOf ) == FALSE ||
  649. obj->isAnyKindOf( modData->m_forbidInsideKindOf ) == TRUE)
  650. {
  651. return false;
  652. }
  653. //
  654. // check relationship, note that this behavior is defined as the relation between
  655. // 'obj' and the container 'us', and not the reverse
  656. //
  657. Bool relationshipRestricted = FALSE;
  658. Relationship r = obj->getRelationship( us );
  659. switch( r )
  660. {
  661. case ALLIES:
  662. if( modData->m_allowAlliesInside == FALSE )
  663. relationshipRestricted = TRUE;
  664. break;
  665. case ENEMIES:
  666. if( modData->m_allowEnemiesInside == FALSE )
  667. relationshipRestricted = TRUE;
  668. break;
  669. case NEUTRAL:
  670. if( modData->m_allowNeutralInside == FALSE )
  671. relationshipRestricted = TRUE;
  672. break;
  673. default:
  674. DEBUG_CRASH(( "isValidContainerFor: Undefined relationship (%d) between '%s' and '%s'",
  675. r, getObject()->getTemplate()->getName().str(),
  676. obj->getTemplate()->getName().str() ));
  677. return FALSE;
  678. } // end switch
  679. if( relationshipRestricted == TRUE )
  680. return FALSE;
  681. // all is well
  682. return true;
  683. } // end isValidContainerFor
  684. // ------------------------------------------------------------------------------------------------
  685. /**
  686. This new call is going to replace all game callings of removeFromContain.
  687. Consider rFC a 'system' call, while this is a 'game' call. rFC will pull
  688. from the contained list and just plop the guy in the world, this function
  689. will then override that location with cool game stuff.
  690. Exit and Evacuate can fail, removeAllContain and removeFromContain cannot.
  691. */
  692. void OpenContain::exitObjectViaDoor( Object *exitObj, ExitDoorType exitDoor )
  693. {
  694. DEBUG_ASSERTCRASH(exitDoor == DOOR_1, ("multiple exit doors not supported here"));
  695. removeFromContain( exitObj );
  696. Object *me = getObject();
  697. m_doorCloseCountdown = getOpenContainModuleData()->m_doorOpenTime;
  698. if (m_doorCloseCountdown)
  699. {
  700. // srj sez: only diddle the doors if this countdown is nonzero.
  701. // this allows us to prevent this module from messing with the doors
  702. // at all, which is required for DeliverPayloadAIUpdate.
  703. /// @todo srj -- for now, OpenContain assumes at most one door
  704. me->clearAndSetModelConditionState( MODELCONDITION_DOOR_1_CLOSING, MODELCONDITION_DOOR_1_OPENING );
  705. }
  706. Int numberExits = getOpenContainModuleData()->m_numberOfExitPaths;
  707. if( numberExits > 0 )
  708. {
  709. // We have ExitStart/End specified in art to use when we kick people out. If >1, then
  710. // we have many and we need to decide which one to use.
  711. AsciiString startBone("ExitStart");
  712. AsciiString endBone("ExitEnd");
  713. Coord3D startPosition;
  714. Coord3D endPosition;
  715. if( numberExits > 1 )
  716. {
  717. char suffix[8];
  718. itoa(m_whichExitPath, suffix, 10);
  719. if( m_whichExitPath < 10 )
  720. {
  721. startBone.concat('0');
  722. endBone.concat('0');
  723. }
  724. m_whichExitPath = (m_whichExitPath % numberExits) + 1;// To cycle from 1 to n
  725. startBone.concat(suffix);
  726. endBone.concat(suffix);
  727. }
  728. me->getSingleLogicalBonePosition( startBone.str(), &startPosition, NULL );
  729. me->getSingleLogicalBonePosition( endBone.str(), &endPosition, NULL );
  730. //startPosition.x = startPosition.y = 0;
  731. Real exitAngle = me->getOrientation();
  732. PhysicsBehavior *physics = exitObj->getPhysics();
  733. // Can fall problem - When units exit from planes, they are airborne for a while as they drop.
  734. // Airborne units dont' do pathfinding, and this is fine.
  735. // However, amphibious transports unload 3 feet off the ground. This is enough to trigger the
  736. // airborne flag, and they don't pathfind. So they all unload at the same exact point, and life
  737. // is ugly. So temporarily, we turn off the allowToFall flag, so they aren't considered airborne,
  738. // and their final destination (adjustToPossibleDestination) considers them as pathfinding, so they
  739. // don't stack up. Then we turn the flag back, so if they are parachuting, they can fly down
  740. // on their parachute. jba.
  741. Bool canFall = false;
  742. if (physics)
  743. {
  744. canFall = physics->getAllowToFall();
  745. }
  746. exitObj->setPosition( &startPosition );
  747. exitObj->setOrientation( exitAngle );
  748. // Per JohnA: We need to set our layer to match our transports layer, or we'll try to pick a spot
  749. // on the ground.
  750. exitObj->setLayer( me->getLayer() );
  751. ///< @todo This really should be automatically wrapped up in an activation sequence for objects in general
  752. // tell the AI about it
  753. AIUpdateInterface *ai = exitObj->getAI();
  754. AIUpdateInterface *myAi = me->getAI();
  755. TheAI->pathfinder()->addObjectToPathfindMap( exitObj );
  756. if (ai)
  757. {
  758. if (myAi && myAi->isIdle() && me->isKindOf(KINDOF_VEHICLE)) {
  759. TheAI->pathfinder()->removeUnitFromPathfindMap(me);
  760. TheAI->pathfinder()->updatePos(me, me->getPosition());
  761. TheAI->pathfinder()->updateGoal(me, me->getPosition(), TheTerrainLogic->getLayerForDestination(me->getPosition()));
  762. }
  763. ai->ignoreObstacle(NULL);
  764. // The units often come out at the same position, and need to ignore collisions briefly
  765. // as they move out. jba.
  766. ai->setIgnoreCollisionTime(LOGICFRAMES_PER_SECOND);
  767. TheAI->pathfinder()->adjustToPossibleDestination(exitObj, ai->getLocomotorSet(), &endPosition);
  768. }
  769. std::vector<Coord3D> exitPath;
  770. exitPath.push_back(endPosition);
  771. exitPath.push_back(endPosition); // Do it twice, in case units stack up due to brief flying. jba.
  772. if (m_rallyPointExists) {
  773. exitPath.push_back(m_rallyPoint);
  774. }
  775. if( ai )
  776. {
  777. if (physics)
  778. {
  779. physics->setAllowToFall(false);
  780. }
  781. ai->aiFollowPath( &exitPath, me, CMD_FROM_AI );
  782. TheAI->pathfinder()->updateGoal(exitObj, &endPosition, TheTerrainLogic->getLayerForDestination(&endPosition));
  783. }
  784. if (physics)
  785. {
  786. physics->setAllowToFall(canFall);
  787. }
  788. }
  789. else
  790. {
  791. ///< @todo This really should be automatically wrapped up in an activation sequence for objects in general
  792. // tell the AI about it
  793. TheAI->pathfinder()->addObjectToPathfindMap( exitObj );
  794. }
  795. }
  796. // ------------------------------------------------------------------------------------------------
  797. /**
  798. This call is used to evacuate units from a tunnel network via a persistent state (guard tunnel networ).
  799. The main difference between this and exitObjectViaDoor is that this exit doesn't change the current
  800. ai state, so the guard state doesn't get blown away. jba.
  801. */
  802. void OpenContain::exitObjectInAHurry( Object *exitObj )
  803. {
  804. removeFromContain( exitObj );
  805. Object *me = getObject();
  806. m_doorCloseCountdown = getOpenContainModuleData()->m_doorOpenTime;
  807. if (m_doorCloseCountdown)
  808. {
  809. // srj sez: only diddle the doors if this countdown is nonzero.
  810. // this allows us to prevent this module from messing with the doors
  811. // at all, which is required for DeliverPayloadAIUpdate.
  812. /// @todo srj -- for now, OpenContain assumes at most one door
  813. me->clearAndSetModelConditionState( MODELCONDITION_DOOR_1_CLOSING, MODELCONDITION_DOOR_1_OPENING );
  814. }
  815. Int numberExits = getOpenContainModuleData()->m_numberOfExitPaths;
  816. if( numberExits > 0 )
  817. {
  818. // We have ExitStart/End specified in art to use when we kick people out. If >1, then
  819. // we have many and we need to decide which one to use.
  820. AsciiString startBone("ExitStart");
  821. AsciiString endBone("ExitEnd");
  822. Coord3D startPosition;
  823. Coord3D endPosition;
  824. if( numberExits > 1 )
  825. {
  826. char suffix[8];
  827. itoa(m_whichExitPath, suffix, 10);
  828. if( m_whichExitPath < 10 )
  829. {
  830. startBone.concat('0');
  831. endBone.concat('0');
  832. }
  833. m_whichExitPath = (m_whichExitPath % numberExits) + 1;// To cycle from 1 to n
  834. startBone.concat(suffix);
  835. endBone.concat(suffix);
  836. }
  837. me->getSingleLogicalBonePosition( startBone.str(), &startPosition, NULL );
  838. me->getSingleLogicalBonePosition( endBone.str(), &endPosition, NULL );
  839. //startPosition.x = startPosition.y = 0;
  840. Real exitAngle = me->getOrientation();
  841. exitObj->setPosition( &startPosition );
  842. exitObj->setOrientation( exitAngle );
  843. // Per JohnA: We need to set our layer to match our transports layer, or we'll try to pick a spot
  844. // on the ground.
  845. exitObj->setLayer( me->getLayer() );
  846. std::vector<Coord3D> exitPath;
  847. exitPath.push_back(endPosition);
  848. AIUpdateInterface *ai = exitObj->getAI();
  849. AIUpdateInterface *myAi = me->getAI();
  850. TheAI->pathfinder()->addObjectToPathfindMap( exitObj );
  851. if (ai)
  852. {
  853. if (myAi && myAi->isIdle() && me->isKindOf(KINDOF_VEHICLE)) {
  854. TheAI->pathfinder()->removeUnitFromPathfindMap(me);
  855. TheAI->pathfinder()->updatePos(me, me->getPosition());
  856. TheAI->pathfinder()->updateGoal(me, me->getPosition(), TheTerrainLogic->getLayerForDestination(me->getPosition()));
  857. }
  858. ai->ignoreObstacle(NULL);
  859. // The units often come out at the same position, and need to ignore collisions briefly
  860. // as they move out. jba.
  861. ai->setIgnoreCollisionTime(LOGICFRAMES_PER_SECOND);
  862. TheAI->pathfinder()->adjustToPossibleDestination(exitObj, ai->getLocomotorSet(), &endPosition);
  863. }
  864. exitPath.push_back(endPosition);
  865. if (m_rallyPointExists) {
  866. exitPath.push_back(m_rallyPoint);
  867. }
  868. if( ai )
  869. {
  870. ai->doQuickExit( &exitPath );
  871. TheAI->pathfinder()->updateGoal(exitObj, &endPosition, TheTerrainLogic->getLayerForDestination(&endPosition));
  872. }
  873. }
  874. else
  875. {
  876. ///< @todo This really should be automatically wrapped up in an activation sequence for objects in general
  877. // tell the AI about it
  878. TheAI->pathfinder()->addObjectToPathfindMap( exitObj );
  879. }
  880. }
  881. //-------------------------------------------------------------------------------------------------
  882. Bool OpenContain::isPassengerAllowedToFire() const
  883. {
  884. const OpenContainModuleData *modData = getOpenContainModuleData();
  885. if( ! modData->m_passengersAllowedToFire )
  886. return FALSE;// Just no, no matter what.
  887. // If we are ourselves contained, our passengers need to check with them if they get past us
  888. if( getObject()->getContainedBy() != NULL )
  889. return getObject()->getContainedBy()->getContain()->isPassengerAllowedToFire();
  890. return TRUE;// We say yes, and we are not inside something.
  891. }
  892. //-------------------------------------------------------------------------------------------------
  893. /** Check to see if our internal knowledge of the artwork matches the current state of
  894. * the model being displayed. When changes occur (from changing the time of day or going
  895. * to a new damage state, we will want to redeploy all our occupants to be at new fire
  896. * points that are reflected in the new artwork */
  897. //-------------------------------------------------------------------------------------------------
  898. void OpenContain::monitorConditionChanges( void )
  899. {
  900. Drawable *draw = getObject()->getDrawable();
  901. Bool stateChanged = false;
  902. ModelConditionFlags currCondition;
  903. if( draw )
  904. {
  905. currCondition = draw->getModelConditionFlags();
  906. if(currCondition != m_conditionState )
  907. stateChanged = TRUE;
  908. }
  909. // on a state change we should redeploy our occupants
  910. if( stateChanged )
  911. {
  912. // just yank all objects from the fire points and redeploy
  913. redeployOccupants();
  914. // record this as our current and up to date state
  915. m_conditionState = currCondition;
  916. }
  917. } // end monitorConditionChanges
  918. // ------------------------------------------------------------------------------------------------
  919. // ------------------------------------------------------------------------------------------------
  920. void OpenContain::redeployOccupants( void )
  921. {
  922. //
  923. // because the state has changed, we will must give the deploy logic the opportunity
  924. // to look for a new set of bones in the art ... so if we had them flagged as not
  925. // having any before (for optimization reasons) we want to check at least one more
  926. // time again
  927. //
  928. m_noFirePointsInArt = false;
  929. m_firePointStart = -1;
  930. m_firePointNext = 0;
  931. m_firePointSize = 0; // 0 here will cause a re-lookup in the new art
  932. //
  933. // redeploy the occupants, note that we're starting at the last occupant and going
  934. // backwards to the start ... that reflects the order in which they actually went
  935. // into the building since all list building constructs at the head
  936. //
  937. for (ContainedItemsList::const_reverse_iterator it = getContainList().rbegin(); it != getContainList().rend(); ++it)
  938. {
  939. putObjAtNextFirePoint( *it );
  940. }
  941. } // end redeployOccupants
  942. //-------------------------------------------------------------------------------------------------
  943. /** Place the object at the 3D position of the next fire point to use */
  944. //-------------------------------------------------------------------------------------------------
  945. void OpenContain::putObjAtNextFirePoint( Object *obj )
  946. {
  947. //
  948. // first, if we need to load the 3D point data from the art do so, if we've already
  949. // determined there is no firepoints in the current art we will avoid searching for
  950. // it all the time
  951. //
  952. if( m_firePointSize == 0 && m_noFirePointsInArt == false )
  953. {
  954. m_firePointSize = getObject()->getMultiLogicalBonePosition("FIREPOINT", MAX_FIRE_POINTS, NULL, m_firePoints, TRUE );
  955. //
  956. // if there is still no firepoints in the art, we'll set a flag so that we don't
  957. // ever go through the art stuff again
  958. //
  959. if( m_firePointSize == 0 )
  960. m_noFirePointsInArt = TRUE;
  961. } // end if
  962. //
  963. // if there are no fire points in the art we just put the object at the center
  964. // of the object
  965. //
  966. if( m_noFirePointsInArt == TRUE )
  967. {
  968. const Coord3D *pos = getObject()->getPosition();
  969. obj->setPosition( pos );
  970. return;
  971. } // end if
  972. // get the position
  973. Matrix3D matrix;
  974. if( getOpenContainModuleData()->m_passengersInTurret )
  975. {
  976. // If our passengers are in our turret, we need to recompute the Matrix.
  977. AsciiString firepoint("FIREPOINT");
  978. char suffix[8];
  979. itoa( m_firePointNext + 1, suffix, 10 );//+1 from bone names starting at 1, not zero like my array
  980. if( m_firePointNext < 10 )
  981. {
  982. firepoint.concat('0');
  983. }
  984. firepoint.concat(suffix);
  985. getObject()->getSingleLogicalBonePositionOnTurret(TURRET_MAIN, firepoint.str(), NULL, &matrix );
  986. }
  987. else
  988. {
  989. matrix = m_firePoints[ m_firePointNext ];
  990. }
  991. Vector3 vectorPos = matrix.Get_Translation();
  992. Coord3D pos;
  993. pos.set( vectorPos.X, vectorPos.Y, vectorPos.Z );
  994. // set the object position
  995. if( isEnclosingContainerFor( obj ) )
  996. obj->setPosition( &pos );
  997. else
  998. obj->setTransformMatrix( &matrix );//Only do everything if it matters
  999. // increment the next firepoint to use ... make sure to wrap if we need to
  1000. m_firePointNext++;
  1001. if( m_firePointNext >= m_firePointSize )
  1002. m_firePointNext = 0;
  1003. } // end putObjAtNextFirePoint
  1004. //-------------------------------------------------------------------------------------------------
  1005. /**
  1006. this is used for containers that must do something to allow people to enter or exit...
  1007. eg, land (for Chinook), open door (whatever)... it's called with wants=WANTS_TO_ENTER
  1008. when something is in the enter state, and wants=WANTS_NOTHING when the unit has
  1009. either entered, or given up...
  1010. */
  1011. void OpenContain::onObjectWantsToEnterOrExit(Object* obj, ObjectEnterExitType wants)
  1012. {
  1013. if (obj == NULL)
  1014. return;
  1015. ObjectID id = obj->getID();
  1016. if (wants == WANTS_NEITHER)
  1017. {
  1018. ObjectEnterExitMap::iterator it = m_objectEnterExitInfo.find(id);
  1019. if (it != m_objectEnterExitInfo.end())
  1020. m_objectEnterExitInfo.erase(it);
  1021. }
  1022. else
  1023. {
  1024. m_objectEnterExitInfo[id] = wants;
  1025. }
  1026. }
  1027. //-------------------------------------------------------------------------------------------------
  1028. void OpenContain::pruneDeadWanters()
  1029. {
  1030. for (ObjectEnterExitMap::iterator it = m_objectEnterExitInfo.begin(); it != m_objectEnterExitInfo.end(); /*++it*/)
  1031. {
  1032. ObjectID id = (*it).first;
  1033. Object* obj = TheGameLogic->findObjectByID(id);
  1034. if (obj == NULL || obj->isEffectivelyDead())
  1035. {
  1036. ObjectEnterExitMap::iterator tmp = it;
  1037. ++it;
  1038. m_objectEnterExitInfo.erase(tmp);
  1039. }
  1040. else
  1041. {
  1042. ++it;
  1043. }
  1044. }
  1045. }
  1046. //-------------------------------------------------------------------------------------------------
  1047. void OpenContain::markAllPassengersDetected( )
  1048. {
  1049. for( ContainedItemsList::iterator it = m_containList.begin(); it != m_containList.end(); )
  1050. {
  1051. // save the rider...
  1052. Object* rider = *it;
  1053. // incr the iterator BEFORE calling the func (if the func removes the rider,
  1054. // the iterator becomes invalid)
  1055. ++it;
  1056. // call it
  1057. if( rider->isKindOf( KINDOF_STEALTH_GARRISON ) )
  1058. {
  1059. static const NameKeyType key_StealthUpdate = NAMEKEY( "StealthUpdate" );
  1060. StealthUpdate* stealth = (StealthUpdate*)rider->findUpdateModule( key_StealthUpdate );
  1061. if( stealth )
  1062. {
  1063. stealth->markAsDetected();
  1064. }
  1065. }
  1066. }
  1067. }
  1068. //-------------------------------------------------------------------------------------------------
  1069. void OpenContain::onSelling()
  1070. {
  1071. // An OpenContain tells everyone to leave.
  1072. orderAllPassengersToExit(CMD_FROM_AI);
  1073. }
  1074. //-------------------------------------------------------------------------------------------------
  1075. void OpenContain::orderAllPassengersToExit( CommandSourceType commandSource )
  1076. {
  1077. for( ContainedItemsList::const_iterator it = getContainedItemsList()->begin(); it != getContainedItemsList()->end(); )
  1078. {
  1079. // save the rider...
  1080. Object* rider = *it;
  1081. // incr the iterator BEFORE calling the func (if the func removes the rider,
  1082. // the iterator becomes invalid)
  1083. ++it;
  1084. // call it
  1085. if( rider->getAI() )
  1086. rider->getAI()->aiExit( getObject(), commandSource );
  1087. }
  1088. }
  1089. //-------------------------------------------------------------------------------------------------
  1090. void OpenContain::processDamageToContained()
  1091. {
  1092. const ContainedItemsList* items = getContainedItemsList();
  1093. if( items )
  1094. {
  1095. ContainedItemsList::const_iterator it;
  1096. it = items->begin();
  1097. while( *it )
  1098. {
  1099. Object *object = *it;
  1100. //Advance to the next iterator before we apply the damage.
  1101. //It's possible that the damage will kill the unit and foobar
  1102. //the iterator list.
  1103. ++it;
  1104. //Calculate the damage to be inflicted on each unit.
  1105. Real damage = object->getBodyModule()->getMaxHealth() * getOpenContainModuleData()->m_damagePercentageToUnits;
  1106. DamageInfo damageInfo;
  1107. damageInfo.in.m_damageType = DAMAGE_UNRESISTABLE;
  1108. damageInfo.in.m_deathType = DEATH_BURNED;
  1109. damageInfo.in.m_sourceID = getObject()->getID();
  1110. damageInfo.in.m_amount = damage;
  1111. object->attemptDamage( &damageInfo );
  1112. if( !object->isEffectivelyDead() && getOpenContainModuleData()->m_damagePercentageToUnits == 1.0f )
  1113. object->kill(); // in case we are carrying flame proof troops we have been asked to kill
  1114. }
  1115. }
  1116. }
  1117. //-------------------------------------------------------------------------------------------------
  1118. Bool OpenContain::isEnclosingContainerFor( const Object * ) const
  1119. {
  1120. return TRUE;
  1121. }
  1122. //-------------------------------------------------------------------------------------------------
  1123. Bool OpenContain::hasObjectsWantingToEnterOrExit() const
  1124. {
  1125. return !m_objectEnterExitInfo.empty();
  1126. }
  1127. //-------------------------------------------------------------------------------------------------
  1128. void OpenContain::setRallyPoint( const Coord3D *pos )
  1129. {
  1130. m_rallyPoint = *pos;
  1131. m_rallyPointExists = true;
  1132. }
  1133. //-------------------------------------------------------------------------------------------------
  1134. const Coord3D *OpenContain::getRallyPoint( void ) const
  1135. {
  1136. if (m_rallyPointExists)
  1137. return &m_rallyPoint;
  1138. return NULL;
  1139. }
  1140. //-------------------------------------------------------------------------------------------------
  1141. Bool OpenContain::getNaturalRallyPoint( Coord3D& rallyPoint, Bool offset ) const
  1142. {
  1143. Int numberExits = getOpenContainModuleData()->m_numberOfExitPaths;
  1144. if( numberExits > 0 )
  1145. {
  1146. AsciiString endBone("ExitEnd");
  1147. if( numberExits > 1 )
  1148. {
  1149. endBone.concat("01");
  1150. }
  1151. getObject()->getSingleLogicalBonePosition( endBone.str(), &rallyPoint, NULL );
  1152. }
  1153. else
  1154. {
  1155. rallyPoint = *getObject()->getPosition();
  1156. }
  1157. return TRUE;
  1158. }
  1159. // ------------------------------------------------------------------------------------------------
  1160. /** CRC */
  1161. // ------------------------------------------------------------------------------------------------
  1162. void OpenContain::crc( Xfer *xfer )
  1163. {
  1164. // extend base class
  1165. UpdateModule::crc( xfer );
  1166. } // end crc
  1167. // ------------------------------------------------------------------------------------------------
  1168. /** Xfer method
  1169. * Version Info:
  1170. * 1: Initial version */
  1171. // ------------------------------------------------------------------------------------------------
  1172. void OpenContain::xfer( Xfer *xfer )
  1173. {
  1174. // version
  1175. const XferVersion currentVersion = 1;
  1176. XferVersion version = currentVersion;
  1177. xfer->xferVersion( &version, currentVersion );
  1178. // extend base class
  1179. UpdateModule::xfer( xfer );
  1180. // contain list data
  1181. ObjectID objectID;
  1182. if( xfer->getXferMode() == XFER_SAVE )
  1183. {
  1184. // contain list size
  1185. xfer->xferUnsignedInt( &m_containListSize );
  1186. for( ContainedItemsList::const_iterator it = m_containList.begin(); it != m_containList.end(); ++it )
  1187. {
  1188. objectID = (*it)->getID();
  1189. xfer->xferObjectID( &objectID );
  1190. } // end for, it
  1191. } // end if, save
  1192. else
  1193. {
  1194. // the containment list should be emtpy at this time
  1195. if( m_containList.empty() == FALSE )
  1196. {
  1197. #if 1
  1198. // srj sez: yeah, it SHOULD be, but a few rogue things come into existence (via their ctor) preloaded
  1199. // with stuff (eg, "CabooseFullOfTerrorists"). so just nuke 'em here. sigh...
  1200. m_containListSize = 0;
  1201. for (ContainedItemsList::iterator it = m_containList.begin(); it != m_containList.end(); /*++it*/ )
  1202. {
  1203. // clear as go along, otherwise the obj will try to remove itself, munging things
  1204. Object* tmp = *it;
  1205. it = m_containList.erase(it);
  1206. TheGameLogic->destroyObject(tmp);
  1207. }
  1208. m_containList.clear();
  1209. #else
  1210. DEBUG_CRASH(( "OpenContain::xfer - Contain list should be empty before load but is not\n" ));
  1211. throw SC_INVALID_DATA;
  1212. #endif
  1213. } // end if
  1214. // contain list size
  1215. xfer->xferUnsignedInt( &m_containListSize );
  1216. // read all contained items
  1217. for( UnsignedInt i = 0; i < m_containListSize; ++i )
  1218. {
  1219. // read id
  1220. xfer->xferObjectID( &objectID );
  1221. // put on list for later processing once objects are loaded
  1222. m_xferContainIDList.push_back( objectID );
  1223. } // end for, i
  1224. } // end else, load
  1225. // player entered mask
  1226. xfer->xferUser( &m_playerEnteredMask, sizeof( PlayerMaskType ) );
  1227. // last unload sound frame
  1228. xfer->xferUnsignedInt( &m_lastUnloadSoundFrame );
  1229. // last load sound frame
  1230. xfer->xferUnsignedInt( &m_lastLoadSoundFrame );
  1231. // stealth units contained
  1232. xfer->xferUnsignedInt( &m_stealthUnitsContained );
  1233. // door close countdown
  1234. xfer->xferUnsignedInt( &m_doorCloseCountdown );
  1235. // conditionstate
  1236. m_conditionState.xfer( xfer );
  1237. // fire points
  1238. xfer->xferUser( &m_firePoints, sizeof( Matrix3D ) * MAX_FIRE_POINTS );
  1239. // fire point start
  1240. xfer->xferInt( &m_firePointStart );
  1241. // fire point next
  1242. xfer->xferInt( &m_firePointNext );
  1243. // fire point size
  1244. xfer->xferInt( &m_firePointSize );
  1245. // no fire points in art
  1246. xfer->xferBool( &m_noFirePointsInArt );
  1247. // rally point
  1248. xfer->xferCoord3D( &m_rallyPoint );
  1249. // rally point exists
  1250. xfer->xferBool( &m_rallyPointExists );
  1251. // enter exit map info
  1252. UnsignedShort enterExitCount = m_objectEnterExitInfo.size();
  1253. xfer->xferUnsignedShort( &enterExitCount );
  1254. ObjectEnterExitType enterExitType;
  1255. if( xfer->getXferMode() == XFER_SAVE )
  1256. {
  1257. ObjectEnterExitMap::const_iterator it;
  1258. for( it = m_objectEnterExitInfo.begin(); it != m_objectEnterExitInfo.end(); ++it )
  1259. {
  1260. // object id
  1261. objectID = (*it).first;
  1262. xfer->xferObjectID( &objectID );
  1263. // enter exit type
  1264. enterExitType = (*it).second;
  1265. xfer->xferUser( &enterExitType, sizeof( ObjectEnterExitType ) );
  1266. } // end for, it
  1267. } // end if, save
  1268. else
  1269. {
  1270. // the map should be emtpy now
  1271. if( m_objectEnterExitInfo.empty() == FALSE )
  1272. {
  1273. DEBUG_CRASH(( "OpenContain::xfer - m_objectEnterExitInfo should be empty, but is not\n" ));
  1274. throw SC_INVALID_DATA;
  1275. } // end if
  1276. // read all data items
  1277. for( UnsignedShort i = 0; i < enterExitCount; ++i )
  1278. {
  1279. // object id
  1280. xfer->xferObjectID( &objectID );
  1281. // enter exit type
  1282. xfer->xferUser( &enterExitType, sizeof( ObjectEnterExitType ) );
  1283. // assign
  1284. m_objectEnterExitInfo[ objectID ] = enterExitType;
  1285. } // end for, i
  1286. } // end else, load
  1287. // which exit path
  1288. xfer->xferInt( &m_whichExitPath );
  1289. } // end xfer
  1290. // ------------------------------------------------------------------------------------------------
  1291. /** Load post process */
  1292. // ------------------------------------------------------------------------------------------------
  1293. void OpenContain::loadPostProcess( void )
  1294. {
  1295. Object *us = getObject();
  1296. // extend base class
  1297. UpdateModule::loadPostProcess();
  1298. // the containment list should be emtpy at this time
  1299. if( m_containList.empty() == FALSE )
  1300. {
  1301. DEBUG_CRASH(( "OpenContain::loadPostProcess - Contain list should be empty before load but is not\n" ));
  1302. throw SC_INVALID_DATA;
  1303. } // end if
  1304. // turn the contained id list into actual object pointers in the contain list
  1305. Object *obj;
  1306. std::list<ObjectID>::const_iterator idIt;
  1307. for( idIt = m_xferContainIDList.begin(); idIt != m_xferContainIDList.end(); ++idIt )
  1308. {
  1309. // find this object
  1310. obj = TheGameLogic->findObjectByID( *idIt );
  1311. // sanity
  1312. if( obj == NULL )
  1313. {
  1314. DEBUG_CRASH(( "OpenContain::loadPostProcess - Unable to find object to put on contain list\n" ));
  1315. throw SC_INVALID_DATA;
  1316. } // end if
  1317. // put object on list
  1318. m_containList.push_back( obj );
  1319. // remove this object from the world if we need to
  1320. if( isEnclosingContainerFor( obj ) )
  1321. addOrRemoveObjFromWorld( obj, FALSE );
  1322. // record in the object who we are contained by
  1323. obj->friend_setContainedBy( us );
  1324. } // end for, idIt
  1325. // sanity
  1326. DEBUG_ASSERTCRASH( m_containListSize == m_containList.size(),
  1327. ("OpenContain::loadPostProcess - contain list count mismatch\n") );
  1328. // clear the list as we don't need it anymore
  1329. m_xferContainIDList.clear();
  1330. } // end loadPostProcess