PartitionManager.cpp 185 KB

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