MobNexusContain.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498
  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: MobNexusContain.cpp //////////////////////////////////////////////////////////////////////
  24. // Author: Mark Lorenzen, August 2002
  25. // Desc: Contain module for mob 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/StealthUpdate.h"
  38. #include "GameLogic/Module/AIUpdate.h"
  39. #include "GameLogic/Module/BodyModule.h"
  40. #include "GameLogic/Module/MobNexusContain.h"
  41. #include "GameLogic/Module/PhysicsUpdate.h"
  42. #include "GameLogic/Object.h"
  43. // ------------------------------------------------------------------------------------------------
  44. // ------------------------------------------------------------------------------------------------
  45. MobNexusContainModuleData::MobNexusContainModuleData()
  46. {
  47. m_slotCapacity = 0;
  48. m_scatterNearbyOnExit = true;
  49. m_orientLikeContainerOnExit = false;
  50. m_keepContainerVelocityOnExit = false;
  51. m_exitPitchRate = 0.0f;
  52. m_initialPayload.count = 0;
  53. m_healthRegen = 0.0f;
  54. //
  55. // by default we say that MobNexae can have infantry inside them, this will be totally
  56. // overwritten by any data provided from the INI entry tho
  57. //
  58. m_allowInsideKindOf = MAKE_KINDOF_MASK( KINDOF_INFANTRY );
  59. }
  60. // ------------------------------------------------------------------------------------------------
  61. // ------------------------------------------------------------------------------------------------
  62. void MobNexusContainModuleData::parseInitialPayload( INI* ini, void *instance, void *store, const void* /*userData*/ )
  63. {
  64. MobNexusContainModuleData* self = (MobNexusContainModuleData*)instance;
  65. const char* name = ini->getNextToken();
  66. const char* countStr = ini->getNextTokenOrNull();
  67. Int count = countStr ? INI::scanInt(countStr) : 1;
  68. self->m_initialPayload.name.set(name);
  69. self->m_initialPayload.count = count;
  70. }
  71. // ------------------------------------------------------------------------------------------------
  72. // ------------------------------------------------------------------------------------------------
  73. void MobNexusContainModuleData::buildFieldParse(MultiIniFieldParse& p)
  74. {
  75. OpenContainModuleData::buildFieldParse(p);
  76. static const FieldParse dataFieldParse[] =
  77. {
  78. { "Slots", INI::parseInt, NULL, offsetof( MobNexusContainModuleData, m_slotCapacity ) },
  79. { "ScatterNearbyOnExit", INI::parseBool, NULL, offsetof( MobNexusContainModuleData, m_scatterNearbyOnExit ) },
  80. { "OrientLikeContainerOnExit", INI::parseBool, NULL, offsetof( MobNexusContainModuleData, m_orientLikeContainerOnExit ) },
  81. { "KeepContainerVelocityOnExit", INI::parseBool, NULL, offsetof( MobNexusContainModuleData, m_keepContainerVelocityOnExit ) },
  82. { "ExitBone", INI::parseAsciiString, NULL, offsetof( MobNexusContainModuleData, m_exitBone ) },
  83. { "ExitPitchRate", INI::parseAngularVelocityReal, NULL, offsetof( MobNexusContainModuleData, m_exitPitchRate ) },
  84. { "InitialPayload", parseInitialPayload, NULL, 0 },
  85. { "HealthRegen%PerSec", INI::parseReal, NULL, offsetof( MobNexusContainModuleData, m_healthRegen ) },
  86. { 0, 0, 0, 0 }
  87. };
  88. p.add(dataFieldParse);
  89. }
  90. // PRIVATE ////////////////////////////////////////////////////////////////////////////////////////
  91. //-------------------------------------------------------------------------------------------------
  92. //-------------------------------------------------------------------------------------------------
  93. Int MobNexusContain::getContainMax( void ) const
  94. {
  95. if (getMobNexusContainModuleData())
  96. return getMobNexusContainModuleData()->m_slotCapacity;
  97. return 0;
  98. }
  99. // PUBLIC /////////////////////////////////////////////////////////////////////////////////////////
  100. //-------------------------------------------------------------------------------------------------
  101. //-------------------------------------------------------------------------------------------------
  102. MobNexusContain::MobNexusContain( Thing *thing, const ModuleData *moduleData ) :
  103. OpenContain( thing, moduleData )
  104. {
  105. m_extraSlotsInUse = 0;
  106. }
  107. //-------------------------------------------------------------------------------------------------
  108. //-------------------------------------------------------------------------------------------------
  109. MobNexusContain::~MobNexusContain( void )
  110. {
  111. }
  112. //-------------------------------------------------------------------------------------------------
  113. //-------------------------------------------------------------------------------------------------
  114. /**
  115. can this container contain this kind of object?
  116. and, if checkCapacity is TRUE, does this container have enough space left to hold the given unit?
  117. */
  118. Bool MobNexusContain::isValidContainerFor(const Object* rider, Bool checkCapacity) const
  119. {
  120. // sanity
  121. if (!rider)
  122. return false;
  123. //The point of this new code is to determine when something has a negative 1 contain max, to
  124. //look at the object inside of it to use that as the valid check. There is a case, when a
  125. //paratrooper (an infantry contained in a parachute). In this case, when we pass this object
  126. //to contain in a --- plane, we want to check the infantry, not the parachute.
  127. // const MobNexusContainModuleData *modData = getMobNexusContainModuleData();
  128. //Check if we are a fake container, and if so, get an object inside it to see what kind this object *is*.
  129. if( rider->getContain() && rider->getContain()->isSpecialZeroSlotContainer() )
  130. {
  131. //Report the first thing inside it!
  132. const ContainedItemsList *items = rider->getContain()->getContainedItemsList();
  133. if( items )
  134. {
  135. ContainedItemsList::const_iterator it;
  136. it = items->begin();
  137. if( *it )
  138. {
  139. //Replace the object we are checking with the *first* object contained within it.
  140. rider = *it;
  141. }
  142. }
  143. }
  144. else
  145. {
  146. //blech! This case may or may not occur... in which case, just use the supplied object.
  147. }
  148. // extend functionality
  149. if( OpenContain::isValidContainerFor( rider, checkCapacity ) == false )
  150. return false;
  151. // only allied objects can be MobNexused.
  152. // order matters: we want to know if IT considers ME to be an ally (a reversal of the usual situation)
  153. if (rider->getRelationship(getObject()) != ALLIES)
  154. return false;
  155. Int mobNexusSlotCount = rider->getTransportSlotCount();
  156. // if 0, this object isn't MobNexusable.
  157. if (mobNexusSlotCount == 0)
  158. return false;
  159. if (checkCapacity)
  160. {
  161. return (m_extraSlotsInUse + getContainCount() + mobNexusSlotCount <= getContainMax());
  162. }
  163. else
  164. {
  165. return true;
  166. }
  167. }
  168. //-------------------------------------------------------------------------------------------------
  169. //-------------------------------------------------------------------------------------------------
  170. void MobNexusContain::onContaining( Object *rider )
  171. {
  172. OpenContain::onContaining(rider);
  173. // objects inside a MobNexus are held
  174. rider->setDisabled( DISABLED_HELD );
  175. Int mobNexusSlotCount = rider->getTransportSlotCount();
  176. DEBUG_ASSERTCRASH(mobNexusSlotCount > 0, ("Hmm, this object isnt MobNexusable"));
  177. m_extraSlotsInUse += mobNexusSlotCount - 1;
  178. DEBUG_ASSERTCRASH(m_extraSlotsInUse >= 0 && m_extraSlotsInUse + getContainCount() <= getContainMax(), ("Hmm, bad slot count"));
  179. //
  180. // when we go from holding nothing to holding something we have a model condition
  181. // to visually show the change
  182. //
  183. if( getContainCount() == 1 )
  184. {
  185. Drawable *draw = getObject()->getDrawable();
  186. if( draw )
  187. draw->setModelConditionState( MODELCONDITION_LOADED );
  188. } // end if
  189. }
  190. //-------------------------------------------------------------------------------------------------
  191. //-------------------------------------------------------------------------------------------------
  192. void MobNexusContain::onRemoving( Object *rider )
  193. {
  194. OpenContain::onRemoving(rider);
  195. // object is no longer held inside a MobNexus
  196. rider->clearDisabled( DISABLED_HELD );
  197. const MobNexusContainModuleData* d = getMobNexusContainModuleData();
  198. if (!d->m_exitBone.isEmpty())
  199. {
  200. Drawable* draw = getObject()->getDrawable();
  201. if (draw)
  202. {
  203. Coord3D bonePos, worldPos;
  204. if (draw->getPristineBonePositions(d->m_exitBone.str(), 0, &bonePos, NULL, 1) == 1)
  205. {
  206. getObject()->convertBonePosToWorldPos(&bonePos, NULL, &worldPos, NULL);
  207. rider->setPosition(&worldPos);
  208. }
  209. }
  210. }
  211. if (d->m_orientLikeContainerOnExit)
  212. {
  213. rider->setOrientation(getObject()->getOrientation());
  214. }
  215. if (d->m_keepContainerVelocityOnExit)
  216. {
  217. PhysicsBehavior* parent = getObject()->getPhysics();
  218. PhysicsBehavior* child = rider->getPhysics();
  219. if (parent && child)
  220. {
  221. Coord3D startingForce = *parent->getVelocity();
  222. Real mass = child->getMass();
  223. startingForce.x *= mass;
  224. startingForce.y *= mass;
  225. startingForce.z *= mass;
  226. child->applyMotiveForce( &startingForce );
  227. Real pitchRate = child->getCenterOfMassOffset() * d->m_exitPitchRate;
  228. child->setPitchRate( pitchRate );
  229. }
  230. }
  231. if (d->m_scatterNearbyOnExit)
  232. scatterToNearbyPosition(rider);
  233. Int mobNexusSlotCount = rider->getTransportSlotCount();
  234. DEBUG_ASSERTCRASH(mobNexusSlotCount > 0, ("This object isnt MobNexusable"));
  235. m_extraSlotsInUse -= mobNexusSlotCount - 1;
  236. DEBUG_ASSERTCRASH(m_extraSlotsInUse >= 0 && m_extraSlotsInUse + getContainCount() <= getContainMax(), ("Bad slot count, MobNexus"));
  237. // when we are empty again, clear the model condition for loaded
  238. if( getContainCount() == 0 )
  239. {
  240. Drawable *draw = getObject()->getDrawable();
  241. if( draw )
  242. draw->clearModelConditionState( MODELCONDITION_LOADED );
  243. } // end if
  244. if (getObject()->isAboveTerrain())
  245. {
  246. // temporarily mark the guy as being allowed to fall
  247. // (overriding his locomotor's stick-to-ground attribute).
  248. // this will be reset (by PhysicsBehavior) when he touches the ground.
  249. PhysicsBehavior* physics = rider->getPhysics();
  250. if (physics)
  251. physics->setAllowToFall(true);
  252. }
  253. }
  254. // ------------------------------------------------------------------------------------------------
  255. // ------------------------------------------------------------------------------------------------
  256. void MobNexusContain::onObjectCreated()
  257. {
  258. MobNexusContainModuleData* self = (MobNexusContainModuleData*)getMobNexusContainModuleData();
  259. Int count = self->m_initialPayload.count;
  260. const ThingTemplate* payloadTemplate = TheThingFactory->findTemplate( self->m_initialPayload.name );
  261. Object* object = getObject();
  262. for( int i = 0; i < count; i++ )
  263. {
  264. //We are creating a MobNexus that comes with a initial payload, so add it now!
  265. Object* payload = TheThingFactory->newObject( payloadTemplate, object->getControllingPlayer()->getDefaultTeam() );
  266. if( object->getContain() && object->getContain()->isValidContainerFor( payload, true ) )
  267. {
  268. object->getContain()->addToContain( payload );
  269. }
  270. else
  271. {
  272. DEBUG_CRASH( ( "DeliverPayload: PutInContainer %s is full, or not valid for the payload %s!", object->getName().str(), self->m_initialPayload.name.str() ) );
  273. }
  274. }
  275. }
  276. // ------------------------------------------------------------------------------------------------
  277. // ------------------------------------------------------------------------------------------------
  278. UpdateSleepTime MobNexusContain::update()
  279. {
  280. MobNexusContainModuleData *moduleData = (MobNexusContainModuleData*)getModuleData();
  281. if( moduleData && moduleData->m_healthRegen )
  282. {
  283. ContainModuleInterface *contain = getObject()->getContain();
  284. if( contain )
  285. {
  286. //This MobNexus has a health regeneration value, so go through and heal all inside.
  287. const ContainedItemsList* items = contain->getContainedItemsList();
  288. if( items )
  289. {
  290. ContainedItemsList::const_iterator it;
  291. it = items->begin();
  292. while( *it )
  293. {
  294. Object *object = *it;
  295. //Advance to the next iterator
  296. it++;
  297. //Determine if we need healing or not.
  298. BodyModuleInterface *body = object->getBodyModule();
  299. if( body->getHealth() < body->getMaxHealth() )
  300. {
  301. //Calculate the health to be regenerated on each unit.
  302. Real regen = body->getMaxHealth() * moduleData->m_healthRegen / 100.0f * SECONDS_PER_LOGICFRAME_REAL;
  303. //Perform the actual healing for this frame.
  304. // DamageInfo damageInfo;
  305. // damageInfo.in.m_damageType = DAMAGE_HEALING;
  306. // damageInfo.in.m_deathType = DEATH_NONE;
  307. // damageInfo.in.m_sourceID = getObject()->getID();
  308. // damageInfo.in.m_amount = regen;
  309. // object->attemptDamage( &damageInfo );
  310. object->attemptHealing( regen, getObject() );
  311. }
  312. }
  313. }
  314. }
  315. }
  316. return OpenContain::update(); //extend
  317. }
  318. // ------------------------------------------------------------------------------------------------
  319. // ------------------------------------------------------------------------------------------------
  320. ExitDoorType MobNexusContain::reserveDoorForExit( const ThingTemplate* objType, Object *specificObject )
  321. {
  322. if( specificObject == NULL )
  323. return DOOR_1;// I can, in general, exit people.
  324. // This is an override, not an extend. I will check for game legality for
  325. // okaying the call to exitObjectViaDoor.
  326. Object *me = getObject();
  327. // this is present solely for some MobNexuss to override, so that they can land before
  328. // allowing people to exit...
  329. AIUpdateInterface* ai = me->getAIUpdateInterface();
  330. if (ai && ai->getAiFreeToExit(me) != FREE_TO_EXIT)
  331. return DOOR_NONE_AVAILABLE;
  332. // I can always kick people out if I am in the air, I know what I'm doing
  333. if( me->isUsingAirborneLocomotor() )
  334. return DOOR_1;
  335. const Coord3D *myPosition = me->getPosition();
  336. if( !specificObject->getAIUpdateInterface() )
  337. {
  338. return DOOR_NONE_AVAILABLE;
  339. }
  340. const Locomotor *hisLocomotor = specificObject->getAIUpdateInterface()->getCurLocomotor();
  341. // He can't get to this spot naturally, so I can't force him there. (amphib MobNexus)
  342. if( ! TheAI->pathfinder()->validMovementTerrain(me->getLayer(), hisLocomotor, myPosition ) )
  343. return DOOR_NONE_AVAILABLE;
  344. return DOOR_1;
  345. }
  346. // ------------------------------------------------------------------------------------------------
  347. // ------------------------------------------------------------------------------------------------
  348. void MobNexusContain::unreserveDoorForExit( ExitDoorType exitDoor )
  349. {
  350. /* nothing */
  351. }
  352. // ------------------------------------------------------------------------------------------------
  353. // ------------------------------------------------------------------------------------------------
  354. Bool MobNexusContain::tryToEvacuate( Bool exposeStealthedUnits )
  355. {
  356. Bool exitedAnyone = false;
  357. ContainedItemsList::const_iterator it = getContainList().begin();
  358. while( it != getContainList().end() )
  359. {
  360. // get the object
  361. Object *obj = *it;
  362. // increment the iterator, since removal will pull this guy from the list somewhere else
  363. // and we might not actually kick anyone so we don't want to loop forever.
  364. ++it;
  365. ExitDoorType exitDoor = reserveDoorForExit(obj->getTemplate(), obj);
  366. if(exitDoor != DOOR_NONE_AVAILABLE)
  367. {
  368. exitObjectViaDoor( obj, exitDoor );
  369. exitedAnyone = true;
  370. if( obj->isKindOf( KINDOF_STEALTH_GARRISON ) && exposeStealthedUnits )
  371. {
  372. static NameKeyType key_StealthUpdate = NAMEKEY( "StealthUpdate" );
  373. StealthUpdate* stealth = (StealthUpdate*)obj->findUpdateModule( key_StealthUpdate );
  374. if( stealth )
  375. {
  376. stealth->markAsDetected();
  377. }
  378. }
  379. }
  380. }
  381. return exitedAnyone;
  382. }
  383. // ------------------------------------------------------------------------------------------------
  384. /** CRC - I like the word nexus */
  385. // ------------------------------------------------------------------------------------------------
  386. void MobNexusContain::crc( Xfer *xfer )
  387. {
  388. // extend base class
  389. OpenContain::crc( xfer );
  390. } // end crc
  391. // ------------------------------------------------------------------------------------------------
  392. /** Xfer method
  393. * Version Info:
  394. * 1: Initial version */
  395. // ------------------------------------------------------------------------------------------------
  396. void MobNexusContain::xfer( Xfer *xfer )
  397. {
  398. // version
  399. XferVersion currentVersion = 1;
  400. XferVersion version = currentVersion;
  401. xfer->xferVersion( &version, currentVersion );
  402. // extend base class
  403. OpenContain::xfer( xfer );
  404. // extra slots in use
  405. xfer->xferInt( &m_extraSlotsInUse );
  406. } // end xfer
  407. // ------------------------------------------------------------------------------------------------
  408. /** Load post process */
  409. // ------------------------------------------------------------------------------------------------
  410. void MobNexusContain::loadPostProcess( void )
  411. {
  412. // extend base class
  413. OpenContain::loadPostProcess();
  414. } // end loadPostProcess