AIUpdate.cpp 170 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304
  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. // AIUpdate.cpp //
  24. // Implementation of generic AI mechanisms
  25. // Author: Michael S. Booth, 2001-2002
  26. // Subsequently : John Ahlquist 2002 and a cast of thousands.
  27. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  28. #define DEFINE_LOCOMOTORSET_NAMES // for TheLocomotorSetNames[]
  29. #define DEFINE_AUTOACQUIRE_NAMES
  30. #include "Common/ActionManager.h"
  31. #include "Common/GameState.h"
  32. #include "Common/CRCDebug.h"
  33. #include "Common/GlobalData.h"
  34. #include "Common/Player.h"
  35. #include "Common/PlayerList.h"
  36. #include "Common/RandomValue.h"
  37. #include "Common/Team.h"
  38. #include "Common/ThingFactory.h"
  39. #include "Common/ThingTemplate.h"
  40. #include "Common/Upgrade.h"
  41. #include "Common/PerfTimer.h"
  42. #include "Common/UnitTimings.h"
  43. #include "Common/Xfer.h"
  44. #include "Common/XferCRC.h"
  45. #include "GameClient/ControlBar.h"
  46. #include "GameClient/Drawable.h"
  47. #include "GameClient/InGameUI.h" // useful for printing quick debug strings when we need to
  48. #include "GameLogic/AI.h"
  49. #include "GameLogic/AIPathfind.h"
  50. #include "GameLogic/Locomotor.h"
  51. #include "GameLogic/Module/AIUpdate.h"
  52. #include "GameLogic/Module/BodyModule.h"
  53. #include "GameLogic/Module/ContainModule.h"
  54. #include "GameLogic/Module/PhysicsUpdate.h"
  55. #include "GameLogic/Module/ProneUpdate.h"
  56. #include "GameLogic/Module/DeliverPayloadAIUpdate.h"
  57. #include "GameLogic/Module/HackInternetAIUpdate.h"
  58. #include "GameLogic/Module/HordeUpdate.h"
  59. #include "GameLogic/Object.h"
  60. #include "GameLogic/PartitionManager.h"
  61. #include "GameLogic/PolygonTrigger.h"
  62. #include "GameLogic/ScriptEngine.h"
  63. #include "GameLogic/TurretAI.h"
  64. #include "GameLogic/Weapon.h"
  65. #include "Common/Radar.h" // For TheRadar
  66. #define SLEEPY_AI
  67. #ifdef _INTERNAL
  68. // for occasional debugging...
  69. //#pragma optimize("", off)
  70. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  71. #endif
  72. //-------------------------------------------------------------------------------------------------
  73. AIUpdateModuleData::AIUpdateModuleData()
  74. {
  75. //m_locomotorTemplates -- nothing to do
  76. for (int i = 0; i < MAX_TURRETS; i++)
  77. m_turretData[i] = NULL;
  78. m_autoAcquireEnemiesWhenIdle = 0;
  79. m_moodAttackCheckRate = LOGICFRAMES_PER_SECOND * 2;
  80. #ifdef ALLOW_SURRENDER
  81. m_surrenderDuration = LOGICFRAMES_PER_SECOND * 120;
  82. #endif
  83. m_forbidPlayerCommands = FALSE;
  84. m_turretsLinked = FALSE;
  85. }
  86. //-------------------------------------------------------------------------------------------------
  87. AIUpdateModuleData::~AIUpdateModuleData()
  88. {
  89. for (int i = 0; i < MAX_TURRETS; i++)
  90. {
  91. if (m_turretData[i])
  92. {
  93. TurretAIData* td = const_cast<TurretAIData*>(m_turretData[i]);
  94. if (td)
  95. td->deleteInstance();
  96. }
  97. }
  98. }
  99. //-------------------------------------------------------------------------------------------------
  100. const LocomotorTemplateVector* AIUpdateModuleData::findLocomotorTemplateVector(LocomotorSetType t) const
  101. {
  102. if (m_locomotorTemplates.empty())
  103. return NULL;
  104. LocomotorTemplateMap::const_iterator it = m_locomotorTemplates.find(t);
  105. if (it == m_locomotorTemplates.end())
  106. {
  107. return NULL;
  108. }
  109. else
  110. {
  111. return &(*it).second;
  112. }
  113. }
  114. //-------------------------------------------------------------------------------------------------
  115. /*static*/ void AIUpdateModuleData::buildFieldParse(MultiIniFieldParse& p)
  116. {
  117. ModuleData::buildFieldParse(p);
  118. static const FieldParse dataFieldParse[] =
  119. {
  120. { "Turret", AIUpdateModuleData::parseTurret, NULL, offsetof(AIUpdateModuleData, m_turretData[0]) },
  121. { "AltTurret", AIUpdateModuleData::parseTurret, NULL, offsetof(AIUpdateModuleData, m_turretData[1]) },
  122. { "AutoAcquireEnemiesWhenIdle", INI::parseBitString32, TheAutoAcquireEnemiesNames, offsetof(AIUpdateModuleData, m_autoAcquireEnemiesWhenIdle) },
  123. { "MoodAttackCheckRate", INI::parseDurationUnsignedInt, NULL, offsetof(AIUpdateModuleData, m_moodAttackCheckRate) },
  124. #ifdef ALLOW_SURRENDER
  125. { "SurrenderDuration", INI::parseDurationUnsignedInt, NULL, offsetof(AIUpdateModuleData, m_surrenderDuration) },
  126. #endif
  127. { "ForbidPlayerCommands", INI::parseBool, NULL, offsetof(AIUpdateModuleData, m_forbidPlayerCommands) },
  128. { "TurretsLinked", INI::parseBool, NULL, offsetof( AIUpdateModuleData, m_turretsLinked ) },
  129. { 0, 0, 0, 0 }
  130. };
  131. p.add(dataFieldParse);
  132. }
  133. //-------------------------------------------------------------------------------------------------
  134. /*static*/ void AIUpdateModuleData::parseTurret(INI* ini, void *instance, void * store, const void* /*userData*/)
  135. {
  136. if (*(TurretAIData**)store)
  137. {
  138. DEBUG_CRASH(("Only one turret to a customer, for now"));
  139. throw INI_INVALID_DATA;
  140. }
  141. TurretAIData* td = newInstance(TurretAIData);
  142. ini->initFromINIMultiProc(td, td->buildFieldParse);
  143. *(TurretAIData**)store = td;
  144. }
  145. //-------------------------------------------------------------------------------------------------
  146. /*static*/ void AIUpdateModuleData::parseLocomotorSet(INI* ini, void *instance, void * /*store*/, const void* /*userData*/)
  147. {
  148. ThingTemplate *tt = (ThingTemplate *)instance;
  149. AIUpdateModuleData *self = tt->friend_getAIModuleInfo();
  150. if (!self)
  151. {
  152. DEBUG_CRASH( ("Attempted to specify a locomotor for object %s without an AIUpdate block.", tt->getName().str() ) );
  153. throw INI_INVALID_DATA;
  154. }
  155. LocomotorSetType set = (LocomotorSetType)INI::scanIndexList(ini->getNextToken(), TheLocomotorSetNames);
  156. if (!self->m_locomotorTemplates[set].empty())
  157. {
  158. if (ini->getLoadType() != INI_LOAD_CREATE_OVERRIDES)
  159. {
  160. DEBUG_CRASH(("re-specifying a LocomotorSet is no longer allowed\n"));
  161. throw INI_INVALID_DATA;
  162. }
  163. }
  164. self->m_locomotorTemplates[set].clear();
  165. for (const char* locoName = ini->getNextToken(); locoName; locoName = ini->getNextTokenOrNull())
  166. {
  167. if (!*locoName || !stricmp(locoName, "None"))
  168. continue;
  169. NameKeyType locoKey = NAMEKEY(locoName);
  170. const LocomotorTemplate* lt = TheLocomotorStore->findLocomotorTemplate(locoKey);
  171. if (!lt)
  172. {
  173. DEBUG_CRASH(("Locomotor %s not found!\n",locoName));
  174. throw INI_INVALID_DATA;
  175. }
  176. self->m_locomotorTemplates[set].push_back(lt);
  177. }
  178. }
  179. //-------------------------------------------------------------------------------------------------
  180. // subclasses may want to override this, to use a subclass of AIStateMachine.
  181. AIStateMachine* AIUpdateInterface::makeStateMachine()
  182. {
  183. return newInstance(AIStateMachine)( getObject(), "AIUpdateInterfaceMachine");
  184. }
  185. //-------------------------------------------------------------------------------------------------
  186. //-------------------------------------------------------------------------------------------------
  187. //-------------------------------------------------------------------------------------------------
  188. AIUpdateInterface::AIUpdateInterface( Thing *thing, const ModuleData* moduleData ) :
  189. UpdateModule( thing, moduleData )
  190. {
  191. int i;
  192. m_priorWaypointID = 0xfacade;
  193. m_currentWaypointID = 0xfacade;
  194. m_stateMachine = NULL;
  195. m_nextEnemyScanTime = 0;
  196. m_currentVictimID = INVALID_ID;
  197. m_desiredSpeed = FAST_AS_POSSIBLE;
  198. m_lastCommandSource = CMD_FROM_AI;
  199. m_guardMode = GUARDMODE_NORMAL;
  200. m_guardTargetType[0] = m_guardTargetType[1] = GUARDTARGET_NONE;
  201. m_locationToGuard.zero();
  202. m_objectToGuard = INVALID_ID;
  203. m_areaToGuard = NULL;
  204. m_attackInfo = NULL;
  205. m_waypointCount = 0;
  206. m_waypointIndex = 0;
  207. m_completedWaypoint = NULL;
  208. m_path = NULL;
  209. m_requestedVictimID = INVALID_ID;
  210. m_requestedDestination.zero();
  211. m_requestedDestination2.zero();
  212. m_pathTimestamp = 0;
  213. m_ignoreObstacleID = INVALID_ID;
  214. m_pathExtraDistance = 0;
  215. m_pathfindGoalCell.x = m_pathfindGoalCell.y = -1;
  216. m_pathfindCurCell.x = m_pathfindCurCell.y = -1;
  217. m_blockedFrames = 0;
  218. m_curMaxBlockedSpeed = 0;
  219. m_bumpSpeedLimit = FAST_AS_POSSIBLE;
  220. m_ignoreCollisionsUntil = 0;
  221. m_queueForPathFrame = 0;
  222. m_finalPosition.zero();
  223. m_repulsor1 = INVALID_ID;
  224. m_repulsor2 = INVALID_ID;
  225. m_nextGoalPathIndex = -1;
  226. m_moveOutOfWay1 = INVALID_ID;
  227. m_moveOutOfWay2 = INVALID_ID;
  228. m_locomotorSet.clear();
  229. m_curLocomotor = NULL;
  230. m_curLocomotorSet = LOCOMOTORSET_INVALID;
  231. m_locomotorGoalType = NONE;
  232. m_locomotorGoalData.zero();
  233. for (i = 0; i < MAX_TURRETS; i++)
  234. m_turretAI[i] = NULL;
  235. m_turretSyncFlag = TURRET_INVALID;
  236. m_attitude = AI_NORMAL;
  237. m_nextMoodCheckTime = 0;
  238. #ifdef ALLOW_DEMORALIZE
  239. m_demoralizedFramesLeft = 0;
  240. #endif
  241. #ifdef ALLOW_SURRENDER
  242. m_surrenderedFramesLeft = 0;
  243. m_surrenderedPlayerIndex = -1;
  244. #endif
  245. m_crateCreated = INVALID_ID;
  246. m_tmpInt = 0;
  247. m_doFinalPosition = FALSE;
  248. m_waitingForPath = FALSE;
  249. m_isAttackPath = FALSE;
  250. m_isFinalGoal = FALSE;
  251. m_isApproachPath = FALSE;
  252. m_isSafePath = FALSE;
  253. m_movementComplete = FALSE;
  254. m_isMoving = FALSE;
  255. m_isBlocked = FALSE;
  256. m_isBlockedAndStuck = FALSE;
  257. m_upgradedLocomotors = FALSE;
  258. m_canPathThroughUnits = FALSE;
  259. m_randomlyOffsetMoodCheck = FALSE;
  260. m_isAiDead = FALSE;
  261. m_isRecruitable = TRUE; // Things default to being recruitable.
  262. m_executingWaypointQueue = FALSE;
  263. m_retryPath = FALSE;
  264. m_isInUpdate = FALSE;
  265. m_fixLocoInPostProcess = FALSE;
  266. // ---------------------------------------------
  267. for (i = 0; i < MAX_TURRETS; i++)
  268. {
  269. if (getAIUpdateModuleData()->m_turretData[i])
  270. {
  271. m_turretAI[i] = newInstance(TurretAI)(getObject(), getAIUpdateModuleData()->m_turretData[i], (WhichTurretType)i);
  272. }
  273. }
  274. chooseLocomotorSet(LOCOMOTORSET_NORMAL);
  275. #ifdef SLEEPY_AI
  276. setWakeFrame(getObject(), UPDATE_SLEEP_NONE);
  277. #endif
  278. }
  279. #ifdef ALLOW_SURRENDER
  280. //=============================================================================
  281. // Object::setSurrendered, and related methods ================================
  282. //=============================================================================
  283. void AIUpdateInterface::setSurrendered( const Object *objWeSurrenderedTo, Bool surrendered )
  284. {
  285. if (surrendered)
  286. {
  287. Bool wasSurrendered = isSurrendered();
  288. const AIUpdateModuleData* d = getAIUpdateModuleData();
  289. if (m_surrenderedFramesLeft < d->m_surrenderDuration)
  290. m_surrenderedFramesLeft = d->m_surrenderDuration;
  291. const Player* playerWeSurrenderedTo = objWeSurrenderedTo ? objWeSurrenderedTo->getControllingPlayer() : NULL;
  292. m_surrenderedPlayerIndex = playerWeSurrenderedTo ? playerWeSurrenderedTo->getPlayerIndex() : -1;
  293. if (!wasSurrendered)
  294. {
  295. //aiIdle(CMD_FROM_AI);
  296. // srj sez: calling aiIdle() won't work, since we are probably "effectivelyDead"...
  297. // meaning we won't respong to aiDoCommand! so go straight to the metal here:
  298. getStateMachine()->clear();
  299. getStateMachine()->setState( AI_IDLE );
  300. setLastCommandSource(CMD_FROM_AI);
  301. // Play our sound surrendered
  302. AudioEventRTS surrenderSound = *getObject()->getTemplate()->getVoiceSurrender();
  303. surrenderSound.setObjectID(getObject()->getID());
  304. TheAudio->addAudioEvent(&surrenderSound);
  305. }
  306. }
  307. else
  308. {
  309. // GS During the act of surrendering, we dipped to 0 and then were manually set to have hit points.
  310. // That made us alive but marked as Dead. Gotta undo that.
  311. getObject()->setEffectivelyDead( FALSE );
  312. m_surrenderedFramesLeft = 0;
  313. m_surrenderedPlayerIndex = -1;
  314. }
  315. }
  316. #endif
  317. //=============================================================================
  318. void AIUpdateInterface::setGoalPositionClipped(const Coord3D* in, CommandSourceType cmdSource)
  319. {
  320. if (in)
  321. {
  322. Coord3D tmp = *in;
  323. if (cmdSource == CMD_FROM_PLAYER)
  324. {
  325. Real fudge = TheGlobalData->m_partitionCellSize * 0.5f;
  326. if (getObject()->isKindOf(KINDOF_AIRCRAFT) && getObject()->isSignificantlyAboveTerrain() && m_curLocomotor != NULL)
  327. {
  328. // aircraft must stay further away from the map edges, to prevent getting "lost"
  329. fudge = max(fudge, m_curLocomotor->getPreferredHeight());
  330. }
  331. Region3D mapRegion;
  332. TheTerrainLogic->getExtent( &mapRegion );
  333. if (tmp.x < mapRegion.lo.x + fudge)
  334. {
  335. tmp.x = mapRegion.lo.x + fudge;
  336. }
  337. if (tmp.x > mapRegion.hi.x - fudge)
  338. {
  339. tmp.x = mapRegion.hi.x - fudge;
  340. }
  341. if (tmp.y < mapRegion.lo.y + fudge)
  342. {
  343. tmp.y = mapRegion.lo.y + fudge;
  344. }
  345. if (tmp.y > mapRegion.hi.y - fudge)
  346. {
  347. tmp.y = mapRegion.hi.y - fudge;
  348. }
  349. }
  350. getStateMachine()->setGoalPosition(&tmp);
  351. }
  352. else
  353. {
  354. getStateMachine()->setGoalPosition(NULL);
  355. }
  356. }
  357. /* Called by the pathfinder when it processes the pathfind queue. Basically, it's our turn
  358. to call use the PathfindServicesInterface to do a pathfind operation. This shouldn't be called
  359. (and in fact is very hard to do because PathfindServicesInterace is private to the pathfinder)
  360. except by the pathfinder during pathfind queue processing. jba */
  361. //-------------------------------------------------------------------------------------------------
  362. void AIUpdateInterface::doPathfind( PathfindServicesInterface *pathfinder )
  363. {
  364. if (!m_waitingForPath) {
  365. return;
  366. }
  367. //CRCDEBUG_LOG(("AIUpdateInterface::doPathfind() for object %d\n", getObject()->getID()));
  368. m_waitingForPath = FALSE;
  369. if (m_isSafePath) {
  370. destroyPath();
  371. Coord3D pos1, pos2;
  372. pos1.set(-1000,-1000,0);
  373. Object *repulsor = TheGameLogic->findObjectByID(m_repulsor1);
  374. if (repulsor) {
  375. pos1 = *repulsor->getPosition();
  376. }
  377. pos2 = pos1;
  378. repulsor = TheGameLogic->findObjectByID(m_repulsor2);
  379. if (repulsor) {
  380. pos2 = *repulsor->getPosition();
  381. }
  382. m_path = pathfinder->findSafePath(getObject(), m_locomotorSet,
  383. getObject()->getPosition(),
  384. &pos1, &pos2,
  385. getObject()->getVisionRange() + TheAI->getAiData()->m_repulsedDistance);
  386. return;
  387. }
  388. if (m_isApproachPath & !isDoingGroundMovement()) {
  389. m_isApproachPath = false;
  390. }
  391. if (m_isApproachPath) {
  392. destroyPath();
  393. m_path = pathfinder->findClosestPath(getObject(), m_locomotorSet, getObject()->getPosition(),
  394. &m_requestedDestination, m_isBlockedAndStuck, 0.2f, FALSE );
  395. if (isDoingGroundMovement() && getPath()) {
  396. TheAI->pathfinder()->updateGoal(getObject(), getPath()->getLastNode()->getPosition(),
  397. getPath()->getLastNode()->getLayer());
  398. }
  399. return;
  400. }
  401. if (m_isAttackPath) {
  402. Object *victim = NULL;
  403. if (m_requestedVictimID != INVALID_ID) {
  404. victim = TheGameLogic->findObjectByID(m_requestedVictimID);
  405. }
  406. if (computeAttackPath(pathfinder, victim, &m_requestedDestination)) {
  407. if (getPath()) {
  408. TheAI->pathfinder()->updateGoal(getObject(), getPath()->getLastNode()->getPosition(),
  409. getPath()->getLastNode()->getLayer());
  410. }
  411. //CRCDEBUG_LOG(("AIUpdateInterface::doPathfind() - m_isAttackPath = TRUE after computeAttackPath\n"));
  412. m_isAttackPath = TRUE;
  413. return;
  414. }
  415. //CRCDEBUG_LOG(("AIUpdateInterface::doPathfind() - m_isAttackPath = FALSE after computeAttackPath()\n"));
  416. m_isAttackPath = FALSE;
  417. if (victim) {
  418. m_requestedDestination = *victim->getPosition();
  419. /* find a pathable destination near the victim.*/
  420. TheAI->pathfinder()->adjustToPossibleDestination(getObject(), getLocomotorSet(), &m_requestedDestination);
  421. ignoreObstacle(victim);
  422. }
  423. }
  424. computePath(pathfinder, &m_requestedDestination);
  425. if (m_isFinalGoal && isDoingGroundMovement() && getPath()) {
  426. TheAI->pathfinder()->updateGoal(getObject(), getPath()->getLastNode()->getPosition(),
  427. getPath()->getLastNode()->getLayer());
  428. }
  429. if (m_queueForPathFrame > TheGameLogic->getFrame()) {
  430. m_waitingForPath = TRUE;
  431. }
  432. #ifdef SLEEPY_AI
  433. // if we're no longer waiting for a path, make sure we wake up right away!
  434. if (!m_waitingForPath)
  435. {
  436. wakeUpNow();
  437. }
  438. #endif
  439. }
  440. /* Requests a path to be found. Note that if it is possible to do it without having to use the
  441. pathfinder (air units just move point to point) it generates the path immediately. Otherwise the path
  442. will be processed when we get to the front of the pathfind queue. jba */
  443. //-------------------------------------------------------------------------------------------------
  444. void AIUpdateInterface::requestPath( Coord3D *destination, Bool isFinalGoal )
  445. {
  446. if (m_locomotorSet.getValidSurfaces() == 0) {
  447. DEBUG_CRASH(("Attempting to path immobile unit."));
  448. }
  449. //DEBUG_LOG(("Request Frame %d, obj %s %x\n", TheGameLogic->getFrame(), getObject()->getTemplate()->getName().str(), getObject()));
  450. m_requestedDestination = *destination;
  451. m_isFinalGoal = isFinalGoal;
  452. CRCDEBUG_LOG(("AIUpdateInterface::requestPath() - m_isAttackPath = FALSE for object %d\n", getObject()->getID()));
  453. m_isAttackPath = FALSE;
  454. m_requestedVictimID = INVALID_ID;
  455. m_isApproachPath = FALSE;
  456. m_isSafePath = FALSE;
  457. if (canComputeQuickPath()) {
  458. computeQuickPath(destination);
  459. return;
  460. }
  461. m_waitingForPath = TRUE;
  462. if (m_pathTimestamp > TheGameLogic->getFrame()-3) {
  463. /* Requesting path very quickly. Can cause a spin. */
  464. //DEBUG_LOG(("%d Pathfind - repathing in less than 3 frames. Waiting 1 second\n",
  465. //TheGameLogic->getFrame()));
  466. setQueueForPathTime(LOGICFRAMES_PER_SECOND);
  467. // See if it has been too soon.
  468. // jba intense debug
  469. //DEBUG_LOG(("Info - RePathing very quickly %d, %d.\n", m_pathTimestamp, TheGameLogic->getFrame()));
  470. if (m_path && m_isBlockedAndStuck) {
  471. setIgnoreCollisionTime(2*LOGICFRAMES_PER_SECOND);
  472. m_blockedFrames = 0;
  473. m_isBlocked = FALSE;
  474. m_isBlockedAndStuck = FALSE;
  475. }
  476. return;
  477. }
  478. TheAI->pathfinder()->queueForPath(getObject()->getID());
  479. }
  480. //-------------------------------------------------------------------------------------------------
  481. void AIUpdateInterface::requestAttackPath( ObjectID victimID, const Coord3D* victimPos )
  482. {
  483. if (m_locomotorSet.getValidSurfaces() == 0) {
  484. DEBUG_CRASH(("Attempting to path immobile unit."));
  485. }
  486. CRCDEBUG_LOG(("AIUpdateInterface::requestAttackPath() - m_isAttackPath = TRUE for object %d\n", getObject()->getID()));
  487. m_requestedDestination = *victimPos;
  488. m_requestedVictimID = victimID;
  489. m_isAttackPath = TRUE;
  490. m_isApproachPath = FALSE;
  491. m_isSafePath = FALSE;
  492. m_waitingForPath = TRUE;
  493. if (m_pathTimestamp > TheGameLogic->getFrame()-3) {
  494. /* Requesting path very quickly. Can cause a spin. */
  495. //DEBUG_LOG(("%d Pathfind - repathing in less than 3 frames. Waiting 2 second\n",TheGameLogic->getFrame()));
  496. setQueueForPathTime(2*LOGICFRAMES_PER_SECOND);
  497. setLocomotorGoalNone();
  498. return;
  499. }
  500. TheAI->pathfinder()->queueForPath(getObject()->getID());
  501. }
  502. //-------------------------------------------------------------------------------------------------
  503. void AIUpdateInterface::requestApproachPath( Coord3D *destination )
  504. {
  505. if (m_locomotorSet.getValidSurfaces() == 0) {
  506. DEBUG_CRASH(("Attempting to path immobile unit."));
  507. }
  508. m_requestedDestination = *destination;
  509. m_isFinalGoal = TRUE;
  510. CRCDEBUG_LOG(("AIUpdateInterface::requestApproachPath() - m_isAttackPath = FALSE for object %d\n", getObject()->getID()));
  511. m_isAttackPath = FALSE;
  512. m_requestedVictimID = INVALID_ID;
  513. m_isApproachPath = TRUE;
  514. m_isSafePath = FALSE;
  515. m_waitingForPath = TRUE;
  516. if (m_pathTimestamp > TheGameLogic->getFrame()-3) {
  517. /* Requesting path very quickly. Can cause a spin. */
  518. //DEBUG_LOG(("%d Pathfind - repathing in less than 3 frames. Waiting 2 second\n",TheGameLogic->getFrame()));
  519. setQueueForPathTime(2*LOGICFRAMES_PER_SECOND);
  520. return;
  521. }
  522. TheAI->pathfinder()->queueForPath(getObject()->getID());
  523. }
  524. //-------------------------------------------------------------------------------------------------
  525. // Requests a safe path away from the repulsor.
  526. void AIUpdateInterface::requestSafePath( ObjectID repulsor )
  527. {
  528. if (repulsor != m_repulsor1) {
  529. m_repulsor2 = m_repulsor1; // save the prior repulsor.
  530. }
  531. m_repulsor1 = repulsor;
  532. m_isFinalGoal = FALSE;
  533. CRCDEBUG_LOG(("AIUpdateInterface::requestSafePath() - m_isAttackPath = FALSE for object %d\n", getObject()->getID()));
  534. m_isAttackPath = FALSE;
  535. m_requestedVictimID = INVALID_ID;
  536. m_isApproachPath = FALSE;
  537. m_isSafePath = TRUE;
  538. m_waitingForPath = TRUE;
  539. if (m_pathTimestamp > TheGameLogic->getFrame()-3) {
  540. /* Requesting path very quickly. Can cause a spin. */
  541. //DEBUG_LOG(("%d Pathfind - repathing in less than 3 frames. Waiting 2 second\n",TheGameLogic->getFrame()));
  542. setQueueForPathTime(2*LOGICFRAMES_PER_SECOND);
  543. return;
  544. }
  545. TheAI->pathfinder()->queueForPath(getObject()->getID());
  546. }
  547. enum {WAYPOINT_PATH_LIMIT=1024};
  548. //-------------------------------------------------------------------------------------------------
  549. //
  550. void AIUpdateInterface::setPathFromWaypoint(const Waypoint *way, const Coord2D *offset)
  551. {
  552. destroyPath();
  553. m_path = newInstance(Path);
  554. Coord3D pos = *getObject()->getPosition();
  555. m_path->prependNode( &pos, LAYER_GROUND );
  556. m_path->markOptimized();
  557. int count = 0;
  558. while (way) {
  559. Coord3D wayPos = *way->getLocation();
  560. wayPos.x += offset->x;
  561. wayPos.y += offset->y;
  562. if (way->getLink(0) == NULL) {
  563. TheAI->pathfinder()->snapPosition(getObject(), &wayPos);
  564. }
  565. m_path->appendNode( &wayPos, LAYER_GROUND );
  566. way = way->getLink(0);
  567. count++;
  568. if (count>WAYPOINT_PATH_LIMIT) break;
  569. }
  570. m_waitingForPath = FALSE;
  571. TheAI->pathfinder()->setDebugPath(m_path);
  572. #ifdef SLEEPY_AI
  573. // if we're no longer waiting for a path, make sure we wake up right away!
  574. wakeUpNow();
  575. #endif
  576. }
  577. //-------------------------------------------------------------------------------------------------
  578. void AIUpdateInterface::onObjectCreated()
  579. {
  580. // create the behavior state machine.
  581. // can't do this in the ctor because makeStateMachine is a protected virtual func,
  582. // and overrides to virtual funcs don't exist in our ctor. (look it up.)
  583. if (m_stateMachine == NULL)
  584. {
  585. m_stateMachine = makeStateMachine();
  586. m_stateMachine->initDefaultState();
  587. }
  588. }
  589. //-------------------------------------------------------------------------------------------------
  590. AIUpdateInterface::~AIUpdateInterface( void )
  591. {
  592. m_locomotorSet.clear();
  593. m_curLocomotor = NULL;
  594. if( m_stateMachine ) {
  595. m_stateMachine->halt();
  596. m_stateMachine->deleteInstance();
  597. }
  598. for (int i = 0; i < MAX_TURRETS; i++)
  599. {
  600. if (m_turretAI[i])
  601. m_turretAI[i]->deleteInstance();
  602. m_turretAI[i] = NULL;
  603. }
  604. m_stateMachine = NULL;
  605. // destroy the current path. (destroyPath is NULL savvy)
  606. destroyPath();
  607. }
  608. //=============================================================================
  609. void AIUpdateInterface::setTurretTargetObject(WhichTurretType tur, Object* o, Bool forceAttacking)
  610. {
  611. if (m_turretAI[tur])
  612. {
  613. m_turretAI[tur]->setTurretTargetObject(o, forceAttacking);
  614. }
  615. }
  616. //=============================================================================
  617. Object* AIUpdateInterface::getTurretTargetObject( WhichTurretType tur, Bool clearDeadTargets )
  618. {
  619. if( m_turretAI[ tur ] )
  620. {
  621. Object *obj;
  622. Coord3D pos;
  623. if( m_turretAI[ tur ]->friend_getTurretTarget( obj, pos, clearDeadTargets ) == TARGET_OBJECT )
  624. {
  625. return obj;
  626. }
  627. }
  628. return NULL;
  629. }
  630. //=============================================================================
  631. void AIUpdateInterface::setTurretTargetPosition(WhichTurretType tur, const Coord3D* pos)
  632. {
  633. if (m_turretAI[tur])
  634. {
  635. m_turretAI[tur]->setTurretTargetPosition(pos);
  636. }
  637. }
  638. //=============================================================================
  639. void AIUpdateInterface::setTurretEnabled(WhichTurretType tur, Bool enabled)
  640. {
  641. if (m_turretAI[tur])
  642. {
  643. m_turretAI[tur]->setTurretEnabled( enabled );
  644. }
  645. }
  646. //=============================================================================
  647. void AIUpdateInterface::recenterTurret(WhichTurretType tur)
  648. {
  649. if (m_turretAI[tur])
  650. {
  651. m_turretAI[tur]->recenterTurret();
  652. }
  653. }
  654. //=============================================================================
  655. Bool AIUpdateInterface::isTurretEnabled( WhichTurretType tur ) const
  656. {
  657. if( m_turretAI[ tur ] )
  658. {
  659. return m_turretAI[ tur ]->isTurretEnabled();
  660. }
  661. return FALSE;
  662. }
  663. //=============================================================================
  664. Bool AIUpdateInterface::isTurretInNaturalPosition(WhichTurretType tur) const
  665. {
  666. if (m_turretAI[tur])
  667. {
  668. return m_turretAI[tur]->isTurretInNaturalPosition();
  669. }
  670. return FALSE;
  671. }
  672. //=============================================================================
  673. Bool AIUpdateInterface::isWeaponSlotOnTurretAndAimingAtTarget(WeaponSlotType wslot, const Object* victim) const
  674. {
  675. for (int i = 0; i < MAX_TURRETS; i++)
  676. {
  677. if (m_turretAI[i] && m_turretAI[i]->isWeaponSlotOnTurret(wslot))
  678. {
  679. return m_turretAI[i]->isTryingToAimAtTarget(victim);
  680. }
  681. }
  682. return FALSE;
  683. }
  684. //=============================================================================
  685. Bool AIUpdateInterface::getTurretRotAndPitch(WhichTurretType tur, Real* turretAngle, Real* turretPitch) const
  686. {
  687. if (m_turretAI[tur])
  688. {
  689. if (turretAngle)
  690. *turretAngle = m_turretAI[tur]->getTurretAngle();
  691. if (turretPitch)
  692. *turretPitch = m_turretAI[tur]->getTurretPitch();
  693. return TRUE;
  694. }
  695. return FALSE;
  696. }
  697. //=============================================================================
  698. Real AIUpdateInterface::getTurretTurnRate(WhichTurretType tur) const
  699. {
  700. return (tur != TURRET_INVALID && m_turretAI[tur] != NULL) ?
  701. m_turretAI[tur]->getTurnRate() :
  702. 0.0f;
  703. }
  704. //=============================================================================
  705. WhichTurretType AIUpdateInterface::getWhichTurretForCurWeapon() const
  706. {
  707. for (int i = 0; i < MAX_TURRETS; ++i)
  708. if (m_turretAI[i] && m_turretAI[i]->isOwnersCurWeaponOnTurret())
  709. return (WhichTurretType)i;
  710. return TURRET_INVALID;
  711. }
  712. //=============================================================================
  713. WhichTurretType AIUpdateInterface::getWhichTurretForWeaponSlot(WeaponSlotType wslot, Real* turretAngle, Real* turretPitch) const
  714. {
  715. for (int i = 0; i < MAX_TURRETS; ++i)
  716. {
  717. if (m_turretAI[i] && m_turretAI[i]->isWeaponSlotOnTurret(wslot))
  718. {
  719. if (turretAngle)
  720. *turretAngle = m_turretAI[i]->getTurretAngle();
  721. if (turretPitch)
  722. *turretPitch = m_turretAI[i]->getTurretPitch();
  723. return (WhichTurretType)i;
  724. }
  725. }
  726. return TURRET_INVALID;
  727. }
  728. //=============================================================================
  729. Real AIUpdateInterface::getCurLocomotorSpeed() const
  730. {
  731. if (m_curLocomotor != NULL)
  732. return m_curLocomotor->getMaxSpeedForCondition(getObject()->getBodyModule()->getDamageState());
  733. DEBUG_LOG(("no current locomotor!"));
  734. return 0.0f;
  735. }
  736. //=============================================================================
  737. void AIUpdateInterface::setLocomotorUpgrade(Bool set)
  738. {
  739. m_upgradedLocomotors = set;
  740. if (m_curLocomotorSet == LOCOMOTORSET_NORMAL || m_curLocomotorSet == LOCOMOTORSET_NORMAL_UPGRADED)
  741. chooseLocomotorSet(LOCOMOTORSET_NORMAL);
  742. }
  743. //=============================================================================
  744. Bool AIUpdateInterface::chooseLocomotorSet(LocomotorSetType wst)
  745. {
  746. DEBUG_ASSERTCRASH(wst != LOCOMOTORSET_NORMAL_UPGRADED, ("never pass LOCOMOTORSET_NORMAL_UPGRADED here"));
  747. if (wst == LOCOMOTORSET_NORMAL && m_upgradedLocomotors)
  748. wst = LOCOMOTORSET_NORMAL_UPGRADED;
  749. if (wst == m_curLocomotorSet)
  750. return TRUE;
  751. if (chooseLocomotorSetExplicit(wst))
  752. {
  753. chooseGoodLocomotorFromCurrentSet();
  754. return TRUE;
  755. }
  756. return FALSE;
  757. }
  758. //=============================================================================
  759. // this should only be called by load/save, or by chooseLocomotorSet.
  760. // it does no sanity checking; it just jams it in.
  761. Bool AIUpdateInterface::chooseLocomotorSetExplicit(LocomotorSetType wst)
  762. {
  763. const LocomotorTemplateVector* set = getAIUpdateModuleData()->findLocomotorTemplateVector(wst);
  764. if (set)
  765. {
  766. m_locomotorSet.clear();
  767. m_curLocomotor = NULL;
  768. for (Int i = 0; i < set->size(); ++i)
  769. {
  770. const LocomotorTemplate* lt = set->at(i);
  771. if (lt)
  772. m_locomotorSet.addLocomotor(lt);
  773. }
  774. m_curLocomotorSet = wst;
  775. return TRUE;
  776. }
  777. return FALSE;
  778. }
  779. //-------------------------------------------------------------------------------------------------
  780. void AIUpdateInterface::chooseGoodLocomotorFromCurrentSet( void )
  781. {
  782. Locomotor* prevLoco = m_curLocomotor;
  783. Locomotor* newLoco = TheAI->pathfinder()->chooseBestLocomotorForPosition(getObject()->getLayer(), &m_locomotorSet, getObject()->getPosition());
  784. if (newLoco == NULL)
  785. {
  786. if (prevLoco != NULL)
  787. {
  788. /* due to physics, we might slight into a cell for which we have no loco
  789. (eg, cliff) and get stuck. this is bad. as a solution, we do this.
  790. this may look a little funny, but as a practical matter, it works well,
  791. since the pathfinder will prevent us from doing any significant "wrong" terrain. */
  792. newLoco = prevLoco;
  793. }
  794. else
  795. {
  796. /* this can happen for a newly-created object, which might come into being in
  797. the middle of an obstacle. for now, we just fake it and choose a ground locomotor. */
  798. newLoco = m_locomotorSet.findLocomotor(LOCOMOTORSURFACE_GROUND);
  799. }
  800. }
  801. m_curLocomotor = newLoco;
  802. if (prevLoco != m_curLocomotor)
  803. {
  804. // make sure the group's speed will be recalculated
  805. if (getGroup())
  806. getGroup()->recomputeGroupSpeed();
  807. // turn off precision-z-pos anytime the loco changes, just in case,
  808. // since it should only be enabled in very special cases
  809. m_curLocomotor->setUsePreciseZPos(FALSE);
  810. // ditto for no-slow-down.
  811. m_curLocomotor->setNoSlowDownAsApproachingDest(FALSE);
  812. // ditto for ultra-accuracy.
  813. m_curLocomotor->setUltraAccurate(FALSE);
  814. }
  815. }
  816. //----------------------------------------------------------------------------------------------------------
  817. Object* AIUpdateInterface::checkForCrateToPickup()
  818. {
  819. if (m_crateCreated != INVALID_ID)
  820. {
  821. m_crateCreated = INVALID_ID; // we have processed it, so clear it.
  822. Object* crate = TheGameLogic->findObjectByID(m_crateCreated);
  823. if (crate)
  824. {
  825. for (BehaviorModule** m = crate->getBehaviorModules(); *m; ++m)
  826. {
  827. CollideModuleInterface* collide = (*m)->getCollide();
  828. if (!collide)
  829. continue;
  830. if( collide->wouldLikeToCollideWith(getObject()))
  831. {
  832. return crate;
  833. }
  834. }
  835. }
  836. }
  837. return NULL;
  838. }
  839. #ifdef ALLOW_SURRENDER
  840. //-------------------------------------------------------------------------------------------------
  841. void AIUpdateInterface::doSurrenderUpdateStuff()
  842. {
  843. RELEASE_CRASH(("Read the comment in doSurrenderUpdateStuff"));
  844. /*
  845. If you ever re-enable this code, you must convert it to be
  846. properly sleepy. It is crucial that we avoid requiring a call
  847. to AIUpdate every frame just to support this. (srj)
  848. */
  849. UnsignedInt prevSurrenderedFrames = m_surrenderedFramesLeft;
  850. if (m_surrenderedFramesLeft > 0)
  851. --m_surrenderedFramesLeft;
  852. if (m_surrenderedFramesLeft > 0)
  853. getObject()->setModelConditionState( MODELCONDITION_SURRENDER );
  854. else
  855. getObject()->clearModelConditionState( MODELCONDITION_SURRENDER );
  856. //
  857. // when we leave a surrendered state we give ourselves an idle command ... why you ask? Well
  858. // during the surrender sequence we might have started moving towards a POW truck come
  859. // to pick us up, but now we should stop and be all normal again
  860. //
  861. if( prevSurrenderedFrames > 0 && m_surrenderedFramesLeft == 0 )
  862. {
  863. m_surrenderedPlayerIndex = -1;
  864. aiIdle( CMD_FROM_AI );
  865. }
  866. }
  867. #endif
  868. //-------------------------------------------------------------------------------------------------
  869. void AIUpdateInterface::setQueueForPathTime(Int frames)
  870. {
  871. #ifdef SLEEPY_AI
  872. if (frames >= UPDATE_SLEEP_NONE && getWakeFrame() > UPDATE_SLEEP(frames))
  873. {
  874. if (m_isInUpdate)
  875. {
  876. // we're changing this while in our own update (probably via a move state).
  877. // just do nothing, since update will calculate the correct sleep behavior at the end.
  878. }
  879. else
  880. {
  881. setWakeFrame(getObject(), UPDATE_SLEEP(frames));
  882. }
  883. }
  884. #endif
  885. m_queueForPathFrame = frames ? (TheGameLogic->getFrame() + frames) : 0;
  886. }
  887. //-------------------------------------------------------------------------------------------------
  888. void AIUpdateInterface::wakeUpNow()
  889. {
  890. #ifdef SLEEPY_AI
  891. if (getWakeFrame() > UPDATE_SLEEP_NONE)
  892. {
  893. if (m_isInUpdate)
  894. {
  895. // we're changing this while in our own update (probably via a move state).
  896. // just do nothing, since update will calculate the correct sleep behavior at the end.
  897. }
  898. else
  899. {
  900. setWakeFrame(getObject(), UPDATE_SLEEP_NONE);
  901. }
  902. }
  903. #endif
  904. }
  905. //-------------------------------------------------------------------------------------------------
  906. void AIUpdateInterface::friend_notifyStateMachineChanged()
  907. {
  908. wakeUpNow();
  909. }
  910. //-------------------------------------------------------------------------------------------------
  911. /**
  912. * The "main loop" of the AI subsystem
  913. */
  914. DECLARE_PERF_TIMER(AIUpdateInterface_update)
  915. UpdateSleepTime AIUpdateInterface::update( void )
  916. {
  917. //DEBUG_LOG(("AIUpdateInterface frame %d: %08lx\n",TheGameLogic->getFrame(),getObject()));
  918. USE_PERF_TIMER(AIUpdateInterface_update)
  919. m_isInUpdate = TRUE;
  920. m_completedWaypoint = NULL; // Reset so state machine update can set it if we just completed the path.
  921. // assume we can sleep forever, unless the state machine (or turret, etc) demand otherwise
  922. UpdateSleepTime subMachineSleep = UPDATE_SLEEP_FOREVER;
  923. StateReturnType stRet = getStateMachine()->updateStateMachine();
  924. if (IS_STATE_SLEEP(stRet))
  925. {
  926. Int frames = GET_STATE_SLEEP_FRAMES(stRet);
  927. if (frames < subMachineSleep)
  928. subMachineSleep = UPDATE_SLEEP(frames);
  929. }
  930. else
  931. {
  932. // it's STATE_CONTINUE, STATE_SUCCESS, or STATE_FAILURE,
  933. // any of which will probably require next frame
  934. subMachineSleep = UPDATE_SLEEP_NONE;
  935. }
  936. // note that this is all OK with sleepiness, since m_movementComplete can
  937. // only be set via our statemachine (via friend_startingMove or friend_endMove),
  938. // which we just called. thus we should
  939. // never have worry about waking ourselves up when this changes, since
  940. // if it changes the code will always flow thru here anyway. (srj)
  941. if (m_movementComplete)
  942. {
  943. setQueueForPathTime(0);
  944. // destroy path
  945. destroyPath();
  946. setLocomotorGoalNone();
  947. getObject()->clearModelConditionState(MODELCONDITION_MOVING);
  948. Coord3D goalPos;
  949. if (TheAI->pathfinder()->goalPosition(getObject(), &goalPos))
  950. {
  951. // Pop to goal - This shouldn't happen (often), but make sure we got to where we're going.
  952. Real dx = goalPos.x-getObject()->getPosition()->x;
  953. Real dy = goalPos.y-getObject()->getPosition()->y;
  954. if (dx*dx+dy*dy>=PATHFIND_CELL_SIZE_F*PATHFIND_CELL_SIZE_F)
  955. {
  956. // Too far, so just grid current pos.
  957. goalPos = *getObject()->getPosition();
  958. TheAI->pathfinder()->snapPosition(getObject(), &goalPos);
  959. }
  960. setFinalPosition(&goalPos);
  961. TheAI->pathfinder()->updateGoal(getObject(), &goalPos, getObject()->getLayer());
  962. }
  963. m_movementComplete = FALSE;
  964. ignoreObstacle(NULL);
  965. }
  966. UnsignedInt now = TheGameLogic->getFrame();
  967. if (m_queueForPathFrame != 0)
  968. {
  969. if (now >= m_queueForPathFrame)
  970. {
  971. TheAI->pathfinder()->queueForPath(getObject()->getID());
  972. setQueueForPathTime(0);
  973. }
  974. else
  975. {
  976. UnsignedInt sleepForPathDelta = m_queueForPathFrame - now;
  977. if (sleepForPathDelta < subMachineSleep)
  978. subMachineSleep = UPDATE_SLEEP(sleepForPathDelta);
  979. }
  980. }
  981. Object *obj = getObject();
  982. if (! obj->isEffectivelyDead() &&
  983. ! obj->isDisabledByType( DISABLED_PARALYZED ) &&
  984. ! obj->isDisabledByType( DISABLED_UNMANNED ) &&
  985. ! obj->isDisabledByType( DISABLED_EMP ) &&
  986. ! obj->isDisabledByType( DISABLED_SUBDUED ) &&
  987. ! obj->isDisabledByType( DISABLED_HACKED ) )
  988. {
  989. // If we are dead, don't let the turrets do anything anymore, or else they will keep attacking
  990. for (int i = 0; i < MAX_TURRETS; ++i)
  991. {
  992. if (m_turretAI[i])
  993. {
  994. UpdateSleepTime tmp = m_turretAI[i]->updateTurretAI();
  995. if (tmp < subMachineSleep)
  996. subMachineSleep = tmp;
  997. }
  998. }
  999. }
  1000. // must do death check outside of the state machine update, to avoid corruption
  1001. if (isAiInDeadState() && !(getStateMachine()->getCurrentStateID() == AI_DEAD) )
  1002. {
  1003. /// @todo Yikes! If we are not interruptable, and we die, what do we do? (MSB)
  1004. getStateMachine()->clear();
  1005. getStateMachine()->setState( AI_DEAD );
  1006. getStateMachine()->lock("AIUpdateInterface::update");
  1007. // strangely, dead things need to NOT sleep at all. (but they don't stay dead for long,
  1008. // so this is not too bad.)
  1009. subMachineSleep = UPDATE_SLEEP_NONE;
  1010. }
  1011. // do this objects movement
  1012. UpdateSleepTime tmp = doLocomotor();
  1013. if (tmp < subMachineSleep)
  1014. subMachineSleep = tmp;
  1015. #ifdef ALLOW_DEMORALIZE
  1016. RELEASE_CRASH(("If ALLOW_DEMORALIZE is ever defined, this code must be redone to do proper SLEEPY updates. (srj)"));
  1017. // update the demoralized frames if present
  1018. if( m_demoralizedFramesLeft > 0 )
  1019. {
  1020. setDemoralized( m_demoralizedFramesLeft - 1 );
  1021. }
  1022. #endif
  1023. #ifdef ALLOW_SURRENDER
  1024. RELEASE_CRASH(("If ALLOW_SURRENDER is ever defined, this code must be redone to do proper SLEEPY updates. (srj)"));
  1025. doSurrenderUpdateStuff();
  1026. #endif
  1027. m_isInUpdate = FALSE;
  1028. if (m_completedWaypoint != NULL)
  1029. {
  1030. // sleep NONE here so that it will get reset next frame.
  1031. // this happen infrequently, so it shouldn't be an issue.
  1032. return UPDATE_SLEEP_NONE;
  1033. }
  1034. else
  1035. {
  1036. #ifdef SLEEPY_AI
  1037. return subMachineSleep;
  1038. #else
  1039. return UPDATE_SLEEP_NONE;
  1040. #endif
  1041. }
  1042. }
  1043. //-------------------------------------------------------------------------------------------------
  1044. /**
  1045. * Append waypoint to queue for later movement
  1046. */
  1047. Bool AIUpdateInterface::queueWaypoint( const Coord3D *pos )
  1048. {
  1049. if (m_waypointCount < MAX_WAYPOINTS)
  1050. {
  1051. m_waypointQueue[ m_waypointCount++ ] = *pos;
  1052. return TRUE;
  1053. }
  1054. return FALSE;
  1055. }
  1056. //-------------------------------------------------------------------------------------------------
  1057. /**
  1058. * Start moving along the waypoint path in the queue
  1059. */
  1060. void AIUpdateInterface::executeWaypointQueue( void )
  1061. {
  1062. // the dead don't listen very well
  1063. if (isAiInDeadState())
  1064. return;
  1065. // m_actionStack->clear();
  1066. if (m_waypointCount > 0)
  1067. {
  1068. m_waypointIndex = 0;
  1069. m_executingWaypointQueue = TRUE;
  1070. }
  1071. }
  1072. //-------------------------------------------------------------------------------------------------
  1073. void AIUpdateInterface::clearWaypointQueue( void )
  1074. {
  1075. m_waypointCount = 0;
  1076. m_executingWaypointQueue = FALSE;
  1077. }
  1078. //-------------------------------------------------------------------------------------------------
  1079. void AIUpdateInterface::markAsDead()
  1080. {
  1081. m_isAiDead = TRUE;
  1082. getObject()->setEffectivelyDead(TRUE);
  1083. wakeUpNow(); // wake us up immediately so that our anim plays promptly!
  1084. }
  1085. //-------------------------------------------------------------------------------------------------
  1086. /* Returns TRUE if this ai has a higher path priority than the other one.
  1087. The way to have a higher priority is:
  1088. 1. If the paths were assigned when both units were in the same ai group, we use the path priority assigned.
  1089. 2. If not, the unit that is in front has the higher priority.
  1090. 3. If exactly tied (usually beacause both units got unfortunately snapped to the same location), ObjectID is used
  1091. to break the tie.
  1092. */
  1093. Bool AIUpdateInterface::hasHigherPathPriority(AIUpdateInterface *otherAI) const
  1094. {
  1095. Object *other = otherAI->getObject();
  1096. // Dozers have highest priority.
  1097. if (getObject()->isKindOf(KINDOF_DOZER) && !other->isKindOf(KINDOF_DOZER)) {
  1098. return TRUE;
  1099. }
  1100. if (!getObject()->isKindOf(KINDOF_DOZER) && other->isKindOf(KINDOF_DOZER)) {
  1101. return FALSE;
  1102. }
  1103. // Vehicles always have higher priority than infantry.
  1104. if (getObject()->isKindOf(KINDOF_VEHICLE) && other->isKindOf(KINDOF_INFANTRY)) {
  1105. return TRUE;
  1106. }
  1107. if (getObject()->isKindOf(KINDOF_INFANTRY) && other->isKindOf(KINDOF_VEHICLE)) {
  1108. return FALSE;
  1109. }
  1110. // The paths aren't of the same group, so see which unit is in front.
  1111. Coord3D ourDir = *getObject()->getUnitDirectionVector2D();
  1112. Coord3D otherDir = *other->getUnitDirectionVector2D();
  1113. if (ourDir.x*otherDir.x + ourDir.y*otherDir.y <= 0) {
  1114. return getObject()->getID() < other->getID();
  1115. }
  1116. Coord2D combinedDir;
  1117. combinedDir.x = ourDir.x + otherDir.x;
  1118. combinedDir.y = ourDir.y + otherDir.y;
  1119. Coord2D vectorToOther;
  1120. vectorToOther.x = other->getPosition()->x - getObject()->getPosition()->x;
  1121. vectorToOther.y = other->getPosition()->y - getObject()->getPosition()->y;
  1122. // Dot product is our directions projected onto each other.
  1123. Real dotProduct = combinedDir.x*vectorToOther.x + combinedDir.y*vectorToOther.y;
  1124. if (dotProduct>0) return FALSE; // other is ahead of us along our directional vector.
  1125. if (dotProduct<0) return TRUE; // We are ahead of other.
  1126. // Exactly equal. Use object id's to break the tie.
  1127. return getObject()->getID() < other->getID();
  1128. }
  1129. //-------------------------------------------------------------------------------------------------
  1130. /* Returns max speed we can have and not run into unit that is blocking us.
  1131. */
  1132. Real AIUpdateInterface::calculateMaxBlockedSpeed(Object *other) const
  1133. {
  1134. Coord3D ourDir = *getObject()->getUnitDirectionVector2D();
  1135. Coord3D otherDir = *other->getUnitDirectionVector2D();
  1136. // Dot product is our directions projected onto each other.
  1137. Coord2D vectorToOther;
  1138. vectorToOther.x = other->getPosition()->x - getObject()->getPosition()->x;
  1139. vectorToOther.y = other->getPosition()->y - getObject()->getPosition()->y;
  1140. vectorToOther.normalize();
  1141. Real dotProduct = vectorToOther.x*otherDir.x + vectorToOther.y*otherDir.y;
  1142. if (dotProduct<0) return 0; // They are running into us.
  1143. Real speedFactor = dotProduct;
  1144. PhysicsBehavior *otherPhysics = other->getPhysics();
  1145. if (!otherPhysics) {
  1146. return m_curMaxBlockedSpeed;
  1147. }
  1148. Coord3D otherVel = *otherPhysics->getVelocity();
  1149. otherVel.z = 0;
  1150. // Calculate how fast other is moving away from us...
  1151. Real awaySpeed = otherVel.length() * speedFactor;
  1152. // Now calculate the amount we are moving relative to towards them...
  1153. dotProduct = vectorToOther.x*ourDir.x + vectorToOther.y*ourDir.y;
  1154. if (dotProduct<=0) {
  1155. // Unexpected - we are moving away. Shouldn't be blocked...
  1156. return m_curMaxBlockedSpeed;
  1157. }
  1158. Real maxSpeed = awaySpeed / dotProduct;
  1159. if (other->getFormationID()!=NO_FORMATION_ID && getObject()->getFormationID()==other->getFormationID()) {
  1160. maxSpeed *= 0.55f; // don't let formations crowd each other.
  1161. }
  1162. if (maxSpeed>m_curMaxBlockedSpeed) return m_curMaxBlockedSpeed;
  1163. return maxSpeed;
  1164. }
  1165. //-------------------------------------------------------------------------------------------------
  1166. Bool AIUpdateInterface::blockedBy(Object *other)
  1167. /* Returns TRUE if we are blocked from moving by the other object.*/
  1168. {
  1169. Coord3D goalPos = *getStateMachine()->getGoalPosition();
  1170. Object *obj = getObject();
  1171. Coord3D pos = *obj->getPosition();
  1172. ICoord2D goalCell = *getPathfindGoalCell();
  1173. // If we are near our final goal, don't get stuck.
  1174. if (goalCell.x>0 && goalCell.y>0) {
  1175. Real dx = fabs(goalPos.x-pos.x);
  1176. Real dy = fabs(goalPos.y-pos.y);
  1177. if (dx<PATHFIND_CELL_SIZE_F && dy<PATHFIND_CELL_SIZE_F) {
  1178. return FALSE; // If we're approaching our goal, ignore obstacles.
  1179. }
  1180. }
  1181. Bool canCrush = obj->canCrushOrSquish(other, TEST_CRUSH_OR_SQUISH);
  1182. if (canCrush) return FALSE; // just run over them.
  1183. AIUpdateInterface* aiOther = other->getAI();
  1184. if (!aiOther->isDoingGroundMovement()) {
  1185. return FALSE; // Can't be blocked if the other is airborne.
  1186. }
  1187. if (!aiOther) return FALSE; // Ignore it.
  1188. if (getCurLocomotor() && getCurLocomotor()->isMovingBackwards()) {
  1189. return false; // don't collide.
  1190. }
  1191. Bool otherMoving = ( aiOther->m_locomotorGoalType != NONE );
  1192. Coord3D otherPos = *other->getPosition();
  1193. Real dx = pos.x-otherPos.x;
  1194. Real dy = pos.y-otherPos.y;
  1195. Real curDSqr = dx*dx+dy*dy;
  1196. if (obj->isKindOf(KINDOF_INFANTRY) && other->isKindOf(KINDOF_INFANTRY)) {
  1197. // Infantry doesn't tend to impede other infantry...
  1198. #ifdef INFANTRY_MOVES_THROUGH_INFANTRY
  1199. if (!otherMoving) {
  1200. return FALSE; // Infantry can run through other infantry.
  1201. }
  1202. return FALSE;
  1203. #else
  1204. // If we are crossing, just pass through.
  1205. Coord3D ourDir = *obj->getUnitDirectionVector2D();
  1206. Coord3D theirDir = *other->getUnitDirectionVector2D();
  1207. // Dot product is our directions projected onto each other.
  1208. Real dotProduct = ourDir.x*theirDir.x + ourDir.y*theirDir.y;
  1209. if (dotProduct<=0.25) return FALSE; // we are not moving in the same direction.
  1210. #endif
  1211. }
  1212. if (curDSqr < PATHFIND_CELL_SIZE_F*PATHFIND_CELL_SIZE_F*0.0001f) {
  1213. // Somehow 2 units ended up on the same grid.
  1214. // Lowest path priority wins.
  1215. return (hasHigherPathPriority(aiOther));
  1216. }
  1217. // we've been blocked for a while. If we're crossing, just move through.
  1218. Coord3D ourDir = *obj->getUnitDirectionVector2D();
  1219. Coord3D theirDir = *other->getUnitDirectionVector2D();
  1220. // Dot product is our directions projected onto each other.
  1221. Real dotProduct = ourDir.x*theirDir.x + ourDir.y*theirDir.y;
  1222. if (getNumFramesBlocked()>LOGICFRAMES_PER_SECOND) {
  1223. if (dotProduct<=0.0f) return FALSE; // we are not moving in the same direction.
  1224. }
  1225. Real collisionAngle = ThePartitionManager->getRelativeAngle2D( obj, &otherPos );
  1226. Real otherAngle = ThePartitionManager->getRelativeAngle2D( other, &pos );
  1227. //DEBUG_LOG(("Collision angle %.2f, %.2f, %s, %x %s\n", collisionAngle*180/PI, otherAngle*180/PI, obj->getTemplate()->getName().str(), obj, other->getTemplate()->getName().str()));
  1228. Real angleLimit = PI/4; // 45 degrees.
  1229. if (collisionAngle>PI/2 || collisionAngle<-PI/2) {
  1230. return FALSE; // we're moving away.
  1231. }
  1232. if (!otherMoving) angleLimit *= 0.75f;
  1233. if (collisionAngle>angleLimit || collisionAngle<-angleLimit) {
  1234. if (dotProduct<=0.0f) return FALSE; // we are not moving in the same direction.
  1235. if (otherMoving && (otherAngle>angleLimit || otherAngle<-angleLimit) ) {
  1236. // See if we're running into each other.
  1237. Coord3D ourDir = *obj->getUnitDirectionVector2D();
  1238. Coord3D theirDir = *other->getUnitDirectionVector2D();
  1239. dx += ourDir.x - theirDir.x;
  1240. dy += ourDir.y - theirDir.y;
  1241. if (curDSqr>dx*dx+dy*dy) {
  1242. if (hasHigherPathPriority(aiOther)) {
  1243. // Lowest path priority wins.
  1244. return FALSE;
  1245. }
  1246. } else {
  1247. //DEBUG_LOG(("Moving Away From EachOther\n"));
  1248. return FALSE; // moving away, so no need for corrective action.
  1249. }
  1250. } else {
  1251. return FALSE; // Off angle, and they're not moving, so we aren't moving into each other.
  1252. }
  1253. }
  1254. if (!aiOther->isAiInDeadState())
  1255. {
  1256. return TRUE;
  1257. }
  1258. return FALSE;
  1259. }
  1260. //-------------------------------------------------------------------------------------------------
  1261. Bool AIUpdateInterface::needToRotate(void)
  1262. /* Returns TRUE if we need to rotate to point in our path's direcion.*/
  1263. {
  1264. if (isWaitingForPath())
  1265. return TRUE; // new path will probably require rotation.
  1266. if (this->getCurLocomotor() && this->getCurLocomotor()->getWanderWidthFactor()>0.0f)
  1267. return FALSE; // wanderers don't need to rotate.
  1268. Real deltaAngle = 0;
  1269. if (getPath())
  1270. {
  1271. ClosestPointOnPathInfo info;
  1272. CRCDEBUG_LOG(("AIUpdateInterface::needToRotate() - calling computePointOnPath() for object %d\n", getObject()->getID()));
  1273. getPath()->computePointOnPath(getObject(), m_locomotorSet, *getObject()->getPosition(), info);
  1274. deltaAngle = ThePartitionManager->getRelativeAngle2D( getObject(), &info.posOnPath );
  1275. }
  1276. if (fabs(deltaAngle)>PI/30)
  1277. {
  1278. return TRUE;
  1279. }
  1280. return FALSE;
  1281. }
  1282. //-------------------------------------------------------------------------------------------------
  1283. /* Returns TRUE if the physics collide should apply the force. Normally not.
  1284. Also determines whether objects are blocked, and if so, if they are stuck. jba.*/
  1285. Bool AIUpdateInterface::processCollision(PhysicsBehavior *physics, Object *other)
  1286. {
  1287. #ifdef DO_UNIT_TIMINGS
  1288. return false;
  1289. #endif
  1290. if (m_ignoreCollisionsUntil > TheGameLogic->getFrame())
  1291. return FALSE;
  1292. if (m_canPathThroughUnits)
  1293. return FALSE;
  1294. AIUpdateInterface* aiOther = other->getAI();
  1295. if (aiOther == NULL)
  1296. return FALSE;
  1297. Bool selfMoving = isMoving();
  1298. Bool otherMoving = ( aiOther && aiOther->isMoving() );
  1299. if (!isDoingGroundMovement()) return FALSE;
  1300. if (!aiOther->isDoingGroundMovement()) return FALSE;
  1301. if (selfMoving)
  1302. {
  1303. Bool blocked = blockedBy(other);
  1304. if (blocked)
  1305. {
  1306. if (getObject()->isKindOf(KINDOF_INFANTRY))
  1307. {
  1308. // Panic bounces around.
  1309. if (getStateMachine()->getCurrentStateID() == AI_PANIC)
  1310. {
  1311. return TRUE; // just bounce off of other humans.
  1312. }
  1313. }
  1314. m_isBlocked = TRUE; // we are blocked.
  1315. if (otherMoving && aiOther->isWaitingForPath())
  1316. {
  1317. return FALSE; // let them get their path;
  1318. }
  1319. Real maxSpeed = calculateMaxBlockedSpeed(other);
  1320. if (maxSpeed < m_curMaxBlockedSpeed)
  1321. {
  1322. m_curMaxBlockedSpeed = maxSpeed;
  1323. }
  1324. if (!aiOther->isMovingAwayFrom(getObject())) {
  1325. if (other->isKindOf(KINDOF_INFANTRY) && !getObject()->isKindOf(KINDOF_INFANTRY))
  1326. {
  1327. //Kris: Patch 1.01 -- November 5, 2003
  1328. //Prevent busy units from being told to move out of the way!
  1329. if( other->testStatus( OBJECT_STATUS_IS_USING_ABILITY ) || other->getAI() && other->getAI()->isBusy() )
  1330. {
  1331. return FALSE;
  1332. }
  1333. aiOther->aiMoveAwayFromUnit(getObject(), CMD_FROM_AI);
  1334. return FALSE;
  1335. }
  1336. #define dont_MOVE_AROUND // It just causes more problems than it fixes. jba.
  1337. #ifdef MOVE_AROUND
  1338. if (m_curLocomotor!=NULL && (other->isKindOf(KINDOF_INFANTRY)==getObject()->isKindOf(KINDOF_INFANTRY))) {
  1339. Real myMaxSpeed = m_curLocomotor->getMaxSpeedForCondition(getObject()->getBodyModule()->getDamageState());
  1340. Locomotor *hisLoco = aiOther->getCurLocomotor();
  1341. if (hisLoco) {
  1342. Real hisMaxSpeed = hisLoco->getMaxSpeedForCondition(other->getBodyModule()->getDamageState());
  1343. if (hisMaxSpeed > 0.05 && hisMaxSpeed < 0.6f*myMaxSpeed) {
  1344. aiOther->aiMoveAwayFromUnit(getObject(), CMD_FROM_AI);
  1345. return FALSE;
  1346. }
  1347. }
  1348. }
  1349. #endif
  1350. }
  1351. //DEBUG_LOG(("Blocked %s, %x, %s\n", getObject()->getTemplate()->getName().str(), getObject(), other->getTemplate()->getName().str()));
  1352. if (m_blockedFrames==0) m_blockedFrames = 1;
  1353. if (!needToRotate())
  1354. {
  1355. // If we are already pointing in the right direction, we may be stuck.
  1356. if (!otherMoving)
  1357. {
  1358. // Intense logging jba
  1359. // DEBUG_LOG(("Blocked&Stuck !otherMoving\n"));
  1360. m_isBlockedAndStuck = TRUE;
  1361. return FALSE;
  1362. }
  1363. // See if other is blocked by us.
  1364. if (aiOther->blockedBy(getObject()))
  1365. {
  1366. if (!aiOther->needToRotate())
  1367. {
  1368. // Deadlocked.
  1369. if (!hasHigherPathPriority(aiOther))
  1370. {
  1371. // get out of his way.
  1372. aiMoveAwayFromUnit(aiOther->getObject(), CMD_FROM_AI);
  1373. //m_isBlockedAndStuck = TRUE;
  1374. // Intense logging jba.
  1375. // DEBUG_LOG(("Blocked&Stuck other is blockedByUs, has higher priority\n"));
  1376. }
  1377. }
  1378. }
  1379. else
  1380. {
  1381. // Just wait.
  1382. }
  1383. }
  1384. else
  1385. {
  1386. // We are rotating, so don't accumulate blocked frames.
  1387. m_blockedFrames = 1;
  1388. }
  1389. }
  1390. }
  1391. else
  1392. {
  1393. if (isAiInDeadState())
  1394. {
  1395. // Dead infantry get pushed around by crushers.
  1396. if (getObject()->isKindOf(KINDOF_INFANTRY) && other->canCrushOrSquish(getObject(), TEST_SQUISH_ONLY))
  1397. {
  1398. return TRUE;
  1399. }
  1400. }
  1401. Coord3D otherPos = *other->getPosition();
  1402. Real dx = getObject()->getPosition()->x - otherPos.x;
  1403. Real dy = getObject()->getPosition()->y - otherPos.y;
  1404. Real curDSqr = dx*dx+dy*dy;
  1405. if (!otherMoving && curDSqr < PATHFIND_CELL_SIZE_F*PATHFIND_CELL_SIZE_F*0.25f)
  1406. {
  1407. if (this->getCurrentStateID() == AI_BUSY) {
  1408. return false;
  1409. }
  1410. if (getObject()->testStatus(OBJECT_STATUS_IS_USING_ABILITY)) {
  1411. return false; // we are doing a special ability. Shouldn't move at this time. jba.
  1412. }
  1413. // jba intense debug
  1414. //DEBUG_LOG(("*****Units ended up on top of each other. Shouldn't happen.\n"));
  1415. if (isIdle()) {
  1416. Coord3D safePosition = *getObject()->getPosition();
  1417. TheAI->pathfinder()->adjustToPossibleDestination(getObject(), getLocomotorSet(), &safePosition);
  1418. aiMoveToPosition( &safePosition, CMD_FROM_AI );
  1419. }
  1420. if (aiOther->isIdle()) {
  1421. TheAI->pathfinder()->adjustToPossibleDestination(other, aiOther->getLocomotorSet(),
  1422. &otherPos);
  1423. aiOther->aiMoveToPosition( &otherPos, CMD_FROM_AI);
  1424. }
  1425. }
  1426. }
  1427. return FALSE;
  1428. }
  1429. //-------------------------------------------------------------------------------------------------
  1430. /**
  1431. * See if we can do a quick path without pathfinding.
  1432. */
  1433. Bool AIUpdateInterface::canComputeQuickPath( void )
  1434. {
  1435. /* Basically, if a unit is moving through the air, we can quick path. jba. */
  1436. Bool landBound = FALSE;
  1437. // Note - if a truck happens to pop into the air and gets a move to command, it still
  1438. // needs to pathfind. So only skip pathfinding for airborne things that can fly... jba.
  1439. if (!(m_locomotorSet.getValidSurfaces() & LOCOMOTORSURFACE_AIR))
  1440. {
  1441. landBound = TRUE;
  1442. }
  1443. Bool unitIsFlyingThroughTheAir = FALSE;
  1444. if (landBound) {
  1445. unitIsFlyingThroughTheAir = FALSE; // Land bound units never fly.
  1446. } else {
  1447. if (!isDoingGroundMovement()) {
  1448. // If it can fly, and it isn't moving on the ground, we're flying.
  1449. unitIsFlyingThroughTheAir = TRUE;
  1450. }
  1451. }
  1452. return unitIsFlyingThroughTheAir;
  1453. }
  1454. //-------------------------------------------------------------------------------------------------
  1455. /**
  1456. * Create a quick path. (Just places the start & end point as the path). jba.
  1457. */
  1458. Bool AIUpdateInterface::computeQuickPath( const Coord3D *destination )
  1459. {
  1460. // for now, quick path objects don't pathfind, generally airborne units
  1461. // build a trivial one-node path containing destination
  1462. // First, see if our path already goes to the destination.
  1463. if (m_path) {
  1464. PathNode *closeNode = NULL;
  1465. closeNode = m_path->getLastNode();
  1466. if (closeNode && closeNode->getNextOptimized()==NULL) {
  1467. Real dxSqr = destination->x - closeNode->getPosition()->x;
  1468. dxSqr *= dxSqr;
  1469. Real dySqr = destination->y - closeNode->getPosition()->y;
  1470. dySqr *= dySqr;
  1471. Real dzSqr = destination->z - closeNode->getPosition()->z;
  1472. dzSqr *= dzSqr;
  1473. if (dxSqr+dySqr+dzSqr<0.25f) {
  1474. return TRUE;
  1475. }
  1476. }
  1477. }
  1478. // destroy previous path
  1479. destroyPath();
  1480. if (getObject()->isKindOf(KINDOF_AIRCRAFT) && !getObject()->isKindOf(KINDOF_PROJECTILE)) {
  1481. m_path = TheAI->pathfinder()->getAircraftPath(getObject(), destination);
  1482. } else {
  1483. m_path = newInstance(Path);
  1484. m_path->prependNode( destination, LAYER_GROUND );
  1485. Coord3D pos = *getObject()->getPosition();
  1486. pos.z = destination->z;
  1487. m_path->prependNode( &pos, getObject()->getLayer() );
  1488. m_path->getFirstNode()->setNextOptimized(m_path->getFirstNode()->getNext());
  1489. if (TheGlobalData->m_debugAI==AI_DEBUG_PATHS)
  1490. {
  1491. TheAI->pathfinder()->setDebugPath(m_path);
  1492. }
  1493. }
  1494. // timestamp when the path was created
  1495. m_pathTimestamp = TheGameLogic->getFrame();
  1496. m_blockedFrames = 0;
  1497. m_isBlockedAndStuck = FALSE;
  1498. return TRUE;
  1499. }
  1500. //-------------------------------------------------------------------------------------------------
  1501. /**
  1502. * Invoke the pathfinder to compute a path to the desired location.
  1503. */
  1504. Bool AIUpdateInterface::computePath( PathfindServicesInterface *pathServices, Coord3D *destination )
  1505. {
  1506. if (!m_isBlockedAndStuck) {
  1507. destroyPath();
  1508. }
  1509. if (canComputeQuickPath())
  1510. {
  1511. return computeQuickPath(destination);
  1512. }
  1513. m_retryPath = false;
  1514. Region3D extent;
  1515. TheTerrainLogic->getMaximumPathfindExtent(&extent);
  1516. if (!extent.isInRegionNoZ(destination)) {
  1517. // We're going off the map.
  1518. Coord3D pos = *getObject()->getPosition();
  1519. if (!extent.isInRegionNoZ(&pos)) {
  1520. // We're starting off the map. Since we're off the map, we can't pathfind so just build a path.
  1521. return computeQuickPath(destination);
  1522. }
  1523. }
  1524. // Special case of exit factory. jba.
  1525. if ((m_stateMachine->getCurrentStateID() == AI_FOLLOW_EXITPRODUCTION_PATH) && canPathThroughUnits()) {
  1526. Bool ok = computeQuickPath(destination);
  1527. if (ok) {
  1528. TheAI->pathfinder()->moveAlliesAwayFromDestination(getObject(), *destination);
  1529. setCanPathThroughUnits(false);
  1530. setGoalPositionClipped(destination, CMD_FROM_AI);
  1531. return ok;
  1532. }
  1533. }
  1534. Path *theNewPath = NULL;
  1535. TheAI->pathfinder()->setIgnoreObstacleID( getIgnoredObstacleID() );
  1536. Coord3D originalDestination = *destination;
  1537. // sanity check - if destination cell is invalid, don't bother pathing
  1538. LocomotorSurfaceTypeMask surfaces = m_locomotorSet.getValidSurfaces();
  1539. if (!m_isFinalGoal && TheAI->pathfinder()->isLinePassable( getObject(), surfaces,
  1540. getObject()->getLayer(), *getObject()->getPosition(), originalDestination, false, true)) {
  1541. return computeQuickPath(destination);
  1542. }
  1543. PathfindLayerEnum destinationLayer = TheTerrainLogic->getLayerForDestination(destination);
  1544. if (TheAI->pathfinder()->validMovementPosition( getObject()->getCrusherLevel()>0, destinationLayer, m_locomotorSet, destination ) == FALSE)
  1545. {
  1546. theNewPath = NULL;
  1547. }
  1548. else
  1549. {
  1550. // compute a ground-based path
  1551. if (m_isBlockedAndStuck) {
  1552. theNewPath = pathServices->patchPath( getObject(), m_locomotorSet,
  1553. getPath(), m_isBlockedAndStuck);
  1554. } else {
  1555. theNewPath = pathServices->findPath( getObject(), m_locomotorSet, getObject()->getPosition(),
  1556. destination);
  1557. }
  1558. }
  1559. if (theNewPath==NULL && m_path==NULL) {
  1560. Real pathCostFactor = 0.0f;
  1561. theNewPath = pathServices->findClosestPath( getObject(), m_locomotorSet, getObject()->getPosition(),
  1562. destination, m_isBlockedAndStuck, pathCostFactor, FALSE );
  1563. m_retryPath = true;
  1564. }
  1565. TheAI->pathfinder()->setIgnoreObstacleID( INVALID_ID );
  1566. if (theNewPath) {
  1567. // destroy previous path
  1568. destroyPath();
  1569. m_path = theNewPath;
  1570. if (getCurLocomotor() && getCurLocomotor()->isUltraAccurate()) {
  1571. // Move exactly to the destination. Normal ground pathfinding moves to a gridded location.
  1572. theNewPath->updateLastNode(&originalDestination);
  1573. }
  1574. setLocomotorGoalPositionOnPath();
  1575. if( !getObject()->isKindOf(KINDOF_NO_COLLIDE))// If I don't collide with things, I don't need to tell them to get out of the way
  1576. TheAI->pathfinder()->moveAllies(getObject(), theNewPath);
  1577. } else {
  1578. // Keep using the old path.
  1579. if (m_path && m_isBlockedAndStuck) {
  1580. destroyPath();
  1581. // Stop and wait one second.
  1582. setQueueForPathTime(LOGICFRAMES_PER_SECOND);
  1583. Coord3D goalPos;
  1584. Object *obj = getObject();
  1585. goalPos = *obj->getPosition();
  1586. TheAI->pathfinder()->snapPosition(obj, &goalPos);
  1587. setFinalPosition(&goalPos);
  1588. setLocomotorGoalNone();
  1589. m_blockedFrames = 0;
  1590. m_isBlocked = FALSE;
  1591. m_isBlockedAndStuck = FALSE;
  1592. }
  1593. }
  1594. // timestamp when the path was created
  1595. m_pathTimestamp = TheGameLogic->getFrame();
  1596. m_blockedFrames = 0;
  1597. m_isBlockedAndStuck = FALSE;
  1598. if (m_path)
  1599. return TRUE;
  1600. return FALSE;
  1601. }
  1602. //-------------------------------------------------------------------------------------------------
  1603. /**
  1604. * Invoke the pathfinder to compute a path to attack the current victim.
  1605. */
  1606. Bool AIUpdateInterface::computeAttackPath( PathfindServicesInterface *pathServices, const Object *victim, const Coord3D* victimPos )
  1607. {
  1608. //CRCDEBUG_LOG(("AIUpdateInterface::computeAttackPath() for object %d\n", getObject()->getID()));
  1609. // See if it has been too soon.
  1610. if (m_pathTimestamp >= TheGameLogic->getFrame()-2)
  1611. {
  1612. // jba intense debug
  1613. //CRCDEBUG_LOG(("Info - RePathing very quickly %d, %d.\n", m_pathTimestamp, TheGameLogic->getFrame()));
  1614. if (m_path && m_isBlockedAndStuck)
  1615. {
  1616. setIgnoreCollisionTime(2*LOGICFRAMES_PER_SECOND);
  1617. m_blockedFrames = 0;
  1618. m_isBlocked = FALSE;
  1619. m_isBlockedAndStuck = FALSE;
  1620. return TRUE;
  1621. }
  1622. }
  1623. Bool landBound = FALSE;
  1624. // Note - if a truck happens to pop into the air and gets a move to command, it still
  1625. // needs to pathfind. So only skip pathfinding for airborne things that can fly... jba.
  1626. if (!(m_locomotorSet.getValidSurfaces() & LOCOMOTORSURFACE_AIR))
  1627. {
  1628. landBound = TRUE;
  1629. }
  1630. Object* source = getObject();
  1631. if (!victim && !victimPos)
  1632. {
  1633. //CRCDEBUG_LOG(("AIUpdateInterface::computeAttackPath() - victim is NULL\n"));
  1634. return FALSE;
  1635. }
  1636. PathfindLayerEnum victimLayer = LAYER_GROUND;
  1637. if (victim) {
  1638. victimLayer = victim->getLayer();
  1639. }
  1640. Weapon *weapon = source->getCurrentWeapon();
  1641. if (!weapon)
  1642. {
  1643. DEBUG_CRASH(("no weapon in AIUpdateInterface::computeAttackPath"));
  1644. return FALSE;
  1645. }
  1646. // is our weapon within attack range?
  1647. // if so, just return TRUE with no path.
  1648. if (victim != NULL)
  1649. {
  1650. if (weapon->isWithinAttackRange(source, victim))
  1651. {
  1652. Bool viewBlocked = FALSE;
  1653. if (isDoingGroundMovement() && !victim->isSignificantlyAboveTerrain())
  1654. {
  1655. viewBlocked = TheAI->pathfinder()->isAttackViewBlockedByObstacle(source, *source->getPosition(), victim, *victim->getPosition());
  1656. }
  1657. if (!viewBlocked)
  1658. {
  1659. destroyPath();
  1660. //CRCDEBUG_LOG(("AIUpdateInterface::computeAttackPath() - target is in range and visible\n"));
  1661. return TRUE;
  1662. }
  1663. }
  1664. }
  1665. else if (victimPos != NULL)
  1666. {
  1667. if (weapon->isWithinAttackRange(source, victimPos))
  1668. {
  1669. Bool viewBlocked = FALSE;
  1670. if (isDoingGroundMovement())
  1671. {
  1672. viewBlocked = TheAI->pathfinder()->isAttackViewBlockedByObstacle(source, *source->getPosition(), NULL, *victimPos);
  1673. }
  1674. if (!viewBlocked) {
  1675. destroyPath();
  1676. //CRCDEBUG_LOG(("AIUpdateInterface::computeAttackPath() target pos is in range and visible\n"));
  1677. return TRUE;
  1678. }
  1679. }
  1680. }
  1681. // Contact weapon
  1682. if (weapon->isContactWeapon())
  1683. {
  1684. // Weapon is basically a contact weapon, like a car bomb. The approach target logic
  1685. // has been modified to let it approach the object, so just approach the target position. jba.
  1686. Coord3D tmp = *victimPos;
  1687. destroyPath();
  1688. if (this->getCurLocomotor())
  1689. {
  1690. getCurLocomotor()->setNoSlowDownAsApproachingDest(TRUE);
  1691. }
  1692. Bool ok = computePath(pathServices, &tmp);
  1693. if (m_path==NULL) return false;
  1694. Real dx, dy;
  1695. dx = victimPos->x - m_path->getLastNode()->getPosition()->x;
  1696. dy = victimPos->y - m_path->getLastNode()->getPosition()->y;
  1697. if (sqr(dx)+sqr(dy) < sqr(PATHFIND_CELL_SIZE_F*3)) {
  1698. if (m_path)
  1699. {
  1700. m_path->updateLastNode(victimPos); // jam in the coordinates of the target.
  1701. }
  1702. }
  1703. dx = source->getPosition()->x - m_path->getLastNode()->getPosition()->x;
  1704. dy = source->getPosition()->y - m_path->getLastNode()->getPosition()->y;
  1705. if (sqr(dx)+sqr(dy) < sqr(PATHFIND_CELL_SIZE_F)) {
  1706. // Very short path - we can't get to the goal.
  1707. destroyPath();
  1708. return false;
  1709. }
  1710. //CRCDEBUG_LOG(("AIUpdateInterface::computeAttackPath() is contact weapon\n"));
  1711. return ok;
  1712. }
  1713. Coord3D localVictimPos;
  1714. if (victim != NULL)
  1715. {
  1716. if (victim->isKindOf(KINDOF_BRIDGE))
  1717. {
  1718. TBridgeAttackInfo info;
  1719. TheTerrainLogic->getBridgeAttackPoints(victim, &info);
  1720. Real distSqr1 = ThePartitionManager->getDistanceSquared( source, &info.attackPoint1, FROM_BOUNDINGSPHERE_3D );
  1721. Real distSqr2 = ThePartitionManager->getDistanceSquared( source, &info.attackPoint2, FROM_BOUNDINGSPHERE_3D );
  1722. if (distSqr2<distSqr1) {
  1723. localVictimPos = info.attackPoint2;
  1724. } else {
  1725. localVictimPos = info.attackPoint1;
  1726. }
  1727. }
  1728. else
  1729. {
  1730. localVictimPos = *victim->getPosition();
  1731. }
  1732. }
  1733. else
  1734. {
  1735. localVictimPos = *victimPos;
  1736. }
  1737. localVictimPos.z = TheTerrainLogic->getLayerHeight( localVictimPos.x, localVictimPos.y, victimLayer );
  1738. if (getObject()->isAboveTerrain() && !landBound)
  1739. {
  1740. // for now, airborne objects don't pathfind
  1741. // build a trivial one-node path containing destination
  1742. weapon->computeApproachTarget(getObject(), victim, &localVictimPos, 0, localVictimPos);
  1743. //DEBUG_ASSERTCRASH(weapon->isGoalPosWithinAttackRange(getObject(), &localVictimPos, victim, victimPos, NULL),
  1744. // ("position we just calced is not acceptable\n"));
  1745. // First, see if our path already goes to the destination.
  1746. if (m_path)
  1747. {
  1748. PathNode *startNode, *closeNode = NULL;
  1749. startNode = m_path->getFirstNode();
  1750. closeNode = startNode->getNextOptimized();
  1751. if (closeNode && closeNode->getNextOptimized()==NULL) {
  1752. Real dxSqr = localVictimPos.x - closeNode->getPosition()->x;
  1753. dxSqr *= dxSqr;
  1754. Real dySqr = localVictimPos.y - closeNode->getPosition()->y;
  1755. dySqr *= dySqr;
  1756. if (dxSqr+dySqr<0.25f)
  1757. {
  1758. return TRUE;
  1759. }
  1760. }
  1761. }
  1762. // destroy previous path
  1763. destroyPath();
  1764. m_path = newInstance(Path);
  1765. m_path->prependNode( &localVictimPos, LAYER_GROUND );
  1766. Coord3D pos = *getObject()->getPosition();
  1767. pos.z = localVictimPos.z;
  1768. m_path->prependNode( &pos, LAYER_GROUND );
  1769. m_path->getFirstNode()->setNextOptimized(m_path->getFirstNode()->getNext());
  1770. if (TheGlobalData->m_debugAI==AI_DEBUG_PATHS)
  1771. {
  1772. TheAI->pathfinder()->setDebugPath(m_path);
  1773. }
  1774. }
  1775. else
  1776. {
  1777. // destroy previous path
  1778. destroyPath();
  1779. TheAI->pathfinder()->setIgnoreObstacleID( getIgnoredObstacleID() );
  1780. // compute a ground-based path
  1781. m_path = pathServices->findAttackPath( getObject(), m_locomotorSet, getObject()->getPosition(),
  1782. victim, &localVictimPos, weapon);
  1783. if (m_path) {
  1784. Coord3D goal = *m_path->getLastNode()->getPosition();
  1785. if (!weapon->isGoalPosWithinAttackRange(getObject(), &goal, victim, &localVictimPos)) {
  1786. // We didn't actually find a path we can attack from. [8/14/2003]
  1787. // If the move is a short distance, just do a find closest path to our current
  1788. // position. This will unstack us if we are on top of another unit. jba.
  1789. Coord3D objPos = *getObject()->getPosition();
  1790. goal.sub(&objPos);
  1791. if (goal.length()<3*PATHFIND_CELL_SIZE_F) {
  1792. destroyPath();
  1793. TheAI->pathfinder()->adjustDestination(getObject(), m_locomotorSet, &objPos);
  1794. m_path = pathServices->findClosestPath(getObject(), m_locomotorSet, getObject()->getPosition(),
  1795. &objPos, false, 0.2f, true );
  1796. }
  1797. if (m_path==NULL) {
  1798. return false;
  1799. }
  1800. }
  1801. goal = *m_path->getLastNode()->getPosition();
  1802. TheAI->pathfinder()->updateGoal(getObject(), &goal, TheTerrainLogic->getLayerForDestination(&goal));
  1803. if (m_path->getBlockedByAlly())
  1804. {
  1805. if( !getObject()->isKindOf(KINDOF_NO_COLLIDE))// If I don't collide with things, I don't need to tell them to get out of the way
  1806. TheAI->pathfinder()->moveAllies(getObject(), m_path);
  1807. }
  1808. }
  1809. TheAI->pathfinder()->setIgnoreObstacleID( INVALID_ID );
  1810. }
  1811. // timestamp when the path was created
  1812. m_pathTimestamp = TheGameLogic->getFrame();
  1813. m_blockedFrames = 0;
  1814. m_isBlockedAndStuck = FALSE;
  1815. //CRCDEBUG_LOG(("AIUpdateInterface::computeAttackPath() done\n"));
  1816. if (m_path)
  1817. return TRUE;
  1818. return FALSE;
  1819. }
  1820. //-------------------------------------------------------------------------------------------------
  1821. /**
  1822. * Destroy the current path, and set it to NULL
  1823. */
  1824. void AIUpdateInterface::destroyPath( void )
  1825. {
  1826. // destroy previous path
  1827. if (m_path)
  1828. m_path->deleteInstance();
  1829. m_path = NULL;
  1830. m_waitingForPath = FALSE; // we no longer need it.
  1831. //CRCDEBUG_LOG(("AIUpdateInterface::destroyPath() - m_isAttackPath = FALSE for object %d\n", getObject()->getID()));
  1832. m_isAttackPath = FALSE;
  1833. setLocomotorGoalNone();
  1834. }
  1835. //-------------------------------------------------------------------------------------------------
  1836. /**
  1837. * This is used by the internal move to state to indicate that a move started.
  1838. */
  1839. void AIUpdateInterface::friend_startingMove(void)
  1840. {
  1841. m_movementComplete = FALSE; // we aren't finished moving.
  1842. m_isMoving = TRUE;
  1843. m_blockedFrames = 0;
  1844. m_isBlockedAndStuck = FALSE;
  1845. }
  1846. //-------------------------------------------------------------------------------------------------
  1847. /**
  1848. * This is used by the internal move to state to indicate that a move completed.
  1849. */
  1850. void AIUpdateInterface::friend_endingMove()
  1851. {
  1852. m_movementComplete = TRUE;
  1853. m_isMoving = FALSE;
  1854. }
  1855. //-------------------------------------------------------------------------------------------------
  1856. /**
  1857. * This is used by the jetai to set a specific path.
  1858. */
  1859. void AIUpdateInterface::friend_setPath(Path *path)
  1860. {
  1861. destroyPath();
  1862. m_path = path;
  1863. }
  1864. //-------------------------------------------------------------------------------------------------
  1865. /**
  1866. * This is used by the guard tunnel network state to set a target object.
  1867. */
  1868. void AIUpdateInterface::friend_setGoalObject(Object *obj)
  1869. {
  1870. Bool locked = getStateMachine()->isLocked();
  1871. getStateMachine()->unlock();
  1872. getStateMachine()->setGoalObject(obj);
  1873. if (locked) {
  1874. getStateMachine()->lock("Friend_setGlobalObject re-locking");
  1875. }
  1876. }
  1877. //-------------------------------------------------------------------------------------------------
  1878. /** Is there a path at all that exists from us to the destination location */
  1879. //-------------------------------------------------------------------------------------------------
  1880. Bool AIUpdateInterface::isPathAvailable( const Coord3D *destination ) const
  1881. {
  1882. // sanity
  1883. if( destination == NULL )
  1884. return FALSE;
  1885. const Coord3D *myPos = getObject()->getPosition();
  1886. return TheAI->pathfinder()->clientSafeQuickDoesPathExist( m_locomotorSet, myPos, destination );
  1887. } // end isPathAvailable
  1888. //-------------------------------------------------------------------------------------------------
  1889. /** Is there a path (computed using the less accurate but quick method )
  1890. * at all that exists from us to the destination location */
  1891. //-------------------------------------------------------------------------------------------------
  1892. Bool AIUpdateInterface::isQuickPathAvailable( const Coord3D *destination ) const
  1893. {
  1894. // sanity
  1895. if( destination == NULL )
  1896. return FALSE;
  1897. const Coord3D *myPos = getObject()->getPosition();
  1898. return TheAI->pathfinder()->clientSafeQuickDoesPathExistForUI( m_locomotorSet, myPos, destination );
  1899. } // end isQuickPathAvailable
  1900. //-------------------------------------------------------------------------------------------------
  1901. Bool AIUpdateInterface::isValidLocomotorPosition(const Coord3D* pos) const
  1902. {
  1903. return TheAI->pathfinder()->validMovementPosition( getObject()->getCrusherLevel()>0, getObject()->getLayer(), m_locomotorSet, pos );
  1904. }
  1905. //-------------------------------------------------------------------------------------------------
  1906. DECLARE_PERF_TIMER(doLocomotor)
  1907. /**
  1908. * Compute drive forces
  1909. */
  1910. UpdateSleepTime AIUpdateInterface::doLocomotor( void )
  1911. {
  1912. USE_PERF_TIMER(doLocomotor)
  1913. if (getObject()->isKindOf(KINDOF_IMMOBILE))
  1914. return UPDATE_SLEEP_FOREVER;
  1915. chooseGoodLocomotorFromCurrentSet();
  1916. if (m_isBlocked)
  1917. {
  1918. ++m_blockedFrames;
  1919. }
  1920. else
  1921. {
  1922. m_blockedFrames = 0;
  1923. }
  1924. m_isBlocked = FALSE;
  1925. Bool blocked = m_blockedFrames > 0;
  1926. Bool requiresConstantCalling = TRUE; // assume the worst.
  1927. if (m_curLocomotor)
  1928. {
  1929. m_curLocomotor->setPhysicsOptions(getObject());
  1930. if (isAiInDeadState() && !m_curLocomotor->getLocomotorWorksWhenDead())
  1931. {
  1932. // now it's over, I'm dead, and I haven't done anything that I want,
  1933. // or, I'm still alive, and there's nothing I want to do
  1934. }
  1935. else
  1936. {
  1937. switch (m_locomotorGoalType)
  1938. {
  1939. case POSITION_EXPLICIT:
  1940. {
  1941. Real speed = m_desiredSpeed;
  1942. Real myMaxSpeed = m_curLocomotor->getMaxSpeedForCondition(getObject()->getBodyModule()->getDamageState());
  1943. if( speed == FAST_AS_POSSIBLE || speed > myMaxSpeed )
  1944. speed = myMaxSpeed;
  1945. m_curLocomotor->locoUpdate_moveTowardsPosition(getObject(),
  1946. m_locomotorGoalData, 0.0f, speed, &blocked);
  1947. m_doFinalPosition = FALSE;
  1948. }
  1949. break;
  1950. case POSITION_ON_PATH:
  1951. {
  1952. if (!getPath())
  1953. {
  1954. if (m_waitingForPath)
  1955. {
  1956. return UPDATE_SLEEP_FOREVER; // Can't move till we get our path.
  1957. }
  1958. DEBUG_LOG(("Dead %d, obj %s %x\n", isAiInDeadState(), getObject()->getTemplate()->getName().str(), getObject()));
  1959. #ifdef STATE_MACHINE_DEBUG
  1960. DEBUG_LOG(("Waiting %d, state %s\n", m_waitingForPath, getStateMachine()->getCurrentStateName().str()));
  1961. m_stateMachine->setDebugOutput(1);
  1962. #endif
  1963. DEBUG_CRASH(("must have a path here (doLocomotor)"));
  1964. break;
  1965. }
  1966. Coord3D goalPos;
  1967. Real onPathDistToGoal;
  1968. if (!isDoingGroundMovement())
  1969. {
  1970. // airborne locomotor. Get the goal and distance direct to the goal, don't consider obstacles.
  1971. onPathDistToGoal = getPath()->computeFlightDistToGoal(getObject()->getPosition(), goalPos);
  1972. }
  1973. else
  1974. {
  1975. // Compute the actual goal position along the path to move towards. Consider
  1976. // obstacles, and follow the intermediate path points.
  1977. ClosestPointOnPathInfo info;
  1978. CRCDEBUG_LOG(("AIUpdateInterface::doLocomotor() - calling computePointOnPath() for %s\n",
  1979. DescribeObject(getObject()).str()));
  1980. getPath()->computePointOnPath(getObject(), m_locomotorSet, *getObject()->getPosition(), info);
  1981. onPathDistToGoal = info.distAlongPath;
  1982. goalPos = info.posOnPath;
  1983. // layer is a possible bridge in the path. Check & set the layer if applicable.
  1984. TheAI->pathfinder()->updateLayer(getObject(), info.layer);
  1985. }
  1986. Real speed = m_desiredSpeed;
  1987. Real myMaxSpeed = m_curLocomotor->getMaxSpeedForCondition(getObject()->getBodyModule()->getDamageState());
  1988. if( speed == FAST_AS_POSSIBLE || speed > myMaxSpeed )
  1989. speed = myMaxSpeed;
  1990. if (blocked && speed>m_curMaxBlockedSpeed)
  1991. {
  1992. speed = m_curMaxBlockedSpeed;
  1993. if (m_bumpSpeedLimit>speed) {
  1994. m_bumpSpeedLimit = speed;
  1995. }
  1996. m_bumpSpeedLimit *= 0.95f;
  1997. speed = m_bumpSpeedLimit;
  1998. }
  1999. else
  2000. {
  2001. blocked = FALSE;
  2002. if (m_bumpSpeedLimit<FAST_AS_POSSIBLE) {
  2003. if (m_bumpSpeedLimit<speed*0.2f) {
  2004. m_bumpSpeedLimit = speed*0.2f;
  2005. }
  2006. m_bumpSpeedLimit *= 1.05f;
  2007. }
  2008. if (speed>m_bumpSpeedLimit) {
  2009. speed = m_bumpSpeedLimit;
  2010. }
  2011. }
  2012. m_curLocomotor->locoUpdate_moveTowardsPosition(getObject(), goalPos,
  2013. onPathDistToGoal+getPathExtraDistance(), speed, &blocked);
  2014. m_doFinalPosition = FALSE;
  2015. }
  2016. break;
  2017. case ANGLE:
  2018. {
  2019. m_curLocomotor->locoUpdate_moveTowardsAngle(getObject(), m_locomotorGoalData.x);
  2020. m_doFinalPosition = FALSE;
  2021. }
  2022. break;
  2023. case NONE:
  2024. {
  2025. if (m_doFinalPosition)
  2026. {
  2027. Coord3D pos = *getObject()->getPosition();
  2028. Bool onGround = !getObject()->isAboveTerrain() && getObject()->getLayer() == LAYER_GROUND;
  2029. Real dx = m_finalPosition.x - pos.x;
  2030. Real dy = m_finalPosition.y - pos.y;
  2031. Real dSqr = dx*dx+dy*dy;
  2032. const Real DARN_CLOSE = 0.25f;
  2033. if (dSqr < DARN_CLOSE)
  2034. {
  2035. m_doFinalPosition = FALSE;
  2036. if (onGround)
  2037. m_finalPosition.z = TheTerrainLogic->getGroundHeight( m_finalPosition.x, m_finalPosition.y );
  2038. else
  2039. m_finalPosition.z = pos.z;
  2040. getObject()->setPosition(&m_finalPosition);
  2041. }
  2042. else
  2043. {
  2044. Real dist = sqrtf(dSqr);
  2045. if (dist<1) dist = 1;
  2046. pos.x += 2*PATHFIND_CELL_SIZE_F*dx/(dist*LOGICFRAMES_PER_SECOND);
  2047. pos.y += 2*PATHFIND_CELL_SIZE_F*dy/(dist*LOGICFRAMES_PER_SECOND);
  2048. if (onGround)
  2049. pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y );
  2050. getObject()->setPosition(&pos);
  2051. }
  2052. }
  2053. requiresConstantCalling = m_curLocomotor->locoUpdate_maintainCurrentPosition(getObject());
  2054. }
  2055. break;
  2056. }
  2057. }
  2058. if (!blocked && m_blockedFrames>1)
  2059. {
  2060. m_blockedFrames = 1;
  2061. }
  2062. // After our movement for the frame, update our AirborneTarget flag.
  2063. if(getObject()->getHeightAboveTerrain() > m_curLocomotor->getAirborneTargetingHeight() )
  2064. getObject()->setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_AIRBORNE_TARGET ) );
  2065. else
  2066. getObject()->clearStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_AIRBORNE_TARGET ) );
  2067. m_curMaxBlockedSpeed = FAST_AS_POSSIBLE;
  2068. }
  2069. if (m_curLocomotor != NULL
  2070. && m_locomotorGoalType == NONE
  2071. && m_doFinalPosition == FALSE
  2072. && m_isBlocked == FALSE
  2073. && requiresConstantCalling == FALSE)
  2074. {
  2075. return UPDATE_SLEEP_FOREVER;
  2076. }
  2077. else
  2078. {
  2079. return UPDATE_SLEEP_NONE;
  2080. }
  2081. }
  2082. //-------------------------------------------------------------------------------------------------
  2083. void AIUpdateInterface::setLocomotorGoalPositionOnPath()
  2084. {
  2085. m_locomotorGoalType = POSITION_ON_PATH;
  2086. m_locomotorGoalData.zero();
  2087. }
  2088. //-------------------------------------------------------------------------------------------------
  2089. void AIUpdateInterface::setLocomotorGoalPositionExplicit(const Coord3D& newPos)
  2090. {
  2091. m_locomotorGoalType = POSITION_EXPLICIT;
  2092. m_locomotorGoalData = newPos;
  2093. #ifdef _DEBUG
  2094. if (_isnan(m_locomotorGoalData.x) || _isnan(m_locomotorGoalData.y) || _isnan(m_locomotorGoalData.z))
  2095. {
  2096. DEBUG_CRASH(("NAN in setLocomotorGoalPositionExplicit"));
  2097. }
  2098. #endif
  2099. }
  2100. //-------------------------------------------------------------------------------------------------
  2101. void AIUpdateInterface::setLocomotorGoalOrientation(Real angle)
  2102. {
  2103. m_locomotorGoalType = ANGLE;
  2104. m_locomotorGoalData.x = angle;
  2105. #ifdef _DEBUG
  2106. if (_isnan(m_locomotorGoalData.x) || _isnan(m_locomotorGoalData.y) || _isnan(m_locomotorGoalData.z))
  2107. {
  2108. DEBUG_CRASH(("NAN in setLocomotorGoalOrientation"));
  2109. }
  2110. #endif
  2111. }
  2112. //-------------------------------------------------------------------------------------------------
  2113. void AIUpdateInterface::setLocomotorGoalNone()
  2114. {
  2115. m_locomotorGoalType = NONE;
  2116. }
  2117. //-------------------------------------------------------------------------------------------------
  2118. Bool AIUpdateInterface::isDoingGroundMovement(void) const
  2119. {
  2120. if (getObject()->isDisabledByType( DISABLED_UNMANNED )
  2121. && getObject()->isKindOf( KINDOF_PRODUCED_AT_HELIPAD ) )
  2122. {
  2123. return TRUE; // an unmanned helicopter gets grounded, eventually.
  2124. }
  2125. if (m_locomotorSet.getValidSurfaces() == LOCOMOTORSURFACE_AIR)
  2126. {
  2127. return FALSE; // air only loco.
  2128. }
  2129. if (m_curLocomotor == NULL)
  2130. {
  2131. return FALSE; // No loco, so we aren't moving.
  2132. }
  2133. // Cur loco is air, so not ground.
  2134. if (m_curLocomotor->getLegalSurfaces() & LOCOMOTORSURFACE_AIR)
  2135. {
  2136. return FALSE;
  2137. }
  2138. // We are held, so not moving on ground.
  2139. if( getObject()->isDisabledByType( DISABLED_HELD ) )
  2140. {
  2141. return FALSE;
  2142. }
  2143. // if we're airborne and "allowed to fall", we are probably deliberately in midair
  2144. // due to rappel or accident...
  2145. const PhysicsBehavior* physics = getObject()->getPhysics();
  2146. if (getObject()->isAboveTerrain() && physics != NULL && physics->getAllowToFall())
  2147. {
  2148. return FALSE;
  2149. }
  2150. // After all exceptions, we must be doing ground movement.
  2151. //DEBUG_ASSERTLOG(getObject()->isSignificantlyAboveTerrain(), ("Object %s is significantly airborne but also doing ground movement. What?\n",getObject()->getTemplate()->getName().str()));
  2152. return TRUE;
  2153. }
  2154. //-------------------------------------------------------------------------------------------------
  2155. /** Some aircraft (comanche in particular, which hover) shouldn't stack destinations.
  2156. Others, like missles, should stack destinations. AdjustDestination in pathfinder unstacks
  2157. destinations, and this routine identifies non-ground units that should unstack. */
  2158. Bool AIUpdateInterface::isAircraftThatAdjustsDestination(void) const
  2159. {
  2160. if (m_curLocomotor == NULL)
  2161. {
  2162. return FALSE; // No loco, so we aren't moving.
  2163. }
  2164. if (m_curLocomotor->getAppearance() == LOCO_HOVER)
  2165. {
  2166. return TRUE; // Hover adjusts.
  2167. }
  2168. if (m_curLocomotor->getAppearance() == LOCO_WINGS)
  2169. {
  2170. return TRUE; // wings adjusts.
  2171. }
  2172. if (m_curLocomotor->getAppearance() == LOCO_THRUST)
  2173. {
  2174. return FALSE; // thrust doesn't adjust.
  2175. }
  2176. return FALSE;
  2177. }
  2178. //-------------------------------------------------------------------------------------------------
  2179. Bool AIUpdateInterface::getTreatAsAircraftForLocoDistToGoal() const
  2180. {
  2181. Bool treatAsAircraft = !isDoingGroundMovement();
  2182. if (getPathExtraDistance() > PATHFIND_CLOSE_ENOUGH)
  2183. {
  2184. // We are following a waypoint or other multiple point path, so use the "easy" success criteria.
  2185. treatAsAircraft = TRUE;
  2186. }
  2187. if (m_curLocomotor && m_curLocomotor->getAppearance() == LOCO_HOVER)
  2188. {
  2189. // Hovercrafts are very sloppy. So use aircraft tests for distance to goal. jba.
  2190. treatAsAircraft = TRUE;
  2191. }
  2192. return treatAsAircraft;
  2193. }
  2194. //-------------------------------------------------------------------------------------------------
  2195. Real AIUpdateInterface::getLocomotorDistanceToGoal()
  2196. {
  2197. switch (m_locomotorGoalType)
  2198. {
  2199. case POSITION_EXPLICIT:
  2200. DEBUG_CRASH(("not yet implemented"));
  2201. return 0.0f;
  2202. case POSITION_ON_PATH:
  2203. if (!getPath())
  2204. {
  2205. DEBUG_CRASH(("must have a path here (getLocomotorDistanceToGoal)"));
  2206. return 0.0f;
  2207. }
  2208. else if (!m_curLocomotor)
  2209. {
  2210. //DEBUG_LOG(("no locomotor here, so no dist. (this is ok.)\n"));
  2211. return 0.0f;
  2212. }
  2213. else if( m_curLocomotor->isCloseEnoughDist3D() || getObject()->isKindOf(KINDOF_PROJECTILE))
  2214. {
  2215. const Object *me = getObject();
  2216. const Coord3D *dest = getGoalPosition();
  2217. if (m_path->getLastNode()) {
  2218. dest = m_path->getLastNode()->getPosition();
  2219. }
  2220. Real distance = ThePartitionManager->getDistanceSquared( me, dest, FROM_CENTER_3D );
  2221. return sqrt( distance );// Other paths return dots of normalized vectors, so one sqrt ain't so bad
  2222. }
  2223. else
  2224. {
  2225. Coord3D goalPos;
  2226. Bool treatAsAircraft = getTreatAsAircraftForLocoDistToGoal();
  2227. Real dist;
  2228. if (treatAsAircraft)
  2229. {
  2230. // airborne locomotor. Get the goal and distance direct to the goal, don't consider obstacles.
  2231. dist = getPath()->computeFlightDistToGoal(getObject()->getPosition(), goalPos);
  2232. } else {
  2233. // Ground based locomotor.
  2234. ClosestPointOnPathInfo info;
  2235. CRCDEBUG_LOG(("AIUpdateInterface::getLocomotorDistanceToGoal() - calling computePointOnPath() for object %d\n", getObject()->getID()));
  2236. getPath()->computePointOnPath(getObject(), m_locomotorSet, *getObject()->getPosition(), info);
  2237. goalPos = info.posOnPath;
  2238. dist = info.distAlongPath;
  2239. }
  2240. if (m_path->getLastNode()) {
  2241. goalPos = *m_path->getLastNode()->getPosition();
  2242. }
  2243. // We are trying to get to goal. So,
  2244. // If the actual distance is farther, then use the actual distance so we get there.
  2245. Real dx = goalPos.x - getObject()->getPosition()->x;
  2246. Real dy = goalPos.y - getObject()->getPosition()->y;
  2247. Real distSqr = dx*dx + dy*dy;
  2248. if (treatAsAircraft)
  2249. {
  2250. if (sqr(dist) > distSqr)
  2251. {
  2252. return sqrt(distSqr);
  2253. }
  2254. else
  2255. {
  2256. return dist;
  2257. }
  2258. }
  2259. if (dist<PATHFIND_CELL_SIZE_F || sqr(dist) < distSqr)
  2260. return sqrtf(distSqr);
  2261. else
  2262. return dist;
  2263. }
  2264. case ANGLE:
  2265. case NONE:
  2266. // If it isn't a positional goal, we are there already.
  2267. return 0.0f;
  2268. }
  2269. return 0.0f;
  2270. }
  2271. /**
  2272. * Catch up with the rest of the team.
  2273. */
  2274. void AIUpdateInterface::joinTeam( void )
  2275. {
  2276. // the dead don't listen very well
  2277. if (isAiInDeadState())
  2278. return;
  2279. if (getObject()->isMobile() == FALSE)
  2280. return;
  2281. chooseLocomotorSet(LOCOMOTORSET_NORMAL);
  2282. getStateMachine()->clear();
  2283. getStateMachine()->setGoalWaypoint(NULL);
  2284. Object *obj = getObject();
  2285. Object *other = NULL;
  2286. Team *team = obj->getTeam();
  2287. for (DLINK_ITERATOR<Object> iter = team->iterate_TeamMemberList(); !iter.done(); iter.advance())
  2288. {
  2289. Object *anObj = iter.cur();
  2290. if (!anObj)
  2291. {
  2292. continue;
  2293. }
  2294. if (obj == anObj)
  2295. {
  2296. // it's us.
  2297. continue;
  2298. }
  2299. else if (anObj->getAI())
  2300. {
  2301. if( !anObj->isDisabledByType( DISABLED_HELD ) )
  2302. {
  2303. other = anObj;
  2304. break;
  2305. }
  2306. }
  2307. }
  2308. if (other) {
  2309. AIUpdateInterface* ai = other->getAI();
  2310. if (ai->isIdle()) {
  2311. aiMoveToPosition(other->getPosition(), CMD_FROM_AI);
  2312. return;
  2313. }
  2314. if (ai->getGoalObject()) {
  2315. getStateMachine()->setGoalObject(ai->getGoalObject());
  2316. } else {
  2317. getStateMachine()->setGoalPosition(ai->getGoalPosition());
  2318. }
  2319. StateID state = getCurrentStateID();
  2320. setLastCommandSource( CMD_FROM_AI );
  2321. // Match the state.
  2322. getStateMachine()->setState( state );
  2323. }
  2324. } // end joinTeam
  2325. //-------------------------------------------------------------------------------------------------
  2326. Bool AIUpdateInterface::isAllowedToRespondToAiCommands(const AICommandParms* parms) const
  2327. {
  2328. // the dead don't listen very well
  2329. // (unless they are seeking to feed on the brains of the living)
  2330. // [urrr, need brains]
  2331. if (getObject()->isEffectivelyDead())
  2332. return FALSE;
  2333. // We're catching the sleep mood here. AI Units that are asleep actually ignore all commands.
  2334. // (See the AI Mood matrix for more info)
  2335. UnsignedInt moodParms = getMoodMatrixValue();
  2336. if ((moodParms & MM_Controller_AI) && (moodParms & MM_Mood_Sleep) && (parms->m_cmd != AICMD_MOVE_TO_POSITION_EVEN_IF_SLEEPING))
  2337. return FALSE;
  2338. const AIUpdateModuleData *data = getAIUpdateModuleData();
  2339. Bool forbidden = data->m_forbidPlayerCommands;
  2340. if ( parms->m_cmdSource == CMD_FROM_PLAYER && forbidden )
  2341. return FALSE;
  2342. // THIS IS JUST FOR THE SPECTREGUNSHIP FOR NOW...
  2343. // IT LOCKS OUT USER INPUT,
  2344. // ALLOWING ONLY THE SPECTREUPDATE TO COMMAND IT VIA CMD_FROM_AI
  2345. // AUTHOR, LORENZEN... 5/15/03
  2346. return TRUE;
  2347. }
  2348. //-------------------------------------------------------------------------------------------------
  2349. void AIUpdateInterface::aiDoCommand(const AICommandParms* parms)
  2350. {
  2351. if (!isAllowedToRespondToAiCommands(parms))
  2352. return;
  2353. #ifdef ALLOW_SURRENDER
  2354. // surrendered items have very limited options, and only via AI cmds
  2355. if (isSurrendered())
  2356. {
  2357. if (parms->m_cmdSource != CMD_FROM_AI)
  2358. return;
  2359. switch (parms->m_cmd)
  2360. {
  2361. case AICMD_MOVE_TO_POSITION:
  2362. case AICMD_MOVE_TO_OBJECT:
  2363. case AICMD_IDLE:
  2364. case AICMD_ENTER:
  2365. case AICMD_EXIT:
  2366. break;
  2367. default:
  2368. DEBUG_LOG(("ignoring ai cmd due to surrender condition"));
  2369. return;
  2370. }
  2371. }
  2372. #endif
  2373. switch (parms->m_cmd)
  2374. {
  2375. case AICMD_MOVE_TO_POSITION:
  2376. case AICMD_MOVE_TO_POSITION_EVEN_IF_SLEEPING:
  2377. privateMoveToPosition(&parms->m_pos, parms->m_cmdSource);
  2378. break;
  2379. case AICMD_MOVE_TO_OBJECT:
  2380. privateMoveToObject(parms->m_obj, parms->m_cmdSource);
  2381. break;
  2382. case AICMD_TIGHTEN_TO_POSITION:
  2383. privateTightenToPosition(&parms->m_pos, parms->m_cmdSource);
  2384. break;
  2385. case AICMD_MOVE_TO_POSITION_AND_EVACUATE:
  2386. privateMoveToAndEvacuate(&parms->m_pos, parms->m_cmdSource);
  2387. break;
  2388. case AICMD_MOVE_TO_POSITION_AND_EVACUATE_AND_EXIT:
  2389. privateMoveToAndEvacuateAndExit(&parms->m_pos, parms->m_cmdSource);
  2390. break;
  2391. case AICMD_IDLE:
  2392. privateIdle(parms->m_cmdSource);
  2393. break;
  2394. case AICMD_FOLLOW_WAYPOINT_PATH:
  2395. privateFollowWaypointPath(parms->m_waypoint, parms->m_cmdSource);
  2396. break;
  2397. case AICMD_FOLLOW_WAYPOINT_PATH_AS_TEAM:
  2398. privateFollowWaypointPathAsTeam(parms->m_waypoint, parms->m_cmdSource);
  2399. break;
  2400. case AICMD_FOLLOW_WAYPOINT_PATH_EXACT:
  2401. privateFollowWaypointPathExact(parms->m_waypoint, parms->m_cmdSource);
  2402. break;
  2403. case AICMD_FOLLOW_WAYPOINT_PATH_AS_TEAM_EXACT:
  2404. privateFollowWaypointPathAsTeamExact(parms->m_waypoint, parms->m_cmdSource);
  2405. break;
  2406. case AICMD_FOLLOW_PATH:
  2407. privateFollowPath(&parms->m_coords, parms->m_obj, parms->m_cmdSource, FALSE);
  2408. break;
  2409. case AICMD_FOLLOW_PATH_APPEND:
  2410. privateFollowPathAppend(&parms->m_pos, parms->m_cmdSource);
  2411. break;
  2412. case AICMD_FOLLOW_EXITPRODUCTION_PATH:
  2413. privateFollowPath(&parms->m_coords, parms->m_obj, parms->m_cmdSource, TRUE);
  2414. break;
  2415. case AICMD_ATTACK_OBJECT:
  2416. privateAttackObject(parms->m_obj, parms->m_intValue, parms->m_cmdSource);
  2417. break;
  2418. case AICMD_FORCE_ATTACK_OBJECT:
  2419. privateForceAttackObject(parms->m_obj, parms->m_intValue, parms->m_cmdSource);
  2420. break;
  2421. case AICMD_GUARD_RETALIATE:
  2422. privateGuardRetaliate( parms->m_obj, &parms->m_pos, parms->m_intValue, parms->m_cmdSource );
  2423. break;
  2424. case AICMD_ATTACK_TEAM:
  2425. privateAttackTeam(parms->m_team, parms->m_intValue, parms->m_cmdSource);
  2426. break;
  2427. case AICMD_ATTACK_POSITION:
  2428. privateAttackPosition(&parms->m_pos, parms->m_intValue, parms->m_cmdSource);
  2429. break;
  2430. case AICMD_ATTACKMOVE_TO_POSITION:
  2431. privateAttackMoveToPosition(&parms->m_pos, parms->m_intValue, parms->m_cmdSource);
  2432. break;
  2433. case AICMD_ATTACKFOLLOW_WAYPOINT_PATH:
  2434. privateAttackFollowWaypointPath(parms->m_waypoint, parms->m_intValue, FALSE, parms->m_cmdSource);
  2435. break;
  2436. case AICMD_ATTACKFOLLOW_WAYPOINT_PATH_AS_TEAM:
  2437. privateAttackFollowWaypointPath(parms->m_waypoint, parms->m_intValue, TRUE, parms->m_cmdSource);
  2438. break;
  2439. case AICMD_HUNT:
  2440. privateHunt(parms->m_cmdSource);
  2441. break;
  2442. case AICMD_ATTACK_AREA:
  2443. privateAttackArea(parms->m_polygon, parms->m_cmdSource);
  2444. break;
  2445. case AICMD_REPAIR:
  2446. privateRepair(parms->m_obj, parms->m_cmdSource);
  2447. break;
  2448. #ifdef ALLOW_SURRENDER
  2449. case AICMD_PICK_UP_PRISONER:
  2450. privatePickUpPrisoner( parms->m_obj, parms->m_cmdSource );
  2451. break;
  2452. case AICMD_RETURN_PRISONERS:
  2453. privateReturnPrisoners( parms->m_obj, parms->m_cmdSource );
  2454. break;
  2455. #endif
  2456. case AICMD_RESUME_CONSTRUCTION:
  2457. privateResumeConstruction(parms->m_obj, parms->m_cmdSource);
  2458. break;
  2459. case AICMD_GET_HEALED:
  2460. privateGetHealed(parms->m_obj, parms->m_cmdSource);
  2461. break;
  2462. case AICMD_GET_REPAIRED:
  2463. privateGetRepaired(parms->m_obj, parms->m_cmdSource);
  2464. break;
  2465. case AICMD_ENTER://///////////////////////////////////////////////////////////////
  2466. privateEnter(parms->m_obj, parms->m_cmdSource);
  2467. break;
  2468. case AICMD_DOCK:
  2469. privateDock(parms->m_obj, parms->m_cmdSource);
  2470. break;
  2471. case AICMD_EXIT:////////////////////////////////////////////////////////////////////
  2472. privateExit(parms->m_obj, parms->m_cmdSource);
  2473. break;
  2474. case AICMD_EXIT_INSTANTLY://///////////////////////////////////////////////////////
  2475. privateExitInstantly( parms->m_obj, parms->m_cmdSource );
  2476. break;
  2477. case AICMD_EVACUATE://///////////////////////////////////////////////////////////
  2478. privateEvacuate(parms->m_intValue, parms->m_cmdSource);
  2479. break;
  2480. case AICMD_EVACUATE_INSTANTLY:////////////////////////////////////////////////////
  2481. privateEvacuateInstantly( parms->m_intValue, parms->m_cmdSource );
  2482. break;
  2483. case AICMD_EXECUTE_RAILED_TRANSPORT:
  2484. privateExecuteRailedTransport( parms->m_cmdSource );
  2485. break;
  2486. case AICMD_GO_PRONE:
  2487. privateGoProne(&parms->m_damage, parms->m_cmdSource);
  2488. break;
  2489. case AICMD_GUARD_POSITION:
  2490. {
  2491. //Kris: Aug 18, 2003 -- If you were retaliating and ordered to enter guard mode,
  2492. //the state needs to be cleared before doing so or else we leave the state too
  2493. //late and clear data AFTER we go into the new guard mode causing units to
  2494. //move to zero (bottom left corner).
  2495. AIStateMachine *state = getStateMachine();
  2496. if( state && state->getCurrentStateID() == AI_GUARD_RETALIATE )
  2497. {
  2498. state->clear();
  2499. }
  2500. //end
  2501. privateGuardPosition(&parms->m_pos, (GuardMode)parms->m_intValue, parms->m_cmdSource);
  2502. break;
  2503. }
  2504. case AICMD_GUARD_OBJECT:
  2505. {
  2506. //Kris: Aug 18, 2003 -- If you were retaliating and ordered to enter guard mode,
  2507. //the state needs to be cleared before doing so or else we leave the state too
  2508. //late and clear data AFTER we go into the new guard mode causing units to
  2509. //move to zero (bottom left corner).
  2510. AIStateMachine *state = getStateMachine();
  2511. if( state && state->getCurrentStateID() == AI_GUARD_RETALIATE )
  2512. {
  2513. state->clear();
  2514. }
  2515. //end
  2516. privateGuardObject(parms->m_obj, (GuardMode)parms->m_intValue, parms->m_cmdSource);
  2517. break;
  2518. }
  2519. case AICMD_GUARD_TUNNEL_NETWORK:
  2520. {
  2521. //Kris: Aug 18, 2003 -- If you were retaliating and ordered to enter guard mode,
  2522. //the state needs to be cleared before doing so or else we leave the state too
  2523. //late and clear data AFTER we go into the new guard mode causing units to
  2524. //move to zero (bottom left corner).
  2525. AIStateMachine *state = getStateMachine();
  2526. if( state && state->getCurrentStateID() == AI_GUARD_RETALIATE )
  2527. {
  2528. state->clear();
  2529. }
  2530. //end
  2531. privateGuardTunnelNetwork((GuardMode)parms->m_intValue, parms->m_cmdSource);
  2532. break;
  2533. }
  2534. case AICMD_GUARD_AREA:
  2535. {
  2536. //Kris: Aug 18, 2003 -- If you were retaliating and ordered to enter guard mode,
  2537. //the state needs to be cleared before doing so or else we leave the state too
  2538. //late and clear data AFTER we go into the new guard mode causing units to
  2539. //move to zero (bottom left corner).
  2540. AIStateMachine *state = getStateMachine();
  2541. if( state && state->getCurrentStateID() == AI_GUARD_RETALIATE )
  2542. {
  2543. state->clear();
  2544. }
  2545. //end
  2546. privateGuardArea(parms->m_polygon, (GuardMode)parms->m_intValue, parms->m_cmdSource);
  2547. break;
  2548. }
  2549. case AICMD_HACK_INTERNET:
  2550. privateHackInternet( parms->m_cmdSource );
  2551. break;
  2552. case AICMD_FACE_OBJECT:
  2553. privateFaceObject( parms->m_obj, parms->m_cmdSource );
  2554. break;
  2555. case AICMD_FACE_POSITION:
  2556. privateFacePosition( &parms->m_pos, parms->m_cmdSource );
  2557. break;
  2558. case AICMD_RAPPEL_INTO:
  2559. privateRappelInto( parms->m_obj, parms->m_pos, parms->m_cmdSource );
  2560. break;
  2561. case AICMD_COMBATDROP:
  2562. privateCombatDrop( parms->m_obj, parms->m_pos, parms->m_cmdSource );
  2563. break;
  2564. case AICMD_COMMANDBUTTON:
  2565. privateCommandButton( parms->m_commandButton, parms->m_cmdSource );
  2566. break;
  2567. case AICMD_COMMANDBUTTON_OBJ:
  2568. privateCommandButtonObject( parms->m_commandButton, parms->m_obj, parms->m_cmdSource );
  2569. break;
  2570. case AICMD_COMMANDBUTTON_POS:
  2571. privateCommandButtonPosition( parms->m_commandButton, &parms->m_pos, parms->m_cmdSource );
  2572. break;
  2573. case AICMD_WANDER:
  2574. privateWander( parms->m_waypoint, parms->m_cmdSource );
  2575. break;
  2576. case AICMD_WANDER_IN_PLACE:
  2577. privateWanderInPlace(parms->m_cmdSource);
  2578. break;
  2579. case AICMD_PANIC:
  2580. privatePanic( parms->m_waypoint, parms->m_cmdSource );
  2581. break;
  2582. case AICMD_BUSY:
  2583. privateBusy( parms->m_cmdSource );
  2584. break;
  2585. case AICMD_MOVE_AWAY_FROM_UNIT:
  2586. privateMoveAwayFromUnit( parms->m_obj, parms->m_cmdSource );
  2587. break;
  2588. default:
  2589. DEBUG_CRASH(("unhandled AI command!"));
  2590. break;
  2591. }
  2592. }
  2593. //-------------------------------------------------------------------------------------------------
  2594. // AI Command Interface implementation for AIUpdateInterface
  2595. //
  2596. /**
  2597. * Move to given position(s)
  2598. */
  2599. void AIUpdateInterface::privateMoveToPosition( const Coord3D *pos, CommandSourceType cmdSource )
  2600. {
  2601. if (getObject()->isMobile() == FALSE)
  2602. return;
  2603. //Resetting the locomotor here was initially added for scripting purposes. It has been moved
  2604. //to the responsibility of the script to reset the locomotor before moving. This is needed because
  2605. //other systems (like the battle drone) change the locomotor based on what it's trying to do, and
  2606. //doesn't want to get reset when ordered to move.
  2607. //chooseLocomotorSet(LOCOMOTORSET_NORMAL);
  2608. if (!isIdle() && cmdSource == CMD_FROM_AI) {
  2609. // This is an internally generated move to, and we are in a non-idle state. [8/19/2003]
  2610. // Our state could be the source of this command, so
  2611. // Move for 20 seconds [8/19/2003]
  2612. // Things like attack state don't take kindly to being booted out unceremoniously. jba. [8/19/2003]
  2613. setGoalPositionClipped(pos, cmdSource);
  2614. m_blockedFrames = 0;
  2615. m_isBlocked = FALSE;
  2616. m_isBlockedAndStuck = FALSE;
  2617. getStateMachine()->setTemporaryState(AI_MOVE_TO, LOGICFRAMES_PER_SECOND * 20);
  2618. } else {
  2619. // Normal user or script command, just do it. [8/19/2003]
  2620. getStateMachine()->clear();
  2621. setGoalPositionClipped(pos, cmdSource);
  2622. m_blockedFrames = 0;
  2623. m_isBlocked = FALSE;
  2624. m_isBlockedAndStuck = FALSE;
  2625. setLastCommandSource( cmdSource );
  2626. getStateMachine()->setState( AI_MOVE_TO );
  2627. }
  2628. }
  2629. //-------------------------------------------------------------------------------------------------
  2630. /**
  2631. * Move to given object
  2632. */
  2633. void AIUpdateInterface::privateMoveToObject( Object *obj, CommandSourceType cmdSource )
  2634. {
  2635. // the dead don't listen very well
  2636. if (m_isAiDead)
  2637. return;
  2638. if (getObject()->isMobile() == FALSE)
  2639. return;
  2640. //Resetting the locomotor here was initially added for scripting purposes. It has been moved
  2641. //to the responsibility of the script to reset the locomotor before moving. This is needed because
  2642. //other systems (like the battle drone) change the locomotor based on what it's trying to do, and
  2643. //doesn't want to get reset when ordered to move.
  2644. //chooseLocomotorSet(LOCOMOTORSET_NORMAL);
  2645. getStateMachine()->clear();
  2646. getStateMachine()->setGoalObject( obj );
  2647. m_blockedFrames = 0;
  2648. m_isBlocked = FALSE;
  2649. m_isBlockedAndStuck = FALSE;
  2650. setLastCommandSource( cmdSource );
  2651. getStateMachine()->setState( AI_MOVE_TO );
  2652. }
  2653. //----------------------------------------------------------------------------------------
  2654. // Face a specified object -- succeed when facing
  2655. //----------------------------------------------------------------------------------------
  2656. void AIUpdateInterface::privateFaceObject( Object *obj, CommandSourceType cmdSource )
  2657. {
  2658. if( !getObject()->isMobile() )
  2659. {
  2660. return;
  2661. }
  2662. //Resetting the locomotor here was initially added for scripting purposes. It has been moved
  2663. //to the responsibility of the script to reset the locomotor before moving. This is needed because
  2664. //other systems (like the battle drone) change the locomotor based on what it's trying to do, and
  2665. //doesn't want to get reset when ordered to move.
  2666. //chooseLocomotorSet(LOCOMOTORSET_NORMAL);
  2667. getStateMachine()->clear();
  2668. getStateMachine()->setGoalObject( obj );
  2669. m_blockedFrames = 0;
  2670. m_isBlocked = FALSE;
  2671. m_isBlockedAndStuck = FALSE;
  2672. setLastCommandSource( cmdSource );
  2673. getStateMachine()->setState( AI_FACE_OBJECT );
  2674. }
  2675. //----------------------------------------------------------------------------------------
  2676. // Face a specified position -- succeed when facing
  2677. //----------------------------------------------------------------------------------------
  2678. void AIUpdateInterface::privateFacePosition( const Coord3D *pos, CommandSourceType cmdSource )
  2679. {
  2680. if( !getObject()->isMobile() )
  2681. {
  2682. return;
  2683. }
  2684. //Resetting the locomotor here was initially added for scripting purposes. It has been moved
  2685. //to the responsibility of the script to reset the locomotor before moving. This is needed because
  2686. //other systems (like the battle drone) change the locomotor based on what it's trying to do, and
  2687. //doesn't want to get reset when ordered to move.
  2688. //chooseLocomotorSet(LOCOMOTORSET_NORMAL);
  2689. getStateMachine()->clear();
  2690. setGoalPositionClipped(pos, cmdSource);
  2691. m_blockedFrames = 0;
  2692. m_isBlocked = FALSE;
  2693. m_isBlockedAndStuck = FALSE;
  2694. setLastCommandSource( cmdSource );
  2695. getStateMachine()->setState( AI_FACE_POSITION );
  2696. }
  2697. //----------------------------------------------------------------------------------------
  2698. // Rappel into target and devastate contents (if not empty).
  2699. // If target is null, rappel to ground.
  2700. //----------------------------------------------------------------------------------------
  2701. void AIUpdateInterface::privateRappelInto( Object *target, const Coord3D& pos, CommandSourceType cmdSource )
  2702. {
  2703. //Resetting the locomotor here was initially added for scripting purposes. It has been moved
  2704. //to the responsibility of the script to reset the locomotor before moving. This is needed because
  2705. //other systems (like the battle drone) change the locomotor based on what it's trying to do, and
  2706. //doesn't want to get reset when ordered to move.
  2707. //chooseLocomotorSet(LOCOMOTORSET_NORMAL);
  2708. getStateMachine()->clear();
  2709. getStateMachine()->setGoalObject( target );
  2710. setGoalPositionClipped(&pos, cmdSource);
  2711. m_blockedFrames = 0;
  2712. m_isBlocked = FALSE;
  2713. m_isBlockedAndStuck = FALSE;
  2714. setLastCommandSource( cmdSource );
  2715. getStateMachine()->setState( AI_RAPPEL_INTO );
  2716. }
  2717. //----------------------------------------------------------------------------------------
  2718. /**
  2719. * Move to given position(s)
  2720. * If transportExits, transport returns and deletes itself.
  2721. */
  2722. void AIUpdateInterface::privateMoveToAndEvacuate( const Coord3D *pos, CommandSourceType cmdSource )
  2723. {
  2724. if (getObject()->isMobile() == FALSE)
  2725. return;
  2726. //Resetting the locomotor here was initially added for scripting purposes. It has been moved
  2727. //to the responsibility of the script to reset the locomotor before moving. This is needed because
  2728. //other systems (like the battle drone) change the locomotor based on what it's trying to do, and
  2729. //doesn't want to get reset when ordered to move.
  2730. //chooseLocomotorSet(LOCOMOTORSET_NORMAL);
  2731. getStateMachine()->clear();
  2732. setGoalPositionClipped(pos, cmdSource);
  2733. m_blockedFrames = 0;
  2734. m_isBlocked = FALSE;
  2735. m_isBlockedAndStuck = FALSE;
  2736. setLastCommandSource( cmdSource );
  2737. m_stateMachine->setState( AI_MOVE_AND_EVACUATE );
  2738. }
  2739. //----------------------------------------------------------------------------------------
  2740. /**
  2741. * Move to given position(s)
  2742. * If transportExits, transport returns and deletes itself.
  2743. */
  2744. void AIUpdateInterface::privateMoveToAndEvacuateAndExit( const Coord3D *pos, CommandSourceType cmdSource )
  2745. {
  2746. if (getObject()->isMobile() == FALSE)
  2747. return;
  2748. //Resetting the locomotor here was initially added for scripting purposes. It has been moved
  2749. //to the responsibility of the script to reset the locomotor before moving. This is needed because
  2750. //other systems (like the battle drone) change the locomotor based on what it's trying to do, and
  2751. //doesn't want to get reset when ordered to move.
  2752. //chooseLocomotorSet(LOCOMOTORSET_NORMAL);
  2753. getStateMachine()->clear();
  2754. setGoalPositionClipped(pos, cmdSource);
  2755. m_blockedFrames = 0;
  2756. m_isBlocked = FALSE;
  2757. m_isBlockedAndStuck = FALSE;
  2758. setLastCommandSource( cmdSource );
  2759. static NameKeyType key_DeliverPayloadAIUpdate = NAMEKEY("DeliverPayloadAIUpdate");
  2760. DeliverPayloadAIUpdate *dp = (DeliverPayloadAIUpdate*)getObject()->findUpdateModule( key_DeliverPayloadAIUpdate );
  2761. if( dp )
  2762. {
  2763. dp->deliverPayloadViaModuleData( pos );
  2764. }
  2765. else
  2766. {
  2767. getStateMachine()->setState( AI_MOVE_AND_EVACUATE_AND_EXIT);
  2768. }
  2769. }
  2770. //----------------------------------------------------------------------------------------
  2771. /**
  2772. * Enter idle state.
  2773. */
  2774. void AIUpdateInterface::privateIdle(CommandSourceType cmdSource)
  2775. {
  2776. if (getObject()->isKindOf(KINDOF_PROJECTILE))
  2777. return;
  2778. getStateMachine()->clear();
  2779. getStateMachine()->setState( AI_IDLE );
  2780. setLastCommandSource( cmdSource );
  2781. ContainModuleInterface *contain = getObject()->getContain();
  2782. if (contain)
  2783. {
  2784. const ContainedItemsList* items = contain->getContainedItemsList();
  2785. if (items)
  2786. {
  2787. for (ContainedItemsList::const_iterator it = items->begin(); it != items->end(); ++it)
  2788. {
  2789. Object* obj = *it;
  2790. AIUpdateInterface* ai = obj ? obj->getAI() : NULL;
  2791. if (ai)
  2792. ai->aiIdle(cmdSource);
  2793. }
  2794. }
  2795. }
  2796. }
  2797. //----------------------------------------------------------------------------------------
  2798. Bool AIUpdateInterface::isIdle() const
  2799. {
  2800. const AIStateMachine *state = getStateMachine();
  2801. if( state->getCurrentStateID() == AI_IDLE )
  2802. {
  2803. return TRUE;
  2804. }
  2805. return state->isInIdleState();
  2806. }
  2807. //----------------------------------------------------------------------------------------
  2808. Bool AIUpdateInterface::isAttacking() const
  2809. {
  2810. return getStateMachine()->isInAttackState();
  2811. }
  2812. //----------------------------------------------------------------------------------------
  2813. //Definition of busy -- when explicitly in the busy state. Moving or attacking is not considered busy!
  2814. //----------------------------------------------------------------------------------------
  2815. Bool AIUpdateInterface::isBusy() const
  2816. {
  2817. return getStateMachine()->isInBusyState();
  2818. }
  2819. //----------------------------------------------------------------------------------------
  2820. Bool AIUpdateInterface::isClearingMines() const
  2821. {
  2822. // if we are attacking with an anti-mine weapon, we are clearing mines, regardless
  2823. // of our target.
  2824. if (!getObject()->testStatus(OBJECT_STATUS_IS_ATTACKING))
  2825. return FALSE;
  2826. const Weapon* weapon = getObject()->getCurrentWeapon();
  2827. if (!weapon)
  2828. return FALSE;
  2829. if ((weapon->getAntiMask() & WEAPON_ANTI_MINE) == 0)
  2830. return FALSE;
  2831. return TRUE;
  2832. }
  2833. //----------------------------------------------------------------------------------------
  2834. /**
  2835. * Take the shortest path towards pos in order to tighten up a formation
  2836. */
  2837. void AIUpdateInterface::privateTightenToPosition( const Coord3D *pos, CommandSourceType cmdSource )
  2838. {
  2839. if (getObject()->isMobile() == FALSE)
  2840. return;
  2841. getStateMachine()->clear();
  2842. getStateMachine()->setGoalObject( NULL );
  2843. setGoalPositionClipped(pos, cmdSource);
  2844. setLastCommandSource( cmdSource );
  2845. getStateMachine()->setState( AI_MOVE_AND_TIGHTEN );
  2846. }
  2847. //----------------------------------------------------------------------------------------
  2848. /**
  2849. * Is this moving out of the way of another unit.
  2850. */
  2851. Bool AIUpdateInterface::isMovingAwayFrom(Object *obj) const
  2852. {
  2853. ObjectID id = obj->getID();
  2854. if (m_stateMachine->getTemporaryState() == AI_MOVE_OUT_OF_THE_WAY) {
  2855. if (m_moveOutOfWay1 == id) return TRUE;
  2856. if (m_moveOutOfWay2 == id) return TRUE;
  2857. }
  2858. return FALSE;
  2859. }
  2860. //----------------------------------------------------------------------------------------
  2861. /**
  2862. * Is this moving out of the way of another unit.
  2863. */
  2864. Bool AIUpdateInterface::isMoving() const
  2865. {
  2866. if (isIdle()) {
  2867. return false;
  2868. }
  2869. if (m_locomotorGoalType != NONE) {
  2870. return TRUE;
  2871. }
  2872. if (m_isMoving) {
  2873. return TRUE;
  2874. }
  2875. return FALSE;
  2876. }
  2877. //----------------------------------------------------------------------------------------
  2878. /**
  2879. * Move out of the way of another unit.
  2880. */
  2881. void AIUpdateInterface::privateMoveAwayFromUnit( Object *unit, CommandSourceType cmdSource )
  2882. {
  2883. // the dead don't listen very well
  2884. if (isAiInDeadState() || (getObject()->isMobile() == FALSE) || !isAllowedToMoveAwayFromUnit())
  2885. {
  2886. return;
  2887. }
  2888. ObjectID id = unit->getID();
  2889. if (m_stateMachine->getTemporaryState() == AI_MOVE_OUT_OF_THE_WAY) {
  2890. if (m_moveOutOfWay1 == id) {
  2891. if (m_isBlocked) {
  2892. setIgnoreCollisionTime(LOGICFRAMES_PER_SECOND*2); // cheat for 2 seconds.
  2893. }
  2894. return;
  2895. }
  2896. if (m_moveOutOfWay2 == id) {
  2897. if (m_isBlocked) {
  2898. setIgnoreCollisionTime(LOGICFRAMES_PER_SECOND*2); // cheat for 2 seconds.
  2899. }
  2900. return;
  2901. }
  2902. }
  2903. m_moveOutOfWay2 = m_moveOutOfWay1;
  2904. m_moveOutOfWay1 = id;
  2905. Object *obj2 = TheGameLogic->findObjectByID(m_moveOutOfWay2);
  2906. Path *path2 = NULL;
  2907. if (obj2 && obj2->getAI()) {
  2908. path2 = obj2->getAI()->getPath();
  2909. }
  2910. Path* unitPath = NULL;
  2911. if (unit && unit->getAI()) {
  2912. unitPath = unit->getAI()->getPath();
  2913. }
  2914. if (unitPath == NULL) return;
  2915. Path *newPath = TheAI->pathfinder()->getMoveAwayFromPath(getObject(), unit, unitPath, obj2, path2);
  2916. if (newPath==NULL && !canPathThroughUnits()) {
  2917. setCanPathThroughUnits(TRUE);
  2918. newPath = TheAI->pathfinder()->getMoveAwayFromPath(getObject(), unit, unitPath, obj2, path2);
  2919. }
  2920. if (newPath) {
  2921. destroyPath();
  2922. m_path = newPath;
  2923. wakeUpNow();
  2924. m_stateMachine->setTemporaryState(AI_MOVE_OUT_OF_THE_WAY, 10*LOGICFRAMES_PER_SECOND);
  2925. if (m_path)
  2926. {
  2927. if( !getObject()->isKindOf(KINDOF_NO_COLLIDE))// If I don't collide with things, I don't need to tell them to get out of the way
  2928. TheAI->pathfinder()->moveAllies(getObject(), m_path);
  2929. }
  2930. }
  2931. }
  2932. //----------------------------------------------------------------------------------------
  2933. /**
  2934. * Start following the path from the given point
  2935. */
  2936. void AIUpdateInterface::privateFollowWaypointPath( const Waypoint *way, CommandSourceType cmdSource )
  2937. {
  2938. if (getObject()->isMobile() == FALSE)
  2939. return;
  2940. //Resetting the locomotor here was initially added for scripting purposes. It has been moved
  2941. //to the responsibility of the script to reset the locomotor before moving. This is needed because
  2942. //other systems (like the battle drone) change the locomotor based on what it's trying to do, and
  2943. //doesn't want to get reset when ordered to move.
  2944. //chooseLocomotorSet(LOCOMOTORSET_NORMAL);
  2945. getStateMachine()->clear();
  2946. getStateMachine()->setGoalWaypoint( way );
  2947. setLastCommandSource( cmdSource );
  2948. getStateMachine()->setState( AI_FOLLOW_WAYPOINT_PATH_AS_INDIVIDUALS );
  2949. }
  2950. //----------------------------------------------------------------------------------------
  2951. /**
  2952. * Start following the path from the given point
  2953. */
  2954. void AIUpdateInterface::privateFollowWaypointPathExact( const Waypoint *way, CommandSourceType cmdSource )
  2955. {
  2956. if (getObject()->isMobile() == FALSE)
  2957. return;
  2958. //Resetting the locomotor here was initially added for scripting purposes. It has been moved
  2959. //to the responsibility of the script to reset the locomotor before moving. This is needed because
  2960. //other systems (like the battle drone) change the locomotor based on what it's trying to do, and
  2961. //doesn't want to get reset when ordered to move.
  2962. //chooseLocomotorSet(LOCOMOTORSET_NORMAL);
  2963. getStateMachine()->clear();
  2964. getStateMachine()->setGoalWaypoint( way );
  2965. setLastCommandSource( cmdSource );
  2966. getStateMachine()->setState( AI_FOLLOW_WAYPOINT_PATH_AS_INDIVIDUALS_EXACT );
  2967. }
  2968. //----------------------------------------------------------------------------------------
  2969. /**
  2970. * Start following the path from the given point
  2971. */
  2972. void AIUpdateInterface::privateFollowWaypointPathAsTeam( const Waypoint *way, CommandSourceType cmdSource )
  2973. {
  2974. if (getObject()->isMobile() == FALSE)
  2975. return;
  2976. //Resetting the locomotor here was initially added for scripting purposes. It has been moved
  2977. //to the responsibility of the script to reset the locomotor before moving. This is needed because
  2978. //other systems (like the battle drone) change the locomotor based on what it's trying to do, and
  2979. //doesn't want to get reset when ordered to move.
  2980. //chooseLocomotorSet(LOCOMOTORSET_NORMAL);
  2981. getStateMachine()->clear();
  2982. getStateMachine()->setGoalWaypoint( way );
  2983. setLastCommandSource( cmdSource );
  2984. getStateMachine()->setState( AI_FOLLOW_WAYPOINT_PATH_AS_TEAM );
  2985. }
  2986. //----------------------------------------------------------------------------------------
  2987. /**
  2988. * Start following the path from the given point
  2989. */
  2990. void AIUpdateInterface::privateFollowWaypointPathAsTeamExact( const Waypoint *way, CommandSourceType cmdSource )
  2991. {
  2992. if (getObject()->isMobile() == FALSE)
  2993. return;
  2994. //Resetting the locomotor here was initially added for scripting purposes. It has been moved
  2995. //to the responsibility of the script to reset the locomotor before moving. This is needed because
  2996. //other systems (like the battle drone) change the locomotor based on what it's trying to do, and
  2997. //doesn't want to get reset when ordered to move.
  2998. //chooseLocomotorSet(LOCOMOTORSET_NORMAL);
  2999. getStateMachine()->clear();
  3000. getStateMachine()->setGoalWaypoint( way );
  3001. setLastCommandSource( cmdSource );
  3002. getStateMachine()->setState( AI_FOLLOW_WAYPOINT_PATH_AS_TEAM_EXACT );
  3003. }
  3004. //----------------------------------------------------------------------------------------
  3005. void AIUpdateInterface::privateFollowPathAppend( const Coord3D *pos, CommandSourceType cmdSource )
  3006. {
  3007. // We're adding a dynamic waypoint!
  3008. Bool effectivelyMoving = isMoving() || isWaitingForPath();
  3009. if (getAIStateType() == AI_FOLLOW_PATH && getStateMachine()->getGoalPathSize() > 0 && effectivelyMoving)
  3010. {
  3011. //We already have a path, so simply add the point to the end of it!
  3012. getStateMachine()->addToGoalPath(pos);
  3013. }
  3014. else if (effectivelyMoving)
  3015. {
  3016. //Our unit is moving to a point already so simply add our waypoint after that point
  3017. //and convert it to a waypoint command!
  3018. std::vector<Coord3D> path;
  3019. path.push_back( *getGoalPosition() );
  3020. path.push_back( *pos );
  3021. privateFollowPath( &path, NULL, cmdSource, false );
  3022. }
  3023. else
  3024. {
  3025. //Hopefully we're idle or doing something that doesn't require movement.
  3026. std::vector<Coord3D> path;
  3027. path.push_back( *pos );
  3028. privateFollowPath( &path, NULL, cmdSource, false );
  3029. }
  3030. }
  3031. //----------------------------------------------------------------------------------------
  3032. /**
  3033. * Follow the path defined by the given array of points
  3034. */
  3035. void AIUpdateInterface::privateFollowPath( const std::vector<Coord3D>* path, Object *ignoreObject, CommandSourceType cmdSource, Bool exitProduction )
  3036. {
  3037. if (getObject()->isMobile() == FALSE)
  3038. return;
  3039. //Resetting the locomotor here was initially added for scripting purposes. It has been moved
  3040. //to the responsibility of the script to reset the locomotor before moving. This is needed because
  3041. //other systems (like the battle drone) change the locomotor based on what it's trying to do, and
  3042. //doesn't want to get reset when ordered to move.
  3043. //chooseLocomotorSet(LOCOMOTORSET_NORMAL);
  3044. // clear current state machine
  3045. getStateMachine()->clear();
  3046. if (path->size()>0) {
  3047. const Coord3D goal = (*path)[path->size()-1];
  3048. getStateMachine()->setGoalPosition(&goal);
  3049. }
  3050. // set path info
  3051. getStateMachine()->setGoalPath( path );
  3052. // set the command source
  3053. setLastCommandSource( cmdSource );
  3054. ignoreObstacle(ignoreObject);
  3055. // start us following
  3056. getStateMachine()->setState( exitProduction ? AI_FOLLOW_EXITPRODUCTION_PATH : AI_FOLLOW_PATH );
  3057. }
  3058. //----------------------------------------------------------------------------------------
  3059. /**
  3060. * Attack given object
  3061. */
  3062. void AIUpdateInterface::privateAttackObject( Object *victim, Int maxShotsToFire, CommandSourceType cmdSource )
  3063. {
  3064. //Resetting the locomotor here was initially added for scripting purposes. It has been moved
  3065. //to the responsibility of the script to reset the locomotor before moving. This is needed because
  3066. //other systems (like the battle drone) change the locomotor based on what it's trying to do, and
  3067. //doesn't want to get reset when ordered to move.
  3068. //chooseLocomotorSet(LOCOMOTORSET_NORMAL);
  3069. if (!victim)
  3070. {
  3071. // Hard to kill em if they're already dead. jba
  3072. return;
  3073. }
  3074. getStateMachine()->clear();
  3075. getStateMachine()->setGoalObject( victim );
  3076. setLastCommandSource( cmdSource );
  3077. getStateMachine()->setState( AI_ATTACK_OBJECT );
  3078. // do this after setting it as the current state, as the max-shots-to-fire is reset in AttackState::onEnter()
  3079. Weapon* weapon = getObject()->getCurrentWeapon();
  3080. if (weapon)
  3081. weapon->setMaxShotCount(maxShotsToFire);
  3082. }
  3083. //-----------------------------------------------------------------------------------------
  3084. void AIUpdateInterface::privateForceAttackObject( Object *victim, Int maxShotsToFire, CommandSourceType cmdSource )
  3085. {
  3086. if (!victim) {
  3087. return;
  3088. }
  3089. getStateMachine()->clear();
  3090. getStateMachine()->setGoalObject( victim );
  3091. setLastCommandSource( cmdSource );
  3092. getStateMachine()->setState( AI_FORCE_ATTACK_OBJECT );
  3093. // do this after setting it as the current state, as the max-shots-to-fire is reset in AttackState::onEnter()
  3094. Weapon* weapon = getObject()->getCurrentWeapon();
  3095. if (weapon)
  3096. weapon->setMaxShotCount(maxShotsToFire);
  3097. }
  3098. //-----------------------------------------------------------------------------------------
  3099. void AIUpdateInterface::privateGuardRetaliate( Object *victim, const Coord3D *pos, Int maxShotsToFire, CommandSourceType cmdSource )
  3100. {
  3101. if (!victim) {
  3102. return;
  3103. }
  3104. getStateMachine()->clear();
  3105. getStateMachine()->setGoalObject( victim );
  3106. setGoalPositionClipped( pos, cmdSource );
  3107. setLastCommandSource( cmdSource );
  3108. getStateMachine()->setState( AI_GUARD_RETALIATE );
  3109. // do this after setting it as the current state, as the max-shots-to-fire is reset in AttackState::onEnter()
  3110. Weapon* weapon = getObject()->getCurrentWeapon();
  3111. if (weapon)
  3112. weapon->setMaxShotCount(maxShotsToFire);
  3113. }
  3114. //----------------------------------------------------------------------------------------
  3115. /**
  3116. * Attack the given team
  3117. */
  3118. void AIUpdateInterface::privateAttackTeam( const Team *team, Int maxShotsToFire, CommandSourceType cmdSource )
  3119. {
  3120. //Resetting the locomotor here was initially added for scripting purposes. It has been moved
  3121. //to the responsibility of the script to reset the locomotor before moving. This is needed because
  3122. //other systems (like the battle drone) change the locomotor based on what it's trying to do, and
  3123. //doesn't want to get reset when ordered to move.
  3124. //chooseLocomotorSet(LOCOMOTORSET_NORMAL);
  3125. getStateMachine()->clear();
  3126. getStateMachine()->setGoalTeam( team );
  3127. setLastCommandSource( cmdSource );
  3128. getStateMachine()->setState( AI_ATTACK_SQUAD );
  3129. // do this after setting it as the current state, as the max-shots-to-fire is reset in AttackState::onEnter()
  3130. Weapon* weapon = getObject()->getCurrentWeapon();
  3131. if (weapon)
  3132. weapon->setMaxShotCount(maxShotsToFire);
  3133. }
  3134. //----------------------------------------------------------------------------------------
  3135. /**
  3136. * Attack given spot
  3137. */
  3138. void AIUpdateInterface::privateAttackPosition( const Coord3D *pos, Int maxShotsToFire, CommandSourceType cmdSource )
  3139. {
  3140. //Resetting the locomotor here was initially added for scripting purposes. It has been moved
  3141. //to the responsibility of the script to reset the locomotor before moving. This is needed because
  3142. //other systems (like the battle drone) change the locomotor based on what it's trying to do, and
  3143. //doesn't want to get reset when ordered to move.
  3144. //chooseLocomotorSet(LOCOMOTORSET_NORMAL);
  3145. Coord3D localPos = *pos;
  3146. pos = NULL;
  3147. // ick... rather grody hack for disarming stuff. if we attack a position,
  3148. // but have a "continue range" for the weapon, try to find a suitable object
  3149. // to attack first.
  3150. Weapon* weapon = getObject()->getCurrentWeapon();
  3151. Real continueRange = weapon ? weapon->getContinueAttackRange() : 0.0f;
  3152. if (continueRange > 0.0f)
  3153. {
  3154. // ick. set this bit so we can find the mine to go target, even if stealthed. (srj)
  3155. getObject()->setStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_IGNORING_STEALTH ) );
  3156. PartitionFilterPossibleToAttack filterAttack(ATTACK_NEW_TARGET, getObject(), cmdSource);
  3157. PartitionFilterSameMapStatus filterMapStatus(getObject());
  3158. PartitionFilter *filters[] = { &filterAttack, &filterMapStatus, NULL };
  3159. Object* victim = ThePartitionManager->getClosestObject(&localPos, continueRange, FROM_CENTER_2D, filters);
  3160. getObject()->clearStatus( MAKE_OBJECT_STATUS_MASK( OBJECT_STATUS_IGNORING_STEALTH ) );
  3161. if (victim)
  3162. {
  3163. aiAttackObject(victim, maxShotsToFire, cmdSource);
  3164. return;
  3165. }
  3166. else
  3167. {
  3168. // limit 'em to one shot, and fall thru.
  3169. maxShotsToFire = 1;
  3170. }
  3171. }
  3172. // if it's a contact weapon, we must be able to path to the target pos. if not, find a spot close by.
  3173. // this fixes an obscure bug with mine-clearing: if you tell someone to clear mines and put the centerpoint
  3174. // inside a building, the dozer/worker will just go thru the building to that spot. ick. so if you find that
  3175. // this clause (below) is problematic, you'll probbaly have to find another way to fix this mine-clearing bug. (srj)
  3176. if (weapon && weapon->isContactWeapon() && !isPathAvailable(&localPos))
  3177. {
  3178. FindPositionOptions fpOptions;
  3179. fpOptions.minRadius = 0.0f;
  3180. fpOptions.maxRadius = 100.0f;
  3181. fpOptions.sourceToPathToDest = getObject();// This makes it find a place forWhom can get to.
  3182. Coord3D tmp;
  3183. if (ThePartitionManager->findPositionAround(&localPos, &fpOptions, &tmp))
  3184. localPos = tmp;
  3185. }
  3186. getStateMachine()->clear();
  3187. destroyPath();
  3188. setGoalPositionClipped(&localPos, cmdSource);
  3189. setLastCommandSource( cmdSource );
  3190. getStateMachine()->setState( AI_ATTACK_POSITION );
  3191. //Set the goal object to NULL because if we are attacking a location, we need to be able to move up to it properly.
  3192. //When this isn't set, the move aborts before getting into firing range, thus deadlocks.
  3193. getStateMachine()->setGoalObject( NULL );
  3194. // do this after setting it as the current state, as the max-shots-to-fire is reset in AttackState::onEnter()
  3195. weapon = getObject()->getCurrentWeapon();
  3196. if (weapon)
  3197. weapon->setMaxShotCount(maxShotsToFire);
  3198. }
  3199. //----------------------------------------------------------------------------------------
  3200. /**
  3201. * Attack move to the given location
  3202. */
  3203. void AIUpdateInterface::privateAttackMoveToPosition( const Coord3D *pos, Int maxShotsToFire, CommandSourceType cmdSource )
  3204. {
  3205. if (m_isAiDead || getObject()->isMobile() == FALSE)
  3206. return;
  3207. //Resetting the locomotor here was initially added for scripting purposes. It has been moved
  3208. //to the responsibility of the script to reset the locomotor before moving. This is needed because
  3209. //other systems (like the battle drone) change the locomotor based on what it's trying to do, and
  3210. //doesn't want to get reset when ordered to move.
  3211. //chooseLocomotorSet(LOCOMOTORSET_NORMAL);
  3212. getStateMachine()->clear();
  3213. setGoalPositionClipped(pos, cmdSource);
  3214. setLastCommandSource( cmdSource );
  3215. getStateMachine()->setState( AI_ATTACK_MOVE_TO );
  3216. // do this after setting it as the current state, as the max-shots-to-fire is reset in AttackState::onEnter()
  3217. Weapon* weapon = getObject()->getCurrentWeapon();
  3218. if (weapon)
  3219. weapon->setMaxShotCount(maxShotsToFire);
  3220. }
  3221. //----------------------------------------------------------------------------------------
  3222. /**
  3223. * Attack move down a given waypoint path. If asTeam is TRUE, do so as a team.
  3224. */
  3225. void AIUpdateInterface::privateAttackFollowWaypointPath( const Waypoint *way, Int maxShotsToFire, Bool asTeam, CommandSourceType cmdSource )
  3226. {
  3227. if (m_isAiDead || getObject()->isMobile() == FALSE)
  3228. return;
  3229. //Resetting the locomotor here was initially added for scripting purposes. It has been moved
  3230. //to the responsibility of the script to reset the locomotor before moving. This is needed because
  3231. //other systems (like the battle drone) change the locomotor based on what it's trying to do, and
  3232. //doesn't want to get reset when ordered to move.
  3233. //chooseLocomotorSet(LOCOMOTORSET_NORMAL);
  3234. getStateMachine()->clear();
  3235. getStateMachine()->setGoalWaypoint( way );
  3236. setLastCommandSource( cmdSource );
  3237. getStateMachine()->setState( (asTeam ? AI_ATTACKFOLLOW_WAYPOINT_PATH_AS_TEAM : AI_ATTACKFOLLOW_WAYPOINT_PATH_AS_INDIVIDUALS) );
  3238. // do this after setting it as the current state, as the max-shots-to-fire is reset in AttackState::onEnter()
  3239. Weapon* weapon = getObject()->getCurrentWeapon();
  3240. if (weapon)
  3241. weapon->setMaxShotCount(maxShotsToFire);
  3242. }
  3243. //----------------------------------------------------------------------------------------
  3244. /**
  3245. * Begin "seek and destroy"
  3246. */
  3247. void AIUpdateInterface::privateHunt( CommandSourceType cmdSource )
  3248. {
  3249. if (getObject()->isMobile() == FALSE)
  3250. return;
  3251. if (getObject()->isKindOf(KINDOF_PROJECTILE))
  3252. return;
  3253. //Resetting the locomotor here was initially added for scripting purposes. It has been moved
  3254. //to the responsibility of the script to reset the locomotor before moving. This is needed because
  3255. //other systems (like the battle drone) change the locomotor based on what it's trying to do, and
  3256. //doesn't want to get reset when ordered to move.
  3257. //chooseLocomotorSet(LOCOMOTORSET_NORMAL);
  3258. getStateMachine()->clear();
  3259. setLastCommandSource( cmdSource );
  3260. getStateMachine()->setState( AI_HUNT );
  3261. }
  3262. //----------------------------------------------------------------------------------------
  3263. /**
  3264. * Begin "seek and destroy"
  3265. */
  3266. void AIUpdateInterface::privateAttackArea( const PolygonTrigger *areaToGuard, CommandSourceType cmdSource )
  3267. {
  3268. if (getObject()->isMobile() == FALSE)
  3269. return;
  3270. if (getObject()->isKindOf(KINDOF_PROJECTILE))
  3271. return;
  3272. m_areaToGuard = areaToGuard;
  3273. //Resetting the locomotor here was initially added for scripting purposes. It has been moved
  3274. //to the responsibility of the script to reset the locomotor before moving. This is needed because
  3275. //other systems (like the battle drone) change the locomotor based on what it's trying to do, and
  3276. //doesn't want to get reset when ordered to move.
  3277. //chooseLocomotorSet(LOCOMOTORSET_NORMAL);
  3278. getStateMachine()->clear();
  3279. setLastCommandSource( cmdSource );
  3280. getStateMachine()->setState( AI_ATTACK_AREA);
  3281. }
  3282. //----------------------------------------------------------------------------------------
  3283. /**
  3284. * Repair the given object
  3285. */
  3286. void AIUpdateInterface::privateRepair( Object *obj, CommandSourceType cmdSource )
  3287. {
  3288. // there is no "default" way for generic objects to repair each other
  3289. return;
  3290. }
  3291. #ifdef ALLOW_SURRENDER
  3292. //----------------------------------------------------------------------------------------
  3293. /**
  3294. * Pick up prisoner
  3295. */
  3296. void AIUpdateInterface::privatePickUpPrisoner( Object *prisoner, CommandSourceType cmdSource )
  3297. {
  3298. // there is no "default" way for generic units to pick up prisoners
  3299. return;
  3300. }
  3301. #endif
  3302. #ifdef ALLOW_SURRENDER
  3303. //----------------------------------------------------------------------------------------
  3304. /**
  3305. * Return prisoners
  3306. */
  3307. void AIUpdateInterface::privateReturnPrisoners( Object *prison, CommandSourceType cmdSource )
  3308. {
  3309. // there is no "default" way for generic units to return prisoners
  3310. return;
  3311. }
  3312. #endif
  3313. //----------------------------------------------------------------------------------------
  3314. /**
  3315. * Resume construction of object
  3316. */
  3317. void AIUpdateInterface::privateResumeConstruction( Object *obj, CommandSourceType cmdSource )
  3318. {
  3319. // there is no "default" way for generic objects to resume construction
  3320. return;
  3321. }
  3322. //----------------------------------------------------------------------------------------
  3323. /**
  3324. * Get healed at the heal depot
  3325. */
  3326. void AIUpdateInterface::privateGetHealed( Object *healDepot, CommandSourceType cmdSource )
  3327. {
  3328. // sanity, if we can't get healed from here get outta here
  3329. if( TheActionManager->canGetHealedAt( getObject(), healDepot, cmdSource ) == FALSE )
  3330. return;
  3331. // enter the heal dest for healing
  3332. aiEnter( healDepot, cmdSource );
  3333. }
  3334. //----------------------------------------------------------------------------------------
  3335. /**
  3336. * Get repaired at the repair depot
  3337. */
  3338. void AIUpdateInterface::privateGetRepaired( Object *repairDepot, CommandSourceType cmdSource )
  3339. {
  3340. // sanity, if we can't get repaired from here get out of here
  3341. if( TheActionManager->canGetRepairedAt( getObject(), repairDepot, cmdSource ) == FALSE )
  3342. return;
  3343. // dock with the repair depot
  3344. aiDock( repairDepot, cmdSource );
  3345. }
  3346. //----------------------------------------------------------------------------------------
  3347. /**
  3348. * Enter the given object
  3349. */
  3350. void AIUpdateInterface::privateEnter( Object *obj, CommandSourceType cmdSource )
  3351. {
  3352. Object *me = getObject();
  3353. if( me->isMobile() == FALSE )
  3354. return;
  3355. //Resetting the locomotor here was initially added for scripting purposes. It has been moved
  3356. //to the responsibility of the script to reset the locomotor before moving. This is needed because
  3357. //other systems (like the battle drone) change the locomotor based on what it's trying to do, and
  3358. //doesn't want to get reset when ordered to move.
  3359. //chooseLocomotorSet(LOCOMOTORSET_NORMAL);
  3360. if( TheActionManager->canEnterObject( me, obj, cmdSource, DONT_CHECK_CAPACITY ) )
  3361. {
  3362. getStateMachine()->clear();
  3363. getStateMachine()->setGoalObject( obj );
  3364. setLastCommandSource( cmdSource );
  3365. getStateMachine()->setState( AI_ENTER );
  3366. }
  3367. }
  3368. //----------------------------------------------------------------------------------------
  3369. /**
  3370. * Dock with the given object
  3371. */
  3372. void AIUpdateInterface::privateDock( Object *obj, CommandSourceType cmdSource )
  3373. {
  3374. if (getObject()->isMobile() == FALSE)
  3375. return;
  3376. getStateMachine()->clear();
  3377. getStateMachine()->setGoalObject( obj );
  3378. setLastCommandSource( cmdSource );
  3379. getStateMachine()->setState( AI_DOCK );
  3380. }
  3381. //----------------------------------------------------------------------------------------
  3382. void AIUpdateInterface::privateCombatDrop( Object *target, const Coord3D& pos, CommandSourceType cmdSource )
  3383. {
  3384. DEBUG_CRASH(("default implementation, should never be called"));
  3385. if( getObject()->getContain() )
  3386. {
  3387. getObject()->getContain()->removeAllContained(FALSE);
  3388. }
  3389. }
  3390. //----------------------------------------------------------------------------------------
  3391. /**
  3392. * Get out of whatever it is inside of
  3393. */
  3394. void AIUpdateInterface::privateExit( Object *objectToExit, CommandSourceType cmdSource )
  3395. {
  3396. Object *us = getObject();
  3397. if (!objectToExit)
  3398. {
  3399. objectToExit = us->getContainedBy();
  3400. }
  3401. if (!objectToExit)
  3402. return;
  3403. if ( objectToExit->isDisabledByType( DISABLED_SUBDUED ) )
  3404. return;
  3405. // we must go thru this state (rather than calling exitObjectViaDoor directly!),
  3406. // because a few containers might need to delay to allow
  3407. // us to exit (eg, Chinooks must land), meaning we might have to wait a bit, and coordinate
  3408. // with the container by actually NOTIFYING it that we want to exit...
  3409. getStateMachine()->clear();
  3410. getStateMachine()->setGoalObject( objectToExit );
  3411. setLastCommandSource( cmdSource );
  3412. getStateMachine()->setState( AI_EXIT );
  3413. }
  3414. //----------------------------------------------------------------------------------------
  3415. /**
  3416. * Get out of whatever it is inside of this frame
  3417. */
  3418. void AIUpdateInterface::privateExitInstantly( Object *objectToExit, CommandSourceType cmdSource )
  3419. {
  3420. Object *us = getObject();
  3421. if (!objectToExit)
  3422. {
  3423. objectToExit = us->getContainedBy();
  3424. }
  3425. if (!objectToExit)
  3426. return;
  3427. if ( objectToExit->isDisabledByType( DISABLED_SUBDUED ) )
  3428. return;
  3429. // we must go thru this state (rather than calling exitObjectViaDoor directly!),
  3430. // because a few containers might need to delay to allow
  3431. // us to exit (eg, Chinooks must land), meaning we might have to wait a bit, and coordinate
  3432. // with the container by actually NOTIFYING it that we want to exit...
  3433. getStateMachine()->clear();
  3434. getStateMachine()->setGoalObject( objectToExit );
  3435. setLastCommandSource( cmdSource );
  3436. getStateMachine()->setState( AI_EXIT_INSTANTLY );
  3437. }
  3438. //----------------------------------------------------------------------------------------
  3439. /**
  3440. * Get out of whatever it is inside of
  3441. */
  3442. void AIUpdateInterface::doQuickExit( const std::vector<Coord3D>* path )
  3443. {
  3444. Bool locked = getStateMachine()->isLocked();
  3445. getStateMachine()->unlock();
  3446. // set path info
  3447. getStateMachine()->setGoalPath( path );
  3448. getStateMachine()->setTemporaryState( AI_FOLLOW_EXITPRODUCTION_PATH, 10*LOGICFRAMES_PER_SECOND);
  3449. if (locked) {
  3450. getStateMachine()->lock("Relocking in doQuickExit.");
  3451. }
  3452. }
  3453. //----------------------------------------------------------------------------------------
  3454. /**
  3455. * Empty its contents
  3456. */
  3457. void AIUpdateInterface::privateEvacuate( Int exposeStealthUnits, CommandSourceType cmdSource )
  3458. {
  3459. if ( getObject()->isDisabledByType( DISABLED_SUBDUED ) )
  3460. return;
  3461. ContainModuleInterface *contain = getObject()->getContain();
  3462. if( contain )
  3463. {
  3464. if( exposeStealthUnits )
  3465. {
  3466. contain->markAllPassengersDetected();
  3467. }
  3468. contain->orderAllPassengersToExit( cmdSource, FALSE );
  3469. }
  3470. }
  3471. //----------------------------------------------------------------------------------------
  3472. /**
  3473. * Empty its contents this frame
  3474. */
  3475. void AIUpdateInterface::privateEvacuateInstantly( Int exposeStealthUnits, CommandSourceType cmdSource )
  3476. {
  3477. if ( getObject()->isDisabledByType( DISABLED_SUBDUED ) )
  3478. return;
  3479. ContainModuleInterface *contain = getObject()->getContain();
  3480. if( contain )
  3481. {
  3482. if( exposeStealthUnits )
  3483. {
  3484. contain->markAllPassengersDetected();
  3485. }
  3486. contain->orderAllPassengersToExit( cmdSource, TRUE );
  3487. }
  3488. }
  3489. // ------------------------------------------------------------------------------------------------
  3490. void AIUpdateInterface::privateExecuteRailedTransport( CommandSourceType cmdSource )
  3491. {
  3492. // there is no default implementation for this
  3493. }
  3494. //----------------------------------------------------------------------------------------
  3495. ///< life altering state change, if this AI can do it
  3496. void AIUpdateInterface::privateGoProne( const DamageInfo *damageInfo, CommandSourceType )
  3497. {
  3498. static NameKeyType proneModuleKey = TheNameKeyGenerator->nameToKey( "ProneUpdate" );
  3499. ProneUpdate *proneModule = (ProneUpdate *)getObject()->findUpdateModule( proneModuleKey );
  3500. if( proneModule )
  3501. proneModule->goProne( damageInfo );
  3502. }
  3503. //-------------------------------------------------------------------------------------------------
  3504. //-------------------------------------------------------------------------------------------------
  3505. /**
  3506. * Wander around
  3507. */
  3508. void AIUpdateInterface::privateWander( const Waypoint *way, CommandSourceType cmdSource )
  3509. {
  3510. if (getObject()->isMobile() == FALSE)
  3511. return;
  3512. //Resetting the locomotor here was initially added for scripting purposes. It has been moved
  3513. //to the responsibility of the script to reset the locomotor before moving. This is needed because
  3514. //other systems (like the battle drone) change the locomotor based on what it's trying to do, and
  3515. //doesn't want to get reset when ordered to move.
  3516. //chooseLocomotorSet(LOCOMOTORSET_WANDER);
  3517. getStateMachine()->clear();
  3518. setLastCommandSource( cmdSource );
  3519. getStateMachine()->setGoalWaypoint( way );
  3520. getStateMachine()->setState( AI_WANDER );
  3521. }
  3522. //-------------------------------------------------------------------------------------------------
  3523. //-------------------------------------------------------------------------------------------------
  3524. /**
  3525. * Wander around
  3526. */
  3527. void AIUpdateInterface::privateWanderInPlace( CommandSourceType cmdSource )
  3528. {
  3529. if (getObject()->isMobile() == FALSE)
  3530. return;
  3531. //Resetting the locomotor here was initially added for scripting purposes. It has been moved
  3532. //to the responsibility of the script to reset the locomotor before moving. This is needed because
  3533. //other systems (like the battle drone) change the locomotor based on what it's trying to do, and
  3534. //doesn't want to get reset when ordered to move.
  3535. //chooseLocomotorSet(LOCOMOTORSET_WANDER);
  3536. getStateMachine()->clear();
  3537. setLastCommandSource( cmdSource );
  3538. getStateMachine()->setState( AI_WANDER_IN_PLACE );
  3539. }
  3540. //-------------------------------------------------------------------------------------------------
  3541. //-------------------------------------------------------------------------------------------------
  3542. /**
  3543. * Panic
  3544. */
  3545. void AIUpdateInterface::privatePanic( const Waypoint *way, CommandSourceType cmdSource )
  3546. {
  3547. if (getObject()->isMobile() == FALSE)
  3548. return;
  3549. //Resetting the locomotor here was initially added for scripting purposes. It has been moved
  3550. //to the responsibility of the script to reset the locomotor before moving. This is needed because
  3551. //other systems (like the battle drone) change the locomotor based on what it's trying to do, and
  3552. //doesn't want to get reset when ordered to move.
  3553. //chooseLocomotorSet(LOCOMOTORSET_PANIC);
  3554. getStateMachine()->clear();
  3555. setLastCommandSource( cmdSource );
  3556. getStateMachine()->setGoalWaypoint( way );
  3557. getStateMachine()->setState( AI_PANIC );
  3558. }
  3559. //-------------------------------------------------------------------------------------------------
  3560. //-------------------------------------------------------------------------------------------------
  3561. /**
  3562. * Busy
  3563. */
  3564. void AIUpdateInterface::privateBusy( CommandSourceType cmdSource )
  3565. {
  3566. getStateMachine()->clear();
  3567. setLastCommandSource( cmdSource );
  3568. getStateMachine()->setState( AI_BUSY );
  3569. }
  3570. //-------------------------------------------------------------------------------------------------
  3571. //-------------------------------------------------------------------------------------------------
  3572. /**
  3573. * Guard the given spot
  3574. */
  3575. void AIUpdateInterface::privateGuardPosition( const Coord3D *pos, GuardMode guardMode, CommandSourceType cmdSource )
  3576. {
  3577. if (getObject()->isMobile() == FALSE)
  3578. return;
  3579. if (getObject()->isKindOf(KINDOF_PROJECTILE))
  3580. return;
  3581. if (m_guardTargetType[1] == GUARDTARGET_NONE) {
  3582. m_guardTargetType[1] = GUARDTARGET_LOCATION;
  3583. } else {
  3584. m_guardTargetType[0] = GUARDTARGET_LOCATION;
  3585. }
  3586. Coord3D adjPos = *pos;
  3587. if (cmdSource==CMD_FROM_PLAYER) {
  3588. // Clip to playable area.
  3589. Region3D r;
  3590. TheTerrainLogic->getExtent(&r);
  3591. if (!r.isInRegionNoZ(&adjPos))
  3592. adjPos = TheTerrainLogic->findClosestEdgePoint(&adjPos);
  3593. }
  3594. m_locationToGuard = adjPos;
  3595. m_guardMode = guardMode;
  3596. getStateMachine()->clear();
  3597. setLastCommandSource( cmdSource );
  3598. getStateMachine()->setState( AI_GUARD );
  3599. }
  3600. //-------------------------------------------------------------------------------------------------
  3601. //-------------------------------------------------------------------------------------------------
  3602. /**
  3603. * Guard the given spot
  3604. */
  3605. void AIUpdateInterface::privateGuardTunnelNetwork( GuardMode guardMode, CommandSourceType cmdSource )
  3606. {
  3607. if (getObject()->isMobile() == FALSE)
  3608. return;
  3609. if (getObject()->isKindOf(KINDOF_PROJECTILE))
  3610. return;
  3611. m_guardMode = guardMode;
  3612. getStateMachine()->clear();
  3613. setLastCommandSource( cmdSource );
  3614. getStateMachine()->setState( AI_GUARD_TUNNEL_NETWORK );
  3615. }
  3616. //-------------------------------------------------------------------------------------------------
  3617. //-------------------------------------------------------------------------------------------------
  3618. /**
  3619. * Guard the given spot
  3620. */
  3621. void AIUpdateInterface::privateGuardObject( Object *objectToGuard, GuardMode guardMode, CommandSourceType cmdSource )
  3622. {
  3623. if (getObject()->isMobile() == FALSE)
  3624. return;
  3625. if (getObject()->isKindOf(KINDOF_PROJECTILE))
  3626. return;
  3627. if (m_guardTargetType[1] == GUARDTARGET_NONE) {
  3628. m_guardTargetType[1] = GUARDTARGET_OBJECT;
  3629. } else {
  3630. m_guardTargetType[0] = GUARDTARGET_OBJECT;
  3631. }
  3632. m_guardMode = guardMode;
  3633. m_objectToGuard = objectToGuard->getID();
  3634. getStateMachine()->clear();
  3635. setLastCommandSource( cmdSource );
  3636. getStateMachine()->setState( AI_GUARD );
  3637. }
  3638. //-------------------------------------------------------------------------------------------------
  3639. //-------------------------------------------------------------------------------------------------
  3640. /**
  3641. * Guard the given spot
  3642. */
  3643. void AIUpdateInterface::privateGuardArea( const PolygonTrigger *areaToGuard, GuardMode guardMode, CommandSourceType cmdSource )
  3644. {
  3645. if (getObject()->isMobile() == FALSE)
  3646. return;
  3647. if (getObject()->isKindOf(KINDOF_PROJECTILE))
  3648. return;
  3649. if (m_guardTargetType[1] == GUARDTARGET_NONE) {
  3650. m_guardTargetType[1] = GUARDTARGET_AREA;
  3651. } else {
  3652. m_guardTargetType[0] = GUARDTARGET_AREA;
  3653. }
  3654. m_areaToGuard = areaToGuard;
  3655. m_guardMode = guardMode;
  3656. Coord3D pos;
  3657. m_areaToGuard->getCenterPoint(&pos);
  3658. m_locationToGuard = pos;
  3659. m_objectToGuard = INVALID_ID; //just in case.
  3660. getStateMachine()->clear();
  3661. setLastCommandSource( cmdSource );
  3662. getStateMachine()->setState( AI_GUARD );
  3663. }
  3664. //-------------------------------------------------------------------------------------------------
  3665. void AIUpdateInterface::privateHackInternet( CommandSourceType cmdSource )
  3666. {
  3667. // We need to be able to hack in containers
  3668. // if (getObject()->isMobile() == FALSE)
  3669. // return;
  3670. getStateMachine()->clear();
  3671. setLastCommandSource( cmdSource );
  3672. static NameKeyType key_HackInternetAIUpdate = NAMEKEY("HackInternetAIUpdate");
  3673. HackInternetAIUpdate *ai = (HackInternetAIUpdate*)getObject()->findUpdateModule( key_HackInternetAIUpdate );
  3674. if( ai )
  3675. {
  3676. ai->hackInternet();
  3677. }
  3678. else
  3679. {
  3680. DEBUG_CRASH(("Unit %s is expecting a 'Update = HackInternetAIUpdate' entry in FactionUnit.ini", getObject()->getTemplate()->getName().str() ) );
  3681. }
  3682. }
  3683. /// if we are attacking "fromID", stop that and attack "toID" instead
  3684. void AIUpdateInterface::transferAttack(ObjectID fromID, ObjectID toID)
  3685. {
  3686. Object *newTarget = TheGameLogic->findObjectByID( toID );
  3687. if (m_currentVictimID == fromID)
  3688. m_currentVictimID = toID;
  3689. Object* goalObj = getStateMachine()->getGoalObject();
  3690. if (goalObj && goalObj->getID() == fromID)
  3691. getStateMachine()->setGoalObject( newTarget );
  3692. //Transfer the turrets too this frame.
  3693. for( Int i = 0; i < MAX_TURRETS; i++ )
  3694. {
  3695. goalObj = getTurretTargetObject( (WhichTurretType)i, FALSE );
  3696. if( goalObj && goalObj->getID() == fromID )
  3697. {
  3698. setTurretTargetObject( (WhichTurretType)i, newTarget, TRUE );
  3699. }
  3700. }
  3701. }
  3702. //----------------------------------------------------------------------------------------------------------
  3703. /**
  3704. * Indicate who we are attacking.
  3705. */
  3706. void AIUpdateInterface::setCurrentVictim( const Object *victim )
  3707. {
  3708. if (victim == NULL)
  3709. {
  3710. // be paranoid, in case we are called from dtors, etc.
  3711. if (m_currentVictimID != INVALID_ID)
  3712. {
  3713. Object* self = getObject();
  3714. Object* target = TheGameLogic->findObjectByID(m_currentVictimID);
  3715. if (self != NULL && target != NULL)
  3716. {
  3717. AIUpdateInterface* targetAI = target->getAI();
  3718. if (targetAI)
  3719. {
  3720. targetAI->addTargeter(self->getID(), FALSE);
  3721. }
  3722. }
  3723. }
  3724. m_currentVictimID = INVALID_ID;
  3725. }
  3726. else
  3727. {
  3728. // we don't add a targeter here, since we usually want to defer
  3729. // that until we are actually aiming (as opposed to, say, approaching)
  3730. // the victim.
  3731. m_currentVictimID = victim->getID();
  3732. }
  3733. }
  3734. /**
  3735. * Who is our current victim?
  3736. */
  3737. Object *AIUpdateInterface::getCurrentVictim( void ) const
  3738. {
  3739. if (m_currentVictimID != INVALID_ID)
  3740. return TheGameLogic->findObjectByID( m_currentVictimID );
  3741. return NULL;
  3742. }
  3743. // if we are attacking a position (and NOT an object), return it. otherwise return null.
  3744. const Coord3D *AIUpdateInterface::getCurrentVictimPos( void ) const
  3745. {
  3746. if (getObject()->testStatus(OBJECT_STATUS_IS_ATTACKING))
  3747. {
  3748. if (m_currentVictimID == INVALID_ID)
  3749. {
  3750. return getStateMachine()->getGoalPosition();
  3751. }
  3752. }
  3753. return NULL;
  3754. }
  3755. /**
  3756. * Set the behavior modifier for this agent
  3757. */
  3758. void AIUpdateInterface::setAttitude( AttitudeType tude )
  3759. {
  3760. m_attitude = tude;
  3761. }
  3762. /**
  3763. * Get the current behavior modifier state
  3764. */
  3765. AttitudeType AIUpdateInterface::getAttitude( void ) const
  3766. {
  3767. return m_attitude;
  3768. }
  3769. /**
  3770. * Return the current state the AI is in.
  3771. */
  3772. AIStateType AIUpdateInterface::getAIStateType() const
  3773. {
  3774. return (AIStateType)getStateMachine()->getCurrentStateID();
  3775. }
  3776. //-------------------------------------------------------------------------------------------------
  3777. void AIUpdateInterface::ignoreObstacle( const Object *obj )
  3778. {
  3779. m_ignoreObstacleID = obj ? obj->getID() : INVALID_ID;
  3780. }
  3781. //-------------------------------------------------------------------------------------------------
  3782. void AIUpdateInterface::ignoreObstacleID( ObjectID id )
  3783. {
  3784. m_ignoreObstacleID = id;
  3785. }
  3786. //-------------------------------------------------------------------------------------------------
  3787. ObjectID AIUpdateInterface::getIgnoredObstacleID( void ) const
  3788. {
  3789. return m_ignoreObstacleID;
  3790. }
  3791. //-------------------------------------------------------------------------------------------------
  3792. Object* AIUpdateInterface::getEnterTarget()
  3793. {
  3794. AIStateType stateType = getAIStateType();
  3795. if( stateType != AI_ENTER &&
  3796. stateType != AI_GUARD_TUNNEL_NETWORK &&
  3797. stateType != AI_GET_REPAIRED )
  3798. return NULL;
  3799. return getStateMachine()->getGoalObject();
  3800. }
  3801. //-------------------------------------------------------------------------------------------------
  3802. void AIUpdateInterface::setLastCommandSource( CommandSourceType source )
  3803. {
  3804. m_lastCommandSource = source;
  3805. }
  3806. //-------------------------------------------------------------------------------------------------
  3807. UnsignedInt AIUpdateInterface::getMoodMatrixValue( void ) const
  3808. {
  3809. UnsignedInt returnVal = 0;
  3810. // seems like a weird way to get my controlling object, but I don't see another
  3811. if (!getStateMachine())
  3812. {
  3813. return returnVal;
  3814. }
  3815. const Object *owner = getObject();
  3816. Player *player = owner->getControllingPlayer();
  3817. if (!player)
  3818. {
  3819. return returnVal;
  3820. }
  3821. if (player->getPlayerType() == PLAYER_HUMAN)
  3822. {
  3823. returnVal |= MM_Controller_Player;
  3824. // Human units don't have a mood.
  3825. }
  3826. else
  3827. {
  3828. returnVal |= MM_Controller_AI;
  3829. switch (getAttitude())
  3830. {
  3831. case AI_SLEEP: returnVal |= MM_Mood_Sleep; break;
  3832. case AI_PASSIVE: returnVal |= MM_Mood_Passive; break;
  3833. case AI_NORMAL: returnVal |= MM_Mood_Normal; break;
  3834. case AI_ALERT: returnVal |= MM_Mood_Alert; break;
  3835. case AI_AGGRESSIVE: returnVal |= MM_Mood_Aggressive; break;
  3836. default:
  3837. DEBUG_CRASH(("Unknown mood '%d' in getMoodMatrixValue. (Team '%s'). Using normal. (jkmcd)", getAttitude(), getObject()->getTeam()->getName().str() ));
  3838. returnVal |= MM_Mood_Normal;
  3839. break;
  3840. }
  3841. }
  3842. if (getLocomotorSet().getValidSurfaces() & LOCOMOTORSURFACE_AIR)
  3843. {
  3844. returnVal |= MM_UnitType_Air;
  3845. }
  3846. else
  3847. {
  3848. if (m_turretAI[0] != NULL)
  3849. {
  3850. returnVal |= MM_UnitType_Turreted;
  3851. }
  3852. else
  3853. {
  3854. returnVal |= MM_UnitType_NonTurreted;
  3855. }
  3856. }
  3857. return returnVal;
  3858. }
  3859. //-------------------------------------------------------------------------------------------------
  3860. UnsignedInt AIUpdateInterface::getMoodMatrixActionAdjustment( MoodMatrixAction action ) const
  3861. {
  3862. // Angry Mob Members (but not Nexi) are never subject to moods. In particular,
  3863. // they must never, ever, ever convert a move into an attack move, or Bad Things
  3864. // will happend, since MobMemberSlavedUpdate expects a moveto to remain a moveto.
  3865. // Mark L sez that members do not, in fact, need any mood adjustment whatsoever,
  3866. // since the mood of the nexus wants to control all this anyway. Unfortunately, there
  3867. // is no KINDOF_MOB_MEMBER, and we don't want to add one at the eleventh hour...
  3868. // this, however, is a unique and safe combination that applies only to mob members. (srj)
  3869. if (getObject()->isKindOf(KINDOF_INFANTRY) && getObject()->isKindOf(KINDOF_IGNORED_IN_GUI))
  3870. {
  3871. return MAA_Action_Ok;
  3872. }
  3873. UnsignedInt moodMatrix = getMoodMatrixValue();
  3874. UnsignedInt returnVal = 0;
  3875. if (moodMatrix & MM_Controller_Player)
  3876. {
  3877. // Player-controlled units can always do actions (from a mood perspective, at any rate)
  3878. returnVal = MAA_Action_Ok;
  3879. return returnVal;
  3880. }
  3881. returnVal = MAA_Action_Ok;
  3882. switch (action)
  3883. {
  3884. case MM_Action_Idle:
  3885. {
  3886. switch( moodMatrix & MM_Mood_Bitmask )
  3887. {
  3888. case MM_Mood_Sleep: returnVal = MAA_Action_Ok | MAA_Affect_Range_IgnoreAll; break;
  3889. case MM_Mood_Passive: returnVal = MAA_Action_Ok | MAA_Affect_Range_WaitForAttack; break;
  3890. case MM_Mood_Normal: returnVal = MAA_Action_Ok; break;
  3891. case MM_Mood_Alert: returnVal = MAA_Action_Ok | MAA_Affect_Range_Alert; break;
  3892. case MM_Mood_Aggressive: returnVal = MAA_Action_Ok | MAA_Affect_Range_Aggressive; break;
  3893. }
  3894. break;
  3895. }
  3896. case MM_Action_Move:
  3897. {
  3898. switch( moodMatrix & MM_Mood_Bitmask )
  3899. {
  3900. case MM_Mood_Sleep: returnVal = MAA_Action_To_Idle | MAA_Affect_Range_IgnoreAll; break;
  3901. case MM_Mood_Passive: returnVal = MAA_Action_Ok | MAA_Affect_Range_WaitForAttack; break;
  3902. case MM_Mood_Normal: returnVal = MAA_Action_Ok; break;
  3903. case MM_Mood_Alert: returnVal = MAA_Action_To_AttackMove | MAA_Affect_Range_Alert; break;
  3904. case MM_Mood_Aggressive: returnVal = MAA_Action_To_AttackMove | MAA_Affect_Range_Aggressive; break;
  3905. }
  3906. break;
  3907. }
  3908. case MM_Action_Attack:
  3909. {
  3910. switch( moodMatrix & MM_Mood_Bitmask )
  3911. {
  3912. case MM_Mood_Sleep: returnVal = MAA_Action_To_Idle | MAA_Affect_Range_IgnoreAll; break;
  3913. case MM_Mood_Passive: returnVal = MAA_Action_Ok; break;
  3914. case MM_Mood_Normal: returnVal = MAA_Action_Ok; break;
  3915. case MM_Mood_Alert: returnVal = MAA_Action_Ok; break;
  3916. case MM_Mood_Aggressive: returnVal = MAA_Action_Ok; break;
  3917. }
  3918. break;
  3919. }
  3920. case MM_Action_AttackMove:
  3921. {
  3922. switch( moodMatrix & MM_Mood_Bitmask )
  3923. {
  3924. case MM_Mood_Sleep: returnVal = MAA_Action_To_Idle | MAA_Affect_Range_IgnoreAll; break;
  3925. case MM_Mood_Passive: returnVal = MAA_Action_Ok; break;
  3926. case MM_Mood_Normal: returnVal = MAA_Action_Ok; break;
  3927. case MM_Mood_Alert: returnVal = MAA_Action_Ok | MAA_Affect_Range_Alert; break;
  3928. case MM_Mood_Aggressive: returnVal = MAA_Action_Ok | MAA_Affect_Range_Aggressive; break;
  3929. }
  3930. break;
  3931. }
  3932. };
  3933. return returnVal;
  3934. }
  3935. //----------------------------------------------------------------------------------------------
  3936. void AIUpdateInterface::wakeUpAndAttemptToTarget( void )
  3937. {
  3938. if (!isIdle()) {
  3939. return;
  3940. }
  3941. UnsignedInt now = TheGameLogic->getFrame();
  3942. m_nextMoodCheckTime = now;
  3943. m_randomlyOffsetMoodCheck = TRUE;
  3944. }
  3945. //----------------------------------------------------------------------------------------------
  3946. /**
  3947. * Reset when we should next look for a target. Usually called by *Idle::onEnter
  3948. */
  3949. void AIUpdateInterface::resetNextMoodCheckTime()
  3950. {
  3951. UnsignedInt now = TheGameLogic->getFrame();
  3952. m_nextMoodCheckTime = now + TheAI->getAiData()->m_forceIdleFramesCount;
  3953. m_randomlyOffsetMoodCheck = TRUE;
  3954. }
  3955. //----------------------------------------------------------------------------------------------
  3956. void AIUpdateInterface::setNextMoodCheckTime( UnsignedInt frame )
  3957. {
  3958. m_nextMoodCheckTime = frame;
  3959. m_randomlyOffsetMoodCheck = false;
  3960. }
  3961. Bool AIUpdateInterface::canAutoAcquireWhileStealthed() const
  3962. {
  3963. if ( getObject() && getObject()->getStealth() && getObject()->getStealth()->isGrantedBySpecialPower() )
  3964. return TRUE;
  3965. return getAIUpdateModuleData()->m_autoAcquireEnemiesWhenIdle & AAS_Idle_Stealthed;
  3966. }
  3967. //----------------------------------------------------------------------------------------------
  3968. /**
  3969. * Return the next object that our mood suggests we should attack.
  3970. */
  3971. Object* AIUpdateInterface::getNextMoodTarget( Bool calledByAI, Bool calledDuringIdle )
  3972. {
  3973. Object *obj = getObject();
  3974. // if we're dead, we can't attack
  3975. if (obj->isEffectivelyDead())
  3976. return NULL;
  3977. if (obj->testStatus(OBJECT_STATUS_IS_USING_ABILITY)) {
  3978. return NULL; // we are doing a special ability. Shouldn't auto-acquire a target at this time. jba.
  3979. }
  3980. const AIUpdateModuleData* d = getAIUpdateModuleData();
  3981. if (calledDuringIdle)
  3982. {
  3983. if ((d->m_autoAcquireEnemiesWhenIdle & AAS_Idle) == 0)
  3984. {
  3985. return NULL;
  3986. }
  3987. }
  3988. // srj sez: this should ignore calledDuringIdle, despite what the name of the bit implies.
  3989. if (isAttacking() && BitTest(d->m_autoAcquireEnemiesWhenIdle, AAS_Idle_Not_While_Attacking))
  3990. {
  3991. return NULL;
  3992. }
  3993. //Check if unit is stealthed... is so we won't acquire targets unless he has
  3994. //AutoAcquireWhenIdle = Yes Stealthed.
  3995. if ( calledDuringIdle )
  3996. {
  3997. if( obj->getStatusBits().test( OBJECT_STATUS_STEALTHED ) )
  3998. {
  3999. if( !canAutoAcquireWhileStealthed() )
  4000. {
  4001. const Object *container = obj->getContainedBy();
  4002. if( ! (container && container->getContain()->isPassengerAllowedToFire()) )
  4003. {
  4004. // Sorry, stealthed and not allowed to idle fire when stealthed.
  4005. // Being in a firing container is an exception to this veto.
  4006. return NULL;
  4007. }
  4008. }
  4009. }
  4010. }
  4011. UnsignedInt now = TheGameLogic->getFrame();
  4012. // Check if team auto targets same victim.
  4013. Object *teamVictim = NULL;
  4014. if (calledByAI && obj->getTeam()->getPrototype()->getTemplateInfo()->m_attackCommonTarget)
  4015. {
  4016. teamVictim = obj->getTeam()->getTeamTargetObject();
  4017. if (teamVictim) {
  4018. // Make sure we can attack the team victim. Mixed teams can acquire aircraft, and units
  4019. // like toxin tractors shouldn't acquire aircraft. jba. [8/27/2003]
  4020. CanAttackResult result = obj->getAbleToAttackSpecificObject( ATTACK_NEW_TARGET, teamVictim, CMD_FROM_AI );
  4021. if( result != ATTACKRESULT_POSSIBLE && result != ATTACKRESULT_POSSIBLE_AFTER_MOVING ) {
  4022. teamVictim = NULL; // Can't attack him. jba [8/27/2003]
  4023. }
  4024. }
  4025. if (teamVictim && getAttitude()>=AI_NORMAL)
  4026. return teamVictim;
  4027. }
  4028. DEBUG_ASSERTCRASH(m_nextMoodCheckTime != 0, ("m_nextMoodCheckTime should never be zero here."));
  4029. if (calledByAI)
  4030. {
  4031. // make sure it's time to check again.
  4032. if (now < m_nextMoodCheckTime)
  4033. return NULL;
  4034. Int checkRate = d->m_moodAttackCheckRate;
  4035. m_nextMoodCheckTime = now + checkRate;
  4036. if (m_randomlyOffsetMoodCheck)
  4037. {
  4038. Int halfRate = checkRate >> 1;
  4039. m_nextMoodCheckTime = (UnsignedInt)((Int)m_nextMoodCheckTime + GameLogicRandomValue(-halfRate, halfRate));
  4040. m_randomlyOffsetMoodCheck = FALSE;
  4041. }
  4042. }
  4043. // Use Guard Outer, which typically corresponds to the total range
  4044. Real rangeToFindWithin = TheAI->getAdjustedVisionRangeForObject(obj, AI_VISIONFACTOR_OWNERTYPE | AI_VISIONFACTOR_MOOD);
  4045. if (rangeToFindWithin <= 0.0f)
  4046. return NULL;
  4047. //If we are contained by an object, add it's bounding radius so that large buildings can auto acquire everything in
  4048. //outer ranges. Calculating this from the center is bad... although this code makes it possible to acquire a target
  4049. //outside of range, but in that case, it'll just fail and continue.
  4050. const Object *container = obj->getContainedBy();
  4051. if( container )
  4052. {
  4053. rangeToFindWithin += container->getGeometryInfo().getBoundingCircleRadius();
  4054. }
  4055. UnsignedInt moodMatrixVal = getMoodMatrixValue();
  4056. if ((moodMatrixVal & MM_Controller_AI) && (moodMatrixVal & MM_Mood_Passive))
  4057. {
  4058. BodyModuleInterface *bmi = obj->getBodyModule();
  4059. if (!bmi)
  4060. return NULL;
  4061. //Kris: August 26, 2003
  4062. //Do not allow units that healed me to get acquired! They are our friends!!!
  4063. if( bmi->getLastDamageInfo()->in.m_damageType != DAMAGE_HEALING )
  4064. {
  4065. return TheGameLogic->findObjectByID(bmi->getLastDamageInfo()->in.m_sourceID);
  4066. }
  4067. }
  4068. UnsignedInt flags = AI::CAN_ATTACK;
  4069. if (TheAI->getAiData()->m_attackUsesLineOfSight) {
  4070. if (obj->isKindOf(KINDOF_ATTACK_NEEDS_LINE_OF_SIGHT)) {
  4071. flags |= AI::CAN_SEE;
  4072. }
  4073. }
  4074. if (TheAI->getAiData()->m_attackIgnoreInsignificantBuildings) {
  4075. flags |= AI::IGNORE_INSIGNIFICANT_BUILDINGS;
  4076. }
  4077. if( d->m_autoAcquireEnemiesWhenIdle & AAS_Idle_Attack_Buildings )
  4078. {
  4079. flags |= AI::ATTACK_BUILDINGS;
  4080. }
  4081. // if we're called by AI, and are human controlled, then our AI will not
  4082. // allow us to pursue the target. therefore, we should ensure that we only
  4083. // look for targets that are already within attack range (as opposed to vision range).
  4084. if (calledByAI && obj->getControllingPlayer()->getPlayerType() == PLAYER_HUMAN)
  4085. {
  4086. flags |= AI::WITHIN_ATTACK_RANGE;
  4087. }
  4088. // Instead of shroud affecting the ability to attack, it affects the ability to target.
  4089. // The same checks apply as the old WeaponSet check (now commented out, search for getShroudedStatus)
  4090. if( calledByAI
  4091. && obj->getControllingPlayer()
  4092. && obj->getControllingPlayer()->getPlayerType() == PLAYER_HUMAN
  4093. )
  4094. {
  4095. flags |= AI::UNFOGGED;
  4096. }
  4097. Object *newVictim = TheAI->findClosestEnemy(obj, rangeToFindWithin, flags, getAttackInfo());
  4098. /*
  4099. DEBUG_LOG(("GNMT frame %d: %s %08lx (con %s %08lx) uses range %f, flags %08lx, %s finds %s %08lx\n",
  4100. now,
  4101. obj->getTemplate()->getName().str(),
  4102. obj,
  4103. container ? container->getTemplate()->getName().str() : "",
  4104. container,
  4105. rangeToFindWithin,
  4106. flags,
  4107. getAttackInfo() != NULL && getAttackInfo() != TheScriptEngine->getDefaultAttackInfo() ? "ATTACKINFO," : "",
  4108. newVictim ? newVictim->getTemplate()->getName().str() : "",
  4109. newVictim
  4110. ));
  4111. */
  4112. if (newVictim)
  4113. {
  4114. CRCDEBUG_LOG(("AIUpdateInterface::getNextMoodTarget() - %d is attacking %d\n", obj->getID(), newVictim->getID()));
  4115. /*
  4116. srj debug hack. ignore.
  4117. Int ot = getTmpValue();
  4118. if (ot!=0&&now>ot&&now-ot<=4)
  4119. ot=ot;
  4120. setTmpValue(now);
  4121. */
  4122. }
  4123. return newVictim;
  4124. }
  4125. // ------------------------------------------------------------------------------------------------
  4126. // ------------------------------------------------------------------------------------------------
  4127. void AIUpdateInterface::evaluateMoraleBonus( void )
  4128. {
  4129. Object *us = getObject();
  4130. #ifdef ALLOW_DEMORALIZE
  4131. Bool demoralized = isDemoralized();
  4132. #endif
  4133. Bool horde = FALSE;
  4134. Bool nationalism = FALSE;
  4135. Bool fanaticism = FALSE;
  4136. // do we have nationalism
  4137. ///@todo Find a better way to represent nationalism without hardcoding here (CBD)
  4138. static const UpgradeTemplate *nationalismTemplate = TheUpgradeCenter->findUpgrade( "Upgrade_Nationalism" );
  4139. DEBUG_ASSERTCRASH( nationalismTemplate != NULL, ("AIUpdateInterface::evaluateMoraleBonus - Nationalism upgrade not found\n") );
  4140. Player *player = us->getControllingPlayer();
  4141. if( player && player->hasUpgradeComplete( nationalismTemplate ) )
  4142. nationalism = TRUE;
  4143. // do we have fanaticism
  4144. ///@todo Find a better way to represent fanaticism without hardcoding here (MAL)
  4145. static const UpgradeTemplate *fanaticismTemplate = TheUpgradeCenter->findUpgrade( "Upgrade_Fanaticism" );
  4146. DEBUG_ASSERTCRASH( fanaticismTemplate != NULL, ("AIUpdateInterface::evaluateMoraleBonus - Fanaticism upgrade not found\n") );
  4147. if( player && player->hasUpgradeComplete( fanaticismTemplate ) )
  4148. fanaticism = TRUE;
  4149. // are we in a horde
  4150. HordeUpdateInterface *hui;
  4151. for( BehaviorModule** u = us->getBehaviorModules(); *u; ++u )
  4152. {
  4153. hui = (*u)->getHordeUpdateInterface();
  4154. if( hui && hui->isInHorde() )
  4155. {
  4156. horde = TRUE;
  4157. if( !hui->isAllowedNationalism() )
  4158. {
  4159. // Sorry CBD and MAL, but the cancer has spread to the lymph nodes. After Alpha, just pump full of painkillers.
  4160. nationalism = FALSE;
  4161. fanaticism = FALSE;
  4162. }
  4163. }
  4164. } // end for
  4165. #ifdef ALLOW_DEMORALIZE
  4166. // if we are are not demoralized we can have horde and nationalism effects
  4167. if( demoralized == FALSE )
  4168. #endif
  4169. {
  4170. #ifdef ALLOW_DEMORALIZE
  4171. // demoralized
  4172. us->clearWeaponBonusCondition( WEAPONBONUSCONDITION_DEMORALIZED );
  4173. #endif
  4174. //Lorenzen temporarily disabled, since it fights with the horde buff
  4175. //Drawable *draw = us->getDrawable();
  4176. //if ( draw && !us->isKindOf( KINDOF_PORTABLE_STRUCTURE ) )
  4177. // draw->setTerrainDecal(TERRAIN_DECAL_NONE);
  4178. // horde
  4179. if( horde )
  4180. {
  4181. us->setWeaponBonusCondition( WEAPONBONUSCONDITION_HORDE );
  4182. } // end if
  4183. else
  4184. us->clearWeaponBonusCondition( WEAPONBONUSCONDITION_HORDE );
  4185. // nationalism
  4186. if( nationalism )
  4187. {
  4188. us->setWeaponBonusCondition( WEAPONBONUSCONDITION_NATIONALISM );
  4189. // fanaticism
  4190. if ( fanaticism )
  4191. us->setWeaponBonusCondition( WEAPONBONUSCONDITION_FANATICISM );// FOR THE NEW GC INFANTRY GENERAL
  4192. else
  4193. us->clearWeaponBonusCondition( WEAPONBONUSCONDITION_FANATICISM );
  4194. }
  4195. else
  4196. us->clearWeaponBonusCondition( WEAPONBONUSCONDITION_NATIONALISM );
  4197. } // end if
  4198. #ifdef ALLOW_DEMORALIZE
  4199. else
  4200. {
  4201. // demoralized
  4202. us->setWeaponBonusCondition( WEAPONBONUSCONDITION_DEMORALIZED );
  4203. // we cannot have horde bonus condition
  4204. us->clearWeaponBonusCondition( WEAPONBONUSCONDITION_HORDE );
  4205. Drawable *draw = us->getDrawable();
  4206. if( draw && !us->isKindOf( KINDOF_PORTABLE_STRUCTURE ) )
  4207. {
  4208. draw->setTerrainDecal(TERRAIN_DECAL_DEMORALIZED);
  4209. }
  4210. // we cannot have nationalism bonus condition
  4211. us->clearWeaponBonusCondition( WEAPONBONUSCONDITION_NATIONALISM );
  4212. us->clearWeaponBonusCondition( WEAPONBONUSCONDITION_FANATICISM );
  4213. } // end else
  4214. #endif
  4215. /*
  4216. UnicodeString msg;
  4217. msg.format( L"'%S' Horde=%d,Nationalism=%d,Demoralized=%d",
  4218. us->getTemplate()->getName().str(), horde, nationalism, demoralized );
  4219. TheInGameUI->message( msg );
  4220. */
  4221. } // end evaluateMoraleBonus
  4222. #ifdef ALLOW_DEMORALIZE
  4223. // ------------------------------------------------------------------------------------------------
  4224. // ------------------------------------------------------------------------------------------------
  4225. void AIUpdateInterface::setDemoralized( UnsignedInt durationInFrames )
  4226. {
  4227. UnsignedInt prevDemoralizedFrames = m_demoralizedFramesLeft;
  4228. // overwrite the previous demoralized time left
  4229. m_demoralizedFramesLeft = durationInFrames;
  4230. // if we turned on or turned off we need to re-evaluate our bonus conditions
  4231. if( (prevDemoralizedFrames == 0 && m_demoralizedFramesLeft > 0) ||
  4232. (prevDemoralizedFrames > 0 && m_demoralizedFramesLeft == 0) )
  4233. {
  4234. // evaluate demoralization, nationalism, and horde effect as they are all intertwined
  4235. evaluateMoraleBonus();
  4236. } // end if
  4237. }
  4238. #endif
  4239. // ------------------------------------------------------------------------------------------------
  4240. // ------------------------------------------------------------------------------------------------
  4241. void AIUpdateInterface::privateCommandButton( const CommandButton *commandButton, CommandSourceType cmdSource )
  4242. {
  4243. if( !commandButton )
  4244. {
  4245. return;
  4246. }
  4247. if (getObject()->isKindOf(KINDOF_PROJECTILE))
  4248. return;
  4249. //First of all, it's quite possible to get this far with an object incapable of performing such a task. Scripts will have
  4250. //entire teams of multiple unit types and want to order units to do something... if they can, great.. if not, ignore.
  4251. Object *owner = getObject();
  4252. if( owner )
  4253. {
  4254. AIUpdateInterface *ai = owner->getAI();
  4255. if( ai )
  4256. {
  4257. //Make sure the owner has the same command button.
  4258. const CommandSet *commandSet = TheControlBar->findCommandSet( owner->getCommandSetString() );
  4259. if( commandSet )
  4260. {
  4261. for( int i = 0; i < MAX_COMMANDS_PER_SET; i++ )
  4262. {
  4263. const CommandButton *aCommandButton = commandSet->getCommandButton(i);
  4264. if( commandButton == aCommandButton )
  4265. {
  4266. //We found the matching command button so now order the unit to do what the button wants.
  4267. switch( commandButton->getCommandType() )
  4268. {
  4269. //ONLY NO TARGET VIA AI BUTTONS NEED BE IMPLEMENTED HERE!
  4270. case GUI_COMMAND_STOP:
  4271. ai->aiIdle( cmdSource );
  4272. break;
  4273. default:
  4274. if( owner->getName().isNotEmpty() )
  4275. {
  4276. DEBUG_ASSERTCRASH( 0, ("AIUpdate::privateCommandButton() -- unit %s ('%s'), command %s not implemented.",
  4277. owner->getTemplate()->getName().str(), owner->getName().str(), commandButton->getTextLabel().str() ) );
  4278. }
  4279. else
  4280. {
  4281. DEBUG_ASSERTCRASH( 0, ("AIUpdate::privateCommandButton() -- unit %s, command %s not implemented.",
  4282. owner->getTemplate()->getName().str(), commandButton->getTextLabel().str() ) );
  4283. }
  4284. }
  4285. }
  4286. }
  4287. }
  4288. }
  4289. }
  4290. }
  4291. // ------------------------------------------------------------------------------------------------
  4292. // ------------------------------------------------------------------------------------------------
  4293. void AIUpdateInterface::privateCommandButtonPosition( const CommandButton *commandButton, const Coord3D *pos, CommandSourceType cmdSource )
  4294. {
  4295. if( !commandButton )
  4296. {
  4297. return;
  4298. }
  4299. if (getObject()->isKindOf(KINDOF_PROJECTILE))
  4300. return;
  4301. //First of all, it's quite possible to get this far with an object incapable of performing such a task. Scripts will have
  4302. //entire teams of multiple unit types and want to order units to do something... if they can, great.. if not, ignore.
  4303. Object *owner = getObject();
  4304. if( owner )
  4305. {
  4306. AIUpdateInterface *ai = owner->getAI();
  4307. if( ai )
  4308. {
  4309. //Make sure the owner has the same command button.
  4310. const CommandSet *commandSet = TheControlBar->findCommandSet( owner->getCommandSetString() );
  4311. if( commandSet )
  4312. {
  4313. for( int i = 0; i < MAX_COMMANDS_PER_SET; i++ )
  4314. {
  4315. const CommandButton *aCommandButton = commandSet->getCommandButton(i);
  4316. if( commandButton == aCommandButton )
  4317. {
  4318. //We found the matching command button so now order the unit to do what the button wants.
  4319. switch( commandButton->getCommandType() )
  4320. {
  4321. //LOCATION BASED COMMANDS ONLY VIA AI
  4322. case GUI_COMMAND_NONE:
  4323. default:
  4324. if( owner->getName().isNotEmpty() )
  4325. {
  4326. DEBUG_ASSERTCRASH( 0, ("AIUpdate::privateCommandButtonPosition() -- unit %s ('%s'), command %s not implemented.",
  4327. owner->getTemplate()->getName().str(), owner->getName().str(), commandButton->getTextLabel().str() ) );
  4328. }
  4329. else
  4330. {
  4331. DEBUG_ASSERTCRASH( 0, ("AIUpdate::privateCommandButtonPosition() -- unit %s, command %s not implemented.",
  4332. owner->getTemplate()->getName().str(), commandButton->getTextLabel().str() ) );
  4333. }
  4334. break;
  4335. }
  4336. }
  4337. }
  4338. }
  4339. }
  4340. }
  4341. }
  4342. // ------------------------------------------------------------------------------------------------
  4343. // ------------------------------------------------------------------------------------------------
  4344. void AIUpdateInterface::privateCommandButtonObject( const CommandButton *commandButton, Object *obj, CommandSourceType cmdSource )
  4345. {
  4346. if( !commandButton )
  4347. {
  4348. return;
  4349. }
  4350. if (getObject()->isKindOf(KINDOF_PROJECTILE))
  4351. return;
  4352. //First of all, it's quite possible to get this far with an object incapable of performing such a task. Scripts will have
  4353. //entire teams of multiple unit types and want to order units to do something... if they can, great.. if not, ignore.
  4354. Object *owner = getObject();
  4355. if( owner )
  4356. {
  4357. AIUpdateInterface *ai = owner->getAI();
  4358. //Make sure the owner has the same command button.
  4359. const CommandSet *commandSet = TheControlBar->findCommandSet( owner->getCommandSetString() );
  4360. if( commandSet )
  4361. {
  4362. for( int i = 0; i < MAX_COMMANDS_PER_SET; i++ )
  4363. {
  4364. const CommandButton *aCommandButton = commandSet->getCommandButton(i);
  4365. if( commandButton == aCommandButton )
  4366. {
  4367. //We found the matching command button so now order the unit to do what the button wants.
  4368. switch( commandButton->getCommandType() )
  4369. {
  4370. //OBJECT BASED COMMANDS ONLY VIA AI
  4371. case GUI_COMMAND_COMBATDROP:
  4372. if( ai )
  4373. {
  4374. ai->aiCombatDrop( obj, *(obj->getPosition()), cmdSource );
  4375. }
  4376. break;
  4377. default:
  4378. {
  4379. AsciiString myName = owner->getTemplate()->getName().str();
  4380. AsciiString myNickname;
  4381. AsciiString targetName = obj->getTemplate()->getName().str();
  4382. AsciiString targetNickname;
  4383. if( owner->getName().isNotEmpty() )
  4384. {
  4385. myNickname.format( "('%s')", owner->getName().str() );
  4386. }
  4387. if( obj->getName().isNotEmpty() )
  4388. {
  4389. targetNickname.format( "('%s')", obj->getName().str() );
  4390. }
  4391. DEBUG_ASSERTCRASH( 0, ("AIUpdate::privateCommandButtonPosition() -- unit %s %s, command %s at unit %s %s not implemented.",
  4392. myName.str(), myNickname.str(), commandButton->getTextLabel().str(), targetName.str(), targetNickname.str() ) );
  4393. }
  4394. }
  4395. }
  4396. }
  4397. }
  4398. }
  4399. }
  4400. // ------------------------------------------------------------------------------------------------
  4401. AIGroup *AIUpdateInterface::getGroup(void)
  4402. {
  4403. return getObject()->getGroup();
  4404. }
  4405. ///////////////////////////////////////////////////////////////////////////////////////////////////
  4406. ///////////////////////////////////////////////////////////////////////////////////////////////////
  4407. ///////////////////////////////////////////////////////////////////////////////////////////////////
  4408. // ------------------------------------------------------------------------------------------------
  4409. /** CRC */
  4410. // ------------------------------------------------------------------------------------------------
  4411. void AIUpdateInterface::crc( Xfer *x )
  4412. {
  4413. CRCGEN_LOG(("AIUpdateInterface::crc() begin - %8.8X\n", ((XferCRC *)x)->getCRC()));
  4414. // extend base class
  4415. UpdateModule::crc( x );
  4416. xfer(x);
  4417. CRCGEN_LOG(("AIUpdateInterface::crc() end - %8.8X\n", ((XferCRC *)x)->getCRC()));
  4418. } // end crc
  4419. // ------------------------------------------------------------------------------------------------
  4420. /** Xfer method
  4421. * Version Info:
  4422. * 1: Initial version */
  4423. // ------------------------------------------------------------------------------------------------
  4424. void AIUpdateInterface::xfer( Xfer *xfer )
  4425. {
  4426. // version
  4427. const XferVersion currentVersion = 4;
  4428. XferVersion version = currentVersion;
  4429. xfer->xferVersion( &version, currentVersion );
  4430. // extend base class
  4431. UpdateModule::xfer( xfer );
  4432. xfer->xferUnsignedInt(&m_priorWaypointID);
  4433. xfer->xferUnsignedInt(&m_currentWaypointID);
  4434. xfer->xferSnapshot(m_stateMachine);
  4435. xfer->xferBool(&m_isAiDead);
  4436. xfer->xferBool(&m_isRecruitable);
  4437. xfer->xferUnsignedInt(&m_nextEnemyScanTime);
  4438. xfer->xferObjectID(&m_currentVictimID);
  4439. xfer->xferReal(&m_desiredSpeed);
  4440. xfer->xferUser(&m_lastCommandSource, sizeof(m_lastCommandSource));
  4441. xfer->xferUser(&m_guardTargetType[0], sizeof(m_guardTargetType));
  4442. xfer->xferUser(&m_guardTargetType[1], sizeof(m_guardTargetType));
  4443. xfer->xferCoord3D(&m_locationToGuard);
  4444. xfer->xferObjectID(&m_objectToGuard);
  4445. AsciiString triggerName;
  4446. if (m_areaToGuard) triggerName = m_areaToGuard->getTriggerName();
  4447. xfer->xferAsciiString(&triggerName);
  4448. if (xfer->getXferMode() == XFER_LOAD)
  4449. {
  4450. if (triggerName.isNotEmpty()) {
  4451. m_areaToGuard = TheTerrainLogic->getTriggerAreaByName(triggerName);
  4452. }
  4453. }
  4454. AsciiString attackName;
  4455. if (m_attackInfo) attackName = m_attackInfo->getName();
  4456. xfer->xferAsciiString(&attackName);
  4457. if (xfer->getXferMode() == XFER_LOAD)
  4458. {
  4459. if (attackName.isNotEmpty()) {
  4460. m_attackInfo = TheScriptEngine->getAttackInfo(attackName);
  4461. }
  4462. }
  4463. xfer->xferInt(&m_waypointCount);
  4464. if (m_waypointCount<0 || m_waypointCount>MAX_WAYPOINTS) {
  4465. DEBUG_CRASH(("Invalid waypoint count %d, max = %d", m_waypointCount, MAX_WAYPOINTS));
  4466. throw SC_INVALID_DATA;
  4467. }
  4468. Int i;
  4469. for (i=0; i<m_waypointCount; i++) {
  4470. xfer->xferCoord3D(&m_waypointQueue[i]);
  4471. }
  4472. xfer->xferInt(&m_waypointIndex);
  4473. xfer->xferBool(&m_executingWaypointQueue);
  4474. UnsignedInt id = INVALID_WAYPOINT_ID;
  4475. if (m_completedWaypoint) {
  4476. id = m_completedWaypoint->getID();
  4477. }
  4478. xfer->xferUnsignedInt(&id);
  4479. if (xfer->getXferMode() == XFER_LOAD)
  4480. {
  4481. m_completedWaypoint = TheTerrainLogic->getWaypointByID(id);
  4482. }
  4483. xfer->xferBool(&m_waitingForPath);
  4484. Bool gotPath = (m_path != NULL);
  4485. xfer->xferBool(&gotPath);
  4486. if (xfer->getXferMode() == XFER_LOAD) {
  4487. if (gotPath) {
  4488. m_path = newInstance(Path);
  4489. }
  4490. }
  4491. if (gotPath) {
  4492. xfer->xferSnapshot(m_path);
  4493. }
  4494. xfer->xferObjectID(&m_requestedVictimID);
  4495. xfer->xferCoord3D(&m_requestedDestination);
  4496. xfer->xferCoord3D(&m_requestedDestination2);
  4497. // Not needed - we will recompute paths on load.
  4498. //xfer->xferUnsignedInt(&m_pathTimestamp);
  4499. xfer->xferObjectID(&m_ignoreObstacleID);
  4500. xfer->xferReal(&m_pathExtraDistance);
  4501. xfer->xferICoord2D(&m_pathfindGoalCell);
  4502. xfer->xferICoord2D(&m_pathfindCurCell);
  4503. // Not needed - jba.
  4504. //Int m_blockedFrames; ///< Number of frames we've been blocked.
  4505. //Real m_curMaxBlockedSpeed; ///< Max speed we can have and not run into blocking things.
  4506. //Bool m_isBlocked;
  4507. //Bool m_isBlockedAndStuck; ///< True if we are stuck & need to recompute path.
  4508. //Bool m_isInUpdate;
  4509. //Bool m_fixLocoInPostProcess;
  4510. xfer->xferUnsignedInt(&m_ignoreCollisionsUntil);
  4511. xfer->xferUnsignedInt(&m_queueForPathFrame);
  4512. xfer->xferCoord3D(&m_finalPosition);
  4513. xfer->xferBool(&m_doFinalPosition);
  4514. xfer->xferBool(&m_isAttackPath);
  4515. xfer->xferBool(&m_isFinalGoal);
  4516. xfer->xferBool(&m_isApproachPath);
  4517. xfer->xferBool(&m_isSafePath);
  4518. xfer->xferBool(&m_movementComplete);
  4519. xfer->xferBool(&m_isSafePath);
  4520. xfer->xferBool(&m_upgradedLocomotors);
  4521. xfer->xferBool(&m_canPathThroughUnits);
  4522. xfer->xferBool(&m_randomlyOffsetMoodCheck);
  4523. xfer->xferObjectID(&m_repulsor1);
  4524. xfer->xferObjectID(&m_repulsor2);
  4525. if (version < 3)
  4526. {
  4527. Int lastFrameMoved = 0;
  4528. xfer->xferInt(&lastFrameMoved);
  4529. }
  4530. xfer->xferObjectID(&m_moveOutOfWay1);
  4531. xfer->xferObjectID(&m_moveOutOfWay2);
  4532. if (xfer->getXferMode() == XFER_LOAD && version < 4)
  4533. {
  4534. // Read in from .ini
  4535. //LocomotorSet m_locomotorSet;
  4536. AsciiString setName;
  4537. if (m_curLocomotorSet > LOCOMOTORSET_INVALID && m_curLocomotorSet < LOCOMOTORSET_COUNT)
  4538. setName = TheLocomotorSetNames[m_curLocomotorSet];
  4539. xfer->xferAsciiString(&setName);
  4540. if (setName.isNotEmpty())
  4541. m_curLocomotorSet = (LocomotorSetType)INI::scanIndexList(setName.str(), TheLocomotorSetNames);
  4542. m_fixLocoInPostProcess = TRUE;
  4543. }
  4544. else
  4545. {
  4546. if (xfer->getXferMode() == XFER_LOAD)
  4547. {
  4548. // our ctor choose a NORMAL set for us. it's simpler
  4549. // to simply clear out whatever we have here and allow
  4550. // xferSelfAndCurLocoPtr() to continue to require a pristine,
  4551. // empty set. (srj)
  4552. m_locomotorSet.clear();
  4553. m_curLocomotor = NULL;
  4554. }
  4555. m_locomotorSet.xferSelfAndCurLocoPtr(xfer, &m_curLocomotor);
  4556. xfer->xferUser(&m_curLocomotorSet, sizeof(m_curLocomotorSet));
  4557. }
  4558. xfer->xferUser(&m_locomotorGoalType, sizeof(m_locomotorGoalType));
  4559. xfer->xferCoord3D(&m_locomotorGoalData);
  4560. for (i=0; i<MAX_TURRETS; i++) {
  4561. if (m_turretAI[i]) {
  4562. xfer->xferSnapshot(m_turretAI[i]);
  4563. }
  4564. }
  4565. xfer->xferUser(&m_turretSyncFlag, sizeof(m_turretSyncFlag));
  4566. xfer->xferUser(&m_attitude, sizeof(m_attitude));
  4567. xfer->xferUnsignedInt(&m_nextMoodCheckTime);
  4568. if (version == 1)
  4569. {
  4570. // surrender + demoralize
  4571. #ifdef ALLOW_DEMORALIZE
  4572. xfer->xferUnsignedInt(&m_demoralizedFramesLeft);
  4573. #else
  4574. UnsignedInt demoralizedFramesLeft = 0;
  4575. xfer->xferUnsignedInt(&demoralizedFramesLeft);
  4576. #endif
  4577. #ifdef ALLOW_SURRENDER
  4578. xfer->xferUnsignedInt(&m_surrenderedFramesLeft);
  4579. xfer->xferInt(&m_surrenderedPlayerIndex);
  4580. #else
  4581. UnsignedInt surrenderedFramesLeft = 0;
  4582. Int surrenderedPlayerIndex = 0;
  4583. xfer->xferUnsignedInt(&surrenderedFramesLeft);
  4584. xfer->xferInt(&surrenderedPlayerIndex);
  4585. #endif
  4586. }
  4587. else if (version == 2)
  4588. {
  4589. #ifdef ALLOW_SURRENDER
  4590. DEBUG_CRASH(("fix me ALLOW_SURRENDER")); // should not happen
  4591. #endif
  4592. // demoralize only
  4593. #ifdef ALLOW_DEMORALIZE
  4594. xfer->xferUnsignedInt(&m_demoralizedFramesLeft);
  4595. #else
  4596. UnsignedInt tmp0 = 0;
  4597. xfer->xferUnsignedInt(&tmp0);
  4598. #endif
  4599. }
  4600. else
  4601. {
  4602. // else no surrender or demoralize
  4603. #ifdef ALLOW_SURRENDER
  4604. DEBUG_CRASH(("fix me ALLOW_SURRENDER")); // should not happen
  4605. #endif
  4606. #ifdef ALLOW_DEMORALIZE
  4607. DEBUG_CRASH(("fix me ALLOW_DEMORALIZE")); // should not happen
  4608. #endif
  4609. }
  4610. xfer->xferObjectID(&m_crateCreated);
  4611. if (version < 3)
  4612. {
  4613. Int repulsorCountdown = 0;
  4614. xfer->xferInt(&repulsorCountdown);
  4615. }
  4616. } // end xfer
  4617. // ------------------------------------------------------------------------------------------------
  4618. /** Load post process */
  4619. // ------------------------------------------------------------------------------------------------
  4620. void AIUpdateInterface::loadPostProcess( void )
  4621. {
  4622. UpdateModule::loadPostProcess();
  4623. if (m_fixLocoInPostProcess && m_curLocomotorSet!=LOCOMOTORSET_INVALID)
  4624. {
  4625. m_fixLocoInPostProcess = FALSE;
  4626. LocomotorSetType lst = m_curLocomotorSet;
  4627. // Set the current to invalid, because chooseLocomotorSet aborts if it is already set to the desired value.
  4628. m_curLocomotorSet = LOCOMOTORSET_INVALID;
  4629. chooseLocomotorSet(lst);
  4630. }
  4631. if (!isMoving()) {
  4632. m_pathfindGoalCell.x = -1;
  4633. m_pathfindGoalCell.y = -1;
  4634. TheAI->pathfinder()->updateGoal(getObject(), getObject()->getPosition(), getObject()->getLayer());
  4635. m_pathfindCurCell.x = -1;
  4636. m_pathfindCurCell.y = -1;
  4637. TheAI->pathfinder()->updatePos(getObject(), getObject()->getPosition());
  4638. } else {
  4639. if (m_pathfindGoalCell.x >= 0 && m_pathfindGoalCell.y >= 0) {
  4640. Coord3D goalPos;
  4641. goalPos.x = m_pathfindGoalCell.x * PATHFIND_CELL_SIZE_F + PATHFIND_CELL_SIZE_F*0.5f;
  4642. goalPos.y = m_pathfindGoalCell.y * PATHFIND_CELL_SIZE_F + PATHFIND_CELL_SIZE_F*0.5f;
  4643. m_pathfindGoalCell.x = -1;
  4644. m_pathfindGoalCell.y = -1;
  4645. TheAI->pathfinder()->updateGoal(getObject(), &goalPos, getObject()->getLayer());
  4646. }
  4647. }
  4648. } // end loadPostProcess
  4649. // ------------------------------------------------------------------------------------------------
  4650. // ------------------------------------------------------------------------------------------------
  4651. Int AIUpdateInterface::friend_getWaypointGoalPathSize() const
  4652. {
  4653. //
  4654. // it is VERY IMPORTANT to check for the current state type as being follow-path,
  4655. // because "getGoalPath" and friends are used for other things (eg, jet takeoff and landing).
  4656. // if you don't do this check, you will end up with really bizarre behavior in obscure jet-related
  4657. // cases, and our users will all laugh at us.
  4658. //
  4659. // the goalpath should really be completely private, but at this point, this ugly scheme
  4660. // has to be lived with. (srj)
  4661. //
  4662. if (getAIStateType() != AI_FOLLOW_PATH)
  4663. return 0;
  4664. return getStateMachine()->getGoalPathSize();
  4665. }
  4666. // ------------------------------------------------------------------------------------------------
  4667. Bool AIUpdateInterface::hasLocomotorForSurface(LocomotorSurfaceType surfaceType)
  4668. {
  4669. LocomotorSurfaceTypeMask surfaceMask = (LocomotorSurfaceTypeMask)surfaceType;
  4670. if (m_locomotorSet.findLocomotor(surfaceMask))
  4671. return TRUE;
  4672. else
  4673. return FALSE;
  4674. }
  4675. // ------------------------------------------------------------------------------------------------