| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712571357145715571657175718571957205721572257235724572557265727572857295730573157325733573457355736573757385739574057415742574357445745574657475748574957505751575257535754575557565757575857595760576157625763576457655766576757685769577057715772577357745775577657775778577957805781 |
- /*
- ** Command & Conquer Generals(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // FILE: PartitionManager.cpp ////////////////////////////////////////////////////
- //-----------------------------------------------------------------------------
- //
- // Westwood Studios Pacific.
- //
- // Confidential Information
- // Copyright (C) 2001 - All Rights Reserved
- //
- //-----------------------------------------------------------------------------
- //
- // Project: RTS3
- //
- // File name: PartitionManager.cpp
- //
- // Created: Steven Johnson, September 2001
- //
- // Desc: Partition management, this system will allow us to partition the
- // objects in space, iterate objects in specified volumes,
- // regions, by types and other properties.
- //
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- // Includes
- //-----------------------------------------------------------------------------
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #include "Common/ActionManager.h"
- #include "Common/DiscreteCircle.h"
- #include "Common/GameEngine.h"
- #include "Common/GameState.h"
- #include "Common/MessageStream.h"
- #include "Common/NameKeyGenerator.h"
- #include "Common/PerfTimer.h"
- #include "Common/Player.h"
- #include "Common/PlayerList.h"
- #include "Common/Radar.h"
- #include "Common/ThingFactory.h" // for bullet type hack
- #include "Common/ThingTemplate.h"
- #include "Common/Xfer.h"
- #include "GameLogic/AIPathfind.h"
- #include "GameLogic/GameLogic.h"
- #include "GameLogic/Object.h"
- #include "GameLogic/Module/AIUpdate.h"
- #include "GameLogic/Module/BodyModule.h"
- #include "GameLogic/Module/CollideModule.h"
- #include "GameLogic/Module/ContainModule.h"
- #include "GameLogic/Module/StealthUpdate.h"
- #include "GameLogic/PartitionManager.h"
- #include "GameLogic/PolygonTrigger.h"
- #include "GameLogic/Squad.h"
- #include "GameLogic/GhostObject.h"
- #include "GameClient/Line2D.h"
- #include "GameClient/ControlBar.h"
- #ifdef _DEBUG
- //#include "GameClient/InGameUI.h" // for debugHints
- #include "Common/PlayerList.h"
- #endif
- #ifdef DUMP_PERF_STATS
- long s_countInClosestObjects = 0;
- long s_countInClosestObjectsThisFrame = 0;
- Int64 s_timeInClosestObjects = 0;
- Int64 s_timeInClosestObjectsThisFrame = 0;
- UnsignedInt s_gcoPerfFrame = 0xffffffff;
- #endif
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
- const Real HUGE_DIST_SQR = (HUGE_DIST*HUGE_DIST);
- #define DISABLE_INVALID_PREVENTION //Steven, I had to turn this off because it was causing problem with map border resizing (USA04). -MW
- //------------------------------------------------------------------------------ Performance Timers
- //#include "Common/PerfMetrics.h"
- //#include "Common/PerfTimer.h"
- //-------------------------------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- // Defines
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- static PartitionContactList* TheContactList = NULL;
- //-----------------------------------------------------------------------------
- // Local Types
- //-----------------------------------------------------------------------------
- struct ThreatValueParms
- {
- Int playerIndex;
- Real xCenter;
- Real yCenter;
- Real radius;
- UnsignedInt threatOrValue;
- };
- struct CollideInfo
- {
- Coord3D position;
- GeometryInfo geom;
- Real angle;
- CollideInfo(const Coord3D* p, const GeometryInfo& g, Real a) : position(*p), geom(g), angle(a) { }
- };
- struct CellValueProcParms
- {
- Int valueRequired;
- Bool greaterThan;
- ValueOrThreat valueType;
- PlayerMaskType allPlayersMask[MAX_PLAYER_COUNT];
- PlayerMaskType allowedPlayersMasks;
- };
- static int cellValueProc(PartitionCell* cell, void* userData);
- /*
- Notes:
- -- collideLoc and collideNormal will only be filled in if result is true;
- they are undefined if result is false.
- -- either collideLoc or collideNormal may be null if you don't need 'em.
- -- collideLoc is not guaranteed to lie precisely on either 'a' or 'b'!
- (this matters iff the objects are overlapping and thus have multiple/ambiguous
- collision points; we pick an 'in-between' spot to simplify matters, such that
- the same spot will be used for the collision in both directions)
- -- collideNormal is the normal the collision surface at 'a' at collideLoc.
- (thus the normal for b is exactly opposite in direction)
- -- collideNormal is guaranteed to be a unit vector
- */
- typedef Bool (*CollideTestProc)(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo);
- // if the dist is greater than maxDist, return false, and the output stuff is undefined.
- typedef Bool (*DistCalcProc)
- (
- const Coord3D *posA,
- const Object *objA,
- const Coord3D *posB,
- const Object *objB,
- Real& abDistSqr,
- Coord3D& abVec,
- Real maxDistSqr
- );
- //-----------------------------------------------------------------------------
- // Inline Functions
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- #define NO_FILTER_PROFILING
- #ifdef FILTER_PROFILING
- Bool DoFilterProfiling = false;
- #endif
- //DECLARE_PERF_TIMER(filtersAllow)
- inline Bool filtersAllow(PartitionFilter **filters, Object *objOther)
- {
- //USE_PERF_TIMER(filtersAllow)
- #ifdef FILTER_PROFILING
- const Int MAXR = 32;
- static const char* names[MAXR];
- static Int rejections[MAXR];
- static Int usefulRejections[MAXR];
- static Int calls = 0;
- static Int maxEver = 0;
- Int idx;
- Bool allow = true;
- if (DoFilterProfiling)
- {
- if (calls == 0)
- {
- for (idx = 0; idx < MAXR; ++idx)
- {
- names[idx] = NULL;
- rejections[idx] = 0;
- usefulRejections[idx] = 0;
- }
- maxEver = 0;
- }
- ++calls;
- }
- for (PartitionFilter **fp = filters; fp && *fp; fp++)
- {
- if (!(*fp)->allow(objOther))
- {
- if (DoFilterProfiling)
- {
- const char** namesarr = names;
- for (idx = 0; idx < maxEver; ++idx)
- if (namesarr[idx] == (*fp)->debugGetName())
- break;
- if (idx == maxEver)
- {
- namesarr[maxEver++] = (*fp)->debugGetName();
- DEBUG_ASSERTCRASH(maxEver<MAXR,("hmm, enlarge this array"));
- }
- ++rejections[idx];
- if (allow)
- ++usefulRejections[idx];
- allow = false;
- if (maxEver < idx)
- maxEver = idx;
- }
- else
- {
- return false;
- }
- }
- }
- if (DoFilterProfiling && calls > 0 && calls % 1000==0)
- {
- DEBUG_LOG(("\n\n"));
- for (idx = 0; idx < maxEver; idx++)
- {
- DEBUG_LOG(("rejections[%s] = %d (useful = %d)\n",names[idx],rejections[idx],usefulRejections[idx]));
- }
- }
- return allow;
- #else
- for (PartitionFilter **fp = filters; fp && *fp; fp++)
- {
- if (!(*fp)->allow(objOther))
- {
- return false;
- }
- }
- // assume true if no filters rejected it
- return true;
- #endif
- }
- //-----------------------------------------------------------------------------
- inline void vecDiff_2D(const Coord3D *posA, const Coord3D *posB, Coord3D *resultVec)
- {
- DEBUG_ASSERTCRASH(posA && posB && resultVec, ("null parm"));
- resultVec->x = posA->x - posB->x;
- resultVec->y = posA->y - posB->y;
- resultVec->z = 0.0f;
- }
- //-----------------------------------------------------------------------------
- inline void vecDiff_3D(const Coord3D *posA, const Coord3D *posB, Coord3D *resultVec)
- {
- DEBUG_ASSERTCRASH(posA && posB && resultVec, ("null parm"));
- resultVec->x = posA->x - posB->x;
- resultVec->y = posA->y - posB->y;
- resultVec->z = posA->z - posB->z;
- }
- //-----------------------------------------------------------------------------
- inline Real calcSqrDist_2D(const Coord3D *dist)
- {
- return sqr(dist->x) + sqr(dist->y);
- }
- //-----------------------------------------------------------------------------
- inline Real calcSqrDist_3D(const Coord3D *dist)
- {
- return sqr(dist->x) + sqr(dist->y) + sqr(dist->z);
- }
- //-----------------------------------------------------------------------------
- inline Int absInt(Int a)
- {
- if (a < 0) return -a; else return a;
- }
- //-----------------------------------------------------------------------------
- inline Int minInt(Int a, Int b)
- {
- if (a < b) return a; else return b;
- }
- //-----------------------------------------------------------------------------
- inline Int maxInt(Int a, Int b)
- {
- if (a > b) return a; else return b;
- }
- //-----------------------------------------------------------------------------
- inline Real minReal(Real a, Real b)
- {
- if (a < b) return a; else return b;
- }
- //-----------------------------------------------------------------------------
- inline Real maxReal(Real a, Real b)
- {
- if (a > b) return a; else return b;
- }
- //-----------------------------------------------------------------------------
- // Local Functions
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- static void projectCoord3D(Coord3D *coord, const Coord3D *unitDir, Real dist);
- static void flipCoord3D(Coord3D *coord);
- static void rectToFourPoints(
- const CollideInfo *a, // z is ignored
- Coord2D pts[]
- );
- static void testRotatedPointsAgainstRect(
- const Coord2D *pts, // an array of 4
- const CollideInfo *a,
- Coord2D *avg,
- Int *avgTot
- );
- static Bool xy_collideTest_Rect_Rect(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo);
- static Bool xy_collideTest_Rect_Circle(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo);
- static Bool xy_collideTest_Circle_Rect(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo);
- static Bool xy_collideTest_Circle_Circle(const Coord3D *a_pos, const Coord3D *b_pos, const Real a_radius, const Real b_radius, CollideLocAndNormal *cinfo);
- static Bool collideTest_Sphere_Sphere(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo);
- static Bool collideTest_Sphere_Cylinder(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo);
- static Bool collideTest_Sphere_Box(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo);
- static Bool collideTest_Cylinder_Sphere(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo);
- static Bool collideTest_Cylinder_Cylinder(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo);
- static Bool collideTest_Cylinder_Box(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo);
- static Bool collideTest_Box_Sphere(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo);
- static Bool collideTest_Box_Cylinder(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo);
- static Bool collideTest_Box_Box(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo);
- static Bool distCalcProc_CenterAndCenter_2D(const Coord3D *posA, const Object *objA, const Coord3D *posB, const Object *objB, Real& abDistSqr, Coord3D& abVec, Real maxDistSqr);
- static Bool distCalcProc_BoundaryAndBoundary_2D(const Coord3D *posA, const Object *objA, const Coord3D *posB, const Object *objB, Real& abDistSqr, Coord3D& abVec, Real maxDistSqr);
- static Bool distCalcProc_CenterAndCenter_3D(const Coord3D *posA, const Object *objA, const Coord3D *posB, const Object *objB, Real& abDistSqr, Coord3D& abVec, Real maxDistSqr);
- static Bool distCalcProc_BoundaryAndBoundary_3D(const Coord3D *posA, const Object *objA, const Coord3D *posB, const Object *objB, Real& abDistSqr, Coord3D& abVec, Real maxDistSqr);
- //-----------------------------------------------------------------------------
- inline void projectCoord3D(Coord3D *coord, const Coord3D *unitDir, Real dist)
- {
- coord->x += unitDir->x * dist;
- coord->y += unitDir->y * dist;
- coord->z += unitDir->z * dist;
- }
- //-----------------------------------------------------------------------------
- inline void flipCoord3D(Coord3D *coord)
- {
- coord->x = -coord->x;
- coord->y = -coord->y;
- coord->z = -coord->z;
- }
- //-----------------------------------------------------------------------------
- static void testRotatedPointsAgainstRect(
- const Coord2D *pts, // an array of 4
- const CollideInfo *a,
- Coord2D *avg,
- Int *avgTot
- )
- {
- //DEBUG_ASSERTCRASH(a->geom.getGeomType() == GEOMETRY_BOX, ("only boxes are ok here"));
- Real major = a->geom.getMajorRadius();
- Real minor = (a->geom.getGeomType() == GEOMETRY_SPHERE) ? a->geom.getMajorRadius() : a->geom.getMinorRadius();
- Real c = (Real)Cos(-a->angle);
- Real s = (Real)Sin(-a->angle);
- for (Int i = 0; i < 4; ++i, ++pts)
- {
- // convert to a delta relative to rect ctr
- Real ptx = pts->x - a->position.x;
- Real pty = pts->y - a->position.y;
- // inverse-rotate it to the right coord system
- Real ptx_new = (Real)fabs(ptx*c - pty*s);
- Real pty_new = (Real)fabs(ptx*s + pty*c);
- #ifdef INTENSE_DEBUG
- Real mag_a = sqr(ptx)+sqr(pty);
- Real mag_b = sqr(ptx_new)+sqr(pty_new);
- DEBUG_ASSERTCRASH(fabs(mag_a - mag_b) <= 1.0, ("hmm, unlikely"));
- #endif
- if (ptx_new <= major && pty_new <= minor)
- {
- avg->x += pts->x;
- avg->y += pts->y;
- *avgTot += 1;
- }
- }
- }
- //-----------------------------------------------------------------------------
- static void rectToFourPoints(
- const CollideInfo *a, // z is ignored
- Coord2D pts[]
- )
- {
- Real c = (Real)Cos(a->angle);
- Real s = (Real)Sin(a->angle);
- Real exc = a->geom.getMajorRadius()*c;
- Real eyc = a->geom.getMinorRadius()*c;
- Real exs = a->geom.getMajorRadius()*s;
- Real eys = a->geom.getMinorRadius()*s;
- // tl
- pts[0].x = a->position.x - exc - eys;
- pts[0].y = a->position.y + eyc - exs;
- // tr
- pts[1].x = a->position.x + exc - eys;
- pts[1].y = a->position.y + eyc + exs;
- // bl
- pts[2].x = a->position.x - exc + eys;
- pts[2].y = a->position.y - eyc - exs;
- // br
- pts[3].x = a->position.x + exc + eys;
- pts[3].y = a->position.y - eyc + exs;
- }
- //-----------------------------------------------------------------------------
- /**
- 2d utility routine for rect/cyl & rect/sphere collisions testing.
- */
- static Bool xy_collideTest_Circle_Rect(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
- {
- Bool result = xy_collideTest_Rect_Circle(b, a, cinfo);
- if (cinfo)
- flipCoord3D(&cinfo->normal);
- return result;
- }
- //-----------------------------------------------------------------------------
- /**
- 2d utility routine for rect/cyl & rect/sphere collisions testing.
- */
- static Bool xy_collideTest_Rect_Circle(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
- {
- #if 1
- /// @todo srj -- this is better than the other one, since it actually handles rotated rects,
- // but still not as accurate as is could be. (srj)
- CollideInfo btmp = *b;
- btmp.geom.setMinorRadius(btmp.geom.getMajorRadius());
- return xy_collideTest_Rect_Rect(a, &btmp, cinfo);
- #else
- // note, this actually tests the intersection of the the rect with the circle's
- // bounding box. in practice, this is usually good enough, since most of
- // our sphere/cyl shapes are small relative to boxes. (in fact, the WWMath
- // library takes a similar shortcut when colliding spheres with boxes in 3d.
- // so I figured it was probably good enough for us too.)
- Real circ_l = b->position.x - b->geom.getMajorRadius();
- Real circ_r = b->position.x + b->geom.getMajorRadius();
- Real circ_t = b->position.y - b->geom.getMajorRadius();
- Real circ_b = b->position.y + b->geom.getMajorRadius();
- Real rect_l = a->position.x - a->geom.getMajorRadius();
- Real rect_r = a->position.x + a->geom.getMajorRadius();
- Real rect_t = a->position.y - a->geom.getMinorRadius();
- Real rect_b = a->position.y + a->geom.getMinorRadius();
- if (circ_r >= rect_l && circ_l <= rect_r &&
- circ_b >= rect_t && circ_t <= rect_b)
- {
- if (cinfo)
- {
- vecDiff_2D(&b->position, &a->position, &cinfo->normal);
- cinfo->normal.normalize();
- cinfo->loc.x = (maxReal(circ_l, rect_l) + minReal(circ_r, rect_r)) * 0.5f;
- cinfo->loc.y = (maxReal(circ_t, rect_t) + minReal(circ_b, rect_b)) * 0.5f;
- cinfo->loc.z = (a->position.z + b->position.z) * 0.5f;
- }
- return true;
- }
- return false;
- #endif
- }
- //-----------------------------------------------------------------------------
- /**
- 2d utility routine for cyl & sphere collisions testing.
- */
- static Bool xy_collideTest_Circle_Circle(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
- {
- Coord3D diff;
- vecDiff_2D(&b->position, &a->position, &diff);
- Real distSqr = calcSqrDist_2D(&diff);
- Real touchingDistSqr = sqr(a->geom.getMajorRadius() + b->geom.getMajorRadius());
- if (distSqr <= touchingDistSqr)
- {
- // calc the approximate location and normal.
- if (cinfo)
- {
- cinfo->normal = diff;
- cinfo->normal.normalize();
- cinfo->loc = a->position;
- projectCoord3D(&cinfo->loc, &cinfo->normal, a->geom.getMajorRadius());
- }
-
- return true;
- }
- return false;
- }
- //-----------------------------------------------------------------------------
- /**
- 2d utility routine for rect collisions testing.
- */
- static Bool xy_collideTest_Rect_Rect(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
- {
- Coord2D pts[4];
- Coord2D avg; avg.x = avg.y = 0.0f;
- Int avgTot = 0;
- rectToFourPoints(a, pts);
- testRotatedPointsAgainstRect(pts, b, &avg, &avgTot);
- rectToFourPoints(b, pts);
- testRotatedPointsAgainstRect(pts, a, &avg, &avgTot);
-
- if (avgTot > 0)
- {
- if (cinfo)
- {
- // this is a little hokey, but generally adequate...
- cinfo->loc.x = avg.x / avgTot;
- cinfo->loc.y = avg.y / avgTot;
- cinfo->loc.z = (a->position.z + b->position.z) * 0.5f;
- // yow, what exactly does it mean to find the normal to two *intersecting*
- // rects? with "true" physics we could project ahead and determine
- // what the collision point would be, which would of course always
- // give a perfect, symmetrical normal. however, we almost always determine
- // collisions "after the fact" and thus the rects already intersect, meaning
- // that it's tricky to find the normal. we could do it the "right" way, which
- // would be to back up the objects to find the perfect point (see above), but
- // we don't have velocity information at this point, so we can't really
- // do that. so, naturally, we provide a (poor) guess by just doing the center-to-center
- // normal. this is rarely the correct surface normal for a box-box collision,
- // but until I either (a) rewrite everything to have predictive collisions,
- // or (b) come up with a better definition of a useful normal in this case,
- // I'm not sure we can do a whole lot better... (srj)
- vecDiff_2D(&b->position, &a->position, &cinfo->normal);
- cinfo->normal.normalize();
- }
- return true;
- }
- return false;
- }
- //-----------------------------------------------------------------------------
- inline Bool z_collideTest_Sphere_Nonsphere(CollideTestProc xyproc, const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
- {
- // case 1: center of sphere is within the nonsphere z-space.
- if (a->position.z >= b->position.z && a->position.z <= b->position.z + b->geom.getMaxHeightAbovePosition())
- {
- if (xyproc(a, b, cinfo))
- {
- // a side-to-side collision; just need to adjust the z-coord of collideLoc
- if (cinfo)
- {
- cinfo->loc.z = a->position.z;
- }
- return true;
- }
- return false;
- }
- // case 2: center of sphere is above or below nonsphere, but within
- // major-radius of top or bottom.
- // 2a: sphere is below the nonsphere
- Real b_bot = b->position.z;
- if (a->position.z < b_bot && a->position.z + a->geom.getMajorRadius() >= b_bot)
- {
- // find the radius of the slice of the sphere that is at b_bot
- CollideInfo amod = *a;
- amod.position.z = b_bot;
- amod.geom.setMajorRadius((Real)sqrtf(sqr(a->geom.getMajorRadius()) - sqr(b_bot - a->position.z)));
- if (xyproc(&amod, b, cinfo))
- {
- // if you want to have 'end' collisions, you should add something like:
- // if (circle a is contained by circle b) normal points straight up;
- // but since that's currently never possible (and even if it was, wouldn't matter)
- // we don't bother with the calculation.
- return true;
- }
- return false;
- }
- // 2b: sphere is above the nonsphere
- Real b_top = b->position.z + b->geom.getMaxHeightAbovePosition();
- if (a->position.z > b_top && a->position.z - a->geom.getMajorRadius() <= b_top)
- {
- CollideInfo amod = *a;
- amod.position.z = b_top;
- amod.geom.setMajorRadius((Real)sqrtf(sqr(a->geom.getMajorRadius()) - sqr(a->position.z - b_top)));
- if (xyproc(&amod, b, cinfo))
- {
- // if you want to have 'end' collisions, you should add something like:
- // if (circle a is contained by circle b) normal points straight down;
- // but since that's currently never possible (and even if it was, wouldn't matter)
- // we don't bother with the calculation.
- return true;
- }
- return false;
- }
- return false;
- }
- //-----------------------------------------------------------------------------
- inline Bool z_collideTest_Nonsphere_Nonsphere(CollideTestProc xyproc, const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
- {
- // note: we already know that there is a z-intersection (our caller filters that out)
- // so we need not recheck that here.
- // Preflight for close objects.
- Real dSqr = sqr(a->position.x-b->position.x) + sqr(a->position.y-b->position.y);
- Real minRadius = a->geom.getMajorRadius();
- Real r = a->geom.getMinorRadius();
- if (minRadius>r) minRadius = r;
- r = b->geom.getMajorRadius();
- if (minRadius>r) minRadius = r;
- r = b->geom.getMinorRadius();
- if (minRadius>r) minRadius = r;
-
- Bool closeEnough = sqr(minRadius) > dSqr;
- if (closeEnough || xyproc(a, b, cinfo))
- {
- // since we allow objects to overlap at times, how do we distinguish
- // between a "top-to-bottom" vs "side-to-side" collision?
- // currently, we don't: cylindrical shapes currently never collide
- // top-to-bottom, so we just declare everything to be side-to-side.
- // if this changes, need to smarten this.
- // case 1: side-to-side collision
- // just need to adjust the z-coord of collideLoc
- if (cinfo)
- {
- if (b->position.z > a->position.z)
- cinfo->loc.z = (b->position.z + a->position.z + a->geom.getMaxHeightAbovePosition()) * 0.5f;
- else
- cinfo->loc.z = (a->position.z + b->position.z + b->geom.getMaxHeightAbovePosition()) * 0.5f;
- }
- return true;
- }
- return false;
- }
- //-----------------------------------------------------------------------------
- static Bool collideTest_Sphere_Sphere(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
- {
- Coord3D diff;
- vecDiff_3D(&b->position, &a->position, &diff);
- Real distSqr = calcSqrDist_3D(&diff);
- Real touchingDistSqr = sqr(a->geom.getMajorRadius() + b->geom.getMajorRadius());
- if (distSqr <= touchingDistSqr)
- {
- if (cinfo)
- {
- cinfo->normal = diff;
- cinfo->normal.normalize();
- cinfo->loc = a->position;
- projectCoord3D(&cinfo->loc, &cinfo->normal, a->geom.getMajorRadius());
- }
- return true;
- }
- return false;
- }
- //-----------------------------------------------------------------------------
- static Bool collideTest_Sphere_Cylinder(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
- {
- return z_collideTest_Sphere_Nonsphere(xy_collideTest_Circle_Circle, a, b, cinfo);
- }
- //-----------------------------------------------------------------------------
- static Bool collideTest_Sphere_Box(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
- {
- return z_collideTest_Sphere_Nonsphere(xy_collideTest_Circle_Rect, a, b, cinfo);
- }
- //-----------------------------------------------------------------------------
- static Bool collideTest_Cylinder_Sphere(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
- {
- Bool result = z_collideTest_Sphere_Nonsphere(xy_collideTest_Circle_Circle, b, a, cinfo);
- if (cinfo)
- flipCoord3D(&cinfo->normal);
- return result;
- }
- //-----------------------------------------------------------------------------
- static Bool collideTest_Cylinder_Cylinder(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
- {
- return z_collideTest_Nonsphere_Nonsphere(xy_collideTest_Circle_Circle, a, b, cinfo);
- }
- //-----------------------------------------------------------------------------
- static Bool collideTest_Cylinder_Box(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
- {
- return z_collideTest_Nonsphere_Nonsphere(xy_collideTest_Circle_Rect, a, b, cinfo);
- }
- //-----------------------------------------------------------------------------
- static Bool collideTest_Box_Sphere(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
- {
- Bool result = z_collideTest_Sphere_Nonsphere(xy_collideTest_Circle_Rect, b, a, cinfo);
- if (cinfo)
- flipCoord3D(&cinfo->normal);
- return result;
- }
- //-----------------------------------------------------------------------------
- static Bool collideTest_Box_Cylinder(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
- {
- return z_collideTest_Nonsphere_Nonsphere(xy_collideTest_Rect_Circle, a, b, cinfo);
- }
- //-----------------------------------------------------------------------------
- static Bool collideTest_Box_Box(const CollideInfo *a, const CollideInfo *b, CollideLocAndNormal *cinfo)
- {
- return z_collideTest_Nonsphere_Nonsphere(xy_collideTest_Rect_Rect, a, b, cinfo);
- }
- //-----------------------------------------------------------------------------
- static Bool distCalcProc_CenterAndCenter_2D(
- const Coord3D *posA,
- const Object *objA,
- const Coord3D *posB,
- const Object *objB,
- Real& abDistSqr,
- Coord3D& abVec,
- Real maxDistSqr
- )
- {
- // note that object positions are defined as the bottom center of the geometry,
- // thus we must add the radius to the z coord to get the proper center of the bounding sphere.
- Coord3D diff;
- diff.x = posB->x - posA->x;
- diff.y = posB->y - posA->y;
- diff.z = 0.0f;
-
- //if (abDistSqr)
- {
- abDistSqr = sqr(diff.x) + sqr(diff.y);
- }
- //if (abVec)
- {
- abVec = diff;
- }
- return abDistSqr < maxDistSqr;
- }
- //-----------------------------------------------------------------------------
- static Bool distCalcProc_BoundaryAndBoundary_2D(
- const Coord3D *posA,
- const Object *objA,
- const Coord3D *posB,
- const Object *objB,
- Real& abDistSqr,
- Coord3D& abVec,
- Real maxDistSqr
- )
- {
- Coord3D diff;
- diff.x = posB->x - posA->x;
- diff.y = posB->y - posA->y;
- diff.z = 0.0f;
-
- Real actualDistSqr = sqr(diff.x) + sqr(diff.y);
- Real shrinkFactor = 1.0f;
- Real shrunkenDistSqr = actualDistSqr;
- Real totalRad = (objA ? objA->getGeometryInfo().getBoundingCircleRadius() : 0.0f) +
- (objB ? objB->getGeometryInfo().getBoundingCircleRadius() : 0.0f);
- if (totalRad > 0.0f)
- {
- Real actualDist = sqrtf(actualDistSqr);
- Real shrunkenDist = actualDist - totalRad;
- if (shrunkenDist <= 0.0f)
- {
- shrinkFactor = 0.0f;
- shrunkenDistSqr = 0.0f; // sorry, distances can't be negative
- }
- else
- {
- shrinkFactor = shrunkenDist / actualDist;
- shrunkenDistSqr = sqr(shrunkenDist);
- }
- }
- //if (abDistSqr)
- {
- abDistSqr = shrunkenDistSqr;
- }
- //if (abVec)
- {
- DEBUG_ASSERTCRASH(shrinkFactor >= 0.0f && shrinkFactor <= 1.0f, ("Hmm, this should not be possible."));
- diff.x *= shrinkFactor;
- diff.y *= shrinkFactor;
- abVec = diff;
- }
- return abDistSqr < maxDistSqr;
- }
- //-----------------------------------------------------------------------------
- static Bool distCalcProc_CenterAndCenter_3D(
- const Coord3D *posA,
- const Object *objA,
- const Coord3D *posB,
- const Object *objB,
- Real& abDistSqr,
- Coord3D& abVec,
- Real maxDistSqr
- )
- {
- // note that object positions are defined as the bottom center of the geometry,
- // thus we must add the radius to the z coord to get the proper center of the bounding sphere.
- Coord3D diff;
- diff.x = posB->x - posA->x;
- diff.y = posB->y - posA->y;
- diff.z = posB->z - posA->z;
-
- //if (abDistSqr)
- {
- abDistSqr = sqr(diff.x) + sqr(diff.y) + sqr(diff.z);
- }
- //if (abVec)
- {
- abVec = diff;
- }
- return abDistSqr < maxDistSqr;
- }
- //-----------------------------------------------------------------------------
- static Bool distCalcProc_BoundaryAndBoundary_3D(
- const Coord3D *posA,
- const Object *objA,
- const Coord3D *posB,
- const Object *objB,
- Real& abDistSqr,
- Coord3D& abVec,
- Real maxDistSqr
- )
- {
- const GeometryInfo* geomA = objA ? &objA->getGeometryInfo() : NULL;
- const GeometryInfo* geomB = objB ? &objB->getGeometryInfo() : NULL;
- // note that object positions are defined as the bottom center of the geometry,
- // thus we must add the radius to the z coord to get the proper center of the bounding sphere.
- Coord3D diff;
- diff.x = posB->x - posA->x;
- diff.y = posB->y - posA->y;
- diff.z = ((posB->z + (geomB ? geomB->getZDeltaToCenterPosition() : 0.0f))
- - (posA->z + (geomA ? geomA->getZDeltaToCenterPosition() : 0.0f)));
-
- Real actualDistSqr = sqr(diff.x) + sqr(diff.y) + sqr(diff.z);
- Real shrinkFactor = 1.0f;
- Real shrunkenDistSqr = actualDistSqr;
- Real totalRad = (geomA?geomA->getBoundingSphereRadius():0) + (geomB?geomB->getBoundingSphereRadius():0);
- if (totalRad > 0.0f)
- {
- Real actualDist = sqrtf(actualDistSqr);
- Real shrunkenDist = actualDist - totalRad;
- if (shrunkenDist <= 0.0f)
- {
- shrinkFactor = 0.0f;
- shrunkenDistSqr = 0.0f; // sorry, distances can't be negative
- }
- else
- {
- shrinkFactor = shrunkenDist / actualDist;
- shrunkenDistSqr = sqr(shrunkenDist);
- }
- }
- //if (abDistSqr)
- {
- abDistSqr = shrunkenDistSqr;
- }
- //if (abVec)
- {
- DEBUG_ASSERTCRASH(shrinkFactor >= 0.0f && shrinkFactor <= 1.0f, ("Hmm, this should not be possible."));
- diff.x *= shrinkFactor;
- diff.y *= shrinkFactor;
- diff.z *= shrinkFactor;
- abVec = diff;
- }
- return abDistSqr < maxDistSqr;
- }
- //-----------------------------------------------------------------------------
- // Private Types
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- // Private Data
- //-----------------------------------------------------------------------------
- static DistCalcProc theDistCalcProcs[] =
- {
- distCalcProc_CenterAndCenter_2D,
- distCalcProc_CenterAndCenter_3D,
- distCalcProc_BoundaryAndBoundary_2D,
- distCalcProc_BoundaryAndBoundary_3D,
- };
- // NOTE: This *DEPENDS* on the order of the geometry enum defines
- static CollideTestProc theCollideTestProcs[] =
- {
- collideTest_Sphere_Sphere,
- collideTest_Sphere_Cylinder,
- collideTest_Sphere_Box,
- collideTest_Cylinder_Sphere,
- collideTest_Cylinder_Cylinder,
- collideTest_Cylinder_Box,
- collideTest_Box_Sphere,
- collideTest_Box_Cylinder,
- collideTest_Box_Box
- };
- //-----------------------------------------------------------------------------
- // Public Data
- //-----------------------------------------------------------------------------
- PartitionManager *ThePartitionManager = NULL; ///< the object manager singleton
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- class PartitionContactListNode : public MemoryPoolObject
- {
- MEMORY_POOL_GLUE_WITH_USERLOOKUP_CREATE(PartitionContactListNode, "PartitionContactListNode" )
- public:
- PartitionContactListNode* m_nextHash; ///< next node with same hash value
- PartitionContactListNode* m_next; ///< next node
- PartitionData* m_obj; ///< one object that is possibly colliding
- PartitionData* m_other; ///< the other object (or null for collisions with the terrain)
- Int m_hashValue;///< index into hash table
- };
- inline PartitionContactListNode::~PartitionContactListNode() { }
- //-----------------------------------------------------------------------------
- class PartitionContactList
- {
- private:
- /*
- socketcount should be prime (and "not too close to a power of 2) for best results.
-
- if this one isn't large enough, try this website:
- http://www.utm.edu/research/primes/lists/small/1000.txt
- So how is this chosen? Eh, pretty much based on experimentation.
- */
- enum { PartitionContactList_SOCKET_COUNT = 5381 };
- PartitionContactListNode* m_contactHash[PartitionContactList_SOCKET_COUNT];
- PartitionContactListNode* m_contactList;
- public:
- PartitionContactList()
- {
- memset(m_contactHash, 0, sizeof(m_contactHash));
- m_contactList = NULL;
- }
- ~PartitionContactList()
- {
- resetContactList();
- }
- /**
- add a pair of objects to the contact list. If the pair is already
- present (in either this order or reverse order), nothing is done.
- Note that it is OK for other==null (this indicates a collisions with
- the ground) but it is not OK for obj==null.
- */
- void addToContactList(PartitionData *obj, PartitionData *other);
- /**
- process all pairs in the contact list: first, determine if they
- truly collide, based on their geometry. if so, call the collide
- actions for each object in the pair.
- */
- void processContactList();
- /**
- discard the contents of the contact list.
- */
- void resetContactList();
- /**
- remove any contacts that refer to the given data.
- */
- void removeSpecificPartitionData(PartitionData* data);
- };
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- #ifdef FASTER_GCO
- // nothing
- #else
- //-----------------------------------------------------------------------------
- CellOutwardIterator::CellOutwardIterator(PartitionManager *mgr, Int x, Int y)
- {
- m_mgr = mgr;
- m_cellCenterX = x;
- m_cellCenterY = y;
- m_cellRadius = 0;
- m_maxRadius = maxInt(maxInt(x, mgr->getCellCountX() - x), maxInt(y, mgr->getCellCountY() - y));
- m_delta[0] = m_delta[1] = 0;
- m_inc = -1;
- m_cnt = 0;
- m_axis = 1;
- m_iter = 1;
- m_delta[m_axis] -= m_inc;
- }
- //-----------------------------------------------------------------------------
- CellOutwardIterator::~CellOutwardIterator()
- {
- }
- //-----------------------------------------------------------------------------
- PartitionCell *CellOutwardIterator::nextCell(Bool skipEmpties)
- {
- try_again:
- if (m_cellRadius <= m_maxRadius)
- {
- if (m_iter > 0)
- {
- m_delta[m_axis] += m_inc;
- --m_iter;
- PartitionCell *cell = m_mgr->getCellAt(m_cellCenterX + m_delta[0], m_cellCenterY + m_delta[1]);
- if (!cell)
- goto try_again;
- if (skipEmpties && !cell->getFirstCoiInCell())
- goto try_again;
- return cell;
- }
- m_axis += 1;
- if (m_axis >= 2)
- {
- m_axis = 0;
- m_inc = -m_inc;
- m_cnt += 1;
- m_cellRadius = maxInt(absInt(m_delta[0]), absInt(m_delta[1]));
- }
- m_iter = m_cnt;
- goto try_again;
- }
- return NULL;
- }
- #endif
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- CellAndObjectIntersection::CellAndObjectIntersection()
- {
- m_cell = NULL;
- m_module = NULL;
- m_prevCoi = NULL;
- m_nextCoi = NULL;
- }
- //-----------------------------------------------------------------------------
- CellAndObjectIntersection::~CellAndObjectIntersection()
- {
- DEBUG_ASSERTCRASH(m_prevCoi == NULL && m_nextCoi == NULL, ("destroying a linked COI"));
- DEBUG_ASSERTCRASH(!getModule(), ("destroying an in-use COI"));
- }
- //-----------------------------------------------------------------------------
- void CellAndObjectIntersection::friend_addToCellList(CellAndObjectIntersection **pListHead)
- {
- 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"));
-
- this->m_nextCoi = *pListHead;
- if (*pListHead)
- (*pListHead)->m_prevCoi = this;
- *pListHead = this;
- }
- //-----------------------------------------------------------------------------
- void CellAndObjectIntersection::friend_removeFromCellList(CellAndObjectIntersection **pListHead)
- {
- #define DEBUG_ASSERTINLIST(c) \
- DEBUG_ASSERTCRASH((c)->m_prevCoi != NULL || (c)->m_nextCoi != NULL || *pListHead == (c), ("cell is not in list"));
- DEBUG_ASSERTINLIST(this);
- if (this->m_prevCoi)
- {
- DEBUG_ASSERTINLIST(this->m_prevCoi);
- DEBUG_ASSERTCRASH(*pListHead != this, ("bad linkage"));
- this->m_prevCoi->m_nextCoi = this->m_nextCoi;
- }
- else
- {
- DEBUG_ASSERTCRASH(*pListHead == this, ("bad linkage"));
- *pListHead = this->m_nextCoi;
- }
- if (this->m_nextCoi)
- {
- DEBUG_ASSERTINLIST(this->m_nextCoi);
- this->m_nextCoi->m_prevCoi = this->m_prevCoi;
- }
- this->m_prevCoi = NULL;
- this->m_nextCoi = NULL;
- #undef DEBUG_ASSERTINLIST
- }
- //-----------------------------------------------------------------------------
- void CellAndObjectIntersection::addCoverage(PartitionCell *cell, PartitionData *module)
- {
- DEBUG_ASSERTCRASH(m_cell == NULL || m_cell == cell, ("mismatch"));
- DEBUG_ASSERTCRASH(m_module == NULL || m_module == module, ("mismatch"));
- if (m_module != NULL && m_module != module)
- {
- DEBUG_CRASH(("COI already in use by another module!"));
- return;
- }
- if (m_cell == NULL)
- cell->friend_addToCellList(this);
- m_cell = cell;
- m_module = module;
- }
- //-----------------------------------------------------------------------------
- void CellAndObjectIntersection::removeAllCoverage()
- {
- if (m_module == NULL)
- {
- DEBUG_CRASH(("COI not in use"));
- return;
- }
- m_cell->friend_removeFromCellList(this);
- m_cell = NULL;
- m_module = NULL;
- }
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- PartitionCell::PartitionCell()
- {
- //Added By Sadullah Nader
- //Initializations inserted
- m_cellX = m_cellY = 0;
- //
- m_firstCoiInCell = NULL;
- m_coiCount = 0;
- #ifdef PM_CACHE_TERRAIN_HEIGHT
- m_loTerrainZ = HUGE_DIST; // huge positive
- m_hiTerrainZ = -HUGE_DIST; // huge negative
- #endif
- /*
- You may be asking yourself: why do we model the shroud for all players,
- rather than just the local player? And the answer is: because this allows
- us to checksum these values for net games, to help prevent "shroud cheaters"
- (who use a trainer to disable the shroud on their system).
- */
- for (int i = 0; i < MAX_PLAYER_COUNT; ++i)
- {
- // Default is "passive shroud". 1,0.
- m_shroudLevel[i].m_currentShroud = 1;
- m_shroudLevel[i].m_activeShroudLevel = 0;
- // default cash value is 0
- m_cashValue[i] = 0;
- // default threat value is 0
- m_threatValue[i] = 0;
- }
- }
- //-----------------------------------------------------------------------------
- PartitionCell::~PartitionCell()
- {
- DEBUG_ASSERTCRASH(m_firstCoiInCell == NULL && m_coiCount == 0, ("destroying a nonempty PartitionCell"));
- // but don't destroy the Cois; they don't belong to us
- }
- //-----------------------------------------------------------------------------
- void PartitionCell::invalidateShroudedStatusForAllCois(Int playerIndex)
- {
- for (CellAndObjectIntersection* coi = m_firstCoiInCell; coi; coi = coi->getNextCoi())
- {
- coi->getModule()->invalidateShroudedStatusForPlayer(playerIndex);
- }
- }
- //-----------------------------------------------------------------------------
- void PartitionCell::addLooker(Int playerIndex)
- {
- CellShroudStatus oldShroud = getShroudStatusForPlayer( playerIndex );
- // The decreasing Algorithm: A 1 will go straight to -1, otherwise it just gets decremented
- m_shroudLevel[playerIndex].m_currentShroud = min( m_shroudLevel[playerIndex].m_currentShroud - 1, -1 );
- CellShroudStatus newShroud = getShroudStatusForPlayer( playerIndex );
- // DEBUG_LOG(( "ADD %d, %d. CS = %d, AS = %d for player %d.\n",
- // m_cellX,
- // m_cellY,
- // m_shroudLevel[playerIndex].m_currentShroud,
- // m_shroudLevel[playerIndex].m_activeShroudLevel,
- // playerIndex
- // ));
- if( oldShroud != newShroud )
- {
- // On an edge trigger, tell all objects to think about their shroudedness
- invalidateShroudedStatusForAllCois( playerIndex );
- if( playerIndex == ThePlayerList->getLocalPlayer()->getPlayerIndex() )
- {
- // and if this is the local player, do the Client update.
- TheDisplay->setShroudLevel(m_cellX, m_cellY, newShroud);
- TheRadar->setShroudLevel(m_cellX, m_cellY, newShroud);
- }
- }
- }
- //-----------------------------------------------------------------------------
- void PartitionCell::removeLooker(Int playerIndex)
- {
- CellShroudStatus oldShroud = getShroudStatusForPlayer( playerIndex );
- // the increasing Algorithm: a -1 goes up to min(1,activeLevel), otherwise it just gets incremented
- if( m_shroudLevel[playerIndex].m_currentShroud == -1 )
- m_shroudLevel[playerIndex].m_currentShroud = min( m_shroudLevel[playerIndex].m_activeShroudLevel, (Short)1 );
- else
- {
- 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.") );
- m_shroudLevel[playerIndex].m_currentShroud++;
- }
- CellShroudStatus newShroud = getShroudStatusForPlayer( playerIndex );
- // DEBUG_LOG(( "REMOVE %d, %d. CS = %d, AS = %d for player %d.\n",
- // m_cellX,
- // m_cellY,
- // m_shroudLevel[playerIndex].m_currentShroud,
- // m_shroudLevel[playerIndex].m_activeShroudLevel,
- // playerIndex
- // ));
- if( oldShroud != newShroud )
- {
- // On an edge trigger, tell all objects to think about their shroudedness
- invalidateShroudedStatusForAllCois( playerIndex );
- if( playerIndex == ThePlayerList->getLocalPlayer()->getPlayerIndex() )
- {
- // and if this is the local player, do the Client update.
- TheDisplay->setShroudLevel(m_cellX, m_cellY, newShroud);
- TheRadar->setShroudLevel(m_cellX, m_cellY, newShroud);
- }
- }
- }
- //-----------------------------------------------------------------------------
- void PartitionCell::addShrouder( Int playerIndex )
- {
- CellShroudStatus oldShroud = getShroudStatusForPlayer( playerIndex );
- // Increasing active shroud: activeLevel gets incremented, and CS is set to 1 if at zero
- // do the algorithm
- m_shroudLevel[playerIndex].m_activeShroudLevel++;
- if( m_shroudLevel[playerIndex].m_currentShroud == 0 )
- {
- m_shroudLevel[playerIndex].m_currentShroud = 1;
- }
- CellShroudStatus newShroud = getShroudStatusForPlayer( playerIndex );
- if( oldShroud != newShroud )
- {
- // On an edge trigger, tell all objects to think about their shroudedness
- invalidateShroudedStatusForAllCois( playerIndex );
- // and update the client if we are on the local player
- if( playerIndex == ThePlayerList->getLocalPlayer()->getPlayerIndex() )
- {
- TheDisplay->setShroudLevel(m_cellX, m_cellY, newShroud);
- TheRadar->setShroudLevel(m_cellX, m_cellY, newShroud);
- }
- }
- }
- //-----------------------------------------------------------------------------
- void PartitionCell::removeShrouder( Int playerIndex )
- {
- // Decreasing active shroud: just decrement activeLevel. This will never result in a client change.
- // Either it was passive shroud and is now active, or it was being looked at and still is.
- m_shroudLevel[playerIndex].m_activeShroudLevel--;
- DEBUG_ASSERTCRASH( m_shroudLevel[playerIndex].m_activeShroudLevel >= 0, ("Shroud generation has gone negative. This can't happen.") );
- }
- //-----------------------------------------------------------------------------
- //Bool PartitionCell::isShroudedForPlayer( Int playerIndex ) const
- //{
- // There isn't an absolute answer. This cell is only shrouded in regards to a person
- // return (m_shroudLevel[playerIndex].m_currentShroud == 1);
- //}
- //-----------------------------------------------------------------------------
- CellShroudStatus PartitionCell::getShroudStatusForPlayer( Int playerIndex ) const
- {
- // There are now three answers, but the question still requires "to whom"
- if( m_shroudLevel[playerIndex].m_currentShroud == 1 )
- return CELLSHROUD_SHROUDED;
- else if( m_shroudLevel[playerIndex].m_currentShroud == 0 )
- return CELLSHROUD_FOGGED;// ie Nobody actively looking
- else
- return CELLSHROUD_CLEAR;
- }
- //-----------------------------------------------------------------------------
- UnsignedInt PartitionCell::getThreatValue( Int playerIndex )
- {
- if (playerIndex >= 0 && playerIndex < MAX_PLAYER_COUNT) {
- return m_threatValue[playerIndex];
- }
- return 0;
- }
- //-----------------------------------------------------------------------------
- void PartitionCell::addThreatValue( Int playerIndex, UnsignedInt threatValue )
- {
- if (playerIndex >= 0 && playerIndex < MAX_PLAYER_COUNT) {
- #ifdef _DEBUG
- UnsignedInt oldThreatVal = m_threatValue[playerIndex];
- DEBUG_ASSERTCRASH(oldThreatVal <= oldThreatVal + threatValue, ("adding new threat value overflowed allotted storage."));
- #endif
- m_threatValue[playerIndex] += threatValue;
- }
- }
- //-----------------------------------------------------------------------------
- void PartitionCell::removeThreatValue( Int playerIndex, UnsignedInt threatValue )
- {
- if (playerIndex >= 0 && playerIndex < MAX_PLAYER_COUNT) {
- #ifdef _DEBUG
- UnsignedInt oldThreatVal = m_threatValue[playerIndex];
- DEBUG_ASSERTCRASH(oldThreatVal >= oldThreatVal - threatValue, ("removing new threat value underflowed allotted storage."));
- #endif
- m_threatValue[playerIndex] -= threatValue;
- }
- }
- //-----------------------------------------------------------------------------
- UnsignedInt PartitionCell::getCashValue( Int playerIndex )
- {
- if (playerIndex >= 0 && playerIndex < MAX_PLAYER_COUNT) {
- return m_cashValue[playerIndex];
- }
- return 0;
- }
- //-----------------------------------------------------------------------------
- void PartitionCell::addCashValue( Int playerIndex, UnsignedInt cashValue )
- {
- if (playerIndex >= 0 && playerIndex < MAX_PLAYER_COUNT) {
- #ifdef _DEBUG
- UnsignedInt oldCashVal = m_cashValue[playerIndex];
- DEBUG_ASSERTCRASH(oldCashVal <= oldCashVal + cashValue, ("adding new cash value overflowed allotted storage."));
- #endif
- m_cashValue[playerIndex] += cashValue;
- }
- }
- //-----------------------------------------------------------------------------
- void PartitionCell::removeCashValue( Int playerIndex, UnsignedInt cashValue )
- {
- if (playerIndex >= 0 && playerIndex < MAX_PLAYER_COUNT) {
- #ifdef _DEBUG
- UnsignedInt oldCashVal = m_cashValue[playerIndex];
- DEBUG_ASSERTCRASH(oldCashVal >= oldCashVal - cashValue, ("removing new cash value underflowed allotted storage."));
- #endif
- m_cashValue[playerIndex] -= cashValue;
- }
- }
- //-----------------------------------------------------------------------------
- void PartitionCell::friend_addToCellList(CellAndObjectIntersection *coi)
- {
- if (coi)
- {
- coi->friend_addToCellList(&m_firstCoiInCell);
- ++m_coiCount;
- }
- }
- //-----------------------------------------------------------------------------
- void PartitionCell::friend_removeFromCellList(CellAndObjectIntersection *coi)
- {
- if (coi)
- {
- coi->friend_removeFromCellList(&m_firstCoiInCell);
- --m_coiCount;
- }
- }
- //-----------------------------------------------------------------------------
- void PartitionCell::getCellCenterPos(Real& x, Real& y)
- {
- ThePartitionManager->getCellCenterPos(m_cellX, m_cellY, x, y);
- }
- //-----------------------------------------------------------------------------
- #ifdef _DEBUG
- void PartitionCell::validateCoiList()
- {
- CellAndObjectIntersection *nextCoi = 0, *prevCoi = 0;
- for (CellAndObjectIntersection *coi = getFirstCoiInCell(); coi; prevCoi = coi, coi = nextCoi)
- {
- nextCoi = coi->getNextCoi();
- DEBUG_ASSERTCRASH(coi->getPrevCoi() == prevCoi, ("coi link mismatch"));
- DEBUG_ASSERTCRASH(prevCoi == NULL || prevCoi->getNextCoi() == coi, ("coi link mismatch"));
- DEBUG_ASSERTCRASH((coi == getFirstCoiInCell()) == (prevCoi == NULL) , ("coi link mismatch"));
- DEBUG_ASSERTCRASH(nextCoi == NULL || nextCoi->getPrevCoi() == coi, ("coi link mismatch"));
- }
- }
- #endif
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void PartitionCell::crc( Xfer *xfer )
- {
- xfer->xferUser(&m_shroudLevel, sizeof(ShroudLevel) * MAX_PLAYER_COUNT);
- xfer->xferUser(&m_cellX, sizeof(m_cellX));
- xfer->xferUser(&m_cellY, sizeof(m_cellY));
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method */
- // ------------------------------------------------------------------------------------------------
- void PartitionCell::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- // xfer shroud data
- xfer->xferUser( &m_shroudLevel, sizeof( ShroudLevel ) * MAX_PLAYER_COUNT );
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void PartitionCell::loadPostProcess( void )
- {
- } // end loadPostProcess
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- PartitionData::PartitionData()
- {
- //DEBUG_LOG(("create pd %08lx\n",this));
- m_next = NULL;
- m_prev = NULL;
- m_nextDirty = NULL;
- m_prevDirty = NULL;
- m_object = NULL;
- m_ghostObject = NULL;
- m_coiArrayCount = 0;
- m_coiArray = NULL;
- m_coiInUseCount = 0;
- m_doneFlag = 0;
- m_dirtyStatus = NOT_DIRTY;
- m_lastCell = NULL;
- for (int i = 0; i < MAX_PLAYER_COUNT; ++i)
- {
- m_everSeenByPlayer[i] = false;
- m_shroudedness[i] = OBJECTSHROUD_INVALID;
- m_shroudednessPrevious[i] = OBJECTSHROUD_INVALID;
- }
- }
- //-----------------------------------------------------------------------------
- PartitionData::~PartitionData()
- {
- //DEBUG_LOG(("toss pd for pd %08lx obj %08lx\n",this,m_object));
- removeAllTouchedCells();
- freeCoiArray();
- DEBUG_ASSERTCRASH(ThePartitionManager, ("ThePartitionManager is null"));
- if (ThePartitionManager && ThePartitionManager->isInListDirtyModules(this))
- {
- //DEBUG_LOG(("remove pd %08lx from dirty list (%08lx %08lx)\n",this,m_prevDirty,m_nextDirty));
- ThePartitionManager->removeFromDirtyModules(this);
- //DEBUG_ASSERTCRASH(!ThePartitionManager->isInListDirtyModules(this), ("hmm\n"));
- }
- }
- //-----------------------------------------------------------------------------
- Int PartitionData::getControllingPlayerIndex() const
- {
- const Player* p = getObject()->getControllingPlayer();
- if (p)
- {
- Int playerIndex = p->getPlayerIndex();
- DEBUG_ASSERTCRASH(playerIndex >= 0 && playerIndex < MAX_PLAYER_COUNT, ("bad playerIndex"));
- return playerIndex;
- }
- DEBUG_CRASH(("this should never happen"));
- throw ERROR_BUG;
- return 0;
- }
- //-----------------------------------------------------------------------------
- // only used to restore state after map border resizing and/or xfer!
- void PartitionData::friend_setShroudednessPrevious(Int playerIndex, ObjectShroudStatus status)
- {
- m_shroudednessPrevious[playerIndex] = status;
- // if our status is anything but shrouded, assume we've never been seen by the player.
- m_everSeenByPlayer[playerIndex] = (status == OBJECTSHROUD_SHROUDED) ? false : true;
- // if we're already invalid, mark us as special-invalid.
- #ifndef DISABLE_INVALID_PREVENTION
- if (m_shroudedness[playerIndex] == OBJECTSHROUD_INVALID)
- m_shroudedness[playerIndex] = OBJECTSHROUD_INVALID_BUT_PREVIOUS_VALID;
- #endif
- }
- //-----------------------------------------------------------------------------
- ObjectShroudStatus PartitionData::getShroudedStatus(Int playerIndex)
- {
- // sanity
- DEBUG_ASSERTCRASH( playerIndex >= 0 && playerIndex < MAX_PLAYER_COUNT,
- ("PartitionData::getShroudedStatus - Invalid player index '%d'\n", playerIndex) );
- if (!ThePartitionManager->getUpdatedSinceLastReset())
- {
- // The shroud should be invalid until update has been called.
- // 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)
- invalidateShroudedStatusForPlayer(playerIndex);
- return m_shroudedness[playerIndex];
- }
- #ifndef DISABLE_INVALID_PREVENTION
- if (m_shroudedness[playerIndex] == OBJECTSHROUD_INVALID || m_shroudedness[playerIndex] == OBJECTSHROUD_INVALID_BUT_PREVIOUS_VALID)
- {
- Bool updateShroudednessPrevious = (m_shroudedness[playerIndex] != OBJECTSHROUD_INVALID_BUT_PREVIOUS_VALID);
- #else
- if (m_shroudedness[playerIndex] == OBJECTSHROUD_INVALID)
- {
- #endif
- Int shroudedCells = 0;
- Int foggedCells = 0;
- CellAndObjectIntersection* coi = m_coiArray;
- for (Int i = m_coiInUseCount; i; --i, ++coi)
- {
- if( coi->getCell()->getShroudStatusForPlayer(playerIndex) == CELLSHROUD_SHROUDED )
- ++shroudedCells;
- else if( coi->getCell()->getShroudStatusForPlayer(playerIndex) == CELLSHROUD_FOGGED )
- ++foggedCells;
- }
- if( m_coiInUseCount == 0 )
- { m_shroudedness[playerIndex] = OBJECTSHROUD_SHROUDED; // Stuff off the map have no COIs since they aren't in the PartitionCell range
- m_everSeenByPlayer[playerIndex] = false; //force object as never seen by the player
- }
- else if( shroudedCells == m_coiInUseCount )
- { m_shroudedness[playerIndex] = OBJECTSHROUD_SHROUDED; // every cell I use is shrouded
- m_everSeenByPlayer[playerIndex] = false; //force object as never seen by the player
- if (m_ghostObject && m_shroudednessPrevious[playerIndex] == OBJECTSHROUD_FOGGED)
- { //we are shrouding an area that used to be fogged so release our memory of what was there.
- m_ghostObject->freeSnapShot(playerIndex);
- }
- }
- else if( shroudedCells + foggedCells == m_coiInUseCount )
- {
- m_shroudedness[playerIndex] = OBJECTSHROUD_FOGGED; //object is visible but fogged.
- if (m_object && m_ghostObject) //object does not exist for modules holding only GhostObjects
- {
- //fogged but may not be visible if faction unit or faction building that has not been seen before
- Player *player=ThePlayerList->getNthPlayer(playerIndex);
- if (player->getRelationship(m_object->getTeam()) == NEUTRAL)
- { //anything neutral that moves around will not be rendered inside fog.
- if (!m_object->isKindOf(KINDOF_IMMOBILE))
- m_shroudedness[playerIndex] = OBJECTSHROUD_SHROUDED;
- }
- else //Not neutral
- { //enemy unit will always be shrouded unless it's a building that's already been seen by the player. Fogged Mines are also always
- //shroued no matter what.
- if (!(m_object->isKindOf(KINDOF_IMMOBILE) && m_everSeenByPlayer[playerIndex]) || m_object->isKindOf(KINDOF_MINE))
- m_shroudedness[playerIndex] = OBJECTSHROUD_SHROUDED;
- }
- if (m_shroudedness[playerIndex] == OBJECTSHROUD_FOGGED)
- { //successfully applied fog to object so check if we need to freeze it's state
- if (m_shroudednessPrevious[playerIndex] < OBJECTSHROUD_FOGGED)
- { //object was not previously fogged but now is fogged.
- //need to record its current state so that it doesn't change
- //while fogged.
- m_ghostObject->snapShot(playerIndex);
- }
- }
- }
- }
- else if( shroudedCells == 0 && foggedCells == 0 )
- { //Record that this object was seen by the player. This info will be used to show fogged enemy faction buildings.
- m_everSeenByPlayer[playerIndex] = true;
- m_shroudedness[playerIndex] = OBJECTSHROUD_CLEAR;
- if (m_ghostObject && m_shroudednessPrevious[playerIndex] == OBJECTSHROUD_FOGGED)
- { //object was previously fogged but now is visible so we no longer
- //need a ghost object.
- m_ghostObject->freeSnapShot(playerIndex);
- }
- } // no cell I use has anything
- else
- { //Record that this object was seen by the player. This info will be used to show fogged enemy faction buildings.
- m_everSeenByPlayer[playerIndex] = true;
- m_shroudedness[playerIndex] = OBJECTSHROUD_PARTIAL_CLEAR; // I am at least partially clear otherwise
- if (m_ghostObject && m_shroudednessPrevious[playerIndex] == OBJECTSHROUD_FOGGED)
- { //object was previously fogged but now is visible so we no longer
- //need a ghost object.
- m_ghostObject->freeSnapShot(playerIndex);
- }
- }
- #ifndef DISABLE_INVALID_PREVENTION
- if (m_coiInUseCount && updateShroudednessPrevious)
- {
- #else
- if (m_coiInUseCount)
- {
- #endif
- //only remember the previous state of objects actually on the map.
- m_shroudednessPrevious[playerIndex] = m_shroudedness[playerIndex];
- }
- }
- return m_shroudedness[playerIndex];
- }
- //-----------------------------------------------------------------------------
- void PartitionData::removeAllTouchedCells()
- {
- CellAndObjectIntersection *coi = m_coiArray;
- for (Int i = m_coiArrayCount; i > 0; --i, ++coi)
- {
- if (coi->getModule())
- {
- DEBUG_ASSERTCRASH(coi->getModule() == this, ("coi uses wrong module"));
- coi->removeAllCoverage();
- DEBUG_ASSERTCRASH(!coi->getModule(), ("coi should no longer be in use"));
- --m_coiInUseCount;
- }
- }
- DEBUG_ASSERTCRASH(m_coiInUseCount == 0, ("hmm, coi count mismatch"));
- }
- // -----------------------------------------------------------------------------
- void PartitionData::addSubPixToCoverage(PartitionCell *cell)
- {
- DEBUG_ASSERTCRASH(m_coiInUseCount < m_coiArrayCount, ("not enough cois allocated for this object"));
- if (cell)
- {
- // see if we already have a coi for this cell.
- CellAndObjectIntersection *coi = m_coiArray;
- CellAndObjectIntersection *coiToUse = NULL;
- for (Int i = m_coiInUseCount; i; --i, ++coi)
- {
- if (coi->getCell() == cell)
- {
- coiToUse = coi;
- break;
- }
- }
- DEBUG_ASSERTCRASH(coiToUse != NULL || m_coiInUseCount < m_coiArrayCount, ("not enough cois allocated for this object"));
- if (coiToUse == NULL && m_coiInUseCount < m_coiArrayCount)
- {
- // nope, no coi for this cell, allocate a new one
- coiToUse = &m_coiArray[m_coiInUseCount++];
- }
- if (coiToUse)
- {
- coiToUse->addCoverage(cell, this);
- }
- }
- }
- // -----------------------------------------------------------------------------
- void PartitionData::doRectFill(
- Real centerX,
- Real centerY,
- Real halfsizeX,
- Real halfsizeY,
- Real angle
- )
- {
- Real c = (Real)Cos(angle);
- Real s = (Real)Sin(angle);
- Real actualCellSize = ThePartitionManager->getCellSize();
- Real stepSize = actualCellSize * 0.5f; // in theory, should be getCellSize() exactly, but needs to be smaller to avoid aliasing problems
- Real ydx = s * stepSize;
- Real ydy = -c * stepSize;
- Real xdx = c * stepSize;
- Real xdy = s * stepSize;
- // GS 12-26-02 Oh my. The step size has been fudged down to ensure that a cell is not jumped over,
- // but then the original step size was used to determine the number of steps. This error gets
- // more pronounced on bigger buildings. ie a 100 X 160 registers cells for a 80 x 100. If you
- // fudge one, you have to fudge the other.
- // Real stepSizeInvTimes2 = 2.0f * ThePartitionManager->getCellSizeInv();
- Real stepSizeInvTimes2 = 2.0f * (1.0f / stepSize);
- /*
- srj fixes subtle bug: need +1 to get all of misaligned geometries accted for.
- */
- // Int numStepsX = REAL_TO_INT_CEIL(halfsizeX * stepSizeInvTimes2) + 1;
- // Int numStepsY = REAL_TO_INT_CEIL(halfsizeY * stepSizeInvTimes2) + 1;
- Int numStepsX = REAL_TO_INT_CEIL(halfsizeX * stepSizeInvTimes2);
- Int numStepsY = REAL_TO_INT_CEIL(halfsizeY * stepSizeInvTimes2);
- // GS 12-26-02 Oh my part 2. Now that we are no longer greatly underestimating our COI imprint,
- // we don't need this fudge. The fudge is not in the calcMaxCOIForShape, so it just results in
- // good COIs getting dropped since we didn't allocate enough to handle this fudge.
- Real tl_x = centerX - halfsizeX*c - halfsizeY*s;
- Real tl_y = centerY + halfsizeY*c - halfsizeX*s;
- for (Int iy = 0; iy < numStepsY; ++iy, tl_x += ydx, tl_y += ydy)
- {
- Real x = tl_x;
- Real y = tl_y;
- for (Int ix = 0; ix < numStepsX; ++ix, x += xdx, y += xdy)
- {
- Int cellx, celly;
- ThePartitionManager->worldToCell(x, y, &cellx, &celly);
- PartitionCell *cell = ThePartitionManager->getCellAt(cellx, celly); // might be null if off the edge
- if (cell)
- {
- addSubPixToCoverage(cell);
- }
- }
- }
- }
- // -----------------------------------------------------------------------------
- void PartitionData::hLineCircle(Int x1, Int x2, Int y)
- {
- for (Int x = x1; x <= x2; ++x)
- {
- PartitionCell* cell = ThePartitionManager->getCellAt(x, y);
- if (cell)
- {
- addSubPixToCoverage(cell);
- }
- }
- }
- // -----------------------------------------------------------------------------
- void PartitionData::doCircleFill(
- Real centerX,
- Real centerY,
- Real radius
- )
- {
- DEBUG_ASSERTCRASH(m_coiInUseCount == 0, ("expected no coi in use here"));
- Int cellCenterX, cellCenterY;
- ThePartitionManager->worldToCell(centerX, centerY, &cellCenterX, &cellCenterY);
- Int cellRadius = ThePartitionManager->worldToCellDist(radius);
- if (cellRadius < 1)
- cellRadius = 1;
- Int y = cellRadius - 1;
- Int dec = 3 - 2*cellRadius;
- for (Int x = 0; x < cellRadius; x++)
- {
- hLineCircle(cellCenterX - x, cellCenterX + x, cellCenterY + y);
- hLineCircle(cellCenterX - x, cellCenterX + x, cellCenterY - y);
- hLineCircle(cellCenterX - y, cellCenterX + y, cellCenterY + x);
- hLineCircle(cellCenterX - y, cellCenterX + y, cellCenterY - x);
- if (dec >= 0)
- {
- dec += (1 - y) << 2;
- --y;
- }
- dec += (x << 2) + 6;
- }
- }
- // -----------------------------------------------------------------------------
- void PartitionData::doSmallFill(
- Real centerX,
- Real centerY,
- Real radius
- )
- {
- DEBUG_ASSERTCRASH(m_coiInUseCount == 0, ("expected no coi in use here"));
- Real halfCellSize = ThePartitionManager->getCellSize() * 0.5f;
- if (radius > halfCellSize)
- {
- DEBUG_CRASH(("object is too large to use a 'small' geometry, truncating size to cellsize\n"));
- radius = halfCellSize;
- }
- Int cx1, cy1, cx2, cy2;
- ThePartitionManager->worldToCell(centerX - radius, centerY - radius, &cx1, &cy1);
- ThePartitionManager->worldToCell(centerX + radius, centerY + radius, &cx2, &cy2);
- DEBUG_ASSERTCRASH(absInt(cx2-cx1)<=1,("bad cx"));
- DEBUG_ASSERTCRASH(absInt(cy2-cy1)<=1,("bad cy"));
- for (Int x = cx1; x <= cx2; x++)
- {
- for (Int y = cy1; y <= cy2; y++)
- {
- PartitionCell *cell = ThePartitionManager->getCellAt(x, y);
- if (cell)
- {
- m_coiArray[m_coiInUseCount++].addCoverage(cell, this);
- }
- }
- }
- #ifdef INTENSE_DEBUG
- for (int i = 0; i < m_coiInUseCount; i++)
- {
- for (int j = 0; j < i; j++)
- {
- DEBUG_ASSERTCRASH(m_coiArray[i].getCell() != m_coiArray[j].getCell(), ("dup cells"));
- }
- }
- #endif
- }
- //-----------------------------------------------------------------------------
- void PartitionData::addPossibleCollisions(PartitionContactList *ctList)
- {
- // actually, we do occasionally want to detect collisions of dead AIs.
- // e.g., dead technicals flying thru the air should check for collisions with
- // immobile structures, because otherwise they pass thru them, which looks dopey.
- // if preventing dead-ai-collisions is necessary to fix something else, please
- // ensure that the above case is cool before signing off. (srj)
- #ifdef NOPE_READ_THE_COMMENT
- // dead objects never collide with things...
- AIUpdateInterface *ai = getObject()->getAIUpdateInterface();
- if (ai && ai->isAiInDeadState())
- {
- return;
- }
- #endif
- //DEBUG_LOG(("adding possible collision for %s\n",getObject()->getTemplate()->getName().str()));
- CellAndObjectIntersection *myCoi = m_coiArray;
- for (Int i = m_coiInUseCount; i > 0; --i, ++myCoi)
- {
- PartitionCell *cell = myCoi->getCell();
- if (cell->getCoiCount() < 2)
- continue;
- for (CellAndObjectIntersection *coi = cell->getFirstCoiInCell(); coi; coi = coi->getNextCoi())
- {
- PartitionData *that = coi->getModule();
- if (this != that)
- {
- ctList->addToContactList(this, that);
- }
- }
- }
- }
- //-----------------------------------------------------------------------------
- Bool PartitionData::collidesWith(const PartitionData *that, CollideLocAndNormal *cinfo) const
- {
- const Object *thisObj = this->getObject();
- const Object *thatObj = that->getObject();
- if( thisObj->isKindOf( KINDOF_NO_COLLIDE ) || thatObj->isKindOf( KINDOF_NO_COLLIDE ) )
- return FALSE; // A collision extent of zero size is still a point and can collide, but we don't always want to.
- CollideInfo thisInfo(thisObj->getPosition(), thisObj->getGeometryInfo(), thisObj->getOrientation());
- CollideInfo thatInfo(thatObj->getPosition(), thatObj->getGeometryInfo(), thatObj->getOrientation());
- // invariant for all geometries: first do z collision check.
- Real thisTop = thisInfo.position.z + thisInfo.geom.getMaxHeightAbovePosition();
- Real thisBot = thisInfo.position.z - thisInfo.geom.getMaxHeightBelowPosition();
- Real thatTop = thatInfo.position.z + thatInfo.geom.getMaxHeightAbovePosition();
- Real thatBot = thatInfo.position.z - thatInfo.geom.getMaxHeightBelowPosition();
- if (thisTop >= thatBot && thisBot <= thatTop)
- {
- GeometryType thisGeom = thisObj->getGeometryInfo().getGeomType();
- GeometryType thatGeom = thatObj->getGeometryInfo().getGeomType();
- //
- // NOTE: This assumes geometry enumerations that start at GEOMETRY_FIRST AND depends on the
- // order in which they appear in the enum list
- //
- CollideTestProc collideProc = theCollideTestProcs[ (thisGeom - GEOMETRY_FIRST) * GEOMETRY_NUM_TYPES + (thatGeom - GEOMETRY_FIRST) ];
- return (*collideProc)(&thisInfo, &thatInfo, cinfo);
- }
- else
- {
- // no z-intersection -> no collision.
- return false;
- }
- }
- //-----------------------------------------------------------------------------
- /* See if thisObj collides with geom at pos & angle. */
- Bool PartitionManager::geomCollidesWithGeom(const Coord3D* pos1,
- const GeometryInfo& geom1,
- Real angle1,
- const Coord3D* pos2,
- const GeometryInfo& geom2,
- Real angle2) const
- {
- CollideInfo thisInfo(pos1, geom1, angle1);
- CollideInfo thatInfo(pos2, geom2, angle2);
- // invariant for all geometries: first do z collision check.
- if (thisInfo.position.z + thisInfo.geom.getMaxHeightAbovePosition() >= thatInfo.position.z &&
- thisInfo.position.z <= thatInfo.position.z + thatInfo.geom.getMaxHeightAbovePosition())
- {
- GeometryType thisGeom = geom1.getGeomType();
- GeometryType thatGeom = geom2.getGeomType();
- //
- // NOTE: This assumes geometry enumerations that start at GEOMETRY_FIRST AND depends on the
- // order in which they appear in the enum list
- //
- CollideTestProc collideProc = theCollideTestProcs[ (thisGeom - GEOMETRY_FIRST) * GEOMETRY_NUM_TYPES + (thatGeom - GEOMETRY_FIRST) ];
- CollideLocAndNormal cloc;
- return (*collideProc)(&thisInfo, &thatInfo, &cloc);
- }
- else
- {
- // no z-intersection -> no collision.
- return false;
- }
- }
- //-----------------------------------------------------------------------------
- void PartitionData::updateCellsTouched()
- {
- GeometryType geom;
- Bool isSmall;
- Coord3D pos;
- Real angle,majorRadius,minorRadius;
- Object *obj = getObject();
- DEBUG_ASSERTCRASH(obj != NULL || m_ghostObject != NULL, ("must be attached to an Object here 1"));
- if (obj)
- {
- //we have no object using this PartitionData but we still have a GhostObject so copy its data.
- geom = obj->getGeometryInfo().getGeomType();
- isSmall = obj->getGeometryInfo().getIsSmall();
- pos = *(obj->getPosition());
- angle = obj->getOrientation();
- majorRadius = obj->getGeometryInfo().getMajorRadius();
- minorRadius = obj->getGeometryInfo().getMinorRadius();
- }
- else if (m_ghostObject)
- {
- geom = m_ghostObject->getGeometryType();
- isSmall = m_ghostObject->getGeometrySmall();
- pos = *m_ghostObject->getParentPosition();
- angle = m_ghostObject->getParentAngle();
- majorRadius = m_ghostObject->getGeometryMajorRadius();
- minorRadius = m_ghostObject->getGeometryMinorRadius();
- }
- removeAllTouchedCells();
- if (isSmall)
- {
- doSmallFill(pos.x, pos.y, majorRadius);
- }
- else
- {
- switch(geom)
- {
- case GEOMETRY_SPHERE:
- case GEOMETRY_CYLINDER:
- {
- doCircleFill(pos.x, pos.y, majorRadius);
- break;
- }
- case GEOMETRY_BOX:
- {
- doRectFill(pos.x, pos.y, majorRadius, minorRadius, angle);
- break;
- }
- };
- }
- Int currentCellIndexX, currentCellIndexY;
- ThePartitionManager->worldToCell( pos.x, pos.y, ¤tCellIndexX, ¤tCellIndexY );
- const PartitionCell *currentCell = ThePartitionManager->getCellAt( currentCellIndexX, currentCellIndexY );
- if(obj && currentCell != m_lastCell )
- {
- // To not expose PartitionCells, he will think in terms of points. He will
- // unlook at a point and look at the new point. We do the rounding and the
- // changing into PartitionCells
- obj->onPartitionCellChange();
- m_lastCell = currentCell;
- }
- // if we have moved, our shroudedness status might be different for all players,
- // so it must all be invalidated.
- invalidateShroudedStatusForAllPlayers();
- #ifdef INTENSE_DEBUG
- for (Int i = 0; i < m_coiInUseCount; i++)
- {
- for (Int j = 0; j < i; j++)
- {
- if (m_coiArray[i].getCell() == m_coiArray[j].getCell())
- {
- DEBUG_CRASH(("dup cells in COI array, this is bad"));
- }
- }
- }
- #endif
- }
- //-----------------------------------------------------------------------------
- void PartitionData::invalidateShroudedStatusForPlayer(Int playerIndex)
- {
- #ifndef DISABLE_INVALID_PREVENTION
- if (m_shroudedness[playerIndex] != OBJECTSHROUD_INVALID && m_shroudedness[playerIndex] != OBJECTSHROUD_INVALID_BUT_PREVIOUS_VALID)
- #endif
- m_shroudedness[playerIndex] = OBJECTSHROUD_INVALID;
- }
- //-----------------------------------------------------------------------------
- void PartitionData::invalidateShroudedStatusForAllPlayers()
- {
- for (Int i = 0; i < MAX_PLAYER_COUNT; i++)
- {
- invalidateShroudedStatusForPlayer(i);
- }
- }
- #if defined(_DEBUG) || defined(_INTERNAL)
- static AsciiString theObjName;
- #endif
- //-----------------------------------------------------------------------------
- Int PartitionData::calcMaxCoiForShape(GeometryType geom, Real majorRadius, Real minorRadius, Bool isSmall)
- {
- Int result;
- if (isSmall)
- {
- #if defined(_DEBUG) || defined(_INTERNAL)
- Int chk = calcMaxCoiForShape(geom, majorRadius, minorRadius, false);
- DEBUG_ASSERTCRASH(chk <= 4, ("Small objects should be <= 4 cells, but I calced %s as %d\n",theObjName.str(),chk));
- #endif
- result = 4;
- }
- else
- {
- switch(geom)
- {
- case GEOMETRY_SPHERE:
- case GEOMETRY_CYLINDER:
- {
- // note that majorRadius is a radius, not a diameter.
- // this actually allocates a few too many, but that's ok.
- Int cells = ThePartitionManager->worldToCellDist(majorRadius*2) + 1;
- result = cells * cells;
- }
- case GEOMETRY_BOX:
- {
- Real diagonal = (Real)(sqrtf(majorRadius*majorRadius + minorRadius*minorRadius));
- Int cells = ThePartitionManager->worldToCellDist(diagonal*2) + 1;
- result = cells * cells;
- }
- };
- }
- if (result < 4)
- result = 4;
- return result;
- }
- //-----------------------------------------------------------------------------
- Int PartitionData::calcMaxCoiForObject()
- {
- Object *obj = getObject();
- DEBUG_ASSERTCRASH(obj != NULL, ("must be attached to an Object here 2"));
-
- GeometryType geom = obj->getGeometryInfo().getGeomType();
- Real majorRadius = obj->getGeometryInfo().getMajorRadius();
- Real minorRadius = obj->getGeometryInfo().getMinorRadius();
- Bool isSmall = obj->getGeometryInfo().getIsSmall();
- #if defined(_DEBUG) || defined(_INTERNAL)
- theObjName = obj->getTemplate()->getName();
- #endif
- return calcMaxCoiForShape(geom, majorRadius, minorRadius, isSmall);
- }
- //-----------------------------------------------------------------------------
- void PartitionData::makeDirty(Bool needToUpdateCells)
- {
- //DEBUG_LOG(("makeDirty for pd %08lx obj %08lx\n",this,m_object));
- if (!ThePartitionManager->isInListDirtyModules(this))
- {
- if (needToUpdateCells)
- m_dirtyStatus = NEED_CELL_UPDATE_AND_COLLISION_CHECK;
- else if (m_dirtyStatus == NOT_DIRTY)
- m_dirtyStatus = NEED_COLLISION_CHECK;
- ThePartitionManager->prependToDirtyModules(this);
- }
- else if (needToUpdateCells)
- {
- // If I am in the list already, I may be in there for a half update, so if my
- // needs have increased I'll upgrade what I want.
- m_dirtyStatus = NEED_CELL_UPDATE_AND_COLLISION_CHECK;
- }
- }
- //-----------------------------------------------------------------------------
- void PartitionData::allocCoiArray()
- {
- DEBUG_ASSERTCRASH(m_coiArrayCount == 0 && m_coiArray == NULL, ("hmm, coi should probably be null here"));
- DEBUG_ASSERTCRASH(m_coiInUseCount == 0, ("hmm, coi count mismatch"));
- m_coiArrayCount = calcMaxCoiForObject();
- m_coiArray = MSGNEW("PartitionManager_COI") CellAndObjectIntersection[m_coiArrayCount]; // may throw!
- m_coiInUseCount = 0;
- makeDirty(true);
- }
- //-----------------------------------------------------------------------------
- void PartitionData::freeCoiArray()
- {
- delete [] m_coiArray; // yes, it's OK to call this on null...
- m_coiArray = NULL;
- m_coiArrayCount = 0;
- m_coiInUseCount = 0;
- makeDirty(true);
- }
- //-----------------------------------------------------------------------------
- void PartitionData::attachToObject(Object* object)
- {
- // remember who contains us
- m_object = object;
- // we only snapshot things that are immobile and have something to draw. Don't need ghostobjects for others.
- if (object->isKindOf(KINDOF_IMMOBILE))
- { const ThingTemplate *tmplate=object->getTemplate();
- const ModuleInfo &drawMod=tmplate->getDrawModuleInfo();
- //Objects with default draw modules usually don't need ghostobjects so skip them to save memory.
- Bool makeGhostObject=TRUE;
- for (Int i=0; i<drawMod.getCount(); i++)
- {
- if (strcmp(drawMod.getNthName(i).str(),"W3DDefaultDraw") == 0)
- { makeGhostObject=FALSE;
- break;
- }
- }
- if (makeGhostObject)
- m_ghostObject = TheGhostObjectManager->addGhostObject(object, this);
- }
- //DEBUG_LOG(("attach pd for pd %08lx obj %08lx\n",this,m_object));
- // (re)calc maxCoi and (re)alloc cois
- DEBUG_ASSERTCRASH(m_coiArrayCount == 0 && m_coiArray == NULL, ("hmm, coi should probably be null here"));
- DEBUG_ASSERTCRASH(m_coiInUseCount == 0, ("hmm, coi count mismatch"));
- freeCoiArray();
- allocCoiArray(); // may throw!
- if (object)
- object->friend_setPartitionData(this);
- makeDirty(true);
- }
- //-----------------------------------------------------------------------------
- void PartitionData::detachFromObject()
- {
- // this is a little hokey... if we are in the midst of processing the contact
- // list, we have to ensure that any potential collisions get removed from that
- // list, since we are about to go bye-bye. ugly, but effective. (srj)
- //
- // note 2: we used to do this in our dtor, but it's too late then, since we require
- // our Object to still be present in order to figure out what to remove.
- // so do it here, since detaching it should nuke it from the obj list anyway. (srj)
- if (TheContactList)
- TheContactList->removeSpecificPartitionData(this);
- if (m_object)
- m_object->friend_setPartitionData(NULL);
- removeAllTouchedCells();
- freeCoiArray();
- //DEBUG_LOG(("detach pd for pd %08lx obj %08lx\n",this,m_object));
- // no longer attached to object
- m_object = NULL;
- TheGhostObjectManager->removeGhostObject(m_ghostObject);
- m_ghostObject = NULL;
- }
- //-----------------------------------------------------------------------------
- void PartitionData::attachToGhostObject(GhostObject* object)
- {
- // remember who contains us
- m_object = NULL; //it's only attached to a ghost object, no parent object.
- m_ghostObject = object;
- // (re)calc maxCoi and (re)alloc cois
- DEBUG_ASSERTCRASH(m_coiArrayCount == 0 && m_coiArray == NULL, ("hmm, coi should probably be null here"));
- DEBUG_ASSERTCRASH(m_coiInUseCount == 0, ("hmm, coi count mismatch"));
- freeCoiArray();
- m_coiArrayCount = calcMaxCoiForShape(object->getGeometryType(), object->getGeometryMajorRadius(), object->getGeometryMinorRadius(),object->getGeometrySmall());
- m_coiArray = MSGNEW("PartitionManager_COI") CellAndObjectIntersection[m_coiArrayCount]; // may throw!
- m_coiInUseCount = 0;
- makeDirty(true);
- if (m_ghostObject)
- m_ghostObject->updateParentObject(NULL,this);
- }
- //-----------------------------------------------------------------------------
- void PartitionData::detachFromGhostObject(void)
- {
- // this is a little hokey... if we are in the midst of processing the contact
- // list, we have to ensure that any potential collisions get removed from that
- // list, since we are about to go bye-bye. ugly, but effective. (srj)
- //
- // note 2: we used to do this in our dtor, but it's too late then, since we require
- // our Object to still be present in order to figure out what to remove.
- // so do it here, since detaching it should nuke it from the obj list anyway. (srj)
- if (TheContactList)
- TheContactList->removeSpecificPartitionData(this);
- removeAllTouchedCells();
- freeCoiArray();
- //DEBUG_LOG(("detach pd for pd %08lx obj %08lx\n",this,m_object));
- // no longer attached to object
- m_object = NULL;
- m_ghostObject = NULL;
- }
- //-----------------------------------------------------------------------------
- inline UnsignedInt hash2ints(Int a, Int b)
- {
- // do it this way so that [a,b] always hashes to the same value as [b,a].
- // this is unsophisticated but reasonable, since all ObjectIDs will
- // quite likely be well below 65536...
- if (a < b)
- {
- return (a<<16)+b;
- }
- else
- {
- return (b<<16)+a;
- }
- }
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- void PartitionContactList::addToContactList( PartitionData *obj, PartitionData *other )
- {
- if (obj == other || obj == NULL || other == NULL)
- return;
-
- Object* obj_obj = obj->getObject();
- Object* other_obj = other->getObject();
- if (obj_obj == NULL || other_obj == NULL)
- return;
- // compute hash index based on object's ids.
- UnsignedInt hashValue = hash2ints(obj_obj->getID(), other_obj->getID());
- hashValue %= PartitionContactList_SOCKET_COUNT;
- // make sure given hit has not already been recorded
- for (PartitionContactListNode* cd = m_contactHash[ hashValue ]; cd; cd = cd->m_nextHash )
- {
- if ((cd->m_obj == obj && cd->m_other == other) ||
- (cd->m_obj == other && cd->m_other == obj))
- {
- // already noted
- return;
- }
- }
- // new hit
- PartitionContactListNode *ncd = newInstance(PartitionContactListNode);
- ncd->m_obj = obj;
- ncd->m_other = other;
- ncd->m_hashValue = hashValue;
- // add to hash table
- ncd->m_nextHash = m_contactHash[ hashValue ];
- m_contactHash[ hashValue ] = ncd;
- // add to list of contacts for this frame
- ncd->m_next = m_contactList;
- m_contactList = ncd;
- #if 0
- Int depth = 0;
- for (PartitionContactListNode *cd2 = m_contactHash[ hashValue ]; cd2; cd2 = cd2->m_nextHash )
- {
- depth++;
- }
- if (depth > 3)
- {
- DEBUG_LOG(("depth is %d for %s %08lx (%d) - %s %08lx (%d)\n",
- depth,obj_obj->getTemplate()->getName().str(),obj_obj,obj_obj->getID(),
- other_obj->getTemplate()->getName().str(),other_obj,other_obj->getID()
- ));
- for (cd2 = m_contactHash[ hashValue ]; cd2; cd2 = cd2->m_nextHash )
- {
- UnsignedInt rawhash = djb2hash2ints(cd2->m_obj->getObject()->getID(), cd2->m_other->getObject()->getID());
- //hashValue %= PartitionContactList_SOCKET_COUNT;
- DEBUG_LOG(("ENTRY: %s %08lx (%d) - %s %08lx (%d) [rawhash %d]\n",
- cd2->m_obj->getObject()->getTemplate()->getName().str(),cd2->m_obj->getObject(),cd2->m_obj->getObject()->getID(),
- cd2->m_other->getObject()->getTemplate()->getName().str(),cd2->m_other->getObject(),cd2->m_other->getObject()->getID(),
- rawhash));
- }
- }
- static Real aggtotal = 0;
- static Real aggfull = 0;
- static Real aggcount = 0;
- for (int ii = 0; ii < PartitionContactList_SOCKET_COUNT; ++ii)
- {
- if (m_contactHash[ii])
- aggfull += 1.0f;
- for (cd2 = m_contactHash[ ii ]; cd2; cd2 = cd2->m_nextHash )
- {
- aggtotal += 1.0f;
- }
- }
- aggcount += 1.0f;
- DEBUG_ASSERTLOG(((Int)aggcount)%1000!=0,("avg hash depth at %f is %f, fullness %f%%\n",
- aggcount,aggtotal/(aggcount*PartitionContactList_SOCKET_COUNT),(aggfull*100)/(aggcount*PartitionContactList_SOCKET_COUNT)));
- #endif
- }
- //-----------------------------------------------------------------------------
- void PartitionContactList::removeSpecificPartitionData(PartitionData* data)
- {
- for (PartitionContactListNode* cd = m_contactList; cd; cd = cd->m_next)
- {
- if (cd->m_obj == data || cd->m_other == data)
- {
- cd->m_obj = NULL;
- cd->m_other = NULL;
- }
- }
- }
- //-----------------------------------------------------------------------------
- void PartitionContactList::resetContactList()
- {
- // remove items from hash table
- PartitionContactListNode* cdnext;
- for (PartitionContactListNode* cd = m_contactList; cd; cd = cdnext)
- {
- cdnext = cd->m_next;
- cd->deleteInstance();
- }
- memset(m_contactHash, 0, sizeof(m_contactHash));
- m_contactList = NULL;
- }
- //-----------------------------------------------------------------------------
- void PartitionContactList::processContactList()
- {
- for (PartitionContactListNode* cd = m_contactList; cd; cd = cd->m_next)
- {
- if (cd->m_obj == NULL || cd->m_other == NULL)
- continue;
- // we know that their partitions overlap; determine if they REALLY collide
- // before proceeding...
- CollideLocAndNormal cinfo;
- if (!cd->m_obj->friend_collidesWith(cd->m_other, &cinfo))
- continue;
- Object* obj = cd->m_obj->getObject();
- Object* other = cd->m_other->getObject();
-
- if ((obj->getStatusBits() & OBJECT_STATUS_NO_COLLISIONS) != 0 ||
- (other->getStatusBits() & OBJECT_STATUS_NO_COLLISIONS) != 0)
- continue;
- DEBUG_ASSERTCRASH(!(obj->isKindOf(KINDOF_IMMOBILE) && other->isKindOf(KINDOF_IMMOBILE)),
- ("we should never have collisions between two immobile things reported"));
- // the onCollide() calls can remove the object(s) from the partition mgr,
- // thus destroying the partitiondata for 'em. go ahead and null these out here
- // so we won't be tempted to use 'em (since they might be bogus).
- cd->m_obj = NULL;
- cd->m_other = NULL;
- obj->onCollide(other, &cinfo.loc, &cinfo.normal);
- flipCoord3D(&cinfo.normal);
- //Before checking the "other" case, make sure that the previous collision didn't
- //absorb him. This becomes a conflict for pilots giving veterancy to transports
- //and pilots entering transports. Both would occur if these isDestroyed checks
- //were missing.
- if( !obj->isDestroyed() && !other->isDestroyed() )
- {
- other->onCollide(obj, &cinfo.loc, &cinfo.normal);
- }
- //
- // NOTE: it is VERY IMPORTANT (for performance reasons) to not re-dirty immobile things.
- //
- // NOTE also that we re-get partitiondata from the object, since it might have been
- // removed from the partition system by the onCollide call...
- //
- if (!obj->isDestroyed() && obj->friend_getPartitionData() != NULL && !obj->isKindOf(KINDOF_IMMOBILE))
- {
- //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));
- obj->friend_getPartitionData()->makeDirty(false);
- }
- if (!other->isDestroyed() && other->friend_getPartitionData() != NULL && !other->isKindOf(KINDOF_IMMOBILE))
- {
- //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));
- other->friend_getPartitionData()->makeDirty(false);
- }
- }
- }
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- PartitionManager::PartitionManager()
- {
- m_moduleList = NULL;
- m_cellSize = m_cellSizeInv = 0.0f;
- m_cellCountX = 0;
- m_cellCountY = 0;
- m_totalCellCount = 0;
- m_cells = NULL;
- m_worldExtents.lo.zero();
- m_worldExtents.hi.zero();
- m_dirtyModules = NULL;
- m_updatedSinceLastReset = false;
- #ifdef FASTER_GCO
- m_maxGcoRadius = 0;
- #endif
- }
- //-----------------------------------------------------------------------------
- PartitionManager::~PartitionManager()
- {
- shutdown();
- } // end ~PartitionManager
- //-----------------------------------------------------------------------------
- #ifdef PM_CACHE_TERRAIN_HEIGHT
- static void calcHeights(const Region3D& world, Real cellSize, Int x, Int y, Real& loZ, Real& hiZ)
- {
- DEBUG_ASSERTCRASH(TheTerrainLogic, ("no TheTerrainLogic"));
- Real xbase = world.lo.x + (x * cellSize);
- Real ybase = world.lo.y + (y * cellSize);
- const Real ROUGH_STEP_SIZE = 2; // roughly every 2 ft, please
- Real numSteps = ceilf(cellSize / ROUGH_STEP_SIZE);
- Real step = cellSize / numSteps;
- loZ = HUGE_DIST; // huge positive
- hiZ = -HUGE_DIST; // huge negative
- for (Real yy = 0; yy <= cellSize; yy += step)
- {
- for (Real xx = 0; xx <= cellSize; xx += step)
- {
- Real h = TheTerrainLogic->getGroundHeight( xbase + xx, ybase + yy );
- if (h < loZ) loZ = h;
- if (h > hiZ) hiZ = h;
- }
- }
- }
- #endif
- //-----------------------------------------------------------------------------
- void PartitionManager::init()
- {
- m_cellSize = TheGlobalData->m_partitionCellSize;
- if (m_cellSize < 1.0)
- m_cellSize = 1.0;
- m_cellSizeInv = (Real)(1.0 / m_cellSize);
- DEBUG_ASSERTCRASH(m_cells == NULL, ("double init"));
- if (TheTerrainLogic)
- {
- TheTerrainLogic->getExtent(&m_worldExtents);
- //DEBUG_ASSERTLOG(m_worldExtents.width() > 0 && m_worldExtents.height() > 0, ("TheTerrainLogic must be loaded before ThePartitionManager"));
- // you'd think it wouldn't be legal for terrainlogic to have zero area, but apparently
- // that's what it resets itself to. let's just make the world a simple place and pretend
- // it's nonzero in area, so that we can proceed without crashing.
- if (m_worldExtents.width() < 1.0f)
- m_worldExtents.hi.x = m_worldExtents.lo.x + 1.0f;
- if (m_worldExtents.height() < 1.0f)
- m_worldExtents.hi.y = m_worldExtents.lo.y + 1.0f;
- m_cellCountX = REAL_TO_INT_CEIL(m_worldExtents.width() * m_cellSizeInv);
- m_cellCountY = REAL_TO_INT_CEIL(m_worldExtents.height() * m_cellSizeInv);
- m_totalCellCount = m_cellCountX * m_cellCountY;
- m_cells = MSGNEW("PartitionManager_Cells") PartitionCell[m_totalCellCount];
- for (Int x = 0; x < m_cellCountX; x++)
- {
- for (Int y = 0; y < m_cellCountY; y++)
- {
- #ifdef PM_CACHE_TERRAIN_HEIGHT
- Real loZ, hiZ;
- calcHeights(m_worldExtents, m_cellSize, x, y, loZ, hiZ);
- getCellAt(x, y)->init(x, y, loZ, hiZ);
- #else
- getCellAt(x, y)->init(x, y);
- #endif
- }
- }
- #ifdef FASTER_GCO
- calcRadiusVec();
- #endif
- }
- else
- {
- m_cellSize = m_cellSizeInv = 0.0f;
- m_cellCountX = 0;
- m_cellCountY = 0;
- m_totalCellCount = 0;
- m_cells = NULL;
- m_worldExtents.lo.zero();
- m_worldExtents.hi.zero();
- }
- }
- //-----------------------------------------------------------------------------
- #ifdef DUMP_PERF_STATS
- void PartitionManager::getPMStats(double& gcoTimeThisFrameTotal, double& gcoTimeThisFrameAvg)
- {
- Int64 freq64;
- GetPrecisionTimerTicksPerSec(&freq64);
- double gcoTimeInMSecs = (double)s_timeInClosestObjectsThisFrame * 1000.0f / (double)freq64;
- gcoTimeThisFrameTotal = gcoTimeInMSecs;
- gcoTimeThisFrameAvg = gcoTimeInMSecs / (double)s_countInClosestObjectsThisFrame;
- }
- #endif
- //-----------------------------------------------------------------------------
- void PartitionManager::reset()
- {
- #ifdef DUMP_PERF_STATS
- s_countInClosestObjects = 0;
- s_timeInClosestObjects = 0;
- s_countInClosestObjectsThisFrame = 0;
- s_timeInClosestObjectsThisFrame = 0;
- s_gcoPerfFrame = 0xffffffff;
- #endif
- resetPendingUndoShroudRevealQueue();
- shutdown();
- //init();
- }
- //-----------------------------------------------------------------------------
- void PartitionManager::shutdown()
- {
- m_updatedSinceLastReset = false;
- ThePartitionManager->removeAllDirtyModules();
- #ifdef _DEBUG
- // the above *should* remove all the touched cells (via unRegisterObject), but let's check:
- DEBUG_ASSERTCRASH( m_moduleList == NULL, ("hmm, modules left over"));
- PartitionData *mod, *nextMod;
- for( mod = m_moduleList; mod; mod = nextMod )
- {
- nextMod = mod->getNext();
- DEBUG_ASSERTCRASH(mod->friend_getCoiInUseCount() == 0, ("hmm, coi count mismatch"));
- mod->friend_removeAllTouchedCells();
- }
- #endif
- #ifdef FASTER_GCO
- m_radiusVec.clear();
- #endif
- resetPendingUndoShroudRevealQueue();
-
- delete [] m_cells;
- m_cells = NULL;
- m_cellSize = m_cellSizeInv = 0.0f;
- m_cellCountX = 0;
- m_cellCountY = 0;
- m_totalCellCount = 0;
- m_worldExtents.lo.zero();
- m_worldExtents.hi.zero();
- }
- //-----------------------------------------------------------------------------
- //DECLARE_PERF_TIMER(PartitionManager_update)
- void PartitionManager::update()
- {
- //USE_PERF_TIMER(PartitionManager_update)
- {
- #ifdef INTENSE_DEBUG
- Int cc = 0;
- #endif
- if (!m_updatedSinceLastReset)
- {
- m_updatedSinceLastReset = true;
- }
- PartitionContactList ctList;
- TheContactList = &ctList;
- while (m_dirtyModules)
- {
- #ifdef INTENSE_DEBUG
- ++cc;
- #endif
- // save it.
- PartitionData *dirty = m_dirtyModules;
- DEBUG_ASSERTCRASH(dirty->getObject() != NULL || dirty->getGhostObject() != NULL,
- ("must be attached to an Object here %08lx",dirty));
- // get this BEFORE removing from dirty list, since that clears the
- // flag in question.
- Bool updateEm = dirty->isInNeedOfUpdatingCells();
- Bool collideEm = dirty->isInNeedOfCollisionCheck() && dirty->getObject(); //only update collisions if we have object
-
- // detach it from the dirty list.
- removeFromDirtyModules(dirty);
- if (updateEm)
- {
- dirty->friend_updateCellsTouched();
- }
- if (collideEm && !dirty->getObject()->isKindOf(KINDOF_IMMOBILE))
- {
- dirty->addPossibleCollisions(&ctList);
- }
- }
-
- ctList.processContactList();
- #ifdef INTENSE_DEBUG
- DEBUG_ASSERTLOG(cc==0,("updated partition info for %d objects\n",cc));
- #endif
- TheContactList = NULL;
- processPendingUndoShroudRevealQueue();
- }
- #if defined(_DEBUG) || defined(_INTERNAL)
- if (TheGlobalData->m_debugThreatMap)
- {
- if (TheGameLogic->getFrame() % TheGlobalData->m_debugThreatMapTileDuration)
- {
- Int cellCount = m_cellCountX * m_cellCountY;
- for (int i = 0; i < cellCount; ++i)
- {
- UnsignedInt threat = m_cells[i].getThreatValue(ThePlayerList->getLocalPlayer()->getPlayerIndex());
- if (threat > 0)
- {
- Real threatMul = INT_TO_REAL(threat) / TheGlobalData->m_maxDebugThreat;
- if (threatMul > 1.0f)
- threatMul = 1.0f;
- Real size = TheGlobalData->m_partitionCellSize;
- Coord3D pos = { m_cells[i].getCellX() * size,
- m_cells[i].getCellY() * size,
- 0 };
- pos.z = TheTerrainLogic->getGroundHeight(pos.x, pos.y);
- RGBColor color;
- color.red = threatMul;
- color.blue = 0.0f;
- color.green = 0.0f;
- addIcon(&pos, size, TheGlobalData->m_debugThreatMapTileDuration, color);
- }
- }
- }
- }
- if (TheGlobalData->m_debugCashValueMap)
- {
- if (TheGameLogic->getFrame() % TheGlobalData->m_debugCashValueMapTileDuration)
- {
- Int cellCount = m_cellCountX * m_cellCountY;
- for (int i = 0; i < cellCount; ++i)
- {
- UnsignedInt value = m_cells[i].getCashValue(ThePlayerList->getLocalPlayer()->getPlayerIndex());
- if (value > 0)
- {
- Real valueMul = INT_TO_REAL(value) / TheGlobalData->m_maxDebugValue;
- if (valueMul > 1.0f)
- valueMul = 1.0f;
- Real size = TheGlobalData->m_partitionCellSize;
- Coord3D pos = { m_cells[i].getCellX() * size,
- m_cells[i].getCellY() * size,
- 0 };
- pos.z = TheTerrainLogic->getGroundHeight(pos.x, pos.y);
- RGBColor color;
- color.red = 0.0f;
- color.blue = 0.0f;
- color.green = valueMul;
- addIcon(&pos, size, TheGlobalData->m_debugCashValueMapTileDuration, color);
- }
- }
- }
- }
- #endif // defined(_DEBUG) || defined(_INTERNAL)
- } // end update
- //------------------------------------------------------------------------------
- void PartitionManager::registerObject( Object* object )
- {
- // sanity
- if( object == NULL )
- return;
- // if object is already part of this system get out of here
- if( object->friend_getPartitionData() != NULL )
- {
- DEBUG_LOG(( "Object '%s' already registered with partition manager\n",
- object->getTemplate()->getName().str() ));
- return;
- } // end if
- // allocate a new module of partition data
- PartitionData *mod = newInstance( PartitionData );
- // link the module to the list in the partition manager
- mod->setPrev( NULL );
- mod->setNext( m_moduleList );
- if( m_moduleList )
- m_moduleList->setPrev( mod );
- m_moduleList = mod;
- // add module to object
- mod->attachToObject( object );
- }
- //------------------------------------------------------------------------------
- void PartitionManager::unRegisterObject( Object* object )
- {
- // sanity
- if( object == NULL )
- return;
- // get the partition module
- PartitionData *mod = object->friend_getPartitionData();
- if( mod == NULL )
- return;
- GhostObject *ghost;
- // need to figure out if any players have a fogged memory of this object.
- // if so, we can't remove it from the shroud system just yet.
- if ((ghost=mod->getGhostObject()) != NULL && mod->wasSeenByAnyPlayers() < MAX_PLAYER_COUNT)
- {
- if (TheContactList)
- TheContactList->removeSpecificPartitionData(mod);
- object->friend_setPartitionData(NULL);
- mod->friend_setObject(NULL);
- //Tell the ghost object that its parent is dead.
- ghost->updateParentObject(NULL, mod);
- return;
- }
- // detach the module from the object
- mod->detachFromObject();
- // remove module from master list
- PartitionData *next = mod->getNext();
- PartitionData *prev = mod->getPrev();
- if( next )
- next->setPrev( prev );
- if( prev )
- prev->setNext( next );
- else
- m_moduleList = next;
- // delete module
- mod->deleteInstance();
- }
- //------------------------------------------------------------------------------
- void PartitionManager::registerGhostObject( GhostObject* object)
- {
- // sanity
- if( object == NULL )
- return;
- // if object is already part of this system get out of here
- if( object->friend_getPartitionData() != NULL )
- {
- DEBUG_LOG(( "GhostObject already registered with partition manager\n"));
- return;
- } // end if
- // allocate a new module of partition data
- PartitionData *mod = newInstance( PartitionData );
- // link the module to the list in the partition manager
- mod->setPrev( NULL );
- mod->setNext( m_moduleList );
- if( m_moduleList )
- m_moduleList->setPrev( mod );
- m_moduleList = mod;
- // add module to object
- mod->attachToGhostObject( object);
- }
- //------------------------------------------------------------------------------
- void PartitionManager::unRegisterGhostObject( GhostObject* object )
- {
- // sanity
- if( object == NULL )
- return;
- // get the partition module
- PartitionData *mod = object->friend_getPartitionData();
- if( mod == NULL )
- return;
- // detach the module from the object
- mod->detachFromGhostObject();
- // remove module from master list
- PartitionData *next = mod->getNext();
- PartitionData *prev = mod->getPrev();
- if( next )
- next->setPrev( prev );
- if( prev )
- prev->setNext( next );
- else
- m_moduleList = next;
- // delete module
- mod->deleteInstance();
- }
- /**
- Reveals the map for the given player, but does not override Shroud generation. (Script)
- */
- void PartitionManager::revealMapForPlayer( Int playerIndex )
- {
- // By looking and then stopping on every cell, I clear all Passive Shroud
- // By adding a looker directly I don't hit the Ally logic of the normal look/doShroudReveal
- for (int i = 0; i < m_totalCellCount; ++i)
- {
- m_cells[i].addLooker( playerIndex );
- m_cells[i].removeLooker( playerIndex );
- }
- }
- /**
- Reveals the map for the given player, AND permanently disables all Shroud generation (Observer Mode).
- */
- void PartitionManager::revealMapForPlayerPermanently( Int playerIndex )
- {
- // By skipping the removeLooker, I consider myself as actively looking at everything,
- // so Shroud generation will no longer function
- // By adding a looker directly I don't hit the Ally logic of the normal look/doShroudReveal
- for (int i = 0; i < m_totalCellCount; ++i)
- {
- m_cells[i].addLooker( playerIndex );
- }
- }
- /**
- Adds a layer of permanent blindness. Used solely to undo the permanent reveal for debugging
- */
- void PartitionManager::undoRevealMapForPlayerPermanently( Int playerIndex )
- {
- //First make sure no lingering looks will leave holes when they aren't wanted.
- processEntirePendingUndoShroudRevealQueue();
- // This will have amusing consequences if done without a preceding revealMapForPlayerPermanently.
- // Everything you own can become shrouded.
- for (int i = 0; i < m_totalCellCount; ++i)
- {
- m_cells[i].removeLooker( playerIndex );
- }
- }
- /**
- Resets the shroud for the given player with passive shroud (can re-explore).
- */
- void PartitionManager::shroudMapForPlayer( Int playerIndex )
- {
- //First make sure no lingering looks will leave holes when they aren't wanted.
- processEntirePendingUndoShroudRevealQueue();
- // By pulsing a blast of shroud like this, we will set everything not actively looked at as Passive Shroud
- for (int i = 0; i < m_totalCellCount; ++i)
- {
- m_cells[i].addShrouder( playerIndex );
- m_cells[i].removeShrouder( playerIndex );
- }
- }
- //-----------------------------------------------------------------------------
- void PartitionManager::refreshShroudForLocalPlayer()
- {
- // This is a drawing refresh only, and so is allowed to use the Local Player.
- TheDisplay->clearShroud();
- TheRadar->clearShroud();
- Int playerIndex = ThePlayerList->getLocalPlayer()->getPlayerIndex();
- for (int i = 0; i < m_totalCellCount; ++i)
- {
- Int x = m_cells[i].getCellX();
- Int y = m_cells[i].getCellY();
- CellShroudStatus status = m_cells[i].getShroudStatusForPlayer(playerIndex);
- TheDisplay->setShroudLevel(x, y, status);
- TheRadar->setShroudLevel(x, y, status);
- m_cells[i].invalidateShroudedStatusForAllCois(playerIndex);
- }
- }
- //-----------------------------------------------------------------------------
- CellShroudStatus PartitionManager::getShroudStatusForPlayer(Int playerIndex, Int x, Int y) const
- {
- if( playerIndex < 0 )
- return CELLSHROUD_SHROUDED;// Safety. There are no Negative players, but PlayerIndex is typedef'd to Int, not UnsignedInt
- const PartitionCell* cell = getCellAt(x, y);
- return cell ? cell->getShroudStatusForPlayer(playerIndex) : CELLSHROUD_SHROUDED;
- }
- //-----------------------------------------------------------------------------
- CellShroudStatus PartitionManager::getShroudStatusForPlayer(Int playerIndex, const Coord3D *loc ) const
- {
- Int x, y;
- ThePartitionManager->worldToCell( loc->x, loc->y, &x, &y );
- return getShroudStatusForPlayer( playerIndex, x, y );
- }
- #ifdef FASTER_GCO
- //-----------------------------------------------------------------------------
- Int PartitionManager::calcMinRadius(const ICoord2D& cur)
- {
- /*
- ok, so if obj "A" is in cell (0,0), and obj "B"
- is in cell (cur.x, cur.y), calc the smallest distance they could be
- from each other, in integral multiples of cellSize. Note that
- each object can be anywhere within a given cell (not just in the center!)
- so even objects in the same cell can still be cellSize*1.414 apart,
- and objects in adjacent cells can be nearly-zero distance apart!
- */
- Real halfCell = m_cellSize * 0.5;
- Coord3D centerPos[4] =
- {
- { -halfCell, -halfCell },
- { halfCell, -halfCell },
- { -halfCell, halfCell },
- { halfCell, halfCell }
- };
- Real x = cur.x * m_cellSize;
- Real y = cur.y * m_cellSize;
- Coord3D otherPos[4] =
- {
- { x - halfCell, y - halfCell },
- { x + halfCell, y - halfCell },
- { x - halfCell, y + halfCell },
- { x + halfCell, y + halfCell }
- };
- /*
- this is ugly and there's probably a faster way, but we only do this once per PM reset,
- so it really shouldn't matter... (I hope)
- */
- double minDistSqr = 1e12; // double, not real
- for (int i = 0; i < 4; ++i)
- {
- for (int j = 0; j < 4; ++j)
- {
- // double, not real
- double dx = centerPos[i].x - otherPos[j].x;
- double dy = centerPos[i].y - otherPos[j].y;
- double curDistSqr = dx*dx + dy*dy;
- if (minDistSqr > curDistSqr)
- minDistSqr = curDistSqr;
- }
- }
- // double, not real
- double dist = sqrtf(minDistSqr);
- Int minRadius = REAL_TO_INT_CEIL( dist / m_cellSize );
- return minRadius;
- }
- #endif
- #ifdef FASTER_GCO
- //-----------------------------------------------------------------------------
- void PartitionManager::calcRadiusVec()
- {
- Real cellSize = getCellSize();
- Int cx = getCellCountX();
- Int cy = getCellCountY();
- // double, not real
- double dx = (double)cx * (double)cellSize;
- double dy = (double)cy * (double)cellSize;
- double maxPossibleDist = sqrt(dx*dx + dy*dy);
-
- m_maxGcoRadius = REAL_TO_INT_CEIL(maxPossibleDist / cellSize);
-
- m_radiusVec.clear();
- m_radiusVec.resize(m_maxGcoRadius+1, OffsetVec());
- ICoord2D cur;
- for (cur.y = -cy+1; cur.y < cy; ++cur.y)
- {
- for (cur.x = -cx+1; cur.x < cx; ++cur.x)
- {
- /*
- m_radiusVec[curRadius] contains a list of the cells (foo) that could
- contain objects that are <= (curRadius * cellSize) distance away from cell (0,0).
- */
- Int curRadius = calcMinRadius(cur);
- DEBUG_ASSERTCRASH(curRadius <= m_maxGcoRadius, ("expected max of %d but got %d\n",m_maxGcoRadius,curRadius));
- if (curRadius <= m_maxGcoRadius)
- m_radiusVec[curRadius].push_back(cur);
- }
- }
- #if defined(_DEBUG) || defined(_INTERNAL)
- Int total = 0;
- for (Int i = 0; i <= m_maxGcoRadius; ++i)
- {
- total += m_radiusVec[i].size();
- //DEBUG_LOG(("radius %d has %d entries\n",i,m_radiusVec[i].size()));
- }
- DEBUG_ASSERTCRASH(total == (cx*2-1)*(cy*2-1),("expected %d, got %d\n",(cx*2-1)*(cy*2-1),total));
- #endif
- }
- #endif
- //-----------------------------------------------------------------------------
- //DECLARE_PERF_TIMER(getClosestObjects)
- Object *PartitionManager::getClosestObjects(
- const Object *obj,
- const Coord3D *pos,
- Real maxDist,
- DistanceCalculationType dc,
- PartitionFilter **filters,
- SimpleObjectIterator *iterArg, // if nonnull, append ALL satisfactory objects to the iterator (not just the single closest)
- Real *closestDistArg,
- Coord3D *closestVecArg
- )
- {
- //USE_PERF_TIMER(getClosestObjects)
- #ifdef DUMP_PERF_STATS
- if (TheGameLogic->getFrame() != s_gcoPerfFrame)
- {
- s_gcoPerfFrame = TheGameLogic->getFrame();
- s_countInClosestObjectsThisFrame = 0;
- s_timeInClosestObjectsThisFrame = 0;
- }
- ++s_countInClosestObjects;
- ++s_countInClosestObjectsThisFrame;
- Int64 startTime64;
- GetPrecisionTimer(&startTime64);
- #endif
-
- #ifdef _DEBUG
- static Int theEntrancyCount = 0;
- DEBUG_ASSERTCRASH(theEntrancyCount == 0, ("sorry, this routine is not reentrant"));
- ++theEntrancyCount;
- #endif
- DEBUG_ASSERTCRASH((obj==NULL) != (pos == NULL), ("either obj or pos must be null"));
- DistCalcProc distProc = theDistCalcProcs[dc];
- const Coord3D *objPos;
- const Object *objToUse;
- if (pos)
- {
- objPos = pos;
- objToUse = NULL;
- }
- else
- {
- objPos = obj->getPosition();
- objToUse = obj;
- }
- Int cellCenterX, cellCenterY;
- worldToCell(objPos->x, objPos->y, &cellCenterX, &cellCenterY);
- Object* closestObj = NULL;
- Real closestDistSqr = maxDist * maxDist; // if it's not closer than this, we shouldn't consider it anyway...
- Coord3D closestVec;
- #ifdef FASTER_GCO
- Int maxRadius = m_maxGcoRadius;
- if (maxDist < HUGE_DIST)
- {
- // don't go outwards any farther than necessary.
- maxRadius = minInt(m_maxGcoRadius, worldToCellDist(maxDist));
- }
- #if defined(INTENSE_DEBUG)
- /*
- Note, if you ever enable this code, be forewarned that it can give
- you "false positives" for objects that are located just off the map... (srj)
- */
- Int maxRadiusLimit = maxRadius + 3;
- if (maxRadiusLimit > m_maxGcoRadius) maxRadiusLimit = m_maxGcoRadius;
- #else
- Int maxRadiusLimit = maxRadius;
- #endif
- Bool foundAny = false;
- static Int theIterFlag = 1; // nonzero, thanks
- ++theIterFlag;
- /*
- m_radiusVec[curRadius] contains a list of the cells (foo) that could
- contain objects that are <= (curRadius * cellSize) distance away from cell (0,0).
- */
- for (Int curRadius = 0; curRadius <= maxRadiusLimit; ++curRadius)
- {
- const OffsetVec& offsets = m_radiusVec[curRadius];
- if (offsets.empty())
- continue;
- for (OffsetVec::const_iterator it = offsets.begin(); it != offsets.end(); ++it)
- {
- PartitionCell* thisCell = getCellAt(cellCenterX + it->x, cellCenterY + it->y);
- if (thisCell == NULL)
- continue;
- for (CellAndObjectIntersection *thisCoi = thisCell->getFirstCoiInCell(); thisCoi; thisCoi = thisCoi->getNextCoi())
- {
- PartitionData *thisMod = thisCoi->getModule();
- Object *thisObj = thisMod->getObject();
- // never compare against ourself.
- if (thisObj == obj || thisObj == NULL)
- continue;
- // since an object can exist in multiple COIs, we use this to avoid processing
- // the same one more than once.
- if (thisMod->friend_getDoneFlag() == theIterFlag)
- continue;
- thisMod->friend_setDoneFlag(theIterFlag);
-
- Real thisDistSqr;
- Coord3D distVec;
- if (!(*distProc)(objPos, objToUse, thisObj->getPosition(), thisObj, thisDistSqr, distVec, closestDistSqr))
- continue;
- if (!filtersAllow(filters, thisObj))
- continue;
- // ok, this is within the range, and the filters allow it.
- // add it to the iter, if we have one....
- if (iterArg)
- {
- iterArg->insert(thisObj, thisDistSqr);
- }
- else
- {
- // hey, this is the new closest object! cool.
- // (note that we can't break out now 'cuz we have to finish examining the
- // rest of curRadius)
- closestObj = thisObj;
- closestDistSqr = thisDistSqr;
- closestVec = distVec;
- if (!foundAny)
- {
- // if not adding to iterArg, we want to stop once we have the closest object.
- maxRadiusLimit = curRadius;
- }
- foundAny = true;
- }
- } // next coi
- } // next cell in this radius
- } // next radius
- #else // not FASTER_GCO
- CellOutwardIterator iter(this, cellCenterX, cellCenterY);
- if (maxDist < HUGE_DIST)
- {
- // don't go outwards any farther than necessary.
- Int max = worldToCellDist(maxDist) + 1;
- // default value for "max" is largest possible, based on map size, so we should
- // never make it any larger than that
- if (max < iter.getMaxRadius())
- iter.setMaxRadius(max);
- }
- Bool foundAny = false;
- static Int theIterFlag = 1; // nonzero, thanks
- ++theIterFlag;
- PartitionCell *thisCell;
- while ((thisCell = iter.nextNonEmpty()) != NULL)
- {
- CellAndObjectIntersection *nextCoi;
- for (CellAndObjectIntersection *thisCoi = thisCell->getFirstCoiInCell(); thisCoi; thisCoi = nextCoi)
- {
- nextCoi = thisCoi->getNextCoi();
-
- PartitionData *thisMod = thisCoi->getModule();
- Object *thisObj = thisMod->getObject();
- // never compare against ourself.
- if (thisObj == obj)
- continue;
- if (thisMod->friend_getDoneFlag() == theIterFlag)
- continue;
- thisMod->friend_setDoneFlag(theIterFlag);
-
- // hmm, ok, calc the distance.
- Real thisDistSqr;
- Coord3D distVec;
- if (!(*distProc)(objPos, objToUse, thisObj->getPosition(), thisObj, &thisDistSqr, &distVec, closestDistSqr))
- continue;
- // check the filters now
- if (!filtersAllow(filters, thisObj))
- continue;
- // ok, guess this is a winner!
- if (iterArg)
- {
- iterArg->insert(thisObj, thisDistSqr);
- }
- else
- {
- closestObj = thisObj;
- closestDistSqr = thisDistSqr;
- closestVec = distVec;
- if (!foundAny)
- {
- // if not adding to iterArg, we want to stop once we have the closest object.
- // since all objects in this radius (and the next radius, due to slop) might
- // be slightly closer, we still have to check all of them. so set the termination
- // radius to be our-current-radius-plus-1. (if we ARE adding to the iterArg, we skip
- // this, cuz we want to go all the way out to the original max we specified as an arg.)
- iter.setMaxRadius(iter.getCurCellRadius() + 2);
- }
- foundAny = true;
- }
- }
- }
-
- #endif // not FASTER_GCO
- if (closestVecArg)
- {
- *closestVecArg = closestVec;
- }
- if (closestDistArg)
- {
- *closestDistArg = (Real)sqrtf(closestDistSqr);
- }
- #ifdef _DEBUG
- --theEntrancyCount;
- #endif
- #ifdef DUMP_PERF_STATS
- Int64 endTime64;
- GetPrecisionTimer(&endTime64);
- Int64 delta = (endTime64 - startTime64);
- s_timeInClosestObjects += delta;
- s_timeInClosestObjectsThisFrame += delta;
- #endif
- return closestObj; // might be null...
- }
- //-----------------------------------------------------------------------------
- Object *PartitionManager::getClosestObject(
- const Object *obj,
- Real maxDist,
- DistanceCalculationType dc,
- PartitionFilter **filters,
- Real *closestDist,
- Coord3D *closestDistVec
- )
- {
- return getClosestObjects(obj, NULL, maxDist, dc, filters, NULL, closestDist, closestDistVec);
- }
- //-----------------------------------------------------------------------------
- Object *PartitionManager::getClosestObject(
- const Coord3D *pos,
- Real maxDist,
- DistanceCalculationType dc,
- PartitionFilter **filters,
- Real *closestDist,
- Coord3D *closestDistVec
- )
- {
- return getClosestObjects(NULL, pos, maxDist, dc, filters, NULL, closestDist, closestDistVec);
- }
- //-----------------------------------------------------------------------------
- void PartitionManager::getVectorTo(const Object *obj, const Object *otherObj, DistanceCalculationType dc, Coord3D& vec)
- {
- DistCalcProc distProc = theDistCalcProcs[dc];
- Real distSqr;
- (*distProc)(obj->getPosition(), obj, otherObj->getPosition(), otherObj, distSqr, vec, HUGE_DIST_SQR);
- }
- //-----------------------------------------------------------------------------
- void PartitionManager::getVectorTo(const Object *obj, const Coord3D *pos, DistanceCalculationType dc, Coord3D& vec)
- {
- DistCalcProc distProc = theDistCalcProcs[dc];
- Real distSqr;
- (*distProc)(obj->getPosition(), obj, pos, NULL, distSqr, vec, HUGE_DIST_SQR);
- }
- //-----------------------------------------------------------------------------
- Real PartitionManager::getDistanceSquared(const Object *obj, const Object *otherObj, DistanceCalculationType dc, Coord3D *vec)
- {
- DistCalcProc distProc = theDistCalcProcs[dc];
- Real thisDistSqr;
- Coord3D thisVec;
- (*distProc)(obj->getPosition(), obj, otherObj->getPosition(), otherObj, thisDistSqr, thisVec, HUGE_DIST_SQR);
- if (vec)
- *vec = thisVec;
- return thisDistSqr;
- }
- //-----------------------------------------------------------------------------
- Real PartitionManager::getDistanceSquared(const Object *obj, const Coord3D *pos, DistanceCalculationType dc, Coord3D *vec)
- {
- DistCalcProc distProc = theDistCalcProcs[dc];
- Real thisDistSqr;
- Coord3D thisVec;
- (*distProc)(obj->getPosition(), obj, pos, NULL, thisDistSqr, thisVec, HUGE_DIST_SQR);
- if (vec)
- *vec = thisVec;
- return thisDistSqr;
- }
- //-----------------------------------------------------------------------------
- // Gets the distance if obj were at goalPos. Used to calculate attack position paths.
- Real PartitionManager::getGoalDistanceSquared(const Object *obj, const Coord3D *goalPos, const Object *otherObj, DistanceCalculationType dc, Coord3D *vec)
- {
- DistCalcProc distProc = theDistCalcProcs[dc];
- Real thisDistSqr;
- Coord3D thisVec;
- (*distProc)(goalPos, obj, otherObj->getPosition(), otherObj, thisDistSqr, thisVec, HUGE_DIST_SQR);
- if (vec)
- *vec = thisVec;
- return thisDistSqr;
- }
- //-----------------------------------------------------------------------------
- // Gets the distance if obj were at goalPos. Used to calculate attack position paths.
- Real PartitionManager::getGoalDistanceSquared(const Object *obj, const Coord3D *goalPos, const Coord3D *otherPos, DistanceCalculationType dc, Coord3D *vec)
- {
- DistCalcProc distProc = theDistCalcProcs[dc];
- Real thisDistSqr;
- Coord3D thisVec;
- (*distProc)(goalPos, obj, otherPos, NULL, thisDistSqr, thisVec, HUGE_DIST_SQR);
- if (vec)
- *vec = thisVec;
- return thisDistSqr;
- }
- //-----------------------------------------------------------------------------
- Real PartitionManager::getRelativeAngle2D( const Object *obj, const Object *otherObj )
- {
- return getRelativeAngle2D(obj, otherObj->getPosition());
- }
- //-----------------------------------------------------------------------------
- Real PartitionManager::getRelativeAngle2D( const Object *obj, const Coord3D *pos )
- {
- Coord3D v;
-
- // compute vector to given position
- Coord3D objPos = *obj->getPosition();
- v.x = pos->x - objPos.x;
- v.y = pos->y - objPos.y;
- v.z = 0.0f;
- Real dist = (Real)sqrtf(sqr(v.x) + sqr(v.y));
- // normalize
- if (dist == 0.0f)
- return 0.0f;
- const Coord3D *dir = obj->getUnitDirectionVector2D();
- Real distInv = 1.0f / dist;
- v.x *= distInv;
- v.y *= distInv;
- v.z *= distInv;
- // dot of two unit vectors is cos of angle
- Real c = dir->x*v.x + dir->y*v.y; // + dir->z*v.z;
- // bound it in case of numerical error
- if (c < -1.0)
- c = -1.0;
- else if (c > 1.0)
- c = 1.0;
- Real value = (Real)ACos( c );
- // Determine sign by checking Z component of dir cross v
- // Note this is assumes 2D, and is identical to dotting the perpendicular of v with dir
- Real perpZ = dir->x * v.y - dir->y * v.x;
- if (perpZ < 0.0f)
- value = -value;
- // note: to make this 3D, 'dir' and 'v' can be normalized and dotted just as they are
- // to test sign, compute N = dir X v, then P = N x dir, then S = P . v, where sign of
- // S is sign of angle - MSB
- return value;
- }
- //-----------------------------------------------------------------------------
- SimpleObjectIterator *PartitionManager::iterateObjectsInRange(
- const Object *obj,
- Real maxDist,
- DistanceCalculationType dc,
- PartitionFilter **filters,
- IterOrderType order
- )
- {
- MemoryPoolObjectHolder iterHolder;
- SimpleObjectIterator *iter = newInstance(SimpleObjectIterator);
- iterHolder.hold(iter);
- getClosestObjects(obj, NULL, maxDist, dc, filters, iter, NULL, NULL);
- iter->sort(order);
- iterHolder.release();
- return iter;
- }
- //-----------------------------------------------------------------------------
- SimpleObjectIterator *PartitionManager::iterateObjectsInRange(
- const Coord3D *pos,
- Real maxDist,
- DistanceCalculationType dc,
- PartitionFilter **filters,
- IterOrderType order
- )
- {
- MemoryPoolObjectHolder iterHolder;
- SimpleObjectIterator *iter = newInstance(SimpleObjectIterator);
- iterHolder.hold(iter);
- getClosestObjects(NULL, pos, maxDist, dc, filters, iter, NULL, NULL);
- iter->sort(order);
- iterHolder.release();
- return iter;
- }
- //-----------------------------------------------------------------------------
- SimpleObjectIterator* PartitionManager::iteratePotentialCollisions(
- const Coord3D* pos,
- const GeometryInfo& geom,
- Real angle,
- Bool use2D
- )
- {
- Real maxDist = geom.getBoundingSphereRadius();
- maxDist *= 1.1f; // just a little slop
- MemoryPoolObjectHolder iterHolder;
- SimpleObjectIterator *iter = newInstance(SimpleObjectIterator);
- iterHolder.hold(iter);
- PartitionFilterWouldCollide filter(*pos, geom, angle, true);
- PartitionFilter *filters[] = { &filter, NULL };
- getClosestObjects(NULL, pos, maxDist, use2D ? FROM_BOUNDINGSPHERE_2D : FROM_BOUNDINGSPHERE_3D, filters, iter, NULL, NULL);
- iterHolder.release();
- return iter;
- }
- //-----------------------------------------------------------------------------
- Bool PartitionManager::isColliding( const Object *a, const Object *b ) const
- {
- //Make sure we have objects
- if( !a || !b )
- {
- return false;
- }
- //Get both object's partition data
- const PartitionData* ad = a->friend_getConstPartitionData();
- const PartitionData* bd = b->friend_getConstPartitionData();
- //Make sure we got it
- if( !ad || !bd )
- {
- return false;
- }
- //See if the partition data collides.
- return ad->friend_collidesWith( bd, NULL );
- }
- //-----------------------------------------------------------------------------
- SimpleObjectIterator *PartitionManager::iterateAllObjects(PartitionFilter **filters)
- {
- MemoryPoolObjectHolder iterHolder;
- SimpleObjectIterator *iter = newInstance(SimpleObjectIterator);
- iterHolder.hold(iter);
- // no distance constraints; we're gonna have to process 'em all anyway,
- // so go thru in the fastest way we know how.
- PartitionData *mod, *nextMod;
- for( mod = m_moduleList; mod; mod = nextMod )
- {
- nextMod = mod->getNext();
- Object *obj = mod->getObject();
- if (obj && filtersAllow(filters, obj))
- {
- iter->insert( obj );
- }
- }
- iterHolder.release();
- return iter;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- Bool PartitionManager::tryPosition( const Coord3D *center,
- Real dist,
- Real angle,
- const FindPositionOptions *options,
- Coord3D *result )
- {
- // compute the spot on the terrain we've picked
- Coord3D pos;
- pos.x = dist * Cos( angle ) + center->x;
- pos.y = dist * Sin( angle ) + center->y;
- PathfindLayerEnum layer = LAYER_GROUND;
- if ((options->flags & FPF_USE_HIGHEST_LAYER) != 0)
- {
- pos.z = 99999.0f;
- layer = TheTerrainLogic->getHighestLayerForDestination(&pos);
- pos.z = TheTerrainLogic->getLayerHeight(pos.x, pos.y, layer);
- // ensure we are slightly above the bridge, to account for fudge & sloppy art
- if (layer != LAYER_GROUND)
- pos.z += 1.0f;
- }
- else
- {
- pos.z = TheTerrainLogic->getGroundHeight( pos.x, pos.y );
- }
- if (fabs(pos.z - center->z) > options->maxZDelta)
- return FALSE;
- //
- // we don't usually find positions on cliffs.
- // someday, add bit options for this, like for water.
- //
- if (TheTerrainLogic->isCliffCell(pos.x, pos.y) && layer == LAYER_GROUND)
- return FALSE;
- //
- // srj sez:
- // we don't usually find positions on impassable areas.
- // someday, add bit options for this, like for water.
- //
- {
- Int cellX = REAL_TO_INT_FLOOR(pos.x / PATHFIND_CELL_SIZE);
- Int cellY = REAL_TO_INT_FLOOR(pos.y / PATHFIND_CELL_SIZE);
- PathfindCell* cell = TheAI->pathfinder()->getCell(layer, cellX, cellY);
- if (!cell || cell->getType() == PathfindCell::CELL_IMPASSABLE)
- {
- return FALSE;
- }
- }
- //
- // we don't usually find positions in the water unless we explicitly say that's OK,
- // or if the option is set we can only pick position underwater
- //
- if( BitTest( options->flags, FPF_IGNORE_WATER ) == FALSE )
- {
- Bool isUnderwater = TheTerrainLogic->isUnderwater( pos.x, pos.y );
- //
- // if we want water spots only and this is underwater it's no good, otherwise we want
- // the default behavior where underwater spots are invalid
- //
- if( BitTest( options->flags, FPF_WATER_ONLY ) && (isUnderwater == FALSE || layer != LAYER_GROUND) )
- return FALSE;
- else if( isUnderwater == TRUE && layer == LAYER_GROUND )
- return FALSE;
- } // end if
- // object checks
- if( BitTest( options->flags, FPF_IGNORE_ALL_OBJECTS ) == FALSE )
- {
- //
- // iterate the potential collisions at this location using a
- // very small sphere geometry around the point
- //
- GeometryInfo geometry( GEOMETRY_SPHERE, TRUE, 5.0f, 5.0f, 5.0f );
- ObjectIterator *iter = ThePartitionManager->iteratePotentialCollisions( &pos, geometry, angle, true );
- MemoryPoolObjectHolder hold( iter );
- // Bool overlap = FALSE;
- for( Object *them = iter->first(); them; them = iter->next() )
- {
- // if this is the ignore object, ignore it
- if( them == options->ignoreObject )
- continue; // continue in the object iterator for loop
- // relationship checks
- if( options->relationshipObject )
- {
- // if this is an ally/neutral unit and we ignore those, do so
- if( BitTest( options->flags, FPF_IGNORE_ALLY_OR_NEUTRAL_UNITS ) == TRUE &&
- options->relationshipObject->getRelationship( them ) != ENEMIES &&
- (them->isKindOf( KINDOF_INFANTRY ) || them->isKindOf( KINDOF_VEHICLE )) )
- continue;
- // if this is an ally/neutral structure and we ignore those, do so
- if( BitTest( options->flags, FPF_IGNORE_ALLY_OR_NEUTRAL_STRUCTURES ) == TRUE &&
- options->relationshipObject->getRelationship( them ) != ENEMIES &&
- them->isKindOf( KINDOF_STRUCTURE ) )
- continue;
- // if this is an enemy unit and we ignore those, do so
- if( BitTest( options->flags, FPF_IGNORE_ENEMY_UNITS ) == TRUE &&
- options->relationshipObject->getRelationship( them ) == ENEMIES &&
- (them->isKindOf( KINDOF_INFANTRY ) || them->isKindOf( KINDOF_VEHICLE )) )
- continue;
- // if this is an enemy structure and we ignore those, do so
- if( BitTest( options->flags, FPF_IGNORE_ENEMY_STRUCTURES ) == TRUE &&
- options->relationshipObject->getRelationship( them ) == ENEMIES &&
- them->isKindOf( KINDOF_STRUCTURE ) )
- continue;
- } // end if, relationship checks
- //
- // if we have a source that must path to the destination we will ignore that too
- // NOTE: If needed, we can reorganize the meaning of this method to be
- // more explicit on when to pay attention to any 'source' objects and when not too
- // but for now we're keeping the API very simple for the kinds of queries
- // that we're likely to do for an RTS (CBD)
- //
- if( them == options->sourceToPathToDest )
- continue; // continue in the object iterator for loop
- // oops, we have overlapped something
- return FALSE;
- } // end for, them
- } // end if
- //
- // finally ... if sourceToPathDest is valid ... the source object must be able to
- // find a path the the position we have selected. We want to do this last because
- // a pathfind can sometimes be an relatively expensive operation
- //
- if( options->sourceToPathToDest )
- {
- const AIUpdateInterface *ai = options->sourceToPathToDest->getAIUpdateInterface();
- // check for path existence
- if( ai && TheAI->pathfinder()->quickDoesPathExist( ai->getLocomotorSet(),
- options->sourceToPathToDest->getPosition(),
- &pos ) == FALSE )
- return FALSE;
- } // end if
- // save result and return TRUE for position found
- *result = pos;
- return TRUE;
- } // end tryPosition
- //
- // the following determines how fast we expand our concentric ring search for the
- // find position methods
- //
- static Real ringSpacing = 5.0f;
- //-------------------------------------------------------------------------------------------------
- /** This method will attempt to find a legal postion from the center position specified,
- * at least minRadis away from it, but no more than maxRadius away.
- *
- * Return TRUE if position is found and that position is returned in 'result'
- * return FALSE no legal position exists or invalid params
- */
- //-------------------------------------------------------------------------------------------------
- Bool PartitionManager::findPositionAround( const Coord3D *center,
- const FindPositionOptions *options,
- Coord3D *result )
- {
- // sanity
- if( center == NULL || result == NULL || options == NULL )
- return FALSE;
- Region3D extent;
- TheTerrainLogic->getMaximumPathfindExtent(&extent);
- // If the goal is off the map, it is a scripted setup, so just
- // use the center.
- if (!extent.isInRegionNoZ(center)) {
- *result = *center;
- return true;
- }
- // sanity, FPF_IGNORE_WATER and FPF_WATER_ONLY are mutually exclusive
- DEBUG_ASSERTCRASH( !(BitTest( options->flags, FPF_IGNORE_WATER ) == TRUE &&
- BitTest( options->flags, FPF_WATER_ONLY ) == TRUE),
- ("PartitionManager::findPositionAround - The options FPF_WATER_ONLY and FPF_IGNORE_WATER are mutually exclusive. You cannot use them together\n") );
- // pick a random angle from the center location to start at
- Real startAngle;
- if( options->startAngle == RANDOM_START_ANGLE )
- startAngle = GameLogicRandomValueReal( 0.0f, TWO_PI );
- else
- startAngle = options->startAngle;
- // start the search at the most inner ring (minRadius) and expand to outer most ring (maxRadius)
- for( Real dist = options->minRadius; dist <= options->maxRadius; dist += ringSpacing )
- {
- //
- // given the spacing that we've been using for the angles on the 'idea' inner circle,
- // we will need more points on larger circles to cover more ground
- //
- Real angleSpacing;
- if( dist == options->minRadius )
- angleSpacing = TWO_PI;
- else
- angleSpacing = (ringSpacing / (dist + 1.0f)) * (TWO_PI / 6.0f); // larger float = more samples
- //
- // on this "ring", try all the angles available to us in a circle ... we'll start at
- // the 'startAngle', then try 'angleSpacing' to the left, then 'angleSpacing' to the
- // right, then '2 angleSpacings' to the left etc., ping ponging around the start angle
- // until we've done them all the way around the circle
- //
- // how many samples will we test
- Int samples = REAL_TO_INT_CEIL( (TWO_PI / angleSpacing) / 2.0f);
- for( Int i = 0; i < samples; ++i )
- {
- // try one "side"
- if( tryPosition( center, dist, startAngle + angleSpacing * i, options, result ) == TRUE )
- return TRUE;
- //
- // try the other "side" ... note for the first point at minRadius this position is
- // the same so we won't test it again in that case
- //
- if( i != 0 )
- if( tryPosition( center, dist, startAngle - angleSpacing * i, options, result ) == TRUE )
- return TRUE;
- } // end if
- } // end for, dist
- // no position was able to be found
- return FALSE;
- } // end findPositionAround
- //-----------------------------------------------------------------------------
- // This is the main accessor of the shroud system. At this level, allies are taken
- // into consideration as specified by the caller. Look/Unlook are the ones sending Ally info, as that
- // is in Object where Allies make sense. AddLooker literally just adds a looker for the player you specify.
- // This way, Full map reveals and Observer mode active look will not carry over to all
- // allies. They'll use the RevealWholeDamnMap series, which call addLooker directly.
- void PartitionManager::doShroudReveal(Real centerX, Real centerY, Real radius, PlayerMaskType playerMask)
- {
- Int cellCenterX, cellCenterY;
- ThePartitionManager->worldToCell(centerX, centerY, &cellCenterX, &cellCenterY);
- Int cellRadius = ThePartitionManager->worldToCellDist(radius);
- if (cellRadius < 1)
- cellRadius = 1;
- DiscreteCircle circle(cellCenterX, cellCenterY, cellRadius);
- for( Int currentIndex = ThePlayerList->getPlayerCount() - 1; currentIndex >=0; currentIndex-- )
- {
- // Object's Look is the one who knows about allies. Anyone can pask a player mask to me and all
- // of those players will have an active looker applied to a bunch of cells
- const Player *currentPlayer = ThePlayerList->getNthPlayer( currentIndex );
- if( BitTest( playerMask, currentPlayer->getPlayerMask() ) )
- {
- circle.drawCircle(hLineAddLooker, (void*)currentIndex);
- }
- }
- }
-
- //-----------------------------------------------------------------------------
- void PartitionManager::processPendingUndoShroudRevealQueue( Bool considerTimestamp )
- {
- //Keep going until the front one is in the future. UndoShroudReveal on each one you process.
- //Again, you know these are in order, because we control the adding of the Constant in-the-queue time.
- UnsignedInt compareTime;
- if(considerTimestamp)
- compareTime = TheGameLogic->getFrame();
- else
- compareTime = UINT_MAX;
- while( !m_pendingUndoShroudReveals.empty() && (m_pendingUndoShroudReveals.front()->m_data < compareTime) )
- {
- SightingInfo *thisInfo = m_pendingUndoShroudReveals.front();
- undoShroudReveal( thisInfo->m_where.x, thisInfo->m_where.y, thisInfo->m_howFar, thisInfo->m_forWhom );
- thisInfo->deleteInstance();
- m_pendingUndoShroudReveals.pop();
- }
- }
- //-----------------------------------------------------------------------------
- void PartitionManager::processEntirePendingUndoShroudRevealQueue()
- {
- // Something major is about to happen, so we need to rush through our queue and get everything in order.
- // Examples are map resize, full map undo-reveal, stuff like that.
- // Don't like duplicating code, so route to original function.
- processPendingUndoShroudRevealQueue(FALSE);
- }
- //-----------------------------------------------------------------------------
- void PartitionManager::resetPendingUndoShroudRevealQueue()
- {
- while( !m_pendingUndoShroudReveals.empty() )
- {
- SightingInfo *thisInfo = m_pendingUndoShroudReveals.front();
- thisInfo->deleteInstance();
- m_pendingUndoShroudReveals.pop();
- }
- }
- //-----------------------------------------------------------------------------
- void PartitionManager::undoShroudReveal(Real centerX, Real centerY, Real radius, PlayerMaskType playerMask)
- {
- Int cellCenterX, cellCenterY;
- ThePartitionManager->worldToCell(centerX, centerY, &cellCenterX, &cellCenterY);
- Int cellRadius = ThePartitionManager->worldToCellDist(radius);
- if (cellRadius < 1)
- cellRadius = 1;
- DiscreteCircle circle(cellCenterX, cellCenterY, cellRadius);
- for( Int currentIndex = ThePlayerList->getPlayerCount() - 1; currentIndex >=0; currentIndex-- )
- {
- const Player *currentPlayer = ThePlayerList->getNthPlayer( currentIndex );
- if( BitTest( playerMask, currentPlayer->getPlayerMask() ) )
- {
- circle.drawCircle(hLineRemoveLooker, (void*)currentIndex);
- }
- }
- }
-
- //-----------------------------------------------------------------------------
- void PartitionManager::queueUndoShroudReveal(Real centerX, Real centerY, Real radius, PlayerMaskType playerMask)
- {
- UnsignedInt now = TheGameLogic->getFrame();
- SightingInfo *newInfo = newInstance(SightingInfo);
- newInfo->m_where.x = centerX;
- newInfo->m_where.y = centerY;
- newInfo->m_howFar = radius;
- newInfo->m_forWhom = playerMask;
- newInfo->m_data = now + TheGlobalData->m_unlookPersistDuration;
- m_pendingUndoShroudReveals.push(newInfo);
- }
-
- //-----------------------------------------------------------------------------
- void PartitionManager::doShroudCover(Real centerX, Real centerY, Real radius, PlayerMaskType playerMask)
- {
- Int cellCenterX, cellCenterY;
- ThePartitionManager->worldToCell(centerX, centerY, &cellCenterX, &cellCenterY);
- Int cellRadius = ThePartitionManager->worldToCellDist(radius);
- if (cellRadius < 1)
- cellRadius = 1;
- DiscreteCircle circle(cellCenterX, cellCenterY, cellRadius);
- for( Int currentIndex = ThePlayerList->getPlayerCount() - 1; currentIndex >=0; currentIndex-- )
- {
- // Object's Shroud is the one who knows about allies. Anyone can pask a player mask to me and all
- // of those players will have an active shrouder applied to a bunch of cells
- const Player *currentPlayer = ThePlayerList->getNthPlayer( currentIndex );
- if( BitTest( playerMask, currentPlayer->getPlayerMask() ) )
- {
- circle.drawCircle(hLineAddShrouder, (void*)currentIndex);
- }
- }
- }
- //-----------------------------------------------------------------------------
- void PartitionManager::undoShroudCover(Real centerX, Real centerY, Real radius, PlayerMaskType playerMask)
- {
- Int cellCenterX, cellCenterY;
- ThePartitionManager->worldToCell(centerX, centerY, &cellCenterX, &cellCenterY);
- Int cellRadius = ThePartitionManager->worldToCellDist(radius);
- if (cellRadius < 1)
- cellRadius = 1;
- DiscreteCircle circle(cellCenterX, cellCenterY, cellRadius);
- for( Int currentIndex = ThePlayerList->getPlayerCount() - 1; currentIndex >=0; currentIndex-- )
- {
- const Player *currentPlayer = ThePlayerList->getNthPlayer( currentIndex );
- if( BitTest( playerMask, currentPlayer->getPlayerMask() ) )
- {
- circle.drawCircle(hLineRemoveShrouder, (void*)currentIndex);
- }
- }
- }
- //-----------------------------------------------------------------------------
- void PartitionManager::doThreatAffect( Real centerX, Real centerY, Real radius, UnsignedInt threatVal, PlayerMaskType playerMask)
- {
- Int cellCenterX, cellCenterY;
- ThePartitionManager->worldToCell(centerX, centerY, &cellCenterX, &cellCenterY);
- Real fCellCenterX = INT_TO_REAL(cellCenterX);
- Real fCellCenterY = INT_TO_REAL(cellCenterY);
- Int cellRadius = ThePartitionManager->worldToCellDist(radius);
- if (cellRadius < 1)
- cellRadius = 1;
- Real fCellRadius = INT_TO_REAL(cellRadius + 1);
- DiscreteCircle circle(cellCenterX, cellCenterY, cellRadius);
- ThreatValueParms parms;
- parms.radius = fCellRadius;
- parms.threatOrValue = threatVal;
- parms.xCenter = fCellCenterX;
- parms.yCenter = fCellCenterY;
- for( Int currentIndex = ThePlayerList->getPlayerCount() - 1; currentIndex >=0; currentIndex-- )
- {
- const Player *currentPlayer = ThePlayerList->getNthPlayer( currentIndex );
- if( BitTest( playerMask, currentPlayer->getPlayerMask() ) )
- {
- parms.playerIndex = currentIndex;
- circle.drawCircle(hLineAddThreat, &parms);
- }
- }
- }
- //-----------------------------------------------------------------------------
- void PartitionManager::undoThreatAffect( Real centerX, Real centerY, Real radius, UnsignedInt threatVal, PlayerMaskType playerMask)
- {
- Int cellCenterX, cellCenterY;
- ThePartitionManager->worldToCell(centerX, centerY, &cellCenterX, &cellCenterY);
- Real fCellCenterX = INT_TO_REAL(cellCenterX);
- Real fCellCenterY = INT_TO_REAL(cellCenterY);
- Int cellRadius = ThePartitionManager->worldToCellDist(radius);
- if (cellRadius < 1)
- cellRadius = 1;
- Real fCellRadius = INT_TO_REAL(cellRadius + 1);
- DiscreteCircle circle(cellCenterX, cellCenterY, cellRadius);
- ThreatValueParms parms;
- parms.radius = fCellRadius;
- parms.threatOrValue = threatVal;
- parms.xCenter = fCellCenterX;
- parms.yCenter = fCellCenterY;
- for( Int currentIndex = ThePlayerList->getPlayerCount() - 1; currentIndex >=0; currentIndex-- )
- {
- const Player *currentPlayer = ThePlayerList->getNthPlayer( currentIndex );
- if( BitTest( playerMask, currentPlayer->getPlayerMask() ) )
- {
- parms.playerIndex = currentIndex;
- circle.drawCircle(hLineRemoveThreat, &parms);
- }
- }
- }
- //-----------------------------------------------------------------------------
- void PartitionManager::doValueAffect( Real centerX, Real centerY, Real radius, UnsignedInt valueVal, PlayerMaskType playerMask)
- {
- Int cellCenterX, cellCenterY;
- ThePartitionManager->worldToCell(centerX, centerY, &cellCenterX, &cellCenterY);
- Real fCellCenterX = INT_TO_REAL(cellCenterX);
- Real fCellCenterY = INT_TO_REAL(cellCenterY);
- Int cellRadius = ThePartitionManager->worldToCellDist(radius);
- if (cellRadius < 1)
- cellRadius = 1;
- Real fCellRadius = INT_TO_REAL(cellRadius + 1);
- DiscreteCircle circle(cellCenterX, cellCenterY, cellRadius);
- ThreatValueParms parms;
- parms.radius = fCellRadius;
- parms.threatOrValue = valueVal;
- parms.xCenter = fCellCenterX;
- parms.yCenter = fCellCenterY;
- for( Int currentIndex = ThePlayerList->getPlayerCount() - 1; currentIndex >=0; currentIndex-- )
- {
- const Player *currentPlayer = ThePlayerList->getNthPlayer( currentIndex );
- if( BitTest( playerMask, currentPlayer->getPlayerMask() ) )
- {
- parms.playerIndex = currentIndex;
- circle.drawCircle(hLineAddValue, &parms);
- }
- }
- }
- //-----------------------------------------------------------------------------
- void PartitionManager::undoValueAffect( Real centerX, Real centerY, Real radius, UnsignedInt valueVal, PlayerMaskType playerMask)
- {
- Int cellCenterX, cellCenterY;
- ThePartitionManager->worldToCell(centerX, centerY, &cellCenterX, &cellCenterY);
- Real fCellCenterX = INT_TO_REAL(cellCenterX);
- Real fCellCenterY = INT_TO_REAL(cellCenterY);
- Int cellRadius = ThePartitionManager->worldToCellDist(radius);
- if (cellRadius < 1)
- cellRadius = 1;
- Real fCellRadius = INT_TO_REAL(cellRadius + 1);
- DiscreteCircle circle(cellCenterX, cellCenterY, cellRadius);
- ThreatValueParms parms;
- parms.radius = fCellRadius;
- parms.threatOrValue = valueVal;
- parms.xCenter = fCellCenterX;
- parms.yCenter = fCellCenterY;
-
- for( Int currentIndex = ThePlayerList->getPlayerCount() - 1; currentIndex >=0; currentIndex-- )
- {
- const Player *currentPlayer = ThePlayerList->getNthPlayer( currentIndex );
- if( BitTest( playerMask, currentPlayer->getPlayerMask() ) )
- {
- parms.playerIndex = currentIndex;
- circle.drawCircle(hLineRemoveValue, &parms);
- }
- }
- }
- //-----------------------------------------------------------------------------
- void PartitionManager::getCellCenterPos(Int x, Int y, Real& xx, Real& yy)
- {
- DEBUG_ASSERTCRASH(x >= 0 && y >= 0, ("hmm, invalid cell"));
- Real half = m_cellSize*0.5f;
- xx = m_worldExtents.lo.x + (x * m_cellSize) + half;
- yy = m_worldExtents.lo.y + (y * m_cellSize) + half;
- }
- //-----------------------------------------------------------------------------
- #ifdef PM_CACHE_TERRAIN_HEIGHT
- struct TerrainExtremeData
- {
- Real* minZ;
- Real* maxZ;
- Coord2D* minZPos;
- Coord2D* maxZPos;
- Bool isValid;
- };
- static Int checkTerrainExtreme(PartitionCell* cell, void* userData)
- {
- TerrainExtremeData* data = (TerrainExtremeData*)userData;
-
- data->isValid = true;
- Real tmp;
- if (data->minZ)
- {
- tmp = cell->getLoTerrain();
- if (tmp < *data->minZ )
- {
- *data->minZ = tmp;
- if (data->minZPos)
- {
- cell->getCellCenterPos(data->minZPos->x, data->minZPos->y);
- }
- }
- }
- if (data->maxZ)
- {
- tmp = cell->getHiTerrain();
- if (tmp > *data->maxZ )
- {
- *data->maxZ = tmp;
- if (data->maxZPos)
- {
- cell->getCellCenterPos(data->maxZPos->x, data->maxZPos->y);
- }
- }
- }
- return 0; // zero to continue
- }
- #endif
- //-----------------------------------------------------------------------------
- #ifdef PM_CACHE_TERRAIN_HEIGHT
- Bool PartitionManager::estimateTerrainExtremesAlongLine(const Coord3D& pos, const Coord3D& posOther, Real* minZ, Real* maxZ, Coord2D* minZPos, Coord2D* maxZPos)
- {
- TerrainExtremeData data;
- data.minZ = minZ;
- data.maxZ = maxZ;
- data.minZPos = minZPos;
- data.maxZPos = maxZPos;
- data.isValid = false;
- if (minZ) *minZ = HUGE_DIST;
- if (maxZ) *maxZ = -HUGE_DIST;
- iterateCellsAlongLine(pos, posOther, checkTerrainExtreme, &data);
- return data.isValid;
- }
- #endif
- //-----------------------------------------------------------------------------
- // Uses Bresenham line algorithm from www.gamedev.net.
- Int PartitionManager::iterateCellsAlongLine(const Coord3D& pos, const Coord3D& posOther, CellAlongLineProc proc, void* userData)
- {
- ICoord2D start, end, delta;
- Int x, y;
- Int xinc1, xinc2;
- Int yinc1, yinc2;
- Int den, num, numadd;
- Int numpixels;
- worldToCell(pos.x, pos.y, &start.x, &start.y);
- worldToCell(posOther.x, posOther.y, &end.x, &end.y);
- delta.x = abs(end.x - start.x); // The difference between the x's
- delta.y = abs(end.y - start.y); // The difference between the y's
- x = start.x; // Start x off at the first pixel
- y = start.y; // Start y off at the first pixel
- if (end.x >= start.x) // The x-values are increasing
- {
- xinc1 = 1;
- xinc2 = 1;
- }
- else // The x-values are decreasing
- {
- xinc1 = -1;
- xinc2 = -1;
- }
- if (end.y >= start.y) // The y-values are increasing
- {
- yinc1 = 1;
- yinc2 = 1;
- }
- else // The y-values are decreasing
- {
- yinc1 = -1;
- yinc2 = -1;
- }
- Bool checkY = true;
- if (delta.x >= delta.y) // There is at least one x-value for every y-value
- {
- xinc1 = 0; // Don't change the x when numerator >= denominator
- yinc2 = 0; // Don't change the y for every iteration
- den = delta.x;
- num = delta.x / 2;
- numadd = delta.y;
- numpixels = delta.x; // There are more x-values than y-values
- }
- else // There is at least one y-value for every x-value
- {
- checkY = false;
- xinc2 = 0; // Don't change the x for every iteration
- yinc1 = 0; // Don't change the y when numerator >= denominator
- den = delta.y;
- num = delta.y / 2;
- numadd = delta.x;
- numpixels = delta.y; // There are more y-values than x-values
- }
- for (Int curpixel = 0; curpixel <= numpixels; curpixel++)
- {
- PartitionCell* cell = ThePartitionManager->getCellAt(x, y); // might be null if off the edge
- DEBUG_ASSERTCRASH(cell != NULL, ("off the map"));
- if (cell)
- {
- Int ret = (*proc)(cell, userData);
- if (ret != 0)
- return ret; // bail early
- }
- num += numadd; // Increase the numerator by the top of the fraction
- if (num >= den) // Check if numerator >= denominator
- {
- num -= den; // Calculate the new numerator value
- x += xinc1; // Change the x as appropriate
- y += yinc1; // Change the y as appropriate
- }
- x += xinc2; // Change the x as appropriate
- y += yinc2; // Change the y as appropriate
- }
-
- return 0;
- }
- //-----------------------------------------------------------------------------
- Int PartitionManager::iterateCellsBreadthFirst(const Coord3D *pos, CellBreadthFirstProc proc, void *userData)
- {
- // starting at pos, iterate the cells in the following manner:
- // left, up, right, down
- // using Breadth-first search.
- // Call proc on each cell, and if it returns non-zero, then return the cell index
- // -1 means error, but we should add a define later for this.
- Int cellX, cellY;
- ThePartitionManager->worldToCell(pos->x, pos->y, &cellX, &cellY);
-
- // Note, bool. not Bool, cause bool will cause this to be a bitfield.
- std::vector<bool> bitField;
- Int cellCount = m_cellCountX * m_cellCountY;
- bitField.resize(cellCount);
- // 0 means not done, 1 means done.
- for (Int i = 0; i < cellCount; ++i) {
- bitField[i] = false;
- }
- std::queue<PartitionCell *> cellQ;
- // mark the starting cell done
- bitField[cellY * m_cellCountX + cellX] = true;
- cellQ.push(&m_cells[cellY * m_cellCountX + cellX]);
- Int curX = cellX;
- Int curY = cellY;
- while (!cellQ.empty()) {
- // first, add the neighbors
- // left
- if (curX - 1 >= 0) {
- if (!bitField[curY * m_cellCountX + curX - 1]) {
- bitField[curY * m_cellCountX + curX - 1] = true;
- cellQ.push(&m_cells[curY * m_cellCountX + curX - 1]);
- }
- }
- // top
- if (curY - 1 >= 0) {
- if (!bitField[(curY - 1) * m_cellCountX + curX]) {
- bitField[(curY - 1) * m_cellCountX + curX] = true;
- cellQ.push(&m_cells[(curY - 1) * m_cellCountX + curX]);
- }
- }
- // right
- if (curX + 1 < m_cellCountX) {
- if (!bitField[curY * m_cellCountX + curX + 1]) {
- bitField[curY * m_cellCountX + curX + 1] = true;
- cellQ.push(&m_cells[curY * m_cellCountX + curX + 1]);
- }
- }
- // bottom
- if (curY + 1 > 0) {
- if (!bitField[(curY + 1) * m_cellCountX + curX]) {
- bitField[(curY + 1) * m_cellCountX + curX] = true;
- cellQ.push(&m_cells[(curY + 1) * m_cellCountX + curX]);
- }
- }
- // now process the current top.
- PartitionCell *curCell = cellQ.front();
- cellQ.pop();
- curX = curCell->getCellX();
- curY = curCell->getCellY();
- if ((proc)(curCell, userData) != 0)
- return (curCell->getCellY() * m_cellCountX + curCell->getCellX());
- }
- return -1;
- }
- //-----------------------------------------------------------------------------
- static Real calcDist2D(Real x1, Real y1, Real x2, Real y2)
- {
- return sqrtf(sqr(x1-x2) + sqr(y1-y2));
- }
- //-----------------------------------------------------------------------------
- Bool PartitionManager::isClearLineOfSightTerrain(const Object* obj, const Coord3D& objPos, const Object* other, const Coord3D& otherPos)
- {
- Coord3D pos, posOther;
-
- if (obj)
- {
- pos = *obj->getPosition();
- // note that we want to measure from the top of the collision
- // shape, not the bottom! (most objects have eyes a lot closer
- // to their head than their feet. if we have really odd critters
- // with eye-feet, we'll need to change this assumption.)
- pos.z += obj->getGeometryInfo().getMaxHeightAbovePosition();
- }
- else
- {
- pos = objPos;
- }
- if (other)
- {
- posOther = *other->getPosition();
- // note that we want to measure from the top of the collision
- // shape, not the bottom! (most objects have eyes a lot closer
- // to their head than their feet. if we have really odd critters
- // with eye-feet, we'll need to change this assumption.)
- posOther.z += other->getGeometryInfo().getMaxHeightAbovePosition();
- }
- else
- {
- posOther = otherPos;
- }
- #ifdef NO_BAD_AND_INACCURATE
- /*
- This looks tempting, and in theory would be better than the below code; however, it
- is not (yet) accurate enough for our targeting needs. what we really need to do is add a Bresenham
- line iterator to the TerrainLogic itself, which would probably suffice. Until then, this code is
- left in as an example of what NOT to do. (srj)
- */
- Real maxZ;
- Coord2D maxZPos;
- Bool valid = estimateTerrainExtremesAlongLine(pos, posOther, NULL, &maxZ, NULL, &maxZPos);
- DEBUG_ASSERTCRASH(valid, ("this should never happen unless both positions are off-map"));
- if (!valid)
- return true;
- Real terrainAtHighPoint = maxZ;
- Real totalDist2D = calcDist2D(pos.x, pos.y, posOther.x, posOther.y);
- Real terrainDist2D = calcDist2D(pos.x, pos.y, maxZPos.x, maxZPos.y);
-
- const Real TINY_DIST = 0.01f;
- Real percent = (totalDist2D > TINY_DIST) ? (terrainDist2D / totalDist2D) : 0.0f;
- if (percent < 0.0f) percent = 0.0f;
- if (percent > 1.0f) percent = 1.0f;
- Real lineOfSightAtHighPoint = pos.z + (posOther.z - pos.z) * percent;
- // if terrainAtHighPoint > lineOfSightAtHighPoint, we can't see.
- // add a little fudge to account for slop.
- const Real LOS_FUDGE = 0.5f;
- if (terrainAtHighPoint > lineOfSightAtHighPoint + LOS_FUDGE)
- {
- //DEBUG_LOG(("isClearLineOfSightTerrain fails\n"));
- return false;
- }
- return true;
- #else
- return TheTerrainLogic->isClearLineOfSight(pos, posOther);
- #endif
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void PartitionManager::crc( Xfer *xfer )
- {
- for (Int i=0; i<m_totalCellCount; ++i)
- {
- m_cells[i].crc(xfer);
- }
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method
- * Version Info:
- * 1: Initial version
- * 2: m_pendingUndoShroudReveals stores Unlooks waiting to happen.
- */
- // ------------------------------------------------------------------------------------------------
- void PartitionManager::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 2;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- // cell size information
- Real cellSize = m_cellSize;
- xfer->xferReal( &cellSize );
- //
- // cell size should match what we have allocated in the partition manager, if it doesn't
- // we need to increment the version here and edit this code to be able to load
- // the old cell size if changed after we release
- //
- if( cellSize != m_cellSize )
- {
- DEBUG_CRASH(( "Partition cell size has changed, this save game file is invalid\n" ));
- throw SC_INVALID_DATA;
- } // end if
- // cell count
- Int totalCellCount = m_totalCellCount;
- xfer->xferInt( &totalCellCount );
-
- // total cell count better match what we have allocated for cells in the partition manager
- if( totalCellCount != m_totalCellCount )
- {
- DEBUG_CRASH(( "Partition total cell count mismatch %d, should be %d\n",
- totalCellCount, m_totalCellCount ));
- throw SC_INVALID_DATA;
- } // end if
- // xfer each cell information
- PartitionCell *cell;
- for( Int i = 0; i < totalCellCount; ++i )
- {
- // get this partition cell
- cell = &m_cells[ i ];
- // xfer the data for this cell
- xfer->xferSnapshot( cell );
- } // end for i
- // when loading tell the partition manager to rethink and refresh all shroud information
- if( xfer->getXferMode() == XFER_LOAD )
- {
- // tell partition manager to re-evaluate shroud things when next asked
- m_updatedSinceLastReset = FALSE;
- // refresh the shroud for the local player which will update the radar and everything
- refreshShroudForLocalPlayer();
- } // end if
- if( version >= 2 )
- {
- Int queueSize = m_pendingUndoShroudReveals.size();
- xfer->xferInt(&queueSize);
- // This xfer thing is still cool. On save, I just wrote the number of elements. On load, the first line
- // above was a decoy, and the second found out the size.
- if(xfer->getXferMode() == XFER_LOAD)
- {
- // have to remove this assert, because during load there is a setTeam call for each guy on a sub-team, and that results
- // in a queued unlook, so we actually have stuff in here at the start. I am fairly certain that setTeam should wait
- // until loadPostProcess, but I ain't gonna change it now.
- // DEBUG_ASSERTCRASH(m_pendingUndoShroudReveals.size() == 0, ("At load, we appear to not be in a reset state.") );
- // I have to split this up though, since on Load I need to make new instances.
- for( Int infoIndex = 0; infoIndex < queueSize; infoIndex++ )
- {
- SightingInfo *newInfo = newInstance(SightingInfo);
- xfer->xferSnapshot(newInfo);
- m_pendingUndoShroudReveals.push(newInfo);
- }
- }
- else
- {
- // And on Save, I need to loop through, but not destroy anything, so I pop-push n times, cycling through the queue once
- // (No random access on queues, and speed not a concern in saveload)
- for( Int infoIndex = 0; infoIndex < queueSize; infoIndex++ )
- {
- SightingInfo *saveInfo = m_pendingUndoShroudReveals.front();
- xfer->xferSnapshot(saveInfo);
- m_pendingUndoShroudReveals.pop();
- m_pendingUndoShroudReveals.push(saveInfo);
- }
- }
- }
- else
- {
- // Version 1 save games will just not have any SightingInfos in the queue to be undone.
- }
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void PartitionManager::loadPostProcess( void )
- {
- } // end loadPostProcess
- //-----------------------------------------------------------------------------
- Real PartitionManager::getGroundOrStructureHeight(Real posx, Real posy)
- {
- // get the terrain height
- Real terrainHeightHere = TheTerrainLogic->getGroundHeight( posx, posy );
- // scan all objects in the radius of our extent and find the tallest height among them
- PartitionFilterAcceptByKindOf filter1( MAKE_KINDOF_MASK( KINDOF_STRUCTURE ), KINDOFMASK_NONE );
- PartitionFilter *filters[] = { &filter1, NULL };
- Coord3D pos;
- pos.x = posx;
- pos.y = posy;
- pos.z = terrainHeightHere;
- const Real RANGE = 1.0f;
- ObjectIterator *iter = iterateObjectsInRange( &pos, RANGE, FROM_BOUNDINGSPHERE_2D, filters );
- MemoryPoolObjectHolder hold( iter );
- Real tallestHeight = 0.0f;
- Real thisHeight;
- for( Object* obj = iter->first(); obj; obj = iter->next() )
- {
- // store the height of the tallest object under us
- thisHeight = obj->getGeometryInfo().getMaxHeightAbovePosition();
- if( thisHeight > tallestHeight )
- tallestHeight = thisHeight;
- }
-
- return terrainHeightHere + tallestHeight;
- }
- //-------------------------------------------------------------------------------------------------
- void PartitionManager::getMostValuableLocation( Int playerIndex, UnsignedInt whichPlayerTypes, ValueOrThreat valType, Coord3D *outLocation )
- {
- if (!outLocation)
- return;
- PlayerMaskType playerMask = ThePlayerList->getPlayersWithRelationship(playerIndex, whichPlayerTypes);
- if (playerMask == 0)
- return;
- Int cellCount = m_cellCountX * m_cellCountY;
-
- PlayerMaskType allPlayerMasks[MAX_PLAYER_COUNT] = { 0 };
- Int totalPlayerCount = ThePlayerList->getPlayerCount();
-
- Int i;
- for (i = 0; i < totalPlayerCount; ++i) {
- Player *player = ThePlayerList->getNthPlayer(i);
- if (!player) {
- continue;
- }
- allPlayerMasks[i] = player->getPlayerMask();
- }
- Int greatestValueCell = -1;
- Int maxCellValue = -1;
- for (i = 0; i < cellCount; ++i) {
- Int cellValue = 0;
- for (Int player = 0; player < MAX_PLAYER_COUNT; ++player) {
- if (BitTest(allPlayerMasks[player], playerMask)) {
- if (valType == VOT_CashValue) {
- cellValue += m_cells[i].getCashValue(player);
- } else {
- cellValue += m_cells[i].getThreatValue(player);
- }
- }
- }
- if (cellValue > maxCellValue) {
- maxCellValue = cellValue;
- greatestValueCell = i;
- }
- }
- if (greatestValueCell == -1 || maxCellValue == -1) {
- DEBUG_CRASH(("PartitionManager::getMostValuableLocation: jkmcd"));
- return;
- }
- outLocation->set(m_cells[greatestValueCell].getCellX() * TheGlobalData->m_partitionCellSize,
- m_cells[greatestValueCell].getCellY() * TheGlobalData->m_partitionCellSize,
- 0
- );
- }
- //-------------------------------------------------------------------------------------------------
- void PartitionManager::getNearestGroupWithValue( Int playerIndex, UnsignedInt whichPlayerTypes, ValueOrThreat valType,
- const Coord3D *sourceLocation, Int valueRequired, Bool greaterThan, Coord3D *outLocation )
- {
- if (!(sourceLocation && outLocation))
- return;
- PlayerMaskType playerMask = ThePlayerList->getPlayersWithRelationship(playerIndex, whichPlayerTypes);
- if (playerMask == 0)
- return;
-
- PlayerMaskType allPlayerMasks[MAX_PLAYER_COUNT] = { 0 };
- Int totalPlayerCount = ThePlayerList->getPlayerCount();
-
- Int i;
- for (i = 0; i < totalPlayerCount; ++i) {
- Player *player = ThePlayerList->getNthPlayer(i);
- if (!player) {
- continue;
- }
- allPlayerMasks[i] = player->getPlayerMask();
- }
- CellValueProcParms parms;
- parms.valueRequired = valueRequired;
- parms.greaterThan = valueRequired;
- parms.valueType = valType;
- parms.allowedPlayersMasks = playerMask;
- for (i = 0; i < MAX_PLAYER_COUNT; ++i)
- parms.allPlayersMask[i] = allPlayerMasks[i];
-
- Int nearestGreat = iterateCellsBreadthFirst(sourceLocation, cellValueProc, &parms);
- if (nearestGreat != -1) {
- (*outLocation).x = m_cells[nearestGreat].getCellX() * TheGlobalData->m_partitionCellSize;
- (*outLocation).y = m_cells[nearestGreat].getCellY() * TheGlobalData->m_partitionCellSize;
- (*outLocation).z = 0;
- }
- // all done
- }
- //-------------------------------------------------------------------------------------------------
- void PartitionManager::storeFoggedCells(ShroudStatusStoreRestore &outPartitionStore, Bool storeToFog) const
- {
- Int i, j, p;
- if (storeToFog) {
- // This is the first pass
- outPartitionStore.m_cellsWide = m_cellCountX;
-
- // Resize all of our arrays.
- for (p = 0; p < MAX_PLAYER_COUNT; ++p) {
- outPartitionStore.m_foggedOrRevealed[p].resize(m_totalCellCount);
- for (i = 0; i < m_totalCellCount; ++i) {
- outPartitionStore.m_foggedOrRevealed[p][i] = STORE_DONTTOUCH;
- }
- }
- }
- for (p = 0; p < MAX_PLAYER_COUNT; ++p) {
- if (outPartitionStore.m_foggedOrRevealed[p].size() != m_totalCellCount) {
- DEBUG_CRASH(("PartitionManager::storeFoggedCells: jkmcd - x36872"));
- continue;
- }
- for (j = 0; j < m_cellCountY; ++j) {
- for (i = 0; i < m_cellCountX; ++i) {
- UnsignedByte &byteToWrite = outPartitionStore.m_foggedOrRevealed[p][j * m_cellCountX + i];
- if (storeToFog && m_cells[j * m_cellCountX + i].getShroudStatusForPlayer(p) == CELLSHROUD_FOGGED) {
- byteToWrite = STORE_FOG;
- }
-
- if (!storeToFog && m_cells[j * m_cellCountX + i].getShroudStatusForPlayer(p) == CELLSHROUD_CLEAR) {
- byteToWrite = STORE_PERMANENTLY_REVEALED;
- }
- }
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- void PartitionManager::restoreFoggedCells(const ShroudStatusStoreRestore &inPartitionStore, Bool restoreToFog)
- {
- Int i, j, p;
- Int storeWidth = inPartitionStore.m_cellsWide;
- Int storeHeight = inPartitionStore.m_foggedOrRevealed[0].size() / storeWidth;
- for (p = 0; p < MAX_PLAYER_COUNT; ++p) {
- for (j = 0; j < storeHeight; ++j) {
- if (j >= m_cellCountY) {
- // All done.
- return;
- }
- for (i = 0; i < storeWidth; ++i) {
- if (i >= m_cellCountX) {
- // Skip to the next line. This info will be thrown away.
- break;
- }
- UnsignedByte byteToRestore = inPartitionStore.m_foggedOrRevealed[p][j * storeWidth + i];
- if (byteToRestore == STORE_DONTTOUCH) {
- continue;
- }
- if (byteToRestore == STORE_FOG && restoreToFog) {
- // restore the fog status of this cell
- m_cells[j * m_cellCountX + i].addLooker(p);
- m_cells[j * m_cellCountX + i].removeLooker(p);
- }
- if (byteToRestore == STORE_PERMANENTLY_REVEALED && !restoreToFog) {
- // Add an extra looker.
- m_cells[j * m_cellCountX + i].addLooker(p);
- }
- }
- }
- }
- }
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- PartitionFilterRejectBuildings::PartitionFilterRejectBuildings(const Object *o) :
- m_self(o),
- m_acquireEnemies(false)
- {
- // if I am a computer-controlled opponent, auto-aquire enemy buildings
- if (m_self->getControllingPlayer()->getPlayerType() == PLAYER_COMPUTER)
- {
- m_acquireEnemies = true;
- }
- }
- //-----------------------------------------------------------------------------
- Bool PartitionFilterRejectBuildings::allow( Object *other )
- {
- // this filter allows all non-buildings
- // um, no, it clearly doesn't any more.
- // didn't the person adding the code below read the comment?
- if (!other->isKindOf( KINDOF_STRUCTURE ))
- return true;
- const Player* myPlayer = m_self->getControllingPlayer();
- if (!myPlayer)
- return false;
- // Get the controlling team of other.
- ContainModuleInterface* contain = other->getContain();
- const Player* otherPlayer = contain ? contain->getApparentControllingPlayer(myPlayer) : NULL;
- if (!otherPlayer)
- otherPlayer = other->getControllingPlayer();
- if (!otherPlayer)
- return false;
- Relationship relationship = myPlayer->getRelationship(otherPlayer->getDefaultTeam());
- if (relationship != ENEMIES)
- return false;
- // if I am a computer-controlled opponent, auto-aquire enemy buildings (if we can see them!)
- if (m_acquireEnemies)
- return true;
- if (other->isKindOf( KINDOF_FS_BASE_DEFENSE))
- {
- // Don't reject base defenses.
- return true;
- }
- if (other->getContain() != NULL && other->isAbleToAttack())
- {
- // Don't reject garrisoned buildings that can attack
- return true;
- }
- return false;
- }
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- Bool PartitionFilterFreeOfFog::allow( Object *other )
- {
- return other->getShroudedStatus(m_comparisonIndex) == OBJECTSHROUD_CLEAR;
- }
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- Bool PartitionFilterInsignificantBuildings::allow( Object *other )
- {
- if (other->isStructure()) {
- if (other->isNonFactionStructure() && !m_allowInsignificant) {
- ContainModuleInterface *cmi = other->getContain();
- if (cmi) {
- if (!cmi->isGarrisonable() || cmi->getContainCount() == 0) {
- return false;
- }
- }
- }
- return true;
-
- } else {
- if (m_allowNonBuildings) {
- return true;
- }
- }
- return false;
- }
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- Bool PartitionFilterRepulsor::allow( Object *other )
- {
- if (other == m_self)
- {
- // don't repulse yourself. :)
- return false;
- }
- // If it's flagged, it's a repulsor.
- if (other->testStatus(OBJECT_STATUS_REPULSOR))
- {
- return true;
- }
- if (other->isEffectivelyDead())
- return false; // no dead enemies.
- Relationship r = m_self->getRelationship(other);
- if (r != ENEMIES)
- {
- return false; // only enemies auto repulse.
- }
- if (other->isKindOf( KINDOF_STRUCTURE ))
- {
- // always pay attention to buildings that can attack
- if (other->isAbleToAttack())
- return true;
- return false;
- }
-
- if ( other->isKindOf( KINDOF_INERT ))
- return false;
- if ( ! other->isAbleToAttack() )
- {
- return false;
- }
- return true;
- }
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- Bool PartitionFilterIrregularArea::allow( Object *other )
- {
- return PointInsideArea2D(other->getPosition(), m_area, m_numPointsInArea);
- }
- //-----------------------------------------------------------------------------
- Bool PartitionFilterPolygonTrigger::allow( Object *other )
- {
- ICoord3D iPos;
- iPos.x = other->getPosition()->x;
- iPos.y = other->getPosition()->y;
- iPos.z = 0; // Trigger areas compare on xy only.
- return m_trigger->pointInTrigger(iPos);
- }
- //-----------------------------------------------------------------------------
- Bool PartitionFilterPlayer::allow( Object *other )
- {
- return ((m_player == other->getControllingPlayer()) == m_match);
- }
- //-----------------------------------------------------------------------------
- Bool PartitionFilterPlayerAffiliation::allow( Object *other )
- {
- Relationship rel = m_player->getRelationship(other->getTeam());
- switch (rel)
- {
- case ENEMIES:
- if (m_affiliation & ALLOW_ENEMIES) {
- return m_match;
- }
- break;
- case NEUTRAL:
- if (m_affiliation & ALLOW_NEUTRAL) {
- return m_match;
- }
- break;
- case ALLIES:
- if (m_affiliation & ALLOW_ALLIES) {
- return m_match;
- }
- break;
- }
- if (other->getControllingPlayer() == m_player) {
- return m_match;
- }
- return !m_match;
- }
- //-----------------------------------------------------------------------------
- Bool PartitionFilterThing::allow( Object *other )
- {
- return (m_tThing->isEquivalentTo(other->getTemplate()) == m_match);
- }
- //-----------------------------------------------------------------------------
- Bool PartitionFilterGarrisonable::allow( Object *other )
- {
- ContainModuleInterface *cmi = other->getContain();
- if (!cmi) {
- return !m_match;
- }
- return (cmi->isGarrisonable() == m_match);
- }
- //-----------------------------------------------------------------------------
- Bool PartitionFilterGarrisonableByPlayer::allow( Object *other )
- {
- return TheActionManager->canPlayerGarrison(m_player, other, m_commandSource) == m_match;
- }
- //-----------------------------------------------------------------------------
- Bool PartitionFilterUnmannedObject::allow( Object *other )
- {
- return (other->isDisabledByType( DISABLED_UNMANNED ) == m_match);
- }
- //-----------------------------------------------------------------------------
- Bool PartitionFilterValidCommandButtonTarget::allow( Object *other )
- {
- return (m_commandButton->isValidToUseOn(m_source, other, NULL, m_commandSource) == m_match);
- }
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- Bool PartitionFilterIsFlying::allow(Object *objOther)
- {
- return objOther->isUsingAirborneLocomotor();
- }
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- PartitionFilterWouldCollide::PartitionFilterWouldCollide(const Coord3D& pos, const GeometryInfo& geom, Real angle, Bool desired) :
- m_position(pos),
- m_geom(geom),
- m_angle(angle),
- m_desiredCollisionResult(desired)
- {
- }
- //-----------------------------------------------------------------------------
- Bool PartitionFilterWouldCollide::allow(Object *objOther)
- {
- CollideInfo thisInfo(&m_position, m_geom, m_angle);
- CollideInfo thatInfo(objOther->getPosition(), objOther->getGeometryInfo(), objOther->getOrientation());
- Bool doesCollide;
- // invariant for all geometries: first do z collision check.
- if (thisInfo.position.z + thisInfo.geom.getMaxHeightAbovePosition() >= thatInfo.position.z &&
- thisInfo.position.z <= thatInfo.position.z + thatInfo.geom.getMaxHeightAbovePosition())
- {
- GeometryType thisGeom = m_geom.getGeomType();
- GeometryType thatGeom = objOther->getGeometryInfo().getGeomType();
- //
- // NOTE: This assumes geometry enumerations that start at GEOMETRY_FIRST AND depends on the
- // order in which they appear in the enum list
- //
- CollideTestProc collideProc = theCollideTestProcs[ (thisGeom - GEOMETRY_FIRST) * GEOMETRY_NUM_TYPES + (thatGeom - GEOMETRY_FIRST) ];
- CollideLocAndNormal cinfo;
- doesCollide = (*collideProc)(&thisInfo, &thatInfo, &cinfo);
- }
- else
- {
- // no z-intersection -> no collision.
- doesCollide = false;
- }
- return doesCollide == m_desiredCollisionResult;
- }
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- Bool PartitionFilterSamePlayer::allow(Object *objOther)
- {
- if (m_player == objOther->getControllingPlayer())
- return true;
- return false;
- }
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- Bool PartitionFilterRelationship::allow(Object *objOther)
- {
- Relationship r = m_obj->getRelationship(objOther);
- if ((m_flags & (1<<r)) != 0)
- return true;
- return false;
- }
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- PartitionFilterAcceptOnTeam::PartitionFilterAcceptOnTeam(const Team *team) : m_team(team)
- {
- }
- //-----------------------------------------------------------------------------
- Bool PartitionFilterAcceptOnTeam::allow(Object *objOther)
- {
- // objOther is guaranteed to be non-null, so we don't need to check (srj)
- return (objOther->getTeam() == m_team);
- }
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- PartitionFilterAcceptOnSquad::PartitionFilterAcceptOnSquad(const Squad *squad) : m_squad(squad)
- {
- }
- //-----------------------------------------------------------------------------
- Bool PartitionFilterAcceptOnSquad::allow(Object *objOther)
- {
- return (m_squad && m_squad->isOnSquad(objOther) && !objOther->isEffectivelyDead());
- }
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- Bool PartitionFilterSameMapStatus::allow(Object* objOther)
- {
- // objOther is guaranteed to be non-null, so we don't need to check (srj)
- return objOther->isOffMap() == m_obj->isOffMap();
- }
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- Bool PartitionFilterOnMap::allow(Object* objOther)
- {
- // objOther is guaranteed to be non-null, so we don't need to check (srj)
- return !objOther->isOffMap();
- }
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- Bool PartitionFilterAlive::allow( Object *objOther )
- {
- return !objOther->isEffectivelyDead();
- }
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- PartitionFilterRejectBehind::PartitionFilterRejectBehind( Object *obj )
- {
- m_obj = obj;
- }
- //-----------------------------------------------------------------------------
- Bool PartitionFilterRejectBehind::allow( Object *other )
- {
- // objOther is guaranteed to be non-null, so we don't need to check (srj)
- //const Coord3D *pos = m_obj->getPosition();
- //const Coord3D *dir = m_obj->getUnitDirectionVector2D();
- Vector3 dir = m_obj->getTransformMatrix()->Get_X_Vector();
- dir.Normalize();
- //const Coord3D *otherPos = other->getPosition();
- Coord3D v;
- ThePartitionManager->getVectorTo( m_obj, other, FROM_CENTER_3D, v );
- Real dot = dir.X * v.x + dir.Y * v.y + dir.Z * v.z;
- if (dot > 0.0f)
- return true;
- return false;
- }
- //-----------------------------------------------------------------------------
- PartitionFilterLineOfSight::PartitionFilterLineOfSight(const Object *obj)
- {
- m_obj = obj;
- }
- //-----------------------------------------------------------------------------
- Bool PartitionFilterLineOfSight::allow(Object *objOther)
- {
- // objOther is guaranteed to be non-null, so we don't need to check (srj)
- if (!ThePartitionManager->isClearLineOfSightTerrain(m_obj, *m_obj->getPosition(), objOther, *objOther->getPosition()))
- return false;
- if (TheAI && TheAI->pathfinder()->isViewBlockedByObstacle(m_obj, objOther))
- return false;
- return true;
- }
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- PartitionFilterPossibleToAttack::PartitionFilterPossibleToAttack(AbleToAttackType t, const Object *obj, CommandSourceType commandSource) :
- m_attackType(t),
- m_obj(obj),
- m_commandSource(commandSource)
- {
- }
- //-----------------------------------------------------------------------------
- Bool PartitionFilterPossibleToAttack::allow(Object *objOther)
- {
- // objOther is guaranteed to be non-null, so we don't need to check (srj)
-
- // don't do this here... done in getAbleToAttackSpecificObject (srj)
- // // cannot attack dead things
- // if (objOther->isEffectivelyDead())
- // return false;
- // don't do this here... done in getAbleToAttackSpecificObject, with more/better checking for Disguise (srj)
- // // stealthed items can't ever be attacked.
- // UnsignedInt status = objOther->getStatusBits();
- // if ((status & OBJECT_STATUS_STEALTHED) && !(status & OBJECT_STATUS_DETECTED))
- // return false;
- // we should have already filtered out isAbleToAttack!
- #ifdef _DEBUG
- // disable this assert for INTERNAL builds (srj)
- DEBUG_ASSERTCRASH(m_obj && m_obj->isAbleToAttack(), ("if the object is unable to attack at all, you should filter that out ahead of time!"));
- #endif
- CanAttackResult result = m_obj->getAbleToAttackSpecificObject( m_attackType, objOther, m_commandSource );
- if( result == ATTACKRESULT_POSSIBLE || result == ATTACKRESULT_POSSIBLE_AFTER_MOVING )
- {
- return TRUE;
- }
- return FALSE;
- }
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- PartitionFilterLastAttackedBy::PartitionFilterLastAttackedBy(Object *obj)
- {
- if (obj && obj->getBodyModule()) {
- m_lastAttackedBy = obj->getBodyModule()->getLastDamageInfo()->in.m_sourceID;
- } else {
- m_lastAttackedBy = INVALID_ID;
- }
- }
- //-----------------------------------------------------------------------------
- Bool PartitionFilterLastAttackedBy::allow(Object *other)
- {
- // objOther is guaranteed to be non-null, so we don't need to check (srj)
- if (other->getID() == m_lastAttackedBy)
- return true;
- return false;
- }
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- Bool PartitionFilterAcceptByObjectStatus::allow(Object *objOther)
- {
- UnsignedInt status = objOther->getStatusBits();
- return ((status & m_mustBeSet) == m_mustBeSet) && ((status & m_mustBeClear) == 0);
- }
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- Bool PartitionFilterRejectByObjectStatus::allow(Object *objOther)
- {
- UnsignedInt status = objOther->getStatusBits();
- return !(((status & m_mustBeSet) == m_mustBeSet) && ((status & m_mustBeClear) == 0));
- }
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- Bool PartitionFilterAcceptByKindOf::allow(Object *objOther)
- {
- return objOther->isKindOfMulti(m_mustBeSet, m_mustBeClear);
- }
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- Bool PartitionFilterRejectByKindOf::allow(Object *objOther)
- {
- return !objOther->isKindOfMulti(m_mustBeSet, m_mustBeClear);
- }
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- //-----------------------------------------------------------------------------
- Bool PartitionFilterStealthedAndUndetected::allow( Object *objOther )
- {
- // objOther is guaranteed to be non-null, so we don't need to check (srj)
- if( BitTest( objOther->getStatusBits(), OBJECT_STATUS_STEALTHED ) && !BitTest( objOther->getStatusBits(), OBJECT_STATUS_DETECTED ) )
- {
- if( !objOther->isKindOf( KINDOF_DISGUISER ) )
- {
- //We have a stealthed & undetected object.
- return m_allow;
- }
- else
- {
- //Exception case -- bomb trucks can't be considered stealthed units when they are disguised as the enemy.
- static NameKeyType key_StealthUpdate = NAMEKEY( "StealthUpdate" );
- StealthUpdate *update = (StealthUpdate*)objOther->findUpdateModule( key_StealthUpdate );
- if( update && update->isDisguised() )
- {
- Player *ourPlayer = m_obj->getControllingPlayer();
- Player *otherPlayer = ThePlayerList->getNthPlayer( update->getDisguisedPlayerIndex() );
- if( ourPlayer && otherPlayer )
- {
- if( ourPlayer->getRelationship( otherPlayer->getDefaultTeam() ) == ENEMIES )
- {
- //Our stealthed & undetected object is disguised as a unit perceived to be our enemy,
- //therefore it's not considered to be stealthed!
- return !m_allow;
- }
- //Our object is disguised as a "friendly" object, therefore it's considered to be stealthed.
- return m_allow;
- }
- }
- //Our bomb truck is not disguised therefore it's not stealthed.
- return !m_allow;
- }
- }
- else
- {
- //This handles neutral containers that hold stealth units. This specifically fixes a bug where hunt scripts would ignore
- //this case -- units would acquire the building Jarmen Kell occupied even though it was not stealth detected.
- const ContainModuleInterface* contain = objOther->getContain();
- if( contain )
- {
- const Player* victimApparentController = contain->getApparentControllingPlayer( m_obj->getControllingPlayer() );
- //Check if it's stealthed!
- if( contain->getStealthUnitsContained() == contain->getContainCount() )
- {
- //Check if the first object inside is detected (if one is detected, all are detected).
- ContainedItemsList::const_iterator it = contain->getContainedItemsList()->begin();
- Object *member = (*it);
- if( member && !BitTest( (*it)->getStatusBits(), OBJECT_STATUS_DETECTED ) )
- {
- //Finally check the relationship!
- if( victimApparentController && m_obj->getTeam()->getRelationship( victimApparentController->getDefaultTeam() ) == ENEMIES )
- {
- //Our object is a neutral building garrisoned by enemy units we can't see, therefore it is stealthed.
- return m_allow;
- }
- }
- }
- }
- }
- //The unit is not stealthed!
- return !m_allow;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- static int cellValueProc(PartitionCell* cell, void* userData)
- {
- CellValueProcParms *parms = (CellValueProcParms*) userData;
- UnsignedInt val = 0;
- for (Int i = 0; i < MAX_PLAYER_COUNT; ++i) {
- if (BitTest(parms->allowedPlayersMasks, parms->allPlayersMask[i])) {
- if (parms->valueType == VOT_CashValue) {
- val += cell->getCashValue(i);
- } else {
- val += cell->getThreatValue(i);
- }
- }
- }
- if ((val > parms->valueRequired && parms->greaterThan) ||
- (val < parms->valueRequired && !parms->greaterThan)) {
- return 1;
- }
- return 0;
- }
- // -----------------------------------------------------------------------------
- static void hLineAddLooker(Int x1, Int x2, Int y, void *playerIndexVoid)
- {
- if (y < 0 || y >= ThePartitionManager->m_cellCountY || x1 >= ThePartitionManager->m_cellCountX || x2 < 0)
- return;
- Int playerIndex = (Int)(playerIndexVoid);
- PartitionCell* cell = &ThePartitionManager->m_cells[y * ThePartitionManager->m_cellCountX + x1]; // yes, this could be invalid. we'll skip the bad ones.
- for (Int x = x1; x <= x2; ++x, ++cell)
- {
- if (x < 0 || x >= ThePartitionManager->m_cellCountX)
- continue;
- cell->addLooker(playerIndex);
- }
- }
- // -----------------------------------------------------------------------------
- static void hLineRemoveLooker(Int x1, Int x2, Int y, void *playerIndexVoid)
- {
- if (y < 0 || y >= ThePartitionManager->m_cellCountY || x1 >= ThePartitionManager->m_cellCountX || x2 < 0)
- return;
- Int playerIndex = (Int)(playerIndexVoid);
- PartitionCell* cell = &ThePartitionManager->m_cells[y * ThePartitionManager->m_cellCountX + x1]; // yes, this could be invalid. we'll skip the bad ones.
- for (Int x = x1; x <= x2; ++x, ++cell)
- {
- if (x < 0 || x >= ThePartitionManager->m_cellCountX)
- continue;
- cell->removeLooker(playerIndex);
- }
- }
- // -----------------------------------------------------------------------------
- static void hLineAddShrouder(Int x1, Int x2, Int y, void *playerIndexVoid)
- {
- if (y < 0 || y >= ThePartitionManager->m_cellCountY || x1 >= ThePartitionManager->m_cellCountX || x2 < 0)
- return;
- Int playerIndex = (Int)(playerIndexVoid);
- PartitionCell* cell = &ThePartitionManager->m_cells[y * ThePartitionManager->m_cellCountX + x1]; // yes, this could be invalid. we'll skip the bad ones.
- for (Int x = x1; x <= x2; ++x, ++cell)
- {
- if (x < 0 || x >= ThePartitionManager->m_cellCountX)
- continue;
- cell->addShrouder( playerIndex );
- }
- }
- // -----------------------------------------------------------------------------
- static void hLineRemoveShrouder(Int x1, Int x2, Int y, void *playerIndexVoid)
- {
- if (y < 0 || y >= ThePartitionManager->m_cellCountY || x1 >= ThePartitionManager->m_cellCountX || x2 < 0)
- return;
- Int playerIndex = (Int)(playerIndexVoid);
- PartitionCell* cell = &ThePartitionManager->m_cells[y * ThePartitionManager->m_cellCountX + x1]; // yes, this could be invalid. we'll skip the bad ones.
- for (Int x = x1; x <= x2; ++x, ++cell)
- {
- if (x < 0 || x >= ThePartitionManager->m_cellCountX)
- continue;
- cell->removeShrouder( playerIndex );
- }
- }
- // -----------------------------------------------------------------------------
- static void hLineAddThreat(Int x1, Int x2, Int y, void *threatValueParms)
- {
- if (y < 0 || y >= ThePartitionManager->m_cellCountY || x1 >= ThePartitionManager->m_cellCountX || x2 < 0)
- return;
- ThreatValueParms *parms = (ThreatValueParms*)threatValueParms;
- Real distance;
- Real mulVal = 1.0f;
- PartitionCell* cell = &ThePartitionManager->m_cells[y * ThePartitionManager->m_cellCountX + x1]; // yes, this could be invalid. we'll skip the bad ones.
- for (Int x = x1; x <= x2; ++x, ++cell)
- {
- if (x < 0 || x >= ThePartitionManager->m_cellCountX)
- continue;
- distance = sqrt( pow(x - parms->xCenter, 2) + pow(y - parms->yCenter, 2) );
- mulVal = 1 - distance / parms->radius;
- if (mulVal < 0.0f)
- mulVal = 0.0f;
- else if (mulVal > 1.0f)
- mulVal = 1.0f;
- cell->addThreatValue( parms->playerIndex, REAL_TO_UNSIGNEDINT(parms->threatOrValue * mulVal) );
- }
- }
- // -----------------------------------------------------------------------------
- static void hLineRemoveThreat(Int x1, Int x2, Int y, void *threatValueParms)
- {
- if (y < 0 || y >= ThePartitionManager->m_cellCountY || x1 >= ThePartitionManager->m_cellCountX || x2 < 0)
- return;
- ThreatValueParms *parms = (ThreatValueParms*)threatValueParms;
- Real distance;
- Real mulVal = 1.0f;
- PartitionCell* cell = &ThePartitionManager->m_cells[y * ThePartitionManager->m_cellCountX + x1]; // yes, this could be invalid. we'll skip the bad ones.
- for (Int x = x1; x <= x2; ++x, ++cell)
- {
- if (x < 0 || x >= ThePartitionManager->m_cellCountX)
- continue;
- distance = sqrt( pow(x - parms->xCenter, 2) + pow(y - parms->yCenter, 2) );
- mulVal = 1 - distance / parms->radius;
- if (mulVal < 0.0f)
- mulVal = 0.0f;
- else if (mulVal > 1.0f)
- mulVal = 1.0f;
-
- cell->removeThreatValue( parms->playerIndex, REAL_TO_UNSIGNEDINT(parms->threatOrValue * mulVal) );
- }
- }
- // -----------------------------------------------------------------------------
- static void hLineAddValue(Int x1, Int x2, Int y, void *threatValueParms)
- {
- if (y < 0 || y >= ThePartitionManager->m_cellCountY || x1 >= ThePartitionManager->m_cellCountX || x2 < 0)
- return;
- ThreatValueParms *parms = (ThreatValueParms*)threatValueParms;
- Real distance;
- Real mulVal = 1.0f;
- PartitionCell* cell = &ThePartitionManager->m_cells[y * ThePartitionManager->m_cellCountX + x1]; // yes, this could be invalid. we'll skip the bad ones.
- for (Int x = x1; x <= x2; ++x, ++cell)
- {
- if (x < 0 || x >= ThePartitionManager->m_cellCountX)
- continue;
- distance = sqrt( pow(x - parms->xCenter, 2) + pow(y - parms->yCenter, 2) );
- mulVal = 1 - distance / parms->radius;
- if (mulVal < 0.0f)
- mulVal = 0.0f;
- else if (mulVal > 1.0f)
- mulVal = 1.0f;
-
- cell->addCashValue( parms->playerIndex, REAL_TO_UNSIGNEDINT(parms->threatOrValue * mulVal) );
- }
- }
- // -----------------------------------------------------------------------------
- static void hLineRemoveValue(Int x1, Int x2, Int y, void *threatValueParms)
- {
- if (y < 0 || y >= ThePartitionManager->m_cellCountY || x1 >= ThePartitionManager->m_cellCountX || x2 < 0)
- return;
- ThreatValueParms *parms = (ThreatValueParms*)threatValueParms;
- Real distance;
- Real mulVal = 1.0f;
- PartitionCell* cell = &ThePartitionManager->m_cells[y * ThePartitionManager->m_cellCountX + x1]; // yes, this could be invalid. we'll skip the bad ones.
- for (Int x = x1; x <= x2; ++x, ++cell)
- {
- if (x < 0 || x >= ThePartitionManager->m_cellCountX)
- continue;
- distance = sqrt( pow(x - parms->xCenter, 2) + pow(y - parms->yCenter, 2) );
- mulVal = 1 - distance / parms->radius;
- if (mulVal < 0.0f)
- mulVal = 0.0f;
- else if (mulVal > 1.0f)
- mulVal = 1.0f;
- cell->removeCashValue( parms->playerIndex, REAL_TO_UNSIGNEDINT(parms->threatOrValue * mulVal) );
- }
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- SightingInfo::SightingInfo()
- {
- reset();
- }
- // ------------------------------------------------------------------------------------------------
- /** Resetting sighting info */
- // ------------------------------------------------------------------------------------------------
- void SightingInfo::reset()
- {
- m_where.zero();
- m_howFar = 0.0f;
- m_forWhom = 0;
- m_data = 0;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- Bool SightingInfo::isInvalid() const
- {
- return m_howFar == 0.0f;
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void SightingInfo::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method
- * Version Info:
- * 1: Initial version */
- // ------------------------------------------------------------------------------------------------
- void SightingInfo::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- // where
- xfer->xferCoord3D( &m_where );
- // how far
- xfer->xferReal( &m_howFar );
- // for whom
- xfer->xferUser( &m_forWhom, sizeof( PlayerMaskType ) );
- // how much
- xfer->xferUnsignedInt( &m_data );
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void SightingInfo::loadPostProcess()
- {
- } // end loadPostProcess
- // ------------------------------------------------------------------------------------------------
- SightingInfo::~SightingInfo()
- {
- } // end loadPostProcess
|