OverlordContain.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638
  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: OverlordContain.cpp ////////////////////////////////////////////////////////////////////////
  24. // Author: Graham Smallwood, September, 2002
  25. // Desc: Contain module that acts as transport normally, but when full it redirects queries to the first passenger
  26. // All of this redirection stuff makes it so that while I am normally a transport
  27. // for Overlord subObjects, once I have a passenger, _I_ become a transport of their type.
  28. // So, the answer to this question depends on if it is my passenger asking, or theirs.
  29. // As always, I can't use convience functions that get redirected on a ? like this.
  30. ///////////////////////////////////////////////////////////////////////////////////////////////////
  31. // USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
  32. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  33. #include "Common/Player.h"
  34. #include "Common/Xfer.h"
  35. #include "Common/ThingTemplate.h"
  36. #include "Common/ThingFactory.h"
  37. #include "GameClient/ControlBar.h"
  38. #include "GameClient/Drawable.h"
  39. #include "GameLogic/ExperienceTracker.h"
  40. #include "GameLogic/Module/BodyModule.h"
  41. #include "GameLogic/Module/OverlordContain.h"
  42. #include "GameLogic/Object.h"
  43. #include "GameLogic/PartitionManager.h"
  44. #ifdef _INTERNAL
  45. // for occasional debugging...
  46. //#pragma optimize("", off)
  47. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  48. #endif
  49. // ------------------------------------------------------------------------------------------------
  50. // ------------------------------------------------------------------------------------------------
  51. OverlordContainModuleData::OverlordContainModuleData()
  52. {
  53. // m_initialPayload.count = 0;
  54. m_experienceSinkForRider = TRUE;
  55. }
  56. // ------------------------------------------------------------------------------------------------
  57. // ------------------------------------------------------------------------------------------------
  58. void OverlordContainModuleData::buildFieldParse(MultiIniFieldParse& p)
  59. {
  60. TransportContainModuleData::buildFieldParse(p);
  61. static const FieldParse dataFieldParse[] =
  62. {
  63. { "PayloadTemplateName", INI::parseAsciiStringVectorAppend, NULL, offsetof(OverlordContainModuleData, m_payloadTemplateNameData) },
  64. { "ExperienceSinkForRider", INI::parseBool, NULL, offsetof(OverlordContainModuleData, m_experienceSinkForRider) },
  65. { 0, 0, 0, 0 }
  66. };
  67. p.add(dataFieldParse);
  68. }
  69. // ------------------------------------------------------------------------------------------------
  70. // ------------------------------------------------------------------------------------------------
  71. //void OverlordContainModuleData::parseInitialPayload( INI* ini, void *instance, void *store, const void* /*userData*/ )
  72. //{
  73. // OverlordContainModuleData* self = (OverlordContainModuleData*)instance;
  74. // const char* name = ini->getNextToken();
  75. // const char* countStr = ini->getNextTokenOrNull();
  76. // Int count = countStr ? INI::scanInt(countStr) : 1;
  77. // self->m_initialPayload.name.set(name);
  78. // self->m_initialPayload.count = count;
  79. //}
  80. // ------------------------------------------------------------------------------------------------
  81. // ------------------------------------------------------------------------------------------------
  82. OverlordContain::OverlordContain( Thing *thing, const ModuleData *moduleData ) :
  83. TransportContain( thing, moduleData )
  84. {
  85. m_redirectionActivated = FALSE;
  86. m_payloadCreated = FALSE;
  87. }
  88. //-------------------------------------------------------------------------------------------------
  89. //-------------------------------------------------------------------------------------------------
  90. OverlordContain::~OverlordContain( void )
  91. {
  92. }
  93. void OverlordContain::onObjectCreated( void )
  94. {
  95. OverlordContain::createPayload();
  96. }
  97. //-------------------------------------------------------------------------------------------------
  98. // ------------------------------------------------------------------------------------------------
  99. void OverlordContain::createPayload()
  100. {
  101. OverlordContainModuleData* self = (OverlordContainModuleData*)getOverlordContainModuleData();
  102. // Any number of different passengers can be loaded here at init time
  103. Object* object = getObject();
  104. ContainModuleInterface *contain = object->getContain();
  105. if( contain )
  106. {
  107. contain->enableLoadSounds( FALSE );
  108. TemplateNameList list = self->m_payloadTemplateNameData;
  109. TemplateNameIterator iter = list.begin();
  110. while ( iter != list.end() )
  111. {
  112. const ThingTemplate* temp = TheThingFactory->findTemplate( *iter );
  113. if (temp)
  114. {
  115. Object* payload = TheThingFactory->newObject( temp, object->getTeam() );
  116. if( contain->isValidContainerFor( payload, true ) )
  117. {
  118. contain->addToContain( payload );
  119. }
  120. else
  121. {
  122. DEBUG_CRASH( ( "OverlordContain::createPayload: %s is full, or not valid for the payload %s!", object->getName().str(), self->m_initialPayload.name.str() ) );
  123. }
  124. }
  125. ++iter;
  126. }
  127. contain->enableLoadSounds( TRUE );
  128. } // endif contain
  129. m_payloadCreated = TRUE;
  130. }
  131. // ------------------------------------------------------------------------------------------------
  132. //-------------------------------------------------------------------------------------------------
  133. void OverlordContain::onBodyDamageStateChange( const DamageInfo* damageInfo,
  134. BodyDamageType oldState,
  135. BodyDamageType newState) ///< state change callback
  136. {
  137. // I can't use any convienience functions, as they will all get routed to the bunker I may carry.
  138. // I want just me.
  139. // Oh, and I don't want this function trying to do death. That is more complicated and will be handled
  140. // on my death.
  141. if( newState != BODY_RUBBLE && m_containListSize == 1 )
  142. {
  143. Object *myGuy = m_containList.front();
  144. myGuy->getBodyModule()->setDamageState( newState );
  145. }
  146. }
  147. //-------------------------------------------------------------------------------------------------
  148. //-------------------------------------------------------------------------------------------------
  149. ContainModuleInterface *OverlordContain::getRedirectedContain() const
  150. {
  151. // Naturally, I can not use a redirectible convienience function
  152. // to answer if I am redirecting yet.
  153. // If I am empty, say no.
  154. if( m_containListSize < 1 )
  155. return NULL;
  156. if( !m_redirectionActivated )
  157. return NULL;// Shut off early to allow death to happen without my bunker having
  158. // trouble finding me to say goodbye as messages get sucked up the pipe to him.
  159. Object *myGuy = m_containList.front();
  160. if( myGuy )
  161. return myGuy->getContain();
  162. return NULL;// Or say no if they have no contain.
  163. }
  164. //-------------------------------------------------------------------------------------------------
  165. //-------------------------------------------------------------------------------------------------
  166. //-------------------------------------------------------------------------------------------------
  167. //-------------------------------------------------------------------------------------------------
  168. void OverlordContain::onDie( const DamageInfo *damageInfo )
  169. {
  170. // Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
  171. if( getRedirectedContain() == NULL )
  172. {
  173. TransportContain::onDie( damageInfo );
  174. return;
  175. }
  176. //Everything is fine if I am empty or carrying a regular guy. If I have a redirected contain
  177. // set up, then I need to handle the order of death explicitly, or things will become confused
  178. // when I stop redirecting in the middle of the process. Or I will get confused as my commands
  179. // get sucked up the pipe.
  180. // So this is an extend that lets me control the order of death.
  181. deactivateRedirectedContain();
  182. Object *myGuy = m_containList.front();
  183. myGuy->kill();
  184. TransportContain::onDie( damageInfo );
  185. }
  186. //-------------------------------------------------------------------------------------------------
  187. void OverlordContain::onDelete( void )
  188. {
  189. // Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
  190. if( getRedirectedContain() == NULL )
  191. {
  192. TransportContain::onDelete( );
  193. return;
  194. }
  195. // Without my throwing the redirect switch, teardown deletion will get confused and fire off a bunch of asserts
  196. getRedirectedContain()->removeAllContained();
  197. deactivateRedirectedContain();
  198. removeAllContained();
  199. TransportContain::onDelete( );
  200. }
  201. // ------------------------------------------------------------------------------------------------
  202. void OverlordContain::onCapture( Player *oldOwner, Player *newOwner )
  203. {
  204. if( m_containListSize < 1 )
  205. return;
  206. // Need to capture our specific rider. He will then kick passengers out if he is a Transport
  207. Object *myGuy = m_containList.front();
  208. myGuy->setTeam( newOwner->getDefaultTeam() );
  209. }
  210. //-------------------------------------------------------------------------------------------------
  211. Bool OverlordContain::isGarrisonable() const
  212. {
  213. if( getRedirectedContain() == NULL )
  214. return FALSE;
  215. return getRedirectedContain()->isGarrisonable();
  216. }
  217. //-------------------------------------------------------------------------------------------------
  218. Bool OverlordContain::isKickOutOnCapture()
  219. {
  220. if( getRedirectedContain() == NULL )
  221. return FALSE;// Me the Overlord doesn't want to
  222. return getRedirectedContain()->isKickOutOnCapture();
  223. }
  224. //-------------------------------------------------------------------------------------------------
  225. void OverlordContain::addToContainList( Object *obj )
  226. {
  227. // Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
  228. if( getRedirectedContain() == NULL )
  229. {
  230. TransportContain::addToContainList( obj );
  231. return;
  232. }
  233. getRedirectedContain()->addToContainList( obj );
  234. }
  235. //-------------------------------------------------------------------------------------------------
  236. void OverlordContain::addToContain( Object *obj )
  237. {
  238. // Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
  239. if( getRedirectedContain() == NULL )
  240. {
  241. TransportContain::addToContain( obj );
  242. return;
  243. }
  244. getRedirectedContain()->addToContain( obj );
  245. }
  246. //-------------------------------------------------------------------------------------------------
  247. /** Remove 'obj' from the m_containList of objects in this module.
  248. * This will trigger an onRemoving event for the object that this module
  249. * is a part of and an onRemovedFrom event for the object being removed */
  250. //-------------------------------------------------------------------------------------------------
  251. void OverlordContain::removeFromContain( Object *obj, Bool exposeStealthUnits )
  252. {
  253. // Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
  254. if( getRedirectedContain() == NULL )
  255. {
  256. TransportContain::removeFromContain( obj, exposeStealthUnits );
  257. return;
  258. }
  259. getRedirectedContain()->removeFromContain( obj, exposeStealthUnits );
  260. }
  261. //-------------------------------------------------------------------------------------------------
  262. /** Remove all contained objects from the contained list */
  263. //-------------------------------------------------------------------------------------------------
  264. void OverlordContain::removeAllContained( Bool exposeStealthUnits )
  265. {
  266. // Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
  267. if( getRedirectedContain() == NULL )
  268. {
  269. TransportContain::removeAllContained( exposeStealthUnits );
  270. return;
  271. }
  272. const ContainedItemsList *fullList = getRedirectedContain()->getContainedItemsList();
  273. Object *obj;
  274. ContainedItemsList::const_iterator it;
  275. it = (*fullList).begin();
  276. while( it != (*fullList).end() )
  277. {
  278. obj = *it;
  279. it++;
  280. removeFromContain( obj, exposeStealthUnits );
  281. }
  282. }
  283. //-------------------------------------------------------------------------------------------------
  284. /** Iterate the contained list and call the callback on each of the objects */
  285. //-------------------------------------------------------------------------------------------------
  286. void OverlordContain::iterateContained( ContainIterateFunc func, void *userData, Bool reverse )
  287. {
  288. // Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
  289. if( getRedirectedContain() == NULL )
  290. {
  291. TransportContain::iterateContained( func, userData, reverse );
  292. return;
  293. }
  294. getRedirectedContain()->iterateContained( func, userData, reverse );
  295. }
  296. //-------------------------------------------------------------------------------------------------
  297. void OverlordContain::onContaining( Object *obj, Bool wasSelected )
  298. {
  299. // Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
  300. if( getRedirectedContain() == NULL )
  301. {
  302. TransportContain::onContaining( obj, wasSelected );
  303. if ( obj->isKindOf( KINDOF_PORTABLE_STRUCTURE ) )
  304. {
  305. activateRedirectedContain();//Am now carrying something
  306. // And this contain style explicitly sucks XP from our little friend.
  307. if( getOverlordContainModuleData()->m_experienceSinkForRider && obj->getExperienceTracker() )
  308. obj->getExperienceTracker()->setExperienceSink(getObject()->getID());
  309. if ( obj->isKindOf( KINDOF_PORTABLE_STRUCTURE ) && getObject()->testStatus( OBJECT_STATUS_STEALTHED ) )
  310. {
  311. StealthUpdate *myStealth = obj->getStealth();
  312. if ( myStealth )
  313. {
  314. myStealth->receiveGrant( true );
  315. // note to anyone... once stealth is granted to this gattlingcannon ( or such )
  316. // let its own stealthupdate govern the allowedtostealth cases
  317. // a portable structure never gets removed, so...
  318. }
  319. }
  320. }
  321. return;
  322. }
  323. OpenContain::onContaining( obj, wasSelected );
  324. getRedirectedContain()->onContaining( obj, wasSelected );
  325. }
  326. //-------------------------------------------------------------------------------------------------
  327. void OverlordContain::killAllContained( void )
  328. {
  329. // This is a game call meant to clear actual passengers. We don't want it to kill our turret. That'd be wierd.
  330. if( getRedirectedContain() )
  331. {
  332. getRedirectedContain()->killAllContained();
  333. }
  334. }
  335. //-------------------------------------------------------------------------------------------------
  336. void OverlordContain::onRemoving( Object *obj )
  337. {
  338. // Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
  339. if( getRedirectedContain() == NULL )
  340. {
  341. TransportContain::onRemoving( obj );
  342. return;
  343. }
  344. OpenContain::onRemoving(obj);
  345. getRedirectedContain()->onRemoving( obj );
  346. }
  347. //-------------------------------------------------------------------------------------------------
  348. Bool OverlordContain::isValidContainerFor(const Object* obj, Bool checkCapacity) const
  349. {
  350. // Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
  351. if( getRedirectedContain() == NULL )
  352. return TransportContain::isValidContainerFor( obj, checkCapacity );
  353. return getRedirectedContain()->isValidContainerFor( obj, checkCapacity );
  354. }
  355. //-------------------------------------------------------------------------------------------------
  356. UnsignedInt OverlordContain::getContainCount() const
  357. {
  358. ContainModuleInterface* redir = getRedirectedContain();
  359. // Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
  360. if( redir == NULL )
  361. return TransportContain::getContainCount( );
  362. return redir->getContainCount();
  363. }
  364. //-------------------------------------------------------------------------------------------------
  365. Bool OverlordContain::getContainerPipsToShow(Int& numTotal, Int& numFull)
  366. {
  367. // Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
  368. if( getRedirectedContain() == NULL )
  369. {
  370. numTotal = 0;
  371. numFull = 0;
  372. return false;
  373. }
  374. else
  375. {
  376. return getRedirectedContain()->getContainerPipsToShow(numTotal, numFull);
  377. }
  378. }
  379. //-------------------------------------------------------------------------------------------------
  380. Int OverlordContain::getContainMax( ) const
  381. {
  382. // Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
  383. if( getRedirectedContain() == NULL )
  384. return TransportContain::getContainMax( );
  385. return getRedirectedContain()->getContainMax();
  386. }
  387. //-------------------------------------------------------------------------------------------------
  388. const ContainedItemsList* OverlordContain::getContainedItemsList() const
  389. {
  390. // Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
  391. if( getRedirectedContain() == NULL )
  392. return TransportContain::getContainedItemsList( );
  393. return getRedirectedContain()->getContainedItemsList();
  394. }
  395. //-------------------------------------------------------------------------------------------------
  396. Bool OverlordContain::isEnclosingContainerFor( const Object *obj ) const
  397. {
  398. // All of this redirection stuff makes it so that while I am normally a transport
  399. // for Overlord subObjects, once I have a passenger, _I_ become a transport of their type.
  400. // So, the answer to this question depends on if it is my passenger asking, or theirs.
  401. // As always, I can't use convience functions that get redirected on a ? like this.
  402. if( m_containListSize > 0 && obj == m_containList.front() )
  403. return FALSE;
  404. return TRUE;
  405. }
  406. //-------------------------------------------------------------------------------------------------
  407. Bool OverlordContain::isDisplayedOnControlBar() const
  408. {
  409. // Do you mean me the Overlord, or my behavior of passing stuff on to my passengers?
  410. if( getRedirectedContain() == NULL )
  411. return FALSE;//No need to call up inheritance, this is a module based question, and I say no.
  412. return getRedirectedContain()->isDisplayedOnControlBar();
  413. }
  414. //-------------------------------------------------------------------------------------------------
  415. const Object *OverlordContain::friend_getRider() const
  416. {
  417. // The draw order dependency bug for riders means that our draw module needs to cheat to get
  418. //around it. So this is another function that knows it is getting around redirection to ask
  419. // an Overlord specific function.
  420. if( m_containListSize > 0 )
  421. return m_containList.front();
  422. return NULL;
  423. }
  424. //-------------------------------------------------------------------------------------------------
  425. void OverlordContain::activateRedirectedContain()
  426. {
  427. m_redirectionActivated = TRUE;
  428. }
  429. //-------------------------------------------------------------------------------------------------
  430. void OverlordContain::deactivateRedirectedContain()
  431. {
  432. m_redirectionActivated = FALSE;
  433. }
  434. //-------------------------------------------------------------------------------------------------
  435. //-------------------------------------------------------------------------------------------------
  436. // if my object gets selected, then my visible passengers should, too
  437. // this gets called from
  438. void OverlordContain::clientVisibleContainedFlashAsSelected()
  439. {
  440. // THIS OVERRIDES GRAHAMS NASTY OVERRIDE THING
  441. // SO WE CAN FLASH THE PORTABLE BUNKER INSTEAD OF ITS OCCUPANTS
  442. const ContainedItemsList* items = TransportContain::getContainedItemsList();
  443. if( items )
  444. {
  445. ContainedItemsList::const_iterator it;
  446. it = items->begin();
  447. while( *it )
  448. {
  449. Object *object = *it;
  450. if ( object && object->isKindOf( KINDOF_PORTABLE_STRUCTURE ) )
  451. {
  452. Drawable *draw = object->getDrawable();
  453. if ( draw )
  454. {
  455. draw->flashAsSelected(); //WOW!
  456. }
  457. }
  458. ++it;
  459. }
  460. }
  461. }
  462. Bool OverlordContain::isPassengerAllowedToFire( ObjectID id ) const
  463. {
  464. Object *passenger = TheGameLogic->findObjectByID(id);
  465. if(passenger != NULL)
  466. {
  467. //only allow infantry, and turrets and such. no vehicles.
  468. if(passenger->isKindOf(KINDOF_INFANTRY) == FALSE && passenger->isKindOf(KINDOF_PORTABLE_STRUCTURE) == FALSE)
  469. return FALSE;
  470. }
  471. if ( getObject() && getObject()->getContainedBy() ) // nested containment voids firing, always
  472. return FALSE;
  473. return TransportContain::isPassengerAllowedToFire();
  474. }
  475. // ------------------------------------------------------------------------------------------------
  476. /** CRC */
  477. // ------------------------------------------------------------------------------------------------
  478. void OverlordContain::crc( Xfer *xfer )
  479. {
  480. // extend base class
  481. TransportContain::crc( xfer );
  482. } // end crc
  483. // ------------------------------------------------------------------------------------------------
  484. /** Xfer method
  485. * Version Info:
  486. * 1: Initial version */
  487. // ------------------------------------------------------------------------------------------------
  488. void OverlordContain::xfer( Xfer *xfer )
  489. {
  490. // version
  491. XferVersion currentVersion = 1;
  492. XferVersion version = currentVersion;
  493. xfer->xferVersion( &version, currentVersion );
  494. // extend base class
  495. TransportContain::xfer( xfer );
  496. // redirection activated
  497. xfer->xferBool( &m_redirectionActivated );
  498. } // end xfer
  499. // ------------------------------------------------------------------------------------------------
  500. /** Load post process */
  501. // ------------------------------------------------------------------------------------------------
  502. void OverlordContain::loadPostProcess( void )
  503. {
  504. // extend base class
  505. TransportContain::loadPostProcess();
  506. } // end loadPostProcess