JetAIUpdate.cpp 72 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372
  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. // JetAIUpdate.cpp //////////
  24. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  25. #define DEFINE_LOCOMOTORSET_NAMES
  26. #include "Common/ActionManager.h"
  27. #include "Common/GlobalData.h"
  28. #include "Common/MiscAudio.h"
  29. #include "Common/ThingFactory.h"
  30. #include "Common/ThingTemplate.h"
  31. #include "GameClient/Drawable.h"
  32. #include "GameClient/GameClient.h"
  33. #include "GameLogic/ExperienceTracker.h"
  34. #include "GameLogic/Locomotor.h"
  35. #include "GameLogic/Module/BodyModule.h"
  36. #include "GameLogic/Module/JetAIUpdate.h"
  37. #include "GameLogic/Module/ParkingPlaceBehavior.h"
  38. #include "GameLogic/Module/PhysicsUpdate.h"
  39. #include "GameLogic/Object.h"
  40. #include "GameLogic/AIPathfind.h"
  41. #include "GameLogic/PartitionManager.h"
  42. #include "GameLogic/Weapon.h"
  43. const Real BIGNUM = 99999.0f;
  44. #ifdef _INTERNAL
  45. // for occasional debugging...
  46. //#pragma optimize("", off)
  47. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  48. #endif
  49. //-------------------------------------------------------------------------------------------------
  50. enum TaxiType
  51. {
  52. FROM_HANGAR,
  53. FROM_PARKING,
  54. TO_PARKING
  55. };
  56. //-------------------------------------------------------------------------------------------------
  57. enum JetAIStateType
  58. {
  59. // note that these must be distinct (numerically) from AIStateType. ick.
  60. JETAISTATETYPE_FIRST = 1000,
  61. TAXI_FROM_HANGAR,
  62. TAKING_OFF_AWAIT_CLEARANCE,
  63. TAXI_TO_TAKEOFF,
  64. PAUSE_BEFORE_TAKEOFF,
  65. TAKING_OFF,
  66. LANDING_AWAIT_CLEARANCE,
  67. LANDING,
  68. TAXI_FROM_LANDING,
  69. ORIENT_FOR_PARKING_PLACE,
  70. RELOAD_AMMO,
  71. RETURNING_FOR_LANDING,
  72. RETURN_TO_DEAD_AIRFIELD,
  73. CIRCLING_DEAD_AIRFIELD,
  74. JETAISTATETYPE_LAST
  75. };
  76. //-------------------------------------------------------------------------------------------------
  77. static Bool isOutOfSpecialReloadAmmo(Object* jet)
  78. {
  79. // if we have at least one special reload weapon,
  80. // AND all such weapons are out of ammo,
  81. // return true.
  82. Int specials = 0;
  83. Int out = 0;
  84. for( Int i = 0; i < WEAPONSLOT_COUNT; i++ )
  85. {
  86. Weapon* weapon = jet->getWeaponInWeaponSlot((WeaponSlotType)i);
  87. if (weapon == NULL || weapon->getReloadType() != RETURN_TO_BASE_TO_RELOAD)
  88. continue;
  89. ++specials;
  90. if (weapon->getStatus() == OUT_OF_AMMO)
  91. ++out;
  92. }
  93. return specials > 0 && out == specials;
  94. }
  95. //-------------------------------------------------------------------------------------------------
  96. static ParkingPlaceBehaviorInterface* getPP(ObjectID id, Object** airfieldPP = NULL)
  97. {
  98. if (airfieldPP)
  99. *airfieldPP = NULL;
  100. Object* airfield = TheGameLogic->findObjectByID( id );
  101. if (airfield == NULL || airfield->isEffectivelyDead() || !airfield->isKindOf(KINDOF_AIRFIELD) || airfield->testStatus(OBJECT_STATUS_SOLD))
  102. return NULL;
  103. if (airfieldPP)
  104. *airfieldPP = airfield;
  105. ParkingPlaceBehaviorInterface* pp = NULL;
  106. for (BehaviorModule** i = airfield->getBehaviorModules(); *i; ++i)
  107. {
  108. if ((pp = (*i)->getParkingPlaceBehaviorInterface()) != NULL)
  109. break;
  110. }
  111. return pp;
  112. }
  113. //-------------------------------------------------------------------------------------------------
  114. class PartitionFilterHasParkingPlace : public PartitionFilter
  115. {
  116. private:
  117. ObjectID m_id;
  118. public:
  119. PartitionFilterHasParkingPlace(ObjectID id) : m_id(id) { }
  120. protected:
  121. #if defined(_DEBUG) || defined(_INTERNAL)
  122. virtual const char* debugGetName() { return "PartitionFilterHasParkingPlace"; }
  123. #endif
  124. virtual Bool allow(Object *objOther)
  125. {
  126. ParkingPlaceBehaviorInterface* pp = getPP(objOther->getID());
  127. if (pp != NULL && pp->reserveSpace(m_id, 0.0f, NULL))
  128. return true;
  129. return false;
  130. }
  131. };
  132. //-------------------------------------------------------------------------------------------------
  133. static Object* findSuitableAirfield(Object* jet)
  134. {
  135. PartitionFilterAcceptByKindOf filterKind(MAKE_KINDOF_MASK(KINDOF_AIRFIELD), KINDOFMASK_NONE);
  136. PartitionFilterRejectByObjectStatus filterStatus(OBJECT_STATUS_UNDER_CONSTRUCTION, 0);
  137. PartitionFilterRejectByObjectStatus filterStatusTwo(OBJECT_STATUS_SOLD, 0); // Independent to make it an OR
  138. PartitionFilterRelationship filterTeam(jet, PartitionFilterRelationship::ALLOW_ALLIES);
  139. PartitionFilterAlive filterAlive;
  140. PartitionFilterSameMapStatus filterMapStatus(jet);
  141. PartitionFilterHasParkingPlace filterPP(jet->getID());
  142. PartitionFilter *filters[16];
  143. Int numFilters = 0;
  144. filters[numFilters++] = &filterKind;
  145. filters[numFilters++] = &filterStatus;
  146. filters[numFilters++] = &filterStatusTwo;
  147. filters[numFilters++] = &filterTeam;
  148. filters[numFilters++] = &filterAlive;
  149. filters[numFilters++] = &filterPP;
  150. filters[numFilters++] = &filterMapStatus;
  151. filters[numFilters] = NULL;
  152. return ThePartitionManager->getClosestObject( jet, HUGE_DIST, FROM_CENTER_2D, filters );
  153. }
  154. //-------------------------------------------------------------------------------------------------
  155. //-------------------------------------------------------------------------------------------------
  156. //-------------------------------------------------------------------------------------------------
  157. //-------------------------------------------------------------------------------------------------
  158. /*
  159. Success: we have runway clearance
  160. Failure: no runway clearance
  161. */
  162. class JetAwaitingRunwayState : public State
  163. {
  164. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(JetAwaitingRunwayState, "JetAwaitingRunwayState")
  165. protected:
  166. // snapshot interface STUBBED.
  167. virtual void crc( Xfer *xfer ){};
  168. virtual void xfer( Xfer *xfer ){XferVersion cv = 1; XferVersion v = cv; xfer->xferVersion( &v, cv );}
  169. virtual void loadPostProcess(){};
  170. private:
  171. const Bool m_landing;
  172. public:
  173. JetAwaitingRunwayState( StateMachine *machine, Bool landing ) : m_landing(landing), State( machine, "JetAwaitingRunwayState") { }
  174. virtual StateReturnType onEnter()
  175. {
  176. Object* jet = getMachineOwner();
  177. JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
  178. if( !jetAI )
  179. return STATE_FAILURE;
  180. jetAI->friend_setTakeoffInProgress(!m_landing);
  181. jetAI->friend_setLandingInProgress(m_landing);
  182. jetAI->friend_setAllowCircling(true);
  183. return STATE_CONTINUE;
  184. }
  185. virtual StateReturnType update()
  186. {
  187. Object* jet = getMachineOwner();
  188. if (jet->isEffectivelyDead())
  189. return STATE_FAILURE;
  190. JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
  191. if( !jetAI )
  192. return STATE_FAILURE;
  193. ParkingPlaceBehaviorInterface* pp = getPP(jet->getProducerID());
  194. if (pp == NULL)
  195. {
  196. // no producer? just skip this step.
  197. return STATE_SUCCESS;
  198. }
  199. // gotta reserve a space in order to reserve a runway
  200. if (!pp->reserveSpace(jet->getID(), jetAI->friend_getParkingOffset(), NULL))
  201. {
  202. DEBUG_ASSERTCRASH(m_landing, ("hmm, this should never happen for taking-off things"));
  203. return STATE_FAILURE;
  204. }
  205. if (pp->reserveRunway(jet->getID(), m_landing))
  206. {
  207. return STATE_SUCCESS;
  208. }
  209. // can't get a runway? gotta wait.
  210. jetAI->setLocomotorGoalNone();
  211. return STATE_CONTINUE;
  212. }
  213. virtual void onExit(StateExitType status)
  214. {
  215. Object* jet = getMachineOwner();
  216. JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
  217. if (jetAI)
  218. {
  219. jetAI->friend_setTakeoffInProgress(false);
  220. jetAI->friend_setLandingInProgress(false);
  221. jetAI->friend_setAllowCircling(false);
  222. }
  223. }
  224. };
  225. EMPTY_DTOR(JetAwaitingRunwayState)
  226. //-------------------------------------------------------------------------------------------------
  227. /*
  228. Success: a new suitable airfield has appeared
  229. Failure: shouldn't normally happen
  230. */
  231. class JetOrHeliCirclingDeadAirfieldState : public State
  232. {
  233. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(JetOrHeliCirclingDeadAirfieldState, "JetOrHeliCirclingDeadAirfieldState")
  234. protected:
  235. // snapshot interface STUBBED.
  236. // The state will check immediately after a load game, but I think that's ok. jba.
  237. virtual void crc( Xfer *xfer ){};
  238. virtual void xfer( Xfer *xfer ){XferVersion cv = 1; XferVersion v = cv; xfer->xferVersion( &v, cv );}
  239. virtual void loadPostProcess(){};
  240. private:
  241. Int m_checkAirfield;
  242. enum
  243. {
  244. // only recheck for new airfields every second or so
  245. HOW_OFTEN_TO_CHECK = LOGICFRAMES_PER_SECOND
  246. };
  247. public:
  248. JetOrHeliCirclingDeadAirfieldState( StateMachine *machine ) :
  249. State( machine, "JetOrHeliCirclingDeadAirfieldState"),
  250. m_checkAirfield(0) { }
  251. virtual StateReturnType onEnter()
  252. {
  253. Object* jet = getMachineOwner();
  254. JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
  255. if( !jetAI )
  256. {
  257. return STATE_FAILURE;
  258. }
  259. // obscure case: if the jet wasn't spawned, but just placed directly on the map,
  260. // it might not have an owning airfield, and it might be trying to return
  261. // simply due to being idle, not out of ammo. so check and don't die in that
  262. // case, but just punt back out to idle.
  263. if (!isOutOfSpecialReloadAmmo(jet) && jet->getProducerID() == INVALID_ID)
  264. {
  265. return STATE_FAILURE;
  266. }
  267. // just stay where we are.
  268. jetAI->setLocomotorGoalNone();
  269. m_checkAirfield = HOW_OFTEN_TO_CHECK;
  270. //Play the "low fuel" voice whenever the craft is circling above the airfield.
  271. AudioEventRTS soundToPlay = *jet->getTemplate()->getPerUnitSound( "VoiceLowFuel" );
  272. soundToPlay.setObjectID( jet->getID() );
  273. TheAudio->addAudioEvent( &soundToPlay );
  274. return STATE_CONTINUE;
  275. }
  276. virtual StateReturnType update()
  277. {
  278. Object* jet = getMachineOwner();
  279. JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
  280. if( !jetAI )
  281. {
  282. return STATE_FAILURE;
  283. }
  284. // just stay where we are.
  285. jetAI->setLocomotorGoalNone();
  286. Real damageRate = jetAI->friend_getOutOfAmmoDamagePerSecond();
  287. if (damageRate > 0)
  288. {
  289. // convert to damage/sec to damage/frame
  290. damageRate *= SECONDS_PER_LOGICFRAME_REAL;
  291. // since it's a percentage, multiply times the max health
  292. damageRate *= jet->getBodyModule()->getMaxHealth();
  293. DamageInfo damageInfo;
  294. damageInfo.in.m_damageType = DAMAGE_UNRESISTABLE;
  295. damageInfo.in.m_deathType = DEATH_NORMAL;
  296. damageInfo.in.m_sourceID = INVALID_ID;
  297. damageInfo.in.m_amount = damageRate;
  298. jet->attemptDamage( &damageInfo );
  299. }
  300. if (--m_checkAirfield <= 0)
  301. {
  302. m_checkAirfield = HOW_OFTEN_TO_CHECK;
  303. Object* airfield = findSuitableAirfield( jet );
  304. if (airfield)
  305. {
  306. jet->setProducer(airfield);
  307. return STATE_SUCCESS;
  308. }
  309. }
  310. return STATE_CONTINUE;
  311. }
  312. };
  313. EMPTY_DTOR(JetOrHeliCirclingDeadAirfieldState)
  314. //-------------------------------------------------------------------------------------------------
  315. /*
  316. Success: we returned to the dead-airfield location
  317. Failure: shouldn't normally happen
  318. */
  319. class JetOrHeliReturningToDeadAirfieldState : public AIInternalMoveToState
  320. {
  321. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(JetOrHeliReturningToDeadAirfieldState, "JetOrHeliReturningToDeadAirfieldState")
  322. public:
  323. JetOrHeliReturningToDeadAirfieldState( StateMachine *machine ) : AIInternalMoveToState( machine, "JetOrHeliReturningToDeadAirfieldState") { }
  324. virtual StateReturnType onEnter()
  325. {
  326. Object* jet = getMachineOwner();
  327. JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
  328. if( !jetAI )
  329. {
  330. return STATE_FAILURE;
  331. }
  332. setAdjustsDestination(true);
  333. m_goalPosition = *jetAI->friend_getProducerLocation();
  334. return AIInternalMoveToState::onEnter();
  335. }
  336. };
  337. EMPTY_DTOR(JetOrHeliReturningToDeadAirfieldState)
  338. //-------------------------------------------------------------------------------------------------
  339. // This solution uses the
  340. // http://www.faqs.org/faqs/graphics/algorithms-faq/
  341. // Subject 1.03
  342. static Bool intersectInfiniteLine2D
  343. (
  344. Real ax, Real ay, Real ao,
  345. Real cx, Real cy, Real co,
  346. Real& ix, Real& iy
  347. )
  348. {
  349. Real bx = ax + Cos(ao);
  350. Real by = ay + Sin(ao);
  351. Real dx = cx + Cos(co);
  352. Real dy = cy + Sin(co);
  353. Real denom = ((bx - ax) * (dy - cy) - (by - ay) * (dx - cx));
  354. if (denom == 0.0f)
  355. {
  356. // the lines are parallel.
  357. return false;
  358. }
  359. // The lines intersect.
  360. Real r = ((ay - cy) * (dx - cx) - (ax - cx) * (dy - cy) ) / denom;
  361. ix = ax + r * (bx - ax);
  362. iy = ay + r * (by - ay);
  363. return true;
  364. }
  365. //-------------------------------------------------------------------------------------------------
  366. /*
  367. Success: we are on the ground at the runway start
  368. Failure: we are unable to get on the ground
  369. */
  370. class JetOrHeliTaxiState : public AIMoveOutOfTheWayState
  371. {
  372. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(JetOrHeliTaxiState, "JetOrHeliTaxiState")
  373. private:
  374. TaxiType m_taxiMode;
  375. public:
  376. JetOrHeliTaxiState( StateMachine *machine, TaxiType m ) : m_taxiMode(m), AIMoveOutOfTheWayState( machine ) { }
  377. virtual StateReturnType onEnter()
  378. {
  379. Object* jet = getMachineOwner();
  380. JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
  381. if( !jetAI )
  382. return STATE_FAILURE;
  383. jetAI->setCanPathThroughUnits(true);
  384. jetAI->friend_setTakeoffInProgress(m_taxiMode != TO_PARKING);
  385. jetAI->friend_setLandingInProgress(m_taxiMode == TO_PARKING);
  386. jetAI->friend_setTaxiInProgress(true);
  387. jetAI->friend_setAllowAirLoco(false);
  388. jetAI->chooseLocomotorSet(LOCOMOTORSET_TAXIING);
  389. DEBUG_ASSERTCRASH(jetAI->getCurLocomotor(), ("no loco"));
  390. Object* airfield;
  391. ParkingPlaceBehaviorInterface* pp = getPP(jet->getProducerID(), &airfield);
  392. if (pp == NULL)
  393. return STATE_SUCCESS; // no airfield? just skip this step.
  394. ParkingPlaceBehaviorInterface::PPInfo ppinfo;
  395. if (!pp->reserveSpace(jet->getID(), jetAI->friend_getParkingOffset(), &ppinfo))
  396. return STATE_FAILURE; // full?
  397. Coord3D intermedPt;
  398. Bool intermed = false;
  399. Real orient = atan2(ppinfo.runwayPrep.y - ppinfo.parkingSpace.y, ppinfo.runwayPrep.x - ppinfo.parkingSpace.x);
  400. if (fabs(stdAngleDiff(orient, ppinfo.parkingOrientation)) > PI/128)
  401. {
  402. intermedPt.z = (ppinfo.parkingSpace.z + ppinfo.runwayPrep.z) * 0.5f;
  403. intermed = intersectInfiniteLine2D(
  404. ppinfo.parkingSpace.x, ppinfo.parkingSpace.y, ppinfo.parkingOrientation,
  405. ppinfo.runwayPrep.x, ppinfo.runwayPrep.y, ppinfo.parkingOrientation + PI/2,
  406. intermedPt.x, intermedPt.y);
  407. }
  408. jetAI->destroyPath();
  409. Path *movePath;
  410. movePath = newInstance(Path);
  411. Coord3D pos = *jet->getPosition();
  412. movePath->prependNode( &pos, LAYER_GROUND );
  413. movePath->markOptimized();
  414. if (m_taxiMode == TO_PARKING)
  415. {
  416. movePath->appendNode( &ppinfo.runwayPrep, LAYER_GROUND );
  417. if (intermed)
  418. movePath->appendNode( &intermedPt, LAYER_GROUND );
  419. movePath->appendNode( &ppinfo.parkingSpace, LAYER_GROUND );
  420. }
  421. else if (m_taxiMode == FROM_PARKING)
  422. {
  423. if (intermed)
  424. movePath->appendNode( &intermedPt, LAYER_GROUND );
  425. movePath->appendNode( &ppinfo.runwayPrep, LAYER_GROUND );
  426. movePath->appendNode( &ppinfo.runwayStart, LAYER_GROUND );
  427. }
  428. else if (m_taxiMode == FROM_HANGAR)
  429. {
  430. movePath->appendNode( &ppinfo.parkingSpace, LAYER_GROUND );
  431. }
  432. m_waitingForPath = FALSE;
  433. TheAI->pathfinder()->setDebugPath(movePath);
  434. setAdjustsDestination(false); // precision is necessary
  435. jetAI->friend_setPath( movePath );
  436. DEBUG_ASSERTCRASH(jetAI->getCurLocomotor(), ("no loco"));
  437. jetAI->getCurLocomotor()->setUsePreciseZPos(true);
  438. jetAI->getCurLocomotor()->setUltraAccurate(true);
  439. jetAI->getCurLocomotor()->setAllowInvalidPosition(true);
  440. jetAI->ignoreObstacleID(jet->getProducerID());
  441. StateReturnType ret = AIMoveOutOfTheWayState::onEnter();
  442. return ret;
  443. }
  444. virtual void onExit( StateExitType status )
  445. {
  446. Object* jet = getMachineOwner();
  447. JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
  448. if (jetAI)
  449. {
  450. jetAI->getCurLocomotor()->setUsePreciseZPos(false);
  451. jetAI->getCurLocomotor()->setUltraAccurate(false);
  452. jetAI->getCurLocomotor()->setAllowInvalidPosition(false);
  453. jetAI->friend_setTakeoffInProgress(false);
  454. jetAI->friend_setLandingInProgress(false);
  455. jetAI->friend_setTaxiInProgress(false);
  456. jetAI->setCanPathThroughUnits(false);
  457. }
  458. AIMoveOutOfTheWayState::onExit(status);
  459. }
  460. };
  461. EMPTY_DTOR(JetOrHeliTaxiState)
  462. //-------------------------------------------------------------------------------------------------
  463. /*
  464. Success: we are on the ground at the runway start
  465. Failure: we are unable to get on the ground
  466. */
  467. class JetTakeoffOrLandingState : public AIFollowPathState
  468. {
  469. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(JetTakeoffOrLandingState, "JetTakeoffOrLandingState")
  470. private:
  471. Real m_maxLift;
  472. Real m_maxSpeed;
  473. #ifdef CIRCLE_FOR_LANDING
  474. Coord3D m_circleForLandingPos;
  475. #endif
  476. Bool m_landing;
  477. Bool m_landingSoundPlayed;
  478. public:
  479. JetTakeoffOrLandingState( StateMachine *machine, Bool landing ) : m_landing(landing), AIFollowPathState( machine, "JetTakeoffOrLandingState" ) { }
  480. virtual StateReturnType onEnter()
  481. {
  482. Object* jet = getMachineOwner();
  483. JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
  484. if (!jetAI)
  485. return STATE_FAILURE;
  486. if (jet->isEffectivelyDead())
  487. return STATE_FAILURE;
  488. jetAI->friend_setTakeoffInProgress(!m_landing);
  489. jetAI->friend_setLandingInProgress(m_landing);
  490. jetAI->friend_setAllowAirLoco(true);
  491. jetAI->chooseLocomotorSet(LOCOMOTORSET_NORMAL);
  492. Locomotor* loco = jetAI->getCurLocomotor();
  493. DEBUG_ASSERTCRASH(loco, ("no loco"));
  494. loco->setMaxLift(BIGNUM);
  495. BodyDamageType bdt = jet->getBodyModule()->getDamageState();
  496. m_maxLift = loco->getMaxLift(bdt);
  497. m_maxSpeed = loco->getMaxSpeedForCondition(bdt);
  498. m_landingSoundPlayed = FALSE;
  499. if (m_landing)
  500. {
  501. loco->setMaxSpeed(loco->getMinSpeed());
  502. }
  503. else
  504. {
  505. loco->setMaxLift(0);
  506. }
  507. loco->setUsePreciseZPos(true);
  508. loco->setUltraAccurate(true);
  509. jetAI->ignoreObstacleID(jet->getProducerID());
  510. ParkingPlaceBehaviorInterface* pp = getPP(jet->getProducerID());
  511. if (pp == NULL)
  512. return STATE_SUCCESS; // no airfield? just skip this step
  513. ParkingPlaceBehaviorInterface::PPInfo ppinfo;
  514. if (!pp->reserveSpace(jet->getID(), jetAI->friend_getParkingOffset(), &ppinfo))
  515. {
  516. // it's full.
  517. return STATE_FAILURE;
  518. }
  519. // only check this for landing; we might have already given up the reservation to the guy behind us for takeoff
  520. if (m_landing)
  521. {
  522. if (!pp->reserveRunway(jet->getID(), m_landing))
  523. {
  524. DEBUG_CRASH(("we should never get to this state unless we have a runway available"));
  525. return STATE_FAILURE;
  526. }
  527. }
  528. std::vector<Coord3D> path;
  529. if (m_landing)
  530. {
  531. #ifdef CIRCLE_FOR_LANDING
  532. m_circleForLandingPos = ppinfo.runwayApproach;
  533. m_circleForLandingPos.z = (ppinfo.runwayEnd.z + ppinfo.runwayApproach.z)*0.5f;
  534. #else
  535. path.push_back(ppinfo.runwayApproach);
  536. #endif
  537. path.push_back(ppinfo.runwayEnd);
  538. path.push_back(ppinfo.runwayStart);
  539. }
  540. else
  541. {
  542. ppinfo.runwayEnd.z = ppinfo.runwayApproach.z;
  543. path.push_back(ppinfo.runwayEnd);
  544. path.push_back(ppinfo.runwayApproach);
  545. }
  546. setAdjustsDestination(false); // precision is necessary
  547. setAdjustFinalDestination(false); // especially at the endpoint!
  548. jetAI->friend_setGoalPath( &path );
  549. StateReturnType ret = AIFollowPathState::onEnter();
  550. return ret;
  551. }
  552. virtual StateReturnType update()
  553. {
  554. Object* jet = getMachineOwner();
  555. if (jet->isEffectivelyDead())
  556. return STATE_FAILURE;
  557. JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
  558. if( !jetAI )
  559. return STATE_FAILURE;
  560. if (m_landing)
  561. {
  562. #ifdef CIRCLE_FOR_LANDING
  563. if (jet->getPosition()->z > m_circleForLandingPos.z)
  564. {
  565. const Real THRESH = 4.0f;
  566. jetAI->getCurLocomotor()->setAltitudeChangeThresholdForCircling(THRESH);
  567. jetAI->setLocomotorGoalPositionExplicit(m_circleForLandingPos);
  568. return STATE_CONTINUE;
  569. }
  570. else
  571. #endif
  572. {
  573. jetAI->getCurLocomotor()->setMaxLift(BIGNUM);
  574. #ifdef CIRCLE_FOR_LANDING
  575. jetAI->getCurLocomotor()->setAltitudeChangeThresholdForCircling(0);
  576. #endif
  577. }
  578. if( !m_landingSoundPlayed )
  579. {
  580. Real zPos = jet->getPosition()->z;
  581. Real zSlop = 0.25f;
  582. PathfindLayerEnum layer = TheTerrainLogic->getHighestLayerForDestination( jet->getPosition() );
  583. Real groundZ = TheTerrainLogic->getLayerHeight( jet->getPosition()->x, jet->getPosition()->y, layer );
  584. if( zPos - zSlop <= groundZ )
  585. {
  586. m_landingSoundPlayed = TRUE;
  587. AudioEventRTS soundToPlay = TheAudio->getMiscAudio()->m_aircraftWheelScreech;
  588. soundToPlay.setPosition( jet->getPosition() );
  589. TheAudio->addAudioEvent( &soundToPlay );
  590. }
  591. }
  592. }
  593. else
  594. {
  595. ParkingPlaceBehaviorInterface* pp = getPP(jet->getProducerID());
  596. if (pp)
  597. pp->transferRunwayReservationToNextInLineForTakeoff(jet->getID());
  598. PhysicsBehavior* physics = jet->getPhysics();
  599. Real ratio = physics->getVelocityMagnitude() / (m_maxSpeed * jetAI->friend_getTakeoffSpeedForMaxLift());
  600. if (ratio < 0.0f) ratio = 0.0f;
  601. if (ratio > 1.0f) ratio = 1.0f;
  602. jetAI->getCurLocomotor()->setMaxLift(m_maxLift * ratio);
  603. }
  604. StateReturnType ret = AIFollowPathState::update();
  605. return ret;
  606. }
  607. virtual void onExit( StateExitType status )
  608. {
  609. AIFollowPathState::onExit(status);
  610. // just in case.
  611. Object* jet = getMachineOwner();
  612. JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
  613. if( !jetAI )
  614. return;
  615. jetAI->friend_setTakeoffInProgress(false);
  616. jetAI->friend_setLandingInProgress(false);
  617. jetAI->friend_enableAfterburners(false);
  618. // Paranoia checks - sometimes onExit is called when we are
  619. // shutting down, and not all pieces are valid. CurLocomotor
  620. // is definitely null in some cases. jba.
  621. Locomotor* loco = jetAI->getCurLocomotor();
  622. if (loco)
  623. {
  624. loco->setUsePreciseZPos(false);
  625. loco->setUltraAccurate(false);
  626. // don't restore lift if dead -- this may fight with JetSlowDeathBehavior!
  627. if (!jet->isEffectivelyDead())
  628. loco->setMaxLift(BIGNUM);
  629. #ifdef CIRCLE_FOR_LANDING
  630. loco->setAltitudeChangeThresholdForCircling(0);
  631. #endif
  632. }
  633. jetAI->ignoreObstacleID(INVALID_ID);
  634. ParkingPlaceBehaviorInterface* pp = getPP(jet->getProducerID());
  635. if (!m_landing)
  636. {
  637. if (pp && !jetAI->friend_keepsParkingSpaceWhenAirborne())
  638. pp->releaseSpace(jet->getID());
  639. }
  640. if (pp)
  641. pp->releaseRunway(jet->getID());
  642. }
  643. };
  644. EMPTY_DTOR(JetTakeoffOrLandingState)
  645. //-------------------------------------------------------------------------------------------------
  646. static Real calcDistSqr(const Coord3D& a, const Coord3D& b)
  647. {
  648. return sqr(a.x-b.x) + sqr(a.y-b.y) + sqr(a.z-b.z);
  649. }
  650. //-------------------------------------------------------------------------------------------------
  651. /*
  652. Success: we are on the ground at the runway start
  653. Failure: we are unable to get on the ground
  654. */
  655. class HeliTakeoffOrLandingState : public State
  656. {
  657. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(HeliTakeoffOrLandingState, "HeliTakeoffOrLandingState")
  658. protected:
  659. // snapshot interface
  660. virtual void crc( Xfer *xfer )
  661. {
  662. // empty. jba.
  663. }
  664. virtual void xfer( Xfer *xfer )
  665. {
  666. // version
  667. XferVersion currentVersion = 1;
  668. XferVersion version = currentVersion;
  669. xfer->xferVersion( &version, currentVersion );
  670. // set on create. xfer->xferBool(&m_landing);
  671. xfer->xferCoord3D(&m_path[0]);
  672. xfer->xferCoord3D(&m_path[1]);
  673. xfer->xferInt(&m_index);
  674. xfer->xferCoord3D(&m_parkingLoc);
  675. xfer->xferReal(&m_parkingOrientation);
  676. }
  677. virtual void loadPostProcess()
  678. {
  679. // empty. jba.
  680. }
  681. private:
  682. Coord3D m_path[2];
  683. Int m_index;
  684. Coord3D m_parkingLoc;
  685. Real m_parkingOrientation;
  686. Bool m_landing;
  687. public:
  688. HeliTakeoffOrLandingState( StateMachine *machine, Bool landing ) : m_landing(landing),
  689. State( machine, "HeliTakeoffOrLandingState" ), m_index(0)
  690. {
  691. m_parkingLoc.zero();
  692. }
  693. virtual StateReturnType onEnter()
  694. {
  695. Object* jet = getMachineOwner();
  696. JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
  697. if( !jetAI )
  698. return STATE_FAILURE;
  699. jetAI->friend_setTakeoffInProgress(!m_landing);
  700. jetAI->friend_setLandingInProgress(m_landing);
  701. jetAI->friend_setAllowAirLoco(true);
  702. jetAI->chooseLocomotorSet(LOCOMOTORSET_NORMAL);
  703. Locomotor* loco = jetAI->getCurLocomotor();
  704. DEBUG_ASSERTCRASH(loco, ("no loco"));
  705. loco->setUsePreciseZPos(true);
  706. loco->setUltraAccurate(true);
  707. jetAI->ignoreObstacleID(jet->getProducerID());
  708. Object* airfield;
  709. ParkingPlaceBehaviorInterface* pp = getPP(jet->getProducerID(), &airfield);
  710. if (pp == NULL)
  711. return STATE_SUCCESS; // no airfield? just skip this step
  712. Coord3D landingApproach;
  713. if (jet->isKindOf(KINDOF_PRODUCED_AT_HELIPAD))
  714. {
  715. if (m_landing)
  716. {
  717. m_parkingLoc = jetAI->friend_getLandingPosForHelipadStuff();
  718. m_parkingOrientation = jet->getOrientation();
  719. }
  720. else
  721. {
  722. m_parkingOrientation = jet->getOrientation();
  723. m_parkingLoc = *jet->getPosition();
  724. }
  725. landingApproach = m_parkingLoc;
  726. landingApproach.z += pp->getApproachHeight();
  727. }
  728. else
  729. {
  730. ParkingPlaceBehaviorInterface::PPInfo ppinfo;
  731. if (!pp->reserveSpace(jet->getID(), jetAI->friend_getParkingOffset(), &ppinfo))
  732. return STATE_FAILURE;
  733. m_parkingLoc = ppinfo.parkingSpace;
  734. m_parkingOrientation = ppinfo.parkingOrientation;
  735. landingApproach = m_parkingLoc;
  736. landingApproach.z += (ppinfo.runwayApproach.z - ppinfo.runwayEnd.z);
  737. }
  738. if (m_landing)
  739. {
  740. m_path[0] = landingApproach;
  741. m_path[1] = m_parkingLoc;
  742. }
  743. else
  744. {
  745. m_path[0] = m_parkingLoc;
  746. m_path[1] = landingApproach;
  747. m_path[1].z = landingApproach.z;
  748. }
  749. m_index = 0;
  750. return STATE_CONTINUE;
  751. }
  752. virtual StateReturnType update()
  753. {
  754. Object* jet = getMachineOwner();
  755. if (jet->isEffectivelyDead())
  756. return STATE_FAILURE;
  757. JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
  758. if( !jetAI )
  759. return STATE_FAILURE;
  760. // I have disabled this because it is no longer necessary and is a bit funky lookin' (srj)
  761. #ifdef NOT_IN_USE
  762. // magically position it correctly.
  763. jet->getPhysics()->scrubVelocity2D(0);
  764. Coord3D hoverloc = m_path[m_index];
  765. hoverloc.z = jet->getPosition()->z;
  766. #if 1
  767. Coord3D pos = *jet->getPosition();
  768. Real dx = hoverloc.x - pos.x;
  769. Real dy = hoverloc.y - pos.y;
  770. Real dSqr = dx*dx+dy*dy;
  771. const Real DARN_CLOSE = 0.25f;
  772. if (dSqr < DARN_CLOSE)
  773. {
  774. jet->setPosition(&hoverloc);
  775. }
  776. else
  777. {
  778. Real dist = sqrtf(dSqr);
  779. if (dist<1) dist = 1;
  780. pos.x += PATHFIND_CELL_SIZE_F*dx/(dist*LOGICFRAMES_PER_SECOND);
  781. pos.y += PATHFIND_CELL_SIZE_F*dy/(dist*LOGICFRAMES_PER_SECOND);
  782. jet->setPosition(&pos);
  783. }
  784. #else
  785. jet->setPosition(&hoverloc);
  786. #endif
  787. jet->setOrientation(m_parkingOrientation);
  788. #endif
  789. if (jet->isKindOf(KINDOF_PRODUCED_AT_HELIPAD) || !m_landing)
  790. {
  791. TheAI->pathfinder()->adjustDestination(jet, jetAI->getLocomotorSet(), &m_path[m_index]);
  792. TheAI->pathfinder()->updateGoal(jet, &m_path[m_index], LAYER_GROUND);
  793. }
  794. jetAI->setLocomotorGoalPositionExplicit(m_path[m_index]);
  795. const Real THRESH = 3.0f;
  796. const Real THRESH_SQR = THRESH*THRESH;
  797. const Coord3D* a = jet->getPosition();
  798. const Coord3D* b = &m_path[m_index];
  799. Real distSqr = calcDistSqr(*a, *b);
  800. if (distSqr <= THRESH_SQR)
  801. ++m_index;
  802. if (m_index >= 2)
  803. return STATE_SUCCESS;
  804. return STATE_CONTINUE;
  805. }
  806. virtual void onExit( StateExitType status )
  807. {
  808. // just in case.
  809. Object* jet = getMachineOwner();
  810. JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
  811. if( !jetAI )
  812. return;
  813. jetAI->friend_setTakeoffInProgress(false);
  814. jetAI->friend_setLandingInProgress(false);
  815. // Paranoia checks - sometimes onExit is called when we are
  816. // shutting down, and not all pieces are valid. CurLocomotor
  817. // is definitely null in some cases. jba.
  818. Locomotor* loco = jetAI->getCurLocomotor();
  819. if (loco)
  820. {
  821. loco->setUsePreciseZPos(false);
  822. loco->setUltraAccurate(false);
  823. // don't restore lift if dead -- this may fight with JetSlowDeathBehavior!
  824. if (!jet->isEffectivelyDead())
  825. loco->setMaxLift(BIGNUM);
  826. }
  827. jetAI->ignoreObstacleID(INVALID_ID);
  828. ParkingPlaceBehaviorInterface* pp = getPP(jet->getProducerID());
  829. if (m_landing)
  830. {
  831. jetAI->friend_setAllowAirLoco(false);
  832. jetAI->chooseLocomotorSet(LOCOMOTORSET_TAXIING);
  833. }
  834. else
  835. {
  836. if (pp && !jetAI->friend_keepsParkingSpaceWhenAirborne())
  837. pp->releaseSpace(jet->getID());
  838. }
  839. }
  840. };
  841. EMPTY_DTOR(HeliTakeoffOrLandingState)
  842. //-------------------------------------------------------------------------------------------------
  843. class JetOrHeliParkOrientState : public State
  844. {
  845. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(JetOrHeliParkOrientState, "JetOrHeliParkOrientState")
  846. protected:
  847. // snapshot interface STUBBED.
  848. virtual void crc( Xfer *xfer ){};
  849. virtual void xfer( Xfer *xfer ){XferVersion cv = 1; XferVersion v = cv; xfer->xferVersion( &v, cv );}
  850. virtual void loadPostProcess(){};
  851. public:
  852. JetOrHeliParkOrientState( StateMachine *machine ) : State( machine, "JetOrHeliParkOrientState") { }
  853. virtual StateReturnType onEnter()
  854. {
  855. Object* jet = getMachineOwner();
  856. JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
  857. if( !jetAI )
  858. return STATE_FAILURE;
  859. if (jet->isKindOf(KINDOF_PRODUCED_AT_HELIPAD))
  860. {
  861. return STATE_SUCCESS;
  862. }
  863. jetAI->friend_setTakeoffInProgress(false);
  864. jetAI->friend_setLandingInProgress(true);
  865. jetAI->ignoreObstacleID(jet->getProducerID());
  866. return STATE_CONTINUE;
  867. }
  868. virtual StateReturnType update()
  869. {
  870. Object* jet = getMachineOwner();
  871. if (jet->isEffectivelyDead())
  872. return STATE_FAILURE;
  873. JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
  874. if( !jetAI )
  875. {
  876. return STATE_FAILURE;
  877. }
  878. ParkingPlaceBehaviorInterface* pp = getPP(jet->getProducerID());
  879. if (pp == NULL)
  880. return STATE_FAILURE;
  881. ParkingPlaceBehaviorInterface::PPInfo ppinfo;
  882. if (!pp->reserveSpace(jet->getID(), jetAI->friend_getParkingOffset(), &ppinfo))
  883. return STATE_FAILURE;
  884. const Real THRESH = 0.001f;
  885. if (fabs(stdAngleDiff(jet->getOrientation(), ppinfo.parkingOrientation)) <= THRESH)
  886. return STATE_SUCCESS;
  887. // magically position it correctly.
  888. jet->getPhysics()->scrubVelocity2D(0);
  889. Coord3D hoverloc = ppinfo.parkingSpace;
  890. hoverloc.z = jet->getPosition()->z;
  891. jet->setPosition(&hoverloc);
  892. jetAI->setLocomotorGoalOrientation(ppinfo.parkingOrientation);
  893. return STATE_CONTINUE;
  894. }
  895. virtual void onExit( StateExitType status )
  896. {
  897. Object* jet = getMachineOwner();
  898. JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
  899. if( !jetAI )
  900. return;
  901. jetAI->friend_setTakeoffInProgress(false);
  902. jetAI->friend_setLandingInProgress(false);
  903. jetAI->ignoreObstacleID(INVALID_ID);
  904. }
  905. };
  906. EMPTY_DTOR(JetOrHeliParkOrientState)
  907. //-------------------------------------------------------------------------------------------------
  908. class JetPauseBeforeTakeoffState : public AIFaceState
  909. {
  910. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(JetPauseBeforeTakeoffState, "JetPauseBeforeTakeoffState")
  911. protected:
  912. // snapshot interface
  913. virtual void crc( Xfer *xfer )
  914. {
  915. // empty. jba.
  916. }
  917. virtual void xfer( Xfer *xfer )
  918. {
  919. // version
  920. XferVersion currentVersion = 1;
  921. XferVersion version = currentVersion;
  922. xfer->xferVersion( &version, currentVersion );
  923. // set on create. xfer->xferBool(&m_landing);
  924. xfer->xferUnsignedInt(&m_when);
  925. xfer->xferUnsignedInt(&m_whenTransfer);
  926. xfer->xferBool(&m_afterburners);
  927. xfer->xferBool(&m_resetTimer);
  928. xfer->xferObjectID(&m_waitedForTaxiID);
  929. }
  930. virtual void loadPostProcess()
  931. {
  932. // empty. jba.
  933. }
  934. private:
  935. UnsignedInt m_when;
  936. UnsignedInt m_whenTransfer;
  937. ObjectID m_waitedForTaxiID;
  938. Bool m_resetTimer;
  939. Bool m_afterburners;
  940. Bool findWaiter()
  941. {
  942. Object* jet = getMachineOwner();
  943. ParkingPlaceBehaviorInterface* pp = getPP(getMachineOwner()->getProducerID());
  944. if (pp)
  945. {
  946. Int count = pp->getRunwayCount();
  947. for (Int i = 0; i < count; ++i)
  948. {
  949. Object* otherJet = TheGameLogic->findObjectByID(pp->getRunwayReservation(i));
  950. if (otherJet == NULL || otherJet == jet)
  951. continue;
  952. AIUpdateInterface* ai = otherJet->getAIUpdateInterface();
  953. if (ai == NULL)
  954. continue;
  955. if (ai->getCurrentStateID() == TAXI_TO_TAKEOFF)
  956. {
  957. if (m_waitedForTaxiID == INVALID_ID)
  958. {
  959. m_waitedForTaxiID = otherJet->getID();
  960. }
  961. return true;
  962. }
  963. }
  964. }
  965. return false;
  966. }
  967. public:
  968. JetPauseBeforeTakeoffState( StateMachine *machine ) :
  969. AIFaceState(machine, false),
  970. m_when(0),
  971. m_whenTransfer(0),
  972. m_waitedForTaxiID(INVALID_ID),
  973. m_resetTimer(false),
  974. m_afterburners(false)
  975. {
  976. // nothing
  977. }
  978. virtual StateReturnType onEnter()
  979. {
  980. Object* jet = getMachineOwner();
  981. JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
  982. if( !jetAI )
  983. return STATE_FAILURE;
  984. jetAI->friend_setTakeoffInProgress(true);
  985. jetAI->friend_setLandingInProgress(false);
  986. m_when = 0;
  987. m_whenTransfer = 0;
  988. m_waitedForTaxiID = INVALID_ID;
  989. m_resetTimer = false;
  990. m_afterburners = false;
  991. ParkingPlaceBehaviorInterface* pp = getPP(jet->getProducerID());
  992. if (pp == NULL)
  993. return STATE_SUCCESS; // no airfield? just skip this step.
  994. ParkingPlaceBehaviorInterface::PPInfo ppinfo;
  995. if (!pp->reserveSpace(jet->getID(), jetAI->friend_getParkingOffset(), &ppinfo))
  996. return STATE_SUCCESS; // full?
  997. getMachine()->setGoalPosition(&ppinfo.runwayEnd);
  998. return AIFaceState::onEnter();
  999. }
  1000. virtual StateReturnType update()
  1001. {
  1002. Object* jet = getMachineOwner();
  1003. JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
  1004. if (jet->isEffectivelyDead())
  1005. return STATE_FAILURE;
  1006. // always call this.
  1007. StateReturnType superStatus = AIFaceState::update();
  1008. if (findWaiter())
  1009. return STATE_CONTINUE;
  1010. UnsignedInt now = TheGameLogic->getFrame();
  1011. if (!m_resetTimer)
  1012. {
  1013. // we had to wait, but now everyone else is ready, so restart our countdown.
  1014. m_when = now + jetAI->friend_getTakeoffPause();
  1015. if (m_waitedForTaxiID == INVALID_ID)
  1016. {
  1017. m_waitedForTaxiID = jet->getID(); // just so we don't pick up anyone else
  1018. m_whenTransfer = now + 1;
  1019. }
  1020. else
  1021. {
  1022. m_whenTransfer = now + 2; // 2 seems odd, but is correct
  1023. }
  1024. m_resetTimer = true;
  1025. }
  1026. if (!m_afterburners)
  1027. {
  1028. jetAI->friend_enableAfterburners(true);
  1029. m_afterburners = true;
  1030. }
  1031. DEBUG_ASSERTCRASH(m_when != 0, ("hmm"));
  1032. DEBUG_ASSERTCRASH(m_whenTransfer != 0, ("hmm"));
  1033. // once we start the final wait, release the runways for guys behind us, so they can start taxiing
  1034. ParkingPlaceBehaviorInterface* pp = getPP(jet->getProducerID());
  1035. if (pp && now >= m_whenTransfer)
  1036. {
  1037. pp->transferRunwayReservationToNextInLineForTakeoff(jet->getID());
  1038. }
  1039. if (now >= m_when)
  1040. return superStatus;
  1041. return STATE_CONTINUE;
  1042. }
  1043. virtual void onExit(StateExitType status)
  1044. {
  1045. Object* jet = getMachineOwner();
  1046. JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
  1047. jetAI->friend_setTakeoffInProgress(false);
  1048. jetAI->friend_setLandingInProgress(false);
  1049. AIFaceState::onExit(status);
  1050. }
  1051. };
  1052. EMPTY_DTOR(JetPauseBeforeTakeoffState)
  1053. //-------------------------------------------------------------------------------------------------
  1054. class JetOrHeliReloadAmmoState : public State
  1055. {
  1056. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(JetOrHeliReloadAmmoState, "JetOrHeliReloadAmmoState")
  1057. private:
  1058. UnsignedInt m_reloadTime;
  1059. UnsignedInt m_reloadDoneFrame;
  1060. protected:
  1061. // snapshot interface
  1062. virtual void crc( Xfer *xfer )
  1063. {
  1064. // empty. jba.
  1065. }
  1066. virtual void xfer( Xfer *xfer )
  1067. {
  1068. // version
  1069. XferVersion currentVersion = 1;
  1070. XferVersion version = currentVersion;
  1071. xfer->xferVersion( &version, currentVersion );
  1072. // set on create. xfer->xferBool(&m_landing);
  1073. xfer->xferUnsignedInt(&m_reloadTime);
  1074. xfer->xferUnsignedInt(&m_reloadDoneFrame);
  1075. }
  1076. virtual void loadPostProcess()
  1077. {
  1078. // empty. jba.
  1079. }
  1080. public:
  1081. JetOrHeliReloadAmmoState( StateMachine *machine ) : State( machine, "JetOrHeliReloadAmmoState") { }
  1082. virtual StateReturnType onEnter()
  1083. {
  1084. Object* jet = getMachineOwner();
  1085. JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
  1086. if( !jetAI )
  1087. return STATE_FAILURE;
  1088. jetAI->friend_setTakeoffInProgress(false);
  1089. jetAI->friend_setLandingInProgress(false);
  1090. jetAI->friend_setUseSpecialReturnLoco(false);
  1091. m_reloadTime = 0;
  1092. for (Int i = 0; i < WEAPONSLOT_COUNT; ++i)
  1093. {
  1094. const Weapon* w = jet->getWeaponInWeaponSlot((WeaponSlotType)i);
  1095. if (w == NULL)
  1096. continue;
  1097. Int remaining = w->getRemainingAmmo();
  1098. Int clipSize = w->getClipSize();
  1099. UnsignedInt rt = w->getClipReloadTime(jet);
  1100. if (clipSize > 0)
  1101. {
  1102. // bias by amount empty.
  1103. Int needed = clipSize - remaining;
  1104. rt = (rt * needed) / clipSize;
  1105. }
  1106. if (rt > m_reloadTime)
  1107. m_reloadTime = rt;
  1108. }
  1109. if (m_reloadTime < 1)
  1110. m_reloadTime = 1;
  1111. m_reloadDoneFrame = m_reloadTime + TheGameLogic->getFrame();
  1112. return STATE_CONTINUE;
  1113. }
  1114. virtual StateReturnType update()
  1115. {
  1116. Object* jet = getMachineOwner();
  1117. UnsignedInt now = TheGameLogic->getFrame();
  1118. Bool allDone = true;
  1119. for (Int i = 0; i < WEAPONSLOT_COUNT; ++i)
  1120. {
  1121. Weapon* w = jet->getWeaponInWeaponSlot((WeaponSlotType)i);
  1122. if (w == NULL)
  1123. continue;
  1124. if (now >= m_reloadDoneFrame)
  1125. w->setClipPercentFull(1.0f, false);
  1126. else
  1127. w->setClipPercentFull((Real)(m_reloadTime - (m_reloadDoneFrame - now)) / m_reloadTime, false);
  1128. if (w->getRemainingAmmo() != w->getClipSize())
  1129. allDone = false;
  1130. }
  1131. if (allDone)
  1132. return STATE_SUCCESS;
  1133. return STATE_CONTINUE;
  1134. }
  1135. virtual void onExit(StateExitType status)
  1136. {
  1137. Object* jet = getMachineOwner();
  1138. JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
  1139. jetAI->friend_setTakeoffInProgress(false);
  1140. jetAI->friend_setLandingInProgress(false);
  1141. }
  1142. };
  1143. EMPTY_DTOR(JetOrHeliReloadAmmoState)
  1144. //-------------------------------------------------------------------------------------------------
  1145. /*
  1146. Success: we are close enough to a friendly airfield to land
  1147. Failure: we are unable to get close enough to a friendly airfield to land
  1148. */
  1149. class JetOrHeliReturnForLandingState : public AIInternalMoveToState
  1150. {
  1151. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(JetOrHeliReturnForLandingState, "JetOrHeliReturnForLandingState")
  1152. public:
  1153. JetOrHeliReturnForLandingState( StateMachine *machine ) : AIInternalMoveToState( machine, "JetOrHeliReturnForLandingState") { }
  1154. virtual StateReturnType onEnter()
  1155. {
  1156. Object* jet = getMachineOwner();
  1157. JetAIUpdate* jetAI = (JetAIUpdate*)jet->getAIUpdateInterface();
  1158. ParkingPlaceBehaviorInterface* pp = getPP(jet->getProducerID());
  1159. if (pp == NULL)
  1160. {
  1161. // nuke the producer id, since it's dead
  1162. jet->setProducer(NULL);
  1163. Object* airfield = findSuitableAirfield( jet );
  1164. pp = airfield ? getPP(airfield->getID()) : NULL;
  1165. if (airfield && pp)
  1166. {
  1167. jet->setProducer(airfield);
  1168. }
  1169. else
  1170. {
  1171. return STATE_FAILURE;
  1172. }
  1173. }
  1174. if (jet->isKindOf(KINDOF_PRODUCED_AT_HELIPAD))
  1175. {
  1176. m_goalPosition = jetAI->friend_getLandingPosForHelipadStuff();
  1177. }
  1178. else
  1179. {
  1180. ParkingPlaceBehaviorInterface::PPInfo ppinfo;
  1181. if (!pp->reserveSpace(jet->getID(), jetAI->friend_getParkingOffset(), &ppinfo))
  1182. return STATE_FAILURE;
  1183. m_goalPosition = jetAI->friend_needsRunway() ? ppinfo.runwayApproach : ppinfo.parkingSpace;
  1184. }
  1185. setAdjustsDestination(false); // precision is necessary
  1186. return AIInternalMoveToState::onEnter();
  1187. }
  1188. };
  1189. EMPTY_DTOR(JetOrHeliReturnForLandingState)
  1190. //-------------------------------------------------------------------------------------------------
  1191. //-------------------------------------------------------------------------------------------------
  1192. //-------------------------------------------------------------------------------------------------
  1193. //-------------------------------------------------------------------------------------------------
  1194. class JetAIStateMachine : public AIStateMachine
  1195. {
  1196. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( JetAIStateMachine, "JetAIStateMachine" );
  1197. public:
  1198. JetAIStateMachine( Object *owner, AsciiString name );
  1199. };
  1200. //-------------------------------------------------------------------------------------------------
  1201. JetAIStateMachine::JetAIStateMachine(Object *owner, AsciiString name) : AIStateMachine(owner, name)
  1202. {
  1203. defineState( RETURNING_FOR_LANDING, newInstance(JetOrHeliReturnForLandingState)( this ), LANDING_AWAIT_CLEARANCE, RETURN_TO_DEAD_AIRFIELD );
  1204. defineState( TAKING_OFF_AWAIT_CLEARANCE, newInstance(JetAwaitingRunwayState)( this, false ), TAXI_TO_TAKEOFF, AI_IDLE );
  1205. defineState( TAXI_TO_TAKEOFF, newInstance(JetOrHeliTaxiState)( this, FROM_PARKING ), PAUSE_BEFORE_TAKEOFF, AI_IDLE );
  1206. defineState( PAUSE_BEFORE_TAKEOFF, newInstance(JetPauseBeforeTakeoffState)( this ), TAKING_OFF, AI_IDLE );
  1207. defineState( TAKING_OFF, newInstance(JetTakeoffOrLandingState)( this, false ), AI_IDLE, AI_IDLE );
  1208. defineState( LANDING_AWAIT_CLEARANCE, newInstance(JetAwaitingRunwayState)( this, true ), LANDING, AI_IDLE );
  1209. defineState( LANDING, newInstance(JetTakeoffOrLandingState)( this, true ), TAXI_FROM_LANDING, AI_IDLE );
  1210. defineState( TAXI_FROM_LANDING, newInstance(JetOrHeliTaxiState)( this, TO_PARKING ), ORIENT_FOR_PARKING_PLACE, AI_IDLE );
  1211. defineState( TAXI_FROM_HANGAR, newInstance(JetOrHeliTaxiState)( this, FROM_HANGAR ), ORIENT_FOR_PARKING_PLACE, AI_IDLE );
  1212. defineState( ORIENT_FOR_PARKING_PLACE, newInstance(JetOrHeliParkOrientState)( this ), RELOAD_AMMO, AI_IDLE );
  1213. defineState( RELOAD_AMMO, newInstance(JetOrHeliReloadAmmoState)( this ), AI_IDLE, AI_IDLE );
  1214. defineState( RETURN_TO_DEAD_AIRFIELD, newInstance(JetOrHeliReturningToDeadAirfieldState)( this ), CIRCLING_DEAD_AIRFIELD, RETURN_TO_DEAD_AIRFIELD );
  1215. defineState( CIRCLING_DEAD_AIRFIELD, newInstance(JetOrHeliCirclingDeadAirfieldState)( this ), AI_IDLE, AI_IDLE );
  1216. }
  1217. //-------------------------------------------------------------------------------------------------
  1218. JetAIStateMachine::~JetAIStateMachine()
  1219. {
  1220. }
  1221. //-------------------------------------------------------------------------------------------------
  1222. //-------------------------------------------------------------------------------------------------
  1223. //-------------------------------------------------------------------------------------------------
  1224. //-------------------------------------------------------------------------------------------------
  1225. class HeliAIStateMachine : public AIStateMachine
  1226. {
  1227. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( HeliAIStateMachine, "HeliAIStateMachine" );
  1228. public:
  1229. HeliAIStateMachine( Object *owner, AsciiString name );
  1230. };
  1231. //-------------------------------------------------------------------------------------------------
  1232. HeliAIStateMachine::HeliAIStateMachine(Object *owner, AsciiString name) : AIStateMachine(owner, name)
  1233. {
  1234. defineState( RETURNING_FOR_LANDING, newInstance(JetOrHeliReturnForLandingState)( this ), LANDING_AWAIT_CLEARANCE, RETURN_TO_DEAD_AIRFIELD );
  1235. defineState( TAKING_OFF_AWAIT_CLEARANCE, newInstance(SuccessState)( this ), TAKING_OFF, AI_IDLE );
  1236. defineState( TAKING_OFF, newInstance(HeliTakeoffOrLandingState)( this, false ), AI_IDLE, AI_IDLE );
  1237. defineState( LANDING_AWAIT_CLEARANCE, newInstance(SuccessState)( this ), ORIENT_FOR_PARKING_PLACE, AI_IDLE );
  1238. defineState( ORIENT_FOR_PARKING_PLACE, newInstance(JetOrHeliParkOrientState)( this ), LANDING, AI_IDLE );
  1239. defineState( LANDING, newInstance(HeliTakeoffOrLandingState)( this, true ), RELOAD_AMMO, AI_IDLE );
  1240. defineState( RELOAD_AMMO, newInstance(JetOrHeliReloadAmmoState)( this ), AI_IDLE, AI_IDLE );
  1241. defineState( RETURN_TO_DEAD_AIRFIELD, newInstance(JetOrHeliReturningToDeadAirfieldState)( this ), CIRCLING_DEAD_AIRFIELD, RETURN_TO_DEAD_AIRFIELD );
  1242. defineState( CIRCLING_DEAD_AIRFIELD, newInstance(JetOrHeliCirclingDeadAirfieldState)( this ), AI_IDLE, AI_IDLE );
  1243. defineState( TAXI_FROM_HANGAR, newInstance(JetOrHeliTaxiState)( this, FROM_HANGAR ), AI_IDLE, AI_IDLE );
  1244. }
  1245. //-------------------------------------------------------------------------------------------------
  1246. HeliAIStateMachine::~HeliAIStateMachine()
  1247. {
  1248. }
  1249. //-------------------------------------------------------------------------------------------------
  1250. //-------------------------------------------------------------------------------------------------
  1251. //-------------------------------------------------------------------------------------------------
  1252. //-------------------------------------------------------------------------------------------------
  1253. JetAIUpdateModuleData::JetAIUpdateModuleData()
  1254. {
  1255. m_outOfAmmoDamagePerSecond = 0;
  1256. m_needsRunway = true;
  1257. m_keepsParkingSpaceWhenAirborne = true;
  1258. m_takeoffSpeedForMaxLift = 1.0f;
  1259. m_minHeight = 0.0f;
  1260. m_parkingOffset = 0.0f;
  1261. m_sneakyOffsetWhenAttacking = 0.0f;
  1262. m_takeoffPause = 0;
  1263. m_attackingLoco = LOCOMOTORSET_NORMAL;
  1264. m_returningLoco = LOCOMOTORSET_NORMAL;
  1265. m_attackLocoPersistTime = 0;
  1266. m_attackersMissPersistTime = 0;
  1267. m_lockonTime = 0;
  1268. m_lockonCursor.clear();
  1269. m_lockonInitialDist = 100;
  1270. m_lockonFreq = 0.5;
  1271. m_lockonAngleSpin = 720;
  1272. m_returnToBaseIdleTime = 0;
  1273. }
  1274. //-------------------------------------------------------------------------------------------------
  1275. /*static*/ void JetAIUpdateModuleData::buildFieldParse(MultiIniFieldParse& p)
  1276. {
  1277. AIUpdateModuleData::buildFieldParse(p);
  1278. static const FieldParse dataFieldParse[] =
  1279. {
  1280. { "OutOfAmmoDamagePerSecond", INI::parsePercentToReal, NULL, offsetof( JetAIUpdateModuleData, m_outOfAmmoDamagePerSecond ) },
  1281. { "NeedsRunway", INI::parseBool, NULL, offsetof( JetAIUpdateModuleData, m_needsRunway ) },
  1282. { "KeepsParkingSpaceWhenAirborne",INI::parseBool, NULL, offsetof( JetAIUpdateModuleData, m_keepsParkingSpaceWhenAirborne ) },
  1283. { "TakeoffSpeedForMaxLift", INI::parsePercentToReal, NULL, offsetof( JetAIUpdateModuleData, m_takeoffSpeedForMaxLift ) },
  1284. { "TakeoffPause", INI::parseDurationUnsignedInt, NULL, offsetof( JetAIUpdateModuleData, m_takeoffPause ) },
  1285. { "MinHeight", INI::parseReal, NULL, offsetof( JetAIUpdateModuleData, m_minHeight ) },
  1286. { "ParkingOffset", INI::parseReal, NULL, offsetof( JetAIUpdateModuleData, m_parkingOffset ) },
  1287. { "SneakyOffsetWhenAttacking", INI::parseReal, NULL, offsetof( JetAIUpdateModuleData, m_sneakyOffsetWhenAttacking ) },
  1288. { "AttackLocomotorType", INI::parseIndexList, TheLocomotorSetNames, offsetof( JetAIUpdateModuleData, m_attackingLoco ) },
  1289. { "AttackLocomotorPersistTime", INI::parseDurationUnsignedInt, NULL, offsetof( JetAIUpdateModuleData, m_attackLocoPersistTime ) },
  1290. { "AttackersMissPersistTime", INI::parseDurationUnsignedInt, NULL, offsetof( JetAIUpdateModuleData, m_attackersMissPersistTime ) },
  1291. { "ReturnForAmmoLocomotorType", INI::parseIndexList, TheLocomotorSetNames, offsetof( JetAIUpdateModuleData, m_returningLoco ) },
  1292. { "LockonTime", INI::parseDurationUnsignedInt, NULL, offsetof( JetAIUpdateModuleData, m_lockonTime ) },
  1293. { "LockonCursor", INI::parseAsciiString, NULL, offsetof( JetAIUpdateModuleData, m_lockonCursor ) },
  1294. { "LockonInitialDist", INI::parseReal, NULL, offsetof( JetAIUpdateModuleData, m_lockonInitialDist ) },
  1295. { "LockonFreq", INI::parseReal, NULL, offsetof( JetAIUpdateModuleData, m_lockonFreq ) },
  1296. { "LockonAngleSpin", INI::parseAngleReal, NULL, offsetof( JetAIUpdateModuleData, m_lockonAngleSpin ) },
  1297. { "LockonBlinky", INI::parseBool, NULL, offsetof( JetAIUpdateModuleData, m_lockonBlinky ) },
  1298. { "ReturnToBaseIdleTime", INI::parseDurationUnsignedInt, NULL, offsetof( JetAIUpdateModuleData, m_returnToBaseIdleTime ) },
  1299. { 0, 0, 0, 0 }
  1300. };
  1301. p.add(dataFieldParse);
  1302. }
  1303. //-------------------------------------------------------------------------------------------------
  1304. //-------------------------------------------------------------------------------------------------
  1305. //-------------------------------------------------------------------------------------------------
  1306. //-------------------------------------------------------------------------------------------------
  1307. AIStateMachine* JetAIUpdate::makeStateMachine()
  1308. {
  1309. if (getJetAIUpdateModuleData()->m_needsRunway)
  1310. return newInstance(JetAIStateMachine)( getObject(), "JetAIStateMachine");
  1311. else
  1312. return newInstance(HeliAIStateMachine)( getObject(), "HeliAIStateMachine");
  1313. }
  1314. //-------------------------------------------------------------------------------------------------
  1315. JetAIUpdate::JetAIUpdate( Thing *thing, const ModuleData* moduleData ) : AIUpdateInterface( thing, moduleData )
  1316. {
  1317. m_flags = 0;
  1318. m_afterburnerSound = *(getObject()->getTemplate()->getPerUnitSound("Afterburner"));
  1319. m_afterburnerSound.setObjectID(getObject()->getID());
  1320. m_attackLocoExpireFrame = 0;
  1321. m_attackersMissExpireFrame = 0;
  1322. m_untargetableExpireFrame = 0;
  1323. m_returnToBaseFrame = 0;
  1324. m_lockonDrawable = NULL;
  1325. m_landingPosForHelipadStuff.zero();
  1326. //Added By Sadullah Nader
  1327. //Initializations missing and needed
  1328. m_producerLocation.zero();
  1329. //
  1330. m_enginesOn = TRUE;
  1331. }
  1332. //-------------------------------------------------------------------------------------------------
  1333. JetAIUpdate::~JetAIUpdate()
  1334. {
  1335. if (m_lockonDrawable)
  1336. {
  1337. TheGameClient->destroyDrawable(m_lockonDrawable);
  1338. m_lockonDrawable = NULL;
  1339. }
  1340. }
  1341. //-------------------------------------------------------------------------------------------------
  1342. Bool JetAIUpdate::isIdle() const
  1343. {
  1344. // we need to do this because we enter an idle state briefly between takeoff/landing in these cases,
  1345. // but scripting relies on us never claiming to be "idle"...
  1346. if (getFlag(HAS_PENDING_COMMAND))
  1347. return false;
  1348. return AIUpdateInterface::isIdle();
  1349. }
  1350. //-------------------------------------------------------------------------------------------------
  1351. void JetAIUpdate::onObjectCreated()
  1352. {
  1353. AIUpdateInterface::onObjectCreated();
  1354. friend_setAllowAirLoco(false);
  1355. chooseLocomotorSet(LOCOMOTORSET_TAXIING);
  1356. }
  1357. //-------------------------------------------------------------------------------------------------
  1358. void JetAIUpdate::onDelete()
  1359. {
  1360. AIUpdateInterface::onDelete();
  1361. ParkingPlaceBehaviorInterface* pp = getPP(getObject()->getProducerID());
  1362. if (pp)
  1363. pp->releaseSpace(getObject()->getID());
  1364. }
  1365. //-------------------------------------------------------------------------------------------------
  1366. void JetAIUpdate::getProducerLocation()
  1367. {
  1368. if (getFlag(HAS_PRODUCER_LOCATION))
  1369. return;
  1370. Object* jet = getObject();
  1371. Object* airfield = TheGameLogic->findObjectByID( jet->getProducerID() );
  1372. if (airfield == NULL)
  1373. m_producerLocation = *jet->getPosition();
  1374. else
  1375. m_producerLocation = *airfield->getPosition();
  1376. /*
  1377. if we aren't allowed to fly, then we should be parked (or at least taxiing),
  1378. which implies we have a parking place reserved. If we don't, it's probably
  1379. because we were directly spawned via script (or directly placed on the map).
  1380. So, check to see if we have no parking place, and if not, quietly enable flight.
  1381. */
  1382. ParkingPlaceBehaviorInterface* pp = getPP(jet->getProducerID());
  1383. if (!pp || !pp->hasReservedSpace(jet->getID()))
  1384. {
  1385. friend_setAllowAirLoco(true);
  1386. chooseLocomotorSet(LOCOMOTORSET_NORMAL);
  1387. }
  1388. else
  1389. {
  1390. friend_setAllowAirLoco(false);
  1391. chooseLocomotorSet(LOCOMOTORSET_TAXIING);
  1392. }
  1393. setFlag(HAS_PRODUCER_LOCATION, true);
  1394. }
  1395. //-------------------------------------------------------------------------------------------------
  1396. UpdateSleepTime JetAIUpdate::update()
  1397. {
  1398. const JetAIUpdateModuleData* d = getJetAIUpdateModuleData();
  1399. getProducerLocation();
  1400. Object* jet = getObject();
  1401. ParkingPlaceBehaviorInterface* pp = getPP(getObject()->getProducerID());
  1402. // If idle & out of ammo, return
  1403. // have to call our parent's isIdle, because we override it to never return true
  1404. // when we have a pending command...
  1405. UnsignedInt now = TheGameLogic->getFrame();
  1406. // srj sez: not 100% sure on this. calling RELOAD_AMMO "idle" allows us to get healed while reloading,
  1407. // but might have other side effects we didn't want. if this does prove to cause a bug, be sure
  1408. // that jets (and ESPECIALLY comanches) are still getting healed at airfields.
  1409. if (AIUpdateInterface::isIdle() || getStateMachine()->getCurrentStateID() == RELOAD_AMMO)
  1410. {
  1411. if (pp != NULL)
  1412. {
  1413. if (!getFlag(ALLOW_AIR_LOCO) &&
  1414. !getFlag(HAS_PENDING_COMMAND) &&
  1415. jet->isKindOf(KINDOF_PRODUCED_AT_HELIPAD) &&
  1416. jet->getBodyModule()->getHealth() == jet->getBodyModule()->getMaxHealth())
  1417. {
  1418. // we're completely healed, so take off again
  1419. pp->setHealee(jet, false);
  1420. friend_setAllowAirLoco(true);
  1421. getStateMachine()->clear();
  1422. setLastCommandSource( CMD_FROM_AI );
  1423. getStateMachine()->setState( TAKING_OFF_AWAIT_CLEARANCE );
  1424. }
  1425. else
  1426. {
  1427. pp->setHealee(jet, !getFlag(ALLOW_AIR_LOCO));
  1428. }
  1429. }
  1430. // note that we might still have weapons with ammo, but still be forced to return to reload.
  1431. if (isOutOfSpecialReloadAmmo(jet) && getFlag(ALLOW_AIR_LOCO))
  1432. {
  1433. m_returnToBaseFrame = 0;
  1434. // this is really a "just-in-case" to ensure the targeter list doesn't spin out of control (srj)
  1435. pruneDeadTargeters();
  1436. setFlag(USE_SPECIAL_RETURN_LOCO, true);
  1437. setLastCommandSource( CMD_FROM_AI );
  1438. getStateMachine()->setState(RETURNING_FOR_LANDING);
  1439. }
  1440. else if (getFlag(HAS_PENDING_COMMAND)
  1441. // srj sez: if we are reloading ammo, wait will we are done before processing the pending command.
  1442. && getStateMachine()->getCurrentStateID() != RELOAD_AMMO)
  1443. {
  1444. m_returnToBaseFrame = 0;
  1445. AICommandParms parms(AICMD_MOVE_TO_POSITION, CMD_FROM_AI); // values don't matter, will be wiped by next line
  1446. m_mostRecentCommand.reconstitute(parms);
  1447. setFlag(HAS_PENDING_COMMAND, false);
  1448. aiDoCommand(&parms);
  1449. }
  1450. else if (m_returnToBaseFrame != 0 && now >= m_returnToBaseFrame && getFlag(ALLOW_AIR_LOCO))
  1451. {
  1452. m_returnToBaseFrame = 0;
  1453. DEBUG_ASSERTCRASH(isOutOfSpecialReloadAmmo(jet) == false, ("Hmm, this seems unlikely -- isOutOfSpecialReloadAmmo(jet)==false"));
  1454. setFlag(USE_SPECIAL_RETURN_LOCO, false);
  1455. setLastCommandSource( CMD_FROM_AI );
  1456. getStateMachine()->setState(RETURNING_FOR_LANDING);
  1457. }
  1458. else if (m_returnToBaseFrame == 0 && d->m_returnToBaseIdleTime > 0 && getFlag(ALLOW_AIR_LOCO))
  1459. {
  1460. m_returnToBaseFrame = now + d->m_returnToBaseIdleTime;
  1461. }
  1462. }
  1463. else
  1464. {
  1465. if (pp != NULL)
  1466. {
  1467. pp->setHealee(getObject(), false);
  1468. }
  1469. m_returnToBaseFrame = 0;
  1470. if (getFlag(ALLOW_INTERRUPT_AND_RESUME_OF_CUR_STATE_FOR_RELOAD) &&
  1471. isOutOfSpecialReloadAmmo(jet) && getFlag(ALLOW_AIR_LOCO))
  1472. {
  1473. setFlag(USE_SPECIAL_RETURN_LOCO, true);
  1474. setFlag(HAS_PENDING_COMMAND, true);
  1475. setFlag(ALLOW_INTERRUPT_AND_RESUME_OF_CUR_STATE_FOR_RELOAD, false);
  1476. setLastCommandSource( CMD_FROM_AI );
  1477. getStateMachine()->setState(RETURNING_FOR_LANDING);
  1478. }
  1479. }
  1480. Real minHeight = friend_getMinHeight();
  1481. Drawable* draw = jet->getDrawable();
  1482. if (draw != NULL)
  1483. {
  1484. StateID id = getStateMachine()->getCurrentStateID();
  1485. Bool needToCheckMinHeight = (id >= JETAISTATETYPE_FIRST && id <= JETAISTATETYPE_LAST) ||
  1486. !jet->isAboveTerrain() ||
  1487. !getFlag(ALLOW_AIR_LOCO);
  1488. if (needToCheckMinHeight)
  1489. {
  1490. Real ht = jet->isAboveTerrain() ? jet->getHeightAboveTerrain() : 0;
  1491. if (ht < minHeight)
  1492. {
  1493. Matrix3D tmp(1);
  1494. tmp.Set_Z_Translation(minHeight - ht);
  1495. draw->setInstanceMatrix(&tmp);
  1496. }
  1497. else
  1498. {
  1499. draw->setInstanceMatrix(NULL);
  1500. }
  1501. }
  1502. else
  1503. {
  1504. draw->setInstanceMatrix(NULL);
  1505. }
  1506. }
  1507. PhysicsBehavior* physics = jet->getPhysics();
  1508. if (physics->getVelocityMagnitude() > 0 && getFlag(ALLOW_AIR_LOCO))
  1509. jet->setModelConditionState(MODELCONDITION_JETEXHAUST);
  1510. else
  1511. jet->clearModelConditionState(MODELCONDITION_JETEXHAUST);
  1512. if (jet->testStatus(OBJECT_STATUS_IS_ATTACKING))
  1513. {
  1514. m_attackLocoExpireFrame = now + d->m_attackLocoPersistTime;
  1515. m_attackersMissExpireFrame = now + d->m_attackersMissPersistTime;
  1516. }
  1517. else
  1518. {
  1519. if (m_attackLocoExpireFrame != 0 && now >= m_attackLocoExpireFrame)
  1520. {
  1521. m_attackLocoExpireFrame = 0;
  1522. }
  1523. if (m_attackersMissExpireFrame != 0 && now >= m_attackersMissExpireFrame)
  1524. {
  1525. m_attackersMissExpireFrame = 0;
  1526. }
  1527. }
  1528. if (m_untargetableExpireFrame != 0 && now >= m_untargetableExpireFrame)
  1529. {
  1530. m_untargetableExpireFrame = 0;
  1531. }
  1532. positionLockon();
  1533. if (m_attackLocoExpireFrame != 0)
  1534. {
  1535. chooseLocomotorSet(d->m_attackingLoco);
  1536. }
  1537. else if (getFlag(USE_SPECIAL_RETURN_LOCO))
  1538. {
  1539. chooseLocomotorSet(d->m_returningLoco);
  1540. }
  1541. if( !jet->isKindOf( KINDOF_PRODUCED_AT_HELIPAD ) )
  1542. {
  1543. Drawable *draw = jet->getDrawable();
  1544. if( draw )
  1545. {
  1546. if( getFlag(TAKEOFF_IN_PROGRESS)
  1547. || getFlag(LANDING_IN_PROGRESS)
  1548. || getObject()->isSignificantlyAboveTerrain()
  1549. || isMoving()
  1550. || isWaitingForPath() )
  1551. {
  1552. if( !m_enginesOn )
  1553. {
  1554. //We just started moving, therefore turn on the engines!
  1555. draw->enableAmbientSound( TRUE );
  1556. m_enginesOn = TRUE;
  1557. }
  1558. }
  1559. else if( m_enginesOn )
  1560. {
  1561. //We're no longer moving, so turn off the engines!
  1562. draw->enableAmbientSound( FALSE );
  1563. m_enginesOn = FALSE;
  1564. }
  1565. }
  1566. }
  1567. /*UpdateSleepTime ret =*/ AIUpdateInterface::update();
  1568. //return (mine < ret) ? mine : ret;
  1569. /// @todo srj -- someday, make sleepy. for now, must not sleep.
  1570. return UPDATE_SLEEP_NONE;
  1571. }
  1572. //-------------------------------------------------------------------------------------------------
  1573. Bool JetAIUpdate::chooseLocomotorSet(LocomotorSetType wst)
  1574. {
  1575. const JetAIUpdateModuleData* d = getJetAIUpdateModuleData();
  1576. if (!getFlag(ALLOW_AIR_LOCO))
  1577. {
  1578. wst = LOCOMOTORSET_TAXIING;
  1579. }
  1580. else if (m_attackLocoExpireFrame != 0)
  1581. {
  1582. wst = d->m_attackingLoco;
  1583. }
  1584. else if (getFlag(USE_SPECIAL_RETURN_LOCO))
  1585. {
  1586. wst = d->m_returningLoco;
  1587. }
  1588. return AIUpdateInterface::chooseLocomotorSet(wst);
  1589. }
  1590. //-------------------------------------------------------------------------------------------------
  1591. void JetAIUpdate::setLocomotorGoalNone()
  1592. {
  1593. if ((getFlag(TAKEOFF_IN_PROGRESS) || getFlag(LANDING_IN_PROGRESS))
  1594. && getFlag(ALLOW_AIR_LOCO) && !getFlag(ALLOW_CIRCLING))
  1595. {
  1596. Object* jet = getObject();
  1597. Coord3D desiredPos = *jet->getPosition();
  1598. const Coord3D* dir = jet->getUnitDirectionVector2D();
  1599. desiredPos.x += dir->x * 1000.0f;
  1600. desiredPos.y += dir->y * 1000.0f;
  1601. setLocomotorGoalPositionExplicit(desiredPos);
  1602. }
  1603. else
  1604. {
  1605. AIUpdateInterface::setLocomotorGoalNone();
  1606. }
  1607. }
  1608. //----------------------------------------------------------------------------------------
  1609. Bool JetAIUpdate::getSneakyTargetingOffset(Coord3D* offset) const
  1610. {
  1611. if (m_attackersMissExpireFrame != 0 && TheGameLogic->getFrame() < m_attackersMissExpireFrame)
  1612. {
  1613. if (offset)
  1614. {
  1615. const JetAIUpdateModuleData* d = getJetAIUpdateModuleData();
  1616. const Object* jet = getObject();
  1617. const Coord3D* dir = jet->getUnitDirectionVector2D();
  1618. offset->x = dir->x * d->m_sneakyOffsetWhenAttacking;
  1619. offset->y = dir->y * d->m_sneakyOffsetWhenAttacking;
  1620. offset->z = 0.0f;
  1621. }
  1622. return true;
  1623. }
  1624. else
  1625. {
  1626. return false;
  1627. }
  1628. }
  1629. //----------------------------------------------------------------------------------------
  1630. void JetAIUpdate::pruneDeadTargeters()
  1631. {
  1632. if (!m_targetedBy.empty())
  1633. {
  1634. for (std::list<ObjectID>::iterator it = m_targetedBy.begin(); it != m_targetedBy.end(); /* empty */ )
  1635. {
  1636. if (TheGameLogic->findObjectByID(*it) == NULL)
  1637. {
  1638. it = m_targetedBy.erase(it);
  1639. }
  1640. else
  1641. {
  1642. ++it;
  1643. }
  1644. }
  1645. }
  1646. }
  1647. //----------------------------------------------------------------------------------------
  1648. void JetAIUpdate::positionLockon()
  1649. {
  1650. if (!m_lockonDrawable)
  1651. return;
  1652. if (m_untargetableExpireFrame == 0)
  1653. {
  1654. TheGameClient->destroyDrawable(m_lockonDrawable);
  1655. m_lockonDrawable = NULL;
  1656. return;
  1657. }
  1658. const JetAIUpdateModuleData* d = getJetAIUpdateModuleData();
  1659. UnsignedInt now = TheGameLogic->getFrame();
  1660. UnsignedInt remaining = m_untargetableExpireFrame - now;
  1661. UnsignedInt elapsed = d->m_lockonTime - remaining;
  1662. Coord3D pos = *getObject()->getPosition();
  1663. Real frac = (Real)remaining / (Real)d->m_lockonTime;
  1664. Real finalDist = getObject()->getGeometryInfo().getBoundingCircleRadius();
  1665. Real dist = finalDist + (d->m_lockonInitialDist - finalDist) * frac;
  1666. Real angle = d->m_lockonAngleSpin * frac;
  1667. pos.x += Cos(angle) * dist;
  1668. pos.y += Sin(angle) * dist;
  1669. // pos.z is untouched
  1670. m_lockonDrawable->setPosition(&pos);
  1671. Real dx = getObject()->getPosition()->x - pos.x;
  1672. Real dy = getObject()->getPosition()->y - pos.y;
  1673. if (dx || dy)
  1674. m_lockonDrawable->setOrientation(atan2(dy, dx));
  1675. // the Gaussian sum, to avoid keeping a running total:
  1676. //
  1677. // 1+2+3+...n = n*(n+1)/2
  1678. //
  1679. Real elapsedTimeSumPrev = 0.5f * (elapsed-1) * (elapsed);
  1680. Real elapsedTimeSumCurr = elapsedTimeSumPrev + elapsed;
  1681. Real factor = d->m_lockonFreq / d->m_lockonTime;
  1682. Bool lastPhase = ((Int)(factor * elapsedTimeSumPrev) & 1) != 0;
  1683. Bool thisPhase = ((Int)(factor * elapsedTimeSumCurr) & 1) != 0;
  1684. if (lastPhase && (!thisPhase))
  1685. {
  1686. AudioEventRTS lockonSound = TheAudio->getMiscAudio()->m_lockonTickSound;
  1687. lockonSound.setObjectID(getObject()->getID());
  1688. TheAudio->addAudioEvent(&lockonSound);
  1689. if (d->m_lockonBlinky)
  1690. m_lockonDrawable->setDrawableHidden(false);
  1691. }
  1692. else
  1693. {
  1694. if (d->m_lockonBlinky)
  1695. m_lockonDrawable->setDrawableHidden(true);
  1696. }
  1697. }
  1698. //----------------------------------------------------------------------------------------
  1699. void JetAIUpdate::buildLockonDrawableIfNecessary()
  1700. {
  1701. if (m_untargetableExpireFrame == 0)
  1702. return;
  1703. const JetAIUpdateModuleData* d = getJetAIUpdateModuleData();
  1704. if (d->m_lockonCursor.isNotEmpty() && m_lockonDrawable == NULL)
  1705. {
  1706. const ThingTemplate* tt = TheThingFactory->findTemplate(d->m_lockonCursor);
  1707. if (tt)
  1708. {
  1709. m_lockonDrawable = TheThingFactory->newDrawable(tt);
  1710. }
  1711. }
  1712. positionLockon();
  1713. }
  1714. //----------------------------------------------------------------------------------------
  1715. void JetAIUpdate::addTargeter(ObjectID id, Bool add)
  1716. {
  1717. const JetAIUpdateModuleData* d = getJetAIUpdateModuleData();
  1718. UnsignedInt lockonTime = d->m_lockonTime;
  1719. if (lockonTime != 0)
  1720. {
  1721. std::list<ObjectID>::iterator it = std::find(m_targetedBy.begin(), m_targetedBy.end(), id);
  1722. if (add)
  1723. {
  1724. if (it == m_targetedBy.end())
  1725. {
  1726. m_targetedBy.push_back(id);
  1727. if (m_untargetableExpireFrame == 0 && m_targetedBy.size() == 1)
  1728. {
  1729. m_untargetableExpireFrame = TheGameLogic->getFrame() + lockonTime;
  1730. buildLockonDrawableIfNecessary();
  1731. }
  1732. }
  1733. }
  1734. else
  1735. {
  1736. if (it != m_targetedBy.end())
  1737. {
  1738. m_targetedBy.erase(it);
  1739. if (m_targetedBy.empty())
  1740. {
  1741. m_untargetableExpireFrame = 0;
  1742. }
  1743. }
  1744. }
  1745. }
  1746. }
  1747. //----------------------------------------------------------------------------------------
  1748. Bool JetAIUpdate::isTemporarilyPreventingAimSuccess() const
  1749. {
  1750. return m_untargetableExpireFrame != 0 && (TheGameLogic->getFrame() < m_untargetableExpireFrame);
  1751. }
  1752. //----------------------------------------------------------------------------------------
  1753. Bool JetAIUpdate::isAllowedToMoveAwayFromUnit() const
  1754. {
  1755. // parked (or landing) units don't get to do this.
  1756. if (!getFlag(ALLOW_AIR_LOCO) || getFlag(TAKEOFF_IN_PROGRESS) || getFlag(LANDING_IN_PROGRESS))
  1757. return false;
  1758. return AIUpdateInterface::isAllowedToMoveAwayFromUnit();
  1759. }
  1760. //-------------------------------------------------------------------------------------------------
  1761. Bool JetAIUpdate::isDoingGroundMovement(void) const
  1762. {
  1763. // srj per jba: Air units should never be doing ground movement, even when taxiing...
  1764. // (exception: see getTreatAsAircraftForLocoDistToGoal)
  1765. return false;
  1766. }
  1767. //-------------------------------------------------------------------------------------------------
  1768. Bool JetAIUpdate::getTreatAsAircraftForLocoDistToGoal() const
  1769. {
  1770. // exception to isDoingGroundMovement: should never treat as aircraft for dist-to-goal when taxiing.
  1771. if (getFlag(TAXI_IN_PROGRESS))
  1772. {
  1773. return false;
  1774. }
  1775. else
  1776. {
  1777. return AIUpdateInterface::getTreatAsAircraftForLocoDistToGoal();
  1778. }
  1779. }
  1780. //----------------------------------------------------------------------------------------
  1781. /**
  1782. * Follow the path defined by the given array of points
  1783. */
  1784. void JetAIUpdate::privateFollowPath( const std::vector<Coord3D>* path, Object *ignoreObject, CommandSourceType cmdSource, Bool exitProduction )
  1785. {
  1786. if (exitProduction)
  1787. {
  1788. getStateMachine()->clear();
  1789. if( ignoreObject )
  1790. ignoreObstacle( ignoreObject );
  1791. setLastCommandSource( cmdSource );
  1792. if (getObject()->isKindOf(KINDOF_PRODUCED_AT_HELIPAD))
  1793. getStateMachine()->setState( TAKING_OFF_AWAIT_CLEARANCE );
  1794. else
  1795. getStateMachine()->setState( TAXI_FROM_HANGAR );
  1796. }
  1797. else
  1798. {
  1799. AIUpdateInterface::privateFollowPath(path, ignoreObject, cmdSource, exitProduction);
  1800. }
  1801. }
  1802. //----------------------------------------------------------------------------------------
  1803. void JetAIUpdate::privateFollowPathAppend( const Coord3D *pos, CommandSourceType cmdSource )
  1804. {
  1805. // nothing yet... might need to override. not sure. (srj)
  1806. AIUpdateInterface::privateFollowPathAppend(pos, cmdSource);
  1807. }
  1808. //----------------------------------------------------------------------------------------
  1809. void JetAIUpdate::doLandingCommand(Object *airfield, CommandSourceType cmdSource)
  1810. {
  1811. if (getObject()->isKindOf(KINDOF_PRODUCED_AT_HELIPAD))
  1812. {
  1813. m_landingPosForHelipadStuff = *airfield->getPosition();
  1814. Coord3D tmp;
  1815. FindPositionOptions options;
  1816. options.maxRadius = airfield->getGeometryInfo().getBoundingCircleRadius() * 10.0f;
  1817. if (ThePartitionManager->findPositionAround(&m_landingPosForHelipadStuff, &options, &tmp))
  1818. m_landingPosForHelipadStuff = tmp;
  1819. }
  1820. for (BehaviorModule** i = airfield->getBehaviorModules(); *i; ++i)
  1821. {
  1822. ParkingPlaceBehaviorInterface* pp = (*i)->getParkingPlaceBehaviorInterface();
  1823. if (pp == NULL)
  1824. continue;
  1825. if (getObject()->isKindOf(KINDOF_PRODUCED_AT_HELIPAD) ||
  1826. pp->reserveSpace(getObject()->getID(), friend_getParkingOffset(), NULL))
  1827. {
  1828. // if we had a space at another airfield, release it
  1829. ParkingPlaceBehaviorInterface* oldPP = getPP(getObject()->getProducerID());
  1830. if (oldPP != NULL && oldPP != pp)
  1831. {
  1832. oldPP->releaseSpace(getObject()->getID());
  1833. }
  1834. getObject()->setProducer(airfield);
  1835. DEBUG_ASSERTCRASH(isOutOfSpecialReloadAmmo(getObject()) == false, ("Hmm, this seems unlikely -- isOutOfSpecialReloadAmmo(jet)==false"));
  1836. setFlag(USE_SPECIAL_RETURN_LOCO, false);
  1837. setFlag(ALLOW_INTERRUPT_AND_RESUME_OF_CUR_STATE_FOR_RELOAD, false);
  1838. setLastCommandSource( cmdSource );
  1839. getStateMachine()->setState(RETURNING_FOR_LANDING);
  1840. return;
  1841. }
  1842. }
  1843. }
  1844. //----------------------------------------------------------------------------------------
  1845. void JetAIUpdate::notifyVictimIsDead()
  1846. {
  1847. if (getJetAIUpdateModuleData()->m_needsRunway)
  1848. m_returnToBaseFrame = TheGameLogic->getFrame();
  1849. }
  1850. //----------------------------------------------------------------------------------------
  1851. /**
  1852. * Enter the given object
  1853. */
  1854. void JetAIUpdate::privateEnter( Object *objectToEnter, CommandSourceType cmdSource )
  1855. {
  1856. // we are already landing. just ignore it.
  1857. if (getFlag(LANDING_IN_PROGRESS))
  1858. return;
  1859. if( !TheActionManager->canEnterObject( getObject(), objectToEnter, cmdSource, DONT_CHECK_CAPACITY ) )
  1860. return;
  1861. doLandingCommand(objectToEnter, cmdSource);
  1862. }
  1863. //----------------------------------------------------------------------------------------
  1864. /**
  1865. * Get repaired at the repair depot
  1866. */
  1867. void JetAIUpdate::privateGetRepaired( Object *repairDepot, CommandSourceType cmdSource )
  1868. {
  1869. // we are already landing. just ignore it.
  1870. if (getFlag(LANDING_IN_PROGRESS))
  1871. return;
  1872. // sanity, if we can't get repaired from here get out of here
  1873. if( TheActionManager->canGetRepairedAt( getObject(), repairDepot, cmdSource ) == FALSE )
  1874. return;
  1875. // dock with the repair depot
  1876. doLandingCommand( repairDepot, cmdSource );
  1877. }
  1878. //-------------------------------------------------------------------------------------------------
  1879. Bool JetAIUpdate::isParkedAt(const Object* obj) const
  1880. {
  1881. if (!getFlag(ALLOW_AIR_LOCO) &&
  1882. !getObject()->isKindOf(KINDOF_PRODUCED_AT_HELIPAD) &&
  1883. obj != NULL)
  1884. {
  1885. Object* airfield;
  1886. ParkingPlaceBehaviorInterface* pp = getPP(getObject()->getProducerID(), &airfield);
  1887. if (pp != NULL && airfield != NULL && airfield == obj)
  1888. {
  1889. return true;
  1890. }
  1891. }
  1892. return false;
  1893. }
  1894. //-------------------------------------------------------------------------------------------------
  1895. void JetAIUpdate::aiDoCommand(const AICommandParms* parms)
  1896. {
  1897. // call this from aiDoCommand as well as update, because this can
  1898. // be called before update ever is... if the unit is placed on a map,
  1899. // and a script tells it to do something with a condition of TRUE!
  1900. getProducerLocation();
  1901. if (!isAllowedToRespondToAiCommands(parms))
  1902. return;
  1903. // note that we always store this, even if nothing will be "pending".
  1904. m_mostRecentCommand.store(*parms);
  1905. if (getFlag(TAKEOFF_IN_PROGRESS) || getFlag(LANDING_IN_PROGRESS))
  1906. {
  1907. // have to wait for takeoff or landing to complete, just store the sucker
  1908. setFlag(HAS_PENDING_COMMAND, true);
  1909. return;
  1910. }
  1911. else if (parms->m_cmd == AICMD_IDLE && getStateMachine()->getCurrentStateID() == RELOAD_AMMO)
  1912. {
  1913. // uber-special-case... if we are told to idle, but are reloading ammo, ignore it for now,
  1914. // since we're already doing "nothing" and responding to this will cease our reload...
  1915. // don't just return, tho, in case we were (say) reloading during a guard stint.
  1916. setFlag(HAS_PENDING_COMMAND, true);
  1917. return;
  1918. }
  1919. else if (!getFlag(ALLOW_AIR_LOCO))
  1920. {
  1921. switch (parms->m_cmd)
  1922. {
  1923. case AICMD_IDLE:
  1924. case AICMD_BUSY:
  1925. case AICMD_FOLLOW_EXITPRODUCTION_PATH:
  1926. // don't need (or want) to take off for these
  1927. break;
  1928. case AICMD_ENTER:
  1929. case AICMD_GET_REPAIRED:
  1930. // if we're already parked at the airfield in question, just ignore.
  1931. if (isParkedAt(parms->m_obj))
  1932. return;
  1933. // else fall thru to the default case!
  1934. default:
  1935. {
  1936. // nuke any existing pending cmd
  1937. m_mostRecentCommand.store(*parms);
  1938. setFlag(HAS_PENDING_COMMAND, true);
  1939. getStateMachine()->clear();
  1940. setLastCommandSource( CMD_FROM_AI );
  1941. getStateMachine()->setState( TAKING_OFF_AWAIT_CLEARANCE );
  1942. return;
  1943. }
  1944. }
  1945. }
  1946. switch (parms->m_cmd)
  1947. {
  1948. case AICMD_GUARD_POSITION:
  1949. case AICMD_GUARD_OBJECT:
  1950. case AICMD_GUARD_AREA:
  1951. case AICMD_HUNT:
  1952. setFlag(ALLOW_INTERRUPT_AND_RESUME_OF_CUR_STATE_FOR_RELOAD, true);
  1953. break;
  1954. default:
  1955. setFlag(ALLOW_INTERRUPT_AND_RESUME_OF_CUR_STATE_FOR_RELOAD, false);
  1956. break;
  1957. }
  1958. setFlag(HAS_PENDING_COMMAND, false);
  1959. AIUpdateInterface::aiDoCommand(parms);
  1960. }
  1961. //-------------------------------------------------------------------------------------------------
  1962. void JetAIUpdate::friend_setAllowAirLoco(Bool allowAirLoco)
  1963. {
  1964. setFlag(ALLOW_AIR_LOCO, allowAirLoco);
  1965. }
  1966. //-------------------------------------------------------------------------------------------------
  1967. void JetAIUpdate::friend_enableAfterburners(Bool v)
  1968. {
  1969. Object* jet = getObject();
  1970. if (v)
  1971. {
  1972. jet->setModelConditionState(MODELCONDITION_JETAFTERBURNER);
  1973. if (!m_afterburnerSound.isCurrentlyPlaying())
  1974. {
  1975. m_afterburnerSound.setObjectID(jet->getID());
  1976. m_afterburnerSound.setPlayingHandle(TheAudio->addAudioEvent(&m_afterburnerSound));
  1977. }
  1978. }
  1979. else
  1980. {
  1981. jet->clearModelConditionState(MODELCONDITION_JETAFTERBURNER);
  1982. if (m_afterburnerSound.isCurrentlyPlaying())
  1983. {
  1984. TheAudio->removeAudioEvent(m_afterburnerSound.getPlayingHandle());
  1985. }
  1986. }
  1987. }
  1988. // ------------------------------------------------------------------------------------------------
  1989. /** CRC */
  1990. // ------------------------------------------------------------------------------------------------
  1991. void JetAIUpdate::crc( Xfer *xfer )
  1992. {
  1993. // extend base class
  1994. AIUpdateInterface::crc(xfer);
  1995. } // end crc
  1996. // ------------------------------------------------------------------------------------------------
  1997. /** Xfer method
  1998. * Version Info:
  1999. * 1: Initial version */
  2000. // ------------------------------------------------------------------------------------------------
  2001. void JetAIUpdate::xfer( Xfer *xfer )
  2002. {
  2003. // version
  2004. XferVersion currentVersion = 2;
  2005. XferVersion version = currentVersion;
  2006. xfer->xferVersion( &version, currentVersion );
  2007. // extend base class
  2008. AIUpdateInterface::xfer(xfer);
  2009. xfer->xferCoord3D(&m_producerLocation);
  2010. m_mostRecentCommand.doXfer(xfer);
  2011. xfer->xferUnsignedInt(&m_attackLocoExpireFrame);
  2012. xfer->xferUnsignedInt(&m_attackersMissExpireFrame);
  2013. xfer->xferUnsignedInt(&m_returnToBaseFrame);
  2014. xfer->xferSTLObjectIDList(&m_targetedBy);
  2015. xfer->xferUnsignedInt(&m_untargetableExpireFrame);
  2016. // Set on create.
  2017. //AudioEventRTS m_afterburnerSound; ///< Sound when afterburners on
  2018. AsciiString drawName;
  2019. if (m_lockonDrawable) {
  2020. drawName = m_lockonDrawable->getTemplate()->getName();
  2021. }
  2022. xfer->xferAsciiString(&drawName);
  2023. if (drawName.isNotEmpty() && m_lockonDrawable==NULL)
  2024. {
  2025. const ThingTemplate* tt = TheThingFactory->findTemplate(drawName);
  2026. if (tt)
  2027. {
  2028. m_lockonDrawable = TheThingFactory->newDrawable(tt);
  2029. }
  2030. }
  2031. xfer->xferInt(&m_flags);
  2032. if( version >= 2 )
  2033. {
  2034. xfer->xferBool( &m_enginesOn );
  2035. }
  2036. else
  2037. {
  2038. //We don't have to be accurate -- this is a patch.
  2039. if( getFlag(TAKEOFF_IN_PROGRESS) || getFlag(LANDING_IN_PROGRESS) || getObject()->isSignificantlyAboveTerrain() || getObject()->isKindOf( KINDOF_PRODUCED_AT_HELIPAD ) )
  2040. {
  2041. m_enginesOn = TRUE;
  2042. }
  2043. else
  2044. {
  2045. m_enginesOn = FALSE;
  2046. }
  2047. }
  2048. } // end xfer
  2049. // ------------------------------------------------------------------------------------------------
  2050. /** Load post process */
  2051. // ------------------------------------------------------------------------------------------------
  2052. void JetAIUpdate::loadPostProcess( void )
  2053. {
  2054. //When drawables are created, so are their ambient sounds. After loading, only turn off the
  2055. //ambient sound if the engine is off.
  2056. if( !m_enginesOn )
  2057. {
  2058. Drawable *draw = getObject()->getDrawable();
  2059. if( draw )
  2060. {
  2061. draw->stopAmbientSound();
  2062. }
  2063. }
  2064. // extend base class
  2065. AIUpdateInterface::loadPostProcess();
  2066. } // end loadPostProcess