TransportContain.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563
  1. /*
  2. ** Command & Conquer Generals(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: TransportContain.cpp //////////////////////////////////////////////////////////////////////
  24. // Author: Steven Johnson, March 2002
  25. // Desc: Contain module for transport units.
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // USER INCLUDES //////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #include "Common/Player.h"
  30. #include "Common/ThingTemplate.h"
  31. #include "Common/ThingFactory.h"
  32. #include "Common/Xfer.h"
  33. #include "GameClient/Drawable.h"
  34. #include "GameLogic/AI.h"
  35. #include "GameLogic/AIPathfind.h"
  36. #include "GameLogic/Locomotor.h"
  37. #include "GameLogic/Module/AIUpdate.h"
  38. #include "GameLogic/Module/BodyModule.h"
  39. #include "GameLogic/Module/PhysicsUpdate.h"
  40. #include "GameLogic/Module/StealthUpdate.h"
  41. #include "GameLogic/Module/TransportContain.h"
  42. #include "GameLogic/Object.h"
  43. #ifdef _INTERNAL
  44. // for occasional debugging...
  45. //#pragma optimize("", off)
  46. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  47. #endif
  48. // ------------------------------------------------------------------------------------------------
  49. // ------------------------------------------------------------------------------------------------
  50. TransportContainModuleData::TransportContainModuleData()
  51. {
  52. m_slotCapacity = 0;
  53. m_scatterNearbyOnExit = true;
  54. m_orientLikeContainerOnExit = false;
  55. m_keepContainerVelocityOnExit = false;
  56. m_goAggressiveOnExit = FALSE;
  57. m_resetMoodCheckTimeOnExit = true;
  58. m_destroyRidersWhoAreNotFreeToExit = false;
  59. m_exitPitchRate = 0.0f;
  60. m_initialPayload.count = 0;
  61. m_healthRegen = 0.0f;
  62. m_exitDelay = 0;
  63. //
  64. // by default we say that transports can have infantry inside them, this will be totally
  65. // overwritten by any data provided from the INI entry tho
  66. //
  67. m_allowInsideKindOf = MAKE_KINDOF_MASK( KINDOF_INFANTRY );
  68. }
  69. // ------------------------------------------------------------------------------------------------
  70. // ------------------------------------------------------------------------------------------------
  71. void TransportContainModuleData::parseInitialPayload( INI* ini, void *instance, void *store, const void* /*userData*/ )
  72. {
  73. TransportContainModuleData* self = (TransportContainModuleData*)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. void TransportContainModuleData::buildFieldParse(MultiIniFieldParse& p)
  83. {
  84. OpenContainModuleData::buildFieldParse(p);
  85. static const FieldParse dataFieldParse[] =
  86. {
  87. { "Slots", INI::parseInt, NULL, offsetof( TransportContainModuleData, m_slotCapacity ) },
  88. { "ScatterNearbyOnExit", INI::parseBool, NULL, offsetof( TransportContainModuleData, m_scatterNearbyOnExit ) },
  89. { "OrientLikeContainerOnExit", INI::parseBool, NULL, offsetof( TransportContainModuleData, m_orientLikeContainerOnExit ) },
  90. { "KeepContainerVelocityOnExit", INI::parseBool, NULL, offsetof( TransportContainModuleData, m_keepContainerVelocityOnExit ) },
  91. { "GoAggressiveOnExit", INI::parseBool, NULL, offsetof( TransportContainModuleData, m_goAggressiveOnExit ) },
  92. { "ResetMoodCheckTimeOnExit", INI::parseBool, NULL, offsetof( TransportContainModuleData, m_resetMoodCheckTimeOnExit ) },
  93. { "DestroyRidersWhoAreNotFreeToExit", INI::parseBool, NULL, offsetof( TransportContainModuleData, m_destroyRidersWhoAreNotFreeToExit ) },
  94. { "ExitBone", INI::parseAsciiString, NULL, offsetof( TransportContainModuleData, m_exitBone ) },
  95. { "ExitPitchRate", INI::parseAngularVelocityReal, NULL, offsetof( TransportContainModuleData, m_exitPitchRate ) },
  96. { "InitialPayload", parseInitialPayload, NULL, 0 },
  97. { "HealthRegen%PerSec", INI::parseReal, NULL, offsetof( TransportContainModuleData, m_healthRegen ) },
  98. { "ExitDelay", INI::parseDurationUnsignedInt, NULL, offsetof( TransportContainModuleData, m_exitDelay ) },
  99. { 0, 0, 0, 0 }
  100. };
  101. p.add(dataFieldParse);
  102. }
  103. // PRIVATE ////////////////////////////////////////////////////////////////////////////////////////
  104. //-------------------------------------------------------------------------------------------------
  105. //-------------------------------------------------------------------------------------------------
  106. Int TransportContain::getContainMax( void ) const
  107. {
  108. if (getTransportContainModuleData())
  109. return getTransportContainModuleData()->m_slotCapacity;
  110. return 0;
  111. }
  112. // PUBLIC /////////////////////////////////////////////////////////////////////////////////////////
  113. //-------------------------------------------------------------------------------------------------
  114. //-------------------------------------------------------------------------------------------------
  115. TransportContain::TransportContain( Thing *thing, const ModuleData *moduleData ) :
  116. OpenContain( thing, moduleData )
  117. {
  118. m_extraSlotsInUse = 0;
  119. m_frameExitNotBusy = 0;
  120. }
  121. //-------------------------------------------------------------------------------------------------
  122. //-------------------------------------------------------------------------------------------------
  123. TransportContain::~TransportContain( 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 TransportContain::isValidContainerFor(const Object* rider, Bool checkCapacity) const
  133. {
  134. // sanity
  135. if (!rider)
  136. return false;
  137. // The point of this new code is to determine when something is a "fake" container, to
  138. // look at the object inside of it to use that as the valid check. There is a case, when a
  139. // paratrooper (an infantry contained in a parachute). In this case, when we pass this object
  140. // to contain in a transport plane, we want to check the infantry, not the parachute.
  141. if (rider->getContain() && rider->getContain()->isSpecialZeroSlotContainer())
  142. {
  143. // Report the first thing inside it!
  144. const ContainedItemsList *items = rider->getContain()->getContainedItemsList();
  145. if (items && !items->empty())
  146. {
  147. if (items->front())
  148. {
  149. // Replace the object we are checking with the *first* object contained within it.
  150. rider = items->front();
  151. }
  152. }
  153. }
  154. // extend functionality
  155. if( OpenContain::isValidContainerFor( rider, checkCapacity ) == false )
  156. return false;
  157. // // only allied objects can be transported.
  158. // // order matters: we want to know if I consider it to be an ally, not vice versa
  159. // if (getObject()->getRelationship(rider) != ALLIES)
  160. // return false;
  161. // no... actually, only OUR OWN units can be transported.
  162. if (rider->getControllingPlayer() != getObject()->getControllingPlayer())
  163. return false;
  164. Int transportSlotCount = rider->getTransportSlotCount();
  165. // if 0, this object isn't transportable.
  166. if (transportSlotCount == 0)
  167. return false;
  168. if (checkCapacity)
  169. {
  170. return (m_extraSlotsInUse + getContainCount() + transportSlotCount <= getContainMax());
  171. }
  172. else
  173. {
  174. return true;
  175. }
  176. }
  177. //-------------------------------------------------------------------------------------------------
  178. //-------------------------------------------------------------------------------------------------
  179. void TransportContain::onContaining( Object *rider )
  180. {
  181. OpenContain::onContaining(rider);
  182. // objects inside a transport are held
  183. rider->setDisabled( DISABLED_HELD );
  184. Int transportSlotCount = rider->getTransportSlotCount();
  185. DEBUG_ASSERTCRASH(transportSlotCount > 0, ("Hmm, this object isnt transportable"));
  186. m_extraSlotsInUse += transportSlotCount - 1;
  187. DEBUG_ASSERTCRASH(m_extraSlotsInUse >= 0 && m_extraSlotsInUse + getContainCount() <= getContainMax(), ("Hmm, bad slot count"));
  188. //
  189. // when we go from holding nothing to holding something we have a model condition
  190. // to visually show the change
  191. //
  192. if( getContainCount() == 1 )
  193. {
  194. Drawable *draw = getObject()->getDrawable();
  195. if( draw )
  196. draw->setModelConditionState( MODELCONDITION_LOADED );
  197. } // end if
  198. }
  199. //-------------------------------------------------------------------------------------------------
  200. //-------------------------------------------------------------------------------------------------
  201. void TransportContain::onRemoving( Object *rider )
  202. {
  203. OpenContain::onRemoving(rider);
  204. // object is no longer held inside a transport
  205. rider->clearDisabled( DISABLED_HELD );
  206. const TransportContainModuleData* d = getTransportContainModuleData();
  207. if (!d->m_exitBone.isEmpty())
  208. {
  209. Drawable* draw = getObject()->getDrawable();
  210. if (draw)
  211. {
  212. Coord3D bonePos, worldPos;
  213. if (draw->getPristineBonePositions(d->m_exitBone.str(), 0, &bonePos, NULL, 1) == 1)
  214. {
  215. getObject()->convertBonePosToWorldPos(&bonePos, NULL, &worldPos, NULL);
  216. rider->setPosition(&worldPos);
  217. }
  218. }
  219. }
  220. if (d->m_orientLikeContainerOnExit)
  221. {
  222. rider->setOrientation(getObject()->getOrientation());
  223. }
  224. if (d->m_keepContainerVelocityOnExit)
  225. {
  226. PhysicsBehavior* parent = getObject()->getPhysics();
  227. PhysicsBehavior* child = rider->getPhysics();
  228. if (parent && child)
  229. {
  230. Coord3D startingForce = *parent->getVelocity();
  231. Real mass = child->getMass();
  232. startingForce.x *= mass;
  233. startingForce.y *= mass;
  234. startingForce.z *= mass;
  235. child->applyMotiveForce( &startingForce );
  236. Real pitchRate = child->getCenterOfMassOffset() * d->m_exitPitchRate;
  237. child->setPitchRate( pitchRate );
  238. }
  239. }
  240. Int transportSlotCount = rider->getTransportSlotCount();
  241. DEBUG_ASSERTCRASH(transportSlotCount > 0, ("Hmm, this object isnt transportable"));
  242. m_extraSlotsInUse -= transportSlotCount - 1;
  243. DEBUG_ASSERTCRASH(m_extraSlotsInUse >= 0 && m_extraSlotsInUse + getContainCount() <= getContainMax(), ("Hmm, bad slot count"));
  244. // when we are empty again, clear the model condition for loaded
  245. if( getContainCount() == 0 )
  246. {
  247. Drawable *draw = getObject()->getDrawable();
  248. if( draw )
  249. draw->clearModelConditionState( MODELCONDITION_LOADED );
  250. } // end if
  251. if (getObject()->isAboveTerrain())
  252. {
  253. // temporarily mark the guy as being allowed to fall
  254. // (overriding his locomotor's stick-to-ground attribute).
  255. // this will be reset (by PhysicsBehavior) when he touches the ground.
  256. PhysicsBehavior* physics = rider->getPhysics();
  257. if (physics)
  258. physics->setAllowToFall(true);
  259. }
  260. // AI might need help using this transport in a good way. Make the passengers aggressive.
  261. //There is no computer player check since Aggressive only means something for computer players anyway
  262. if( d->m_goAggressiveOnExit && rider->getAI() )
  263. {
  264. rider->getAI()->setAttitude( AI_AGGRESSIVE );
  265. }
  266. if (getObject()->isEffectivelyDead()) {
  267. scatterToNearbyPosition(rider);
  268. }
  269. if( d->m_resetMoodCheckTimeOnExit && rider->getAI() )
  270. {
  271. rider->getAI()->wakeUpAndAttemptToTarget();
  272. }
  273. m_frameExitNotBusy = TheGameLogic->getFrame() + d->m_exitDelay;
  274. }
  275. // ------------------------------------------------------------------------------------------------
  276. // ------------------------------------------------------------------------------------------------
  277. void TransportContain::createPayload()
  278. {
  279. TransportContainModuleData* self = (TransportContainModuleData*)getTransportContainModuleData();
  280. Int count = self->m_initialPayload.count;
  281. const ThingTemplate* payloadTemplate = TheThingFactory->findTemplate( self->m_initialPayload.name );
  282. Object* object = getObject();
  283. ContainModuleInterface *contain = object->getContain();
  284. if( contain )
  285. {
  286. contain->enableLoadSounds( FALSE );
  287. for( int i = 0; i < count; i++ )
  288. {
  289. //We are creating a transport that comes with a initial payload, so add it now!
  290. Object* payload = TheThingFactory->newObject( payloadTemplate, object->getControllingPlayer()->getDefaultTeam() );
  291. if( contain->isValidContainerFor( payload, true ) )
  292. {
  293. contain->addToContain( payload );
  294. }
  295. else
  296. {
  297. DEBUG_CRASH( ( "DeliverPayload: PutInContainer %s is full, or not valid for the payload %s!", object->getName().str(), self->m_initialPayload.name.str() ) );
  298. }
  299. }
  300. contain->enableLoadSounds( TRUE );
  301. }
  302. m_payloadCreated = TRUE;
  303. }
  304. // ------------------------------------------------------------------------------------------------
  305. // ------------------------------------------------------------------------------------------------
  306. UpdateSleepTime TransportContain::update()
  307. {
  308. const TransportContainModuleData *moduleData = getTransportContainModuleData();
  309. if( m_payloadCreated == FALSE )
  310. createPayload();
  311. if( moduleData && moduleData->m_healthRegen )
  312. {
  313. ContainModuleInterface *contain = getObject()->getContain();
  314. if( contain )
  315. {
  316. //This transport has a health regeneration value, so go through and heal all inside.
  317. const ContainedItemsList* items = contain->getContainedItemsList();
  318. if( items )
  319. {
  320. ContainedItemsList::const_iterator it;
  321. it = items->begin();
  322. while( *it )
  323. {
  324. Object *object = *it;
  325. //Advance to the next iterator
  326. it++;
  327. //Determine if we need healing or not.
  328. BodyModuleInterface *body = object->getBodyModule();
  329. if( body->getHealth() < body->getMaxHealth() )
  330. {
  331. //Calculate the health to be regenerated on each unit.
  332. Real regen = body->getMaxHealth() * moduleData->m_healthRegen / 100.0f * SECONDS_PER_LOGICFRAME_REAL;
  333. //Perform the actual healing for this frame.
  334. // DamageInfo damageInfo;
  335. // damageInfo.in.m_damageType = DAMAGE_HEALING;
  336. // damageInfo.in.m_deathType = DEATH_NONE;
  337. // damageInfo.in.m_sourceID = getObject()->getID();
  338. // damageInfo.in.m_amount = regen;
  339. // object->attemptDamage( &damageInfo );
  340. object->attemptHealing( regen, getObject() );
  341. }
  342. }
  343. }
  344. }
  345. }
  346. return OpenContain::update(); //extend
  347. }
  348. // ------------------------------------------------------------------------------------------------
  349. // ------------------------------------------------------------------------------------------------
  350. void TransportContain::unreserveDoorForExit( ExitDoorType exitDoor )
  351. {
  352. /* nothing */
  353. }
  354. // ------------------------------------------------------------------------------------------------
  355. // ------------------------------------------------------------------------------------------------
  356. void TransportContain::killRidersWhoAreNotFreeToExit()
  357. {
  358. const TransportContainModuleData* d = getTransportContainModuleData();
  359. ContainedItemsList::const_iterator it = getContainList().begin();
  360. while( it != getContainList().end() )
  361. {
  362. // get the object
  363. Object *obj = *it;
  364. // increment the iterator, since death will pull this guy from the list somewhere else
  365. ++it;
  366. if (!isSpecificRiderFreeToExit(obj)) // only TransportContain has a meaningful failure for isFreeToExit
  367. {
  368. // If we cannot exit this guy legally, kill the bastard before removeAllContained forces him out.
  369. if (d->m_destroyRidersWhoAreNotFreeToExit)
  370. TheGameLogic->destroyObject(obj);
  371. else
  372. obj->kill();
  373. }
  374. }
  375. }
  376. // ------------------------------------------------------------------------------------------------
  377. // ------------------------------------------------------------------------------------------------
  378. Bool TransportContain::isSpecificRiderFreeToExit(Object* specificObject)
  379. {
  380. if( specificObject == NULL )
  381. return TRUE; // I can, in general, exit people.
  382. // This is a override, not an extend. I will check for game legality for
  383. // okaying the call to exitObjectViaDoor.
  384. const Object* me = getObject();
  385. // this is present solely for some transports to override, so that they can land before
  386. // allowing people to exit...
  387. const AIUpdateInterface* ai = me->getAIUpdateInterface();
  388. if (ai && ai->getAiFreeToExit(specificObject) != FREE_TO_EXIT)
  389. return FALSE;
  390. // I can always kick people out if I am in the air, I know what I'm doing
  391. if (me->isUsingAirborneLocomotor())
  392. return TRUE;
  393. const Coord3D *myPosition = me->getPosition();
  394. if (!specificObject->getAIUpdateInterface())
  395. return FALSE;
  396. const Locomotor *hisLocomotor = specificObject->getAIUpdateInterface()->getCurLocomotor();
  397. if( hisLocomotor == FALSE )
  398. return FALSE;
  399. // He can't get to this spot naturally, so I can't force him there. (amphib transport)
  400. if (!TheAI->pathfinder()->validMovementTerrain(me->getLayer(), hisLocomotor, myPosition))
  401. return FALSE;
  402. return TRUE;
  403. }
  404. // ------------------------------------------------------------------------------------------------
  405. // ------------------------------------------------------------------------------------------------
  406. ExitDoorType TransportContain::reserveDoorForExit( const ThingTemplate* objType, Object *specificObject )
  407. {
  408. return isSpecificRiderFreeToExit(specificObject) ? DOOR_1 : DOOR_NONE_AVAILABLE;
  409. }
  410. // ------------------------------------------------------------------------------------------------
  411. // ------------------------------------------------------------------------------------------------
  412. Bool TransportContain::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.
  413. {
  414. return TheGameLogic->getFrame() < m_frameExitNotBusy;
  415. }
  416. // ------------------------------------------------------------------------------------------------
  417. // ------------------------------------------------------------------------------------------------
  418. void TransportContain::onCapture( Player *oldOwner, Player *newOwner )
  419. {
  420. if( oldOwner != newOwner )
  421. {
  422. if( getObject()->isDisabledByType( DISABLED_UNMANNED ) )
  423. {
  424. //If this vehicle was sniped, then instantly eject everyone (otherwise, the next guy to eject will recapture it).
  425. removeAllContained();
  426. }
  427. else
  428. {
  429. //Use standard
  430. orderAllPassengersToExit( CMD_FROM_AI );
  431. }
  432. }
  433. }
  434. // ------------------------------------------------------------------------------------------------
  435. /** CRC */
  436. // ------------------------------------------------------------------------------------------------
  437. void TransportContain::crc( Xfer *xfer )
  438. {
  439. // extend base class
  440. OpenContain::crc( xfer );
  441. } // end crc
  442. // ------------------------------------------------------------------------------------------------
  443. /** Xfer method
  444. * Version Info:
  445. * 1: Initial version */
  446. // ------------------------------------------------------------------------------------------------
  447. void TransportContain::xfer( Xfer *xfer )
  448. {
  449. // version
  450. XferVersion currentVersion = 1;
  451. XferVersion version = currentVersion;
  452. xfer->xferVersion( &version, currentVersion );
  453. // extend base class
  454. OpenContain::xfer( xfer );
  455. // payload created
  456. xfer->xferBool( &m_payloadCreated );
  457. // extra slots in use
  458. xfer->xferInt( &m_extraSlotsInUse );
  459. // frame exit not busy
  460. xfer->xferUnsignedInt( &m_frameExitNotBusy );
  461. } // end xfer
  462. // ------------------------------------------------------------------------------------------------
  463. /** Load post process */
  464. // ------------------------------------------------------------------------------------------------
  465. void TransportContain::loadPostProcess( void )
  466. {
  467. // extend base class
  468. OpenContain::loadPostProcess();
  469. } // end loadPostProcess