MobMemberSlavedUpdate.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471
  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. ///////////////////////////////////////////////////////////////////////////////////////////////////
  24. // MobMemberSlavedUpdate.cpp ///////////////////////////////////////////////////////////////////////////
  25. // Will obey spawner... or die trying
  26. // Author: Mark Lorenzen, August 2002
  27. // Desc: Slaved unit(s) remain close to their master. Used by angry Mob members (various)
  28. ///////////////////////////////////////////////////////////////////////////////////////////////////
  29. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  30. #include "GameClient/InGameUI.h"// selection logic
  31. #include "GameClient/Drawable.h"
  32. #include "Common/RandomValue.h"
  33. #include "Common/Xfer.h"
  34. #include "GameClient/Drawable.h"
  35. #include "GameClient/ParticleSys.h"
  36. #include "GameLogic/AIPathfind.h"
  37. #include "GameLogic/Damage.h"
  38. #include "GameLogic/GameLogic.h"
  39. #include "GameLogic/Locomotor.h"
  40. #include "GameLogic/Object.h"
  41. #include "GameLogic/PartitionManager.h"
  42. #include "GameLogic/TerrainLogic.h"
  43. #include "GameLogic/Module/AIUpdate.h"
  44. #include "GameLogic/Module/BodyModule.h"
  45. #include "GameLogic/Module/MobMemberSlavedUpdate.h"
  46. #include "GameLogic/Module/SpawnBehavior.h"
  47. #include "GameClient/InGameUI.h"// selection logic
  48. #include "GameClient/Drawable.h"
  49. #include "Common/ThingFactory.h"
  50. #include "Common/ThingTemplate.h"
  51. #ifdef _INTERNAL
  52. // for occasional debugging...
  53. //#pragma optimize("", off)
  54. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  55. #endif
  56. #define STRAY_MULTIPLIER 2.0f // Multiplier from stating diestance from tunnel, to max distance from
  57. const Real CLOSE_ENOUGH = 15; // Our moveTo commands and pathfinding can't handle people in the way, so quit trying to hump someone on your spot
  58. const Real CLOSE_ENOUGH_SQR = (CLOSE_ENOUGH * CLOSE_ENOUGH);
  59. //-------------------------------------------------------------------------------------------------
  60. MobMemberSlavedUpdate::MobMemberSlavedUpdate( Thing *thing, const ModuleData* moduleData ) : UpdateModule( thing, moduleData )
  61. {
  62. m_slaver = INVALID_ID;
  63. m_framesToWait = GameLogicRandomValue(0,20);
  64. // MDC: moving to GameLogicRandomValue. This does not need to be synced, but having it so makes searches *so* much nicer.
  65. m_personalColor.red = GameLogicRandomValueReal( 0.2f, 0.4f );
  66. m_personalColor.green = GameLogicRandomValueReal( 0.2f, 0.4f );
  67. m_personalColor.blue = GameLogicRandomValueReal( 0.2f, 0.4f );
  68. // Drawable *myDraw = getObject()->getDrawable();
  69. // if ( myDraw )
  70. // myDraw->colorTint( &m_personalColor );
  71. m_mobState = MOB_STATE_NONE;
  72. m_primaryVictimID = INVALID_ID;
  73. m_squirrellinessRatio = 0;
  74. m_isSelfTasking = FALSE;
  75. m_catchUpCrisisTimer = 0;
  76. // MDC: moving to GameLogicRandomValue. This does not need to be synced, but having it so makes searches *so* much nicer.
  77. //getObject()->getDrawable()->setInstanceScale(GameLogicRandomValueReal( 5.0f, 1.5f ));
  78. }
  79. //-------------------------------------------------------------------------------------------------
  80. MobMemberSlavedUpdate::~MobMemberSlavedUpdate( void )
  81. {
  82. }
  83. //-------------------------------------------------------------------------------------------------
  84. void MobMemberSlavedUpdate::onObjectCreated()
  85. {
  86. const MobMemberSlavedUpdateModuleData* data = getMobMemberSlavedUpdateModuleData();
  87. m_squirrellinessRatio = MIN(MAX_SQUIRRELLINESS, MAX(0, data->m_squirrellinessRatio));
  88. }
  89. //-------------------------------------------------------------------------------------------------
  90. void MobMemberSlavedUpdate::onEnslave( const Object *slaver )
  91. {
  92. startSlavedEffects( slaver );
  93. }
  94. //-------------------------------------------------------------------------------------------------
  95. void MobMemberSlavedUpdate::onSlaverDie( const DamageInfo *info )
  96. {
  97. stopSlavedEffects();
  98. }
  99. //-------------------------------------------------------------------------------------------------
  100. void MobMemberSlavedUpdate::onSlaverDamage( const DamageInfo *info )
  101. {
  102. // Only slaves with a ProneUpdate will even care.
  103. AIUpdateInterface *ai = getObject()->getAIUpdateInterface();
  104. if( ai )
  105. ai->aiGoProne( info, CMD_FROM_AI );
  106. }
  107. //-------------------------------------------------------------------------------------------------
  108. UpdateSleepTime MobMemberSlavedUpdate::update( void )
  109. {
  110. /// @todo srj use SLEEPY_UPDATE here
  111. const MobMemberSlavedUpdateModuleData* data = getMobMemberSlavedUpdateModuleData();
  112. Object *me = getObject();
  113. if( !me )
  114. {
  115. return UPDATE_SLEEP_NONE;
  116. }
  117. Object *master = TheGameLogic->findObjectByID( m_slaver );
  118. if( master == NULL )
  119. {
  120. stopSlavedEffects();
  121. //TheGameLogic->destroyObject( me );
  122. me->kill();
  123. return UPDATE_SLEEP_NONE; // you cannot return SLEEP_FOREVER unless you make yourself sleepy...
  124. }
  125. AIUpdateInterface *myAI = me->getAIUpdateInterface();
  126. AIUpdateInterface *masterAI = master->getAIUpdateInterface();
  127. if( ! myAI || ! masterAI)
  128. {
  129. return UPDATE_SLEEP_NONE;
  130. }
  131. Drawable *myDraw = me->getDrawable();
  132. Drawable *masterDraw = master->getDrawable();
  133. if ( ! myDraw || ! masterDraw)
  134. {
  135. return UPDATE_SLEEP_NONE;
  136. }
  137. // myDraw->colorTint( &m_personalColor );
  138. const ModelConditionFlags flags = myDraw->getModelConditionFlags();
  139. if (flags.anyIntersectionWith(MAKE_MODELCONDITION_MASK(MODELCONDITION_WEAPONSET_PLAYER_UPGRADE)))
  140. {
  141. ModelConditionFlags clearFlags;
  142. clearFlags.clear();
  143. clearFlags.set( MODELCONDITION_RELOADING_A );
  144. clearFlags.set( MODELCONDITION_BETWEEN_FIRING_SHOTS_A );
  145. clearFlags.set( MODELCONDITION_PREATTACK_A );
  146. clearFlags.set( MODELCONDITION_FIRING_A );
  147. clearFlags.set( MODELCONDITION_USING_WEAPON_A );
  148. myDraw->clearModelConditionFlags( clearFlags );
  149. }
  150. if ( ++m_framesToWait < 16)
  151. return UPDATE_SLEEP_NONE;
  152. m_framesToWait = 0;
  153. Locomotor *locomotor = myAI->getCurLocomotor();
  154. if( !locomotor )
  155. {
  156. return UPDATE_SLEEP_NONE;
  157. }
  158. Object *victim = getObject()->getAIUpdateInterface()->getCurrentVictim();
  159. Object *masterVictim = master->getAIUpdateInterface()->getCurrentVictim();
  160. if (masterVictim)
  161. {
  162. m_primaryVictimID = masterVictim->getID();
  163. }
  164. Object *primaryVictim = TheGameLogic->findObjectByID(m_primaryVictimID);
  165. //now, we don't know if master is standing still or going somewhere, so
  166. Real masterPathDistToGoal = masterAI->getLocomotorDistanceToGoal();
  167. Real myPathDistToGoal = myAI->getLocomotorDistanceToGoal();
  168. Real catchUpRadiusSquared = ThePartitionManager->getDistanceSquared( me, master, FROM_CENTER_3D );
  169. // I'm too far from the nexus... I need to catch up now!
  170. if( catchUpRadiusSquared > sqr( data->m_mustCatchUpRadius ) )
  171. {
  172. if ( masterAI->isMoving() )// master is on the move
  173. {
  174. if ( masterPathDistToGoal > myPathDistToGoal ) // I'm getting ahead of master, need to slow down
  175. myAI->chooseLocomotorSet(LOCOMOTORSET_WANDER);
  176. else
  177. myAI->chooseLocomotorSet(LOCOMOTORSET_PANIC); // I'm lagging, so I need to snap to it!
  178. Coord3D nuPos = *masterAI->getGoalPosition();
  179. if ( nuPos.length() < 1.0f ) //if a nasty error has sent me to map origin
  180. {
  181. myAI->aiMoveToPosition( master->getPosition(), CMD_FROM_AI ); // NASTY BEEHIVE EFFECT
  182. }
  183. else
  184. {
  185. Coord3D goalDelta = *myAI->getGoalPosition();
  186. goalDelta.sub( &nuPos );
  187. if ( goalDelta.length() > 5.0f * PATHFIND_CELL_SIZE_F )// only if I am not headed there already
  188. {
  189. myAI->aiMoveToPosition( &nuPos, CMD_FROM_AI ); // Whither thou goest... THis causes the mob to reconverge
  190. }
  191. }
  192. // on the fly, instead of doubling back to reconverge
  193. }
  194. else // master is still, so let's re group in a hurry
  195. {
  196. myAI->chooseLocomotorSet(LOCOMOTORSET_PANIC);
  197. myAI->aiMoveToPosition( master->getPosition(), CMD_FROM_AI );
  198. }
  199. if (catchUpRadiusSquared > sqr( data->m_mustCatchUpRadius * 3))// I am critically far, now!
  200. {
  201. ++ m_catchUpCrisisTimer; // I'm way too far from the nexus this frame
  202. if ( m_catchUpCrisisTimer > data->m_catchUpCrisisBailTime)
  203. {
  204. me->kill();
  205. return UPDATE_SLEEP_NONE;
  206. // Here is the rethink:
  207. // If the nexus has outrun me to the target by so much, //
  208. // lets make the nexus come to me, and try to return to where I find it
  209. // Coord3D masterPosition = *master->getPosition();
  210. // master->setPosition( me->getPosition() );
  211. // if ( ! masterAI->isMoving() )
  212. // {
  213. // masterAI->aiMoveToPosition( &masterPosition, CMD_FROM_AI );
  214. // }
  215. // m_catchUpCrisisTimer = 0;
  216. }
  217. else if ( m_catchUpCrisisTimer > data->m_catchUpCrisisBailTime/3 )
  218. {
  219. myAI->aiMoveToPosition( master->getPosition(), CMD_FROM_AI ); // NASTY BEEHIVE EFFECT
  220. }
  221. }
  222. }
  223. else if ( myAI->isMoving() ) // we're all on a trip, together
  224. {
  225. m_catchUpCrisisTimer = 0; // I'm not too far from the nexus this frame
  226. Int seed = GameLogicRandomValue( 0, 10 );
  227. if ( seed == 1 )
  228. myAI->chooseLocomotorSet(LOCOMOTORSET_WANDER);
  229. else if ( seed == 2 )
  230. myAI->chooseLocomotorSet(LOCOMOTORSET_PANIC);
  231. else if ( seed == 3 )
  232. myAI->chooseLocomotorSet(LOCOMOTORSET_NORMAL);
  233. // else if ( seed >= 5 ) // go towards mommy's goal
  234. // {
  235. // Coord3D destination = *me->getPosition();
  236. // TheAI->pathfinder()->adjustToPossibleDestination(me, myAI->getLocomotorSet(), &destination);
  237. // myAI->aiMoveToPosition( &destination, CMD_FROM_AI ); // reconverge
  238. // }
  239. }
  240. else // give me something to do while I'm standing here...
  241. {
  242. m_catchUpCrisisTimer = 0; // I'm not too far from the nexus this frame
  243. SpawnBehaviorInterface *spawnerBehavior = master->getSpawnBehaviorInterface();
  244. if ( spawnerBehavior ) // if I have a mommy
  245. {
  246. if ( masterAI->isIdle() ) // if controlling player has pressed stop, we stop! That's it!
  247. {
  248. myAI->aiIdle(CMD_FROM_AI);
  249. primaryVictim = NULL;
  250. m_primaryVictimID = INVALID_ID;
  251. return UPDATE_SLEEP_NONE;
  252. }
  253. if ( spawnerBehavior->maySpawnSelfTaskAI( m_squirrellinessRatio ) ) // if mommy says it is okay
  254. {
  255. if ( myAI->getLastCommandSource() != CMD_FROM_AI ) // I may have been told to attack directly more recently
  256. {
  257. Object *newTarget = myAI->getNextMoodTarget( FALSE, FALSE );
  258. if ( newTarget && ( newTarget != victim) ) // if there is someone else around to attack
  259. {
  260. victim = newTarget;
  261. myAI->aiAttackObject( newTarget, 999, CMD_FROM_AI ); // go ahead and do it
  262. m_isSelfTasking = TRUE;
  263. }
  264. }
  265. }
  266. if ( ! victim ) // If I still don't have anyone to shoot at
  267. {
  268. if ( primaryVictim ) // I remember the last target
  269. {
  270. myAI->aiAttackObject( primaryVictim, 999, CMD_FROM_AI );
  271. }
  272. else if( ! masterAI->isAttacking())// there Is no previous target and master isn't attacking
  273. {
  274. /// myAI->aiIdle( CMD_FROM_AI );// auto acquire mode
  275. }
  276. m_isSelfTasking = FALSE;
  277. }
  278. }
  279. else
  280. {
  281. DEBUG_ASSERTCRASH(( spawnerBehavior != NULL ),("Hey!, why for this mob member got no spawner? MLorenzen"));
  282. }
  283. }
  284. return UPDATE_SLEEP_NONE;
  285. }
  286. //-------------------------------------------------------------------------------------------------
  287. // We are too far from nexus, so we need to catch-up
  288. //-------------------------------------------------------------------------------------------------
  289. void MobMemberSlavedUpdate::doCatchUpLogic( Coord3D *pos )
  290. {
  291. Coord3D nuPos;
  292. const MobMemberSlavedUpdateModuleData* data = getMobMemberSlavedUpdateModuleData();
  293. // recalc where we want to be if we wander around
  294. Real randomDirection = GameLogicRandomValue( 0, 2*PI );
  295. Real randomRadius = GameLogicRandomValue( 0, data->m_noNeedToCatchUpRadius );
  296. nuPos.set(pos);
  297. nuPos.x += randomRadius * Cos( randomDirection );
  298. nuPos.y += randomRadius * Sin( randomDirection );
  299. nuPos.z = TheTerrainLogic->getGroundHeight( nuPos.x, nuPos.y );
  300. AIUpdateInterface *ai = getObject()->getAIUpdateInterface();
  301. if( ai )
  302. {
  303. ai->aiMoveToPosition( &nuPos, CMD_FROM_AI );
  304. }
  305. setMobState(MOB_STATE_CATCHING_UP);
  306. }
  307. //-------------------------------------------------------------------------------------------------
  308. void MobMemberSlavedUpdate::startSlavedEffects( const Object *slaver )
  309. {
  310. if( slaver == NULL )
  311. return;
  312. m_slaver = slaver->getID();
  313. // mark selves as not selectable
  314. //getObject()->setStatus( OBJECT_STATUS_UNSELECTABLE );
  315. }
  316. //-------------------------------------------------------------------------------------------------
  317. void MobMemberSlavedUpdate::stopSlavedEffects()
  318. {
  319. m_slaver = INVALID_ID;
  320. /// @todo Just a thought. Our Status bits on objects really need to be reference counts so you don't clear someone else's flag
  321. getObject()->clearStatus( OBJECT_STATUS_UNSELECTABLE );
  322. getObject()->clearDisabled( DISABLED_HELD );
  323. }
  324. // ------------------------------------------------------------------------------------------------
  325. /** CRC */
  326. // ------------------------------------------------------------------------------------------------
  327. void MobMemberSlavedUpdate::crc( Xfer *xfer )
  328. {
  329. // extend base class
  330. UpdateModule::crc( xfer );
  331. } // end crc
  332. // ------------------------------------------------------------------------------------------------
  333. /** Xfer method
  334. * Version Info:
  335. * 1: Initial version */
  336. // ------------------------------------------------------------------------------------------------
  337. void MobMemberSlavedUpdate::xfer( Xfer *xfer )
  338. {
  339. // version
  340. XferVersion currentVersion = 1;
  341. XferVersion version = currentVersion;
  342. xfer->xferVersion( &version, currentVersion );
  343. // extend base class
  344. UpdateModule::xfer( xfer );
  345. // slaves
  346. xfer->xferObjectID( &m_slaver );
  347. // frames to wait
  348. xfer->xferInt( &m_framesToWait );
  349. // mob state
  350. xfer->xferUser( &m_mobState, sizeof( MobStates ) );
  351. // personal color
  352. xfer->xferRGBColor( &m_personalColor );
  353. // primary victim
  354. xfer->xferObjectID( &m_primaryVictimID );
  355. // squirrelliness ration
  356. xfer->xferReal( &m_squirrellinessRatio );
  357. // is self tasking
  358. xfer->xferBool( &m_isSelfTasking );
  359. // catch up crisis timer
  360. xfer->xferUnsignedInt( &m_catchUpCrisisTimer );
  361. } // end xfer
  362. // ------------------------------------------------------------------------------------------------
  363. /** Load post process */
  364. // ------------------------------------------------------------------------------------------------
  365. void MobMemberSlavedUpdate::loadPostProcess( void )
  366. {
  367. // extend base class
  368. UpdateModule::loadPostProcess();
  369. } // end loadPostProcess