OpenContain.cpp 58 KB

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