RiderChangeContain.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  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: RiderChangeContain.cpp //////////////////////////////////////////////////////////////////////
  24. // Author: Kris Morness, May 2003
  25. // Desc: Contain module for the combat bike (transport that switches units).
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #define DEFINE_LOCOMOTORSET_NAMES //Gain access to TheLocomotorSetNames[]
  30. #include "Common/Player.h"
  31. #include "Common/PlayerList.h"
  32. #include "Common/ThingTemplate.h"
  33. #include "Common/ThingFactory.h"
  34. #include "Common/Xfer.h"
  35. #include "GameClient/ControlBar.h"
  36. #include "GameClient/Drawable.h"
  37. #include "GameClient/InGameUI.h"
  38. #include "GameLogic/AI.h"
  39. #include "GameLogic/AIPathfind.h"
  40. #include "GameLogic/ExperienceTracker.h"
  41. #include "GameLogic/Object.h"
  42. #include "GameLogic/Locomotor.h"
  43. #include "GameLogic/Module/AIUpdate.h"
  44. #include "GameLogic/Module/BodyModule.h"
  45. #include "GameLogic/Module/PhysicsUpdate.h"
  46. #include "GameLogic/Module/StealthUpdate.h"
  47. #include "GameLogic/Module/RiderChangeContain.h"
  48. #ifdef _INTERNAL
  49. // for occasional debugging...
  50. //#pragma optimize("", off)
  51. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  52. #endif
  53. // ------------------------------------------------------------------------------------------------
  54. // ------------------------------------------------------------------------------------------------
  55. RiderChangeContainModuleData::RiderChangeContainModuleData()
  56. {
  57. m_scuttleFrames = 0;
  58. m_scuttleState = MODELCONDITION_TOPPLED;
  59. }
  60. // ------------------------------------------------------------------------------------------------
  61. // ------------------------------------------------------------------------------------------------
  62. void RiderChangeContainModuleData::parseRiderInfo( INI* ini, void *instance, void *store, const void* /*userData*/ )
  63. {
  64. RiderInfo* rider = (RiderInfo*)store;
  65. const char* name = ini->getNextToken();
  66. //Template name
  67. rider->m_templateName.format( name );
  68. //Model condition state
  69. INI::parseIndexList( ini, instance, &(rider->m_modelConditionFlagType), ModelConditionFlags::getBitNames() );
  70. //Weaponset
  71. INI::parseIndexList( ini, instance, &(rider->m_weaponSetFlag), WeaponSetFlags::getBitNames() );
  72. //Object status
  73. INI::parseIndexList( ini, instance, &(rider->m_objectStatusType), ObjectStatusMaskType::getBitNames() );
  74. //Command set override
  75. name = ini->getNextToken();
  76. rider->m_commandSet.format( name );
  77. //Locomotor set type
  78. rider->m_locomotorSetType = (LocomotorSetType)INI::scanIndexList( ini->getNextToken(), TheLocomotorSetNames );
  79. }
  80. // ------------------------------------------------------------------------------------------------
  81. // ------------------------------------------------------------------------------------------------
  82. void RiderChangeContainModuleData::buildFieldParse(MultiIniFieldParse& p)
  83. {
  84. TransportContainModuleData::buildFieldParse(p);
  85. static const FieldParse dataFieldParse[] =
  86. {
  87. { "Rider1", parseRiderInfo, NULL, offsetof( RiderChangeContainModuleData, m_riders[0] ) },
  88. { "Rider2", parseRiderInfo, NULL, offsetof( RiderChangeContainModuleData, m_riders[1] ) },
  89. { "Rider3", parseRiderInfo, NULL, offsetof( RiderChangeContainModuleData, m_riders[2] ) },
  90. { "Rider4", parseRiderInfo, NULL, offsetof( RiderChangeContainModuleData, m_riders[3] ) },
  91. { "Rider5", parseRiderInfo, NULL, offsetof( RiderChangeContainModuleData, m_riders[4] ) },
  92. { "Rider6", parseRiderInfo, NULL, offsetof( RiderChangeContainModuleData, m_riders[5] ) },
  93. { "Rider7", parseRiderInfo, NULL, offsetof( RiderChangeContainModuleData, m_riders[6] ) },
  94. { "Rider8", parseRiderInfo, NULL, offsetof( RiderChangeContainModuleData, m_riders[7] ) },
  95. { "ScuttleDelay", INI::parseDurationUnsignedInt, NULL, offsetof( RiderChangeContainModuleData, m_scuttleFrames ) },
  96. { "ScuttleStatus", INI::parseIndexList, ModelConditionFlags::getBitNames(), offsetof( RiderChangeContainModuleData, m_scuttleState ) },
  97. { 0, 0, 0, 0 }
  98. };
  99. p.add(dataFieldParse);
  100. }
  101. // PRIVATE ////////////////////////////////////////////////////////////////////////////////////////
  102. //-------------------------------------------------------------------------------------------------
  103. //-------------------------------------------------------------------------------------------------
  104. Int RiderChangeContain::getContainMax( void ) const
  105. {
  106. if (getRiderChangeContainModuleData())
  107. return getRiderChangeContainModuleData()->m_slotCapacity;
  108. return 0;
  109. }
  110. // PUBLIC /////////////////////////////////////////////////////////////////////////////////////////
  111. //-------------------------------------------------------------------------------------------------
  112. //-------------------------------------------------------------------------------------------------
  113. RiderChangeContain::RiderChangeContain( Thing *thing, const ModuleData *moduleData ) :
  114. TransportContain( thing, moduleData )
  115. {
  116. m_extraSlotsInUse = 0;
  117. m_frameExitNotBusy = 0;
  118. m_containing = FALSE;
  119. m_scuttledOnFrame = 0;
  120. }
  121. //-------------------------------------------------------------------------------------------------
  122. //-------------------------------------------------------------------------------------------------
  123. RiderChangeContain::~RiderChangeContain( void )
  124. {
  125. }
  126. //-------------------------------------------------------------------------------------------------
  127. //-------------------------------------------------------------------------------------------------
  128. /**
  129. can this container contain this kind of object?
  130. and, if checkCapacity is TRUE, does this container have enough space left to hold the given unit?
  131. */
  132. Bool RiderChangeContain::isValidContainerFor(const Object* rider, Bool checkCapacity) const
  133. {
  134. //Don't check capacity because our rider will kick the other rider out!
  135. if( TransportContain::isValidContainerFor( rider, FALSE ) )
  136. {
  137. if( m_scuttledOnFrame != 0 )
  138. {
  139. //Scuttled... too late!
  140. return FALSE;
  141. }
  142. //We can enter this bike... but now we need to extend the base functionality by limiting
  143. //which infantry can enter.
  144. const RiderChangeContainModuleData *data = getRiderChangeContainModuleData();
  145. for( int i = 0; i < MAX_RIDERS; i++ )
  146. {
  147. const ThingTemplate *thing = TheThingFactory->findTemplate( data->m_riders[ i ].m_templateName );
  148. if( thing->isEquivalentTo( rider->getTemplate() ) )
  149. {
  150. //We found a valid rider, so return success.
  151. return TRUE;
  152. }
  153. }
  154. }
  155. return FALSE;
  156. }
  157. //-------------------------------------------------------------------------------------------------
  158. //-------------------------------------------------------------------------------------------------
  159. void RiderChangeContain::onContaining( Object *rider, Bool wasSelected )
  160. {
  161. Object *obj = getObject();
  162. m_containing = TRUE;
  163. //Remove our existing rider
  164. if( m_payloadCreated )
  165. {
  166. obj->getAI()->aiEvacuateInstantly( TRUE, CMD_FROM_AI );
  167. }
  168. //If the rider is currently selected, transfer selection to the container and preserve other units
  169. //that may be already selected. Note that containing the rider will automatically cause it to be
  170. //deselected, so all we have to do is select the container (if not already selected)!
  171. Drawable *containDraw = getObject()->getDrawable();
  172. if( containDraw && wasSelected && !containDraw->isSelected() )
  173. {
  174. //Create the selection message
  175. GameMessage *teamMsg = TheMessageStream->appendMessage( GameMessage::MSG_CREATE_SELECTED_GROUP );
  176. teamMsg->appendBooleanArgument( FALSE );// not creating new team so pass false
  177. teamMsg->appendObjectIDArgument( getObject()->getID() );
  178. TheInGameUI->selectDrawable( containDraw );
  179. TheInGameUI->setDisplayedMaxWarning( FALSE );
  180. }
  181. //Find the rider in the list and set the appropriate model condition
  182. const RiderChangeContainModuleData *data = getRiderChangeContainModuleData();
  183. for( int i = 0; i < MAX_RIDERS; i++ )
  184. {
  185. const ThingTemplate *thing = TheThingFactory->findTemplate( data->m_riders[ i ].m_templateName );
  186. if( thing->isEquivalentTo( rider->getTemplate() ) )
  187. {
  188. //This is our rider, so set the correct model condition.
  189. obj->setModelConditionState( data->m_riders[ i ].m_modelConditionFlagType );
  190. //Also set the correct weaponset flag
  191. obj->setWeaponSetFlag( data->m_riders[ i ].m_weaponSetFlag );
  192. //Also set the object status
  193. obj->setStatus( MAKE_OBJECT_STATUS_MASK( data->m_riders[ i ].m_objectStatusType ) );
  194. //Set the new commandset override
  195. obj->setCommandSetStringOverride( data->m_riders[ i ].m_commandSet );
  196. TheControlBar->markUIDirty(); // Refresh the UI in case we are selected
  197. //Change the locomotor.
  198. AIUpdateInterface* ai = obj->getAI();
  199. if( ai )
  200. {
  201. ai->chooseLocomotorSet( data->m_riders[ i ].m_locomotorSetType );
  202. }
  203. if( obj->getStatusBits().test( OBJECT_STATUS_STEALTHED ) )
  204. {
  205. StealthUpdate* stealth = obj->getStealth();
  206. if( stealth )
  207. {
  208. stealth->markAsDetected();
  209. }
  210. }
  211. //Transfer experience from the rider to the bike.
  212. ExperienceTracker *riderTracker = rider->getExperienceTracker();
  213. ExperienceTracker *bikeTracker = obj->getExperienceTracker();
  214. bikeTracker->setVeterancyLevel( riderTracker->getVeterancyLevel(), FALSE );
  215. riderTracker->setExperienceAndLevel( 0, FALSE );
  216. break;
  217. }
  218. }
  219. //Extend base class
  220. TransportContain::onContaining( rider, wasSelected );
  221. m_containing = FALSE;
  222. }
  223. //-------------------------------------------------------------------------------------------------
  224. //-------------------------------------------------------------------------------------------------
  225. void RiderChangeContain::onRemoving( Object *rider )
  226. {
  227. Object *bike = getObject();
  228. //Note if the bike dies, the rider dies too.
  229. if( bike->isEffectivelyDead() )
  230. {
  231. TheGameLogic->destroyObject( rider );
  232. return;
  233. }
  234. if( m_payloadCreated )
  235. {
  236. //Extend base class
  237. TransportContain::onRemoving( rider );
  238. }
  239. //Find the rider in the list and clear various data.
  240. const RiderChangeContainModuleData *data = getRiderChangeContainModuleData();
  241. for( int i = 0; i < MAX_RIDERS; i++ )
  242. {
  243. const ThingTemplate *thing = TheThingFactory->findTemplate( data->m_riders[ i ].m_templateName );
  244. if( thing->isEquivalentTo( rider->getTemplate() ) )
  245. {
  246. //This is our rider, so clear the current model condition.
  247. bike->clearModelConditionFlags( MAKE_MODELCONDITION_MASK2( data->m_riders[ i ].m_modelConditionFlagType, MODELCONDITION_DOOR_1_CLOSING ) );
  248. //Also clear the current weaponset flag
  249. bike->clearWeaponSetFlag( data->m_riders[ i ].m_weaponSetFlag );
  250. //Also clear the object status
  251. bike->clearStatus( MAKE_OBJECT_STATUS_MASK( data->m_riders[ i ].m_objectStatusType ) );
  252. if( rider->getControllingPlayer() != NULL )
  253. {
  254. //Wow, completely unforseeable game teardown order crash. SetVeterancyLevel results in a call to player
  255. //about upgrade masks. So if we have a null player, it is game teardown, so don't worry about transfering exp.
  256. //Transfer experience from the bike to the rider.
  257. ExperienceTracker *riderTracker = rider->getExperienceTracker();
  258. ExperienceTracker *bikeTracker = bike->getExperienceTracker();
  259. riderTracker->setVeterancyLevel( bikeTracker->getVeterancyLevel(), FALSE );
  260. bikeTracker->setExperienceAndLevel( 0, FALSE );
  261. }
  262. break;
  263. }
  264. }
  265. //If we're not replacing the rider, then if the cycle is selected, transfer selection
  266. //to the rider getting off (because the bike is gonna blow).
  267. if( !m_containing )
  268. {
  269. Drawable *containDraw = bike->getDrawable();
  270. Drawable *riderDraw = rider->getDrawable();
  271. if( containDraw && riderDraw )
  272. {
  273. //Create the selection message for the rider if it's ours and SELECTED!
  274. if( bike->getControllingPlayer() == ThePlayerList->getLocalPlayer() && containDraw->isSelected() )
  275. {
  276. GameMessage *teamMsg = TheMessageStream->appendMessage( GameMessage::MSG_CREATE_SELECTED_GROUP );
  277. teamMsg->appendBooleanArgument( FALSE );// not creating new team so pass false
  278. teamMsg->appendObjectIDArgument( rider->getID() );
  279. TheInGameUI->selectDrawable( riderDraw );
  280. TheInGameUI->setDisplayedMaxWarning( FALSE );
  281. //Create the de-selection message for the container
  282. teamMsg = TheMessageStream->appendMessage( GameMessage::MSG_REMOVE_FROM_SELECTED_GROUP );
  283. teamMsg->appendObjectIDArgument( bike->getID() );
  284. TheInGameUI->deselectDrawable( containDraw );
  285. }
  286. //Finally, scuttle the bike so nobody else can use it! <Design Spec>
  287. m_scuttledOnFrame = TheGameLogic->getFrame();
  288. bike->setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_UNSELECTABLE ) );
  289. bike->setModelConditionState( data->m_scuttleState );
  290. if( !bike->getAI()->isMoving() )
  291. {
  292. bike->setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_IMMOBILE ) );
  293. }
  294. }
  295. }
  296. }
  297. // ------------------------------------------------------------------------------------------------
  298. // ------------------------------------------------------------------------------------------------
  299. void RiderChangeContain::createPayload()
  300. {
  301. // extend base class
  302. TransportContain::createPayload();
  303. }
  304. // ------------------------------------------------------------------------------------------------
  305. // ------------------------------------------------------------------------------------------------
  306. UpdateSleepTime RiderChangeContain::update()
  307. {
  308. if( m_scuttledOnFrame != 0 )
  309. {
  310. //Bike in the process of getting scuttled.
  311. const RiderChangeContainModuleData *data = getRiderChangeContainModuleData();
  312. UnsignedInt now = TheGameLogic->getFrame();
  313. if( m_scuttledOnFrame + data->m_scuttleFrames <= now )
  314. {
  315. //We have scuttled the bike (at least as far as tipping it over via scuttle animation. Now
  316. //kill the bike in a way that will cause it to sink into the ground without any real destruction.
  317. getObject()->kill( DAMAGE_UNRESISTABLE, DEATH_TOPPLED ); //Sneaky, eh? Toppled heheh.
  318. }
  319. }
  320. // extend base class
  321. return TransportContain::update(); //extend
  322. }
  323. // ------------------------------------------------------------------------------------------------
  324. // ------------------------------------------------------------------------------------------------
  325. void RiderChangeContain::unreserveDoorForExit( ExitDoorType exitDoor )
  326. {
  327. /* nothing */
  328. }
  329. // ------------------------------------------------------------------------------------------------
  330. // ------------------------------------------------------------------------------------------------
  331. void RiderChangeContain::killRidersWhoAreNotFreeToExit()
  332. {
  333. // extend base class
  334. TransportContain::killRidersWhoAreNotFreeToExit();
  335. }
  336. // ------------------------------------------------------------------------------------------------
  337. // ------------------------------------------------------------------------------------------------
  338. Bool RiderChangeContain::isSpecificRiderFreeToExit(Object* specificObject)
  339. {
  340. // extend base class
  341. return TransportContain::isSpecificRiderFreeToExit( specificObject );
  342. }
  343. // ------------------------------------------------------------------------------------------------
  344. // ------------------------------------------------------------------------------------------------
  345. ExitDoorType RiderChangeContain::reserveDoorForExit( const ThingTemplate* objType, Object *specificObject )
  346. {
  347. // extend base class
  348. return TransportContain::reserveDoorForExit( objType, specificObject );
  349. }
  350. // ------------------------------------------------------------------------------------------------
  351. // ------------------------------------------------------------------------------------------------
  352. Bool RiderChangeContain::isExitBusy() const ///< Contain style exiters are getting the ability to space out exits, so ask this before reserveDoor as a kind of no-commitment check.
  353. {
  354. // extend base class
  355. return FALSE; //return TransportContain::isExitBusy();
  356. }
  357. // ------------------------------------------------------------------------------------------------
  358. // ------------------------------------------------------------------------------------------------
  359. void RiderChangeContain::onCapture( Player *oldOwner, Player *newOwner )
  360. {
  361. // extend base class
  362. TransportContain::onCapture( oldOwner, newOwner );
  363. }
  364. //-------------------------------------------------------------------------------------------------
  365. Bool RiderChangeContain::getContainerPipsToShow(Int& numTotal, Int& numFull)
  366. {
  367. //Don't show any pips for motorcycles as they always have one rider unless dead!
  368. numTotal = 0;
  369. numFull = 0;
  370. return false;
  371. }
  372. //-------------------------------------------------------------------------------------------------
  373. const Object *RiderChangeContain::friend_getRider() const
  374. {
  375. if( m_containListSize > 0 ) // Yes, this does assume that infantry never ride double on the bike
  376. return m_containList.front();
  377. return NULL;
  378. }
  379. // ------------------------------------------------------------------------------------------------
  380. /** CRC */
  381. // ------------------------------------------------------------------------------------------------
  382. void RiderChangeContain::crc( Xfer *xfer )
  383. {
  384. // extend base class
  385. TransportContain::crc( xfer );
  386. } // end crc
  387. // ------------------------------------------------------------------------------------------------
  388. /** Xfer method
  389. * Version Info:
  390. * 1: Initial version */
  391. // ------------------------------------------------------------------------------------------------
  392. void RiderChangeContain::xfer( Xfer *xfer )
  393. {
  394. // version
  395. XferVersion currentVersion = 1;
  396. XferVersion version = currentVersion;
  397. xfer->xferVersion( &version, currentVersion );
  398. // extend base class
  399. TransportContain::xfer( xfer );
  400. // payload created
  401. xfer->xferBool( &m_payloadCreated );
  402. // extra slots in use
  403. xfer->xferInt( &m_extraSlotsInUse );
  404. // frame exit not busy
  405. xfer->xferUnsignedInt( &m_frameExitNotBusy );
  406. } // end xfer
  407. // ------------------------------------------------------------------------------------------------
  408. /** Load post process */
  409. // ------------------------------------------------------------------------------------------------
  410. void RiderChangeContain::loadPostProcess( void )
  411. {
  412. // extend base class
  413. TransportContain::loadPostProcess();
  414. } // end loadPostProcess