RailedTransportDockUpdate.cpp 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570
  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: RailedTransportDockUpdate.cpp ////////////////////////////////////////////////////////////
  24. // Author: Colin Day, August 2002
  25. // Desc: Railed Transport Dock Update
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #include "Common/ThingTemplate.h"
  30. #include "Common/Xfer.h"
  31. #include "GameClient/Drawable.h"
  32. #include "GameClient/InGameUI.h"
  33. #include "GameLogic/GameLogic.h"
  34. #include "GameLogic/Object.h"
  35. #include "GameLogic/PartitionManager.h"
  36. #include "GameLogic/Module/AIUpdate.h"
  37. #include "GameLogic/Module/ContainModule.h"
  38. #include "GameLogic/Module/OpenContain.h"
  39. #include "GameLogic/Module/RailedTransportDockUpdate.h"
  40. // TYPES //////////////////////////////////////////////////////////////////////////////////////////
  41. enum { UNLOAD_ALL = -1 };
  42. // ------------------------------------------------------------------------------------------------
  43. // ------------------------------------------------------------------------------------------------
  44. RailedTransportDockUpdateModuleData::RailedTransportDockUpdateModuleData( void )
  45. {
  46. m_pullInsideDurationInFrames = 0;
  47. m_pushOutsideDurationInFrames = 0;
  48. } // end RailedTransportDockUpdateModuleData
  49. // ------------------------------------------------------------------------------------------------
  50. // ------------------------------------------------------------------------------------------------
  51. /*static*/ void RailedTransportDockUpdateModuleData::buildFieldParse( MultiIniFieldParse &p )
  52. {
  53. DockUpdateModuleData::buildFieldParse( p );
  54. static const FieldParse dataFieldParse[] =
  55. {
  56. { "PullInsideDuration", INI::parseDurationUnsignedInt, NULL, offsetof( RailedTransportDockUpdateModuleData, m_pullInsideDurationInFrames ) },
  57. { "PushOutsideDuration",INI::parseDurationUnsignedInt, NULL, offsetof( RailedTransportDockUpdateModuleData, m_pushOutsideDurationInFrames ) },
  58. { 0, 0, 0, 0 }
  59. };
  60. p.add( dataFieldParse );
  61. } // end buildFieldParse
  62. ///////////////////////////////////////////////////////////////////////////////////////////////////
  63. ///////////////////////////////////////////////////////////////////////////////////////////////////
  64. ///////////////////////////////////////////////////////////////////////////////////////////////////
  65. // ------------------------------------------------------------------------------------------------
  66. // ------------------------------------------------------------------------------------------------
  67. RailedTransportDockUpdate::RailedTransportDockUpdate( Thing *thing, const ModuleData *moduleData )
  68. : DockUpdate( thing, moduleData )
  69. {
  70. m_dockingObjectID = INVALID_ID;
  71. m_pullInsideDistancePerFrame = 0.0f;
  72. m_unloadingObjectID = INVALID_ID;
  73. m_pushOutsideDistancePerFrame = 0.0f;
  74. m_unloadCount = UNLOAD_ALL;
  75. } // end RailedTransportDockUpdate
  76. // ------------------------------------------------------------------------------------------------
  77. // ------------------------------------------------------------------------------------------------
  78. RailedTransportDockUpdate::~RailedTransportDockUpdate( void )
  79. {
  80. } // end ~RailedTransportDockUpdate
  81. // ------------------------------------------------------------------------------------------------
  82. // ------------------------------------------------------------------------------------------------
  83. UpdateSleepTime RailedTransportDockUpdate::update( void )
  84. {
  85. // extend functionality
  86. UpdateSleepTime result;
  87. result = DockUpdate::update();
  88. // do any pull in docking we need to
  89. doPullInDocking();
  90. // do any push out docking
  91. doPushOutDocking();
  92. return UPDATE_SLEEP_NONE;
  93. } // end update
  94. // ------------------------------------------------------------------------------------------------
  95. /** The dock action callback, return FALSE when done docking */
  96. // ------------------------------------------------------------------------------------------------
  97. Bool RailedTransportDockUpdate::action( Object *docker, Object *drone )
  98. {
  99. Object *us = getObject();
  100. // sanity
  101. if( docker == NULL )
  102. return FALSE;
  103. // set this object as docking with us if not already done so
  104. if( m_dockingObjectID != docker->getID() )
  105. {
  106. m_dockingObjectID = docker->getID();
  107. // don't let the user interact with this object anymore
  108. TheGameLogic->deselectObject(docker, PLAYERMASK_ALL, TRUE);
  109. docker->setStatus( OBJECT_STATUS_UNSELECTABLE );
  110. // hold the object so physics doesn't mess with it anymore
  111. docker->setDisabled( DISABLED_HELD );
  112. //
  113. // given the amount of time we want it to take to "pull" the object inside, figure out
  114. // how much distance it should traverse towards our center every frame
  115. //
  116. const Coord3D *dockerPos = docker->getPosition();
  117. const Coord3D *dockPos = us->getPosition();
  118. Coord3D v;
  119. v.x = dockPos->x - dockerPos->x;
  120. v.y = dockPos->y - dockerPos->y;
  121. v.z = dockPos->z - dockerPos->z;
  122. // how far do we have to go
  123. Real mag = v.length();
  124. // now that we know how far we must go, now much distance should we travel every frame
  125. const RailedTransportDockUpdateModuleData *modData = getRailedTransportDockUpdateModuleData();
  126. m_pullInsideDistancePerFrame = mag / modData->m_pullInsideDurationInFrames;
  127. // orient docker so its facing toward the transport
  128. Coord2D angleVector;
  129. angleVector.x = dockPos->x - dockerPos->x;
  130. angleVector.y = dockPos->y - dockerPos->y;
  131. docker->setOrientation( angleVector.toAngle() );
  132. } // end if
  133. return TRUE;
  134. } // end action
  135. // ------------------------------------------------------------------------------------------------
  136. /** Is clear to enter the railed transport */
  137. // ------------------------------------------------------------------------------------------------
  138. Bool RailedTransportDockUpdate::isClearToEnter( Object const *docker ) const
  139. {
  140. const Object *us = getObject();
  141. // first do base class restrictions
  142. Bool clear = DockUpdate::isClearToEnter( docker );
  143. if( clear == FALSE )
  144. return FALSE;
  145. // we have additional requirements, we are a transporting dock so we can't be full
  146. ContainModuleInterface *contain = us->getContain();
  147. if( contain && contain->isValidContainerFor( docker, TRUE ) == FALSE )
  148. return FALSE;
  149. return TRUE;
  150. } // end isClearToEnter
  151. // ------------------------------------------------------------------------------------------------
  152. /** Is anything currently loading or unloading */
  153. // ------------------------------------------------------------------------------------------------
  154. Bool RailedTransportDockUpdate::isLoadingOrUnloading( void )
  155. {
  156. if( m_unloadingObjectID != INVALID_ID || m_dockingObjectID != INVALID_ID )
  157. return TRUE;
  158. return FALSE;
  159. } // end isLoadingOrUnloading
  160. // ------------------------------------------------------------------------------------------------
  161. /** Start the unload process */
  162. // ------------------------------------------------------------------------------------------------
  163. void RailedTransportDockUpdate::unloadAll( void )
  164. {
  165. // sanity, if we're already unloading, ignore this command and just allow us to finish
  166. if( m_unloadingObjectID != INVALID_ID )
  167. return;
  168. // start our first object unloading and continue through them all
  169. m_unloadCount = UNLOAD_ALL;
  170. unloadNext();
  171. } // end manualUnload
  172. // ------------------------------------------------------------------------------------------------
  173. /** Unload a single individual only */
  174. // ------------------------------------------------------------------------------------------------
  175. void RailedTransportDockUpdate::unloadSingleObject( Object *obj )
  176. {
  177. // start the unload process of a single object
  178. m_unloadCount = 1;
  179. unloadNext();
  180. } // end unloadSingleObject
  181. ///////////////////////////////////////////////////////////////////////////////////////////////////
  182. ///////////////////////////////////////////////////////////////////////////////////////////////////
  183. ///////////////////////////////////////////////////////////////////////////////////////////////////
  184. // ------------------------------------------------------------------------------------------------
  185. /** If we have an object recorded as currently docking with us, pull that object inside
  186. * and when it is inside, contain it */
  187. // ------------------------------------------------------------------------------------------------
  188. void RailedTransportDockUpdate::doPullInDocking( void )
  189. {
  190. //
  191. // if we're pulling an object inside of us, do that pull now. we need this so that the
  192. // railed transport can "pull" objects inside it because typically those objects can only drive
  193. // on land and have a hard time driving "inside" the railed transport ... so we fake it!
  194. //
  195. if( m_dockingObjectID != INVALID_ID )
  196. {
  197. Object *us = getObject();
  198. Object *docker = TheGameLogic->findObjectByID( m_dockingObjectID );
  199. // check for docker gone
  200. if( docker == NULL )
  201. m_dockingObjectID = INVALID_ID;
  202. // pull it
  203. if( docker )
  204. {
  205. const Coord3D *dockerPos = docker->getPosition();
  206. const Coord3D *dockPos = us->getPosition();
  207. // get the vector from the docker to the dock pos
  208. Coord3D v;
  209. v.x = dockPos->x - dockerPos->x;
  210. v.y = dockPos->y - dockerPos->y;
  211. v.z = dockPos->z - dockerPos->z;
  212. v.normalize();
  213. // apply "movement" to the vector
  214. v.x *= m_pullInsideDistancePerFrame;
  215. v.y *= m_pullInsideDistancePerFrame;
  216. // apply current position of the docker to the vector
  217. v.x += dockerPos->x;
  218. v.y += dockerPos->y;
  219. v.z = dockerPos->z; // keep Z height the same and just scoot along the ground
  220. // set the new position
  221. docker->setPosition( &v );
  222. //
  223. // set the model condition for the object as "moving" even though it really
  224. // isn't in the traditional sense, but we don't want them to scoot slide into
  225. // the transport and look wierd
  226. //
  227. docker->setModelConditionState( MODELCONDITION_MOVING );
  228. // if we're at the destination then stop and put is inside the dock object
  229. Real distSq = ThePartitionManager->getDistanceSquared( docker, us, FROM_CENTER_2D );
  230. Real closeEnoughDistance = 6.0f;
  231. if( distSq <= (closeEnoughDistance * closeEnoughDistance) )
  232. {
  233. // the object is now no longer "moving"
  234. docker->clearModelConditionState( MODELCONDITION_MOVING );
  235. // stop the dock action
  236. cancelDock( docker );
  237. // stop the docker from doing anything by going idle
  238. AIUpdateInterface *dockerAI = docker->getAIUpdateInterface();
  239. if( dockerAI )
  240. dockerAI->aiIdle( CMD_FROM_AI );
  241. // put object inside us
  242. ContainModuleInterface *contain = us->getContain();
  243. if( contain )
  244. {
  245. contain->addToContain( docker );
  246. }
  247. // no object is docking now
  248. m_dockingObjectID = INVALID_ID;
  249. } // end if
  250. } // end if
  251. } // end if
  252. } // end doPullInDocking
  253. // ------------------------------------------------------------------------------------------------
  254. /** If we have an object recorded as being pushed out of us then do that here */
  255. // ------------------------------------------------------------------------------------------------
  256. void RailedTransportDockUpdate::doPushOutDocking( void )
  257. {
  258. if( m_unloadingObjectID )
  259. {
  260. Object *unloader = TheGameLogic->findObjectByID( m_unloadingObjectID );
  261. // if unloader is not found (like they got destroyed) unload the next object inside
  262. if( unloader == NULL )
  263. {
  264. unloadNext();
  265. return;
  266. } // end if
  267. // pull it
  268. if( unloader )
  269. {
  270. const Coord3D *unloaderPos = unloader->getPosition();
  271. // get the destination point as the DOCKEND
  272. Coord3D destPos;
  273. getExitPosition( unloader, &destPos );
  274. destPos.z = TheTerrainLogic->getGroundHeight( destPos.x, destPos.y );
  275. // get the vector from the unloader to the destination point
  276. Coord3D v;
  277. v.x = destPos.x - unloaderPos->x;
  278. v.y = destPos.y - unloaderPos->y;
  279. v.z = destPos.z - unloaderPos->z;
  280. v.normalize();
  281. // apply "movement" to that vector
  282. v.x *= m_pushOutsideDistancePerFrame;
  283. v.y *= m_pushOutsideDistancePerFrame;
  284. // apply current position of the unloader to the vector
  285. v.x += unloaderPos->x;
  286. v.y += unloaderPos->y;
  287. v.z = destPos.z; // keep Z height the same and just scoot along the ground
  288. // set the new position
  289. unloader->setPosition( &v );
  290. //
  291. // set the model condition for the object as "moving" even though it really
  292. // isn't in the traditional sense, but we don't want them to scoot slide into
  293. // the transport and look wierd
  294. //
  295. unloader->setModelConditionState( MODELCONDITION_MOVING );
  296. // if we're at the destination then stop and unload the next object if present
  297. Real distSq = sqr( destPos.x - v.x ) + sqr( destPos.y - v.y ) + sqr( destPos.z - v.z );
  298. Real closeEnoughDistance = 3.0f;
  299. if( distSq <= sqr( closeEnoughDistance ) )
  300. {
  301. Object *us = getObject();
  302. // the object is now no longer "moving"
  303. unloader->clearModelConditionState( MODELCONDITION_MOVING );
  304. // set the unloaded object as idle
  305. AIUpdateInterface *unloaderAI = unloader->getAIUpdateInterface();
  306. if( unloaderAI )
  307. unloaderAI->aiIdle( CMD_FROM_AI );
  308. // clear the held status from this unloading object
  309. unloader->clearDisabled( DISABLED_HELD );
  310. // we can now be selected by the player again
  311. unloader->clearStatus( OBJECT_STATUS_UNSELECTABLE );
  312. // tell the unloader to move to one of the dock positions and out of the way
  313. Drawable *draw = us->getDrawable();
  314. if( unloaderAI && draw )
  315. {
  316. Coord3D finalPos;
  317. draw->getPristineBonePositions( "DOCKWAITING07", 0, &finalPos, NULL, 1 );
  318. us->convertBonePosToWorldPos( &finalPos, NULL, &finalPos, NULL );
  319. unloaderAI->aiMoveToPosition( &finalPos, CMD_FROM_AI );
  320. } // end if
  321. // unload the next object
  322. unloadNext();
  323. } // end if
  324. } // end if
  325. } // end if, m_unloadingID
  326. } // end doPushOutDocking
  327. // ------------------------------------------------------------------------------------------------
  328. /** Iterate callback for the finding the first contained object */
  329. // ------------------------------------------------------------------------------------------------
  330. static void getFirstContain( Object *obj, void *userData )
  331. {
  332. Object **firstContain = (Object **)userData;
  333. // if object has been found get out of here
  334. if( *firstContain != NULL )
  335. return;
  336. // assign this as the first object found
  337. *firstContain = obj;
  338. } // end getFirstContain
  339. // ------------------------------------------------------------------------------------------------
  340. /** Start the next object contained by us as "unloading and coming out" */
  341. // ------------------------------------------------------------------------------------------------
  342. void RailedTransportDockUpdate::unloadNext( void )
  343. {
  344. Object *us = getObject();
  345. // by default, setup our unloading process to be done with no objects being considered
  346. m_unloadingObjectID = INVALID_ID;
  347. //
  348. // if our unload count is zero we can't unload any more until we receive a command to
  349. // unload another one or everything we've got
  350. //
  351. if( m_unloadCount == 0 )
  352. return;
  353. // better be an open container
  354. ContainModuleInterface *contain = us->getContain();
  355. OpenContain *openContain = contain ? contain->asOpenContain() : NULL;
  356. DEBUG_ASSERTCRASH( openContain, ("Unloading next from railed transport, but '%s' has no open container\n",
  357. us->getTemplate()->getName().str()) );
  358. // get the first contained object
  359. Object *unloader = NULL;
  360. openContain->iterateContained( getFirstContain, &unloader, FALSE );
  361. if( unloader )
  362. {
  363. // remove us from the container
  364. openContain->removeFromContain( unloader );
  365. // set position of the loader to our position
  366. unloader->setPosition( us->getPosition() );
  367. // orient unloader to the same angle as us so we can drive out the front
  368. unloader->setOrientation( us->getOrientation() );
  369. // mark us as HELD so physics or anything else can't mess with our position
  370. unloader->setDisabled( DISABLED_HELD );
  371. //
  372. // get the dock point that we're going to go to ... that is where we came in
  373. // at the DOCKEND point
  374. //
  375. Coord3D dockPosition;
  376. getExitPosition( unloader, &dockPosition );
  377. // get unloader position
  378. const Coord3D *unloaderPos = unloader->getPosition();
  379. // how far is it from our current position to the dock position
  380. Coord3D v;
  381. v.x = dockPosition.x - unloaderPos->x;
  382. v.y = dockPosition.y - unloaderPos->y;
  383. v.z = dockPosition.z - unloaderPos->z;
  384. Real mag = v.length();
  385. // now that we know how far we must go, now much distance should we travel every frame
  386. const RailedTransportDockUpdateModuleData *modData = getRailedTransportDockUpdateModuleData();
  387. m_pushOutsideDistancePerFrame = mag / modData->m_pushOutsideDurationInFrames;
  388. // set this as our current unloader
  389. m_unloadingObjectID = unloader->getID();
  390. // we've now used an unload (if we're keeping count for single exits)
  391. if( m_unloadCount != UNLOAD_ALL )
  392. --m_unloadCount;
  393. } // end if
  394. } // end unloadNext
  395. // ------------------------------------------------------------------------------------------------
  396. /** CRC */
  397. // ------------------------------------------------------------------------------------------------
  398. void RailedTransportDockUpdate::crc( Xfer *xfer )
  399. {
  400. // extend base class
  401. DockUpdate::crc( xfer );
  402. } // end crc
  403. // ------------------------------------------------------------------------------------------------
  404. /** Xfer method
  405. * Version Info:
  406. * 1: Initial version */
  407. // ------------------------------------------------------------------------------------------------
  408. void RailedTransportDockUpdate::xfer( Xfer *xfer )
  409. {
  410. // version
  411. XferVersion currentVersion = 1;
  412. XferVersion version = currentVersion;
  413. xfer->xferVersion( &version, currentVersion );
  414. // extend base class
  415. DockUpdate::xfer( xfer );
  416. // docking object id
  417. xfer->xferObjectID( &m_dockingObjectID );
  418. // pull inside distance per frame
  419. xfer->xferReal( &m_pullInsideDistancePerFrame );
  420. // unloading object id
  421. xfer->xferObjectID( &m_unloadingObjectID );
  422. // push outside distance per frame
  423. xfer->xferReal( &m_pushOutsideDistancePerFrame );
  424. // unload count
  425. xfer->xferInt( &m_unloadCount );
  426. } // end xfer
  427. // ------------------------------------------------------------------------------------------------
  428. /** Load post process */
  429. // ------------------------------------------------------------------------------------------------
  430. void RailedTransportDockUpdate::loadPostProcess( void )
  431. {
  432. // extend base class
  433. DockUpdate::loadPostProcess();
  434. } // end loadPostProcess