JetAIUpdate.cpp 80 KB

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