CountermeasuresBehavior.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418
  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) 2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: CountermeasuresBehavior.cpp //////////////////////////////////////////////////////////////
  24. // Author: Kris Morness, April 2003
  25. // Desc: Handles countermeasure firing when under missile threat, and responsible
  26. // for diverting missiles to the flares.
  27. ///////////////////////////////////////////////////////////////////////////////////////////////////
  28. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  29. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  30. #include "Common/Thing.h"
  31. #include "Common/ThingTemplate.h"
  32. #include "Common/INI.h"
  33. #include "Common/Player.h"
  34. #include "Common/ThingFactory.h"
  35. #include "Common/Xfer.h"
  36. #include "GameClient/ParticleSys.h"
  37. #include "GameClient/Anim2D.h"
  38. #include "GameClient/InGameUI.h"
  39. #include "GameLogic/Module/CountermeasuresBehavior.h"
  40. #include "GameLogic/Module/BodyModule.h"
  41. #include "GameLogic/Module/PhysicsUpdate.h"
  42. #include "GameLogic/GameLogic.h"
  43. #include "GameLogic/Object.h"
  44. #include "GameLogic/PartitionManager.h"
  45. #ifdef _INTERNAL
  46. // for occasional debugging...
  47. //#pragma optimize("", off)
  48. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  49. #endif
  50. //-------------------------------------------------------------------------------------------------
  51. //-------------------------------------------------------------------------------------------------
  52. struct CountermeasuresPlayerScanHelper
  53. {
  54. KindOfMaskType m_kindOfToTest;
  55. Object *m_theHealer;
  56. ObjectPointerList *m_objectList;
  57. };
  58. static void checkForCountermeasures( Object *testObj, void *userData )
  59. {
  60. CountermeasuresPlayerScanHelper *helper = (CountermeasuresPlayerScanHelper*)userData;
  61. ObjectPointerList *listToAddTo = helper->m_objectList;
  62. if( testObj->isEffectivelyDead() )
  63. return;
  64. if( testObj->getControllingPlayer() != helper->m_theHealer->getControllingPlayer() )
  65. return;
  66. if( testObj->isOffMap() )
  67. return;
  68. if( !testObj->isAnyKindOf(helper->m_kindOfToTest) )
  69. return;
  70. if( testObj->getBodyModule()->getHealth() >= testObj->getBodyModule()->getMaxHealth() )
  71. return;
  72. listToAddTo->push_back(testObj);
  73. }
  74. //-------------------------------------------------------------------------------------------------
  75. //-------------------------------------------------------------------------------------------------
  76. CountermeasuresBehavior::CountermeasuresBehavior( Thing *thing, const ModuleData* moduleData ) : UpdateModule( thing, moduleData )
  77. {
  78. const CountermeasuresBehaviorModuleData *data = getCountermeasuresBehaviorModuleData();
  79. m_availableCountermeasures = data->m_numberOfVolleys * data->m_volleySize;
  80. m_reactionFrame = 0;
  81. m_activeCountermeasures = 0;
  82. m_divertedMissiles = 0;
  83. m_incomingMissiles = 0;
  84. m_nextVolleyFrame = 0;
  85. setWakeFrame( getObject(), UPDATE_SLEEP_NONE );
  86. }
  87. //-------------------------------------------------------------------------------------------------
  88. //-------------------------------------------------------------------------------------------------
  89. CountermeasuresBehavior::~CountermeasuresBehavior( void )
  90. {
  91. }
  92. // ------------------------------------------------------------------------------------------------
  93. void CountermeasuresBehavior::reportMissileForCountermeasures( Object *missile )
  94. {
  95. if( !missile )
  96. {
  97. return;
  98. }
  99. //Record the number of missiles that have been fired at us
  100. m_incomingMissiles++;
  101. if( m_availableCountermeasures + m_activeCountermeasures > 0 )
  102. {
  103. //We have countermeasures we can use. Determine now whether or not the incoming missile will
  104. //be diverted.
  105. const CountermeasuresBehaviorModuleData *data = getCountermeasuresBehaviorModuleData();
  106. if( GameLogicRandomValueReal( 0.0f, 1.0f ) < data->m_evasionRate )
  107. {
  108. //This missile will be diverted!
  109. ProjectileUpdateInterface* pui = NULL;
  110. for( BehaviorModule** u = missile->getBehaviorModules(); *u; ++u )
  111. {
  112. if( (pui = (*u)->getProjectileUpdateInterface()) != NULL )
  113. {
  114. //Make sure the missile diverts after a delay. The delay needs to be larger than
  115. //the countermeasure reaction time or else the missile won't have a countermeasure to divert to!
  116. DEBUG_ASSERTCRASH( data->m_countermeasureReactionFrames < data->m_missileDecoyFrames,
  117. ("MissileDecoyDelay needs to be less than CountermeasureReactionTime in order to function properly.") );
  118. pui->setFramesTillCountermeasureDiversionOccurs( data->m_missileDecoyFrames );
  119. m_divertedMissiles++;
  120. if( m_activeCountermeasures == 0 && m_reactionFrame == 0 )
  121. {
  122. //We need to launch our first volley of countermeasures, but we can't do it now. If we
  123. //do, it'll look too artificial. Instead, we need to set up a timer to fake a reaction
  124. //delay.
  125. m_reactionFrame = TheGameLogic->getFrame() + data->m_countermeasureReactionFrames;
  126. }
  127. break;
  128. }
  129. }
  130. }
  131. }
  132. }
  133. //-------------------------------------------------------------------------------------------------
  134. ObjectID CountermeasuresBehavior::calculateCountermeasureToDivertTo( const Object& victim )
  135. {
  136. const CountermeasuresBehaviorModuleData *data = getCountermeasuresBehaviorModuleData();
  137. //Flares are pushed to the front of the list, but we only want to acquire the "newest" of the flares, therefore
  138. //stop iterating after we've reached size of a single volley.
  139. Int iteratorMax = MAX( data->m_volleySize, 1 );
  140. Real closestDist = 1e15f;
  141. Object *closestFlare = NULL;
  142. //Start at the end of the list and go towards the beginning.
  143. CountermeasuresVec::iterator it = m_counterMeasures.end();
  144. //end is actually the end so advance the iterator.
  145. if( it )
  146. {
  147. --it;
  148. while( iteratorMax-- )
  149. {
  150. Object *obj = TheGameLogic->findObjectByID( *it );
  151. if( obj )
  152. {
  153. Real dist = ThePartitionManager->getDistanceSquared( obj, getObject(), FROM_CENTER_2D );
  154. if( dist < closestDist )
  155. {
  156. closestDist = dist;
  157. closestFlare = obj;
  158. }
  159. }
  160. else
  161. {
  162. --it;
  163. }
  164. }
  165. }
  166. if( closestFlare )
  167. {
  168. return closestFlare->getID();
  169. }
  170. return INVALID_ID;
  171. }
  172. //-------------------------------------------------------------------------------------------------
  173. Bool CountermeasuresBehavior::isActive() const
  174. {
  175. return isUpgradeActive();
  176. }
  177. //-------------------------------------------------------------------------------------------------
  178. /** The update callback. */
  179. //-------------------------------------------------------------------------------------------------
  180. UpdateSleepTime CountermeasuresBehavior::update( void )
  181. {
  182. UnsignedInt now = TheGameLogic->getFrame();
  183. const CountermeasuresBehaviorModuleData *data = getCountermeasuresBehaviorModuleData();
  184. Object *obj = getObject();
  185. if( obj->isEffectivelyDead() )
  186. {
  187. return UPDATE_SLEEP_FOREVER;
  188. }
  189. if( !isUpgradeActive() )
  190. {
  191. return UPDATE_SLEEP_FOREVER;
  192. }
  193. //Validate all existing flares, and clean them up as needed.
  194. for (CountermeasuresVec::iterator it = m_counterMeasures.begin(); it != m_counterMeasures.end(); /*nothing*/ )
  195. {
  196. Object *obj = TheGameLogic->findObjectByID( *it );
  197. if( !obj )
  198. {
  199. it = m_counterMeasures.erase( it );
  200. m_activeCountermeasures--;
  201. }
  202. else
  203. {
  204. ++it;
  205. }
  206. }
  207. if( obj->isAirborneTarget() )
  208. {
  209. //Handle flare volley launching (initial reaction, and continuation firing).
  210. if( m_availableCountermeasures )
  211. {
  212. //Deal with the initial volley, but wait until we are permitted to react.
  213. if( m_reactionFrame )
  214. {
  215. if( m_reactionFrame == now )
  216. {
  217. //We have been shot at and now that the reaction timer has expired, fire a full volley of
  218. //countermeasures.
  219. launchVolley();
  220. m_nextVolleyFrame = now + data->m_framesBetweenVolleys;
  221. m_reactionFrame = 0;
  222. }
  223. }
  224. //Handle subsequent volley launching.
  225. if( m_nextVolleyFrame == now )
  226. {
  227. launchVolley();
  228. m_nextVolleyFrame = now + data->m_framesBetweenVolleys;
  229. }
  230. }
  231. }
  232. //Handle auto-reloading (data->m_reloadFrames of zero means it's not possible to auto-reload).
  233. //Aircraft that don't auto-reload require landing at an airfield for resupply.
  234. if( !m_availableCountermeasures && data->m_reloadFrames )
  235. {
  236. if( m_reloadFrame != 0 )
  237. {
  238. if( m_reloadFrame <= now )
  239. {
  240. //We've successfully reloaded automatically.
  241. reloadCountermeasures();
  242. }
  243. }
  244. else
  245. {
  246. //We just started reloading, so set the frame it'll be ready.
  247. m_reloadFrame = now + data->m_reloadFrames;
  248. }
  249. }
  250. return UPDATE_SLEEP( UPDATE_SLEEP_NONE );
  251. }
  252. //-------------------------------------------------------------------------------------------------
  253. void CountermeasuresBehavior::reloadCountermeasures()
  254. {
  255. const CountermeasuresBehaviorModuleData *data = getCountermeasuresBehaviorModuleData();
  256. m_availableCountermeasures = data->m_numberOfVolleys * data->m_volleySize;
  257. m_reloadFrame = 0;
  258. }
  259. //-------------------------------------------------------------------------------------------------
  260. void CountermeasuresBehavior::launchVolley()
  261. {
  262. const CountermeasuresBehaviorModuleData *data = getCountermeasuresBehaviorModuleData();
  263. Object *obj = getObject();
  264. Real volleySize = (Real)data->m_volleySize;
  265. for( int i = 0; i < data->m_volleySize; i++ )
  266. {
  267. //Each flare in a volley will calculate a different vector to fly out. We have a +/- angle to
  268. //spread out equally. With only one flare, it'll come straight out the back. Two flares will
  269. //launch at the extreme positive and negative angle. Three flares will launch at extreme angles
  270. //plus straight back. Four or more will divy it up equally.
  271. Real currentVolley = (Real)i;
  272. Real ratio = 0.0f;
  273. if( volleySize != 1.0f )
  274. {
  275. //ratio between -1.0 and +1.0f
  276. ratio = currentVolley / (volleySize - 1.0f) * 2.0f - 1.0f;
  277. }
  278. //Now calculate the angle. Simply multiply it by the ratio!
  279. Real angle = ratio * data->m_volleyArcAngle;
  280. Coord3D vel;
  281. PhysicsBehavior *physics = obj->getPhysics();
  282. //Calculate the angle to fire the flare by taking the facing angle and rotating it
  283. //and then scaling it by it's velocity (if it's moving).
  284. obj->getUnitDirectionVector3D( vel );
  285. Vector2 flareVector;
  286. flareVector.X = vel.x;
  287. flareVector.Y = vel.y;
  288. flareVector.Normalize();
  289. flareVector.Rotate( angle );
  290. //Give it back to the Coord3D
  291. vel.x = flareVector.X;
  292. vel.y = flareVector.Y;
  293. vel.z = 0.0f;
  294. Real velocity = physics->getVelocityMagnitude();
  295. if( velocity < 1.0f )
  296. {
  297. velocity = -10.0f;
  298. }
  299. vel.scale( velocity * data->m_volleyVelocityFactor );
  300. const ThingTemplate *thing = TheThingFactory->findTemplate( data->m_flareTemplateName );
  301. if( thing )
  302. {
  303. Object *flare = TheThingFactory->newObject( thing, obj->getControllingPlayer()->getDefaultTeam() );
  304. flare->setPosition( obj->getPosition() );
  305. flare->setOrientation( obj->getOrientation() );
  306. physics->transferVelocityTo( flare->getPhysics() );
  307. flare->getPhysics()->applyMotiveForce( &vel );
  308. m_activeCountermeasures++;
  309. m_availableCountermeasures--;
  310. m_counterMeasures.push_back( flare->getID() );
  311. }
  312. }
  313. }
  314. //------------------------------------------------------------------------------------------------
  315. /** CRC */
  316. //------------------------------------------------------------------------------------------------
  317. void CountermeasuresBehavior::crc( Xfer *xfer )
  318. {
  319. // extend base class
  320. UpdateModule::crc( xfer );
  321. // extend base class
  322. UpgradeMux::upgradeMuxCRC( xfer );
  323. } // end crc
  324. //------------------------------------------------------------------------------------------------
  325. /** Xfer method
  326. * Version Info:
  327. * 1: Initial version */
  328. //------------------------------------------------------------------------------------------------
  329. void CountermeasuresBehavior::xfer( Xfer *xfer )
  330. {
  331. // version
  332. XferVersion currentVersion = 2;
  333. XferVersion version = currentVersion;
  334. xfer->xferVersion( &version, currentVersion );
  335. // extend base class
  336. UpdateModule::xfer( xfer );
  337. // extend base class
  338. UpgradeMux::upgradeMuxXfer( xfer );
  339. if( currentVersion >= 2 )
  340. {
  341. xfer->xferSTLObjectIDVector( &m_counterMeasures );
  342. xfer->xferUnsignedInt( &m_availableCountermeasures );
  343. xfer->xferUnsignedInt( &m_activeCountermeasures );
  344. xfer->xferUnsignedInt( &m_divertedMissiles );
  345. xfer->xferUnsignedInt( &m_incomingMissiles );
  346. xfer->xferUnsignedInt( &m_reactionFrame );
  347. xfer->xferUnsignedInt( &m_nextVolleyFrame );
  348. }
  349. } // end xfer
  350. //------------------------------------------------------------------------------------------------
  351. /** Load post process */
  352. //------------------------------------------------------------------------------------------------
  353. void CountermeasuresBehavior::loadPostProcess( void )
  354. {
  355. // extend base class
  356. UpdateModule::loadPostProcess();
  357. // extend base class
  358. UpgradeMux::upgradeMuxLoadPostProcess();
  359. } // end loadPostProcess