AIUpdate.cpp 160 KB

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