AIPathfind.cpp 306 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272327332743275327632773278327932803281328232833284328532863287328832893290329132923293329432953296329732983299330033013302330333043305330633073308330933103311331233133314331533163317331833193320332133223323332433253326332733283329333033313332333333343335333633373338333933403341334233433344334533463347334833493350335133523353335433553356335733583359336033613362336333643365336633673368336933703371337233733374337533763377337833793380338133823383338433853386338733883389339033913392339333943395339633973398339934003401340234033404340534063407340834093410341134123413341434153416341734183419342034213422342334243425342634273428342934303431343234333434343534363437343834393440344134423443344434453446344734483449345034513452345334543455345634573458345934603461346234633464346534663467346834693470347134723473347434753476347734783479348034813482348334843485348634873488348934903491349234933494349534963497349834993500350135023503350435053506350735083509351035113512351335143515351635173518351935203521352235233524352535263527352835293530353135323533353435353536353735383539354035413542354335443545354635473548354935503551355235533554355535563557355835593560356135623563356435653566356735683569357035713572357335743575357635773578357935803581358235833584358535863587358835893590359135923593359435953596359735983599360036013602360336043605360636073608360936103611361236133614361536163617361836193620362136223623362436253626362736283629363036313632363336343635363636373638363936403641364236433644364536463647364836493650365136523653365436553656365736583659366036613662366336643665366636673668366936703671367236733674367536763677367836793680368136823683368436853686368736883689369036913692369336943695369636973698369937003701370237033704370537063707370837093710371137123713371437153716371737183719372037213722372337243725372637273728372937303731373237333734373537363737373837393740374137423743374437453746374737483749375037513752375337543755375637573758375937603761376237633764376537663767376837693770377137723773377437753776377737783779378037813782378337843785378637873788378937903791379237933794379537963797379837993800380138023803380438053806380738083809381038113812381338143815381638173818381938203821382238233824382538263827382838293830383138323833383438353836383738383839384038413842384338443845384638473848384938503851385238533854385538563857385838593860386138623863386438653866386738683869387038713872387338743875387638773878387938803881388238833884388538863887388838893890389138923893389438953896389738983899390039013902390339043905390639073908390939103911391239133914391539163917391839193920392139223923392439253926392739283929393039313932393339343935393639373938393939403941394239433944394539463947394839493950395139523953395439553956395739583959396039613962396339643965396639673968396939703971397239733974397539763977397839793980398139823983398439853986398739883989399039913992399339943995399639973998399940004001400240034004400540064007400840094010401140124013401440154016401740184019402040214022402340244025402640274028402940304031403240334034403540364037403840394040404140424043404440454046404740484049405040514052405340544055405640574058405940604061406240634064406540664067406840694070407140724073407440754076407740784079408040814082408340844085408640874088408940904091409240934094409540964097409840994100410141024103410441054106410741084109411041114112411341144115411641174118411941204121412241234124412541264127412841294130413141324133413441354136413741384139414041414142414341444145414641474148414941504151415241534154415541564157415841594160416141624163416441654166416741684169417041714172417341744175417641774178417941804181418241834184418541864187418841894190419141924193419441954196419741984199420042014202420342044205420642074208420942104211421242134214421542164217421842194220422142224223422442254226422742284229423042314232423342344235423642374238423942404241424242434244424542464247424842494250425142524253425442554256425742584259426042614262426342644265426642674268426942704271427242734274427542764277427842794280428142824283428442854286428742884289429042914292429342944295429642974298429943004301430243034304430543064307430843094310431143124313431443154316431743184319432043214322432343244325432643274328432943304331433243334334433543364337433843394340434143424343434443454346434743484349435043514352435343544355435643574358435943604361436243634364436543664367436843694370437143724373437443754376437743784379438043814382438343844385438643874388438943904391439243934394439543964397439843994400440144024403440444054406440744084409441044114412441344144415441644174418441944204421442244234424442544264427442844294430443144324433443444354436443744384439444044414442444344444445444644474448444944504451445244534454445544564457445844594460446144624463446444654466446744684469447044714472447344744475447644774478447944804481448244834484448544864487448844894490449144924493449444954496449744984499450045014502450345044505450645074508450945104511451245134514451545164517451845194520452145224523452445254526452745284529453045314532453345344535453645374538453945404541454245434544454545464547454845494550455145524553455445554556455745584559456045614562456345644565456645674568456945704571457245734574457545764577457845794580458145824583458445854586458745884589459045914592459345944595459645974598459946004601460246034604460546064607460846094610461146124613461446154616461746184619462046214622462346244625462646274628462946304631463246334634463546364637463846394640464146424643464446454646464746484649465046514652465346544655465646574658465946604661466246634664466546664667466846694670467146724673467446754676467746784679468046814682468346844685468646874688468946904691469246934694469546964697469846994700470147024703470447054706470747084709471047114712471347144715471647174718471947204721472247234724472547264727472847294730473147324733473447354736473747384739474047414742474347444745474647474748474947504751475247534754475547564757475847594760476147624763476447654766476747684769477047714772477347744775477647774778477947804781478247834784478547864787478847894790479147924793479447954796479747984799480048014802480348044805480648074808480948104811481248134814481548164817481848194820482148224823482448254826482748284829483048314832483348344835483648374838483948404841484248434844484548464847484848494850485148524853485448554856485748584859486048614862486348644865486648674868486948704871487248734874487548764877487848794880488148824883488448854886488748884889489048914892489348944895489648974898489949004901490249034904490549064907490849094910491149124913491449154916491749184919492049214922492349244925492649274928492949304931493249334934493549364937493849394940494149424943494449454946494749484949495049514952495349544955495649574958495949604961496249634964496549664967496849694970497149724973497449754976497749784979498049814982498349844985498649874988498949904991499249934994499549964997499849995000500150025003500450055006500750085009501050115012501350145015501650175018501950205021502250235024502550265027502850295030503150325033503450355036503750385039504050415042504350445045504650475048504950505051505250535054505550565057505850595060506150625063506450655066506750685069507050715072507350745075507650775078507950805081508250835084508550865087508850895090509150925093509450955096509750985099510051015102510351045105510651075108510951105111511251135114511551165117511851195120512151225123512451255126512751285129513051315132513351345135513651375138513951405141514251435144514551465147514851495150515151525153515451555156515751585159516051615162516351645165516651675168516951705171517251735174517551765177517851795180518151825183518451855186518751885189519051915192519351945195519651975198519952005201520252035204520552065207520852095210521152125213521452155216521752185219522052215222522352245225522652275228522952305231523252335234523552365237523852395240524152425243524452455246524752485249525052515252525352545255525652575258525952605261526252635264526552665267526852695270527152725273527452755276527752785279528052815282528352845285528652875288528952905291529252935294529552965297529852995300530153025303530453055306530753085309531053115312531353145315531653175318531953205321532253235324532553265327532853295330533153325333533453355336533753385339534053415342534353445345534653475348534953505351535253535354535553565357535853595360536153625363536453655366536753685369537053715372537353745375537653775378537953805381538253835384538553865387538853895390539153925393539453955396539753985399540054015402540354045405540654075408540954105411541254135414541554165417541854195420542154225423542454255426542754285429543054315432543354345435543654375438543954405441544254435444544554465447544854495450545154525453545454555456545754585459546054615462546354645465546654675468546954705471547254735474547554765477547854795480548154825483548454855486548754885489549054915492549354945495549654975498549955005501550255035504550555065507550855095510551155125513551455155516551755185519552055215522552355245525552655275528552955305531553255335534553555365537553855395540554155425543554455455546554755485549555055515552555355545555555655575558555955605561556255635564556555665567556855695570557155725573557455755576557755785579558055815582558355845585558655875588558955905591559255935594559555965597559855995600560156025603560456055606560756085609561056115612561356145615561656175618561956205621562256235624562556265627562856295630563156325633563456355636563756385639564056415642564356445645564656475648564956505651565256535654565556565657565856595660566156625663566456655666566756685669567056715672567356745675567656775678567956805681568256835684568556865687568856895690569156925693569456955696569756985699570057015702570357045705570657075708570957105711571257135714571557165717571857195720572157225723572457255726572757285729573057315732573357345735573657375738573957405741574257435744574557465747574857495750575157525753575457555756575757585759576057615762576357645765576657675768576957705771577257735774577557765777577857795780578157825783578457855786578757885789579057915792579357945795579657975798579958005801580258035804580558065807580858095810581158125813581458155816581758185819582058215822582358245825582658275828582958305831583258335834583558365837583858395840584158425843584458455846584758485849585058515852585358545855585658575858585958605861586258635864586558665867586858695870587158725873587458755876587758785879588058815882588358845885588658875888588958905891589258935894589558965897589858995900590159025903590459055906590759085909591059115912591359145915591659175918591959205921592259235924592559265927592859295930593159325933593459355936593759385939594059415942594359445945594659475948594959505951595259535954595559565957595859595960596159625963596459655966596759685969597059715972597359745975597659775978597959805981598259835984598559865987598859895990599159925993599459955996599759985999600060016002600360046005600660076008600960106011601260136014601560166017601860196020602160226023602460256026602760286029603060316032603360346035603660376038603960406041604260436044604560466047604860496050605160526053605460556056605760586059606060616062606360646065606660676068606960706071607260736074607560766077607860796080608160826083608460856086608760886089609060916092609360946095609660976098609961006101610261036104610561066107610861096110611161126113611461156116611761186119612061216122612361246125612661276128612961306131613261336134613561366137613861396140614161426143614461456146614761486149615061516152615361546155615661576158615961606161616261636164616561666167616861696170617161726173617461756176617761786179618061816182618361846185618661876188618961906191619261936194619561966197619861996200620162026203620462056206620762086209621062116212621362146215621662176218621962206221622262236224622562266227622862296230623162326233623462356236623762386239624062416242624362446245624662476248624962506251625262536254625562566257625862596260626162626263626462656266626762686269627062716272627362746275627662776278627962806281628262836284628562866287628862896290629162926293629462956296629762986299630063016302630363046305630663076308630963106311631263136314631563166317631863196320632163226323632463256326632763286329633063316332633363346335633663376338633963406341634263436344634563466347634863496350635163526353635463556356635763586359636063616362636363646365636663676368636963706371637263736374637563766377637863796380638163826383638463856386638763886389639063916392639363946395639663976398639964006401640264036404640564066407640864096410641164126413641464156416641764186419642064216422642364246425642664276428642964306431643264336434643564366437643864396440644164426443644464456446644764486449645064516452645364546455645664576458645964606461646264636464646564666467646864696470647164726473647464756476647764786479648064816482648364846485648664876488648964906491649264936494649564966497649864996500650165026503650465056506650765086509651065116512651365146515651665176518651965206521652265236524652565266527652865296530653165326533653465356536653765386539654065416542654365446545654665476548654965506551655265536554655565566557655865596560656165626563656465656566656765686569657065716572657365746575657665776578657965806581658265836584658565866587658865896590659165926593659465956596659765986599660066016602660366046605660666076608660966106611661266136614661566166617661866196620662166226623662466256626662766286629663066316632663366346635663666376638663966406641664266436644664566466647664866496650665166526653665466556656665766586659666066616662666366646665666666676668666966706671667266736674667566766677667866796680668166826683668466856686668766886689669066916692669366946695669666976698669967006701670267036704670567066707670867096710671167126713671467156716671767186719672067216722672367246725672667276728672967306731673267336734673567366737673867396740674167426743674467456746674767486749675067516752675367546755675667576758675967606761676267636764676567666767676867696770677167726773677467756776677767786779678067816782678367846785678667876788678967906791679267936794679567966797679867996800680168026803680468056806680768086809681068116812681368146815681668176818681968206821682268236824682568266827682868296830683168326833683468356836683768386839684068416842684368446845684668476848684968506851685268536854685568566857685868596860686168626863686468656866686768686869687068716872687368746875687668776878687968806881688268836884688568866887688868896890689168926893689468956896689768986899690069016902690369046905690669076908690969106911691269136914691569166917691869196920692169226923692469256926692769286929693069316932693369346935693669376938693969406941694269436944694569466947694869496950695169526953695469556956695769586959696069616962696369646965696669676968696969706971697269736974697569766977697869796980698169826983698469856986698769886989699069916992699369946995699669976998699970007001700270037004700570067007700870097010701170127013701470157016701770187019702070217022702370247025702670277028702970307031703270337034703570367037703870397040704170427043704470457046704770487049705070517052705370547055705670577058705970607061706270637064706570667067706870697070707170727073707470757076707770787079708070817082708370847085708670877088708970907091709270937094709570967097709870997100710171027103710471057106710771087109711071117112711371147115711671177118711971207121712271237124712571267127712871297130713171327133713471357136713771387139714071417142714371447145714671477148714971507151715271537154715571567157715871597160716171627163716471657166716771687169717071717172717371747175717671777178717971807181718271837184718571867187718871897190719171927193719471957196719771987199720072017202720372047205720672077208720972107211721272137214721572167217721872197220722172227223722472257226722772287229723072317232723372347235723672377238723972407241724272437244724572467247724872497250725172527253725472557256725772587259726072617262726372647265726672677268726972707271727272737274727572767277727872797280728172827283728472857286728772887289729072917292729372947295729672977298729973007301730273037304730573067307730873097310731173127313731473157316731773187319732073217322732373247325732673277328732973307331733273337334733573367337733873397340734173427343734473457346734773487349735073517352735373547355735673577358735973607361736273637364736573667367736873697370737173727373737473757376737773787379738073817382738373847385738673877388738973907391739273937394739573967397739873997400740174027403740474057406740774087409741074117412741374147415741674177418741974207421742274237424742574267427742874297430743174327433743474357436743774387439744074417442744374447445744674477448744974507451745274537454745574567457745874597460746174627463746474657466746774687469747074717472747374747475747674777478747974807481748274837484748574867487748874897490749174927493749474957496749774987499750075017502750375047505750675077508750975107511751275137514751575167517751875197520752175227523752475257526752775287529753075317532753375347535753675377538753975407541754275437544754575467547754875497550755175527553755475557556755775587559756075617562756375647565756675677568756975707571757275737574757575767577757875797580758175827583758475857586758775887589759075917592759375947595759675977598759976007601760276037604760576067607760876097610761176127613761476157616761776187619762076217622762376247625762676277628762976307631763276337634763576367637763876397640764176427643764476457646764776487649765076517652765376547655765676577658765976607661766276637664766576667667766876697670767176727673767476757676767776787679768076817682768376847685768676877688768976907691769276937694769576967697769876997700770177027703770477057706770777087709771077117712771377147715771677177718771977207721772277237724772577267727772877297730773177327733773477357736773777387739774077417742774377447745774677477748774977507751775277537754775577567757775877597760776177627763776477657766776777687769777077717772777377747775777677777778777977807781778277837784778577867787778877897790779177927793779477957796779777987799780078017802780378047805780678077808780978107811781278137814781578167817781878197820782178227823782478257826782778287829783078317832783378347835783678377838783978407841784278437844784578467847784878497850785178527853785478557856785778587859786078617862786378647865786678677868786978707871787278737874787578767877787878797880788178827883788478857886788778887889789078917892789378947895789678977898789979007901790279037904790579067907790879097910791179127913791479157916791779187919792079217922792379247925792679277928792979307931793279337934793579367937793879397940794179427943794479457946794779487949795079517952795379547955795679577958795979607961796279637964796579667967796879697970797179727973797479757976797779787979798079817982798379847985798679877988798979907991799279937994799579967997799879998000800180028003800480058006800780088009801080118012801380148015801680178018801980208021802280238024802580268027802880298030803180328033803480358036803780388039804080418042804380448045804680478048804980508051805280538054805580568057805880598060806180628063806480658066806780688069807080718072807380748075807680778078807980808081808280838084808580868087808880898090809180928093809480958096809780988099810081018102810381048105810681078108810981108111811281138114811581168117811881198120812181228123812481258126812781288129813081318132813381348135813681378138813981408141814281438144814581468147814881498150815181528153815481558156815781588159816081618162816381648165816681678168816981708171817281738174817581768177817881798180818181828183818481858186818781888189819081918192819381948195819681978198819982008201820282038204820582068207820882098210821182128213821482158216821782188219822082218222822382248225822682278228822982308231823282338234823582368237823882398240824182428243824482458246824782488249825082518252825382548255825682578258825982608261826282638264826582668267826882698270827182728273827482758276827782788279828082818282828382848285828682878288828982908291829282938294829582968297829882998300830183028303830483058306830783088309831083118312831383148315831683178318831983208321832283238324832583268327832883298330833183328333833483358336833783388339834083418342834383448345834683478348834983508351835283538354835583568357835883598360836183628363836483658366836783688369837083718372837383748375837683778378837983808381838283838384838583868387838883898390839183928393839483958396839783988399840084018402840384048405840684078408840984108411841284138414841584168417841884198420842184228423842484258426842784288429843084318432843384348435843684378438843984408441844284438444844584468447844884498450845184528453845484558456845784588459846084618462846384648465846684678468846984708471847284738474847584768477847884798480848184828483848484858486848784888489849084918492849384948495849684978498849985008501850285038504850585068507850885098510851185128513851485158516851785188519852085218522852385248525852685278528852985308531853285338534853585368537853885398540854185428543854485458546854785488549855085518552855385548555855685578558855985608561856285638564856585668567856885698570857185728573857485758576857785788579858085818582858385848585858685878588858985908591859285938594859585968597859885998600860186028603860486058606860786088609861086118612861386148615861686178618861986208621862286238624862586268627862886298630863186328633863486358636863786388639864086418642864386448645864686478648864986508651865286538654865586568657865886598660866186628663866486658666866786688669867086718672867386748675867686778678867986808681868286838684868586868687868886898690869186928693869486958696869786988699870087018702870387048705870687078708870987108711871287138714871587168717871887198720872187228723872487258726872787288729873087318732873387348735873687378738873987408741874287438744874587468747874887498750875187528753875487558756875787588759876087618762876387648765876687678768876987708771877287738774877587768777877887798780878187828783878487858786878787888789879087918792879387948795879687978798879988008801880288038804880588068807880888098810881188128813881488158816881788188819882088218822882388248825882688278828882988308831883288338834883588368837883888398840884188428843884488458846884788488849885088518852885388548855885688578858885988608861886288638864886588668867886888698870887188728873887488758876887788788879888088818882888388848885888688878888888988908891889288938894889588968897889888998900890189028903890489058906890789088909891089118912891389148915891689178918891989208921892289238924892589268927892889298930893189328933893489358936893789388939894089418942894389448945894689478948894989508951895289538954895589568957895889598960896189628963896489658966896789688969897089718972897389748975897689778978897989808981898289838984898589868987898889898990899189928993899489958996899789988999900090019002900390049005900690079008900990109011901290139014901590169017901890199020902190229023902490259026902790289029903090319032903390349035903690379038903990409041904290439044904590469047904890499050905190529053905490559056905790589059906090619062906390649065906690679068906990709071907290739074907590769077907890799080908190829083908490859086908790889089909090919092909390949095909690979098909991009101910291039104910591069107910891099110911191129113911491159116911791189119912091219122912391249125912691279128912991309131913291339134913591369137913891399140914191429143914491459146914791489149915091519152915391549155915691579158915991609161916291639164916591669167916891699170917191729173917491759176917791789179918091819182918391849185918691879188918991909191919291939194919591969197919891999200920192029203920492059206920792089209921092119212921392149215921692179218921992209221922292239224922592269227922892299230923192329233923492359236923792389239924092419242924392449245924692479248924992509251925292539254925592569257925892599260926192629263926492659266926792689269927092719272927392749275927692779278927992809281928292839284928592869287928892899290929192929293929492959296929792989299930093019302930393049305930693079308930993109311931293139314931593169317931893199320932193229323932493259326932793289329933093319332933393349335933693379338933993409341934293439344934593469347934893499350935193529353935493559356935793589359936093619362936393649365936693679368936993709371937293739374937593769377937893799380938193829383938493859386938793889389939093919392939393949395939693979398939994009401940294039404940594069407940894099410941194129413941494159416941794189419942094219422942394249425942694279428942994309431943294339434943594369437943894399440944194429443944494459446944794489449945094519452945394549455945694579458945994609461946294639464946594669467946894699470947194729473947494759476947794789479948094819482948394849485948694879488948994909491949294939494949594969497949894999500950195029503950495059506950795089509951095119512951395149515951695179518951995209521952295239524952595269527952895299530953195329533953495359536953795389539954095419542954395449545954695479548954995509551955295539554955595569557955895599560956195629563956495659566956795689569957095719572957395749575957695779578957995809581958295839584958595869587958895899590959195929593959495959596959795989599960096019602960396049605960696079608960996109611961296139614961596169617961896199620962196229623962496259626962796289629963096319632963396349635963696379638963996409641964296439644964596469647964896499650965196529653965496559656965796589659966096619662966396649665966696679668966996709671967296739674967596769677967896799680968196829683968496859686968796889689969096919692969396949695969696979698969997009701970297039704970597069707970897099710971197129713971497159716971797189719972097219722972397249725972697279728972997309731973297339734973597369737973897399740974197429743974497459746974797489749975097519752975397549755975697579758975997609761976297639764976597669767976897699770977197729773977497759776977797789779978097819782978397849785978697879788978997909791979297939794979597969797979897999800980198029803980498059806980798089809981098119812981398149815981698179818981998209821982298239824982598269827982898299830983198329833983498359836983798389839984098419842984398449845984698479848984998509851985298539854985598569857985898599860986198629863986498659866986798689869987098719872987398749875987698779878987998809881988298839884988598869887988898899890989198929893989498959896989798989899990099019902990399049905990699079908990999109911991299139914991599169917991899199920992199229923992499259926992799289929993099319932993399349935993699379938993999409941994299439944994599469947994899499950995199529953995499559956995799589959996099619962996399649965996699679968996999709971997299739974997599769977997899799980998199829983998499859986998799889989999099919992999399949995999699979998999910000100011000210003100041000510006100071000810009100101001110012100131001410015100161001710018100191002010021100221002310024100251002610027100281002910030100311003210033100341003510036100371003810039100401004110042100431004410045100461004710048100491005010051100521005310054100551005610057100581005910060100611006210063100641006510066100671006810069100701007110072100731007410075100761007710078100791008010081100821008310084100851008610087100881008910090100911009210093100941009510096100971009810099101001010110102101031010410105101061010710108101091011010111101121011310114101151011610117101181011910120101211012210123101241012510126101271012810129101301013110132101331013410135101361013710138101391014010141101421014310144101451014610147101481014910150101511015210153101541015510156101571015810159101601016110162101631016410165101661016710168101691017010171101721017310174101751017610177101781017910180101811018210183101841018510186101871018810189101901019110192101931019410195101961019710198101991020010201102021020310204102051020610207102081020910210102111021210213102141021510216102171021810219102201022110222102231022410225102261022710228102291023010231102321023310234102351023610237102381023910240102411024210243102441024510246102471024810249102501025110252102531025410255102561025710258102591026010261102621026310264102651026610267102681026910270102711027210273102741027510276102771027810279102801028110282102831028410285102861028710288102891029010291102921029310294102951029610297102981029910300103011030210303103041030510306103071030810309103101031110312103131031410315103161031710318103191032010321103221032310324103251032610327103281032910330103311033210333103341033510336103371033810339103401034110342103431034410345103461034710348103491035010351103521035310354103551035610357103581035910360103611036210363103641036510366103671036810369103701037110372103731037410375103761037710378103791038010381103821038310384103851038610387103881038910390103911039210393103941039510396103971039810399104001040110402104031040410405104061040710408104091041010411104121041310414104151041610417104181041910420104211042210423
  1. /*
  2. ** Command & Conquer Generals(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // 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. struct TCheckMovementInfo
  64. {
  65. // Input
  66. ICoord2D cell;
  67. PathfindLayerEnum layer;
  68. Int radius;
  69. Bool centerInCell;
  70. Bool considerTransient;
  71. LocomotorSurfaceTypeMask acceptableSurfaces;
  72. // Output
  73. Int allyFixedCount;
  74. Bool enemyFixed;
  75. Bool allyMoving;
  76. Bool allyGoal;
  77. };
  78. inline Int IABS(Int x) { if (x>=0) return x; return -x;};
  79. //-----------------------------------------------------------------------------------
  80. static Int frameToShowObstacles;
  81. //-----------------------------------------------------------------------------------
  82. PathNode::PathNode() :
  83. m_nextOpti(0),
  84. m_next(0),
  85. m_prev(0),
  86. m_nextOptiDist2D(0),
  87. m_canOptimize(false),
  88. m_id(-1)
  89. {
  90. m_nextOptiDirNorm2D.x = 0;
  91. m_nextOptiDirNorm2D.y = 0;
  92. m_pos.zero();
  93. m_layer = LAYER_INVALID;
  94. }
  95. //-----------------------------------------------------------------------------------
  96. PathNode::~PathNode()
  97. {
  98. }
  99. //-----------------------------------------------------------------------------------
  100. void PathNode::setNextOptimized(PathNode *node)
  101. {
  102. m_nextOpti = node;
  103. if (node)
  104. {
  105. m_nextOptiDirNorm2D.x = node->getPosition()->x - getPosition()->x;
  106. m_nextOptiDirNorm2D.y = node->getPosition()->y - getPosition()->y;
  107. m_nextOptiDist2D = m_nextOptiDirNorm2D.length();
  108. if (m_nextOptiDist2D == 0.0f)
  109. {
  110. //DEBUG_LOG(("Warning - Path Seg length == 0, adjusting. john a.\n"));
  111. m_nextOptiDist2D = 0.01f;
  112. }
  113. m_nextOptiDirNorm2D.x /= m_nextOptiDist2D;
  114. m_nextOptiDirNorm2D.y /= m_nextOptiDist2D;
  115. }
  116. else
  117. {
  118. m_nextOptiDist2D = 0;
  119. }
  120. }
  121. //-----------------------------------------------------------------------------------
  122. /// given a list, prepend this node, return new list
  123. PathNode *PathNode::prependToList( PathNode *list )
  124. {
  125. m_next = list;
  126. if (list)
  127. list->m_prev = this;
  128. m_prev = NULL;
  129. return this;
  130. }
  131. //-----------------------------------------------------------------------------------
  132. /// given a list, append this node, return new list. slow implementation.
  133. /// @todo optimize this
  134. PathNode *PathNode::appendToList( PathNode *list )
  135. {
  136. if (list == NULL)
  137. {
  138. m_next = NULL;
  139. m_prev = NULL;
  140. return this;
  141. }
  142. PathNode *tail;
  143. for( tail = list; tail->m_next; tail = tail->m_next )
  144. ;
  145. tail->m_next = this;
  146. m_prev = tail;
  147. m_next = NULL;
  148. return list;
  149. }
  150. //-----------------------------------------------------------------------------------
  151. /// given a node, append new node to this.
  152. void PathNode::append( PathNode *newNode )
  153. {
  154. newNode->m_next = this->m_next;
  155. newNode->m_prev = this;
  156. if (newNode->m_next) {
  157. newNode->m_next->m_prev = newNode;
  158. }
  159. this->m_next = newNode;
  160. }
  161. //-----------------------------------------------------------------------------------
  162. /**
  163. * Compute direction vector to next node
  164. */
  165. const Coord3D *PathNode::computeDirectionVector( void )
  166. {
  167. static Coord3D dir;
  168. if (m_next == NULL)
  169. {
  170. if (m_prev == NULL)
  171. {
  172. // only one node on whole path - no direction
  173. dir.x = 0.0f;
  174. dir.y = 0.0f;
  175. dir.z = 0.0f;
  176. }
  177. else
  178. {
  179. // tail node - continue prior direction
  180. return m_prev->computeDirectionVector();
  181. }
  182. }
  183. else
  184. {
  185. dir.x = m_next->m_pos.x - m_pos.x;
  186. dir.y = m_next->m_pos.y - m_pos.y;
  187. dir.z = m_next->m_pos.z - m_pos.z;
  188. }
  189. return &dir;
  190. }
  191. //-----------------------------------------------------------------------------------
  192. Path::Path():
  193. m_path(NULL),
  194. m_pathTail(NULL),
  195. m_isOptimized(FALSE),
  196. m_blockedByAlly(FALSE),
  197. m_cpopRecentStart(NULL),
  198. m_cpopCountdown(MAX_CPOP),
  199. m_cpopValid(FALSE)
  200. {
  201. m_cpopIn.zero();
  202. m_cpopOut.distAlongPath=0;
  203. m_cpopOut.layer = LAYER_GROUND;
  204. m_cpopOut.posOnPath.zero();
  205. }
  206. Path::~Path( void )
  207. {
  208. PathNode *node, *nextNode;
  209. // delete all of the path nodes
  210. for( node = m_path; node; node = nextNode )
  211. {
  212. nextNode = node->getNext();
  213. node->deleteInstance();
  214. }
  215. }
  216. // ------------------------------------------------------------------------------------------------
  217. /** CRC */
  218. // ------------------------------------------------------------------------------------------------
  219. void Path::crc( Xfer *xfer )
  220. {
  221. } // end crc
  222. // ------------------------------------------------------------------------------------------------
  223. /** Xfer Method */
  224. // ------------------------------------------------------------------------------------------------
  225. void Path::xfer( Xfer *xfer )
  226. {
  227. // version
  228. XferVersion currentVersion = 1;
  229. XferVersion version = currentVersion;
  230. xfer->xferVersion( &version, currentVersion );
  231. PathNode *node = m_path;
  232. Int count = 0;
  233. while (node) {
  234. count++;
  235. node = node->getNext();
  236. }
  237. xfer->xferInt(&count);
  238. if (xfer->getXferMode() == XFER_SAVE) {
  239. node = m_pathTail; // Write them out backwards.
  240. while (node) {
  241. node->m_id = count;
  242. xfer->xferInt(&count);
  243. Coord3D pos = *node->getPosition();
  244. xfer->xferCoord3D(&pos);
  245. PathfindLayerEnum layer = node->getLayer();
  246. xfer->xferUser(&layer, sizeof(layer));
  247. Bool canOpt = node->getCanOptimize();
  248. xfer->xferBool(&canOpt);
  249. Int id = -1;
  250. if (node->getNextOptimized()) {
  251. id = node->getNextOptimized()->m_id;
  252. }
  253. xfer->xferInt(&id);
  254. count--;
  255. node = node->getPrevious();
  256. }
  257. DEBUG_ASSERTCRASH(count==0, ("Wrong data count"));
  258. } else {
  259. m_cpopValid = FALSE;
  260. while (count) {
  261. Int nodeId;
  262. xfer->xferInt(&nodeId);
  263. DEBUG_ASSERTCRASH(nodeId==count, ("Bad data"));
  264. Coord3D pos;
  265. xfer->xferCoord3D(&pos);
  266. PathfindLayerEnum layer;
  267. xfer->xferUser(&layer, sizeof(layer));
  268. Bool canOpt;
  269. xfer->xferBool(&canOpt);
  270. Int optID = -1;
  271. xfer->xferInt(&optID);
  272. PathNode *node = newInstance(PathNode);
  273. node->m_id = nodeId;
  274. node->setPosition(&pos);
  275. node->setLayer(layer);
  276. node->setCanOptimize(canOpt);
  277. PathNode *optNode = NULL;
  278. if (optID > 0) {
  279. optNode = m_path;
  280. while (optNode && optNode->m_id != optID) {
  281. optNode = optNode->getNext();
  282. }
  283. DEBUG_ASSERTCRASH (optNode && optNode->m_id == optID, ("Could not find optimized link."));
  284. }
  285. m_path = node->prependToList(m_path);
  286. if (m_pathTail == NULL)
  287. m_pathTail = node;
  288. if (optNode) {
  289. node->setNextOptimized(optNode);
  290. }
  291. count--;
  292. }
  293. }
  294. xfer->xferBool(&m_isOptimized);
  295. Int obsolete1 = 0;
  296. xfer->xferInt(&obsolete1);
  297. UnsignedInt obsolete2;
  298. xfer->xferUnsignedInt(&obsolete2);
  299. xfer->xferBool(&m_blockedByAlly);
  300. #if defined _DEBUG || defined _INTERNAL
  301. if (TheGlobalData->m_debugAI == AI_DEBUG_PATHS)
  302. {
  303. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  304. RGBColor color;
  305. color.blue = 0;
  306. color.red = color.green = 1;
  307. Coord3D pos;
  308. addIcon(NULL, 0, 0, color); // erase feedback.
  309. for( PathNode *node = getFirstNode(); node; node = node->getNext() )
  310. {
  311. // create objects to show path - they decay
  312. pos = *node->getPosition();
  313. addIcon(&pos, PATHFIND_CELL_SIZE_F*.25f, 200, color);
  314. }
  315. // show optimized path
  316. for( node = getFirstNode(); node; node = node->getNextOptimized() )
  317. {
  318. pos = *node->getPosition();
  319. addIcon(&pos, PATHFIND_CELL_SIZE_F*.8f, 200, color);
  320. }
  321. TheAI->pathfinder()->setDebugPath(this);
  322. }
  323. #endif
  324. } // end xfer
  325. // ------------------------------------------------------------------------------------------------
  326. /** Load post process */
  327. // ------------------------------------------------------------------------------------------------
  328. void Path::loadPostProcess( void )
  329. {
  330. } // end loadPostProcess
  331. /**
  332. * Create a new node at the head of the path
  333. */
  334. void Path::prependNode( const Coord3D *pos, PathfindLayerEnum layer )
  335. {
  336. PathNode *node = newInstance(PathNode);
  337. node->setPosition( pos );
  338. node->setLayer(layer);
  339. m_path = node->prependToList( m_path );
  340. if (m_pathTail == NULL)
  341. m_pathTail = node;
  342. m_isOptimized = false;
  343. #ifdef CPOP_STARTS_FROM_PREV_SEG
  344. m_cpopRecentStart = NULL;
  345. #endif
  346. }
  347. /**
  348. * Create a new node at the tail of the path
  349. */
  350. void Path::appendNode( const Coord3D *pos, PathfindLayerEnum layer )
  351. {
  352. if (m_isOptimized && m_pathTail)
  353. {
  354. /* Check for duplicates. */
  355. if (pos->x == m_pathTail->getPosition()->x && pos->y == m_pathTail->getPosition()->y) {
  356. DEBUG_LOG(("Warning - Path Seg length == 0, ignoring. john a.\n"));
  357. return;
  358. }
  359. }
  360. PathNode *node = newInstance(PathNode);
  361. node->setPosition( pos );
  362. node->setLayer(layer);
  363. m_path = node->appendToList( m_path );
  364. if (m_isOptimized && m_pathTail)
  365. {
  366. m_pathTail->setNextOptimized(node);
  367. }
  368. m_pathTail = node;
  369. #ifdef CPOP_STARTS_FROM_PREV_SEG
  370. m_cpopRecentStart = NULL;
  371. #endif
  372. }
  373. /**
  374. * Create a new node at the tail of the path
  375. */
  376. void Path::updateLastNode( const Coord3D *pos )
  377. {
  378. PathfindLayerEnum layer = TheTerrainLogic->getLayerForDestination(pos);
  379. if (m_pathTail) {
  380. m_pathTail->setPosition(pos);
  381. m_pathTail->setLayer(layer);
  382. }
  383. if (m_isOptimized && m_pathTail)
  384. {
  385. PathNode *node = m_path;
  386. while(node && node->getNextOptimized() != m_pathTail) {
  387. node = node->getNextOptimized();
  388. }
  389. if (node && node->getNextOptimized() == m_pathTail) {
  390. node->setNextOptimized(m_pathTail);
  391. }
  392. }
  393. }
  394. /**
  395. * Optimize the path by checking line of sight
  396. */
  397. void Path::optimize( const Object *obj, LocomotorSurfaceTypeMask acceptableSurfaces, Bool blocked )
  398. {
  399. PathNode *node, *anchor;
  400. // start with first node in the path
  401. anchor = getFirstNode();
  402. Bool firstNode = true;
  403. PathfindLayerEnum firstLayer = anchor->getLayer();
  404. // backwards.
  405. //
  406. // For each node in the path, check LOS from last node in path, working forward.
  407. // When a clear LOS is found, keep the resulting straight line segment.
  408. //
  409. while( anchor != getLastNode() )
  410. {
  411. // find the farthest node in the path that has a clear line-of-sight to this anchor
  412. Bool optimizedSegment = false;
  413. PathfindLayerEnum layer = anchor->getLayer();
  414. PathfindLayerEnum curLayer = anchor->getLayer();
  415. Int count = 0;
  416. const Int ALLOWED_STEPS = 3; // we can optimize 3 steps to or from a bridge. Otherwise, we need to insert a point. jba.
  417. for (node = anchor->getNext(); node->getNext(); node=node->getNext()) {
  418. count++;
  419. if (curLayer==LAYER_GROUND) {
  420. if (node->getLayer() != curLayer) {
  421. layer = node->getLayer();
  422. curLayer = layer;
  423. if (count > ALLOWED_STEPS) break;
  424. }
  425. } else {
  426. if (node->getNext()->getLayer() != curLayer) {
  427. if (count > ALLOWED_STEPS) break;
  428. }
  429. }
  430. curLayer = node->getLayer();
  431. if (node->getCanOptimize()==false) {
  432. break;
  433. }
  434. }
  435. if (firstNode) {
  436. layer = firstLayer;
  437. firstNode = false;
  438. }
  439. //PathfindLayerEnum curLayer = LAYER_GROUND;
  440. for( ; node != anchor; node = node->getPrevious() )
  441. {
  442. Bool isPassable = false;
  443. //CRCDEBUG_LOG(("Path::optimize() calling isLinePassable()\n"));
  444. if (TheAI->pathfinder()->isLinePassable( obj, acceptableSurfaces, layer, *anchor->getPosition(),
  445. *node->getPosition(), blocked, false))
  446. {
  447. isPassable = true;
  448. }
  449. PathfindCell* cell = TheAI->pathfinder()->getCell( layer, node->getPosition());
  450. if (cell && cell->getType()==PathfindCell::CELL_CLIFF && !cell->getPinched()) {
  451. isPassable = true;
  452. }
  453. // Horizontal, diagonal, and vertical steps are passable.
  454. if (!isPassable) {
  455. Int dx = node->getPosition()->x - anchor->getPosition()->x;
  456. Int dy = node->getPosition()->y - anchor->getPosition()->y;
  457. Bool mightBePassable = false;
  458. if (IABS(dx)==PATHFIND_CELL_SIZE && IABS(dy)==PATHFIND_CELL_SIZE) {
  459. isPassable = true;
  460. }
  461. PathNode *tmpNode;
  462. if (dx==0) {
  463. mightBePassable = true;
  464. for (tmpNode = node->getPrevious(); tmpNode && tmpNode != anchor; tmpNode = tmpNode->getPrevious()) {
  465. dx = tmpNode->getNext()->getPosition()->x - tmpNode->getPosition()->x;
  466. if (dx!=0) mightBePassable = false;
  467. }
  468. }
  469. if (dy==0) {
  470. mightBePassable = true;
  471. for (tmpNode = node->getPrevious(); tmpNode && tmpNode != anchor; tmpNode = tmpNode->getPrevious()) {
  472. dy = tmpNode->getNext()->getPosition()->y - tmpNode->getPosition()->y;
  473. if (dy!=0) mightBePassable = false;
  474. }
  475. }
  476. if (dx == dy) {
  477. mightBePassable = true;
  478. for (tmpNode = node->getPrevious(); tmpNode && tmpNode != anchor; tmpNode = tmpNode->getPrevious()) {
  479. dx = tmpNode->getNext()->getPosition()->x - tmpNode->getPosition()->x;
  480. dy = tmpNode->getNext()->getPosition()->y - tmpNode->getPosition()->y;
  481. if (dy!=dx) mightBePassable = false;
  482. }
  483. }
  484. if (dx == -dy) {
  485. mightBePassable = true;
  486. for (tmpNode = node->getPrevious(); tmpNode && tmpNode != anchor; tmpNode = tmpNode->getPrevious()) {
  487. dx = tmpNode->getNext()->getPosition()->x - tmpNode->getPosition()->x;
  488. dy = tmpNode->getNext()->getPosition()->y - tmpNode->getPosition()->y;
  489. if (dy!=-dx) mightBePassable = false;
  490. }
  491. }
  492. if (mightBePassable) {
  493. isPassable = true;
  494. }
  495. }
  496. if (isPassable)
  497. {
  498. // anchor can directly see this node, make it next in the optimized path
  499. anchor->setNextOptimized( node );
  500. anchor = node;
  501. optimizedSegment = true;
  502. break;
  503. }
  504. }
  505. if (optimizedSegment == false)
  506. {
  507. // for some reason, there is no clear LOS between the anchor node and the very next node
  508. anchor->setNextOptimized( anchor->getNext() );
  509. anchor = anchor->getNext();
  510. }
  511. }
  512. // the path has been optimized
  513. m_isOptimized = true;
  514. }
  515. /**
  516. * Optimize the path by checking line of sight
  517. */
  518. void Path::optimizeGroundPath( Bool crusher, Int pathDiameter )
  519. {
  520. PathNode *node, *anchor;
  521. // start with first node in the path
  522. anchor = getFirstNode();
  523. //
  524. // For each node in the path, check LOS from last node in path, working forward.
  525. // When a clear LOS is found, keep the resulting straight line segment.
  526. //
  527. while( anchor != getLastNode() )
  528. {
  529. // find the farthest node in the path that has a clear line-of-sight to this anchor
  530. Bool optimizedSegment = false;
  531. PathfindLayerEnum layer = anchor->getLayer();
  532. PathfindLayerEnum curLayer = anchor->getLayer();
  533. Int count = 0;
  534. const Int ALLOWED_STEPS = 3; // we can optimize 3 steps to or from a bridge. Otherwise, we need to insert a point. jba.
  535. for (node = anchor->getNext(); node->getNext(); node=node->getNext()) {
  536. count++;
  537. if (curLayer==LAYER_GROUND) {
  538. if (node->getLayer() != curLayer) {
  539. layer = node->getLayer();
  540. curLayer = layer;
  541. if (count > ALLOWED_STEPS) break;
  542. }
  543. } else {
  544. if (node->getNext()->getLayer() != curLayer) {
  545. if (count > ALLOWED_STEPS) break;
  546. }
  547. }
  548. curLayer = node->getLayer();
  549. }
  550. // find the farthest node in the path that has a clear line-of-sight to this anchor
  551. for( ; node != anchor; node = node->getPrevious() )
  552. {
  553. Bool isPassable = false;
  554. //CRCDEBUG_LOG(("Path::optimize() calling isLinePassable()\n"));
  555. if (TheAI->pathfinder()->isGroundPathPassable( crusher, *anchor->getPosition(), layer,
  556. *node->getPosition(), pathDiameter))
  557. {
  558. isPassable = true;
  559. }
  560. // Horizontal, diagonal, and vertical steps are passable.
  561. if (!isPassable) {
  562. Int dx = node->getPosition()->x - anchor->getPosition()->x;
  563. Int dy = node->getPosition()->y - anchor->getPosition()->y;
  564. Bool mightBePassable = false;
  565. PathNode *tmpNode;
  566. if (dx==0) {
  567. mightBePassable = true;
  568. for (tmpNode = node->getPrevious(); tmpNode && tmpNode != anchor; tmpNode = tmpNode->getPrevious()) {
  569. dx = tmpNode->getNext()->getPosition()->x - tmpNode->getPosition()->x;
  570. if (dx!=0) mightBePassable = false;
  571. }
  572. }
  573. if (dy==0) {
  574. mightBePassable = true;
  575. for (tmpNode = node->getPrevious(); tmpNode && tmpNode != anchor; tmpNode = tmpNode->getPrevious()) {
  576. dy = tmpNode->getNext()->getPosition()->y - tmpNode->getPosition()->y;
  577. if (dy!=0) mightBePassable = false;
  578. }
  579. }
  580. if (dx == dy) {
  581. mightBePassable = true;
  582. for (tmpNode = node->getPrevious(); tmpNode && tmpNode != anchor; tmpNode = tmpNode->getPrevious()) {
  583. dx = tmpNode->getNext()->getPosition()->x - tmpNode->getPosition()->x;
  584. dy = tmpNode->getNext()->getPosition()->y - tmpNode->getPosition()->y;
  585. if (dy!=dx) mightBePassable = false;
  586. }
  587. }
  588. if (dx == -dy) {
  589. mightBePassable = true;
  590. for (tmpNode = node->getPrevious(); tmpNode && tmpNode != anchor; tmpNode = tmpNode->getPrevious()) {
  591. dx = tmpNode->getNext()->getPosition()->x - tmpNode->getPosition()->x;
  592. dy = tmpNode->getNext()->getPosition()->y - tmpNode->getPosition()->y;
  593. if (dy!=-dx) mightBePassable = false;
  594. }
  595. }
  596. if (mightBePassable) {
  597. isPassable = true;
  598. }
  599. }
  600. if (isPassable)
  601. {
  602. // anchor can directly see this node, make it next in the optimized path
  603. anchor->setNextOptimized( node );
  604. anchor = node;
  605. optimizedSegment = true;
  606. break;
  607. }
  608. }
  609. if (optimizedSegment == false)
  610. {
  611. // for some reason, there is no clear LOS between the anchor node and the very next node
  612. anchor->setNextOptimized( anchor->getNext() );
  613. anchor = anchor->getNext();
  614. }
  615. }
  616. // Remove jig/jogs :) jba.
  617. for (anchor=getFirstNode(); anchor!=NULL; anchor=anchor->getNextOptimized()) {
  618. node = anchor->getNextOptimized();
  619. if (node && node->getNextOptimized()) {
  620. Real dx = node->getPosition()->x - anchor->getPosition()->x;
  621. Real dy = node->getPosition()->y - anchor->getPosition()->y;
  622. // If the x & y offsets are less than 2 pathfind cells, kill it.
  623. if (dx*dx+dy*dy < sqr(PATHFIND_CELL_SIZE_F)*3.9f) {
  624. anchor->setNextOptimized(node->getNextOptimized());
  625. }
  626. }
  627. }
  628. // the path has been optimized
  629. m_isOptimized = true;
  630. }
  631. inline Bool isReallyClose(const Coord3D& a, const Coord3D& b)
  632. {
  633. const Real CLOSE_ENOUGH = 0.1f;
  634. return
  635. fabs(a.x-b.x) <= CLOSE_ENOUGH &&
  636. fabs(a.y-b.y) <= CLOSE_ENOUGH &&
  637. fabs(a.z-b.z) <= CLOSE_ENOUGH;
  638. }
  639. /**
  640. * Given a location, return the closest position on the path.
  641. * If 'allowBacktrack' is true, the entire path is considered.
  642. * If it is false, the point computed cannot be prior to previously returned non-backtracking points on this path.
  643. * Because the path "knows" the direction of travel, it will "lead" the given position a bit
  644. * to ensure the path is followed in the inteded direction.
  645. *
  646. * Note: The path cleanup does not take into account rolling terrain, so we can end up with
  647. * these situations:
  648. *
  649. * B
  650. * ######
  651. * ##########
  652. * A-##----------##---C
  653. * #######################
  654. *
  655. *
  656. * When an agent gets to B, he seems far off of the path, but it really not.
  657. * There are similar problems with valleys.
  658. *
  659. * Since agents track the closest path, if a high hill gets close to the underside of
  660. * a bridge, an agent may 'jump' to the higher path. This must be avoided in maps.
  661. *
  662. * return along-path distance to the end will be returned as function result
  663. */
  664. void Path::computePointOnPath(
  665. const Object* obj,
  666. const LocomotorSet& locomotorSet,
  667. const Coord3D& pos,
  668. ClosestPointOnPathInfo& out
  669. )
  670. {
  671. CRCDEBUG_LOG(("Path::computePointOnPath() fzor %s\n", DescribeObject(obj).str()));
  672. out.layer = LAYER_GROUND;
  673. out.posOnPath.zero();
  674. out.distAlongPath = 0;
  675. if (m_path == NULL)
  676. {
  677. m_cpopValid = false;
  678. return;
  679. }
  680. out.layer = m_path->getLayer();
  681. if (m_cpopValid && m_cpopCountdown>0 && isReallyClose(pos, m_cpopIn))
  682. {
  683. out = m_cpopOut;
  684. m_cpopCountdown--;
  685. CRCDEBUG_LOG(("Path::computePointOnPath() end because we're really close\n"));
  686. return;
  687. }
  688. m_cpopCountdown = MAX_CPOP;
  689. // default pathPos to end of the path
  690. out.posOnPath = *getLastNode()->getPosition();
  691. const PathNode* closeNode = NULL;
  692. Coord2D toPos;
  693. Real closeDistSqr = 99999999.9f;
  694. Real totalPathLength = 0.0f;
  695. Real lengthAlongPathToPos = 0.0f;
  696. //
  697. // Find the closest segment of the path
  698. //
  699. #ifdef CPOP_STARTS_FROM_PREV_SEG
  700. const PathNode* prevNode = m_cpopRecentStart;
  701. if (prevNode == NULL)
  702. prevNode = m_path;
  703. #else
  704. const PathNode* prevNode = m_path;
  705. #endif
  706. Coord2D segmentDirNorm;
  707. Real segmentLength;
  708. // note that the seg dir and len returned by this is the dist & vec from 'prevNode' to 'node'
  709. for ( const PathNode* node = prevNode->getNextOptimized(&segmentDirNorm, &segmentLength);
  710. node != NULL;
  711. node = node->getNextOptimized(&segmentDirNorm, &segmentLength) )
  712. {
  713. const Coord3D* prevNodePos = prevNode->getPosition();
  714. const Coord3D* nodePos = node->getPosition();
  715. // compute vector from start of segment to pos
  716. toPos.x = pos.x - prevNodePos->x;
  717. toPos.y = pos.y - prevNodePos->y;
  718. // compute distance projection of 'toPos' onto segment
  719. Real alongPathDist = segmentDirNorm.x * toPos.x + segmentDirNorm.y * toPos.y;
  720. Coord3D pointOnPath;
  721. if (alongPathDist < 0.0f)
  722. {
  723. // projected point is before start of segment, use starting point
  724. alongPathDist = 0.0f;
  725. pointOnPath = *prevNodePos;
  726. }
  727. else if (alongPathDist > segmentLength)
  728. {
  729. // projected point is beyond end of segment, use end point
  730. if (node->getNextOptimized() == NULL)
  731. {
  732. alongPathDist = segmentLength;
  733. pointOnPath = *nodePos;
  734. }
  735. else
  736. {
  737. // beyond the end of this segment, skip this segment
  738. // if bend is sharp, start of next segment will grab this point
  739. // if bend is gradual, the point will project into the next segment
  740. totalPathLength += segmentLength;
  741. prevNode = node;
  742. continue;
  743. }
  744. }
  745. else
  746. {
  747. // projected point is on this segment, compute it
  748. pointOnPath.x = prevNodePos->x + alongPathDist * segmentDirNorm.x;
  749. pointOnPath.y = prevNodePos->y + alongPathDist * segmentDirNorm.y;
  750. pointOnPath.z = 0;
  751. }
  752. // compute distance to point on path, and track the closest we've found so far
  753. Coord2D offset;
  754. offset.x = pos.x - pointOnPath.x;
  755. offset.y = pos.y - pointOnPath.y;
  756. Real offsetDistSqr = offset.x*offset.x + offset.y*offset.y;
  757. if (offsetDistSqr < closeDistSqr)
  758. {
  759. closeDistSqr = offsetDistSqr;
  760. closeNode = prevNode;
  761. out.posOnPath = pointOnPath;
  762. lengthAlongPathToPos = totalPathLength + alongPathDist;
  763. }
  764. // add this segment's length to find total path length
  765. /// @todo Precompute this and store in path
  766. totalPathLength += segmentLength;
  767. prevNode = node;
  768. DUMPCOORD3D(&pointOnPath);
  769. }
  770. #ifdef CPOP_STARTS_FROM_PREV_SEG
  771. m_cpopRecentStart = closeNode;
  772. #endif
  773. //
  774. // Compute the goal movement position for this agent
  775. //
  776. if (closeNode && closeNode->getNextOptimized())
  777. {
  778. // note that the seg dir and len returned by this is the dist & vec from 'closeNode' to 'closeNext'
  779. const PathNode* closeNext = closeNode->getNextOptimized(&segmentDirNorm, &segmentLength);
  780. const Coord3D* nextNodePos = closeNext->getPosition();
  781. const Coord3D* closeNodePos = closeNode->getPosition();
  782. const PathNode* closePrev = closeNode->getPrevious();
  783. if (closePrev && closePrev->getLayer() > LAYER_GROUND)
  784. {
  785. out.layer = closeNode->getLayer();
  786. }
  787. if (closeNode->getLayer() > LAYER_GROUND)
  788. {
  789. out.layer = closeNode->getLayer();
  790. }
  791. if (closeNext->getLayer() > LAYER_GROUND)
  792. {
  793. out.layer = closeNext->getLayer();
  794. }
  795. // compute vector from start of segment to pos
  796. toPos.x = pos.x - closeNodePos->x;
  797. toPos.y = pos.y - closeNodePos->y;
  798. // compute distance projection of 'toPos' onto segment
  799. Real alongPathDist = segmentDirNorm.x * toPos.x + segmentDirNorm.y * toPos.y;
  800. // we know this is the closest segment, so don't allow farther back than the start node
  801. if (alongPathDist < 0.0f)
  802. alongPathDist = 0.0f;
  803. // compute distance of point from this path segment
  804. Real toDistSqr = sqr(toPos.x) + sqr(toPos.y);
  805. Real offsetDistSq = toDistSqr - sqr(alongPathDist);
  806. Real offsetDist = (offsetDistSq <= 0.0) ? 0.0 : sqrt(offsetDistSq);
  807. // If we are basically on the path, return the next path node as the movement goal.
  808. // However, the farther off the path we get, the movement goal becomes closer to our
  809. // projected position on the path. If we are very far off the path, we will move
  810. // directly towards the nearest point on the path, and not the next path node.
  811. const Real maxPathError = 3.0f * PATHFIND_CELL_SIZE_F;
  812. const Real maxPathErrorInv = 1.0 / maxPathError;
  813. Real k = offsetDist * maxPathErrorInv;
  814. if (k > 1.0f)
  815. k = 1.0f;
  816. Bool gotPos = false;
  817. CRCDEBUG_LOG(("Path::computePointOnPath() calling isLinePassable() 1\n"));
  818. if (TheAI->pathfinder()->isLinePassable( obj, locomotorSet.getValidSurfaces(), out.layer, pos, *nextNodePos,
  819. false, true ))
  820. {
  821. out.posOnPath = *nextNodePos;
  822. gotPos = true;
  823. Bool tryAhead = alongPathDist > segmentLength * 0.5;
  824. if (closeNext->getCanOptimize() == false)
  825. {
  826. tryAhead = false; // don't go past no-opt nodes.
  827. }
  828. if (closeNode->getLayer() != closeNext->getLayer())
  829. {
  830. tryAhead = false; // don't go past layers.
  831. }
  832. if (obj->getLayer()!=LAYER_GROUND) {
  833. tryAhead = false;
  834. }
  835. Bool veryClose = false;
  836. if (segmentLength-alongPathDist<1.0f) {
  837. tryAhead = true;
  838. veryClose = true;
  839. }
  840. if (tryAhead)
  841. {
  842. // try next segment middle.
  843. const PathNode *next = closeNext->getNextOptimized();
  844. if (next)
  845. {
  846. Coord3D tryPos;
  847. tryPos.x = (nextNodePos->x + next->getPosition()->x) * 0.5;
  848. tryPos.y = (nextNodePos->y + next->getPosition()->y) * 0.5;
  849. tryPos.z = nextNodePos->z;
  850. CRCDEBUG_LOG(("Path::computePointOnPath() calling isLinePassable() 2\n"));
  851. if (veryClose || TheAI->pathfinder()->isLinePassable( obj, locomotorSet.getValidSurfaces(), closeNext->getLayer(), pos, tryPos, false, true ))
  852. {
  853. gotPos = true;
  854. out.posOnPath = tryPos;
  855. }
  856. }
  857. }
  858. }
  859. else if (k > 0.5f)
  860. {
  861. Real tryDist = alongPathDist + (0.5) * (segmentLength - alongPathDist);
  862. // projected point is on this segment, compute it
  863. out.posOnPath.x = closeNodePos->x + tryDist * segmentDirNorm.x;
  864. out.posOnPath.y = closeNodePos->y + tryDist * segmentDirNorm.y;
  865. out.posOnPath.z = closeNodePos->z;
  866. CRCDEBUG_LOG(("Path::computePointOnPath() calling isLinePassable() 3\n"));
  867. if (TheAI->pathfinder()->isLinePassable( obj, locomotorSet.getValidSurfaces(), out.layer, pos, out.posOnPath, false, true ))
  868. {
  869. k = 0.5f;
  870. gotPos = true;
  871. }
  872. }
  873. // if we are on the path (k == 0), then alongPathDist == segmentLength
  874. // if we are way off the path (k == 1), then alongPathDist is unchanged, and it projection of actual pos
  875. alongPathDist += (1.0f - k) * (segmentLength - alongPathDist);
  876. if (!gotPos)
  877. {
  878. if (alongPathDist > segmentLength)
  879. {
  880. alongPathDist = segmentLength;
  881. out.posOnPath = *nextNodePos;
  882. }
  883. else
  884. {
  885. // projected point is on this segment, compute it
  886. out.posOnPath.x = closeNodePos->x + alongPathDist * segmentDirNorm.x;
  887. out.posOnPath.y = closeNodePos->y + alongPathDist * segmentDirNorm.y;
  888. out.posOnPath.z = closeNodePos->z;
  889. Real dx = fabs(pos.x - out.posOnPath.x);
  890. Real dy = fabs(pos.y - out.posOnPath.y);
  891. if (dx<1 && dy<1 && closeNode->getNextOptimized() && closeNode->getNextOptimized()->getNextOptimized()) {
  892. out.posOnPath = *closeNode->getNextOptimized()->getNextOptimized()->getPosition();
  893. }
  894. }
  895. }
  896. }
  897. TheAI->pathfinder()->setDebugPathPosition( &out.posOnPath );
  898. out.distAlongPath = totalPathLength - lengthAlongPathToPos;
  899. Coord3D delta;
  900. delta.x = out.posOnPath.x - pos.x;
  901. delta.y = out.posOnPath.y - pos.y;
  902. delta.z = 0;
  903. Real lenDelta = delta.length();
  904. if (lenDelta > out.distAlongPath && out.distAlongPath > PATHFIND_CLOSE_ENOUGH)
  905. {
  906. out.distAlongPath = lenDelta;
  907. }
  908. m_cpopIn = pos;
  909. m_cpopOut = out;
  910. m_cpopValid = true;
  911. CRCDEBUG_LOG(("Path::computePointOnPath() end\n"));
  912. }
  913. /**
  914. Given a position, computes the distance to the goal. Returns 0 if we are past the goal.
  915. Returns the goal position in goalPos. This is intended for use with flying paths, that go
  916. directly to the goal and don't consider obstacles. jba.
  917. */
  918. Real Path::computeFlightDistToGoal( const Coord3D *pos, Coord3D& goalPos )
  919. {
  920. if (m_path == NULL)
  921. {
  922. goalPos.x = 0.0f;
  923. goalPos.y = 0.0f;
  924. goalPos.z = 0.0f;
  925. return 0.0f;
  926. }
  927. const PathNode *curNode = getFirstNode();
  928. if (m_cpopRecentStart) {
  929. curNode = m_cpopRecentStart;
  930. } else {
  931. m_cpopRecentStart = curNode;
  932. }
  933. const PathNode *nextNode = curNode->getNextOptimized();
  934. goalPos = *curNode->getPosition();
  935. Real distance = 0;
  936. Bool useNext = true;
  937. while (nextNode) {
  938. if (useNext) {
  939. goalPos = *nextNode->getPosition();
  940. }
  941. Coord3D startPos = *curNode->getPosition();
  942. Coord3D endPos = *nextNode->getPosition();
  943. Coord2D posToGoalVector;
  944. // posToGoalVector is pos to goalPos vector.
  945. posToGoalVector.x = endPos.x - pos->x;
  946. posToGoalVector.y = endPos.y - pos->y;
  947. // pathVector is the startPos to goal pos vector.
  948. Coord2D pathVector;
  949. pathVector.x = endPos.x - startPos.x;
  950. pathVector.y = endPos.y - startPos.y;
  951. // Normalize pathVector
  952. pathVector.normalize();
  953. // Dot product is the posToGoal vector projected onto the path vector.
  954. Real dotProduct = posToGoalVector.x*pathVector.x + posToGoalVector.y*pathVector.y;
  955. if (dotProduct>=0) {
  956. distance += dotProduct;
  957. useNext = false;
  958. } else if (useNext) {
  959. m_cpopRecentStart = nextNode;
  960. }
  961. curNode = nextNode;
  962. nextNode = curNode->getNextOptimized();
  963. }
  964. return distance;
  965. }
  966. //-----------------------------------------------------------------------------------
  967. enum { PATHFIND_CELLS_PER_FRAME=5000}; // Number of cells we will search pathfinding per frame.
  968. enum {CELL_INFOS_TO_ALLOCATE = 30000};
  969. PathfindCellInfo *PathfindCellInfo::s_infoArray = NULL;
  970. PathfindCellInfo *PathfindCellInfo::s_firstFree = NULL;
  971. /**
  972. * Allocates a pool of pathfind cell infos.
  973. */
  974. void PathfindCellInfo::allocateCellInfos(void)
  975. {
  976. releaseCellInfos();
  977. s_infoArray = MSGNEW("PathfindCellInfo") PathfindCellInfo[CELL_INFOS_TO_ALLOCATE]; // pool[]ify
  978. s_infoArray[CELL_INFOS_TO_ALLOCATE-1].m_pathParent = NULL;
  979. s_infoArray[CELL_INFOS_TO_ALLOCATE-1].m_isFree = true;
  980. s_firstFree = s_infoArray;
  981. for (Int i=0; i<CELL_INFOS_TO_ALLOCATE-1; i++) {
  982. s_infoArray[i].m_pathParent = &s_infoArray[i+1];
  983. s_infoArray[i].m_isFree = true;
  984. }
  985. }
  986. /**
  987. * Releases a pool of pathfind cell infos.
  988. */
  989. void PathfindCellInfo::releaseCellInfos(void)
  990. {
  991. if (s_infoArray==NULL) {
  992. return; // haven't allocated any yet.
  993. }
  994. Int count=0;
  995. while (s_firstFree) {
  996. count++;
  997. DEBUG_ASSERTCRASH(s_firstFree->m_isFree, ("Should be freed."));
  998. s_firstFree = s_firstFree->m_pathParent;
  999. }
  1000. DEBUG_ASSERTCRASH(count==CELL_INFOS_TO_ALLOCATE, ("Error - Allocated cellinfos."));
  1001. delete s_infoArray;
  1002. s_infoArray = NULL;
  1003. s_firstFree = NULL;
  1004. }
  1005. /**
  1006. * Gets a pathfindcellinfo.
  1007. */
  1008. PathfindCellInfo *PathfindCellInfo::getACellInfo(PathfindCell *cell,const ICoord2D &pos)
  1009. {
  1010. PathfindCellInfo *info = s_firstFree;
  1011. if (s_firstFree) {
  1012. DEBUG_ASSERTCRASH(s_firstFree->m_isFree, ("Should be freed."));
  1013. s_firstFree = s_firstFree->m_pathParent;
  1014. info->m_isFree = false; // Just allocated it.
  1015. info->m_cell = cell;
  1016. info->m_pos = pos;
  1017. info->m_nextOpen = NULL;
  1018. info->m_prevOpen = NULL;
  1019. info->m_pathParent = NULL;
  1020. info->m_costSoFar = 0;
  1021. info->m_totalCost = 0;
  1022. info->m_open = 0;
  1023. info->m_closed = 0;
  1024. info->m_obstacleID = INVALID_ID;
  1025. info->m_goalUnitID = INVALID_ID;
  1026. info->m_posUnitID = INVALID_ID;
  1027. info->m_goalAircraftID = INVALID_ID;
  1028. info->m_obstacleIsFence = false;
  1029. info->m_obstacleIsTransparent = false;
  1030. info->m_blockedByAlly = false;
  1031. }
  1032. return info;
  1033. }
  1034. /**
  1035. * Returns a pathfindcellinfo.
  1036. */
  1037. void PathfindCellInfo::releaseACellInfo(PathfindCellInfo *theInfo)
  1038. {
  1039. DEBUG_ASSERTCRASH(!theInfo->m_isFree, ("Shouldn't be free."));
  1040. //@ todo -fix this assert on usa04. jba.
  1041. //DEBUG_ASSERTCRASH(theInfo->m_obstacleID==0, ("Shouldn't be obstacle."));
  1042. theInfo->m_pathParent = s_firstFree;
  1043. s_firstFree = theInfo;
  1044. s_firstFree->m_isFree = true;
  1045. }
  1046. //-----------------------------------------------------------------------------------
  1047. /**
  1048. * Constructor
  1049. */
  1050. PathfindCell::PathfindCell( void ) :m_info(NULL)
  1051. {
  1052. reset();
  1053. }
  1054. /**
  1055. * Destructor
  1056. */
  1057. PathfindCell::~PathfindCell( void )
  1058. {
  1059. if (m_info) PathfindCellInfo::releaseACellInfo(m_info);
  1060. m_info = NULL;
  1061. static warn = true;
  1062. if (warn) {
  1063. warn = false;
  1064. DEBUG_LOG( ("PathfindCell::~PathfindCell m_info Allocated."));
  1065. }
  1066. }
  1067. /**
  1068. * Reset the cell to default values
  1069. */
  1070. void PathfindCell::reset( )
  1071. {
  1072. m_type = PathfindCell::CELL_CLEAR;
  1073. m_flags = PathfindCell::NO_UNITS;
  1074. m_zone = 0;
  1075. m_aircraftGoal = false;
  1076. m_pinched = false;
  1077. if (m_info) {
  1078. m_info->m_obstacleID = INVALID_ID;
  1079. PathfindCellInfo::releaseACellInfo(m_info);
  1080. m_info = NULL;
  1081. }
  1082. m_connectsToLayer = LAYER_INVALID;
  1083. m_layer = LAYER_GROUND;
  1084. }
  1085. /**
  1086. * Reset the pathfinding values in the cell.
  1087. */
  1088. Bool PathfindCell::startPathfind( PathfindCell *goalCell )
  1089. {
  1090. DEBUG_ASSERTCRASH(m_info, ("Has to have info."));
  1091. m_info->m_nextOpen = NULL;
  1092. m_info->m_prevOpen = NULL;
  1093. m_info->m_pathParent = NULL;
  1094. m_info->m_costSoFar = 0; // start node, no cost to get here
  1095. m_info->m_totalCost = 0;
  1096. if (goalCell) {
  1097. m_info->m_totalCost = costToGoal( goalCell );
  1098. }
  1099. m_info->m_open = TRUE;
  1100. m_info->m_closed = FALSE;
  1101. return true;
  1102. }
  1103. /**
  1104. * Set the parent pointer.
  1105. */
  1106. void PathfindCell::setParentCell( PathfindCell* parent )
  1107. {
  1108. DEBUG_ASSERTCRASH(m_info, ("Has to have info."));
  1109. m_info->m_pathParent = parent->m_info;
  1110. Int dx = m_info->m_pos.x - parent->m_info->m_pos.x;
  1111. Int dy = m_info->m_pos.y - parent->m_info->m_pos.y;
  1112. if (dx<-1 || dx>1 || dy<-1 || dy>1) {
  1113. DEBUG_CRASH(("Invalid parent index."));
  1114. }
  1115. }
  1116. /**
  1117. * Set the parent pointer.
  1118. */
  1119. void PathfindCell::setParentCellHierarchical( PathfindCell* parent )
  1120. {
  1121. DEBUG_ASSERTCRASH(m_info, ("Has to have info."));
  1122. m_info->m_pathParent = parent->m_info;
  1123. }
  1124. /**
  1125. * Reset the parent cell.
  1126. */
  1127. void PathfindCell::clearParentCell( void )
  1128. {
  1129. DEBUG_ASSERTCRASH(m_info, ("Has to have info."));
  1130. m_info->m_pathParent = NULL;
  1131. }
  1132. /**
  1133. * Allocates an info record for a cell.
  1134. */
  1135. Bool PathfindCell::allocateInfo( const ICoord2D &pos )
  1136. {
  1137. if (!m_info) {
  1138. m_info = PathfindCellInfo::getACellInfo(this, pos);
  1139. return (m_info != NULL);
  1140. }
  1141. return true;
  1142. }
  1143. /**
  1144. * Releases an info record for a cell.
  1145. */
  1146. void PathfindCell::releaseInfo( void )
  1147. {
  1148. if (m_type==PathfindCell::CELL_OBSTACLE) {
  1149. return;
  1150. }
  1151. if (m_flags!=NO_UNITS) {
  1152. return;
  1153. }
  1154. if (m_aircraftGoal) {
  1155. return;
  1156. }
  1157. if (m_info) {
  1158. DEBUG_ASSERTCRASH(m_info->m_prevOpen==NULL && m_info->m_nextOpen==NULL, ("Shouldn't be linked."));
  1159. DEBUG_ASSERTCRASH(m_info->m_open==NULL && m_info->m_closed==NULL, ("Shouldn't be linked."));
  1160. DEBUG_ASSERTCRASH(m_info->m_goalUnitID==INVALID_ID && m_info->m_posUnitID==INVALID_ID, ("Shouldn't be occupied."));
  1161. DEBUG_ASSERTCRASH(m_info->m_goalAircraftID==INVALID_ID , ("Shouldn't be occupied by aircraft."));
  1162. if (m_info->m_prevOpen || m_info->m_nextOpen || m_info->m_open || m_info->m_closed) {
  1163. // Bad release. Skip for now, better leak than crash. jba.
  1164. return;
  1165. }
  1166. PathfindCellInfo::releaseACellInfo(m_info);
  1167. m_info = NULL;
  1168. }
  1169. }
  1170. /**
  1171. * Sets the goal unit into the info record for a cell.
  1172. */
  1173. void PathfindCell::setGoalUnit(ObjectID unitID, const ICoord2D &pos )
  1174. {
  1175. if (unitID==INVALID_ID) {
  1176. // removing goal.
  1177. if (m_info) {
  1178. m_info->m_goalUnitID = INVALID_ID;
  1179. if (m_info->m_posUnitID == INVALID_ID) {
  1180. // No units here.
  1181. DEBUG_ASSERTCRASH(m_flags==UNIT_GOAL, ("Bad flags."));
  1182. m_flags = NO_UNITS;
  1183. releaseInfo();
  1184. } else{
  1185. m_flags = UNIT_PRESENT_MOVING;
  1186. }
  1187. } else {
  1188. DEBUG_ASSERTCRASH(m_flags == NO_UNITS, ("Bad flags."));
  1189. }
  1190. } else {
  1191. // adding goal.
  1192. if (!m_info) {
  1193. DEBUG_ASSERTCRASH(m_flags == NO_UNITS, ("Bad flags."));
  1194. allocateInfo(pos);
  1195. }
  1196. if (!m_info) {
  1197. DEBUG_CRASH(("Ran out of pathfind cells - fatal error!!!!! jba. "));
  1198. return;
  1199. }
  1200. m_info->m_goalUnitID = unitID;
  1201. if (unitID==m_info->m_posUnitID) {
  1202. m_flags = UNIT_PRESENT_FIXED;
  1203. } else if (m_info->m_posUnitID==INVALID_ID) {
  1204. m_flags = UNIT_GOAL;
  1205. } else {
  1206. m_flags = UNIT_GOAL_OTHER_MOVING;
  1207. }
  1208. }
  1209. }
  1210. /**
  1211. * Sets the goal aircraft into the info record for a cell.
  1212. */
  1213. void PathfindCell::setGoalAircraft(ObjectID unitID, const ICoord2D &pos )
  1214. {
  1215. if (unitID==INVALID_ID) {
  1216. // removing goal.
  1217. if (m_info) {
  1218. m_info->m_goalAircraftID = INVALID_ID;
  1219. m_aircraftGoal = false;
  1220. releaseInfo();
  1221. } else {
  1222. DEBUG_ASSERTCRASH(m_aircraftGoal==false, ("Bad flags."));
  1223. }
  1224. } else {
  1225. // adding goal.
  1226. if (!m_info) {
  1227. DEBUG_ASSERTCRASH(m_aircraftGoal==false, ("Bad flags."));
  1228. allocateInfo(pos);
  1229. }
  1230. if (!m_info) {
  1231. DEBUG_CRASH(("Ran out of pathfind cells - fatal error!!!!! jba. "));
  1232. return;
  1233. }
  1234. m_info->m_goalAircraftID = unitID;
  1235. m_aircraftGoal = true;
  1236. }
  1237. }
  1238. /**
  1239. * Sets the position unit into the info record for a cell.
  1240. */
  1241. void PathfindCell::setPosUnit(ObjectID unitID, const ICoord2D &pos )
  1242. {
  1243. if (unitID==INVALID_ID) {
  1244. // removing position.
  1245. if (m_info) {
  1246. m_info->m_posUnitID = INVALID_ID;
  1247. if (m_info->m_goalUnitID == INVALID_ID) {
  1248. // No units here.
  1249. DEBUG_ASSERTCRASH(m_flags==UNIT_PRESENT_MOVING, ("Bad flags."));
  1250. m_flags = NO_UNITS;
  1251. releaseInfo();
  1252. } else {
  1253. m_flags = UNIT_GOAL;
  1254. }
  1255. } else {
  1256. DEBUG_ASSERTCRASH(m_flags == NO_UNITS, ("Bad flags."));
  1257. }
  1258. } else {
  1259. // adding goal.
  1260. if (!m_info) {
  1261. DEBUG_ASSERTCRASH(m_flags == NO_UNITS, ("Bad flags."));
  1262. allocateInfo(pos);
  1263. }
  1264. if (!m_info) {
  1265. DEBUG_CRASH(("Ran out of pathfind cells - fatal error!!!!! jba. "));
  1266. return;
  1267. }
  1268. if (m_info->m_goalUnitID!=INVALID_ID && (m_info->m_goalUnitID==m_info->m_posUnitID)) {
  1269. // A unit is already occupying this cell.
  1270. return;
  1271. }
  1272. m_info->m_posUnitID = unitID;
  1273. if (unitID==m_info->m_goalUnitID) {
  1274. m_flags = UNIT_PRESENT_FIXED;
  1275. } else if (m_info->m_goalUnitID==INVALID_ID) {
  1276. m_flags = UNIT_PRESENT_MOVING;
  1277. } else {
  1278. m_flags = UNIT_GOAL_OTHER_MOVING;
  1279. }
  1280. }
  1281. }
  1282. /**
  1283. * Flag this cell as an obstacle, from the given one
  1284. */
  1285. void PathfindCell::setTypeAsObstacle( Object *obstacle, Bool isFence, const ICoord2D &pos )
  1286. {
  1287. if (m_type!=PathfindCell::CELL_CLEAR && m_type != PathfindCell::CELL_IMPASSABLE) {
  1288. return;
  1289. }
  1290. Bool isRubble = false;
  1291. if (obstacle->getBodyModule() && obstacle->getBodyModule()->getDamageState() == BODY_RUBBLE)
  1292. {
  1293. isRubble = true;
  1294. }
  1295. if (isRubble) {
  1296. m_type = PathfindCell::CELL_RUBBLE;
  1297. if (m_info) {
  1298. m_info->m_obstacleID = INVALID_ID;
  1299. releaseInfo();
  1300. }
  1301. return;
  1302. }
  1303. m_type = PathfindCell::CELL_OBSTACLE ;
  1304. if (!m_info) {
  1305. m_info = PathfindCellInfo::getACellInfo(this, pos);
  1306. if (!m_info) {
  1307. DEBUG_CRASH(("Not enough PathFindCellInfos in pool."));
  1308. return;
  1309. }
  1310. }
  1311. m_info->m_obstacleID = obstacle->getID();
  1312. m_info->m_obstacleIsFence = isFence;
  1313. m_info->m_obstacleIsTransparent = obstacle->isKindOf(KINDOF_CAN_SEE_THROUGH_STRUCTURE);
  1314. }
  1315. /**
  1316. * Flag this cell as given type.
  1317. */
  1318. void PathfindCell::setType( CellType type )
  1319. {
  1320. if (m_info && (m_info->m_obstacleID != INVALID_ID)) {
  1321. DEBUG_ASSERTCRASH(type==PathfindCell::CELL_OBSTACLE, ("Wrong type."));
  1322. m_type = PathfindCell::CELL_OBSTACLE;
  1323. return;
  1324. }
  1325. m_type = type;
  1326. }
  1327. /**
  1328. * Flag this cell as an obstacle, from the given one
  1329. */
  1330. void PathfindCell::removeObstacle( Object *obstacle )
  1331. {
  1332. if (m_type == PathfindCell::CELL_RUBBLE) {
  1333. m_type = PathfindCell::CELL_CLEAR;
  1334. }
  1335. if (!m_info) return;
  1336. if (m_info->m_obstacleID != obstacle->getID()) return;
  1337. m_type = PathfindCell::CELL_CLEAR;
  1338. if (m_info) {
  1339. m_info->m_obstacleID = INVALID_ID;
  1340. releaseInfo();
  1341. }
  1342. }
  1343. /// put self on "open" list in ascending cost order, return new list
  1344. PathfindCell *PathfindCell::putOnSortedOpenList( PathfindCell *list )
  1345. {
  1346. DEBUG_ASSERTCRASH(m_info, ("Has to have info."));
  1347. DEBUG_ASSERTCRASH(m_info->m_closed==FALSE && m_info->m_open==FALSE, ("Serious error - Invalid flags. jba"));
  1348. if (list == NULL)
  1349. {
  1350. list = this;
  1351. m_info->m_prevOpen = NULL;
  1352. m_info->m_nextOpen = NULL;
  1353. }
  1354. else
  1355. {
  1356. // insertion sort
  1357. PathfindCell *c, *lastCell = NULL;
  1358. for( c = list; c; c = c->getNextOpen() )
  1359. {
  1360. if (c->m_info->m_totalCost > m_info->m_totalCost)
  1361. break;
  1362. lastCell = c;
  1363. }
  1364. if (c)
  1365. {
  1366. // insert just before "c"
  1367. if (c->m_info->m_prevOpen)
  1368. c->m_info->m_prevOpen->m_nextOpen = this->m_info;
  1369. else
  1370. list = this;
  1371. m_info->m_prevOpen = c->m_info->m_prevOpen;
  1372. c->m_info->m_prevOpen = this->m_info;
  1373. m_info->m_nextOpen = c->m_info;
  1374. }
  1375. else
  1376. {
  1377. // append after "lastCell" - end of list
  1378. lastCell->m_info->m_nextOpen = this->m_info;
  1379. m_info->m_prevOpen = lastCell->m_info;
  1380. m_info->m_nextOpen = NULL;
  1381. }
  1382. }
  1383. // mark newCell as being on open list
  1384. m_info->m_open = true;
  1385. m_info->m_closed = false;
  1386. return list;
  1387. }
  1388. /// remove self from "open" list
  1389. PathfindCell *PathfindCell::removeFromOpenList( PathfindCell *list )
  1390. {
  1391. DEBUG_ASSERTCRASH(m_info, ("Has to have info."));
  1392. DEBUG_ASSERTCRASH(m_info->m_closed==FALSE && m_info->m_open==TRUE, ("Serious error - Invalid flags. jba"));
  1393. if (m_info->m_nextOpen)
  1394. m_info->m_nextOpen->m_prevOpen = m_info->m_prevOpen;
  1395. if (m_info->m_prevOpen)
  1396. m_info->m_prevOpen->m_nextOpen = m_info->m_nextOpen;
  1397. else
  1398. list = getNextOpen();
  1399. m_info->m_open = false;
  1400. m_info->m_nextOpen = NULL;
  1401. m_info->m_prevOpen = NULL;
  1402. return list;
  1403. }
  1404. /// remove all cells from "open" list
  1405. Int PathfindCell::releaseOpenList( PathfindCell *list )
  1406. {
  1407. Int count = 0;
  1408. while (list) {
  1409. count++;
  1410. DEBUG_ASSERTCRASH(list->m_info, ("Has to have info."));
  1411. DEBUG_ASSERTCRASH(list->m_info->m_closed==FALSE && list->m_info->m_open==TRUE, ("Serious error - Invalid flags. jba"));
  1412. PathfindCell *cur = list;
  1413. PathfindCellInfo *curInfo = list->m_info;
  1414. if (curInfo->m_nextOpen) {
  1415. list = curInfo->m_nextOpen->m_cell;
  1416. } else {
  1417. list = NULL;
  1418. }
  1419. DEBUG_ASSERTCRASH(cur == curInfo->m_cell, ("Bad backpointer in PathfindCellInfo"));
  1420. curInfo->m_nextOpen = NULL;
  1421. curInfo->m_prevOpen = NULL;
  1422. curInfo->m_open = FALSE;
  1423. cur->releaseInfo();
  1424. }
  1425. return count;
  1426. }
  1427. /// remove all cells from "closed" list
  1428. Int PathfindCell::releaseClosedList( PathfindCell *list )
  1429. {
  1430. Int count = 0;
  1431. while (list) {
  1432. count++;
  1433. DEBUG_ASSERTCRASH(list->m_info, ("Has to have info."));
  1434. DEBUG_ASSERTCRASH(list->m_info->m_closed==TRUE && list->m_info->m_open==FALSE, ("Serious error - Invalid flags. jba"));
  1435. PathfindCell *cur = list;
  1436. PathfindCellInfo *curInfo = list->m_info;
  1437. if (curInfo->m_nextOpen) {
  1438. list = curInfo->m_nextOpen->m_cell;
  1439. } else {
  1440. list = NULL;
  1441. }
  1442. DEBUG_ASSERTCRASH(cur == curInfo->m_cell, ("Bad backpointer in PathfindCellInfo"));
  1443. curInfo->m_nextOpen = NULL;
  1444. curInfo->m_prevOpen = NULL;
  1445. curInfo->m_closed = FALSE;
  1446. cur->releaseInfo();
  1447. }
  1448. return count;
  1449. }
  1450. /// put self on "closed" list, return new list
  1451. PathfindCell *PathfindCell::putOnClosedList( PathfindCell *list )
  1452. {
  1453. DEBUG_ASSERTCRASH(m_info, ("Has to have info."));
  1454. DEBUG_ASSERTCRASH(m_info->m_closed==FALSE && m_info->m_open==FALSE, ("Serious error - Invalid flags. jba"));
  1455. // only put on list if not already on it
  1456. if (m_info->m_closed == FALSE)
  1457. {
  1458. m_info->m_closed = FALSE;
  1459. m_info->m_closed = TRUE;
  1460. m_info->m_prevOpen = NULL;
  1461. m_info->m_nextOpen = list?list->m_info:NULL;
  1462. if (list)
  1463. list->m_info->m_prevOpen = this->m_info;
  1464. list = this;
  1465. }
  1466. return list;
  1467. }
  1468. /// remove self from "closed" list
  1469. PathfindCell *PathfindCell::removeFromClosedList( PathfindCell *list )
  1470. {
  1471. DEBUG_ASSERTCRASH(m_info, ("Has to have info."));
  1472. DEBUG_ASSERTCRASH(m_info->m_closed==TRUE && m_info->m_open==FALSE, ("Serious error - Invalid flags. jba"));
  1473. if (m_info->m_nextOpen)
  1474. m_info->m_nextOpen->m_prevOpen = m_info->m_prevOpen;
  1475. if (m_info->m_prevOpen)
  1476. m_info->m_prevOpen->m_nextOpen = m_info->m_nextOpen;
  1477. else
  1478. list = getNextOpen();
  1479. m_info->m_closed = false;
  1480. m_info->m_nextOpen = NULL;
  1481. m_info->m_prevOpen = NULL;
  1482. return list;
  1483. }
  1484. const Int COST_ORTHOGONAL = 10;
  1485. const Int COST_DIAGONAL = 14;
  1486. const Real COST_TO_DISTANCE_FACTOR = 1.0f/10.0f;
  1487. const Real COST_TO_DISTANCE_FACTOR_SQR = COST_TO_DISTANCE_FACTOR*COST_TO_DISTANCE_FACTOR;
  1488. UnsignedInt PathfindCell::costToGoal( PathfindCell *goal )
  1489. {
  1490. DEBUG_ASSERTCRASH(m_info, ("Has to have info."));
  1491. Int dx = m_info->m_pos.x - goal->getXIndex();
  1492. Int dy = m_info->m_pos.y - goal->getYIndex();
  1493. #define NO_REAL_DIST
  1494. #ifdef REAL_DIST
  1495. Int cost = COST_ORTHOGONAL*sqrt(dx*dx + dy*dy);
  1496. #else
  1497. if (dx<0) dx = -dx;
  1498. if (dy<0) dy = -dy;
  1499. Int cost;
  1500. if (dx>dy) {
  1501. cost= COST_ORTHOGONAL*dx + (COST_ORTHOGONAL*dy)/2;
  1502. } else {
  1503. cost= COST_ORTHOGONAL*dy + (COST_ORTHOGONAL*dx)/2;
  1504. }
  1505. #endif
  1506. return cost;
  1507. }
  1508. UnsignedInt PathfindCell::costToHierGoal( PathfindCell *goal )
  1509. {
  1510. DEBUG_ASSERTCRASH(m_info, ("Has to have info."));
  1511. Int dx = m_info->m_pos.x - goal->getXIndex();
  1512. Int dy = m_info->m_pos.y - goal->getYIndex();
  1513. Int cost = REAL_TO_INT_FLOOR(COST_ORTHOGONAL*sqrt(dx*dx + dy*dy) + 0.5f);
  1514. return cost;
  1515. }
  1516. UnsignedInt PathfindCell::costSoFar( PathfindCell *parent )
  1517. {
  1518. DEBUG_ASSERTCRASH(m_info, ("Has to have info."));
  1519. // very first node in path - no turns, no cost
  1520. if (parent == NULL)
  1521. return 0;
  1522. // add in number of turns in path so far
  1523. ICoord2D prevDir;
  1524. Int cost;
  1525. prevDir.x = parent->getXIndex() - m_info->m_pos.x;
  1526. prevDir.y = parent->getYIndex() - m_info->m_pos.y;
  1527. // diagonal moves cost a bit more than orthogonal ones
  1528. if (prevDir.x == 0 || prevDir.y == 0)
  1529. cost = parent->getCostSoFar() + COST_ORTHOGONAL;
  1530. else
  1531. cost = parent->getCostSoFar() + COST_DIAGONAL;
  1532. if (getPinched()) {
  1533. cost += 1*COST_DIAGONAL;
  1534. }
  1535. #if 1
  1536. // Increase cost of turns.
  1537. Int numTurns = 0;
  1538. PathfindCell *prevCell = parent->getParentCell();
  1539. if (prevCell) {
  1540. ICoord2D dir;
  1541. dir.x = prevCell->getXIndex() - parent->getXIndex();
  1542. dir.y = prevCell->getYIndex() - parent->getYIndex();
  1543. // count number of direction changes
  1544. if (dir.x != prevDir.x || dir.y != prevDir.y)
  1545. {
  1546. Int dot = dir.x * prevDir.x + dir.y * prevDir.y;
  1547. if (dot > 0)
  1548. numTurns=4; // 45 degree turn
  1549. else if (dot == 0)
  1550. numTurns = 8; // 90 degree turn
  1551. else
  1552. numTurns = 16; // 135 degree turn
  1553. }
  1554. }
  1555. return cost + numTurns;
  1556. #else
  1557. return cost;
  1558. #endif
  1559. }
  1560. inline Bool typesMatch(const PathfindCell &targetCell, const PathfindCell &sourceCell) {
  1561. PathfindCell::CellType targetType = targetCell.getType();
  1562. PathfindCell::CellType srcType = sourceCell.getType();
  1563. if (targetType == srcType) return true;
  1564. return false;
  1565. }
  1566. inline Bool waterGround(const PathfindCell &targetCell, const PathfindCell &sourceCell) {
  1567. PathfindCell::CellType targetType = targetCell.getType();
  1568. PathfindCell::CellType srcType = sourceCell.getType();
  1569. if ( (targetType==PathfindCell::CELL_CLEAR &&
  1570. (srcType&PathfindCell::CELL_WATER ))) {
  1571. return true;
  1572. }
  1573. if ( (srcType==PathfindCell::CELL_CLEAR &&
  1574. (targetType&PathfindCell::CELL_WATER ))) {
  1575. return true;
  1576. }
  1577. return false;
  1578. }
  1579. inline Bool groundRubble(const PathfindCell &targetCell, const PathfindCell &sourceCell) {
  1580. PathfindCell::CellType targetType = targetCell.getType();
  1581. PathfindCell::CellType srcType = sourceCell.getType();
  1582. if ( (targetType==PathfindCell::CELL_CLEAR &&
  1583. (srcType==PathfindCell::CELL_RUBBLE ))) {
  1584. return true;
  1585. }
  1586. if ( (srcType==PathfindCell::CELL_CLEAR &&
  1587. (targetType==PathfindCell::CELL_RUBBLE ))) {
  1588. return true;
  1589. }
  1590. return false;
  1591. }
  1592. inline Bool terrain(const PathfindCell &targetCell, const PathfindCell &sourceCell) {
  1593. Int targetType = targetCell.getType();
  1594. Int srcType = sourceCell.getType();
  1595. if (targetType == PathfindCell::CELL_OBSTACLE) targetType = PathfindCell::CELL_CLEAR;
  1596. if (srcType == PathfindCell::CELL_OBSTACLE) srcType = PathfindCell::CELL_CLEAR;
  1597. if (targetType==srcType) {
  1598. return true;
  1599. }
  1600. return false;
  1601. }
  1602. inline Bool crusherGround(const PathfindCell &targetCell, const PathfindCell &sourceCell) {
  1603. Int targetType = targetCell.getType();
  1604. Int srcType = sourceCell.getType();
  1605. if (targetType==PathfindCell::CELL_OBSTACLE) {
  1606. if (targetCell.isObstacleFence()) {
  1607. if (srcType == PathfindCell::CELL_CLEAR) {
  1608. return true;
  1609. }
  1610. }
  1611. }
  1612. if (srcType==PathfindCell::CELL_OBSTACLE) {
  1613. if (sourceCell.isObstacleFence()) {
  1614. if (targetType == PathfindCell::CELL_CLEAR) {
  1615. return true;
  1616. }
  1617. }
  1618. }
  1619. return false;
  1620. }
  1621. inline Bool groundCliff(const PathfindCell &targetCell, const PathfindCell &sourceCell) {
  1622. PathfindCell::CellType targetType = targetCell.getType();
  1623. PathfindCell::CellType srcType = sourceCell.getType();
  1624. if ( (targetType==PathfindCell::CELL_CLIFF ) &&
  1625. (srcType==PathfindCell::CELL_CLEAR) ) {
  1626. return true;
  1627. }
  1628. if ( (targetType==PathfindCell::CELL_CLEAR ) &&
  1629. (srcType==PathfindCell::CELL_CLIFF) ) {
  1630. return true;
  1631. }
  1632. return false;
  1633. }
  1634. static void __fastcall resolveBlockZones(Int srcZone, Int targetZone, UnsignedShort *zoneEquivalency, Int sizeOfZE)
  1635. {
  1636. Int i;
  1637. // We have two zones being combined now. Keep the lower zone.
  1638. DEBUG_ASSERTCRASH(srcZone!=0 && targetZone!=0, ("Bad resolve zones ."));
  1639. if (targetZone<srcZone) {
  1640. for (i=0; i<sizeOfZE; i++) {
  1641. if (zoneEquivalency[i] == srcZone) {
  1642. zoneEquivalency[i] = targetZone;
  1643. }
  1644. }
  1645. } else {
  1646. for (i=0; i<sizeOfZE; i++) {
  1647. if (zoneEquivalency[i] == targetZone) {
  1648. zoneEquivalency[i] = srcZone;
  1649. }
  1650. }
  1651. }
  1652. }
  1653. static void __fastcall resolveZones(Int srcZone, Int targetZone, UnsignedShort *zoneEquivalency, Int sizeOfZE)
  1654. {
  1655. Int i;
  1656. // We have two zones being combined now. Keep the lower zone.
  1657. DEBUG_ASSERTCRASH(srcZone!=0 && targetZone!=0, ("Bad resolve zones ."));
  1658. DEBUG_ASSERTCRASH(srcZone<sizeOfZE && targetZone<sizeOfZE, ("Bad resolve zones ."));
  1659. srcZone = zoneEquivalency[srcZone];
  1660. targetZone = zoneEquivalency[targetZone];
  1661. DEBUG_ASSERTCRASH(srcZone<sizeOfZE && targetZone<sizeOfZE, ("Bad resolve zones ."));
  1662. UnsignedShort finalZone;
  1663. if (targetZone<srcZone) {
  1664. finalZone = zoneEquivalency[targetZone];
  1665. } else {
  1666. finalZone = zoneEquivalency[srcZone];
  1667. }
  1668. DEBUG_ASSERTCRASH(finalZone<sizeOfZE , ("Bad resolve zones ."));
  1669. for (i=0; i<sizeOfZE; i++) {
  1670. UnsignedShort ze = zoneEquivalency[i];
  1671. if (ze == targetZone || ze == srcZone) {
  1672. zoneEquivalency[i] = finalZone;
  1673. }
  1674. }
  1675. }
  1676. static void flattenZones(UnsignedShort *zoneArray, UnsignedShort *zoneHierarchical, Int sizeOfZones)
  1677. {
  1678. Int i;
  1679. for (i=0; i<sizeOfZones; i++) {
  1680. Int zone1 = zoneArray[i];
  1681. Int zone2 = zoneHierarchical[zone1];
  1682. zone1 = zoneArray[zone2];
  1683. zone2 = zoneHierarchical[zone1];
  1684. zoneArray[i] = zone2;
  1685. }
  1686. #if 1
  1687. for (i=0; i<sizeOfZones; i++) {
  1688. Int zone1 = zoneArray[i];
  1689. Int zone2 = zoneHierarchical[i];
  1690. if (zone1!=zone2) {
  1691. resolveZones(zone1, zone2, zoneArray, sizeOfZones);
  1692. }
  1693. }
  1694. #endif
  1695. }
  1696. inline void applyZone(PathfindCell &targetCell, const PathfindCell &sourceCell, UnsignedShort *zoneEquivalency, Int sizeOfZE)
  1697. {
  1698. DEBUG_ASSERTCRASH(sourceCell.getZone()!=0, ("Unset source zone."));
  1699. Int srcZone = zoneEquivalency[sourceCell.getZone()];
  1700. //DEBUG_ASSERTCRASH(srcZone!=0, ("Bad zone equivalency zone."));
  1701. Int targetZone = zoneEquivalency[targetCell.getZone()];
  1702. if (targetZone == 0) {
  1703. targetCell.setZone(srcZone);
  1704. return;
  1705. }
  1706. if (targetZone == srcZone) {
  1707. return; // already match.
  1708. }
  1709. resolveZones(srcZone, targetZone, zoneEquivalency, sizeOfZE);
  1710. }
  1711. inline void applyBlockZone(PathfindCell &targetCell, const PathfindCell &sourceCell,
  1712. UnsignedShort *zoneEquivalency, Int firstZone, Int sizeOfZE)
  1713. {
  1714. DEBUG_ASSERTCRASH(sourceCell.getZone()>=firstZone && sourceCell.getZone()<firstZone+sizeOfZE, ("Memory overrun - FATAL ERROR."));
  1715. Int srcZone = zoneEquivalency[sourceCell.getZone()-firstZone];
  1716. DEBUG_ASSERTCRASH(targetCell.getZone()>=firstZone && sourceCell.getZone()<firstZone+sizeOfZE, ("Memory overrun - FATAL ERROR."));
  1717. Int targetZone = zoneEquivalency[targetCell.getZone()-firstZone];
  1718. if (targetZone == srcZone) {
  1719. return; // already match.
  1720. }
  1721. resolveBlockZones(srcZone, targetZone, zoneEquivalency, sizeOfZE);
  1722. }
  1723. //------------------------ ZoneBlock -------------------------------
  1724. ZoneBlock::ZoneBlock() : m_firstZone(0),
  1725. m_numZones(0),
  1726. m_groundCliffZones(NULL),
  1727. m_groundWaterZones(NULL),
  1728. m_groundRubbleZones(NULL),
  1729. m_crusherZones(NULL),
  1730. m_zonesAllocated(0),
  1731. m_interactsWithBridge(FALSE)
  1732. {
  1733. m_cellOrigin.x = 0;
  1734. m_cellOrigin.y = 0;
  1735. //Added By Sadullah Nader
  1736. //Initialization(s) inserted
  1737. m_firstZone = 0;
  1738. m_markedPassable = TRUE;
  1739. //
  1740. }
  1741. ZoneBlock::~ZoneBlock()
  1742. {
  1743. freeZones();
  1744. }
  1745. void ZoneBlock::freeZones(void)
  1746. {
  1747. if (m_groundCliffZones) {
  1748. delete [] m_groundCliffZones;
  1749. m_groundCliffZones = NULL;
  1750. }
  1751. if (m_groundWaterZones) {
  1752. delete [] m_groundWaterZones;
  1753. m_groundWaterZones = NULL;
  1754. }
  1755. if (m_groundRubbleZones) {
  1756. delete [] m_groundRubbleZones;
  1757. m_groundRubbleZones = NULL;
  1758. }
  1759. if (m_crusherZones) {
  1760. delete [] m_crusherZones;
  1761. m_crusherZones = NULL;
  1762. }
  1763. }
  1764. /* Allocate zone equivalency arrays large enough to hold required entries. If the arrays are already
  1765. large enough, reuse. Then calculate terrain equivalencies. */
  1766. void ZoneBlock::blockCalculateZones(PathfindCell **map, PathfindLayer layers[], const IRegion2D &bounds)
  1767. {
  1768. Int i, j;
  1769. m_cellOrigin = bounds.lo;
  1770. UnsignedInt minZone = map[bounds.lo.x][bounds.lo.y].getZone();
  1771. UnsignedInt maxZone = minZone;
  1772. for( j=bounds.lo.y; j<=bounds.hi.y; j++ ) {
  1773. for( i=bounds.lo.x; i<=bounds.hi.x; i++ ) {
  1774. PathfindCell *cell = &map[i][j];
  1775. UnsignedShort zone = cell->getZone();
  1776. if (minZone>zone) minZone=zone;
  1777. if (maxZone<zone) maxZone=zone;
  1778. }
  1779. }
  1780. m_firstZone = minZone;
  1781. m_numZones = 1 + maxZone - minZone;
  1782. allocateZones();
  1783. if (m_numZones==1) return; // all zones are equivalent.
  1784. // Determine water/ground equivalent zones, and ground/cliff equivalent zones.
  1785. for (i=0; i<m_zonesAllocated; i++) {
  1786. m_groundCliffZones[i] = i+m_firstZone;
  1787. m_groundWaterZones[i] = i+m_firstZone;
  1788. m_groundRubbleZones[i] = i+m_firstZone;
  1789. m_crusherZones[i] = i+m_firstZone;
  1790. }
  1791. for( j=bounds.lo.y; j<=bounds.hi.y; j++ ) {
  1792. for( i=bounds.lo.x; i<=bounds.hi.x; i++ ) {
  1793. if (i>bounds.lo.x && map[i][j].getZone()!=map[i-1][j].getZone()) {
  1794. if (waterGround(map[i][j], map[i-1][j])) {
  1795. applyBlockZone(map[i][j], map[i-1][j], m_groundWaterZones, m_firstZone, m_numZones);
  1796. }
  1797. if (groundRubble(map[i][j], map[i-1][j])) {
  1798. applyBlockZone(map[i][j], map[i-1][j], m_groundRubbleZones, m_firstZone, m_numZones);
  1799. }
  1800. if (groundCliff(map[i][j], map[i-1][j])) {
  1801. applyBlockZone(map[i][j], map[i-1][j], m_groundCliffZones, m_firstZone, m_numZones);
  1802. }
  1803. if (crusherGround(map[i][j], map[i-1][j])) {
  1804. applyBlockZone(map[i][j], map[i-1][j], m_crusherZones, m_firstZone, m_numZones);
  1805. }
  1806. }
  1807. if (j>bounds.lo.y && map[i][j].getZone()!=map[i][j-1].getZone()) {
  1808. if (waterGround(map[i][j],map[i][j-1])) {
  1809. applyBlockZone(map[i][j], map[i][j-1], m_groundWaterZones, m_firstZone, m_numZones);
  1810. }
  1811. if (groundRubble(map[i][j], map[i][j-1])) {
  1812. applyBlockZone(map[i][j], map[i][j-1], m_groundRubbleZones, m_firstZone, m_numZones);
  1813. }
  1814. if (groundCliff(map[i][j],map[i][j-1])) {
  1815. applyBlockZone(map[i][j], map[i][j-1], m_groundCliffZones, m_firstZone, m_numZones);
  1816. }
  1817. if (crusherGround(map[i][j], map[i][j-1])) {
  1818. applyBlockZone(map[i][j], map[i][j-1], m_crusherZones, m_firstZone, m_numZones);
  1819. }
  1820. }
  1821. DEBUG_ASSERTCRASH(map[i][j].getZone() != 0, ("Cleared the zone."));
  1822. }
  1823. }
  1824. }
  1825. //
  1826. // Return the zone at this location.
  1827. //
  1828. UnsignedShort ZoneBlock::getEffectiveZone( LocomotorSurfaceTypeMask acceptableSurfaces,
  1829. Bool crusher, UnsignedShort zone) const
  1830. {
  1831. DEBUG_ASSERTCRASH(zone, ("Zone not set"));
  1832. if (acceptableSurfaces&LOCOMOTORSURFACE_AIR) return 1; // air is all zone 1.
  1833. if ( (acceptableSurfaces&LOCOMOTORSURFACE_GROUND) &&
  1834. (acceptableSurfaces&LOCOMOTORSURFACE_WATER) &&
  1835. (acceptableSurfaces&LOCOMOTORSURFACE_CLIFF)) {
  1836. // Locomotors can go on ground, water & cliff, so all is zone 1.
  1837. return 1;
  1838. }
  1839. if (m_numZones<2) {
  1840. return m_firstZone; // if we only got 1 zone, it's all the same zone.
  1841. }
  1842. DEBUG_ASSERTCRASH(zone >=m_firstZone && zone < m_firstZone+m_numZones, ("Invalid range."));
  1843. if (zone<m_firstZone || zone >= m_firstZone+m_numZones) {
  1844. return m_firstZone;
  1845. }
  1846. zone -= m_firstZone;
  1847. if (crusher) {
  1848. zone = m_crusherZones[zone];
  1849. DEBUG_ASSERTCRASH(zone >=m_firstZone && zone < m_firstZone+m_numZones, ("Invalid range."));
  1850. zone -= m_firstZone;
  1851. }
  1852. if ( (acceptableSurfaces&LOCOMOTORSURFACE_GROUND) &&
  1853. (acceptableSurfaces&LOCOMOTORSURFACE_CLIFF)) {
  1854. // Locomotors can go on ground & cliff, so use the ground cliff combiner.
  1855. zone = m_groundCliffZones[zone];
  1856. DEBUG_ASSERTCRASH(zone >=m_firstZone && zone < m_firstZone+m_numZones, ("Invalid range."));
  1857. return zone;
  1858. }
  1859. if ( (acceptableSurfaces&LOCOMOTORSURFACE_GROUND) &&
  1860. (acceptableSurfaces&LOCOMOTORSURFACE_WATER)) {
  1861. // Locomotors can go on ground & water, so use the ground water combiner.
  1862. zone = m_groundWaterZones[zone];
  1863. DEBUG_ASSERTCRASH(zone >=m_firstZone && zone < m_firstZone+m_numZones, ("Invalid range."));
  1864. return zone;
  1865. }
  1866. if ( (acceptableSurfaces&LOCOMOTORSURFACE_GROUND) &&
  1867. (acceptableSurfaces&LOCOMOTORSURFACE_RUBBLE)) {
  1868. // Locomotors can go on ground & rubble, so use the ground rubble combiner.
  1869. zone = m_groundRubbleZones[zone];
  1870. return zone;
  1871. }
  1872. if ( (acceptableSurfaces&LOCOMOTORSURFACE_CLIFF) &&
  1873. (acceptableSurfaces&LOCOMOTORSURFACE_WATER)) {
  1874. // Locomotors can go on ground & cliff, so use the ground cliff combiner.
  1875. DEBUG_CRASH(("Cliff water only locomotor sets not supported yet."));
  1876. }
  1877. return zone+m_firstZone;
  1878. }
  1879. /* Allocate zone equivalency arrays large enough to hold m_maxZone entries. If the arrays are already
  1880. large enough, just return. */
  1881. void ZoneBlock::allocateZones(void)
  1882. {
  1883. if (m_zonesAllocated>m_numZones && m_groundCliffZones!=NULL) {
  1884. return;
  1885. }
  1886. freeZones();
  1887. if (m_numZones==1) {
  1888. return; // we don't need any zone equivalency tables.
  1889. }
  1890. if (m_zonesAllocated == 0) {
  1891. m_zonesAllocated = 4;
  1892. }
  1893. while (m_zonesAllocated <= m_numZones) {
  1894. m_zonesAllocated *= 2;
  1895. }
  1896. // pool[]ify
  1897. m_groundCliffZones = MSGNEW("PathfindZoneInfo") UnsignedShort[m_zonesAllocated];
  1898. m_groundWaterZones = MSGNEW("PathfindZoneInfo") UnsignedShort[m_zonesAllocated];
  1899. m_groundRubbleZones = MSGNEW("PathfindZoneInfo") UnsignedShort[m_zonesAllocated];
  1900. m_crusherZones = MSGNEW("PathfindZoneInfo") UnsignedShort[m_zonesAllocated];
  1901. }
  1902. //------------------------ PathfindZoneManager -------------------------------
  1903. PathfindZoneManager::PathfindZoneManager() : m_maxZone(0),
  1904. m_needToCalculateZones(false),
  1905. m_groundCliffZones(NULL),
  1906. m_groundWaterZones(NULL),
  1907. m_groundRubbleZones(NULL),
  1908. m_terrainZones(NULL),
  1909. m_crusherZones(NULL),
  1910. m_hierarchicalZones(NULL),
  1911. m_blockOfZoneBlocks(NULL),
  1912. m_zoneBlocks(NULL),
  1913. m_zonesAllocated(0)
  1914. {
  1915. m_zoneBlockExtent.x = 0;
  1916. m_zoneBlockExtent.y = 0;
  1917. }
  1918. PathfindZoneManager::~PathfindZoneManager()
  1919. {
  1920. freeZones();
  1921. freeBlocks();
  1922. }
  1923. void PathfindZoneManager::freeZones()
  1924. {
  1925. if (m_groundCliffZones) {
  1926. delete [] m_groundCliffZones;
  1927. m_groundCliffZones = NULL;
  1928. }
  1929. if (m_groundWaterZones) {
  1930. delete [] m_groundWaterZones;
  1931. m_groundWaterZones = NULL;
  1932. }
  1933. if (m_groundRubbleZones) {
  1934. delete [] m_groundRubbleZones;
  1935. m_groundRubbleZones = NULL;
  1936. }
  1937. if (m_terrainZones) {
  1938. delete [] m_terrainZones;
  1939. m_terrainZones = NULL;
  1940. }
  1941. if (m_crusherZones) {
  1942. delete [] m_crusherZones;
  1943. m_crusherZones = NULL;
  1944. }
  1945. if (m_hierarchicalZones) {
  1946. delete [] m_hierarchicalZones;
  1947. m_hierarchicalZones = NULL;
  1948. }
  1949. m_zonesAllocated = 0;
  1950. }
  1951. void PathfindZoneManager::freeBlocks()
  1952. {
  1953. if (m_blockOfZoneBlocks) {
  1954. delete [] m_blockOfZoneBlocks;
  1955. m_blockOfZoneBlocks = NULL;
  1956. }
  1957. if (m_zoneBlocks) {
  1958. delete [] m_zoneBlocks;
  1959. m_zoneBlocks = NULL;
  1960. }
  1961. m_zoneBlockExtent.x = 0;
  1962. m_zoneBlockExtent.y = 0;
  1963. }
  1964. /* Allocate zone equivalency arrays large enough to hold m_maxZone entries. If the arrays are already
  1965. large enough, just return. */
  1966. void PathfindZoneManager::allocateZones(void)
  1967. {
  1968. if (m_zonesAllocated>m_maxZone && m_groundCliffZones!=NULL) {
  1969. return;
  1970. }
  1971. freeZones();
  1972. if (m_zonesAllocated == 0) {
  1973. m_zonesAllocated = INITIAL_ZONES;
  1974. }
  1975. while (m_zonesAllocated <= m_maxZone) {
  1976. m_zonesAllocated *= 2;
  1977. }
  1978. DEBUG_LOG(("Allocating zone tables of size %d\n", m_zonesAllocated));
  1979. // pool[]ify
  1980. m_groundCliffZones = MSGNEW("PathfindZoneInfo") UnsignedShort[m_zonesAllocated];
  1981. m_groundWaterZones = MSGNEW("PathfindZoneInfo") UnsignedShort[m_zonesAllocated];
  1982. m_groundRubbleZones = MSGNEW("PathfindZoneInfo") UnsignedShort[m_zonesAllocated];
  1983. m_terrainZones = MSGNEW("PathfindZoneInfo") UnsignedShort[m_zonesAllocated];
  1984. m_crusherZones = MSGNEW("PathfindZoneInfo") UnsignedShort[m_zonesAllocated];
  1985. m_hierarchicalZones = MSGNEW("PathfindZoneInfo") UnsignedShort[m_zonesAllocated];
  1986. }
  1987. /* Allocate zone blocks for hierarchical pathfinding. */
  1988. void PathfindZoneManager::allocateBlocks(const IRegion2D &globalBounds)
  1989. {
  1990. freeBlocks();
  1991. m_zoneBlockExtent.x = (globalBounds.hi.x-globalBounds.lo.x+1+ZONE_BLOCK_SIZE-1)/ZONE_BLOCK_SIZE;
  1992. m_zoneBlockExtent.y = (globalBounds.hi.y-globalBounds.lo.y+1+ZONE_BLOCK_SIZE-1)/ZONE_BLOCK_SIZE;
  1993. m_blockOfZoneBlocks = MSGNEW("PathfindZoneBlocks") ZoneBlock[(m_zoneBlockExtent.x)*(m_zoneBlockExtent.y)];
  1994. m_zoneBlocks = MSGNEW("PathfindZoneBlocks") ZoneBlockP[m_zoneBlockExtent.x];
  1995. Int i;
  1996. for (i=0; i<m_zoneBlockExtent.x; i++) {
  1997. m_zoneBlocks[i] = &m_blockOfZoneBlocks[i*(m_zoneBlockExtent.y)];
  1998. }
  1999. }
  2000. void PathfindZoneManager::markZonesDirty(void) ///< Called when the zones need to be recalculated.
  2001. {
  2002. m_needToCalculateZones = true;
  2003. }
  2004. void PathfindZoneManager::reset(void) ///< Called when the map is reset.
  2005. {
  2006. freeZones();
  2007. freeBlocks();
  2008. }
  2009. /**
  2010. * Calculate zones. A zone is an area of the same terrain - clear, water or cliff.
  2011. * The utility of zones is that if current location and destiontion are in the same zone,
  2012. * you can successfully pathfind.
  2013. * If you are a multiple terrain vehicle, like amphibious transport, the lookup is a little more
  2014. * complicated.
  2015. */
  2016. void PathfindZoneManager::calculateZones( PathfindCell **map, PathfindLayer layers[], const IRegion2D &globalBounds )
  2017. {
  2018. #ifdef DEBUG_QPF
  2019. #if defined(DEBUG_LOGGING)
  2020. __int64 startTime64;
  2021. double timeToUpdate=0.0f;
  2022. __int64 endTime64,freq64;
  2023. QueryPerformanceFrequency((LARGE_INTEGER *)&freq64);
  2024. QueryPerformanceCounter((LARGE_INTEGER *)&startTime64);
  2025. #endif
  2026. #endif
  2027. m_maxZone = 1; // we start using zone 0 as a flag.
  2028. const Int maxZones=24000;
  2029. UnsignedShort zoneEquivalency[maxZones];
  2030. Int i, j;
  2031. for (i=0; i<maxZones; i++) {
  2032. zoneEquivalency[i] = i;
  2033. }
  2034. for (i=0; i<=LAYER_LAST; i++) {
  2035. layers[i].setZone(0);
  2036. }
  2037. Int xCount = (globalBounds.hi.x-globalBounds.lo.x+1+ZONE_BLOCK_SIZE-1)/ZONE_BLOCK_SIZE;
  2038. Int yCount = (globalBounds.hi.y-globalBounds.lo.y+1+ZONE_BLOCK_SIZE-1)/ZONE_BLOCK_SIZE;
  2039. Int xBlock, yBlock;
  2040. for (xBlock = 0; xBlock<xCount; xBlock++) {
  2041. for (yBlock=0; yBlock<yCount; yBlock++) {
  2042. IRegion2D bounds;
  2043. bounds.lo.x = globalBounds.lo.x + xBlock*ZONE_BLOCK_SIZE;
  2044. bounds.lo.y = globalBounds.lo.y + yBlock*ZONE_BLOCK_SIZE;
  2045. bounds.hi.x = bounds.lo.x + ZONE_BLOCK_SIZE - 1; // bounds are inclusive.
  2046. bounds.hi.y = bounds.lo.y + ZONE_BLOCK_SIZE - 1; // bounds are inclusive.
  2047. if (bounds.hi.x > globalBounds.hi.x) {
  2048. bounds.hi.x = globalBounds.hi.x;
  2049. }
  2050. if (bounds.hi.y > globalBounds.hi.y) {
  2051. bounds.hi.y = globalBounds.hi.y;
  2052. }
  2053. if (bounds.lo.x>bounds.hi.x || bounds.lo.y>bounds.hi.y) {
  2054. DEBUG_CRASH(("Incorrect bounds calculation. Logic error, fix me. jba."));
  2055. continue;
  2056. }
  2057. m_zoneBlocks[xBlock][yBlock].setInteractsWithBridge(false);
  2058. for( j=bounds.lo.y; j<=bounds.hi.y; j++ ) {
  2059. for( i=bounds.lo.x; i<=bounds.hi.x; i++ ) {
  2060. PathfindCell *cell = &map[i][j];
  2061. cell->setZone(0);
  2062. if (i>bounds.lo.x) {
  2063. if (map[i][j].getType() == map[i-1][j].getType()) {
  2064. applyZone(map[i][j], map[i-1][j], zoneEquivalency, m_maxZone);
  2065. }
  2066. }
  2067. if (j>bounds.lo.y) {
  2068. if (map[i][j].getType() == map[i][j-1].getType()) {
  2069. applyZone(map[i][j], map[i][j-1], zoneEquivalency, m_maxZone);
  2070. }
  2071. }
  2072. if (cell->getZone()==0) {
  2073. cell->setZone(m_maxZone);
  2074. m_maxZone++;
  2075. if (m_maxZone>= maxZones) {
  2076. DEBUG_CRASH(("Ran out of pathfind zones. SERIOUS ERROR! jba."));
  2077. break;
  2078. }
  2079. }
  2080. if (cell->getConnectLayer() > LAYER_GROUND) {
  2081. m_zoneBlocks[xBlock][yBlock].setInteractsWithBridge(true);
  2082. }
  2083. }
  2084. }
  2085. //DEBUG_LOG(("Collapsed zones %d\n", m_maxZone));
  2086. }
  2087. }
  2088. Int totalZones = m_maxZone;
  2089. if (totalZones>maxZones/2) {
  2090. DEBUG_LOG(("Max zones %d\n", m_maxZone));
  2091. }
  2092. // Collapse the zones into a 1,2,3... sequence, removing collapsed zones.
  2093. m_maxZone = 1;
  2094. Int collapsedZones[maxZones];
  2095. collapsedZones[0] = 0;
  2096. for (i=1; i<totalZones; i++) {
  2097. Int zone = zoneEquivalency[i];
  2098. if (zone == i) {
  2099. collapsedZones[i] = m_maxZone;
  2100. ++m_maxZone;
  2101. } else {
  2102. collapsedZones[i] = collapsedZones[zone];
  2103. }
  2104. }
  2105. #ifdef DEBUG_QPF
  2106. #if defined(DEBUG_LOGGING)
  2107. QueryPerformanceCounter((LARGE_INTEGER *)&endTime64);
  2108. timeToUpdate = ((double)(endTime64-startTime64) / (double)(freq64));
  2109. DEBUG_LOG(("Time to calculate first %f\n", timeToUpdate));
  2110. #endif
  2111. #endif
  2112. // Now map the zones in the map back into the collapsed zones.
  2113. for( j=globalBounds.lo.y; j<=globalBounds.hi.y; j++ ) {
  2114. for( i=globalBounds.lo.x; i<=globalBounds.hi.x; i++ ) {
  2115. map[i][j].setZone(collapsedZones[map[i][j].getZone()]);
  2116. if (map[i][j].getZone()==0) {
  2117. DEBUG_CRASH(("Zone not set cell %d, %d", i, j));
  2118. }
  2119. }
  2120. }
  2121. for (i=0; i<=LAYER_LAST; i++) {
  2122. Int zone = collapsedZones[layers[i].getZone()];
  2123. if (zone == 0) {
  2124. zone = m_maxZone;
  2125. m_maxZone++;
  2126. }
  2127. layers[i].setZone( zone );
  2128. if (!layers[i].isUnused() && !layers[i].isDestroyed() && layers[i].getZone()==0) {
  2129. DEBUG_CRASH(("Zone not set Layer %d", i));
  2130. }
  2131. layers[i].applyZone();
  2132. if (!layers[i].isUnused() && !layers[i].isDestroyed()) {
  2133. ICoord2D ndx;
  2134. layers[i].getStartCellIndex(&ndx);
  2135. setBridge(ndx.x, ndx.y, true);
  2136. layers[i].getEndCellIndex(&ndx);
  2137. setBridge(ndx.x, ndx.y, true);
  2138. }
  2139. }
  2140. allocateZones();
  2141. DEBUG_ASSERTCRASH(xBlock==m_zoneBlockExtent.x && yBlock==m_zoneBlockExtent.y, ("Inconsistent allocation - SERIOUS ERROR. jba"));
  2142. for (xBlock=0; xBlock<xCount; xBlock++) {
  2143. for (yBlock=0; yBlock<yCount; yBlock++) {
  2144. IRegion2D bounds;
  2145. bounds.lo.x = globalBounds.lo.x + xBlock*ZONE_BLOCK_SIZE;
  2146. bounds.lo.y = globalBounds.lo.y + yBlock*ZONE_BLOCK_SIZE;
  2147. bounds.hi.x = bounds.lo.x + ZONE_BLOCK_SIZE - 1; // bounds are inclusive.
  2148. bounds.hi.y = bounds.lo.y + ZONE_BLOCK_SIZE - 1; // bounds are inclusive.
  2149. if (bounds.hi.x > globalBounds.hi.x) {
  2150. bounds.hi.x = globalBounds.hi.x;
  2151. }
  2152. if (bounds.hi.y > globalBounds.hi.y) {
  2153. bounds.hi.y = globalBounds.hi.y;
  2154. }
  2155. if (bounds.lo.x>bounds.hi.x || bounds.lo.y>bounds.hi.y) {
  2156. DEBUG_CRASH(("Incorrect bounds calculation. Logic error, fix me. jba."));
  2157. continue;
  2158. }
  2159. m_zoneBlocks[xBlock][yBlock].blockCalculateZones(map, layers, bounds);
  2160. }
  2161. }
  2162. #ifdef DEBUG_QPF
  2163. #if defined(DEBUG_LOGGING)
  2164. QueryPerformanceCounter((LARGE_INTEGER *)&endTime64);
  2165. timeToUpdate = ((double)(endTime64-startTime64) / (double)(freq64));
  2166. DEBUG_LOG(("Time to calculate second %f\n", timeToUpdate));
  2167. #endif
  2168. #endif
  2169. // Determine water/ground equivalent zones, and ground/cliff equivalent zones.
  2170. for (i=0; i<m_zonesAllocated; i++) {
  2171. m_groundCliffZones[i] = i;
  2172. m_groundWaterZones[i] = i;
  2173. m_groundRubbleZones[i] = i;
  2174. m_terrainZones[i] = i;
  2175. m_crusherZones[i] = i;
  2176. m_hierarchicalZones[i] = i;
  2177. }
  2178. for( j=globalBounds.lo.y; j<=globalBounds.hi.y; j++ ) {
  2179. for( i=globalBounds.lo.x; i<=globalBounds.hi.x; i++ ) {
  2180. if ( (map[i][j].getConnectLayer() > LAYER_GROUND) &&
  2181. (map[i][j].getType() == PathfindCell::CELL_CLEAR) ) {
  2182. PathfindLayer *layer = layers + map[i][j].getConnectLayer();
  2183. resolveZones(map[i][j].getZone(), layer->getZone(), m_hierarchicalZones, m_maxZone);
  2184. }
  2185. if (i>globalBounds.lo.x && map[i][j].getZone()!=map[i-1][j].getZone()) {
  2186. if (map[i][j].getType() == map[i-1][j].getType()) {
  2187. applyZone(map[i][j], map[i-1][j], m_hierarchicalZones, m_maxZone);
  2188. }
  2189. if (waterGround(map[i][j], map[i-1][j])) {
  2190. applyZone(map[i][j], map[i-1][j], m_groundWaterZones, m_maxZone);
  2191. }
  2192. if (groundRubble(map[i][j], map[i-1][j])) {
  2193. Int zone1 = map[i][j].getZone();
  2194. Int zone2 = map[i-1][j].getZone();
  2195. if (m_terrainZones[zone1] != m_terrainZones[zone2]) {
  2196. //DEBUG_LOG(("Matching terrain zone %d to %d.\n", zone1, zone2));
  2197. }
  2198. applyZone(map[i][j], map[i-1][j], m_groundRubbleZones, m_maxZone);
  2199. }
  2200. if (groundCliff(map[i][j], map[i-1][j])) {
  2201. applyZone(map[i][j], map[i-1][j], m_groundCliffZones, m_maxZone);
  2202. }
  2203. if (terrain(map[i][j], map[i-1][j])) {
  2204. applyZone(map[i][j], map[i-1][j], m_terrainZones, m_maxZone);
  2205. }
  2206. if (crusherGround(map[i][j], map[i-1][j])) {
  2207. applyZone(map[i][j], map[i-1][j], m_crusherZones, m_maxZone);
  2208. }
  2209. }
  2210. if (j>globalBounds.lo.y && map[i][j].getZone()!=map[i][j-1].getZone()) {
  2211. if (map[i][j].getType() == map[i][j-1].getType()) {
  2212. applyZone(map[i][j], map[i][j-1], m_hierarchicalZones, m_maxZone);
  2213. }
  2214. if (waterGround(map[i][j],map[i][j-1])) {
  2215. applyZone(map[i][j], map[i][j-1], m_groundWaterZones, m_maxZone);
  2216. }
  2217. if (groundRubble(map[i][j], map[i][j-1])) {
  2218. Int zone1 = map[i][j].getZone();
  2219. Int zone2 = map[i][j-1].getZone();
  2220. if (m_terrainZones[zone1] != m_terrainZones[zone2]) {
  2221. //DEBUG_LOG(("Matching terrain zone %d to %d.\n", zone1, zone2));
  2222. }
  2223. applyZone(map[i][j], map[i][j-1], m_groundRubbleZones, m_maxZone);
  2224. }
  2225. if (groundCliff(map[i][j],map[i][j-1])) {
  2226. applyZone(map[i][j], map[i][j-1], m_groundCliffZones, m_maxZone);
  2227. }
  2228. if (terrain(map[i][j], map[i][j-1])) {
  2229. applyZone(map[i][j], map[i][j-1], m_terrainZones, m_maxZone);
  2230. }
  2231. if (crusherGround(map[i][j], map[i][j-1])) {
  2232. applyZone(map[i][j], map[i][j-1], m_crusherZones, m_maxZone);
  2233. }
  2234. /* No diagonals. jba.
  2235. if (i>globalBounds.lo.x) {
  2236. if (waterGround(map[i][j],map[i-1][j-1])) {
  2237. applyZone(map[i][j], map[i-1][j-1], m_groundWaterZones, m_maxZone);
  2238. }
  2239. if (groundCliff(map[i][j],map[i-1][j-1])) {
  2240. applyZone(map[i][j], map[i-1][j-1], m_groundCliffZones, m_maxZone);
  2241. }
  2242. if (terrain(map[i][j],map[i-1][j-1])) {
  2243. applyZone(map[i][j], map[i-1][j-1], m_terrainZones, m_maxZone);
  2244. }
  2245. }
  2246. */
  2247. }
  2248. DEBUG_ASSERTCRASH(map[i][j].getZone() != 0, ("Cleared the zone."));
  2249. }
  2250. }
  2251. if (m_maxZone >= m_zonesAllocated) {
  2252. RELEASE_CRASH("Pathfind allocation error - fatal. see jba.");
  2253. }
  2254. for (i=1; i<m_maxZone; i++) {
  2255. // Flatten hierarchical zones.
  2256. Int zone = m_hierarchicalZones[i];
  2257. m_hierarchicalZones[i] = m_hierarchicalZones[zone];
  2258. }
  2259. flattenZones(m_groundCliffZones, m_hierarchicalZones, m_maxZone);
  2260. flattenZones(m_groundWaterZones, m_hierarchicalZones, m_maxZone);
  2261. flattenZones(m_groundRubbleZones, m_hierarchicalZones, m_maxZone);
  2262. flattenZones(m_terrainZones, m_hierarchicalZones, m_maxZone);
  2263. flattenZones(m_crusherZones, m_hierarchicalZones, m_maxZone);
  2264. #ifdef DEBUG_QPF
  2265. #if defined(DEBUG_LOGGING)
  2266. QueryPerformanceCounter((LARGE_INTEGER *)&endTime64);
  2267. timeToUpdate = ((double)(endTime64-startTime64) / (double)(freq64));
  2268. DEBUG_LOG(("Time to calculate zones %f, cells %d\n", timeToUpdate, (globalBounds.hi.x-globalBounds.lo.x)*(globalBounds.hi.y-globalBounds.lo.y)));
  2269. #endif
  2270. #endif
  2271. #if defined _DEBUG || defined _INTERNAL
  2272. if (TheGlobalData->m_debugAI && false)
  2273. {
  2274. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  2275. RGBColor color;
  2276. memset(&color, 0, sizeof(Color));
  2277. addIcon(NULL, 0, 0, color);
  2278. for( j=0; j<globalBounds.hi.y; j++ ) {
  2279. for( i=0; i<globalBounds.hi.x; i++ ) {
  2280. Int zone = map[i][j].getZone();
  2281. //zone = m_terrainZones[zone];
  2282. zone = m_groundCliffZones[zone];
  2283. color.blue = (zone%3) * 0.5f;
  2284. zone = zone/3;
  2285. color.green = (zone%3) * 0.5f;
  2286. zone = zone/3;
  2287. color.red = (zone%3) * 0.5;
  2288. Coord3D pos;
  2289. pos.x = ((Real)i + 0.5f) * PATHFIND_CELL_SIZE_F;
  2290. pos.y = ((Real)j + 0.5f) * PATHFIND_CELL_SIZE_F;
  2291. pos.z = TheTerrainLogic->getLayerHeight( pos.x, pos.y, map[i][j].getLayer() ) + 0.5f;
  2292. addIcon(&pos, PATHFIND_CELL_SIZE_F*0.8f, 500, color);
  2293. }
  2294. }
  2295. }
  2296. #endif
  2297. m_needToCalculateZones = false;
  2298. }
  2299. //
  2300. // Clear the passable flags.
  2301. //
  2302. void PathfindZoneManager::clearPassableFlags( )
  2303. { Int blockX;
  2304. Int blockY;
  2305. for (blockX = 0; blockX<m_zoneBlockExtent.x; blockX++) {
  2306. for (blockY = 0; blockY<m_zoneBlockExtent.y; blockY++) {
  2307. m_zoneBlocks[blockX][blockY].setPassable(false);
  2308. }
  2309. }
  2310. }
  2311. //
  2312. // Set the passable flags.
  2313. //
  2314. void PathfindZoneManager::setAllPassable( )
  2315. { Int blockX;
  2316. Int blockY;
  2317. for (blockX = 0; blockX<m_zoneBlockExtent.x; blockX++) {
  2318. for (blockY = 0; blockY<m_zoneBlockExtent.y; blockY++) {
  2319. m_zoneBlocks[blockX][blockY].setPassable(true);
  2320. }
  2321. }
  2322. }
  2323. //
  2324. // Set the passable flag for the block at this location.
  2325. //
  2326. void PathfindZoneManager::setPassable(Int cellX, Int cellY, Bool passable)
  2327. {
  2328. Int blockX = cellX/ZONE_BLOCK_SIZE;
  2329. Int blockY = cellY/ZONE_BLOCK_SIZE;
  2330. if (blockX<0 || blockX>=m_zoneBlockExtent.x) {
  2331. DEBUG_CRASH(("Invalid block."));
  2332. return;
  2333. }
  2334. if (blockY<0 || blockY>=m_zoneBlockExtent.y) {
  2335. DEBUG_CRASH(("Invalid block."));
  2336. return;
  2337. }
  2338. m_zoneBlocks[blockX][blockY].setPassable(passable);
  2339. }
  2340. //
  2341. // Get the passable flag for the block at this location.
  2342. //
  2343. Bool PathfindZoneManager::isPassable(Int cellX, Int cellY) const
  2344. {
  2345. Int blockX = cellX/ZONE_BLOCK_SIZE;
  2346. Int blockY = cellY/ZONE_BLOCK_SIZE;
  2347. if (blockX<0 || blockX>=m_zoneBlockExtent.x) {
  2348. DEBUG_CRASH(("Invalid block."));
  2349. return false;
  2350. }
  2351. if (blockY<0 || blockY>=m_zoneBlockExtent.y) {
  2352. DEBUG_CRASH(("Invalid block."));
  2353. return false;
  2354. }
  2355. return m_zoneBlocks[blockX][blockY].isPassable();
  2356. }
  2357. //
  2358. // Get the passable flag for the block at this location.
  2359. //
  2360. Bool PathfindZoneManager::clipIsPassable(Int cellX, Int cellY) const
  2361. {
  2362. Int blockX = cellX/ZONE_BLOCK_SIZE;
  2363. Int blockY = cellY/ZONE_BLOCK_SIZE;
  2364. if (blockX<0 || blockX>=m_zoneBlockExtent.x) {
  2365. return false;
  2366. }
  2367. if (blockY<0 || blockY>=m_zoneBlockExtent.y) {
  2368. return false;
  2369. }
  2370. return m_zoneBlocks[blockX][blockY].isPassable();
  2371. }
  2372. //
  2373. // Set the bridge flag for the block at this location.
  2374. //
  2375. void PathfindZoneManager::setBridge(Int cellX, Int cellY, Bool bridge)
  2376. {
  2377. Int blockX = cellX/ZONE_BLOCK_SIZE;
  2378. Int blockY = cellY/ZONE_BLOCK_SIZE;
  2379. if (blockX<0 || blockX>=m_zoneBlockExtent.x) {
  2380. // DEBUG_CRASH(("Invalid block.")); Bridges can be off the playable grid, so don't crash. jba.
  2381. return;
  2382. }
  2383. if (blockY<0 || blockY>=m_zoneBlockExtent.y) {
  2384. // DEBUG_CRASH(("Invalid block.")); Bridges can be off the playable grid, so don't crash. jba.
  2385. return;
  2386. }
  2387. m_zoneBlocks[blockX][blockY].setInteractsWithBridge(bridge);
  2388. }
  2389. //
  2390. // Set the bridge flag for the block at this location.
  2391. //
  2392. Bool PathfindZoneManager::interactsWithBridge(Int cellX, Int cellY) const
  2393. {
  2394. Int blockX = cellX/ZONE_BLOCK_SIZE;
  2395. Int blockY = cellY/ZONE_BLOCK_SIZE;
  2396. if (blockX<0 || blockX>=m_zoneBlockExtent.x) {
  2397. DEBUG_CRASH(("Invalid block."));
  2398. return false;
  2399. }
  2400. if (blockY<0 || blockY>=m_zoneBlockExtent.y) {
  2401. DEBUG_CRASH(("Invalid block."));
  2402. return false;
  2403. }
  2404. return m_zoneBlocks[blockX][blockY].getInteractsWithBridge();
  2405. }
  2406. //
  2407. // Return the zone at this location.
  2408. //
  2409. UnsignedShort PathfindZoneManager::getBlockZone(LocomotorSurfaceTypeMask acceptableSurfaces, Bool crusher,Int cellX, Int cellY, PathfindCell **map) const
  2410. {
  2411. PathfindCell *cell = &(map[cellX][cellY]);
  2412. Int blockX = cellX/ZONE_BLOCK_SIZE;
  2413. Int blockY = cellY/ZONE_BLOCK_SIZE;
  2414. if (blockX<0 || blockX>=m_zoneBlockExtent.x) {
  2415. DEBUG_CRASH(("Invalid block."));
  2416. return 0;
  2417. }
  2418. if (blockY<0 || blockY>=m_zoneBlockExtent.y) {
  2419. DEBUG_CRASH(("Invalid block."));
  2420. return 0;
  2421. }
  2422. UnsignedShort zone = m_zoneBlocks[blockX][blockY].getEffectiveZone(acceptableSurfaces, crusher, cell->getZone());
  2423. if (zone > m_maxZone) {
  2424. DEBUG_CRASH(("Invalid zone."));
  2425. return 0;
  2426. }
  2427. return zone;
  2428. }
  2429. //
  2430. // Return the zone at this location.
  2431. //
  2432. UnsignedShort PathfindZoneManager::getEffectiveTerrainZone(UnsignedShort zone) const
  2433. {
  2434. return m_hierarchicalZones[m_terrainZones[zone]];
  2435. }
  2436. //
  2437. // Return the zone at this location.
  2438. //
  2439. UnsignedShort PathfindZoneManager::getEffectiveZone( LocomotorSurfaceTypeMask acceptableSurfaces,
  2440. Bool crusher, UnsignedShort zone) const
  2441. {
  2442. //DEBUG_ASSERTCRASH(zone, ("Zone not set"));
  2443. if (zone>m_maxZone) {
  2444. DEBUG_CRASH(("Invalid zone"));
  2445. return (0);
  2446. }
  2447. if (zone>m_maxZone) {
  2448. DEBUG_CRASH(("Invalid zone"));
  2449. return (0);
  2450. }
  2451. if (acceptableSurfaces&LOCOMOTORSURFACE_AIR) return 1; // air is all zone 1.
  2452. if ( (acceptableSurfaces&LOCOMOTORSURFACE_GROUND) &&
  2453. (acceptableSurfaces&LOCOMOTORSURFACE_WATER) &&
  2454. (acceptableSurfaces&LOCOMOTORSURFACE_CLIFF)) {
  2455. // Locomotors can go on ground, water & cliff, so all is zone 1.
  2456. return 1;
  2457. }
  2458. if (crusher) {
  2459. zone = m_crusherZones[zone];
  2460. }
  2461. if ( (acceptableSurfaces&LOCOMOTORSURFACE_GROUND) &&
  2462. (acceptableSurfaces&LOCOMOTORSURFACE_CLIFF)) {
  2463. // Locomotors can go on ground & cliff, so use the ground cliff combiner.
  2464. zone = m_groundCliffZones[zone];
  2465. return zone;
  2466. }
  2467. if ( (acceptableSurfaces&LOCOMOTORSURFACE_GROUND) &&
  2468. (acceptableSurfaces&LOCOMOTORSURFACE_WATER)) {
  2469. // Locomotors can go on ground & water, so use the ground water combiner.
  2470. zone = m_groundWaterZones[zone];
  2471. return zone;
  2472. }
  2473. if ( (acceptableSurfaces&LOCOMOTORSURFACE_GROUND) &&
  2474. (acceptableSurfaces&LOCOMOTORSURFACE_RUBBLE)) {
  2475. // Locomotors can go on ground & rubble, so use the ground rubble combiner.
  2476. zone = m_groundRubbleZones[zone];
  2477. return zone;
  2478. }
  2479. if ( (acceptableSurfaces&LOCOMOTORSURFACE_CLIFF) &&
  2480. (acceptableSurfaces&LOCOMOTORSURFACE_WATER)) {
  2481. // Locomotors can go on ground & cliff, so use the ground cliff combiner.
  2482. DEBUG_CRASH(("Cliff water only locomotor sets not supported yet."));
  2483. }
  2484. zone = m_hierarchicalZones[zone];
  2485. return zone;
  2486. }
  2487. //-------------------- PathfindLayer ----------------------------------------
  2488. PathfindLayer::PathfindLayer() : m_blockOfMapCells(NULL), m_layerCells(NULL), m_bridge(NULL),
  2489. // Added By Sadullah Nader
  2490. // Initializations inserted
  2491. m_destroyed(FALSE),
  2492. m_height(0),
  2493. m_width(0),
  2494. m_xOrigin(0),
  2495. m_yOrigin(0),
  2496. m_zone(0)
  2497. //
  2498. {
  2499. m_startCell.x = -1;
  2500. m_startCell.y = -1;
  2501. m_endCell.x = -1;
  2502. m_endCell.y = -1;
  2503. }
  2504. PathfindLayer::~PathfindLayer()
  2505. {
  2506. reset();
  2507. }
  2508. /**
  2509. * Returns true if the layer is avaialble for use.
  2510. */
  2511. void PathfindLayer::reset(void)
  2512. {
  2513. m_bridge = NULL;
  2514. if (m_layerCells) {
  2515. Int i, j;
  2516. for (i=0; i<m_width; i++) {
  2517. for (j=0; j<m_height; j++) {
  2518. PathfindCell *cell = &m_layerCells[i][j];
  2519. cell->reset();
  2520. }
  2521. }
  2522. delete [] m_layerCells;
  2523. m_layerCells = NULL;
  2524. }
  2525. if (m_blockOfMapCells) {
  2526. delete [] m_blockOfMapCells;
  2527. m_blockOfMapCells = NULL;
  2528. }
  2529. m_width = 0;
  2530. m_height = 0;
  2531. m_xOrigin = 0;
  2532. m_yOrigin = 0;
  2533. m_startCell.x = -1;
  2534. m_startCell.y = -1;
  2535. m_endCell.x = -1;
  2536. m_endCell.y = -1;
  2537. m_layer = LAYER_GROUND;
  2538. }
  2539. /**
  2540. * Returns true if the layer is avaialble for use.
  2541. */
  2542. Bool PathfindLayer::isUnused(void)
  2543. {
  2544. // Special case - wall layer is built from not a bridge. jba.
  2545. if (m_layer == LAYER_WALL && m_width>0) return false;
  2546. if (m_bridge==NULL) return true;
  2547. return false;
  2548. }
  2549. /**
  2550. * Draws debug cell info.
  2551. */
  2552. #if defined _DEBUG || defined _INTERNAL
  2553. void PathfindLayer::doDebugIcons(void) {
  2554. if (isUnused()) return;
  2555. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  2556. // render AI debug information
  2557. {
  2558. Coord3D topLeftCorner;
  2559. RGBColor color;
  2560. color.red = color.green = color.blue = 0;
  2561. Coord3D center;
  2562. center.x = (m_xOrigin+m_width/2)*PATHFIND_CELL_SIZE_F;
  2563. center.y = (m_yOrigin+m_height/2)*PATHFIND_CELL_SIZE_F;
  2564. center.z = 0;
  2565. Real bridgeHeight = TheTerrainLogic->getLayerHeight(center.x , center.y, m_layer);
  2566. if (m_layer == LAYER_WALL) {
  2567. bridgeHeight = TheAI->pathfinder()->getWallHeight();
  2568. }
  2569. Bool showCells = TheGlobalData->m_debugAI==AI_DEBUG_CELLS;
  2570. // show the pathfind grid
  2571. for( int j=0; j<m_height; j++ )
  2572. {
  2573. topLeftCorner.y = (Real)(j+m_yOrigin) * PATHFIND_CELL_SIZE_F;
  2574. for( int i=0; i<m_width; i++ )
  2575. {
  2576. topLeftCorner.x = (Real)(i+m_xOrigin) * PATHFIND_CELL_SIZE_F;
  2577. color.red = color.green = color.blue = 0;
  2578. Bool empty = true;
  2579. const PathfindCell *cell = &m_layerCells[i][j];
  2580. if (cell)
  2581. {
  2582. if (cell->getConnectLayer()==LAYER_GROUND) {
  2583. color.green = 1;
  2584. color.blue = 1;
  2585. empty = false;
  2586. } else if (cell->getType() == PathfindCell::CELL_IMPASSABLE) {
  2587. color.red = color.green = color.blue = 1;
  2588. empty = false;
  2589. } else if (cell->getType() == PathfindCell::CELL_CLIFF) {
  2590. color.red = 1;
  2591. empty = false;
  2592. }
  2593. }
  2594. if (showCells) {
  2595. empty = true;
  2596. color.red = color.green = color.blue = 0;
  2597. if (empty && cell) {
  2598. if (cell->getFlags()!=PathfindCell::NO_UNITS) {
  2599. empty = false;
  2600. if (cell->getFlags() == PathfindCell::UNIT_GOAL) {
  2601. color.red = 1;
  2602. } else if (cell->getFlags() == PathfindCell::UNIT_PRESENT_FIXED) {
  2603. color.green = color.blue = color.red = 1;
  2604. } else if (cell->getFlags() == PathfindCell::UNIT_PRESENT_MOVING) {
  2605. color.green = 1;
  2606. } else {
  2607. color.green = color.red = 1;
  2608. }
  2609. }
  2610. }
  2611. }
  2612. if (!empty) {
  2613. Coord3D loc;
  2614. loc.x = topLeftCorner.x + PATHFIND_CELL_SIZE_F/2.0f;
  2615. loc.y = topLeftCorner.y + PATHFIND_CELL_SIZE_F/2.0f;
  2616. loc.z = bridgeHeight;
  2617. addIcon(&loc, PATHFIND_CELL_SIZE_F*0.8f, 99, color);
  2618. }
  2619. }
  2620. }
  2621. }
  2622. }
  2623. #endif
  2624. /**
  2625. * Sets the bridge & layer number for a layer.
  2626. */
  2627. Bool PathfindLayer::init(Bridge *theBridge, PathfindLayerEnum layer)
  2628. {
  2629. if (m_bridge!=NULL) return false;
  2630. m_bridge = theBridge;
  2631. m_layer = layer;
  2632. m_destroyed = false;
  2633. return true;
  2634. }
  2635. /**
  2636. * Allocates the pathfind cells for the bridge layer.
  2637. */
  2638. void PathfindLayer::allocateCells(const IRegion2D *extent)
  2639. {
  2640. if (m_bridge == NULL) return;
  2641. Region2D bridgeBounds = *m_bridge->getBounds();
  2642. Int maxX, maxY;
  2643. m_xOrigin = REAL_TO_INT_FLOOR((bridgeBounds.lo.x-PATHFIND_CELL_SIZE/100)/PATHFIND_CELL_SIZE);
  2644. m_yOrigin = REAL_TO_INT_FLOOR((bridgeBounds.lo.y-PATHFIND_CELL_SIZE/100)/PATHFIND_CELL_SIZE);
  2645. m_width = 0;
  2646. m_height = 0;
  2647. maxX = REAL_TO_INT_CEIL((bridgeBounds.hi.x+PATHFIND_CELL_SIZE/100)/PATHFIND_CELL_SIZE);
  2648. maxY = REAL_TO_INT_CEIL((bridgeBounds.hi.y+PATHFIND_CELL_SIZE/100)/PATHFIND_CELL_SIZE);
  2649. // Pad with 1 extra;
  2650. m_xOrigin--;
  2651. m_yOrigin--;
  2652. maxX++;
  2653. maxY++;
  2654. if (m_xOrigin < extent->lo.x) m_xOrigin = extent->lo.x;
  2655. if (m_yOrigin < extent->lo.y) m_yOrigin = extent->lo.y;
  2656. if (maxX > extent->hi.x) maxX = extent->hi.x;
  2657. if (maxY > extent->hi.y) maxY = extent->hi.y;
  2658. if (maxX <= m_xOrigin) return;
  2659. if (maxY <= m_yOrigin) return;
  2660. m_width = maxX - m_xOrigin;
  2661. m_height = maxY - m_yOrigin;
  2662. // Allocate cells.
  2663. // pool[]ify
  2664. m_blockOfMapCells = MSGNEW("PathfindMapCells") PathfindCell[m_width*m_height];
  2665. m_layerCells = MSGNEW("PathfindMapCells") PathfindCellP[m_width];
  2666. Int i;
  2667. for (i=0; i<m_width; i++) {
  2668. m_layerCells[i] = &m_blockOfMapCells[i*m_height];
  2669. }
  2670. }
  2671. /**
  2672. * Allocates the pathfind cells for the wall bridge layer.
  2673. */
  2674. void PathfindLayer::allocateCellsForWallLayer(const IRegion2D *extent, ObjectID *wallPieces, Int numPieces)
  2675. {
  2676. DEBUG_ASSERTCRASH(m_layer==LAYER_WALL, ("Wrong layer for wall."));
  2677. if (m_layer != LAYER_WALL) return;
  2678. Region2D bridgeBounds;
  2679. Int i;
  2680. Bool first = true;
  2681. for (i=0; i<numPieces; i++) {
  2682. Object *obj = TheGameLogic->findObjectByID(wallPieces[i]);
  2683. Region2D objBounds;
  2684. if (obj==NULL) continue;
  2685. obj->getGeometryInfo().get2DBounds(*obj->getPosition(), obj->getOrientation(), objBounds);
  2686. if (first) {
  2687. bridgeBounds = objBounds;
  2688. first = false;
  2689. } else {
  2690. if (bridgeBounds.lo.x>objBounds.lo.x) bridgeBounds.lo.x = objBounds.lo.x;
  2691. if (bridgeBounds.lo.y>objBounds.lo.y) bridgeBounds.lo.y = objBounds.lo.y;
  2692. if (bridgeBounds.hi.x<objBounds.hi.x) bridgeBounds.hi.x = objBounds.hi.x;
  2693. if (bridgeBounds.hi.y<objBounds.hi.y) bridgeBounds.hi.y = objBounds.hi.y;
  2694. }
  2695. }
  2696. Int maxX, maxY;
  2697. m_xOrigin = REAL_TO_INT_FLOOR((bridgeBounds.lo.x-PATHFIND_CELL_SIZE/100)/PATHFIND_CELL_SIZE);
  2698. m_yOrigin = REAL_TO_INT_FLOOR((bridgeBounds.lo.y-PATHFIND_CELL_SIZE/100)/PATHFIND_CELL_SIZE);
  2699. m_width = 0;
  2700. m_height = 0;
  2701. maxX = REAL_TO_INT_CEIL((bridgeBounds.hi.x+PATHFIND_CELL_SIZE/100)/PATHFIND_CELL_SIZE);
  2702. maxY = REAL_TO_INT_CEIL((bridgeBounds.hi.y+PATHFIND_CELL_SIZE/100)/PATHFIND_CELL_SIZE);
  2703. // Pad with 1 extra;
  2704. m_xOrigin--;
  2705. m_yOrigin--;
  2706. maxX++;
  2707. maxY++;
  2708. if (m_xOrigin < extent->lo.x) m_xOrigin = extent->lo.x;
  2709. if (m_yOrigin < extent->lo.y) m_yOrigin = extent->lo.y;
  2710. if (maxX > extent->hi.x) maxX = extent->hi.x;
  2711. if (maxY > extent->hi.y) maxY = extent->hi.y;
  2712. if (maxX <= m_xOrigin) return;
  2713. if (maxY <= m_yOrigin) return;
  2714. m_width = maxX - m_xOrigin;
  2715. m_height = maxY - m_yOrigin;
  2716. // Allocate cells.
  2717. m_blockOfMapCells = MSGNEW("PathfindMapCells") PathfindCell[m_width*m_height];
  2718. m_layerCells = MSGNEW("PathfindMapCells") PathfindCellP[m_width];
  2719. for (i=0; i<m_width; i++) {
  2720. m_layerCells[i] = &m_blockOfMapCells[i*m_height];
  2721. }
  2722. }
  2723. /**
  2724. * Checks to see if a broken bridge connects 2 zones.
  2725. */
  2726. Bool PathfindLayer::connectsZones(PathfindZoneManager *zm, const LocomotorSet& locoSet,
  2727. Int zone1, Int zone2)
  2728. {
  2729. if (!m_destroyed) {
  2730. return false;
  2731. }
  2732. Bool found1 = false;
  2733. Bool found2 = false;
  2734. Int i, j;
  2735. for (i=0; i<m_width; i++) {
  2736. for (j=0; j<m_height; j++) {
  2737. PathfindCell *cell = &m_layerCells[i][j];
  2738. if (cell->getConnectLayer()==LAYER_GROUND) {
  2739. PathfindCell *groundCell = TheAI->pathfinder()->getCell(LAYER_GROUND, i+m_xOrigin, j+m_yOrigin);
  2740. DEBUG_ASSERTCRASH(groundCell, ("Should have cell."));
  2741. if (groundCell) {
  2742. UnsignedShort zone = zm->getEffectiveZone(locoSet.getValidSurfaces(),
  2743. true, groundCell->getZone());
  2744. zone = zm->getEffectiveTerrainZone(zone);
  2745. if (zone == zone1) found1 = true;
  2746. if (zone == zone2) found2 = true;
  2747. }
  2748. }
  2749. }
  2750. }
  2751. return found1 && found2;
  2752. }
  2753. /**
  2754. * Classifies the pathfind cells for the bridge layer.
  2755. */
  2756. void PathfindLayer::classifyCells()
  2757. {
  2758. m_startCell.x = -1;
  2759. m_startCell.y = -1;
  2760. m_endCell.x = -1;
  2761. m_endCell.y = -1;
  2762. Int i, j;
  2763. for (i=0; i<m_width; i++) {
  2764. for (j=0; j<m_height; j++) {
  2765. PathfindCell *cell = &m_layerCells[i][j];
  2766. cell->setConnectLayer(LAYER_INVALID);
  2767. cell->setLayer(m_layer);
  2768. classifyLayerMapCell(i+m_xOrigin, j+m_yOrigin, cell, m_bridge);
  2769. }
  2770. BridgeInfo info;
  2771. m_bridge->getBridgeInfo(&info);
  2772. Coord3D bridgeDir = info.to;
  2773. bridgeDir.x -= info.from.x;
  2774. bridgeDir.y -= info.from.y;
  2775. bridgeDir.z -= info.from.z;
  2776. bridgeDir.normalize();
  2777. bridgeDir.x *= PATHFIND_CELL_SIZE_F*0.7f;
  2778. bridgeDir.y *= PATHFIND_CELL_SIZE_F*0.7f;
  2779. m_startCell.x = REAL_TO_INT_FLOOR((info.from.x-bridgeDir.x) / PATHFIND_CELL_SIZE_F);
  2780. m_startCell.y = REAL_TO_INT_FLOOR((info.from.y-bridgeDir.y) / PATHFIND_CELL_SIZE_F);
  2781. m_endCell.x = REAL_TO_INT_FLOOR((info.to.x+bridgeDir.x) / PATHFIND_CELL_SIZE_F);
  2782. m_endCell.y = REAL_TO_INT_FLOOR((info.to.y+bridgeDir.y) / PATHFIND_CELL_SIZE_F);
  2783. }
  2784. if (m_destroyed) {
  2785. Int i, j;
  2786. for (i=0; i<m_width; i++) {
  2787. for (j=0; j<m_height; j++) {
  2788. PathfindCell *cell = &m_layerCells[i][j];
  2789. if (cell->getConnectLayer() == LAYER_GROUND) {
  2790. PathfindCell *groundCell = TheAI->pathfinder()->getCell(LAYER_GROUND, i+m_xOrigin, j+m_yOrigin);
  2791. DEBUG_ASSERTCRASH(groundCell, ("Should have cell."));
  2792. if (groundCell) {
  2793. DEBUG_ASSERTCRASH(groundCell->getConnectLayer()==m_layer, ("Should connect to this layer.jba."));
  2794. groundCell->setConnectLayer(LAYER_INVALID); // disconnect it.
  2795. }
  2796. }
  2797. cell->setType(PathfindCell::CELL_IMPASSABLE);
  2798. }
  2799. }
  2800. }
  2801. }
  2802. /**
  2803. * Classifies the pathfind cells for the wall bridge layer.
  2804. */
  2805. void PathfindLayer::classifyWallCells(ObjectID *wallPieces, Int numPieces)
  2806. {
  2807. DEBUG_ASSERTCRASH(m_layer==LAYER_WALL, ("Wrong layer for wall."));
  2808. if (m_layer != LAYER_WALL) return;
  2809. if (m_layerCells == NULL) return;
  2810. Int i, j;
  2811. for (i=0; i<m_width; i++) {
  2812. for (j=0; j<m_height; j++) {
  2813. PathfindCell *cell = &m_layerCells[i][j];
  2814. cell->setConnectLayer(LAYER_INVALID);
  2815. cell->setLayer(m_layer);
  2816. classifyWallMapCell(i+m_xOrigin, j+m_yOrigin, cell, wallPieces, numPieces);
  2817. cell->setPinched(false);
  2818. }
  2819. }
  2820. if (m_destroyed) {
  2821. Int i, j;
  2822. for (i=0; i<m_width; i++) {
  2823. for (j=0; j<m_height; j++) {
  2824. PathfindCell *cell = &m_layerCells[i][j];
  2825. if (cell->getConnectLayer() == LAYER_GROUND) {
  2826. PathfindCell *groundCell = TheAI->pathfinder()->getCell(LAYER_GROUND, i+m_xOrigin, j+m_yOrigin);
  2827. DEBUG_ASSERTCRASH(groundCell, ("Should have cell."));
  2828. if (groundCell) {
  2829. DEBUG_ASSERTCRASH(groundCell->getConnectLayer()==m_layer, ("Should connect to this layer.jba."));
  2830. groundCell->setConnectLayer(LAYER_INVALID); // disconnect it.
  2831. }
  2832. }
  2833. cell->setType(PathfindCell::CELL_IMPASSABLE);
  2834. }
  2835. }
  2836. }
  2837. // Tighten up 1 cell.
  2838. for (i=1; i<m_width-1; i++) {
  2839. for (j=1; j<m_height-1; j++) {
  2840. PathfindCell *cell = &m_layerCells[i][j];
  2841. Int k, l;
  2842. for (k=i-1; k<i+2; k++) {
  2843. for (l=j-1; l<j+2; l++) {
  2844. PathfindCell *adjacentCell = &m_layerCells[k][l];
  2845. if (adjacentCell->getType() != PathfindCell::CELL_CLEAR) {
  2846. cell->setPinched(true);
  2847. }
  2848. }
  2849. }
  2850. }
  2851. }
  2852. for (i=0; i<m_width; i++) {
  2853. for (j=0; j<m_height; j++) {
  2854. PathfindCell *cell = &m_layerCells[i][j];
  2855. if (cell->getPinched() && cell->getType() == PathfindCell::CELL_CLEAR) {
  2856. cell->setType(PathfindCell::CELL_CLIFF);
  2857. }
  2858. cell->setPinched(false);
  2859. }
  2860. }
  2861. }
  2862. /**
  2863. * Relassifies the pathfind cells for the destroyed bridge layer.
  2864. */
  2865. Bool PathfindLayer::setDestroyed(Bool destroyed)
  2866. {
  2867. if (destroyed == m_destroyed) return false;
  2868. m_destroyed = destroyed;
  2869. classifyCells();
  2870. return true;
  2871. }
  2872. /**
  2873. * Copies m_zone into the zone for all the member cells.
  2874. */
  2875. void PathfindLayer::applyZone( void )
  2876. {
  2877. Int i, j;
  2878. for (i=0; i<m_width; i++) {
  2879. for (j=0; j<m_height; j++) {
  2880. PathfindCell *cell = &m_layerCells[i][j];
  2881. cell->setZone(m_zone);
  2882. }
  2883. }
  2884. }
  2885. /**
  2886. * Return the bridge's object id.
  2887. */
  2888. ObjectID PathfindLayer::getBridgeID(void)
  2889. {
  2890. return m_bridge->peekBridgeInfo()->bridgeObjectID;
  2891. }
  2892. /**
  2893. * Return the cell at the index location.
  2894. */
  2895. PathfindCell *PathfindLayer::getCell(Int x, Int y)
  2896. {
  2897. DEBUG_ASSERTCRASH(m_layerCells, ("no data in layer, why get cells?"));
  2898. if (m_layerCells==NULL) {
  2899. return NULL;
  2900. }
  2901. x -= m_xOrigin;
  2902. y -= m_yOrigin;
  2903. if (x<0 || x>=m_width) return NULL;
  2904. if (y<0 || y>=m_height) return NULL;
  2905. PathfindCell *cell = &m_layerCells[x][y];
  2906. if (cell->getType() == PathfindCell::CELL_IMPASSABLE) {
  2907. return NULL; // Impassable cells are ignored.
  2908. }
  2909. return cell;
  2910. }
  2911. /**
  2912. * Classify the given map cell as clear, or not, etc.
  2913. */
  2914. void PathfindLayer::classifyLayerMapCell( Int i, Int j , PathfindCell *cell, Bridge *theBridge)
  2915. {
  2916. Coord3D topLeftCorner, bottomRightCorner;
  2917. topLeftCorner.y = (Real)j * PATHFIND_CELL_SIZE_F;
  2918. bottomRightCorner.y = topLeftCorner.y + PATHFIND_CELL_SIZE_F;
  2919. topLeftCorner.x = (Real)i * PATHFIND_CELL_SIZE_F;
  2920. bottomRightCorner.x = topLeftCorner.x + PATHFIND_CELL_SIZE_F;
  2921. Int bridgeCount = 0;
  2922. Coord3D pt;
  2923. if (theBridge->isPointOnBridge(&topLeftCorner) ) {
  2924. bridgeCount++;
  2925. }
  2926. pt = topLeftCorner;
  2927. pt.y = bottomRightCorner.y;
  2928. if (theBridge->isPointOnBridge(&pt) ) {
  2929. bridgeCount++;
  2930. }
  2931. if (theBridge->isPointOnBridge(&bottomRightCorner) ) {
  2932. bridgeCount++;
  2933. }
  2934. pt = topLeftCorner;
  2935. pt.x = bottomRightCorner.x;
  2936. if (theBridge->isPointOnBridge(&pt) ) {
  2937. bridgeCount++;
  2938. }
  2939. cell->reset( );
  2940. cell->setLayer(m_layer);
  2941. cell->setType(PathfindCell::CELL_IMPASSABLE);
  2942. if (bridgeCount == 4) {
  2943. cell->setType(PathfindCell::CELL_CLEAR);
  2944. } else {
  2945. if (bridgeCount!=0) {
  2946. cell->setType(PathfindCell::CELL_CLIFF); // it's off the bridge.
  2947. }
  2948. // check against the end lines.
  2949. Region2D cellBounds;
  2950. cellBounds.lo.x = topLeftCorner.x;
  2951. cellBounds.lo.y = topLeftCorner.y;
  2952. cellBounds.hi.x = bottomRightCorner.x;
  2953. cellBounds.hi.y = bottomRightCorner.y;
  2954. if (m_bridge->isCellOnEnd(&cellBounds)) {
  2955. cell->setType(PathfindCell::CELL_CLEAR);
  2956. }
  2957. if (m_bridge->isCellOnSide(&cellBounds)) {
  2958. cell->setType(PathfindCell::CELL_CLIFF);
  2959. } else {
  2960. if (m_bridge->isCellEntryPoint(&cellBounds)) {
  2961. cell->setType(PathfindCell::CELL_CLEAR);
  2962. cell->setConnectLayer(LAYER_GROUND);
  2963. PathfindCell *groundCell = TheAI->pathfinder()->getCell(LAYER_GROUND, i, j );
  2964. groundCell->setConnectLayer(cell->getLayer());
  2965. }
  2966. }
  2967. }
  2968. Coord3D center = topLeftCorner;
  2969. center.x += PATHFIND_CELL_SIZE/2;
  2970. center.y += PATHFIND_CELL_SIZE/2;
  2971. if (cell->getType()!=PathfindCell::CELL_IMPASSABLE) {
  2972. if (!(cell->getConnectLayer()==LAYER_GROUND) ) {
  2973. // Check for bridge clearance. If the ground isn't 1 pathfind cells below, mark impassable.
  2974. Real groundHeight = TheTerrainLogic->getLayerHeight( center.x, center.y, LAYER_GROUND );
  2975. Real bridgeHeight = theBridge->getBridgeHeight( &center, NULL );
  2976. if (groundHeight+LAYER_Z_CLOSE_ENOUGH_F > bridgeHeight) {
  2977. PathfindCell *groundCell = TheAI->pathfinder()->getCell(LAYER_GROUND,i, j);
  2978. if (!(groundCell->getType()==PathfindCell::CELL_OBSTACLE)) {
  2979. groundCell->setType(PathfindCell::CELL_IMPASSABLE);
  2980. }
  2981. }
  2982. }
  2983. }
  2984. return;
  2985. }
  2986. Bool PathfindLayer::isPointOnWall(ObjectID *wallPieces, Int numPieces, const Coord3D *pt)
  2987. {
  2988. Int i;
  2989. for (i=0; i<numPieces; i++) {
  2990. Object *obj = TheGameLogic->findObjectByID(wallPieces[i]);
  2991. if (obj==NULL) continue;
  2992. Real major = obj->getGeometryInfo().getMajorRadius();
  2993. Real minor = (obj->getGeometryInfo().getGeomType() == GEOMETRY_SPHERE) ? obj->getGeometryInfo().getMajorRadius() : obj->getGeometryInfo().getMinorRadius();
  2994. Real c = (Real)Cos(-obj->getOrientation());
  2995. Real s = (Real)Sin(-obj->getOrientation());
  2996. // convert to a delta relative to rect ctr
  2997. Real ptx = pt->x - obj->getPosition()->x;
  2998. Real pty = pt->y - obj->getPosition()->y;
  2999. // inverse-rotate it to the right coord system
  3000. Real ptx_new = (Real)fabs(ptx*c - pty*s);
  3001. Real pty_new = (Real)fabs(ptx*s + pty*c);
  3002. if (ptx_new <= major && pty_new <= minor)
  3003. {
  3004. return true;
  3005. }
  3006. }
  3007. return false;
  3008. }
  3009. /**
  3010. * Classify the given map cell as clear, or not, etc.
  3011. */
  3012. void PathfindLayer::classifyWallMapCell( Int i, Int j , PathfindCell *cell, ObjectID *wallPieces, Int numPieces)
  3013. {
  3014. Coord3D topLeftCorner, bottomRightCorner;
  3015. topLeftCorner.y = (Real)j * PATHFIND_CELL_SIZE_F;
  3016. bottomRightCorner.y = topLeftCorner.y + PATHFIND_CELL_SIZE_F;
  3017. topLeftCorner.x = (Real)i * PATHFIND_CELL_SIZE_F;
  3018. bottomRightCorner.x = topLeftCorner.x + PATHFIND_CELL_SIZE_F;
  3019. Int bridgeCount = 0;
  3020. Coord3D pt;
  3021. if (isPointOnWall(wallPieces, numPieces, &topLeftCorner) ) {
  3022. bridgeCount++;
  3023. }
  3024. pt = topLeftCorner;
  3025. pt.y = bottomRightCorner.y;
  3026. if (isPointOnWall(wallPieces, numPieces, &pt) ) {
  3027. bridgeCount++;
  3028. }
  3029. if (isPointOnWall(wallPieces, numPieces, &bottomRightCorner) ) {
  3030. bridgeCount++;
  3031. }
  3032. pt = topLeftCorner;
  3033. pt.x = bottomRightCorner.x;
  3034. if (isPointOnWall(wallPieces, numPieces, &pt) ) {
  3035. bridgeCount++;
  3036. }
  3037. cell->reset( );
  3038. cell->setLayer(m_layer);
  3039. cell->setType(PathfindCell::CELL_IMPASSABLE);
  3040. if (bridgeCount == 4) {
  3041. cell->setType(PathfindCell::CELL_CLEAR);
  3042. } else {
  3043. if (bridgeCount!=0) {
  3044. cell->setType(PathfindCell::CELL_CLIFF); // it's off the bridge.
  3045. }
  3046. }
  3047. }
  3048. //----------------------- Pathfinder ---------------------------------------
  3049. Pathfinder::Pathfinder( void ) :m_map(NULL)
  3050. {
  3051. debugPath = NULL;
  3052. PathfindCellInfo::allocateCellInfos();
  3053. reset();
  3054. }
  3055. Pathfinder::~Pathfinder( void )
  3056. {
  3057. PathfindCellInfo::releaseCellInfos();
  3058. }
  3059. void Pathfinder::reset( void )
  3060. {
  3061. frameToShowObstacles = 0;
  3062. DEBUG_LOG(("Pathfind cell is %d bytes, PathfindCellInfo is %d bytes\n", sizeof(PathfindCell), sizeof(PathfindCellInfo)));
  3063. if (m_blockOfMapCells) {
  3064. delete []m_blockOfMapCells;
  3065. m_blockOfMapCells = NULL;
  3066. }
  3067. if (m_map) {
  3068. delete [] m_map;
  3069. m_map = NULL;
  3070. }
  3071. Int i;
  3072. for (i=0; i<=LAYER_LAST; i++) {
  3073. m_layers[i].reset();
  3074. }
  3075. // reset the pathfind grid
  3076. m_extent.lo.x=m_extent.lo.y=m_extent.hi.x=m_extent.hi.y=0;
  3077. m_logicalExtent.lo.x=m_logicalExtent.lo.y=m_logicalExtent.hi.x=m_logicalExtent.hi.y=0;
  3078. m_openList = NULL;
  3079. m_closedList = NULL;
  3080. m_ignoreObstacleID = INVALID_ID;
  3081. m_isTunneling = false;
  3082. m_moveAlliesDepth = 0;
  3083. // pathfind grid cells have not been classified yet
  3084. m_isMapReady = false;
  3085. m_cumulativeCellsAllocated = 0;
  3086. debugPathPos.x = 0.0f;
  3087. debugPathPos.y = 0.0f;
  3088. debugPathPos.z = 0.0f;
  3089. if (debugPath)
  3090. debugPath->deleteInstance();
  3091. debugPath = NULL;
  3092. m_frameToShowObstacles = 0;
  3093. for (m_queuePRHead=0; m_queuePRHead<PATHFIND_QUEUE_LEN; m_queuePRHead++) {
  3094. m_queuedPathfindRequests[m_queuePRHead] = INVALID_ID;
  3095. }
  3096. m_queuePRHead = 0;
  3097. m_queuePRTail = 0;
  3098. m_numWallPieces = 0;
  3099. for (i=0; i<MAX_WALL_PIECES; ++i)
  3100. {
  3101. m_wallPieces[i] = INVALID_ID;
  3102. }
  3103. if (TheAI && TheAI->getAiData()) {
  3104. m_wallHeight = TheAI->getAiData()->m_wallHeight;
  3105. }
  3106. else
  3107. {
  3108. m_wallHeight = 0.0f;
  3109. }
  3110. m_zoneManager.reset();
  3111. }
  3112. /**
  3113. * Adds a piece of a wall.
  3114. */
  3115. void Pathfinder::addWallPiece(Object *wallPiece)
  3116. {
  3117. if (m_numWallPieces<MAX_WALL_PIECES-1) {
  3118. m_wallPieces[m_numWallPieces] = wallPiece->getID();
  3119. m_numWallPieces++;
  3120. }
  3121. }
  3122. /**
  3123. * Removes a piece of a wall
  3124. */
  3125. void Pathfinder::removeWallPiece(Object *wallPiece)
  3126. {
  3127. // sanity
  3128. if( wallPiece == NULL )
  3129. return;
  3130. // find entry
  3131. for( Int i = 0; i < m_numWallPieces; ++i )
  3132. {
  3133. // match by id
  3134. if( m_wallPieces[ i ] == wallPiece->getID() )
  3135. {
  3136. // put the last id in the wall piece array here
  3137. m_wallPieces[ i ] = m_wallPieces[ m_numWallPieces - 1 ];
  3138. // we now have one less entry
  3139. m_numWallPieces--;
  3140. // all done
  3141. return;
  3142. } // end if
  3143. } // end for, i
  3144. } // end removeWallPiece
  3145. /**
  3146. * Checks if a point is on the wall.
  3147. */
  3148. Bool Pathfinder::isPointOnWall(const Coord3D *pos)
  3149. {
  3150. if (m_numWallPieces==0) return false;
  3151. if (m_layers[LAYER_WALL].isUnused()) return false;
  3152. PathfindLayerEnum layer = (PathfindLayerEnum)LAYER_WALL;
  3153. PathfindCell *cell = getCell(layer, pos);
  3154. // make sure the layer matches, since getCell can return ground layer cells if the pos is 'off' the bridge/wall
  3155. if (cell && cell->getLayer() == layer) {
  3156. if (cell->getType() == PathfindCell::CELL_CLEAR) {
  3157. return true;
  3158. }
  3159. }
  3160. return false;
  3161. }
  3162. /**
  3163. * Adds a bridge & returns the layer.
  3164. */
  3165. PathfindLayerEnum Pathfinder::addBridge(Bridge *theBridge)
  3166. {
  3167. Int layer = LAYER_GROUND+1;
  3168. while (layer<=LAYER_WALL) {
  3169. if (m_layers[layer].isUnused()) {
  3170. if (m_layers[layer].init(theBridge, (PathfindLayerEnum)layer) ) {
  3171. return (PathfindLayerEnum)layer;
  3172. }
  3173. DEBUG_LOG(("WARNING: Bridge failed to init in pathfinder\n"));
  3174. return LAYER_GROUND; // failed to init, usually cause off of the map. jba.
  3175. }
  3176. layer++;
  3177. }
  3178. DEBUG_CRASH(("Ran out of bridge layers."));
  3179. return LAYER_GROUND;
  3180. }
  3181. /**
  3182. * Updates an object's layer, making sure the object is actually on the bridge first.
  3183. */
  3184. void Pathfinder::updateLayer(Object *obj, PathfindLayerEnum layer)
  3185. {
  3186. if (layer != LAYER_GROUND) {
  3187. if (!TheTerrainLogic->objectInteractsWithBridgeLayer(obj, layer)) {
  3188. layer = LAYER_GROUND;
  3189. }
  3190. }
  3191. //DEBUG_LOG(("Object layer is %d\n", layer));
  3192. obj->setLayer(layer);
  3193. }
  3194. /**
  3195. * Classify the cells under the given object
  3196. * If 'insert' is true, object is being added
  3197. * If 'insert' is false, object is being removed
  3198. */
  3199. void Pathfinder::classifyFence( Object *obj, Bool insert )
  3200. {
  3201. m_zoneManager.markZonesDirty();
  3202. const Coord3D *pos = obj->getPosition();
  3203. Real angle = obj->getOrientation();
  3204. Real halfsizeX = obj->getTemplate()->getFenceWidth()/2;
  3205. Real halfsizeY = PATHFIND_CELL_SIZE_F/10.0f;
  3206. Real fenceOffset = obj->getTemplate()->getFenceXOffset();
  3207. Real c = (Real)Cos(angle);
  3208. Real s = (Real)Sin(angle);
  3209. 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
  3210. Real ydx = s * STEP_SIZE;
  3211. Real ydy = -c * STEP_SIZE;
  3212. Real xdx = c * STEP_SIZE;
  3213. Real xdy = s * STEP_SIZE;
  3214. Int numStepsX = REAL_TO_INT_CEIL(2.0f * halfsizeX / STEP_SIZE);
  3215. Int numStepsY = REAL_TO_INT_CEIL(2.0f * halfsizeY / STEP_SIZE);
  3216. Real tl_x = pos->x - fenceOffset*c - halfsizeY*s;
  3217. Real tl_y = pos->y + halfsizeY*c - fenceOffset*s;
  3218. for (Int iy = 0; iy < numStepsY; ++iy, tl_x += ydx, tl_y += ydy)
  3219. {
  3220. Real x = tl_x;
  3221. Real y = tl_y;
  3222. for (Int ix = 0; ix < numStepsX; ++ix, x += xdx, y += xdy)
  3223. {
  3224. Int cx = REAL_TO_INT_FLOOR((x + 0.5f)/PATHFIND_CELL_SIZE_F);
  3225. Int cy = REAL_TO_INT_FLOOR((y + 0.5f)/PATHFIND_CELL_SIZE_F);
  3226. if (cx >= 0 && cy >= 0 && cx < m_extent.hi.x && cy < m_extent.hi.y)
  3227. {
  3228. if (insert) {
  3229. ICoord2D pos;
  3230. pos.x = cx;
  3231. pos.y = cy;
  3232. m_map[cx][cy].setTypeAsObstacle( obj, true, pos );
  3233. }
  3234. else
  3235. m_map[cx][cy].removeObstacle(obj);
  3236. }
  3237. }
  3238. }
  3239. #if 0
  3240. // Perhaps it would make more sense to use the iteratecellsalongpath() provided in this class,
  3241. // but this way works well and is very traceable
  3242. // neither one is true Bresenham
  3243. // this one assumes zero fence thickness
  3244. const Coord3D *fencePos = obj->getPosition();
  3245. Coord3D fenceNorm;
  3246. obj->getUnitDirectionVector2D( fenceNorm );
  3247. Coord3D halfLength = fenceNorm;
  3248. halfLength.scale( obj->getGeometryInfo().getMajorRadius() );
  3249. Coord3D head = *fencePos;
  3250. head.add( &halfLength );
  3251. Coord3D tail = *fencePos;
  3252. tail.sub( &halfLength );
  3253. Real stepLength = 1.0f / ((halfLength.length()*2.0f) / PATHFIND_CELL_SIZE_F);
  3254. for ( Real t = 0.0f; t <= 1.0f; t += stepLength )
  3255. {
  3256. Real lengthWalk_x = (head.x * t) + (tail.x * (1.0f-t));
  3257. Real lengthWalk_y = (head.y * t) + (tail.y * (1.0f-t));
  3258. Int cx = REAL_TO_INT_FLOOR( lengthWalk_x / PATHFIND_CELL_SIZE_F );
  3259. Int cy = REAL_TO_INT_FLOOR( lengthWalk_y / PATHFIND_CELL_SIZE_F );
  3260. if (cx >= 0 && cy >= 0 && cx < m_extent.hi.x && cy < m_extent.hi.y)
  3261. {
  3262. if (insert) {
  3263. ICoord2D pos = { cx, cy };
  3264. m_map[cx][cy].setTypeAsObstacle( obj, true, pos );
  3265. }
  3266. else
  3267. m_map[cx][cy].removeObstacle(obj);
  3268. }
  3269. }
  3270. #endif
  3271. }
  3272. /**
  3273. * Classify the cells under the given object
  3274. * If 'insert' is true, object is being added
  3275. * If 'insert' is false, object is being removed
  3276. */
  3277. void Pathfinder::classifyObjectFootprint( Object *obj, Bool insert )
  3278. {
  3279. if (obj->isKindOf(KINDOF_MINE)) {
  3280. return; // don't pathfind around mines.
  3281. }
  3282. if (obj->isKindOf(KINDOF_PROJECTILE)) {
  3283. return; // don't care about projectiles.
  3284. }
  3285. if (obj->isKindOf(KINDOF_BRIDGE_TOWER)) {
  3286. return; // It is important to not abuse bridge towers.
  3287. }
  3288. if (obj->getTemplate()->getFenceWidth() > 0.0f)
  3289. {
  3290. if (!obj->isKindOf(KINDOF_DEFENSIVE_WALL))
  3291. {
  3292. classifyFence(obj, insert);
  3293. return;
  3294. }
  3295. }
  3296. if (!insert) {
  3297. // Just in case, remove the object. Remove checks that the object has been added before
  3298. // removing, so it's safer to just remove it, as by the time some units "die", they've become
  3299. // lifeless immobile husks of debris, but we still need to remove them. jba.
  3300. removeUnitFromPathfindMap(obj);
  3301. if (obj->isKindOf(KINDOF_WALK_ON_TOP_OF_WALL)) {
  3302. if (!m_layers[LAYER_WALL].isUnused()) {
  3303. Int i;
  3304. ObjectID curID = obj->getID();
  3305. for (i=0; i<m_numWallPieces; i++) {
  3306. if (curID == m_wallPieces[i]) {
  3307. m_wallPieces[i]=INVALID_ID;
  3308. }
  3309. }
  3310. // Kill anybody on the wall.
  3311. Object *obj;
  3312. for (obj = TheGameLogic->getFirstObject(); obj; obj=obj->getNextObject()) {
  3313. if (obj->getLayer() == LAYER_WALL) {
  3314. if (m_layers[LAYER_WALL].isPointOnWall(&curID, 1, obj->getPosition()))
  3315. {
  3316. // The object fell off the wall.
  3317. // Destroy it.
  3318. DamageInfo extraDamageInfo;
  3319. extraDamageInfo.in.m_damageType = DAMAGE_FALLING;
  3320. extraDamageInfo.in.m_deathType = DEATH_SPLATTED;
  3321. extraDamageInfo.in.m_sourceID = obj->getID();
  3322. extraDamageInfo.in.m_amount = HUGE_DAMAGE_AMOUNT;
  3323. obj->attemptDamage(&extraDamageInfo);
  3324. }
  3325. }
  3326. }
  3327. // recalc the wall.
  3328. m_layers[LAYER_WALL].classifyWallCells(m_wallPieces, m_numWallPieces);
  3329. }
  3330. }
  3331. }
  3332. if (!obj->isKindOf(KINDOF_STRUCTURE)) {
  3333. return; // Only path around structures.
  3334. }
  3335. if (obj->isMobile()) {
  3336. return; // mobile aren't obstacles.
  3337. }
  3338. /// For now, all small objects will not be obstacles
  3339. if (obj->getGeometryInfo().getIsSmall()) {
  3340. return;
  3341. }
  3342. if (obj->getHeightAboveTerrain() > PATHFIND_CELL_SIZE_F) {
  3343. return; // Don't add bounds that are up in the air.
  3344. }
  3345. internal_classifyObjectFootprint(obj, insert);
  3346. }
  3347. void Pathfinder::internal_classifyObjectFootprint( Object *obj, Bool insert )
  3348. {
  3349. switch(obj->getGeometryInfo().getGeomType())
  3350. {
  3351. case GEOMETRY_BOX:
  3352. {
  3353. m_zoneManager.markZonesDirty();
  3354. const Coord3D *pos = obj->getPosition();
  3355. Real angle = obj->getOrientation();
  3356. Real halfsizeX = obj->getGeometryInfo().getMajorRadius();
  3357. Real halfsizeY = obj->getGeometryInfo().getMinorRadius();
  3358. Real c = (Real)Cos(angle);
  3359. Real s = (Real)Sin(angle);
  3360. 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
  3361. Real ydx = s * STEP_SIZE;
  3362. Real ydy = -c * STEP_SIZE;
  3363. Real xdx = c * STEP_SIZE;
  3364. Real xdy = s * STEP_SIZE;
  3365. Int numStepsX = REAL_TO_INT_CEIL(2.0f * halfsizeX / STEP_SIZE);
  3366. Int numStepsY = REAL_TO_INT_CEIL(2.0f * halfsizeY / STEP_SIZE);
  3367. Real tl_x = pos->x - halfsizeX*c - halfsizeY*s;
  3368. Real tl_y = pos->y + halfsizeY*c - halfsizeX*s;
  3369. for (Int iy = 0; iy < numStepsY; ++iy, tl_x += ydx, tl_y += ydy)
  3370. {
  3371. Real x = tl_x;
  3372. Real y = tl_y;
  3373. for (Int ix = 0; ix < numStepsX; ++ix, x += xdx, y += xdy)
  3374. {
  3375. Int cx = REAL_TO_INT_FLOOR((x + 0.5f)/PATHFIND_CELL_SIZE_F);
  3376. Int cy = REAL_TO_INT_FLOOR((y + 0.5f)/PATHFIND_CELL_SIZE_F);
  3377. if (cx >= 0 && cy >= 0 && cx < m_extent.hi.x && cy < m_extent.hi.y)
  3378. {
  3379. if (insert) {
  3380. ICoord2D pos;
  3381. pos.x = cx;
  3382. pos.y = cy;
  3383. m_map[cx][cy].setTypeAsObstacle( obj, false, pos );
  3384. }
  3385. else
  3386. m_map[cx][cy].removeObstacle(obj);
  3387. }
  3388. }
  3389. }
  3390. }
  3391. break;
  3392. case GEOMETRY_SPHERE: // not quite right, but close enough
  3393. case GEOMETRY_CYLINDER:
  3394. {
  3395. m_zoneManager.markZonesDirty();
  3396. // fill in all cells that overlap as obstacle cells
  3397. /// @todo This is a very inefficient circle-rasterizer
  3398. ICoord2D topLeft, bottomRight;
  3399. Coord2D center, delta;
  3400. const Coord3D *pos = obj->getPosition();
  3401. Real radius = obj->getGeometryInfo().getMajorRadius();
  3402. Real r2, size;
  3403. topLeft.x = REAL_TO_INT_FLOOR(0.5f + (pos->x - radius)/PATHFIND_CELL_SIZE_F)-1;
  3404. topLeft.y = REAL_TO_INT_FLOOR(0.5f + (pos->y - radius)/PATHFIND_CELL_SIZE_F)-1;
  3405. size = (radius/PATHFIND_CELL_SIZE_F);
  3406. center.x = (pos->x/PATHFIND_CELL_SIZE_F);
  3407. center.y = (pos->y/PATHFIND_CELL_SIZE_F);
  3408. size += 0.4f;
  3409. r2 = size*size;
  3410. bottomRight.x = topLeft.x + 2*size + 2;
  3411. bottomRight.y = topLeft.y + 2*size + 2;
  3412. for( int j = topLeft.y; j < bottomRight.y; j++ )
  3413. {
  3414. for( int i = topLeft.x; i < bottomRight.x; i++ )
  3415. {
  3416. delta.x = i+0.5f - center.x;
  3417. delta.y = j+0.5f - center.y;
  3418. if (delta.x*delta.x + delta.y*delta.y <= r2)
  3419. {
  3420. if (i >= 0 && j >= 0 && i < m_extent.hi.x && j < m_extent.hi.y)
  3421. {
  3422. if (insert) {
  3423. ICoord2D pos;
  3424. pos.x = i;
  3425. pos.y = j;
  3426. m_map[i][j].setTypeAsObstacle( obj, false, pos );
  3427. }
  3428. else
  3429. m_map[i][j].removeObstacle( obj );
  3430. }
  3431. }
  3432. } // for i
  3433. } // for j
  3434. } // cylinder
  3435. break;
  3436. } // switch
  3437. Region2D bounds;
  3438. Int i, j;
  3439. obj->getGeometryInfo().get2DBounds(*obj->getPosition(), obj->getOrientation(), bounds);
  3440. IRegion2D cellBounds;
  3441. cellBounds.lo.x = REAL_TO_INT_FLOOR(bounds.lo.x/PATHFIND_CELL_SIZE_F)-1;
  3442. cellBounds.lo.y = REAL_TO_INT_FLOOR(bounds.lo.y/PATHFIND_CELL_SIZE_F)-1;
  3443. cellBounds.hi.x = REAL_TO_INT_CEIL(bounds.hi.x/PATHFIND_CELL_SIZE_F)+1;
  3444. cellBounds.hi.y = REAL_TO_INT_CEIL(bounds.hi.y/PATHFIND_CELL_SIZE_F)+1;
  3445. if (cellBounds.lo.x < m_extent.lo.x) {
  3446. cellBounds.lo.x = m_extent.lo.x;
  3447. }
  3448. if (cellBounds.lo.y < m_extent.lo.y) {
  3449. cellBounds.lo.y = m_extent.lo.y;
  3450. }
  3451. if (cellBounds.lo.y < m_extent.lo.y) {
  3452. cellBounds.lo.y = m_extent.lo.y;
  3453. }
  3454. if (cellBounds.hi.x > m_extent.hi.x) {
  3455. cellBounds.hi.x = m_extent.hi.x;
  3456. }
  3457. if (cellBounds.hi.y > m_extent.hi.y) {
  3458. cellBounds.hi.y = m_extent.hi.y;
  3459. }
  3460. // Expand building bounds 1 cell.
  3461. #define no_EXPAND_ONE_CELL
  3462. #ifdef EXPAND_ONE_CELL
  3463. for( j=cellBounds.lo.y; j<=cellBounds.hi.y; j++ )
  3464. {
  3465. for( i=cellBounds.lo.x; i<=cellBounds.hi.x; i++ )
  3466. {
  3467. if (!insert) {
  3468. if (m_map[i][j].getType() == PathfindCell::CELL_IMPASSABLE) {
  3469. m_map[i][j].setType(PathfindCell::CELL_CLEAR);
  3470. }
  3471. m_map[i][j].setPinched(false);
  3472. }
  3473. if (!insert) {
  3474. if (m_map[i][j].isObstaclePresent(obj->getID())) {
  3475. m_map[i][j].removeObstacle( obj );
  3476. }
  3477. continue;
  3478. }
  3479. if (m_map[i][j].getType() == PathfindCell::CELL_CLEAR) {
  3480. Bool obstacleAdjacent = false;
  3481. Int k, l;
  3482. for (k=i-1; k<i+2; k++) {
  3483. if (k<m_extent.lo.x || k> m_extent.hi.x) continue;
  3484. for (l=j-1; l<j+2; l++) {
  3485. if (l<m_extent.lo.y || l> m_extent.hi.y) continue;
  3486. if ((k==i) && (l==j)) continue;
  3487. if ((k!=i) && (l!=j)) continue;
  3488. if (m_map[k][l].getType()!=PathfindCell::CELL_CLEAR)) {
  3489. objectAdjacent = true;
  3490. break;
  3491. }
  3492. }
  3493. }
  3494. if (obstacleAdjacent) {
  3495. m_map[i][j].setPinched(true);
  3496. }
  3497. // If the total open cells are < 2
  3498. }
  3499. }
  3500. }
  3501. if (insert) {
  3502. for( j=cellBounds.lo.y; j<=cellBounds.hi.y; j++ )
  3503. {
  3504. for( i=cellBounds.lo.x; i<=cellBounds.hi.x; i++ )
  3505. {
  3506. if (m_map[i][j].getPinched() && m_map[i][j].getType() == PathfindCell::CELL_CLEAR) {
  3507. ICoord2D pos;
  3508. pos.x = i;
  3509. pos.y = j;
  3510. m_map[i][j].setTypeAsObstacle( obj, false, pos );
  3511. //m_map[i][j].setType(PathfindCell::CELL_CLIFF);
  3512. m_map[i][j].setPinched(false);
  3513. }
  3514. }
  3515. }
  3516. }
  3517. #endif
  3518. if (!insert) {
  3519. for( j=cellBounds.lo.y; j<=cellBounds.hi.y; j++ )
  3520. {
  3521. for( i=cellBounds.lo.x; i<=cellBounds.hi.x; i++ )
  3522. {
  3523. if (m_map[i][j].getType()==PathfindCell::CELL_IMPASSABLE) {
  3524. m_map[i][j].setType(PathfindCell::CELL_CLEAR);
  3525. }
  3526. }
  3527. }
  3528. }
  3529. // Check for pinched cells, and close them off.
  3530. for( j=cellBounds.lo.y; j<=cellBounds.hi.y; j++ )
  3531. {
  3532. for( i=cellBounds.lo.x; i<=cellBounds.hi.x; i++ )
  3533. {
  3534. m_map[i][j].setPinched(false);
  3535. if (m_map[i][j].getType() == PathfindCell::CELL_CLEAR) {
  3536. Int totalCount = 0;
  3537. Int orthogonalCount = 0;
  3538. Int k, l;
  3539. for (k=i-1; k<i+2; k++) {
  3540. if (k<m_extent.lo.x || k> m_extent.hi.x) continue;
  3541. for (l=j-1; l<j+2; l++) {
  3542. if (l<m_extent.lo.y || l> m_extent.hi.y) continue;
  3543. if ((k==i) && (j==l)) continue;
  3544. if (m_map[k][l].getType() == PathfindCell::CELL_CLEAR) {
  3545. totalCount++;
  3546. if ((k==i) || (l==j)) {
  3547. orthogonalCount++;
  3548. }
  3549. }
  3550. }
  3551. }
  3552. // If the total open cells are < 2 or total cells < 4, we are pinched.
  3553. if (orthogonalCount<2 || totalCount<4) {
  3554. m_map[i][j].setPinched(true);
  3555. }
  3556. }
  3557. }
  3558. }
  3559. for( j=cellBounds.lo.y; j<=cellBounds.hi.y; j++ )
  3560. {
  3561. for( i=cellBounds.lo.x; i<=cellBounds.hi.x; i++ )
  3562. {
  3563. if (m_map[i][j].getPinched() && (m_map[i][j].getType() == PathfindCell::CELL_CLEAR)) {
  3564. m_map[i][j].setType(PathfindCell::CELL_IMPASSABLE);
  3565. m_map[i][j].setPinched(false);
  3566. }
  3567. }
  3568. }
  3569. // Expand building bounds 1 cell.
  3570. #define MARK_BORDER_PINCHED
  3571. #ifdef MARK_BORDER_PINCHED
  3572. for( j=cellBounds.lo.y; j<=cellBounds.hi.y; j++ )
  3573. {
  3574. for( i=cellBounds.lo.x; i<=cellBounds.hi.x; i++ )
  3575. {
  3576. if (m_map[i][j].getType() == PathfindCell::CELL_CLEAR) {
  3577. Bool objectAdjacent = false;
  3578. Int k, l;
  3579. for (k=i-1; k<i+2; k++) {
  3580. if (k<m_extent.lo.x || k> m_extent.hi.x) continue;
  3581. for (l=j-1; l<j+2; l++) {
  3582. if (l<m_extent.lo.y || l> m_extent.hi.y) continue;
  3583. if ((k==i) && (l==j)) continue;
  3584. if ((k!=i) && (l!=j)) continue;
  3585. if (m_map[k][l].getType() == PathfindCell::CELL_OBSTACLE) {
  3586. objectAdjacent = true;
  3587. break;
  3588. }
  3589. }
  3590. }
  3591. if (objectAdjacent) {
  3592. m_map[i][j].setPinched(true);
  3593. }
  3594. }
  3595. }
  3596. }
  3597. #endif
  3598. }
  3599. /**
  3600. * Classify the given map cell as WATER, CLIFF, etc.
  3601. * Note that this does NOT classify cells as OBSTACLES.
  3602. * OBSTACLE cells are classified only via objects.
  3603. * @todo optimize this - lots of redundant computation
  3604. */
  3605. void Pathfinder::classifyMapCell( Int i, Int j , PathfindCell *cell)
  3606. {
  3607. Coord3D topLeftCorner, bottomRightCorner;
  3608. Bool hasObstacle = (cell->getType() == PathfindCell::CELL_OBSTACLE) ;
  3609. topLeftCorner.y = (Real)j * PATHFIND_CELL_SIZE_F;
  3610. bottomRightCorner.y = topLeftCorner.y + PATHFIND_CELL_SIZE_F;
  3611. topLeftCorner.x = (Real)i * PATHFIND_CELL_SIZE_F;
  3612. bottomRightCorner.x = topLeftCorner.x + PATHFIND_CELL_SIZE_F;
  3613. cell->setPinched(false);
  3614. PathfindCell::CellType type = PathfindCell::CELL_CLEAR;
  3615. if (TheTerrainLogic->isCliffCell(topLeftCorner.x, topLeftCorner.y))
  3616. {
  3617. type = PathfindCell::CELL_CLIFF;
  3618. }
  3619. //
  3620. // If any corners are underwater, this is a water cell
  3621. //
  3622. if (TheTerrainLogic->isUnderwater( topLeftCorner.x, topLeftCorner.y ) ) type = PathfindCell::CELL_WATER;
  3623. if (TheTerrainLogic->isUnderwater( topLeftCorner.x, bottomRightCorner.y) ) type = PathfindCell::CELL_WATER;
  3624. if (TheTerrainLogic->isUnderwater( bottomRightCorner.x, bottomRightCorner.y ) ) type = PathfindCell::CELL_WATER;
  3625. if (TheTerrainLogic->isUnderwater( bottomRightCorner.x, topLeftCorner.y ) ) type = PathfindCell::CELL_WATER;
  3626. if (hasObstacle) {
  3627. type = PathfindCell::CELL_OBSTACLE;
  3628. }
  3629. cell->setType( type );
  3630. cell->releaseInfo();
  3631. }
  3632. /**
  3633. * Set up for a new map.
  3634. */
  3635. void Pathfinder::newMap( void )
  3636. {
  3637. m_wallHeight = TheAI->getAiData()->m_wallHeight; // may be updated by map.ini.
  3638. Region3D terrainExtent;
  3639. TheTerrainLogic->getMaximumPathfindExtent( &terrainExtent );
  3640. IRegion2D bounds;
  3641. bounds.lo.x = REAL_TO_INT_FLOOR(terrainExtent.lo.x / PATHFIND_CELL_SIZE_F);
  3642. bounds.hi.x = REAL_TO_INT_FLOOR(terrainExtent.hi.x / PATHFIND_CELL_SIZE_F);
  3643. bounds.lo.y = REAL_TO_INT_FLOOR(terrainExtent.lo.y / PATHFIND_CELL_SIZE_F);
  3644. bounds.hi.y = REAL_TO_INT_FLOOR(terrainExtent.hi.y / PATHFIND_CELL_SIZE_F);
  3645. bounds.hi.x--;
  3646. bounds.hi.y--;
  3647. Bool dataAllocated = false;
  3648. if (m_extent.hi.x==bounds.hi.x && m_extent.hi.y==bounds.hi.y) {
  3649. if (m_blockOfMapCells != NULL && m_map!=NULL) {
  3650. dataAllocated = true;
  3651. }
  3652. }
  3653. // For map load from file, we have to call newMap twice to do sequencing issues.
  3654. // so the second time through, dataAllocated==TRUE, so we skip the allocate.
  3655. if (!dataAllocated) {
  3656. m_extent = bounds;
  3657. DEBUG_ASSERTCRASH(m_map == NULL, ("Can't reallocate pathfind cells."));
  3658. m_zoneManager.allocateBlocks(m_extent);
  3659. // Allocate cells.
  3660. m_blockOfMapCells = MSGNEW("PathfindMapCells") PathfindCell[(bounds.hi.x+1)*(bounds.hi.y+1)];
  3661. m_map = MSGNEW("PathfindMapCells") PathfindCellP[bounds.hi.x+1];
  3662. Int i;
  3663. for (i=0; i<=bounds.hi.x; i++) {
  3664. m_map[i] = &m_blockOfMapCells[i*(bounds.hi.y+1)];
  3665. }
  3666. for (i=0; i<LAYER_LAST; i++) {
  3667. if (!m_layers[i].isUnused()) {
  3668. m_layers[i].allocateCells(&m_extent);
  3669. }
  3670. }
  3671. if (m_numWallPieces>0) {
  3672. m_layers[LAYER_WALL].init(NULL, LAYER_WALL);
  3673. m_layers[LAYER_WALL].allocateCellsForWallLayer(&m_extent, m_wallPieces, m_numWallPieces);
  3674. }
  3675. }
  3676. classifyMap();
  3677. // Add existing objects.
  3678. Object *obj;
  3679. for( obj = TheGameLogic->getFirstObject(); obj; obj = obj->getNextObject() )
  3680. {
  3681. classifyObjectFootprint(obj, true);
  3682. }
  3683. m_isMapReady = true;
  3684. }
  3685. /**
  3686. * Classify all cells in grid as obstacles, etc.
  3687. */
  3688. void Pathfinder::classifyMap(void)
  3689. {
  3690. Int i, j;
  3691. // for now, sample cell corners and classify cell accordingly
  3692. for( j=m_extent.lo.y; j<=m_extent.hi.y; j++ )
  3693. {
  3694. for( i=m_extent.lo.x; i<=m_extent.hi.x; i++ )
  3695. {
  3696. classifyMapCell( i, j, &m_map[i][j]);
  3697. }
  3698. }
  3699. #if 1
  3700. // Expand all cliff cells one step (mark pinched)
  3701. for( j=m_extent.lo.y; j<=m_extent.hi.y; j++ )
  3702. {
  3703. for( i=m_extent.lo.x; i<=m_extent.hi.x; i++ )
  3704. {
  3705. if (m_map[i][j].getType() & PathfindCell::CELL_CLIFF) {
  3706. Int k, l;
  3707. for (k=i-1; k<i+2; k++) {
  3708. if (k<m_extent.lo.x || k> m_extent.hi.x) continue;
  3709. for (l=j-1; l<j+2; l++) {
  3710. if (l<m_extent.lo.y || l> m_extent.hi.y) continue;
  3711. if (m_map[k][l].getType() == PathfindCell::CELL_CLEAR) {
  3712. m_map[k][l].setPinched(true);
  3713. }
  3714. }
  3715. }
  3716. }
  3717. }
  3718. }
  3719. // Convert pinched to cliff.
  3720. for( j=m_extent.lo.y; j<=m_extent.hi.y; j++ )
  3721. {
  3722. for( i=m_extent.lo.x; i<=m_extent.hi.x; i++ )
  3723. {
  3724. if (m_map[i][j].getPinched()) {
  3725. if (m_map[i][j].getType()==PathfindCell::CELL_CLEAR) {
  3726. m_map[i][j].setType(PathfindCell::CELL_CLIFF);
  3727. }
  3728. }
  3729. }
  3730. }
  3731. // Add a border of pinched cells to cliffs.
  3732. for( j=m_extent.lo.y; j<=m_extent.hi.y; j++ )
  3733. {
  3734. for( i=m_extent.lo.x; i<=m_extent.hi.x; i++ )
  3735. {
  3736. if (m_map[i][j].getType() & PathfindCell::CELL_CLIFF) {
  3737. Int k, l;
  3738. for (k=i-1; k<i+2; k++) {
  3739. if (k<m_extent.lo.x || k> m_extent.hi.x) continue;
  3740. for (l=j-1; l<j+2; l++) {
  3741. if (l<m_extent.lo.y || l> m_extent.hi.y) continue;
  3742. if (m_map[k][l].getType() == PathfindCell::CELL_CLEAR) {
  3743. m_map[k][l].setPinched(true);
  3744. }
  3745. }
  3746. }
  3747. }
  3748. }
  3749. }
  3750. #endif
  3751. for (i=0; i<LAYER_LAST; i++) {
  3752. if (!m_layers[i].isUnused()) {
  3753. m_layers[i].classifyCells();
  3754. }
  3755. }
  3756. if (!m_layers[LAYER_WALL].isUnused()) {
  3757. m_layers[LAYER_WALL].classifyWallCells(m_wallPieces, m_numWallPieces);
  3758. }
  3759. m_zoneManager.calculateZones(m_map, m_layers, m_extent);
  3760. }
  3761. /**
  3762. * Force pathfind map recomputation.
  3763. */
  3764. void Pathfinder::forceMapRecalculation( void )
  3765. {
  3766. classifyMap( );
  3767. }
  3768. /**
  3769. * Show all cells touched in the last search
  3770. */
  3771. void Pathfinder::debugShowSearch( Bool pathFound )
  3772. {
  3773. if (!TheGlobalData->m_debugAI) {
  3774. return;
  3775. }
  3776. #if defined _DEBUG || defined _INTERNAL
  3777. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  3778. // show all explored cells for debugging
  3779. PathfindCell *s;
  3780. RGBColor color;
  3781. color.red = color.blue = color.green = 1;
  3782. if (!pathFound) {
  3783. addIcon(NULL, 0, 0, color); // erase.
  3784. }
  3785. for( s = m_openList; s; s=s->getNextOpen() )
  3786. {
  3787. // create objects to show path - they decay
  3788. RGBColor color;
  3789. color.red = color.green = 0;
  3790. color.blue = 1;
  3791. Coord3D pos;
  3792. pos.x = ((Real)s->getXIndex() + 0.5f) * PATHFIND_CELL_SIZE_F;
  3793. pos.y = ((Real)s->getYIndex() + 0.5f) * PATHFIND_CELL_SIZE_F;
  3794. pos.z = TheTerrainLogic->getLayerHeight( pos.x, pos.y, s->getLayer() ) + 0.5f;
  3795. addIcon(&pos, PATHFIND_CELL_SIZE_F*.6f, 200, color);
  3796. }
  3797. for( s = m_closedList; s; s=s->getNextOpen() )
  3798. {
  3799. // create objects to show path - they decay
  3800. // create objects to show path - they decay
  3801. RGBColor color;
  3802. color.red = color.blue = 1;
  3803. color.green = 0;
  3804. if (!pathFound) color.blue = 0;
  3805. Int length=200;
  3806. if (!pathFound)
  3807. length *= 2;
  3808. Coord3D pos;
  3809. pos.x = ((Real)s->getXIndex() + 0.5f) * PATHFIND_CELL_SIZE_F;
  3810. pos.y = ((Real)s->getYIndex() + 0.5f) * PATHFIND_CELL_SIZE_F;
  3811. pos.z = TheTerrainLogic->getLayerHeight( pos.x, pos.y, s->getLayer()) + 0.5f;
  3812. addIcon(&pos, PATHFIND_CELL_SIZE_F*.6f, length, color);
  3813. }
  3814. #endif
  3815. }
  3816. Locomotor* Pathfinder::chooseBestLocomotorForPosition(PathfindLayerEnum layer, LocomotorSet* locomotorSet, const Coord3D* pos )
  3817. {
  3818. Int x = REAL_TO_INT_FLOOR(pos->x/PATHFIND_CELL_SIZE);
  3819. Int y = REAL_TO_INT_FLOOR(pos->y/PATHFIND_CELL_SIZE);
  3820. PathfindCell* cell = getCell(layer, x, y );
  3821. // off the map? call it CELL_CLEAR...
  3822. PathfindCell::CellType celltype = cell ? cell->getType() : PathfindCell::CELL_CLEAR;
  3823. LocomotorSurfaceTypeMask acceptableSurfaces = validLocomotorSurfacesForCellType(celltype);
  3824. return locomotorSet->findLocomotor(acceptableSurfaces);
  3825. }
  3826. /*static*/ LocomotorSurfaceTypeMask Pathfinder::validLocomotorSurfacesForCellType(PathfindCell::CellType t)
  3827. {
  3828. if (t == PathfindCell::CELL_OBSTACLE) {
  3829. return LOCOMOTORSURFACE_AIR;
  3830. }
  3831. if (t == PathfindCell::CELL_IMPASSABLE) {
  3832. return LOCOMOTORSURFACE_AIR;
  3833. }
  3834. if (t==PathfindCell::CELL_CLEAR) {
  3835. return LOCOMOTORSURFACE_GROUND | LOCOMOTORSURFACE_AIR;
  3836. }
  3837. if (t == PathfindCell::CELL_WATER) {
  3838. return LOCOMOTORSURFACE_WATER | LOCOMOTORSURFACE_AIR;
  3839. }
  3840. if (t == PathfindCell::CELL_RUBBLE) {
  3841. return LOCOMOTORSURFACE_RUBBLE | LOCOMOTORSURFACE_AIR;
  3842. }
  3843. if ( (t == PathfindCell::CELL_CLIFF) ) {
  3844. return LOCOMOTORSURFACE_CLIFF | LOCOMOTORSURFACE_AIR;
  3845. }
  3846. return NO_SURFACES;
  3847. }
  3848. //
  3849. // Return true if we can move onto this position
  3850. //
  3851. Bool Pathfinder::validMovementTerrain( PathfindLayerEnum layer, const Locomotor* locomotor, const Coord3D *pos)
  3852. {
  3853. Int x = REAL_TO_INT_FLOOR(pos->x/PATHFIND_CELL_SIZE);
  3854. Int y = REAL_TO_INT_FLOOR(pos->y/PATHFIND_CELL_SIZE);
  3855. PathfindCell *toCell = NULL;
  3856. toCell = getCell( layer, x, y );
  3857. if (toCell == NULL)
  3858. return false;
  3859. // Only do terrain, not obstacle cells. jba.
  3860. if (toCell->getType()==PathfindCell::CELL_OBSTACLE) return true;
  3861. if (toCell->getType()==PathfindCell::CELL_IMPASSABLE) return true;
  3862. if (toCell->getLayer()!=LAYER_GROUND && toCell->getLayer() == PathfindCell::CELL_CLEAR) {
  3863. return true;
  3864. }
  3865. // check validity of destination cell
  3866. LocomotorSurfaceTypeMask acceptableSurfaces = validLocomotorSurfacesForCellType(toCell->getType());
  3867. if ((locomotor->getLegalSurfaces() & acceptableSurfaces) == 0)
  3868. return false;
  3869. return true;
  3870. }
  3871. //
  3872. // Releases the cells on the open & closed lists.
  3873. //
  3874. void Pathfinder::cleanOpenAndClosedLists(void) {
  3875. Int count = 0;
  3876. if (m_openList) {
  3877. count += PathfindCell::releaseOpenList(m_openList);
  3878. m_openList = NULL;
  3879. }
  3880. if (m_closedList) {
  3881. count += PathfindCell::releaseClosedList(m_closedList);
  3882. m_closedList = NULL;
  3883. }
  3884. m_cumulativeCellsAllocated += count;
  3885. //#ifdef _DEBUG
  3886. #if 0
  3887. // Check for dangling cells.
  3888. for( int j=0; j<=m_extent.hi.y; j++ )
  3889. for( int i=0; i<=m_extent.hi.x; i++ )
  3890. if (m_map[ i ][ j ].hasInfo()) {
  3891. DEBUG_ASSERTCRASH((m_map[i][j].getXIndex()==i && m_map[i][j].getYIndex()==j), ("Wrong cell coordinates"));
  3892. Bool needInfo = (m_map[ i ][ j ].getType() == PathfindCell::CELL_OBSTACLE);
  3893. if (m_map[i][j].isAircraftGoal()) {
  3894. needInfo = true;
  3895. }
  3896. if (m_map[i][j].getFlags() != PathfindCell::NO_UNITS) {
  3897. needInfo = true;
  3898. }
  3899. if (!needInfo) {
  3900. DEBUG_LOG(("leaked cell %d, %d\n", m_map[i][j].getXIndex(), m_map[i][j].getYIndex()));
  3901. m_map[i][j].releaseInfo();
  3902. }
  3903. DEBUG_ASSERTCRASH((needInfo), ("Minor temporary memory leak - Extra cell allocated. Tell JBA steps if repeatable."));
  3904. };
  3905. //DEBUG_LOG(("Pathfind used %d cells.\n", count));
  3906. #endif
  3907. //#endif
  3908. }
  3909. //
  3910. // Return true if we can move onto this position
  3911. //
  3912. Bool Pathfinder::validMovementPosition( Bool isCrusher, LocomotorSurfaceTypeMask acceptableSurfaces,
  3913. PathfindCell *toCell, PathfindCell *fromCell )
  3914. {
  3915. if (toCell == NULL)
  3916. return false;
  3917. // check if the destination cell is classified as an obstacle,
  3918. // and we happen to be ignoring it
  3919. if (toCell->isObstaclePresent( m_ignoreObstacleID ))
  3920. return true;
  3921. if (isCrusher && toCell->isObstacleFence()) {
  3922. return true;
  3923. }
  3924. // check validity of destination cell
  3925. LocomotorSurfaceTypeMask cellSurfaces = validLocomotorSurfacesForCellType(toCell->getType());
  3926. if ((cellSurfaces & acceptableSurfaces) == 0)
  3927. return false;
  3928. #if 0
  3929. //
  3930. // For diagonal moves, check neighboring vertical and horizontal
  3931. // steps as well to avoid "squeezing through a crack".
  3932. //
  3933. if (fromCell)
  3934. {
  3935. ICoord2D delta;
  3936. delta.x = toCell->getXIndex() - fromCell->getXIndex();
  3937. delta.y = toCell->getYIndex() - fromCell->getYIndex();
  3938. if (delta.x != 0 && delta.y != 0)
  3939. {
  3940. // test vertical movement
  3941. PathfindCell *otherCell = getCell( toCell->getXIndex(), fromCell->getYIndex() );
  3942. Bool open = true;
  3943. // check if cell is on the map
  3944. if (otherCell == NULL)
  3945. open = false;
  3946. // check if we can move onto this new cell
  3947. if (validMovementPosition( locomotorSet, otherCell, fromCell ) == false)
  3948. open = false;
  3949. // test horizontal movement
  3950. if (open == false)
  3951. {
  3952. otherCell = getCell( fromCell->getXIndex(), toCell->getYIndex() );
  3953. // check if cell is on the map
  3954. if (otherCell == NULL)
  3955. return false;
  3956. // check if we can move onto this new cell
  3957. if (validMovementPosition( locomotorSet, otherCell, fromCell ) == false)
  3958. return false;
  3959. }
  3960. }
  3961. }
  3962. #endif
  3963. return true;
  3964. }
  3965. /**
  3966. * Checks to see if obj can occupy the pathfind cell at x,y.
  3967. * Returns false if there is another unit's goal already there.
  3968. * Assumes your locomotor already said you can go there.
  3969. */
  3970. Bool Pathfinder::checkDestination(const Object *obj, Int cellX, Int cellY, PathfindLayerEnum layer, Int iRadius, Bool centerInCell)
  3971. {
  3972. // If obj==NULL, means we are checking for any ground units present. jba.
  3973. Int numCellsAbove = iRadius;
  3974. if (centerInCell) numCellsAbove++;
  3975. Bool checkForAircraft = false;
  3976. Int i, j;
  3977. ObjectID ignoreId = INVALID_ID;
  3978. ObjectID objID = INVALID_ID;
  3979. if (obj && obj->getAIUpdateInterface()) {
  3980. ignoreId = obj->getAIUpdateInterface()->getIgnoredObstacleID();
  3981. checkForAircraft = obj->getAI()->isAircraftThatAdjustsDestination();
  3982. objID = obj->getID();
  3983. }
  3984. for (i=cellX-iRadius; i<cellX+numCellsAbove; i++) {
  3985. for (j=cellY-iRadius; j<cellY+numCellsAbove; j++) {
  3986. PathfindCell *cell = getCell(layer, i, j);
  3987. if (cell) {
  3988. if (checkForAircraft) {
  3989. if (!cell->isAircraftGoal()) continue;
  3990. if (cell->getGoalAircraft()==objID) continue;
  3991. return false;
  3992. }
  3993. if (cell->getType()==PathfindCell::CELL_OBSTACLE) {
  3994. if (cell->isObstaclePresent( ignoreId ))
  3995. continue;
  3996. return false;
  3997. }
  3998. if (cell->getFlags() == PathfindCell::NO_UNITS) {
  3999. continue; // Nobody is here, so it's ok.
  4000. }
  4001. ObjectID goalUnitID = cell->getGoalUnit();
  4002. if (goalUnitID==objID) {
  4003. continue; // we got it.
  4004. } else if (ignoreId==goalUnitID) {
  4005. continue; // we are ignoring it.
  4006. } else if (goalUnitID!=INVALID_ID) {
  4007. if (obj==NULL) {
  4008. return false;
  4009. }
  4010. Object *unit = TheGameLogic->findObjectByID(goalUnitID);
  4011. if (unit) {
  4012. // order matters: we want to know if I consider it to be an ally, not vice versa
  4013. if (obj->getRelationship(unit) == ALLIES) {
  4014. return false; // Don't usurp your allies goals. jba.
  4015. }
  4016. if (cell->getFlags()==PathfindCell::UNIT_PRESENT_FIXED) {
  4017. Bool canCrush = obj->canCrushOrSquish(unit, TEST_CRUSH_OR_SQUISH);
  4018. if (!canCrush) {
  4019. return false; // Don't move to an occupied cell.
  4020. }
  4021. }
  4022. }
  4023. }
  4024. } else {
  4025. return false; // off the map, so can't place here.
  4026. }
  4027. }
  4028. }
  4029. return true;
  4030. }
  4031. /**
  4032. * Checks to see if obj can move through the pathfind cell at x,y.
  4033. * Returns false if there are other units already there.
  4034. * Assumes your locomotor already said you can go there.
  4035. */
  4036. Bool Pathfinder::checkForMovement(const Object *obj, TCheckMovementInfo &info)
  4037. {
  4038. info.allyFixedCount = 0;
  4039. info.allyMoving = false;
  4040. info.allyGoal = false;
  4041. info.enemyFixed = false;
  4042. const Int maxAlly = 5;
  4043. ObjectID allies[maxAlly];
  4044. Int numAlly = 0;
  4045. if (!obj) return true; // not object can move there.
  4046. ObjectID ignoreId = INVALID_ID;
  4047. if (obj->getAIUpdateInterface()) {
  4048. ignoreId = obj->getAIUpdateInterface()->getIgnoredObstacleID();
  4049. }
  4050. Int numCellsAbove = info.radius;
  4051. if (info.centerInCell) numCellsAbove++;
  4052. Int i, j;
  4053. // Bool isInfantry = obj->isKindOf(KINDOF_INFANTRY);
  4054. for (i=info.cell.x-info.radius; i<info.cell.x+numCellsAbove; i++) {
  4055. for (j=info.cell.y-info.radius; j<info.cell.y+numCellsAbove; j++) {
  4056. PathfindCell *cell = getCell(info.layer,i, j);
  4057. if (cell) {
  4058. enum PathfindCell::CellFlags flags = cell->getFlags();
  4059. ObjectID posUnit = cell->getPosUnit();
  4060. if ((flags == PathfindCell::UNIT_GOAL) || (flags == PathfindCell::UNIT_GOAL_OTHER_MOVING)) {
  4061. info.allyGoal = true;
  4062. }
  4063. if (flags == PathfindCell::NO_UNITS) {
  4064. continue; // Nobody is here, so it's ok.
  4065. } else if (posUnit==obj->getID()) {
  4066. continue; // we got it.
  4067. } else if (posUnit==ignoreId) {
  4068. continue; // we are ignoring this one.
  4069. } else {
  4070. Bool check = false;
  4071. Object *unit = NULL;
  4072. if (flags==PathfindCell::UNIT_PRESENT_MOVING || flags==PathfindCell::UNIT_GOAL_OTHER_MOVING) {
  4073. unit = TheGameLogic->findObjectByID(posUnit);
  4074. // order matters: we want to know if I consider it to be an ally, not vice versa
  4075. if (unit && obj->getRelationship(unit) == ALLIES) {
  4076. info.allyMoving = true;
  4077. }
  4078. if (info.considerTransient) {
  4079. check = true;
  4080. }
  4081. }
  4082. if (flags == PathfindCell::UNIT_PRESENT_FIXED) {
  4083. check = true;
  4084. unit = TheGameLogic->findObjectByID(posUnit);
  4085. }
  4086. if (check && unit!=NULL) {
  4087. if (obj->getAIUpdateInterface() && obj->getAIUpdateInterface()->getIgnoredObstacleID()==unit->getID()) {
  4088. // Don't check if it's the ignored obstacle.
  4089. check = false;
  4090. }
  4091. }
  4092. if (check && unit) {
  4093. #ifdef INFANTRY_MOVES_THROUGH_INFANTRY
  4094. if (obj->isKindOf(KINDOF_INFANTRY) && unit->isKindOf(KINDOF_INFANTRY)) {
  4095. // Infantry can run through infantry.
  4096. continue; //
  4097. }
  4098. #endif
  4099. // See if it is an ally.
  4100. // order matters: we want to know if I consider it to be an ally, not vice versa
  4101. if (obj->getRelationship(unit) == ALLIES) {
  4102. if (!unit->getAIUpdateInterface()) {
  4103. return false; // can't path through not-idle units.
  4104. }
  4105. if (!unit->getAIUpdateInterface()->isIdle()) {
  4106. return false; // can't path through not-idle units.
  4107. }
  4108. Bool found = false;
  4109. Int k;
  4110. for (k=0; k<numAlly; k++) {
  4111. if (allies[k] == unit->getID()) {
  4112. found = true;
  4113. }
  4114. }
  4115. if (!found) {
  4116. info.allyFixedCount++;
  4117. if (numAlly < maxAlly) {
  4118. allies[numAlly] = unit->getID();
  4119. numAlly++;
  4120. }
  4121. }
  4122. } else {
  4123. Bool canCrush = obj->canCrushOrSquish( unit, TEST_CRUSH_OR_SQUISH );
  4124. if (!canCrush) {
  4125. info.enemyFixed = true;
  4126. }
  4127. }
  4128. }
  4129. }
  4130. } else {
  4131. return false; // off the map, so can't place here.
  4132. }
  4133. }
  4134. }
  4135. return true;
  4136. }
  4137. /**
  4138. * Adjusts a coordinate to the center of it's cell.
  4139. */
  4140. // Snaps the current position to it's grid location.
  4141. void Pathfinder::snapPosition(Object *obj, Coord3D *pos)
  4142. {
  4143. Int iRadius;
  4144. Bool center;
  4145. getRadiusAndCenter(obj, iRadius, center);
  4146. ICoord2D cell;
  4147. Coord3D adjustDest = *pos;
  4148. if (!center) {
  4149. adjustDest.x += PATHFIND_CELL_SIZE_F/2;
  4150. adjustDest.y += PATHFIND_CELL_SIZE_F/2;
  4151. }
  4152. worldToCell( &adjustDest, &cell );
  4153. adjustCoordToCell(cell.x, cell.y, center, *pos, LAYER_GROUND);
  4154. }
  4155. /**
  4156. * Adjusts a goal position to the center of it's cell.
  4157. */
  4158. // Snaps the current position to it's grid location.
  4159. void Pathfinder::snapClosestGoalPosition(Object *obj, Coord3D *pos)
  4160. {
  4161. Int iRadius;
  4162. Bool center;
  4163. getRadiusAndCenter(obj, iRadius, center);
  4164. ICoord2D cell;
  4165. Coord3D adjustDest = *pos;
  4166. if (!center) {
  4167. adjustDest.x += PATHFIND_CELL_SIZE_F/2;
  4168. adjustDest.y += PATHFIND_CELL_SIZE_F/2;
  4169. }
  4170. PathfindLayerEnum layer = TheTerrainLogic->getLayerForDestination(pos);
  4171. worldToCell( &adjustDest, &cell );
  4172. adjustCoordToCell(cell.x, cell.y, center, *pos, LAYER_GROUND);
  4173. if (checkDestination(obj, cell.x, cell.y , layer, iRadius, center)) {
  4174. return;
  4175. }
  4176. // Try adjusting by 1.
  4177. Int i,j;
  4178. for (i=cell.x-1; i<cell.x+2; i++) {
  4179. for (j=cell.y-1; j<cell.y+2; j++) {
  4180. if (checkDestination(obj, i, j, layer, iRadius, center)) {
  4181. adjustCoordToCell(i, j, center, *pos, layer);
  4182. return;
  4183. }
  4184. }
  4185. }
  4186. if (iRadius==0) {
  4187. // Try to find an unoccupied cell.
  4188. for (i=cell.x-1; i<cell.x+2; i++) {
  4189. for (j=cell.y-1; j<cell.y+2; j++) {
  4190. PathfindCell *newCell = getCell(layer,i, j);
  4191. if (newCell) {
  4192. if (newCell->getGoalUnit()==INVALID_ID || newCell->getGoalUnit()==obj->getID()) {
  4193. adjustCoordToCell(i, j, center, *pos, layer);
  4194. return;
  4195. }
  4196. }
  4197. }
  4198. }
  4199. // Try to find an unoccupied cell.
  4200. for (i=cell.x-1; i<cell.x+2; i++) {
  4201. for (j=cell.y-1; j<cell.y+2; j++) {
  4202. PathfindCell *newCell = getCell(layer,i, j);
  4203. if (newCell) {
  4204. if (newCell->getFlags()!=PathfindCell::UNIT_PRESENT_FIXED) {
  4205. adjustCoordToCell(i, j, center, *pos, layer);
  4206. return;
  4207. }
  4208. }
  4209. }
  4210. }
  4211. }
  4212. //DEBUG_LOG(("Couldn't find goal.\n"));
  4213. }
  4214. /**
  4215. * Returns coordinates of goal.
  4216. *
  4217. */
  4218. Bool Pathfinder::goalPosition(Object *obj, Coord3D *pos)
  4219. {
  4220. Int iRadius;
  4221. Bool center;
  4222. AIUpdateInterface *ai = obj->getAIUpdateInterface();
  4223. if (!ai) return false; // only consider ai objects.
  4224. getRadiusAndCenter(obj, iRadius, center);
  4225. ICoord2D cell = *ai->getPathfindGoalCell();
  4226. pos->zero();
  4227. if (cell.x<0 || cell.y<0) return false;
  4228. adjustCoordToCell(cell.x, cell.y, center, *pos, LAYER_GROUND);
  4229. return true;
  4230. }
  4231. Bool Pathfinder::checkForAdjust(Object *obj, const LocomotorSet& locomotorSet, Bool isHuman,
  4232. Int cellX, Int cellY, PathfindLayerEnum layer,
  4233. Int iRadius, Bool center, Coord3D *dest, const Coord3D *groupDest)
  4234. {
  4235. Coord3D adjustDest;
  4236. PathfindCell *cellP = getCell(layer, cellX, cellY);
  4237. if (cellP==NULL) return false;
  4238. if (cellP && cellP->getType() == PathfindCell::CELL_CLIFF) {
  4239. return false; // no final destinations on cliffs.
  4240. }
  4241. if (isHuman) {
  4242. // check if new cell is in logical map. (computer can move off logical map)
  4243. if (cellX < m_logicalExtent.lo.x ||
  4244. cellY < m_logicalExtent.lo.y ||
  4245. cellX > m_logicalExtent.hi.x ||
  4246. cellY > m_logicalExtent.hi.y) return false;
  4247. }
  4248. if (checkDestination(obj, cellX, cellY, layer, iRadius, center)) {
  4249. adjustCoordToCell(cellX, cellY, center, adjustDest, cellP->getLayer());
  4250. Bool pathExists;
  4251. Bool adjustedPathExists;
  4252. if (obj->isKindOf(KINDOF_AIRCRAFT)) {
  4253. pathExists = true;
  4254. adjustedPathExists = true;
  4255. } else {
  4256. pathExists = quickDoesPathExist( locomotorSet, obj->getPosition(), dest);
  4257. adjustedPathExists = quickDoesPathExist( locomotorSet, obj->getPosition(), &adjustDest);
  4258. if (!pathExists) {
  4259. if (quickDoesPathExist( locomotorSet, dest, &adjustDest)) {
  4260. adjustedPathExists = true;
  4261. }
  4262. }
  4263. }
  4264. if ( adjustedPathExists ) {
  4265. if (groupDest) {
  4266. tightenPath(obj, locomotorSet, &adjustDest, groupDest);
  4267. // Check to see if it is a long way to get to the adjusted destination.
  4268. Int cost = checkPathCost(obj, locomotorSet, groupDest, &adjustDest);
  4269. Int dx = IABS(groupDest->x-adjustDest.x);
  4270. Int dy = IABS(groupDest->y-adjustDest.y);
  4271. if (1.4f*(dx+dy)<cost) {
  4272. return false;
  4273. }
  4274. }
  4275. *dest = adjustDest;
  4276. return true;
  4277. }
  4278. }
  4279. return false;
  4280. }
  4281. Bool Pathfinder::checkForLanding(Int cellX, Int cellY, PathfindLayerEnum layer,
  4282. Int iRadius, Bool center, Coord3D *dest)
  4283. {
  4284. Coord3D adjustDest;
  4285. PathfindCell *cellP = getCell(layer, cellX, cellY);
  4286. if (cellP==NULL) return false;
  4287. switch (cellP->getType())
  4288. {
  4289. case PathfindCell::CELL_CLIFF:
  4290. case PathfindCell::CELL_WATER:
  4291. case PathfindCell::CELL_IMPASSABLE:
  4292. return false; // no final destinations on cliffs, water, etc.
  4293. }
  4294. if (checkDestination(NULL, cellX, cellY, layer, iRadius, center)) {
  4295. adjustCoordToCell(cellX, cellY, center, adjustDest, cellP->getLayer());
  4296. *dest = adjustDest;
  4297. return true;
  4298. }
  4299. return false;
  4300. }
  4301. /**
  4302. * Find an unoccupied spot for a unit to land at.
  4303. * Returns false if there are no spots available within a reasonable radius.
  4304. */
  4305. Bool Pathfinder::adjustToLandingDestination(Object *obj, Coord3D *dest)
  4306. {
  4307. Int iRadius;
  4308. Bool center;
  4309. getRadiusAndCenter(obj, iRadius, center);
  4310. ICoord2D cell;
  4311. Coord3D adjustDest = *dest;
  4312. Region3D extent;
  4313. TheTerrainLogic->getMaximumPathfindExtent(&extent);
  4314. // If the object is off the map & the goal is off the map, it is a scripted setup, so just
  4315. // go to the dest.
  4316. if (!extent.isInRegionNoZ(dest)) {
  4317. if (!extent.isInRegionNoZ(obj->getPosition())) {
  4318. return true;
  4319. }
  4320. }
  4321. if (!center) {
  4322. adjustDest.x += PATHFIND_CELL_SIZE_F/2;
  4323. adjustDest.y += PATHFIND_CELL_SIZE_F/2;
  4324. }
  4325. worldToCell( &adjustDest, &cell );
  4326. enum {MAX_CELLS_TO_TRY=400};
  4327. Int limit = MAX_CELLS_TO_TRY;
  4328. Int i, j;
  4329. i = cell.x;
  4330. j = cell.y;
  4331. PathfindLayerEnum layer = TheTerrainLogic->getLayerForDestination(dest);
  4332. if (checkForLanding(i,j, layer, iRadius, center, dest)) {
  4333. return true;
  4334. }
  4335. Int delta=1;
  4336. Int count;
  4337. while (limit>0) {
  4338. for (count = delta; count>0; count--) {
  4339. i++;
  4340. limit--;
  4341. if (checkForLanding(i,j, layer, iRadius, center, dest)) {
  4342. return true;
  4343. }
  4344. }
  4345. for (count = delta; count>0; count--) {
  4346. j++;
  4347. limit--;
  4348. if (checkForLanding(i,j, layer, iRadius, center, dest)) {
  4349. return true;
  4350. }
  4351. }
  4352. delta++;
  4353. for (count = delta; count>0; count--) {
  4354. i--;
  4355. limit--;
  4356. if (checkForLanding(i,j, layer, iRadius, center, dest)) {
  4357. return true;
  4358. }
  4359. }
  4360. for (count = delta; count>0; count--) {
  4361. j--;
  4362. limit--;
  4363. if (checkForLanding(i,j, layer, iRadius, center, dest)) {
  4364. return true;
  4365. }
  4366. }
  4367. delta++;
  4368. }
  4369. return false;
  4370. }
  4371. /**
  4372. * Find an unoccupied spot for a unit to move to.
  4373. * Returns false if there are no spots available within a reasonable radius.
  4374. */
  4375. Bool Pathfinder::adjustDestination(Object *obj, const LocomotorSet& locomotorSet, Coord3D *dest, const Coord3D *groupDest)
  4376. {
  4377. if( obj->isKindOf(KINDOF_PROJECTILE) )
  4378. {
  4379. return true; // missiles can go wherever they want to. jba.
  4380. }
  4381. Bool isHuman = true;
  4382. if (obj && obj->getControllingPlayer() && (obj->getControllingPlayer()->getPlayerType()==PLAYER_COMPUTER)) {
  4383. isHuman = false; // computer gets to cheat.
  4384. }
  4385. Int iRadius;
  4386. Bool center;
  4387. getRadiusAndCenter(obj, iRadius, center);
  4388. ICoord2D cell;
  4389. Coord3D adjustDest = *dest;
  4390. if (!center) {
  4391. adjustDest.x += PATHFIND_CELL_SIZE_F/2;
  4392. adjustDest.y += PATHFIND_CELL_SIZE_F/2;
  4393. }
  4394. worldToCell( &adjustDest, &cell );
  4395. PathfindLayerEnum layer = TheTerrainLogic->getLayerForDestination(dest);
  4396. if (groupDest) {
  4397. layer = TheTerrainLogic->getLayerForDestination(groupDest);
  4398. }
  4399. enum {MAX_CELLS_TO_TRY=400};
  4400. Int limit = MAX_CELLS_TO_TRY;
  4401. Int i, j;
  4402. i = cell.x;
  4403. j = cell.y;
  4404. if (checkForAdjust(obj, locomotorSet, isHuman, i,j, layer, iRadius, center, dest, groupDest)) {
  4405. return true;
  4406. }
  4407. Int delta=1;
  4408. Int count;
  4409. while (limit>0) {
  4410. for (count = delta; count>0; count--) {
  4411. i++;
  4412. limit--;
  4413. if (checkForAdjust(obj, locomotorSet, isHuman, i,j, layer, iRadius, center, dest, groupDest)) {
  4414. return true;
  4415. }
  4416. }
  4417. for (count = delta; count>0; count--) {
  4418. j++;
  4419. limit--;
  4420. if (checkForAdjust(obj, locomotorSet, isHuman, i,j, layer, iRadius, center, dest, groupDest)) {
  4421. return true;
  4422. }
  4423. }
  4424. delta++;
  4425. for (count = delta; count>0; count--) {
  4426. i--;
  4427. limit--;
  4428. if (checkForAdjust(obj, locomotorSet, isHuman, i,j, layer, iRadius, center, dest, groupDest)) {
  4429. return true;
  4430. }
  4431. }
  4432. for (count = delta; count>0; count--) {
  4433. j--;
  4434. limit--;
  4435. if (checkForAdjust(obj, locomotorSet, isHuman, i,j, layer, iRadius, center, dest, groupDest)) {
  4436. return true;
  4437. }
  4438. }
  4439. delta++;
  4440. }
  4441. if (groupDest) {
  4442. // Didn't work, so just do simple adjust.
  4443. return(adjustDestination(obj, locomotorSet, dest, NULL));
  4444. }
  4445. //DEBUG_LOG(("adjustDestination failed, dest (%f, %f), adj dest (%f,%f), %x %s\n", dest->x, dest->y, adjustDest.x, adjustDest.y,
  4446. //obj, obj->getTemplate()->getName().str()));
  4447. return false;
  4448. }
  4449. Bool Pathfinder::checkForTarget(const Object *obj, Int cellX, Int cellY, const Weapon *weapon,
  4450. const Object *victim, const Coord3D *victimPos,
  4451. Int iRadius, Bool center,Coord3D *dest)
  4452. {
  4453. Coord3D adjustDest;
  4454. if (checkDestination(obj, cellX, cellY, LAYER_GROUND, iRadius, center)) {
  4455. adjustCoordToCell(cellX, cellY, center, adjustDest, LAYER_GROUND);
  4456. if (weapon->isGoalPosWithinAttackRange( obj, &adjustDest, victim, victimPos )) {
  4457. *dest = adjustDest;
  4458. return true;
  4459. }
  4460. }
  4461. return false;
  4462. }
  4463. /**
  4464. * Find an unoccupied spot for a unit to move to that can fire at victim.
  4465. * Returns false if there are no spots available within a reasonable radius.
  4466. */
  4467. Bool Pathfinder::adjustTargetDestination(const Object *obj, const Object *target, const Coord3D *targetPos,
  4468. const Weapon *weapon, Coord3D *dest)
  4469. {
  4470. Int iRadius;
  4471. Bool center;
  4472. getRadiusAndCenter(obj, iRadius, center);
  4473. ICoord2D cell;
  4474. Coord3D adjustDest = *dest;
  4475. if (!center) {
  4476. adjustDest.x += PATHFIND_CELL_SIZE_F/2;
  4477. adjustDest.y += PATHFIND_CELL_SIZE_F/2;
  4478. }
  4479. if (worldToCell( &adjustDest, &cell )) {
  4480. return false; // outside of bounds.
  4481. }
  4482. enum {MAX_CELLS_TO_TRY=400};
  4483. Int limit = MAX_CELLS_TO_TRY;
  4484. Int i, j;
  4485. i = cell.x;
  4486. j = cell.y;
  4487. if (checkForTarget(obj, i,j, weapon, target, targetPos, iRadius, center, dest)) {
  4488. return true;
  4489. }
  4490. Int delta=1;
  4491. Int count;
  4492. while (limit>0) {
  4493. for (count = delta; count>0; count--) {
  4494. i++;
  4495. limit--;
  4496. if (checkForTarget(obj, i,j, weapon, target, targetPos, iRadius, center, dest)) {
  4497. return true;
  4498. }
  4499. }
  4500. for (count = delta; count>0; count--) {
  4501. j++;
  4502. limit--;
  4503. if (checkForTarget(obj, i,j, weapon, target, targetPos, iRadius, center, dest)) {
  4504. return true;
  4505. }
  4506. }
  4507. delta++;
  4508. for (count = delta; count>0; count--) {
  4509. i--;
  4510. limit--;
  4511. if (checkForTarget(obj, i,j, weapon, target, targetPos, iRadius, center, dest)) {
  4512. return true;
  4513. }
  4514. }
  4515. for (count = delta; count>0; count--) {
  4516. j--;
  4517. limit--;
  4518. if (checkForTarget(obj, i,j, weapon, target, targetPos, iRadius, center, dest)) {
  4519. return true;
  4520. }
  4521. }
  4522. delta++;
  4523. }
  4524. return false;
  4525. }
  4526. Bool Pathfinder::checkForPossible(Bool isCrusher, Int fromZone, Bool center, const LocomotorSet& locomotorSet,
  4527. Int cellX, Int cellY, PathfindLayerEnum layer, Coord3D *dest, Bool startingInObstacle)
  4528. {
  4529. PathfindCell *goalCell = getCell(layer, cellX, cellY);
  4530. if (!goalCell) return false;
  4531. if (goalCell->getType() == PathfindCell::CELL_OBSTACLE) return false;
  4532. Int zone2 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), isCrusher, goalCell->getZone());
  4533. if (startingInObstacle) {
  4534. zone2 = m_zoneManager.getEffectiveTerrainZone(zone2);
  4535. }
  4536. if (fromZone==zone2) {
  4537. adjustCoordToCell(cellX, cellY, center, *dest, layer);
  4538. return true;
  4539. }
  4540. return false;
  4541. }
  4542. /**
  4543. * Find a pathable spot near the destination.
  4544. * Returns false if there are no spots available within a reasonable radius.
  4545. */
  4546. Bool Pathfinder::adjustToPossibleDestination(Object *obj, const LocomotorSet& locomotorSet,
  4547. Coord3D *dest)
  4548. {
  4549. Int radius;
  4550. Bool center;
  4551. getRadiusAndCenter(obj, radius, center);
  4552. ICoord2D goalCellNdx;
  4553. Coord3D adjustDest = *dest;
  4554. if (!center) {
  4555. adjustDest.x += PATHFIND_CELL_SIZE_F/2;
  4556. adjustDest.y += PATHFIND_CELL_SIZE_F/2;
  4557. }
  4558. if (worldToCell( &adjustDest, &goalCellNdx )) {
  4559. return false; // outside of bounds.
  4560. }
  4561. // determine goal cell
  4562. PathfindCell *goalCell;
  4563. PathfindLayerEnum destinationLayer = TheTerrainLogic->getLayerForDestination(dest);
  4564. goalCell = getCell(destinationLayer, goalCellNdx.x, goalCellNdx.y);
  4565. Coord3D from = *obj->getPosition();
  4566. // determine start cell
  4567. ICoord2D startCellNdx;
  4568. worldToCell(&from, &startCellNdx);
  4569. PathfindLayerEnum layer = LAYER_GROUND;
  4570. if (obj) {
  4571. layer = obj->getLayer();
  4572. }
  4573. PathfindCell *parentCell = getClippedCell( layer, &from );
  4574. if (parentCell == NULL) {
  4575. return false;
  4576. }
  4577. //
  4578. Int zone1, zone2;
  4579. Bool isCrusher = obj ? obj->getCrusherLevel() > 0 : false;
  4580. zone1 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), isCrusher, parentCell->getZone());
  4581. Bool isObstacle = false;
  4582. if (parentCell->getType() == PathfindCell::CELL_OBSTACLE) {
  4583. isObstacle = true;
  4584. }
  4585. if (isObstacle) {
  4586. zone1 = m_zoneManager.getEffectiveTerrainZone(zone1);
  4587. zone1 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), isCrusher, zone1);
  4588. }
  4589. zone2 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), isCrusher, goalCell->getZone());
  4590. if (zone1 == zone2) {
  4591. if (checkDestination(obj, goalCellNdx.x, goalCellNdx.y, destinationLayer, radius, center)) {
  4592. return true;
  4593. }
  4594. }
  4595. enum {MAX_CELLS_TO_TRY=400};
  4596. Int limit = MAX_CELLS_TO_TRY;
  4597. Int i, j;
  4598. i = goalCellNdx.x;
  4599. j = goalCellNdx.y;
  4600. Int delta=1;
  4601. Int count;
  4602. while (limit>0) {
  4603. for (count = delta; count>0; count--) {
  4604. i++;
  4605. limit--;
  4606. if (checkForPossible(isCrusher, zone1, center, locomotorSet, i,j, destinationLayer, dest, isObstacle)) {
  4607. if (checkDestination(obj, i, j, destinationLayer, radius, center)) {
  4608. return true;
  4609. }
  4610. }
  4611. }
  4612. for (count = delta; count>0; count--) {
  4613. j++;
  4614. limit--;
  4615. if (checkForPossible(isCrusher, zone1, center, locomotorSet, i,j, destinationLayer, dest, isObstacle)) {
  4616. if (checkDestination(obj, i, j, destinationLayer, radius, center)) {
  4617. return true;
  4618. }
  4619. }
  4620. }
  4621. delta++;
  4622. for (count = delta; count>0; count--) {
  4623. i--;
  4624. limit--;
  4625. if (checkForPossible(isCrusher, zone1, center, locomotorSet, i,j, destinationLayer, dest, isObstacle)) {
  4626. if (checkDestination(obj, i, j, destinationLayer, radius, center)) {
  4627. return true;
  4628. }
  4629. }
  4630. }
  4631. for (count = delta; count>0; count--) {
  4632. j--;
  4633. limit--;
  4634. if (checkForPossible(isCrusher, zone1, center, locomotorSet, i,j, destinationLayer, dest, isObstacle)) {
  4635. if (checkDestination(obj, i, j, destinationLayer, radius, center)) {
  4636. return true;
  4637. }
  4638. }
  4639. }
  4640. delta++;
  4641. }
  4642. return false;
  4643. }
  4644. /**
  4645. * Queues an object to do a pathfind.
  4646. * It will call the object's ai update->doPathfind() during processPathfindQueue().
  4647. */
  4648. Bool Pathfinder::queueForPath(ObjectID id)
  4649. {
  4650. #if defined(_DEBUG) || defined(_INTERNAL)
  4651. {
  4652. Object *tmpObj = TheGameLogic->findObjectByID(id);
  4653. if (tmpObj) {
  4654. AIUpdateInterface *tmpAI = tmpObj->getAIUpdateInterface();
  4655. if (tmpAI) {
  4656. const Coord3D* pos = tmpAI->friend_getRequestedDestination();
  4657. 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()));
  4658. }
  4659. }
  4660. }
  4661. #endif
  4662. /* Check & see if we are already queued. */
  4663. Int slot = m_queuePRHead;
  4664. while (slot != m_queuePRTail) {
  4665. if (m_queuedPathfindRequests[slot] == id) {
  4666. return true;
  4667. }
  4668. slot++;
  4669. if (slot >= PATHFIND_QUEUE_LEN) {
  4670. slot = 0;
  4671. }
  4672. }
  4673. // Tail is the first available slot.
  4674. Int nextSlot = m_queuePRTail+1;
  4675. if (nextSlot >= PATHFIND_QUEUE_LEN) {
  4676. nextSlot = 0;
  4677. }
  4678. if (nextSlot==m_queuePRHead) {
  4679. DEBUG_CRASH(("Ran out of pathfind queue slots."));
  4680. return false;
  4681. }
  4682. m_queuedPathfindRequests[m_queuePRTail] = id;
  4683. m_queuePRTail = nextSlot;
  4684. return true;
  4685. }
  4686. #if defined _DEBUG || defined _INTERNAL
  4687. void Pathfinder::doDebugIcons(void) {
  4688. const Int FRAMES_TO_SHOW_OBSTACLES = 100;
  4689. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  4690. // render AI debug information
  4691. if (TheGlobalData->m_debugAI!=AI_DEBUG_CELLS && TheGlobalData->m_debugAI!=AI_DEBUG_TERRAIN) {
  4692. return;
  4693. }
  4694. RGBColor color;
  4695. color.red = color.green = color.blue = 0;
  4696. addIcon(NULL, 0, 0, color); // clear.
  4697. Coord3D topLeftCorner;
  4698. Bool showCells = TheGlobalData->m_debugAI==AI_DEBUG_CELLS;
  4699. Int i;
  4700. for (i=0; i<=LAYER_LAST; i++) {
  4701. m_layers[i].doDebugIcons();
  4702. }
  4703. if (!showCells) {
  4704. frameToShowObstacles = TheGameLogic->getFrame()+FRAMES_TO_SHOW_OBSTACLES;
  4705. //return;
  4706. }
  4707. // show the pathfind grid
  4708. for( int j=0; j<getExtent()->y; j++ )
  4709. {
  4710. topLeftCorner.y = (Real)j * PATHFIND_CELL_SIZE_F;
  4711. for( int i=0; i<getExtent()->x; i++ )
  4712. {
  4713. topLeftCorner.x = (Real)i * PATHFIND_CELL_SIZE_F;
  4714. color.red = color.green = color.blue = 0;
  4715. Bool empty = true;
  4716. const PathfindCell *cell = TheAI->pathfinder()->getCell( LAYER_GROUND, i, j );
  4717. if (cell)
  4718. {
  4719. switch (cell->getType())
  4720. {
  4721. case PathfindCell::CELL_CLIFF:
  4722. color.red = 1;
  4723. empty = false;
  4724. break;
  4725. case PathfindCell::CELL_IMPASSABLE:
  4726. color.green = 1;
  4727. empty = false;
  4728. break;
  4729. case PathfindCell::CELL_WATER:
  4730. color.blue = 1;
  4731. empty = false;
  4732. break;
  4733. case PathfindCell::CELL_RUBBLE:
  4734. color.red = 1;
  4735. color.green = 0.5;
  4736. empty = false;
  4737. break;
  4738. case PathfindCell::CELL_OBSTACLE:
  4739. color.red = color.green = 1;
  4740. empty = false;
  4741. break;
  4742. default:
  4743. if (cell->getPinched()) {
  4744. color.blue = color.green = 0.7f;
  4745. empty = false;
  4746. }
  4747. break;
  4748. }
  4749. }
  4750. if (showCells) {
  4751. empty = true;
  4752. color.red = color.green = color.blue = 0;
  4753. if (empty && cell) {
  4754. if (cell->getFlags()!=PathfindCell::NO_UNITS) {
  4755. empty = false;
  4756. if (cell->getFlags() == PathfindCell::UNIT_GOAL) {
  4757. color.red = 1;
  4758. } else if (cell->getFlags() == PathfindCell::UNIT_PRESENT_FIXED) {
  4759. color.green = color.blue = color.red = 1;
  4760. } else if (cell->getFlags() == PathfindCell::UNIT_PRESENT_MOVING) {
  4761. color.green = 1;
  4762. } else {
  4763. color.green = color.red = 1;
  4764. }
  4765. }
  4766. if (cell->isAircraftGoal()) {
  4767. empty = false;
  4768. color.red = 0;
  4769. color.green = color.blue = 1;
  4770. }
  4771. }
  4772. }
  4773. if (!empty) {
  4774. Coord3D loc;
  4775. loc.x = topLeftCorner.x + PATHFIND_CELL_SIZE_F/2.0f;
  4776. loc.y = topLeftCorner.y + PATHFIND_CELL_SIZE_F/2.0f;
  4777. loc.z = TheTerrainLogic->getGroundHeight(loc.x , loc.y);
  4778. addIcon(&loc, PATHFIND_CELL_SIZE_F*0.8f, FRAMES_TO_SHOW_OBSTACLES-1, color);
  4779. }
  4780. }
  4781. }
  4782. }
  4783. #endif
  4784. //-------------------------------------------------------------------------------------------------
  4785. /**
  4786. * Create an aircraft path. Just jogs around tall buildings marked with KINDOF_AIRCRAFT_PATH_AROUND.
  4787. */
  4788. Path *Pathfinder::getAircraftPath( const Object *obj, const Coord3D *to )
  4789. {
  4790. // for now, quick path objects don't pathfind, generally airborne units
  4791. // build a trivial one-node path containing destination, then avoid buildings.
  4792. Path *thePath = newInstance(Path);
  4793. const AIUpdateInterface *ai = obj->getAI();
  4794. ObjectID avoidObject = INVALID_ID;
  4795. if (ai) {
  4796. avoidObject = ai->getBuildingToNotPathAround();
  4797. }
  4798. // If it is an aircraft that circles (like raptors & migs) we need to adjust the destination
  4799. // to one that doesn't clip buildings.
  4800. Bool checkClips = false;
  4801. if (ai && ai->getCurLocomotor()) {
  4802. if (ai->getCurLocomotor()->getAppearance() == LOCO_WINGS) {
  4803. checkClips = true;
  4804. }
  4805. }
  4806. Real radius = 100;
  4807. Coord3D adjDest = *to;
  4808. if (checkClips) {
  4809. circleClipsTallBuilding(obj->getPosition(), to, radius, avoidObject, &adjDest);
  4810. }
  4811. thePath->prependNode(&adjDest, LAYER_GROUND);
  4812. Coord3D pos = *obj->getPosition();
  4813. pos.z = to->z;
  4814. thePath->prependNode( &pos, LAYER_GROUND );
  4815. Int limit = 20;
  4816. PathNode *curNode = thePath->getFirstNode();
  4817. while (curNode && curNode->getNext()) {
  4818. Coord3D newPos1, newPos2, newPos3;
  4819. if (segmentIntersectsTallBuilding(curNode, curNode->getNext(), avoidObject, &newPos1, &newPos2, &newPos3)) {
  4820. PathNode *newNode3 = newInstance(PathNode);
  4821. newNode3->setPosition( &newPos3 );
  4822. newNode3->setLayer(LAYER_GROUND);
  4823. curNode->append(newNode3);
  4824. PathNode *newNode2 = newInstance(PathNode);
  4825. newNode2->setPosition( &newPos2 );
  4826. newNode2->setLayer(LAYER_GROUND);
  4827. curNode->append(newNode2);
  4828. PathNode *newNode1 = newInstance(PathNode);
  4829. newNode1->setPosition( &newPos1 );
  4830. newNode1->setLayer(LAYER_GROUND);
  4831. curNode->append(newNode1);
  4832. curNode = newNode2;
  4833. }
  4834. curNode = curNode->getNext();
  4835. limit--;
  4836. if (limit<0) break;
  4837. }
  4838. curNode = thePath->getFirstNode();
  4839. while (curNode && curNode->getNext()) {
  4840. curNode->setNextOptimized(curNode->getNext());
  4841. curNode = curNode->getNext();
  4842. }
  4843. thePath->markOptimized();
  4844. if (TheGlobalData->m_debugAI==AI_DEBUG_PATHS) {
  4845. TheAI->pathfinder()->setDebugPath(thePath);
  4846. }
  4847. return thePath;
  4848. }
  4849. /**
  4850. * Process some path requests in the pathfind queue.
  4851. */
  4852. //DECLARE_PERF_TIMER(processPathfindQueue)
  4853. void Pathfinder::processPathfindQueue(void)
  4854. {
  4855. //USE_PERF_TIMER(processPathfindQueue)
  4856. if (!m_isMapReady) {
  4857. return;
  4858. }
  4859. #ifdef DEBUG_QPF
  4860. #if defined _DEBUG || defined _INTERNAL
  4861. Int startTimeMS = ::GetTickCount();
  4862. __int64 startTime64;
  4863. double timeToUpdate=0.0f;
  4864. __int64 endTime64,freq64;
  4865. QueryPerformanceFrequency((LARGE_INTEGER *)&freq64);
  4866. QueryPerformanceCounter((LARGE_INTEGER *)&startTime64);
  4867. #endif
  4868. #endif
  4869. if (m_zoneManager.needToCalculateZones()) {
  4870. m_zoneManager.calculateZones(m_map, m_layers, m_extent);
  4871. return;
  4872. }
  4873. // Get the current logical extent.
  4874. Region3D terrainExtent;
  4875. TheTerrainLogic->getExtent( &terrainExtent );
  4876. IRegion2D bounds;
  4877. bounds.lo.x = REAL_TO_INT_FLOOR(terrainExtent.lo.x / PATHFIND_CELL_SIZE_F);
  4878. bounds.hi.x = REAL_TO_INT_FLOOR(terrainExtent.hi.x / PATHFIND_CELL_SIZE_F);
  4879. bounds.lo.y = REAL_TO_INT_FLOOR(terrainExtent.lo.y / PATHFIND_CELL_SIZE_F);
  4880. bounds.hi.y = REAL_TO_INT_FLOOR(terrainExtent.hi.y / PATHFIND_CELL_SIZE_F);
  4881. bounds.hi.x--;
  4882. bounds.hi.y--;
  4883. m_logicalExtent = bounds;
  4884. m_cumulativeCellsAllocated = 0; // Number of pathfind cells examined.
  4885. Int pathsFound = 0;
  4886. while (m_cumulativeCellsAllocated < PATHFIND_CELLS_PER_FRAME &&
  4887. m_queuePRTail!=m_queuePRHead) {
  4888. Object *obj = TheGameLogic->findObjectByID(m_queuedPathfindRequests[m_queuePRHead]);
  4889. m_queuedPathfindRequests[m_queuePRHead] = INVALID_ID;
  4890. if (obj) {
  4891. AIUpdateInterface *ai = obj->getAIUpdateInterface();
  4892. if (ai) {
  4893. ai->doPathfind(this);
  4894. pathsFound++;
  4895. }
  4896. }
  4897. m_queuePRHead = m_queuePRHead+1;
  4898. if (m_queuePRHead >= PATHFIND_QUEUE_LEN) {
  4899. m_queuePRHead = 0;
  4900. }
  4901. }
  4902. if (pathsFound>0) {
  4903. #ifdef DEBUG_QPF
  4904. #if defined _DEBUG || defined _INTERNAL
  4905. QueryPerformanceCounter((LARGE_INTEGER *)&endTime64);
  4906. timeToUpdate = ((double)(endTime64-startTime64) / (double)(freq64));
  4907. if (timeToUpdate>0.01f)
  4908. {
  4909. DEBUG_LOG(("%d Pathfind queue: %d paths, %d cells", TheGameLogic->getFrame(), pathsFound, m_cumulativeCellsAllocated));
  4910. DEBUG_LOG(("Time %f (%f)", timeToUpdate, (::GetTickCount()-startTimeMS)/1000.0f));
  4911. DEBUG_LOG(("\n"));
  4912. }
  4913. #endif
  4914. #endif
  4915. }
  4916. #if defined _DEBUG || defined _INTERNAL
  4917. doDebugIcons();
  4918. #endif
  4919. }
  4920. void Pathfinder::checkChangeLayers(PathfindCell *parentCell)
  4921. {
  4922. ICoord2D newCellCoord;
  4923. PathfindCell *newCell;
  4924. if (parentCell->getConnectLayer() != LAYER_INVALID) {
  4925. newCellCoord.x = parentCell->getXIndex();
  4926. newCellCoord.y = parentCell->getYIndex();
  4927. if (parentCell->getConnectLayer() == LAYER_GROUND) {
  4928. newCell = getCell(LAYER_GROUND, newCellCoord.x, newCellCoord.y );
  4929. } else {
  4930. newCell = getCell(parentCell->getConnectLayer(), newCellCoord.x, newCellCoord.y);
  4931. }
  4932. DEBUG_ASSERTCRASH(newCell, ("Couldn't find cell."));
  4933. if (newCell) {
  4934. Bool onList = false;
  4935. if (newCell->hasInfo()) {
  4936. if (newCell->getOpen() || newCell->getClosed())
  4937. {
  4938. // already on one of the lists
  4939. onList = true;
  4940. }
  4941. }
  4942. if (!onList) {
  4943. if (!newCell->allocateInfo(newCellCoord)) {
  4944. // Out of cells for pathing...
  4945. return;
  4946. }
  4947. // compute cost of path thus far
  4948. // keep track of path we're building - point back to cell we moved here from
  4949. newCell->setParentCell(parentCell) ;
  4950. // store cost of this path
  4951. newCell->setCostSoFar(parentCell->getCostSoFar()); // same as parent cost
  4952. newCell->setTotalCost(parentCell->getTotalCost()) ;
  4953. // insert newCell in open list such that open list is sorted, smallest total path cost first
  4954. m_openList = newCell->putOnSortedOpenList( m_openList );
  4955. }
  4956. }
  4957. }
  4958. }
  4959. struct ExamineCellsStruct
  4960. {
  4961. Pathfinder *thePathfinder;
  4962. const LocomotorSet *theLoco;
  4963. Bool centerInCell;
  4964. Bool isHuman;
  4965. Int radius;
  4966. const Object *obj;
  4967. PathfindCell *goalCell;
  4968. };
  4969. /*static*/ Int Pathfinder::examineCellsCallback(Pathfinder* pathfinder, PathfindCell* from, PathfindCell* to, Int to_x, Int to_y, void* userData)
  4970. {
  4971. ExamineCellsStruct* d = (ExamineCellsStruct*)userData;
  4972. Bool isCrusher = d->obj ? d->obj->getCrusherLevel() > 0 : false;
  4973. if (d->thePathfinder->m_isTunneling) return 1; // abort.
  4974. if (from && to) {
  4975. if (!d->thePathfinder->validMovementPosition( isCrusher, d->theLoco->getValidSurfaces(), to, from )) {
  4976. return 1;
  4977. }
  4978. if ( (to->getLayer() == LAYER_GROUND) && !d->thePathfinder->m_zoneManager.isPassable(to_x, to_y) ) {
  4979. return 1;
  4980. }
  4981. Bool onList = false;
  4982. if (to->hasInfo()) {
  4983. if (to->getOpen() || to->getClosed())
  4984. {
  4985. // already on one of the lists
  4986. onList = true;
  4987. }
  4988. }
  4989. if (to->getPinched()) {
  4990. return 1; // abort.
  4991. }
  4992. if (d->isHuman) {
  4993. // check if new cell is in logical map. (computer can move off logical map)
  4994. if (to_x < d->thePathfinder->m_logicalExtent.lo.x) return 1; // abort
  4995. if (to_y < d->thePathfinder->m_logicalExtent.lo.y) return 1; // abort
  4996. if (to_x > d->thePathfinder->m_logicalExtent.hi.x) return 1; // abort
  4997. if (to_y > d->thePathfinder->m_logicalExtent.hi.y) return 1; // abort
  4998. }
  4999. TCheckMovementInfo info;
  5000. info.cell.x = to_x;
  5001. info.cell.y = to_y;
  5002. info.layer = from->getLayer();
  5003. info.centerInCell = d->centerInCell;
  5004. info.radius = d->radius;
  5005. info.considerTransient = false;
  5006. info.acceptableSurfaces = d->theLoco->getValidSurfaces();
  5007. if (!d->thePathfinder->checkForMovement(d->obj, info) || info.enemyFixed) {
  5008. return 1; //abort.
  5009. }
  5010. if (info.enemyFixed) {
  5011. return 1; //abort.
  5012. }
  5013. ICoord2D newCellCoord;
  5014. newCellCoord.x = to_x;
  5015. newCellCoord.y = to_y;
  5016. UnsignedInt newCostSoFar = from->getCostSoFar( ) + 0.5f*COST_ORTHOGONAL;
  5017. if (to->getType() == PathfindCell::CELL_CLIFF ) {
  5018. return 1;
  5019. }
  5020. if (info.allyFixedCount) {
  5021. return 1;
  5022. } else if (info.enemyFixed) {
  5023. return 1;
  5024. }
  5025. if (!to->allocateInfo(newCellCoord)) {
  5026. // Out of cells for pathing...
  5027. return 1;
  5028. }
  5029. to->setBlockedByAlly(false);
  5030. Int costRemaining = 0;
  5031. costRemaining = to->costToGoal( d->goalCell );
  5032. // check if this neighbor cell is already on the open (waiting to be tried)
  5033. // or closed (already tried) lists
  5034. if (onList)
  5035. {
  5036. // already on one of the lists - if existing costSoFar is less,
  5037. // the new cell is on a longer path, so skip it
  5038. if (to->getCostSoFar() <= newCostSoFar)
  5039. return 0; // keep going.
  5040. }
  5041. to->setCostSoFar(newCostSoFar);
  5042. // keep track of path we're building - point back to cell we moved here from
  5043. to->setParentCell(from) ;
  5044. to->setTotalCost(to->getCostSoFar() + costRemaining) ;
  5045. //DEBUG_LOG(("Cell (%d,%d), Parent cost %d, newCostSoFar %d, cost rem %d, tot %d\n",
  5046. // to->getXIndex(), to->getYIndex(),
  5047. // to->costSoFar(from), newCostSoFar, costRemaining, to->getCostSoFar() + costRemaining));
  5048. // if to was on closed list, remove it from the list
  5049. if (to->getClosed())
  5050. d->thePathfinder->m_closedList = to->removeFromClosedList( d->thePathfinder->m_closedList );
  5051. // if the to was already on the open list, remove it so it can be re-inserted in order
  5052. if (to->getOpen())
  5053. d->thePathfinder->m_openList = to->removeFromOpenList( d->thePathfinder->m_openList );
  5054. // insert to in open list such that open list is sorted, smallest total path cost first
  5055. d->thePathfinder->m_openList = to->putOnSortedOpenList( d->thePathfinder->m_openList );
  5056. }
  5057. return 0; // keep going
  5058. }
  5059. Int Pathfinder::examineNeighboringCells(PathfindCell *parentCell, PathfindCell *goalCell, const LocomotorSet& locomotorSet,
  5060. Bool isHuman, Bool centerInCell, Int radius, const ICoord2D &startCellNdx,
  5061. const Object *obj, Int attackDistance)
  5062. {
  5063. Bool canPathThroughUnits = false;
  5064. if (obj && obj->getAIUpdateInterface()) {
  5065. canPathThroughUnits = obj->getAIUpdateInterface()->canPathThroughUnits();
  5066. }
  5067. Bool isCrusher = obj ? obj->getCrusherLevel() > 0 : false;
  5068. if (attackDistance==NO_ATTACK && !m_isTunneling && !locomotorSet.isDownhillOnly() && goalCell) {
  5069. ExamineCellsStruct info;
  5070. info.thePathfinder = this;
  5071. info.theLoco = &locomotorSet;
  5072. info.centerInCell = centerInCell;
  5073. info.radius = radius;
  5074. info.obj = obj;
  5075. info.isHuman = isHuman;
  5076. info.goalCell = goalCell;
  5077. ICoord2D start, end;
  5078. start.x = parentCell->getXIndex();
  5079. start.y = parentCell->getYIndex();
  5080. end.x = goalCell->getXIndex();
  5081. end.y = goalCell->getYIndex();
  5082. iterateCellsAlongLine(start, end, parentCell->getLayer(), examineCellsCallback, &info);
  5083. }
  5084. Int cellCount = 0;
  5085. // expand search to neighboring orthogonal cells
  5086. static ICoord2D delta[] =
  5087. {
  5088. { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 },
  5089. { 1, 1 }, { -1, 1 }, { -1, -1 }, { 1, -1 }
  5090. };
  5091. const Int numNeighbors = 8;
  5092. const Int firstDiagonal = 4;
  5093. ICoord2D newCellCoord;
  5094. PathfindCell *newCell;
  5095. const Int adjacent[5] = {0, 1, 2, 3, 0};
  5096. Bool neighborFlags[8] = {false, false, false, false, false, false, false};
  5097. UnsignedInt newCostSoFar;
  5098. for( int i=0; i<numNeighbors; i++ )
  5099. {
  5100. neighborFlags[i] = false;
  5101. // determine neighbor cell to try
  5102. newCellCoord.x = parentCell->getXIndex() + delta[i].x;
  5103. newCellCoord.y = parentCell->getYIndex() + delta[i].y;
  5104. // get the neighboring cell
  5105. newCell = getCell(parentCell->getLayer(), newCellCoord.x, newCellCoord.y );
  5106. // check if cell is on the map
  5107. if (newCell == NULL)
  5108. continue;
  5109. Bool notZonePassable = false;
  5110. if ((newCell->getLayer()==LAYER_GROUND) && !m_zoneManager.isPassable(newCellCoord.x, newCellCoord.y)) {
  5111. notZonePassable = true;
  5112. }
  5113. if (isHuman) {
  5114. // check if new cell is in logical map. (computer can move off logical map)
  5115. if (newCellCoord.x < m_logicalExtent.lo.x) continue;
  5116. if (newCellCoord.y < m_logicalExtent.lo.y) continue;
  5117. if (newCellCoord.x > m_logicalExtent.hi.x) continue;
  5118. if (newCellCoord.y > m_logicalExtent.hi.y) continue;
  5119. }
  5120. // check if this neighbor cell is already on the open (waiting to be tried)
  5121. // or closed (already tried) lists
  5122. Bool onList = false;
  5123. if (newCell->hasInfo()) {
  5124. if (newCell->getOpen() || newCell->getClosed())
  5125. {
  5126. // already on one of the lists
  5127. onList = true;
  5128. }
  5129. }
  5130. if (onList) {
  5131. // we have already examined this one, so continue.
  5132. continue;
  5133. }
  5134. if (i>=firstDiagonal) {
  5135. // make sure one of the adjacent sides is open.
  5136. if (!neighborFlags[adjacent[i-4]] && !neighborFlags[adjacent[i-3]]) {
  5137. continue;
  5138. }
  5139. }
  5140. // do the gravity check here
  5141. if ( locomotorSet.isDownhillOnly() )
  5142. {
  5143. Coord3D fromPos;
  5144. fromPos.x = parentCell->getXIndex() * PATHFIND_CELL_SIZE_F ;
  5145. fromPos.y = parentCell->getYIndex() * PATHFIND_CELL_SIZE_F ;
  5146. fromPos.z = TheTerrainLogic->getGroundHeight(fromPos.x , fromPos.y);
  5147. Coord3D toPos;
  5148. toPos.x = newCellCoord.x * PATHFIND_CELL_SIZE_F ;
  5149. toPos.y = newCellCoord.y * PATHFIND_CELL_SIZE_F ;
  5150. toPos.z = TheTerrainLogic->getGroundHeight(toPos.x , toPos.y);
  5151. if ( fromPos.z < toPos.z )
  5152. continue;
  5153. }
  5154. Bool movementValid = true;
  5155. Bool dozerHack = false;
  5156. if (validMovementPosition( isCrusher, locomotorSet.getValidSurfaces(), newCell, parentCell )) {
  5157. } else {
  5158. movementValid = false;
  5159. if (obj->isKindOf(KINDOF_DOZER)) {
  5160. if (newCell->getType()==PathfindCell::CELL_OBSTACLE) {
  5161. Object *obstacle = TheGameLogic->findObjectByID(newCell->getObstacleID());
  5162. if (obstacle && !(obj->getRelationship(obstacle)==ENEMIES)) {
  5163. movementValid = true;
  5164. dozerHack = true;
  5165. }
  5166. }
  5167. }
  5168. if (!movementValid && !m_isTunneling) {
  5169. continue;
  5170. }
  5171. }
  5172. if (!dozerHack)
  5173. neighborFlags[i] = true;
  5174. TCheckMovementInfo info;
  5175. info.cell = newCellCoord;
  5176. info.layer = parentCell->getLayer();
  5177. info.centerInCell = centerInCell;
  5178. info.radius = radius;
  5179. info.considerTransient = false;
  5180. info.acceptableSurfaces = locomotorSet.getValidSurfaces();
  5181. Int dx = newCellCoord.x-startCellNdx.x;
  5182. Int dy = newCellCoord.y-startCellNdx.y;
  5183. if (dx<0) dx = -dx;
  5184. if (dy<0) dy = -dy;
  5185. if (dx>1+radius) info.considerTransient = false;
  5186. if (dy>1+radius) info.considerTransient = false;
  5187. if (!checkForMovement(obj, info) || info.enemyFixed) {
  5188. if (!m_isTunneling) {
  5189. continue;
  5190. }
  5191. movementValid = false;
  5192. }
  5193. if (movementValid && !newCell->getPinched()) {
  5194. //Note to self - only turn off tunneling after check for movement.jba.
  5195. m_isTunneling = false;
  5196. }
  5197. if (!newCell->hasInfo()) {
  5198. if (!newCell->allocateInfo(newCellCoord)) {
  5199. // Out of cells for pathing...
  5200. return cellCount;
  5201. }
  5202. cellCount++;
  5203. }
  5204. newCostSoFar = newCell->costSoFar( parentCell );
  5205. if (info.allyMoving && dx<10 && dy<10) {
  5206. newCostSoFar += 3*COST_DIAGONAL;
  5207. }
  5208. if (newCell->getType() == PathfindCell::CELL_CLIFF && !newCell->getPinched() ) {
  5209. Coord3D fromPos;
  5210. fromPos.x = parentCell->getXIndex() * PATHFIND_CELL_SIZE_F ;
  5211. fromPos.y = parentCell->getYIndex() * PATHFIND_CELL_SIZE_F ;
  5212. fromPos.z = TheTerrainLogic->getGroundHeight(fromPos.x , fromPos.y);
  5213. Coord3D toPos;
  5214. toPos.x = newCellCoord.x * PATHFIND_CELL_SIZE_F ;
  5215. toPos.y = newCellCoord.y * PATHFIND_CELL_SIZE_F ;
  5216. toPos.z = TheTerrainLogic->getGroundHeight(toPos.x , toPos.y);
  5217. if ( fabs(fromPos.z - toPos.z)<PATHFIND_CELL_SIZE_F) {
  5218. newCostSoFar += 7*COST_DIAGONAL;
  5219. }
  5220. } else if (newCell->getPinched()) {
  5221. newCostSoFar += COST_ORTHOGONAL;
  5222. }
  5223. newCell->setBlockedByAlly(false);
  5224. if (info.allyFixedCount) {
  5225. if (canPathThroughUnits) {
  5226. newCostSoFar += 3*COST_DIAGONAL*info.allyFixedCount;
  5227. } else {
  5228. newCell->setBlockedByAlly(true);
  5229. newCostSoFar += 3*COST_DIAGONAL*info.allyFixedCount;
  5230. }
  5231. }
  5232. Int costRemaining = 0;
  5233. if (goalCell) {
  5234. if (attackDistance == 0) {
  5235. costRemaining = newCell->costToGoal( goalCell );
  5236. } else {
  5237. dx = newCellCoord.x - goalCell->getXIndex();
  5238. dy = newCellCoord.y - goalCell->getYIndex();
  5239. costRemaining = COST_ORTHOGONAL*sqrt(dx*dx + dy*dy);
  5240. costRemaining -= attackDistance/2;
  5241. if (costRemaining<0) costRemaining=0;
  5242. if (info.allyGoal) {
  5243. if (obj->isKindOf(KINDOF_VEHICLE)) {
  5244. newCostSoFar += 3*COST_ORTHOGONAL;
  5245. } else {
  5246. // Infantry can pass through infantry.
  5247. newCostSoFar += COST_ORTHOGONAL;
  5248. }
  5249. }
  5250. }
  5251. }
  5252. if (notZonePassable) {
  5253. newCostSoFar += 100*COST_ORTHOGONAL;
  5254. }
  5255. if (newCell->getType()==PathfindCell::CELL_OBSTACLE) {
  5256. newCostSoFar += 100*COST_ORTHOGONAL;
  5257. }
  5258. // check if this neighbor cell is already on the open (waiting to be tried)
  5259. // or closed (already tried) lists
  5260. if (onList)
  5261. {
  5262. // already on one of the lists - if existing costSoFar is less,
  5263. // the new cell is on a longer path, so skip it
  5264. if (newCell->getCostSoFar() <= newCostSoFar)
  5265. continue;
  5266. }
  5267. if (m_isTunneling) {
  5268. if (!validMovementPosition( isCrusher, locomotorSet.getValidSurfaces(), newCell, parentCell )) {
  5269. newCostSoFar += 10*COST_ORTHOGONAL;
  5270. }
  5271. }
  5272. newCell->setCostSoFar(newCostSoFar);
  5273. // keep track of path we're building - point back to cell we moved here from
  5274. newCell->setParentCell(parentCell) ;
  5275. if (m_isTunneling) {
  5276. costRemaining = 0; // find the closest valid cell.
  5277. }
  5278. newCell->setTotalCost(newCell->getCostSoFar() + costRemaining) ;
  5279. //DEBUG_LOG(("Cell (%d,%d), Parent cost %d, newCostSoFar %d, cost rem %d, tot %d\n",
  5280. // newCell->getXIndex(), newCell->getYIndex(),
  5281. // newCell->costSoFar(parentCell), newCostSoFar, costRemaining, newCell->getCostSoFar() + costRemaining));
  5282. // if newCell was on closed list, remove it from the list
  5283. if (newCell->getClosed())
  5284. m_closedList = newCell->removeFromClosedList( m_closedList );
  5285. // if the newCell was already on the open list, remove it so it can be re-inserted in order
  5286. if (newCell->getOpen())
  5287. m_openList = newCell->removeFromOpenList( m_openList );
  5288. // insert newCell in open list such that open list is sorted, smallest total path cost first
  5289. m_openList = newCell->putOnSortedOpenList( m_openList );
  5290. }
  5291. return cellCount;
  5292. }
  5293. /**
  5294. * Find a short, valid path between given locations.
  5295. * Uses A* algorithm.
  5296. */
  5297. Path *Pathfinder::findPath( Object *obj, const LocomotorSet& locomotorSet, const Coord3D *from,
  5298. const Coord3D *rawTo)
  5299. {
  5300. if (!quickDoesPathExist(locomotorSet, from, rawTo)) {
  5301. return NULL;
  5302. }
  5303. Bool isHuman = true;
  5304. if (obj && obj->getControllingPlayer() && (obj->getControllingPlayer()->getPlayerType()==PLAYER_COMPUTER)) {
  5305. isHuman = false; // computer gets to cheat.
  5306. }
  5307. m_zoneManager.clearPassableFlags();
  5308. Path *hPat = findHierarchicalPath(isHuman, locomotorSet, from, rawTo, false);
  5309. if (hPat) {
  5310. hPat->deleteInstance();
  5311. } else {
  5312. m_zoneManager.setAllPassable();
  5313. }
  5314. Path *pat = internalFindPath(obj, locomotorSet, from, rawTo);
  5315. if (pat!=NULL) {
  5316. return pat;
  5317. }
  5318. /* hierarchical build path code.
  5319. if (pat) {
  5320. Path *path = newInstance(Path);
  5321. PathNode *prior = NULL;
  5322. for (PathNode *node = pat->getLastNode(); node; node=node->getPrevious()) {
  5323. Bool unobstructed = true;
  5324. if (prior!=NULL) {
  5325. unobstructed = isLinePassable( obj, locomotorSet.getValidSurfaces(),
  5326. prior->getLayer(), *prior->getPosition(), *node->getPosition(), false, true);
  5327. }
  5328. if (unobstructed) {
  5329. path->prependNode( node->getPosition(), node->getLayer() );
  5330. path->getFirstNode()->setCanOptimize(node->getCanOptimize());
  5331. path->getFirstNode()->setNextOptimized(path->getFirstNode()->getNext());
  5332. } else {
  5333. Path *linkPath = findClosestPath(obj, locomotorSet, node->getPosition(),
  5334. prior->getPosition(), false, 0, true);
  5335. if (linkPath==NULL) {
  5336. DEBUG_LOG(("Couldn't find path - unexpected. jba.\n"));
  5337. continue;
  5338. }
  5339. PathNode *linkNode = linkPath->getLastNode();
  5340. if (linkNode==NULL) {
  5341. DEBUG_LOG(("Empty path - unexpected. jba.\n"));
  5342. continue;
  5343. }
  5344. for (linkNode=linkNode->getPrevious(); linkNode; linkNode=linkNode->getPrevious()) {
  5345. path->prependNode( linkNode->getPosition(), linkNode->getLayer() );
  5346. path->getFirstNode()->setCanOptimize(linkNode->getCanOptimize());
  5347. path->getFirstNode()->setNextOptimized(path->getFirstNode()->getNext());
  5348. }
  5349. linkPath->deleteInstance();
  5350. }
  5351. prior = node;
  5352. }
  5353. pat->deleteInstance();
  5354. path->optimize(obj, locomotorSet.getValidSurfaces(), false);
  5355. if (TheGlobalData->m_debugAI) {
  5356. setDebugPath(path);
  5357. }
  5358. return path;
  5359. }
  5360. */
  5361. return NULL;
  5362. }
  5363. /**
  5364. * Find a short, valid path between given locations.
  5365. * Uses A* algorithm.
  5366. */
  5367. Path *Pathfinder::internalFindPath( Object *obj, const LocomotorSet& locomotorSet, const Coord3D *from,
  5368. const Coord3D *rawTo)
  5369. {
  5370. //CRCDEBUG_LOG(("Pathfinder::findPath()\n"));
  5371. #ifdef INTENSE_DEBUG
  5372. DEBUG_LOG(("internal find path...\n"));
  5373. #endif
  5374. #if defined _DEBUG || defined _INTERNAL
  5375. Int startTimeMS = ::GetTickCount();
  5376. #endif
  5377. Bool centerInCell = true;
  5378. Int radius = 0;
  5379. if (obj) {
  5380. getRadiusAndCenter(obj, radius, centerInCell);
  5381. }
  5382. Bool isHuman = true;
  5383. if (obj && obj->getControllingPlayer() && (obj->getControllingPlayer()->getPlayerType()==PLAYER_COMPUTER)) {
  5384. isHuman = false; // computer gets to cheat.
  5385. }
  5386. if (rawTo->x == 0.0f && rawTo->y == 0.0f) {
  5387. DEBUG_LOG(("Attempting pathfind to 0,0, generally a bug.\n"));
  5388. return NULL;
  5389. }
  5390. DEBUG_ASSERTCRASH(m_openList==NULL && m_closedList == NULL, ("Dangling lists."));
  5391. if (m_isMapReady == false) {
  5392. return NULL;
  5393. }
  5394. Coord3D adjustTo = *rawTo;
  5395. Coord3D *to = &adjustTo;
  5396. Coord3D clipFrom = *from;
  5397. clip(&clipFrom, &adjustTo);
  5398. if (!centerInCell) {
  5399. adjustTo.x += PATHFIND_CELL_SIZE_F/2;
  5400. adjustTo.y += PATHFIND_CELL_SIZE_F/2;
  5401. }
  5402. m_isTunneling = false;
  5403. PathfindLayerEnum destinationLayer = TheTerrainLogic->getLayerForDestination(to);
  5404. // determine goal cell
  5405. PathfindCell *goalCell = getCell( destinationLayer, to );
  5406. if (goalCell == NULL) {
  5407. return NULL;
  5408. }
  5409. ICoord2D cell;
  5410. worldToCell( to, &cell );
  5411. if (!checkDestination(obj, cell.x, cell.y, destinationLayer, radius, centerInCell)) {
  5412. return false;
  5413. }
  5414. // determine start cell
  5415. ICoord2D startCellNdx;
  5416. worldToCell(&clipFrom, &startCellNdx);
  5417. PathfindLayerEnum layer = LAYER_GROUND;
  5418. if (obj) {
  5419. layer = obj->getLayer();
  5420. }
  5421. PathfindCell *parentCell = getClippedCell( layer,&clipFrom );
  5422. if (parentCell == NULL) {
  5423. return NULL;
  5424. }
  5425. ICoord2D pos2d;
  5426. worldToCell(to, &pos2d);
  5427. if (!goalCell->allocateInfo(pos2d)) {
  5428. return NULL;
  5429. }
  5430. if (parentCell!=goalCell) {
  5431. worldToCell(&clipFrom, &pos2d);
  5432. if (!parentCell->allocateInfo(pos2d)) {
  5433. goalCell->releaseInfo();
  5434. return NULL;
  5435. }
  5436. }
  5437. //
  5438. // Determine if this pathfind is "tunneling" or not.
  5439. // A tunneling pathfind starts from within an obstacle, and is allowed
  5440. // to ignore obstacle cells until it reaches a cell that is no longer
  5441. // classified as an obstacle. At that point, the pathfind behaves normally.
  5442. //
  5443. if (parentCell->getType() == PathfindCell::CELL_OBSTACLE) {
  5444. m_isTunneling = true;
  5445. }
  5446. else {
  5447. m_isTunneling = false;
  5448. }
  5449. Int zone1, zone2;
  5450. Bool isCrusher = obj ? obj->getCrusherLevel() > 0 : false;
  5451. zone1 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), isCrusher, parentCell->getZone());
  5452. zone2 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), isCrusher, goalCell->getZone());
  5453. if (layer==LAYER_WALL && zone1 == 0) {
  5454. return NULL;
  5455. }
  5456. if (destinationLayer==LAYER_WALL && zone2 == 0) {
  5457. return NULL;
  5458. }
  5459. if (goalCell->isObstaclePresent(m_ignoreObstacleID) || m_isTunneling) {
  5460. // Use terrain zones instead of building zones, since we are moving into or out of a building.
  5461. zone2 = m_zoneManager.getEffectiveTerrainZone(zone2);
  5462. zone2 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), isCrusher, zone2);
  5463. zone1 = m_zoneManager.getEffectiveTerrainZone(zone1);
  5464. zone1 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), isCrusher, zone1);
  5465. }
  5466. //DEBUG_LOG(("Zones %d to %d\n", zone1, zone2));
  5467. if ( zone1 != zone2) {
  5468. //DEBUG_LOG(("Intense Debug Info - Pathfind Zone screen failed-cannot reach desired location.\n"));
  5469. goalCell->releaseInfo();
  5470. parentCell->releaseInfo();
  5471. return NULL;
  5472. }
  5473. // sanity check - if destination is invalid, can't path there
  5474. if (validMovementPosition( isCrusher, destinationLayer, locomotorSet, to ) == false) {
  5475. m_isTunneling = false;
  5476. goalCell->releaseInfo();
  5477. parentCell->releaseInfo();
  5478. return NULL;
  5479. }
  5480. // sanity check - if source is invalid, we have to cheat
  5481. if (validMovementPosition( isCrusher, layer, locomotorSet, from ) == false) {
  5482. // somehow we got to an impassable location.
  5483. m_isTunneling = true;
  5484. }
  5485. parentCell->startPathfind(goalCell);
  5486. // initialize "open" list to contain start cell
  5487. m_openList = parentCell;
  5488. // "closed" list is initially empty
  5489. m_closedList = NULL;
  5490. Int cellCount = 0;
  5491. //
  5492. // Continue search until "open" list is empty, or
  5493. // until goal is found.
  5494. //
  5495. while( m_openList != NULL )
  5496. {
  5497. // take head cell off of open list - it has lowest estimated total path cost
  5498. parentCell = m_openList;
  5499. m_openList = parentCell->removeFromOpenList(m_openList);
  5500. if (parentCell == goalCell)
  5501. {
  5502. // success - found a path to the goal
  5503. Bool show = TheGlobalData->m_debugAI==AI_DEBUG_PATHS;
  5504. #ifdef INTENSE_DEBUG
  5505. DEBUG_LOG(("internal find path SUCCESS...\n"));
  5506. Int count = 0;
  5507. if (cellCount>1000 && obj) {
  5508. show = true;
  5509. 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));
  5510. #ifdef STATE_MACHINE_DEBUG
  5511. if( obj->getAIUpdateInterface() )
  5512. {
  5513. DEBUG_LOG(("State %s\n", obj->getAIUpdateInterface()->getCurrentStateName().str()));
  5514. }
  5515. #endif
  5516. TheScriptEngine->AppendDebugMessage("Big path", false);
  5517. }
  5518. #endif
  5519. if (show)
  5520. debugShowSearch(true);
  5521. m_isTunneling = false;
  5522. // construct and return path
  5523. Path *path = buildActualPath( obj, locomotorSet.getValidSurfaces(), from, goalCell, centerInCell, false );
  5524. parentCell->releaseInfo();
  5525. cleanOpenAndClosedLists();
  5526. return path;
  5527. }
  5528. // put parent cell onto closed list - its evaluation is finished
  5529. m_closedList = parentCell->putOnClosedList( m_closedList );
  5530. // Check to see if we can change layers in this cell.
  5531. checkChangeLayers(parentCell);
  5532. cellCount += examineNeighboringCells(parentCell, goalCell, locomotorSet, isHuman, centerInCell, radius, startCellNdx, obj, NO_ATTACK);
  5533. }
  5534. // failure - goal cannot be reached
  5535. #if defined _DEBUG || defined _INTERNAL
  5536. #ifdef INTENSE_DEBUG
  5537. DEBUG_LOG(("internal find path FAILURE...\n"));
  5538. #endif
  5539. if (TheGlobalData->m_debugAI == AI_DEBUG_PATHS)
  5540. {
  5541. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  5542. RGBColor color;
  5543. color.blue = 0;
  5544. color.red = color.green = 1;
  5545. addIcon(NULL, 0, 0, color);
  5546. debugShowSearch(false);
  5547. Coord3D pos;
  5548. pos = *from;
  5549. pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y ) + 0.5f;
  5550. addIcon(&pos, 3*PATHFIND_CELL_SIZE_F, 600, color);
  5551. pos = *to;
  5552. pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y ) + 0.5f;
  5553. addIcon(&pos, 3*PATHFIND_CELL_SIZE_F, 600, color);
  5554. Real dx, dy;
  5555. dx = from->x - to->x;
  5556. dy = from->y - to->y;
  5557. Int count = sqrt(dx*dx+dy*dy)/(PATHFIND_CELL_SIZE_F/2);
  5558. if (count<2) count = 2;
  5559. Int i;
  5560. color.green = 0;
  5561. for (i=1; i<count; i++) {
  5562. pos.x = from->x + (to->x-from->x)*i/count;
  5563. pos.y = from->y + (to->y-from->y)*i/count;
  5564. pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y ) + 0.5f;
  5565. addIcon(&pos, PATHFIND_CELL_SIZE_F/2, 60, color);
  5566. }
  5567. }
  5568. if (obj) {
  5569. Bool valid;
  5570. valid = validMovementPosition( isCrusher, obj->getLayer(), locomotorSet, to ) ;
  5571. DEBUG_LOG(("%d ", TheGameLogic->getFrame()));
  5572. DEBUG_LOG(("Pathfind failed from (%f,%f) to (%f,%f), OV %d\n", from->x, from->y, to->x, to->y, valid));
  5573. DEBUG_LOG(("Unit '%s', time %f, cells %d\n", obj->getTemplate()->getName().str(), (::GetTickCount()-startTimeMS)/1000.0f,cellCount));
  5574. #ifdef DUMP_PERF_STATS
  5575. TheGameLogic->incrementOverallFailedPathfinds();
  5576. #endif
  5577. #ifdef STATE_MACHINE_DEBUG
  5578. if( obj->getAIUpdateInterface() )
  5579. {
  5580. DEBUG_LOG(("state %s\n", obj->getAIUpdateInterface()->getCurrentStateName().str()));
  5581. }
  5582. #endif
  5583. }
  5584. #endif
  5585. m_isTunneling = false;
  5586. goalCell->releaseInfo();
  5587. cleanOpenAndClosedLists();
  5588. return NULL;
  5589. }
  5590. /**
  5591. * Checks to see if there is enough path width at this cell for ground
  5592. * movement. Returns the width available.
  5593. */
  5594. Int Pathfinder::clearCellForDiameter(Bool crusher, Int cellX, Int cellY, PathfindLayerEnum layer, Int pathDiameter)
  5595. {
  5596. Int radius = pathDiameter/2;
  5597. Int numCellsAbove = radius;
  5598. if (radius==0) numCellsAbove++;
  5599. Int i, j;
  5600. Bool clear = true;
  5601. Bool cutCorners = false;
  5602. if (radius>1) {
  5603. cutCorners = true;
  5604. // We remove the outside corner cells from the check.
  5605. }
  5606. for (i=cellX-radius; i<cellX+numCellsAbove; i++) {
  5607. Bool xMinOrMax = (i==cellX-radius) || (i==cellX+numCellsAbove-1);
  5608. for (j=cellY-radius; j<cellY+numCellsAbove; j++) {
  5609. Bool yMinOrMax = (j==cellY-radius) || (j==cellY+numCellsAbove-1);
  5610. if (xMinOrMax && yMinOrMax && cutCorners) {
  5611. continue; // this is an outside corner cell, and we are cutting corners. jba. :)
  5612. }
  5613. PathfindCell *cell = getCell(layer, i, j);
  5614. if (cell) {
  5615. if (cell->getType() != PathfindCell::CELL_CLEAR) {
  5616. if (cell->getType() == PathfindCell::CELL_OBSTACLE) {
  5617. if (cell->isObstacleFence()) {
  5618. if (!crusher) {
  5619. clear = false;
  5620. }
  5621. } else {
  5622. clear = false;
  5623. }
  5624. } else {
  5625. clear = false;
  5626. }
  5627. }
  5628. if (cell->getFlags() == PathfindCell::UNIT_PRESENT_FIXED && pathDiameter>=2) {
  5629. Object *obj = TheGameLogic->findObjectByID(cell->getPosUnit());
  5630. if (obj) {
  5631. if (crusher) {
  5632. if (obj->getCrushableLevel()>1) {
  5633. clear = false;
  5634. }
  5635. } else {
  5636. if (obj->getCrushableLevel()>0) {
  5637. clear = false;
  5638. }
  5639. }
  5640. }
  5641. }
  5642. } else {
  5643. return false; // off the map.
  5644. }
  5645. if (!clear) break;
  5646. }
  5647. }
  5648. if (clear) {
  5649. if (radius==0) return 1;
  5650. return 2*radius;
  5651. }
  5652. if (pathDiameter < 2) return 0;
  5653. return clearCellForDiameter(crusher, cellX, cellY, layer, pathDiameter-2);
  5654. }
  5655. /**
  5656. * Work backwards from goal cell to construct final path.
  5657. */
  5658. Path *Pathfinder::buildGroundPath(Bool isCrusher, const Coord3D *fromPos, PathfindCell *goalCell, Bool center, Int pathDiameter )
  5659. {
  5660. DEBUG_ASSERTCRASH( goalCell, ("Pathfinder::buildActualPath: goalCell == NULL") );
  5661. Path *path = newInstance(Path);
  5662. prependCells(path, fromPos, goalCell, center);
  5663. // cleanup the path by checking line of sight
  5664. path->optimizeGroundPath( isCrusher, pathDiameter );
  5665. #if defined _DEBUG || defined _INTERNAL
  5666. if (TheGlobalData->m_debugAI==AI_DEBUG_GROUND_PATHS)
  5667. {
  5668. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  5669. RGBColor color;
  5670. color.blue = 0;
  5671. color.red = color.green = 1;
  5672. Coord3D pos;
  5673. for( PathNode *node = path->getFirstNode(); node; node = node->getNext() )
  5674. {
  5675. // create objects to show path - they decay
  5676. pos = *node->getPosition();
  5677. color.red = color.green = 1;
  5678. if (node->getLayer() != LAYER_GROUND) {
  5679. color.red = 0;
  5680. }
  5681. addIcon(&pos, PATHFIND_CELL_SIZE_F*.25f, 200, color);
  5682. }
  5683. // show optimized path
  5684. for( node = path->getFirstNode(); node; node = node->getNextOptimized() )
  5685. {
  5686. pos = *node->getPosition();
  5687. addIcon(&pos, PATHFIND_CELL_SIZE_F*.8f, 200, color);
  5688. }
  5689. setDebugPath(path);
  5690. }
  5691. #endif
  5692. return path;
  5693. }
  5694. /**
  5695. * Work backwards from goal cell to construct final path.
  5696. */
  5697. Path *Pathfinder::buildHierachicalPath( const Coord3D *fromPos, PathfindCell *goalCell )
  5698. {
  5699. DEBUG_ASSERTCRASH( goalCell, ("Pathfinder::buildHierachicalPath: goalCell == NULL") );
  5700. Path *path = newInstance(Path);
  5701. prependCells(path, fromPos, goalCell, true);
  5702. #if defined _DEBUG || defined _INTERNAL
  5703. if (TheGlobalData->m_debugAI==AI_DEBUG_PATHS)
  5704. {
  5705. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  5706. RGBColor color;
  5707. color.blue = 0;
  5708. color.red = color.green = 1;
  5709. Coord3D pos;
  5710. Int i;
  5711. for (i=0; i<3; i++)
  5712. for( PathNode *node = path->getFirstNode(); node; node = node->getNext() )
  5713. {
  5714. // create objects to show path - they decay
  5715. pos = *node->getPosition();
  5716. color.red = 1;
  5717. color.green = 0.4f;
  5718. if (node->getLayer() != LAYER_GROUND) {
  5719. color.red = 0;
  5720. }
  5721. addIcon(&pos, PATHFIND_CELL_SIZE_F, 200, color);
  5722. }
  5723. setDebugPath(path);
  5724. }
  5725. #endif
  5726. return path;
  5727. }
  5728. struct MADStruct
  5729. {
  5730. Pathfinder *thePathfinder;
  5731. Object *obj;
  5732. ObjectID ignoreID;
  5733. };
  5734. /*static*/ Int Pathfinder::moveAlliesDestinationCallback(Pathfinder* pathfinder, PathfindCell* from, PathfindCell* to, Int to_x, Int to_y, void* userData)
  5735. {
  5736. MADStruct* d = (MADStruct*)userData;
  5737. if (to) {
  5738. if (to->getPosUnit()==INVALID_ID) {
  5739. return 0;
  5740. }
  5741. if (to->getPosUnit()==d->obj->getID()) {
  5742. return 0; // It's us.
  5743. }
  5744. if (to->getPosUnit()==d->ignoreID) {
  5745. return 0; // It's the one we are ignoring.
  5746. }
  5747. Object *otherObj = TheGameLogic->findObjectByID(to->getPosUnit());
  5748. if (otherObj==NULL) return 0;
  5749. if (d->obj->getRelationship(otherObj)!=ALLIES) {
  5750. return 0; // Only move allies.
  5751. }
  5752. if (otherObj && otherObj->getAI() && !otherObj->getAI()->isMoving()) {
  5753. //DEBUG_LOG(("Moving ally\n"));
  5754. otherObj->getAI()->aiMoveAwayFromUnit(d->obj, CMD_FROM_AI);
  5755. }
  5756. }
  5757. return 0; // keep going
  5758. }
  5759. void Pathfinder::moveAlliesAwayFromDestination(Object *obj,const Coord3D& destination)
  5760. {
  5761. MADStruct info;
  5762. info.obj = obj;
  5763. info.ignoreID = obj->getAI()->getIgnoredObstacleID();
  5764. info.thePathfinder = this;
  5765. PathfindLayerEnum layer = obj->getLayer();
  5766. if (layer==LAYER_GROUND) {
  5767. layer = TheTerrainLogic->getLayerForDestination(&destination);
  5768. }
  5769. iterateCellsAlongLine(*obj->getPosition(), destination, layer, moveAlliesDestinationCallback, &info);
  5770. }
  5771. struct GroundCellsStruct
  5772. {
  5773. Pathfinder *thePathfinder;
  5774. Bool centerInCell;
  5775. Int pathDiameter;
  5776. PathfindCell *goalCell;
  5777. Bool crusher;
  5778. };
  5779. /*static*/ Int Pathfinder::groundCellsCallback(Pathfinder* pathfinder, PathfindCell* from, PathfindCell* to, Int to_x, Int to_y, void* userData)
  5780. {
  5781. GroundCellsStruct* d = (GroundCellsStruct*)userData;
  5782. if (from && to) {
  5783. if (to->hasInfo()) {
  5784. if (to->getOpen() || to->getClosed())
  5785. {
  5786. // already on one of the lists
  5787. return 1; // abort.
  5788. }
  5789. }
  5790. // See how wide the cell is.
  5791. Int clearDiameter = d->thePathfinder->clearCellForDiameter(d->crusher, to_x, to_y, to->getLayer(), d->pathDiameter);
  5792. if (clearDiameter != d->pathDiameter) {
  5793. return 1;
  5794. }
  5795. ICoord2D newCellCoord;
  5796. newCellCoord.x = to_x;
  5797. newCellCoord.y = to_y;
  5798. if (!to->allocateInfo(newCellCoord)) {
  5799. // Out of cells for pathing...
  5800. return 1;
  5801. }
  5802. UnsignedInt newCostSoFar = from->getCostSoFar( ) + 0.5f*COST_ORTHOGONAL;
  5803. to->setBlockedByAlly(false);
  5804. Int costRemaining = 0;
  5805. costRemaining = to->costToGoal( d->goalCell );
  5806. to->setCostSoFar(newCostSoFar);
  5807. // keep track of path we're building - point back to cell we moved here from
  5808. to->setParentCell(from) ;
  5809. to->setTotalCost(to->getCostSoFar() + costRemaining) ;
  5810. // insert to in open list such that open list is sorted, smallest total path cost first
  5811. d->thePathfinder->m_openList = to->putOnSortedOpenList( d->thePathfinder->m_openList );
  5812. }
  5813. return 0; // keep going
  5814. }
  5815. /**
  5816. * Find a short, valid path between given locations.
  5817. * Uses A* algorithm.
  5818. */
  5819. Path *Pathfinder::findGroundPath( const Coord3D *from,
  5820. const Coord3D *rawTo, Int pathDiameter, Bool crusher)
  5821. {
  5822. //CRCDEBUG_LOG(("Pathfinder::findGroundPath()\n"));
  5823. #if defined _DEBUG || defined _INTERNAL
  5824. Int startTimeMS = ::GetTickCount();
  5825. #endif
  5826. #ifdef INTENSE_DEBUG
  5827. DEBUG_LOG(("Find ground path..."));
  5828. #endif
  5829. Bool centerInCell = false;
  5830. m_zoneManager.clearPassableFlags();
  5831. Bool isHuman = true;
  5832. Path *hPat = internal_findHierarchicalPath(isHuman, LOCOMOTORSURFACE_GROUND, from, rawTo, false, false);
  5833. if (hPat) {
  5834. hPat->deleteInstance();
  5835. } else {
  5836. m_zoneManager.setAllPassable();
  5837. }
  5838. if (rawTo->x == 0.0f && rawTo->y == 0.0f) {
  5839. DEBUG_LOG(("Attempting pathfind to 0,0, generally a bug.\n"));
  5840. return NULL;
  5841. }
  5842. DEBUG_ASSERTCRASH(m_openList==NULL && m_closedList == NULL, ("Dangling lists."));
  5843. if (m_isMapReady == false) {
  5844. return NULL;
  5845. }
  5846. Coord3D adjustTo = *rawTo;
  5847. Coord3D *to = &adjustTo;
  5848. Coord3D clipFrom = *from;
  5849. clip(&clipFrom, &adjustTo);
  5850. m_isTunneling = false;
  5851. PathfindLayerEnum destinationLayer = TheTerrainLogic->getLayerForDestination(to);
  5852. ICoord2D cell;
  5853. worldToCell( to, &cell );
  5854. if (pathDiameter!=clearCellForDiameter(crusher, cell.x, cell.y, destinationLayer, pathDiameter)) {
  5855. Int offset=1;
  5856. ICoord2D newCell;
  5857. const Int MAX_OFFSET = 8;
  5858. while (offset<MAX_OFFSET) {
  5859. newCell = cell;
  5860. cell.x += offset;
  5861. if (clearCellForDiameter(crusher, cell.x, cell.y, destinationLayer, pathDiameter)==pathDiameter) break;
  5862. cell.y += offset;
  5863. if (clearCellForDiameter(crusher, cell.x, cell.y, destinationLayer, pathDiameter)==pathDiameter) break;
  5864. cell.x -= offset;
  5865. if (clearCellForDiameter(crusher, cell.x, cell.y, destinationLayer, pathDiameter)==pathDiameter) break;
  5866. cell.x -= offset;
  5867. if (clearCellForDiameter(crusher, cell.x, cell.y, destinationLayer, pathDiameter)==pathDiameter) break;
  5868. cell.y -= offset;
  5869. if (clearCellForDiameter(crusher, cell.x, cell.y, destinationLayer, pathDiameter)==pathDiameter) break;
  5870. cell.y -= offset;
  5871. if (clearCellForDiameter(crusher, cell.x, cell.y, destinationLayer, pathDiameter)==pathDiameter) break;
  5872. cell.x += offset;
  5873. if (clearCellForDiameter(crusher, cell.x, cell.y, destinationLayer, pathDiameter)==pathDiameter) break;
  5874. cell.x += offset;
  5875. if (clearCellForDiameter(crusher, cell.x, cell.y, destinationLayer, pathDiameter)==pathDiameter) break;
  5876. offset++;
  5877. cell = newCell;
  5878. }
  5879. if (offset >= MAX_OFFSET) {
  5880. return NULL;
  5881. }
  5882. }
  5883. // determine goal cell
  5884. PathfindCell *goalCell = getCell( destinationLayer, cell.x, cell.y );
  5885. if (goalCell == NULL) {
  5886. return NULL;
  5887. }
  5888. if (!goalCell->allocateInfo(cell)) {
  5889. return NULL;
  5890. }
  5891. // determine start cell
  5892. ICoord2D startCellNdx;
  5893. PathfindLayerEnum layer = TheTerrainLogic->getLayerForDestination(from);
  5894. PathfindCell *parentCell = getClippedCell( layer,&clipFrom );
  5895. if (parentCell == NULL) {
  5896. return NULL;
  5897. }
  5898. if (parentCell!=goalCell) {
  5899. worldToCell(&clipFrom, &startCellNdx);
  5900. if (!parentCell->allocateInfo(startCellNdx)) {
  5901. goalCell->releaseInfo();
  5902. return NULL;
  5903. }
  5904. }
  5905. Int zone1, zone2;
  5906. // m_isCrusher = false;
  5907. zone1 = m_zoneManager.getEffectiveZone(LOCOMOTORSURFACE_GROUND, false, parentCell->getZone());
  5908. zone2 = m_zoneManager.getEffectiveZone(LOCOMOTORSURFACE_GROUND, false, goalCell->getZone());
  5909. //DEBUG_LOG(("Zones %d to %d\n", zone1, zone2));
  5910. if ( zone1 != zone2) {
  5911. goalCell->releaseInfo();
  5912. parentCell->releaseInfo();
  5913. return NULL;
  5914. }
  5915. parentCell->startPathfind(goalCell);
  5916. // initialize "open" list to contain start cell
  5917. m_openList = parentCell;
  5918. // "closed" list is initially empty
  5919. m_closedList = NULL;
  5920. //
  5921. // Continue search until "open" list is empty, or
  5922. // until goal is found.
  5923. //
  5924. Int cellCount = 0;
  5925. while( m_openList != NULL )
  5926. {
  5927. // take head cell off of open list - it has lowest estimated total path cost
  5928. parentCell = m_openList;
  5929. m_openList = parentCell->removeFromOpenList(m_openList);
  5930. if (parentCell == goalCell)
  5931. {
  5932. // success - found a path to the goal
  5933. #ifdef INTENSE_DEBUG
  5934. DEBUG_LOG((" time %d msec %d cells", (::GetTickCount()-startTimeMS), cellCount));
  5935. DEBUG_LOG((" SUCCESS\n"));
  5936. #endif
  5937. #if defined _DEBUG || defined _INTERNAL
  5938. Bool show = TheGlobalData->m_debugAI==AI_DEBUG_GROUND_PATHS;
  5939. if (show)
  5940. debugShowSearch(true);
  5941. #endif
  5942. m_isTunneling = false;
  5943. // construct and return path
  5944. Path *path = buildGroundPath(crusher, from, goalCell, centerInCell, pathDiameter );
  5945. parentCell->releaseInfo();
  5946. cleanOpenAndClosedLists();
  5947. return path;
  5948. }
  5949. // put parent cell onto closed list - its evaluation is finished
  5950. m_closedList = parentCell->putOnClosedList( m_closedList );
  5951. // Check to see if we can change layers in this cell.
  5952. checkChangeLayers(parentCell);
  5953. GroundCellsStruct info;
  5954. info.thePathfinder = this;
  5955. info.centerInCell = centerInCell;
  5956. info.pathDiameter = pathDiameter;
  5957. info.goalCell = goalCell;
  5958. info.crusher = crusher;
  5959. ICoord2D start, end;
  5960. start.x = parentCell->getXIndex();
  5961. start.y = parentCell->getYIndex();
  5962. end.x = goalCell->getXIndex();
  5963. end.y = goalCell->getYIndex();
  5964. iterateCellsAlongLine(start, end, parentCell->getLayer(), groundCellsCallback, &info);
  5965. // expand search to neighboring orthogonal cells
  5966. static ICoord2D delta[] =
  5967. {
  5968. { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 },
  5969. { 1, 1 }, { -1, 1 }, { -1, -1 }, { 1, -1 }
  5970. };
  5971. const Int numNeighbors = 8;
  5972. const Int firstDiagonal = 4;
  5973. ICoord2D newCellCoord;
  5974. PathfindCell *newCell;
  5975. const Int adjacent[5] = {0, 1, 2, 3, 0};
  5976. Bool neighborFlags[8] = {false, false, false, false, false, false, false};
  5977. UnsignedInt newCostSoFar;
  5978. for( int i=0; i<numNeighbors; i++ )
  5979. {
  5980. neighborFlags[i] = false;
  5981. // determine neighbor cell to try
  5982. newCellCoord.x = parentCell->getXIndex() + delta[i].x;
  5983. newCellCoord.y = parentCell->getYIndex() + delta[i].y;
  5984. // get the neighboring cell
  5985. newCell = getCell(parentCell->getLayer(), newCellCoord.x, newCellCoord.y );
  5986. // check if cell is on the map
  5987. if (newCell == NULL)
  5988. continue;
  5989. if ((newCell->getLayer()==LAYER_GROUND) && !m_zoneManager.isPassable(newCellCoord.x, newCellCoord.y)) {
  5990. // check if we are within 3.
  5991. Bool passable = false;
  5992. if (m_zoneManager.clipIsPassable(newCellCoord.x+3, newCellCoord.y+3)) passable = true;
  5993. if (m_zoneManager.clipIsPassable(newCellCoord.x-3, newCellCoord.y+3)) passable = true;
  5994. if (m_zoneManager.clipIsPassable(newCellCoord.x+3, newCellCoord.y-3)) passable = true;
  5995. if (m_zoneManager.clipIsPassable(newCellCoord.x-3, newCellCoord.y-3)) passable = true;
  5996. if (!passable) continue;
  5997. }
  5998. // check if this neighbor cell is already on the open (waiting to be tried)
  5999. // or closed (already tried) lists
  6000. Bool onList = false;
  6001. if (newCell->hasInfo()) {
  6002. if (newCell->getOpen() || newCell->getClosed())
  6003. {
  6004. // already on one of the lists
  6005. onList = true;
  6006. }
  6007. }
  6008. Int clearDiameter = 0;
  6009. if (newCell!=goalCell) {
  6010. if (i>=firstDiagonal) {
  6011. // make sure one of the adjacent sides is open.
  6012. if (!neighborFlags[adjacent[i-4]] && !neighborFlags[adjacent[i-3]]) {
  6013. continue;
  6014. }
  6015. }
  6016. // See how wide the cell is.
  6017. clearDiameter = clearCellForDiameter(crusher, newCellCoord.x, newCellCoord.y, newCell->getLayer(), pathDiameter);
  6018. if (newCell->getType() != PathfindCell::CELL_CLEAR) {
  6019. continue;
  6020. }
  6021. if (newCell->getPinched()) {
  6022. continue;
  6023. }
  6024. neighborFlags[i] = true;
  6025. if (!newCell->allocateInfo(newCellCoord)) {
  6026. // Out of cells for pathing...
  6027. continue;
  6028. }
  6029. cellCount++;
  6030. newCostSoFar = newCell->costSoFar( parentCell );
  6031. if (clearDiameter<pathDiameter) {
  6032. int delta = pathDiameter-clearDiameter;
  6033. newCostSoFar += 0.6f*(delta*COST_ORTHOGONAL);
  6034. }
  6035. newCell->setBlockedByAlly(false);
  6036. }
  6037. Int costRemaining = 0;
  6038. costRemaining = newCell->costToGoal( goalCell );
  6039. // check if this neighbor cell is already on the open (waiting to be tried)
  6040. // or closed (already tried) lists
  6041. if (onList)
  6042. {
  6043. // already on one of the lists - if existing costSoFar is less,
  6044. // the new cell is on a longer path, so skip it
  6045. if (newCell->getCostSoFar() <= newCostSoFar)
  6046. continue;
  6047. }
  6048. //DEBUG_LOG(("CELL(%d,%d)L%d CD%d CSF %d, CR%d // ",newCell->getXIndex(), newCell->getYIndex(),
  6049. // newCell->getLayer(), clearDiameter, newCostSoFar, costRemaining));
  6050. //if ((cellCount&7)==0) DEBUG_LOG(("\n"));
  6051. newCell->setCostSoFar(newCostSoFar);
  6052. // keep track of path we're building - point back to cell we moved here from
  6053. newCell->setParentCell(parentCell) ;
  6054. newCell->setTotalCost(newCell->getCostSoFar() + costRemaining) ;
  6055. //DEBUG_LOG(("Cell (%d,%d), Parent cost %d, newCostSoFar %d, cost rem %d, tot %d\n",
  6056. // newCell->getXIndex(), newCell->getYIndex(),
  6057. // newCell->costSoFar(parentCell), newCostSoFar, costRemaining, newCell->getCostSoFar() + costRemaining));
  6058. // if newCell was on closed list, remove it from the list
  6059. if (newCell->getClosed())
  6060. m_closedList = newCell->removeFromClosedList( m_closedList );
  6061. // if the newCell was already on the open list, remove it so it can be re-inserted in order
  6062. if (newCell->getOpen())
  6063. m_openList = newCell->removeFromOpenList( m_openList );
  6064. // insert newCell in open list such that open list is sorted, smallest total path cost first
  6065. m_openList = newCell->putOnSortedOpenList( m_openList );
  6066. }
  6067. }
  6068. // failure - goal cannot be reached
  6069. #ifdef INTENSE_DEBUG
  6070. DEBUG_LOG((" FAILURE\n"));
  6071. #endif
  6072. #if defined _DEBUG || defined _INTERNAL
  6073. if (TheGlobalData->m_debugAI)
  6074. {
  6075. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  6076. RGBColor color;
  6077. color.blue = 0;
  6078. color.red = color.green = 1;
  6079. addIcon(NULL, 0, 0, color);
  6080. debugShowSearch(false);
  6081. Coord3D pos;
  6082. pos = *from;
  6083. pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y ) + 0.5f;
  6084. addIcon(&pos, 3*PATHFIND_CELL_SIZE_F, 600, color);
  6085. pos = *to;
  6086. pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y ) + 0.5f;
  6087. addIcon(&pos, 3*PATHFIND_CELL_SIZE_F, 600, color);
  6088. Real dx, dy;
  6089. dx = from->x - to->x;
  6090. dy = from->y - to->y;
  6091. Int count = sqrt(dx*dx+dy*dy)/(PATHFIND_CELL_SIZE_F/2);
  6092. if (count<2) count = 2;
  6093. Int i;
  6094. color.green = 0;
  6095. for (i=1; i<count; i++) {
  6096. pos.x = from->x + (to->x-from->x)*i/count;
  6097. pos.y = from->y + (to->y-from->y)*i/count;
  6098. pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y ) + 0.5f;
  6099. addIcon(&pos, PATHFIND_CELL_SIZE_F/2, 60, color);
  6100. }
  6101. }
  6102. DEBUG_LOG(("%d ", TheGameLogic->getFrame()));
  6103. DEBUG_LOG(("FindGroundPath failed from (%f,%f) to (%f,%f)\n", from->x, from->y, to->x, to->y));
  6104. DEBUG_LOG(("time %f\n", (::GetTickCount()-startTimeMS)/1000.0f));
  6105. #endif
  6106. #ifdef DUMP_PERF_STATS
  6107. TheGameLogic->incrementOverallFailedPathfinds();
  6108. #endif
  6109. m_isTunneling = false;
  6110. goalCell->releaseInfo();
  6111. cleanOpenAndClosedLists();
  6112. return NULL;
  6113. }
  6114. /**
  6115. * Find a short, valid path between given locations.
  6116. * Uses A* algorithm.
  6117. */
  6118. void Pathfinder::processHierarchicalCell( const ICoord2D &scanCell, const ICoord2D &delta, PathfindCell *parentCell,
  6119. PathfindCell *goalCell, UnsignedShort parentZone,
  6120. UnsignedShort *examinedZones, Int &numExZones,
  6121. Bool crusher, Int &cellCount)
  6122. {
  6123. if (scanCell.x<m_extent.lo.x || scanCell.x>m_extent.hi.x ||
  6124. scanCell.y<m_extent.lo.y || scanCell.y>m_extent.hi.y) {
  6125. return;
  6126. }
  6127. if (parentZone == m_zoneManager.getBlockZone(LOCOMOTORSURFACE_GROUND,
  6128. crusher, scanCell.x, scanCell.y, m_map)) {
  6129. PathfindCell *newCell = getCell(LAYER_GROUND, scanCell.x, scanCell.y);
  6130. if (newCell->hasInfo() && (newCell->getOpen() || newCell->getClosed())) return; // already looked at this one.
  6131. ICoord2D adjacentCell = scanCell;
  6132. //DEBUG_ASSERTCRASH(parentZone==newCell->getZone(), ("Different zones?"));
  6133. if (parentZone!=newCell->getZone()) return;
  6134. adjacentCell.x += delta.x;
  6135. adjacentCell.y += delta.y;
  6136. if (adjacentCell.x<m_extent.lo.x || adjacentCell.x>m_extent.hi.x ||
  6137. adjacentCell.y<m_extent.lo.y || adjacentCell.y>m_extent.hi.y) {
  6138. return;
  6139. }
  6140. PathfindCell *adjNewCell = getCell(LAYER_GROUND, adjacentCell.x, adjacentCell.y);
  6141. if (adjNewCell->hasInfo() && (adjNewCell->getOpen() || adjNewCell->getClosed())) return; // already looked at this one.
  6142. UnsignedShort parentGlobalZone = m_zoneManager.getEffectiveZone(LOCOMOTORSURFACE_GROUND, crusher, parentZone);
  6143. /// @todo - somehow out of bounds or bogus newZone.
  6144. UnsignedShort newZone = m_zoneManager.getBlockZone(LOCOMOTORSURFACE_GROUND,
  6145. crusher, adjacentCell.x, adjacentCell.y, m_map);
  6146. UnsignedShort newGlobalZone = m_zoneManager.getEffectiveZone(LOCOMOTORSURFACE_GROUND, crusher, newZone);
  6147. if (newGlobalZone != parentGlobalZone) {
  6148. return; // can't step over. jba.
  6149. }
  6150. Int j;
  6151. Bool found=false;
  6152. for (j=0; j<numExZones; j++) {
  6153. if (examinedZones[j] == newZone) {
  6154. found = true;
  6155. break;
  6156. }
  6157. }
  6158. if (found) {
  6159. return;
  6160. }
  6161. newCell->allocateInfo(scanCell);
  6162. if (!newCell->getClosed() && !newCell->getOpen()) {
  6163. m_closedList = newCell->putOnClosedList(m_closedList);
  6164. }
  6165. adjNewCell->allocateInfo(adjacentCell);
  6166. cellCount++;
  6167. Int curCost = adjNewCell->costToHierGoal(parentCell);
  6168. Int remCost = adjNewCell->costToHierGoal(goalCell);
  6169. if (adjNewCell->getPinched() || newCell->getPinched()) {
  6170. curCost += 2*COST_ORTHOGONAL;
  6171. } else {
  6172. examinedZones[numExZones] = newZone;
  6173. numExZones++;
  6174. }
  6175. adjNewCell->setCostSoFar(parentCell->getCostSoFar() + curCost);
  6176. adjNewCell->setTotalCost(adjNewCell->getCostSoFar()+remCost);
  6177. adjNewCell->setParentCellHierarchical(parentCell);
  6178. // insert newCell in open list such that open list is sorted, smallest total path cost first
  6179. m_openList = adjNewCell->putOnSortedOpenList( m_openList );
  6180. }
  6181. }
  6182. /**
  6183. * Find a short, valid path between given locations.
  6184. * Uses A* algorithm.
  6185. */
  6186. Path *Pathfinder::findHierarchicalPath( Bool isHuman, const LocomotorSet& locomotorSet, const Coord3D *from,
  6187. const Coord3D *to, Bool crusher)
  6188. {
  6189. return internal_findHierarchicalPath(isHuman, locomotorSet.getValidSurfaces(), from, to, crusher, FALSE);
  6190. }
  6191. /**
  6192. * Find a short, valid path between given locations.
  6193. * Uses A* algorithm.
  6194. */
  6195. Path *Pathfinder::findClosestHierarchicalPath( Bool isHuman, const LocomotorSet& locomotorSet, const Coord3D *from,
  6196. const Coord3D *to, Bool crusher)
  6197. {
  6198. return internal_findHierarchicalPath(isHuman, locomotorSet.getValidSurfaces(), from, to, crusher, TRUE);
  6199. }
  6200. /**
  6201. * Find a short, valid path between given locations.
  6202. * Uses A* algorithm.
  6203. */
  6204. Path *Pathfinder::internal_findHierarchicalPath( Bool isHuman, const LocomotorSurfaceTypeMask locomotorSurface, const Coord3D *from,
  6205. const Coord3D *rawTo, Bool crusher, Bool closestOK)
  6206. {
  6207. //CRCDEBUG_LOG(("Pathfinder::findGroundPath()\n"));
  6208. #if defined _DEBUG || defined _INTERNAL
  6209. Int startTimeMS = ::GetTickCount();
  6210. #endif
  6211. if (rawTo->x == 0.0f && rawTo->y == 0.0f) {
  6212. DEBUG_LOG(("Attempting pathfind to 0,0, generally a bug.\n"));
  6213. return NULL;
  6214. }
  6215. DEBUG_ASSERTCRASH(m_openList==NULL && m_closedList == NULL, ("Dangling lists."));
  6216. if (m_isMapReady == false) {
  6217. return NULL;
  6218. }
  6219. Coord3D adjustTo = *rawTo;
  6220. Coord3D *to = &adjustTo;
  6221. Coord3D clipFrom = *from;
  6222. clip(&clipFrom, &adjustTo);
  6223. m_isTunneling = false;
  6224. PathfindLayerEnum destinationLayer = TheTerrainLogic->getLayerForDestination(to);
  6225. ICoord2D cell;
  6226. worldToCell( to, &cell );
  6227. // determine goal cell
  6228. PathfindCell *goalCell = getCell( destinationLayer, cell.x, cell.y );
  6229. if (goalCell == NULL) {
  6230. return NULL;
  6231. }
  6232. if (!goalCell->allocateInfo(cell)) {
  6233. return NULL;
  6234. }
  6235. // determine start cell
  6236. ICoord2D startCellNdx;
  6237. PathfindLayerEnum layer = TheTerrainLogic->getLayerForDestination(from);
  6238. PathfindCell *parentCell = getClippedCell( layer,&clipFrom );
  6239. if (parentCell == NULL) {
  6240. return NULL;
  6241. }
  6242. if (parentCell!=goalCell) {
  6243. worldToCell(&clipFrom, &startCellNdx);
  6244. if (!parentCell->allocateInfo(startCellNdx)) {
  6245. goalCell->releaseInfo();
  6246. return NULL;
  6247. }
  6248. }
  6249. Int zone1, zone2;
  6250. // m_isCrusher = false;
  6251. zone1 = m_zoneManager.getEffectiveZone(locomotorSurface, false, parentCell->getZone());
  6252. zone2 = m_zoneManager.getEffectiveZone(locomotorSurface, false, goalCell->getZone());
  6253. if ( zone1 != zone2) {
  6254. goalCell->releaseInfo();
  6255. parentCell->releaseInfo();
  6256. return NULL;
  6257. }
  6258. parentCell->startPathfind(goalCell);
  6259. // "closed" list is initially empty
  6260. m_closedList = NULL;
  6261. Int cellCount = 0;
  6262. UnsignedShort goalBlockZone;
  6263. ICoord2D goalBlockNdx;
  6264. if (goalCell->getLayer()==LAYER_GROUND) {
  6265. goalBlockZone = m_zoneManager.getBlockZone(locomotorSurface,
  6266. crusher, goalCell->getXIndex(), goalCell->getYIndex(), m_map);
  6267. goalBlockNdx.x = goalCell->getXIndex()/PathfindZoneManager::ZONE_BLOCK_SIZE;
  6268. goalBlockNdx.y = goalCell->getYIndex()/PathfindZoneManager::ZONE_BLOCK_SIZE;
  6269. } else {
  6270. goalBlockZone = goalCell->getZone();
  6271. goalBlockNdx.x = -1;
  6272. goalBlockNdx.y = -1;
  6273. }
  6274. if (parentCell->getLayer()==LAYER_GROUND) {
  6275. // initialize "open" list to contain start cell
  6276. m_openList = parentCell;
  6277. } else {
  6278. m_openList = parentCell;
  6279. PathfindLayerEnum layer = parentCell->getLayer();
  6280. // We're starting on a bridge, so link to land at the bridge end points.
  6281. ICoord2D ndx;
  6282. ICoord2D toNdx;
  6283. m_layers[layer].getStartCellIndex(&ndx);
  6284. m_layers[layer].getEndCellIndex(&toNdx);
  6285. PathfindCell *cell = getCell(LAYER_GROUND, toNdx.x, toNdx.y);
  6286. PathfindCell *startCell = getCell(LAYER_GROUND, ndx.x, ndx.y);
  6287. if (cell && startCell) {
  6288. // Close parent cell;
  6289. m_openList = parentCell->removeFromOpenList(m_openList);
  6290. m_closedList = parentCell->putOnClosedList(m_closedList);
  6291. startCell->allocateInfo(ndx);
  6292. startCell->setParentCellHierarchical(parentCell);
  6293. cellCount++;
  6294. Int curCost = startCell->costToHierGoal(parentCell);
  6295. Int remCost = startCell->costToHierGoal(goalCell);
  6296. startCell->setCostSoFar(curCost);
  6297. startCell->setTotalCost(remCost);
  6298. startCell->setParentCellHierarchical(parentCell);
  6299. // insert newCell in open list such that open list is sorted, smallest total path cost first
  6300. m_openList = startCell->putOnSortedOpenList( m_openList );
  6301. cellCount++;
  6302. cell->allocateInfo(toNdx);
  6303. curCost = cell->costToHierGoal(parentCell);
  6304. remCost = cell->costToHierGoal(goalCell);
  6305. cell->setCostSoFar(curCost);
  6306. cell->setTotalCost(remCost);
  6307. cell->setParentCellHierarchical(parentCell);
  6308. // insert newCell in open list such that open list is sorted, smallest total path cost first
  6309. m_openList = cell->putOnSortedOpenList( m_openList );
  6310. }
  6311. }
  6312. PathfindCell *closestCell = NULL;
  6313. Real closestDistSqr = sqr(HUGE_DIST);
  6314. //
  6315. // Continue search until "open" list is empty, or
  6316. // until goal is found.
  6317. //
  6318. while( m_openList != NULL )
  6319. {
  6320. // take head cell off of open list - it has lowest estimated total path cost
  6321. parentCell = m_openList;
  6322. m_openList = parentCell->removeFromOpenList(m_openList);
  6323. UnsignedShort parentZone;
  6324. if (parentCell->getLayer()==LAYER_GROUND) {
  6325. parentZone = m_zoneManager.getBlockZone(locomotorSurface,
  6326. crusher, parentCell->getXIndex(), parentCell->getYIndex(), m_map);
  6327. } else {
  6328. parentZone = parentCell->getZone();
  6329. }
  6330. Bool reachedGoal = false;
  6331. Int blockX = parentCell->getXIndex()/PathfindZoneManager::ZONE_BLOCK_SIZE;
  6332. Int blockY = parentCell->getYIndex()/PathfindZoneManager::ZONE_BLOCK_SIZE;
  6333. if (parentZone == goalBlockZone) {
  6334. if (goalBlockNdx.x == -1 || (blockX==goalBlockNdx.x && blockY == goalBlockNdx.y)) {
  6335. reachedGoal = true;
  6336. } else {
  6337. DEBUG_LOG(("Hmm, got match before correct cell."));
  6338. }
  6339. }
  6340. ICoord2D zoneBlockExtent;
  6341. m_zoneManager.getExtent(zoneBlockExtent);
  6342. if (!reachedGoal && m_zoneManager.interactsWithBridge(parentCell->getXIndex(), parentCell->getYIndex())) {
  6343. Int i;
  6344. for (i=0; i<=LAYER_LAST; i++) {
  6345. if (m_layers[i].isUnused() || m_layers[i].isDestroyed()) {
  6346. continue;
  6347. }
  6348. ICoord2D ndx;
  6349. ICoord2D toNdx;
  6350. m_layers[i].getStartCellIndex(&ndx);
  6351. m_layers[i].getEndCellIndex(&toNdx);
  6352. if (ndx.x/PathfindZoneManager::ZONE_BLOCK_SIZE != blockX ||
  6353. ndx.y/PathfindZoneManager::ZONE_BLOCK_SIZE != blockY) {
  6354. m_layers[i].getStartCellIndex(&toNdx);
  6355. m_layers[i].getEndCellIndex(&ndx);
  6356. }
  6357. if (ndx.x<0 || ndx.y<0) continue;
  6358. if (toNdx.x<0 || toNdx.y<0) continue;
  6359. if (ndx.x/PathfindZoneManager::ZONE_BLOCK_SIZE == blockX &&
  6360. ndx.y/PathfindZoneManager::ZONE_BLOCK_SIZE == blockY) {
  6361. // Bridge connects to this block.
  6362. Int bridgeZone = m_zoneManager.getBlockZone(locomotorSurface, crusher, ndx.x, ndx.y, m_map);
  6363. if (bridgeZone != parentZone) {
  6364. continue;
  6365. }
  6366. // We have a winner.
  6367. if (m_layers[i].getZone() == goalBlockZone) {
  6368. reachedGoal = true;
  6369. break;
  6370. }
  6371. PathfindCell *cell = getCell(LAYER_GROUND, toNdx.x, toNdx.y);
  6372. if (cell==NULL) continue;
  6373. if (cell->hasInfo() && (cell->getClosed() || cell->getOpen())) {
  6374. continue;
  6375. }
  6376. PathfindCell *startCell = getCell(LAYER_GROUND, ndx.x, ndx.y);
  6377. if (startCell==NULL) continue;
  6378. if (startCell != parentCell) {
  6379. startCell->allocateInfo(ndx);
  6380. startCell->setParentCellHierarchical(parentCell);
  6381. if (!startCell->getClosed() && !startCell->getOpen()) {
  6382. m_closedList = startCell->putOnClosedList(m_closedList);
  6383. }
  6384. }
  6385. cell->allocateInfo(toNdx);
  6386. cell->setParentCellHierarchical(startCell);
  6387. cellCount++;
  6388. Int curCost = cell->costToHierGoal(startCell);
  6389. Int remCost = cell->costToHierGoal(goalCell);
  6390. cell->setCostSoFar(startCell->getCostSoFar() + curCost);
  6391. cell->setTotalCost(cell->getCostSoFar()+remCost);
  6392. cell->setParentCellHierarchical(startCell);
  6393. // insert newCell in open list such that open list is sorted, smallest total path cost first
  6394. m_openList = cell->putOnSortedOpenList( m_openList );
  6395. }
  6396. }
  6397. }
  6398. if (reachedGoal)
  6399. {
  6400. if (parentCell != goalCell) {
  6401. goalCell->setParentCellHierarchical(parentCell);
  6402. }
  6403. // success - found a path to the goal
  6404. m_isTunneling = false;
  6405. // construct and return path
  6406. Path *path = buildHierachicalPath( from, goalCell );
  6407. #if defined _DEBUG || defined _INTERNAL
  6408. Bool show = TheGlobalData->m_debugAI==AI_DEBUG_PATHS;
  6409. show |= (TheGlobalData->m_debugAI==AI_DEBUG_GROUND_PATHS);
  6410. if (show) {
  6411. debugShowSearch(true);
  6412. #if 0 // Show hierarchical blocks (big blue ones.)
  6413. Int i, j;
  6414. ICoord2D extent;
  6415. m_zoneManager.getExtent(extent);
  6416. for (i=0; i<extent.x; i++) {
  6417. for (j=0; j<extent.y; j++) {
  6418. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  6419. RGBColor color;
  6420. color.blue = 1;
  6421. color.red = color.green = 0;
  6422. Coord3D pos;
  6423. pos.x = ((i+0.5f)*PathfindZoneManager::ZONE_BLOCK_SIZE)*PATHFIND_CELL_SIZE_F ;
  6424. pos.y = ((j+0.5f)*PathfindZoneManager::ZONE_BLOCK_SIZE)*PATHFIND_CELL_SIZE_F ;
  6425. pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y ) + 0.5f;
  6426. if (m_zoneManager.isPassable(i*PathfindZoneManager::ZONE_BLOCK_SIZE, j*PathfindZoneManager::ZONE_BLOCK_SIZE)) {
  6427. addIcon(&pos, 5*PATHFIND_CELL_SIZE_F, 300, color);
  6428. }
  6429. }
  6430. }
  6431. #endif
  6432. }
  6433. #endif
  6434. if (goalCell->hasInfo() && !goalCell->getClosed() && !goalCell->getOpen()) {
  6435. goalCell->releaseInfo();
  6436. }
  6437. parentCell->releaseInfo();
  6438. cleanOpenAndClosedLists();
  6439. return path;
  6440. }
  6441. #if defined _DEBUG || defined _INTERNAL
  6442. #if 0
  6443. Bool show = TheGlobalData->m_debugAI==AI_DEBUG_PATHS;
  6444. show |= (TheGlobalData->m_debugAI==AI_DEBUG_GROUND_PATHS);
  6445. if (show) {
  6446. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  6447. RGBColor color;
  6448. color.blue = 1;
  6449. color.red = 1;
  6450. color.green = 0;
  6451. Coord3D pos;
  6452. pos.x = ((blockX+0.5f)*PathfindZoneManager::ZONE_BLOCK_SIZE)*PATHFIND_CELL_SIZE_F ;
  6453. pos.y = ((blockY+0.5f)*PathfindZoneManager::ZONE_BLOCK_SIZE)*PATHFIND_CELL_SIZE_F ;
  6454. pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y ) + 0.5f;
  6455. addIcon(&pos, 5*PATHFIND_CELL_SIZE_F, 300, color);
  6456. }
  6457. #endif
  6458. #endif
  6459. Real dx = IABS(goalCell->getXIndex()-parentCell->getXIndex());
  6460. Real dy = IABS(goalCell->getYIndex()-parentCell->getYIndex());
  6461. Real distSqr = dx*dx+dy*dy;
  6462. if (distSqr < closestDistSqr) {
  6463. closestCell = parentCell;
  6464. closestDistSqr = distSqr;
  6465. }
  6466. // put parent cell onto closed list - its evaluation is finished
  6467. m_closedList = parentCell->putOnClosedList( m_closedList );
  6468. Int i;
  6469. UnsignedShort examinedZones[PathfindZoneManager::ZONE_BLOCK_SIZE];
  6470. Int numExZones = 0;
  6471. // Left side.
  6472. if (blockX>0) {
  6473. for (i=1; i<=PathfindZoneManager::ZONE_BLOCK_SIZE; i++) {
  6474. ICoord2D scanCell;
  6475. scanCell.x = blockX*PathfindZoneManager::ZONE_BLOCK_SIZE;
  6476. scanCell.y = (blockY*PathfindZoneManager::ZONE_BLOCK_SIZE);
  6477. scanCell.y += PathfindZoneManager::ZONE_BLOCK_SIZE/2;
  6478. Int offset = i>>1;
  6479. if (i&1) offset = -offset;
  6480. scanCell.y += offset;
  6481. ICoord2D delta;
  6482. delta.x = -1; // left side moves -1.
  6483. delta.y = 0;
  6484. PathfindCell *cell = getCell(LAYER_GROUND, scanCell.x, scanCell.y);
  6485. if (cell==NULL) continue;
  6486. if (cell->hasInfo() && (cell->getClosed() || cell->getOpen())) {
  6487. if (parentZone == m_zoneManager.getBlockZone(locomotorSurface,
  6488. crusher, scanCell.x, scanCell.y, m_map)) {
  6489. break;
  6490. }
  6491. }
  6492. if (isHuman) {
  6493. if (scanCell.x < m_logicalExtent.lo.x || scanCell.x > m_logicalExtent.hi.x ||
  6494. scanCell.y < m_logicalExtent.lo.y || scanCell.y > m_logicalExtent.hi.y) {
  6495. continue;
  6496. }
  6497. }
  6498. processHierarchicalCell(scanCell, delta, parentCell,
  6499. goalCell, parentZone, examinedZones, numExZones, crusher, cellCount);
  6500. }
  6501. }
  6502. // Right side.
  6503. if (blockX<zoneBlockExtent.x-1) {
  6504. numExZones = 0;
  6505. for (i=1; i<=PathfindZoneManager::ZONE_BLOCK_SIZE; i++) {
  6506. ICoord2D scanCell;
  6507. scanCell.x = blockX*PathfindZoneManager::ZONE_BLOCK_SIZE;
  6508. scanCell.x += PathfindZoneManager::ZONE_BLOCK_SIZE-1;
  6509. scanCell.y = (blockY*PathfindZoneManager::ZONE_BLOCK_SIZE);
  6510. scanCell.y += PathfindZoneManager::ZONE_BLOCK_SIZE/2;
  6511. Int offset = i>>1;
  6512. if (i&1) offset = -offset;
  6513. scanCell.y += offset;
  6514. ICoord2D delta;
  6515. delta.x = 1; // right side moves +1.
  6516. delta.y = 0;
  6517. PathfindCell *cell = getCell(LAYER_GROUND, scanCell.x, scanCell.y);
  6518. if (cell==NULL) continue;
  6519. if (cell->hasInfo() && (cell->getClosed() || cell->getOpen())) {
  6520. if (parentZone == m_zoneManager.getBlockZone(locomotorSurface,
  6521. crusher, scanCell.x, scanCell.y, m_map)) {
  6522. break;
  6523. }
  6524. }
  6525. if (isHuman) {
  6526. if (scanCell.x < m_logicalExtent.lo.x || scanCell.x > m_logicalExtent.hi.x ||
  6527. scanCell.y < m_logicalExtent.lo.y || scanCell.y > m_logicalExtent.hi.y) {
  6528. continue;
  6529. }
  6530. }
  6531. processHierarchicalCell(scanCell, delta, parentCell,
  6532. goalCell, parentZone, examinedZones, numExZones, crusher, cellCount);
  6533. }
  6534. }
  6535. // Top side.
  6536. if (blockY>0) {
  6537. numExZones = 0;
  6538. for (i=1; i<=PathfindZoneManager::ZONE_BLOCK_SIZE; i++) {
  6539. ICoord2D scanCell;
  6540. scanCell.y = blockY*PathfindZoneManager::ZONE_BLOCK_SIZE;
  6541. scanCell.x = (blockX*PathfindZoneManager::ZONE_BLOCK_SIZE);
  6542. scanCell.x += PathfindZoneManager::ZONE_BLOCK_SIZE/2;
  6543. Int offset = i>>1;
  6544. if (i&1) offset = -offset;
  6545. scanCell.x += offset;
  6546. ICoord2D delta;
  6547. delta.x = 0;
  6548. delta.y = -1; // Top side moves -1.
  6549. PathfindCell *cell = getCell(LAYER_GROUND, scanCell.x, scanCell.y);
  6550. if (cell==NULL) continue;
  6551. if (cell->hasInfo() && (cell->getClosed() || cell->getOpen())) {
  6552. if (parentZone == m_zoneManager.getBlockZone(locomotorSurface,
  6553. crusher, scanCell.x, scanCell.y, m_map)) {
  6554. break;
  6555. }
  6556. }
  6557. if (isHuman) {
  6558. if (scanCell.x < m_logicalExtent.lo.x || scanCell.x > m_logicalExtent.hi.x ||
  6559. scanCell.y < m_logicalExtent.lo.y || scanCell.y > m_logicalExtent.hi.y) {
  6560. continue;
  6561. }
  6562. }
  6563. processHierarchicalCell(scanCell, delta, parentCell,
  6564. goalCell, parentZone, examinedZones, numExZones, crusher, cellCount);
  6565. }
  6566. }
  6567. // Bottom side.
  6568. if (blockY<zoneBlockExtent.y-1) {
  6569. numExZones = 0;
  6570. for (i=1; i<=PathfindZoneManager::ZONE_BLOCK_SIZE; i++) {
  6571. ICoord2D scanCell;
  6572. scanCell.y = blockY*PathfindZoneManager::ZONE_BLOCK_SIZE;
  6573. scanCell.y += PathfindZoneManager::ZONE_BLOCK_SIZE-1;
  6574. scanCell.x = (blockX*PathfindZoneManager::ZONE_BLOCK_SIZE);
  6575. scanCell.x += PathfindZoneManager::ZONE_BLOCK_SIZE/2;
  6576. Int offset = i>>1;
  6577. if (i&1) offset = -offset;
  6578. scanCell.x += offset;
  6579. ICoord2D delta;
  6580. delta.x = 0;
  6581. delta.y = 1; // Top side moves +1.
  6582. PathfindCell *cell = getCell(LAYER_GROUND, scanCell.x, scanCell.y);
  6583. if (cell==NULL) continue;
  6584. if (cell->hasInfo() && (cell->getClosed() || cell->getOpen())) {
  6585. if (parentZone == m_zoneManager.getBlockZone(locomotorSurface,
  6586. crusher, scanCell.x, scanCell.y, m_map)) {
  6587. break;
  6588. }
  6589. }
  6590. if (isHuman) {
  6591. if (scanCell.x < m_logicalExtent.lo.x || scanCell.x > m_logicalExtent.hi.x ||
  6592. scanCell.y < m_logicalExtent.lo.y || scanCell.y > m_logicalExtent.hi.y) {
  6593. continue;
  6594. }
  6595. }
  6596. processHierarchicalCell(scanCell, delta, parentCell,
  6597. goalCell, parentZone, examinedZones, numExZones, crusher, cellCount);
  6598. }
  6599. }
  6600. }
  6601. if (closestOK && closestCell) {
  6602. m_isTunneling = false;
  6603. // construct and return path
  6604. Path *path = buildHierachicalPath( from, closestCell );
  6605. #if defined _DEBUG || defined _INTERNAL
  6606. #if 0
  6607. if (TheGlobalData->m_debugAI)
  6608. {
  6609. debugShowSearch(true);
  6610. Int i, j;
  6611. ICoord2D extent;
  6612. m_zoneManager.getExtent(extent);
  6613. for (i=0; i<extent.x; i++) {
  6614. for (j=0; j<extent.y; j++) {
  6615. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  6616. RGBColor color;
  6617. color.blue = 1;
  6618. color.red = color.green = 0;
  6619. Coord3D pos;
  6620. pos.x = ((i+0.5f)*PathfindZoneManager::ZONE_BLOCK_SIZE)*PATHFIND_CELL_SIZE_F ;
  6621. pos.y = ((j+0.5f)*PathfindZoneManager::ZONE_BLOCK_SIZE)*PATHFIND_CELL_SIZE_F ;
  6622. pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y ) + 0.5f;
  6623. if (m_zoneManager.isPassable(i*PathfindZoneManager::ZONE_BLOCK_SIZE, j*PathfindZoneManager::ZONE_BLOCK_SIZE)) {
  6624. addIcon(&pos, 5*PATHFIND_CELL_SIZE_F, 300, color);
  6625. }
  6626. }
  6627. }
  6628. }
  6629. #endif
  6630. #endif
  6631. if (goalCell->hasInfo() && !goalCell->getClosed() && !goalCell->getOpen()) {
  6632. goalCell->releaseInfo();
  6633. }
  6634. cleanOpenAndClosedLists();
  6635. return path;
  6636. }
  6637. // failure - goal cannot be reached
  6638. #if defined _DEBUG || defined _INTERNAL
  6639. if (TheGlobalData->m_debugAI)
  6640. {
  6641. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  6642. RGBColor color;
  6643. color.blue = 0;
  6644. color.red = color.green = 1;
  6645. addIcon(NULL, 0, 0, color);
  6646. debugShowSearch(false);
  6647. Coord3D pos;
  6648. pos = *from;
  6649. pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y ) + 0.5f;
  6650. addIcon(&pos, 3*PATHFIND_CELL_SIZE_F, 600, color);
  6651. pos = *to;
  6652. pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y ) + 0.5f;
  6653. addIcon(&pos, 3*PATHFIND_CELL_SIZE_F, 600, color);
  6654. Real dx, dy;
  6655. dx = from->x - to->x;
  6656. dy = from->y - to->y;
  6657. Int count = sqrt(dx*dx+dy*dy)/(PATHFIND_CELL_SIZE_F/2);
  6658. if (count<2) count = 2;
  6659. Int i;
  6660. color.green = 0;
  6661. for (i=1; i<count; i++) {
  6662. pos.x = from->x + (to->x-from->x)*i/count;
  6663. pos.y = from->y + (to->y-from->y)*i/count;
  6664. pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y ) + 0.5f;
  6665. addIcon(&pos, PATHFIND_CELL_SIZE_F/2, 60, color);
  6666. }
  6667. }
  6668. DEBUG_LOG(("%d ", TheGameLogic->getFrame()));
  6669. DEBUG_LOG(("FindHierarchicalPath failed from (%f,%f) to (%f,%f)\n", from->x, from->y, to->x, to->y));
  6670. DEBUG_LOG(("time %f\n", (::GetTickCount()-startTimeMS)/1000.0f));
  6671. #endif
  6672. #ifdef DUMP_PERF_STATS
  6673. TheGameLogic->incrementOverallFailedPathfinds();
  6674. #endif
  6675. m_isTunneling = false;
  6676. goalCell->releaseInfo();
  6677. cleanOpenAndClosedLists();
  6678. return NULL;
  6679. }
  6680. /**
  6681. * Does any broken bridge join from and to?
  6682. * True means that if bridge BridgeID is repaired, there is a land path from to to..
  6683. */
  6684. Bool Pathfinder::findBrokenBridge(const LocomotorSet& locoSet,
  6685. const Coord3D *from, const Coord3D *to, ObjectID *bridgeID)
  6686. {
  6687. // See if terrain or building is blocking the destination.
  6688. PathfindLayerEnum destinationLayer = TheTerrainLogic->getLayerForDestination(to);
  6689. PathfindLayerEnum fromLayer = TheTerrainLogic->getLayerForDestination(from);
  6690. Int zone1, zone2;
  6691. *bridgeID = INVALID_ID;
  6692. PathfindCell *parentCell = getClippedCell(fromLayer, from);
  6693. PathfindCell *goalCell = getClippedCell(destinationLayer, to);
  6694. zone1 = m_zoneManager.getEffectiveZone(locoSet.getValidSurfaces(), false, parentCell->getZone());
  6695. zone2 = m_zoneManager.getEffectiveZone(locoSet.getValidSurfaces(), false, goalCell->getZone());
  6696. zone1 = m_zoneManager.getEffectiveTerrainZone(zone1);
  6697. zone2 = m_zoneManager.getEffectiveTerrainZone(zone2);
  6698. zone1 = m_zoneManager.getEffectiveZone(locoSet.getValidSurfaces(), false, zone1);
  6699. zone2 = m_zoneManager.getEffectiveZone(locoSet.getValidSurfaces(), false, zone2);
  6700. // If the terrain is connected using this locomotor set, we can path somehow.
  6701. if (zone1 == zone2) {
  6702. // There is not terrain blocking the from & to.
  6703. return false;
  6704. }
  6705. // Check broken bridges.
  6706. Int i;
  6707. for (i=0; i<=LAYER_LAST; i++) {
  6708. if (m_layers[i].isDestroyed()) {
  6709. if (m_layers[i].connectsZones(&m_zoneManager, locoSet, zone1, zone2)) {
  6710. *bridgeID = m_layers[i].getBridgeID();
  6711. return true;
  6712. }
  6713. }
  6714. }
  6715. return false;
  6716. }
  6717. /**
  6718. * Does any path exist from 'from' to 'to' given the locomotor set
  6719. * This is the quick check, only looks at whether the terrain is possible or
  6720. * impossible to path over. Doesn't take other units into account.
  6721. * False means it is impossible to path.
  6722. * True means it is possible given the terrain, but there may be units in the way.
  6723. */
  6724. Bool Pathfinder::quickDoesPathExist( const LocomotorSet& locomotorSet,
  6725. const Coord3D *from,
  6726. const Coord3D *to )
  6727. {
  6728. // See if terrain or building is blocking the destination.
  6729. PathfindLayerEnum destinationLayer = TheTerrainLogic->getLayerForDestination(to);
  6730. PathfindLayerEnum fromLayer = TheTerrainLogic->getLayerForDestination(from);
  6731. Int zone1, zone2;
  6732. PathfindCell *parentCell = getClippedCell(fromLayer, from);
  6733. PathfindCell *goalCell = getClippedCell(destinationLayer, to);
  6734. if (goalCell->getType()==PathfindCell::CELL_CLIFF) {
  6735. return false; // No goals on cliffs.
  6736. }
  6737. Bool doingTerrainZone = false;
  6738. zone1 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), false, parentCell->getZone());
  6739. if (parentCell->getType() == PathfindCell::CELL_OBSTACLE) {
  6740. doingTerrainZone = true;
  6741. }
  6742. zone2 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), false, goalCell->getZone());
  6743. if (goalCell->getType() == PathfindCell::CELL_OBSTACLE) {
  6744. doingTerrainZone = true;
  6745. }
  6746. if (doingTerrainZone) {
  6747. zone1 = parentCell->getZone();
  6748. zone1 = m_zoneManager.getEffectiveTerrainZone(zone1);
  6749. zone1 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), false, zone1);
  6750. zone1 = m_zoneManager.getEffectiveTerrainZone(zone1);
  6751. zone2 = goalCell->getZone();
  6752. zone2 = m_zoneManager.getEffectiveTerrainZone(zone2);
  6753. zone2 = m_zoneManager.getEffectiveZone(locomotorSet.getValidSurfaces(), false, zone2);
  6754. zone2 = m_zoneManager.getEffectiveTerrainZone(zone2);
  6755. }
  6756. if (!validMovementPosition(false, destinationLayer, locomotorSet, to)) {
  6757. return false;
  6758. }
  6759. // If the terrain is connected using this locomotor set, we can path somehow.
  6760. if (zone1 == zone2) {
  6761. // There is not terrain blocking the from & to.
  6762. return true;
  6763. }
  6764. return FALSE; // no path exists
  6765. }
  6766. /**
  6767. * Does any path exist from 'from' to 'to' given the locomotor set
  6768. * This is the careful check, looks at whether the terrain, buindings and units are possible or
  6769. * impossible to path over. Takes other units into account.
  6770. * False means it is impossible to path.
  6771. * True means it is possible to path.
  6772. */
  6773. Bool Pathfinder::slowDoesPathExist( Object *obj,
  6774. const Coord3D *from,
  6775. const Coord3D *to,
  6776. ObjectID ignoreObject)
  6777. {
  6778. AIUpdateInterface *ai = obj->getAI();
  6779. if (ai==NULL) {
  6780. return false;
  6781. }
  6782. const LocomotorSet &locoSet = ai->getLocomotorSet();
  6783. m_ignoreObstacleID = ignoreObject;
  6784. Path *path = findPath(obj, locoSet, from, to);
  6785. m_ignoreObstacleID = INVALID_ID;
  6786. Bool found = (path!=NULL);
  6787. if (path) {
  6788. path->deleteInstance();
  6789. path = NULL;
  6790. }
  6791. return found;
  6792. }
  6793. void Pathfinder::clip( Coord3D *from, Coord3D *to )
  6794. {
  6795. ICoord2D fromCell, toCell;
  6796. ICoord2D clipFromCell, clipToCell;
  6797. fromCell.x = REAL_TO_INT_FLOOR(from->x/PATHFIND_CELL_SIZE);
  6798. fromCell.y = REAL_TO_INT_FLOOR(from->y/PATHFIND_CELL_SIZE);
  6799. toCell.x = REAL_TO_INT_FLOOR(to->x/PATHFIND_CELL_SIZE);
  6800. toCell.y = REAL_TO_INT_FLOOR(to->y/PATHFIND_CELL_SIZE);
  6801. if (ClipLine2D(&fromCell, &toCell, &clipFromCell, &clipToCell,&m_extent)) {
  6802. if (fromCell.x!=clipFromCell.x || fromCell.y != clipFromCell.y) {
  6803. from->x = clipFromCell.x*PATHFIND_CELL_SIZE_F + 0.05f;
  6804. from->y = clipFromCell.y*PATHFIND_CELL_SIZE_F + 0.05f;
  6805. }
  6806. if (toCell.x!=clipToCell.x || toCell.y != clipToCell.y) {
  6807. to->x = clipToCell.x*PATHFIND_CELL_SIZE_F + 0.05f;
  6808. to->y = clipToCell.y*PATHFIND_CELL_SIZE_F + 0.05f;
  6809. }
  6810. }
  6811. }
  6812. Bool Pathfinder::pathDestination( Object *obj, const LocomotorSet& locomotorSet, Coord3D *dest,
  6813. PathfindLayerEnum layer, const Coord3D *groupDest)
  6814. {
  6815. //CRCDEBUG_LOG(("Pathfinder::pathDestination()\n"));
  6816. if (m_isMapReady == false) return NULL;
  6817. if (!obj) return false;
  6818. Int cellCount = 0;
  6819. #define MAX_CELL_COUNT 500
  6820. Coord3D adjustTo = *groupDest;
  6821. Coord3D *to = &adjustTo;
  6822. DEBUG_ASSERTCRASH(m_openList==NULL && m_closedList == NULL, ("Dangling lists."));
  6823. // create unique "mark" values for open and closed cells for this pathfind invocation
  6824. Bool isCrusher = obj ? obj->getCrusherLevel() > 0 : false;
  6825. PathfindLayerEnum desiredLayer = TheTerrainLogic->getLayerForDestination(dest);
  6826. // determine desired
  6827. PathfindCell *desiredCell = getClippedCell( desiredLayer, dest );
  6828. if (desiredCell == NULL)
  6829. return FALSE;
  6830. PathfindLayerEnum goalLayer = TheTerrainLogic->getLayerForDestination(to);
  6831. // determine goal cell
  6832. PathfindCell *goalCell = getClippedCell( goalLayer, to );
  6833. if (goalCell == NULL)
  6834. return FALSE;
  6835. Bool isHuman = true;
  6836. if (obj && obj->getControllingPlayer() && (obj->getControllingPlayer()->getPlayerType()==PLAYER_COMPUTER)) {
  6837. isHuman = false; // computer gets to cheat.
  6838. }
  6839. Bool center;
  6840. Int radius;
  6841. getRadiusAndCenter(obj, radius, center);
  6842. // determine start cell
  6843. ICoord2D startCellNdx;
  6844. worldToCell(dest, &startCellNdx);
  6845. PathfindCell *parentCell = getCell( layer, startCellNdx.x, startCellNdx.y );
  6846. if (parentCell == NULL)
  6847. return FALSE;
  6848. ICoord2D pos2d;
  6849. worldToCell(to, &pos2d);
  6850. if (!goalCell->allocateInfo(pos2d)) {
  6851. return FALSE;
  6852. }
  6853. if (parentCell!=goalCell) {
  6854. if (!parentCell->allocateInfo(startCellNdx)) {
  6855. desiredCell->releaseInfo();
  6856. goalCell->releaseInfo();
  6857. return FALSE;
  6858. }
  6859. }
  6860. PathfindCell *closestCell = NULL;
  6861. Real closestDistanceSqr = FLT_MAX;
  6862. Coord3D closestPos;
  6863. if (validMovementPosition( isCrusher, locomotorSet.getValidSurfaces(), parentCell ) == false) {
  6864. parentCell->releaseInfo();
  6865. goalCell->releaseInfo();
  6866. return FALSE;
  6867. }
  6868. parentCell->startPathfind(goalCell);
  6869. // initialize "open" list to contain start cell
  6870. m_openList = parentCell;
  6871. // "closed" list is initially empty
  6872. m_closedList = NULL;
  6873. //
  6874. // Continue search until "open" list is empty, or
  6875. // until goal is found.
  6876. //
  6877. while( m_openList != NULL )
  6878. {
  6879. // take head cell off of open list - it has lowest estimated total path cost
  6880. parentCell = m_openList;
  6881. m_openList = parentCell->removeFromOpenList(m_openList);
  6882. Coord3D pos;
  6883. // put parent cell onto closed list - its evaluation is finished
  6884. m_closedList = parentCell->putOnClosedList( m_closedList );
  6885. if (checkForAdjust(obj, locomotorSet, isHuman, parentCell->getXIndex(), parentCell->getYIndex(), parentCell->getLayer(),
  6886. radius, center, &pos, groupDest)) {
  6887. Int dx = IABS(goalCell->getXIndex()-parentCell->getXIndex());
  6888. Int dy = IABS(goalCell->getYIndex()-parentCell->getYIndex());
  6889. Real distSqr = dx*dx+dy*dy;
  6890. //Real cost = (parentCell->getCostSoFar()*(parentCell->getCostSoFar()*COST_TO_DISTANCE_FACTOR))*0.5f;
  6891. //distSqr += sqr(cost);
  6892. if (distSqr < closestDistanceSqr) {
  6893. closestCell = parentCell;
  6894. closestDistanceSqr = distSqr;
  6895. closestPos = pos;
  6896. } else {
  6897. continue;
  6898. }
  6899. } else {
  6900. continue;
  6901. }
  6902. if (cellCount > MAX_CELL_COUNT) {
  6903. continue;
  6904. }
  6905. // Check to see if we can change layers in this cell.
  6906. checkChangeLayers(parentCell);
  6907. // expand search to neighboring orthogonal cells
  6908. static ICoord2D delta[] =
  6909. {
  6910. { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 },
  6911. { 1, 1 }, { -1, 1 }, { -1, -1 }, { 1, -1 }
  6912. };
  6913. const Int numNeighbors = 8;
  6914. const Int firstDiagonal = 4;
  6915. ICoord2D newCellCoord;
  6916. PathfindCell *newCell;
  6917. const Int adjacent[5] = {0, 1, 2, 3, 0};
  6918. Bool neighborFlags[8] = {false, false, false, false, false, false, false};
  6919. UnsignedInt newCostSoFar;
  6920. for( int i=0; i<numNeighbors; i++ )
  6921. {
  6922. neighborFlags[i] = false;
  6923. // determine neighbor cell to try
  6924. newCellCoord.x = parentCell->getXIndex() + delta[i].x;
  6925. newCellCoord.y = parentCell->getYIndex() + delta[i].y;
  6926. // get the neighboring cell
  6927. newCell = getCell(parentCell->getLayer(), newCellCoord.x, newCellCoord.y );
  6928. // check if cell is on the map
  6929. if (newCell == NULL)
  6930. continue;
  6931. // check if this neighbor cell is already on the open (waiting to be tried)
  6932. // or closed (already tried) lists
  6933. Bool onList = false;
  6934. if (newCell->hasInfo()) {
  6935. if (newCell->getOpen() || newCell->getClosed())
  6936. {
  6937. // already on one of the lists
  6938. onList = true;
  6939. }
  6940. }
  6941. if (i>=firstDiagonal) {
  6942. // make sure one of the adjacent sides is open.
  6943. if (!neighborFlags[adjacent[i-4]] && !neighborFlags[adjacent[i-3]]) {
  6944. continue;
  6945. }
  6946. }
  6947. if (!validMovementPosition( isCrusher, locomotorSet.getValidSurfaces(), newCell, parentCell )) {
  6948. continue;
  6949. }
  6950. neighborFlags[i] = true;
  6951. if (!newCell->allocateInfo(newCellCoord)) {
  6952. // Out of cells for pathing...
  6953. return cellCount;
  6954. }
  6955. cellCount++;
  6956. newCostSoFar = newCell->costSoFar( parentCell );
  6957. newCell->setBlockedByAlly(false);
  6958. // check if this neighbor cell is already on the open (waiting to be tried)
  6959. // or closed (already tried) lists
  6960. if (onList)
  6961. {
  6962. // already on one of the lists - if existing costSoFar is less,
  6963. // the new cell is on a longer path, so skip it
  6964. if (newCell->getCostSoFar() <= newCostSoFar)
  6965. continue;
  6966. }
  6967. // keep track of path we're building - point back to cell we moved here from
  6968. newCell->setParentCell(parentCell) ;
  6969. // store cost of this path
  6970. newCell->setCostSoFar(newCostSoFar);
  6971. Int costRemaining = 0;
  6972. if (goalCell) {
  6973. costRemaining = newCell->costToGoal( goalCell );
  6974. }
  6975. newCell->setTotalCost(newCell->getCostSoFar() + costRemaining) ;
  6976. //DEBUG_LOG(("Cell (%d,%d), Parent cost %d, newCostSoFar %d, cost rem %d, tot %d\n",
  6977. // newCell->getXIndex(), newCell->getYIndex(),
  6978. // newCell->costSoFar(parentCell), newCostSoFar, costRemaining, newCell->getCostSoFar() + costRemaining));
  6979. // if newCell was on closed list, remove it from the list
  6980. if (newCell->getClosed())
  6981. m_closedList = newCell->removeFromClosedList( m_closedList );
  6982. // if the newCell was already on the open list, remove it so it can be re-inserted in order
  6983. if (newCell->getOpen())
  6984. m_openList = newCell->removeFromOpenList( m_openList );
  6985. // insert newCell in open list such that open list is sorted, smallest total path cost first
  6986. m_openList = newCell->putOnSortedOpenList( m_openList );
  6987. }
  6988. }
  6989. #if defined _DEBUG || defined _INTERNAL
  6990. if (closestCell) {
  6991. debugShowSearch(true);
  6992. *dest = closestPos;
  6993. } else {
  6994. debugShowSearch(true);
  6995. }
  6996. #endif
  6997. m_isTunneling = false;
  6998. cleanOpenAndClosedLists();
  6999. goalCell->releaseInfo();
  7000. return false;
  7001. }
  7002. struct TightenPathStruct
  7003. {
  7004. Object *obj;
  7005. const LocomotorSet *locomotorSet;
  7006. PathfindLayerEnum layer;
  7007. Int radius;
  7008. Bool center;
  7009. Bool foundDest;
  7010. Coord3D destPos;
  7011. };
  7012. /*static*/ Int Pathfinder::tightenPathCallback(Pathfinder* pathfinder, PathfindCell* from, PathfindCell* to, Int to_x, Int to_y, void* userData)
  7013. {
  7014. TightenPathStruct* d = (TightenPathStruct*)userData;
  7015. if (from == NULL || to==NULL) return 0;
  7016. if (d->layer != to->getLayer()) {
  7017. return 0; // abort.
  7018. }
  7019. Coord3D pos;
  7020. if (!TheAI->pathfinder()->checkForAdjust(d->obj, *d->locomotorSet, true, to_x, to_y, to->getLayer(), d->radius, d->center, &pos, NULL))
  7021. {
  7022. return 0; // bail early
  7023. }
  7024. d->foundDest = true;
  7025. d->destPos = pos;
  7026. return 0; // keep going
  7027. }
  7028. /* Returns the cost, which is in the same units as coord3d distance. */
  7029. void Pathfinder::tightenPath(Object *obj, const LocomotorSet& locomotorSet, Coord3D *from,
  7030. const Coord3D *to)
  7031. {
  7032. TightenPathStruct info;
  7033. getRadiusAndCenter(obj, info.radius, info.center);
  7034. info.layer = TheTerrainLogic->getLayerForDestination(from);
  7035. info.obj = obj;
  7036. info.locomotorSet = &locomotorSet;
  7037. info.foundDest = false;
  7038. iterateCellsAlongLine(*from, *to, info.layer, tightenPathCallback, &info);
  7039. if (info.foundDest) {
  7040. *from = info.destPos;
  7041. }
  7042. }
  7043. /* Returns the cost, which is in the same units as coord3d distance. */
  7044. Int Pathfinder::checkPathCost(Object *obj, const LocomotorSet& locomotorSet, const Coord3D *from,
  7045. const Coord3D *rawTo)
  7046. {
  7047. //CRCDEBUG_LOG(("Pathfinder::checkPathCost()\n"));
  7048. if (m_isMapReady == false) return NULL;
  7049. enum {MAX_COST = 0x7fff0000};
  7050. if (!obj) return MAX_COST;
  7051. Int cellCount = 0;
  7052. #define MAX_CELL_COUNT 500
  7053. Coord3D adjustTo = *rawTo;
  7054. Coord3D *to = &adjustTo;
  7055. DEBUG_ASSERTCRASH(m_openList==NULL && m_closedList == NULL, ("Dangling lists."));
  7056. // create unique "mark" values for open and closed cells for this pathfind invocation
  7057. Bool isCrusher = obj ? obj->getCrusherLevel() > 0 : false;
  7058. PathfindLayerEnum goalLayer = TheTerrainLogic->getLayerForDestination(to);
  7059. // determine goal cell
  7060. PathfindCell *goalCell = getClippedCell( goalLayer, to );
  7061. if (goalCell == NULL)
  7062. return MAX_COST;
  7063. Bool center;
  7064. Int radius;
  7065. getRadiusAndCenter(obj, radius, center);
  7066. // determine start cell
  7067. ICoord2D startCellNdx;
  7068. worldToCell(from, &startCellNdx);
  7069. PathfindLayerEnum fromLayer = TheTerrainLogic->getLayerForDestination(from);
  7070. PathfindCell *parentCell = getCell( fromLayer, from );
  7071. if (parentCell == NULL)
  7072. return MAX_COST;
  7073. ICoord2D pos2d;
  7074. worldToCell(to, &pos2d);
  7075. if (!goalCell->allocateInfo(pos2d)) {
  7076. return MAX_COST;
  7077. }
  7078. if (parentCell!=goalCell) {
  7079. if (!parentCell->allocateInfo(startCellNdx)) {
  7080. goalCell->releaseInfo();
  7081. return MAX_COST;
  7082. }
  7083. }
  7084. if (validMovementPosition( isCrusher, locomotorSet.getValidSurfaces(), parentCell ) == false) {
  7085. parentCell->releaseInfo();
  7086. goalCell->releaseInfo();
  7087. return MAX_COST;
  7088. }
  7089. parentCell->startPathfind(goalCell);
  7090. // initialize "open" list to contain start cell
  7091. m_openList = parentCell;
  7092. // "closed" list is initially empty
  7093. m_closedList = NULL;
  7094. //
  7095. // Continue search until "open" list is empty, or
  7096. // until goal is found.
  7097. //
  7098. while( m_openList != NULL )
  7099. {
  7100. // take head cell off of open list - it has lowest estimated total path cost
  7101. parentCell = m_openList;
  7102. m_openList = parentCell->removeFromOpenList(m_openList);
  7103. // put parent cell onto closed list - its evaluation is finished
  7104. m_closedList = parentCell->putOnClosedList( m_closedList );
  7105. if (parentCell==goalCell) {
  7106. Int cost = parentCell->getTotalCost();
  7107. m_isTunneling = false;
  7108. cleanOpenAndClosedLists();
  7109. return cost;
  7110. }
  7111. if (cellCount > MAX_CELL_COUNT) {
  7112. continue;
  7113. }
  7114. // Check to see if we can change layers in this cell.
  7115. checkChangeLayers(parentCell);
  7116. // expand search to neighboring orthogonal cells
  7117. static ICoord2D delta[] =
  7118. {
  7119. { 1, 0 }, { 0, 1 }, { -1, 0 }, { 0, -1 },
  7120. { 1, 1 }, { -1, 1 }, { -1, -1 }, { 1, -1 }
  7121. };
  7122. const Int numNeighbors = 8;
  7123. const Int firstDiagonal = 4;
  7124. ICoord2D newCellCoord;
  7125. PathfindCell *newCell;
  7126. const Int adjacent[5] = {0, 1, 2, 3, 0};
  7127. Bool neighborFlags[8] = {false, false, false, false, false, false, false};
  7128. UnsignedInt newCostSoFar;
  7129. for( int i=0; i<numNeighbors; i++ )
  7130. {
  7131. neighborFlags[i] = false;
  7132. // determine neighbor cell to try
  7133. newCellCoord.x = parentCell->getXIndex() + delta[i].x;
  7134. newCellCoord.y = parentCell->getYIndex() + delta[i].y;
  7135. // get the neighboring cell
  7136. newCell = getCell(parentCell->getLayer(), newCellCoord.x, newCellCoord.y );
  7137. // check if cell is on the map
  7138. if (newCell == NULL)
  7139. continue;
  7140. // check if this neighbor cell is already on the open (waiting to be tried)
  7141. // or closed (already tried) lists
  7142. Bool onList = false;
  7143. if (newCell->hasInfo()) {
  7144. if (newCell->getOpen() || newCell->getClosed())
  7145. {
  7146. // already on one of the lists
  7147. onList = true;
  7148. }
  7149. }
  7150. if (i>=firstDiagonal) {
  7151. // make sure one of the adjacent sides is open.
  7152. if (!neighborFlags[adjacent[i-4]] && !neighborFlags[adjacent[i-3]]) {
  7153. continue;
  7154. }
  7155. }
  7156. if (!validMovementPosition( isCrusher, locomotorSet.getValidSurfaces(), newCell, parentCell )) {
  7157. continue;
  7158. }
  7159. neighborFlags[i] = true;
  7160. if (!newCell->allocateInfo(newCellCoord)) {
  7161. // Out of cells for pathing...
  7162. return cellCount;
  7163. }
  7164. cellCount++;
  7165. newCostSoFar = newCell->costSoFar( parentCell );
  7166. newCell->setBlockedByAlly(false);
  7167. // check if this neighbor cell is already on the open (waiting to be tried)
  7168. // or closed (already tried) lists
  7169. if (onList)
  7170. {
  7171. // already on one of the lists - if existing costSoFar is less,
  7172. // the new cell is on a longer path, so skip it
  7173. if (newCell->getCostSoFar() <= newCostSoFar)
  7174. continue;
  7175. }
  7176. // keep track of path we're building - point back to cell we moved here from
  7177. newCell->setParentCell(parentCell) ;
  7178. // store cost of this path
  7179. newCell->setCostSoFar(newCostSoFar);
  7180. Int costRemaining = 0;
  7181. if (goalCell) {
  7182. costRemaining = newCell->costToGoal( goalCell );
  7183. }
  7184. newCell->setTotalCost(newCell->getCostSoFar() + costRemaining) ;
  7185. //DEBUG_LOG(("Cell (%d,%d), Parent cost %d, newCostSoFar %d, cost rem %d, tot %d\n",
  7186. // newCell->getXIndex(), newCell->getYIndex(),
  7187. // newCell->costSoFar(parentCell), newCostSoFar, costRemaining, newCell->getCostSoFar() + costRemaining));
  7188. // if newCell was on closed list, remove it from the list
  7189. if (newCell->getClosed())
  7190. m_closedList = newCell->removeFromClosedList( m_closedList );
  7191. // if the newCell was already on the open list, remove it so it can be re-inserted in order
  7192. if (newCell->getOpen())
  7193. m_openList = newCell->removeFromOpenList( m_openList );
  7194. // insert newCell in open list such that open list is sorted, smallest total path cost first
  7195. m_openList = newCell->putOnSortedOpenList( m_openList );
  7196. }
  7197. }
  7198. m_isTunneling = false;
  7199. if (goalCell->hasInfo() && !goalCell->getClosed() && !goalCell->getOpen()) {
  7200. goalCell->releaseInfo();
  7201. }
  7202. cleanOpenAndClosedLists();
  7203. return MAX_COST;
  7204. }
  7205. /**
  7206. * Find a short, valid path between the FROM location and a location NEAR the to location.
  7207. * Uses A* algorithm.
  7208. */
  7209. Path *Pathfinder::findClosestPath( Object *obj, const LocomotorSet& locomotorSet, const Coord3D *from,
  7210. Coord3D *rawTo, Bool blocked, Real pathCostMultiplier, Bool moveAllies)
  7211. {
  7212. //CRCDEBUG_LOG(("Pathfinder::findClosestPath()\n"));
  7213. #if defined _DEBUG || defined _INTERNAL
  7214. Int startTimeMS = ::GetTickCount();
  7215. #endif
  7216. Bool isHuman = true;
  7217. if (obj && obj->getControllingPlayer() && (obj->getControllingPlayer()->getPlayerType()==PLAYER_COMPUTER)) {
  7218. isHuman = false; // computer gets to cheat.
  7219. }
  7220. if (locomotorSet.getValidSurfaces() == 0) {
  7221. DEBUG_CRASH(("Attempting to path immobile unit."));
  7222. return NULL;
  7223. }
  7224. if (m_isMapReady == false) return NULL;
  7225. m_isTunneling = false;
  7226. if (!obj) return NULL;
  7227. Bool canPathThroughUnits = false;
  7228. if (obj && obj->getAIUpdateInterface()) {
  7229. canPathThroughUnits = obj->getAIUpdateInterface()->canPathThroughUnits();
  7230. }
  7231. Bool centerInCell;
  7232. Int radius;
  7233. getRadiusAndCenter(obj, radius, centerInCell);
  7234. Coord3D adjustTo = *rawTo;
  7235. Coord3D *to = &adjustTo;
  7236. if (!centerInCell) {
  7237. adjustTo.x += PATHFIND_CELL_SIZE_F/2;
  7238. adjustTo.y += PATHFIND_CELL_SIZE_F/2;
  7239. }
  7240. DEBUG_ASSERTCRASH(m_openList==NULL && m_closedList == NULL, ("Dangling lists."));
  7241. // create unique "mark" values for open and closed cells for this pathfind invocation
  7242. Bool isCrusher = obj ? obj->getCrusherLevel() > 0 : false;
  7243. Coord3D clipFrom = *from;
  7244. clip(&clipFrom, &adjustTo);
  7245. PathfindLayerEnum destinationLayer = TheTerrainLogic->getLayerForDestination(to);
  7246. // determine goal cell
  7247. PathfindCell *goalCell = getClippedCell( destinationLayer, to );
  7248. if (goalCell == NULL)
  7249. return NULL;
  7250. if (goalCell->getZone()==0 && destinationLayer==LAYER_WALL) {
  7251. return NULL;
  7252. }
  7253. Bool goalOnObstacle = false;
  7254. if (m_ignoreObstacleID != INVALID_ID) {
  7255. // Check for object on structure.
  7256. // srj sez: check for obstacle on AIRFIELD... only want to do this for things
  7257. // that are "parked" on the airfield, but not for things hovering over an obstacle
  7258. // (eg, a chinook over a supply dock).
  7259. Object *goalObj = TheGameLogic->findObjectByID(m_ignoreObstacleID);
  7260. if (goalObj) {
  7261. PathfindCell *ignoreCell = getClippedCell(goalObj->getLayer(), goalObj->getPosition());
  7262. if ( (goalCell->getObstacleID()==ignoreCell->getObstacleID()) && (goalCell->getObstacleID() != INVALID_ID) ) {
  7263. Object* newObstacle = TheGameLogic->findObjectByID(goalCell->getObstacleID());
  7264. if (newObstacle != NULL && newObstacle->isKindOf(KINDOF_AIRFIELD))
  7265. {
  7266. m_ignoreObstacleID = goalCell->getObstacleID();
  7267. goalOnObstacle = true;
  7268. }
  7269. else
  7270. {
  7271. if (m_ignoreObstacleID == goalCell->getObstacleID()) {
  7272. goalOnObstacle = true;
  7273. }
  7274. }
  7275. }
  7276. }
  7277. }
  7278. // determine start cell
  7279. ICoord2D startCellNdx;
  7280. worldToCell(from, &startCellNdx);
  7281. PathfindCell *parentCell = getClippedCell( obj->getLayer(), &clipFrom );
  7282. if (parentCell == NULL)
  7283. return NULL;
  7284. if (validMovementPosition( isCrusher, locomotorSet.getValidSurfaces(), parentCell ) == false) {
  7285. m_isTunneling = true; // We can't move from our current location. So relax the constraints.
  7286. }
  7287. TCheckMovementInfo info;
  7288. info.cell = startCellNdx;
  7289. info.layer = obj->getLayer();
  7290. info.centerInCell = centerInCell;
  7291. info.radius = radius;
  7292. info.considerTransient = blocked;
  7293. info.acceptableSurfaces = locomotorSet.getValidSurfaces();
  7294. if (!checkForMovement(obj, info) || info.enemyFixed) {
  7295. m_isTunneling = true; // We can't move from our current location. So relax the constraints.
  7296. }
  7297. Bool gotHierarchicalPath = false;
  7298. if (m_isTunneling) {
  7299. m_zoneManager.setAllPassable(); // can't optimize.
  7300. } else {
  7301. m_zoneManager.clearPassableFlags();
  7302. Path *hPat = findClosestHierarchicalPath(isHuman, locomotorSet, from, rawTo, false);
  7303. if (hPat) {
  7304. hPat->deleteInstance();
  7305. gotHierarchicalPath = true;
  7306. } else {
  7307. m_zoneManager.setAllPassable();
  7308. }
  7309. }
  7310. const Bool startedStuck = m_isTunneling;
  7311. ICoord2D pos2d;
  7312. worldToCell(to, &pos2d);
  7313. if (!goalCell->allocateInfo(pos2d)) {
  7314. return NULL;
  7315. }
  7316. if (parentCell!=goalCell) {
  7317. worldToCell(&clipFrom, &pos2d);
  7318. if (!parentCell->allocateInfo(pos2d)) {
  7319. return NULL;
  7320. }
  7321. }
  7322. parentCell->startPathfind(goalCell);
  7323. PathfindCell *closesetCell = NULL;
  7324. Real closestDistanceSqr = FLT_MAX;
  7325. Real closestDistScreenSqr = FLT_MAX;
  7326. // initialize "open" list to contain start cell
  7327. m_openList = parentCell;
  7328. // "closed" list is initially empty
  7329. m_closedList = NULL;
  7330. Int count = 0;
  7331. //
  7332. // Continue search until "open" list is empty, or
  7333. // until goal is found.
  7334. //
  7335. while( m_openList != NULL )
  7336. {
  7337. Real dx;
  7338. Real dy;
  7339. Real distSqr;
  7340. // take head cell off of open list - it has lowest estimated total path cost
  7341. parentCell = m_openList;
  7342. m_openList = parentCell->removeFromOpenList(m_openList);
  7343. if (parentCell == goalCell)
  7344. {
  7345. // success - found a path to the goal
  7346. if (!goalOnObstacle) {
  7347. // See if the goal is a valid destination. If not, accept closest cell.
  7348. if (closesetCell!=NULL && !canPathThroughUnits && !checkDestination(obj, parentCell->getXIndex(), parentCell->getYIndex(), parentCell->getLayer(), radius, centerInCell)) {
  7349. break;
  7350. }
  7351. }
  7352. Bool show = TheGlobalData->m_debugAI;
  7353. #ifdef INTENSE_DEBUG
  7354. Int count = 0;
  7355. PathfindCell *cur;
  7356. for (cur = m_closedList; cur; cur=cur->getNextOpen()) {
  7357. count++;
  7358. }
  7359. if (count>1000) {
  7360. show = true;
  7361. DEBUG_LOG(("FCP - cells %d obj %s %x\n", count, obj->getTemplate()->getName().str(), obj));
  7362. #ifdef STATE_MACHINE_DEBUG
  7363. if( obj->getAIUpdateInterface() )
  7364. {
  7365. DEBUG_LOG(("State %s\n", obj->getAIUpdateInterface()->getCurrentStateName().str()));
  7366. }
  7367. #endif
  7368. TheScriptEngine->AppendDebugMessage("Big path FCP", false);
  7369. }
  7370. #endif
  7371. if (show)
  7372. debugShowSearch(true);
  7373. m_isTunneling = false;
  7374. // construct and return path
  7375. Path *path = buildActualPath( obj, locomotorSet.getValidSurfaces(), from, goalCell, centerInCell, blocked);
  7376. parentCell->releaseInfo();
  7377. goalCell->releaseInfo();
  7378. cleanOpenAndClosedLists();
  7379. return path;
  7380. }
  7381. // put parent cell onto closed list - its evaluation is finished
  7382. m_closedList = parentCell->putOnClosedList( m_closedList );
  7383. if (!m_isTunneling && checkDestination(obj, parentCell->getXIndex(), parentCell->getYIndex(), parentCell->getLayer(), radius, centerInCell)) {
  7384. if (!startedStuck || validMovementPosition( isCrusher, locomotorSet.getValidSurfaces(), parentCell )) {
  7385. dx = IABS(goalCell->getXIndex()-parentCell->getXIndex());
  7386. dy = IABS(goalCell->getYIndex()-parentCell->getYIndex());
  7387. distSqr = dx*dx+dy*dy;
  7388. if (distSqr<closestDistScreenSqr) {
  7389. closestDistScreenSqr = distSqr;
  7390. }
  7391. distSqr += (parentCell->getCostSoFar()*(parentCell->getCostSoFar()*COST_TO_DISTANCE_FACTOR_SQR))*pathCostMultiplier;
  7392. if (distSqr < closestDistanceSqr) {
  7393. closesetCell = parentCell;
  7394. closestDistanceSqr = distSqr;
  7395. }
  7396. }
  7397. }
  7398. dx = IABS(goalCell->getXIndex()-parentCell->getXIndex());
  7399. dy = IABS(goalCell->getYIndex()-parentCell->getYIndex());
  7400. distSqr = dx*dx+dy*dy;
  7401. // If we are 2x farther than the closest location already found, don't continue.
  7402. if (distSqr > closestDistScreenSqr*4) {
  7403. Bool skip = false;
  7404. if (!gotHierarchicalPath) {
  7405. skip = true;
  7406. }
  7407. if (count>2000) {
  7408. skip = true;
  7409. }
  7410. if (closestDistScreenSqr < 10*10*PATHFIND_CELL_SIZE_F) {
  7411. skip = true;
  7412. }
  7413. if (skip) {
  7414. continue;
  7415. }
  7416. }
  7417. // Check to see if we can change layers in this cell.
  7418. checkChangeLayers(parentCell);
  7419. count += examineNeighboringCells(parentCell, goalCell, locomotorSet, isHuman, centerInCell, radius, startCellNdx, obj, NO_ATTACK);
  7420. }
  7421. if (closesetCell) {
  7422. // success - found a path to near the goal
  7423. Bool show = TheGlobalData->m_debugAI;
  7424. #ifdef INTENSE_DEBUG
  7425. if (count>5000) {
  7426. show = true;
  7427. DEBUG_LOG(("FCP CC cells %d obj %s %x\n", count, obj->getTemplate()->getName().str(), obj));
  7428. #ifdef STATE_MACHINE_DEBUG
  7429. if( obj->getAIUpdateInterface() )
  7430. {
  7431. DEBUG_LOG(("State %s\n", obj->getAIUpdateInterface()->getCurrentStateName().str()));
  7432. }
  7433. #endif
  7434. DEBUG_LOG(("%d ", TheGameLogic->getFrame()));
  7435. DEBUG_LOG(("Pathfind(findClosestPath) chugged from (%f,%f) to (%f,%f), --", from->x, from->y, to->x, to->y));
  7436. DEBUG_LOG(("Unit '%s', time %f\n", obj->getTemplate()->getName().str(), (::GetTickCount()-startTimeMS)/1000.0f));
  7437. #ifdef INTENSE_DEBUG
  7438. TheScriptEngine->AppendDebugMessage("Big path FCP CC", false);
  7439. #endif
  7440. }
  7441. #endif
  7442. if (show)
  7443. debugShowSearch(true);
  7444. m_isTunneling = false;
  7445. rawTo->x = closesetCell->getXIndex()*PATHFIND_CELL_SIZE_F + PATHFIND_CELL_SIZE_F/2.0f;
  7446. rawTo->y = closesetCell->getYIndex()*PATHFIND_CELL_SIZE_F + PATHFIND_CELL_SIZE_F/2.0f;
  7447. // construct and return path
  7448. Path *path = buildActualPath( obj, locomotorSet.getValidSurfaces(), from, closesetCell, centerInCell, blocked );
  7449. goalCell->releaseInfo();
  7450. cleanOpenAndClosedLists();
  7451. return path;
  7452. }
  7453. // failure - goal cannot be reached
  7454. #if defined _DEBUG || defined _INTERNAL
  7455. Bool valid;
  7456. valid = validMovementPosition( isCrusher, obj->getLayer(), locomotorSet, to ) ;
  7457. DEBUG_LOG(("%d ", TheGameLogic->getFrame()));
  7458. DEBUG_LOG(("Pathfind(findClosestPath) failed from (%f,%f) to (%f,%f), original valid %d --", from->x, from->y, to->x, to->y, valid));
  7459. DEBUG_LOG(("Unit '%s', time %f\n", obj->getTemplate()->getName().str(), (::GetTickCount()-startTimeMS)/1000.0f));
  7460. if (TheGlobalData->m_debugAI)
  7461. debugShowSearch(false);
  7462. #endif
  7463. #ifdef DUMP_PERF_STATS
  7464. TheGameLogic->incrementOverallFailedPathfinds();
  7465. #endif
  7466. m_isTunneling = false;
  7467. goalCell->releaseInfo();
  7468. cleanOpenAndClosedLists();
  7469. return NULL;
  7470. }
  7471. void Pathfinder::adjustCoordToCell(Int cellX, Int cellY, Bool centerInCell, Coord3D &pos, PathfindLayerEnum layer)
  7472. {
  7473. if (centerInCell) {
  7474. pos.x = ((Real)cellX + 0.5f) * PATHFIND_CELL_SIZE_F;
  7475. pos.y = ((Real)cellY + 0.5f) * PATHFIND_CELL_SIZE_F;
  7476. } else {
  7477. pos.x = ((Real)cellX+0.05) * PATHFIND_CELL_SIZE_F;
  7478. pos.y = ((Real)cellY+0.05) * PATHFIND_CELL_SIZE_F;
  7479. }
  7480. pos.z = TheTerrainLogic->getLayerHeight( pos.x, pos.y, layer );
  7481. }
  7482. /**
  7483. * Work backwards from goal cell to construct final path.
  7484. */
  7485. Path *Pathfinder::buildActualPath( const Object *obj, LocomotorSurfaceTypeMask acceptableSurfaces, const Coord3D *fromPos,
  7486. PathfindCell *goalCell, Bool center, Bool blocked )
  7487. {
  7488. DEBUG_ASSERTCRASH( goalCell, ("Pathfinder::buildActualPath: goalCell == NULL") );
  7489. Path *path = newInstance(Path);
  7490. if (goalCell->getPinched() && goalCell->getParentCell() && !goalCell->getParentCell()->getPinched()) {
  7491. goalCell = goalCell->getParentCell();
  7492. }
  7493. prependCells(path, fromPos, goalCell, center);
  7494. // cleanup the path by checking line of sight
  7495. path->optimize(obj, acceptableSurfaces, blocked);
  7496. #if defined _DEBUG || defined _INTERNAL
  7497. if (TheGlobalData->m_debugAI==AI_DEBUG_PATHS)
  7498. {
  7499. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  7500. RGBColor color;
  7501. color.blue = 0;
  7502. color.red = color.green = 1;
  7503. Coord3D pos;
  7504. for( PathNode *node = path->getFirstNode(); node; node = node->getNext() )
  7505. {
  7506. // create objects to show path - they decay
  7507. pos = *node->getPosition();
  7508. color.red = color.green = 1;
  7509. if (node->getLayer() != LAYER_GROUND) {
  7510. color.red = 0;
  7511. }
  7512. addIcon(&pos, PATHFIND_CELL_SIZE_F*.25f, 200, color);
  7513. }
  7514. // show optimized path
  7515. for( node = path->getFirstNode(); node; node = node->getNextOptimized() )
  7516. {
  7517. pos = *node->getPosition();
  7518. addIcon(&pos, PATHFIND_CELL_SIZE_F*.8f, 200, color);
  7519. }
  7520. setDebugPath(path);
  7521. }
  7522. #endif
  7523. return path;
  7524. }
  7525. /**
  7526. * Work backwards from goal cell to construct final path.
  7527. */
  7528. void Pathfinder::prependCells( Path *path, const Coord3D *fromPos,
  7529. PathfindCell *goalCell, Bool center )
  7530. {
  7531. // traverse path cells in REVERSE order, creating path in desired order
  7532. // skip the LAST node, as that will be in the same cell as the unit itself - so use the unit's position
  7533. Coord3D pos;
  7534. PathfindCell *cell, *prevCell = NULL;
  7535. Bool goalCellNull = (goalCell->getParentCell()==NULL);
  7536. for( cell = goalCell; cell->getParentCell(); cell = cell->getParentCell() )
  7537. {
  7538. m_zoneManager.setPassable(cell->getXIndex(), cell->getYIndex(), true);
  7539. adjustCoordToCell(cell->getXIndex(), cell->getYIndex(), center, pos, cell->getLayer());
  7540. if (prevCell && cell->getXIndex()==prevCell->getXIndex() && cell->getYIndex()==prevCell->getYIndex()) {
  7541. // transitioning layers.
  7542. PathfindLayerEnum layer = cell->getLayer();
  7543. if (layer==LAYER_GROUND) {
  7544. layer = prevCell->getLayer();
  7545. }
  7546. DEBUG_ASSERTCRASH(layer!=LAYER_GROUND, ("Should have at 1 non-ground layer. jba"));
  7547. path->getFirstNode()->setLayer(layer);
  7548. continue;
  7549. }
  7550. Bool canOptimize = true;
  7551. if (cell->getType() == PathfindCell::CELL_CLIFF) {
  7552. if (prevCell && prevCell->getType() != PathfindCell::CELL_CLIFF) {
  7553. if (path->getFirstNode()) {
  7554. path->getFirstNode()->setCanOptimize(false);
  7555. }
  7556. }
  7557. } else {
  7558. if (prevCell && prevCell->getType() == PathfindCell::CELL_CLIFF) {
  7559. canOptimize = false;
  7560. }
  7561. }
  7562. path->prependNode( &pos, cell->getLayer() );
  7563. path->getFirstNode()->setCanOptimize(canOptimize);
  7564. if (cell->isBlockedByAlly()) {
  7565. path->setBlockedByAlly(true);
  7566. }
  7567. if (prevCell) {
  7568. prevCell->clearParentCell();
  7569. }
  7570. prevCell = cell;
  7571. }
  7572. m_zoneManager.setPassable(cell->getXIndex(), cell->getYIndex(), true);
  7573. if (goalCellNull) {
  7574. // Very short path.
  7575. adjustCoordToCell(cell->getXIndex(), cell->getYIndex(), center, pos, cell->getLayer());
  7576. path->prependNode( &pos, cell->getLayer() );
  7577. }
  7578. // put actual start position as first node on the path, so it begins right at the unit's feet
  7579. if (fromPos->x != path->getFirstNode()->getPosition()->x || fromPos->y != path->getFirstNode()->getPosition()->y) {
  7580. path->prependNode( fromPos, cell->getLayer() );
  7581. }
  7582. }
  7583. void Pathfinder::setDebugPath(Path *newDebugpath)
  7584. {
  7585. if (TheGlobalData->m_debugAI)
  7586. {
  7587. // copy the path for debugging
  7588. if (debugPath)
  7589. debugPath->deleteInstance();
  7590. debugPath = newInstance(Path);
  7591. for( PathNode *copyNode = newDebugpath->getFirstNode(); copyNode; copyNode = copyNode->getNextOptimized() )
  7592. debugPath->appendNode( copyNode->getPosition(), copyNode->getLayer() );
  7593. }
  7594. }
  7595. /**
  7596. * Given two world-space points, call callback for each cell.
  7597. * Uses Bresenham line algorithm from www.gamedev.net.
  7598. */
  7599. Int Pathfinder::iterateCellsAlongLine( const Coord3D& startWorld, const Coord3D& endWorld,
  7600. PathfindLayerEnum layer, CellAlongLineProc proc, void* userData )
  7601. {
  7602. ICoord2D start, end;
  7603. worldToCell( &startWorld, &start );
  7604. worldToCell( &endWorld, &end );
  7605. return iterateCellsAlongLine(start, end, layer, proc, userData);
  7606. }
  7607. /**
  7608. * Given two world-space points, call callback for each cell.
  7609. * Uses Bresenham line algorithm from www.gamedev.net.
  7610. */
  7611. Int Pathfinder::iterateCellsAlongLine( const ICoord2D &start, const ICoord2D &end,
  7612. PathfindLayerEnum layer, CellAlongLineProc proc, void* userData )
  7613. {
  7614. Int delta_x = abs(end.x - start.x); // The difference between the x's
  7615. Int delta_y = abs(end.y - start.y); // The difference between the y's
  7616. Int x = start.x; // Start x off at the first pixel
  7617. Int y = start.y; // Start y off at the first pixel
  7618. Int xinc1, xinc2;
  7619. if (end.x >= start.x) // The x-values are increasing
  7620. {
  7621. xinc1 = 1;
  7622. xinc2 = 1;
  7623. }
  7624. else // The x-values are decreasing
  7625. {
  7626. xinc1 = -1;
  7627. xinc2 = -1;
  7628. }
  7629. Int yinc1, yinc2;
  7630. if (end.y >= start.y) // The y-values are increasing
  7631. {
  7632. yinc1 = 1;
  7633. yinc2 = 1;
  7634. }
  7635. else // The y-values are decreasing
  7636. {
  7637. yinc1 = -1;
  7638. yinc2 = -1;
  7639. }
  7640. Bool checkY = true;
  7641. Int den, num, numadd, numpixels;
  7642. if (delta_x >= delta_y) // There is at least one x-value for every y-value
  7643. {
  7644. xinc1 = 0; // Don't change the x when numerator >= denominator
  7645. yinc2 = 0; // Don't change the y for every iteration
  7646. den = delta_x;
  7647. num = delta_x / 2;
  7648. numadd = delta_y;
  7649. numpixels = delta_x; // There are more x-values than y-values
  7650. }
  7651. else // There is at least one y-value for every x-value
  7652. {
  7653. checkY = false;
  7654. xinc2 = 0; // Don't change the x for every iteration
  7655. yinc1 = 0; // Don't change the y when numerator >= denominator
  7656. den = delta_y;
  7657. num = delta_y / 2;
  7658. numadd = delta_x;
  7659. numpixels = delta_y; // There are more y-values than x-values
  7660. }
  7661. PathfindCell* from = NULL;
  7662. for (Int curpixel = 0; curpixel <= numpixels; curpixel++)
  7663. {
  7664. PathfindCell* to = getCell( layer, x, y );
  7665. if (to==NULL) return 0;
  7666. Int ret = (*proc)(this, from, to, x, y, userData);
  7667. if (ret != 0)
  7668. return ret;
  7669. num += numadd; // Increase the numerator by the top of the fraction
  7670. if (num >= den) // Check if numerator >= denominator
  7671. {
  7672. num -= den; // Calculate the new numerator value
  7673. x += xinc1; // Change the x as appropriate
  7674. y += yinc1; // Change the y as appropriate
  7675. from = to;
  7676. to = getCell( layer, x, y );
  7677. if (to==NULL) return 0;
  7678. Int ret = (*proc)(this, from, to, x, y, userData);
  7679. if (ret != 0)
  7680. return ret;
  7681. }
  7682. x += xinc2; // Change the x as appropriate
  7683. y += yinc2; // Change the y as appropriate
  7684. from = to;
  7685. }
  7686. return 0;
  7687. }
  7688. //-----------------------------------------------------------------------------
  7689. static ObjectID getSlaverID(const Object* o)
  7690. {
  7691. for (BehaviorModule** update = o->getBehaviorModules(); *update; ++update)
  7692. {
  7693. SlavedUpdateInterface* sdu = (*update)->getSlavedUpdateInterface();
  7694. if (sdu != NULL)
  7695. {
  7696. return sdu->getSlaverID();
  7697. }
  7698. }
  7699. return INVALID_ID;
  7700. }
  7701. static ObjectID getContainerID(const Object* o)
  7702. {
  7703. const Object* container = o ? o->getContainedBy() : NULL;
  7704. return container ? container->getID() : INVALID_ID;
  7705. }
  7706. struct segmentIntersectsStruct
  7707. {
  7708. Object *theTallBuilding;
  7709. ObjectID ignoreBuilding;
  7710. };
  7711. /*static*/ Int Pathfinder::segmentIntersectsBuildingCallback(Pathfinder* pathfinder, PathfindCell* from, PathfindCell* to, Int to_x, Int to_y, void* userData)
  7712. {
  7713. segmentIntersectsStruct* d = (segmentIntersectsStruct*)userData;
  7714. if (to != NULL && (to->getType() == PathfindCell::CELL_OBSTACLE))
  7715. {
  7716. Object *obj = TheGameLogic->findObjectByID(to->getObstacleID());
  7717. if (obj && obj->isKindOf(KINDOF_AIRCRAFT_PATH_AROUND)) {
  7718. if (obj->getID() == d->ignoreBuilding) {
  7719. return 0;
  7720. }
  7721. d->theTallBuilding = obj;
  7722. return 1;
  7723. }
  7724. }
  7725. return 0; // keep going
  7726. }
  7727. struct ViewBlockedStruct
  7728. {
  7729. const Object *obj;
  7730. const Object *objOther;
  7731. };
  7732. /*static*/ Int Pathfinder::lineBlockedByObstacleCallback(Pathfinder* pathfinder, PathfindCell* from, PathfindCell* to, Int to_x, Int to_y, void* userData)
  7733. {
  7734. const ViewBlockedStruct* d = (const ViewBlockedStruct*)userData;
  7735. if (to != NULL && (to->getType() == PathfindCell::CELL_OBSTACLE))
  7736. {
  7737. // we never block our own view!
  7738. if (to->isObstaclePresent(d->obj->getID()))
  7739. return 0;
  7740. // nor does the object we're trying to see!
  7741. if (to->isObstaclePresent(d->objOther->getID()))
  7742. return 0;
  7743. // if the obstacle is our container, ignore it as an obstacle.
  7744. if (to->isObstaclePresent(getContainerID(d->obj)))
  7745. return 0;
  7746. // @todo: if the obstacle is objOther's container, AND it's a "visible" container, ignore it.
  7747. // if the obstacle is the item to which we are slaved, ignore it as an obstacle.
  7748. if (to->isObstaclePresent(getSlaverID(d->obj)))
  7749. return 0;
  7750. // if the obstacle is the item to which objOther is slaved, ignore it as an obstacle.
  7751. if (to->isObstaclePresent(getSlaverID(d->objOther)))
  7752. return 0;
  7753. // if the obstacle is transparent, ignore it, since this callback is only used for line-of-sight. (srj)
  7754. if (to->isObstacleTransparent())
  7755. return 0;
  7756. return 1; // bail early
  7757. }
  7758. return 0; // keep going
  7759. }
  7760. struct ViewAttackBlockedStruct
  7761. {
  7762. const Object *obj;
  7763. const Object *victim;
  7764. const PathfindCell *victimCell;
  7765. Int skipCount;
  7766. };
  7767. /*static*/ Int Pathfinder::attackBlockedByObstacleCallback(Pathfinder* pathfinder, PathfindCell* from, PathfindCell* to, Int to_x, Int to_y, void* userData)
  7768. {
  7769. ViewAttackBlockedStruct* d = (ViewAttackBlockedStruct*)userData;
  7770. if (d->skipCount>0) {
  7771. d->skipCount--;
  7772. return 0;
  7773. }
  7774. if (to != NULL && (to->getType() == PathfindCell::CELL_OBSTACLE))
  7775. {
  7776. // we never block our own view!
  7777. if (to->isObstaclePresent(d->obj->getID()))
  7778. return 0;
  7779. if (d->victim) {
  7780. // nor does the object we're trying to attack!
  7781. if (to->isObstaclePresent(d->victim->getID()))
  7782. return 0;
  7783. // if the obstacle is the item to which objOther is slaved, ignore it as an obstacle.
  7784. if (to->isObstaclePresent(getSlaverID(d->victim)))
  7785. return 0;
  7786. }
  7787. // if the obstacle is our container, ignore it as an obstacle.
  7788. if (to->isObstaclePresent(getContainerID(d->obj)))
  7789. return 0;
  7790. // @todo: if the obstacle is objOther's container, AND it's a "visible" container, ignore it.
  7791. // if the obstacle is the item to which we are slaved, ignore it as an obstacle.
  7792. if (to->isObstaclePresent(getSlaverID(d->obj)))
  7793. return 0;
  7794. if (to->isObstacleTransparent())
  7795. return 0;
  7796. //Kris: Added the check for victimCell because in China01 -- after the intro, NW of your
  7797. //base is a cream colored building that lies in a negative coord. When you order units to
  7798. //force attack it, it crashes.
  7799. if( d->victimCell && to->isObstaclePresent( d->victimCell->getObstacleID() ) )
  7800. {
  7801. // Victim is inside the bounds of another object. We don't let this block us,
  7802. // as usually it is on the edge and it looks like we should be able to shoot it. jba.
  7803. return 0;
  7804. }
  7805. return 1; // bail early
  7806. }
  7807. return 0; // keep going
  7808. }
  7809. //-----------------------------------------------------------------------------
  7810. Bool Pathfinder::isViewBlockedByObstacle(const Object* obj, const Object* objOther)
  7811. {
  7812. ViewBlockedStruct info;
  7813. info.obj = obj;
  7814. info.objOther = objOther;
  7815. if (objOther && objOther->isSignificantlyAboveTerrain()) {
  7816. return false; // We don't check los to flying objects. jba.
  7817. }
  7818. #if 1
  7819. return isAttackViewBlockedByObstacle(obj, *obj->getPosition(), objOther, *objOther->getPosition());
  7820. #else
  7821. PathfindLayerEnum layer = objOther->getLayer();
  7822. if (layer==LAYER_GROUND) {
  7823. layer = obj->getLayer();
  7824. }
  7825. Int ret = iterateCellsAlongLine(*obj->getPosition(), *objOther->getPosition(),
  7826. layer, lineBlockedByObstacleCallback, &info);
  7827. return ret != 0;
  7828. #endif
  7829. }
  7830. //-----------------------------------------------------------------------------
  7831. Bool Pathfinder::isAttackViewBlockedByObstacle(const Object* attacker, const Coord3D& attackerPos, const Object* victim, const Coord3D& victimPos)
  7832. {
  7833. //CRCDEBUG_LOG(("Pathfinder::isAttackViewBlockedByObstacle() - attackerPos is (%g,%g,%g) (%X,%X,%X)\n",
  7834. // attackerPos.x, attackerPos.y, attackerPos.z,
  7835. // AS_INT(attackerPos.x),AS_INT(attackerPos.y),AS_INT(attackerPos.z)));
  7836. //CRCDEBUG_LOG(("Pathfinder::isAttackViewBlockedByObstacle() - victimPos is (%g,%g,%g) (%X,%X,%X)\n",
  7837. // victimPos.x, victimPos.y, victimPos.z,
  7838. // AS_INT(victimPos.x),AS_INT(victimPos.y),AS_INT(victimPos.z)));
  7839. // Global switch to turn this off in case it doesn't work.
  7840. if (!TheAI->getAiData()->m_attackUsesLineOfSight)
  7841. {
  7842. //CRCDEBUG_LOG(("Pathfinder::isAttackViewBlockedByObstacle() 1\n"));
  7843. return false;
  7844. }
  7845. // If the attacker doesn't need line of sight, isn't blocked.
  7846. if (!attacker->isKindOf(KINDOF_ATTACK_NEEDS_LINE_OF_SIGHT))
  7847. {
  7848. //CRCDEBUG_LOG(("Pathfinder::isAttackViewBlockedByObstacle() 2\n"));
  7849. return false;
  7850. }
  7851. // srj sez: this is a good start at taking terrain into account for attacks, but findAttackPath needs to be smartened also
  7852. #define LOS_TERRAIN
  7853. #ifdef LOS_TERRAIN
  7854. const Weapon* w = attacker->getCurrentWeapon();
  7855. if (attacker->isKindOf(KINDOF_IMMOBILE)) {
  7856. // Don't take terrain blockage into account, since we can't move around it. jba.
  7857. w = NULL;
  7858. }
  7859. if (w)
  7860. {
  7861. Bool viewBlocked;
  7862. if (victim)
  7863. viewBlocked = !w->isClearGoalFiringLineOfSightTerrain(attacker, attackerPos, victim);
  7864. else
  7865. viewBlocked = !w->isClearGoalFiringLineOfSightTerrain(attacker, attackerPos, victimPos);
  7866. if (viewBlocked)
  7867. {
  7868. //CRCDEBUG_LOG(("Pathfinder::isAttackViewBlockedByObstacle() 3\n"));
  7869. return true;
  7870. }
  7871. }
  7872. #endif
  7873. ViewAttackBlockedStruct info;
  7874. info.obj = attacker;
  7875. info.victim = victim;
  7876. PathfindLayerEnum layer = LAYER_GROUND;
  7877. if (victim) {
  7878. layer = victim->getLayer();
  7879. }
  7880. info.victimCell = getCell(layer, &victimPos);
  7881. info.skipCount = 0;
  7882. if (attacker->getLayer() != LAYER_GROUND)
  7883. {
  7884. info.skipCount = 3; /// srj -- someone wanna tell me what this magic number means?
  7885. /// jba - Yes, it means that if someone is on a bridge, or rooftop, they can see
  7886. /// 3 pathfind cells out of whatever they are standing on.
  7887. /// srj -- awesome! thank you very much :-)
  7888. if (layer==LAYER_GROUND) {
  7889. layer = attacker->getLayer();
  7890. }
  7891. }
  7892. Int ret = iterateCellsAlongLine(attackerPos, victimPos, layer, attackBlockedByObstacleCallback, &info);
  7893. //CRCDEBUG_LOG(("Pathfinder::isAttackViewBlockedByObstacle() 4\n"));
  7894. return ret != 0;
  7895. }
  7896. static void computeNormalRadialOffset(const Coord3D& from, Coord3D& insert, const Coord3D& to,
  7897. Object *obj, Real radius)
  7898. {
  7899. Real crossProduct;
  7900. Real dx = to.x - from.x;
  7901. Real dy = to.y -from.y;
  7902. Coord3D objPos = *obj->getPosition();
  7903. Real objDx = objPos.x - from.x;
  7904. Real objDy = objPos.y - from.y;
  7905. crossProduct = dx*objDy - dy*objDx;
  7906. Coord3D fromToNormal;
  7907. fromToNormal.z = 0;
  7908. if (crossProduct>0) {
  7909. fromToNormal.x = dy;
  7910. fromToNormal.y = -dx;
  7911. } else {
  7912. fromToNormal.x = -dy;
  7913. fromToNormal.y = dx;
  7914. }
  7915. fromToNormal.normalize();
  7916. Real length = radius;
  7917. insert = *obj->getPosition();
  7918. insert.x += fromToNormal.x*length;
  7919. insert.y += fromToNormal.y*length;
  7920. }
  7921. //-----------------------------------------------------------------------------
  7922. Bool Pathfinder::segmentIntersectsTallBuilding(const PathNode *curNode,
  7923. PathNode *nextNode, ObjectID ignoreBuilding, Coord3D *insertPos1, Coord3D *insertPos2, Coord3D *insertPos3 )
  7924. {
  7925. segmentIntersectsStruct info;
  7926. info.theTallBuilding = NULL;
  7927. info.ignoreBuilding = ignoreBuilding;
  7928. Coord3D fromPos = *curNode->getPosition();
  7929. Coord3D toPos = *nextNode->getPosition();
  7930. Int i;
  7931. for (i=0; i<2; i++) {
  7932. Int ret = iterateCellsAlongLine(fromPos, toPos, LAYER_GROUND, segmentIntersectsBuildingCallback, &info);
  7933. if (ret!=0 && info.theTallBuilding) {
  7934. // see if toPos is inside the radius of the tall building.
  7935. Coord3D bldgPos = *info.theTallBuilding->getPosition();
  7936. Coord2D delta;
  7937. Real radius = info.theTallBuilding->getGeometryInfo().getBoundingCircleRadius() + 2*PATHFIND_CELL_SIZE_F;
  7938. delta.x = toPos.x - bldgPos.x;
  7939. delta.y = toPos.y - bldgPos.y;
  7940. if (delta.length() <= radius*0.98) {
  7941. if (delta.length() < 0.1) {
  7942. delta.x = 1;
  7943. }
  7944. delta.normalize();
  7945. delta.x *= radius;
  7946. delta.y *= radius;
  7947. toPos.x = bldgPos.x+delta.x;
  7948. toPos.y = bldgPos.y+delta.y;
  7949. nextNode->setPosition(&toPos);
  7950. continue;
  7951. }
  7952. delta.x = fromPos.x - bldgPos.x;
  7953. delta.y = fromPos.y - bldgPos.y;
  7954. if (delta.length() <= radius*0.98) {
  7955. if (delta.length() < 0.1) {
  7956. delta.x = 1;
  7957. }
  7958. delta.normalize();
  7959. delta.x *= radius;
  7960. delta.y *= radius;
  7961. fromPos.x = bldgPos.x+delta.x;
  7962. fromPos.y = bldgPos.y+delta.y;
  7963. }
  7964. computeNormalRadialOffset(fromPos, *insertPos2, toPos, info.theTallBuilding, radius);
  7965. computeNormalRadialOffset(fromPos, *insertPos1, *insertPos2, info.theTallBuilding, radius);
  7966. computeNormalRadialOffset(*insertPos2, *insertPos3, toPos, info.theTallBuilding, radius);
  7967. return true;
  7968. }
  7969. }
  7970. return false;
  7971. }
  7972. //-----------------------------------------------------------------------------
  7973. Bool Pathfinder::circleClipsTallBuilding( const Coord3D *from, const Coord3D *to, Real circleRadius, ObjectID ignoreBuilding, Coord3D *adjustTo)
  7974. {
  7975. PartitionFilterAcceptByKindOf filterKindof(MAKE_KINDOF_MASK(KINDOF_AIRCRAFT_PATH_AROUND), KINDOFMASK_NONE);
  7976. PartitionFilter *filters[] = { &filterKindof, NULL };
  7977. Object* tallBuilding = ThePartitionManager->getClosestObject(to, circleRadius, FROM_BOUNDINGSPHERE_2D, filters);
  7978. if (tallBuilding) {
  7979. Real radius = tallBuilding->getGeometryInfo().getBoundingCircleRadius() + 2*PATHFIND_CELL_SIZE_F;
  7980. computeNormalRadialOffset(*from, *adjustTo, *to, tallBuilding, circleRadius+radius);
  7981. Object* otherTallBuilding = ThePartitionManager->getClosestObject(adjustTo, circleRadius, FROM_BOUNDINGSPHERE_2D, filters);
  7982. if (otherTallBuilding && otherTallBuilding!=tallBuilding) {
  7983. radius = otherTallBuilding->getGeometryInfo().getBoundingCircleRadius() + 2*PATHFIND_CELL_SIZE_F;
  7984. Coord3D tmpTo = *adjustTo;
  7985. computeNormalRadialOffset(*from, *adjustTo, tmpTo, otherTallBuilding, circleRadius+radius);
  7986. }
  7987. return true;
  7988. }
  7989. return false;
  7990. }
  7991. //-----------------------------------------------------------------------------
  7992. struct LinePassableStruct
  7993. {
  7994. const Object *obj;
  7995. LocomotorSurfaceTypeMask acceptableSurfaces;
  7996. Int radius;
  7997. Bool centerInCell;
  7998. Bool blocked;
  7999. Bool allowPinched;
  8000. };
  8001. /*static*/ Int Pathfinder::linePassableCallback(Pathfinder* pathfinder, PathfindCell* from, PathfindCell* to, Int to_x, Int to_y, void* userData)
  8002. {
  8003. const LinePassableStruct* d = (const LinePassableStruct*)userData;
  8004. Bool isCrusher = d->obj ? d->obj->getCrusherLevel() > 0 : false;
  8005. TCheckMovementInfo info;
  8006. info.cell.x = to_x;
  8007. info.cell.y = to_y;
  8008. info.layer = to->getLayer();
  8009. info.centerInCell = d->centerInCell;
  8010. info.radius = d->radius;
  8011. info.considerTransient = d->blocked;
  8012. info.acceptableSurfaces = d->acceptableSurfaces;
  8013. if (!pathfinder->checkForMovement(d->obj, info))
  8014. {
  8015. return 1; // bail out
  8016. }
  8017. if (info.allyFixedCount || info.enemyFixed)
  8018. {
  8019. return 1; // bail out
  8020. }
  8021. if (!d->allowPinched && to->getPinched()) {
  8022. return 1; // bail out.
  8023. }
  8024. if (from && to->getLayer() != LAYER_GROUND && from->getLayer() == to->getLayer()) {
  8025. if (to->getType() == PathfindCell::CELL_CLEAR) {
  8026. return 0;
  8027. }
  8028. }
  8029. if (pathfinder->validMovementPosition( isCrusher, d->acceptableSurfaces, to, from ) == false)
  8030. {
  8031. return 1; // bail out
  8032. }
  8033. return 0; // keep going
  8034. }
  8035. //-----------------------------------------------------------------------------
  8036. struct GroundPathPassableStruct
  8037. {
  8038. Int diameter;
  8039. Bool crusher;
  8040. };
  8041. /*static*/ Int Pathfinder::groundPathPassableCallback(Pathfinder* pathfinder, PathfindCell* from, PathfindCell* to, Int to_x, Int to_y, void* userData)
  8042. {
  8043. const GroundPathPassableStruct* d = (const GroundPathPassableStruct*)userData;
  8044. Int curDiameter = pathfinder->clearCellForDiameter(d->crusher, to_x, to_y, to->getLayer(), d->diameter);
  8045. if (curDiameter==d->diameter) return 0; // good to go.
  8046. if (from && to->getLayer() != LAYER_GROUND && from->getLayer() == to->getLayer()) {
  8047. return 0;
  8048. }
  8049. return 1; // failed.
  8050. }
  8051. //-----------------------------------------------------------------------------
  8052. /**
  8053. * Given two world-space points, check the line of sight between them for any impassible cells.
  8054. * Uses Bresenham line algorithm from www.gamedev.net.
  8055. */
  8056. Bool Pathfinder::isLinePassable( const Object *obj, LocomotorSurfaceTypeMask acceptableSurfaces,
  8057. PathfindLayerEnum layer, const Coord3D& startWorld,
  8058. const Coord3D& endWorld, Bool blocked,
  8059. Bool allowPinched)
  8060. {
  8061. LinePassableStruct info;
  8062. //CRCDEBUG_LOG(("Pathfinder::isLinePassable(): %d %d %d \n", m_ignoreObstacleID, m_isMapReady, m_isTunneling));
  8063. info.obj = obj;
  8064. info.acceptableSurfaces = acceptableSurfaces;
  8065. getRadiusAndCenter(obj, info.radius, info.centerInCell);
  8066. info.blocked = blocked;
  8067. info.allowPinched = allowPinched;
  8068. Int ret = iterateCellsAlongLine(startWorld, endWorld, layer, linePassableCallback, (void*)&info);
  8069. return ret == 0;
  8070. }
  8071. //-----------------------------------------------------------------------------
  8072. /**
  8073. * Given two world-space points, check the line of sight between them for any impassible cells.
  8074. * Uses Bresenham line algorithm from www.gamedev.net.
  8075. */
  8076. Bool Pathfinder::isGroundPathPassable( Bool isCrusher, const Coord3D& startWorld, PathfindLayerEnum startLayer,
  8077. const Coord3D& endWorld, Int pathDiameter)
  8078. {
  8079. GroundPathPassableStruct info;
  8080. info.diameter = pathDiameter;
  8081. info.crusher = isCrusher;
  8082. Int ret = iterateCellsAlongLine(startWorld, endWorld, startLayer, groundPathPassableCallback, (void*)&info);
  8083. return ret == 0;
  8084. }
  8085. /**
  8086. * Classify the cells under the bridge
  8087. * If 'repaired' is true, bridge is repaired
  8088. * If 'repaired' is false, bridge has been damaged to be impassable
  8089. */
  8090. void Pathfinder::changeBridgeState( PathfindLayerEnum layer, Bool repaired)
  8091. {
  8092. if (m_layers[layer].isUnused()) return;
  8093. if (m_layers[layer].setDestroyed(!repaired)) {
  8094. m_zoneManager.markZonesDirty();
  8095. }
  8096. }
  8097. void Pathfinder::getRadiusAndCenter(const Object *obj, Int &iRadius, Bool &center)
  8098. {
  8099. enum {MAX_RADIUS = 2};
  8100. if (!obj)
  8101. {
  8102. center = true;
  8103. iRadius = 0;
  8104. return;
  8105. }
  8106. Real diameter = 2*obj->getGeometryInfo().getBoundingCircleRadius();
  8107. if (diameter>PATHFIND_CELL_SIZE_F && diameter<2.0f*PATHFIND_CELL_SIZE_F) {
  8108. diameter = 2.0f*PATHFIND_CELL_SIZE_F;
  8109. }
  8110. iRadius = REAL_TO_INT_FLOOR(diameter/PATHFIND_CELL_SIZE_F+0.3f);
  8111. center = false;
  8112. if (iRadius==0) iRadius++;
  8113. if (iRadius&1)
  8114. {
  8115. center = true;
  8116. }
  8117. iRadius /= 2;
  8118. if (iRadius > MAX_RADIUS)
  8119. {
  8120. iRadius = MAX_RADIUS;
  8121. center = true;
  8122. }
  8123. }
  8124. /**
  8125. * Updates the goal cell for an ai unit.
  8126. */
  8127. void Pathfinder::updateGoal( Object *obj, const Coord3D *newGoalPos, PathfindLayerEnum layer)
  8128. {
  8129. if (obj->isKindOf(KINDOF_IMMOBILE)) {
  8130. // Only consider mobile.
  8131. return;
  8132. }
  8133. AIUpdateInterface *ai = obj->getAIUpdateInterface();
  8134. if (!ai) return; // only consider ai objects.
  8135. if (!ai->isDoingGroundMovement()) {
  8136. updateAircraftGoal(obj, newGoalPos);
  8137. return;
  8138. }
  8139. PathfindLayerEnum originalLayer = obj->getDestinationLayer();
  8140. //DEBUG_LOG(("Object Goal layer is %d\n", layer));
  8141. Bool layerChanged = originalLayer != layer;
  8142. Bool doGround=false;
  8143. Bool doLayer=false;
  8144. if (layer==LAYER_GROUND) {
  8145. doGround = true;
  8146. } else {
  8147. doLayer = true;
  8148. if (TheTerrainLogic->objectInteractsWithBridgeEnd(obj, layer)) {
  8149. doGround = true;
  8150. }
  8151. }
  8152. ICoord2D goalCell = *ai->getPathfindGoalCell();
  8153. Bool centerInCell;
  8154. Int radius;
  8155. ICoord2D newCell;
  8156. getRadiusAndCenter(obj, radius, centerInCell);
  8157. Int numCellsAbove = radius;
  8158. if (centerInCell) numCellsAbove++;
  8159. if (centerInCell) {
  8160. newCell.x = REAL_TO_INT_FLOOR(newGoalPos->x/PATHFIND_CELL_SIZE_F);
  8161. newCell.y = REAL_TO_INT_FLOOR(newGoalPos->y/PATHFIND_CELL_SIZE_F);
  8162. } else {
  8163. newCell.x = REAL_TO_INT_FLOOR(0.5f+newGoalPos->x/PATHFIND_CELL_SIZE_F);
  8164. newCell.y = REAL_TO_INT_FLOOR(0.5f+newGoalPos->y/PATHFIND_CELL_SIZE_F);
  8165. }
  8166. if (!layerChanged && newCell.x==goalCell.x && newCell.y == goalCell.y) {
  8167. return;
  8168. }
  8169. removeGoal(obj);
  8170. obj->setDestinationLayer(layer);
  8171. ai->setPathfindGoalCell(newCell);
  8172. Int i,j;
  8173. ICoord2D cellNdx;
  8174. Bool warn = true;
  8175. for (i=newCell.x-radius; i<newCell.x+numCellsAbove; i++) {
  8176. for (j=newCell.y-radius; j<newCell.y+numCellsAbove; j++) {
  8177. PathfindCell *cell;
  8178. if (doLayer) {
  8179. cell = getCell(layer, i, j);
  8180. if (cell) {
  8181. if (warn && cell->getGoalUnit()!=INVALID_ID && cell->getGoalUnit() != obj->getID()) {
  8182. warn = false;
  8183. // jba intense debug
  8184. //DEBUG_LOG(, ("Units got stuck close to each other. jba\n"));
  8185. }
  8186. cellNdx.x = i;
  8187. cellNdx.y = j;
  8188. cell->setGoalUnit(obj->getID(), cellNdx);
  8189. }
  8190. }
  8191. if (doGround) {
  8192. cell = getCell(LAYER_GROUND, i, j);
  8193. if (cell) {
  8194. if (warn && cell->getGoalUnit()!=INVALID_ID && cell->getGoalUnit() != obj->getID()) {
  8195. warn = false;
  8196. // jba intense debug
  8197. //DEBUG_LOG(, ("Units got stuck close to each other. jba\n"));
  8198. }
  8199. cellNdx.x = i;
  8200. cellNdx.y = j;
  8201. cell->setGoalUnit(obj->getID(), cellNdx);
  8202. }
  8203. }
  8204. }
  8205. }
  8206. }
  8207. /**
  8208. * Updates the goal cell for an ai unit.
  8209. */
  8210. void Pathfinder::updateAircraftGoal( Object *obj, const Coord3D *newGoalPos)
  8211. {
  8212. if (obj->isKindOf(KINDOF_IMMOBILE)) {
  8213. // Only consider mobile.
  8214. return;
  8215. }
  8216. removeGoal(obj);
  8217. AIUpdateInterface *ai = obj->getAIUpdateInterface();
  8218. if (!ai) return; // only consider ai objects.
  8219. if (ai->isDoingGroundMovement()) {
  8220. return; // shouldn't really happen, but just in case.
  8221. }
  8222. // For now, we are only doing HOVER, and WINGS.
  8223. if (!ai->isAircraftThatAdjustsDestination()) return;
  8224. ICoord2D goalCell = *ai->getPathfindGoalCell();
  8225. Bool centerInCell;
  8226. Int radius;
  8227. ICoord2D newCell;
  8228. getRadiusAndCenter(obj, radius, centerInCell);
  8229. Int numCellsAbove = radius;
  8230. if (centerInCell) numCellsAbove++;
  8231. if (centerInCell) {
  8232. newCell.x = REAL_TO_INT_FLOOR(newGoalPos->x/PATHFIND_CELL_SIZE_F);
  8233. newCell.y = REAL_TO_INT_FLOOR(newGoalPos->y/PATHFIND_CELL_SIZE_F);
  8234. } else {
  8235. newCell.x = REAL_TO_INT_FLOOR(0.5f+newGoalPos->x/PATHFIND_CELL_SIZE_F);
  8236. newCell.y = REAL_TO_INT_FLOOR(0.5f+newGoalPos->y/PATHFIND_CELL_SIZE_F);
  8237. }
  8238. if (newCell.x==goalCell.x && newCell.y == goalCell.y) {
  8239. return;
  8240. }
  8241. ai->setPathfindGoalCell(newCell);
  8242. Int i,j;
  8243. ICoord2D cellNdx;
  8244. for (i=newCell.x-radius; i<newCell.x+numCellsAbove; i++) {
  8245. for (j=newCell.y-radius; j<newCell.y+numCellsAbove; j++) {
  8246. PathfindCell *cell;
  8247. cell = getCell(LAYER_GROUND, i, j);
  8248. if (cell) {
  8249. cellNdx.x = i;
  8250. cellNdx.y = j;
  8251. cell->setGoalAircraft(obj->getID(), cellNdx);
  8252. }
  8253. }
  8254. }
  8255. }
  8256. /**
  8257. * Removes the goal cell for an ai unit.
  8258. * Used for a unit that is going to be moving several times, like following a waypoint path,
  8259. * or intentionally collides with other units (like a car bomb). jba
  8260. */
  8261. void Pathfinder::removeGoal( Object *obj)
  8262. {
  8263. if (obj->isKindOf(KINDOF_IMMOBILE)) {
  8264. // Only consider mobile.
  8265. return;
  8266. }
  8267. AIUpdateInterface *ai = obj->getAIUpdateInterface();
  8268. if (!ai) return; // only consider ai objects.
  8269. ICoord2D goalCell = *ai->getPathfindGoalCell();
  8270. Bool centerInCell;
  8271. Int radius;
  8272. ICoord2D newCell;
  8273. getRadiusAndCenter(obj, radius, centerInCell);
  8274. if (radius==0) {
  8275. radius++;
  8276. }
  8277. Int numCellsAbove = radius;
  8278. if (centerInCell) numCellsAbove++;
  8279. newCell.x = newCell.y = -1;
  8280. if (newCell.x==goalCell.x && newCell.y == goalCell.y) {
  8281. return;
  8282. }
  8283. ICoord2D cellNdx;
  8284. ai->setPathfindGoalCell(newCell);
  8285. Int i,j;
  8286. if (goalCell.x>=0 && goalCell.y>=0) {
  8287. for (i=goalCell.x-radius; i<goalCell.x+numCellsAbove; i++) {
  8288. for (j=goalCell.y-radius; j<goalCell.y+numCellsAbove; j++) {
  8289. PathfindCell *cell = getCell(LAYER_GROUND, i, j);
  8290. if (cell) {
  8291. if (cell->getGoalUnit()==obj->getID()) {
  8292. cellNdx.x = i;
  8293. cellNdx.y = j;
  8294. cell->setGoalUnit(INVALID_ID, cellNdx);
  8295. }
  8296. if (cell->getGoalAircraft()==obj->getID()) {
  8297. cellNdx.x = i;
  8298. cellNdx.y = j;
  8299. cell->setGoalAircraft(INVALID_ID, cellNdx);
  8300. }
  8301. }
  8302. if (obj->getDestinationLayer()!=LAYER_GROUND) {
  8303. cell = getCell( obj->getDestinationLayer(), i, j);
  8304. if (cell) {
  8305. if (cell->getGoalUnit()==obj->getID()) {
  8306. cellNdx.x = i;
  8307. cellNdx.y = j;
  8308. cell->setGoalUnit(INVALID_ID, cellNdx);
  8309. }
  8310. }
  8311. }
  8312. }
  8313. }
  8314. }
  8315. }
  8316. /**
  8317. * Updates the position cell for an ai unit.
  8318. */
  8319. void Pathfinder::updatePos( Object *obj, const Coord3D *newPos)
  8320. {
  8321. if (obj->isKindOf(KINDOF_IMMOBILE))
  8322. {
  8323. // Only consider mobile.
  8324. return;
  8325. }
  8326. if (!m_isMapReady)
  8327. return;
  8328. AIUpdateInterface *ai = obj->getAIUpdateInterface();
  8329. if (!ai)
  8330. return; // only consider ai objects.
  8331. ICoord2D curCell = *ai->getCurPathfindCell();
  8332. if (!ai->isDoingGroundMovement())
  8333. {
  8334. if (curCell.x>=0 && curCell.y>=0)
  8335. {
  8336. removePos(obj);
  8337. }
  8338. return;
  8339. }
  8340. Bool centerInCell;
  8341. Int radius;
  8342. ICoord2D newCell;
  8343. getRadiusAndCenter(obj, radius, centerInCell);
  8344. Int numCellsAbove = radius;
  8345. if (centerInCell)
  8346. numCellsAbove++;
  8347. if (centerInCell)
  8348. {
  8349. newCell.x = REAL_TO_INT_FLOOR(newPos->x/PATHFIND_CELL_SIZE_F);
  8350. newCell.y = REAL_TO_INT_FLOOR(newPos->y/PATHFIND_CELL_SIZE_F);
  8351. }
  8352. else
  8353. {
  8354. newCell.x = REAL_TO_INT_FLOOR(0.5f+newPos->x/PATHFIND_CELL_SIZE_F);
  8355. newCell.y = REAL_TO_INT_FLOOR(0.5f+newPos->y/PATHFIND_CELL_SIZE_F);
  8356. }
  8357. if (newCell.x==curCell.x && newCell.y == curCell.y)
  8358. {
  8359. return;
  8360. }
  8361. PathfindLayerEnum layer = obj->getLayer();
  8362. Bool doGround=false;
  8363. Bool doLayer=false;
  8364. if (layer==LAYER_GROUND) {
  8365. doGround = true; // just have to do ground
  8366. } else {
  8367. doLayer = true; // have to do the layer
  8368. if (TheTerrainLogic->objectInteractsWithBridgeEnd(obj, layer)) {
  8369. doGround = true; // In this case, have to both layer & ground, as they overlap here.
  8370. }
  8371. }
  8372. ai->setCurPathfindCell(newCell);
  8373. Int i,j;
  8374. ICoord2D cellNdx;
  8375. //DEBUG_LOG(("Updating unit pos at cell %d, %d\n", newCell.x, newCell.y));
  8376. if (curCell.x>=0 && curCell.y>=0) {
  8377. for (i=curCell.x-radius; i<curCell.x+numCellsAbove; i++) {
  8378. for (j=curCell.y-radius; j<curCell.y+numCellsAbove; j++) {
  8379. cellNdx.x = i;
  8380. cellNdx.y = j;
  8381. PathfindCell *cell = getCell(layer, i, j);
  8382. if (cell) {
  8383. if (cell->getPosUnit()==obj->getID()) {
  8384. cell->setPosUnit(INVALID_ID, cellNdx);
  8385. }
  8386. }
  8387. if (layer!=LAYER_GROUND) {
  8388. // Remove from the ground, if present.
  8389. cell = getCell(LAYER_GROUND, i, j);
  8390. if (cell) {
  8391. if (cell->getPosUnit()==obj->getID()) {
  8392. cell->setPosUnit(INVALID_ID, cellNdx);
  8393. }
  8394. }
  8395. }
  8396. }
  8397. }
  8398. }
  8399. for (i=newCell.x-radius; i<newCell.x+numCellsAbove; i++) {
  8400. for (j=newCell.y-radius; j<newCell.y+numCellsAbove; j++) {
  8401. PathfindCell *cell;
  8402. cellNdx.x = i;
  8403. cellNdx.y = j;
  8404. if (doLayer) {
  8405. cell = getCell(layer, i, j);
  8406. if (cell) {
  8407. cell->setPosUnit(obj->getID(), cellNdx);
  8408. }
  8409. }
  8410. if (doGround) {
  8411. cell = getCell(LAYER_GROUND, i, j);
  8412. if (cell) {
  8413. cell->setPosUnit(obj->getID(), cellNdx);
  8414. }
  8415. }
  8416. }
  8417. }
  8418. }
  8419. /**
  8420. * Removes the position cell flags for an ai unit.
  8421. */
  8422. void Pathfinder::removePos( Object *obj)
  8423. {
  8424. if (obj->isKindOf(KINDOF_IMMOBILE)) {
  8425. // Only consider mobile.
  8426. return;
  8427. }
  8428. if (!m_isMapReady) return;
  8429. AIUpdateInterface *ai = obj->getAIUpdateInterface();
  8430. if (!ai) return; // only consider ai objects.
  8431. ICoord2D curCell = *ai->getCurPathfindCell();
  8432. Bool centerInCell;
  8433. Int radius;
  8434. getRadiusAndCenter(obj, radius, centerInCell);
  8435. Int numCellsAbove = radius;
  8436. if (centerInCell) numCellsAbove++;
  8437. PathfindLayerEnum layer = obj->getLayer();
  8438. ICoord2D newCell;
  8439. newCell.x = newCell.y = -1;
  8440. ai->setCurPathfindCell(newCell);
  8441. Int i,j;
  8442. ICoord2D cellNdx;
  8443. //DEBUG_LOG(("Updating unit pos at cell %d, %d\n", newCell.x, newCell.y));
  8444. if (curCell.x>=0 && curCell.y>=0) {
  8445. for (i=curCell.x-radius; i<curCell.x+numCellsAbove; i++) {
  8446. for (j=curCell.y-radius; j<curCell.y+numCellsAbove; j++) {
  8447. cellNdx.x = i;
  8448. cellNdx.y = j;
  8449. PathfindCell *cell = getCell(layer, i, j);
  8450. if (cell) {
  8451. if (cell->getPosUnit()==obj->getID()) {
  8452. cell->setPosUnit(INVALID_ID, cellNdx);
  8453. }
  8454. }
  8455. if (layer!=LAYER_GROUND) {
  8456. // Remove from the ground, if present.
  8457. cell = getCell(LAYER_GROUND, i, j);
  8458. if (cell) {
  8459. if (cell->getPosUnit()==obj->getID()) {
  8460. cell->setPosUnit(INVALID_ID, cellNdx);
  8461. }
  8462. }
  8463. }
  8464. }
  8465. }
  8466. }
  8467. }
  8468. /**
  8469. * Removes a mobile unit from the pathfind grid.
  8470. */
  8471. void Pathfinder::removeUnitFromPathfindMap( Object *obj )
  8472. {
  8473. removePos(obj);
  8474. removeGoal(obj);
  8475. }
  8476. Bool Pathfinder::moveAllies(Object *obj, Path *path)
  8477. {
  8478. #ifdef DO_UNIT_TIMINGS
  8479. #pragma MESSAGE("*** WARNING *** DOING DO_UNIT_TIMINGS!!!!")
  8480. extern Bool g_UT_startTiming;
  8481. if (g_UT_startTiming) return false;
  8482. #endif
  8483. if (!obj->isKindOf(KINDOF_DOZER) && !obj->isKindOf(KINDOF_HARVESTER)) {
  8484. // Harvesters & dozers want a clear path.
  8485. if (!path->getBlockedByAlly()) return FALSE; // Only move units if it is required.
  8486. }
  8487. LatchRestore<Int> recursiveDepth(m_moveAlliesDepth, m_moveAlliesDepth+1);
  8488. if (m_moveAlliesDepth > 2) return false;
  8489. Bool centerInCell;
  8490. Int radius;
  8491. getRadiusAndCenter(obj, radius, centerInCell);
  8492. Int numCellsAbove = radius;
  8493. if (centerInCell) numCellsAbove++;
  8494. PathNode *node;
  8495. ObjectID ignoreId = INVALID_ID;
  8496. if (obj->getAIUpdateInterface()) {
  8497. ignoreId = obj->getAIUpdateInterface()->getIgnoredObstacleID();
  8498. }
  8499. for( node = path->getLastNode(); node && node != path->getFirstNode(); node = node->getPrevious() ) {
  8500. ICoord2D curCell;
  8501. worldToCell(node->getPosition(), &curCell);
  8502. Int i, j;
  8503. for (i=curCell.x-radius; i<curCell.x+numCellsAbove; i++) {
  8504. for (j=curCell.y-radius; j<curCell.y+numCellsAbove; j++) {
  8505. PathfindCell *cell = getCell(node->getLayer(), i, j);
  8506. if (cell) {
  8507. if (cell->getPosUnit()==INVALID_ID) {
  8508. continue;
  8509. }
  8510. if (cell->getPosUnit()==obj->getID()) {
  8511. continue; // It's us.
  8512. }
  8513. if (cell->getPosUnit()==ignoreId) {
  8514. continue; // It's the one we are ignoring.
  8515. }
  8516. Object *otherObj = TheGameLogic->findObjectByID(cell->getPosUnit());
  8517. if (obj->getRelationship(otherObj)!=ALLIES) {
  8518. continue; // Only move allies.
  8519. }
  8520. if (otherObj==NULL) continue;
  8521. if (obj->isKindOf(KINDOF_INFANTRY) && otherObj->isKindOf(KINDOF_INFANTRY)) {
  8522. continue; // infantry can walk through other infantry, so just let them.
  8523. }
  8524. if (obj->isKindOf(KINDOF_INFANTRY) && !otherObj->isKindOf(KINDOF_INFANTRY)) {
  8525. // If this is a general clear operation, don't let infantry push vehicles.
  8526. if (!path->getBlockedByAlly()) continue;
  8527. }
  8528. if (otherObj && otherObj->getAI() && !otherObj->getAI()->isMoving()) {
  8529. //DEBUG_LOG(("Moving ally\n"));
  8530. otherObj->getAI()->aiMoveAwayFromUnit(obj, CMD_FROM_AI);
  8531. }
  8532. }
  8533. }
  8534. }
  8535. }
  8536. return true;
  8537. }
  8538. /**
  8539. * Moves an allied unit out of the path of another unit.
  8540. * Uses A* algorithm.
  8541. */
  8542. Path *Pathfinder::getMoveAwayFromPath(Object* obj, Object *otherObj,
  8543. Path *pathToAvoid, Object *otherObj2, Path *pathToAvoid2)
  8544. {
  8545. if (m_isMapReady == false) return false; // Should always be ok.
  8546. #if defined _DEBUG || defined _INTERNAL
  8547. Int startTimeMS = ::GetTickCount();
  8548. #endif
  8549. Bool isHuman = true;
  8550. if (obj && obj->getControllingPlayer() && (obj->getControllingPlayer()->getPlayerType()==PLAYER_COMPUTER)) {
  8551. isHuman = false; // computer gets to cheat.
  8552. }
  8553. Bool otherCenter;
  8554. Int otherRadius;
  8555. getRadiusAndCenter(otherObj, otherRadius, otherCenter);
  8556. Bool isCrusher = obj ? obj->getCrusherLevel() > 0 : false;
  8557. m_zoneManager.setAllPassable();
  8558. Bool centerInCell;
  8559. Int radius;
  8560. getRadiusAndCenter(obj, radius, centerInCell);
  8561. DEBUG_ASSERTCRASH(m_openList==NULL && m_closedList == NULL, ("Dangling lists."));
  8562. // determine start cell
  8563. ICoord2D startCellNdx;
  8564. Coord3D startPos = *obj->getPosition();
  8565. if (!centerInCell) {
  8566. startPos.x += PATHFIND_CELL_SIZE_F*0.5f;
  8567. startPos.x += PATHFIND_CELL_SIZE_F*0.5f;
  8568. }
  8569. worldToCell(&startPos, &startCellNdx);
  8570. PathfindCell *parentCell = getClippedCell( obj->getLayer(), obj->getPosition() );
  8571. if (parentCell == NULL)
  8572. return false;
  8573. if (!obj->getAIUpdateInterface()) {
  8574. return false; // shouldn't happen, but can't move it without an ai.
  8575. }
  8576. const LocomotorSet& locomotorSet = obj->getAIUpdateInterface()->getLocomotorSet();
  8577. m_isTunneling = false;
  8578. if (validMovementPosition( isCrusher, locomotorSet.getValidSurfaces(), parentCell ) == false) {
  8579. m_isTunneling = true; // We can't move from our current location. So relax the constraints.
  8580. }
  8581. TCheckMovementInfo info;
  8582. info.cell = startCellNdx;
  8583. info.layer = obj->getLayer();
  8584. info.centerInCell = centerInCell;
  8585. info.radius = radius;
  8586. info.considerTransient = false;
  8587. info.acceptableSurfaces = locomotorSet.getValidSurfaces();
  8588. if (!checkForMovement(obj, info) || info.enemyFixed) {
  8589. m_isTunneling = true; // We can't move from our current location. So relax the constraints.
  8590. }
  8591. if (!parentCell->allocateInfo(startCellNdx)) {
  8592. return false;
  8593. }
  8594. parentCell->startPathfind(NULL);
  8595. // initialize "open" list to contain start cell
  8596. m_openList = parentCell;
  8597. // "closed" list is initially empty
  8598. m_closedList = NULL;
  8599. //
  8600. // Continue search until "open" list is empty, or
  8601. // until goal is found.
  8602. //
  8603. Real boxHalfWidth = radius*PATHFIND_CELL_SIZE_F - (PATHFIND_CELL_SIZE_F/4.0f);
  8604. if (centerInCell) boxHalfWidth+=PATHFIND_CELL_SIZE_F/2;
  8605. boxHalfWidth += otherRadius*PATHFIND_CELL_SIZE_F;
  8606. if (otherCenter) boxHalfWidth+=PATHFIND_CELL_SIZE_F/2;
  8607. while( m_openList != NULL )
  8608. {
  8609. // take head cell off of open list - it has lowest estimated total path cost
  8610. parentCell = m_openList;
  8611. m_openList = parentCell->removeFromOpenList(m_openList);
  8612. Region2D bounds;
  8613. Coord3D cellCenter;
  8614. adjustCoordToCell(parentCell->getXIndex(), parentCell->getYIndex(), centerInCell, cellCenter, parentCell->getLayer());
  8615. bounds.lo.x = cellCenter.x-boxHalfWidth;
  8616. bounds.lo.y = cellCenter.y-boxHalfWidth;
  8617. bounds.hi.x = cellCenter.x+boxHalfWidth;
  8618. bounds.hi.y = cellCenter.y+boxHalfWidth;
  8619. PathNode *node;
  8620. Bool overlap = false;
  8621. if (obj) {
  8622. if (bounds.lo.x<obj->getPosition()->x && bounds.hi.x>obj->getPosition()->x &&
  8623. bounds.lo.y<obj->getPosition()->y && bounds.hi.y>obj->getPosition()->y) {
  8624. //overlap = true;
  8625. }
  8626. }
  8627. for( node = pathToAvoid->getFirstNode(); node && node->getNextOptimized(); node = node->getNextOptimized() ) {
  8628. Coord2D start, end;
  8629. start.x = node->getPosition()->x;
  8630. start.y = node->getPosition()->y;
  8631. end.x = node->getNextOptimized()->getPosition()->x;
  8632. end.y = node->getNextOptimized()->getPosition()->y;
  8633. if (LineInRegion(&start, &end, &bounds)) {
  8634. overlap = true;
  8635. break;
  8636. }
  8637. }
  8638. if (otherObj) {
  8639. if (bounds.lo.x<otherObj->getPosition()->x && bounds.hi.x>otherObj->getPosition()->x &&
  8640. bounds.lo.y<otherObj->getPosition()->y && bounds.hi.y>otherObj->getPosition()->y) {
  8641. //overlap = true;
  8642. }
  8643. }
  8644. if (!overlap && pathToAvoid2) {
  8645. for( node = pathToAvoid2->getFirstNode(); node && node->getNextOptimized(); node = node->getNextOptimized() ) {
  8646. Coord2D start, end;
  8647. start.x = node->getPosition()->x;
  8648. start.y = node->getPosition()->y;
  8649. end.x = node->getNextOptimized()->getPosition()->x;
  8650. end.y = node->getNextOptimized()->getPosition()->y;
  8651. if (LineInRegion(&start, &end, &bounds)) {
  8652. overlap = true;
  8653. break;
  8654. }
  8655. }
  8656. }
  8657. if (!overlap) {
  8658. if (startCellNdx.x == parentCell->getXIndex() && startCellNdx.y == parentCell->getYIndex()) {
  8659. // we didn't move. Always move at least 1 cell. jba.
  8660. overlap = true;
  8661. }
  8662. }
  8663. ///@todo - Adjust cost intersecting path - closer to front is more expensive. jba.
  8664. if (!overlap && checkDestination(obj, parentCell->getXIndex(), parentCell->getYIndex(),
  8665. parentCell->getLayer(), radius, centerInCell)) {
  8666. // success - found a path to the goal
  8667. if (false && TheGlobalData->m_debugAI)
  8668. debugShowSearch(true);
  8669. m_isTunneling = false;
  8670. // construct and return path
  8671. Path *newPath = buildActualPath( obj, locomotorSet.getValidSurfaces(), obj->getPosition(), parentCell, centerInCell, false);
  8672. parentCell->releaseInfo();
  8673. cleanOpenAndClosedLists();
  8674. return newPath;
  8675. }
  8676. // put parent cell onto closed list - its evaluation is finished
  8677. m_closedList = parentCell->putOnClosedList( m_closedList );
  8678. // Check to see if we can change layers in this cell.
  8679. checkChangeLayers(parentCell);
  8680. examineNeighboringCells(parentCell, NULL, locomotorSet, isHuman, centerInCell, radius, startCellNdx, obj, NO_ATTACK);
  8681. }
  8682. #if defined _DEBUG || defined _INTERNAL
  8683. debugShowSearch(true);
  8684. DEBUG_LOG(("%d ", TheGameLogic->getFrame()));
  8685. DEBUG_LOG(("getMoveAwayFromPath pathfind failed -- "));
  8686. DEBUG_LOG(("Unit '%s', time %f\n", obj->getTemplate()->getName().str(), (::GetTickCount()-startTimeMS)/1000.0f));
  8687. #endif
  8688. m_isTunneling = false;
  8689. cleanOpenAndClosedLists();
  8690. return NULL;
  8691. }
  8692. /** Patch to the exiting path from the current position, either because we became blocked,
  8693. or because we had to move off the path to avoid other units. */
  8694. Path *Pathfinder::patchPath( const Object *obj, const LocomotorSet& locomotorSet,
  8695. Path *originalPath, Bool blocked )
  8696. {
  8697. //CRCDEBUG_LOG(("Pathfinder::patchPath()\n"));
  8698. #if defined _DEBUG || defined _INTERNAL
  8699. Int startTimeMS = ::GetTickCount();
  8700. #endif
  8701. if (originalPath==NULL) return NULL;
  8702. Bool centerInCell;
  8703. Int radius;
  8704. getRadiusAndCenter(obj, radius, centerInCell);
  8705. Bool isHuman = true;
  8706. if (obj && obj->getControllingPlayer() && (obj->getControllingPlayer()->getPlayerType()==PLAYER_COMPUTER)) {
  8707. isHuman = false; // computer gets to cheat.
  8708. }
  8709. m_zoneManager.setAllPassable();
  8710. DEBUG_ASSERTCRASH(m_openList==NULL && m_closedList == NULL, ("Dangling lists."));
  8711. enum {CELL_LIMIT = 2000}; // max cells to examine.
  8712. Int cellCount = 0;
  8713. Coord3D currentPosition = *obj->getPosition();
  8714. // determine start cell
  8715. ICoord2D startCellNdx;
  8716. Coord3D startPos = *obj->getPosition();
  8717. if (!centerInCell) {
  8718. startPos.x += PATHFIND_CELL_SIZE_F*0.5f;
  8719. startPos.x += PATHFIND_CELL_SIZE_F*0.5f;
  8720. }
  8721. worldToCell(&startPos, &startCellNdx);
  8722. //worldToCell(obj->getPosition(), &startCellNdx);
  8723. PathfindCell *parentCell = getClippedCell( obj->getLayer(), &currentPosition);
  8724. if (parentCell == NULL)
  8725. return false;
  8726. if (!obj->getAIUpdateInterface()) {
  8727. return false; // shouldn't happen, but can't move it without an ai.
  8728. }
  8729. m_isTunneling = false;
  8730. if (!parentCell->allocateInfo(startCellNdx)) {
  8731. return false;
  8732. }
  8733. parentCell->startPathfind( NULL);
  8734. // initialize "open" list to contain start cell
  8735. m_openList = parentCell;
  8736. // "closed" list is initially empty
  8737. m_closedList = NULL;
  8738. //
  8739. // Continue search until "open" list is empty, or
  8740. // until goal is found.
  8741. //
  8742. #if defined _DEBUG || defined _INTERNAL
  8743. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  8744. if (TheGlobalData->m_debugAI)
  8745. {
  8746. RGBColor color;
  8747. color.setFromInt(0);
  8748. addIcon(NULL, 0,0,color);
  8749. }
  8750. #endif
  8751. PathNode *startNode;
  8752. Coord3D goalPos = *originalPath->getLastNode()->getPosition();
  8753. Real goalDeltaSqr = sqr(goalPos.x-currentPosition.x) + sqr(goalPos.y - currentPosition.y);
  8754. for( startNode = originalPath->getLastNode(); startNode != originalPath->getFirstNode(); startNode = startNode->getPrevious() ) {
  8755. ICoord2D cellCoord;
  8756. worldToCell(startNode->getPosition(), &cellCoord);
  8757. TCheckMovementInfo info;
  8758. info.cell = cellCoord;
  8759. info.layer = startNode->getLayer();
  8760. info.centerInCell = centerInCell;
  8761. info.radius = radius;
  8762. info.considerTransient = blocked;
  8763. info.acceptableSurfaces = locomotorSet.getValidSurfaces();
  8764. #if defined _DEBUG || defined _INTERNAL
  8765. if (TheGlobalData->m_debugAI) {
  8766. RGBColor color;
  8767. color.setFromInt(0);
  8768. color.green = 1;
  8769. addIcon(startNode->getPosition(), PATHFIND_CELL_SIZE_F*0.5f, 100, color);
  8770. }
  8771. #endif
  8772. Int dx = cellCoord.x-startCellNdx.x;
  8773. Int dy = cellCoord.y-startCellNdx.y;
  8774. if (dx<-2 || dx>2) info.considerTransient = false;
  8775. if (dy<-2 || dy>2) info.considerTransient = false;
  8776. if (!checkForMovement(obj, info)) {
  8777. break;
  8778. }
  8779. if (info.allyFixedCount || info.enemyFixed) {
  8780. break; // Don't patch through cells that are occupied.
  8781. }
  8782. Real curSqr = sqr(startNode->getPosition()->x-currentPosition.x) + sqr(startNode->getPosition()->y - currentPosition.y);
  8783. if (curSqr < goalDeltaSqr) {
  8784. goalPos = *startNode->getPosition();
  8785. goalDeltaSqr = curSqr;
  8786. }
  8787. }
  8788. if (startNode == originalPath->getLastNode()) {
  8789. cleanOpenAndClosedLists();
  8790. return NULL; // no open nodes.
  8791. }
  8792. PathfindCell *candidateGoal;
  8793. candidateGoal = getCell(LAYER_GROUND, &goalPos); // just using for cost estimates.
  8794. ICoord2D goalCellNdx;
  8795. worldToCell(&goalPos, &goalCellNdx);
  8796. if (!candidateGoal->allocateInfo(goalCellNdx)) {
  8797. return NULL;
  8798. }
  8799. while( m_openList != NULL )
  8800. {
  8801. // take head cell off of open list - it has lowest estimated total path cost
  8802. parentCell = m_openList;
  8803. m_openList = parentCell->removeFromOpenList(m_openList);
  8804. Coord3D cellCenter;
  8805. adjustCoordToCell(parentCell->getXIndex(), parentCell->getYIndex(), centerInCell, cellCenter, parentCell->getLayer());
  8806. PathNode *matchNode;
  8807. Bool found = false;
  8808. for( matchNode = originalPath->getLastNode(); matchNode != startNode; matchNode = matchNode->getPrevious() ) {
  8809. if (cellCenter.x == matchNode->getPosition()->x && cellCenter.y == matchNode->getPosition()->y) {
  8810. found = true;
  8811. break;
  8812. }
  8813. }
  8814. if (found ) {
  8815. // success - found a path to the goal
  8816. if ( TheGlobalData->m_debugAI)
  8817. debugShowSearch(true);
  8818. m_isTunneling = false;
  8819. // construct and return path
  8820. Path *path = newInstance(Path);
  8821. PathNode *node;
  8822. for( node = originalPath->getLastNode(); node != matchNode; node = node->getPrevious() ) {
  8823. path->prependNode(node->getPosition(), node->getLayer());
  8824. }
  8825. prependCells(path, obj->getPosition(), parentCell, centerInCell);
  8826. // cleanup the path by checking line of sight
  8827. path->optimize(obj, locomotorSet.getValidSurfaces(), blocked);
  8828. parentCell->releaseInfo();
  8829. cleanOpenAndClosedLists();
  8830. candidateGoal->releaseInfo();
  8831. return path;
  8832. }
  8833. // put parent cell onto closed list - its evaluation is finished
  8834. m_closedList = parentCell->putOnClosedList( m_closedList );
  8835. if (cellCount < CELL_LIMIT) {
  8836. // Check to see if we can change layers in this cell.
  8837. checkChangeLayers(parentCell);
  8838. cellCount += examineNeighboringCells(parentCell, NULL, locomotorSet, isHuman, centerInCell, radius, startCellNdx, obj, NO_ATTACK);
  8839. }
  8840. }
  8841. #if defined _DEBUG || defined _INTERNAL
  8842. DEBUG_LOG(("%d ", TheGameLogic->getFrame()));
  8843. DEBUG_LOG(("patchPath Pathfind failed -- "));
  8844. DEBUG_LOG(("Unit '%s', time %f\n", obj->getTemplate()->getName().str(), (::GetTickCount()-startTimeMS)/1000.0f));
  8845. if (TheGlobalData->m_debugAI) {
  8846. debugShowSearch(true);
  8847. }
  8848. #endif
  8849. m_isTunneling = false;
  8850. if (!candidateGoal->getOpen() && !candidateGoal->getClosed())
  8851. {
  8852. // Not on one of the lists
  8853. candidateGoal->releaseInfo();
  8854. }
  8855. cleanOpenAndClosedLists();
  8856. return false;
  8857. }
  8858. /** Find a short, valid path to a location that obj can attack victim from. */
  8859. Path *Pathfinder::findAttackPath( const Object *obj, const LocomotorSet& locomotorSet, const Coord3D *from,
  8860. const Object *victim, const Coord3D* victimPos, const Weapon *weapon )
  8861. {
  8862. /*
  8863. CRCDEBUG_LOG(("Pathfinder::findAttackPath() for object %d (%s)\n", obj->getID(), obj->getTemplate()->getName().str()));
  8864. XferCRC xferCRC;
  8865. xferCRC.open("lightCRC");
  8866. xferCRC.xferSnapshot((Object *)obj);
  8867. xferCRC.close();
  8868. CRCDEBUG_LOG(("obj CRC is %X\n", xferCRC.getCRC()));
  8869. if (from)
  8870. {
  8871. CRCDEBUG_LOG(("from: (%g,%g,%g) (%X,%X,%X)\n",
  8872. from->x, from->y, from->z,
  8873. AS_INT(from->x), AS_INT(from->y), AS_INT(from->z)));
  8874. }
  8875. if (victim)
  8876. {
  8877. CRCDEBUG_LOG(("victim is %d (%s)\n", victim->getID(), victim->getTemplate()->getName().str()));
  8878. XferCRC xferCRC;
  8879. xferCRC.open("lightCRC");
  8880. xferCRC.xferSnapshot((Object *)victim);
  8881. xferCRC.close();
  8882. CRCDEBUG_LOG(("victim CRC is %X\n", xferCRC.getCRC()));
  8883. }
  8884. if (victimPos)
  8885. {
  8886. CRCDEBUG_LOG(("from: (%g,%g,%g) (%X,%X,%X)\n",
  8887. victimPos->x, victimPos->y, victimPos->z,
  8888. AS_INT(victimPos->x), AS_INT(victimPos->y), AS_INT(victimPos->z)));
  8889. }
  8890. */
  8891. if (m_isMapReady == false) return false; // Should always be ok.
  8892. #if defined _DEBUG || defined _INTERNAL
  8893. // Int startTimeMS = ::GetTickCount();
  8894. #endif
  8895. Bool isCrusher = obj ? obj->getCrusherLevel() > 0 : false;
  8896. Int radius;
  8897. Bool centerInCell;
  8898. getRadiusAndCenter(obj, radius, centerInCell);
  8899. // Quick check: See if moving couple of cells towards the victim will work.
  8900. {
  8901. Coord3D curPos = *obj->getPosition();
  8902. Coord3D goalPos = victim?*victim->getPosition():*victimPos;
  8903. Coord3D delta;
  8904. delta.set(goalPos.x-curPos.x, goalPos.y-curPos.y, 0);
  8905. delta.normalize();
  8906. delta.x *= PATHFIND_CELL_SIZE_F;
  8907. delta.y *= PATHFIND_CELL_SIZE_F;
  8908. Int i;
  8909. for (i=1; i<10; i++) {
  8910. Coord3D testPos = curPos;
  8911. testPos.x += delta.x*i*0.5f;
  8912. testPos.y += delta.y*i*0.5f;
  8913. ICoord2D cellNdx;
  8914. worldToCell(&testPos, &cellNdx);
  8915. PathfindCell *aCell = getCell(obj->getLayer(), cellNdx.x, cellNdx.y);
  8916. if (aCell==NULL) break;
  8917. if (!validMovementPosition( isCrusher, locomotorSet.getValidSurfaces(), aCell )) {
  8918. break;
  8919. }
  8920. if (!checkDestination(obj, cellNdx.x, cellNdx.y, obj->getLayer(), radius, centerInCell)) {
  8921. break;
  8922. }
  8923. if (weapon->isGoalPosWithinAttackRange(obj, &testPos, victim, victimPos)) {
  8924. if (!isAttackViewBlockedByObstacle(obj, testPos, victim, *victimPos)) {
  8925. // return path.
  8926. Path *path = newInstance(Path);
  8927. path->prependNode( &testPos, obj->getLayer() );
  8928. path->prependNode( &curPos, obj->getLayer() );
  8929. path->getFirstNode()->setNextOptimized(path->getFirstNode()->getNext());
  8930. if (TheGlobalData->m_debugAI==AI_DEBUG_PATHS) {
  8931. setDebugPath(path);
  8932. }
  8933. return path;
  8934. }
  8935. }
  8936. }
  8937. }
  8938. const Int ATTACK_CELL_LIMIT = 2500; // this is a rather expensive operation, so limit the search.
  8939. Bool isHuman = true;
  8940. if (obj && obj->getControllingPlayer() && (obj->getControllingPlayer()->getPlayerType()==PLAYER_COMPUTER)) {
  8941. isHuman = false; // computer gets to cheat.
  8942. }
  8943. m_zoneManager.clearPassableFlags();
  8944. Path *hPat = findClosestHierarchicalPath(isHuman, locomotorSet, from, victimPos, isCrusher);
  8945. if (hPat) {
  8946. hPat->deleteInstance();
  8947. } else {
  8948. m_zoneManager.setAllPassable();
  8949. }
  8950. Int cellCount = 0;
  8951. DEBUG_ASSERTCRASH(m_openList==NULL && m_closedList == NULL, ("Dangling lists."));
  8952. Int attackDistance = weapon->getAttackDistance(obj, victim, victimPos);
  8953. attackDistance += 3*PATHFIND_CELL_SIZE;
  8954. // determine start cell
  8955. ICoord2D startCellNdx;
  8956. Coord3D objPos = *obj->getPosition();
  8957. // since worldtocell truncates, add.
  8958. if (centerInCell) {
  8959. objPos.x += PATHFIND_CELL_SIZE_F/2.0f;
  8960. objPos.y += PATHFIND_CELL_SIZE_F/2.0f;
  8961. }
  8962. worldToCell(&objPos, &startCellNdx);
  8963. PathfindCell *parentCell = getClippedCell( obj->getLayer(), &objPos );
  8964. if (parentCell == NULL)
  8965. return false;
  8966. if (!obj->getAIUpdateInterface()) {
  8967. return false; // shouldn't happen, but can't move it without an ai.
  8968. }
  8969. const PathfindCell *startCell = parentCell;
  8970. if (!parentCell->allocateInfo(startCellNdx)) {
  8971. return false;
  8972. }
  8973. parentCell->startPathfind(NULL);
  8974. // determine start cell
  8975. ICoord2D victimCellNdx;
  8976. worldToCell(victim ? victim->getPosition() : victimPos, &victimCellNdx);
  8977. // determine goal cell
  8978. PathfindCell *goalCell = getCell( LAYER_GROUND, victimCellNdx.x, victimCellNdx.y );
  8979. if (goalCell == NULL)
  8980. return NULL;
  8981. if (!goalCell->allocateInfo(victimCellNdx)) {
  8982. return false;
  8983. }
  8984. // initialize "open" list to contain start cell
  8985. m_openList = parentCell;
  8986. // "closed" list is initially empty
  8987. m_closedList = NULL;
  8988. //
  8989. // Continue search until "open" list is empty, or
  8990. // until goal is found.
  8991. //
  8992. PathfindCell *closestCell = NULL;
  8993. Real closestDistanceSqr = FLT_MAX;
  8994. Bool checkLOS = false;
  8995. if (!victim) {
  8996. checkLOS = true;
  8997. }
  8998. if (victim && !victim->isSignificantlyAboveTerrain()) {
  8999. checkLOS = true;
  9000. }
  9001. while( m_openList != NULL )
  9002. {
  9003. // take head cell off of open list - it has lowest estimated total path cost
  9004. parentCell = m_openList;
  9005. m_openList = parentCell->removeFromOpenList(m_openList);
  9006. Coord3D cellCenter;
  9007. adjustCoordToCell(parentCell->getXIndex(), parentCell->getYIndex(), centerInCell, cellCenter, parentCell->getLayer());
  9008. ///@todo - Adjust cost intersecting path - closer to front is more expensive. jba.
  9009. if (weapon->isGoalPosWithinAttackRange(obj, &cellCenter, victim, victimPos) &&
  9010. checkDestination(obj, parentCell->getXIndex(), parentCell->getYIndex(),
  9011. parentCell->getLayer(), radius, centerInCell)) {
  9012. // check line of sight.
  9013. Bool viewBlocked = false;
  9014. if (checkLOS)
  9015. {
  9016. viewBlocked = isAttackViewBlockedByObstacle(obj, cellCenter, victim, *victimPos);
  9017. }
  9018. if (startCell == parentCell) {
  9019. // We never want to accept our starting cell.
  9020. // If we could attack from there, we wouldn't be calling
  9021. // FindAttackPath. Usually happens cause the cell is valid for attack, but
  9022. // a point near the cell center isn't, and that happens to be where the
  9023. // attacker is standing, and it's too close to move to.
  9024. viewBlocked = true;
  9025. } else {
  9026. // If through some unfortunate rounding, we end up moving near ourselves,
  9027. // don't want it.
  9028. Coord3D cellPos;
  9029. adjustCoordToCell(parentCell->getXIndex(), parentCell->getYIndex(), centerInCell, cellPos, parentCell->getLayer());
  9030. Real dx = (cellPos.x - objPos.x);
  9031. Real dy = (cellPos.y - objPos.y);
  9032. if (sqr(dx) + sqr(dy) < sqr(PATHFIND_CELL_SIZE_F*0.5f)) {
  9033. viewBlocked = true;
  9034. }
  9035. }
  9036. if (!viewBlocked)
  9037. {
  9038. // success - found a path to the goal
  9039. Bool show = TheGlobalData->m_debugAI;
  9040. #ifdef INTENSE_DEBUG
  9041. Int count = 0;
  9042. PathfindCell *cur;
  9043. for (cur = m_closedList; cur; cur=cur->getNextOpen()) {
  9044. count++;
  9045. }
  9046. if (count>1000) {
  9047. show = true;
  9048. DEBUG_LOG(("FAP cells %d obj %s %x\n", count, obj->getTemplate()->getName().str(), obj));
  9049. #ifdef STATE_MACHINE_DEBUG
  9050. if( obj->getAIUpdateInterface() )
  9051. {
  9052. DEBUG_LOG(("State %s\n", obj->getAIUpdateInterface()->getCurrentStateName().str()));
  9053. }
  9054. #endif
  9055. TheScriptEngine->AppendDebugMessage("Big Attack path", false);
  9056. }
  9057. #endif
  9058. if (show)
  9059. debugShowSearch(true);
  9060. #if defined _DEBUG || defined _INTERNAL
  9061. //DEBUG_LOG(("Attack path took %d cells, %f sec\n", cellCount, (::GetTickCount()-startTimeMS)/1000.0f));
  9062. #endif
  9063. // construct and return path
  9064. Path *path = buildActualPath( obj, locomotorSet.getValidSurfaces(), obj->getPosition(), parentCell, centerInCell, false);
  9065. parentCell->releaseInfo();
  9066. if (goalCell->hasInfo() && !goalCell->getClosed() && !goalCell->getOpen()) {
  9067. goalCell->releaseInfo();
  9068. }
  9069. cleanOpenAndClosedLists();
  9070. return path;
  9071. }
  9072. }
  9073. if (checkDestination(obj, parentCell->getXIndex(), parentCell->getYIndex(),
  9074. parentCell->getLayer(), radius, centerInCell)) {
  9075. if (validMovementPosition( isCrusher, locomotorSet.getValidSurfaces(), parentCell )) {
  9076. Real dx = IABS(victimCellNdx.x-parentCell->getXIndex());
  9077. Real dy = IABS(victimCellNdx.y-parentCell->getYIndex());
  9078. Real distSqr = dx*dx+dy*dy;
  9079. if (distSqr < closestDistanceSqr) {
  9080. closestCell = parentCell;
  9081. closestDistanceSqr = distSqr;
  9082. }
  9083. }
  9084. }
  9085. // put parent cell onto closed list - its evaluation is finished
  9086. m_closedList = parentCell->putOnClosedList( m_closedList );
  9087. if (cellCount < ATTACK_CELL_LIMIT) {
  9088. // Check to see if we can change layers in this cell.
  9089. checkChangeLayers(parentCell);
  9090. cellCount += examineNeighboringCells(parentCell, goalCell, locomotorSet, isHuman, centerInCell,
  9091. radius, startCellNdx, obj, attackDistance);
  9092. }
  9093. }
  9094. #ifdef INTENSE_DEBUG
  9095. DEBUG_LOG(("obj %s %x\n", obj->getTemplate()->getName().str(), obj));
  9096. #ifdef STATE_MACHINE_DEBUG
  9097. if( obj->getAIUpdateInterface() )
  9098. {
  9099. DEBUG_LOG(("State %s\n", obj->getAIUpdateInterface()->getCurrentStateName().str()));
  9100. }
  9101. #endif
  9102. debugShowSearch(true);
  9103. TheScriptEngine->AppendDebugMessage("Overflowed attack path", false);
  9104. #endif
  9105. #if 0
  9106. if (closestCell) {
  9107. // construct and return path
  9108. Path *path = buildActualPath( obj, locomotorSet, obj->getPosition(), closestCell, centerInCell, false);
  9109. cleanOpenAndClosedLists();
  9110. return path;
  9111. }
  9112. #if defined _DEBUG || defined _INTERNAL
  9113. DEBUG_LOG(("%d (%d cells)", TheGameLogic->getFrame(), cellCount));
  9114. DEBUG_LOG(("Attack Pathfind failed from (%f,%f) to (%f,%f) -- \n", from->x, from->y, victim->getPosition()->x, victim->getPosition()->y));
  9115. DEBUG_LOG(("Unit '%s', attacking '%s' time %f\n", obj->getTemplate()->getName().str(), victim->getTemplate()->getName().str(), (::GetTickCount()-startTimeMS)/1000.0f));
  9116. #endif
  9117. #endif
  9118. #ifdef DUMP_PERF_STATS
  9119. TheGameLogic->incrementOverallFailedPathfinds();
  9120. #endif
  9121. m_isTunneling = false;
  9122. if (goalCell->hasInfo() && !goalCell->getClosed() && !goalCell->getOpen()) {
  9123. goalCell->releaseInfo();
  9124. }
  9125. cleanOpenAndClosedLists();
  9126. return false;
  9127. }
  9128. /** Find a short, valid path to a location that is safe from the repulsors. */
  9129. Path *Pathfinder::findSafePath( const Object *obj, const LocomotorSet& locomotorSet,
  9130. const Coord3D *from, const Coord3D* repulsorPos1, const Coord3D* repulsorPos2, Real repulsorRadius)
  9131. {
  9132. //CRCDEBUG_LOG(("Pathfinder::findSafePath()\n"));
  9133. if (m_isMapReady == false) return false; // Should always be ok.
  9134. #if defined _DEBUG || defined _INTERNAL
  9135. // Int startTimeMS = ::GetTickCount();
  9136. #endif
  9137. const Int MAX_CELLS = 2000; // this is a rather expensive operation, so limit the search.
  9138. Bool centerInCell;
  9139. Int radius;
  9140. getRadiusAndCenter(obj, radius, centerInCell);
  9141. Real repulsorDistSqr = repulsorRadius*repulsorRadius;
  9142. Int cellCount = 0;
  9143. Bool isHuman = true;
  9144. if (obj && obj->getControllingPlayer() && (obj->getControllingPlayer()->getPlayerType()==PLAYER_COMPUTER)) {
  9145. isHuman = false; // computer gets to cheat.
  9146. }
  9147. DEBUG_ASSERTCRASH(m_openList==NULL && m_closedList == NULL, ("Dangling lists."));
  9148. // create unique "mark" values for open and closed cells for this pathfind invocation
  9149. m_zoneManager.setAllPassable();
  9150. // determine start cell
  9151. ICoord2D startCellNdx;
  9152. worldToCell(obj->getPosition(), &startCellNdx);
  9153. PathfindCell *parentCell = getClippedCell( obj->getLayer(), obj->getPosition() );
  9154. if (parentCell == NULL)
  9155. return false;
  9156. if (!obj->getAIUpdateInterface()) {
  9157. return false; // shouldn't happen, but can't move it without an ai.
  9158. }
  9159. if (!parentCell->allocateInfo(startCellNdx)) {
  9160. return false;
  9161. }
  9162. parentCell->startPathfind( NULL);
  9163. // initialize "open" list to contain start cell
  9164. m_openList = parentCell;
  9165. // "closed" list is initially empty
  9166. m_closedList = NULL;
  9167. //
  9168. // Continue search until "open" list is empty, or
  9169. // until goal is found.
  9170. //
  9171. Real farthestDistanceSqr = 0;
  9172. while( m_openList != NULL )
  9173. {
  9174. // take head cell off of open list - it has lowest estimated total path cost
  9175. parentCell = m_openList;
  9176. m_openList = parentCell->removeFromOpenList(m_openList);
  9177. Coord3D cellCenter;
  9178. adjustCoordToCell(parentCell->getXIndex(), parentCell->getYIndex(), centerInCell, cellCenter, parentCell->getLayer());
  9179. ///@todo - Adjust cost intersecting path - closer to front is more expensive. jba.
  9180. Real dx = cellCenter.x-repulsorPos1->x;
  9181. Real dy = cellCenter.y-repulsorPos1->y;
  9182. Bool ok = false;
  9183. Real distSqr = dx*dx+dy*dy;
  9184. dx = cellCenter.x-repulsorPos2->x;
  9185. dy = cellCenter.y-repulsorPos2->y;
  9186. Real distSqr2 = dx*dx+dy*dy;
  9187. if (distSqr2<distSqr) {
  9188. distSqr = distSqr2;
  9189. }
  9190. if (distSqr>repulsorDistSqr) {
  9191. ok = true;
  9192. }
  9193. if (m_openList == NULL && cellCount>0) {
  9194. ok = true; // exhausted the search space, just take the last cell.
  9195. }
  9196. if (distSqr > farthestDistanceSqr) {
  9197. farthestDistanceSqr = distSqr;
  9198. if (cellCount > MAX_CELLS) {
  9199. #ifdef INTENSE_DEBUG
  9200. DEBUG_LOG(("Took intermediate path, dist %f, goal dist %f\n", sqrt(farthestDistanceSqr), repulsorRadius));
  9201. #endif
  9202. ok = true; // Already a big search, just take this one.
  9203. }
  9204. }
  9205. if ( ok &&
  9206. checkDestination(obj, parentCell->getXIndex(), parentCell->getYIndex(),
  9207. parentCell->getLayer(), radius, centerInCell)) {
  9208. // success - found a path to the goal
  9209. Bool show = TheGlobalData->m_debugAI;
  9210. #ifdef INTENSE_DEBUG
  9211. Int count = 0;
  9212. PathfindCell *cur;
  9213. for (cur = m_closedList; cur; cur=cur->getNextOpen()) {
  9214. count++;
  9215. }
  9216. if (count>2000) {
  9217. show = true;
  9218. DEBUG_LOG(("cells %d obj %s %x\n", count, obj->getTemplate()->getName().str(), obj));
  9219. #ifdef STATE_MACHINE_DEBUG
  9220. if( obj->getAIUpdateInterface() )
  9221. {
  9222. DEBUG_LOG(("State %s\n", obj->getAIUpdateInterface()->getCurrentStateName().str()));
  9223. }
  9224. #endif
  9225. TheScriptEngine->AppendDebugMessage("Big Safe path", false);
  9226. }
  9227. #endif
  9228. if (show)
  9229. debugShowSearch(true);
  9230. #if defined _DEBUG || defined _INTERNAL
  9231. //DEBUG_LOG(("Attack path took %d cells, %f sec\n", cellCount, (::GetTickCount()-startTimeMS)/1000.0f));
  9232. #endif
  9233. // construct and return path
  9234. Path *path = buildActualPath( obj, locomotorSet.getValidSurfaces(), obj->getPosition(), parentCell, centerInCell, false);
  9235. parentCell->releaseInfo();
  9236. cleanOpenAndClosedLists();
  9237. return path;
  9238. }
  9239. // put parent cell onto closed list - its evaluation is finished
  9240. m_closedList = parentCell->putOnClosedList( m_closedList );
  9241. // Check to see if we can change layers in this cell.
  9242. checkChangeLayers(parentCell);
  9243. cellCount += examineNeighboringCells(parentCell, NULL, locomotorSet, isHuman, centerInCell, radius, startCellNdx, obj, NO_ATTACK);
  9244. }
  9245. #ifdef INTENSE_DEBUG
  9246. DEBUG_LOG(("obj %s %x count %d\n", obj->getTemplate()->getName().str(), obj, cellCount));
  9247. #ifdef STATE_MACHINE_DEBUG
  9248. if( obj->getAIUpdateInterface() )
  9249. {
  9250. DEBUG_LOG(("State %s\n", obj->getAIUpdateInterface()->getCurrentStateName().str()));
  9251. }
  9252. #endif
  9253. TheScriptEngine->AppendDebugMessage("Overflowed Safe path", false);
  9254. #endif
  9255. #if 0
  9256. #if defined _DEBUG || defined _INTERNAL
  9257. DEBUG_LOG(("%d (%d cells)", TheGameLogic->getFrame(), cellCount));
  9258. DEBUG_LOG(("Attack Pathfind failed from (%f,%f) to (%f,%f) -- \n", from->x, from->y, victim->getPosition()->x, victim->getPosition()->y));
  9259. DEBUG_LOG(("Unit '%s', attacking '%s' time %f\n", obj->getTemplate()->getName().str(), victim->getTemplate()->getName().str(), (::GetTickCount()-startTimeMS)/1000.0f));
  9260. #endif
  9261. #endif
  9262. #ifdef DUMP_PERF_STATS
  9263. TheGameLogic->incrementOverallFailedPathfinds();
  9264. #endif
  9265. m_isTunneling = false;
  9266. cleanOpenAndClosedLists();
  9267. return false;
  9268. }
  9269. //-----------------------------------------------------------------------------
  9270. void Pathfinder::crc( Xfer *xfer )
  9271. {
  9272. CRCDEBUG_LOG(("Pathfinder::crc() on frame %d\n", TheGameLogic->getFrame()));
  9273. CRCDEBUG_LOG(("beginning CRC: %8.8X\n", ((XferCRC *)xfer)->getCRC()));
  9274. xfer->xferUser( &m_extent, sizeof(IRegion2D) );
  9275. CRCDEBUG_LOG(("m_extent: %8.8X\n", ((XferCRC *)xfer)->getCRC()));
  9276. xfer->xferBool( &m_isMapReady );
  9277. CRCDEBUG_LOG(("m_isMapReady: %8.8X\n", ((XferCRC *)xfer)->getCRC()));
  9278. xfer->xferBool( &m_isTunneling );
  9279. CRCDEBUG_LOG(("m_isTunneling: %8.8X\n", ((XferCRC *)xfer)->getCRC()));
  9280. Int obsolete1 = 0;
  9281. xfer->xferInt( &obsolete1 );
  9282. xfer->xferUser(&m_ignoreObstacleID, sizeof(ObjectID));
  9283. CRCDEBUG_LOG(("m_ignoreObstacleID: %8.8X\n", ((XferCRC *)xfer)->getCRC()));
  9284. xfer->xferUser(m_queuedPathfindRequests, sizeof(ObjectID)*PATHFIND_QUEUE_LEN);
  9285. CRCDEBUG_LOG(("m_queuedPathfindRequests: %8.8X\n", ((XferCRC *)xfer)->getCRC()));
  9286. xfer->xferInt(&m_queuePRHead);
  9287. CRCDEBUG_LOG(("m_queuePRHead: %8.8X\n", ((XferCRC *)xfer)->getCRC()));
  9288. xfer->xferInt(&m_queuePRTail);
  9289. CRCDEBUG_LOG(("m_queuePRTail: %8.8X\n", ((XferCRC *)xfer)->getCRC()));
  9290. xfer->xferInt(&m_numWallPieces);
  9291. CRCDEBUG_LOG(("m_numWallPieces: %8.8X\n", ((XferCRC *)xfer)->getCRC()));
  9292. for (Int i=0; i<MAX_WALL_PIECES; ++i)
  9293. {
  9294. xfer->xferObjectID(&m_wallPieces[MAX_WALL_PIECES]);
  9295. }
  9296. CRCDEBUG_LOG(("m_wallPieces: %8.8X\n", ((XferCRC *)xfer)->getCRC()));
  9297. xfer->xferReal(&m_wallHeight);
  9298. CRCDEBUG_LOG(("m_wallHeight: %8.8X\n", ((XferCRC *)xfer)->getCRC()));
  9299. xfer->xferInt(&m_cumulativeCellsAllocated);
  9300. CRCDEBUG_LOG(("m_cumulativeCellsAllocated: %8.8X\n", ((XferCRC *)xfer)->getCRC()));
  9301. } // end crc
  9302. //-----------------------------------------------------------------------------
  9303. void Pathfinder::xfer( Xfer *xfer )
  9304. {
  9305. // version
  9306. XferVersion currentVersion = 1;
  9307. XferVersion version = currentVersion;
  9308. xfer->xferVersion( &version, currentVersion );
  9309. } // end xfer
  9310. //-----------------------------------------------------------------------------
  9311. void Pathfinder::loadPostProcess( void )
  9312. {
  9313. } // end loadPostProcess