PartitionManager.cpp 187 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781578257835784578557865787578857895790579157925793579457955796579757985799580058015802580358045805580658075808580958105811581258135814581558165817581858195820582158225823582458255826582758285829583058315832583358345835583658375838583958405841584258435844584558465847584858495850585158525853
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE: PartitionManager.cpp ////////////////////////////////////////////////////
  24. //-----------------------------------------------------------------------------
  25. //
  26. // Westwood Studios Pacific.
  27. //
  28. // Confidential Information
  29. // Copyright (C) 2001 - All Rights Reserved
  30. //
  31. //-----------------------------------------------------------------------------
  32. //
  33. // Project: RTS3
  34. //
  35. // File name: PartitionManager.cpp
  36. //
  37. // Created: Steven Johnson, September 2001
  38. //
  39. // Desc: Partition management, this system will allow us to partition the
  40. // objects in space, iterate objects in specified volumes,
  41. // regions, by types and other properties.
  42. //
  43. //-----------------------------------------------------------------------------
  44. //-----------------------------------------------------------------------------
  45. // Includes
  46. //-----------------------------------------------------------------------------
  47. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  48. #include "Common/ActionManager.h"
  49. #include "Common/DiscreteCircle.h"
  50. #include "Common/GameEngine.h"
  51. #include "Common/GameState.h"
  52. #include "Common/MessageStream.h"
  53. #include "Common/NameKeyGenerator.h"
  54. #include "Common/PerfTimer.h"
  55. #include "Common/Player.h"
  56. #include "Common/PlayerList.h"
  57. #include "Common/Radar.h"
  58. #include "Common/ThingFactory.h" // for bullet type hack
  59. #include "Common/ThingTemplate.h"
  60. #include "Common/Xfer.h"
  61. #include "GameLogic/AIPathfind.h"
  62. #include "GameLogic/GameLogic.h"
  63. #include "GameLogic/Object.h"
  64. #include "GameLogic/Module/AIUpdate.h"
  65. #include "GameLogic/Module/BodyModule.h"
  66. #include "GameLogic/Module/CollideModule.h"
  67. #include "GameLogic/Module/ContainModule.h"
  68. #include "GameLogic/Module/StealthUpdate.h"
  69. #include "GameLogic/PartitionManager.h"
  70. #include "GameLogic/PolygonTrigger.h"
  71. #include "GameLogic/Squad.h"
  72. #include "GameLogic/GhostObject.h"
  73. #include "GameClient/Line2D.h"
  74. #include "GameClient/ControlBar.h"
  75. #ifdef _DEBUG
  76. //#include "GameClient/InGameUI.h" // for debugHints
  77. #include "Common/PlayerList.h"
  78. #endif
  79. #ifdef PM_CACHE_TERRAIN_HEIGHT
  80. #include "common/mapobject.h"
  81. #endif
  82. #ifdef DUMP_PERF_STATS
  83. long s_countInClosestObjects = 0;
  84. long s_countInClosestObjectsThisFrame = 0;
  85. Int64 s_timeInClosestObjects = 0;
  86. Int64 s_timeInClosestObjectsThisFrame = 0;
  87. UnsignedInt s_gcoPerfFrame = 0xffffffff;
  88. #endif
  89. #ifdef _INTERNAL
  90. // for occasional debugging...
  91. //#pragma optimize("", off)
  92. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  93. #endif
  94. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  95. const Real HUGE_DIST_SQR = (HUGE_DIST*HUGE_DIST);
  96. #define DISABLE_INVALID_PREVENTION //Steven, I had to turn this off because it was causing problem with map border resizing (USA04). -MW
  97. //------------------------------------------------------------------------------ Performance Timers
  98. //#include "Common/PerfMetrics.h"
  99. //#include "Common/PerfTimer.h"
  100. //-------------------------------------------------------------------------------------------------
  101. //-----------------------------------------------------------------------------
  102. // Defines
  103. //-----------------------------------------------------------------------------
  104. //-----------------------------------------------------------------------------
  105. static PartitionContactList* TheContactList = NULL;
  106. //-----------------------------------------------------------------------------
  107. // Local Types
  108. //-----------------------------------------------------------------------------
  109. struct ThreatValueParms
  110. {
  111. Int playerIndex;
  112. Real xCenter;
  113. Real yCenter;
  114. Real radius;
  115. UnsignedInt threatOrValue;
  116. };
  117. struct CollideInfo
  118. {
  119. Coord3D position;
  120. GeometryInfo geom;
  121. Real angle;
  122. CollideInfo(const Coord3D* p, const GeometryInfo& g, Real a) : position(*p), geom(g), angle(a) { }
  123. };
  124. struct CellValueProcParms
  125. {
  126. Int valueRequired;
  127. Bool greaterThan;
  128. ValueOrThreat valueType;
  129. PlayerMaskType allPlayersMask[MAX_PLAYER_COUNT];
  130. PlayerMaskType allowedPlayersMasks;
  131. };
  132. static int cellValueProc(PartitionCell* cell, void* userData);
  133. /*
  134. Notes:
  135. -- collideLoc and collideNormal will only be filled in if result is true;
  136. they are undefined if result is false.
  137. -- either collideLoc or collideNormal may be null if you don't need 'em.
  138. -- collideLoc is not guaranteed to lie precisely on either 'a' or 'b'!
  139. (this matters iff the objects are overlapping and thus have multiple/ambiguous
  140. collision points; we pick an 'in-between' spot to simplify matters, such that
  141. the same spot will be used for the collision in both directions)
  142. -- collideNormal is the normal the collision surface at 'a' at collideLoc.
  143. (thus the normal for b is exactly opposite in direction)
  144. -- collideNormal is guaranteed to be a unit vector
  145. */
  146. typedef Bool (*CollideTestProc)(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo);
  147. // if the dist is greater than maxDist, return false, and the output stuff is undefined.
  148. typedef Bool (*DistCalcProc)
  149. (
  150. const Coord3D *posA,
  151. const Object *objA,
  152. const Coord3D *posB,
  153. const Object *objB,
  154. Real& abDistSqr,
  155. Coord3D& abVec,
  156. Real maxDistSqr
  157. );
  158. //-----------------------------------------------------------------------------
  159. // Inline Functions
  160. //-----------------------------------------------------------------------------
  161. //-----------------------------------------------------------------------------
  162. #define NO_FILTER_PROFILING
  163. #ifdef FILTER_PROFILING
  164. Bool DoFilterProfiling = false;
  165. #endif
  166. //DECLARE_PERF_TIMER(filtersAllow)
  167. inline Bool filtersAllow(PartitionFilter **filters, Object *objOther)
  168. {
  169. //USE_PERF_TIMER(filtersAllow)
  170. #ifdef FILTER_PROFILING
  171. const Int MAXR = 32;
  172. static const char* names[MAXR];
  173. static Int rejections[MAXR];
  174. static Int usefulRejections[MAXR];
  175. static Int calls = 0;
  176. static Int maxEver = 0;
  177. Int idx;
  178. Bool allow = true;
  179. if (DoFilterProfiling)
  180. {
  181. if (calls == 0)
  182. {
  183. for (idx = 0; idx < MAXR; ++idx)
  184. {
  185. names[idx] = NULL;
  186. rejections[idx] = 0;
  187. usefulRejections[idx] = 0;
  188. }
  189. maxEver = 0;
  190. }
  191. ++calls;
  192. }
  193. for (PartitionFilter **fp = filters; fp && *fp; fp++)
  194. {
  195. if (!(*fp)->allow(objOther))
  196. {
  197. if (DoFilterProfiling)
  198. {
  199. const char** namesarr = names;
  200. for (idx = 0; idx < maxEver; ++idx)
  201. if (namesarr[idx] == (*fp)->debugGetName())
  202. break;
  203. if (idx == maxEver)
  204. {
  205. namesarr[maxEver++] = (*fp)->debugGetName();
  206. DEBUG_ASSERTCRASH(maxEver<MAXR,("hmm, enlarge this array"));
  207. }
  208. ++rejections[idx];
  209. if (allow)
  210. ++usefulRejections[idx];
  211. allow = false;
  212. if (maxEver < idx)
  213. maxEver = idx;
  214. }
  215. else
  216. {
  217. return false;
  218. }
  219. }
  220. }
  221. if (DoFilterProfiling && calls > 0 && calls % 1000==0)
  222. {
  223. DEBUG_LOG(("\n\n"));
  224. for (idx = 0; idx < maxEver; idx++)
  225. {
  226. DEBUG_LOG(("rejections[%s] = %d (useful = %d)\n",names[idx],rejections[idx],usefulRejections[idx]));
  227. }
  228. }
  229. return allow;
  230. #else
  231. for (PartitionFilter **fp = filters; fp && *fp; fp++)
  232. {
  233. if (!(*fp)->allow(objOther))
  234. {
  235. return false;
  236. }
  237. }
  238. // assume true if no filters rejected it
  239. return true;
  240. #endif
  241. }
  242. //-----------------------------------------------------------------------------
  243. inline void vecDiff_2D(const Coord3D *posA, const Coord3D *posB, Coord3D *resultVec)
  244. {
  245. DEBUG_ASSERTCRASH(posA && posB && resultVec, ("null parm"));
  246. resultVec->x = posA->x - posB->x;
  247. resultVec->y = posA->y - posB->y;
  248. resultVec->z = 0.0f;
  249. }
  250. //-----------------------------------------------------------------------------
  251. inline void vecDiff_3D(const Coord3D *posA, const Coord3D *posB, Coord3D *resultVec)
  252. {
  253. DEBUG_ASSERTCRASH(posA && posB && resultVec, ("null parm"));
  254. resultVec->x = posA->x - posB->x;
  255. resultVec->y = posA->y - posB->y;
  256. resultVec->z = posA->z - posB->z;
  257. }
  258. //-----------------------------------------------------------------------------
  259. inline Real calcSqrDist_2D(const Coord3D *dist)
  260. {
  261. return sqr(dist->x) + sqr(dist->y);
  262. }
  263. //-----------------------------------------------------------------------------
  264. inline Real calcSqrDist_3D(const Coord3D *dist)
  265. {
  266. return sqr(dist->x) + sqr(dist->y) + sqr(dist->z);
  267. }
  268. //-----------------------------------------------------------------------------
  269. inline Int absInt(Int a)
  270. {
  271. if (a < 0) return -a; else return a;
  272. }
  273. //-----------------------------------------------------------------------------
  274. inline Int minInt(Int a, Int b)
  275. {
  276. if (a < b) return a; else return b;
  277. }
  278. //-----------------------------------------------------------------------------
  279. inline Int maxInt(Int a, Int b)
  280. {
  281. if (a > b) return a; else return b;
  282. }
  283. //-----------------------------------------------------------------------------
  284. inline Real minReal(Real a, Real b)
  285. {
  286. if (a < b) return a; else return b;
  287. }
  288. //-----------------------------------------------------------------------------
  289. inline Real maxReal(Real a, Real b)
  290. {
  291. if (a > b) return a; else return b;
  292. }
  293. //-----------------------------------------------------------------------------
  294. // Local Functions
  295. //-----------------------------------------------------------------------------
  296. //-----------------------------------------------------------------------------
  297. static void projectCoord3D(Coord3D *coord, const Coord3D *unitDir, Real dist);
  298. static void flipCoord3D(Coord3D *coord);
  299. static void rectToFourPoints(
  300. const CollideInfo *a, // z is ignored
  301. Coord2D pts[]
  302. );
  303. static void testRotatedPointsAgainstRect(
  304. const Coord2D *pts, // an array of 4
  305. const CollideInfo *a,
  306. Coord2D *avg,
  307. Int *avgTot
  308. );
  309. static Bool xy_collideTest_Rect_Rect(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo);
  310. static Bool xy_collideTest_Rect_Circle(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo);
  311. static Bool xy_collideTest_Circle_Rect(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo);
  312. static Bool xy_collideTest_Circle_Circle(const Coord3D *a_pos, const Coord3D *b_pos, const Real a_radius, const Real b_radius, CollideLocAndNormal *cinfo);
  313. static Bool collideTest_Sphere_Sphere(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo);
  314. static Bool collideTest_Sphere_Cylinder(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo);
  315. static Bool collideTest_Sphere_Box(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo);
  316. static Bool collideTest_Cylinder_Sphere(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo);
  317. static Bool collideTest_Cylinder_Cylinder(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo);
  318. static Bool collideTest_Cylinder_Box(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo);
  319. static Bool collideTest_Box_Sphere(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo);
  320. static Bool collideTest_Box_Cylinder(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo);
  321. static Bool collideTest_Box_Box(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo);
  322. static Bool distCalcProc_CenterAndCenter_2D(const Coord3D *posA, const Object *objA, const Coord3D *posB, const Object *objB, Real& abDistSqr, Coord3D& abVec, Real maxDistSqr);
  323. static Bool distCalcProc_BoundaryAndBoundary_2D(const Coord3D *posA, const Object *objA, const Coord3D *posB, const Object *objB, Real& abDistSqr, Coord3D& abVec, Real maxDistSqr);
  324. static Bool distCalcProc_CenterAndCenter_3D(const Coord3D *posA, const Object *objA, const Coord3D *posB, const Object *objB, Real& abDistSqr, Coord3D& abVec, Real maxDistSqr);
  325. static Bool distCalcProc_BoundaryAndBoundary_3D(const Coord3D *posA, const Object *objA, const Coord3D *posB, const Object *objB, Real& abDistSqr, Coord3D& abVec, Real maxDistSqr);
  326. //-----------------------------------------------------------------------------
  327. inline void projectCoord3D(Coord3D *coord, const Coord3D *unitDir, Real dist)
  328. {
  329. coord->x += unitDir->x * dist;
  330. coord->y += unitDir->y * dist;
  331. coord->z += unitDir->z * dist;
  332. }
  333. //-----------------------------------------------------------------------------
  334. inline void flipCoord3D(Coord3D *coord)
  335. {
  336. coord->x = -coord->x;
  337. coord->y = -coord->y;
  338. coord->z = -coord->z;
  339. }
  340. //-----------------------------------------------------------------------------
  341. static void testRotatedPointsAgainstRect(
  342. const Coord2D *pts, // an array of 4
  343. const CollideInfo *a,
  344. Coord2D *avg,
  345. Int *avgTot
  346. )
  347. {
  348. //DEBUG_ASSERTCRASH(a->geom.getGeomType() == GEOMETRY_BOX, ("only boxes are ok here"));
  349. Real major = a->geom.getMajorRadius();
  350. Real minor = (a->geom.getGeomType() == GEOMETRY_SPHERE) ? a->geom.getMajorRadius() : a->geom.getMinorRadius();
  351. Real c = (Real)Cos(-a->angle);
  352. Real s = (Real)Sin(-a->angle);
  353. for (Int i = 0; i < 4; ++i, ++pts)
  354. {
  355. // convert to a delta relative to rect ctr
  356. Real ptx = pts->x - a->position.x;
  357. Real pty = pts->y - a->position.y;
  358. // inverse-rotate it to the right coord system
  359. Real ptx_new = (Real)fabs(ptx*c - pty*s);
  360. Real pty_new = (Real)fabs(ptx*s + pty*c);
  361. #ifdef INTENSE_DEBUG
  362. Real mag_a = sqr(ptx)+sqr(pty);
  363. Real mag_b = sqr(ptx_new)+sqr(pty_new);
  364. DEBUG_ASSERTCRASH(fabs(mag_a - mag_b) <= 1.0, ("hmm, unlikely"));
  365. #endif
  366. if (ptx_new <= major && pty_new <= minor)
  367. {
  368. avg->x += pts->x;
  369. avg->y += pts->y;
  370. *avgTot += 1;
  371. }
  372. }
  373. }
  374. //-----------------------------------------------------------------------------
  375. static void rectToFourPoints(
  376. const CollideInfo *a, // z is ignored
  377. Coord2D pts[]
  378. )
  379. {
  380. Real c = (Real)Cos(a->angle);
  381. Real s = (Real)Sin(a->angle);
  382. Real exc = a->geom.getMajorRadius()*c;
  383. Real eyc = a->geom.getMinorRadius()*c;
  384. Real exs = a->geom.getMajorRadius()*s;
  385. Real eys = a->geom.getMinorRadius()*s;
  386. // tl
  387. pts[0].x = a->position.x - exc - eys;
  388. pts[0].y = a->position.y + eyc - exs;
  389. // tr
  390. pts[1].x = a->position.x + exc - eys;
  391. pts[1].y = a->position.y + eyc + exs;
  392. // bl
  393. pts[2].x = a->position.x - exc + eys;
  394. pts[2].y = a->position.y - eyc - exs;
  395. // br
  396. pts[3].x = a->position.x + exc + eys;
  397. pts[3].y = a->position.y - eyc + exs;
  398. }
  399. //-----------------------------------------------------------------------------
  400. /**
  401. 2d utility routine for rect/cyl & rect/sphere collisions testing.
  402. */
  403. static Bool xy_collideTest_Circle_Rect(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
  404. {
  405. Bool result = xy_collideTest_Rect_Circle(b, a, cinfo);
  406. if (cinfo)
  407. flipCoord3D(&cinfo->normal);
  408. return result;
  409. }
  410. //-----------------------------------------------------------------------------
  411. /**
  412. 2d utility routine for rect/cyl & rect/sphere collisions testing.
  413. */
  414. static Bool xy_collideTest_Rect_Circle(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
  415. {
  416. #if 1
  417. /// @todo srj -- this is better than the other one, since it actually handles rotated rects,
  418. // but still not as accurate as is could be. (srj)
  419. CollideInfo btmp = *b;
  420. btmp.geom.setMinorRadius(btmp.geom.getMajorRadius());
  421. return xy_collideTest_Rect_Rect(a, &btmp, cinfo);
  422. #else
  423. // note, this actually tests the intersection of the the rect with the circle's
  424. // bounding box. in practice, this is usually good enough, since most of
  425. // our sphere/cyl shapes are small relative to boxes. (in fact, the WWMath
  426. // library takes a similar shortcut when colliding spheres with boxes in 3d.
  427. // so I figured it was probably good enough for us too.)
  428. Real circ_l = b->position.x - b->geom.getMajorRadius();
  429. Real circ_r = b->position.x + b->geom.getMajorRadius();
  430. Real circ_t = b->position.y - b->geom.getMajorRadius();
  431. Real circ_b = b->position.y + b->geom.getMajorRadius();
  432. Real rect_l = a->position.x - a->geom.getMajorRadius();
  433. Real rect_r = a->position.x + a->geom.getMajorRadius();
  434. Real rect_t = a->position.y - a->geom.getMinorRadius();
  435. Real rect_b = a->position.y + a->geom.getMinorRadius();
  436. if (circ_r >= rect_l && circ_l <= rect_r &&
  437. circ_b >= rect_t && circ_t <= rect_b)
  438. {
  439. if (cinfo)
  440. {
  441. vecDiff_2D(&b->position, &a->position, &cinfo->normal);
  442. cinfo->normal.normalize();
  443. cinfo->loc.x = (maxReal(circ_l, rect_l) + minReal(circ_r, rect_r)) * 0.5f;
  444. cinfo->loc.y = (maxReal(circ_t, rect_t) + minReal(circ_b, rect_b)) * 0.5f;
  445. cinfo->loc.z = (a->position.z + b->position.z) * 0.5f;
  446. }
  447. return true;
  448. }
  449. return false;
  450. #endif
  451. }
  452. //-----------------------------------------------------------------------------
  453. /**
  454. 2d utility routine for cyl & sphere collisions testing.
  455. */
  456. static Bool xy_collideTest_Circle_Circle(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
  457. {
  458. Coord3D diff;
  459. vecDiff_2D(&b->position, &a->position, &diff);
  460. Real distSqr = calcSqrDist_2D(&diff);
  461. Real touchingDistSqr = sqr(a->geom.getMajorRadius() + b->geom.getMajorRadius());
  462. if (distSqr <= touchingDistSqr)
  463. {
  464. // calc the approximate location and normal.
  465. if (cinfo)
  466. {
  467. cinfo->normal = diff;
  468. cinfo->normal.normalize();
  469. cinfo->loc = a->position;
  470. projectCoord3D(&cinfo->loc, &cinfo->normal, a->geom.getMajorRadius());
  471. }
  472. return true;
  473. }
  474. return false;
  475. }
  476. //-----------------------------------------------------------------------------
  477. /**
  478. 2d utility routine for rect collisions testing.
  479. */
  480. static Bool xy_collideTest_Rect_Rect(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
  481. {
  482. Coord2D pts[4];
  483. Coord2D avg; avg.x = avg.y = 0.0f;
  484. Int avgTot = 0;
  485. rectToFourPoints(a, pts);
  486. testRotatedPointsAgainstRect(pts, b, &avg, &avgTot);
  487. rectToFourPoints(b, pts);
  488. testRotatedPointsAgainstRect(pts, a, &avg, &avgTot);
  489. if (avgTot > 0)
  490. {
  491. if (cinfo)
  492. {
  493. // this is a little hokey, but generally adequate...
  494. cinfo->loc.x = avg.x / avgTot;
  495. cinfo->loc.y = avg.y / avgTot;
  496. cinfo->loc.z = (a->position.z + b->position.z) * 0.5f;
  497. // yow, what exactly does it mean to find the normal to two *intersecting*
  498. // rects? with "true" physics we could project ahead and determine
  499. // what the collision point would be, which would of course always
  500. // give a perfect, symmetrical normal. however, we almost always determine
  501. // collisions "after the fact" and thus the rects already intersect, meaning
  502. // that it's tricky to find the normal. we could do it the "right" way, which
  503. // would be to back up the objects to find the perfect point (see above), but
  504. // we don't have velocity information at this point, so we can't really
  505. // do that. so, naturally, we provide a (poor) guess by just doing the center-to-center
  506. // normal. this is rarely the correct surface normal for a box-box collision,
  507. // but until I either (a) rewrite everything to have predictive collisions,
  508. // or (b) come up with a better definition of a useful normal in this case,
  509. // I'm not sure we can do a whole lot better... (srj)
  510. vecDiff_2D(&b->position, &a->position, &cinfo->normal);
  511. cinfo->normal.normalize();
  512. }
  513. return true;
  514. }
  515. return false;
  516. }
  517. //-----------------------------------------------------------------------------
  518. inline Bool z_collideTest_Sphere_Nonsphere(CollideTestProc xyproc, const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
  519. {
  520. // case 1: center of sphere is within the nonsphere z-space.
  521. if (a->position.z >= b->position.z && a->position.z <= b->position.z + b->geom.getMaxHeightAbovePosition())
  522. {
  523. if (xyproc(a, b, cinfo))
  524. {
  525. // a side-to-side collision; just need to adjust the z-coord of collideLoc
  526. if (cinfo)
  527. {
  528. cinfo->loc.z = a->position.z;
  529. }
  530. return true;
  531. }
  532. return false;
  533. }
  534. // case 2: center of sphere is above or below nonsphere, but within
  535. // major-radius of top or bottom.
  536. // 2a: sphere is below the nonsphere
  537. Real b_bot = b->position.z;
  538. if (a->position.z < b_bot && a->position.z + a->geom.getMajorRadius() >= b_bot)
  539. {
  540. // find the radius of the slice of the sphere that is at b_bot
  541. CollideInfo amod = *a;
  542. amod.position.z = b_bot;
  543. amod.geom.setMajorRadius((Real)sqrtf(sqr(a->geom.getMajorRadius()) - sqr(b_bot - a->position.z)));
  544. if (xyproc(&amod, b, cinfo))
  545. {
  546. // if you want to have 'end' collisions, you should add something like:
  547. // if (circle a is contained by circle b) normal points straight up;
  548. // but since that's currently never possible (and even if it was, wouldn't matter)
  549. // we don't bother with the calculation.
  550. return true;
  551. }
  552. return false;
  553. }
  554. // 2b: sphere is above the nonsphere
  555. Real b_top = b->position.z + b->geom.getMaxHeightAbovePosition();
  556. if (a->position.z > b_top && a->position.z - a->geom.getMajorRadius() <= b_top)
  557. {
  558. CollideInfo amod = *a;
  559. amod.position.z = b_top;
  560. amod.geom.setMajorRadius((Real)sqrtf(sqr(a->geom.getMajorRadius()) - sqr(a->position.z - b_top)));
  561. if (xyproc(&amod, b, cinfo))
  562. {
  563. // if you want to have 'end' collisions, you should add something like:
  564. // if (circle a is contained by circle b) normal points straight down;
  565. // but since that's currently never possible (and even if it was, wouldn't matter)
  566. // we don't bother with the calculation.
  567. return true;
  568. }
  569. return false;
  570. }
  571. return false;
  572. }
  573. //-----------------------------------------------------------------------------
  574. inline Bool z_collideTest_Nonsphere_Nonsphere(CollideTestProc xyproc, const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
  575. {
  576. // note: we already know that there is a z-intersection (our caller filters that out)
  577. // so we need not recheck that here.
  578. // Preflight for close objects.
  579. Real dSqr = sqr(a->position.x-b->position.x) + sqr(a->position.y-b->position.y);
  580. Real minRadius = a->geom.getMajorRadius();
  581. Real r = a->geom.getMinorRadius();
  582. if (minRadius>r) minRadius = r;
  583. r = b->geom.getMajorRadius();
  584. if (minRadius>r) minRadius = r;
  585. r = b->geom.getMinorRadius();
  586. if (minRadius>r) minRadius = r;
  587. Bool closeEnough = sqr(minRadius) > dSqr;
  588. if (closeEnough || xyproc(a, b, cinfo))
  589. {
  590. // since we allow objects to overlap at times, how do we distinguish
  591. // between a "top-to-bottom" vs "side-to-side" collision?
  592. // currently, we don't: cylindrical shapes currently never collide
  593. // top-to-bottom, so we just declare everything to be side-to-side.
  594. // if this changes, need to smarten this.
  595. // case 1: side-to-side collision
  596. // just need to adjust the z-coord of collideLoc
  597. if (cinfo)
  598. {
  599. if (b->position.z > a->position.z)
  600. cinfo->loc.z = (b->position.z + a->position.z + a->geom.getMaxHeightAbovePosition()) * 0.5f;
  601. else
  602. cinfo->loc.z = (a->position.z + b->position.z + b->geom.getMaxHeightAbovePosition()) * 0.5f;
  603. }
  604. return true;
  605. }
  606. return false;
  607. }
  608. //-----------------------------------------------------------------------------
  609. static Bool collideTest_Sphere_Sphere(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
  610. {
  611. Coord3D diff;
  612. vecDiff_3D(&b->position, &a->position, &diff);
  613. Real distSqr = calcSqrDist_3D(&diff);
  614. Real touchingDistSqr = sqr(a->geom.getMajorRadius() + b->geom.getMajorRadius());
  615. if (distSqr <= touchingDistSqr)
  616. {
  617. if (cinfo)
  618. {
  619. cinfo->normal = diff;
  620. cinfo->normal.normalize();
  621. cinfo->loc = a->position;
  622. projectCoord3D(&cinfo->loc, &cinfo->normal, a->geom.getMajorRadius());
  623. }
  624. return true;
  625. }
  626. return false;
  627. }
  628. //-----------------------------------------------------------------------------
  629. static Bool collideTest_Sphere_Cylinder(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
  630. {
  631. return z_collideTest_Sphere_Nonsphere(xy_collideTest_Circle_Circle, a, b, cinfo);
  632. }
  633. //-----------------------------------------------------------------------------
  634. static Bool collideTest_Sphere_Box(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
  635. {
  636. return z_collideTest_Sphere_Nonsphere(xy_collideTest_Circle_Rect, a, b, cinfo);
  637. }
  638. //-----------------------------------------------------------------------------
  639. static Bool collideTest_Cylinder_Sphere(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
  640. {
  641. Bool result = z_collideTest_Sphere_Nonsphere(xy_collideTest_Circle_Circle, b, a, cinfo);
  642. if (cinfo)
  643. flipCoord3D(&cinfo->normal);
  644. return result;
  645. }
  646. //-----------------------------------------------------------------------------
  647. static Bool collideTest_Cylinder_Cylinder(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
  648. {
  649. return z_collideTest_Nonsphere_Nonsphere(xy_collideTest_Circle_Circle, a, b, cinfo);
  650. }
  651. //-----------------------------------------------------------------------------
  652. static Bool collideTest_Cylinder_Box(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
  653. {
  654. return z_collideTest_Nonsphere_Nonsphere(xy_collideTest_Circle_Rect, a, b, cinfo);
  655. }
  656. //-----------------------------------------------------------------------------
  657. static Bool collideTest_Box_Sphere(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
  658. {
  659. Bool result = z_collideTest_Sphere_Nonsphere(xy_collideTest_Circle_Rect, b, a, cinfo);
  660. if (cinfo)
  661. flipCoord3D(&cinfo->normal);
  662. return result;
  663. }
  664. //-----------------------------------------------------------------------------
  665. static Bool collideTest_Box_Cylinder(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
  666. {
  667. return z_collideTest_Nonsphere_Nonsphere(xy_collideTest_Rect_Circle, a, b, cinfo);
  668. }
  669. //-----------------------------------------------------------------------------
  670. static Bool collideTest_Box_Box(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
  671. {
  672. return z_collideTest_Nonsphere_Nonsphere(xy_collideTest_Rect_Rect, a, b, cinfo);
  673. }
  674. //-----------------------------------------------------------------------------
  675. static Bool distCalcProc_CenterAndCenter_2D(
  676. const Coord3D *posA,
  677. const Object *objA,
  678. const Coord3D *posB,
  679. const Object *objB,
  680. Real& abDistSqr,
  681. Coord3D& abVec,
  682. Real maxDistSqr
  683. )
  684. {
  685. // note that object positions are defined as the bottom center of the geometry,
  686. // thus we must add the radius to the z coord to get the proper center of the bounding sphere.
  687. Coord3D diff;
  688. diff.x = posB->x - posA->x;
  689. diff.y = posB->y - posA->y;
  690. diff.z = 0.0f;
  691. //if (abDistSqr)
  692. {
  693. abDistSqr = sqr(diff.x) + sqr(diff.y);
  694. }
  695. //if (abVec)
  696. {
  697. abVec = diff;
  698. }
  699. return abDistSqr < maxDistSqr;
  700. }
  701. //-----------------------------------------------------------------------------
  702. static Bool distCalcProc_BoundaryAndBoundary_2D(
  703. const Coord3D *posA,
  704. const Object *objA,
  705. const Coord3D *posB,
  706. const Object *objB,
  707. Real& abDistSqr,
  708. Coord3D& abVec,
  709. Real maxDistSqr
  710. )
  711. {
  712. Coord3D diff;
  713. diff.x = posB->x - posA->x;
  714. diff.y = posB->y - posA->y;
  715. diff.z = 0.0f;
  716. Real actualDistSqr = sqr(diff.x) + sqr(diff.y);
  717. Real shrinkFactor = 1.0f;
  718. Real shrunkenDistSqr = actualDistSqr;
  719. Real totalRad = (objA ? objA->getGeometryInfo().getBoundingCircleRadius() : 0.0f) +
  720. (objB ? objB->getGeometryInfo().getBoundingCircleRadius() : 0.0f);
  721. if (totalRad > 0.0f)
  722. {
  723. Real actualDist = sqrtf(actualDistSqr);
  724. Real shrunkenDist = actualDist - totalRad;
  725. if (shrunkenDist <= 0.0f)
  726. {
  727. shrinkFactor = 0.0f;
  728. shrunkenDistSqr = 0.0f; // sorry, distances can't be negative
  729. }
  730. else
  731. {
  732. shrinkFactor = shrunkenDist / actualDist;
  733. shrunkenDistSqr = sqr(shrunkenDist);
  734. }
  735. }
  736. //if (abDistSqr)
  737. {
  738. abDistSqr = shrunkenDistSqr;
  739. }
  740. //if (abVec)
  741. {
  742. DEBUG_ASSERTCRASH(shrinkFactor >= 0.0f && shrinkFactor <= 1.0f, ("Hmm, this should not be possible."));
  743. diff.x *= shrinkFactor;
  744. diff.y *= shrinkFactor;
  745. abVec = diff;
  746. }
  747. return abDistSqr < maxDistSqr;
  748. }
  749. //-----------------------------------------------------------------------------
  750. static Bool distCalcProc_CenterAndCenter_3D(
  751. const Coord3D *posA,
  752. const Object *objA,
  753. const Coord3D *posB,
  754. const Object *objB,
  755. Real& abDistSqr,
  756. Coord3D& abVec,
  757. Real maxDistSqr
  758. )
  759. {
  760. // note that object positions are defined as the bottom center of the geometry,
  761. // thus we must add the radius to the z coord to get the proper center of the bounding sphere.
  762. Coord3D diff;
  763. diff.x = posB->x - posA->x;
  764. diff.y = posB->y - posA->y;
  765. diff.z = posB->z - posA->z;
  766. //if (abDistSqr)
  767. {
  768. abDistSqr = sqr(diff.x) + sqr(diff.y) + sqr(diff.z);
  769. }
  770. //if (abVec)
  771. {
  772. abVec = diff;
  773. }
  774. return abDistSqr < maxDistSqr;
  775. }
  776. //-----------------------------------------------------------------------------
  777. static Bool distCalcProc_BoundaryAndBoundary_3D(
  778. const Coord3D *posA,
  779. const Object *objA,
  780. const Coord3D *posB,
  781. const Object *objB,
  782. Real& abDistSqr,
  783. Coord3D& abVec,
  784. Real maxDistSqr
  785. )
  786. {
  787. const GeometryInfo* geomA = objA ? &objA->getGeometryInfo() : NULL;
  788. const GeometryInfo* geomB = objB ? &objB->getGeometryInfo() : NULL;
  789. // note that object positions are defined as the bottom center of the geometry,
  790. // thus we must add the radius to the z coord to get the proper center of the bounding sphere.
  791. Coord3D diff;
  792. diff.x = posB->x - posA->x;
  793. diff.y = posB->y - posA->y;
  794. diff.z = ((posB->z + (geomB ? geomB->getZDeltaToCenterPosition() : 0.0f))
  795. - (posA->z + (geomA ? geomA->getZDeltaToCenterPosition() : 0.0f)));
  796. Real actualDistSqr = sqr(diff.x) + sqr(diff.y) + sqr(diff.z);
  797. Real shrinkFactor = 1.0f;
  798. Real shrunkenDistSqr = actualDistSqr;
  799. Real totalRad = (geomA?geomA->getBoundingSphereRadius():0) + (geomB?geomB->getBoundingSphereRadius():0);
  800. if (totalRad > 0.0f)
  801. {
  802. Real actualDist = sqrtf(actualDistSqr);
  803. Real shrunkenDist = actualDist - totalRad;
  804. if (shrunkenDist <= 0.0f)
  805. {
  806. shrinkFactor = 0.0f;
  807. shrunkenDistSqr = 0.0f; // sorry, distances can't be negative
  808. }
  809. else
  810. {
  811. shrinkFactor = shrunkenDist / actualDist;
  812. shrunkenDistSqr = sqr(shrunkenDist);
  813. }
  814. }
  815. //if (abDistSqr)
  816. {
  817. abDistSqr = shrunkenDistSqr;
  818. }
  819. //if (abVec)
  820. {
  821. DEBUG_ASSERTCRASH(shrinkFactor >= 0.0f && shrinkFactor <= 1.0f, ("Hmm, this should not be possible."));
  822. diff.x *= shrinkFactor;
  823. diff.y *= shrinkFactor;
  824. diff.z *= shrinkFactor;
  825. abVec = diff;
  826. }
  827. return abDistSqr < maxDistSqr;
  828. }
  829. //-----------------------------------------------------------------------------
  830. // Private Types
  831. //-----------------------------------------------------------------------------
  832. //-----------------------------------------------------------------------------
  833. // Private Data
  834. //-----------------------------------------------------------------------------
  835. static DistCalcProc theDistCalcProcs[] =
  836. {
  837. distCalcProc_CenterAndCenter_2D,
  838. distCalcProc_CenterAndCenter_3D,
  839. distCalcProc_BoundaryAndBoundary_2D,
  840. distCalcProc_BoundaryAndBoundary_3D,
  841. };
  842. // NOTE: This *DEPENDS* on the order of the geometry enum defines
  843. static CollideTestProc theCollideTestProcs[] =
  844. {
  845. collideTest_Sphere_Sphere,
  846. collideTest_Sphere_Cylinder,
  847. collideTest_Sphere_Box,
  848. collideTest_Cylinder_Sphere,
  849. collideTest_Cylinder_Cylinder,
  850. collideTest_Cylinder_Box,
  851. collideTest_Box_Sphere,
  852. collideTest_Box_Cylinder,
  853. collideTest_Box_Box
  854. };
  855. //-----------------------------------------------------------------------------
  856. // Public Data
  857. //-----------------------------------------------------------------------------
  858. PartitionManager *ThePartitionManager = NULL; ///< the object manager singleton
  859. //-----------------------------------------------------------------------------
  860. //-----------------------------------------------------------------------------
  861. //-----------------------------------------------------------------------------
  862. class PartitionContactListNode : public MemoryPoolObject
  863. {
  864. MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(PartitionContactListNode, "PartitionContactListNode" )
  865. public:
  866. PartitionContactListNode* m_nextHash; ///< next node with same hash value
  867. PartitionContactListNode* m_next; ///< next node
  868. PartitionData* m_obj; ///< one object that is possibly colliding
  869. PartitionData* m_other; ///< the other object (or null for collisions with the terrain)
  870. Int m_hashValue;///< index into hash table
  871. };
  872. inline PartitionContactListNode::~PartitionContactListNode() { }
  873. //-----------------------------------------------------------------------------
  874. class PartitionContactList
  875. {
  876. private:
  877. /*
  878. socketcount should be prime (and "not too close to a power of 2) for best results.
  879. if this one isn't large enough, try this website:
  880. http://www.utm.edu/research/primes/lists/small/1000.txt
  881. So how is this chosen? Eh, pretty much based on experimentation.
  882. */
  883. enum { PartitionContactList_SOCKET_COUNT = 5381 };
  884. PartitionContactListNode* m_contactHash[PartitionContactList_SOCKET_COUNT];
  885. PartitionContactListNode* m_contactList;
  886. public:
  887. PartitionContactList()
  888. {
  889. memset(m_contactHash, 0, sizeof(m_contactHash));
  890. m_contactList = NULL;
  891. }
  892. ~PartitionContactList()
  893. {
  894. resetContactList();
  895. }
  896. /**
  897. add a pair of objects to the contact list. If the pair is already
  898. present (in either this order or reverse order), nothing is done.
  899. Note that it is OK for other==null (this indicates a collisions with
  900. the ground) but it is not OK for obj==null.
  901. */
  902. void addToContactList(PartitionData *obj, PartitionData *other);
  903. /**
  904. process all pairs in the contact list: first, determine if they
  905. truly collide, based on their geometry. if so, call the collide
  906. actions for each object in the pair.
  907. */
  908. void processContactList();
  909. /**
  910. discard the contents of the contact list.
  911. */
  912. void resetContactList();
  913. /**
  914. remove any contacts that refer to the given data.
  915. */
  916. void removeSpecificPartitionData(PartitionData* data);
  917. };
  918. //-----------------------------------------------------------------------------
  919. //-----------------------------------------------------------------------------
  920. //-----------------------------------------------------------------------------
  921. #ifdef FASTER_GCO
  922. // nothing
  923. #else
  924. //-----------------------------------------------------------------------------
  925. CellOutwardIterator::CellOutwardIterator(PartitionManager *mgr, Int x, Int y)
  926. {
  927. m_mgr = mgr;
  928. m_cellCenterX = x;
  929. m_cellCenterY = y;
  930. m_cellRadius = 0;
  931. m_maxRadius = maxInt(maxInt(x, mgr->getCellCountX() - x), maxInt(y, mgr->getCellCountY() - y));
  932. m_delta[0] = m_delta[1] = 0;
  933. m_inc = -1;
  934. m_cnt = 0;
  935. m_axis = 1;
  936. m_iter = 1;
  937. m_delta[m_axis] -= m_inc;
  938. }
  939. //-----------------------------------------------------------------------------
  940. CellOutwardIterator::~CellOutwardIterator()
  941. {
  942. }
  943. //-----------------------------------------------------------------------------
  944. PartitionCell *CellOutwardIterator::nextCell(Bool skipEmpties)
  945. {
  946. try_again:
  947. if (m_cellRadius <= m_maxRadius)
  948. {
  949. if (m_iter > 0)
  950. {
  951. m_delta[m_axis] += m_inc;
  952. --m_iter;
  953. PartitionCell *cell = m_mgr->getCellAt(m_cellCenterX + m_delta[0], m_cellCenterY + m_delta[1]);
  954. if (!cell)
  955. goto try_again;
  956. if (skipEmpties && !cell->getFirstCoiInCell())
  957. goto try_again;
  958. return cell;
  959. }
  960. m_axis += 1;
  961. if (m_axis >= 2)
  962. {
  963. m_axis = 0;
  964. m_inc = -m_inc;
  965. m_cnt += 1;
  966. m_cellRadius = maxInt(absInt(m_delta[0]), absInt(m_delta[1]));
  967. }
  968. m_iter = m_cnt;
  969. goto try_again;
  970. }
  971. return NULL;
  972. }
  973. #endif
  974. //-----------------------------------------------------------------------------
  975. //-----------------------------------------------------------------------------
  976. //-----------------------------------------------------------------------------
  977. //-----------------------------------------------------------------------------
  978. CellAndObjectIntersection::CellAndObjectIntersection()
  979. {
  980. m_cell = NULL;
  981. m_module = NULL;
  982. m_prevCoi = NULL;
  983. m_nextCoi = NULL;
  984. }
  985. //-----------------------------------------------------------------------------
  986. CellAndObjectIntersection::~CellAndObjectIntersection()
  987. {
  988. DEBUG_ASSERTCRASH(m_prevCoi == NULL && m_nextCoi == NULL, ("destroying a linked COI"));
  989. DEBUG_ASSERTCRASH(!getModule(), ("destroying an in-use COI"));
  990. }
  991. //-----------------------------------------------------------------------------
  992. void CellAndObjectIntersection::friend_addToCellList(CellAndObjectIntersection **pListHead)
  993. {
  994. DEBUG_ASSERTCRASH(m_prevCoi == NULL && m_nextCoi == NULL && *pListHead != this, ("trying to add a cell to list, but it appears to already be in a list"));
  995. this->m_nextCoi = *pListHead;
  996. if (*pListHead)
  997. (*pListHead)->m_prevCoi = this;
  998. *pListHead = this;
  999. }
  1000. //-----------------------------------------------------------------------------
  1001. void CellAndObjectIntersection::friend_removeFromCellList(CellAndObjectIntersection **pListHead)
  1002. {
  1003. #define DEBUG_ASSERTINLIST(c) \
  1004. DEBUG_ASSERTCRASH((c)->m_prevCoi != NULL || (c)->m_nextCoi != NULL || *pListHead == (c), ("cell is not in list"));
  1005. DEBUG_ASSERTINLIST(this);
  1006. if (this->m_prevCoi)
  1007. {
  1008. DEBUG_ASSERTINLIST(this->m_prevCoi);
  1009. DEBUG_ASSERTCRASH(*pListHead != this, ("bad linkage"));
  1010. this->m_prevCoi->m_nextCoi = this->m_nextCoi;
  1011. }
  1012. else
  1013. {
  1014. DEBUG_ASSERTCRASH(*pListHead == this, ("bad linkage"));
  1015. *pListHead = this->m_nextCoi;
  1016. }
  1017. if (this->m_nextCoi)
  1018. {
  1019. DEBUG_ASSERTINLIST(this->m_nextCoi);
  1020. this->m_nextCoi->m_prevCoi = this->m_prevCoi;
  1021. }
  1022. this->m_prevCoi = NULL;
  1023. this->m_nextCoi = NULL;
  1024. #undef DEBUG_ASSERTINLIST
  1025. }
  1026. //-----------------------------------------------------------------------------
  1027. void CellAndObjectIntersection::addCoverage(PartitionCell *cell, PartitionData *module)
  1028. {
  1029. DEBUG_ASSERTCRASH(m_cell == NULL || m_cell == cell, ("mismatch"));
  1030. DEBUG_ASSERTCRASH(m_module == NULL || m_module == module, ("mismatch"));
  1031. if (m_module != NULL && m_module != module)
  1032. {
  1033. DEBUG_CRASH(("COI already in use by another module!"));
  1034. return;
  1035. }
  1036. if (m_cell == NULL)
  1037. cell->friend_addToCellList(this);
  1038. m_cell = cell;
  1039. m_module = module;
  1040. }
  1041. //-----------------------------------------------------------------------------
  1042. void CellAndObjectIntersection::removeAllCoverage()
  1043. {
  1044. if (m_module == NULL)
  1045. {
  1046. DEBUG_CRASH(("COI not in use"));
  1047. return;
  1048. }
  1049. m_cell->friend_removeFromCellList(this);
  1050. m_cell = NULL;
  1051. m_module = NULL;
  1052. }
  1053. //-----------------------------------------------------------------------------
  1054. //-----------------------------------------------------------------------------
  1055. //-----------------------------------------------------------------------------
  1056. //-----------------------------------------------------------------------------
  1057. PartitionCell::PartitionCell()
  1058. {
  1059. //Added By Sadullah Nader
  1060. //Initializations inserted
  1061. m_cellX = m_cellY = 0;
  1062. //
  1063. m_firstCoiInCell = NULL;
  1064. m_coiCount = 0;
  1065. #ifdef PM_CACHE_TERRAIN_HEIGHT
  1066. m_loTerrainZ = HUGE_DIST; // huge positive
  1067. m_hiTerrainZ = -HUGE_DIST; // huge negative
  1068. #endif
  1069. /*
  1070. You may be asking yourself: why do we model the shroud for all players,
  1071. rather than just the local player? And the answer is: because this allows
  1072. us to checksum these values for net games, to help prevent "shroud cheaters"
  1073. (who use a trainer to disable the shroud on their system).
  1074. */
  1075. for (int i = 0; i < MAX_PLAYER_COUNT; ++i)
  1076. {
  1077. // Default is "passive shroud". 1,0.
  1078. m_shroudLevel[i].m_currentShroud = 1;
  1079. m_shroudLevel[i].m_activeShroudLevel = 0;
  1080. // default cash value is 0
  1081. m_cashValue[i] = 0;
  1082. // default threat value is 0
  1083. m_threatValue[i] = 0;
  1084. }
  1085. }
  1086. //-----------------------------------------------------------------------------
  1087. PartitionCell::~PartitionCell()
  1088. {
  1089. DEBUG_ASSERTCRASH(m_firstCoiInCell == NULL && m_coiCount == 0, ("destroying a nonempty PartitionCell"));
  1090. // but don't destroy the Cois; they don't belong to us
  1091. }
  1092. //-----------------------------------------------------------------------------
  1093. void PartitionCell::invalidateShroudedStatusForAllCois(Int playerIndex)
  1094. {
  1095. for (CellAndObjectIntersection* coi = m_firstCoiInCell; coi; coi = coi->getNextCoi())
  1096. {
  1097. coi->getModule()->invalidateShroudedStatusForPlayer(playerIndex);
  1098. }
  1099. }
  1100. //-----------------------------------------------------------------------------
  1101. void PartitionCell::addLooker(Int playerIndex)
  1102. {
  1103. CellShroudStatus oldShroud = getShroudStatusForPlayer( playerIndex );
  1104. // The decreasing Algorithm: A 1 will go straight to -1, otherwise it just gets decremented
  1105. m_shroudLevel[playerIndex].m_currentShroud = min( m_shroudLevel[playerIndex].m_currentShroud - 1, -1 );
  1106. CellShroudStatus newShroud = getShroudStatusForPlayer( playerIndex );
  1107. // DEBUG_LOG(( "ADD %d, %d. CS = %d, AS = %d for player %d.\n",
  1108. // m_cellX,
  1109. // m_cellY,
  1110. // m_shroudLevel[playerIndex].m_currentShroud,
  1111. // m_shroudLevel[playerIndex].m_activeShroudLevel,
  1112. // playerIndex
  1113. // ));
  1114. if( oldShroud != newShroud )
  1115. {
  1116. // On an edge trigger, tell all objects to think about their shroudedness
  1117. invalidateShroudedStatusForAllCois( playerIndex );
  1118. if( playerIndex == ThePlayerList->getLocalPlayer()->getPlayerIndex() )
  1119. {
  1120. // and if this is the local player, do the Client update.
  1121. TheDisplay->setShroudLevel(m_cellX, m_cellY, newShroud);
  1122. TheRadar->setShroudLevel(m_cellX, m_cellY, newShroud);
  1123. }
  1124. }
  1125. }
  1126. //-----------------------------------------------------------------------------
  1127. void PartitionCell::removeLooker(Int playerIndex)
  1128. {
  1129. CellShroudStatus oldShroud = getShroudStatusForPlayer( playerIndex );
  1130. // the increasing Algorithm: a -1 goes up to min(1,activeLevel), otherwise it just gets incremented
  1131. if( m_shroudLevel[playerIndex].m_currentShroud == -1 )
  1132. m_shroudLevel[playerIndex].m_currentShroud = min( m_shroudLevel[playerIndex].m_activeShroudLevel, (Short)1 );
  1133. else
  1134. {
  1135. DEBUG_ASSERTCRASH( m_shroudLevel[playerIndex].m_currentShroud < 0, ("Someone is RemoveLooker-ing on a cell that is not looked at. This will make a permanent shroud blob.") );
  1136. m_shroudLevel[playerIndex].m_currentShroud++;
  1137. }
  1138. CellShroudStatus newShroud = getShroudStatusForPlayer( playerIndex );
  1139. // DEBUG_LOG(( "REMOVE %d, %d. CS = %d, AS = %d for player %d.\n",
  1140. // m_cellX,
  1141. // m_cellY,
  1142. // m_shroudLevel[playerIndex].m_currentShroud,
  1143. // m_shroudLevel[playerIndex].m_activeShroudLevel,
  1144. // playerIndex
  1145. // ));
  1146. if( oldShroud != newShroud )
  1147. {
  1148. // On an edge trigger, tell all objects to think about their shroudedness
  1149. invalidateShroudedStatusForAllCois( playerIndex );
  1150. if( playerIndex == ThePlayerList->getLocalPlayer()->getPlayerIndex() )
  1151. {
  1152. // and if this is the local player, do the Client update.
  1153. TheDisplay->setShroudLevel(m_cellX, m_cellY, newShroud);
  1154. TheRadar->setShroudLevel(m_cellX, m_cellY, newShroud);
  1155. }
  1156. }
  1157. }
  1158. //-----------------------------------------------------------------------------
  1159. void PartitionCell::addShrouder( Int playerIndex )
  1160. {
  1161. CellShroudStatus oldShroud = getShroudStatusForPlayer( playerIndex );
  1162. // Increasing active shroud: activeLevel gets incremented, and CS is set to 1 if at zero
  1163. // do the algorithm
  1164. m_shroudLevel[playerIndex].m_activeShroudLevel++;
  1165. if( m_shroudLevel[playerIndex].m_currentShroud == 0 )
  1166. {
  1167. m_shroudLevel[playerIndex].m_currentShroud = 1;
  1168. }
  1169. CellShroudStatus newShroud = getShroudStatusForPlayer( playerIndex );
  1170. if( oldShroud != newShroud )
  1171. {
  1172. // On an edge trigger, tell all objects to think about their shroudedness
  1173. invalidateShroudedStatusForAllCois( playerIndex );
  1174. // and update the client if we are on the local player
  1175. if( playerIndex == ThePlayerList->getLocalPlayer()->getPlayerIndex() )
  1176. {
  1177. TheDisplay->setShroudLevel(m_cellX, m_cellY, newShroud);
  1178. TheRadar->setShroudLevel(m_cellX, m_cellY, newShroud);
  1179. }
  1180. }
  1181. }
  1182. //-----------------------------------------------------------------------------
  1183. void PartitionCell::removeShrouder( Int playerIndex )
  1184. {
  1185. // Decreasing active shroud: just decrement activeLevel. This will never result in a client change.
  1186. // Either it was passive shroud and is now active, or it was being looked at and still is.
  1187. m_shroudLevel[playerIndex].m_activeShroudLevel--;
  1188. DEBUG_ASSERTCRASH( m_shroudLevel[playerIndex].m_activeShroudLevel >= 0, ("Shroud generation has gone negative. This can't happen.") );
  1189. }
  1190. //-----------------------------------------------------------------------------
  1191. //Bool PartitionCell::isShroudedForPlayer( Int playerIndex ) const
  1192. //{
  1193. // There isn't an absolute answer. This cell is only shrouded in regards to a person
  1194. // return (m_shroudLevel[playerIndex].m_currentShroud == 1);
  1195. //}
  1196. //-----------------------------------------------------------------------------
  1197. CellShroudStatus PartitionCell::getShroudStatusForPlayer( Int playerIndex ) const
  1198. {
  1199. // There are now three answers, but the question still requires "to whom"
  1200. if( m_shroudLevel[playerIndex].m_currentShroud == 1 )
  1201. return CELLSHROUD_SHROUDED;
  1202. else if( m_shroudLevel[playerIndex].m_currentShroud == 0 )
  1203. return CELLSHROUD_FOGGED;// ie Nobody actively looking
  1204. else
  1205. return CELLSHROUD_CLEAR;
  1206. }
  1207. //-----------------------------------------------------------------------------
  1208. UnsignedInt PartitionCell::getThreatValue( Int playerIndex )
  1209. {
  1210. if (playerIndex >= 0 && playerIndex < MAX_PLAYER_COUNT) {
  1211. return m_threatValue[playerIndex];
  1212. }
  1213. return 0;
  1214. }
  1215. //-----------------------------------------------------------------------------
  1216. void PartitionCell::addThreatValue( Int playerIndex, UnsignedInt threatValue )
  1217. {
  1218. if (playerIndex >= 0 && playerIndex < MAX_PLAYER_COUNT) {
  1219. #ifdef _DEBUG
  1220. UnsignedInt oldThreatVal = m_threatValue[playerIndex];
  1221. DEBUG_ASSERTCRASH(oldThreatVal <= oldThreatVal + threatValue, ("adding new threat value overflowed allotted storage."));
  1222. #endif
  1223. m_threatValue[playerIndex] += threatValue;
  1224. }
  1225. }
  1226. //-----------------------------------------------------------------------------
  1227. void PartitionCell::removeThreatValue( Int playerIndex, UnsignedInt threatValue )
  1228. {
  1229. if (playerIndex >= 0 && playerIndex < MAX_PLAYER_COUNT) {
  1230. #ifdef _DEBUG
  1231. UnsignedInt oldThreatVal = m_threatValue[playerIndex];
  1232. DEBUG_ASSERTCRASH(oldThreatVal >= oldThreatVal - threatValue, ("removing new threat value underflowed allotted storage."));
  1233. #endif
  1234. m_threatValue[playerIndex] -= threatValue;
  1235. }
  1236. }
  1237. //-----------------------------------------------------------------------------
  1238. UnsignedInt PartitionCell::getCashValue( Int playerIndex )
  1239. {
  1240. if (playerIndex >= 0 && playerIndex < MAX_PLAYER_COUNT) {
  1241. return m_cashValue[playerIndex];
  1242. }
  1243. return 0;
  1244. }
  1245. //-----------------------------------------------------------------------------
  1246. void PartitionCell::addCashValue( Int playerIndex, UnsignedInt cashValue )
  1247. {
  1248. if (playerIndex >= 0 && playerIndex < MAX_PLAYER_COUNT) {
  1249. #ifdef _DEBUG
  1250. UnsignedInt oldCashVal = m_cashValue[playerIndex];
  1251. DEBUG_ASSERTCRASH(oldCashVal <= oldCashVal + cashValue, ("adding new cash value overflowed allotted storage."));
  1252. #endif
  1253. m_cashValue[playerIndex] += cashValue;
  1254. }
  1255. }
  1256. //-----------------------------------------------------------------------------
  1257. void PartitionCell::removeCashValue( Int playerIndex, UnsignedInt cashValue )
  1258. {
  1259. if (playerIndex >= 0 && playerIndex < MAX_PLAYER_COUNT) {
  1260. #ifdef _DEBUG
  1261. UnsignedInt oldCashVal = m_cashValue[playerIndex];
  1262. DEBUG_ASSERTCRASH(oldCashVal >= oldCashVal - cashValue, ("removing new cash value underflowed allotted storage."));
  1263. #endif
  1264. m_cashValue[playerIndex] -= cashValue;
  1265. }
  1266. }
  1267. //-----------------------------------------------------------------------------
  1268. void PartitionCell::friend_addToCellList(CellAndObjectIntersection *coi)
  1269. {
  1270. if (coi)
  1271. {
  1272. coi->friend_addToCellList(&m_firstCoiInCell);
  1273. ++m_coiCount;
  1274. }
  1275. }
  1276. //-----------------------------------------------------------------------------
  1277. void PartitionCell::friend_removeFromCellList(CellAndObjectIntersection *coi)
  1278. {
  1279. if (coi)
  1280. {
  1281. coi->friend_removeFromCellList(&m_firstCoiInCell);
  1282. --m_coiCount;
  1283. }
  1284. }
  1285. //-----------------------------------------------------------------------------
  1286. void PartitionCell::getCellCenterPos(Real& x, Real& y)
  1287. {
  1288. ThePartitionManager->getCellCenterPos(m_cellX, m_cellY, x, y);
  1289. }
  1290. //-----------------------------------------------------------------------------
  1291. #ifdef _DEBUG
  1292. void PartitionCell::validateCoiList()
  1293. {
  1294. CellAndObjectIntersection *nextCoi = 0, *prevCoi = 0;
  1295. for (CellAndObjectIntersection *coi = getFirstCoiInCell(); coi; prevCoi = coi, coi = nextCoi)
  1296. {
  1297. nextCoi = coi->getNextCoi();
  1298. DEBUG_ASSERTCRASH(coi->getPrevCoi() == prevCoi, ("coi link mismatch"));
  1299. DEBUG_ASSERTCRASH(prevCoi == NULL || prevCoi->getNextCoi() == coi, ("coi link mismatch"));
  1300. DEBUG_ASSERTCRASH((coi == getFirstCoiInCell()) == (prevCoi == NULL) , ("coi link mismatch"));
  1301. DEBUG_ASSERTCRASH(nextCoi == NULL || nextCoi->getPrevCoi() == coi, ("coi link mismatch"));
  1302. }
  1303. }
  1304. #endif
  1305. // ------------------------------------------------------------------------------------------------
  1306. /** CRC */
  1307. // ------------------------------------------------------------------------------------------------
  1308. void PartitionCell::crc( Xfer *xfer )
  1309. {
  1310. xfer->xferUser(&m_shroudLevel, sizeof(ShroudLevel) * MAX_PLAYER_COUNT);
  1311. xfer->xferUser(&m_cellX, sizeof(m_cellX));
  1312. xfer->xferUser(&m_cellY, sizeof(m_cellY));
  1313. } // end crc
  1314. // ------------------------------------------------------------------------------------------------
  1315. /** Xfer Method */
  1316. // ------------------------------------------------------------------------------------------------
  1317. void PartitionCell::xfer( Xfer *xfer )
  1318. {
  1319. // version
  1320. XferVersion currentVersion = 1;
  1321. XferVersion version = currentVersion;
  1322. xfer->xferVersion( &version, currentVersion );
  1323. // xfer shroud data
  1324. xfer->xferUser( &m_shroudLevel, sizeof( ShroudLevel ) * MAX_PLAYER_COUNT );
  1325. } // end xfer
  1326. // ------------------------------------------------------------------------------------------------
  1327. /** Load post process */
  1328. // ------------------------------------------------------------------------------------------------
  1329. void PartitionCell::loadPostProcess( void )
  1330. {
  1331. } // end loadPostProcess
  1332. //-----------------------------------------------------------------------------
  1333. //-----------------------------------------------------------------------------
  1334. //-----------------------------------------------------------------------------
  1335. //-----------------------------------------------------------------------------
  1336. PartitionData::PartitionData()
  1337. {
  1338. //DEBUG_LOG(("create pd %08lx\n",this));
  1339. m_next = NULL;
  1340. m_prev = NULL;
  1341. m_nextDirty = NULL;
  1342. m_prevDirty = NULL;
  1343. m_object = NULL;
  1344. m_ghostObject = NULL;
  1345. m_coiArrayCount = 0;
  1346. m_coiArray = NULL;
  1347. m_coiInUseCount = 0;
  1348. m_doneFlag = 0;
  1349. m_dirtyStatus = NOT_DIRTY;
  1350. m_lastCell = NULL;
  1351. for (int i = 0; i < MAX_PLAYER_COUNT; ++i)
  1352. {
  1353. m_everSeenByPlayer[i] = false;
  1354. m_shroudedness[i] = OBJECTSHROUD_INVALID;
  1355. m_shroudednessPrevious[i] = OBJECTSHROUD_INVALID;
  1356. }
  1357. }
  1358. //-----------------------------------------------------------------------------
  1359. PartitionData::~PartitionData()
  1360. {
  1361. //DEBUG_LOG(("toss pd for pd %08lx obj %08lx\n",this,m_object));
  1362. removeAllTouchedCells();
  1363. freeCoiArray();
  1364. DEBUG_ASSERTCRASH(ThePartitionManager, ("ThePartitionManager is null"));
  1365. if (ThePartitionManager && ThePartitionManager->isInListDirtyModules(this))
  1366. {
  1367. //DEBUG_LOG(("remove pd %08lx from dirty list (%08lx %08lx)\n",this,m_prevDirty,m_nextDirty));
  1368. ThePartitionManager->removeFromDirtyModules(this);
  1369. //DEBUG_ASSERTCRASH(!ThePartitionManager->isInListDirtyModules(this), ("hmm\n"));
  1370. }
  1371. }
  1372. //-----------------------------------------------------------------------------
  1373. Int PartitionData::getControllingPlayerIndex() const
  1374. {
  1375. const Player* p = getObject()->getControllingPlayer();
  1376. if (p)
  1377. {
  1378. Int playerIndex = p->getPlayerIndex();
  1379. DEBUG_ASSERTCRASH(playerIndex >= 0 && playerIndex < MAX_PLAYER_COUNT, ("bad playerIndex"));
  1380. return playerIndex;
  1381. }
  1382. DEBUG_CRASH(("this should never happen"));
  1383. throw ERROR_BUG;
  1384. return 0;
  1385. }
  1386. //-----------------------------------------------------------------------------
  1387. // only used to restore state after map border resizing and/or xfer!
  1388. void PartitionData::friend_setShroudednessPrevious(Int playerIndex, ObjectShroudStatus status)
  1389. {
  1390. m_shroudednessPrevious[playerIndex] = status;
  1391. // if our status is anything but shrouded, assume we've never been seen by the player.
  1392. m_everSeenByPlayer[playerIndex] = (status == OBJECTSHROUD_SHROUDED) ? false : true;
  1393. // if we're already invalid, mark us as special-invalid.
  1394. #ifndef DISABLE_INVALID_PREVENTION
  1395. if (m_shroudedness[playerIndex] == OBJECTSHROUD_INVALID)
  1396. m_shroudedness[playerIndex] = OBJECTSHROUD_INVALID_BUT_PREVIOUS_VALID;
  1397. #endif
  1398. }
  1399. //-----------------------------------------------------------------------------
  1400. ObjectShroudStatus PartitionData::getShroudedStatus(Int playerIndex)
  1401. {
  1402. // sanity
  1403. DEBUG_ASSERTCRASH( playerIndex >= 0 && playerIndex < MAX_PLAYER_COUNT,
  1404. ("PartitionData::getShroudedStatus - Invalid player index '%d'\n", playerIndex) );
  1405. if (!ThePartitionManager->getUpdatedSinceLastReset())
  1406. {
  1407. // The shroud should be invalid until update has been called.
  1408. // I'll set myself as invalid so the next time you ask I'll recompute. (If I just return Invalid, then the old state will persist unfairly over a reset)
  1409. invalidateShroudedStatusForPlayer(playerIndex);
  1410. return m_shroudedness[playerIndex];
  1411. }
  1412. #ifndef DISABLE_INVALID_PREVENTION
  1413. if (m_shroudedness[playerIndex] == OBJECTSHROUD_INVALID || m_shroudedness[playerIndex] == OBJECTSHROUD_INVALID_BUT_PREVIOUS_VALID)
  1414. {
  1415. Bool updateShroudednessPrevious = (m_shroudedness[playerIndex] != OBJECTSHROUD_INVALID_BUT_PREVIOUS_VALID);
  1416. #else
  1417. if (m_shroudedness[playerIndex] == OBJECTSHROUD_INVALID)
  1418. {
  1419. #endif
  1420. Int shroudedCells = 0;
  1421. Int foggedCells = 0;
  1422. CellAndObjectIntersection* coi = m_coiArray;
  1423. for (Int i = m_coiInUseCount; i; --i, ++coi)
  1424. {
  1425. if( coi->getCell()->getShroudStatusForPlayer(playerIndex) == CELLSHROUD_SHROUDED )
  1426. ++shroudedCells;
  1427. else if( coi->getCell()->getShroudStatusForPlayer(playerIndex) == CELLSHROUD_FOGGED )
  1428. ++foggedCells;
  1429. }
  1430. if( m_coiInUseCount == 0 )
  1431. { m_shroudedness[playerIndex] = OBJECTSHROUD_SHROUDED; // Stuff off the map have no COIs since they aren't in the PartitionCell range
  1432. m_everSeenByPlayer[playerIndex] = false; //force object as never seen by the player
  1433. }
  1434. else if( shroudedCells == m_coiInUseCount )
  1435. { m_shroudedness[playerIndex] = OBJECTSHROUD_SHROUDED; // every cell I use is shrouded
  1436. m_everSeenByPlayer[playerIndex] = false; //force object as never seen by the player
  1437. if (m_ghostObject && m_shroudednessPrevious[playerIndex] == OBJECTSHROUD_FOGGED)
  1438. { //we are shrouding an area that used to be fogged so release our memory of what was there.
  1439. m_ghostObject->freeSnapShot(playerIndex);
  1440. }
  1441. }
  1442. else if( shroudedCells + foggedCells == m_coiInUseCount )
  1443. {
  1444. m_shroudedness[playerIndex] = OBJECTSHROUD_FOGGED; //object is visible but fogged.
  1445. if (m_object && m_ghostObject) //object does not exist for modules holding only GhostObjects
  1446. {
  1447. //fogged but may not be visible if faction unit or faction building that has not been seen before
  1448. Player *player=ThePlayerList->getNthPlayer(playerIndex);
  1449. if (player->getRelationship(m_object->getTeam()) == NEUTRAL)
  1450. { //anything neutral that moves around will not be rendered inside fog.
  1451. if (!m_object->isKindOf(KINDOF_IMMOBILE))
  1452. m_shroudedness[playerIndex] = OBJECTSHROUD_SHROUDED;
  1453. }
  1454. else //Not neutral
  1455. { //enemy unit will always be shrouded unless it's a building that's already been seen by the player. Fogged Mines are also always
  1456. //shroued no matter what.
  1457. if (!(m_object->isKindOf(KINDOF_IMMOBILE) && m_everSeenByPlayer[playerIndex]) || m_object->isKindOf(KINDOF_MINE))
  1458. m_shroudedness[playerIndex] = OBJECTSHROUD_SHROUDED;
  1459. }
  1460. if (m_shroudedness[playerIndex] == OBJECTSHROUD_FOGGED)
  1461. { //successfully applied fog to object so check if we need to freeze it's state
  1462. if (m_shroudednessPrevious[playerIndex] < OBJECTSHROUD_FOGGED)
  1463. { //object was not previously fogged but now is fogged.
  1464. //need to record its current state so that it doesn't change
  1465. //while fogged.
  1466. m_ghostObject->snapShot(playerIndex);
  1467. }
  1468. }
  1469. }
  1470. }
  1471. else if( shroudedCells == 0 && foggedCells == 0 )
  1472. { //Record that this object was seen by the player. This info will be used to show fogged enemy faction buildings.
  1473. m_everSeenByPlayer[playerIndex] = true;
  1474. m_shroudedness[playerIndex] = OBJECTSHROUD_CLEAR;
  1475. if (m_ghostObject && m_shroudednessPrevious[playerIndex] == OBJECTSHROUD_FOGGED)
  1476. { //object was previously fogged but now is visible so we no longer
  1477. //need a ghost object.
  1478. m_ghostObject->freeSnapShot(playerIndex);
  1479. }
  1480. } // no cell I use has anything
  1481. else
  1482. { //Record that this object was seen by the player. This info will be used to show fogged enemy faction buildings.
  1483. m_everSeenByPlayer[playerIndex] = true;
  1484. m_shroudedness[playerIndex] = OBJECTSHROUD_PARTIAL_CLEAR; // I am at least partially clear otherwise
  1485. if (m_ghostObject && m_shroudednessPrevious[playerIndex] == OBJECTSHROUD_FOGGED)
  1486. { //object was previously fogged but now is visible so we no longer
  1487. //need a ghost object.
  1488. m_ghostObject->freeSnapShot(playerIndex);
  1489. }
  1490. }
  1491. #ifndef DISABLE_INVALID_PREVENTION
  1492. if (m_coiInUseCount && updateShroudednessPrevious)
  1493. {
  1494. #else
  1495. if (m_coiInUseCount)
  1496. {
  1497. #endif
  1498. //only remember the previous state of objects actually on the map.
  1499. m_shroudednessPrevious[playerIndex] = m_shroudedness[playerIndex];
  1500. }
  1501. }
  1502. return m_shroudedness[playerIndex];
  1503. }
  1504. //-----------------------------------------------------------------------------
  1505. void PartitionData::removeAllTouchedCells()
  1506. {
  1507. CellAndObjectIntersection *coi = m_coiArray;
  1508. for (Int i = m_coiArrayCount; i > 0; --i, ++coi)
  1509. {
  1510. if (coi->getModule())
  1511. {
  1512. DEBUG_ASSERTCRASH(coi->getModule() == this, ("coi uses wrong module"));
  1513. coi->removeAllCoverage();
  1514. DEBUG_ASSERTCRASH(!coi->getModule(), ("coi should no longer be in use"));
  1515. --m_coiInUseCount;
  1516. }
  1517. }
  1518. DEBUG_ASSERTCRASH(m_coiInUseCount == 0, ("hmm, coi count mismatch"));
  1519. }
  1520. // -----------------------------------------------------------------------------
  1521. void PartitionData::addSubPixToCoverage(PartitionCell *cell)
  1522. {
  1523. DEBUG_ASSERTCRASH(m_coiInUseCount < m_coiArrayCount, ("not enough cois allocated for this object"));
  1524. if (cell)
  1525. {
  1526. // see if we already have a coi for this cell.
  1527. CellAndObjectIntersection *coi = m_coiArray;
  1528. CellAndObjectIntersection *coiToUse = NULL;
  1529. for (Int i = __min(m_coiInUseCount,m_coiArrayCount); i; --i, ++coi)
  1530. {
  1531. if (coi->getCell() == cell)
  1532. {
  1533. coiToUse = coi;
  1534. break;
  1535. }
  1536. }
  1537. DEBUG_ASSERTCRASH(coiToUse != NULL || m_coiInUseCount < m_coiArrayCount, ("not enough cois allocated for this object"));
  1538. if (coiToUse == NULL && m_coiInUseCount < m_coiArrayCount)
  1539. {
  1540. // nope, no coi for this cell, allocate a new one
  1541. coiToUse = &m_coiArray[m_coiInUseCount++];
  1542. }
  1543. if (coiToUse)
  1544. {
  1545. coiToUse->addCoverage(cell, this);
  1546. }
  1547. }
  1548. }
  1549. // -----------------------------------------------------------------------------
  1550. void PartitionData::doRectFill(
  1551. Real centerX,
  1552. Real centerY,
  1553. Real halfsizeX,
  1554. Real halfsizeY,
  1555. Real angle
  1556. )
  1557. {
  1558. Real c = (Real)Cos(angle);
  1559. Real s = (Real)Sin(angle);
  1560. Real actualCellSize = ThePartitionManager->getCellSize();
  1561. Real stepSize = actualCellSize * 0.5f; // in theory, should be getCellSize() exactly, but needs to be smaller to avoid aliasing problems
  1562. Real ydx = s * stepSize;
  1563. Real ydy = -c * stepSize;
  1564. Real xdx = c * stepSize;
  1565. Real xdy = s * stepSize;
  1566. // GS 12-26-02 Oh my. The step size has been fudged down to ensure that a cell is not jumped over,
  1567. // but then the original step size was used to determine the number of steps. This error gets
  1568. // more pronounced on bigger buildings. ie a 100 X 160 registers cells for a 80 x 100. If you
  1569. // fudge one, you have to fudge the other.
  1570. // Real stepSizeInvTimes2 = 2.0f * ThePartitionManager->getCellSizeInv();
  1571. Real stepSizeInvTimes2 = 2.0f * (1.0f / stepSize);
  1572. /*
  1573. srj fixes subtle bug: need +1 to get all of misaligned geometries accted for.
  1574. */
  1575. // Int numStepsX = REAL_TO_INT_CEIL(halfsizeX * stepSizeInvTimes2) + 1;
  1576. // Int numStepsY = REAL_TO_INT_CEIL(halfsizeY * stepSizeInvTimes2) + 1;
  1577. Int numStepsX = REAL_TO_INT_CEIL(halfsizeX * stepSizeInvTimes2);
  1578. Int numStepsY = REAL_TO_INT_CEIL(halfsizeY * stepSizeInvTimes2);
  1579. // GS 12-26-02 Oh my part 2. Now that we are no longer greatly underestimating our COI imprint,
  1580. // we don't need this fudge. The fudge is not in the calcMaxCOIForShape, so it just results in
  1581. // good COIs getting dropped since we didn't allocate enough to handle this fudge.
  1582. Real tl_x = centerX - halfsizeX*c - halfsizeY*s;
  1583. Real tl_y = centerY + halfsizeY*c - halfsizeX*s;
  1584. for (Int iy = 0; iy < numStepsY; ++iy, tl_x += ydx, tl_y += ydy)
  1585. {
  1586. Real x = tl_x;
  1587. Real y = tl_y;
  1588. for (Int ix = 0; ix < numStepsX; ++ix, x += xdx, y += xdy)
  1589. {
  1590. Int cellx, celly;
  1591. ThePartitionManager->worldToCell(x, y, &cellx, &celly);
  1592. PartitionCell *cell = ThePartitionManager->getCellAt(cellx, celly); // might be null if off the edge
  1593. if (cell)
  1594. {
  1595. addSubPixToCoverage(cell);
  1596. }
  1597. }
  1598. }
  1599. }
  1600. // -----------------------------------------------------------------------------
  1601. void PartitionData::hLineCircle(Int x1, Int x2, Int y)
  1602. {
  1603. for (Int x = x1; x <= x2; ++x)
  1604. {
  1605. PartitionCell* cell = ThePartitionManager->getCellAt(x, y);
  1606. if (cell)
  1607. {
  1608. addSubPixToCoverage(cell);
  1609. }
  1610. }
  1611. }
  1612. // -----------------------------------------------------------------------------
  1613. void PartitionData::doCircleFill(
  1614. Real centerX,
  1615. Real centerY,
  1616. Real radius
  1617. )
  1618. {
  1619. DEBUG_ASSERTCRASH(m_coiInUseCount == 0, ("expected no coi in use here"));
  1620. Int cellCenterX, cellCenterY;
  1621. ThePartitionManager->worldToCell(centerX, centerY, &cellCenterX, &cellCenterY);
  1622. Int cellRadius = ThePartitionManager->worldToCellDist(radius);
  1623. if (cellRadius < 1)
  1624. cellRadius = 1;
  1625. Int y = cellRadius - 1;
  1626. Int dec = 3 - 2*cellRadius;
  1627. for (Int x = 0; x < cellRadius; x++)
  1628. {
  1629. hLineCircle(cellCenterX - x, cellCenterX + x, cellCenterY + y);
  1630. hLineCircle(cellCenterX - x, cellCenterX + x, cellCenterY - y);
  1631. hLineCircle(cellCenterX - y, cellCenterX + y, cellCenterY + x);
  1632. hLineCircle(cellCenterX - y, cellCenterX + y, cellCenterY - x);
  1633. if (dec >= 0)
  1634. {
  1635. dec += (1 - y) << 2;
  1636. --y;
  1637. }
  1638. dec += (x << 2) + 6;
  1639. }
  1640. }
  1641. // -----------------------------------------------------------------------------
  1642. void PartitionData::doSmallFill(
  1643. Real centerX,
  1644. Real centerY,
  1645. Real radius
  1646. )
  1647. {
  1648. DEBUG_ASSERTCRASH(m_coiInUseCount == 0, ("expected no coi in use here"));
  1649. Real halfCellSize = ThePartitionManager->getCellSize() * 0.5f;
  1650. if (radius > halfCellSize)
  1651. {
  1652. DEBUG_CRASH(("object is too large to use a 'small' geometry, truncating size to cellsize\n"));
  1653. radius = halfCellSize;
  1654. }
  1655. Int cx1, cy1, cx2, cy2;
  1656. ThePartitionManager->worldToCell(centerX - radius, centerY - radius, &cx1, &cy1);
  1657. ThePartitionManager->worldToCell(centerX + radius, centerY + radius, &cx2, &cy2);
  1658. DEBUG_ASSERTCRASH(absInt(cx2-cx1)<=1,("bad cx"));
  1659. DEBUG_ASSERTCRASH(absInt(cy2-cy1)<=1,("bad cy"));
  1660. for (Int x = cx1; x <= cx2; x++)
  1661. {
  1662. for (Int y = cy1; y <= cy2; y++)
  1663. {
  1664. PartitionCell *cell = ThePartitionManager->getCellAt(x, y);
  1665. if (cell)
  1666. {
  1667. m_coiArray[m_coiInUseCount++].addCoverage(cell, this);
  1668. }
  1669. }
  1670. }
  1671. #ifdef INTENSE_DEBUG
  1672. for (int i = 0; i < m_coiInUseCount; i++)
  1673. {
  1674. for (int j = 0; j < i; j++)
  1675. {
  1676. DEBUG_ASSERTCRASH(m_coiArray[i].getCell() != m_coiArray[j].getCell(), ("dup cells"));
  1677. }
  1678. }
  1679. #endif
  1680. }
  1681. //-----------------------------------------------------------------------------
  1682. void PartitionData::addPossibleCollisions(PartitionContactList *ctList)
  1683. {
  1684. // actually, we do occasionally want to detect collisions of dead AIs.
  1685. // e.g., dead technicals flying thru the air should check for collisions with
  1686. // immobile structures, because otherwise they pass thru them, which looks dopey.
  1687. // if preventing dead-ai-collisions is necessary to fix something else, please
  1688. // ensure that the above case is cool before signing off. (srj)
  1689. #ifdef NOPE_READ_THE_COMMENT
  1690. // dead objects never collide with things...
  1691. AIUpdateInterface *ai = getObject()->getAIUpdateInterface();
  1692. if (ai && ai->isAiInDeadState())
  1693. {
  1694. return;
  1695. }
  1696. #endif
  1697. //DEBUG_LOG(("adding possible collision for %s\n",getObject()->getTemplate()->getName().str()));
  1698. CellAndObjectIntersection *myCoi = m_coiArray;
  1699. for (Int i = m_coiInUseCount; i > 0; --i, ++myCoi)
  1700. {
  1701. PartitionCell *cell = myCoi->getCell();
  1702. if (cell->getCoiCount() < 2)
  1703. continue;
  1704. for (CellAndObjectIntersection *coi = cell->getFirstCoiInCell(); coi; coi = coi->getNextCoi())
  1705. {
  1706. PartitionData *that = coi->getModule();
  1707. if (this != that)
  1708. {
  1709. ctList->addToContactList(this, that);
  1710. }
  1711. }
  1712. }
  1713. }
  1714. //-----------------------------------------------------------------------------
  1715. Bool PartitionData::collidesWith(const PartitionData *that, CollideLocAndNormal *cinfo) const
  1716. {
  1717. const Object *thisObj = this->getObject();
  1718. const Object *thatObj = that->getObject();
  1719. if( thisObj->isKindOf( KINDOF_NO_COLLIDE ) || thatObj->isKindOf( KINDOF_NO_COLLIDE ) )
  1720. return FALSE; // A collision extent of zero size is still a point and can collide, but we don't always want to.
  1721. CollideInfo thisInfo(thisObj->getPosition(), thisObj->getGeometryInfo(), thisObj->getOrientation());
  1722. CollideInfo thatInfo(thatObj->getPosition(), thatObj->getGeometryInfo(), thatObj->getOrientation());
  1723. // invariant for all geometries: first do z collision check.
  1724. Real thisTop = thisInfo.position.z + thisInfo.geom.getMaxHeightAbovePosition();
  1725. Real thisBot = thisInfo.position.z - thisInfo.geom.getMaxHeightBelowPosition();
  1726. Real thatTop = thatInfo.position.z + thatInfo.geom.getMaxHeightAbovePosition();
  1727. Real thatBot = thatInfo.position.z - thatInfo.geom.getMaxHeightBelowPosition();
  1728. if (thisTop >= thatBot && thisBot <= thatTop)
  1729. {
  1730. GeometryType thisGeom = thisObj->getGeometryInfo().getGeomType();
  1731. GeometryType thatGeom = thatObj->getGeometryInfo().getGeomType();
  1732. //
  1733. // NOTE: This assumes geometry enumerations that start at GEOMETRY_FIRST AND depends on the
  1734. // order in which they appear in the enum list
  1735. //
  1736. CollideTestProc collideProc = theCollideTestProcs[ (thisGeom - GEOMETRY_FIRST) * GEOMETRY_NUM_TYPES + (thatGeom - GEOMETRY_FIRST) ];
  1737. return (*collideProc)(&thisInfo, &thatInfo, cinfo);
  1738. }
  1739. else
  1740. {
  1741. // no z-intersection -> no collision.
  1742. return false;
  1743. }
  1744. }
  1745. //-----------------------------------------------------------------------------
  1746. /* See if thisObj collides with geom at pos & angle. */
  1747. Bool PartitionManager::geomCollidesWithGeom(const Coord3D* pos1,
  1748. const GeometryInfo& geom1,
  1749. Real angle1,
  1750. const Coord3D* pos2,
  1751. const GeometryInfo& geom2,
  1752. Real angle2) const
  1753. {
  1754. CollideInfo thisInfo(pos1, geom1, angle1);
  1755. CollideInfo thatInfo(pos2, geom2, angle2);
  1756. // invariant for all geometries: first do z collision check.
  1757. if (thisInfo.position.z + thisInfo.geom.getMaxHeightAbovePosition() >= thatInfo.position.z &&
  1758. thisInfo.position.z <= thatInfo.position.z + thatInfo.geom.getMaxHeightAbovePosition())
  1759. {
  1760. GeometryType thisGeom = geom1.getGeomType();
  1761. GeometryType thatGeom = geom2.getGeomType();
  1762. //
  1763. // NOTE: This assumes geometry enumerations that start at GEOMETRY_FIRST AND depends on the
  1764. // order in which they appear in the enum list
  1765. //
  1766. CollideTestProc collideProc = theCollideTestProcs[ (thisGeom - GEOMETRY_FIRST) * GEOMETRY_NUM_TYPES + (thatGeom - GEOMETRY_FIRST) ];
  1767. CollideLocAndNormal cloc;
  1768. return (*collideProc)(&thisInfo, &thatInfo, &cloc);
  1769. }
  1770. else
  1771. {
  1772. // no z-intersection -> no collision.
  1773. return false;
  1774. }
  1775. }
  1776. //-----------------------------------------------------------------------------
  1777. void PartitionData::updateCellsTouched()
  1778. {
  1779. GeometryType geom;
  1780. Bool isSmall;
  1781. Coord3D pos;
  1782. Real angle,majorRadius,minorRadius;
  1783. Object *obj = getObject();
  1784. DEBUG_ASSERTCRASH(obj != NULL || m_ghostObject != NULL, ("must be attached to an Object here 1"));
  1785. if (obj)
  1786. {
  1787. //we have no object using this PartitionData but we still have a GhostObject so copy its data.
  1788. geom = obj->getGeometryInfo().getGeomType();
  1789. isSmall = obj->getGeometryInfo().getIsSmall();
  1790. pos = *(obj->getPosition());
  1791. angle = obj->getOrientation();
  1792. majorRadius = obj->getGeometryInfo().getMajorRadius();
  1793. minorRadius = obj->getGeometryInfo().getMinorRadius();
  1794. }
  1795. else if (m_ghostObject)
  1796. {
  1797. geom = m_ghostObject->getGeometryType();
  1798. isSmall = m_ghostObject->getGeometrySmall();
  1799. pos = *m_ghostObject->getParentPosition();
  1800. angle = m_ghostObject->getParentAngle();
  1801. majorRadius = m_ghostObject->getGeometryMajorRadius();
  1802. minorRadius = m_ghostObject->getGeometryMinorRadius();
  1803. }
  1804. removeAllTouchedCells();
  1805. if (isSmall)
  1806. {
  1807. doSmallFill(pos.x, pos.y, majorRadius);
  1808. }
  1809. else
  1810. {
  1811. switch(geom)
  1812. {
  1813. case GEOMETRY_SPHERE:
  1814. case GEOMETRY_CYLINDER:
  1815. {
  1816. doCircleFill(pos.x, pos.y, majorRadius);
  1817. break;
  1818. }
  1819. case GEOMETRY_BOX:
  1820. {
  1821. doRectFill(pos.x, pos.y, majorRadius, minorRadius, angle);
  1822. break;
  1823. }
  1824. };
  1825. }
  1826. Int currentCellIndexX, currentCellIndexY;
  1827. ThePartitionManager->worldToCell( pos.x, pos.y, &currentCellIndexX, &currentCellIndexY );
  1828. const PartitionCell *currentCell = ThePartitionManager->getCellAt( currentCellIndexX, currentCellIndexY );
  1829. if(obj && currentCell != m_lastCell )
  1830. {
  1831. // To not expose PartitionCells, he will think in terms of points. He will
  1832. // unlook at a point and look at the new point. We do the rounding and the
  1833. // changing into PartitionCells
  1834. obj->onPartitionCellChange();
  1835. m_lastCell = currentCell;
  1836. }
  1837. // if we have moved, our shroudedness status might be different for all players,
  1838. // so it must all be invalidated.
  1839. invalidateShroudedStatusForAllPlayers();
  1840. #ifdef INTENSE_DEBUG
  1841. for (Int i = 0; i < m_coiInUseCount; i++)
  1842. {
  1843. for (Int j = 0; j < i; j++)
  1844. {
  1845. if (m_coiArray[i].getCell() == m_coiArray[j].getCell())
  1846. {
  1847. DEBUG_CRASH(("dup cells in COI array, this is bad"));
  1848. }
  1849. }
  1850. }
  1851. #endif
  1852. }
  1853. //-----------------------------------------------------------------------------
  1854. void PartitionData::invalidateShroudedStatusForPlayer(Int playerIndex)
  1855. {
  1856. #ifndef DISABLE_INVALID_PREVENTION
  1857. if (m_shroudedness[playerIndex] != OBJECTSHROUD_INVALID && m_shroudedness[playerIndex] != OBJECTSHROUD_INVALID_BUT_PREVIOUS_VALID)
  1858. #endif
  1859. m_shroudedness[playerIndex] = OBJECTSHROUD_INVALID;
  1860. }
  1861. //-----------------------------------------------------------------------------
  1862. void PartitionData::invalidateShroudedStatusForAllPlayers()
  1863. {
  1864. for (Int i = 0; i < MAX_PLAYER_COUNT; i++)
  1865. {
  1866. invalidateShroudedStatusForPlayer(i);
  1867. }
  1868. }
  1869. #if defined(_DEBUG) || defined(_INTERNAL)
  1870. static AsciiString theObjName;
  1871. #endif
  1872. //-----------------------------------------------------------------------------
  1873. Int PartitionData::calcMaxCoiForShape(GeometryType geom, Real majorRadius, Real minorRadius, Bool isSmall)
  1874. {
  1875. Int result;
  1876. // THis is commented out, since some cases od big extets labeled small seem to be escaping.
  1877. //M Lorenzen 8/26/03
  1878. // if (isSmall)
  1879. // {
  1880. // #if defined(_DEBUG) || defined(_INTERNAL)
  1881. // Int chk = calcMaxCoiForShape(geom, majorRadius, minorRadius, false);
  1882. // DEBUG_ASSERTCRASH(chk <= 4, ("Small objects should be <= 4 cells, but I calced %s as %d\n",theObjName.str(),chk));
  1883. // #endif
  1884. // result = 4;
  1885. // }
  1886. // else
  1887. {
  1888. switch(geom)
  1889. {
  1890. case GEOMETRY_SPHERE:
  1891. case GEOMETRY_CYLINDER:
  1892. {
  1893. // note that majorRadius is a radius, not a diameter.
  1894. // this actually allocates a few too many, but that's ok.
  1895. Int cells = ThePartitionManager->worldToCellDist(majorRadius*2) + 1;
  1896. result = cells * cells;
  1897. }
  1898. case GEOMETRY_BOX:
  1899. {
  1900. Real diagonal = (Real)(sqrtf(majorRadius*majorRadius + minorRadius*minorRadius));
  1901. Int cells = ThePartitionManager->worldToCellDist(diagonal*2) + 1;
  1902. result = cells * cells;
  1903. }
  1904. };
  1905. }
  1906. if (result < 4)
  1907. result = 4;
  1908. return result;
  1909. }
  1910. //-----------------------------------------------------------------------------
  1911. Int PartitionData::calcMaxCoiForObject()
  1912. {
  1913. Object *obj = getObject();
  1914. DEBUG_ASSERTCRASH(obj != NULL, ("must be attached to an Object here 2"));
  1915. GeometryType geom = obj->getGeometryInfo().getGeomType();
  1916. Real majorRadius = obj->getGeometryInfo().getMajorRadius();
  1917. Real minorRadius = obj->getGeometryInfo().getMinorRadius();
  1918. Bool isSmall = obj->getGeometryInfo().getIsSmall();
  1919. #if defined(_DEBUG) || defined(_INTERNAL)
  1920. theObjName = obj->getTemplate()->getName();
  1921. #endif
  1922. return calcMaxCoiForShape(geom, majorRadius, minorRadius, isSmall);
  1923. }
  1924. //-----------------------------------------------------------------------------
  1925. void PartitionData::makeDirty(Bool needToUpdateCells)
  1926. {
  1927. //DEBUG_LOG(("makeDirty for pd %08lx obj %08lx\n",this,m_object));
  1928. if (!ThePartitionManager->isInListDirtyModules(this))
  1929. {
  1930. if (needToUpdateCells)
  1931. m_dirtyStatus = NEED_CELL_UPDATE_AND_COLLISION_CHECK;
  1932. else if (m_dirtyStatus == NOT_DIRTY)
  1933. m_dirtyStatus = NEED_COLLISION_CHECK;
  1934. ThePartitionManager->prependToDirtyModules(this);
  1935. }
  1936. else if (needToUpdateCells)
  1937. {
  1938. // If I am in the list already, I may be in there for a half update, so if my
  1939. // needs have increased I'll upgrade what I want.
  1940. m_dirtyStatus = NEED_CELL_UPDATE_AND_COLLISION_CHECK;
  1941. }
  1942. }
  1943. //-----------------------------------------------------------------------------
  1944. void PartitionData::allocCoiArray()
  1945. {
  1946. DEBUG_ASSERTCRASH(m_coiArrayCount == 0 && m_coiArray == NULL, ("hmm, coi should probably be null here"));
  1947. DEBUG_ASSERTCRASH(m_coiInUseCount == 0, ("hmm, coi count mismatch"));
  1948. m_coiArrayCount = calcMaxCoiForObject();
  1949. m_coiArray = MSGNEW("PartitionManager_COI") CellAndObjectIntersection[m_coiArrayCount]; // may throw!
  1950. m_coiInUseCount = 0;
  1951. makeDirty(true);
  1952. }
  1953. //-----------------------------------------------------------------------------
  1954. void PartitionData::freeCoiArray()
  1955. {
  1956. delete [] m_coiArray; // yes, it's OK to call this on null...
  1957. m_coiArray = NULL;
  1958. m_coiArrayCount = 0;
  1959. m_coiInUseCount = 0;
  1960. makeDirty(true);
  1961. }
  1962. //-----------------------------------------------------------------------------
  1963. void PartitionData::attachToObject(Object* object)
  1964. {
  1965. // remember who contains us
  1966. m_object = object;
  1967. // we only snapshot things that are immobile and have something to draw. Don't need ghostobjects for others.
  1968. if (object->isKindOf(KINDOF_IMMOBILE))
  1969. { const ThingTemplate *tmplate=object->getTemplate();
  1970. const ModuleInfo &drawMod=tmplate->getDrawModuleInfo();
  1971. //Objects with default draw modules usually don't need ghostobjects so skip them to save memory.
  1972. Bool makeGhostObject=TRUE;
  1973. for (Int i=0; i<drawMod.getCount(); i++)
  1974. {
  1975. if (strcmp(drawMod.getNthName(i).str(),"W3DDefaultDraw") == 0)
  1976. { makeGhostObject=FALSE;
  1977. break;
  1978. }
  1979. }
  1980. if (makeGhostObject)
  1981. m_ghostObject = TheGhostObjectManager->addGhostObject(object, this);
  1982. }
  1983. //DEBUG_LOG(("attach pd for pd %08lx obj %08lx\n",this,m_object));
  1984. // (re)calc maxCoi and (re)alloc cois
  1985. DEBUG_ASSERTCRASH(m_coiArrayCount == 0 && m_coiArray == NULL, ("hmm, coi should probably be null here"));
  1986. DEBUG_ASSERTCRASH(m_coiInUseCount == 0, ("hmm, coi count mismatch"));
  1987. freeCoiArray();
  1988. allocCoiArray(); // may throw!
  1989. if (object)
  1990. object->friend_setPartitionData(this);
  1991. makeDirty(true);
  1992. }
  1993. //-----------------------------------------------------------------------------
  1994. void PartitionData::detachFromObject()
  1995. {
  1996. // this is a little hokey... if we are in the midst of processing the contact
  1997. // list, we have to ensure that any potential collisions get removed from that
  1998. // list, since we are about to go bye-bye. ugly, but effective. (srj)
  1999. //
  2000. // note 2: we used to do this in our dtor, but it's too late then, since we require
  2001. // our Object to still be present in order to figure out what to remove.
  2002. // so do it here, since detaching it should nuke it from the obj list anyway. (srj)
  2003. if (TheContactList)
  2004. TheContactList->removeSpecificPartitionData(this);
  2005. if (m_object)
  2006. m_object->friend_setPartitionData(NULL);
  2007. removeAllTouchedCells();
  2008. freeCoiArray();
  2009. //DEBUG_LOG(("detach pd for pd %08lx obj %08lx\n",this,m_object));
  2010. // no longer attached to object
  2011. m_object = NULL;
  2012. TheGhostObjectManager->removeGhostObject(m_ghostObject);
  2013. m_ghostObject = NULL;
  2014. }
  2015. //-----------------------------------------------------------------------------
  2016. void PartitionData::attachToGhostObject(GhostObject* object)
  2017. {
  2018. // remember who contains us
  2019. m_object = NULL; //it's only attached to a ghost object, no parent object.
  2020. m_ghostObject = object;
  2021. // (re)calc maxCoi and (re)alloc cois
  2022. DEBUG_ASSERTCRASH(m_coiArrayCount == 0 && m_coiArray == NULL, ("hmm, coi should probably be null here"));
  2023. DEBUG_ASSERTCRASH(m_coiInUseCount == 0, ("hmm, coi count mismatch"));
  2024. freeCoiArray();
  2025. m_coiArrayCount = calcMaxCoiForShape(object->getGeometryType(), object->getGeometryMajorRadius(), object->getGeometryMinorRadius(),object->getGeometrySmall());
  2026. m_coiArray = MSGNEW("PartitionManager_COI") CellAndObjectIntersection[m_coiArrayCount]; // may throw!
  2027. m_coiInUseCount = 0;
  2028. makeDirty(true);
  2029. if (m_ghostObject)
  2030. m_ghostObject->updateParentObject(NULL,this);
  2031. }
  2032. //-----------------------------------------------------------------------------
  2033. void PartitionData::detachFromGhostObject(void)
  2034. {
  2035. // this is a little hokey... if we are in the midst of processing the contact
  2036. // list, we have to ensure that any potential collisions get removed from that
  2037. // list, since we are about to go bye-bye. ugly, but effective. (srj)
  2038. //
  2039. // note 2: we used to do this in our dtor, but it's too late then, since we require
  2040. // our Object to still be present in order to figure out what to remove.
  2041. // so do it here, since detaching it should nuke it from the obj list anyway. (srj)
  2042. if (TheContactList)
  2043. TheContactList->removeSpecificPartitionData(this);
  2044. removeAllTouchedCells();
  2045. freeCoiArray();
  2046. //DEBUG_LOG(("detach pd for pd %08lx obj %08lx\n",this,m_object));
  2047. // no longer attached to object
  2048. m_object = NULL;
  2049. m_ghostObject = NULL;
  2050. }
  2051. //-----------------------------------------------------------------------------
  2052. inline UnsignedInt hash2ints(Int a, Int b)
  2053. {
  2054. // do it this way so that [a,b] always hashes to the same value as [b,a].
  2055. // this is unsophisticated but reasonable, since all ObjectIDs will
  2056. // quite likely be well below 65536...
  2057. if (a < b)
  2058. {
  2059. return (a<<16)+b;
  2060. }
  2061. else
  2062. {
  2063. return (b<<16)+a;
  2064. }
  2065. }
  2066. //-----------------------------------------------------------------------------
  2067. //-----------------------------------------------------------------------------
  2068. //-----------------------------------------------------------------------------
  2069. //-----------------------------------------------------------------------------
  2070. void PartitionContactList::addToContactList( PartitionData *obj, PartitionData *other )
  2071. {
  2072. if (obj == other || obj == NULL || other == NULL)
  2073. return;
  2074. Object* obj_obj = obj->getObject();
  2075. Object* other_obj = other->getObject();
  2076. if (obj_obj == NULL || other_obj == NULL)
  2077. return;
  2078. // compute hash index based on object's ids.
  2079. UnsignedInt hashValue = hash2ints(obj_obj->getID(), other_obj->getID());
  2080. hashValue %= PartitionContactList_SOCKET_COUNT;
  2081. // make sure given hit has not already been recorded
  2082. for (PartitionContactListNode* cd = m_contactHash[ hashValue ]; cd; cd = cd->m_nextHash )
  2083. {
  2084. if ((cd->m_obj == obj && cd->m_other == other) ||
  2085. (cd->m_obj == other && cd->m_other == obj))
  2086. {
  2087. // already noted
  2088. return;
  2089. }
  2090. }
  2091. // new hit
  2092. PartitionContactListNode *ncd = newInstance(PartitionContactListNode);
  2093. ncd->m_obj = obj;
  2094. ncd->m_other = other;
  2095. ncd->m_hashValue = hashValue;
  2096. // add to hash table
  2097. ncd->m_nextHash = m_contactHash[ hashValue ];
  2098. m_contactHash[ hashValue ] = ncd;
  2099. // add to list of contacts for this frame
  2100. ncd->m_next = m_contactList;
  2101. m_contactList = ncd;
  2102. #if 0
  2103. Int depth = 0;
  2104. for (PartitionContactListNode *cd2 = m_contactHash[ hashValue ]; cd2; cd2 = cd2->m_nextHash )
  2105. {
  2106. depth++;
  2107. }
  2108. if (depth > 3)
  2109. {
  2110. DEBUG_LOG(("depth is %d for %s %08lx (%d) - %s %08lx (%d)\n",
  2111. depth,obj_obj->getTemplate()->getName().str(),obj_obj,obj_obj->getID(),
  2112. other_obj->getTemplate()->getName().str(),other_obj,other_obj->getID()
  2113. ));
  2114. for (cd2 = m_contactHash[ hashValue ]; cd2; cd2 = cd2->m_nextHash )
  2115. {
  2116. UnsignedInt rawhash = djb2hash2ints(cd2->m_obj->getObject()->getID(), cd2->m_other->getObject()->getID());
  2117. //hashValue %= PartitionContactList_SOCKET_COUNT;
  2118. DEBUG_LOG(("ENTRY: %s %08lx (%d) - %s %08lx (%d) [rawhash %d]\n",
  2119. cd2->m_obj->getObject()->getTemplate()->getName().str(),cd2->m_obj->getObject(),cd2->m_obj->getObject()->getID(),
  2120. cd2->m_other->getObject()->getTemplate()->getName().str(),cd2->m_other->getObject(),cd2->m_other->getObject()->getID(),
  2121. rawhash));
  2122. }
  2123. }
  2124. static Real aggtotal = 0;
  2125. static Real aggfull = 0;
  2126. static Real aggcount = 0;
  2127. for (int ii = 0; ii < PartitionContactList_SOCKET_COUNT; ++ii)
  2128. {
  2129. if (m_contactHash[ii])
  2130. aggfull += 1.0f;
  2131. for (cd2 = m_contactHash[ ii ]; cd2; cd2 = cd2->m_nextHash )
  2132. {
  2133. aggtotal += 1.0f;
  2134. }
  2135. }
  2136. aggcount += 1.0f;
  2137. DEBUG_ASSERTLOG(((Int)aggcount)%1000!=0,("avg hash depth at %f is %f, fullness %f%%\n",
  2138. aggcount,aggtotal/(aggcount*PartitionContactList_SOCKET_COUNT),(aggfull*100)/(aggcount*PartitionContactList_SOCKET_COUNT)));
  2139. #endif
  2140. }
  2141. //-----------------------------------------------------------------------------
  2142. void PartitionContactList::removeSpecificPartitionData(PartitionData* data)
  2143. {
  2144. for (PartitionContactListNode* cd = m_contactList; cd; cd = cd->m_next)
  2145. {
  2146. if (cd->m_obj == data || cd->m_other == data)
  2147. {
  2148. cd->m_obj = NULL;
  2149. cd->m_other = NULL;
  2150. }
  2151. }
  2152. }
  2153. //-----------------------------------------------------------------------------
  2154. void PartitionContactList::resetContactList()
  2155. {
  2156. // remove items from hash table
  2157. PartitionContactListNode* cdnext;
  2158. for (PartitionContactListNode* cd = m_contactList; cd; cd = cdnext)
  2159. {
  2160. cdnext = cd->m_next;
  2161. cd->deleteInstance();
  2162. }
  2163. memset(m_contactHash, 0, sizeof(m_contactHash));
  2164. m_contactList = NULL;
  2165. }
  2166. //-----------------------------------------------------------------------------
  2167. void PartitionContactList::processContactList()
  2168. {
  2169. for (PartitionContactListNode* cd = m_contactList; cd; cd = cd->m_next)
  2170. {
  2171. if (cd->m_obj == NULL || cd->m_other == NULL)
  2172. continue;
  2173. // we know that their partitions overlap; determine if they REALLY collide
  2174. // before proceeding...
  2175. CollideLocAndNormal cinfo;
  2176. if (!cd->m_obj->friend_collidesWith(cd->m_other, &cinfo))
  2177. continue;
  2178. Object* obj = cd->m_obj->getObject();
  2179. Object* other = cd->m_other->getObject();
  2180. if( obj->getStatusBits().test( OBJECT_STATUS_NO_COLLISIONS ) ||
  2181. other->getStatusBits().test( OBJECT_STATUS_NO_COLLISIONS ) )
  2182. continue;
  2183. DEBUG_ASSERTCRASH(!(obj->isKindOf(KINDOF_IMMOBILE) && other->isKindOf(KINDOF_IMMOBILE)),
  2184. ("we should never have collisions between two immobile things reported"));
  2185. // the onCollide() calls can remove the object(s) from the partition mgr,
  2186. // thus destroying the partitiondata for 'em. go ahead and null these out here
  2187. // so we won't be tempted to use 'em (since they might be bogus).
  2188. cd->m_obj = NULL;
  2189. cd->m_other = NULL;
  2190. obj->onCollide(other, &cinfo.loc, &cinfo.normal);
  2191. flipCoord3D(&cinfo.normal);
  2192. //Before checking the "other" case, make sure that the previous collision didn't
  2193. //absorb him. This becomes a conflict for pilots giving veterancy to transports
  2194. //and pilots entering transports. Both would occur if these isDestroyed checks
  2195. //were missing.
  2196. if( !obj->isDestroyed() && !other->isDestroyed() )
  2197. {
  2198. other->onCollide(obj, &cinfo.loc, &cinfo.normal);
  2199. }
  2200. //
  2201. // NOTE: it is VERY IMPORTANT (for performance reasons) to not re-dirty immobile things.
  2202. //
  2203. // NOTE also that we re-get partitiondata from the object, since it might have been
  2204. // removed from the partition system by the onCollide call...
  2205. //
  2206. if (!obj->isDestroyed() && obj->friend_getPartitionData() != NULL && !obj->isKindOf(KINDOF_IMMOBILE))
  2207. {
  2208. //DEBUG_LOG(("%d: re-dirtying collision of %s %08lx with %s %08lx\n",TheGameLogic->getFrame(),obj->getTemplate()->getName().str(),obj,other->getTemplate()->getName().str(),other));
  2209. obj->friend_getPartitionData()->makeDirty(false);
  2210. }
  2211. if (!other->isDestroyed() && other->friend_getPartitionData() != NULL && !other->isKindOf(KINDOF_IMMOBILE))
  2212. {
  2213. //DEBUG_LOG(("%d: re-dirtying collision of %s %08lx with %s %08lx [other]\n",TheGameLogic->getFrame(),other->getTemplate()->getName().str(),other,obj->getTemplate()->getName().str(),obj));
  2214. other->friend_getPartitionData()->makeDirty(false);
  2215. }
  2216. }
  2217. }
  2218. //-----------------------------------------------------------------------------
  2219. //-----------------------------------------------------------------------------
  2220. //-----------------------------------------------------------------------------
  2221. //-----------------------------------------------------------------------------
  2222. PartitionManager::PartitionManager()
  2223. {
  2224. m_moduleList = NULL;
  2225. m_cellSize = m_cellSizeInv = 0.0f;
  2226. m_cellCountX = 0;
  2227. m_cellCountY = 0;
  2228. m_totalCellCount = 0;
  2229. m_cells = NULL;
  2230. m_worldExtents.lo.zero();
  2231. m_worldExtents.hi.zero();
  2232. m_dirtyModules = NULL;
  2233. m_updatedSinceLastReset = false;
  2234. #ifdef FASTER_GCO
  2235. m_maxGcoRadius = 0;
  2236. #endif
  2237. }
  2238. //-----------------------------------------------------------------------------
  2239. PartitionManager::~PartitionManager()
  2240. {
  2241. shutdown();
  2242. } // end ~PartitionManager
  2243. //-----------------------------------------------------------------------------
  2244. #ifdef PM_CACHE_TERRAIN_HEIGHT
  2245. static void calcHeights(const Region3D& world, Real cellSize, Int x, Int y, Real& loZ, Real& hiZ)
  2246. {
  2247. DEBUG_ASSERTCRASH(TheTerrainLogic, ("no TheTerrainLogic"));
  2248. Real xbase = world.lo.x + (x * cellSize);
  2249. Real ybase = world.lo.y + (y * cellSize);
  2250. const Real ROUGH_STEP_SIZE = MAP_XY_FACTOR; // no point in stepping smaller than grid scale
  2251. Real numSteps = ceilf(cellSize / ROUGH_STEP_SIZE);
  2252. Real step = cellSize / numSteps;
  2253. loZ = HUGE_DIST; // huge positive
  2254. hiZ = -HUGE_DIST; // huge negative
  2255. for (Real yy = 0; yy <= cellSize; yy += step)
  2256. {
  2257. for (Real xx = 0; xx <= cellSize; xx += step)
  2258. {
  2259. Real h = TheTerrainLogic->getGroundHeight( xbase + xx, ybase + yy );
  2260. if (h < loZ) loZ = h;
  2261. if (h > hiZ) hiZ = h;
  2262. }
  2263. }
  2264. }
  2265. #endif
  2266. //-----------------------------------------------------------------------------
  2267. void PartitionManager::init()
  2268. {
  2269. m_cellSize = TheGlobalData->m_partitionCellSize;
  2270. if (m_cellSize < 1.0)
  2271. m_cellSize = 1.0;
  2272. m_cellSizeInv = (Real)(1.0 / m_cellSize);
  2273. DEBUG_ASSERTCRASH(m_cells == NULL, ("double init"));
  2274. if (TheTerrainLogic)
  2275. {
  2276. TheTerrainLogic->getExtent(&m_worldExtents);
  2277. //DEBUG_ASSERTLOG(m_worldExtents.width() > 0 && m_worldExtents.height() > 0, ("TheTerrainLogic must be loaded before ThePartitionManager"));
  2278. // you'd think it wouldn't be legal for terrainlogic to have zero area, but apparently
  2279. // that's what it resets itself to. let's just make the world a simple place and pretend
  2280. // it's nonzero in area, so that we can proceed without crashing.
  2281. if (m_worldExtents.width() < 1.0f)
  2282. m_worldExtents.hi.x = m_worldExtents.lo.x + 1.0f;
  2283. if (m_worldExtents.height() < 1.0f)
  2284. m_worldExtents.hi.y = m_worldExtents.lo.y + 1.0f;
  2285. m_cellCountX = REAL_TO_INT_CEIL(m_worldExtents.width() * m_cellSizeInv);
  2286. m_cellCountY = REAL_TO_INT_CEIL(m_worldExtents.height() * m_cellSizeInv);
  2287. m_totalCellCount = m_cellCountX * m_cellCountY;
  2288. m_cells = MSGNEW("PartitionManager_Cells") PartitionCell[m_totalCellCount];
  2289. for (Int x = 0; x < m_cellCountX; x++)
  2290. {
  2291. for (Int y = 0; y < m_cellCountY; y++)
  2292. {
  2293. #ifdef PM_CACHE_TERRAIN_HEIGHT
  2294. Real loZ, hiZ;
  2295. calcHeights(m_worldExtents, m_cellSize, x, y, loZ, hiZ);
  2296. getCellAt(x, y)->init(x, y, loZ, hiZ);
  2297. #else
  2298. getCellAt(x, y)->init(x, y);
  2299. #endif
  2300. }
  2301. }
  2302. #ifdef FASTER_GCO
  2303. calcRadiusVec();
  2304. #endif
  2305. }
  2306. else
  2307. {
  2308. m_cellSize = m_cellSizeInv = 0.0f;
  2309. m_cellCountX = 0;
  2310. m_cellCountY = 0;
  2311. m_totalCellCount = 0;
  2312. m_cells = NULL;
  2313. m_worldExtents.lo.zero();
  2314. m_worldExtents.hi.zero();
  2315. }
  2316. }
  2317. //-----------------------------------------------------------------------------
  2318. #ifdef DUMP_PERF_STATS
  2319. void PartitionManager::getPMStats(double& gcoTimeThisFrameTotal, double& gcoTimeThisFrameAvg)
  2320. {
  2321. Int64 freq64;
  2322. GetPrecisionTimerTicksPerSec(&freq64);
  2323. double gcoTimeInMSecs = (double)s_timeInClosestObjectsThisFrame * 1000.0f / (double)freq64;
  2324. gcoTimeThisFrameTotal = gcoTimeInMSecs;
  2325. gcoTimeThisFrameAvg = gcoTimeInMSecs / (double)s_countInClosestObjectsThisFrame;
  2326. }
  2327. #endif
  2328. //-----------------------------------------------------------------------------
  2329. void PartitionManager::reset()
  2330. {
  2331. #ifdef DUMP_PERF_STATS
  2332. s_countInClosestObjects = 0;
  2333. s_timeInClosestObjects = 0;
  2334. s_countInClosestObjectsThisFrame = 0;
  2335. s_timeInClosestObjectsThisFrame = 0;
  2336. s_gcoPerfFrame = 0xffffffff;
  2337. #endif
  2338. resetPendingUndoShroudRevealQueue();
  2339. shutdown();
  2340. //init();
  2341. }
  2342. //-----------------------------------------------------------------------------
  2343. void PartitionManager::shutdown()
  2344. {
  2345. m_updatedSinceLastReset = false;
  2346. ThePartitionManager->removeAllDirtyModules();
  2347. #ifdef _DEBUG
  2348. // the above *should* remove all the touched cells (via unRegisterObject), but let's check:
  2349. DEBUG_ASSERTCRASH( m_moduleList == NULL, ("hmm, modules left over"));
  2350. PartitionData *mod, *nextMod;
  2351. for( mod = m_moduleList; mod; mod = nextMod )
  2352. {
  2353. nextMod = mod->getNext();
  2354. DEBUG_ASSERTCRASH(mod->friend_getCoiInUseCount() == 0, ("hmm, coi count mismatch"));
  2355. mod->friend_removeAllTouchedCells();
  2356. }
  2357. #endif
  2358. #ifdef FASTER_GCO
  2359. m_radiusVec.clear();
  2360. #endif
  2361. resetPendingUndoShroudRevealQueue();
  2362. delete [] m_cells;
  2363. m_cells = NULL;
  2364. m_cellSize = m_cellSizeInv = 0.0f;
  2365. m_cellCountX = 0;
  2366. m_cellCountY = 0;
  2367. m_totalCellCount = 0;
  2368. m_worldExtents.lo.zero();
  2369. m_worldExtents.hi.zero();
  2370. }
  2371. //-----------------------------------------------------------------------------
  2372. //DECLARE_PERF_TIMER(PartitionManager_update)
  2373. void PartitionManager::update()
  2374. {
  2375. //USE_PERF_TIMER(PartitionManager_update)
  2376. {
  2377. #ifdef INTENSE_DEBUG
  2378. Int cc = 0;
  2379. #endif
  2380. if (!m_updatedSinceLastReset)
  2381. {
  2382. m_updatedSinceLastReset = true;
  2383. }
  2384. PartitionContactList ctList;
  2385. TheContactList = &ctList;
  2386. while (m_dirtyModules)
  2387. {
  2388. #ifdef INTENSE_DEBUG
  2389. ++cc;
  2390. #endif
  2391. // save it.
  2392. PartitionData *dirty = m_dirtyModules;
  2393. DEBUG_ASSERTCRASH(dirty->getObject() != NULL || dirty->getGhostObject() != NULL,
  2394. ("must be attached to an Object here %08lx",dirty));
  2395. // get this BEFORE removing from dirty list, since that clears the
  2396. // flag in question.
  2397. Bool updateEm = dirty->isInNeedOfUpdatingCells();
  2398. Bool collideEm = dirty->isInNeedOfCollisionCheck() && dirty->getObject(); //only update collisions if we have object
  2399. // detach it from the dirty list.
  2400. removeFromDirtyModules(dirty);
  2401. if (updateEm)
  2402. {
  2403. dirty->friend_updateCellsTouched();
  2404. }
  2405. if (collideEm && !dirty->getObject()->isKindOf(KINDOF_IMMOBILE))
  2406. {
  2407. dirty->addPossibleCollisions(&ctList);
  2408. }
  2409. }
  2410. ctList.processContactList();
  2411. #ifdef INTENSE_DEBUG
  2412. DEBUG_ASSERTLOG(cc==0,("updated partition info for %d objects\n",cc));
  2413. #endif
  2414. TheContactList = NULL;
  2415. processPendingUndoShroudRevealQueue();
  2416. }
  2417. #if defined(_DEBUG) || defined(_INTERNAL)
  2418. if (TheGlobalData->m_debugThreatMap)
  2419. {
  2420. if (TheGameLogic->getFrame() % TheGlobalData->m_debugThreatMapTileDuration)
  2421. {
  2422. Int cellCount = m_cellCountX * m_cellCountY;
  2423. for (int i = 0; i < cellCount; ++i)
  2424. {
  2425. UnsignedInt threat = m_cells[i].getThreatValue(ThePlayerList->getLocalPlayer()->getPlayerIndex());
  2426. if (threat > 0)
  2427. {
  2428. Real threatMul = INT_TO_REAL(threat) / TheGlobalData->m_maxDebugThreat;
  2429. if (threatMul > 1.0f)
  2430. threatMul = 1.0f;
  2431. Real size = TheGlobalData->m_partitionCellSize;
  2432. Coord3D pos = { m_cells[i].getCellX() * size,
  2433. m_cells[i].getCellY() * size,
  2434. 0 };
  2435. pos.z = TheTerrainLogic->getGroundHeight(pos.x, pos.y);
  2436. RGBColor color;
  2437. color.red = threatMul;
  2438. color.blue = 0.0f;
  2439. color.green = 0.0f;
  2440. addIcon(&pos, size, TheGlobalData->m_debugThreatMapTileDuration, color);
  2441. }
  2442. }
  2443. }
  2444. }
  2445. if (TheGlobalData->m_debugCashValueMap)
  2446. {
  2447. if (TheGameLogic->getFrame() % TheGlobalData->m_debugCashValueMapTileDuration)
  2448. {
  2449. Int cellCount = m_cellCountX * m_cellCountY;
  2450. for (int i = 0; i < cellCount; ++i)
  2451. {
  2452. UnsignedInt value = m_cells[i].getCashValue(ThePlayerList->getLocalPlayer()->getPlayerIndex());
  2453. if (value > 0)
  2454. {
  2455. Real valueMul = INT_TO_REAL(value) / TheGlobalData->m_maxDebugValue;
  2456. if (valueMul > 1.0f)
  2457. valueMul = 1.0f;
  2458. Real size = TheGlobalData->m_partitionCellSize;
  2459. Coord3D pos = { m_cells[i].getCellX() * size,
  2460. m_cells[i].getCellY() * size,
  2461. 0 };
  2462. pos.z = TheTerrainLogic->getGroundHeight(pos.x, pos.y);
  2463. RGBColor color;
  2464. color.red = 0.0f;
  2465. color.blue = 0.0f;
  2466. color.green = valueMul;
  2467. addIcon(&pos, size, TheGlobalData->m_debugCashValueMapTileDuration, color);
  2468. }
  2469. }
  2470. }
  2471. }
  2472. #endif // defined(_DEBUG) || defined(_INTERNAL)
  2473. } // end update
  2474. //------------------------------------------------------------------------------
  2475. void PartitionManager::registerObject( Object* object )
  2476. {
  2477. // sanity
  2478. if( object == NULL )
  2479. return;
  2480. // if object is already part of this system get out of here
  2481. if( object->friend_getPartitionData() != NULL )
  2482. {
  2483. DEBUG_LOG(( "Object '%s' already registered with partition manager\n",
  2484. object->getTemplate()->getName().str() ));
  2485. return;
  2486. } // end if
  2487. // allocate a new module of partition data
  2488. PartitionData *mod = newInstance( PartitionData );
  2489. // link the module to the list in the partition manager
  2490. mod->setPrev( NULL );
  2491. mod->setNext( m_moduleList );
  2492. if( m_moduleList )
  2493. m_moduleList->setPrev( mod );
  2494. m_moduleList = mod;
  2495. // add module to object
  2496. mod->attachToObject( object );
  2497. }
  2498. //------------------------------------------------------------------------------
  2499. void PartitionManager::unRegisterObject( Object* object )
  2500. {
  2501. // sanity
  2502. if( object == NULL )
  2503. return;
  2504. // get the partition module
  2505. PartitionData *mod = object->friend_getPartitionData();
  2506. if( mod == NULL )
  2507. return;
  2508. GhostObject *ghost;
  2509. // need to figure out if any players have a fogged memory of this object.
  2510. // if so, we can't remove it from the shroud system just yet.
  2511. if ((ghost=mod->getGhostObject()) != NULL && mod->wasSeenByAnyPlayers() < MAX_PLAYER_COUNT)
  2512. {
  2513. if (TheContactList)
  2514. TheContactList->removeSpecificPartitionData(mod);
  2515. object->friend_setPartitionData(NULL);
  2516. mod->friend_setObject(NULL);
  2517. //Tell the ghost object that its parent is dead.
  2518. ghost->updateParentObject(NULL, mod);
  2519. return;
  2520. }
  2521. // detach the module from the object
  2522. mod->detachFromObject();
  2523. // remove module from master list
  2524. PartitionData *next = mod->getNext();
  2525. PartitionData *prev = mod->getPrev();
  2526. if( next )
  2527. next->setPrev( prev );
  2528. if( prev )
  2529. prev->setNext( next );
  2530. else
  2531. m_moduleList = next;
  2532. // delete module
  2533. mod->deleteInstance();
  2534. }
  2535. //------------------------------------------------------------------------------
  2536. void PartitionManager::registerGhostObject( GhostObject* object)
  2537. {
  2538. // sanity
  2539. if( object == NULL )
  2540. return;
  2541. // if object is already part of this system get out of here
  2542. if( object->friend_getPartitionData() != NULL )
  2543. {
  2544. DEBUG_LOG(( "GhostObject already registered with partition manager\n"));
  2545. return;
  2546. } // end if
  2547. // allocate a new module of partition data
  2548. PartitionData *mod = newInstance( PartitionData );
  2549. // link the module to the list in the partition manager
  2550. mod->setPrev( NULL );
  2551. mod->setNext( m_moduleList );
  2552. if( m_moduleList )
  2553. m_moduleList->setPrev( mod );
  2554. m_moduleList = mod;
  2555. // add module to object
  2556. mod->attachToGhostObject( object);
  2557. }
  2558. //------------------------------------------------------------------------------
  2559. void PartitionManager::unRegisterGhostObject( GhostObject* object )
  2560. {
  2561. // sanity
  2562. if( object == NULL )
  2563. return;
  2564. // get the partition module
  2565. PartitionData *mod = object->friend_getPartitionData();
  2566. if( mod == NULL )
  2567. return;
  2568. // detach the module from the object
  2569. mod->detachFromGhostObject();
  2570. // remove module from master list
  2571. PartitionData *next = mod->getNext();
  2572. PartitionData *prev = mod->getPrev();
  2573. if( next )
  2574. next->setPrev( prev );
  2575. if( prev )
  2576. prev->setNext( next );
  2577. else
  2578. m_moduleList = next;
  2579. // delete module
  2580. mod->deleteInstance();
  2581. }
  2582. /**
  2583. Reveals the map for the given player, but does not override Shroud generation. (Script)
  2584. */
  2585. void PartitionManager::revealMapForPlayer( Int playerIndex )
  2586. {
  2587. // By looking and then stopping on every cell, I clear all Passive Shroud
  2588. // By adding a looker directly I don't hit the Ally logic of the normal look/doShroudReveal
  2589. for (int i = 0; i < m_totalCellCount; ++i)
  2590. {
  2591. m_cells[i].addLooker( playerIndex );
  2592. m_cells[i].removeLooker( playerIndex );
  2593. }
  2594. }
  2595. /**
  2596. Reveals the map for the given player, AND permanently disables all Shroud generation (Observer Mode).
  2597. */
  2598. void PartitionManager::revealMapForPlayerPermanently( Int playerIndex )
  2599. {
  2600. // By skipping the removeLooker, I consider myself as actively looking at everything,
  2601. // so Shroud generation will no longer function
  2602. // By adding a looker directly I don't hit the Ally logic of the normal look/doShroudReveal
  2603. for (int i = 0; i < m_totalCellCount; ++i)
  2604. {
  2605. m_cells[i].addLooker( playerIndex );
  2606. }
  2607. }
  2608. /**
  2609. Adds a layer of permanent blindness. Used solely to undo the permanent reveal for debugging
  2610. */
  2611. void PartitionManager::undoRevealMapForPlayerPermanently( Int playerIndex )
  2612. {
  2613. //First make sure no lingering looks will leave holes when they aren't wanted.
  2614. processEntirePendingUndoShroudRevealQueue();
  2615. // This will have amusing consequences if done without a preceding revealMapForPlayerPermanently.
  2616. // Everything you own can become shrouded.
  2617. for (int i = 0; i < m_totalCellCount; ++i)
  2618. {
  2619. m_cells[i].removeLooker( playerIndex );
  2620. }
  2621. }
  2622. /**
  2623. Resets the shroud for the given player with passive shroud (can re-explore).
  2624. */
  2625. void PartitionManager::shroudMapForPlayer( Int playerIndex )
  2626. {
  2627. //First make sure no lingering looks will leave holes when they aren't wanted.
  2628. processEntirePendingUndoShroudRevealQueue();
  2629. // By pulsing a blast of shroud like this, we will set everything not actively looked at as Passive Shroud
  2630. for (int i = 0; i < m_totalCellCount; ++i)
  2631. {
  2632. m_cells[i].addShrouder( playerIndex );
  2633. m_cells[i].removeShrouder( playerIndex );
  2634. }
  2635. }
  2636. //-----------------------------------------------------------------------------
  2637. void PartitionManager::refreshShroudForLocalPlayer()
  2638. {
  2639. // This is a drawing refresh only, and so is allowed to use the Local Player.
  2640. TheDisplay->clearShroud();
  2641. TheRadar->clearShroud();
  2642. Int playerIndex = ThePlayerList->getLocalPlayer()->getPlayerIndex();
  2643. for (int i = 0; i < m_totalCellCount; ++i)
  2644. {
  2645. Int x = m_cells[i].getCellX();
  2646. Int y = m_cells[i].getCellY();
  2647. CellShroudStatus status = m_cells[i].getShroudStatusForPlayer(playerIndex);
  2648. TheDisplay->setShroudLevel(x, y, status);
  2649. TheRadar->setShroudLevel(x, y, status);
  2650. m_cells[i].invalidateShroudedStatusForAllCois(playerIndex);
  2651. }
  2652. }
  2653. //-----------------------------------------------------------------------------
  2654. CellShroudStatus PartitionManager::getShroudStatusForPlayer(Int playerIndex, Int x, Int y) const
  2655. {
  2656. if( playerIndex < 0 )
  2657. return CELLSHROUD_SHROUDED;// Safety. There are no Negative players, but PlayerIndex is typedef'd to Int, not UnsignedInt
  2658. const PartitionCell* cell = getCellAt(x, y);
  2659. return cell ? cell->getShroudStatusForPlayer(playerIndex) : CELLSHROUD_SHROUDED;
  2660. }
  2661. //-----------------------------------------------------------------------------
  2662. CellShroudStatus PartitionManager::getShroudStatusForPlayer(Int playerIndex, const Coord3D *loc ) const
  2663. {
  2664. Int x, y;
  2665. ThePartitionManager->worldToCell( loc->x, loc->y, &x, &y );
  2666. return getShroudStatusForPlayer( playerIndex, x, y );
  2667. }
  2668. //-----------------------------------------------------------------------------
  2669. ObjectShroudStatus PartitionManager::getPropShroudStatusForPlayer(Int playerIndex, const Coord3D *loc ) const
  2670. {
  2671. Int x, y;
  2672. ThePartitionManager->worldToCell( loc->x - m_cellSize*0.5f, loc->y - m_cellSize*0.5f, &x, &y );
  2673. CellShroudStatus cellStat = getShroudStatusForPlayer( playerIndex, x, y );
  2674. if (cellStat != getShroudStatusForPlayer( playerIndex, x+1, y )) {
  2675. return OBJECTSHROUD_PARTIAL_CLEAR;
  2676. }
  2677. if (cellStat != getShroudStatusForPlayer( playerIndex, x+1, y+1 )) {
  2678. return OBJECTSHROUD_PARTIAL_CLEAR;
  2679. }
  2680. if (cellStat != getShroudStatusForPlayer( playerIndex, x, y+1 )) {
  2681. return OBJECTSHROUD_PARTIAL_CLEAR;
  2682. }
  2683. if (cellStat == CELLSHROUD_SHROUDED) {
  2684. return OBJECTSHROUD_SHROUDED;
  2685. }
  2686. if (cellStat == CELLSHROUD_CLEAR) {
  2687. return OBJECTSHROUD_CLEAR;
  2688. }
  2689. return OBJECTSHROUD_FOGGED;
  2690. }
  2691. #ifdef FASTER_GCO
  2692. //-----------------------------------------------------------------------------
  2693. Int PartitionManager::calcMinRadius(const ICoord2D& cur)
  2694. {
  2695. /*
  2696. ok, so if obj "A" is in cell (0,0), and obj "B"
  2697. is in cell (cur.x, cur.y), calc the smallest distance they could be
  2698. from each other, in integral multiples of cellSize. Note that
  2699. each object can be anywhere within a given cell (not just in the center!)
  2700. so even objects in the same cell can still be cellSize*1.414 apart,
  2701. and objects in adjacent cells can be nearly-zero distance apart!
  2702. */
  2703. Real halfCell = m_cellSize * 0.5;
  2704. Coord3D centerPos[4] =
  2705. {
  2706. { -halfCell, -halfCell },
  2707. { halfCell, -halfCell },
  2708. { -halfCell, halfCell },
  2709. { halfCell, halfCell }
  2710. };
  2711. Real x = cur.x * m_cellSize;
  2712. Real y = cur.y * m_cellSize;
  2713. Coord3D otherPos[4] =
  2714. {
  2715. { x - halfCell, y - halfCell },
  2716. { x + halfCell, y - halfCell },
  2717. { x - halfCell, y + halfCell },
  2718. { x + halfCell, y + halfCell }
  2719. };
  2720. /*
  2721. this is ugly and there's probably a faster way, but we only do this once per PM reset,
  2722. so it really shouldn't matter... (I hope)
  2723. */
  2724. double minDistSqr = 1e12; // double, not real
  2725. for (int i = 0; i < 4; ++i)
  2726. {
  2727. for (int j = 0; j < 4; ++j)
  2728. {
  2729. // double, not real
  2730. double dx = centerPos[i].x - otherPos[j].x;
  2731. double dy = centerPos[i].y - otherPos[j].y;
  2732. double curDistSqr = dx*dx + dy*dy;
  2733. if (minDistSqr > curDistSqr)
  2734. minDistSqr = curDistSqr;
  2735. }
  2736. }
  2737. // double, not real
  2738. double dist = sqrtf(minDistSqr);
  2739. Int minRadius = REAL_TO_INT_CEIL( dist / m_cellSize );
  2740. return minRadius;
  2741. }
  2742. #endif
  2743. #ifdef FASTER_GCO
  2744. //-----------------------------------------------------------------------------
  2745. void PartitionManager::calcRadiusVec()
  2746. {
  2747. Real cellSize = getCellSize();
  2748. Int cx = getCellCountX();
  2749. Int cy = getCellCountY();
  2750. // double, not real
  2751. double dx = (double)cx * (double)cellSize;
  2752. double dy = (double)cy * (double)cellSize;
  2753. double maxPossibleDist = sqrt(dx*dx + dy*dy);
  2754. m_maxGcoRadius = REAL_TO_INT_CEIL(maxPossibleDist / cellSize);
  2755. m_radiusVec.clear();
  2756. m_radiusVec.resize(m_maxGcoRadius+1, OffsetVec());
  2757. ICoord2D cur;
  2758. for (cur.y = -cy+1; cur.y < cy; ++cur.y)
  2759. {
  2760. for (cur.x = -cx+1; cur.x < cx; ++cur.x)
  2761. {
  2762. /*
  2763. m_radiusVec[curRadius] contains a list of the cells (foo) that could
  2764. contain objects that are <= (curRadius * cellSize) distance away from cell (0,0).
  2765. */
  2766. Int curRadius = calcMinRadius(cur);
  2767. DEBUG_ASSERTCRASH(curRadius <= m_maxGcoRadius, ("expected max of %d but got %d\n",m_maxGcoRadius,curRadius));
  2768. if (curRadius <= m_maxGcoRadius)
  2769. m_radiusVec[curRadius].push_back(cur);
  2770. }
  2771. }
  2772. #if defined(_DEBUG) || defined(_INTERNAL)
  2773. Int total = 0;
  2774. for (Int i = 0; i <= m_maxGcoRadius; ++i)
  2775. {
  2776. total += m_radiusVec[i].size();
  2777. //DEBUG_LOG(("radius %d has %d entries\n",i,m_radiusVec[i].size()));
  2778. }
  2779. DEBUG_ASSERTCRASH(total == (cx*2-1)*(cy*2-1),("expected %d, got %d\n",(cx*2-1)*(cy*2-1),total));
  2780. #endif
  2781. }
  2782. #endif
  2783. //-----------------------------------------------------------------------------
  2784. //DECLARE_PERF_TIMER(getClosestObjects)
  2785. Object *PartitionManager::getClosestObjects(
  2786. const Object *obj,
  2787. const Coord3D *pos,
  2788. Real maxDist,
  2789. DistanceCalculationType dc,
  2790. PartitionFilter **filters,
  2791. SimpleObjectIterator *iterArg, // if nonnull, append ALL satisfactory objects to the iterator (not just the single closest)
  2792. Real *closestDistArg,
  2793. Coord3D *closestVecArg
  2794. )
  2795. {
  2796. //USE_PERF_TIMER(getClosestObjects)
  2797. #ifdef DUMP_PERF_STATS
  2798. if (TheGameLogic->getFrame() != s_gcoPerfFrame)
  2799. {
  2800. s_gcoPerfFrame = TheGameLogic->getFrame();
  2801. s_countInClosestObjectsThisFrame = 0;
  2802. s_timeInClosestObjectsThisFrame = 0;
  2803. }
  2804. ++s_countInClosestObjects;
  2805. ++s_countInClosestObjectsThisFrame;
  2806. Int64 startTime64;
  2807. GetPrecisionTimer(&startTime64);
  2808. #endif
  2809. #ifdef _DEBUG
  2810. static Int theEntrancyCount = 0;
  2811. DEBUG_ASSERTCRASH(theEntrancyCount == 0, ("sorry, this routine is not reentrant"));
  2812. ++theEntrancyCount;
  2813. #endif
  2814. DEBUG_ASSERTCRASH((obj==NULL) != (pos == NULL), ("either obj or pos must be null"));
  2815. DistCalcProc distProc = theDistCalcProcs[dc];
  2816. const Coord3D *objPos;
  2817. const Object *objToUse;
  2818. if (pos)
  2819. {
  2820. objPos = pos;
  2821. objToUse = NULL;
  2822. }
  2823. else
  2824. {
  2825. objPos = obj->getPosition();
  2826. objToUse = obj;
  2827. }
  2828. Int cellCenterX, cellCenterY;
  2829. worldToCell(objPos->x, objPos->y, &cellCenterX, &cellCenterY);
  2830. Object* closestObj = NULL;
  2831. Real closestDistSqr = maxDist * maxDist; // if it's not closer than this, we shouldn't consider it anyway...
  2832. Coord3D closestVec;
  2833. #ifdef FASTER_GCO
  2834. Int maxRadius = m_maxGcoRadius;
  2835. if (maxDist < HUGE_DIST)
  2836. {
  2837. // don't go outwards any farther than necessary.
  2838. maxRadius = minInt(m_maxGcoRadius, worldToCellDist(maxDist));
  2839. }
  2840. #if defined(INTENSE_DEBUG)
  2841. /*
  2842. Note, if you ever enable this code, be forewarned that it can give
  2843. you "false positives" for objects that are located just off the map... (srj)
  2844. */
  2845. Int maxRadiusLimit = maxRadius + 3;
  2846. if (maxRadiusLimit > m_maxGcoRadius) maxRadiusLimit = m_maxGcoRadius;
  2847. #else
  2848. Int maxRadiusLimit = maxRadius;
  2849. #endif
  2850. Bool foundAny = false;
  2851. static Int theIterFlag = 1; // nonzero, thanks
  2852. ++theIterFlag;
  2853. /*
  2854. m_radiusVec[curRadius] contains a list of the cells (foo) that could
  2855. contain objects that are <= (curRadius * cellSize) distance away from cell (0,0).
  2856. */
  2857. for (Int curRadius = 0; curRadius <= maxRadiusLimit; ++curRadius)
  2858. {
  2859. const OffsetVec& offsets = m_radiusVec[curRadius];
  2860. if (offsets.empty())
  2861. continue;
  2862. for (OffsetVec::const_iterator it = offsets.begin(); it != offsets.end(); ++it)
  2863. {
  2864. PartitionCell* thisCell = getCellAt(cellCenterX + it->x, cellCenterY + it->y);
  2865. if (thisCell == NULL)
  2866. continue;
  2867. for (CellAndObjectIntersection *thisCoi = thisCell->getFirstCoiInCell(); thisCoi; thisCoi = thisCoi->getNextCoi())
  2868. {
  2869. PartitionData *thisMod = thisCoi->getModule();
  2870. Object *thisObj = thisMod->getObject();
  2871. // never compare against ourself.
  2872. if (thisObj == obj || thisObj == NULL)
  2873. continue;
  2874. // since an object can exist in multiple COIs, we use this to avoid processing
  2875. // the same one more than once.
  2876. if (thisMod->friend_getDoneFlag() == theIterFlag)
  2877. continue;
  2878. thisMod->friend_setDoneFlag(theIterFlag);
  2879. Real thisDistSqr;
  2880. Coord3D distVec;
  2881. if (!(*distProc)(objPos, objToUse, thisObj->getPosition(), thisObj, thisDistSqr, distVec, closestDistSqr))
  2882. continue;
  2883. if (!filtersAllow(filters, thisObj))
  2884. continue;
  2885. // ok, this is within the range, and the filters allow it.
  2886. // add it to the iter, if we have one....
  2887. if (iterArg)
  2888. {
  2889. iterArg->insert(thisObj, thisDistSqr);
  2890. }
  2891. else
  2892. {
  2893. // hey, this is the new closest object! cool.
  2894. // (note that we can't break out now 'cuz we have to finish examining the
  2895. // rest of curRadius)
  2896. closestObj = thisObj;
  2897. closestDistSqr = thisDistSqr;
  2898. closestVec = distVec;
  2899. if (!foundAny)
  2900. {
  2901. // if not adding to iterArg, we want to stop once we have the closest object.
  2902. maxRadiusLimit = curRadius;
  2903. }
  2904. foundAny = true;
  2905. }
  2906. } // next coi
  2907. } // next cell in this radius
  2908. } // next radius
  2909. #else // not FASTER_GCO
  2910. CellOutwardIterator iter(this, cellCenterX, cellCenterY);
  2911. if (maxDist < HUGE_DIST)
  2912. {
  2913. // don't go outwards any farther than necessary.
  2914. Int max = worldToCellDist(maxDist) + 1;
  2915. // default value for "max" is largest possible, based on map size, so we should
  2916. // never make it any larger than that
  2917. if (max < iter.getMaxRadius())
  2918. iter.setMaxRadius(max);
  2919. }
  2920. Bool foundAny = false;
  2921. static Int theIterFlag = 1; // nonzero, thanks
  2922. ++theIterFlag;
  2923. PartitionCell *thisCell;
  2924. while ((thisCell = iter.nextNonEmpty()) != NULL)
  2925. {
  2926. CellAndObjectIntersection *nextCoi;
  2927. for (CellAndObjectIntersection *thisCoi = thisCell->getFirstCoiInCell(); thisCoi; thisCoi = nextCoi)
  2928. {
  2929. nextCoi = thisCoi->getNextCoi();
  2930. PartitionData *thisMod = thisCoi->getModule();
  2931. Object *thisObj = thisMod->getObject();
  2932. // never compare against ourself.
  2933. if (thisObj == obj)
  2934. continue;
  2935. if (thisMod->friend_getDoneFlag() == theIterFlag)
  2936. continue;
  2937. thisMod->friend_setDoneFlag(theIterFlag);
  2938. // hmm, ok, calc the distance.
  2939. Real thisDistSqr;
  2940. Coord3D distVec;
  2941. if (!(*distProc)(objPos, objToUse, thisObj->getPosition(), thisObj, &thisDistSqr, &distVec, closestDistSqr))
  2942. continue;
  2943. // check the filters now
  2944. if (!filtersAllow(filters, thisObj))
  2945. continue;
  2946. // ok, guess this is a winner!
  2947. if (iterArg)
  2948. {
  2949. iterArg->insert(thisObj, thisDistSqr);
  2950. }
  2951. else
  2952. {
  2953. closestObj = thisObj;
  2954. closestDistSqr = thisDistSqr;
  2955. closestVec = distVec;
  2956. if (!foundAny)
  2957. {
  2958. // if not adding to iterArg, we want to stop once we have the closest object.
  2959. // since all objects in this radius (and the next radius, due to slop) might
  2960. // be slightly closer, we still have to check all of them. so set the termination
  2961. // radius to be our-current-radius-plus-1. (if we ARE adding to the iterArg, we skip
  2962. // this, cuz we want to go all the way out to the original max we specified as an arg.)
  2963. iter.setMaxRadius(iter.getCurCellRadius() + 2);
  2964. }
  2965. foundAny = true;
  2966. }
  2967. }
  2968. }
  2969. #endif // not FASTER_GCO
  2970. if (closestVecArg)
  2971. {
  2972. *closestVecArg = closestVec;
  2973. }
  2974. if (closestDistArg)
  2975. {
  2976. *closestDistArg = (Real)sqrtf(closestDistSqr);
  2977. }
  2978. #ifdef _DEBUG
  2979. --theEntrancyCount;
  2980. #endif
  2981. #ifdef DUMP_PERF_STATS
  2982. Int64 endTime64;
  2983. GetPrecisionTimer(&endTime64);
  2984. Int64 delta = (endTime64 - startTime64);
  2985. s_timeInClosestObjects += delta;
  2986. s_timeInClosestObjectsThisFrame += delta;
  2987. #endif
  2988. return closestObj; // might be null...
  2989. }
  2990. //-----------------------------------------------------------------------------
  2991. Object *PartitionManager::getClosestObject(
  2992. const Object *obj,
  2993. Real maxDist,
  2994. DistanceCalculationType dc,
  2995. PartitionFilter **filters,
  2996. Real *closestDist,
  2997. Coord3D *closestDistVec
  2998. )
  2999. {
  3000. return getClosestObjects(obj, NULL, maxDist, dc, filters, NULL, closestDist, closestDistVec);
  3001. }
  3002. //-----------------------------------------------------------------------------
  3003. Object *PartitionManager::getClosestObject(
  3004. const Coord3D *pos,
  3005. Real maxDist,
  3006. DistanceCalculationType dc,
  3007. PartitionFilter **filters,
  3008. Real *closestDist,
  3009. Coord3D *closestDistVec
  3010. )
  3011. {
  3012. return getClosestObjects(NULL, pos, maxDist, dc, filters, NULL, closestDist, closestDistVec);
  3013. }
  3014. //-----------------------------------------------------------------------------
  3015. void PartitionManager::getVectorTo(const Object *obj, const Object *otherObj, DistanceCalculationType dc, Coord3D& vec)
  3016. {
  3017. DistCalcProc distProc = theDistCalcProcs[dc];
  3018. Real distSqr;
  3019. (*distProc)(obj->getPosition(), obj, otherObj->getPosition(), otherObj, distSqr, vec, HUGE_DIST_SQR);
  3020. }
  3021. //-----------------------------------------------------------------------------
  3022. void PartitionManager::getVectorTo(const Object *obj, const Coord3D *pos, DistanceCalculationType dc, Coord3D& vec)
  3023. {
  3024. DistCalcProc distProc = theDistCalcProcs[dc];
  3025. Real distSqr;
  3026. (*distProc)(obj->getPosition(), obj, pos, NULL, distSqr, vec, HUGE_DIST_SQR);
  3027. }
  3028. //-----------------------------------------------------------------------------
  3029. Real PartitionManager::getDistanceSquared(const Object *obj, const Object *otherObj, DistanceCalculationType dc, Coord3D *vec)
  3030. {
  3031. DistCalcProc distProc = theDistCalcProcs[dc];
  3032. Real thisDistSqr;
  3033. Coord3D thisVec;
  3034. (*distProc)(obj->getPosition(), obj, otherObj->getPosition(), otherObj, thisDistSqr, thisVec, HUGE_DIST_SQR);
  3035. if (vec)
  3036. *vec = thisVec;
  3037. return thisDistSqr;
  3038. }
  3039. //-----------------------------------------------------------------------------
  3040. Real PartitionManager::getDistanceSquared(const Object *obj, const Coord3D *pos, DistanceCalculationType dc, Coord3D *vec)
  3041. {
  3042. DistCalcProc distProc = theDistCalcProcs[dc];
  3043. Real thisDistSqr;
  3044. Coord3D thisVec;
  3045. (*distProc)(obj->getPosition(), obj, pos, NULL, thisDistSqr, thisVec, HUGE_DIST_SQR);
  3046. if (vec)
  3047. *vec = thisVec;
  3048. return thisDistSqr;
  3049. }
  3050. //-----------------------------------------------------------------------------
  3051. // Gets the distance if obj were at goalPos. Used to calculate attack position paths.
  3052. Real PartitionManager::getGoalDistanceSquared(const Object *obj, const Coord3D *goalPos, const Object *otherObj, DistanceCalculationType dc, Coord3D *vec)
  3053. {
  3054. DistCalcProc distProc = theDistCalcProcs[dc];
  3055. Real thisDistSqr;
  3056. Coord3D thisVec;
  3057. (*distProc)(goalPos, obj, otherObj->getPosition(), otherObj, thisDistSqr, thisVec, HUGE_DIST_SQR);
  3058. if (vec)
  3059. *vec = thisVec;
  3060. return thisDistSqr;
  3061. }
  3062. //-----------------------------------------------------------------------------
  3063. // Gets the distance if obj were at goalPos. Used to calculate attack position paths.
  3064. Real PartitionManager::getGoalDistanceSquared(const Object *obj, const Coord3D *goalPos, const Coord3D *otherPos, DistanceCalculationType dc, Coord3D *vec)
  3065. {
  3066. DistCalcProc distProc = theDistCalcProcs[dc];
  3067. Real thisDistSqr;
  3068. Coord3D thisVec;
  3069. (*distProc)(goalPos, obj, otherPos, NULL, thisDistSqr, thisVec, HUGE_DIST_SQR);
  3070. if (vec)
  3071. *vec = thisVec;
  3072. return thisDistSqr;
  3073. }
  3074. //-----------------------------------------------------------------------------
  3075. Real PartitionManager::getRelativeAngle2D( const Object *obj, const Object *otherObj )
  3076. {
  3077. return getRelativeAngle2D(obj, otherObj->getPosition());
  3078. }
  3079. //-----------------------------------------------------------------------------
  3080. Real PartitionManager::getRelativeAngle2D( const Object *obj, const Coord3D *pos )
  3081. {
  3082. Coord3D v;
  3083. // compute vector to given position
  3084. Coord3D objPos = *obj->getPosition();
  3085. v.x = pos->x - objPos.x;
  3086. v.y = pos->y - objPos.y;
  3087. v.z = 0.0f;
  3088. Real dist = (Real)sqrtf(sqr(v.x) + sqr(v.y));
  3089. // normalize
  3090. if (dist == 0.0f)
  3091. return 0.0f;
  3092. const Coord3D *dir = obj->getUnitDirectionVector2D();
  3093. Real distInv = 1.0f / dist;
  3094. v.x *= distInv;
  3095. v.y *= distInv;
  3096. v.z *= distInv;
  3097. // dot of two unit vectors is cos of angle
  3098. Real c = dir->x*v.x + dir->y*v.y; // + dir->z*v.z;
  3099. // bound it in case of numerical error
  3100. if (c < -1.0)
  3101. c = -1.0;
  3102. else if (c > 1.0)
  3103. c = 1.0;
  3104. Real value = (Real)ACos( c );
  3105. // Determine sign by checking Z component of dir cross v
  3106. // Note this is assumes 2D, and is identical to dotting the perpendicular of v with dir
  3107. Real perpZ = dir->x * v.y - dir->y * v.x;
  3108. if (perpZ < 0.0f)
  3109. value = -value;
  3110. // note: to make this 3D, 'dir' and 'v' can be normalized and dotted just as they are
  3111. // to test sign, compute N = dir X v, then P = N x dir, then S = P . v, where sign of
  3112. // S is sign of angle - MSB
  3113. return value;
  3114. }
  3115. //-----------------------------------------------------------------------------
  3116. SimpleObjectIterator *PartitionManager::iterateObjectsInRange(
  3117. const Object *obj,
  3118. Real maxDist,
  3119. DistanceCalculationType dc,
  3120. PartitionFilter **filters,
  3121. IterOrderType order
  3122. )
  3123. {
  3124. MemoryPoolObjectHolder iterHolder;
  3125. SimpleObjectIterator *iter = newInstance(SimpleObjectIterator);
  3126. iterHolder.hold(iter);
  3127. getClosestObjects(obj, NULL, maxDist, dc, filters, iter, NULL, NULL);
  3128. iter->sort(order);
  3129. iterHolder.release();
  3130. return iter;
  3131. }
  3132. //-----------------------------------------------------------------------------
  3133. SimpleObjectIterator *PartitionManager::iterateObjectsInRange(
  3134. const Coord3D *pos,
  3135. Real maxDist,
  3136. DistanceCalculationType dc,
  3137. PartitionFilter **filters,
  3138. IterOrderType order
  3139. )
  3140. {
  3141. MemoryPoolObjectHolder iterHolder;
  3142. SimpleObjectIterator *iter = newInstance(SimpleObjectIterator);
  3143. iterHolder.hold(iter);
  3144. getClosestObjects(NULL, pos, maxDist, dc, filters, iter, NULL, NULL);
  3145. iter->sort(order);
  3146. iterHolder.release();
  3147. return iter;
  3148. }
  3149. //-----------------------------------------------------------------------------
  3150. SimpleObjectIterator* PartitionManager::iteratePotentialCollisions(
  3151. const Coord3D* pos,
  3152. const GeometryInfo& geom,
  3153. Real angle,
  3154. Bool use2D
  3155. )
  3156. {
  3157. Real maxDist = geom.getBoundingSphereRadius();
  3158. maxDist *= 1.1f; // just a little slop
  3159. MemoryPoolObjectHolder iterHolder;
  3160. SimpleObjectIterator *iter = newInstance(SimpleObjectIterator);
  3161. iterHolder.hold(iter);
  3162. PartitionFilterWouldCollide filter(*pos, geom, angle, true);
  3163. PartitionFilter *filters[] = { &filter, NULL };
  3164. getClosestObjects(NULL, pos, maxDist, use2D ? FROM_BOUNDINGSPHERE_2D : FROM_BOUNDINGSPHERE_3D, filters, iter, NULL, NULL);
  3165. iterHolder.release();
  3166. return iter;
  3167. }
  3168. //-----------------------------------------------------------------------------
  3169. Bool PartitionManager::isColliding( const Object *a, const Object *b ) const
  3170. {
  3171. //Make sure we have objects
  3172. if( !a || !b )
  3173. {
  3174. return false;
  3175. }
  3176. //Get both object's partition data
  3177. const PartitionData* ad = a->friend_getConstPartitionData();
  3178. const PartitionData* bd = b->friend_getConstPartitionData();
  3179. //Make sure we got it
  3180. if( !ad || !bd )
  3181. {
  3182. return false;
  3183. }
  3184. //See if the partition data collides.
  3185. return ad->friend_collidesWith( bd, NULL );
  3186. }
  3187. //-----------------------------------------------------------------------------
  3188. SimpleObjectIterator *PartitionManager::iterateAllObjects(PartitionFilter **filters)
  3189. {
  3190. MemoryPoolObjectHolder iterHolder;
  3191. SimpleObjectIterator *iter = newInstance(SimpleObjectIterator);
  3192. iterHolder.hold(iter);
  3193. // no distance constraints; we're gonna have to process 'em all anyway,
  3194. // so go thru in the fastest way we know how.
  3195. PartitionData *mod, *nextMod;
  3196. for( mod = m_moduleList; mod; mod = nextMod )
  3197. {
  3198. nextMod = mod->getNext();
  3199. Object *obj = mod->getObject();
  3200. if (obj && filtersAllow(filters, obj))
  3201. {
  3202. iter->insert( obj );
  3203. }
  3204. }
  3205. iterHolder.release();
  3206. return iter;
  3207. }
  3208. // ------------------------------------------------------------------------------------------------
  3209. // ------------------------------------------------------------------------------------------------
  3210. Bool PartitionManager::tryPosition( const Coord3D *center,
  3211. Real dist,
  3212. Real angle,
  3213. const FindPositionOptions *options,
  3214. Coord3D *result )
  3215. {
  3216. // compute the spot on the terrain we've picked
  3217. Coord3D pos;
  3218. pos.x = dist * Cos( angle ) + center->x;
  3219. pos.y = dist * Sin( angle ) + center->y;
  3220. PathfindLayerEnum layer = LAYER_GROUND;
  3221. if ((options->flags & FPF_USE_HIGHEST_LAYER) != 0)
  3222. {
  3223. pos.z = 99999.0f;
  3224. layer = TheTerrainLogic->getHighestLayerForDestination(&pos);
  3225. pos.z = TheTerrainLogic->getLayerHeight(pos.x, pos.y, layer);
  3226. // ensure we are slightly above the bridge, to account for fudge & sloppy art
  3227. if (layer != LAYER_GROUND)
  3228. pos.z += 1.0f;
  3229. }
  3230. else
  3231. {
  3232. pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y );
  3233. }
  3234. if (fabs(pos.z - center->z) > options->maxZDelta)
  3235. return FALSE;
  3236. //
  3237. // we don't usually find positions on cliffs.
  3238. // someday, add bit options for this, like for water.
  3239. //
  3240. if (TheTerrainLogic->isCliffCell(pos.x, pos.y) && layer == LAYER_GROUND)
  3241. return FALSE;
  3242. //
  3243. // srj sez:
  3244. // we don't usually find positions on impassable areas.
  3245. // someday, add bit options for this, like for water.
  3246. //
  3247. {
  3248. Int cellX = REAL_TO_INT_FLOOR(pos.x / PATHFIND_CELL_SIZE);
  3249. Int cellY = REAL_TO_INT_FLOOR(pos.y / PATHFIND_CELL_SIZE);
  3250. PathfindCell* cell = TheAI->pathfinder()->getCell(layer, cellX, cellY);
  3251. if (!cell || cell->getType() == PathfindCell::CELL_IMPASSABLE)
  3252. {
  3253. return FALSE;
  3254. }
  3255. if( BitTest(options->flags, FPF_CLEAR_CELLS_ONLY) && cell->getType() != PathfindCell::CELL_CLEAR )
  3256. return FALSE;
  3257. }
  3258. //
  3259. // we don't usually find positions in the water unless we explicitly say that's OK,
  3260. // or if the option is set we can only pick position underwater
  3261. //
  3262. if( BitTest( options->flags, FPF_IGNORE_WATER ) == FALSE )
  3263. {
  3264. Bool isUnderwater = TheTerrainLogic->isUnderwater( pos.x, pos.y );
  3265. //
  3266. // if we want water spots only and this is underwater it's no good, otherwise we want
  3267. // the default behavior where underwater spots are invalid
  3268. //
  3269. if( BitTest( options->flags, FPF_WATER_ONLY ) && (isUnderwater == FALSE || layer != LAYER_GROUND) )
  3270. return FALSE;
  3271. else if( isUnderwater == TRUE && layer == LAYER_GROUND )
  3272. return FALSE;
  3273. } // end if
  3274. // object checks
  3275. if( BitTest( options->flags, FPF_IGNORE_ALL_OBJECTS ) == FALSE )
  3276. {
  3277. //
  3278. // iterate the potential collisions at this location using a
  3279. // very small sphere geometry around the point
  3280. //
  3281. GeometryInfo geometry( GEOMETRY_SPHERE, TRUE, 5.0f, 5.0f, 5.0f );
  3282. ObjectIterator *iter = ThePartitionManager->iteratePotentialCollisions( &pos, geometry, angle, true );
  3283. MemoryPoolObjectHolder hold( iter );
  3284. // Bool overlap = FALSE;
  3285. for( Object *them = iter->first(); them; them = iter->next() )
  3286. {
  3287. // if this is the ignore object, ignore it
  3288. if( them == options->ignoreObject )
  3289. continue; // continue in the object iterator for loop
  3290. // relationship checks
  3291. if( options->relationshipObject )
  3292. {
  3293. // if this is an ally/neutral unit and we ignore those, do so
  3294. if( BitTest( options->flags, FPF_IGNORE_ALLY_OR_NEUTRAL_UNITS ) == TRUE &&
  3295. options->relationshipObject->getRelationship( them ) != ENEMIES &&
  3296. (them->isKindOf( KINDOF_INFANTRY ) || them->isKindOf( KINDOF_VEHICLE )) )
  3297. continue;
  3298. // if this is an ally/neutral structure and we ignore those, do so
  3299. if( BitTest( options->flags, FPF_IGNORE_ALLY_OR_NEUTRAL_STRUCTURES ) == TRUE &&
  3300. options->relationshipObject->getRelationship( them ) != ENEMIES &&
  3301. them->isKindOf( KINDOF_STRUCTURE ) )
  3302. continue;
  3303. // if this is an enemy unit and we ignore those, do so
  3304. if( BitTest( options->flags, FPF_IGNORE_ENEMY_UNITS ) == TRUE &&
  3305. options->relationshipObject->getRelationship( them ) == ENEMIES &&
  3306. (them->isKindOf( KINDOF_INFANTRY ) || them->isKindOf( KINDOF_VEHICLE )) )
  3307. continue;
  3308. // if this is an enemy structure and we ignore those, do so
  3309. if( BitTest( options->flags, FPF_IGNORE_ENEMY_STRUCTURES ) == TRUE &&
  3310. options->relationshipObject->getRelationship( them ) == ENEMIES &&
  3311. them->isKindOf( KINDOF_STRUCTURE ) )
  3312. continue;
  3313. } // end if, relationship checks
  3314. //
  3315. // if we have a source that must path to the destination we will ignore that too
  3316. // NOTE: If needed, we can reorganize the meaning of this method to be
  3317. // more explicit on when to pay attention to any 'source' objects and when not too
  3318. // but for now we're keeping the API very simple for the kinds of queries
  3319. // that we're likely to do for an RTS (CBD)
  3320. //
  3321. if( them == options->sourceToPathToDest )
  3322. continue; // continue in the object iterator for loop
  3323. // oops, we have overlapped something
  3324. return FALSE;
  3325. } // end for, them
  3326. } // end if
  3327. //
  3328. // finally ... if sourceToPathDest is valid ... the source object must be able to
  3329. // find a path the the position we have selected. We want to do this last because
  3330. // a pathfind can sometimes be an relatively expensive operation
  3331. //
  3332. if( options->sourceToPathToDest )
  3333. {
  3334. const AIUpdateInterface *ai = options->sourceToPathToDest->getAIUpdateInterface();
  3335. // check for path existence
  3336. if( ai && TheAI->pathfinder()->clientSafeQuickDoesPathExist( ai->getLocomotorSet(),
  3337. options->sourceToPathToDest->getPosition(),
  3338. &pos ) == FALSE )
  3339. return FALSE;
  3340. } // end if
  3341. // save result and return TRUE for position found
  3342. *result = pos;
  3343. return TRUE;
  3344. } // end tryPosition
  3345. //
  3346. // the following determines how fast we expand our concentric ring search for the
  3347. // find position methods
  3348. //
  3349. static Real ringSpacing = 5.0f;
  3350. //-------------------------------------------------------------------------------------------------
  3351. /** This method will attempt to find a legal postion from the center position specified,
  3352. * at least minRadis away from it, but no more than maxRadius away.
  3353. *
  3354. * Return TRUE if position is found and that position is returned in 'result'
  3355. * return FALSE no legal position exists or invalid params
  3356. */
  3357. //-------------------------------------------------------------------------------------------------
  3358. Bool PartitionManager::findPositionAround( const Coord3D *center,
  3359. const FindPositionOptions *options,
  3360. Coord3D *result )
  3361. {
  3362. // sanity
  3363. if( center == NULL || result == NULL || options == NULL )
  3364. return FALSE;
  3365. Region3D extent;
  3366. TheTerrainLogic->getMaximumPathfindExtent(&extent);
  3367. // If the goal is off the map, it is a scripted setup, so just
  3368. // use the center.
  3369. if (!extent.isInRegionNoZ(center)) {
  3370. *result = *center;
  3371. return true;
  3372. }
  3373. // sanity, FPF_IGNORE_WATER and FPF_WATER_ONLY are mutually exclusive
  3374. DEBUG_ASSERTCRASH( !(BitTest( options->flags, FPF_IGNORE_WATER ) == TRUE &&
  3375. BitTest( options->flags, FPF_WATER_ONLY ) == TRUE),
  3376. ("PartitionManager::findPositionAround - The options FPF_WATER_ONLY and FPF_IGNORE_WATER are mutually exclusive. You cannot use them together\n") );
  3377. // pick a random angle from the center location to start at
  3378. Real startAngle;
  3379. if( options->startAngle == RANDOM_START_ANGLE )
  3380. startAngle = GameLogicRandomValueReal( 0.0f, TWO_PI );
  3381. else
  3382. startAngle = options->startAngle;
  3383. // start the search at the most inner ring (minRadius) and expand to outer most ring (maxRadius)
  3384. for( Real dist = options->minRadius; dist <= options->maxRadius; dist += ringSpacing )
  3385. {
  3386. //
  3387. // given the spacing that we've been using for the angles on the 'idea' inner circle,
  3388. // we will need more points on larger circles to cover more ground
  3389. //
  3390. Real angleSpacing;
  3391. if( dist == options->minRadius )
  3392. angleSpacing = TWO_PI;
  3393. else
  3394. angleSpacing = (ringSpacing / (dist + 1.0f)) * (TWO_PI / 6.0f); // larger float = more samples
  3395. //
  3396. // on this "ring", try all the angles available to us in a circle ... we'll start at
  3397. // the 'startAngle', then try 'angleSpacing' to the left, then 'angleSpacing' to the
  3398. // right, then '2 angleSpacings' to the left etc., ping ponging around the start angle
  3399. // until we've done them all the way around the circle
  3400. //
  3401. // how many samples will we test
  3402. Int samples = REAL_TO_INT_CEIL( (TWO_PI / angleSpacing) / 2.0f);
  3403. for( Int i = 0; i < samples; ++i )
  3404. {
  3405. // try one "side"
  3406. if( tryPosition( center, dist, startAngle + angleSpacing * i, options, result ) == TRUE )
  3407. return TRUE;
  3408. //
  3409. // try the other "side" ... note for the first point at minRadius this position is
  3410. // the same so we won't test it again in that case
  3411. //
  3412. if( i != 0 )
  3413. if( tryPosition( center, dist, startAngle - angleSpacing * i, options, result ) == TRUE )
  3414. return TRUE;
  3415. } // end if
  3416. } // end for, dist
  3417. // no position was able to be found
  3418. return FALSE;
  3419. } // end findPositionAround
  3420. //-----------------------------------------------------------------------------
  3421. // This is the main accessor of the shroud system. At this level, allies are taken
  3422. // into consideration as specified by the caller. Look/Unlook are the ones sending Ally info, as that
  3423. // is in Object where Allies make sense. AddLooker literally just adds a looker for the player you specify.
  3424. // This way, Full map reveals and Observer mode active look will not carry over to all
  3425. // allies. They'll use the RevealWholeDamnMap series, which call addLooker directly.
  3426. void PartitionManager::doShroudReveal(Real centerX, Real centerY, Real radius, PlayerMaskType playerMask)
  3427. {
  3428. Int cellCenterX, cellCenterY;
  3429. ThePartitionManager->worldToCell(centerX, centerY, &cellCenterX, &cellCenterY);
  3430. Int cellRadius = ThePartitionManager->worldToCellDist(radius);
  3431. if (cellRadius < 1)
  3432. cellRadius = 1;
  3433. DiscreteCircle circle(cellCenterX, cellCenterY, cellRadius);
  3434. for( Int currentIndex = ThePlayerList->getPlayerCount() - 1; currentIndex >=0; currentIndex-- )
  3435. {
  3436. // Object's Look is the one who knows about allies. Anyone can pask a player mask to me and all
  3437. // of those players will have an active looker applied to a bunch of cells
  3438. const Player *currentPlayer = ThePlayerList->getNthPlayer( currentIndex );
  3439. if( BitTest( playerMask, currentPlayer->getPlayerMask() ) )
  3440. {
  3441. circle.drawCircle(hLineAddLooker, (void*)currentIndex);
  3442. }
  3443. }
  3444. }
  3445. //-----------------------------------------------------------------------------
  3446. void PartitionManager::processPendingUndoShroudRevealQueue( Bool considerTimestamp )
  3447. {
  3448. //Keep going until the front one is in the future. UndoShroudReveal on each one you process.
  3449. //Again, you know these are in order, because we control the adding of the Constant in-the-queue time.
  3450. UnsignedInt compareTime;
  3451. if(considerTimestamp)
  3452. compareTime = TheGameLogic->getFrame();
  3453. else
  3454. compareTime = UINT_MAX;
  3455. while( !m_pendingUndoShroudReveals.empty() && (m_pendingUndoShroudReveals.front()->m_data < compareTime) )
  3456. {
  3457. SightingInfo *thisInfo = m_pendingUndoShroudReveals.front();
  3458. undoShroudReveal( thisInfo->m_where.x, thisInfo->m_where.y, thisInfo->m_howFar, thisInfo->m_forWhom );
  3459. thisInfo->deleteInstance();
  3460. m_pendingUndoShroudReveals.pop();
  3461. }
  3462. }
  3463. //-----------------------------------------------------------------------------
  3464. void PartitionManager::processEntirePendingUndoShroudRevealQueue()
  3465. {
  3466. // Something major is about to happen, so we need to rush through our queue and get everything in order.
  3467. // Examples are map resize, full map undo-reveal, stuff like that.
  3468. // Don't like duplicating code, so route to original function.
  3469. processPendingUndoShroudRevealQueue(FALSE);
  3470. }
  3471. //-----------------------------------------------------------------------------
  3472. void PartitionManager::resetPendingUndoShroudRevealQueue()
  3473. {
  3474. while( !m_pendingUndoShroudReveals.empty() )
  3475. {
  3476. SightingInfo *thisInfo = m_pendingUndoShroudReveals.front();
  3477. thisInfo->deleteInstance();
  3478. m_pendingUndoShroudReveals.pop();
  3479. }
  3480. }
  3481. //-----------------------------------------------------------------------------
  3482. void PartitionManager::undoShroudReveal(Real centerX, Real centerY, Real radius, PlayerMaskType playerMask)
  3483. {
  3484. Int cellCenterX, cellCenterY;
  3485. ThePartitionManager->worldToCell(centerX, centerY, &cellCenterX, &cellCenterY);
  3486. Int cellRadius = ThePartitionManager->worldToCellDist(radius);
  3487. if (cellRadius < 1)
  3488. cellRadius = 1;
  3489. DiscreteCircle circle(cellCenterX, cellCenterY, cellRadius);
  3490. for( Int currentIndex = ThePlayerList->getPlayerCount() - 1; currentIndex >=0; currentIndex-- )
  3491. {
  3492. const Player *currentPlayer = ThePlayerList->getNthPlayer( currentIndex );
  3493. if( BitTest( playerMask, currentPlayer->getPlayerMask() ) )
  3494. {
  3495. circle.drawCircle(hLineRemoveLooker, (void*)currentIndex);
  3496. }
  3497. }
  3498. }
  3499. //-----------------------------------------------------------------------------
  3500. void PartitionManager::queueUndoShroudReveal(Real centerX, Real centerY, Real radius, PlayerMaskType playerMask)
  3501. {
  3502. UnsignedInt now = TheGameLogic->getFrame();
  3503. SightingInfo *newInfo = newInstance(SightingInfo);
  3504. newInfo->m_where.x = centerX;
  3505. newInfo->m_where.y = centerY;
  3506. newInfo->m_howFar = radius;
  3507. newInfo->m_forWhom = playerMask;
  3508. newInfo->m_data = now + TheGlobalData->m_unlookPersistDuration;
  3509. m_pendingUndoShroudReveals.push(newInfo);
  3510. }
  3511. //-----------------------------------------------------------------------------
  3512. void PartitionManager::doShroudCover(Real centerX, Real centerY, Real radius, PlayerMaskType playerMask)
  3513. {
  3514. Int cellCenterX, cellCenterY;
  3515. ThePartitionManager->worldToCell(centerX, centerY, &cellCenterX, &cellCenterY);
  3516. Int cellRadius = ThePartitionManager->worldToCellDist(radius);
  3517. if (cellRadius < 1)
  3518. cellRadius = 1;
  3519. DiscreteCircle circle(cellCenterX, cellCenterY, cellRadius);
  3520. for( Int currentIndex = ThePlayerList->getPlayerCount() - 1; currentIndex >=0; currentIndex-- )
  3521. {
  3522. // Object's Shroud is the one who knows about allies. Anyone can pask a player mask to me and all
  3523. // of those players will have an active shrouder applied to a bunch of cells
  3524. const Player *currentPlayer = ThePlayerList->getNthPlayer( currentIndex );
  3525. if( BitTest( playerMask, currentPlayer->getPlayerMask() ) )
  3526. {
  3527. circle.drawCircle(hLineAddShrouder, (void*)currentIndex);
  3528. }
  3529. }
  3530. }
  3531. //-----------------------------------------------------------------------------
  3532. void PartitionManager::undoShroudCover(Real centerX, Real centerY, Real radius, PlayerMaskType playerMask)
  3533. {
  3534. Int cellCenterX, cellCenterY;
  3535. ThePartitionManager->worldToCell(centerX, centerY, &cellCenterX, &cellCenterY);
  3536. Int cellRadius = ThePartitionManager->worldToCellDist(radius);
  3537. if (cellRadius < 1)
  3538. cellRadius = 1;
  3539. DiscreteCircle circle(cellCenterX, cellCenterY, cellRadius);
  3540. for( Int currentIndex = ThePlayerList->getPlayerCount() - 1; currentIndex >=0; currentIndex-- )
  3541. {
  3542. const Player *currentPlayer = ThePlayerList->getNthPlayer( currentIndex );
  3543. if( BitTest( playerMask, currentPlayer->getPlayerMask() ) )
  3544. {
  3545. circle.drawCircle(hLineRemoveShrouder, (void*)currentIndex);
  3546. }
  3547. }
  3548. }
  3549. //-----------------------------------------------------------------------------
  3550. void PartitionManager::doThreatAffect( Real centerX, Real centerY, Real radius, UnsignedInt threatVal, PlayerMaskType playerMask)
  3551. {
  3552. Int cellCenterX, cellCenterY;
  3553. ThePartitionManager->worldToCell(centerX, centerY, &cellCenterX, &cellCenterY);
  3554. Real fCellCenterX = INT_TO_REAL(cellCenterX);
  3555. Real fCellCenterY = INT_TO_REAL(cellCenterY);
  3556. Int cellRadius = ThePartitionManager->worldToCellDist(radius);
  3557. if (cellRadius < 1)
  3558. cellRadius = 1;
  3559. Real fCellRadius = INT_TO_REAL(cellRadius + 1);
  3560. DiscreteCircle circle(cellCenterX, cellCenterY, cellRadius);
  3561. ThreatValueParms parms;
  3562. parms.radius = fCellRadius;
  3563. parms.threatOrValue = threatVal;
  3564. parms.xCenter = fCellCenterX;
  3565. parms.yCenter = fCellCenterY;
  3566. for( Int currentIndex = ThePlayerList->getPlayerCount() - 1; currentIndex >=0; currentIndex-- )
  3567. {
  3568. const Player *currentPlayer = ThePlayerList->getNthPlayer( currentIndex );
  3569. if( BitTest( playerMask, currentPlayer->getPlayerMask() ) )
  3570. {
  3571. parms.playerIndex = currentIndex;
  3572. circle.drawCircle(hLineAddThreat, &parms);
  3573. }
  3574. }
  3575. }
  3576. //-----------------------------------------------------------------------------
  3577. void PartitionManager::undoThreatAffect( Real centerX, Real centerY, Real radius, UnsignedInt threatVal, PlayerMaskType playerMask)
  3578. {
  3579. Int cellCenterX, cellCenterY;
  3580. ThePartitionManager->worldToCell(centerX, centerY, &cellCenterX, &cellCenterY);
  3581. Real fCellCenterX = INT_TO_REAL(cellCenterX);
  3582. Real fCellCenterY = INT_TO_REAL(cellCenterY);
  3583. Int cellRadius = ThePartitionManager->worldToCellDist(radius);
  3584. if (cellRadius < 1)
  3585. cellRadius = 1;
  3586. Real fCellRadius = INT_TO_REAL(cellRadius + 1);
  3587. DiscreteCircle circle(cellCenterX, cellCenterY, cellRadius);
  3588. ThreatValueParms parms;
  3589. parms.radius = fCellRadius;
  3590. parms.threatOrValue = threatVal;
  3591. parms.xCenter = fCellCenterX;
  3592. parms.yCenter = fCellCenterY;
  3593. for( Int currentIndex = ThePlayerList->getPlayerCount() - 1; currentIndex >=0; currentIndex-- )
  3594. {
  3595. const Player *currentPlayer = ThePlayerList->getNthPlayer( currentIndex );
  3596. if( BitTest( playerMask, currentPlayer->getPlayerMask() ) )
  3597. {
  3598. parms.playerIndex = currentIndex;
  3599. circle.drawCircle(hLineRemoveThreat, &parms);
  3600. }
  3601. }
  3602. }
  3603. //-----------------------------------------------------------------------------
  3604. void PartitionManager::doValueAffect( Real centerX, Real centerY, Real radius, UnsignedInt valueVal, PlayerMaskType playerMask)
  3605. {
  3606. Int cellCenterX, cellCenterY;
  3607. ThePartitionManager->worldToCell(centerX, centerY, &cellCenterX, &cellCenterY);
  3608. Real fCellCenterX = INT_TO_REAL(cellCenterX);
  3609. Real fCellCenterY = INT_TO_REAL(cellCenterY);
  3610. Int cellRadius = ThePartitionManager->worldToCellDist(radius);
  3611. if (cellRadius < 1)
  3612. cellRadius = 1;
  3613. Real fCellRadius = INT_TO_REAL(cellRadius + 1);
  3614. DiscreteCircle circle(cellCenterX, cellCenterY, cellRadius);
  3615. ThreatValueParms parms;
  3616. parms.radius = fCellRadius;
  3617. parms.threatOrValue = valueVal;
  3618. parms.xCenter = fCellCenterX;
  3619. parms.yCenter = fCellCenterY;
  3620. for( Int currentIndex = ThePlayerList->getPlayerCount() - 1; currentIndex >=0; currentIndex-- )
  3621. {
  3622. const Player *currentPlayer = ThePlayerList->getNthPlayer( currentIndex );
  3623. if( BitTest( playerMask, currentPlayer->getPlayerMask() ) )
  3624. {
  3625. parms.playerIndex = currentIndex;
  3626. circle.drawCircle(hLineAddValue, &parms);
  3627. }
  3628. }
  3629. }
  3630. //-----------------------------------------------------------------------------
  3631. void PartitionManager::undoValueAffect( Real centerX, Real centerY, Real radius, UnsignedInt valueVal, PlayerMaskType playerMask)
  3632. {
  3633. Int cellCenterX, cellCenterY;
  3634. ThePartitionManager->worldToCell(centerX, centerY, &cellCenterX, &cellCenterY);
  3635. Real fCellCenterX = INT_TO_REAL(cellCenterX);
  3636. Real fCellCenterY = INT_TO_REAL(cellCenterY);
  3637. Int cellRadius = ThePartitionManager->worldToCellDist(radius);
  3638. if (cellRadius < 1)
  3639. cellRadius = 1;
  3640. Real fCellRadius = INT_TO_REAL(cellRadius + 1);
  3641. DiscreteCircle circle(cellCenterX, cellCenterY, cellRadius);
  3642. ThreatValueParms parms;
  3643. parms.radius = fCellRadius;
  3644. parms.threatOrValue = valueVal;
  3645. parms.xCenter = fCellCenterX;
  3646. parms.yCenter = fCellCenterY;
  3647. for( Int currentIndex = ThePlayerList->getPlayerCount() - 1; currentIndex >=0; currentIndex-- )
  3648. {
  3649. const Player *currentPlayer = ThePlayerList->getNthPlayer( currentIndex );
  3650. if( BitTest( playerMask, currentPlayer->getPlayerMask() ) )
  3651. {
  3652. parms.playerIndex = currentIndex;
  3653. circle.drawCircle(hLineRemoveValue, &parms);
  3654. }
  3655. }
  3656. }
  3657. //-----------------------------------------------------------------------------
  3658. void PartitionManager::getCellCenterPos(Int x, Int y, Real& xx, Real& yy)
  3659. {
  3660. DEBUG_ASSERTCRASH(x >= 0 && y >= 0, ("hmm, invalid cell"));
  3661. Real half = m_cellSize*0.5f;
  3662. xx = m_worldExtents.lo.x + (x * m_cellSize) + half;
  3663. yy = m_worldExtents.lo.y + (y * m_cellSize) + half;
  3664. }
  3665. //-----------------------------------------------------------------------------
  3666. #ifdef PM_CACHE_TERRAIN_HEIGHT
  3667. struct TerrainExtremeData
  3668. {
  3669. Real* minZ;
  3670. Real* maxZ;
  3671. Coord2D* minZPos;
  3672. Coord2D* maxZPos;
  3673. Bool isValid;
  3674. };
  3675. static Int checkTerrainExtreme(PartitionCell* cell, void* userData)
  3676. {
  3677. TerrainExtremeData* data = (TerrainExtremeData*)userData;
  3678. data->isValid = true;
  3679. Real tmp;
  3680. if (data->minZ)
  3681. {
  3682. tmp = cell->getLoTerrain();
  3683. if (tmp < *data->minZ )
  3684. {
  3685. *data->minZ = tmp;
  3686. if (data->minZPos)
  3687. {
  3688. cell->getCellCenterPos(data->minZPos->x, data->minZPos->y);
  3689. }
  3690. }
  3691. }
  3692. if (data->maxZ)
  3693. {
  3694. tmp = cell->getHiTerrain();
  3695. if (tmp > *data->maxZ )
  3696. {
  3697. *data->maxZ = tmp;
  3698. if (data->maxZPos)
  3699. {
  3700. cell->getCellCenterPos(data->maxZPos->x, data->maxZPos->y);
  3701. }
  3702. }
  3703. }
  3704. return 0; // zero to continue
  3705. }
  3706. #endif
  3707. //-----------------------------------------------------------------------------
  3708. #ifdef PM_CACHE_TERRAIN_HEIGHT
  3709. Bool PartitionManager::estimateTerrainExtremesAlongLine(const Coord3D& pos, const Coord3D& posOther, Real* minZ, Real* maxZ, Coord2D* minZPos, Coord2D* maxZPos)
  3710. {
  3711. TerrainExtremeData data;
  3712. data.minZ = minZ;
  3713. data.maxZ = maxZ;
  3714. data.minZPos = minZPos;
  3715. data.maxZPos = maxZPos;
  3716. data.isValid = false;
  3717. if (minZ) *minZ = HUGE_DIST;
  3718. if (maxZ) *maxZ = -HUGE_DIST;
  3719. iterateCellsAlongLine(pos, posOther, checkTerrainExtreme, &data);
  3720. return data.isValid;
  3721. }
  3722. #endif
  3723. //-----------------------------------------------------------------------------
  3724. // Uses Bresenham line algorithm from www.gamedev.net.
  3725. Int PartitionManager::iterateCellsAlongLine(const Coord3D& pos, const Coord3D& posOther, CellAlongLineProc proc, void* userData)
  3726. {
  3727. ICoord2D start, end, delta;
  3728. Int x, y;
  3729. Int xinc1, xinc2;
  3730. Int yinc1, yinc2;
  3731. Int den, num, numadd;
  3732. Int numpixels;
  3733. worldToCell(pos.x, pos.y, &start.x, &start.y);
  3734. worldToCell(posOther.x, posOther.y, &end.x, &end.y);
  3735. delta.x = abs(end.x - start.x); // The difference between the x's
  3736. delta.y = abs(end.y - start.y); // The difference between the y's
  3737. x = start.x; // Start x off at the first pixel
  3738. y = start.y; // Start y off at the first pixel
  3739. if (end.x >= start.x) // The x-values are increasing
  3740. {
  3741. xinc1 = 1;
  3742. xinc2 = 1;
  3743. }
  3744. else // The x-values are decreasing
  3745. {
  3746. xinc1 = -1;
  3747. xinc2 = -1;
  3748. }
  3749. if (end.y >= start.y) // The y-values are increasing
  3750. {
  3751. yinc1 = 1;
  3752. yinc2 = 1;
  3753. }
  3754. else // The y-values are decreasing
  3755. {
  3756. yinc1 = -1;
  3757. yinc2 = -1;
  3758. }
  3759. Bool checkY = true;
  3760. if (delta.x >= delta.y) // There is at least one x-value for every y-value
  3761. {
  3762. xinc1 = 0; // Don't change the x when numerator >= denominator
  3763. yinc2 = 0; // Don't change the y for every iteration
  3764. den = delta.x;
  3765. num = delta.x / 2;
  3766. numadd = delta.y;
  3767. numpixels = delta.x; // There are more x-values than y-values
  3768. }
  3769. else // There is at least one y-value for every x-value
  3770. {
  3771. checkY = false;
  3772. xinc2 = 0; // Don't change the x for every iteration
  3773. yinc1 = 0; // Don't change the y when numerator >= denominator
  3774. den = delta.y;
  3775. num = delta.y / 2;
  3776. numadd = delta.x;
  3777. numpixels = delta.y; // There are more y-values than x-values
  3778. }
  3779. for (Int curpixel = 0; curpixel <= numpixels; curpixel++)
  3780. {
  3781. PartitionCell* cell = ThePartitionManager->getCellAt(x, y); // might be null if off the edge
  3782. DEBUG_ASSERTCRASH(cell != NULL, ("off the map"));
  3783. if (cell)
  3784. {
  3785. Int ret = (*proc)(cell, userData);
  3786. if (ret != 0)
  3787. return ret; // bail early
  3788. }
  3789. num += numadd; // Increase the numerator by the top of the fraction
  3790. if (num >= den) // Check if numerator >= denominator
  3791. {
  3792. num -= den; // Calculate the new numerator value
  3793. x += xinc1; // Change the x as appropriate
  3794. y += yinc1; // Change the y as appropriate
  3795. }
  3796. x += xinc2; // Change the x as appropriate
  3797. y += yinc2; // Change the y as appropriate
  3798. }
  3799. return 0;
  3800. }
  3801. //-----------------------------------------------------------------------------
  3802. Int PartitionManager::iterateCellsBreadthFirst(const Coord3D *pos, CellBreadthFirstProc proc, void *userData)
  3803. {
  3804. // starting at pos, iterate the cells in the following manner:
  3805. // left, up, right, down
  3806. // using Breadth-first search.
  3807. // Call proc on each cell, and if it returns non-zero, then return the cell index
  3808. // -1 means error, but we should add a define later for this.
  3809. Int cellX, cellY;
  3810. ThePartitionManager->worldToCell(pos->x, pos->y, &cellX, &cellY);
  3811. // Note, bool. not Bool, cause bool will cause this to be a bitfield.
  3812. std::vector<bool> bitField;
  3813. Int cellCount = m_cellCountX * m_cellCountY;
  3814. bitField.resize(cellCount);
  3815. // 0 means not done, 1 means done.
  3816. for (Int i = 0; i < cellCount; ++i) {
  3817. bitField[i] = false;
  3818. }
  3819. std::queue<PartitionCell *> cellQ;
  3820. // mark the starting cell done
  3821. bitField[cellY * m_cellCountX + cellX] = true;
  3822. cellQ.push(&m_cells[cellY * m_cellCountX + cellX]);
  3823. Int curX = cellX;
  3824. Int curY = cellY;
  3825. while (!cellQ.empty()) {
  3826. // first, add the neighbors
  3827. // left
  3828. if (curX - 1 >= 0) {
  3829. if (!bitField[curY * m_cellCountX + curX - 1]) {
  3830. bitField[curY * m_cellCountX + curX - 1] = true;
  3831. cellQ.push(&m_cells[curY * m_cellCountX + curX - 1]);
  3832. }
  3833. }
  3834. // top
  3835. if (curY - 1 >= 0) {
  3836. if (!bitField[(curY - 1) * m_cellCountX + curX]) {
  3837. bitField[(curY - 1) * m_cellCountX + curX] = true;
  3838. cellQ.push(&m_cells[(curY - 1) * m_cellCountX + curX]);
  3839. }
  3840. }
  3841. // right
  3842. if (curX + 1 < m_cellCountX) {
  3843. if (!bitField[curY * m_cellCountX + curX + 1]) {
  3844. bitField[curY * m_cellCountX + curX + 1] = true;
  3845. cellQ.push(&m_cells[curY * m_cellCountX + curX + 1]);
  3846. }
  3847. }
  3848. // bottom
  3849. if (curY + 1 > 0) {
  3850. if (!bitField[(curY + 1) * m_cellCountX + curX]) {
  3851. bitField[(curY + 1) * m_cellCountX + curX] = true;
  3852. cellQ.push(&m_cells[(curY + 1) * m_cellCountX + curX]);
  3853. }
  3854. }
  3855. // now process the current top.
  3856. PartitionCell *curCell = cellQ.front();
  3857. cellQ.pop();
  3858. curX = curCell->getCellX();
  3859. curY = curCell->getCellY();
  3860. if ((proc)(curCell, userData) != 0)
  3861. return (curCell->getCellY() * m_cellCountX + curCell->getCellX());
  3862. }
  3863. return -1;
  3864. }
  3865. //-----------------------------------------------------------------------------
  3866. static Real calcDist2D(Real x1, Real y1, Real x2, Real y2)
  3867. {
  3868. return sqrtf(sqr(x1-x2) + sqr(y1-y2));
  3869. }
  3870. //-----------------------------------------------------------------------------
  3871. Bool PartitionManager::isClearLineOfSightTerrain(const Object* obj, const Coord3D& objPos, const Object* other, const Coord3D& otherPos)
  3872. {
  3873. Coord3D pos, posOther;
  3874. if (obj)
  3875. {
  3876. pos = *obj->getPosition();
  3877. // note that we want to measure from the top of the collision
  3878. // shape, not the bottom! (most objects have eyes a lot closer
  3879. // to their head than their feet. if we have really odd critters
  3880. // with eye-feet, we'll need to change this assumption.)
  3881. pos.z += obj->getGeometryInfo().getMaxHeightAbovePosition();
  3882. }
  3883. else
  3884. {
  3885. pos = objPos;
  3886. }
  3887. if (other)
  3888. {
  3889. posOther = *other->getPosition();
  3890. // note that we want to measure from the top of the collision
  3891. // shape, not the bottom! (most objects have eyes a lot closer
  3892. // to their head than their feet. if we have really odd critters
  3893. // with eye-feet, we'll need to change this assumption.)
  3894. posOther.z += other->getGeometryInfo().getMaxHeightAbovePosition();
  3895. }
  3896. else
  3897. {
  3898. posOther = otherPos;
  3899. }
  3900. #ifdef NO_BAD_AND_INACCURATE
  3901. /*
  3902. This looks tempting, and in theory would be better than the below code; however, it
  3903. is not (yet) accurate enough for our targeting needs. what we really need to do is add a Bresenham
  3904. line iterator to the TerrainLogic itself, which would probably suffice. Until then, this code is
  3905. left in as an example of what NOT to do. (srj)
  3906. */
  3907. Real maxZ;
  3908. Coord2D maxZPos;
  3909. Bool valid = estimateTerrainExtremesAlongLine(pos, posOther, NULL, &maxZ, NULL, &maxZPos);
  3910. DEBUG_ASSERTCRASH(valid, ("this should never happen unless both positions are off-map"));
  3911. if (!valid)
  3912. return true;
  3913. Real terrainAtHighPoint = maxZ;
  3914. Real totalDist2D = calcDist2D(pos.x, pos.y, posOther.x, posOther.y);
  3915. Real terrainDist2D = calcDist2D(pos.x, pos.y, maxZPos.x, maxZPos.y);
  3916. const Real TINY_DIST = 0.01f;
  3917. Real percent = (totalDist2D > TINY_DIST) ? (terrainDist2D / totalDist2D) : 0.0f;
  3918. if (percent < 0.0f) percent = 0.0f;
  3919. if (percent > 1.0f) percent = 1.0f;
  3920. Real lineOfSightAtHighPoint = pos.z + (posOther.z - pos.z) * percent;
  3921. // if terrainAtHighPoint > lineOfSightAtHighPoint, we can't see.
  3922. // add a little fudge to account for slop.
  3923. const Real LOS_FUDGE = 0.5f;
  3924. if (terrainAtHighPoint > lineOfSightAtHighPoint + LOS_FUDGE)
  3925. {
  3926. //DEBUG_LOG(("isClearLineOfSightTerrain fails\n"));
  3927. return false;
  3928. }
  3929. return true;
  3930. #else
  3931. return TheTerrainLogic->isClearLineOfSight(pos, posOther);
  3932. #endif
  3933. }
  3934. // ------------------------------------------------------------------------------------------------
  3935. /** CRC */
  3936. // ------------------------------------------------------------------------------------------------
  3937. void PartitionManager::crc( Xfer *xfer )
  3938. {
  3939. for (Int i=0; i<m_totalCellCount; ++i)
  3940. {
  3941. m_cells[i].crc(xfer);
  3942. }
  3943. } // end crc
  3944. // ------------------------------------------------------------------------------------------------
  3945. /** Xfer Method
  3946. * Version Info:
  3947. * 1: Initial version
  3948. * 2: m_pendingUndoShroudReveals stores Unlooks waiting to happen.
  3949. */
  3950. // ------------------------------------------------------------------------------------------------
  3951. void PartitionManager::xfer( Xfer *xfer )
  3952. {
  3953. // version
  3954. XferVersion currentVersion = 2;
  3955. XferVersion version = currentVersion;
  3956. xfer->xferVersion( &version, currentVersion );
  3957. // cell size information
  3958. Real cellSize = m_cellSize;
  3959. xfer->xferReal( &cellSize );
  3960. //
  3961. // cell size should match what we have allocated in the partition manager, if it doesn't
  3962. // we need to increment the version here and edit this code to be able to load
  3963. // the old cell size if changed after we release
  3964. //
  3965. if( cellSize != m_cellSize )
  3966. {
  3967. DEBUG_CRASH(( "Partition cell size has changed, this save game file is invalid\n" ));
  3968. throw SC_INVALID_DATA;
  3969. } // end if
  3970. // cell count
  3971. Int totalCellCount = m_totalCellCount;
  3972. xfer->xferInt( &totalCellCount );
  3973. // total cell count better match what we have allocated for cells in the partition manager
  3974. if( totalCellCount != m_totalCellCount )
  3975. {
  3976. DEBUG_CRASH(( "Partition total cell count mismatch %d, should be %d\n",
  3977. totalCellCount, m_totalCellCount ));
  3978. throw SC_INVALID_DATA;
  3979. } // end if
  3980. // xfer each cell information
  3981. PartitionCell *cell;
  3982. for( Int i = 0; i < totalCellCount; ++i )
  3983. {
  3984. // get this partition cell
  3985. cell = &m_cells[ i ];
  3986. // xfer the data for this cell
  3987. xfer->xferSnapshot( cell );
  3988. } // end for i
  3989. // when loading tell the partition manager to rethink and refresh all shroud information
  3990. if( xfer->getXferMode() == XFER_LOAD )
  3991. {
  3992. // tell partition manager to re-evaluate shroud things when next asked
  3993. m_updatedSinceLastReset = FALSE;
  3994. // refresh the shroud for the local player which will update the radar and everything
  3995. refreshShroudForLocalPlayer();
  3996. } // end if
  3997. if( version >= 2 )
  3998. {
  3999. Int queueSize = m_pendingUndoShroudReveals.size();
  4000. xfer->xferInt(&queueSize);
  4001. // This xfer thing is still cool. On save, I just wrote the number of elements. On load, the first line
  4002. // above was a decoy, and the second found out the size.
  4003. if(xfer->getXferMode() == XFER_LOAD)
  4004. {
  4005. // have to remove this assert, because during load there is a setTeam call for each guy on a sub-team, and that results
  4006. // in a queued unlook, so we actually have stuff in here at the start. I am fairly certain that setTeam should wait
  4007. // until loadPostProcess, but I ain't gonna change it now.
  4008. // DEBUG_ASSERTCRASH(m_pendingUndoShroudReveals.size() == 0, ("At load, we appear to not be in a reset state.") );
  4009. // I have to split this up though, since on Load I need to make new instances.
  4010. for( Int infoIndex = 0; infoIndex < queueSize; infoIndex++ )
  4011. {
  4012. SightingInfo *newInfo = newInstance(SightingInfo);
  4013. xfer->xferSnapshot(newInfo);
  4014. m_pendingUndoShroudReveals.push(newInfo);
  4015. }
  4016. }
  4017. else
  4018. {
  4019. // And on Save, I need to loop through, but not destroy anything, so I pop-push n times, cycling through the queue once
  4020. // (No random access on queues, and speed not a concern in saveload)
  4021. for( Int infoIndex = 0; infoIndex < queueSize; infoIndex++ )
  4022. {
  4023. SightingInfo *saveInfo = m_pendingUndoShroudReveals.front();
  4024. xfer->xferSnapshot(saveInfo);
  4025. m_pendingUndoShroudReveals.pop();
  4026. m_pendingUndoShroudReveals.push(saveInfo);
  4027. }
  4028. }
  4029. }
  4030. else
  4031. {
  4032. // Version 1 save games will just not have any SightingInfos in the queue to be undone.
  4033. }
  4034. } // end xfer
  4035. // ------------------------------------------------------------------------------------------------
  4036. /** Load post process */
  4037. // ------------------------------------------------------------------------------------------------
  4038. void PartitionManager::loadPostProcess( void )
  4039. {
  4040. } // end loadPostProcess
  4041. //-----------------------------------------------------------------------------
  4042. Real PartitionManager::getGroundOrStructureHeight(Real posx, Real posy)
  4043. {
  4044. // get the terrain height
  4045. Real terrainHeightHere = TheTerrainLogic->getGroundHeight( posx, posy );
  4046. // scan all objects in the radius of our extent and find the tallest height among them
  4047. PartitionFilterAcceptByKindOf filter1( MAKE_KINDOF_MASK( KINDOF_STRUCTURE ), KINDOFMASK_NONE );
  4048. PartitionFilter *filters[] = { &filter1, NULL };
  4049. Coord3D pos;
  4050. pos.x = posx;
  4051. pos.y = posy;
  4052. pos.z = terrainHeightHere;
  4053. const Real RANGE = 1.0f;
  4054. ObjectIterator *iter = iterateObjectsInRange( &pos, RANGE, FROM_BOUNDINGSPHERE_2D, filters );
  4055. MemoryPoolObjectHolder hold( iter );
  4056. Real tallestHeight = 0.0f;
  4057. Real thisHeight;
  4058. for( Object* obj = iter->first(); obj; obj = iter->next() )
  4059. {
  4060. // store the height of the tallest object under us
  4061. thisHeight = obj->getGeometryInfo().getMaxHeightAbovePosition();
  4062. if( thisHeight > tallestHeight )
  4063. tallestHeight = thisHeight;
  4064. }
  4065. return terrainHeightHere + tallestHeight;
  4066. }
  4067. //-------------------------------------------------------------------------------------------------
  4068. void PartitionManager::getMostValuableLocation( Int playerIndex, UnsignedInt whichPlayerTypes, ValueOrThreat valType, Coord3D *outLocation )
  4069. {
  4070. if (!outLocation)
  4071. return;
  4072. PlayerMaskType playerMask = ThePlayerList->getPlayersWithRelationship(playerIndex, whichPlayerTypes);
  4073. if (playerMask == 0)
  4074. return;
  4075. Int cellCount = m_cellCountX * m_cellCountY;
  4076. PlayerMaskType allPlayerMasks[MAX_PLAYER_COUNT] = { 0 };
  4077. Int totalPlayerCount = ThePlayerList->getPlayerCount();
  4078. Int i;
  4079. for (i = 0; i < totalPlayerCount; ++i) {
  4080. Player *player = ThePlayerList->getNthPlayer(i);
  4081. if (!player) {
  4082. continue;
  4083. }
  4084. allPlayerMasks[i] = player->getPlayerMask();
  4085. }
  4086. Int greatestValueCell = -1;
  4087. Int maxCellValue = -1;
  4088. for (i = 0; i < cellCount; ++i) {
  4089. Int cellValue = 0;
  4090. for (Int player = 0; player < MAX_PLAYER_COUNT; ++player) {
  4091. if (BitTest(allPlayerMasks[player], playerMask)) {
  4092. if (valType == VOT_CashValue) {
  4093. cellValue += m_cells[i].getCashValue(player);
  4094. } else {
  4095. cellValue += m_cells[i].getThreatValue(player);
  4096. }
  4097. }
  4098. }
  4099. if (cellValue > maxCellValue) {
  4100. maxCellValue = cellValue;
  4101. greatestValueCell = i;
  4102. }
  4103. }
  4104. if (greatestValueCell == -1 || maxCellValue == -1) {
  4105. DEBUG_CRASH(("PartitionManager::getMostValuableLocation: jkmcd"));
  4106. return;
  4107. }
  4108. outLocation->set(m_cells[greatestValueCell].getCellX() * TheGlobalData->m_partitionCellSize,
  4109. m_cells[greatestValueCell].getCellY() * TheGlobalData->m_partitionCellSize,
  4110. 0
  4111. );
  4112. }
  4113. //-------------------------------------------------------------------------------------------------
  4114. void PartitionManager::getNearestGroupWithValue( Int playerIndex, UnsignedInt whichPlayerTypes, ValueOrThreat valType,
  4115. const Coord3D *sourceLocation, Int valueRequired, Bool greaterThan, Coord3D *outLocation )
  4116. {
  4117. if (!(sourceLocation && outLocation))
  4118. return;
  4119. PlayerMaskType playerMask = ThePlayerList->getPlayersWithRelationship(playerIndex, whichPlayerTypes);
  4120. if (playerMask == 0)
  4121. return;
  4122. PlayerMaskType allPlayerMasks[MAX_PLAYER_COUNT] = { 0 };
  4123. Int totalPlayerCount = ThePlayerList->getPlayerCount();
  4124. Int i;
  4125. for (i = 0; i < totalPlayerCount; ++i) {
  4126. Player *player = ThePlayerList->getNthPlayer(i);
  4127. if (!player) {
  4128. continue;
  4129. }
  4130. allPlayerMasks[i] = player->getPlayerMask();
  4131. }
  4132. CellValueProcParms parms;
  4133. parms.valueRequired = valueRequired;
  4134. parms.greaterThan = valueRequired;
  4135. parms.valueType = valType;
  4136. parms.allowedPlayersMasks = playerMask;
  4137. for (i = 0; i < MAX_PLAYER_COUNT; ++i)
  4138. parms.allPlayersMask[i] = allPlayerMasks[i];
  4139. Int nearestGreat = iterateCellsBreadthFirst(sourceLocation, cellValueProc, &parms);
  4140. if (nearestGreat != -1) {
  4141. (*outLocation).x = m_cells[nearestGreat].getCellX() * TheGlobalData->m_partitionCellSize;
  4142. (*outLocation).y = m_cells[nearestGreat].getCellY() * TheGlobalData->m_partitionCellSize;
  4143. (*outLocation).z = 0;
  4144. }
  4145. // all done
  4146. }
  4147. //-------------------------------------------------------------------------------------------------
  4148. void PartitionManager::storeFoggedCells(ShroudStatusStoreRestore &outPartitionStore, Bool storeToFog) const
  4149. {
  4150. Int i, j, p;
  4151. if (storeToFog) {
  4152. // This is the first pass
  4153. outPartitionStore.m_cellsWide = m_cellCountX;
  4154. // Resize all of our arrays.
  4155. for (p = 0; p < MAX_PLAYER_COUNT; ++p) {
  4156. outPartitionStore.m_foggedOrRevealed[p].resize(m_totalCellCount);
  4157. for (i = 0; i < m_totalCellCount; ++i) {
  4158. outPartitionStore.m_foggedOrRevealed[p][i] = STORE_DONTTOUCH;
  4159. }
  4160. }
  4161. }
  4162. for (p = 0; p < MAX_PLAYER_COUNT; ++p) {
  4163. if (outPartitionStore.m_foggedOrRevealed[p].size() != m_totalCellCount) {
  4164. DEBUG_CRASH(("PartitionManager::storeFoggedCells: jkmcd - x36872"));
  4165. continue;
  4166. }
  4167. for (j = 0; j < m_cellCountY; ++j) {
  4168. for (i = 0; i < m_cellCountX; ++i) {
  4169. UnsignedByte &byteToWrite = outPartitionStore.m_foggedOrRevealed[p][j * m_cellCountX + i];
  4170. if (storeToFog && m_cells[j * m_cellCountX + i].getShroudStatusForPlayer(p) == CELLSHROUD_FOGGED) {
  4171. byteToWrite = STORE_FOG;
  4172. }
  4173. if (!storeToFog && m_cells[j * m_cellCountX + i].getShroudStatusForPlayer(p) == CELLSHROUD_CLEAR) {
  4174. byteToWrite = STORE_PERMANENTLY_REVEALED;
  4175. }
  4176. }
  4177. }
  4178. }
  4179. }
  4180. //-------------------------------------------------------------------------------------------------
  4181. void PartitionManager::restoreFoggedCells(const ShroudStatusStoreRestore &inPartitionStore, Bool restoreToFog)
  4182. {
  4183. Int i, j, p;
  4184. Int storeWidth = inPartitionStore.m_cellsWide;
  4185. Int storeHeight = inPartitionStore.m_foggedOrRevealed[0].size() / storeWidth;
  4186. for (p = 0; p < MAX_PLAYER_COUNT; ++p) {
  4187. for (j = 0; j < storeHeight; ++j) {
  4188. if (j >= m_cellCountY) {
  4189. // All done.
  4190. return;
  4191. }
  4192. for (i = 0; i < storeWidth; ++i) {
  4193. if (i >= m_cellCountX) {
  4194. // Skip to the next line. This info will be thrown away.
  4195. break;
  4196. }
  4197. UnsignedByte byteToRestore = inPartitionStore.m_foggedOrRevealed[p][j * storeWidth + i];
  4198. if (byteToRestore == STORE_DONTTOUCH) {
  4199. continue;
  4200. }
  4201. if (byteToRestore == STORE_FOG && restoreToFog) {
  4202. // restore the fog status of this cell
  4203. m_cells[j * m_cellCountX + i].addLooker(p);
  4204. m_cells[j * m_cellCountX + i].removeLooker(p);
  4205. }
  4206. if (byteToRestore == STORE_PERMANENTLY_REVEALED && !restoreToFog) {
  4207. // Add an extra looker.
  4208. m_cells[j * m_cellCountX + i].addLooker(p);
  4209. }
  4210. }
  4211. }
  4212. }
  4213. }
  4214. //-----------------------------------------------------------------------------
  4215. //-----------------------------------------------------------------------------
  4216. //-----------------------------------------------------------------------------
  4217. //-----------------------------------------------------------------------------
  4218. PartitionFilterRejectBuildings::PartitionFilterRejectBuildings(const Object *o) :
  4219. m_self(o),
  4220. m_acquireEnemies(false)
  4221. {
  4222. // if I am a computer-controlled opponent, auto-aquire enemy buildings
  4223. if (m_self->getControllingPlayer()->getPlayerType() == PLAYER_COMPUTER)
  4224. {
  4225. m_acquireEnemies = true;
  4226. }
  4227. }
  4228. //-----------------------------------------------------------------------------
  4229. Bool PartitionFilterRejectBuildings::allow( Object *other )
  4230. {
  4231. // this filter allows all non-buildings
  4232. // um, no, it clearly doesn't any more.
  4233. // didn't the person adding the code below read the comment?
  4234. if (!other->isKindOf( KINDOF_STRUCTURE ))
  4235. return true;
  4236. const Player* myPlayer = m_self->getControllingPlayer();
  4237. if (!myPlayer)
  4238. return false;
  4239. // Get the controlling team of other.
  4240. ContainModuleInterface* contain = other->getContain();
  4241. const Player* otherPlayer = contain ? contain->getApparentControllingPlayer(myPlayer) : NULL;
  4242. if (!otherPlayer)
  4243. otherPlayer = other->getControllingPlayer();
  4244. if (!otherPlayer)
  4245. return false;
  4246. Relationship relationship = myPlayer->getRelationship(otherPlayer->getDefaultTeam());
  4247. if (relationship != ENEMIES)
  4248. return false;
  4249. // if I am a computer-controlled opponent, auto-aquire enemy buildings (if we can see them!)
  4250. if (m_acquireEnemies)
  4251. return true;
  4252. if (other->isKindOf( KINDOF_FS_BASE_DEFENSE))
  4253. {
  4254. // Don't reject base defenses.
  4255. return true;
  4256. }
  4257. if (other->getContain() != NULL && other->isAbleToAttack())
  4258. {
  4259. // Don't reject garrisoned buildings that can attack
  4260. return true;
  4261. }
  4262. return false;
  4263. }
  4264. //-----------------------------------------------------------------------------
  4265. //-----------------------------------------------------------------------------
  4266. Bool PartitionFilterFreeOfFog::allow( Object *other )
  4267. {
  4268. return other->getShroudedStatus(m_comparisonIndex) == OBJECTSHROUD_CLEAR;
  4269. }
  4270. //-----------------------------------------------------------------------------
  4271. //-----------------------------------------------------------------------------
  4272. Bool PartitionFilterInsignificantBuildings::allow( Object *other )
  4273. {
  4274. if (other->isStructure()) {
  4275. if (other->isNonFactionStructure() && !m_allowInsignificant) {
  4276. ContainModuleInterface *cmi = other->getContain();
  4277. if (cmi) {
  4278. if (!cmi->isGarrisonable() || cmi->getContainCount() == 0) {
  4279. return false;
  4280. }
  4281. }
  4282. }
  4283. return true;
  4284. } else {
  4285. if (m_allowNonBuildings) {
  4286. return true;
  4287. }
  4288. }
  4289. return false;
  4290. }
  4291. //-----------------------------------------------------------------------------
  4292. //-----------------------------------------------------------------------------
  4293. //-----------------------------------------------------------------------------
  4294. //-----------------------------------------------------------------------------
  4295. Bool PartitionFilterRepulsor::allow( Object *other )
  4296. {
  4297. if (other == m_self)
  4298. {
  4299. // don't repulse yourself. :)
  4300. return false;
  4301. }
  4302. // If it's flagged, it's a repulsor.
  4303. if (other->testStatus(OBJECT_STATUS_REPULSOR))
  4304. {
  4305. return true;
  4306. }
  4307. if (other->isEffectivelyDead())
  4308. return false; // no dead enemies.
  4309. Relationship r = m_self->getRelationship(other);
  4310. if (r != ENEMIES)
  4311. {
  4312. return false; // only enemies auto repulse.
  4313. }
  4314. if (other->isKindOf( KINDOF_STRUCTURE ))
  4315. {
  4316. // always pay attention to buildings that can attack
  4317. if (other->isAbleToAttack())
  4318. return true;
  4319. return false;
  4320. }
  4321. if ( other->isKindOf( KINDOF_INERT ))
  4322. return false;
  4323. if ( ! other->isAbleToAttack() )
  4324. {
  4325. return false;
  4326. }
  4327. return true;
  4328. }
  4329. //-----------------------------------------------------------------------------
  4330. //-----------------------------------------------------------------------------
  4331. //-----------------------------------------------------------------------------
  4332. //-----------------------------------------------------------------------------
  4333. Bool PartitionFilterIrregularArea::allow( Object *other )
  4334. {
  4335. return PointInsideArea2D(other->getPosition(), m_area, m_numPointsInArea);
  4336. }
  4337. //-----------------------------------------------------------------------------
  4338. Bool PartitionFilterPolygonTrigger::allow( Object *other )
  4339. {
  4340. ICoord3D iPos;
  4341. iPos.x = other->getPosition()->x;
  4342. iPos.y = other->getPosition()->y;
  4343. iPos.z = 0; // Trigger areas compare on xy only.
  4344. return m_trigger->pointInTrigger(iPos);
  4345. }
  4346. //-----------------------------------------------------------------------------
  4347. Bool PartitionFilterPlayer::allow( Object *other )
  4348. {
  4349. return ((m_player == other->getControllingPlayer()) == m_match);
  4350. }
  4351. //-----------------------------------------------------------------------------
  4352. Bool PartitionFilterPlayerAffiliation::allow( Object *other )
  4353. {
  4354. Relationship rel = m_player->getRelationship(other->getTeam());
  4355. switch (rel)
  4356. {
  4357. case ENEMIES:
  4358. if (m_affiliation & ALLOW_ENEMIES) {
  4359. return m_match;
  4360. }
  4361. break;
  4362. case NEUTRAL:
  4363. if (m_affiliation & ALLOW_NEUTRAL) {
  4364. return m_match;
  4365. }
  4366. break;
  4367. case ALLIES:
  4368. if (m_affiliation & ALLOW_ALLIES) {
  4369. return m_match;
  4370. }
  4371. break;
  4372. }
  4373. if (other->getControllingPlayer() == m_player) {
  4374. return m_match;
  4375. }
  4376. return !m_match;
  4377. }
  4378. //-----------------------------------------------------------------------------
  4379. Bool PartitionFilterThing::allow( Object *other )
  4380. {
  4381. return (m_tThing->isEquivalentTo(other->getTemplate()) == m_match);
  4382. }
  4383. //-----------------------------------------------------------------------------
  4384. Bool PartitionFilterGarrisonable::allow( Object *other )
  4385. {
  4386. ContainModuleInterface *cmi = other->getContain();
  4387. if (!cmi) {
  4388. return !m_match;
  4389. }
  4390. return (cmi->isGarrisonable() == m_match);
  4391. }
  4392. //-----------------------------------------------------------------------------
  4393. Bool PartitionFilterGarrisonableByPlayer::allow( Object *other )
  4394. {
  4395. return TheActionManager->canPlayerGarrison(m_player, other, m_commandSource) == m_match;
  4396. }
  4397. //-----------------------------------------------------------------------------
  4398. Bool PartitionFilterUnmannedObject::allow( Object *other )
  4399. {
  4400. return (other->isDisabledByType( DISABLED_UNMANNED ) == m_match);
  4401. }
  4402. //-----------------------------------------------------------------------------
  4403. Bool PartitionFilterValidCommandButtonTarget::allow( Object *other )
  4404. {
  4405. return (m_commandButton->isValidToUseOn(m_source, other, NULL, m_commandSource) == m_match);
  4406. }
  4407. //-----------------------------------------------------------------------------
  4408. //-----------------------------------------------------------------------------
  4409. //-----------------------------------------------------------------------------
  4410. //-----------------------------------------------------------------------------
  4411. Bool PartitionFilterIsFlying::allow(Object *objOther)
  4412. {
  4413. return objOther->isUsingAirborneLocomotor();
  4414. }
  4415. //-----------------------------------------------------------------------------
  4416. //-----------------------------------------------------------------------------
  4417. //-----------------------------------------------------------------------------
  4418. //-----------------------------------------------------------------------------
  4419. PartitionFilterWouldCollide::PartitionFilterWouldCollide(const Coord3D& pos, const GeometryInfo& geom, Real angle, Bool desired) :
  4420. m_position(pos),
  4421. m_geom(geom),
  4422. m_angle(angle),
  4423. m_desiredCollisionResult(desired)
  4424. {
  4425. }
  4426. //-----------------------------------------------------------------------------
  4427. Bool PartitionFilterWouldCollide::allow(Object *objOther)
  4428. {
  4429. CollideInfo thisInfo(&m_position, m_geom, m_angle);
  4430. CollideInfo thatInfo(objOther->getPosition(), objOther->getGeometryInfo(), objOther->getOrientation());
  4431. Bool doesCollide;
  4432. // invariant for all geometries: first do z collision check.
  4433. if (thisInfo.position.z + thisInfo.geom.getMaxHeightAbovePosition() >= thatInfo.position.z &&
  4434. thisInfo.position.z <= thatInfo.position.z + thatInfo.geom.getMaxHeightAbovePosition())
  4435. {
  4436. GeometryType thisGeom = m_geom.getGeomType();
  4437. GeometryType thatGeom = objOther->getGeometryInfo().getGeomType();
  4438. //
  4439. // NOTE: This assumes geometry enumerations that start at GEOMETRY_FIRST AND depends on the
  4440. // order in which they appear in the enum list
  4441. //
  4442. CollideTestProc collideProc = theCollideTestProcs[ (thisGeom - GEOMETRY_FIRST) * GEOMETRY_NUM_TYPES + (thatGeom - GEOMETRY_FIRST) ];
  4443. CollideLocAndNormal cinfo;
  4444. doesCollide = (*collideProc)(&thisInfo, &thatInfo, &cinfo);
  4445. }
  4446. else
  4447. {
  4448. // no z-intersection -> no collision.
  4449. doesCollide = false;
  4450. }
  4451. return doesCollide == m_desiredCollisionResult;
  4452. }
  4453. //-----------------------------------------------------------------------------
  4454. //-----------------------------------------------------------------------------
  4455. //-----------------------------------------------------------------------------
  4456. //-----------------------------------------------------------------------------
  4457. Bool PartitionFilterSamePlayer::allow(Object *objOther)
  4458. {
  4459. if (m_player == objOther->getControllingPlayer())
  4460. return true;
  4461. return false;
  4462. }
  4463. //-----------------------------------------------------------------------------
  4464. //-----------------------------------------------------------------------------
  4465. //-----------------------------------------------------------------------------
  4466. //-----------------------------------------------------------------------------
  4467. Bool PartitionFilterRelationship::allow(Object *objOther)
  4468. {
  4469. Relationship r = m_obj->getRelationship(objOther);
  4470. if ((m_flags & (1<<r)) != 0)
  4471. return true;
  4472. return false;
  4473. }
  4474. //-----------------------------------------------------------------------------
  4475. //-----------------------------------------------------------------------------
  4476. //-----------------------------------------------------------------------------
  4477. //-----------------------------------------------------------------------------
  4478. PartitionFilterAcceptOnTeam::PartitionFilterAcceptOnTeam(const Team *team) : m_team(team)
  4479. {
  4480. }
  4481. //-----------------------------------------------------------------------------
  4482. Bool PartitionFilterAcceptOnTeam::allow(Object *objOther)
  4483. {
  4484. // objOther is guaranteed to be non-null, so we don't need to check (srj)
  4485. return (objOther->getTeam() == m_team);
  4486. }
  4487. //-----------------------------------------------------------------------------
  4488. //-----------------------------------------------------------------------------
  4489. //-----------------------------------------------------------------------------
  4490. //-----------------------------------------------------------------------------
  4491. PartitionFilterAcceptOnSquad::PartitionFilterAcceptOnSquad(const Squad *squad) : m_squad(squad)
  4492. {
  4493. }
  4494. //-----------------------------------------------------------------------------
  4495. Bool PartitionFilterAcceptOnSquad::allow(Object *objOther)
  4496. {
  4497. return (m_squad && m_squad->isOnSquad(objOther) && !objOther->isEffectivelyDead());
  4498. }
  4499. //-----------------------------------------------------------------------------
  4500. //-----------------------------------------------------------------------------
  4501. //-----------------------------------------------------------------------------
  4502. //-----------------------------------------------------------------------------
  4503. Bool PartitionFilterSameMapStatus::allow(Object* objOther)
  4504. {
  4505. // objOther is guaranteed to be non-null, so we don't need to check (srj)
  4506. return objOther->isOffMap() == m_obj->isOffMap();
  4507. }
  4508. //-----------------------------------------------------------------------------
  4509. //-----------------------------------------------------------------------------
  4510. //-----------------------------------------------------------------------------
  4511. //-----------------------------------------------------------------------------
  4512. Bool PartitionFilterOnMap::allow(Object* objOther)
  4513. {
  4514. // objOther is guaranteed to be non-null, so we don't need to check (srj)
  4515. return !objOther->isOffMap();
  4516. }
  4517. //-----------------------------------------------------------------------------
  4518. //-----------------------------------------------------------------------------
  4519. //-----------------------------------------------------------------------------
  4520. //-----------------------------------------------------------------------------
  4521. Bool PartitionFilterAlive::allow( Object *objOther )
  4522. {
  4523. return !objOther->isEffectivelyDead();
  4524. }
  4525. //-----------------------------------------------------------------------------
  4526. //-----------------------------------------------------------------------------
  4527. //-----------------------------------------------------------------------------
  4528. //-----------------------------------------------------------------------------
  4529. PartitionFilterRejectBehind::PartitionFilterRejectBehind( Object *obj )
  4530. {
  4531. m_obj = obj;
  4532. }
  4533. //-----------------------------------------------------------------------------
  4534. Bool PartitionFilterRejectBehind::allow( Object *other )
  4535. {
  4536. // objOther is guaranteed to be non-null, so we don't need to check (srj)
  4537. //const Coord3D *pos = m_obj->getPosition();
  4538. //const Coord3D *dir = m_obj->getUnitDirectionVector2D();
  4539. Vector3 dir = m_obj->getTransformMatrix()->Get_X_Vector();
  4540. dir.Normalize();
  4541. //const Coord3D *otherPos = other->getPosition();
  4542. Coord3D v;
  4543. ThePartitionManager->getVectorTo( m_obj, other, FROM_CENTER_3D, v );
  4544. Real dot = dir.X * v.x + dir.Y * v.y + dir.Z * v.z;
  4545. if (dot > 0.0f)
  4546. return true;
  4547. return false;
  4548. }
  4549. //-----------------------------------------------------------------------------
  4550. PartitionFilterLineOfSight::PartitionFilterLineOfSight(const Object *obj)
  4551. {
  4552. m_obj = obj;
  4553. }
  4554. //-----------------------------------------------------------------------------
  4555. Bool PartitionFilterLineOfSight::allow(Object *objOther)
  4556. {
  4557. // objOther is guaranteed to be non-null, so we don't need to check (srj)
  4558. if (!ThePartitionManager->isClearLineOfSightTerrain(m_obj, *m_obj->getPosition(), objOther, *objOther->getPosition()))
  4559. return false;
  4560. if (TheAI && TheAI->pathfinder()->isViewBlockedByObstacle(m_obj, objOther))
  4561. return false;
  4562. return true;
  4563. }
  4564. //-----------------------------------------------------------------------------
  4565. //-----------------------------------------------------------------------------
  4566. //-----------------------------------------------------------------------------
  4567. //-----------------------------------------------------------------------------
  4568. PartitionFilterPossibleToAttack::PartitionFilterPossibleToAttack(AbleToAttackType t, const Object *obj, CommandSourceType commandSource) :
  4569. m_attackType(t),
  4570. m_obj(obj),
  4571. m_commandSource(commandSource)
  4572. {
  4573. }
  4574. //-----------------------------------------------------------------------------
  4575. Bool PartitionFilterPossibleToAttack::allow(Object *objOther)
  4576. {
  4577. // objOther is guaranteed to be non-null, so we don't need to check (srj)
  4578. // we should have already filtered out isAbleToAttack!
  4579. #ifdef _DEBUG
  4580. // disable this assert for INTERNAL builds (srj)
  4581. DEBUG_ASSERTCRASH(m_obj && m_obj->isAbleToAttack(), ("if the object is unable to attack at all, you should filter that out ahead of time!"));
  4582. #endif
  4583. CanAttackResult result = m_obj->getAbleToAttackSpecificObject( m_attackType, objOther, m_commandSource );
  4584. if( result == ATTACKRESULT_POSSIBLE || result == ATTACKRESULT_POSSIBLE_AFTER_MOVING )
  4585. {
  4586. return TRUE;
  4587. }
  4588. return FALSE;
  4589. }
  4590. //-----------------------------------------------------------------------------
  4591. //-----------------------------------------------------------------------------
  4592. PartitionFilterPossibleToEnter::PartitionFilterPossibleToEnter(const Object *obj, CommandSourceType commandSource) :
  4593. m_obj(obj),
  4594. m_commandSource(commandSource)
  4595. {
  4596. }
  4597. //-----------------------------------------------------------------------------
  4598. Bool PartitionFilterPossibleToEnter::allow(Object *objOther)
  4599. {
  4600. if (!objOther || !m_obj)
  4601. return FALSE;
  4602. if( TheActionManager->canEnterObject( m_obj, objOther, m_commandSource, DONT_CHECK_CAPACITY ) )
  4603. return TRUE;
  4604. else
  4605. return FALSE;
  4606. }
  4607. //-----------------------------------------------------------------------------
  4608. //-----------------------------------------------------------------------------
  4609. //-----------------------------------------------------------------------------
  4610. PartitionFilterPossibleToHijack::PartitionFilterPossibleToHijack(const Object *obj, CommandSourceType commandSource) :
  4611. m_obj(obj),
  4612. m_commandSource(commandSource)
  4613. {
  4614. }
  4615. //-----------------------------------------------------------------------------
  4616. Bool PartitionFilterPossibleToHijack::allow(Object *objOther)
  4617. {
  4618. if (!objOther || !m_obj)
  4619. return FALSE;
  4620. if( TheActionManager->canHijackVehicle(m_obj, objOther, m_commandSource) )
  4621. return TRUE;
  4622. else
  4623. return FALSE;
  4624. }
  4625. //-----------------------------------------------------------------------------
  4626. //-----------------------------------------------------------------------------
  4627. //-----------------------------------------------------------------------------
  4628. PartitionFilterLastAttackedBy::PartitionFilterLastAttackedBy(Object *obj)
  4629. {
  4630. if (obj && obj->getBodyModule()) {
  4631. m_lastAttackedBy = obj->getBodyModule()->getLastDamageInfo()->in.m_sourceID;
  4632. } else {
  4633. m_lastAttackedBy = INVALID_ID;
  4634. }
  4635. }
  4636. //-----------------------------------------------------------------------------
  4637. Bool PartitionFilterLastAttackedBy::allow(Object *other)
  4638. {
  4639. // objOther is guaranteed to be non-null, so we don't need to check (srj)
  4640. if (other->getID() == m_lastAttackedBy)
  4641. return true;
  4642. return false;
  4643. }
  4644. //-----------------------------------------------------------------------------
  4645. //-----------------------------------------------------------------------------
  4646. //-----------------------------------------------------------------------------
  4647. //-----------------------------------------------------------------------------
  4648. Bool PartitionFilterAcceptByObjectStatus::allow(Object *objOther)
  4649. {
  4650. ObjectStatusMaskType status = objOther->getStatusBits();
  4651. return status.testForAll( m_mustBeSet ) && status.testForNone( m_mustBeClear );
  4652. }
  4653. //-----------------------------------------------------------------------------
  4654. //-----------------------------------------------------------------------------
  4655. //-----------------------------------------------------------------------------
  4656. //-----------------------------------------------------------------------------
  4657. Bool PartitionFilterRejectByObjectStatus::allow(Object *objOther)
  4658. {
  4659. ObjectStatusMaskType status = objOther->getStatusBits();
  4660. return !( status.testForAll( m_mustBeSet ) && status.testForNone( m_mustBeClear ) );
  4661. }
  4662. //-----------------------------------------------------------------------------
  4663. //-----------------------------------------------------------------------------
  4664. //-----------------------------------------------------------------------------
  4665. //-----------------------------------------------------------------------------
  4666. Bool PartitionFilterAcceptByKindOf::allow(Object *objOther)
  4667. {
  4668. return objOther->isKindOfMulti(m_mustBeSet, m_mustBeClear);
  4669. }
  4670. //-----------------------------------------------------------------------------
  4671. //-----------------------------------------------------------------------------
  4672. //-----------------------------------------------------------------------------
  4673. //-----------------------------------------------------------------------------
  4674. Bool PartitionFilterRejectByKindOf::allow(Object *objOther)
  4675. {
  4676. return !objOther->isKindOfMulti(m_mustBeSet, m_mustBeClear);
  4677. }
  4678. //-----------------------------------------------------------------------------
  4679. //-----------------------------------------------------------------------------
  4680. //-----------------------------------------------------------------------------
  4681. //-----------------------------------------------------------------------------
  4682. Bool PartitionFilterStealthedAndUndetected::allow( Object *objOther )
  4683. {
  4684. // objOther is guaranteed to be non-null, so we don't need to check (srj)
  4685. Bool stealthed = objOther->testStatus( OBJECT_STATUS_STEALTHED );
  4686. Bool detected = objOther->testStatus( OBJECT_STATUS_DETECTED );
  4687. Bool disguised = objOther->testStatus( OBJECT_STATUS_DISGUISED );
  4688. if( stealthed && !detected )
  4689. {
  4690. if( !objOther->isKindOf( KINDOF_DISGUISER ) )
  4691. {
  4692. //We have a stealthed & undetected object.
  4693. return m_allow;
  4694. }
  4695. else if( disguised )
  4696. {
  4697. //Exception case -- bomb trucks can't be considered stealthed units when they are disguised as the enemy.
  4698. StealthUpdate *update = objOther->getStealth();
  4699. if( update && update->isDisguised() )
  4700. {
  4701. Player *ourPlayer = m_obj->getControllingPlayer();
  4702. Player *otherPlayer = ThePlayerList->getNthPlayer( update->getDisguisedPlayerIndex() );
  4703. if( ourPlayer && otherPlayer )
  4704. {
  4705. if( ourPlayer->getRelationship( otherPlayer->getDefaultTeam() ) == ENEMIES )
  4706. {
  4707. //Our stealthed & undetected object is disguised as a unit perceived to be our enemy,
  4708. //therefore it's not considered to be stealthed!
  4709. return !m_allow;
  4710. }
  4711. //Our object is disguised as a "friendly" object, therefore it's considered to be stealthed.
  4712. return m_allow;
  4713. }
  4714. }
  4715. //Our bomb truck is not disguised therefore it's not stealthed.
  4716. return !m_allow;
  4717. }
  4718. }
  4719. else
  4720. {
  4721. //This handles neutral containers that hold stealth units. This specifically fixes a bug where hunt scripts would ignore
  4722. //this case -- units would acquire the building Jarmen Kell occupied even though it was not stealth detected.
  4723. const ContainModuleInterface* contain = objOther->getContain();
  4724. if( contain )
  4725. {
  4726. const Player* victimApparentController = contain->getApparentControllingPlayer( m_obj->getControllingPlayer() );
  4727. //Check if it's stealthed!
  4728. if( contain->getStealthUnitsContained() == contain->getContainCount() )
  4729. {
  4730. //Check if the first object inside is detected (if one is detected, all are detected).
  4731. ContainedItemsList::const_iterator it = contain->getContainedItemsList()->begin();
  4732. Object *member = (*it);
  4733. if( member && !(*it)->getStatusBits().test( OBJECT_STATUS_DETECTED ) )
  4734. {
  4735. //Finally check the relationship!
  4736. if( victimApparentController && m_obj->getTeam()->getRelationship( victimApparentController->getDefaultTeam() ) == ENEMIES )
  4737. {
  4738. //Our object is a neutral building garrisoned by enemy units we can't see, therefore it is stealthed.
  4739. return m_allow;
  4740. }
  4741. }
  4742. }
  4743. }
  4744. }
  4745. //The unit is not stealthed!
  4746. return !m_allow;
  4747. }
  4748. //-------------------------------------------------------------------------------------------------
  4749. //-------------------------------------------------------------------------------------------------
  4750. //-------------------------------------------------------------------------------------------------
  4751. static int cellValueProc(PartitionCell* cell, void* userData)
  4752. {
  4753. CellValueProcParms *parms = (CellValueProcParms*) userData;
  4754. UnsignedInt val = 0;
  4755. for (Int i = 0; i < MAX_PLAYER_COUNT; ++i) {
  4756. if (BitTest(parms->allowedPlayersMasks, parms->allPlayersMask[i])) {
  4757. if (parms->valueType == VOT_CashValue) {
  4758. val += cell->getCashValue(i);
  4759. } else {
  4760. val += cell->getThreatValue(i);
  4761. }
  4762. }
  4763. }
  4764. if ((val > parms->valueRequired && parms->greaterThan) ||
  4765. (val < parms->valueRequired && !parms->greaterThan)) {
  4766. return 1;
  4767. }
  4768. return 0;
  4769. }
  4770. // -----------------------------------------------------------------------------
  4771. static void hLineAddLooker(Int x1, Int x2, Int y, void *playerIndexVoid)
  4772. {
  4773. if (y < 0 || y >= ThePartitionManager->m_cellCountY || x1 >= ThePartitionManager->m_cellCountX || x2 < 0)
  4774. return;
  4775. Int playerIndex = (Int)(playerIndexVoid);
  4776. PartitionCell* cell = &ThePartitionManager->m_cells[y * ThePartitionManager->m_cellCountX + x1]; // yes, this could be invalid. we'll skip the bad ones.
  4777. for (Int x = x1; x <= x2; ++x, ++cell)
  4778. {
  4779. if (x < 0 || x >= ThePartitionManager->m_cellCountX)
  4780. continue;
  4781. cell->addLooker(playerIndex);
  4782. }
  4783. }
  4784. // -----------------------------------------------------------------------------
  4785. static void hLineRemoveLooker(Int x1, Int x2, Int y, void *playerIndexVoid)
  4786. {
  4787. if (y < 0 || y >= ThePartitionManager->m_cellCountY || x1 >= ThePartitionManager->m_cellCountX || x2 < 0)
  4788. return;
  4789. Int playerIndex = (Int)(playerIndexVoid);
  4790. PartitionCell* cell = &ThePartitionManager->m_cells[y * ThePartitionManager->m_cellCountX + x1]; // yes, this could be invalid. we'll skip the bad ones.
  4791. for (Int x = x1; x <= x2; ++x, ++cell)
  4792. {
  4793. if (x < 0 || x >= ThePartitionManager->m_cellCountX)
  4794. continue;
  4795. cell->removeLooker(playerIndex);
  4796. }
  4797. }
  4798. // -----------------------------------------------------------------------------
  4799. static void hLineAddShrouder(Int x1, Int x2, Int y, void *playerIndexVoid)
  4800. {
  4801. if (y < 0 || y >= ThePartitionManager->m_cellCountY || x1 >= ThePartitionManager->m_cellCountX || x2 < 0)
  4802. return;
  4803. Int playerIndex = (Int)(playerIndexVoid);
  4804. PartitionCell* cell = &ThePartitionManager->m_cells[y * ThePartitionManager->m_cellCountX + x1]; // yes, this could be invalid. we'll skip the bad ones.
  4805. for (Int x = x1; x <= x2; ++x, ++cell)
  4806. {
  4807. if (x < 0 || x >= ThePartitionManager->m_cellCountX)
  4808. continue;
  4809. cell->addShrouder( playerIndex );
  4810. }
  4811. }
  4812. // -----------------------------------------------------------------------------
  4813. static void hLineRemoveShrouder(Int x1, Int x2, Int y, void *playerIndexVoid)
  4814. {
  4815. if (y < 0 || y >= ThePartitionManager->m_cellCountY || x1 >= ThePartitionManager->m_cellCountX || x2 < 0)
  4816. return;
  4817. Int playerIndex = (Int)(playerIndexVoid);
  4818. PartitionCell* cell = &ThePartitionManager->m_cells[y * ThePartitionManager->m_cellCountX + x1]; // yes, this could be invalid. we'll skip the bad ones.
  4819. for (Int x = x1; x <= x2; ++x, ++cell)
  4820. {
  4821. if (x < 0 || x >= ThePartitionManager->m_cellCountX)
  4822. continue;
  4823. cell->removeShrouder( playerIndex );
  4824. }
  4825. }
  4826. // -----------------------------------------------------------------------------
  4827. static void hLineAddThreat(Int x1, Int x2, Int y, void *threatValueParms)
  4828. {
  4829. if (y < 0 || y >= ThePartitionManager->m_cellCountY || x1 >= ThePartitionManager->m_cellCountX || x2 < 0)
  4830. return;
  4831. ThreatValueParms *parms = (ThreatValueParms*)threatValueParms;
  4832. Real distance;
  4833. Real mulVal = 1.0f;
  4834. PartitionCell* cell = &ThePartitionManager->m_cells[y * ThePartitionManager->m_cellCountX + x1]; // yes, this could be invalid. we'll skip the bad ones.
  4835. for (Int x = x1; x <= x2; ++x, ++cell)
  4836. {
  4837. if (x < 0 || x >= ThePartitionManager->m_cellCountX)
  4838. continue;
  4839. distance = sqrt( pow(x - parms->xCenter, 2) + pow(y - parms->yCenter, 2) );
  4840. mulVal = 1 - distance / parms->radius;
  4841. if (mulVal < 0.0f)
  4842. mulVal = 0.0f;
  4843. else if (mulVal > 1.0f)
  4844. mulVal = 1.0f;
  4845. cell->addThreatValue( parms->playerIndex, REAL_TO_UNSIGNEDINT(parms->threatOrValue * mulVal) );
  4846. }
  4847. }
  4848. // -----------------------------------------------------------------------------
  4849. static void hLineRemoveThreat(Int x1, Int x2, Int y, void *threatValueParms)
  4850. {
  4851. if (y < 0 || y >= ThePartitionManager->m_cellCountY || x1 >= ThePartitionManager->m_cellCountX || x2 < 0)
  4852. return;
  4853. ThreatValueParms *parms = (ThreatValueParms*)threatValueParms;
  4854. Real distance;
  4855. Real mulVal = 1.0f;
  4856. PartitionCell* cell = &ThePartitionManager->m_cells[y * ThePartitionManager->m_cellCountX + x1]; // yes, this could be invalid. we'll skip the bad ones.
  4857. for (Int x = x1; x <= x2; ++x, ++cell)
  4858. {
  4859. if (x < 0 || x >= ThePartitionManager->m_cellCountX)
  4860. continue;
  4861. distance = sqrt( pow(x - parms->xCenter, 2) + pow(y - parms->yCenter, 2) );
  4862. mulVal = 1 - distance / parms->radius;
  4863. if (mulVal < 0.0f)
  4864. mulVal = 0.0f;
  4865. else if (mulVal > 1.0f)
  4866. mulVal = 1.0f;
  4867. cell->removeThreatValue( parms->playerIndex, REAL_TO_UNSIGNEDINT(parms->threatOrValue * mulVal) );
  4868. }
  4869. }
  4870. // -----------------------------------------------------------------------------
  4871. static void hLineAddValue(Int x1, Int x2, Int y, void *threatValueParms)
  4872. {
  4873. if (y < 0 || y >= ThePartitionManager->m_cellCountY || x1 >= ThePartitionManager->m_cellCountX || x2 < 0)
  4874. return;
  4875. ThreatValueParms *parms = (ThreatValueParms*)threatValueParms;
  4876. Real distance;
  4877. Real mulVal = 1.0f;
  4878. PartitionCell* cell = &ThePartitionManager->m_cells[y * ThePartitionManager->m_cellCountX + x1]; // yes, this could be invalid. we'll skip the bad ones.
  4879. for (Int x = x1; x <= x2; ++x, ++cell)
  4880. {
  4881. if (x < 0 || x >= ThePartitionManager->m_cellCountX)
  4882. continue;
  4883. distance = sqrt( pow(x - parms->xCenter, 2) + pow(y - parms->yCenter, 2) );
  4884. mulVal = 1 - distance / parms->radius;
  4885. if (mulVal < 0.0f)
  4886. mulVal = 0.0f;
  4887. else if (mulVal > 1.0f)
  4888. mulVal = 1.0f;
  4889. cell->addCashValue( parms->playerIndex, REAL_TO_UNSIGNEDINT(parms->threatOrValue * mulVal) );
  4890. }
  4891. }
  4892. // -----------------------------------------------------------------------------
  4893. static void hLineRemoveValue(Int x1, Int x2, Int y, void *threatValueParms)
  4894. {
  4895. if (y < 0 || y >= ThePartitionManager->m_cellCountY || x1 >= ThePartitionManager->m_cellCountX || x2 < 0)
  4896. return;
  4897. ThreatValueParms *parms = (ThreatValueParms*)threatValueParms;
  4898. Real distance;
  4899. Real mulVal = 1.0f;
  4900. PartitionCell* cell = &ThePartitionManager->m_cells[y * ThePartitionManager->m_cellCountX + x1]; // yes, this could be invalid. we'll skip the bad ones.
  4901. for (Int x = x1; x <= x2; ++x, ++cell)
  4902. {
  4903. if (x < 0 || x >= ThePartitionManager->m_cellCountX)
  4904. continue;
  4905. distance = sqrt( pow(x - parms->xCenter, 2) + pow(y - parms->yCenter, 2) );
  4906. mulVal = 1 - distance / parms->radius;
  4907. if (mulVal < 0.0f)
  4908. mulVal = 0.0f;
  4909. else if (mulVal > 1.0f)
  4910. mulVal = 1.0f;
  4911. cell->removeCashValue( parms->playerIndex, REAL_TO_UNSIGNEDINT(parms->threatOrValue * mulVal) );
  4912. }
  4913. }
  4914. // ------------------------------------------------------------------------------------------------
  4915. // ------------------------------------------------------------------------------------------------
  4916. SightingInfo::SightingInfo()
  4917. {
  4918. reset();
  4919. }
  4920. // ------------------------------------------------------------------------------------------------
  4921. /** Resetting sighting info */
  4922. // ------------------------------------------------------------------------------------------------
  4923. void SightingInfo::reset()
  4924. {
  4925. m_where.zero();
  4926. m_howFar = 0.0f;
  4927. m_forWhom = 0;
  4928. m_data = 0;
  4929. }
  4930. // ------------------------------------------------------------------------------------------------
  4931. // ------------------------------------------------------------------------------------------------
  4932. Bool SightingInfo::isInvalid() const
  4933. {
  4934. return m_howFar == 0.0f;
  4935. }
  4936. // ------------------------------------------------------------------------------------------------
  4937. /** CRC */
  4938. // ------------------------------------------------------------------------------------------------
  4939. void SightingInfo::crc( Xfer *xfer )
  4940. {
  4941. } // end crc
  4942. // ------------------------------------------------------------------------------------------------
  4943. /** Xfer Method
  4944. * Version Info:
  4945. * 1: Initial version */
  4946. // ------------------------------------------------------------------------------------------------
  4947. void SightingInfo::xfer( Xfer *xfer )
  4948. {
  4949. // version
  4950. XferVersion currentVersion = 1;
  4951. XferVersion version = currentVersion;
  4952. xfer->xferVersion( &version, currentVersion );
  4953. // where
  4954. xfer->xferCoord3D( &m_where );
  4955. // how far
  4956. xfer->xferReal( &m_howFar );
  4957. // for whom
  4958. xfer->xferUser( &m_forWhom, sizeof( PlayerMaskType ) );
  4959. // how much
  4960. xfer->xferUnsignedInt( &m_data );
  4961. } // end xfer
  4962. // ------------------------------------------------------------------------------------------------
  4963. /** Load post process */
  4964. // ------------------------------------------------------------------------------------------------
  4965. void SightingInfo::loadPostProcess()
  4966. {
  4967. } // end loadPostProcess
  4968. // ------------------------------------------------------------------------------------------------
  4969. SightingInfo::~SightingInfo()
  4970. {
  4971. } // end loadPostProcess