RailedTransportDockUpdate.cpp 20 KB

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