TurretAI.cpp 49 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // 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. // If we turrets are linked, ai wants us to fire together, regardless of slot
  403. if( getOwner()->getAI()->areTurretsLinked() )
  404. return TRUE;
  405. return isWeaponSlotOnTurret(wslot);
  406. }
  407. //----------------------------------------------------------------------------------------------------------
  408. Real TurretAI::getTurretFireAngleSweepForWeaponSlot( WeaponSlotType slot ) const
  409. {
  410. return m_data->m_turretFireAngleSweep[slot];
  411. }
  412. //----------------------------------------------------------------------------------------------------------
  413. Real TurretAI::getTurretSweepSpeedModifierForWeaponSlot( WeaponSlotType slot ) const
  414. {
  415. return m_data->m_turretSweepSpeedModifier[slot];
  416. }
  417. //----------------------------------------------------------------------------------------------------------
  418. void TurretAI::notifyFired()
  419. {
  420. m_didFire = true;
  421. }
  422. //----------------------------------------------------------------------------------------------------------
  423. void TurretAI::notifyNewVictimChosen(Object* victim)
  424. {
  425. setTurretTargetObject(victim, FALSE);
  426. }
  427. //----------------------------------------------------------------------------------------------------------
  428. Bool TurretAI::isTryingToAimAtTarget(const Object* victim) const
  429. {
  430. StateID sid = m_turretStateMachine->getCurrentStateID();
  431. Object* obj;
  432. Coord3D pos;
  433. return (sid == TURRETAI_AIM && friend_getTurretTarget(obj, pos) == TARGET_OBJECT && obj == victim);
  434. }
  435. //----------------------------------------------------------------------------------------------------------
  436. Bool TurretAI::isOwnersCurWeaponOnTurret() const
  437. {
  438. WeaponSlotType wslot;
  439. Weapon* w = m_owner->getCurrentWeapon(&wslot);
  440. return w != NULL && isWeaponSlotOnTurret(wslot);
  441. }
  442. //----------------------------------------------------------------------------------------------------------
  443. Bool TurretAI::isWeaponSlotOnTurret(WeaponSlotType wslot) const
  444. {
  445. return (m_data->m_turretWeaponSlots & (1 << wslot)) != 0;
  446. }
  447. //----------------------------------------------------------------------------------------------------------
  448. TurretTargetType TurretAI::friend_getTurretTarget( Object*& obj, Coord3D& pos, Bool clearDeadTargets ) const
  449. {
  450. obj = NULL;
  451. pos.zero();
  452. if (m_target == TARGET_OBJECT)
  453. {
  454. obj = m_turretStateMachine->getGoalObject();
  455. // clear it explicitly if null -- could be deceased but holding the
  456. // old (bogus) objectid internally
  457. if( clearDeadTargets )
  458. {
  459. if (obj == NULL || obj->isEffectivelyDead())
  460. {
  461. m_turretStateMachine->setGoalObject(NULL);
  462. m_target = TARGET_NONE;
  463. m_targetWasSetByIdleMood = false;
  464. }
  465. }
  466. }
  467. else if (m_target == TARGET_POSITION)
  468. {
  469. obj = NULL;
  470. pos = *m_turretStateMachine->getGoalPosition();
  471. }
  472. return m_target;
  473. }
  474. //----------------------------------------------------------------------------------------------------------
  475. void TurretAI::removeSelfAsTargeter()
  476. {
  477. // be paranoid, in case we are called from dtors, etc.
  478. if (m_target == TARGET_OBJECT && m_turretStateMachine != NULL)
  479. {
  480. Object* self = m_owner;
  481. Object* target = m_turretStateMachine->getGoalObject();
  482. if (self != NULL && target != NULL)
  483. {
  484. AIUpdateInterface* targetAI = target->getAI();
  485. if (targetAI)
  486. {
  487. targetAI->addTargeter(self->getID(), false);
  488. }
  489. }
  490. }
  491. }
  492. //----------------------------------------------------------------------------------------------------------
  493. void TurretAI::setTurretTargetObject( Object *victim, Bool forceAttacking )
  494. {
  495. if( !victim || victim->isEffectivelyDead() || !isOwnersCurWeaponOnTurret() )
  496. {
  497. if( !getOwner()->getAI()->areTurretsLinked() )
  498. {
  499. victim = NULL;
  500. }
  501. }
  502. if (victim == NULL)
  503. {
  504. // if nuking the victim, remove self as targeter before doing anything else.
  505. // (note that we never ADD self as targeter here; that is done in the aim state)
  506. removeSelfAsTargeter();
  507. }
  508. m_turretStateMachine->setGoalObject( victim ); // could be null! this is OK!
  509. m_target = victim ? TARGET_OBJECT : TARGET_NONE;
  510. m_targetWasSetByIdleMood = false;
  511. m_isForceAttacking = forceAttacking;
  512. StateID sid = m_turretStateMachine->getCurrentStateID();
  513. if (victim != NULL)
  514. {
  515. // if we're already in the aim state, don't call setState, since
  516. // it would go thru the exit/enter stuff, which we don't really want
  517. // to do...
  518. if (sid != TURRETAI_AIM && sid != TURRETAI_FIRE)
  519. m_turretStateMachine->setState( TURRETAI_AIM );
  520. m_victimInitialTeam = victim->getTeam();
  521. }
  522. else
  523. {
  524. // only change states if we are aiming.
  525. if (sid == TURRETAI_AIM || sid == TURRETAI_FIRE)
  526. m_turretStateMachine->setState(TURRETAI_HOLD);
  527. m_victimInitialTeam = NULL;
  528. }
  529. }
  530. //----------------------------------------------------------------------------------------------------------
  531. void TurretAI::setTurretTargetPosition( const Coord3D* pos )
  532. {
  533. if (!pos || !isOwnersCurWeaponOnTurret())
  534. {
  535. if( !getOwner()->getAI()->areTurretsLinked() )
  536. {
  537. pos = NULL;
  538. }
  539. }
  540. // remove self as targeter before doing anything else.
  541. // (note that we never ADD self as targeter here; that is done in the aim state)
  542. removeSelfAsTargeter();
  543. m_turretStateMachine->setGoalObject( NULL );
  544. if (pos)
  545. m_turretStateMachine->setGoalPosition( pos );
  546. m_target = pos ? TARGET_POSITION : TARGET_NONE;
  547. m_targetWasSetByIdleMood = false;
  548. StateID sid = m_turretStateMachine->getCurrentStateID();
  549. if (pos != NULL)
  550. {
  551. // if we're already in the aim state, don't call setState, since
  552. // it would go thru the exit/enter stuff, which we don't really want
  553. // to do...
  554. if (sid != TURRETAI_AIM && sid != TURRETAI_FIRE)
  555. m_turretStateMachine->setState( TURRETAI_AIM );
  556. m_victimInitialTeam = NULL;
  557. }
  558. else
  559. {
  560. // only change states if we are aiming.
  561. if (sid == TURRETAI_AIM || sid == TURRETAI_FIRE)
  562. m_turretStateMachine->setState(TURRETAI_HOLD);
  563. m_victimInitialTeam = NULL;
  564. }
  565. }
  566. //----------------------------------------------------------------------------------------------------------
  567. void TurretAI::recenterTurret()
  568. {
  569. m_turretStateMachine->setState( TURRETAI_RECENTER );
  570. }
  571. //----------------------------------------------------------------------------------------------------------
  572. Bool TurretAI::isTurretInNaturalPosition() const
  573. {
  574. if( this->getOwner()->testStatus( OBJECT_STATUS_UNDER_CONSTRUCTION))
  575. return true;//ML so that under-construction base-defenses do not re-center while under construction
  576. if( getNaturalTurretAngle() == getTurretAngle() &&
  577. getNaturalTurretPitch() == getTurretPitch() )
  578. {
  579. return true;
  580. }
  581. return false;
  582. }
  583. //----------------------------------------------------------------------------------------------------------
  584. void TurretAI::friend_notifyStateMachineChanged()
  585. {
  586. m_sleepUntil = TheGameLogic->getFrame();
  587. }
  588. //----------------------------------------------------------------------------------------------------------
  589. /*
  590. Note that TurretAI isn't a behavior at all, so doesn't really "sleep"; however, it does return
  591. this information so that its AIUpdate owner can decide how long it (the AIUpdate) should sleep.
  592. So it behooves you to maximize "sleep" potential here! (srj)
  593. */
  594. DECLARE_PERF_TIMER(TurretAI)
  595. UpdateSleepTime TurretAI::updateTurretAI()
  596. {
  597. USE_PERF_TIMER(TurretAI)
  598. #if defined(_DEBUG) || defined(_INTERNAL)
  599. DEBUG_ASSERTCRASH(!m_enabled ||
  600. m_turretStateMachine->peekSleepTill() == 0 ||
  601. m_turretStateMachine->peekSleepTill() >= m_sleepUntil, ("Turret Machine is less sleepy than turret"));
  602. #endif
  603. UnsignedInt now = TheGameLogic->getFrame();
  604. if (m_sleepUntil != 0 && now < m_sleepUntil)
  605. {
  606. return UPDATE_SLEEP(m_sleepUntil - now);
  607. }
  608. //DEBUG_LOG(("updateTurretAI frame %d: %08lx\n",TheGameLogic->getFrame(),getOwner()));
  609. UpdateSleepTime subMachineSleep = UPDATE_SLEEP_FOREVER; // assume the best!
  610. // either we don't care about continuous fire stuff, or we care, but time has elapsed
  611. if ((!m_firesWhileTurning) || (m_continuousFireExpirationFrame <= now))
  612. {
  613. m_playRotSound = false;
  614. m_playPitchSound = false;
  615. }
  616. if (m_enabled || m_turretStateMachine->getCurrentStateID() == TURRETAI_RECENTER)
  617. {
  618. m_didFire = false;
  619. // run the behavior state machine BEFORE doing sound check
  620. StateReturnType stRet = m_turretStateMachine->updateStateMachine();
  621. if (m_didFire)
  622. {
  623. // if we fired, enable sweeping for a few frames.
  624. const ENABLE_SWEEP_FRAME_COUNT = 3;
  625. m_enableSweepUntil = now + ENABLE_SWEEP_FRAME_COUNT;
  626. m_continuousFireExpirationFrame = now + ENABLE_SWEEP_FRAME_COUNT;// so the recent firing will not interrupt the moving sound
  627. }
  628. if (m_playRotSound || m_playPitchSound)
  629. startRotOrPitchSound();
  630. else
  631. stopRotOrPitchSound();
  632. if (IS_STATE_SLEEP(stRet))
  633. {
  634. Int frames = GET_STATE_SLEEP_FRAMES(stRet);
  635. if (frames < subMachineSleep)
  636. subMachineSleep = UPDATE_SLEEP(frames);
  637. }
  638. else
  639. {
  640. // it's STATE_CONTINUE, STATE_SUCCESS, or STATE_FAILURE,
  641. // any of which will probably require next frame
  642. subMachineSleep = UPDATE_SLEEP_NONE;
  643. }
  644. } // if enabled or recentering
  645. m_sleepUntil = now + subMachineSleep;
  646. #if defined(_DEBUG) || defined(_INTERNAL)
  647. DEBUG_ASSERTCRASH(!m_enabled ||
  648. m_turretStateMachine->peekSleepTill() == 0 ||
  649. m_turretStateMachine->peekSleepTill() >= m_sleepUntil, ("Turret Machine is less sleepy than turret"));
  650. #endif
  651. return subMachineSleep;
  652. }
  653. //-------------------------------------------------------------------------------------------------
  654. void TurretAI::setTurretEnabled( Bool enabled )
  655. {
  656. if (enabled && !m_enabled)
  657. {
  658. // be sure we wake up!
  659. m_sleepUntil = TheGameLogic->getFrame();
  660. }
  661. m_enabled = enabled;
  662. }
  663. //-------------------------------------------------------------------------------------------------
  664. /**
  665. * Start turret rotation sound
  666. */
  667. void TurretAI::startRotOrPitchSound()
  668. {
  669. if (!m_turretRotOrPitchSound.isCurrentlyPlaying())
  670. {
  671. m_turretRotOrPitchSound.setObjectID(m_owner->getID());
  672. m_turretRotOrPitchSound.setPlayingHandle(TheAudio->addAudioEvent(&m_turretRotOrPitchSound));
  673. }
  674. }
  675. //-------------------------------------------------------------------------------------------------
  676. /**
  677. * Stop turret rotation sound
  678. */
  679. void TurretAI::stopRotOrPitchSound()
  680. {
  681. if (m_turretRotOrPitchSound.isCurrentlyPlaying())
  682. {
  683. TheAudio->removeAudioEvent(m_turretRotOrPitchSound.getPlayingHandle());
  684. }
  685. }
  686. #ifdef INTER_TURRET_DELAY
  687. //----------------------------------------------------------------------------------------------------------
  688. void TurretAI::getOtherTurretWeaponInfo(Int& numSelf, Int& numSelfReloading, Int& numSelfReady, Int& numOther, Int& numOtherReloading, Int& numOtherReady) const
  689. {
  690. numSelf = 0;
  691. numSelfReady = 0;
  692. numSelfReloading = 0;
  693. numOther = 0;
  694. numOtherReady = 0;
  695. numOtherReloading = 0;
  696. for( Int i = 0; i < WEAPONSLOT_COUNT; i++ )
  697. {
  698. // ignore empty slots.
  699. const Weapon* w = getOwner()->getWeaponInWeaponSlot((WeaponSlotType)i);
  700. if (w == NULL)
  701. continue;
  702. // ignore the weapons on this turret.
  703. if (isWeaponSlotOnTurret((WeaponSlotType)i))
  704. {
  705. ++numSelf;
  706. if (w->getStatus() == RELOADING_CLIP)
  707. ++numSelfReloading;
  708. else if (w->getStatus() == READY_TO_FIRE)
  709. ++numSelfReady;
  710. }
  711. else
  712. {
  713. ++numOther;
  714. if (w->getStatus() == RELOADING_CLIP)
  715. ++numOtherReloading;
  716. else if (w->getStatus() == READY_TO_FIRE)
  717. ++numOtherReady;
  718. }
  719. }
  720. }
  721. #endif
  722. //----------------------------------------------------------------------------------------------------------
  723. Bool TurretAI::friend_isAnyWeaponInRangeOf(const Object* o) const
  724. {
  725. // see if we've moved out of range, and if so, nuke the target.
  726. for (Int i = 0; i < WEAPONSLOT_COUNT; i++ )
  727. {
  728. // ignore empty slots.
  729. const Weapon* w = getOwner()->getWeaponInWeaponSlot((WeaponSlotType)i);
  730. if (w == NULL || !isWeaponSlotOnTurret((WeaponSlotType)i))
  731. continue;
  732. if (w->isWithinAttackRange(getOwner(), o)
  733. // srj sez: not sure if we want to do this or not.
  734. // jba sez: no, don't do this. isWithinAttackRange checks terrain los now, and
  735. // some weapons (like tomahawk) can fire beyond los, so this check is problematical here.
  736. // && w->isClearFiringLineOfSightTerrain(getOwner(), o)
  737. )
  738. {
  739. return true;
  740. }
  741. }
  742. return false;
  743. }
  744. //----------------------------------------------------------------------------------------------------------
  745. Bool TurretAI::friend_isSweepEnabled() const
  746. {
  747. if (m_enableSweepUntil != 0 && m_enableSweepUntil > TheGameLogic->getFrame())
  748. return true;
  749. return false;
  750. }
  751. //----------------------------------------------------------------------------------------------------------
  752. UnsignedInt TurretAI::friend_getNextIdleMoodTargetFrame() const
  753. {
  754. const Object* obj = getOwner();
  755. const AIUpdateInterface *ai = obj->getAIUpdateInterface();
  756. // ai can be null during object construction.
  757. return ai ? ai->getNextMoodCheckTime() : TheGameLogic->getFrame();
  758. }
  759. //----------------------------------------------------------------------------------------------------------
  760. void TurretAI::friend_checkForIdleMoodTarget()
  761. {
  762. Object* obj = getOwner();
  763. AIUpdateInterface *ai = obj->getAIUpdateInterface();
  764. // This state is used internally some places, so we don't necessarily want to be looking for targets
  765. // Places that use AI_IDLE internally should set this to false in the constructor. jkmcd
  766. UnsignedInt moodAdjust = ai->getMoodMatrixActionAdjustment(MM_Action_Idle);
  767. if (moodAdjust & MAA_Affect_Range_IgnoreAll)
  768. return;
  769. // If we're supposed to attack based on mood, etc, then we will do so.
  770. Object* enemy = ai->getNextMoodTarget( true, true );
  771. if (enemy)
  772. {
  773. setTurretTargetObject(enemy, FALSE);
  774. obj->chooseBestWeaponForTarget(enemy, PREFER_MOST_DAMAGE, CMD_FROM_AI);
  775. // set this magic flag to indicate that this was an independent attack-of-opportunity,
  776. // and we should check to see if out-of-range, and if so, nuke it
  777. m_targetWasSetByIdleMood = true;
  778. }
  779. }
  780. #ifdef INTER_TURRET_DELAY
  781. //----------------------------------------------------------------------------------------------------------
  782. UnsignedInt TurretAI::friend_getInterTurretDelay()
  783. {
  784. if (m_data->m_interTurretDelay == 0)
  785. return 0;
  786. Int numSelf;
  787. Int numSelfReloading;
  788. Int numSelfReady;
  789. Int numOther;
  790. Int numOtherReloading;
  791. Int numOtherReady;
  792. getOtherTurretWeaponInfo(numSelf, numSelfReloading, numSelfReady, numOther, numOtherReloading, numOtherReady);
  793. AIUpdateInterface* ai = getOwner()->getAIUpdateInterface();
  794. WhichTurretType t = ai->friend_getTurretSync();
  795. if (t == TURRET_INVALID || t == m_whichTurret)
  796. {
  797. if (numSelf == numSelfReady)
  798. {
  799. ai->friend_setTurretSync(m_whichTurret);
  800. // if we just got the flag, use our delay, otherwise go immediately
  801. if (t == TURRET_INVALID)
  802. return m_data->m_interTurretDelay;
  803. else
  804. return 0;
  805. }
  806. else if (numSelf == numSelfReloading)
  807. {
  808. // we're done... relinquish the flag
  809. ai->friend_setTurretSync(TURRET_INVALID);
  810. return WAIT_INDEFINITELY;
  811. }
  812. else
  813. {
  814. // we're not ready, but not reloading either, so retain the flag and wait
  815. return WAIT_INDEFINITELY;
  816. }
  817. }
  818. else
  819. {
  820. // someone else has it, so just wait.
  821. return WAIT_INDEFINITELY;
  822. }
  823. }
  824. #endif
  825. //----------------------------------------------------------------------------------------------------------
  826. //----------------------------------------------------------------------------------------------------------
  827. //----------------------------------------------------------------------------------------------------------
  828. //----------------------------------------------------------------------------------------------------------
  829. StateReturnType TurretAIAimTurretState::onEnter()
  830. {
  831. // don't set the "turret-rotate" bit here; wait and see if we really turn or not.
  832. #ifdef INTER_TURRET_DELAY
  833. m_extraDelay = 0;
  834. #endif
  835. return STATE_CONTINUE;
  836. }
  837. //-------------------------------------------------------------------------------------------------
  838. /**
  839. * Rotate our turret to point at the machine's goal
  840. */
  841. StateReturnType TurretAIAimTurretState::update()
  842. {
  843. //DEBUG_LOG(("TurretAIAimTurretState frame %d: %08lx\n",TheGameLogic->getFrame(),getTurretAI()->getOwner()));
  844. TurretAI* turret = getTurretAI();
  845. Object* obj = turret->getOwner();
  846. AIUpdateInterface* ai = obj->getAIUpdateInterface();
  847. if( !ai )
  848. {
  849. return STATE_FAILURE;
  850. }
  851. Object* enemy;
  852. AIUpdateInterface* enemyAI=NULL;
  853. Coord3D enemyPosition;
  854. Bool preventing = false;
  855. TurretTargetType targetType = turret->friend_getTurretTarget(enemy, enemyPosition);
  856. Object *enemyForDistanceCheckOnly = enemy; // Note: Do not use this anywhere except for the range check.
  857. Bool nothingInRange = false;
  858. switch (targetType)
  859. {
  860. case TARGET_NONE:
  861. {
  862. return STATE_FAILURE;
  863. }
  864. case TARGET_OBJECT:
  865. {
  866. Bool isPrimaryEnemy = (enemy && enemy == ai->getGoalObject());
  867. // if the enemy is gone, or we're out of range, or it changed teams, the attack is over
  868. Bool ableToAttackTarget = obj->isAbleToAttack();
  869. if (ableToAttackTarget)
  870. {
  871. // srj sez: since we have already acquired this target, we should use
  872. // the CONTINUED attack tests, not the new ones.
  873. CanAttackResult result = obj->getAbleToAttackSpecificObject(
  874. turret->isForceAttacking() ? ATTACK_CONTINUED_TARGET_FORCED : ATTACK_CONTINUED_TARGET,
  875. enemy,
  876. ai->getLastCommandSource()
  877. );
  878. ableToAttackTarget = result == ATTACKRESULT_POSSIBLE || result == ATTACKRESULT_POSSIBLE_AFTER_MOVING;
  879. }
  880. nothingInRange = !turret->friend_isAnyWeaponInRangeOf(enemy);
  881. if (enemy == NULL || !ableToAttackTarget ||
  882. (!isPrimaryEnemy && nothingInRange) ||
  883. enemy->getTeam() != turret->friend_getVictimInitialTeam()
  884. )
  885. {
  886. if (turret->friend_getTargetWasSetByIdleMood())
  887. {
  888. turret->setTurretTargetObject(NULL, FALSE);
  889. }
  890. return STATE_FAILURE;
  891. }
  892. // aim turret towards enemy (turret angle is relative to its parent object)
  893. if (enemy->isKindOf(KINDOF_BRIDGE))
  894. {
  895. // Special case - bridges have two attackable points at either end.
  896. TBridgeAttackInfo info;
  897. TheTerrainLogic->getBridgeAttackPoints(enemy, &info);
  898. Real distSqr = ThePartitionManager->getDistanceSquared( obj, &info.attackPoint1, FROM_BOUNDINGSPHERE_3D );
  899. if (distSqr > ThePartitionManager->getDistanceSquared( obj, &info.attackPoint2, FROM_BOUNDINGSPHERE_3D ) )
  900. {
  901. enemyPosition = info.attackPoint2;
  902. }
  903. else
  904. {
  905. enemyPosition = info.attackPoint1;
  906. }
  907. }
  908. else
  909. {
  910. enemyPosition = *enemy->getPosition();
  911. }
  912. enemyAI = enemy ? enemy->getAI() : NULL;
  913. // add ourself as a targeter BEFORE calling isTemporarilyPreventingAimSuccess().
  914. // we do this every time thru, just in case we get into a squabble with our ai
  915. // over whether or not we are a targeter... (srj)
  916. if (enemyAI)
  917. enemyAI->addTargeter(obj->getID(), true);
  918. preventing = enemyAI && enemyAI->isTemporarilyPreventingAimSuccess();
  919. // don't use 'enemy' after this point, just the position. to help
  920. // enforce this, we'll null it out.
  921. enemy = NULL;
  922. break;
  923. }
  924. case TARGET_POSITION:
  925. {
  926. // nothing, just break.
  927. break;
  928. }
  929. }
  930. WeaponSlotType slot;
  931. Weapon *curWeapon = obj->getCurrentWeapon( &slot );
  932. if (!curWeapon)
  933. {
  934. DEBUG_CRASH(("TurretAIAimTurretState::update - curWeapon is NULL.\n"));
  935. return STATE_FAILURE;
  936. }
  937. Real turnSpeedModifier = 1.0f;// Just like how recentering turns you half speed, sweeping can change your turn speed
  938. Real relAngle = ThePartitionManager->getRelativeAngle2D( obj, &enemyPosition );
  939. Real aimAngle = relAngle;
  940. Real sweep = turret->getTurretFireAngleSweepForWeaponSlot( slot );
  941. if (sweep > 0.0f && turret->friend_isSweepEnabled())
  942. {
  943. if (turret->friend_getPositiveSweep())
  944. aimAngle += sweep;
  945. else
  946. aimAngle -= sweep;
  947. turnSpeedModifier = turret->getTurretSweepSpeedModifierForWeaponSlot( slot );
  948. }
  949. const Real REL_THRESH = 0.035f; // about 2 degrees. (getRelativeAngle2D is current only accurate to about 1.25 degrees)
  950. Bool turnAlignedToNemesis = turret->friend_turnTowardsAngle(aimAngle, turnSpeedModifier, REL_THRESH);
  951. // this section we do even if sweep is "disabled", so that we can start firing
  952. // once we get into sweep "range"
  953. if (sweep > 0.0f)
  954. {
  955. if (turnAlignedToNemesis)
  956. turret->friend_setPositiveSweep(!turret->friend_getPositiveSweep());
  957. Real angleDiff = normalizeAngle(relAngle - turret->getTurretAngle());
  958. turnAlignedToNemesis = (fabs(angleDiff) < sweep);
  959. }
  960. Bool pitchAlignedToNemesis = true;
  961. // Now do pitch
  962. if( turret->isAllowsPitch() )
  963. {
  964. Real desiredPitch = 0;
  965. // Some turrets want to fire at a set pitch, and some want to fire straight to the target
  966. if( turret->getFirePitch() > 0 )
  967. {
  968. desiredPitch = turret->getFirePitch();
  969. }
  970. else
  971. {
  972. // Find the pitch to the target, but unlike Turn, we can't go 360 so bind at 0;
  973. Coord3D v;
  974. ThePartitionManager->getVectorTo(obj, &enemyPosition, FROM_CENTER_3D, v);
  975. //GetVectorTo only takes Object as the first, but we want the angle from our Weapon to the
  976. // target, not us to the target. Raise our side to get the line to make sense.
  977. v.z -= obj->getGeometryInfo().getMaxHeightAbovePosition() / 2; // I kinda hate our logic/client split.
  978. //The point to fire from should be intrinsic to the turret, but in reality it is very slow to look it up.
  979. Real actualPitch;
  980. if( v.length() > 0 )
  981. actualPitch = ASin( v.z / v.length() );
  982. else
  983. actualPitch = 0;// Don't point at NAN, just point at 0 if they are right on us
  984. desiredPitch = actualPitch;
  985. if( desiredPitch < turret->getMinPitch() )
  986. {
  987. desiredPitch = turret->getMinPitch();
  988. }
  989. if (turret->getGroundUnitPitch() > 0) {
  990. Bool adjust = false;
  991. if (!enemy) {
  992. adjust = true; // adjust for ground targets.
  993. }
  994. if (enemy && enemy->isKindOf(KINDOF_IMMOBILE)) {
  995. adjust = true;
  996. }
  997. if (enemyAI && enemyAI->isDoingGroundMovement()) {
  998. adjust = true;
  999. }
  1000. if (adjust) {
  1001. Real range = curWeapon->getAttackRange(obj);
  1002. Real dist = v.length();
  1003. if (range<1) range = 1; // paranoia. jba.
  1004. // As the unit gets closer, reduce the pitch so we don't shoot over him.
  1005. Real groundPitch = turret->getGroundUnitPitch() * (dist/range);
  1006. desiredPitch = actualPitch+groundPitch;
  1007. if (desiredPitch < turret->getMinPitch()) {
  1008. desiredPitch = turret->getMinPitch();
  1009. }
  1010. }
  1011. }
  1012. }
  1013. pitchAlignedToNemesis = turret->friend_turnTowardsPitch(desiredPitch, 1.0f);
  1014. }
  1015. // For now, we require that we're within range before we can successfully exit the AIM state,
  1016. // and move into the FIRE state.
  1017. if (turnAlignedToNemesis && pitchAlignedToNemesis &&
  1018. ((enemyForDistanceCheckOnly && curWeapon->isWithinAttackRange(obj, enemyForDistanceCheckOnly)) ||
  1019. (!enemyForDistanceCheckOnly && curWeapon->isWithinAttackRange(obj, &enemyPosition))))
  1020. {
  1021. #ifdef INTER_TURRET_DELAY
  1022. if (m_extraDelay > 0)
  1023. {
  1024. --m_extraDelay;
  1025. }
  1026. else
  1027. {
  1028. m_extraDelay = turret->friend_getInterTurretDelay();
  1029. if (m_extraDelay == WAIT_INDEFINITELY)
  1030. {
  1031. m_extraDelay = 0;
  1032. return STATE_CONTINUE;
  1033. }
  1034. }
  1035. if (m_extraDelay != 0)
  1036. {
  1037. return STATE_CONTINUE;
  1038. }
  1039. #endif
  1040. if (preventing || nothingInRange)
  1041. {
  1042. return STATE_CONTINUE;
  1043. }
  1044. return STATE_SUCCESS;
  1045. }
  1046. // stay in aiming state until directed to do something else
  1047. return STATE_CONTINUE;
  1048. }
  1049. //-------------------------------------------------------------------------------------------------
  1050. void TurretAIAimTurretState::onExit( StateExitType status )
  1051. {
  1052. }
  1053. //-------------------------------------------------------------------------------------------------
  1054. //-------------------------------------------------------------------------------------------------
  1055. //-------------------------------------------------------------------------------------------------
  1056. //----------------------------------------------------------------------------------------------------------
  1057. StateReturnType TurretAIRecenterTurretState::onEnter()
  1058. {
  1059. return STATE_CONTINUE;
  1060. }
  1061. //-------------------------------------------------------------------------------------------------
  1062. /**
  1063. * Rotate the owner's turret to its home orientation.
  1064. */
  1065. StateReturnType TurretAIRecenterTurretState::update()
  1066. {
  1067. //DEBUG_LOG(("TurretAIRecenterTurretState frame %d: %08lx\n",TheGameLogic->getFrame(),getTurretAI()->getOwner()));
  1068. if( getMachineOwner()->testStatus( OBJECT_STATUS_UNDER_CONSTRUCTION))
  1069. return STATE_CONTINUE;//ML so that under-construction base-defenses do not re-center while under construction
  1070. TurretAI* turret = getTurretAI();
  1071. Bool angleAligned = turret->friend_turnTowardsAngle(turret->getNaturalTurretAngle(), 0.5f, 0.0f);
  1072. Bool pitchAligned = turret->friend_turnTowardsPitch(turret->getNaturalTurretPitch(), 0.5f);
  1073. if( angleAligned && pitchAligned )
  1074. return STATE_SUCCESS;
  1075. return STATE_CONTINUE;
  1076. }
  1077. //-------------------------------------------------------------------------------------------------
  1078. /**
  1079. * Stop rotation sound
  1080. */
  1081. void TurretAIRecenterTurretState::onExit( StateExitType status )
  1082. {
  1083. }
  1084. //----------------------------------------------------------------------------------------------------------
  1085. //----------------------------------------------------------------------------------------------------------
  1086. //----------------------------------------------------------------------------------------------------------
  1087. // ------------------------------------------------------------------------------------------------
  1088. /** CRC */
  1089. // ------------------------------------------------------------------------------------------------
  1090. void TurretAIIdleState::crc( Xfer *xfer )
  1091. {
  1092. } // end crc
  1093. // ------------------------------------------------------------------------------------------------
  1094. /** Xfer Method */
  1095. // ------------------------------------------------------------------------------------------------
  1096. void TurretAIIdleState::xfer( Xfer *xfer )
  1097. {
  1098. // version
  1099. XferVersion currentVersion = 1;
  1100. XferVersion version = currentVersion;
  1101. xfer->xferVersion( &version, currentVersion );
  1102. xfer->xferUnsignedInt(&m_nextIdleScan);
  1103. } // end xfer
  1104. // ------------------------------------------------------------------------------------------------
  1105. /** Load post process */
  1106. // ------------------------------------------------------------------------------------------------
  1107. void TurretAIIdleState::loadPostProcess( void )
  1108. {
  1109. } // end loadPostProcess
  1110. //----------------------------------------------------------------------------------------------------------
  1111. void TurretAIIdleState::resetIdleScan()
  1112. {
  1113. UnsignedInt now = TheGameLogic->getFrame();
  1114. UnsignedInt delay = GameLogicRandomValue(getTurretAI()->getMinIdleScanInterval(), getTurretAI()->getMaxIdleScanInterval());
  1115. m_nextIdleScan = now + delay;
  1116. }
  1117. //----------------------------------------------------------------------------------------------------------
  1118. StateReturnType TurretAIIdleState::onEnter()
  1119. {
  1120. AIUpdateInterface *ai = getMachineOwner()->getAIUpdateInterface();
  1121. if (ai)
  1122. {
  1123. ai->resetNextMoodCheckTime();
  1124. if (ai->friend_getTurretSync() == getTurretAI()->friend_getWhichTurret())
  1125. ai->friend_setTurretSync(TURRET_INVALID);
  1126. } // ai doesn't exist if the object was just created this frame.
  1127. resetIdleScan();
  1128. TurretAI* turret = getTurretAI();
  1129. return frameToSleepTime(turret->friend_getNextIdleMoodTargetFrame(), m_nextIdleScan);
  1130. }
  1131. //----------------------------------------------------------------------------------------------------------
  1132. StateReturnType TurretAIIdleState::update()
  1133. {
  1134. //DEBUG_LOG(("TurretAIIdleState frame %d: %08lx\n",TheGameLogic->getFrame(),getTurretAI()->getOwner()));
  1135. UnsignedInt now = TheGameLogic->getFrame();
  1136. if (now >= m_nextIdleScan)
  1137. {
  1138. // this is redundant, since we're exiting the state, and will reset
  1139. // it again in onEnter next time (srj)
  1140. // resetIdleScan();
  1141. return STATE_FAILURE;
  1142. }
  1143. TurretAI* turret = getTurretAI();
  1144. turret->friend_checkForIdleMoodTarget();
  1145. return frameToSleepTime(turret->friend_getNextIdleMoodTargetFrame(), m_nextIdleScan);
  1146. }
  1147. //----------------------------------------------------------------------------------------------------------
  1148. //----------------------------------------------------------------------------------------------------------
  1149. //----------------------------------------------------------------------------------------------------------
  1150. // ------------------------------------------------------------------------------------------------
  1151. /** CRC */
  1152. // ------------------------------------------------------------------------------------------------
  1153. void TurretAIIdleScanState::crc( Xfer *xfer )
  1154. {
  1155. } // end crc
  1156. // ------------------------------------------------------------------------------------------------
  1157. /** Xfer Method */
  1158. // ------------------------------------------------------------------------------------------------
  1159. void TurretAIIdleScanState::xfer( Xfer *xfer )
  1160. {
  1161. // version
  1162. XferVersion currentVersion = 1;
  1163. XferVersion version = currentVersion;
  1164. xfer->xferVersion( &version, currentVersion );
  1165. xfer->xferReal(&m_desiredAngle);
  1166. } // end xfer
  1167. // ------------------------------------------------------------------------------------------------
  1168. /** Load post process */
  1169. // ------------------------------------------------------------------------------------------------
  1170. void TurretAIIdleScanState::loadPostProcess( void )
  1171. {
  1172. } // end loadPostProcess
  1173. //----------------------------------------------------------------------------------------------------------
  1174. StateReturnType TurretAIIdleScanState::onEnter()
  1175. {
  1176. Real minA = getTurretAI()->getMinIdleScanAngle();
  1177. Real maxA = getTurretAI()->getMaxIdleScanAngle();
  1178. if (minA == 0.0f && maxA == 0.0f)
  1179. return STATE_SUCCESS;
  1180. m_desiredAngle = minA + GameLogicRandomValueReal(0, maxA - minA);
  1181. if (GameLogicRandomValue( 0, 1 ) == 0)
  1182. m_desiredAngle = -m_desiredAngle;
  1183. return STATE_CONTINUE;
  1184. }
  1185. //-------------------------------------------------------------------------------------------------
  1186. /**
  1187. * Rotate the owner's turret to its scan orientation.
  1188. */
  1189. StateReturnType TurretAIIdleScanState::update()
  1190. {
  1191. //DEBUG_LOG(("TurretAIIdleScanState frame %d: %08lx\n",TheGameLogic->getFrame(),getTurretAI()->getOwner()));
  1192. if( getMachineOwner()->testStatus( OBJECT_STATUS_UNDER_CONSTRUCTION))
  1193. return STATE_CONTINUE;//ML so that under-construction base-defenses do not idle-scan while under construction
  1194. Bool angleAligned = getTurretAI()->friend_turnTowardsAngle(getTurretAI()->getNaturalTurretAngle() + m_desiredAngle, 0.5f, 0.0f);
  1195. Bool pitchAligned = getTurretAI()->friend_turnTowardsPitch(getTurretAI()->getNaturalTurretPitch(), 0.5f);
  1196. if( angleAligned && pitchAligned )
  1197. return STATE_SUCCESS;
  1198. return STATE_CONTINUE;
  1199. }
  1200. //-------------------------------------------------------------------------------------------------
  1201. /**
  1202. * Stop rotation sound
  1203. */
  1204. void TurretAIIdleScanState::onExit( StateExitType status )
  1205. {
  1206. }
  1207. //----------------------------------------------------------------------------------------------------------
  1208. //----------------------------------------------------------------------------------------------------------
  1209. //----------------------------------------------------------------------------------------------------------
  1210. // ------------------------------------------------------------------------------------------------
  1211. /** CRC */
  1212. // ------------------------------------------------------------------------------------------------
  1213. void TurretAIHoldTurretState::crc( Xfer *xfer )
  1214. {
  1215. } // end crc
  1216. // ------------------------------------------------------------------------------------------------
  1217. /** Xfer Method */
  1218. // ------------------------------------------------------------------------------------------------
  1219. void TurretAIHoldTurretState::xfer( Xfer *xfer )
  1220. {
  1221. // version
  1222. XferVersion currentVersion = 1;
  1223. XferVersion version = currentVersion;
  1224. xfer->xferVersion( &version, currentVersion );
  1225. xfer->xferUnsignedInt(&m_timestamp);
  1226. } // end xfer
  1227. // ------------------------------------------------------------------------------------------------
  1228. /** Load post process */
  1229. // ------------------------------------------------------------------------------------------------
  1230. void TurretAIHoldTurretState::loadPostProcess( void )
  1231. {
  1232. } // end loadPostProcess
  1233. //----------------------------------------------------------------------------------------------------------
  1234. StateReturnType TurretAIHoldTurretState::onEnter()
  1235. {
  1236. m_timestamp = TheGameLogic->getFrame() + getTurretAI()->getRecenterTime();
  1237. TurretAI* turret = getTurretAI();
  1238. return frameToSleepTime(turret->friend_getNextIdleMoodTargetFrame(), m_timestamp);
  1239. }
  1240. //-------------------------------------------------------------------------------------------------
  1241. void TurretAIHoldTurretState::onExit( StateExitType status )
  1242. {
  1243. }
  1244. //----------------------------------------------------------------------------------------------------------
  1245. /**
  1246. * Hold the turret's position for a short time before returning to center.
  1247. */
  1248. StateReturnType TurretAIHoldTurretState::update()
  1249. {
  1250. //DEBUG_LOG(("TurretAIHoldTurretState frame %d: %08lx\n",TheGameLogic->getFrame(),getTurretAI()->getOwner()));
  1251. if (TheGameLogic->getFrame() >= m_timestamp)
  1252. return STATE_SUCCESS;
  1253. TurretAI* turret = getTurretAI();
  1254. turret->friend_checkForIdleMoodTarget();
  1255. return frameToSleepTime(turret->friend_getNextIdleMoodTargetFrame(), m_timestamp);
  1256. }