TurretAI.cpp 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441
  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. // TurretAI.cpp
  24. // Turret behavior implementation
  25. // Author: Steven Johnson, April 2002
  26. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  27. #define DEFINE_WEAPONSLOTTYPE_NAMES
  28. #include "Common/GameAudio.h"
  29. #include "Common/PerfTimer.h"
  30. #include "Common/RandomValue.h"
  31. #include "Common/ThingTemplate.h"
  32. #include "Common/Xfer.h"
  33. #include "GameLogic/GameLogic.h"
  34. #include "GameLogic/Module/AIUpdate.h"
  35. #include "GameLogic/Object.h"
  36. #include "GameLogic/PartitionManager.h"
  37. #include "GameLogic/TerrainLogic.h"
  38. #include "GameLogic/TurretAI.h"
  39. #include "GameLogic/Weapon.h"
  40. #include "GameLogic/WeaponSet.h"
  41. const UnsignedInt WAIT_INDEFINITELY = 0xffffffff;
  42. #ifdef _INTERNAL
  43. // for occasional debugging...
  44. //#pragma optimize("", off)
  45. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  46. #endif
  47. // ------------------------------------------------------------------------------------------------
  48. // ------------------------------------------------------------------------------------------------
  49. static StateReturnType frameToSleepTime(
  50. UnsignedInt frame1,
  51. UnsignedInt frame2 = FOREVER,
  52. UnsignedInt frame3 = FOREVER,
  53. UnsignedInt frame4 = FOREVER
  54. )
  55. {
  56. if (frame1 > frame2) frame1 = frame2;
  57. if (frame1 > frame3) frame1 = frame3;
  58. if (frame1 > frame4) frame1 = frame4;
  59. UnsignedInt now = TheGameLogic->getFrame();
  60. if (frame1 > now)
  61. {
  62. return STATE_SLEEP(frame1 - now);
  63. }
  64. else
  65. {
  66. // ignore times that are in the past, since this can frequently happen
  67. return STATE_CONTINUE;
  68. }
  69. }
  70. //----------------------------------------------------------------------------------------------------------
  71. //----------------------------------------------------------------------------------------------------------
  72. //----------------------------------------------------------------------------------------------------------
  73. //----------------------------------------------------------------------------------------------------------
  74. /**
  75. * Create a TurretAI state machine. Define all of the states the machine
  76. * can possibly be in, and set the initial (default) state.
  77. */
  78. TurretStateMachine::TurretStateMachine( TurretAI* tai, Object *obj, AsciiString name ) : m_turretAI(tai), StateMachine( obj, name )
  79. {
  80. static const StateConditionInfo fireConditions[] =
  81. {
  82. StateConditionInfo(outOfWeaponRangeObject, TURRETAI_AIM, NULL),
  83. StateConditionInfo(NULL, NULL, NULL) // keep last
  84. };
  85. // order matters: first state is the default state.
  86. defineState( TURRETAI_IDLE, newInstance(TurretAIIdleState)( this ), TURRETAI_IDLE, TURRETAI_IDLESCAN );
  87. defineState( TURRETAI_IDLESCAN, newInstance(TurretAIIdleScanState)( this ), TURRETAI_HOLD, TURRETAI_HOLD );
  88. defineState( TURRETAI_AIM, newInstance(TurretAIAimTurretState)( this ), TURRETAI_FIRE, TURRETAI_HOLD );
  89. defineState( TURRETAI_FIRE, newInstance(AIAttackFireWeaponState)( this, tai ), TURRETAI_AIM, TURRETAI_AIM, fireConditions );
  90. defineState( TURRETAI_RECENTER, newInstance(TurretAIRecenterTurretState)( this ), TURRETAI_IDLE, TURRETAI_IDLE );
  91. defineState( TURRETAI_HOLD, newInstance(TurretAIHoldTurretState)( this ), TURRETAI_RECENTER, TURRETAI_RECENTER );
  92. }
  93. //----------------------------------------------------------------------------------------------------------
  94. TurretStateMachine::~TurretStateMachine()
  95. {
  96. }
  97. //----------------------------------------------------------------------------------------------------------
  98. void TurretStateMachine::clear()
  99. {
  100. StateMachine::clear();
  101. TurretAI* turret = getTurretAI();
  102. if (turret)
  103. turret->friend_notifyStateMachineChanged();
  104. }
  105. //----------------------------------------------------------------------------------------------------------
  106. StateReturnType TurretStateMachine::resetToDefaultState()
  107. {
  108. StateReturnType tmp = StateMachine::resetToDefaultState();
  109. TurretAI* turret = getTurretAI();
  110. if (turret)
  111. turret->friend_notifyStateMachineChanged();
  112. return tmp;
  113. }
  114. //----------------------------------------------------------------------------------------------------------
  115. StateReturnType TurretStateMachine::setState(StateID newStateID)
  116. {
  117. StateID oldID = getCurrentStateID();
  118. StateReturnType tmp = StateMachine::setState(newStateID);
  119. TurretAI* turret = getTurretAI();
  120. if (turret && oldID != newStateID)
  121. turret->friend_notifyStateMachineChanged();
  122. return tmp;
  123. }
  124. // ------------------------------------------------------------------------------------------------
  125. /** CRC */
  126. // ------------------------------------------------------------------------------------------------
  127. void TurretStateMachine::crc( Xfer *xfer )
  128. {
  129. } // end crc
  130. // ------------------------------------------------------------------------------------------------
  131. /** Xfer Method */
  132. // ------------------------------------------------------------------------------------------------
  133. void TurretStateMachine::xfer( Xfer *xfer )
  134. {
  135. XferVersion cv = 1;
  136. XferVersion v = cv;
  137. xfer->xferVersion( &v, cv );
  138. } // end xfer
  139. // ------------------------------------------------------------------------------------------------
  140. /** Load post process */
  141. // ------------------------------------------------------------------------------------------------
  142. void TurretStateMachine::loadPostProcess( void )
  143. {
  144. } // end loadPostProcess
  145. //----------------------------------------------------------------------------------------------------------
  146. //----------------------------------------------------------------------------------------------------------
  147. //----------------------------------------------------------------------------------------------------------
  148. //----------------------------------------------------------------------------------------------------------
  149. TurretAIData::TurretAIData()
  150. {
  151. m_turnRate = DEFAULT_TURN_RATE;
  152. m_pitchRate = DEFAULT_PITCH_RATE;
  153. m_naturalTurretAngle = 0.0f;
  154. m_naturalTurretPitch = 0.0f;
  155. for( Int slotIndex = 0; slotIndex < WEAPONSLOT_COUNT; ++slotIndex )
  156. {
  157. m_turretFireAngleSweep[slotIndex] = 0.0f;
  158. m_turretSweepSpeedModifier[slotIndex] = 1.0f;
  159. }
  160. m_firePitch = 0.0f;
  161. m_minPitch = 0.0f;
  162. m_groundUnitPitch = 0;
  163. m_turretWeaponSlots = 0;
  164. #ifdef INTER_TURRET_DELAY
  165. m_interTurretDelay = 0;
  166. #endif
  167. m_minIdleScanAngle = 0.0f;
  168. m_maxIdleScanAngle = 0.0f;
  169. m_groundUnitPitch = 0.0f;
  170. m_minIdleScanInterval = 9999999;
  171. m_maxIdleScanInterval = 9999999;
  172. m_recenterTime = 2*LOGICFRAMES_PER_SECOND;
  173. m_initiallyDisabled = false;
  174. m_firesWhileTurning = FALSE;
  175. m_isAllowsPitch = false;
  176. }
  177. //-------------------------------------------------------------------------------------------------
  178. static void parseTWS(INI* ini, void * /*instance*/, void * store, const void* /*userData*/)
  179. {
  180. UnsignedInt* tws = (UnsignedInt*)store;
  181. const char* token = ini->getNextToken();
  182. while (token != NULL)
  183. {
  184. WeaponSlotType wslot = (WeaponSlotType)INI::scanIndexList(token, TheWeaponSlotTypeNames);
  185. *tws |= (1 << wslot);
  186. token = ini->getNextTokenOrNull();
  187. }
  188. }
  189. //-------------------------------------------------------------------------------------------------
  190. void TurretAIData::parseTurretSweep(INI* ini, void *instance, void * /*store*/, const void* userData)
  191. {
  192. TurretAIData* self = (TurretAIData*)instance;
  193. WeaponSlotType wslot = (WeaponSlotType)INI::scanIndexList(ini->getNextToken(), TheWeaponSlotTypeNames);
  194. INI::parseAngleReal( ini, instance, &self->m_turretFireAngleSweep[wslot], NULL );
  195. }
  196. //-------------------------------------------------------------------------------------------------
  197. void TurretAIData::parseTurretSweepSpeed(INI* ini, void *instance, void * /*store*/, const void* userData)
  198. {
  199. TurretAIData* self = (TurretAIData*)instance;
  200. WeaponSlotType wslot = (WeaponSlotType)INI::scanIndexList(ini->getNextToken(), TheWeaponSlotTypeNames);
  201. INI::parseReal( ini, instance, &self->m_turretSweepSpeedModifier[wslot], NULL );
  202. }
  203. //----------------------------------------------------------------------------------------------------------
  204. void TurretAIData::buildFieldParse(MultiIniFieldParse& p)
  205. {
  206. static const FieldParse dataFieldParse[] =
  207. {
  208. { "TurretTurnRate", INI::parseAngularVelocityReal, NULL, offsetof( TurretAIData, m_turnRate ) },
  209. { "TurretPitchRate", INI::parseAngularVelocityReal, NULL, offsetof( TurretAIData, m_pitchRate ) },
  210. { "NaturalTurretAngle", INI::parseAngleReal, NULL, offsetof( TurretAIData, m_naturalTurretAngle ) },
  211. { "NaturalTurretPitch", INI::parseAngleReal, NULL, offsetof( TurretAIData, m_naturalTurretPitch ) },
  212. { "FirePitch", INI::parseAngleReal, NULL, offsetof( TurretAIData, m_firePitch ) },
  213. { "MinPhysicalPitch", INI::parseAngleReal, NULL, offsetof( TurretAIData, m_minPitch ) },
  214. { "GroundUnitPitch", INI::parseAngleReal, NULL, offsetof( TurretAIData, m_groundUnitPitch ) },
  215. { "TurretFireAngleSweep", TurretAIData::parseTurretSweep, NULL, NULL },
  216. { "TurretSweepSpeedModifier",TurretAIData::parseTurretSweepSpeed, NULL, NULL },
  217. { "ControlledWeaponSlots", parseTWS, NULL, offsetof( TurretAIData, m_turretWeaponSlots ) },
  218. { "AllowsPitch", INI::parseBool, NULL, offsetof( TurretAIData, m_isAllowsPitch ) },
  219. #ifdef INTER_TURRET_DELAY
  220. { "InterTurretDelay", INI::parseDurationUnsignedInt, NULL, offsetof( TurretAIData, m_interTurretDelay ) },
  221. #endif
  222. { "MinIdleScanAngle", INI::parseAngleReal, NULL, offsetof( TurretAIData, m_minIdleScanAngle ) },
  223. { "MaxIdleScanAngle", INI::parseAngleReal, NULL, offsetof( TurretAIData, m_maxIdleScanAngle ) },
  224. { "MinIdleScanInterval", INI::parseDurationUnsignedInt, NULL, offsetof( TurretAIData, m_minIdleScanInterval ) },
  225. { "MaxIdleScanInterval", INI::parseDurationUnsignedInt, NULL, offsetof( TurretAIData, m_maxIdleScanInterval ) },
  226. { "RecenterTime", INI::parseDurationUnsignedInt, NULL, offsetof( TurretAIData, m_recenterTime ) },
  227. { "InitiallyDisabled", INI::parseBool, NULL, offsetof( TurretAIData, m_initiallyDisabled ) },
  228. { "FiresWhileTurning", INI::parseBool, NULL, offsetof( TurretAIData, m_firesWhileTurning ) },
  229. { 0, 0, 0, 0 }
  230. };
  231. p.add(dataFieldParse);
  232. }
  233. //----------------------------------------------------------------------------------------------------------
  234. //----------------------------------------------------------------------------------------------------------
  235. //----------------------------------------------------------------------------------------------------------
  236. //----------------------------------------------------------------------------------------------------------
  237. TurretAI::TurretAI(Object* owner, const TurretAIData* data, WhichTurretType tur) :
  238. m_owner(owner),
  239. m_whichTurret(tur),
  240. m_data(data),
  241. m_turretStateMachine(NULL),
  242. m_playRotSound(false),
  243. m_playPitchSound(false),
  244. m_positiveSweep(true),
  245. m_enableSweepUntil(0),
  246. m_sleepUntil(0),
  247. m_didFire(false),
  248. m_target(TARGET_NONE),
  249. m_targetWasSetByIdleMood(false),
  250. m_enabled(!data->m_initiallyDisabled),
  251. m_firesWhileTurning(data->m_firesWhileTurning),
  252. m_isForceAttacking(false),
  253. //Added By Sadullah Nader
  254. //Initialization(s) inserted
  255. m_victimInitialTeam(NULL)
  256. //
  257. {
  258. //Added By Sadullah Nader
  259. //Initialization(s) inserted
  260. m_continuousFireExpirationFrame = -1;
  261. //
  262. if (!m_data)
  263. {
  264. DEBUG_CRASH(("TurretAI MUST have ModuleData"));
  265. throw INI_INVALID_DATA;
  266. }
  267. if (m_data->m_turretWeaponSlots == 0)
  268. {
  269. DEBUG_CRASH(("TurretAI MUST specify controlled weapon slots!"));
  270. throw INI_INVALID_DATA;
  271. }
  272. m_angle = getNaturalTurretAngle();
  273. m_pitch = getNaturalTurretPitch();
  274. #ifdef _DEBUG
  275. char smbuf[256];
  276. sprintf(smbuf, "TurretStateMachine for tur %08lx slot %d",this,tur);
  277. const char* smname = smbuf;
  278. #else
  279. const char* smname = "TurretStateMachine";
  280. #endif
  281. m_turretStateMachine = newInstance(TurretStateMachine)(this, m_owner, smname);
  282. m_turretStateMachine->initDefaultState();
  283. m_turretRotOrPitchSound = *(m_owner->getTemplate()->getPerUnitSound("TurretMoveLoop"));
  284. }
  285. //----------------------------------------------------------------------------------------------------------
  286. TurretAI::~TurretAI()
  287. {
  288. stopRotOrPitchSound();
  289. if (m_turretStateMachine)
  290. m_turretStateMachine->deleteInstance();
  291. }
  292. // ------------------------------------------------------------------------------------------------
  293. /** CRC */
  294. // ------------------------------------------------------------------------------------------------
  295. void TurretAI::crc( Xfer *xfer )
  296. {
  297. } // end crc
  298. // ------------------------------------------------------------------------------------------------
  299. /** Xfer Method */
  300. // ------------------------------------------------------------------------------------------------
  301. void TurretAI::xfer( Xfer *xfer )
  302. {
  303. // version
  304. const XferVersion currentVersion = 2;
  305. XferVersion version = currentVersion;
  306. xfer->xferVersion( &version, currentVersion );
  307. /* These 4 are loaded on creation, and don't change. jba.
  308. const TurretAIData* m_data;
  309. WhichTurretType m_whichTurret;
  310. Object* m_owner;
  311. AudioEventRTS m_turretRotOrPitchSound; ///< Sound of turret rotation
  312. */
  313. xfer->xferSnapshot(m_turretStateMachine);
  314. xfer->xferReal(&m_angle);
  315. xfer->xferReal(&m_pitch);
  316. xfer->xferUnsignedInt(&m_enableSweepUntil);
  317. xfer->xferUser(&m_target, sizeof(m_target));
  318. xfer->xferUnsignedInt(&m_continuousFireExpirationFrame);
  319. Bool tmpBool;
  320. #define UNPACK_AND_XFER(val) {tmpBool = val; xfer->xferBool(&tmpBool); val = tmpBool;}
  321. UNPACK_AND_XFER(m_playRotSound);
  322. UNPACK_AND_XFER(m_playPitchSound);
  323. UNPACK_AND_XFER(m_positiveSweep);
  324. UNPACK_AND_XFER(m_didFire);
  325. UNPACK_AND_XFER(m_enabled);
  326. UNPACK_AND_XFER(m_firesWhileTurning);
  327. UNPACK_AND_XFER(m_targetWasSetByIdleMood);
  328. #undef UNPACK_AND_XFER
  329. if (version >= 2)
  330. xfer->xferUnsignedInt(&m_sleepUntil);
  331. } // end xfer
  332. // ------------------------------------------------------------------------------------------------
  333. /** Load post process */
  334. // ------------------------------------------------------------------------------------------------
  335. void TurretAI::loadPostProcess( void )
  336. {
  337. Object *victim = m_turretStateMachine->getGoalObject();
  338. if (victim) {
  339. m_victimInitialTeam = victim->getTeam();
  340. }
  341. } // end loadPostProcess
  342. //----------------------------------------------------------------------------------------------------------
  343. Bool TurretAI::friend_turnTowardsAngle(Real desiredAngle, Real rateModifier, Real relThresh)
  344. {
  345. desiredAngle = normalizeAngle(desiredAngle);
  346. // rotate turret back to zero angle
  347. Real origAngle = getTurretAngle();
  348. Real actualAngle = origAngle;
  349. Real turnRate = getTurnRate() * rateModifier;
  350. Real angleDiff = normalizeAngle(desiredAngle - actualAngle);
  351. // Are we close enough to the desired angle to just snap there?
  352. if (fabs(angleDiff) < turnRate)
  353. {
  354. // we are centered
  355. actualAngle = desiredAngle;
  356. getOwner()->clearModelConditionState(MODELCONDITION_TURRET_ROTATE);
  357. }
  358. else
  359. {
  360. if (angleDiff > 0)
  361. actualAngle += turnRate;
  362. else
  363. actualAngle -= turnRate;
  364. getOwner()->setModelConditionState(MODELCONDITION_TURRET_ROTATE);
  365. m_playRotSound = true;
  366. }
  367. m_angle = normalizeAngle(actualAngle);
  368. if( m_angle != origAngle )
  369. getOwner()->reactToTurretChange( m_whichTurret, origAngle, m_pitch );
  370. Bool aligned = fabs(m_angle - desiredAngle) <= relThresh;
  371. return aligned;
  372. }
  373. //----------------------------------------------------------------------------------------------------------
  374. Bool TurretAI::friend_turnTowardsPitch(Real desiredPitch, Real rateModifier)
  375. {
  376. if (!isAllowsPitch())
  377. return true;
  378. desiredPitch = normalizeAngle(desiredPitch);
  379. // rotate turret back to zero angle
  380. Real actualPitch = getTurretPitch();
  381. Real pitchRate = getPitchRate() * rateModifier;
  382. Real pitchDiff = normalizeAngle(desiredPitch - actualPitch);
  383. if (fabs(pitchDiff) < pitchRate)
  384. {
  385. // we are centered
  386. actualPitch = desiredPitch;
  387. }
  388. else
  389. {
  390. if (pitchDiff > 0)
  391. actualPitch += pitchRate;
  392. else
  393. actualPitch -= pitchRate;
  394. m_playPitchSound = true;
  395. }
  396. m_pitch = normalizeAngle(actualPitch);
  397. return (m_pitch == desiredPitch);
  398. }
  399. //----------------------------------------------------------------------------------------------------------
  400. Bool TurretAI::isWeaponSlotOkToFire(WeaponSlotType wslot) const
  401. {
  402. return isWeaponSlotOnTurret(wslot);
  403. }
  404. //----------------------------------------------------------------------------------------------------------
  405. Real TurretAI::getTurretFireAngleSweepForWeaponSlot( WeaponSlotType slot ) const
  406. {
  407. return m_data->m_turretFireAngleSweep[slot];
  408. }
  409. //----------------------------------------------------------------------------------------------------------
  410. Real TurretAI::getTurretSweepSpeedModifierForWeaponSlot( WeaponSlotType slot ) const
  411. {
  412. return m_data->m_turretSweepSpeedModifier[slot];
  413. }
  414. //----------------------------------------------------------------------------------------------------------
  415. void TurretAI::notifyFired()
  416. {
  417. m_didFire = true;
  418. }
  419. //----------------------------------------------------------------------------------------------------------
  420. void TurretAI::notifyNewVictimChosen(Object* victim)
  421. {
  422. setTurretTargetObject(victim, FALSE);
  423. }
  424. //----------------------------------------------------------------------------------------------------------
  425. Bool TurretAI::isTryingToAimAtTarget(const Object* victim) const
  426. {
  427. StateID sid = m_turretStateMachine->getCurrentStateID();
  428. Object* obj;
  429. Coord3D pos;
  430. return (sid == TURRETAI_AIM && friend_getTurretTarget(obj, pos) == TARGET_OBJECT && obj == victim);
  431. }
  432. //----------------------------------------------------------------------------------------------------------
  433. Bool TurretAI::isOwnersCurWeaponOnTurret() const
  434. {
  435. WeaponSlotType wslot;
  436. Weapon* w = m_owner->getCurrentWeapon(&wslot);
  437. return w != NULL && isWeaponSlotOnTurret(wslot);
  438. }
  439. //----------------------------------------------------------------------------------------------------------
  440. Bool TurretAI::isWeaponSlotOnTurret(WeaponSlotType wslot) const
  441. {
  442. return (m_data->m_turretWeaponSlots & (1 << wslot)) != 0;
  443. }
  444. //----------------------------------------------------------------------------------------------------------
  445. TurretTargetType TurretAI::friend_getTurretTarget(Object*& obj, Coord3D& pos) const
  446. {
  447. obj = NULL;
  448. pos.zero();
  449. if (m_target == TARGET_OBJECT)
  450. {
  451. obj = m_turretStateMachine->getGoalObject();
  452. // clear it explicitly if null -- could be deceased but holding the
  453. // old (bogus) objectid internally
  454. if (obj == NULL || obj->isEffectivelyDead())
  455. {
  456. m_turretStateMachine->setGoalObject(NULL);
  457. m_target = TARGET_NONE;
  458. m_targetWasSetByIdleMood = false;
  459. }
  460. }
  461. else if (m_target == TARGET_POSITION)
  462. {
  463. obj = NULL;
  464. pos = *m_turretStateMachine->getGoalPosition();
  465. }
  466. return m_target;
  467. }
  468. //----------------------------------------------------------------------------------------------------------
  469. void TurretAI::removeSelfAsTargeter()
  470. {
  471. // be paranoid, in case we are called from dtors, etc.
  472. if (m_target == TARGET_OBJECT && m_turretStateMachine != NULL)
  473. {
  474. Object* self = m_owner;
  475. Object* target = m_turretStateMachine->getGoalObject();
  476. if (self != NULL && target != NULL)
  477. {
  478. AIUpdateInterface* targetAI = target->getAI();
  479. if (targetAI)
  480. {
  481. targetAI->addTargeter(self->getID(), false);
  482. }
  483. }
  484. }
  485. }
  486. //----------------------------------------------------------------------------------------------------------
  487. void TurretAI::setTurretTargetObject( Object *victim, Bool forceAttacking )
  488. {
  489. if (!victim ||
  490. victim->isEffectivelyDead() ||
  491. !isOwnersCurWeaponOnTurret())
  492. {
  493. victim = NULL;
  494. }
  495. if (victim == NULL)
  496. {
  497. // if nuking the victim, remove self as targeter before doing anything else.
  498. // (note that we never ADD self as targeter here; that is done in the aim state)
  499. removeSelfAsTargeter();
  500. }
  501. m_turretStateMachine->setGoalObject( victim ); // could be null! this is OK!
  502. m_target = victim ? TARGET_OBJECT : TARGET_NONE;
  503. m_targetWasSetByIdleMood = false;
  504. m_isForceAttacking = forceAttacking;
  505. StateID sid = m_turretStateMachine->getCurrentStateID();
  506. if (victim != NULL)
  507. {
  508. // if we're already in the aim state, don't call setState, since
  509. // it would go thru the exit/enter stuff, which we don't really want
  510. // to do...
  511. if (sid != TURRETAI_AIM && sid != TURRETAI_FIRE)
  512. m_turretStateMachine->setState( TURRETAI_AIM );
  513. m_victimInitialTeam = victim->getTeam();
  514. }
  515. else
  516. {
  517. // only change states if we are aiming.
  518. if (sid == TURRETAI_AIM || sid == TURRETAI_FIRE)
  519. m_turretStateMachine->setState(TURRETAI_HOLD);
  520. m_victimInitialTeam = NULL;
  521. }
  522. }
  523. //----------------------------------------------------------------------------------------------------------
  524. void TurretAI::setTurretTargetPosition( const Coord3D* pos )
  525. {
  526. if (!pos || !isOwnersCurWeaponOnTurret())
  527. {
  528. pos = NULL;
  529. }
  530. // remove self as targeter before doing anything else.
  531. // (note that we never ADD self as targeter here; that is done in the aim state)
  532. removeSelfAsTargeter();
  533. m_turretStateMachine->setGoalObject( NULL );
  534. if (pos)
  535. m_turretStateMachine->setGoalPosition( pos );
  536. m_target = pos ? TARGET_POSITION : TARGET_NONE;
  537. m_targetWasSetByIdleMood = false;
  538. StateID sid = m_turretStateMachine->getCurrentStateID();
  539. if (pos != NULL)
  540. {
  541. // if we're already in the aim state, don't call setState, since
  542. // it would go thru the exit/enter stuff, which we don't really want
  543. // to do...
  544. if (sid != TURRETAI_AIM && sid != TURRETAI_FIRE)
  545. m_turretStateMachine->setState( TURRETAI_AIM );
  546. m_victimInitialTeam = NULL;
  547. }
  548. else
  549. {
  550. // only change states if we are aiming.
  551. if (sid == TURRETAI_AIM || sid == TURRETAI_FIRE)
  552. m_turretStateMachine->setState(TURRETAI_HOLD);
  553. m_victimInitialTeam = NULL;
  554. }
  555. }
  556. //----------------------------------------------------------------------------------------------------------
  557. void TurretAI::recenterTurret()
  558. {
  559. m_turretStateMachine->setState( TURRETAI_RECENTER );
  560. }
  561. //----------------------------------------------------------------------------------------------------------
  562. Bool TurretAI::isTurretInNaturalPosition() const
  563. {
  564. if( getNaturalTurretAngle() == getTurretAngle() &&
  565. getNaturalTurretPitch() == getTurretPitch() )
  566. {
  567. return true;
  568. }
  569. return false;
  570. }
  571. //----------------------------------------------------------------------------------------------------------
  572. void TurretAI::friend_notifyStateMachineChanged()
  573. {
  574. m_sleepUntil = TheGameLogic->getFrame();
  575. }
  576. //----------------------------------------------------------------------------------------------------------
  577. /*
  578. Note that TurretAI isn't a behavior at all, so doesn't really "sleep"; however, it does return
  579. this information so that its AIUpdate owner can decide how long it (the AIUpdate) should sleep.
  580. So it behooves you to maximize "sleep" potential here! (srj)
  581. */
  582. DECLARE_PERF_TIMER(TurretAI)
  583. UpdateSleepTime TurretAI::updateTurretAI()
  584. {
  585. USE_PERF_TIMER(TurretAI)
  586. #if defined(_DEBUG) || defined(_INTERNAL)
  587. DEBUG_ASSERTCRASH(!m_enabled ||
  588. m_turretStateMachine->peekSleepTill() == 0 ||
  589. m_turretStateMachine->peekSleepTill() >= m_sleepUntil, ("Turret Machine is less sleepy than turret"));
  590. #endif
  591. UnsignedInt now = TheGameLogic->getFrame();
  592. if (m_sleepUntil != 0 && now < m_sleepUntil)
  593. {
  594. return UPDATE_SLEEP(m_sleepUntil - now);
  595. }
  596. //DEBUG_LOG(("updateTurretAI frame %d: %08lx\n",TheGameLogic->getFrame(),getOwner()));
  597. UpdateSleepTime subMachineSleep = UPDATE_SLEEP_FOREVER; // assume the best!
  598. // either we don't care about continuous fire stuff, or we care, but time has elapsed
  599. if ((!m_firesWhileTurning) || (m_continuousFireExpirationFrame <= now))
  600. {
  601. m_playRotSound = false;
  602. m_playPitchSound = false;
  603. }
  604. if (m_enabled || m_turretStateMachine->getCurrentStateID() == TURRETAI_RECENTER)
  605. {
  606. m_didFire = false;
  607. // run the behavior state machine BEFORE doing sound check
  608. StateReturnType stRet = m_turretStateMachine->updateStateMachine();
  609. if (m_didFire)
  610. {
  611. // if we fired, enable sweeping for a few frames.
  612. const ENABLE_SWEEP_FRAME_COUNT = 3;
  613. m_enableSweepUntil = now + ENABLE_SWEEP_FRAME_COUNT;
  614. m_continuousFireExpirationFrame = now + ENABLE_SWEEP_FRAME_COUNT;// so the recent firing will not interrupt the moving sound
  615. }
  616. if (m_playRotSound || m_playPitchSound)
  617. startRotOrPitchSound();
  618. else
  619. stopRotOrPitchSound();
  620. if (IS_STATE_SLEEP(stRet))
  621. {
  622. Int frames = GET_STATE_SLEEP_FRAMES(stRet);
  623. if (frames < subMachineSleep)
  624. subMachineSleep = UPDATE_SLEEP(frames);
  625. }
  626. else
  627. {
  628. // it's STATE_CONTINUE, STATE_SUCCESS, or STATE_FAILURE,
  629. // any of which will probably require next frame
  630. subMachineSleep = UPDATE_SLEEP_NONE;
  631. }
  632. } // if enabled or recentering
  633. m_sleepUntil = now + subMachineSleep;
  634. #if defined(_DEBUG) || defined(_INTERNAL)
  635. DEBUG_ASSERTCRASH(!m_enabled ||
  636. m_turretStateMachine->peekSleepTill() == 0 ||
  637. m_turretStateMachine->peekSleepTill() >= m_sleepUntil, ("Turret Machine is less sleepy than turret"));
  638. #endif
  639. return subMachineSleep;
  640. }
  641. //-------------------------------------------------------------------------------------------------
  642. void TurretAI::setTurretEnabled( Bool enabled )
  643. {
  644. if (enabled && !m_enabled)
  645. {
  646. // be sure we wake up!
  647. m_sleepUntil = TheGameLogic->getFrame();
  648. }
  649. m_enabled = enabled;
  650. }
  651. //-------------------------------------------------------------------------------------------------
  652. /**
  653. * Start turret rotation sound
  654. */
  655. void TurretAI::startRotOrPitchSound()
  656. {
  657. if (!m_turretRotOrPitchSound.isCurrentlyPlaying())
  658. {
  659. m_turretRotOrPitchSound.setObjectID(m_owner->getID());
  660. m_turretRotOrPitchSound.setPlayingHandle(TheAudio->addAudioEvent(&m_turretRotOrPitchSound));
  661. }
  662. }
  663. //-------------------------------------------------------------------------------------------------
  664. /**
  665. * Stop turret rotation sound
  666. */
  667. void TurretAI::stopRotOrPitchSound()
  668. {
  669. if (m_turretRotOrPitchSound.isCurrentlyPlaying())
  670. {
  671. TheAudio->removeAudioEvent(m_turretRotOrPitchSound.getPlayingHandle());
  672. }
  673. }
  674. #ifdef INTER_TURRET_DELAY
  675. //----------------------------------------------------------------------------------------------------------
  676. void TurretAI::getOtherTurretWeaponInfo(Int& numSelf, Int& numSelfReloading, Int& numSelfReady, Int& numOther, Int& numOtherReloading, Int& numOtherReady) const
  677. {
  678. numSelf = 0;
  679. numSelfReady = 0;
  680. numSelfReloading = 0;
  681. numOther = 0;
  682. numOtherReady = 0;
  683. numOtherReloading = 0;
  684. for( Int i = 0; i < WEAPONSLOT_COUNT; i++ )
  685. {
  686. // ignore empty slots.
  687. const Weapon* w = getOwner()->getWeaponInWeaponSlot((WeaponSlotType)i);
  688. if (w == NULL)
  689. continue;
  690. // ignore the weapons on this turret.
  691. if (isWeaponSlotOnTurret((WeaponSlotType)i))
  692. {
  693. ++numSelf;
  694. if (w->getStatus() == RELOADING_CLIP)
  695. ++numSelfReloading;
  696. else if (w->getStatus() == READY_TO_FIRE)
  697. ++numSelfReady;
  698. }
  699. else
  700. {
  701. ++numOther;
  702. if (w->getStatus() == RELOADING_CLIP)
  703. ++numOtherReloading;
  704. else if (w->getStatus() == READY_TO_FIRE)
  705. ++numOtherReady;
  706. }
  707. }
  708. }
  709. #endif
  710. //----------------------------------------------------------------------------------------------------------
  711. Bool TurretAI::friend_isAnyWeaponInRangeOf(const Object* o) const
  712. {
  713. // see if we've moved out of range, and if so, nuke the target.
  714. for (Int i = 0; i < WEAPONSLOT_COUNT; i++ )
  715. {
  716. // ignore empty slots.
  717. const Weapon* w = getOwner()->getWeaponInWeaponSlot((WeaponSlotType)i);
  718. if (w == NULL || !isWeaponSlotOnTurret((WeaponSlotType)i))
  719. continue;
  720. if (w->isWithinAttackRange(getOwner(), o)
  721. // srj sez: not sure if we want to do this or not.
  722. // jba sez: no, don't do this. isWithinAttackRange checks terrain los now, and
  723. // some weapons (like tomahawk) can fire beyond los, so this check is problematical here.
  724. // && w->isClearFiringLineOfSightTerrain(getOwner(), o)
  725. )
  726. {
  727. return true;
  728. }
  729. }
  730. return false;
  731. }
  732. //----------------------------------------------------------------------------------------------------------
  733. Bool TurretAI::friend_isSweepEnabled() const
  734. {
  735. if (m_enableSweepUntil != 0 && m_enableSweepUntil > TheGameLogic->getFrame())
  736. return true;
  737. return false;
  738. }
  739. //----------------------------------------------------------------------------------------------------------
  740. UnsignedInt TurretAI::friend_getNextIdleMoodTargetFrame() const
  741. {
  742. const Object* obj = getOwner();
  743. const AIUpdateInterface *ai = obj->getAIUpdateInterface();
  744. // ai can be null during object construction.
  745. return ai ? ai->getNextMoodCheckTime() : TheGameLogic->getFrame();
  746. }
  747. //----------------------------------------------------------------------------------------------------------
  748. void TurretAI::friend_checkForIdleMoodTarget()
  749. {
  750. Object* obj = getOwner();
  751. AIUpdateInterface *ai = obj->getAIUpdateInterface();
  752. // This state is used internally some places, so we don't necessarily want to be looking for targets
  753. // Places that use AI_IDLE internally should set this to false in the constructor. jkmcd
  754. UnsignedInt moodAdjust = ai->getMoodMatrixActionAdjustment(MM_Action_Idle);
  755. if (moodAdjust & MAA_Affect_Range_IgnoreAll)
  756. return;
  757. // If we're supposed to attack based on mood, etc, then we will do so.
  758. Object* enemy = ai->getNextMoodTarget( true, true );
  759. if (enemy)
  760. {
  761. setTurretTargetObject(enemy, FALSE);
  762. obj->chooseBestWeaponForTarget(enemy, PREFER_MOST_DAMAGE, CMD_FROM_AI);
  763. // set this magic flag to indicate that this was an independent attack-of-opportunity,
  764. // and we should check to see if out-of-range, and if so, nuke it
  765. m_targetWasSetByIdleMood = true;
  766. }
  767. }
  768. #ifdef INTER_TURRET_DELAY
  769. //----------------------------------------------------------------------------------------------------------
  770. UnsignedInt TurretAI::friend_getInterTurretDelay()
  771. {
  772. if (m_data->m_interTurretDelay == 0)
  773. return 0;
  774. Int numSelf;
  775. Int numSelfReloading;
  776. Int numSelfReady;
  777. Int numOther;
  778. Int numOtherReloading;
  779. Int numOtherReady;
  780. getOtherTurretWeaponInfo(numSelf, numSelfReloading, numSelfReady, numOther, numOtherReloading, numOtherReady);
  781. AIUpdateInterface* ai = getOwner()->getAIUpdateInterface();
  782. WhichTurretType t = ai->friend_getTurretSync();
  783. if (t == TURRET_INVALID || t == m_whichTurret)
  784. {
  785. if (numSelf == numSelfReady)
  786. {
  787. ai->friend_setTurretSync(m_whichTurret);
  788. // if we just got the flag, use our delay, otherwise go immediately
  789. if (t == TURRET_INVALID)
  790. return m_data->m_interTurretDelay;
  791. else
  792. return 0;
  793. }
  794. else if (numSelf == numSelfReloading)
  795. {
  796. // we're done... relinquish the flag
  797. ai->friend_setTurretSync(TURRET_INVALID);
  798. return WAIT_INDEFINITELY;
  799. }
  800. else
  801. {
  802. // we're not ready, but not reloading either, so retain the flag and wait
  803. return WAIT_INDEFINITELY;
  804. }
  805. }
  806. else
  807. {
  808. // someone else has it, so just wait.
  809. return WAIT_INDEFINITELY;
  810. }
  811. }
  812. #endif
  813. //----------------------------------------------------------------------------------------------------------
  814. //----------------------------------------------------------------------------------------------------------
  815. //----------------------------------------------------------------------------------------------------------
  816. //----------------------------------------------------------------------------------------------------------
  817. StateReturnType TurretAIAimTurretState::onEnter()
  818. {
  819. // don't set the "turret-rotate" bit here; wait and see if we really turn or not.
  820. #ifdef INTER_TURRET_DELAY
  821. m_extraDelay = 0;
  822. #endif
  823. return STATE_CONTINUE;
  824. }
  825. //-------------------------------------------------------------------------------------------------
  826. /**
  827. * Rotate our turret to point at the machine's goal
  828. */
  829. StateReturnType TurretAIAimTurretState::update()
  830. {
  831. //DEBUG_LOG(("TurretAIAimTurretState frame %d: %08lx\n",TheGameLogic->getFrame(),getTurretAI()->getOwner()));
  832. TurretAI* turret = getTurretAI();
  833. Object* obj = turret->getOwner();
  834. AIUpdateInterface* ai = obj->getAIUpdateInterface();
  835. if( !ai )
  836. {
  837. return STATE_FAILURE;
  838. }
  839. Object* enemy;
  840. AIUpdateInterface* enemyAI=NULL;
  841. Coord3D enemyPosition;
  842. Bool preventing = false;
  843. TurretTargetType targetType = turret->friend_getTurretTarget(enemy, enemyPosition);
  844. Object *enemyForDistanceCheckOnly = enemy; // Note: Do not use this anywhere except for the range check.
  845. Bool nothingInRange = false;
  846. switch (targetType)
  847. {
  848. case TARGET_NONE:
  849. {
  850. return STATE_FAILURE;
  851. }
  852. case TARGET_OBJECT:
  853. {
  854. Bool isPrimaryEnemy = (enemy && enemy == ai->getGoalObject());
  855. // if the enemy is gone, or we're out of range, or it changed teams, the attack is over
  856. Bool ableToAttackTarget = obj->isAbleToAttack();
  857. if (ableToAttackTarget)
  858. {
  859. // srj sez: since we have already acquired this target, we should use
  860. // the CONTINUED attack tests, not the new ones.
  861. CanAttackResult result = obj->getAbleToAttackSpecificObject(
  862. turret->isForceAttacking() ? ATTACK_CONTINUED_TARGET_FORCED : ATTACK_CONTINUED_TARGET,
  863. enemy,
  864. ai->getLastCommandSource()
  865. );
  866. ableToAttackTarget = result == ATTACKRESULT_POSSIBLE || result == ATTACKRESULT_POSSIBLE_AFTER_MOVING;
  867. }
  868. nothingInRange = !turret->friend_isAnyWeaponInRangeOf(enemy);
  869. if (enemy == NULL || !ableToAttackTarget ||
  870. (!isPrimaryEnemy && nothingInRange) ||
  871. enemy->getTeam() != turret->friend_getVictimInitialTeam()
  872. )
  873. {
  874. if (turret->friend_getTargetWasSetByIdleMood())
  875. {
  876. turret->setTurretTargetObject(NULL, FALSE);
  877. }
  878. return STATE_FAILURE;
  879. }
  880. // aim turret towards enemy (turret angle is relative to its parent object)
  881. if (enemy->isKindOf(KINDOF_BRIDGE))
  882. {
  883. // Special case - bridges have two attackable points at either end.
  884. TBridgeAttackInfo info;
  885. TheTerrainLogic->getBridgeAttackPoints(enemy, &info);
  886. Real distSqr = ThePartitionManager->getDistanceSquared( obj, &info.attackPoint1, FROM_BOUNDINGSPHERE_3D );
  887. if (distSqr > ThePartitionManager->getDistanceSquared( obj, &info.attackPoint2, FROM_BOUNDINGSPHERE_3D ) )
  888. {
  889. enemyPosition = info.attackPoint2;
  890. }
  891. else
  892. {
  893. enemyPosition = info.attackPoint1;
  894. }
  895. }
  896. else
  897. {
  898. enemyPosition = *enemy->getPosition();
  899. }
  900. enemyAI = enemy ? enemy->getAI() : NULL;
  901. // add ourself as a targeter BEFORE calling isTemporarilyPreventingAimSuccess().
  902. // we do this every time thru, just in case we get into a squabble with our ai
  903. // over whether or not we are a targeter... (srj)
  904. if (enemyAI)
  905. enemyAI->addTargeter(obj->getID(), true);
  906. preventing = enemyAI && enemyAI->isTemporarilyPreventingAimSuccess();
  907. // don't use 'enemy' after this point, just the position. to help
  908. // enforce this, we'll null it out.
  909. enemy = NULL;
  910. break;
  911. }
  912. case TARGET_POSITION:
  913. {
  914. // nothing, just break.
  915. break;
  916. }
  917. }
  918. WeaponSlotType slot;
  919. Weapon *curWeapon = obj->getCurrentWeapon( &slot );
  920. if (!curWeapon)
  921. {
  922. DEBUG_CRASH(("TurretAIAimTurretState::update - curWeapon is NULL.\n"));
  923. return STATE_FAILURE;
  924. }
  925. Real turnSpeedModifier = 1.0f;// Just like how recentering turns you half speed, sweeping can change your turn speed
  926. Real relAngle = ThePartitionManager->getRelativeAngle2D( obj, &enemyPosition );
  927. Real aimAngle = relAngle;
  928. Real sweep = turret->getTurretFireAngleSweepForWeaponSlot( slot );
  929. if (sweep > 0.0f && turret->friend_isSweepEnabled())
  930. {
  931. if (turret->friend_getPositiveSweep())
  932. aimAngle += sweep;
  933. else
  934. aimAngle -= sweep;
  935. turnSpeedModifier = turret->getTurretSweepSpeedModifierForWeaponSlot( slot );
  936. }
  937. const Real REL_THRESH = 0.035f; // about 2 degrees. (getRelativeAngle2D is current only accurate to about 1.25 degrees)
  938. Bool turnAlignedToNemesis = turret->friend_turnTowardsAngle(aimAngle, turnSpeedModifier, REL_THRESH);
  939. // this section we do even if sweep is "disabled", so that we can start firing
  940. // once we get into sweep "range"
  941. if (sweep > 0.0f)
  942. {
  943. if (turnAlignedToNemesis)
  944. turret->friend_setPositiveSweep(!turret->friend_getPositiveSweep());
  945. Real angleDiff = normalizeAngle(relAngle - turret->getTurretAngle());
  946. turnAlignedToNemesis = (fabs(angleDiff) < sweep);
  947. }
  948. Bool pitchAlignedToNemesis = true;
  949. // Now do pitch
  950. if( turret->isAllowsPitch() )
  951. {
  952. Real desiredPitch = 0;
  953. // Some turrets want to fire at a set pitch, and some want to fire straight to the target
  954. if( turret->getFirePitch() > 0 )
  955. {
  956. desiredPitch = turret->getFirePitch();
  957. }
  958. else
  959. {
  960. // Find the pitch to the target, but unlike Turn, we can't go 360 so bind at 0;
  961. Coord3D v;
  962. ThePartitionManager->getVectorTo(obj, &enemyPosition, FROM_CENTER_3D, v);
  963. //GetVectorTo only takes Object as the first, but we want the angle from our Weapon to the
  964. // target, not us to the target. Raise our side to get the line to make sense.
  965. v.z -= obj->getGeometryInfo().getMaxHeightAbovePosition() / 2; // I kinda hate our logic/client split.
  966. //The point to fire from should be intrinsic to the turret, but in reality it is very slow to look it up.
  967. Real actualPitch;
  968. if( v.length() > 0 )
  969. actualPitch = ASin( v.z / v.length() );
  970. else
  971. actualPitch = 0;// Don't point at NAN, just point at 0 if they are right on us
  972. desiredPitch = actualPitch;
  973. if( desiredPitch < turret->getMinPitch() )
  974. {
  975. desiredPitch = turret->getMinPitch();
  976. }
  977. if (turret->getGroundUnitPitch() > 0) {
  978. Bool adjust = false;
  979. if (!enemy) {
  980. adjust = true; // adjust for ground targets.
  981. }
  982. if (enemy && enemy->isKindOf(KINDOF_IMMOBILE)) {
  983. adjust = true;
  984. }
  985. if (enemyAI && enemyAI->isDoingGroundMovement()) {
  986. adjust = true;
  987. }
  988. if (adjust) {
  989. Real range = curWeapon->getAttackRange(obj);
  990. Real dist = v.length();
  991. if (range<1) range = 1; // paranoia. jba.
  992. // As the unit gets closer, reduce the pitch so we don't shoot over him.
  993. Real groundPitch = turret->getGroundUnitPitch() * (dist/range);
  994. desiredPitch = actualPitch+groundPitch;
  995. if (desiredPitch < turret->getMinPitch()) {
  996. desiredPitch = turret->getMinPitch();
  997. }
  998. }
  999. }
  1000. }
  1001. pitchAlignedToNemesis = turret->friend_turnTowardsPitch(desiredPitch, 1.0f);
  1002. }
  1003. // For now, we require that we're within range before we can successfully exit the AIM state,
  1004. // and move into the FIRE state.
  1005. if (turnAlignedToNemesis && pitchAlignedToNemesis &&
  1006. ((enemyForDistanceCheckOnly && curWeapon->isWithinAttackRange(obj, enemyForDistanceCheckOnly)) ||
  1007. (!enemyForDistanceCheckOnly && curWeapon->isWithinAttackRange(obj, &enemyPosition))))
  1008. {
  1009. #ifdef INTER_TURRET_DELAY
  1010. if (m_extraDelay > 0)
  1011. {
  1012. --m_extraDelay;
  1013. }
  1014. else
  1015. {
  1016. m_extraDelay = turret->friend_getInterTurretDelay();
  1017. if (m_extraDelay == WAIT_INDEFINITELY)
  1018. {
  1019. m_extraDelay = 0;
  1020. return STATE_CONTINUE;
  1021. }
  1022. }
  1023. if (m_extraDelay != 0)
  1024. {
  1025. return STATE_CONTINUE;
  1026. }
  1027. #endif
  1028. if (preventing || nothingInRange)
  1029. {
  1030. return STATE_CONTINUE;
  1031. }
  1032. return STATE_SUCCESS;
  1033. }
  1034. // stay in aiming state until directed to do something else
  1035. return STATE_CONTINUE;
  1036. }
  1037. //-------------------------------------------------------------------------------------------------
  1038. void TurretAIAimTurretState::onExit( StateExitType status )
  1039. {
  1040. }
  1041. //-------------------------------------------------------------------------------------------------
  1042. //-------------------------------------------------------------------------------------------------
  1043. //-------------------------------------------------------------------------------------------------
  1044. //----------------------------------------------------------------------------------------------------------
  1045. StateReturnType TurretAIRecenterTurretState::onEnter()
  1046. {
  1047. return STATE_CONTINUE;
  1048. }
  1049. //-------------------------------------------------------------------------------------------------
  1050. /**
  1051. * Rotate the owner's turret to its home orientation.
  1052. */
  1053. StateReturnType TurretAIRecenterTurretState::update()
  1054. {
  1055. //DEBUG_LOG(("TurretAIRecenterTurretState frame %d: %08lx\n",TheGameLogic->getFrame(),getTurretAI()->getOwner()));
  1056. TurretAI* turret = getTurretAI();
  1057. Bool angleAligned = turret->friend_turnTowardsAngle(turret->getNaturalTurretAngle(), 0.5f, 0.0f);
  1058. Bool pitchAligned = turret->friend_turnTowardsPitch(turret->getNaturalTurretPitch(), 0.5f);
  1059. if( angleAligned && pitchAligned )
  1060. return STATE_SUCCESS;
  1061. return STATE_CONTINUE;
  1062. }
  1063. //-------------------------------------------------------------------------------------------------
  1064. /**
  1065. * Stop rotation sound
  1066. */
  1067. void TurretAIRecenterTurretState::onExit( StateExitType status )
  1068. {
  1069. }
  1070. //----------------------------------------------------------------------------------------------------------
  1071. //----------------------------------------------------------------------------------------------------------
  1072. //----------------------------------------------------------------------------------------------------------
  1073. // ------------------------------------------------------------------------------------------------
  1074. /** CRC */
  1075. // ------------------------------------------------------------------------------------------------
  1076. void TurretAIIdleState::crc( Xfer *xfer )
  1077. {
  1078. } // end crc
  1079. // ------------------------------------------------------------------------------------------------
  1080. /** Xfer Method */
  1081. // ------------------------------------------------------------------------------------------------
  1082. void TurretAIIdleState::xfer( Xfer *xfer )
  1083. {
  1084. // version
  1085. XferVersion currentVersion = 1;
  1086. XferVersion version = currentVersion;
  1087. xfer->xferVersion( &version, currentVersion );
  1088. xfer->xferUnsignedInt(&m_nextIdleScan);
  1089. } // end xfer
  1090. // ------------------------------------------------------------------------------------------------
  1091. /** Load post process */
  1092. // ------------------------------------------------------------------------------------------------
  1093. void TurretAIIdleState::loadPostProcess( void )
  1094. {
  1095. } // end loadPostProcess
  1096. //----------------------------------------------------------------------------------------------------------
  1097. void TurretAIIdleState::resetIdleScan()
  1098. {
  1099. UnsignedInt now = TheGameLogic->getFrame();
  1100. UnsignedInt delay = GameLogicRandomValue(getTurretAI()->getMinIdleScanInterval(), getTurretAI()->getMaxIdleScanInterval());
  1101. m_nextIdleScan = now + delay;
  1102. }
  1103. //----------------------------------------------------------------------------------------------------------
  1104. StateReturnType TurretAIIdleState::onEnter()
  1105. {
  1106. AIUpdateInterface *ai = getMachineOwner()->getAIUpdateInterface();
  1107. if (ai)
  1108. {
  1109. ai->resetNextMoodCheckTime();
  1110. if (ai->friend_getTurretSync() == getTurretAI()->friend_getWhichTurret())
  1111. ai->friend_setTurretSync(TURRET_INVALID);
  1112. } // ai doesn't exist if the object was just created this frame.
  1113. resetIdleScan();
  1114. TurretAI* turret = getTurretAI();
  1115. return frameToSleepTime(turret->friend_getNextIdleMoodTargetFrame(), m_nextIdleScan);
  1116. }
  1117. //----------------------------------------------------------------------------------------------------------
  1118. StateReturnType TurretAIIdleState::update()
  1119. {
  1120. //DEBUG_LOG(("TurretAIIdleState frame %d: %08lx\n",TheGameLogic->getFrame(),getTurretAI()->getOwner()));
  1121. UnsignedInt now = TheGameLogic->getFrame();
  1122. if (now >= m_nextIdleScan)
  1123. {
  1124. // this is redundant, since we're exiting the state, and will reset
  1125. // it again in onEnter next time (srj)
  1126. // resetIdleScan();
  1127. return STATE_FAILURE;
  1128. }
  1129. TurretAI* turret = getTurretAI();
  1130. turret->friend_checkForIdleMoodTarget();
  1131. return frameToSleepTime(turret->friend_getNextIdleMoodTargetFrame(), m_nextIdleScan);
  1132. }
  1133. //----------------------------------------------------------------------------------------------------------
  1134. //----------------------------------------------------------------------------------------------------------
  1135. //----------------------------------------------------------------------------------------------------------
  1136. // ------------------------------------------------------------------------------------------------
  1137. /** CRC */
  1138. // ------------------------------------------------------------------------------------------------
  1139. void TurretAIIdleScanState::crc( Xfer *xfer )
  1140. {
  1141. } // end crc
  1142. // ------------------------------------------------------------------------------------------------
  1143. /** Xfer Method */
  1144. // ------------------------------------------------------------------------------------------------
  1145. void TurretAIIdleScanState::xfer( Xfer *xfer )
  1146. {
  1147. // version
  1148. XferVersion currentVersion = 1;
  1149. XferVersion version = currentVersion;
  1150. xfer->xferVersion( &version, currentVersion );
  1151. xfer->xferReal(&m_desiredAngle);
  1152. } // end xfer
  1153. // ------------------------------------------------------------------------------------------------
  1154. /** Load post process */
  1155. // ------------------------------------------------------------------------------------------------
  1156. void TurretAIIdleScanState::loadPostProcess( void )
  1157. {
  1158. } // end loadPostProcess
  1159. //----------------------------------------------------------------------------------------------------------
  1160. StateReturnType TurretAIIdleScanState::onEnter()
  1161. {
  1162. Real minA = getTurretAI()->getMinIdleScanAngle();
  1163. Real maxA = getTurretAI()->getMaxIdleScanAngle();
  1164. if (minA == 0.0f && maxA == 0.0f)
  1165. return STATE_SUCCESS;
  1166. m_desiredAngle = minA + GameLogicRandomValueReal(0, maxA - minA);
  1167. if (GameLogicRandomValue( 0, 1 ) == 0)
  1168. m_desiredAngle = -m_desiredAngle;
  1169. return STATE_CONTINUE;
  1170. }
  1171. //-------------------------------------------------------------------------------------------------
  1172. /**
  1173. * Rotate the owner's turret to its scan orientation.
  1174. */
  1175. StateReturnType TurretAIIdleScanState::update()
  1176. {
  1177. //DEBUG_LOG(("TurretAIIdleScanState frame %d: %08lx\n",TheGameLogic->getFrame(),getTurretAI()->getOwner()));
  1178. Bool angleAligned = getTurretAI()->friend_turnTowardsAngle(getTurretAI()->getNaturalTurretAngle() + m_desiredAngle, 0.5f, 0.0f);
  1179. Bool pitchAligned = getTurretAI()->friend_turnTowardsPitch(getTurretAI()->getNaturalTurretPitch(), 0.5f);
  1180. if( angleAligned && pitchAligned )
  1181. return STATE_SUCCESS;
  1182. return STATE_CONTINUE;
  1183. }
  1184. //-------------------------------------------------------------------------------------------------
  1185. /**
  1186. * Stop rotation sound
  1187. */
  1188. void TurretAIIdleScanState::onExit( StateExitType status )
  1189. {
  1190. }
  1191. //----------------------------------------------------------------------------------------------------------
  1192. //----------------------------------------------------------------------------------------------------------
  1193. //----------------------------------------------------------------------------------------------------------
  1194. // ------------------------------------------------------------------------------------------------
  1195. /** CRC */
  1196. // ------------------------------------------------------------------------------------------------
  1197. void TurretAIHoldTurretState::crc( Xfer *xfer )
  1198. {
  1199. } // end crc
  1200. // ------------------------------------------------------------------------------------------------
  1201. /** Xfer Method */
  1202. // ------------------------------------------------------------------------------------------------
  1203. void TurretAIHoldTurretState::xfer( Xfer *xfer )
  1204. {
  1205. // version
  1206. XferVersion currentVersion = 1;
  1207. XferVersion version = currentVersion;
  1208. xfer->xferVersion( &version, currentVersion );
  1209. xfer->xferUnsignedInt(&m_timestamp);
  1210. } // end xfer
  1211. // ------------------------------------------------------------------------------------------------
  1212. /** Load post process */
  1213. // ------------------------------------------------------------------------------------------------
  1214. void TurretAIHoldTurretState::loadPostProcess( void )
  1215. {
  1216. } // end loadPostProcess
  1217. //----------------------------------------------------------------------------------------------------------
  1218. StateReturnType TurretAIHoldTurretState::onEnter()
  1219. {
  1220. m_timestamp = TheGameLogic->getFrame() + getTurretAI()->getRecenterTime();
  1221. TurretAI* turret = getTurretAI();
  1222. return frameToSleepTime(turret->friend_getNextIdleMoodTargetFrame(), m_timestamp);
  1223. }
  1224. //-------------------------------------------------------------------------------------------------
  1225. void TurretAIHoldTurretState::onExit( StateExitType status )
  1226. {
  1227. }
  1228. //----------------------------------------------------------------------------------------------------------
  1229. /**
  1230. * Hold the turret's position for a short time before returning to center.
  1231. */
  1232. StateReturnType TurretAIHoldTurretState::update()
  1233. {
  1234. //DEBUG_LOG(("TurretAIHoldTurretState frame %d: %08lx\n",TheGameLogic->getFrame(),getTurretAI()->getOwner()));
  1235. if (TheGameLogic->getFrame() >= m_timestamp)
  1236. return STATE_SUCCESS;
  1237. TurretAI* turret = getTurretAI();
  1238. turret->friend_checkForIdleMoodTarget();
  1239. return frameToSleepTime(turret->friend_getNextIdleMoodTargetFrame(), m_timestamp);
  1240. }