| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931 |
- /*
- ** Command & Conquer Generals(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // AIStates.cpp
- // Implementation of AI behavior states
- // Author: Michael S. Booth, January 2002
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #include "Common/ActionManager.h"
- #include "Common/AudioHandleSpecialValues.h"
- #include "Common/CRCDebug.h"
- #include "Common/GameAudio.h"
- #include "Common/GlobalData.h"
- #include "Common/Money.h"
- #include "Common/PerfTimer.h"
- #include "Common/Player.h"
- #include "Common/PlayerList.h"
- #include "Common/RandomValue.h"
- #include "Common/Team.h"
- #include "Common/ThingTemplate.h"
- #include "Common/ThingFactory.h"
- #include "Common/Xfer.h"
- #include "Common/XFerCRC.h"
- #include "GameClient/ControlBar.h"
- #include "GameClient/FXList.h"
- #include "GameClient/InGameUI.h"
- #include "GameLogic/AIDock.h"
- #include "GameLogic/AIGuard.h"
- #include "GameLogic/AITNGuard.h"
- #include "GameLogic/AIStateMachine.h"
- #include "GameLogic/AIPathfind.h"
- #include "GameLogic/Locomotor.h"
- #include "GameLogic/Module/AIUpdate.h"
- #include "GameLogic/Module/BodyModule.h"
- #include "GameLogic/Module/ContainModule.h"
- #include "GameLogic/Module/PhysicsUpdate.h"
- #include "GameLogic/Module/StealthUpdate.h"
- #include "GameLogic/PartitionManager.h"
- #include "GameLogic/PolygonTrigger.h"
- #include "GameLogic/ScriptEngine.h"
- #include "GameLogic/Squad.h"
- #include "GameLogic/TurretAI.h"
- #include "GameLogic/Weapon.h"
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- static Bool cannotPossiblyAttackObject( State *thisState, void* userData );
- //----------------------------------------------------------------------------------------------------------
- AICommandParms::AICommandParms(AICommandType cmd, CommandSourceType cmdSource) :
- m_cmd(cmd),
- m_cmdSource(cmdSource),
- m_obj(NULL),
- m_otherObj(NULL),
- m_team(NULL),
- m_waypoint(NULL),
- m_polygon(NULL),
- m_intValue(0),
- m_commandButton(NULL),
- m_path(NULL)
- {
- m_pos.zero();
- m_coords.clear();
- }
- //----------------------------------------------------------------------------------------------------------
- void AICommandParmsStorage::store(const AICommandParms& parms)
- {
- m_cmd = parms.m_cmd;
- m_cmdSource = parms.m_cmdSource;
- m_pos = parms.m_pos;
- m_obj = parms.m_obj ? parms.m_obj->getID() : INVALID_ID;
- m_otherObj = parms.m_otherObj ? parms.m_otherObj->getID() : INVALID_ID;
- m_teamName = parms.m_team ? parms.m_team->getName() : AsciiString::TheEmptyString;
- m_coords = parms.m_coords;
- m_waypoint = parms.m_waypoint;
- m_polygon = parms.m_polygon;
- m_intValue = parms.m_intValue; /// misc usage
- m_damage = parms.m_damage;
- m_commandButton = parms.m_commandButton;
- m_path = parms.m_path; /// @todo srj -- probably need a better way to safely save/restore this
- }
- //----------------------------------------------------------------------------------------------------------
- void AICommandParmsStorage::reconstitute(AICommandParms& parms) const
- {
- parms.m_cmd = m_cmd;
- parms.m_cmdSource = m_cmdSource;
- parms.m_pos = m_pos;
- parms.m_obj = TheGameLogic->findObjectByID(m_obj);
- parms.m_otherObj = TheGameLogic->findObjectByID(m_otherObj);
- parms.m_team = TheTeamFactory->findTeam(m_teamName);
- parms.m_coords = m_coords;
- parms.m_waypoint = m_waypoint;
- parms.m_polygon = m_polygon;
- parms.m_intValue = m_intValue;
- parms.m_damage = m_damage;
- parms.m_commandButton = m_commandButton;
- parms.m_path = m_path; /// @todo srj -- probably need a better way to safely save/restore this
- }
- //----------------------------------------------------------------------------------------------------------
- void AICommandParmsStorage::doXfer(Xfer *xfer)
- {
- xfer->xferUser(&m_cmd, sizeof(m_cmd));
- xfer->xferUser(&m_cmd, sizeof(m_cmdSource));
- xfer->xferCoord3D(&m_pos);
- xfer->xferObjectID(&m_obj);
- xfer->xferObjectID(&m_otherObj);
- xfer->xferAsciiString(&m_teamName);
- Int numCoords = m_coords.size();
- xfer->xferInt(&numCoords);
- Int i;
- if (xfer->getXferMode() == XFER_LOAD)
- {
- for (i=0; i<numCoords; i++) {
- Coord3D pos;
- xfer->xferCoord3D(&pos);
- m_coords.push_back(pos);
- }
- } else {
- for (i=0; i<numCoords; i++) {
- Coord3D pos = m_coords[i];
- xfer->xferCoord3D(&pos);
- }
- }
- UnsignedInt id = INVALID_WAYPOINT_ID;
- if (m_waypoint) {
- id = m_waypoint->getID();
- }
- xfer->xferUnsignedInt(&id);
- if (xfer->getXferMode() == XFER_LOAD && id!= INVALID_WAYPOINT_ID)
- {
- m_waypoint = TheTerrainLogic->getWaypointByID(id);
- }
- AsciiString triggerName;
- if (m_polygon) triggerName = m_polygon->getTriggerName();
- xfer->xferAsciiString(&triggerName);
- if (xfer->getXferMode() == XFER_LOAD)
- {
- if (triggerName.isNotEmpty()) {
- m_polygon = TheTerrainLogic->getTriggerAreaByName(triggerName);
- }
- }
- xfer->xferInt(&m_intValue);
- xfer->xferSnapshot(&m_damage);
- AsciiString cmdName;
- if (m_commandButton) {
- cmdName = m_commandButton->getName();
- }
- xfer->xferAsciiString(&cmdName);
- if (cmdName.isNotEmpty() && m_commandButton==NULL) {
- m_commandButton = TheControlBar->findCommandButton(cmdName);
- }
- Bool hasPath = m_path!=NULL;
- xfer->xferBool(&hasPath);
- if (hasPath && m_path==NULL) {
- m_path = newInstance(Path);
- }
- if (hasPath) {
- xfer->xferSnapshot(m_path);
- }
- }
- //----------------------------------------------------------------------------------------------------------
- /**
- * Compare two positions to see if they are logically equal
- * @todo Move this somewhere more useful (MSB)
- */
- static Bool isSamePosition( const Coord3D *ourPos, const Coord3D *prevTargetPos, const Coord3D *curTargetPos )
- {
- Coord3D diff;
- // for pathfinding purposes, only care about 2d pos. (srj)
- diff.x = curTargetPos->x - prevTargetPos->x;
- diff.y = curTargetPos->y - prevTargetPos->y;
- Coord3D toTarget;
- // for pathfinding purposes, only care about 2d pos. (srj)
- toTarget.x = curTargetPos->x - ourPos->x;
- toTarget.y = curTargetPos->y - ourPos->y;
- // Tolerance is (dist/10)squared.
- const Real TOLERANCE_FACTOR = 1.0f / (10.0f * 10.0f);
- Real toleranceSqr = (toTarget.x*toTarget.x+toTarget.y*toTarget.y) * TOLERANCE_FACTOR;
- if (diff.x * diff.x + diff.y * diff.y > toleranceSqr)
- return false;
- return true;
- }
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AttackStateMachine::crc( Xfer *xfer )
- {
- StateMachine::crc(xfer);
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AttackStateMachine::xfer( Xfer *xfer )
- {
- XferVersion cv = 1;
- XferVersion v = cv;
- xfer->xferVersion( &v, cv );
- StateMachine::xfer(xfer);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AttackStateMachine::loadPostProcess( void )
- {
- StateMachine::loadPostProcess();
- } // end loadPostProcess
- //----------------------------------------------------------------------------------------------------------
- static Bool inWeaponRangeObject(State *thisState, void* userData);
- //----------------------------------------------------------------------------------------------------------
- /**
- * Create an AI state machine. Define all of the states the machine
- * can possibly be in, and set the initial (default) state.
- */
- AttackStateMachine::AttackStateMachine( Object *obj, AIAttackState* att, AsciiString name, Bool follow, Bool attackingObject, Bool forceAttacking )
- : StateMachine( obj, name )
- {
- // we want to use the CONTINUE mode (not NEW) since we already have acquired the target.
- static const StateConditionInfo objectConditionsNormal[] =
- {
- StateConditionInfo(outOfWeaponRangeObject, AttackStateMachine::CHASE_TARGET, NULL),
- StateConditionInfo(wantToSquishTarget, AttackStateMachine::CHASE_TARGET, NULL),
- StateConditionInfo(cannotPossiblyAttackObject, EXIT_MACHINE_WITH_FAILURE, (void*)ATTACK_CONTINUED_TARGET),
- StateConditionInfo(NULL, NULL, NULL) // keep last
- };
- // we want to use the CONTINUE mode (not NEW) since we already have acquired the target.
- static const StateConditionInfo objectConditionsForced[] =
- {
- StateConditionInfo(outOfWeaponRangeObject, AttackStateMachine::CHASE_TARGET, NULL),
- StateConditionInfo(cannotPossiblyAttackObject, EXIT_MACHINE_WITH_FAILURE, (void*)ATTACK_CONTINUED_TARGET_FORCED),
- StateConditionInfo(wantToSquishTarget, AttackStateMachine::CHASE_TARGET, NULL),
- StateConditionInfo(NULL, NULL, NULL) // keep last
- };
- const StateConditionInfo* objectConditions = forceAttacking ? objectConditionsForced : objectConditionsNormal;
- static const StateConditionInfo positionConditions[] =
- {
- StateConditionInfo(outOfWeaponRangePosition, AttackStateMachine::CHASE_TARGET, NULL),
- StateConditionInfo(NULL, NULL, NULL) // keep last
- };
- #ifdef STATE_MACHINE_DEBUG
- AsciiString fullName = name;
- if (follow) fullName.concat(" follow");
- if (attackingObject) fullName.concat(" object");
- setName(fullName);
- //setDebugOutput(true);
- #endif
- // order matters: first state is the default state.
- // The default is Aim rather than Approach so things that cannot move will be able to shoot
- // things that are in range. Things that cannot move will automatically FAILURE on approach state.
- /*
- This state will succeed when we are aiming a useful weapon at the victim, and fail
- if the victim is dead. (Exception: if the weapon is on a turret, we don't leave this
- state unless we get out of range.)
- */
- defineState( AttackStateMachine::AIM_AT_TARGET,
- newInstance(AIAttackAimAtTargetState)( this, attackingObject, forceAttacking ),
- AttackStateMachine::FIRE_WEAPON,
- EXIT_MACHINE_WITH_FAILURE,
- attackingObject ? objectConditions : positionConditions );
- /*
- Note that the fire state succeeds iff it is able to fire... it will "fail"
- if unable to fire. However, it may be unable to fire because the target object
- is already dead.
- */
- defineState( AttackStateMachine::FIRE_WEAPON,
- newInstance(AIAttackFireWeaponState)( this, att ),
- AttackStateMachine::AIM_AT_TARGET,
- AttackStateMachine::AIM_AT_TARGET,
- attackingObject ? objectConditions : positionConditions );
- if (obj->isKindOf(KINDOF_IMMOBILE) == FALSE)
- {
- if (obj->isKindOf(KINDOF_PORTABLE_STRUCTURE) && obj->isKindOf(KINDOF_CAN_ATTACK))
- {
- static const StateConditionInfo portableStructureChaseConditions[] =
- {
- StateConditionInfo(inWeaponRangeObject, AttackStateMachine::AIM_AT_TARGET, NULL),
- StateConditionInfo(NULL, NULL, NULL) // keep last
- };
- /* we're a rider on a mobile object, so we can't control our motion.
- just make bogus states that always fall back into "aim".
- */
- defineState( AttackStateMachine::CHASE_TARGET,
- newInstance(ContinueState)(this),
- EXIT_MACHINE_WITH_FAILURE,
- EXIT_MACHINE_WITH_FAILURE,
- portableStructureChaseConditions );
- }
- else if (attackingObject)
- {
- /*
- This state will pursue a target that is moving away from it. If it is not moving away,
- it will drop into the AIAttackApproachTarget state.
- */
- defineState( AttackStateMachine::CHASE_TARGET,
- newInstance(AIAttackPursueTargetState)( this, follow, attackingObject, forceAttacking ),
- AttackStateMachine::APPROACH_TARGET,
- AttackStateMachine::APPROACH_TARGET );
- /*
- This state will succeed when we have a useful weapon within range of victim, and fail
- if the victim is dead
- */
- defineState( AttackStateMachine::APPROACH_TARGET,
- newInstance(AIAttackApproachTargetState)( this, follow, attackingObject, forceAttacking ),
- AttackStateMachine::AIM_AT_TARGET,
- EXIT_MACHINE_WITH_FAILURE );
- }
- else
- {
- /*
- This state will succeed when we have a useful weapon within range of victim, and fail
- if the victim is dead
- */
- defineState( AttackStateMachine::CHASE_TARGET,
- newInstance(AIAttackApproachTargetState)( this, follow, attackingObject, forceAttacking ),
- AttackStateMachine::AIM_AT_TARGET,
- EXIT_MACHINE_WITH_FAILURE );
- /*
- This state will succeed when we have a useful weapon within range of victim, and fail
- if the victim is dead
- */
- defineState( AttackStateMachine::APPROACH_TARGET,
- newInstance(AIAttackApproachTargetState)( this, follow, attackingObject, forceAttacking ),
- AttackStateMachine::AIM_AT_TARGET,
- EXIT_MACHINE_WITH_FAILURE );
- }
- }
- else
- {
- /*
- This state always instantly fails, so when immobile things transition here, we bail.
- */
- defineState( AttackStateMachine::CHASE_TARGET,
- newInstance(FailureState)(this),
- EXIT_MACHINE_WITH_FAILURE,
- EXIT_MACHINE_WITH_FAILURE );
- }
- };
- //----------------------------------------------------------------------------------------------------------
- AttackStateMachine::~AttackStateMachine()
- {
- }
- //-----------------------------------------------------------------------------------------------------------
- //-----------------------------------------------------------------------------------------------------------
- //-----------------------------------------------------------------------------------------------------------
- static Object* findEnemyInContainer(Object* killer, Object* bldg)
- {
- const ContainedItemsList* items = bldg->getContain() ? bldg->getContain()->getContainedItemsList() : NULL;
- if (items)
- {
- for (ContainedItemsList::const_iterator it = items->begin(); it != items->end(); ++it )
- {
- if ((*it)->isEffectivelyDead())
- {
- DEBUG_CRASH(("why is there a dead thing in this container?"));
- continue;
- }
- // order matters: we want to know if I consider it to be an enemy, not vice versa
- if (killer->getRelationship(*it) == ENEMIES)
- {
- return *it;
- }
- }
- }
- return NULL;
- }
- //-----------------------------------------------------------------------------------------------------------
- static Int killEnemiesInContainer(Object* killer, Object* bldg, Int maxToKill)
- {
- Int numKilled = 0;
- while (numKilled < maxToKill)
- {
- Object* enemy = findEnemyInContainer(killer, bldg);
- if (enemy)
- {
- Object *containedByObject = enemy->getContainedBy();
- if( containedByObject )
- {
- ContainModuleInterface *contain = containedByObject->getContain();
- if( contain )
- {
- contain->removeFromContain(enemy);
- }
- }
- if (killer)
- killer->scoreTheKill( enemy );
- enemy->kill();
- ++numKilled;
- }
- else
- {
- break;
- }
- }
- return numKilled;
- }
- //-----------------------------------------------------------------------------------------------------------
- //-----------------------------------------------------------------------------------------------------------
- AIRappelState::AIRappelState( StateMachine *machine ) : State( machine, "AIRappelState" )
- {
- }
- //-----------------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIRappelState::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIRappelState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
-
- xfer->xferReal(&m_rappelRate);
- xfer->xferReal(&m_destZ);
- xfer->xferBool(&m_targetIsBldg);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIRappelState::loadPostProcess( void )
- {
- } // end loadPostProcess
- //-----------------------------------------------------------------------------------------------------------
- StateReturnType AIRappelState::onEnter()
- {
- Object* obj = getMachineOwner();
- if (!obj->isKindOf(KINDOF_CAN_RAPPEL))
- return STATE_FAILURE;
- //AIUpdateInterface* ai = obj->getAI();
- obj->setModelConditionState(MODELCONDITION_RAPPELLING);
- // don't do this, or we'll be unable to be forced out of this state.
- // instead, just manipulate physics directly.
- //obj->setHeld();
- obj->getPhysics()->resetDynamicPhysics();
- m_targetIsBldg = true;
- Object* bldg = getMachineGoalObject();
- if (bldg == NULL || bldg->isEffectivelyDead() || !bldg->isKindOf(KINDOF_STRUCTURE))
- m_targetIsBldg = false;
- const Coord3D* pos = obj->getPosition();
- const Bool onlyHealthyBridges = true; // ignore dead bridges.
- PathfindLayerEnum layerAtDest = TheTerrainLogic->getHighestLayerForDestination(pos, onlyHealthyBridges);
- m_destZ = TheTerrainLogic->getLayerHeight(pos->x, pos->y, layerAtDest);
- if (m_targetIsBldg)
- m_destZ += bldg->getGeometryInfo().getMaxHeightAbovePosition();
- else
- obj->setLayer(layerAtDest);
- AIUpdateInterface *ai = obj->getAI();
- Real MAX_RAPPEL_RATE = fabs(TheGlobalData->m_gravity) * LOGICFRAMES_PER_SECOND * 2.5f;
- m_rappelRate = -min(ai->getDesiredSpeed(), MAX_RAPPEL_RATE);
- return STATE_CONTINUE;
- }
- //-----------------------------------------------------------------------------------------------------------
- StateReturnType AIRappelState::update()
- {
- StateReturnType result = STATE_CONTINUE;
-
- Object* obj = getMachineOwner();
- const Coord3D* pos = obj->getPosition();
- Object* bldg = getMachineGoalObject();
- if (m_targetIsBldg && (bldg == NULL || bldg->isEffectivelyDead()))
- {
- // if bldg is destroyed, just head for the ground
- // BGC - bldg could be destroyed as they are heading down the rope.
- m_targetIsBldg = false;
- m_destZ = TheTerrainLogic->getGroundHeight(pos->x, pos->y);
- }
-
- // nuke 2d speed...
- obj->getPhysics()->scrubVelocity2D(0);
- // and clamp z speed to rappel rate (gravity will have accelerated us)
- obj->getPhysics()->scrubVelocityZ(m_rappelRate);
- if (!m_targetIsBldg)
- {
- // if heading for ground, do this every frame... since jitter at the very start
- // can move us slightly, and on uneven ground it might matter.
- m_destZ = TheTerrainLogic->getLayerHeight(pos->x, pos->y, obj->getLayer());
- }
- if (pos->z <= m_destZ)
- {
- Coord3D tmp = *pos;
- tmp.z = m_destZ;
- obj->setPosition(&tmp);
-
- if (m_targetIsBldg)
- {
- DEBUG_ASSERTCRASH(TheActionManager->canEnterObject(obj, bldg, obj->getAI()->getLastCommandSource(), COMBATDROP_INTO), ("Hmm, this seems unlikely"));
- // if there are enemies... kill up to two. if we kill two, then we die ourselves,
- // otherwise we enter the bldg.
- const Int MAX_TO_KILL = 2;
- Int numKilled = killEnemiesInContainer(obj, bldg, MAX_TO_KILL);
-
- if (numKilled > 0)
- {
- const FXList* fx = obj->getTemplate()->getPerUnitFX("CombatDropKillFX");
- FXList::doFXObj(fx, bldg, NULL);
- DEBUG_LOG(("Killing %d enemies in combat drop!\n",numKilled));
- }
- if (numKilled == MAX_TO_KILL)
- {
- obj->kill();
- DEBUG_LOG(("Killing SELF in combat drop!\n"));
- }
- else
- {
- if (bldg->getContain() && bldg->getContain()->isValidContainerFor(obj, TRUE ))
- {
- bldg->getContain()->addToContain(obj);
- }
- else
- {
- // this can legitimately happen if you drop into a full (or nearly full) building.
- // let's just place the guy on the ground nearby, since it sucks to fall from the top of a building.
- Real exitAngle = bldg->getOrientation();
- // Garrison doesn't have reserveDoor or exitDelay, so if we do nothing, everyone will appear on top
- // of each other and get stuck inside each others' extent (except for the first guy). So we'll
- // scatter the start point around a little to make it better.
- Real offset = min(obj->getGeometryInfo().getBoundingCircleRadius(),
- bldg->getGeometryInfo().getBoundingCircleRadius());
- Real angle = GameLogicRandomValueReal( PI, 2*PI );//Downish.
- Coord3D startPosition = *bldg->getPosition();
- startPosition.x += offset * Cos( angle );
- startPosition.y += offset * Sin( angle );
- startPosition.z = TheTerrainLogic->getGroundHeight( startPosition.x, startPosition.y );
- obj->setPosition( &startPosition );
- obj->setOrientation( exitAngle );
-
- FindPositionOptions options;
- options.startAngle = (Real)(1.5 * PI);//Down.
- options.maxRadius = 200;
- Coord3D endPosition;
- Bool foundPosition = ThePartitionManager->findPositionAround( &startPosition, &options, &endPosition );
- if( foundPosition )
- {
- std::vector<Coord3D> exitPath;
- exitPath.push_back(endPosition);
- AIUpdateInterface* ai = obj->getAI();
- if( ai )
- {
- ai->aiFollowPath( &exitPath, bldg, CMD_FROM_AI );
- }
- }
- }
- }
- }
- result = STATE_SUCCESS;
- }
- return result;
- }
- //-----------------------------------------------------------------------------------------------------------
- void AIRappelState::onExit( StateExitType status )
- {
- Object* obj = getMachineOwner();
- AIUpdateInterface *ai = obj->getAI();
- obj->clearModelConditionState(MODELCONDITION_RAPPELLING);
- // don't do this, or we'll be unable to be forced out of this state.
- // instead, just manipulate physics directly.
- //obj->clearHeld();
- ai->setDesiredSpeed( FAST_AS_POSSIBLE );
- }
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- /*
- NOTE NOTE NOTE NOTE NOTE
- Do NOT subclass this unless you want ALL of the states this machine possesses.
- If you only want SOME of the states, please make a new StateMachine, descended
- from StateMachine, NOT AIStateMachine. Thank you. (srj)
- NOTE NOTE NOTE NOTE NOTE
- */
- AIStateMachine::AIStateMachine( Object *obj, AsciiString name ) : StateMachine( obj, name )
- {
- DEBUG_ASSERTCRASH(getOwner(), ("An AI State Machine '%s' was constructed without an owner, please tell JKMCD", name));
- DEBUG_ASSERTCRASH(getOwner()->getAI(), ("An AI State Machine '%s' was constructed without an AIUpdateInterface, please tell JKMCD", name));
- m_goalPath.clear();
- m_goalWaypoint = NULL;
- m_goalSquad = NULL;
- m_temporaryState = NULL;
- m_temporaryStateFramEnd = 0;
- // order matters: first state is the default state.
- defineState( AI_IDLE, newInstance(AIIdleState)( this, AIIdleState::LOOK_FOR_TARGETS), AI_IDLE, AI_IDLE );
- defineState( AI_MOVE_TO, newInstance(AIMoveToState)( this ), AI_IDLE, AI_IDLE );
- defineState( AI_MOVE_OUT_OF_THE_WAY, newInstance(AIMoveOutOfTheWayState)( this ), AI_IDLE, AI_IDLE );
- defineState( AI_MOVE_AND_TIGHTEN, newInstance(AIMoveAndTightenState)( this ), AI_IDLE, AI_IDLE );
- defineState( AI_MOVE_AWAY_FROM_REPULSORS, newInstance(AIMoveAwayFromRepulsorsState)( this ), AI_WANDER_IN_PLACE, AI_WANDER_IN_PLACE );
- defineState( AI_WANDER_IN_PLACE, newInstance(AIWanderInPlaceState)( this ), AI_MOVE_AWAY_FROM_REPULSORS, AI_MOVE_AWAY_FROM_REPULSORS );
- defineState( AI_ATTACK_MOVE_TO, newInstance(AIAttackMoveToState)( this ), AI_IDLE, AI_IDLE );
- defineState( AI_ATTACKFOLLOW_WAYPOINT_PATH_AS_TEAM, newInstance(AIAttackFollowWaypointPathState)( this, true ), AI_IDLE, AI_IDLE );
- defineState( AI_ATTACKFOLLOW_WAYPOINT_PATH_AS_INDIVIDUALS, newInstance(AIAttackFollowWaypointPathState)( this, false ), AI_IDLE, AI_IDLE );
-
- defineState( AI_FOLLOW_WAYPOINT_PATH_AS_TEAM, newInstance(AIFollowWaypointPathState)( this, true ), AI_IDLE, AI_IDLE );
- defineState( AI_FOLLOW_WAYPOINT_PATH_AS_INDIVIDUALS, newInstance(AIFollowWaypointPathState)( this, false ), AI_IDLE, AI_IDLE );
- defineState( AI_FOLLOW_WAYPOINT_PATH_AS_TEAM_EXACT, newInstance(AIFollowWaypointPathExactState)( this, true ), AI_IDLE, AI_IDLE );
- defineState( AI_FOLLOW_WAYPOINT_PATH_AS_INDIVIDUALS_EXACT,newInstance(AIFollowWaypointPathExactState)( this, false ), AI_IDLE, AI_IDLE );
- defineState( AI_FOLLOW_PATH, newInstance(AIFollowPathState)( this ), AI_IDLE, AI_IDLE );
- defineState( AI_FOLLOW_EXITPRODUCTION_PATH, newInstance(AIFollowPathState)( this ), AI_IDLE, AI_IDLE );
- defineState( AI_MOVE_AND_EVACUATE, newInstance(AIMoveAndEvacuateState)( this ), AI_IDLE, AI_IDLE );
- defineState( AI_MOVE_AND_EVACUATE_AND_EXIT, newInstance(AIMoveAndEvacuateState)( this ), AI_MOVE_AND_DELETE, AI_MOVE_AND_DELETE );
- defineState( AI_MOVE_AND_DELETE, newInstance(AIMoveAndDeleteState)( this ), AI_IDLE, AI_IDLE );
- defineState( AI_WAIT, newInstance(AIWaitState)( this ), AI_IDLE, AI_IDLE );
- defineState( AI_ATTACK_POSITION, newInstance(AIAttackState)( this, false, false, false, NULL ), AI_IDLE, AI_IDLE );
- defineState( AI_ATTACK_OBJECT, newInstance(AIAttackState)( this, false, true, false, NULL ), AI_IDLE, AI_IDLE );
- defineState( AI_FORCE_ATTACK_OBJECT, newInstance(AIAttackState)( this, false, true, true, NULL ), AI_IDLE, AI_IDLE );
- defineState( AI_ATTACK_AND_FOLLOW_OBJECT, newInstance(AIAttackState)( this, true, true, false, NULL ), AI_IDLE, AI_IDLE );
- defineState( AI_ATTACK_SQUAD, newInstance(AIAttackSquadState)( this, NULL ), AI_IDLE, AI_IDLE );
- defineState( AI_WANDER, newInstance(AIWanderState)( this ), AI_IDLE, AI_MOVE_AWAY_FROM_REPULSORS );
- defineState( AI_PANIC, newInstance(AIPanicState)( this ), AI_IDLE, AI_MOVE_AWAY_FROM_REPULSORS );
- defineState( AI_DEAD, newInstance(AIDeadState)( this ), AI_IDLE, AI_IDLE );
- defineState( AI_DOCK, newInstance(AIDockState)( this ), AI_IDLE, AI_IDLE );
- defineState( AI_ENTER, newInstance(AIEnterState)( this ), AI_IDLE, AI_IDLE );
- defineState( AI_EXIT, newInstance(AIExitState)( this ), AI_IDLE, AI_IDLE );
- defineState( AI_GUARD, newInstance(AIGuardState)( this ), AI_IDLE, AI_IDLE );
- defineState( AI_GUARD_TUNNEL_NETWORK, newInstance(AITunnelNetworkGuardState)( this ), AI_IDLE, AI_IDLE );
- defineState( AI_HUNT, newInstance(AIHuntState)( this ), AI_IDLE, AI_IDLE );
- defineState( AI_ATTACK_AREA, newInstance(AIAttackAreaState)( this ), AI_IDLE, AI_IDLE );
- defineState( AI_FACE_OBJECT, newInstance(AIFaceState)( this, true ), AI_IDLE, AI_IDLE );
- defineState( AI_FACE_POSITION, newInstance(AIFaceState)( this, false ), AI_IDLE, AI_IDLE );
- defineState( AI_PICK_UP_CRATE, newInstance(AIPickUpCrateState)( this ), AI_IDLE, AI_IDLE );
- defineState( AI_RAPPEL_INTO, newInstance(AIRappelState)( this ), AI_IDLE, AI_IDLE );
- defineState( AI_BUSY, newInstance(AIBusyState)( this ), AI_IDLE, AI_IDLE );
- }
- //----------------------------------------------------------------------------------------------------------
- AIStateMachine::~AIStateMachine()
- {
- if (m_goalSquad)
- {
- m_goalSquad->deleteInstance();
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIStateMachine::crc( Xfer *xfer )
- {
- StateMachine::crc(xfer);
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIStateMachine::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- // extend base class
- StateMachine::xfer(xfer);
- Int i;
- Int count = m_goalPath.size();
- xfer->xferInt(&count);
- for (i=0; i<count; i++) {
- Coord3D pos;
- if (xfer->getXferMode() != XFER_LOAD)
- {
- pos = m_goalPath[i];
- }
- xfer->xferCoord3D(&pos);
- if (xfer->getXferMode() == XFER_LOAD)
- {
- m_goalPath.push_back(pos);
- }
- }
- AsciiString waypointName;
- if (m_goalWaypoint) waypointName = m_goalWaypoint->getName();
- xfer->xferAsciiString(&waypointName);
- if (xfer->getXferMode() == XFER_LOAD)
- {
- if (waypointName.isNotEmpty()) {
- m_goalWaypoint = TheTerrainLogic->getWaypointByName(waypointName);
- }
- }
- Bool hasSquad = (m_goalSquad!=NULL);
- xfer->xferBool(&hasSquad);
- if (xfer->getXferMode() == XFER_LOAD)
- {
- if (hasSquad && m_goalSquad==NULL) {
- m_goalSquad = newInstance( Squad );
- }
- }
- if (hasSquad) {
- xfer->xferSnapshot(m_goalSquad);
- }
- StateID id = INVALID_STATE_ID;
- if (m_temporaryState) {
- id = m_temporaryState->getID();
- DEBUG_ASSERTCRASH(id!=INVALID_STATE_ID, ("State has invalid state id, no really. jba."));
- }
- xfer->xferUnsignedInt(&id);
- if (xfer->getXferMode() == XFER_LOAD && id != INVALID_STATE_ID) {
- m_temporaryState = internalGetState( id );
- }
- if (m_temporaryState!=NULL) {
- xfer->xferSnapshot(m_temporaryState);
- }
- xfer->xferUnsignedInt(&m_temporaryStateFramEnd);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIStateMachine::loadPostProcess( void )
- {
- StateMachine::loadPostProcess();
- } // end loadPostProcess
- //----------------------------------------------------------------------------------------------------------
- /**
- * Define a simple path
- */
- void AIStateMachine::setGoalPath( const std::vector<Coord3D>* path )
- {
- m_goalPath = *path;
- }
- #ifdef STATE_MACHINE_DEBUG
- //----------------------------------------------------------------------------------------------------------
- /**
- * Get the current state name.
- */
- AsciiString AIStateMachine::getCurrentStateName(void) const
- {
- AsciiString name = StateMachine::getCurrentStateName();
- if (m_temporaryState) {
- name.concat(" /T/");
- name.concat(m_temporaryState->getName());
- }
- return name;
- }
- #endif
- //-----------------------------------------------------------------------------
- /**
- * Run one step of the machine
- */
- StateReturnType AIStateMachine::updateStateMachine()
- {
- if (m_temporaryState)
- {
- // execute this state
- StateReturnType status = m_temporaryState->update();
- if (m_temporaryStateFramEnd < TheGameLogic->getFrame()) {
- // ran out of time.
- if (status == STATE_CONTINUE) {
- status = STATE_SUCCESS;
- }
- }
- if (status==STATE_CONTINUE) {
- return status;
- }
- m_temporaryState->onExit(EXIT_NORMAL);
- m_temporaryState = NULL;
- }
- return StateMachine::updateStateMachine();
- }
- //----------------------------------------------------------------------------------------------------------
- /**
- * Change the temporary state of the machine, and number of frames limit.th
- */
- StateReturnType AIStateMachine::setTemporaryState( StateID newStateID, Int frameLimitCoount )
- {
- // extract the state associated with the given ID
- State *newState = internalGetState( newStateID );
- #ifdef STATE_MACHINE_DEBUG
- if (getWantsDebugOutput())
- {
- StateID curState = INVALID_STATE_ID;
- if (m_temporaryState) {
- curState = m_temporaryState->getID();
- }
- DEBUG_LOG(("%d '%s' -(TEMP)- '%s' %x exit ", TheGameLogic->getFrame(), getOwner()->getTemplate()->getName().str(), getName().str(), this));
- if (m_temporaryState) {
- DEBUG_LOG((" '%s' ", m_temporaryState->getName().str()));
- } else {
- DEBUG_LOG((" INVALID_STATE_ID "));
- }
- if (newState) {
- DEBUG_LOG(("enter '%s' \n", newState->getName().str()));
- } else {
- DEBUG_LOG(("to INVALID_STATE\n"));
- }
- }
- #endif
- if (m_temporaryState) {
- m_temporaryState->onExit(EXIT_RESET);
- m_temporaryState = NULL;
- }
- if (newState) {
- m_temporaryState = newState;
- StateReturnType ret = m_temporaryState->onEnter();
- if (ret != STATE_CONTINUE) {
- m_temporaryState->onExit(EXIT_NORMAL);
- m_temporaryState = NULL;
- return ret;
- }
- enum {FRAME_COUNT_MAX = 60*LOGICFRAMES_PER_SECOND};
- // If you need to up this check, ok, but 1 minute seems overly long for a temporary state override. jba.
- DEBUG_ASSERTCRASH(frameLimitCoount<=FRAME_COUNT_MAX, ("Unusually long time to set temporary state."));
- if (frameLimitCoount>FRAME_COUNT_MAX) {
- frameLimitCoount = FRAME_COUNT_MAX;
- }
- m_temporaryStateFramEnd = TheGameLogic->getFrame()+frameLimitCoount;
- return ret;
- }
- return STATE_FAILURE;
- }
- //----------------------------------------------------------------------------------------------------------
- /**
- * Add a point to a simple path
- */
- void AIStateMachine::addToGoalPath( const Coord3D *pathPoint)
- {
- if (m_goalPath.size()==0) {
- m_goalPath.push_back(*pathPoint);
- } else {
- Coord3D *finalPoint = &m_goalPath[ m_goalPath.size() - 1 ];
- if( !finalPoint->equals( *pathPoint ) )
- {
- m_goalPath.push_back(*pathPoint);
- }
- }
- }
- //----------------------------------------------------------------------------------------------------------
- /**
- * Return path position at index "i"
- */
- const Coord3D *AIStateMachine::getGoalPathPosition( Int i ) const
- {
- if (i < 0 || i >= m_goalPath.size())
- return NULL;
- return &m_goalPath[i];
- }
- //----------------------------------------------------------------------------------------------------------
- /**
- * Set the current goal waypoint. If we reach this waypoint and there
- * are connections to further points, continue on.
- */
- void AIStateMachine::setGoalWaypoint( const Waypoint *way )
- {
- m_goalWaypoint = way;
- }
- //----------------------------------------------------------------------------------------------------------
- /**
- * Return the current goal waypoint
- */
- const Waypoint *AIStateMachine::getGoalWaypoint()
- {
- return m_goalWaypoint;
- }
- //----------------------------------------------------------------------------------------------------------
- void AIStateMachine::clear()
- {
- StateMachine::clear();
- m_goalPath.clear();
- m_goalWaypoint = NULL;
- m_goalSquad = NULL;
- AIUpdateInterface* ai = getOwner()->getAI();
- if (ai)
- ai->friend_notifyStateMachineChanged();
- }
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIStateMachine::resetToDefaultState()
- {
- StateReturnType tmp = StateMachine::resetToDefaultState();
- AIUpdateInterface* ai = getOwner()->getAI();
- if (ai)
- ai->friend_notifyStateMachineChanged();
- return tmp;
- }
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIStateMachine::setState(StateID newStateID)
- {
- StateID oldID = getCurrentStateID();
- StateReturnType tmp = StateMachine::setState(newStateID);
- AIUpdateInterface* ai = getOwner()->getAI();
- if (ai && oldID != newStateID)
- ai->friend_notifyStateMachineChanged();
- return tmp;
- }
- //----------------------------------------------------------------------------------------------------------
- void AIStateMachine::setGoalTeam( const Team *team )
- {
- if (m_goalSquad == NULL) {
- m_goalSquad = newInstance( Squad );
- }
- m_goalSquad->squadFromTeam(team, true);
- }
- //----------------------------------------------------------------------------------------------------------
- void AIStateMachine::setGoalSquad( const Squad *squad )
- {
- if (m_goalSquad == NULL) {
- m_goalSquad = newInstance( Squad );
- }
- (*m_goalSquad) = (*squad);
- }
- //----------------------------------------------------------------------------------------------------------
- void AIStateMachine::setGoalAIGroup( const AIGroup *group )
- {
- if (m_goalSquad == NULL) {
- m_goalSquad = newInstance( Squad );
- }
- m_goalSquad->squadFromAIGroup(group, true);
- }
- //----------------------------------------------------------------------------------------------------------
- Squad *AIStateMachine::getGoalSquad( void )
- {
- return m_goalSquad;
- }
- // State transition conditions ----------------------------------------------------------------------------
- /**
- * Return true if the machine's owner's current weapon's range
- * cannot reach the goalObject.
- */
- Bool outOfWeaponRangeObject( State *thisState, void* userData )
- {
- Object *obj = thisState->getMachineOwner();
- Object *victim = thisState->getMachineGoalObject();
- Weapon *weapon = obj->getCurrentWeapon();
- CRCDEBUG_LOG(("outOfWeaponRangeObject()\n"));
- if (victim && weapon)
- {
- Bool viewBlocked = false;
- AIUpdateInterface *ai = obj->getAI();
- Bool onGround = true;
- if (ai) {
- onGround = ai->isDoingGroundMovement();
- }
- if( obj->isKindOf(KINDOF_IMMOBILE) ) {
- onGround = true;
- }
- // brutal special case for stinger soldiers, who
- // generally don't have locomotors, but are still on the ground.
- if (obj->isKindOf(KINDOF_SPAWNS_ARE_THE_WEAPONS))
- {
- onGround = true;
- }
- Object *containedBy = obj->getContainedBy();
- if( containedBy && (containedBy->isKindOf( KINDOF_STRUCTURE ) || !containedBy->isAirborneTarget()) )
- {
- //Contained objects on the ground -- garrisoned buildings for example!
- onGround = true;
- }
- // srj sez: at tiny ranges, isAttackViewBlockedByObstacle() can return false positives,
- // so just skip it for contact weapons
- if (victim && !weapon->isContactWeapon() && onGround && !victim->isSignificantlyAboveTerrain())
- {
- viewBlocked = TheAI->pathfinder()->isAttackViewBlockedByObstacle(obj, *obj->getPosition(), victim, *victim->getPosition());
- }
- // A weapon with leech range temporarily has unlimited range and is locked onto its target.
- if (!weapon->hasLeechRange() && viewBlocked)
- {
- //CRCDEBUG_LOG(("outOfWeaponRangeObject() - object %d (%s) view is blocked for attacking %d (%s)\n",
- // obj->getID(), obj->getTemplate()->getName().str(),
- // victim->getID(), victim->getTemplate()->getName().str()));
- return true;
- }
- if (!weapon->hasLeechRange() && !weapon->isWithinAttackRange(obj, victim))
- {
- //CRCDEBUG_LOG(("outOfWeaponRangeObject() - object %d (%s) is out of range for attacking %d (%s)\n",
- // obj->getID(), obj->getTemplate()->getName().str(),
- // victim->getID(), victim->getTemplate()->getName().str()));
- return true;
- }
- }
- return false;
- }
- static Bool inWeaponRangeObject(State *thisState, void* userData)
- {
- return !outOfWeaponRangeObject(thisState, userData);
- }
- Bool wantToSquishTarget( State *thisState, void* userData )
- {
- Object *obj = thisState->getMachineOwner();
- Object *victim = thisState->getMachineGoalObject();
- if (obj && victim)
- {
- if (victim->getContainedBy()) {
- return false; // can't crush guys in buildings or vehicles.
- }
- if( obj->getAI() && (obj->getAI()->getWhichTurretForCurWeapon() != TURRET_INVALID) )
- {
- // I can only decide to crush-attack if I am attacking with a turreted weapon.
- if (TheAI->getAiData()->m_aiCrushesInfantry) {
- if (obj && obj->getControllingPlayer() &&
- obj->getControllingPlayer()->getPlayerType()==PLAYER_COMPUTER) {
- if (obj->canCrushOrSquish(victim)) {
- if (!obj->isKindOf(KINDOF_DONT_AUTO_CRUSH_INFANTRY)) {
- return true;
- }
- }
- }
- }
- }
- }
- return false;
- }
- Bool outOfWeaponRangePosition( State *thisState, void* userData )
- {
- Object *obj = thisState->getMachineOwner();
- const Coord3D *pos = thisState->getMachineGoalPosition();
- Weapon *weapon = obj->getCurrentWeapon();
- if (weapon && pos)
- {
- AIUpdateInterface *ai = obj->getAI();
- Bool onGround = true;
- if (ai) {
- onGround = ai->isDoingGroundMovement();
- }
- if( obj->isKindOf(KINDOF_IMMOBILE) ) {
- onGround = true;
- }
- // brutal special case for stinger soldiers, who
- // generally don't have locomotors, but are still on the ground.
- if (obj->isKindOf(KINDOF_SPAWNS_ARE_THE_WEAPONS))
- {
- onGround = true;
- }
- Object *containedBy = obj->getContainedBy();
- if( containedBy && (containedBy->isKindOf( KINDOF_STRUCTURE ) || !containedBy->isAirborneTarget()) )
- {
- //Contained objects on the ground -- garrisoned buildings for example!
- onGround = true;
- }
- Bool viewBlocked = false;
- if (onGround)
- {
- viewBlocked = TheAI->pathfinder()->isAttackViewBlockedByObstacle(obj, *obj->getPosition(), NULL, *pos);
- }
- if (viewBlocked)
- {
- return true;
- }
- if (!weapon->isWithinAttackRange(obj, pos))
- {
- return true;
- }
- }
- return false;
- }
- /**
- * Return true if the machine's owner's cannot attack in any way
- */
- static Bool cannotPossiblyAttackObject( State *thisState, void* userData )
- {
- AbleToAttackType attackType = (AbleToAttackType)(UnsignedInt)userData;
- Object *obj = thisState->getMachineOwner();
- Object *victim = thisState->getMachineGoalObject();
- if (obj && victim)
- {
- if( !obj->isAbleToAttack() )
- {
- return TRUE;
- }
- CanAttackResult result = obj->getAbleToAttackSpecificObject( attackType, victim, obj->getAI()->getLastCommandSource() );
- if( result != ATTACKRESULT_POSSIBLE && result != ATTACKRESULT_POSSIBLE_AFTER_MOVING )
- {
- return TRUE;
- }
- }
- return FALSE;
- }
- //----------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------
- const UnsignedInt IDLE_COUNTDOWN_DELAY = (LOGICFRAMES_PER_SECOND * 2);
- //----------------------------------------------------------------------------------------------
- AIIdleState::AIIdleState( StateMachine *machine, AIIdleState::AIIdleTargetingType shouldLookForTargets ) :
- State( machine,"AIIdleState"),
- m_shouldLookForTargets(shouldLookForTargets == AIIdleState::LOOK_FOR_TARGETS)
- {
- m_inited = FALSE;
- m_initialSleepOffset = -1;
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIIdleState::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIIdleState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
-
- xfer->xferUnsignedShort(&m_initialSleepOffset);
- xfer->xferBool(&m_shouldLookForTargets);
- xfer->xferBool(&m_inited);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIIdleState::loadPostProcess( void )
- {
- } // end loadPostProcess
- //----------------------------------------------------------------------------------------------
- /**
- * Stake out our space.
- */
- DECLARE_PERF_TIMER(AIIdleState)
- StateReturnType AIIdleState::onEnter()
- {
- USE_PERF_TIMER(AIIdleState)
- Object *obj = getMachineOwner();
- AIUpdateInterface *ai = obj->getAI();
- // We could possibly not have ai here if we were constructed this frame. Strange but true. :-<
- if (ai)
- ai->resetNextMoodCheckTime();
- m_inited = true;
- // reset the idle countdown so that we don't do expensive checks too often,
- // randomized so we avoid spikes
- m_initialSleepOffset = (UnsignedShort)GameLogicRandomValue(0, IDLE_COUNTDOWN_DELAY);
- // never sleep at the start, since we have to do checkGoalPos first time thru
- return STATE_CONTINUE;
- }
- //----------------------------------------------------------------------------------------------
- void AIIdleState::doInitIdleState()
- {
- // Only do it once, but do it in the update cause onEnter for idle is called during object creation. jba.
- if (!m_inited)
- return;
- m_inited = false;
- Object *obj = getMachineOwner();
- AIUpdateInterface *ai = obj->getAI();
- const Locomotor* loco = ai->getCurLocomotor();
- Bool ultraAccurate = (loco != NULL && loco->isUltraAccurate());
- #define NO_STOP_AND_SLIDE
- if (ai->isIdle() && ai->isDoingGroundMovement())
- {
- /*
- You may be asking yourself, "If I'm in an idle state, how can I be doing ground movement?"
- answer from jba:
- If a unit is moving, and you hit stop, it forces it into the idle state.
- Depending where it is, it may be between pathfind grids.
- This is a bad thing.
- So it "cheat moves", to the nearest grid.
- Also, for locos the "close enough" distance is 1 or so.
- So it moves the rest of the way to it's goal location by cheating.
- */
- // Update the goal.
- Coord3D goalPos = *obj->getPosition();
- // but only if we have a valid position.
- if (goalPos.x || goalPos.y || goalPos.z)
- {
- TheAI->pathfinder()->updateGoal(obj, &goalPos, obj->getLayer());
- if (!ultraAccurate && TheAI->pathfinder()->goalPosition(obj, &goalPos))
- {
- if (TheGameLogic->getFrame()<=1) {
- obj->setPosition(&goalPos);
- } else {
- #ifdef STOP_AND_SLIDE
- ai->setFinalPosition(&goalPos);
- #endif
- }
- TheAI->pathfinder()->updateGoal(obj, &goalPos, obj->getLayer());
- }
- }
- }
- ai->setLocomotorGoalNone();
- ai->setCurrentVictim(NULL);
- }
- //----------------------------------------------------------------------------------------------
- /**
- * Just sit there.
- */
- StateReturnType AIIdleState::update()
- {
- USE_PERF_TIMER(AIIdleState)
- doInitIdleState();
- UnsignedInt timeToSleep = IDLE_COUNTDOWN_DELAY + m_initialSleepOffset;
- UnsignedInt oldSleepOffset = m_initialSleepOffset;
- m_initialSleepOffset = 0;
- // This state is used internally some places, so we don't necessarily want to be looking for targets
- // Places that use AI_IDLE internally should set this to false in the constructor. jkmcd
- if ( m_shouldLookForTargets && !getMachine()->isLocked() )
- {
- // if we are here, it's time to check again
- Object *obj = getMachineOwner();
- AIUpdateInterface *ai = obj->getAI();
- // do repulsor logic
- if (obj->isKindOf(KINDOF_CAN_BE_REPULSED) && ai->isIdle())
- {
- Object* enemy = TheAI->findClosestRepulsor(obj, obj->getVisionRange());
- if (enemy)
- {
- getMachine()->setState(AI_MOVE_AWAY_FROM_REPULSORS);
- // since we just changed the state, it doesn't really matter what we return here.
- return STATE_CONTINUE;
- }
- }
- // Check to see if we have created a crate we need to pick up.
- Object* crate = ai->checkForCrateToPickup();
- if (crate)
- {
- ai->aiMoveToObject(crate, CMD_FROM_AI);
- // since we just changed the state, it doesn't really matter what we return here.
- return STATE_CONTINUE;
- }
-
- if (! obj->isDisabledByType( DISABLED_PARALYZED ) &&
- ! obj->isDisabledByType( DISABLED_UNMANNED ) &&
- ! obj->isDisabledByType( DISABLED_EMP ) &&
- ! obj->isDisabledByType( DISABLED_HACKED ) )
- {
- // mood targeting
- UnsignedInt moodAdjust = ai->getMoodMatrixActionAdjustment(MM_Action_Idle);
- if ((moodAdjust & MAA_Affect_Range_IgnoreAll) == 0)
- {
- // If we're supposed to attack based on mood, etc, then we will do so.
- Object* enemy = ai->getNextMoodTarget( true, true );
- if (enemy)
- {
- ai->aiAttackObject(enemy, NO_MAX_SHOTS_LIMIT, CMD_FROM_AI);
- // weird but true. return state_continue, because if we're here, we're actually an attack state
- // since we just changed the state, it doesn't really matter what we return here.
- return STATE_CONTINUE;
- }
- }
- }
- UnsignedInt now = TheGameLogic->getFrame();
- UnsignedInt nextMoodCheckTime = ai->getNextMoodCheckTime();
- if (nextMoodCheckTime > now)
- {
- // if we need to look for targets, might need to sleep less.
- UnsignedInt moodSleep = nextMoodCheckTime - now;
- if (moodSleep < timeToSleep)
- {
- timeToSleep = moodSleep;
- // if we do this, save the random sleep offset for next time.
- m_initialSleepOffset = oldSleepOffset;
- }
- }
- } // end if, should look for targets
-
- return STATE_SLEEP(timeToSleep);
- }
- //----------------------------------------------------------------------------------------------
- /**
- * Just sit there, but dead-like.
- */
- StateReturnType AIDeadState::onEnter()
- {
- Object *obj = getMachineOwner();
- // How can an object be NULL here? I don't think it actually can, but this check must be
- // here for a reason. - jkmcd
- if (obj)
- {
- ModelConditionFlags nonDyingStuff;
- nonDyingStuff.set(MODELCONDITION_USING_WEAPON_A);
- nonDyingStuff.set(MODELCONDITION_USING_WEAPON_B);
- nonDyingStuff.set(MODELCONDITION_USING_WEAPON_C);
- nonDyingStuff.set(MODELCONDITION_FIRING_A);
- nonDyingStuff.set(MODELCONDITION_FIRING_B);
- nonDyingStuff.set(MODELCONDITION_FIRING_C);
- nonDyingStuff.set(MODELCONDITION_BETWEEN_FIRING_SHOTS_A);
- nonDyingStuff.set(MODELCONDITION_BETWEEN_FIRING_SHOTS_B);
- nonDyingStuff.set(MODELCONDITION_BETWEEN_FIRING_SHOTS_C);
- nonDyingStuff.set(MODELCONDITION_RELOADING_A);
- nonDyingStuff.set(MODELCONDITION_RELOADING_B);
- nonDyingStuff.set(MODELCONDITION_RELOADING_C);
- nonDyingStuff.set(MODELCONDITION_PREATTACK_A);
- nonDyingStuff.set(MODELCONDITION_PREATTACK_B);
- nonDyingStuff.set(MODELCONDITION_PREATTACK_C);
- #ifdef ALLOW_SURRENDER
- nonDyingStuff.set(MODELCONDITION_SURRENDER);
- #endif
- nonDyingStuff.set(MODELCONDITION_MOVING);
- // dying objects are NEVER firing, surrendered, etc, so clear 'em all here.
- obj->clearAndSetModelConditionFlags(nonDyingStuff, MAKE_MODELCONDITION_MASK(MODELCONDITION_DYING));
- TheScriptEngine->notifyOfObjectCreationOrDestruction();
- }
- return STATE_CONTINUE;
- }
- StateReturnType AIDeadState::update()
- {
- Object *obj = getMachineOwner();
- AIUpdateInterface *ai = obj->getAI();
- ai->setLocomotorGoalNone();
- // re-mark this every time, just in case our health miraculously heals from 0 to nonzero...
- obj->setEffectivelyDead(true);
- if (obj->isKindOf(KINDOF_INFANTRY))
- {
- PhysicsBehavior* phys = obj->getPhysics();
- if (phys)
- {
- // we want 'em to stop, but looks wonky if they stop dead in their tracks in onEnter.
- // this slows 'em down quickly
- const Real FACTOR = 0.8f; // 0.8 ^ 30 == 0.012, so they slow to 4% of speed over 1 sec
- Real vel = phys->getVelocityMagnitude();
- phys->scrubVelocity2D(vel * FACTOR);
- }
- }
- return STATE_CONTINUE;
- }
- void AIDeadState::onExit( StateExitType status )
- {
- Object *obj = getMachineOwner();
- obj->clearModelConditionState(MODELCONDITION_DYING);
- }
- //----------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIInternalMoveToState::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIInternalMoveToState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
-
- // extend base class
- xfer->xferCoord3D(&m_goalPosition);
- xfer->xferUser(&m_goalLayer, sizeof(m_goalLayer));
- xfer->xferBool(&m_waitingForPath);
- xfer->xferCoord3D(&m_pathGoalPosition);
- xfer->xferUnsignedInt(&m_pathTimestamp);
- xfer->xferUnsignedInt(&m_blockedRepathTimestamp);
- xfer->xferBool(&m_adjustDestinations);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIInternalMoveToState::loadPostProcess( void )
- {
- startMoveSound();
- } // end loadPostProcess
- Bool AIInternalMoveToState::getAdjustsDestination() const
- {
- const Object *obj = getMachineOwner();
- if (obj->testStatus(OBJECT_STATUS_PARACHUTING))
- return false;
- const AIUpdateInterface* ai = obj->getAI();
- if (ai && !ai->isAllowedToAdjustDestination())
- return false;
- return m_adjustDestinations;
- }
- /**
- * (Re)compute a path to the goal position, if we are on our own,
- * or we are the leader of a group.
- */
- Bool AIInternalMoveToState::computePath()
- {
- Object *obj = getMachineOwner();
- AIUpdateInterface *ai = obj->getAI();
- m_waitingForPath = true;
- ai->requestPath(&m_goalPosition, getAdjustsDestination());
- ai->friend_startingMove();
- return true;
- }
- /**
- * We are initiating a moveTo action.
- * Pathfind from m_goalPosition to goal.
- */
- StateReturnType AIInternalMoveToState::onEnter()
- {
- m_ambientPlayingHandle = AHSV_Error;
- Object *obj = getMachineOwner();
- AIUpdateInterface *ai = obj->getAI();
- m_waitingForPath = ai->isWaitingForPath();
- if (ai->getCurLocomotor()) {
- ai->getCurLocomotor()->startMove();
- if (ai->getCurLocomotor()->isUltraAccurate())
- {
- setAdjustsDestination(false); // if we're being ultra accurate, we can't adjust the destination. jba.
- }
- }
- m_tryOneMoreRepath = true; // We may try one more repath after the first one is finished.
- ai->friend_startingMove();
- // get object physics state
- PhysicsBehavior *physics = obj->getPhysics();
- if (physics && physics->isMotive()) {
- // If we were already moving, go immediately to the animation.
- // This is so if you redirect a moving infantry or other animated model, he doesn't
- // pop into idle for 1 frame. jba.
- //Determine if we are on a cliff cell... if so, use the climbing model condition
- //instead of moving.
- Int cellX = REAL_TO_INT( obj->getPosition()->x / PATHFIND_CELL_SIZE );
- Int cellY = REAL_TO_INT( obj->getPosition()->y / PATHFIND_CELL_SIZE );
-
- PathfindCell* cell = TheAI->pathfinder()->getCell( obj->getLayer(), cellX, cellY );
- ModelConditionFlagType modelConditionFlag = (cell && cell->getType() != PathfindCell::CELL_CLIFF) ? MODELCONDITION_MOVING : MODELCONDITION_CLIMBING;
- obj->setModelConditionState( modelConditionFlag );
- }
- // This is a classic January 2 type of hack
- // Since the worker can go into its attack modelcondition in AIAttack::OnEnter(),
- // it needs to get its moving modelcondition set timely enough to trigger a transition
- // from conditionX into ATTACKING|MOVING... Thanks for reading, MLorenzen, Jan 2, 2003
- else if ( obj->isKindOf( KINDOF_DOZER ) && obj->isKindOf( KINDOF_HARVESTER ) )
- obj->setModelConditionState( MODELCONDITION_MOVING );
- if (getAdjustsDestination())
- {
- if (!TheAI->pathfinder()->adjustDestination(obj, ai->getLocomotorSet(), &m_goalPosition))
- {
- TheAI->pathfinder()->snapClosestGoalPosition(obj, &m_goalPosition);
- }
- TheAI->pathfinder()->updateGoal(obj, &m_goalPosition, TheTerrainLogic->getLayerForDestination(&m_goalPosition));
- }
- // request a path to the destination
- if (!computePath())
- {
- ai->friend_endingMove();
- return STATE_FAILURE;
- }
- // Target to stop at the end of this path.
- // This value will be overriden by the FollowWaypoint ai state.
- ai->setPathExtraDistance(0);
- ai->setDesiredSpeed( FAST_AS_POSSIBLE );
- startMoveSound();
- return STATE_CONTINUE;
- }
- /**
- * Start playing the object's move sound.
- */
- void AIInternalMoveToState::startMoveSound(void)
- {
- Object *obj = getMachineOwner();
- const BodyModuleInterface *objBody = obj->getBodyModule();
- if (objBody && IS_CONDITION_WORSE(objBody->getDamageState(), BODY_DAMAGED))
- {
- AudioEventRTS soundEventMoveDamaged = *obj->getTemplate()->getSoundMoveStartDamaged();
- if (!soundEventMoveDamaged.getEventName().isEmpty())
- {
- soundEventMoveDamaged.setObjectID(obj->getID());
- TheAudio->addAudioEvent( &soundEventMoveDamaged );
- }
- else
- {
- soundEventMoveDamaged = *obj->getTemplate()->getSoundMoveLoopDamaged();
- if (!soundEventMoveDamaged.getEventName().isEmpty())
- {
- soundEventMoveDamaged.setObjectID(obj->getID());
- m_ambientPlayingHandle = TheAudio->addAudioEvent( &soundEventMoveDamaged );
- }
- }
- }
- else
- {
- AudioEventRTS soundEventMove = *obj->getTemplate()->getSoundMoveStart();
- soundEventMove.setObjectID(obj->getID());
- if (!soundEventMove.getEventName().isEmpty())
- {
- TheAudio->addAudioEvent( &soundEventMove );
- }
- else
- {
- soundEventMove = *obj->getTemplate()->getSoundMoveLoop();
- soundEventMove.setObjectID(obj->getID());
- if (!soundEventMove.getEventName().isEmpty())
- {
- m_ambientPlayingHandle = TheAudio->addAudioEvent( &soundEventMove );
- }
- }
- }
- }
- /**
- * We are leaving the moveTo state.
- */
- void AIInternalMoveToState::onExit( StateExitType status )
- {
- Object *obj = getMachineOwner();
- AIUpdateInterface *ai = obj->getAI();
- // stop ambient sound associated with movement
- TheAudio->removeAudioEvent( m_ambientPlayingHandle );
- // If this onExit is the result of the state machine being deleted, then there is no AI.
- // (This is why destructors should not do game logic)
- if (ai) {
- ai->friend_endingMove();
- DEBUG_ASSERTLOG(obj->getTeam(), ("AIInternalMoveToState::onExit obj has NULL team.\n"));
- if (obj->getTeam() && ai->isDoingGroundMovement() && ai->getCurLocomotor() &&
- ai->getCurLocomotor()->isUltraAccurate()) {
- Real dx = m_goalPosition.x-obj->getPosition()->x;
- Real dy = m_goalPosition.y-obj->getPosition()->y;
- if (dx*dx+dy*dy<PATHFIND_CELL_SIZE_F*PATHFIND_CELL_SIZE_F)
- {
- // We are doing accurate ground movement, so make sure we end exactly at the goal.
- ai->setFinalPosition(&m_goalPosition);
- }
- }
- }
- }
- /**
- * Execute the moveTo behavior towards GoalPosition.
- */
- StateReturnType AIInternalMoveToState::update()
- {
- Object *obj = getMachineOwner();
- AIUpdateInterface *ai = obj->getAI();
- Path *thePath = ai->getPath();
- if (m_waitingForPath)
- {
- // bump the timer.
- m_pathTimestamp = TheGameLogic->getFrame();
- if (ai->isWaitingForPath()) {
- /// @todo srj -- find a way to sleep for a number of frames here, if possible
- return STATE_CONTINUE;
- }
- if (thePath==NULL) {
- return STATE_FAILURE;
- }
- m_waitingForPath = false;
- m_pathGoalPosition = m_goalPosition;
- if (this->getAdjustsDestination()) {
- TheAI->pathfinder()->updateGoal(obj, thePath->getLastNode()->getPosition(), thePath->getLastNode()->getLayer());
- } else {
- TheAI->pathfinder()->removeGoal(obj);
- }
- if (!ai->getRetryPath()) {
- m_tryOneMoreRepath = false;
- }
- }
- Bool forceRecompute = false;
- if (thePath==NULL) {
- forceRecompute = true;
- }
- Bool blocked=false;
- if (ai->isBlockedAndStuck() || ai->getNumFramesBlocked()>2*LOGICFRAMES_PER_SECOND) {
- forceRecompute = true;
- blocked = true;
- m_blockedRepathTimestamp = TheGameLogic->getFrame();
- // Intense debug logging jba.
- //DEBUG_LOG(("Info - Blocked - recomputing.\n"));
- }
- //Determine if we are on a cliff cell... if so, use the climbing model condition
- //instead of moving.
- Int cellX = REAL_TO_INT( obj->getPosition()->x / PATHFIND_CELL_SIZE );
- Int cellY = REAL_TO_INT( obj->getPosition()->y / PATHFIND_CELL_SIZE );
-
- PathfindCell* cell = TheAI->pathfinder()->getCell( obj->getLayer(), cellX, cellY );
- ModelConditionFlagType setConditionFlag = MODELCONDITION_MOVING;
- // Totally hacky set of conditions to make col. burton's monkey ass not slide down
- // the cliffs backwards. This could use some improvement at some point. jba. 31DEC2002
- if (cell && cell->getType() == PathfindCell::CELL_CLIFF && !cell->getPinched()) {
- if (ai->getCurLocomotor() && ai->getCurLocomotor()->isMovingBackwards()) {
- setConditionFlag = MODELCONDITION_RAPPELLING;
- obj->clearModelConditionState( MODELCONDITION_CLIMBING );
- } else {
- setConditionFlag = MODELCONDITION_CLIMBING;
- obj->clearModelConditionState( MODELCONDITION_RAPPELLING );
- }
- }
- if (ai->getNumFramesBlocked()>LOGICFRAMES_PER_SECOND/4)
- {
- obj->clearModelConditionState( MODELCONDITION_MOVING );
- }
- else
- {
- //Clear climbing if modelConditionFlag is not climbing
- if( setConditionFlag == MODELCONDITION_MOVING )
- {
- obj->clearModelConditionState( MODELCONDITION_CLIMBING );
- obj->clearModelConditionState( MODELCONDITION_RAPPELLING );
- }
- obj->setModelConditionState(MODELCONDITION_MOVING);
- if (setConditionFlag!=MODELCONDITION_MOVING) {
- obj->setModelConditionState( setConditionFlag );
- }
- }
- if (ai->canComputeQuickPath()) {
- if (obj->isKindOf(KINDOF_PROJECTILE)) {
- m_pathTimestamp = 0; // We can compute paths cheap, so don't delay to recompute. jba.
- forceRecompute = true;
- }
- }
- if (thePath != NULL)
- ai->setLocomotorGoalPositionOnPath();
- // if our goal has moved, recompute our path
- if (forceRecompute || TheGameLogic->getFrame() - m_pathTimestamp > MIN_REPATH_TIME)
- {
- if (forceRecompute || !isSamePosition(obj->getPosition(), &m_pathGoalPosition, &m_goalPosition ))
- {
- // goal moved - repath
- if (!computePath())
- return STATE_FAILURE;
- // srj sez: must re-set setLocoGoal after computePath, since computePath
- // can set the loco goal to NONE...
- if (ai->getPath() != NULL)
- ai->setLocomotorGoalPositionOnPath();
- else
- return STATE_CONTINUE;
- }
- }
- //
- // Check if we have reached our destination
- //
- Real onPathDistToGoal = ai->getLocomotorDistanceToGoal();
- //DEBUG_LOG(("onPathDistToGoal = %f %s\n",onPathDistToGoal, obj->getTemplate()->getName().str()));
- if (ai->getCurLocomotor() && (onPathDistToGoal < ai->getCurLocomotor()->getCloseEnoughDist()))
- {
- if (ai->isDoingGroundMovement()) {
- // sanity check
- Coord3D delta;
- Coord3D goalPos = m_goalPosition;
- if (ai->getPath()->getLastNode()) {
- goalPos = *ai->getPath()->getLastNode()->getPosition();
- }
- delta.x = obj->getPosition()->x - goalPos.x;
- delta.y = obj->getPosition()->y - goalPos.y;
- delta.z = 0;
- if (delta.length() > 4*PATHFIND_CELL_SIZE_F) {
- //DEBUG_LOG(("AIInternalMoveToState Trying to finish early. Continuing...\n"));
- onPathDistToGoal = ai->getLocomotorDistanceToGoal();
- return STATE_CONTINUE;
- }
- }
- // we have reached the end of the path
- if (getAdjustsDestination())
- {
- ai->setLocomotorGoalNone();
- }
- DEBUG_ASSERTLOG(!getMachine()->getWantsDebugOutput(), ("AIInternalMoveToState::update: reached end of path, exiting state with success\n"));
- return STATE_SUCCESS;
- }
- return STATE_CONTINUE;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-----------------------------------------------------------------------------------------------------------
- class AIAttackMoveStateMachine : public StateMachine
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( AIAttackMoveStateMachine, "AIAttackMoveStateMachine" );
- public:
- AIAttackMoveStateMachine( Object *owner, AsciiString name );
- protected:
- // snapshot interface
- virtual void crc( Xfer *xfer );
- virtual void xfer( Xfer *xfer );
- virtual void loadPostProcess();
- };
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIAttackMoveStateMachine::crc( Xfer *xfer )
- {
- StateMachine::crc(xfer);
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIAttackMoveStateMachine::xfer( Xfer *xfer )
- {
- XferVersion cv = 1;
- XferVersion v = cv;
- xfer->xferVersion( &v, cv );
- StateMachine::xfer(xfer);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIAttackMoveStateMachine::loadPostProcess( void )
- {
- StateMachine::loadPostProcess();
- } // end loadPostProcess
- //-----------------------------------------------------------------------------------------------------------
- AIAttackMoveStateMachine::AIAttackMoveStateMachine(Object *owner, AsciiString name) : StateMachine(owner, name)
- {
- // order matters: first state is the default state.
- defineState( AI_IDLE, newInstance(AIIdleState)( this, AIIdleState::DO_NOT_LOOK_FOR_TARGETS ), AI_IDLE, AI_IDLE );
- defineState( AI_PICK_UP_CRATE, newInstance(AIPickUpCrateState)( this ), AI_IDLE, AI_IDLE );
- defineState( AI_ATTACK_OBJECT, newInstance(AIAttackState)(this, false, true, false, NULL ), AI_IDLE, AI_IDLE);
- }
- //----------------------------------------------------------------------------------------------------------
- AIAttackMoveStateMachine::~AIAttackMoveStateMachine()
- {
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //
- // note - has no crc/xfer as has no member vars. jba.
- //-------------------------------------------------------------------------------------------------
- AIMoveToState::AIMoveToState(StateMachine *machine) : m_isMoveTo(true), AIInternalMoveToState( machine, "AIMoveToState" )
- {
- // m_isMoveTo is a boolean that specifies that this thing is ACTUALLY A MOVE TO.
- // child classes should set it false. (We don't have or want RTTI, so...)
- }
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIMoveToState::onEnter()
- {
- setAdjustsDestination(true);
- //If we have a goal object and are trying to ignore it as an obstacle...
- //This is used in the case of units trying to get really close.
- AIUpdateInterface *ai = getMachineOwner()->getAI();
- if( getMachineGoalObject() )
- {
- if( ai && getMachineGoalObject()->getID() == ai->getIgnoredObstacleID() )
- {
- setAdjustsDestination( false );
- }
- }
- // if we have a goal object, move to it, otherwise move to goal position
- if (getMachineGoalObject()) {
- m_goalPosition = *getMachineGoalObject()->getPosition();
- if (getMachineOwner()->isKindOf(KINDOF_PROJECTILE)) {
- Real halfHeight = getMachineGoalObject()->getGeometryInfo().getMaxHeightAbovePosition()/2.0f;
- m_goalPosition.z += halfHeight;
- if (getMachineGoalObject()->getPosition()->z < m_goalPosition.z) {
- m_goalPosition.z += halfHeight;
- }
- }
- } else
- m_goalPosition = *getMachineGoalPosition();
- StateReturnType ret = AIInternalMoveToState::onEnter();
- if (getMachineOwner()->getFormationID() != NO_FORMATION_ID) {
- AIGroup *group = ai->getGroup();
- if (group) {
- Real speed = group->getSpeed();
- ai->setDesiredSpeed(speed);
- }
- }
- return ret;
- }
- //----------------------------------------------------------------------------------------------------------
- void AIMoveToState::onExit( StateExitType status )
- {
- AIInternalMoveToState::onExit( status );
- }
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIMoveToState::update()
- {
- AIUpdateInterface *ai = getMachineOwner()->getAI();
- UnsignedInt adjustment = ai->getMoodMatrixActionAdjustment(MM_Action_Move);
- if (m_isMoveTo && (adjustment & MAA_Action_To_AttackMove))
- ai->aiAttackMoveToPosition(&m_goalPosition, NO_MAX_SHOTS_LIMIT, CMD_FROM_AI);
- // if we have a goal object, move to it, as it may have moved
- Object* goalObj = getMachineGoalObject();
- Object *obj = getMachineOwner();
- if (goalObj)
- {
- m_goalPosition = *goalObj->getPosition();
- Bool gotPhysics = obj->getPhysics()!=NULL && goalObj->getPhysics()!=NULL;
- Bool isMissile = obj->isKindOf(KINDOF_PROJECTILE);
- if (isMissile) {
- Real halfHeight = getMachineGoalObject()->getGeometryInfo().getMaxHeightAbovePosition()/2.0f;
- m_goalPosition.z += halfHeight;
- Real zDelta = m_goalPosition.z - obj->getPosition()->z;
- if (zDelta>0) {
- m_goalPosition.z += zDelta;
- }
- }
- //gotPhysics = false;
- if (gotPhysics && isMissile && !goalObj->isKindOf(KINDOF_IMMOBILE)) {
- Coord3D ourPos = *obj->getPosition();
- Coord3D delta;
- delta.x = m_goalPosition.x - ourPos.x;
- delta.y = m_goalPosition.y - ourPos.y;
- delta.z = m_goalPosition.z - ourPos.z;
- Real mySpeed = obj->getPhysics()->getVelocityMagnitude();
- Real goalSpeed = goalObj->getPhysics()->getVelocityMagnitude();
- if (mySpeed<5.0f) mySpeed = 5.0f; // avoid divide by 0.
- Real leadDistance = (0.5*delta.length()) * goalSpeed / mySpeed;
- Coord3D dir;
- goalObj->getUnitDirectionVector3D(dir);
- m_goalPosition.x += dir.x*leadDistance;
- m_goalPosition.y += dir.y*leadDistance;
- m_goalPosition.z += dir.z*leadDistance;
- }
- //DEBUG_LOG(("update goal pos to %f %f %f\n",m_goalPosition.x,m_goalPosition.y,m_goalPosition.z));
- } else {
- Bool isMissile = obj->isKindOf(KINDOF_PROJECTILE);
- if (isMissile) {
- // When missiles are moving uphill, they need to start up quickly to clear hills. jba.
- m_goalPosition = *getMachineGoalPosition();
- Real zDelta = m_goalPosition.z - obj->getPosition()->z;
- if (zDelta>0) {
- m_goalPosition.z += zDelta;
- }
- }
- }
- return AIInternalMoveToState::update();
- }
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //
- // note - has no crc/xfer as has no member vars. jba.
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIMoveOutOfTheWayState::onEnter()
- {
- setAdjustsDestination(true);
- Object *obj = getMachineOwner();
- AIUpdateInterface *ai = obj->getAI();
- if (ai->getPath()==NULL) {
- // Must have existing path.
- return STATE_FAILURE;
- }
- m_goalPosition = *ai->getPath()->getLastNode()->getPosition();
- return AIInternalMoveToState::onEnter();
- }
- //----------------------------------------------------------------------------------------------------------
- Bool AIMoveOutOfTheWayState::computePath()
- {
- Object *obj = getMachineOwner();
- AIUpdateInterface *ai = obj->getAI();
- m_waitingForPath = true;
- if (ai->isBlockedAndStuck()) {
- ai->setCanPathThroughUnits(true);
- return true; // don't repath, just stop.
- }
- return true; // just use the existing path. See above.
- }
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIMoveOutOfTheWayState::update()
- {
- return AIInternalMoveToState::update();
- }
- //----------------------------------------------------------------------------------------------------------
- void AIMoveOutOfTheWayState::onExit( StateExitType status )
- {
- AIInternalMoveToState::onExit(status);
- AIUpdateInterface *ai = getMachineOwner()->getAI();
- if (ai) {
- ai->destroyPath();
- ai->setCanPathThroughUnits(false);
- ai->clearMoveOutOfWay();
- }
- }
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIMoveAndTightenState::crc( Xfer *xfer )
- {
- AIInternalMoveToState::crc(xfer);
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIMoveAndTightenState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
-
- // extend base class
- AIInternalMoveToState::xfer(xfer);
- xfer->xferInt(&m_okToRepathTimes);
- xfer->xferBool(&m_checkForPath);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIMoveAndTightenState::loadPostProcess( void )
- {
- AIInternalMoveToState::loadPostProcess();
- } // end loadPostProcess
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIMoveAndTightenState::onEnter()
- {
- setAdjustsDestination(false);
- Object *obj = getMachineOwner();
- AIUpdateInterface *ai = obj->getAI();
- m_okToRepathTimes = 1;
- m_checkForPath = true;
- TheAI->pathfinder()->removeGoal(obj);
- m_goalPosition = *getMachineGoalPosition();
- ai->requestApproachPath(&m_goalPosition);
- return AIInternalMoveToState::onEnter();
- }
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIMoveAndTightenState::update()
- {
- if (m_checkForPath) {
- Object *obj = getMachineOwner();
- AIUpdateInterface *ai = obj->getAI();
- Path *thePath = ai->getPath();
- if (thePath && !ai->isWaitingForPath()) {
- setAdjustsDestination(true);
- m_checkForPath = false;
- }
- }
- return AIInternalMoveToState::update();
- }
- //----------------------------------------------------------------------------------------------------------
- Bool AIMoveAndTightenState::computePath()
- {
- Object *obj = getMachineOwner();
- AIUpdateInterface *ai = obj->getAI();
- if (ai->isBlockedAndStuck()) {
- if (m_okToRepathTimes>0) {
- m_okToRepathTimes--;
- m_waitingForPath = true;
- ai->requestPath(&m_goalPosition, true);
- return true;
- }
- //DEBUG_LOG(("AIMoveAndTightenState::computePath - stuck, failing.\n"));
- return false; // don't repath for now. jba.
- }
- return true; // just use the existing path. See above.
- }
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIMoveAwayFromRepulsorsState::onEnter()
- {
- setAdjustsDestination(false);
- Object *obj = getMachineOwner();
- Object* enemy = TheAI->findClosestRepulsor(getMachineOwner(), obj->getVisionRange());
- AIUpdateInterface *ai = getMachineOwner()->getAI();
- if (!enemy || !ai) {
- return STATE_FAILURE;
- }
- ai->chooseLocomotorSet(LOCOMOTORSET_PANIC);
- if (obj)
- {
- obj->setModelConditionState(MODELCONDITION_PANICKING);
- }
- m_okToRepathTimes = 1;
- m_checkForPath = true;
- TheAI->pathfinder()->removeGoal(obj);
- ai->requestSafePath(enemy->getID());
- return AIInternalMoveToState::onEnter();
- }
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIMoveAwayFromRepulsorsState::update()
- {
- if (m_checkForPath) {
- Object *obj = getMachineOwner();
- AIUpdateInterface *ai = obj->getAI();
- Path *thePath = ai->getPath();
- if (thePath && !ai->isWaitingForPath()) {
- m_goalPosition = *thePath->getLastNode()->getPosition();
- setAdjustsDestination(false);
- m_checkForPath = false;
- }
- }
- return AIInternalMoveToState::update();
- }
- //----------------------------------------------------------------------------------------------------------
- Bool AIMoveAwayFromRepulsorsState::computePath()
- {
- if (m_okToRepathTimes>0) {
- m_okToRepathTimes--;
- return true;
- }
- return false; // don't recompute path, just stop moving.
- }
- //----------------------------------------------------------------------------------------------------------
- void AIMoveAwayFromRepulsorsState::onExit( StateExitType status )
- {
- AIInternalMoveToState::onExit( status );
- Object *obj = getMachineOwner();
- if (obj)
- {
- obj->clearModelConditionState(MODELCONDITION_PANICKING);
- }
- }
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- /**
- * Returns true if we can pursue the unit. Requires that :
- * 1. We are faster than the other unit.
- * 2. The other unit is moving.
- * 3. The other unit is moving away from us.
- */
- static Bool canPursue(Object *source, Weapon *weapon, Object *victim)
- {
- /* This state is only used if the target is moving away from us, and has physics. */
- if (!victim->getPhysics()) {
- return false;
- }
- AIUpdateInterface *ai = source->getAI();
- if (!ai) {
- return false;
- }
- // Have to have a turret to pursue.
- WhichTurretType tur = ai->getWhichTurretForCurWeapon();
- if (tur == TURRET_INVALID) {
- return false;
- }
- if (TheAI->getAiData()->m_aiCrushesInfantry) {
- if ( source->getControllingPlayer() &&
- (source->getControllingPlayer()->getPlayerType() == PLAYER_COMPUTER) &&
- source->canCrushOrSquish(victim) ) {
- return true; // Always pursue if we can squish.
- }
- }
- if (weapon->isTooClose(source, victim)) {
- return false; // Don't chase it if we are already too close.
- }
- Real ourMaxSpeed = source->getAI()->getCurLocomotorSpeed();
- Real victimSpeed = victim->getPhysics()->getForwardSpeed2D();
- if (victimSpeed >= ourMaxSpeed) {
- return false; // we can't catch them.
- }
- if (victimSpeed < ourMaxSpeed/10) {
- return false; // They aren't moving very fast, so don't chase.
- }
- Real dx = victim->getPosition()->x - source->getPosition()->x;
- Real dy = victim->getPosition()->y - source->getPosition()->y;
- Coord3D victimVector = *victim->getUnitDirectionVector2D();
- if (dx*victimVector.x + dy*victimVector.y < 0 ) {
- return false; // they are moving towards us.
- }
- return true;
- }
- //----------------------------------------------------------------------------------------------------------
- /**
- * Compute a valid spot to fire our weapon from.
- * Result in m_goalPosition.
- * Return false if can't find a good spot.
- */
- Bool AIAttackApproachTargetState::computePath()
- {
- Bool forceRepath = false;
- // if we're immobile we can't possibly approach the target
- if( getMachineOwner()->isMobile() == false )
- return false;
- //CRCDEBUG_LOG(("AIAttackApproachTargetState::computePath - begin for object %d\n", getMachineOwner()->getID()));
- AIUpdateInterface *ai = getMachineOwner()->getAI();
- if (ai->isBlockedAndStuck())
- {
- forceRepath = true;
- // Intense logging. jba
- //CRCDEBUG_LOG(("AIAttackApproachTargetState::computePath - stuck, recomputing for object %d\n", getMachineOwner()->getID()));
- }
- if (m_waitingForPath) return true;
- if (!forceRepath && ai->getPath()==NULL && !ai->isWaitingForPath())
- {
- forceRepath = true;
- }
- // force minimum time between recomputation
- /// @todo Unify recomputation conditions & account for obj ID so everyone doesnt compute on the same frame (MSB)
- if (!forceRepath && TheGameLogic->getFrame() - m_approachTimestamp < MIN_RECOMPUTE_TIME)
- {
- //CRCDEBUG_LOG(("AIAttackApproachTargetState::computePath - bailing because of min time for object %d\n", getMachineOwner()->getID()));
- return true;
- }
- m_approachTimestamp = TheGameLogic->getFrame();
- // if we have a goal object, move to it, otherwise move to goal position
- if (getMachineGoalObject())
- {
- Object* source = getMachineOwner();
- // if our victim's position hasn't changed, don't re-path
- if (!forceRepath && isSamePosition(source->getPosition(), &m_prevVictimPos, getMachineGoalObject()->getPosition() ))
- {
- CRCDEBUG_LOG(("AIAttackApproachTargetState::computePath - bailing because victim in same place for object %d\n", getMachineOwner()->getID()));
- return true;
- }
- Weapon* weapon = source->getCurrentWeapon();
- if (!weapon)
- {
- CRCDEBUG_LOG(("AIAttackApproachTargetState::computePath - bailing because of no weapon for object %d\n", getMachineOwner()->getID()));
- return false;
- }
- // remember where we think our victim is, so if it moves, we can re-path
- Object *victim = getMachineGoalObject();
- m_prevVictimPos = *victim->getPosition();
- if (canPursue(source, weapon, victim))
- {
- return false; // break out, and do the pursuit state.
- }
- setAdjustsDestination(true);
- if (weapon->isContactWeapon())
- {
- // Weapon is basically a contact weapon, so let the attacker pathfind into the target.
- ai->ignoreObstacle(victim);
- setAdjustsDestination(false); // We want to run into the target.
- ai->setPathExtraDistance(10*PATHFIND_CELL_SIZE_F); // We don't want it to slow down.
- }
- if (ai->isBlockedAndStuck())
- {
- m_waitingForPath = true;
- m_goalPosition = m_prevVictimPos;
- CRCDEBUG_LOG(("AIAttackApproachTargetState::computePath - requestPath() for object %d\n", getMachineOwner()->getID()));
- ai->requestPath(&m_goalPosition, getAdjustsDestination());
- }
- else
- {
- m_goalPosition = m_prevVictimPos;
- m_waitingForPath = true;
- Coord3D pos;
- victim->getGeometryInfo().getCenterPosition( *victim->getPosition(), pos );
-
- CRCDEBUG_LOG(("AIAttackApproachTargetState::computePath - requestAttackPath() for object %d\n", getMachineOwner()->getID()));
- ai->requestAttackPath(victim->getID(), &pos );
- m_stopIfInRange = false; // we have calculated a position to shoot from, so go there.
- }
- CRCDEBUG_LOG(("AIAttackApproachTargetState::computePath - bailing after repathing for object %d\n", getMachineOwner()->getID()));
- return true;
- }
- else
- {
- // goal position.
- setAdjustsDestination(true);
- m_stopIfInRange = false; // Attack position is used by missiles, and they hit the position.
- m_goalPosition = *getMachineGoalPosition();
- if (!forceRepath)
- {
- CRCDEBUG_LOG(("AIAttackApproachTargetState::computePath - bailing because we're aiming for a fixed position for object %d\n", getMachineOwner()->getID()));
- return true; // fixed positions don't move.
- }
- // must use computeAttackPath so that min ranges are considered.
- m_waitingForPath = true;
- ai->requestAttackPath(INVALID_ID, &m_goalPosition);
- CRCDEBUG_LOG(("AIAttackApproachTargetState::computePath - bailing after repathing at a fixed position for object %d\n", getMachineOwner()->getID()));
- return true;
- }
- CRCDEBUG_LOG(("AIAttackApproachTargetState::computePath - bailing at end of function for object %d\n", getMachineOwner()->getID()));
- return true;
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIAttackApproachTargetState::crc( Xfer *xfer )
- {
- AIInternalMoveToState::crc(xfer);
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIAttackApproachTargetState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- // extend base class
- AIInternalMoveToState::xfer( xfer );
-
- xfer->xferCoord3D(&m_prevVictimPos);
- xfer->xferUnsignedInt(&m_approachTimestamp);
- xfer->xferBool(&m_follow);
- xfer->xferBool(&m_isAttackingObject);
- xfer->xferBool(&m_stopIfInRange);
- xfer->xferBool(&m_isInitialApproach);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIAttackApproachTargetState::loadPostProcess( void )
- {
- // extend base class
- AIInternalMoveToState::loadPostProcess();
- } // end loadPostProcess
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIAttackApproachTargetState::onEnter()
- {
- // contained by AIAttackState, so no separate timer
- // urg. hacky. if we are a projectile, turn on precise z-pos.
- //CRCDEBUG_LOG(("AIAttackApproachTargetState::onEnter() - object %d\n", getMachineOwner()->getID()));
- Object* source = getMachineOwner();
- AIUpdateInterface* ai = source->getAI();
- if (source->isKindOf(KINDOF_PROJECTILE))
- {
- if (ai->getCurLocomotor())
- ai->getCurLocomotor()->setUsePreciseZPos(true);
- }
- if (getMachine()->isGoalObjectDestroyed())
- {
- return STATE_SUCCESS; // Already killed victim.
- }
- m_prevVictimPos.x = 0.0f;
- m_prevVictimPos.y = 0.0f;
- m_prevVictimPos.z = 0.0f;
- m_approachTimestamp = -MIN_RECOMPUTE_TIME;
- // See if we're close enough.
- Object *victim = getMachineGoalObject();
- if (victim)
- {
- Weapon* weapon = source->getCurrentWeapon();
- if (!weapon)
- {
- return STATE_FAILURE;
- }
- if (weapon->isWithinAttackRange(source, victim))
- {
- Bool viewBlocked = false;
- if (source && victim && ai->isDoingGroundMovement() && !victim->isSignificantlyAboveTerrain())
- {
- viewBlocked = TheAI->pathfinder()->isAttackViewBlockedByObstacle(source, *source->getPosition(), victim, *victim->getPosition());
- }
- if (!viewBlocked)
- {
- return STATE_SUCCESS;
- }
- }
- // Check here: If we are a player, and we got to this state via an ai command (ie we auto-acquired),
- // we don't want to chase the unit. isAllowedToChase is set when we are in a deploy and attack state (troop crawler).
- if (source->getControllingPlayer()->getPlayerType() == PLAYER_HUMAN) {
- if (ai->getLastCommandSource() == CMD_FROM_AI && !ai->isAllowedToChase() ) {
- if (!weapon->isContactWeapon()) {
- return STATE_FAILURE;
- }
- }
- }
- if (canPursue(source, weapon, victim))
- {
- return STATE_SUCCESS; // break out, and do the pursuit state.
- }
- }
- // If we have a turret, start aiming.
- WhichTurretType tur = ai->getWhichTurretForCurWeapon();
- if (tur != TURRET_INVALID)
- {
- if (m_isAttackingObject)
- {
- ai->setTurretTargetObject(tur, victim, m_isForceAttacking);
- }
- else
- {
- ai->setTurretTargetPosition(tur, getMachineGoalPosition());
- }
- }
- // find a good spot to shoot from
- //CRCDEBUG_LOG(("AIAttackApproachTargetState::onEnter() - calling computePath() for object %d\n", getMachineOwner()->getID()));
- if (computePath() == false)
- return STATE_FAILURE;
- return AIInternalMoveToState::onEnter();
- }
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIAttackApproachTargetState::updateInternal()
- {
- AIUpdateInterface* ai = getMachineOwner()->getAI();
- //CRCDEBUG_LOG(("AIAttackApproachTargetState::updateInternal() - object %d\n", getMachineOwner()->getID()));
- if (getMachine()->isGoalObjectDestroyed())
- {
- ai->notifyVictimIsDead();
- ai->setCurrentVictim(NULL);
- return STATE_FAILURE;
- }
- m_stopIfInRange = !ai->isAttackPath();
- StateReturnType code = STATE_FAILURE;
- Object* source = getMachineOwner();
- Weapon* weapon = source->getCurrentWeapon();
- Object *victim = getMachineGoalObject();
- if (victim)
- {
- if( victim->testStatus( OBJECT_STATUS_STEALTHED ) && !victim->testStatus( OBJECT_STATUS_DETECTED ) ) {
- return STATE_FAILURE; // If obj is stealthed, can no longer approach.
- }
- ai->setCurrentVictim(victim);
- // Attacking an object.
- if (weapon && weapon->isContactWeapon() && weapon->isWithinAttackRange(source, victim))
- {
- return STATE_SUCCESS;
- }
- if (m_stopIfInRange && weapon && weapon->isWithinAttackRange(source, victim))
- {
- Bool viewBlocked = false;
- if (victim && ai->isDoingGroundMovement() && !victim->isSignificantlyAboveTerrain())
- {
- viewBlocked = TheAI->pathfinder()->isAttackViewBlockedByObstacle(source, *source->getPosition(), victim, *victim->getPosition());
- }
- if (!viewBlocked)
- {
- return STATE_SUCCESS;
- }
- }
- // find a good spot to shoot from
- //CRCDEBUG_LOG(("AIAttackApproachTargetState::updateInternal() - calling computePath() to victim for object %d\n", getMachineOwner()->getID()));
- if (computePath() == false)
- return STATE_SUCCESS;
- code = AIInternalMoveToState::update();
- if (code != STATE_CONTINUE)
- {
- return STATE_SUCCESS; // Always return state success, as state failure exits the attack.
- // we may need to aim & do another approach if the target moved. jba.
- }
- }
- else
- {
- // Attacking a position.
- // find a good spot to shoot from
- //CRCDEBUG_LOG(("AIAttackApproachTargetState::updateInternal() - calling computePath() to position for object %d\n", getMachineOwner()->getID()));
- if (m_stopIfInRange && weapon && weapon->isWithinAttackRange(source, &m_goalPosition))
- {
- Bool viewBlocked = false;
- if ( ai->isDoingGroundMovement() )
- {
- viewBlocked = TheAI->pathfinder()->isAttackViewBlockedByObstacle(source, *source->getPosition(), NULL, m_goalPosition);
- }
- if (!viewBlocked)
- {
- return STATE_SUCCESS;
- }
- }
- if (computePath() == false)
- return STATE_FAILURE;
- code = AIInternalMoveToState::update();
- }
- return code;
- }
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIAttackApproachTargetState::update()
- {
- // contained by AIAttackState, so no separate timer
- StateReturnType code = updateInternal();
- Object* source = getMachineOwner();
- AIUpdateInterface *ai = source->getAI();
-
- if (m_follow && m_isAttackingObject)
- {
- // Basically, if the object is alive, we continue, in case the target moves.
- Object* victim = getMachineGoalObject();
- if (source && victim && source->isMobile() && !victim->getTemplate()->isKindOf(KINDOF_IMMOBILE))
- {
- if (code != STATE_CONTINUE)
- {
- m_isInitialApproach = false;
- }
- // Object is still alive (and so are we)
- // It could move (and so can we), so just continue & keep checking.
- code = STATE_CONTINUE;
- }
- }
- if (m_isInitialApproach)
- {
- WhichTurretType tur = ai->getWhichTurretForCurWeapon();
- if (tur != TURRET_INVALID)
- {
- Object *temporaryTarget = ai->getNextMoodTarget( true, false );
- if (temporaryTarget)
- {
- ai->setTurretTargetObject(tur, temporaryTarget, m_isForceAttacking);
- }
- }
- }
- return code;
- }
- //----------------------------------------------------------------------------------------------------------
- void AIAttackApproachTargetState::onExit( StateExitType status )
- {
- // contained by AIAttackState, so no separate timer
- AIInternalMoveToState::onExit( status );
- AIUpdateInterface *ai = getMachineOwner()->getAI();
- Object *obj = getMachineOwner();
- if (ai) {
- ai->ignoreObstacle(NULL);
-
- // Per JohnA, this state should not be calling ai->destroyPath, because we can have spastic users
- // that click the target repeadedly. This will prevent the unit from stuttering for said spastic
- // users.
- // ai->destroyPath();
- // urg. hacky. if we are a projectile, reset precise z-pos.
- if (getMachineOwner()->isKindOf(KINDOF_PROJECTILE))
- {
- if (ai && ai->getCurLocomotor())
- ai->getCurLocomotor()->setUsePreciseZPos(false);
- }
- if (ai->isDoingGroundMovement()) {
- Real dx = m_goalPosition.x-obj->getPosition()->x;
- Real dy = m_goalPosition.y-obj->getPosition()->y;
- if (dx*dx+dy*dy<PATHFIND_CELL_SIZE_F*PATHFIND_CELL_SIZE_F*0.125)
- {
- // We are doing accurate ground movement, so make sure we end exactly at the goal.
- obj->setPosition(&m_goalPosition);
- }
- }
- }
- m_isInitialApproach = false; // We only want to allow turreted things to fire at enemies during their
- // first approach
- }
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- /**
- * Compute a valid spot to fire our weapon from.
- * Result in m_goalPosition.
- * Return false if can't find a good spot.
- */
- Bool AIAttackPursueTargetState::computePath()
- {
- Bool forceRepath = false;
- // if we're immobile we can't possibly approach the target
- if( getMachineOwner()->isMobile() == false )
- return false;
- AIUpdateInterface *ai = getMachineOwner()->getAI();
- if (ai->isBlockedAndStuck())
- {
- return false;
- }
- if (m_waitingForPath) return true;
- if (!forceRepath && ai->getPath()==NULL && !ai->isWaitingForPath())
- {
- forceRepath = true;
- }
- // force minimum time between recomputation
- /// @todo Unify recomputation conditions & account for obj ID so everyone doesnt compute on the same frame (MSB)
- if (!forceRepath && TheGameLogic->getFrame() - m_approachTimestamp < MIN_RECOMPUTE_TIME)
- {
- return true;
- }
- m_approachTimestamp = TheGameLogic->getFrame();
- DEBUG_ASSERTLOG(getMachineGoalObject(), ("***************************Should only be pursuing objects. jba"));
- // if we have a goal object, move to it, otherwise fail & continue to AIAttackApproachTargetState
- if (getMachineGoalObject())
- {
- Object* source = getMachineOwner();
- // if our victim's position hasn't changed, don't re-path
- if (!forceRepath && isSamePosition(source->getPosition(), &m_prevVictimPos, getMachineGoalObject()->getPosition() ))
- return true;
- Weapon* weapon = source->getCurrentWeapon();
- if (!weapon)
- {
- return false;
- }
- if (!canPursue(source, weapon, getMachineGoalObject())) {
- return false;
- }
- // remember where we think our victim is, so if it moves, we can re-path
- Object *victim = getMachineGoalObject();
- m_prevVictimPos = *victim->getPosition();
- setAdjustsDestination(true);
- m_goalPosition = m_prevVictimPos;
- m_waitingForPath = true;
- ai->requestPath(&m_goalPosition, false);
- m_stopIfInRange = false; // we have calculated a position to shoot from, so go there.
- return true;
- }
- return false;
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIAttackPursueTargetState::crc( Xfer *xfer )
- {
- AIInternalMoveToState::crc(xfer);
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIAttackPursueTargetState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- // extend base class
- AIInternalMoveToState::xfer( xfer );
-
- xfer->xferCoord3D(&m_prevVictimPos);
- xfer->xferUnsignedInt(&m_approachTimestamp);
- xfer->xferBool(&m_follow);
- xfer->xferBool(&m_isAttackingObject);
- xfer->xferBool(&m_stopIfInRange);
- xfer->xferBool(&m_isInitialApproach);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIAttackPursueTargetState::loadPostProcess( void )
- {
- // extend base class
- AIInternalMoveToState::loadPostProcess();
- } // end loadPostProcess
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIAttackPursueTargetState::onEnter()
- {
- // contained by AIAttackState, so no separate timer
- // If we return STATE_SUCCESS or STATE_FAILURE, we proceed to AIAttackApproachTargetState.
- Object* source = getMachineOwner();
- AIUpdateInterface* ai = source->getAI();
- if (source->isKindOf(KINDOF_PROJECTILE))
- {
- //CRCDEBUG_LOG(("AIAttackPursueTargetState::onEnter() - is a projectile for object %d (%s)\n", getMachineOwner()->getID(), getMachineOwner()->getTemplate()->getName().str()));
- return STATE_SUCCESS; // Projectiles go directly to AIAttackApproachTargetState.
- }
- if (getMachine()->isGoalObjectDestroyed())
- {
- //CRCDEBUG_LOG(("AIAttackPursueTargetState::onEnter() - goal object is destroyed for object %d (%s)\n", getMachineOwner()->getID(), getMachineOwner()->getTemplate()->getName().str()));
- return STATE_SUCCESS; // Already killed victim.
- }
- if (!m_isAttackingObject) {
- //CRCDEBUG_LOG(("AIAttackPursueTargetState::onEnter() - not attacking for object %d (%s)\n", getMachineOwner()->getID(), getMachineOwner()->getTemplate()->getName().str()));
- return STATE_SUCCESS; // only pursue objects - positions don't move.
- }
- setAdjustsDestination(false);
- // Check here: If we are a player, and we got to this state via an ai command (ie we auto-acquired),
- // we don't want to chase the unit.
- if (source->getControllingPlayer()->getPlayerType() == PLAYER_HUMAN) {
- if (ai->getLastCommandSource() == CMD_FROM_AI) {
- return STATE_SUCCESS;
- }
- }
- m_prevVictimPos.x = 0.0f;
- m_prevVictimPos.y = 0.0f;
- m_prevVictimPos.z = 0.0f;
- m_approachTimestamp = -MIN_RECOMPUTE_TIME;
- // See if we're close enough.
- Object *victim = getMachineGoalObject();
- if (victim) {
- Weapon* weapon = source->getCurrentWeapon();
- if (!weapon)
- {
- return STATE_FAILURE;
- }
- if (!canPursue(source, weapon, victim) )
- {
- //CRCDEBUG_LOG(("AIAttackPursueTargetState::onEnter() - can't pursue for object %d (%s)\n", getMachineOwner()->getID(), getMachineOwner()->getTemplate()->getName().str()));
- return STATE_SUCCESS;
- }
- } else {
- //CRCDEBUG_LOG(("AIAttackPursueTargetState::onEnter() - no victim for object %d (%s)\n", getMachineOwner()->getID(), getMachineOwner()->getTemplate()->getName().str()));
- return STATE_SUCCESS; // gotta have a victim.
- }
- // If we have a turret, start aiming.
- WhichTurretType tur = ai->getWhichTurretForCurWeapon();
- if (tur != TURRET_INVALID)
- {
- ai->setTurretTargetObject(tur, victim, m_isForceAttacking);
- } else {
- //CRCDEBUG_LOG(("AIAttackPursueTargetState::onEnter() - no turret for object %d (%s)\n", getMachineOwner()->getID(), getMachineOwner()->getTemplate()->getName().str()));
- return STATE_SUCCESS; // we only pursue with turrets, as non-turreted weapons can't fire on the run.
- }
- // find a good spot to shoot from
- if (computePath() == false)
- return STATE_SUCCESS;
- return AIInternalMoveToState::onEnter();
- }
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIAttackPursueTargetState::updateInternal()
- {
- AIUpdateInterface* ai = getMachineOwner()->getAI();
- if (getMachine()->isGoalObjectDestroyed())
- {
- ai->notifyVictimIsDead();
- ai->setCurrentVictim(NULL);
- return STATE_FAILURE;
- }
- m_stopIfInRange = false;
- Object* source = getMachineOwner();
- StateReturnType code = STATE_FAILURE;
- Object *victim = getMachineGoalObject();
- if (victim)
- {
- if( victim->testStatus( OBJECT_STATUS_STEALTHED ) && !victim->testStatus( OBJECT_STATUS_DETECTED ) ){
- return STATE_FAILURE; // If obj is stealthed, can no longer pursue.
- }
- ai->setCurrentVictim(victim);
- // Attacking an object.
- // find a good spot to shoot from
- if (computePath() == false)
- return STATE_FAILURE;
- code = AIInternalMoveToState::update();
- if (code != STATE_CONTINUE)
- {
- //CRCDEBUG_LOG(("AIAttackPursueTargetState::updateInternal() - failed internal update() for object %d (%s)\n", getMachineOwner()->getID(), getMachineOwner()->getTemplate()->getName().str()));
- return STATE_SUCCESS; // Always return state success, as state failure exits the attack.
- // we may need to aim & do another approach if the target moved. jba.
- }
- Weapon* weapon = source->getCurrentWeapon();
- if (!weapon)
- return STATE_FAILURE;
- // If we have a turret, start aiming.
- WhichTurretType tur = ai->getWhichTurretForCurWeapon();
- if (tur == TURRET_INVALID)
- {
- //CRCDEBUG_LOG(("AIAttackPursueTargetState::updateInternal() - no turret for object %d (%s)\n", getMachineOwner()->getID(), getMachineOwner()->getTemplate()->getName().str()));
- return STATE_SUCCESS; // We currently only pursue with a turret weapon.
- }
- Bool viewBlocked = false;
- if (ai->isDoingGroundMovement() && !victim->isSignificantlyAboveTerrain())
- {
- viewBlocked = TheAI->pathfinder()->isAttackViewBlockedByObstacle(source, *source->getPosition(), victim, *victim->getPosition());
- }
- if (!viewBlocked && victim->getPhysics() && weapon->isWithinAttackRange(source, victim)) {
- // If we have a turret, start aiming.
- ai->setTurretTargetObject(tur, victim, m_isForceAttacking);
- // match speeds;
- m_isInitialApproach = false;
- Real victimSpeed = victim->getPhysics()->getForwardSpeed2D();
- if (weapon->isGoalPosWithinAttackRange(source, source->getPosition(), victim, victim->getPosition())){
- victimSpeed *= 0.95f;
- }
- if (source->canCrushOrSquish(victim)) {
- victimSpeed = FAST_AS_POSSIBLE;
- }
- ai->setDesiredSpeed(victimSpeed);
- // Really intense debug info. jba.
- // DEBUG_LOG(("VS %f, OS %f, goal %f\n", victim->getPhysics()->getForwardSpeed2D(), source->getPhysics()->getForwardSpeed2D(), victimSpeed));
- } else {
- ai->setDesiredSpeed(FAST_AS_POSSIBLE);
- }
- }
- return code;
- }
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIAttackPursueTargetState::update()
- {
- // contained by AIAttackState, so no separate timer
- StateReturnType code = updateInternal();
- Object* source = getMachineOwner();
- AIUpdateInterface *ai = source->getAI();
- if (m_isInitialApproach)
- {
- WhichTurretType tur = ai->getWhichTurretForCurWeapon();
- if (tur != TURRET_INVALID)
- {
- Object *temporaryTarget = ai->getNextMoodTarget( true, false );
- if (temporaryTarget)
- {
- ai->setTurretTargetObject(tur, temporaryTarget, m_isForceAttacking);
- }
- }
- }
- return code;
- }
- //----------------------------------------------------------------------------------------------------------
- void AIAttackPursueTargetState::onExit( StateExitType status )
- {
- // contained by AIAttackState, so no separate timer
- //CRCDEBUG_LOG(("AIAttackPursueTargetState::onExit() for object %d (%s)\n", getMachineOwner()->getID(), getMachineOwner()->getTemplate()->getName().str()));
- AIInternalMoveToState::onExit( status );
- m_isInitialApproach = false; // We only want to allow turreted things to fire at enemies during their
- // first approach
- }
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- Bool AIPickUpCrateState::computePath()
- {
- return AIInternalMoveToState::computePath();
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIPickUpCrateState::crc( Xfer *xfer )
- {
- AIInternalMoveToState::crc(xfer);
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIPickUpCrateState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- // extend base class
- AIInternalMoveToState::xfer( xfer );
-
- xfer->xferInt(&m_delayCounter);
- xfer->xferCoord3D(&m_goalPosition);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIPickUpCrateState::loadPostProcess( void )
- {
- // extend base class
- AIInternalMoveToState::loadPostProcess();
- } // end loadPostProcess
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIPickUpCrateState::onEnter()
- {
- Object* goalObj = getMachineGoalObject();
- if (!goalObj) {
- return STATE_FAILURE;
- }
- setAdjustsDestination(true);
- m_goalPosition = *goalObj->getPosition();
- m_delayCounter = 3;
- return STATE_CONTINUE;
- }
- //----------------------------------------------------------------------------------------------------------
- void AIPickUpCrateState::onExit( StateExitType status )
- {
- AIInternalMoveToState::onExit( status );
- }
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIPickUpCrateState::update()
- {
- /// @todo srj -- find a way to sleep for a number of frames here, if possible
- if (m_delayCounter) {
- m_delayCounter--;
- if (m_delayCounter == 0) {
- return AIInternalMoveToState::onEnter();
- }
- return STATE_CONTINUE;
- }
- // do movement
- StateReturnType status = AIInternalMoveToState::update();
- return status;
- }
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIFollowPathState::crc( Xfer *xfer )
- {
- AIInternalMoveToState::crc(xfer);
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIFollowPathState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- AIInternalMoveToState::xfer(xfer);
- xfer->xferInt(&m_index);
- xfer->xferBool(&m_adjustFinal);
- xfer->xferBool(&m_adjustFinalOverride);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIFollowPathState::loadPostProcess( void )
- {
- AIInternalMoveToState::loadPostProcess();
- } // end loadPostProcess
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIFollowPathState::onEnter()
- {
- Object *obj = getMachineOwner();
- AIUpdateInterface *ai = obj->getAI();
- m_index = 0;
- const Coord3D *pos = ai->friend_getGoalPathPosition( m_index );
- if (pos == NULL)
- return STATE_FAILURE;
- // set initial movement goal
- m_goalPosition = *pos;
- const Coord3D *nextPos = ai->friend_getGoalPathPosition( m_index+1 );
- m_adjustFinal = true;
- //Assign this value to the AIUpdateInterface so object's can access this value while
- //determine which waypoints to plot in the waypoint renderer.
- ai->friend_setCurrentGoalPathIndex( m_index );
- if (getID() == AI_FOLLOW_EXITPRODUCTION_PATH) {
- ai->setCanPathThroughUnits(true);
- setAdjustsDestination(false);
- m_adjustFinal = true;
- }
- StateReturnType ret = AIInternalMoveToState::onEnter();
- if (obj->getFormationID() != NO_FORMATION_ID) {
- AIGroup *group = ai->getGroup();
- if (group) {
- Real speed = group->getSpeed();
- ai->setDesiredSpeed(speed);
- }
- }
- if (nextPos)
- {
- Coord2D delta;
- delta.x = nextPos->x - pos->x;
- delta.y = nextPos->y - pos->y;
- Real offset = delta.length();
- const Coord3D *followingPos = ai->friend_getGoalPathPosition( m_index+2 );
- if (followingPos) offset += 4*PATHFIND_CELL_SIZE_F;
- ai->setPathExtraDistance(offset);
- // We are in the middle of a path, so don't set the final goal location yet.
- setAdjustsDestination(false);
- }
- else
- {
- setAdjustsDestination(m_adjustFinal);
- ai->setPathExtraDistance(0);
- // urg. hacky. if we are a projectile on the last segment, turn on precise z-pos.
- if (obj->isKindOf(KINDOF_PROJECTILE))
- {
- if (ai && ai->getCurLocomotor())
- ai->getCurLocomotor()->setUsePreciseZPos(true);
- }
- }
- return ret;
- }
- //----------------------------------------------------------------------------------------------------------
- void AIFollowPathState::onExit( StateExitType status )
- {
- AIInternalMoveToState::onExit( status );
- // turn off precision-z-pos when we exit, just in case.
- AIUpdateInterface *ai = getMachineOwner()->getAI();
- if (!ai) return;
- ai->setCanPathThroughUnits(false);
- if (ai->getCurLocomotor())
- ai->getCurLocomotor()->setUsePreciseZPos(false);
- //Assign this value to the AIUpdateInterface so object's can access this value while
- //determine which waypoints to plot in the waypoint renderer.
- ai->friend_setCurrentGoalPathIndex( -1 );
- }
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIFollowPathState::update()
- {
- getMachine()->setGoalPosition(&m_goalPosition);
- // do movement
- StateReturnType status = AIInternalMoveToState::update();
- // if move to has finished, move to next point on path
- if (status == STATE_SUCCESS || status == STATE_FAILURE)
- {
- Object *obj = getMachineOwner();
- AIUpdateInterface *ai = obj->getAI();
- if (status == STATE_FAILURE && m_retryCount>0) {
- // If we failed, & haven't reached retry limit, try again. jba.
- m_retryCount--;
- } else {
- ++m_index;
- }
- const Coord3D *pos = ai->friend_getGoalPathPosition( m_index );
- Bool tooClose=true;
- while (pos && tooClose) {
- Real dx = pos->x - obj->getPosition()->x;
- Real dy = pos->y - obj->getPosition()->y;
- tooClose = false;
- if (sqr(dx) + sqr(dy) < sqr(PATHFIND_CELL_SIZE_F)) {
- tooClose = true;
- }
- if (tooClose) {
- m_index++;
- pos = ai->friend_getGoalPathPosition(m_index);
- }
- }
-
- //Assign this value to the AIUpdateInterface so object's can access this value while
- //determine which waypoints to plot in the waypoint renderer.
- ai->friend_setCurrentGoalPathIndex( m_index );
- ai->ignoreObstacleID(INVALID_ID); // we have exited whatever object we are leaving, if any. jba.
- if (pos == NULL)
- {
- // reached the end of the path
- return STATE_SUCCESS;
- }
- ai->friend_startingMove();
- // set next movement goal
- m_goalPosition = *pos;
- const Coord3D *nextPos = ai->friend_getGoalPathPosition( m_index+1 );
- if (nextPos)
- {
- Coord2D delta;
- delta.x = nextPos->x - pos->x;
- delta.y = nextPos->y - pos->y;
- Real offset = delta.length();
- const Coord3D *followingPos = ai->friend_getGoalPathPosition( m_index+2 );
- if (followingPos) offset += 4*PATHFIND_CELL_SIZE_F;
- ai->setPathExtraDistance(offset);
- // We are in the middle of a path, so don't set the final goal location yet.
- setAdjustsDestination(false);
- }
- else
- {
- setAdjustsDestination(m_adjustFinal && (m_adjustFinalOverride || ai->isDoingGroundMovement()));
- if (getAdjustsDestination())
- {
- if (!TheAI->pathfinder()->adjustDestination(getMachineOwner(), ai->getLocomotorSet(), &m_goalPosition)) {
- return STATE_FAILURE;
- }
- TheAI->pathfinder()->updateGoal(getMachineOwner(), &m_goalPosition, TheTerrainLogic->getLayerForDestination(&m_goalPosition));
- }
- // urg. hacky. if we are a projectile on the last segment, turn on precise z-pos.
- if (obj->isKindOf(KINDOF_PROJECTILE))
- {
- if (ai && ai->getCurLocomotor())
- ai->getCurLocomotor()->setUsePreciseZPos(true);
- }
- }
- computePath();
- return STATE_CONTINUE;
- }
- return status;
- }
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIMoveAndEvacuateState::crc( Xfer *xfer )
- {
- AIInternalMoveToState::crc(xfer);
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIMoveAndEvacuateState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- AIInternalMoveToState::xfer(xfer);
- xfer->xferCoord3D(&m_origin);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIMoveAndEvacuateState::loadPostProcess( void )
- {
- AIInternalMoveToState::loadPostProcess();
- } // end loadPostProcess
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIMoveAndEvacuateState::onEnter()
- {
- Object *obj = getMachineOwner();
- getMachine()->lock("AIMoveAndEvacuateState::onEnter"); // This state is not user interruptable.
- m_origin = *obj->getPosition();
- setAdjustsDestination(true);
- // if we have a goal object, move to it, otherwise move to goal position
- if (getMachine()->getGoalObject())
- m_goalPosition = *getMachine()->getGoalObject()->getPosition();
- else
- m_goalPosition = *getMachine()->getGoalPosition();
- return AIInternalMoveToState::onEnter();
- }
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIMoveAndEvacuateState::update()
- {
- Object *obj = getMachine()->getOwner();
- if (obj->isEffectivelyDead())
- {
- return STATE_FAILURE;
- }
- // do movement
- StateReturnType status = AIInternalMoveToState::update();
- if (status != STATE_CONTINUE)
- {
- Object *obj = getMachineOwner();
- if (obj->isEffectivelyDead())
- {
- return STATE_FAILURE;
- }
- AIUpdateInterface *ai = obj->getAI();
- ai->aiEvacuate(FALSE, CMD_FROM_AI);
- obj->getTeam()->setActive();
- }
- return status;
- }
- //----------------------------------------------------------------------------------------------------------
- void AIMoveAndEvacuateState::onExit( StateExitType status )
- {
- getMachine()->unlock();
- getMachine()->setGoalPosition(&m_origin); // In case we follow with a AIMoveAndDeleteState.
- AIInternalMoveToState::onExit( status );
- }
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- AIAttackMoveToState::AIAttackMoveToState( StateMachine *machine ) : AIMoveToState(machine)
- {
- #ifdef STATE_MACHINE_DEBUG
- setName("AIAttackMoveToState");
- #endif // set up the state
- m_isMoveTo = false;
- m_frameToSleepUntil = 0;
- m_retryCount = ATTACK_RETRY_COUNT;
- m_attackMoveMachine = newInstance(AIAttackMoveStateMachine)(getMachineOwner(), "AIAttackMoveMachine");
- m_attackMoveMachine->initDefaultState();
- }
- //----------------------------------------------------------------------------------------------------------
- AIAttackMoveToState::~AIAttackMoveToState()
- {
- m_attackMoveMachine->deleteInstance();
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIAttackMoveToState::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIAttackMoveToState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 2;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- // extend base class
- AIMoveToState::xfer( xfer );
- if (version>=2) {
- xfer->xferUnsignedInt(&m_frameToSleepUntil);
- xfer->xferInt(&m_retryCount);
- }
- xfer->xferSnapshot(m_attackMoveMachine);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIAttackMoveToState::loadPostProcess( void )
- {
- } // end loadPostProcess
- #ifdef STATE_MACHINE_DEBUG
- //----------------------------------------------------------------------------------------------------------
- AsciiString AIAttackMoveToState::getName( ) const
- {
- AsciiString name = m_name;
- name.concat("/");
- if (m_attackMoveMachine) name.concat(m_attackMoveMachine->getCurrentStateName());
- else name.concat("*NULL m_deployMachine");
- return name;
- }
- #endif
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIAttackMoveToState::onEnter()
- {
- Object *owner = getMachineOwner();
- AIUpdateInterface *ai = owner->getAI();
- m_attackMoveMachine->clear();
- m_attackMoveMachine->setState( AI_IDLE );
- m_commandSrc = ai->getLastCommandSource();
- m_retryCount = ATTACK_RETRY_COUNT;
- m_frameToSleepUntil = 0;
- return AIMoveToState::onEnter();
- }
- //----------------------------------------------------------------------------------------------------------
- void AIAttackMoveToState::onExit( StateExitType status )
- {
- m_attackMoveMachine->setState(AI_IDLE);
- AIMoveToState::onExit(status);
- }
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIAttackMoveToState::update()
- {
- Object *owner = getMachineOwner();
- AIUpdateInterface *ai = owner->getAI();
- Bool forceRetargetThisFrame = false;
- Bool shouldRepathThisFrame = false;
- if (!m_attackMoveMachine->isInIdleState())
- {
- ai->setLocomotorGoalNone();
- owner->clearModelConditionState(MODELCONDITION_MOVING);
- m_attackMoveMachine->updateStateMachine();
-
- // if the machine is now idling, then we need to attempt to get a new target
- if (m_attackMoveMachine->isInIdleState()) {
- forceRetargetThisFrame = true;
- shouldRepathThisFrame = true;
- ai->friend_setLastCommandSource(m_commandSrc);
- } else {
- return STATE_CONTINUE;
- }
- }
- if (m_attackMoveMachine->isInIdleState())
- {
- // Check to see if we have created a crate we need to pick up.
- Object* crate = ai->checkForCrateToPickup();
- if (crate)
- {
- m_attackMoveMachine->setGoalObject(crate);
- m_attackMoveMachine->setState( AI_PICK_UP_CRATE );
- return STATE_CONTINUE;
- }
-
- Object* nextObjectToAttack;
- nextObjectToAttack = ai->getNextMoodTarget( !forceRetargetThisFrame, false );
- if (nextObjectToAttack != NULL)
- {
- ai->friend_endingMove();
- m_attackMoveMachine->setGoalObject(nextObjectToAttack);
- m_attackMoveMachine->setState( AI_ATTACK_OBJECT );
- shouldRepathThisFrame = false; // we're about to drop out of this function, but this is semantic emphasis.
- // Note that we picked up this command from the ai.
- ai->friend_setLastCommandSource(CMD_FROM_AI);
- // we don't want an update to take place 'till next frame.
- return STATE_CONTINUE;
- }
- }
- if (m_frameToSleepUntil>TheGameLogic->getFrame()) {
- return STATE_CONTINUE;
- } else if (m_frameToSleepUntil == TheGameLogic->getFrame()) {
- shouldRepathThisFrame = true;
- }
- if (shouldRepathThisFrame)
- {
- AIMoveToState::onEnter();
- forceRepath();
- }
- StateReturnType ret = AIMoveToState::update();
- if (ret != STATE_CONTINUE) {
- if (m_retryCount<1) return ret;
- /* check for close enough. */
- Real distSqr = sqr(owner->getPosition()->x - m_pathGoalPosition.x) + sqr(owner->getPosition()->y-m_pathGoalPosition.y);
- if (distSqr < sqr(ATTACK_CLOSE_ENOUGH_CELLS*PATHFIND_CELL_SIZE_F)) {
- return ret;
- }
- DEBUG_LOG(("AIAttackMoveToState::update Distance from goal %f, retrying.\n", sqrt(distSqr)));
- ret = STATE_CONTINUE;
- m_retryCount--;
- // Sleep 3 seconds. We can attack during these frames, just not move.
- m_frameToSleepUntil = TheGameLogic->getFrame() + 3*LOGICFRAMES_PER_SECOND;
- }
- return ret;
- }
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIMoveAndDeleteState::crc( Xfer *xfer )
- {
- AIInternalMoveToState::crc(xfer);
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIMoveAndDeleteState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- AIInternalMoveToState::xfer(xfer);
- xfer->xferBool(&m_appendGoalPosition);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIMoveAndDeleteState::loadPostProcess( void )
- {
- AIInternalMoveToState::loadPostProcess();
- } // end loadPostProcess
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIMoveAndDeleteState::onEnter()
- {
- setAdjustsDestination(false);
- getMachine()->lock("AIMoveAndDeleteState::onEnter");
- // if we have a goal object, move to it, otherwise move to goal position
- if (getMachine()->getGoalObject())
- m_goalPosition = *getMachine()->getGoalObject()->getPosition();
- else
- m_goalPosition = *getMachine()->getGoalPosition();
- m_appendGoalPosition = true; // We may be moving off the map.
- return AIInternalMoveToState::onEnter();
- }
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIMoveAndDeleteState::update()
- {
- Object *obj = getMachine()->getOwner();
- if (obj->isEffectivelyDead())
- {
- return STATE_FAILURE;
- }
- // do movement
- AIUpdateInterface *ai = obj->getAI();
- if (ai->getCurLocomotor())
- {
- ai->getCurLocomotor()->setAllowInvalidPosition(true);
- }
- if (m_appendGoalPosition)
- {
- Path *thePath = ai->getPath();
- if (!ai->isWaitingForPath() && ai->getPath())
- {
- m_goalPosition.z = TheTerrainLogic->getGroundHeight(m_goalPosition.x, m_goalPosition.y);
- thePath->appendNode( &m_goalPosition, LAYER_GROUND);
- m_appendGoalPosition = false; // just did it.
- }
- }
- StateReturnType status = AIInternalMoveToState::update();
- if (status != STATE_CONTINUE)
- {
- Object *obj = getMachineOwner();
- TheGameLogic->destroyObject(obj);
- }
- return status;
- }
- //----------------------------------------------------------------------------------------------------------
- void AIMoveAndDeleteState::onExit( StateExitType status )
- {
- getMachine()->unlock();
- AIInternalMoveToState::onExit( status );
- }
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- #define ALLOW_BACKTRACK
- //----------------------------------------------------------------------------------------------------------
- const Waypoint * AIFollowWaypointPathState::getNextWaypoint(void)
- {
- #ifdef ALLOW_BACKTRACK
- Int linkCount = m_currentWaypoint->getNumLinks();
- Int which = GameLogicRandomValue( 0, linkCount-1 );
- const Waypoint *nextWay = m_currentWaypoint->getLink( which );
- m_priorWaypoint = m_currentWaypoint;
- getMachine()->setGoalPosition(m_currentWaypoint->getLocation());// THANKS, JOHN
- return nextWay;
- #else
- if (!hasNextWaypoint()) {
- m_priorWaypoint = m_currentWaypoint;
- return NULL;
- }
- Int skip = -1;
- Int i;
- Int linkCount = m_currentWaypoint->getNumLinks();
- for (i=0; i<linkCount; i++) {
- if (m_priorWaypoint == m_currentWaypoint->getLink(i)) {
- skip = i;
- break;
- }
- }
- Int which = 0;
- if (skip >= 0) {
- which = GameLogicRandomValue( 0, linkCount-2 );
- if (which == skip) which = linkCount-1;
- } else {
- // pick a random link
- which = GameLogicRandomValue( 0, linkCount-1 );
- }
- const Waypoint *nextWay = m_currentWaypoint->getLink( which );
- m_priorWaypoint = m_currentWaypoint;
- return nextWay;
- #endif
- }
- //----------------------------------------------------------------------------------------------------------
- Bool AIFollowWaypointPathState::hasNextWaypoint(void)
- {
- #ifdef ALLOW_BACKTRACK
- return m_currentWaypoint->getNumLinks()>0;
- #else
- if (m_currentWaypoint->getNumLinks()==0) {
- return false; // no links, no next.
- }
- if (m_priorWaypoint==NULL) {
- return m_currentWaypoint->getNumLinks()>0;
- }
- if (m_currentWaypoint->getNumLinks()>1) {
- // Two links, always works.
- return true;
- }
- // We have a prior waypoint, and 1 link.
- if (m_priorWaypoint == m_currentWaypoint->getLink(0)) {
- return false; // don't go back to same waypoint.
- }
- return true;
- #endif
- }
- //----------------------------------------------------------------------------------------------------------
- Real AIFollowWaypointPathState::calcExtraPathDistance(void)
- {
- Real extra = PATHFIND_CELL_SIZE_F/10.0f;
- const Waypoint *curWay = m_currentWaypoint;
- Int limit = 5; // just look ahead 5, in case of circular paths. jba
- while (curWay && limit>0) {
- limit--;
- Int linkCount = curWay->getNumLinks();
- if (linkCount == 0) return extra;
- Int which = 0;
- const Waypoint *nextWay = curWay->getLink( which );
- Coord2D delta;
- delta.x = nextWay->getLocation()->x - curWay->getLocation()->x;
- delta.y = nextWay->getLocation()->y - curWay->getLocation()->y;
- extra += delta.length();
- curWay = nextWay;
- }
- return extra;
- }
- //----------------------------------------------------------------------------------------------------------
- void AIFollowWaypointPathState::computeGoal(Bool useGroupOffsets)
- {
- if (m_currentWaypoint == NULL)
- return;
- Object *obj = getMachineOwner();
- AIUpdateInterface *ai = obj->getAI();
- Coord3D dest = *m_currentWaypoint->getLocation();
- m_goalLayer = LAYER_GROUND; // waypoints are always on the ground.
- if (TheAI->pathfinder()->isPointOnWall(&dest)) {
- // except when they're on the wall. jba.
- dest.z = TheAI->pathfinder()->getWallHeight();
- m_goalLayer = LAYER_WALL;
- }
- ai->setPathExtraDistance(calcExtraPathDistance());
- if (hasNextWaypoint()) {
- // We are in the middle of a path, so don't set the final goal location yet.
- setAdjustsDestination(false);
- } else {
- setAdjustsDestination(true);
- // urg. hacky. if we are a projectile on the last segment, turn on precise z-pos.
- if (obj->isKindOf(KINDOF_PROJECTILE))
- {
- if (ai && ai->getCurLocomotor())
- ai->getCurLocomotor()->setUsePreciseZPos(true);
- }
- }
- #define NO_ROTATE_OFFSETS
- #ifdef ROTATE_OFFSETS
- Real dx = (dest.x) - (obj->getPosition()->x - m_groupOffset.x);
- Real dy = (dest.y) - (obj->getPosition()->y - m_groupOffset.y);
- Real angle;
- if (m_priorWaypoint) {
- dx = dest.x - m_priorWaypoint->getLocation()->x;
- dy = dest.y - m_priorWaypoint->getLocation()->y;
- angle = atan2(dy, dx);
- Real deltaAngle = angle - m_angle;
- Real s = sin(deltaAngle);
- Real c = cos(deltaAngle);
- Real x = m_groupOffset.x * c - m_groupOffset.y * s;
- Real y = m_groupOffset.y * c + m_groupOffset.x * s;
- m_groupOffset.x = x;
- m_groupOffset.y = y;
- } else {
- angle = atan2(dy, dx);
- }
- m_angle = angle;
- #endif
-
- m_goalPosition = dest;
- m_goalPosition.x += m_groupOffset.x;
- m_goalPosition.y += m_groupOffset.y;
- if (m_goalLayer == LAYER_WALL) {
- if (!TheAI->pathfinder()->isPointOnWall(&m_goalPosition)) {
- m_goalPosition = dest;
- }
- } else {
- m_goalPosition.z = TheTerrainLogic->getGroundHeight(m_goalPosition.x, m_goalPosition.y);
- }
- Region3D extent;
- TheTerrainLogic->getMaximumPathfindExtent(&extent);
- if (!extent.isInRegionNoZ(&m_goalPosition)) {
- setAdjustsDestination(false); // moving off the map.
- ai->getCurLocomotor()->setAllowInvalidPosition(true); // allow it to move off the map.
- m_appendGoalPosition = true; // Moving off the map.
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIFollowWaypointPathState::crc( Xfer *xfer )
- {
- AIInternalMoveToState::crc(xfer);
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIFollowWaypointPathState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- AIInternalMoveToState::xfer(xfer);
- xfer->xferCoord2D(&m_groupOffset);
- xfer->xferReal(&m_angle);
- xfer->xferInt(&m_framesSleeping);
- UnsignedInt id = INVALID_WAYPOINT_ID;
- if (m_currentWaypoint) {
- id = m_currentWaypoint->getID();
- }
- xfer->xferUnsignedInt(&id);
- if (xfer->getXferMode() == XFER_LOAD)
- {
- m_currentWaypoint = TheTerrainLogic->getWaypointByID(id);
- }
- id = INVALID_WAYPOINT_ID;
- if (m_priorWaypoint) {
- id = m_priorWaypoint->getID();
- }
- xfer->xferUnsignedInt(&id);
- if (xfer->getXferMode() == XFER_LOAD)
- {
- m_priorWaypoint = TheTerrainLogic->getWaypointByID(id);
- }
- xfer->xferBool(&m_appendGoalPosition);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIFollowWaypointPathState::loadPostProcess( void )
- {
- AIInternalMoveToState::loadPostProcess();
- } // end loadPostProcess
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIFollowWaypointPathState::onEnter()
- {
- m_appendGoalPosition = false; // not moving off the map at this point.
- m_priorWaypoint = NULL;
- m_currentWaypoint = ((AIStateMachine *)getMachine())->getGoalWaypoint();
- AIUpdateInterface *ai = getMachineOwner()->getAI();
- if (m_currentWaypoint == NULL && !m_moveAsGroup) return STATE_FAILURE;
- getMachine()->setGoalPosition(m_currentWaypoint->getLocation());
- m_framesSleeping = 0;
- m_groupOffset.x = m_groupOffset.y = 0;
- Object *obj = getMachineOwner();
- /* Interesting thought experiment. Didn't work well. jba
- Real distSqrLimit = 9*obj->getGeometryInfo().getMajorRadius()*obj->getGeometryInfo().getMajorRadius();
- const Waypoint *way = m_currentWaypoint;
- Bool doPrecise = false;
- while (way) {
- if (way->getNext()) {
- Real dx = way->getLocation()->x - way->getNext()->getLocation()->x;
- Real dy = way->getLocation()->y - way->getNext()->getLocation()->y;
- Real distSqr = dx*dx + dy*dy;
- if (distSqr < distSqrLimit) {
- doPrecise = true;
- }
- }
- way = way->getNext();
- }
- if (doPrecise && ai->getCurLocomotor()) {
- //ai->getCurLocomotor()->setUltraAccurate(true);
- }
- */
- Real speed = FAST_AS_POSSIBLE;
- if (m_moveAsGroup && m_currentWaypoint) {
- obj->getTeam()->setCurrentWaypoint(m_currentWaypoint);
- AIGroup *group = ai->getGroup();
- if (group) {
- speed = group->getSpeed();
- Coord3D center;
-
- group->getCenter( ¢er );
- m_groupOffset.x = obj->getPosition()->x - center.x;
- m_groupOffset.y = obj->getPosition()->y - center.y;
- }
- }
- if (m_currentWaypoint==NULL && m_moveAsGroup) {
- m_currentWaypoint = obj->getTeam()->getCurrentWaypoint();
- }
- // set initial movement goal
- computeGoal(m_moveAsGroup);
- StateReturnType ret = AIInternalMoveToState::onEnter();
- ai->setDesiredSpeed(speed);
- // Update the extra path distance. AIInternalMoveToState::onEnter resets it.
- ai->setPathExtraDistance(calcExtraPathDistance());
- if (hasNextWaypoint()) {
- // We are in the middle of a path, so don't set the final goal location yet.
- setAdjustsDestination(false);
- } else {
- setAdjustsDestination(ai->isDoingGroundMovement());
- if (getAdjustsDestination()) {
- if (!TheAI->pathfinder()->adjustDestination(getMachineOwner(), ai->getLocomotorSet(), &m_goalPosition)) {
- DEBUG_LOG(("Breaking out of follow waypoint path\n"));
- return STATE_FAILURE;
- }
- TheAI->pathfinder()->updateGoal(getMachineOwner(), &m_goalPosition, m_goalLayer);
- }
- // urg. hacky. if we are a projectile on the last segment, turn on precise z-pos.
- if (obj->isKindOf(KINDOF_PROJECTILE))
- {
- if (ai && ai->getCurLocomotor())
- ai->getCurLocomotor()->setUsePreciseZPos(true);
- }
- }
- if (ret != STATE_CONTINUE) {
- DEBUG_LOG(("Breaking out of follow waypoint path\n"));
- }
- return ret;
- }
- //----------------------------------------------------------------------------------------------------------
- void AIFollowWaypointPathState::onExit( StateExitType status )
- {
- AIInternalMoveToState::onExit( status );
- // turn off precision-z-pos when we exit, just in case.
- AIUpdateInterface *ai = getMachineOwner()->getAI();
- if (ai && ai->getCurLocomotor()) {
- ai->getCurLocomotor()->setUsePreciseZPos(false);
- ai->getCurLocomotor()->setUltraAccurate(false);
- }
- }
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIFollowWaypointPathState::update()
- {
- if (m_framesSleeping>0) {
- m_framesSleeping--;
- return STATE_CONTINUE;
- }
- Object *obj = getMachineOwner();
- AIUpdateInterface *ai = obj->getAI();
- getMachine()->setGoalPosition(m_currentWaypoint->getLocation());
-
- UnsignedInt adjustment = ai->getMoodMatrixActionAdjustment(MM_Action_Move);
- if (m_isFollowWaypointPathState && (adjustment & MAA_Action_To_AttackMove)) {
- if (m_moveAsGroup) {
- ai->aiAttackFollowWaypointPathAsTeam(m_currentWaypoint, NO_MAX_SHOTS_LIMIT, CMD_FROM_AI);
- } else {
- ai->aiAttackFollowWaypointPath(m_currentWaypoint, NO_MAX_SHOTS_LIMIT, CMD_FROM_AI);
- }
- }
- if (m_appendGoalPosition) {
- Path *thePath = ai->getPath();
- if (!ai->isWaitingForPath() && ai->getPath()) {
- //Coord3D pathEnd = *thePath->getLastNode()->getPosition();
- thePath->appendNode(&m_goalPosition, LAYER_GROUND); // waypoints are always on the ground.
- m_appendGoalPosition = false; // just did it.
- }
- }
- if (m_moveAsGroup && m_currentWaypoint != obj->getTeam()->getCurrentWaypoint()) {
- m_priorWaypoint = m_currentWaypoint;
- m_currentWaypoint = obj->getTeam()->getCurrentWaypoint();
- if (m_currentWaypoint == NULL) {
- return STATE_SUCCESS;
- }
- computeGoal(false);
- if (getAdjustsDestination() && ai->isDoingGroundMovement()) {
- if (!TheAI->pathfinder()->adjustDestination(obj, ai->getLocomotorSet(), &m_goalPosition)) {
- if (m_currentWaypoint) {
- DEBUG_LOG(("Breaking out of follow waypoint path %s of %s\n",
- m_currentWaypoint->getName().str(), m_currentWaypoint->getPathLabel1().str()));
- }
- return STATE_FAILURE;
- }
- }
- ai->friend_startingMove();
- computePath();
- if (getAdjustsDestination()) {
- TheAI->pathfinder()->updateGoal(obj, &m_goalPosition, m_goalLayer);
- }
- }
- // do movement
- StateReturnType status = AIInternalMoveToState::update();
-
- // We may want to allow ourselves to bail out of this one early. In order to do this, we check and
- // see if we're moving as a group, and then if our team is owned by an AI Skirmish player.
- // If it is, then we compute the group centroid, and see if it is within some distance of the
- if (m_moveAsGroup) {
- if (obj->getControllingPlayer()->isSkirmishAIPlayer()) {
- Team *team = obj->getTeam();
- AIGroup *group = TheAI->createGroup();
- team->getTeamAsAIGroup(group);
- Coord3D pos;
- group->getCenter(&pos);
- pos.x -= m_goalPosition.x;
- pos.y -= m_goalPosition.y;
- pos.z = 0;
- Int numInGroup = group->getCount();
- if (pos.length() <= (numInGroup * TheAI->getAiData()->m_skirmishGroupFudgeValue)) {
- // Consider ourselves close enough.
- status = STATE_SUCCESS;
- }
- }
- }
- // if move to has finished, move to next point on waypoint path
- if (status != STATE_CONTINUE)
- {
- m_currentWaypoint = getNextWaypoint();
- //LORENZEN ADDED LORENZEN ADDED LORENZEN ADDED
- Object *obj = getMachineOwner();
- AIUpdateInterface *ai = obj->getAI();
- if ( m_priorWaypoint )
- ai->setPriorWaypointID( m_priorWaypoint->getID() );
- if ( m_currentWaypoint )
- ai->setCurrentWaypointID( m_currentWaypoint->getID() );
- //LORENZEN ADDED LORENZEN ADDED LORENZEN ADDED
- // if there are no links from this waypoint, we're done
- if (m_currentWaypoint==NULL) {
- /// Trigger "end of waypoint path" scripts (jba)
- ai->setCompletedWaypoint(m_priorWaypoint);
- return STATE_SUCCESS;
- }
- if (m_moveAsGroup) {
- obj->getTeam()->setCurrentWaypoint(m_currentWaypoint);
- }
-
- computeGoal(false);
- if (getAdjustsDestination() && ai->isDoingGroundMovement()) {
- if (!TheAI->pathfinder()->adjustDestination(obj, ai->getLocomotorSet(), &m_goalPosition)) {
- if (m_currentWaypoint) {
- DEBUG_LOG(("Breaking out of follow waypoint path %s of %s\n",
- m_currentWaypoint->getName().str(), m_currentWaypoint->getPathLabel1().str()));
- }
- return STATE_FAILURE;
- }
- }
- ai->friend_startingMove();
- computePath();
- if (getAdjustsDestination()) {
- TheAI->pathfinder()->updateGoal(obj, &m_goalPosition, m_goalLayer);
- }
- return STATE_CONTINUE;
- }
- if (status != STATE_CONTINUE) {
- DEBUG_LOG(("Breaking out of follow waypoint path\n"));
- }
- return status;
- }
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIFollowWaypointPathExactState::crc( Xfer *xfer )
- {
- AIInternalMoveToState::crc(xfer);
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIFollowWaypointPathExactState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- AIInternalMoveToState::xfer(xfer);
- UnsignedInt id = INVALID_WAYPOINT_ID;
- if (m_lastWaypoint) {
- id = m_lastWaypoint->getID();
- }
- xfer->xferUnsignedInt(&id);
- if (xfer->getXferMode() == XFER_LOAD)
- {
- m_lastWaypoint = TheTerrainLogic->getWaypointByID(id);
- }
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIFollowWaypointPathExactState::loadPostProcess( void )
- {
- AIInternalMoveToState::loadPostProcess();
- } // end loadPostProcess
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIFollowWaypointPathExactState::onEnter()
- {
- const Waypoint *currentWaypoint = ((AIStateMachine *)getMachine())->getGoalWaypoint();
- AIUpdateInterface *ai = getMachineOwner()->getAI();
- if (currentWaypoint == NULL) return STATE_FAILURE;
- getMachine()->setGoalPosition(currentWaypoint->getLocation());
- Coord2D groupOffset;
- groupOffset.x = groupOffset.y = 0;
- Object *obj = getMachineOwner();
- Real speed = FAST_AS_POSSIBLE;
- if (m_moveAsGroup) {
- AIGroup *group = ai->getGroup();
- if (group) {
- speed = group->getSpeed();
- Coord3D center;
-
- group->getCenter( ¢er );
- groupOffset.x = obj->getPosition()->x - center.x;
- groupOffset.y = obj->getPosition()->y - center.y;
- }
- }
- ai->setCanPathThroughUnits(true);
- setAdjustsDestination(false);
- // set initial movement goal
- StateReturnType ret = AIInternalMoveToState::onEnter();
- ai->setPathFromWaypoint(currentWaypoint, &groupOffset);
- m_lastWaypoint = currentWaypoint;
- ai->getCurLocomotor()->setAllowInvalidPosition(true); // allow it to move off the map.
- //Kris: October 4, 2002 -- Commented out by guidance of John A.
- // Artist couldn't load his map, and turned out that it was because
- // there was a waypoint path that pointed to itself (2 point path).
- // John said that this code only needs "a" point, not the "last" point.
- //while (m_lastWaypoint && m_lastWaypoint->getLink(0)) {
- // m_lastWaypoint = m_lastWaypoint->getLink(0);
- //}
- ai->setDesiredSpeed(speed);
- return ret;
- }
- //----------------------------------------------------------------------------------------------------------
- void AIFollowWaypointPathExactState::onExit( StateExitType status )
- {
- AIInternalMoveToState::onExit( status );
- // turn off precision-z-pos when we exit, just in case.
- AIUpdateInterface *ai = getMachineOwner()->getAI();
- if (ai) {
- ai->setCompletedWaypoint(m_lastWaypoint);
- ai->setCanPathThroughUnits(false);
- ai->getCurLocomotor()->setAllowInvalidPosition(false); // turn off allow it to move off the map.
- }
- }
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIFollowWaypointPathExactState::update()
- {
- AIUpdateInterface *ai = getMachineOwner()->getAI();
- if (ai) ai->setCanPathThroughUnits(true);
- // do movement
- StateReturnType status = AIInternalMoveToState::update();
- return status;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- AIAttackFollowWaypointPathState::AIAttackFollowWaypointPathState ( StateMachine *machine, Bool asGroup ) :
- AIFollowWaypointPathState ( machine, asGroup, false )
- {
- #ifdef STATE_MACHINE_DEBUG
- setName("AIAttackFollowWaypointPathState");
- #endif
- m_attackFollowMachine = newInstance(AIAttackMoveStateMachine)(getMachineOwner(), "AIAttackFollowMachine");
- m_attackFollowMachine->initDefaultState();
- }
- //-------------------------------------------------------------------------------------------------
- AIAttackFollowWaypointPathState::~AIAttackFollowWaypointPathState()
- {
- m_attackFollowMachine->deleteInstance();
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIAttackFollowWaypointPathState::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIAttackFollowWaypointPathState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- // extend base class
- AIFollowWaypointPathState::xfer( xfer );
-
- xfer->xferSnapshot(m_attackFollowMachine);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIAttackFollowWaypointPathState::loadPostProcess( void )
- {
- } // end loadPostProcess
- #ifdef STATE_MACHINE_DEBUG
- //----------------------------------------------------------------------------------------------------------
- AsciiString AIAttackFollowWaypointPathState::getName( ) const
- {
- AsciiString name = m_name;
- name.concat("/");
- if (m_attackFollowMachine) name.concat(m_attackFollowMachine->getCurrentStateName());
- else name.concat("*NULL m_attackFollowMachine");
- return name;
- }
- #endif
- //-------------------------------------------------------------------------------------------------
- StateReturnType AIAttackFollowWaypointPathState ::onEnter()
- {
- m_attackFollowMachine->clear();
- m_attackFollowMachine->setState( AI_IDLE );
- return AIFollowWaypointPathState::onEnter();
- }
- //-------------------------------------------------------------------------------------------------
- StateReturnType AIAttackFollowWaypointPathState::update()
- {
- Object *owner = getMachineOwner();
- AIUpdateInterface *ai = owner->getAI();
- Bool forceRetargetThisFrame = false;
- Bool shouldRepathThisFrame = false;
- if (!m_attackFollowMachine->isInIdleState())
- {
- ai->setLocomotorGoalNone();
- owner->clearModelConditionState(MODELCONDITION_MOVING);
- m_attackFollowMachine->updateStateMachine();
-
- // if the machine is now idling, then we need to attempt to get a new target
- if (m_attackFollowMachine->isInIdleState())
- {
- forceRetargetThisFrame = true;
- shouldRepathThisFrame = true;
- }
- else
- {
- return STATE_CONTINUE;
- }
- }
- if (m_attackFollowMachine->isInIdleState())
- {
- // Check to see if we have created a crate we need to pick up.
- Object* crate = ai->checkForCrateToPickup();
- if (crate)
- {
- m_attackFollowMachine->setGoalObject(crate);
- m_attackFollowMachine->setState( AI_PICK_UP_CRATE );
- return STATE_CONTINUE;
- }
- Object* nextObjectToAttack;
- {
- nextObjectToAttack = ai->getNextMoodTarget( !forceRetargetThisFrame, false );
- }
- if (nextObjectToAttack != NULL)
- {
- m_attackFollowMachine->setGoalObject(nextObjectToAttack);
- m_attackFollowMachine->setState( AI_ATTACK_OBJECT );
- shouldRepathThisFrame = false; // we're about to drop out of this function, but this is semantic emphasis.
- // we don't want an update to take place 'till next frame.
- return STATE_CONTINUE;
- }
- }
- if (shouldRepathThisFrame)
- {
- // Update the goal waypoint if we've moved along the path.
- computeGoal(m_moveAsGroup);
- computePath();
- }
- return AIFollowWaypointPathState::update();
- }
- //-------------------------------------------------------------------------------------------------
- void AIAttackFollowWaypointPathState ::onExit( StateExitType status )
- {
- m_attackFollowMachine->setState(AI_IDLE);
- AIFollowWaypointPathState::onExit(status);
- }
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- /**
- * Wander along a waypoint path.
- */
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIWanderState::crc( Xfer *xfer )
- {
- AIFollowWaypointPathState::crc(xfer);
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIWanderState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- AIFollowWaypointPathState::xfer(xfer);
- xfer->xferInt(&m_waitFrames);
- xfer->xferInt(&m_timer);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIWanderState::loadPostProcess( void )
- {
- AIFollowWaypointPathState::loadPostProcess();
- } // end loadPostProcess
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- StateReturnType AIWanderState::onEnter()
- {
- m_currentWaypoint = ((AIStateMachine *)getMachine())->getGoalWaypoint();
- AIUpdateInterface *ai = getMachineOwner()->getAI();
- m_priorWaypoint = NULL;
- if (m_currentWaypoint == NULL || ai==NULL)
- return STATE_FAILURE;
- m_groupOffset.x = m_groupOffset.y = 0;
- Locomotor* curLoco = ai->getCurLocomotor();
- if (curLoco && curLoco->getWanderWidthFactor() > 0.0f) {
- Int delta = REAL_TO_INT_FLOOR(curLoco->getWanderWidthFactor()+0.5f);
- if (delta<1) delta = 1;
- m_groupOffset.x = GameLogicRandomValue(-delta, delta)*PATHFIND_CELL_SIZE_F;
- m_groupOffset.y = GameLogicRandomValue(-delta, delta)*PATHFIND_CELL_SIZE_F;
- }
- m_timer = 0;
- m_waitFrames = 10 + (getMachineOwner()->getID() & 0x7);
- // set initial movement goal
- computeGoal(false);
- StateReturnType ret = AIInternalMoveToState::onEnter();
- ai->setPathExtraDistance(calcExtraPathDistance());
- return ret;
- }
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIWanderState::update()
- {
- // do movement
- Object *obj = getMachineOwner();
- StateReturnType status = AIInternalMoveToState::update();
- if (obj->isKindOf(KINDOF_CAN_BE_REPULSED)) {
- m_timer--;
- if (m_timer<0) {
- m_timer = m_waitFrames;
- Object* enemy = TheAI->findClosestRepulsor(getMachineOwner(), obj->getVisionRange());
- if (enemy) {
- return STATE_FAILURE;
- }
- }
- }
- // if move to has finished, move to next point on waypoint path
- if (status != STATE_CONTINUE)
- {
- AIUpdateInterface *ai = obj->getAI();
- m_currentWaypoint = getNextWaypoint();
- // if there are no links from this waypoint, we're done
- if (m_currentWaypoint == NULL) {
- /// Trigger "end of waypoint path" scripts (jba)
- ai->setCompletedWaypoint(m_priorWaypoint);
-
- return STATE_SUCCESS;
- }
- Locomotor* curLoco = ai->getCurLocomotor();
- if (curLoco && curLoco->getWanderWidthFactor() > 0.0f) {
- Int delta = REAL_TO_INT_FLOOR(curLoco->getWanderWidthFactor()+0.5f);
- if (delta<1) delta = 1;
- m_groupOffset.x = GameLogicRandomValue(-delta, delta)*PATHFIND_CELL_SIZE_F;
- m_groupOffset.y = GameLogicRandomValue(-delta, delta)*PATHFIND_CELL_SIZE_F;
- }
- computeGoal(false);
- computePath();
- return STATE_CONTINUE;
- }
- // Never leave this state until told to.
- return STATE_CONTINUE;
- }
- //----------------------------------------------------------------------------------------------------------
- void AIWanderState::onExit( StateExitType status )
- {
- AIFollowWaypointPathState::onExit( status );
- }
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- /**
- * Wander around a point.
- */
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIWanderInPlaceState::crc( Xfer *xfer )
- {
- AIInternalMoveToState::crc(xfer);
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIWanderInPlaceState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- AIInternalMoveToState::xfer(xfer);
- xfer->xferCoord3D(&m_origin);
- xfer->xferInt(&m_waitFrames);
- xfer->xferInt(&m_timer);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** LoadPostProcess */
- // ------------------------------------------------------------------------------------------------
- void AIWanderInPlaceState::loadPostProcess( void )
- {
- AIInternalMoveToState::loadPostProcess();
- } // end loadPostProcess
- // ------------------------------------------------------------------------------------------------
- StateReturnType AIWanderInPlaceState::onEnter()
- {
- m_origin = *getMachineOwner()->getPosition();
- AIUpdateInterface *ai = getMachineOwner()->getAI();
- if (ai) {
- ai->chooseLocomotorSet(LOCOMOTORSET_WANDER);
- }
- Int delta = 3;
- if (ai->getCurLocomotor()) {
- delta = REAL_TO_INT_FLOOR( (ai->getCurLocomotor()->getWanderAboutPointRadius()/PATHFIND_CELL_SIZE_F) + 0.5f);
- }
- Coord3D offset;
- offset.x = GameLogicRandomValue(-delta, delta)*PATHFIND_CELL_SIZE_F;
- offset.y = GameLogicRandomValue(-delta, delta)*PATHFIND_CELL_SIZE_F;
- m_goalPosition = m_origin;
- m_goalPosition.x += offset.x;
- m_goalPosition.y += offset.y;
- m_timer = 0;
- m_waitFrames = 10 + (getMachineOwner()->getID() & 0x7);
- StateReturnType ret = AIInternalMoveToState::onEnter();
- return ret;
- }
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIWanderInPlaceState::update()
- {
- // do movement
- StateReturnType status = AIInternalMoveToState::update();
- Object *obj = getMachineOwner();
- AIUpdateInterface *ai = getMachineOwner()->getAI();
- if (!ai) return STATE_FAILURE;
- if (obj->isKindOf(KINDOF_CAN_BE_REPULSED)) {
- m_timer--;
- if (m_timer<0) {
- m_timer = m_waitFrames;
- Object* enemy = TheAI->findClosestRepulsor(getMachineOwner(), obj->getVisionRange());
- if (enemy) {
- return STATE_FAILURE;
- }
- }
- }
- // if move to has finished, move to next point on waypoint path
- if (status != STATE_CONTINUE)
- {
- Int delta = 3;
- if (ai->getCurLocomotor()) {
- delta = REAL_TO_INT_FLOOR( (ai->getCurLocomotor()->getWanderAboutPointRadius()/PATHFIND_CELL_SIZE_F) + 0.5f);
- }
- Coord3D offset;
- offset.x = GameLogicRandomValue(-delta, delta)*PATHFIND_CELL_SIZE_F;
- offset.y = GameLogicRandomValue(-delta, delta)*PATHFIND_CELL_SIZE_F;
- m_goalPosition = m_origin;
- m_goalPosition.x += offset.x;
- m_goalPosition.y += offset.y;
- AIInternalMoveToState::onEnter();
- return STATE_CONTINUE;
- }
- // Never leave this state until told to.
- return STATE_CONTINUE;
- }
- //----------------------------------------------------------------------------------------------------------
- void AIWanderInPlaceState::onExit( StateExitType status )
- {
- AIInternalMoveToState::onExit( status );
- }
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIPanicState::crc( Xfer *xfer )
- {
- AIFollowWaypointPathState::crc(xfer);
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIPanicState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- AIFollowWaypointPathState::xfer(xfer);
- xfer->xferInt(&m_waitFrames);
- xfer->xferInt(&m_timer);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIPanicState::loadPostProcess( void )
- {
- AIFollowWaypointPathState::loadPostProcess();
- } // end loadPostProcess
- /**
- * Panic and run screaming along a waypoint path.
- */
- StateReturnType AIPanicState::onEnter()
- {
- m_currentWaypoint = ((AIStateMachine *)getMachine())->getGoalWaypoint();
- Object *obj = getMachineOwner();
- AIUpdateInterface *ai = obj->getAI();
- if (m_currentWaypoint == NULL)
- return STATE_FAILURE;
- // set initial movement goal
- Locomotor* curLoco = ai->getCurLocomotor();
- if (curLoco && curLoco->getWanderWidthFactor() > 0.0f) {
- Int delta = REAL_TO_INT_FLOOR(curLoco->getWanderWidthFactor()+0.5f);
- if (delta<1) delta = 1;
- m_groupOffset.x = GameLogicRandomValue(-delta, delta)*PATHFIND_CELL_SIZE_F;
- m_groupOffset.y = GameLogicRandomValue(-delta, delta)*PATHFIND_CELL_SIZE_F;
- }
- computeGoal(false);
- StateReturnType ret = AIInternalMoveToState::onEnter();
- m_timer = 0;
- m_waitFrames = 10 + (getMachineOwner()->getID() & 0x7);
- // Update the extra path distance. AIInternalMoveToState::onEnter resets it.
- ai->setPathExtraDistance(calcExtraPathDistance());
- if (obj)
- {
- obj->setModelConditionState(MODELCONDITION_PANICKING);
- }
- return ret;
- }
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIPanicState::update()
- {
- // do movement
- StateReturnType status = AIInternalMoveToState::update();
- Object *obj = getMachineOwner();
- if (obj->isKindOf(KINDOF_CAN_BE_REPULSED)) {
- m_timer--;
- if (m_timer<0) {
- m_timer = m_waitFrames;
- Object* enemy = TheAI->findClosestRepulsor(getMachineOwner(), obj->getVisionRange());
- if (enemy) {
- return STATE_FAILURE;
- }
- }
- }
- // if move to has finished, move to next point on waypoint path
- if (status == STATE_SUCCESS)
- {
- Object *obj = getMachineOwner();
- AIUpdateInterface *ai = obj->getAI();
- m_currentWaypoint = getNextWaypoint();
- // if there are no links from this waypoint, we're done
- if (m_currentWaypoint == NULL) {
- /// Trigger "end of waypoint path" scripts (jba)
- ai->setCompletedWaypoint(m_priorWaypoint);
-
- return STATE_SUCCESS;
- }
- Locomotor* curLoco = ai->getCurLocomotor();
- if (curLoco && curLoco->getWanderWidthFactor() > 0.0f) {
- Int delta = REAL_TO_INT_FLOOR(curLoco->getWanderWidthFactor()+0.5f);
- if (delta<1) delta = 1;
- m_groupOffset.x = GameLogicRandomValue(-delta, delta)*PATHFIND_CELL_SIZE_F;
- m_groupOffset.y = GameLogicRandomValue(-delta, delta)*PATHFIND_CELL_SIZE_F;
- }
- computeGoal(false);
- computePath();
- return STATE_CONTINUE;
- }
- // Never leave this state until told to.
- return STATE_CONTINUE;
- }
- //----------------------------------------------------------------------------------------------------------
- void AIPanicState::onExit( StateExitType status )
- {
- Object *obj = getMachineOwner();
- obj->clearModelConditionState(MODELCONDITION_PANICKING);
- AIInternalMoveToState::onExit( status );
- }
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIAttackAimAtTargetState::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIAttackAimAtTargetState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- xfer->xferBool(&m_canTurnInPlace);
- xfer->xferBool(&m_setLocomotor);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIAttackAimAtTargetState::loadPostProcess( void )
- {
- } // end loadPostProcess
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIAttackAimAtTargetState::onEnter()
- {
- // contained by AIAttackState, so no separate timer
- Object* source = getMachineOwner();
- Weapon* weapon = source->getCurrentWeapon();
- Object* victim = getMachineGoalObject();
- const Coord3D* targetPos = getMachineGoalPosition();
- AIUpdateInterface* sourceAI = source->getAI();
- AIUpdateInterface* victimAI = victim ? victim->getAI() : NULL;
- Locomotor* curLoco = sourceAI->getCurLocomotor();
- m_canTurnInPlace = curLoco ? curLoco->getMinSpeed() == 0.0f : false;
- // if (!victim)
- // return STATE_CONTINUE; // Just continue till we get a victim.
- // Ick. This was originally a safety to a single line that required victim, and was never meant
- // as an early return to all cases. We now want to use preattack frames on ground position targets
- Bool inFiringRange = FALSE;
- //If the object is garrisoning a building, then we want to force the object
- //to move to the best fire point, check if it's in firing range, and if not
- //move it back so another unit with longer range can!
- Object *containedBy = source->getContainedBy();
- if( containedBy && weapon )
- {
- ContainModuleInterface *contain = containedBy->getContain();
- if (victim)
- {
- inFiringRange = contain->attemptBestFirePointPosition( source, weapon, victim );
- }
- else
- {
- inFiringRange = contain->attemptBestFirePointPosition( source, weapon, targetPos );
- }
- }
- else if( victim && weapon )
- inFiringRange = weapon->isWithinAttackRange( source, victim );
- else if( weapon )
- inFiringRange = weapon->isWithinAttackRange( source, targetPos );//See, I can be attacking the ground. Perfectly valid.
- else
- return STATE_FAILURE; // can't happen.
- // add ourself as a targeter BEFORE calling isTemporarilyPreventingAimSuccess().
- if (victimAI)
- victimAI->addTargeter(source->getID(), true);
-
- WhichTurretType tur = sourceAI->getWhichTurretForCurWeapon();
- if (tur != TURRET_INVALID)
- {
- if (m_isAttackingObject)
- {
- sourceAI->setTurretTargetObject(tur, victim, m_isForceAttacking);
- }
- else
- {
- sourceAI->setTurretTargetPosition(tur, getMachineGoalPosition());
- }
- }
- else
- {
- // GS moved contact weapon check in here, because Success can never be given to a unit in this state
- // using a turret to attack. Check out ::update and you will see.
- Bool preventing = victimAI && victimAI->isTemporarilyPreventingAimSuccess();
- // Contact weapons don't aim. They just go boom. jba.
- if( weapon && weapon->isContactWeapon() && inFiringRange && !preventing )
- return STATE_SUCCESS;
- }
- m_setLocomotor = false;
- source->setStatus( OBJECT_STATUS_IS_AIMING_WEAPON, true );
- return STATE_CONTINUE;
- }
- //----------------------------------------------------------------------------------------------------------
- /**
- * Orient the machine's owner to face towards the given target
- */
- StateReturnType AIAttackAimAtTargetState::update()
- {
- // contained by AIAttackState, so no separate timer
- Object* source = getMachineOwner();
- AIUpdateInterface* sourceAI = source->getAI();
- if (!source->hasAnyWeapon())
- return STATE_FAILURE;
- Object* victim = getMachineGoalObject();
- if (m_isAttackingObject)
- {
- if (!victim || victim->isEffectivelyDead())
- return STATE_FAILURE; // can't aim at dead things
- }
- WhichTurretType tur = sourceAI->getWhichTurretForCurWeapon();
- if (tur != TURRET_INVALID)
- {
- if (m_isAttackingObject)
- {
- sourceAI->setTurretTargetObject(tur, victim, m_isForceAttacking);
- }
- else
- {
- sourceAI->setTurretTargetPosition(tur, getMachineGoalPosition());
- }
- // if we have a turret, but it is incapable of turning, turn ourself.
- // (gotta do this for units like the Comanche, which have fake "turrets"
- // solely to allow for attacking-on-the-move...)
- if (sourceAI->getTurretTurnRate(tur) != 0.0f)
- {
- // The Body can never return Success if the weapon is on the turret, or else we end
- // up shooting the current weapon (which is on the turret) in the wrong direction.
- // We always say Continue, so the Turret can do its own Aiming state.
- // if (m_isAttackingObject && source->canCrushOrSquish(victim)) {
- // return STATE_SUCCESS;
- // }
- return STATE_CONTINUE;
- }
- // else fall thru!
- }
-
- // no else here!
- {
- Real relAngle = m_isAttackingObject ?
- ThePartitionManager->getRelativeAngle2D( source, victim ) :
- ThePartitionManager->getRelativeAngle2D( source, getMachineGoalPosition() );
- const Real REL_THRESH = 0.035f; // about 2 degrees. (getRelativeAngle2D is current only accurate to about 1.25 degrees)
- Weapon* weapon = source->getCurrentWeapon();
- Real aimDelta = weapon ? weapon->getAimDelta() : 0.0f;
- if (aimDelta < REL_THRESH) aimDelta = REL_THRESH;
- //DEBUG_LOG(("AIM: desired %f, actual %f, delta %f, aimDelta %f, goalpos %f %f\n",rad2deg(obj->getOrientation() + relAngle),rad2deg(obj->getOrientation()),rad2deg(relAngle),rad2deg(aimDelta),victim->getPosition()->x,victim->getPosition()->y));
- if (m_canTurnInPlace)
- {
- if (fabs(relAngle) > aimDelta)
- {
- Real desiredAngle = source->getOrientation() + relAngle;
- sourceAI->setLocomotorGoalOrientation(desiredAngle);
- m_setLocomotor = true;
- }
- }
- else
- {
- sourceAI->setLocomotorGoalPositionExplicit(m_isAttackingObject ? *victim->getPosition() : *getMachineGoalPosition());
- }
- if (fabs(relAngle) < aimDelta /*&& !m_preAttackFrames*/ )
- {
- AIUpdateInterface* victimAI = victim ? victim->getAI() : NULL;
- // add ourself as a targeter BEFORE calling isTemporarilyPreventingAimSuccess().
- // we do this every time thru, just in case we get into a squabble with our turret-ai
- // over whether or not we are a targeter... (srj)
- if (victimAI)
- victimAI->addTargeter(source->getID(), true);
- Bool preventing = victimAI && victimAI->isTemporarilyPreventingAimSuccess();
- if (preventing)
- {
- return STATE_CONTINUE;
- }
- else
- {
- return STATE_SUCCESS;
- }
- }
- }
- if (source->isDisabledByType(DISABLED_HELD))
- {
- // We are contained by something (transport, building). This means we can't
- // actually move to the location and if we are firing on a specific target
- // and that target is no longer in range, then we need to abort the state
- // so it can fire on other targets.
- // Are we still in range?
- Weapon *weapon = source->getCurrentWeapon();
- const Coord3D *pos = m_isAttackingObject ? victim->getPosition() : getMachineGoalPosition();
- if( !weapon || !weapon->isWithinAttackRange( source, pos ) )
- {
- // We're no longer in range, so exit with failure so we can automatically
- // reacquire a closer target if possible.
- return STATE_FAILURE;
- }
- }
- return STATE_CONTINUE;
- }
- //----------------------------------------------------------------------------------------------------------
- void AIAttackAimAtTargetState::onExit( StateExitType status )
- {
- // contained by AIAttackState, so no separate timer
- if (m_canTurnInPlace)
- {
- AIUpdateInterface* sourceAI = getMachineOwner()->getAI();
- // Tell the ai we are done moving, if we set the locomotor goal.
- if (sourceAI && m_setLocomotor)
- sourceAI->setLocomotorGoalNone();
- }
- else
- {
- // don't do the loco call, or else we will "wiggle"... we already have an appropriate goal
- }
- getMachineOwner()->setStatus( OBJECT_STATUS_IS_AIMING_WEAPON, false );
- //getMachineOwner()->clearModelConditionState( MODELCONDITION_PREATTACK );
- }
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- /**
- * Start firing.
- */
- StateReturnType AIAttackFireWeaponState::onEnter()
- {
- // contained by AIAttackState, so no separate timer
- DEBUG_ASSERTCRASH(m_att != NULL, ("m_att may not be null"));
- Object *obj = getMachineOwner();
- AIUpdateInterface *ai = obj->getAI();
- // Passive stuff will approach but not attack, so we check here (after approach is complete)
- UnsignedInt adjust = ai->getMoodMatrixActionAdjustment(MM_Action_Attack);
- if ((adjust & MAA_Action_Ok) == 0)
- {
- return STATE_FAILURE;
- }
- Object *victim = getMachineGoalObject();
- if (victim && obj->getTeam()->getPrototype()->getTemplateInfo()->m_attackCommonTarget) {
- if (obj->getTeam()->getTeamTargetObject()==NULL) {
- obj->getTeam()->setTeamTargetObject(victim);
- }
- }
- obj->setStatus( OBJECT_STATUS_IS_FIRING_WEAPON, true );
- obj->preFireCurrentWeapon( getMachineGoalObject() );
- return STATE_CONTINUE;
- }
- //----------------------------------------------------------------------------------------------------------
- /**
- * Fire the owner's weapon once and exit.
- */
- StateReturnType AIAttackFireWeaponState::update()
- {
- // contained by AIAttackState, so no separate timer
- Object *obj = getMachineOwner();
- Object* victim = getMachineGoalObject();
- if (m_att->isAttackingObject())
- {
- // if our target is dead, go ahead and stop.
- if (!victim || victim->isEffectivelyDead())
- return STATE_FAILURE;
- }
- WeaponSlotType wslot;
- Weapon* weapon = obj->getCurrentWeapon(&wslot);
- if (!weapon)
- {
- return STATE_FAILURE;
- }
-
- WeaponStatus status = weapon->getStatus();
- if (status == PRE_ATTACK)
- {
- return STATE_CONTINUE;
- }
- else if (status != READY_TO_FIRE)
- {
- return STATE_FAILURE;
- }
- /**
- this is the weird case where we have multi turrets, and turret 'a' wants
- to fire, but someone has changed the current weapon to be one not on him.
- rather than addressing the situation, we just punt and wait for it to clear
- up on its own.
- */
- if (m_att && !m_att->isWeaponSlotOkToFire(wslot))
- {
- return STATE_FAILURE;
- }
- // must adjust the state BEFORE calling fireWeapon, for FX to work correctly...
- obj->setFiringConditionForCurrentWeapon();
- if (m_att->isAttackingObject())
- {
- obj->fireCurrentWeapon(victim);
- // clear this, just in case.
- obj->setStatus( OBJECT_STATUS_IGNORING_STEALTH, false );
- Real continueRange = weapon->getContinueAttackRange();
- if (
- continueRange > 0.0f &&
- victim &&
- (victim->isDestroyed() || victim->isEffectivelyDead() || (victim->isKindOf(KINDOF_MINE) && victim->testStatus(OBJECT_STATUS_MASKED)))
- )
- {
- const Coord3D* originalVictimPos = m_att ? m_att->getOriginalVictimPos() : NULL;
- if (originalVictimPos)
- {
- // note that it is important to use getLastCommandSource here; this allows
- // dozers that were ordered to clear mines by the human to continue to autoacquire,
- // but not if they were ordered by ai.
- AIUpdateInterface* ai = obj->getAI();
- CommandSourceType lastCmdSource = ai ? ai->getLastCommandSource() : CMD_FROM_AI;
- PartitionFilterSamePlayer filterPlayer( victim->getControllingPlayer() );
- PartitionFilterSameMapStatus filterMapStatus(obj);
- PartitionFilterPossibleToAttack filterAttack(ATTACK_NEW_TARGET, obj, lastCmdSource);
- PartitionFilter *filters[] = { &filterAttack, &filterPlayer, &filterMapStatus, NULL };
- // note that we look around originalVictimPos, *not* the current victim's pos.
- victim = ThePartitionManager->getClosestObject( originalVictimPos, continueRange, FROM_CENTER_2D, filters );// could be null. this is ok.
- if (victim)
- {
- getMachine()->setGoalObject(victim);
- m_att->notifyNewVictimChosen(victim);
- }
- }
- }
- }
- else
- {
- obj->fireCurrentWeapon(getMachineGoalPosition());
- // clear this, just in case.
- obj->setStatus( OBJECT_STATUS_IGNORING_STEALTH, false );
- }
-
- m_att->notifyFired();
- return STATE_SUCCESS;
- }
- //----------------------------------------------------------------------------------------------------------
- /**
- Stop firing.
- */
- void AIAttackFireWeaponState::onExit( StateExitType status )
- {
- // contained by AIAttackState, so no separate timer
- Object *obj = getMachineOwner();
- obj->setStatus( OBJECT_STATUS_IS_FIRING_WEAPON, false );
- // clear this, just in case.
- obj->setStatus( OBJECT_STATUS_IGNORING_STEALTH, false );
- // this can occur if we start a preattack (eg, bayonet)
- // and the target moves out range before we can actually "fire"...
- // leaving us thinking we're still "pre attacking". cancel this state
- // to avoid confusion. (srj)
- Weapon* weapon = obj->getCurrentWeapon();
- if (weapon && weapon->getStatus() == PRE_ATTACK)
- {
- weapon->setPreAttackFinishedFrame(0);
- }
- }
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- /**
- * Do nothing for a period of time.
- */
- StateReturnType AIWaitState::update()
- {
- /// @todo srj -- find a way to sleep for a number of frames here, if possible
- return STATE_CONTINUE;
- }
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- AIAttackState::AIAttackState( StateMachine *machine, Bool follow, Bool attackingObject, Bool forceAttacking, AttackExitConditionsInterface* attackParameters) :
- State( machine , "AIAttackState"),
- m_attackMachine(NULL),
- m_attackParameters(attackParameters),
- m_lockedWeaponOnEnter(NULL),
- m_follow(follow),
- m_isAttackingObject(attackingObject),
- m_isForceAttacking(forceAttacking),
- m_victimTeam( NULL )
- {
- m_originalVictimPos.zero();
- }
- //----------------------------------------------------------------------------------------------------------
- AIAttackState::~AIAttackState()
- {
- // nope, don't do this, since we may well still have it targeted
- // even though we're leaving this state.
- // turn it off when we do setCurrentVictim(NULL).
- //addSelfAsTargeter(false);
- if (m_attackMachine)
- {
- m_attackMachine->halt();
- m_attackMachine->deleteInstance();
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIAttackState::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIAttackState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- Bool hasMachine = m_attackMachine!=NULL;
-
- xfer->xferBool(&hasMachine);
- xfer->xferCoord3D(&m_originalVictimPos);
- if (hasMachine && m_attackMachine==NULL) {
- // create new state machine for attack behavior
- m_attackMachine = newInstance(AttackStateMachine)(getMachineOwner(), this, "AIAttackMachine", m_follow, m_isAttackingObject, m_isForceAttacking );
- }
- if (hasMachine) {
- xfer->xferSnapshot(m_attackMachine); ///< state sub-machine for attack behavior
- }
- /* Not saved or loaded - passed in on creation.
- Bool m_follow;
- Bool m_isAttackingObject; // if false, attacking position
- AttackExitConditionsInterface* m_attackParameters; ///< these are not owned by this, and will not be deleted on destruction
- Bool m_isForceAttacking
- */
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIAttackState::loadPostProcess( void )
- {
- Object* victim = getMachineGoalObject();
- if (victim)
- {
- m_victimTeam = victim->getTeam();
- }
- Object* source = getMachineOwner();
- m_lockedWeaponOnEnter = source->isCurWeaponLocked() ? source->getCurrentWeapon() : NULL;
- } // end loadPostProcess
- #ifdef STATE_MACHINE_DEBUG
- //----------------------------------------------------------------------------------------------------------
- AsciiString AIAttackState::getName( ) const
- {
- AsciiString name = m_name;
- name.concat("/");
- if (m_attackMachine) name.concat(m_attackMachine->getCurrentStateName());
- else name.concat("*NULL m_attackMachine");
- return name;
- }
- #endif
- //----------------------------------------------------------------------------------------------------------
- void AIAttackState::chooseWeapon()
- {
- Object* victim = getMachineGoalObject();
- if (m_isAttackingObject && !victim)
- return;
- Object* source = getMachineOwner();
- AIUpdateInterface *ai = source->getAI();
- if (victim)
- {
- /*bool found =*/ source->chooseBestWeaponForTarget(victim, PREFER_MOST_DAMAGE, ai->getLastCommandSource());
- //DEBUG_ASSERTLOG(found, ("unable to autochoose any weapon for %s\n",source->getTemplate()->getName().str()));
- }
- // Check if we need to update because of the weapon choice switch.
- source->adjustModelConditionForWeaponStatus();
- }
- //----------------------------------------------------------------------------------------------------------
- void AIAttackState::notifyNewVictimChosen(Object* victim)
- {
- // do NOT update m_originalVictimPos here. It's a new victim, not the original!
- getMachine()->setGoalObject(victim);
- if (m_attackMachine)
- m_attackMachine->setGoalObject(victim);
- }
- //----------------------------------------------------------------------------------------------------------
- /**
- * Begin an attack on the machine's goal object.
- * To do this complex behavior, instantiate another state machine as a "sub-machine" of
- * the attack state.
- */
- DECLARE_PERF_TIMER(AIAttackState)
- StateReturnType AIAttackState::onEnter()
- {
- USE_PERF_TIMER(AIAttackState)
- //CRCDEBUG_LOG(("AIAttackState::onEnter() - start for object %d\n", getMachineOwner()->getID()));
- Object* source = getMachineOwner();
- AIUpdateInterface *ai = source->getAI();
- // if we are in sleep mode, we will not attack
- if ((ai->getMoodMatrixActionAdjustment(MM_Action_Attack) & MAA_Action_Ok) == 0)
- return STATE_SUCCESS;
- // if we've met the conditions specified by m_attackParameters, we consider ourselves "successful."
- if (m_attackParameters && m_attackParameters->shouldExit(getMachine()))
- return STATE_SUCCESS;
- // if all of our weapons are out of ammo, can't attack.
- // (this can happen for units which never auto-reload, like the Raptor)
- if (source->isOutOfAmmo() && !source->isKindOf(KINDOF_PROJECTILE))
- return STATE_FAILURE;
- // create new state machine for attack behavior
- //CRCDEBUG_LOG(("AIAttackState::onEnter() - constructing state machine for object %d\n", getMachineOwner()->getID()));
- m_attackMachine = newInstance(AttackStateMachine)(source, this, "AIAttackMachine", m_follow, m_isAttackingObject, m_isForceAttacking );
- // tell the attack machine who the victim of the attack is
- if (m_isAttackingObject)
- {
- Object* victim = getMachineGoalObject();
- if (victim == NULL || victim->isEffectivelyDead())
- {
- ai->notifyVictimIsDead();
- return STATE_FAILURE; // we have nothing to attack!
- }
- m_victimTeam = victim->getTeam();
- m_attackMachine->setGoalObject( victim );
- m_originalVictimPos = *victim->getPosition();
- }
- else
- {
- m_attackMachine->setGoalPosition(getMachineGoalPosition());
- m_originalVictimPos = *getMachineGoalPosition();
- }
- chooseWeapon();
- Weapon* curWeapon = source->getCurrentWeapon();
- if (curWeapon)
- {
- curWeapon->setMaxShotCount(NO_MAX_SHOTS_LIMIT);
- // icky special case for ignoring stealth units we might be targeting, that are currently stealthed. (srj)
- if (curWeapon->getContinueAttackRange() > 0.0f)
- source->setStatus(OBJECT_STATUS_IGNORING_STEALTH, true);
- }
- m_lockedWeaponOnEnter = source->isCurWeaponLocked() ? curWeapon : NULL;
- StateReturnType retType = m_attackMachine->initDefaultState();
- if( retType == STATE_CONTINUE )
- {
- source->setStatus( OBJECT_STATUS_IS_ATTACKING, true );
- source->setModelConditionState( MODELCONDITION_ATTACKING );
- }
- return retType;
- }
- //----------------------------------------------------------------------------------------------------------
- /**
- * Execute one frame of "attack enemy" behavior.
- */
- StateReturnType AIAttackState::update()
- {
- USE_PERF_TIMER(AIAttackState)
- // if we've met the conditions specified by m_attackParameters, we consider ourselves "successful."
- if (m_attackParameters && m_attackParameters->shouldExit(getMachine()))
- {
- return STATE_SUCCESS;
- }
- Object* source = getMachineOwner();
-
- // if all of our weapons are out of ammo, can't attack.
- // (this can happen for units which never auto-reload, like the Raptor)
- if (source->isOutOfAmmo() && !source->isKindOf(KINDOF_PROJECTILE))
- {
- return STATE_FAILURE;
- }
- if (m_isAttackingObject)
- {
- Object* victim = getMachineGoalObject();
- if (victim == NULL || victim->isEffectivelyDead())
- {
- source->getAI()->notifyVictimIsDead();
- return STATE_SUCCESS; // my, that was easy
- }
- if (victim)
- {
- source->getAI()->setCurrentVictim(victim);
- }
- if( victim->getTeam() != m_victimTeam )
- {
- // If, while I have been attacking my victim, it has lost its ability to attack
- //(a recently de-garrisoned building) I should bail here...
- // We are not sure whether the problem occurs here or sometime before, but this is an edge case failsafe for it
- // Steven calls this hack 'greasy,' and I agreesy.-Lorenzen
- AIUpdateInterface *ai = source->getAI();
- if (ai)
- {
- if ( (victim->getStatusBits() & OBJECT_STATUS_CAN_ATTACK) == 0 )
- {
- if ( victim->getContain() != NULL )
- {
- if (victim->getContain()->isGarrisonable() && (victim->getContain()->getContainCount() == 0) )
- {
- if ( source->getRelationship( victim ) == NEUTRAL )
- {
- ai->friend_setGoalObject(NULL);
- if (victim==source->getTeam()->getTeamTargetObject()) {
- source->getTeam()->setTeamTargetObject(NULL);
- }
- ai->notifyVictimIsDead(); // well, not "dead", but longer attackable
- return STATE_FAILURE;
- }
- }
- }
- }
- }
- //The team of the victim has changed since we began attacking it. Evaluate
- //whether or not we should keep attacking it.
- // order matters: we want to know if I consider it to be an enemy, not vice versa
- if( source->getRelationship( victim ) != ENEMIES)
- {
- ai->friend_setGoalObject(NULL);
- if (victim==source->getTeam()->getTeamTargetObject()) {
- source->getTeam()->setTeamTargetObject(NULL);
- }
- ai->notifyVictimIsDead(); // well, not "dead", but longer attackable
- return STATE_FAILURE;
- }
- }
- if (victim != m_attackMachine->getGoalObject())
- {
- // Our parent machine has changed the goal. We need to reset to the new goal. jba.
- m_attackMachine->setGoalObject( victim );
- }
- }
- // re-evaluate our weapon choice every frame, so the sub-states don't have to.
- chooseWeapon();
- Weapon* curWeapon = source->getCurrentWeapon();
- // if we entered with a locked weapon (ie, a special weapon), then we will
- // only keep attacking as long as that weapon remains the cur weapon...
- // if anything ever changes that weapon, we exit attack mode immediately.
- if (m_lockedWeaponOnEnter != NULL && m_lockedWeaponOnEnter != curWeapon)
- return STATE_FAILURE;
- // we've shot as many times as we are allowed to
- if (curWeapon == NULL || curWeapon->getMaxShotCount() <= 0)
- return STATE_FAILURE;
- /**
- * Run the attack state sub-machine.
- * If the attack state machine returns anything other than CONTINUE,
- * it has finished. Propagating the return code will cause
- * the containing state machine to do the right thing.
- * Note the use of CONVERT_SLEEP_TO_CONTINUE; even if the sub-machine
- * sleeps, we still need to be called every frame.
- */
- return CONVERT_SLEEP_TO_CONTINUE(m_attackMachine->updateStateMachine());
- }
- //----------------------------------------------------------------------------------------------------------
- void AIAttackState::onExit( StateExitType status )
- {
- USE_PERF_TIMER(AIAttackState)
- // nope, don't do this, since we may well still have it targeted
- // even though we're leaving this state. turn it off when we
- // turn it off when we do setCurrentVictim(NULL).
- //addSelfAsTargeter(false);
- // destroy the attack machine
- if (m_attackMachine)
- {
- m_attackMachine->deleteInstance();
- m_attackMachine = NULL;
- }
- Object *obj = getMachineOwner();
- obj->setStatus( OBJECT_STATUS_IS_FIRING_WEAPON, false );
- obj->setStatus( OBJECT_STATUS_IS_AIMING_WEAPON, false );
- obj->setStatus( OBJECT_STATUS_IS_ATTACKING, false );
- obj->setStatus( OBJECT_STATUS_IGNORING_STEALTH, false );
- obj->clearModelConditionState( MODELCONDITION_ATTACKING );
- obj->clearLeechRangeModeForAllWeapons();
- AIUpdateInterface *ai = obj->getAI();
- if (ai)
- {
- //ai->notifyVictimIsDead(); no, do NOT do this here.
- ai->setCurrentVictim(NULL);
- for (int i = 0; i < MAX_TURRETS; ++i)
- ai->setTurretTargetObject((WhichTurretType)i, NULL, 0);
- ai->friend_setGoalObject(NULL);
- }
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-----------------------------------------------------------------------------------------------------------
- class AIAttackThenIdleStateMachine : public StateMachine
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE( AIAttackThenIdleStateMachine, "AIAttackThenIdleStateMachine" );
- public:
- AIAttackThenIdleStateMachine( Object *owner, AsciiString name );
- protected:
- // snapshot interface .
- virtual void crc( Xfer *xfer );
- virtual void xfer( Xfer *xfer );
- virtual void loadPostProcess();
- };
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIAttackThenIdleStateMachine::crc( Xfer *xfer )
- {
- StateMachine::crc(xfer);
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIAttackThenIdleStateMachine::xfer( Xfer *xfer )
- {
- XferVersion cv = 1;
- XferVersion v = cv;
- xfer->xferVersion( &v, cv );
- StateMachine::xfer(xfer);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIAttackThenIdleStateMachine::loadPostProcess( void )
- {
- StateMachine::loadPostProcess();
- } // end loadPostProcess
- //-----------------------------------------------------------------------------------------------------------
- AIAttackThenIdleStateMachine::AIAttackThenIdleStateMachine(Object *owner, AsciiString name) : StateMachine(owner, name)
- {
- // order matters: first state is the default state.
- defineState( AI_ATTACK_OBJECT, newInstance(AIAttackState)(this, false, true, false, NULL ), AI_IDLE, AI_IDLE );
- defineState( AI_PICK_UP_CRATE, newInstance(AIPickUpCrateState)( this ), AI_IDLE, AI_IDLE );
- defineState( AI_IDLE, newInstance(AIIdleState)( this, AIIdleState::DO_NOT_LOOK_FOR_TARGETS ), AI_IDLE, AI_IDLE );
- }
- //----------------------------------------------------------------------------------------------------------
- AIAttackThenIdleStateMachine::~AIAttackThenIdleStateMachine()
- {
- }
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- AIAttackSquadState::~AIAttackSquadState()
- {
- if (m_attackSquadMachine) {
- m_attackSquadMachine->halt();
- m_attackSquadMachine->deleteInstance();
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIAttackSquadState::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIAttackSquadState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
-
- Bool hasMachine = m_attackSquadMachine!=NULL;
-
- xfer->xferBool(&hasMachine);
- if (hasMachine && m_attackSquadMachine==NULL) {
- // create new state machine for attack behavior
- m_attackSquadMachine = newInstance(AIAttackThenIdleStateMachine)( getMachineOwner(), "AIAttackMachine" );
- }
- if (hasMachine) {
- xfer->xferSnapshot(m_attackSquadMachine);
- }
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIAttackSquadState::loadPostProcess( void )
- {
- } // end loadPostProcess
- #ifdef STATE_MACHINE_DEBUG
- //----------------------------------------------------------------------------------------------------------
- AsciiString AIAttackSquadState::getName( ) const
- {
- AsciiString name = m_name;
- name.concat("/");
- if (m_attackSquadMachine) name.concat(m_attackSquadMachine->getCurrentStateName());
- else name.concat("*NULL m_attackSquadMachine");
- return name;
- }
- #endif
- //----------------------------------------------------------------------------------------------------------
- /**
- * Begin an attack on the machine's goal team.
- * To do this, we use a sub attack machine.
- */
- StateReturnType AIAttackSquadState::onEnter( void )
- {
- // create new state machine for attack behavior
- m_attackSquadMachine = newInstance(AIAttackThenIdleStateMachine)( getMachineOwner(), "AIAttackMachine" );
-
- Object *victim = chooseVictim();
- // tell the attack machine who the victim of the attack is
- m_attackSquadMachine->setGoalObject( victim );
- // initial state of attack state machine
- return m_attackSquadMachine->initDefaultState();
- }
- //----------------------------------------------------------------------------------------------------------
- /**
- * Execute one frame of "attack enemy" behavior.
- */
- StateReturnType AIAttackSquadState::update( void )
- {
- if( !m_attackSquadMachine )
- {
- return STATE_FAILURE;
- }
- /*
- Note the use of CONVERT_SLEEP_TO_CONTINUE; even if the sub-machine
- sleeps, we still need to be called every frame.
- */
- StateReturnType attackStatus = CONVERT_SLEEP_TO_CONTINUE(m_attackSquadMachine->updateStateMachine());
- // if we're in attack state,
- if (m_attackSquadMachine->getCurrentStateID() != AI_IDLE)
- {
- return attackStatus;
- }
- // Check to see if we have created a crate we need to pick up.
- AIUpdateInterface *ai = getMachineOwner()->getAI();
- Object* crate = ai->checkForCrateToPickup();
- if (crate)
- {
- m_attackSquadMachine->setGoalObject(crate);
- m_attackSquadMachine->setState( AI_PICK_UP_CRATE );
- return STATE_CONTINUE;
- }
- // choose a new target and start over.
- Object *victim = chooseVictim();
- if (!victim)
- {
- return STATE_SUCCESS;
- }
- m_attackSquadMachine->setGoalObject( victim );
- m_attackSquadMachine->setState(AI_ATTACK_OBJECT);
- return STATE_CONTINUE;
- }
- //----------------------------------------------------------------------------------------------------------
- void AIAttackSquadState::onExit( StateExitType status )
- {
- if( m_attackSquadMachine )
- {
- // destroy the attack machine
- m_attackSquadMachine->deleteInstance();
- m_attackSquadMachine = NULL;
- }
- }
- //----------------------------------------------------------------------------------------------------------
- Object *AIAttackSquadState::chooseVictim(void)
- {
- Squad *victimSquad = ((AIStateMachine*)getMachine())->getGoalSquad();
- if (!victimSquad)
- {
- return NULL;
- }
- Object *owner = getMachineOwner();
- AIUpdateInterface *ai = owner->getAI();
- UnsignedInt moodVal = ai->getMoodMatrixValue();
-
- if (moodVal & MM_Controller_AI)
- {
- if (moodVal & MM_Mood_Sleep)
- {
- return NULL;
- }
- if (moodVal & MM_Mood_Passive)
- {
- BodyModuleInterface *bmi = owner->getBodyModule();
- if (!bmi) {
- return NULL;
- }
-
- const DamageInfo *di = bmi->getLastDamageInfo();
- if (!di)
- {
- return NULL;
- }
- return TheGameLogic->findObjectByID(di->in.m_sourceID);
- }
- }
- GameDifficulty difficulty = owner->getControllingPlayer()->getPlayerDifficulty();
- const CommandSourceType cmdSource = ai->getLastCommandSource();
-
- if (cmdSource == CMD_FROM_PLAYER)
- {
- // if a player did this, we want to always give them the Seek and obliterate method.
- difficulty = DIFFICULTY_HARD;
- }
- if (TheScriptEngine->getChooseVictimAlwaysUsesNormal())
- {
- difficulty = DIFFICULTY_NORMAL;
- }
- switch(difficulty)
- {
- case DIFFICULTY_EASY:
- {
- // pick a random unit
- VecObjectPtr objects = victimSquad->getLiveObjects();
- Int numUnits = objects.size();
- if (numUnits == 0)
- {
- return NULL;
- }
- Int unitToAttack = GameLogicRandomValue(0, numUnits - 1);
- return objects[unitToAttack];
- break;
- }
- case DIFFICULTY_NORMAL:
- {
- // pick the closest unit
- PartitionFilterAcceptOnSquad f1(victimSquad);
- PartitionFilterSameMapStatus filterMapStatus(getMachineOwner());
- PartitionFilter *filters[] = { &f1, &filterMapStatus, NULL };
-
- Object *victim = ThePartitionManager->getClosestObject(getMachineOwner(), HUGE_DIST, FROM_CENTER_2D, filters, NULL, NULL);
- return victim;
- break;
- }
- case DIFFICULTY_HARD:
- {
- // everyone picks the same unit
- VecObjectPtr objects = victimSquad->getLiveObjects();
- if (objects.size() > 0)
- {
- return objects[0];
- }
- return NULL;
- break;
- }
- };
- return NULL;
- }
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- AIDockState::~AIDockState()
- {
- if (m_dockMachine) {
- m_dockMachine->halt();
- m_dockMachine->deleteInstance();
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIDockState::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIDockState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- Bool hasMachine = m_dockMachine!=NULL;
-
- xfer->xferBool(&hasMachine);
- if (hasMachine && m_dockMachine==NULL) {
- // create new state machine for attack behavior
- m_dockMachine = newInstance(AIDockMachine)( getMachineOwner());
- }
- if (hasMachine) {
- xfer->xferSnapshot(m_dockMachine);
- }
- xfer->xferBool(&m_usingPrecisionMovement);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIDockState::loadPostProcess( void )
- {
- } // end loadPostProcess
- #ifdef STATE_MACHINE_DEBUG
- //----------------------------------------------------------------------------------------------------------
- AsciiString AIDockState::getName( ) const
- {
- AsciiString name = m_name;
- name.concat("/");
- if (m_dockMachine) name.concat(m_dockMachine->getCurrentStateName());
- else name.concat("*NULL m_dockMachine");
- return name;
- }
- #endif
- //----------------------------------------------------------------------------------------------
- /**
- * Dock with the GoalObject.
- * When we enter the AI_DOCK state, create a docking state machine
- * that implements the details of the docking behavior.
- */
- StateReturnType AIDockState::onEnter()
- {
- // who are we docking with?
- Object *dockWithMe = getMachineGoalObject();
- if (dockWithMe == NULL)
- {
- // we have nothing to dock with!
- DEBUG_LOG(("No goal in AIDockState::onEnter - exiting.\n"));
- return STATE_FAILURE;
- }
- DockUpdateInterface *dock = NULL;
- dock = dockWithMe->getDockUpdateInterface();
- // if we have nothing to dock with, fail
- if (dock == NULL) {
- DEBUG_LOG(("Goal is not a dock in AIDockState::onEnter - exiting.\n"));
- return STATE_FAILURE;
- }
- // tell the pathfinder to ignore the object we are docking with, so it doesn't block us
- AIUpdateInterface *ai = getMachineOwner()->getAI();
- if( ai )
- {
- ai->ignoreObstacle( dockWithMe );
- }
- // create new state machine for attack behavior
- m_dockMachine = newInstance(AIDockMachine)( getMachineOwner());
- // tell the docking machine what it is docking with
- m_dockMachine->setGoalObject( dockWithMe );
- // now that essential parameters are set, set the machine's initial state
- return m_dockMachine->initDefaultState( );
- }
- /**
- * For whatever reason, we are leaving the AI_DOCK state.
- * Destroy the docking sub-machine.
- */
- void AIDockState::onExit( StateExitType status )
- {
- // destroy the dock machine
- if (m_dockMachine) {
- m_dockMachine->halt();// GS, you have to halt before you delete to do cleanup.
- m_dockMachine->deleteInstance();
- m_dockMachine = NULL;
- } else {
- DEBUG_LOG(("Dock exited immediately\n"));
- }
- // stop ignoring our goal object
- AIUpdateInterface *ai = getMachineOwner()->getAI();
- if (ai)
- {
- ai->setCanPathThroughUnits(false);
- ai->ignoreObstacle( NULL );
- }
-
- }
- /**
- * We are in the AI_DOCK state, execute the docking behavior.
- */
- StateReturnType AIDockState::update()
- {
- /**
- * Run the docking state sub-machine.
- * If the dock state machine returns anything other than CONTINUE,
- * it has finished. propagating the return code will cause
- * the containing state machine to do the right thing.
- */
- AIUpdateInterface *ai = getMachineOwner()->getAI();
- if (ai)
- {
- ai->setCanPathThroughUnits(true);
- //if (ai->isBlockedAndStuck()) {
- //DEBUG_LOG(("Blocked and stuck.\n"));
- //}
- //if (ai->getNumFramesBlocked()>5) {
- //DEBUG_LOG(("Blocked %d frames\n", ai->getNumFramesBlocked()));
- //}
- }
- /*
- Note the use of CONVERT_SLEEP_TO_CONTINUE; even if the sub-machine
- sleeps, we still need to be called every frame.
- */
- return CONVERT_SLEEP_TO_CONTINUE(m_dockMachine->updateStateMachine());
- }
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIEnterState::crc( Xfer *xfer )
- {
- AIInternalMoveToState::crc(xfer);
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIEnterState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- AIInternalMoveToState::xfer(xfer);
- xfer->xferObjectID(&m_entryToClear);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIEnterState::loadPostProcess( void )
- {
- AIInternalMoveToState::loadPostProcess();
- } // end loadPostProcess
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIEnterState::onEnter()
- {
- m_entryToClear = INVALID_ID;
- Object* obj = getMachineOwner();
- Object* goal = getMachineGoalObject();
- if (goal)
- {
- if( !TheActionManager->canEnterObject( obj, goal, obj->getAI()->getLastCommandSource(), CHECK_CAPACITY ) )
- return STATE_FAILURE;
- m_goalPosition = *goal->getPosition();
- ContainModuleInterface* contain = goal->getContain();
- if (contain)
- {
- contain->onObjectWantsToEnterOrExit(obj, WANTS_TO_ENTER);
- m_entryToClear = goal->getID();
- }
- }
- else
- {
- return STATE_FAILURE;
- }
- // tell the pathfinder to ignore the enterable object
- AIUpdateInterface *ai = obj->getAI();
- ai->ignoreObstacle( getMachineGoalObject() );
- if (ai->getCurLocomotor())
- {
- ai->getCurLocomotor()->setAllowInvalidPosition(true);
- }
- setAdjustsDestination(false);
- return AIInternalMoveToState::onEnter();
- }
- //----------------------------------------------------------------------------------------------------------
- void AIEnterState::onExit( StateExitType status )
- {
- Object* obj = getMachineOwner();
- AIInternalMoveToState::onExit( status );
- // tell the pathfinder to stop ignoring the object
- AIUpdateInterface *ai = obj->getAI();
- if (ai)
- {
- ai->ignoreObstacle( NULL );
- if (ai->getCurLocomotor())
- {
- ai->getCurLocomotor()->setAllowInvalidPosition(false);
- }
- }
- // use this, rather than getMachineGoalObject, in case the goal
- // is killed while we were waiting...
- if (m_entryToClear != INVALID_ID)
- {
- Object* goal = TheGameLogic->findObjectByID(m_entryToClear);
- if (goal)
- {
- ContainModuleInterface* contain = goal->getContain();
- if (contain)
- {
- contain->onObjectWantsToEnterOrExit(obj, WANTS_NEITHER);
- }
- }
- }
- }
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIEnterState::update()
- {
- // update the goal position to coincide with the GoalObject
- Object* obj = getMachineOwner();
- Object* goal = getMachineGoalObject();
- if (goal)
- {
- // if our goal is contained by something else, give up. this is for the following bug:
- // -- tell some rangers to enter a humvee
- // -- tell the humvee to enter a chinook
- // -- fly the chinook around; the rangers follow the chinook like dopes
- // this just bails in this case. (srj)
- if (goal->getContainedBy() != NULL && goal->isAboveTerrain() && !obj->isAboveTerrain())
- {
- return STATE_FAILURE;
- }
- m_goalPosition = *goal->getPosition();
- obj->getAI()->friend_setGoalObject(goal);
- if (!TheActionManager->canEnterObject(obj, goal, obj->getAI()->getLastCommandSource(), CHECK_CAPACITY))
- {
- /*
- special-case: if it's an enemy, try attacking it instead. this is to address this bug: (srj)
- Bug: Units stop instead of attacking if building they were trying to garrison is taken by enemy units first.
- 1. any game/any faction
- 2. build infantry units that can garrison neutral buildings
- 3. have infantry units garrison a neutral building, before they garrison the building have some enemy units garrison it first.
- Expected result: Based on test plan: Instead of just stopping when enemy units garrison building first, they should continue and attack the building.
- */
- if( obj->getRelationship(goal) == ENEMIES && obj->getAI() )
- {
- CanAttackResult result = TheActionManager->getCanAttackObject(obj, goal, obj->getAI()->getLastCommandSource(), ATTACK_NEW_TARGET);
- if( result == ATTACKRESULT_POSSIBLE || result == ATTACKRESULT_POSSIBLE_AFTER_MOVING )
- {
- obj->getAI()->aiAttackObject(goal, NO_MAX_SHOTS_LIMIT, obj->getAI()->getLastCommandSource());
- // weird but true. return state_continue, because if we're here, we're actually an attack state
- // since we just changed the state, it doesn't really matter what we return here.
- return STATE_CONTINUE;
- }
- return STATE_FAILURE;
- }
- return STATE_FAILURE;
- }
- // If we are held, then we must have entered the goal.
- if( getMachineOwner()->isDisabledByType( DISABLED_HELD ) )
- {
- return STATE_SUCCESS;
- }
- }
- else
- {
- return STATE_FAILURE;
- }
- StateReturnType code = AIInternalMoveToState::update();
- // if it's airborne, wait for it to land
- if (code == STATE_SUCCESS && goal->isAboveTerrain() && !obj->isAboveTerrain())
- {
- code = STATE_CONTINUE;
- }
- if (code == STATE_SUCCESS)
- {
- // Make sure we entered the container.
- // srj sez: I don't think we want to restrict this to HELD items. See the intro of GLA02.map
- // for an example of guys-off-the-border-but-not-held who need this check.
- //if( obj->isDisabledByType( DISABLED_HELD ) )
- {
- if (goal)
- {
- // we didn't enter. See if we're close.
- Real dx = (obj->getPosition()->x - goal->getPosition()->x);
- Real dy = (obj->getPosition()->y - goal->getPosition()->y);
- Real radius = goal->getGeometryInfo().getMinorRadius();
- if (goal->getGeometryInfo().getGeomType()!=GEOMETRY_BOX) {
- radius = goal->getGeometryInfo().getMajorRadius();
- }
- Bool closeEnough = dx*dx+dy*dy < sqr(radius);
- if (closeEnough) {
- // Grab the container and force ourselves into it.
- // This case is primarily to handle transports on the map border for scripted setup.
- // The partition manager doesn't generate collisions in the border area, so we have to
- // add ourselves. jba.
- ContainModuleInterface* contain = goal->getContain();
- if (contain)
- {
- contain->addToContain(obj);
- }
- }
- }
- }
- }
- return code;
- }
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIExitState::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIExitState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- xfer->xferObjectID(&m_entryToClear);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIExitState::loadPostProcess( void )
- {
- } // end loadPostProcess
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIExitState::onEnter()
- {
- m_entryToClear = INVALID_ID;
- Object* obj = getMachineOwner();
- Object* goal = getMachineGoalObject();
- if (goal)
- {
- ContainModuleInterface* contain = goal->getContain();
- if (contain)
- {
- contain->onObjectWantsToEnterOrExit(obj, WANTS_TO_EXIT);
- m_entryToClear = goal->getID();
- }
- return STATE_CONTINUE;
- }
- else
- {
- return STATE_FAILURE;
- }
- }
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIExitState::update()
- {
- // update the goal position to coincide with the GoalObject
- Object* obj = getMachineOwner();
- Object* goal = getMachineGoalObject();
- if (goal)
- {
- AIUpdateInterface* goalAI = goal->getAI();
- if (goalAI && goalAI->getAiFreeToExit(obj) == WAIT_TO_EXIT)
- return STATE_CONTINUE;
- DEBUG_ASSERTCRASH(obj, ("obj must not be null here"));
- //GS. The goal of unified ExitInterfaces dies a horrible death. I can't ask Object for the exit,
- // as removeFromContain is only in the Contain type. I'm spliting the names in shame.
- ExitInterface* goalExitInterface = goal->getContain() ? goal->getContain()->getContainExitInterface() : NULL;
- if( goalExitInterface == NULL )
- return STATE_FAILURE;
- if( goalExitInterface->isExitBusy() )
- return STATE_CONTINUE;// Just wait a sec.
- ExitDoorType exitDoor = goalExitInterface ? goalExitInterface->reserveDoorForExit(obj->getTemplate(), obj) : DOOR_NONE_NEEDED;
- if (exitDoor == DOOR_NONE_AVAILABLE)
- return STATE_FAILURE;
- goalExitInterface->exitObjectViaDoor(obj, exitDoor);
- if( getMachine()->getCurrentStateID() != getID() )
- return STATE_CONTINUE;// Not sucess, because exitViaDoor has changed us to FollowPath, and if we say Success, our machine will think FollowPath succeeded
- else
- return STATE_SUCCESS;
- }
- else
- {
- return STATE_FAILURE;
- }
- }
- //----------------------------------------------------------------------------------------------------------
- void AIExitState::onExit( StateExitType status )
- {
- Object* obj = getMachineOwner();
- // use this, rather than getMachineGoalObject, in case the goal
- // is killed while we were waiting...
- if (m_entryToClear != INVALID_ID)
- {
- Object* goal = TheGameLogic->findObjectByID(m_entryToClear);
- if (goal)
- {
- ContainModuleInterface* contain = goal->getContain();
- if (contain)
- {
- contain->onObjectWantsToEnterOrExit(obj, WANTS_NEITHER);
- }
- }
- }
- }
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- AIGuardState::~AIGuardState()
- {
- if (m_guardMachine) {
- m_guardMachine->halt();
- m_guardMachine->deleteInstance();
- }
- }
- #ifdef STATE_MACHINE_DEBUG
- //----------------------------------------------------------------------------------------------------------
- AsciiString AIGuardState::getName( ) const
- {
- AsciiString name = m_name;
- name.concat("/");
- if (m_guardMachine) name.concat(m_guardMachine->getCurrentStateName());
- else name.concat("*NULL guardMachine");
- return name;
- }
- #endif
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIGuardState::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIGuardState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- Bool hasMachine = m_guardMachine!=NULL;
-
- xfer->xferBool(&hasMachine);
- if (hasMachine && m_guardMachine==NULL) {
- // create new state machine for guard behavior
- m_guardMachine = newInstance(AIGuardMachine)( getMachineOwner());
- }
- if (hasMachine) {
- xfer->xferSnapshot(m_guardMachine);
- }
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIGuardState::loadPostProcess( void )
- {
- } // end loadPostProcess
- //----------------------------------------------------------------------------------------------------------
- /**
- * Guard location.
- */
- StateReturnType AIGuardState::onEnter()
- {
- Object *obj = getMachineOwner();
- AIUpdateInterface *ai = obj->getAI();
- m_guardMachine = newInstance(AIGuardMachine)( getMachineOwner());
- // tell the guarding machine what it is guarding with
- switch(ai->getGuardTargetType())
- {
- case GUARDTARGET_LOCATION: m_guardMachine->setTargetPositionToGuard( ai->getGuardLocation() ); break;
- case GUARDTARGET_OBJECT: m_guardMachine->setTargetToGuard( TheGameLogic->findObjectByID(ai->getGuardObject()) ); break;
- case GUARDTARGET_AREA: m_guardMachine->setAreaToGuard( ai->getAreaToGuard() ); break;
- }
- m_guardMachine->setGuardMode(ai->getGuardMode());
- // now that essential parameters are set, set the machine's initial state
- if (m_guardMachine->initDefaultState() == STATE_FAILURE)
- return STATE_FAILURE;
- return m_guardMachine->setState(AI_GUARD_RETURN);
- }
- //----------------------------------------------------------------------------------------------------------
- void AIGuardState::onExit( StateExitType status )
- {
- m_guardMachine->deleteInstance();
- m_guardMachine = NULL;
- Object *obj = getMachineOwner();
- obj->getAI()->clearGuardTargetType();
- }
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIGuardState::update()
- {
- //DEBUG_LOG(("AIGuardState frame %d: %08lx\n",TheGameLogic->getFrame(),getMachineOwner()));
- if (m_guardMachine == NULL)
- {
- return STATE_FAILURE; // We actually already exited.
- }
- // if all of our weapons are out of ammo, can't attack.
- // (this can happen for units which never auto-reload, like the Raptor)
- Object* owner = getMachineOwner();
- if (owner->isOutOfAmmo() && !owner->isKindOf(KINDOF_PROJECTILE))
- {
- DEBUG_CRASH(("Hmm, this should probably never happen, since this case should be intercepted by JetAIUpdate\n"));
- return STATE_FAILURE;
- }
- getMachine()->lock("AIGuardState::update"); // We don't want to switch out of guard during the update.
- StateReturnType ret = m_guardMachine->updateStateMachine();
- getMachine()->unlock();
- return ret;
- }
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- AITunnelNetworkGuardState::~AITunnelNetworkGuardState()
- {
- if (m_guardMachine) {
- m_guardMachine->halt();
- m_guardMachine->deleteInstance();
- }
- }
- #ifdef STATE_MACHINE_DEBUG
- //----------------------------------------------------------------------------------------------------------
- AsciiString AITunnelNetworkGuardState::getName( ) const
- {
- AsciiString name = m_name;
- name.concat("/");
- if (m_guardMachine) name.concat(m_guardMachine->getCurrentStateName());
- else name.concat("*NULL guardMachine");
- return name;
- }
- #endif
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AITunnelNetworkGuardState::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AITunnelNetworkGuardState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- Bool hasMachine = m_guardMachine!=NULL;
-
- xfer->xferBool(&hasMachine);
- if (hasMachine && m_guardMachine==NULL) {
- // create new state machine for guard behavior
- m_guardMachine = newInstance(AITNGuardMachine)( getMachineOwner());
- }
- if (hasMachine) {
- xfer->xferSnapshot(m_guardMachine);
- }
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AITunnelNetworkGuardState::loadPostProcess( void )
- {
- } // end loadPostProcess
- //----------------------------------------------------------------------------------------------------------
- /**
- * Guard location.
- */
- StateReturnType AITunnelNetworkGuardState::onEnter()
- {
- Object *obj = getMachineOwner();
- AIUpdateInterface *ai = obj->getAI();
- m_guardMachine = newInstance(AITNGuardMachine)( getMachineOwner());
- // tell the guarding machine what it is guarding with
- m_guardMachine->setTargetPositionToGuard( ai->getGuardLocation() );
- m_guardMachine->setGuardMode(ai->getGuardMode());
- // now that essential parameters are set, set the machine's initial state
- if (m_guardMachine->initDefaultState() == STATE_FAILURE)
- return STATE_FAILURE;
- return m_guardMachine->setState(AI_GUARD_RETURN);
- }
- //----------------------------------------------------------------------------------------------------------
- void AITunnelNetworkGuardState::onExit( StateExitType status )
- {
- m_guardMachine->deleteInstance();
- m_guardMachine = NULL;
- Object *obj = getMachineOwner();
- obj->getAI()->clearGuardTargetType();
- }
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AITunnelNetworkGuardState::update()
- {
- //DEBUG_LOG(("AITunnelNetworkGuardState frame %d: %08lx\n",TheGameLogic->getFrame(),getMachineOwner()));
- if (m_guardMachine == NULL)
- {
- return STATE_FAILURE; // We actually already exited.
- }
- // if all of our weapons are out of ammo, can't attack.
- // (this can happen for units which never auto-reload, like the Raptor)
- Object* owner = getMachineOwner();
- if (owner->isOutOfAmmo() && !owner->isKindOf(KINDOF_PROJECTILE))
- {
- DEBUG_CRASH(("Hmm, this should probably never happen, since this case should be intercepted by JetAIUpdate\n"));
- return STATE_FAILURE;
- }
- getMachine()->lock("AITunnelNetworkGuardState::update"); // We don't want to switch out of guard during the update.
- StateReturnType ret = m_guardMachine->updateStateMachine();
- getMachine()->unlock();
- return ret;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- AIHuntState::~AIHuntState()
- {
- if (m_huntMachine)
- {
- m_huntMachine->halt();
- m_huntMachine->deleteInstance();
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIHuntState::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIHuntState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- Bool hasMachine = m_huntMachine!=NULL;
-
- xfer->xferBool(&hasMachine);
- if (hasMachine && m_huntMachine==NULL) {
- // create new state machine for hunt behavior
- m_huntMachine = newInstance(AIAttackThenIdleStateMachine)( getMachineOwner(), "AIAttackThenIdleStateMachine");
- }
- if (hasMachine) {
- xfer->xferSnapshot(m_huntMachine);
- }
- xfer->xferUnsignedInt(&m_nextEnemyScanTime);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIHuntState::loadPostProcess( void )
- {
- } // end loadPostProcess
- //----------------------------------------------------------------------------------------------------------
- /**
- * Hunt (seek and destroy).
- */
- StateReturnType AIHuntState::onEnter()
- {
- // create new state machine for hunt behavior
- m_huntMachine = newInstance(AIAttackThenIdleStateMachine)( getMachineOwner(), "AIAttackThenIdleStateMachine");
- // first time thru, use a random amount so that everyone doesn't scan on the same frame,
- // to avoid "spikes".
- UnsignedInt sleepTime = GameLogicRandomValue(0, ENEMY_SCAN_RATE);
- UnsignedInt now = TheGameLogic->getFrame();
- m_nextEnemyScanTime = now + sleepTime;
- // initial state of hunt state machine
- return m_huntMachine->initDefaultState();
- }
- //----------------------------------------------------------------------------------------------------------
- void AIHuntState::onExit( StateExitType status )
- {
- // destroy the hunt machine
- m_huntMachine->deleteInstance();
- m_huntMachine = NULL;
- Object *obj = getMachineOwner();
- if (obj)
- {
- obj->releaseWeaponLock(LOCKED_TEMPORARILY); // release any temporary locks.
- }
- }
- #ifdef STATE_MACHINE_DEBUG
- //----------------------------------------------------------------------------------------------------------
- AsciiString AIHuntState::getName( ) const
- {
- AsciiString name = m_name;
- name.concat("/");
- if (m_huntMachine) name.concat(m_huntMachine->getCurrentStateName());
- else name.concat("*NULL huntMachine");
- return name;
- }
- #endif
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIHuntState::update()
- {
- // look around for better victims every so often
- UnsignedInt now = TheGameLogic->getFrame();
- if (now >= m_nextEnemyScanTime)
- {
- Object* owner = getMachineOwner();
- // if all of our weapons are out of ammo, can't hunt.
- // (this can happen for units which never auto-reload, like the Raptor)
- if (owner->isOutOfAmmo() && !owner->isKindOf(KINDOF_PROJECTILE))
- return STATE_FAILURE;
- // Check to see if we have created a crate we need to pick up.
- AIUpdateInterface *ai = owner->getAI();
- Object* crate = ai->checkForCrateToPickup();
- if (crate)
- {
- m_huntMachine->setGoalObject(crate);
- m_huntMachine->setState( AI_PICK_UP_CRATE );
- return STATE_CONTINUE;
- }
- m_nextEnemyScanTime = now + ENEMY_SCAN_RATE;
- const AttackPriorityInfo *info = NULL;
- info = ai->getAttackInfo();
- // Check if team auto targets same victim.
- Object* teamVictim = NULL;
- if (owner->getTeam()->getPrototype()->getTemplateInfo()->m_attackCommonTarget)
- {
- teamVictim = owner->getTeam()->getTeamTargetObject();
- }
- Object* victim = NULL;
- if (teamVictim && info==NULL)
- {
- victim = teamVictim;
- }
- else
- {
- // do NOT do line of sight check - we want to find everything
- victim = TheAI->findClosestEnemy( owner, 9999.9f, AI::CAN_ATTACK, info );
- if (victim==NULL && owner->getControllingPlayer() && owner->getControllingPlayer()->getUnitsShouldHunt()) {
- // If we are doing an all hunt, try hunting without the attack priority info. jba.
- victim = TheAI->findClosestEnemy(owner, 9999.9f, AI::CAN_ATTACK, NULL);
- }
- if (owner->getTeam()->getPrototype()->getTemplateInfo()->m_attackCommonTarget)
- {
- // Check priorities.
- if (teamVictim && info) {
- if (victim==NULL) {
- DEBUG_LOG(("Couldnt' find victim. hmm."));
- victim = teamVictim;
- }
- Int teamVictimPriority = info->getPriority(teamVictim->getTemplate());
- Int victimPriority;
- if( victim )
- victimPriority = info->getPriority(victim->getTemplate());
- else
- victimPriority = 0;
- if (teamVictimPriority>=victimPriority) {
- victim = teamVictim;
- }
- }
- owner->getTeam()->setTeamTargetObject(victim);
- }
- }
- m_huntMachine->setGoalObject( victim );
- if (m_huntMachine->getCurrentStateID() == AI_IDLE && victim)
- {
- m_huntMachine->setState( AI_ATTACK_OBJECT );
- }
- if (owner->getControllingPlayer() && owner->getControllingPlayer()->getUnitsShouldHunt()==FALSE) {
- // If we are not doing an all hunt, then exit hunt state - no more victims.
- if (m_huntMachine->getCurrentStateID() == AI_IDLE && victim==NULL) {
- return STATE_SUCCESS; // we killed everything :) jba.
- }
- }
- }
- getMachine()->lock("AIHuntState::update"); // The idle state in the sub machine can sometimes acquire targets.
- // It is important to not switch out of this state via a sub machine call. jba.
- /*
- Note the use of CONVERT_SLEEP_TO_CONTINUE; even if the sub-machine
- sleeps, we still need to be called every frame.
- */
- /// @todo srj -- find a way to sleep for a number of frames here, if possible
- StateReturnType ret = CONVERT_SLEEP_TO_CONTINUE(m_huntMachine->updateStateMachine());
- getMachine()->unlock();
- return ret;
- }
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- AIAttackAreaState::~AIAttackAreaState()
- {
- if (m_attackMachine) {
- m_attackMachine->halt();
- m_attackMachine->deleteInstance();
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIAttackAreaState::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIAttackAreaState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- Bool hasMachine = m_attackMachine!=NULL;
-
- xfer->xferBool(&hasMachine);
- if (hasMachine && m_attackMachine==NULL) {
- // create new state machine for hunt behavior
- m_attackMachine = newInstance(AIAttackThenIdleStateMachine)( getMachineOwner(), "AIAttackThenIdleStateMachine");
- }
- if (hasMachine) {
- xfer->xferSnapshot(m_attackMachine);
- }
- xfer->xferUnsignedInt(&m_nextEnemyScanTime);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIAttackAreaState::loadPostProcess( void )
- {
- } // end loadPostProcess
- #ifdef STATE_MACHINE_DEBUG
- //----------------------------------------------------------------------------------------------------------
- AsciiString AIAttackAreaState::getName( ) const
- {
- AsciiString name = m_name;
- name.concat("/");
- if (m_attackMachine) name.concat(m_attackMachine->getCurrentStateName());
- else name.concat("*NULL m_attackMachine");
- return name;
- }
- #endif
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIAttackAreaState::onEnter()
- {
- // create new state machine for hunt behavior
- m_attackMachine = newInstance(AIAttackThenIdleStateMachine)( getMachineOwner(), "AIAttackThenIdleStateMachine");
- // first time thru, use a random amount so that everyone doesn't scan on the same frame,
- // to avoid "spikes".
- UnsignedInt now = TheGameLogic->getFrame();
- m_nextEnemyScanTime = now + GameLogicRandomValue(0, ENEMY_SCAN_RATE);
- // initial state of hunt state machine
- return m_attackMachine->initDefaultState();
- }
- //----------------------------------------------------------------------------------------------------------
- void AIAttackAreaState::onExit( StateExitType status )
- {
- // destroy the hunt machine
- m_attackMachine->deleteInstance();
- m_attackMachine = NULL;
- }
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIAttackAreaState::update()
- {
- // look around for better victims every so often
- UnsignedInt now = TheGameLogic->getFrame();
- if (now >= m_nextEnemyScanTime)
- {
- Object* owner = getMachineOwner();
- // if all of our weapons are out of ammo, can't hunt.
- // (this can happen for units which never auto-reload, like the Raptor)
- if (owner->isOutOfAmmo() && !owner->isKindOf(KINDOF_PROJECTILE))
- return STATE_FAILURE;
- // first time thru, add a random amount so that everyone doesn't scan on the same frame,
- // to avoid "spikes". Note that this implementation ensures that this unit checks immediately
- // upon entering this state, then wait a possibly-longer-than-usual time (due to randomness),
- // then settle into a regular schedule.
- m_nextEnemyScanTime = now + ENEMY_SCAN_RATE;
- AIUpdateInterface *ai = owner->getAI();
- if (ai->getAreaToGuard() == NULL)
- return STATE_FAILURE;
- const AttackPriorityInfo *info = NULL;
- info = ai->getAttackInfo();
- PartitionFilterPolygonTrigger polyFilter(ai->getAreaToGuard());
- // do NOT do line of sight check - we want to find everything
- Object *victim = TheAI->findClosestEnemy( owner, 9999.9f, AI::CAN_ATTACK, info, &polyFilter );
- m_attackMachine->setGoalObject( victim );
- if (m_attackMachine->getCurrentStateID() == AI_IDLE && victim)
- {
- m_attackMachine->setState( AI_ATTACK_OBJECT );
- }
- if (victim==NULL) {
- return STATE_SUCCESS;
- }
- }
- getMachine()->lock("AIAttackAreaState::update"); // The idle state in the sub machine can sometimes acquire targets.
- // It is important to not switch out of this state via a sub machine call. jba.
- /*
- Note the use of CONVERT_SLEEP_TO_CONTINUE; even if the sub-machine
- sleeps, we still need to be called every frame.
- */
- /// @todo srj -- find a way to sleep for a number of frames here, if possible
- StateReturnType ret = CONVERT_SLEEP_TO_CONTINUE(m_attackMachine->updateStateMachine());
- getMachine()->unlock();
- return ret;
- }
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- //----------------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void AIFaceState::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void AIFaceState::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- xfer->xferBool(&m_canTurnInPlace);
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void AIFaceState::loadPostProcess( void )
- {
- // empty. jba.
- } // end loadPostProcess
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIFaceState::onEnter()
- {
- Object* source = getMachineOwner();
- AIUpdateInterface* ai = source->getAI();
- Locomotor* curLoco = ai->getCurLocomotor();
- m_canTurnInPlace = curLoco ? curLoco->getMinSpeed() == 0.0f : false;
- Object* target = getMachineGoalObject();
- if (m_obj && target == NULL )
- {
- // Nothing to face...
- return STATE_FAILURE;
- }
- return STATE_CONTINUE;
- }
- //----------------------------------------------------------------------------------------------------------
- void AIFaceState::onExit( StateExitType status )
- {
- }
- //----------------------------------------------------------------------------------------------------------
- StateReturnType AIFaceState::update()
- {
- Object *obj = getMachineOwner();
- AIUpdateInterface *ai = obj->getAI();
- const Coord3D* pos = getMachineGoalPosition();
- if (m_obj)
- {
- Object *target = getMachineGoalObject();
- if (!target)
- {
- // Nothing to face.
- return STATE_FAILURE;
- }
- pos = target->getPosition();
- }
- Real relAngle = ThePartitionManager->getRelativeAngle2D( obj, pos );
- const Real REL_THRESH = 0.035f; // about 2 degrees. (getRelativeAngle2D is current only accurate to about 1.25 degrees)
- if( fabs( relAngle ) < REL_THRESH )
- {
- return STATE_SUCCESS;
- }
- // turnDelta = yawRate() NO, do not get this, it is not useful. (srj)
- if (m_canTurnInPlace)
- {
- Real desiredAngle = obj->getOrientation() + relAngle;
- ai->setLocomotorGoalOrientation( desiredAngle );
- }
- else
- {
- ai->setLocomotorGoalPositionExplicit(*pos);
- }
- return STATE_CONTINUE;
- }
|