| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893 |
- /*
- ** Command & Conquer Generals(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** This program is free software: you can redistribute it and/or modify
- ** it under the terms of the GNU General Public License as published by
- ** the Free Software Foundation, either version 3 of the License, or
- ** (at your option) any later version.
- **
- ** This program is distributed in the hope that it will be useful,
- ** but WITHOUT ANY WARRANTY; without even the implied warranty of
- ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- ** GNU General Public License for more details.
- **
- ** You should have received a copy of the GNU General Public License
- ** along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- ////////////////////////////////////////////////////////////////////////////////
- // //
- // (c) 2001-2003 Electronic Arts Inc. //
- // //
- ////////////////////////////////////////////////////////////////////////////////
- // Drawable.cpp ///////////////////////////////////////////////////////////////////////////////////
- // "Drawables" - graphical GameClient entities bound to GameLogic objects
- // Author: Michael S. Booth, March 2001
- ///////////////////////////////////////////////////////////////////////////////////////////////////
-
- #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
- #include "Common/AudioEventInfo.h"
- #include "Common/AudioSettings.h"
- #include "Common/BitFlagsIO.h"
- #include "Common/BuildAssistant.h"
- #include "Common/ClientUpdateModule.h"
- #include "Common/DrawModule.h"
- #include "Common/GameAudio.h"
- #include "Common/GameEngine.h"
- #include "Common/GameLOD.h"
- #include "Common/GameState.h"
- #include "Common/GlobalData.h"
- #include "Common/ModuleFactory.h"
- #include "Common/PerfTimer.h"
- #include "Common/Player.h"
- #include "Common/PlayerList.h"
- #include "Common/ThingFactory.h"
- #include "Common/ThingTemplate.h"
- #include "Common/Xfer.h"
- #include "GameLogic/ExperienceTracker.h"
- #include "GameLogic/GameLogic.h" // for logic frame count
- #include "GameLogic/Object.h"
- #include "GameLogic/Locomotor.h"
- #include "GameLogic/Module/AIUpdate.h"
- #include "GameLogic/Module/BodyModule.h"
- #include "GameLogic/Module/ContainModule.h"
- #include "GameLogic/Module/PhysicsUpdate.h"
- #include "GameLogic/Module/StealthUpdate.h"
- #include "GameLogic/Module/StickyBombUpdate.h"
- #include "GameLogic/Module/BattlePlanUpdate.h"
- #include "GameLogic/ScriptEngine.h"
- #include "GameLogic/Weapon.h"
- #include "GameClient/Anim2D.h"
- #include "GameClient/Display.h"
- #include "GameClient/DisplayStringManager.h"
- #include "GameClient/Drawable.h"
- #include "GameClient/DrawGroupInfo.h"
- #include "GameClient/GameClient.h"
- #include "GameClient/GlobalLanguage.h"
- #include "GameClient/InGameUI.h"
- #include "GameClient/Image.h"
- #include "GameClient/ParticleSys.h"
- #include "GameClient/LanguageFilter.h"
- #include "GameClient/Shadow.h"
- #include "GameClient/GameText.h"
- #ifdef _INTERNAL
- // for occasional debugging...
- //#pragma optimize("", off)
- //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
- #endif
- #define VERY_TRANSPARENT_HEATVISION (0.001f)
- #define HEATVISION_FADE_SCALAR (0.8f)
- static const char *TheDrawableIconNames[] =
- {
- "DefaultHeal",
- "StructureHeal",
- "VehicleHeal",
- #ifdef ALLOW_DEMORALIZE
- "Demoralized",
- #else
- "Demoralized_OBSOLETE",
- #endif
- "BombTimed",
- "BombRemote",
- "Disabled",
- "BattlePlanIcon_Bombard",
- "BattlePlanIcon_HoldTheLine",
- "BattlePlanIcon_SeekAndDestroy",
- "Emoticon",
- "Enthusiastic",//a red cross? // soon to replace?
- "Subliminal", //with the gold border! replace?
- "CarBomb",
- NULL
- };
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- DrawableIconInfo::DrawableIconInfo()
- {
- for (int i = 0; i < MAX_ICONS; ++i)
- {
- m_icon[i] = NULL;
- m_keepTillFrame[i] = 0;
- }
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- DrawableIconInfo::~DrawableIconInfo()
- {
- clear();
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void DrawableIconInfo::clear()
- {
- for (int i = 0; i < MAX_ICONS; ++i)
- {
- if (m_icon[i])
- m_icon[i]->deleteInstance();
- m_icon[i] = NULL;
- m_keepTillFrame[i] = 0;
- }
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void DrawableIconInfo::killIcon(DrawableIconType t)
- {
- if (m_icon[t])
- {
- m_icon[t]->deleteInstance();
- m_icon[t] = NULL;
- m_keepTillFrame[t] = 0;
- }
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- DrawableLocoInfo::DrawableLocoInfo()
- {
- m_pitch = 0.0f;
- m_pitchRate = 0.0f;
- m_roll = 0.0f;
- m_rollRate = 0.0f;
- m_yaw = 0.0f;
- m_accelerationPitch = 0.0f;
- m_accelerationPitchRate = 0.0f;
- m_accelerationRoll = 0.0f;
- m_accelerationRollRate = 0.0f;
- m_overlapZVel = 0.0f;
- m_overlapZ = 0.0f;
- m_wobble = 1.0f;
- m_wheelInfo.m_frontLeftHeightOffset = 0;
- m_wheelInfo.m_frontRightHeightOffset = 0;
- m_wheelInfo.m_rearLeftHeightOffset = 0;
- m_wheelInfo.m_rearRightHeightOffset = 0;
- m_wheelInfo.m_framesAirborneCounter = 0;
- m_wheelInfo.m_framesAirborne = 0;
- m_wheelInfo.m_wheelAngle = 0;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- DrawableLocoInfo::~DrawableLocoInfo()
- {
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- static const char *drawableIconIndexToName( DrawableIconType iconIndex )
- {
- DEBUG_ASSERTCRASH( iconIndex >= ICON_FIRST && iconIndex < MAX_ICONS,
- ("drawableIconIndexToName - Illegal index '%d'\n", iconIndex) );
- return TheDrawableIconNames[ iconIndex ];
- } // end drawableIconIndexToName
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- static DrawableIconType drawableIconNameToIndex( const char *iconName )
- {
- DEBUG_ASSERTCRASH( iconName != NULL, ("drawableIconNameToIndex - Illegal name\n") );
- for( Int i = ICON_FIRST; i < MAX_ICONS; ++i )
- if( stricmp( TheDrawableIconNames[ i ], iconName ) == 0 )
- return (DrawableIconType)i;
- return ICON_INVALID;
- } // end drawableIconNameToIndex
- // ------------------------------------------------------------------------------------------------
- // constants
- const UnsignedInt HEALING_ICON_DISPLAY_TIME = LOGICFRAMES_PER_SECOND * 3;
- const UnsignedInt DEFAULT_HEAL_ICON_WIDTH = 32;
- const UnsignedInt DEFAULT_HEAL_ICON_HEIGHT = 32;
- const RGBColor SICKLY_GREEN_POISONED_COLOR = {-1.0f, 1.0f, -1.0f};
- const RGBColor DARK_GRAY_DISABLED_COLOR = {-0.5f, -0.5f, -0.5f};
- const RGBColor RED_IRRADIATED_COLOR = { 1.0f, -1.0f, -1.0f};
- const Int MAX_ENABLED_MODULES = 16;
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- /*static*/ Bool Drawable::s_staticImagesInited = false;
- /*static*/ const Image* Drawable::s_veterancyImage[LEVEL_COUNT] = { NULL };
- /*static*/ const Image* Drawable::s_fullAmmo = NULL;
- /*static*/ const Image* Drawable::s_emptyAmmo = NULL;
- /*static*/ const Image* Drawable::s_fullContainer = NULL;
- /*static*/ const Image* Drawable::s_emptyContainer = NULL;
- /*static*/ Anim2DTemplate** Drawable::s_animationTemplates = NULL;
- #ifdef DIRTY_CONDITION_FLAGS
- /*static*/ Int Drawable::s_modelLockCount = 0;
- #endif
- // ------------------------------------------------------------------------------------------------
- /*static*/ void Drawable::initStaticImages()
- {
- if (s_staticImagesInited)
- return;
- s_veterancyImage[0] = NULL;
- s_veterancyImage[1] = TheMappedImageCollection->findImageByName("SCVeter1");
- s_veterancyImage[2] = TheMappedImageCollection->findImageByName("SCVeter2");
- s_veterancyImage[3] = TheMappedImageCollection->findImageByName("SCVeter3");
- s_fullAmmo = TheMappedImageCollection->findImageByName("SCPAmmoFull");
- s_emptyAmmo = TheMappedImageCollection->findImageByName("SCPAmmoEmpty");
- s_fullContainer = TheMappedImageCollection->findImageByName("SCPPipFull");
- s_emptyContainer = TheMappedImageCollection->findImageByName("SCPPipEmpty");
-
- s_animationTemplates = NEW Anim2DTemplate* [ MAX_ICONS ];
- s_animationTemplates[ICON_DEFAULT_HEAL] = TheAnim2DCollection->findTemplate(TheDrawableIconNames[ICON_DEFAULT_HEAL]);
- s_animationTemplates[ICON_STRUCTURE_HEAL] = TheAnim2DCollection->findTemplate(TheDrawableIconNames[ICON_STRUCTURE_HEAL]);
- s_animationTemplates[ICON_VEHICLE_HEAL] = TheAnim2DCollection->findTemplate(TheDrawableIconNames[ICON_VEHICLE_HEAL]);
- #ifdef ALLOW_DEMORALIZE
- s_animationTemplates[ICON_DEMORALIZED] = TheAnim2DCollection->findTemplate(TheDrawableIconNames[ICON_DEMORALIZED]);
- #endif
- s_animationTemplates[ICON_BOMB_TIMED] = TheAnim2DCollection->findTemplate(TheDrawableIconNames[ICON_BOMB_TIMED]);
- s_animationTemplates[ICON_BOMB_REMOTE] = TheAnim2DCollection->findTemplate(TheDrawableIconNames[ICON_BOMB_REMOTE]);
- s_animationTemplates[ICON_DISABLED] = TheAnim2DCollection->findTemplate(TheDrawableIconNames[ICON_DISABLED]);
- s_animationTemplates[ICON_BATTLEPLAN_BOMBARD] = TheAnim2DCollection->findTemplate(TheDrawableIconNames[ICON_BATTLEPLAN_BOMBARD]);
- s_animationTemplates[ICON_BATTLEPLAN_HOLDTHELINE] = TheAnim2DCollection->findTemplate(TheDrawableIconNames[ICON_BATTLEPLAN_HOLDTHELINE]);
- s_animationTemplates[ICON_BATTLEPLAN_SEARCHANDDESTROY] = TheAnim2DCollection->findTemplate(TheDrawableIconNames[ICON_BATTLEPLAN_SEARCHANDDESTROY]);
- s_animationTemplates[ICON_EMOTICON] = NULL; //Emoticons can be anything, so we'll need to handle it dynamically.
- s_animationTemplates[ICON_ENTHUSIASTIC] = TheAnim2DCollection->findTemplate(TheDrawableIconNames[ICON_ENTHUSIASTIC]);
- s_animationTemplates[ICON_ENTHUSIASTIC_SUBLIMINAL] = TheAnim2DCollection->findTemplate(TheDrawableIconNames[ICON_ENTHUSIASTIC_SUBLIMINAL]);
- s_animationTemplates[ICON_CARBOMB] = TheAnim2DCollection->findTemplate(TheDrawableIconNames[ICON_CARBOMB]);
- s_staticImagesInited = true;
- }
- //-------------------------------------------------------------------------------------------------
- /*static*/ void Drawable::killStaticImages()
- {
- if( s_animationTemplates )
- {
- delete s_animationTemplates;
- s_animationTemplates = NULL;
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Drawable::saturateRGB(RGBColor& color, Real factor)
- {
- color.red *= factor;
- color.green *= factor;
- color.blue *= factor;
- Real halfFactor = factor * 0.5f;
- color.red -= halfFactor;
- color.green -= halfFactor;
- color.blue -= halfFactor;
- }
- //--- A MACRO TO APPLY TO TheTacticalView->getZoom() ------ To Clamp the return to a visually pleasing size
- //--- so that icons, emoticons, health bars, pips, etc, look reasonably solid and don't shimmer or tweed
- //#define CLAMP_ICON_ZOOM_FACTOR(n) (MAX(0.80f, MIN(1.00f, n)))
- #define CLAMP_ICON_ZOOM_FACTOR(n) (n)//nothing
- //-------------------------------------------------------------------------------------------------
- /** Drawables are lightweight, graphical entities which live on the GameClient,
- * and are usually bound to GameLogic objects. In other words, they are the
- * graphical side of a logical object, whereas GameLogic objects encapsulate
- * behaviors and physics. */
- //-------------------------------------------------------------------------------------------------
- Drawable::Drawable( const ThingTemplate *thingTemplate, DrawableStatus statusBits )
- : Thing( thingTemplate )
- {
- // assign status bits before anything else can be done
- m_status = statusBits;
-
- // Added By Sadullah Nader
- // Initialization missing and needed
- m_nextDrawable = NULL;
- m_prevDrawable = NULL;
- //
- // register drawable with the GameClient ... do this first before we start doing anything
- // complex that uses any of the drawable data so that we have and ID!! It's ok to initialize
- // members of the drawable before this registration happens
- //
- TheGameClient->registerDrawable( this );
- Int i;
- // Added By Sadullah Nader
- // Initialization missing and needed
- m_flashColor = 0;
- m_selected = '\0';
- //
- m_expirationDate = 0; // 0 == never expires
- m_lastConstructDisplayed = -1.0f;
-
- //Added By Sadullah Nader
- //Fix for the building percent
- m_constructDisplayString = TheDisplayStringManager->newDisplayString();
- m_constructDisplayString->setFont(TheFontLibrary->getFont(TheInGameUI->getDrawableCaptionFontName(),
- TheGlobalLanguageData->adjustFontSize(TheInGameUI->getDrawableCaptionPointSize()),
- TheInGameUI->isDrawableCaptionBold() ));
- m_ambientSound = NULL;
- m_decalOpacityFadeTarget = 0;
- m_decalOpacityFadeRate = 0;
- m_decalOpacity = 0;
- m_explicitOpacity = 1.0f;
- m_stealthOpacity = 1.0f;
- m_effectiveStealthOpacity = 1.0f;
- m_terrainDecalType = TERRAIN_DECAL_NONE;
- m_fadeMode = FADING_NONE;
- m_timeElapsedFade = 0;
- m_timeToFade = 0;
- m_shroudClearFrame = 0;
- for (i = 0; i < NUM_DRAWABLE_MODULE_TYPES; ++i)
- m_modules[i] = NULL;
- m_stealthLook = STEALTHLOOK_NONE;
- m_flashCount = 0;
- m_locoInfo = NULL;
- // sanity
- if( TheGameClient == NULL || thingTemplate == NULL )
- {
- assert( 0 );
- return;
- } // end if
- m_instance.Make_Identity();
- m_instanceIsIdentity = true;
- //Real scaleFuzziness = thingTemplate->getInstanceScaleFuzziness();
- //Real fuzzyScale = ( 1.0f + GameClientRandomValueReal( -scaleFuzziness, scaleFuzziness ));
- m_instanceScale = thingTemplate->getAssetScale();// * fuzzyScale;
- // initially not bound to an object
- m_object = NULL;
- m_particle = NULL;
- // tintStatusTracking
- m_tintStatus = 0;
- m_prevTintStatus = 0;
- #ifdef DIRTY_CONDITION_FLAGS
- m_isModelDirty = true;
- #endif
- m_hidden = false;
- m_hiddenByStealth = false;
- m_heatVisionOpacity = 0.0f;
- m_drawableFullyObscuredByShroud = false;
- m_ambientSoundEnabled = TRUE;
- //
- // allocate any modules we need to, we should keep
- // this at or near the end of the drawable construction so that we have
- // all the valid data about the thing when we create the module
- //
- //Filter out drawable modules which have been disabled because of game LOD.
- Int modIdx;
- Module** m;
- const ModuleInfo& drawMI = thingTemplate->getDrawModuleInfo();
- m_modules[MODULETYPE_DRAW - FIRST_DRAWABLE_MODULE_TYPE] = MSGNEW("ModulePtrs") Module*[drawMI.getCount()+1]; // pool[]ify
- m = m_modules[MODULETYPE_DRAW - FIRST_DRAWABLE_MODULE_TYPE];
- for (modIdx = 0; modIdx < drawMI.getCount(); ++modIdx)
- {
- const ModuleData* newModData = drawMI.getNthData(modIdx);
- if (TheGlobalData->m_useDrawModuleLOD &&
- newModData->getMinimumRequiredGameLOD() > TheGameLODManager->getStaticLODLevel())
- continue;
- *m++ = TheModuleFactory->newModule(this, drawMI.getNthName(modIdx), newModData, MODULETYPE_DRAW);
- }
- *m = NULL;
- const ModuleInfo& cuMI = thingTemplate->getClientUpdateModuleInfo();
- if (cuMI.getCount())
- {
- // since most things don't have CU modules, we allow this to be null!
- m_modules[MODULETYPE_CLIENT_UPDATE - FIRST_DRAWABLE_MODULE_TYPE] = MSGNEW("ModulePtrs") Module*[cuMI.getCount()+1]; // pool[]ify
- m = m_modules[MODULETYPE_CLIENT_UPDATE - FIRST_DRAWABLE_MODULE_TYPE];
- for (modIdx = 0; modIdx < cuMI.getCount(); ++modIdx)
- {
- const ModuleData* newModData = cuMI.getNthData(modIdx);
- /// @todo srj -- this is evil, we shouldn't look at the module name directly!
- if (thingTemplate->isKindOf(KINDOF_SHRUBBERY) &&
- !TheGlobalData->m_useTreeSway &&
- cuMI.getNthName(modIdx).compareNoCase("SwayClientUpdate") == 0)
- continue;
- *m++ = TheModuleFactory->newModule(this, cuMI.getNthName(modIdx), newModData, MODULETYPE_CLIENT_UPDATE);
- }
- *m = NULL;
- }
- /// allow for inter-Module resolution
- for (i = 0; i < NUM_DRAWABLE_MODULE_TYPES; ++i)
- {
- for (Module** m = m_modules[i]; m && *m; ++m)
- (*m)->onObjectCreated();
- }
-
- m_groupNumber = NULL;
- m_captionDisplayString = NULL;
- m_drawableInfo.m_drawable = this;
- m_drawableInfo.m_ghostObject = NULL;
- m_iconInfo = NULL; // lazily allocate!
- m_selectionFlashEnvelope = NULL; // lazily allocate!
- m_colorTintEnvelope = NULL; // lazily allocate!
- initStaticImages();
- startAmbientSound();
- } // end Drawable
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- Drawable::~Drawable()
- {
- Int i;
- if( m_constructDisplayString )
- TheDisplayStringManager->freeDisplayString( m_constructDisplayString );
- m_constructDisplayString = NULL;
- if ( m_captionDisplayString )
- TheDisplayStringManager->freeDisplayString( m_captionDisplayString );
- m_captionDisplayString = NULL;
- m_groupNumber = NULL;
- // delete any modules callbacks
- for (i = 0; i < NUM_DRAWABLE_MODULE_TYPES; ++i)
- {
- for (Module** m = m_modules[i]; m && *m; ++m)
- {
- (*m)->deleteInstance();
- *m = NULL; // in case other modules call findModule from their dtor!
- }
- delete [] m_modules[i];
- m_modules[i] = NULL;
- }
- stopAmbientSound();
- if (m_ambientSound)
- {
- m_ambientSound->deleteInstance();
- m_ambientSound = NULL;
- }
- /// @todo this is nasty, we need a real general effects system
- // remove any entries that might be present from the ray effect system
- TheGameClient->removeFromRayEffects( this );
- // reset object to NULL so we never mistaken grab "dead" objects
- m_object = NULL;
- m_particle = NULL;
- // delete any icons present
- if (m_iconInfo)
- m_iconInfo->deleteInstance();
- if (m_selectionFlashEnvelope)
- m_selectionFlashEnvelope->deleteInstance();
- if (m_colorTintEnvelope)
- m_colorTintEnvelope->deleteInstance();
- if (m_locoInfo)
- {
- m_locoInfo->deleteInstance();
- m_locoInfo = NULL;
- }
- }
- //-------------------------------------------------------------------------------------------------
- /** Run from GameClient::destroyDrawable */
- //-------------------------------------------------------------------------------------------------
- void Drawable::onDestroy( void )
- {
- //
- // run the onDelete on all modules present so they each have an opportunity to cleanup
- // anything they need to ... including talking to any other modules
- //
- for( Int i = 0; i < NUM_DRAWABLE_MODULE_TYPES; i++ )
- {
- for( Module** m = m_modules[ i ]; m && *m; ++m )
- (*m)->onDelete();
- } // end for i
- } // end onDestroy
- //-------------------------------------------------------------------------------------------------
- Bool Drawable::isVisible()
- {
- for (DrawModule** dm = getDrawModules(); *dm; ++dm)
- {
- if ((*dm)->isVisible())
- {
- return TRUE;
- }
- }
- return FALSE;
- }
- //-------------------------------------------------------------------------------------------------
- Bool Drawable::getShouldAnimate( Bool considerPower ) const
- {
- const Object *obj = getObject();
- if (obj)
- {
- if (considerPower && obj->testScriptStatusBit(OBJECT_STATUS_SCRIPT_UNPOWERED))
- return FALSE;
- if (obj->isDisabled())
- {
- if( obj->isDisabledByType( DISABLED_HACKED )
- || obj->isDisabledByType( DISABLED_PARALYZED )
- || obj->isDisabledByType( DISABLED_EMP )
- // srj sez: unmanned things also should not animate. (eg, gattling tanks,
- // which have a slight barrel animation even when at rest). if this causes
- // a problem, we will need to fix gattling tanks in another way.
- || obj->isDisabledByType( DISABLED_UNMANNED )
- )
- return FALSE;
- if (considerPower && obj->isDisabledByType(DISABLED_UNDERPOWERED))
- {
- // We only pause animations if this draw module says so
- // By checking for the others first, we prevent underpower from allowing a True on an addition disable type
- return FALSE;
- }
- }
- }
- return TRUE;
- }
- //-------------------------------------------------------------------------------------------------
- // this method must ONLY be called from the client, NEVER From the logic, not even indirectly.
- Bool Drawable::clientOnly_getFirstRenderObjInfo(Coord3D* pos, Real* boundingSphereRadius, Matrix3D* transform)
- {
- DrawModule** dm = getDrawModules();
- const ObjectDrawInterface* di = (dm && *dm) ? (*dm)->getObjectDrawInterface() : NULL;
- if (di)
- {
- return di->clientOnly_getRenderObjInfo(pos, boundingSphereRadius, transform);
- }
- return false;
- }
- //-------------------------------------------------------------------------------------------------
- Bool Drawable::getProjectileLaunchOffset(WeaponSlotType wslot, Int specificBarrelToUse, Matrix3D* launchPos, WhichTurretType tur, Coord3D* turretRotPos, Coord3D* turretPitchPos) const
- {
- for (const DrawModule** dm = getDrawModules(); *dm; ++dm)
- {
- const ObjectDrawInterface* di = (*dm)->getObjectDrawInterface();
- if (di && di->getProjectileLaunchOffset(m_conditionState, wslot, specificBarrelToUse, launchPos, tur, turretRotPos, turretPitchPos))
- return true;
- }
- return false;
- }
- //-------------------------------------------------------------------------------------------------
- void Drawable::setAnimationLoopDuration(UnsignedInt numFrames)
- {
- for (DrawModule** dm = getDrawModules(); *dm; ++dm)
- {
- ObjectDrawInterface* di = (*dm)->getObjectDrawInterface();
- if (di)
- di->setAnimationLoopDuration(numFrames);
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Drawable::setAnimationCompletionTime(UnsignedInt numFrames)
- {
- for (DrawModule** dm = getDrawModules(); *dm; ++dm)
- {
- ObjectDrawInterface* di = (*dm)->getObjectDrawInterface();
- if (di)
- di->setAnimationCompletionTime(numFrames);
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Drawable::updateSubObjects()
- {
- for (DrawModule** dm = getDrawModules(); *dm; ++dm)
- {
- ObjectDrawInterface* di = (*dm)->getObjectDrawInterface();
- if (di)
- di->updateSubObjects();
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Drawable::showSubObject( const AsciiString& name, Bool show )
- {
- for (DrawModule** dm = getDrawModules(); *dm; ++dm)
- {
- ObjectDrawInterface* di = (*dm)->getObjectDrawInterface();
- if (di)
- {
- di->showSubObject( name, show );
- }
- }
- }
- #ifdef ALLOW_ANIM_INQUIRIES
- // srj sez: not sure if this is a good idea, for net sync reasons...
- //-------------------------------------------------------------------------------------------------
- /**
- This call asks, "In the current animation (if any) how far along are you, from 0.0f to 1.0f".
- */
- Real Drawable::getAnimationScrubScalar( void ) const // lorenzen
- {
- for (const DrawModule** dm = getDrawModules(); *dm; ++dm)
- {
- const ObjectDrawInterface* di = (*dm)->getObjectDrawInterface();
- if (di)
- {
- return di->getAnimationScrubScalar();
- }
- }
-
- return 0.0f;
- }
- #endif
- //-------------------------------------------------------------------------------------------------
- Int Drawable::getPristineBonePositions(const char* boneNamePrefix, Int startIndex, Coord3D* positions, Matrix3D* transforms, Int maxBones) const
- {
- Int count = 0;
- for (const DrawModule** dm = getDrawModules(); *dm; ++dm)
- {
- if (maxBones <= 0)
- break;
- const ObjectDrawInterface* di = (*dm)->getObjectDrawInterface();
- if (di)
- {
- Int subcount =
- di->getPristineBonePositionsForConditionState(m_conditionState, boneNamePrefix, startIndex, positions, transforms, maxBones);
- if (subcount > 0)
- {
- count += subcount;
- if (positions)
- positions += subcount;
- if (transforms)
- transforms += subcount;
- maxBones -= subcount;
- }
- }
- }
- return count;
- }
- //-------------------------------------------------------------------------------------------------
- Int Drawable::getCurrentClientBonePositions(const char* boneNamePrefix, Int startIndex, Coord3D* positions, Matrix3D* transforms, Int maxBones) const
- {
- Int count = 0;
- for (const DrawModule** dm = getDrawModules(); *dm; ++dm)
- {
- if (maxBones <= 0)
- break;
- const ObjectDrawInterface* di = (*dm)->getObjectDrawInterface();
- if (di)
- {
- Int subcount =
- di->getCurrentBonePositions(boneNamePrefix, startIndex, positions, transforms, maxBones);
- if (subcount > 0)
- {
- count += subcount;
- if (positions)
- positions += subcount;
- if (transforms)
- transforms += subcount;
- maxBones -= subcount;
- }
- }
- }
- return count;
- }
- //-------------------------------------------------------------------------------------------------
- Bool Drawable::getCurrentWorldspaceClientBonePositions(const char* boneName, Matrix3D& transform) const
- {
- for (const DrawModule** dm = getDrawModules(); *dm; ++dm)
- {
- const ObjectDrawInterface* di = (*dm)->getObjectDrawInterface();
- if (di && di->getCurrentWorldspaceClientBonePositions(boneName, transform))
- return true;
- }
- return false;
- }
- //-------------------------------------------------------------------------------------------------
- /** Attach to a particle system */
- //-------------------------------------------------------------------------------------------------
- void Drawable::attachToParticleSystem( Particle *p )
- {
- m_particle = p;
- }
- //-------------------------------------------------------------------------------------------------
- /** Detach from a particle system, if attached */
- //-------------------------------------------------------------------------------------------------
- void Drawable::detachFromParticleSystem( void )
- {
- if (m_particle)
- {
- m_particle->detachDrawable();
- m_particle = NULL;
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Drawable::setTerrainDecal(TerrainDecalType type)
- {
- if (m_terrainDecalType == type)
- return;
- m_terrainDecalType=type;
- DrawModule** dm = getDrawModules();
- //Only the first draw module gets a decal to prevent stacking.
- //Should be okay as long as we keep the primary object in the
- //first module.
- if (*dm)
- (*dm)->setTerrainDecal(type);
- }
- //-------------------------------------------------------------------------------------------------
- void Drawable::setTerrainDecalSize(Real x, Real y)
- {
- DrawModule** dm = getDrawModules();
- if (*dm)
- (*dm)->setTerrainDecalSize(x,y);
- }
- //-------------------------------------------------------------------------------------------------
- void Drawable::setTerrainDecalFadeTarget(Real target, Real rate)
- {
- if (m_decalOpacityFadeTarget != target)
- {
- m_decalOpacityFadeTarget = target;
- m_decalOpacityFadeRate = rate;
- }
- //else
- // m_decalOpacityFadeRate = 0;
- }
- //-------------------------------------------------------------------------------------------------
- void Drawable::setShadowsEnabled(Bool enable)
- {
- // set status bit
- if( enable )
- setDrawableStatus( DRAWABLE_STATUS_SHADOWS );
- else
- clearDrawableStatus( DRAWABLE_STATUS_SHADOWS );
- for (DrawModule** dm = getDrawModules(); *dm; ++dm)
- {
- (*dm)->setShadowsEnabled(enable);
- }
- }
- //-------------------------------------------------------------------------------------------------
- /**frees all shadow resources used by this module - used by Options screen.*/
- void Drawable::releaseShadows(void)
- {
- for (DrawModule** dm = getDrawModules(); *dm; ++dm)
- {
- (*dm)->releaseShadows();
- }
- }
- //-------------------------------------------------------------------------------------------------
- /**create shadow resources if not already present. Used by Options screen.*/
- void Drawable::allocateShadows(void)
- {
- for (DrawModule** dm = getDrawModules(); *dm; ++dm)
- {
- (*dm)->allocateShadows();
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Drawable::setFullyObscuredByShroud(Bool fullyObscured)
- {
- if (m_drawableFullyObscuredByShroud != fullyObscured)
- {
- for (DrawModule** dm = getDrawModules(); *dm; ++dm)
- {
- (*dm)->setFullyObscuredByShroud(fullyObscured);
- }
- m_drawableFullyObscuredByShroud = fullyObscured;
- }
- }
- //-------------------------------------------------------------------------------------------------
- /** Set drawable's "selected" status, if not already set. Also update running
- * total count of selected drawables. */
- //-------------------------------------------------------------------------------------------------
- void Drawable::friend_setSelected( void )
- {
- if(isSelected() == false)
- {
- m_selected = TRUE;
- onSelected();
- }
- }
- //-------------------------------------------------------------------------------------------------
- /** Clear drawable's "selected" status, if not already clear. Also update running
- * total count of selected drawables. */
- //-------------------------------------------------------------------------------------------------
- void Drawable::friend_clearSelected( void )
- {
- if(isSelected())
- {
- m_selected = FALSE;
- onUnselected();
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** Flash the drawable with the color */
- // ------------------------------------------------------------------------------------------------
- void Drawable::colorFlash( const RGBColor* color, UnsignedInt decayFrames, UnsignedInt attackFrames, UnsignedInt sustainAtPeak )
- {
- if (m_colorTintEnvelope == NULL)
- m_colorTintEnvelope = newInstance(TintEnvelope);
- if( color )
- {
- m_colorTintEnvelope->play( color, attackFrames, decayFrames, sustainAtPeak);
- }
- else
- {
- RGBColor white;
- white.setFromInt(0xffffffff);
- m_colorTintEnvelope->play( &white );
- }
- // make sure the tint color is unlocked so we "fade back down" to normal
- clearDrawableStatus( DRAWABLE_STATUS_TINT_COLOR_LOCKED );
- }
- // ------------------------------------------------------------------------------------------------
- /** Tint a drawable a specified color */
- // ------------------------------------------------------------------------------------------------
- void Drawable::colorTint( const RGBColor* color )
- {
- if( color )
- {
- // set the color via color flash
- colorFlash( color, 0, 0, TRUE );
- // lock the tint color so the flash never "fades back down"
- setDrawableStatus( DRAWABLE_STATUS_TINT_COLOR_LOCKED );
- }
- else
- {
- if (m_colorTintEnvelope == NULL)
- m_colorTintEnvelope = newInstance(TintEnvelope);
- // remove the tint applied to the object
- m_colorTintEnvelope->rest();
- // set the tint as unlocked so we can flash and stuff again
- clearDrawableStatus( DRAWABLE_STATUS_TINT_COLOR_LOCKED );
- }
- }
- //-------------------------------------------------------------------------------------------------
- /** Gathering point for all things besides actual selection that must happen on selection */
- //-------------------------------------------------------------------------------------------------
- void Drawable::onSelected()
- {
- flashAsSelected();//much simpler
-
- Object* obj = getObject();
- if ( obj )
- {
- ContainModuleInterface* contain = obj->getContain();
- if ( contain )
- {
- contain->clientVisibleContainedFlashAsSelected();
- }
- }
- } // end onSelected
- //-------------------------------------------------------------------------------------------------
- /** Gathering point for all things besides actual selection that must happen on deselection */
- //-------------------------------------------------------------------------------------------------
- void Drawable::onUnselected()
- {
- // nothing
- }
- //-------------------------------------------------------------------------------------------------
- /** get FX color value to add to ALL LIGHTS when drawing */
- //-------------------------------------------------------------------------------------------------
- const Vector3 * Drawable::getTintColor( void ) const
- {
- if ( m_colorTintEnvelope )
- {
- if (m_colorTintEnvelope->isEffective())
- {
- return m_colorTintEnvelope->getColor();
- }
- }
- return NULL;
- }
- //-------------------------------------------------------------------------------------------------
- /** get SELECTION color value to add to ALL LIGHTS when drawing */
- //-------------------------------------------------------------------------------------------------
- const Vector3 * Drawable::getSelectionColor( void ) const
- {
- if (m_selectionFlashEnvelope)
- {
- if (m_selectionFlashEnvelope->isEffective())
- {
- return m_selectionFlashEnvelope->getColor();
- }
- }
- return NULL;
- }
-
- //-------------------------------------------------------------------------------------------------
- /** fades the object out gradually...how gradually is determined by number of frames */
- //-------------------------------------------------------------------------------------------------
- void Drawable::fadeOut( UnsignedInt frames ) ///< cloak object
- {
- setDrawableOpacity(1.0);
- m_fadeMode = FADING_OUT;
- m_timeToFade = frames;
- m_timeElapsedFade = 0;
- }
- //-------------------------------------------------------------------------------------------------
- /** fades the object in gradually...how gradually is determined by number of frames */
- //-------------------------------------------------------------------------------------------------
- void Drawable::fadeIn( UnsignedInt frames ) ///< decloak object
- {
- setDrawableOpacity(0.0);
- m_fadeMode = FADING_IN;
- m_timeToFade = frames;
- m_timeElapsedFade = 0;
- }
- //-------------------------------------------------------------------------------------------------
- const Real Drawable::getScale (void) const
- {
- return m_instanceScale;
- // return getTemplate()->getAssetScale();
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void Drawable::reactToBodyDamageStateChange(BodyDamageType newState)
- {
- static const ModelConditionFlagType TheDamageMap[BODYDAMAGETYPE_COUNT] =
- {
- MODELCONDITION_INVALID,
- MODELCONDITION_DAMAGED,
- MODELCONDITION_REALLY_DAMAGED,
- MODELCONDITION_RUBBLE,
- };
- ModelConditionFlags newDamage;
- if (TheDamageMap[newState] != MODELCONDITION_INVALID)
- newDamage.set(TheDamageMap[newState]);
- clearAndSetModelConditionFlags(
- MAKE_MODELCONDITION_MASK3(MODELCONDITION_DAMAGED, MODELCONDITION_REALLY_DAMAGED, MODELCONDITION_RUBBLE),
- newDamage);
- startAmbientSound(newState, TheGlobalData->m_timeOfDay);
- }
- //-------------------------------------------------------------------------------------------------
- void Drawable::setEffectiveOpacity( Real pulseFactor, Real explicitOpacity /* = -1.0f */)
- {
- if( explicitOpacity != -1.0f )
- {
- m_stealthOpacity = MIN( 1.0f, MAX( 0.0f, explicitOpacity ) );
- }
- Real pf = MIN(1.0f, MAX(0.0f, pulseFactor));
- Real pulseMargin = (1.0f - m_stealthOpacity);
- Real pulseAmount = pulseMargin * pf;
- m_effectiveStealthOpacity = m_stealthOpacity + pulseAmount;
- } ///< get alpha/opacity value used to override defaults when drawing.
- //-------------------------------------------------------------------------------------------------
- /** update is called once per frame */
- //-------------------------------------------------------------------------------------------------
- //DECLARE_PERF_TIMER(updateDrawable)
- void Drawable::updateDrawable( void )
- {
- //USE_PERF_TIMER(updateDrawable)
- UnsignedInt now = TheGameLogic->getFrame();
- Object *obj = getObject();
- {
- for (ClientUpdateModule** cu = getClientUpdateModules(); cu && *cu; ++cu)
- {
- (*cu)->clientUpdate();
- }
- }
- {
- // handle fading in or out
- if (m_fadeMode != FADING_NONE)
- {
- Real numer = (m_fadeMode == FADING_IN) ? (m_timeElapsedFade) : (m_timeToFade-m_timeElapsedFade);
- setDrawableOpacity(numer/(Real)m_timeToFade);
- ++m_timeElapsedFade;
-
- if (m_timeElapsedFade > m_timeToFade)
- m_fadeMode = FADING_NONE;
- }
- }
- if ( getTerrainDecalType() != TERRAIN_DECAL_NONE )
- {
- DrawModule** dm = getDrawModules();
- if (*dm)
- {
- if (m_decalOpacityFadeRate != 0)
- {
- //LERP
- (*dm)->setTerrainDecalOpacity(m_decalOpacity);
- m_decalOpacity += m_decalOpacityFadeRate;
- }
- //---------------
- if (m_decalOpacityFadeRate < 0 && m_decalOpacity <= 0 )
- {
- m_decalOpacityFadeRate = 0.0f;
- m_decalOpacity = 0.0f;
- this->setTerrainDecal(TERRAIN_DECAL_NONE);
- }
- else if (m_decalOpacityFadeRate > 0 && m_decalOpacity >= 1.0f)
- {
- m_decalOpacity = 1.0f;
- m_decalOpacityFadeRate = 0.0f;
- (*dm)->setTerrainDecalOpacity(m_decalOpacity);
- }
- }//end if (*dm)
- }
- else
- m_decalOpacity = 0;
- {
- if (m_expirationDate != 0 && now >= m_expirationDate)
- {
- DEBUG_ASSERTCRASH(obj == NULL, ("Drawables with Objects should not have expiration dates!"));
- TheGameClient->destroyDrawable(this);
- return;
- }
- }
- {
- if (m_flashCount > 0 && (TheGameClient->getFrame() % DRAWABLE_FRAMES_PER_FLASH) == 0)
- {
- RGBColor tmp;
- tmp.setFromInt(m_flashColor);
- colorFlash(&tmp);
- m_flashCount--;
- }
- }
- //Lets figure out whether we should be changing colors right about now
- // we'll use an ifelseif ladder since we are scanning bits
- if( m_prevTintStatus != m_tintStatus )// edge test
- {
- if ( testTintStatus( TINT_STATUS_DISABLED ) )
- {
- if (m_colorTintEnvelope == NULL)
- m_colorTintEnvelope = newInstance(TintEnvelope);
- m_colorTintEnvelope->play( &DARK_GRAY_DISABLED_COLOR, 30, 30, SUSTAIN_INDEFINITELY);
- }
- // else if ( testTintStatus( TINT_STATUS_POISONED) )
- // {
- // if (m_colorTintEnvelope == NULL)
- // m_colorTintEnvelope = newInstance(TintEnvelope);
- // m_colorTintEnvelope->play( &SICKLY_GREEN_POISONED_COLOR, 30, 30, SUSTAIN_INDEFINITELY);
- // }
- // else if ( testTintStatus( TINT_STATUS_IRRADIATED) )
- // {
- // if (m_colorTintEnvelope == NULL)
- // m_colorTintEnvelope = newInstance(TintEnvelope);
- // m_colorTintEnvelope->play( &RED_IRRADIATED_COLOR, 30, 30, SUSTAIN_INDEFINITELY);
- // }
- else
- {
- // NO TINTING SHOULD BE PRESENT
- if (m_colorTintEnvelope == NULL)
- m_colorTintEnvelope = newInstance(TintEnvelope);
- m_colorTintEnvelope->release(); // head on back to normal, now
- }
- }
- m_prevTintStatus = m_tintStatus;//for next frame
- if ( obj )
- {
- if ( ! obj->isEffectivelyDead() )
- clearTintStatus( TINT_STATUS_IRRADIATED); // so the res glow stops when not exposed
- }
- if (m_colorTintEnvelope)
- m_colorTintEnvelope->update(); // defector fx, disable fx, etc...
- if (m_selectionFlashEnvelope)
- m_selectionFlashEnvelope->update(); // selection flashing
- //If we have an ambient sound, and we aren't currently playing it, attempt to play it now
- if( m_ambientSound && m_ambientSoundEnabled && !m_ambientSound->m_event.getEventName().isEmpty() && !m_ambientSound->m_event.isCurrentlyPlaying() )
- {
- startAmbientSound();
- }
- }
-
- //-------------------------------------------------------------------------------------------------
- void Drawable::flashAsSelected( const RGBColor *color ) ///< drawable takes care of the details if you spec no color
- {
- if (m_selectionFlashEnvelope == NULL)
- m_selectionFlashEnvelope = newInstance(TintEnvelope);
- if ( color )
- {
- m_selectionFlashEnvelope->play( color, 0, 4 );
- }
- else
- {
- Object *obj = getObject();
- if (obj)
- {
- RGBColor tempColor;
- if (TheGlobalData->m_selectionFlashHouseColor)
- tempColor.setFromInt(obj->getIndicatorColor());
- else
- tempColor.setFromInt(0xffffffff);//white
- Real saturation = TheGlobalData->m_selectionFlashSaturationFactor;
- saturateRGB( tempColor, saturation );
- m_selectionFlashEnvelope->play( &tempColor, 0, 4 );
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Drawable::applyPhysicsXform(Matrix3D* mtx)
- {
- const Object *obj = getObject();
- if( !obj || obj->isDisabledByType( DISABLED_HELD ) || !TheGlobalData->m_showClientPhysics )
- {
- return;
- }
- Bool frozen = TheTacticalView->isTimeFrozen() && !TheTacticalView->isCameraMovementFinished();
- frozen = frozen || TheScriptEngine->isTimeFrozenDebug() || TheScriptEngine->isTimeFrozenScript();
- if (frozen)
- return;
- PhysicsXformInfo info;
- if (calcPhysicsXform(info))
- {
- mtx->Translate(0.0f, 0.0f, info.m_totalZ);
- mtx->Rotate_Y( info.m_totalPitch );
- mtx->Rotate_X( -info.m_totalRoll );
- mtx->Rotate_Z( info.m_totalYaw );
- }
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- Bool Drawable::calcPhysicsXform(PhysicsXformInfo& info)
- {
- const Object* obj = getObject();
- const AIUpdateInterface *ai = obj ? obj->getAIUpdateInterface() : NULL;
- Bool hasPhysicsXform = false;
- if (ai)
- {
- const Locomotor *locomotor = ai->getCurLocomotor();
- if (locomotor)
- {
- switch (locomotor->getAppearance())
- {
- case LOCO_WHEELS_FOUR:
- calcPhysicsXformWheels(locomotor, info);
- hasPhysicsXform = true;
- break;
- case LOCO_TREADS:
- calcPhysicsXformTreads(locomotor, info);
- hasPhysicsXform = true;
- break;
- case LOCO_HOVER:
- case LOCO_WINGS:
- calcPhysicsXformHoverOrWings(locomotor, info);
- hasPhysicsXform = true;
- break;
- case LOCO_THRUST:
- calcPhysicsXformThrust(locomotor, info);
- hasPhysicsXform = true;
- break;
- }
- }
- }
- if (hasPhysicsXform)
- {
- // HOTFIX: Ensure that we are not passing denormalized values back to caller
- // @todo remove hotfix
- if (info.m_totalPitch>-1e-20f&&info.m_totalPitch<1e-20f)
- info.m_totalPitch=0.f;
- if (info.m_totalRoll>-1e-20f&&info.m_totalRoll<1e-20f)
- info.m_totalRoll=0.f;
- if (info.m_totalYaw>-1e-20f&&info.m_totalYaw<1e-20f)
- info.m_totalYaw=0.f;
- if (info.m_totalZ>-1e-20f&&info.m_totalZ<1e-20f)
- info.m_totalZ=0.f;
- }
- return hasPhysicsXform;
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void Drawable::calcPhysicsXformThrust( const Locomotor *locomotor, PhysicsXformInfo& info )
- {
- if (m_locoInfo == NULL)
- m_locoInfo = newInstance(DrawableLocoInfo);
- Real THRUST_ROLL = locomotor->getThrustRoll();
- Real WOBBLE_RATE = locomotor->getWobbleRate();
- Real MAX_WOBBLE = locomotor->getMaxWobble();
- Real MIN_WOBBLE = locomotor->getMinWobble();
- //
- // this is a kind of quick thrust implementation cause we need scud missiles to wobble *now*,
- // we deal with just adjusting pitch, yaw, and roll just a little bit
- //
- if( WOBBLE_RATE )
- {
- if( m_locoInfo->m_wobble >= 1.0f )
- {
- if( m_locoInfo->m_pitch < MAX_WOBBLE - WOBBLE_RATE * 2 )
- {
- m_locoInfo->m_pitch += WOBBLE_RATE;
- m_locoInfo->m_yaw += WOBBLE_RATE;
- } // end if
- else
- {
- m_locoInfo->m_pitch += (WOBBLE_RATE / 2.0f);
- m_locoInfo->m_yaw += (WOBBLE_RATE / 2.0f);
- } // end else
- if( m_locoInfo->m_pitch >= MAX_WOBBLE )
- m_locoInfo->m_wobble = -1.0f;
- } // end if
- else
- {
- if( m_locoInfo->m_pitch >= MIN_WOBBLE + WOBBLE_RATE * 2.0f )
- {
- m_locoInfo->m_pitch -= WOBBLE_RATE;
- m_locoInfo->m_yaw -= WOBBLE_RATE;
- } // end if
- else
- {
- m_locoInfo->m_pitch -= (WOBBLE_RATE / 2.0f);
- m_locoInfo->m_yaw -= (WOBBLE_RATE / 2.0f);
- } // end else
- if( m_locoInfo->m_pitch <= MIN_WOBBLE )
- m_locoInfo->m_wobble = 1.0f;
- } // end else
- info.m_totalPitch = m_locoInfo->m_pitch;
- info.m_totalYaw = m_locoInfo->m_yaw;
- } // end if, wobble exists
- if( THRUST_ROLL )
- {
- m_locoInfo->m_roll += THRUST_ROLL;
- info.m_totalRoll = m_locoInfo->m_roll;
- } // end if
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void Drawable::calcPhysicsXformHoverOrWings( const Locomotor *locomotor, PhysicsXformInfo& info )
- {
- if (m_locoInfo == NULL)
- m_locoInfo = newInstance(DrawableLocoInfo);
- const Real ACCEL_PITCH_LIMIT = locomotor->getAccelPitchLimit();
- const Real PITCH_STIFFNESS = locomotor->getPitchStiffness();
- const Real ROLL_STIFFNESS = locomotor->getRollStiffness();
- const Real PITCH_DAMPING = locomotor->getPitchDamping();
- const Real ROLL_DAMPING = locomotor->getRollDamping();
- const Real Z_VEL_PITCH_COEFF = locomotor->getPitchByZVelCoef();
- const Real FORWARD_VEL_COEFF = locomotor->getForwardVelCoef();
- const Real LATERAL_VEL_COEFF = locomotor->getLateralVelCoef();
- const Real FORWARD_ACCEL_COEFF = locomotor->getForwardAccelCoef();
- const Real LATERAL_ACCEL_COEFF = locomotor->getLateralAccelCoef();
- const Real UNIFORM_AXIAL_DAMPING = locomotor->getUniformAxialDamping();
- // get object from logic
- Object *obj = getObject();
- if (obj == NULL)
- return;
- AIUpdateInterface *ai = obj->getAIUpdateInterface();
- if (ai == NULL)
- return;
- // get object physics state
- PhysicsBehavior *physics = obj->getPhysics();
- if (physics == NULL)
- return;
- // get our position and direction vector
- //const Coord3D *pos = getPosition();
- const Coord3D* dir = getUnitDirectionVector2D();
- const Coord3D* accel = physics->getAcceleration();
- const Coord3D* vel = physics->getVelocity();
- m_locoInfo->m_pitchRate += ((-PITCH_STIFFNESS * m_locoInfo->m_pitch) + (-PITCH_DAMPING * m_locoInfo->m_pitchRate)); // spring/damper
- m_locoInfo->m_rollRate += ((-ROLL_STIFFNESS * m_locoInfo->m_roll) + (-ROLL_DAMPING * m_locoInfo->m_rollRate)); // spring/damper
- m_locoInfo->m_pitch += m_locoInfo->m_pitchRate * UNIFORM_AXIAL_DAMPING;
- m_locoInfo->m_roll += m_locoInfo->m_rollRate * UNIFORM_AXIAL_DAMPING;
- // process chassis acceleration dynamics - damp back towards zero
- m_locoInfo->m_accelerationPitchRate += ((-PITCH_STIFFNESS * (m_locoInfo->m_accelerationPitch)) + (-PITCH_DAMPING * m_locoInfo->m_accelerationPitchRate)); // spring/damper
- m_locoInfo->m_accelerationPitch += m_locoInfo->m_accelerationPitchRate;
- m_locoInfo->m_accelerationRollRate += ((-ROLL_STIFFNESS * m_locoInfo->m_accelerationRoll) + (-ROLL_DAMPING * m_locoInfo->m_accelerationRollRate)); // spring/damper
- m_locoInfo->m_accelerationRoll += m_locoInfo->m_accelerationRollRate;
- // compute total pitch and roll of tank
- info.m_totalPitch = m_locoInfo->m_pitch + m_locoInfo->m_accelerationPitch;
- info.m_totalRoll = m_locoInfo->m_roll + m_locoInfo->m_accelerationRoll;
- if (physics->isMotive())
- {
- if (Z_VEL_PITCH_COEFF != 0.0f)
- {
- const Real TINY_DZ = 0.001f;
- if (fabs(vel->z) > TINY_DZ)
- {
- Real pitch = atan2(vel->z, sqrt(sqr(vel->x)+sqr(vel->y)));
- m_locoInfo->m_pitch -= Z_VEL_PITCH_COEFF * pitch;
- }
- }
- // cause the chassis to pitch & roll in reaction to current speed
- Real forwardVel = dir->x * vel->x + dir->y * vel->y;
- m_locoInfo->m_pitch += -(FORWARD_VEL_COEFF * forwardVel);
- Real lateralVel = -dir->y * vel->x + dir->x * vel->y;
- m_locoInfo->m_roll += -(LATERAL_VEL_COEFF * lateralVel);
- // cause the chassis to pitch & roll in reaction to acceleration/deceleration
- Real forwardAccel = dir->x * accel->x + dir->y * accel->y;
- m_locoInfo->m_accelerationPitchRate += -(FORWARD_ACCEL_COEFF * forwardAccel);
- Real lateralAccel = -dir->y * accel->x + dir->x * accel->y;
- m_locoInfo->m_accelerationRollRate += -(LATERAL_ACCEL_COEFF * lateralAccel);
- }
- // limit acceleration pitch and roll
- if (m_locoInfo->m_accelerationPitch > ACCEL_PITCH_LIMIT)
- m_locoInfo->m_accelerationPitch = ACCEL_PITCH_LIMIT;
- else if (m_locoInfo->m_accelerationPitch < -ACCEL_PITCH_LIMIT)
- m_locoInfo->m_accelerationPitch = -ACCEL_PITCH_LIMIT;
- if (m_locoInfo->m_accelerationRoll > ACCEL_PITCH_LIMIT)
- m_locoInfo->m_accelerationRoll = ACCEL_PITCH_LIMIT;
- else if (m_locoInfo->m_accelerationRoll < -ACCEL_PITCH_LIMIT)
- m_locoInfo->m_accelerationRoll = -ACCEL_PITCH_LIMIT;
- info.m_totalZ = 0.0f;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void Drawable::calcPhysicsXformTreads( const Locomotor *locomotor, PhysicsXformInfo& info )
- {
- if (m_locoInfo == NULL)
- m_locoInfo = newInstance(DrawableLocoInfo);
- const Real OVERLAP_SHRINK_FACTOR = 0.8f;
- const Real FLATTENED_OBJECT_HEIGHT = 0.5f;
- const Real LEAVE_OVERLAP_PITCH_KICK = PI/128;
- const Real OVERLAP_ROUGH_VIBRATION_FACTOR = 5.0f;
- const Real MAX_ROUGH_VIBRATION = 0.5f;
- const Real ACCEL_PITCH_LIMIT = locomotor->getAccelPitchLimit();
- const Real PITCH_STIFFNESS = locomotor->getPitchStiffness();
- const Real ROLL_STIFFNESS = locomotor->getRollStiffness();
- const Real PITCH_DAMPING = locomotor->getPitchDamping();
- const Real ROLL_DAMPING = locomotor->getRollDamping();
- const Real FORWARD_ACCEL_COEFF = locomotor->getForwardAccelCoef();
- const Real LATERAL_ACCEL_COEFF = locomotor->getLateralAccelCoef();
- const Real UNIFORM_AXIAL_DAMPING = locomotor->getUniformAxialDamping();
- // get object from logic
- Object *obj = getObject();
- if (obj == NULL)
- return;
- AIUpdateInterface *ai = obj->getAIUpdateInterface();
- if (ai == NULL)
- return ;
- // get object physics state
- PhysicsBehavior *physics = obj->getPhysics();
- if (physics == NULL)
- return;
- // get our position and direction vector
- const Coord3D *pos = getPosition();
- const Coord3D *dir = getUnitDirectionVector2D();
- const Coord3D *accel = physics->getAcceleration();
- const Coord3D *vel = physics->getVelocity();
- // compute perpendicular (2d)
- Coord3D perp;
- perp.x = -dir->y;
- perp.y = dir->x;
- perp.z = 0.0f;
- // find pitch and roll of terrain under chassis
- Coord3D normal;
- /* Real hheight = */ TheTerrainLogic->getLayerHeight( pos->x, pos->y, obj->getLayer(), &normal );
- // override surface normal if we are overlapping another object - crushing it
- Real overlapZ = 0.0f;
- // get object we are currently overlapping, if any
- Object* overlapped = TheGameLogic->findObjectByID(physics->getCurrentOverlap());
- if (overlapped && overlapped->isKindOf(KINDOF_SHRUBBERY)) {
- overlapped = NULL; // We just smash through shrubbery. jba.
- }
- if (overlapped)
- {
- const Coord3D *overPos = overlapped->getPosition();
- Real dx = overPos->x - pos->x;
- Real dy = overPos->y - pos->y;
- Real centerDistSqr = sqr(dx) + sqr(dy);
- // compute maximum distance between objects, if their edges just touched
- Real ourSize = getDrawableGeometryInfo().getBoundingCircleRadius();
- Real otherSize = overlapped->getGeometryInfo().getBoundingCircleRadius();
- Real maxCenterDist = otherSize + ourSize;
- // shrink the overlap distance a bit to avoid floating
- maxCenterDist *= OVERLAP_SHRINK_FACTOR;
- if (centerDistSqr < sqr(maxCenterDist))
- {
- Real centerDist = sqrtf(centerDistSqr);
- Real amount = 1.0f - centerDist/maxCenterDist;
- if (amount < 0.0f)
- amount = 0.0f;
- else if (amount > 1.0f)
- amount = 1.0f;
- // rough vibrations proportional to speed when we drive over something
- Real rough = (vel->x*vel->x + vel->y*vel->y) * OVERLAP_ROUGH_VIBRATION_FACTOR;
- if (rough > MAX_ROUGH_VIBRATION)
- rough = MAX_ROUGH_VIBRATION;
-
- Real height = overlapped->getGeometryInfo().getMaxHeightAbovePosition();
- // do not "go up" flattened crushed things
- Bool flat = false;
- if (overlapped->isKindOf(KINDOF_LOW_OVERLAPPABLE) ||
- overlapped->isKindOf(KINDOF_INFANTRY) ||
- (overlapped->getBodyModule()->getFrontCrushed() && overlapped->getBodyModule()->getBackCrushed()))
- {
- flat = true;
- height = FLATTENED_OBJECT_HEIGHT;
- }
- if (amount < FLATTENED_OBJECT_HEIGHT && flat == false)
- {
- overlapZ = height * 2.0f * amount;
- // compute vector along "surface"
- // not proportional to actual geometry to avoid overlay steep inclines, etc
- Coord3D v;
- v.x = dx/centerDist;
- v.y = dy/centerDist;
- v.z = 0.2f; // 0.25
- Coord3D up;
- up.x = GameClientRandomValueReal( -rough, rough );
- up.y = GameClientRandomValueReal( -rough, rough );
- up.z = 1.0f;
- up.normalize();
- Coord3D prp;
- prp.crossProduct( &v, &up, &prp );
- normal.crossProduct( &prp, &v, &normal );
- // compute unit normal
- normal.normalize();
- }
- else
- {
- // sitting on top of object
- overlapZ = height;
- normal.x = GameClientRandomValueReal( -rough, rough );
- normal.y = GameClientRandomValueReal( -rough, rough );
- normal.z = 1.0f;
- normal.normalize();
- }
- }
- }
- else // no overlap this frame
- {
- // if we had an overlap last frame, and we're now in the air, give a
- // kick to the pitch for effect
- if (physics->getPreviousOverlap() != INVALID_ID && m_locoInfo->m_overlapZ > 0.0f)
- m_locoInfo->m_pitchRate += LEAVE_OVERLAP_PITCH_KICK;
- }
- Real dot = normal.x * dir->x + normal.y * dir->y;
- Real groundPitch = dot * (PI/2.0f);
- dot = normal.x * perp.x + normal.y * perp.y;
- Real groundRoll = dot * (PI/2.0f);
- // process chassis suspension dynamics - damp back towards groundPitch
- // the ground can only push back if we're touching it
- if (overlapped || m_locoInfo->m_overlapZ <= 0.0f)
- {
- m_locoInfo->m_pitchRate += ((-PITCH_STIFFNESS * (m_locoInfo->m_pitch - groundPitch)) + (-PITCH_DAMPING * m_locoInfo->m_pitchRate)); // spring/damper
- if (m_locoInfo->m_pitchRate > 0.0f)
- m_locoInfo->m_pitchRate *= 0.5f;
- m_locoInfo->m_rollRate += ((-ROLL_STIFFNESS * (m_locoInfo->m_roll - groundRoll)) + (-ROLL_DAMPING * m_locoInfo->m_rollRate)); // spring/damper
- }
- m_locoInfo->m_pitch += m_locoInfo->m_pitchRate * UNIFORM_AXIAL_DAMPING;
- m_locoInfo->m_roll += m_locoInfo->m_rollRate * UNIFORM_AXIAL_DAMPING;
- // process chassis recoil dynamics - damp back towards zero
- m_locoInfo->m_accelerationPitchRate += ((-PITCH_STIFFNESS * (m_locoInfo->m_accelerationPitch)) + (-PITCH_DAMPING * m_locoInfo->m_accelerationPitchRate)); // spring/damper
- m_locoInfo->m_accelerationPitch += m_locoInfo->m_accelerationPitchRate;
- m_locoInfo->m_accelerationRollRate += ((-ROLL_STIFFNESS * m_locoInfo->m_accelerationRoll) + (-ROLL_DAMPING * m_locoInfo->m_accelerationRollRate)); // spring/damper
- m_locoInfo->m_accelerationRoll += m_locoInfo->m_accelerationRollRate;
- // compute total pitch and roll of tank
- info.m_totalPitch = m_locoInfo->m_pitch + m_locoInfo->m_accelerationPitch;
- info.m_totalRoll = m_locoInfo->m_roll + m_locoInfo->m_accelerationRoll;
- if (physics->isMotive())
- {
- // cause the chassis to pitch & roll in reaction to acceleration/deceleration
- Real forwardAccel = dir->x * accel->x + dir->y * accel->y;
- m_locoInfo->m_accelerationPitchRate += -(FORWARD_ACCEL_COEFF * forwardAccel);
- Real lateralAccel = -dir->y * accel->x + dir->x * accel->y;
- m_locoInfo->m_accelerationRollRate += -(LATERAL_ACCEL_COEFF * lateralAccel);
- }
- #ifdef RECOIL_FROM_BEING_DAMAGED
- // recoil from being hit
- /// @todo Recoil needs to be based on sane damage amounts (MSB)
- const DamageInfo *damageInfo = obj->getBodyModule()->getLastDamageInfo();
- if (damageInfo)
- {
- if (obj->getBodyModule()->getLastDamageTimestamp() > m_lastDamageTimestamp && damageInfo->in.m_amount > RECOIL_DAMAGE)
- {
- Object *attacker = TheGameLogic->getObject( damageInfo->in.m_sourceID );
- if (attacker)
- {
- Coord3D to;
- ThePartitionManager->getVectorTo( obj, attacker, FROM_CENTER_2D, &to );
- to.normalize();
- Real forward = dir->x * to.x + dir->y * to.y;
- Real lateral = perp.x * to.x + perp.y * to.y;
- Real recoil = PI/16.0f * GameClientRandomValueReal( 0.5f, 1.0f );
-
- m_locoInfo->m_accelerationPitchRate -= recoil * forward;
- m_locoInfo->m_accelerationRollRate -= recoil * lateral;
- }
- m_lastDamageTimestamp = obj->getBodyModule()->getLastDamageTimestamp();
- }
- }
- #endif
- // limit recoil pitch and roll
- if (m_locoInfo->m_accelerationPitch > ACCEL_PITCH_LIMIT)
- m_locoInfo->m_accelerationPitch = ACCEL_PITCH_LIMIT;
- else if (m_locoInfo->m_accelerationPitch < -ACCEL_PITCH_LIMIT)
- m_locoInfo->m_accelerationPitch = -ACCEL_PITCH_LIMIT;
- if (m_locoInfo->m_accelerationRoll > ACCEL_PITCH_LIMIT)
- m_locoInfo->m_accelerationRoll = ACCEL_PITCH_LIMIT;
- else if (m_locoInfo->m_accelerationRoll < -ACCEL_PITCH_LIMIT)
- m_locoInfo->m_accelerationRoll = -ACCEL_PITCH_LIMIT;
- // adjust z
- if (overlapZ > m_locoInfo->m_overlapZ)
- {
- m_locoInfo->m_overlapZ = overlapZ;
- /// @todo Z needs to accelerate/decelerate, not be directly set (MSB)
- // m_locoInfo->m_overlapZ += 0.4f;
- m_locoInfo->m_overlapZVel = 0.0f;
- }
-
- Real ztmp = m_locoInfo->m_overlapZ/2.0f;
- // do fake Z physics
- if (m_locoInfo->m_overlapZ > 0.0f)
- {
- m_locoInfo->m_overlapZVel -= 0.2f;
- m_locoInfo->m_overlapZ += m_locoInfo->m_overlapZVel;
- }
- if (m_locoInfo->m_overlapZ <= 0.0f)
- {
- m_locoInfo->m_overlapZ = 0.0f;
- m_locoInfo->m_overlapZVel = 0.0f;
- }
- info.m_totalZ = ztmp;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void Drawable::calcPhysicsXformWheels( const Locomotor *locomotor, PhysicsXformInfo& info )
- {
- if (m_locoInfo == NULL)
- m_locoInfo = newInstance(DrawableLocoInfo);
- const Real ACCEL_PITCH_LIMIT = locomotor->getAccelPitchLimit();
- const Real BOUNCE_ANGLE_KICK = locomotor->getBounceKick();
- const Real PITCH_STIFFNESS = locomotor->getPitchStiffness();
- const Real ROLL_STIFFNESS = locomotor->getRollStiffness();
- const Real PITCH_DAMPING = locomotor->getPitchDamping();
- const Real ROLL_DAMPING = locomotor->getRollDamping();
- const Real FORWARD_ACCEL_COEFF = locomotor->getForwardAccelCoef();
- const Real LATERAL_ACCEL_COEFF = locomotor->getLateralAccelCoef();
- const Real UNIFORM_AXIAL_DAMPING = locomotor->getUniformAxialDamping();
- const Real MAX_SUSPENSION_EXTENSION = locomotor->getMaxWheelExtension(); //-2.3f;
- // const Real MAX_SUSPENSION_COMPRESSION = locomotor->getMaxWheelCompression(); //1.4f;
- const Real WHEEL_ANGLE = locomotor->getWheelTurnAngle(); //PI/8;
- const Bool DO_WHEELS = locomotor->hasSuspension();
- // get object from logic
- Object *obj = getObject();
- if (obj == NULL)
- return;
- AIUpdateInterface *ai = obj->getAIUpdateInterface();
- if (ai == NULL)
- return ;
- // get object physics state
- PhysicsBehavior *physics = obj->getPhysics();
- if (physics == NULL)
- return ;
- // get our position and direction vector
- const Coord3D *pos = getPosition();
- const Coord3D *dir = getUnitDirectionVector2D();
- const Coord3D *accel = physics->getAcceleration();
- // compute perpendicular (2d)
- Coord3D perp;
- perp.x = -dir->y;
- perp.y = dir->x;
- perp.z = 0.0f;
- // find pitch and roll of terrain under chassis
- Coord3D normal;
- Real hheight = TheTerrainLogic->getLayerHeight( pos->x, pos->y, obj->getLayer(), &normal );
- Real dot = normal.x * dir->x + normal.y * dir->y;
- Real groundPitch = dot * (PI/2.0f);
- dot = normal.x * perp.x + normal.y * perp.y;
- Real groundRoll = dot * (PI/2.0f);
- Bool airborne = obj->isSignificantlyAboveTerrain();
- if (airborne)
- {
- if (DO_WHEELS)
- {
- // Wheels extend when airborne.
- m_locoInfo->m_wheelInfo.m_framesAirborne = 0;
- m_locoInfo->m_wheelInfo.m_framesAirborneCounter++;
- if (pos->z - hheight > -MAX_SUSPENSION_EXTENSION)
- {
- m_locoInfo->m_wheelInfo.m_rearLeftHeightOffset += (MAX_SUSPENSION_EXTENSION - m_locoInfo->m_wheelInfo.m_rearLeftHeightOffset)/2.0f;
- m_locoInfo->m_wheelInfo.m_rearRightHeightOffset += (MAX_SUSPENSION_EXTENSION - m_locoInfo->m_wheelInfo.m_rearRightHeightOffset)/2.0f;
- }
- else
- {
- m_locoInfo->m_wheelInfo.m_rearLeftHeightOffset += (0 - m_locoInfo->m_wheelInfo.m_rearLeftHeightOffset)/2.0f;
- m_locoInfo->m_wheelInfo.m_rearRightHeightOffset += (0 - m_locoInfo->m_wheelInfo.m_rearRightHeightOffset)/2.0f;
- }
- }
- // Calculate suspension info.
- Real length = obj->getGeometryInfo().getMajorRadius();
- Real width = obj->getGeometryInfo().getMinorRadius();
- Real pitchHeight = length*Sin(m_locoInfo->m_pitch + m_locoInfo->m_accelerationPitch - groundPitch);
- Real rollHeight = width*Sin(m_locoInfo->m_roll + m_locoInfo->m_accelerationRoll - groundRoll);
- info.m_totalZ = fabs(pitchHeight)/4 + fabs(rollHeight)/4;
- return; // maintain the same orientation while we fly through the air.
- }
- // Bouncy.
- Real curSpeed = physics->getVelocityMagnitude();
- #if 1
- Real maxSpeed = ai->getCurLocomotorSpeed();
- if (!airborne && curSpeed > maxSpeed/10)
- {
- Real factor = curSpeed/maxSpeed;
- if (fabs(m_locoInfo->m_pitchRate)<factor*BOUNCE_ANGLE_KICK/4 && fabs(m_locoInfo->m_rollRate)<factor*BOUNCE_ANGLE_KICK/8)
- {
- // do the bouncy.
- switch (GameClientRandomValue(0,3))
- {
- case 0:
- m_locoInfo->m_pitchRate -= BOUNCE_ANGLE_KICK*factor;
- m_locoInfo->m_rollRate -= BOUNCE_ANGLE_KICK*factor/2;
- break;
- case 1:
- m_locoInfo->m_pitchRate += BOUNCE_ANGLE_KICK*factor;
- m_locoInfo->m_rollRate -= BOUNCE_ANGLE_KICK*factor/2;
- break;
- case 2:
- m_locoInfo->m_pitchRate -= BOUNCE_ANGLE_KICK*factor;
- m_locoInfo->m_rollRate += BOUNCE_ANGLE_KICK*factor/2;
- break;
- case 3:
- m_locoInfo->m_pitchRate += BOUNCE_ANGLE_KICK*factor;
- m_locoInfo->m_rollRate += BOUNCE_ANGLE_KICK*factor/2;
- break;
- }
- }
- }
- #endif
- // process chassis suspension dynamics - damp back towards groundPitch
- // the ground can only push back if we're touching it
- if (!airborne)
- {
- m_locoInfo->m_pitchRate += ((-PITCH_STIFFNESS * (m_locoInfo->m_pitch - groundPitch)) + (-PITCH_DAMPING * m_locoInfo->m_pitchRate)); // spring/damper
- if (m_locoInfo->m_pitchRate > 0.0f)
- m_locoInfo->m_pitchRate *= 0.5f;
- m_locoInfo->m_rollRate += ((-ROLL_STIFFNESS * (m_locoInfo->m_roll - groundRoll)) + (-ROLL_DAMPING * m_locoInfo->m_rollRate)); // spring/damper
- }
- m_locoInfo->m_pitch += m_locoInfo->m_pitchRate * UNIFORM_AXIAL_DAMPING;
- m_locoInfo->m_roll += m_locoInfo->m_rollRate * UNIFORM_AXIAL_DAMPING;
- // process chassis acceleration dynamics - damp back towards zero
- m_locoInfo->m_accelerationPitchRate += ((-PITCH_STIFFNESS * (m_locoInfo->m_accelerationPitch)) + (-PITCH_DAMPING * m_locoInfo->m_accelerationPitchRate)); // spring/damper
- m_locoInfo->m_accelerationPitch += m_locoInfo->m_accelerationPitchRate;
- m_locoInfo->m_accelerationRollRate += ((-ROLL_STIFFNESS * m_locoInfo->m_accelerationRoll) + (-ROLL_DAMPING * m_locoInfo->m_accelerationRollRate)); // spring/damper
- m_locoInfo->m_accelerationRoll += m_locoInfo->m_accelerationRollRate;
- // compute total pitch and roll of tank
- info.m_totalPitch = m_locoInfo->m_pitch + m_locoInfo->m_accelerationPitch;
- info.m_totalRoll = m_locoInfo->m_roll + m_locoInfo->m_accelerationRoll;
- if (physics->isMotive())
- {
- // cause the chassis to pitch & roll in reaction to acceleration/deceleration
- Real forwardAccel = dir->x * accel->x + dir->y * accel->y;
- m_locoInfo->m_accelerationPitchRate += -(FORWARD_ACCEL_COEFF * forwardAccel);
- Real lateralAccel = -dir->y * accel->x + dir->x * accel->y;
- m_locoInfo->m_accelerationRollRate += -(LATERAL_ACCEL_COEFF * lateralAccel);
- }
- // limit acceleration pitch and roll
- if (m_locoInfo->m_accelerationPitch > ACCEL_PITCH_LIMIT)
- m_locoInfo->m_accelerationPitch = ACCEL_PITCH_LIMIT;
- else if (m_locoInfo->m_accelerationPitch < -ACCEL_PITCH_LIMIT)
- m_locoInfo->m_accelerationPitch = -ACCEL_PITCH_LIMIT;
- if (m_locoInfo->m_accelerationRoll > ACCEL_PITCH_LIMIT)
- m_locoInfo->m_accelerationRoll = ACCEL_PITCH_LIMIT;
- else if (m_locoInfo->m_accelerationRoll < -ACCEL_PITCH_LIMIT)
- m_locoInfo->m_accelerationRoll = -ACCEL_PITCH_LIMIT;
- info.m_totalZ = 0;
- // Calculate suspension info.
- Real length = obj->getGeometryInfo().getMajorRadius();
- Real width = obj->getGeometryInfo().getMinorRadius();
- Real pitchHeight = length*Sin(info.m_totalPitch-groundPitch);
- Real rollHeight = width*Sin(info.m_totalRoll-groundRoll);
- if (DO_WHEELS)
- {
- // calculate each wheel position
- m_locoInfo->m_wheelInfo.m_framesAirborne = m_locoInfo->m_wheelInfo.m_framesAirborneCounter;
- m_locoInfo->m_wheelInfo.m_framesAirborneCounter = 0;
- TWheelInfo newInfo = m_locoInfo->m_wheelInfo;
- PhysicsTurningType rotation = physics->getTurning();
- if (rotation == TURN_NEGATIVE) {
- newInfo.m_wheelAngle = -WHEEL_ANGLE;
- } else if (rotation == TURN_POSITIVE) {
- newInfo.m_wheelAngle = WHEEL_ANGLE;
- } else {
- newInfo.m_wheelAngle = 0;
- }
- if (physics->getForwardSpeed2D() < 0.0f) {
- // if we're moving backwards, the wheels rotate in the opposite direction.
- newInfo.m_wheelAngle = -newInfo.m_wheelAngle;
- }
- //
- ///@todo Steven/John ... please review this and make sure it makes sense (CBD)
- // we're going to add the angle to the current wheel rotation ... but we're going to
- // divide that number to add small angles. This allows for "smoother" wheel turning
- // transitions ... and when the AI has things move in a straight line, since it's
- // constantly telling the object to go left, go straight, go right, go straight,
- // etc, this smaller angle we'll be adding covers the constant wheel shifting
- // left and right when moving in a relatively straight line
- //
- #define WHEEL_SMOOTHNESS 10.0f // higher numbers add smaller angles, make it more "smooth"
- m_locoInfo->m_wheelInfo.m_wheelAngle += (newInfo.m_wheelAngle - m_locoInfo->m_wheelInfo.m_wheelAngle)/WHEEL_SMOOTHNESS;
- const Real SPRING_FACTOR = 0.9f;
- if (pitchHeight<0) { // Front raising up
- newInfo.m_frontLeftHeightOffset = SPRING_FACTOR*(pitchHeight/3+pitchHeight/2);
- newInfo.m_frontRightHeightOffset = SPRING_FACTOR*(pitchHeight/3+pitchHeight/2);
- newInfo.m_rearLeftHeightOffset = -pitchHeight/2 + pitchHeight/4;
- newInfo.m_rearRightHeightOffset = -pitchHeight/2 + pitchHeight/4;
- } else { // Back rasing up.
- newInfo.m_frontLeftHeightOffset = (-pitchHeight/4+pitchHeight/2);
- newInfo.m_frontRightHeightOffset = (-pitchHeight/4+pitchHeight/2);
- newInfo.m_rearLeftHeightOffset = SPRING_FACTOR*(-pitchHeight/2 + -pitchHeight/3);
- newInfo.m_rearRightHeightOffset = SPRING_FACTOR*(-pitchHeight/2 + -pitchHeight/3);
- }
- if (rollHeight>0) { // Right raising up
- newInfo.m_frontRightHeightOffset += -SPRING_FACTOR*(rollHeight/3+rollHeight/2);
- newInfo.m_rearRightHeightOffset += -SPRING_FACTOR*(rollHeight/3+rollHeight/2);
- newInfo.m_rearLeftHeightOffset += rollHeight/2 - rollHeight/4;
- newInfo.m_frontLeftHeightOffset += rollHeight/2 - rollHeight/4;
- } else { // Left rasing up.
- newInfo.m_frontRightHeightOffset += -rollHeight/2 + rollHeight/4;
- newInfo.m_rearRightHeightOffset += -rollHeight/2 + rollHeight/4;
- newInfo.m_rearLeftHeightOffset += SPRING_FACTOR*(rollHeight/3+rollHeight/2);
- newInfo.m_frontLeftHeightOffset += SPRING_FACTOR*(rollHeight/3+rollHeight/2);
- }
- if (newInfo.m_frontLeftHeightOffset < m_locoInfo->m_wheelInfo.m_frontLeftHeightOffset) {
- // If it's going down, dampen the movement a bit
- m_locoInfo->m_wheelInfo.m_frontLeftHeightOffset += (newInfo.m_frontLeftHeightOffset - m_locoInfo->m_wheelInfo.m_frontLeftHeightOffset)/2.0f;
- } else {
- m_locoInfo->m_wheelInfo.m_frontLeftHeightOffset = newInfo.m_frontLeftHeightOffset;
- }
- if (newInfo.m_frontRightHeightOffset < m_locoInfo->m_wheelInfo.m_frontRightHeightOffset) {
- // If it's going down, dampen the movement a bit
- m_locoInfo->m_wheelInfo.m_frontRightHeightOffset += (newInfo.m_frontRightHeightOffset - m_locoInfo->m_wheelInfo.m_frontRightHeightOffset)/2.0f;
- } else {
- m_locoInfo->m_wheelInfo.m_frontRightHeightOffset = newInfo.m_frontRightHeightOffset;
- }
- if (newInfo.m_rearLeftHeightOffset < m_locoInfo->m_wheelInfo.m_rearLeftHeightOffset) {
- // If it's going down, dampen the movement a bit
- m_locoInfo->m_wheelInfo.m_rearLeftHeightOffset += (newInfo.m_rearLeftHeightOffset - m_locoInfo->m_wheelInfo.m_rearLeftHeightOffset)/2.0f;
- } else {
- m_locoInfo->m_wheelInfo.m_rearLeftHeightOffset = newInfo.m_rearLeftHeightOffset;
- }
- if (newInfo.m_rearRightHeightOffset < m_locoInfo->m_wheelInfo.m_rearRightHeightOffset) {
- // If it's going down, dampen the movement a bit
- m_locoInfo->m_wheelInfo.m_rearRightHeightOffset += (newInfo.m_rearRightHeightOffset - m_locoInfo->m_wheelInfo.m_rearRightHeightOffset)/2.0f;
- } else {
- m_locoInfo->m_wheelInfo.m_rearRightHeightOffset = newInfo.m_rearRightHeightOffset;
- }
- //m_locoInfo->m_wheelInfo = newInfo;
- if (m_locoInfo->m_wheelInfo.m_frontLeftHeightOffset<MAX_SUSPENSION_EXTENSION) {
- m_locoInfo->m_wheelInfo.m_frontLeftHeightOffset=MAX_SUSPENSION_EXTENSION;
- }
- if (m_locoInfo->m_wheelInfo.m_frontRightHeightOffset<MAX_SUSPENSION_EXTENSION) {
- m_locoInfo->m_wheelInfo.m_frontRightHeightOffset=MAX_SUSPENSION_EXTENSION;
- }
- if (m_locoInfo->m_wheelInfo.m_rearLeftHeightOffset<MAX_SUSPENSION_EXTENSION) {
- m_locoInfo->m_wheelInfo.m_rearLeftHeightOffset=MAX_SUSPENSION_EXTENSION;
- }
- if (m_locoInfo->m_wheelInfo.m_rearRightHeightOffset<MAX_SUSPENSION_EXTENSION) {
- m_locoInfo->m_wheelInfo.m_rearRightHeightOffset=MAX_SUSPENSION_EXTENSION;
- }
- /*
- if (m_locoInfo->m_wheelInfo.m_frontLeftHeightOffset>MAX_SUSPENSION_COMPRESSION) {
- m_locoInfo->m_wheelInfo.m_frontLeftHeightOffset=MAX_SUSPENSION_COMPRESSION;
- }
- if (m_locoInfo->m_wheelInfo.m_frontRightHeightOffset>MAX_SUSPENSION_COMPRESSION) {
- m_locoInfo->m_wheelInfo.m_frontRightHeightOffset=MAX_SUSPENSION_COMPRESSION;
- }
- if (m_locoInfo->m_wheelInfo.m_rearLeftHeightOffset>MAX_SUSPENSION_COMPRESSION) {
- m_locoInfo->m_wheelInfo.m_rearLeftHeightOffset=MAX_SUSPENSION_COMPRESSION;
- }
- if (m_locoInfo->m_wheelInfo.m_rearRightHeightOffset>MAX_SUSPENSION_COMPRESSION) {
- m_locoInfo->m_wheelInfo.m_rearRightHeightOffset=MAX_SUSPENSION_COMPRESSION;
- }
- */
- }
- // If we are > 22 degrees, need to raise height;
- Real divisor = 4;
- Real pitch = fabs(info.m_totalPitch-groundPitch);
- if (pitch>PI/8) {
- divisor = ((4*PI/8) + (1*(pitch-PI/8)))/pitch;
- }
- info.m_totalZ += fabs(pitchHeight)/divisor;
- info.m_totalZ += fabs(rollHeight)/divisor;
- }
- //-------------------------------------------------------------------------------------------------
- /** decodes the current previous damage type and sets the ambient sound set from that. */
- //-------------------------------------------------------------------------------------------------
- const AudioEventRTS& Drawable::getAmbientSoundByDamage(BodyDamageType dt)
- {
- switch (dt)
- {
- case BODY_DAMAGED:
- return *getTemplate()->getSoundAmbientDamaged();
- case BODY_REALLYDAMAGED:
- return *getTemplate()->getSoundAmbientReallyDamaged();
- case BODY_RUBBLE:
- return *getTemplate()->getSoundAmbientRubble();
- case BODY_PRISTINE:
- default:
- return *getTemplate()->getSoundAmbient();
- }
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- #ifdef _DEBUG
- void Drawable::validatePos() const
- {
- const Coord3D* ourPos = getPosition();
- if (_isnan(ourPos->x) || _isnan(ourPos->y) || _isnan(ourPos->z))
- {
- DEBUG_CRASH(("Drawable/Object position NAN! '%s'\n", getTemplate()->getName().str()));
- }
- if (getObject())
- {
- const Coord3D* objPos = getObject()->getPosition();
- if (ourPos->x != objPos->x || ourPos->y != objPos->y || ourPos->z != objPos->z)
- {
- DEBUG_CRASH(("Drawable/Object position mismatch! '%s'\n", getTemplate()->getName().str()));
- }
- }
- }
- #endif
- //=============================================================================
- void Drawable::setStealthLook(StealthLookType look)
- {
- if (look != m_stealthLook)
- {
- m_stealthOpacity = 1.0f; //assume not transparent
- switch (look)
- {
- case STEALTHLOOK_NONE:
- m_hiddenByStealth = false;
- m_heatVisionOpacity = 0.0f;
- break;
- case STEALTHLOOK_VISIBLE_FRIENDLY:
- case STEALTHLOOK_VISIBLE_FRIENDLY_DETECTED:
- {
- Real opacity = TheGlobalData->m_stealthFriendlyOpacity;
- Object *obj = getObject();
- if( obj )
- {
- //Try to get the stealthupdate module and see if the opacity value is overriden.
- static NameKeyType key_StealthUpdate = NAMEKEY("StealthUpdate");
- StealthUpdate* stealth = (StealthUpdate *)obj->findUpdateModule(key_StealthUpdate);
- if( stealth )
- {
- if( stealth->isDisguised() )
- {
- //Disguised objects drive the opacity level directly, hence the break.
- m_hiddenByStealth = false;
- break;
- }
- else
- {
- Real friendlyOpacity = stealth->getFriendlyOpacity();
- if( friendlyOpacity != INVALID_OPACITY )
- {
- opacity = friendlyOpacity;
- }
- }
- }
- }
- m_stealthOpacity = opacity; // make as partially transparent as this while pulsing
- m_hiddenByStealth = false;
- /** @todo srj -- evil hack here... this whole heat-vision thing is fucked.
- don't want it on mines but no good way to do that. hack for now. */
- if (look == STEALTHLOOK_VISIBLE_FRIENDLY_DETECTED && !isKindOf(KINDOF_MINE))
- m_heatVisionOpacity = 1.0f;
- else
- m_heatVisionOpacity = 0.0f;
- break;
- }
-
- case STEALTHLOOK_DISGUISED_ENEMY:
- m_hiddenByStealth = false;
- m_heatVisionOpacity = 0.0f;
- break;
- // this is for the non-controllingplayer that can see me anyway
- case STEALTHLOOK_VISIBLE_DETECTED:
- m_hiddenByStealth = false;// let the scene omit the first drawing pass
- /** @todo srj -- evil hack here... this whole heat-vision thing is fucked.
- don't want it on mines but no good way to do that. hack for now. */
- if (isKindOf(KINDOF_MINE))
- m_heatVisionOpacity = 0.0f;
- else
- m_heatVisionOpacity = 1.0f;// Draw() will fade until it is set to 1 again
- break;
- case STEALTHLOOK_INVISIBLE:
- m_hiddenByStealth = true;
- m_heatVisionOpacity = 0.0f;
- break;
- }
- m_stealthLook = look;
- updateHiddenStatus();
- }
- }
- //-------------------------------------------------------------------------------------------------
- /** default draw is to just call the database defined draw */
- //-------------------------------------------------------------------------------------------------
- void Drawable::draw( View *view )
- {
- if ( getObject() && getObject()->isEffectivelyDead() )
- m_heatVisionOpacity = 0.0f;//dead folks don't stealth anyway
- else if ( m_heatVisionOpacity > VERY_TRANSPARENT_HEATVISION )// keep fading any heatvision unless something has set it to zero
- m_heatVisionOpacity *= HEATVISION_FADE_SCALAR;
- else
- m_heatVisionOpacity = 0.0f;
- if (m_hidden || m_hiddenByStealth || getFullyObscuredByShroud())
- return; // my, that was easy
- if ( getObject() && !getObject()->isEffectivelyDead() )
- setShadowsEnabled( m_stealthLook != STEALTHLOOK_VISIBLE_DETECTED );
- #ifdef _DEBUG
- validatePos();
- #endif
- // call the database defined draw action method
- Matrix3D transformMtx = *getTransformMatrix();
- if (!isInstanceIdentity())
- {
- #ifdef ALLOW_TEMPORARIES
- transformMtx = transformMtx * (*getInstanceMatrix());
- #else
- transformMtx.postMul(*getInstanceMatrix());
- #endif
- }
- applyPhysicsXform(&transformMtx);
- for (DrawModule** dm = getDrawModules(); *dm; ++dm)
- {
- (*dm)->doDrawModule(&transformMtx);
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** Compute the health bar region based on the health of the object and the
- * zoom level of the camera */
- // ------------------------------------------------------------------------------------------------
- static Bool computeHealthRegion( const Drawable *draw, IRegion2D& region )
- {
- // sanity
- if( draw == NULL )
- return FALSE;
- const Object *obj = draw->getObject();
- if( obj == NULL )
- return FALSE;
- Coord3D p;
- obj->getHealthBoxPosition(p);
- ICoord2D screenCenter;
- if( !TheTacticalView->worldToScreen( &p, &screenCenter ) )
- return FALSE;
- Real healthBoxWidth, healthBoxHeight;
- if (!obj->getHealthBoxDimensions(healthBoxHeight, healthBoxWidth))
- return FALSE;
- // scale the health bars according to the zoom
- Real zoom = TheTacticalView->getZoom();
- //Real widthScale = 1.3f / zoom;
- Real widthScale = 1.0f / zoom;
- //Real heightScale = 0.8f / zoom;
- Real heightScale = 1.0f;
- healthBoxWidth *= widthScale;
- healthBoxHeight *= heightScale;
- // do this so health bar doesn't get too skinny or fat after scaling
- //healthBoxHeight = max(3.0f, healthBoxHeight);
- healthBoxHeight = 3.0f;
- // figure out the final region for the health box
- region.lo.x = screenCenter.x - healthBoxWidth * 0.45f;
- region.lo.y = screenCenter.y - healthBoxHeight * 0.5f;
- region.hi.x = region.lo.x + healthBoxWidth;
- region.hi.y = region.lo.y + healthBoxHeight;
- return TRUE;
- } // end computeHealthRegion
- // ------------------------------------------------------------------------------------------------
- Bool Drawable::drawsAnyUIText( void )
- {
- if (!isSelected())
- return FALSE;
- const Object *obj = getObject();
- if ( !obj || !obj->isLocallyControlled() )
- return FALSE;
- Player *owner = obj->getControllingPlayer();
- Int groupNum = owner->getSquadNumberForObject(obj);
- if (groupNum > NO_HOTKEY_SQUAD && groupNum < NUM_HOTKEY_SQUADS )
- return TRUE;
- else
- m_groupNumber = NULL;
-
- if ( obj->getFormationID() != NO_FORMATION_ID )
- return TRUE;
- return FALSE;
- }
- // ------------------------------------------------------------------------------------------------
- /** This is called as part of the "post draw" phase when drawable a drawable. It is there
- * that we should overlay on the screen any 2D elements for purposes of user interface
- * information (such as a heatlh bar, veterency levels, etc.) */
- // ------------------------------------------------------------------------------------------------
- void Drawable::drawIconUI( void )
- {
- if( TheGameLogic->getDrawIconUI() && (TheScriptEngine->getFade()==ScriptEngine::FADE_NONE) )
- {
- IRegion2D healthBarRegionStorage;
- const IRegion2D* healthBarRegion = NULL;
- if (computeHealthRegion(this, healthBarRegionStorage))
- healthBarRegion = &healthBarRegionStorage; //both data and a PointerAsFlag for logic in the methods below
- Object *obj = getObject();
-
- // we only draw icons drawables with objects, so one bail here -------------------------
- if ( ! obj )
- return;
- //Icons that can be drawn on dead things
- drawHealthBar( healthBarRegion );
- drawEmoticon( healthBarRegion );
- drawCaption( healthBarRegion );
- drawConstructPercent( healthBarRegion );
- //All Icons Below only draw on ALIVE things, so bail here -------------------------
- if( obj->isEffectivelyDead() || obj->isKindOf( KINDOF_IGNORED_IN_GUI )) // object explicitly wants nothing to do with these icons, so...
- return;
- drawHealing( healthBarRegion );//call so dead things can kill their healing icons
- drawBombed( healthBarRegion );
-
-
- //Disabled for multiplay!
- //drawBattlePlans( healthBarRegion );
-
- if ( drawsAnyUIText() )
- TheGameClient->addTextBearingDrawable( this );
- drawEnthusiastic( healthBarRegion );
- #ifdef ALLOW_DEMORALIZE
- drawDemoralized( healthBarRegion );
- #endif
- drawDisabled( healthBarRegion );
- drawAmmo( healthBarRegion );
- drawContained( healthBarRegion );
- //Moved this to last so that it shows up over contained and ammo icons.
- drawVeterancy( healthBarRegion );
- }
- }
- //------------------------------------------------------------------------------------------------
- DrawableIconInfo* Drawable::getIconInfo()
- {
- if (m_iconInfo == NULL)
- m_iconInfo = newInstance(DrawableIconInfo);
- return m_iconInfo;
- }
- //------------------------------------------------------------------------------------------------
- void Drawable::clearEmoticon()
- {
- if (!hasIconInfo())
- return;
- killIcon(ICON_EMOTICON);
- }
- //------------------------------------------------------------------------------------------------
- void Drawable::setEmoticon( const AsciiString &name, Int duration )
- {
- //A duration of -1 means FOREVER
- clearEmoticon();
- Anim2DTemplate *animTemplate = TheAnim2DCollection->findTemplate( name );
- if( animTemplate )
- {
- DEBUG_ASSERTCRASH( getIconInfo()->m_icon[ ICON_EMOTICON ] == NULL, ("Drawable::setEmoticon - Emoticon isn't empty, need to refuse to set or destroy the old one in favor of the new one\n") );
- if( getIconInfo()->m_icon[ ICON_EMOTICON ] == NULL )
- {
- getIconInfo()->m_icon[ ICON_EMOTICON ] = newInstance(Anim2D)( animTemplate, TheAnim2DCollection );
- getIconInfo()->m_keepTillFrame[ ICON_EMOTICON ] = duration >= 0 ? TheGameLogic->getFrame() + duration : FOREVER;
- }
- }
- }
- //------------------------------------------------------------------------------------------------
- void Drawable::drawEmoticon( const IRegion2D *healthBarRegion )
- {
- if( hasIconInfo() && getIconInfo()->m_icon[ ICON_EMOTICON ] )
- {
- UnsignedInt now = TheGameLogic->getFrame();
- if( healthBarRegion && getIconInfo()->m_keepTillFrame[ ICON_EMOTICON ] >= now )
- {
- //Draw the emoticon.
- Int barWidth = healthBarRegion->hi.x - healthBarRegion->lo.x;
- //Int barHeight = healthBarRegion.hi.y - healthBarRegion.lo.y;
- Int frameWidth = getIconInfo()->m_icon[ ICON_EMOTICON ]->getCurrentFrameWidth();
- Int frameHeight = getIconInfo()->m_icon[ ICON_EMOTICON ]->getCurrentFrameHeight();
- #ifdef SCALE_ICONS_WITH_ZOOM_ML
- // adjust the width to be a % of the health bar region size
- Int size = REAL_TO_INT( barWidth * 0.3f );
- frameHeight = REAL_TO_INT((INT_TO_REAL(size) / INT_TO_REAL(frameWidth)) * frameHeight);
- frameWidth = size;
- #endif
- // given our scaled width and height we need to find the top left point to draw the image at
- ICoord2D screen;
- screen.x = (Int)(healthBarRegion->lo.x + (barWidth * 0.5f) - (frameWidth * 0.5f));
- screen.y = healthBarRegion->hi.y - frameHeight;
- getIconInfo()->m_icon[ ICON_EMOTICON ]->draw( screen.x, screen.y, frameWidth, frameHeight );
- }
- else
- {
- //Get rid of the emoticon.
- clearEmoticon();
- }
- }
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void Drawable::drawAmmo( const IRegion2D *healthBarRegion )
- {
- const Object *obj = getObject();
- if (!(
- TheGlobalData->m_showObjectHealth &&
- (isSelected() || (TheInGameUI && (TheInGameUI->getMousedOverDrawableID() == getID()))) &&
- obj->getControllingPlayer() == ThePlayerList->getLocalPlayer()
- ))
- return;
- Int numTotal;
- Int numFull;
- if (!obj->getAmmoPipShowingInfo(numTotal, numFull))
- return;
- if (!s_fullAmmo || !s_emptyAmmo)
- return;
-
- #ifdef SCALE_ICONS_WITH_ZOOM_ML
- Real scale = TheGlobalData->m_ammoPipScaleFactor / CLAMP_ICON_ZOOM_FACTOR( TheTacticalView->getZoom() );
- #else
- Real scale = 1.0f;
- #endif
- Int boxWidth = REAL_TO_INT(s_emptyAmmo->getImageWidth() * scale);
- Int boxHeight = REAL_TO_INT(s_emptyAmmo->getImageHeight() * scale);
- const Int SPACING = 1;
- //Int totalWidth = (boxWidth+SPACING)*numTotal;
- ICoord2D screenCenter;
- Coord3D pos = *obj->getPosition();
- pos.x += TheGlobalData->m_ammoPipWorldOffset.x;
- pos.y += TheGlobalData->m_ammoPipWorldOffset.y;
- pos.z += TheGlobalData->m_ammoPipWorldOffset.z + obj->getGeometryInfo().getMaxHeightAbovePosition();
- if( !TheTacticalView->worldToScreen( &pos, &screenCenter ) )
- return;
- Real bounding = obj->getGeometryInfo().getBoundingSphereRadius() * scale;
- //Int posx = screenCenter.x + REAL_TO_INT(TheGlobalData->m_ammoPipScreenOffset.x*bounding) - totalWidth;
- //**CHANGING CODE: Left justify with health bar min
- Int posx = healthBarRegion->lo.x;
- Int posy = screenCenter.y + REAL_TO_INT(TheGlobalData->m_ammoPipScreenOffset.y*bounding);
- for (Int i = 0; i < numTotal; ++i)
- {
- TheDisplay->drawImage(i < numFull ? s_fullAmmo : s_emptyAmmo, posx, posy + 1, posx + boxWidth, posy + 1 + boxHeight);
- posx += boxWidth + SPACING;
- }
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void Drawable::drawContained( const IRegion2D *healthBarRegion )
- {
- const Object *obj = getObject();
- ContainModuleInterface* container = obj->getContain();
- if (!container)
- return;
- if (!(
- TheGlobalData->m_showObjectHealth &&
- (isSelected() || (TheInGameUI && (TheInGameUI->getMousedOverDrawableID() == getID()))) &&
- obj->getControllingPlayer() == ThePlayerList->getLocalPlayer()
- ))
- return;
- Int numTotal;
- Int numFull;
- if (!container->getContainerPipsToShow(numTotal, numFull))
- return;
- // if empty, don't show nothin'
- if (numFull == 0)
- return;
- Int numInfantry = 0;
- const ContainedItemsList* contained = container->getContainedItemsList();
- if (contained)
- {
- for (ContainedItemsList::const_iterator it = contained->begin(); it != contained->end(); ++it)
- {
- if ((*it)->isKindOf(KINDOF_INFANTRY))
- ++numInfantry;
- }
- }
- #ifdef SCALE_ICONS_WITH_ZOOM_ML
- Real scale = TheGlobalData->m_ammoPipScaleFactor / CLAMP_ICON_ZOOM_FACTOR( TheTacticalView->getZoom() );
- #else
- Real scale = 1.0f;
- #endif
- Int boxWidth = REAL_TO_INT(s_emptyContainer->getImageWidth() * scale);
- Int boxHeight = REAL_TO_INT(s_emptyContainer->getImageHeight() * scale);
- const Int SPACING = 1;
- //Int totalWidth = (boxWidth+SPACING)*numTotal;
- ICoord2D screenCenter;
- Coord3D pos = *obj->getPosition();
- pos.x += TheGlobalData->m_containerPipWorldOffset.x;
- pos.y += TheGlobalData->m_containerPipWorldOffset.y;
- pos.z += TheGlobalData->m_containerPipWorldOffset.z + obj->getGeometryInfo().getMaxHeightAbovePosition();
- if( !TheTacticalView->worldToScreen( &pos, &screenCenter ) )
- return;
- Real bounding = obj->getGeometryInfo().getBoundingSphereRadius() * scale;
-
- //Int posx = screenCenter.x + REAL_TO_INT(TheGlobalData->m_containerPipScreenOffset.x*bounding) - totalWidth;
- //**CHANGING CODE: Left justify with health bar min
- Int posx = healthBarRegion->lo.x;
- Int posy = screenCenter.y + REAL_TO_INT(TheGlobalData->m_containerPipScreenOffset.y*bounding);
- for (Int i = 0; i < numTotal; ++i)
- {
- const Color INFANTRY_COLOR = GameMakeColor(0, 255, 0, 255);
- const Color NON_INFANTRY_COLOR = GameMakeColor(0, 0, 255, 255);
- if (i < numFull)
- TheDisplay->drawImage(s_fullContainer, posx, posy, posx + boxWidth, posy + boxHeight,
- (i < numInfantry) ? INFANTRY_COLOR : NON_INFANTRY_COLOR);
- else
- TheDisplay->drawImage(s_emptyContainer, posx, posy + 1, posx + boxWidth, posy + 1 + boxHeight);
- posx += boxWidth + SPACING;
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Drawable::drawBattlePlans( const IRegion2D *healthBarRegion )
- {
- Object *obj = getObject();
- if( !obj || !healthBarRegion )
- {
- return;
- }
- Player *player = obj->getControllingPlayer();
- if( player && player->getNumBattlePlansActive() > 0 && player->doesObjectQualifyForBattlePlan( obj ) )
- {
- if( player->getBattlePlansActiveSpecific( PLANSTATUS_BOMBARDMENT ) )
- {
- if( !getIconInfo()->m_icon[ ICON_BATTLEPLAN_BOMBARD ] )
- {
- getIconInfo()->m_icon[ ICON_BATTLEPLAN_BOMBARD ] = newInstance(Anim2D)( s_animationTemplates[ ICON_BATTLEPLAN_BOMBARD ], TheAnim2DCollection );
- }
- //Int barHeight = healthBarRegion.hi.y - healthBarRegion.lo.y;
- Int frameWidth = getIconInfo()->m_icon[ ICON_BATTLEPLAN_BOMBARD ]->getCurrentFrameWidth();
- Int frameHeight = getIconInfo()->m_icon[ ICON_BATTLEPLAN_BOMBARD ]->getCurrentFrameHeight();
-
- #ifdef SCALE_ICONS_WITH_ZOOM_ML
- // adjust the width to be a % of the health bar region size
- Int barWidth = healthBarRegion->hi.x - healthBarRegion->lo.x;
- Int size = REAL_TO_INT( barWidth * 0.3f );
- frameHeight = REAL_TO_INT((INT_TO_REAL(size) / INT_TO_REAL(frameWidth)) * frameHeight);
- frameWidth = size;
- #endif
- // given our scaled width and height we need to find the top left point to draw the image at
- ICoord2D screen;
- screen.x = healthBarRegion->lo.x;
- screen.y = healthBarRegion->lo.y + frameHeight;
- getIconInfo()->m_icon[ ICON_BATTLEPLAN_BOMBARD ]->draw( screen.x, screen.y, frameWidth, frameHeight );
- }
- else
- {
- killIcon(ICON_BATTLEPLAN_BOMBARD);
- }
- if( player->getBattlePlansActiveSpecific( PLANSTATUS_HOLDTHELINE ) )
- {
- if( !getIconInfo()->m_icon[ ICON_BATTLEPLAN_HOLDTHELINE ] )
- {
- getIconInfo()->m_icon[ ICON_BATTLEPLAN_HOLDTHELINE ] = newInstance(Anim2D)( s_animationTemplates[ ICON_BATTLEPLAN_HOLDTHELINE ], TheAnim2DCollection );
- }
- // draw the icon
- Int frameWidth = getIconInfo()->m_icon[ ICON_BATTLEPLAN_HOLDTHELINE ]->getCurrentFrameWidth();
- Int frameHeight = getIconInfo()->m_icon[ ICON_BATTLEPLAN_HOLDTHELINE ]->getCurrentFrameHeight();
-
- #ifdef SCALE_ICONS_WITH_ZOOM_ML
- // adjust the width to be a % of the health bar region size
- Int barWidth = healthBarRegion->hi.x - healthBarRegion->lo.x;
- Int size = REAL_TO_INT( barWidth * 0.3f );
- frameHeight = REAL_TO_INT((INT_TO_REAL(size) / INT_TO_REAL(frameWidth)) * frameHeight);
- frameWidth = size;
- #endif
- // given our scaled width and height we need to find the top left point to draw the image at
- ICoord2D screen;
- screen.x = healthBarRegion->lo.x;
- screen.y = healthBarRegion->lo.y + frameHeight;
- getIconInfo()->m_icon[ ICON_BATTLEPLAN_HOLDTHELINE ]->draw( screen.x + frameWidth, screen.y, frameWidth, frameHeight );
- }
- else
- {
- killIcon(ICON_BATTLEPLAN_HOLDTHELINE);
- }
- if( player->getBattlePlansActiveSpecific( PLANSTATUS_SEARCHANDDESTROY ) )
- {
- if( !getIconInfo()->m_icon[ ICON_BATTLEPLAN_SEARCHANDDESTROY ] )
- {
- getIconInfo()->m_icon[ ICON_BATTLEPLAN_SEARCHANDDESTROY ] = newInstance(Anim2D)( s_animationTemplates[ ICON_BATTLEPLAN_SEARCHANDDESTROY ], TheAnim2DCollection );
- }
- // draw the icon
- Int frameWidth = getIconInfo()->m_icon[ ICON_BATTLEPLAN_SEARCHANDDESTROY ]->getCurrentFrameWidth();
- Int frameHeight = getIconInfo()->m_icon[ ICON_BATTLEPLAN_SEARCHANDDESTROY ]->getCurrentFrameHeight();
- #ifdef SCALE_ICONS_WITH_ZOOM_ML
- // adjust the width to be a % of the health bar region size
- Int barWidth = healthBarRegion->hi.x - healthBarRegion->lo.x;
- Int size = REAL_TO_INT( barWidth * 0.3f );
- frameHeight = REAL_TO_INT((INT_TO_REAL(size) / INT_TO_REAL(frameWidth)) * frameHeight);
- frameWidth = size;
- #endif
- // given our scaled width and height we need to find the top left point to draw the image at
- ICoord2D screen;
- screen.x = healthBarRegion->lo.x;
- screen.y = healthBarRegion->lo.y + frameHeight;
- getIconInfo()->m_icon[ ICON_BATTLEPLAN_SEARCHANDDESTROY ]->draw( screen.x + (frameWidth * 2), screen.y, frameWidth, frameHeight );
- }
- else
- {
- killIcon(ICON_BATTLEPLAN_SEARCHANDDESTROY);
- }
-
- }
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void Drawable::drawUIText()
- {
- // This gets called by GameClient now
- // GameClient caches a list of us drawables during Drawablepostdraw()
- // then our group numbers get spit out last, so they draw in front
-
- const IRegion2D* healthBarRegion = NULL;
- IRegion2D healthBarRegionStorage;
- if (computeHealthRegion(this, healthBarRegionStorage))
- healthBarRegion = &healthBarRegionStorage; //both data and a PointerAsFlag for logic in the methods below
- if (!healthBarRegion)
- return;
- const Object *obj = getObject();
- Player *owner = obj->getControllingPlayer();
- Int groupNum = owner->getSquadNumberForObject(obj);
- Color color = TheDrawGroupInfo->m_usePlayerColor ? owner->getPlayerColor() : TheDrawGroupInfo->m_colorForText;
- if (groupNum > NO_HOTKEY_SQUAD && groupNum < NUM_HOTKEY_SQUADS )
- {
- Int xPos = healthBarRegion->lo.x;
- Int yPos = healthBarRegion->lo.y;
- if (TheDrawGroupInfo->m_usingPixelOffsetX) {
- xPos += TheDrawGroupInfo->m_pixelOffsetX;
- } else {
- xPos += (healthBarRegion->width() * TheDrawGroupInfo->m_percentOffsetX);
- }
- if (TheDrawGroupInfo->m_usingPixelOffsetY) {
- yPos += TheDrawGroupInfo->m_pixelOffsetY;
- } else {
- yPos += (healthBarRegion->width() * TheDrawGroupInfo->m_percentOffsetY);
- }
- m_groupNumber = TheDisplayStringManager->getGroupNumeralString(groupNum);
-
- m_groupNumber->draw(xPos, yPos, color,
- TheDrawGroupInfo->m_colorForTextDropShadow,
- TheDrawGroupInfo->m_dropShadowOffsetX,
- TheDrawGroupInfo->m_dropShadowOffsetY);
- }
- if ( obj->getFormationID() != NO_FORMATION_ID )
- {
- //draw an F, here
- Coord3D p;
- ICoord2D screenCenter;
- obj->getHealthBoxPosition(p);
- if( ! TheTacticalView->worldToScreen( &p, &screenCenter ) )
- return;
- Real healthBoxWidth, healthBoxHeight;
- if ( ! obj->getHealthBoxDimensions(healthBoxHeight, healthBoxWidth))
- return;
- Real scale = 1.3f/CLAMP_ICON_ZOOM_FACTOR( TheTacticalView->getZoom() );
- screenCenter.x += (healthBoxWidth * scale * 0.5f) + 10 ;
- DisplayString *formationMarker = TheDisplayStringManager->getFormationLetterString();
- //static DisplayString *formationMarker = TheDisplayStringManager->getGroupNumeralString( 5 );
- if ( formationMarker )
- formationMarker->draw(screenCenter.x, screenCenter.y, color,
- TheDrawGroupInfo->m_colorForTextDropShadow,
- TheDrawGroupInfo->m_dropShadowOffsetX,
- TheDrawGroupInfo->m_dropShadowOffsetY);
- }
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void Drawable::drawHealing(const IRegion2D* healthBarRegion)
- {
- const Object *obj = getObject();
- // we do show show icons for things that explicitly forbid it
- if( obj->isKindOf( KINDOF_NO_HEAL_ICON ) || BitTest( obj->getStatusBits(), OBJECT_STATUS_SOLD ))
- return;
- // see if healing has been done to us recently
- Bool showHealing = FALSE;
- BodyModuleInterface *body = obj->getBodyModule();
- if( body->getHealth() != body->getMaxHealth() )
- {
- // const DamageInfo* lastDamage = body->getLastDamageInfo();
- // if( lastDamage != NULL && lastDamage->in.m_damageType == DAMAGE_HEALING
- // &&(TheGameLogic->getFrame() - body->getLastHealingTimestamp()) <= HEALING_ICON_DISPLAY_TIME
- // )
- if ( TheGameLogic->getFrame() > HEALING_ICON_DISPLAY_TIME && // because so many things init health early in game
- (TheGameLogic->getFrame() - body->getLastHealingTimestamp() <= HEALING_ICON_DISPLAY_TIME) )
- showHealing = TRUE;
- }
- // based on our own kind of we have certain icons to display at a size scale
- Real scale;
- DrawableIconType typeIndex;
- if( isKindOf( KINDOF_STRUCTURE ) )
- {
- typeIndex = ICON_STRUCTURE_HEAL;
- scale = 0.33f;
- }
- else if( isKindOf( KINDOF_VEHICLE ) )
- {
- typeIndex = ICON_VEHICLE_HEAL;
- scale = 0.7f;
- }
- else
- {
- typeIndex = ICON_DEFAULT_HEAL;
- scale = 0.7f;
- }
- //
- // if we are to show healing make sure we have the animation for it allocated, otherwise
- // free any animation we may have allocated back to the animation memory pool
- //
- if( showHealing ) /// @todo HERE, WE NEED TO LEAVE STUFF ALONE, IF WE ARE ALREADY SHOWING HEALING
- {
- if (healthBarRegion != NULL)
- {
- if( getIconInfo()->m_icon[ typeIndex ] == NULL )
- getIconInfo()->m_icon[ typeIndex ] = newInstance(Anim2D)( s_animationTemplates[ typeIndex ], TheAnim2DCollection );
- // draw the animation if present
- if( getIconInfo()->m_icon[ typeIndex ] != NULL)
- {
-
- //
- // we are going to draw the healing icon relative to the size of the health bar region
- // since that region takes into account hit point size and zoom factor of the camera too
- //
- Int barWidth = healthBarRegion->hi.x - healthBarRegion->lo.x;
- Int frameWidth = getIconInfo()->m_icon[ typeIndex ]->getCurrentFrameWidth();
- Int frameHeight = getIconInfo()->m_icon[ typeIndex ]->getCurrentFrameHeight();
- #ifdef SCALE_ICONS_WITH_ZOOM_ML
- // adjust the width to be a % of the health bar region size
- Int size = REAL_TO_INT( barWidth * scale );
- frameHeight = REAL_TO_INT((INT_TO_REAL(size) / INT_TO_REAL(frameWidth)) * frameHeight);
- frameWidth = size;
- #endif
- // given our scaled width and height we need to find the top left point to draw the image at
- ICoord2D screen;
- screen.x = REAL_TO_INT( healthBarRegion->lo.x + (barWidth * 0.75f) - (frameWidth * 0.5f) );
- screen.y = REAL_TO_INT( healthBarRegion->lo.y - frameHeight );
- getIconInfo()->m_icon[ typeIndex ]->draw( screen.x, screen.y, frameWidth, frameHeight );
-
- }
- }
- }
- else
- {
- killIcon(typeIndex);
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** This enthusiastic effect is TEMPORARY for the multiplayer test */
- // ------------------------------------------------------------------------------------------------
- void Drawable::drawEnthusiastic(const IRegion2D* healthBarRegion)
- {
- const Object *obj = getObject();
- //
- // if we are to show effect make sure we have the animation for it allocated, otherwise
- // free any animation we may have allocated back to the animation memory pool
- //
- // only display if have enthusiasm
-
- if( obj->testWeaponBonusCondition( WEAPONBONUSCONDITION_ENTHUSIASTIC ) == TRUE &&
- healthBarRegion != NULL )
- {
- DrawableIconType iconIndex = ICON_ENTHUSIASTIC;
- if (obj->testWeaponBonusCondition( WEAPONBONUSCONDITION_SUBLIMINAL ) == TRUE )// unless...
- iconIndex = ICON_ENTHUSIASTIC_SUBLIMINAL;
- if( getIconInfo()->m_icon[ iconIndex ] == NULL )
- getIconInfo()->m_icon[ iconIndex ] = newInstance(Anim2D)( s_animationTemplates[ iconIndex ], TheAnim2DCollection );
- // draw the animation if present
- if( getIconInfo()->m_icon[ iconIndex ] != NULL)
- {
-
- //
- // we are going to draw the healing icon relative to the size of the health bar region
- // since that region takes into account hit point size and zoom factor of the camera too
- //
- Int barWidth = healthBarRegion->hi.x - healthBarRegion->lo.x;// used for position
- // based on our own kind of we have certain icons to display at a size scale
- Real scale;
- if( isKindOf( KINDOF_STRUCTURE ) || isKindOf( KINDOF_HUGE_VEHICLE ) )
- scale = 1.00f;
- else if( isKindOf( KINDOF_VEHICLE ) )
- scale = 0.75f;
- else
- scale = 0.5f;
- Int frameWidth = getIconInfo()->m_icon[ iconIndex ]->getCurrentFrameWidth() * scale;
- Int frameHeight = getIconInfo()->m_icon[ iconIndex ]->getCurrentFrameHeight() * scale;
- #ifdef SCALE_ICONS_WITH_ZOOM_ML
- // adjust the width to be a % of the health bar region size
- Int size = REAL_TO_INT( barWidth * scale );
- frameHeight = REAL_TO_INT((INT_TO_REAL(size) / INT_TO_REAL(frameWidth)) * frameHeight);
- frameWidth = size;
- #endif
- // given our scaled width and height we need to find the bottom left point to draw the image at
- ICoord2D screen;
- screen.x = REAL_TO_INT( healthBarRegion->lo.x + (barWidth * 0.25f) - (frameWidth * 0.5f) );
- screen.y = healthBarRegion->hi.y;
- getIconInfo()->m_icon[ iconIndex ]->draw( screen.x, screen.y, frameWidth, frameHeight );
-
- }
- }
- else
- {
- killIcon(ICON_ENTHUSIASTIC);
- killIcon(ICON_ENTHUSIASTIC_SUBLIMINAL);
- }
- }
- #ifdef ALLOW_DEMORALIZE
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void Drawable::drawDemoralized(const IRegion2D* healthBarRegion)
- {
- const Object *obj = getObject();
- //
- // Demoralized
- //
- const AIUpdateInterface *ai = obj->getAIUpdateInterface();
- if (!ai)
- return;
- if( ai->isDemoralized() )
- {
- // draw the icon
- if( healthBarRegion )
- {
- // create icon if necessary
- if( getIconInfo()->m_icon[ ICON_DEMORALIZED ] == NULL )
- getIconInfo()->m_icon[ ICON_DEMORALIZED ] = newInstance(Anim2D)( s_animationTemplates[ ICON_DEMORALIZED ], TheAnim2DCollection );
-
- if (getIconInfo()->m_icon[ ICON_DEMORALIZED ])
- {
- Int frameWidth = getIconInfo()->m_icon[ ICON_DEMORALIZED ]->getCurrentFrameWidth();
- Int frameHeight = getIconInfo()->m_icon[ ICON_DEMORALIZED ]->getCurrentFrameHeight();
- #ifdef SCALE_ICONS_WITH_ZOOM_ML
- // adjust the width to be a % of the health bar region size
- Int barWidth = healthBarRegion->hi.x - healthBarRegion->lo.x;
- Int size = REAL_TO_INT( barWidth * 0.3f );
- frameHeight = REAL_TO_INT((INT_TO_REAL(size) / INT_TO_REAL(frameWidth)) * frameHeight);
- frameWidth = size;
- #endif
- // given our scaled width and height we need to find the top left point to draw the image at
- ICoord2D screen;
- screen.x = healthBarRegion->lo.x;
- screen.y = healthBarRegion->hi.y;
- getIconInfo()->m_icon[ ICON_DEMORALIZED ]->draw( screen.x, screen.y, frameWidth, frameHeight );
- }
- }
- }
- else
- {
- killIcon(ICON_DEMORALIZED);
- }
- }
- #endif
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void Drawable::drawBombed(const IRegion2D* healthBarRegion)
- {
- const Object *obj = getObject();
- UnsignedInt now = TheGameLogic->getFrame();
- if( obj->testWeaponSetFlag( WEAPONSET_CARBOMB ) &&
- obj->getControllingPlayer() == ThePlayerList->getLocalPlayer())
- {
- if( !getIconInfo()->m_icon[ ICON_CARBOMB ] )
- getIconInfo()->m_icon[ ICON_CARBOMB ] = newInstance(Anim2D)( s_animationTemplates[ ICON_CARBOMB ], TheAnim2DCollection );
- if( getIconInfo()->m_icon[ ICON_CARBOMB ] )
- {
- //
- // we are going to draw the healing icon relative to the size of the health bar region
- // since that region takes into account hit point size and zoom factor of the camera too
- //
- if( healthBarRegion )
- {
- Int barWidth = healthBarRegion->hi.x - healthBarRegion->lo.x;
- Int barHeight = healthBarRegion->hi.y - healthBarRegion->lo.y;
- Int frameWidth = getIconInfo()->m_icon[ ICON_CARBOMB ]->getCurrentFrameWidth();
- Int frameHeight = getIconInfo()->m_icon[ ICON_CARBOMB ]->getCurrentFrameHeight();
- // adjust the width to be a % of the health bar region size
- Int size = REAL_TO_INT( barWidth * 0.5f );
- frameHeight = REAL_TO_INT((INT_TO_REAL(size) / INT_TO_REAL(frameWidth)) * frameHeight);
- frameWidth = size;
- // given our scaled width and height we need to find the top left point to draw the image at
- ICoord2D screen;
- screen.x = REAL_TO_INT( healthBarRegion->lo.x + (barWidth * 0.5f) - (frameWidth * 0.5f) );
- screen.y = REAL_TO_INT( healthBarRegion->lo.y + barHeight * 0.5f );
- getIconInfo()->m_icon[ ICON_CARBOMB ]->draw( screen.x, screen.y, frameWidth, frameHeight );
- getIconInfo()->m_keepTillFrame[ ICON_CARBOMB ] = FOREVER;
- }
- }
- }
- else
- {
- killIcon(ICON_CARBOMB);
- }
- //
- // Bombed?
- //
- static NameKeyType key_StickyBombUpdate = NAMEKEY( "StickyBombUpdate" );
- StickyBombUpdate *update = (StickyBombUpdate*)obj->findUpdateModule( key_StickyBombUpdate );
- if( update )
- {
- //This case is tricky. The object that is bombed doesn't know it... but the bomb itself does.
- //So what we do is get it's target, then determine if the target has the icon or not.
- Object *target = update->getTargetObject();
- if( target )
- {
- if( update->isTimedBomb() )
- {
- //Timed bomb
- if( !getIconInfo()->m_icon[ ICON_BOMB_TIMED ] )
- {
- getIconInfo()->m_icon[ ICON_BOMB_REMOTE ] = newInstance(Anim2D)( s_animationTemplates[ ICON_BOMB_REMOTE ], TheAnim2DCollection );
- getIconInfo()->m_icon[ ICON_BOMB_TIMED ] = newInstance(Anim2D)( s_animationTemplates[ ICON_BOMB_TIMED ], TheAnim2DCollection );
- //Because this is a counter icon that ranges from 0-60 seconds, we need to calculate which frame to
- //start the animation from. Because timers are second based -- 1000 ms equal 1 frame. So we simply
- //calculate the time via detonation frame.
- //
- // srj sez: this may sound familiar somehow, but let me reiterate, just in case you missed it:
- //
- // hardcoding is bad.
- //
- // the anim got changed and now is only 20 seconds max, so the previous code was wrong.
- //
- // hey, I've got an idea! why don't we ASK the anim how long it is?
- //
- UnsignedInt dieFrame = update->getDetonationFrame();
- UnsignedInt seconds = REAL_TO_INT_CEIL( (dieFrame - now) * SECONDS_PER_LOGICFRAME_REAL);
- UnsignedInt numFrames = getIconInfo()->m_icon[ ICON_BOMB_TIMED ]->getAnimTemplate()->getNumFrames();
- // this anim goes from "N" seconds down to zero, so the max seconds we can use is N-1.
- if (seconds > numFrames - 1)
- seconds = numFrames - 1;
-
- getIconInfo()->m_icon[ ICON_BOMB_TIMED ]->setMinFrame(numFrames - seconds - 1);
- getIconInfo()->m_icon[ ICON_BOMB_TIMED ]->reset();
- }
- if( getIconInfo()->m_icon[ ICON_BOMB_TIMED ] )
- {
- //
- // we are going to draw the healing icon relative to the size of the health bar region
- // since that region takes into account hit point size and zoom factor of the camera too
- //
- if( healthBarRegion )
- {
- Int barWidth = healthBarRegion->hi.x - healthBarRegion->lo.x;
- Int barHeight = healthBarRegion->hi.y - healthBarRegion->lo.y;
- Int frameWidth = getIconInfo()->m_icon[ ICON_BOMB_TIMED ]->getCurrentFrameWidth();
- Int frameHeight = getIconInfo()->m_icon[ ICON_BOMB_TIMED ]->getCurrentFrameHeight();
- // adjust the width to be a % of the health bar region size
- Int size = REAL_TO_INT( barWidth * 0.65f );
- frameHeight = REAL_TO_INT((INT_TO_REAL(size) / INT_TO_REAL(frameWidth)) * frameHeight);
- frameWidth = size;
-
- // given our scaled width and height we need to find the top left point to draw the image at
- ICoord2D screen;
- screen.x = REAL_TO_INT( healthBarRegion->lo.x + (barWidth * 0.5f) - (frameWidth * 0.5f) );
- screen.y = REAL_TO_INT( healthBarRegion->lo.y + barHeight * 0.5f );
- getIconInfo()->m_icon[ ICON_BOMB_REMOTE ]->draw( screen.x, screen.y, frameWidth, frameHeight );
- getIconInfo()->m_keepTillFrame[ ICON_BOMB_REMOTE ] = now + 1;
- getIconInfo()->m_icon[ ICON_BOMB_TIMED ]->draw( screen.x, screen.y, frameWidth, frameHeight );
- getIconInfo()->m_keepTillFrame[ ICON_BOMB_TIMED ] = now + 1;
- }
- }
- }
- else
- {
- //Remote charge
- //Timed bomb
- if( !getIconInfo()->m_icon[ ICON_BOMB_REMOTE ] )
- {
- getIconInfo()->m_icon[ ICON_BOMB_REMOTE ] = newInstance(Anim2D)( s_animationTemplates[ ICON_BOMB_REMOTE ], TheAnim2DCollection );
- }
- if( getIconInfo()->m_icon[ ICON_BOMB_REMOTE ] )
- {
- //
- // we are going to draw the healing icon relative to the size of the health bar region
- // since that region takes into account hit point size and zoom factor of the camera too
- //
- if( healthBarRegion )
- {
- Int barWidth = healthBarRegion->hi.x - healthBarRegion->lo.x;
- Int barHeight = healthBarRegion->hi.y - healthBarRegion->lo.y;
- Int frameWidth = getIconInfo()->m_icon[ ICON_BOMB_REMOTE ]->getCurrentFrameWidth();
- Int frameHeight = getIconInfo()->m_icon[ ICON_BOMB_REMOTE ]->getCurrentFrameHeight();
- // adjust the width to be a % of the health bar region size
- Int size = REAL_TO_INT( barWidth * 0.65f );
- frameHeight = REAL_TO_INT((INT_TO_REAL(size) / INT_TO_REAL(frameWidth)) * frameHeight);
- frameWidth = size;
- // given our scaled width and height we need to find the top left point to draw the image at
- ICoord2D screen;
- screen.x = REAL_TO_INT( healthBarRegion->lo.x + (barWidth * 0.5f) - (frameWidth * 0.5f) );
- screen.y = REAL_TO_INT( healthBarRegion->lo.y + barHeight * 0.5f );
- getIconInfo()->m_icon[ ICON_BOMB_REMOTE ]->draw( screen.x, screen.y, frameWidth, frameHeight );
- getIconInfo()->m_keepTillFrame[ ICON_BOMB_REMOTE ] = now + 1;
- }
- }
- }
- }
- }
- if (hasIconInfo())
- {
- if(getIconInfo()->m_keepTillFrame[ ICON_BOMB_TIMED ] <= now )
- {
- killIcon(ICON_BOMB_TIMED);
- }
- if(getIconInfo()->m_keepTillFrame[ ICON_BOMB_REMOTE ] <= now )
- {
- killIcon(ICON_BOMB_REMOTE);
- }
- }
- }
- // ------------------------------------------------------------------------------------------------
- /** Draw any icon information that needs to be drawn */
- // ------------------------------------------------------------------------------------------------
- void Drawable::drawDisabled(const IRegion2D* healthBarRegion)
- {
- const Object *obj = getObject();
- //
- // Disabled Emoticon /Lightning
- // 7/
- if( obj->isDisabledByType( DISABLED_HACKED )
- || obj->isDisabledByType( DISABLED_PARALYZED )
- || obj->isDisabledByType( DISABLED_EMP )
- || obj->isDisabledByType( DISABLED_UNDERPOWERED )
- )
- {
- // create icon if necessary
- if( getIconInfo()->m_icon[ ICON_DISABLED ] == NULL )
- {
- getIconInfo()->m_icon[ ICON_DISABLED ] = newInstance(Anim2D)
- ( s_animationTemplates[ ICON_DISABLED ], TheAnim2DCollection );
- }
- // draw the icon
- if( healthBarRegion )
- {
- Int barHeight = healthBarRegion->hi.y - healthBarRegion->lo.y;
- Int frameWidth = getIconInfo()->m_icon[ ICON_DISABLED ]->getCurrentFrameWidth();
- Int frameHeight = getIconInfo()->m_icon[ ICON_DISABLED ]->getCurrentFrameHeight();
- #ifdef SCALE_ICONS_WITH_ZOOM_ML
- // adjust the width to be a % of the health bar region size
- Int barWidth = healthBarRegion->hi.x - healthBarRegion->lo.x;
- Int size = REAL_TO_INT( barWidth * 0.3f );
- frameHeight = REAL_TO_INT((INT_TO_REAL(size) / INT_TO_REAL(frameWidth)) * frameHeight);
- frameWidth = size;
- #endif
- // given our scaled width and height we need to find the top left point to draw the image at
- ICoord2D screen;
- screen.x = healthBarRegion->lo.x;
- screen.y = healthBarRegion->hi.y - (frameHeight + barHeight);
- getIconInfo()->m_icon[ ICON_DISABLED ]->draw( screen.x, screen.y, frameWidth, frameHeight );
- } // end if
- } // end if
- else
- {
- // delete icon if necessary
- killIcon(ICON_DISABLED);
- } // end if
- }
- //-------------------------------------------------------------------------------------------------
- /** Draw construction percent for drawables that have objects that are "under construction" */
- //-------------------------------------------------------------------------------------------------
- void Drawable::drawConstructPercent( const IRegion2D *healthBarRegion )
- {
- // this data is in an attached object
- Object *obj = getObject();
- if( obj == NULL || BitTest( obj->getStatusBits(), OBJECT_STATUS_UNDER_CONSTRUCTION ) == FALSE ||
- BitTest( obj->getStatusBits(), OBJECT_STATUS_SOLD ) == TRUE )
- {
- // no object, or we are now complete get rid of the string if we have one
- if( m_constructDisplayString )
- {
-
- TheDisplayStringManager->freeDisplayString( m_constructDisplayString );
- m_constructDisplayString = NULL;
- }
- return;
- }
- //if( obj->isEffectivelyDead() )
- //{
- //Don't render icons for dead things.
- // return;
- //}
- // construction is partially complete, allocate a display string if we need one
- if( m_constructDisplayString == NULL )
- m_constructDisplayString = TheDisplayStringManager->newDisplayString();
- // set the string if the value has changed
- if( m_lastConstructDisplayed != obj->getConstructionPercent() )
- {
- UnicodeString buffer;
-
- buffer.format( TheGameText->fetch("CONTROLBAR:UnderConstructionDesc"), obj->getConstructionPercent());
- m_constructDisplayString->setText( buffer );
- // record this percent as our last displayed so we don't un-necessarily rebuild the string
- m_lastConstructDisplayed = obj->getConstructionPercent();
-
- } // end if
- // get center position in drawable
- ICoord2D screen;
- Coord3D pos;
- getDrawableGeometryInfo().getCenterPosition(*getPosition(), pos);
- // convert drawable center position to screen coords
- TheTacticalView->worldToScreen( &pos, &screen );
- // draw the text
- Color color = GameMakeColor( 255, 255, 255, 255 );
- Color dropColor = GameMakeColor( 0, 0, 0, 255 );
- screen.x -= (m_constructDisplayString->getWidth() / 2);
- m_constructDisplayString->draw( screen.x, screen.y, color, dropColor );
- } // end drawConstructPercent
- //-------------------------------------------------------------------------------------------------
- /** Draw caption */
- //-------------------------------------------------------------------------------------------------
- void Drawable::drawCaption( const IRegion2D *healthBarRegion )
- {
- if (!m_captionDisplayString)
- return;
- // get center position in drawable
- ICoord2D screen;
- Coord3D pos;
- getDrawableGeometryInfo().getCenterPosition(*getPosition(), pos);
- // convert drawable center position to screen coords
- TheTacticalView->worldToScreen( &pos, &screen );
- screen.x -= (m_captionDisplayString->getWidth() / 2);
- // draw background
- {
- Int width, xPos;
- Int height, yPos;
- m_captionDisplayString->getSize(&width,&height);
- xPos = screen.x - 1;
- yPos = screen.y - 1;
- TheDisplay->drawFillRect(xPos, yPos, width + 2,height + 2, GameMakeColor(0,0,0,125));
- TheDisplay->drawOpenRect(xPos, yPos, width + 2,height + 2, 1.0, GameMakeColor(20,20,20,255));
- }
- // draw the text
- Color color = TheInGameUI->getDrawableCaptionColor();
- Color dropColor = GameMakeColor( 0, 0, 0, 255 );
- m_captionDisplayString->draw( screen.x, screen.y, color, dropColor );
- } // end drawCaption
- // ------------------------------------------------------------------------------------------------
- /** Draw any veterency markers that should be displayed */
- // ------------------------------------------------------------------------------------------------
- void Drawable::drawVeterancy( const IRegion2D *healthBarRegion )
- {
- // get object from drawble
- Object* obj = getObject();
- if( obj->getExperienceTracker() == NULL )
- {
- //Only objects with experience trackers can possibly have veterancy.
- return;
- }
- VeterancyLevel level = obj->getVeterancyLevel();
- const Image* image = s_veterancyImage[level];
- if (!image)
- return;
- Real scale = 1.3f/CLAMP_ICON_ZOOM_FACTOR( TheTacticalView->getZoom() );
- #ifdef SCALE_ICONS_WITH_ZOOM_ML
- Real objScale = scale * 1.55f;
- #else
- Real objScale = 1.0f;
- #endif
- Real vetBoxWidth = image->getImageWidth()*objScale;
- Real vetBoxHeight = image->getImageHeight()*objScale;
- //
- // take the center position of the object, go down to it's bottom extent, and project
- // that point to the screen, that will be the "center" of our veterancy box
- //
- Coord3D p;
- ICoord2D screenCenter;
- obj->getHealthBoxPosition(p);
- if( !TheTacticalView->worldToScreen( &p, &screenCenter ) )
- return;
- Real healthBoxWidth, healthBoxHeight;
- if (!obj->getHealthBoxDimensions(healthBoxHeight, healthBoxWidth))
- return;
- screenCenter.x += healthBoxWidth * scale * 0.5f;
- // draw the image
- TheDisplay->drawImage(image, screenCenter.x + 1, screenCenter.y + 1, screenCenter.x + 1 + vetBoxWidth, screenCenter.y + 1 + vetBoxHeight);
- } // end drawVeterancy
- // ------------------------------------------------------------------------------------------------
- /** Draw health bar information for drawable */
- // ------------------------------------------------------------------------------------------------
- void Drawable::drawHealthBar(const IRegion2D* healthBarRegion)
- {
- if (!healthBarRegion)
- return;
- //
- // only draw health for selected drawbles and drawables that have been moused over
- // by the cursor
- //
- if( TheGlobalData->m_showObjectHealth &&
- (isSelected() || (TheInGameUI && (TheInGameUI->getMousedOverDrawableID() == getID()))) )
- {
- Object *obj = getObject();
- // if no object, nothing to do
- if( obj == NULL )
- return;
- if( obj->isKindOf( KINDOF_FORCEATTACKABLE ) )
- {
- //Currently (Nov 2002), everything that is forceattackable are civ fences, and they all have a
- //single hit point and they aren't selectable. However, a bug is when you force attack it, it shows
- //the healthbar. Well, this stops it, however, should force attackable kindofs change, then this
- //will require reevaluation.
- return;
- }
- // get body module of object
- BodyModuleInterface *body = obj->getBodyModule();
- // get the health and max health
- Real health = body->getHealth();
- Real maxHealth = body->getMaxHealth();
- // if no max health or health at all we will draw nothing
- if( maxHealth == 0.0f || health == 0.0f )
- return;
- // what is our health ratio
- Real healthRatio = health / maxHealth;
- //
- // what color will we use for the health bar based on our ratio, this makes it
- // slowly go from green to red, (or from blue to cyan if under construction, or disabled)
- //
- Color color, outlineColor;
- if( BitTest( obj->getStatusBits(), OBJECT_STATUS_UNDER_CONSTRUCTION ) || (obj->isDisabled() && !obj->isDisabledByType(DISABLED_HELD)) )
- {
- color = GameMakeColor( 0, healthRatio * 255.0f, 255, 255 );//blue to cyan
- outlineColor = GameMakeColor( 0, healthRatio * 128.0f, 128, 255 );//dark blue to dark cyan
- }
- else //red to green
- {
- RGBColor inColor, outColor;
- inColor.blue = 0; // health bars do not display blue...
- outColor.blue = 0; // health bars do not display blue...
- if( healthRatio >= 0.5f )
- {
- inColor.red = 1.0f - ((healthRatio - 0.5f) / 0.5f);
- inColor.green = 1.0f;
- // color = GameMakeColor ( 255 - ((healthRatio - 0.5f) / 0.5f) * 255, 255, 0, 255 );
- // outlineColor = GameMakeColor( (255 - ((healthRatio - 0.5f) / 0.5f) * 255) * 0.5, 255 * 0.5, 0, 255 );
- }
- else
- {
- inColor.red = 1.0f;
- inColor.green = 1.0f - ((0.5f - healthRatio) / 0.5f);
- // color = GameMakeColor( 255, 255 - ((0.5f - healthRatio) / 0.5f) * 255, 0, 255 );
- // outlineColor = GameMakeColor( 255 * 0.5, (255 - ((0.5f - healthRatio) / 0.5f) * 255) * 0.5, 0, 255 );
- }
- outColor.red = inColor.red * 0.5f;
- outColor.green =inColor.green * 0.5f;
- if( m_conditionState.test( MODELCONDITION_REALLY_DAMAGED ) == TRUE )
- {//average the above color with red
- inColor.red = (1.0f + inColor.red) * 0.5f;
- inColor.green *= 0.5f;
- }
- else if ( m_conditionState.test( MODELCONDITION_DAMAGED ) == FALSE )
- {//average the above color with green
- inColor.green = (1.0f + inColor.green) * 0.5f;
- inColor.red *= 0.5f;
- }
- color = GameMakeColor( 255.0 * inColor.red, 255.0 * inColor.green, 255.0 * inColor.blue, 255);
- outlineColor = GameMakeColor( 255.0 * outColor.red, 255.0 * outColor.green, 255.0 * outColor.blue, 255);
-
-
- }
- /// Real scale = 1.3f / TheTacticalView->getZoom();
- Real healthBoxWidth = healthBarRegion->hi.x - healthBarRegion->lo.x;
-
- Real healthBoxHeight = max(3, healthBarRegion->hi.y - healthBarRegion->lo.y);
- Real healthBoxOutlineSize = 1.0f;
- // draw the health box outline
- TheDisplay->drawOpenRect( healthBarRegion->lo.x, healthBarRegion->lo.y, healthBoxWidth, healthBoxHeight,
- healthBoxOutlineSize, outlineColor );
- // draw a filled bar for the health
- TheDisplay->drawFillRect( healthBarRegion->lo.x + 1, healthBarRegion->lo.y + 1,
- (healthBoxWidth - 2) * healthRatio, healthBoxHeight - 2,
- color );
- } // end if
- } // end drawHealthBar
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void Drawable::clearAndSetModelConditionState( ModelConditionFlagType clr, ModelConditionFlagType set )
- {
- ModelConditionFlags c, s;
- if (clr != MODELCONDITION_INVALID)
- c.set(clr);
- if (set != MODELCONDITION_INVALID)
- s.set(set);
- clearAndSetModelConditionFlags(c, s);
- }
- //-------------------------------------------------------------------------------------------------
- DrawModule** Drawable::getDrawModules()
- {
- DrawModule** dm = (DrawModule**)getModuleList(MODULETYPE_DRAW);
- #ifdef DIRTY_CONDITION_FLAGS
- if (m_isModelDirty)
- {
- if (s_modelLockCount > 0)
- {
- DEBUG_CRASH(("Should not need to update dirty stuff while locked-for-iteration. Ignoring."));
- // this shouldn't happen, but if it does, just return the current (dirty) scenario.
- // we must NOT update the condition state, as someone is relying on the current
- // list of W3D render objects not being munged. (srj)
- }
- else
- {
- for (DrawModule** dm2 = dm; *dm2; ++dm2)
- {
- ObjectDrawInterface* di = (*dm2)->getObjectDrawInterface();
- if (di)
- di->replaceModelConditionState( m_conditionState );
- }
- m_isModelDirty = false;
- }
- }
- #endif
- return dm;
- }
- //-------------------------------------------------------------------------------------------------
- DrawModule const** Drawable::getDrawModules() const
- {
- DrawModule const** dm = (DrawModule const**)getModuleList(MODULETYPE_DRAW);
- #ifdef DIRTY_CONDITION_FLAGS
- if (m_isModelDirty)
- {
- if (s_modelLockCount > 0)
- {
- DEBUG_CRASH(("Should not need to update dirty stuff while locked-for-iteration. Ignoring."));
- // this shouldn't happen, but if it does, just return the current (dirty) scenario.
- // we must NOT update the condition state, as someone is relying on the current
- // list of W3D render objects not being munged. (srj)
- }
- else
- {
- // yeah, yeah, yeah... I know (srj)
- for (DrawModule** dm2 = (DrawModule**)dm; *dm2; ++dm2)
- {
- ObjectDrawInterface* di = (*dm2)->getObjectDrawInterface();
- if (di)
- di->replaceModelConditionState( m_conditionState );
- }
- m_isModelDirty = false;
- }
- }
- #endif
- return dm;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void Drawable::clearAndSetModelConditionFlags(const ModelConditionFlags& clr, const ModelConditionFlags& setf)
- {
- ModelConditionFlags oldFlags = m_conditionState;
- m_conditionState.clearAndSet(clr, setf);
-
- if (m_conditionState == oldFlags)
- return;
- #ifdef DIRTY_CONDITION_FLAGS
- m_isModelDirty = true;
- #else
- for (DrawModule** dm = getDrawModules(); *dm; ++dm)
- {
- ObjectDrawInterface* di = (*dm)->getObjectDrawInterface();
- if (di)
- di->replaceModelConditionState( m_conditionState );
- }
- #endif
- }
- // ------------------------------------------------------------------------------------------------
- // ------------------------------------------------------------------------------------------------
- void Drawable::replaceModelConditionFlags( const ModelConditionFlags &flags, Bool forceReplace )
- {
- //
- // this is a no-op if the new flags are the same as our existing flags (unless we
- // have the forceReplace parameter set, in which case we will force the setting of the
- // new flags)
- //
- if( forceReplace == FALSE && m_conditionState == flags )
- return;
-
- m_conditionState = flags;
- #ifdef DIRTY_CONDITION_FLAGS
- // when forcing a replace we won't use dirty flags, we will immediately do an update now
- if( forceReplace == TRUE )
- {
- for (DrawModule** dm = getDrawModules(); *dm; ++dm)
- {
- ObjectDrawInterface* di = (*dm)->getObjectDrawInterface();
- if (di)
- di->replaceModelConditionState( m_conditionState );
- }
- m_isModelDirty = false;
- }
- else
- m_isModelDirty = true;
- #else
- for (DrawModule** dm = getDrawModules(); *dm; ++dm)
- {
- ObjectDrawInterface* di = (*dm)->getObjectDrawInterface();
- if (di)
- di->replaceModelConditionState( m_conditionState );
- }
- #endif
- }
- //-------------------------------------------------------------------------------------------------
- void Drawable::setIndicatorColor(Color color)
- {
- for (DrawModule** dm = getDrawModules(); *dm; ++dm)
- {
- ObjectDrawInterface* di = (*dm)->getObjectDrawInterface();
- if (di)
- di->replaceIndicatorColor(color);
- }
- }
- // ------------------------------------------------------------------------------------------------
- const GeometryInfo& Drawable::getDrawableGeometryInfo() const
- {
- return getObject() ? getObject()->getGeometryInfo() : getTemplate()->getTemplateGeometryInfo();
- }
- // ------------------------------------------------------------------------------------------------
- /** Set the id for this drawable */
- // ------------------------------------------------------------------------------------------------
- void Drawable::setID( DrawableID id )
- {
- // if id hasn't changed do nothing
- if( m_id == id )
- return;
- // remove this objects previous id from the lookup table
- if( m_id != INVALID_DRAWABLE_ID )
- TheGameClient->removeDrawableFromLookupTable( this );
- // assign new id
- m_id = id;
- // add new id to lookup table
- if( m_id != INVALID_DRAWABLE_ID )
- {
- TheGameClient->addDrawableToLookupTable( this );
- if (m_ambientSound)
- m_ambientSound->m_event.setDrawableID(m_id);
- }
- } // end setID
- // ------------------------------------------------------------------------------------------------
- /** Return drawable ID, this ID is only good on the client */
- // ------------------------------------------------------------------------------------------------
- DrawableID Drawable::getID( void ) const
- {
- // we should never be getting the ID of a drawable who doesn't yet have and ID assigned to it
- DEBUG_ASSERTCRASH( m_id != 0, ("Drawable::getID - Using ID before it was assigned!!!!\n") );
- return m_id;
- } // end get ID
- //-------------------------------------------------------------------------------------------------
- void Drawable::friend_bindToObject( Object *obj ) ///< bind this drawable to an object ID
- {
- m_object = obj;
- if (getObject())
- {
- if (TheGlobalData->m_timeOfDay == TIME_OF_DAY_NIGHT)
- setIndicatorColor(getObject()->getNightIndicatorColor());
- else
- setIndicatorColor(getObject()->getIndicatorColor());
- }
- for (DrawModule** dm = getDrawModules(); *dm; ++dm)
- {
- (*dm)->onDrawableBoundToObject();
- }
- }
- //-------------------------------------------------------------------------------------------------
- // when our Object changes teams, it calls us to let us know, so
- // we can update our model, etc., if necessary. NOTE, we don't guarantee
- // that the new team is different from the old team, nor do we guarantee
- // that the team is nonnull.
- void Drawable::changedTeam()
- {
- Object *object = getObject();
- if( object )
- {
- if (TheGlobalData->m_timeOfDay == TIME_OF_DAY_NIGHT)
- setIndicatorColor( object->getNightIndicatorColor() );
- else
- setIndicatorColor( object->getIndicatorColor() );
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Drawable::setPosition(const Coord3D *pos)
- {
- // extend
- Thing::setPosition(pos);
- }
- //-------------------------------------------------------------------------------------------------
- void Drawable::reactToTransformChange(const Matrix3D* oldMtx, const Coord3D* oldPos, Real oldAngle)
- {
- for (DrawModule** dm = getDrawModules(); *dm; ++dm)
- {
- (*dm)->reactToTransformChange(oldMtx, oldPos, oldAngle);
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Drawable::reactToGeometryChange()
- {
- for (DrawModule** dm = getDrawModules(); *dm; ++dm)
- {
- (*dm)->reactToGeometryChange();
- }
- }
- //-------------------------------------------------------------------------------------------------
- Bool Drawable::handleWeaponFireFX(WeaponSlotType wslot, Int specificBarrelToUse, const FXList* fxl, Real weaponSpeed, Real recoilAmount, Real recoilAngle, const Coord3D* victimPos, Real damageRadius)
- {
- if (recoilAmount != 0.0f)
- {
- // adjust recoil from absolute to relative.
- if (getObject())
- recoilAngle -= getObject()->getOrientation();
- // flip direction 180 degrees.
- recoilAngle += PI;
- if (m_locoInfo)
- {
- m_locoInfo->m_accelerationPitchRate += recoilAmount * Cos(recoilAngle);
- m_locoInfo->m_accelerationRollRate += recoilAmount * Sin(recoilAngle);
- }
- }
- for (DrawModule** dm = getDrawModules(); *dm; ++dm)
- {
- ObjectDrawInterface* di = (*dm)->getObjectDrawInterface();
- if (di && di->handleWeaponFireFX(wslot, specificBarrelToUse, fxl, weaponSpeed, victimPos, damageRadius))
- return true;
- }
- return false;
- }
- //-------------------------------------------------------------------------------------------------
- Int Drawable::getBarrelCount(WeaponSlotType wslot) const
- {
- for (const DrawModule** dm = getDrawModules(); *dm; ++dm)
- {
- const ObjectDrawInterface* di = (*dm)->getObjectDrawInterface();
- Int count = di ? di->getBarrelCount(wslot) : 0;
- if (count != 0)
- return count;
- }
- return 0;
- }
- //-------------------------------------------------------------------------------------------------
- /** Set the Drawable's instance transform */
- //-------------------------------------------------------------------------------------------------
- void Drawable::setInstanceMatrix( const Matrix3D *instance )
- {
- if (instance)
- {
- m_instance = *instance;
- m_instanceIsIdentity = false;
- }
- else
- {
- m_instance.Make_Identity();
- m_instanceIsIdentity = true;
- }
- }
- //-------------------------------------------------------------------------------------------------
- /**
- * Return the Drawable's world transform.
- * If this Drawable is attached to an Object, return the Object's transform instead.
- */
- //-------------------------------------------------------------------------------------------------
- const Matrix3D *Drawable::getTransformMatrix( void ) const
- {
- const Object *obj = getObject();
- if (obj)
- return obj->getTransformMatrix();
- else
- return Thing::getTransformMatrix();
- }
- //-------------------------------------------------------------------------------------------------
- /**
- * Set and clear the drawable's caption text
- */
- //-------------------------------------------------------------------------------------------------
- void Drawable::setCaptionText( const UnicodeString& captionText )
- {
- if (captionText.isEmpty())
- {
- clearCaptionText();
- return;
- }
- UnicodeString sanitizedString = captionText;
- TheLanguageFilter->filterLine(sanitizedString);
- if( m_captionDisplayString == NULL )
- {
- m_captionDisplayString = TheDisplayStringManager->newDisplayString();
- GameFont *font = TheFontLibrary->getFont(
- TheInGameUI->getDrawableCaptionFontName(),
- TheGlobalLanguageData->adjustFontSize(TheInGameUI->getDrawableCaptionPointSize()),
- TheInGameUI->isDrawableCaptionBold() );
- m_captionDisplayString->setFont( font );
- m_captionDisplayString->setText( sanitizedString );
- }
- else
- {
- // set the string if the value has changed
- if( m_captionDisplayString->getText().compare(sanitizedString) != 0 )
- {
- m_captionDisplayString->setText( sanitizedString );
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Drawable::clearCaptionText( void )
- {
- if (m_captionDisplayString)
- TheDisplayStringManager->freeDisplayString(m_captionDisplayString);
- m_captionDisplayString = NULL;
- }
- //-------------------------------------------------------------------------------------------------
- UnicodeString Drawable::getCaptionText( void )
- {
- if (m_captionDisplayString)
- return m_captionDisplayString->getText();
- return UnicodeString::TheEmptyString;
- }
- //-------------------------------------------------------------------------------------------------
- /** Attach and start playing an ambient sound to this drawable */
- //-------------------------------------------------------------------------------------------------
- void Drawable::setTimeOfDay(TimeOfDay tod)
- {
- BodyDamageType dt = BODY_PRISTINE;
- if (getObject() && getObject()->getBodyModule())
- dt = getObject()->getBodyModule()->getDamageState();
- startAmbientSound(dt, tod);
- ModelConditionFlags c = m_conditionState;
- c.set(MODELCONDITION_NIGHT, (tod == TIME_OF_DAY_NIGHT) ? 1 : 0);
- replaceModelConditionFlags(c);
- }
- //-------------------------------------------------------------------------------------------------
- /** Attach and start playing an ambient sound to this drawable */
- //-------------------------------------------------------------------------------------------------
- void Drawable::startAmbientSound(BodyDamageType dt, TimeOfDay tod)
- {
- stopAmbientSound();
- //Get the specific ambient sound for the damage type.
- const AudioEventRTS& audio = getAmbientSoundByDamage(dt);
- Bool trySound = FALSE;
- if( audio.getEventName().isNotEmpty() )
- {
- if (m_ambientSound == NULL)
- m_ambientSound = newInstance(DynamicAudioEventRTS);
- (m_ambientSound->m_event) = audio;
- trySound = TRUE;
- }
- else if( dt != BODY_PRISTINE && dt != BODY_RUBBLE )
- {
- //If the ambient sound was absent in the case of non-pristine damage types,
- //try getting the pristine one. Most of our cases actually specify just the
- //pristine sound and want to use it for all states (except dead/rubble).
- const AudioEventRTS& pristineAudio = getAmbientSoundByDamage( BODY_PRISTINE );
- if( pristineAudio.getEventName().isNotEmpty() )
- {
- if (m_ambientSound == NULL)
- m_ambientSound = newInstance(DynamicAudioEventRTS);
- (m_ambientSound->m_event) = pristineAudio;
- trySound = TRUE;
- }
- }
-
- if( trySound && m_ambientSound )
- {
- const AudioEventInfo *info = m_ambientSound->m_event.getAudioEventInfo();
- if( info )
- {
- if( BitTest( info->m_type, ST_GLOBAL) || info->m_priority == AP_CRITICAL )
- {
- //Play it anyways.
- m_ambientSound->m_event.setDrawableID(getID());
- m_ambientSound->m_event.setTimeOfDay(tod);
- m_ambientSound->m_event.setPlayingHandle(TheAudio->addAudioEvent( &m_ambientSound->m_event ));
- }
- else
- {
- //Check if it's close enough to try playing (optimization)
- Coord3D vector = *getPosition();
- vector.sub( TheAudio->getListenerPosition() );
- Real distSqr = vector.lengthSqr();
- if( distSqr < sqr( info->m_maxDistance ) )
- {
- m_ambientSound->m_event.setDrawableID(getID());
- m_ambientSound->m_event.setTimeOfDay(tod);
- m_ambientSound->m_event.setPlayingHandle(TheAudio->addAudioEvent( &m_ambientSound->m_event ));
- }
- }
- }
- else
- {
- DEBUG_CRASH( ("Ambient sound %s missing! Skipping...", m_ambientSound->m_event.getEventName().str() ) );
- m_ambientSound->deleteInstance();
- m_ambientSound = NULL;
- }
- }
- }
- //-------------------------------------------------------------------------------------------------
- // Attach and start playing an ambient sound to this drawable. Calculates states automatically.
- //-------------------------------------------------------------------------------------------------
- void Drawable::startAmbientSound()
- {
- stopAmbientSound();
- BodyDamageType bodyCondition = BODY_PRISTINE;
- Object *obj = getObject();
- if( obj )
- {
- bodyCondition = obj->getBodyModule()->getDamageState();
- }
- startAmbientSound( bodyCondition, TheGlobalData->m_timeOfDay );
- }
- //-------------------------------------------------------------------------------------------------
- /** Stop playing the drawables ambient sound if it has one */
- //-------------------------------------------------------------------------------------------------
- void Drawable::stopAmbientSound( void )
- {
- if (m_ambientSound)
- TheAudio->removeAudioEvent(m_ambientSound->m_event.getPlayingHandle());
- }
- //-------------------------------------------------------------------------------------------------
- void Drawable::enableAmbientSound( Bool enable )
- {
- if( m_ambientSoundEnabled == enable )
- {
- return;
- }
- m_ambientSoundEnabled = enable;
- if( enable )
- {
- startAmbientSound();
- }
- else
- {
- stopAmbientSound();
- }
- }
- //-------------------------------------------------------------------------------------------------
- /** add self to the linked list */
- //-------------------------------------------------------------------------------------------------
- void Drawable::prependToList(Drawable **pListHead)
- {
- // add the object to the global list
- m_prevDrawable = NULL;
- m_nextDrawable = *pListHead;
- if (*pListHead)
- (*pListHead)->m_prevDrawable = this;
- *pListHead = this;
- }
- //-------------------------------------------------------------------------------------------------
- /** remove self from the linked list */
- //-------------------------------------------------------------------------------------------------
- void Drawable::removeFromList(Drawable **pListHead)
- {
- if (m_nextDrawable)
- m_nextDrawable->m_prevDrawable = m_prevDrawable;
- if (m_prevDrawable)
- m_prevDrawable->m_nextDrawable = m_nextDrawable;
- else
- *pListHead = m_nextDrawable;
- }
- //-------------------------------------------------------------------------------------------------
- //-------------------------------------------------------------------------------------------------
- void Drawable::updateHiddenStatus()
- {
- Bool hidden = m_hidden || m_hiddenByStealth;
- if( hidden )
- TheInGameUI->deselectDrawable( this );
- for (DrawModule** dm = getDrawModules(); *dm; ++dm)
- {
- ObjectDrawInterface* di = (*dm)->getObjectDrawInterface();
- if (di)
- di->setHidden(hidden != 0);
- }
-
- }
- //-------------------------------------------------------------------------------------------------
- /** Hide or un-hide drawable */
- //-------------------------------------------------------------------------------------------------
- void Drawable::setDrawableHidden( Bool hidden )
- {
- if (hidden != m_hidden)
- {
- m_hidden = hidden;
- updateHiddenStatus();
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Drawable::updateDrawableClipStatus( UnsignedInt shotsRemaining, UnsignedInt maxShots, WeaponSlotType slot )
- {
- for (DrawModule** dm = getDrawModules(); *dm; ++dm)
- {
- ObjectDrawInterface* di = (*dm)->getObjectDrawInterface();
- if (di)
- di->updateProjectileClipStatus(shotsRemaining, maxShots, slot);
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Drawable::updateDrawableSupplyStatus( Int maxSupply, Int currentSupply )
- {
- for (DrawModule** dm = getDrawModules(); *dm; ++dm)
- {
- ObjectDrawInterface* di = (*dm)->getObjectDrawInterface();
- if (di)
- di->updateDrawModuleSupplyStatus( maxSupply, currentSupply );
- }
- }
- //-------------------------------------------------------------------------------------------------
- void Drawable::notifyDrawableDependencyCleared()
- {
- for (DrawModule** dm = getDrawModules(); *dm; ++dm)
- {
- ObjectDrawInterface* di = (*dm)->getObjectDrawInterface();
- if (di)
- di->notifyDrawModuleDependencyCleared();
- }
- }
- //-------------------------------------------------------------------------------------------------
- /** Set as selectable or not. */
- //-------------------------------------------------------------------------------------------------
- void Drawable::setSelectable( Bool selectable )
- {
- // unselct drawable if it is no longer selectable.
- if( !selectable )
- TheInGameUI->deselectDrawable( this );
- for (DrawModule** dm = getDrawModules(); *dm; ++dm)
- {
- ObjectDrawInterface* di = (*dm)->getObjectDrawInterface();
- if (di)
- di->setSelectable(selectable);
- }
- }
- //-------------------------------------------------------------------------------------------------
- /** Return whether or not this Drawable is selectable. */
- //-------------------------------------------------------------------------------------------------
- Bool Drawable::isSelectable( void ) const
- {
- return getObject() && getObject()->isSelectable();
- }
- //-------------------------------------------------------------------------------------------------
- /** Return whether or not this Drawable is selectable as part of a group. */
- //-------------------------------------------------------------------------------------------------
- Bool Drawable::isMassSelectable( void ) const
- {
- return getObject() && getObject()->isMassSelectable();
- }
- //-------------------------------------------------------------------------------------------------
- /** Preload all our assets that we can for all our possible states in this time of day */
- //-------------------------------------------------------------------------------------------------
- void Drawable::preloadAssets( TimeOfDay timeOfDay )
- {
- /// walk all our modules and preload any assets we need to
- for( Int i = 0; i < NUM_DRAWABLE_MODULE_TYPES; ++i )
- for( Module** m = m_modules[i]; m && *m; ++m )
- (*m)->preloadAssets( timeOfDay );
- } // end preloadAssets
- //-------------------------------------------------------------------------------------------------
- // Simply searches for the first occurrence of a specified client update module.
- //-------------------------------------------------------------------------------------------------
- ClientUpdateModule* Drawable::findClientUpdateModule( NameKeyType key )
- {
- ClientUpdateModule **clientModules = getClientUpdateModules();
- if( clientModules )
- {
- while( *clientModules )
- {
- if( (*clientModules)->getModuleNameKey() == key )
- {
- return *clientModules;
- }
- }
- }
- return NULL;
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void Drawable::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer the drawable modules
- * Version Info:
- * 1: Initial version */
- // ------------------------------------------------------------------------------------------------
- void Drawable::xferDrawableModules( Xfer *xfer )
- {
- // version
- const XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- //
- // when using dirty condition flags ... we want to make sure that the modules are updated to
- // the current state of the drawable
- //
- #ifdef DIRTY_CONDITION_FLAGS
- if( xfer->getXferMode() == XFER_SAVE )
- getDrawModules(); // will re-evaluate modules that are dirty and update them
- #endif
- // xfer number of module types
- UnsignedShort moduleTypes = NUM_DRAWABLE_MODULE_TYPES;
- xfer->xferUnsignedShort( &moduleTypes );
- // xfer each set of modules for each type
- AsciiString moduleIdentifier;
- for( UnsignedShort curModuleType = 0; curModuleType < moduleTypes; ++curModuleType )
- {
- // how many modules are here for this type
- Module **m;
- UnsignedShort moduleCount = 0;
- for( m = m_modules[ curModuleType ]; m && *m; ++m )
- moduleCount++;
- xfer->xferUnsignedShort( &moduleCount );
- // xfer each module data
- if( xfer->getXferMode() == XFER_SAVE )
- {
- // save each module
- for( m = m_modules[ curModuleType ]; m && *m; ++m )
- {
- // write module identifier
- moduleIdentifier = TheNameKeyGenerator->keyToName( (*m)->getModuleTagNameKey() );
- DEBUG_ASSERTCRASH( moduleIdentifier != AsciiString::TheEmptyString,
- ("Drawable::xferDrawableModules - module name key does not translate to a string!\n") );
- xfer->xferAsciiString( &moduleIdentifier );
- // begin data block
- xfer->beginBlock();
- // xfer data
- xfer->xferSnapshot( *m );
- // end data block
- xfer->endBlock();
- } // end for, m
- } // end if, save
- else
- {
- // read each module
- for( UnsignedShort j = 0; j < moduleCount; ++j )
- {
- // read module identifier
- xfer->xferAsciiString( &moduleIdentifier );
- NameKeyType moduleIdentifierKey = TheNameKeyGenerator->nameToKey(moduleIdentifier);
- // find module in the drawable module list
- Module* module = NULL;
- for( Module **m = m_modules[curModuleType]; m && *m; ++m )
- {
- if (moduleIdentifierKey == (*m)->getModuleTagNameKey())
- {
- module = *m;
- break; // exit for m
- } // end if
- } // end for, m
- // new block of data
- Int dataSize = xfer->beginBlock();
- //
- // if we didn't find the module, it's quite possible that we have removed
- // it from the object definition in a future patch, if that is so, we need to
- // skip the module data in the file
- //
- if( module == NULL )
- {
- // for testing purposes, this module better be found
- DEBUG_CRASH(( "Drawable::xferDrawableModules - Module '%s' was indicated in file, but not found on Drawable %s %d\n",
- moduleIdentifier.str(), getTemplate()->getName().str(),getID() ));
- // skip this data in the file
- xfer->skip( dataSize );
- } // end if
- else
- {
- // xfer the data into this module
- xfer->xferSnapshot( module );
- } // end else
- // end of data block
- xfer->endBlock();
- } // end for j
- } // end else, load
- } // end for curModuleType
- } // end xferDrawableModules
- // ------------------------------------------------------------------------------------------------
- /** Xfer method
- * Version Info;
- * 1: Initial version
- * 2: Moved condition state xfer before module xfer so we can restore anim frame
- * during the module xfer (CBD)
- * 4: Added m_ambientSoundEnabled flag
- * 5: save full mtx, not pos+orient.
- */
- // ------------------------------------------------------------------------------------------------
- void Drawable::xfer( Xfer *xfer )
- {
- // version
- const XferVersion currentVersion = 5;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- //Wow, because the constructor creates the ambient sound, the xfer can
- //change the ID the sound points to, therefore, we must remove it now
- //and restore it in loadPostProcess().
- if( xfer->getXferMode() == XFER_LOAD && m_ambientSound )
- {
- TheAudio->killAudioEventImmediately( m_ambientSound->m_event.getPlayingHandle() );
- m_ambientSound->deleteInstance();
- m_ambientSound = NULL;
- }
- // drawable id
- DrawableID id = getID();
- xfer->xferDrawableID( &id );
- setID( id );
- // condition state, note that when we're loading we need to force a replace of these flags
- if( version >= 2 )
- {
- m_conditionState.xfer( xfer );
- if( xfer->getXferMode() == XFER_LOAD )
- replaceModelConditionFlags( m_conditionState, TRUE );
- } // end if
- if( version >= 3 )
- {
- if (version >= 5)
- {
- Matrix3D mtx = *getTransformMatrix();
- xfer->xferMatrix3D(&mtx);
- setTransformMatrix(&mtx);
- }
- else
- {
- // position
- Coord3D pos = *getPosition();
- xfer->xferCoord3D( &pos );
- setPosition( &pos );
- // orientation
- Real orientation = getOrientation();
- xfer->xferReal( &orientation );
- setOrientation( orientation );
- }
- }
- // selection flash envelope
- Bool selFlash = (m_selectionFlashEnvelope != NULL);
- xfer->xferBool( &selFlash );
- if( selFlash )
- {
- // allocate selection flash envelope if we need to
- if( m_selectionFlashEnvelope == NULL )
- m_selectionFlashEnvelope = newInstance( TintEnvelope );
- // xfer
- xfer->xferSnapshot( m_selectionFlashEnvelope );
- } // end if
- // color tint envelope
- Bool colFlash = (m_colorTintEnvelope != NULL);
- xfer->xferBool( &colFlash );
- if( colFlash )
- {
- // allocate envelope if we need to
- if( m_colorTintEnvelope == NULL )
- m_colorTintEnvelope = newInstance( TintEnvelope );
- // xfer
- xfer->xferSnapshot( m_colorTintEnvelope );
- } // end if
- // terrain decal type
- TerrainDecalType decal = getTerrainDecalType();
- xfer->xferUser( &decal, sizeof( TerrainDecalType ) );
- if( xfer->getXferMode() == XFER_LOAD )
- setTerrainDecal( decal );
- // explicit opacity
- xfer->xferReal( &m_explicitOpacity );
- // stealth opacity
- xfer->xferReal( &m_stealthOpacity );
- // effective stealth opacity
- xfer->xferReal( &m_effectiveStealthOpacity );
- // decalOpacityFadeTarget
- xfer->xferReal( &m_decalOpacityFadeTarget );
- // decalOpacityFadeRate
- xfer->xferReal( &m_decalOpacityFadeRate );
- // decalOpacityFadeRate
- xfer->xferReal( &m_decalOpacity );
- // object (if present)
- ObjectID objectID = m_object ? m_object->getID() : INVALID_ID;
- xfer->xferObjectID( &objectID );
- // sanity
- if( xfer->getXferMode() == XFER_LOAD )
- {
- if( m_object )
- {
- if( objectID != m_object->getID() )
- {
-
- DEBUG_CRASH(( "Drawable::xfer - Drawable '%s' is attached to wrong object '%s'\n",
- getTemplate()->getName().str(), m_object->getTemplate()->getName().str() ));
- throw SC_INVALID_DATA;
- } // end if
- } // end if
- else
- {
- if( objectID != INVALID_ID )
- {
- #ifdef DEBUG_CRASHING
- Object *obj = TheGameLogic->findObjectByID( objectID );
- DEBUG_CRASH(( "Drawable::xfer - Drawable '%s' is not attached to an object but should be attached to object '%s' with id '%d'\n",
- getTemplate()->getName().str(),
- obj ? obj->getTemplate()->getName().str() : "Unknown",
- objectID ));
- #endif
- throw SC_INVALID_DATA;
- } // end if
- } // end else
- } // end if
- // particle
- // we don't need to worry about this, the particle itself will set it upon loading
- // selected
- // we won't worry about selection, we'll let TheInGameUI take care of it all
- // status
- xfer->xferUnsignedInt( &m_status );
- // tint status
- xfer->xferUnsignedInt( &m_tintStatus );
- // prev tint status
- xfer->xferUnsignedInt( &m_prevTintStatus );
- // fading mode
- xfer->xferUser( &m_fadeMode, sizeof( FadingMode ) );
- // time elapsed fade
- xfer->xferUnsignedInt( &m_timeElapsedFade );
- // time to fade
- xfer->xferUnsignedInt( &m_timeToFade );
- Bool hasLocoInfo = (m_locoInfo != NULL);
- xfer->xferBool( &hasLocoInfo );
- if (hasLocoInfo)
- {
- if( xfer->getXferMode() == XFER_LOAD && m_locoInfo == NULL )
- m_locoInfo = newInstance(DrawableLocoInfo);
- // pitch
- xfer->xferReal( &m_locoInfo->m_pitch );
- // pitch rate
- xfer->xferReal( &m_locoInfo->m_pitchRate );
- // roll
- xfer->xferReal( &m_locoInfo->m_roll );
- // roll rate
- xfer->xferReal( &m_locoInfo->m_rollRate );
- // yaw
- xfer->xferReal( &m_locoInfo->m_yaw );
- // acceleration pitch
- xfer->xferReal( &m_locoInfo->m_accelerationPitch );
- // acceleration pitch rate
- xfer->xferReal( &m_locoInfo->m_accelerationPitchRate );
- // acceleration roll
- xfer->xferReal( &m_locoInfo->m_accelerationRoll );
- // acceleration roll rate
- xfer->xferReal( &m_locoInfo->m_accelerationRollRate );
- // overlap z vel
- xfer->xferReal( &m_locoInfo->m_overlapZVel );
- // overlap z
- xfer->xferReal( &m_locoInfo->m_overlapZ );
- // wobble
- xfer->xferReal( &m_locoInfo->m_wobble );
- // wheel info
- xfer->xferReal( &m_locoInfo->m_wheelInfo.m_frontLeftHeightOffset );
- xfer->xferReal( &m_locoInfo->m_wheelInfo.m_frontRightHeightOffset );
- xfer->xferReal( &m_locoInfo->m_wheelInfo.m_rearLeftHeightOffset );
- xfer->xferReal( &m_locoInfo->m_wheelInfo.m_rearRightHeightOffset );
- xfer->xferReal( &m_locoInfo->m_wheelInfo.m_wheelAngle );
- xfer->xferInt( &m_locoInfo->m_wheelInfo.m_framesAirborneCounter );
- xfer->xferInt( &m_locoInfo->m_wheelInfo.m_framesAirborne );
- }
- // modules
- xferDrawableModules( xfer );
- // stealth look
- xfer->xferUser( &m_stealthLook, sizeof( StealthLookType ) );
- // flash count
- xfer->xferInt( &m_flashCount );
- // flash color
- xfer->xferColor( &m_flashColor );
- // hidden
- xfer->xferBool( &m_hidden );
- // hidden by stealth
- xfer->xferBool( &m_hiddenByStealth );
- // heat vision opacity
- xfer->xferReal( &m_heatVisionOpacity );
- // instance is identity
- xfer->xferBool( &m_instanceIsIdentity );
- // instance matrix
- xfer->xferUser( &m_instance, sizeof( Matrix3D ) );
- // instance scale
- xfer->xferReal( &m_instanceScale );
- // drawable Info - mostly hold FOW related data.
- xfer->xferObjectID(&m_drawableInfo.m_shroudStatusObjectID);
- // we do not need to save m_drawableInfo
- // m_drawableInfo <--- do nothing with this
- // condition state used to be here so we must keep it here for compatibility
- if( version < 2 )
- {
- // sanity, we don't write old versions we can only read them
- DEBUG_ASSERTCRASH( xfer->getXferMode() == XFER_LOAD,
- ("Drawable::xfer - Writing an old format!!!\n") );
- // condition state, note that when we're loading we need to force a replace of these flags
- m_conditionState.xfer( xfer );
- if( xfer->getXferMode() == XFER_LOAD )
- replaceModelConditionFlags( m_conditionState, TRUE );
- } // end if
- // expiration date
- xfer->xferUnsignedInt( &m_expirationDate );
- // icon count
- UnsignedByte iconCount = 0;
- if (hasIconInfo())
- {
- for( UnsignedByte i = 0; i < MAX_ICONS; ++i )
- if( getIconInfo()->m_icon[ i ] )
- iconCount++;
- }
- xfer->xferUnsignedByte( &iconCount );
- // icon data
- AsciiString iconIndexName;
- AsciiString iconTemplateName;
- UnsignedInt iconKeepFrame;
- if( xfer->getXferMode() == XFER_SAVE )
- {
- for( UnsignedByte i = 0; i < MAX_ICONS; ++i )
- {
- // skip empty icon slots
- if( !hasIconInfo() || getIconInfo()->m_icon[ i ] == NULL )
- continue;
- // icon index name
- iconIndexName.set( drawableIconIndexToName( (DrawableIconType)i ) );
- xfer->xferAsciiString( &iconIndexName );
- // keep till frame
- iconKeepFrame = getIconInfo()->m_keepTillFrame[ i ];
- xfer->xferUnsignedInt( &iconKeepFrame );
- // icon template name
- iconTemplateName = getIconInfo()->m_icon[ i ]->getAnimTemplate()->getName();
- xfer->xferAsciiString( &iconTemplateName );
- // icon data
- xfer->xferSnapshot( getIconInfo()->m_icon[ i ] );
- } // end for, i
- } // end if, save
- else
- {
- Int i;
- // destroy any icons that might be present right now in favor of what we'll load from the file
- if (hasIconInfo())
- getIconInfo()->clear();
- // read each data segment from the file
- DrawableIconType iconIndex;
- Anim2DTemplate *animTemplate;
- for( i = 0; i < iconCount; ++i )
- {
- // icon index name
- xfer->xferAsciiString( &iconIndexName );
- iconIndex = drawableIconNameToIndex( iconIndexName.str() );
- // keep till frame
- xfer->xferUnsignedInt( &iconKeepFrame );
- getIconInfo()->m_keepTillFrame[ iconIndex ] = iconKeepFrame;
- // icon template name
- xfer->xferAsciiString( &iconTemplateName );
- animTemplate = TheAnim2DCollection->findTemplate( iconTemplateName );
- if( animTemplate == NULL )
- {
- DEBUG_CRASH(( "Drawable::xfer - Unknown icon template '%s'\n", iconTemplateName.str() ));
- throw SC_INVALID_DATA;
- } // end if
- // create icon
- getIconInfo()->m_icon[ iconIndex ] = newInstance(Anim2D)( animTemplate, TheAnim2DCollection );
- // icon data
- xfer->xferSnapshot( getIconInfo()->m_icon[ iconIndex ] );
- } // end for, i
- } // end else, load
- if( xfer->getXferMode() == XFER_LOAD )
- {
- // On load, we want to set it to none, because stealthlook updates
- // when it changes. So in the next stealth update, it will be set to
- // it's correct value, and the drawable updated (hide shadows and such). jba.
- m_stealthLook = STEALTHLOOK_NONE;
- // Also, need to update the hidden status for all sub-modules.
- if (m_hidden || m_hiddenByStealth) {
- updateHiddenStatus();
- }
- }
- //
- // when saving we should never have dirty modules, but when loading we will force the modules
- // to be dirty just to be sure that they get re-evaluated after the load
- //
- #ifdef DIRTY_CONDITION_FLAGS
- if( xfer->getXferMode() == XFER_SAVE )
- DEBUG_ASSERTCRASH( m_isModelDirty == FALSE, ("Drawble::xfer - m_isModelDirty is not FALSE!\n") );
- else
- m_isModelDirty = TRUE;
- #endif
- if( version >= 4 )
- {
- xfer->xferBool( &m_ambientSoundEnabled );
- }
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load post process */
- // ------------------------------------------------------------------------------------------------
- void Drawable::loadPostProcess( void )
- {
- // if we have an object, we don't need to save/load the pos, just restore it.
- // if we don't, we'd better save it!
- if (m_object != NULL)
- {
- setTransformMatrix(m_object->getTransformMatrix());
- }
-
- if( m_ambientSoundEnabled )
- {
- startAmbientSound();
- }
- else
- {
- stopAmbientSound();
- }
- } // end loadPostProcess
- //=================================================================================================
- //=================================================================================================
- #ifdef DIRTY_CONDITION_FLAGS
- /*static*/ void Drawable::friend_lockDirtyStuffForIteration()
- {
- if (s_modelLockCount == 0)
- {
- if (TheGameClient) // WB has no GameClient!
- {
- for (Drawable* d = TheGameClient->firstDrawable(); d != NULL; d = d->getNextDrawable())
- {
- // this will force us to update stuff.
- d->getDrawModules();
- }
- }
- }
- ++s_modelLockCount;
- }
- #endif
- //=================================================================================================
- //=================================================================================================
- #ifdef DIRTY_CONDITION_FLAGS
- /*static*/ void Drawable::friend_unlockDirtyStuffForIteration()
- {
- if (s_modelLockCount > 0)
- --s_modelLockCount;
- }
- #endif
- //=================================================================================================
- //=================================================================================================
- TintEnvelope::TintEnvelope(void)
- {
- m_attackRate.Set(0,0,0);
- m_decayRate.Set(0,0,0);
- m_peakColor.Set(0,0,0);
- m_currentColor.Set(0,0,0);
- m_envState = ENVELOPE_STATE_REST;
- m_sustainCounter = 0;
- m_affect = FALSE;
- }
- //-------------------------------------------------------------------------------------------------
- const Real FADE_RATE_EPSILON = (0.001f);
- //-------------------------------------------------------------------------------------------------
- void TintEnvelope::play(const RGBColor *peak, UnsignedInt atackFrames, UnsignedInt decayFrames, UnsignedInt sustainAtPeak )
- {
- setPeakColor( peak );
- setAttackFrames( atackFrames );
- setDecayFrames( decayFrames );
- m_envState = ENVELOPE_STATE_ATTACK;
- m_sustainCounter = sustainAtPeak;
- m_affect = TRUE;
- Vector3 delta;
- Vector3::Subtract(m_currentColor, m_peakColor, &delta);
- if ( delta.Length() <= FADE_RATE_EPSILON ) // we are practically already at this color
- m_envState = ENVELOPE_STATE_SUSTAIN;
- }
- //-------------------------------------------------------------------------------------------------
- void TintEnvelope::setAttackFrames(UnsignedInt frames)
- {
- Real recipFrames = 1.0f / (Real)MAX(1,frames);
- m_attackRate.Set( m_currentColor );
- Vector3::Subtract( m_peakColor, m_attackRate, &m_attackRate);
- m_attackRate.Scale( Vector3(recipFrames, recipFrames, recipFrames) );
- }
- //-------------------------------------------------------------------------------------------------
- void TintEnvelope::setDecayFrames( UnsignedInt frames )
- {
- Real recipFrames = ( -1.0f ) / (Real)MAX(1,frames);
- m_decayRate.Set( m_peakColor );
- m_decayRate.Scale( Vector3(recipFrames, recipFrames, recipFrames) );
- }
- //-------------------------------------------------------------------------------------------------
- void TintEnvelope::update(void)
- {
- switch ( m_envState )
- {
- case ( ENVELOPE_STATE_REST ) : //most likely case
- {
- m_currentColor.Set(0,0,0);
- m_affect = FALSE;
- break;
- }
- case ( ENVELOPE_STATE_DECAY ) : // much more likely than attack
- {
- if (m_decayRate.Length() > m_currentColor.Length() || m_currentColor.Length() <= FADE_RATE_EPSILON) //we are at rest
- {
- m_envState = ENVELOPE_STATE_REST;
- m_affect = FALSE;
- }
- else
- {
- Vector3::Add( m_decayRate, m_currentColor, &m_currentColor );//Add the decayRate to the current color;
- m_affect = TRUE;
- }
- break;
- }
- case ( ENVELOPE_STATE_ATTACK ) :
- {
- Vector3 delta;
- Vector3::Subtract(m_currentColor, m_peakColor, &delta);
-
- if (m_attackRate.Length() > delta.Length() || delta.Length() <= FADE_RATE_EPSILON) //we are at the peak
- {
- if ( m_sustainCounter )
- {
- m_envState = ENVELOPE_STATE_SUSTAIN;
- }
- else
- {
- m_envState = ENVELOPE_STATE_DECAY;
- }
- }
- else
- {
- Vector3::Add( m_attackRate, m_currentColor, &m_currentColor );//Add the attackRate to the current color;
- m_affect = TRUE;
- }
-
- break;
- }
- case ( ENVELOPE_STATE_SUSTAIN ) :
- {
- if ( m_sustainCounter > 0 )
- --m_sustainCounter;
- else
- release();
-
- break;
- }
- default:
- {
- //do nothing, we are sustaining until externally triggered to release (decay)
- break;
- }
- }
- // here we transition the color from current to peak to release, according to
- }
- // ------------------------------------------------------------------------------------------------
- /** CRC */
- // ------------------------------------------------------------------------------------------------
- void TintEnvelope::crc( Xfer *xfer )
- {
- } // end crc
- // ------------------------------------------------------------------------------------------------
- /** Xfer Method
- * Version Info;
- * 1: Initial version */
- // ------------------------------------------------------------------------------------------------
- void TintEnvelope::xfer( Xfer *xfer )
- {
- // version
- XferVersion currentVersion = 1;
- XferVersion version = currentVersion;
- xfer->xferVersion( &version, currentVersion );
- // attack rate
- xfer->xferUser( &m_attackRate, sizeof( Vector3 ) );
- // decay rate
- xfer->xferUser( &m_decayRate, sizeof( Vector3 ) );
- // peak color
- xfer->xferUser( &m_peakColor, sizeof( Vector3 ) );
- // current color
- xfer->xferUser( &m_currentColor, sizeof( Vector3 ) );
- // sustain counter
- xfer->xferUnsignedInt( &m_sustainCounter );
- // affect
- xfer->xferBool( &m_affect );
- // state
- xfer->xferByte( &m_envState );
- } // end xfer
- // ------------------------------------------------------------------------------------------------
- /** Load Post Process */
- // ------------------------------------------------------------------------------------------------
- void TintEnvelope::loadPostProcess( void )
- {
- } // end loadPostProcess
|