AssaultTransportAIUpdate.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539
  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. // AssaultTransportAIUpdate.cpp ////////////
  24. // Author: Kris Morness, December 2002
  25. // Desc: State machine that allows assault transports (troop crawler) to deploy
  26. // troops, order them to attack, then return. Can do extra things like ordering
  27. // injured troops to return to the transport for healing purposes.
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #include "Common/Player.h"
  30. #include "Common/ThingFactory.h"
  31. #include "GameClient/Drawable.h"
  32. #include "GameClient/InGameUI.h"
  33. #include "GameLogic/ExperienceTracker.h"
  34. #include "GameLogic/Module/BodyModule.h"
  35. #include "GameLogic/Module/ContainModule.h"
  36. #include "GameLogic/Module/AssaultTransportAIUpdate.h"
  37. #include "GameLogic/Module/PhysicsUpdate.h"
  38. #include "GameLogic/Object.h"
  39. #include "GameLogic/PartitionManager.h"
  40. #include "GameLogic/Weapon.h"
  41. #ifdef _INTERNAL
  42. // for occasional debugging...
  43. //#pragma optimize("", off)
  44. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  45. #endif
  46. //-------------------------------------------------------------------------------------------------
  47. //-------------------------------------------------------------------------------------------------
  48. //-------------------------------------------------------------------------------------------------
  49. //-------------------------------------------------------------------------------------------------
  50. AssaultTransportAIUpdate::AssaultTransportAIUpdate( Thing *thing, const ModuleData* moduleData ) : AIUpdateInterface( thing, moduleData )
  51. {
  52. m_currentMembers = MAX_TRANSPORT_SLOTS; //First time, max it out, to ensure clearing arrays in reset.
  53. reset();
  54. }
  55. //-------------------------------------------------------------------------------------------------
  56. void AssaultTransportAIUpdate::reset()
  57. {
  58. for( int i = 0; i < m_currentMembers; i++ )
  59. {
  60. m_memberIDs[ i ] = INVALID_ID;
  61. m_memberHealing[ i ] = FALSE;
  62. m_newMember[ i ] = FALSE;
  63. }
  64. m_currentMembers = 0;
  65. m_attackMoveGoalPos.zero();
  66. m_designatedTarget = INVALID_ID;
  67. m_state = IDLE;
  68. m_framesRemaining = 0;
  69. m_isAttackMove = FALSE;
  70. m_isAttackObject = FALSE;
  71. m_newOccupantsAreNewMembers = FALSE;
  72. }
  73. //-------------------------------------------------------------------------------------------------
  74. AssaultTransportAIUpdate::~AssaultTransportAIUpdate( void )
  75. {
  76. }
  77. //-------------------------------------------------------------------------------------------------
  78. void AssaultTransportAIUpdate::aiDoCommand(const AICommandParms* parms)
  79. {
  80. //Inspect the command and reset everything when necessary.
  81. if( parms->m_cmdSource != CMD_FROM_AI )
  82. {
  83. //Now the only time we care about anything is if we were ordered to attack something or attack move.
  84. switch( parms->m_cmd )
  85. {
  86. case AICMD_ATTACKMOVE_TO_POSITION:
  87. //Reset because we have been ordered to do something.
  88. reset();
  89. m_attackMoveGoalPos = parms->m_pos;
  90. m_isAttackMove = TRUE;
  91. break;
  92. case AICMD_ATTACK_OBJECT:
  93. //Reset because we have been ordered to do something.
  94. reset();
  95. //m_designatedTarget = parms->m_obj ? parms->m_obj->getID() : INVALID_ID;
  96. m_isAttackObject = TRUE;
  97. break;
  98. case AICMD_IDLE:
  99. m_designatedTarget = INVALID_ID;
  100. //Order all outside members back inside!
  101. retrieveMembers();
  102. reset();
  103. break;
  104. default:
  105. //Reset because we have been ordered to do something we're not handling.
  106. reset();
  107. break;
  108. }
  109. }
  110. //Note, in both cases, the transport will fire a dummy DEPLOY weapon that will trigger the
  111. //evacuation of the troops.
  112. AIUpdateInterface::aiDoCommand( parms );
  113. }
  114. //-------------------------------------------------------------------------------------------------
  115. void AssaultTransportAIUpdate::beginAssault( const Object *designatedTarget ) const
  116. {
  117. //The transport has determined it is in range to begin the assault (via weapon system).
  118. //Now order the evacuation of healthy troops, and let the update handle moving them.
  119. if( designatedTarget )
  120. {
  121. m_designatedTarget = designatedTarget->getID();
  122. }
  123. }
  124. //-------------------------------------------------------------------------------------------------
  125. Bool AssaultTransportAIUpdate::isIdle() const
  126. {
  127. return AIUpdateInterface::isIdle();
  128. }
  129. //-------------------------------------------------------------------------------------------------
  130. UpdateSleepTime calcSleepTime()
  131. {
  132. return UPDATE_SLEEP_NONE;
  133. }
  134. //-------------------------------------------------------------------------------------------------
  135. UpdateSleepTime AssaultTransportAIUpdate::update( void )
  136. {
  137. Object *transport = getObject();
  138. //const AssaultTransportAIUpdateModuleData *data = getAssaultTransportAIUpdateModuleData();
  139. if( transport->isEffectivelyDead() )
  140. {
  141. giveFinalOrders();
  142. return UPDATE_SLEEP_FOREVER;
  143. }
  144. //First removing dead members or members that have been ordered to do something outside of this AI.
  145. if( m_currentMembers )
  146. {
  147. for( int i = 0; i < m_currentMembers; i++ )
  148. {
  149. Object *member = TheGameLogic->findObjectByID( m_memberIDs[ i ] );
  150. AIUpdateInterface *ai = member ? member->getAI() : NULL;
  151. if( !member || member->isEffectivelyDead() || ai->getLastCommandSource() != CMD_FROM_AI )
  152. {
  153. //Member is toast -- so remove him from our list!
  154. if( m_currentMembers - 1 > i )
  155. {
  156. //Move the last slot to this slot to keep array contiguous.
  157. m_memberIDs[ i ] = m_memberIDs[ m_currentMembers - 1 ];
  158. m_memberHealing[ i ] = m_memberHealing[ m_currentMembers - 1 ];
  159. m_newMember[ i ] = m_newMember[ m_currentMembers - 1 ];
  160. }
  161. else
  162. {
  163. //Just clean out last slot.
  164. m_memberIDs[ i ] = INVALID_ID;
  165. m_memberHealing[ i ] = FALSE;
  166. m_newMember[ i ] = FALSE;
  167. }
  168. if( ai )
  169. {
  170. //Important! Members of our assault transport must be allowed to chase down designated enemies.
  171. //Generally only player commands allow this, so this flag allows AI commands to do the same.
  172. //We need to turn this off though, because this ex-member is no longer under transport control.
  173. ai->setAllowedToChase( FALSE );
  174. }
  175. m_currentMembers--;
  176. }
  177. }
  178. }
  179. //Now add any potentially new members to the group.
  180. ContainModuleInterface *contain = transport->getContain();
  181. if( contain )
  182. {
  183. const ContainedItemsList *passengerList = contain->getContainedItemsList();
  184. ContainedItemsList::const_iterator passengerIterator;
  185. passengerIterator = passengerList->begin();
  186. while( passengerIterator != passengerList->end() )
  187. {
  188. Object *passenger = *passengerIterator;
  189. //Advance to the next iterator
  190. passengerIterator++;
  191. //Make sure it isn't in our list already.
  192. Bool found = FALSE;
  193. for( int i = 0; i < m_currentMembers; i++ )
  194. {
  195. if( passenger->getID() == m_memberIDs[ i ] )
  196. {
  197. //He is in the list... so skip him.
  198. found = TRUE;
  199. break;
  200. }
  201. }
  202. if( found )
  203. {
  204. //Get next passenger.
  205. continue;
  206. }
  207. //It's possible to add members manually -- but if we already have 10 members, then wait!
  208. if( m_currentMembers < MAX_TRANSPORT_SLOTS )
  209. {
  210. //Not in list, so add him!
  211. m_memberIDs[ m_currentMembers ] = passenger->getID();
  212. if( passenger->getAI() )
  213. {
  214. //Important! Members of our assault transport must be allowed to chase down designated enemies.
  215. //Generally only player commands allow this, so this flag allows AI commands to do the same.
  216. passenger->getAI()->setAllowedToChase( TRUE );
  217. }
  218. //Check if the passenger is wounded below threshhold (if so make sure we heal him before ordering him to fight!)
  219. if( isMemberWounded( passenger ) )
  220. {
  221. m_memberHealing[ m_currentMembers ] = TRUE;
  222. }
  223. if( m_newOccupantsAreNewMembers )
  224. {
  225. //New members won't eject out until a new attack order is issued.
  226. m_newMember[ m_currentMembers ] = TRUE;
  227. }
  228. m_currentMembers++;
  229. }
  230. }
  231. m_newOccupantsAreNewMembers = TRUE;
  232. }
  233. if( isAttackPointless() )
  234. {
  235. aiIdle( CMD_FROM_AI );
  236. return UPDATE_SLEEP_NONE;
  237. }
  238. //Keep track of the average position of all combat units assigned to me.
  239. Coord3D fighterCentroidPos;
  240. UnsignedInt fightingMembers = 0;
  241. fighterCentroidPos.zero();
  242. //If we're already in the process, reacquire the designated target again... see if
  243. //it's still alive.
  244. Object *designatedTarget = TheGameLogic->findObjectByID( m_designatedTarget );
  245. if( designatedTarget && designatedTarget->isEffectivelyDead() )
  246. {
  247. designatedTarget = NULL;
  248. }
  249. if( designatedTarget )
  250. {
  251. //Look for members not currently attacking this target.
  252. for( int i = 0; i < m_currentMembers; i++ )
  253. {
  254. Object *member = TheGameLogic->findObjectByID( m_memberIDs[ i ] );
  255. AIUpdateInterface *ai = member ? member->getAI() : NULL;
  256. if( member && ai )
  257. {
  258. Bool contained = member->isContained();
  259. Bool wounded = isMemberWounded( member );
  260. if( contained && isMemberHealthy( member ) && !m_newMember[ i ] )
  261. {
  262. //This contained member is healthy so order him to exit to start fighting!
  263. //New members are exempt!
  264. ai->aiExit( transport, CMD_FROM_AI );
  265. }
  266. if( !contained )
  267. {
  268. if( wounded )
  269. {
  270. if( ai->getAIStateType() != AI_ENTER )
  271. {
  272. //Order wounded members back to get healed.
  273. ai->aiEnter( transport, CMD_FROM_AI );
  274. }
  275. }
  276. else
  277. {
  278. //Increment the number of fighters and their position.
  279. fighterCentroidPos.add( member->getPosition() );
  280. fightingMembers++;
  281. if( !ai->isMoving() )
  282. {
  283. if( ai->getGoalObject() != designatedTarget )
  284. {
  285. //Okay, this dude is outside and waiting... order him to attack the designated target
  286. ai->aiAttackObject( designatedTarget, NO_MAX_SHOTS_LIMIT, CMD_FROM_AI );
  287. }
  288. }
  289. }
  290. }
  291. }
  292. }
  293. }
  294. else
  295. {
  296. if( m_isAttackMove && getAIStateType() != AI_ATTACK_MOVE_TO )
  297. {
  298. //Continue to move towards the attackmove area.
  299. aiAttackMoveToPosition( &m_attackMoveGoalPos, NO_MAX_SHOTS_LIMIT, CMD_FROM_AI );
  300. }
  301. else if( m_isAttackObject )
  302. {
  303. retrieveMembers();
  304. }
  305. }
  306. /*
  307. //Keep near the troops.
  308. if( !m_framesRemaining )
  309. {
  310. if( !isMoving() && fightingMembers && designatedTarget )
  311. {
  312. m_framesRemaining = 45;
  313. //Get centriod pos now that we know the number of fighting members.
  314. Real scale = 1.0f / (Real)fightingMembers;
  315. fighterCentroidPos.scale( scale );
  316. Coord3D designatedTargetPos = *designatedTarget->getPosition();
  317. //Calculate a vector from the target passed the fighters to be at a safe place
  318. //to be as a transport.
  319. Coord3D vector;
  320. vector.set( &fighterCentroidPos );
  321. vector.sub( &designatedTargetPos );
  322. vector.normalize();
  323. vector.scale( 150.0f );
  324. Coord3D transportGoalPos;
  325. transportGoalPos.set( &designatedTargetPos );
  326. transportGoalPos.add( &vector );
  327. Real distanceSqrd = ThePartitionManager->getDistanceSquared( transport, &transportGoalPos, FROM_CENTER_2D );
  328. if( distanceSqrd > 40.0f * 40.0f )
  329. {
  330. //Order the transport to move to the safer position
  331. //aiMoveToPosition( &transportGoalPos, CMD_FROM_AI );
  332. }
  333. }
  334. }
  335. else
  336. {
  337. m_framesRemaining--;
  338. }
  339. if( designatedTarget && !isMoving() )
  340. {
  341. //Order the transport to face the designated target!
  342. //aiFaceObject( designatedTarget, CMD_FROM_AI );
  343. }
  344. */
  345. /*UpdateSleepTime ret =*/ AIUpdateInterface::update();
  346. //return (mine < ret) ? mine : ret;
  347. /// @todo srj -- someday, make sleepy. for now, must not sleep.
  348. return UPDATE_SLEEP_NONE;
  349. }
  350. //-------------------------------------------------------------------------------------------------
  351. Bool AssaultTransportAIUpdate::isAttackPointless() const
  352. {
  353. //If all members are new members (thus can't attack), and the transport itself
  354. //is still attacking, stop!
  355. const Object *transport = getObject();
  356. if( transport->testStatus( OBJECT_STATUS_IS_ATTACKING ) )
  357. {
  358. for( int i = 0; i < m_currentMembers; i++ )
  359. {
  360. if( !m_newMember[ i ] )
  361. {
  362. //We have a non-new member, so attack is valid.
  363. return FALSE;
  364. }
  365. }
  366. //We are trying to attack, but can't because all our members are new.
  367. return TRUE;
  368. }
  369. //We aren't trying to attack, so everything is good.
  370. return FALSE;
  371. }
  372. //-------------------------------------------------------------------------------------------------
  373. Bool AssaultTransportAIUpdate::isMemberWounded( const Object *member ) const
  374. {
  375. const AssaultTransportAIUpdateModuleData *data = getAssaultTransportAIUpdateModuleData();
  376. BodyModuleInterface *body = member->getBodyModule();
  377. if( body )
  378. {
  379. Real ratio = body->getHealth() / body->getMaxHealth();
  380. if( ratio < data->m_membersGetHealedAtLifeRatio )
  381. {
  382. return TRUE;
  383. }
  384. }
  385. return FALSE;
  386. }
  387. //-------------------------------------------------------------------------------------------------
  388. Bool AssaultTransportAIUpdate::isMemberHealthy( const Object *member ) const
  389. {
  390. BodyModuleInterface *body = member->getBodyModule();
  391. if( body )
  392. {
  393. if( body->getHealth() == body->getMaxHealth() )
  394. {
  395. return TRUE;
  396. }
  397. }
  398. return FALSE;
  399. }
  400. //-------------------------------------------------------------------------------------------------
  401. void AssaultTransportAIUpdate::retrieveMembers()
  402. {
  403. //Order all outside members back inside!
  404. for( int i = 0; i < m_currentMembers; i++ )
  405. {
  406. Object *member = TheGameLogic->findObjectByID( m_memberIDs[ i ] );
  407. AIUpdateInterface *ai = member ? member->getAI() : NULL;
  408. if( member && ai )
  409. {
  410. Bool contained = member->isContained();
  411. if( !contained )
  412. {
  413. //This contained member is healthy so order him to exit to start fighting!
  414. ai->aiEnter( getObject(), CMD_FROM_AI );
  415. }
  416. }
  417. }
  418. }
  419. //-------------------------------------------------------------------------------------------------
  420. void AssaultTransportAIUpdate::giveFinalOrders()
  421. {
  422. //All members have been ejected outside already -- transfer the original order to the troops
  423. for( int i = 0; i < m_currentMembers; i++ )
  424. {
  425. Object *member = TheGameLogic->findObjectByID( m_memberIDs[ i ] );
  426. AIUpdateInterface *ai = member ? member->getAI() : NULL;
  427. if( member && ai )
  428. {
  429. Object *designatedTarget = TheGameLogic->findObjectByID( m_designatedTarget );
  430. if( m_isAttackObject && designatedTarget )
  431. {
  432. ai->aiAttackObject( designatedTarget, NO_MAX_SHOTS_LIMIT, CMD_FROM_PLAYER );
  433. }
  434. else if( m_isAttackMove )
  435. {
  436. ai->aiAttackMoveToPosition( &m_attackMoveGoalPos, NO_MAX_SHOTS_LIMIT, CMD_FROM_PLAYER );
  437. }
  438. ai->setAllowedToChase( FALSE );
  439. }
  440. }
  441. }
  442. //-------------------------------------------------------------------------------------------------
  443. /** CRC */
  444. //-------------------------------------------------------------------------------------------------
  445. void AssaultTransportAIUpdate::crc( Xfer *xfer )
  446. {
  447. // extend base class
  448. AIUpdateInterface::crc(xfer);
  449. } // end crc
  450. //-------------------------------------------------------------------------------------------------
  451. /** Xfer method
  452. * Version Info:
  453. * 1: Initial version */
  454. //-------------------------------------------------------------------------------------------------
  455. void AssaultTransportAIUpdate::xfer( Xfer *xfer )
  456. {
  457. // version
  458. XferVersion currentVersion = 1;
  459. XferVersion version = currentVersion;
  460. xfer->xferVersion( &version, currentVersion );
  461. // extend base class
  462. AIUpdateInterface::xfer(xfer);
  463. xfer->xferInt( &m_currentMembers );
  464. for( int i = 0; i < m_currentMembers; i++ )
  465. {
  466. xfer->xferObjectID( &(m_memberIDs[ i ]) );
  467. xfer->xferBool( &(m_memberHealing[ i ]) );
  468. }
  469. xfer->xferCoord3D( &m_attackMoveGoalPos );
  470. xfer->xferObjectID( &m_designatedTarget );
  471. Int state = (Int)m_state;
  472. xfer->xferInt( &state );
  473. m_state = (AssaultStateTypes)state;
  474. xfer->xferUnsignedInt( &m_framesRemaining );
  475. xfer->xferBool( &m_isAttackMove );
  476. xfer->xferBool( &m_isAttackObject );
  477. } // end xfer
  478. //-------------------------------------------------------------------------------------------------
  479. /** Load post process */
  480. //-------------------------------------------------------------------------------------------------
  481. void AssaultTransportAIUpdate::loadPostProcess( void )
  482. {
  483. // extend base class
  484. AIUpdateInterface::loadPostProcess();
  485. } // end loadPostProcess