AIPathfind.cpp 327 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364336533663367336833693370337133723373337433753376337733783379338033813382338333843385338633873388338933903391339233933394339533963397339833993400340134023403340434053406340734083409341034113412341334143415341634173418341934203421342234233424342534263427342834293430343134323433343434353436343734383439344034413442344334443445344634473448344934503451345234533454345534563457345834593460346134623463346434653466346734683469347034713472347334743475347634773478347934803481348234833484348534863487348834893490349134923493349434953496349734983499350035013502350335043505350635073508350935103511351235133514351535163517351835193520352135223523352435253526352735283529353035313532353335343535353635373538353935403541354235433544354535463547354835493550355135523553355435553556355735583559356035613562356335643565356635673568356935703571357235733574357535763577357835793580358135823583358435853586358735883589359035913592359335943595359635973598359936003601360236033604360536063607360836093610361136123613361436153616361736183619362036213622362336243625362636273628362936303631363236333634363536363637363836393640364136423643364436453646364736483649365036513652365336543655365636573658365936603661366236633664366536663667366836693670367136723673367436753676367736783679368036813682368336843685368636873688368936903691369236933694369536963697369836993700370137023703370437053706370737083709371037113712371337143715371637173718371937203721372237233724372537263727372837293730373137323733373437353736373737383739374037413742374337443745374637473748374937503751375237533754375537563757375837593760376137623763376437653766376737683769377037713772377337743775377637773778377937803781378237833784378537863787378837893790379137923793379437953796379737983799380038013802380338043805380638073808380938103811381238133814381538163817381838193820382138223823382438253826382738283829383038313832383338343835383638373838383938403841384238433844384538463847384838493850385138523853385438553856385738583859386038613862386338643865386638673868386938703871387238733874387538763877387838793880388138823883388438853886388738883889389038913892389338943895389638973898389939003901390239033904390539063907390839093910391139123913391439153916391739183919392039213922392339243925392639273928392939303931393239333934393539363937393839393940394139423943394439453946394739483949395039513952395339543955395639573958395939603961396239633964396539663967396839693970397139723973397439753976397739783979398039813982398339843985398639873988398939903991399239933994399539963997399839994000400140024003400440054006400740084009401040114012401340144015401640174018401940204021402240234024402540264027402840294030403140324033403440354036403740384039404040414042404340444045404640474048404940504051405240534054405540564057405840594060406140624063406440654066406740684069407040714072407340744075407640774078407940804081408240834084408540864087408840894090409140924093409440954096409740984099410041014102410341044105410641074108410941104111411241134114411541164117411841194120412141224123412441254126412741284129413041314132413341344135413641374138413941404141414241434144414541464147414841494150415141524153415441554156415741584159416041614162416341644165416641674168416941704171417241734174417541764177417841794180418141824183418441854186418741884189419041914192419341944195419641974198419942004201420242034204420542064207420842094210421142124213421442154216421742184219422042214222422342244225422642274228422942304231423242334234423542364237423842394240424142424243424442454246424742484249425042514252425342544255425642574258425942604261426242634264426542664267426842694270427142724273427442754276427742784279428042814282428342844285428642874288428942904291429242934294429542964297429842994300430143024303430443054306430743084309431043114312431343144315431643174318431943204321432243234324432543264327432843294330433143324333433443354336433743384339434043414342434343444345434643474348434943504351435243534354435543564357435843594360436143624363436443654366436743684369437043714372437343744375437643774378437943804381438243834384438543864387438843894390439143924393439443954396439743984399440044014402440344044405440644074408440944104411441244134414441544164417441844194420442144224423442444254426442744284429443044314432443344344435443644374438443944404441444244434444444544464447444844494450445144524453445444554456445744584459446044614462446344644465446644674468446944704471447244734474447544764477447844794480448144824483448444854486448744884489449044914492449344944495449644974498449945004501450245034504450545064507450845094510451145124513451445154516451745184519452045214522452345244525452645274528452945304531453245334534453545364537453845394540454145424543454445454546454745484549455045514552455345544555455645574558455945604561456245634564456545664567456845694570457145724573457445754576457745784579458045814582458345844585458645874588458945904591459245934594459545964597459845994600460146024603460446054606460746084609461046114612461346144615461646174618461946204621462246234624462546264627462846294630463146324633463446354636463746384639464046414642464346444645464646474648464946504651465246534654465546564657465846594660466146624663466446654666466746684669467046714672467346744675467646774678467946804681468246834684468546864687468846894690469146924693469446954696469746984699470047014702470347044705470647074708470947104711471247134714471547164717471847194720472147224723472447254726472747284729473047314732473347344735473647374738473947404741474247434744474547464747474847494750475147524753475447554756475747584759476047614762476347644765476647674768476947704771477247734774477547764777477847794780478147824783478447854786478747884789479047914792479347944795479647974798479948004801480248034804480548064807480848094810481148124813481448154816481748184819482048214822482348244825482648274828482948304831483248334834483548364837483848394840484148424843484448454846484748484849485048514852485348544855485648574858485948604861486248634864486548664867486848694870487148724873487448754876487748784879488048814882488348844885488648874888488948904891489248934894489548964897489848994900490149024903490449054906490749084909491049114912491349144915491649174918491949204921492249234924492549264927492849294930493149324933493449354936493749384939494049414942494349444945494649474948494949504951495249534954495549564957495849594960496149624963496449654966496749684969497049714972497349744975497649774978497949804981498249834984498549864987498849894990499149924993499449954996499749984999500050015002500350045005500650075008500950105011501250135014501550165017501850195020502150225023502450255026502750285029503050315032503350345035503650375038503950405041504250435044504550465047504850495050505150525053505450555056505750585059506050615062506350645065506650675068506950705071507250735074507550765077507850795080508150825083508450855086508750885089509050915092509350945095509650975098509951005101510251035104510551065107510851095110511151125113511451155116511751185119512051215122512351245125512651275128512951305131513251335134513551365137513851395140514151425143514451455146514751485149515051515152515351545155515651575158515951605161516251635164516551665167516851695170517151725173517451755176517751785179518051815182518351845185518651875188518951905191519251935194519551965197519851995200520152025203520452055206520752085209521052115212521352145215521652175218521952205221522252235224522552265227522852295230523152325233523452355236523752385239524052415242524352445245524652475248524952505251525252535254525552565257525852595260526152625263526452655266526752685269527052715272527352745275527652775278527952805281528252835284528552865287528852895290529152925293529452955296529752985299530053015302530353045305530653075308530953105311531253135314531553165317531853195320532153225323532453255326532753285329533053315332533353345335533653375338533953405341534253435344534553465347534853495350535153525353535453555356535753585359536053615362536353645365536653675368536953705371537253735374537553765377537853795380538153825383538453855386538753885389539053915392539353945395539653975398539954005401540254035404540554065407540854095410541154125413541454155416541754185419542054215422542354245425542654275428542954305431543254335434543554365437543854395440544154425443544454455446544754485449545054515452545354545455545654575458545954605461546254635464546554665467546854695470547154725473547454755476547754785479548054815482548354845485548654875488548954905491549254935494549554965497549854995500550155025503550455055506550755085509551055115512551355145515551655175518551955205521552255235524552555265527552855295530553155325533553455355536553755385539554055415542554355445545554655475548554955505551555255535554555555565557555855595560556155625563556455655566556755685569557055715572557355745575557655775578557955805581558255835584558555865587558855895590559155925593559455955596559755985599560056015602560356045605560656075608560956105611561256135614561556165617561856195620562156225623562456255626562756285629563056315632563356345635563656375638563956405641564256435644564556465647564856495650565156525653565456555656565756585659566056615662566356645665566656675668566956705671567256735674567556765677567856795680568156825683568456855686568756885689569056915692569356945695569656975698569957005701570257035704570557065707570857095710571157125713571457155716571757185719572057215722572357245725572657275728572957305731573257335734573557365737573857395740574157425743574457455746574757485749575057515752575357545755575657575758575957605761576257635764576557665767576857695770577157725773577457755776577757785779578057815782578357845785578657875788578957905791579257935794579557965797579857995800580158025803580458055806580758085809581058115812581358145815581658175818581958205821582258235824582558265827582858295830583158325833583458355836583758385839584058415842584358445845584658475848584958505851585258535854585558565857585858595860586158625863586458655866586758685869587058715872587358745875587658775878587958805881588258835884588558865887588858895890589158925893589458955896589758985899590059015902590359045905590659075908590959105911591259135914591559165917591859195920592159225923592459255926592759285929593059315932593359345935593659375938593959405941594259435944594559465947594859495950595159525953595459555956595759585959596059615962596359645965596659675968596959705971597259735974597559765977597859795980598159825983598459855986598759885989599059915992599359945995599659975998599960006001600260036004600560066007600860096010601160126013601460156016601760186019602060216022602360246025602660276028602960306031603260336034603560366037603860396040604160426043604460456046604760486049605060516052605360546055605660576058605960606061606260636064606560666067606860696070607160726073607460756076607760786079608060816082608360846085608660876088608960906091609260936094609560966097609860996100610161026103610461056106610761086109611061116112611361146115611661176118611961206121612261236124612561266127612861296130613161326133613461356136613761386139614061416142614361446145614661476148614961506151615261536154615561566157615861596160616161626163616461656166616761686169617061716172617361746175617661776178617961806181618261836184618561866187618861896190619161926193619461956196619761986199620062016202620362046205620662076208620962106211621262136214621562166217621862196220622162226223622462256226622762286229623062316232623362346235623662376238623962406241624262436244624562466247624862496250625162526253625462556256625762586259626062616262626362646265626662676268626962706271627262736274627562766277627862796280628162826283628462856286628762886289629062916292629362946295629662976298629963006301630263036304630563066307630863096310631163126313631463156316631763186319632063216322632363246325632663276328632963306331633263336334633563366337633863396340634163426343634463456346634763486349635063516352635363546355635663576358635963606361636263636364636563666367636863696370637163726373637463756376637763786379638063816382638363846385638663876388638963906391639263936394639563966397639863996400640164026403640464056406640764086409641064116412641364146415641664176418641964206421642264236424642564266427642864296430643164326433643464356436643764386439644064416442644364446445644664476448644964506451645264536454645564566457645864596460646164626463646464656466646764686469647064716472647364746475647664776478647964806481648264836484648564866487648864896490649164926493649464956496649764986499650065016502650365046505650665076508650965106511651265136514651565166517651865196520652165226523652465256526652765286529653065316532653365346535653665376538653965406541654265436544654565466547654865496550655165526553655465556556655765586559656065616562656365646565656665676568656965706571657265736574657565766577657865796580658165826583658465856586658765886589659065916592659365946595659665976598659966006601660266036604660566066607660866096610661166126613661466156616661766186619662066216622662366246625662666276628662966306631663266336634663566366637663866396640664166426643664466456646664766486649665066516652665366546655665666576658665966606661666266636664666566666667666866696670667166726673667466756676667766786679668066816682668366846685668666876688668966906691669266936694669566966697669866996700670167026703670467056706670767086709671067116712671367146715671667176718671967206721672267236724672567266727672867296730673167326733673467356736673767386739674067416742674367446745674667476748674967506751675267536754675567566757675867596760676167626763676467656766676767686769677067716772677367746775677667776778677967806781678267836784678567866787678867896790679167926793679467956796679767986799680068016802680368046805680668076808680968106811681268136814681568166817681868196820682168226823682468256826682768286829683068316832683368346835683668376838683968406841684268436844684568466847684868496850685168526853685468556856685768586859686068616862686368646865686668676868686968706871687268736874687568766877687868796880688168826883688468856886688768886889689068916892689368946895689668976898689969006901690269036904690569066907690869096910691169126913691469156916691769186919692069216922692369246925692669276928692969306931693269336934693569366937693869396940694169426943694469456946694769486949695069516952695369546955695669576958695969606961696269636964696569666967696869696970697169726973697469756976697769786979698069816982698369846985698669876988698969906991699269936994699569966997699869997000700170027003700470057006700770087009701070117012701370147015701670177018701970207021702270237024702570267027702870297030703170327033703470357036703770387039704070417042704370447045704670477048704970507051705270537054705570567057705870597060706170627063706470657066706770687069707070717072707370747075707670777078707970807081708270837084708570867087708870897090709170927093709470957096709770987099710071017102710371047105710671077108710971107111711271137114711571167117711871197120712171227123712471257126712771287129713071317132713371347135713671377138713971407141714271437144714571467147714871497150715171527153715471557156715771587159716071617162716371647165716671677168716971707171717271737174717571767177717871797180718171827183718471857186718771887189719071917192719371947195719671977198719972007201720272037204720572067207720872097210721172127213721472157216721772187219722072217222722372247225722672277228722972307231723272337234723572367237723872397240724172427243724472457246724772487249725072517252725372547255725672577258725972607261726272637264726572667267726872697270727172727273727472757276727772787279728072817282728372847285728672877288728972907291729272937294729572967297729872997300730173027303730473057306730773087309731073117312731373147315731673177318731973207321732273237324732573267327732873297330733173327333733473357336733773387339734073417342734373447345734673477348734973507351735273537354735573567357735873597360736173627363736473657366736773687369737073717372737373747375737673777378737973807381738273837384738573867387738873897390739173927393739473957396739773987399740074017402740374047405740674077408740974107411741274137414741574167417741874197420742174227423742474257426742774287429743074317432743374347435743674377438743974407441744274437444744574467447744874497450745174527453745474557456745774587459746074617462746374647465746674677468746974707471747274737474747574767477747874797480748174827483748474857486748774887489749074917492749374947495749674977498749975007501750275037504750575067507750875097510751175127513751475157516751775187519752075217522752375247525752675277528752975307531753275337534753575367537753875397540754175427543754475457546754775487549755075517552755375547555755675577558755975607561756275637564756575667567756875697570757175727573757475757576757775787579758075817582758375847585758675877588758975907591759275937594759575967597759875997600760176027603760476057606760776087609761076117612761376147615761676177618761976207621762276237624762576267627762876297630763176327633763476357636763776387639764076417642764376447645764676477648764976507651765276537654765576567657765876597660766176627663766476657666766776687669767076717672767376747675767676777678767976807681768276837684768576867687768876897690769176927693769476957696769776987699770077017702770377047705770677077708770977107711771277137714771577167717771877197720772177227723772477257726772777287729773077317732773377347735773677377738773977407741774277437744774577467747774877497750775177527753775477557756775777587759776077617762776377647765776677677768776977707771777277737774777577767777777877797780778177827783778477857786778777887789779077917792779377947795779677977798779978007801780278037804780578067807780878097810781178127813781478157816781778187819782078217822782378247825782678277828782978307831783278337834783578367837783878397840784178427843784478457846784778487849785078517852785378547855785678577858785978607861786278637864786578667867786878697870787178727873787478757876787778787879788078817882788378847885788678877888788978907891789278937894789578967897789878997900790179027903790479057906790779087909791079117912791379147915791679177918791979207921792279237924792579267927792879297930793179327933793479357936793779387939794079417942794379447945794679477948794979507951795279537954795579567957795879597960796179627963796479657966796779687969797079717972797379747975797679777978797979807981798279837984798579867987798879897990799179927993799479957996799779987999800080018002800380048005800680078008800980108011801280138014801580168017801880198020802180228023802480258026802780288029803080318032803380348035803680378038803980408041804280438044804580468047804880498050805180528053805480558056805780588059806080618062806380648065806680678068806980708071807280738074807580768077807880798080808180828083808480858086808780888089809080918092809380948095809680978098809981008101810281038104810581068107810881098110811181128113811481158116811781188119812081218122812381248125812681278128812981308131813281338134813581368137813881398140814181428143814481458146814781488149815081518152815381548155815681578158815981608161816281638164816581668167816881698170817181728173817481758176817781788179818081818182818381848185818681878188818981908191819281938194819581968197819881998200820182028203820482058206820782088209821082118212821382148215821682178218821982208221822282238224822582268227822882298230823182328233823482358236823782388239824082418242824382448245824682478248824982508251825282538254825582568257825882598260826182628263826482658266826782688269827082718272827382748275827682778278827982808281828282838284828582868287828882898290829182928293829482958296829782988299830083018302830383048305830683078308830983108311831283138314831583168317831883198320832183228323832483258326832783288329833083318332833383348335833683378338833983408341834283438344834583468347834883498350835183528353835483558356835783588359836083618362836383648365836683678368836983708371837283738374837583768377837883798380838183828383838483858386838783888389839083918392839383948395839683978398839984008401840284038404840584068407840884098410841184128413841484158416841784188419842084218422842384248425842684278428842984308431843284338434843584368437843884398440844184428443844484458446844784488449845084518452845384548455845684578458845984608461846284638464846584668467846884698470847184728473847484758476847784788479848084818482848384848485848684878488848984908491849284938494849584968497849884998500850185028503850485058506850785088509851085118512851385148515851685178518851985208521852285238524852585268527852885298530853185328533853485358536853785388539854085418542854385448545854685478548854985508551855285538554855585568557855885598560856185628563856485658566856785688569857085718572857385748575857685778578857985808581858285838584858585868587858885898590859185928593859485958596859785988599860086018602860386048605860686078608860986108611861286138614861586168617861886198620862186228623862486258626862786288629863086318632863386348635863686378638863986408641864286438644864586468647864886498650865186528653865486558656865786588659866086618662866386648665866686678668866986708671867286738674867586768677867886798680868186828683868486858686868786888689869086918692869386948695869686978698869987008701870287038704870587068707870887098710871187128713871487158716871787188719872087218722872387248725872687278728872987308731873287338734873587368737873887398740874187428743874487458746874787488749875087518752875387548755875687578758875987608761876287638764876587668767876887698770877187728773877487758776877787788779878087818782878387848785878687878788878987908791879287938794879587968797879887998800880188028803880488058806880788088809881088118812881388148815881688178818881988208821882288238824882588268827882888298830883188328833883488358836883788388839884088418842884388448845884688478848884988508851885288538854885588568857885888598860886188628863886488658866886788688869887088718872887388748875887688778878887988808881888288838884888588868887888888898890889188928893889488958896889788988899890089018902890389048905890689078908890989108911891289138914891589168917891889198920892189228923892489258926892789288929893089318932893389348935893689378938893989408941894289438944894589468947894889498950895189528953895489558956895789588959896089618962896389648965896689678968896989708971897289738974897589768977897889798980898189828983898489858986898789888989899089918992899389948995899689978998899990009001900290039004900590069007900890099010901190129013901490159016901790189019902090219022902390249025902690279028902990309031903290339034903590369037903890399040904190429043904490459046904790489049905090519052905390549055905690579058905990609061906290639064906590669067906890699070907190729073907490759076907790789079908090819082908390849085908690879088908990909091909290939094909590969097909890999100910191029103910491059106910791089109911091119112911391149115911691179118911991209121912291239124912591269127912891299130913191329133913491359136913791389139914091419142914391449145914691479148914991509151915291539154915591569157915891599160916191629163916491659166916791689169917091719172917391749175917691779178917991809181918291839184918591869187918891899190919191929193919491959196919791989199920092019202920392049205920692079208920992109211921292139214921592169217921892199220922192229223922492259226922792289229923092319232923392349235923692379238923992409241924292439244924592469247924892499250925192529253925492559256925792589259926092619262926392649265926692679268926992709271927292739274927592769277927892799280928192829283928492859286928792889289929092919292929392949295929692979298929993009301930293039304930593069307930893099310931193129313931493159316931793189319932093219322932393249325932693279328932993309331933293339334933593369337933893399340934193429343934493459346934793489349935093519352935393549355935693579358935993609361936293639364936593669367936893699370937193729373937493759376937793789379938093819382938393849385938693879388938993909391939293939394939593969397939893999400940194029403940494059406940794089409941094119412941394149415941694179418941994209421942294239424942594269427942894299430943194329433943494359436943794389439944094419442944394449445944694479448944994509451945294539454945594569457945894599460946194629463946494659466946794689469947094719472947394749475947694779478947994809481948294839484948594869487948894899490949194929493949494959496949794989499950095019502950395049505950695079508950995109511951295139514951595169517951895199520952195229523952495259526952795289529953095319532953395349535953695379538953995409541954295439544954595469547954895499550955195529553955495559556955795589559956095619562956395649565956695679568956995709571957295739574957595769577957895799580958195829583958495859586958795889589959095919592959395949595959695979598959996009601960296039604960596069607960896099610961196129613961496159616961796189619962096219622962396249625962696279628962996309631963296339634963596369637963896399640964196429643964496459646964796489649965096519652965396549655965696579658965996609661966296639664966596669667966896699670967196729673967496759676967796789679968096819682968396849685968696879688968996909691969296939694969596969697969896999700970197029703970497059706970797089709971097119712971397149715971697179718971997209721972297239724972597269727972897299730973197329733973497359736973797389739974097419742974397449745974697479748974997509751975297539754975597569757975897599760976197629763976497659766976797689769977097719772977397749775977697779778977997809781978297839784978597869787978897899790979197929793979497959796979797989799980098019802980398049805980698079808980998109811981298139814981598169817981898199820982198229823982498259826982798289829983098319832983398349835983698379838983998409841984298439844984598469847984898499850985198529853985498559856985798589859986098619862986398649865986698679868986998709871987298739874987598769877987898799880988198829883988498859886988798889889989098919892989398949895989698979898989999009901990299039904990599069907990899099910991199129913991499159916991799189919992099219922992399249925992699279928992999309931993299339934993599369937993899399940994199429943994499459946994799489949995099519952995399549955995699579958995999609961996299639964996599669967996899699970997199729973997499759976997799789979998099819982998399849985998699879988998999909991999299939994999599969997999899991000010001100021000310004100051000610007100081000910010100111001210013100141001510016100171001810019100201002110022100231002410025100261002710028100291003010031100321003310034100351003610037100381003910040100411004210043100441004510046100471004810049100501005110052100531005410055100561005710058100591006010061100621006310064100651006610067100681006910070100711007210073100741007510076100771007810079100801008110082100831008410085100861008710088100891009010091100921009310094100951009610097100981009910100101011010210103101041010510106101071010810109101101011110112101131011410115101161011710118101191012010121101221012310124101251012610127101281012910130101311013210133101341013510136101371013810139101401014110142101431014410145101461014710148101491015010151101521015310154101551015610157101581015910160101611016210163101641016510166101671016810169101701017110172101731017410175101761017710178101791018010181101821018310184101851018610187101881018910190101911019210193101941019510196101971019810199102001020110202102031020410205102061020710208102091021010211102121021310214102151021610217102181021910220102211022210223102241022510226102271022810229102301023110232102331023410235102361023710238102391024010241102421024310244102451024610247102481024910250102511025210253102541025510256102571025810259102601026110262102631026410265102661026710268102691027010271102721027310274102751027610277102781027910280102811028210283102841028510286102871028810289102901029110292102931029410295102961029710298102991030010301103021030310304103051030610307103081030910310103111031210313103141031510316103171031810319103201032110322103231032410325103261032710328103291033010331103321033310334103351033610337103381033910340103411034210343103441034510346103471034810349103501035110352103531035410355103561035710358103591036010361103621036310364103651036610367103681036910370103711037210373103741037510376103771037810379103801038110382103831038410385103861038710388103891039010391103921039310394103951039610397103981039910400104011040210403104041040510406104071040810409104101041110412104131041410415104161041710418104191042010421104221042310424104251042610427104281042910430104311043210433104341043510436104371043810439104401044110442104431044410445104461044710448104491045010451104521045310454104551045610457104581045910460104611046210463104641046510466104671046810469104701047110472104731047410475104761047710478104791048010481104821048310484104851048610487104881048910490104911049210493104941049510496104971049810499105001050110502105031050410505105061050710508105091051010511105121051310514105151051610517105181051910520105211052210523105241052510526105271052810529105301053110532105331053410535105361053710538105391054010541105421054310544105451054610547105481054910550105511055210553105541055510556105571055810559105601056110562105631056410565105661056710568105691057010571105721057310574105751057610577105781057910580105811058210583105841058510586105871058810589105901059110592105931059410595105961059710598105991060010601106021060310604106051060610607106081060910610106111061210613106141061510616106171061810619106201062110622106231062410625106261062710628106291063010631106321063310634106351063610637106381063910640106411064210643106441064510646106471064810649106501065110652106531065410655106561065710658106591066010661106621066310664106651066610667106681066910670106711067210673106741067510676106771067810679106801068110682106831068410685106861068710688106891069010691106921069310694106951069610697106981069910700107011070210703107041070510706107071070810709107101071110712107131071410715107161071710718107191072010721107221072310724107251072610727107281072910730107311073210733107341073510736107371073810739107401074110742107431074410745107461074710748107491075010751107521075310754107551075610757107581075910760107611076210763107641076510766107671076810769107701077110772107731077410775107761077710778107791078010781107821078310784107851078610787107881078910790107911079210793107941079510796107971079810799108001080110802108031080410805108061080710808108091081010811108121081310814108151081610817108181081910820108211082210823108241082510826108271082810829108301083110832108331083410835108361083710838108391084010841108421084310844108451084610847108481084910850108511085210853108541085510856108571085810859108601086110862108631086410865108661086710868108691087010871108721087310874108751087610877108781087910880108811088210883108841088510886108871088810889108901089110892108931089410895108961089710898108991090010901109021090310904109051090610907109081090910910109111091210913109141091510916109171091810919109201092110922109231092410925109261092710928109291093010931109321093310934109351093610937109381093910940109411094210943109441094510946109471094810949109501095110952109531095410955109561095710958109591096010961109621096310964109651096610967109681096910970109711097210973109741097510976109771097810979109801098110982109831098410985109861098710988109891099010991109921099310994109951099610997109981099911000110011100211003110041100511006110071100811009110101101111012110131101411015110161101711018110191102011021110221102311024110251102611027110281102911030110311103211033110341103511036110371103811039110401104111042110431104411045110461104711048110491105011051110521105311054110551105611057110581105911060110611106211063110641106511066110671106811069110701107111072110731107411075110761107711078110791108011081110821108311084110851108611087110881108911090110911109211093110941109511096110971109811099111001110111102111031110411105111061110711108111091111011111111121111311114111151111611117111181111911120111211112211123
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // AIPathfind.cpp
  24. // AI pathfinding system
  25. // Author: Michael S. Booth, October 2001
  26. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  27. #include "GameLogic/AIPathfind.h"
  28. #include "Common/PerfTimer.h"
  29. #include "Common/Player.h"
  30. #include "Common/CRCDebug.h"
  31. #include "Common/GlobalData.h"
  32. #include "Common/LatchRestore.h"
  33. #include "Common/ThingTemplate.h"
  34. #include "Common/ThingFactory.h"
  35. #include "GameClient/Line2D.h"
  36. #include "GameLogic/AI.h"
  37. #include "GameLogic/GameLogic.h"
  38. #include "GameLogic/Locomotor.h"
  39. #include "GameLogic/Module/ContainModule.h"
  40. #include "GameLogic/Module/AIUpdate.h"
  41. #include "GameLogic/Module/PhysicsUpdate.h"
  42. #include "GameLogic/Object.h"
  43. #include "GameLogic/PartitionManager.h"
  44. #include "GameLogic/TerrainLogic.h"
  45. #include "GameLogic/Weapon.h"
  46. #include "Common/UnitTimings.h" //Contains the DO_UNIT_TIMINGS define jba.
  47. #define no_INTENSE_DEBUG
  48. #define DEBUG_QPF
  49. #ifdef INTENSE_DEBUG
  50. #include "GameLogic/ScriptEngine.h"
  51. #endif
  52. #include "Common/Xfer.h"
  53. #include "Common/XferCRC.h"
  54. //------------------------------------------------------------------------------ Performance Timers
  55. #include "Common/PerfMetrics.h"
  56. #include "Common/PerfTimer.h"
  57. //-------------------------------------------------------------------------------------------------
  58. #ifdef _INTERNAL
  59. // for occasional debugging...
  60. //#pragma optimize("", off)
  61. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  62. #endif
  63. static inline Bool IS_IMPASSABLE(PathfindCell::CellType type) {
  64. // Return true if cell is impassable to ground units. jba. [8/18/2003]
  65. if (type==PathfindCell::CELL_IMPASSABLE) {
  66. return true;
  67. }
  68. if (type==PathfindCell::CELL_OBSTACLE) {
  69. return true;
  70. }
  71. if (type==PathfindCell::CELL_BRIDGE_IMPASSABLE) {
  72. return true;
  73. }
  74. return false;
  75. }
  76. struct TCheckMovementInfo
  77. {
  78. // Input
  79. ICoord2D cell;
  80. PathfindLayerEnum layer;
  81. Int radius;
  82. Bool centerInCell;
  83. Bool considerTransient;
  84. LocomotorSurfaceTypeMask acceptableSurfaces;
  85. // Output
  86. Int allyFixedCount;
  87. Bool enemyFixed;
  88. Bool allyMoving;
  89. Bool allyGoal;
  90. };
  91. inline Int IABS(Int x) { if (x>=0) return x; return -x;};
  92. //-----------------------------------------------------------------------------------
  93. static Int frameToShowObstacles;
  94. static UnsignedInt ZONE_UPDATE_FREQUENCY = 300;
  95. //-----------------------------------------------------------------------------------
  96. PathNode::PathNode() :
  97. m_nextOpti(0),
  98. m_next(0),
  99. m_prev(0),
  100. m_nextOptiDist2D(0),
  101. m_canOptimize(false),
  102. m_id(-1)
  103. {
  104. m_nextOptiDirNorm2D.x = 0;
  105. m_nextOptiDirNorm2D.y = 0;
  106. m_pos.zero();
  107. m_layer = LAYER_INVALID;
  108. }
  109. //-----------------------------------------------------------------------------------
  110. PathNode::~PathNode()
  111. {
  112. }
  113. //-----------------------------------------------------------------------------------
  114. void PathNode::setNextOptimized(PathNode *node)
  115. {
  116. m_nextOpti = node;
  117. if (node)
  118. {
  119. m_nextOptiDirNorm2D.x = node->getPosition()->x - getPosition()->x;
  120. m_nextOptiDirNorm2D.y = node->getPosition()->y - getPosition()->y;
  121. m_nextOptiDist2D = m_nextOptiDirNorm2D.length();
  122. if (m_nextOptiDist2D == 0.0f)
  123. {
  124. //DEBUG_LOG(("Warning - Path Seg length == 0, adjusting. john a.\n"));
  125. m_nextOptiDist2D = 0.01f;
  126. }
  127. m_nextOptiDirNorm2D.x /= m_nextOptiDist2D;
  128. m_nextOptiDirNorm2D.y /= m_nextOptiDist2D;
  129. }
  130. else
  131. {
  132. m_nextOptiDist2D = 0;
  133. }
  134. }
  135. //-----------------------------------------------------------------------------------
  136. /// given a list, prepend this node, return new list
  137. PathNode *PathNode::prependToList( PathNode *list )
  138. {
  139. m_next = list;
  140. if (list)
  141. list->m_prev = this;
  142. m_prev = NULL;
  143. return this;
  144. }
  145. //-----------------------------------------------------------------------------------
  146. /// given a list, append this node, return new list. slow implementation.
  147. /// @todo optimize this
  148. PathNode *PathNode::appendToList( PathNode *list )
  149. {
  150. if (list == NULL)
  151. {
  152. m_next = NULL;
  153. m_prev = NULL;
  154. return this;
  155. }
  156. PathNode *tail;
  157. for( tail = list; tail->m_next; tail = tail->m_next )
  158. ;
  159. tail->m_next = this;
  160. m_prev = tail;
  161. m_next = NULL;
  162. return list;
  163. }
  164. //-----------------------------------------------------------------------------------
  165. /// given a node, append new node to this.
  166. void PathNode::append( PathNode *newNode )
  167. {
  168. newNode->m_next = this->m_next;
  169. newNode->m_prev = this;
  170. if (newNode->m_next) {
  171. newNode->m_next->m_prev = newNode;
  172. }
  173. this->m_next = newNode;
  174. }
  175. //-----------------------------------------------------------------------------------
  176. /**
  177. * Compute direction vector to next node
  178. */
  179. const Coord3D *PathNode::computeDirectionVector( void )
  180. {
  181. static Coord3D dir;
  182. if (m_next == NULL)
  183. {
  184. if (m_prev == NULL)
  185. {
  186. // only one node on whole path - no direction
  187. dir.x = 0.0f;
  188. dir.y = 0.0f;
  189. dir.z = 0.0f;
  190. }
  191. else
  192. {
  193. // tail node - continue prior direction
  194. return m_prev->computeDirectionVector();
  195. }
  196. }
  197. else
  198. {
  199. dir.x = m_next->m_pos.x - m_pos.x;
  200. dir.y = m_next->m_pos.y - m_pos.y;
  201. dir.z = m_next->m_pos.z - m_pos.z;
  202. }
  203. return &dir;
  204. }
  205. //-----------------------------------------------------------------------------------
  206. Path::Path():
  207. m_path(NULL),
  208. m_pathTail(NULL),
  209. m_isOptimized(FALSE),
  210. m_blockedByAlly(FALSE),
  211. m_cpopRecentStart(NULL),
  212. m_cpopCountdown(MAX_CPOP),
  213. m_cpopValid(FALSE)
  214. {
  215. m_cpopIn.zero();
  216. m_cpopOut.distAlongPath=0;
  217. m_cpopOut.layer = LAYER_GROUND;
  218. m_cpopOut.posOnPath.zero();
  219. }
  220. Path::~Path( void )
  221. {
  222. PathNode *node, *nextNode;
  223. // delete all of the path nodes
  224. for( node = m_path; node; node = nextNode )
  225. {
  226. nextNode = node->getNext();
  227. node->deleteInstance();
  228. }
  229. }
  230. // ------------------------------------------------------------------------------------------------
  231. /** CRC */
  232. // ------------------------------------------------------------------------------------------------
  233. void Path::crc( Xfer *xfer )
  234. {
  235. } // end crc
  236. // ------------------------------------------------------------------------------------------------
  237. /** Xfer Method */
  238. // ------------------------------------------------------------------------------------------------
  239. void Path::xfer( Xfer *xfer )
  240. {
  241. // version
  242. XferVersion currentVersion = 1;
  243. XferVersion version = currentVersion;
  244. xfer->xferVersion( &version, currentVersion );
  245. PathNode *node = m_path;
  246. Int count = 0;
  247. while (node) {
  248. count++;
  249. node = node->getNext();
  250. }
  251. xfer->xferInt(&count);
  252. if (xfer->getXferMode() == XFER_SAVE) {
  253. node = m_pathTail; // Write them out backwards.
  254. while (node) {
  255. node->m_id = count;
  256. xfer->xferInt(&count);
  257. Coord3D pos = *node->getPosition();
  258. xfer->xferCoord3D(&pos);
  259. PathfindLayerEnum layer = node->getLayer();
  260. xfer->xferUser(&layer, sizeof(layer));
  261. Bool canOpt = node->getCanOptimize();
  262. xfer->xferBool(&canOpt);
  263. Int id = -1;
  264. if (node->getNextOptimized()) {
  265. id = node->getNextOptimized()->m_id;
  266. }
  267. xfer->xferInt(&id);
  268. count--;
  269. node = node->getPrevious();
  270. }
  271. DEBUG_ASSERTCRASH(count==0, ("Wrong data count"));
  272. } else {
  273. m_cpopValid = FALSE;
  274. while (count) {
  275. Int nodeId;
  276. xfer->xferInt(&nodeId);
  277. DEBUG_ASSERTCRASH(nodeId==count, ("Bad data"));
  278. Coord3D pos;
  279. xfer->xferCoord3D(&pos);
  280. PathfindLayerEnum layer;
  281. xfer->xferUser(&layer, sizeof(layer));
  282. Bool canOpt;
  283. xfer->xferBool(&canOpt);
  284. Int optID = -1;
  285. xfer->xferInt(&optID);
  286. PathNode *node = newInstance(PathNode);
  287. node->m_id = nodeId;
  288. node->setPosition(&pos);
  289. node->setLayer(layer);
  290. node->setCanOptimize(canOpt);
  291. PathNode *optNode = NULL;
  292. if (optID > 0) {
  293. optNode = m_path;
  294. while (optNode && optNode->m_id != optID) {
  295. optNode = optNode->getNext();
  296. }
  297. DEBUG_ASSERTCRASH (optNode && optNode->m_id == optID, ("Could not find optimized link."));
  298. }
  299. m_path = node->prependToList(m_path);
  300. if (m_pathTail == NULL)
  301. m_pathTail = node;
  302. if (optNode) {
  303. node->setNextOptimized(optNode);
  304. }
  305. count--;
  306. }
  307. }
  308. xfer->xferBool(&m_isOptimized);
  309. Int obsolete1 = 0;
  310. xfer->xferInt(&obsolete1);
  311. UnsignedInt obsolete2;
  312. xfer->xferUnsignedInt(&obsolete2);
  313. xfer->xferBool(&m_blockedByAlly);
  314. #if defined _DEBUG || defined _INTERNAL
  315. if (TheGlobalData->m_debugAI == AI_DEBUG_PATHS)
  316. {
  317. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  318. RGBColor color;
  319. color.blue = 0;
  320. color.red = color.green = 1;
  321. Coord3D pos;
  322. addIcon(NULL, 0, 0, color); // erase feedback.
  323. for( PathNode *node = getFirstNode(); node; node = node->getNext() )
  324. {
  325. // create objects to show path - they decay
  326. pos = *node->getPosition();
  327. addIcon(&pos, PATHFIND_CELL_SIZE_F*.25f, 200, color);
  328. }
  329. // show optimized path
  330. for( node = getFirstNode(); node; node = node->getNextOptimized() )
  331. {
  332. pos = *node->getPosition();
  333. addIcon(&pos, PATHFIND_CELL_SIZE_F*.8f, 200, color);
  334. }
  335. TheAI->pathfinder()->setDebugPath(this);
  336. }
  337. #endif
  338. } // end xfer
  339. // ------------------------------------------------------------------------------------------------
  340. /** Load post process */
  341. // ------------------------------------------------------------------------------------------------
  342. void Path::loadPostProcess( void )
  343. {
  344. } // end loadPostProcess
  345. /**
  346. * Create a new node at the head of the path
  347. */
  348. void Path::prependNode( const Coord3D *pos, PathfindLayerEnum layer )
  349. {
  350. PathNode *node = newInstance(PathNode);
  351. node->setPosition( pos );
  352. node->setLayer(layer);
  353. m_path = node->prependToList( m_path );
  354. if (m_pathTail == NULL)
  355. m_pathTail = node;
  356. m_isOptimized = false;
  357. #ifdef CPOP_STARTS_FROM_PREV_SEG
  358. m_cpopRecentStart = NULL;
  359. #endif
  360. }
  361. /**
  362. * Create a new node at the tail of the path
  363. */
  364. void Path::appendNode( const Coord3D *pos, PathfindLayerEnum layer )
  365. {
  366. if (m_isOptimized && m_pathTail)
  367. {
  368. /* Check for duplicates. */
  369. if (pos->x == m_pathTail->getPosition()->x && pos->y == m_pathTail->getPosition()->y) {
  370. DEBUG_LOG(("Warning - Path Seg length == 0, ignoring. john a.\n"));
  371. return;
  372. }
  373. }
  374. PathNode *node = newInstance(PathNode);
  375. node->setPosition( pos );
  376. node->setLayer(layer);
  377. m_path = node->appendToList( m_path );
  378. if (m_isOptimized && m_pathTail)
  379. {
  380. m_pathTail->setNextOptimized(node);
  381. }
  382. m_pathTail = node;
  383. #ifdef CPOP_STARTS_FROM_PREV_SEG
  384. m_cpopRecentStart = NULL;
  385. #endif
  386. }
  387. /**
  388. * Create a new node at the tail of the path
  389. */
  390. void Path::updateLastNode( const Coord3D *pos )
  391. {
  392. PathfindLayerEnum layer = TheTerrainLogic->getLayerForDestination(pos);
  393. if (m_pathTail) {
  394. m_pathTail->setPosition(pos);
  395. m_pathTail->setLayer(layer);
  396. }
  397. if (m_isOptimized && m_pathTail)
  398. {
  399. PathNode *node = m_path;
  400. while(node && node->getNextOptimized() != m_pathTail) {
  401. node = node->getNextOptimized();
  402. }
  403. if (node && node->getNextOptimized() == m_pathTail) {
  404. node->setNextOptimized(m_pathTail);
  405. }
  406. }
  407. }
  408. /**
  409. * Optimize the path by checking line of sight
  410. */
  411. void Path::optimize( const Object *obj, LocomotorSurfaceTypeMask acceptableSurfaces, Bool blocked )
  412. {
  413. PathNode *node, *anchor;
  414. // start with first node in the path
  415. anchor = getFirstNode();
  416. Bool firstNode = true;
  417. PathfindLayerEnum firstLayer = anchor->getLayer();
  418. // backwards.
  419. //
  420. // For each node in the path, check LOS from last node in path, working forward.
  421. // When a clear LOS is found, keep the resulting straight line segment.
  422. //
  423. while( anchor != getLastNode() )
  424. {
  425. // find the farthest node in the path that has a clear line-of-sight to this anchor
  426. Bool optimizedSegment = false;
  427. PathfindLayerEnum layer = anchor->getLayer();
  428. PathfindLayerEnum curLayer = anchor->getLayer();
  429. Int count = 0;
  430. const Int ALLOWED_STEPS = 3; // we can optimize 3 steps to or from a bridge. Otherwise, we need to insert a point. jba.
  431. for (node = anchor->getNext(); node->getNext(); node=node->getNext()) {
  432. count++;
  433. if (curLayer==LAYER_GROUND) {
  434. if (node->getLayer() != curLayer) {
  435. layer = node->getLayer();
  436. curLayer = layer;
  437. if (count > ALLOWED_STEPS) break;
  438. }
  439. } else {
  440. if (node->getNext()->getLayer() != curLayer) {
  441. if (count > ALLOWED_STEPS) break;
  442. }
  443. }
  444. curLayer = node->getLayer();
  445. if (node->getCanOptimize()==false) {
  446. break;
  447. }
  448. }
  449. if (firstNode) {
  450. layer = firstLayer;
  451. firstNode = false;
  452. }
  453. //PathfindLayerEnum curLayer = LAYER_GROUND;
  454. for( ; node != anchor; node = node->getPrevious() )
  455. {
  456. Bool isPassable = false;
  457. //CRCDEBUG_LOG(("Path::optimize() calling isLinePassable()\n"));
  458. if (TheAI->pathfinder()->isLinePassable( obj, acceptableSurfaces, layer, *anchor->getPosition(),
  459. *node->getPosition(), blocked, false))
  460. {
  461. isPassable = true;
  462. }
  463. PathfindCell* cell = TheAI->pathfinder()->getCell( layer, node->getPosition());
  464. if (cell && cell->getType()==PathfindCell::CELL_CLIFF && !cell->getPinched()) {
  465. isPassable = true;
  466. }
  467. // Horizontal, diagonal, and vertical steps are passable.
  468. if (!isPassable) {
  469. Int dx = node->getPosition()->x - anchor->getPosition()->x;
  470. Int dy = node->getPosition()->y - anchor->getPosition()->y;
  471. Bool mightBePassable = false;
  472. if (IABS(dx)==PATHFIND_CELL_SIZE && IABS(dy)==PATHFIND_CELL_SIZE) {
  473. isPassable = true;
  474. }
  475. PathNode *tmpNode;
  476. if (dx==0) {
  477. mightBePassable = true;
  478. for (tmpNode = node->getPrevious(); tmpNode && tmpNode != anchor; tmpNode = tmpNode->getPrevious()) {
  479. dx = tmpNode->getNext()->getPosition()->x - tmpNode->getPosition()->x;
  480. if (dx!=0) mightBePassable = false;
  481. }
  482. }
  483. if (dy==0) {
  484. mightBePassable = true;
  485. for (tmpNode = node->getPrevious(); tmpNode && tmpNode != anchor; tmpNode = tmpNode->getPrevious()) {
  486. dy = tmpNode->getNext()->getPosition()->y - tmpNode->getPosition()->y;
  487. if (dy!=0) mightBePassable = false;
  488. }
  489. }
  490. if (dx == dy) {
  491. mightBePassable = true;
  492. for (tmpNode = node->getPrevious(); tmpNode && tmpNode != anchor; tmpNode = tmpNode->getPrevious()) {
  493. dx = tmpNode->getNext()->getPosition()->x - tmpNode->getPosition()->x;
  494. dy = tmpNode->getNext()->getPosition()->y - tmpNode->getPosition()->y;
  495. if (dy!=dx) mightBePassable = false;
  496. }
  497. }
  498. if (dx == -dy) {
  499. mightBePassable = true;
  500. for (tmpNode = node->getPrevious(); tmpNode && tmpNode != anchor; tmpNode = tmpNode->getPrevious()) {
  501. dx = tmpNode->getNext()->getPosition()->x - tmpNode->getPosition()->x;
  502. dy = tmpNode->getNext()->getPosition()->y - tmpNode->getPosition()->y;
  503. if (dy!=-dx) mightBePassable = false;
  504. }
  505. }
  506. if (mightBePassable) {
  507. isPassable = true;
  508. }
  509. }
  510. if (isPassable)
  511. {
  512. // anchor can directly see this node, make it next in the optimized path
  513. anchor->setNextOptimized( node );
  514. anchor = node;
  515. optimizedSegment = true;
  516. break;
  517. }
  518. }
  519. if (optimizedSegment == false)
  520. {
  521. // for some reason, there is no clear LOS between the anchor node and the very next node
  522. anchor->setNextOptimized( anchor->getNext() );
  523. anchor = anchor->getNext();
  524. }
  525. }
  526. // the path has been optimized
  527. m_isOptimized = true;
  528. }
  529. /**
  530. * Optimize the path by checking line of sight
  531. */
  532. void Path::optimizeGroundPath( Bool crusher, Int pathDiameter )
  533. {
  534. PathNode *node, *anchor;
  535. // start with first node in the path
  536. anchor = getFirstNode();
  537. //
  538. // For each node in the path, check LOS from last node in path, working forward.
  539. // When a clear LOS is found, keep the resulting straight line segment.
  540. //
  541. while( anchor != getLastNode() )
  542. {
  543. // find the farthest node in the path that has a clear line-of-sight to this anchor
  544. Bool optimizedSegment = false;
  545. PathfindLayerEnum layer = anchor->getLayer();
  546. PathfindLayerEnum curLayer = anchor->getLayer();
  547. Int count = 0;
  548. const Int ALLOWED_STEPS = 3; // we can optimize 3 steps to or from a bridge. Otherwise, we need to insert a point. jba.
  549. for (node = anchor->getNext(); node->getNext(); node=node->getNext()) {
  550. count++;
  551. if (curLayer==LAYER_GROUND) {
  552. if (node->getLayer() != curLayer) {
  553. layer = node->getLayer();
  554. curLayer = layer;
  555. if (count > ALLOWED_STEPS) break;
  556. }
  557. } else {
  558. if (node->getNext()->getLayer() != curLayer) {
  559. if (count > ALLOWED_STEPS) break;
  560. }
  561. }
  562. curLayer = node->getLayer();
  563. }
  564. // find the farthest node in the path that has a clear line-of-sight to this anchor
  565. for( ; node != anchor; node = node->getPrevious() )
  566. {
  567. Bool isPassable = false;
  568. //CRCDEBUG_LOG(("Path::optimize() calling isLinePassable()\n"));
  569. if (TheAI->pathfinder()->isGroundPathPassable( crusher, *anchor->getPosition(), layer,
  570. *node->getPosition(), pathDiameter))
  571. {
  572. isPassable = true;
  573. }
  574. // Horizontal, diagonal, and vertical steps are passable.
  575. if (!isPassable) {
  576. Int dx = node->getPosition()->x - anchor->getPosition()->x;
  577. Int dy = node->getPosition()->y - anchor->getPosition()->y;
  578. Bool mightBePassable = false;
  579. PathNode *tmpNode;
  580. if (dx==0) {
  581. mightBePassable = true;
  582. for (tmpNode = node->getPrevious(); tmpNode && tmpNode != anchor; tmpNode = tmpNode->getPrevious()) {
  583. dx = tmpNode->getNext()->getPosition()->x - tmpNode->getPosition()->x;
  584. if (dx!=0) mightBePassable = false;
  585. }
  586. }
  587. if (dy==0) {
  588. mightBePassable = true;
  589. for (tmpNode = node->getPrevious(); tmpNode && tmpNode != anchor; tmpNode = tmpNode->getPrevious()) {
  590. dy = tmpNode->getNext()->getPosition()->y - tmpNode->getPosition()->y;
  591. if (dy!=0) mightBePassable = false;
  592. }
  593. }
  594. if (dx == dy) {
  595. mightBePassable = true;
  596. for (tmpNode = node->getPrevious(); tmpNode && tmpNode != anchor; tmpNode = tmpNode->getPrevious()) {
  597. dx = tmpNode->getNext()->getPosition()->x - tmpNode->getPosition()->x;
  598. dy = tmpNode->getNext()->getPosition()->y - tmpNode->getPosition()->y;
  599. if (dy!=dx) mightBePassable = false;
  600. }
  601. }
  602. if (dx == -dy) {
  603. mightBePassable = true;
  604. for (tmpNode = node->getPrevious(); tmpNode && tmpNode != anchor; tmpNode = tmpNode->getPrevious()) {
  605. dx = tmpNode->getNext()->getPosition()->x - tmpNode->getPosition()->x;
  606. dy = tmpNode->getNext()->getPosition()->y - tmpNode->getPosition()->y;
  607. if (dy!=-dx) mightBePassable = false;
  608. }
  609. }
  610. if (mightBePassable) {
  611. isPassable = true;
  612. }
  613. }
  614. if (isPassable)
  615. {
  616. // anchor can directly see this node, make it next in the optimized path
  617. anchor->setNextOptimized( node );
  618. anchor = node;
  619. optimizedSegment = true;
  620. break;
  621. }
  622. }
  623. if (optimizedSegment == false)
  624. {
  625. // for some reason, there is no clear LOS between the anchor node and the very next node
  626. anchor->setNextOptimized( anchor->getNext() );
  627. anchor = anchor->getNext();
  628. }
  629. }
  630. // Remove jig/jogs :) jba.
  631. for (anchor=getFirstNode(); anchor!=NULL; anchor=anchor->getNextOptimized()) {
  632. node = anchor->getNextOptimized();
  633. if (node && node->getNextOptimized()) {
  634. Real dx = node->getPosition()->x - anchor->getPosition()->x;
  635. Real dy = node->getPosition()->y - anchor->getPosition()->y;
  636. // If the x & y offsets are less than 2 pathfind cells, kill it.
  637. if (dx*dx+dy*dy < sqr(PATHFIND_CELL_SIZE_F)*3.9f) {
  638. anchor->setNextOptimized(node->getNextOptimized());
  639. }
  640. }
  641. }
  642. // the path has been optimized
  643. m_isOptimized = true;
  644. }
  645. inline Bool isReallyClose(const Coord3D& a, const Coord3D& b)
  646. {
  647. const Real CLOSE_ENOUGH = 0.1f;
  648. return
  649. fabs(a.x-b.x) <= CLOSE_ENOUGH &&
  650. fabs(a.y-b.y) <= CLOSE_ENOUGH &&
  651. fabs(a.z-b.z) <= CLOSE_ENOUGH;
  652. }
  653. /**
  654. * Given a location, return the closest position on the path.
  655. * If 'allowBacktrack' is true, the entire path is considered.
  656. * If it is false, the point computed cannot be prior to previously returned non-backtracking points on this path.
  657. * Because the path "knows" the direction of travel, it will "lead" the given position a bit
  658. * to ensure the path is followed in the inteded direction.
  659. *
  660. * Note: The path cleanup does not take into account rolling terrain, so we can end up with
  661. * these situations:
  662. *
  663. * B
  664. * ######
  665. * ##########
  666. * A-##----------##---C
  667. * #######################
  668. *
  669. *
  670. * When an agent gets to B, he seems far off of the path, but it really not.
  671. * There are similar problems with valleys.
  672. *
  673. * Since agents track the closest path, if a high hill gets close to the underside of
  674. * a bridge, an agent may 'jump' to the higher path. This must be avoided in maps.
  675. *
  676. * return along-path distance to the end will be returned as function result
  677. */
  678. void Path::computePointOnPath(
  679. const Object* obj,
  680. const LocomotorSet& locomotorSet,
  681. const Coord3D& pos,
  682. ClosestPointOnPathInfo& out
  683. )
  684. {
  685. CRCDEBUG_LOG(("Path::computePointOnPath() fzor %s\n", DescribeObject(obj).str()));
  686. out.layer = LAYER_GROUND;
  687. out.posOnPath.zero();
  688. out.distAlongPath = 0;
  689. if (m_path == NULL)
  690. {
  691. m_cpopValid = false;
  692. return;
  693. }
  694. out.layer = m_path->getLayer();
  695. if (m_cpopValid && m_cpopCountdown>0 && isReallyClose(pos, m_cpopIn))
  696. {
  697. out = m_cpopOut;
  698. m_cpopCountdown--;
  699. CRCDEBUG_LOG(("Path::computePointOnPath() end because we're really close\n"));
  700. return;
  701. }
  702. m_cpopCountdown = MAX_CPOP;
  703. // default pathPos to end of the path
  704. out.posOnPath = *getLastNode()->getPosition();
  705. const PathNode* closeNode = NULL;
  706. Coord2D toPos;
  707. Real closeDistSqr = 99999999.9f;
  708. Real totalPathLength = 0.0f;
  709. Real lengthAlongPathToPos = 0.0f;
  710. //
  711. // Find the closest segment of the path
  712. //
  713. #ifdef CPOP_STARTS_FROM_PREV_SEG
  714. const PathNode* prevNode = m_cpopRecentStart;
  715. if (prevNode == NULL)
  716. prevNode = m_path;
  717. #else
  718. const PathNode* prevNode = m_path;
  719. #endif
  720. Coord2D segmentDirNorm;
  721. Real segmentLength;
  722. // note that the seg dir and len returned by this is the dist & vec from 'prevNode' to 'node'
  723. for ( const PathNode* node = prevNode->getNextOptimized(&segmentDirNorm, &segmentLength);
  724. node != NULL;
  725. node = node->getNextOptimized(&segmentDirNorm, &segmentLength) )
  726. {
  727. const Coord3D* prevNodePos = prevNode->getPosition();
  728. const Coord3D* nodePos = node->getPosition();
  729. // compute vector from start of segment to pos
  730. toPos.x = pos.x - prevNodePos->x;
  731. toPos.y = pos.y - prevNodePos->y;
  732. // compute distance projection of 'toPos' onto segment
  733. Real alongPathDist = segmentDirNorm.x * toPos.x + segmentDirNorm.y * toPos.y;
  734. Coord3D pointOnPath;
  735. if (alongPathDist < 0.0f)
  736. {
  737. // projected point is before start of segment, use starting point
  738. alongPathDist = 0.0f;
  739. pointOnPath = *prevNodePos;
  740. }
  741. else if (alongPathDist > segmentLength)
  742. {
  743. // projected point is beyond end of segment, use end point
  744. if (node->getNextOptimized() == NULL)
  745. {
  746. alongPathDist = segmentLength;
  747. pointOnPath = *nodePos;
  748. }
  749. else
  750. {
  751. // beyond the end of this segment, skip this segment
  752. // if bend is sharp, start of next segment will grab this point
  753. // if bend is gradual, the point will project into the next segment
  754. totalPathLength += segmentLength;
  755. prevNode = node;
  756. continue;
  757. }
  758. }
  759. else
  760. {
  761. // projected point is on this segment, compute it
  762. pointOnPath.x = prevNodePos->x + alongPathDist * segmentDirNorm.x;
  763. pointOnPath.y = prevNodePos->y + alongPathDist * segmentDirNorm.y;
  764. pointOnPath.z = 0;
  765. }
  766. // compute distance to point on path, and track the closest we've found so far
  767. Coord2D offset;
  768. offset.x = pos.x - pointOnPath.x;
  769. offset.y = pos.y - pointOnPath.y;
  770. Real offsetDistSqr = offset.x*offset.x + offset.y*offset.y;
  771. if (offsetDistSqr < closeDistSqr)
  772. {
  773. closeDistSqr = offsetDistSqr;
  774. closeNode = prevNode;
  775. out.posOnPath = pointOnPath;
  776. lengthAlongPathToPos = totalPathLength + alongPathDist;
  777. }
  778. // add this segment's length to find total path length
  779. /// @todo Precompute this and store in path
  780. totalPathLength += segmentLength;
  781. prevNode = node;
  782. DUMPCOORD3D(&pointOnPath);
  783. }
  784. #ifdef CPOP_STARTS_FROM_PREV_SEG
  785. m_cpopRecentStart = closeNode;
  786. #endif
  787. //
  788. // Compute the goal movement position for this agent
  789. //
  790. if (closeNode && closeNode->getNextOptimized())
  791. {
  792. // note that the seg dir and len returned by this is the dist & vec from 'closeNode' to 'closeNext'
  793. const PathNode* closeNext = closeNode->getNextOptimized(&segmentDirNorm, &segmentLength);
  794. const Coord3D* nextNodePos = closeNext->getPosition();
  795. const Coord3D* closeNodePos = closeNode->getPosition();
  796. const PathNode* closePrev = closeNode->getPrevious();
  797. if (closePrev && closePrev->getLayer() > LAYER_GROUND)
  798. {
  799. out.layer = closeNode->getLayer();
  800. }
  801. if (closeNode->getLayer() > LAYER_GROUND)
  802. {
  803. out.layer = closeNode->getLayer();
  804. }
  805. if (closeNext->getLayer() > LAYER_GROUND)
  806. {
  807. out.layer = closeNext->getLayer();
  808. }
  809. // compute vector from start of segment to pos
  810. toPos.x = pos.x - closeNodePos->x;
  811. toPos.y = pos.y - closeNodePos->y;
  812. // compute distance projection of 'toPos' onto segment
  813. Real alongPathDist = segmentDirNorm.x * toPos.x + segmentDirNorm.y * toPos.y;
  814. // we know this is the closest segment, so don't allow farther back than the start node
  815. if (alongPathDist < 0.0f)
  816. alongPathDist = 0.0f;
  817. // compute distance of point from this path segment
  818. Real toDistSqr = sqr(toPos.x) + sqr(toPos.y);
  819. Real offsetDistSq = toDistSqr - sqr(alongPathDist);
  820. Real offsetDist = (offsetDistSq <= 0.0) ? 0.0 : sqrt(offsetDistSq);
  821. // If we are basically on the path, return the next path node as the movement goal.
  822. // However, the farther off the path we get, the movement goal becomes closer to our
  823. // projected position on the path. If we are very far off the path, we will move
  824. // directly towards the nearest point on the path, and not the next path node.
  825. const Real maxPathError = 3.0f * PATHFIND_CELL_SIZE_F;
  826. const Real maxPathErrorInv = 1.0 / maxPathError;
  827. Real k = offsetDist * maxPathErrorInv;
  828. if (k > 1.0f)
  829. k = 1.0f;
  830. Bool gotPos = false;
  831. CRCDEBUG_LOG(("Path::computePointOnPath() calling isLinePassable() 1\n"));
  832. if (TheAI->pathfinder()->isLinePassable( obj, locomotorSet.getValidSurfaces(), out.layer, pos, *nextNodePos,
  833. false, true ))
  834. {
  835. out.posOnPath = *nextNodePos;
  836. gotPos = true;
  837. Bool tryAhead = alongPathDist > segmentLength * 0.5;
  838. if (closeNext->getCanOptimize() == false)
  839. {
  840. tryAhead = false; // don't go past no-opt nodes.
  841. }
  842. if (closeNode->getLayer() != closeNext->getLayer())
  843. {
  844. tryAhead = false; // don't go past layers.
  845. }
  846. if (obj->getLayer()!=LAYER_GROUND) {
  847. tryAhead = false;
  848. }
  849. Bool veryClose = false;
  850. if (segmentLength-alongPathDist<1.0f) {
  851. tryAhead = true;
  852. veryClose = true;
  853. }
  854. if (tryAhead)
  855. {
  856. // try next segment middle.
  857. const PathNode *next = closeNext->getNextOptimized();
  858. if (next)
  859. {
  860. Coord3D tryPos;
  861. tryPos.x = (nextNodePos->x + next->getPosition()->x) * 0.5;
  862. tryPos.y = (nextNodePos->y + next->getPosition()->y) * 0.5;
  863. tryPos.z = nextNodePos->z;
  864. CRCDEBUG_LOG(("Path::computePointOnPath() calling isLinePassable() 2\n"));
  865. if (veryClose || TheAI->pathfinder()->isLinePassable( obj, locomotorSet.getValidSurfaces(), closeNext->getLayer(), pos, tryPos, false, true ))
  866. {
  867. gotPos = true;
  868. out.posOnPath = tryPos;
  869. }
  870. }
  871. }
  872. }
  873. else if (k > 0.5f)
  874. {
  875. Real tryDist = alongPathDist + (0.5) * (segmentLength - alongPathDist);
  876. // projected point is on this segment, compute it
  877. out.posOnPath.x = closeNodePos->x + tryDist * segmentDirNorm.x;
  878. out.posOnPath.y = closeNodePos->y + tryDist * segmentDirNorm.y;
  879. out.posOnPath.z = closeNodePos->z;
  880. CRCDEBUG_LOG(("Path::computePointOnPath() calling isLinePassable() 3\n"));
  881. if (TheAI->pathfinder()->isLinePassable( obj, locomotorSet.getValidSurfaces(), out.layer, pos, out.posOnPath, false, true ))
  882. {
  883. k = 0.5f;
  884. gotPos = true;
  885. }
  886. }
  887. // if we are on the path (k == 0), then alongPathDist == segmentLength
  888. // if we are way off the path (k == 1), then alongPathDist is unchanged, and it projection of actual pos
  889. alongPathDist += (1.0f - k) * (segmentLength - alongPathDist);
  890. if (!gotPos)
  891. {
  892. if (alongPathDist > segmentLength)
  893. {
  894. alongPathDist = segmentLength;
  895. out.posOnPath = *nextNodePos;
  896. }
  897. else
  898. {
  899. // projected point is on this segment, compute it
  900. out.posOnPath.x = closeNodePos->x + alongPathDist * segmentDirNorm.x;
  901. out.posOnPath.y = closeNodePos->y + alongPathDist * segmentDirNorm.y;
  902. out.posOnPath.z = closeNodePos->z;
  903. Real dx = fabs(pos.x - out.posOnPath.x);
  904. Real dy = fabs(pos.y - out.posOnPath.y);
  905. if (dx<1 && dy<1 && closeNode->getNextOptimized() && closeNode->getNextOptimized()->getNextOptimized()) {
  906. out.posOnPath = *closeNode->getNextOptimized()->getNextOptimized()->getPosition();
  907. }
  908. }
  909. }
  910. }
  911. TheAI->pathfinder()->setDebugPathPosition( &out.posOnPath );
  912. out.distAlongPath = totalPathLength - lengthAlongPathToPos;
  913. Coord3D delta;
  914. delta.x = out.posOnPath.x - pos.x;
  915. delta.y = out.posOnPath.y - pos.y;
  916. delta.z = 0;
  917. Real lenDelta = delta.length();
  918. if (lenDelta > out.distAlongPath && out.distAlongPath > PATHFIND_CLOSE_ENOUGH)
  919. {
  920. out.distAlongPath = lenDelta;
  921. }
  922. m_cpopIn = pos;
  923. m_cpopOut = out;
  924. m_cpopValid = true;
  925. CRCDEBUG_LOG(("Path::computePointOnPath() end\n"));
  926. }
  927. /**
  928. Given a position, computes the distance to the goal. Returns 0 if we are past the goal.
  929. Returns the goal position in goalPos. This is intended for use with flying paths, that go
  930. directly to the goal and don't consider obstacles. jba.
  931. */
  932. Real Path::computeFlightDistToGoal( const Coord3D *pos, Coord3D& goalPos )
  933. {
  934. if (m_path == NULL)
  935. {
  936. goalPos.x = 0.0f;
  937. goalPos.y = 0.0f;
  938. goalPos.z = 0.0f;
  939. return 0.0f;
  940. }
  941. const PathNode *curNode = getFirstNode();
  942. if (m_cpopRecentStart) {
  943. curNode = m_cpopRecentStart;
  944. } else {
  945. m_cpopRecentStart = curNode;
  946. }
  947. const PathNode *nextNode = curNode->getNextOptimized();
  948. goalPos = *curNode->getPosition();
  949. Real distance = 0;
  950. Bool useNext = true;
  951. while (nextNode) {
  952. if (useNext) {
  953. goalPos = *nextNode->getPosition();
  954. }
  955. Coord3D startPos = *curNode->getPosition();
  956. Coord3D endPos = *nextNode->getPosition();
  957. Coord2D posToGoalVector;
  958. // posToGoalVector is pos to goalPos vector.
  959. posToGoalVector.x = endPos.x - pos->x;
  960. posToGoalVector.y = endPos.y - pos->y;
  961. // pathVector is the startPos to goal pos vector.
  962. Coord2D pathVector;
  963. pathVector.x = endPos.x - startPos.x;
  964. pathVector.y = endPos.y - startPos.y;
  965. // Normalize pathVector
  966. pathVector.normalize();
  967. // Dot product is the posToGoal vector projected onto the path vector.
  968. Real dotProduct = posToGoalVector.x*pathVector.x + posToGoalVector.y*pathVector.y;
  969. if (dotProduct>=0) {
  970. distance += dotProduct;
  971. useNext = false;
  972. } else if (useNext) {
  973. m_cpopRecentStart = nextNode;
  974. }
  975. curNode = nextNode;
  976. nextNode = curNode->getNextOptimized();
  977. }
  978. return distance;
  979. }
  980. //-----------------------------------------------------------------------------------
  981. enum { PATHFIND_CELLS_PER_FRAME=5000}; // Number of cells we will search pathfinding per frame.
  982. enum {CELL_INFOS_TO_ALLOCATE = 30000};
  983. PathfindCellInfo *PathfindCellInfo::s_infoArray = NULL;
  984. PathfindCellInfo *PathfindCellInfo::s_firstFree = NULL;
  985. /**
  986. * Allocates a pool of pathfind cell infos.
  987. */
  988. void PathfindCellInfo::allocateCellInfos(void)
  989. {
  990. releaseCellInfos();
  991. s_infoArray = MSGNEW("PathfindCellInfo") PathfindCellInfo[CELL_INFOS_TO_ALLOCATE]; // pool[]ify
  992. s_infoArray[CELL_INFOS_TO_ALLOCATE-1].m_pathParent = NULL;
  993. s_infoArray[CELL_INFOS_TO_ALLOCATE-1].m_isFree = true;
  994. s_firstFree = s_infoArray;
  995. for (Int i=0; i<CELL_INFOS_TO_ALLOCATE-1; i++) {
  996. s_infoArray[i].m_pathParent = &s_infoArray[i+1];
  997. s_infoArray[i].m_isFree = true;
  998. }
  999. }
  1000. /**
  1001. * Releases a pool of pathfind cell infos.
  1002. */
  1003. void PathfindCellInfo::releaseCellInfos(void)
  1004. {
  1005. if (s_infoArray==NULL) {
  1006. return; // haven't allocated any yet.
  1007. }
  1008. Int count=0;
  1009. while (s_firstFree) {
  1010. count++;
  1011. DEBUG_ASSERTCRASH(s_firstFree->m_isFree, ("Should be freed."));
  1012. s_firstFree = s_firstFree->m_pathParent;
  1013. }
  1014. DEBUG_ASSERTCRASH(count==CELL_INFOS_TO_ALLOCATE, ("Error - Allocated cellinfos."));
  1015. delete s_infoArray;
  1016. s_infoArray = NULL;
  1017. s_firstFree = NULL;
  1018. }
  1019. /**
  1020. * Gets a pathfindcellinfo.
  1021. */
  1022. PathfindCellInfo *PathfindCellInfo::getACellInfo(PathfindCell *cell,const ICoord2D &pos)
  1023. {
  1024. PathfindCellInfo *info = s_firstFree;
  1025. if (s_firstFree) {
  1026. DEBUG_ASSERTCRASH(s_firstFree->m_isFree, ("Should be freed."));
  1027. s_firstFree = s_firstFree->m_pathParent;
  1028. info->m_isFree = false; // Just allocated it.
  1029. info->m_cell = cell;
  1030. info->m_pos = pos;
  1031. info->m_nextOpen = NULL;
  1032. info->m_prevOpen = NULL;
  1033. info->m_pathParent = NULL;
  1034. info->m_costSoFar = 0;
  1035. info->m_totalCost = 0;
  1036. info->m_open = 0;
  1037. info->m_closed = 0;
  1038. info->m_obstacleID = INVALID_ID;
  1039. info->m_goalUnitID = INVALID_ID;
  1040. info->m_posUnitID = INVALID_ID;
  1041. info->m_goalAircraftID = INVALID_ID;
  1042. info->m_obstacleIsFence = false;
  1043. info->m_obstacleIsTransparent = false;
  1044. info->m_blockedByAlly = false;
  1045. }
  1046. return info;
  1047. }
  1048. /**
  1049. * Returns a pathfindcellinfo.
  1050. */
  1051. void PathfindCellInfo::releaseACellInfo(PathfindCellInfo *theInfo)
  1052. {
  1053. DEBUG_ASSERTCRASH(!theInfo->m_isFree, ("Shouldn't be free."));
  1054. //@ todo -fix this assert on usa04. jba.
  1055. //DEBUG_ASSERTCRASH(theInfo->m_obstacleID==0, ("Shouldn't be obstacle."));
  1056. theInfo->m_pathParent = s_firstFree;
  1057. s_firstFree = theInfo;
  1058. s_firstFree->m_isFree = true;
  1059. }
  1060. //-----------------------------------------------------------------------------------
  1061. /**
  1062. * Constructor
  1063. */
  1064. PathfindCell::PathfindCell( void ) :m_info(NULL)
  1065. {
  1066. reset();
  1067. }
  1068. /**
  1069. * Destructor
  1070. */
  1071. PathfindCell::~PathfindCell( void )
  1072. {
  1073. if (m_info) PathfindCellInfo::releaseACellInfo(m_info);
  1074. m_info = NULL;
  1075. static warn = true;
  1076. if (warn) {
  1077. warn = false;
  1078. DEBUG_LOG( ("PathfindCell::~PathfindCell m_info Allocated."));
  1079. }
  1080. }
  1081. /**
  1082. * Reset the cell to default values
  1083. */
  1084. void PathfindCell::reset( )
  1085. {
  1086. m_type = PathfindCell::CELL_CLEAR;
  1087. m_flags = PathfindCell::NO_UNITS;
  1088. m_zone = 0;
  1089. m_aircraftGoal = false;
  1090. m_pinched = false;
  1091. if (m_info) {
  1092. m_info->m_obstacleID = INVALID_ID;
  1093. PathfindCellInfo::releaseACellInfo(m_info);
  1094. m_info = NULL;
  1095. }
  1096. m_connectsToLayer = LAYER_INVALID;
  1097. m_layer = LAYER_GROUND;
  1098. }
  1099. /**
  1100. * Reset the pathfinding values in the cell.
  1101. */
  1102. Bool PathfindCell::startPathfind( PathfindCell *goalCell )
  1103. {
  1104. DEBUG_ASSERTCRASH(m_info, ("Has to have info."));
  1105. m_info->m_nextOpen = NULL;
  1106. m_info->m_prevOpen = NULL;
  1107. m_info->m_pathParent = NULL;
  1108. m_info->m_costSoFar = 0; // start node, no cost to get here
  1109. m_info->m_totalCost = 0;
  1110. if (goalCell) {
  1111. m_info->m_totalCost = costToGoal( goalCell );
  1112. }
  1113. m_info->m_open = TRUE;
  1114. m_info->m_closed = FALSE;
  1115. return true;
  1116. }
  1117. /**
  1118. * Set the parent pointer.
  1119. */
  1120. void PathfindCell::setParentCell( PathfindCell* parent )
  1121. {
  1122. DEBUG_ASSERTCRASH(m_info, ("Has to have info."));
  1123. m_info->m_pathParent = parent->m_info;
  1124. Int dx = m_info->m_pos.x - parent->m_info->m_pos.x;
  1125. Int dy = m_info->m_pos.y - parent->m_info->m_pos.y;
  1126. if (dx<-1 || dx>1 || dy<-1 || dy>1) {
  1127. DEBUG_CRASH(("Invalid parent index."));
  1128. }
  1129. }
  1130. /**
  1131. * Set the parent pointer.
  1132. */
  1133. void PathfindCell::setParentCellHierarchical( PathfindCell* parent )
  1134. {
  1135. DEBUG_ASSERTCRASH(m_info, ("Has to have info."));
  1136. m_info->m_pathParent = parent->m_info;
  1137. }
  1138. /**
  1139. * Reset the parent cell.
  1140. */
  1141. void PathfindCell::clearParentCell( void )
  1142. {
  1143. DEBUG_ASSERTCRASH(m_info, ("Has to have info."));
  1144. m_info->m_pathParent = NULL;
  1145. }
  1146. /**
  1147. * Allocates an info record for a cell.
  1148. */
  1149. Bool PathfindCell::allocateInfo( const ICoord2D &pos )
  1150. {
  1151. if (!m_info) {
  1152. m_info = PathfindCellInfo::getACellInfo(this, pos);
  1153. return (m_info != NULL);
  1154. }
  1155. return true;
  1156. }
  1157. /**
  1158. * Releases an info record for a cell.
  1159. */
  1160. void PathfindCell::releaseInfo( void )
  1161. {
  1162. if (m_type==PathfindCell::CELL_OBSTACLE) {
  1163. return;
  1164. }
  1165. if (m_flags!=NO_UNITS) {
  1166. return;
  1167. }
  1168. if (m_aircraftGoal) {
  1169. return;
  1170. }
  1171. if (m_info) {
  1172. DEBUG_ASSERTCRASH(m_info->m_prevOpen==NULL && m_info->m_nextOpen==NULL, ("Shouldn't be linked."));
  1173. DEBUG_ASSERTCRASH(m_info->m_open==NULL && m_info->m_closed==NULL, ("Shouldn't be linked."));
  1174. DEBUG_ASSERTCRASH(m_info->m_goalUnitID==INVALID_ID && m_info->m_posUnitID==INVALID_ID, ("Shouldn't be occupied."));
  1175. DEBUG_ASSERTCRASH(m_info->m_goalAircraftID==INVALID_ID , ("Shouldn't be occupied by aircraft."));
  1176. if (m_info->m_prevOpen || m_info->m_nextOpen || m_info->m_open || m_info->m_closed) {
  1177. // Bad release. Skip for now, better leak than crash. jba.
  1178. return;
  1179. }
  1180. PathfindCellInfo::releaseACellInfo(m_info);
  1181. m_info = NULL;
  1182. }
  1183. }
  1184. /**
  1185. * Sets the goal unit into the info record for a cell.
  1186. */
  1187. void PathfindCell::setGoalUnit(ObjectID unitID, const ICoord2D &pos )
  1188. {
  1189. if (unitID==INVALID_ID) {
  1190. // removing goal.
  1191. if (m_info) {
  1192. m_info->m_goalUnitID = INVALID_ID;
  1193. if (m_info->m_posUnitID == INVALID_ID) {
  1194. // No units here.
  1195. DEBUG_ASSERTCRASH(m_flags==UNIT_GOAL, ("Bad flags."));
  1196. m_flags = NO_UNITS;
  1197. releaseInfo();
  1198. } else{
  1199. m_flags = UNIT_PRESENT_MOVING;
  1200. }
  1201. } else {
  1202. DEBUG_ASSERTCRASH(m_flags == NO_UNITS, ("Bad flags."));
  1203. }
  1204. } else {
  1205. // adding goal.
  1206. if (!m_info) {
  1207. DEBUG_ASSERTCRASH(m_flags == NO_UNITS, ("Bad flags."));
  1208. allocateInfo(pos);
  1209. }
  1210. if (!m_info) {
  1211. DEBUG_CRASH(("Ran out of pathfind cells - fatal error!!!!! jba. "));
  1212. return;
  1213. }
  1214. m_info->m_goalUnitID = unitID;
  1215. if (unitID==m_info->m_posUnitID) {
  1216. m_flags = UNIT_PRESENT_FIXED;
  1217. } else if (m_info->m_posUnitID==INVALID_ID) {
  1218. m_flags = UNIT_GOAL;
  1219. } else {
  1220. m_flags = UNIT_GOAL_OTHER_MOVING;
  1221. }
  1222. }
  1223. }
  1224. /**
  1225. * Sets the goal aircraft into the info record for a cell.
  1226. */
  1227. void PathfindCell::setGoalAircraft(ObjectID unitID, const ICoord2D &pos )
  1228. {
  1229. if (unitID==INVALID_ID) {
  1230. // removing goal.
  1231. if (m_info) {
  1232. m_info->m_goalAircraftID = INVALID_ID;
  1233. m_aircraftGoal = false;
  1234. releaseInfo();
  1235. } else {
  1236. DEBUG_ASSERTCRASH(m_aircraftGoal==false, ("Bad flags."));
  1237. }
  1238. } else {
  1239. // adding goal.
  1240. if (!m_info) {
  1241. DEBUG_ASSERTCRASH(m_aircraftGoal==false, ("Bad flags."));
  1242. allocateInfo(pos);
  1243. }
  1244. if (!m_info) {
  1245. DEBUG_CRASH(("Ran out of pathfind cells - fatal error!!!!! jba. "));
  1246. return;
  1247. }
  1248. m_info->m_goalAircraftID = unitID;
  1249. m_aircraftGoal = true;
  1250. }
  1251. }
  1252. /**
  1253. * Sets the position unit into the info record for a cell.
  1254. */
  1255. void PathfindCell::setPosUnit(ObjectID unitID, const ICoord2D &pos )
  1256. {
  1257. if (unitID==INVALID_ID) {
  1258. // removing position.
  1259. if (m_info) {
  1260. m_info->m_posUnitID = INVALID_ID;
  1261. if (m_info->m_goalUnitID == INVALID_ID) {
  1262. // No units here.
  1263. DEBUG_ASSERTCRASH(m_flags==UNIT_PRESENT_MOVING, ("Bad flags."));
  1264. m_flags = NO_UNITS;
  1265. releaseInfo();
  1266. } else {
  1267. m_flags = UNIT_GOAL;
  1268. }
  1269. } else {
  1270. DEBUG_ASSERTCRASH(m_flags == NO_UNITS, ("Bad flags."));
  1271. }
  1272. } else {
  1273. // adding goal.
  1274. if (!m_info) {
  1275. DEBUG_ASSERTCRASH(m_flags == NO_UNITS, ("Bad flags."));
  1276. allocateInfo(pos);
  1277. }
  1278. if (!m_info) {
  1279. DEBUG_CRASH(("Ran out of pathfind cells - fatal error!!!!! jba. "));
  1280. return;
  1281. }
  1282. if (m_info->m_goalUnitID!=INVALID_ID && (m_info->m_goalUnitID==m_info->m_posUnitID)) {
  1283. // A unit is already occupying this cell.
  1284. return;
  1285. }
  1286. m_info->m_posUnitID = unitID;
  1287. if (unitID==m_info->m_goalUnitID) {
  1288. m_flags = UNIT_PRESENT_FIXED;
  1289. } else if (m_info->m_goalUnitID==INVALID_ID) {
  1290. m_flags = UNIT_PRESENT_MOVING;
  1291. } else {
  1292. m_flags = UNIT_GOAL_OTHER_MOVING;
  1293. }
  1294. }
  1295. }
  1296. /**
  1297. * Flag this cell as an obstacle, from the given one.
  1298. * Return true if cell was flagged.
  1299. */
  1300. Bool PathfindCell::setTypeAsObstacle( Object *obstacle, Bool isFence, const ICoord2D &pos )
  1301. {
  1302. if (m_type!=PathfindCell::CELL_CLEAR && m_type != PathfindCell::CELL_IMPASSABLE) {
  1303. return false;
  1304. }
  1305. Bool isRubble = false;
  1306. if (obstacle->getBodyModule() && obstacle->getBodyModule()->getDamageState() == BODY_RUBBLE)
  1307. {
  1308. isRubble = true;
  1309. }
  1310. if (isRubble) {
  1311. m_type = PathfindCell::CELL_RUBBLE;
  1312. if (m_info) {
  1313. m_info->m_obstacleID = INVALID_ID;
  1314. releaseInfo();
  1315. }
  1316. return true;
  1317. }
  1318. m_type = PathfindCell::CELL_OBSTACLE ;
  1319. if (!m_info) {
  1320. m_info = PathfindCellInfo::getACellInfo(this, pos);
  1321. if (!m_info) {
  1322. DEBUG_CRASH(("Not enough PathFindCellInfos in pool."));
  1323. return false;
  1324. }
  1325. }
  1326. m_info->m_obstacleID = obstacle->getID();
  1327. m_info->m_obstacleIsFence = isFence;
  1328. m_info->m_obstacleIsTransparent = obstacle->isKindOf(KINDOF_CAN_SEE_THROUGH_STRUCTURE);
  1329. return true;
  1330. }
  1331. /**
  1332. * Flag this cell as given type.
  1333. */
  1334. void PathfindCell::setType( CellType type )
  1335. {
  1336. if (m_info && (m_info->m_obstacleID != INVALID_ID)) {
  1337. DEBUG_ASSERTCRASH(type==PathfindCell::CELL_OBSTACLE, ("Wrong type."));
  1338. m_type = PathfindCell::CELL_OBSTACLE;
  1339. return;
  1340. }
  1341. m_type = type;
  1342. }
  1343. /**
  1344. * Unflag this cell as an obstacle, from the given one.
  1345. * Return true if this cell was previously flagged as an obstacle by this object.
  1346. */
  1347. Bool PathfindCell::removeObstacle( Object *obstacle )
  1348. {
  1349. if (m_type == PathfindCell::CELL_RUBBLE) {
  1350. m_type = PathfindCell::CELL_CLEAR;
  1351. }
  1352. if (!m_info) return false;
  1353. if (m_info->m_obstacleID != obstacle->getID()) return false;
  1354. m_type = PathfindCell::CELL_CLEAR;
  1355. m_info->m_obstacleID = INVALID_ID;
  1356. releaseInfo();
  1357. return true;
  1358. }
  1359. /// put self on "open" list in ascending cost order, return new list
  1360. PathfindCell *PathfindCell::putOnSortedOpenList( PathfindCell *list )
  1361. {
  1362. DEBUG_ASSERTCRASH(m_info, ("Has to have info."));
  1363. DEBUG_ASSERTCRASH(m_info->m_closed==FALSE && m_info->m_open==FALSE, ("Serious error - Invalid flags. jba"));
  1364. if (list == NULL)
  1365. {
  1366. list = this;
  1367. m_info->m_prevOpen = NULL;
  1368. m_info->m_nextOpen = NULL;
  1369. }
  1370. else
  1371. {
  1372. // insertion sort
  1373. PathfindCell *c, *lastCell = NULL;
  1374. for( c = list; c; c = c->getNextOpen() )
  1375. {
  1376. if (c->m_info->m_totalCost > m_info->m_totalCost)
  1377. break;
  1378. lastCell = c;
  1379. }
  1380. if (c)
  1381. {
  1382. // insert just before "c"
  1383. if (c->m_info->m_prevOpen)
  1384. c->m_info->m_prevOpen->m_nextOpen = this->m_info;
  1385. else
  1386. list = this;
  1387. m_info->m_prevOpen = c->m_info->m_prevOpen;
  1388. c->m_info->m_prevOpen = this->m_info;
  1389. m_info->m_nextOpen = c->m_info;
  1390. }
  1391. else
  1392. {
  1393. // append after "lastCell" - end of list
  1394. lastCell->m_info->m_nextOpen = this->m_info;
  1395. m_info->m_prevOpen = lastCell->m_info;
  1396. m_info->m_nextOpen = NULL;
  1397. }
  1398. }
  1399. // mark newCell as being on open list
  1400. m_info->m_open = true;
  1401. m_info->m_closed = false;
  1402. return list;
  1403. }
  1404. /// remove self from "open" list
  1405. PathfindCell *PathfindCell::removeFromOpenList( PathfindCell *list )
  1406. {
  1407. DEBUG_ASSERTCRASH(m_info, ("Has to have info."));
  1408. DEBUG_ASSERTCRASH(m_info->m_closed==FALSE && m_info->m_open==TRUE, ("Serious error - Invalid flags. jba"));
  1409. if (m_info->m_nextOpen)
  1410. m_info->m_nextOpen->m_prevOpen = m_info->m_prevOpen;
  1411. if (m_info->m_prevOpen)
  1412. m_info->m_prevOpen->m_nextOpen = m_info->m_nextOpen;
  1413. else
  1414. list = getNextOpen();
  1415. m_info->m_open = false;
  1416. m_info->m_nextOpen = NULL;
  1417. m_info->m_prevOpen = NULL;
  1418. return list;
  1419. }
  1420. /// remove all cells from "open" list
  1421. Int PathfindCell::releaseOpenList( PathfindCell *list )
  1422. {
  1423. Int count = 0;
  1424. while (list) {
  1425. count++;
  1426. DEBUG_ASSERTCRASH(list->m_info, ("Has to have info."));
  1427. DEBUG_ASSERTCRASH(list->m_info->m_closed==FALSE && list->m_info->m_open==TRUE, ("Serious error - Invalid flags. jba"));
  1428. PathfindCell *cur = list;
  1429. PathfindCellInfo *curInfo = list->m_info;
  1430. if (curInfo->m_nextOpen) {
  1431. list = curInfo->m_nextOpen->m_cell;
  1432. } else {
  1433. list = NULL;
  1434. }
  1435. DEBUG_ASSERTCRASH(cur == curInfo->m_cell, ("Bad backpointer in PathfindCellInfo"));
  1436. curInfo->m_nextOpen = NULL;
  1437. curInfo->m_prevOpen = NULL;
  1438. curInfo->m_open = FALSE;
  1439. cur->releaseInfo();
  1440. }
  1441. return count;
  1442. }
  1443. /// remove all cells from "closed" list
  1444. Int PathfindCell::releaseClosedList( PathfindCell *list )
  1445. {
  1446. Int count = 0;
  1447. while (list) {
  1448. count++;
  1449. DEBUG_ASSERTCRASH(list->m_info, ("Has to have info."));
  1450. DEBUG_ASSERTCRASH(list->m_info->m_closed==TRUE && list->m_info->m_open==FALSE, ("Serious error - Invalid flags. jba"));
  1451. PathfindCell *cur = list;
  1452. PathfindCellInfo *curInfo = list->m_info;
  1453. if (curInfo->m_nextOpen) {
  1454. list = curInfo->m_nextOpen->m_cell;
  1455. } else {
  1456. list = NULL;
  1457. }
  1458. DEBUG_ASSERTCRASH(cur == curInfo->m_cell, ("Bad backpointer in PathfindCellInfo"));
  1459. curInfo->m_nextOpen = NULL;
  1460. curInfo->m_prevOpen = NULL;
  1461. curInfo->m_closed = FALSE;
  1462. cur->releaseInfo();
  1463. }
  1464. return count;
  1465. }
  1466. /// put self on "closed" list, return new list
  1467. PathfindCell *PathfindCell::putOnClosedList( PathfindCell *list )
  1468. {
  1469. DEBUG_ASSERTCRASH(m_info, ("Has to have info."));
  1470. DEBUG_ASSERTCRASH(m_info->m_closed==FALSE && m_info->m_open==FALSE, ("Serious error - Invalid flags. jba"));
  1471. // only put on list if not already on it
  1472. if (m_info->m_closed == FALSE)
  1473. {
  1474. m_info->m_closed = FALSE;
  1475. m_info->m_closed = TRUE;
  1476. m_info->m_prevOpen = NULL;
  1477. m_info->m_nextOpen = list?list->m_info:NULL;
  1478. if (list)
  1479. list->m_info->m_prevOpen = this->m_info;
  1480. list = this;
  1481. }
  1482. return list;
  1483. }
  1484. /// remove self from "closed" list
  1485. PathfindCell *PathfindCell::removeFromClosedList( PathfindCell *list )
  1486. {
  1487. DEBUG_ASSERTCRASH(m_info, ("Has to have info."));
  1488. DEBUG_ASSERTCRASH(m_info->m_closed==TRUE && m_info->m_open==FALSE, ("Serious error - Invalid flags. jba"));
  1489. if (m_info->m_nextOpen)
  1490. m_info->m_nextOpen->m_prevOpen = m_info->m_prevOpen;
  1491. if (m_info->m_prevOpen)
  1492. m_info->m_prevOpen->m_nextOpen = m_info->m_nextOpen;
  1493. else
  1494. list = getNextOpen();
  1495. m_info->m_closed = false;
  1496. m_info->m_nextOpen = NULL;
  1497. m_info->m_prevOpen = NULL;
  1498. return list;
  1499. }
  1500. const Int COST_ORTHOGONAL = 10;
  1501. const Int COST_DIAGONAL = 14;
  1502. const Real COST_TO_DISTANCE_FACTOR = 1.0f/10.0f;
  1503. const Real COST_TO_DISTANCE_FACTOR_SQR = COST_TO_DISTANCE_FACTOR*COST_TO_DISTANCE_FACTOR;
  1504. UnsignedInt PathfindCell::costToGoal( PathfindCell *goal )
  1505. {
  1506. DEBUG_ASSERTCRASH(m_info, ("Has to have info."));
  1507. Int dx = m_info->m_pos.x - goal->getXIndex();
  1508. Int dy = m_info->m_pos.y - goal->getYIndex();
  1509. #define NO_REAL_DIST
  1510. #ifdef REAL_DIST
  1511. Int cost = COST_ORTHOGONAL*sqrt(dx*dx + dy*dy);
  1512. #else
  1513. if (dx<0) dx = -dx;
  1514. if (dy<0) dy = -dy;
  1515. Int cost;
  1516. if (dx>dy) {
  1517. cost= COST_ORTHOGONAL*dx + (COST_ORTHOGONAL*dy)/2;
  1518. } else {
  1519. cost= COST_ORTHOGONAL*dy + (COST_ORTHOGONAL*dx)/2;
  1520. }
  1521. #endif
  1522. return cost;
  1523. }
  1524. UnsignedInt PathfindCell::costToHierGoal( PathfindCell *goal )
  1525. {
  1526. if( !m_info )
  1527. {
  1528. DEBUG_CRASH( ("Has to have info.") );
  1529. return 100000; //...patch hack 1.01
  1530. }
  1531. Int dx = m_info->m_pos.x - goal->getXIndex();
  1532. Int dy = m_info->m_pos.y - goal->getYIndex();
  1533. Int cost = REAL_TO_INT_FLOOR(COST_ORTHOGONAL*sqrt(dx*dx + dy*dy) + 0.5f);
  1534. return cost;
  1535. }
  1536. UnsignedInt PathfindCell::costSoFar( PathfindCell *parent )
  1537. {
  1538. DEBUG_ASSERTCRASH(m_info, ("Has to have info."));
  1539. // very first node in path - no turns, no cost
  1540. if (parent == NULL)
  1541. return 0;
  1542. // add in number of turns in path so far
  1543. ICoord2D prevDir;
  1544. Int cost;
  1545. prevDir.x = parent->getXIndex() - m_info->m_pos.x;
  1546. prevDir.y = parent->getYIndex() - m_info->m_pos.y;
  1547. // diagonal moves cost a bit more than orthogonal ones
  1548. if (prevDir.x == 0 || prevDir.y == 0)
  1549. cost = parent->getCostSoFar() + COST_ORTHOGONAL;
  1550. else
  1551. cost = parent->getCostSoFar() + COST_DIAGONAL;
  1552. if (getPinched()) {
  1553. cost += 1*COST_DIAGONAL;
  1554. }
  1555. #if 1
  1556. // Increase cost of turns.
  1557. Int numTurns = 0;
  1558. PathfindCell *prevCell = parent->getParentCell();
  1559. if (prevCell) {
  1560. ICoord2D dir;
  1561. dir.x = prevCell->getXIndex() - parent->getXIndex();
  1562. dir.y = prevCell->getYIndex() - parent->getYIndex();
  1563. // count number of direction changes
  1564. if (dir.x != prevDir.x || dir.y != prevDir.y)
  1565. {
  1566. Int dot = dir.x * prevDir.x + dir.y * prevDir.y;
  1567. if (dot > 0)
  1568. numTurns=4; // 45 degree turn
  1569. else if (dot == 0)
  1570. numTurns = 8; // 90 degree turn
  1571. else
  1572. numTurns = 16; // 135 degree turn
  1573. }
  1574. }
  1575. return cost + numTurns;
  1576. #else
  1577. return cost;
  1578. #endif
  1579. }
  1580. inline Bool typesMatch(const PathfindCell &targetCell, const PathfindCell &sourceCell) {
  1581. PathfindCell::CellType targetType = targetCell.getType();
  1582. PathfindCell::CellType srcType = sourceCell.getType();
  1583. if (targetType == srcType) return true;
  1584. return false;
  1585. }
  1586. inline Bool waterGround(const PathfindCell &targetCell, const PathfindCell &sourceCell) {
  1587. PathfindCell::CellType targetType = targetCell.getType();
  1588. PathfindCell::CellType srcType = sourceCell.getType();
  1589. if ( (targetType==PathfindCell::CELL_CLEAR &&
  1590. (srcType&PathfindCell::CELL_WATER ))) {
  1591. return true;
  1592. }
  1593. if ( (srcType==PathfindCell::CELL_CLEAR &&
  1594. (targetType&PathfindCell::CELL_WATER ))) {
  1595. return true;
  1596. }
  1597. return false;
  1598. }
  1599. inline Bool groundRubble(const PathfindCell &targetCell, const PathfindCell &sourceCell) {
  1600. PathfindCell::CellType targetType = targetCell.getType();
  1601. PathfindCell::CellType srcType = sourceCell.getType();
  1602. if ( (targetType==PathfindCell::CELL_CLEAR &&
  1603. (srcType==PathfindCell::CELL_RUBBLE ))) {
  1604. return true;
  1605. }
  1606. if ( (srcType==PathfindCell::CELL_CLEAR &&
  1607. (targetType==PathfindCell::CELL_RUBBLE ))) {
  1608. return true;
  1609. }
  1610. return false;
  1611. }
  1612. inline Bool terrain(const PathfindCell &targetCell, const PathfindCell &sourceCell) {
  1613. Int targetType = targetCell.getType();
  1614. Int srcType = sourceCell.getType();
  1615. if (targetType == PathfindCell::CELL_OBSTACLE) targetType = PathfindCell::CELL_CLEAR;
  1616. if (srcType == PathfindCell::CELL_OBSTACLE) srcType = PathfindCell::CELL_CLEAR;
  1617. if (targetType==srcType) {
  1618. return true;
  1619. }
  1620. return false;
  1621. }
  1622. inline Bool crusherGround(const PathfindCell &targetCell, const PathfindCell &sourceCell) {
  1623. Int targetType = targetCell.getType();
  1624. Int srcType = sourceCell.getType();
  1625. if (targetType==PathfindCell::CELL_OBSTACLE) {
  1626. if (targetCell.isObstacleFence()) {
  1627. if (srcType == PathfindCell::CELL_CLEAR) {
  1628. return true;
  1629. }
  1630. }
  1631. }
  1632. if (srcType==PathfindCell::CELL_OBSTACLE) {
  1633. if (sourceCell.isObstacleFence()) {
  1634. if (targetType == PathfindCell::CELL_CLEAR) {
  1635. return true;
  1636. }
  1637. }
  1638. }
  1639. return false;
  1640. }
  1641. inline Bool groundCliff(const PathfindCell &targetCell, const PathfindCell &sourceCell) {
  1642. PathfindCell::CellType targetType = targetCell.getType();
  1643. PathfindCell::CellType srcType = sourceCell.getType();
  1644. if ( (targetType==PathfindCell::CELL_CLIFF ) &&
  1645. (srcType==PathfindCell::CELL_CLEAR) ) {
  1646. return true;
  1647. }
  1648. if ( (targetType==PathfindCell::CELL_CLEAR ) &&
  1649. (srcType==PathfindCell::CELL_CLIFF) ) {
  1650. return true;
  1651. }
  1652. return false;
  1653. }
  1654. static void __fastcall resolveBlockZones(Int srcZone, Int targetZone, zoneStorageType *zoneEquivalency, Int sizeOfZE)
  1655. {
  1656. Int i;
  1657. // We have two zones being combined now. Keep the lower zone.
  1658. DEBUG_ASSERTCRASH(srcZone!=0 && targetZone!=0, ("Bad resolve zones ."));
  1659. if (targetZone<srcZone) {
  1660. for (i=0; i<sizeOfZE; i++) {
  1661. if (zoneEquivalency[i] == srcZone) {
  1662. zoneEquivalency[i] = targetZone;
  1663. }
  1664. }
  1665. } else {
  1666. for (i=0; i<sizeOfZE; i++) {
  1667. if (zoneEquivalency[i] == targetZone) {
  1668. zoneEquivalency[i] = srcZone;
  1669. }
  1670. }
  1671. }
  1672. }
  1673. static void __fastcall resolveZones(Int srcZone, Int targetZone, zoneStorageType *zoneEquivalency, Int sizeOfZE)
  1674. {
  1675. Int i;
  1676. // We have two zones being combined now. Keep the lower zone.
  1677. DEBUG_ASSERTCRASH(srcZone!=0 && targetZone!=0, ("Bad resolve zones ."));
  1678. DEBUG_ASSERTCRASH(srcZone<sizeOfZE && targetZone<sizeOfZE, ("Bad resolve zones ."));
  1679. srcZone = zoneEquivalency[srcZone];
  1680. targetZone = zoneEquivalency[targetZone];
  1681. DEBUG_ASSERTCRASH(srcZone<sizeOfZE && targetZone<sizeOfZE, ("Bad resolve zones ."));
  1682. zoneStorageType finalZone;
  1683. if (targetZone<srcZone) {
  1684. finalZone = zoneEquivalency[targetZone];
  1685. } else {
  1686. finalZone = zoneEquivalency[srcZone];
  1687. }
  1688. DEBUG_ASSERTCRASH(finalZone<sizeOfZE , ("Bad resolve zones ."));
  1689. for (i=0; i<sizeOfZE; i++) {
  1690. zoneStorageType ze = zoneEquivalency[i];
  1691. if (ze == targetZone || ze == srcZone) {
  1692. zoneEquivalency[i] = finalZone;
  1693. }
  1694. }
  1695. }
  1696. static void flattenZones(zoneStorageType *zoneArray, zoneStorageType *zoneHierarchical, Int sizeOfZones)
  1697. {
  1698. Int i;
  1699. for (i=0; i<sizeOfZones; i++) {
  1700. Int zone1 = zoneArray[i];
  1701. Int zone2 = zoneHierarchical[zone1];
  1702. zone1 = zoneArray[zone2];
  1703. zone2 = zoneHierarchical[zone1];
  1704. zoneArray[i] = zone2;
  1705. }
  1706. #if 1
  1707. for (i=0; i<sizeOfZones; i++) {
  1708. Int zone1 = zoneArray[i];
  1709. Int zone2 = zoneHierarchical[i];
  1710. if (zone1!=zone2) {
  1711. resolveZones(zone1, zone2, zoneArray, sizeOfZones);
  1712. }
  1713. }
  1714. #endif
  1715. }
  1716. inline void applyZone(PathfindCell &targetCell, const PathfindCell &sourceCell, zoneStorageType *zoneEquivalency, Int sizeOfZE)
  1717. {
  1718. DEBUG_ASSERTCRASH(sourceCell.getZone()!=0, ("Unset source zone."));
  1719. Int srcZone = zoneEquivalency[sourceCell.getZone()];
  1720. //DEBUG_ASSERTCRASH(srcZone!=0, ("Bad zone equivalency zone."));
  1721. Int targetZone = zoneEquivalency[targetCell.getZone()];
  1722. if (targetZone == 0) {
  1723. targetCell.setZone(srcZone);
  1724. return;
  1725. }
  1726. if (targetZone == srcZone) {
  1727. return; // already match.
  1728. }
  1729. resolveZones(srcZone, targetZone, zoneEquivalency, sizeOfZE);
  1730. }
  1731. inline void applyBlockZone(PathfindCell &targetCell, const PathfindCell &sourceCell,
  1732. zoneStorageType *zoneEquivalency, Int firstZone, Int sizeOfZE)
  1733. {
  1734. DEBUG_ASSERTCRASH(sourceCell.getZone()>=firstZone && sourceCell.getZone()<firstZone+sizeOfZE, ("Memory overrun - FATAL ERROR."));
  1735. Int srcZone = zoneEquivalency[sourceCell.getZone()-firstZone];
  1736. DEBUG_ASSERTCRASH(targetCell.getZone()>=firstZone && sourceCell.getZone()<firstZone+sizeOfZE, ("Memory overrun - FATAL ERROR."));
  1737. Int targetZone = zoneEquivalency[targetCell.getZone()-firstZone];
  1738. if (targetZone == srcZone) {
  1739. return; // already match.
  1740. }
  1741. resolveBlockZones(srcZone, targetZone, zoneEquivalency, sizeOfZE);
  1742. }
  1743. //------------------------ ZoneBlock -------------------------------
  1744. ZoneBlock::ZoneBlock() : m_firstZone(0),
  1745. m_numZones(0),
  1746. m_groundCliffZones(NULL),
  1747. m_groundWaterZones(NULL),
  1748. m_groundRubbleZones(NULL),
  1749. m_crusherZones(NULL),
  1750. m_zonesAllocated(0),
  1751. m_interactsWithBridge(FALSE)
  1752. {
  1753. m_cellOrigin.x = 0;
  1754. m_cellOrigin.y = 0;
  1755. //Added By Sadullah Nader
  1756. //Initialization(s) inserted
  1757. m_firstZone = 0;
  1758. m_markedPassable = TRUE;
  1759. //
  1760. }
  1761. ZoneBlock::~ZoneBlock()
  1762. {
  1763. freeZones();
  1764. }
  1765. void ZoneBlock::freeZones(void)
  1766. {
  1767. if (m_groundCliffZones) {
  1768. delete [] m_groundCliffZones;
  1769. m_groundCliffZones = NULL;
  1770. }
  1771. if (m_groundWaterZones) {
  1772. delete [] m_groundWaterZones;
  1773. m_groundWaterZones = NULL;
  1774. }
  1775. if (m_groundRubbleZones) {
  1776. delete [] m_groundRubbleZones;
  1777. m_groundRubbleZones = NULL;
  1778. }
  1779. if (m_crusherZones) {
  1780. delete [] m_crusherZones;
  1781. m_crusherZones = NULL;
  1782. }
  1783. }
  1784. /* Allocate zone equivalency arrays large enough to hold required entries. If the arrays are already
  1785. large enough, reuse. Then calculate terrain equivalencies. */
  1786. void ZoneBlock::blockCalculateZones(PathfindCell **map, PathfindLayer layers[], const IRegion2D &bounds)
  1787. {
  1788. Int i, j;
  1789. m_cellOrigin = bounds.lo;
  1790. UnsignedInt minZone = map[bounds.lo.x][bounds.lo.y].getZone();
  1791. UnsignedInt maxZone = minZone;
  1792. for( j=bounds.lo.y; j<=bounds.hi.y; j++ ) {
  1793. for( i=bounds.lo.x; i<=bounds.hi.x; i++ ) {
  1794. PathfindCell *cell = &map[i][j];
  1795. zoneStorageType zone = cell->getZone();
  1796. if (minZone>zone) minZone=zone;
  1797. if (maxZone<zone) maxZone=zone;
  1798. }
  1799. }
  1800. m_firstZone = minZone;
  1801. m_numZones = 1 + maxZone - minZone;
  1802. allocateZones();
  1803. if (m_numZones==1) return; // all zones are equivalent.
  1804. // Determine water/ground equivalent zones, and ground/cliff equivalent zones.
  1805. for (i=0; i<m_zonesAllocated; i++) {
  1806. m_groundCliffZones[i] = i+m_firstZone;
  1807. m_groundWaterZones[i] = i+m_firstZone;
  1808. m_groundRubbleZones[i] = i+m_firstZone;
  1809. m_crusherZones[i] = i+m_firstZone;
  1810. }
  1811. for( j=bounds.lo.y; j<=bounds.hi.y; j++ ) {
  1812. for( i=bounds.lo.x; i<=bounds.hi.x; i++ ) {
  1813. if (i>bounds.lo.x && map[i][j].getZone()!=map[i-1][j].getZone()) {
  1814. if (waterGround(map[i][j], map[i-1][j])) {
  1815. applyBlockZone(map[i][j], map[i-1][j], m_groundWaterZones, m_firstZone, m_numZones);
  1816. }
  1817. if (groundRubble(map[i][j], map[i-1][j])) {
  1818. applyBlockZone(map[i][j], map[i-1][j], m_groundRubbleZones, m_firstZone, m_numZones);
  1819. }
  1820. if (groundCliff(map[i][j], map[i-1][j])) {
  1821. applyBlockZone(map[i][j], map[i-1][j], m_groundCliffZones, m_firstZone, m_numZones);
  1822. }
  1823. if (crusherGround(map[i][j], map[i-1][j])) {
  1824. applyBlockZone(map[i][j], map[i-1][j], m_crusherZones, m_firstZone, m_numZones);
  1825. }
  1826. }
  1827. if (j>bounds.lo.y && map[i][j].getZone()!=map[i][j-1].getZone()) {
  1828. if (waterGround(map[i][j],map[i][j-1])) {
  1829. applyBlockZone(map[i][j], map[i][j-1], m_groundWaterZones, m_firstZone, m_numZones);
  1830. }
  1831. if (groundRubble(map[i][j], map[i][j-1])) {
  1832. applyBlockZone(map[i][j], map[i][j-1], m_groundRubbleZones, m_firstZone, m_numZones);
  1833. }
  1834. if (groundCliff(map[i][j],map[i][j-1])) {
  1835. applyBlockZone(map[i][j], map[i][j-1], m_groundCliffZones, m_firstZone, m_numZones);
  1836. }
  1837. if (crusherGround(map[i][j], map[i][j-1])) {
  1838. applyBlockZone(map[i][j], map[i][j-1], m_crusherZones, m_firstZone, m_numZones);
  1839. }
  1840. }
  1841. DEBUG_ASSERTCRASH(map[i][j].getZone() != 0, ("Cleared the zone."));
  1842. }
  1843. }
  1844. }
  1845. //
  1846. // Return the zone at this location.
  1847. //
  1848. zoneStorageType ZoneBlock::getEffectiveZone( LocomotorSurfaceTypeMask acceptableSurfaces,
  1849. Bool crusher, zoneStorageType zone) const
  1850. {
  1851. if (zone==PathfindZoneManager::UNINITIALIZED_ZONE) {
  1852. return zone;
  1853. }
  1854. if (acceptableSurfaces&LOCOMOTORSURFACE_AIR) return 1; // air is all zone 1.
  1855. if ( (acceptableSurfaces&LOCOMOTORSURFACE_GROUND) &&
  1856. (acceptableSurfaces&LOCOMOTORSURFACE_WATER) &&
  1857. (acceptableSurfaces&LOCOMOTORSURFACE_CLIFF)) {
  1858. // Locomotors can go on ground, water & cliff, so all is zone 1.
  1859. return 1;
  1860. }
  1861. if (m_numZones<2) {
  1862. return m_firstZone; // if we only got 1 zone, it's all the same zone.
  1863. }
  1864. DEBUG_ASSERTCRASH(zone >=m_firstZone && zone < m_firstZone+m_numZones, ("Invalid range."));
  1865. if (zone<m_firstZone || zone >= m_firstZone+m_numZones) {
  1866. return m_firstZone;
  1867. }
  1868. zone -= m_firstZone;
  1869. if (crusher) {
  1870. zone = m_crusherZones[zone];
  1871. DEBUG_ASSERTCRASH(zone >=m_firstZone && zone < m_firstZone+m_numZones, ("Invalid range."));
  1872. zone -= m_firstZone;
  1873. }
  1874. if ( (acceptableSurfaces&LOCOMOTORSURFACE_GROUND) &&
  1875. (acceptableSurfaces&LOCOMOTORSURFACE_CLIFF)) {
  1876. // Locomotors can go on ground & cliff, so use the ground cliff combiner.
  1877. zone = m_groundCliffZones[zone];
  1878. DEBUG_ASSERTCRASH(zone >=m_firstZone && zone < m_firstZone+m_numZones, ("Invalid range."));
  1879. return zone;
  1880. }
  1881. if ( (acceptableSurfaces&LOCOMOTORSURFACE_GROUND) &&
  1882. (acceptableSurfaces&LOCOMOTORSURFACE_WATER)) {
  1883. // Locomotors can go on ground & water, so use the ground water combiner.
  1884. zone = m_groundWaterZones[zone];
  1885. DEBUG_ASSERTCRASH(zone >=m_firstZone && zone < m_firstZone+m_numZones, ("Invalid range."));
  1886. return zone;
  1887. }
  1888. if ( (acceptableSurfaces&LOCOMOTORSURFACE_GROUND) &&
  1889. (acceptableSurfaces&LOCOMOTORSURFACE_RUBBLE)) {
  1890. // Locomotors can go on ground & rubble, so use the ground rubble combiner.
  1891. zone = m_groundRubbleZones[zone];
  1892. return zone;
  1893. }
  1894. if ( (acceptableSurfaces&LOCOMOTORSURFACE_CLIFF) &&
  1895. (acceptableSurfaces&LOCOMOTORSURFACE_WATER)) {
  1896. // Locomotors can go on ground & cliff, so use the ground cliff combiner.
  1897. DEBUG_CRASH(("Cliff water only locomotor sets not supported yet."));
  1898. }
  1899. return zone+m_firstZone;
  1900. }
  1901. /* Allocate zone equivalency arrays large enough to hold m_maxZone entries. If the arrays are already
  1902. large enough, just return. */
  1903. void ZoneBlock::allocateZones(void)
  1904. {
  1905. if (m_zonesAllocated>m_numZones && m_groundCliffZones!=NULL) {
  1906. return;
  1907. }
  1908. freeZones();
  1909. if (m_numZones==1) {
  1910. return; // we don't need any zone equivalency tables.
  1911. }
  1912. if (m_zonesAllocated == 0) {
  1913. m_zonesAllocated = 4;
  1914. }
  1915. while (m_zonesAllocated <= m_numZones) {
  1916. m_zonesAllocated *= 2;
  1917. }
  1918. // pool[]ify
  1919. m_groundCliffZones = MSGNEW("PathfindZoneInfo") zoneStorageType [m_zonesAllocated];
  1920. m_groundWaterZones = MSGNEW("PathfindZoneInfo") zoneStorageType[m_zonesAllocated];
  1921. m_groundRubbleZones = MSGNEW("PathfindZoneInfo") zoneStorageType[m_zonesAllocated];
  1922. m_crusherZones = MSGNEW("PathfindZoneInfo") zoneStorageType[m_zonesAllocated];
  1923. }
  1924. //------------------------ PathfindZoneManager -------------------------------
  1925. PathfindZoneManager::PathfindZoneManager() : m_maxZone(0),
  1926. m_nextFrameToCalculateZones(0),
  1927. m_groundCliffZones(NULL),
  1928. m_groundWaterZones(NULL),
  1929. m_groundRubbleZones(NULL),
  1930. m_terrainZones(NULL),
  1931. m_crusherZones(NULL),
  1932. m_hierarchicalZones(NULL),
  1933. m_blockOfZoneBlocks(NULL),
  1934. m_zoneBlocks(NULL),
  1935. m_zonesAllocated(0)
  1936. {
  1937. m_zoneBlockExtent.x = 0;
  1938. m_zoneBlockExtent.y = 0;
  1939. }
  1940. PathfindZoneManager::~PathfindZoneManager()
  1941. {
  1942. freeZones();
  1943. freeBlocks();
  1944. }
  1945. void PathfindZoneManager::freeZones()
  1946. {
  1947. if (m_groundCliffZones) {
  1948. delete [] m_groundCliffZones;
  1949. m_groundCliffZones = NULL;
  1950. }
  1951. if (m_groundWaterZones) {
  1952. delete [] m_groundWaterZones;
  1953. m_groundWaterZones = NULL;
  1954. }
  1955. if (m_groundRubbleZones) {
  1956. delete [] m_groundRubbleZones;
  1957. m_groundRubbleZones = NULL;
  1958. }
  1959. if (m_terrainZones) {
  1960. delete [] m_terrainZones;
  1961. m_terrainZones = NULL;
  1962. }
  1963. if (m_crusherZones) {
  1964. delete [] m_crusherZones;
  1965. m_crusherZones = NULL;
  1966. }
  1967. if (m_hierarchicalZones) {
  1968. delete [] m_hierarchicalZones;
  1969. m_hierarchicalZones = NULL;
  1970. }
  1971. m_zonesAllocated = 0;
  1972. }
  1973. void PathfindZoneManager::freeBlocks()
  1974. {
  1975. if (m_blockOfZoneBlocks) {
  1976. delete [] m_blockOfZoneBlocks;
  1977. m_blockOfZoneBlocks = NULL;
  1978. }
  1979. if (m_zoneBlocks) {
  1980. delete [] m_zoneBlocks;
  1981. m_zoneBlocks = NULL;
  1982. }
  1983. m_zoneBlockExtent.x = 0;
  1984. m_zoneBlockExtent.y = 0;
  1985. }
  1986. /* Allocate zone equivalency arrays large enough to hold m_maxZone entries. If the arrays are already
  1987. large enough, just return. */
  1988. void PathfindZoneManager::allocateZones(void)
  1989. {
  1990. if (m_zonesAllocated>m_maxZone && m_groundCliffZones!=NULL) {
  1991. return;
  1992. }
  1993. freeZones();
  1994. if (m_zonesAllocated == 0) {
  1995. m_zonesAllocated = INITIAL_ZONES;
  1996. }
  1997. while (m_zonesAllocated <= m_maxZone) {
  1998. m_zonesAllocated *= 2;
  1999. }
  2000. DEBUG_LOG(("Allocating zone tables of size %d\n", m_zonesAllocated));
  2001. // pool[]ify
  2002. m_groundCliffZones = MSGNEW("PathfindZoneInfo") zoneStorageType[m_zonesAllocated];
  2003. m_groundWaterZones = MSGNEW("PathfindZoneInfo") zoneStorageType[m_zonesAllocated];
  2004. m_groundRubbleZones = MSGNEW("PathfindZoneInfo") zoneStorageType[m_zonesAllocated];
  2005. m_terrainZones = MSGNEW("PathfindZoneInfo") zoneStorageType[m_zonesAllocated];
  2006. m_crusherZones = MSGNEW("PathfindZoneInfo") zoneStorageType[m_zonesAllocated];
  2007. m_hierarchicalZones = MSGNEW("PathfindZoneInfo") zoneStorageType[m_zonesAllocated];
  2008. }
  2009. /* Allocate zone blocks for hierarchical pathfinding. */
  2010. void PathfindZoneManager::allocateBlocks(const IRegion2D &globalBounds)
  2011. {
  2012. freeBlocks();
  2013. m_zoneBlockExtent.x = (globalBounds.hi.x-globalBounds.lo.x+1+ZONE_BLOCK_SIZE-1)/ZONE_BLOCK_SIZE;
  2014. m_zoneBlockExtent.y = (globalBounds.hi.y-globalBounds.lo.y+1+ZONE_BLOCK_SIZE-1)/ZONE_BLOCK_SIZE;
  2015. m_blockOfZoneBlocks = MSGNEW("PathfindZoneBlocks") ZoneBlock[(m_zoneBlockExtent.x)*(m_zoneBlockExtent.y)];
  2016. m_zoneBlocks = MSGNEW("PathfindZoneBlocks") ZoneBlockP[m_zoneBlockExtent.x];
  2017. Int i;
  2018. for (i=0; i<m_zoneBlockExtent.x; i++) {
  2019. m_zoneBlocks[i] = &m_blockOfZoneBlocks[i*(m_zoneBlockExtent.y)];
  2020. }
  2021. }
  2022. void PathfindZoneManager::reset(void) ///< Called when the map is reset.
  2023. {
  2024. freeZones();
  2025. freeBlocks();
  2026. }
  2027. void PathfindZoneManager::markZonesDirty( Bool insert ) ///< Called when the zones need to be recalculated.
  2028. {
  2029. if (TheGameLogic->getFrame()<2) {
  2030. m_nextFrameToCalculateZones = 2;
  2031. return;
  2032. }
  2033. // if ( insert )
  2034. // m_nextFrameToCalculateZones = TheGameLogic->getFrame();
  2035. // else
  2036. m_nextFrameToCalculateZones = MIN( m_nextFrameToCalculateZones, TheGameLogic->getFrame() + ZONE_UPDATE_FREQUENCY );
  2037. }
  2038. /**
  2039. * Calculate zones. A zone is an area of the same terrain - clear, water or cliff.
  2040. * The utility of zones is that if current location and destiontion are in the same zone,
  2041. * you can successfully pathfind.
  2042. * If you are a multiple terrain vehicle, like amphibious transport, the lookup is a little more
  2043. * complicated.
  2044. */
  2045. #define dont_forceRefreshCalling
  2046. #ifdef forceRefreshCalling
  2047. static Bool s_stopForceCalling = FALSE;
  2048. #endif
  2049. void PathfindZoneManager::calculateZones( PathfindCell **map, PathfindLayer layers[], const IRegion2D &globalBounds )
  2050. {
  2051. #ifdef DEBUG_QPF
  2052. #if defined(DEBUG_LOGGING)
  2053. __int64 startTime64;
  2054. static double timeToUpdate = 0.0f;
  2055. static double averageTimeToUpdate = 0.0f;
  2056. static Int updateSamples = 0;
  2057. __int64 endTime64,freq64;
  2058. QueryPerformanceFrequency((LARGE_INTEGER *)&freq64);
  2059. QueryPerformanceCounter((LARGE_INTEGER *)&startTime64);
  2060. #endif
  2061. #endif
  2062. m_maxZone = 1; // we start using zone 0 as a flag.
  2063. const Int maxZones=24000;
  2064. zoneStorageType zoneEquivalency[maxZones];
  2065. Int i, j;
  2066. for (i=0; i<maxZones; i++) {
  2067. zoneEquivalency[i] = i;
  2068. }
  2069. for (i=0; i<=LAYER_LAST; i++) {
  2070. layers[i].setZone(0);
  2071. }
  2072. Int xCount = (globalBounds.hi.x-globalBounds.lo.x+1+ZONE_BLOCK_SIZE-1)/ZONE_BLOCK_SIZE;
  2073. Int yCount = (globalBounds.hi.y-globalBounds.lo.y+1+ZONE_BLOCK_SIZE-1)/ZONE_BLOCK_SIZE;
  2074. Int xBlock, yBlock;
  2075. for (xBlock = 0; xBlock<xCount; xBlock++) {
  2076. for (yBlock=0; yBlock<yCount; yBlock++) {
  2077. IRegion2D bounds;
  2078. bounds.lo.x = globalBounds.lo.x + xBlock*ZONE_BLOCK_SIZE;
  2079. bounds.lo.y = globalBounds.lo.y + yBlock*ZONE_BLOCK_SIZE;
  2080. bounds.hi.x = bounds.lo.x + ZONE_BLOCK_SIZE - 1; // bounds are inclusive.
  2081. bounds.hi.y = bounds.lo.y + ZONE_BLOCK_SIZE - 1; // bounds are inclusive.
  2082. if (bounds.hi.x > globalBounds.hi.x) {
  2083. bounds.hi.x = globalBounds.hi.x;
  2084. }
  2085. if (bounds.hi.y > globalBounds.hi.y) {
  2086. bounds.hi.y = globalBounds.hi.y;
  2087. }
  2088. // if (bounds.lo.x>bounds.hi.x || bounds.lo.y>bounds.hi.y) {
  2089. // DEBUG_CRASH(("Incorrect bounds calculation. Logic error, fix me. jba."));
  2090. // continue;
  2091. // }
  2092. m_zoneBlocks[xBlock][yBlock].setInteractsWithBridge(false);
  2093. for( j=bounds.lo.y; j<=bounds.hi.y; j++ ) {
  2094. for( i=bounds.lo.x; i<=bounds.hi.x; i++ ) {
  2095. PathfindCell *cell = &map[i][j];
  2096. cell->setZone(0);
  2097. if (i>bounds.lo.x) {
  2098. if (map[i][j].getType() == map[i-1][j].getType()) {
  2099. applyZone(map[i][j], map[i-1][j], zoneEquivalency, m_maxZone);
  2100. }
  2101. }
  2102. if (j>bounds.lo.y) {
  2103. if (map[i][j].getType() == map[i][j-1].getType()) {
  2104. applyZone(map[i][j], map[i][j-1], zoneEquivalency, m_maxZone);
  2105. }
  2106. }
  2107. if (cell->getZone()==0) {
  2108. cell->setZone(m_maxZone);
  2109. m_maxZone++;
  2110. // if (m_maxZone>= maxZones) {
  2111. // DEBUG_CRASH(("Ran out of pathfind zones. SERIOUS ERROR! jba."));
  2112. // break;
  2113. // }
  2114. }
  2115. if (cell->getConnectLayer() > LAYER_GROUND) {
  2116. m_zoneBlocks[xBlock][yBlock].setInteractsWithBridge(true);
  2117. }
  2118. }
  2119. }
  2120. //DEBUG_LOG(("Collapsed zones %d\n", m_maxZone));
  2121. }
  2122. }
  2123. Int totalZones = m_maxZone;
  2124. // if (totalZones>maxZones/2) {
  2125. // DEBUG_LOG(("Max zones %d\n", m_maxZone));
  2126. // }
  2127. // Collapse the zones into a 1,2,3... sequence, removing collapsed zones.
  2128. m_maxZone = 1;
  2129. Int collapsedZones[maxZones];
  2130. collapsedZones[0] = 0;
  2131. i = 1;
  2132. while ( i < totalZones )
  2133. {
  2134. Int zone = zoneEquivalency[ i ];
  2135. if (zone == i)
  2136. {
  2137. collapsedZones[ i ] = m_maxZone;
  2138. ++m_maxZone;
  2139. }
  2140. else
  2141. collapsedZones[ i ] = collapsedZones[zone];
  2142. ++i;
  2143. }
  2144. // for (i=1; i<totalZones; i++) {
  2145. // Int zone = zoneEquivalency[i];
  2146. // if (zone == i) {
  2147. // collapsedZones[i] = m_maxZone;
  2148. // ++m_maxZone;
  2149. // } else {
  2150. // collapsedZones[i] = collapsedZones[zone];
  2151. // }
  2152. // }
  2153. //#ifdef DEBUG_QPF
  2154. //#if defined(DEBUG_LOGGING)
  2155. // QueryPerformanceCounter((LARGE_INTEGER *)&endTime64);
  2156. // timeToUpdate = ((double)(endTime64-startTime64) / (double)(freq64));
  2157. // DEBUG_LOG(("Time to calculate first %f\n", timeToUpdate));
  2158. //#endif
  2159. //#endif
  2160. // Now map the zones in the map back into the collapsed zones.
  2161. j=globalBounds.lo.y;
  2162. while( j<=globalBounds.hi.y )
  2163. {
  2164. i=globalBounds.lo.x;
  2165. while( i<=globalBounds.hi.x )
  2166. {
  2167. PathfindCell &cell = map[i][j];
  2168. cell.setZone(collapsedZones[cell.getZone()]);
  2169. //if (cell.getZone()==0)
  2170. //{
  2171. // DEBUG_CRASH(("Zone not set cell %d, %d", i, j));
  2172. //}
  2173. ++i;
  2174. }
  2175. ++j;
  2176. }
  2177. // for( j=globalBounds.lo.y; j<=globalBounds.hi.y; j++ ) {
  2178. // for( i=globalBounds.lo.x; i<=globalBounds.hi.x; i++ ) {
  2179. // map[i][j].setZone(collapsedZones[map[i][j].getZone()]);
  2180. //// if (map[i][j].getZone()==0) {
  2181. //// DEBUG_CRASH(("Zone not set cell %d, %d", i, j));
  2182. //// }
  2183. // }
  2184. // }
  2185. i = 0;
  2186. while ( i <= LAYER_LAST )
  2187. {
  2188. PathfindLayer &r_thisLayer = layers[i];
  2189. Int zone = collapsedZones[r_thisLayer.getZone()];
  2190. if (zone == 0)
  2191. {
  2192. zone = m_maxZone;
  2193. m_maxZone++;
  2194. }
  2195. r_thisLayer.setZone( zone );
  2196. r_thisLayer.applyZone();
  2197. if (!r_thisLayer.isUnused() && !r_thisLayer.isDestroyed())
  2198. {
  2199. ICoord2D ndx;
  2200. r_thisLayer.getStartCellIndex(&ndx);
  2201. setBridge(ndx.x, ndx.y, true);
  2202. r_thisLayer.getEndCellIndex(&ndx);
  2203. setBridge(ndx.x, ndx.y, true);
  2204. }
  2205. ++i;
  2206. }
  2207. // for (i=0; i<=LAYER_LAST; i++) {
  2208. // Int zone = collapsedZones[layers[i].getZone()];
  2209. // if (zone == 0) {
  2210. // zone = m_maxZone;
  2211. // m_maxZone++;
  2212. // }
  2213. // layers[i].setZone( zone );
  2214. //// if (!layers[i].isUnused() && !layers[i].isDestroyed() && layers[i].getZone()==0) {
  2215. //// DEBUG_CRASH(("Zone not set Layer %d", i));
  2216. //// }
  2217. // layers[i].applyZone();
  2218. // if (!layers[i].isUnused() && !layers[i].isDestroyed()) {
  2219. // ICoord2D ndx;
  2220. // layers[i].getStartCellIndex(&ndx);
  2221. // setBridge(ndx.x, ndx.y, true);
  2222. // layers[i].getEndCellIndex(&ndx);
  2223. // setBridge(ndx.x, ndx.y, true);
  2224. // }
  2225. // }
  2226. allocateZones();
  2227. // DEBUG_ASSERTCRASH(xBlock==m_zoneBlockExtent.x && yBlock==m_zoneBlockExtent.y, ("Inconsistent allocation - SERIOUS ERROR. jba"));
  2228. for (xBlock=0; xBlock<xCount; xBlock++)
  2229. {
  2230. for (yBlock=0; yBlock<yCount; yBlock++)
  2231. {
  2232. IRegion2D bounds;
  2233. bounds.lo.x = globalBounds.lo.x + xBlock*ZONE_BLOCK_SIZE;
  2234. bounds.lo.y = globalBounds.lo.y + yBlock*ZONE_BLOCK_SIZE;
  2235. bounds.hi.x = bounds.lo.x + ZONE_BLOCK_SIZE - 1; // bounds are inclusive.
  2236. bounds.hi.y = bounds.lo.y + ZONE_BLOCK_SIZE - 1; // bounds are inclusive.
  2237. if (bounds.hi.x > globalBounds.hi.x)
  2238. bounds.hi.x = globalBounds.hi.x;
  2239. if (bounds.hi.y > globalBounds.hi.y)
  2240. bounds.hi.y = globalBounds.hi.y;
  2241. // Although a good safeguard, the logic is already proven, thus skip the check
  2242. // if (bounds.lo.x>bounds.hi.x || bounds.lo.y>bounds.hi.y) {
  2243. // DEBUG_CRASH(("Incorrect bounds calculation. Logic error, fix me. jba."));
  2244. // continue;
  2245. // }
  2246. m_zoneBlocks[xBlock][yBlock].blockCalculateZones(map, layers, bounds);
  2247. }
  2248. }
  2249. //#ifdef DEBUG_QPF
  2250. //#if defined(DEBUG_LOGGING)
  2251. // QueryPerformanceCounter((LARGE_INTEGER *)&endTime64);
  2252. // timeToUpdate = ((double)(endTime64-startTime64) / (double)(freq64));
  2253. // DEBUG_LOG(("Time to calculate second %f\n", timeToUpdate));
  2254. //#endif
  2255. //#endif
  2256. // Determine water/ground equivalent zones, and ground/cliff equivalent zones.
  2257. // for (i=0; i<m_zonesAllocated; i++) {
  2258. // m_groundCliffZones[i] = i;
  2259. // m_groundWaterZones[i] = i;
  2260. // m_groundRubbleZones[i] = i;
  2261. // m_terrainZones[i] = i;
  2262. // m_crusherZones[i] = i;
  2263. // m_hierarchicalZones[i] = i;
  2264. // }
  2265. i = 0;
  2266. while ( i < m_zonesAllocated )
  2267. {
  2268. m_groundCliffZones[i] = m_groundWaterZones[i] = m_groundRubbleZones[i] = m_terrainZones[i] = m_crusherZones[i] = m_hierarchicalZones[i] = i;
  2269. i++;
  2270. }
  2271. // for( j=globalBounds.lo.y; j<=globalBounds.hi.y; j++ ) {
  2272. // for( i=globalBounds.lo.x; i<=globalBounds.hi.x; i++ ) {
  2273. // if ( (map[i][j].getConnectLayer() > LAYER_GROUND) &&
  2274. // (map[i][j].getType() == PathfindCell::CELL_CLEAR) ) {
  2275. // PathfindLayer *layer = layers + map[i][j].getConnectLayer();
  2276. // resolveZones(map[i][j].getZone(), layer->getZone(), m_hierarchicalZones, m_maxZone);
  2277. // }
  2278. // if (i>globalBounds.lo.x && map[i][j].getZone()!=map[i-1][j].getZone()) {
  2279. // if (map[i][j].getType() == map[i-1][j].getType()) {
  2280. // applyZone(map[i][j], map[i-1][j], m_hierarchicalZones, m_maxZone);
  2281. // }
  2282. // if (waterGround(map[i][j], map[i-1][j])) {
  2283. // applyZone(map[i][j], map[i-1][j], m_groundWaterZones, m_maxZone);
  2284. // }
  2285. // if (groundRubble(map[i][j], map[i-1][j])) {
  2286. //// Int zone1 = map[i][j].getZone();
  2287. //// Int zone2 = map[i-1][j].getZone();
  2288. //// if (m_terrainZones[zone1] != m_terrainZones[zone2]) {
  2289. //// //DEBUG_LOG(("Matching terrain zone %d to %d.\n", zone1, zone2));
  2290. //// }
  2291. // applyZone(map[i][j], map[i-1][j], m_groundRubbleZones, m_maxZone);
  2292. // }
  2293. // if (groundCliff(map[i][j], map[i-1][j])) {
  2294. // applyZone(map[i][j], map[i-1][j], m_groundCliffZones, m_maxZone);
  2295. // }
  2296. // if (terrain(map[i][j], map[i-1][j])) {
  2297. // applyZone(map[i][j], map[i-1][j], m_terrainZones, m_maxZone);
  2298. // }
  2299. // if (crusherGround(map[i][j], map[i-1][j])) {
  2300. // applyZone(map[i][j], map[i-1][j], m_crusherZones, m_maxZone);
  2301. // }
  2302. // }
  2303. // if (j>globalBounds.lo.y && map[i][j].getZone()!=map[i][j-1].getZone()) {
  2304. // if (map[i][j].getType() == map[i][j-1].getType()) {
  2305. // applyZone(map[i][j], map[i][j-1], m_hierarchicalZones, m_maxZone);
  2306. // }
  2307. // if (waterGround(map[i][j],map[i][j-1])) {
  2308. // applyZone(map[i][j], map[i][j-1], m_groundWaterZones, m_maxZone);
  2309. // }
  2310. // if (groundRubble(map[i][j], map[i][j-1])) {
  2311. //// Int zone1 = map[i][j].getZone();
  2312. //// Int zone2 = map[i][j-1].getZone();
  2313. //// if (m_terrainZones[zone1] != m_terrainZones[zone2]) {
  2314. //// //DEBUG_LOG(("Matching terrain zone %d to %d.\n", zone1, zone2));
  2315. //// }
  2316. // applyZone(map[i][j], map[i][j-1], m_groundRubbleZones, m_maxZone);
  2317. // }
  2318. // if (groundCliff(map[i][j],map[i][j-1])) {
  2319. // applyZone(map[i][j], map[i][j-1], m_groundCliffZones, m_maxZone);
  2320. // }
  2321. // if (terrain(map[i][j], map[i][j-1])) {
  2322. // applyZone(map[i][j], map[i][j-1], m_terrainZones, m_maxZone);
  2323. // }
  2324. // if (crusherGround(map[i][j], map[i][j-1])) {
  2325. // applyZone(map[i][j], map[i][j-1], m_crusherZones, m_maxZone);
  2326. // }
  2327. // }
  2328. // // DEBUG_ASSERTCRASH(map[i][j].getZone() != 0, ("Cleared the zone."));
  2329. // }
  2330. // }
  2331. register UnsignedInt maxZone = m_maxZone;
  2332. j=globalBounds.lo.y;
  2333. while( j <= globalBounds.hi.y )
  2334. {
  2335. i=globalBounds.lo.x;
  2336. while( i <= globalBounds.hi.x )
  2337. {
  2338. PathfindCell &r_thisCell = map[i][j];
  2339. if ( (r_thisCell.getConnectLayer() > LAYER_GROUND) &&
  2340. (r_thisCell.getType() == PathfindCell::CELL_CLEAR) )
  2341. {
  2342. PathfindLayer *layer = layers + r_thisCell.getConnectLayer();
  2343. resolveZones(r_thisCell.getZone(), layer->getZone(), m_hierarchicalZones, maxZone);
  2344. }
  2345. if ( i > globalBounds.lo.x && r_thisCell.getZone() != map[i-1][j].getZone() )
  2346. {
  2347. const PathfindCell &r_leftCell = map[i-1][j];
  2348. if (r_thisCell.getType() == r_leftCell.getType())
  2349. applyZone(r_thisCell, r_leftCell, m_hierarchicalZones, maxZone);//if this is true, skip all the ones below
  2350. else
  2351. {
  2352. Bool notTerrainOrCrusher = TRUE; // if this is false, skip the if-else-ladder below
  2353. if (terrain(r_thisCell, r_leftCell))
  2354. {
  2355. applyZone(r_thisCell, r_leftCell, m_terrainZones, maxZone);
  2356. notTerrainOrCrusher = FALSE;
  2357. }
  2358. if (crusherGround(r_thisCell, r_leftCell))
  2359. {
  2360. applyZone(r_thisCell, r_leftCell, m_crusherZones, maxZone);
  2361. notTerrainOrCrusher = FALSE;
  2362. }
  2363. if ( notTerrainOrCrusher )
  2364. {
  2365. if (waterGround(r_thisCell, r_leftCell))
  2366. applyZone(r_thisCell, r_leftCell, m_groundWaterZones, maxZone);
  2367. else if (groundRubble(r_thisCell, r_leftCell))
  2368. applyZone(r_thisCell, r_leftCell, m_groundRubbleZones, maxZone);
  2369. else if (groundCliff(r_thisCell, r_leftCell))
  2370. applyZone(r_thisCell, r_leftCell, m_groundCliffZones, maxZone);
  2371. }
  2372. }
  2373. }
  2374. if (j>globalBounds.lo.y && r_thisCell.getZone()!=map[i][j-1].getZone())
  2375. {
  2376. const PathfindCell &r_topCell = map[i][j-1];
  2377. if (r_thisCell.getType() == r_topCell.getType())
  2378. applyZone(r_thisCell, r_topCell, m_hierarchicalZones, maxZone);
  2379. else
  2380. {
  2381. Bool notTerrainOrCrusher = TRUE; // if this is false, skip the if-else-ladder below
  2382. if (terrain(r_thisCell, r_topCell))
  2383. {
  2384. applyZone(r_thisCell, r_topCell, m_terrainZones, maxZone);
  2385. notTerrainOrCrusher = FALSE;
  2386. }
  2387. if (crusherGround(r_thisCell, r_topCell))
  2388. {
  2389. applyZone(r_thisCell, r_topCell, m_crusherZones, maxZone);
  2390. notTerrainOrCrusher = FALSE;
  2391. }
  2392. if (waterGround(r_thisCell,r_topCell))
  2393. applyZone(r_thisCell, r_topCell, m_groundWaterZones, maxZone);
  2394. else if (groundRubble(r_thisCell, r_topCell))
  2395. applyZone(r_thisCell, r_topCell, m_groundRubbleZones, maxZone);
  2396. else if (groundCliff(r_thisCell,r_topCell))
  2397. applyZone(r_thisCell, r_topCell, m_groundCliffZones, maxZone);
  2398. }
  2399. }
  2400. // DEBUG_ASSERTCRASH(r_thisCell.getZone() != 0, ("Cleared the zone."));
  2401. ++i;
  2402. }
  2403. ++j;
  2404. }
  2405. // if (m_maxZone >= m_zonesAllocated) {
  2406. // RELEASE_CRASH("Pathfind allocation error - fatal. see jba.");
  2407. // }
  2408. // for (i=1; i<m_maxZone; i++) {
  2409. // // Flatten hierarchical zones.
  2410. // Int zone = m_hierarchicalZones[i];
  2411. // m_hierarchicalZones[i] = m_hierarchicalZones[zone];
  2412. // }
  2413. //FLATTEN HIERARCHICAL ZONES
  2414. {
  2415. i = 1;
  2416. register Int zone;
  2417. while ( i < maxZone )
  2418. { // Flatten hierarchical zones.
  2419. zone = m_hierarchicalZones[i];
  2420. m_hierarchicalZones[i] = m_hierarchicalZones[ zone ];
  2421. ++i;
  2422. }
  2423. }
  2424. //THIS BLOCK IS 20%
  2425. flattenZones(m_groundCliffZones, m_hierarchicalZones, m_maxZone);
  2426. flattenZones(m_groundWaterZones, m_hierarchicalZones, m_maxZone);
  2427. flattenZones(m_groundRubbleZones, m_hierarchicalZones, m_maxZone);
  2428. flattenZones(m_terrainZones, m_hierarchicalZones, m_maxZone);
  2429. flattenZones(m_crusherZones, m_hierarchicalZones, m_maxZone);
  2430. #ifdef DEBUG_QPF
  2431. #if defined(DEBUG_LOGGING)
  2432. QueryPerformanceCounter((LARGE_INTEGER *)&endTime64);
  2433. timeToUpdate = ((double)(endTime64-startTime64) / (double)(freq64));
  2434. // DEBUG_LOG(("Time to calculate zones %f, cells %d\n", timeToUpdate, (globalBounds.hi.x-globalBounds.lo.x)*(globalBounds.hi.y-globalBounds.lo.y)));
  2435. if ( updateSamples < 400 )
  2436. {
  2437. averageTimeToUpdate = ((averageTimeToUpdate * updateSamples) + timeToUpdate) / (updateSamples + 1.0f);
  2438. updateSamples++;
  2439. DEBUG_LOG(("computing...: %f, \n", averageTimeToUpdate));
  2440. }
  2441. else if ( updateSamples == 400 )
  2442. {
  2443. DEBUG_LOG((" =============DONE============= Average time to calculate zones: %f, \n", averageTimeToUpdate));
  2444. DEBUG_LOG((" Percent of baseline : %f, \n", averageTimeToUpdate/0.003335f));
  2445. updateSamples = 777;
  2446. #ifdef forceRefreshCalling
  2447. s_stopForceCalling = TRUE;
  2448. #endif
  2449. }
  2450. #endif
  2451. #endif
  2452. #if defined _DEBUG || defined _INTERNAL
  2453. if (TheGlobalData->m_debugAI == AI_DEBUG_ZONES)
  2454. {
  2455. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  2456. RGBColor color;
  2457. memset(&color, 0, sizeof(Color));
  2458. addIcon(NULL, 0, 0, color);
  2459. for( j=0; j<globalBounds.hi.y; j++ ) {
  2460. for( i=0; i<globalBounds.hi.x; i++ ) {
  2461. Int zone = map[i][j].getZone();
  2462. //zone = m_terrainZones[zone];
  2463. //zone = m_groundCliffZones[zone];
  2464. zone = m_hierarchicalZones[zone];
  2465. color.blue = (zone%3) * 0.5f;
  2466. zone = zone/3;
  2467. color.green = (zone%3) * 0.5f;
  2468. zone = zone/3;
  2469. color.red = (zone%3) * 0.5;
  2470. Coord3D pos;
  2471. pos.x = ((Real)i + 0.5f) * PATHFIND_CELL_SIZE_F;
  2472. pos.y = ((Real)j + 0.5f) * PATHFIND_CELL_SIZE_F;
  2473. pos.z = TheTerrainLogic->getLayerHeight( pos.x, pos.y, map[i][j].getLayer() ) + 0.5f;
  2474. addIcon(&pos, PATHFIND_CELL_SIZE_F*0.8f, 500, color);
  2475. }
  2476. }
  2477. }
  2478. #endif
  2479. m_nextFrameToCalculateZones = 0xffffffff;
  2480. }
  2481. /**
  2482. * Update zones where a structure has been added or removed.
  2483. * This can be done by just updating the equivalency arrays, without rezoning the map..
  2484. */
  2485. void PathfindZoneManager::updateZonesForModify(PathfindCell **map, PathfindLayer layers[], const IRegion2D &structureBounds, const IRegion2D &globalBounds )
  2486. {
  2487. #ifdef DEBUG_QPF
  2488. #if defined(DEBUG_LOGGING)
  2489. __int64 startTime64;
  2490. double timeToUpdate=0.0f;
  2491. __int64 endTime64,freq64;
  2492. QueryPerformanceFrequency((LARGE_INTEGER *)&freq64);
  2493. QueryPerformanceCounter((LARGE_INTEGER *)&startTime64);
  2494. #endif
  2495. #endif
  2496. IRegion2D bounds = structureBounds;
  2497. bounds.hi.x++;
  2498. bounds.hi.y++;
  2499. if (bounds.hi.x > globalBounds.hi.x) {
  2500. bounds.hi.x = globalBounds.hi.x;
  2501. }
  2502. if (bounds.hi.y > globalBounds.hi.y) {
  2503. bounds.hi.y = globalBounds.hi.y;
  2504. }
  2505. Int xBlock, yBlock;
  2506. for (xBlock = 0; xBlock<m_zoneBlockExtent.x; xBlock++) {
  2507. for (yBlock=0; yBlock<m_zoneBlockExtent.y; yBlock++) {
  2508. IRegion2D blockBounds;
  2509. blockBounds.lo.x = globalBounds.lo.x + xBlock*ZONE_BLOCK_SIZE;
  2510. blockBounds.lo.y = globalBounds.lo.y + yBlock*ZONE_BLOCK_SIZE;
  2511. blockBounds.hi.x = blockBounds.lo.x + ZONE_BLOCK_SIZE - 1; // blockBounds are inclusive.
  2512. blockBounds.hi.y = blockBounds.lo.y + ZONE_BLOCK_SIZE - 1; // blockBounds are inclusive.
  2513. if (blockBounds.hi.x > bounds.hi.x) {
  2514. blockBounds.hi.x = bounds.hi.x;
  2515. }
  2516. if (blockBounds.hi.y > bounds.hi.y) {
  2517. blockBounds.hi.y = bounds.hi.y;
  2518. }
  2519. if (blockBounds.lo.x < bounds.lo.x) {
  2520. blockBounds.lo.x = bounds.lo.x;
  2521. }
  2522. if (blockBounds.lo.y < bounds.lo.y) {
  2523. blockBounds.lo.y = bounds.lo.y;
  2524. }
  2525. if (blockBounds.lo.x>blockBounds.hi.x || blockBounds.lo.y>blockBounds.hi.y) {
  2526. continue;
  2527. }
  2528. m_zoneBlocks[xBlock][yBlock].setInteractsWithBridge(false);
  2529. Int i, j;
  2530. for( j=blockBounds.lo.y; j<=blockBounds.hi.y; j++ ) {
  2531. for( i=blockBounds.lo.x; i<=blockBounds.hi.x; i++ ) {
  2532. PathfindCell *cell = &map[i][j];
  2533. if (cell->getZone()!=UNINITIALIZED_ZONE) continue;
  2534. if (i>blockBounds.lo.x) {
  2535. if (map[i][j].getType() == map[i-1][j].getType()) {
  2536. cell->setZone(map[i-1][j].getZone());
  2537. if (cell->getZone()!=UNINITIALIZED_ZONE) continue;
  2538. }
  2539. }
  2540. if (j>blockBounds.lo.y) {
  2541. if (cell->getType() == map[i][j-1].getType()) {
  2542. cell->setZone(map[i][j-1].getZone());
  2543. if (cell->getZone()!=UNINITIALIZED_ZONE) continue;
  2544. }
  2545. if (i<blockBounds.hi.x) {
  2546. if (typesMatch(*cell, map[i+1][j-1]) &&
  2547. typesMatch(*cell, map[i+1][j])) {
  2548. cell->setZone(map[i+1][j-1].getZone());
  2549. if (cell->getZone()!=UNINITIALIZED_ZONE) continue;
  2550. }
  2551. }
  2552. }
  2553. }
  2554. }
  2555. for( j=blockBounds.hi.y; j>=blockBounds.lo.y; j-- ) {
  2556. for( i=blockBounds.hi.x; i>=blockBounds.lo.x; i-- ) {
  2557. PathfindCell *cell = &map[i][j];
  2558. if (cell->getZone()!=UNINITIALIZED_ZONE) continue;
  2559. if (i<blockBounds.hi.x) {
  2560. if (map[i][j].getType() == map[i+1][j].getType()) {
  2561. cell->setZone(map[i+1][j].getZone());
  2562. if (cell->getZone()!=UNINITIALIZED_ZONE) continue;
  2563. }
  2564. }
  2565. if (j<blockBounds.hi.y) {
  2566. if (cell->getType() == map[i][j+1].getType()) {
  2567. cell->setZone(map[i][j+1].getZone());
  2568. if (cell->getZone()!=UNINITIALIZED_ZONE) continue;
  2569. }
  2570. if (i<blockBounds.hi.x) {
  2571. if (typesMatch(*cell, map[i+1][j+1]) &&
  2572. typesMatch(*cell, map[i+1][j])) {
  2573. cell->setZone(map[i+1][j+1].getZone());
  2574. if (cell->getZone()!=UNINITIALIZED_ZONE) continue;
  2575. }
  2576. }
  2577. }
  2578. }
  2579. }
  2580. //DEBUG_LOG(("Collapsed zones %d\n", m_maxZone));
  2581. }
  2582. }
  2583. #ifdef DEBUG_QPF
  2584. #if defined(DEBUG_LOGGING)
  2585. QueryPerformanceCounter((LARGE_INTEGER *)&endTime64);
  2586. timeToUpdate = ((double)(endTime64-startTime64) / (double)(freq64));
  2587. //DEBUG_LOG(("Time to update zones %f, cells %d\n", timeToUpdate, (globalBounds.hi.x-globalBounds.lo.x)*(globalBounds.hi.y-globalBounds.lo.y)));
  2588. #endif
  2589. #endif
  2590. #if defined _DEBUG || defined _INTERNAL
  2591. if (TheGlobalData->m_debugAI==AI_DEBUG_ZONES)
  2592. {
  2593. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  2594. RGBColor color;
  2595. memset(&color, 0, sizeof(Color));
  2596. addIcon(NULL, 0, 0, color);
  2597. Int i, j;
  2598. for( j=0; j<globalBounds.hi.y; j++ ) {
  2599. for( i=0; i<globalBounds.hi.x; i++ ) {
  2600. Int zone = map[i][j].getZone();
  2601. //zone = m_terrainZones[zone];
  2602. zone = m_hierarchicalZones[zone];
  2603. color.blue = (zone%3) * 0.5f;
  2604. zone = zone/3;
  2605. color.green = (zone%3) * 0.5f;
  2606. zone = zone/3;
  2607. color.red = (zone%3) * 0.5;
  2608. Coord3D pos;
  2609. pos.x = ((Real)i + 0.5f) * PATHFIND_CELL_SIZE_F;
  2610. pos.y = ((Real)j + 0.5f) * PATHFIND_CELL_SIZE_F;
  2611. pos.z = TheTerrainLogic->getLayerHeight( pos.x, pos.y, map[i][j].getLayer() ) + 0.5f;
  2612. addIcon(&pos, PATHFIND_CELL_SIZE_F*0.8f, 200, color);
  2613. }
  2614. }
  2615. }
  2616. #endif
  2617. }
  2618. //
  2619. // Clear the passable flags.
  2620. //
  2621. void PathfindZoneManager::clearPassableFlags( )
  2622. { Int blockX;
  2623. Int blockY;
  2624. for (blockX = 0; blockX<m_zoneBlockExtent.x; blockX++) {
  2625. for (blockY = 0; blockY<m_zoneBlockExtent.y; blockY++) {
  2626. m_zoneBlocks[blockX][blockY].setPassable(false);
  2627. }
  2628. }
  2629. }
  2630. //
  2631. // Set the passable flags.
  2632. //
  2633. void PathfindZoneManager::setAllPassable( )
  2634. { Int blockX;
  2635. Int blockY;
  2636. for (blockX = 0; blockX<m_zoneBlockExtent.x; blockX++) {
  2637. for (blockY = 0; blockY<m_zoneBlockExtent.y; blockY++) {
  2638. m_zoneBlocks[blockX][blockY].setPassable(true);
  2639. }
  2640. }
  2641. }
  2642. //
  2643. // Set the passable flag for the block at this location.
  2644. //
  2645. void PathfindZoneManager::setPassable(Int cellX, Int cellY, Bool passable)
  2646. {
  2647. Int blockX = cellX/ZONE_BLOCK_SIZE;
  2648. Int blockY = cellY/ZONE_BLOCK_SIZE;
  2649. if (blockX<0 || blockX>=m_zoneBlockExtent.x) {
  2650. DEBUG_CRASH(("Invalid block."));
  2651. return;
  2652. }
  2653. if (blockY<0 || blockY>=m_zoneBlockExtent.y) {
  2654. DEBUG_CRASH(("Invalid block."));
  2655. return;
  2656. }
  2657. m_zoneBlocks[blockX][blockY].setPassable(passable);
  2658. }
  2659. //
  2660. // Get the passable flag for the block at this location.
  2661. //
  2662. Bool PathfindZoneManager::isPassable(Int cellX, Int cellY) const
  2663. {
  2664. Int blockX = cellX/ZONE_BLOCK_SIZE;
  2665. Int blockY = cellY/ZONE_BLOCK_SIZE;
  2666. if (blockX<0 || blockX>=m_zoneBlockExtent.x) {
  2667. DEBUG_CRASH(("Invalid block."));
  2668. return false;
  2669. }
  2670. if (blockY<0 || blockY>=m_zoneBlockExtent.y) {
  2671. DEBUG_CRASH(("Invalid block."));
  2672. return false;
  2673. }
  2674. return m_zoneBlocks[blockX][blockY].isPassable();
  2675. }
  2676. //
  2677. // Get the passable flag for the block at this location.
  2678. //
  2679. Bool PathfindZoneManager::clipIsPassable(Int cellX, Int cellY) const
  2680. {
  2681. Int blockX = cellX/ZONE_BLOCK_SIZE;
  2682. Int blockY = cellY/ZONE_BLOCK_SIZE;
  2683. if (blockX<0 || blockX>=m_zoneBlockExtent.x) {
  2684. return false;
  2685. }
  2686. if (blockY<0 || blockY>=m_zoneBlockExtent.y) {
  2687. return false;
  2688. }
  2689. return m_zoneBlocks[blockX][blockY].isPassable();
  2690. }
  2691. //
  2692. // Set the bridge flag for the block at this location.
  2693. //
  2694. void PathfindZoneManager::setBridge(Int cellX, Int cellY, Bool bridge)
  2695. {
  2696. Int blockX = cellX/ZONE_BLOCK_SIZE;
  2697. Int blockY = cellY/ZONE_BLOCK_SIZE;
  2698. if (blockX<0 || blockX>=m_zoneBlockExtent.x) {
  2699. // DEBUG_CRASH(("Invalid block.")); Bridges can be off the playable grid, so don't crash. jba.
  2700. return;
  2701. }
  2702. if (blockY<0 || blockY>=m_zoneBlockExtent.y) {
  2703. // DEBUG_CRASH(("Invalid block.")); Bridges can be off the playable grid, so don't crash. jba.
  2704. return;
  2705. }
  2706. m_zoneBlocks[blockX][blockY].setInteractsWithBridge(bridge);
  2707. }
  2708. //
  2709. // Set the bridge flag for the block at this location.
  2710. //
  2711. Bool PathfindZoneManager::interactsWithBridge(Int cellX, Int cellY) const
  2712. {
  2713. Int blockX = cellX/ZONE_BLOCK_SIZE;
  2714. Int blockY = cellY/ZONE_BLOCK_SIZE;
  2715. if (blockX<0 || blockX>=m_zoneBlockExtent.x) {
  2716. DEBUG_CRASH(("Invalid block."));
  2717. return false;
  2718. }
  2719. if (blockY<0 || blockY>=m_zoneBlockExtent.y) {
  2720. DEBUG_CRASH(("Invalid block."));
  2721. return false;
  2722. }
  2723. return m_zoneBlocks[blockX][blockY].getInteractsWithBridge();
  2724. }
  2725. //
  2726. // Return the zone at this location.
  2727. //
  2728. zoneStorageType PathfindZoneManager::getBlockZone(LocomotorSurfaceTypeMask acceptableSurfaces, Bool crusher,Int cellX, Int cellY, PathfindCell **map) const
  2729. {
  2730. PathfindCell *cell = &(map[cellX][cellY]);
  2731. Int blockX = cellX/ZONE_BLOCK_SIZE;
  2732. Int blockY = cellY/ZONE_BLOCK_SIZE;
  2733. if (blockX<0 || blockX>=m_zoneBlockExtent.x) {
  2734. DEBUG_CRASH(("Invalid block."));
  2735. return 0;
  2736. }
  2737. if (blockY<0 || blockY>=m_zoneBlockExtent.y) {
  2738. DEBUG_CRASH(("Invalid block."));
  2739. return 0;
  2740. }
  2741. zoneStorageType zone = m_zoneBlocks[blockX][blockY].getEffectiveZone(acceptableSurfaces, crusher, cell->getZone());
  2742. if (zone >= m_maxZone) {
  2743. DEBUG_CRASH(("Invalid zone."));
  2744. return UNINITIALIZED_ZONE;
  2745. }
  2746. return zone;
  2747. }
  2748. //
  2749. // Return the zone at this location.
  2750. //
  2751. zoneStorageType PathfindZoneManager::getEffectiveTerrainZone(zoneStorageType zone) const
  2752. {
  2753. return m_hierarchicalZones[m_terrainZones[zone]];
  2754. }
  2755. //
  2756. // Return the zone at this location.
  2757. //
  2758. zoneStorageType PathfindZoneManager::getEffectiveZone( LocomotorSurfaceTypeMask acceptableSurfaces,
  2759. Bool crusher, zoneStorageType zone) const
  2760. {
  2761. //DEBUG_ASSERTCRASH(zone, ("Zone not set"));
  2762. if (zone>m_maxZone) {
  2763. DEBUG_CRASH(("Invalid zone"));
  2764. return (0);
  2765. }
  2766. if (zone>m_maxZone) {
  2767. DEBUG_CRASH(("Invalid zone"));
  2768. return (0);
  2769. }
  2770. if (acceptableSurfaces&LOCOMOTORSURFACE_AIR) return 1; // air is all zone 1.
  2771. if ( (acceptableSurfaces&LOCOMOTORSURFACE_GROUND) &&
  2772. (acceptableSurfaces&LOCOMOTORSURFACE_WATER) &&
  2773. (acceptableSurfaces&LOCOMOTORSURFACE_CLIFF)) {
  2774. // Locomotors can go on ground, water & cliff, so all is zone 1.
  2775. return 1;
  2776. }
  2777. if (crusher) {
  2778. zone = m_crusherZones[zone];
  2779. }
  2780. if ( (acceptableSurfaces&LOCOMOTORSURFACE_GROUND) &&
  2781. (acceptableSurfaces&LOCOMOTORSURFACE_CLIFF)) {
  2782. // Locomotors can go on ground & cliff, so use the ground cliff combiner.
  2783. zone = m_groundCliffZones[zone];
  2784. return zone;
  2785. }
  2786. if ( (acceptableSurfaces&LOCOMOTORSURFACE_GROUND) &&
  2787. (acceptableSurfaces&LOCOMOTORSURFACE_WATER)) {
  2788. // Locomotors can go on ground & water, so use the ground water combiner.
  2789. zone = m_groundWaterZones[zone];
  2790. return zone;
  2791. }
  2792. if ( (acceptableSurfaces&LOCOMOTORSURFACE_GROUND) &&
  2793. (acceptableSurfaces&LOCOMOTORSURFACE_RUBBLE)) {
  2794. // Locomotors can go on ground & rubble, so use the ground rubble combiner.
  2795. zone = m_groundRubbleZones[zone];
  2796. return zone;
  2797. }
  2798. if ( (acceptableSurfaces&LOCOMOTORSURFACE_CLIFF) &&
  2799. (acceptableSurfaces&LOCOMOTORSURFACE_WATER)) {
  2800. // Locomotors can go on ground & cliff, so use the ground cliff combiner.
  2801. DEBUG_CRASH(("Cliff water only locomotor sets not supported yet."));
  2802. }
  2803. zone = m_hierarchicalZones[zone];
  2804. return zone;
  2805. }
  2806. //-------------------- PathfindLayer ----------------------------------------
  2807. PathfindLayer::PathfindLayer() : m_blockOfMapCells(NULL), m_layerCells(NULL), m_bridge(NULL),
  2808. // Added By Sadullah Nader
  2809. // Initializations inserted
  2810. m_destroyed(FALSE),
  2811. m_height(0),
  2812. m_width(0),
  2813. m_xOrigin(0),
  2814. m_yOrigin(0),
  2815. m_zone(0)
  2816. //
  2817. {
  2818. m_startCell.x = -1;
  2819. m_startCell.y = -1;
  2820. m_endCell.x = -1;
  2821. m_endCell.y = -1;
  2822. }
  2823. PathfindLayer::~PathfindLayer()
  2824. {
  2825. reset();
  2826. }
  2827. /**
  2828. * Returns true if the layer is avaialble for use.
  2829. */
  2830. void PathfindLayer::reset(void)
  2831. {
  2832. m_bridge = NULL;
  2833. if (m_layerCells) {
  2834. Int i, j;
  2835. for (i=0; i<m_width; i++) {
  2836. for (j=0; j<m_height; j++) {
  2837. PathfindCell *cell = &m_layerCells[i][j];
  2838. cell->reset();
  2839. }
  2840. }
  2841. delete [] m_layerCells;
  2842. m_layerCells = NULL;
  2843. }
  2844. if (m_blockOfMapCells) {
  2845. delete [] m_blockOfMapCells;
  2846. m_blockOfMapCells = NULL;
  2847. }
  2848. m_width = 0;
  2849. m_height = 0;
  2850. m_xOrigin = 0;
  2851. m_yOrigin = 0;
  2852. m_startCell.x = -1;
  2853. m_startCell.y = -1;
  2854. m_endCell.x = -1;
  2855. m_endCell.y = -1;
  2856. m_layer = LAYER_GROUND;
  2857. }
  2858. /**
  2859. * Returns true if the layer is avaialble for use.
  2860. */
  2861. Bool PathfindLayer::isUnused(void)
  2862. {
  2863. // Special case - wall layer is built from not a bridge. jba.
  2864. if (m_layer == LAYER_WALL && m_width>0) return false;
  2865. if (m_bridge==NULL) return true;
  2866. return false;
  2867. }
  2868. /**
  2869. * Draws debug cell info.
  2870. */
  2871. #if defined _DEBUG || defined _INTERNAL
  2872. void PathfindLayer::doDebugIcons(void) {
  2873. if (isUnused()) return;
  2874. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  2875. // render AI debug information
  2876. {
  2877. Coord3D topLeftCorner;
  2878. RGBColor color;
  2879. color.red = color.green = color.blue = 0;
  2880. Coord3D center;
  2881. center.x = (m_xOrigin+m_width/2)*PATHFIND_CELL_SIZE_F;
  2882. center.y = (m_yOrigin+m_height/2)*PATHFIND_CELL_SIZE_F;
  2883. center.z = 0;
  2884. Real bridgeHeight = TheTerrainLogic->getLayerHeight(center.x , center.y, m_layer);
  2885. if (m_layer == LAYER_WALL) {
  2886. bridgeHeight = TheAI->pathfinder()->getWallHeight();
  2887. }
  2888. static Int flash = 0;
  2889. flash--;
  2890. if (flash<1) flash = 20;
  2891. if (flash < 10) return;
  2892. Bool showCells = TheGlobalData->m_debugAI==AI_DEBUG_CELLS;
  2893. // show the pathfind grid
  2894. for( int j=0; j<m_height; j++ )
  2895. {
  2896. topLeftCorner.y = (Real)(j+m_yOrigin) * PATHFIND_CELL_SIZE_F;
  2897. for( int i=0; i<m_width; i++ )
  2898. {
  2899. topLeftCorner.x = (Real)(i+m_xOrigin) * PATHFIND_CELL_SIZE_F;
  2900. color.red = color.green = color.blue = 0;
  2901. Bool empty = false;
  2902. Real size = 0.4f;
  2903. const PathfindCell *cell = &m_layerCells[i][j];
  2904. if (cell)
  2905. {
  2906. if (cell->getConnectLayer()==LAYER_GROUND) {
  2907. color.green = 1;
  2908. color.blue = 1;
  2909. empty = false;
  2910. } else if (cell->getType() == PathfindCell::CELL_IMPASSABLE) {
  2911. color.red = color.green = color.blue = 1;
  2912. size = 0.2f;
  2913. empty = false;
  2914. } else if (cell->getType() == PathfindCell::CELL_BRIDGE_IMPASSABLE) {
  2915. color.blue = color.red = 1;
  2916. empty = false;
  2917. } else if (cell->getType() == PathfindCell::CELL_CLIFF) {
  2918. color.red = 1;
  2919. empty = false;
  2920. } else {
  2921. size = 0.2f;
  2922. }
  2923. }
  2924. if (showCells) {
  2925. empty = true;
  2926. color.red = color.green = color.blue = 0;
  2927. if (empty && cell) {
  2928. if (cell->getFlags()!=PathfindCell::NO_UNITS) {
  2929. empty = false;
  2930. if (cell->getFlags() == PathfindCell::UNIT_GOAL) {
  2931. color.red = 1;
  2932. } else if (cell->getFlags() == PathfindCell::UNIT_PRESENT_FIXED) {
  2933. color.green = color.blue = color.red = 1;
  2934. } else if (cell->getFlags() == PathfindCell::UNIT_PRESENT_MOVING) {
  2935. color.green = 1;
  2936. } else {
  2937. color.green = color.red = 1;
  2938. }
  2939. }
  2940. }
  2941. }
  2942. if (!empty) {
  2943. Coord3D loc;
  2944. loc.x = topLeftCorner.x + PATHFIND_CELL_SIZE_F/2.0f;
  2945. loc.y = topLeftCorner.y + PATHFIND_CELL_SIZE_F/2.0f;
  2946. loc.z = bridgeHeight;
  2947. addIcon(&loc, PATHFIND_CELL_SIZE_F*size, 99, color);
  2948. }
  2949. }
  2950. }
  2951. }
  2952. }
  2953. #endif
  2954. /**
  2955. * Sets the bridge & layer number for a layer.
  2956. */
  2957. Bool PathfindLayer::init(Bridge *theBridge, PathfindLayerEnum layer)
  2958. {
  2959. if (m_bridge!=NULL) return false;
  2960. m_bridge = theBridge;
  2961. m_layer = layer;
  2962. m_destroyed = false;
  2963. return true;
  2964. }
  2965. /**
  2966. * Allocates the pathfind cells for the bridge layer.
  2967. */
  2968. void PathfindLayer::allocateCells(const IRegion2D *extent)
  2969. {
  2970. if (m_bridge == NULL) return;
  2971. Region2D bridgeBounds = *m_bridge->getBounds();
  2972. Int maxX, maxY;
  2973. m_xOrigin = REAL_TO_INT_FLOOR((bridgeBounds.lo.x-PATHFIND_CELL_SIZE/100)/PATHFIND_CELL_SIZE);
  2974. m_yOrigin = REAL_TO_INT_FLOOR((bridgeBounds.lo.y-PATHFIND_CELL_SIZE/100)/PATHFIND_CELL_SIZE);
  2975. m_width = 0;
  2976. m_height = 0;
  2977. maxX = REAL_TO_INT_CEIL((bridgeBounds.hi.x+PATHFIND_CELL_SIZE/100)/PATHFIND_CELL_SIZE);
  2978. maxY = REAL_TO_INT_CEIL((bridgeBounds.hi.y+PATHFIND_CELL_SIZE/100)/PATHFIND_CELL_SIZE);
  2979. // Pad with 1 extra;
  2980. m_xOrigin--;
  2981. m_yOrigin--;
  2982. maxX++;
  2983. maxY++;
  2984. if (m_xOrigin < extent->lo.x) m_xOrigin = extent->lo.x;
  2985. if (m_yOrigin < extent->lo.y) m_yOrigin = extent->lo.y;
  2986. if (maxX > extent->hi.x) maxX = extent->hi.x;
  2987. if (maxY > extent->hi.y) maxY = extent->hi.y;
  2988. if (maxX <= m_xOrigin) return;
  2989. if (maxY <= m_yOrigin) return;
  2990. m_width = maxX - m_xOrigin;
  2991. m_height = maxY - m_yOrigin;
  2992. // Allocate cells.
  2993. // pool[]ify
  2994. m_blockOfMapCells = MSGNEW("PathfindMapCells") PathfindCell[m_width*m_height];
  2995. m_layerCells = MSGNEW("PathfindMapCells") PathfindCellP[m_width];
  2996. Int i;
  2997. for (i=0; i<m_width; i++) {
  2998. m_layerCells[i] = &m_blockOfMapCells[i*m_height];
  2999. }
  3000. }
  3001. /**
  3002. * Allocates the pathfind cells for the wall bridge layer.
  3003. */
  3004. void PathfindLayer::allocateCellsForWallLayer(const IRegion2D *extent, ObjectID *wallPieces, Int numPieces)
  3005. {
  3006. DEBUG_ASSERTCRASH(m_layer==LAYER_WALL, ("Wrong layer for wall."));
  3007. if (m_layer != LAYER_WALL) return;
  3008. Region2D bridgeBounds;
  3009. Int i;
  3010. Bool first = true;
  3011. for (i=0; i<numPieces; i++) {
  3012. Object *obj = TheGameLogic->findObjectByID(wallPieces[i]);
  3013. Region2D objBounds;
  3014. if (obj==NULL) continue;
  3015. obj->getGeometryInfo().get2DBounds(*obj->getPosition(), obj->getOrientation(), objBounds);
  3016. if (first) {
  3017. bridgeBounds = objBounds;
  3018. first = false;
  3019. } else {
  3020. if (bridgeBounds.lo.x>objBounds.lo.x) bridgeBounds.lo.x = objBounds.lo.x;
  3021. if (bridgeBounds.lo.y>objBounds.lo.y) bridgeBounds.lo.y = objBounds.lo.y;
  3022. if (bridgeBounds.hi.x<objBounds.hi.x) bridgeBounds.hi.x = objBounds.hi.x;
  3023. if (bridgeBounds.hi.y<objBounds.hi.y) bridgeBounds.hi.y = objBounds.hi.y;
  3024. }
  3025. }
  3026. Int maxX, maxY;
  3027. m_xOrigin = REAL_TO_INT_FLOOR((bridgeBounds.lo.x-PATHFIND_CELL_SIZE/100)/PATHFIND_CELL_SIZE);
  3028. m_yOrigin = REAL_TO_INT_FLOOR((bridgeBounds.lo.y-PATHFIND_CELL_SIZE/100)/PATHFIND_CELL_SIZE);
  3029. m_width = 0;
  3030. m_height = 0;
  3031. maxX = REAL_TO_INT_CEIL((bridgeBounds.hi.x+PATHFIND_CELL_SIZE/100)/PATHFIND_CELL_SIZE);
  3032. maxY = REAL_TO_INT_CEIL((bridgeBounds.hi.y+PATHFIND_CELL_SIZE/100)/PATHFIND_CELL_SIZE);
  3033. // Pad with 1 extra;
  3034. m_xOrigin--;
  3035. m_yOrigin--;
  3036. maxX++;
  3037. maxY++;
  3038. if (m_xOrigin < extent->lo.x) m_xOrigin = extent->lo.x;
  3039. if (m_yOrigin < extent->lo.y) m_yOrigin = extent->lo.y;
  3040. if (maxX > extent->hi.x) maxX = extent->hi.x;
  3041. if (maxY > extent->hi.y) maxY = extent->hi.y;
  3042. if (maxX <= m_xOrigin) return;
  3043. if (maxY <= m_yOrigin) return;
  3044. m_width = maxX - m_xOrigin;
  3045. m_height = maxY - m_yOrigin;
  3046. // Allocate cells.
  3047. m_blockOfMapCells = MSGNEW("PathfindMapCells") PathfindCell[m_width*m_height];
  3048. m_layerCells = MSGNEW("PathfindMapCells") PathfindCellP[m_width];
  3049. for (i=0; i<m_width; i++) {
  3050. m_layerCells[i] = &m_blockOfMapCells[i*m_height];
  3051. }
  3052. }
  3053. /**
  3054. * Checks to see if a broken bridge connects 2 zones.
  3055. */
  3056. Bool PathfindLayer::connectsZones(PathfindZoneManager *zm, const LocomotorSet& locoSet,
  3057. Int zone1, Int zone2)
  3058. {
  3059. if (!m_destroyed) {
  3060. return false;
  3061. }
  3062. Bool found1 = false;
  3063. Bool found2 = false;
  3064. Int i, j;
  3065. for (i=0; i<m_width; i++) {
  3066. for (j=0; j<m_height; j++) {
  3067. PathfindCell *cell = &m_layerCells[i][j];
  3068. if (cell->getConnectLayer()==LAYER_GROUND) {
  3069. PathfindCell *groundCell = TheAI->pathfinder()->getCell(LAYER_GROUND, i+m_xOrigin, j+m_yOrigin);
  3070. DEBUG_ASSERTCRASH(groundCell, ("Should have cell."));
  3071. if (groundCell) {
  3072. zoneStorageType zone = zm->getEffectiveZone(locoSet.getValidSurfaces(),
  3073. true, groundCell->getZone());
  3074. zone = zm->getEffectiveTerrainZone(zone);
  3075. if (zone == zone1) found1 = true;
  3076. if (zone == zone2) found2 = true;
  3077. }
  3078. }
  3079. }
  3080. }
  3081. return found1 && found2;
  3082. }
  3083. /**
  3084. * Classifies the pathfind cells for the bridge layer.
  3085. */
  3086. void PathfindLayer::classifyCells()
  3087. {
  3088. m_startCell.x = -1;
  3089. m_startCell.y = -1;
  3090. m_endCell.x = -1;
  3091. m_endCell.y = -1;
  3092. Int i, j;
  3093. for (i=0; i<m_width; i++) {
  3094. for (j=0; j<m_height; j++) {
  3095. PathfindCell *cell = &m_layerCells[i][j];
  3096. cell->setConnectLayer(LAYER_INVALID);
  3097. cell->setLayer(m_layer);
  3098. classifyLayerMapCell(i+m_xOrigin, j+m_yOrigin, cell, m_bridge);
  3099. }
  3100. BridgeInfo info;
  3101. m_bridge->getBridgeInfo(&info);
  3102. Coord3D bridgeDir = info.to;
  3103. bridgeDir.x -= info.from.x;
  3104. bridgeDir.y -= info.from.y;
  3105. bridgeDir.z -= info.from.z;
  3106. bridgeDir.normalize();
  3107. bridgeDir.x *= PATHFIND_CELL_SIZE_F*0.7f;
  3108. bridgeDir.y *= PATHFIND_CELL_SIZE_F*0.7f;
  3109. m_startCell.x = REAL_TO_INT_FLOOR((info.from.x-bridgeDir.x) / PATHFIND_CELL_SIZE_F);
  3110. m_startCell.y = REAL_TO_INT_FLOOR((info.from.y-bridgeDir.y) / PATHFIND_CELL_SIZE_F);
  3111. m_endCell.x = REAL_TO_INT_FLOOR((info.to.x+bridgeDir.x) / PATHFIND_CELL_SIZE_F);
  3112. m_endCell.y = REAL_TO_INT_FLOOR((info.to.y+bridgeDir.y) / PATHFIND_CELL_SIZE_F);
  3113. }
  3114. if (m_destroyed) {
  3115. Int i, j;
  3116. for (i=0; i<m_width; i++) {
  3117. for (j=0; j<m_height; j++) {
  3118. PathfindCell *cell = &m_layerCells[i][j];
  3119. if (cell->getConnectLayer() == LAYER_GROUND) {
  3120. PathfindCell *groundCell = TheAI->pathfinder()->getCell(LAYER_GROUND, i+m_xOrigin, j+m_yOrigin);
  3121. DEBUG_ASSERTCRASH(groundCell, ("Should have cell."));
  3122. if (groundCell) {
  3123. DEBUG_ASSERTCRASH(groundCell->getConnectLayer()==m_layer, ("Should connect to this layer.jba."));
  3124. groundCell->setConnectLayer(LAYER_INVALID); // disconnect it.
  3125. }
  3126. }
  3127. cell->setType(PathfindCell::CELL_BRIDGE_IMPASSABLE);
  3128. }
  3129. }
  3130. }
  3131. }
  3132. /**
  3133. * Classifies the pathfind cells for the wall bridge layer.
  3134. */
  3135. void PathfindLayer::classifyWallCells(ObjectID *wallPieces, Int numPieces)
  3136. {
  3137. DEBUG_ASSERTCRASH(m_layer==LAYER_WALL, ("Wrong layer for wall."));
  3138. if (m_layer != LAYER_WALL) return;
  3139. if (m_layerCells == NULL) return;
  3140. Int i, j;
  3141. for (i=0; i<m_width; i++) {
  3142. for (j=0; j<m_height; j++) {
  3143. PathfindCell *cell = &m_layerCells[i][j];
  3144. cell->setConnectLayer(LAYER_INVALID);
  3145. cell->setLayer(m_layer);
  3146. classifyWallMapCell(i+m_xOrigin, j+m_yOrigin, cell, wallPieces, numPieces);
  3147. cell->setPinched(false);
  3148. }
  3149. }
  3150. if (m_destroyed) {
  3151. Int i, j;
  3152. for (i=0; i<m_width; i++) {
  3153. for (j=0; j<m_height; j++) {
  3154. PathfindCell *cell = &m_layerCells[i][j];
  3155. if (cell->getConnectLayer() == LAYER_GROUND) {
  3156. PathfindCell *groundCell = TheAI->pathfinder()->getCell(LAYER_GROUND, i+m_xOrigin, j+m_yOrigin);
  3157. DEBUG_ASSERTCRASH(groundCell, ("Should have cell."));
  3158. if (groundCell) {
  3159. DEBUG_ASSERTCRASH(groundCell->getConnectLayer()==m_layer, ("Should connect to this layer.jba."));
  3160. groundCell->setConnectLayer(LAYER_INVALID); // disconnect it.
  3161. }
  3162. }
  3163. cell->setType(PathfindCell::CELL_IMPASSABLE);
  3164. }
  3165. }
  3166. }
  3167. // Tighten up 1 cell.
  3168. for (i=1; i<m_width-1; i++) {
  3169. for (j=1; j<m_height-1; j++) {
  3170. PathfindCell *cell = &m_layerCells[i][j];
  3171. Int k, l;
  3172. for (k=i-1; k<i+2; k++) {
  3173. for (l=j-1; l<j+2; l++) {
  3174. PathfindCell *adjacentCell = &m_layerCells[k][l];
  3175. if (adjacentCell->getType() != PathfindCell::CELL_CLEAR) {
  3176. cell->setPinched(true);
  3177. }
  3178. }
  3179. }
  3180. }
  3181. }
  3182. for (i=0; i<m_width; i++) {
  3183. for (j=0; j<m_height; j++) {
  3184. PathfindCell *cell = &m_layerCells[i][j];
  3185. if (cell->getPinched() && cell->getType() == PathfindCell::CELL_CLEAR) {
  3186. cell->setType(PathfindCell::CELL_CLIFF);
  3187. }
  3188. cell->setPinched(false);
  3189. }
  3190. }
  3191. }
  3192. /**
  3193. * Relassifies the pathfind cells for the destroyed bridge layer.
  3194. */
  3195. Bool PathfindLayer::setDestroyed(Bool destroyed)
  3196. {
  3197. if (destroyed == m_destroyed) return false;
  3198. m_destroyed = destroyed;
  3199. classifyCells();
  3200. return true;
  3201. }
  3202. /**
  3203. * Copies m_zone into the zone for all the member cells.
  3204. */
  3205. void PathfindLayer::applyZone( void )
  3206. {
  3207. Int i, j;
  3208. for (i=0; i<m_width; i++) {
  3209. for (j=0; j<m_height; j++) {
  3210. PathfindCell *cell = &m_layerCells[i][j];
  3211. cell->setZone(m_zone);
  3212. }
  3213. }
  3214. }
  3215. /**
  3216. * Return the bridge's object id.
  3217. */
  3218. ObjectID PathfindLayer::getBridgeID(void)
  3219. {
  3220. return m_bridge->peekBridgeInfo()->bridgeObjectID;
  3221. }
  3222. /**
  3223. * Return the cell at the index location.
  3224. */
  3225. PathfindCell *PathfindLayer::getCell(Int x, Int y)
  3226. {
  3227. DEBUG_ASSERTCRASH(m_layerCells, ("no data in layer, why get cells?"));
  3228. if (m_layerCells==NULL) {
  3229. return NULL;
  3230. }
  3231. x -= m_xOrigin;
  3232. y -= m_yOrigin;
  3233. if (x<0 || x>=m_width) return NULL;
  3234. if (y<0 || y>=m_height) return NULL;
  3235. PathfindCell *cell = &m_layerCells[x][y];
  3236. if (cell->getType() == PathfindCell::CELL_IMPASSABLE) {
  3237. return NULL; // Impassable cells are ignored.
  3238. }
  3239. return cell;
  3240. }
  3241. /**
  3242. * Classify the given map cell as clear, or not, etc.
  3243. */
  3244. void PathfindLayer::classifyLayerMapCell( Int i, Int j , PathfindCell *cell, Bridge *theBridge)
  3245. {
  3246. Coord3D topLeftCorner, bottomRightCorner;
  3247. topLeftCorner.y = (Real)j * PATHFIND_CELL_SIZE_F;
  3248. bottomRightCorner.y = topLeftCorner.y + PATHFIND_CELL_SIZE_F;
  3249. topLeftCorner.x = (Real)i * PATHFIND_CELL_SIZE_F;
  3250. bottomRightCorner.x = topLeftCorner.x + PATHFIND_CELL_SIZE_F;
  3251. Int bridgeCount = 0;
  3252. Coord3D pt;
  3253. if (theBridge->isPointOnBridge(&topLeftCorner) ) {
  3254. bridgeCount++;
  3255. }
  3256. pt = topLeftCorner;
  3257. pt.y = bottomRightCorner.y;
  3258. if (theBridge->isPointOnBridge(&pt) ) {
  3259. bridgeCount++;
  3260. }
  3261. if (theBridge->isPointOnBridge(&bottomRightCorner) ) {
  3262. bridgeCount++;
  3263. }
  3264. pt = topLeftCorner;
  3265. pt.x = bottomRightCorner.x;
  3266. if (theBridge->isPointOnBridge(&pt) ) {
  3267. bridgeCount++;
  3268. }
  3269. cell->reset( );
  3270. cell->setLayer(m_layer);
  3271. cell->setType(PathfindCell::CELL_IMPASSABLE);
  3272. if (bridgeCount == 4) {
  3273. cell->setType(PathfindCell::CELL_CLEAR);
  3274. } else {
  3275. if (bridgeCount!=0) {
  3276. cell->setType(PathfindCell::CELL_BRIDGE_IMPASSABLE); // it's off the bridge.
  3277. }
  3278. // check against the end lines.
  3279. Region2D cellBounds;
  3280. cellBounds.lo.x = topLeftCorner.x;
  3281. cellBounds.lo.y = topLeftCorner.y;
  3282. cellBounds.hi.x = bottomRightCorner.x;
  3283. cellBounds.hi.y = bottomRightCorner.y;
  3284. if (m_bridge->isCellOnSide(&cellBounds)) {
  3285. cell->setType(PathfindCell::CELL_BRIDGE_IMPASSABLE);
  3286. } else {
  3287. if (m_bridge->isCellOnEnd(&cellBounds)) {
  3288. cell->setType(PathfindCell::CELL_CLEAR);
  3289. }
  3290. if (m_bridge->isCellEntryPoint(&cellBounds)) {
  3291. cell->setType(PathfindCell::CELL_CLEAR);
  3292. cell->setConnectLayer(LAYER_GROUND);
  3293. PathfindCell *groundCell = TheAI->pathfinder()->getCell(LAYER_GROUND, i, j );
  3294. groundCell->setConnectLayer(cell->getLayer());
  3295. }
  3296. }
  3297. }
  3298. Coord3D center = topLeftCorner;
  3299. center.x += PATHFIND_CELL_SIZE/2;
  3300. center.y += PATHFIND_CELL_SIZE/2;
  3301. if (cell->getType()!=PathfindCell::CELL_IMPASSABLE) {
  3302. if (!(cell->getConnectLayer()==LAYER_GROUND) ) {
  3303. // Check for bridge clearance. If the ground isn't 1 pathfind cells below, mark impassable.
  3304. Real groundHeight = TheTerrainLogic->getLayerHeight( center.x, center.y, LAYER_GROUND );
  3305. Real bridgeHeight = theBridge->getBridgeHeight( &center, NULL );
  3306. if (groundHeight+LAYER_Z_CLOSE_ENOUGH_F > bridgeHeight) {
  3307. PathfindCell *groundCell = TheAI->pathfinder()->getCell(LAYER_GROUND,i, j);
  3308. if (!(groundCell->getType()==PathfindCell::CELL_OBSTACLE)) {
  3309. groundCell->setType(PathfindCell::CELL_BRIDGE_IMPASSABLE);
  3310. }
  3311. }
  3312. }
  3313. }
  3314. return;
  3315. }
  3316. Bool PathfindLayer::isPointOnWall(ObjectID *wallPieces, Int numPieces, const Coord3D *pt)
  3317. {
  3318. Int i;
  3319. for (i=0; i<numPieces; i++) {
  3320. Object *obj = TheGameLogic->findObjectByID(wallPieces[i]);
  3321. if (obj==NULL) continue;
  3322. Real major = obj->getGeometryInfo().getMajorRadius();
  3323. Real minor = (obj->getGeometryInfo().getGeomType() == GEOMETRY_SPHERE) ? obj->getGeometryInfo().getMajorRadius() : obj->getGeometryInfo().getMinorRadius();
  3324. Real c = (Real)Cos(-obj->getOrientation());
  3325. Real s = (Real)Sin(-obj->getOrientation());
  3326. // convert to a delta relative to rect ctr
  3327. Real ptx = pt->x - obj->getPosition()->x;
  3328. Real pty = pt->y - obj->getPosition()->y;
  3329. // inverse-rotate it to the right coord system
  3330. Real ptx_new = (Real)fabs(ptx*c - pty*s);
  3331. Real pty_new = (Real)fabs(ptx*s + pty*c);
  3332. if (ptx_new <= major && pty_new <= minor)
  3333. {
  3334. return true;
  3335. }
  3336. }
  3337. return false;
  3338. }
  3339. /**
  3340. * Classify the given map cell as clear, or not, etc.
  3341. */
  3342. void PathfindLayer::classifyWallMapCell( Int i, Int j , PathfindCell *cell, ObjectID *wallPieces, Int numPieces)
  3343. {
  3344. Coord3D topLeftCorner, bottomRightCorner;
  3345. topLeftCorner.y = (Real)j * PATHFIND_CELL_SIZE_F;
  3346. bottomRightCorner.y = topLeftCorner.y + PATHFIND_CELL_SIZE_F;
  3347. topLeftCorner.x = (Real)i * PATHFIND_CELL_SIZE_F;
  3348. bottomRightCorner.x = topLeftCorner.x + PATHFIND_CELL_SIZE_F;
  3349. Int bridgeCount = 0;
  3350. Coord3D pt;
  3351. if (isPointOnWall(wallPieces, numPieces, &topLeftCorner) ) {
  3352. bridgeCount++;
  3353. }
  3354. pt = topLeftCorner;
  3355. pt.y = bottomRightCorner.y;
  3356. if (isPointOnWall(wallPieces, numPieces, &pt) ) {
  3357. bridgeCount++;
  3358. }
  3359. if (isPointOnWall(wallPieces, numPieces, &bottomRightCorner) ) {
  3360. bridgeCount++;
  3361. }
  3362. pt = topLeftCorner;
  3363. pt.x = bottomRightCorner.x;
  3364. if (isPointOnWall(wallPieces, numPieces, &pt) ) {
  3365. bridgeCount++;
  3366. }
  3367. cell->reset( );
  3368. cell->setLayer(m_layer);
  3369. cell->setType(PathfindCell::CELL_IMPASSABLE);
  3370. if (bridgeCount == 4) {
  3371. cell->setType(PathfindCell::CELL_CLEAR);
  3372. } else {
  3373. if (bridgeCount!=0) {
  3374. cell->setType(PathfindCell::CELL_BRIDGE_IMPASSABLE); // it's off the bridge.
  3375. }
  3376. }
  3377. }
  3378. //----------------------- Pathfinder ---------------------------------------
  3379. Pathfinder::Pathfinder( void ) :m_map(NULL)
  3380. {
  3381. debugPath = NULL;
  3382. PathfindCellInfo::allocateCellInfos();
  3383. reset();
  3384. }
  3385. Pathfinder::~Pathfinder( void )
  3386. {
  3387. PathfindCellInfo::releaseCellInfos();
  3388. }
  3389. void Pathfinder::reset( void )
  3390. {
  3391. frameToShowObstacles = 0;
  3392. DEBUG_LOG(("Pathfind cell is %d bytes, PathfindCellInfo is %d bytes\n", sizeof(PathfindCell), sizeof(PathfindCellInfo)));
  3393. if (m_blockOfMapCells) {
  3394. delete []m_blockOfMapCells;
  3395. m_blockOfMapCells = NULL;
  3396. }
  3397. if (m_map) {
  3398. delete [] m_map;
  3399. m_map = NULL;
  3400. }
  3401. Int i;
  3402. for (i=0; i<=LAYER_LAST; i++) {
  3403. m_layers[i].reset();
  3404. }
  3405. // reset the pathfind grid
  3406. m_extent.lo.x=m_extent.lo.y=m_extent.hi.x=m_extent.hi.y=0;
  3407. m_logicalExtent.lo.x=m_logicalExtent.lo.y=m_logicalExtent.hi.x=m_logicalExtent.hi.y=0;
  3408. m_openList = NULL;
  3409. m_closedList = NULL;
  3410. m_ignoreObstacleID = INVALID_ID;
  3411. m_isTunneling = false;
  3412. m_moveAlliesDepth = 0;
  3413. // pathfind grid cells have not been classified yet
  3414. m_isMapReady = false;
  3415. m_cumulativeCellsAllocated = 0;
  3416. debugPathPos.x = 0.0f;
  3417. debugPathPos.y = 0.0f;
  3418. debugPathPos.z = 0.0f;
  3419. if (debugPath)
  3420. debugPath->deleteInstance();
  3421. debugPath = NULL;
  3422. m_frameToShowObstacles = 0;
  3423. for (m_queuePRHead=0; m_queuePRHead<PATHFIND_QUEUE_LEN; m_queuePRHead++) {
  3424. m_queuedPathfindRequests[m_queuePRHead] = INVALID_ID;
  3425. }
  3426. m_queuePRHead = 0;
  3427. m_queuePRTail = 0;
  3428. m_numWallPieces = 0;
  3429. for (i=0; i<MAX_WALL_PIECES; ++i)
  3430. {
  3431. m_wallPieces[i] = INVALID_ID;
  3432. }
  3433. if (TheAI && TheAI->getAiData()) {
  3434. m_wallHeight = TheAI->getAiData()->m_wallHeight;
  3435. }
  3436. else
  3437. {
  3438. m_wallHeight = 0.0f;
  3439. }
  3440. m_zoneManager.reset();
  3441. }
  3442. /**
  3443. * Adds a piece of a wall.
  3444. */
  3445. void Pathfinder::addWallPiece(Object *wallPiece)
  3446. {
  3447. if (m_numWallPieces<MAX_WALL_PIECES-1) {
  3448. m_wallPieces[m_numWallPieces] = wallPiece->getID();
  3449. m_numWallPieces++;
  3450. }
  3451. }
  3452. /**
  3453. * Removes a piece of a wall
  3454. */
  3455. void Pathfinder::removeWallPiece(Object *wallPiece)
  3456. {
  3457. // sanity
  3458. if( wallPiece == NULL )
  3459. return;
  3460. // find entry
  3461. for( Int i = 0; i < m_numWallPieces; ++i )
  3462. {
  3463. // match by id
  3464. if( m_wallPieces[ i ] == wallPiece->getID() )
  3465. {
  3466. // put the last id in the wall piece array here
  3467. m_wallPieces[ i ] = m_wallPieces[ m_numWallPieces - 1 ];
  3468. // we now have one less entry
  3469. m_numWallPieces--;
  3470. // all done
  3471. return;
  3472. } // end if
  3473. } // end for, i
  3474. } // end removeWallPiece
  3475. /**
  3476. * Checks if a point is on the wall.
  3477. */
  3478. Bool Pathfinder::isPointOnWall(const Coord3D *pos)
  3479. {
  3480. if (m_numWallPieces==0) return false;
  3481. if (m_layers[LAYER_WALL].isUnused()) return false;
  3482. PathfindLayerEnum layer = (PathfindLayerEnum)LAYER_WALL;
  3483. PathfindCell *cell = getCell(layer, pos);
  3484. // make sure the layer matches, since getCell can return ground layer cells if the pos is 'off' the bridge/wall
  3485. if (cell && cell->getLayer() == layer) {
  3486. if (cell->getType() == PathfindCell::CELL_CLEAR) {
  3487. return true;
  3488. }
  3489. }
  3490. return false;
  3491. }
  3492. /**
  3493. * Adds a bridge & returns the layer.
  3494. */
  3495. PathfindLayerEnum Pathfinder::addBridge(Bridge *theBridge)
  3496. {
  3497. Int layer = LAYER_GROUND+1;
  3498. while (layer<=LAYER_WALL) {
  3499. if (m_layers[layer].isUnused()) {
  3500. if (m_layers[layer].init(theBridge, (PathfindLayerEnum)layer) ) {
  3501. return (PathfindLayerEnum)layer;
  3502. }
  3503. DEBUG_LOG(("WARNING: Bridge failed to init in pathfinder\n"));
  3504. return LAYER_GROUND; // failed to init, usually cause off of the map. jba.
  3505. }
  3506. layer++;
  3507. }
  3508. DEBUG_CRASH(("Ran out of bridge layers."));
  3509. return LAYER_GROUND;
  3510. }
  3511. /**
  3512. * Updates an object's layer, making sure the object is actually on the bridge first.
  3513. */
  3514. void Pathfinder::updateLayer(Object *obj, PathfindLayerEnum layer)
  3515. {
  3516. if (layer != LAYER_GROUND) {
  3517. if (!TheTerrainLogic->objectInteractsWithBridgeLayer(obj, layer)) {
  3518. layer = LAYER_GROUND;
  3519. }
  3520. }
  3521. //DEBUG_LOG(("Object layer is %d\n", layer));
  3522. obj->setLayer(layer);
  3523. }
  3524. /**
  3525. * Classify the cells under the given object
  3526. * If 'insert' is true, object is being added
  3527. * If 'insert' is false, object is being removed
  3528. */
  3529. void Pathfinder::classifyFence( Object *obj, Bool insert )
  3530. {
  3531. const Coord3D *pos = obj->getPosition();
  3532. Real angle = obj->getOrientation();
  3533. Real halfsizeX = obj->getTemplate()->getFenceWidth()/2;
  3534. Real halfsizeY = PATHFIND_CELL_SIZE_F/10.0f;
  3535. Real fenceOffset = obj->getTemplate()->getFenceXOffset();
  3536. Real c = (Real)Cos(angle);
  3537. Real s = (Real)Sin(angle);
  3538. const Real STEP_SIZE = PATHFIND_CELL_SIZE_F * 0.5f; // in theory, should be PATHFIND_CELL_SIZE_F exactly, but needs to be smaller to avoid aliasing problems
  3539. Real ydx = s * STEP_SIZE;
  3540. Real ydy = -c * STEP_SIZE;
  3541. Real xdx = c * STEP_SIZE;
  3542. Real xdy = s * STEP_SIZE;
  3543. Int numStepsX = REAL_TO_INT_CEIL(2.0f * halfsizeX / STEP_SIZE);
  3544. Int numStepsY = REAL_TO_INT_CEIL(2.0f * halfsizeY / STEP_SIZE);
  3545. Real tl_x = pos->x - fenceOffset*c - halfsizeY*s;
  3546. Real tl_y = pos->y + halfsizeY*c - fenceOffset*s;
  3547. IRegion2D cellBounds;
  3548. cellBounds.lo.x = REAL_TO_INT_FLOOR((pos->x + 0.5f)/PATHFIND_CELL_SIZE_F);
  3549. cellBounds.lo.y = REAL_TO_INT_FLOOR((pos->y + 0.5f)/PATHFIND_CELL_SIZE_F);
  3550. Bool didAnything = false;
  3551. for (Int iy = 0; iy < numStepsY; ++iy, tl_x += ydx, tl_y += ydy)
  3552. {
  3553. Real x = tl_x;
  3554. Real y = tl_y;
  3555. for (Int ix = 0; ix < numStepsX; ++ix, x += xdx, y += xdy)
  3556. {
  3557. Int cx = REAL_TO_INT_FLOOR((x + 0.5f)/PATHFIND_CELL_SIZE_F);
  3558. Int cy = REAL_TO_INT_FLOOR((y + 0.5f)/PATHFIND_CELL_SIZE_F);
  3559. if (cx >= 0 && cy >= 0 && cx < m_extent.hi.x && cy < m_extent.hi.y)
  3560. {
  3561. if (insert) {
  3562. ICoord2D pos;
  3563. pos.x = cx;
  3564. pos.y = cy;
  3565. if (m_map[cx][cy].setTypeAsObstacle( obj, true, pos )) {
  3566. didAnything = true;
  3567. m_map[cx][cy].setZone(PathfindZoneManager::UNINITIALIZED_ZONE);
  3568. }
  3569. }
  3570. else {
  3571. if (m_map[cx][cy].removeObstacle(obj)) {
  3572. didAnything = true;
  3573. m_map[cx][cy].setZone(PathfindZoneManager::UNINITIALIZED_ZONE);
  3574. }
  3575. }
  3576. if (cellBounds.lo.x>cx) cellBounds.lo.x = cx;
  3577. if (cellBounds.lo.y>cy) cellBounds.lo.y = cy;
  3578. if (cellBounds.hi.x<cx) cellBounds.hi.x = cx;
  3579. if (cellBounds.hi.y<cy) cellBounds.hi.y = cy;
  3580. }
  3581. }
  3582. }
  3583. if (didAnything) {
  3584. m_zoneManager.markZonesDirty( insert );
  3585. m_zoneManager.updateZonesForModify(m_map, m_layers, cellBounds, m_extent);
  3586. }
  3587. #if 0
  3588. // Perhaps it would make more sense to use the iteratecellsalongpath() provided in this class,
  3589. // but this way works well and is very traceable
  3590. // neither one is true Bresenham
  3591. // this one assumes zero fence thickness
  3592. const Coord3D *fencePos = obj->getPosition();
  3593. Coord3D fenceNorm;
  3594. obj->getUnitDirectionVector2D( fenceNorm );
  3595. Coord3D halfLength = fenceNorm;
  3596. halfLength.scale( obj->getGeometryInfo().getMajorRadius() );
  3597. Coord3D head = *fencePos;
  3598. head.add( &halfLength );
  3599. Coord3D tail = *fencePos;
  3600. tail.sub( &halfLength );
  3601. Real stepLength = 1.0f / ((halfLength.length()*2.0f) / PATHFIND_CELL_SIZE_F);
  3602. for ( Real t = 0.0f; t <= 1.0f; t += stepLength )
  3603. {
  3604. Real lengthWalk_x = (head.x * t) + (tail.x * (1.0f-t));
  3605. Real lengthWalk_y = (head.y * t) + (tail.y * (1.0f-t));
  3606. Int cx = REAL_TO_INT_FLOOR( lengthWalk_x / PATHFIND_CELL_SIZE_F );
  3607. Int cy = REAL_TO_INT_FLOOR( lengthWalk_y / PATHFIND_CELL_SIZE_F );
  3608. if (cx >= 0 && cy >= 0 && cx < m_extent.hi.x && cy < m_extent.hi.y)
  3609. {
  3610. if (insert) {
  3611. ICoord2D pos = { cx, cy };
  3612. m_map[cx][cy].setTypeAsObstacle( obj, true, pos );
  3613. }
  3614. else
  3615. m_map[cx][cy].removeObstacle(obj);
  3616. }
  3617. }
  3618. #endif
  3619. }
  3620. /**
  3621. * Classify the cells under the given object
  3622. * If 'insert' is true, object is being added
  3623. * If 'insert' is false, object is being removed
  3624. */
  3625. void Pathfinder::classifyObjectFootprint( Object *obj, Bool insert )
  3626. {
  3627. if (obj->isKindOf(KINDOF_MINE)) {
  3628. return; // don't pathfind around mines.
  3629. }
  3630. if (obj->isKindOf(KINDOF_PROJECTILE)) {
  3631. return; // don't care about projectiles.
  3632. }
  3633. if (obj->isKindOf(KINDOF_BRIDGE_TOWER)) {
  3634. return; // It is important to not abuse bridge towers.
  3635. }
  3636. if (obj->getTemplate()->getFenceWidth() > 0.0f)
  3637. {
  3638. if (!obj->isKindOf(KINDOF_DEFENSIVE_WALL))
  3639. {
  3640. classifyFence(obj, insert);
  3641. return;
  3642. }
  3643. }
  3644. if (!insert) {
  3645. // Just in case, remove the object. Remove checks that the object has been added before
  3646. // removing, so it's safer to just remove it, as by the time some units "die", they've become
  3647. // lifeless immobile husks of debris, but we still need to remove them. jba.
  3648. if ( obj->isKindOf( KINDOF_BLAST_CRATER ) ) // since these footprints are permanent, never remove them
  3649. return;
  3650. removeUnitFromPathfindMap(obj);
  3651. if (obj->isKindOf(KINDOF_WALK_ON_TOP_OF_WALL)) {
  3652. if (!m_layers[LAYER_WALL].isUnused()) {
  3653. Int i;
  3654. ObjectID curID = obj->getID();
  3655. for (i=0; i<m_numWallPieces; i++) {
  3656. if (curID == m_wallPieces[i]) {
  3657. m_wallPieces[i]=INVALID_ID;
  3658. }
  3659. }
  3660. // Kill anybody on the wall.
  3661. Object *obj;
  3662. for (obj = TheGameLogic->getFirstObject(); obj; obj=obj->getNextObject()) {
  3663. if (obj->getLayer() == LAYER_WALL) {
  3664. if (m_layers[LAYER_WALL].isPointOnWall(&curID, 1, obj->getPosition()))
  3665. {
  3666. // The object fell off the wall.
  3667. // Destroy it.
  3668. DamageInfo extraDamageInfo;
  3669. extraDamageInfo.in.m_damageType = DAMAGE_FALLING;
  3670. extraDamageInfo.in.m_deathType = DEATH_SPLATTED;
  3671. extraDamageInfo.in.m_sourceID = obj->getID();
  3672. extraDamageInfo.in.m_amount = HUGE_DAMAGE_AMOUNT;
  3673. obj->attemptDamage(&extraDamageInfo);
  3674. }
  3675. }
  3676. }
  3677. // recalc the wall.
  3678. m_layers[LAYER_WALL].classifyWallCells(m_wallPieces, m_numWallPieces);
  3679. }
  3680. }
  3681. }
  3682. if (!obj->isKindOf(KINDOF_STRUCTURE)) {
  3683. return; // Only path around structures.
  3684. }
  3685. if (obj->isMobile()) {
  3686. return; // mobile aren't obstacles.
  3687. }
  3688. /// For now, all small objects will not be obstacles
  3689. if (obj->getGeometryInfo().getIsSmall()) {
  3690. return;
  3691. }
  3692. if (obj->getHeightAboveTerrain() > PATHFIND_CELL_SIZE_F && ( ! obj->isKindOf( KINDOF_BLAST_CRATER ) ) )
  3693. {
  3694. return; // Don't add bounds that are up in the air.... unless a blast crater wants to do just that
  3695. }
  3696. internal_classifyObjectFootprint(obj, insert);
  3697. }
  3698. void Pathfinder::internal_classifyObjectFootprint( Object *obj, Bool insert )
  3699. {
  3700. IRegion2D cellBounds;
  3701. const Coord3D *pos = obj->getPosition();
  3702. cellBounds.lo.x = REAL_TO_INT_FLOOR((pos->x + 0.5f)/PATHFIND_CELL_SIZE_F);
  3703. cellBounds.lo.y = REAL_TO_INT_FLOOR((pos->y + 0.5f)/PATHFIND_CELL_SIZE_F);
  3704. cellBounds.hi = cellBounds.lo;
  3705. switch(obj->getGeometryInfo().getGeomType())
  3706. {
  3707. case GEOMETRY_BOX:
  3708. {
  3709. m_zoneManager.markZonesDirty( insert );
  3710. Real angle = obj->getOrientation();
  3711. Real halfsizeX = obj->getGeometryInfo().getMajorRadius();
  3712. Real halfsizeY = obj->getGeometryInfo().getMinorRadius();
  3713. Real c = (Real)Cos(angle);
  3714. Real s = (Real)Sin(angle);
  3715. const Real STEP_SIZE = PATHFIND_CELL_SIZE_F * 0.5f; // in theory, should be PATHFIND_CELL_SIZE_F exactly, but needs to be smaller to avoid aliasing problems
  3716. Real ydx = s * STEP_SIZE;
  3717. Real ydy = -c * STEP_SIZE;
  3718. Real xdx = c * STEP_SIZE;
  3719. Real xdy = s * STEP_SIZE;
  3720. Int numStepsX = REAL_TO_INT_CEIL(2.0f * halfsizeX / STEP_SIZE);
  3721. Int numStepsY = REAL_TO_INT_CEIL(2.0f * halfsizeY / STEP_SIZE);
  3722. Real tl_x = pos->x - halfsizeX*c - halfsizeY*s;
  3723. Real tl_y = pos->y + halfsizeY*c - halfsizeX*s;
  3724. for (Int iy = 0; iy < numStepsY; ++iy, tl_x += ydx, tl_y += ydy)
  3725. {
  3726. Real x = tl_x;
  3727. Real y = tl_y;
  3728. for (Int ix = 0; ix < numStepsX; ++ix, x += xdx, y += xdy)
  3729. {
  3730. Int cx = REAL_TO_INT_FLOOR((x + 0.5f)/PATHFIND_CELL_SIZE_F);
  3731. Int cy = REAL_TO_INT_FLOOR((y + 0.5f)/PATHFIND_CELL_SIZE_F);
  3732. if (cx >= 0 && cy >= 0 && cx < m_extent.hi.x && cy < m_extent.hi.y)
  3733. {
  3734. if (insert) {
  3735. ICoord2D pos;
  3736. pos.x = cx;
  3737. pos.y = cy;
  3738. if (m_map[cx][cy].setTypeAsObstacle( obj, false, pos )) {
  3739. m_map[cx][cy].setZone(PathfindZoneManager::UNINITIALIZED_ZONE);
  3740. }
  3741. }
  3742. else {
  3743. if (m_map[cx][cy].removeObstacle(obj)) {
  3744. m_map[cx][cy].setZone(PathfindZoneManager::UNINITIALIZED_ZONE);
  3745. }
  3746. }
  3747. if (cellBounds.lo.x>cx) cellBounds.lo.x = cx;
  3748. if (cellBounds.lo.y>cy) cellBounds.lo.y = cy;
  3749. if (cellBounds.hi.x<cx) cellBounds.hi.x = cx;
  3750. if (cellBounds.hi.y<cy) cellBounds.hi.y = cy;
  3751. }
  3752. }
  3753. }
  3754. }
  3755. break;
  3756. case GEOMETRY_SPHERE: // not quite right, but close enough
  3757. case GEOMETRY_CYLINDER:
  3758. {
  3759. m_zoneManager.markZonesDirty( insert );
  3760. // fill in all cells that overlap as obstacle cells
  3761. /// @todo This is a very inefficient circle-rasterizer
  3762. ICoord2D topLeft, bottomRight;
  3763. Coord2D center, delta;
  3764. Real radius = obj->getGeometryInfo().getMajorRadius();
  3765. Real r2, size;
  3766. topLeft.x = REAL_TO_INT_FLOOR(0.5f + (pos->x - radius)/PATHFIND_CELL_SIZE_F)-1;
  3767. topLeft.y = REAL_TO_INT_FLOOR(0.5f + (pos->y - radius)/PATHFIND_CELL_SIZE_F)-1;
  3768. size = (radius/PATHFIND_CELL_SIZE_F);
  3769. center.x = (pos->x/PATHFIND_CELL_SIZE_F);
  3770. center.y = (pos->y/PATHFIND_CELL_SIZE_F);
  3771. size += 0.4f;
  3772. r2 = size*size;
  3773. bottomRight.x = topLeft.x + 2*size + 2;
  3774. bottomRight.y = topLeft.y + 2*size + 2;
  3775. for( int j = topLeft.y; j < bottomRight.y; j++ )
  3776. {
  3777. for( int i = topLeft.x; i < bottomRight.x; i++ )
  3778. {
  3779. delta.x = i+0.5f - center.x;
  3780. delta.y = j+0.5f - center.y;
  3781. if (delta.x*delta.x + delta.y*delta.y <= r2)
  3782. {
  3783. if (i >= 0 && j >= 0 && i < m_extent.hi.x && j < m_extent.hi.y)
  3784. {
  3785. if (insert) {
  3786. ICoord2D pos;
  3787. pos.x = i;
  3788. pos.y = j;
  3789. if (m_map[i][j].setTypeAsObstacle( obj, false, pos )) {
  3790. m_map[i][j].setZone(PathfindZoneManager::UNINITIALIZED_ZONE);
  3791. }
  3792. }
  3793. else {
  3794. if (m_map[i][j].removeObstacle(obj)) {
  3795. m_map[i][j].setZone(PathfindZoneManager::UNINITIALIZED_ZONE);
  3796. }
  3797. }
  3798. if (cellBounds.lo.x>i) cellBounds.lo.x = i;
  3799. if (cellBounds.lo.y>j) cellBounds.lo.y = j;
  3800. if (cellBounds.hi.x<i) cellBounds.hi.x = i;
  3801. if (cellBounds.hi.y<j) cellBounds.hi.y = j;
  3802. }
  3803. }
  3804. } // for i
  3805. } // for j
  3806. } // cylinder
  3807. break;
  3808. } // switch
  3809. m_zoneManager.updateZonesForModify(m_map, m_layers, cellBounds, m_extent);
  3810. Int i, j;
  3811. cellBounds.lo.x -= 2;
  3812. cellBounds.lo.y -= 2;
  3813. cellBounds.hi.x += 2;
  3814. cellBounds.hi.y += 2;
  3815. if (cellBounds.lo.x < m_extent.lo.x) {
  3816. cellBounds.lo.x = m_extent.lo.x;
  3817. }
  3818. if (cellBounds.lo.y < m_extent.lo.y) {
  3819. cellBounds.lo.y = m_extent.lo.y;
  3820. }
  3821. if (cellBounds.lo.y < m_extent.lo.y) {
  3822. cellBounds.lo.y = m_extent.lo.y;
  3823. }
  3824. if (cellBounds.hi.x > m_extent.hi.x) {
  3825. cellBounds.hi.x = m_extent.hi.x;
  3826. }
  3827. if (cellBounds.hi.y > m_extent.hi.y) {
  3828. cellBounds.hi.y = m_extent.hi.y;
  3829. }
  3830. // Expand building bounds 1 cell.
  3831. #define no_EXPAND_ONE_CELL
  3832. #ifdef EXPAND_ONE_CELL
  3833. for( j=cellBounds.lo.y; j<=cellBounds.hi.y; j++ )
  3834. {
  3835. for( i=cellBounds.lo.x; i<=cellBounds.hi.x; i++ )
  3836. {
  3837. if (!insert) {
  3838. if (m_map[i][j].getType() == PathfindCell::CELL_IMPASSABLE) {
  3839. m_map[i][j].setType(PathfindCell::CELL_CLEAR);
  3840. }
  3841. m_map[i][j].setPinched(false);
  3842. }
  3843. if (!insert) {
  3844. if (m_map[i][j].isObstaclePresent(obj->getID())) {
  3845. m_map[i][j].removeObstacle( obj );
  3846. }
  3847. continue;
  3848. }
  3849. if (m_map[i][j].getType() == PathfindCell::CELL_CLEAR) {
  3850. Bool obstacleAdjacent = false;
  3851. Int k, l;
  3852. for (k=i-1; k<i+2; k++) {
  3853. if (k<m_extent.lo.x || k> m_extent.hi.x) continue;
  3854. for (l=j-1; l<j+2; l++) {
  3855. if (l<m_extent.lo.y || l> m_extent.hi.y) continue;
  3856. if ((k==i) && (l==j)) continue;
  3857. if ((k!=i) && (l!=j)) continue;
  3858. if (m_map[k][l].getType()!=PathfindCell::CELL_CLEAR)) {
  3859. objectAdjacent = true;
  3860. break;
  3861. }
  3862. }
  3863. }
  3864. if (obstacleAdjacent) {
  3865. m_map[i][j].setPinched(true);
  3866. }
  3867. // If the total open cells are < 2
  3868. }
  3869. }
  3870. }
  3871. if (insert) {
  3872. for( j=cellBounds.lo.y; j<=cellBounds.hi.y; j++ )
  3873. {
  3874. for( i=cellBounds.lo.x; i<=cellBounds.hi.x; i++ )
  3875. {
  3876. if (m_map[i][j].getPinched() && m_map[i][j].getType() == PathfindCell::CELL_CLEAR) {
  3877. ICoord2D pos;
  3878. pos.x = i;
  3879. pos.y = j;
  3880. m_map[i][j].setTypeAsObstacle( obj, false, pos );
  3881. //m_map[i][j].setType(PathfindCell::CELL_CLIFF);
  3882. m_map[i][j].setPinched(false);
  3883. }
  3884. }
  3885. }
  3886. }
  3887. #endif
  3888. if (!insert) {
  3889. for( j=cellBounds.lo.y; j<=cellBounds.hi.y; j++ )
  3890. {
  3891. for( i=cellBounds.lo.x; i<=cellBounds.hi.x; i++ )
  3892. {
  3893. if (m_map[i][j].getType()==PathfindCell::CELL_IMPASSABLE) {
  3894. m_map[i][j].setType(PathfindCell::CELL_CLEAR);
  3895. }
  3896. }
  3897. }
  3898. }
  3899. // Check for pinched cells, and close them off.
  3900. for( j=cellBounds.lo.y; j<=cellBounds.hi.y; j++ )
  3901. {
  3902. for( i=cellBounds.lo.x; i<=cellBounds.hi.x; i++ )
  3903. {
  3904. m_map[i][j].setPinched(false);
  3905. if (m_map[i][j].getType() == PathfindCell::CELL_CLEAR) {
  3906. Int totalCount = 0;
  3907. Int orthogonalCount = 0;
  3908. Int k, l;
  3909. for (k=i-1; k<i+2; k++) {
  3910. if (k<m_extent.lo.x || k> m_extent.hi.x) continue;
  3911. for (l=j-1; l<j+2; l++) {
  3912. if (l<m_extent.lo.y || l> m_extent.hi.y) continue;
  3913. if ((k==i) && (j==l)) continue;
  3914. if (m_map[k][l].getType() == PathfindCell::CELL_CLEAR) {
  3915. totalCount++;
  3916. if ((k==i) || (l==j)) {
  3917. orthogonalCount++;
  3918. }
  3919. }
  3920. }
  3921. }
  3922. // If the total open cells are < 2 or total cells < 4, we are pinched.
  3923. if (orthogonalCount<2 || totalCount<4) {
  3924. m_map[i][j].setPinched(true);
  3925. }
  3926. }
  3927. }
  3928. }
  3929. for( j=cellBounds.lo.y; j<=cellBounds.hi.y; j++ )
  3930. {
  3931. for( i=cellBounds.lo.x; i<=cellBounds.hi.x; i++ )
  3932. {
  3933. if (m_map[i][j].getPinched() && (m_map[i][j].getType() == PathfindCell::CELL_CLEAR)) {
  3934. m_map[i][j].setType(PathfindCell::CELL_IMPASSABLE);
  3935. m_map[i][j].setPinched(false);
  3936. }
  3937. }
  3938. }
  3939. // Expand building bounds 1 cell.
  3940. #define MARK_BORDER_PINCHED
  3941. #ifdef MARK_BORDER_PINCHED
  3942. for( j=cellBounds.lo.y; j<=cellBounds.hi.y; j++ )
  3943. {
  3944. for( i=cellBounds.lo.x; i<=cellBounds.hi.x; i++ )
  3945. {
  3946. if (m_map[i][j].getType() == PathfindCell::CELL_CLEAR) {
  3947. Bool objectAdjacent = false;
  3948. Int k, l;
  3949. for (k=i-1; k<i+2; k++) {
  3950. if (k<m_extent.lo.x || k> m_extent.hi.x) continue;
  3951. for (l=j-1; l<j+2; l++) {
  3952. if (l<m_extent.lo.y || l> m_extent.hi.y) continue;
  3953. if ((k==i) && (l==j)) continue;
  3954. if ((k!=i) && (l!=j)) continue;
  3955. if (m_map[k][l].getType() == PathfindCell::CELL_OBSTACLE) {
  3956. objectAdjacent = true;
  3957. break;
  3958. }
  3959. }
  3960. }
  3961. if (objectAdjacent) {
  3962. m_map[i][j].setPinched(true);
  3963. }
  3964. }
  3965. }
  3966. }
  3967. #endif
  3968. }
  3969. /**
  3970. * Classify the given map cell as WATER, CLIFF, etc.
  3971. * Note that this does NOT classify cells as OBSTACLES.
  3972. * OBSTACLE cells are classified only via objects.
  3973. * @todo optimize this - lots of redundant computation
  3974. */
  3975. void Pathfinder::classifyMapCell( Int i, Int j , PathfindCell *cell)
  3976. {
  3977. Coord3D topLeftCorner, bottomRightCorner;
  3978. Bool hasObstacle = (cell->getType() == PathfindCell::CELL_OBSTACLE) ;
  3979. topLeftCorner.y = (Real)j * PATHFIND_CELL_SIZE_F;
  3980. bottomRightCorner.y = topLeftCorner.y + PATHFIND_CELL_SIZE_F;
  3981. topLeftCorner.x = (Real)i * PATHFIND_CELL_SIZE_F;
  3982. bottomRightCorner.x = topLeftCorner.x + PATHFIND_CELL_SIZE_F;
  3983. cell->setPinched(false);
  3984. PathfindCell::CellType type = PathfindCell::CELL_CLEAR;
  3985. if (TheTerrainLogic->isCliffCell(topLeftCorner.x, topLeftCorner.y))
  3986. {
  3987. type = PathfindCell::CELL_CLIFF;
  3988. }
  3989. //
  3990. // If any corners are underwater, this is a water cell
  3991. //
  3992. if (TheTerrainLogic->isUnderwater( topLeftCorner.x, topLeftCorner.y ) ) type = PathfindCell::CELL_WATER;
  3993. if (TheTerrainLogic->isUnderwater( topLeftCorner.x, bottomRightCorner.y) ) type = PathfindCell::CELL_WATER;
  3994. if (TheTerrainLogic->isUnderwater( bottomRightCorner.x, bottomRightCorner.y ) ) type = PathfindCell::CELL_WATER;
  3995. if (TheTerrainLogic->isUnderwater( bottomRightCorner.x, topLeftCorner.y ) ) type = PathfindCell::CELL_WATER;
  3996. if (hasObstacle) {
  3997. type = PathfindCell::CELL_OBSTACLE;
  3998. }
  3999. cell->setType( type );
  4000. cell->releaseInfo();
  4001. }
  4002. /**
  4003. * Set up for a new map.
  4004. */
  4005. void Pathfinder::newMap( void )
  4006. {
  4007. m_wallHeight = TheAI->getAiData()->m_wallHeight; // may be updated by map.ini.
  4008. Region3D terrainExtent;
  4009. TheTerrainLogic->getMaximumPathfindExtent( &terrainExtent );
  4010. IRegion2D bounds;
  4011. bounds.lo.x = REAL_TO_INT_FLOOR(terrainExtent.lo.x / PATHFIND_CELL_SIZE_F);
  4012. bounds.hi.x = REAL_TO_INT_FLOOR(terrainExtent.hi.x / PATHFIND_CELL_SIZE_F);
  4013. bounds.lo.y = REAL_TO_INT_FLOOR(terrainExtent.lo.y / PATHFIND_CELL_SIZE_F);
  4014. bounds.hi.y = REAL_TO_INT_FLOOR(terrainExtent.hi.y / PATHFIND_CELL_SIZE_F);
  4015. bounds.hi.x--;
  4016. bounds.hi.y--;
  4017. Bool dataAllocated = false;
  4018. if (m_extent.hi.x==bounds.hi.x && m_extent.hi.y==bounds.hi.y) {
  4019. if (m_blockOfMapCells != NULL && m_map!=NULL) {
  4020. dataAllocated = true;
  4021. }
  4022. }
  4023. // For map load from file, we have to call newMap twice to do sequencing issues.
  4024. // so the second time through, dataAllocated==TRUE, so we skip the allocate.
  4025. if (!dataAllocated) {
  4026. m_extent = bounds;
  4027. DEBUG_ASSERTCRASH(m_map == NULL, ("Can't reallocate pathfind cells."));
  4028. m_zoneManager.allocateBlocks(m_extent);
  4029. // Allocate cells.
  4030. m_blockOfMapCells = MSGNEW("PathfindMapCells") PathfindCell[(bounds.hi.x+1)*(bounds.hi.y+1)];
  4031. m_map = MSGNEW("PathfindMapCells") PathfindCellP[bounds.hi.x+1];
  4032. Int i;
  4033. for (i=0; i<=bounds.hi.x; i++) {
  4034. m_map[i] = &m_blockOfMapCells[i*(bounds.hi.y+1)];
  4035. }
  4036. for (i=0; i<LAYER_LAST; i++) {
  4037. if (!m_layers[i].isUnused()) {
  4038. m_layers[i].allocateCells(&m_extent);
  4039. }
  4040. }
  4041. if (m_numWallPieces>0) {
  4042. m_layers[LAYER_WALL].init(NULL, LAYER_WALL);
  4043. m_layers[LAYER_WALL].allocateCellsForWallLayer(&m_extent, m_wallPieces, m_numWallPieces);
  4044. }
  4045. }
  4046. classifyMap();
  4047. // Add existing objects.
  4048. Object *obj;
  4049. for( obj = TheGameLogic->getFirstObject(); obj; obj = obj->getNextObject() )
  4050. {
  4051. classifyObjectFootprint(obj, true);
  4052. }
  4053. m_isMapReady = true;
  4054. }
  4055. /**
  4056. * Classify all cells in grid as obstacles, etc.
  4057. */
  4058. void Pathfinder::classifyMap(void)
  4059. {
  4060. Int i, j;
  4061. // for now, sample cell corners and classify cell accordingly
  4062. for( j=m_extent.lo.y; j<=m_extent.hi.y; j++ )
  4063. {
  4064. for( i=m_extent.lo.x; i<=m_extent.hi.x; i++ )
  4065. {
  4066. classifyMapCell( i, j, &m_map[i][j]);
  4067. }
  4068. }
  4069. #if 1
  4070. // Expand all cliff cells one step (mark pinched)
  4071. for( j=m_extent.lo.y; j<=m_extent.hi.y; j++ )
  4072. {
  4073. for( i=m_extent.lo.x; i<=m_extent.hi.x; i++ )
  4074. {
  4075. if (m_map[i][j].getType() & PathfindCell::CELL_CLIFF) {
  4076. Int k, l;
  4077. for (k=i-1; k<i+2; k++) {
  4078. if (k<m_extent.lo.x || k> m_extent.hi.x) continue;
  4079. for (l=j-1; l<j+2; l++) {
  4080. if (l<m_extent.lo.y || l> m_extent.hi.y) continue;
  4081. if (m_map[k][l].getType() == PathfindCell::CELL_CLEAR) {
  4082. m_map[k][l].setPinched(true);
  4083. }
  4084. }
  4085. }
  4086. }
  4087. }
  4088. }
  4089. // Convert pinched to cliff.
  4090. for( j=m_extent.lo.y; j<=m_extent.hi.y; j++ )
  4091. {
  4092. for( i=m_extent.lo.x; i<=m_extent.hi.x; i++ )
  4093. {
  4094. if (m_map[i][j].getPinched()) {
  4095. if (m_map[i][j].getType()==PathfindCell::CELL_CLEAR) {
  4096. m_map[i][j].setType(PathfindCell::CELL_CLIFF);
  4097. }
  4098. }
  4099. }
  4100. }
  4101. // Add a border of pinched cells to cliffs.
  4102. for( j=m_extent.lo.y; j<=m_extent.hi.y; j++ )
  4103. {
  4104. for( i=m_extent.lo.x; i<=m_extent.hi.x; i++ )
  4105. {
  4106. if (m_map[i][j].getType() & PathfindCell::CELL_CLIFF) {
  4107. Int k, l;
  4108. for (k=i-1; k<i+2; k++) {
  4109. if (k<m_extent.lo.x || k> m_extent.hi.x) continue;
  4110. for (l=j-1; l<j+2; l++) {
  4111. if (l<m_extent.lo.y || l> m_extent.hi.y) continue;
  4112. if (m_map[k][l].getType() == PathfindCell::CELL_CLEAR) {
  4113. m_map[k][l].setPinched(true);
  4114. }
  4115. }
  4116. }
  4117. }
  4118. }
  4119. }
  4120. #endif
  4121. for (i=0; i<LAYER_LAST; i++) {
  4122. if (!m_layers[i].isUnused()) {
  4123. m_layers[i].classifyCells();
  4124. }
  4125. }
  4126. if (!m_layers[LAYER_WALL].isUnused()) {
  4127. m_layers[LAYER_WALL].classifyWallCells(m_wallPieces, m_numWallPieces);
  4128. }
  4129. m_zoneManager.calculateZones(m_map, m_layers, m_extent);
  4130. }
  4131. /**
  4132. * Force pathfind map recomputation.
  4133. */
  4134. void Pathfinder::forceMapRecalculation( void )
  4135. {
  4136. classifyMap( );
  4137. }
  4138. /**
  4139. * Show all cells touched in the last search
  4140. */
  4141. void Pathfinder::debugShowSearch( Bool pathFound )
  4142. {
  4143. if (!TheGlobalData->m_debugAI) {
  4144. return;
  4145. }
  4146. #if defined _DEBUG || defined _INTERNAL
  4147. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  4148. // show all explored cells for debugging
  4149. PathfindCell *s;
  4150. RGBColor color;
  4151. color.red = color.blue = color.green = 1;
  4152. if (!pathFound) {
  4153. addIcon(NULL, 0, 0, color); // erase.
  4154. }
  4155. for( s = m_openList; s; s=s->getNextOpen() )
  4156. {
  4157. // create objects to show path - they decay
  4158. RGBColor color;
  4159. color.red = color.green = 0;
  4160. color.blue = 1;
  4161. Coord3D pos;
  4162. pos.x = ((Real)s->getXIndex() + 0.5f) * PATHFIND_CELL_SIZE_F;
  4163. pos.y = ((Real)s->getYIndex() + 0.5f) * PATHFIND_CELL_SIZE_F;
  4164. pos.z = TheTerrainLogic->getLayerHeight( pos.x, pos.y, s->getLayer() ) + 0.5f;
  4165. addIcon(&pos, PATHFIND_CELL_SIZE_F*.6f, 200, color);
  4166. }
  4167. for( s = m_closedList; s; s=s->getNextOpen() )
  4168. {
  4169. // create objects to show path - they decay
  4170. // create objects to show path - they decay
  4171. RGBColor color;
  4172. color.red = color.blue = 1;
  4173. color.green = 0;
  4174. if (!pathFound) color.blue = 0;
  4175. Int length=200;
  4176. if (!pathFound)
  4177. length *= 2;
  4178. Coord3D pos;
  4179. pos.x = ((Real)s->getXIndex() + 0.5f) * PATHFIND_CELL_SIZE_F;
  4180. pos.y = ((Real)s->getYIndex() + 0.5f) * PATHFIND_CELL_SIZE_F;
  4181. pos.z = TheTerrainLogic->getLayerHeight( pos.x, pos.y, s->getLayer()) + 0.5f;
  4182. addIcon(&pos, PATHFIND_CELL_SIZE_F*.6f, length, color);
  4183. }
  4184. #endif
  4185. }
  4186. Locomotor* Pathfinder::chooseBestLocomotorForPosition(PathfindLayerEnum layer, LocomotorSet* locomotorSet, const Coord3D* pos )
  4187. {
  4188. Int x = REAL_TO_INT_FLOOR(pos->x/PATHFIND_CELL_SIZE);
  4189. Int y = REAL_TO_INT_FLOOR(pos->y/PATHFIND_CELL_SIZE);
  4190. PathfindCell* cell = getCell(layer, x, y );
  4191. // off the map? call it CELL_CLEAR...
  4192. PathfindCell::CellType celltype = cell ? cell->getType() : PathfindCell::CELL_CLEAR;
  4193. LocomotorSurfaceTypeMask acceptableSurfaces = validLocomotorSurfacesForCellType(celltype);
  4194. return locomotorSet->findLocomotor(acceptableSurfaces);
  4195. }
  4196. /*static*/ LocomotorSurfaceTypeMask Pathfinder::validLocomotorSurfacesForCellType(PathfindCell::CellType t)
  4197. {
  4198. if (t == PathfindCell::CELL_OBSTACLE) {
  4199. return LOCOMOTORSURFACE_AIR;
  4200. }
  4201. if (t == PathfindCell::CELL_IMPASSABLE) {
  4202. return LOCOMOTORSURFACE_AIR;
  4203. }
  4204. if (t == PathfindCell::CELL_BRIDGE_IMPASSABLE) {
  4205. return LOCOMOTORSURFACE_AIR;
  4206. }
  4207. if (t==PathfindCell::CELL_CLEAR) {
  4208. return LOCOMOTORSURFACE_GROUND | LOCOMOTORSURFACE_AIR;
  4209. }
  4210. if (t == PathfindCell::CELL_WATER) {
  4211. return LOCOMOTORSURFACE_WATER | LOCOMOTORSURFACE_AIR;
  4212. }
  4213. if (t == PathfindCell::CELL_RUBBLE) {
  4214. return LOCOMOTORSURFACE_RUBBLE | LOCOMOTORSURFACE_AIR;
  4215. }
  4216. if ( (t == PathfindCell::CELL_CLIFF) ) {
  4217. return LOCOMOTORSURFACE_CLIFF | LOCOMOTORSURFACE_AIR;
  4218. }
  4219. return NO_SURFACES;
  4220. }
  4221. //
  4222. // Return true if we can move onto this position
  4223. //
  4224. Bool Pathfinder::validMovementTerrain( PathfindLayerEnum layer, const Locomotor* locomotor, const Coord3D *pos)
  4225. {
  4226. Int x = REAL_TO_INT_FLOOR(pos->x/PATHFIND_CELL_SIZE);
  4227. Int y = REAL_TO_INT_FLOOR(pos->y/PATHFIND_CELL_SIZE);
  4228. PathfindCell *toCell = NULL;
  4229. toCell = getCell( layer, x, y );
  4230. if (toCell == NULL)
  4231. return false;
  4232. if (toCell->getType()==PathfindCell::CELL_OBSTACLE) return true;
  4233. if (toCell->getType()==PathfindCell::CELL_IMPASSABLE) return true;
  4234. if (toCell->getLayer()!=LAYER_GROUND && toCell->getLayer() == PathfindCell::CELL_CLEAR) {
  4235. return true;
  4236. }
  4237. // check validity of destination cell
  4238. LocomotorSurfaceTypeMask acceptableSurfaces = validLocomotorSurfacesForCellType(toCell->getType());
  4239. if ((locomotor->getLegalSurfaces() & acceptableSurfaces) == 0)
  4240. return false;
  4241. return true;
  4242. }
  4243. //
  4244. // Releases the cells on the open & closed lists.
  4245. //
  4246. void Pathfinder::cleanOpenAndClosedLists(void) {
  4247. Int count = 0;
  4248. if (m_openList) {
  4249. count += PathfindCell::releaseOpenList(m_openList);
  4250. m_openList = NULL;
  4251. }
  4252. if (m_closedList) {
  4253. count += PathfindCell::releaseClosedList(m_closedList);
  4254. m_closedList = NULL;
  4255. }
  4256. m_cumulativeCellsAllocated += count;
  4257. //#ifdef _DEBUG
  4258. #if 0
  4259. // Check for dangling cells.
  4260. for( int j=0; j<=m_extent.hi.y; j++ )
  4261. for( int i=0; i<=m_extent.hi.x; i++ )
  4262. if (m_map[ i ][ j ].hasInfo()) {
  4263. DEBUG_ASSERTCRASH((m_map[i][j].getXIndex()==i && m_map[i][j].getYIndex()==j), ("Wrong cell coordinates"));
  4264. Bool needInfo = (m_map[ i ][ j ].getType() == PathfindCell::CELL_OBSTACLE);
  4265. if (m_map[i][j].isAircraftGoal()) {
  4266. needInfo = true;
  4267. }
  4268. if (m_map[i][j].getFlags() != PathfindCell::NO_UNITS) {
  4269. needInfo = true;
  4270. }
  4271. if (!needInfo) {
  4272. DEBUG_LOG(("leaked cell %d, %d\n", m_map[i][j].getXIndex(), m_map[i][j].getYIndex()));
  4273. m_map[i][j].releaseInfo();
  4274. }
  4275. DEBUG_ASSERTCRASH((needInfo), ("Minor temporary memory leak - Extra cell allocated. Tell JBA steps if repeatable."));
  4276. };
  4277. //DEBUG_LOG(("Pathfind used %d cells.\n", count));
  4278. #endif
  4279. //#endif
  4280. }
  4281. //
  4282. // Return true if we can move onto this position
  4283. //
  4284. Bool Pathfinder::validMovementPosition( Bool isCrusher, LocomotorSurfaceTypeMask acceptableSurfaces,
  4285. PathfindCell *toCell, PathfindCell *fromCell )
  4286. {
  4287. if (toCell == NULL)
  4288. return false;
  4289. // check if the destination cell is classified as an obstacle,
  4290. // and we happen to be ignoring it
  4291. if (toCell->isObstaclePresent( m_ignoreObstacleID ))
  4292. return true;
  4293. if (isCrusher && toCell->isObstacleFence()) {
  4294. return true;
  4295. }
  4296. // check validity of destination cell
  4297. LocomotorSurfaceTypeMask cellSurfaces = validLocomotorSurfacesForCellType(toCell->getType());
  4298. if ((cellSurfaces & acceptableSurfaces) == 0)
  4299. return false;
  4300. #if 0
  4301. //
  4302. // For diagonal moves, check neighboring vertical and horizontal
  4303. // steps as well to avoid "squeezing through a crack".
  4304. //
  4305. if (fromCell)
  4306. {
  4307. ICoord2D delta;
  4308. delta.x = toCell->getXIndex() - fromCell->getXIndex();
  4309. delta.y = toCell->getYIndex() - fromCell->getYIndex();
  4310. if (delta.x != 0 && delta.y != 0)
  4311. {
  4312. // test vertical movement
  4313. PathfindCell *otherCell = getCell( toCell->getXIndex(), fromCell->getYIndex() );
  4314. Bool open = true;
  4315. // check if cell is on the map
  4316. if (otherCell == NULL)
  4317. open = false;
  4318. // check if we can move onto this new cell
  4319. if (validMovementPosition( locomotorSet, otherCell, fromCell ) == false)
  4320. open = false;
  4321. // test horizontal movement
  4322. if (open == false)
  4323. {
  4324. otherCell = getCell( fromCell->getXIndex(), toCell->getYIndex() );
  4325. // check if cell is on the map
  4326. if (otherCell == NULL)
  4327. return false;
  4328. // check if we can move onto this new cell
  4329. if (validMovementPosition( locomotorSet, otherCell, fromCell ) == false)
  4330. return false;
  4331. }
  4332. }
  4333. }
  4334. #endif
  4335. return true;
  4336. }
  4337. /**
  4338. * Checks to see if obj can occupy the pathfind cell at x,y.
  4339. * Returns false if there is another unit's goal already there.
  4340. * Assumes your locomotor already said you can go there.
  4341. */
  4342. Bool Pathfinder::checkDestination(const Object *obj, Int cellX, Int cellY, PathfindLayerEnum layer, Int iRadius, Bool centerInCell)
  4343. {
  4344. // If obj==NULL, means we are checking for any ground units present. jba.
  4345. Int numCellsAbove = iRadius;
  4346. if (centerInCell) numCellsAbove++;
  4347. Bool checkForAircraft = false;
  4348. Int i, j;
  4349. ObjectID ignoreId = INVALID_ID;
  4350. ObjectID objID = INVALID_ID;
  4351. if (obj && obj->getAIUpdateInterface()) {
  4352. ignoreId = obj->getAIUpdateInterface()->getIgnoredObstacleID();
  4353. checkForAircraft = obj->getAI()->isAircraftThatAdjustsDestination();
  4354. objID = obj->getID();
  4355. }
  4356. for (i=cellX-iRadius; i<cellX+numCellsAbove; i++) {
  4357. for (j=cellY-iRadius; j<cellY+numCellsAbove; j++) {
  4358. PathfindCell *cell = getCell(layer, i, j);
  4359. if (cell) {
  4360. if (checkForAircraft) {
  4361. if (!cell->isAircraftGoal()) continue;
  4362. if (cell->getGoalAircraft()==objID) continue;
  4363. return false;
  4364. }
  4365. if (cell->getType()==PathfindCell::CELL_OBSTACLE) {
  4366. if (cell->isObstaclePresent( ignoreId ))
  4367. continue;
  4368. return false;
  4369. }
  4370. if (IS_IMPASSABLE(cell->getType())) {
  4371. return false;
  4372. }
  4373. if (cell->getFlags() == PathfindCell::NO_UNITS) {
  4374. continue; // Nobody is here, so it's ok.
  4375. }
  4376. ObjectID goalUnitID = cell->getGoalUnit();
  4377. if (goalUnitID==objID) {
  4378. continue; // we got it.
  4379. } else if (ignoreId==goalUnitID) {
  4380. continue; // we are ignoring it.
  4381. } else if (goalUnitID!=INVALID_ID) {
  4382. if (obj==NULL) {
  4383. return false;
  4384. }
  4385. Object *unit = TheGameLogic->findObjectByID(goalUnitID);
  4386. if (unit) {
  4387. // order matters: we want to know if I consider it to be an ally, not vice versa
  4388. if (obj->getRelationship(unit) == ALLIES) {
  4389. return false; // Don't usurp your allies goals. jba.
  4390. }
  4391. if (cell->getFlags()==PathfindCell::UNIT_PRESENT_FIXED) {
  4392. Bool canCrush = obj->canCrushOrSquish(unit, TEST_CRUSH_OR_SQUISH);
  4393. if (!canCrush) {
  4394. return false; // Don't move to an occupied cell.
  4395. }
  4396. }
  4397. }
  4398. }
  4399. } else {
  4400. return false; // off the map, so can't place here.
  4401. }
  4402. }
  4403. }
  4404. return true;
  4405. }
  4406. /**
  4407. * Checks to see if obj can move through the pathfind cell at x,y.
  4408. * Returns false if there are other units already there.
  4409. * Assumes your locomotor already said you can go there.
  4410. */
  4411. Bool Pathfinder::checkForMovement(const Object *obj, TCheckMovementInfo &info)
  4412. {
  4413. info.allyFixedCount = 0;
  4414. info.allyMoving = false;
  4415. info.allyGoal = false;
  4416. info.enemyFixed = false;
  4417. const Int maxAlly = 5;
  4418. ObjectID allies[maxAlly];
  4419. Int numAlly = 0;
  4420. if (!obj) return true; // not object can move there.
  4421. ObjectID ignoreId = INVALID_ID;
  4422. if (obj->getAIUpdateInterface()) {
  4423. ignoreId = obj->getAIUpdateInterface()->getIgnoredObstacleID();
  4424. }
  4425. Int numCellsAbove = info.radius;
  4426. if (info.centerInCell) numCellsAbove++;
  4427. Int i, j;
  4428. // Bool isInfantry = obj->isKindOf(KINDOF_INFANTRY);
  4429. for (i=info.cell.x-info.radius; i<info.cell.x+numCellsAbove; i++) {
  4430. for (j=info.cell.y-info.radius; j<info.cell.y+numCellsAbove; j++) {
  4431. PathfindCell *cell = getCell(info.layer,i, j);
  4432. if (cell) {
  4433. enum PathfindCell::CellFlags flags = cell->getFlags();
  4434. ObjectID posUnit = cell->getPosUnit();
  4435. if ((flags == PathfindCell::UNIT_GOAL) || (flags == PathfindCell::UNIT_GOAL_OTHER_MOVING)) {
  4436. info.allyGoal = true;
  4437. }
  4438. if (flags == PathfindCell::NO_UNITS) {
  4439. continue; // Nobody is here, so it's ok.
  4440. } else if (posUnit==obj->getID()) {
  4441. continue; // we got it.
  4442. } else if (posUnit==ignoreId) {
  4443. continue; // we are ignoring this one.
  4444. } else {
  4445. Bool check = false;
  4446. Object *unit = NULL;
  4447. if (flags==PathfindCell::UNIT_PRESENT_MOVING || flags==PathfindCell::UNIT_GOAL_OTHER_MOVING) {
  4448. unit = TheGameLogic->findObjectByID(posUnit);
  4449. // order matters: we want to know if I consider it to be an ally, not vice versa
  4450. if (unit && obj->getRelationship(unit) == ALLIES) {
  4451. info.allyMoving = true;
  4452. }
  4453. if (info.considerTransient) {
  4454. check = true;
  4455. }
  4456. }
  4457. if (flags == PathfindCell::UNIT_PRESENT_FIXED) {
  4458. check = true;
  4459. unit = TheGameLogic->findObjectByID(posUnit);
  4460. }
  4461. if (check && unit!=NULL) {
  4462. if (obj->getAIUpdateInterface() && obj->getAIUpdateInterface()->getIgnoredObstacleID()==unit->getID()) {
  4463. // Don't check if it's the ignored obstacle.
  4464. check = false;
  4465. }
  4466. }
  4467. if (check && unit) {
  4468. #ifdef INFANTRY_MOVES_THROUGH_INFANTRY
  4469. if (obj->isKindOf(KINDOF_INFANTRY) && unit->isKindOf(KINDOF_INFANTRY)) {
  4470. // Infantry can run through infantry.
  4471. continue; //
  4472. }
  4473. #endif
  4474. // See if it is an ally.
  4475. // order matters: we want to know if I consider it to be an ally, not vice versa
  4476. if (obj->getRelationship(unit) == ALLIES) {
  4477. if (!unit->getAIUpdateInterface()) {
  4478. return false; // can't path through not-idle units.
  4479. }
  4480. #if 0
  4481. if (!unit->getAIUpdateInterface()->isIdle()) {
  4482. return false; // can't path through not-idle units.
  4483. }
  4484. #endif
  4485. Bool found = false;
  4486. Int k;
  4487. for (k=0; k<numAlly; k++) {
  4488. if (allies[k] == unit->getID()) {
  4489. found = true;
  4490. }
  4491. }
  4492. if (!found) {
  4493. info.allyFixedCount++;
  4494. if (numAlly < maxAlly) {
  4495. allies[numAlly] = unit->getID();
  4496. numAlly++;
  4497. }
  4498. }
  4499. } else {
  4500. Bool canCrush = obj->canCrushOrSquish( unit, TEST_CRUSH_OR_SQUISH );
  4501. if (!canCrush) {
  4502. info.enemyFixed = true;
  4503. }
  4504. }
  4505. }
  4506. }
  4507. } else {
  4508. return false; // off the map, so can't place here.
  4509. }
  4510. }
  4511. }
  4512. return true;
  4513. }
  4514. /**
  4515. * Adjusts a coordinate to the center of it's cell.
  4516. */
  4517. // Snaps the current position to it's grid location.
  4518. void Pathfinder::snapPosition(Object *obj, Coord3D *pos)
  4519. {
  4520. Int iRadius;
  4521. Bool center;
  4522. getRadiusAndCenter(obj, iRadius, center);
  4523. ICoord2D cell;
  4524. Coord3D adjustDest = *pos;
  4525. if (!center) {
  4526. adjustDest.x += PATHFIND_CELL_SIZE_F/2;
  4527. adjustDest.y += PATHFIND_CELL_SIZE_F/2;
  4528. }
  4529. worldToCell( &adjustDest, &cell );
  4530. adjustCoordToCell(cell.x, cell.y, center, *pos, LAYER_GROUND);
  4531. }
  4532. /**
  4533. * Adjusts a goal position to the center of it's cell.
  4534. */
  4535. // Snaps the current position to it's grid location.
  4536. void Pathfinder::snapClosestGoalPosition(Object *obj, Coord3D *pos)
  4537. {
  4538. Int iRadius;
  4539. Bool center;
  4540. getRadiusAndCenter(obj, iRadius, center);
  4541. ICoord2D cell;
  4542. Coord3D adjustDest = *pos;
  4543. if (!center) {
  4544. adjustDest.x += PATHFIND_CELL_SIZE_F/2;
  4545. adjustDest.y += PATHFIND_CELL_SIZE_F/2;
  4546. }
  4547. PathfindLayerEnum layer = TheTerrainLogic->getLayerForDestination(pos);
  4548. worldToCell( &adjustDest, &cell );
  4549. adjustCoordToCell(cell.x, cell.y, center, *pos, LAYER_GROUND);
  4550. if (checkDestination(obj, cell.x, cell.y , layer, iRadius, center)) {
  4551. return;
  4552. }
  4553. // Try adjusting by 1.
  4554. Int i,j;
  4555. for (i=cell.x-1; i<cell.x+2; i++) {
  4556. for (j=cell.y-1; j<cell.y+2; j++) {
  4557. if (checkDestination(obj, i, j, layer, iRadius, center)) {
  4558. adjustCoordToCell(i, j, center, *pos, layer);
  4559. return;
  4560. }
  4561. }
  4562. }
  4563. if (iRadius==0) {
  4564. // Try to find an unoccupied cell.
  4565. for (i=cell.x-1; i<cell.x+2; i++) {
  4566. for (j=cell.y-1; j<cell.y+2; j++) {
  4567. PathfindCell *newCell = getCell(layer,i, j);
  4568. if (newCell) {
  4569. if (newCell->getGoalUnit()==INVALID_ID || newCell->getGoalUnit()==obj->getID()) {
  4570. adjustCoordToCell(i, j, center, *pos, layer);
  4571. return;
  4572. }
  4573. }
  4574. }
  4575. }
  4576. // Try to find an unoccupied cell.
  4577. for (i=cell.x-1; i<cell.x+2; i++) {
  4578. for (j=cell.y-1; j<cell.y+2; j++) {
  4579. PathfindCell *newCell = getCell(layer,i, j);
  4580. if (newCell) {
  4581. if (newCell->getFlags()!=PathfindCell::UNIT_PRESENT_FIXED) {
  4582. adjustCoordToCell(i, j, center, *pos, layer);
  4583. return;
  4584. }
  4585. }
  4586. }
  4587. }
  4588. }
  4589. //DEBUG_LOG(("Couldn't find goal.\n"));
  4590. }
  4591. /**
  4592. * Returns coordinates of goal.
  4593. *
  4594. */
  4595. Bool Pathfinder::goalPosition(Object *obj, Coord3D *pos)
  4596. {
  4597. Int iRadius;
  4598. Bool center;
  4599. AIUpdateInterface *ai = obj->getAIUpdateInterface();
  4600. if (!ai) return false; // only consider ai objects.
  4601. getRadiusAndCenter(obj, iRadius, center);
  4602. ICoord2D cell = *ai->getPathfindGoalCell();
  4603. pos->zero();
  4604. if (cell.x<0 || cell.y<0) return false;
  4605. adjustCoordToCell(cell.x, cell.y, center, *pos, LAYER_GROUND);
  4606. return true;
  4607. }
  4608. Bool Pathfinder::checkForAdjust(Object *obj, const LocomotorSet& locomotorSet, Bool isHuman,
  4609. Int cellX, Int cellY, PathfindLayerEnum layer,
  4610. Int iRadius, Bool center, Coord3D *dest, const Coord3D *groupDest)
  4611. {
  4612. Coord3D adjustDest;
  4613. PathfindCell *cellP = getCell(layer, cellX, cellY);
  4614. if (cellP==NULL) return false;
  4615. if (cellP && cellP->getType() == PathfindCell::CELL_CLIFF) {
  4616. return false; // no final destinations on cliffs.
  4617. }
  4618. if (isHuman) {
  4619. // check if new cell is in logical map. (computer can move off logical map)
  4620. if (cellX < m_logicalExtent.lo.x ||
  4621. cellY < m_logicalExtent.lo.y ||
  4622. cellX > m_logicalExtent.hi.x ||
  4623. cellY > m_logicalExtent.hi.y) return false;
  4624. }
  4625. if (checkDestination(obj, cellX, cellY, layer, iRadius, center)) {
  4626. adjustCoordToCell(cellX, cellY, center, adjustDest, cellP->getLayer());
  4627. Bool pathExists;
  4628. Bool adjustedPathExists;
  4629. if (obj->isKindOf(KINDOF_AIRCRAFT)) {
  4630. pathExists = true;
  4631. adjustedPathExists = true;
  4632. } else {
  4633. pathExists = clientSafeQuickDoesPathExist( locomotorSet, obj->getPosition(), dest);
  4634. adjustedPathExists = clientSafeQuickDoesPathExist( locomotorSet, obj->getPosition(), &adjustDest);
  4635. if (!pathExists) {
  4636. if (clientSafeQuickDoesPathExist( locomotorSet, dest, &adjustDest)) {
  4637. adjustedPathExists = true;
  4638. }
  4639. }
  4640. }
  4641. if ( adjustedPathExists ) {
  4642. if (groupDest) {
  4643. tightenPath(obj, locomotorSet, &adjustDest, groupDest);
  4644. // Check to see if it is a long way to get to the adjusted destination.
  4645. Int cost = checkPathCost(obj, locomotorSet, groupDest, &adjustDest);
  4646. Int dx = IABS(groupDest->x-adjustDest.x);
  4647. Int dy = IABS(groupDest->y-adjustDest.y);
  4648. if (1.4f*(dx+dy)<cost) {
  4649. return false;
  4650. }
  4651. }
  4652. *dest = adjustDest;
  4653. return true;
  4654. }
  4655. }
  4656. return false;
  4657. }
  4658. Bool Pathfinder::checkForLanding(Int cellX, Int cellY, PathfindLayerEnum layer,
  4659. Int iRadius, Bool center, Coord3D *dest)
  4660. {
  4661. Coord3D adjustDest;
  4662. PathfindCell *cellP = getCell(layer, cellX, cellY);
  4663. if (cellP==NULL) return false;
  4664. switch (cellP->getType())
  4665. {
  4666. case PathfindCell::CELL_CLIFF:
  4667. case PathfindCell::CELL_WATER:
  4668. case PathfindCell::CELL_IMPASSABLE:
  4669. return false; // no final destinations on cliffs, water, etc.
  4670. }
  4671. if (checkDestination(NULL, cellX, cellY, layer, iRadius, center)) {
  4672. adjustCoordToCell(cellX, cellY, center, adjustDest, cellP->getLayer());
  4673. *dest = adjustDest;
  4674. return true;
  4675. }
  4676. return false;
  4677. }
  4678. /**
  4679. * Find an unoccupied spot for a unit to land at.
  4680. * Returns false if there are no spots available within a reasonable radius.
  4681. */
  4682. Bool Pathfinder::adjustToLandingDestination(Object *obj, Coord3D *dest)
  4683. {
  4684. Int iRadius;
  4685. Bool center;
  4686. getRadiusAndCenter(obj, iRadius, center);
  4687. ICoord2D cell;
  4688. Coord3D adjustDest = *dest;
  4689. Region3D extent;
  4690. TheTerrainLogic->getMaximumPathfindExtent(&extent);
  4691. // If the object is off the map & the goal is off the map, it is a scripted setup, so just
  4692. // go to the dest.
  4693. if (!extent.isInRegionNoZ(dest)) {
  4694. if (!extent.isInRegionNoZ(obj->getPosition())) {
  4695. return true;
  4696. }
  4697. }
  4698. if (!center) {
  4699. adjustDest.x += PATHFIND_CELL_SIZE_F/2;
  4700. adjustDest.y += PATHFIND_CELL_SIZE_F/2;
  4701. }
  4702. worldToCell( &adjustDest, &cell );
  4703. enum {MAX_CELLS_TO_TRY=400};
  4704. Int limit = MAX_CELLS_TO_TRY;
  4705. Int i, j;
  4706. i = cell.x;
  4707. j = cell.y;
  4708. PathfindLayerEnum layer = TheTerrainLogic->getLayerForDestination(dest);
  4709. if (checkForLanding(i,j, layer, iRadius, center, dest)) {
  4710. return true;
  4711. }
  4712. Int delta=1;
  4713. Int count;
  4714. while (limit>0) {
  4715. for (count = delta; count>0; count--) {
  4716. i++;
  4717. limit--;
  4718. if (checkForLanding(i,j, layer, iRadius, center, dest)) {
  4719. return true;
  4720. }
  4721. }
  4722. for (count = delta; count>0; count--) {
  4723. j++;
  4724. limit--;
  4725. if (checkForLanding(i,j, layer, iRadius, center, dest)) {
  4726. return true;
  4727. }
  4728. }
  4729. delta++;
  4730. for (count = delta; count>0; count--) {
  4731. i--;
  4732. limit--;
  4733. if (checkForLanding(i,j, layer, iRadius, center, dest)) {
  4734. return true;
  4735. }
  4736. }
  4737. for (count = delta; count>0; count--) {
  4738. j--;
  4739. limit--;
  4740. if (checkForLanding(i,j, layer, iRadius, center, dest)) {
  4741. return true;
  4742. }
  4743. }
  4744. delta++;
  4745. }
  4746. return false;
  4747. }
  4748. /**
  4749. * Find an unoccupied spot for a unit to move to.
  4750. * Returns false if there are no spots available within a reasonable radius.
  4751. */
  4752. Bool Pathfinder::adjustDestination(Object *obj, const LocomotorSet& locomotorSet, Coord3D *dest, const Coord3D *groupDest)
  4753. {
  4754. if( obj->isKindOf(KINDOF_PROJECTILE) )
  4755. {
  4756. return true; // missiles can go wherever they want to. jba.
  4757. }
  4758. Bool isHuman = true;
  4759. if (obj && obj->getControllingPlayer() && (obj->getControllingPlayer()->getPlayerType()==PLAYER_COMPUTER)) {
  4760. isHuman = false; // computer gets to cheat.
  4761. }
  4762. Int iRadius;
  4763. Bool center;
  4764. getRadiusAndCenter(obj, iRadius, center);
  4765. ICoord2D cell;
  4766. Coord3D adjustDest = *dest;
  4767. if (!center) {
  4768. adjustDest.x += PATHFIND_CELL_SIZE_F/2;
  4769. adjustDest.y += PATHFIND_CELL_SIZE_F/2;
  4770. }
  4771. worldToCell( &adjustDest, &cell );
  4772. PathfindLayerEnum layer = TheTerrainLogic->getLayerForDestination(dest);
  4773. if (groupDest) {
  4774. layer = TheTerrainLogic->getLayerForDestination(groupDest);
  4775. }
  4776. enum {MAX_CELLS_TO_TRY=400};
  4777. Int limit = MAX_CELLS_TO_TRY;
  4778. Int i, j;
  4779. i = cell.x;
  4780. j = cell.y;
  4781. if (checkForAdjust(obj, locomotorSet, isHuman, i,j, layer, iRadius, center, dest, groupDest)) {
  4782. return true;
  4783. }
  4784. Int delta=1;
  4785. Int count;
  4786. while (limit>0) {
  4787. for (count = delta; count>0; count--) {
  4788. i++;
  4789. limit--;
  4790. if (checkForAdjust(obj, locomotorSet, isHuman, i,j, layer, iRadius, center, dest, groupDest)) {
  4791. return true;
  4792. }
  4793. }
  4794. for (count = delta; count>0; count--) {
  4795. j++;
  4796. limit--;
  4797. if (checkForAdjust(obj, locomotorSet, isHuman, i,j, layer, iRadius, center, dest, groupDest)) {
  4798. return true;
  4799. }
  4800. }
  4801. delta++;
  4802. for (count = delta; count>0; count--) {
  4803. i--;
  4804. limit--;
  4805. if (checkForAdjust(obj, locomotorSet, isHuman, i,j, layer, iRadius, center, dest, groupDest)) {
  4806. return true;
  4807. }
  4808. }
  4809. for (count = delta; count>0; count--) {
  4810. j--;
  4811. limit--;
  4812. if (checkForAdjust(obj, locomotorSet, isHuman, i,j, layer, iRadius, center, dest, groupDest)) {
  4813. return true;
  4814. }
  4815. }
  4816. delta++;
  4817. }
  4818. if (groupDest) {
  4819. // Didn't work, so just do simple adjust.
  4820. return(adjustDestination(obj, locomotorSet, dest, NULL));
  4821. }
  4822. //DEBUG_LOG(("adjustDestination failed, dest (%f, %f), adj dest (%f,%f), %x %s\n", dest->x, dest->y, adjustDest.x, adjustDest.y,
  4823. //obj, obj->getTemplate()->getName().str()));
  4824. return false;
  4825. }
  4826. Bool Pathfinder::checkForTarget(const Object *obj, Int cellX, Int cellY, const Weapon *weapon,
  4827. const Object *victim, const Coord3D *victimPos,
  4828. Int iRadius, Bool center,Coord3D *dest)
  4829. {
  4830. Coord3D adjustDest;
  4831. if (checkDestination(obj, cellX, cellY, LAYER_GROUND, iRadius, center)) {
  4832. adjustCoordToCell(cellX, cellY, center, adjustDest, LAYER_GROUND);
  4833. if (weapon->isGoalPosWithinAttackRange( obj, &adjustDest, victim, victimPos )) {
  4834. *dest = adjustDest;
  4835. return true;
  4836. }
  4837. }
  4838. return false;
  4839. }
  4840. /**
  4841. * Find an unoccupied spot for a unit to move to that can fire at victim.
  4842. * Returns false if there are no spots available within a reasonable radius.
  4843. */
  4844. Bool Pathfinder::adjustTargetDestination(const Object *obj, const Object *target, const Coord3D *targetPos,
  4845. const Weapon *weapon, Coord3D *dest)
  4846. {
  4847. Int iRadius;
  4848. Bool center;
  4849. getRadiusAndCenter(obj, iRadius, center);
  4850. ICoord2D cell;
  4851. Coord3D adjustDest = *dest;
  4852. if (!center) {
  4853. adjustDest.x += PATHFIND_CELL_SIZE_F/2;
  4854. adjustDest.y += PATHFIND_CELL_SIZE_F/2;
  4855. }
  4856. if (worldToCell( &adjustDest, &cell )) {
  4857. return false; // outside of bounds.
  4858. }
  4859. enum {MAX_CELLS_TO_TRY=400};
  4860. Int limit = MAX_CELLS_TO_TRY;
  4861. Int i, j;
  4862. i = cell.x;
  4863. j = cell.y;
  4864. if (checkForTarget(obj, i,j, weapon, target, targetPos, iRadius, center, dest)) {
  4865. return true;
  4866. }
  4867. Int delta=1;
  4868. Int count;
  4869. while (limit>0) {
  4870. for (count = delta; count>0; count--) {
  4871. i++;
  4872. limit--;
  4873. if (checkForTarget(obj, i,j, weapon, target, targetPos, iRadius, center, dest)) {
  4874. return true;
  4875. }
  4876. }
  4877. for (count = delta; count>0; count--) {
  4878. j++;
  4879. limit--;
  4880. if (checkForTarget(obj, i,j, weapon, target, targetPos, iRadius, center, dest)) {
  4881. return true;
  4882. }
  4883. }
  4884. delta++;
  4885. for (count = delta; count>0; count--) {
  4886. i--;
  4887. limit--;
  4888. if (checkForTarget(obj, i,j, weapon, target, targetPos, iRadius, center, dest)) {
  4889. return true;
  4890. }
  4891. }
  4892. for (count = delta; count>0; count--) {
  4893. j--;
  4894. limit--;
  4895. if (checkForTarget(obj, i,j, weapon, target, targetPos, iRadius, center, dest)) {
  4896. return true;
  4897. }
  4898. }
  4899. delta++;
  4900. }
  4901. return false;
  4902. }
  4903. Bool Pathfinder::checkForPossible(Bool isCrusher, Int fromZone, Bool center, const LocomotorSet& locomotorSet,
  4904. Int cellX, Int cellY, PathfindLayerEnum layer, Coord3D *dest, Bool startingInObstacle)
  4905. {
  4906. PathfindCell *goalCell = getCell(layer, cellX, cellY);
  4907. if (!goalCell) return false;
  4908. if (IS_IMPASSABLE(goalCell->getType())) return false;
  4909. Int zone2 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), isCrusher, goalCell->getZone());
  4910. if (startingInObstacle) {
  4911. zone2 = m_zoneManager.getEffectiveTerrainZone(zone2);
  4912. }
  4913. if (fromZone==zone2) {
  4914. adjustCoordToCell(cellX, cellY, center, *dest, layer);
  4915. return true;
  4916. }
  4917. return false;
  4918. }
  4919. /**
  4920. * Find a pathable spot near the destination.
  4921. * Returns false if there are no spots available within a reasonable radius.
  4922. */
  4923. Bool Pathfinder::adjustToPossibleDestination(Object *obj, const LocomotorSet& locomotorSet,
  4924. Coord3D *dest)
  4925. {
  4926. Int radius;
  4927. Bool center;
  4928. getRadiusAndCenter(obj, radius, center);
  4929. ICoord2D goalCellNdx;
  4930. Coord3D adjustDest = *dest;
  4931. if (!center) {
  4932. adjustDest.x += PATHFIND_CELL_SIZE_F/2;
  4933. adjustDest.y += PATHFIND_CELL_SIZE_F/2;
  4934. }
  4935. if (worldToCell( &adjustDest, &goalCellNdx )) {
  4936. return false; // outside of bounds.
  4937. }
  4938. // determine goal cell
  4939. PathfindCell *goalCell;
  4940. PathfindLayerEnum destinationLayer = TheTerrainLogic->getLayerForDestination(dest);
  4941. goalCell = getCell(destinationLayer, goalCellNdx.x, goalCellNdx.y);
  4942. Coord3D from = *obj->getPosition();
  4943. // determine start cell
  4944. ICoord2D startCellNdx;
  4945. worldToCell(&from, &startCellNdx);
  4946. PathfindLayerEnum layer = LAYER_GROUND;
  4947. if (obj) {
  4948. layer = obj->getLayer();
  4949. }
  4950. PathfindCell *parentCell = getClippedCell( layer, &from );
  4951. if (parentCell == NULL) {
  4952. return false;
  4953. }
  4954. //
  4955. Int zone1, zone2;
  4956. Bool isCrusher = obj ? obj->getCrusherLevel() > 0 : false;
  4957. zone1 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), isCrusher, parentCell->getZone());
  4958. Bool isObstacle = false;
  4959. if (parentCell->getType() == PathfindCell::CELL_OBSTACLE) {
  4960. isObstacle = true;
  4961. }
  4962. if (isObstacle) {
  4963. zone1 = m_zoneManager.getEffectiveTerrainZone(zone1);
  4964. zone1 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), isCrusher, zone1);
  4965. }
  4966. zone2 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), isCrusher, goalCell->getZone());
  4967. if (zone1 == zone2) {
  4968. if (checkDestination(obj, goalCellNdx.x, goalCellNdx.y, destinationLayer, radius, center)) {
  4969. return true;
  4970. }
  4971. }
  4972. enum {MAX_CELLS_TO_TRY=400};
  4973. Int limit = MAX_CELLS_TO_TRY;
  4974. Int i, j;
  4975. i = goalCellNdx.x;
  4976. j = goalCellNdx.y;
  4977. Int delta=1;
  4978. Int count;
  4979. while (limit>0) {
  4980. for (count = delta; count>0; count--) {
  4981. i++;
  4982. limit--;
  4983. if (checkForPossible(isCrusher, zone1, center, locomotorSet, i,j, destinationLayer, dest, isObstacle)) {
  4984. if (checkDestination(obj, i, j, destinationLayer, radius, center)) {
  4985. return true;
  4986. }
  4987. }
  4988. }
  4989. for (count = delta; count>0; count--) {
  4990. j++;
  4991. limit--;
  4992. if (checkForPossible(isCrusher, zone1, center, locomotorSet, i,j, destinationLayer, dest, isObstacle)) {
  4993. if (checkDestination(obj, i, j, destinationLayer, radius, center)) {
  4994. return true;
  4995. }
  4996. }
  4997. }
  4998. delta++;
  4999. for (count = delta; count>0; count--) {
  5000. i--;
  5001. limit--;
  5002. if (checkForPossible(isCrusher, zone1, center, locomotorSet, i,j, destinationLayer, dest, isObstacle)) {
  5003. if (checkDestination(obj, i, j, destinationLayer, radius, center)) {
  5004. return true;
  5005. }
  5006. }
  5007. }
  5008. for (count = delta; count>0; count--) {
  5009. j--;
  5010. limit--;
  5011. if (checkForPossible(isCrusher, zone1, center, locomotorSet, i,j, destinationLayer, dest, isObstacle)) {
  5012. if (checkDestination(obj, i, j, destinationLayer, radius, center)) {
  5013. return true;
  5014. }
  5015. }
  5016. }
  5017. delta++;
  5018. }
  5019. return false;
  5020. }
  5021. /**
  5022. * Queues an object to do a pathfind.
  5023. * It will call the object's ai update->doPathfind() during processPathfindQueue().
  5024. */
  5025. Bool Pathfinder::queueForPath(ObjectID id)
  5026. {
  5027. #if defined(_DEBUG) || defined(_INTERNAL)
  5028. {
  5029. Object *tmpObj = TheGameLogic->findObjectByID(id);
  5030. if (tmpObj) {
  5031. AIUpdateInterface *tmpAI = tmpObj->getAIUpdateInterface();
  5032. if (tmpAI) {
  5033. const Coord3D* pos = tmpAI->friend_getRequestedDestination();
  5034. DEBUG_ASSERTLOG(pos->x != 0.0 && pos->y != 0.0, ("Queueing pathfind to (0, 0), usually a bug. (Unit Name: '%s', Type: '%s' \n", tmpObj->getName().str(), tmpObj->getTemplate()->getName().str()));
  5035. }
  5036. }
  5037. }
  5038. #endif
  5039. /* Check & see if we are already queued. */
  5040. Int slot = m_queuePRHead;
  5041. while (slot != m_queuePRTail) {
  5042. if (m_queuedPathfindRequests[slot] == id) {
  5043. return true;
  5044. }
  5045. slot++;
  5046. if (slot >= PATHFIND_QUEUE_LEN) {
  5047. slot = 0;
  5048. }
  5049. }
  5050. // Tail is the first available slot.
  5051. Int nextSlot = m_queuePRTail+1;
  5052. if (nextSlot >= PATHFIND_QUEUE_LEN) {
  5053. nextSlot = 0;
  5054. }
  5055. if (nextSlot==m_queuePRHead) {
  5056. DEBUG_CRASH(("Ran out of pathfind queue slots."));
  5057. return false;
  5058. }
  5059. m_queuedPathfindRequests[m_queuePRTail] = id;
  5060. m_queuePRTail = nextSlot;
  5061. return true;
  5062. }
  5063. #if defined _DEBUG || defined _INTERNAL
  5064. void Pathfinder::doDebugIcons(void) {
  5065. const Int FRAMES_TO_SHOW_OBSTACLES = 100;
  5066. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  5067. // render AI debug information
  5068. if (TheGlobalData->m_debugAI!=AI_DEBUG_CELLS && TheGlobalData->m_debugAI!=AI_DEBUG_TERRAIN) {
  5069. return;
  5070. }
  5071. RGBColor color;
  5072. color.red = color.green = color.blue = 0;
  5073. addIcon(NULL, 0, 0, color); // clear.
  5074. Coord3D topLeftCorner;
  5075. Bool showCells = TheGlobalData->m_debugAI==AI_DEBUG_CELLS;
  5076. Int i;
  5077. for (i=0; i<=LAYER_LAST; i++) {
  5078. m_layers[i].doDebugIcons();
  5079. }
  5080. if (!showCells) {
  5081. frameToShowObstacles = TheGameLogic->getFrame()+FRAMES_TO_SHOW_OBSTACLES;
  5082. //return;
  5083. }
  5084. // show the pathfind grid
  5085. for( int j=0; j<getExtent()->y; j++ )
  5086. {
  5087. topLeftCorner.y = (Real)j * PATHFIND_CELL_SIZE_F;
  5088. for( int i=0; i<getExtent()->x; i++ )
  5089. {
  5090. topLeftCorner.x = (Real)i * PATHFIND_CELL_SIZE_F;
  5091. color.red = color.green = color.blue = 0;
  5092. Bool empty = true;
  5093. const PathfindCell *cell = TheAI->pathfinder()->getCell( LAYER_GROUND, i, j );
  5094. if (cell)
  5095. {
  5096. switch (cell->getType())
  5097. {
  5098. case PathfindCell::CELL_CLIFF:
  5099. color.red = 1;
  5100. empty = false;
  5101. break;
  5102. case PathfindCell::CELL_BRIDGE_IMPASSABLE:
  5103. color.blue = color.red = 1;
  5104. empty = false;
  5105. break;
  5106. case PathfindCell::CELL_IMPASSABLE:
  5107. color.green = 1;
  5108. empty = false;
  5109. break;
  5110. case PathfindCell::CELL_WATER:
  5111. color.blue = 1;
  5112. empty = false;
  5113. break;
  5114. case PathfindCell::CELL_RUBBLE:
  5115. color.red = 1;
  5116. color.green = 0.5;
  5117. empty = false;
  5118. break;
  5119. case PathfindCell::CELL_OBSTACLE:
  5120. color.red = color.green = 1;
  5121. empty = false;
  5122. break;
  5123. default:
  5124. if (cell->getPinched()) {
  5125. color.blue = color.green = 0.7f;
  5126. empty = false;
  5127. }
  5128. break;
  5129. }
  5130. }
  5131. if (showCells) {
  5132. empty = true;
  5133. color.red = color.green = color.blue = 0;
  5134. if (empty && cell) {
  5135. if (cell->getFlags()!=PathfindCell::NO_UNITS) {
  5136. empty = false;
  5137. if (cell->getFlags() == PathfindCell::UNIT_GOAL) {
  5138. color.red = 1;
  5139. } else if (cell->getFlags() == PathfindCell::UNIT_PRESENT_FIXED) {
  5140. color.green = color.blue = color.red = 1;
  5141. } else if (cell->getFlags() == PathfindCell::UNIT_PRESENT_MOVING) {
  5142. color.green = 1;
  5143. } else {
  5144. color.green = color.red = 1;
  5145. }
  5146. }
  5147. if (cell->isAircraftGoal()) {
  5148. empty = false;
  5149. color.red = 0;
  5150. color.green = color.blue = 1;
  5151. }
  5152. }
  5153. }
  5154. if (!empty) {
  5155. Coord3D loc;
  5156. loc.x = topLeftCorner.x + PATHFIND_CELL_SIZE_F/2.0f;
  5157. loc.y = topLeftCorner.y + PATHFIND_CELL_SIZE_F/2.0f;
  5158. loc.z = TheTerrainLogic->getGroundHeight(loc.x , loc.y);
  5159. addIcon(&loc, PATHFIND_CELL_SIZE_F*0.8f, FRAMES_TO_SHOW_OBSTACLES-1, color);
  5160. }
  5161. }
  5162. }
  5163. }
  5164. #endif
  5165. //-------------------------------------------------------------------------------------------------
  5166. /**
  5167. * Create an aircraft path. Just jogs around tall buildings marked with KINDOF_AIRCRAFT_PATH_AROUND.
  5168. */
  5169. Path *Pathfinder::getAircraftPath( const Object *obj, const Coord3D *to )
  5170. {
  5171. // for now, quick path objects don't pathfind, generally airborne units
  5172. // build a trivial one-node path containing destination, then avoid buildings.
  5173. Path *thePath = newInstance(Path);
  5174. const AIUpdateInterface *ai = obj->getAI();
  5175. ObjectID avoidObject = INVALID_ID;
  5176. if (ai) {
  5177. avoidObject = ai->getBuildingToNotPathAround();
  5178. }
  5179. // If it is an aircraft that circles (like raptors & migs) we need to adjust the destination
  5180. // to one that doesn't clip buildings.
  5181. Bool checkClips = false;
  5182. if (ai && ai->getCurLocomotor()) {
  5183. if (ai->getCurLocomotor()->getAppearance() == LOCO_WINGS) {
  5184. checkClips = true;
  5185. }
  5186. }
  5187. Real radius = 100;
  5188. Coord3D adjDest = *to;
  5189. if (checkClips) {
  5190. circleClipsTallBuilding(obj->getPosition(), to, radius, avoidObject, &adjDest);
  5191. }
  5192. thePath->prependNode(&adjDest, LAYER_GROUND);
  5193. Coord3D pos = *obj->getPosition();
  5194. pos.z = to->z;
  5195. thePath->prependNode( &pos, LAYER_GROUND );
  5196. Int limit = 20;
  5197. PathNode *curNode = thePath->getFirstNode();
  5198. while (curNode && curNode->getNext()) {
  5199. Coord3D newPos1, newPos2, newPos3;
  5200. if (segmentIntersectsTallBuilding(curNode, curNode->getNext(), avoidObject, &newPos1, &newPos2, &newPos3)) {
  5201. PathNode *newNode3 = newInstance(PathNode);
  5202. newNode3->setPosition( &newPos3 );
  5203. newNode3->setLayer(LAYER_GROUND);
  5204. curNode->append(newNode3);
  5205. PathNode *newNode2 = newInstance(PathNode);
  5206. newNode2->setPosition( &newPos2 );
  5207. newNode2->setLayer(LAYER_GROUND);
  5208. curNode->append(newNode2);
  5209. PathNode *newNode1 = newInstance(PathNode);
  5210. newNode1->setPosition( &newPos1 );
  5211. newNode1->setLayer(LAYER_GROUND);
  5212. curNode->append(newNode1);
  5213. curNode = newNode2;
  5214. }
  5215. curNode = curNode->getNext();
  5216. limit--;
  5217. if (limit<0) break;
  5218. }
  5219. curNode = thePath->getFirstNode();
  5220. while (curNode && curNode->getNext()) {
  5221. curNode->setNextOptimized(curNode->getNext());
  5222. curNode = curNode->getNext();
  5223. }
  5224. thePath->markOptimized();
  5225. if (TheGlobalData->m_debugAI==AI_DEBUG_PATHS) {
  5226. TheAI->pathfinder()->setDebugPath(thePath);
  5227. }
  5228. return thePath;
  5229. }
  5230. /**
  5231. * Process some path requests in the pathfind queue.
  5232. */
  5233. //DECLARE_PERF_TIMER(processPathfindQueue)
  5234. void Pathfinder::processPathfindQueue(void)
  5235. {
  5236. //USE_PERF_TIMER(processPathfindQueue)
  5237. if (!m_isMapReady) {
  5238. return;
  5239. }
  5240. #ifdef DEBUG_QPF
  5241. #if defined _DEBUG || defined _INTERNAL
  5242. Int startTimeMS = ::GetTickCount();
  5243. __int64 startTime64;
  5244. double timeToUpdate=0.0f;
  5245. __int64 endTime64,freq64;
  5246. QueryPerformanceFrequency((LARGE_INTEGER *)&freq64);
  5247. QueryPerformanceCounter((LARGE_INTEGER *)&startTime64);
  5248. #endif
  5249. #endif
  5250. if (
  5251. #ifdef forceRefreshCalling
  5252. #pragma message("AHHHH!, forced calls to pathzonerefresh still in code... notify M Lorenzen")
  5253. s_stopForceCalling==FALSE ||
  5254. #endif
  5255. m_zoneManager.needToCalculateZones())
  5256. {
  5257. m_zoneManager.calculateZones(m_map, m_layers, m_extent);
  5258. return;
  5259. }
  5260. // Get the current logical extent.
  5261. Region3D terrainExtent;
  5262. TheTerrainLogic->getExtent( &terrainExtent );
  5263. IRegion2D bounds;
  5264. bounds.lo.x = REAL_TO_INT_FLOOR(terrainExtent.lo.x / PATHFIND_CELL_SIZE_F);
  5265. bounds.hi.x = REAL_TO_INT_FLOOR(terrainExtent.hi.x / PATHFIND_CELL_SIZE_F);
  5266. bounds.lo.y = REAL_TO_INT_FLOOR(terrainExtent.lo.y / PATHFIND_CELL_SIZE_F);
  5267. bounds.hi.y = REAL_TO_INT_FLOOR(terrainExtent.hi.y / PATHFIND_CELL_SIZE_F);
  5268. bounds.hi.x--;
  5269. bounds.hi.y--;
  5270. m_logicalExtent = bounds;
  5271. m_cumulativeCellsAllocated = 0; // Number of pathfind cells examined.
  5272. #ifdef DEBUG_QPF
  5273. Int pathsFound = 0;
  5274. #endif
  5275. while (m_cumulativeCellsAllocated < PATHFIND_CELLS_PER_FRAME &&
  5276. m_queuePRTail!=m_queuePRHead) {
  5277. Object *obj = TheGameLogic->findObjectByID(m_queuedPathfindRequests[m_queuePRHead]);
  5278. m_queuedPathfindRequests[m_queuePRHead] = INVALID_ID;
  5279. if (obj) {
  5280. AIUpdateInterface *ai = obj->getAIUpdateInterface();
  5281. if (ai) {
  5282. ai->doPathfind(this);
  5283. #ifdef DEBUG_QPF
  5284. pathsFound++;
  5285. #endif
  5286. }
  5287. }
  5288. m_queuePRHead = m_queuePRHead+1;
  5289. if (m_queuePRHead >= PATHFIND_QUEUE_LEN) {
  5290. m_queuePRHead = 0;
  5291. }
  5292. }
  5293. if (pathsFound>0) {
  5294. #ifdef DEBUG_QPF
  5295. #if defined _DEBUG || defined _INTERNAL
  5296. QueryPerformanceCounter((LARGE_INTEGER *)&endTime64);
  5297. timeToUpdate = ((double)(endTime64-startTime64) / (double)(freq64));
  5298. if (timeToUpdate>0.01f)
  5299. {
  5300. DEBUG_LOG(("%d Pathfind queue: %d paths, %d cells", TheGameLogic->getFrame(), pathsFound, m_cumulativeCellsAllocated));
  5301. DEBUG_LOG(("Time %f (%f)", timeToUpdate, (::GetTickCount()-startTimeMS)/1000.0f));
  5302. DEBUG_LOG(("\n"));
  5303. }
  5304. #endif
  5305. #endif
  5306. }
  5307. #if defined _DEBUG || defined _INTERNAL
  5308. doDebugIcons();
  5309. #endif
  5310. }
  5311. void Pathfinder::checkChangeLayers(PathfindCell *parentCell)
  5312. {
  5313. ICoord2D newCellCoord;
  5314. PathfindCell *newCell;
  5315. if (parentCell->getConnectLayer() != LAYER_INVALID) {
  5316. newCellCoord.x = parentCell->getXIndex();
  5317. newCellCoord.y = parentCell->getYIndex();
  5318. if (parentCell->getConnectLayer() == LAYER_GROUND) {
  5319. newCell = getCell(LAYER_GROUND, newCellCoord.x, newCellCoord.y );
  5320. } else {
  5321. newCell = getCell(parentCell->getConnectLayer(), newCellCoord.x, newCellCoord.y);
  5322. }
  5323. DEBUG_ASSERTCRASH(newCell, ("Couldn't find cell."));
  5324. if (newCell) {
  5325. Bool onList = false;
  5326. if (newCell->hasInfo()) {
  5327. if (newCell->getOpen() || newCell->getClosed())
  5328. {
  5329. // already on one of the lists
  5330. onList = true;
  5331. }
  5332. }
  5333. if (!onList) {
  5334. if (!newCell->allocateInfo(newCellCoord)) {
  5335. // Out of cells for pathing...
  5336. return;
  5337. }
  5338. // compute cost of path thus far
  5339. // keep track of path we're building - point back to cell we moved here from
  5340. newCell->setParentCell(parentCell) ;
  5341. // store cost of this path
  5342. newCell->setCostSoFar(parentCell->getCostSoFar()); // same as parent cost
  5343. newCell->setTotalCost(parentCell->getTotalCost()) ;
  5344. // insert newCell in open list such that open list is sorted, smallest total path cost first
  5345. m_openList = newCell->putOnSortedOpenList( m_openList );
  5346. }
  5347. }
  5348. }
  5349. }
  5350. struct ExamineCellsStruct
  5351. {
  5352. Pathfinder *thePathfinder;
  5353. const LocomotorSet *theLoco;
  5354. Bool centerInCell;
  5355. Bool isHuman;
  5356. Int radius;
  5357. const Object *obj;
  5358. PathfindCell *goalCell;
  5359. };
  5360. /*static*/ Int Pathfinder::examineCellsCallback(Pathfinder* pathfinder, PathfindCell* from, PathfindCell* to, Int to_x, Int to_y, void* userData)
  5361. {
  5362. ExamineCellsStruct* d = (ExamineCellsStruct*)userData;
  5363. Bool isCrusher = d->obj ? d->obj->getCrusherLevel() > 0 : false;
  5364. if (d->thePathfinder->m_isTunneling) return 1; // abort.
  5365. if (from && to) {
  5366. if (!d->thePathfinder->validMovementPosition( isCrusher, d->theLoco->getValidSurfaces(), to, from )) {
  5367. return 1;
  5368. }
  5369. if ( (to->getLayer() == LAYER_GROUND) && !d->thePathfinder->m_zoneManager.isPassable(to_x, to_y) ) {
  5370. return 1;
  5371. }
  5372. Bool onList = false;
  5373. if (to->hasInfo()) {
  5374. if (to->getOpen() || to->getClosed())
  5375. {
  5376. // already on one of the lists
  5377. onList = true;
  5378. }
  5379. }
  5380. if (to->getPinched()) {
  5381. return 1; // abort.
  5382. }
  5383. if (d->isHuman) {
  5384. // check if new cell is in logical map. (computer can move off logical map)
  5385. if (to_x < d->thePathfinder->m_logicalExtent.lo.x) return 1; // abort
  5386. if (to_y < d->thePathfinder->m_logicalExtent.lo.y) return 1; // abort
  5387. if (to_x > d->thePathfinder->m_logicalExtent.hi.x) return 1; // abort
  5388. if (to_y > d->thePathfinder->m_logicalExtent.hi.y) return 1; // abort
  5389. }
  5390. TCheckMovementInfo info;
  5391. info.cell.x = to_x;
  5392. info.cell.y = to_y;
  5393. info.layer = from->getLayer();
  5394. info.centerInCell = d->centerInCell;
  5395. info.radius = d->radius;
  5396. info.considerTransient = false;
  5397. info.acceptableSurfaces = d->theLoco->getValidSurfaces();
  5398. if (!d->thePathfinder->checkForMovement(d->obj, info) || info.enemyFixed) {
  5399. return 1; //abort.
  5400. }
  5401. if (info.enemyFixed) {
  5402. return 1; //abort.
  5403. }
  5404. ICoord2D newCellCoord;
  5405. newCellCoord.x = to_x;
  5406. newCellCoord.y = to_y;
  5407. UnsignedInt newCostSoFar = from->getCostSoFar( ) + 0.5f*COST_ORTHOGONAL;
  5408. if (to->getType() == PathfindCell::CELL_CLIFF ) {
  5409. return 1;
  5410. }
  5411. if (info.allyFixedCount) {
  5412. return 1;
  5413. } else if (info.enemyFixed) {
  5414. return 1;
  5415. }
  5416. if (!to->allocateInfo(newCellCoord)) {
  5417. // Out of cells for pathing...
  5418. return 1;
  5419. }
  5420. to->setBlockedByAlly(false);
  5421. Int costRemaining = 0;
  5422. costRemaining = to->costToGoal( d->goalCell );
  5423. // check if this neighbor cell is already on the open (waiting to be tried)
  5424. // or closed (already tried) lists
  5425. if (onList)
  5426. {
  5427. // already on one of the lists - if existing costSoFar is less,
  5428. // the new cell is on a longer path, so skip it
  5429. if (to->getCostSoFar() <= newCostSoFar)
  5430. return 0; // keep going.
  5431. }
  5432. to->setCostSoFar(newCostSoFar);
  5433. // keep track of path we're building - point back to cell we moved here from
  5434. to->setParentCell(from) ;
  5435. to->setTotalCost(to->getCostSoFar() + costRemaining) ;
  5436. //DEBUG_LOG(("Cell (%d,%d), Parent cost %d, newCostSoFar %d, cost rem %d, tot %d\n",
  5437. // to->getXIndex(), to->getYIndex(),
  5438. // to->costSoFar(from), newCostSoFar, costRemaining, to->getCostSoFar() + costRemaining));
  5439. // if to was on closed list, remove it from the list
  5440. if (to->getClosed())
  5441. d->thePathfinder->m_closedList = to->removeFromClosedList( d->thePathfinder->m_closedList );
  5442. // if the to was already on the open list, remove it so it can be re-inserted in order
  5443. if (to->getOpen())
  5444. d->thePathfinder->m_openList = to->removeFromOpenList( d->thePathfinder->m_openList );
  5445. // insert to in open list such that open list is sorted, smallest total path cost first
  5446. d->thePathfinder->m_openList = to->putOnSortedOpenList( d->thePathfinder->m_openList );
  5447. }
  5448. return 0; // keep going
  5449. }
  5450. Int Pathfinder::examineNeighboringCells(PathfindCell *parentCell, PathfindCell *goalCell, const LocomotorSet& locomotorSet,
  5451. Bool isHuman, Bool centerInCell, Int radius, const ICoord2D &startCellNdx,
  5452. const Object *obj, Int attackDistance)
  5453. {
  5454. Bool canPathThroughUnits = false;
  5455. if (obj && obj->getAIUpdateInterface()) {
  5456. canPathThroughUnits = obj->getAIUpdateInterface()->canPathThroughUnits();
  5457. }
  5458. Bool isCrusher = obj ? obj->getCrusherLevel() > 0 : false;
  5459. if (attackDistance==NO_ATTACK && !m_isTunneling && !locomotorSet.isDownhillOnly() && goalCell) {
  5460. ExamineCellsStruct info;
  5461. info.thePathfinder = this;
  5462. info.theLoco = &locomotorSet;
  5463. info.centerInCell = centerInCell;
  5464. info.radius = radius;
  5465. info.obj = obj;
  5466. info.isHuman = isHuman;
  5467. info.goalCell = goalCell;
  5468. ICoord2D start, end;
  5469. start.x = parentCell->getXIndex();
  5470. start.y = parentCell->getYIndex();
  5471. end.x = goalCell->getXIndex();
  5472. end.y = goalCell->getYIndex();
  5473. iterateCellsAlongLine(start, end, parentCell->getLayer(), examineCellsCallback, &info);
  5474. }
  5475. Int cellCount = 0;
  5476. // expand search to neighboring orthogonal cells
  5477. static ICoord2D delta[] =
  5478. {
  5479. { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 },
  5480. { 1, 1 }, { -1, 1 }, { -1, -1 }, { 1, -1 }
  5481. };
  5482. const Int numNeighbors = 8;
  5483. const Int firstDiagonal = 4;
  5484. ICoord2D newCellCoord;
  5485. PathfindCell *newCell;
  5486. const Int adjacent[5] = {0, 1, 2, 3, 0};
  5487. Bool neighborFlags[8] = {false, false, false, false, false, false, false};
  5488. UnsignedInt newCostSoFar;
  5489. for( int i=0; i<numNeighbors; i++ )
  5490. {
  5491. neighborFlags[i] = false;
  5492. // determine neighbor cell to try
  5493. newCellCoord.x = parentCell->getXIndex() + delta[i].x;
  5494. newCellCoord.y = parentCell->getYIndex() + delta[i].y;
  5495. // get the neighboring cell
  5496. newCell = getCell(parentCell->getLayer(), newCellCoord.x, newCellCoord.y );
  5497. // check if cell is on the map
  5498. if (newCell == NULL)
  5499. continue;
  5500. Bool notZonePassable = false;
  5501. if ((newCell->getLayer()==LAYER_GROUND) && !m_zoneManager.isPassable(newCellCoord.x, newCellCoord.y)) {
  5502. notZonePassable = true;
  5503. }
  5504. if (isHuman) {
  5505. // check if new cell is in logical map. (computer can move off logical map)
  5506. if (newCellCoord.x < m_logicalExtent.lo.x) continue;
  5507. if (newCellCoord.y < m_logicalExtent.lo.y) continue;
  5508. if (newCellCoord.x > m_logicalExtent.hi.x) continue;
  5509. if (newCellCoord.y > m_logicalExtent.hi.y) continue;
  5510. }
  5511. // check if this neighbor cell is already on the open (waiting to be tried)
  5512. // or closed (already tried) lists
  5513. Bool onList = false;
  5514. if (newCell->hasInfo()) {
  5515. if (newCell->getOpen() || newCell->getClosed())
  5516. {
  5517. // already on one of the lists
  5518. onList = true;
  5519. }
  5520. }
  5521. if (onList) {
  5522. // we have already examined this one, so continue.
  5523. continue;
  5524. }
  5525. if (i>=firstDiagonal) {
  5526. // make sure one of the adjacent sides is open.
  5527. if (!neighborFlags[adjacent[i-4]] && !neighborFlags[adjacent[i-3]]) {
  5528. continue;
  5529. }
  5530. }
  5531. // do the gravity check here
  5532. if ( locomotorSet.isDownhillOnly() )
  5533. {
  5534. Coord3D fromPos;
  5535. fromPos.x = parentCell->getXIndex() * PATHFIND_CELL_SIZE_F ;
  5536. fromPos.y = parentCell->getYIndex() * PATHFIND_CELL_SIZE_F ;
  5537. fromPos.z = TheTerrainLogic->getGroundHeight(fromPos.x , fromPos.y);
  5538. Coord3D toPos;
  5539. toPos.x = newCellCoord.x * PATHFIND_CELL_SIZE_F ;
  5540. toPos.y = newCellCoord.y * PATHFIND_CELL_SIZE_F ;
  5541. toPos.z = TheTerrainLogic->getGroundHeight(toPos.x , toPos.y);
  5542. if ( fromPos.z < toPos.z )
  5543. continue;
  5544. }
  5545. Bool movementValid = true;
  5546. Bool dozerHack = false;
  5547. if (validMovementPosition( isCrusher, locomotorSet.getValidSurfaces(), newCell, parentCell )) {
  5548. } else {
  5549. movementValid = false;
  5550. if (obj->isKindOf(KINDOF_DOZER)) {
  5551. if (newCell->getType()==PathfindCell::CELL_OBSTACLE) {
  5552. Object *obstacle = TheGameLogic->findObjectByID(newCell->getObstacleID());
  5553. if (obstacle && !(obj->getRelationship(obstacle)==ENEMIES)) {
  5554. movementValid = true;
  5555. dozerHack = true;
  5556. }
  5557. }
  5558. }
  5559. if (!movementValid && !m_isTunneling) {
  5560. continue;
  5561. }
  5562. }
  5563. if (!dozerHack)
  5564. neighborFlags[i] = true;
  5565. TCheckMovementInfo info;
  5566. info.cell = newCellCoord;
  5567. info.layer = parentCell->getLayer();
  5568. info.centerInCell = centerInCell;
  5569. info.radius = radius;
  5570. info.considerTransient = false;
  5571. info.acceptableSurfaces = locomotorSet.getValidSurfaces();
  5572. Int dx = newCellCoord.x-startCellNdx.x;
  5573. Int dy = newCellCoord.y-startCellNdx.y;
  5574. if (dx<0) dx = -dx;
  5575. if (dy<0) dy = -dy;
  5576. if (dx>1+radius) info.considerTransient = false;
  5577. if (dy>1+radius) info.considerTransient = false;
  5578. if (!checkForMovement(obj, info) || info.enemyFixed) {
  5579. if (!m_isTunneling) {
  5580. continue;
  5581. }
  5582. movementValid = false;
  5583. }
  5584. if (movementValid && !newCell->getPinched()) {
  5585. //Note to self - only turn off tunneling after check for movement.jba.
  5586. m_isTunneling = false;
  5587. }
  5588. if (!newCell->hasInfo()) {
  5589. if (!newCell->allocateInfo(newCellCoord)) {
  5590. // Out of cells for pathing...
  5591. return cellCount;
  5592. }
  5593. cellCount++;
  5594. }
  5595. newCostSoFar = newCell->costSoFar( parentCell );
  5596. if (info.allyMoving && dx<10 && dy<10) {
  5597. newCostSoFar += 3*COST_DIAGONAL;
  5598. }
  5599. if (newCell->getType() == PathfindCell::CELL_CLIFF && !newCell->getPinched() ) {
  5600. Coord3D fromPos;
  5601. fromPos.x = parentCell->getXIndex() * PATHFIND_CELL_SIZE_F ;
  5602. fromPos.y = parentCell->getYIndex() * PATHFIND_CELL_SIZE_F ;
  5603. fromPos.z = TheTerrainLogic->getGroundHeight(fromPos.x , fromPos.y);
  5604. Coord3D toPos;
  5605. toPos.x = newCellCoord.x * PATHFIND_CELL_SIZE_F ;
  5606. toPos.y = newCellCoord.y * PATHFIND_CELL_SIZE_F ;
  5607. toPos.z = TheTerrainLogic->getGroundHeight(toPos.x , toPos.y);
  5608. if ( fabs(fromPos.z - toPos.z)<PATHFIND_CELL_SIZE_F) {
  5609. newCostSoFar += 7*COST_DIAGONAL;
  5610. }
  5611. } else if (newCell->getPinched()) {
  5612. newCostSoFar += COST_ORTHOGONAL;
  5613. }
  5614. newCell->setBlockedByAlly(false);
  5615. if (info.allyFixedCount>0) {
  5616. Int costFactor = 3*COST_DIAGONAL;
  5617. if (attackDistance != NO_ATTACK) {
  5618. costFactor = 3*COST_DIAGONAL;
  5619. }
  5620. if (canPathThroughUnits) {
  5621. newCostSoFar += costFactor;
  5622. } else {
  5623. newCell->setBlockedByAlly(true);
  5624. newCostSoFar += costFactor;
  5625. }
  5626. }
  5627. Int costRemaining = 0;
  5628. if (goalCell) {
  5629. if (attackDistance == NO_ATTACK) {
  5630. costRemaining = newCell->costToGoal( goalCell );
  5631. } else {
  5632. dx = newCellCoord.x - goalCell->getXIndex();
  5633. dy = newCellCoord.y - goalCell->getYIndex();
  5634. costRemaining = COST_ORTHOGONAL*sqrt(dx*dx + dy*dy);
  5635. costRemaining -= attackDistance/2;
  5636. if (costRemaining<0) costRemaining=0;
  5637. if (info.allyGoal) {
  5638. if (obj->isKindOf(KINDOF_VEHICLE)) {
  5639. newCostSoFar += 3*COST_ORTHOGONAL;
  5640. } else {
  5641. // Infantry can pass through infantry.
  5642. newCostSoFar += COST_ORTHOGONAL;
  5643. }
  5644. }
  5645. }
  5646. }
  5647. if (notZonePassable) {
  5648. newCostSoFar += 100*COST_ORTHOGONAL;
  5649. }
  5650. if (newCell->getType()==PathfindCell::CELL_OBSTACLE) {
  5651. newCostSoFar += 100*COST_ORTHOGONAL;
  5652. }
  5653. // check if this neighbor cell is already on the open (waiting to be tried)
  5654. // or closed (already tried) lists
  5655. if (onList)
  5656. {
  5657. // already on one of the lists - if existing costSoFar is less,
  5658. // the new cell is on a longer path, so skip it
  5659. if (newCell->getCostSoFar() <= newCostSoFar)
  5660. continue;
  5661. }
  5662. if (m_isTunneling) {
  5663. if (!validMovementPosition( isCrusher, locomotorSet.getValidSurfaces(), newCell, parentCell )) {
  5664. newCostSoFar += 10*COST_ORTHOGONAL;
  5665. }
  5666. }
  5667. newCell->setCostSoFar(newCostSoFar);
  5668. // keep track of path we're building - point back to cell we moved here from
  5669. newCell->setParentCell(parentCell) ;
  5670. if (m_isTunneling) {
  5671. costRemaining = 0; // find the closest valid cell.
  5672. }
  5673. newCell->setTotalCost(newCell->getCostSoFar() + costRemaining) ;
  5674. //DEBUG_LOG(("Cell (%d,%d), Parent cost %d, newCostSoFar %d, cost rem %d, tot %d\n",
  5675. // newCell->getXIndex(), newCell->getYIndex(),
  5676. // newCell->costSoFar(parentCell), newCostSoFar, costRemaining, newCell->getCostSoFar() + costRemaining));
  5677. // if newCell was on closed list, remove it from the list
  5678. if (newCell->getClosed())
  5679. m_closedList = newCell->removeFromClosedList( m_closedList );
  5680. // if the newCell was already on the open list, remove it so it can be re-inserted in order
  5681. if (newCell->getOpen())
  5682. m_openList = newCell->removeFromOpenList( m_openList );
  5683. // insert newCell in open list such that open list is sorted, smallest total path cost first
  5684. m_openList = newCell->putOnSortedOpenList( m_openList );
  5685. }
  5686. return cellCount;
  5687. }
  5688. /**
  5689. * Find a short, valid path between given locations.
  5690. * Uses A* algorithm.
  5691. */
  5692. Path *Pathfinder::findPath( Object *obj, const LocomotorSet& locomotorSet, const Coord3D *from,
  5693. const Coord3D *rawTo)
  5694. {
  5695. if (!clientSafeQuickDoesPathExist(locomotorSet, from, rawTo)) {
  5696. return NULL;
  5697. }
  5698. Bool isHuman = true;
  5699. if (obj && obj->getControllingPlayer() && (obj->getControllingPlayer()->getPlayerType()==PLAYER_COMPUTER)) {
  5700. isHuman = false; // computer gets to cheat.
  5701. }
  5702. m_zoneManager.clearPassableFlags();
  5703. Path *hPat = findHierarchicalPath(isHuman, locomotorSet, from, rawTo, false);
  5704. if (hPat) {
  5705. hPat->deleteInstance();
  5706. } else {
  5707. m_zoneManager.setAllPassable();
  5708. }
  5709. Path *pat = internalFindPath(obj, locomotorSet, from, rawTo);
  5710. if (pat!=NULL) {
  5711. return pat;
  5712. }
  5713. /* hierarchical build path code.
  5714. if (pat) {
  5715. Path *path = newInstance(Path);
  5716. PathNode *prior = NULL;
  5717. for (PathNode *node = pat->getLastNode(); node; node=node->getPrevious()) {
  5718. Bool unobstructed = true;
  5719. if (prior!=NULL) {
  5720. unobstructed = isLinePassable( obj, locomotorSet.getValidSurfaces(),
  5721. prior->getLayer(), *prior->getPosition(), *node->getPosition(), false, true);
  5722. }
  5723. if (unobstructed) {
  5724. path->prependNode( node->getPosition(), node->getLayer() );
  5725. path->getFirstNode()->setCanOptimize(node->getCanOptimize());
  5726. path->getFirstNode()->setNextOptimized(path->getFirstNode()->getNext());
  5727. } else {
  5728. Path *linkPath = findClosestPath(obj, locomotorSet, node->getPosition(),
  5729. prior->getPosition(), false, 0, true);
  5730. if (linkPath==NULL) {
  5731. DEBUG_LOG(("Couldn't find path - unexpected. jba.\n"));
  5732. continue;
  5733. }
  5734. PathNode *linkNode = linkPath->getLastNode();
  5735. if (linkNode==NULL) {
  5736. DEBUG_LOG(("Empty path - unexpected. jba.\n"));
  5737. continue;
  5738. }
  5739. for (linkNode=linkNode->getPrevious(); linkNode; linkNode=linkNode->getPrevious()) {
  5740. path->prependNode( linkNode->getPosition(), linkNode->getLayer() );
  5741. path->getFirstNode()->setCanOptimize(linkNode->getCanOptimize());
  5742. path->getFirstNode()->setNextOptimized(path->getFirstNode()->getNext());
  5743. }
  5744. linkPath->deleteInstance();
  5745. }
  5746. prior = node;
  5747. }
  5748. pat->deleteInstance();
  5749. path->optimize(obj, locomotorSet.getValidSurfaces(), false);
  5750. if (TheGlobalData->m_debugAI) {
  5751. setDebugPath(path);
  5752. }
  5753. return path;
  5754. }
  5755. */
  5756. return NULL;
  5757. }
  5758. /**
  5759. * Find a short, valid path between given locations.
  5760. * Uses A* algorithm.
  5761. */
  5762. Path *Pathfinder::internalFindPath( Object *obj, const LocomotorSet& locomotorSet, const Coord3D *from,
  5763. const Coord3D *rawTo)
  5764. {
  5765. //CRCDEBUG_LOG(("Pathfinder::findPath()\n"));
  5766. #ifdef INTENSE_DEBUG
  5767. DEBUG_LOG(("internal find path...\n"));
  5768. #endif
  5769. #if defined _DEBUG || defined _INTERNAL
  5770. Int startTimeMS = ::GetTickCount();
  5771. #endif
  5772. Bool centerInCell = true;
  5773. Int radius = 0;
  5774. if (obj) {
  5775. getRadiusAndCenter(obj, radius, centerInCell);
  5776. }
  5777. Bool isHuman = true;
  5778. if (obj && obj->getControllingPlayer() && (obj->getControllingPlayer()->getPlayerType()==PLAYER_COMPUTER)) {
  5779. isHuman = false; // computer gets to cheat.
  5780. }
  5781. if (rawTo->x == 0.0f && rawTo->y == 0.0f) {
  5782. DEBUG_LOG(("Attempting pathfind to 0,0, generally a bug.\n"));
  5783. return NULL;
  5784. }
  5785. DEBUG_ASSERTCRASH(m_openList==NULL && m_closedList == NULL, ("Dangling lists."));
  5786. if (m_isMapReady == false) {
  5787. return NULL;
  5788. }
  5789. Coord3D adjustTo = *rawTo;
  5790. Coord3D *to = &adjustTo;
  5791. Coord3D clipFrom = *from;
  5792. clip(&clipFrom, &adjustTo);
  5793. if (!centerInCell) {
  5794. adjustTo.x += PATHFIND_CELL_SIZE_F/2;
  5795. adjustTo.y += PATHFIND_CELL_SIZE_F/2;
  5796. }
  5797. m_isTunneling = false;
  5798. PathfindLayerEnum destinationLayer = TheTerrainLogic->getLayerForDestination(to);
  5799. // determine goal cell
  5800. PathfindCell *goalCell = getCell( destinationLayer, to );
  5801. if (goalCell == NULL) {
  5802. return NULL;
  5803. }
  5804. ICoord2D cell;
  5805. worldToCell( to, &cell );
  5806. if (!checkDestination(obj, cell.x, cell.y, destinationLayer, radius, centerInCell)) {
  5807. return false;
  5808. }
  5809. // determine start cell
  5810. ICoord2D startCellNdx;
  5811. worldToCell(&clipFrom, &startCellNdx);
  5812. PathfindLayerEnum layer = LAYER_GROUND;
  5813. if (obj) {
  5814. layer = obj->getLayer();
  5815. }
  5816. PathfindCell *parentCell = getClippedCell( layer,&clipFrom );
  5817. if (parentCell == NULL) {
  5818. return NULL;
  5819. }
  5820. ICoord2D pos2d;
  5821. worldToCell(to, &pos2d);
  5822. if (!goalCell->allocateInfo(pos2d)) {
  5823. return NULL;
  5824. }
  5825. if (parentCell!=goalCell) {
  5826. worldToCell(&clipFrom, &pos2d);
  5827. if (!parentCell->allocateInfo(pos2d)) {
  5828. goalCell->releaseInfo();
  5829. return NULL;
  5830. }
  5831. }
  5832. //
  5833. // Determine if this pathfind is "tunneling" or not.
  5834. // A tunneling pathfind starts from within an obstacle, and is allowed
  5835. // to ignore obstacle cells until it reaches a cell that is no longer
  5836. // classified as an obstacle. At that point, the pathfind behaves normally.
  5837. //
  5838. if (parentCell->getType() == PathfindCell::CELL_OBSTACLE) {
  5839. m_isTunneling = true;
  5840. }
  5841. else {
  5842. m_isTunneling = false;
  5843. }
  5844. Int zone1, zone2;
  5845. Bool isCrusher = obj ? obj->getCrusherLevel() > 0 : false;
  5846. zone1 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), isCrusher, parentCell->getZone());
  5847. zone2 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), isCrusher, goalCell->getZone());
  5848. if (layer==LAYER_WALL && zone1 == 0) {
  5849. return NULL;
  5850. }
  5851. if (destinationLayer==LAYER_WALL && zone2 == 0) {
  5852. return NULL;
  5853. }
  5854. if (goalCell->isObstaclePresent(m_ignoreObstacleID) || m_isTunneling) {
  5855. // Use terrain zones instead of building zones, since we are moving into or out of a building.
  5856. zone2 = m_zoneManager.getEffectiveTerrainZone(zone2);
  5857. zone2 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), isCrusher, zone2);
  5858. zone1 = m_zoneManager.getEffectiveTerrainZone(zone1);
  5859. zone1 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), isCrusher, zone1);
  5860. }
  5861. //DEBUG_LOG(("Zones %d to %d\n", zone1, zone2));
  5862. if ( zone1 != zone2) {
  5863. //DEBUG_LOG(("Intense Debug Info - Pathfind Zone screen failed-cannot reach desired location.\n"));
  5864. goalCell->releaseInfo();
  5865. parentCell->releaseInfo();
  5866. return NULL;
  5867. }
  5868. // sanity check - if destination is invalid, can't path there
  5869. if (validMovementPosition( isCrusher, destinationLayer, locomotorSet, to ) == false) {
  5870. m_isTunneling = false;
  5871. goalCell->releaseInfo();
  5872. parentCell->releaseInfo();
  5873. return NULL;
  5874. }
  5875. // sanity check - if source is invalid, we have to cheat
  5876. if (validMovementPosition( isCrusher, layer, locomotorSet, from ) == false) {
  5877. // somehow we got to an impassable location.
  5878. m_isTunneling = true;
  5879. }
  5880. parentCell->startPathfind(goalCell);
  5881. // initialize "open" list to contain start cell
  5882. m_openList = parentCell;
  5883. // "closed" list is initially empty
  5884. m_closedList = NULL;
  5885. Int cellCount = 0;
  5886. //
  5887. // Continue search until "open" list is empty, or
  5888. // until goal is found.
  5889. //
  5890. while( m_openList != NULL )
  5891. {
  5892. // take head cell off of open list - it has lowest estimated total path cost
  5893. parentCell = m_openList;
  5894. m_openList = parentCell->removeFromOpenList(m_openList);
  5895. if (parentCell == goalCell)
  5896. {
  5897. // success - found a path to the goal
  5898. Bool show = TheGlobalData->m_debugAI==AI_DEBUG_PATHS;
  5899. #ifdef INTENSE_DEBUG
  5900. DEBUG_LOG(("internal find path SUCCESS...\n"));
  5901. Int count = 0;
  5902. if (cellCount>1000 && obj) {
  5903. show = true;
  5904. DEBUG_LOG(("cells %d obj %s %x from (%f,%f) to(%f, %f)\n", count, obj->getTemplate()->getName().str(), obj, from->x, from->y, to->x, to->y));
  5905. #ifdef STATE_MACHINE_DEBUG
  5906. if( obj->getAIUpdateInterface() )
  5907. {
  5908. DEBUG_LOG(("State %s\n", obj->getAIUpdateInterface()->getCurrentStateName().str()));
  5909. }
  5910. #endif
  5911. TheScriptEngine->AppendDebugMessage("Big path", false);
  5912. }
  5913. #endif
  5914. if (show)
  5915. debugShowSearch(true);
  5916. m_isTunneling = false;
  5917. // construct and return path
  5918. Path *path = buildActualPath( obj, locomotorSet.getValidSurfaces(), from, goalCell, centerInCell, false );
  5919. parentCell->releaseInfo();
  5920. cleanOpenAndClosedLists();
  5921. return path;
  5922. }
  5923. // put parent cell onto closed list - its evaluation is finished
  5924. m_closedList = parentCell->putOnClosedList( m_closedList );
  5925. // Check to see if we can change layers in this cell.
  5926. checkChangeLayers(parentCell);
  5927. cellCount += examineNeighboringCells(parentCell, goalCell, locomotorSet, isHuman, centerInCell, radius, startCellNdx, obj, NO_ATTACK);
  5928. }
  5929. // failure - goal cannot be reached
  5930. #if defined _DEBUG || defined _INTERNAL
  5931. #ifdef INTENSE_DEBUG
  5932. DEBUG_LOG(("internal find path FAILURE...\n"));
  5933. #endif
  5934. if (TheGlobalData->m_debugAI == AI_DEBUG_PATHS)
  5935. {
  5936. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  5937. RGBColor color;
  5938. color.blue = 0;
  5939. color.red = color.green = 1;
  5940. addIcon(NULL, 0, 0, color);
  5941. debugShowSearch(false);
  5942. Coord3D pos;
  5943. pos = *from;
  5944. pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y ) + 0.5f;
  5945. addIcon(&pos, 3*PATHFIND_CELL_SIZE_F, 600, color);
  5946. pos = *to;
  5947. pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y ) + 0.5f;
  5948. addIcon(&pos, 3*PATHFIND_CELL_SIZE_F, 600, color);
  5949. Real dx, dy;
  5950. dx = from->x - to->x;
  5951. dy = from->y - to->y;
  5952. Int count = sqrt(dx*dx+dy*dy)/(PATHFIND_CELL_SIZE_F/2);
  5953. if (count<2) count = 2;
  5954. Int i;
  5955. color.green = 0;
  5956. for (i=1; i<count; i++) {
  5957. pos.x = from->x + (to->x-from->x)*i/count;
  5958. pos.y = from->y + (to->y-from->y)*i/count;
  5959. pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y ) + 0.5f;
  5960. addIcon(&pos, PATHFIND_CELL_SIZE_F/2, 60, color);
  5961. }
  5962. }
  5963. if (obj) {
  5964. Bool valid;
  5965. valid = validMovementPosition( isCrusher, obj->getLayer(), locomotorSet, to ) ;
  5966. DEBUG_LOG(("%d ", TheGameLogic->getFrame()));
  5967. DEBUG_LOG(("Pathfind failed from (%f,%f) to (%f,%f), OV %d\n", from->x, from->y, to->x, to->y, valid));
  5968. DEBUG_LOG(("Unit '%s', time %f, cells %d\n", obj->getTemplate()->getName().str(), (::GetTickCount()-startTimeMS)/1000.0f,cellCount));
  5969. #ifdef DUMP_PERF_STATS
  5970. TheGameLogic->incrementOverallFailedPathfinds();
  5971. #endif
  5972. #ifdef STATE_MACHINE_DEBUG
  5973. if( obj->getAIUpdateInterface() )
  5974. {
  5975. DEBUG_LOG(("state %s\n", obj->getAIUpdateInterface()->getCurrentStateName().str()));
  5976. }
  5977. #endif
  5978. }
  5979. #endif
  5980. m_isTunneling = false;
  5981. goalCell->releaseInfo();
  5982. cleanOpenAndClosedLists();
  5983. return NULL;
  5984. }
  5985. /**
  5986. * Checks to see if there is enough path width at this cell for ground
  5987. * movement. Returns the width available.
  5988. */
  5989. Int Pathfinder::clearCellForDiameter(Bool crusher, Int cellX, Int cellY, PathfindLayerEnum layer, Int pathDiameter)
  5990. {
  5991. Int radius = pathDiameter/2;
  5992. Int numCellsAbove = radius;
  5993. if (radius==0) numCellsAbove++;
  5994. Int i, j;
  5995. Bool clear = true;
  5996. Bool cutCorners = false;
  5997. if (radius>1) {
  5998. cutCorners = true;
  5999. // We remove the outside corner cells from the check.
  6000. }
  6001. for (i=cellX-radius; i<cellX+numCellsAbove; i++) {
  6002. Bool xMinOrMax = (i==cellX-radius) || (i==cellX+numCellsAbove-1);
  6003. for (j=cellY-radius; j<cellY+numCellsAbove; j++) {
  6004. Bool yMinOrMax = (j==cellY-radius) || (j==cellY+numCellsAbove-1);
  6005. if (xMinOrMax && yMinOrMax && cutCorners) {
  6006. continue; // this is an outside corner cell, and we are cutting corners. jba. :)
  6007. }
  6008. PathfindCell *cell = getCell(layer, i, j);
  6009. if (cell) {
  6010. if (cell->getType() != PathfindCell::CELL_CLEAR) {
  6011. if (cell->getType() == PathfindCell::CELL_OBSTACLE) {
  6012. if (cell->isObstacleFence()) {
  6013. if (!crusher) {
  6014. clear = false;
  6015. }
  6016. } else {
  6017. clear = false;
  6018. }
  6019. } else {
  6020. clear = false;
  6021. }
  6022. }
  6023. if (cell->getFlags() == PathfindCell::UNIT_PRESENT_FIXED && pathDiameter>=2) {
  6024. Object *obj = TheGameLogic->findObjectByID(cell->getPosUnit());
  6025. if (obj) {
  6026. if (crusher) {
  6027. if (obj->getCrushableLevel()>1) {
  6028. clear = false;
  6029. }
  6030. } else {
  6031. if (obj->getCrushableLevel()>0) {
  6032. clear = false;
  6033. }
  6034. }
  6035. }
  6036. }
  6037. } else {
  6038. return false; // off the map.
  6039. }
  6040. if (!clear) break;
  6041. }
  6042. }
  6043. if (clear) {
  6044. if (radius==0) return 1;
  6045. return 2*radius;
  6046. }
  6047. if (pathDiameter < 2) return 0;
  6048. return clearCellForDiameter(crusher, cellX, cellY, layer, pathDiameter-2);
  6049. }
  6050. /**
  6051. * Work backwards from goal cell to construct final path.
  6052. */
  6053. Path *Pathfinder::buildGroundPath(Bool isCrusher, const Coord3D *fromPos, PathfindCell *goalCell, Bool center, Int pathDiameter )
  6054. {
  6055. DEBUG_ASSERTCRASH( goalCell, ("Pathfinder::buildActualPath: goalCell == NULL") );
  6056. Path *path = newInstance(Path);
  6057. prependCells(path, fromPos, goalCell, center);
  6058. // cleanup the path by checking line of sight
  6059. path->optimizeGroundPath( isCrusher, pathDiameter );
  6060. #if defined _DEBUG || defined _INTERNAL
  6061. if (TheGlobalData->m_debugAI==AI_DEBUG_GROUND_PATHS)
  6062. {
  6063. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  6064. RGBColor color;
  6065. color.blue = 0;
  6066. color.red = color.green = 1;
  6067. Coord3D pos;
  6068. for( PathNode *node = path->getFirstNode(); node; node = node->getNext() )
  6069. {
  6070. // create objects to show path - they decay
  6071. pos = *node->getPosition();
  6072. color.red = color.green = 1;
  6073. if (node->getLayer() != LAYER_GROUND) {
  6074. color.red = 0;
  6075. }
  6076. addIcon(&pos, PATHFIND_CELL_SIZE_F*.25f, 200, color);
  6077. }
  6078. // show optimized path
  6079. for( node = path->getFirstNode(); node; node = node->getNextOptimized() )
  6080. {
  6081. pos = *node->getPosition();
  6082. addIcon(&pos, PATHFIND_CELL_SIZE_F*.8f, 200, color);
  6083. }
  6084. setDebugPath(path);
  6085. }
  6086. #endif
  6087. return path;
  6088. }
  6089. /**
  6090. * Work backwards from goal cell to construct final path.
  6091. */
  6092. Path *Pathfinder::buildHierachicalPath( const Coord3D *fromPos, PathfindCell *goalCell )
  6093. {
  6094. DEBUG_ASSERTCRASH( goalCell, ("Pathfinder::buildHierachicalPath: goalCell == NULL") );
  6095. Path *path = newInstance(Path);
  6096. prependCells(path, fromPos, goalCell, true);
  6097. // Expand the hierarchical path around the starting point. jba [8/24/2003]
  6098. // This allows the unit to get around friendly units that may be near it.
  6099. Coord3D pos = *path->getFirstNode()->getPosition();
  6100. Coord3D minPos = pos;
  6101. minPos.x -= PathfindZoneManager::ZONE_BLOCK_SIZE*PATHFIND_CELL_SIZE_F;
  6102. minPos.y -= PathfindZoneManager::ZONE_BLOCK_SIZE*PATHFIND_CELL_SIZE_F;
  6103. Coord3D maxPos = pos;
  6104. maxPos.x += PathfindZoneManager::ZONE_BLOCK_SIZE*PATHFIND_CELL_SIZE_F;
  6105. maxPos.y += PathfindZoneManager::ZONE_BLOCK_SIZE*PATHFIND_CELL_SIZE_F;
  6106. ICoord2D cellNdxMin, cellNdxMax;
  6107. worldToCell(&minPos, &cellNdxMin);
  6108. worldToCell(&maxPos, &cellNdxMax);
  6109. Int i, j;
  6110. for (i=cellNdxMin.x; i<=cellNdxMax.x; i++) {
  6111. for (j=cellNdxMin.y; j<=cellNdxMax.y; j++) {
  6112. m_zoneManager.setPassable(i, j, true);
  6113. }
  6114. }
  6115. #if defined _DEBUG || defined _INTERNAL
  6116. if (TheGlobalData->m_debugAI==AI_DEBUG_PATHS)
  6117. {
  6118. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  6119. RGBColor color;
  6120. color.blue = 0;
  6121. color.red = color.green = 1;
  6122. Coord3D pos;
  6123. Int i;
  6124. for (i=0; i<3; i++)
  6125. for( PathNode *node = path->getFirstNode(); node; node = node->getNext() )
  6126. {
  6127. // create objects to show path - they decay
  6128. pos = *node->getPosition();
  6129. color.red = 1;
  6130. color.green = 0.4f;
  6131. if (node->getLayer() != LAYER_GROUND) {
  6132. color.red = 0;
  6133. }
  6134. addIcon(&pos, PATHFIND_CELL_SIZE_F, 200, color);
  6135. }
  6136. setDebugPath(path);
  6137. }
  6138. #endif
  6139. return path;
  6140. }
  6141. struct MADStruct
  6142. {
  6143. Pathfinder *thePathfinder;
  6144. Object *obj;
  6145. ObjectID ignoreID;
  6146. };
  6147. /*static*/ Int Pathfinder::moveAlliesDestinationCallback(Pathfinder* pathfinder, PathfindCell* from, PathfindCell* to, Int to_x, Int to_y, void* userData)
  6148. {
  6149. MADStruct* d = (MADStruct*)userData;
  6150. if (to) {
  6151. if (to->getPosUnit()==INVALID_ID) {
  6152. return 0;
  6153. }
  6154. if (to->getPosUnit()==d->obj->getID()) {
  6155. return 0; // It's us.
  6156. }
  6157. if (to->getPosUnit()==d->ignoreID) {
  6158. return 0; // It's the one we are ignoring.
  6159. }
  6160. Object *otherObj = TheGameLogic->findObjectByID(to->getPosUnit());
  6161. if (otherObj==NULL) return 0;
  6162. if (d->obj->getRelationship(otherObj)!=ALLIES) {
  6163. return 0; // Only move allies.
  6164. }
  6165. if( otherObj && otherObj->getAI() && !otherObj->getAI()->isMoving() )
  6166. {
  6167. //Kris: Patch 1.01 November 3, 2003
  6168. //Black Lotus exploit fix -- moving while hacking.
  6169. if( otherObj->testStatus( OBJECT_STATUS_IS_USING_ABILITY ) || otherObj->getAI()->isBusy() )
  6170. {
  6171. return 0; // Packing or unpacking objects for example
  6172. }
  6173. //DEBUG_LOG(("Moving ally\n"));
  6174. otherObj->getAI()->aiMoveAwayFromUnit(d->obj, CMD_FROM_AI);
  6175. }
  6176. }
  6177. return 0; // keep going
  6178. }
  6179. void Pathfinder::moveAlliesAwayFromDestination(Object *obj,const Coord3D& destination)
  6180. {
  6181. MADStruct info;
  6182. info.obj = obj;
  6183. info.ignoreID = obj->getAI()->getIgnoredObstacleID();
  6184. info.thePathfinder = this;
  6185. PathfindLayerEnum layer = obj->getLayer();
  6186. if (layer==LAYER_GROUND) {
  6187. layer = TheTerrainLogic->getLayerForDestination(&destination);
  6188. }
  6189. iterateCellsAlongLine(*obj->getPosition(), destination, layer, moveAlliesDestinationCallback, &info);
  6190. }
  6191. struct GroundCellsStruct
  6192. {
  6193. Pathfinder *thePathfinder;
  6194. Bool centerInCell;
  6195. Int pathDiameter;
  6196. PathfindCell *goalCell;
  6197. Bool crusher;
  6198. };
  6199. /*static*/ Int Pathfinder::groundCellsCallback(Pathfinder* pathfinder, PathfindCell* from, PathfindCell* to, Int to_x, Int to_y, void* userData)
  6200. {
  6201. GroundCellsStruct* d = (GroundCellsStruct*)userData;
  6202. if (from && to) {
  6203. if (to->hasInfo()) {
  6204. if (to->getOpen() || to->getClosed())
  6205. {
  6206. // already on one of the lists
  6207. return 1; // abort.
  6208. }
  6209. }
  6210. // See how wide the cell is.
  6211. Int clearDiameter = d->thePathfinder->clearCellForDiameter(d->crusher, to_x, to_y, to->getLayer(), d->pathDiameter);
  6212. if (clearDiameter != d->pathDiameter) {
  6213. return 1;
  6214. }
  6215. ICoord2D newCellCoord;
  6216. newCellCoord.x = to_x;
  6217. newCellCoord.y = to_y;
  6218. if (!to->allocateInfo(newCellCoord)) {
  6219. // Out of cells for pathing...
  6220. return 1;
  6221. }
  6222. UnsignedInt newCostSoFar = from->getCostSoFar( ) + 0.5f*COST_ORTHOGONAL;
  6223. to->setBlockedByAlly(false);
  6224. Int costRemaining = 0;
  6225. costRemaining = to->costToGoal( d->goalCell );
  6226. to->setCostSoFar(newCostSoFar);
  6227. // keep track of path we're building - point back to cell we moved here from
  6228. to->setParentCell(from) ;
  6229. to->setTotalCost(to->getCostSoFar() + costRemaining) ;
  6230. // insert to in open list such that open list is sorted, smallest total path cost first
  6231. d->thePathfinder->m_openList = to->putOnSortedOpenList( d->thePathfinder->m_openList );
  6232. }
  6233. return 0; // keep going
  6234. }
  6235. /**
  6236. * Find a short, valid path between given locations.
  6237. * Uses A* algorithm.
  6238. */
  6239. Path *Pathfinder::findGroundPath( const Coord3D *from,
  6240. const Coord3D *rawTo, Int pathDiameter, Bool crusher)
  6241. {
  6242. //CRCDEBUG_LOG(("Pathfinder::findGroundPath()\n"));
  6243. #if defined _DEBUG || defined _INTERNAL
  6244. Int startTimeMS = ::GetTickCount();
  6245. #endif
  6246. #ifdef INTENSE_DEBUG
  6247. DEBUG_LOG(("Find ground path..."));
  6248. #endif
  6249. Bool centerInCell = false;
  6250. m_zoneManager.clearPassableFlags();
  6251. Bool isHuman = true;
  6252. Path *hPat = internal_findHierarchicalPath(isHuman, LOCOMOTORSURFACE_GROUND, from, rawTo, false, false);
  6253. if (hPat) {
  6254. hPat->deleteInstance();
  6255. } else {
  6256. m_zoneManager.setAllPassable();
  6257. }
  6258. if (rawTo->x == 0.0f && rawTo->y == 0.0f) {
  6259. DEBUG_LOG(("Attempting pathfind to 0,0, generally a bug.\n"));
  6260. return NULL;
  6261. }
  6262. DEBUG_ASSERTCRASH(m_openList==NULL && m_closedList == NULL, ("Dangling lists."));
  6263. if (m_isMapReady == false) {
  6264. return NULL;
  6265. }
  6266. Coord3D adjustTo = *rawTo;
  6267. Coord3D *to = &adjustTo;
  6268. Coord3D clipFrom = *from;
  6269. clip(&clipFrom, &adjustTo);
  6270. m_isTunneling = false;
  6271. PathfindLayerEnum destinationLayer = TheTerrainLogic->getLayerForDestination(to);
  6272. ICoord2D cell;
  6273. worldToCell( to, &cell );
  6274. if (pathDiameter!=clearCellForDiameter(crusher, cell.x, cell.y, destinationLayer, pathDiameter)) {
  6275. Int offset=1;
  6276. ICoord2D newCell;
  6277. const Int MAX_OFFSET = 8;
  6278. while (offset<MAX_OFFSET) {
  6279. newCell = cell;
  6280. cell.x += offset;
  6281. if (clearCellForDiameter(crusher, cell.x, cell.y, destinationLayer, pathDiameter)==pathDiameter) break;
  6282. cell.y += offset;
  6283. if (clearCellForDiameter(crusher, cell.x, cell.y, destinationLayer, pathDiameter)==pathDiameter) break;
  6284. cell.x -= offset;
  6285. if (clearCellForDiameter(crusher, cell.x, cell.y, destinationLayer, pathDiameter)==pathDiameter) break;
  6286. cell.x -= offset;
  6287. if (clearCellForDiameter(crusher, cell.x, cell.y, destinationLayer, pathDiameter)==pathDiameter) break;
  6288. cell.y -= offset;
  6289. if (clearCellForDiameter(crusher, cell.x, cell.y, destinationLayer, pathDiameter)==pathDiameter) break;
  6290. cell.y -= offset;
  6291. if (clearCellForDiameter(crusher, cell.x, cell.y, destinationLayer, pathDiameter)==pathDiameter) break;
  6292. cell.x += offset;
  6293. if (clearCellForDiameter(crusher, cell.x, cell.y, destinationLayer, pathDiameter)==pathDiameter) break;
  6294. cell.x += offset;
  6295. if (clearCellForDiameter(crusher, cell.x, cell.y, destinationLayer, pathDiameter)==pathDiameter) break;
  6296. offset++;
  6297. cell = newCell;
  6298. }
  6299. if (offset >= MAX_OFFSET) {
  6300. return NULL;
  6301. }
  6302. }
  6303. // determine goal cell
  6304. PathfindCell *goalCell = getCell( destinationLayer, cell.x, cell.y );
  6305. if (goalCell == NULL) {
  6306. return NULL;
  6307. }
  6308. if (!goalCell->allocateInfo(cell)) {
  6309. return NULL;
  6310. }
  6311. // determine start cell
  6312. ICoord2D startCellNdx;
  6313. PathfindLayerEnum layer = TheTerrainLogic->getLayerForDestination(from);
  6314. PathfindCell *parentCell = getClippedCell( layer,&clipFrom );
  6315. if (parentCell == NULL) {
  6316. return NULL;
  6317. }
  6318. if (parentCell!=goalCell) {
  6319. worldToCell(&clipFrom, &startCellNdx);
  6320. if (!parentCell->allocateInfo(startCellNdx)) {
  6321. goalCell->releaseInfo();
  6322. return NULL;
  6323. }
  6324. }
  6325. Int zone1, zone2;
  6326. // m_isCrusher = false;
  6327. zone1 = m_zoneManager.getEffectiveZone(LOCOMOTORSURFACE_GROUND, false, parentCell->getZone());
  6328. zone2 = m_zoneManager.getEffectiveZone(LOCOMOTORSURFACE_GROUND, false, goalCell->getZone());
  6329. //DEBUG_LOG(("Zones %d to %d\n", zone1, zone2));
  6330. if ( zone1 != zone2) {
  6331. goalCell->releaseInfo();
  6332. parentCell->releaseInfo();
  6333. return NULL;
  6334. }
  6335. parentCell->startPathfind(goalCell);
  6336. // initialize "open" list to contain start cell
  6337. m_openList = parentCell;
  6338. // "closed" list is initially empty
  6339. m_closedList = NULL;
  6340. //
  6341. // Continue search until "open" list is empty, or
  6342. // until goal is found.
  6343. //
  6344. Int cellCount = 0;
  6345. while( m_openList != NULL )
  6346. {
  6347. // take head cell off of open list - it has lowest estimated total path cost
  6348. parentCell = m_openList;
  6349. m_openList = parentCell->removeFromOpenList(m_openList);
  6350. if (parentCell == goalCell)
  6351. {
  6352. // success - found a path to the goal
  6353. #ifdef INTENSE_DEBUG
  6354. DEBUG_LOG((" time %d msec %d cells", (::GetTickCount()-startTimeMS), cellCount));
  6355. DEBUG_LOG((" SUCCESS\n"));
  6356. #endif
  6357. #if defined _DEBUG || defined _INTERNAL
  6358. Bool show = TheGlobalData->m_debugAI==AI_DEBUG_GROUND_PATHS;
  6359. if (show)
  6360. debugShowSearch(true);
  6361. #endif
  6362. m_isTunneling = false;
  6363. // construct and return path
  6364. Path *path = buildGroundPath(crusher, from, goalCell, centerInCell, pathDiameter );
  6365. parentCell->releaseInfo();
  6366. cleanOpenAndClosedLists();
  6367. return path;
  6368. }
  6369. // put parent cell onto closed list - its evaluation is finished
  6370. m_closedList = parentCell->putOnClosedList( m_closedList );
  6371. // Check to see if we can change layers in this cell.
  6372. checkChangeLayers(parentCell);
  6373. GroundCellsStruct info;
  6374. info.thePathfinder = this;
  6375. info.centerInCell = centerInCell;
  6376. info.pathDiameter = pathDiameter;
  6377. info.goalCell = goalCell;
  6378. info.crusher = crusher;
  6379. ICoord2D start, end;
  6380. start.x = parentCell->getXIndex();
  6381. start.y = parentCell->getYIndex();
  6382. end.x = goalCell->getXIndex();
  6383. end.y = goalCell->getYIndex();
  6384. iterateCellsAlongLine(start, end, parentCell->getLayer(), groundCellsCallback, &info);
  6385. // expand search to neighboring orthogonal cells
  6386. static ICoord2D delta[] =
  6387. {
  6388. { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 },
  6389. { 1, 1 }, { -1, 1 }, { -1, -1 }, { 1, -1 }
  6390. };
  6391. const Int numNeighbors = 8;
  6392. const Int firstDiagonal = 4;
  6393. ICoord2D newCellCoord;
  6394. PathfindCell *newCell;
  6395. const Int adjacent[5] = {0, 1, 2, 3, 0};
  6396. Bool neighborFlags[8] = {false, false, false, false, false, false, false};
  6397. UnsignedInt newCostSoFar;
  6398. for( int i=0; i<numNeighbors; i++ )
  6399. {
  6400. neighborFlags[i] = false;
  6401. // determine neighbor cell to try
  6402. newCellCoord.x = parentCell->getXIndex() + delta[i].x;
  6403. newCellCoord.y = parentCell->getYIndex() + delta[i].y;
  6404. // get the neighboring cell
  6405. newCell = getCell(parentCell->getLayer(), newCellCoord.x, newCellCoord.y );
  6406. // check if cell is on the map
  6407. if (newCell == NULL)
  6408. continue;
  6409. if ((newCell->getLayer()==LAYER_GROUND) && !m_zoneManager.isPassable(newCellCoord.x, newCellCoord.y)) {
  6410. // check if we are within 3.
  6411. Bool passable = false;
  6412. if (m_zoneManager.clipIsPassable(newCellCoord.x+3, newCellCoord.y+3)) passable = true;
  6413. if (m_zoneManager.clipIsPassable(newCellCoord.x-3, newCellCoord.y+3)) passable = true;
  6414. if (m_zoneManager.clipIsPassable(newCellCoord.x+3, newCellCoord.y-3)) passable = true;
  6415. if (m_zoneManager.clipIsPassable(newCellCoord.x-3, newCellCoord.y-3)) passable = true;
  6416. if (!passable) continue;
  6417. }
  6418. // check if this neighbor cell is already on the open (waiting to be tried)
  6419. // or closed (already tried) lists
  6420. Bool onList = false;
  6421. if (newCell->hasInfo()) {
  6422. if (newCell->getOpen() || newCell->getClosed())
  6423. {
  6424. // already on one of the lists
  6425. onList = true;
  6426. }
  6427. }
  6428. Int clearDiameter = 0;
  6429. if (newCell!=goalCell) {
  6430. if (i>=firstDiagonal) {
  6431. // make sure one of the adjacent sides is open.
  6432. if (!neighborFlags[adjacent[i-4]] && !neighborFlags[adjacent[i-3]]) {
  6433. continue;
  6434. }
  6435. }
  6436. // See how wide the cell is.
  6437. clearDiameter = clearCellForDiameter(crusher, newCellCoord.x, newCellCoord.y, newCell->getLayer(), pathDiameter);
  6438. if (newCell->getType() != PathfindCell::CELL_CLEAR) {
  6439. continue;
  6440. }
  6441. if (newCell->getPinched()) {
  6442. continue;
  6443. }
  6444. neighborFlags[i] = true;
  6445. if (!newCell->allocateInfo(newCellCoord)) {
  6446. // Out of cells for pathing...
  6447. continue;
  6448. }
  6449. cellCount++;
  6450. newCostSoFar = newCell->costSoFar( parentCell );
  6451. if (clearDiameter<pathDiameter) {
  6452. int delta = pathDiameter-clearDiameter;
  6453. newCostSoFar += 0.6f*(delta*COST_ORTHOGONAL);
  6454. }
  6455. newCell->setBlockedByAlly(false);
  6456. }
  6457. Int costRemaining = 0;
  6458. costRemaining = newCell->costToGoal( goalCell );
  6459. // check if this neighbor cell is already on the open (waiting to be tried)
  6460. // or closed (already tried) lists
  6461. if (onList)
  6462. {
  6463. // already on one of the lists - if existing costSoFar is less,
  6464. // the new cell is on a longer path, so skip it
  6465. if (newCell->getCostSoFar() <= newCostSoFar)
  6466. continue;
  6467. }
  6468. //DEBUG_LOG(("CELL(%d,%d)L%d CD%d CSF %d, CR%d // ",newCell->getXIndex(), newCell->getYIndex(),
  6469. // newCell->getLayer(), clearDiameter, newCostSoFar, costRemaining));
  6470. //if ((cellCount&7)==0) DEBUG_LOG(("\n"));
  6471. newCell->setCostSoFar(newCostSoFar);
  6472. // keep track of path we're building - point back to cell we moved here from
  6473. newCell->setParentCell(parentCell) ;
  6474. newCell->setTotalCost(newCell->getCostSoFar() + costRemaining) ;
  6475. //DEBUG_LOG(("Cell (%d,%d), Parent cost %d, newCostSoFar %d, cost rem %d, tot %d\n",
  6476. // newCell->getXIndex(), newCell->getYIndex(),
  6477. // newCell->costSoFar(parentCell), newCostSoFar, costRemaining, newCell->getCostSoFar() + costRemaining));
  6478. // if newCell was on closed list, remove it from the list
  6479. if (newCell->getClosed())
  6480. m_closedList = newCell->removeFromClosedList( m_closedList );
  6481. // if the newCell was already on the open list, remove it so it can be re-inserted in order
  6482. if (newCell->getOpen())
  6483. m_openList = newCell->removeFromOpenList( m_openList );
  6484. // insert newCell in open list such that open list is sorted, smallest total path cost first
  6485. m_openList = newCell->putOnSortedOpenList( m_openList );
  6486. }
  6487. }
  6488. // failure - goal cannot be reached
  6489. #ifdef INTENSE_DEBUG
  6490. DEBUG_LOG((" FAILURE\n"));
  6491. #endif
  6492. #if defined _DEBUG || defined _INTERNAL
  6493. if (TheGlobalData->m_debugAI)
  6494. {
  6495. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  6496. RGBColor color;
  6497. color.blue = 0;
  6498. color.red = color.green = 1;
  6499. addIcon(NULL, 0, 0, color);
  6500. debugShowSearch(false);
  6501. Coord3D pos;
  6502. pos = *from;
  6503. pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y ) + 0.5f;
  6504. addIcon(&pos, 3*PATHFIND_CELL_SIZE_F, 600, color);
  6505. pos = *to;
  6506. pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y ) + 0.5f;
  6507. addIcon(&pos, 3*PATHFIND_CELL_SIZE_F, 600, color);
  6508. Real dx, dy;
  6509. dx = from->x - to->x;
  6510. dy = from->y - to->y;
  6511. Int count = sqrt(dx*dx+dy*dy)/(PATHFIND_CELL_SIZE_F/2);
  6512. if (count<2) count = 2;
  6513. Int i;
  6514. color.green = 0;
  6515. for (i=1; i<count; i++) {
  6516. pos.x = from->x + (to->x-from->x)*i/count;
  6517. pos.y = from->y + (to->y-from->y)*i/count;
  6518. pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y ) + 0.5f;
  6519. addIcon(&pos, PATHFIND_CELL_SIZE_F/2, 60, color);
  6520. }
  6521. }
  6522. DEBUG_LOG(("%d ", TheGameLogic->getFrame()));
  6523. DEBUG_LOG(("FindGroundPath failed from (%f,%f) to (%f,%f)\n", from->x, from->y, to->x, to->y));
  6524. DEBUG_LOG(("time %f\n", (::GetTickCount()-startTimeMS)/1000.0f));
  6525. #endif
  6526. #ifdef DUMP_PERF_STATS
  6527. TheGameLogic->incrementOverallFailedPathfinds();
  6528. #endif
  6529. m_isTunneling = false;
  6530. goalCell->releaseInfo();
  6531. cleanOpenAndClosedLists();
  6532. return NULL;
  6533. }
  6534. /**
  6535. * Find a short, valid path between given locations.
  6536. * Uses A* algorithm.
  6537. */
  6538. void Pathfinder::processHierarchicalCell( const ICoord2D &scanCell, const ICoord2D &delta, PathfindCell *parentCell,
  6539. PathfindCell *goalCell, zoneStorageType parentZone,
  6540. zoneStorageType *examinedZones, Int &numExZones,
  6541. Bool crusher, Int &cellCount)
  6542. {
  6543. if (scanCell.x<m_extent.lo.x || scanCell.x>m_extent.hi.x ||
  6544. scanCell.y<m_extent.lo.y || scanCell.y>m_extent.hi.y) {
  6545. return;
  6546. }
  6547. if (parentZone == PathfindZoneManager::UNINITIALIZED_ZONE) {
  6548. return;
  6549. }
  6550. if (parentZone == m_zoneManager.getBlockZone(LOCOMOTORSURFACE_GROUND,
  6551. crusher, scanCell.x, scanCell.y, m_map)) {
  6552. PathfindCell *newCell = getCell(LAYER_GROUND, scanCell.x, scanCell.y);
  6553. if( !newCell->hasInfo() )
  6554. {
  6555. return;
  6556. }
  6557. if( newCell->getOpen() || newCell->getClosed() )
  6558. return; // already looked at this one.
  6559. ICoord2D adjacentCell = scanCell;
  6560. //DEBUG_ASSERTCRASH(parentZone==newCell->getZone(), ("Different zones?"));
  6561. if (parentZone!=newCell->getZone()) return;
  6562. adjacentCell.x += delta.x;
  6563. adjacentCell.y += delta.y;
  6564. if (adjacentCell.x<m_extent.lo.x || adjacentCell.x>m_extent.hi.x ||
  6565. adjacentCell.y<m_extent.lo.y || adjacentCell.y>m_extent.hi.y) {
  6566. return;
  6567. }
  6568. PathfindCell *adjNewCell = getCell(LAYER_GROUND, adjacentCell.x, adjacentCell.y);
  6569. if (adjNewCell->hasInfo() && (adjNewCell->getOpen() || adjNewCell->getClosed())) return; // already looked at this one.
  6570. zoneStorageType parentGlobalZone = m_zoneManager.getEffectiveZone(LOCOMOTORSURFACE_GROUND, crusher, parentZone);
  6571. /// @todo - somehow out of bounds or bogus newZone.
  6572. zoneStorageType newZone = m_zoneManager.getBlockZone(LOCOMOTORSURFACE_GROUND,
  6573. crusher, adjacentCell.x, adjacentCell.y, m_map);
  6574. zoneStorageType newGlobalZone = m_zoneManager.getEffectiveZone(LOCOMOTORSURFACE_GROUND, crusher, newZone);
  6575. if (newGlobalZone != parentGlobalZone) {
  6576. return; // can't step over. jba.
  6577. }
  6578. Int j;
  6579. Bool found=false;
  6580. for (j=0; j<numExZones; j++) {
  6581. if (examinedZones[j] == newZone) {
  6582. found = true;
  6583. break;
  6584. }
  6585. }
  6586. if (found) {
  6587. return;
  6588. }
  6589. newCell->allocateInfo(scanCell);
  6590. if (!newCell->getClosed() && !newCell->getOpen()) {
  6591. m_closedList = newCell->putOnClosedList(m_closedList);
  6592. }
  6593. adjNewCell->allocateInfo(adjacentCell);
  6594. if( adjNewCell->hasInfo() )
  6595. {
  6596. cellCount++;
  6597. Int curCost = adjNewCell->costToHierGoal(parentCell);
  6598. Int remCost = adjNewCell->costToHierGoal(goalCell);
  6599. if (adjNewCell->getPinched() || newCell->getPinched()) {
  6600. curCost += 2*COST_ORTHOGONAL;
  6601. } else {
  6602. examinedZones[numExZones] = newZone;
  6603. numExZones++;
  6604. }
  6605. adjNewCell->setCostSoFar(parentCell->getCostSoFar() + curCost);
  6606. adjNewCell->setTotalCost(adjNewCell->getCostSoFar()+remCost);
  6607. adjNewCell->setParentCellHierarchical(parentCell);
  6608. // insert newCell in open list such that open list is sorted, smallest total path cost first
  6609. m_openList = adjNewCell->putOnSortedOpenList( m_openList );
  6610. }
  6611. }
  6612. }
  6613. /**
  6614. * Find a short, valid path between given locations.
  6615. * Uses A* algorithm.
  6616. */
  6617. Path *Pathfinder::findHierarchicalPath( Bool isHuman, const LocomotorSet& locomotorSet, const Coord3D *from,
  6618. const Coord3D *to, Bool crusher)
  6619. {
  6620. return internal_findHierarchicalPath(isHuman, locomotorSet.getValidSurfaces(), from, to, crusher, FALSE);
  6621. }
  6622. /**
  6623. * Find a short, valid path between given locations.
  6624. * Uses A* algorithm.
  6625. */
  6626. Path *Pathfinder::findClosestHierarchicalPath( Bool isHuman, const LocomotorSet& locomotorSet, const Coord3D *from,
  6627. const Coord3D *to, Bool crusher)
  6628. {
  6629. return internal_findHierarchicalPath(isHuman, locomotorSet.getValidSurfaces(), from, to, crusher, TRUE);
  6630. }
  6631. /**
  6632. * Find a short, valid path between given locations.
  6633. * Uses A* algorithm.
  6634. */
  6635. Path *Pathfinder::internal_findHierarchicalPath( Bool isHuman, const LocomotorSurfaceTypeMask locomotorSurface, const Coord3D *from,
  6636. const Coord3D *rawTo, Bool crusher, Bool closestOK)
  6637. {
  6638. //CRCDEBUG_LOG(("Pathfinder::findGroundPath()\n"));
  6639. #if defined _DEBUG || defined _INTERNAL
  6640. Int startTimeMS = ::GetTickCount();
  6641. #endif
  6642. if (rawTo->x == 0.0f && rawTo->y == 0.0f) {
  6643. DEBUG_LOG(("Attempting pathfind to 0,0, generally a bug.\n"));
  6644. return NULL;
  6645. }
  6646. DEBUG_ASSERTCRASH(m_openList==NULL && m_closedList == NULL, ("Dangling lists."));
  6647. if (m_isMapReady == false) {
  6648. return NULL;
  6649. }
  6650. Coord3D adjustTo = *rawTo;
  6651. Coord3D *to = &adjustTo;
  6652. Coord3D clipFrom = *from;
  6653. clip(&clipFrom, &adjustTo);
  6654. m_isTunneling = false;
  6655. PathfindLayerEnum destinationLayer = TheTerrainLogic->getLayerForDestination(to);
  6656. ICoord2D cell;
  6657. worldToCell( to, &cell );
  6658. // determine goal cell
  6659. PathfindCell *goalCell = getCell( destinationLayer, cell.x, cell.y );
  6660. if (goalCell == NULL) {
  6661. return NULL;
  6662. }
  6663. if (!goalCell->allocateInfo(cell)) {
  6664. return NULL;
  6665. }
  6666. // determine start cell
  6667. ICoord2D startCellNdx;
  6668. PathfindLayerEnum layer = TheTerrainLogic->getLayerForDestination(from);
  6669. PathfindCell *parentCell = getClippedCell( layer,&clipFrom );
  6670. if (parentCell == NULL) {
  6671. return NULL;
  6672. }
  6673. if (parentCell!=goalCell) {
  6674. worldToCell(&clipFrom, &startCellNdx);
  6675. if (!parentCell->allocateInfo(startCellNdx)) {
  6676. goalCell->releaseInfo();
  6677. return NULL;
  6678. }
  6679. }
  6680. Int zone1, zone2;
  6681. // m_isCrusher = false;
  6682. zone1 = m_zoneManager.getEffectiveZone(locomotorSurface, false, parentCell->getZone());
  6683. zone2 = m_zoneManager.getEffectiveZone(locomotorSurface, false, goalCell->getZone());
  6684. if ( zone1 != zone2) {
  6685. goalCell->releaseInfo();
  6686. parentCell->releaseInfo();
  6687. return NULL;
  6688. }
  6689. parentCell->startPathfind(goalCell);
  6690. // "closed" list is initially empty
  6691. m_closedList = NULL;
  6692. Int cellCount = 0;
  6693. zoneStorageType goalBlockZone;
  6694. ICoord2D goalBlockNdx;
  6695. if (goalCell->getLayer()==LAYER_GROUND) {
  6696. goalBlockZone = m_zoneManager.getBlockZone(locomotorSurface,
  6697. crusher, goalCell->getXIndex(), goalCell->getYIndex(), m_map);
  6698. goalBlockNdx.x = goalCell->getXIndex()/PathfindZoneManager::ZONE_BLOCK_SIZE;
  6699. goalBlockNdx.y = goalCell->getYIndex()/PathfindZoneManager::ZONE_BLOCK_SIZE;
  6700. } else {
  6701. goalBlockZone = goalCell->getZone();
  6702. goalBlockNdx.x = -1;
  6703. goalBlockNdx.y = -1;
  6704. }
  6705. if (parentCell->getLayer()==LAYER_GROUND) {
  6706. // initialize "open" list to contain start cell
  6707. m_openList = parentCell;
  6708. } else {
  6709. m_openList = parentCell;
  6710. PathfindLayerEnum layer = parentCell->getLayer();
  6711. // We're starting on a bridge, so link to land at the bridge end points.
  6712. ICoord2D ndx;
  6713. ICoord2D toNdx;
  6714. m_layers[layer].getStartCellIndex(&ndx);
  6715. m_layers[layer].getEndCellIndex(&toNdx);
  6716. PathfindCell *cell = getCell(LAYER_GROUND, toNdx.x, toNdx.y);
  6717. PathfindCell *startCell = getCell(LAYER_GROUND, ndx.x, ndx.y);
  6718. if (cell && startCell) {
  6719. // Close parent cell;
  6720. m_openList = parentCell->removeFromOpenList(m_openList);
  6721. m_closedList = parentCell->putOnClosedList(m_closedList);
  6722. startCell->allocateInfo(ndx);
  6723. startCell->setParentCellHierarchical(parentCell);
  6724. cellCount++;
  6725. Int curCost = startCell->costToHierGoal(parentCell);
  6726. Int remCost = startCell->costToHierGoal(goalCell);
  6727. startCell->setCostSoFar(curCost);
  6728. startCell->setTotalCost(remCost);
  6729. startCell->setParentCellHierarchical(parentCell);
  6730. // insert newCell in open list such that open list is sorted, smallest total path cost first
  6731. m_openList = startCell->putOnSortedOpenList( m_openList );
  6732. cellCount++;
  6733. cell->allocateInfo(toNdx);
  6734. curCost = cell->costToHierGoal(parentCell);
  6735. remCost = cell->costToHierGoal(goalCell);
  6736. cell->setCostSoFar(curCost);
  6737. cell->setTotalCost(remCost);
  6738. cell->setParentCellHierarchical(parentCell);
  6739. // insert newCell in open list such that open list is sorted, smallest total path cost first
  6740. m_openList = cell->putOnSortedOpenList( m_openList );
  6741. }
  6742. }
  6743. PathfindCell *closestCell = NULL;
  6744. Real closestDistSqr = sqr(HUGE_DIST);
  6745. //
  6746. // Continue search until "open" list is empty, or
  6747. // until goal is found.
  6748. //
  6749. while( m_openList != NULL )
  6750. {
  6751. // take head cell off of open list - it has lowest estimated total path cost
  6752. parentCell = m_openList;
  6753. m_openList = parentCell->removeFromOpenList(m_openList);
  6754. zoneStorageType parentZone;
  6755. if (parentCell->getLayer()==LAYER_GROUND) {
  6756. parentZone = m_zoneManager.getBlockZone(locomotorSurface,
  6757. crusher, parentCell->getXIndex(), parentCell->getYIndex(), m_map);
  6758. } else {
  6759. parentZone = parentCell->getZone();
  6760. }
  6761. Bool reachedGoal = false;
  6762. Int blockX = parentCell->getXIndex()/PathfindZoneManager::ZONE_BLOCK_SIZE;
  6763. Int blockY = parentCell->getYIndex()/PathfindZoneManager::ZONE_BLOCK_SIZE;
  6764. if (parentZone == goalBlockZone) {
  6765. if (goalBlockNdx.x == -1 || (blockX==goalBlockNdx.x && blockY == goalBlockNdx.y)) {
  6766. reachedGoal = true;
  6767. } else {
  6768. DEBUG_LOG(("Hmm, got match before correct cell."));
  6769. }
  6770. }
  6771. ICoord2D zoneBlockExtent;
  6772. m_zoneManager.getExtent(zoneBlockExtent);
  6773. if (!reachedGoal && m_zoneManager.interactsWithBridge(parentCell->getXIndex(), parentCell->getYIndex())) {
  6774. Int i;
  6775. for (i=0; i<=LAYER_LAST; i++) {
  6776. if (m_layers[i].isUnused() || m_layers[i].isDestroyed()) {
  6777. continue;
  6778. }
  6779. ICoord2D ndx;
  6780. ICoord2D toNdx;
  6781. m_layers[i].getStartCellIndex(&ndx);
  6782. m_layers[i].getEndCellIndex(&toNdx);
  6783. if (ndx.x/PathfindZoneManager::ZONE_BLOCK_SIZE != blockX ||
  6784. ndx.y/PathfindZoneManager::ZONE_BLOCK_SIZE != blockY) {
  6785. m_layers[i].getStartCellIndex(&toNdx);
  6786. m_layers[i].getEndCellIndex(&ndx);
  6787. }
  6788. if (ndx.x<0 || ndx.y<0) continue;
  6789. if (toNdx.x<0 || toNdx.y<0) continue;
  6790. if (ndx.x/PathfindZoneManager::ZONE_BLOCK_SIZE == blockX &&
  6791. ndx.y/PathfindZoneManager::ZONE_BLOCK_SIZE == blockY) {
  6792. // Bridge connects to this block.
  6793. Int bridgeZone = m_zoneManager.getBlockZone(locomotorSurface, crusher, ndx.x, ndx.y, m_map);
  6794. if (bridgeZone != parentZone) {
  6795. continue;
  6796. }
  6797. // We have a winner.
  6798. if (m_layers[i].getZone() == goalBlockZone) {
  6799. reachedGoal = true;
  6800. break;
  6801. }
  6802. PathfindCell *cell = getCell(LAYER_GROUND, toNdx.x, toNdx.y);
  6803. if (cell==NULL) continue;
  6804. if (cell->hasInfo() && (cell->getClosed() || cell->getOpen())) {
  6805. continue;
  6806. }
  6807. PathfindCell *startCell = getCell(LAYER_GROUND, ndx.x, ndx.y);
  6808. if (startCell==NULL) continue;
  6809. if (startCell != parentCell) {
  6810. startCell->allocateInfo(ndx);
  6811. startCell->setParentCellHierarchical(parentCell);
  6812. if (!startCell->getClosed() && !startCell->getOpen()) {
  6813. m_closedList = startCell->putOnClosedList(m_closedList);
  6814. }
  6815. }
  6816. cell->allocateInfo(toNdx);
  6817. cell->setParentCellHierarchical(startCell);
  6818. cellCount++;
  6819. Int curCost = cell->costToHierGoal(startCell);
  6820. Int remCost = cell->costToHierGoal(goalCell);
  6821. cell->setCostSoFar(startCell->getCostSoFar() + curCost);
  6822. cell->setTotalCost(cell->getCostSoFar()+remCost);
  6823. cell->setParentCellHierarchical(startCell);
  6824. // insert newCell in open list such that open list is sorted, smallest total path cost first
  6825. m_openList = cell->putOnSortedOpenList( m_openList );
  6826. }
  6827. }
  6828. }
  6829. if (reachedGoal)
  6830. {
  6831. if (parentCell != goalCell) {
  6832. goalCell->setParentCellHierarchical(parentCell);
  6833. }
  6834. // success - found a path to the goal
  6835. m_isTunneling = false;
  6836. // construct and return path
  6837. Path *path = buildHierachicalPath( from, goalCell );
  6838. #if defined _DEBUG || defined _INTERNAL
  6839. Bool show = TheGlobalData->m_debugAI==AI_DEBUG_PATHS;
  6840. show |= (TheGlobalData->m_debugAI==AI_DEBUG_GROUND_PATHS);
  6841. if (show) {
  6842. debugShowSearch(true);
  6843. #if 0 // Show hierarchical blocks (big blue ones.)
  6844. Int i, j;
  6845. ICoord2D extent;
  6846. m_zoneManager.getExtent(extent);
  6847. for (i=0; i<extent.x; i++) {
  6848. for (j=0; j<extent.y; j++) {
  6849. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  6850. RGBColor color;
  6851. color.blue = 1;
  6852. color.red = color.green = 0;
  6853. Coord3D pos;
  6854. pos.x = ((i+0.5f)*PathfindZoneManager::ZONE_BLOCK_SIZE)*PATHFIND_CELL_SIZE_F ;
  6855. pos.y = ((j+0.5f)*PathfindZoneManager::ZONE_BLOCK_SIZE)*PATHFIND_CELL_SIZE_F ;
  6856. pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y ) + 0.5f;
  6857. if (m_zoneManager.isPassable(i*PathfindZoneManager::ZONE_BLOCK_SIZE, j*PathfindZoneManager::ZONE_BLOCK_SIZE)) {
  6858. addIcon(&pos, 5*PATHFIND_CELL_SIZE_F, 300, color);
  6859. }
  6860. }
  6861. }
  6862. #endif
  6863. }
  6864. #endif
  6865. if (goalCell->hasInfo() && !goalCell->getClosed() && !goalCell->getOpen()) {
  6866. goalCell->releaseInfo();
  6867. }
  6868. parentCell->releaseInfo();
  6869. cleanOpenAndClosedLists();
  6870. return path;
  6871. }
  6872. #if defined _DEBUG || defined _INTERNAL
  6873. #if 0
  6874. Bool show = TheGlobalData->m_debugAI==AI_DEBUG_PATHS;
  6875. show |= (TheGlobalData->m_debugAI==AI_DEBUG_GROUND_PATHS);
  6876. if (show) {
  6877. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  6878. RGBColor color;
  6879. color.blue = 1;
  6880. color.red = 1;
  6881. color.green = 0;
  6882. Coord3D pos;
  6883. pos.x = ((blockX+0.5f)*PathfindZoneManager::ZONE_BLOCK_SIZE)*PATHFIND_CELL_SIZE_F ;
  6884. pos.y = ((blockY+0.5f)*PathfindZoneManager::ZONE_BLOCK_SIZE)*PATHFIND_CELL_SIZE_F ;
  6885. pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y ) + 0.5f;
  6886. addIcon(&pos, 5*PATHFIND_CELL_SIZE_F, 300, color);
  6887. }
  6888. #endif
  6889. #endif
  6890. Real dx = IABS(goalCell->getXIndex()-parentCell->getXIndex());
  6891. Real dy = IABS(goalCell->getYIndex()-parentCell->getYIndex());
  6892. Real distSqr = dx*dx+dy*dy;
  6893. if (distSqr < closestDistSqr) {
  6894. closestCell = parentCell;
  6895. closestDistSqr = distSqr;
  6896. }
  6897. // put parent cell onto closed list - its evaluation is finished
  6898. m_closedList = parentCell->putOnClosedList( m_closedList );
  6899. Int i;
  6900. zoneStorageType examinedZones[PathfindZoneManager::ZONE_BLOCK_SIZE];
  6901. Int numExZones = 0;
  6902. // Left side.
  6903. if (blockX>0) {
  6904. for (i=1; i<=PathfindZoneManager::ZONE_BLOCK_SIZE; i++) {
  6905. ICoord2D scanCell;
  6906. scanCell.x = blockX*PathfindZoneManager::ZONE_BLOCK_SIZE;
  6907. scanCell.y = (blockY*PathfindZoneManager::ZONE_BLOCK_SIZE);
  6908. scanCell.y += PathfindZoneManager::ZONE_BLOCK_SIZE/2;
  6909. Int offset = i>>1;
  6910. if (i&1) offset = -offset;
  6911. scanCell.y += offset;
  6912. ICoord2D delta;
  6913. delta.x = -1; // left side moves -1.
  6914. delta.y = 0;
  6915. PathfindCell *cell = getCell(LAYER_GROUND, scanCell.x, scanCell.y);
  6916. if (cell==NULL) continue;
  6917. if (cell->hasInfo() && (cell->getClosed() || cell->getOpen())) {
  6918. if (parentZone == m_zoneManager.getBlockZone(locomotorSurface,
  6919. crusher, scanCell.x, scanCell.y, m_map)) {
  6920. break;
  6921. }
  6922. }
  6923. if (isHuman) {
  6924. if (scanCell.x < m_logicalExtent.lo.x || scanCell.x > m_logicalExtent.hi.x ||
  6925. scanCell.y < m_logicalExtent.lo.y || scanCell.y > m_logicalExtent.hi.y) {
  6926. continue;
  6927. }
  6928. }
  6929. processHierarchicalCell(scanCell, delta, parentCell,
  6930. goalCell, parentZone, examinedZones, numExZones, crusher, cellCount);
  6931. }
  6932. }
  6933. // Right side.
  6934. if (blockX<zoneBlockExtent.x-1) {
  6935. numExZones = 0;
  6936. for (i=1; i<=PathfindZoneManager::ZONE_BLOCK_SIZE; i++) {
  6937. ICoord2D scanCell;
  6938. scanCell.x = blockX*PathfindZoneManager::ZONE_BLOCK_SIZE;
  6939. scanCell.x += PathfindZoneManager::ZONE_BLOCK_SIZE-1;
  6940. scanCell.y = (blockY*PathfindZoneManager::ZONE_BLOCK_SIZE);
  6941. scanCell.y += PathfindZoneManager::ZONE_BLOCK_SIZE/2;
  6942. Int offset = i>>1;
  6943. if (i&1) offset = -offset;
  6944. scanCell.y += offset;
  6945. ICoord2D delta;
  6946. delta.x = 1; // right side moves +1.
  6947. delta.y = 0;
  6948. PathfindCell *cell = getCell(LAYER_GROUND, scanCell.x, scanCell.y);
  6949. if (cell==NULL) continue;
  6950. if (cell->hasInfo() && (cell->getClosed() || cell->getOpen())) {
  6951. if (parentZone == m_zoneManager.getBlockZone(locomotorSurface,
  6952. crusher, scanCell.x, scanCell.y, m_map)) {
  6953. break;
  6954. }
  6955. }
  6956. if (isHuman) {
  6957. if (scanCell.x < m_logicalExtent.lo.x || scanCell.x > m_logicalExtent.hi.x ||
  6958. scanCell.y < m_logicalExtent.lo.y || scanCell.y > m_logicalExtent.hi.y) {
  6959. continue;
  6960. }
  6961. }
  6962. processHierarchicalCell(scanCell, delta, parentCell,
  6963. goalCell, parentZone, examinedZones, numExZones, crusher, cellCount);
  6964. }
  6965. }
  6966. // Top side.
  6967. if (blockY>0) {
  6968. numExZones = 0;
  6969. for (i=1; i<=PathfindZoneManager::ZONE_BLOCK_SIZE; i++) {
  6970. ICoord2D scanCell;
  6971. scanCell.y = blockY*PathfindZoneManager::ZONE_BLOCK_SIZE;
  6972. scanCell.x = (blockX*PathfindZoneManager::ZONE_BLOCK_SIZE);
  6973. scanCell.x += PathfindZoneManager::ZONE_BLOCK_SIZE/2;
  6974. Int offset = i>>1;
  6975. if (i&1) offset = -offset;
  6976. scanCell.x += offset;
  6977. ICoord2D delta;
  6978. delta.x = 0;
  6979. delta.y = -1; // Top side moves -1.
  6980. PathfindCell *cell = getCell(LAYER_GROUND, scanCell.x, scanCell.y);
  6981. if (cell==NULL) continue;
  6982. if (cell->hasInfo() && (cell->getClosed() || cell->getOpen())) {
  6983. if (parentZone == m_zoneManager.getBlockZone(locomotorSurface,
  6984. crusher, scanCell.x, scanCell.y, m_map)) {
  6985. break;
  6986. }
  6987. }
  6988. if (isHuman) {
  6989. if (scanCell.x < m_logicalExtent.lo.x || scanCell.x > m_logicalExtent.hi.x ||
  6990. scanCell.y < m_logicalExtent.lo.y || scanCell.y > m_logicalExtent.hi.y) {
  6991. continue;
  6992. }
  6993. }
  6994. processHierarchicalCell(scanCell, delta, parentCell,
  6995. goalCell, parentZone, examinedZones, numExZones, crusher, cellCount);
  6996. }
  6997. }
  6998. // Bottom side.
  6999. if (blockY<zoneBlockExtent.y-1) {
  7000. numExZones = 0;
  7001. for (i=1; i<=PathfindZoneManager::ZONE_BLOCK_SIZE; i++) {
  7002. ICoord2D scanCell;
  7003. scanCell.y = blockY*PathfindZoneManager::ZONE_BLOCK_SIZE;
  7004. scanCell.y += PathfindZoneManager::ZONE_BLOCK_SIZE-1;
  7005. scanCell.x = (blockX*PathfindZoneManager::ZONE_BLOCK_SIZE);
  7006. scanCell.x += PathfindZoneManager::ZONE_BLOCK_SIZE/2;
  7007. Int offset = i>>1;
  7008. if (i&1) offset = -offset;
  7009. scanCell.x += offset;
  7010. ICoord2D delta;
  7011. delta.x = 0;
  7012. delta.y = 1; // Top side moves +1.
  7013. PathfindCell *cell = getCell(LAYER_GROUND, scanCell.x, scanCell.y);
  7014. if (cell==NULL) continue;
  7015. if (cell->hasInfo() && (cell->getClosed() || cell->getOpen())) {
  7016. if (parentZone == m_zoneManager.getBlockZone(locomotorSurface,
  7017. crusher, scanCell.x, scanCell.y, m_map)) {
  7018. break;
  7019. }
  7020. }
  7021. if (isHuman) {
  7022. if (scanCell.x < m_logicalExtent.lo.x || scanCell.x > m_logicalExtent.hi.x ||
  7023. scanCell.y < m_logicalExtent.lo.y || scanCell.y > m_logicalExtent.hi.y) {
  7024. continue;
  7025. }
  7026. }
  7027. processHierarchicalCell(scanCell, delta, parentCell,
  7028. goalCell, parentZone, examinedZones, numExZones, crusher, cellCount);
  7029. }
  7030. }
  7031. }
  7032. if (closestOK && closestCell) {
  7033. m_isTunneling = false;
  7034. // construct and return path
  7035. Path *path = buildHierachicalPath( from, closestCell );
  7036. #if defined _DEBUG || defined _INTERNAL
  7037. #if 0
  7038. if (TheGlobalData->m_debugAI)
  7039. {
  7040. debugShowSearch(true);
  7041. Int i, j;
  7042. ICoord2D extent;
  7043. m_zoneManager.getExtent(extent);
  7044. for (i=0; i<extent.x; i++) {
  7045. for (j=0; j<extent.y; j++) {
  7046. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  7047. RGBColor color;
  7048. color.blue = 1;
  7049. color.red = color.green = 0;
  7050. Coord3D pos;
  7051. pos.x = ((i+0.5f)*PathfindZoneManager::ZONE_BLOCK_SIZE)*PATHFIND_CELL_SIZE_F ;
  7052. pos.y = ((j+0.5f)*PathfindZoneManager::ZONE_BLOCK_SIZE)*PATHFIND_CELL_SIZE_F ;
  7053. pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y ) + 0.5f;
  7054. if (m_zoneManager.isPassable(i*PathfindZoneManager::ZONE_BLOCK_SIZE, j*PathfindZoneManager::ZONE_BLOCK_SIZE)) {
  7055. addIcon(&pos, 5*PATHFIND_CELL_SIZE_F, 300, color);
  7056. }
  7057. }
  7058. }
  7059. }
  7060. #endif
  7061. #endif
  7062. if (goalCell->hasInfo() && !goalCell->getClosed() && !goalCell->getOpen()) {
  7063. goalCell->releaseInfo();
  7064. }
  7065. cleanOpenAndClosedLists();
  7066. return path;
  7067. }
  7068. // failure - goal cannot be reached
  7069. #if defined _DEBUG || defined _INTERNAL
  7070. if (TheGlobalData->m_debugAI)
  7071. {
  7072. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  7073. RGBColor color;
  7074. color.blue = 0;
  7075. color.red = color.green = 1;
  7076. addIcon(NULL, 0, 0, color);
  7077. debugShowSearch(false);
  7078. Coord3D pos;
  7079. pos = *from;
  7080. pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y ) + 0.5f;
  7081. addIcon(&pos, 3*PATHFIND_CELL_SIZE_F, 600, color);
  7082. pos = *to;
  7083. pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y ) + 0.5f;
  7084. addIcon(&pos, 3*PATHFIND_CELL_SIZE_F, 600, color);
  7085. Real dx, dy;
  7086. dx = from->x - to->x;
  7087. dy = from->y - to->y;
  7088. Int count = sqrt(dx*dx+dy*dy)/(PATHFIND_CELL_SIZE_F/2);
  7089. if (count<2) count = 2;
  7090. Int i;
  7091. color.green = 0;
  7092. for (i=1; i<count; i++) {
  7093. pos.x = from->x + (to->x-from->x)*i/count;
  7094. pos.y = from->y + (to->y-from->y)*i/count;
  7095. pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y ) + 0.5f;
  7096. addIcon(&pos, PATHFIND_CELL_SIZE_F/2, 60, color);
  7097. }
  7098. }
  7099. DEBUG_LOG(("%d ", TheGameLogic->getFrame()));
  7100. DEBUG_LOG(("FindHierarchicalPath failed from (%f,%f) to (%f,%f)\n", from->x, from->y, to->x, to->y));
  7101. DEBUG_LOG(("time %f\n", (::GetTickCount()-startTimeMS)/1000.0f));
  7102. #endif
  7103. #ifdef DUMP_PERF_STATS
  7104. TheGameLogic->incrementOverallFailedPathfinds();
  7105. #endif
  7106. m_isTunneling = false;
  7107. goalCell->releaseInfo();
  7108. cleanOpenAndClosedLists();
  7109. return NULL;
  7110. }
  7111. /**
  7112. * Does any broken bridge join from and to?
  7113. * True means that if bridge BridgeID is repaired, there is a land path from to to..
  7114. */
  7115. Bool Pathfinder::findBrokenBridge(const LocomotorSet& locoSet,
  7116. const Coord3D *from, const Coord3D *to, ObjectID *bridgeID)
  7117. {
  7118. // See if terrain or building is blocking the destination.
  7119. PathfindLayerEnum destinationLayer = TheTerrainLogic->getLayerForDestination(to);
  7120. PathfindLayerEnum fromLayer = TheTerrainLogic->getLayerForDestination(from);
  7121. Int zone1, zone2;
  7122. *bridgeID = INVALID_ID;
  7123. PathfindCell *parentCell = getClippedCell(fromLayer, from);
  7124. PathfindCell *goalCell = getClippedCell(destinationLayer, to);
  7125. zone1 = m_zoneManager.getEffectiveZone(locoSet.getValidSurfaces(), false, parentCell->getZone());
  7126. zone2 = m_zoneManager.getEffectiveZone(locoSet.getValidSurfaces(), false, goalCell->getZone());
  7127. zone1 = m_zoneManager.getEffectiveTerrainZone(zone1);
  7128. zone2 = m_zoneManager.getEffectiveTerrainZone(zone2);
  7129. zone1 = m_zoneManager.getEffectiveZone(locoSet.getValidSurfaces(), false, zone1);
  7130. zone2 = m_zoneManager.getEffectiveZone(locoSet.getValidSurfaces(), false, zone2);
  7131. // If the terrain is connected using this locomotor set, we can path somehow.
  7132. if (zone1 == zone2) {
  7133. // There is not terrain blocking the from & to.
  7134. return false;
  7135. }
  7136. // Check broken bridges.
  7137. Int i;
  7138. for (i=0; i<=LAYER_LAST; i++) {
  7139. if (m_layers[i].isDestroyed()) {
  7140. if (m_layers[i].connectsZones(&m_zoneManager, locoSet, zone1, zone2)) {
  7141. *bridgeID = m_layers[i].getBridgeID();
  7142. return true;
  7143. }
  7144. }
  7145. }
  7146. return false;
  7147. }
  7148. /**
  7149. * Does any path exist from 'from' to 'to' given the locomotor set
  7150. * This is the quick check, only looks at whether the terrain is possible or
  7151. * impossible to path over. Doesn't take other units into account.
  7152. * False means it is impossible to path.
  7153. * True means it is possible given the terrain, but there may be units in the way.
  7154. */
  7155. Bool Pathfinder::clientSafeQuickDoesPathExist( const LocomotorSet& locomotorSet,
  7156. const Coord3D *from,
  7157. const Coord3D *to )
  7158. {
  7159. // See if terrain or building is blocking the destination.
  7160. PathfindLayerEnum destinationLayer = TheTerrainLogic->getLayerForDestination(to);
  7161. if (!validMovementPosition(false, destinationLayer, locomotorSet, to)) {
  7162. return false;
  7163. }
  7164. PathfindLayerEnum fromLayer = TheTerrainLogic->getLayerForDestination(from);
  7165. Int zone1, zone2;
  7166. PathfindCell *parentCell = getClippedCell(fromLayer, from);
  7167. PathfindCell *goalCell = getClippedCell(destinationLayer, to);
  7168. if (goalCell->getType()==PathfindCell::CELL_CLIFF) {
  7169. return false; // No goals on cliffs.
  7170. }
  7171. Bool doingTerrainZone = false;
  7172. zone1 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), false, parentCell->getZone());
  7173. if (parentCell->getType() == PathfindCell::CELL_OBSTACLE) {
  7174. doingTerrainZone = true;
  7175. if (zone1 == PathfindZoneManager::UNINITIALIZED_ZONE) {
  7176. // We are in a building that just got placed, and zones haven't been updated yet. [8/8/2003]
  7177. // It is better to return a false positive than a false negative. jba.
  7178. return true;
  7179. }
  7180. }
  7181. zone2 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), false, goalCell->getZone());
  7182. if (goalCell->getType() == PathfindCell::CELL_OBSTACLE) {
  7183. doingTerrainZone = true;
  7184. }
  7185. if (doingTerrainZone) {
  7186. zone1 = parentCell->getZone();
  7187. zone1 = m_zoneManager.getEffectiveTerrainZone(zone1);
  7188. zone1 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), false, zone1);
  7189. zone1 = m_zoneManager.getEffectiveTerrainZone(zone1);
  7190. zone2 = goalCell->getZone();
  7191. zone2 = m_zoneManager.getEffectiveTerrainZone(zone2);
  7192. zone2 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), false, zone2);
  7193. zone2 = m_zoneManager.getEffectiveTerrainZone(zone2);
  7194. }
  7195. // If the terrain is connected using this locomotor set, we can path somehow.
  7196. if (zone1 == zone2) {
  7197. // There is not terrain blocking the from & to.
  7198. return true;
  7199. }
  7200. return FALSE; // no path exists
  7201. }
  7202. /**
  7203. * Does any path exist from 'from' to 'to' given the locomotor set
  7204. * This is the quick check, only looks at whether the terrain is possible or
  7205. * impossible to path over. Doesn't take other units into account.
  7206. * False means it is impossible to path.
  7207. * True means it is possible given the terrain, but there may be units in the way.
  7208. */
  7209. Bool Pathfinder::clientSafeQuickDoesPathExistForUI( const LocomotorSet& locomotorSet,
  7210. const Coord3D *from,
  7211. const Coord3D *to )
  7212. {
  7213. // See if terrain or building is blocking the destination.
  7214. PathfindLayerEnum destinationLayer = TheTerrainLogic->getLayerForDestination(to);
  7215. PathfindLayerEnum fromLayer = TheTerrainLogic->getLayerForDestination(from);
  7216. Int zone1, zone2;
  7217. PathfindCell *parentCell = getClippedCell(fromLayer, from);
  7218. PathfindCell *goalCell = getClippedCell(destinationLayer, to);
  7219. if (goalCell->getType()==PathfindCell::CELL_CLIFF) {
  7220. return false; // No goals on cliffs.
  7221. }
  7222. zone1 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), false, parentCell->getZone());
  7223. zone2 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), false, goalCell->getZone());
  7224. if (zone1 == PathfindZoneManager::UNINITIALIZED_ZONE ||
  7225. zone2 == PathfindZoneManager::UNINITIALIZED_ZONE) {
  7226. // We are in a building that just got placed, and zones haven't been updated yet. [8/8/2003]
  7227. // It is better to return a false positive than a false negative. jba.
  7228. return true;
  7229. }
  7230. /* Do the effective terrain zone. This feedback is for the ui, so we won't take structures into account,
  7231. beacuse if they are visible it will be obvious, and if they are stealthed they should be invisible to the
  7232. pathing as well. jba. */
  7233. zone1 = parentCell->getZone();
  7234. zone1 = m_zoneManager.getEffectiveTerrainZone(zone1);
  7235. zone1 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), false, zone1);
  7236. zone1 = m_zoneManager.getEffectiveTerrainZone(zone1);
  7237. zone2 = goalCell->getZone();
  7238. zone2 = m_zoneManager.getEffectiveTerrainZone(zone2);
  7239. zone2 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), false, zone2);
  7240. zone2 = m_zoneManager.getEffectiveTerrainZone(zone2);
  7241. if (zone1 == PathfindZoneManager::UNINITIALIZED_ZONE) {
  7242. // We are in a building that just got placed, and zones haven't been updated yet. [8/8/2003]
  7243. // It is better to return a false positive than a false negative. jba.
  7244. return true;
  7245. }
  7246. // If the terrain is connected using this locomotor set, we can path somehow.
  7247. if (zone1 == zone2) {
  7248. // There is not terrain blocking the from & to.
  7249. return true;
  7250. }
  7251. return FALSE; // no path exists
  7252. }
  7253. /**
  7254. * Does any path exist from 'from' to 'to' given the locomotor set
  7255. * This is the careful check, looks at whether the terrain, buindings and units are possible or
  7256. * impossible to path over. Takes other units into account.
  7257. * False means it is impossible to path.
  7258. * True means it is possible to path.
  7259. */
  7260. Bool Pathfinder::slowDoesPathExist( Object *obj,
  7261. const Coord3D *from,
  7262. const Coord3D *to,
  7263. ObjectID ignoreObject)
  7264. {
  7265. AIUpdateInterface *ai = obj->getAI();
  7266. if (ai==NULL) {
  7267. return false;
  7268. }
  7269. const LocomotorSet &locoSet = ai->getLocomotorSet();
  7270. m_ignoreObstacleID = ignoreObject;
  7271. Path *path = findPath(obj, locoSet, from, to);
  7272. m_ignoreObstacleID = INVALID_ID;
  7273. Bool found = (path!=NULL);
  7274. if (path) {
  7275. path->deleteInstance();
  7276. path = NULL;
  7277. }
  7278. return found;
  7279. }
  7280. void Pathfinder::clip( Coord3D *from, Coord3D *to )
  7281. {
  7282. ICoord2D fromCell, toCell;
  7283. ICoord2D clipFromCell, clipToCell;
  7284. fromCell.x = REAL_TO_INT_FLOOR(from->x/PATHFIND_CELL_SIZE);
  7285. fromCell.y = REAL_TO_INT_FLOOR(from->y/PATHFIND_CELL_SIZE);
  7286. toCell.x = REAL_TO_INT_FLOOR(to->x/PATHFIND_CELL_SIZE);
  7287. toCell.y = REAL_TO_INT_FLOOR(to->y/PATHFIND_CELL_SIZE);
  7288. if (ClipLine2D(&fromCell, &toCell, &clipFromCell, &clipToCell,&m_extent)) {
  7289. if (fromCell.x!=clipFromCell.x || fromCell.y != clipFromCell.y) {
  7290. from->x = clipFromCell.x*PATHFIND_CELL_SIZE_F + 0.05f;
  7291. from->y = clipFromCell.y*PATHFIND_CELL_SIZE_F + 0.05f;
  7292. }
  7293. if (toCell.x!=clipToCell.x || toCell.y != clipToCell.y) {
  7294. to->x = clipToCell.x*PATHFIND_CELL_SIZE_F + 0.05f;
  7295. to->y = clipToCell.y*PATHFIND_CELL_SIZE_F + 0.05f;
  7296. }
  7297. }
  7298. }
  7299. Bool Pathfinder::pathDestination( Object *obj, const LocomotorSet& locomotorSet, Coord3D *dest,
  7300. PathfindLayerEnum layer, const Coord3D *groupDest)
  7301. {
  7302. //CRCDEBUG_LOG(("Pathfinder::pathDestination()\n"));
  7303. if (m_isMapReady == false) return NULL;
  7304. if (!obj) return false;
  7305. Int cellCount = 0;
  7306. #define MAX_CELL_COUNT 500
  7307. Coord3D adjustTo = *groupDest;
  7308. Coord3D *to = &adjustTo;
  7309. DEBUG_ASSERTCRASH(m_openList==NULL && m_closedList == NULL, ("Dangling lists."));
  7310. // create unique "mark" values for open and closed cells for this pathfind invocation
  7311. Bool isCrusher = obj ? obj->getCrusherLevel() > 0 : false;
  7312. PathfindLayerEnum desiredLayer = TheTerrainLogic->getLayerForDestination(dest);
  7313. // determine desired
  7314. PathfindCell *desiredCell = getClippedCell( desiredLayer, dest );
  7315. if (desiredCell == NULL)
  7316. return FALSE;
  7317. PathfindLayerEnum goalLayer = TheTerrainLogic->getLayerForDestination(to);
  7318. // determine goal cell
  7319. PathfindCell *goalCell = getClippedCell( goalLayer, to );
  7320. if (goalCell == NULL)
  7321. return FALSE;
  7322. Bool isHuman = true;
  7323. if (obj && obj->getControllingPlayer() && (obj->getControllingPlayer()->getPlayerType()==PLAYER_COMPUTER)) {
  7324. isHuman = false; // computer gets to cheat.
  7325. }
  7326. Bool center;
  7327. Int radius;
  7328. getRadiusAndCenter(obj, radius, center);
  7329. // determine start cell
  7330. ICoord2D startCellNdx;
  7331. worldToCell(dest, &startCellNdx);
  7332. PathfindCell *parentCell = getCell( layer, startCellNdx.x, startCellNdx.y );
  7333. if (parentCell == NULL)
  7334. return FALSE;
  7335. ICoord2D pos2d;
  7336. worldToCell(to, &pos2d);
  7337. if (!goalCell->allocateInfo(pos2d)) {
  7338. return FALSE;
  7339. }
  7340. if (parentCell!=goalCell) {
  7341. if (!parentCell->allocateInfo(startCellNdx)) {
  7342. desiredCell->releaseInfo();
  7343. goalCell->releaseInfo();
  7344. return FALSE;
  7345. }
  7346. }
  7347. PathfindCell *closestCell = NULL;
  7348. Real closestDistanceSqr = FLT_MAX;
  7349. Coord3D closestPos;
  7350. if (validMovementPosition( isCrusher, locomotorSet.getValidSurfaces(), parentCell ) == false) {
  7351. parentCell->releaseInfo();
  7352. goalCell->releaseInfo();
  7353. return FALSE;
  7354. }
  7355. parentCell->startPathfind(goalCell);
  7356. // initialize "open" list to contain start cell
  7357. m_openList = parentCell;
  7358. // "closed" list is initially empty
  7359. m_closedList = NULL;
  7360. //
  7361. // Continue search until "open" list is empty, or
  7362. // until goal is found.
  7363. //
  7364. while( m_openList != NULL )
  7365. {
  7366. // take head cell off of open list - it has lowest estimated total path cost
  7367. parentCell = m_openList;
  7368. m_openList = parentCell->removeFromOpenList(m_openList);
  7369. Coord3D pos;
  7370. // put parent cell onto closed list - its evaluation is finished
  7371. m_closedList = parentCell->putOnClosedList( m_closedList );
  7372. if (checkForAdjust(obj, locomotorSet, isHuman, parentCell->getXIndex(), parentCell->getYIndex(), parentCell->getLayer(),
  7373. radius, center, &pos, groupDest)) {
  7374. Int dx = IABS(goalCell->getXIndex()-parentCell->getXIndex());
  7375. Int dy = IABS(goalCell->getYIndex()-parentCell->getYIndex());
  7376. Real distSqr = dx*dx+dy*dy;
  7377. //Real cost = (parentCell->getCostSoFar()*(parentCell->getCostSoFar()*COST_TO_DISTANCE_FACTOR))*0.5f;
  7378. //distSqr += sqr(cost);
  7379. if (distSqr < closestDistanceSqr) {
  7380. closestCell = parentCell;
  7381. closestDistanceSqr = distSqr;
  7382. closestPos = pos;
  7383. } else {
  7384. continue;
  7385. }
  7386. } else {
  7387. continue;
  7388. }
  7389. if (cellCount > MAX_CELL_COUNT) {
  7390. continue;
  7391. }
  7392. // Check to see if we can change layers in this cell.
  7393. checkChangeLayers(parentCell);
  7394. // expand search to neighboring orthogonal cells
  7395. static ICoord2D delta[] =
  7396. {
  7397. { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 },
  7398. { 1, 1 }, { -1, 1 }, { -1, -1 }, { 1, -1 }
  7399. };
  7400. const Int numNeighbors = 8;
  7401. const Int firstDiagonal = 4;
  7402. ICoord2D newCellCoord;
  7403. PathfindCell *newCell;
  7404. const Int adjacent[5] = {0, 1, 2, 3, 0};
  7405. Bool neighborFlags[8] = {false, false, false, false, false, false, false};
  7406. UnsignedInt newCostSoFar;
  7407. for( int i=0; i<numNeighbors; i++ )
  7408. {
  7409. neighborFlags[i] = false;
  7410. // determine neighbor cell to try
  7411. newCellCoord.x = parentCell->getXIndex() + delta[i].x;
  7412. newCellCoord.y = parentCell->getYIndex() + delta[i].y;
  7413. // get the neighboring cell
  7414. newCell = getCell(parentCell->getLayer(), newCellCoord.x, newCellCoord.y );
  7415. // check if cell is on the map
  7416. if (newCell == NULL)
  7417. continue;
  7418. // check if this neighbor cell is already on the open (waiting to be tried)
  7419. // or closed (already tried) lists
  7420. Bool onList = false;
  7421. if (newCell->hasInfo()) {
  7422. if (newCell->getOpen() || newCell->getClosed())
  7423. {
  7424. // already on one of the lists
  7425. onList = true;
  7426. }
  7427. }
  7428. if (i>=firstDiagonal) {
  7429. // make sure one of the adjacent sides is open.
  7430. if (!neighborFlags[adjacent[i-4]] && !neighborFlags[adjacent[i-3]]) {
  7431. continue;
  7432. }
  7433. }
  7434. if (!validMovementPosition( isCrusher, locomotorSet.getValidSurfaces(), newCell, parentCell )) {
  7435. continue;
  7436. }
  7437. neighborFlags[i] = true;
  7438. if (!newCell->allocateInfo(newCellCoord)) {
  7439. // Out of cells for pathing...
  7440. return cellCount;
  7441. }
  7442. cellCount++;
  7443. newCostSoFar = newCell->costSoFar( parentCell );
  7444. newCell->setBlockedByAlly(false);
  7445. // check if this neighbor cell is already on the open (waiting to be tried)
  7446. // or closed (already tried) lists
  7447. if (onList)
  7448. {
  7449. // already on one of the lists - if existing costSoFar is less,
  7450. // the new cell is on a longer path, so skip it
  7451. if (newCell->getCostSoFar() <= newCostSoFar)
  7452. continue;
  7453. }
  7454. // keep track of path we're building - point back to cell we moved here from
  7455. newCell->setParentCell(parentCell) ;
  7456. // store cost of this path
  7457. newCell->setCostSoFar(newCostSoFar);
  7458. Int costRemaining = 0;
  7459. if (goalCell) {
  7460. costRemaining = newCell->costToGoal( goalCell );
  7461. }
  7462. newCell->setTotalCost(newCell->getCostSoFar() + costRemaining) ;
  7463. //DEBUG_LOG(("Cell (%d,%d), Parent cost %d, newCostSoFar %d, cost rem %d, tot %d\n",
  7464. // newCell->getXIndex(), newCell->getYIndex(),
  7465. // newCell->costSoFar(parentCell), newCostSoFar, costRemaining, newCell->getCostSoFar() + costRemaining));
  7466. // if newCell was on closed list, remove it from the list
  7467. if (newCell->getClosed())
  7468. m_closedList = newCell->removeFromClosedList( m_closedList );
  7469. // if the newCell was already on the open list, remove it so it can be re-inserted in order
  7470. if (newCell->getOpen())
  7471. m_openList = newCell->removeFromOpenList( m_openList );
  7472. // insert newCell in open list such that open list is sorted, smallest total path cost first
  7473. m_openList = newCell->putOnSortedOpenList( m_openList );
  7474. }
  7475. }
  7476. #if defined _DEBUG || defined _INTERNAL
  7477. if (closestCell) {
  7478. debugShowSearch(true);
  7479. *dest = closestPos;
  7480. } else {
  7481. debugShowSearch(true);
  7482. }
  7483. #endif
  7484. m_isTunneling = false;
  7485. cleanOpenAndClosedLists();
  7486. goalCell->releaseInfo();
  7487. return false;
  7488. }
  7489. struct TightenPathStruct
  7490. {
  7491. Object *obj;
  7492. const LocomotorSet *locomotorSet;
  7493. PathfindLayerEnum layer;
  7494. Int radius;
  7495. Bool center;
  7496. Bool foundDest;
  7497. Coord3D destPos;
  7498. };
  7499. /*static*/ Int Pathfinder::tightenPathCallback(Pathfinder* pathfinder, PathfindCell* from, PathfindCell* to, Int to_x, Int to_y, void* userData)
  7500. {
  7501. TightenPathStruct* d = (TightenPathStruct*)userData;
  7502. if (from == NULL || to==NULL) return 0;
  7503. if (d->layer != to->getLayer()) {
  7504. return 0; // abort.
  7505. }
  7506. Coord3D pos;
  7507. if (!TheAI->pathfinder()->checkForAdjust(d->obj, *d->locomotorSet, true, to_x, to_y, to->getLayer(), d->radius, d->center, &pos, NULL))
  7508. {
  7509. return 0; // bail early
  7510. }
  7511. d->foundDest = true;
  7512. d->destPos = pos;
  7513. return 0; // keep going
  7514. }
  7515. /* Returns the cost, which is in the same units as coord3d distance. */
  7516. void Pathfinder::tightenPath(Object *obj, const LocomotorSet& locomotorSet, Coord3D *from,
  7517. const Coord3D *to)
  7518. {
  7519. TightenPathStruct info;
  7520. getRadiusAndCenter(obj, info.radius, info.center);
  7521. info.layer = TheTerrainLogic->getLayerForDestination(from);
  7522. info.obj = obj;
  7523. info.locomotorSet = &locomotorSet;
  7524. info.foundDest = false;
  7525. iterateCellsAlongLine(*from, *to, info.layer, tightenPathCallback, &info);
  7526. if (info.foundDest) {
  7527. *from = info.destPos;
  7528. }
  7529. }
  7530. /* Returns the cost, which is in the same units as coord3d distance. */
  7531. Int Pathfinder::checkPathCost(Object *obj, const LocomotorSet& locomotorSet, const Coord3D *from,
  7532. const Coord3D *rawTo)
  7533. {
  7534. //CRCDEBUG_LOG(("Pathfinder::checkPathCost()\n"));
  7535. if (m_isMapReady == false) return NULL;
  7536. enum {MAX_COST = 0x7fff0000};
  7537. if (!obj) return MAX_COST;
  7538. Int cellCount = 0;
  7539. #define MAX_CELL_COUNT 500
  7540. Coord3D adjustTo = *rawTo;
  7541. Coord3D *to = &adjustTo;
  7542. DEBUG_ASSERTCRASH(m_openList==NULL && m_closedList == NULL, ("Dangling lists."));
  7543. // create unique "mark" values for open and closed cells for this pathfind invocation
  7544. Bool isCrusher = obj ? obj->getCrusherLevel() > 0 : false;
  7545. PathfindLayerEnum goalLayer = TheTerrainLogic->getLayerForDestination(to);
  7546. // determine goal cell
  7547. PathfindCell *goalCell = getClippedCell( goalLayer, to );
  7548. if (goalCell == NULL)
  7549. return MAX_COST;
  7550. Bool center;
  7551. Int radius;
  7552. getRadiusAndCenter(obj, radius, center);
  7553. // determine start cell
  7554. ICoord2D startCellNdx;
  7555. worldToCell(from, &startCellNdx);
  7556. PathfindLayerEnum fromLayer = TheTerrainLogic->getLayerForDestination(from);
  7557. PathfindCell *parentCell = getCell( fromLayer, from );
  7558. if (parentCell == NULL)
  7559. return MAX_COST;
  7560. ICoord2D pos2d;
  7561. worldToCell(to, &pos2d);
  7562. if (!goalCell->allocateInfo(pos2d)) {
  7563. return MAX_COST;
  7564. }
  7565. if (parentCell!=goalCell) {
  7566. if (!parentCell->allocateInfo(startCellNdx)) {
  7567. goalCell->releaseInfo();
  7568. return MAX_COST;
  7569. }
  7570. }
  7571. if (validMovementPosition( isCrusher, locomotorSet.getValidSurfaces(), parentCell ) == false) {
  7572. parentCell->releaseInfo();
  7573. goalCell->releaseInfo();
  7574. return MAX_COST;
  7575. }
  7576. parentCell->startPathfind(goalCell);
  7577. // initialize "open" list to contain start cell
  7578. m_openList = parentCell;
  7579. // "closed" list is initially empty
  7580. m_closedList = NULL;
  7581. //
  7582. // Continue search until "open" list is empty, or
  7583. // until goal is found.
  7584. //
  7585. while( m_openList != NULL )
  7586. {
  7587. // take head cell off of open list - it has lowest estimated total path cost
  7588. parentCell = m_openList;
  7589. m_openList = parentCell->removeFromOpenList(m_openList);
  7590. // put parent cell onto closed list - its evaluation is finished
  7591. m_closedList = parentCell->putOnClosedList( m_closedList );
  7592. if (parentCell==goalCell) {
  7593. Int cost = parentCell->getTotalCost();
  7594. m_isTunneling = false;
  7595. cleanOpenAndClosedLists();
  7596. return cost;
  7597. }
  7598. if (cellCount > MAX_CELL_COUNT) {
  7599. continue;
  7600. }
  7601. // Check to see if we can change layers in this cell.
  7602. checkChangeLayers(parentCell);
  7603. // expand search to neighboring orthogonal cells
  7604. static ICoord2D delta[] =
  7605. {
  7606. { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 },
  7607. { 1, 1 }, { -1, 1 }, { -1, -1 }, { 1, -1 }
  7608. };
  7609. const Int numNeighbors = 8;
  7610. const Int firstDiagonal = 4;
  7611. ICoord2D newCellCoord;
  7612. PathfindCell *newCell;
  7613. const Int adjacent[5] = {0, 1, 2, 3, 0};
  7614. Bool neighborFlags[8] = {false, false, false, false, false, false, false};
  7615. UnsignedInt newCostSoFar;
  7616. for( int i=0; i<numNeighbors; i++ )
  7617. {
  7618. neighborFlags[i] = false;
  7619. // determine neighbor cell to try
  7620. newCellCoord.x = parentCell->getXIndex() + delta[i].x;
  7621. newCellCoord.y = parentCell->getYIndex() + delta[i].y;
  7622. // get the neighboring cell
  7623. newCell = getCell(parentCell->getLayer(), newCellCoord.x, newCellCoord.y );
  7624. // check if cell is on the map
  7625. if (newCell == NULL)
  7626. continue;
  7627. // check if this neighbor cell is already on the open (waiting to be tried)
  7628. // or closed (already tried) lists
  7629. Bool onList = false;
  7630. if (newCell->hasInfo()) {
  7631. if (newCell->getOpen() || newCell->getClosed())
  7632. {
  7633. // already on one of the lists
  7634. onList = true;
  7635. }
  7636. }
  7637. if (i>=firstDiagonal) {
  7638. // make sure one of the adjacent sides is open.
  7639. if (!neighborFlags[adjacent[i-4]] && !neighborFlags[adjacent[i-3]]) {
  7640. continue;
  7641. }
  7642. }
  7643. if (!validMovementPosition( isCrusher, locomotorSet.getValidSurfaces(), newCell, parentCell )) {
  7644. continue;
  7645. }
  7646. neighborFlags[i] = true;
  7647. if (!newCell->allocateInfo(newCellCoord)) {
  7648. // Out of cells for pathing...
  7649. return cellCount;
  7650. }
  7651. cellCount++;
  7652. newCostSoFar = newCell->costSoFar( parentCell );
  7653. newCell->setBlockedByAlly(false);
  7654. // check if this neighbor cell is already on the open (waiting to be tried)
  7655. // or closed (already tried) lists
  7656. if (onList)
  7657. {
  7658. // already on one of the lists - if existing costSoFar is less,
  7659. // the new cell is on a longer path, so skip it
  7660. if (newCell->getCostSoFar() <= newCostSoFar)
  7661. continue;
  7662. }
  7663. // keep track of path we're building - point back to cell we moved here from
  7664. newCell->setParentCell(parentCell) ;
  7665. // store cost of this path
  7666. newCell->setCostSoFar(newCostSoFar);
  7667. Int costRemaining = 0;
  7668. if (goalCell) {
  7669. costRemaining = newCell->costToGoal( goalCell );
  7670. }
  7671. newCell->setTotalCost(newCell->getCostSoFar() + costRemaining) ;
  7672. //DEBUG_LOG(("Cell (%d,%d), Parent cost %d, newCostSoFar %d, cost rem %d, tot %d\n",
  7673. // newCell->getXIndex(), newCell->getYIndex(),
  7674. // newCell->costSoFar(parentCell), newCostSoFar, costRemaining, newCell->getCostSoFar() + costRemaining));
  7675. // if newCell was on closed list, remove it from the list
  7676. if (newCell->getClosed())
  7677. m_closedList = newCell->removeFromClosedList( m_closedList );
  7678. // if the newCell was already on the open list, remove it so it can be re-inserted in order
  7679. if (newCell->getOpen())
  7680. m_openList = newCell->removeFromOpenList( m_openList );
  7681. // insert newCell in open list such that open list is sorted, smallest total path cost first
  7682. m_openList = newCell->putOnSortedOpenList( m_openList );
  7683. }
  7684. }
  7685. m_isTunneling = false;
  7686. if (goalCell->hasInfo() && !goalCell->getClosed() && !goalCell->getOpen()) {
  7687. goalCell->releaseInfo();
  7688. }
  7689. cleanOpenAndClosedLists();
  7690. return MAX_COST;
  7691. }
  7692. /**
  7693. * Find a short, valid path between the FROM location and a location NEAR the to location.
  7694. * Uses A* algorithm.
  7695. */
  7696. Path *Pathfinder::findClosestPath( Object *obj, const LocomotorSet& locomotorSet, const Coord3D *from,
  7697. Coord3D *rawTo, Bool blocked, Real pathCostMultiplier, Bool moveAllies)
  7698. {
  7699. //CRCDEBUG_LOG(("Pathfinder::findClosestPath()\n"));
  7700. #if defined _DEBUG || defined _INTERNAL
  7701. Int startTimeMS = ::GetTickCount();
  7702. #endif
  7703. Bool isHuman = true;
  7704. if (obj && obj->getControllingPlayer() && (obj->getControllingPlayer()->getPlayerType()==PLAYER_COMPUTER)) {
  7705. isHuman = false; // computer gets to cheat.
  7706. }
  7707. if (locomotorSet.getValidSurfaces() == 0) {
  7708. DEBUG_CRASH(("Attempting to path immobile unit."));
  7709. return NULL;
  7710. }
  7711. if (m_isMapReady == false) return NULL;
  7712. m_isTunneling = false;
  7713. if (!obj) return NULL;
  7714. Bool canPathThroughUnits = false;
  7715. if (obj && obj->getAIUpdateInterface()) {
  7716. canPathThroughUnits = obj->getAIUpdateInterface()->canPathThroughUnits();
  7717. }
  7718. Bool centerInCell;
  7719. Int radius;
  7720. getRadiusAndCenter(obj, radius, centerInCell);
  7721. Coord3D adjustTo = *rawTo;
  7722. Coord3D *to = &adjustTo;
  7723. if (!centerInCell) {
  7724. adjustTo.x += PATHFIND_CELL_SIZE_F/2;
  7725. adjustTo.y += PATHFIND_CELL_SIZE_F/2;
  7726. }
  7727. DEBUG_ASSERTCRASH(m_openList==NULL && m_closedList == NULL, ("Dangling lists."));
  7728. // create unique "mark" values for open and closed cells for this pathfind invocation
  7729. Bool isCrusher = obj ? obj->getCrusherLevel() > 0 : false;
  7730. Coord3D clipFrom = *from;
  7731. clip(&clipFrom, &adjustTo);
  7732. PathfindLayerEnum destinationLayer = TheTerrainLogic->getLayerForDestination(to);
  7733. // determine goal cell
  7734. PathfindCell *goalCell = getClippedCell( destinationLayer, to );
  7735. if (goalCell == NULL)
  7736. return NULL;
  7737. if (goalCell->getZone()==0 && destinationLayer==LAYER_WALL) {
  7738. return NULL;
  7739. }
  7740. Bool goalOnObstacle = false;
  7741. if (m_ignoreObstacleID != INVALID_ID) {
  7742. // Check for object on structure.
  7743. // srj sez: check for obstacle on AIRFIELD... only want to do this for things
  7744. // that are "parked" on the airfield, but not for things hovering over an obstacle
  7745. // (eg, a chinook over a supply dock).
  7746. Object *goalObj = TheGameLogic->findObjectByID(m_ignoreObstacleID);
  7747. if (goalObj) {
  7748. PathfindCell *ignoreCell = getClippedCell(goalObj->getLayer(), goalObj->getPosition());
  7749. if ( (goalCell->getObstacleID()==ignoreCell->getObstacleID()) && (goalCell->getObstacleID() != INVALID_ID) ) {
  7750. Object* newObstacle = TheGameLogic->findObjectByID(goalCell->getObstacleID());
  7751. if (newObstacle != NULL && newObstacle->isKindOf(KINDOF_FS_AIRFIELD))
  7752. {
  7753. m_ignoreObstacleID = goalCell->getObstacleID();
  7754. goalOnObstacle = true;
  7755. }
  7756. else
  7757. {
  7758. if (m_ignoreObstacleID == goalCell->getObstacleID()) {
  7759. goalOnObstacle = true;
  7760. }
  7761. }
  7762. }
  7763. }
  7764. }
  7765. // determine start cell
  7766. ICoord2D startCellNdx;
  7767. worldToCell(from, &startCellNdx);
  7768. PathfindCell *parentCell = getClippedCell( obj->getLayer(), &clipFrom );
  7769. if (parentCell == NULL)
  7770. return NULL;
  7771. if (validMovementPosition( isCrusher, locomotorSet.getValidSurfaces(), parentCell ) == false) {
  7772. m_isTunneling = true; // We can't move from our current location. So relax the constraints.
  7773. }
  7774. TCheckMovementInfo info;
  7775. info.cell = startCellNdx;
  7776. info.layer = obj->getLayer();
  7777. info.centerInCell = centerInCell;
  7778. info.radius = radius;
  7779. info.considerTransient = blocked;
  7780. info.acceptableSurfaces = locomotorSet.getValidSurfaces();
  7781. if (!checkForMovement(obj, info) || info.enemyFixed) {
  7782. m_isTunneling = true; // We can't move from our current location. So relax the constraints.
  7783. }
  7784. Bool gotHierarchicalPath = false;
  7785. if (m_isTunneling) {
  7786. m_zoneManager.setAllPassable(); // can't optimize.
  7787. } else {
  7788. m_zoneManager.clearPassableFlags();
  7789. Path *hPat = findClosestHierarchicalPath(isHuman, locomotorSet, from, rawTo, false);
  7790. if (hPat) {
  7791. hPat->deleteInstance();
  7792. gotHierarchicalPath = true;
  7793. } else {
  7794. m_zoneManager.setAllPassable();
  7795. }
  7796. }
  7797. const Bool startedStuck = m_isTunneling;
  7798. ICoord2D pos2d;
  7799. worldToCell(to, &pos2d);
  7800. if (!goalCell->allocateInfo(pos2d)) {
  7801. return NULL;
  7802. }
  7803. if (parentCell!=goalCell) {
  7804. worldToCell(&clipFrom, &pos2d);
  7805. if (!parentCell->allocateInfo(pos2d)) {
  7806. return NULL;
  7807. }
  7808. }
  7809. parentCell->startPathfind(goalCell);
  7810. PathfindCell *closesetCell = NULL;
  7811. Real closestDistanceSqr = FLT_MAX;
  7812. Real closestDistScreenSqr = FLT_MAX;
  7813. // initialize "open" list to contain start cell
  7814. m_openList = parentCell;
  7815. // "closed" list is initially empty
  7816. m_closedList = NULL;
  7817. Int count = 0;
  7818. //
  7819. // Continue search until "open" list is empty, or
  7820. // until goal is found.
  7821. //
  7822. Bool foundGoal = false;
  7823. while( m_openList != NULL )
  7824. {
  7825. Real dx;
  7826. Real dy;
  7827. Real distSqr;
  7828. // take head cell off of open list - it has lowest estimated total path cost
  7829. parentCell = m_openList;
  7830. m_openList = parentCell->removeFromOpenList(m_openList);
  7831. if (parentCell == goalCell)
  7832. {
  7833. // success - found a path to the goal
  7834. if (!goalOnObstacle) {
  7835. // See if the goal is a valid destination. If not, accept closest cell.
  7836. if (closesetCell!=NULL && !canPathThroughUnits && !checkDestination(obj, parentCell->getXIndex(), parentCell->getYIndex(), parentCell->getLayer(), radius, centerInCell)) {
  7837. foundGoal = true;
  7838. // Continue processing the open list to find a possibly closer cell. jba. [8/25/2003]
  7839. continue;
  7840. }
  7841. }
  7842. Bool show = TheGlobalData->m_debugAI;
  7843. #ifdef INTENSE_DEBUG
  7844. Int count = 0;
  7845. PathfindCell *cur;
  7846. for (cur = m_closedList; cur; cur=cur->getNextOpen()) {
  7847. count++;
  7848. }
  7849. if (count>1000) {
  7850. show = true;
  7851. DEBUG_LOG(("FCP - cells %d obj %s %x\n", count, obj->getTemplate()->getName().str(), obj));
  7852. #ifdef STATE_MACHINE_DEBUG
  7853. if( obj->getAIUpdateInterface() )
  7854. {
  7855. DEBUG_LOG(("State %s\n", obj->getAIUpdateInterface()->getCurrentStateName().str()));
  7856. }
  7857. #endif
  7858. TheScriptEngine->AppendDebugMessage("Big path FCP", false);
  7859. }
  7860. #endif
  7861. if (show)
  7862. debugShowSearch(true);
  7863. m_isTunneling = false;
  7864. // construct and return path
  7865. Path *path = buildActualPath( obj, locomotorSet.getValidSurfaces(), from, goalCell, centerInCell, blocked);
  7866. parentCell->releaseInfo();
  7867. goalCell->releaseInfo();
  7868. cleanOpenAndClosedLists();
  7869. return path;
  7870. }
  7871. // put parent cell onto closed list - its evaluation is finished
  7872. m_closedList = parentCell->putOnClosedList( m_closedList );
  7873. if (!m_isTunneling && checkDestination(obj, parentCell->getXIndex(), parentCell->getYIndex(), parentCell->getLayer(), radius, centerInCell)) {
  7874. if (!startedStuck || validMovementPosition( isCrusher, locomotorSet.getValidSurfaces(), parentCell )) {
  7875. dx = IABS(goalCell->getXIndex()-parentCell->getXIndex());
  7876. dy = IABS(goalCell->getYIndex()-parentCell->getYIndex());
  7877. distSqr = dx*dx+dy*dy;
  7878. if (distSqr<closestDistScreenSqr) {
  7879. closestDistScreenSqr = distSqr;
  7880. }
  7881. distSqr += (parentCell->getCostSoFar()*(parentCell->getCostSoFar()*COST_TO_DISTANCE_FACTOR_SQR))*pathCostMultiplier;
  7882. if (distSqr < closestDistanceSqr) {
  7883. closesetCell = parentCell;
  7884. closestDistanceSqr = distSqr;
  7885. }
  7886. }
  7887. }
  7888. dx = IABS(goalCell->getXIndex()-parentCell->getXIndex());
  7889. dy = IABS(goalCell->getYIndex()-parentCell->getYIndex());
  7890. distSqr = dx*dx+dy*dy;
  7891. // If we are 2x farther than the closest location already found, don't continue.
  7892. if (distSqr > closestDistScreenSqr*4) {
  7893. Bool skip = false;
  7894. if (!gotHierarchicalPath) {
  7895. skip = true;
  7896. }
  7897. if (count>2000) {
  7898. skip = true;
  7899. }
  7900. if (closestDistScreenSqr < 10*10*PATHFIND_CELL_SIZE_F) {
  7901. skip = true;
  7902. }
  7903. if (skip) {
  7904. continue;
  7905. }
  7906. }
  7907. // If we haven't already found the goal cell, continue examining. [8/25/2003]
  7908. if (!foundGoal) {
  7909. // Check to see if we can change layers in this cell.
  7910. checkChangeLayers(parentCell);
  7911. count += examineNeighboringCells(parentCell, goalCell, locomotorSet, isHuman, centerInCell, radius, startCellNdx, obj, NO_ATTACK);
  7912. }
  7913. }
  7914. if (closesetCell) {
  7915. // success - found a path to near the goal
  7916. Bool show = TheGlobalData->m_debugAI;
  7917. #ifdef INTENSE_DEBUG
  7918. if (count>5000) {
  7919. show = true;
  7920. DEBUG_LOG(("FCP CC cells %d obj %s %x\n", count, obj->getTemplate()->getName().str(), obj));
  7921. #ifdef STATE_MACHINE_DEBUG
  7922. if( obj->getAIUpdateInterface() )
  7923. {
  7924. DEBUG_LOG(("State %s\n", obj->getAIUpdateInterface()->getCurrentStateName().str()));
  7925. }
  7926. #endif
  7927. DEBUG_LOG(("%d ", TheGameLogic->getFrame()));
  7928. DEBUG_LOG(("Pathfind(findClosestPath) chugged from (%f,%f) to (%f,%f), --", from->x, from->y, to->x, to->y));
  7929. DEBUG_LOG(("Unit '%s', time %f\n", obj->getTemplate()->getName().str(), (::GetTickCount()-startTimeMS)/1000.0f));
  7930. #ifdef INTENSE_DEBUG
  7931. TheScriptEngine->AppendDebugMessage("Big path FCP CC", false);
  7932. #endif
  7933. }
  7934. #endif
  7935. if (show)
  7936. debugShowSearch(true);
  7937. m_isTunneling = false;
  7938. rawTo->x = closesetCell->getXIndex()*PATHFIND_CELL_SIZE_F + PATHFIND_CELL_SIZE_F/2.0f;
  7939. rawTo->y = closesetCell->getYIndex()*PATHFIND_CELL_SIZE_F + PATHFIND_CELL_SIZE_F/2.0f;
  7940. // construct and return path
  7941. Path *path = buildActualPath( obj, locomotorSet.getValidSurfaces(), from, closesetCell, centerInCell, blocked );
  7942. goalCell->releaseInfo();
  7943. cleanOpenAndClosedLists();
  7944. return path;
  7945. }
  7946. // failure - goal cannot be reached
  7947. #if defined _DEBUG || defined _INTERNAL
  7948. Bool valid;
  7949. valid = validMovementPosition( isCrusher, obj->getLayer(), locomotorSet, to ) ;
  7950. DEBUG_LOG(("%d ", TheGameLogic->getFrame()));
  7951. DEBUG_LOG(("Pathfind(findClosestPath) failed from (%f,%f) to (%f,%f), original valid %d --", from->x, from->y, to->x, to->y, valid));
  7952. DEBUG_LOG(("Unit '%s', time %f\n", obj->getTemplate()->getName().str(), (::GetTickCount()-startTimeMS)/1000.0f));
  7953. if (TheGlobalData->m_debugAI)
  7954. debugShowSearch(false);
  7955. #endif
  7956. #ifdef DUMP_PERF_STATS
  7957. TheGameLogic->incrementOverallFailedPathfinds();
  7958. #endif
  7959. m_isTunneling = false;
  7960. goalCell->releaseInfo();
  7961. cleanOpenAndClosedLists();
  7962. return NULL;
  7963. }
  7964. void Pathfinder::adjustCoordToCell(Int cellX, Int cellY, Bool centerInCell, Coord3D &pos, PathfindLayerEnum layer)
  7965. {
  7966. if (centerInCell) {
  7967. pos.x = ((Real)cellX + 0.5f) * PATHFIND_CELL_SIZE_F;
  7968. pos.y = ((Real)cellY + 0.5f) * PATHFIND_CELL_SIZE_F;
  7969. } else {
  7970. pos.x = ((Real)cellX+0.05) * PATHFIND_CELL_SIZE_F;
  7971. pos.y = ((Real)cellY+0.05) * PATHFIND_CELL_SIZE_F;
  7972. }
  7973. pos.z = TheTerrainLogic->getLayerHeight( pos.x, pos.y, layer );
  7974. }
  7975. /**
  7976. * Work backwards from goal cell to construct final path.
  7977. */
  7978. Path *Pathfinder::buildActualPath( const Object *obj, LocomotorSurfaceTypeMask acceptableSurfaces, const Coord3D *fromPos,
  7979. PathfindCell *goalCell, Bool center, Bool blocked )
  7980. {
  7981. DEBUG_ASSERTCRASH( goalCell, ("Pathfinder::buildActualPath: goalCell == NULL") );
  7982. Path *path = newInstance(Path);
  7983. if (goalCell->getPinched() && goalCell->getParentCell() && !goalCell->getParentCell()->getPinched()) {
  7984. goalCell = goalCell->getParentCell();
  7985. }
  7986. prependCells(path, fromPos, goalCell, center);
  7987. // cleanup the path by checking line of sight
  7988. path->optimize(obj, acceptableSurfaces, blocked);
  7989. #if defined _DEBUG || defined _INTERNAL
  7990. if (TheGlobalData->m_debugAI==AI_DEBUG_PATHS)
  7991. {
  7992. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  7993. RGBColor color;
  7994. color.blue = 0;
  7995. color.red = color.green = 1;
  7996. Coord3D pos;
  7997. for( PathNode *node = path->getFirstNode(); node; node = node->getNext() )
  7998. {
  7999. // create objects to show path - they decay
  8000. pos = *node->getPosition();
  8001. color.red = color.green = 1;
  8002. if (node->getLayer() != LAYER_GROUND) {
  8003. color.red = 0;
  8004. }
  8005. addIcon(&pos, PATHFIND_CELL_SIZE_F*.25f, 200, color);
  8006. }
  8007. // show optimized path
  8008. for( node = path->getFirstNode(); node; node = node->getNextOptimized() )
  8009. {
  8010. pos = *node->getPosition();
  8011. addIcon(&pos, PATHFIND_CELL_SIZE_F*.8f, 200, color);
  8012. }
  8013. setDebugPath(path);
  8014. }
  8015. #endif
  8016. return path;
  8017. }
  8018. /**
  8019. * Work backwards from goal cell to construct final path.
  8020. */
  8021. void Pathfinder::prependCells( Path *path, const Coord3D *fromPos,
  8022. PathfindCell *goalCell, Bool center )
  8023. {
  8024. // traverse path cells in REVERSE order, creating path in desired order
  8025. // skip the LAST node, as that will be in the same cell as the unit itself - so use the unit's position
  8026. Coord3D pos;
  8027. PathfindCell *cell, *prevCell = NULL;
  8028. Bool goalCellNull = (goalCell->getParentCell()==NULL);
  8029. for( cell = goalCell; cell->getParentCell(); cell = cell->getParentCell() )
  8030. {
  8031. m_zoneManager.setPassable(cell->getXIndex(), cell->getYIndex(), true);
  8032. adjustCoordToCell(cell->getXIndex(), cell->getYIndex(), center, pos, cell->getLayer());
  8033. if (prevCell && cell->getXIndex()==prevCell->getXIndex() && cell->getYIndex()==prevCell->getYIndex()) {
  8034. // transitioning layers.
  8035. PathfindLayerEnum layer = cell->getLayer();
  8036. if (layer==LAYER_GROUND) {
  8037. layer = prevCell->getLayer();
  8038. }
  8039. DEBUG_ASSERTCRASH(layer!=LAYER_GROUND, ("Should have at 1 non-ground layer. jba"));
  8040. path->getFirstNode()->setLayer(layer);
  8041. continue;
  8042. }
  8043. Bool canOptimize = true;
  8044. if (cell->getType() == PathfindCell::CELL_CLIFF) {
  8045. if (prevCell && prevCell->getType() != PathfindCell::CELL_CLIFF) {
  8046. if (path->getFirstNode()) {
  8047. path->getFirstNode()->setCanOptimize(false);
  8048. }
  8049. }
  8050. } else {
  8051. if (prevCell && prevCell->getType() == PathfindCell::CELL_CLIFF) {
  8052. canOptimize = false;
  8053. }
  8054. }
  8055. path->prependNode( &pos, cell->getLayer() );
  8056. path->getFirstNode()->setCanOptimize(canOptimize);
  8057. if (cell->isBlockedByAlly()) {
  8058. path->setBlockedByAlly(true);
  8059. }
  8060. if (prevCell) {
  8061. prevCell->clearParentCell();
  8062. }
  8063. prevCell = cell;
  8064. }
  8065. m_zoneManager.setPassable(cell->getXIndex(), cell->getYIndex(), true);
  8066. if (goalCellNull) {
  8067. // Very short path.
  8068. adjustCoordToCell(cell->getXIndex(), cell->getYIndex(), center, pos, cell->getLayer());
  8069. path->prependNode( &pos, cell->getLayer() );
  8070. }
  8071. // put actual start position as first node on the path, so it begins right at the unit's feet
  8072. if (fromPos->x != path->getFirstNode()->getPosition()->x || fromPos->y != path->getFirstNode()->getPosition()->y) {
  8073. path->prependNode( fromPos, cell->getLayer() );
  8074. }
  8075. }
  8076. void Pathfinder::setDebugPath(Path *newDebugpath)
  8077. {
  8078. if (TheGlobalData->m_debugAI)
  8079. {
  8080. // copy the path for debugging
  8081. if (debugPath)
  8082. debugPath->deleteInstance();
  8083. debugPath = newInstance(Path);
  8084. for( PathNode *copyNode = newDebugpath->getFirstNode(); copyNode; copyNode = copyNode->getNextOptimized() )
  8085. debugPath->appendNode( copyNode->getPosition(), copyNode->getLayer() );
  8086. }
  8087. }
  8088. /**
  8089. * Given two world-space points, call callback for each cell.
  8090. * Uses Bresenham line algorithm from www.gamedev.net.
  8091. */
  8092. Int Pathfinder::iterateCellsAlongLine( const Coord3D& startWorld, const Coord3D& endWorld,
  8093. PathfindLayerEnum layer, CellAlongLineProc proc, void* userData )
  8094. {
  8095. ICoord2D start, end;
  8096. worldToCell( &startWorld, &start );
  8097. worldToCell( &endWorld, &end );
  8098. return iterateCellsAlongLine(start, end, layer, proc, userData);
  8099. }
  8100. /**
  8101. * Given two world-space points, call callback for each cell.
  8102. * Uses Bresenham line algorithm from www.gamedev.net.
  8103. */
  8104. Int Pathfinder::iterateCellsAlongLine( const ICoord2D &start, const ICoord2D &end,
  8105. PathfindLayerEnum layer, CellAlongLineProc proc, void* userData )
  8106. {
  8107. Int delta_x = abs(end.x - start.x); // The difference between the x's
  8108. Int delta_y = abs(end.y - start.y); // The difference between the y's
  8109. Int x = start.x; // Start x off at the first pixel
  8110. Int y = start.y; // Start y off at the first pixel
  8111. Int xinc1, xinc2;
  8112. if (end.x >= start.x) // The x-values are increasing
  8113. {
  8114. xinc1 = 1;
  8115. xinc2 = 1;
  8116. }
  8117. else // The x-values are decreasing
  8118. {
  8119. xinc1 = -1;
  8120. xinc2 = -1;
  8121. }
  8122. Int yinc1, yinc2;
  8123. if (end.y >= start.y) // The y-values are increasing
  8124. {
  8125. yinc1 = 1;
  8126. yinc2 = 1;
  8127. }
  8128. else // The y-values are decreasing
  8129. {
  8130. yinc1 = -1;
  8131. yinc2 = -1;
  8132. }
  8133. Bool checkY = true;
  8134. Int den, num, numadd, numpixels;
  8135. if (delta_x >= delta_y) // There is at least one x-value for every y-value
  8136. {
  8137. xinc1 = 0; // Don't change the x when numerator >= denominator
  8138. yinc2 = 0; // Don't change the y for every iteration
  8139. den = delta_x;
  8140. num = delta_x / 2;
  8141. numadd = delta_y;
  8142. numpixels = delta_x; // There are more x-values than y-values
  8143. }
  8144. else // There is at least one y-value for every x-value
  8145. {
  8146. checkY = false;
  8147. xinc2 = 0; // Don't change the x for every iteration
  8148. yinc1 = 0; // Don't change the y when numerator >= denominator
  8149. den = delta_y;
  8150. num = delta_y / 2;
  8151. numadd = delta_x;
  8152. numpixels = delta_y; // There are more y-values than x-values
  8153. }
  8154. PathfindCell* from = NULL;
  8155. for (Int curpixel = 0; curpixel <= numpixels; curpixel++)
  8156. {
  8157. PathfindCell* to = getCell( layer, x, y );
  8158. if (to==NULL) return 0;
  8159. Int ret = (*proc)(this, from, to, x, y, userData);
  8160. if (ret != 0)
  8161. return ret;
  8162. num += numadd; // Increase the numerator by the top of the fraction
  8163. if (num >= den) // Check if numerator >= denominator
  8164. {
  8165. num -= den; // Calculate the new numerator value
  8166. x += xinc1; // Change the x as appropriate
  8167. y += yinc1; // Change the y as appropriate
  8168. from = to;
  8169. to = getCell( layer, x, y );
  8170. if (to==NULL) return 0;
  8171. Int ret = (*proc)(this, from, to, x, y, userData);
  8172. if (ret != 0)
  8173. return ret;
  8174. }
  8175. x += xinc2; // Change the x as appropriate
  8176. y += yinc2; // Change the y as appropriate
  8177. from = to;
  8178. }
  8179. return 0;
  8180. }
  8181. //-----------------------------------------------------------------------------
  8182. static ObjectID getSlaverID(const Object* o)
  8183. {
  8184. for (BehaviorModule** update = o->getBehaviorModules(); *update; ++update)
  8185. {
  8186. SlavedUpdateInterface* sdu = (*update)->getSlavedUpdateInterface();
  8187. if (sdu != NULL)
  8188. {
  8189. return sdu->getSlaverID();
  8190. }
  8191. }
  8192. return INVALID_ID;
  8193. }
  8194. static ObjectID getContainerID(const Object* o)
  8195. {
  8196. const Object* container = o ? o->getContainedBy() : NULL;
  8197. return container ? container->getID() : INVALID_ID;
  8198. }
  8199. struct segmentIntersectsStruct
  8200. {
  8201. Object *theTallBuilding;
  8202. ObjectID ignoreBuilding;
  8203. };
  8204. /*static*/ Int Pathfinder::segmentIntersectsBuildingCallback(Pathfinder* pathfinder, PathfindCell* from, PathfindCell* to, Int to_x, Int to_y, void* userData)
  8205. {
  8206. segmentIntersectsStruct* d = (segmentIntersectsStruct*)userData;
  8207. if (to != NULL && (to->getType() == PathfindCell::CELL_OBSTACLE))
  8208. {
  8209. Object *obj = TheGameLogic->findObjectByID(to->getObstacleID());
  8210. if (obj && obj->isKindOf(KINDOF_AIRCRAFT_PATH_AROUND)) {
  8211. if (obj->getID() == d->ignoreBuilding) {
  8212. return 0;
  8213. }
  8214. d->theTallBuilding = obj;
  8215. return 1;
  8216. }
  8217. }
  8218. return 0; // keep going
  8219. }
  8220. struct ViewBlockedStruct
  8221. {
  8222. const Object *obj;
  8223. const Object *objOther;
  8224. };
  8225. /*static*/ Int Pathfinder::lineBlockedByObstacleCallback(Pathfinder* pathfinder, PathfindCell* from, PathfindCell* to, Int to_x, Int to_y, void* userData)
  8226. {
  8227. const ViewBlockedStruct* d = (const ViewBlockedStruct*)userData;
  8228. if (to != NULL && (to->getType() == PathfindCell::CELL_OBSTACLE))
  8229. {
  8230. // we never block our own view!
  8231. if (to->isObstaclePresent(d->obj->getID()))
  8232. return 0;
  8233. // nor does the object we're trying to see!
  8234. if (to->isObstaclePresent(d->objOther->getID()))
  8235. return 0;
  8236. // if the obstacle is our container, ignore it as an obstacle.
  8237. if (to->isObstaclePresent(getContainerID(d->obj)))
  8238. return 0;
  8239. // @todo: if the obstacle is objOther's container, AND it's a "visible" container, ignore it.
  8240. // if the obstacle is the item to which we are slaved, ignore it as an obstacle.
  8241. if (to->isObstaclePresent(getSlaverID(d->obj)))
  8242. return 0;
  8243. // if the obstacle is the item to which objOther is slaved, ignore it as an obstacle.
  8244. if (to->isObstaclePresent(getSlaverID(d->objOther)))
  8245. return 0;
  8246. // if the obstacle is transparent, ignore it, since this callback is only used for line-of-sight. (srj)
  8247. if (to->isObstacleTransparent())
  8248. return 0;
  8249. return 1; // bail early
  8250. }
  8251. return 0; // keep going
  8252. }
  8253. struct ViewAttackBlockedStruct
  8254. {
  8255. const Object *obj;
  8256. const Object *victim;
  8257. const PathfindCell *victimCell;
  8258. Int skipCount;
  8259. };
  8260. /*static*/ Int Pathfinder::attackBlockedByObstacleCallback(Pathfinder* pathfinder, PathfindCell* from, PathfindCell* to, Int to_x, Int to_y, void* userData)
  8261. {
  8262. ViewAttackBlockedStruct* d = (ViewAttackBlockedStruct*)userData;
  8263. if (d->skipCount>0) {
  8264. d->skipCount--;
  8265. return 0;
  8266. }
  8267. if (to != NULL && (to->getType() == PathfindCell::CELL_OBSTACLE))
  8268. {
  8269. // we never block our own view!
  8270. if (to->isObstaclePresent(d->obj->getID()))
  8271. return 0;
  8272. if (d->victim) {
  8273. // nor does the object we're trying to attack!
  8274. if (to->isObstaclePresent(d->victim->getID()))
  8275. return 0;
  8276. // if the obstacle is the item to which objOther is slaved, ignore it as an obstacle.
  8277. if (to->isObstaclePresent(getSlaverID(d->victim)))
  8278. return 0;
  8279. }
  8280. // if the obstacle is our container, ignore it as an obstacle.
  8281. if (to->isObstaclePresent(getContainerID(d->obj)))
  8282. return 0;
  8283. // @todo: if the obstacle is objOther's container, AND it's a "visible" container, ignore it.
  8284. // if the obstacle is the item to which we are slaved, ignore it as an obstacle.
  8285. if (to->isObstaclePresent(getSlaverID(d->obj)))
  8286. return 0;
  8287. if (to->isObstacleTransparent())
  8288. return 0;
  8289. //Kris: Added the check for victimCell because in China01 -- after the intro, NW of your
  8290. //base is a cream colored building that lies in a negative coord. When you order units to
  8291. //force attack it, it crashes.
  8292. if( d->victimCell && to->isObstaclePresent( d->victimCell->getObstacleID() ) )
  8293. {
  8294. // Victim is inside the bounds of another object. We don't let this block us,
  8295. // as usually it is on the edge and it looks like we should be able to shoot it. jba.
  8296. return 0;
  8297. }
  8298. return 1; // bail early
  8299. }
  8300. return 0; // keep going
  8301. }
  8302. //-----------------------------------------------------------------------------
  8303. Bool Pathfinder::isViewBlockedByObstacle(const Object* obj, const Object* objOther)
  8304. {
  8305. ViewBlockedStruct info;
  8306. info.obj = obj;
  8307. info.objOther = objOther;
  8308. if (objOther && objOther->isSignificantlyAboveTerrain()) {
  8309. return false; // We don't check los to flying objects. jba.
  8310. }
  8311. #if 1
  8312. return isAttackViewBlockedByObstacle(obj, *obj->getPosition(), objOther, *objOther->getPosition());
  8313. #else
  8314. PathfindLayerEnum layer = objOther->getLayer();
  8315. if (layer==LAYER_GROUND) {
  8316. layer = obj->getLayer();
  8317. }
  8318. Int ret = iterateCellsAlongLine(*obj->getPosition(), *objOther->getPosition(),
  8319. layer, lineBlockedByObstacleCallback, &info);
  8320. return ret != 0;
  8321. #endif
  8322. }
  8323. //-----------------------------------------------------------------------------
  8324. Bool Pathfinder::isAttackViewBlockedByObstacle(const Object* attacker, const Coord3D& attackerPos, const Object* victim, const Coord3D& victimPos)
  8325. {
  8326. //CRCDEBUG_LOG(("Pathfinder::isAttackViewBlockedByObstacle() - attackerPos is (%g,%g,%g) (%X,%X,%X)\n",
  8327. // attackerPos.x, attackerPos.y, attackerPos.z,
  8328. // AS_INT(attackerPos.x),AS_INT(attackerPos.y),AS_INT(attackerPos.z)));
  8329. //CRCDEBUG_LOG(("Pathfinder::isAttackViewBlockedByObstacle() - victimPos is (%g,%g,%g) (%X,%X,%X)\n",
  8330. // victimPos.x, victimPos.y, victimPos.z,
  8331. // AS_INT(victimPos.x),AS_INT(victimPos.y),AS_INT(victimPos.z)));
  8332. // Global switch to turn this off in case it doesn't work.
  8333. if (!TheAI->getAiData()->m_attackUsesLineOfSight)
  8334. {
  8335. //CRCDEBUG_LOG(("Pathfinder::isAttackViewBlockedByObstacle() 1\n"));
  8336. return false;
  8337. }
  8338. // If the attacker doesn't need line of sight, isn't blocked.
  8339. if (!attacker->isKindOf(KINDOF_ATTACK_NEEDS_LINE_OF_SIGHT))
  8340. {
  8341. //CRCDEBUG_LOG(("Pathfinder::isAttackViewBlockedByObstacle() 2\n"));
  8342. return false;
  8343. }
  8344. // srj sez: this is a good start at taking terrain into account for attacks, but findAttackPath needs to be smartened also
  8345. #define LOS_TERRAIN
  8346. #ifdef LOS_TERRAIN
  8347. const Weapon* w = attacker->getCurrentWeapon();
  8348. if (attacker->isKindOf(KINDOF_IMMOBILE)) {
  8349. // Don't take terrain blockage into account, since we can't move around it. jba.
  8350. w = NULL;
  8351. }
  8352. if (w)
  8353. {
  8354. Bool viewBlocked;
  8355. if (victim)
  8356. viewBlocked = !w->isClearGoalFiringLineOfSightTerrain(attacker, attackerPos, victim);
  8357. else
  8358. viewBlocked = !w->isClearGoalFiringLineOfSightTerrain(attacker, attackerPos, victimPos);
  8359. if (viewBlocked)
  8360. {
  8361. //CRCDEBUG_LOG(("Pathfinder::isAttackViewBlockedByObstacle() 3\n"));
  8362. return true;
  8363. }
  8364. }
  8365. #endif
  8366. ViewAttackBlockedStruct info;
  8367. info.obj = attacker;
  8368. info.victim = victim;
  8369. PathfindLayerEnum layer = LAYER_GROUND;
  8370. if (victim) {
  8371. layer = victim->getLayer();
  8372. }
  8373. info.victimCell = getCell(layer, &victimPos);
  8374. info.skipCount = 0;
  8375. if (attacker->getLayer() != LAYER_GROUND)
  8376. {
  8377. info.skipCount = 3; /// srj -- someone wanna tell me what this magic number means?
  8378. /// jba - Yes, it means that if someone is on a bridge, or rooftop, they can see
  8379. /// 3 pathfind cells out of whatever they are standing on.
  8380. /// srj -- awesome! thank you very much :-)
  8381. if (layer==LAYER_GROUND) {
  8382. layer = attacker->getLayer();
  8383. }
  8384. }
  8385. Int ret = iterateCellsAlongLine(attackerPos, victimPos, layer, attackBlockedByObstacleCallback, &info);
  8386. //CRCDEBUG_LOG(("Pathfinder::isAttackViewBlockedByObstacle() 4\n"));
  8387. return ret != 0;
  8388. }
  8389. static void computeNormalRadialOffset(const Coord3D& from, Coord3D& insert, const Coord3D& to,
  8390. Object *obj, Real radius)
  8391. {
  8392. Real crossProduct;
  8393. Real dx = to.x - from.x;
  8394. Real dy = to.y -from.y;
  8395. Coord3D objPos = *obj->getPosition();
  8396. Real objDx = objPos.x - from.x;
  8397. Real objDy = objPos.y - from.y;
  8398. crossProduct = dx*objDy - dy*objDx;
  8399. Coord3D fromToNormal;
  8400. fromToNormal.z = 0;
  8401. if (crossProduct>0) {
  8402. fromToNormal.x = dy;
  8403. fromToNormal.y = -dx;
  8404. } else {
  8405. fromToNormal.x = -dy;
  8406. fromToNormal.y = dx;
  8407. }
  8408. fromToNormal.normalize();
  8409. Real length = radius;
  8410. insert = *obj->getPosition();
  8411. insert.x += fromToNormal.x*length;
  8412. insert.y += fromToNormal.y*length;
  8413. }
  8414. //-----------------------------------------------------------------------------
  8415. Bool Pathfinder::segmentIntersectsTallBuilding(const PathNode *curNode,
  8416. PathNode *nextNode, ObjectID ignoreBuilding, Coord3D *insertPos1, Coord3D *insertPos2, Coord3D *insertPos3 )
  8417. {
  8418. segmentIntersectsStruct info;
  8419. info.theTallBuilding = NULL;
  8420. info.ignoreBuilding = ignoreBuilding;
  8421. Coord3D fromPos = *curNode->getPosition();
  8422. Coord3D toPos = *nextNode->getPosition();
  8423. Int i;
  8424. for (i=0; i<2; i++) {
  8425. Int ret = iterateCellsAlongLine(fromPos, toPos, LAYER_GROUND, segmentIntersectsBuildingCallback, &info);
  8426. if (ret!=0 && info.theTallBuilding) {
  8427. // see if toPos is inside the radius of the tall building.
  8428. Coord3D bldgPos = *info.theTallBuilding->getPosition();
  8429. Coord2D delta;
  8430. Real radius = info.theTallBuilding->getGeometryInfo().getBoundingCircleRadius() + 2*PATHFIND_CELL_SIZE_F;
  8431. delta.x = toPos.x - bldgPos.x;
  8432. delta.y = toPos.y - bldgPos.y;
  8433. if (delta.length() <= radius*0.98) {
  8434. if (delta.length() < 0.1) {
  8435. delta.x = 1;
  8436. }
  8437. delta.normalize();
  8438. delta.x *= radius;
  8439. delta.y *= radius;
  8440. toPos.x = bldgPos.x+delta.x;
  8441. toPos.y = bldgPos.y+delta.y;
  8442. nextNode->setPosition(&toPos);
  8443. continue;
  8444. }
  8445. delta.x = fromPos.x - bldgPos.x;
  8446. delta.y = fromPos.y - bldgPos.y;
  8447. if (delta.length() <= radius*0.98) {
  8448. if (delta.length() < 0.1) {
  8449. delta.x = 1;
  8450. }
  8451. delta.normalize();
  8452. delta.x *= radius;
  8453. delta.y *= radius;
  8454. fromPos.x = bldgPos.x+delta.x;
  8455. fromPos.y = bldgPos.y+delta.y;
  8456. }
  8457. computeNormalRadialOffset(fromPos, *insertPos2, toPos, info.theTallBuilding, radius);
  8458. computeNormalRadialOffset(fromPos, *insertPos1, *insertPos2, info.theTallBuilding, radius);
  8459. computeNormalRadialOffset(*insertPos2, *insertPos3, toPos, info.theTallBuilding, radius);
  8460. return true;
  8461. }
  8462. }
  8463. return false;
  8464. }
  8465. //-----------------------------------------------------------------------------
  8466. Bool Pathfinder::circleClipsTallBuilding( const Coord3D *from, const Coord3D *to, Real circleRadius, ObjectID ignoreBuilding, Coord3D *adjustTo)
  8467. {
  8468. PartitionFilterAcceptByKindOf filterKindof(MAKE_KINDOF_MASK(KINDOF_AIRCRAFT_PATH_AROUND), KINDOFMASK_NONE);
  8469. PartitionFilter *filters[] = { &filterKindof, NULL };
  8470. Object* tallBuilding = ThePartitionManager->getClosestObject(to, circleRadius, FROM_BOUNDINGSPHERE_2D, filters);
  8471. if (tallBuilding) {
  8472. Real radius = tallBuilding->getGeometryInfo().getBoundingCircleRadius() + 2*PATHFIND_CELL_SIZE_F;
  8473. computeNormalRadialOffset(*from, *adjustTo, *to, tallBuilding, circleRadius+radius);
  8474. Object* otherTallBuilding = ThePartitionManager->getClosestObject(adjustTo, circleRadius, FROM_BOUNDINGSPHERE_2D, filters);
  8475. if (otherTallBuilding && otherTallBuilding!=tallBuilding) {
  8476. radius = otherTallBuilding->getGeometryInfo().getBoundingCircleRadius() + 2*PATHFIND_CELL_SIZE_F;
  8477. Coord3D tmpTo = *adjustTo;
  8478. computeNormalRadialOffset(*from, *adjustTo, tmpTo, otherTallBuilding, circleRadius+radius);
  8479. }
  8480. return true;
  8481. }
  8482. return false;
  8483. }
  8484. //-----------------------------------------------------------------------------
  8485. struct LinePassableStruct
  8486. {
  8487. const Object *obj;
  8488. LocomotorSurfaceTypeMask acceptableSurfaces;
  8489. Int radius;
  8490. Bool centerInCell;
  8491. Bool blocked;
  8492. Bool allowPinched;
  8493. };
  8494. /*static*/ Int Pathfinder::linePassableCallback(Pathfinder* pathfinder, PathfindCell* from, PathfindCell* to, Int to_x, Int to_y, void* userData)
  8495. {
  8496. const LinePassableStruct* d = (const LinePassableStruct*)userData;
  8497. Bool isCrusher = d->obj ? d->obj->getCrusherLevel() > 0 : false;
  8498. TCheckMovementInfo info;
  8499. info.cell.x = to_x;
  8500. info.cell.y = to_y;
  8501. info.layer = to->getLayer();
  8502. info.centerInCell = d->centerInCell;
  8503. info.radius = d->radius;
  8504. info.considerTransient = d->blocked;
  8505. info.acceptableSurfaces = d->acceptableSurfaces;
  8506. if (!pathfinder->checkForMovement(d->obj, info))
  8507. {
  8508. return 1; // bail out
  8509. }
  8510. if (info.allyFixedCount || info.enemyFixed)
  8511. {
  8512. return 1; // bail out
  8513. }
  8514. if (!d->allowPinched && to->getPinched()) {
  8515. return 1; // bail out.
  8516. }
  8517. if (from && to->getLayer() != LAYER_GROUND && from->getLayer() == to->getLayer()) {
  8518. if (to->getType() == PathfindCell::CELL_CLEAR) {
  8519. return 0;
  8520. }
  8521. }
  8522. if (pathfinder->validMovementPosition( isCrusher, d->acceptableSurfaces, to, from ) == false)
  8523. {
  8524. return 1; // bail out
  8525. }
  8526. return 0; // keep going
  8527. }
  8528. //-----------------------------------------------------------------------------
  8529. struct GroundPathPassableStruct
  8530. {
  8531. Int diameter;
  8532. Bool crusher;
  8533. };
  8534. /*static*/ Int Pathfinder::groundPathPassableCallback(Pathfinder* pathfinder, PathfindCell* from, PathfindCell* to, Int to_x, Int to_y, void* userData)
  8535. {
  8536. const GroundPathPassableStruct* d = (const GroundPathPassableStruct*)userData;
  8537. Int curDiameter = pathfinder->clearCellForDiameter(d->crusher, to_x, to_y, to->getLayer(), d->diameter);
  8538. if (curDiameter==d->diameter) return 0; // good to go.
  8539. if (from && to->getLayer() != LAYER_GROUND && from->getLayer() == to->getLayer()) {
  8540. return 0;
  8541. }
  8542. return 1; // failed.
  8543. }
  8544. //-----------------------------------------------------------------------------
  8545. /**
  8546. * Given two world-space points, check the line of sight between them for any impassible cells.
  8547. * Uses Bresenham line algorithm from www.gamedev.net.
  8548. */
  8549. Bool Pathfinder::isLinePassable( const Object *obj, LocomotorSurfaceTypeMask acceptableSurfaces,
  8550. PathfindLayerEnum layer, const Coord3D& startWorld,
  8551. const Coord3D& endWorld, Bool blocked,
  8552. Bool allowPinched)
  8553. {
  8554. LinePassableStruct info;
  8555. //CRCDEBUG_LOG(("Pathfinder::isLinePassable(): %d %d %d \n", m_ignoreObstacleID, m_isMapReady, m_isTunneling));
  8556. info.obj = obj;
  8557. info.acceptableSurfaces = acceptableSurfaces;
  8558. getRadiusAndCenter(obj, info.radius, info.centerInCell);
  8559. info.blocked = blocked;
  8560. info.allowPinched = allowPinched;
  8561. Int ret = iterateCellsAlongLine(startWorld, endWorld, layer, linePassableCallback, (void*)&info);
  8562. return ret == 0;
  8563. }
  8564. //-----------------------------------------------------------------------------
  8565. /**
  8566. * Given two world-space points, check the line of sight between them for any impassible cells.
  8567. * Uses Bresenham line algorithm from www.gamedev.net.
  8568. */
  8569. Bool Pathfinder::isGroundPathPassable( Bool isCrusher, const Coord3D& startWorld, PathfindLayerEnum startLayer,
  8570. const Coord3D& endWorld, Int pathDiameter)
  8571. {
  8572. GroundPathPassableStruct info;
  8573. info.diameter = pathDiameter;
  8574. info.crusher = isCrusher;
  8575. Int ret = iterateCellsAlongLine(startWorld, endWorld, startLayer, groundPathPassableCallback, (void*)&info);
  8576. return ret == 0;
  8577. }
  8578. /**
  8579. * Classify the cells under the bridge
  8580. * If 'repaired' is true, bridge is repaired
  8581. * If 'repaired' is false, bridge has been damaged to be impassable
  8582. */
  8583. void Pathfinder::changeBridgeState( PathfindLayerEnum layer, Bool repaired)
  8584. {
  8585. if (m_layers[layer].isUnused()) return;
  8586. if (m_layers[layer].setDestroyed(!repaired)) {
  8587. m_zoneManager.markZonesDirty( repaired );
  8588. }
  8589. }
  8590. void Pathfinder::getRadiusAndCenter(const Object *obj, Int &iRadius, Bool &center)
  8591. {
  8592. enum {MAX_RADIUS = 2};
  8593. if (!obj)
  8594. {
  8595. center = true;
  8596. iRadius = 0;
  8597. return;
  8598. }
  8599. Real diameter = 2*obj->getGeometryInfo().getBoundingCircleRadius();
  8600. if (diameter>PATHFIND_CELL_SIZE_F && diameter<2.0f*PATHFIND_CELL_SIZE_F) {
  8601. diameter = 2.0f*PATHFIND_CELL_SIZE_F;
  8602. }
  8603. iRadius = REAL_TO_INT_FLOOR(diameter/PATHFIND_CELL_SIZE_F+0.3f);
  8604. center = false;
  8605. if (iRadius==0) iRadius++;
  8606. if (iRadius&1)
  8607. {
  8608. center = true;
  8609. }
  8610. iRadius /= 2;
  8611. if (iRadius > MAX_RADIUS)
  8612. {
  8613. iRadius = MAX_RADIUS;
  8614. center = true;
  8615. }
  8616. }
  8617. /**
  8618. * Updates the goal cell for an ai unit.
  8619. */
  8620. void Pathfinder::updateGoal( Object *obj, const Coord3D *newGoalPos, PathfindLayerEnum layer)
  8621. {
  8622. if (obj->isKindOf(KINDOF_IMMOBILE)) {
  8623. // Only consider mobile.
  8624. return;
  8625. }
  8626. AIUpdateInterface *ai = obj->getAIUpdateInterface();
  8627. if (!ai) return; // only consider ai objects.
  8628. if (!ai->isDoingGroundMovement()) // exception:sniped choppers are on ground
  8629. {
  8630. Bool isUnmannedHelicopter = ( obj->isKindOf( KINDOF_PRODUCED_AT_HELIPAD ) && obj->isDisabledByType( DISABLED_UNMANNED ) ) ;
  8631. if ( ! isUnmannedHelicopter )
  8632. {
  8633. updateAircraftGoal(obj, newGoalPos);
  8634. return;
  8635. }
  8636. }
  8637. PathfindLayerEnum originalLayer = obj->getDestinationLayer();
  8638. //DEBUG_LOG(("Object Goal layer is %d\n", layer));
  8639. Bool layerChanged = originalLayer != layer;
  8640. Bool doGround=false;
  8641. Bool doLayer=false;
  8642. if (layer==LAYER_GROUND) {
  8643. doGround = true;
  8644. } else {
  8645. doLayer = true;
  8646. if (TheTerrainLogic->objectInteractsWithBridgeEnd(obj, layer)) {
  8647. doGround = true;
  8648. }
  8649. }
  8650. ICoord2D goalCell = *ai->getPathfindGoalCell();
  8651. Bool centerInCell;
  8652. Int radius;
  8653. ICoord2D newCell;
  8654. getRadiusAndCenter(obj, radius, centerInCell);
  8655. Int numCellsAbove = radius;
  8656. if (centerInCell) numCellsAbove++;
  8657. if (centerInCell) {
  8658. newCell.x = REAL_TO_INT_FLOOR(newGoalPos->x/PATHFIND_CELL_SIZE_F);
  8659. newCell.y = REAL_TO_INT_FLOOR(newGoalPos->y/PATHFIND_CELL_SIZE_F);
  8660. } else {
  8661. newCell.x = REAL_TO_INT_FLOOR(0.5f+newGoalPos->x/PATHFIND_CELL_SIZE_F);
  8662. newCell.y = REAL_TO_INT_FLOOR(0.5f+newGoalPos->y/PATHFIND_CELL_SIZE_F);
  8663. }
  8664. if (!layerChanged && newCell.x==goalCell.x && newCell.y == goalCell.y) {
  8665. return;
  8666. }
  8667. removeGoal(obj);
  8668. obj->setDestinationLayer(layer);
  8669. ai->setPathfindGoalCell(newCell);
  8670. Int i,j;
  8671. ICoord2D cellNdx;
  8672. Bool warn = true;
  8673. for (i=newCell.x-radius; i<newCell.x+numCellsAbove; i++) {
  8674. for (j=newCell.y-radius; j<newCell.y+numCellsAbove; j++) {
  8675. PathfindCell *cell;
  8676. if (doLayer) {
  8677. cell = getCell(layer, i, j);
  8678. if (cell) {
  8679. if (warn && cell->getGoalUnit()!=INVALID_ID && cell->getGoalUnit() != obj->getID()) {
  8680. warn = false;
  8681. // jba intense debug
  8682. //DEBUG_LOG(, ("Units got stuck close to each other. jba\n"));
  8683. }
  8684. cellNdx.x = i;
  8685. cellNdx.y = j;
  8686. cell->setGoalUnit(obj->getID(), cellNdx);
  8687. }
  8688. }
  8689. if (doGround) {
  8690. cell = getCell(LAYER_GROUND, i, j);
  8691. if (cell) {
  8692. if (warn && cell->getGoalUnit()!=INVALID_ID && cell->getGoalUnit() != obj->getID()) {
  8693. warn = false;
  8694. // jba intense debug
  8695. //DEBUG_LOG(, ("Units got stuck close to each other. jba\n"));
  8696. }
  8697. cellNdx.x = i;
  8698. cellNdx.y = j;
  8699. cell->setGoalUnit(obj->getID(), cellNdx);
  8700. }
  8701. }
  8702. }
  8703. }
  8704. }
  8705. /**
  8706. * Updates the goal cell for an ai unit.
  8707. */
  8708. void Pathfinder::updateAircraftGoal( Object *obj, const Coord3D *newGoalPos)
  8709. {
  8710. if (obj->isKindOf(KINDOF_IMMOBILE)) {
  8711. // Only consider mobile.
  8712. return;
  8713. }
  8714. removeGoal(obj);
  8715. AIUpdateInterface *ai = obj->getAIUpdateInterface();
  8716. if (!ai) return; // only consider ai objects.
  8717. if (ai->isDoingGroundMovement()) {
  8718. return; // shouldn't really happen, but just in case.
  8719. }
  8720. // For now, we are only doing HOVER, and WINGS.
  8721. if (!ai->isAircraftThatAdjustsDestination()) return;
  8722. ICoord2D goalCell = *ai->getPathfindGoalCell();
  8723. Bool centerInCell;
  8724. Int radius;
  8725. ICoord2D newCell;
  8726. getRadiusAndCenter(obj, radius, centerInCell);
  8727. Int numCellsAbove = radius;
  8728. if (centerInCell) numCellsAbove++;
  8729. if (centerInCell) {
  8730. newCell.x = REAL_TO_INT_FLOOR(newGoalPos->x/PATHFIND_CELL_SIZE_F);
  8731. newCell.y = REAL_TO_INT_FLOOR(newGoalPos->y/PATHFIND_CELL_SIZE_F);
  8732. } else {
  8733. newCell.x = REAL_TO_INT_FLOOR(0.5f+newGoalPos->x/PATHFIND_CELL_SIZE_F);
  8734. newCell.y = REAL_TO_INT_FLOOR(0.5f+newGoalPos->y/PATHFIND_CELL_SIZE_F);
  8735. }
  8736. if (newCell.x==goalCell.x && newCell.y == goalCell.y) {
  8737. return;
  8738. }
  8739. ai->setPathfindGoalCell(newCell);
  8740. Int i,j;
  8741. ICoord2D cellNdx;
  8742. for (i=newCell.x-radius; i<newCell.x+numCellsAbove; i++) {
  8743. for (j=newCell.y-radius; j<newCell.y+numCellsAbove; j++) {
  8744. PathfindCell *cell;
  8745. cell = getCell(LAYER_GROUND, i, j);
  8746. if (cell) {
  8747. cellNdx.x = i;
  8748. cellNdx.y = j;
  8749. cell->setGoalAircraft(obj->getID(), cellNdx);
  8750. }
  8751. }
  8752. }
  8753. }
  8754. /**
  8755. * Removes the goal cell for an ai unit.
  8756. * Used for a unit that is going to be moving several times, like following a waypoint path,
  8757. * or intentionally collides with other units (like a car bomb). jba
  8758. */
  8759. void Pathfinder::removeGoal( Object *obj)
  8760. {
  8761. if (obj->isKindOf(KINDOF_IMMOBILE)) {
  8762. // Only consider mobile.
  8763. return;
  8764. }
  8765. AIUpdateInterface *ai = obj->getAIUpdateInterface();
  8766. if (!ai) return; // only consider ai objects.
  8767. ICoord2D goalCell = *ai->getPathfindGoalCell();
  8768. Bool centerInCell;
  8769. Int radius;
  8770. ICoord2D newCell;
  8771. getRadiusAndCenter(obj, radius, centerInCell);
  8772. if (radius==0) {
  8773. radius++;
  8774. }
  8775. Int numCellsAbove = radius;
  8776. if (centerInCell) numCellsAbove++;
  8777. newCell.x = newCell.y = -1;
  8778. if (newCell.x==goalCell.x && newCell.y == goalCell.y) {
  8779. return;
  8780. }
  8781. ICoord2D cellNdx;
  8782. ai->setPathfindGoalCell(newCell);
  8783. Int i,j;
  8784. if (goalCell.x>=0 && goalCell.y>=0) {
  8785. for (i=goalCell.x-radius; i<goalCell.x+numCellsAbove; i++) {
  8786. for (j=goalCell.y-radius; j<goalCell.y+numCellsAbove; j++) {
  8787. PathfindCell *cell = getCell(LAYER_GROUND, i, j);
  8788. if (cell) {
  8789. if (cell->getGoalUnit()==obj->getID()) {
  8790. cellNdx.x = i;
  8791. cellNdx.y = j;
  8792. cell->setGoalUnit(INVALID_ID, cellNdx);
  8793. }
  8794. if (cell->getGoalAircraft()==obj->getID()) {
  8795. cellNdx.x = i;
  8796. cellNdx.y = j;
  8797. cell->setGoalAircraft(INVALID_ID, cellNdx);
  8798. }
  8799. }
  8800. if (obj->getDestinationLayer()!=LAYER_GROUND) {
  8801. cell = getCell( obj->getDestinationLayer(), i, j);
  8802. if (cell) {
  8803. if (cell->getGoalUnit()==obj->getID()) {
  8804. cellNdx.x = i;
  8805. cellNdx.y = j;
  8806. cell->setGoalUnit(INVALID_ID, cellNdx);
  8807. }
  8808. }
  8809. }
  8810. }
  8811. }
  8812. }
  8813. }
  8814. /**
  8815. * Updates the position cell for an ai unit.
  8816. */
  8817. void Pathfinder::updatePos( Object *obj, const Coord3D *newPos)
  8818. {
  8819. if (obj->isKindOf(KINDOF_IMMOBILE))
  8820. {
  8821. // Only consider mobile.
  8822. return;
  8823. }
  8824. if (!m_isMapReady)
  8825. return;
  8826. AIUpdateInterface *ai = obj->getAIUpdateInterface();
  8827. if (!ai)
  8828. return; // only consider ai objects.
  8829. ICoord2D curCell = *ai->getCurPathfindCell();
  8830. if (!ai->isDoingGroundMovement())
  8831. {
  8832. if (curCell.x>=0 && curCell.y>=0)
  8833. {
  8834. removePos(obj);
  8835. }
  8836. return;
  8837. }
  8838. Bool centerInCell;
  8839. Int radius;
  8840. ICoord2D newCell;
  8841. getRadiusAndCenter(obj, radius, centerInCell);
  8842. Int numCellsAbove = radius;
  8843. if (centerInCell)
  8844. numCellsAbove++;
  8845. if (centerInCell)
  8846. {
  8847. newCell.x = REAL_TO_INT_FLOOR(newPos->x/PATHFIND_CELL_SIZE_F);
  8848. newCell.y = REAL_TO_INT_FLOOR(newPos->y/PATHFIND_CELL_SIZE_F);
  8849. }
  8850. else
  8851. {
  8852. newCell.x = REAL_TO_INT_FLOOR(0.5f+newPos->x/PATHFIND_CELL_SIZE_F);
  8853. newCell.y = REAL_TO_INT_FLOOR(0.5f+newPos->y/PATHFIND_CELL_SIZE_F);
  8854. }
  8855. if (newCell.x==curCell.x && newCell.y == curCell.y)
  8856. {
  8857. return;
  8858. }
  8859. PathfindLayerEnum layer = obj->getLayer();
  8860. Bool doGround=false;
  8861. Bool doLayer=false;
  8862. if (layer==LAYER_GROUND) {
  8863. doGround = true; // just have to do ground
  8864. } else {
  8865. doLayer = true; // have to do the layer
  8866. if (TheTerrainLogic->objectInteractsWithBridgeEnd(obj, layer)) {
  8867. doGround = true; // In this case, have to both layer & ground, as they overlap here.
  8868. }
  8869. }
  8870. ai->setCurPathfindCell(newCell);
  8871. Int i,j;
  8872. ICoord2D cellNdx;
  8873. //DEBUG_LOG(("Updating unit pos at cell %d, %d\n", newCell.x, newCell.y));
  8874. if (curCell.x>=0 && curCell.y>=0) {
  8875. for (i=curCell.x-radius; i<curCell.x+numCellsAbove; i++) {
  8876. for (j=curCell.y-radius; j<curCell.y+numCellsAbove; j++) {
  8877. cellNdx.x = i;
  8878. cellNdx.y = j;
  8879. PathfindCell *cell = getCell(layer, i, j);
  8880. if (cell) {
  8881. if (cell->getPosUnit()==obj->getID()) {
  8882. cell->setPosUnit(INVALID_ID, cellNdx);
  8883. }
  8884. }
  8885. if (layer!=LAYER_GROUND) {
  8886. // Remove from the ground, if present.
  8887. cell = getCell(LAYER_GROUND, i, j);
  8888. if (cell) {
  8889. if (cell->getPosUnit()==obj->getID()) {
  8890. cell->setPosUnit(INVALID_ID, cellNdx);
  8891. }
  8892. }
  8893. }
  8894. }
  8895. }
  8896. }
  8897. for (i=newCell.x-radius; i<newCell.x+numCellsAbove; i++) {
  8898. for (j=newCell.y-radius; j<newCell.y+numCellsAbove; j++) {
  8899. PathfindCell *cell;
  8900. cellNdx.x = i;
  8901. cellNdx.y = j;
  8902. if (doLayer) {
  8903. cell = getCell(layer, i, j);
  8904. if (cell) {
  8905. cell->setPosUnit(obj->getID(), cellNdx);
  8906. }
  8907. }
  8908. if (doGround) {
  8909. cell = getCell(LAYER_GROUND, i, j);
  8910. if (cell) {
  8911. cell->setPosUnit(obj->getID(), cellNdx);
  8912. }
  8913. }
  8914. }
  8915. }
  8916. }
  8917. /**
  8918. * Removes the position cell flags for an ai unit.
  8919. */
  8920. void Pathfinder::removePos( Object *obj)
  8921. {
  8922. if (obj->isKindOf(KINDOF_IMMOBILE)) {
  8923. // Only consider mobile.
  8924. return;
  8925. }
  8926. if (!m_isMapReady) return;
  8927. AIUpdateInterface *ai = obj->getAIUpdateInterface();
  8928. if (!ai) return; // only consider ai objects.
  8929. ICoord2D curCell = *ai->getCurPathfindCell();
  8930. Bool centerInCell;
  8931. Int radius;
  8932. getRadiusAndCenter(obj, radius, centerInCell);
  8933. Int numCellsAbove = radius;
  8934. if (centerInCell) numCellsAbove++;
  8935. PathfindLayerEnum layer = obj->getLayer();
  8936. ICoord2D newCell;
  8937. newCell.x = newCell.y = -1;
  8938. ai->setCurPathfindCell(newCell);
  8939. Int i,j;
  8940. ICoord2D cellNdx;
  8941. //DEBUG_LOG(("Updating unit pos at cell %d, %d\n", newCell.x, newCell.y));
  8942. if (curCell.x>=0 && curCell.y>=0) {
  8943. for (i=curCell.x-radius; i<curCell.x+numCellsAbove; i++) {
  8944. for (j=curCell.y-radius; j<curCell.y+numCellsAbove; j++) {
  8945. cellNdx.x = i;
  8946. cellNdx.y = j;
  8947. PathfindCell *cell = getCell(layer, i, j);
  8948. if (cell) {
  8949. if (cell->getPosUnit()==obj->getID()) {
  8950. cell->setPosUnit(INVALID_ID, cellNdx);
  8951. }
  8952. }
  8953. if (layer!=LAYER_GROUND) {
  8954. // Remove from the ground, if present.
  8955. cell = getCell(LAYER_GROUND, i, j);
  8956. if (cell) {
  8957. if (cell->getPosUnit()==obj->getID()) {
  8958. cell->setPosUnit(INVALID_ID, cellNdx);
  8959. }
  8960. }
  8961. }
  8962. }
  8963. }
  8964. }
  8965. }
  8966. /**
  8967. * Removes a mobile unit from the pathfind grid.
  8968. */
  8969. void Pathfinder::removeUnitFromPathfindMap( Object *obj )
  8970. {
  8971. removePos(obj);
  8972. removeGoal(obj);
  8973. }
  8974. Bool Pathfinder::moveAllies(Object *obj, Path *path)
  8975. {
  8976. #ifdef DO_UNIT_TIMINGS
  8977. #pragma MESSAGE("*** WARNING *** DOING DO_UNIT_TIMINGS!!!!")
  8978. extern Bool g_UT_startTiming;
  8979. if (g_UT_startTiming) return false;
  8980. #endif
  8981. if (!obj->isKindOf(KINDOF_DOZER) && !obj->isKindOf(KINDOF_HARVESTER)) {
  8982. // Harvesters & dozers want a clear path.
  8983. if (!path->getBlockedByAlly()) return FALSE; // Only move units if it is required.
  8984. }
  8985. LatchRestore<Int> recursiveDepth(m_moveAlliesDepth, m_moveAlliesDepth+1);
  8986. if (m_moveAlliesDepth > 2) return false;
  8987. Bool centerInCell;
  8988. Int radius;
  8989. getRadiusAndCenter(obj, radius, centerInCell);
  8990. Int numCellsAbove = radius;
  8991. if (centerInCell) numCellsAbove++;
  8992. PathNode *node;
  8993. ObjectID ignoreId = INVALID_ID;
  8994. if (obj->getAIUpdateInterface()) {
  8995. ignoreId = obj->getAIUpdateInterface()->getIgnoredObstacleID();
  8996. }
  8997. for( node = path->getLastNode(); node && node != path->getFirstNode(); node = node->getPrevious() ) {
  8998. ICoord2D curCell;
  8999. worldToCell(node->getPosition(), &curCell);
  9000. Int i, j;
  9001. for (i=curCell.x-radius; i<curCell.x+numCellsAbove; i++) {
  9002. for (j=curCell.y-radius; j<curCell.y+numCellsAbove; j++) {
  9003. PathfindCell *cell = getCell(node->getLayer(), i, j);
  9004. if (cell) {
  9005. if (cell->getPosUnit()==INVALID_ID) {
  9006. continue;
  9007. }
  9008. if (cell->getPosUnit()==obj->getID()) {
  9009. continue; // It's us.
  9010. }
  9011. if (cell->getPosUnit()==ignoreId) {
  9012. continue; // It's the one we are ignoring.
  9013. }
  9014. Object *otherObj = TheGameLogic->findObjectByID(cell->getPosUnit());
  9015. if (obj->getRelationship(otherObj)!=ALLIES) {
  9016. continue; // Only move allies.
  9017. }
  9018. if (otherObj==NULL) continue;
  9019. if (obj->isKindOf(KINDOF_INFANTRY) && otherObj->isKindOf(KINDOF_INFANTRY)) {
  9020. continue; // infantry can walk through other infantry, so just let them.
  9021. }
  9022. if (obj->isKindOf(KINDOF_INFANTRY) && !otherObj->isKindOf(KINDOF_INFANTRY)) {
  9023. // If this is a general clear operation, don't let infantry push vehicles.
  9024. if (!path->getBlockedByAlly()) continue;
  9025. }
  9026. if( otherObj && otherObj->getAI() && !otherObj->getAI()->isMoving() )
  9027. {
  9028. if( otherObj->getAI()->isAttacking() )
  9029. {
  9030. continue; // Don't move units that are attacking. [8/14/2003]
  9031. }
  9032. //Kris: Patch 1.01 November 3, 2003
  9033. //Black Lotus exploit fix -- moving while hacking.
  9034. if( otherObj->testStatus( OBJECT_STATUS_IS_USING_ABILITY ) || otherObj->getAI()->isBusy() )
  9035. {
  9036. continue; // Packing or unpacking objects for example
  9037. }
  9038. //DEBUG_LOG(("Moving ally\n"));
  9039. otherObj->getAI()->aiMoveAwayFromUnit(obj, CMD_FROM_AI);
  9040. }
  9041. }
  9042. }
  9043. }
  9044. }
  9045. return true;
  9046. }
  9047. /**
  9048. * Moves an allied unit out of the path of another unit.
  9049. * Uses A* algorithm.
  9050. */
  9051. Path *Pathfinder::getMoveAwayFromPath(Object* obj, Object *otherObj,
  9052. Path *pathToAvoid, Object *otherObj2, Path *pathToAvoid2)
  9053. {
  9054. if (m_isMapReady == false) return false; // Should always be ok.
  9055. #if defined _DEBUG || defined _INTERNAL
  9056. Int startTimeMS = ::GetTickCount();
  9057. #endif
  9058. Bool isHuman = true;
  9059. if (obj && obj->getControllingPlayer() && (obj->getControllingPlayer()->getPlayerType()==PLAYER_COMPUTER)) {
  9060. isHuman = false; // computer gets to cheat.
  9061. }
  9062. Bool otherCenter;
  9063. Int otherRadius;
  9064. getRadiusAndCenter(otherObj, otherRadius, otherCenter);
  9065. Bool isCrusher = obj ? obj->getCrusherLevel() > 0 : false;
  9066. m_zoneManager.setAllPassable();
  9067. Bool centerInCell;
  9068. Int radius;
  9069. getRadiusAndCenter(obj, radius, centerInCell);
  9070. DEBUG_ASSERTCRASH(m_openList==NULL && m_closedList == NULL, ("Dangling lists."));
  9071. // determine start cell
  9072. ICoord2D startCellNdx;
  9073. Coord3D startPos = *obj->getPosition();
  9074. if (!centerInCell) {
  9075. startPos.x += PATHFIND_CELL_SIZE_F*0.5f;
  9076. startPos.x += PATHFIND_CELL_SIZE_F*0.5f;
  9077. }
  9078. worldToCell(&startPos, &startCellNdx);
  9079. PathfindCell *parentCell = getClippedCell( obj->getLayer(), obj->getPosition() );
  9080. if (parentCell == NULL)
  9081. return false;
  9082. if (!obj->getAIUpdateInterface()) {
  9083. return false; // shouldn't happen, but can't move it without an ai.
  9084. }
  9085. const LocomotorSet& locomotorSet = obj->getAIUpdateInterface()->getLocomotorSet();
  9086. m_isTunneling = false;
  9087. if (validMovementPosition( isCrusher, locomotorSet.getValidSurfaces(), parentCell ) == false) {
  9088. m_isTunneling = true; // We can't move from our current location. So relax the constraints.
  9089. }
  9090. TCheckMovementInfo info;
  9091. info.cell = startCellNdx;
  9092. info.layer = obj->getLayer();
  9093. info.centerInCell = centerInCell;
  9094. info.radius = radius;
  9095. info.considerTransient = false;
  9096. info.acceptableSurfaces = locomotorSet.getValidSurfaces();
  9097. if (!checkForMovement(obj, info) || info.enemyFixed) {
  9098. m_isTunneling = true; // We can't move from our current location. So relax the constraints.
  9099. }
  9100. if (!parentCell->allocateInfo(startCellNdx)) {
  9101. return false;
  9102. }
  9103. parentCell->startPathfind(NULL);
  9104. // initialize "open" list to contain start cell
  9105. m_openList = parentCell;
  9106. // "closed" list is initially empty
  9107. m_closedList = NULL;
  9108. //
  9109. // Continue search until "open" list is empty, or
  9110. // until goal is found.
  9111. //
  9112. Real boxHalfWidth = radius*PATHFIND_CELL_SIZE_F - (PATHFIND_CELL_SIZE_F/4.0f);
  9113. if (centerInCell) boxHalfWidth+=PATHFIND_CELL_SIZE_F/2;
  9114. boxHalfWidth += otherRadius*PATHFIND_CELL_SIZE_F;
  9115. if (otherCenter) boxHalfWidth+=PATHFIND_CELL_SIZE_F/2;
  9116. while( m_openList != NULL )
  9117. {
  9118. // take head cell off of open list - it has lowest estimated total path cost
  9119. parentCell = m_openList;
  9120. m_openList = parentCell->removeFromOpenList(m_openList);
  9121. Region2D bounds;
  9122. Coord3D cellCenter;
  9123. adjustCoordToCell(parentCell->getXIndex(), parentCell->getYIndex(), centerInCell, cellCenter, parentCell->getLayer());
  9124. bounds.lo.x = cellCenter.x-boxHalfWidth;
  9125. bounds.lo.y = cellCenter.y-boxHalfWidth;
  9126. bounds.hi.x = cellCenter.x+boxHalfWidth;
  9127. bounds.hi.y = cellCenter.y+boxHalfWidth;
  9128. PathNode *node;
  9129. Bool overlap = false;
  9130. if (obj) {
  9131. if (bounds.lo.x<obj->getPosition()->x && bounds.hi.x>obj->getPosition()->x &&
  9132. bounds.lo.y<obj->getPosition()->y && bounds.hi.y>obj->getPosition()->y) {
  9133. //overlap = true;
  9134. }
  9135. }
  9136. for( node = pathToAvoid->getFirstNode(); node && node->getNextOptimized(); node = node->getNextOptimized() ) {
  9137. Coord2D start, end;
  9138. start.x = node->getPosition()->x;
  9139. start.y = node->getPosition()->y;
  9140. end.x = node->getNextOptimized()->getPosition()->x;
  9141. end.y = node->getNextOptimized()->getPosition()->y;
  9142. if (LineInRegion(&start, &end, &bounds)) {
  9143. overlap = true;
  9144. break;
  9145. }
  9146. }
  9147. if (otherObj) {
  9148. if (bounds.lo.x<otherObj->getPosition()->x && bounds.hi.x>otherObj->getPosition()->x &&
  9149. bounds.lo.y<otherObj->getPosition()->y && bounds.hi.y>otherObj->getPosition()->y) {
  9150. //overlap = true;
  9151. }
  9152. }
  9153. if (!overlap && pathToAvoid2) {
  9154. for( node = pathToAvoid2->getFirstNode(); node && node->getNextOptimized(); node = node->getNextOptimized() ) {
  9155. Coord2D start, end;
  9156. start.x = node->getPosition()->x;
  9157. start.y = node->getPosition()->y;
  9158. end.x = node->getNextOptimized()->getPosition()->x;
  9159. end.y = node->getNextOptimized()->getPosition()->y;
  9160. if (LineInRegion(&start, &end, &bounds)) {
  9161. overlap = true;
  9162. break;
  9163. }
  9164. }
  9165. }
  9166. if (!overlap) {
  9167. if (startCellNdx.x == parentCell->getXIndex() && startCellNdx.y == parentCell->getYIndex()) {
  9168. // we didn't move. Always move at least 1 cell. jba.
  9169. overlap = true;
  9170. }
  9171. }
  9172. ///@todo - Adjust cost intersecting path - closer to front is more expensive. jba.
  9173. if (!overlap && checkDestination(obj, parentCell->getXIndex(), parentCell->getYIndex(),
  9174. parentCell->getLayer(), radius, centerInCell)) {
  9175. // success - found a path to the goal
  9176. if (false && TheGlobalData->m_debugAI)
  9177. debugShowSearch(true);
  9178. m_isTunneling = false;
  9179. // construct and return path
  9180. Path *newPath = buildActualPath( obj, locomotorSet.getValidSurfaces(), obj->getPosition(), parentCell, centerInCell, false);
  9181. parentCell->releaseInfo();
  9182. cleanOpenAndClosedLists();
  9183. return newPath;
  9184. }
  9185. // put parent cell onto closed list - its evaluation is finished
  9186. m_closedList = parentCell->putOnClosedList( m_closedList );
  9187. // Check to see if we can change layers in this cell.
  9188. checkChangeLayers(parentCell);
  9189. examineNeighboringCells(parentCell, NULL, locomotorSet, isHuman, centerInCell, radius, startCellNdx, obj, NO_ATTACK);
  9190. }
  9191. #if defined _DEBUG || defined _INTERNAL
  9192. debugShowSearch(true);
  9193. DEBUG_LOG(("%d ", TheGameLogic->getFrame()));
  9194. DEBUG_LOG(("getMoveAwayFromPath pathfind failed -- "));
  9195. DEBUG_LOG(("Unit '%s', time %f\n", obj->getTemplate()->getName().str(), (::GetTickCount()-startTimeMS)/1000.0f));
  9196. #endif
  9197. m_isTunneling = false;
  9198. cleanOpenAndClosedLists();
  9199. return NULL;
  9200. }
  9201. /** Patch to the exiting path from the current position, either because we became blocked,
  9202. or because we had to move off the path to avoid other units. */
  9203. Path *Pathfinder::patchPath( const Object *obj, const LocomotorSet& locomotorSet,
  9204. Path *originalPath, Bool blocked )
  9205. {
  9206. //CRCDEBUG_LOG(("Pathfinder::patchPath()\n"));
  9207. #if defined _DEBUG || defined _INTERNAL
  9208. Int startTimeMS = ::GetTickCount();
  9209. #endif
  9210. if (originalPath==NULL) return NULL;
  9211. Bool centerInCell;
  9212. Int radius;
  9213. getRadiusAndCenter(obj, radius, centerInCell);
  9214. Bool isHuman = true;
  9215. if (obj && obj->getControllingPlayer() && (obj->getControllingPlayer()->getPlayerType()==PLAYER_COMPUTER)) {
  9216. isHuman = false; // computer gets to cheat.
  9217. }
  9218. m_zoneManager.setAllPassable();
  9219. DEBUG_ASSERTCRASH(m_openList==NULL && m_closedList == NULL, ("Dangling lists."));
  9220. enum {CELL_LIMIT = 2000}; // max cells to examine.
  9221. Int cellCount = 0;
  9222. Coord3D currentPosition = *obj->getPosition();
  9223. // determine start cell
  9224. ICoord2D startCellNdx;
  9225. Coord3D startPos = *obj->getPosition();
  9226. if (!centerInCell) {
  9227. startPos.x += PATHFIND_CELL_SIZE_F*0.5f;
  9228. startPos.x += PATHFIND_CELL_SIZE_F*0.5f;
  9229. }
  9230. worldToCell(&startPos, &startCellNdx);
  9231. //worldToCell(obj->getPosition(), &startCellNdx);
  9232. PathfindCell *parentCell = getClippedCell( obj->getLayer(), &currentPosition);
  9233. if (parentCell == NULL)
  9234. return false;
  9235. if (!obj->getAIUpdateInterface()) {
  9236. return false; // shouldn't happen, but can't move it without an ai.
  9237. }
  9238. m_isTunneling = false;
  9239. if (!parentCell->allocateInfo(startCellNdx)) {
  9240. return false;
  9241. }
  9242. parentCell->startPathfind( NULL);
  9243. // initialize "open" list to contain start cell
  9244. m_openList = parentCell;
  9245. // "closed" list is initially empty
  9246. m_closedList = NULL;
  9247. //
  9248. // Continue search until "open" list is empty, or
  9249. // until goal is found.
  9250. //
  9251. #if defined _DEBUG || defined _INTERNAL
  9252. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  9253. if (TheGlobalData->m_debugAI)
  9254. {
  9255. RGBColor color;
  9256. color.setFromInt(0);
  9257. addIcon(NULL, 0,0,color);
  9258. }
  9259. #endif
  9260. PathNode *startNode;
  9261. Coord3D goalPos = *originalPath->getLastNode()->getPosition();
  9262. Real goalDeltaSqr = sqr(goalPos.x-currentPosition.x) + sqr(goalPos.y - currentPosition.y);
  9263. for( startNode = originalPath->getLastNode(); startNode != originalPath->getFirstNode(); startNode = startNode->getPrevious() ) {
  9264. ICoord2D cellCoord;
  9265. worldToCell(startNode->getPosition(), &cellCoord);
  9266. TCheckMovementInfo info;
  9267. info.cell = cellCoord;
  9268. info.layer = startNode->getLayer();
  9269. info.centerInCell = centerInCell;
  9270. info.radius = radius;
  9271. info.considerTransient = blocked;
  9272. info.acceptableSurfaces = locomotorSet.getValidSurfaces();
  9273. #if defined _DEBUG || defined _INTERNAL
  9274. if (TheGlobalData->m_debugAI) {
  9275. RGBColor color;
  9276. color.setFromInt(0);
  9277. color.green = 1;
  9278. addIcon(startNode->getPosition(), PATHFIND_CELL_SIZE_F*0.5f, 100, color);
  9279. }
  9280. #endif
  9281. Int dx = cellCoord.x-startCellNdx.x;
  9282. Int dy = cellCoord.y-startCellNdx.y;
  9283. if (dx<-2 || dx>2) info.considerTransient = false;
  9284. if (dy<-2 || dy>2) info.considerTransient = false;
  9285. if (!checkForMovement(obj, info)) {
  9286. break;
  9287. }
  9288. if (info.allyFixedCount || info.enemyFixed) {
  9289. break; // Don't patch through cells that are occupied.
  9290. }
  9291. Real curSqr = sqr(startNode->getPosition()->x-currentPosition.x) + sqr(startNode->getPosition()->y - currentPosition.y);
  9292. if (curSqr < goalDeltaSqr) {
  9293. goalPos = *startNode->getPosition();
  9294. goalDeltaSqr = curSqr;
  9295. }
  9296. }
  9297. if (startNode == originalPath->getLastNode()) {
  9298. cleanOpenAndClosedLists();
  9299. return NULL; // no open nodes.
  9300. }
  9301. PathfindCell *candidateGoal;
  9302. candidateGoal = getCell(LAYER_GROUND, &goalPos); // just using for cost estimates.
  9303. ICoord2D goalCellNdx;
  9304. worldToCell(&goalPos, &goalCellNdx);
  9305. if (!candidateGoal->allocateInfo(goalCellNdx)) {
  9306. return NULL;
  9307. }
  9308. while( m_openList != NULL )
  9309. {
  9310. // take head cell off of open list - it has lowest estimated total path cost
  9311. parentCell = m_openList;
  9312. m_openList = parentCell->removeFromOpenList(m_openList);
  9313. Coord3D cellCenter;
  9314. adjustCoordToCell(parentCell->getXIndex(), parentCell->getYIndex(), centerInCell, cellCenter, parentCell->getLayer());
  9315. PathNode *matchNode;
  9316. Bool found = false;
  9317. for( matchNode = originalPath->getLastNode(); matchNode != startNode; matchNode = matchNode->getPrevious() ) {
  9318. if (cellCenter.x == matchNode->getPosition()->x && cellCenter.y == matchNode->getPosition()->y) {
  9319. found = true;
  9320. break;
  9321. }
  9322. }
  9323. if (found ) {
  9324. // success - found a path to the goal
  9325. if ( TheGlobalData->m_debugAI)
  9326. debugShowSearch(true);
  9327. m_isTunneling = false;
  9328. // construct and return path
  9329. Path *path = newInstance(Path);
  9330. PathNode *node;
  9331. for( node = originalPath->getLastNode(); node != matchNode; node = node->getPrevious() ) {
  9332. path->prependNode(node->getPosition(), node->getLayer());
  9333. }
  9334. prependCells(path, obj->getPosition(), parentCell, centerInCell);
  9335. // cleanup the path by checking line of sight
  9336. path->optimize(obj, locomotorSet.getValidSurfaces(), blocked);
  9337. parentCell->releaseInfo();
  9338. cleanOpenAndClosedLists();
  9339. candidateGoal->releaseInfo();
  9340. return path;
  9341. }
  9342. // put parent cell onto closed list - its evaluation is finished
  9343. m_closedList = parentCell->putOnClosedList( m_closedList );
  9344. if (cellCount < CELL_LIMIT) {
  9345. // Check to see if we can change layers in this cell.
  9346. checkChangeLayers(parentCell);
  9347. cellCount += examineNeighboringCells(parentCell, NULL, locomotorSet, isHuman, centerInCell, radius, startCellNdx, obj, NO_ATTACK);
  9348. }
  9349. }
  9350. #if defined _DEBUG || defined _INTERNAL
  9351. DEBUG_LOG(("%d ", TheGameLogic->getFrame()));
  9352. DEBUG_LOG(("patchPath Pathfind failed -- "));
  9353. DEBUG_LOG(("Unit '%s', time %f\n", obj->getTemplate()->getName().str(), (::GetTickCount()-startTimeMS)/1000.0f));
  9354. if (TheGlobalData->m_debugAI) {
  9355. debugShowSearch(true);
  9356. }
  9357. #endif
  9358. m_isTunneling = false;
  9359. if (!candidateGoal->getOpen() && !candidateGoal->getClosed())
  9360. {
  9361. // Not on one of the lists
  9362. candidateGoal->releaseInfo();
  9363. }
  9364. cleanOpenAndClosedLists();
  9365. return false;
  9366. }
  9367. /** Find a short, valid path to a location that obj can attack victim from. */
  9368. Path *Pathfinder::findAttackPath( const Object *obj, const LocomotorSet& locomotorSet, const Coord3D *from,
  9369. const Object *victim, const Coord3D* victimPos, const Weapon *weapon )
  9370. {
  9371. /*
  9372. CRCDEBUG_LOG(("Pathfinder::findAttackPath() for object %d (%s)\n", obj->getID(), obj->getTemplate()->getName().str()));
  9373. XferCRC xferCRC;
  9374. xferCRC.open("lightCRC");
  9375. xferCRC.xferSnapshot((Object *)obj);
  9376. xferCRC.close();
  9377. CRCDEBUG_LOG(("obj CRC is %X\n", xferCRC.getCRC()));
  9378. if (from)
  9379. {
  9380. CRCDEBUG_LOG(("from: (%g,%g,%g) (%X,%X,%X)\n",
  9381. from->x, from->y, from->z,
  9382. AS_INT(from->x), AS_INT(from->y), AS_INT(from->z)));
  9383. }
  9384. if (victim)
  9385. {
  9386. CRCDEBUG_LOG(("victim is %d (%s)\n", victim->getID(), victim->getTemplate()->getName().str()));
  9387. XferCRC xferCRC;
  9388. xferCRC.open("lightCRC");
  9389. xferCRC.xferSnapshot((Object *)victim);
  9390. xferCRC.close();
  9391. CRCDEBUG_LOG(("victim CRC is %X\n", xferCRC.getCRC()));
  9392. }
  9393. if (victimPos)
  9394. {
  9395. CRCDEBUG_LOG(("from: (%g,%g,%g) (%X,%X,%X)\n",
  9396. victimPos->x, victimPos->y, victimPos->z,
  9397. AS_INT(victimPos->x), AS_INT(victimPos->y), AS_INT(victimPos->z)));
  9398. }
  9399. */
  9400. if (m_isMapReady == false) return false; // Should always be ok.
  9401. #if defined _DEBUG || defined _INTERNAL
  9402. // Int startTimeMS = ::GetTickCount();
  9403. #endif
  9404. Bool isCrusher = obj ? obj->getCrusherLevel() > 0 : false;
  9405. Int radius;
  9406. Bool centerInCell;
  9407. getRadiusAndCenter(obj, radius, centerInCell);
  9408. // Quick check: See if moving couple of cells towards the victim will work.
  9409. {
  9410. Coord3D curPos = *obj->getPosition();
  9411. Coord3D goalPos = victim?*victim->getPosition():*victimPos;
  9412. Coord3D delta;
  9413. delta.set(goalPos.x-curPos.x, goalPos.y-curPos.y, 0);
  9414. delta.normalize();
  9415. delta.x *= PATHFIND_CELL_SIZE_F;
  9416. delta.y *= PATHFIND_CELL_SIZE_F;
  9417. Int i;
  9418. for (i=1; i<10; i++) {
  9419. Coord3D testPos = curPos;
  9420. testPos.x += delta.x*i*0.5f;
  9421. testPos.y += delta.y*i*0.5f;
  9422. ICoord2D cellNdx;
  9423. worldToCell(&testPos, &cellNdx);
  9424. PathfindCell *aCell = getCell(obj->getLayer(), cellNdx.x, cellNdx.y);
  9425. if (aCell==NULL) break;
  9426. if (!validMovementPosition( isCrusher, locomotorSet.getValidSurfaces(), aCell )) {
  9427. break;
  9428. }
  9429. if (!checkDestination(obj, cellNdx.x, cellNdx.y, obj->getLayer(), radius, centerInCell)) {
  9430. break;
  9431. }
  9432. if (weapon->isGoalPosWithinAttackRange(obj, &testPos, victim, victimPos)) {
  9433. if (!isAttackViewBlockedByObstacle(obj, testPos, victim, *victimPos)) {
  9434. // return path.
  9435. Path *path = newInstance(Path);
  9436. path->prependNode( &testPos, obj->getLayer() );
  9437. path->prependNode( &curPos, obj->getLayer() );
  9438. path->getFirstNode()->setNextOptimized(path->getFirstNode()->getNext());
  9439. if (TheGlobalData->m_debugAI==AI_DEBUG_PATHS) {
  9440. setDebugPath(path);
  9441. }
  9442. return path;
  9443. }
  9444. }
  9445. }
  9446. }
  9447. const Int ATTACK_CELL_LIMIT = 2500; // this is a rather expensive operation, so limit the search.
  9448. Bool isHuman = true;
  9449. if (obj && obj->getControllingPlayer() && (obj->getControllingPlayer()->getPlayerType()==PLAYER_COMPUTER)) {
  9450. isHuman = false; // computer gets to cheat.
  9451. }
  9452. m_zoneManager.clearPassableFlags();
  9453. Path *hPat = findClosestHierarchicalPath(isHuman, locomotorSet, from, victimPos, isCrusher);
  9454. if (hPat) {
  9455. hPat->deleteInstance();
  9456. } else {
  9457. m_zoneManager.setAllPassable();
  9458. }
  9459. Int cellCount = 0;
  9460. DEBUG_ASSERTCRASH(m_openList==NULL && m_closedList == NULL, ("Dangling lists."));
  9461. Int attackDistance = weapon->getAttackDistance(obj, victim, victimPos);
  9462. attackDistance += 3*PATHFIND_CELL_SIZE;
  9463. // determine start cell
  9464. ICoord2D startCellNdx;
  9465. Coord3D objPos = *obj->getPosition();
  9466. // since worldtocell truncates, add.
  9467. if (centerInCell) {
  9468. objPos.x += PATHFIND_CELL_SIZE_F/2.0f;
  9469. objPos.y += PATHFIND_CELL_SIZE_F/2.0f;
  9470. }
  9471. worldToCell(&objPos, &startCellNdx);
  9472. PathfindCell *parentCell = getClippedCell( obj->getLayer(), &objPos );
  9473. if (parentCell == NULL)
  9474. return false;
  9475. if (!obj->getAIUpdateInterface()) {
  9476. return false; // shouldn't happen, but can't move it without an ai.
  9477. }
  9478. const PathfindCell *startCell = parentCell;
  9479. if (!parentCell->allocateInfo(startCellNdx)) {
  9480. return false;
  9481. }
  9482. parentCell->startPathfind(NULL);
  9483. // determine start cell
  9484. ICoord2D victimCellNdx;
  9485. worldToCell(victim ? victim->getPosition() : victimPos, &victimCellNdx);
  9486. // determine goal cell
  9487. PathfindCell *goalCell = getCell( LAYER_GROUND, victimCellNdx.x, victimCellNdx.y );
  9488. if (goalCell == NULL)
  9489. return NULL;
  9490. if (!goalCell->allocateInfo(victimCellNdx)) {
  9491. return false;
  9492. }
  9493. // initialize "open" list to contain start cell
  9494. m_openList = parentCell;
  9495. // "closed" list is initially empty
  9496. m_closedList = NULL;
  9497. //
  9498. // Continue search until "open" list is empty, or
  9499. // until goal is found.
  9500. //
  9501. PathfindCell *closestCell = NULL;
  9502. Real closestDistanceSqr = FLT_MAX;
  9503. Bool checkLOS = false;
  9504. if (!victim) {
  9505. checkLOS = true;
  9506. }
  9507. if (victim && !victim->isSignificantlyAboveTerrain()) {
  9508. checkLOS = true;
  9509. }
  9510. while( m_openList != NULL )
  9511. {
  9512. // take head cell off of open list - it has lowest estimated total path cost
  9513. parentCell = m_openList;
  9514. m_openList = parentCell->removeFromOpenList(m_openList);
  9515. Coord3D cellCenter;
  9516. adjustCoordToCell(parentCell->getXIndex(), parentCell->getYIndex(), centerInCell, cellCenter, parentCell->getLayer());
  9517. ///@todo - Adjust cost intersecting path - closer to front is more expensive. jba.
  9518. if (weapon->isGoalPosWithinAttackRange(obj, &cellCenter, victim, victimPos) &&
  9519. checkDestination(obj, parentCell->getXIndex(), parentCell->getYIndex(),
  9520. parentCell->getLayer(), radius, centerInCell)) {
  9521. // check line of sight.
  9522. Bool viewBlocked = false;
  9523. if (checkLOS)
  9524. {
  9525. viewBlocked = isAttackViewBlockedByObstacle(obj, cellCenter, victim, *victimPos);
  9526. }
  9527. if (startCell == parentCell) {
  9528. // We never want to accept our starting cell.
  9529. // If we could attack from there, we wouldn't be calling
  9530. // FindAttackPath. Usually happens cause the cell is valid for attack, but
  9531. // a point near the cell center isn't, and that happens to be where the
  9532. // attacker is standing, and it's too close to move to.
  9533. viewBlocked = true;
  9534. } else {
  9535. // If through some unfortunate rounding, we end up moving near ourselves,
  9536. // don't want it.
  9537. Coord3D cellPos;
  9538. adjustCoordToCell(parentCell->getXIndex(), parentCell->getYIndex(), centerInCell, cellPos, parentCell->getLayer());
  9539. Real dx = (cellPos.x - objPos.x);
  9540. Real dy = (cellPos.y - objPos.y);
  9541. if (sqr(dx) + sqr(dy) < sqr(PATHFIND_CELL_SIZE_F*0.5f)) {
  9542. viewBlocked = true;
  9543. }
  9544. }
  9545. if (!viewBlocked)
  9546. {
  9547. // success - found a path to the goal
  9548. Bool show = TheGlobalData->m_debugAI;
  9549. #ifdef INTENSE_DEBUG
  9550. Int count = 0;
  9551. PathfindCell *cur;
  9552. for (cur = m_closedList; cur; cur=cur->getNextOpen()) {
  9553. count++;
  9554. }
  9555. if (count>1000) {
  9556. show = true;
  9557. DEBUG_LOG(("FAP cells %d obj %s %x\n", count, obj->getTemplate()->getName().str(), obj));
  9558. #ifdef STATE_MACHINE_DEBUG
  9559. if( obj->getAIUpdateInterface() )
  9560. {
  9561. DEBUG_LOG(("State %s\n", obj->getAIUpdateInterface()->getCurrentStateName().str()));
  9562. }
  9563. #endif
  9564. TheScriptEngine->AppendDebugMessage("Big Attack path", false);
  9565. }
  9566. #endif
  9567. if (show)
  9568. debugShowSearch(true);
  9569. #if defined _DEBUG || defined _INTERNAL
  9570. //DEBUG_LOG(("Attack path took %d cells, %f sec\n", cellCount, (::GetTickCount()-startTimeMS)/1000.0f));
  9571. #endif
  9572. // put parent cell onto closed list - its evaluation is finished
  9573. m_closedList = parentCell->putOnClosedList( m_closedList );
  9574. // construct and return path
  9575. if (obj->isKindOf(KINDOF_VEHICLE)) {
  9576. // Strip backwards.
  9577. PathfindCell *lastBlocked = NULL;
  9578. PathfindCell *cur = parentCell;
  9579. Bool useLargeRadius = false;
  9580. Int cellLimit = 12; // Magic number, yes I know - jba. It is about 4 * size of an average vehicle width (3 cells) [8/15/2003]
  9581. while (cur) {
  9582. cellLimit--;
  9583. if (cellLimit<0) {
  9584. break;
  9585. }
  9586. TCheckMovementInfo info;
  9587. info.cell.x = cur->getXIndex();
  9588. info.cell.y = cur->getYIndex();
  9589. info.layer = cur->getLayer();
  9590. if (useLargeRadius) {
  9591. info.centerInCell = centerInCell;
  9592. info.radius = radius;
  9593. } else {
  9594. info.centerInCell = true;
  9595. info.radius = 0;
  9596. }
  9597. info.considerTransient = false;
  9598. info.acceptableSurfaces = locomotorSet.getValidSurfaces();
  9599. PathfindCell *cell = getCell(info.layer,info.cell.x,info.cell.y);
  9600. Bool unitIdle = false;
  9601. if (cell) {
  9602. ObjectID posUnit = cell->getPosUnit();
  9603. Object *unit = TheGameLogic->findObjectByID(posUnit);
  9604. if (unit && unit->getAI() && unit->getAI()->isIdle()) {
  9605. unitIdle = true;
  9606. }
  9607. }
  9608. Bool checkMovement = checkForMovement(obj, info);
  9609. Bool blockedByEnemy = info.enemyFixed;
  9610. Bool blockedByAllies = info.allyFixedCount || info.allyGoal;
  9611. if (unitIdle) {
  9612. // If the unit present is idle, it doesn't block allies. [8/18/2003]
  9613. blockedByAllies = false;
  9614. }
  9615. if (!checkMovement || blockedByEnemy || blockedByAllies) {
  9616. lastBlocked = cur;
  9617. useLargeRadius = true;
  9618. } else {
  9619. useLargeRadius = false;
  9620. }
  9621. cur = cur->getParentCell();
  9622. }
  9623. if (lastBlocked) {
  9624. parentCell = lastBlocked;
  9625. if (lastBlocked->getParentCell()) {
  9626. parentCell = lastBlocked->getParentCell();
  9627. }
  9628. }
  9629. }
  9630. Path *path = buildActualPath( obj, locomotorSet.getValidSurfaces(), obj->getPosition(), parentCell, centerInCell, false);
  9631. if (goalCell->hasInfo() && !goalCell->getClosed() && !goalCell->getOpen()) {
  9632. goalCell->releaseInfo();
  9633. }
  9634. cleanOpenAndClosedLists();
  9635. return path;
  9636. }
  9637. }
  9638. if (checkDestination(obj, parentCell->getXIndex(), parentCell->getYIndex(),
  9639. parentCell->getLayer(), radius, centerInCell)) {
  9640. if (validMovementPosition( isCrusher, locomotorSet.getValidSurfaces(), parentCell )) {
  9641. Real dx = IABS(victimCellNdx.x-parentCell->getXIndex());
  9642. Real dy = IABS(victimCellNdx.y-parentCell->getYIndex());
  9643. Real distSqr = dx*dx+dy*dy;
  9644. if (distSqr < closestDistanceSqr) {
  9645. closestCell = parentCell;
  9646. closestDistanceSqr = distSqr;
  9647. }
  9648. }
  9649. }
  9650. // put parent cell onto closed list - its evaluation is finished
  9651. m_closedList = parentCell->putOnClosedList( m_closedList );
  9652. if (cellCount < ATTACK_CELL_LIMIT) {
  9653. // Check to see if we can change layers in this cell.
  9654. checkChangeLayers(parentCell);
  9655. cellCount += examineNeighboringCells(parentCell, goalCell, locomotorSet, isHuman, centerInCell,
  9656. radius, startCellNdx, obj, attackDistance);
  9657. }
  9658. }
  9659. #ifdef INTENSE_DEBUG
  9660. DEBUG_LOG(("obj %s %x\n", obj->getTemplate()->getName().str(), obj));
  9661. #ifdef STATE_MACHINE_DEBUG
  9662. if( obj->getAIUpdateInterface() )
  9663. {
  9664. DEBUG_LOG(("State %s\n", obj->getAIUpdateInterface()->getCurrentStateName().str()));
  9665. }
  9666. #endif
  9667. debugShowSearch(true);
  9668. TheScriptEngine->AppendDebugMessage("Overflowed attack path", false);
  9669. #endif
  9670. #if 0
  9671. if (closestCell) {
  9672. // construct and return path
  9673. Path *path = buildActualPath( obj, locomotorSet, obj->getPosition(), closestCell, centerInCell, false);
  9674. cleanOpenAndClosedLists();
  9675. return path;
  9676. }
  9677. #if defined _DEBUG || defined _INTERNAL
  9678. DEBUG_LOG(("%d (%d cells)", TheGameLogic->getFrame(), cellCount));
  9679. DEBUG_LOG(("Attack Pathfind failed from (%f,%f) to (%f,%f) -- \n", from->x, from->y, victim->getPosition()->x, victim->getPosition()->y));
  9680. DEBUG_LOG(("Unit '%s', attacking '%s' time %f\n", obj->getTemplate()->getName().str(), victim->getTemplate()->getName().str(), (::GetTickCount()-startTimeMS)/1000.0f));
  9681. #endif
  9682. #endif
  9683. #ifdef DUMP_PERF_STATS
  9684. TheGameLogic->incrementOverallFailedPathfinds();
  9685. #endif
  9686. m_isTunneling = false;
  9687. if (goalCell->hasInfo() && !goalCell->getClosed() && !goalCell->getOpen()) {
  9688. goalCell->releaseInfo();
  9689. }
  9690. cleanOpenAndClosedLists();
  9691. return false;
  9692. }
  9693. /** Find a short, valid path to a location that is safe from the repulsors. */
  9694. Path *Pathfinder::findSafePath( const Object *obj, const LocomotorSet& locomotorSet,
  9695. const Coord3D *from, const Coord3D* repulsorPos1, const Coord3D* repulsorPos2, Real repulsorRadius)
  9696. {
  9697. //CRCDEBUG_LOG(("Pathfinder::findSafePath()\n"));
  9698. if (m_isMapReady == false) return false; // Should always be ok.
  9699. #if defined _DEBUG || defined _INTERNAL
  9700. // Int startTimeMS = ::GetTickCount();
  9701. #endif
  9702. const Int MAX_CELLS = 2000; // this is a rather expensive operation, so limit the search.
  9703. Bool centerInCell;
  9704. Int radius;
  9705. getRadiusAndCenter(obj, radius, centerInCell);
  9706. Real repulsorDistSqr = repulsorRadius*repulsorRadius;
  9707. Int cellCount = 0;
  9708. Bool isHuman = true;
  9709. if (obj && obj->getControllingPlayer() && (obj->getControllingPlayer()->getPlayerType()==PLAYER_COMPUTER)) {
  9710. isHuman = false; // computer gets to cheat.
  9711. }
  9712. DEBUG_ASSERTCRASH(m_openList==NULL && m_closedList == NULL, ("Dangling lists."));
  9713. // create unique "mark" values for open and closed cells for this pathfind invocation
  9714. m_zoneManager.setAllPassable();
  9715. // determine start cell
  9716. ICoord2D startCellNdx;
  9717. worldToCell(obj->getPosition(), &startCellNdx);
  9718. PathfindCell *parentCell = getClippedCell( obj->getLayer(), obj->getPosition() );
  9719. if (parentCell == NULL)
  9720. return false;
  9721. if (!obj->getAIUpdateInterface()) {
  9722. return false; // shouldn't happen, but can't move it without an ai.
  9723. }
  9724. if (!parentCell->allocateInfo(startCellNdx)) {
  9725. return false;
  9726. }
  9727. parentCell->startPathfind( NULL);
  9728. // initialize "open" list to contain start cell
  9729. m_openList = parentCell;
  9730. // "closed" list is initially empty
  9731. m_closedList = NULL;
  9732. //
  9733. // Continue search until "open" list is empty, or
  9734. // until goal is found.
  9735. //
  9736. Real farthestDistanceSqr = 0;
  9737. while( m_openList != NULL )
  9738. {
  9739. // take head cell off of open list - it has lowest estimated total path cost
  9740. parentCell = m_openList;
  9741. m_openList = parentCell->removeFromOpenList(m_openList);
  9742. Coord3D cellCenter;
  9743. adjustCoordToCell(parentCell->getXIndex(), parentCell->getYIndex(), centerInCell, cellCenter, parentCell->getLayer());
  9744. ///@todo - Adjust cost intersecting path - closer to front is more expensive. jba.
  9745. Real dx = cellCenter.x-repulsorPos1->x;
  9746. Real dy = cellCenter.y-repulsorPos1->y;
  9747. Bool ok = false;
  9748. Real distSqr = dx*dx+dy*dy;
  9749. dx = cellCenter.x-repulsorPos2->x;
  9750. dy = cellCenter.y-repulsorPos2->y;
  9751. Real distSqr2 = dx*dx+dy*dy;
  9752. if (distSqr2<distSqr) {
  9753. distSqr = distSqr2;
  9754. }
  9755. if (distSqr>repulsorDistSqr) {
  9756. ok = true;
  9757. }
  9758. if (m_openList == NULL && cellCount>0) {
  9759. ok = true; // exhausted the search space, just take the last cell.
  9760. }
  9761. if (distSqr > farthestDistanceSqr) {
  9762. farthestDistanceSqr = distSqr;
  9763. if (cellCount > MAX_CELLS) {
  9764. #ifdef INTENSE_DEBUG
  9765. DEBUG_LOG(("Took intermediate path, dist %f, goal dist %f\n", sqrt(farthestDistanceSqr), repulsorRadius));
  9766. #endif
  9767. ok = true; // Already a big search, just take this one.
  9768. }
  9769. }
  9770. if ( ok &&
  9771. checkDestination(obj, parentCell->getXIndex(), parentCell->getYIndex(),
  9772. parentCell->getLayer(), radius, centerInCell)) {
  9773. // success - found a path to the goal
  9774. Bool show = TheGlobalData->m_debugAI;
  9775. #ifdef INTENSE_DEBUG
  9776. Int count = 0;
  9777. PathfindCell *cur;
  9778. for (cur = m_closedList; cur; cur=cur->getNextOpen()) {
  9779. count++;
  9780. }
  9781. if (count>2000) {
  9782. show = true;
  9783. DEBUG_LOG(("cells %d obj %s %x\n", count, obj->getTemplate()->getName().str(), obj));
  9784. #ifdef STATE_MACHINE_DEBUG
  9785. if( obj->getAIUpdateInterface() )
  9786. {
  9787. DEBUG_LOG(("State %s\n", obj->getAIUpdateInterface()->getCurrentStateName().str()));
  9788. }
  9789. #endif
  9790. TheScriptEngine->AppendDebugMessage("Big Safe path", false);
  9791. }
  9792. #endif
  9793. if (show)
  9794. debugShowSearch(true);
  9795. #if defined _DEBUG || defined _INTERNAL
  9796. //DEBUG_LOG(("Attack path took %d cells, %f sec\n", cellCount, (::GetTickCount()-startTimeMS)/1000.0f));
  9797. #endif
  9798. // construct and return path
  9799. Path *path = buildActualPath( obj, locomotorSet.getValidSurfaces(), obj->getPosition(), parentCell, centerInCell, false);
  9800. parentCell->releaseInfo();
  9801. cleanOpenAndClosedLists();
  9802. return path;
  9803. }
  9804. // put parent cell onto closed list - its evaluation is finished
  9805. m_closedList = parentCell->putOnClosedList( m_closedList );
  9806. // Check to see if we can change layers in this cell.
  9807. checkChangeLayers(parentCell);
  9808. cellCount += examineNeighboringCells(parentCell, NULL, locomotorSet, isHuman, centerInCell, radius, startCellNdx, obj, NO_ATTACK);
  9809. }
  9810. #ifdef INTENSE_DEBUG
  9811. DEBUG_LOG(("obj %s %x count %d\n", obj->getTemplate()->getName().str(), obj, cellCount));
  9812. #ifdef STATE_MACHINE_DEBUG
  9813. if( obj->getAIUpdateInterface() )
  9814. {
  9815. DEBUG_LOG(("State %s\n", obj->getAIUpdateInterface()->getCurrentStateName().str()));
  9816. }
  9817. #endif
  9818. TheScriptEngine->AppendDebugMessage("Overflowed Safe path", false);
  9819. #endif
  9820. #if 0
  9821. #if defined _DEBUG || defined _INTERNAL
  9822. DEBUG_LOG(("%d (%d cells)", TheGameLogic->getFrame(), cellCount));
  9823. DEBUG_LOG(("Attack Pathfind failed from (%f,%f) to (%f,%f) -- \n", from->x, from->y, victim->getPosition()->x, victim->getPosition()->y));
  9824. DEBUG_LOG(("Unit '%s', attacking '%s' time %f\n", obj->getTemplate()->getName().str(), victim->getTemplate()->getName().str(), (::GetTickCount()-startTimeMS)/1000.0f));
  9825. #endif
  9826. #endif
  9827. #ifdef DUMP_PERF_STATS
  9828. TheGameLogic->incrementOverallFailedPathfinds();
  9829. #endif
  9830. m_isTunneling = false;
  9831. cleanOpenAndClosedLists();
  9832. return false;
  9833. }
  9834. //-----------------------------------------------------------------------------
  9835. void Pathfinder::crc( Xfer *xfer )
  9836. {
  9837. CRCDEBUG_LOG(("Pathfinder::crc() on frame %d\n", TheGameLogic->getFrame()));
  9838. CRCDEBUG_LOG(("beginning CRC: %8.8X\n", ((XferCRC *)xfer)->getCRC()));
  9839. xfer->xferUser( &m_extent, sizeof(IRegion2D) );
  9840. CRCDEBUG_LOG(("m_extent: %8.8X\n", ((XferCRC *)xfer)->getCRC()));
  9841. xfer->xferBool( &m_isMapReady );
  9842. CRCDEBUG_LOG(("m_isMapReady: %8.8X\n", ((XferCRC *)xfer)->getCRC()));
  9843. xfer->xferBool( &m_isTunneling );
  9844. CRCDEBUG_LOG(("m_isTunneling: %8.8X\n", ((XferCRC *)xfer)->getCRC()));
  9845. Int obsolete1 = 0;
  9846. xfer->xferInt( &obsolete1 );
  9847. xfer->xferUser(&m_ignoreObstacleID, sizeof(ObjectID));
  9848. CRCDEBUG_LOG(("m_ignoreObstacleID: %8.8X\n", ((XferCRC *)xfer)->getCRC()));
  9849. xfer->xferUser(m_queuedPathfindRequests, sizeof(ObjectID)*PATHFIND_QUEUE_LEN);
  9850. CRCDEBUG_LOG(("m_queuedPathfindRequests: %8.8X\n", ((XferCRC *)xfer)->getCRC()));
  9851. xfer->xferInt(&m_queuePRHead);
  9852. CRCDEBUG_LOG(("m_queuePRHead: %8.8X\n", ((XferCRC *)xfer)->getCRC()));
  9853. xfer->xferInt(&m_queuePRTail);
  9854. CRCDEBUG_LOG(("m_queuePRTail: %8.8X\n", ((XferCRC *)xfer)->getCRC()));
  9855. xfer->xferInt(&m_numWallPieces);
  9856. CRCDEBUG_LOG(("m_numWallPieces: %8.8X\n", ((XferCRC *)xfer)->getCRC()));
  9857. for (Int i=0; i<MAX_WALL_PIECES; ++i)
  9858. {
  9859. xfer->xferObjectID(&m_wallPieces[MAX_WALL_PIECES]);
  9860. }
  9861. CRCDEBUG_LOG(("m_wallPieces: %8.8X\n", ((XferCRC *)xfer)->getCRC()));
  9862. xfer->xferReal(&m_wallHeight);
  9863. CRCDEBUG_LOG(("m_wallHeight: %8.8X\n", ((XferCRC *)xfer)->getCRC()));
  9864. xfer->xferInt(&m_cumulativeCellsAllocated);
  9865. CRCDEBUG_LOG(("m_cumulativeCellsAllocated: %8.8X\n", ((XferCRC *)xfer)->getCRC()));
  9866. } // end crc
  9867. //-----------------------------------------------------------------------------
  9868. void Pathfinder::xfer( Xfer *xfer )
  9869. {
  9870. // version
  9871. XferVersion currentVersion = 1;
  9872. XferVersion version = currentVersion;
  9873. xfer->xferVersion( &version, currentVersion );
  9874. } // end xfer
  9875. //-----------------------------------------------------------------------------
  9876. void Pathfinder::loadPostProcess( void )
  9877. {
  9878. } // end loadPostProcess