Object.cpp 169 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415
  1. /*
  2. ** Command & Conquer Generals(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // FILE Object.cpp ////////////////////////////////////////////////////////////////////////////////
  24. // Simple base object
  25. // Author: Michael S. Booth, October 2000
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. // INCLUDES ///////////////////////////////////////////////////////////////////////////////////////
  28. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  29. #define DEFINE_WEAPONCONDITIONMAP
  30. #include "Common/BitFlagsIO.h"
  31. #include "Common/BuildAssistant.h"
  32. #include "Common/Dict.h"
  33. #include "Common/GameEngine.h"
  34. #include "Common/GameState.h"
  35. #include "Common/ModuleFactory.h"
  36. #include "Common/Player.h"
  37. #include "Common/PlayerList.h"
  38. #include "Common/Radar.h"
  39. #include "Common/SpecialPower.h"
  40. #include "Common/Team.h"
  41. #include "Common/ThingFactory.h"
  42. #include "Common/ThingTemplate.h"
  43. #include "Common/Upgrade.h"
  44. #include "Common/WellKnownKeys.h"
  45. #include "Common/Xfer.h"
  46. #include "Common/XferCRC.h"
  47. #include "Common/PerfTimer.h"
  48. #include "GameClient/Anim2D.h"
  49. #include "GameClient/ControlBar.h"
  50. #include "GameClient/Drawable.h"
  51. #include "GameClient/Eva.h"
  52. #include "GameClient/InGameUI.h"
  53. #include "GameLogic/AI.h"
  54. #include "GameLogic/AIPathfind.h"
  55. #include "GameLogic/ExperienceTracker.h"
  56. #include "GameLogic/FiringTracker.h"
  57. #include "GameLogic/GameLogic.h"
  58. #include "GameLogic/Locomotor.h"
  59. #include "GameLogic/Module/AIUpdate.h"
  60. #include "GameLogic/Module/AutoHealBehavior.h"
  61. #include "GameLogic/Module/BehaviorModule.h"
  62. #include "GameLogic/Module/BodyModule.h"
  63. #include "GameLogic/Module/CollideModule.h"
  64. #include "GameLogic/Module/ContainModule.h"
  65. #include "GameLogic/Module/CreateModule.h"
  66. #include "GameLogic/Module/DamageModule.h"
  67. #include "GameLogic/Module/DeletionUpdate.h"
  68. #include "GameLogic/Module/DestroyModule.h"
  69. #include "GameLogic/Module/DieModule.h"
  70. #include "GameLogic/Module/ObjectDefectionHelper.h"
  71. #include "GameLogic/Module/ObjectRepulsorHelper.h"
  72. #include "GameLogic/Module/ObjectSMCHelper.h"
  73. #include "GameLogic/Module/ObjectWeaponStatusHelper.h"
  74. #include "GameLogic/Module/OverchargeBehavior.h"
  75. #include "GameLogic/Module/PhysicsUpdate.h"
  76. #include "GameLogic/Module/PowerPlantUpgrade.h"
  77. #include "GameLogic/Module/ProductionUpdate.h"
  78. #include "GameLogic/Module/RadarUpgrade.h"
  79. #include "GameLogic/Module/SpawnBehavior.h"
  80. #include "GameLogic/Module/SpecialPowerModule.h"
  81. #include "GameLogic/Module/SpecialAbilityUpdate.h"
  82. #include "GameLogic/Module/ToppleUpdate.h"
  83. #include "GameLogic/Module/UpdateModule.h"
  84. #include "GameLogic/Module/UpgradeModule.h"
  85. #include "GameLogic/Object.h"
  86. #include "GameLogic/PartitionManager.h"
  87. #include "GameLogic/PolygonTrigger.h"
  88. #include "GameLogic/ScriptEngine.h"
  89. #include "GameLogic/Weapon.h"
  90. #include "GameLogic/WeaponSet.h"
  91. #include "GameLogic/Module/RadarUpdate.h"
  92. #include "GameLogic/Module/PowerPlantUpdate.h"
  93. #include "Common/CRCDebug.h"
  94. #include "Common/MiscAudio.h"
  95. #ifdef _INTERNAL
  96. // for occasional debugging...
  97. //#pragma optimize("", off)
  98. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  99. #endif
  100. #ifdef DEBUG_OBJECT_ID_EXISTS
  101. ObjectID TheObjectIDToDebug = INVALID_ID;
  102. #endif
  103. // ------------------------------------------------------------------------------------------------
  104. static const ModelConditionFlags s_allWeaponFireFlags[WEAPONSLOT_COUNT] =
  105. {
  106. MAKE_MODELCONDITION_MASK5(
  107. MODELCONDITION_FIRING_A,
  108. MODELCONDITION_BETWEEN_FIRING_SHOTS_A,
  109. MODELCONDITION_RELOADING_A,
  110. MODELCONDITION_PREATTACK_A,
  111. MODELCONDITION_USING_WEAPON_A
  112. ),
  113. MAKE_MODELCONDITION_MASK5(
  114. MODELCONDITION_FIRING_B,
  115. MODELCONDITION_BETWEEN_FIRING_SHOTS_B,
  116. MODELCONDITION_RELOADING_B,
  117. MODELCONDITION_PREATTACK_B,
  118. MODELCONDITION_USING_WEAPON_B
  119. ),
  120. MAKE_MODELCONDITION_MASK5(
  121. MODELCONDITION_FIRING_C,
  122. MODELCONDITION_BETWEEN_FIRING_SHOTS_C,
  123. MODELCONDITION_RELOADING_C,
  124. MODELCONDITION_PREATTACK_C,
  125. MODELCONDITION_USING_WEAPON_C
  126. )
  127. };
  128. //-------------------------------------------------------------------------------------------------
  129. extern void addIcon(const Coord3D *pos, Real width, Int numFramesDuration, RGBColor color);
  130. //-------------------------------------------------------------------------------------------------
  131. //-------------------------------------------------------------------------------------------------
  132. #ifdef DEBUG_LOGGING
  133. AsciiString DescribeObject(const Object *obj)
  134. {
  135. if (!obj)
  136. return "<No Object>";
  137. AsciiString ret;
  138. if (obj->getName().isNotEmpty())
  139. {
  140. ret.format("Object %d (%s) [%s, owned by player %d (%ls)]",
  141. obj->getID(), obj->getName().str(), obj->getTemplate()->getName().str(),
  142. obj->getControllingPlayer()->getPlayerIndex(),
  143. obj->getControllingPlayer()->getPlayerDisplayName().str());
  144. }
  145. else
  146. {
  147. ret.format("Object %d [%s, owned by player %d (%ls)]",
  148. obj->getID(), obj->getTemplate()->getName().str(),
  149. obj->getControllingPlayer()->getPlayerIndex(),
  150. obj->getControllingPlayer()->getPlayerDisplayName().str());
  151. }
  152. return ret;
  153. }
  154. #endif // DEBUG_LOGGING
  155. //-------------------------------------------------------------------------------------------------
  156. //-------------------------------------------------------------------------------------------------
  157. Object::Object( const ThingTemplate *tt, ObjectStatusBits statusBits, Team *team ) :
  158. Thing(tt),
  159. m_indicatorColor(0),
  160. m_ai(NULL),
  161. m_physics(NULL),
  162. m_geometryInfo(tt->getTemplateGeometryInfo()),
  163. m_containedBy(NULL),
  164. m_xferContainedByID(INVALID_ID),
  165. m_containedByFrame(0),
  166. m_behaviors(NULL),
  167. m_body(NULL),
  168. m_contain(NULL),
  169. m_partitionData(NULL),
  170. m_radarData(NULL),
  171. m_drawable(NULL),
  172. m_next(NULL),
  173. m_prev(NULL),
  174. m_team(NULL),
  175. m_experienceTracker(NULL),
  176. m_firingTracker(NULL),
  177. m_repulsorHelper(NULL),
  178. m_smcHelper(NULL),
  179. m_wsHelper(NULL),
  180. m_defectionHelper(NULL),
  181. m_partitionLastLook(NULL),
  182. m_partitionLastShroud(NULL),
  183. m_partitionLastThreat(NULL),
  184. m_partitionLastValue(NULL),
  185. m_smcUntil(NEVER),
  186. m_privateStatus(0),
  187. m_formationID(NO_FORMATION_ID),
  188. m_isReceivingDifficultyBonus(FALSE)
  189. {
  190. #if defined(_DEBUG) || defined(_INTERNAL)
  191. m_hasDiedAlready = false;
  192. #endif
  193. //Modules have not been created yet!
  194. m_modulesReady = false;
  195. // Force the thing template to use the most overridden version of itself - jkmcd
  196. // Note that after this, the object will be using m_template, which forces the usage of the
  197. // most overridden version of tt, so this is okay.
  198. tt = (const ThingTemplate *) tt->getFinalOverride();
  199. Int i, modIdx;
  200. AsciiString modName;
  201. //Added By Sadullah Nader
  202. //Initializations inserted
  203. m_formationOffset.x = m_formationOffset.y = 0.0f;
  204. m_iPos.zero();
  205. //
  206. for( i = 0; i < DISABLED_COUNT; i++ )
  207. {
  208. m_disabledTillFrame[ i ] = NEVER;
  209. }
  210. // sanity
  211. if( TheGameLogic == NULL || tt == NULL )
  212. {
  213. assert( 0 );
  214. return;
  215. } // end if
  216. // Object's set of these persist for the life of the object.
  217. m_partitionLastLook = newInstance(SightingInfo);
  218. m_partitionLastLook->reset();
  219. m_partitionLastShroud = newInstance(SightingInfo);
  220. m_partitionLastShroud->reset();
  221. m_partitionLastThreat = newInstance(SightingInfo);
  222. m_partitionLastThreat->reset();
  223. m_partitionLastValue = newInstance(SightingInfo);
  224. m_partitionLastValue->reset();
  225. // must set ID to zero, since some of these set methods
  226. // will cause network messages to be sent
  227. // which use this ID.
  228. m_id = INVALID_ID;
  229. m_producerID = INVALID_ID;
  230. m_builderID = INVALID_ID;
  231. m_status = statusBits;
  232. m_layer = LAYER_GROUND;
  233. m_group = NULL;
  234. m_constructionPercent = CONSTRUCTION_COMPLETE; // complete by default
  235. m_objectUpgradesCompleted = 0;
  236. m_visionRange = tt->friend_getVisionRange();
  237. m_shroudClearingRange = tt->friend_getShroudClearingRange();
  238. if( m_shroudClearingRange == -1.0f )
  239. m_shroudClearingRange = m_visionRange;// Backwards compatible, and perfectly logical default to assign
  240. m_shroudRange = 0.0f;
  241. m_singleUseCommandUsed = false;
  242. // assign unique object id
  243. setID( TheGameLogic->allocateObjectID() );
  244. //
  245. // allocate any modules we need to, we should keep
  246. // this at or near the end of the drawable construction so that we have
  247. // all the valid data about the thing when we create the module
  248. //
  249. Int totalModules = tt->getBehaviorModuleInfo().getCount() + NUM_SLEEP_HELPERS; // need to take into account all the helper modules
  250. // allocate the publicModule arrays
  251. // pool[]ify
  252. m_behaviors = MSGNEW("ModulePtrs") BehaviorModule*[totalModules + 1];
  253. BehaviorModule** curB = m_behaviors;
  254. // set m_team to null before the first call, to avoid naughtiness...
  255. // If no team is specified in the constructor, then assign the object
  256. // to the neutral team.
  257. setTeam(team ? team : ThePlayerList->getNeutralPlayer()->getDefaultTeam());
  258. // the helpers are done first -- even before Behaviors! -- in case a module needs
  259. // to call something that uses them.
  260. static const NameKeyType smcHelperModuleDataTagNameKey = NAMEKEY( "ModuleTag_SMCHelper" );
  261. static ObjectSMCHelperModuleData smcModuleData;
  262. smcModuleData.setModuleTagNameKey( smcHelperModuleDataTagNameKey );
  263. m_smcHelper = newInstance(ObjectSMCHelper)(this, &smcModuleData);
  264. *curB++ = m_smcHelper;
  265. if (TheAI != NULL
  266. && TheAI->getAiData()->m_enableRepulsors
  267. && isKindOf(KINDOF_CAN_BE_REPULSED))
  268. {
  269. // if we can ever be a temporary-repulsor, make a repulsor helper. (srj)
  270. static const NameKeyType repulsorHelperModuleDataTagNameKey = NAMEKEY( "ModuleTag_RepulsorHelper" );
  271. static ObjectRepulsorHelperModuleData repulsorModuleData;
  272. repulsorModuleData.setModuleTagNameKey( repulsorHelperModuleDataTagNameKey );
  273. m_repulsorHelper = newInstance(ObjectRepulsorHelper)(this, &repulsorModuleData);
  274. *curB++ = m_repulsorHelper;
  275. }
  276. /** @todo srj -- figure out how to create this only on demand.
  277. currently we don't have a good way to add/remove update modules from
  278. an object on-the-fly, so we fake it here, and just skip the creation
  279. if it is impossible for this object to ever defect... */
  280. // shrubbery cannot defect. no, really.
  281. if (!tt->isKindOf(KINDOF_SHRUBBERY))
  282. {
  283. static const NameKeyType defectionModuleDataTagNameKey = NAMEKEY( "ModuleTag_DefectionHelper" );
  284. static ObjectDefectionHelperModuleData defectionModuleData;
  285. defectionModuleData.setModuleTagNameKey( defectionModuleDataTagNameKey );
  286. m_defectionHelper = newInstance(ObjectDefectionHelper)(this, &defectionModuleData);
  287. *curB++ = m_defectionHelper;
  288. }
  289. if (tt->canPossiblyHaveAnyWeapon())
  290. {
  291. // we only need a firingtracker and wshelper if we can possibly have a weapon.
  292. static const NameKeyType weaponStatusModuleDataTagNameKey = NAMEKEY( "ModuleTag_WeaponStatusHelper" );
  293. static ObjectWeaponStatusHelperModuleData weaponStatusModuleData;
  294. weaponStatusModuleData.setModuleTagNameKey( weaponStatusModuleDataTagNameKey );
  295. m_wsHelper = newInstance(ObjectWeaponStatusHelper)(this, &weaponStatusModuleData);
  296. *curB++ = m_wsHelper;
  297. static const NameKeyType firingTrackerModuleDataTagNameKey = NAMEKEY( "ModuleTag_FiringTrackerHelper" );
  298. static FiringTrackerModuleData firingTrackerModuleData;
  299. firingTrackerModuleData.setModuleTagNameKey( firingTrackerModuleDataTagNameKey );
  300. m_firingTracker = newInstance(FiringTracker)(this, &firingTrackerModuleData);
  301. *curB++ = m_firingTracker;
  302. }
  303. // behaviors are always done first, so they get into the publicModule arrays
  304. // before anything else.
  305. const ModuleInfo& mi = tt->getBehaviorModuleInfo();
  306. for (modIdx = 0; modIdx < mi.getCount(); ++modIdx)
  307. {
  308. modName = mi.getNthName(modIdx);
  309. if (modName.isEmpty())
  310. continue;
  311. BehaviorModule* newMod = (BehaviorModule*)TheModuleFactory->newModule(this, modName, mi.getNthData(modIdx), MODULETYPE_BEHAVIOR);
  312. *curB++ = newMod;
  313. BodyModuleInterface* body = newMod->getBody();
  314. if (body)
  315. {
  316. DEBUG_ASSERTCRASH(m_body == NULL, ("Duplicate bodies"));
  317. m_body = body;
  318. }
  319. ContainModuleInterface* contain = newMod->getContain();
  320. if (contain)
  321. {
  322. DEBUG_ASSERTCRASH(m_contain == NULL, ("Duplicate containers"));
  323. m_contain = contain;
  324. }
  325. AIUpdateInterface* ai = newMod->getAIUpdateInterface();
  326. if (ai)
  327. {
  328. DEBUG_ASSERTCRASH(m_ai == NULL, ("You should never have more than one AI module (srj)\n"));
  329. m_ai = ai;
  330. }
  331. static NameKeyType key_PhysicsUpdate = NAMEKEY("PhysicsBehavior");
  332. if (newMod->getModuleNameKey() == key_PhysicsUpdate)
  333. {
  334. DEBUG_ASSERTCRASH(m_physics == NULL, ("You should never have more than one Physics module (%s)\n",getTemplate()->getName().str()));
  335. m_physics = (PhysicsBehavior*)newMod;
  336. }
  337. }
  338. *curB = NULL;
  339. AIUpdateInterface *ai = getAIUpdateInterface();
  340. if (ai) {
  341. ai->setAttitude(getTeam()->getPrototype()->getTemplateInfo()->m_initialTeamAttitude);
  342. if (m_team && m_team->getPrototype() && m_team->getPrototype()->getAttackPriorityName().isNotEmpty()) {
  343. AsciiString name = m_team->getPrototype()->getAttackPriorityName();
  344. const AttackPriorityInfo *info = TheScriptEngine->getAttackInfo(name);
  345. if (info && info->getName().isNotEmpty()) {
  346. ai->setAttackInfo(info);
  347. }
  348. }
  349. }
  350. // allocate experience tracker
  351. m_experienceTracker = newInstance(ExperienceTracker)(this);
  352. // If a valid team has been assigned me, then I have a Player I can ask about my starting level
  353. const Player* controller = getControllingPlayer();
  354. m_experienceTracker->setVeterancyLevel( controller->getProductionVeterancyLevel( getTemplate()->getName() ) );
  355. /// allow for inter-Module resolution
  356. for (BehaviorModule** b = m_behaviors; *b; ++b)
  357. {
  358. (*b)->onObjectCreated();
  359. }
  360. m_numTriggerAreasActive = 0;
  361. m_enteredOrExitedFrame = 0;
  362. m_isSelectable = tt->isKindOf(KINDOF_SELECTABLE);
  363. m_healthBoxOffset.zero();// this is used for units that are amorphous, like angry mob
  364. //Modules have now been completely created!
  365. m_modulesReady = true;
  366. TheRadar->addObject( this );
  367. // register the object with the GameLogic
  368. TheGameLogic->registerObject( this );
  369. //disable occlusion for some time after object is created to allow them to exit the factory/building.
  370. m_safeOcclusionFrame = TheGameLogic->getFrame()+tt->getOcclusionDelay();
  371. m_soleHealingBenefactorID = INVALID_ID; ///< who is the only other object that can give me this non-stacking heal benefit?
  372. m_soleHealingBenefactorExpirationFrame = 0; ///< on what frame can I accept healing (thus to switch) from a new benefactor
  373. } // end Object
  374. //-------------------------------------------------------------------------------------------------
  375. /** Emit message announcing object's creation
  376. * Note: Have to do this in virtual init() method because virtual methods
  377. * don't become virtual until AFTER the constructor has completed, and we
  378. * need to send our type in this message via virtual getType(). */
  379. //-------------------------------------------------------------------------------------------------
  380. void Object::initObject()
  381. {
  382. // Weapons & Damage -------------------------------------------------------------------------------------------------
  383. // Force the initial weapon set to be instantiated & reloaded.
  384. m_curWeaponSetFlags.clear();
  385. m_weaponSet.updateWeaponSet(this);
  386. m_weaponBonusCondition = 0;
  387. for (int i = 0; i < WEAPONSLOT_COUNT; ++i)
  388. m_lastWeaponCondition[i] = WSF_INVALID;
  389. // If I have a valid team assigned, I can run through my Upgrade modules with his flags
  390. updateUpgradeModules();
  391. // emit message announcing object's creation
  392. TheGameLogic->sendObjectCreated( this );
  393. //If the player has battle plans (America Strategy Center), then apply those bonuses
  394. //to this object if applicable. Internally it validates certain kinds of objects.
  395. const Player* controller = getControllingPlayer();
  396. if (controller)
  397. {
  398. if (!getReceivingDifficultyBonus() && TheScriptEngine->getObjectsShouldReceiveDifficultyBonus())
  399. {
  400. setReceivingDifficultyBonus(TRUE);
  401. }
  402. if (controller->getNumBattlePlansActive() > 0)
  403. {
  404. controller->applyBattlePlanBonusesForObject( this );
  405. }
  406. }
  407. //For each special power module that we have, add it's type to the specialpower bits. This is
  408. //for optimal access later.
  409. for (BehaviorModule** m = m_behaviors; *m; ++m)
  410. {
  411. SpecialPowerModuleInterface* sp = (*m)->getSpecialPower();
  412. if (!sp)
  413. continue;
  414. const SpecialPowerTemplate *spTemplate = sp->getSpecialPowerTemplate();
  415. if( spTemplate )
  416. {
  417. SET_SPECIALPOWERMASK( m_specialPowerBits, spTemplate->getSpecialPowerType() );
  418. }
  419. }
  420. // Kris -- All missiles must be projectiles! This is the perfect place to assert them!
  421. // srj: yes, but only in debug...
  422. #if defined(_DEBUG) || defined(_INTERNAL)
  423. if( !isKindOf( KINDOF_PROJECTILE ) )
  424. {
  425. if( isKindOf( KINDOF_SMALL_MISSILE ) || isKindOf( KINDOF_BALLISTIC_MISSILE ) )
  426. {
  427. //Warning only...
  428. 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() ) );
  429. }
  430. }
  431. #endif
  432. if (!isKindOf(KINDOF_PROJECTILE) && !isKindOf(KINDOF_INERT)) {
  433. // Notify script conditions to update conditions that consider unit counts.
  434. // We ignore projectiles cause they are frequently created & destroyed, and are not
  435. // of general interest. Normal unit count tests consider tanks or infantry or planes, etc. jba.
  436. TheScriptEngine->notifyOfObjectCreationOrDestruction();
  437. TheGameLogic->updateObjectsChangedTriggerAreas();
  438. }
  439. }
  440. //-------------------------------------------------------------------------------------------------
  441. //-------------------------------------------------------------------------------------------------
  442. Object::~Object()
  443. {
  444. // tell the AI the building is gone
  445. /// @todo Generalize the notion of objects entering and leaving the world, so we don't have to special case this
  446. TheAI->pathfinder()->removeObjectFromPathfindMap( this );
  447. if (!isKindOf(KINDOF_PROJECTILE) && !isKindOf(KINDOF_INERT)) {
  448. // Notify script conditions to update conditions that consider unit counts.
  449. // We ignore projectiles cause they are frequently created & destroyed, and are not
  450. // of general interest. Normal unit count tests consider tanks or infantry or planes, etc. jba.
  451. TheGameLogic->updateObjectsChangedTriggerAreas();
  452. TheScriptEngine->notifyOfObjectCreationOrDestruction();
  453. }
  454. //
  455. // remove from radar before we NULL out the team ... the order of ops are critical here
  456. // because the radar code will sometimes look at the team info and it is assumed through
  457. // the team and player code that the team is valid
  458. //
  459. if( m_radarData )
  460. TheRadar->removeObject( this );
  461. // emit message announcing object's destruction. Again, order is important; we must do this
  462. // before wiping out the team.
  463. TheGameLogic->sendObjectDestroyed( this );
  464. // empty the team
  465. setTeam( NULL );
  466. // Object's set of these persist for the life of the object.
  467. m_partitionLastLook->deleteInstance();
  468. m_partitionLastLook = NULL;
  469. m_partitionLastShroud->deleteInstance();
  470. m_partitionLastShroud = NULL;
  471. m_partitionLastThreat->deleteInstance();
  472. m_partitionLastThreat = NULL;
  473. m_partitionLastValue->deleteInstance();
  474. m_partitionLastValue = NULL;
  475. // remove the object from the partition system if present
  476. if( m_partitionData )
  477. ThePartitionManager->unRegisterObject( this );
  478. // if we are in a group, remove us
  479. if (m_group)
  480. m_group->remove( this );
  481. // note, do NOT free these, there are just a shadow copy!
  482. m_ai = NULL;
  483. m_physics = NULL;
  484. // delete any modules present
  485. for (BehaviorModule** b = m_behaviors; *b; ++b)
  486. {
  487. (*b)->deleteInstance();
  488. *b = NULL; // in case other modules call findModule from their dtor!
  489. }
  490. delete [] m_behaviors;
  491. m_behaviors = NULL;
  492. if( m_experienceTracker )
  493. m_experienceTracker->deleteInstance();
  494. m_experienceTracker = NULL;
  495. // we don't need to delete these, there were deleted on the m_behaviors list
  496. m_firingTracker = NULL;
  497. m_repulsorHelper = NULL;
  498. m_smcHelper = NULL;
  499. m_wsHelper = NULL;
  500. m_defectionHelper = NULL;
  501. // reset id to zero so we never mistaken grab "dead" objects
  502. m_id = INVALID_ID;
  503. // Instead of removing it from the named cache, notify the script engine that it has died.
  504. // The script engine will remove it from the cache if necessary. The script engine needs to take
  505. // a crack at this in case it is the current "This Object" pointer.
  506. TheScriptEngine->notifyOfObjectDestruction(this);
  507. }
  508. //-------------------------------------------------------------------------------------------------
  509. void localIsHero( Object *obj, void* userData )
  510. {
  511. Bool *hero = (Bool*)userData;
  512. if( obj && obj->isKindOf( KINDOF_HERO ) )
  513. {
  514. *hero = TRUE;
  515. }
  516. }
  517. //-------------------------------------------------------------------------------------------------
  518. Bool Object::isHero() const
  519. {
  520. ContainModuleInterface *contain = getContain();
  521. if( contain )
  522. {
  523. Bool heroInside = FALSE;
  524. contain->iterateContained( localIsHero, (void*)(&heroInside), FALSE );
  525. if( heroInside )
  526. {
  527. return TRUE;
  528. }
  529. }
  530. return isKindOf( KINDOF_HERO );
  531. }
  532. //-------------------------------------------------------------------------------------------------
  533. /// this object now contained in "containedBy"
  534. //-------------------------------------------------------------------------------------------------
  535. void Object::onContainedBy( Object *containedBy )
  536. {
  537. setStatus(OBJECT_STATUS_UNSELECTABLE);
  538. if (containedBy && containedBy->getContain()->isEnclosingContainerFor(this))
  539. setStatus(OBJECT_STATUS_MASKED);
  540. else
  541. clearStatus(OBJECT_STATUS_MASKED);
  542. m_containedBy = containedBy;
  543. m_containedByFrame = TheGameLogic->getFrame();
  544. }
  545. //-------------------------------------------------------------------------------------------------
  546. /// this object no longer contained in "containedBy"
  547. //-------------------------------------------------------------------------------------------------
  548. void Object::onRemovedFrom( Object *removedFrom )
  549. {
  550. clearStatus(OBJECT_STATUS_UNSELECTABLE);
  551. clearStatus(OBJECT_STATUS_MASKED);
  552. m_containedBy = NULL;
  553. m_containedByFrame = 0;
  554. }
  555. //-------------------------------------------------------------------------------------------------
  556. //-------------------------------------------------------------------------------------------------
  557. Int Object::getTransportSlotCount() const
  558. {
  559. Int count = getTemplate()->getRawTransportSlotCount();
  560. ContainModuleInterface* contain = getContain();
  561. if ( contain && contain->isSpecialZeroSlotContainer() )
  562. {
  563. count = 0;
  564. const ContainedItemsList* items = contain->getContainedItemsList();
  565. if (items)
  566. {
  567. for (ContainedItemsList::const_iterator it = items->begin(); it != items->end(); ++it)
  568. {
  569. count += (*it)->getTransportSlotCount();
  570. }
  571. }
  572. }
  573. return count;
  574. }
  575. //-------------------------------------------------------------------------------------------------
  576. /** Run from GameLogic::destroyObject */
  577. //-------------------------------------------------------------------------------------------------
  578. void Object::onDestroy()
  579. {
  580. // This is the old cleanUpContain safeguard. Say goodbye so they don't try to look us up.
  581. if( m_containedBy && m_containedBy->getContain() )
  582. {
  583. m_containedBy->getContain()->removeFromContain( this );
  584. }
  585. //
  586. // run the onDelete on all modules present so they each have an opportunity to cleanup
  587. // anything they need to ... including talking to any other modules
  588. //
  589. for (BehaviorModule** b = m_behaviors; *b; ++b)
  590. {
  591. (*b)->onDelete();
  592. }
  593. //Have to remove ourself from looking as well. RebuildHoleWorkers definately hit here.
  594. handlePartitionCellMaintenance();
  595. } // end onDestroy
  596. //=============================================================================
  597. //=============================================================================
  598. void Object::setGeometryInfo(const GeometryInfo& geom)
  599. {
  600. m_geometryInfo = geom;
  601. if( m_partitionData )
  602. {
  603. // if our geometry changes, we unregister and re-register with the partitionmgr
  604. // so that our size gets updated appropriately. this shouldn't be a problem
  605. // unless setGeometryInfo gets called frequently. (srj)
  606. ThePartitionManager->unRegisterObject( this );
  607. ThePartitionManager->registerObject( this );
  608. }
  609. if (m_drawable)
  610. m_drawable->reactToGeometryChange();
  611. }
  612. //=============================================================================
  613. //=============================================================================
  614. void Object::setGeometryInfoZ( Real newZ )
  615. {
  616. // A Z change only does not need to un/register with the PartitionManager
  617. m_geometryInfo.setMaxHeightAbovePosition( newZ );
  618. if (m_drawable)
  619. m_drawable->reactToGeometryChange();
  620. }
  621. //=============================================================================
  622. void Object::friend_setUndetectedDefector( Bool status )
  623. {
  624. if (status)
  625. m_privateStatus |= UNDETECTED_DEFECTOR;
  626. else
  627. m_privateStatus &= ~UNDETECTED_DEFECTOR;
  628. }
  629. //=============================================================================
  630. void Object::restoreOriginalTeam()
  631. {
  632. if( m_team == NULL || m_originalTeamName.isEmpty() )
  633. return;
  634. Team* origTeam = TheTeamFactory->findTeam(m_originalTeamName);
  635. if (origTeam == NULL)
  636. {
  637. DEBUG_CRASH(("Object original team (%s) could not be found or created! (srj)\n",m_originalTeamName.str()));
  638. return;
  639. }
  640. if (m_team == origTeam)
  641. {
  642. DEBUG_CRASH(("Object appears to still be on its original team, so why are we attempting to restore it? (srj)\n"));
  643. return;
  644. }
  645. setTeam(origTeam);
  646. }
  647. //=============================================================================
  648. //=============================================================================
  649. void Object::setTeam( Team *team )
  650. {
  651. // In order to prevent spawning useful units for a player after he dies, we
  652. // just assign objects to the neutral player if we try to misbehave.
  653. if (team && !team->getControllingPlayer()->isPlayerActive())
  654. team = ThePlayerList->getNeutralPlayer()->getDefaultTeam();
  655. setTemporaryTeam(team);
  656. m_originalTeamName = m_team ? m_team->getName() : AsciiString::TheEmptyString;
  657. }
  658. //=============================================================================
  659. //=============================================================================
  660. void Object::setTemporaryTeam( Team *team )
  661. {
  662. const Bool restoring = false;
  663. setOrRestoreTeam(team, restoring);
  664. }
  665. //=============================================================================
  666. //=============================================================================
  667. void Object::setOrRestoreTeam( Team* team, Bool restoring )
  668. {
  669. // don't do anything if the team hasn't changed
  670. if( m_team == team )
  671. return;
  672. Team* oldTeam = m_team;
  673. // Before Switch //////////////////////////
  674. if (m_team)
  675. {
  676. if (m_team->isInList_TeamMemberList(this))
  677. {
  678. m_team->removeFrom_TeamMemberList(this);
  679. m_team->getControllingPlayer()->becomingTeamMember(this, false);
  680. }
  681. }
  682. // Switch //////////////////////////
  683. m_team = team;
  684. // After Switch //////////////////////////
  685. if (m_team)
  686. {
  687. if (!m_team->isInList_TeamMemberList(this))
  688. {
  689. m_team->prependTo_TeamMemberList(this);
  690. m_team->getControllingPlayer()->becomingTeamMember(this, true);
  691. }
  692. // now, adjust the attitude of the unit to its new team.
  693. const TeamPrototype* proto = m_team->getPrototype();
  694. if (proto && proto->getTemplateInfo())
  695. {
  696. AIUpdateInterface *ai = getAIUpdateInterface();
  697. if (ai)
  698. {
  699. ai->setAttitude(proto->getTemplateInfo()->m_initialTeamAttitude);
  700. if (proto->getAttackPriorityName().isNotEmpty()) {
  701. AsciiString name = proto->getAttackPriorityName();
  702. const AttackPriorityInfo *info = TheScriptEngine->getAttackInfo(name);
  703. if (info && info->getName().isNotEmpty()) {
  704. ai->setAttackInfo(info);
  705. }
  706. }
  707. }
  708. }
  709. // emit message announcing object's new alliance
  710. Drawable *draw = getDrawable();
  711. if (draw)
  712. draw->changedTeam();
  713. }
  714. // This can't just go in ::defect, because some things just do setTeam. The act of
  715. // setting a new team needs to tell the modules and do other important stuff.
  716. // And it needs to happen after the switch.
  717. if( oldTeam && team && !restoring )
  718. onCapture( oldTeam->getControllingPlayer(), team->getControllingPlayer() );
  719. //
  720. // the team changed we have a change in priorities on the radar if we are
  721. // a candidate for the radar as it is
  722. //
  723. if( m_radarData )
  724. {
  725. // removing it and adding it will cause a resort to happen
  726. TheRadar->removeObject( this );
  727. TheRadar->addObject( this );
  728. }
  729. // Tell TheInGameUI that the object has changed hands
  730. Int oldPlayerIndex = (oldTeam)?(oldTeam->getControllingPlayer()->getPlayerIndex()):-1;
  731. Int newPlayerIndex = (m_team)?(m_team->getControllingPlayer()->getPlayerIndex()):-1;
  732. if (oldPlayerIndex != newPlayerIndex)
  733. TheInGameUI->objectChangedTeam(this, oldPlayerIndex, newPlayerIndex);
  734. }
  735. //=============================================================================
  736. void Object::setStatus( ObjectStatusBits bits, Bool set )
  737. {
  738. UnsignedInt oldStatus = m_status;
  739. if (set)
  740. m_status |= bits;
  741. else
  742. m_status &= ~bits;
  743. if (m_status != oldStatus)
  744. {
  745. if (set
  746. && (bits & OBJECT_STATUS_REPULSOR) != 0
  747. && m_repulsorHelper != NULL)
  748. {
  749. // Damaged repulsable civilians scare (repulse) other civs, but only
  750. // for a short amount of time... use the repulsor helper to turn off repulsion shortly.
  751. m_repulsorHelper->sleepUntil(TheGameLogic->getFrame() + 2*LOGICFRAMES_PER_SECOND);
  752. }
  753. // when an object's construction status changes, it needs to have its partition data updated,
  754. // in order to maintain the shroud correctly.
  755. if ((m_status & OBJECT_STATUS_UNDER_CONSTRUCTION) != (oldStatus & OBJECT_STATUS_UNDER_CONSTRUCTION))
  756. {
  757. // CHECK FOR MINES, AND DETONATE THEM NOW
  758. ObjectIterator *iter =
  759. ThePartitionManager->iteratePotentialCollisions( getPosition(), getGeometryInfo(), getOrientation() );
  760. MemoryPoolObjectHolder hold( iter );
  761. Object *them;
  762. for( them = iter->first(); them; them = iter->next() )
  763. {
  764. if (them->isKindOf( KINDOF_MINE ))
  765. {
  766. //DETONATE ANY ENEMY MINES, OR DELETE FRIENDLY ONES
  767. Relationship r = getRelationship(them);
  768. if (r == ENEMIES)
  769. {
  770. them->kill(); // detonate mine
  771. }
  772. else
  773. {
  774. TheGameLogic->destroyObject(them);
  775. }
  776. }
  777. }// next object
  778. if (m_partitionData)
  779. m_partitionData->makeDirty(true);
  780. }
  781. }
  782. }
  783. //=============================================================================
  784. void Object::setScriptStatus( ObjectScriptStatusBit bit, Bool set )
  785. {
  786. UnsignedInt oldScriptStatus = m_scriptStatus;
  787. if( set )
  788. {
  789. m_scriptStatus |= bit;
  790. }
  791. else
  792. {
  793. m_scriptStatus &= ~bit;
  794. }
  795. if( m_scriptStatus != oldScriptStatus )
  796. {
  797. if( (m_scriptStatus & OBJECT_STATUS_SCRIPT_DISABLED) != (oldScriptStatus & OBJECT_STATUS_SCRIPT_DISABLED) )
  798. {
  799. if( m_partitionData )
  800. {
  801. // if an object becomes disabled or unpowered, then you have to update its partition data because it will
  802. // change how far it can see.
  803. m_partitionData->makeDirty(true);
  804. }
  805. if( m_scriptStatus & OBJECT_STATUS_SCRIPT_DISABLED )
  806. {
  807. //I am now disabled, so tell the main game engine!
  808. setDisabled( DISABLED_SCRIPT_DISABLED );
  809. }
  810. else
  811. {
  812. //I am no longer disabled, so tell the main game engine!
  813. clearDisabled( DISABLED_SCRIPT_DISABLED );
  814. }
  815. }
  816. if( (m_scriptStatus & OBJECT_STATUS_SCRIPT_UNPOWERED) != (oldScriptStatus & OBJECT_STATUS_SCRIPT_UNPOWERED) )
  817. {
  818. if( m_partitionData )
  819. {
  820. // if an object becomes disabled or unpowered, then you have to update its partition data because it will
  821. // change how far it can see.
  822. m_partitionData->makeDirty(true);
  823. }
  824. if( m_scriptStatus & OBJECT_STATUS_SCRIPT_UNPOWERED )
  825. {
  826. //I am now underpowered, so tell the main game engine!
  827. setDisabled( DISABLED_SCRIPT_UNDERPOWERED );
  828. }
  829. else
  830. {
  831. //I am no longer undperpowered, so tell the main game engine!
  832. clearDisabled( DISABLED_SCRIPT_UNDERPOWERED );
  833. }
  834. }
  835. }
  836. }
  837. //=============================================================================
  838. Bool Object::canCrushOrSquish(Object *otherObj, CrushSquishTestType testType ) const
  839. {
  840. DEBUG_ASSERTCRASH(this, ("null this in canCrushOrSquish"));
  841. if( !otherObj )
  842. {
  843. //Can't crush anything.
  844. return false;
  845. }
  846. if( isDisabledByType( DISABLED_UNMANNED ) )
  847. {
  848. //Unmanned vehicles cannot crush troops. This was happening when Jarmen Kell sniped
  849. //the vehicle and booted the guys out while still moving, as the vehicle is now
  850. //on a different team.
  851. return false;
  852. }
  853. UnsignedByte crusherLevel = getCrusherLevel();
  854. // order matters: we want to know if I consider it to be an ally, not vice versa
  855. if( getRelationship( otherObj ) == ALLIES )
  856. {
  857. //Friends don't let friends crush friends.
  858. return false;
  859. }
  860. if( !crusherLevel )
  861. {
  862. //Can't crush anything!
  863. return false;
  864. }
  865. //Test this case for generic infantry getting squished by vehicles!
  866. if( testType == TEST_SQUISH_ONLY || testType == TEST_CRUSH_OR_SQUISH )
  867. {
  868. //****************************************************************************************
  869. //NOTE: This section of code is used by the pathfinder to determine if the object should
  870. // move to the target. I don't think it's the right place to check for this because
  871. // the semantics check to see if we can squish something -- not approach it. However
  872. // I'm not moving it for fear of some major breakage! -- KM
  873. //Bool squisher = crusherLevel > 0;
  874. //if( !squisher )
  875. //{
  876. // Weapon *weapon = getCurrentWeapon();
  877. // if( weapon && weapon->isContactWeapon() )
  878. // {
  879. // squisher = true;
  880. // }
  881. //}
  882. //if( squisher )
  883. //NOTE2: *** IF YOU REENABLE THIS CODE -- Move the "if( !crusherLevel ) return false" below
  884. // this squish section.
  885. //****************************************************************************************
  886. {
  887. // See if other is squishable
  888. static NameKeyType key_squish = NAMEKEY( "SquishCollide" );
  889. if( otherObj->findModule( key_squish ) )
  890. {
  891. return true; // squishable.
  892. }
  893. }
  894. }
  895. UnsignedByte crushableLevel = otherObj->getCrushableLevel();
  896. if( testType == TEST_CRUSH_ONLY || testType == TEST_CRUSH_OR_SQUISH )
  897. {
  898. if( crusherLevel > crushableLevel )
  899. {
  900. return true;
  901. }
  902. }
  903. return false;
  904. }
  905. //-------------------------------------------------------------------------------------------------
  906. UnsignedByte Object::getCrusherLevel() const
  907. {
  908. return getTemplate()->getCrusherLevel();
  909. }
  910. //-------------------------------------------------------------------------------------------------
  911. UnsignedByte Object::getCrushableLevel() const
  912. {
  913. return getTemplate()->getCrushableLevel();
  914. }
  915. // ------------------------------------------------------------------------------------------------
  916. /** Topple an object, if possible */
  917. // ------------------------------------------------------------------------------------------------
  918. void Object::topple( const Coord3D *toppleDirection, Real toppleSpeed, UnsignedInt options )
  919. {
  920. static NameKeyType key_ToppleUpdate = NAMEKEY("ToppleUpdate");
  921. ToppleUpdate* toppleUpdate = (ToppleUpdate*)findModule(key_ToppleUpdate);
  922. if( toppleUpdate && toppleUpdate->isAbleToBeToppled() )
  923. {
  924. // apply the topple force
  925. toppleUpdate->applyTopplingForce( toppleDirection, toppleSpeed, options );
  926. } // end if
  927. } // end topple
  928. //=============================================================================
  929. void Object::reloadAllAmmo(Bool now)
  930. {
  931. m_weaponSet.reloadAllAmmo(this, now);
  932. }
  933. //=============================================================================
  934. Bool Object::isOutOfAmmo() const
  935. {
  936. return m_weaponSet.isOutOfAmmo();
  937. }
  938. //=============================================================================
  939. Bool Object::hasAnyWeapon() const
  940. {
  941. return m_weaponSet.hasAnyWeapon();
  942. }
  943. //=============================================================================
  944. Bool Object::hasAnyDamageWeapon() const
  945. {
  946. //First check to see if we have any weapons -- if not return false.
  947. if( !m_weaponSet.hasAnyDamageWeapon() )
  948. {
  949. return FALSE;
  950. }
  951. return TRUE;
  952. }
  953. //=============================================================================
  954. Bool Object::hasWeaponToDealDamageType(DamageType typeToDeal) const
  955. {
  956. return m_weaponSet.hasWeaponToDealDamageType(typeToDeal);
  957. }
  958. //=============================================================================
  959. Real Object::getLargestWeaponRange() const
  960. {
  961. Real retVal = -1;
  962. for (Int i = PRIMARY_WEAPON; i < WEAPONSLOT_COUNT; ++i) {
  963. Weapon* weapon = m_weaponSet.getWeaponInWeaponSlot((WeaponSlotType)i);
  964. if (!weapon) {
  965. continue;
  966. }
  967. Real tmpVal = weapon->getAttackRange(this);
  968. if (tmpVal > retVal) {
  969. retVal = tmpVal;
  970. }
  971. }
  972. return retVal;
  973. }
  974. //=============================================================================
  975. void Object::setFiringConditionForCurrentWeapon() const
  976. {
  977. if (m_drawable)
  978. {
  979. WeaponSlotType wslot = m_weaponSet.getCurWeaponSlot();
  980. ModelConditionFlags c = m_weaponSet.getModelConditionForWeaponSlot(wslot, WSF_FIRING);
  981. m_drawable->clearAndSetModelConditionFlags(s_allWeaponFireFlags[wslot], c);
  982. }
  983. }
  984. //=============================================================================
  985. void Object::setModelConditionState( ModelConditionFlagType a )
  986. {
  987. if (m_drawable)
  988. {
  989. m_drawable->setModelConditionState(a);
  990. }
  991. }
  992. //=============================================================================
  993. void Object::clearModelConditionState( ModelConditionFlagType a )
  994. {
  995. if (m_drawable)
  996. {
  997. m_drawable->clearModelConditionState(a);
  998. }
  999. }
  1000. //=============================================================================
  1001. void Object::clearAndSetModelConditionState( ModelConditionFlagType clr, ModelConditionFlagType set )
  1002. {
  1003. if (m_drawable)
  1004. {
  1005. m_drawable->clearAndSetModelConditionState(clr, set);
  1006. }
  1007. }
  1008. //=============================================================================
  1009. void Object::clearModelConditionFlags( const ModelConditionFlags& clr )
  1010. {
  1011. if (m_drawable)
  1012. {
  1013. m_drawable->clearModelConditionFlags(clr);
  1014. }
  1015. }
  1016. //=============================================================================
  1017. void Object::setModelConditionFlags( const ModelConditionFlags& set )
  1018. {
  1019. if (m_drawable)
  1020. {
  1021. m_drawable->setModelConditionFlags(set);
  1022. }
  1023. }
  1024. //=============================================================================
  1025. void Object::clearAndSetModelConditionFlags( const ModelConditionFlags& clr, const ModelConditionFlags& set )
  1026. {
  1027. if (m_drawable)
  1028. {
  1029. m_drawable->clearAndSetModelConditionFlags(clr, set);
  1030. }
  1031. }
  1032. //=============================================================================
  1033. // Special model states are states that are turned on for a period of time, and
  1034. // turned off automatically -- used for cheer, and scripted special moment
  1035. // animations. Setting a special state will automatically clear any other
  1036. // special states that may be turned on so you can only have one at a time.
  1037. //=============================================================================
  1038. void Object::setSpecialModelConditionState( ModelConditionFlagType set, UnsignedInt frames )
  1039. {
  1040. clearSpecialModelConditionStates();
  1041. setModelConditionState( set );
  1042. if( frames == 0 )
  1043. {
  1044. frames = 1;
  1045. }
  1046. m_smcUntil = TheGameLogic->getFrame() + frames;
  1047. m_smcHelper->sleepUntil(m_smcUntil);
  1048. }
  1049. //=============================================================================
  1050. void Object::clearSpecialModelConditionStates()
  1051. {
  1052. clearModelConditionFlags( MAKE_MODELCONDITION_MASK( MODELCONDITION_SPECIAL_CHEERING ) );
  1053. m_smcUntil = NEVER;
  1054. }
  1055. // Lorenzen has some interest in this, ask before deleting
  1056. //=============================================================================
  1057. //const ModelConditionFlags& Object::getModelConditionFlags() const
  1058. //{
  1059. // if (m_drawable)
  1060. // {
  1061. // return m_drawable->getModelConditionFlags();
  1062. // }
  1063. // else
  1064. // {
  1065. // DEBUG_CRASH(("NULL Drawable at this point, you can't get modelconditionflags now."));
  1066. // static ModelConditionFlags noFlags;
  1067. // return noFlags;
  1068. // }
  1069. //}
  1070. //=============================================================================
  1071. Weapon* Object::getCurrentWeapon(WeaponSlotType* wslot)
  1072. {
  1073. if (!m_weaponSet.hasAnyWeapon())
  1074. return NULL;
  1075. if (wslot)
  1076. *wslot = m_weaponSet.getCurWeaponSlot();
  1077. return m_weaponSet.getCurWeapon();
  1078. }
  1079. //=============================================================================
  1080. const Weapon* Object::getCurrentWeapon(WeaponSlotType* wslot) const
  1081. {
  1082. if (!m_weaponSet.hasAnyWeapon())
  1083. return NULL;
  1084. if (wslot)
  1085. *wslot = m_weaponSet.getCurWeaponSlot();
  1086. return m_weaponSet.getCurWeapon();
  1087. }
  1088. //=============================================================================
  1089. Weapon* Object::findWaypointFollowingCapableWeapon()
  1090. {
  1091. return m_weaponSet.findWaypointFollowingCapableWeapon();
  1092. }
  1093. //=============================================================================
  1094. Bool Object::getAmmoPipShowingInfo(Int& numTotal, Int& numFull) const
  1095. {
  1096. /// @todo srj -- may need to cache this inside weaponset.
  1097. const Weapon* w = m_weaponSet.findAmmoPipShowingWeapon();
  1098. if (w)
  1099. {
  1100. numTotal = w->getClipSize();
  1101. numFull = w->getRemainingAmmo();
  1102. return true;
  1103. }
  1104. else
  1105. {
  1106. return false;
  1107. }
  1108. }
  1109. //=============================================================================
  1110. /*
  1111. NOTE: getAbleToAttackSpecificObject NO LONGER internally calls isAbleToAttack(),
  1112. since that isn't an incredibly fast call, and this is called repeatedly in some inner loops
  1113. where we already know that isAbleToAttack() == true. so you should always
  1114. call isAbleToAttack prior to calling this! (srj)
  1115. */
  1116. CanAttackResult Object::getAbleToAttackSpecificObject( AbleToAttackType t, const Object* target, CommandSourceType commandSource ) const
  1117. {
  1118. // NO! BAD! WRONG!
  1119. // If we can't attack at all, then we cannot attack this
  1120. //if (!isAbleToAttack())
  1121. // return FALSE;
  1122. // Otherwise leave it up to our weapons.
  1123. return m_weaponSet.getAbleToAttackSpecificObject( t, this, target, commandSource );
  1124. }
  1125. //=============================================================================
  1126. //Used for base defenses and otherwise stationary units to see if you can attack a position potentially out of range.
  1127. CanAttackResult Object::getAbleToUseWeaponAgainstTarget( AbleToAttackType attackType, const Object *victim, const Coord3D *pos, CommandSourceType commandSource ) const
  1128. {
  1129. return m_weaponSet.getAbleToUseWeaponAgainstTarget( attackType, this, victim, pos, commandSource );
  1130. }
  1131. //=============================================================================
  1132. Bool Object::chooseBestWeaponForTarget(const Object* target, WeaponChoiceCriteria criteria, CommandSourceType cmdSource )
  1133. {
  1134. return m_weaponSet.chooseBestWeaponForTarget(this, target, criteria, cmdSource );
  1135. }
  1136. //DECLARE_PERF_TIMER(fireCurrentWeapon)
  1137. //=============================================================================
  1138. void Object::fireCurrentWeapon(Object *target)
  1139. {
  1140. //USE_PERF_TIMER(fireCurrentWeapon)
  1141. // victim may have already been destroyed
  1142. if (target == NULL)
  1143. return;
  1144. Weapon* weapon = m_weaponSet.getCurWeapon();
  1145. if (weapon && (weapon->getStatus() == READY_TO_FIRE))
  1146. {
  1147. Bool reloaded = weapon->fireWeapon(this, target);
  1148. DEBUG_ASSERTCRASH(m_firingTracker, ("hey, we are firing but have no firing tracker. this is wrong."));
  1149. if (m_firingTracker)
  1150. m_firingTracker->shotFired(weapon, target->getID());
  1151. if (reloaded)
  1152. releaseWeaponLock(LOCKED_TEMPORARILY); // release any temporary locks.
  1153. friend_setUndetectedDefector( FALSE );// My secret is out
  1154. }
  1155. }
  1156. //=============================================================================
  1157. void Object::fireCurrentWeapon(const Coord3D* pos)
  1158. {
  1159. //USE_PERF_TIMER(fireCurrentWeapon)
  1160. if (pos == NULL)
  1161. return;
  1162. Weapon* weapon = m_weaponSet.getCurWeapon();
  1163. if (weapon && (weapon->getStatus() == READY_TO_FIRE))
  1164. {
  1165. Bool reloaded = weapon->fireWeapon(this, pos);
  1166. DEBUG_ASSERTCRASH(m_firingTracker, ("hey, we are firing but have no firing tracker. this is wrong."));
  1167. if (m_firingTracker)
  1168. m_firingTracker->shotFired(weapon, INVALID_ID);
  1169. if (reloaded)
  1170. releaseWeaponLock(LOCKED_TEMPORARILY); // release any temporary locks.
  1171. friend_setUndetectedDefector( FALSE );// My secret is out
  1172. }
  1173. }
  1174. //=============================================================================
  1175. void Object::preFireCurrentWeapon( const Object *victim )
  1176. {
  1177. Weapon* weapon = m_weaponSet.getCurWeapon();
  1178. //If we are going to be capable of firing our weapon NEXT frame, set the pre-attack
  1179. //up now. This gets called by AIAttackFireWeaponState::onEnter().. but the update happens
  1180. //next frame.
  1181. if (weapon && TheGameLogic->getFrame() + 1 >= weapon->getPossibleNextShotFrame() )
  1182. {
  1183. weapon->preFireWeapon( this, victim );
  1184. friend_setUndetectedDefector( FALSE );// My secret is out
  1185. }
  1186. }
  1187. // ============================================================================
  1188. /** Using the firing tracker, return the frame a shot was last fired on */
  1189. // ============================================================================
  1190. UnsignedInt Object::getLastShotFiredFrame() const
  1191. {
  1192. UnsignedInt recent = 0;
  1193. for (int i = 0; i < WEAPONSLOT_COUNT; ++i)
  1194. {
  1195. const Weapon* w = m_weaponSet.getWeaponInWeaponSlot((WeaponSlotType)i);
  1196. if (w)
  1197. {
  1198. UnsignedInt when = w->getLastShotFrame();
  1199. if (when > recent)
  1200. recent = when;
  1201. }
  1202. }
  1203. return recent;
  1204. }
  1205. // ============================================================================
  1206. /** Get the victim ID we last shot at */
  1207. // ============================================================================
  1208. ObjectID Object::getLastVictimID() const
  1209. {
  1210. return m_firingTracker ? m_firingTracker->getLastShotVictim() : INVALID_ID;
  1211. }
  1212. //=============================================================================
  1213. // Object::getRelationship
  1214. //=============================================================================
  1215. Relationship Object::getRelationship(const Object *that) const
  1216. {
  1217. const Team *myTeam = getTeam();
  1218. if (myTeam && that)
  1219. {
  1220. if (getIsUndetectedDefector())
  1221. {
  1222. return NEUTRAL; // so my AI does not give away my position by auto acquire
  1223. }
  1224. else if (that->getIsUndetectedDefector())
  1225. {
  1226. return ALLIES; // so I treat undetecteddefectors like they were my very own
  1227. }
  1228. else
  1229. {
  1230. return myTeam->getRelationship( that->getTeam() );
  1231. }
  1232. }
  1233. return NEUTRAL;
  1234. }
  1235. //=============================================================================
  1236. // Object::getControllingPlayer
  1237. //=============================================================================
  1238. Player * Object::getControllingPlayer() const
  1239. {
  1240. const Team* myTeam = this->getTeam();
  1241. if (myTeam)
  1242. return myTeam->getControllingPlayer();
  1243. return NULL;
  1244. }
  1245. //=============================================================================
  1246. void Object::setProducer(const Object* obj)
  1247. {
  1248. m_producerID = obj ? obj->getID() : INVALID_ID;
  1249. // seems like a good idea, but is not. (srj)
  1250. // if (obj)
  1251. // m_indicatorColor = obj->m_indicatorColor;
  1252. }
  1253. //=============================================================================
  1254. void Object::setBuilder( const Object *obj )
  1255. {
  1256. m_builderID = obj ? obj->getID() : INVALID_ID;
  1257. }
  1258. //=============================================================================
  1259. void Object::setCustomIndicatorColor(Color c)
  1260. {
  1261. if (m_indicatorColor != c)
  1262. {
  1263. m_indicatorColor = c;
  1264. if (m_drawable)
  1265. m_drawable->changedTeam();
  1266. }
  1267. }
  1268. //=============================================================================
  1269. void Object::removeCustomIndicatorColor()
  1270. {
  1271. setCustomIndicatorColor(0);
  1272. }
  1273. //=============================================================================
  1274. // Object::getIndicatorColor
  1275. //=============================================================================
  1276. Color Object::getIndicatorColor() const
  1277. {
  1278. if (m_indicatorColor == 0)
  1279. {
  1280. const Team *myTeam = getTeam();
  1281. if (myTeam)
  1282. {
  1283. const Player* p = myTeam->getControllingPlayer();
  1284. if (p)
  1285. {
  1286. return p->getPlayerColor();
  1287. }
  1288. }
  1289. return GameMakeColor(0, 0, 0, 255);
  1290. }
  1291. else
  1292. {
  1293. return m_indicatorColor;
  1294. }
  1295. }
  1296. //=============================================================================
  1297. // Object::getNightIndicatorColor - used to make blue/purple easier to see on night models.
  1298. //=============================================================================
  1299. Color Object::getNightIndicatorColor() const
  1300. {
  1301. if (m_indicatorColor == 0)
  1302. {
  1303. const Team *myTeam = getTeam();
  1304. if (myTeam)
  1305. {
  1306. const Player* p = myTeam->getControllingPlayer();
  1307. if (p)
  1308. {
  1309. return p->getPlayerNightColor();
  1310. }
  1311. }
  1312. return GameMakeColor(0, 0, 0, 255);
  1313. }
  1314. else
  1315. {
  1316. return m_indicatorColor;
  1317. }
  1318. }
  1319. //=============================================================================
  1320. // Object::isLocallyControlled
  1321. //=============================================================================
  1322. Bool Object::isLocallyControlled() const
  1323. {
  1324. return getControllingPlayer() == ThePlayerList->getLocalPlayer();
  1325. }
  1326. //=============================================================================
  1327. // Object::isLocallyControlled
  1328. //=============================================================================
  1329. Bool Object::isNeutralControlled() const
  1330. {
  1331. return getControllingPlayer() == ThePlayerList->getNeutralPlayer();
  1332. }
  1333. //-------------------------------------------------------------------------------------------------
  1334. inline Bool isPosDifferent(const Coord3D* a, const Coord3D* b)
  1335. {
  1336. // this is necessary because PhysicsBehavior may generate tiny changes even when
  1337. // "standing still", due to roundoff errors. It's important that we only invalidate
  1338. // the PartitionManager stuff when the pos/orientation really changes (for efficiency purposes)
  1339. // so we must put in some cleverness...
  1340. const Real THRESH = 0.01f;
  1341. if (fabs(a->x - b->x) > THRESH)
  1342. return true;
  1343. if (fabs(a->y - b->y) > THRESH)
  1344. return true;
  1345. if (fabs(a->z - b->z) > THRESH)
  1346. return true;
  1347. return false;
  1348. }
  1349. //-------------------------------------------------------------------------------------------------
  1350. inline Bool isAngleDifferent(Real a, Real b)
  1351. {
  1352. // this is necessary because PhysicsBehavior may generate tiny changes even when
  1353. // "standing still", due to roundoff errors. It's important that we only invalidate
  1354. // the PartitionManager stuff when the pos/orientation really changes (for efficiency purposes)
  1355. // so we must put in some cleverness...
  1356. const Real THRESH = 0.01f; // in radians, this is approx 1/2 degree.
  1357. if (fabs(a - b) > THRESH)
  1358. return true;
  1359. return false;
  1360. }
  1361. //-------------------------------------------------------------------------------------------------
  1362. void Object::reactToTurretChange( WhichTurretType turret, Real oldRotation, Real oldPitch )
  1363. {
  1364. Real currentRotation = 0.0f;
  1365. Real currentPitch = 0.0f;
  1366. if( getAI() )
  1367. {
  1368. getAI()->getTurretRotAndPitch( turret, &currentRotation, &currentPitch );
  1369. }
  1370. Bool rotationChange = (currentRotation != oldRotation);
  1371. // Bool pitchChange = (currentPitch != oldPitch);
  1372. if( rotationChange )
  1373. {
  1374. if (getContain())
  1375. getContain()->containReactToTransformChange();
  1376. }
  1377. }
  1378. //-------------------------------------------------------------------------------------------------
  1379. //DECLARE_PERF_TIMER(Object_reactToTransformChange)
  1380. void Object::reactToTransformChange(const Matrix3D* oldMtx, const Coord3D* oldPos, Real oldAngle)
  1381. {
  1382. //USE_PERF_TIMER(Object_reactToTransformChange)
  1383. if(_isnan(getPosition()->x) || _isnan(getPosition()->y) || _isnan(getPosition()->z)) {
  1384. DEBUG_CRASH(("Object pos is nan."));
  1385. TheGameLogic->destroyObject(this);
  1386. }
  1387. if (m_drawable)
  1388. {
  1389. m_drawable->setTransformMatrix( this->getTransformMatrix() );
  1390. }
  1391. Bool posDiff = isPosDifferent(oldPos, getPosition());
  1392. Bool angDiff = isAngleDifferent(oldAngle, getOrientation());
  1393. if (posDiff || angDiff)
  1394. {
  1395. if (m_partitionData)
  1396. m_partitionData->makeDirty(true);
  1397. if (getContain())
  1398. getContain()->containReactToTransformChange();
  1399. }
  1400. if (posDiff)
  1401. {
  1402. setTriggerAreaFlagsForChangeInPosition(); // Update for entered/exited
  1403. Region3D mapExtent;
  1404. TheTerrainLogic->getExtent(&mapExtent);
  1405. if (mapExtent.isInRegionNoZ(getPosition()))
  1406. m_privateStatus &= ~OFF_MAP;
  1407. else
  1408. m_privateStatus |= OFF_MAP;
  1409. }
  1410. }
  1411. //-------------------------------------------------------------------------------------------------
  1412. ObjectShroudStatus Object::getShroudedStatus(Int playerIndex) const
  1413. {
  1414. if (getTemplate()->isKindOf( KINDOF_ALWAYS_VISIBLE ))
  1415. return OBJECTSHROUD_CLEAR;
  1416. if (m_partitionData)
  1417. return m_partitionData->getShroudedStatus(playerIndex);
  1418. // This can happen for objects removed from the partition system (e.g.,
  1419. // for soldiers that are garrisoned inside a building).
  1420. return OBJECTSHROUD_CLEAR;
  1421. }
  1422. //-------------------------------------------------------------------------------------------------
  1423. /** Something is attempting to damage this object */
  1424. //-------------------------------------------------------------------------------------------------
  1425. void Object::attemptDamage( DamageInfo *damageInfo )
  1426. {
  1427. BodyModuleInterface* body = getBodyModule();
  1428. if (body)
  1429. body->attemptDamage( damageInfo );
  1430. /// @todo track damage dealt/attempted
  1431. //
  1432. // if actual damage occurred, and this is an object owned by the local player we
  1433. // might do a radar event for under attack. Note that we do not even try
  1434. // to do radar events for DAMAGE_PENALTY as that damage type is a type of damage
  1435. // that occurs with explicit player knowledge
  1436. //
  1437. if( damageInfo->out.m_actualDamageDealt > 0.0f &&
  1438. damageInfo->in.m_damageType != DAMAGE_PENALTY &&
  1439. damageInfo->in.m_damageType != DAMAGE_HEALING &&
  1440. !BitTest(damageInfo->in.m_sourcePlayerMask, getControllingPlayer()->getPlayerMask()) &&
  1441. m_radarData != NULL &&
  1442. getControllingPlayer() == ThePlayerList->getLocalPlayer() )
  1443. TheRadar->tryUnderAttackEvent( this );
  1444. }
  1445. //-------------------------------------------------------------------------------------------------
  1446. void Object::attemptHealing(Real amount, const Object* source)
  1447. {
  1448. BodyModuleInterface* body = getBodyModule();
  1449. if (body)
  1450. {
  1451. DamageInfo damageInfo;
  1452. damageInfo.in.m_damageType = DAMAGE_HEALING;
  1453. damageInfo.in.m_deathType = DEATH_NONE;
  1454. damageInfo.in.m_sourceID = source ? source->getID() : INVALID_ID;
  1455. damageInfo.in.m_amount = amount;
  1456. body->attemptHealing( &damageInfo );
  1457. }
  1458. }
  1459. ObjectID Object::getSoleHealingBenefactor( void ) const
  1460. {
  1461. UnsignedInt now = TheGameLogic->getFrame();
  1462. if( now > m_soleHealingBenefactorExpirationFrame )
  1463. return INVALID_ID;
  1464. return m_soleHealingBenefactorID;
  1465. }
  1466. Bool Object::attemptHealingFromSoleBenefactor ( Real amount, const Object* source, UnsignedInt duration )
  1467. {///< for the non-stacking healers like ambulance and propaganda
  1468. if( ! source ) // sanity
  1469. return FALSE;
  1470. UnsignedInt now = TheGameLogic->getFrame();
  1471. ObjectID id = source->getID();
  1472. // Either it is ok to accept healing from any who offer or this is my guy, calling again
  1473. if( now > m_soleHealingBenefactorExpirationFrame || m_soleHealingBenefactorID == id )
  1474. {
  1475. m_soleHealingBenefactorID = id;
  1476. m_soleHealingBenefactorExpirationFrame = now + duration;
  1477. BodyModuleInterface* body = getBodyModule();
  1478. if (body)
  1479. {
  1480. DamageInfo damageInfo;
  1481. damageInfo.in.m_damageType = DAMAGE_HEALING;
  1482. damageInfo.in.m_deathType = DEATH_NONE;
  1483. damageInfo.in.m_sourceID = source ? source->getID() : INVALID_ID;
  1484. damageInfo.in.m_amount = amount;
  1485. body->attemptHealing( &damageInfo );
  1486. }
  1487. return TRUE;
  1488. }
  1489. return FALSE;
  1490. }
  1491. //-------------------------------------------------------------------------------------------------
  1492. Real Object::estimateDamage( DamageInfoInput& damageInfo ) const
  1493. {
  1494. BodyModuleInterface* body = getBodyModule();
  1495. if (body)
  1496. return body->estimateDamage( damageInfo );
  1497. return 0.0f;
  1498. }
  1499. //-------------------------------------------------------------------------------------------------
  1500. /** Do so much damage to an object that it will certainly die */
  1501. //-------------------------------------------------------------------------------------------------
  1502. void Object::kill()
  1503. {
  1504. DamageInfo damageInfo;
  1505. // Do unmodifiable damage equal to their max health to kill.
  1506. damageInfo.in.m_damageType = DAMAGE_UNRESISTABLE;
  1507. damageInfo.in.m_deathType = DEATH_NORMAL;
  1508. damageInfo.in.m_sourceID = INVALID_ID;
  1509. damageInfo.in.m_amount = getBodyModule()->getMaxHealth();
  1510. attemptDamage( &damageInfo );
  1511. DEBUG_ASSERTCRASH(!damageInfo.out.m_noEffect, ("Attempting to kill an unKillable object (InactiveBody?)\n"));
  1512. } // end kill
  1513. //-------------------------------------------------------------------------------------------------
  1514. /** Restore max health to this Object */
  1515. //-------------------------------------------------------------------------------------------------
  1516. void Object::healCompletely()
  1517. {
  1518. attemptHealing(HUGE_DAMAGE_AMOUNT, NULL);
  1519. }
  1520. //-------------------------------------------------------------------------------------------------
  1521. //-------------------------------------------------------------------------------------------------
  1522. void Object::setEffectivelyDead(Bool dead)
  1523. {
  1524. if (dead)
  1525. BitSet(m_privateStatus, EFFECTIVELY_DEAD);
  1526. else
  1527. BitClear(m_privateStatus, EFFECTIVELY_DEAD);
  1528. if (dead)
  1529. {
  1530. if( m_radarData )
  1531. TheRadar->removeObject( this );
  1532. }
  1533. }
  1534. //-------------------------------------------------------------------------------------------------
  1535. void Object::setCaptured(Bool isCaptured)
  1536. {
  1537. if (isCaptured)
  1538. BitSet(m_privateStatus, CAPTURED);
  1539. else
  1540. {
  1541. DEBUG_LOG(("Clearing Captured Status. This should never happen. jkmcd"));
  1542. BitClear(m_privateStatus, CAPTURED);
  1543. }
  1544. // No need to see if we should skip updates, this flag has no effect on skipping updates.
  1545. }
  1546. //-------------------------------------------------------------------------------------------------
  1547. Bool Object::isStructure(void) const
  1548. {
  1549. return isKindOf(KINDOF_STRUCTURE);
  1550. }
  1551. //-------------------------------------------------------------------------------------------------
  1552. Bool Object::isFactionStructure(void) const
  1553. {
  1554. KindOfMaskType bits;
  1555. bits.set(KINDOF_FS_POWER);
  1556. bits.set(KINDOF_FS_FACTORY);
  1557. bits.set(KINDOF_FS_BASE_DEFENSE);
  1558. bits.set(KINDOF_FS_TECHNOLOGY);
  1559. return isAnyKindOf(bits);
  1560. }
  1561. //-------------------------------------------------------------------------------------------------
  1562. Bool Object::isNonFactionStructure(void) const
  1563. {
  1564. return isStructure() && !isFactionStructure();
  1565. }
  1566. //-------------------------------------------------------------------------------------------------
  1567. void Object::setReceivingDifficultyBonus(Bool receive)
  1568. {
  1569. if (receive == m_isReceivingDifficultyBonus) {
  1570. return;
  1571. }
  1572. m_isReceivingDifficultyBonus = receive;
  1573. getControllingPlayer()->friend_applyDifficultyBonusesForObject(this, m_isReceivingDifficultyBonus);
  1574. }
  1575. //-------------------------------------------------------------------------------------------------
  1576. //- DISABLEDNESS STUFF ----------------------------------------------------------------------------
  1577. //-------------------------------------------------------------------------------------------------
  1578. void Object::setDisabled( DisabledType type )
  1579. {
  1580. setDisabledUntil(type, FOREVER);
  1581. }
  1582. //-------------------------------------------------------------------------------------------------
  1583. void Object::setDisabledUntil( DisabledType type, UnsignedInt frame )
  1584. {
  1585. Bool edgeCase = !isDisabled();
  1586. if( type < 0 || type >= DISABLED_COUNT )
  1587. {
  1588. DEBUG_CRASH( ("Invalid disabled type value %d specified -- doesn't not exist!", type ) );
  1589. return;
  1590. }
  1591. //Handle audio events!
  1592. AudioEventRTS sound;
  1593. if( type == DISABLED_UNMANNED && !isKindOf( KINDOF_DRONE ) )
  1594. {
  1595. //We've been sniped! Play a splatter sound for the pilot losing his face.
  1596. sound = TheAudio->getMiscAudio()->m_splatterVehiclePilotsBrain;
  1597. sound.setPosition( getPosition() );
  1598. TheAudio->addAudioEvent( &sound );
  1599. }
  1600. else if( type == DISABLED_UNDERPOWERED || type == DISABLED_EMP || type == DISABLED_HACKED )
  1601. {
  1602. //We've lost power -- make sure we aren't already out of power as the sounds shouldn't happen
  1603. //if you were already disabled.
  1604. if( !isDisabledByType( DISABLED_UNDERPOWERED ) &&
  1605. !isDisabledByType( DISABLED_EMP ) &&
  1606. !isDisabledByType( DISABLED_HACKED ) )
  1607. {
  1608. if( isKindOf( KINDOF_STRUCTURE ) )
  1609. {
  1610. sound = TheAudio->getMiscAudio()->m_buildingDisabled;
  1611. sound.setPosition( getPosition() );
  1612. TheAudio->addAudioEvent( &sound );
  1613. }
  1614. else if( isKindOf( KINDOF_VEHICLE ) )
  1615. {
  1616. sound = TheAudio->getMiscAudio()->m_vehicleDisabled;
  1617. sound.setPosition( getPosition() );
  1618. TheAudio->addAudioEvent( &sound );
  1619. }
  1620. }
  1621. }
  1622. if( m_disabledTillFrame[ type ] != frame )
  1623. {
  1624. // an edge-test for disabledness, for type. This INCREMENTS m_pauseCount
  1625. // srj sez: HELD nevers disables special powers.
  1626. if ( type != DISABLED_HELD && !isDisabledByType( type ) )
  1627. pauseAllSpecialPowers( TRUE );
  1628. m_disabledTillFrame[ type ] = frame;
  1629. m_disabledMask.set( type, frame > TheGameLogic->getFrame() );
  1630. if( m_drawable )
  1631. {
  1632. if( isDisabled() )
  1633. {
  1634. // Held does not tint anybody. If we are multiply disabled, the other setting will hit the tint,
  1635. // and in clear, only-held and not-disabled are both causes to untint.
  1636. // Doh. Also shouldn't be tinting when disabled by scripting.
  1637. // Doh^2. Also shouldn't be CLEARING tinting if we're disabling by held or script disabledness
  1638. // Doh^3. Unmanned is no tint too
  1639. if( type != DISABLED_HELD && type != DISABLED_SCRIPT_DISABLED && type != DISABLED_UNMANNED )
  1640. {
  1641. m_drawable->setTintStatus( TINT_STATUS_DISABLED );
  1642. }
  1643. }
  1644. }
  1645. ContainModuleInterface *contain = getContain();
  1646. if ( contain )
  1647. {
  1648. Object *rider = (Object*)contain->friend_getRider();
  1649. if ( rider )
  1650. {
  1651. rider->setDisabledUntil(type, frame);
  1652. }
  1653. }
  1654. }
  1655. if( type == DISABLED_UNMANNED && !isKindOf( KINDOF_DRONE ) )
  1656. {
  1657. //strange but true: If I am a carbomb,
  1658. //my driver actually has a dead-man's
  1659. //trigger for my dynamite...
  1660. //If he gets sniped, I blow up! Wheeee!
  1661. WeaponSetFlags flags;
  1662. flags.set( WEAPONSET_CARBOMB );
  1663. const WeaponTemplateSet* set = getTemplate()->findWeaponTemplateSet( flags );
  1664. if( set && set->testWeaponSetFlag( WEAPONSET_CARBOMB ) )
  1665. {
  1666. Object* sniper = TheGameLogic->findObjectByID( getBodyModule()->getLastDamageInfo()->in.m_sourceID );
  1667. if ( sniper )
  1668. sniper->scoreTheKill( this );
  1669. kill();
  1670. }
  1671. else
  1672. {
  1673. //This vehicle's pilot has been sniped, so we want to clear the veterancy rating (if any)
  1674. ExperienceTracker *xpTracker = getExperienceTracker();
  1675. if( xpTracker )
  1676. {
  1677. xpTracker->setExperienceAndLevel( 0 );
  1678. }
  1679. //Not only that, but it also loses any healing bonuses it may have earned in its prior life
  1680. {
  1681. static const NameKeyType key_AutoHealBehavior = NAMEKEY("AutoHealBehavior");
  1682. AutoHealBehavior* autoHeal = (AutoHealBehavior*)(findUpdateModule( key_AutoHealBehavior ));
  1683. if (autoHeal)
  1684. autoHeal->undoUpgrade();
  1685. }
  1686. }
  1687. }
  1688. // This will only be called if we were NOT disabled before coming into this function.
  1689. if (edgeCase) {
  1690. onDisabledEdge(true);
  1691. }
  1692. }
  1693. //-------------------------------------------------------------------------------------------------
  1694. Bool Object::clearDisabled( DisabledType type )
  1695. {
  1696. if( type < 0 || type >= DISABLED_COUNT )
  1697. {
  1698. DEBUG_CRASH( ("Invalid disabled type value %d specified -- doesn't not exist!", type ) );
  1699. return FALSE;
  1700. }
  1701. if (!isDisabledByType(type)) {
  1702. return FALSE;
  1703. }
  1704. if( type == DISABLED_UNDERPOWERED || type == DISABLED_EMP || type == DISABLED_HACKED )
  1705. {
  1706. //We've regained power-- make sure we aren't still disabled by another type.
  1707. AudioEventRTS sound;
  1708. if( (!isDisabledByType( DISABLED_UNDERPOWERED ) || type == DISABLED_UNDERPOWERED ) &&
  1709. (!isDisabledByType( DISABLED_EMP ) || type == DISABLED_EMP ) &&
  1710. (!isDisabledByType( DISABLED_HACKED ) || type == DISABLED_HACKED ) )
  1711. {
  1712. if( isKindOf( KINDOF_STRUCTURE ) )
  1713. {
  1714. sound = TheAudio->getMiscAudio()->m_buildingReenabled;
  1715. sound.setPosition( getPosition() );
  1716. TheAudio->addAudioEvent( &sound );
  1717. }
  1718. else if( isKindOf( KINDOF_VEHICLE ) )
  1719. {
  1720. sound = TheAudio->getMiscAudio()->m_vehicleReenabled;
  1721. sound.setPosition( getPosition() );
  1722. TheAudio->addAudioEvent( &sound );
  1723. }
  1724. }
  1725. }
  1726. // an edge-test for disabledness, for type. This DECREMENTS m_pauseCount
  1727. // srj sez: HELD nevers disables special powers.
  1728. if ( type != DISABLED_HELD && isDisabledByType( type ) )
  1729. pauseAllSpecialPowers( FALSE );
  1730. ContainModuleInterface *contain = getContain();
  1731. if ( contain )
  1732. {
  1733. // We explicitly pass stuff in up in the set, so we need to turn it off if it is a forever type
  1734. Object *rider = (Object*)contain->friend_getRider();
  1735. if( rider && (m_disabledTillFrame[ type ] == FOREVER) )
  1736. {
  1737. rider->clearDisabled(type);
  1738. }
  1739. }
  1740. m_disabledTillFrame[ type ] = NEVER;
  1741. m_disabledMask.set( type, 0 );
  1742. DisabledMaskType exceptions;
  1743. exceptions.set(DISABLED_HELD);
  1744. exceptions.set(DISABLED_SCRIPT_DISABLED);
  1745. exceptions.set(DISABLED_UNMANNED);
  1746. // to clarify, if I am NOT disabled by anything other than DISABLED_HELD, or DISABLED_SCRIPT_DISABLED
  1747. if( !isDisabled() || getDisabledFlags().countInverseIntersection( exceptions ) == 0 )
  1748. {
  1749. if (m_drawable)
  1750. m_drawable->clearTintStatus( TINT_STATUS_DISABLED );
  1751. }
  1752. checkDisabledStatus();// in case we just edged
  1753. // if we're no longer disabled by anything, then call the edge function.
  1754. if (!isDisabled()) {
  1755. onDisabledEdge(false);
  1756. }
  1757. return TRUE;
  1758. }
  1759. //-------------------------------------------------------------------------------------------------
  1760. //Checks any timers and clears disabled statii that have expired.
  1761. //-------------------------------------------------------------------------------------------------
  1762. void Object::checkDisabledStatus()
  1763. {
  1764. UnsignedInt now = TheGameLogic->getFrame();
  1765. for( int i = 0; i < DISABLED_COUNT; i++ )
  1766. {
  1767. DisabledType type = (DisabledType)i;
  1768. if( isDisabledByType( type ) )
  1769. {
  1770. if ( now >= m_disabledTillFrame[ i ] )
  1771. {
  1772. clearDisabled( type ); // This will also DECREMENT m_pauseCount in all specialpowers
  1773. m_disabledMask.set( type, 0 );
  1774. }
  1775. }
  1776. }
  1777. }
  1778. //-------------------------------------------------------------------------------------------------
  1779. void Object::pauseAllSpecialPowers( const Bool disabling ) const
  1780. {
  1781. for (BehaviorModule** m = m_behaviors; *m; ++m)
  1782. {
  1783. SpecialPowerModuleInterface* sp = (*m)->getSpecialPower();
  1784. if (!sp)
  1785. continue;
  1786. sp->pauseCountdown( disabling );// So it will pause if we are disabling.
  1787. }
  1788. }
  1789. //-------------------------------------------------------------------------------------------------
  1790. //-------------------------------------------------------------------------------------------------
  1791. /** Clear the previous entered/exited flags. */
  1792. //-------------------------------------------------------------------------------------------------
  1793. void Object::updateTriggerAreaFlags()
  1794. {
  1795. Int j = 0;
  1796. // Update the flags, and remove any trigger areas that this object isn't inside.
  1797. for (Int i=0; i<m_numTriggerAreasActive; i++)
  1798. {
  1799. if (!m_triggerInfo[j].isInside)
  1800. continue;
  1801. m_triggerInfo[j].entered = false;
  1802. m_triggerInfo[j].exited = false;
  1803. m_triggerInfo[j].isInside = m_triggerInfo[i].isInside;
  1804. m_triggerInfo[j].pTrigger = m_triggerInfo[i].pTrigger;
  1805. j++;
  1806. }
  1807. m_numTriggerAreasActive = j;
  1808. }
  1809. //-------------------------------------------------------------------------------------------------
  1810. void Object::onCollide( Object *other, const Coord3D *loc, const Coord3D *normal )
  1811. {
  1812. for (BehaviorModule** m = m_behaviors; *m; ++m)
  1813. {
  1814. CollideModuleInterface* collide = (*m)->getCollide();
  1815. if (!collide)
  1816. continue;
  1817. // check each time thru the loop, in case a collide module sets it
  1818. if (getStatusBits() & OBJECT_STATUS_NO_COLLISIONS)
  1819. {
  1820. #ifdef DEBUG_CRC
  1821. //DEBUG_LOG(("Object::onCollide() - OBJECT_STATUS_NO_COLLISIONS set\n"));
  1822. #endif
  1823. break;
  1824. }
  1825. #ifdef DEBUG_CRC
  1826. //DEBUG_LOG(("Object::onCollide() - calling collide module\n"));
  1827. #endif
  1828. collide->onCollide(other, loc, normal);
  1829. }
  1830. }
  1831. //-------------------------------------------------------------------------------------------------
  1832. Bool Object::isSalvageCrate() const
  1833. {
  1834. for( BehaviorModule** m = m_behaviors; *m; ++m )
  1835. {
  1836. CollideModuleInterface* collide = (*m)->getCollide();
  1837. if( collide && collide->isSalvageCrateCollide() )
  1838. {
  1839. return true;
  1840. }
  1841. }
  1842. return false;
  1843. }
  1844. //-------------------------------------------------------------------------------------------------
  1845. /**
  1846. Our owning player is telling us to recheck our UpgradeModules, as an upgrade has completed
  1847. */
  1848. void Object::updateUpgradeModules()
  1849. {
  1850. Int64 playerMask = getControllingPlayer()->getCompletedUpgradeMask();
  1851. Int64 objectMask = getObjectCompletedUpgradeMask();
  1852. Int64 maskToCheck = playerMask | objectMask;
  1853. // We need to add in all of the already owned upgrades to handle "AND" requiring upgrades.
  1854. // We combine all the masks in case someone has a Object AND Player combination
  1855. for (BehaviorModule** module = m_behaviors; *module; ++module)
  1856. {
  1857. UpgradeModuleInterface* upgrade = (*module)->getUpgrade();
  1858. if (!upgrade)
  1859. continue;
  1860. if( !upgrade->isAlreadyUpgraded() )
  1861. {
  1862. upgrade->attemptUpgrade( maskToCheck );
  1863. }
  1864. }
  1865. }
  1866. //-------------------------------------------------------------------------------------------------
  1867. //This function sucks.
  1868. //It was added for objects that can disguise as other objects and contain upgraded subobject overrides.
  1869. //A concrete example is the bomb truck. Different payloads are displayed based on which upgrades have been
  1870. //made. When the bomb truck disguises as something else, these subobjects are lost because the vector is
  1871. //stored in W3DDrawModule. When we revert back to the original bomb truck, we call this function to
  1872. //recalculate those upgraded subobjects.
  1873. //-------------------------------------------------------------------------------------------------
  1874. void Object::forceRefreshSubObjectUpgradeStatus()
  1875. {
  1876. for (BehaviorModule** module = m_behaviors; *module; ++module)
  1877. {
  1878. UpgradeModuleInterface* upgrade = (*module)->getUpgrade();
  1879. if (!upgrade)
  1880. continue;
  1881. if( upgrade->isSubObjectsUpgrade() )
  1882. {
  1883. upgrade->forceRefreshUpgrade();
  1884. }
  1885. }
  1886. }
  1887. //-------------------------------------------------------------------------------------------------
  1888. /** Returns whether an object entered or exited an area. */
  1889. //-------------------------------------------------------------------------------------------------
  1890. Bool Object::didEnterOrExit() const
  1891. {
  1892. if (isKindOf(KINDOF_INERT)) {
  1893. return FALSE;
  1894. }
  1895. // note that this needs to return true if we
  1896. // entered or exited on the current frame OR
  1897. // the previous frame... since the current execution
  1898. // order is ScriptEngine, then ObjectUpdates,
  1899. // enter/exits detected in ObjectUpdate on frame N
  1900. // won't be noticed by the ScriptEngine till frame N+1.
  1901. UnsignedInt now = TheGameLogic->getFrame();
  1902. return m_enteredOrExitedFrame == now || m_enteredOrExitedFrame == now - 1;
  1903. }
  1904. //-------------------------------------------------------------------------------------------------
  1905. /** Returns whether an object entered an area. */
  1906. //-------------------------------------------------------------------------------------------------
  1907. Bool Object::didEnter(const PolygonTrigger *pTrigger) const
  1908. {
  1909. if (!didEnterOrExit())
  1910. return false;
  1911. DEBUG_ASSERTCRASH(!isKindOf(KINDOF_INERT), ("Asking whether an inert object entered or exited. This is invalid.\n"));
  1912. for (Int i=0; i<m_numTriggerAreasActive; i++)
  1913. {
  1914. if (m_triggerInfo[i].entered && m_triggerInfo[i].pTrigger == pTrigger)
  1915. return true;
  1916. }
  1917. return false;
  1918. }
  1919. //-------------------------------------------------------------------------------------------------
  1920. /** Returns whether an object entered an area. */
  1921. //-------------------------------------------------------------------------------------------------
  1922. Bool Object::didExit(const PolygonTrigger *pTrigger) const
  1923. {
  1924. if (!didEnterOrExit())
  1925. return false;
  1926. DEBUG_ASSERTCRASH(!isKindOf(KINDOF_INERT), ("Asking whether an inert object entered or exited. This is invalid.\n"));
  1927. for (Int i=0; i<m_numTriggerAreasActive; i++)
  1928. {
  1929. if (m_triggerInfo[i].exited && m_triggerInfo[i].pTrigger == pTrigger)
  1930. return true;
  1931. }
  1932. return false;
  1933. }
  1934. //-------------------------------------------------------------------------------------------------
  1935. /** Returns whether an object is inside an area. */
  1936. //-------------------------------------------------------------------------------------------------
  1937. Bool Object::isInside(const PolygonTrigger *pTrigger) const
  1938. {
  1939. DEBUG_ASSERTCRASH(!isKindOf(KINDOF_INERT), ("Asking whether an inert is inside a trigger area. This is invalid.\n"));
  1940. for (Int i=0; i<m_numTriggerAreasActive; i++)
  1941. {
  1942. if (m_triggerInfo[i].isInside && m_triggerInfo[i].pTrigger == pTrigger)
  1943. return true;
  1944. }
  1945. return false;
  1946. }
  1947. // ------------------------------------------------------------------------------------------------
  1948. /** Get production exit interface in object is present */
  1949. // ------------------------------------------------------------------------------------------------
  1950. ExitInterface *Object::getObjectExitInterface() const
  1951. {
  1952. ExitInterface *exitInterface = NULL;
  1953. for( BehaviorModule **umod = m_behaviors; *umod; ++umod )
  1954. {
  1955. if( (exitInterface = (*umod)->getUpdateExitInterface()) != NULL )
  1956. break;
  1957. }
  1958. // If you don't have a fancy one, you may have one from your contain module,
  1959. // since if you can contain something, they will need to get out.
  1960. if( exitInterface == NULL )
  1961. {
  1962. ContainModuleInterface *cmod = getContain();
  1963. if( cmod )
  1964. {
  1965. exitInterface = cmod->getContainExitInterface();
  1966. }
  1967. }
  1968. return exitInterface;
  1969. } // end getObjectExitInterface
  1970. //-------------------------------------------------------------------------------------------------
  1971. /** Checks the object against trigger areas when the position changes. */
  1972. //-------------------------------------------------------------------------------------------------
  1973. void Object::setTriggerAreaFlagsForChangeInPosition()
  1974. {
  1975. // projectiles cannot trigger areas. (jkmcd)
  1976. // neither can inert objects, like the radar ping, etc. (jkmcd)
  1977. if (isKindOf(KINDOF_PROJECTILE) || isKindOf(KINDOF_INERT))
  1978. return;
  1979. ICoord3D iPos;
  1980. Coord3D pos = *getPosition();
  1981. iPos.x = REAL_TO_INT(pos.x);
  1982. iPos.y = REAL_TO_INT(pos.y);
  1983. iPos.z = 0; // Trigger areas compare on xy only.
  1984. if (m_iPos.x == iPos.x && m_iPos.y == iPos.y)
  1985. {
  1986. return; // didn't move enough to change integer position.
  1987. }
  1988. if (getAIUpdateInterface())
  1989. {
  1990. TheAI->pathfinder()->updatePos(this, getPosition());
  1991. }
  1992. UnsignedInt now = TheGameLogic->getFrame();
  1993. if (m_enteredOrExitedFrame != 0 && m_enteredOrExitedFrame != now)
  1994. updateTriggerAreaFlags();
  1995. // Check for exited.
  1996. Int i;
  1997. for (i=0; i<m_numTriggerAreasActive; i++)
  1998. {
  1999. if (!m_triggerInfo[i].pTrigger->pointInTrigger(m_iPos))
  2000. {
  2001. m_triggerInfo[i].isInside = false;
  2002. m_triggerInfo[i].exited = true;
  2003. m_enteredOrExitedFrame = now;
  2004. if (m_team)
  2005. m_team->setEnteredExited();
  2006. TheGameLogic->updateObjectsChangedTriggerAreas();
  2007. #ifdef _DEBUG
  2008. //TheScriptEngine->AppendDebugMessage("Object exited.", false);
  2009. #endif
  2010. }
  2011. }
  2012. m_iPos = iPos;
  2013. for (const PolygonTrigger *pTrig = PolygonTrigger::getFirstPolygonTrigger(); pTrig; pTrig = pTrig->getNext())
  2014. {
  2015. Bool skip = false;
  2016. for (i = 0; i < m_numTriggerAreasActive; i++)
  2017. {
  2018. if (m_triggerInfo[i].pTrigger == pTrig)
  2019. {
  2020. // Already handled this one in the check for exited above.
  2021. skip = true;
  2022. break;
  2023. }
  2024. }
  2025. if (skip)
  2026. continue;
  2027. if (pTrig->pointInTrigger(m_iPos))
  2028. {
  2029. if (m_numTriggerAreasActive < MAX_TRIGGER_AREA_INFOS)
  2030. {
  2031. m_triggerInfo[m_numTriggerAreasActive].isInside = true;
  2032. m_triggerInfo[m_numTriggerAreasActive].entered = true;
  2033. m_triggerInfo[m_numTriggerAreasActive].exited = false;
  2034. m_triggerInfo[m_numTriggerAreasActive].pTrigger = pTrig;
  2035. m_enteredOrExitedFrame = now;
  2036. if (m_team)
  2037. m_team->setEnteredExited();
  2038. TheGameLogic->updateObjectsChangedTriggerAreas();
  2039. ++m_numTriggerAreasActive;
  2040. #ifdef _DEBUG
  2041. //TheScriptEngine->AppendDebugMessage("Object entered.", false);
  2042. #endif
  2043. }
  2044. else
  2045. {
  2046. // Shouldn't happen.
  2047. static Bool didWarn = false;
  2048. if (!didWarn)
  2049. {
  2050. didWarn = true;
  2051. TheScriptEngine->AppendDebugMessage("***WARNING - Too many nested trigger areas. ***", true);
  2052. }
  2053. }
  2054. }
  2055. }
  2056. }
  2057. //-------------------------------------------------------------------------------------------------
  2058. //-------------------------------------------------------------------------------------------------
  2059. Bool Object::isInList(Object **pListHead) const
  2060. {
  2061. Bool result = m_prev || m_next || *pListHead == this;
  2062. #ifdef INTENSE_DEBUG
  2063. Bool found = false;
  2064. for (Object* o = *pListHead; o; o = o->m_next)
  2065. {
  2066. if (o == this)
  2067. {
  2068. found = true;
  2069. break;
  2070. }
  2071. }
  2072. DEBUG_ASSERTCRASH(found==result,("inconsistent links in Object::isInList"));
  2073. #endif
  2074. return result;
  2075. }
  2076. //-------------------------------------------------------------------------------------------------
  2077. //-------------------------------------------------------------------------------------------------
  2078. void Object::prependToList(Object **pListHead)
  2079. {
  2080. DEBUG_ASSERTCRASH(!isInList(pListHead), ("obj is already in a list"));
  2081. m_prev = NULL;
  2082. m_next = *pListHead;
  2083. if (*pListHead)
  2084. (*pListHead)->m_prev = this;
  2085. *pListHead = this;
  2086. }
  2087. //-------------------------------------------------------------------------------------------------
  2088. //-------------------------------------------------------------------------------------------------
  2089. void Object::setLayer(PathfindLayerEnum layer)
  2090. {
  2091. if (layer!=m_layer) {
  2092. #define no_SET_LAYER_INTENSE_DEBUG
  2093. #ifdef SET_LAYER_INTENSE_DEBUG
  2094. DEBUG_LOG(("Changing layer from %d to %d\n", m_layer, layer));
  2095. if (m_layer != LAYER_GROUND) {
  2096. if (TheTerrainLogic->objectInteractsWithBridgeLayer(this, m_layer)) {
  2097. DEBUG_CRASH(("Probably shouldn't be chaging layer. jba."));
  2098. }
  2099. }
  2100. #endif
  2101. TheAI->pathfinder()->removePos(this);
  2102. m_layer = layer;
  2103. TheAI->pathfinder()->updatePos(this, getPosition());
  2104. }
  2105. }
  2106. //-------------------------------------------------------------------------------------------------
  2107. //-------------------------------------------------------------------------------------------------
  2108. void Object::setDestinationLayer(PathfindLayerEnum layer)
  2109. {
  2110. if (layer!=m_destinationLayer) {
  2111. m_destinationLayer = layer;
  2112. }
  2113. }
  2114. // ------------------------------------------------------------------------------------------------
  2115. /** Set unique ID */
  2116. // ------------------------------------------------------------------------------------------------
  2117. void Object::setID( ObjectID id )
  2118. {
  2119. // sanity
  2120. DEBUG_ASSERTCRASH( id != INVALID_ID, ("Object::setID - Invalid id\n") );
  2121. // if id hasn't changed do nothing
  2122. if( m_id == id )
  2123. return;
  2124. // remove this objects previous id from the lookup table
  2125. TheGameLogic->removeObjectFromLookupTable( this );
  2126. // assign new id
  2127. m_id = id;
  2128. // add new id to lookup table
  2129. TheGameLogic->addObjectToLookupTable( this );
  2130. } // end setID
  2131. // ------------------------------------------------------------------------------------------------
  2132. Real Object::calculateHeightAboveTerrain(void) const
  2133. {
  2134. const Coord3D* pos = getPosition();
  2135. Real terrainZ = TheTerrainLogic->getLayerHeight( pos->x, pos->y, m_layer );
  2136. Real myZ = pos->z;
  2137. return myZ - terrainZ;
  2138. }
  2139. //-------------------------------------------------------------------------------------------------
  2140. //-------------------------------------------------------------------------------------------------
  2141. void Object::removeFromList(Object **pListHead)
  2142. {
  2143. if (m_next)
  2144. m_next->m_prev = m_prev;
  2145. if (m_prev)
  2146. m_prev->m_next = m_next;
  2147. else
  2148. *pListHead = m_next;
  2149. m_prev = NULL;
  2150. m_next = NULL;
  2151. }
  2152. //-------------------------------------------------------------------------------------------------
  2153. //-------------------------------------------------------------------------------------------------
  2154. void Object::friend_prepareForMapBoundaryAdjust(void)
  2155. {
  2156. // NOTE - DO NOT remove from pathfind map. jba.
  2157. // NO NO. jba. TheAI->pathfinder()->removeObjectFromPathfindMap( this );
  2158. // remove from the radar, remove from the partition manager
  2159. TheRadar->removeObject(this);
  2160. ThePartitionManager->unRegisterObject(this);
  2161. // The whole PartitionManager and all of the Looker data is about to be blown away,
  2162. // so forget what I think I have done
  2163. m_partitionLastLook->reset();
  2164. m_partitionLastShroud->reset();
  2165. m_partitionLastThreat->reset();
  2166. m_partitionLastValue->reset();
  2167. }
  2168. //-------------------------------------------------------------------------------------------------
  2169. //-------------------------------------------------------------------------------------------------
  2170. void Object::friend_notifyOfNewMapBoundary(void)
  2171. {
  2172. ThePartitionManager->registerObject(this);
  2173. TheRadar->addObject(this);
  2174. TheAI->pathfinder()->addObjectToPathfindMap( this );
  2175. // Now that the PartitionManager has finished its reset, we need to relook
  2176. handlePartitionCellMaintenance();
  2177. Region3D mapExtent;
  2178. TheTerrainLogic->getExtent(&mapExtent);
  2179. if (mapExtent.isInRegionNoZ(getPosition()))
  2180. m_privateStatus &= ~OFF_MAP;
  2181. else
  2182. m_privateStatus |= OFF_MAP;
  2183. }
  2184. //-------------------------------------------------------------------------------------------------
  2185. //-------------------------------------------------------------------------------------------------
  2186. void Object::calcNaturalRallyPoint(Coord2D *pt)
  2187. {
  2188. const Matrix3D *transform = getTransformMatrix();
  2189. Vector3 v;
  2190. //
  2191. // get the natural rally point from the template, this coord is in model space relative
  2192. // to the model (0,0,0)
  2193. //
  2194. /*
  2195. const Coord3D *naturalRallyPoint;
  2196. naturalRallyPoint = m_template->getNaturalRallyPoint();
  2197. v.X = naturalRallyPoint->x;
  2198. v.Y = naturalRallyPoint->y;
  2199. v.Z = naturalRallyPoint->z;
  2200. */
  2201. v.Set( 0, 0, 0 );
  2202. // transform the point into world space
  2203. transform->Transform_Vector( *transform, v, &v );
  2204. // we're only concerned with the 2D elements for now
  2205. pt->x = v.X;
  2206. pt->y = v.Y;
  2207. }
  2208. //-------------------------------------------------------------------------------------------------
  2209. Module* Object::findModule(NameKeyType key) const
  2210. {
  2211. Module* m = NULL;
  2212. for (BehaviorModule** b = m_behaviors; *b; ++b)
  2213. {
  2214. if ((*b)->getModuleNameKey() == key)
  2215. {
  2216. #ifdef INTENSE_DEBUG
  2217. if (m == NULL)
  2218. {
  2219. m = *b;
  2220. }
  2221. else
  2222. {
  2223. DEBUG_CRASH(("Duplicate modules found for name %s!\n",TheNameKeyGenerator->keyToName(key).str()));
  2224. }
  2225. #else
  2226. m = *b;
  2227. break;
  2228. #endif
  2229. }
  2230. }
  2231. return m;
  2232. }
  2233. //-------------------------------------------------------------------------------------------------
  2234. /**
  2235. * Returns true if object is currently able to move.
  2236. */
  2237. Bool Object::isMobile() const
  2238. {
  2239. if (isKindOf(KINDOF_IMMOBILE))
  2240. return false;
  2241. if( isDisabled() )
  2242. return false;
  2243. return true;
  2244. }
  2245. //-------------------------------------------------------------------------------------------------
  2246. void Object::scoreTheKill( const Object *victim )
  2247. {
  2248. // Do stuff that has nothing to do with experience points here, like tell our Player we killed something
  2249. /// @todo Multiplayer score hook location?
  2250. Player* victimController = victim->getControllingPlayer();
  2251. // if the other player is not a playable side (i.e. they are civilian, observer, whatever)
  2252. // we shouldn't count the kill.
  2253. if (victimController->isPlayableSide() == FALSE)
  2254. {
  2255. return;
  2256. }
  2257. if ( victim->isKindOf( KINDOF_IGNORED_IN_GUI ) )
  2258. return;
  2259. Player* controller = getControllingPlayer();
  2260. if (victimController)
  2261. {
  2262. victimController->getScoreKeeper()->addObjectLost(victim);
  2263. }
  2264. Relationship r = getRelationship(victim);
  2265. if (r != ENEMIES)
  2266. return;
  2267. // Don't count kills that I do on my own buildings or units, cause thats just silly.
  2268. if (controller == victimController)
  2269. {
  2270. return;
  2271. }
  2272. if (controller)
  2273. {
  2274. controller->getScoreKeeper()->addObjectDestroyed(victim);
  2275. controller->addSkillPointsForKill(this, victim);
  2276. controller->doBountyForKill(this, victim);
  2277. }
  2278. // Now handle experience, if we can gain any
  2279. if (m_experienceTracker && m_experienceTracker->isAcceptingExperiencePoints())
  2280. {
  2281. // srj sez: per dustin, no experience (et al) for killing things under construction.
  2282. if (!victim->testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION))
  2283. {
  2284. Int experienceValue = victim->getExperienceTracker()->getExperienceValue( this );
  2285. getExperienceTracker()->addExperiencePoints( experienceValue );
  2286. }
  2287. }
  2288. }
  2289. //-------------------------------------------------------------------------------------------------
  2290. VeterancyLevel Object::getVeterancyLevel() const
  2291. {
  2292. return m_experienceTracker ? m_experienceTracker->getVeterancyLevel() : LEVEL_REGULAR;
  2293. }
  2294. //-------------------------------------------------------------------------------------------------
  2295. void Object::friend_bindToDrawable( Drawable *draw )
  2296. {
  2297. m_drawable = draw;
  2298. if (m_drawable)
  2299. {
  2300. ModelConditionFlags set;
  2301. ModelConditionFlags clr;
  2302. for (int i = 0; i < WEAPONSET_COUNT; ++i)
  2303. {
  2304. ModelConditionFlagType mcs = TheWeaponSetTypeToModelConditionTypeMap[i];
  2305. if (m_curWeaponSetFlags.test(i))
  2306. set.set(mcs);
  2307. else
  2308. clr.set(mcs);
  2309. }
  2310. if (TheGlobalData)
  2311. {
  2312. if (TheGlobalData->m_forceModelsToFollowTimeOfDay)
  2313. {
  2314. set.set(MODELCONDITION_NIGHT, (TheGlobalData->m_timeOfDay == TIME_OF_DAY_NIGHT) ? 1 : 0);
  2315. }
  2316. if (TheGlobalData->m_forceModelsToFollowWeather)
  2317. {
  2318. set.set(MODELCONDITION_SNOW, (TheGlobalData->m_weather == WEATHER_SNOWY) ? 1 : 0);
  2319. }
  2320. }
  2321. m_drawable->clearAndSetModelConditionFlags(clr, set);
  2322. }
  2323. for (BehaviorModule** b = m_behaviors; *b; ++b)
  2324. {
  2325. (*b)->onDrawableBoundToObject();
  2326. }
  2327. }
  2328. //-------------------------------------------------------------------------------------------------
  2329. void Object::setSelectable(Bool selectable)
  2330. {
  2331. m_isSelectable = selectable;
  2332. if (m_drawable)
  2333. {
  2334. m_drawable->setSelectable(selectable);
  2335. }
  2336. }
  2337. //-------------------------------------------------------------------------------------------------
  2338. Bool Object::isSelectable() const
  2339. {
  2340. return getTemplate()->isKindOf(KINDOF_ALWAYS_SELECTABLE)
  2341. || (m_isSelectable
  2342. && !testStatus(OBJECT_STATUS_UNSELECTABLE)
  2343. && !isEffectivelyDead()
  2344. && !getTemplate()->isKindOf(KINDOF_DRONE)//Most drones are unselectable from being slaved, but the SpyDrone needs help
  2345. );
  2346. }
  2347. //-------------------------------------------------------------------------------------------------
  2348. Bool Object::isMassSelectable() const
  2349. {
  2350. return isSelectable() && !isKindOf(KINDOF_STRUCTURE);
  2351. }
  2352. //-------------------------------------------------------------------------------------------------
  2353. void Object::setWeaponSetFlag(WeaponSetType wst)
  2354. {
  2355. m_curWeaponSetFlags.set(wst);
  2356. m_weaponSet.updateWeaponSet(this);
  2357. if (m_drawable)
  2358. {
  2359. m_drawable->setModelConditionState(TheWeaponSetTypeToModelConditionTypeMap[wst]);
  2360. }
  2361. }
  2362. //-------------------------------------------------------------------------------------------------
  2363. void Object::clearWeaponSetFlag(WeaponSetType wst)
  2364. {
  2365. m_curWeaponSetFlags.set(wst, 0);
  2366. m_weaponSet.updateWeaponSet(this);
  2367. if (m_drawable)
  2368. {
  2369. m_drawable->clearModelConditionState(TheWeaponSetTypeToModelConditionTypeMap[wst]);
  2370. }
  2371. }
  2372. //-------------------------------------------------------------------------------------------------
  2373. Bool Object::hasSpecialPower( SpecialPowerType type ) const
  2374. {
  2375. return TEST_SPECIALPOWERMASK( m_specialPowerBits, type );
  2376. }
  2377. //-------------------------------------------------------------------------------------------------
  2378. void Object::onVeterancyLevelChanged( VeterancyLevel oldLevel, VeterancyLevel newLevel )
  2379. {
  2380. updateUpgradeModules();
  2381. const UpgradeTemplate* up = TheUpgradeCenter->findVeterancyUpgrade(newLevel);
  2382. if (up)
  2383. giveUpgrade(up);
  2384. BodyModuleInterface* body = getBodyModule();
  2385. if (body)
  2386. body->onVeterancyLevelChanged(oldLevel, newLevel);
  2387. Bool hideAnimationForStealth = ( ! isLocallyControlled() && testStatus(OBJECT_STATUS_STEALTHED));
  2388. Bool doAnimation = ( ! hideAnimationForStealth
  2389. && (newLevel > oldLevel)
  2390. && ( ! isKindOf(KINDOF_IGNORED_IN_GUI))); //First, we plan to do the animation if the level went up
  2391. switch (newLevel)
  2392. {
  2393. case LEVEL_REGULAR:
  2394. clearWeaponSetFlag(WEAPONSET_VETERAN);
  2395. clearWeaponSetFlag(WEAPONSET_ELITE);
  2396. clearWeaponSetFlag(WEAPONSET_HERO);
  2397. clearWeaponBonusCondition(WEAPONBONUSCONDITION_VETERAN);
  2398. clearWeaponBonusCondition(WEAPONBONUSCONDITION_ELITE);
  2399. clearWeaponBonusCondition(WEAPONBONUSCONDITION_HERO);
  2400. doAnimation = FALSE;//... but not if somehow up to Regular
  2401. break;
  2402. case LEVEL_VETERAN:
  2403. setWeaponSetFlag(WEAPONSET_VETERAN);
  2404. clearWeaponSetFlag(WEAPONSET_ELITE);
  2405. clearWeaponSetFlag(WEAPONSET_HERO);
  2406. setWeaponBonusCondition(WEAPONBONUSCONDITION_VETERAN);
  2407. clearWeaponBonusCondition(WEAPONBONUSCONDITION_ELITE);
  2408. clearWeaponBonusCondition(WEAPONBONUSCONDITION_HERO);
  2409. break;
  2410. case LEVEL_ELITE:
  2411. clearWeaponSetFlag(WEAPONSET_VETERAN);
  2412. setWeaponSetFlag(WEAPONSET_ELITE);
  2413. clearWeaponSetFlag(WEAPONSET_HERO);
  2414. clearWeaponBonusCondition(WEAPONBONUSCONDITION_VETERAN);
  2415. setWeaponBonusCondition(WEAPONBONUSCONDITION_ELITE);
  2416. clearWeaponBonusCondition(WEAPONBONUSCONDITION_HERO);
  2417. break;
  2418. case LEVEL_HEROIC:
  2419. clearWeaponSetFlag(WEAPONSET_VETERAN);
  2420. clearWeaponSetFlag(WEAPONSET_ELITE);
  2421. setWeaponSetFlag(WEAPONSET_HERO);
  2422. clearWeaponBonusCondition(WEAPONBONUSCONDITION_VETERAN);
  2423. clearWeaponBonusCondition(WEAPONBONUSCONDITION_ELITE);
  2424. setWeaponBonusCondition(WEAPONBONUSCONDITION_HERO);
  2425. break;
  2426. }
  2427. if( doAnimation && TheGameLogic->getDrawIconUI() )
  2428. {
  2429. if( TheAnim2DCollection && TheGlobalData->m_levelGainAnimationName.isEmpty() == FALSE )
  2430. {
  2431. Anim2DTemplate *animTemplate = TheAnim2DCollection->findTemplate( TheGlobalData->m_levelGainAnimationName );
  2432. Coord3D pos = *getPosition();
  2433. pos.add(&m_healthBoxOffset);
  2434. TheInGameUI->addWorldAnimation( animTemplate,
  2435. &pos,
  2436. WORLD_ANIM_FADE_ON_EXPIRE,
  2437. TheGlobalData->m_levelGainAnimationDisplayTimeInSeconds,
  2438. TheGlobalData->m_levelGainAnimationZRisePerSecond);
  2439. }
  2440. AudioEventRTS soundToPlay = TheAudio->getMiscAudio()->m_unitPromoted;
  2441. soundToPlay.setObjectID( getID() );
  2442. TheAudio->addAudioEvent( &soundToPlay );
  2443. }
  2444. }
  2445. //-------------------------------------------------------------------------------------------------
  2446. /**
  2447. * Returns true if object currently has some kind of attack capability
  2448. */
  2449. Bool Object::isAbleToAttack() const
  2450. {
  2451. //******************************************************
  2452. //********* AUTOMATICALLY FALSE CONDITIONS *************
  2453. //******************************************************
  2454. // For things that may or may not be able to normally attack, but are under a status condition
  2455. if (getStatusBits() & OBJECT_STATUS_NO_ATTACK)
  2456. return false;
  2457. // if we're contained within a transport we cannot attack unless it specifically allows us
  2458. const Object *containedBy = getContainedBy();
  2459. DEBUG_ASSERTCRASH( (containedBy == NULL) || (containedBy->getContain() != NULL), ("A %s thinks they are contained by something with no contain module!", getTemplate()->getName().str() ) );
  2460. if( containedBy && containedBy->getContain() && !containedBy->getContain()->isPassengerAllowedToFire() )
  2461. return false;
  2462. // We can't fire if under construction
  2463. if( testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION) )
  2464. return false;
  2465. // or being sold
  2466. if( testStatus(OBJECT_STATUS_SOLD) )
  2467. return false;
  2468. //We can't fire if we, as a portable structure, are aptly disabled
  2469. if ( isKindOf( KINDOF_PORTABLE_STRUCTURE ) || isKindOf( KINDOF_SPAWNS_ARE_THE_WEAPONS ))
  2470. {
  2471. if( isDisabledByType( DISABLED_HACKED ) || isDisabledByType( DISABLED_EMP ) )
  2472. return false;
  2473. }
  2474. //We can't fire if all our weapons are disabled!
  2475. //Currently, only turreted weapons can be disabled.
  2476. //ONLY DO THIS CHECK IF OUR UNIT DOESN'T HAVE THE
  2477. //KINDOF_CAN_ATTACK flag... nuke cannons have disabled
  2478. //turrets when not deployed, and need to be able to attack to deploy!
  2479. //Strategy centers can't attack when bombardment isn't active!
  2480. Bool anyEnabled = FALSE;
  2481. Bool anyWeapon = FALSE;
  2482. const AIUpdateInterface *ai = getAI();
  2483. if( ai && !isKindOf( KINDOF_CAN_ATTACK ) )
  2484. {
  2485. for( Int i = 0; i < WEAPONSLOT_COUNT; i++ )
  2486. {
  2487. //Find the weapon in this slot.
  2488. Weapon* weapon = getWeaponInWeaponSlot( (WeaponSlotType)i );
  2489. if( !weapon )
  2490. continue;
  2491. anyWeapon = TRUE;
  2492. //We found a weapon, is it a turret?
  2493. Real dummy;
  2494. WhichTurretType tur = ai->getWhichTurretForWeaponSlot( (WeaponSlotType)i, &dummy );
  2495. if( tur == TURRET_INVALID )
  2496. {
  2497. //Currently impossible to disable a non-turreted weapon, so we
  2498. //have a non turreted weapon that is enabled. Quit.
  2499. anyEnabled = TRUE;
  2500. break;
  2501. }
  2502. if( ai->isTurretEnabled( tur ) )
  2503. {
  2504. //The turret is enable, meaning we have an enabled weapon. Quit.
  2505. anyEnabled = TRUE;
  2506. break;;
  2507. }
  2508. }
  2509. if( anyWeapon && !anyEnabled )
  2510. {
  2511. //We failed to find any active weapons.
  2512. return FALSE;
  2513. }
  2514. }
  2515. //***************************************
  2516. //********* TRUE CONDITIONS *************
  2517. //***************************************
  2518. // for certain buildings
  2519. if (isKindOf(KINDOF_CAN_ATTACK))
  2520. return true;
  2521. // for garrisonned buildings that can attack sometimes
  2522. if (getStatusBits() & OBJECT_STATUS_CAN_ATTACK)
  2523. return true;
  2524. // for weaponless transports. This will make me think I can, but I will check if I literally can by looking
  2525. // at passenger weapons in CanAttack.
  2526. const ContainModuleInterface* contain = getContain();
  2527. if( contain && contain->isPassengerAllowedToFire() && contain->getContainCount() > 0 )
  2528. return true;
  2529. // if we have AI and a weapon, assume we know how to use it
  2530. if (getAIUpdateInterface() != NULL && m_weaponSet.hasAnyWeapon())
  2531. {
  2532. // actually, we don't want to do this; we want the troop crawler to be considered "able to attack"
  2533. // even if empty, so sayeth Dustin. (srj)
  2534. // // special case: if the only damage we do is DEPLOY, we must have some guys contained.
  2535. // if (m_weaponSet.hasSingleDamageType(DAMAGE_DEPLOY))
  2536. // {
  2537. // return contain->getContainCount() > 0;
  2538. // }
  2539. // else
  2540. {
  2541. return true;
  2542. }
  2543. }
  2544. SpawnBehaviorInterface *spawnInterface = getSpawnBehaviorInterface();
  2545. if( spawnInterface )
  2546. {
  2547. if( spawnInterface->canAnySlavesAttack() )
  2548. {
  2549. return TRUE;
  2550. }
  2551. }
  2552. //Default is no
  2553. return false;
  2554. }
  2555. //-------------------------------------------------------------------------------------------------
  2556. /**
  2557. * Mask/Un-Mask an object
  2558. */
  2559. void Object::maskObject( Bool mask )
  2560. {
  2561. // set or clear the mask bit
  2562. setStatus( OBJECT_STATUS_MASKED, mask );
  2563. //
  2564. // when masking objects they become unselected ... we do this in any situation for
  2565. // any player cause you aren't allowed to select masked objects, if the object is not
  2566. // selected (ie, belongs to another player) it's no big deal cause it won't be selected
  2567. // anyway
  2568. //
  2569. if (mask)
  2570. TheGameLogic->deselectObject(this, ~getControllingPlayer()->getPlayerMask(), TRUE);
  2571. } // end maskObject
  2572. //-------------------------------------------------------------------------------------------------
  2573. /*
  2574. * returns true if the current locomotor is an airborne one
  2575. */
  2576. Bool Object::isUsingAirborneLocomotor( void ) const
  2577. {
  2578. return ( m_ai && m_ai->getCurLocomotor() && ((m_ai->getCurLocomotor()->getLegalSurfaces() & LOCOMOTORSURFACE_AIR) != 0) );
  2579. }
  2580. //-------------------------------------------------------------------------------------------------
  2581. //THIS FUNCTION BELONGS AT THE OBJECT LEVEL BECAUSE THERE IS AT LEAST ONE SPECIAL UNIT
  2582. //(ANGRY MOB) WHICH NEEDS LOGIC-SIDE POSITION CALC'S...
  2583. //IT WOULD PROBABLY BE WISE TO MOVE ALL THE HARD-CODED DEFAULTS BELOW
  2584. //INTO A NEW Drawable::getHealthBox..() WHICH USES GEOM0INFO, MODEL DATA, INI DATA, ETC.
  2585. void Object::getHealthBoxPosition(Coord3D& pos) const
  2586. {
  2587. pos = *getPosition();
  2588. pos.z += getGeometryInfo().getMaxHeightAbovePosition() + 10;
  2589. pos.add(&m_healthBoxOffset);
  2590. // this needs to get moved to the mobspawnerupdate
  2591. if (isKindOf(KINDOF_MOB_NEXUS)) // quicker idiot test
  2592. {
  2593. pos.z += 20;// dear God, I confess my kluge, and repent.
  2594. }
  2595. }
  2596. //-------------------------------------------------------------------------------------------------
  2597. //THIS FUNCTION BELONGS AT THE OBJECT LEVEL BECAUSE THERE IS AT LEAST ONE SPECIAL UNIT
  2598. //(ANGRY MOB) WHICH NEEDS LOGIC-SIDE POSITION CALC'S...
  2599. //IT WOULD PROBABLY BE WISE TO MOVE ALL THE HARD-CODED DEFAULTS BELOW
  2600. //INTO A NEW Drawable::getHealthBox..() WHICH USES GEOM0INFO, MODEL DATA, INI DATA, ETC.
  2601. Bool Object::getHealthBoxDimensions(Real &healthBoxHeight, Real &healthBoxWidth) const
  2602. {
  2603. #ifdef CALC_HEALTHBAR_FROM_HITPOINTS
  2604. Real maxHP = getBodyModule()->getMaxHealth();
  2605. if( isKindOf( KINDOF_STRUCTURE ) )
  2606. {
  2607. //enforce healthBoxHeightMinimum/Maximum
  2608. healthBoxHeight = min(3.0f, max(5.0f, maxHP/50));
  2609. //enforce healthBoxWidthMinimum/Maximum
  2610. healthBoxWidth = min(150.0f, max(100.0f, maxHP/10));
  2611. return true;
  2612. }
  2613. else if ( isKindOf(KINDOF_MOB_NEXUS) )
  2614. {
  2615. //enforce healthBoxHeightMinimum/Maximum
  2616. healthBoxHeight = min(3.0f, max(5.0f, maxHP/50));
  2617. //enforce healthBoxWidthMinimum/Maximum
  2618. healthBoxWidth = min(100.0f, max(66.0f, maxHP/10));
  2619. return true;
  2620. }
  2621. else if ( isKindOf( KINDOF_IGNORED_IN_GUI ) )
  2622. {
  2623. healthBoxHeight = 0;
  2624. healthBoxWidth = 0;
  2625. return false;
  2626. }
  2627. else
  2628. {
  2629. //enforce healthBoxHeightMinimum/Maximum
  2630. healthBoxHeight = min(3.0f, max(5.0f, maxHP/50));
  2631. //enforce healthBoxWidthMinimum/Maximum
  2632. healthBoxWidth = min(150.0f, max(35.0f, maxHP/10));
  2633. return true;
  2634. }
  2635. #else
  2636. if ( isKindOf( KINDOF_IGNORED_IN_GUI ) )
  2637. {
  2638. healthBoxHeight = 0;
  2639. healthBoxWidth = 0;
  2640. return false;
  2641. }
  2642. //just add the major and minor axes
  2643. Real size = MAX(20.0f, MIN(150.0f, (getGeometryInfo().getMajorRadius() + getGeometryInfo().getMinorRadius())) );
  2644. healthBoxHeight = 3.0f;
  2645. healthBoxWidth = MAX(20.0f, size * 2.0f);
  2646. return TRUE;
  2647. #endif
  2648. }
  2649. //-------------------------------------------------------------------------------------------------
  2650. /**
  2651. * Update this object instance with properties from the map object
  2652. *
  2653. */
  2654. void Object::updateObjValuesFromMapProperties(Dict* properties)
  2655. {
  2656. Bool exists;
  2657. AsciiString valStr;
  2658. Bool valBool = false;
  2659. Int valInt = 0;
  2660. Real valReal = 0.0f;
  2661. valStr = properties->getAsciiString(TheKey_objectName, &exists);
  2662. if (exists) {
  2663. setName(valStr);
  2664. }
  2665. valInt = properties->getInt(TheKey_objectMaxHPs, &exists);
  2666. if (exists && valInt >= 0) {
  2667. BodyModuleInterface* body = getBodyModule();
  2668. if (body) {
  2669. body->setMaxHealth(valInt);
  2670. }
  2671. }
  2672. valInt = properties->getInt(TheKey_objectInitialHealth, &exists);
  2673. if (exists) {
  2674. BodyModuleInterface* body = getBodyModule();
  2675. if (body) {
  2676. body->setInitialHealth(valInt);
  2677. }
  2678. }
  2679. valInt = properties->getInt(TheKey_objectTime, &exists);
  2680. if (exists)
  2681. {
  2682. switch (valInt)
  2683. {
  2684. case 1:
  2685. getDrawable()->clearModelConditionState(MODELCONDITION_NIGHT);
  2686. break;
  2687. case 2:
  2688. getDrawable()->setModelConditionState(MODELCONDITION_NIGHT);
  2689. break;
  2690. default:
  2691. break;
  2692. }
  2693. }
  2694. valInt = properties->getInt(TheKey_objectWeather, &exists);
  2695. if (exists)
  2696. {
  2697. switch (valInt)
  2698. {
  2699. case 1:
  2700. getDrawable()->clearModelConditionState(MODELCONDITION_SNOW);
  2701. break;
  2702. case 2:
  2703. getDrawable()->setModelConditionState(MODELCONDITION_SNOW);
  2704. break;
  2705. default:
  2706. break;
  2707. }
  2708. }
  2709. // set the veterancy level
  2710. valInt = properties->getInt(TheKey_objectVeterancy, &exists);
  2711. if (exists) {
  2712. if (m_experienceTracker && m_experienceTracker->isTrainable())
  2713. {
  2714. m_experienceTracker->setVeterancyLevel((VeterancyLevel)valInt);
  2715. }
  2716. }
  2717. // set the aggressiveness/mood
  2718. valInt = properties->getInt(TheKey_objectAggressiveness, &exists);
  2719. if (exists) {
  2720. AIUpdateInterface *ai = getAIUpdateInterface();
  2721. if (ai)
  2722. {
  2723. ai->setAttitude((AttitudeType)valInt);
  2724. }
  2725. }
  2726. // set recruitable
  2727. valBool = properties->getBool(TheKey_objectRecruitableAI, &exists);
  2728. if (exists) {
  2729. if (getAIUpdateInterface())
  2730. {
  2731. getAIUpdateInterface()->setIsRecruitable(valBool);
  2732. }
  2733. }
  2734. // set selectable
  2735. valBool = properties->getBool(TheKey_objectSelectable, &exists);
  2736. if (exists) {
  2737. if (valBool != isSelectable()) {
  2738. setSelectable(valBool);
  2739. }
  2740. }
  2741. // set the stopping distance
  2742. valReal = properties->getReal(TheKey_objectStoppingDistance, &exists);
  2743. if (exists && valReal >= 0.5f)
  2744. {
  2745. if (getAIUpdateInterface() && getAIUpdateInterface()->getCurLocomotor())
  2746. {
  2747. Locomotor *loco = getAIUpdateInterface()->getCurLocomotor();
  2748. loco->setCloseEnoughDist(valReal);
  2749. }
  2750. }
  2751. // set the disabledness of this object
  2752. valBool = properties->getBool(TheKey_objectEnabled, &exists);
  2753. if (exists) {
  2754. setScriptStatus(OBJECT_STATUS_SCRIPT_DISABLED, !valBool);
  2755. }
  2756. // set the disabledness of this object
  2757. valBool = properties->getBool(TheKey_objectPowered, &exists);
  2758. if (exists) {
  2759. setScriptStatus(OBJECT_STATUS_SCRIPT_UNPOWERED, !valBool);
  2760. }
  2761. // set the invulnerability of the object
  2762. valBool = properties->getBool(TheKey_objectIndestructible, &exists);
  2763. if (exists) {
  2764. BodyModuleInterface* body = getBodyModule();
  2765. if (body) {
  2766. body->setIndestructible(valBool);
  2767. }
  2768. }
  2769. // set the sellability of the object
  2770. valBool = properties->getBool(TheKey_objectUnsellable, &exists);
  2771. if (exists) {
  2772. setScriptStatus(OBJECT_STATUS_SCRIPT_UNSELLABLE, valBool);
  2773. }
  2774. //Set the player targetable setting of the object
  2775. valBool = properties->getBool( TheKey_objectTargetable, &exists );
  2776. if( exists )
  2777. {
  2778. setScriptStatus(OBJECT_STATUS_SCRIPT_TARGETABLE, valBool);
  2779. }
  2780. // adjust the vision distance of this object, overriding its default vision distance
  2781. valInt = properties->getInt(TheKey_objectVisualRange, &exists);
  2782. if (exists)
  2783. {
  2784. if (valInt < 0)
  2785. valInt = 0;
  2786. m_visionRange = INT_TO_REAL(valInt);
  2787. }
  2788. // adjust the shroud clearing distance of this object, overriding its default distance
  2789. valInt = properties->getInt(TheKey_objectShroudClearingDistance, &exists);
  2790. if (exists)
  2791. {
  2792. if (valInt < 0)
  2793. valInt = 0.0f;
  2794. m_shroudClearingRange = INT_TO_REAL(valInt);
  2795. }
  2796. Int upgradeNum = 0;
  2797. do
  2798. {
  2799. AsciiString keyName;
  2800. keyName.format("%s%d", TheNameKeyGenerator->keyToName(TheKey_objectGrantUpgrade).str(), upgradeNum);
  2801. valStr = properties->getAsciiString(NAMEKEY(keyName), &exists);
  2802. if (exists)
  2803. {
  2804. const UpgradeTemplate *ut = TheUpgradeCenter->findUpgrade(valStr);
  2805. if (ut)
  2806. giveUpgrade(ut);
  2807. }
  2808. else
  2809. {
  2810. valStr.clear();
  2811. }
  2812. ++upgradeNum;
  2813. } while (!valStr.isEmpty());
  2814. }
  2815. //-------------------------------------------------------------------------------------------------
  2816. void Object::friend_adjustPowerForPlayer( Bool incoming )
  2817. {
  2818. if (isDisabled() && getTemplate()->getEnergyProduction() > 0)
  2819. {
  2820. // Disabledness only affects Producers, not Consumers.
  2821. return;
  2822. }
  2823. if (incoming) {
  2824. getControllingPlayer()->getEnergy()->objectEnteringInfluence(this);
  2825. } else {
  2826. getControllingPlayer()->getEnergy()->objectLeavingInfluence(this);
  2827. }
  2828. }
  2829. ///////////////////////////////////////////////////////////////////////////////////////////////////
  2830. ///////////////////////////////////////////////////////////////////////////////////////////////////
  2831. ///////////////////////////////////////////////////////////////////////////////////////////////////
  2832. //-------------------------------------------------------------------------------------------------
  2833. void Object::onDisabledEdge(Bool becomingDisabled)
  2834. {
  2835. Player* controller = getControllingPlayer();
  2836. // can be called during game teardown, thus controller can be null
  2837. if (controller)
  2838. {
  2839. //@todo jkmcd - Colin suggested we rewrite this to use the interface stuff. I agree, but need
  2840. // to get some more bugs fixed today.
  2841. static NameKeyType radar = NAMEKEY("RadarUpgrade");
  2842. Module *mod = mod = findModule(radar);
  2843. if (mod) {
  2844. RadarUpgrade *radarMod = (RadarUpgrade*) mod;
  2845. if (radarMod->isAlreadyUpgraded()) {
  2846. // Need to decrement the count here, because we own a radar upgrade
  2847. if (becomingDisabled) {
  2848. controller->removeRadar(radarMod->getIsDisableProof());
  2849. } else {
  2850. controller->addRadar(radarMod->getIsDisableProof());
  2851. }
  2852. }
  2853. }
  2854. }
  2855. // We will need to adjust power ... somehow ...
  2856. Int powerToAdjust = getTemplate()->getEnergyProduction();
  2857. if( powerToAdjust > 0 )
  2858. {
  2859. // We can't affect something that consumes, or else we go low power which removes the consumption
  2860. // which makes us not low power so we add the consumption so we go low power...
  2861. // This check also guaards the IsDisabled in friend_adjustPower above
  2862. static NameKeyType powerPlant = NAMEKEY("PowerPlantUpgrade");
  2863. static NameKeyType overCharge = NAMEKEY("OverchargeBehavior");
  2864. Module* mod = findModule(powerPlant);
  2865. if (mod) {
  2866. PowerPlantUpgrade *powerPlantMod = (PowerPlantUpgrade*) mod;
  2867. if (powerPlantMod->isAlreadyUpgraded()) {
  2868. powerToAdjust += getTemplate()->getEnergyBonus();
  2869. }
  2870. }
  2871. mod = findModule(overCharge);
  2872. if (mod) {
  2873. OverchargeBehavior *overChargeMod = (OverchargeBehavior*) mod;
  2874. if (overChargeMod->isOverchargeActive()) {
  2875. powerToAdjust += getTemplate()->getEnergyBonus();
  2876. }
  2877. }
  2878. // Now, adjust the power for the player.
  2879. if (controller)
  2880. controller->getEnergy()->adjustPower(powerToAdjust, !becomingDisabled);
  2881. }
  2882. }
  2883. //-------------------------------------------------------------------------------------------------
  2884. /** Object CRC implemtation */
  2885. //-------------------------------------------------------------------------------------------------
  2886. void Object::crc( Xfer *xfer )
  2887. {
  2888. // This is evil - we cast the const Matrix3D * to a Matrix3D * because the XferCRC class must use
  2889. // the same interface as the XferLoad class for save game restore. This only works because
  2890. // XferCRC does not modify its data.
  2891. #ifdef DEBUG_CRC
  2892. // g_logObjectCRCs = TRUE;
  2893. // Bool g_logAllObjects = TRUE;
  2894. AsciiString logString;
  2895. AsciiString tmp;
  2896. Bool doLogging = g_logObjectCRCs /* && getControllingPlayer()->getPlayerType() == PLAYER_HUMAN */;
  2897. if (doLogging)
  2898. {
  2899. tmp.format("CRC of Object %d (%s), owned by player %d, ", m_id, getTemplate()->getName().str(), getControllingPlayer()->getPlayerIndex());
  2900. logString.concat(tmp);
  2901. }
  2902. #endif // DEBUG_CRC
  2903. xfer->xferUnsignedByte(&m_privateStatus);
  2904. #ifdef DEBUG_CRC
  2905. if (doLogging)
  2906. {
  2907. tmp.format("m_privateStatus: %X, ", (UnsignedInt)m_privateStatus);
  2908. logString.concat(tmp);
  2909. }
  2910. #endif // DEBUG_CRC
  2911. xfer->xferUser((Matrix3D *)getTransformMatrix(), sizeof(Matrix3D));
  2912. #ifdef DEBUG_CRC
  2913. if (doLogging)
  2914. {
  2915. XferCRC tmpXfer;
  2916. tmpXfer.open("tmp");
  2917. tmpXfer.xferUser((Matrix3D *)getTransformMatrix(), sizeof(Matrix3D));
  2918. tmp.format("getTransformMatrix(): %8.8X, ", tmpXfer.getCRC());
  2919. tmpXfer.close();
  2920. logString.concat(tmp);
  2921. }
  2922. #endif // DEBUG_CRC
  2923. #ifdef DEBUG_CRC
  2924. if (doLogging)
  2925. {
  2926. const Matrix3D *mtx = getTransformMatrix();
  2927. CRCDEBUG_LOG(("CRC of Object %d (%s), owned by player %d, ", m_id, getTemplate()->getName().str(), getControllingPlayer()->getPlayerIndex()));
  2928. DUMPMATRIX3D(mtx);
  2929. }
  2930. #endif //DEBUG_CRC
  2931. xfer->xferUser(&m_id, sizeof(m_id));
  2932. #ifdef DEBUG_CRC
  2933. if (doLogging)
  2934. {
  2935. tmp.format("m_id: %d, ", m_id);
  2936. logString.concat(tmp);
  2937. }
  2938. #endif // DEBUG_CRC
  2939. xfer->xferUser(&m_objectUpgradesCompleted, sizeof(Int64));
  2940. #ifdef DEBUG_CRC
  2941. if (doLogging)
  2942. {
  2943. tmp.format("m_objectUpgradesCompleted: %I64X, ", m_objectUpgradesCompleted);
  2944. logString.concat(tmp);
  2945. }
  2946. #endif // DEBUG_CRC
  2947. if (m_experienceTracker)
  2948. xfer->xferSnapshot( m_experienceTracker );
  2949. #ifdef DEBUG_CRC
  2950. if (doLogging)
  2951. {
  2952. XferCRC tmpXfer;
  2953. tmpXfer.open("tmp");
  2954. tmpXfer.xferSnapshot(m_experienceTracker);
  2955. tmp.format("m_experienceTracker: %8.8X, ", tmpXfer.getCRC());
  2956. tmpXfer.close();
  2957. logString.concat(tmp);
  2958. }
  2959. #endif // DEBUG_CRC
  2960. Real health = getBodyModule()->getHealth();
  2961. xfer->xferUser(&health, sizeof(health));
  2962. #ifdef DEBUG_CRC
  2963. if (doLogging)
  2964. {
  2965. tmp.format("health: %g/%8.8X, ", health, AS_INT(health));
  2966. logString.concat(tmp);
  2967. }
  2968. #endif // DEBUG_CRC
  2969. xfer->xferUnsignedInt(&m_weaponBonusCondition);
  2970. #ifdef DEBUG_CRC
  2971. if (doLogging)
  2972. {
  2973. tmp.format("m_weaponBonusCondition: %8.8X, ", m_weaponBonusCondition);
  2974. logString.concat(tmp);
  2975. }
  2976. #endif // DEBUG_CRC
  2977. Real scalar = getBodyModule()->getDamageScalar();
  2978. xfer->xferUser(&scalar, sizeof(scalar));
  2979. #ifdef DEBUG_CRC
  2980. if (doLogging)
  2981. {
  2982. tmp.format("damage scalar: %g/%8.8X\n", scalar, AS_INT(scalar));
  2983. logString.concat(tmp);
  2984. CRCDEBUG_LOG(("%s", logString.str()));
  2985. }
  2986. #endif // DEBUG_CRC
  2987. for (Int i=0; i<WEAPONSLOT_COUNT; ++i)
  2988. {
  2989. Weapon *thisWeapon = getWeaponInWeaponSlot((WeaponSlotType)i);
  2990. if (thisWeapon)
  2991. {
  2992. xfer->xferSnapshot( thisWeapon );
  2993. }
  2994. }
  2995. } // end crc
  2996. //-------------------------------------------------------------------------------------------------
  2997. /** Object xfer implemtation
  2998. * Version Info:
  2999. * 1: Initial version
  3000. * 2: Xfers m_singleUseCommandUsed... determines if the single use command button has been used or not.
  3001. * 3: Xfers the solehealingbenefactor ID and expiration frame
  3002. * 4: misc stuff that got missed somehow
  3003. * 5: m_isReceivingDifficultyBonus
  3004. * 6: We do indeed need to save m_containedBy. The comment misrepresents what the contain module will do.
  3005. * 7: save full mtx, not pos+orient.
  3006. */
  3007. //-------------------------------------------------------------------------------------------------
  3008. void Object::xfer( Xfer *xfer )
  3009. {
  3010. // version
  3011. const XferVersion currentVersion = 7;
  3012. XferVersion version = currentVersion;
  3013. xfer->xferVersion( &version, currentVersion );
  3014. // object ID
  3015. ObjectID id = getID();
  3016. xfer->xferObjectID( &id );
  3017. setID( id );
  3018. DEBUG_LOG(("Xfer Object %s id=%d\n",getTemplate()->getName().str(),id));
  3019. if (version >= 7)
  3020. {
  3021. Matrix3D mtx = *getTransformMatrix();
  3022. xfer->xferMatrix3D(&mtx);
  3023. setTransformMatrix(&mtx);
  3024. }
  3025. else
  3026. {
  3027. // object position
  3028. Coord3D pos = *getPosition();
  3029. xfer->xferCoord3D( &pos );
  3030. setPosition( &pos );
  3031. // orientation
  3032. Real orientation = getOrientation();
  3033. xfer->xferReal( &orientation );
  3034. setOrientation( orientation );
  3035. }
  3036. // team
  3037. TeamID teamID = m_team ? m_team->getID() : TEAM_ID_INVALID;
  3038. xfer->xferUser( &teamID, sizeof( TeamID ) );
  3039. // DON'T set the team yet; must wait till we read our status bits,
  3040. // since setTeam can affect the player's power usage, but that could
  3041. // be done incorrectly if our status bits aren't accurate yet... (srj)
  3042. // producer id
  3043. xfer->xferObjectID( &m_producerID );
  3044. // builder id
  3045. xfer->xferObjectID( &m_builderID );
  3046. // drawable id
  3047. Drawable *draw = getDrawable();
  3048. DrawableID drawableID = draw ? draw->getID() : INVALID_DRAWABLE_ID;
  3049. xfer->xferDrawableID( &drawableID );
  3050. if( xfer->getXferMode() == XFER_LOAD )
  3051. {
  3052. // change the ID of the drawable attached to be the same ID as it was when it was saved
  3053. draw->setID( drawableID );
  3054. } // end if
  3055. // internal name
  3056. xfer->xferAsciiString( &m_name );
  3057. // status
  3058. xfer->xferUnsignedInt( &m_status );
  3059. // script status
  3060. xfer->xferUnsignedByte( &m_scriptStatus );
  3061. // private status
  3062. xfer->xferUnsignedByte( &m_privateStatus );
  3063. // OK, now that we have xferred our status bits, it's safe to set the team...
  3064. if( xfer->getXferMode() == XFER_LOAD )
  3065. {
  3066. Team *team = TheTeamFactory->findTeamByID( teamID );
  3067. if( team == NULL )
  3068. {
  3069. DEBUG_CRASH(( "Object::xfer - Unable to load team\n" ));
  3070. throw SC_INVALID_DATA;
  3071. }
  3072. const Bool restoring = true;
  3073. setOrRestoreTeam( team, restoring );
  3074. }
  3075. // geometry info
  3076. xfer->xferSnapshot( &m_geometryInfo );
  3077. // sighting info, last look - must be saved cause we save PartitionCell::m_shroudLevel
  3078. xfer->xferSnapshot( m_partitionLastLook );
  3079. // sighting info, last shroud - must be saved cause we save PartitionCell::m_shroudLevel
  3080. xfer->xferSnapshot( m_partitionLastShroud );
  3081. // sighting info, last threat
  3082. // John M says we don't need to save this (CBD)
  3083. // xfer->xferSnapshot( &m_partitionLastThreat );
  3084. // sighting info, last value
  3085. // John M says we don't need to save this (CBD)
  3086. // xfer->xferSnapshot( &m_partitionLastValue );
  3087. // vision range
  3088. xfer->xferReal( &m_visionRange );
  3089. // shroud clearing range
  3090. xfer->xferReal( &m_shroudClearingRange );
  3091. // shroud range
  3092. xfer->xferReal( &m_shroudRange );
  3093. // disabled mask
  3094. m_disabledMask.xfer( xfer );
  3095. //New var added for version 2. Determines if the single use command button has been used or not.
  3096. if( xfer->getXferMode() == XFER_SAVE || version >= 2 )
  3097. {
  3098. xfer->xferBool( &m_singleUseCommandUsed );
  3099. }
  3100. else
  3101. {
  3102. m_singleUseCommandUsed = false;
  3103. }
  3104. // disabled till frame
  3105. xfer->xferUser( m_disabledTillFrame, sizeof( UnsignedInt ) * DISABLED_COUNT );
  3106. // special model condition until
  3107. xfer->xferUnsignedInt( &m_smcUntil );
  3108. //
  3109. // radar data ... when loading, we will remove all objects from the radar and let
  3110. // the radar system load itself as a separate chunk of data from the save file
  3111. //
  3112. if( xfer->getXferMode() == XFER_LOAD && m_radarData )
  3113. TheRadar->removeObject( this );
  3114. // experience tracker
  3115. xfer->xferSnapshot( m_experienceTracker );
  3116. //
  3117. // we do not need to do anything with our m_containedBy pointer, the post process
  3118. // of that objects contain module will actually re-do the contain process again
  3119. //
  3120. // m_containedBy <-- do nothing with this right now
  3121. if( version >= 6 )
  3122. {
  3123. // No, the contain module is just going to friend_ reach in and set this for us.
  3124. // Containers more complicated than Open (like Tunnel) can't do that. Our variable,
  3125. // our responsibility.
  3126. if( xfer->getXferMode() == XFER_SAVE )
  3127. {
  3128. if( m_containedBy != NULL )
  3129. m_xferContainedByID = m_containedBy->getID();
  3130. else
  3131. m_xferContainedByID = INVALID_ID;
  3132. }
  3133. xfer->xferObjectID( &m_xferContainedByID );
  3134. }
  3135. // contained by frame
  3136. xfer->xferUnsignedInt( &m_containedByFrame );
  3137. // construction percent
  3138. xfer->xferReal( &m_constructionPercent );
  3139. // upgrades completed
  3140. xfer->xferUpgradeMask( &m_objectUpgradesCompleted );
  3141. // original team name
  3142. xfer->xferAsciiString( &m_originalTeamName );
  3143. // indicator color
  3144. xfer->xferColor( &m_indicatorColor );
  3145. // health box offset
  3146. xfer->xferCoord3D( &m_healthBoxOffset );
  3147. // Entered & exited housekeeping.
  3148. Int i;
  3149. xfer->xferByte(&m_numTriggerAreasActive);
  3150. xfer->xferUnsignedInt(&m_enteredOrExitedFrame);
  3151. xfer->xferICoord3D(&m_iPos);
  3152. if (m_numTriggerAreasActive<0 || m_numTriggerAreasActive>MAX_TRIGGER_AREA_INFOS) {
  3153. DEBUG_CRASH(("Invalid m_numTriggerAreasActive = %d, max is %d", m_numTriggerAreasActive,
  3154. MAX_TRIGGER_AREA_INFOS));
  3155. throw SC_INVALID_DATA;
  3156. }
  3157. for (i=0; i<m_numTriggerAreasActive; i++) {
  3158. AsciiString triggerName;
  3159. if (m_triggerInfo[i].pTrigger) {
  3160. triggerName = m_triggerInfo[i].pTrigger->getTriggerName();
  3161. }
  3162. xfer->xferAsciiString(&triggerName);
  3163. if (xfer->getXferMode() == XFER_LOAD)
  3164. {
  3165. //
  3166. // CBD (11-13-2002) I'm disabling this because it appears there might be some areas with
  3167. // empty names, see John A. for more info
  3168. //
  3169. //if (triggerName.isNotEmpty())
  3170. m_triggerInfo[i].pTrigger = TheTerrainLogic->getTriggerAreaByName(triggerName);
  3171. }
  3172. xfer->xferByte(&m_triggerInfo[i].entered);
  3173. xfer->xferByte(&m_triggerInfo[i].exited);
  3174. xfer->xferByte(&m_triggerInfo[i].isInside);
  3175. }
  3176. // Layer object is pathing on.
  3177. xfer->xferUser(&m_layer, sizeof(m_layer));
  3178. // Layer of current path goal.
  3179. xfer->xferUser(&m_destinationLayer, sizeof(m_destinationLayer));
  3180. // Object selectability.
  3181. xfer->xferBool(&m_isSelectable);
  3182. xfer->xferUnsignedInt(&m_safeOcclusionFrame);
  3183. // User formations.
  3184. xfer->xferUser(&m_formationID, sizeof(m_formationID));
  3185. if (m_formationID!=NO_FORMATION_ID) {
  3186. xfer->xferCoord2D(&m_formationOffset);
  3187. }
  3188. // module count
  3189. UnsignedShort moduleCount = 0;
  3190. for (BehaviorModule** b = m_behaviors; *b; ++b)
  3191. ++moduleCount;
  3192. xfer->xferUnsignedShort( &moduleCount );
  3193. AsciiString moduleIdentifier;
  3194. BehaviorModule *module;
  3195. if( xfer->getXferMode() == XFER_SAVE )
  3196. {
  3197. // go through all modules
  3198. for (BehaviorModule** b = m_behaviors; *b; ++b)
  3199. {
  3200. // get module
  3201. module = *b;
  3202. // write module identifier
  3203. moduleIdentifier = TheNameKeyGenerator->keyToName( module->getModuleTagNameKey() );
  3204. DEBUG_ASSERTCRASH( moduleIdentifier != AsciiString::TheEmptyString,
  3205. ("Object::xfer - Module tag key does not translate to a string!\n") );
  3206. xfer->xferAsciiString( &moduleIdentifier );
  3207. // begin a data block
  3208. xfer->beginBlock();
  3209. // xfer data
  3210. xfer->xferSnapshot( module );
  3211. // end data block
  3212. xfer->endBlock();
  3213. } // end for, it
  3214. } // end if, save
  3215. else
  3216. {
  3217. AsciiString otherModuleIdentifier;
  3218. // read all module data
  3219. for( UnsignedShort i = 0; i < moduleCount; ++i )
  3220. {
  3221. // read module name
  3222. xfer->xferAsciiString( &moduleIdentifier );
  3223. NameKeyType moduleIdentifierKey = TheNameKeyGenerator->nameToKey(moduleIdentifier);
  3224. // find the module with this identifier in the module list
  3225. module = NULL;
  3226. for (BehaviorModule** b = m_behaviors; b && *b; ++b)
  3227. {
  3228. if (moduleIdentifierKey == (*b)->getModuleTagNameKey())
  3229. {
  3230. module = *b;
  3231. break;
  3232. }
  3233. } // end for, moduleIt
  3234. // start of a new block
  3235. Int dataSize = xfer->beginBlock();
  3236. //
  3237. // if we didn't find the module, it's quite possible that we have removed
  3238. // it from the object definition in a future patch, if that is so, we need to
  3239. // skip the module data in the file
  3240. //
  3241. if( module == NULL )
  3242. {
  3243. // for testing purposes, this module better be found
  3244. DEBUG_CRASH(( "Object::xfer - Module '%s' was indicated in file, but not found on object '%s'(%d)\n",
  3245. moduleIdentifier.str(), getTemplate()->getName().str(), getID() ));
  3246. // skip this data in the file
  3247. xfer->skip( dataSize );
  3248. } // end if
  3249. else
  3250. {
  3251. // xfer the data into this module
  3252. xfer->xferSnapshot( module );
  3253. } // end else
  3254. // end block
  3255. xfer->endBlock();
  3256. } // end for, i module count recorded in file
  3257. } // end else, load
  3258. if ( version >= 3 )
  3259. {
  3260. xfer->xferObjectID( &m_soleHealingBenefactorID );
  3261. xfer->xferUnsignedInt( &m_soleHealingBenefactorExpirationFrame );
  3262. }
  3263. else if ( xfer->getXferMode() == XFER_LOAD )
  3264. {
  3265. m_soleHealingBenefactorID = INVALID_ID;
  3266. m_soleHealingBenefactorExpirationFrame = 0;
  3267. }
  3268. // Doesn't need to be saved. These are created as needed. jba.
  3269. //AIGroup* m_group; ///< if non-NULL, we are part of this group of agents
  3270. // don't need to save m_partitionData.
  3271. DEBUG_ASSERTCRASH(!(xfer->getXferMode() == XFER_LOAD && m_partitionData == NULL), ("should not be in partitionmgr yet"));
  3272. // don't need to be saved or loaded; are inited & cached for runtime only by our ctor (srj)
  3273. //m_repulsorHelper;
  3274. //m_smcHelper;
  3275. //m_wsHelper;
  3276. //m_defectionHelper;
  3277. //m_firingTracker;
  3278. //m_contain;
  3279. //m_body;
  3280. //m_ai;
  3281. //m_physics;
  3282. #if defined(_DEBUG) || defined(_INTERNAL)
  3283. //m_hasDiedAlready;
  3284. #endif
  3285. if (version >= 4)
  3286. {
  3287. // xfer the weaponSetFlags FIRST, since we need 'em to restore the weaponSet properly. (srj)
  3288. m_curWeaponSetFlags.xfer( xfer );
  3289. xfer->xferUnsignedInt(&m_weaponBonusCondition);
  3290. xfer->xferUser(&m_lastWeaponCondition, sizeof(m_lastWeaponCondition));
  3291. // do the weaponSet itself after all the weapon-related stuff, just in case
  3292. xfer->xferSnapshot(&m_weaponSet);
  3293. m_specialPowerBits.xfer( xfer );
  3294. xfer->xferAsciiString(&m_commandSetStringOverride);
  3295. xfer->xferBool(&m_modulesReady);
  3296. }
  3297. if (version >= 5)
  3298. {
  3299. xfer->xferBool(&m_isReceivingDifficultyBonus);
  3300. }
  3301. else
  3302. m_isReceivingDifficultyBonus = FALSE;
  3303. } // end xfer
  3304. //-------------------------------------------------------------------------------------------------
  3305. /** Object load game post process phase */
  3306. //-------------------------------------------------------------------------------------------------
  3307. void Object::loadPostProcess()
  3308. {
  3309. if( m_xferContainedByID != INVALID_ID )
  3310. m_containedBy = TheGameLogic->findObjectByID(m_xferContainedByID);
  3311. else
  3312. m_containedBy = NULL;
  3313. } // end loadPostProcess
  3314. //-------------------------------------------------------------------------------------------------
  3315. /** Does this object have this upgrade */
  3316. //-------------------------------------------------------------------------------------------------
  3317. Bool Object::hasUpgrade( const UpgradeTemplate *upgradeT ) const
  3318. {
  3319. return BitTest( m_objectUpgradesCompleted, upgradeT->getUpgradeMask() );
  3320. } // end hasUpgrade
  3321. //-------------------------------------------------------------------------------------------------
  3322. /** Is this object capable of having this upgrade */
  3323. //-------------------------------------------------------------------------------------------------
  3324. Bool Object::affectedByUpgrade( const UpgradeTemplate *upgradeT ) const
  3325. {
  3326. Int64 objectMask = getObjectCompletedUpgradeMask();
  3327. Int64 playerMask = getControllingPlayer()->getCompletedUpgradeMask();
  3328. Int64 maskToCheck = playerMask | objectMask | upgradeT->getUpgradeMask();
  3329. // We need to add in all of the already owned upgrades to handle "AND" requiring upgrades.
  3330. // We combine all the masks in case someone has a Object AND Player combination
  3331. for (BehaviorModule** module = m_behaviors; *module; ++module)
  3332. {
  3333. UpgradeModuleInterface* upgrade = (*module)->getUpgrade();
  3334. if (!upgrade)
  3335. continue;
  3336. if( upgrade->wouldUpgrade( maskToCheck ) )
  3337. {
  3338. // if any of my many upgrade modules would execute in response to this flag, say yes.
  3339. return TRUE;
  3340. }
  3341. }
  3342. return FALSE;
  3343. } // end affectedByUpgrade
  3344. //-------------------------------------------------------------------------------------------------
  3345. /** Give this upgrade to this object */
  3346. //-------------------------------------------------------------------------------------------------
  3347. void Object::giveUpgrade( const UpgradeTemplate *upgradeT )
  3348. {
  3349. if (upgradeT)
  3350. {
  3351. BitSet( m_objectUpgradesCompleted, upgradeT->getUpgradeMask() );
  3352. //
  3353. // iterate through all the upgrade modules of this object and call the method to
  3354. // grant a new upgrade
  3355. //
  3356. updateUpgradeModules();
  3357. }
  3358. } // end giveUpgrade
  3359. //-------------------------------------------------------------------------------------------------
  3360. /** Remove this upgrade from this object */
  3361. //-------------------------------------------------------------------------------------------------
  3362. void Object::removeUpgrade( const UpgradeTemplate *upgradeT )
  3363. {
  3364. BitClear( m_objectUpgradesCompleted, upgradeT->getUpgradeMask() );
  3365. for (BehaviorModule** module = m_behaviors; *module; ++module)
  3366. {
  3367. UpgradeModuleInterface* upgrade = (*module)->getUpgrade();
  3368. if (!upgrade)
  3369. continue;
  3370. upgrade->resetUpgrade( upgradeT->getUpgradeMask() );
  3371. }
  3372. }
  3373. //-------------------------------------------------------------------------------------------------
  3374. /** Central point for onCapture logic */
  3375. //-------------------------------------------------------------------------------------------------
  3376. void Object::onCapture( Player *oldOwner, Player *newOwner )
  3377. {
  3378. // Everybody dhills when they captured so they don't keep doing something the new player might not want him to be doing
  3379. if( getAIUpdateInterface() && (oldOwner != newOwner) )
  3380. getAIUpdateInterface()->aiIdle(CMD_FROM_AI);
  3381. // this gets the new owner some points
  3382. newOwner->getScoreKeeper()->addObjectCaptured(this);
  3383. // rip through the behavior modules and call the onCapture for any modules that care
  3384. for( BehaviorModule **module = m_behaviors; *module; ++module )
  3385. (*module)->onCapture( oldOwner, newOwner );
  3386. //
  3387. // We have to undo our look for the old team and redo it for the new.
  3388. // onCapture is used now, so it better be called after ownership changes and not before.
  3389. //
  3390. handlePartitionCellMaintenance();
  3391. // Design needs the player to be able to sell buildings he steals from the AI's build list, and this is the
  3392. // easiest fix. The only snafu would be a key building build listed by the AI that the player can capture
  3393. // and the AI tries to capture back but needs to not sell. In that case, a Cinematic Unsellable version
  3394. // of the building needs to be made. This fix has been okayed as the most non-lethal in November.
  3395. clearScriptStatus(OBJECT_STATUS_SCRIPT_UNSELLABLE);
  3396. // mark the command bar to redraw
  3397. TheControlBar->markUIDirty();
  3398. if (oldOwner!=newOwner && newOwner->isSkirmishAIPlayer()) {
  3399. // The skirmish ai doesn't know what to do with captured faction buildings except sell them.
  3400. if (isFactionStructure()) {
  3401. TheBuildAssistant->sellObject( this );
  3402. }
  3403. }
  3404. } // end onCapture
  3405. //-------------------------------------------------------------------------------------------------
  3406. /// Object level events that need to happen upon game death
  3407. void Object::onDie( DamageInfo *damageInfo )
  3408. {
  3409. #if defined(_DEBUG) || defined(_INTERNAL)
  3410. DEBUG_ASSERTCRASH(m_hasDiedAlready == false, ("Object::onDie has been called multiple times. This is invalid. jkmcd"));
  3411. m_hasDiedAlready = true;
  3412. #endif
  3413. Bool selfInflicted = (damageInfo->in.m_sourceID == getID());
  3414. // FIRST, call our die modules.
  3415. for (BehaviorModule** d = m_behaviors; *d; ++d)
  3416. {
  3417. DieModuleInterface* die = (*d)->getDie();
  3418. if (die)
  3419. die->onDie(damageInfo);
  3420. }
  3421. // When objects die we remove from the radar as they're really not interesting anymore
  3422. if( m_radarData )
  3423. TheRadar->removeObject( this );
  3424. // Just in case I have been sporting one of thise fancy Terrain Decals,
  3425. //I naturally lose it now, because I'm dead.
  3426. Drawable *draw = getDrawable();
  3427. if (draw) draw->setTerrainDecalFadeTarget(0.0f, -0.03f);//fade...
  3428. //if (draw) draw->setTerrainDecal(TERRAIN_DECAL_NONE);//pop!
  3429. // objects that were spawned from something, need to tell their spawner that they have died
  3430. Object* spawner = TheGameLogic->findObjectByID( getProducerID() );
  3431. if( spawner )
  3432. {
  3433. // get the spawn behavior interface of the spawner
  3434. SpawnBehaviorInterface *spawnerBehavior = spawner->getSpawnBehaviorInterface();
  3435. if( spawnerBehavior )
  3436. spawnerBehavior->onSpawnDeath( getID(), damageInfo );
  3437. }
  3438. handlePartitionCellMaintenance();
  3439. if(m_team)
  3440. m_team->notifyTeamOfObjectDeath();
  3441. // Play death sound here.
  3442. AudioEventRTS deathSound = *getTemplate()->getSoundDie();
  3443. // If we were killed by fire, or by poison, we should play those die sounds instead of the usual
  3444. // sound
  3445. if (damageInfo->in.m_deathType == DEATH_BURNED)
  3446. deathSound = *getTemplate()->getSoundDieFire();
  3447. else if (damageInfo->in.m_deathType == DEATH_POISONED || damageInfo->in.m_deathType == DEATH_POISONED_BETA)
  3448. deathSound = *getTemplate()->getSoundDieToxin();
  3449. // If we didn't actually have a specialized die sound (for the case of fire or poison, we
  3450. // should use the generic death sound)
  3451. if (!TheAudio->isValidAudioEvent(&deathSound))
  3452. deathSound = *getTemplate()->getSoundDie();
  3453. // Use the position. Next frame, when this unit is gone, this sound will be clipped because we
  3454. // can no longer automatically find its position. - jkmcd
  3455. deathSound.setPosition(getPosition());
  3456. PlayerIndex index = getControllingPlayer() ? getControllingPlayer()->getPlayerIndex() : 0;
  3457. deathSound.setPlayerIndex( index );
  3458. TheAudio->addAudioEvent(&deathSound);
  3459. if (isLocallyControlled() && !selfInflicted) // wasLocallyControlled? :-)
  3460. {
  3461. if (isKindOf(KINDOF_STRUCTURE) && isKindOf(KINDOF_MP_COUNT_FOR_VICTORY))
  3462. {
  3463. TheEva->setShouldPlay(EVA_BuldingLost);
  3464. }
  3465. else if (isKindOf(KINDOF_INFANTRY) || isKindOf(KINDOF_VEHICLE))
  3466. {
  3467. TheEva->setShouldPlay(EVA_UnitLost);
  3468. //Create a fake radar event so the user can use the spacebar to quickly jump to this!
  3469. TheRadar->tryEvent( RADAR_EVENT_FAKE, getPosition() );
  3470. }
  3471. }
  3472. // This call won't do anything if we aren't actually in the list.
  3473. TheInGameUI->removeIdleWorker(this, this->getControllingPlayer()->getPlayerIndex());
  3474. }
  3475. //-------------------------------------------------------------------------------------------------
  3476. /**
  3477. A weapon cannot be in charge of maintaining condition flags as it is all event driven.
  3478. I will maintain my ModelCondition myself if it should change. Firing is set by firing logic,
  3479. so I don't include it here. It is only the states that expire on timers that noone watches
  3480. that I am concerned with.
  3481. */
  3482. //-------------------------------------------------------------------------------------------------
  3483. void Object::adjustModelConditionForWeaponStatus()
  3484. {
  3485. UnsignedInt now = TheGameLogic->getFrame();
  3486. for (int i = 0; i < WEAPONSLOT_COUNT; ++i)
  3487. {
  3488. const Weapon* w = m_weaponSet.getWeaponInWeaponSlot((WeaponSlotType)i);
  3489. if (!w)
  3490. {
  3491. m_lastWeaponCondition[i] = WSF_NONE;
  3492. continue;
  3493. }
  3494. WeaponSetConditionType conditionToSet = WSF_INVALID;
  3495. if (i != m_weaponSet.getCurWeaponSlot())
  3496. {
  3497. // if this isn't the current weapon, then we never set ANYTHING for it.
  3498. conditionToSet = WSF_NONE;
  3499. }
  3500. else if (w->getLastShotFrame() == now)
  3501. {
  3502. // yep, this overrides any weapon-status condition!
  3503. conditionToSet = WSF_FIRING;
  3504. }
  3505. else if (!testStatus( OBJECT_STATUS_IS_ATTACKING ))
  3506. {
  3507. // srj sez: not 100% sure about this one, but the problem is: say we were attacking,
  3508. // then issue a move command. if we didn't do this here, we might still have a 'firing'
  3509. // pose, because his weapon might be in 'reloading' mode. since we're not attacking, however,
  3510. // we really don't care, so we just force the issue here. (This might still need tweaking for the pursue state.)
  3511. conditionToSet = WSF_NONE;
  3512. }
  3513. else
  3514. {
  3515. WeaponStatus newStatus = w->getStatus();
  3516. const static WeaponSetConditionType s_wsfLookup[WEAPON_STATUS_COUNT] =
  3517. {
  3518. WSF_NONE, // READY_TO_FIRE,
  3519. WSF_NONE, // OUT_OF_AMMO,
  3520. WSF_BETWEEN, // BETWEEN_FIRING_SHOTS,
  3521. WSF_RELOADING, // RELOADING_CLIP,
  3522. WSF_PREATTACK // PRE_ATTACK,
  3523. };
  3524. conditionToSet = s_wsfLookup[newStatus];
  3525. // special case this: say we are firing in bursts: pow-pow-pow-pause, etc.
  3526. // then we might have a frame where we have reloaded and are ready-to-fire,
  3527. // but haven't fired yet this frame. in that case, use 'between' so we still have
  3528. // a firing pose, 'cuz if we use 'none' we will 'pop' back to idle for a frame. (srj)
  3529. // additional note: only do if aiming or firing, since we could also be in this state if
  3530. // we are approaching or pursuing a target! (srj)
  3531. if (newStatus == READY_TO_FIRE && conditionToSet == WSF_NONE && testStatus( OBJECT_STATUS_IS_ATTACKING ) &&
  3532. (testStatus( OBJECT_STATUS_IS_AIMING_WEAPON ) || testStatus( OBJECT_STATUS_IS_FIRING_WEAPON )))
  3533. {
  3534. conditionToSet = WSF_BETWEEN;
  3535. }
  3536. }
  3537. if (m_drawable)
  3538. {
  3539. m_drawable->updateDrawableClipStatus( w->getRemainingAmmo(), w->getClipSize(), w->getWeaponSlot() );
  3540. if (conditionToSet != WSF_INVALID && conditionToSet != m_lastWeaponCondition[i])
  3541. {
  3542. m_lastWeaponCondition[i] = conditionToSet;
  3543. ModelConditionFlags c = m_weaponSet.getModelConditionForWeaponSlot((WeaponSlotType)i, conditionToSet);
  3544. m_drawable->clearAndSetModelConditionFlags(s_allWeaponFireFlags[i], c);
  3545. if (conditionToSet == WSF_PREATTACK)
  3546. {
  3547. // in the preattack state, adjust the speed of the preattack anim to match the actual time it will take
  3548. UnsignedInt preAttackDone = w->getPreAttackFinishedFrame();
  3549. if (preAttackDone > now)
  3550. m_drawable->setAnimationLoopDuration(preAttackDone - now);
  3551. }
  3552. }
  3553. }
  3554. }
  3555. }
  3556. //-------------------------------------------------------------------------------------------------
  3557. /// We have moved a 'significant' amount, so do maintenence that can be considered 'cell-based'
  3558. void Object::onPartitionCellChange()
  3559. {
  3560. handlePartitionCellMaintenance();
  3561. }
  3562. //-------------------------------------------------------------------------------------------------
  3563. void Object::handlePartitionCellMaintenance()
  3564. {
  3565. handleShroud();
  3566. handleValueMap();
  3567. handleThreatMap();
  3568. }
  3569. //-------------------------------------------------------------------------------------------------
  3570. void Object::handleShroud()
  3571. {
  3572. // Undo last looking
  3573. unlook();
  3574. // and shrouding
  3575. unshroud();
  3576. // redo shrouding
  3577. shroud();
  3578. // Redo looking
  3579. look();
  3580. }
  3581. //-------------------------------------------------------------------------------------------------
  3582. void Object::handleValueMap()
  3583. {
  3584. removeValue();
  3585. addValue();
  3586. }
  3587. //-------------------------------------------------------------------------------------------------
  3588. void Object::handleThreatMap()
  3589. {
  3590. removeThreat();
  3591. addThreat();
  3592. }
  3593. //-------------------------------------------------------------------------------------------------
  3594. void Object::addValue()
  3595. {
  3596. if( !m_partitionLastValue->isInvalid() )
  3597. {
  3598. DEBUG_CRASH( ("An Object is adding value, but hasn't removed his previous value.") );
  3599. return;
  3600. }
  3601. if (!getControllingPlayer())
  3602. return;
  3603. if( ((getStatusBits() & OBJECT_STATUS_UNDER_CONSTRUCTION) != 0)
  3604. || ( isEffectivelyDead() )
  3605. || ( getShroudClearingRange() <= 0.0f ))
  3606. return;
  3607. m_partitionLastValue->m_where = *getPosition();
  3608. m_partitionLastValue->m_data = getTemplate()->friend_getBuildCost();
  3609. m_partitionLastValue->m_forWhom = getControllingPlayer()->getPlayerMask();
  3610. m_partitionLastValue->m_howFar = getVisionRange(); // we are valuable all the way to where we can target.
  3611. ThePartitionManager->doValueAffect(m_partitionLastValue->m_where.x,
  3612. m_partitionLastValue->m_where.y,
  3613. m_partitionLastValue->m_howFar,
  3614. m_partitionLastValue->m_data,
  3615. m_partitionLastValue->m_forWhom
  3616. );
  3617. }
  3618. //-------------------------------------------------------------------------------------------------
  3619. void Object::removeValue()
  3620. {
  3621. if( m_partitionLastValue->isInvalid() )
  3622. {
  3623. // removing before adding is valid, cause we always remove before adding. (So the first remove
  3624. // will occur before the first add)
  3625. return;
  3626. }
  3627. ThePartitionManager->undoValueAffect(m_partitionLastValue->m_where.x,
  3628. m_partitionLastValue->m_where.y,
  3629. m_partitionLastValue->m_howFar,
  3630. m_partitionLastValue->m_data,
  3631. m_partitionLastValue->m_forWhom
  3632. );
  3633. m_partitionLastValue->reset();
  3634. }
  3635. //-------------------------------------------------------------------------------------------------
  3636. void Object::addThreat()
  3637. {
  3638. if( !m_partitionLastThreat->isInvalid() )
  3639. {
  3640. DEBUG_CRASH( ("An Object is adding threat, but hasn't removed his previous threat. (He hasn't finished the threat?)") );
  3641. return;
  3642. }
  3643. if (!getControllingPlayer())
  3644. return;
  3645. if( ((getStatusBits() & OBJECT_STATUS_UNDER_CONSTRUCTION) != 0)
  3646. || ( isEffectivelyDead() )
  3647. || ( getShroudClearingRange() <= 0.0f ))
  3648. return;
  3649. m_partitionLastThreat->m_where = *getPosition();
  3650. m_partitionLastThreat->m_data = getTemplate()->getThreatValue();
  3651. m_partitionLastThreat->m_forWhom = getControllingPlayer()->getPlayerMask();
  3652. m_partitionLastThreat->m_howFar = getVisionRange(); // we are threatening all the way to where we can target.
  3653. ThePartitionManager->doThreatAffect(m_partitionLastThreat->m_where.x,
  3654. m_partitionLastThreat->m_where.y,
  3655. m_partitionLastThreat->m_howFar,
  3656. m_partitionLastThreat->m_data,
  3657. m_partitionLastThreat->m_forWhom
  3658. );
  3659. }
  3660. //-------------------------------------------------------------------------------------------------
  3661. void Object::removeThreat()
  3662. {
  3663. if( m_partitionLastThreat->isInvalid() )
  3664. {
  3665. // removing before adding is valid, cause we always remove before adding. (So the first remove
  3666. // will occur before the first add)
  3667. return;
  3668. }
  3669. ThePartitionManager->undoThreatAffect(m_partitionLastThreat->m_where.x,
  3670. m_partitionLastThreat->m_where.y,
  3671. m_partitionLastThreat->m_howFar,
  3672. m_partitionLastThreat->m_data,
  3673. m_partitionLastThreat->m_forWhom
  3674. );
  3675. m_partitionLastThreat->reset();
  3676. }
  3677. //-------------------------------------------------------------------------------------------------
  3678. void Object::look()
  3679. {
  3680. if( ! m_partitionLastLook->isInvalid() )
  3681. {
  3682. DEBUG_CRASH( ("An Object is looking, but hasn't unlooked the last one.") );
  3683. return;
  3684. }
  3685. Player* controller = getControllingPlayer();
  3686. if ( controller )
  3687. {
  3688. // I removed the check for objects under construction by request of designers since
  3689. // they want constructing objects to have a reduced sight range now. -MW
  3690. // dead or blind things don't reveal shroud
  3691. if( ( ! isDestroyed() )// Some things get Destroyed directly without hitting Death.
  3692. && ( ! isEffectivelyDead() )
  3693. && ( getShroudClearingRange() > 0.0f )
  3694. )
  3695. {
  3696. PlayerMaskType lookingMask = 0;
  3697. for( Int currentIndex = ThePlayerList->getPlayerCount() - 1; currentIndex >=0; currentIndex-- )
  3698. {
  3699. const Player *currentPlayer = ThePlayerList->getNthPlayer( currentIndex );
  3700. // Build mask of of allies who can see me.
  3701. // This is the Object-centric game level that cares
  3702. if( getControllingPlayer()->getRelationship( currentPlayer->getDefaultTeam() ) == ALLIES )
  3703. {
  3704. lookingMask |= currentPlayer->getPlayerMask();
  3705. }
  3706. }
  3707. // Other players can also be looking through our eyes.
  3708. lookingMask |= controller->getVisionSpiedMask();
  3709. if ( isKindOf(KINDOF_REVEAL_TO_ALL) )
  3710. lookingMask = PLAYERMASK_ALL;
  3711. Coord3D pos = *getPosition();
  3712. ThePartitionManager->doShroudReveal(pos.x,
  3713. pos.y,
  3714. getShroudClearingRange(),
  3715. lookingMask
  3716. );
  3717. m_partitionLastLook->m_where = pos;
  3718. m_partitionLastLook->m_forWhom = lookingMask;
  3719. m_partitionLastLook->m_howFar = getShroudClearingRange();
  3720. // DEBUG_LOG(( "A %s looks at %f, %f for %x at range %f\n",
  3721. // getTemplate()->getName().str(),
  3722. // pos.x,
  3723. // pos.y,
  3724. // lookingMask,
  3725. // getShroudClearingRange()
  3726. // ));
  3727. }
  3728. }
  3729. }
  3730. //-------------------------------------------------------------------------------------------------
  3731. void Object::unlook()
  3732. {
  3733. if( m_partitionLastLook->isInvalid() )
  3734. {
  3735. // Your very first action will be an unlook, so of course you haven't looked yet. This is not an error
  3736. // This early return prevents an extra unlook if you never looked. Like you have 0 vision.
  3737. return;
  3738. }
  3739. ThePartitionManager->queueUndoShroudReveal(m_partitionLastLook->m_where.x,
  3740. m_partitionLastLook->m_where.y,
  3741. m_partitionLastLook->m_howFar,
  3742. m_partitionLastLook->m_forWhom
  3743. );
  3744. // DEBUG_LOG(( "A %s queues an unlook at %f, %f for %x at range %f\n",
  3745. // getTemplate()->getName().str(),
  3746. // m_partitionLastLook.m_where.x,
  3747. // m_partitionLastLook.m_where.y,
  3748. // m_partitionLastLook.m_forWhom,
  3749. // m_partitionLastLook.m_howFar
  3750. // ));
  3751. m_partitionLastLook->reset();
  3752. }
  3753. //-------------------------------------------------------------------------------------------------
  3754. void Object::shroud()
  3755. {
  3756. if( ! m_partitionLastShroud->isInvalid() )
  3757. {
  3758. DEBUG_CRASH( ("An Object is shrouding, but hasn't unshrouded the last one.") );
  3759. return;
  3760. }
  3761. Player* controller = getControllingPlayer();
  3762. if ( controller )
  3763. {
  3764. // things under construction don't shroud. (srj), nor do dead or blind things
  3765. if( ((getStatusBits() & OBJECT_STATUS_UNDER_CONSTRUCTION) == 0)
  3766. && ( ! isEffectivelyDead() )
  3767. && ( getShroudRange() > 0.0f )
  3768. )
  3769. {
  3770. PlayerMaskType shroudingMask = 0;
  3771. for( Int currentIndex = ThePlayerList->getPlayerCount() - 1; currentIndex >=0; currentIndex-- )
  3772. {
  3773. const Player *currentPlayer = ThePlayerList->getNthPlayer( currentIndex );
  3774. //Build mask of NON-allies. This is the Object-centric game level that cares
  3775. if( getControllingPlayer()->getRelationship( currentPlayer->getDefaultTeam() ) != ALLIES )
  3776. {
  3777. shroudingMask |= currentPlayer->getPlayerMask();
  3778. }
  3779. }
  3780. Coord3D pos = *getPosition();
  3781. ThePartitionManager->doShroudCover(pos.x, pos.y,
  3782. getShroudRange(),
  3783. shroudingMask);
  3784. m_partitionLastShroud->m_where = pos;
  3785. m_partitionLastShroud->m_forWhom = shroudingMask;
  3786. m_partitionLastShroud->m_howFar = getShroudRange();
  3787. }
  3788. }
  3789. }
  3790. //-------------------------------------------------------------------------------------------------
  3791. void Object::unshroud()
  3792. {
  3793. if( m_partitionLastShroud->isInvalid() )
  3794. {
  3795. // Your very first action will be an unlook, so of course you haven't looked yet. This is not an error
  3796. // This early return prevents an extra unlook if you never looked. Like you have 0 shroud generation.
  3797. return;
  3798. }
  3799. ThePartitionManager->undoShroudCover(m_partitionLastShroud->m_where.x,
  3800. m_partitionLastShroud->m_where.y,
  3801. m_partitionLastShroud->m_howFar,
  3802. m_partitionLastShroud->m_forWhom);
  3803. m_partitionLastShroud->reset();
  3804. }
  3805. //-------------------------------------------------------------------------------------------------
  3806. Real Object::getVisionRange() const
  3807. {
  3808. #if defined(_DEBUG) || defined(_INTERNAL)
  3809. if (TheGlobalData->m_debugVisibility)
  3810. {
  3811. Vector3 pos(m_visionRange, 0, 0);
  3812. for (int i = 0; i < TheGlobalData->m_debugVisibilityTileCount; ++i)
  3813. {
  3814. pos.Rotate_Z(1.0f * i / TheGlobalData->m_debugVisibilityTileCount * 2 * PI);
  3815. Coord3D coord = { pos.X + getPosition()->x, pos.Y + getPosition()->y, pos.Z + getPosition()->z };
  3816. addIcon(&coord, TheGlobalData->m_debugVisibilityTileWidth,
  3817. TheGlobalData->m_debugVisibilityTileDuration,
  3818. TheGlobalData->m_debugVisibilityTargettableColor);
  3819. }
  3820. }
  3821. #endif
  3822. return m_visionRange;
  3823. }
  3824. //-------------------------------------------------------------------------------------------------
  3825. void Object::setVisionRange( Real newVisionRange )
  3826. {
  3827. m_visionRange = newVisionRange;
  3828. }
  3829. //-------------------------------------------------------------------------------------------------
  3830. Real Object::getShroudClearingRange() const
  3831. {
  3832. Real shroudClearingRange=m_shroudClearingRange;
  3833. if ((getStatusBits() & OBJECT_STATUS_UNDER_CONSTRUCTION))
  3834. { //structures under construction have limited vision range. For now, base it
  3835. //on the geometry extents so the structure can only see itself.
  3836. shroudClearingRange = getGeometryInfo().getBoundingCircleRadius();
  3837. }
  3838. #if defined(_DEBUG) || defined(_INTERNAL)
  3839. if (TheGlobalData->m_debugVisibility)
  3840. {
  3841. Vector3 pos(shroudClearingRange, 0, 0);
  3842. for (int i = 0; i < TheGlobalData->m_debugVisibilityTileCount; ++i)
  3843. {
  3844. pos.Rotate_Z(1.0f * i / TheGlobalData->m_debugVisibilityTileCount * 2 * PI);
  3845. Coord3D coord = { pos.X + getPosition()->x, pos.Y + getPosition()->y, pos.Z + getPosition()->z };
  3846. addIcon(&coord, TheGlobalData->m_debugVisibilityTileWidth,
  3847. TheGlobalData->m_debugVisibilityTileDuration,
  3848. TheGlobalData->m_debugVisibilityDeshroudColor);
  3849. }
  3850. }
  3851. #endif
  3852. return shroudClearingRange;
  3853. }
  3854. //-------------------------------------------------------------------------------------------------
  3855. void Object::setShroudClearingRange( Real newShroudClearingRange )
  3856. {
  3857. if( newShroudClearingRange != m_shroudClearingRange )
  3858. {
  3859. // The partition cell refresh is a slow operation, so only do it if you really have to.
  3860. // Range change is a valid reason to relook.
  3861. m_shroudClearingRange = newShroudClearingRange;
  3862. /*
  3863. Complete and total monkey hack fix.
  3864. The problem: newObject doesn't get an initial pos, so all objects start at 0,0,0.
  3865. Most code paths instantly move 'em to a good pos, but in some cases, that is too late:
  3866. If we have search-and-destroy battle plan, we will apply it at that point, and clear out
  3867. a vision range based on our current (wrong) location. Doh!
  3868. So, this just sez: if you are at 0,0,0, don't call handlePartitionCellMaintenance()... since
  3869. you will either (1) be moved elsewhere immediately, thus forcing it to be called via
  3870. another route anyway, or (2) not be moved, which means you are a very naughty and worthless
  3871. object anyway and we should just ignore you.
  3872. Proper fix for next version is to require initial pos to be passed in to newObject so that
  3873. all objects can start at their proper initial position from the start of the ctor.
  3874. (srj)
  3875. */
  3876. const Coord3D* pos = getPosition();
  3877. if (pos->x || pos->y || pos->z)
  3878. {
  3879. handlePartitionCellMaintenance();
  3880. }
  3881. }
  3882. }
  3883. //-------------------------------------------------------------------------------------------------
  3884. Real Object::getShroudRange() const
  3885. {
  3886. #if defined(_DEBUG) || defined(_INTERNAL)
  3887. if (TheGlobalData->m_debugVisibility)
  3888. {
  3889. Vector3 pos(m_shroudRange, 0, 0);
  3890. for (int i = 0; i < TheGlobalData->m_debugVisibilityTileCount; ++i)
  3891. {
  3892. pos.Rotate_Z(1.0f * i / TheGlobalData->m_debugVisibilityTileCount * 2 * PI);
  3893. Coord3D coord = { pos.X + getPosition()->x, pos.Y + getPosition()->y, pos.Z + getPosition()->z };
  3894. addIcon(&coord, TheGlobalData->m_debugVisibilityTileWidth,
  3895. TheGlobalData->m_debugVisibilityTileDuration,
  3896. TheGlobalData->m_debugVisibilityGapColor);
  3897. }
  3898. }
  3899. #endif
  3900. return m_shroudRange;
  3901. }
  3902. //-------------------------------------------------------------------------------------------------
  3903. void Object::setShroudRange( Real newShroudRange )
  3904. {
  3905. m_shroudRange = newShroudRange;
  3906. }
  3907. //-------------------------------------------------------------------------------------------------
  3908. /** Given a special power template, find the module in the object that can implement it.
  3909. * There can be at most one */
  3910. //-------------------------------------------------------------------------------------------------
  3911. SpecialPowerModuleInterface *Object::getSpecialPowerModule( const SpecialPowerTemplate *specialPowerTemplate ) const
  3912. {
  3913. // sanity
  3914. if( specialPowerTemplate == NULL )
  3915. return NULL;
  3916. // search the modules for the one with the matching template
  3917. for (BehaviorModule** m = m_behaviors; *m; ++m)
  3918. {
  3919. SpecialPowerModuleInterface* sp = (*m)->getSpecialPower();
  3920. if (!sp)
  3921. continue;
  3922. if( sp->isModuleForPower( specialPowerTemplate ) )
  3923. return sp;
  3924. }
  3925. return NULL;
  3926. }
  3927. //-------------------------------------------------------------------------------------------------
  3928. /** Execute special power */
  3929. //-------------------------------------------------------------------------------------------------
  3930. void Object::doSpecialPower( const SpecialPowerTemplate *specialPowerTemplate, UnsignedInt commandOptions, Bool forced )
  3931. {
  3932. if (isDisabled())
  3933. return;
  3934. // sanity
  3935. if( !forced && TheSpecialPowerStore->canUseSpecialPower( this, specialPowerTemplate ) == FALSE )
  3936. return;
  3937. // get the module and execute
  3938. SpecialPowerModuleInterface *mod = getSpecialPowerModule( specialPowerTemplate );
  3939. if( mod )
  3940. mod->doSpecialPower( commandOptions );
  3941. }
  3942. //-------------------------------------------------------------------------------------------------
  3943. /** Execute special power */
  3944. //-------------------------------------------------------------------------------------------------
  3945. void Object::doSpecialPowerAtObject( const SpecialPowerTemplate *specialPowerTemplate, Object *obj, UnsignedInt commandOptions, Bool forced )
  3946. {
  3947. if (isDisabled())
  3948. return;
  3949. // sanity
  3950. if( !forced && TheSpecialPowerStore->canUseSpecialPower( this, specialPowerTemplate ) == FALSE )
  3951. return;
  3952. // get the module and execute
  3953. SpecialPowerModuleInterface *mod = getSpecialPowerModule( specialPowerTemplate );
  3954. if( mod )
  3955. mod->doSpecialPowerAtObject( obj, commandOptions );
  3956. }
  3957. //-------------------------------------------------------------------------------------------------
  3958. /** Execute special power */
  3959. //-------------------------------------------------------------------------------------------------
  3960. void Object::doSpecialPowerAtLocation( const SpecialPowerTemplate *specialPowerTemplate,
  3961. const Coord3D *loc, UnsignedInt commandOptions, Bool forced )
  3962. {
  3963. if (isDisabled())
  3964. return;
  3965. // sanity
  3966. if( !forced && TheSpecialPowerStore->canUseSpecialPower( this, specialPowerTemplate ) == FALSE )
  3967. return;
  3968. // get the module and execute
  3969. SpecialPowerModuleInterface *mod = getSpecialPowerModule( specialPowerTemplate );
  3970. if( mod )
  3971. mod->doSpecialPowerAtLocation( loc, commandOptions );
  3972. }
  3973. //-------------------------------------------------------------------------------------------------
  3974. /** Execute special power */
  3975. //-------------------------------------------------------------------------------------------------
  3976. void Object::doSpecialPowerAtMultipleLocations( const SpecialPowerTemplate *specialPowerTemplate,
  3977. const Coord3D *locations, Int locCount, UnsignedInt commandOptions, Bool forced )
  3978. {
  3979. if (isDisabled())
  3980. return;
  3981. // sanity
  3982. if( !forced && TheSpecialPowerStore->canUseSpecialPower( this, specialPowerTemplate ) == FALSE )
  3983. return;
  3984. // get the module and execute
  3985. SpecialPowerModuleInterface *mod = getSpecialPowerModule( specialPowerTemplate );
  3986. if( mod )
  3987. mod->doSpecialPowerAtMultipleLocations( locations, locCount, commandOptions );
  3988. }
  3989. //-------------------------------------------------------------------------------------------------
  3990. /** Execute command button ability */
  3991. //-------------------------------------------------------------------------------------------------
  3992. void Object::doCommandButton( const CommandButton *commandButton, CommandSourceType cmdSource )
  3993. {
  3994. if (isDisabled())
  3995. return;
  3996. AIUpdateInterface *ai = getAIUpdateInterface();
  3997. if( commandButton )
  3998. {
  3999. switch( commandButton->getCommandType() )
  4000. {
  4001. case GUI_COMMAND_SPECIAL_POWER:
  4002. if( commandButton->getSpecialPowerTemplate() )
  4003. {
  4004. CommandOption commandOptions = (CommandOption)(commandButton->getOptions() | COMMAND_FIRED_BY_SCRIPT);
  4005. doSpecialPower( commandButton->getSpecialPowerTemplate(), commandOptions, cmdSource == CMD_FROM_SCRIPT );
  4006. }
  4007. break;
  4008. case GUI_COMMAND_STOP:
  4009. if( ai )
  4010. {
  4011. ai->aiIdle( cmdSource );
  4012. }
  4013. break;
  4014. case GUI_COMMAND_SWITCH_WEAPON:
  4015. {
  4016. WeaponSlotType weaponSlot = commandButton->getWeaponSlot();
  4017. // GUI_COMMAND_SWITCH_WEAPON switches until un-switched, or switched to something else.
  4018. this->setWeaponLock( weaponSlot, LOCKED_PERMANENTLY );
  4019. break;
  4020. }
  4021. case GUI_COMMAND_FIRE_WEAPON:
  4022. {
  4023. WeaponSlotType weaponSlot = commandButton->getWeaponSlot();
  4024. // GUI_COMMAND_FIRE_WEAPON merely fires till the weapon is empty or the attack is "done"
  4025. this->setWeaponLock( weaponSlot, LOCKED_TEMPORARILY );
  4026. break;
  4027. }
  4028. case GUI_COMMAND_OBJECT_UPGRADE:
  4029. case GUI_COMMAND_PLAYER_UPGRADE:
  4030. {
  4031. const UpgradeTemplate *upgradeT = commandButton->getUpgradeTemplate();
  4032. DEBUG_ASSERTCRASH( upgradeT, ("Undefined upgrade '%s' in player upgrade command\n", "UNKNOWN") );
  4033. // sanity
  4034. if( upgradeT == NULL )
  4035. break;
  4036. if( upgradeT->getUpgradeType() == UPGRADE_TYPE_OBJECT )
  4037. {
  4038. if( hasUpgrade( upgradeT ) || !affectedByUpgrade( upgradeT ) )
  4039. break;
  4040. }
  4041. // producer must have a production update
  4042. ProductionUpdateInterface *pu = getProductionUpdateInterface();
  4043. if( pu == NULL )
  4044. break;
  4045. // queue the upgrade "research"
  4046. pu->queueUpgrade( upgradeT );
  4047. }
  4048. break;
  4049. case GUI_COMMAND_UNIT_BUILD:
  4050. case GUI_COMMAND_DOZER_CONSTRUCT: {
  4051. const ThingTemplate *tt = commandButton->getThingTemplate();
  4052. ProductionUpdateInterface *pu = this->getProductionUpdateInterface();
  4053. if (pu && tt) {
  4054. pu->queueCreateUnit( tt, pu->requestUniqueUnitID());
  4055. }
  4056. break;
  4057. }
  4058. case GUI_COMMAND_HACK_INTERNET:{
  4059. if( ai )
  4060. {
  4061. ai->aiHackInternet( cmdSource );
  4062. }
  4063. break;
  4064. }
  4065. //Feel free to implement object based command buttons.
  4066. case GUI_COMMAND_COMBATDROP:
  4067. case GUI_COMMAND_DOZER_CONSTRUCT_CANCEL:
  4068. case GUI_COMMAND_CANCEL_UNIT_BUILD:
  4069. case GUI_COMMAND_CANCEL_UPGRADE:
  4070. case GUI_COMMAND_ATTACK_MOVE:
  4071. case GUI_COMMAND_GUARD:
  4072. case GUI_COMMAND_GUARD_WITHOUT_PURSUIT:
  4073. case GUI_COMMAND_GUARD_FLYING_UNITS_ONLY:
  4074. case GUI_COMMAND_WAYPOINTS:
  4075. case GUI_COMMAND_EXIT_CONTAINER:
  4076. case GUI_COMMAND_EVACUATE:
  4077. case GUI_COMMAND_EXECUTE_RAILED_TRANSPORT:
  4078. case GUI_COMMAND_BEACON_DELETE:
  4079. case GUI_COMMAND_SET_RALLY_POINT:
  4080. case GUI_COMMAND_SELL:
  4081. case GUI_COMMAND_TOGGLE_OVERCHARGE:
  4082. #ifdef ALLOW_SURRENDER
  4083. case GUI_COMMAND_POW_RETURN_TO_PRISON:
  4084. #endif
  4085. case GUICOMMANDMODE_HIJACK_VEHICLE:
  4086. case GUICOMMANDMODE_CONVERT_TO_CARBOMB:
  4087. #ifdef ALLOW_SURRENDER
  4088. case GUICOMMANDMODE_PICK_UP_PRISONER:
  4089. #endif
  4090. default:
  4091. break;
  4092. }
  4093. }
  4094. }
  4095. //-------------------------------------------------------------------------------------------------
  4096. /** Execute command button ability directed at an object target */
  4097. //-------------------------------------------------------------------------------------------------
  4098. void Object::doCommandButtonAtObject( const CommandButton *commandButton, Object *obj, CommandSourceType cmdSource )
  4099. {
  4100. if (isDisabled())
  4101. return;
  4102. AIUpdateInterface *ai = getAIUpdateInterface();
  4103. if( commandButton )
  4104. {
  4105. switch( commandButton->getCommandType() )
  4106. {
  4107. case GUI_COMMAND_COMBATDROP:
  4108. if( ai )
  4109. {
  4110. ai->aiCombatDrop( obj, *(obj->getPosition()), cmdSource );
  4111. }
  4112. break;
  4113. case GUI_COMMAND_SPECIAL_POWER:
  4114. {
  4115. if( commandButton->getSpecialPowerTemplate() )
  4116. {
  4117. CommandOption commandOptions = (CommandOption)(commandButton->getOptions() | COMMAND_FIRED_BY_SCRIPT);
  4118. doSpecialPowerAtObject( commandButton->getSpecialPowerTemplate(), obj, commandOptions, cmdSource == CMD_FROM_SCRIPT );
  4119. }
  4120. break;
  4121. }
  4122. case GUI_COMMAND_STOP:
  4123. if( ai )
  4124. {
  4125. ai->aiIdle( cmdSource );
  4126. }
  4127. break;
  4128. case GUICOMMANDMODE_HIJACK_VEHICLE:
  4129. if ( ai )
  4130. {
  4131. ai->aiEnter(obj, cmdSource);
  4132. }
  4133. break;
  4134. //Feel free to implement object based command buttons.
  4135. case GUI_COMMAND_DOZER_CONSTRUCT:
  4136. case GUI_COMMAND_DOZER_CONSTRUCT_CANCEL:
  4137. case GUI_COMMAND_UNIT_BUILD:
  4138. case GUI_COMMAND_CANCEL_UNIT_BUILD:
  4139. case GUI_COMMAND_PLAYER_UPGRADE:
  4140. case GUI_COMMAND_OBJECT_UPGRADE:
  4141. case GUI_COMMAND_CANCEL_UPGRADE:
  4142. case GUI_COMMAND_ATTACK_MOVE:
  4143. case GUI_COMMAND_GUARD:
  4144. case GUI_COMMAND_GUARD_WITHOUT_PURSUIT:
  4145. case GUI_COMMAND_GUARD_FLYING_UNITS_ONLY:
  4146. case GUI_COMMAND_WAYPOINTS:
  4147. case GUI_COMMAND_EXIT_CONTAINER:
  4148. case GUI_COMMAND_EVACUATE:
  4149. case GUI_COMMAND_EXECUTE_RAILED_TRANSPORT:
  4150. case GUI_COMMAND_BEACON_DELETE:
  4151. case GUI_COMMAND_SET_RALLY_POINT:
  4152. case GUI_COMMAND_SELL:
  4153. case GUI_COMMAND_FIRE_WEAPON:
  4154. case GUI_COMMAND_HACK_INTERNET:
  4155. case GUI_COMMAND_TOGGLE_OVERCHARGE:
  4156. #ifdef ALLOW_SURRENDER
  4157. case GUI_COMMAND_POW_RETURN_TO_PRISON:
  4158. #endif
  4159. case GUI_COMMAND_SWITCH_WEAPON:
  4160. case GUICOMMANDMODE_CONVERT_TO_CARBOMB:
  4161. #ifdef ALLOW_SURRENDER
  4162. case GUICOMMANDMODE_PICK_UP_PRISONER:
  4163. #endif
  4164. default:
  4165. break;
  4166. }
  4167. }
  4168. }
  4169. //-------------------------------------------------------------------------------------------------
  4170. /** Execute command button ability directed at a location */
  4171. //-------------------------------------------------------------------------------------------------
  4172. void Object::doCommandButtonAtPosition( const CommandButton *commandButton, const Coord3D *pos, CommandSourceType cmdSource )
  4173. {
  4174. if (isDisabled())
  4175. return;
  4176. AIUpdateInterface *ai = getAIUpdateInterface();
  4177. if( commandButton )
  4178. {
  4179. switch( commandButton->getCommandType() )
  4180. {
  4181. case GUI_COMMAND_SPECIAL_POWER:
  4182. {
  4183. if( commandButton->getSpecialPowerTemplate() )
  4184. {
  4185. CommandOption commandOptions = (CommandOption)(commandButton->getOptions() | COMMAND_FIRED_BY_SCRIPT);
  4186. doSpecialPowerAtLocation( commandButton->getSpecialPowerTemplate(), pos, commandOptions, cmdSource == CMD_FROM_SCRIPT );
  4187. }
  4188. break;
  4189. }
  4190. case GUI_COMMAND_ATTACK_MOVE:
  4191. if( ai )
  4192. {
  4193. ai->aiAttackMoveToPosition( pos, commandButton->getMaxShotsToFire(), cmdSource );
  4194. }
  4195. break;
  4196. case GUI_COMMAND_STOP:
  4197. if( ai )
  4198. {
  4199. ai->aiIdle( cmdSource );
  4200. }
  4201. break;
  4202. case GUI_COMMAND_DOZER_CONSTRUCT:
  4203. TheBuildAssistant->buildObjectNow( this, commandButton->getThingTemplate(), pos, 0.0f, getControllingPlayer() );
  4204. break;
  4205. case GUI_COMMAND_DOZER_CONSTRUCT_CANCEL:
  4206. case GUI_COMMAND_UNIT_BUILD:
  4207. case GUI_COMMAND_CANCEL_UNIT_BUILD:
  4208. case GUI_COMMAND_PLAYER_UPGRADE:
  4209. case GUI_COMMAND_OBJECT_UPGRADE:
  4210. case GUI_COMMAND_CANCEL_UPGRADE:
  4211. case GUI_COMMAND_GUARD:
  4212. case GUI_COMMAND_GUARD_WITHOUT_PURSUIT:
  4213. case GUI_COMMAND_GUARD_FLYING_UNITS_ONLY:
  4214. case GUI_COMMAND_WAYPOINTS:
  4215. case GUI_COMMAND_EXIT_CONTAINER:
  4216. case GUI_COMMAND_EVACUATE:
  4217. case GUI_COMMAND_EXECUTE_RAILED_TRANSPORT:
  4218. case GUI_COMMAND_BEACON_DELETE:
  4219. case GUI_COMMAND_SET_RALLY_POINT:
  4220. case GUI_COMMAND_SELL:
  4221. case GUI_COMMAND_FIRE_WEAPON:
  4222. case GUI_COMMAND_HACK_INTERNET:
  4223. case GUI_COMMAND_TOGGLE_OVERCHARGE:
  4224. #ifdef ALLOW_SURRENDER
  4225. case GUI_COMMAND_POW_RETURN_TO_PRISON:
  4226. #endif
  4227. case GUI_COMMAND_COMBATDROP:
  4228. case GUI_COMMAND_SWITCH_WEAPON:
  4229. case GUICOMMANDMODE_HIJACK_VEHICLE:
  4230. case GUICOMMANDMODE_CONVERT_TO_CARBOMB:
  4231. #ifdef ALLOW_SURRENDER
  4232. case GUICOMMANDMODE_PICK_UP_PRISONER:
  4233. #endif
  4234. default:
  4235. break;
  4236. }
  4237. }
  4238. }
  4239. // ------------------------------------------------------------------------------------------------
  4240. // ------------------------------------------------------------------------------------------------
  4241. void Object::clearLeechRangeModeForAllWeapons()
  4242. {
  4243. m_weaponSet.clearLeechRangeModeForAllWeapons();
  4244. }
  4245. // ------------------------------------------------------------------------------------------------
  4246. /** Search our update modules for a production update interface and return it if one is found */
  4247. // ------------------------------------------------------------------------------------------------
  4248. ProductionUpdateInterface* Object::getProductionUpdateInterface( void )
  4249. {
  4250. ProductionUpdateInterface *pui;
  4251. // tell our update modules that we intend to do this special power.
  4252. for( BehaviorModule** u = m_behaviors; *u; ++u )
  4253. {
  4254. pui = (*u)->getProductionUpdateInterface();
  4255. if( pui )
  4256. return pui;
  4257. } // end for
  4258. return NULL;
  4259. } // end getProductionUpdateInterface
  4260. // ------------------------------------------------------------------------------------------------
  4261. // ------------------------------------------------------------------------------------------------
  4262. DockUpdateInterface *Object::getDockUpdateInterface( void )
  4263. {
  4264. DockUpdateInterface *dock = NULL;
  4265. for( BehaviorModule **u = m_behaviors; *u; ++u )
  4266. {
  4267. if( (dock = (*u)->getDockUpdateInterface()) != NULL )
  4268. return dock;
  4269. }
  4270. return NULL;
  4271. } // end getDockUpdateInterface
  4272. // ------------------------------------------------------------------------------------------------
  4273. // Search our special power modules for a specific one.
  4274. // ------------------------------------------------------------------------------------------------
  4275. SpecialPowerModuleInterface* Object::findSpecialPowerModuleInterface( SpecialPowerType type ) const
  4276. {
  4277. for (BehaviorModule** m = m_behaviors; *m; ++m)
  4278. {
  4279. SpecialPowerModuleInterface* sp = (*m)->getSpecialPower();
  4280. if (!sp)
  4281. continue;
  4282. const SpecialPowerTemplate *spTemplate = sp->getSpecialPowerTemplate();
  4283. if (spTemplate && spTemplate->getSpecialPowerType() == type)
  4284. {
  4285. return sp;
  4286. }
  4287. }
  4288. return NULL;
  4289. }
  4290. // ------------------------------------------------------------------------------------------------
  4291. /** Get spawn behavior interface from object */
  4292. // ------------------------------------------------------------------------------------------------
  4293. SpawnBehaviorInterface* Object::getSpawnBehaviorInterface() const
  4294. {
  4295. for (BehaviorModule** m = m_behaviors; *m; ++m)
  4296. {
  4297. SpawnBehaviorInterface *sbi = (*m)->getSpawnBehaviorInterface();
  4298. if( sbi )
  4299. {
  4300. return sbi;
  4301. }
  4302. }
  4303. return NULL;
  4304. } // end getSpawnBehaviorInterfaceFromObject
  4305. // ------------------------------------------------------------------------------------------------
  4306. // Simply find the special power module that is currently allowing plotting of positions to target.
  4307. // ------------------------------------------------------------------------------------------------
  4308. SpecialPowerUpdateInterface* Object::findSpecialPowerWithOverridableDestinationActive( SpecialPowerType type ) const
  4309. {
  4310. for( BehaviorModule** u = m_behaviors; *u; ++u )
  4311. {
  4312. SpecialPowerUpdateInterface *spInterface = (*u)->getSpecialPowerUpdateInterface();
  4313. if( spInterface )
  4314. {
  4315. if( spInterface->doesSpecialPowerHaveOverridableDestinationActive() )
  4316. {
  4317. return spInterface;
  4318. }
  4319. }
  4320. } // end for
  4321. return NULL;
  4322. }
  4323. // ------------------------------------------------------------------------------------------------
  4324. // Search our special ability updates for a specific one.
  4325. // ------------------------------------------------------------------------------------------------
  4326. SpecialAbilityUpdate* Object::findSpecialAbilityUpdate( SpecialPowerType type ) const
  4327. {
  4328. for( BehaviorModule** u = m_behaviors; *u; ++u )
  4329. {
  4330. SpecialPowerUpdateInterface *spInterface = (*u)->getSpecialPowerUpdateInterface();
  4331. if( spInterface && spInterface->isSpecialAbility() )
  4332. {
  4333. SpecialAbilityUpdate *spUpdate = (SpecialAbilityUpdate*)spInterface;
  4334. if( spUpdate->getSpecialPowerType() == type )
  4335. {
  4336. return spUpdate;
  4337. }
  4338. }
  4339. } // end for
  4340. return NULL;
  4341. }
  4342. // ------------------------------------------------------------------------------------------------
  4343. SpecialPowerCompletionDie* Object::findSpecialPowerCompletionDie() const
  4344. {
  4345. static NameKeyType key_SpecialPowerCompletionDie = NAMEKEY("SpecialPowerCompletionDie");
  4346. return (SpecialPowerCompletionDie*)findModule(key_SpecialPowerCompletionDie);
  4347. }
  4348. // ------------------------------------------------------------------------------------------------
  4349. Int Object::getNumConsecutiveShotsFiredAtTarget( const Object *victim ) const
  4350. {
  4351. return m_firingTracker ? m_firingTracker->getNumConsecutiveShotsAtVictim( victim ) : 0;
  4352. }
  4353. // ------------------------------------------------------------------------------------------------
  4354. // ------------------------------------------------------------------------------------------------
  4355. Bool Object::getSingleLogicalBonePosition(const char* boneName, Coord3D* position, Matrix3D* transform) const
  4356. {
  4357. if (m_drawable && m_drawable->getPristineBonePositions( boneName, 0, position, transform, 1 ) == 1 )
  4358. {
  4359. m_drawable->convertBonePosToWorldPos( position, transform, position, transform );
  4360. return true;
  4361. }
  4362. else
  4363. {
  4364. if (position)
  4365. *position = *getPosition();
  4366. if (transform)
  4367. *transform = *getTransformMatrix();
  4368. return false;
  4369. }
  4370. }
  4371. // ------------------------------------------------------------------------------------------------
  4372. // ------------------------------------------------------------------------------------------------
  4373. Bool Object::getSingleLogicalBonePositionOnTurret( WhichTurretType whichTurret, const char* boneName, Coord3D* position, Matrix3D* transform ) const
  4374. {
  4375. Coord3D turretPosition;
  4376. Coord3D bonePosition;
  4377. if( getDrawable() == NULL || getAI() == NULL )
  4378. return FALSE;
  4379. // We need to find the TurretBone's pristine position.
  4380. getDrawable()->getProjectileLaunchOffset( PRIMARY_WEAPON, 1, NULL, whichTurret, &turretPosition, NULL );
  4381. // And the required bone's pristine position
  4382. if( getDrawable()->getPristineBonePositions(boneName, 0, &bonePosition, NULL, 1) != 1 )
  4383. return FALSE;
  4384. //Then we mojo the Logic position of the required bone like Missile firing does. Using the logic twist of the turret
  4385. Real turretRotation;
  4386. getAI()->getTurretRotAndPitch( whichTurret, &turretRotation, NULL );
  4387. Matrix3D boneOffset(TRUE);// This will be from the turret to the requested bone
  4388. // Vector3 bonePositionVector( bonePosition.x - turretPosition.x,
  4389. // bonePosition.y - turretPosition.y,
  4390. // bonePosition.z - turretPosition.z );
  4391. Vector3 bonePositionVector( bonePosition.x,
  4392. bonePosition.y,
  4393. bonePosition.z );
  4394. boneOffset.Translate(bonePositionVector);
  4395. Matrix3D turnAdjustment(TRUE);// this is the turret twist to be applied to the final answer
  4396. turnAdjustment.Translate( turretPosition.x, turretPosition.y, turretPosition.z );
  4397. turnAdjustment.In_Place_Pre_Rotate_Z(turretRotation);
  4398. turnAdjustment.Translate( -turretPosition.x, -turretPosition.y, -turretPosition.z );
  4399. Matrix3D boneLogicTransform;
  4400. boneLogicTransform.mul( turnAdjustment, boneOffset );
  4401. Matrix3D worldTransform;
  4402. convertBonePosToWorldPos(NULL, &boneLogicTransform, NULL, &worldTransform);
  4403. Vector3 tmp = worldTransform.Get_Translation();
  4404. Coord3D worldPos;
  4405. worldPos.x = tmp.X;
  4406. worldPos.y = tmp.Y;
  4407. worldPos.z = tmp.Z;
  4408. if( position )
  4409. *position = worldPos;
  4410. if( transform )
  4411. *transform = worldTransform;
  4412. return TRUE;
  4413. }
  4414. // ------------------------------------------------------------------------------------------------
  4415. // ------------------------------------------------------------------------------------------------
  4416. Int Object::getMultiLogicalBonePosition(const char* boneNamePrefix, Int maxBones,
  4417. Coord3D* positions, Matrix3D* transforms,
  4418. Bool convertToWorld ) const
  4419. {
  4420. Int count;
  4421. if (m_drawable && (count = m_drawable->getPristineBonePositions( boneNamePrefix, 1, positions, transforms, maxBones )) > 0 )
  4422. {
  4423. if( convertToWorld )
  4424. {
  4425. for (Int i = 0; i < count; ++i)
  4426. m_drawable->convertBonePosToWorldPos( positions ? &positions[i] : NULL, transforms ? &transforms[i] : NULL, positions ? &positions[i] : NULL, transforms ? &transforms[i] : NULL );
  4427. }
  4428. return count;
  4429. }
  4430. else
  4431. {
  4432. return 0;
  4433. }
  4434. }
  4435. //=============================================================================
  4436. Bool Object::canProduceUpgrade( const UpgradeTemplate *upgrade )
  4437. {
  4438. // We need to have the button to make the upgrade. CommandSets are a weird Logic/Client hybrid.
  4439. const CommandSet *set = TheControlBar->findCommandSet(getCommandSetString());
  4440. for( Int buttonIndex = 0; buttonIndex < MAX_COMMANDS_PER_SET; buttonIndex++ )
  4441. {
  4442. const CommandButton *button = set->getCommandButton(buttonIndex);
  4443. if( button
  4444. && ( (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
  4445. && button->getUpgradeTemplate()
  4446. && (button->getUpgradeTemplate() == upgrade)
  4447. )
  4448. return TRUE; // getUpgradeTemplate only returns something if it is actually an upgrade
  4449. }
  4450. return FALSE;// Cheatin' punk.
  4451. }
  4452. //=============================================================================
  4453. const AsciiString& Object::getCommandSetString() const
  4454. {
  4455. if (m_commandSetStringOverride.isNotEmpty())
  4456. return m_commandSetStringOverride;
  4457. return getTemplate()->friend_getCommandSetString();
  4458. }
  4459. //=============================================================================
  4460. // Object::defect, and related methods =
  4461. //=============================================================================
  4462. void Object::defect( Team* newTeam, UnsignedInt detectionTime )
  4463. {
  4464. if ( isContained() ) //@todo (KRIS?) make contained units unselectable, until then... lorenzen
  4465. {
  4466. return;
  4467. }
  4468. Player *player = getControllingPlayer();
  4469. if ( !player )
  4470. return;
  4471. Team* myTeam = player->getDefaultTeam();
  4472. if ( myTeam == newTeam ) // can't defect from my own team, that would be silly
  4473. return;
  4474. // things that are under construction, or sold, cannot defect.
  4475. if (testStatus(OBJECT_STATUS_UNDER_CONSTRUCTION) ||
  4476. testStatus(OBJECT_STATUS_SOLD))
  4477. {
  4478. return;
  4479. }
  4480. // Before switch ////////////////////////////////////////
  4481. //Design says:
  4482. ProductionUpdateInterface *production = getProductionUpdateInterface();
  4483. if ( production )
  4484. {
  4485. production->cancelAndRefundAllProduction();
  4486. }
  4487. // pop it up on the radar, so as to warn those who care
  4488. // do this first, since after setTeam() the infiltrator
  4489. // becomes the controllingplayer, not me
  4490. // But don't do this is if the new team is not a real team. "'Enemy' infiltration" wouldn't make
  4491. // sense, and we are probably just reverting a cave or something.
  4492. if( friend_getRadarData() && newTeam->getControllingPlayer()->isPlayableSide() && myTeam->getControllingPlayer()->isPlayableSide())
  4493. {
  4494. TheRadar->tryInfiltrationEvent( this );
  4495. }
  4496. friend_setUndetectedDefector( detectionTime > 0 );
  4497. if (m_defectionHelper)
  4498. m_defectionHelper->startDefectionTimer(detectionTime);
  4499. // Switch ////////////////////////////////////////
  4500. setTeam( newTeam );
  4501. // After switch ////////////////////////////////////////
  4502. AIUpdateInterface *ai = getAI();
  4503. handlePartitionCellMaintenance();// to clear the shoud for my new master
  4504. if ( ai )
  4505. {
  4506. ai->aiIdle( CMD_FROM_AI );
  4507. }
  4508. // Play our sound indicating we've been defected. (weird verbage, but true.)
  4509. AudioEventRTS voiceDefect = *getTemplate()->getVoiceDefect();
  4510. voiceDefect.setObjectID(getID());
  4511. TheAudio->addAudioEvent(&voiceDefect);
  4512. //make the new recruit the only selected thing, awaiting new command to move, attack, etc...
  4513. Drawable *dr = getDrawable();
  4514. if (dr)
  4515. {
  4516. dr->flashAsSelected(); //This is the first of several flashes which get cue'd by doDefectorUpdateStuff()
  4517. AudioEventRTS defectorTimerSound = TheAudio->getMiscAudio()->m_defectorTimerTickSound;
  4518. defectorTimerSound.setObjectID( getID() );
  4519. TheAudio->addAudioEvent(&defectorTimerSound);
  4520. }
  4521. ContainModuleInterface *ct = getContain();
  4522. if( ct && ct->isKickOutOnCapture() )
  4523. {
  4524. // Caves really really don't want to do this.
  4525. ct->removeAllContained( TRUE );
  4526. }
  4527. // if it has parking places, defect anything parked there.
  4528. for (BehaviorModule** i = getBehaviorModules(); *i; ++i)
  4529. {
  4530. ParkingPlaceBehaviorInterface* pp = (*i)->getParkingPlaceBehaviorInterface();
  4531. if (pp)
  4532. {
  4533. pp->defectAllParkedUnits(newTeam, detectionTime);
  4534. break;
  4535. }
  4536. }
  4537. // defect any mines that are owned by this structure, right now.
  4538. // unfortunately, structures don't keep list of mines they own, so we must do
  4539. // this the hard way :-( [fortunately, this doens't happen very often, so this
  4540. // is probably an acceptable, if icky, solution.] (srj)
  4541. for (Object* mine = TheGameLogic->getFirstObject(); mine; mine = mine->getNextObject())
  4542. {
  4543. if (mine->isKindOf(KINDOF_MINE))
  4544. {
  4545. if (mine->getProducerID() == this->getID())
  4546. {
  4547. mine->setTeam(newTeam);
  4548. }
  4549. }
  4550. }
  4551. }
  4552. //=============================================================================
  4553. // Object::goInvulnerable
  4554. //=============================================================================
  4555. void Object::goInvulnerable( UnsignedInt time )
  4556. {
  4557. const Bool WITHOUT_DEFECTOR_FX = FALSE;
  4558. friend_setUndetectedDefector( time > 0 );
  4559. if (m_defectionHelper)
  4560. m_defectionHelper->startDefectionTimer(time, WITHOUT_DEFECTOR_FX);
  4561. }
  4562. // ------------------------------------------------------------------------------------------------
  4563. /** Return the radar priority for this object type */
  4564. // ------------------------------------------------------------------------------------------------
  4565. RadarPriorityType Object::getRadarPriority( void ) const
  4566. {
  4567. RadarPriorityType priority = RADAR_PRIORITY_INVALID;
  4568. // first, get the priority at the thing template level
  4569. priority = getTemplate()->getDefaultRadarPriority();
  4570. //
  4571. // there are some objects that we want to show up on the radar when they have
  4572. // certain properties ... here we will check for those properties unless the INI
  4573. // setting of "not on radar" has been manually entered which explicitly forbids an
  4574. // object from being on the radar ... by default objects get an "invalid" priority
  4575. // on the radar and this means that we are free to decide one here if we want
  4576. //
  4577. if( priority == RADAR_PRIORITY_INVALID )
  4578. {
  4579. // objects that are "garrisonable" show up on the radar
  4580. ContainModuleInterface *cmi = getContain();
  4581. if( cmi && cmi->isGarrisonable() )
  4582. priority = RADAR_PRIORITY_STRUCTURE;
  4583. // objects that are "capturable" show up on the radar
  4584. if( isKindOf( KINDOF_CAPTURABLE ) )
  4585. priority = RADAR_PRIORITY_STRUCTURE;
  4586. } // end if
  4587. // Carbombs will show up as units regardless of their default priority
  4588. if ( testStatus( OBJECT_STATUS_IS_CARBOMB ) )
  4589. priority = RADAR_PRIORITY_UNIT;
  4590. // return the priority we're going to use
  4591. return priority;
  4592. } // end getRadarPriority
  4593. // ------------------------------------------------------------------------------------------------
  4594. AIGroup *Object::getGroup(void)
  4595. {
  4596. return m_group;
  4597. }
  4598. //-------------------------------------------------------------------------------------------------
  4599. void Object::enterGroup( AIGroup *group )
  4600. {
  4601. // DEBUG_LOG(("***AIGROUP %x involved in enterGroup on %x\n", group, this));
  4602. // if we are in another group, remove ourselves from it first
  4603. leaveGroup();
  4604. m_group = group;
  4605. }
  4606. //-------------------------------------------------------------------------------------------------
  4607. void Object::leaveGroup( void )
  4608. {
  4609. // DEBUG_LOG(("***AIGROUP %x involved in leaveGroup on %x\n", m_group, this));
  4610. // if we are in a group, remove ourselves from it
  4611. if (m_group)
  4612. {
  4613. // to avoid recursion, set m_group to NULL before removing
  4614. AIGroup *group = m_group;
  4615. m_group = NULL;
  4616. group->remove( this );
  4617. }
  4618. }