| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415 |
- /*
- ** Command & Conquer Generals(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // FILE Object.cpp ////////////////////////////////////////////////////////////////////////////////
- // Simple base object
- // Author: Michael S. Booth, October 2000
- ///////////////////////////////////////////////////////////////////////////////////////////////////
-
- // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #define DEFINE_WEAPONCONDITIONMAP
- #include "Common/BitFlagsIO.h"
- #include "Common/BuildAssistant.h"
- #include "Common/Dict.h"
- #include "Common/GameEngine.h"
- #include "Common/GameState.h"
- #include "Common/ModuleFactory.h"
- #include "Common/Player.h"
- #include "Common/PlayerList.h"
- #include "Common/Radar.h"
- #include "Common/SpecialPower.h"
- #include "Common/Team.h"
- #include "Common/ThingFactory.h"
- #include "Common/ThingTemplate.h"
- #include "Common/Upgrade.h"
- #include "Common/WellKnownKeys.h"
- #include "Common/Xfer.h"
- #include "Common/XferCRC.h"
- #include "Common/PerfTimer.h"
- #include "GameClient/Anim2D.h"
- #include "GameClient/ControlBar.h"
- #include "GameClient/Drawable.h"
- #include "GameClient/Eva.h"
- #include "GameClient/InGameUI.h"
- #include "GameLogic/AI.h"
- #include "GameLogic/AIPathfind.h"
- #include "GameLogic/ExperienceTracker.h"
- #include "GameLogic/FiringTracker.h"
- #include "GameLogic/GameLogic.h"
- #include "GameLogic/Locomotor.h"
- #include "GameLogic/Module/AIUpdate.h"
- #include "GameLogic/Module/AutoHealBehavior.h"
- #include "GameLogic/Module/BehaviorModule.h"
- #include "GameLogic/Module/BodyModule.h"
- #include "GameLogic/Module/CollideModule.h"
- #include "GameLogic/Module/ContainModule.h"
- #include "GameLogic/Module/CreateModule.h"
- #include "GameLogic/Module/DamageModule.h"
- #include "GameLogic/Module/DeletionUpdate.h"
- #include "GameLogic/Module/DestroyModule.h"
- #include "GameLogic/Module/DieModule.h"
- #include "GameLogic/Module/ObjectDefectionHelper.h"
- #include "GameLogic/Module/ObjectRepulsorHelper.h"
- #include "GameLogic/Module/ObjectSMCHelper.h"
- #include "GameLogic/Module/ObjectWeaponStatusHelper.h"
- #include "GameLogic/Module/OverchargeBehavior.h"
- #include "GameLogic/Module/PhysicsUpdate.h"
- #include "GameLogic/Module/PowerPlantUpgrade.h"
- #include "GameLogic/Module/ProductionUpdate.h"
- #include "GameLogic/Module/RadarUpgrade.h"
- #include "GameLogic/Module/SpawnBehavior.h"
- #include "GameLogic/Module/SpecialPowerModule.h"
- #include "GameLogic/Module/SpecialAbilityUpdate.h"
- #include "GameLogic/Module/ToppleUpdate.h"
- #include "GameLogic/Module/UpdateModule.h"
- #include "GameLogic/Module/UpgradeModule.h"
- #include "GameLogic/Object.h"
- #include "GameLogic/PartitionManager.h"
- #include "GameLogic/PolygonTrigger.h"
- #include "GameLogic/ScriptEngine.h"
- #include "GameLogic/Weapon.h"
- #include "GameLogic/WeaponSet.h"
- #include "GameLogic/Module/RadarUpdate.h"
- #include "GameLogic/Module/PowerPlantUpdate.h"
- #include "Common/CRCDebug.h"
- #include "Common/MiscAudio.h"
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- #ifdef DEBUG_OBJECT_ID_EXISTS
- ObjectID TheObjectIDToDebug = INVALID_ID;
- #endif
- // ------------------------------------------------------------------------------------------------
- static const ModelConditionFlags s_allWeaponFireFlags[WEAPONSLOT_COUNT] =
- {
- MAKE_MODELCONDITION_MASK5(
- MODELCONDITION_FIRING_A,
- MODELCONDITION_BETWEEN_FIRING_SHOTS_A,
- MODELCONDITION_RELOADING_A,
- MODELCONDITION_PREATTACK_A,
- MODELCONDITION_USING_WEAPON_A
- ),
- MAKE_MODELCONDITION_MASK5(
- MODELCONDITION_FIRING_B,
- MODELCONDITION_BETWEEN_FIRING_SHOTS_B,
- MODELCONDITION_RELOADING_B,
- MODELCONDITION_PREATTACK_B,
- MODELCONDITION_USING_WEAPON_B
- ),
- MAKE_MODELCONDITION_MASK5(
- MODELCONDITION_FIRING_C,
- MODELCONDITION_BETWEEN_FIRING_SHOTS_C,
- MODELCONDITION_RELOADING_C,
- MODELCONDITION_PREATTACK_C,
- MODELCONDITION_USING_WEAPON_C
- )
- };
- //-------------------------------------------------------------------------------------------------
- extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- #ifdef DEBUG_LOGGING
- AsciiString DescribeObject(const Object *obj)
- {
- if (!obj)
- return "<No Object>";
- AsciiString ret;
- if (obj->getName().isNotEmpty())
- {
- ret.format("Object %d (%s) [%s, owned by player %d (%ls)]",
- obj->getID(), obj->getName().str(), obj->getTemplate()->getName().str(),
- obj->getControllingPlayer()->getPlayerIndex(),
- obj->getControllingPlayer()->getPlayerDisplayName().str());
- }
- else
- {
- ret.format("Object %d [%s, owned by player %d (%ls)]",
- obj->getID(), obj->getTemplate()->getName().str(),
- obj->getControllingPlayer()->getPlayerIndex(),
- obj->getControllingPlayer()->getPlayerDisplayName().str());
- }
- return ret;
- }
- #endif // DEBUG_LOGGING
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- Object::Object( const ThingTemplate *tt, ObjectStatusBits statusBits, Team *team ) :
- Thing(tt),
- m_indicatorColor(0),
- m_ai(NULL),
- m_physics(NULL),
- m_geometryInfo(tt->getTemplateGeometryInfo()),
- m_containedBy(NULL),
- m_xferContainedByID(INVALID_ID),
- m_containedByFrame(0),
- m_behaviors(NULL),
- m_body(NULL),
- m_contain(NULL),
- m_partitionData(NULL),
- m_radarData(NULL),
- m_drawable(NULL),
- m_next(NULL),
- m_prev(NULL),
- m_team(NULL),
- m_experienceTracker(NULL),
- m_firingTracker(NULL),
- m_repulsorHelper(NULL),
- m_smcHelper(NULL),
- m_wsHelper(NULL),
- m_defectionHelper(NULL),
- m_partitionLastLook(NULL),
- m_partitionLastShroud(NULL),
- m_partitionLastThreat(NULL),
- m_partitionLastValue(NULL),
- m_smcUntil(NEVER),
- m_privateStatus(0),
- m_formationID(NO_FORMATION_ID),
- m_isReceivingDifficultyBonus(FALSE)
- {
- #if defined(_DEBUG) || defined(_INTERNAL)
- m_hasDiedAlready = false;
- #endif
- //Modules have not been created yet!
- m_modulesReady = false;
- // Force the thing template to use the most overridden version of itself - jkmcd
- // Note that after this, the object will be using m_template, which forces the usage of the
- // most overridden version of tt, so this is okay.
- tt = (const ThingTemplate *) tt->getFinalOverride();
-
- Int i, modIdx;
- AsciiString modName;
-
- //Added By Sadullah Nader
- //Initializations inserted
- m_formationOffset.x = m_formationOffset.y = 0.0f;
- m_iPos.zero();
- //
- for( i = 0; i < DISABLED_COUNT; i++ )
- {
- m_disabledTillFrame[ i ] = NEVER;
- }
- // sanity
- if( TheGameLogic == NULL || tt == NULL )
- {
- assert( 0 );
- return;
- } // end if
- // Object's set of these persist for the life of the object.
- m_partitionLastLook = newInstance(SightingInfo);
- m_partitionLastLook->reset();
- m_partitionLastShroud = newInstance(SightingInfo);
- m_partitionLastShroud->reset();
- m_partitionLastThreat = newInstance(SightingInfo);
- m_partitionLastThreat->reset();
- m_partitionLastValue = newInstance(SightingInfo);
- m_partitionLastValue->reset();
- // must set ID to zero, since some of these set methods
- // will cause network messages to be sent
- // which use this ID.
- m_id = INVALID_ID;
- m_producerID = INVALID_ID;
- m_builderID = INVALID_ID;
- m_status = statusBits;
- m_layer = LAYER_GROUND;
- m_group = NULL;
- m_constructionPercent = CONSTRUCTION_COMPLETE; // complete by default
- m_objectUpgradesCompleted = 0;
- m_visionRange = tt->friend_getVisionRange();
- m_shroudClearingRange = tt->friend_getShroudClearingRange();
- if( m_shroudClearingRange == -1.0f )
- m_shroudClearingRange = m_visionRange;// Backwards compatible, and perfectly logical default to assign
- m_shroudRange = 0.0f;
-
- m_singleUseCommandUsed = false;
- // assign unique object id
- setID( TheGameLogic->allocateObjectID() );
- //
- // allocate any modules we need to, we should keep
- // this at or near the end of the drawable construction so that we have
- // all the valid data about the thing when we create the module
- //
- Int totalModules = tt->getBehaviorModuleInfo().getCount() + NUM_SLEEP_HELPERS; // need to take into account all the helper modules
- // allocate the publicModule arrays
- // pool[]ify
- m_behaviors = MSGNEW("ModulePtrs") BehaviorModule*[totalModules + 1];
- BehaviorModule** curB = m_behaviors;
- // set m_team to null before the first call, to avoid naughtiness...
- // If no team is specified in the constructor, then assign the object
- // to the neutral team.
- setTeam(team ? team : ThePlayerList->getNeutralPlayer()->getDefaultTeam());
- // the helpers are done first -- even before Behaviors! -- in case a module needs
- // to call something that uses them.
- static const NameKeyType smcHelperModuleDataTagNameKey = NAMEKEY( "ModuleTag_SMCHelper" );
- static ObjectSMCHelperModuleData smcModuleData;
- smcModuleData.setModuleTagNameKey( smcHelperModuleDataTagNameKey );
- m_smcHelper = newInstance(ObjectSMCHelper)(this, &smcModuleData);
- *curB++ = m_smcHelper;
- if (TheAI != NULL
- && TheAI->getAiData()->m_enableRepulsors
- && isKindOf(KINDOF_CAN_BE_REPULSED))
- {
- // if we can ever be a temporary-repulsor, make a repulsor helper. (srj)
- static const NameKeyType repulsorHelperModuleDataTagNameKey = NAMEKEY( "ModuleTag_RepulsorHelper" );
- static ObjectRepulsorHelperModuleData repulsorModuleData;
- repulsorModuleData.setModuleTagNameKey( repulsorHelperModuleDataTagNameKey );
- m_repulsorHelper = newInstance(ObjectRepulsorHelper)(this, &repulsorModuleData);
- *curB++ = m_repulsorHelper;
- }
- /** @todo srj -- figure out how to create this only on demand.
- currently we don't have a good way to add/remove update modules from
- an object on-the-fly, so we fake it here, and just skip the creation
- if it is impossible for this object to ever defect... */
- // shrubbery cannot defect. no, really.
- if (!tt->isKindOf(KINDOF_SHRUBBERY))
- {
- static const NameKeyType defectionModuleDataTagNameKey = NAMEKEY( "ModuleTag_DefectionHelper" );
- static ObjectDefectionHelperModuleData defectionModuleData;
- defectionModuleData.setModuleTagNameKey( defectionModuleDataTagNameKey );
- m_defectionHelper = newInstance(ObjectDefectionHelper)(this, &defectionModuleData);
- *curB++ = m_defectionHelper;
- }
- if (tt->canPossiblyHaveAnyWeapon())
- {
- // we only need a firingtracker and wshelper if we can possibly have a weapon.
- static const NameKeyType weaponStatusModuleDataTagNameKey = NAMEKEY( "ModuleTag_WeaponStatusHelper" );
- static ObjectWeaponStatusHelperModuleData weaponStatusModuleData;
- weaponStatusModuleData.setModuleTagNameKey( weaponStatusModuleDataTagNameKey );
- m_wsHelper = newInstance(ObjectWeaponStatusHelper)(this, &weaponStatusModuleData);
- *curB++ = m_wsHelper;
- static const NameKeyType firingTrackerModuleDataTagNameKey = NAMEKEY( "ModuleTag_FiringTrackerHelper" );
- static FiringTrackerModuleData firingTrackerModuleData;
- firingTrackerModuleData.setModuleTagNameKey( firingTrackerModuleDataTagNameKey );
- m_firingTracker = newInstance(FiringTracker)(this, &firingTrackerModuleData);
- *curB++ = m_firingTracker;
- }
- // behaviors are always done first, so they get into the publicModule arrays
- // before anything else.
- const ModuleInfo& mi = tt->getBehaviorModuleInfo();
- for (modIdx = 0; modIdx < mi.getCount(); ++modIdx)
- {
- modName = mi.getNthName(modIdx);
- if (modName.isEmpty())
- continue;
- BehaviorModule* newMod = (BehaviorModule*)TheModuleFactory->newModule(this, modName, mi.getNthData(modIdx), MODULETYPE_BEHAVIOR);
- *curB++ = newMod;
- BodyModuleInterface* body = newMod->getBody();
- if (body)
- {
- DEBUG_ASSERTCRASH(m_body == NULL, ("Duplicate bodies"));
- m_body = body;
- }
- ContainModuleInterface* contain = newMod->getContain();
- if (contain)
- {
- DEBUG_ASSERTCRASH(m_contain == NULL, ("Duplicate containers"));
- m_contain = contain;
- }
- AIUpdateInterface* ai = newMod->getAIUpdateInterface();
- if (ai)
- {
- DEBUG_ASSERTCRASH(m_ai == NULL, ("You should never have more than one AI module (srj)\n"));
- m_ai = ai;
- }
- static NameKeyType key_PhysicsUpdate = NAMEKEY("PhysicsBehavior");
- if (newMod->getModuleNameKey() == key_PhysicsUpdate)
- {
- DEBUG_ASSERTCRASH(m_physics == NULL, ("You should never have more than one Physics module (%s)\n",getTemplate()->getName().str()));
- m_physics = (PhysicsBehavior*)newMod;
- }
- }
- *curB = NULL;
- AIUpdateInterface *ai = getAIUpdateInterface();
- if (ai) {
- ai->setAttitude(getTeam()->getPrototype()->getTemplateInfo()->m_initialTeamAttitude);
- if (m_team && m_team->getPrototype() && m_team->getPrototype()->getAttackPriorityName().isNotEmpty()) {
- AsciiString name = m_team->getPrototype()->getAttackPriorityName();
- const AttackPriorityInfo *info = TheScriptEngine->getAttackInfo(name);
- if (info && info->getName().isNotEmpty()) {
- ai->setAttackInfo(info);
- }
- }
- }
- // allocate experience tracker
- m_experienceTracker = newInstance(ExperienceTracker)(this);
- // If a valid team has been assigned me, then I have a Player I can ask about my starting level
- const Player* controller = getControllingPlayer();
- m_experienceTracker->setVeterancyLevel( controller->getProductionVeterancyLevel( getTemplate()->getName() ) );
- /// allow for inter-Module resolution
- for (BehaviorModule** b = m_behaviors; *b; ++b)
- {
- (*b)->onObjectCreated();
- }
- m_numTriggerAreasActive = 0;
- m_enteredOrExitedFrame = 0;
- m_isSelectable = tt->isKindOf(KINDOF_SELECTABLE);
- m_healthBoxOffset.zero();// this is used for units that are amorphous, like angry mob
- //Modules have now been completely created!
- m_modulesReady = true;
- TheRadar->addObject( this );
- // register the object with the GameLogic
- TheGameLogic->registerObject( this );
- //disable occlusion for some time after object is created to allow them to exit the factory/building.
- m_safeOcclusionFrame = TheGameLogic->getFrame()+tt->getOcclusionDelay();
- m_soleHealingBenefactorID = INVALID_ID; ///< who is the only other object that can give me this non-stacking heal benefit?
- m_soleHealingBenefactorExpirationFrame = 0; ///< on what frame can I accept healing (thus to switch) from a new benefactor
- } // end Object
- //-------------------------------------------------------------------------------------------------
- /** Emit message announcing object's creation
- * Note: Have to do this in virtual init() method because virtual methods
- * don't become virtual until AFTER the constructor has completed, and we
- * need to send our type in this message via virtual getType(). */
- //-------------------------------------------------------------------------------------------------
- void Object::initObject()
- {
- // Weapons & Damage -------------------------------------------------------------------------------------------------
- // Force the initial weapon set to be instantiated & reloaded.
- m_curWeaponSetFlags.clear();
- m_weaponSet.updateWeaponSet(this);
- m_weaponBonusCondition = 0;
- for (int i = 0; i < WEAPONSLOT_COUNT; ++i)
- m_lastWeaponCondition[i] = WSF_INVALID;
- // If I have a valid team assigned, I can run through my Upgrade modules with his flags
- updateUpgradeModules();
- // emit message announcing object's creation
- TheGameLogic->sendObjectCreated( this );
- //If the player has battle plans (America Strategy Center), then apply those bonuses
- //to this object if applicable. Internally it validates certain kinds of objects.
- const Player* controller = getControllingPlayer();
- if (controller)
- {
- if (!getReceivingDifficultyBonus() && TheScriptEngine->getObjectsShouldReceiveDifficultyBonus())
- {
- setReceivingDifficultyBonus(TRUE);
- }
-
- if (controller->getNumBattlePlansActive() > 0)
- {
- controller->applyBattlePlanBonusesForObject( this );
- }
- }
- //For each special power module that we have, add it's type to the specialpower bits. This is
- //for optimal access later.
- for (BehaviorModule** m = m_behaviors; *m; ++m)
- {
- SpecialPowerModuleInterface* sp = (*m)->getSpecialPower();
- if (!sp)
- continue;
- const SpecialPowerTemplate *spTemplate = sp->getSpecialPowerTemplate();
- if( spTemplate )
- {
- SET_SPECIALPOWERMASK( m_specialPowerBits, spTemplate->getSpecialPowerType() );
- }
- }
- // Kris -- All missiles must be projectiles! This is the perfect place to assert them!
- // srj: yes, but only in debug...
- #if defined(_DEBUG) || defined(_INTERNAL)
- if( !isKindOf( KINDOF_PROJECTILE ) )
- {
- if( isKindOf( KINDOF_SMALL_MISSILE ) || isKindOf( KINDOF_BALLISTIC_MISSILE ) )
- {
- //Warning only...
- DEBUG_CRASH( ("Missile %s must also be a KindOf = PROJECTILE in addition to being either a SMALL_MISSILE or PROJECTILE_MISSILE -- call Kris (36844) for questions!", getTemplate()->getName().str() ) );
- }
- }
- #endif
- if (!isKindOf(KINDOF_PROJECTILE) && !isKindOf(KINDOF_INERT)) {
- // Notify script conditions to update conditions that consider unit counts.
- // We ignore projectiles cause they are frequently created & destroyed, and are not
- // of general interest. Normal unit count tests consider tanks or infantry or planes, etc. jba.
- TheScriptEngine->notifyOfObjectCreationOrDestruction();
- TheGameLogic->updateObjectsChangedTriggerAreas();
- }
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- Object::~Object()
- {
- // tell the AI the building is gone
- /// @todo Generalize the notion of objects entering and leaving the world, so we don't have to special case this
- TheAI->pathfinder()->removeObjectFromPathfindMap( this );
- if (!isKindOf(KINDOF_PROJECTILE) && !isKindOf(KINDOF_INERT)) {
- // Notify script conditions to update conditions that consider unit counts.
- // We ignore projectiles cause they are frequently created & destroyed, and are not
- // of general interest. Normal unit count tests consider tanks or infantry or planes, etc. jba.
- TheGameLogic->updateObjectsChangedTriggerAreas();
- TheScriptEngine->notifyOfObjectCreationOrDestruction();
- }
- //
- // remove from radar before we NULL out the team ... the order of ops are critical here
- // because the radar code will sometimes look at the team info and it is assumed through
- // the team and player code that the team is valid
- //
- if( m_radarData )
- TheRadar->removeObject( this );
- // emit message announcing object's destruction. Again, order is important; we must do this
- // before wiping out the team.
- TheGameLogic->sendObjectDestroyed( this );
- // empty the team
- setTeam( NULL );
- // Object's set of these persist for the life of the object.
- m_partitionLastLook->deleteInstance();
- m_partitionLastLook = NULL;
- m_partitionLastShroud->deleteInstance();
- m_partitionLastShroud = NULL;
- m_partitionLastThreat->deleteInstance();
- m_partitionLastThreat = NULL;
- m_partitionLastValue->deleteInstance();
- m_partitionLastValue = NULL;
- // remove the object from the partition system if present
- if( m_partitionData )
- ThePartitionManager->unRegisterObject( this );
- // if we are in a group, remove us
- if (m_group)
- m_group->remove( this );
-
- // note, do NOT free these, there are just a shadow copy!
- m_ai = NULL;
- m_physics = NULL;
- // delete any modules present
- for (BehaviorModule** b = m_behaviors; *b; ++b)
- {
- (*b)->deleteInstance();
- *b = NULL; // in case other modules call findModule from their dtor!
- }
- delete [] m_behaviors;
- m_behaviors = NULL;
- if( m_experienceTracker )
- m_experienceTracker->deleteInstance();
- m_experienceTracker = NULL;
- // we don't need to delete these, there were deleted on the m_behaviors list
- m_firingTracker = NULL;
- m_repulsorHelper = NULL;
- m_smcHelper = NULL;
- m_wsHelper = NULL;
- m_defectionHelper = NULL;
- // reset id to zero so we never mistaken grab "dead" objects
- m_id = INVALID_ID;
- // Instead of removing it from the named cache, notify the script engine that it has died.
- // The script engine will remove it from the cache if necessary. The script engine needs to take
- // a crack at this in case it is the current "This Object" pointer.
- TheScriptEngine->notifyOfObjectDestruction(this);
- }
- //-------------------------------------------------------------------------------------------------
- void localIsHero( Object *obj, void* userData )
- {
- Bool *hero = (Bool*)userData;
-
- if( obj && obj->isKindOf( KINDOF_HERO ) )
- {
- *hero = TRUE;
- }
- }
- //-------------------------------------------------------------------------------------------------
- Bool Object::isHero() const
- {
- ContainModuleInterface *contain = getContain();
- if( contain )
- {
- Bool heroInside = FALSE;
- contain->iterateContained( localIsHero, (void*)(&heroInside), FALSE );
- if( heroInside )
- {
- return TRUE;
- }
- }
- return isKindOf( KINDOF_HERO );
- }
-
- //-------------------------------------------------------------------------------------------------
- /// this object now contained in "containedBy"
- //-------------------------------------------------------------------------------------------------
- void Object::onContainedBy( Object *containedBy )
- {
- setStatus(OBJECT_STATUS_UNSELECTABLE);
- if (containedBy && containedBy->getContain()->isEnclosingContainerFor(this))
- setStatus(OBJECT_STATUS_MASKED);
- else
- clearStatus(OBJECT_STATUS_MASKED);
- m_containedBy = containedBy;
- m_containedByFrame = TheGameLogic->getFrame();
- }
- //-------------------------------------------------------------------------------------------------
- /// this object no longer contained in "containedBy"
- //-------------------------------------------------------------------------------------------------
- void Object::onRemovedFrom( Object *removedFrom )
- {
- clearStatus(OBJECT_STATUS_UNSELECTABLE);
- clearStatus(OBJECT_STATUS_MASKED);
- m_containedBy = NULL;
- m_containedByFrame = 0;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- Int Object::getTransportSlotCount() const
- {
- Int count = getTemplate()->getRawTransportSlotCount();
- ContainModuleInterface* contain = getContain();
- if ( contain && contain->isSpecialZeroSlotContainer() )
- {
- count = 0;
- const ContainedItemsList* items = contain->getContainedItemsList();
- if (items)
- {
- for (ContainedItemsList::const_iterator it = items->begin(); it != items->end(); ++it)
- {
- count += (*it)->getTransportSlotCount();
- }
- }
- }
- return count;
- }
- //-------------------------------------------------------------------------------------------------
- /** Run from GameLogic::destroyObject */
- //-------------------------------------------------------------------------------------------------
- void Object::onDestroy()
- {
- // This is the old cleanUpContain safeguard. Say goodbye so they don't try to look us up.
- if( m_containedBy && m_containedBy->getContain() )
- {
- m_containedBy->getContain()->removeFromContain( this );
- }
- //
- // run the onDelete on all modules present so they each have an opportunity to cleanup
- // anything they need to ... including talking to any other modules
- //
- for (BehaviorModule** b = m_behaviors; *b; ++b)
- {
- (*b)->onDelete();
- }
- //Have to remove ourself from looking as well. RebuildHoleWorkers definately hit here.
- handlePartitionCellMaintenance();
- } // end onDestroy
- //=============================================================================
- //=============================================================================
- void Object::setGeometryInfo(const GeometryInfo& geom)
- {
- m_geometryInfo = geom;
- if( m_partitionData )
- {
- // if our geometry changes, we unregister and re-register with the partitionmgr
- // so that our size gets updated appropriately. this shouldn't be a problem
- // unless setGeometryInfo gets called frequently. (srj)
- ThePartitionManager->unRegisterObject( this );
- ThePartitionManager->registerObject( this );
- }
- if (m_drawable)
- m_drawable->reactToGeometryChange();
- }
- //=============================================================================
- //=============================================================================
- void Object::setGeometryInfoZ( Real newZ )
- {
- // A Z change only does not need to un/register with the PartitionManager
- m_geometryInfo.setMaxHeightAbovePosition( newZ );
- if (m_drawable)
- m_drawable->reactToGeometryChange();
- }
- //=============================================================================
- void Object::friend_setUndetectedDefector( Bool status )
- {
- if (status)
- m_privateStatus |= UNDETECTED_DEFECTOR;
- else
- m_privateStatus &= ~UNDETECTED_DEFECTOR;
- }
- //=============================================================================
- void Object::restoreOriginalTeam()
- {
- if( m_team == NULL || m_originalTeamName.isEmpty() )
- return;
-
- Team* origTeam = TheTeamFactory->findTeam(m_originalTeamName);
- if (origTeam == NULL)
- {
- DEBUG_CRASH(("Object original team (%s) could not be found or created! (srj)\n",m_originalTeamName.str()));
- return;
- }
- if (m_team == origTeam)
- {
- DEBUG_CRASH(("Object appears to still be on its original team, so why are we attempting to restore it? (srj)\n"));
- return;
- }
- setTeam(origTeam);
- }
- //=============================================================================
- //=============================================================================
- void Object::setTeam( Team *team )
- {
- // In order to prevent spawning useful units for a player after he dies, we
- // just assign objects to the neutral player if we try to misbehave.
- if (team && !team->getControllingPlayer()->isPlayerActive())
- team = ThePlayerList->getNeutralPlayer()->getDefaultTeam();
- setTemporaryTeam(team);
- m_originalTeamName = m_team ? m_team->getName() : AsciiString::TheEmptyString;
- }
- //=============================================================================
- //=============================================================================
- void Object::setTemporaryTeam( Team *team )
- {
- const Bool restoring = false;
- setOrRestoreTeam(team, restoring);
- }
- //=============================================================================
- //=============================================================================
- void Object::setOrRestoreTeam( Team* team, Bool restoring )
- {
- // don't do anything if the team hasn't changed
- if( m_team == team )
- return;
- Team* oldTeam = m_team;
- // Before Switch //////////////////////////
- if (m_team)
- {
- if (m_team->isInList_TeamMemberList(this))
- {
- m_team->removeFrom_TeamMemberList(this);
- m_team->getControllingPlayer()->becomingTeamMember(this, false);
- }
- }
-
- // Switch //////////////////////////
- m_team = team;
- // After Switch //////////////////////////
- if (m_team)
- {
- if (!m_team->isInList_TeamMemberList(this))
- {
- m_team->prependTo_TeamMemberList(this);
- m_team->getControllingPlayer()->becomingTeamMember(this, true);
- }
-
- // now, adjust the attitude of the unit to its new team.
- const TeamPrototype* proto = m_team->getPrototype();
- if (proto && proto->getTemplateInfo())
- {
- AIUpdateInterface *ai = getAIUpdateInterface();
- if (ai)
- {
- ai->setAttitude(proto->getTemplateInfo()->m_initialTeamAttitude);
- if (proto->getAttackPriorityName().isNotEmpty()) {
- AsciiString name = proto->getAttackPriorityName();
- const AttackPriorityInfo *info = TheScriptEngine->getAttackInfo(name);
- if (info && info->getName().isNotEmpty()) {
- ai->setAttackInfo(info);
- }
- }
- }
- }
- // emit message announcing object's new alliance
- Drawable *draw = getDrawable();
- if (draw)
- draw->changedTeam();
- }
- // This can't just go in ::defect, because some things just do setTeam. The act of
- // setting a new team needs to tell the modules and do other important stuff.
- // And it needs to happen after the switch.
- if( oldTeam && team && !restoring )
- onCapture( oldTeam->getControllingPlayer(), team->getControllingPlayer() );
- //
- // the team changed we have a change in priorities on the radar if we are
- // a candidate for the radar as it is
- //
- if( m_radarData )
- {
- // removing it and adding it will cause a resort to happen
- TheRadar->removeObject( this );
- TheRadar->addObject( this );
- }
- // Tell TheInGameUI that the object has changed hands
- Int oldPlayerIndex = (oldTeam)?(oldTeam->getControllingPlayer()->getPlayerIndex()):-1;
- Int newPlayerIndex = (m_team)?(m_team->getControllingPlayer()->getPlayerIndex()):-1;
- if (oldPlayerIndex != newPlayerIndex)
- TheInGameUI->objectChangedTeam(this, oldPlayerIndex, newPlayerIndex);
- }
- //=============================================================================
- void Object::setStatus( ObjectStatusBits bits, Bool set )
- {
- UnsignedInt oldStatus = m_status;
- if (set)
- m_status |= bits;
- else
- m_status &= ~bits;
- if (m_status != oldStatus)
- {
- if (set
- && (bits & OBJECT_STATUS_REPULSOR) != 0
- && m_repulsorHelper != NULL)
- {
- // Damaged repulsable civilians scare (repulse) other civs, but only
- // for a short amount of time... use the repulsor helper to turn off repulsion shortly.
- m_repulsorHelper->sleepUntil(TheGameLogic->getFrame() + 2*LOGICFRAMES_PER_SECOND);
- }
- // when an object's construction status changes, it needs to have its partition data updated,
- // in order to maintain the shroud correctly.
- if ((m_status & OBJECT_STATUS_UNDER_CONSTRUCTION) != (oldStatus & OBJECT_STATUS_UNDER_CONSTRUCTION))
- {
- // CHECK FOR MINES, AND DETONATE THEM NOW
- ObjectIterator *iter =
- ThePartitionManager->iteratePotentialCollisions( getPosition(), getGeometryInfo(), getOrientation() );
- MemoryPoolObjectHolder hold( iter );
- Object *them;
- for( them = iter->first(); them; them = iter->next() )
- {
- if (them->isKindOf( KINDOF_MINE ))
- {
- //DETONATE ANY ENEMY MINES, OR DELETE FRIENDLY ONES
- Relationship r = getRelationship(them);
- if (r == ENEMIES)
- {
- them->kill(); // detonate mine
- }
- else
- {
- TheGameLogic->destroyObject(them);
- }
- }
- }// next object
- if (m_partitionData)
- m_partitionData->makeDirty(true);
- }
- }
- }
- //=============================================================================
- void Object::setScriptStatus( ObjectScriptStatusBit bit, Bool set )
- {
- UnsignedInt oldScriptStatus = m_scriptStatus;
- if( set )
- {
- m_scriptStatus |= bit;
- }
- else
- {
- m_scriptStatus &= ~bit;
- }
- if( m_scriptStatus != oldScriptStatus )
- {
- if( (m_scriptStatus & OBJECT_STATUS_SCRIPT_DISABLED) != (oldScriptStatus & OBJECT_STATUS_SCRIPT_DISABLED) )
- {
- if( m_partitionData )
- {
- // if an object becomes disabled or unpowered, then you have to update its partition data because it will
- // change how far it can see.
- m_partitionData->makeDirty(true);
- }
- if( m_scriptStatus & OBJECT_STATUS_SCRIPT_DISABLED )
- {
- //I am now disabled, so tell the main game engine!
- setDisabled( DISABLED_SCRIPT_DISABLED );
- }
- else
- {
- //I am no longer disabled, so tell the main game engine!
- clearDisabled( DISABLED_SCRIPT_DISABLED );
- }
- }
- if( (m_scriptStatus & OBJECT_STATUS_SCRIPT_UNPOWERED) != (oldScriptStatus & OBJECT_STATUS_SCRIPT_UNPOWERED) )
- {
- if( m_partitionData )
- {
- // if an object becomes disabled or unpowered, then you have to update its partition data because it will
- // change how far it can see.
- m_partitionData->makeDirty(true);
- }
- if( m_scriptStatus & OBJECT_STATUS_SCRIPT_UNPOWERED )
- {
- //I am now underpowered, so tell the main game engine!
- setDisabled( DISABLED_SCRIPT_UNDERPOWERED );
- }
- else
- {
- //I am no longer undperpowered, so tell the main game engine!
- clearDisabled( DISABLED_SCRIPT_UNDERPOWERED );
- }
- }
- }
- }
- //=============================================================================
- Bool Object::canCrushOrSquish(Object *otherObj, CrushSquishTestType testType ) const
- {
- DEBUG_ASSERTCRASH(this, ("null this in canCrushOrSquish"));
- if( !otherObj )
- {
- //Can't crush anything.
- return false;
- }
- if( isDisabledByType( DISABLED_UNMANNED ) )
- {
- //Unmanned vehicles cannot crush troops. This was happening when Jarmen Kell sniped
- //the vehicle and booted the guys out while still moving, as the vehicle is now
- //on a different team.
- return false;
- }
- UnsignedByte crusherLevel = getCrusherLevel();
-
- // order matters: we want to know if I consider it to be an ally, not vice versa
- if( getRelationship( otherObj ) == ALLIES )
- {
- //Friends don't let friends crush friends.
- return false;
- }
- if( !crusherLevel )
- {
- //Can't crush anything!
- return false;
- }
- //Test this case for generic infantry getting squished by vehicles!
- if( testType == TEST_SQUISH_ONLY || testType == TEST_CRUSH_OR_SQUISH )
- {
- //****************************************************************************************
- //NOTE: This section of code is used by the pathfinder to determine if the object should
- // move to the target. I don't think it's the right place to check for this because
- // the semantics check to see if we can squish something -- not approach it. However
- // I'm not moving it for fear of some major breakage! -- KM
- //Bool squisher = crusherLevel > 0;
- //if( !squisher )
- //{
- // Weapon *weapon = getCurrentWeapon();
- // if( weapon && weapon->isContactWeapon() )
- // {
- // squisher = true;
- // }
- //}
- //if( squisher )
- //NOTE2: *** IF YOU REENABLE THIS CODE -- Move the "if( !crusherLevel ) return false" below
- // this squish section.
- //****************************************************************************************
- {
- // See if other is squishable
- static NameKeyType key_squish = NAMEKEY( "SquishCollide" );
- if( otherObj->findModule( key_squish ) )
- {
- return true; // squishable.
- }
- }
- }
- UnsignedByte crushableLevel = otherObj->getCrushableLevel();
- if( testType == TEST_CRUSH_ONLY || testType == TEST_CRUSH_OR_SQUISH )
- {
- if( crusherLevel > crushableLevel )
- {
- return true;
- }
- }
-
- return false;
- }
- //-------------------------------------------------------------------------------------------------
- UnsignedByte Object::getCrusherLevel() const
- {
- return getTemplate()->getCrusherLevel();
- }
- //-------------------------------------------------------------------------------------------------
- UnsignedByte Object::getCrushableLevel() const
- {
- return getTemplate()->getCrushableLevel();
- }
- // ------------------------------------------------------------------------------------------------
- /** Topple an object, if possible */
- // ------------------------------------------------------------------------------------------------
- void Object::topple( const Coord3D *toppleDirection, Real toppleSpeed, UnsignedInt options )
- {
- static NameKeyType key_ToppleUpdate = NAMEKEY("ToppleUpdate");
- ToppleUpdate* toppleUpdate = (ToppleUpdate*)findModule(key_ToppleUpdate);
- if( toppleUpdate && toppleUpdate->isAbleToBeToppled() )
- {
- // apply the topple force
- toppleUpdate->applyTopplingForce( toppleDirection, toppleSpeed, options );
- } // end if
- } // end topple
- //=============================================================================
- void Object::reloadAllAmmo(Bool now)
- {
- m_weaponSet.reloadAllAmmo(this, now);
- }
- //=============================================================================
- Bool Object::isOutOfAmmo() const
- {
- return m_weaponSet.isOutOfAmmo();
- }
- //=============================================================================
- Bool Object::hasAnyWeapon() const
- {
- return m_weaponSet.hasAnyWeapon();
- }
- //=============================================================================
- Bool Object::hasAnyDamageWeapon() const
- {
- //First check to see if we have any weapons -- if not return false.
- if( !m_weaponSet.hasAnyDamageWeapon() )
- {
- return FALSE;
- }
- return TRUE;
- }
- //=============================================================================
- Bool Object::hasWeaponToDealDamageType(DamageType typeToDeal) const
- {
- return m_weaponSet.hasWeaponToDealDamageType(typeToDeal);
- }
- //=============================================================================
- Real Object::getLargestWeaponRange() const
- {
- Real retVal = -1;
- for (Int i = PRIMARY_WEAPON; i < WEAPONSLOT_COUNT; ++i) {
- Weapon* weapon = m_weaponSet.getWeaponInWeaponSlot((WeaponSlotType)i);
- if (!weapon) {
- continue;
- }
- Real tmpVal = weapon->getAttackRange(this);
- if (tmpVal > retVal) {
- retVal = tmpVal;
- }
- }
- return retVal;
- }
- //=============================================================================
- void Object::setFiringConditionForCurrentWeapon() const
- {
- if (m_drawable)
- {
- WeaponSlotType wslot = m_weaponSet.getCurWeaponSlot();
- ModelConditionFlags c = m_weaponSet.getModelConditionForWeaponSlot(wslot, WSF_FIRING);
- m_drawable->clearAndSetModelConditionFlags(s_allWeaponFireFlags[wslot], c);
- }
- }
- //=============================================================================
- void Object::setModelConditionState( ModelConditionFlagType a )
- {
- if (m_drawable)
- {
- m_drawable->setModelConditionState(a);
- }
- }
- //=============================================================================
- void Object::clearModelConditionState( ModelConditionFlagType a )
- {
- if (m_drawable)
- {
- m_drawable->clearModelConditionState(a);
- }
- }
- //=============================================================================
- void Object::clearAndSetModelConditionState( ModelConditionFlagType clr, ModelConditionFlagType set )
- {
- if (m_drawable)
- {
- m_drawable->clearAndSetModelConditionState(clr, set);
- }
- }
- //=============================================================================
- void Object::clearModelConditionFlags( const ModelConditionFlags& clr )
- {
- if (m_drawable)
- {
- m_drawable->clearModelConditionFlags(clr);
- }
- }
- //=============================================================================
- void Object::setModelConditionFlags( const ModelConditionFlags& set )
- {
- if (m_drawable)
- {
- m_drawable->setModelConditionFlags(set);
- }
- }
- //=============================================================================
- void Object::clearAndSetModelConditionFlags( const ModelConditionFlags& clr, const ModelConditionFlags& set )
- {
- if (m_drawable)
- {
- m_drawable->clearAndSetModelConditionFlags(clr, set);
- }
- }
- //=============================================================================
- // Special model states are states that are turned on for a period of time, and
- // turned off automatically -- used for cheer, and scripted special moment
- // animations. Setting a special state will automatically clear any other
- // special states that may be turned on so you can only have one at a time.
- //=============================================================================
- void Object::setSpecialModelConditionState( ModelConditionFlagType set, UnsignedInt frames )
- {
- clearSpecialModelConditionStates();
- setModelConditionState( set );
- if( frames == 0 )
- {
- frames = 1;
- }
- m_smcUntil = TheGameLogic->getFrame() + frames;
- m_smcHelper->sleepUntil(m_smcUntil);
- }
- //=============================================================================
- void Object::clearSpecialModelConditionStates()
- {
- clearModelConditionFlags( MAKE_MODELCONDITION_MASK( MODELCONDITION_SPECIAL_CHEERING ) );
- m_smcUntil = NEVER;
- }
- // Lorenzen has some interest in this, ask before deleting
- //=============================================================================
- //const ModelConditionFlags& Object::getModelConditionFlags() const
- //{
- // if (m_drawable)
- // {
- // return m_drawable->getModelConditionFlags();
- // }
- // else
- // {
- // DEBUG_CRASH(("NULL Drawable at this point, you can't get modelconditionflags now."));
- // static ModelConditionFlags noFlags;
- // return noFlags;
- // }
- //}
- //=============================================================================
- Weapon* Object::getCurrentWeapon(WeaponSlotType* wslot)
- {
- if (!m_weaponSet.hasAnyWeapon())
- return NULL;
- if (wslot)
- *wslot = m_weaponSet.getCurWeaponSlot();
- return m_weaponSet.getCurWeapon();
- }
- //=============================================================================
- const Weapon* Object::getCurrentWeapon(WeaponSlotType* wslot) const
- {
- if (!m_weaponSet.hasAnyWeapon())
- return NULL;
- if (wslot)
- *wslot = m_weaponSet.getCurWeaponSlot();
- return m_weaponSet.getCurWeapon();
- }
- //=============================================================================
- Weapon* Object::findWaypointFollowingCapableWeapon()
- {
- return m_weaponSet.findWaypointFollowingCapableWeapon();
- }
- //=============================================================================
- Bool Object::getAmmoPipShowingInfo(Int& numTotal, Int& numFull) const
- {
- /// @todo srj -- may need to cache this inside weaponset.
- const Weapon* w = m_weaponSet.findAmmoPipShowingWeapon();
- if (w)
- {
- numTotal = w->getClipSize();
- numFull = w->getRemainingAmmo();
- return true;
- }
- else
- {
- return false;
- }
- }
- //=============================================================================
- /*
- NOTE: getAbleToAttackSpecificObject NO LONGER internally calls isAbleToAttack(),
- since that isn't an incredibly fast call, and this is called repeatedly in some inner loops
- where we already know that isAbleToAttack() == true. so you should always
- call isAbleToAttack prior to calling this! (srj)
- */
- CanAttackResult Object::getAbleToAttackSpecificObject( AbleToAttackType t, const Object* target, CommandSourceType commandSource ) const
- {
- // NO! BAD! WRONG!
- // If we can't attack at all, then we cannot attack this
- //if (!isAbleToAttack())
- // return FALSE;
- // Otherwise leave it up to our weapons.
- return m_weaponSet.getAbleToAttackSpecificObject( t, this, target, commandSource );
- }
- //=============================================================================
- //Used for base defenses and otherwise stationary units to see if you can attack a position potentially out of range.
- CanAttackResult Object::getAbleToUseWeaponAgainstTarget( AbleToAttackType attackType, const Object *victim, const Coord3D *pos, CommandSourceType commandSource ) const
- {
- return m_weaponSet.getAbleToUseWeaponAgainstTarget( attackType, this, victim, pos, commandSource );
- }
- //=============================================================================
- Bool Object::chooseBestWeaponForTarget(const Object* target, WeaponChoiceCriteria criteria, CommandSourceType cmdSource )
- {
- return m_weaponSet.chooseBestWeaponForTarget(this, target, criteria, cmdSource );
- }
- //DECLARE_PERF_TIMER(fireCurrentWeapon)
- //=============================================================================
- void Object::fireCurrentWeapon(Object *target)
- {
- //USE_PERF_TIMER(fireCurrentWeapon)
- // victim may have already been destroyed
- if (target == NULL)
- return;
- Weapon* weapon = m_weaponSet.getCurWeapon();
- if (weapon && (weapon->getStatus() == READY_TO_FIRE))
- {
- Bool reloaded = weapon->fireWeapon(this, target);
- DEBUG_ASSERTCRASH(m_firingTracker, ("hey, we are firing but have no firing tracker. this is wrong."));
- if (m_firingTracker)
- m_firingTracker->shotFired(weapon, target->getID());
- if (reloaded)
- releaseWeaponLock(LOCKED_TEMPORARILY); // release any temporary locks.
- friend_setUndetectedDefector( FALSE );// My secret is out
- }
- }
- //=============================================================================
- void Object::fireCurrentWeapon(const Coord3D* pos)
- {
- //USE_PERF_TIMER(fireCurrentWeapon)
- if (pos == NULL)
- return;
- Weapon* weapon = m_weaponSet.getCurWeapon();
- if (weapon && (weapon->getStatus() == READY_TO_FIRE))
- {
- Bool reloaded = weapon->fireWeapon(this, pos);
- DEBUG_ASSERTCRASH(m_firingTracker, ("hey, we are firing but have no firing tracker. this is wrong."));
- if (m_firingTracker)
- m_firingTracker->shotFired(weapon, INVALID_ID);
- if (reloaded)
- releaseWeaponLock(LOCKED_TEMPORARILY); // release any temporary locks.
- friend_setUndetectedDefector( FALSE );// My secret is out
- }
- }
- //=============================================================================
- void Object::preFireCurrentWeapon( const Object *victim )
- {
- Weapon* weapon = m_weaponSet.getCurWeapon();
- //If we are going to be capable of firing our weapon NEXT frame, set the pre-attack
- //up now. This gets called by AIAttackFireWeaponState::onEnter().. but the update happens
- //next frame.
- if (weapon && TheGameLogic->getFrame() + 1 >= weapon->getPossibleNextShotFrame() )
- {
- weapon->preFireWeapon( this, victim );
- friend_setUndetectedDefector( FALSE );// My secret is out
- }
- }
- // ============================================================================
- /** Using the firing tracker, return the frame a shot was last fired on */
- // ============================================================================
- UnsignedInt Object::getLastShotFiredFrame() const
- {
- UnsignedInt recent = 0;
- for (int i = 0; i < WEAPONSLOT_COUNT; ++i)
- {
- const Weapon* w = m_weaponSet.getWeaponInWeaponSlot((WeaponSlotType)i);
- if (w)
- {
- UnsignedInt when = w->getLastShotFrame();
- if (when > recent)
- recent = when;
- }
- }
- return recent;
- }
- // ============================================================================
- /** Get the victim ID we last shot at */
- // ============================================================================
- ObjectID Object::getLastVictimID() const
- {
- return m_firingTracker ? m_firingTracker->getLastShotVictim() : INVALID_ID;
- }
- //=============================================================================
- // Object::getRelationship
- //=============================================================================
- Relationship Object::getRelationship(const Object *that) const
- {
- const Team *myTeam = getTeam();
- if (myTeam && that)
- {
- if (getIsUndetectedDefector())
- {
- return NEUTRAL; // so my AI does not give away my position by auto acquire
- }
- else if (that->getIsUndetectedDefector())
- {
- return ALLIES; // so I treat undetecteddefectors like they were my very own
- }
- else
- {
- return myTeam->getRelationship( that->getTeam() );
- }
- }
- return NEUTRAL;
- }
- //=============================================================================
- // Object::getControllingPlayer
- //=============================================================================
- Player * Object::getControllingPlayer() const
- {
- const Team* myTeam = this->getTeam();
- if (myTeam)
- return myTeam->getControllingPlayer();
- return NULL;
- }
- //=============================================================================
- void Object::setProducer(const Object* obj)
- {
- m_producerID = obj ? obj->getID() : INVALID_ID;
- // seems like a good idea, but is not. (srj)
- // if (obj)
- // m_indicatorColor = obj->m_indicatorColor;
- }
- //=============================================================================
- void Object::setBuilder( const Object *obj )
- {
- m_builderID = obj ? obj->getID() : INVALID_ID;
- }
- //=============================================================================
- void Object::setCustomIndicatorColor(Color c)
- {
- if (m_indicatorColor != c)
- {
- m_indicatorColor = c;
- if (m_drawable)
- m_drawable->changedTeam();
- }
- }
- //=============================================================================
- void Object::removeCustomIndicatorColor()
- {
- setCustomIndicatorColor(0);
- }
- //=============================================================================
- // Object::getIndicatorColor
- //=============================================================================
- Color Object::getIndicatorColor() const
- {
- if (m_indicatorColor == 0)
- {
- const Team *myTeam = getTeam();
- if (myTeam)
- {
- const Player* p = myTeam->getControllingPlayer();
- if (p)
- {
- return p->getPlayerColor();
- }
- }
- return GameMakeColor(0, 0, 0, 255);
- }
- else
- {
- return m_indicatorColor;
- }
- }
- //=============================================================================
- // Object::getNightIndicatorColor - used to make blue/purple easier to see on night models.
- //=============================================================================
- Color Object::getNightIndicatorColor() const
- {
- if (m_indicatorColor == 0)
- {
- const Team *myTeam = getTeam();
- if (myTeam)
- {
- const Player* p = myTeam->getControllingPlayer();
- if (p)
- {
- return p->getPlayerNightColor();
- }
- }
- return GameMakeColor(0, 0, 0, 255);
- }
- else
- {
- return m_indicatorColor;
- }
- }
- //=============================================================================
- // Object::isLocallyControlled
- //=============================================================================
- Bool Object::isLocallyControlled() const
- {
- return getControllingPlayer() == ThePlayerList->getLocalPlayer();
- }
- //=============================================================================
- // Object::isLocallyControlled
- //=============================================================================
- Bool Object::isNeutralControlled() const
- {
- return getControllingPlayer() == ThePlayerList->getNeutralPlayer();
- }
- //-------------------------------------------------------------------------------------------------
- inline Bool isPosDifferent(const Coord3D* a, const Coord3D* b)
- {
- // this is necessary because PhysicsBehavior may generate tiny changes even when
- // "standing still", due to roundoff errors. It's important that we only invalidate
- // the PartitionManager stuff when the pos/orientation really changes (for efficiency purposes)
- // so we must put in some cleverness...
- const Real THRESH = 0.01f;
- if (fabs(a->x - b->x) > THRESH)
- return true;
- if (fabs(a->y - b->y) > THRESH)
- return true;
- if (fabs(a->z - b->z) > THRESH)
- return true;
- return false;
- }
- //-------------------------------------------------------------------------------------------------
- inline Bool isAngleDifferent(Real a, Real b)
- {
- // this is necessary because PhysicsBehavior may generate tiny changes even when
- // "standing still", due to roundoff errors. It's important that we only invalidate
- // the PartitionManager stuff when the pos/orientation really changes (for efficiency purposes)
- // so we must put in some cleverness...
- const Real THRESH = 0.01f; // in radians, this is approx 1/2 degree.
- if (fabs(a - b) > THRESH)
- return true;
- return false;
- }
- //-------------------------------------------------------------------------------------------------
- void Object::reactToTurretChange( WhichTurretType turret, Real oldRotation, Real oldPitch )
- {
- Real currentRotation = 0.0f;
- Real currentPitch = 0.0f;
- if( getAI() )
- {
- getAI()->getTurretRotAndPitch( turret, ¤tRotation, ¤tPitch );
- }
- Bool rotationChange = (currentRotation != oldRotation);
- // Bool pitchChange = (currentPitch != oldPitch);
- if( rotationChange )
- {
- if (getContain())
- getContain()->containReactToTransformChange();
- }
- }
- //-------------------------------------------------------------------------------------------------
- //DECLARE_PERF_TIMER(Object_reactToTransformChange)
- void Object::reactToTransformChange(const Matrix3D* oldMtx, const Coord3D* oldPos, Real oldAngle)
- {
- //USE_PERF_TIMER(Object_reactToTransformChange)
- if(_isnan(getPosition()->x) || _isnan(getPosition()->y) || _isnan(getPosition()->z)) {
- DEBUG_CRASH(("Object pos is nan."));
- TheGameLogic->destroyObject(this);
- }
- if (m_drawable)
- {
- m_drawable->setTransformMatrix( this->getTransformMatrix() );
- }
- Bool posDiff = isPosDifferent(oldPos, getPosition());
- Bool angDiff = isAngleDifferent(oldAngle, getOrientation());
- if (posDiff || angDiff)
- {
- if (m_partitionData)
- m_partitionData->makeDirty(true);
-
- if (getContain())
- getContain()->containReactToTransformChange();
- }
- if (posDiff)
- {
- setTriggerAreaFlagsForChangeInPosition(); // Update for entered/exited
-
- Region3D mapExtent;
- TheTerrainLogic->getExtent(&mapExtent);
- if (mapExtent.isInRegionNoZ(getPosition()))
- m_privateStatus &= ~OFF_MAP;
- else
- m_privateStatus |= OFF_MAP;
- }
- }
- //-------------------------------------------------------------------------------------------------
- ObjectShroudStatus Object::getShroudedStatus(Int playerIndex) const
- {
- if (getTemplate()->isKindOf( KINDOF_ALWAYS_VISIBLE ))
- return OBJECTSHROUD_CLEAR;
- if (m_partitionData)
- return m_partitionData->getShroudedStatus(playerIndex);
- // This can happen for objects removed from the partition system (e.g.,
- // for soldiers that are garrisoned inside a building).
- return OBJECTSHROUD_CLEAR;
- }
- //-------------------------------------------------------------------------------------------------
- /** Something is attempting to damage this object */
- //-------------------------------------------------------------------------------------------------
- void Object::attemptDamage( DamageInfo *damageInfo )
- {
- BodyModuleInterface* body = getBodyModule();
- if (body)
- body->attemptDamage( damageInfo );
-
- /// @todo track damage dealt/attempted
- //
- // if actual damage occurred, and this is an object owned by the local player we
- // might do a radar event for under attack. Note that we do not even try
- // to do radar events for DAMAGE_PENALTY as that damage type is a type of damage
- // that occurs with explicit player knowledge
- //
- if( damageInfo->out.m_actualDamageDealt > 0.0f &&
- damageInfo->in.m_damageType != DAMAGE_PENALTY &&
- damageInfo->in.m_damageType != DAMAGE_HEALING &&
- !BitTest(damageInfo->in.m_sourcePlayerMask, getControllingPlayer()->getPlayerMask()) &&
- m_radarData != NULL &&
- getControllingPlayer() == ThePlayerList->getLocalPlayer() )
- TheRadar->tryUnderAttackEvent( this );
- }
- //-------------------------------------------------------------------------------------------------
- void Object::attemptHealing(Real amount, const Object* source)
- {
- BodyModuleInterface* body = getBodyModule();
- if (body)
- {
- DamageInfo damageInfo;
- damageInfo.in.m_damageType = DAMAGE_HEALING;
- damageInfo.in.m_deathType = DEATH_NONE;
- damageInfo.in.m_sourceID = source ? source->getID() : INVALID_ID;
- damageInfo.in.m_amount = amount;
- body->attemptHealing( &damageInfo );
- }
- }
- ObjectID Object::getSoleHealingBenefactor( void ) const
- {
- UnsignedInt now = TheGameLogic->getFrame();
- if( now > m_soleHealingBenefactorExpirationFrame )
- return INVALID_ID;
- return m_soleHealingBenefactorID;
- }
- Bool Object::attemptHealingFromSoleBenefactor ( Real amount, const Object* source, UnsignedInt duration )
- {///< for the non-stacking healers like ambulance and propaganda
- if( ! source ) // sanity
- return FALSE;
- UnsignedInt now = TheGameLogic->getFrame();
- ObjectID id = source->getID();
- // Either it is ok to accept healing from any who offer or this is my guy, calling again
- if( now > m_soleHealingBenefactorExpirationFrame || m_soleHealingBenefactorID == id )
- {
- m_soleHealingBenefactorID = id;
- m_soleHealingBenefactorExpirationFrame = now + duration;
- BodyModuleInterface* body = getBodyModule();
- if (body)
- {
- DamageInfo damageInfo;
- damageInfo.in.m_damageType = DAMAGE_HEALING;
- damageInfo.in.m_deathType = DEATH_NONE;
- damageInfo.in.m_sourceID = source ? source->getID() : INVALID_ID;
- damageInfo.in.m_amount = amount;
- body->attemptHealing( &damageInfo );
- }
- return TRUE;
- }
- return FALSE;
- }
- //-------------------------------------------------------------------------------------------------
- Real Object::estimateDamage( DamageInfoInput& damageInfo ) const
- {
- BodyModuleInterface* body = getBodyModule();
- if (body)
- return body->estimateDamage( damageInfo );
-
- return 0.0f;
- }
- //-------------------------------------------------------------------------------------------------
- /** Do so much damage to an object that it will certainly die */
- //-------------------------------------------------------------------------------------------------
- void Object::kill()
- {
- DamageInfo damageInfo;
- // Do unmodifiable damage equal to their max health to kill.
- damageInfo.in.m_damageType = DAMAGE_UNRESISTABLE;
- damageInfo.in.m_deathType = DEATH_NORMAL;
- damageInfo.in.m_sourceID = INVALID_ID;
- damageInfo.in.m_amount = getBodyModule()->getMaxHealth();
- attemptDamage( &damageInfo );
- DEBUG_ASSERTCRASH(!damageInfo.out.m_noEffect, ("Attempting to kill an unKillable object (InactiveBody?)\n"));
- } // end kill
- //-------------------------------------------------------------------------------------------------
- /** Restore max health to this Object */
- //-------------------------------------------------------------------------------------------------
- void Object::healCompletely()
- {
- attemptHealing(HUGE_DAMAGE_AMOUNT, NULL);
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void Object::setEffectivelyDead(Bool dead)
- {
- if (dead)
- BitSet(m_privateStatus, EFFECTIVELY_DEAD);
- else
- BitClear(m_privateStatus, EFFECTIVELY_DEAD);
- if (dead)
- {
- if( m_radarData )
- TheRadar->removeObject( this );
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Object::setCaptured(Bool isCaptured)
- {
- if (isCaptured)
- BitSet(m_privateStatus, CAPTURED);
- else
- {
- DEBUG_LOG(("Clearing Captured Status. This should never happen. jkmcd"));
- BitClear(m_privateStatus, CAPTURED);
- }
- // No need to see if we should skip updates, this flag has no effect on skipping updates.
- }
- //-------------------------------------------------------------------------------------------------
- Bool Object::isStructure(void) const
- {
- return isKindOf(KINDOF_STRUCTURE);
- }
- //-------------------------------------------------------------------------------------------------
- Bool Object::isFactionStructure(void) const
- {
- KindOfMaskType bits;
- bits.set(KINDOF_FS_POWER);
- bits.set(KINDOF_FS_FACTORY);
- bits.set(KINDOF_FS_BASE_DEFENSE);
- bits.set(KINDOF_FS_TECHNOLOGY);
- return isAnyKindOf(bits);
- }
- //-------------------------------------------------------------------------------------------------
- Bool Object::isNonFactionStructure(void) const
- {
- return isStructure() && !isFactionStructure();
- }
- //-------------------------------------------------------------------------------------------------
- void Object::setReceivingDifficultyBonus(Bool receive)
- {
- if (receive == m_isReceivingDifficultyBonus) {
- return;
- }
- m_isReceivingDifficultyBonus = receive;
- getControllingPlayer()->friend_applyDifficultyBonusesForObject(this, m_isReceivingDifficultyBonus);
- }
- //-------------------------------------------------------------------------------------------------
- //- DISABLEDNESS STUFF ----------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void Object::setDisabled( DisabledType type )
- {
- setDisabledUntil(type, FOREVER);
- }
- //-------------------------------------------------------------------------------------------------
- void Object::setDisabledUntil( DisabledType type, UnsignedInt frame )
- {
- Bool edgeCase = !isDisabled();
- if( type < 0 || type >= DISABLED_COUNT )
- {
- DEBUG_CRASH( ("Invalid disabled type value %d specified -- doesn't not exist!", type ) );
- return;
- }
- //Handle audio events!
- AudioEventRTS sound;
- if( type == DISABLED_UNMANNED && !isKindOf( KINDOF_DRONE ) )
- {
- //We've been sniped! Play a splatter sound for the pilot losing his face.
- sound = TheAudio->getMiscAudio()->m_splatterVehiclePilotsBrain;
- sound.setPosition( getPosition() );
- TheAudio->addAudioEvent( &sound );
- }
- else if( type == DISABLED_UNDERPOWERED || type == DISABLED_EMP || type == DISABLED_HACKED )
- {
- //We've lost power -- make sure we aren't already out of power as the sounds shouldn't happen
- //if you were already disabled.
- if( !isDisabledByType( DISABLED_UNDERPOWERED ) &&
- !isDisabledByType( DISABLED_EMP ) &&
- !isDisabledByType( DISABLED_HACKED ) )
- {
- if( isKindOf( KINDOF_STRUCTURE ) )
- {
- sound = TheAudio->getMiscAudio()->m_buildingDisabled;
- sound.setPosition( getPosition() );
- TheAudio->addAudioEvent( &sound );
- }
- else if( isKindOf( KINDOF_VEHICLE ) )
- {
- sound = TheAudio->getMiscAudio()->m_vehicleDisabled;
- sound.setPosition( getPosition() );
- TheAudio->addAudioEvent( &sound );
- }
- }
- }
- if( m_disabledTillFrame[ type ] != frame )
- {
- // an edge-test for disabledness, for type. This INCREMENTS m_pauseCount
- // srj sez: HELD nevers disables special powers.
- if ( type != DISABLED_HELD && !isDisabledByType( type ) )
- pauseAllSpecialPowers( TRUE );
-
- m_disabledTillFrame[ type ] = frame;
- m_disabledMask.set( type, frame > TheGameLogic->getFrame() );
- if( m_drawable )
- {
- if( isDisabled() )
- {
- // Held does not tint anybody. If we are multiply disabled, the other setting will hit the tint,
- // and in clear, only-held and not-disabled are both causes to untint.
- // Doh. Also shouldn't be tinting when disabled by scripting.
- // Doh^2. Also shouldn't be CLEARING tinting if we're disabling by held or script disabledness
- // Doh^3. Unmanned is no tint too
- if( type != DISABLED_HELD && type != DISABLED_SCRIPT_DISABLED && type != DISABLED_UNMANNED )
- {
- m_drawable->setTintStatus( TINT_STATUS_DISABLED );
- }
- }
- }
- ContainModuleInterface *contain = getContain();
- if ( contain )
- {
- Object *rider = (Object*)contain->friend_getRider();
- if ( rider )
- {
- rider->setDisabledUntil(type, frame);
- }
- }
- }
- if( type == DISABLED_UNMANNED && !isKindOf( KINDOF_DRONE ) )
- {
- //strange but true: If I am a carbomb,
- //my driver actually has a dead-man's
- //trigger for my dynamite...
- //If he gets sniped, I blow up! Wheeee!
- WeaponSetFlags flags;
- flags.set( WEAPONSET_CARBOMB );
- const WeaponTemplateSet* set = getTemplate()->findWeaponTemplateSet( flags );
- if( set && set->testWeaponSetFlag( WEAPONSET_CARBOMB ) )
- {
- Object* sniper = TheGameLogic->findObjectByID( getBodyModule()->getLastDamageInfo()->in.m_sourceID );
- if ( sniper )
- sniper->scoreTheKill( this );
-
- kill();
- }
- else
- {
- //This vehicle's pilot has been sniped, so we want to clear the veterancy rating (if any)
- ExperienceTracker *xpTracker = getExperienceTracker();
- if( xpTracker )
- {
- xpTracker->setExperienceAndLevel( 0 );
- }
- //Not only that, but it also loses any healing bonuses it may have earned in its prior life
- {
- static const NameKeyType key_AutoHealBehavior = NAMEKEY("AutoHealBehavior");
- AutoHealBehavior* autoHeal = (AutoHealBehavior*)(findUpdateModule( key_AutoHealBehavior ));
- if (autoHeal)
- autoHeal->undoUpgrade();
-
- }
- }
-
- }
-
- // This will only be called if we were NOT disabled before coming into this function.
- if (edgeCase) {
- onDisabledEdge(true);
- }
- }
- //-------------------------------------------------------------------------------------------------
- Bool Object::clearDisabled( DisabledType type )
- {
- if( type < 0 || type >= DISABLED_COUNT )
- {
- DEBUG_CRASH( ("Invalid disabled type value %d specified -- doesn't not exist!", type ) );
- return FALSE;
- }
- if (!isDisabledByType(type)) {
- return FALSE;
- }
- if( type == DISABLED_UNDERPOWERED || type == DISABLED_EMP || type == DISABLED_HACKED )
- {
- //We've regained power-- make sure we aren't still disabled by another type.
- AudioEventRTS sound;
- if( (!isDisabledByType( DISABLED_UNDERPOWERED ) || type == DISABLED_UNDERPOWERED ) &&
- (!isDisabledByType( DISABLED_EMP ) || type == DISABLED_EMP ) &&
- (!isDisabledByType( DISABLED_HACKED ) || type == DISABLED_HACKED ) )
- {
- if( isKindOf( KINDOF_STRUCTURE ) )
- {
- sound = TheAudio->getMiscAudio()->m_buildingReenabled;
- sound.setPosition( getPosition() );
- TheAudio->addAudioEvent( &sound );
- }
- else if( isKindOf( KINDOF_VEHICLE ) )
- {
- sound = TheAudio->getMiscAudio()->m_vehicleReenabled;
- sound.setPosition( getPosition() );
- TheAudio->addAudioEvent( &sound );
- }
- }
- }
- // an edge-test for disabledness, for type. This DECREMENTS m_pauseCount
- // srj sez: HELD nevers disables special powers.
- if ( type != DISABLED_HELD && isDisabledByType( type ) )
- pauseAllSpecialPowers( FALSE );
- ContainModuleInterface *contain = getContain();
- if ( contain )
- {
- // We explicitly pass stuff in up in the set, so we need to turn it off if it is a forever type
- Object *rider = (Object*)contain->friend_getRider();
- if( rider && (m_disabledTillFrame[ type ] == FOREVER) )
- {
- rider->clearDisabled(type);
- }
- }
- m_disabledTillFrame[ type ] = NEVER;
- m_disabledMask.set( type, 0 );
- DisabledMaskType exceptions;
- exceptions.set(DISABLED_HELD);
- exceptions.set(DISABLED_SCRIPT_DISABLED);
- exceptions.set(DISABLED_UNMANNED);
- // to clarify, if I am NOT disabled by anything other than DISABLED_HELD, or DISABLED_SCRIPT_DISABLED
- if( !isDisabled() || getDisabledFlags().countInverseIntersection( exceptions ) == 0 )
- {
- if (m_drawable)
- m_drawable->clearTintStatus( TINT_STATUS_DISABLED );
- }
- checkDisabledStatus();// in case we just edged
- // if we're no longer disabled by anything, then call the edge function.
- if (!isDisabled()) {
- onDisabledEdge(false);
- }
- return TRUE;
- }
- //-------------------------------------------------------------------------------------------------
- //Checks any timers and clears disabled statii that have expired.
- //-------------------------------------------------------------------------------------------------
- void Object::checkDisabledStatus()
- {
- UnsignedInt now = TheGameLogic->getFrame();
- for( int i = 0; i < DISABLED_COUNT; i++ )
- {
- DisabledType type = (DisabledType)i;
- if( isDisabledByType( type ) )
- {
- if ( now >= m_disabledTillFrame[ i ] )
- {
- clearDisabled( type ); // This will also DECREMENT m_pauseCount in all specialpowers
- m_disabledMask.set( type, 0 );
- }
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Object::pauseAllSpecialPowers( const Bool disabling ) const
- {
- for (BehaviorModule** m = m_behaviors; *m; ++m)
- {
- SpecialPowerModuleInterface* sp = (*m)->getSpecialPower();
- if (!sp)
- continue;
- sp->pauseCountdown( disabling );// So it will pause if we are disabling.
- }
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- /** Clear the previous entered/exited flags. */
- //-------------------------------------------------------------------------------------------------
- void Object::updateTriggerAreaFlags()
- {
- Int j = 0;
- // Update the flags, and remove any trigger areas that this object isn't inside.
- for (Int i=0; i<m_numTriggerAreasActive; i++)
- {
- if (!m_triggerInfo[j].isInside)
- continue;
- m_triggerInfo[j].entered = false;
- m_triggerInfo[j].exited = false;
- m_triggerInfo[j].isInside = m_triggerInfo[i].isInside;
- m_triggerInfo[j].pTrigger = m_triggerInfo[i].pTrigger;
- j++;
- }
- m_numTriggerAreasActive = j;
- }
- //-------------------------------------------------------------------------------------------------
- void Object::onCollide( Object *other, const Coord3D *loc, const Coord3D *normal )
- {
- for (BehaviorModule** m = m_behaviors; *m; ++m)
- {
- CollideModuleInterface* collide = (*m)->getCollide();
- if (!collide)
- continue;
- // check each time thru the loop, in case a collide module sets it
- if (getStatusBits() & OBJECT_STATUS_NO_COLLISIONS)
- {
- #ifdef DEBUG_CRC
- //DEBUG_LOG(("Object::onCollide() - OBJECT_STATUS_NO_COLLISIONS set\n"));
- #endif
- break;
- }
- #ifdef DEBUG_CRC
- //DEBUG_LOG(("Object::onCollide() - calling collide module\n"));
- #endif
- collide->onCollide(other, loc, normal);
- }
- }
- //-------------------------------------------------------------------------------------------------
- Bool Object::isSalvageCrate() const
- {
- for( BehaviorModule** m = m_behaviors; *m; ++m )
- {
- CollideModuleInterface* collide = (*m)->getCollide();
- if( collide && collide->isSalvageCrateCollide() )
- {
- return true;
- }
- }
- return false;
- }
- //-------------------------------------------------------------------------------------------------
- /**
- Our owning player is telling us to recheck our UpgradeModules, as an upgrade has completed
- */
- void Object::updateUpgradeModules()
- {
- Int64 playerMask = getControllingPlayer()->getCompletedUpgradeMask();
- Int64 objectMask = getObjectCompletedUpgradeMask();
- Int64 maskToCheck = playerMask | objectMask;
- // We need to add in all of the already owned upgrades to handle "AND" requiring upgrades.
- // We combine all the masks in case someone has a Object AND Player combination
- for (BehaviorModule** module = m_behaviors; *module; ++module)
- {
- UpgradeModuleInterface* upgrade = (*module)->getUpgrade();
- if (!upgrade)
- continue;
- if( !upgrade->isAlreadyUpgraded() )
- {
- upgrade->attemptUpgrade( maskToCheck );
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- //This function sucks.
- //It was added for objects that can disguise as other objects and contain upgraded subobject overrides.
- //A concrete example is the bomb truck. Different payloads are displayed based on which upgrades have been
- //made. When the bomb truck disguises as something else, these subobjects are lost because the vector is
- //stored in W3DDrawModule. When we revert back to the original bomb truck, we call this function to
- //recalculate those upgraded subobjects.
- //-------------------------------------------------------------------------------------------------
- void Object::forceRefreshSubObjectUpgradeStatus()
- {
- for (BehaviorModule** module = m_behaviors; *module; ++module)
- {
- UpgradeModuleInterface* upgrade = (*module)->getUpgrade();
- if (!upgrade)
- continue;
- if( upgrade->isSubObjectsUpgrade() )
- {
- upgrade->forceRefreshUpgrade();
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- /** Returns whether an object entered or exited an area. */
- //-------------------------------------------------------------------------------------------------
- Bool Object::didEnterOrExit() const
- {
- if (isKindOf(KINDOF_INERT)) {
- return FALSE;
- }
- // note that this needs to return true if we
- // entered or exited on the current frame OR
- // the previous frame... since the current execution
- // order is ScriptEngine, then ObjectUpdates,
- // enter/exits detected in ObjectUpdate on frame N
- // won't be noticed by the ScriptEngine till frame N+1.
- UnsignedInt now = TheGameLogic->getFrame();
- return m_enteredOrExitedFrame == now || m_enteredOrExitedFrame == now - 1;
- }
- //-------------------------------------------------------------------------------------------------
- /** Returns whether an object entered an area. */
- //-------------------------------------------------------------------------------------------------
- Bool Object::didEnter(const PolygonTrigger *pTrigger) const
- {
- if (!didEnterOrExit())
- return false;
- DEBUG_ASSERTCRASH(!isKindOf(KINDOF_INERT), ("Asking whether an inert object entered or exited. This is invalid.\n"));
- for (Int i=0; i<m_numTriggerAreasActive; i++)
- {
- if (m_triggerInfo[i].entered && m_triggerInfo[i].pTrigger == pTrigger)
- return true;
- }
- return false;
- }
- //-------------------------------------------------------------------------------------------------
- /** Returns whether an object entered an area. */
- //-------------------------------------------------------------------------------------------------
- Bool Object::didExit(const PolygonTrigger *pTrigger) const
- {
- if (!didEnterOrExit())
- return false;
- DEBUG_ASSERTCRASH(!isKindOf(KINDOF_INERT), ("Asking whether an inert object entered or exited. This is invalid.\n"));
- for (Int i=0; i<m_numTriggerAreasActive; i++)
- {
- if (m_triggerInfo[i].exited && m_triggerInfo[i].pTrigger == pTrigger)
- return true;
- }
- return false;
- }
- //-------------------------------------------------------------------------------------------------
- /** Returns whether an object is inside an area. */
- //-------------------------------------------------------------------------------------------------
- Bool Object::isInside(const PolygonTrigger *pTrigger) const
- {
- DEBUG_ASSERTCRASH(!isKindOf(KINDOF_INERT), ("Asking whether an inert is inside a trigger area. This is invalid.\n"));
- for (Int i=0; i<m_numTriggerAreasActive; i++)
- {
- if (m_triggerInfo[i].isInside && m_triggerInfo[i].pTrigger == pTrigger)
- return true;
- }
- return false;
- }
- // ------------------------------------------------------------------------------------------------
- /** Get production exit interface in object is present */
- // ------------------------------------------------------------------------------------------------
- ExitInterface *Object::getObjectExitInterface() const
- {
- ExitInterface *exitInterface = NULL;
- for( BehaviorModule **umod = m_behaviors; *umod; ++umod )
- {
- if( (exitInterface = (*umod)->getUpdateExitInterface()) != NULL )
- break;
- }
- // If you don't have a fancy one, you may have one from your contain module,
- // since if you can contain something, they will need to get out.
- if( exitInterface == NULL )
- {
- ContainModuleInterface *cmod = getContain();
- if( cmod )
- {
- exitInterface = cmod->getContainExitInterface();
- }
- }
- return exitInterface;
- } // end getObjectExitInterface
- //-------------------------------------------------------------------------------------------------
- /** Checks the object against trigger areas when the position changes. */
- //-------------------------------------------------------------------------------------------------
- void Object::setTriggerAreaFlagsForChangeInPosition()
- {
- // projectiles cannot trigger areas. (jkmcd)
- // neither can inert objects, like the radar ping, etc. (jkmcd)
- if (isKindOf(KINDOF_PROJECTILE) || isKindOf(KINDOF_INERT))
- return;
- ICoord3D iPos;
- Coord3D pos = *getPosition();
- iPos.x = REAL_TO_INT(pos.x);
- iPos.y = REAL_TO_INT(pos.y);
- iPos.z = 0; // Trigger areas compare on xy only.
- if (m_iPos.x == iPos.x && m_iPos.y == iPos.y)
- {
- return; // didn't move enough to change integer position.
- }
- if (getAIUpdateInterface())
- {
- TheAI->pathfinder()->updatePos(this, getPosition());
- }
- UnsignedInt now = TheGameLogic->getFrame();
- if (m_enteredOrExitedFrame != 0 && m_enteredOrExitedFrame != now)
- updateTriggerAreaFlags();
- // Check for exited.
- Int i;
- for (i=0; i<m_numTriggerAreasActive; i++)
- {
- if (!m_triggerInfo[i].pTrigger->pointInTrigger(m_iPos))
- {
- m_triggerInfo[i].isInside = false;
- m_triggerInfo[i].exited = true;
- m_enteredOrExitedFrame = now;
- if (m_team)
- m_team->setEnteredExited();
- TheGameLogic->updateObjectsChangedTriggerAreas();
- #ifdef _DEBUG
- //TheScriptEngine->AppendDebugMessage("Object exited.", false);
- #endif
- }
- }
- m_iPos = iPos;
- for (const PolygonTrigger *pTrig = PolygonTrigger::getFirstPolygonTrigger(); pTrig; pTrig = pTrig->getNext())
- {
- Bool skip = false;
- for (i = 0; i < m_numTriggerAreasActive; i++)
- {
- if (m_triggerInfo[i].pTrigger == pTrig)
- {
- // Already handled this one in the check for exited above.
- skip = true;
- break;
- }
- }
- if (skip)
- continue;
- if (pTrig->pointInTrigger(m_iPos))
- {
- if (m_numTriggerAreasActive < MAX_TRIGGER_AREA_INFOS)
- {
- m_triggerInfo[m_numTriggerAreasActive].isInside = true;
- m_triggerInfo[m_numTriggerAreasActive].entered = true;
- m_triggerInfo[m_numTriggerAreasActive].exited = false;
- m_triggerInfo[m_numTriggerAreasActive].pTrigger = pTrig;
- m_enteredOrExitedFrame = now;
- if (m_team)
- m_team->setEnteredExited();
- TheGameLogic->updateObjectsChangedTriggerAreas();
- ++m_numTriggerAreasActive;
- #ifdef _DEBUG
- //TheScriptEngine->AppendDebugMessage("Object entered.", false);
- #endif
- }
- else
- {
- // Shouldn't happen.
- static Bool didWarn = false;
- if (!didWarn)
- {
- didWarn = true;
- TheScriptEngine->AppendDebugMessage("***WARNING - Too many nested trigger areas. ***", true);
- }
- }
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- Bool Object::isInList(Object **pListHead) const
- {
- Bool result = m_prev || m_next || *pListHead == this;
- #ifdef INTENSE_DEBUG
- Bool found = false;
- for (Object* o = *pListHead; o; o = o->m_next)
- {
- if (o == this)
- {
- found = true;
- break;
- }
- }
- DEBUG_ASSERTCRASH(found==result,("inconsistent links in Object::isInList"));
- #endif
- return result;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void Object::prependToList(Object **pListHead)
- {
- DEBUG_ASSERTCRASH(!isInList(pListHead), ("obj is already in a list"));
- m_prev = NULL;
- m_next = *pListHead;
- if (*pListHead)
- (*pListHead)->m_prev = this;
- *pListHead = this;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void Object::setLayer(PathfindLayerEnum layer)
- {
- if (layer!=m_layer) {
- #define no_SET_LAYER_INTENSE_DEBUG
- #ifdef SET_LAYER_INTENSE_DEBUG
- DEBUG_LOG(("Changing layer from %d to %d\n", m_layer, layer));
- if (m_layer != LAYER_GROUND) {
- if (TheTerrainLogic->objectInteractsWithBridgeLayer(this, m_layer)) {
- DEBUG_CRASH(("Probably shouldn't be chaging layer. jba."));
- }
- }
- #endif
- TheAI->pathfinder()->removePos(this);
- m_layer = layer;
- TheAI->pathfinder()->updatePos(this, getPosition());
- }
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void Object::setDestinationLayer(PathfindLayerEnum layer)
- {
- if (layer!=m_destinationLayer) {
- m_destinationLayer = layer;
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** Set unique ID */
- // ------------------------------------------------------------------------------------------------
- void Object::setID( ObjectID id )
- {
- // sanity
- DEBUG_ASSERTCRASH( id != INVALID_ID, ("Object::setID - Invalid id\n") );
- // if id hasn't changed do nothing
- if( m_id == id )
- return;
-
- // remove this objects previous id from the lookup table
- TheGameLogic->removeObjectFromLookupTable( this );
- // assign new id
- m_id = id;
- // add new id to lookup table
- TheGameLogic->addObjectToLookupTable( this );
- } // end setID
- // ------------------------------------------------------------------------------------------------
- Real Object::calculateHeightAboveTerrain(void) const
- {
- const Coord3D* pos = getPosition();
- Real terrainZ = TheTerrainLogic->getLayerHeight( pos->x, pos->y, m_layer );
- Real myZ = pos->z;
- return myZ - terrainZ;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void Object::removeFromList(Object **pListHead)
- {
- if (m_next)
- m_next->m_prev = m_prev;
- if (m_prev)
- m_prev->m_next = m_next;
- else
- *pListHead = m_next;
- m_prev = NULL;
- m_next = NULL;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void Object::friend_prepareForMapBoundaryAdjust(void)
- {
- // NOTE - DO NOT remove from pathfind map. jba.
- // NO NO. jba. TheAI->pathfinder()->removeObjectFromPathfindMap( this );
- // remove from the radar, remove from the partition manager
- TheRadar->removeObject(this);
- ThePartitionManager->unRegisterObject(this);
- // The whole PartitionManager and all of the Looker data is about to be blown away,
- // so forget what I think I have done
- m_partitionLastLook->reset();
- m_partitionLastShroud->reset();
- m_partitionLastThreat->reset();
- m_partitionLastValue->reset();
-
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void Object::friend_notifyOfNewMapBoundary(void)
- {
- ThePartitionManager->registerObject(this);
- TheRadar->addObject(this);
- TheAI->pathfinder()->addObjectToPathfindMap( this );
- // Now that the PartitionManager has finished its reset, we need to relook
- handlePartitionCellMaintenance();
- Region3D mapExtent;
- TheTerrainLogic->getExtent(&mapExtent);
- if (mapExtent.isInRegionNoZ(getPosition()))
- m_privateStatus &= ~OFF_MAP;
- else
- m_privateStatus |= OFF_MAP;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void Object::calcNaturalRallyPoint(Coord2D *pt)
- {
- const Matrix3D *transform = getTransformMatrix();
- Vector3 v;
- //
- // get the natural rally point from the template, this coord is in model space relative
- // to the model (0,0,0)
- //
- /*
- const Coord3D *naturalRallyPoint;
- naturalRallyPoint = m_template->getNaturalRallyPoint();
- v.X = naturalRallyPoint->x;
- v.Y = naturalRallyPoint->y;
- v.Z = naturalRallyPoint->z;
- */
- v.Set( 0, 0, 0 );
- // transform the point into world space
- transform->Transform_Vector( *transform, v, &v );
- // we're only concerned with the 2D elements for now
- pt->x = v.X;
- pt->y = v.Y;
- }
- //-------------------------------------------------------------------------------------------------
- Module* Object::findModule(NameKeyType key) const
- {
- Module* m = NULL;
- for (BehaviorModule** b = m_behaviors; *b; ++b)
- {
- if ((*b)->getModuleNameKey() == key)
- {
- #ifdef INTENSE_DEBUG
- if (m == NULL)
- {
- m = *b;
- }
- else
- {
- DEBUG_CRASH(("Duplicate modules found for name %s!\n",TheNameKeyGenerator->keyToName(key).str()));
- }
- #else
- m = *b;
- break;
- #endif
- }
- }
- return m;
- }
- //-------------------------------------------------------------------------------------------------
- /**
- * Returns true if object is currently able to move.
- */
- Bool Object::isMobile() const
- {
- if (isKindOf(KINDOF_IMMOBILE))
- return false;
- if( isDisabled() )
- return false;
- return true;
- }
- //-------------------------------------------------------------------------------------------------
- void Object::scoreTheKill( const Object *victim )
- {
- // Do stuff that has nothing to do with experience points here, like tell our Player we killed something
- /// @todo Multiplayer score hook location?
- Player* victimController = victim->getControllingPlayer();
- // if the other player is not a playable side (i.e. they are civilian, observer, whatever)
- // we shouldn't count the kill.
- if (victimController->isPlayableSide() == FALSE)
- {
- return;
- }
-
- if ( victim->isKindOf( KINDOF_IGNORED_IN_GUI ) )
- return;
- Player* controller = getControllingPlayer();
- if (victimController)
- {
- victimController->getScoreKeeper()->addObjectLost(victim);
- }
- Relationship r = getRelationship(victim);
- if (r != ENEMIES)
- return;
- // Don't count kills that I do on my own buildings or units, cause thats just silly.
- if (controller == victimController)
- {
- return;
- }
- if (controller)
- {
- controller->getScoreKeeper()->addObjectDestroyed(victim);
- controller->addSkillPointsForKill(this, victim);
- controller->doBountyForKill(this, victim);
- }
- // Now handle experience, if we can gain any
- if (m_experienceTracker && m_experienceTracker->isAcceptingExperiencePoints())
- {
- // srj sez: per dustin, no experience (et al) for killing things under construction.
- if (!victim->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION))
- {
- Int experienceValue = victim->getExperienceTracker()->getExperienceValue( this );
- getExperienceTracker()->addExperiencePoints( experienceValue );
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- VeterancyLevel Object::getVeterancyLevel() const
- {
- return m_experienceTracker ? m_experienceTracker->getVeterancyLevel() : LEVEL_REGULAR;
- }
- //-------------------------------------------------------------------------------------------------
- void Object::friend_bindToDrawable( Drawable *draw )
- {
- m_drawable = draw;
- if (m_drawable)
- {
- ModelConditionFlags set;
- ModelConditionFlags clr;
- for (int i = 0; i < WEAPONSET_COUNT; ++i)
- {
- ModelConditionFlagType mcs = TheWeaponSetTypeToModelConditionTypeMap[i];
- if (m_curWeaponSetFlags.test(i))
- set.set(mcs);
- else
- clr.set(mcs);
- }
- if (TheGlobalData)
- {
- if (TheGlobalData->m_forceModelsToFollowTimeOfDay)
- {
- set.set(MODELCONDITION_NIGHT, (TheGlobalData->m_timeOfDay == TIME_OF_DAY_NIGHT) ? 1 : 0);
- }
- if (TheGlobalData->m_forceModelsToFollowWeather)
- {
- set.set(MODELCONDITION_SNOW, (TheGlobalData->m_weather == WEATHER_SNOWY) ? 1 : 0);
- }
- }
- m_drawable->clearAndSetModelConditionFlags(clr, set);
- }
- for (BehaviorModule** b = m_behaviors; *b; ++b)
- {
- (*b)->onDrawableBoundToObject();
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Object::setSelectable(Bool selectable)
- {
- m_isSelectable = selectable;
- if (m_drawable)
- {
- m_drawable->setSelectable(selectable);
- }
- }
- //-------------------------------------------------------------------------------------------------
- Bool Object::isSelectable() const
- {
- return getTemplate()->isKindOf(KINDOF_ALWAYS_SELECTABLE)
- || (m_isSelectable
- && !testStatus(OBJECT_STATUS_UNSELECTABLE)
- && !isEffectivelyDead()
- && !getTemplate()->isKindOf(KINDOF_DRONE)//Most drones are unselectable from being slaved, but the SpyDrone needs help
- );
- }
- //-------------------------------------------------------------------------------------------------
- Bool Object::isMassSelectable() const
- {
- return isSelectable() && !isKindOf(KINDOF_STRUCTURE);
- }
- //-------------------------------------------------------------------------------------------------
- void Object::setWeaponSetFlag(WeaponSetType wst)
- {
- m_curWeaponSetFlags.set(wst);
- m_weaponSet.updateWeaponSet(this);
- if (m_drawable)
- {
- m_drawable->setModelConditionState(TheWeaponSetTypeToModelConditionTypeMap[wst]);
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Object::clearWeaponSetFlag(WeaponSetType wst)
- {
- m_curWeaponSetFlags.set(wst, 0);
- m_weaponSet.updateWeaponSet(this);
- if (m_drawable)
- {
- m_drawable->clearModelConditionState(TheWeaponSetTypeToModelConditionTypeMap[wst]);
- }
- }
- //-------------------------------------------------------------------------------------------------
- Bool Object::hasSpecialPower( SpecialPowerType type ) const
- {
- return TEST_SPECIALPOWERMASK( m_specialPowerBits, type );
- }
- //-------------------------------------------------------------------------------------------------
- void Object::onVeterancyLevelChanged( VeterancyLevel oldLevel, VeterancyLevel newLevel )
- {
- updateUpgradeModules();
- const UpgradeTemplate* up = TheUpgradeCenter->findVeterancyUpgrade(newLevel);
- if (up)
- giveUpgrade(up);
- BodyModuleInterface* body = getBodyModule();
- if (body)
- body->onVeterancyLevelChanged(oldLevel, newLevel);
-
-
- Bool hideAnimationForStealth = ( ! isLocallyControlled() && testStatus(OBJECT_STATUS_STEALTHED));
- Bool doAnimation = ( ! hideAnimationForStealth
- && (newLevel > oldLevel)
- && ( ! isKindOf(KINDOF_IGNORED_IN_GUI))); //First, we plan to do the animation if the level went up
- switch (newLevel)
- {
- case LEVEL_REGULAR:
- clearWeaponSetFlag(WEAPONSET_VETERAN);
- clearWeaponSetFlag(WEAPONSET_ELITE);
- clearWeaponSetFlag(WEAPONSET_HERO);
- clearWeaponBonusCondition(WEAPONBONUSCONDITION_VETERAN);
- clearWeaponBonusCondition(WEAPONBONUSCONDITION_ELITE);
- clearWeaponBonusCondition(WEAPONBONUSCONDITION_HERO);
- doAnimation = FALSE;//... but not if somehow up to Regular
- break;
- case LEVEL_VETERAN:
- setWeaponSetFlag(WEAPONSET_VETERAN);
- clearWeaponSetFlag(WEAPONSET_ELITE);
- clearWeaponSetFlag(WEAPONSET_HERO);
- setWeaponBonusCondition(WEAPONBONUSCONDITION_VETERAN);
- clearWeaponBonusCondition(WEAPONBONUSCONDITION_ELITE);
- clearWeaponBonusCondition(WEAPONBONUSCONDITION_HERO);
- break;
- case LEVEL_ELITE:
- clearWeaponSetFlag(WEAPONSET_VETERAN);
- setWeaponSetFlag(WEAPONSET_ELITE);
- clearWeaponSetFlag(WEAPONSET_HERO);
- clearWeaponBonusCondition(WEAPONBONUSCONDITION_VETERAN);
- setWeaponBonusCondition(WEAPONBONUSCONDITION_ELITE);
- clearWeaponBonusCondition(WEAPONBONUSCONDITION_HERO);
- break;
- case LEVEL_HEROIC:
- clearWeaponSetFlag(WEAPONSET_VETERAN);
- clearWeaponSetFlag(WEAPONSET_ELITE);
- setWeaponSetFlag(WEAPONSET_HERO);
- clearWeaponBonusCondition(WEAPONBONUSCONDITION_VETERAN);
- clearWeaponBonusCondition(WEAPONBONUSCONDITION_ELITE);
- setWeaponBonusCondition(WEAPONBONUSCONDITION_HERO);
- break;
- }
- if( doAnimation && TheGameLogic->getDrawIconUI() )
- {
- if( TheAnim2DCollection && TheGlobalData->m_levelGainAnimationName.isEmpty() == FALSE )
- {
- Anim2DTemplate *animTemplate = TheAnim2DCollection->findTemplate( TheGlobalData->m_levelGainAnimationName );
- Coord3D pos = *getPosition();
- pos.add(&m_healthBoxOffset);
- TheInGameUI->addWorldAnimation( animTemplate,
- &pos,
- WORLD_ANIM_FADE_ON_EXPIRE,
- TheGlobalData->m_levelGainAnimationDisplayTimeInSeconds,
- TheGlobalData->m_levelGainAnimationZRisePerSecond);
- }
- AudioEventRTS soundToPlay = TheAudio->getMiscAudio()->m_unitPromoted;
- soundToPlay.setObjectID( getID() );
- TheAudio->addAudioEvent( &soundToPlay );
- }
- }
- //-------------------------------------------------------------------------------------------------
- /**
- * Returns true if object currently has some kind of attack capability
- */
- Bool Object::isAbleToAttack() const
- {
- //******************************************************
- //********* AUTOMATICALLY FALSE CONDITIONS *************
- //******************************************************
- // For things that may or may not be able to normally attack, but are under a status condition
- if (getStatusBits() & OBJECT_STATUS_NO_ATTACK)
- return false;
- // if we're contained within a transport we cannot attack unless it specifically allows us
- const Object *containedBy = getContainedBy();
- DEBUG_ASSERTCRASH( (containedBy == NULL) || (containedBy->getContain() != NULL), ("A %s thinks they are contained by something with no contain module!", getTemplate()->getName().str() ) );
- if( containedBy && containedBy->getContain() && !containedBy->getContain()->isPassengerAllowedToFire() )
- return false;
-
- // We can't fire if under construction
- if( testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION) )
- return false;
- // or being sold
- if( testStatus(OBJECT_STATUS_SOLD) )
- return false;
- //We can't fire if we, as a portable structure, are aptly disabled
- if ( isKindOf( KINDOF_PORTABLE_STRUCTURE ) || isKindOf( KINDOF_SPAWNS_ARE_THE_WEAPONS ))
- {
- if( isDisabledByType( DISABLED_HACKED ) || isDisabledByType( DISABLED_EMP ) )
- return false;
- }
- //We can't fire if all our weapons are disabled!
- //Currently, only turreted weapons can be disabled.
- //ONLY DO THIS CHECK IF OUR UNIT DOESN'T HAVE THE
- //KINDOF_CAN_ATTACK flag... nuke cannons have disabled
- //turrets when not deployed, and need to be able to attack to deploy!
- //Strategy centers can't attack when bombardment isn't active!
- Bool anyEnabled = FALSE;
- Bool anyWeapon = FALSE;
- const AIUpdateInterface *ai = getAI();
- if( ai && !isKindOf( KINDOF_CAN_ATTACK ) )
- {
- for( Int i = 0; i < WEAPONSLOT_COUNT; i++ )
- {
- //Find the weapon in this slot.
- Weapon* weapon = getWeaponInWeaponSlot( (WeaponSlotType)i );
- if( !weapon )
- continue;
- anyWeapon = TRUE;
-
- //We found a weapon, is it a turret?
- Real dummy;
- WhichTurretType tur = ai->getWhichTurretForWeaponSlot( (WeaponSlotType)i, &dummy );
- if( tur == TURRET_INVALID )
- {
- //Currently impossible to disable a non-turreted weapon, so we
- //have a non turreted weapon that is enabled. Quit.
- anyEnabled = TRUE;
- break;
- }
-
- if( ai->isTurretEnabled( tur ) )
- {
- //The turret is enable, meaning we have an enabled weapon. Quit.
- anyEnabled = TRUE;
- break;;
- }
- }
- if( anyWeapon && !anyEnabled )
- {
- //We failed to find any active weapons.
- return FALSE;
- }
- }
- //***************************************
- //********* TRUE CONDITIONS *************
- //***************************************
- // for certain buildings
- if (isKindOf(KINDOF_CAN_ATTACK))
- return true;
- // for garrisonned buildings that can attack sometimes
- if (getStatusBits() & OBJECT_STATUS_CAN_ATTACK)
- return true;
-
- // for weaponless transports. This will make me think I can, but I will check if I literally can by looking
- // at passenger weapons in CanAttack.
- const ContainModuleInterface* contain = getContain();
- if( contain && contain->isPassengerAllowedToFire() && contain->getContainCount() > 0 )
- return true;
- // if we have AI and a weapon, assume we know how to use it
- if (getAIUpdateInterface() != NULL && m_weaponSet.hasAnyWeapon())
- {
- // actually, we don't want to do this; we want the troop crawler to be considered "able to attack"
- // even if empty, so sayeth Dustin. (srj)
- // // special case: if the only damage we do is DEPLOY, we must have some guys contained.
- // if (m_weaponSet.hasSingleDamageType(DAMAGE_DEPLOY))
- // {
- // return contain->getContainCount() > 0;
- // }
- // else
- {
- return true;
- }
- }
- SpawnBehaviorInterface *spawnInterface = getSpawnBehaviorInterface();
- if( spawnInterface )
- {
- if( spawnInterface->canAnySlavesAttack() )
- {
- return TRUE;
- }
- }
- //Default is no
- return false;
- }
- //-------------------------------------------------------------------------------------------------
- /**
- * Mask/Un-Mask an object
- */
- void Object::maskObject( Bool mask )
- {
- // set or clear the mask bit
- setStatus( OBJECT_STATUS_MASKED, mask );
- //
- // when masking objects they become unselected ... we do this in any situation for
- // any player cause you aren't allowed to select masked objects, if the object is not
- // selected (ie, belongs to another player) it's no big deal cause it won't be selected
- // anyway
- //
- if (mask)
- TheGameLogic->deselectObject(this, ~getControllingPlayer()->getPlayerMask(), TRUE);
- } // end maskObject
- //-------------------------------------------------------------------------------------------------
- /*
- * returns true if the current locomotor is an airborne one
- */
- Bool Object::isUsingAirborneLocomotor( void ) const
- {
- return ( m_ai && m_ai->getCurLocomotor() && ((m_ai->getCurLocomotor()->getLegalSurfaces() & LOCOMOTORSURFACE_AIR) != 0) );
- }
- //-------------------------------------------------------------------------------------------------
- //THIS FUNCTION BELONGS AT THE OBJECT LEVEL BECAUSE THERE IS AT LEAST ONE SPECIAL UNIT
- //(ANGRY MOB) WHICH NEEDS LOGIC-SIDE POSITION CALC'S...
- //IT WOULD PROBABLY BE WISE TO MOVE ALL THE HARD-CODED DEFAULTS BELOW
- //INTO A NEW Drawable::getHealthBox..() WHICH USES GEOM0INFO, MODEL DATA, INI DATA, ETC.
- void Object::getHealthBoxPosition(Coord3D& pos) const
- {
- pos = *getPosition();
- pos.z += getGeometryInfo().getMaxHeightAbovePosition() + 10;
- pos.add(&m_healthBoxOffset);
- // this needs to get moved to the mobspawnerupdate
- if (isKindOf(KINDOF_MOB_NEXUS)) // quicker idiot test
- {
- pos.z += 20;// dear God, I confess my kluge, and repent.
- }
- }
- //-------------------------------------------------------------------------------------------------
- //THIS FUNCTION BELONGS AT THE OBJECT LEVEL BECAUSE THERE IS AT LEAST ONE SPECIAL UNIT
- //(ANGRY MOB) WHICH NEEDS LOGIC-SIDE POSITION CALC'S...
- //IT WOULD PROBABLY BE WISE TO MOVE ALL THE HARD-CODED DEFAULTS BELOW
- //INTO A NEW Drawable::getHealthBox..() WHICH USES GEOM0INFO, MODEL DATA, INI DATA, ETC.
- Bool Object::getHealthBoxDimensions(Real &healthBoxHeight, Real &healthBoxWidth) const
- {
- #ifdef CALC_HEALTHBAR_FROM_HITPOINTS
- Real maxHP = getBodyModule()->getMaxHealth();
- if( isKindOf( KINDOF_STRUCTURE ) )
- {
- //enforce healthBoxHeightMinimum/Maximum
- healthBoxHeight = min(3.0f, max(5.0f, maxHP/50));
- //enforce healthBoxWidthMinimum/Maximum
- healthBoxWidth = min(150.0f, max(100.0f, maxHP/10));
- return true;
- }
- else if ( isKindOf(KINDOF_MOB_NEXUS) )
- {
- //enforce healthBoxHeightMinimum/Maximum
- healthBoxHeight = min(3.0f, max(5.0f, maxHP/50));
- //enforce healthBoxWidthMinimum/Maximum
- healthBoxWidth = min(100.0f, max(66.0f, maxHP/10));
- return true;
- }
- else if ( isKindOf( KINDOF_IGNORED_IN_GUI ) )
- {
- healthBoxHeight = 0;
- healthBoxWidth = 0;
- return false;
- }
- else
- {
- //enforce healthBoxHeightMinimum/Maximum
- healthBoxHeight = min(3.0f, max(5.0f, maxHP/50));
- //enforce healthBoxWidthMinimum/Maximum
- healthBoxWidth = min(150.0f, max(35.0f, maxHP/10));
- return true;
- }
- #else
- if ( isKindOf( KINDOF_IGNORED_IN_GUI ) )
- {
- healthBoxHeight = 0;
- healthBoxWidth = 0;
- return false;
- }
- //just add the major and minor axes
- Real size = MAX(20.0f, MIN(150.0f, (getGeometryInfo().getMajorRadius() + getGeometryInfo().getMinorRadius())) );
- healthBoxHeight = 3.0f;
- healthBoxWidth = MAX(20.0f, size * 2.0f);
- return TRUE;
- #endif
- }
- //-------------------------------------------------------------------------------------------------
- /**
- * Update this object instance with properties from the map object
- *
- */
- void Object::updateObjValuesFromMapProperties(Dict* properties)
- {
- Bool exists;
- AsciiString valStr;
- Bool valBool = false;
- Int valInt = 0;
- Real valReal = 0.0f;
- valStr = properties->getAsciiString(TheKey_objectName, &exists);
- if (exists) {
- setName(valStr);
- }
- valInt = properties->getInt(TheKey_objectMaxHPs, &exists);
- if (exists && valInt >= 0) {
- BodyModuleInterface* body = getBodyModule();
- if (body) {
- body->setMaxHealth(valInt);
- }
- }
- valInt = properties->getInt(TheKey_objectInitialHealth, &exists);
- if (exists) {
- BodyModuleInterface* body = getBodyModule();
- if (body) {
- body->setInitialHealth(valInt);
- }
- }
- valInt = properties->getInt(TheKey_objectTime, &exists);
- if (exists)
- {
- switch (valInt)
- {
- case 1:
- getDrawable()->clearModelConditionState(MODELCONDITION_NIGHT);
- break;
- case 2:
- getDrawable()->setModelConditionState(MODELCONDITION_NIGHT);
- break;
- default:
- break;
- }
- }
- valInt = properties->getInt(TheKey_objectWeather, &exists);
- if (exists)
- {
- switch (valInt)
- {
- case 1:
- getDrawable()->clearModelConditionState(MODELCONDITION_SNOW);
- break;
- case 2:
- getDrawable()->setModelConditionState(MODELCONDITION_SNOW);
- break;
- default:
- break;
- }
- }
- // set the veterancy level
- valInt = properties->getInt(TheKey_objectVeterancy, &exists);
- if (exists) {
- if (m_experienceTracker && m_experienceTracker->isTrainable())
- {
- m_experienceTracker->setVeterancyLevel((VeterancyLevel)valInt);
- }
- }
- // set the aggressiveness/mood
- valInt = properties->getInt(TheKey_objectAggressiveness, &exists);
- if (exists) {
- AIUpdateInterface *ai = getAIUpdateInterface();
- if (ai)
- {
- ai->setAttitude((AttitudeType)valInt);
- }
- }
-
- // set recruitable
- valBool = properties->getBool(TheKey_objectRecruitableAI, &exists);
- if (exists) {
- if (getAIUpdateInterface())
- {
- getAIUpdateInterface()->setIsRecruitable(valBool);
- }
- }
-
- // set selectable
- valBool = properties->getBool(TheKey_objectSelectable, &exists);
- if (exists) {
- if (valBool != isSelectable()) {
- setSelectable(valBool);
- }
- }
-
- // set the stopping distance
- valReal = properties->getReal(TheKey_objectStoppingDistance, &exists);
- if (exists && valReal >= 0.5f)
- {
- if (getAIUpdateInterface() && getAIUpdateInterface()->getCurLocomotor())
- {
- Locomotor *loco = getAIUpdateInterface()->getCurLocomotor();
- loco->setCloseEnoughDist(valReal);
- }
- }
-
- // set the disabledness of this object
- valBool = properties->getBool(TheKey_objectEnabled, &exists);
- if (exists) {
- setScriptStatus(OBJECT_STATUS_SCRIPT_DISABLED, !valBool);
- }
- // set the disabledness of this object
- valBool = properties->getBool(TheKey_objectPowered, &exists);
- if (exists) {
- setScriptStatus(OBJECT_STATUS_SCRIPT_UNPOWERED, !valBool);
- }
- // set the invulnerability of the object
- valBool = properties->getBool(TheKey_objectIndestructible, &exists);
- if (exists) {
- BodyModuleInterface* body = getBodyModule();
- if (body) {
- body->setIndestructible(valBool);
- }
- }
- // set the sellability of the object
- valBool = properties->getBool(TheKey_objectUnsellable, &exists);
- if (exists) {
- setScriptStatus(OBJECT_STATUS_SCRIPT_UNSELLABLE, valBool);
- }
- //Set the player targetable setting of the object
- valBool = properties->getBool( TheKey_objectTargetable, &exists );
- if( exists )
- {
- setScriptStatus(OBJECT_STATUS_SCRIPT_TARGETABLE, valBool);
- }
- // adjust the vision distance of this object, overriding its default vision distance
- valInt = properties->getInt(TheKey_objectVisualRange, &exists);
- if (exists)
- {
- if (valInt < 0)
- valInt = 0;
- m_visionRange = INT_TO_REAL(valInt);
- }
- // adjust the shroud clearing distance of this object, overriding its default distance
- valInt = properties->getInt(TheKey_objectShroudClearingDistance, &exists);
- if (exists)
- {
- if (valInt < 0)
- valInt = 0.0f;
- m_shroudClearingRange = INT_TO_REAL(valInt);
- }
- Int upgradeNum = 0;
- do
- {
- AsciiString keyName;
- keyName.format("%s%d", TheNameKeyGenerator->keyToName(TheKey_objectGrantUpgrade).str(), upgradeNum);
- valStr = properties->getAsciiString(NAMEKEY(keyName), &exists);
- if (exists)
- {
- const UpgradeTemplate *ut = TheUpgradeCenter->findUpgrade(valStr);
- if (ut)
- giveUpgrade(ut);
- }
- else
- {
- valStr.clear();
- }
- ++upgradeNum;
- } while (!valStr.isEmpty());
-
- }
- //-------------------------------------------------------------------------------------------------
- void Object::friend_adjustPowerForPlayer( Bool incoming )
- {
- if (isDisabled() && getTemplate()->getEnergyProduction() > 0)
- {
- // Disabledness only affects Producers, not Consumers.
- return;
- }
- if (incoming) {
- getControllingPlayer()->getEnergy()->objectEnteringInfluence(this);
- } else {
- getControllingPlayer()->getEnergy()->objectLeavingInfluence(this);
- }
- }
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- //-------------------------------------------------------------------------------------------------
- void Object::onDisabledEdge(Bool becomingDisabled)
- {
- Player* controller = getControllingPlayer();
- // can be called during game teardown, thus controller can be null
- if (controller)
- {
- //@todo jkmcd - Colin suggested we rewrite this to use the interface stuff. I agree, but need
- // to get some more bugs fixed today.
- static NameKeyType radar = NAMEKEY("RadarUpgrade");
- Module *mod = mod = findModule(radar);
- if (mod) {
- RadarUpgrade *radarMod = (RadarUpgrade*) mod;
- if (radarMod->isAlreadyUpgraded()) {
- // Need to decrement the count here, because we own a radar upgrade
- if (becomingDisabled) {
- controller->removeRadar(radarMod->getIsDisableProof());
- } else {
- controller->addRadar(radarMod->getIsDisableProof());
- }
- }
- }
- }
- // We will need to adjust power ... somehow ...
- Int powerToAdjust = getTemplate()->getEnergyProduction();
-
- if( powerToAdjust > 0 )
- {
- // We can't affect something that consumes, or else we go low power which removes the consumption
- // which makes us not low power so we add the consumption so we go low power...
- // This check also guaards the IsDisabled in friend_adjustPower above
- static NameKeyType powerPlant = NAMEKEY("PowerPlantUpgrade");
- static NameKeyType overCharge = NAMEKEY("OverchargeBehavior");
-
- Module* mod = findModule(powerPlant);
- if (mod) {
- PowerPlantUpgrade *powerPlantMod = (PowerPlantUpgrade*) mod;
- if (powerPlantMod->isAlreadyUpgraded()) {
- powerToAdjust += getTemplate()->getEnergyBonus();
- }
- }
-
- mod = findModule(overCharge);
- if (mod) {
- OverchargeBehavior *overChargeMod = (OverchargeBehavior*) mod;
- if (overChargeMod->isOverchargeActive()) {
- powerToAdjust += getTemplate()->getEnergyBonus();
- }
- }
-
- // Now, adjust the power for the player.
- if (controller)
- controller->getEnergy()->adjustPower(powerToAdjust, !becomingDisabled);
- }
- }
- //-------------------------------------------------------------------------------------------------
- /** Object CRC implemtation */
- //-------------------------------------------------------------------------------------------------
- void Object::crc( Xfer *xfer )
- {
- // This is evil - we cast the const Matrix3D * to a Matrix3D * because the XferCRC class must use
- // the same interface as the XferLoad class for save game restore. This only works because
- // XferCRC does not modify its data.
- #ifdef DEBUG_CRC
- // g_logObjectCRCs = TRUE;
- // Bool g_logAllObjects = TRUE;
- AsciiString logString;
- AsciiString tmp;
- Bool doLogging = g_logObjectCRCs /* && getControllingPlayer()->getPlayerType() == PLAYER_HUMAN */;
- if (doLogging)
- {
- tmp.format("CRC of Object %d (%s), owned by player %d, ", m_id, getTemplate()->getName().str(), getControllingPlayer()->getPlayerIndex());
- logString.concat(tmp);
- }
- #endif // DEBUG_CRC
- xfer->xferUnsignedByte(&m_privateStatus);
- #ifdef DEBUG_CRC
- if (doLogging)
- {
- tmp.format("m_privateStatus: %X, ", (UnsignedInt)m_privateStatus);
- logString.concat(tmp);
- }
- #endif // DEBUG_CRC
- xfer->xferUser((Matrix3D *)getTransformMatrix(), sizeof(Matrix3D));
- #ifdef DEBUG_CRC
- if (doLogging)
- {
- XferCRC tmpXfer;
- tmpXfer.open("tmp");
- tmpXfer.xferUser((Matrix3D *)getTransformMatrix(), sizeof(Matrix3D));
- tmp.format("getTransformMatrix(): %8.8X, ", tmpXfer.getCRC());
- tmpXfer.close();
- logString.concat(tmp);
- }
- #endif // DEBUG_CRC
-
- #ifdef DEBUG_CRC
- if (doLogging)
- {
- const Matrix3D *mtx = getTransformMatrix();
- CRCDEBUG_LOG(("CRC of Object %d (%s), owned by player %d, ", m_id, getTemplate()->getName().str(), getControllingPlayer()->getPlayerIndex()));
- DUMPMATRIX3D(mtx);
- }
- #endif //DEBUG_CRC
- xfer->xferUser(&m_id, sizeof(m_id));
- #ifdef DEBUG_CRC
- if (doLogging)
- {
- tmp.format("m_id: %d, ", m_id);
- logString.concat(tmp);
- }
- #endif // DEBUG_CRC
- xfer->xferUser(&m_objectUpgradesCompleted, sizeof(Int64));
- #ifdef DEBUG_CRC
- if (doLogging)
- {
- tmp.format("m_objectUpgradesCompleted: %I64X, ", m_objectUpgradesCompleted);
- logString.concat(tmp);
- }
- #endif // DEBUG_CRC
- if (m_experienceTracker)
- xfer->xferSnapshot( m_experienceTracker );
- #ifdef DEBUG_CRC
- if (doLogging)
- {
- XferCRC tmpXfer;
- tmpXfer.open("tmp");
- tmpXfer.xferSnapshot(m_experienceTracker);
- tmp.format("m_experienceTracker: %8.8X, ", tmpXfer.getCRC());
- tmpXfer.close();
- logString.concat(tmp);
- }
- #endif // DEBUG_CRC
- Real health = getBodyModule()->getHealth();
- xfer->xferUser(&health, sizeof(health));
- #ifdef DEBUG_CRC
- if (doLogging)
- {
- tmp.format("health: %g/%8.8X, ", health, AS_INT(health));
- logString.concat(tmp);
- }
- #endif // DEBUG_CRC
- xfer->xferUnsignedInt(&m_weaponBonusCondition);
- #ifdef DEBUG_CRC
- if (doLogging)
- {
- tmp.format("m_weaponBonusCondition: %8.8X, ", m_weaponBonusCondition);
- logString.concat(tmp);
- }
- #endif // DEBUG_CRC
- Real scalar = getBodyModule()->getDamageScalar();
- xfer->xferUser(&scalar, sizeof(scalar));
- #ifdef DEBUG_CRC
- if (doLogging)
- {
- tmp.format("damage scalar: %g/%8.8X\n", scalar, AS_INT(scalar));
- logString.concat(tmp);
- CRCDEBUG_LOG(("%s", logString.str()));
- }
- #endif // DEBUG_CRC
- for (Int i=0; i<WEAPONSLOT_COUNT; ++i)
- {
- Weapon *thisWeapon = getWeaponInWeaponSlot((WeaponSlotType)i);
- if (thisWeapon)
- {
- xfer->xferSnapshot( thisWeapon );
- }
- }
-
- } // end crc
- //-------------------------------------------------------------------------------------------------
- /** Object xfer implemtation
- * Version Info:
- * 1: Initial version
- * 2: Xfers m_singleUseCommandUsed... determines if the single use command button has been used or not.
- * 3: Xfers the solehealingbenefactor ID and expiration frame
- * 4: misc stuff that got missed somehow
- * 5: m_isReceivingDifficultyBonus
- * 6: We do indeed need to save m_containedBy. The comment misrepresents what the contain module will do.
- * 7: save full mtx, not pos+orient.
- */
- //-------------------------------------------------------------------------------------------------
- void Object::xfer( Xfer *xfer )
- {
-
- // version
- const XferVersion currentVersion = 7;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- // object ID
- ObjectID id = getID();
- xfer->xferObjectID( &id );
- setID( id );
- DEBUG_LOG(("Xfer Object %s id=%d\n",getTemplate()->getName().str(),id));
- if (version >= 7)
- {
- Matrix3D mtx = *getTransformMatrix();
- xfer->xferMatrix3D(&mtx);
- setTransformMatrix(&mtx);
- }
- else
- {
- // object position
- Coord3D pos = *getPosition();
- xfer->xferCoord3D( &pos );
- setPosition( &pos );
- // orientation
- Real orientation = getOrientation();
- xfer->xferReal( &orientation );
- setOrientation( orientation );
- }
- // team
- TeamID teamID = m_team ? m_team->getID() : TEAM_ID_INVALID;
- xfer->xferUser( &teamID, sizeof( TeamID ) );
- // DON'T set the team yet; must wait till we read our status bits,
- // since setTeam can affect the player's power usage, but that could
- // be done incorrectly if our status bits aren't accurate yet... (srj)
- // producer id
- xfer->xferObjectID( &m_producerID );
- // builder id
- xfer->xferObjectID( &m_builderID );
- // drawable id
- Drawable *draw = getDrawable();
- DrawableID drawableID = draw ? draw->getID() : INVALID_DRAWABLE_ID;
- xfer->xferDrawableID( &drawableID );
- if( xfer->getXferMode() == XFER_LOAD )
- {
- // change the ID of the drawable attached to be the same ID as it was when it was saved
- draw->setID( drawableID );
- } // end if
- // internal name
- xfer->xferAsciiString( &m_name );
- // status
- xfer->xferUnsignedInt( &m_status );
- // script status
- xfer->xferUnsignedByte( &m_scriptStatus );
- // private status
- xfer->xferUnsignedByte( &m_privateStatus );
- // OK, now that we have xferred our status bits, it's safe to set the team...
- if( xfer->getXferMode() == XFER_LOAD )
- {
- Team *team = TheTeamFactory->findTeamByID( teamID );
- if( team == NULL )
- {
- DEBUG_CRASH(( "Object::xfer - Unable to load team\n" ));
- throw SC_INVALID_DATA;
- }
- const Bool restoring = true;
- setOrRestoreTeam( team, restoring );
- }
- // geometry info
- xfer->xferSnapshot( &m_geometryInfo );
- // sighting info, last look - must be saved cause we save PartitionCell::m_shroudLevel
- xfer->xferSnapshot( m_partitionLastLook );
- // sighting info, last shroud - must be saved cause we save PartitionCell::m_shroudLevel
- xfer->xferSnapshot( m_partitionLastShroud );
- // sighting info, last threat
- // John M says we don't need to save this (CBD)
- // xfer->xferSnapshot( &m_partitionLastThreat );
- // sighting info, last value
- // John M says we don't need to save this (CBD)
- // xfer->xferSnapshot( &m_partitionLastValue );
- // vision range
- xfer->xferReal( &m_visionRange );
- // shroud clearing range
- xfer->xferReal( &m_shroudClearingRange );
- // shroud range
- xfer->xferReal( &m_shroudRange );
- // disabled mask
- m_disabledMask.xfer( xfer );
- //New var added for version 2. Determines if the single use command button has been used or not.
- if( xfer->getXferMode() == XFER_SAVE || version >= 2 )
- {
- xfer->xferBool( &m_singleUseCommandUsed );
- }
- else
- {
- m_singleUseCommandUsed = false;
- }
- // disabled till frame
- xfer->xferUser( m_disabledTillFrame, sizeof( UnsignedInt ) * DISABLED_COUNT );
- // special model condition until
- xfer->xferUnsignedInt( &m_smcUntil );
- //
- // radar data ... when loading, we will remove all objects from the radar and let
- // the radar system load itself as a separate chunk of data from the save file
- //
- if( xfer->getXferMode() == XFER_LOAD && m_radarData )
- TheRadar->removeObject( this );
- // experience tracker
- xfer->xferSnapshot( m_experienceTracker );
- //
- // we do not need to do anything with our m_containedBy pointer, the post process
- // of that objects contain module will actually re-do the contain process again
- //
- // m_containedBy <-- do nothing with this right now
- if( version >= 6 )
- {
- // No, the contain module is just going to friend_ reach in and set this for us.
- // Containers more complicated than Open (like Tunnel) can't do that. Our variable,
- // our responsibility.
- if( xfer->getXferMode() == XFER_SAVE )
- {
- if( m_containedBy != NULL )
- m_xferContainedByID = m_containedBy->getID();
- else
- m_xferContainedByID = INVALID_ID;
- }
- xfer->xferObjectID( &m_xferContainedByID );
- }
- // contained by frame
- xfer->xferUnsignedInt( &m_containedByFrame );
- // construction percent
- xfer->xferReal( &m_constructionPercent );
- // upgrades completed
- xfer->xferUpgradeMask( &m_objectUpgradesCompleted );
- // original team name
- xfer->xferAsciiString( &m_originalTeamName );
- // indicator color
- xfer->xferColor( &m_indicatorColor );
- // health box offset
- xfer->xferCoord3D( &m_healthBoxOffset );
- // Entered & exited housekeeping.
- Int i;
- xfer->xferByte(&m_numTriggerAreasActive);
- xfer->xferUnsignedInt(&m_enteredOrExitedFrame);
- xfer->xferICoord3D(&m_iPos);
- if (m_numTriggerAreasActive<0 || m_numTriggerAreasActive>MAX_TRIGGER_AREA_INFOS) {
- DEBUG_CRASH(("Invalid m_numTriggerAreasActive = %d, max is %d", m_numTriggerAreasActive,
- MAX_TRIGGER_AREA_INFOS));
- throw SC_INVALID_DATA;
- }
- for (i=0; i<m_numTriggerAreasActive; i++) {
- AsciiString triggerName;
- if (m_triggerInfo[i].pTrigger) {
- triggerName = m_triggerInfo[i].pTrigger->getTriggerName();
- }
- xfer->xferAsciiString(&triggerName);
- if (xfer->getXferMode() == XFER_LOAD)
- {
- //
- // CBD (11-13-2002) I'm disabling this because it appears there might be some areas with
- // empty names, see John A. for more info
- //
- //if (triggerName.isNotEmpty())
- m_triggerInfo[i].pTrigger = TheTerrainLogic->getTriggerAreaByName(triggerName);
- }
- xfer->xferByte(&m_triggerInfo[i].entered);
- xfer->xferByte(&m_triggerInfo[i].exited);
- xfer->xferByte(&m_triggerInfo[i].isInside);
- }
- // Layer object is pathing on.
- xfer->xferUser(&m_layer, sizeof(m_layer));
- // Layer of current path goal.
- xfer->xferUser(&m_destinationLayer, sizeof(m_destinationLayer));
- // Object selectability.
- xfer->xferBool(&m_isSelectable);
- xfer->xferUnsignedInt(&m_safeOcclusionFrame);
- // User formations.
- xfer->xferUser(&m_formationID, sizeof(m_formationID));
- if (m_formationID!=NO_FORMATION_ID) {
- xfer->xferCoord2D(&m_formationOffset);
- }
- // module count
- UnsignedShort moduleCount = 0;
- for (BehaviorModule** b = m_behaviors; *b; ++b)
- ++moduleCount;
- xfer->xferUnsignedShort( &moduleCount );
- AsciiString moduleIdentifier;
- BehaviorModule *module;
- if( xfer->getXferMode() == XFER_SAVE )
- {
- // go through all modules
- for (BehaviorModule** b = m_behaviors; *b; ++b)
- {
- // get module
- module = *b;
- // write module identifier
- moduleIdentifier = TheNameKeyGenerator->keyToName( module->getModuleTagNameKey() );
- DEBUG_ASSERTCRASH( moduleIdentifier != AsciiString::TheEmptyString,
- ("Object::xfer - Module tag key does not translate to a string!\n") );
- xfer->xferAsciiString( &moduleIdentifier );
- // begin a data block
- xfer->beginBlock();
- // xfer data
- xfer->xferSnapshot( module );
- // end data block
- xfer->endBlock();
- } // end for, it
- } // end if, save
- else
- {
- AsciiString otherModuleIdentifier;
- // read all module data
- for( UnsignedShort i = 0; i < moduleCount; ++i )
- {
- // read module name
- xfer->xferAsciiString( &moduleIdentifier );
- NameKeyType moduleIdentifierKey = TheNameKeyGenerator->nameToKey(moduleIdentifier);
- // find the module with this identifier in the module list
- module = NULL;
- for (BehaviorModule** b = m_behaviors; b && *b; ++b)
- {
- if (moduleIdentifierKey == (*b)->getModuleTagNameKey())
- {
- module = *b;
- break;
- }
- } // end for, moduleIt
- // start of a new block
- Int dataSize = xfer->beginBlock();
- //
- // if we didn't find the module, it's quite possible that we have removed
- // it from the object definition in a future patch, if that is so, we need to
- // skip the module data in the file
- //
- if( module == NULL )
- {
- // for testing purposes, this module better be found
- DEBUG_CRASH(( "Object::xfer - Module '%s' was indicated in file, but not found on object '%s'(%d)\n",
- moduleIdentifier.str(), getTemplate()->getName().str(), getID() ));
- // skip this data in the file
- xfer->skip( dataSize );
- } // end if
- else
- {
- // xfer the data into this module
- xfer->xferSnapshot( module );
- } // end else
-
- // end block
- xfer->endBlock();
- } // end for, i module count recorded in file
- } // end else, load
- if ( version >= 3 )
- {
- xfer->xferObjectID( &m_soleHealingBenefactorID );
- xfer->xferUnsignedInt( &m_soleHealingBenefactorExpirationFrame );
- }
- else if ( xfer->getXferMode() == XFER_LOAD )
- {
- m_soleHealingBenefactorID = INVALID_ID;
- m_soleHealingBenefactorExpirationFrame = 0;
- }
- // Doesn't need to be saved. These are created as needed. jba.
- //AIGroup* m_group; ///< if non-NULL, we are part of this group of agents
- // don't need to save m_partitionData.
- DEBUG_ASSERTCRASH(!(xfer->getXferMode() == XFER_LOAD && m_partitionData == NULL), ("should not be in partitionmgr yet"));
- // don't need to be saved or loaded; are inited & cached for runtime only by our ctor (srj)
- //m_repulsorHelper;
- //m_smcHelper;
- //m_wsHelper;
- //m_defectionHelper;
- //m_firingTracker;
- //m_contain;
- //m_body;
- //m_ai;
- //m_physics;
- #if defined(_DEBUG) || defined(_INTERNAL)
- //m_hasDiedAlready;
- #endif
- if (version >= 4)
- {
- // xfer the weaponSetFlags FIRST, since we need 'em to restore the weaponSet properly. (srj)
- m_curWeaponSetFlags.xfer( xfer );
- xfer->xferUnsignedInt(&m_weaponBonusCondition);
- xfer->xferUser(&m_lastWeaponCondition, sizeof(m_lastWeaponCondition));
- // do the weaponSet itself after all the weapon-related stuff, just in case
- xfer->xferSnapshot(&m_weaponSet);
- m_specialPowerBits.xfer( xfer );
- xfer->xferAsciiString(&m_commandSetStringOverride);
- xfer->xferBool(&m_modulesReady);
- }
- if (version >= 5)
- {
- xfer->xferBool(&m_isReceivingDifficultyBonus);
- }
- else
- m_isReceivingDifficultyBonus = FALSE;
- } // end xfer
- //-------------------------------------------------------------------------------------------------
- /** Object load game post process phase */
- //-------------------------------------------------------------------------------------------------
- void Object::loadPostProcess()
- {
- if( m_xferContainedByID != INVALID_ID )
- m_containedBy = TheGameLogic->findObjectByID(m_xferContainedByID);
- else
- m_containedBy = NULL;
- } // end loadPostProcess
- //-------------------------------------------------------------------------------------------------
- /** Does this object have this upgrade */
- //-------------------------------------------------------------------------------------------------
- Bool Object::hasUpgrade( const UpgradeTemplate *upgradeT ) const
- {
- return BitTest( m_objectUpgradesCompleted, upgradeT->getUpgradeMask() );
- } // end hasUpgrade
- //-------------------------------------------------------------------------------------------------
- /** Is this object capable of having this upgrade */
- //-------------------------------------------------------------------------------------------------
- Bool Object::affectedByUpgrade( const UpgradeTemplate *upgradeT ) const
- {
- Int64 objectMask = getObjectCompletedUpgradeMask();
- Int64 playerMask = getControllingPlayer()->getCompletedUpgradeMask();
- Int64 maskToCheck = playerMask | objectMask | upgradeT->getUpgradeMask();
- // We need to add in all of the already owned upgrades to handle "AND" requiring upgrades.
- // We combine all the masks in case someone has a Object AND Player combination
- for (BehaviorModule** module = m_behaviors; *module; ++module)
- {
- UpgradeModuleInterface* upgrade = (*module)->getUpgrade();
- if (!upgrade)
- continue;
- if( upgrade->wouldUpgrade( maskToCheck ) )
- {
- // if any of my many upgrade modules would execute in response to this flag, say yes.
- return TRUE;
- }
- }
- return FALSE;
- } // end affectedByUpgrade
- //-------------------------------------------------------------------------------------------------
- /** Give this upgrade to this object */
- //-------------------------------------------------------------------------------------------------
- void Object::giveUpgrade( const UpgradeTemplate *upgradeT )
- {
- if (upgradeT)
- {
- BitSet( m_objectUpgradesCompleted, upgradeT->getUpgradeMask() );
- //
- // iterate through all the upgrade modules of this object and call the method to
- // grant a new upgrade
- //
- updateUpgradeModules();
- }
- } // end giveUpgrade
- //-------------------------------------------------------------------------------------------------
- /** Remove this upgrade from this object */
- //-------------------------------------------------------------------------------------------------
- void Object::removeUpgrade( const UpgradeTemplate *upgradeT )
- {
- BitClear( m_objectUpgradesCompleted, upgradeT->getUpgradeMask() );
- for (BehaviorModule** module = m_behaviors; *module; ++module)
- {
- UpgradeModuleInterface* upgrade = (*module)->getUpgrade();
- if (!upgrade)
- continue;
- upgrade->resetUpgrade( upgradeT->getUpgradeMask() );
- }
- }
- //-------------------------------------------------------------------------------------------------
- /** Central point for onCapture logic */
- //-------------------------------------------------------------------------------------------------
- void Object::onCapture( Player *oldOwner, Player *newOwner )
- {
- // Everybody dhills when they captured so they don't keep doing something the new player might not want him to be doing
- if( getAIUpdateInterface() && (oldOwner != newOwner) )
- getAIUpdateInterface()->aiIdle(CMD_FROM_AI);
- // this gets the new owner some points
- newOwner->getScoreKeeper()->addObjectCaptured(this);
- // rip through the behavior modules and call the onCapture for any modules that care
- for( BehaviorModule **module = m_behaviors; *module; ++module )
- (*module)->onCapture( oldOwner, newOwner );
- //
- // We have to undo our look for the old team and redo it for the new.
- // onCapture is used now, so it better be called after ownership changes and not before.
- //
- handlePartitionCellMaintenance();
-
- // Design needs the player to be able to sell buildings he steals from the AI's build list, and this is the
- // easiest fix. The only snafu would be a key building build listed by the AI that the player can capture
- // and the AI tries to capture back but needs to not sell. In that case, a Cinematic Unsellable version
- // of the building needs to be made. This fix has been okayed as the most non-lethal in November.
- clearScriptStatus(OBJECT_STATUS_SCRIPT_UNSELLABLE);
- // mark the command bar to redraw
- TheControlBar->markUIDirty();
- if (oldOwner!=newOwner && newOwner->isSkirmishAIPlayer()) {
- // The skirmish ai doesn't know what to do with captured faction buildings except sell them.
- if (isFactionStructure()) {
- TheBuildAssistant->sellObject( this );
- }
- }
- } // end onCapture
- //-------------------------------------------------------------------------------------------------
- /// Object level events that need to happen upon game death
- void Object::onDie( DamageInfo *damageInfo )
- {
- #if defined(_DEBUG) || defined(_INTERNAL)
- DEBUG_ASSERTCRASH(m_hasDiedAlready == false, ("Object::onDie has been called multiple times. This is invalid. jkmcd"));
- m_hasDiedAlready = true;
- #endif
-
- Bool selfInflicted = (damageInfo->in.m_sourceID == getID());
- // FIRST, call our die modules.
- for (BehaviorModule** d = m_behaviors; *d; ++d)
- {
- DieModuleInterface* die = (*d)->getDie();
- if (die)
- die->onDie(damageInfo);
- }
- // When objects die we remove from the radar as they're really not interesting anymore
- if( m_radarData )
- TheRadar->removeObject( this );
-
- // Just in case I have been sporting one of thise fancy Terrain Decals,
- //I naturally lose it now, because I'm dead.
- Drawable *draw = getDrawable();
- if (draw) draw->setTerrainDecalFadeTarget(0.0f, -0.03f);//fade...
- //if (draw) draw->setTerrainDecal(TERRAIN_DECAL_NONE);//pop!
- // objects that were spawned from something, need to tell their spawner that they have died
- Object* spawner = TheGameLogic->findObjectByID( getProducerID() );
- if( spawner )
- {
- // get the spawn behavior interface of the spawner
- SpawnBehaviorInterface *spawnerBehavior = spawner->getSpawnBehaviorInterface();
- if( spawnerBehavior )
- spawnerBehavior->onSpawnDeath( getID(), damageInfo );
- }
- handlePartitionCellMaintenance();
- if(m_team)
- m_team->notifyTeamOfObjectDeath();
- // Play death sound here.
- AudioEventRTS deathSound = *getTemplate()->getSoundDie();
- // If we were killed by fire, or by poison, we should play those die sounds instead of the usual
- // sound
- if (damageInfo->in.m_deathType == DEATH_BURNED)
- deathSound = *getTemplate()->getSoundDieFire();
- else if (damageInfo->in.m_deathType == DEATH_POISONED || damageInfo->in.m_deathType == DEATH_POISONED_BETA)
- deathSound = *getTemplate()->getSoundDieToxin();
- // If we didn't actually have a specialized die sound (for the case of fire or poison, we
- // should use the generic death sound)
- if (!TheAudio->isValidAudioEvent(&deathSound))
- deathSound = *getTemplate()->getSoundDie();
- // Use the position. Next frame, when this unit is gone, this sound will be clipped because we
- // can no longer automatically find its position. - jkmcd
- deathSound.setPosition(getPosition());
- PlayerIndex index = getControllingPlayer() ? getControllingPlayer()->getPlayerIndex() : 0;
- deathSound.setPlayerIndex( index );
- TheAudio->addAudioEvent(&deathSound);
-
- if (isLocallyControlled() && !selfInflicted) // wasLocallyControlled? :-)
- {
- if (isKindOf(KINDOF_STRUCTURE) && isKindOf(KINDOF_MP_COUNT_FOR_VICTORY))
- {
- TheEva->setShouldPlay(EVA_BuldingLost);
- }
- else if (isKindOf(KINDOF_INFANTRY) || isKindOf(KINDOF_VEHICLE))
- {
- TheEva->setShouldPlay(EVA_UnitLost);
- //Create a fake radar event so the user can use the spacebar to quickly jump to this!
- TheRadar->tryEvent( RADAR_EVENT_FAKE, getPosition() );
- }
- }
- // This call won't do anything if we aren't actually in the list.
- TheInGameUI->removeIdleWorker(this, this->getControllingPlayer()->getPlayerIndex());
- }
- //-------------------------------------------------------------------------------------------------
- /**
- A weapon cannot be in charge of maintaining condition flags as it is all event driven.
- I will maintain my ModelCondition myself if it should change. Firing is set by firing logic,
- so I don't include it here. It is only the states that expire on timers that noone watches
- that I am concerned with.
- */
- //-------------------------------------------------------------------------------------------------
- void Object::adjustModelConditionForWeaponStatus()
- {
- UnsignedInt now = TheGameLogic->getFrame();
- for (int i = 0; i < WEAPONSLOT_COUNT; ++i)
- {
- const Weapon* w = m_weaponSet.getWeaponInWeaponSlot((WeaponSlotType)i);
- if (!w)
- {
- m_lastWeaponCondition[i] = WSF_NONE;
- continue;
- }
-
- WeaponSetConditionType conditionToSet = WSF_INVALID;
- if (i != m_weaponSet.getCurWeaponSlot())
- {
- // if this isn't the current weapon, then we never set ANYTHING for it.
- conditionToSet = WSF_NONE;
- }
- else if (w->getLastShotFrame() == now)
- {
- // yep, this overrides any weapon-status condition!
- conditionToSet = WSF_FIRING;
- }
- else if (!testStatus( OBJECT_STATUS_IS_ATTACKING ))
- {
- // srj sez: not 100% sure about this one, but the problem is: say we were attacking,
- // then issue a move command. if we didn't do this here, we might still have a 'firing'
- // pose, because his weapon might be in 'reloading' mode. since we're not attacking, however,
- // we really don't care, so we just force the issue here. (This might still need tweaking for the pursue state.)
- conditionToSet = WSF_NONE;
- }
- else
- {
- WeaponStatus newStatus = w->getStatus();
- const static WeaponSetConditionType s_wsfLookup[WEAPON_STATUS_COUNT] =
- {
- WSF_NONE, // READY_TO_FIRE,
- WSF_NONE, // OUT_OF_AMMO,
- WSF_BETWEEN, // BETWEEN_FIRING_SHOTS,
- WSF_RELOADING, // RELOADING_CLIP,
- WSF_PREATTACK // PRE_ATTACK,
- };
- conditionToSet = s_wsfLookup[newStatus];
- // special case this: say we are firing in bursts: pow-pow-pow-pause, etc.
- // then we might have a frame where we have reloaded and are ready-to-fire,
- // but haven't fired yet this frame. in that case, use 'between' so we still have
- // a firing pose, 'cuz if we use 'none' we will 'pop' back to idle for a frame. (srj)
- // additional note: only do if aiming or firing, since we could also be in this state if
- // we are approaching or pursuing a target! (srj)
- if (newStatus == READY_TO_FIRE && conditionToSet == WSF_NONE && testStatus( OBJECT_STATUS_IS_ATTACKING ) &&
- (testStatus( OBJECT_STATUS_IS_AIMING_WEAPON ) || testStatus( OBJECT_STATUS_IS_FIRING_WEAPON )))
- {
- conditionToSet = WSF_BETWEEN;
- }
- }
- if (m_drawable)
- {
- m_drawable->updateDrawableClipStatus( w->getRemainingAmmo(), w->getClipSize(), w->getWeaponSlot() );
- if (conditionToSet != WSF_INVALID && conditionToSet != m_lastWeaponCondition[i])
- {
- m_lastWeaponCondition[i] = conditionToSet;
- ModelConditionFlags c = m_weaponSet.getModelConditionForWeaponSlot((WeaponSlotType)i, conditionToSet);
- m_drawable->clearAndSetModelConditionFlags(s_allWeaponFireFlags[i], c);
- if (conditionToSet == WSF_PREATTACK)
- {
- // in the preattack state, adjust the speed of the preattack anim to match the actual time it will take
- UnsignedInt preAttackDone = w->getPreAttackFinishedFrame();
- if (preAttackDone > now)
- m_drawable->setAnimationLoopDuration(preAttackDone - now);
- }
- }
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- /// We have moved a 'significant' amount, so do maintenence that can be considered 'cell-based'
- void Object::onPartitionCellChange()
- {
- handlePartitionCellMaintenance();
- }
- //-------------------------------------------------------------------------------------------------
- void Object::handlePartitionCellMaintenance()
- {
- handleShroud();
- handleValueMap();
- handleThreatMap();
- }
- //-------------------------------------------------------------------------------------------------
- void Object::handleShroud()
- {
- // Undo last looking
- unlook();
- // and shrouding
- unshroud();
- // redo shrouding
- shroud();
- // Redo looking
- look();
- }
- //-------------------------------------------------------------------------------------------------
- void Object::handleValueMap()
- {
- removeValue();
- addValue();
- }
- //-------------------------------------------------------------------------------------------------
- void Object::handleThreatMap()
- {
- removeThreat();
- addThreat();
- }
- //-------------------------------------------------------------------------------------------------
- void Object::addValue()
- {
- if( !m_partitionLastValue->isInvalid() )
- {
- DEBUG_CRASH( ("An Object is adding value, but hasn't removed his previous value.") );
- return;
- }
- if (!getControllingPlayer())
- return;
- if( ((getStatusBits() & OBJECT_STATUS_UNDER_CONSTRUCTION) != 0)
- || ( isEffectivelyDead() )
- || ( getShroudClearingRange() <= 0.0f ))
- return;
- m_partitionLastValue->m_where = *getPosition();
- m_partitionLastValue->m_data = getTemplate()->friend_getBuildCost();
- m_partitionLastValue->m_forWhom = getControllingPlayer()->getPlayerMask();
- m_partitionLastValue->m_howFar = getVisionRange(); // we are valuable all the way to where we can target.
- ThePartitionManager->doValueAffect(m_partitionLastValue->m_where.x,
- m_partitionLastValue->m_where.y,
- m_partitionLastValue->m_howFar,
- m_partitionLastValue->m_data,
- m_partitionLastValue->m_forWhom
- );
- }
- //-------------------------------------------------------------------------------------------------
- void Object::removeValue()
- {
- if( m_partitionLastValue->isInvalid() )
- {
- // removing before adding is valid, cause we always remove before adding. (So the first remove
- // will occur before the first add)
- return;
- }
- ThePartitionManager->undoValueAffect(m_partitionLastValue->m_where.x,
- m_partitionLastValue->m_where.y,
- m_partitionLastValue->m_howFar,
- m_partitionLastValue->m_data,
- m_partitionLastValue->m_forWhom
- );
- m_partitionLastValue->reset();
- }
- //-------------------------------------------------------------------------------------------------
- void Object::addThreat()
- {
- if( !m_partitionLastThreat->isInvalid() )
- {
- DEBUG_CRASH( ("An Object is adding threat, but hasn't removed his previous threat. (He hasn't finished the threat?)") );
- return;
- }
-
- if (!getControllingPlayer())
- return;
- if( ((getStatusBits() & OBJECT_STATUS_UNDER_CONSTRUCTION) != 0)
- || ( isEffectivelyDead() )
- || ( getShroudClearingRange() <= 0.0f ))
- return;
- m_partitionLastThreat->m_where = *getPosition();
- m_partitionLastThreat->m_data = getTemplate()->getThreatValue();
- m_partitionLastThreat->m_forWhom = getControllingPlayer()->getPlayerMask();
- m_partitionLastThreat->m_howFar = getVisionRange(); // we are threatening all the way to where we can target.
- ThePartitionManager->doThreatAffect(m_partitionLastThreat->m_where.x,
- m_partitionLastThreat->m_where.y,
- m_partitionLastThreat->m_howFar,
- m_partitionLastThreat->m_data,
- m_partitionLastThreat->m_forWhom
- );
- }
- //-------------------------------------------------------------------------------------------------
- void Object::removeThreat()
- {
- if( m_partitionLastThreat->isInvalid() )
- {
- // removing before adding is valid, cause we always remove before adding. (So the first remove
- // will occur before the first add)
- return;
- }
- ThePartitionManager->undoThreatAffect(m_partitionLastThreat->m_where.x,
- m_partitionLastThreat->m_where.y,
- m_partitionLastThreat->m_howFar,
- m_partitionLastThreat->m_data,
- m_partitionLastThreat->m_forWhom
- );
- m_partitionLastThreat->reset();
- }
- //-------------------------------------------------------------------------------------------------
- void Object::look()
- {
- if( ! m_partitionLastLook->isInvalid() )
- {
- DEBUG_CRASH( ("An Object is looking, but hasn't unlooked the last one.") );
- return;
- }
- Player* controller = getControllingPlayer();
- if ( controller )
- {
- // I removed the check for objects under construction by request of designers since
- // they want constructing objects to have a reduced sight range now. -MW
- // dead or blind things don't reveal shroud
- if( ( ! isDestroyed() )// Some things get Destroyed directly without hitting Death.
- && ( ! isEffectivelyDead() )
- && ( getShroudClearingRange() > 0.0f )
- )
- {
- PlayerMaskType lookingMask = 0;
- for( Int currentIndex = ThePlayerList->getPlayerCount() - 1; currentIndex >=0; currentIndex-- )
- {
- const Player *currentPlayer = ThePlayerList->getNthPlayer( currentIndex );
- // Build mask of of allies who can see me.
- // This is the Object-centric game level that cares
- if( getControllingPlayer()->getRelationship( currentPlayer->getDefaultTeam() ) == ALLIES )
- {
- lookingMask |= currentPlayer->getPlayerMask();
- }
- }
-
- // Other players can also be looking through our eyes.
- lookingMask |= controller->getVisionSpiedMask();
- if ( isKindOf(KINDOF_REVEAL_TO_ALL) )
- lookingMask = PLAYERMASK_ALL;
- Coord3D pos = *getPosition();
- ThePartitionManager->doShroudReveal(pos.x,
- pos.y,
- getShroudClearingRange(),
- lookingMask
- );
- m_partitionLastLook->m_where = pos;
- m_partitionLastLook->m_forWhom = lookingMask;
- m_partitionLastLook->m_howFar = getShroudClearingRange();
- // DEBUG_LOG(( "A %s looks at %f, %f for %x at range %f\n",
- // getTemplate()->getName().str(),
- // pos.x,
- // pos.y,
- // lookingMask,
- // getShroudClearingRange()
- // ));
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Object::unlook()
- {
- if( m_partitionLastLook->isInvalid() )
- {
- // Your very first action will be an unlook, so of course you haven't looked yet. This is not an error
- // This early return prevents an extra unlook if you never looked. Like you have 0 vision.
- return;
- }
- ThePartitionManager->queueUndoShroudReveal(m_partitionLastLook->m_where.x,
- m_partitionLastLook->m_where.y,
- m_partitionLastLook->m_howFar,
- m_partitionLastLook->m_forWhom
- );
- // DEBUG_LOG(( "A %s queues an unlook at %f, %f for %x at range %f\n",
- // getTemplate()->getName().str(),
- // m_partitionLastLook.m_where.x,
- // m_partitionLastLook.m_where.y,
- // m_partitionLastLook.m_forWhom,
- // m_partitionLastLook.m_howFar
- // ));
- m_partitionLastLook->reset();
- }
- //-------------------------------------------------------------------------------------------------
- void Object::shroud()
- {
- if( ! m_partitionLastShroud->isInvalid() )
- {
- DEBUG_CRASH( ("An Object is shrouding, but hasn't unshrouded the last one.") );
- return;
- }
- Player* controller = getControllingPlayer();
- if ( controller )
- {
- // things under construction don't shroud. (srj), nor do dead or blind things
- if( ((getStatusBits() & OBJECT_STATUS_UNDER_CONSTRUCTION) == 0)
- && ( ! isEffectivelyDead() )
- && ( getShroudRange() > 0.0f )
- )
- {
- PlayerMaskType shroudingMask = 0;
- for( Int currentIndex = ThePlayerList->getPlayerCount() - 1; currentIndex >=0; currentIndex-- )
- {
- const Player *currentPlayer = ThePlayerList->getNthPlayer( currentIndex );
- //Build mask of NON-allies. This is the Object-centric game level that cares
- if( getControllingPlayer()->getRelationship( currentPlayer->getDefaultTeam() ) != ALLIES )
- {
- shroudingMask |= currentPlayer->getPlayerMask();
- }
- }
- Coord3D pos = *getPosition();
- ThePartitionManager->doShroudCover(pos.x, pos.y,
- getShroudRange(),
- shroudingMask);
- m_partitionLastShroud->m_where = pos;
- m_partitionLastShroud->m_forWhom = shroudingMask;
- m_partitionLastShroud->m_howFar = getShroudRange();
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Object::unshroud()
- {
- if( m_partitionLastShroud->isInvalid() )
- {
- // Your very first action will be an unlook, so of course you haven't looked yet. This is not an error
- // This early return prevents an extra unlook if you never looked. Like you have 0 shroud generation.
- return;
- }
- ThePartitionManager->undoShroudCover(m_partitionLastShroud->m_where.x,
- m_partitionLastShroud->m_where.y,
- m_partitionLastShroud->m_howFar,
- m_partitionLastShroud->m_forWhom);
- m_partitionLastShroud->reset();
- }
- //-------------------------------------------------------------------------------------------------
- Real Object::getVisionRange() const
- {
- #if defined(_DEBUG) || defined(_INTERNAL)
- if (TheGlobalData->m_debugVisibility)
- {
- Vector3 pos(m_visionRange, 0, 0);
- for (int i = 0; i < TheGlobalData->m_debugVisibilityTileCount; ++i)
- {
- pos.Rotate_Z(1.0f * i / TheGlobalData->m_debugVisibilityTileCount * 2 * PI);
- Coord3D coord = { pos.X + getPosition()->x, pos.Y + getPosition()->y, pos.Z + getPosition()->z };
- addIcon(&coord, TheGlobalData->m_debugVisibilityTileWidth,
- TheGlobalData->m_debugVisibilityTileDuration,
- TheGlobalData->m_debugVisibilityTargettableColor);
- }
- }
- #endif
- return m_visionRange;
- }
- //-------------------------------------------------------------------------------------------------
- void Object::setVisionRange( Real newVisionRange )
- {
- m_visionRange = newVisionRange;
- }
- //-------------------------------------------------------------------------------------------------
- Real Object::getShroudClearingRange() const
- {
- Real shroudClearingRange=m_shroudClearingRange;
- if ((getStatusBits() & OBJECT_STATUS_UNDER_CONSTRUCTION))
- { //structures under construction have limited vision range. For now, base it
- //on the geometry extents so the structure can only see itself.
- shroudClearingRange = getGeometryInfo().getBoundingCircleRadius();
- }
- #if defined(_DEBUG) || defined(_INTERNAL)
- if (TheGlobalData->m_debugVisibility)
- {
- Vector3 pos(shroudClearingRange, 0, 0);
- for (int i = 0; i < TheGlobalData->m_debugVisibilityTileCount; ++i)
- {
- pos.Rotate_Z(1.0f * i / TheGlobalData->m_debugVisibilityTileCount * 2 * PI);
- Coord3D coord = { pos.X + getPosition()->x, pos.Y + getPosition()->y, pos.Z + getPosition()->z };
- addIcon(&coord, TheGlobalData->m_debugVisibilityTileWidth,
- TheGlobalData->m_debugVisibilityTileDuration,
- TheGlobalData->m_debugVisibilityDeshroudColor);
- }
- }
- #endif
- return shroudClearingRange;
- }
- //-------------------------------------------------------------------------------------------------
- void Object::setShroudClearingRange( Real newShroudClearingRange )
- {
- if( newShroudClearingRange != m_shroudClearingRange )
- {
- // The partition cell refresh is a slow operation, so only do it if you really have to.
- // Range change is a valid reason to relook.
- m_shroudClearingRange = newShroudClearingRange;
- /*
- Complete and total monkey hack fix.
- The problem: newObject doesn't get an initial pos, so all objects start at 0,0,0.
- Most code paths instantly move 'em to a good pos, but in some cases, that is too late:
- If we have search-and-destroy battle plan, we will apply it at that point, and clear out
- a vision range based on our current (wrong) location. Doh!
- So, this just sez: if you are at 0,0,0, don't call handlePartitionCellMaintenance()... since
- you will either (1) be moved elsewhere immediately, thus forcing it to be called via
- another route anyway, or (2) not be moved, which means you are a very naughty and worthless
- object anyway and we should just ignore you.
- Proper fix for next version is to require initial pos to be passed in to newObject so that
- all objects can start at their proper initial position from the start of the ctor.
- (srj)
- */
- const Coord3D* pos = getPosition();
- if (pos->x || pos->y || pos->z)
- {
- handlePartitionCellMaintenance();
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- Real Object::getShroudRange() const
- {
- #if defined(_DEBUG) || defined(_INTERNAL)
- if (TheGlobalData->m_debugVisibility)
- {
- Vector3 pos(m_shroudRange, 0, 0);
- for (int i = 0; i < TheGlobalData->m_debugVisibilityTileCount; ++i)
- {
- pos.Rotate_Z(1.0f * i / TheGlobalData->m_debugVisibilityTileCount * 2 * PI);
- Coord3D coord = { pos.X + getPosition()->x, pos.Y + getPosition()->y, pos.Z + getPosition()->z };
- addIcon(&coord, TheGlobalData->m_debugVisibilityTileWidth,
- TheGlobalData->m_debugVisibilityTileDuration,
- TheGlobalData->m_debugVisibilityGapColor);
- }
- }
- #endif
- return m_shroudRange;
- }
- //-------------------------------------------------------------------------------------------------
- void Object::setShroudRange( Real newShroudRange )
- {
- m_shroudRange = newShroudRange;
- }
- //-------------------------------------------------------------------------------------------------
- /** Given a special power template, find the module in the object that can implement it.
- * There can be at most one */
- //-------------------------------------------------------------------------------------------------
- SpecialPowerModuleInterface *Object::getSpecialPowerModule( const SpecialPowerTemplate *specialPowerTemplate ) const
- {
- // sanity
- if( specialPowerTemplate == NULL )
- return NULL;
- // search the modules for the one with the matching template
- for (BehaviorModule** m = m_behaviors; *m; ++m)
- {
- SpecialPowerModuleInterface* sp = (*m)->getSpecialPower();
- if (!sp)
- continue;
- if( sp->isModuleForPower( specialPowerTemplate ) )
- return sp;
- }
- return NULL;
- }
- //-------------------------------------------------------------------------------------------------
- /** Execute special power */
- //-------------------------------------------------------------------------------------------------
- void Object::doSpecialPower( const SpecialPowerTemplate *specialPowerTemplate, UnsignedInt commandOptions, Bool forced )
- {
- if (isDisabled())
- return;
-
- // sanity
- if( !forced && TheSpecialPowerStore->canUseSpecialPower( this, specialPowerTemplate ) == FALSE )
- return;
- // get the module and execute
- SpecialPowerModuleInterface *mod = getSpecialPowerModule( specialPowerTemplate );
- if( mod )
- mod->doSpecialPower( commandOptions );
- }
- //-------------------------------------------------------------------------------------------------
- /** Execute special power */
- //-------------------------------------------------------------------------------------------------
- void Object::doSpecialPowerAtObject( const SpecialPowerTemplate *specialPowerTemplate, Object *obj, UnsignedInt commandOptions, Bool forced )
- {
- if (isDisabled())
- return;
-
- // sanity
- if( !forced && TheSpecialPowerStore->canUseSpecialPower( this, specialPowerTemplate ) == FALSE )
- return;
- // get the module and execute
- SpecialPowerModuleInterface *mod = getSpecialPowerModule( specialPowerTemplate );
- if( mod )
- mod->doSpecialPowerAtObject( obj, commandOptions );
- }
- //-------------------------------------------------------------------------------------------------
- /** Execute special power */
- //-------------------------------------------------------------------------------------------------
- void Object::doSpecialPowerAtLocation( const SpecialPowerTemplate *specialPowerTemplate,
- const Coord3D *loc, UnsignedInt commandOptions, Bool forced )
- {
- if (isDisabled())
- return;
-
- // sanity
- if( !forced && TheSpecialPowerStore->canUseSpecialPower( this, specialPowerTemplate ) == FALSE )
- return;
- // get the module and execute
- SpecialPowerModuleInterface *mod = getSpecialPowerModule( specialPowerTemplate );
- if( mod )
- mod->doSpecialPowerAtLocation( loc, commandOptions );
- }
- //-------------------------------------------------------------------------------------------------
- /** Execute special power */
- //-------------------------------------------------------------------------------------------------
- void Object::doSpecialPowerAtMultipleLocations( const SpecialPowerTemplate *specialPowerTemplate,
- const Coord3D *locations, Int locCount, UnsignedInt commandOptions, Bool forced )
- {
- if (isDisabled())
- return;
-
- // sanity
- if( !forced && TheSpecialPowerStore->canUseSpecialPower( this, specialPowerTemplate ) == FALSE )
- return;
- // get the module and execute
- SpecialPowerModuleInterface *mod = getSpecialPowerModule( specialPowerTemplate );
- if( mod )
- mod->doSpecialPowerAtMultipleLocations( locations, locCount, commandOptions );
- }
- //-------------------------------------------------------------------------------------------------
- /** Execute command button ability */
- //-------------------------------------------------------------------------------------------------
- void Object::doCommandButton( const CommandButton *commandButton, CommandSourceType cmdSource )
- {
- if (isDisabled())
- return;
-
- AIUpdateInterface *ai = getAIUpdateInterface();
- if( commandButton )
- {
- switch( commandButton->getCommandType() )
- {
- case GUI_COMMAND_SPECIAL_POWER:
- if( commandButton->getSpecialPowerTemplate() )
- {
- CommandOption commandOptions = (CommandOption)(commandButton->getOptions() | COMMAND_FIRED_BY_SCRIPT);
- doSpecialPower( commandButton->getSpecialPowerTemplate(), commandOptions, cmdSource == CMD_FROM_SCRIPT );
- }
- break;
- case GUI_COMMAND_STOP:
- if( ai )
- {
- ai->aiIdle( cmdSource );
- }
- break;
- case GUI_COMMAND_SWITCH_WEAPON:
- {
- WeaponSlotType weaponSlot = commandButton->getWeaponSlot();
- // GUI_COMMAND_SWITCH_WEAPON switches until un-switched, or switched to something else.
- this->setWeaponLock( weaponSlot, LOCKED_PERMANENTLY );
- break;
- }
- case GUI_COMMAND_FIRE_WEAPON:
- {
- WeaponSlotType weaponSlot = commandButton->getWeaponSlot();
- // GUI_COMMAND_FIRE_WEAPON merely fires till the weapon is empty or the attack is "done"
- this->setWeaponLock( weaponSlot, LOCKED_TEMPORARILY );
- break;
- }
- case GUI_COMMAND_OBJECT_UPGRADE:
- case GUI_COMMAND_PLAYER_UPGRADE:
- {
- const UpgradeTemplate *upgradeT = commandButton->getUpgradeTemplate();
- DEBUG_ASSERTCRASH( upgradeT, ("Undefined upgrade '%s' in player upgrade command\n", "UNKNOWN") );
- // sanity
- if( upgradeT == NULL )
- break;
- if( upgradeT->getUpgradeType() == UPGRADE_TYPE_OBJECT )
- {
- if( hasUpgrade( upgradeT ) || !affectedByUpgrade( upgradeT ) )
- break;
- }
- // producer must have a production update
- ProductionUpdateInterface *pu = getProductionUpdateInterface();
- if( pu == NULL )
- break;
- // queue the upgrade "research"
- pu->queueUpgrade( upgradeT );
- }
- break;
- case GUI_COMMAND_UNIT_BUILD:
- case GUI_COMMAND_DOZER_CONSTRUCT: {
- const ThingTemplate *tt = commandButton->getThingTemplate();
- ProductionUpdateInterface *pu = this->getProductionUpdateInterface();
- if (pu && tt) {
- pu->queueCreateUnit( tt, pu->requestUniqueUnitID());
- }
- break;
- }
- case GUI_COMMAND_HACK_INTERNET:{
- if( ai )
- {
- ai->aiHackInternet( cmdSource );
- }
- break;
- }
-
- //Feel free to implement object based command buttons.
- case GUI_COMMAND_COMBATDROP:
- case GUI_COMMAND_DOZER_CONSTRUCT_CANCEL:
- case GUI_COMMAND_CANCEL_UNIT_BUILD:
- case GUI_COMMAND_CANCEL_UPGRADE:
- case GUI_COMMAND_ATTACK_MOVE:
- case GUI_COMMAND_GUARD:
- case GUI_COMMAND_GUARD_WITHOUT_PURSUIT:
- case GUI_COMMAND_GUARD_FLYING_UNITS_ONLY:
- case GUI_COMMAND_WAYPOINTS:
- case GUI_COMMAND_EXIT_CONTAINER:
- case GUI_COMMAND_EVACUATE:
- case GUI_COMMAND_EXECUTE_RAILED_TRANSPORT:
- case GUI_COMMAND_BEACON_DELETE:
- case GUI_COMMAND_SET_RALLY_POINT:
- case GUI_COMMAND_SELL:
- case GUI_COMMAND_TOGGLE_OVERCHARGE:
- #ifdef ALLOW_SURRENDER
- case GUI_COMMAND_POW_RETURN_TO_PRISON:
- #endif
- case GUICOMMANDMODE_HIJACK_VEHICLE:
- case GUICOMMANDMODE_CONVERT_TO_CARBOMB:
- #ifdef ALLOW_SURRENDER
- case GUICOMMANDMODE_PICK_UP_PRISONER:
- #endif
- default:
- break;
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- /** Execute command button ability directed at an object target */
- //-------------------------------------------------------------------------------------------------
- void Object::doCommandButtonAtObject( const CommandButton *commandButton, Object *obj, CommandSourceType cmdSource )
- {
- if (isDisabled())
- return;
-
- AIUpdateInterface *ai = getAIUpdateInterface();
- if( commandButton )
- {
- switch( commandButton->getCommandType() )
- {
- case GUI_COMMAND_COMBATDROP:
- if( ai )
- {
- ai->aiCombatDrop( obj, *(obj->getPosition()), cmdSource );
- }
- break;
- case GUI_COMMAND_SPECIAL_POWER:
- {
- if( commandButton->getSpecialPowerTemplate() )
- {
- CommandOption commandOptions = (CommandOption)(commandButton->getOptions() | COMMAND_FIRED_BY_SCRIPT);
- doSpecialPowerAtObject( commandButton->getSpecialPowerTemplate(), obj, commandOptions, cmdSource == CMD_FROM_SCRIPT );
- }
- break;
- }
- case GUI_COMMAND_STOP:
- if( ai )
- {
- ai->aiIdle( cmdSource );
- }
- break;
- case GUICOMMANDMODE_HIJACK_VEHICLE:
- if ( ai )
- {
- ai->aiEnter(obj, cmdSource);
- }
- break;
-
- //Feel free to implement object based command buttons.
- case GUI_COMMAND_DOZER_CONSTRUCT:
- case GUI_COMMAND_DOZER_CONSTRUCT_CANCEL:
- case GUI_COMMAND_UNIT_BUILD:
- case GUI_COMMAND_CANCEL_UNIT_BUILD:
- case GUI_COMMAND_PLAYER_UPGRADE:
- case GUI_COMMAND_OBJECT_UPGRADE:
- case GUI_COMMAND_CANCEL_UPGRADE:
- case GUI_COMMAND_ATTACK_MOVE:
- case GUI_COMMAND_GUARD:
- case GUI_COMMAND_GUARD_WITHOUT_PURSUIT:
- case GUI_COMMAND_GUARD_FLYING_UNITS_ONLY:
- case GUI_COMMAND_WAYPOINTS:
- case GUI_COMMAND_EXIT_CONTAINER:
- case GUI_COMMAND_EVACUATE:
- case GUI_COMMAND_EXECUTE_RAILED_TRANSPORT:
- case GUI_COMMAND_BEACON_DELETE:
- case GUI_COMMAND_SET_RALLY_POINT:
- case GUI_COMMAND_SELL:
- case GUI_COMMAND_FIRE_WEAPON:
- case GUI_COMMAND_HACK_INTERNET:
- case GUI_COMMAND_TOGGLE_OVERCHARGE:
- #ifdef ALLOW_SURRENDER
- case GUI_COMMAND_POW_RETURN_TO_PRISON:
- #endif
- case GUI_COMMAND_SWITCH_WEAPON:
- case GUICOMMANDMODE_CONVERT_TO_CARBOMB:
- #ifdef ALLOW_SURRENDER
- case GUICOMMANDMODE_PICK_UP_PRISONER:
- #endif
- default:
- break;
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- /** Execute command button ability directed at a location */
- //-------------------------------------------------------------------------------------------------
- void Object::doCommandButtonAtPosition( const CommandButton *commandButton, const Coord3D *pos, CommandSourceType cmdSource )
- {
- if (isDisabled())
- return;
-
- AIUpdateInterface *ai = getAIUpdateInterface();
- if( commandButton )
- {
- switch( commandButton->getCommandType() )
- {
- case GUI_COMMAND_SPECIAL_POWER:
- {
- if( commandButton->getSpecialPowerTemplate() )
- {
- CommandOption commandOptions = (CommandOption)(commandButton->getOptions() | COMMAND_FIRED_BY_SCRIPT);
- doSpecialPowerAtLocation( commandButton->getSpecialPowerTemplate(), pos, commandOptions, cmdSource == CMD_FROM_SCRIPT );
- }
- break;
- }
- case GUI_COMMAND_ATTACK_MOVE:
- if( ai )
- {
- ai->aiAttackMoveToPosition( pos, commandButton->getMaxShotsToFire(), cmdSource );
- }
- break;
- case GUI_COMMAND_STOP:
- if( ai )
- {
- ai->aiIdle( cmdSource );
- }
- break;
- case GUI_COMMAND_DOZER_CONSTRUCT:
- TheBuildAssistant->buildObjectNow( this, commandButton->getThingTemplate(), pos, 0.0f, getControllingPlayer() );
- break;
- case GUI_COMMAND_DOZER_CONSTRUCT_CANCEL:
- case GUI_COMMAND_UNIT_BUILD:
- case GUI_COMMAND_CANCEL_UNIT_BUILD:
- case GUI_COMMAND_PLAYER_UPGRADE:
- case GUI_COMMAND_OBJECT_UPGRADE:
- case GUI_COMMAND_CANCEL_UPGRADE:
- case GUI_COMMAND_GUARD:
- case GUI_COMMAND_GUARD_WITHOUT_PURSUIT:
- case GUI_COMMAND_GUARD_FLYING_UNITS_ONLY:
- case GUI_COMMAND_WAYPOINTS:
- case GUI_COMMAND_EXIT_CONTAINER:
- case GUI_COMMAND_EVACUATE:
- case GUI_COMMAND_EXECUTE_RAILED_TRANSPORT:
- case GUI_COMMAND_BEACON_DELETE:
- case GUI_COMMAND_SET_RALLY_POINT:
- case GUI_COMMAND_SELL:
- case GUI_COMMAND_FIRE_WEAPON:
- case GUI_COMMAND_HACK_INTERNET:
- case GUI_COMMAND_TOGGLE_OVERCHARGE:
- #ifdef ALLOW_SURRENDER
- case GUI_COMMAND_POW_RETURN_TO_PRISON:
- #endif
- case GUI_COMMAND_COMBATDROP:
- case GUI_COMMAND_SWITCH_WEAPON:
- case GUICOMMANDMODE_HIJACK_VEHICLE:
- case GUICOMMANDMODE_CONVERT_TO_CARBOMB:
- #ifdef ALLOW_SURRENDER
- case GUICOMMANDMODE_PICK_UP_PRISONER:
- #endif
- default:
- break;
- }
- }
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void Object::clearLeechRangeModeForAllWeapons()
- {
- m_weaponSet.clearLeechRangeModeForAllWeapons();
- }
- // ------------------------------------------------------------------------------------------------
- /** Search our update modules for a production update interface and return it if one is found */
- // ------------------------------------------------------------------------------------------------
- ProductionUpdateInterface* Object::getProductionUpdateInterface( void )
- {
- ProductionUpdateInterface *pui;
- // tell our update modules that we intend to do this special power.
- for( BehaviorModule** u = m_behaviors; *u; ++u )
- {
- pui = (*u)->getProductionUpdateInterface();
- if( pui )
- return pui;
- } // end for
- return NULL;
- } // end getProductionUpdateInterface
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- DockUpdateInterface *Object::getDockUpdateInterface( void )
- {
- DockUpdateInterface *dock = NULL;
- for( BehaviorModule **u = m_behaviors; *u; ++u )
- {
- if( (dock = (*u)->getDockUpdateInterface()) != NULL )
- return dock;
- }
- return NULL;
- } // end getDockUpdateInterface
- // ------------------------------------------------------------------------------------------------
- // Search our special power modules for a specific one.
- // ------------------------------------------------------------------------------------------------
- SpecialPowerModuleInterface* Object::findSpecialPowerModuleInterface( SpecialPowerType type ) const
- {
- for (BehaviorModule** m = m_behaviors; *m; ++m)
- {
- SpecialPowerModuleInterface* sp = (*m)->getSpecialPower();
- if (!sp)
- continue;
- const SpecialPowerTemplate *spTemplate = sp->getSpecialPowerTemplate();
- if (spTemplate && spTemplate->getSpecialPowerType() == type)
- {
- return sp;
- }
- }
- return NULL;
- }
- // ------------------------------------------------------------------------------------------------
- /** Get spawn behavior interface from object */
- // ------------------------------------------------------------------------------------------------
- SpawnBehaviorInterface* Object::getSpawnBehaviorInterface() const
- {
- for (BehaviorModule** m = m_behaviors; *m; ++m)
- {
- SpawnBehaviorInterface *sbi = (*m)->getSpawnBehaviorInterface();
- if( sbi )
- {
- return sbi;
- }
- }
- return NULL;
- } // end getSpawnBehaviorInterfaceFromObject
- // ------------------------------------------------------------------------------------------------
- // Simply find the special power module that is currently allowing plotting of positions to target.
- // ------------------------------------------------------------------------------------------------
- SpecialPowerUpdateInterface* Object::findSpecialPowerWithOverridableDestinationActive( SpecialPowerType type ) const
- {
- for( BehaviorModule** u = m_behaviors; *u; ++u )
- {
- SpecialPowerUpdateInterface *spInterface = (*u)->getSpecialPowerUpdateInterface();
- if( spInterface )
- {
- if( spInterface->doesSpecialPowerHaveOverridableDestinationActive() )
- {
- return spInterface;
- }
- }
- } // end for
- return NULL;
- }
- // ------------------------------------------------------------------------------------------------
- // Search our special ability updates for a specific one.
- // ------------------------------------------------------------------------------------------------
- SpecialAbilityUpdate* Object::findSpecialAbilityUpdate( SpecialPowerType type ) const
- {
- for( BehaviorModule** u = m_behaviors; *u; ++u )
- {
- SpecialPowerUpdateInterface *spInterface = (*u)->getSpecialPowerUpdateInterface();
- if( spInterface && spInterface->isSpecialAbility() )
- {
- SpecialAbilityUpdate *spUpdate = (SpecialAbilityUpdate*)spInterface;
- if( spUpdate->getSpecialPowerType() == type )
- {
- return spUpdate;
- }
- }
- } // end for
- return NULL;
- }
- // ------------------------------------------------------------------------------------------------
- SpecialPowerCompletionDie* Object::findSpecialPowerCompletionDie() const
- {
- static NameKeyType key_SpecialPowerCompletionDie = NAMEKEY("SpecialPowerCompletionDie");
- return (SpecialPowerCompletionDie*)findModule(key_SpecialPowerCompletionDie);
- }
- // ------------------------------------------------------------------------------------------------
- Int Object::getNumConsecutiveShotsFiredAtTarget( const Object *victim ) const
- {
- return m_firingTracker ? m_firingTracker->getNumConsecutiveShotsAtVictim( victim ) : 0;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- Bool Object::getSingleLogicalBonePosition(const char* boneName, Coord3D* position, Matrix3D* transform) const
- {
- if (m_drawable && m_drawable->getPristineBonePositions( boneName, 0, position, transform, 1 ) == 1 )
- {
- m_drawable->convertBonePosToWorldPos( position, transform, position, transform );
- return true;
- }
- else
- {
- if (position)
- *position = *getPosition();
- if (transform)
- *transform = *getTransformMatrix();
- return false;
- }
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- Bool Object::getSingleLogicalBonePositionOnTurret( WhichTurretType whichTurret, const char* boneName, Coord3D* position, Matrix3D* transform ) const
- {
- Coord3D turretPosition;
- Coord3D bonePosition;
- if( getDrawable() == NULL || getAI() == NULL )
- return FALSE;
- // We need to find the TurretBone's pristine position.
- getDrawable()->getProjectileLaunchOffset( PRIMARY_WEAPON, 1, NULL, whichTurret, &turretPosition, NULL );
- // And the required bone's pristine position
- if( getDrawable()->getPristineBonePositions(boneName, 0, &bonePosition, NULL, 1) != 1 )
- return FALSE;
- //Then we mojo the Logic position of the required bone like Missile firing does. Using the logic twist of the turret
- Real turretRotation;
- getAI()->getTurretRotAndPitch( whichTurret, &turretRotation, NULL );
- Matrix3D boneOffset(TRUE);// This will be from the turret to the requested bone
- // Vector3 bonePositionVector( bonePosition.x - turretPosition.x,
- // bonePosition.y - turretPosition.y,
- // bonePosition.z - turretPosition.z );
- Vector3 bonePositionVector( bonePosition.x,
- bonePosition.y,
- bonePosition.z );
- boneOffset.Translate(bonePositionVector);
- Matrix3D turnAdjustment(TRUE);// this is the turret twist to be applied to the final answer
- turnAdjustment.Translate( turretPosition.x, turretPosition.y, turretPosition.z );
- turnAdjustment.In_Place_Pre_Rotate_Z(turretRotation);
- turnAdjustment.Translate( -turretPosition.x, -turretPosition.y, -turretPosition.z );
- Matrix3D boneLogicTransform;
- boneLogicTransform.mul( turnAdjustment, boneOffset );
- Matrix3D worldTransform;
- convertBonePosToWorldPos(NULL, &boneLogicTransform, NULL, &worldTransform);
- Vector3 tmp = worldTransform.Get_Translation();
- Coord3D worldPos;
- worldPos.x = tmp.X;
- worldPos.y = tmp.Y;
- worldPos.z = tmp.Z;
- if( position )
- *position = worldPos;
- if( transform )
- *transform = worldTransform;
- return TRUE;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- Int Object::getMultiLogicalBonePosition(const char* boneNamePrefix, Int maxBones,
- Coord3D* positions, Matrix3D* transforms,
- Bool convertToWorld ) const
- {
- Int count;
- if (m_drawable && (count = m_drawable->getPristineBonePositions( boneNamePrefix, 1, positions, transforms, maxBones )) > 0 )
- {
- if( convertToWorld )
- {
- for (Int i = 0; i < count; ++i)
- m_drawable->convertBonePosToWorldPos( positions ? &positions[i] : NULL, transforms ? &transforms[i] : NULL, positions ? &positions[i] : NULL, transforms ? &transforms[i] : NULL );
- }
- return count;
- }
- else
- {
- return 0;
- }
- }
- //=============================================================================
- Bool Object::canProduceUpgrade( const UpgradeTemplate *upgrade )
- {
- // We need to have the button to make the upgrade. CommandSets are a weird Logic/Client hybrid.
- const CommandSet *set = TheControlBar->findCommandSet(getCommandSetString());
-
- for( Int buttonIndex = 0; buttonIndex < MAX_COMMANDS_PER_SET; buttonIndex++ )
- {
- const CommandButton *button = set->getCommandButton(buttonIndex);
- if( button
- && ( (button->getCommandType() == GUI_COMMAND_PLAYER_UPGRADE) || (button->getCommandType() == GUI_COMMAND_OBJECT_UPGRADE) ) // Or else a button that requires an upgrade will appear the same as a button that gives an upgrade
- && button->getUpgradeTemplate()
- && (button->getUpgradeTemplate() == upgrade)
- )
- return TRUE; // getUpgradeTemplate only returns something if it is actually an upgrade
- }
-
- return FALSE;// Cheatin' punk.
- }
-
- //=============================================================================
- const AsciiString& Object::getCommandSetString() const
- {
- if (m_commandSetStringOverride.isNotEmpty())
- return m_commandSetStringOverride;
- return getTemplate()->friend_getCommandSetString();
- }
- //=============================================================================
- // Object::defect, and related methods =
- //=============================================================================
- void Object::defect( Team* newTeam, UnsignedInt detectionTime )
- {
- if ( isContained() ) //@todo (KRIS?) make contained units unselectable, until then... lorenzen
- {
- return;
- }
- Player *player = getControllingPlayer();
- if ( !player )
- return;
- Team* myTeam = player->getDefaultTeam();
- if ( myTeam == newTeam ) // can't defect from my own team, that would be silly
- return;
-
- // things that are under construction, or sold, cannot defect.
- if (testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION) ||
- testStatus(OBJECT_STATUS_SOLD))
- {
- return;
- }
- // Before switch ////////////////////////////////////////
- //Design says:
- ProductionUpdateInterface *production = getProductionUpdateInterface();
- if ( production )
- {
- production->cancelAndRefundAllProduction();
- }
- // pop it up on the radar, so as to warn those who care
- // do this first, since after setTeam() the infiltrator
- // becomes the controllingplayer, not me
- // But don't do this is if the new team is not a real team. "'Enemy' infiltration" wouldn't make
- // sense, and we are probably just reverting a cave or something.
- if( friend_getRadarData() && newTeam->getControllingPlayer()->isPlayableSide() && myTeam->getControllingPlayer()->isPlayableSide())
- {
- TheRadar->tryInfiltrationEvent( this );
- }
- friend_setUndetectedDefector( detectionTime > 0 );
- if (m_defectionHelper)
- m_defectionHelper->startDefectionTimer(detectionTime);
- // Switch ////////////////////////////////////////
- setTeam( newTeam );
- // After switch ////////////////////////////////////////
-
- AIUpdateInterface *ai = getAI();
- handlePartitionCellMaintenance();// to clear the shoud for my new master
- if ( ai )
- {
- ai->aiIdle( CMD_FROM_AI );
- }
- // Play our sound indicating we've been defected. (weird verbage, but true.)
- AudioEventRTS voiceDefect = *getTemplate()->getVoiceDefect();
- voiceDefect.setObjectID(getID());
- TheAudio->addAudioEvent(&voiceDefect);
- //make the new recruit the only selected thing, awaiting new command to move, attack, etc...
- Drawable *dr = getDrawable();
- if (dr)
- {
- dr->flashAsSelected(); //This is the first of several flashes which get cue'd by doDefectorUpdateStuff()
- AudioEventRTS defectorTimerSound = TheAudio->getMiscAudio()->m_defectorTimerTickSound;
- defectorTimerSound.setObjectID( getID() );
- TheAudio->addAudioEvent(&defectorTimerSound);
- }
-
- ContainModuleInterface *ct = getContain();
- if( ct && ct->isKickOutOnCapture() )
- {
- // Caves really really don't want to do this.
- ct->removeAllContained( TRUE );
- }
- // if it has parking places, defect anything parked there.
- for (BehaviorModule** i = getBehaviorModules(); *i; ++i)
- {
- ParkingPlaceBehaviorInterface* pp = (*i)->getParkingPlaceBehaviorInterface();
- if (pp)
- {
- pp->defectAllParkedUnits(newTeam, detectionTime);
- break;
- }
- }
- // defect any mines that are owned by this structure, right now.
- // unfortunately, structures don't keep list of mines they own, so we must do
- // this the hard way :-( [fortunately, this doens't happen very often, so this
- // is probably an acceptable, if icky, solution.] (srj)
- for (Object* mine = TheGameLogic->getFirstObject(); mine; mine = mine->getNextObject())
- {
- if (mine->isKindOf(KINDOF_MINE))
- {
- if (mine->getProducerID() == this->getID())
- {
- mine->setTeam(newTeam);
- }
- }
- }
- }
- //=============================================================================
- // Object::goInvulnerable
- //=============================================================================
- void Object::goInvulnerable( UnsignedInt time )
- {
- const Bool WITHOUT_DEFECTOR_FX = FALSE;
- friend_setUndetectedDefector( time > 0 );
- if (m_defectionHelper)
- m_defectionHelper->startDefectionTimer(time, WITHOUT_DEFECTOR_FX);
- }
- // ------------------------------------------------------------------------------------------------
- /** Return the radar priority for this object type */
- // ------------------------------------------------------------------------------------------------
- RadarPriorityType Object::getRadarPriority( void ) const
- {
- RadarPriorityType priority = RADAR_PRIORITY_INVALID;
- // first, get the priority at the thing template level
- priority = getTemplate()->getDefaultRadarPriority();
- //
- // there are some objects that we want to show up on the radar when they have
- // certain properties ... here we will check for those properties unless the INI
- // setting of "not on radar" has been manually entered which explicitly forbids an
- // object from being on the radar ... by default objects get an "invalid" priority
- // on the radar and this means that we are free to decide one here if we want
- //
- if( priority == RADAR_PRIORITY_INVALID )
- {
- // objects that are "garrisonable" show up on the radar
- ContainModuleInterface *cmi = getContain();
- if( cmi && cmi->isGarrisonable() )
- priority = RADAR_PRIORITY_STRUCTURE;
- // objects that are "capturable" show up on the radar
- if( isKindOf( KINDOF_CAPTURABLE ) )
- priority = RADAR_PRIORITY_STRUCTURE;
- } // end if
- // Carbombs will show up as units regardless of their default priority
- if ( testStatus( OBJECT_STATUS_IS_CARBOMB ) )
- priority = RADAR_PRIORITY_UNIT;
- // return the priority we're going to use
- return priority;
- } // end getRadarPriority
- // ------------------------------------------------------------------------------------------------
- AIGroup *Object::getGroup(void)
- {
- return m_group;
- }
- //-------------------------------------------------------------------------------------------------
- void Object::enterGroup( AIGroup *group )
- {
- // DEBUG_LOG(("***AIGROUP %x involved in enterGroup on %x\n", group, this));
- // if we are in another group, remove ourselves from it first
- leaveGroup();
- m_group = group;
- }
- //-------------------------------------------------------------------------------------------------
- void Object::leaveGroup( void )
- {
- // DEBUG_LOG(("***AIGROUP %x involved in leaveGroup on %x\n", m_group, this));
- // if we are in a group, remove ourselves from it
- if (m_group)
- {
- // to avoid recursion, set m_group to NULL before removing
- AIGroup *group = m_group;
- m_group = NULL;
- group->remove( this );
- }
- }
|