InGameUI.cpp 193 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962496349644965496649674968496949704971497249734974497549764977497849794980498149824983498449854986498749884989499049914992499349944995499649974998499950005001500250035004500550065007500850095010501150125013501450155016501750185019502050215022502350245025502650275028502950305031503250335034503550365037503850395040504150425043504450455046504750485049505050515052505350545055505650575058505950605061506250635064506550665067506850695070507150725073507450755076507750785079508050815082508350845085508650875088508950905091509250935094509550965097509850995100510151025103510451055106510751085109511051115112511351145115511651175118511951205121512251235124512551265127512851295130513151325133513451355136513751385139514051415142514351445145514651475148514951505151515251535154515551565157515851595160516151625163516451655166516751685169517051715172517351745175517651775178517951805181518251835184518551865187518851895190519151925193519451955196519751985199520052015202520352045205520652075208520952105211521252135214521552165217521852195220522152225223522452255226522752285229523052315232523352345235523652375238523952405241524252435244524552465247524852495250525152525253525452555256525752585259526052615262526352645265526652675268526952705271527252735274527552765277527852795280528152825283528452855286528752885289529052915292529352945295529652975298529953005301530253035304530553065307530853095310531153125313531453155316531753185319532053215322532353245325532653275328532953305331533253335334533553365337533853395340534153425343534453455346534753485349535053515352535353545355535653575358535953605361536253635364536553665367536853695370537153725373537453755376537753785379538053815382538353845385538653875388538953905391539253935394539553965397539853995400540154025403540454055406540754085409541054115412541354145415541654175418541954205421542254235424542554265427542854295430543154325433543454355436543754385439544054415442544354445445544654475448544954505451545254535454545554565457545854595460546154625463546454655466546754685469547054715472547354745475547654775478547954805481548254835484548554865487548854895490549154925493549454955496549754985499550055015502550355045505550655075508550955105511551255135514551555165517551855195520552155225523552455255526552755285529553055315532553355345535553655375538553955405541554255435544554555465547554855495550555155525553555455555556555755585559556055615562556355645565556655675568556955705571557255735574557555765577557855795580558155825583558455855586558755885589559055915592559355945595559655975598559956005601560256035604560556065607560856095610561156125613561456155616561756185619562056215622562356245625562656275628562956305631563256335634563556365637563856395640564156425643564456455646564756485649565056515652565356545655565656575658565956605661566256635664566556665667566856695670567156725673567456755676567756785679568056815682568356845685568656875688568956905691569256935694569556965697569856995700570157025703570457055706570757085709571057115712
  1. /*
  2. ** Command & Conquer Generals Zero Hour(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. ////////////////////////////////////////////////////////////////////////////////
  19. // //
  20. // (c) 2001-2003 Electronic Arts Inc. //
  21. // //
  22. ////////////////////////////////////////////////////////////////////////////////
  23. // InGameUI.cpp ///////////////////////////////////////////////////////////////////////////////////
  24. // Implementation of in-game user interface singleton inteface
  25. // Author: Michael S. Booth, March 2001
  26. ///////////////////////////////////////////////////////////////////////////////////////////////////
  27. #include "PreRTS.h" // This must go first in EVERY cpp file int the GameEngine
  28. #define DEFINE_SHADOW_NAMES
  29. #include "Common/ActionManager.h"
  30. #include "Common/GameAudio.h"
  31. #include "Common/GameEngine.h"
  32. #include "Common/GameType.h"
  33. #include "Common/MessageStream.h"
  34. #include "Common/PerfTimer.h"
  35. #include "Common/Player.h"
  36. #include "Common/PlayerList.h"
  37. #include "Common/Radar.h"
  38. #include "Common/Team.h"
  39. #include "Common/ThingFactory.h"
  40. #include "Common/ThingTemplate.h"
  41. #include "Common/BuildAssistant.h"
  42. #include "Common/Recorder.h"
  43. #include "Common/BuildAssistant.h"
  44. #include "Common/SpecialPower.h"
  45. #include "GameClient/Anim2D.h"
  46. #include "GameClient/ControlBar.h"
  47. #include "GameClient/DisplayStringManager.h"
  48. #include "GameClient/Diplomacy.h"
  49. #include "GameClient/Eva.h"
  50. #include "GameClient/GameText.h"
  51. #include "GameClient/GameWindowManager.h"
  52. #include "GameClient/Drawable.h"
  53. #include "GameClient/GadgetPushButton.h"
  54. #include "GameClient/GameClient.h"
  55. #include "GameClient/GameWindowGlobal.h"
  56. #include "GameClient/GameWindowID.h"
  57. #include "GameClient/GUICallbacks.h"
  58. #include "GameClient/InGameUI.h"
  59. #include "GameClient/VideoPlayer.h"
  60. #include "GameClient/Mouse.h"
  61. #include "GameClient/GadgetStaticText.h"
  62. #include "GameClient/View.h"
  63. #include "GameClient/TerrainVisual.h"
  64. #include "GameClient/ControlBar.h"
  65. #include "GameClient/Display.h"
  66. #include "GameClient/WindowLayout.h"
  67. #include "GameClient/LookAtXlat.h"
  68. #include "GameClient/SelectionXlat.h"
  69. #include "GameClient/Shadow.h"
  70. #include "GameClient/GlobalLanguage.h"
  71. #include "GameLogic/AIGuard.h"
  72. #include "GameLogic/Weapon.h"
  73. #include "GameLogic/Object.h"
  74. #include "GameLogic/GameLogic.h"
  75. #include "GameLogic/PartitionManager.h"
  76. #include "GameLogic/ScriptEngine.h"
  77. #include "GameLogic/Module/ContainModule.h"
  78. #include "GameLogic/Module/ProductionUpdate.h"
  79. #include "GameLogic/Module/SpecialPowerModule.h"
  80. #include "GameLogic/Module/StealthUpdate.h"
  81. #include "GameLogic/Module/SupplyWarehouseDockUpdate.h"
  82. #include "GameLogic/Module/MobMemberSlavedUpdate.h"//ML
  83. #include "Common/UnitTimings.h" //Contains the DO_UNIT_TIMINGS define jba.
  84. #ifdef _INTERNAL
  85. // for occasional debugging...
  86. //#pragma optimize("", off)
  87. //#pragma MESSAGE("************************************** WARNING, optimization disabled for debugging purposes")
  88. #endif
  89. // ------------------------------------------------------------------------------------------------
  90. static const Real placementOpacity = 0.45f;
  91. static const RGBColor illegalBuildColor = { 1.0, 0.0, 0.0 };
  92. //-------------------------------------------------------------------------------------------------
  93. /// The InGameUI singleton instance.
  94. InGameUI *TheInGameUI = NULL;
  95. GameWindow *m_replayWindow = NULL;
  96. // ------------------------------------------------------------------------------------------------
  97. struct KindOfSelectionData
  98. {
  99. KindOfMaskType m_mustbeSet;
  100. KindOfMaskType m_mustbeClear;
  101. DrawableList newlySelectedDrawables;
  102. };
  103. // ------------------------------------------------------------------------------------------------
  104. static Bool kindOfUnitSelection( Drawable *test, void *userData )
  105. {
  106. KindOfSelectionData *data = (KindOfSelectionData *) userData;
  107. if( test )
  108. {
  109. const Object *object = test->getObject();
  110. // Only things with objects can be selected, and the code below isn't
  111. // safe unless you've verified that there is a valid object.
  112. if (!object)
  113. return FALSE;
  114. Bool isKindOfMatch = object->isKindOfMulti(data->m_mustbeSet, data->m_mustbeClear);
  115. // only select objects if not already selected
  116. if( object && isKindOfMatch
  117. && object->isLocallyControlled()
  118. && !object->isContained()
  119. && !object->getDrawable()->isSelected()
  120. && !object->isEffectivelyDead()
  121. && object->isMassSelectable()
  122. && !object->isOffMap()
  123. )
  124. {
  125. // enforce optional unit cap
  126. if (TheInGameUI->getMaxSelectCount() > 0 && TheInGameUI->getSelectCount() >= TheInGameUI->getMaxSelectCount())
  127. {
  128. if ( !TheInGameUI->getDisplayedMaxWarning() )
  129. {
  130. TheInGameUI->setDisplayedMaxWarning( TRUE );
  131. UnicodeString msg;
  132. msg.format(TheGameText->fetch("GUI:MaxSelectionSize").str(), TheInGameUI->getMaxSelectCount());
  133. TheInGameUI->message(msg);
  134. }
  135. }
  136. else
  137. {
  138. TheInGameUI->selectDrawable( test );
  139. TheInGameUI->setDisplayedMaxWarning( FALSE );
  140. data->newlySelectedDrawables.push_back(test);
  141. return TRUE;
  142. }
  143. }
  144. }
  145. return FALSE;
  146. }
  147. // ------------------------------------------------------------------------------------------------
  148. struct MatchingUnitSelectionData
  149. {
  150. const ThingTemplate *templateToSelect;
  151. DrawableList newlySelectedDrawables;
  152. Bool isCarBomb;
  153. };
  154. // ------------------------------------------------------------------------------------------------
  155. static Bool similarUnitSelection( Drawable *test, void *userData )
  156. {
  157. MatchingUnitSelectionData *data = (MatchingUnitSelectionData *) userData;
  158. const ThingTemplate *selectedType = data->templateToSelect;
  159. if( test )
  160. {
  161. const Object *object = test->getObject();
  162. // Only things with objects can be selected, and the code below isn't
  163. // safe unless you've verified that there is a valid object.
  164. if (!object)
  165. return FALSE;
  166. Bool isEquivalent = object->getTemplate()->isEquivalentTo( selectedType );
  167. if( data->isCarBomb && !isEquivalent && object->testStatus( OBJECT_STATUS_IS_CARBOMB ) )
  168. {
  169. isEquivalent = TRUE;
  170. }
  171. // only select objects if not already selected
  172. if( object && isEquivalent
  173. && object->isLocallyControlled()
  174. && !object->isContained()
  175. && !( object->getDrawable()->isSelected() )
  176. && object->isMassSelectable() // And only if they can be multiply selected. (otherwise the drawable will be, but the object will not be)
  177. && !object->isOffMap()
  178. )
  179. {
  180. // enforce optional unit cap
  181. if (TheInGameUI->getMaxSelectCount() > 0 && TheInGameUI->getSelectCount() >= TheInGameUI->getMaxSelectCount())
  182. {
  183. if ( !TheInGameUI->getDisplayedMaxWarning() )
  184. {
  185. TheInGameUI->setDisplayedMaxWarning( TRUE );
  186. UnicodeString msg;
  187. msg.format(TheGameText->fetch("GUI:MaxSelectionSize").str(), TheInGameUI->getMaxSelectCount());
  188. TheInGameUI->message(msg);
  189. }
  190. }
  191. else
  192. {
  193. TheInGameUI->selectDrawable( test );
  194. TheInGameUI->setDisplayedMaxWarning( FALSE );
  195. data->newlySelectedDrawables.push_back(test);
  196. return TRUE;
  197. }
  198. }
  199. }
  200. return FALSE;
  201. }
  202. // ------------------------------------------------------------------------------------------------
  203. // ------------------------------------------------------------------------------------------------
  204. void showReplayControls( void )
  205. {
  206. if (m_replayWindow)
  207. {
  208. Bool show = TheGameLogic->isInReplayGame();
  209. m_replayWindow->winHide(!show);
  210. }
  211. }
  212. // ------------------------------------------------------------------------------------------------
  213. // ------------------------------------------------------------------------------------------------
  214. void hideReplayControls( void )
  215. {
  216. if (m_replayWindow)
  217. {
  218. m_replayWindow->winHide(TRUE);
  219. }
  220. }
  221. // ------------------------------------------------------------------------------------------------
  222. // ------------------------------------------------------------------------------------------------
  223. void toggleReplayControls( void )
  224. {
  225. if (m_replayWindow)
  226. {
  227. Bool show = TheGameLogic->isInReplayGame() && m_replayWindow->winIsHidden();
  228. m_replayWindow->winHide(!show);
  229. }
  230. }
  231. // ------------------------------------------------------------------------------------------------
  232. // ------------------------------------------------------------------------------------------------
  233. SuperweaponInfo::SuperweaponInfo(
  234. ObjectID id,
  235. UnsignedInt timestamp,
  236. Bool hiddenByScript,
  237. Bool hiddenByScience,
  238. Bool ready,
  239. Bool evaReadyPlayed,
  240. const AsciiString& superweaponNormalFont,
  241. Int superweaponNormalPointSize,
  242. Bool superweaponNormalBold,
  243. Color c,
  244. const SpecialPowerTemplate* spt
  245. ) :
  246. m_id(id),
  247. m_timestamp(timestamp),
  248. m_hiddenByScript(hiddenByScript),
  249. m_hiddenByScience(hiddenByScience),
  250. m_ready(ready),
  251. m_evaReadyPlayed( evaReadyPlayed ),
  252. m_forceUpdateText(false),
  253. m_nameDisplayString(NULL),
  254. m_timeDisplayString(NULL),
  255. m_color(c),
  256. m_powerTemplate(spt)
  257. {
  258. m_nameDisplayString = TheDisplayStringManager->newDisplayString();
  259. m_nameDisplayString->reset();
  260. m_nameDisplayString->setText( UnicodeString::TheEmptyString );
  261. m_timeDisplayString = TheDisplayStringManager->newDisplayString();
  262. m_timeDisplayString->reset();
  263. m_timeDisplayString->setText( UnicodeString::TheEmptyString );
  264. setFont( superweaponNormalFont, superweaponNormalPointSize, superweaponNormalBold );
  265. }
  266. // ------------------------------------------------------------------------------------------------
  267. // ------------------------------------------------------------------------------------------------
  268. SuperweaponInfo::~SuperweaponInfo()
  269. {
  270. if (m_nameDisplayString)
  271. TheDisplayStringManager->freeDisplayString( m_nameDisplayString );
  272. m_nameDisplayString = NULL;
  273. if (m_timeDisplayString)
  274. TheDisplayStringManager->freeDisplayString( m_timeDisplayString );
  275. m_timeDisplayString = NULL;
  276. }
  277. // ------------------------------------------------------------------------------------------------
  278. // ------------------------------------------------------------------------------------------------
  279. void SuperweaponInfo::setFont(const AsciiString& superweaponNormalFont, Int superweaponNormalPointSize, Bool superweaponNormalBold)
  280. {
  281. m_nameDisplayString->setFont( TheFontLibrary->getFont( superweaponNormalFont,
  282. TheGlobalLanguageData->adjustFontSize(superweaponNormalPointSize), superweaponNormalBold ) );
  283. m_timeDisplayString->setFont( TheFontLibrary->getFont( superweaponNormalFont,
  284. TheGlobalLanguageData->adjustFontSize(superweaponNormalPointSize), superweaponNormalBold ) );
  285. }
  286. // ------------------------------------------------------------------------------------------------
  287. void SuperweaponInfo::setText(const UnicodeString& name, const UnicodeString& time)
  288. {
  289. m_nameDisplayString->setText(name);
  290. m_timeDisplayString->setText(time);
  291. }
  292. // ------------------------------------------------------------------------------------------------
  293. void SuperweaponInfo::drawName(Int x, Int y, Color color, Color dropColor)
  294. {
  295. if (color == 0)
  296. color = m_color;
  297. m_nameDisplayString->draw(x - m_nameDisplayString->getWidth(), y, color, dropColor);
  298. }
  299. // ------------------------------------------------------------------------------------------------
  300. void SuperweaponInfo::drawTime(Int x, Int y, Color color, Color dropColor)
  301. {
  302. if (color == 0)
  303. color = m_color;
  304. m_timeDisplayString->draw(x, y, color, dropColor);
  305. }
  306. // ------------------------------------------------------------------------------------------------
  307. // ------------------------------------------------------------------------------------------------
  308. Real SuperweaponInfo::getHeight() const
  309. {
  310. return m_nameDisplayString->getFont()->height;
  311. }
  312. // ------------------------------------------------------------------------------------------------
  313. /** CRC */
  314. // ------------------------------------------------------------------------------------------------
  315. void InGameUI::crc( Xfer *xfer )
  316. {
  317. } // end crc
  318. // ------------------------------------------------------------------------------------------------
  319. /** Xfer method
  320. * Version Info:
  321. * 1: Initial version
  322. * 2: Save NamedTimers, but not specifically their Info structs. We'll recreate them.
  323. * 3: Added m_evaReadyPlayed boolean to transfer
  324. */
  325. // ------------------------------------------------------------------------------------------------
  326. void InGameUI::xfer( Xfer *xfer )
  327. {
  328. // version
  329. const XferVersion currentVersion = 3;
  330. XferVersion version = currentVersion;
  331. xfer->xferVersion( &version, currentVersion );
  332. if( version >= 2 )
  333. {
  334. // Saving the named timer infos and their friends so we get script timers back after we load
  335. xfer->xferInt(&m_namedTimerLastFlashFrame);
  336. xfer->xferBool(&m_namedTimerUsedFlashColor);
  337. xfer->xferBool(&m_showNamedTimers);
  338. // For the timers themselves, all I need to save is the things that are used in the call to addNamedTimer.
  339. // It is okay to do this, because SuperweaponInfos pushes things on to a map; addNamedTimer is just a more
  340. // organized way to push things on the namedTimer Map.
  341. // addNamedTimer needs (const AsciiString& timerName, const UnicodeString& text, Bool isCountdown)
  342. if (xfer->getXferMode() == XFER_SAVE)
  343. {
  344. Int timerCount = m_namedTimers.size();
  345. xfer->xferInt( &timerCount );
  346. for( NamedTimerMapIt timerIter = m_namedTimers.begin(); timerIter != m_namedTimers.end(); ++timerIter )
  347. {
  348. xfer->xferAsciiString( &(timerIter->second->m_timerName) );
  349. xfer->xferUnicodeString( &(timerIter->second->timerText) );
  350. xfer->xferBool( &(timerIter->second->isCountdown) );
  351. }
  352. }
  353. else // iz a Load
  354. {
  355. Int timerCount;
  356. xfer->xferInt( &timerCount );
  357. for( Int timerIndex = 0; timerIndex < timerCount; ++timerIndex )
  358. {
  359. AsciiString timerName;
  360. UnicodeString timerText;
  361. Bool isCountdown;
  362. xfer->xferAsciiString( &timerName );
  363. xfer->xferUnicodeString( &timerText );
  364. xfer->xferBool( &isCountdown );
  365. addNamedTimer( timerName, timerText, isCountdown );
  366. }
  367. }
  368. }
  369. xfer->xferBool(&m_superweaponHiddenByScript);
  370. //xfer->xferBool(&m_inputEnabled); // no, don't save this yet. somewhat problematic.
  371. if (xfer->getXferMode() == XFER_SAVE)
  372. {
  373. for (Int playerIndex = 0; playerIndex < MAX_PLAYER_COUNT; ++playerIndex)
  374. {
  375. for (SuperweaponMap::iterator mapIt = m_superweapons[playerIndex].begin(); mapIt != m_superweapons[playerIndex].end(); ++mapIt)
  376. {
  377. AsciiString powerName = mapIt->first;
  378. SuperweaponList& swList = mapIt->second;
  379. for (SuperweaponList::iterator listIt = swList.begin(); listIt != swList.end(); ++listIt)
  380. {
  381. SuperweaponInfo* swInfo = *listIt;
  382. // since this list tends to be somewhat sparse, we write stuff out pretty explicitly.
  383. xfer->xferInt(&playerIndex);
  384. AsciiString templateName = swInfo->getSpecialPowerTemplate()->getName();
  385. xfer->xferAsciiString(&templateName);
  386. xfer->xferAsciiString(&powerName);
  387. xfer->xferObjectID(&swInfo->m_id);
  388. xfer->xferUnsignedInt(&swInfo->m_timestamp);
  389. xfer->xferBool(&swInfo->m_hiddenByScript);
  390. xfer->xferBool(&swInfo->m_hiddenByScience);
  391. xfer->xferBool(&swInfo->m_ready);
  392. if ( currentVersion >= 3 )
  393. {
  394. xfer->xferBool( &swInfo->m_evaReadyPlayed );
  395. }
  396. }
  397. }
  398. }
  399. Int noMorePlayers = -1; // our "done" sentinel
  400. xfer->xferInt(&noMorePlayers);
  401. }
  402. else if (xfer->getXferMode() == XFER_LOAD)
  403. {
  404. for (;;)
  405. {
  406. Int playerIndex;
  407. xfer->xferInt(&playerIndex);
  408. if (playerIndex == -1)
  409. {
  410. break; // our "done" sentinel
  411. }
  412. else if (playerIndex < 0 || playerIndex >= MAX_PLAYER_COUNT)
  413. {
  414. DEBUG_CRASH(("SWInfo bad plyrindex\n"));
  415. throw INI_INVALID_DATA;
  416. }
  417. AsciiString templateName;
  418. xfer->xferAsciiString(&templateName);
  419. const SpecialPowerTemplate* powerTemplate = TheSpecialPowerStore->findSpecialPowerTemplate(templateName);
  420. if (powerTemplate == NULL)
  421. {
  422. DEBUG_CRASH(("power %s not found\n",templateName.str()));
  423. throw INI_INVALID_DATA;
  424. }
  425. AsciiString powerName;
  426. ObjectID id;
  427. UnsignedInt timestamp;
  428. Bool hiddenByScript, hiddenByScience, ready, evaReadyPlayed;
  429. xfer->xferAsciiString(&powerName);
  430. xfer->xferObjectID(&id);
  431. xfer->xferUnsignedInt(&timestamp);
  432. xfer->xferBool(&hiddenByScript);
  433. xfer->xferBool(&hiddenByScience);
  434. xfer->xferBool(&ready);
  435. if ( currentVersion >= 3 )
  436. {
  437. xfer->xferBool( &evaReadyPlayed );
  438. }
  439. else
  440. {
  441. evaReadyPlayed = ready;
  442. }
  443. // srj sez: due to order-of-operation stuff, sometimes these will already exist,
  444. // sometimes not. not sure why. so handle both cases.
  445. SuperweaponInfo* swInfo = findSWInfo(playerIndex, powerName, id, powerTemplate);
  446. if (swInfo == NULL)
  447. {
  448. const Player* player = ThePlayerList->getNthPlayer(playerIndex);
  449. swInfo = newInstance(SuperweaponInfo)(
  450. id,
  451. timestamp,
  452. hiddenByScript,
  453. hiddenByScience,
  454. ready,
  455. evaReadyPlayed,
  456. m_superweaponNormalFont,
  457. m_superweaponNormalPointSize,
  458. m_superweaponNormalBold,
  459. player->getPlayerColor(),
  460. powerTemplate);
  461. m_superweapons[playerIndex][powerName].push_back(swInfo);
  462. }
  463. else
  464. {
  465. // swInfo->m_id = id; // redundant, already matches
  466. swInfo->m_timestamp = timestamp;
  467. swInfo->m_hiddenByScript = hiddenByScript;
  468. swInfo->m_hiddenByScience = hiddenByScience;
  469. swInfo->m_ready = ready;
  470. swInfo->m_evaReadyPlayed = evaReadyPlayed;
  471. }
  472. swInfo->m_forceUpdateText = true;
  473. }
  474. }
  475. }
  476. // ------------------------------------------------------------------------------------------------
  477. /** Load post process */
  478. // ------------------------------------------------------------------------------------------------
  479. void InGameUI::loadPostProcess( void )
  480. {
  481. } // end loadPostProcess
  482. // ------------------------------------------------------------------------------------------------
  483. // ------------------------------------------------------------------------------------------------
  484. void InGameUI::setMouseCursor(Mouse::MouseCursor c)
  485. {
  486. if (!TheMouse)
  487. return;
  488. TheMouse->setCursor(c);
  489. if (m_mouseMode == MOUSEMODE_GUI_COMMAND && c != Mouse::ARROW && c != Mouse::SCROLL)
  490. m_mouseModeCursor = c;
  491. }
  492. // ------------------------------------------------------------------------------------------------
  493. // ------------------------------------------------------------------------------------------------
  494. SuperweaponInfo* InGameUI::findSWInfo(Int playerIndex, const AsciiString& powerName, ObjectID id, const SpecialPowerTemplate *powerTemplate)
  495. {
  496. SuperweaponMap::iterator mapIt = m_superweapons[playerIndex].find(powerName);
  497. if (mapIt != m_superweapons[playerIndex].end())
  498. {
  499. for (SuperweaponList::iterator listIt = mapIt->second.begin(); listIt != mapIt->second.end(); ++listIt)
  500. {
  501. if ((*listIt)->m_id == id)
  502. {
  503. return *listIt;
  504. }
  505. }
  506. }
  507. return NULL;
  508. }
  509. // ------------------------------------------------------------------------------------------------
  510. // ------------------------------------------------------------------------------------------------
  511. void InGameUI::addSuperweapon(Int playerIndex, const AsciiString& powerName, ObjectID id, const SpecialPowerTemplate *powerTemplate)
  512. {
  513. if (powerTemplate == NULL)
  514. return;
  515. // srj sez: don't allow adding the same superweapon more than once. it can happen. not sure how. (srj)
  516. SuperweaponInfo* swInfo = findSWInfo(playerIndex, powerName, id, powerTemplate);
  517. if (swInfo != NULL)
  518. return;
  519. const Player* player = ThePlayerList->getNthPlayer(playerIndex);
  520. Bool hiddenByScience = (powerTemplate->getRequiredScience() != SCIENCE_INVALID) && (player->hasScience(powerTemplate->getRequiredScience()) == false);
  521. #ifndef DO_UNIT_TIMINGS
  522. DEBUG_LOG(("Adding superweapon UI timer\n"));
  523. #endif
  524. SuperweaponInfo *info = newInstance(SuperweaponInfo)(
  525. id,
  526. -1, // timestamp
  527. FALSE, // hiddenByScript
  528. hiddenByScience,//Aaayeeee! This is meaningless and just clogs up the works, sez srj, nuke or repair or SHIP WITH(tm), ASAP
  529. // THe trouble is: There is no mechanism to clear this bit when the science is granted, thus,
  530. // the timer never, ever, ever get drawn.... unless the owning object is post-science constructed.
  531. FALSE, // ready
  532. FALSE, // evaReadyPlayed
  533. m_superweaponNormalFont,
  534. m_superweaponNormalPointSize,
  535. m_superweaponNormalBold,
  536. player->getPlayerColor(),
  537. powerTemplate);
  538. m_superweapons[playerIndex][powerName].push_back(info);
  539. }
  540. // ------------------------------------------------------------------------------------------------
  541. // ------------------------------------------------------------------------------------------------
  542. Bool InGameUI::removeSuperweapon(Int playerIndex, const AsciiString& powerName, ObjectID id, const SpecialPowerTemplate *powerTemplate)
  543. {
  544. DEBUG_LOG(("Removing superweapon UI timer\n"));
  545. SuperweaponMap::iterator mapIt = m_superweapons[playerIndex].find(powerName);
  546. if (mapIt != m_superweapons[playerIndex].end())
  547. {
  548. SuperweaponList& swList = mapIt->second;
  549. for (SuperweaponList::iterator listIt = swList.begin(); listIt != swList.end(); ++listIt)
  550. {
  551. if ((*listIt)->m_id == id)
  552. {
  553. SuperweaponInfo *info = *listIt;
  554. swList.erase(listIt);
  555. info->deleteInstance();
  556. if (swList.size() == 0)
  557. {
  558. m_superweapons[playerIndex].erase(mapIt);
  559. }
  560. return TRUE;
  561. }
  562. }
  563. }
  564. return FALSE;
  565. }
  566. // ------------------------------------------------------------------------------------------------
  567. // ------------------------------------------------------------------------------------------------
  568. void InGameUI::objectChangedTeam(const Object *obj, Int oldPlayerIndex, Int newPlayerIndex)
  569. {
  570. // if we already had it listed, remove and re-add it
  571. if (obj && oldPlayerIndex >= 0 && newPlayerIndex >= 0)
  572. {
  573. ObjectID id = obj->getID();
  574. AsciiString powerName;
  575. for (BehaviorModule** m = obj->getBehaviorModules(); *m; ++m)
  576. {
  577. SpecialPowerModuleInterface* sp = (*m)->getSpecialPower();
  578. if (!sp)
  579. continue;
  580. const SpecialPowerTemplate *powerTemplate = sp->getSpecialPowerTemplate();
  581. powerName = powerTemplate->getName();
  582. SuperweaponMap::iterator mapIt = m_superweapons[oldPlayerIndex].find(powerName);
  583. Bool found = false;
  584. if (mapIt != m_superweapons[oldPlayerIndex].end())
  585. {
  586. for (SuperweaponList::iterator listIt = mapIt->second.begin(); listIt != mapIt->second.end(); ++listIt)
  587. {
  588. if ((*listIt)->m_id == id)
  589. {
  590. removeSuperweapon(oldPlayerIndex, powerName, id, powerTemplate);
  591. addSuperweapon(newPlayerIndex, powerName, id, powerTemplate);
  592. found = true;
  593. break;
  594. }
  595. }
  596. }
  597. if (!found)
  598. {
  599. if( TheGameLogic->getFrame() == 0 && !obj->getStatusBits().test( OBJECT_STATUS_UNDER_CONSTRUCTION ) &&
  600. obj->isKindOf( KINDOF_COMMANDCENTER ) == FALSE )
  601. addSuperweapon(newPlayerIndex, powerName, id, powerTemplate);
  602. }
  603. }
  604. }
  605. }
  606. // ------------------------------------------------------------------------------------------------
  607. // ------------------------------------------------------------------------------------------------
  608. void InGameUI::hideObjectSuperweaponDisplayByScript(const Object *obj)
  609. {
  610. ObjectID objID = obj->getID();
  611. for (Int playerIndex = 0; playerIndex < MAX_PLAYER_COUNT; ++playerIndex)
  612. {
  613. for (SuperweaponMap::iterator mapIt = m_superweapons[playerIndex].begin(); mapIt != m_superweapons[playerIndex].end(); ++mapIt)
  614. {
  615. for (SuperweaponList::iterator listIt = mapIt->second.begin(); listIt != mapIt->second.end(); ++listIt)
  616. {
  617. if ((*listIt)->m_id == objID)
  618. {
  619. (*listIt)->m_hiddenByScript = TRUE;
  620. }
  621. }
  622. }
  623. }
  624. }
  625. // ------------------------------------------------------------------------------------------------
  626. // ------------------------------------------------------------------------------------------------
  627. void InGameUI::showObjectSuperweaponDisplayByScript(const Object *obj)
  628. {
  629. ObjectID objID = obj->getID();
  630. for (Int playerIndex = 0; playerIndex < MAX_PLAYER_COUNT; ++playerIndex)
  631. {
  632. for (SuperweaponMap::iterator mapIt = m_superweapons[playerIndex].begin(); mapIt != m_superweapons[playerIndex].end(); ++mapIt)
  633. {
  634. for (SuperweaponList::iterator listIt = mapIt->second.begin(); listIt != mapIt->second.end(); ++listIt)
  635. {
  636. if ((*listIt)->m_id == objID)
  637. {
  638. (*listIt)->m_hiddenByScript = FALSE;
  639. }
  640. }
  641. }
  642. }
  643. }
  644. // ------------------------------------------------------------------------------------------------
  645. // ------------------------------------------------------------------------------------------------
  646. void InGameUI::setSuperweaponDisplayEnabledByScript(Bool enable)
  647. {
  648. m_superweaponHiddenByScript = !enable;
  649. }
  650. // ------------------------------------------------------------------------------------------------
  651. // ------------------------------------------------------------------------------------------------
  652. Bool InGameUI::getSuperweaponDisplayEnabledByScript(void) const
  653. {
  654. return m_superweaponHiddenByScript;
  655. }
  656. // ------------------------------------------------------------------------------------------------
  657. // ------------------------------------------------------------------------------------------------
  658. void InGameUI::addNamedTimer( const AsciiString& timerName, const UnicodeString& text, Bool isCountdown )
  659. {
  660. NamedTimerInfo *info = newInstance( NamedTimerInfo );
  661. info->m_timerName = timerName;
  662. info->color = m_namedTimerNormalColor;
  663. info->timerText = text;
  664. info->displayString = TheDisplayStringManager->newDisplayString();
  665. info->displayString->reset();
  666. info->displayString->setFont( TheFontLibrary->getFont( m_namedTimerNormalFont,
  667. TheGlobalLanguageData->adjustFontSize(m_namedTimerNormalPointSize), m_namedTimerNormalBold ) );
  668. info->displayString->setText( UnicodeString::TheEmptyString );
  669. info->timestamp = -1;
  670. info->isCountdown = isCountdown;
  671. // GameFont *font = info->displayString->getFont();
  672. removeNamedTimer(timerName);
  673. m_namedTimers[timerName] = info;
  674. }
  675. // ------------------------------------------------------------------------------------------------
  676. // ------------------------------------------------------------------------------------------------
  677. void InGameUI::removeNamedTimer( const AsciiString& timerName )
  678. {
  679. NamedTimerMapIt mapIt = m_namedTimers.find(timerName);
  680. if (mapIt != m_namedTimers.end())
  681. {
  682. TheDisplayStringManager->freeDisplayString( mapIt->second->displayString );
  683. mapIt->second->deleteInstance();
  684. m_namedTimers.erase(mapIt);
  685. return;
  686. }
  687. }
  688. // ------------------------------------------------------------------------------------------------
  689. // ------------------------------------------------------------------------------------------------
  690. void InGameUI::showNamedTimerDisplay( Bool show )
  691. {
  692. m_showNamedTimers = show;
  693. }
  694. //-------------------------------------------------------------------------------------------------
  695. //-------------------------------------------------------------------------------------------------
  696. const FieldParse InGameUI::s_fieldParseTable[] =
  697. {
  698. { "MaxSelectionSize", INI::parseInt, NULL, offsetof( InGameUI, m_maxSelectCount ) },
  699. { "MessageColor1", INI::parseColorInt, NULL, offsetof( InGameUI, m_messageColor1 ) },
  700. { "MessageColor2", INI::parseColorInt, NULL, offsetof( InGameUI, m_messageColor2 ) },
  701. { "MessagePosition", INI::parseICoord2D, NULL, offsetof( InGameUI, m_messagePosition ) },
  702. { "MessageFont", INI::parseAsciiString, NULL, offsetof( InGameUI, m_messageFont ) },
  703. { "MessagePointSize", INI::parseInt, NULL, offsetof( InGameUI, m_messagePointSize ) },
  704. { "MessageBold", INI::parseBool, NULL, offsetof( InGameUI, m_messageBold ) },
  705. { "MessageDelayMS", INI::parseInt, NULL, offsetof( InGameUI, m_messageDelayMS ) },
  706. { "MilitaryCaptionColor", INI::parseRGBAColorInt, NULL, offsetof( InGameUI, m_militaryCaptionColor ) },
  707. { "MilitaryCaptionPosition", INI::parseICoord2D, NULL, offsetof( InGameUI, m_militaryCaptionPosition ) },
  708. { "MilitaryCaptionTitleFont", INI::parseAsciiString, NULL, offsetof( InGameUI, m_militaryCaptionTitleFont ) },
  709. { "MilitaryCaptionTitlePointSize", INI::parseInt, NULL, offsetof( InGameUI, m_militaryCaptionTitlePointSize ) },
  710. { "MilitaryCaptionTitleBold", INI::parseBool, NULL, offsetof( InGameUI, m_militaryCaptionTitleBold ) },
  711. { "MilitaryCaptionFont", INI::parseAsciiString, NULL, offsetof( InGameUI, m_militaryCaptionFont ) },
  712. { "MilitaryCaptionPointSize", INI::parseInt, NULL, offsetof( InGameUI, m_militaryCaptionPointSize ) },
  713. { "MilitaryCaptionBold", INI::parseBool, NULL, offsetof( InGameUI, m_militaryCaptionBold ) },
  714. { "MilitaryCaptionRandomizeTyping", INI::parseBool, NULL, offsetof( InGameUI, m_militaryCaptionRandomizeTyping ) },
  715. { "MilitaryCaptionSpeed", INI::parseInt, NULL, offsetof( InGameUI, m_militaryCaptionSpeed ) },
  716. { "MilitaryCaptionPosition", INI::parseICoord2D, NULL, offsetof( InGameUI, m_militaryCaptionPosition ) },
  717. { "SuperweaponCountdownPosition", INI::parseCoord2D, NULL, offsetof( InGameUI, m_superweaponPosition ) },
  718. { "SuperweaponCountdownFlashDuration", INI::parseDurationReal, NULL, offsetof( InGameUI, m_superweaponFlashDuration ) },
  719. { "SuperweaponCountdownFlashColor", INI::parseColorInt, NULL, offsetof( InGameUI, m_superweaponFlashColor ) },
  720. { "SuperweaponCountdownNormalFont", INI::parseAsciiString, NULL, offsetof( InGameUI, m_superweaponNormalFont ) },
  721. { "SuperweaponCountdownNormalPointSize", INI::parseInt, NULL, offsetof( InGameUI, m_superweaponNormalPointSize ) },
  722. { "SuperweaponCountdownNormalBold", INI::parseBool, NULL, offsetof( InGameUI, m_superweaponNormalBold ) },
  723. { "SuperweaponCountdownReadyFont", INI::parseAsciiString, NULL, offsetof( InGameUI, m_superweaponReadyFont ) },
  724. { "SuperweaponCountdownReadyPointSize", INI::parseInt, NULL, offsetof( InGameUI, m_superweaponReadyPointSize ) },
  725. { "SuperweaponCountdownReadyBold", INI::parseBool, NULL, offsetof( InGameUI, m_superweaponReadyBold ) },
  726. { "NamedTimerCountdownPosition", INI::parseCoord2D, NULL, offsetof( InGameUI, m_namedTimerPosition ) },
  727. { "NamedTimerCountdownFlashDuration", INI::parseDurationReal, NULL, offsetof( InGameUI, m_namedTimerFlashDuration ) },
  728. { "NamedTimerCountdownFlashColor", INI::parseColorInt, NULL, offsetof( InGameUI, m_namedTimerFlashColor ) },
  729. { "NamedTimerCountdownNormalFont", INI::parseAsciiString, NULL, offsetof( InGameUI, m_namedTimerNormalFont ) },
  730. { "NamedTimerCountdownNormalPointSize", INI::parseInt, NULL, offsetof( InGameUI, m_namedTimerNormalPointSize ) },
  731. { "NamedTimerCountdownNormalBold", INI::parseBool, NULL, offsetof( InGameUI, m_namedTimerNormalBold ) },
  732. { "NamedTimerCountdownNormalColor", INI::parseColorInt, NULL, offsetof( InGameUI, m_namedTimerNormalColor ) },
  733. { "NamedTimerCountdownReadyFont", INI::parseAsciiString, NULL, offsetof( InGameUI, m_namedTimerReadyFont ) },
  734. { "NamedTimerCountdownReadyPointSize", INI::parseInt, NULL, offsetof( InGameUI, m_namedTimerReadyPointSize ) },
  735. { "NamedTimerCountdownReadyBold", INI::parseBool, NULL, offsetof( InGameUI, m_namedTimerReadyBold ) },
  736. { "NamedTimerCountdownReadyColor", INI::parseColorInt, NULL, offsetof( InGameUI, m_namedTimerReadyColor ) },
  737. { "FloatingTextTimeOut", INI::parseDurationUnsignedInt, NULL, offsetof( InGameUI, m_floatingTextTimeOut ) },
  738. { "FloatingTextMoveUpSpeed", INI::parseVelocityReal, NULL, offsetof( InGameUI, m_floatingTextMoveUpSpeed ) },
  739. { "FloatingTextVanishRate", INI::parseVelocityReal, NULL, offsetof( InGameUI, m_floatingTextMoveVanishRate ) },
  740. { "PopupMessageColor", INI::parseColorInt, NULL, offsetof( InGameUI, m_popupMessageColor ) },
  741. { "DrawableCaptionFont", INI::parseAsciiString, NULL, offsetof( InGameUI, m_drawableCaptionFont ) },
  742. { "DrawableCaptionPointSize", INI::parseInt, NULL, offsetof( InGameUI, m_drawableCaptionPointSize ) },
  743. { "DrawableCaptionBold", INI::parseBool, NULL, offsetof( InGameUI, m_drawableCaptionBold ) },
  744. { "DrawableCaptionColor", INI::parseColorInt, NULL, offsetof( InGameUI, m_drawableCaptionColor ) },
  745. { "DrawRMBScrollAnchor", INI::parseBool, NULL, offsetof( InGameUI, m_drawRMBScrollAnchor ) },
  746. { "MoveRMBScrollAnchor", INI::parseBool, NULL, offsetof( InGameUI, m_moveRMBScrollAnchor ) },
  747. { "AttackDamageAreaRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[RADIUSCURSOR_ATTACK_DAMAGE_AREA] ) },
  748. { "AttackScatterAreaRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[RADIUSCURSOR_ATTACK_SCATTER_AREA] ) },
  749. { "AttackContinueAreaRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[RADIUSCURSOR_ATTACK_CONTINUE_AREA] ) },
  750. { "FriendlySpecialPowerRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[RADIUSCURSOR_FRIENDLY_SPECIALPOWER] ) },
  751. { "OffensiveSpecialPowerRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[RADIUSCURSOR_OFFENSIVE_SPECIALPOWER] ) },
  752. { "SuperweaponScatterAreaRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[RADIUSCURSOR_SUPERWEAPON_SCATTER_AREA] ) },
  753. { "GuardAreaRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[RADIUSCURSOR_GUARD_AREA] ) },
  754. { "EmergencyRepairRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[RADIUSCURSOR_EMERGENCY_REPAIR] ) },
  755. { "ParticleCannonRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[ RADIUSCURSOR_PARTICLECANNON] ) },
  756. { "A10StrikeRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[ RADIUSCURSOR_A10STRIKE] ) },
  757. { "CarpetBombRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[ RADIUSCURSOR_CARPETBOMB] ) },
  758. { "DaisyCutterRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[ RADIUSCURSOR_DAISYCUTTER] ) },
  759. { "ParadropRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[ RADIUSCURSOR_PARADROP] ) },
  760. { "SpySatelliteRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[ RADIUSCURSOR_SPYSATELLITE] ) },
  761. { "SpectreGunshipRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[ RADIUSCURSOR_SPECTREGUNSHIP] ) },
  762. { "HelixNapalmBombRadiusCursor",RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[ RADIUSCURSOR_HELIX_NAPALM_BOMB] ) },
  763. { "NuclearMissileRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[ RADIUSCURSOR_NUCLEARMISSILE] ) },
  764. { "EMPPulseRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[ RADIUSCURSOR_EMPPULSE] ) },
  765. { "ArtilleryRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[ RADIUSCURSOR_ARTILLERYBARRAGE] ) },
  766. { "FrenzyRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[ RADIUSCURSOR_FRENZY] ) },
  767. { "NapalmStrikeRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[ RADIUSCURSOR_NAPALMSTRIKE] ) },
  768. { "ClusterMinesRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[ RADIUSCURSOR_CLUSTERMINES] ) },
  769. { "ScudStormRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[ RADIUSCURSOR_SCUDSTORM] ) },
  770. { "AnthraxBombRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[ RADIUSCURSOR_ANTHRAXBOMB] ) },
  771. { "AmbushRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[ RADIUSCURSOR_AMBUSH] ) },
  772. { "RadarRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[ RADIUSCURSOR_RADAR] ) },
  773. { "SpyDroneRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[ RADIUSCURSOR_SPYDRONE] ) },
  774. { "ClearMinesRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[ RADIUSCURSOR_CLEARMINES] ) },
  775. { "AmbulanceRadiusCursor", RadiusDecalTemplate::parseRadiusDecalTemplate, NULL, offsetof( InGameUI, m_radiusCursors[ RADIUSCURSOR_AMBULANCE] ) },
  776. { NULL, NULL, NULL, 0 } // keep this last
  777. };
  778. //-------------------------------------------------------------------------------------------------
  779. /** Parse MouseCursor entry */
  780. //-------------------------------------------------------------------------------------------------
  781. void INI::parseInGameUIDefinition( INI* ini )
  782. {
  783. if( TheInGameUI )
  784. {
  785. // parse the ini weapon definition
  786. ini->initFromINI( TheInGameUI, TheInGameUI->getFieldParse() );
  787. }
  788. }
  789. //-------------------------------------------------------------------------------------------------
  790. //-------------------------------------------------------------------------------------------------
  791. InGameUI::InGameUI()
  792. {
  793. Int i;
  794. m_inputEnabled = true;
  795. m_isDragSelecting = false;
  796. m_nextMoveHint = 0;
  797. m_selectCount = 0;
  798. m_frameSelectionChanged = 0;
  799. m_duringDoubleClickAttackMoveGuardHintTimer = 0;
  800. m_duringDoubleClickAttackMoveGuardHintStashedPosition.zero();
  801. m_maxSelectCount = -1;
  802. m_isScrolling = FALSE;
  803. m_isSelecting = FALSE;
  804. m_mouseMode = MOUSEMODE_DEFAULT;
  805. m_mouseModeCursor = Mouse::ARROW;
  806. m_mousedOverDrawableID = INVALID_DRAWABLE_ID;
  807. //Added By Sadullah Nader
  808. //Initializations missing and needed
  809. m_currentlyPlayingMovie.clear();
  810. m_militarySubtitle = NULL;
  811. m_popupMessageData = NULL;
  812. m_waypointMode = FALSE;
  813. m_clientQuiet = FALSE;
  814. m_messageColor1 = GameMakeColor( 255, 255, 255, 255 );
  815. m_messageColor2 = GameMakeColor( 180, 180, 180, 255 );
  816. m_messagePosition.x = 10;
  817. m_messagePosition.y = 10;
  818. m_messageFont = "Arial";
  819. m_messagePointSize = 10;
  820. m_messageBold = FALSE;
  821. m_messageDelayMS = 5000;
  822. m_militaryCaptionColor.red = 200;
  823. m_militaryCaptionColor.green = 200;
  824. m_militaryCaptionColor.blue = 30;
  825. m_militaryCaptionColor.alpha = 255;
  826. m_militaryCaptionPosition.x = 10;
  827. m_militaryCaptionPosition.y = 380;
  828. m_militaryCaptionTitleFont = "Courier";
  829. m_militaryCaptionTitlePointSize = 12;
  830. m_militaryCaptionTitleBold = TRUE;
  831. m_militaryCaptionFont = "Courier";
  832. m_militaryCaptionPointSize = 12;
  833. m_militaryCaptionBold = FALSE;
  834. m_militaryCaptionRandomizeTyping = FALSE;
  835. m_militaryCaptionSpeed = 1;
  836. m_popupMessageColor = GameMakeColor(255,255,255,255);
  837. m_tooltipsDisabledUntil = 0;
  838. // init hint lists
  839. for( i = 0; i < MAX_MOVE_HINTS; i++ )
  840. {
  841. m_moveHint[ i ].pos.zero();
  842. m_moveHint[ i ].sourceID = 0;
  843. m_moveHint[ i ].frame = 0;
  844. } // end for i
  845. for( i = 0; i < MAX_BUILD_PROGRESS; i++ )
  846. {
  847. m_buildProgress[ i ].m_thingTemplate = NULL;
  848. m_buildProgress[ i ].m_percentComplete = 0.0f;
  849. m_buildProgress[ i ].m_control = NULL;
  850. } // end for i
  851. m_pendingGUICommand = NULL;
  852. // allocate an array for the placement icons
  853. m_placeIcon = NEW Drawable* [ TheGlobalData->m_maxLineBuildObjects ];
  854. for( i = 0; i < TheGlobalData->m_maxLineBuildObjects; i++ )
  855. m_placeIcon[ i ] = NULL;
  856. m_pendingPlaceType = NULL;
  857. m_pendingPlaceSourceObjectID = INVALID_ID;
  858. m_preventLeftClickDeselectionInAlternateMouseModeForOneClick = FALSE;
  859. m_placeAnchorStart.x = m_placeAnchorStart.y = 0;
  860. m_placeAnchorEnd.x = m_placeAnchorEnd.y = 0;
  861. m_placeAnchorInProgress = FALSE;
  862. m_videoStream = NULL;
  863. m_videoBuffer = NULL;
  864. m_cameoVideoStream = NULL;
  865. m_cameoVideoBuffer = NULL;
  866. // message info
  867. for( i = 0; i < MAX_UI_MESSAGES; i++ )
  868. {
  869. m_uiMessages[ i ].fullText.clear();
  870. m_uiMessages[ i ].displayString = NULL;
  871. m_uiMessages[ i ].timestamp = 0;
  872. m_uiMessages[ i ].color = 0;
  873. } // end for i
  874. m_replayWindow = NULL;
  875. m_messagesOn = TRUE;
  876. m_superweaponPosition.x = 0.7f;
  877. m_superweaponPosition.y = 0.7f;
  878. m_superweaponFlashDuration = 1.0f;
  879. m_superweaponNormalFont = "Arial";
  880. m_superweaponNormalPointSize = 10;
  881. m_superweaponNormalBold = FALSE;
  882. m_superweaponReadyFont = "Arial";
  883. m_superweaponReadyPointSize = 10;
  884. m_superweaponReadyBold = FALSE;
  885. m_superweaponFlashColor = GameMakeColor(255, 255, 255, 255);
  886. m_superweaponLastFlashFrame = 0;
  887. m_superweaponUsedFlashColor = TRUE; // so next one is false
  888. m_superweaponHiddenByScript = FALSE;
  889. m_namedTimerPosition.x = 0.05f;
  890. m_namedTimerPosition.y = 0.7f;
  891. m_namedTimerFlashDuration = 1.0f;
  892. m_namedTimerNormalFont = "Arial";
  893. m_namedTimerNormalPointSize = 10;
  894. m_namedTimerNormalBold = FALSE;
  895. m_namedTimerReadyFont = "Arial";
  896. m_namedTimerReadyPointSize = 10;
  897. m_namedTimerReadyBold = FALSE;
  898. m_namedTimerNormalColor = GameMakeColor(255, 255, 0, 255);
  899. m_namedTimerReadyColor = GameMakeColor(255, 0, 255, 255);
  900. m_namedTimerFlashColor = GameMakeColor( 0, 255, 255, 255);
  901. m_namedTimerLastFlashFrame = 0;
  902. m_namedTimerUsedFlashColor = TRUE; // so next one is false
  903. m_showNamedTimers = TRUE;
  904. m_floatingTextTimeOut = DEFAULT_FLOATING_TEXT_TIMEOUT;
  905. m_floatingTextMoveUpSpeed = 1.0f;
  906. m_floatingTextMoveVanishRate = 0.1f;
  907. m_drawableCaptionFont = "Arial";
  908. m_drawableCaptionPointSize = 10;
  909. m_drawableCaptionBold = FALSE;
  910. m_drawableCaptionColor = GameMakeColor(255, 255, 255, 255);
  911. m_drawRMBScrollAnchor = FALSE;
  912. m_moveRMBScrollAnchor = FALSE;
  913. m_displayedMaxWarning = FALSE;
  914. m_idleWorkerWin = NULL;
  915. m_currentIdleWorkerDisplay = -1;
  916. m_waypointMode = false;
  917. m_forceAttackMode = false;
  918. m_forceMoveToMode = false;
  919. m_attackMoveToMode = false;
  920. m_preferSelection = false;
  921. m_curRcType = RADIUSCURSOR_NONE;
  922. m_soloNexusSelectedDrawableID = INVALID_DRAWABLE_ID;
  923. } // end InGameUI
  924. //-------------------------------------------------------------------------------------------------
  925. //-------------------------------------------------------------------------------------------------
  926. InGameUI::~InGameUI()
  927. {
  928. delete TheControlBar;
  929. TheControlBar = NULL;
  930. // free all the display strings if we're
  931. removeMilitarySubtitle();
  932. stopMovie();
  933. stopCameoMovie();
  934. // remove any build available status
  935. placeBuildAvailable( NULL, NULL );
  936. setRadiusCursorNone();
  937. // delete the message resources
  938. freeMessageResources();
  939. // delete the array for the drawbles
  940. delete [] m_placeIcon;
  941. m_placeIcon = NULL;
  942. // clear floating text
  943. clearFloatingText();
  944. // clear world animations
  945. clearWorldAnimations();
  946. resetIdleWorker();
  947. }
  948. //-------------------------------------------------------------------------------------------------
  949. /** Initialize the in game user interface */
  950. //-------------------------------------------------------------------------------------------------
  951. void InGameUI::init( void )
  952. {
  953. INI ini;
  954. ini.load( AsciiString( "Data\\INI\\InGameUI.ini" ), INI_LOAD_OVERWRITE, NULL );
  955. //override INI values with language localized values:
  956. if (TheGlobalLanguageData)
  957. {
  958. if (TheGlobalLanguageData->m_drawableCaptionFont.name.isNotEmpty())
  959. { m_drawableCaptionFont = TheGlobalLanguageData->m_drawableCaptionFont.name;
  960. m_drawableCaptionPointSize = TheGlobalLanguageData->m_drawableCaptionFont.size;
  961. m_drawableCaptionBold = TheGlobalLanguageData->m_drawableCaptionFont.bold;
  962. }
  963. if (TheGlobalLanguageData->m_messageFont.name.isNotEmpty())
  964. { m_messageFont = TheGlobalLanguageData->m_messageFont.name;
  965. m_messagePointSize = TheGlobalLanguageData->m_messageFont.size;
  966. m_messageBold = TheGlobalLanguageData->m_messageFont.bold;
  967. }
  968. if (TheGlobalLanguageData->m_militaryCaptionTitleFont.name.isNotEmpty())
  969. { m_militaryCaptionTitleFont = TheGlobalLanguageData->m_militaryCaptionTitleFont.name;
  970. m_militaryCaptionTitlePointSize = TheGlobalLanguageData->m_militaryCaptionTitleFont.size;
  971. m_militaryCaptionTitleBold = TheGlobalLanguageData->m_militaryCaptionTitleFont.bold;
  972. }
  973. if (TheGlobalLanguageData->m_militaryCaptionFont.name.isNotEmpty())
  974. { m_militaryCaptionFont = TheGlobalLanguageData->m_militaryCaptionFont.name;
  975. m_militaryCaptionPointSize = TheGlobalLanguageData->m_militaryCaptionFont.size;
  976. m_militaryCaptionBold = TheGlobalLanguageData->m_militaryCaptionFont.bold;
  977. }
  978. if (TheGlobalLanguageData->m_superweaponCountdownNormalFont.name.isNotEmpty())
  979. { m_superweaponNormalFont = TheGlobalLanguageData->m_superweaponCountdownNormalFont.name;
  980. m_superweaponNormalPointSize = TheGlobalLanguageData->m_superweaponCountdownNormalFont.size;
  981. m_superweaponNormalBold = TheGlobalLanguageData->m_superweaponCountdownNormalFont.bold;
  982. }
  983. if (TheGlobalLanguageData->m_superweaponCountdownReadyFont.name.isNotEmpty())
  984. { m_superweaponReadyFont = TheGlobalLanguageData->m_superweaponCountdownReadyFont.name;
  985. m_superweaponReadyPointSize = TheGlobalLanguageData->m_superweaponCountdownReadyFont.size;
  986. m_superweaponReadyBold = TheGlobalLanguageData->m_superweaponCountdownReadyFont.bold;
  987. }
  988. if (TheGlobalLanguageData->m_namedTimerCountdownNormalFont.name.isNotEmpty())
  989. { m_namedTimerNormalFont = TheGlobalLanguageData->m_namedTimerCountdownNormalFont.name;
  990. m_namedTimerNormalPointSize = TheGlobalLanguageData->m_namedTimerCountdownNormalFont.size;
  991. m_namedTimerNormalBold = TheGlobalLanguageData->m_namedTimerCountdownNormalFont.bold;
  992. }
  993. if (TheGlobalLanguageData->m_namedTimerCountdownReadyFont.name.isNotEmpty())
  994. { m_namedTimerReadyFont = TheGlobalLanguageData->m_namedTimerCountdownReadyFont.name;
  995. m_namedTimerReadyPointSize = TheGlobalLanguageData->m_namedTimerCountdownReadyFont.size;
  996. m_namedTimerReadyBold = TheGlobalLanguageData->m_namedTimerCountdownReadyFont.bold;
  997. }
  998. }
  999. /**@ todo we used to put in the hint spy translator, but it's difficult
  1000. to order the translators when the code is not centralized so it has
  1001. been moved to where all the other translators are attached in game client */
  1002. // create the tactical view
  1003. if (TheDisplay)
  1004. {
  1005. TheTacticalView = createView();
  1006. TheTacticalView->init();
  1007. TheDisplay->attachView( TheTacticalView );
  1008. // make the tactical display the full screen width for now
  1009. TheTacticalView->setWidth( TheDisplay->getWidth());
  1010. // make the tactical display 0.76 of full screen so no drawing under GUI.
  1011. TheTacticalView->setHeight( TheDisplay->getHeight() * 0.77f);
  1012. }
  1013. TheTacticalView->setDefaultView(0.0f, 0.0f, 1.0f);
  1014. /** @todo this may be the wrong place to create the sidebar, but for now
  1015. this is where it lives */
  1016. createControlBar();
  1017. /** @todo This may be the wrong place to create the replay menu, but for now
  1018. this is where it lives */
  1019. createReplayControl();
  1020. // create the command bar
  1021. TheControlBar = NEW ControlBar;
  1022. TheControlBar->init();
  1023. m_windowLayouts.clear();
  1024. m_soloNexusSelectedDrawableID = INVALID_DRAWABLE_ID;
  1025. } // end init
  1026. //-------------------------------------------------------------------------------------------------
  1027. //-------------------------------------------------------------------------------------------------
  1028. void InGameUI::setRadiusCursor(RadiusCursorType cursorType, const SpecialPowerTemplate* specPowTempl, WeaponSlotType weaponSlot)
  1029. {
  1030. if (cursorType == m_curRcType)
  1031. return;
  1032. m_curRadiusCursor.clear();
  1033. m_curRcType = RADIUSCURSOR_NONE;
  1034. if (cursorType == RADIUSCURSOR_NONE)
  1035. return;
  1036. Object* obj = NULL;
  1037. if( m_pendingGUICommand && m_pendingGUICommand->getCommandType() == GUI_COMMAND_SPECIAL_POWER_FROM_SHORTCUT )
  1038. {
  1039. if( ThePlayerList && ThePlayerList->getLocalPlayer() && specPowTempl != NULL )
  1040. {
  1041. obj = ThePlayerList->getLocalPlayer()->findMostReadyShortcutSpecialPowerOfType( specPowTempl->getSpecialPowerType() );
  1042. }
  1043. }
  1044. else
  1045. {
  1046. if (getSelectCount() == 0)
  1047. return;
  1048. Drawable *draw = getFirstSelectedDrawable();
  1049. if (draw == NULL)
  1050. return;
  1051. obj = draw->getObject();
  1052. }
  1053. if (obj == NULL)
  1054. return;
  1055. Player* controller = obj->getControllingPlayer();
  1056. if (controller == NULL)
  1057. return;
  1058. Real radius = 0.0f;
  1059. const Weapon* w = NULL;
  1060. switch (cursorType)
  1061. {
  1062. // already handled
  1063. //case RADIUSCURSOR_NONE:
  1064. // return;
  1065. case RADIUSCURSOR_ATTACK_DAMAGE_AREA:
  1066. w = obj->getWeaponInWeaponSlot(weaponSlot);
  1067. radius = w ? w->getPrimaryDamageRadius(obj) : 0.0f;
  1068. break;
  1069. case RADIUSCURSOR_ATTACK_SCATTER_AREA:
  1070. w = obj->getWeaponInWeaponSlot(weaponSlot);
  1071. radius = w ? (w->getScatterRadius() + w->getScatterTargetScalar()) : 0.0f;
  1072. break;
  1073. case RADIUSCURSOR_ATTACK_CONTINUE_AREA:
  1074. case RADIUSCURSOR_CLEARMINES:
  1075. w = obj->getWeaponInWeaponSlot(weaponSlot);
  1076. radius = w ? w->getContinueAttackRange() : 0.0f;
  1077. break;
  1078. case RADIUSCURSOR_GUARD_AREA:
  1079. radius = AIGuardMachine::getStdGuardRange(obj);
  1080. break;
  1081. case RADIUSCURSOR_FRIENDLY_SPECIALPOWER:
  1082. case RADIUSCURSOR_OFFENSIVE_SPECIALPOWER:
  1083. case RADIUSCURSOR_SUPERWEAPON_SCATTER_AREA:
  1084. case RADIUSCURSOR_EMERGENCY_REPAIR:
  1085. case RADIUSCURSOR_PARTICLECANNON:
  1086. case RADIUSCURSOR_A10STRIKE:
  1087. case RADIUSCURSOR_SPECTREGUNSHIP:
  1088. case RADIUSCURSOR_HELIX_NAPALM_BOMB:
  1089. case RADIUSCURSOR_DAISYCUTTER:
  1090. case RADIUSCURSOR_CARPETBOMB:
  1091. case RADIUSCURSOR_PARADROP:
  1092. case RADIUSCURSOR_SPYSATELLITE:
  1093. case RADIUSCURSOR_NUCLEARMISSILE:
  1094. case RADIUSCURSOR_EMPPULSE:
  1095. case RADIUSCURSOR_ARTILLERYBARRAGE:
  1096. case RADIUSCURSOR_FRENZY:
  1097. case RADIUSCURSOR_NAPALMSTRIKE:
  1098. case RADIUSCURSOR_CLUSTERMINES:
  1099. case RADIUSCURSOR_SCUDSTORM:
  1100. case RADIUSCURSOR_ANTHRAXBOMB:
  1101. case RADIUSCURSOR_AMBUSH:
  1102. case RADIUSCURSOR_RADAR:
  1103. case RADIUSCURSOR_SPYDRONE:
  1104. case RADIUSCURSOR_AMBULANCE:
  1105. radius = specPowTempl ? specPowTempl->getRadiusCursorRadius() : 0.0f;
  1106. break;
  1107. }
  1108. if (radius <= 0.0f)
  1109. return;
  1110. Coord3D pos = { 0, 0, 0 }; // will be updated right away
  1111. m_radiusCursors[cursorType].createRadiusDecal(pos, radius, controller, m_curRadiusCursor);
  1112. m_curRcType = cursorType;
  1113. handleRadiusCursor();
  1114. }
  1115. //-------------------------------------------------------------------------------------------------
  1116. /** handle updating of "radius cursors" that follow the mouse pos */
  1117. //-------------------------------------------------------------------------------------------------
  1118. void InGameUI::handleRadiusCursor()
  1119. {
  1120. if (!m_curRadiusCursor.isEmpty())
  1121. {
  1122. const MouseIO* mouseIO = TheMouse->getMouseStatus();
  1123. Coord3D pos;
  1124. //
  1125. // if the mouse is in the radar window, the position in the world is that which is
  1126. // represented by the radar, otherwise we use the mouse position itself transformed
  1127. // from screen to world
  1128. // But only if the radar is on.
  1129. //
  1130. Bool radarOn = TheRadar->isRadarForced()
  1131. || ( !TheRadar->isRadarHidden()
  1132. && ThePlayerList->getLocalPlayer()
  1133. && ThePlayerList->getLocalPlayer()->hasRadar()
  1134. );
  1135. if( !radarOn || (TheRadar->screenPixelToWorld( &mouseIO->pos, &pos ) == FALSE) )// if radar off, or point not on radar
  1136. TheTacticalView->screenToTerrain( &mouseIO->pos, &pos );
  1137. if ( TheGlobalData->m_doubleClickAttackMove && m_duringDoubleClickAttackMoveGuardHintTimer > 0 )
  1138. {
  1139. m_curRadiusCursor.setOpacity( m_duringDoubleClickAttackMoveGuardHintTimer * 0.1f );
  1140. m_curRadiusCursor.setPosition( m_duringDoubleClickAttackMoveGuardHintStashedPosition ); //world space position of center of decal
  1141. }
  1142. else
  1143. {
  1144. m_curRadiusCursor.setPosition(pos); //world space position of center of decal
  1145. m_curRadiusCursor.update();
  1146. }
  1147. }
  1148. }
  1149. void InGameUI::triggerDoubleClickAttackMoveGuardHint( void )
  1150. {
  1151. m_duringDoubleClickAttackMoveGuardHintTimer = 11;
  1152. const MouseIO* mouseIO = TheMouse->getMouseStatus();
  1153. TheTacticalView->screenToTerrain( &mouseIO->pos, &m_duringDoubleClickAttackMoveGuardHintStashedPosition );
  1154. }
  1155. //-------------------------------------------------------------------------------------------------
  1156. /** Handle the placement "icons" that appear at the cursor when we're putting down a
  1157. * structure to build. Note that this has additional logic to also show a line
  1158. * of objects because when we build "walls" we want to draw a line of repeating
  1159. * wall pieces on the map where we want to put all of them */
  1160. //-------------------------------------------------------------------------------------------------
  1161. void InGameUI::evaluateSoloNexus( Drawable *newlyAddedDrawable )
  1162. {
  1163. m_soloNexusSelectedDrawableID = INVALID_DRAWABLE_ID;//failsafe...
  1164. // short test: If the thing just added is a nonmobster, bail with NULL
  1165. if ( newlyAddedDrawable )
  1166. {
  1167. const Object *newObj = newlyAddedDrawable->getObject();
  1168. if ( newObj && ! ( newObj->isKindOf(KINDOF_MOB_NEXUS) || newObj->isKindOf(KINDOF_IGNORED_IN_GUI) ) )
  1169. return;
  1170. }
  1171. //LoopAllSelectedDrawables
  1172. UnsignedShort nexaeFound = 0;
  1173. for( DrawableListCIt it = m_selectedDrawables.begin(); it != m_selectedDrawables.end(); ++it )
  1174. {
  1175. Drawable *draw = (*it);
  1176. const Object *obj = draw->getObject();
  1177. if ( ! obj )
  1178. continue;
  1179. if ( obj->isKindOf( KINDOF_MOB_NEXUS ) )
  1180. {
  1181. ++nexaeFound;
  1182. if ( nexaeFound == 1 )
  1183. {
  1184. m_soloNexusSelectedDrawableID = draw->getID();
  1185. }
  1186. else // darn! more than one!
  1187. {
  1188. m_soloNexusSelectedDrawableID = INVALID_DRAWABLE_ID;
  1189. return;
  1190. }
  1191. }
  1192. else if ( ! obj->isKindOf( KINDOF_IGNORED_IN_GUI ) )// darn! a non-angrymobster!
  1193. {
  1194. m_soloNexusSelectedDrawableID = INVALID_DRAWABLE_ID;
  1195. return;
  1196. }
  1197. } // end for
  1198. }
  1199. void InGameUI::handleBuildPlacements( void )
  1200. {
  1201. //
  1202. // if we're in the process of placing something we need up update one or more drawables
  1203. // based on the position of the mouse
  1204. //
  1205. if( m_pendingPlaceType )
  1206. {
  1207. ICoord2D loc;
  1208. Coord3D world;
  1209. Real angle = m_placeIcon[ 0 ]->getOrientation();
  1210. // update the angle of the icon to match any placement angle and pick the
  1211. // location the icon will be at (anchored is the start, otherwise it's the mouse)
  1212. if( isPlacementAnchored() )
  1213. {
  1214. ICoord2D start, end;
  1215. // get the placement arrow points
  1216. getPlacementPoints( &start, &end );
  1217. // set icon to anchor point
  1218. loc = start;
  1219. // only adjust angle if we've actually moved the mouse
  1220. if( start.x != end.x || start.y != end.y )
  1221. {
  1222. Coord3D worldStart, worldEnd;
  1223. // project the start and the end points of the line anchor into the 3D world
  1224. TheTacticalView->screenToTerrain( &start, &worldStart );
  1225. TheTacticalView->screenToTerrain( &end, &worldEnd );
  1226. Coord2D v;
  1227. v.x = worldEnd.x - worldStart.x;
  1228. v.y = worldEnd.y - worldStart.y;
  1229. angle = v.toAngle();
  1230. } // end if
  1231. } // end if
  1232. else
  1233. {
  1234. const MouseIO *mouseIO = TheMouse->getMouseStatus();
  1235. // location is the mouse position
  1236. loc = mouseIO->pos;
  1237. } // end else
  1238. // set the location and angle of the place icon
  1239. /**@todo this whole orientation vector thing is LAME! Must replace, all I want to
  1240. to do is set a simple angle and have it automatically change, ug! */
  1241. TheTacticalView->screenToTerrain( &loc, &world );
  1242. m_placeIcon[ 0 ]->setPosition( &world );
  1243. m_placeIcon[ 0 ]->setOrientation( angle );
  1244. //
  1245. // check to see if this is a legal location to build something at and tint or "un-tint"
  1246. // the cursor icons as appropriate. This involves a pathfind which could be
  1247. // expensive so we don't want to do it on every frame (althought that would be ideal)
  1248. // If we discover there are cases that this is just too slow we should increase the
  1249. // delay time between checks or we need to come up with a way of recording what is
  1250. // valid and what isn't or "fudge" the results to feel "ok"
  1251. //
  1252. if( TheGameClient->getFrame() & 0x1 )
  1253. {
  1254. TheTerrainVisual->removeAllBibs();
  1255. Object *builderObject = TheGameLogic->findObjectByID( getPendingPlaceSourceObjectID() );
  1256. LegalBuildCode lbc;
  1257. lbc = TheBuildAssistant->isLocationLegalToBuild( &world,
  1258. m_pendingPlaceType,
  1259. angle,
  1260. BuildAssistant::USE_QUICK_PATHFIND |
  1261. BuildAssistant::TERRAIN_RESTRICTIONS |
  1262. BuildAssistant::CLEAR_PATH |
  1263. BuildAssistant::NO_OBJECT_OVERLAP |
  1264. BuildAssistant::SHROUD_REVEALED |
  1265. BuildAssistant::IGNORE_STEALTHED,
  1266. builderObject,
  1267. NULL );
  1268. if( lbc != LBC_OK )
  1269. m_placeIcon[ 0 ]->colorTint( &illegalBuildColor );
  1270. else
  1271. m_placeIcon[ 0 ]->colorTint( NULL );
  1272. // Add the bibs around the structure.
  1273. if (lbc != LBC_OK)
  1274. {
  1275. TheTerrainVisual->addFactionBibDrawable(m_placeIcon[0], lbc != LBC_OK);
  1276. } else {
  1277. TheTerrainVisual->removeFactionBibDrawable(m_placeIcon[0]);
  1278. }
  1279. } // end if
  1280. //
  1281. // we have additional place icons when we're placing down a line of walls or other
  1282. // similarly placed object ... for those we will have them be oriented the same way
  1283. // as the first one, but we'll set their positions so that they "tile" end to end
  1284. //
  1285. if( isPlacementAnchored() && TheBuildAssistant->isLineBuildTemplate( m_pendingPlaceType ) )
  1286. {
  1287. Int i;
  1288. // get our line placement points
  1289. ICoord2D screenStart, screenEnd;
  1290. getPlacementPoints( &screenStart, &screenEnd );
  1291. // project the start and the end points of the line anchor into the 3D world
  1292. Coord3D worldStart, worldEnd;
  1293. TheTacticalView->screenToTerrain( &screenStart, &worldStart );
  1294. TheTacticalView->screenToTerrain( &screenEnd, &worldEnd );
  1295. // how big are each of our objects
  1296. Real objectSize = m_pendingPlaceType->getTemplateGeometryInfo().getMajorRadius() * 2.0f;
  1297. // what is our max tiling length we can make
  1298. Int maxObjects = TheGlobalData->m_maxLineBuildObjects;
  1299. // get the builder object that will be constructing things
  1300. Object *builderObject = TheGameLogic->findObjectByID( TheInGameUI->getPendingPlaceSourceObjectID() );
  1301. //
  1302. // given the start/end points in the world and the the angle of the wall, fill
  1303. // out an array of positions that "tile" this wall across the landscape
  1304. //
  1305. BuildAssistant::TileBuildInfo *tileBuildInfo;
  1306. tileBuildInfo = TheBuildAssistant->buildTiledLocations( m_pendingPlaceType, angle,
  1307. &worldStart, &worldEnd,
  1308. objectSize, maxObjects,
  1309. builderObject );
  1310. // create any necessary drawables we need to "fill out" the line
  1311. for( i = 0; i < tileBuildInfo->tilesUsed; i++ )
  1312. {
  1313. if( m_placeIcon[ i ] == NULL )
  1314. m_placeIcon[ i ] = TheThingFactory->newDrawable( m_pendingPlaceType,
  1315. DRAWABLE_STATUS_NO_STATE_PARTICLES );
  1316. } // end for i
  1317. //
  1318. // destroy any drawables that we're not using anymore because a previous
  1319. // line length was longer
  1320. //
  1321. for( i = tileBuildInfo->tilesUsed; i < maxObjects; i++ )
  1322. {
  1323. if( m_placeIcon[ i ] != NULL )
  1324. TheGameClient->destroyDrawable( m_placeIcon[ i ] );
  1325. m_placeIcon[ i ] = NULL;
  1326. } // end for i
  1327. //
  1328. // march down each drawable and set the position based on its position in the
  1329. // line and set their angles all the same
  1330. //
  1331. for( i = 0; i < tileBuildInfo->tilesUsed; i++ )
  1332. {
  1333. // set the drawble position
  1334. m_placeIcon[ i ]->setPosition( &tileBuildInfo->positions[ i ] );
  1335. // set opacity for the drawble
  1336. m_placeIcon[ i ]->setDrawableOpacity( placementOpacity );
  1337. // set the drawable angle
  1338. m_placeIcon[ i ]->setOrientation( angle );
  1339. } // end for i
  1340. } // end if
  1341. } // end if
  1342. } // end handleBuildPlacements
  1343. //-------------------------------------------------------------------------------------------------
  1344. /** Pre-draw phase of the in game ui */
  1345. //-------------------------------------------------------------------------------------------------
  1346. void InGameUI::preDraw( void )
  1347. {
  1348. // handle any "icons" for the act of building things and placing them in the world
  1349. handleBuildPlacements();
  1350. // handle radius-cursors, if any
  1351. handleRadiusCursor();
  1352. // draw the floating text first;
  1353. drawFloatingText();
  1354. // draw world animations
  1355. updateAndDrawWorldAnimations();
  1356. } // end preDraw
  1357. //-------------------------------------------------------------------------------------------------
  1358. /** Update the in game user interface */
  1359. //-------------------------------------------------------------------------------------------------
  1360. //DECLARE_PERF_TIMER(InGameUI_update)
  1361. void InGameUI::update( void )
  1362. {
  1363. //USE_PERF_TIMER(InGameUI_update)
  1364. Int i;
  1365. /// @todo make sure this code gets called even when the UI is not being drawn
  1366. if ( m_videoStream && m_videoBuffer )
  1367. {
  1368. if ( m_videoStream->isFrameReady())
  1369. {
  1370. m_videoStream->frameDecompress();
  1371. m_videoStream->frameRender( m_videoBuffer );
  1372. m_videoStream->frameNext();
  1373. if ( m_videoStream->frameIndex() == 0 )
  1374. {
  1375. stopMovie();
  1376. }
  1377. }
  1378. }
  1379. if ( m_cameoVideoStream && m_cameoVideoBuffer )
  1380. {
  1381. if ( m_cameoVideoStream->isFrameReady())
  1382. {
  1383. m_cameoVideoStream->frameDecompress();
  1384. m_cameoVideoStream->frameRender( m_cameoVideoBuffer );
  1385. m_cameoVideoStream->frameNext();
  1386. // if ( m_cameoVideoStream->frameIndex() == 0 )
  1387. // {
  1388. // stopMovie();
  1389. // }
  1390. }
  1391. }
  1392. //
  1393. // remove any message strings that have expired, note that the oldest strings are
  1394. // always at the end of the array (higher index numbers) so we can just remove things
  1395. // from the rear and never have to worry about shifting entries cause we check every
  1396. // frame
  1397. //
  1398. UnsignedInt currLogicFrame = TheGameLogic->getFrame();
  1399. const int messageTimeout = m_messageDelayMS / LOGICFRAMES_PER_SECOND / 1000;
  1400. UnsignedByte r, g, b, a;
  1401. Int amount;
  1402. for( i = MAX_UI_MESSAGES - 1; i >= 0; i-- )
  1403. {
  1404. if( currLogicFrame - m_uiMessages[ i ].timestamp > messageTimeout )
  1405. {
  1406. // get the current color of this text
  1407. GameGetColorComponents( m_uiMessages[ i ].color, &r, &g, &b, &a );
  1408. // start fading the alpha on this color down
  1409. amount = REAL_TO_INT( ((currLogicFrame - m_uiMessages[ i ].timestamp) * 0.01f) );
  1410. if( a - amount < 0 )
  1411. a = 0;
  1412. else
  1413. a -= amount;
  1414. // set the new color
  1415. m_uiMessages[ i ].color = GameMakeColor( r, g, b, a );
  1416. // when alpha is completely zero we remove this string
  1417. if( a == 0 )
  1418. removeMessageAtIndex( i );
  1419. } // end if
  1420. } // end for i
  1421. //
  1422. // Update the Military Subtitle display
  1423. //
  1424. if( m_militarySubtitle ) // if we have a subtitle, work on it
  1425. {
  1426. // if the timeis frozen by a script, then we still want the text to display
  1427. if(TheScriptEngine->isTimeFrozenScript())
  1428. {
  1429. m_militarySubtitle->lifetime--;
  1430. m_militarySubtitle->blockBeginFrame--;
  1431. m_militarySubtitle->incrementOnFrame--;
  1432. }
  1433. // if it's time to remove the subtitle, Then remove it
  1434. if((Int)m_militarySubtitle->lifetime < (Int)currLogicFrame)
  1435. {
  1436. //steal colins fade from above :)
  1437. GameGetColorComponents( m_militarySubtitle->color, &r, &g, &b, &a );
  1438. // start fading the alpha on this color down
  1439. amount = REAL_TO_INT( ((currLogicFrame - m_militarySubtitle->lifetime ) * 0.1f) );
  1440. if( a - amount < 0 )
  1441. {
  1442. removeMilitarySubtitle();
  1443. }
  1444. else
  1445. {
  1446. a -= amount;
  1447. m_militarySubtitle->color = GameMakeColor(r, g, b, a);
  1448. }
  1449. }
  1450. else
  1451. {
  1452. // trigger whether or not we should draw the block
  1453. if( m_militarySubtitle->blockBeginFrame + 9 < currLogicFrame )
  1454. {
  1455. m_militarySubtitle->blockBeginFrame = currLogicFrame;
  1456. m_militarySubtitle->blockDrawn = !m_militarySubtitle->blockDrawn;
  1457. }
  1458. // If it's time to add another letter to the display string, lets do that.
  1459. if( m_militarySubtitle->incrementOnFrame < currLogicFrame )
  1460. {
  1461. // first grab the letter we want to add
  1462. WideChar tempWChar = m_militarySubtitle->subtitle.getCharAt(m_militarySubtitle->index);
  1463. // if that letter is a return, add a new line
  1464. if(tempWChar == L'\n')
  1465. {
  1466. // increment the Block position's Y value to draw it on the next line
  1467. Int height;
  1468. m_militarySubtitle->displayStrings[m_militarySubtitle->currentDisplayString]->getSize(NULL, &height);
  1469. m_militarySubtitle->blockPos.y = m_militarySubtitle->blockPos.y + height;
  1470. // Now add a new display string
  1471. m_militarySubtitle->currentDisplayString++;
  1472. if(!(m_militarySubtitle->currentDisplayString >= MAX_SUBTITLE_LINES) )
  1473. {
  1474. m_militarySubtitle->blockPos.x = m_militarySubtitle->position.x;
  1475. m_militarySubtitle->displayStrings[m_militarySubtitle->currentDisplayString] = TheDisplayStringManager->newDisplayString();
  1476. m_militarySubtitle->displayStrings[m_militarySubtitle->currentDisplayString]->reset();
  1477. m_militarySubtitle->displayStrings[m_militarySubtitle->currentDisplayString]->setFont( TheFontLibrary->getFont( m_militaryCaptionFont, TheGlobalLanguageData->adjustFontSize(m_militaryCaptionPointSize), m_militaryCaptionBold ) ) ;
  1478. m_militarySubtitle->blockDrawn = TRUE;
  1479. m_militarySubtitle->incrementOnFrame = currLogicFrame + (Int)(((Real)LOGICFRAMES_PER_SECOND * TheGlobalLanguageData->m_militaryCaptionDelayMS)/1000.0f);
  1480. }
  1481. else
  1482. {
  1483. // if we've exceeded the allocated number of display strings, this will force us to essentially truncate the remaining text
  1484. m_militarySubtitle->index = m_militarySubtitle->subtitle.getLength();
  1485. DEBUG_CRASH(("You're Only Allowed to use %d lines of subtitle text\n",MAX_SUBTITLE_LINES));
  1486. }
  1487. }
  1488. else
  1489. {
  1490. // okay, we're not a \n, lets append this character to the display string
  1491. m_militarySubtitle->displayStrings[m_militarySubtitle->currentDisplayString]->appendChar(tempWChar);
  1492. // increment the draw position of the block
  1493. Int width;
  1494. m_militarySubtitle->displayStrings[m_militarySubtitle->currentDisplayString]->getSize(&width,NULL);
  1495. m_militarySubtitle->blockPos.x = m_militarySubtitle->position.x + width;
  1496. // lets make a sound
  1497. static AudioEventRTS click("MilitarySubtitlesTyping");
  1498. TheAudio->addAudioEvent(&click);
  1499. if(TheGlobalLanguageData)
  1500. m_militarySubtitle->incrementOnFrame = currLogicFrame + TheGlobalLanguageData->m_militaryCaptionSpeed;
  1501. else
  1502. m_militarySubtitle->incrementOnFrame = currLogicFrame + m_militaryCaptionSpeed;
  1503. }
  1504. // increment the index
  1505. m_militarySubtitle->index++;
  1506. if(m_militarySubtitle->index >= m_militarySubtitle->subtitle.getLength())
  1507. {
  1508. // We're at the end of the subtitle, set everything to persist till the subtitle has expired
  1509. m_militarySubtitle->incrementOnFrame = m_militarySubtitle->lifetime + 1;
  1510. }
  1511. /*
  1512. else
  1513. {
  1514. // randomize the space between printing of characters
  1515. if(GameClientRandomValueReal(0,1) < 0.95f)
  1516. {
  1517. m_militarySubtitle->incrementOnFrame = GameClientRandomValue(2, 5) + currLogicFrame;
  1518. }
  1519. else
  1520. {
  1521. m_militarySubtitle->incrementOnFrame = GameClientRandomValue(10, 13) + currLogicFrame;
  1522. }
  1523. }*/
  1524. }
  1525. }
  1526. }
  1527. // update the player money window if the money amount has changed
  1528. // this seems like as good a place as any to do the power hide/show
  1529. static Int lastMoney = -1;
  1530. static NameKeyType moneyWindowKey = TheNameKeyGenerator->nameToKey( "ControlBar.wnd:MoneyDisplay" );
  1531. static NameKeyType powerWindowKey = TheNameKeyGenerator->nameToKey( "ControlBar.wnd:PowerWindow" );
  1532. GameWindow *moneyWin = TheWindowManager->winGetWindowFromId( NULL, moneyWindowKey );
  1533. GameWindow *powerWin = TheWindowManager->winGetWindowFromId( NULL, powerWindowKey );
  1534. // if( moneyWin == NULL )
  1535. // {
  1536. // NameKeyType moneyWindowKey = TheNameKeyGenerator->nameToKey( "ControlBar.wnd:MoneyDisplay" );
  1537. //
  1538. // moneyWin = TheWindowManager->winGetWindowFromId( NULL, moneyWindowKey );
  1539. //
  1540. // } // end if
  1541. Player *moneyPlayer = NULL;
  1542. if( TheControlBar->isObserverControlBarOn())
  1543. moneyPlayer = TheControlBar->getObserverLookAtPlayer();
  1544. else
  1545. moneyPlayer = ThePlayerList->getLocalPlayer();
  1546. if( moneyPlayer)
  1547. {
  1548. Int currentMoney = moneyPlayer->getMoney()->countMoney();
  1549. if( lastMoney != currentMoney )
  1550. {
  1551. UnicodeString buffer;
  1552. buffer.format( TheGameText->fetch( "GUI:ControlBarMoneyDisplay" ), currentMoney );
  1553. GadgetStaticTextSetText( moneyWin, buffer );
  1554. lastMoney = currentMoney;
  1555. } // end if
  1556. moneyWin->winHide(FALSE);
  1557. powerWin->winHide(FALSE);
  1558. }
  1559. else
  1560. {
  1561. moneyWin->winHide(TRUE);
  1562. powerWin->winHide(TRUE);
  1563. }
  1564. // Update the floating Text;
  1565. updateFloatingText();
  1566. // update the control bar
  1567. TheControlBar->update();
  1568. updateIdleWorker();
  1569. // update any random window layout that so requests
  1570. for (std::list<WindowLayout *>::iterator it = m_windowLayouts.begin(); it != m_windowLayouts.end(); ++it)
  1571. {
  1572. WindowLayout *layout = *it;
  1573. layout->runUpdate();
  1574. }
  1575. //Handle keyboard camera rotations
  1576. if( m_cameraRotatingLeft && !m_cameraRotatingRight )
  1577. {
  1578. //Keyboard rotate left
  1579. TheTacticalView->setAngle( TheTacticalView->getAngle() - TheGlobalData->m_keyboardCameraRotateSpeed );
  1580. }
  1581. if( m_cameraRotatingRight && !m_cameraRotatingLeft )
  1582. {
  1583. //Keyboard rotate right
  1584. TheTacticalView->setAngle( TheTacticalView->getAngle() + TheGlobalData->m_keyboardCameraRotateSpeed );
  1585. }
  1586. if( m_cameraZoomingIn && !m_cameraZoomingOut )
  1587. {
  1588. //Keyboard zoom in
  1589. TheTacticalView->zoomIn();
  1590. }
  1591. if( m_cameraZoomingOut && !m_cameraZoomingIn )
  1592. {
  1593. //Keyboard zoom out
  1594. TheTacticalView->zoomOut();
  1595. }
  1596. } // end update
  1597. //-------------------------------------------------------------------------------------------------
  1598. void InGameUI::registerWindowLayout( WindowLayout *layout )
  1599. {
  1600. unregisterWindowLayout(layout); // sanity
  1601. m_windowLayouts.push_back(layout);
  1602. }
  1603. //-------------------------------------------------------------------------------------------------
  1604. void InGameUI::unregisterWindowLayout( WindowLayout *layout )
  1605. {
  1606. for (std::list<WindowLayout *>::iterator it = m_windowLayouts.begin(); it != m_windowLayouts.end(); ++it)
  1607. {
  1608. if (*it == layout)
  1609. {
  1610. m_windowLayouts.erase(it);
  1611. return;
  1612. }
  1613. }
  1614. }
  1615. //-------------------------------------------------------------------------------------------------
  1616. /** Reset the in game user interface */
  1617. //-------------------------------------------------------------------------------------------------
  1618. void InGameUI::reset( void )
  1619. {
  1620. m_isQuitMenuVisible = FALSE;
  1621. m_inputEnabled = true;
  1622. // reset the command bar
  1623. TheControlBar->reset();
  1624. TheTacticalView->setDefaultView(0.0f, 0.0f, 1.0f);
  1625. ResetInGameChat();
  1626. // stop any movie currently playing
  1627. stopMovie();
  1628. // remove any pending GUI command
  1629. setGUICommand( NULL );
  1630. // remove any build available status
  1631. placeBuildAvailable( NULL, NULL );
  1632. // free any message resources allocated
  1633. freeMessageResources();
  1634. Int i;
  1635. for (i=0; i<MAX_PLAYER_COUNT; ++i)
  1636. {
  1637. for (SuperweaponMap::iterator mapIt = m_superweapons[i].begin(); mapIt != m_superweapons[i].end(); ++mapIt)
  1638. {
  1639. for (SuperweaponList::iterator listIt = mapIt->second.begin(); listIt != mapIt->second.end(); ++listIt)
  1640. {
  1641. SuperweaponInfo *info = *listIt;
  1642. info->deleteInstance();
  1643. }
  1644. mapIt->second.clear();
  1645. }
  1646. m_superweapons[i].clear();
  1647. }
  1648. for (NamedTimerMapIt timerIt = m_namedTimers.begin(); timerIt != m_namedTimers.end(); ++timerIt)
  1649. {
  1650. NamedTimerInfo *info = timerIt->second;
  1651. TheDisplayStringManager->freeDisplayString(info->displayString);
  1652. info->deleteInstance();
  1653. }
  1654. m_namedTimers.clear();
  1655. m_namedTimerLastFlashFrame = 0;
  1656. m_namedTimerUsedFlashColor = TRUE; // so next one is false
  1657. m_showNamedTimers = TRUE;
  1658. removeMilitarySubtitle();
  1659. clearPopupMessageData();
  1660. m_superweaponLastFlashFrame = 0;
  1661. m_superweaponUsedFlashColor = TRUE; // so next one is false
  1662. m_superweaponHiddenByScript = FALSE;
  1663. clearFloatingText();
  1664. clearWorldAnimations();
  1665. resetIdleWorker();
  1666. // clear hint lists
  1667. for( i = 0; i < MAX_MOVE_HINTS; i++ )
  1668. {
  1669. m_moveHint[ i ].pos.zero();
  1670. m_moveHint[ i ].sourceID = 0;
  1671. m_moveHint[ i ].frame = 0;
  1672. } // end for i
  1673. m_waypointMode = false;
  1674. m_forceAttackMode = false;
  1675. m_forceMoveToMode = false;
  1676. m_attackMoveToMode = false;
  1677. m_preferSelection = false;
  1678. m_clientQuiet = false;
  1679. m_windowLayouts.clear();
  1680. m_tooltipsDisabledUntil = 0;
  1681. UpdateDiplomacyBriefingText(AsciiString::TheEmptyString, TRUE);
  1682. } // end reset
  1683. //-------------------------------------------------------------------------------------------------
  1684. /** Free any resources we used for our messages */
  1685. //-------------------------------------------------------------------------------------------------
  1686. void InGameUI::freeMessageResources( void )
  1687. {
  1688. Int i;
  1689. // release display strings and set text to empty
  1690. for( i = 0; i < MAX_UI_MESSAGES; i++ )
  1691. {
  1692. // emtpy text
  1693. m_uiMessages[ i ].fullText.clear();
  1694. // free display string
  1695. if( m_uiMessages[ i ].displayString )
  1696. TheDisplayStringManager->freeDisplayString( m_uiMessages[ i ].displayString );
  1697. m_uiMessages[ i ].displayString = NULL;
  1698. // set timestamp to zero
  1699. m_uiMessages[ i ].timestamp = 0;
  1700. } // end for i
  1701. } // end freeMessageResources
  1702. //-------------------------------------------------------------------------------------------------
  1703. /** Same as the unicode message method, but this takes an ascii string which is assumed
  1704. * to me a string manager label */
  1705. //-------------------------------------------------------------------------------------------------
  1706. // srj sez: passing as const-ref screws up varargs for some reason. dunno why. just pass by value.
  1707. void InGameUI::message( AsciiString stringManagerLabel, ... )
  1708. {
  1709. UnicodeString stringManagerString;
  1710. UnicodeString formattedMessage;
  1711. // fetch the string from the string manger
  1712. stringManagerString = TheGameText->fetch( stringManagerLabel.str() );
  1713. // construct the final text after formatting
  1714. va_list args;
  1715. va_start( args, stringManagerLabel );
  1716. WideChar buf[ UnicodeString::MAX_FORMAT_BUF_LEN ];
  1717. if( _vsnwprintf(buf, sizeof( buf )/sizeof( WideChar ) - 1, stringManagerString.str(), args ) < 0 )
  1718. throw ERROR_OUT_OF_MEMORY;
  1719. formattedMessage.set( buf );
  1720. va_end(args);
  1721. // add the text to the ui
  1722. addMessageText( formattedMessage );
  1723. } // end
  1724. //-------------------------------------------------------------------------------------------------
  1725. /** Interface for display text messages to the user */
  1726. //-------------------------------------------------------------------------------------------------
  1727. // srj sez: passing as const-ref screws up varargs for some reason. dunno why. just pass by value.
  1728. void InGameUI::message( UnicodeString format, ... )
  1729. {
  1730. UnicodeString formattedMessage;
  1731. // construct the final text after formatting
  1732. va_list args;
  1733. va_start( args, format );
  1734. WideChar buf[ UnicodeString::MAX_FORMAT_BUF_LEN ];
  1735. if( _vsnwprintf(buf, sizeof( buf )/sizeof( WideChar ) - 1, format.str(), args ) < 0 )
  1736. throw ERROR_OUT_OF_MEMORY;
  1737. formattedMessage.set( buf );
  1738. va_end(args);
  1739. // add the text to the ui
  1740. addMessageText( formattedMessage );
  1741. } // end message
  1742. //-------------------------------------------------------------------------------------------------
  1743. /** Interface for display text messages to the user */
  1744. //-------------------------------------------------------------------------------------------------
  1745. // srj sez: passing as const-ref screws up varargs for some reason. dunno why. just pass by value.
  1746. void InGameUI::messageColor( const RGBColor *rgbColor, UnicodeString format, ... )
  1747. {
  1748. UnicodeString formattedMessage;
  1749. // construct the final text after formatting
  1750. va_list args;
  1751. va_start( args, format );
  1752. WideChar buf[ UnicodeString::MAX_FORMAT_BUF_LEN ];
  1753. if( _vsnwprintf(buf, sizeof( buf )/sizeof( WideChar ) - 1, format.str(), args ) < 0 )
  1754. throw ERROR_OUT_OF_MEMORY;
  1755. formattedMessage.set( buf );
  1756. va_end(args);
  1757. // add the text to the ui
  1758. addMessageText( formattedMessage, rgbColor );
  1759. } // end message
  1760. //-------------------------------------------------------------------------------------------------
  1761. //-------------------------------------------------------------------------------------------------
  1762. void InGameUI::addMessageText( const UnicodeString& formattedMessage, const RGBColor *rgbColor )
  1763. {
  1764. Int i;
  1765. Color color1 = m_messageColor1;
  1766. Color color2 = m_messageColor2;
  1767. if (rgbColor)
  1768. {
  1769. color1 = rgbColor->getAsInt() | GameMakeColor( 0, 0, 0, 255 );
  1770. color2 = rgbColor->getAsInt() | GameMakeColor( 0, 0, 0, 255 );
  1771. }
  1772. // delete the message stuff at the last index
  1773. m_uiMessages[ MAX_UI_MESSAGES - 1 ].fullText.clear();
  1774. if( m_uiMessages[ MAX_UI_MESSAGES - 1 ].displayString )
  1775. TheDisplayStringManager->freeDisplayString( m_uiMessages[ MAX_UI_MESSAGES - 1 ].displayString );
  1776. m_uiMessages[ MAX_UI_MESSAGES - 1 ].displayString = NULL;
  1777. m_uiMessages[ MAX_UI_MESSAGES - 1 ].timestamp = 0;
  1778. // shift all the messages down one index and remove the last one
  1779. for( i = MAX_UI_MESSAGES - 1; i >= 1; i-- )
  1780. m_uiMessages[ i ] = m_uiMessages[ i - 1 ];
  1781. //
  1782. // set the new message in index 0, note that we need to allocate a display string, but
  1783. // we do not need to free the one that is already there because it has been moved
  1784. // "up" an index
  1785. //
  1786. m_uiMessages[ 0 ].fullText = formattedMessage;
  1787. m_uiMessages[ 0 ].timestamp = TheGameLogic->getFrame();
  1788. m_uiMessages[ 0 ].displayString = TheDisplayStringManager->newDisplayString();
  1789. m_uiMessages[ 0 ].displayString->setFont( TheFontLibrary->getFont( m_messageFont,
  1790. TheGlobalLanguageData->adjustFontSize(m_messagePointSize), m_messageBold ) );
  1791. m_uiMessages[ 0 ].displayString->setText( m_uiMessages[ 0 ].fullText );
  1792. //
  1793. // assign a color for this string instance that will stay with it no matter what
  1794. // line it is rendered on
  1795. //
  1796. if( m_uiMessages[ 1 ].displayString == NULL || m_uiMessages[ 1 ].color == color2 )
  1797. m_uiMessages[ 0 ].color = color1;
  1798. else
  1799. m_uiMessages[ 0 ].color = color2;
  1800. } // end addFormattedMessage
  1801. //-------------------------------------------------------------------------------------------------
  1802. /** Remove the message on screen at index i */
  1803. //-------------------------------------------------------------------------------------------------
  1804. void InGameUI::removeMessageAtIndex( Int i )
  1805. {
  1806. m_uiMessages[ i ].fullText.clear();
  1807. if( m_uiMessages[ i ].displayString )
  1808. TheDisplayStringManager->freeDisplayString( m_uiMessages[ i ].displayString );
  1809. m_uiMessages[ i ].displayString = NULL;
  1810. m_uiMessages[ i ].timestamp = 0;
  1811. } // end removeMessageAtIndex
  1812. //-------------------------------------------------------------------------------------------------
  1813. /** An area selection is occurring, start graphical "hint". */
  1814. //-------------------------------------------------------------------------------------------------
  1815. void InGameUI::beginAreaSelectHint( const GameMessage *msg )
  1816. {
  1817. m_isDragSelecting = true;
  1818. m_dragSelectRegion = msg->getArgument( 0 )->pixelRegion;
  1819. }
  1820. //-------------------------------------------------------------------------------------------------
  1821. /** An area selection has occurred, finish graphical "hint". */
  1822. //-------------------------------------------------------------------------------------------------
  1823. void InGameUI::endAreaSelectHint( const GameMessage *msg )
  1824. {
  1825. m_isDragSelecting = false;
  1826. }
  1827. //-------------------------------------------------------------------------------------------------
  1828. /** A move command has occurred, start graphical "hint". */
  1829. //-------------------------------------------------------------------------------------------------
  1830. void InGameUI::createMoveHint( const GameMessage *msg )
  1831. {
  1832. Int i;
  1833. // first, remove any existing move hint for this source if present
  1834. for( i = 0; i < MAX_MOVE_HINTS; i++ )
  1835. if( m_moveHint[ i ].sourceID == msg->getArgument( 0 )->objectID &&
  1836. m_moveHint[ i ].frame != 0 )
  1837. expireHint( MOVE_HINT, i );
  1838. if( getSelectCount() == 1 )
  1839. {
  1840. Drawable *draw = getFirstSelectedDrawable();
  1841. Object *obj = draw ? draw->getObject() : NULL;
  1842. if( obj && obj->isKindOf( KINDOF_IMMOBILE ) )
  1843. {
  1844. //Don't allow move hints to be created if our selected object can't move!
  1845. return;
  1846. }
  1847. }
  1848. m_moveHint[ m_nextMoveHint ].frame = TheGameClient->getFrame();
  1849. m_moveHint[ m_nextMoveHint ].pos = msg->getArgument( 0 )->location;
  1850. m_nextMoveHint++;
  1851. // wrap around
  1852. if (m_nextMoveHint == InGameUI::MAX_MOVE_HINTS)
  1853. m_nextMoveHint = 0;
  1854. }
  1855. //-------------------------------------------------------------------------------------------------
  1856. /** An attack command has occurred, start graphical "hint". */
  1857. //-------------------------------------------------------------------------------------------------
  1858. void InGameUI::createAttackHint( const GameMessage *msg )
  1859. {
  1860. }
  1861. //-------------------------------------------------------------------------------------------------
  1862. /** A force attack command has occurred, start graphical "hint". */
  1863. //-------------------------------------------------------------------------------------------------
  1864. void InGameUI::createForceAttackHint( const GameMessage *msg )
  1865. {
  1866. }
  1867. //-------------------------------------------------------------------------------------------------
  1868. /** An garrison command has occurred, start graphical "hint". */
  1869. //-------------------------------------------------------------------------------------------------
  1870. void InGameUI::createGarrisonHint( const GameMessage *msg )
  1871. {
  1872. Drawable *draw = TheGameClient->findDrawableByID( msg->getArgument(0)->drawableID );
  1873. if( draw )
  1874. {
  1875. draw->onSelected();
  1876. }
  1877. }
  1878. #if defined(_DEBUG) || defined(_INTERNAL)
  1879. #define AI_DEBUG_TOOLTIPS 1
  1880. #ifdef AI_DEBUG_TOOLTIPS
  1881. #include "Common/StateMachine.h"
  1882. #include "GameLogic/Module/AIUpdate.h"
  1883. #include "GameLogic/AIPathfind.h"
  1884. #endif // AI_DEBUG_TOOLTIPS
  1885. #endif // defined(_DEBUG) || defined(_INTERNAL)
  1886. //-------------------------------------------------------------------------------------------------
  1887. /** Details of what is mouse hovered over right now are in this message. Terrain might result
  1888. * in just a tooltip. An object might get a tooltip and show its hit points.
  1889. */
  1890. //-------------------------------------------------------------------------------------------------
  1891. void InGameUI::createMouseoverHint( const GameMessage *msg )
  1892. {
  1893. if (m_isScrolling || m_isSelecting)
  1894. return; // no mouseover for you
  1895. GameWindow *window = NULL;
  1896. const MouseIO *io = TheMouse->getMouseStatus();
  1897. Bool underWindow = false;
  1898. if (io && TheWindowManager)
  1899. window = TheWindowManager->getWindowUnderCursor(io->pos.x, io->pos.y);
  1900. while (window)
  1901. {
  1902. if (window->winGetInputFunc() == LeftHUDInput) {
  1903. underWindow = false;
  1904. break;
  1905. }
  1906. // check to see if it or any of its parents are opaque. If so, we can't select anything.
  1907. if (!BitTest( window->winGetStatus(), WIN_STATUS_SEE_THRU ))
  1908. {
  1909. underWindow = true;
  1910. break;
  1911. }
  1912. window = window->winGetParent();
  1913. }
  1914. if (underWindow)
  1915. {
  1916. setMouseCursor(Mouse::ARROW); // regardless of m_mouseMode
  1917. return;
  1918. }
  1919. DrawableID oldID = m_mousedOverDrawableID;
  1920. if (msg->getType() == GameMessage::MSG_MOUSEOVER_DRAWABLE_HINT)
  1921. {
  1922. TheMouse->setCursorTooltip(UnicodeString::TheEmptyString );
  1923. m_mousedOverDrawableID = INVALID_DRAWABLE_ID;
  1924. const Drawable *draw = TheGameClient->findDrawableByID(msg->getArgument(0)->drawableID);
  1925. const Object *obj = draw ? draw->getObject() : NULL;
  1926. if( obj )
  1927. {
  1928. //Ahh, here is a wierd exception: if the moused-over drawable is a mob-member
  1929. //(e.g. AngryMob), Lets fool the UI into creating the hint for the NEXUS instead...
  1930. if (obj->isKindOf( KINDOF_IGNORED_IN_GUI ))
  1931. {
  1932. static NameKeyType key_MobMemberSlavedUpdate = NAMEKEY( "MobMemberSlavedUpdate" );
  1933. MobMemberSlavedUpdate *MMSUpdate = (MobMemberSlavedUpdate*)obj->findUpdateModule( key_MobMemberSlavedUpdate );
  1934. if( MMSUpdate )
  1935. {
  1936. Object *slaver = TheGameLogic->findObjectByID(MMSUpdate->getSlaverID());
  1937. if ( slaver )
  1938. {
  1939. Drawable *slaverDraw = slaver->getDrawable();
  1940. if ( slaverDraw )
  1941. m_mousedOverDrawableID = slaverDraw->getID();
  1942. // if this fails, not to worry... it has already defaulted to INVALID_DRAWABLE_ID, above
  1943. }
  1944. }
  1945. }
  1946. else
  1947. m_mousedOverDrawableID = draw->getID();
  1948. #if defined(_DEBUG) || defined(_INTERNAL) //Extra hacky, sorry, but I need to use this in constantdebug report
  1949. if ( TheGlobalData->m_constantDebugUpdate == TRUE )
  1950. m_mousedOverDrawableID = draw->getID();
  1951. #endif
  1952. const Player* player = NULL;
  1953. const ThingTemplate *thingTemplate = obj->getTemplate();
  1954. ContainModuleInterface* contain = obj->getContain();
  1955. if( contain )
  1956. player = contain->getApparentControllingPlayer(ThePlayerList->getLocalPlayer());
  1957. if (player == NULL)
  1958. player = obj->getControllingPlayer();
  1959. Bool disguised = false;
  1960. if( obj->isKindOf( KINDOF_DISGUISER ) )
  1961. {
  1962. //Because we have support for disguised units pretending to be units from another
  1963. //team, we need to intercept it here and make sure it's rendered appropriately
  1964. //based on which client is rendering it.
  1965. StealthUpdate *update = obj->getStealth();
  1966. if( update )
  1967. {
  1968. if( update->isDisguised() )
  1969. {
  1970. Player *clientPlayer = ThePlayerList->getLocalPlayer();
  1971. Player *disguisedPlayer = ThePlayerList->getNthPlayer( update->getDisguisedPlayerIndex() );
  1972. if( player->getRelationship( clientPlayer->getDefaultTeam() ) != ALLIES && clientPlayer->isPlayerActive() )
  1973. {
  1974. //Neutrals and enemies will see this disguised unit as the team it's disguised as.
  1975. player = disguisedPlayer;
  1976. const ThingTemplate *disguisedTemplate = update->getDisguisedTemplate();
  1977. if( disguisedTemplate )
  1978. {
  1979. thingTemplate = disguisedTemplate;
  1980. disguised = true;
  1981. }
  1982. }
  1983. //Otherwise, the color will show up as the team it really belongs to (already set above).
  1984. }
  1985. }
  1986. }
  1987. UnicodeString str = thingTemplate->getDisplayName();
  1988. UnicodeString displayName = thingTemplate->getDisplayName();
  1989. if( str.isEmpty() )
  1990. {
  1991. AsciiString txtTemp;
  1992. txtTemp.format("ThingTemplate:%s", obj->getTemplate()->getName().str());
  1993. str = TheGameText->fetch(txtTemp);
  1994. //str.format(L"ThingTemplate:'%hs'", obj->getTemplate()->getName().str());
  1995. }
  1996. #ifdef AI_DEBUG_TOOLTIPS
  1997. if (TheGlobalData->m_debugAI) {
  1998. const Team *team = obj->getTeam();
  1999. AsciiString objName = obj->getName();
  2000. AsciiString teamName;
  2001. AsciiString stateName;
  2002. AIUpdateInterface *ai = (AIUpdateInterface*)obj->getAI();
  2003. if (ai) {
  2004. if (ai->getPath()) {
  2005. TheAI->pathfinder()->setDebugPath(ai->getPath());
  2006. }
  2007. #ifdef STATE_MACHINE_DEBUG
  2008. stateName = ai->getCurrentStateName();
  2009. if (ai->getAttackInfo()) {
  2010. stateName.concat(" AttackPriority=");
  2011. stateName.concat(ai->getAttackInfo()->getName());
  2012. }
  2013. #endif
  2014. }
  2015. if( team )
  2016. {
  2017. teamName = team->getName();
  2018. }
  2019. if (!objName.isEmpty())
  2020. {
  2021. if (!teamName.isEmpty())
  2022. {
  2023. str.format(L"%hs(%hs): %s", teamName.str(), objName.str(), str.str());
  2024. }
  2025. else
  2026. {
  2027. str.format(L"%hs: %s", objName.str(), str.str());
  2028. }
  2029. }
  2030. else
  2031. {
  2032. if (!teamName.isEmpty())
  2033. {
  2034. str.format(L"%hs: %s", teamName.str(), str.str());
  2035. }
  2036. }
  2037. str.format(L"%s - %hs", str.str(), stateName.str());
  2038. }
  2039. #endif
  2040. UnicodeString warehouseFeedback;
  2041. // Add on dollar amount of warehouse contents so people don't freak out until the art is hooked up
  2042. static const NameKeyType warehouseModuleKey = TheNameKeyGenerator->nameToKey( "SupplyWarehouseDockUpdate" );
  2043. SupplyWarehouseDockUpdate *warehouseModule = (SupplyWarehouseDockUpdate *)obj->findUpdateModule( warehouseModuleKey );
  2044. if( warehouseModule != NULL )
  2045. {
  2046. Int boxes = warehouseModule->getBoxesStored();
  2047. Int value = boxes * TheGlobalData->m_baseValuePerSupplyBox;
  2048. warehouseFeedback.format(TheGameText->fetch("TOOLTIP:SupplyWarehouse"), value);
  2049. str.concat(warehouseFeedback);
  2050. }
  2051. if (player)
  2052. {
  2053. UnicodeString tooltip;
  2054. //if (TheRecorder->isMultiplayer() && player->getPlayerType() == PLAYER_HUMAN)
  2055. if (TheRecorder->isMultiplayer() && player->isPlayableSide())
  2056. tooltip.format(L"%s\n%s", str.str(), ((Player *)player)->getPlayerDisplayName().str());
  2057. else
  2058. tooltip = str;
  2059. Int localPlayerIndex = ThePlayerList ? ThePlayerList->getLocalPlayer()->getPlayerIndex() : 0;
  2060. Int x, y;
  2061. ThePartitionManager->worldToCell(obj->getPosition()->x, obj->getPosition()->y, &x, &y);
  2062. if( ThePartitionManager->getShroudStatusForPlayer(localPlayerIndex, x, y) == CELLSHROUD_CLEAR )
  2063. {
  2064. RGBColor rgb;
  2065. if( disguised )
  2066. {
  2067. rgb.setFromInt( player->getPlayerColor() );
  2068. }
  2069. else
  2070. {
  2071. rgb.setFromInt(draw->getObject()->getIndicatorColor());
  2072. // Unless this is a stealth garrisoned building,
  2073. // Let's not use the contained's housecolor
  2074. const Object *obj = draw->getObject();
  2075. if ( obj )
  2076. {
  2077. ContainModuleInterface *contain = obj->getContain();
  2078. if ( contain && contain->isGarrisonable() )
  2079. {
  2080. const Player *play = contain->getApparentControllingPlayer( ThePlayerList->getLocalPlayer() );
  2081. if ( play )
  2082. rgb.setFromInt( play->getPlayerColor() );
  2083. }
  2084. }
  2085. }
  2086. //Object:Prop is a blank string... but we don't want to show
  2087. //any popup box at all if that is the case!
  2088. if( displayName.compare( TheGameText->fetch( "OBJECT:Prop" ) ) )
  2089. {
  2090. TheMouse->setCursorTooltip(tooltip, -1, &rgb );
  2091. }
  2092. }
  2093. }
  2094. }
  2095. }
  2096. else
  2097. {
  2098. m_mousedOverDrawableID = INVALID_DRAWABLE_ID;
  2099. }
  2100. if (oldID != m_mousedOverDrawableID)
  2101. {
  2102. //DEBUG_LOG(("Resetting tooltip delay\n"));
  2103. TheMouse->resetTooltipDelay();
  2104. }
  2105. if (m_mouseMode == MOUSEMODE_DEFAULT && !m_isScrolling && !m_isSelecting && !TheInGameUI->getSelectCount() && (TheRecorder->getMode() != RECORDERMODETYPE_PLAYBACK || TheLookAtTranslator->hasMouseMovedRecently()))
  2106. {
  2107. if( m_mousedOverDrawableID != INVALID_DRAWABLE_ID )
  2108. {
  2109. Drawable *draw = TheGameClient->findDrawableByID(m_mousedOverDrawableID);
  2110. //Add basic logic to determine if we can select a unit (or hint)
  2111. const Object *obj = draw ? draw->getObject() : NULL;
  2112. Bool drawSelectable = CanSelectDrawable(draw, FALSE);
  2113. if( !obj )
  2114. {
  2115. drawSelectable = false;
  2116. }
  2117. if( drawSelectable && obj->isLocallyControlled() )
  2118. {
  2119. setMouseCursor(Mouse::SELECTING);
  2120. }
  2121. else
  2122. {
  2123. setMouseCursor(Mouse::ARROW);
  2124. }
  2125. }
  2126. else
  2127. {
  2128. setMouseCursor(Mouse::ARROW);
  2129. }
  2130. }
  2131. else if (m_mouseMode != MOUSEMODE_DEFAULT && m_mouseMode != MOUSEMODE_BUILD_PLACE )
  2132. {
  2133. setMouseCursor((Mouse::MouseCursor)m_mouseModeCursor);
  2134. }
  2135. }
  2136. //-------------------------------------------------------------------------------------------------
  2137. /** A command would be given if a click were to happen, so give a preview hint of what it would be.
  2138. * Changing the mouse cursor is an example
  2139. */
  2140. void InGameUI::createCommandHint( const GameMessage *msg )
  2141. {
  2142. if (m_isScrolling || m_isSelecting || TheRecorder->getMode() == RECORDERMODETYPE_PLAYBACK)
  2143. return;
  2144. const Drawable *draw = TheGameClient->findDrawableByID(m_mousedOverDrawableID);
  2145. GameMessage::Type t = msg->getType();
  2146. //#ifdef DO_SHROUD_PROJECTION
  2147. if( draw && (t == GameMessage::MSG_DO_ATTACK_OBJECT_HINT || t == GameMessage::MSG_DO_ATTACK_OBJECT_AFTER_MOVING_HINT) )
  2148. {
  2149. const Object* obj = draw->getObject();
  2150. Int localPlayerIndex = ThePlayerList ? ThePlayerList->getLocalPlayer()->getPlayerIndex() : 0;
  2151. #if defined(_DEBUG) || defined(_INTERNAL)
  2152. ObjectShroudStatus ss = (!obj || !TheGlobalData->m_shroudOn) ? OBJECTSHROUD_CLEAR : obj->getShroudedStatus(localPlayerIndex);
  2153. #else
  2154. ObjectShroudStatus ss = (!obj) ? OBJECTSHROUD_CLEAR : obj->getShroudedStatus(localPlayerIndex);
  2155. #endif
  2156. if (ss == OBJECTSHROUD_SHROUDED)
  2157. {
  2158. t = GameMessage::MSG_DO_MOVETO_HINT; // if the object is hidden, switch to something innocuous
  2159. }
  2160. }
  2161. //#endif
  2162. setRadiusCursorNone();
  2163. if ( TheGlobalData->m_doubleClickAttackMove )
  2164. {
  2165. if ( --m_duringDoubleClickAttackMoveGuardHintTimer > 0 )
  2166. {
  2167. setMouseCursor(Mouse::FORCE_ATTACK_GROUND);
  2168. setRadiusCursor(RADIUSCURSOR_GUARD_AREA,
  2169. NULL,
  2170. PRIMARY_WEAPON);
  2171. return;
  2172. }
  2173. }
  2174. // set cursor to normal if there is a window under the cursor
  2175. GameWindow *window = NULL;
  2176. const MouseIO *io = TheMouse->getMouseStatus();
  2177. Bool underWindow = false;
  2178. if (io && TheWindowManager)
  2179. window = TheWindowManager->getWindowUnderCursor(io->pos.x, io->pos.y);
  2180. while (window)
  2181. {
  2182. if (window->winGetInputFunc() == LeftHUDInput) {
  2183. underWindow = false;
  2184. break;
  2185. }
  2186. // check to see if it or any of its parents are opaque. If so, we can't select anything.
  2187. if (!BitTest( window->winGetStatus(), WIN_STATUS_SEE_THRU ))
  2188. {
  2189. underWindow = true;
  2190. break;
  2191. }
  2192. window = window->winGetParent();
  2193. }
  2194. //Add basic logic to determine if we can select a unit (or hint)
  2195. const Object *obj = draw ? draw->getObject() : NULL;
  2196. Bool drawSelectable = CanSelectDrawable(draw, FALSE);
  2197. if( !obj )
  2198. {
  2199. drawSelectable = false;
  2200. }
  2201. // Note: These are only non-NULL if there is exactly one thing selected.
  2202. const Drawable *srcDraw = NULL;
  2203. const Object *srcObj = NULL;
  2204. if (getSelectCount() == 1) {
  2205. srcDraw = getAllSelectedDrawables()->front();
  2206. srcObj = (srcDraw ? srcDraw->getObject() : NULL);
  2207. }
  2208. switch (m_mouseMode)
  2209. {
  2210. case MOUSEMODE_DEFAULT:
  2211. {
  2212. // This section of code only gets called when there is no specific cursor mode happening.
  2213. if (underWindow || (srcObj && !srcObj->isLocallyControlled()))
  2214. {
  2215. setMouseCursor(Mouse::ARROW);
  2216. return;
  2217. }
  2218. switch (t)
  2219. {
  2220. case GameMessage::MSG_DO_MOVETO_HINT:
  2221. {
  2222. if( !drawSelectable && srcObj && srcObj->isLocallyControlled() && srcObj->isKindOf(KINDOF_STRUCTURE))
  2223. setMouseCursor( Mouse::GENERIC_INVALID );
  2224. else if( drawSelectable && obj->isLocallyControlled() && !obj->isKindOf(KINDOF_MINE))
  2225. setMouseCursor( Mouse::SELECTING );
  2226. else if( TheRadar->isRadarWindow( window ) &&
  2227. TheRadar->isRadarForced() == FALSE &&
  2228. (TheRadar->isRadarHidden() ||
  2229. ThePlayerList->getLocalPlayer()->hasRadar() == FALSE) )
  2230. setMouseCursor( Mouse::ARROW );
  2231. else
  2232. setMouseCursor( Mouse::MOVETO );
  2233. break;
  2234. }
  2235. case GameMessage::MSG_DO_ATTACKMOVETO_HINT:
  2236. if( drawSelectable && obj->isLocallyControlled() )
  2237. setMouseCursor( Mouse::SELECTING );
  2238. else
  2239. setMouseCursor( Mouse::ATTACKMOVETO );
  2240. break;
  2241. case GameMessage::MSG_ADD_WAYPOINT_HINT:
  2242. setMouseCursor( Mouse::WAYPOINT );
  2243. break;
  2244. case GameMessage::MSG_DO_ATTACK_OBJECT_HINT:
  2245. setMouseCursor( Mouse::ATTACK_OBJECT );
  2246. break;
  2247. case GameMessage::MSG_DO_ATTACK_OBJECT_AFTER_MOVING_HINT:
  2248. setMouseCursor( Mouse::OUTRANGE );
  2249. break;
  2250. case GameMessage::MSG_DO_FORCE_ATTACK_OBJECT_HINT:
  2251. setMouseCursor( Mouse::FORCE_ATTACK_OBJECT );
  2252. break;
  2253. case GameMessage::MSG_DO_FORCE_ATTACK_GROUND_HINT:
  2254. setMouseCursor( Mouse::FORCE_ATTACK_GROUND );
  2255. break;
  2256. case GameMessage::MSG_GET_REPAIRED_HINT:
  2257. setMouseCursor( Mouse::GET_REPAIRED );
  2258. break;
  2259. case GameMessage::MSG_DOCK_HINT:
  2260. setMouseCursor( Mouse::DOCK );
  2261. break;
  2262. case GameMessage::MSG_GET_HEALED_HINT:
  2263. setMouseCursor( Mouse::GET_HEALED );
  2264. break;
  2265. case GameMessage::MSG_DO_REPAIR_HINT:
  2266. setMouseCursor( Mouse::DO_REPAIR );
  2267. break;
  2268. case GameMessage::MSG_RESUME_CONSTRUCTION_HINT:
  2269. setMouseCursor( Mouse::RESUME_CONSTRUCTION );
  2270. break;
  2271. case GameMessage::MSG_ENTER_HINT:
  2272. setMouseCursor( Mouse::ENTER_FRIENDLY );
  2273. break;
  2274. case GameMessage::MSG_CONVERT_TO_CARBOMB_HINT:
  2275. case GameMessage::MSG_HIJACK_HINT:
  2276. case GameMessage::MSG_SABOTAGE_HINT:
  2277. setMouseCursor( Mouse::ENTER_AGGRESSIVELY );
  2278. break;
  2279. case GameMessage::MSG_DEFECTOR_HINT:
  2280. setMouseCursor( Mouse::DEFECTOR );
  2281. break;
  2282. #ifdef ALLOW_SURRENDER
  2283. case GameMessage::MSG_PICK_UP_PRISONER_HINT:
  2284. setMouseCursor( Mouse::PICK_UP_PRISONER );
  2285. break;
  2286. #endif
  2287. case GameMessage::MSG_CAPTUREBUILDING_HINT:
  2288. setMouseCursor( Mouse::CAPTUREBUILDING );
  2289. break;
  2290. case GameMessage::MSG_HACK_HINT:
  2291. setMouseCursor( Mouse::HACK );
  2292. break;
  2293. case GameMessage::MSG_IMPOSSIBLE_ATTACK_HINT:
  2294. setMouseCursor( Mouse::GENERIC_INVALID );
  2295. break;
  2296. case GameMessage::MSG_SET_RALLY_POINT_HINT:
  2297. if ( !drawSelectable )
  2298. setMouseCursor( Mouse::SET_RALLY_POINT );
  2299. else
  2300. setMouseCursor( Mouse::SELECTING );
  2301. break;
  2302. case GameMessage::MSG_DO_SPECIAL_POWER_OVERRIDE_DESTINATION_HINT:
  2303. setMouseCursor( Mouse::PARTICLE_UPLINK_CANNON );
  2304. break;
  2305. case GameMessage::MSG_DO_SALVAGE_HINT:
  2306. setMouseCursor( Mouse::MOVETO );
  2307. break;
  2308. case GameMessage::MSG_DO_INVALID_HINT:
  2309. setMouseCursor( Mouse::GENERIC_INVALID );
  2310. break;
  2311. }
  2312. }
  2313. break;
  2314. case MOUSEMODE_BUILD_PLACE:
  2315. {
  2316. if (underWindow)
  2317. {
  2318. setMouseCursor(Mouse::ARROW);
  2319. return;
  2320. }
  2321. switch (t)
  2322. {
  2323. case GameMessage::MSG_DO_MOVETO_HINT:
  2324. case GameMessage::MSG_DO_ATTACKMOVETO_HINT:
  2325. case GameMessage::MSG_ADD_WAYPOINT:
  2326. setMouseCursor(Mouse::BUILD_PLACEMENT);
  2327. break;
  2328. case GameMessage::MSG_DO_ATTACK_OBJECT_HINT:
  2329. case GameMessage::MSG_DO_ATTACK_OBJECT_AFTER_MOVING_HINT:
  2330. setMouseCursor(Mouse::INVALID_BUILD_PLACEMENT);
  2331. break;
  2332. }
  2333. }
  2334. break;
  2335. case MOUSEMODE_GUI_COMMAND:
  2336. {
  2337. if (underWindow)
  2338. {
  2339. setMouseCursor(Mouse::ARROW);
  2340. return;
  2341. }
  2342. // set the mouse cursor for commands that need a targeting or to normal with no command
  2343. if( m_pendingGUICommand )
  2344. {
  2345. if( m_pendingGUICommand->isContextCommand() ||
  2346. m_pendingGUICommand->getCommandType() == GUI_COMMAND_SPECIAL_POWER ||
  2347. m_pendingGUICommand->getCommandType() == GUI_COMMAND_SPECIAL_POWER_FROM_SHORTCUT )
  2348. {
  2349. //Here is the hook for when we are in a context sensitive command mode. We can
  2350. //either do the specified command mode command or nothing! Whether or not the
  2351. //command is valid or not was determined in evaluateContextCommand which is
  2352. //called first, and posts the appropriate message.
  2353. AsciiString cursorName; // empty by default
  2354. switch( t )
  2355. {
  2356. case GameMessage::MSG_VALID_GUICOMMAND_HINT:
  2357. cursorName = m_pendingGUICommand->getCursorName();
  2358. break;
  2359. case GameMessage::MSG_INVALID_GUICOMMAND_HINT:
  2360. default:
  2361. cursorName = m_pendingGUICommand->getInvalidCursorName();
  2362. break;
  2363. }
  2364. Int index = TheMouse->getCursorIndex(cursorName);
  2365. if( index != Mouse::INVALID_MOUSE_CURSOR )
  2366. {
  2367. setMouseCursor( (Mouse::MouseCursor)index );
  2368. }
  2369. else
  2370. {
  2371. setMouseCursor( Mouse::CROSS );
  2372. }
  2373. setRadiusCursor(m_pendingGUICommand->getRadiusCursorType(), //*****************************************************************
  2374. m_pendingGUICommand->getSpecialPowerTemplate(),
  2375. m_pendingGUICommand->getWeaponSlot());
  2376. }
  2377. else if( BitTest( m_pendingGUICommand->getOptions(), COMMAND_OPTION_NEED_TARGET ) )
  2378. {
  2379. Int index = TheMouse->getCursorIndex(m_pendingGUICommand->getCursorName());
  2380. if (index != Mouse::INVALID_MOUSE_CURSOR)
  2381. setMouseCursor( (Mouse::MouseCursor)index );
  2382. else
  2383. setMouseCursor( Mouse::CROSS );
  2384. setRadiusCursor(m_pendingGUICommand->getRadiusCursorType(), //*****************************************************************
  2385. m_pendingGUICommand->getSpecialPowerTemplate(),
  2386. m_pendingGUICommand->getWeaponSlot());
  2387. }
  2388. else
  2389. {
  2390. setRadiusCursorNone();
  2391. }
  2392. }
  2393. }
  2394. break;
  2395. }
  2396. }
  2397. //-------------------------------------------------------------------------------------------------
  2398. /// Get drawable ID under cursor
  2399. //-------------------------------------------------------------------------------------------------
  2400. DrawableID InGameUI::getMousedOverDrawableID( void ) const
  2401. {
  2402. return m_mousedOverDrawableID;
  2403. }
  2404. //-------------------------------------------------------------------------------------------------
  2405. /// set right-click scroll mode
  2406. //-------------------------------------------------------------------------------------------------
  2407. void InGameUI::setScrolling( Bool isScrolling )
  2408. {
  2409. if (m_isScrolling == isScrolling)
  2410. {
  2411. return;
  2412. }
  2413. if (isScrolling)
  2414. {
  2415. TheMouse->capture();
  2416. setMouseCursor( Mouse::SCROLL );
  2417. // break any camera locks
  2418. TheTacticalView->setCameraLock( INVALID_ID );
  2419. TheTacticalView->setCameraLockDrawable( NULL );
  2420. }
  2421. else
  2422. {
  2423. setMouseCursor( Mouse::ARROW );
  2424. TheMouse->releaseCapture();
  2425. }
  2426. m_isScrolling = isScrolling;
  2427. }
  2428. //-------------------------------------------------------------------------------------------------
  2429. /// are we scrolling?
  2430. //-------------------------------------------------------------------------------------------------
  2431. Bool InGameUI::isScrolling( void )
  2432. {
  2433. return m_isScrolling;
  2434. }
  2435. //-------------------------------------------------------------------------------------------------
  2436. /// set drag select mode
  2437. //-------------------------------------------------------------------------------------------------
  2438. void InGameUI::setSelecting( Bool isSelecting )
  2439. {
  2440. if (m_isSelecting == isSelecting)
  2441. {
  2442. return;
  2443. }
  2444. //setMouseCursor( Mouse::SELECTING );
  2445. m_isSelecting = isSelecting;
  2446. }
  2447. //-------------------------------------------------------------------------------------------------
  2448. /// are we selecting?
  2449. //-------------------------------------------------------------------------------------------------
  2450. Bool InGameUI::isSelecting( void )
  2451. {
  2452. return m_isSelecting;
  2453. }
  2454. //-------------------------------------------------------------------------------------------------
  2455. /// get scroll amount
  2456. //-------------------------------------------------------------------------------------------------
  2457. void InGameUI::setScrollAmount( Coord2D amt )
  2458. {
  2459. m_scrollAmt = amt;
  2460. }
  2461. //-------------------------------------------------------------------------------------------------
  2462. /// get scroll amount
  2463. //-------------------------------------------------------------------------------------------------
  2464. Coord2D InGameUI::getScrollAmount( void )
  2465. {
  2466. return m_scrollAmt;
  2467. }
  2468. //-------------------------------------------------------------------------------------------------
  2469. /** Like the building "placement" mode, clicking on some buttons in the UI require us to
  2470. * provide additional data by clicking on a target object/location in the world. This
  2471. * is where we enable that "mode" so that we can get the additional data needed for a
  2472. * command from the user */
  2473. //-------------------------------------------------------------------------------------------------
  2474. void InGameUI::setGUICommand( const CommandButton *command )
  2475. {
  2476. if (TheRecorder->getMode() == RECORDERMODETYPE_PLAYBACK)
  2477. return;
  2478. // sanity
  2479. if( command )
  2480. {
  2481. if( BitTest( command->getOptions(), COMMAND_OPTION_NEED_TARGET ) == FALSE )
  2482. {
  2483. DEBUG_ASSERTCRASH( 0, ("setGUICommand: Command '%s' does not need additional user interaction\n",
  2484. command->getName().str()) );
  2485. m_pendingGUICommand = NULL;
  2486. m_mouseMode = MOUSEMODE_DEFAULT;
  2487. return;
  2488. } // end if
  2489. m_mouseMode = MOUSEMODE_GUI_COMMAND;
  2490. } // end if
  2491. else
  2492. {
  2493. m_mouseMode = MOUSEMODE_DEFAULT;
  2494. }
  2495. // set the command
  2496. m_pendingGUICommand = command;
  2497. // set the mouse cursor for commands that need a targeting or to normal with no command
  2498. if( command && BitTest( command->getOptions(), COMMAND_OPTION_NEED_TARGET ) && !command->isContextCommand() )
  2499. {
  2500. setMouseCursor( Mouse::ARROW );// This occurs on the mouse-up of a panel button, so make an arrow
  2501. // the mouseoverhint code will take care of the cursor context, once the mouse leaves the panel
  2502. // but we will set the radius cursor here, so you can see it bleeding out from beneath the panel
  2503. setRadiusCursor(command->getRadiusCursorType(), //*****************************************************************
  2504. command->getSpecialPowerTemplate(),
  2505. command->getWeaponSlot());
  2506. }
  2507. else
  2508. {
  2509. if (TheMouse)
  2510. {
  2511. setMouseCursor( Mouse::ARROW );
  2512. }
  2513. setRadiusCursorNone();
  2514. }
  2515. m_mouseModeCursor = TheMouse->getMouseCursor();
  2516. } // end setGUICommand
  2517. //-------------------------------------------------------------------------------------------------
  2518. /** Get the pending gui command */
  2519. //-------------------------------------------------------------------------------------------------
  2520. const CommandButton *InGameUI::getGUICommand( void ) const
  2521. {
  2522. return m_pendingGUICommand;
  2523. }
  2524. //-------------------------------------------------------------------------------------------------
  2525. /** Destroy any drawables we have in our placement icon array and set to NULL */
  2526. //-------------------------------------------------------------------------------------------------
  2527. void InGameUI::destroyPlacementIcons( void )
  2528. {
  2529. Int i;
  2530. for( i = 0; i < TheGlobalData->m_maxLineBuildObjects; ++i )
  2531. {
  2532. if( m_placeIcon[ i ] )
  2533. {
  2534. TheTerrainVisual->removeFactionBibDrawable(m_placeIcon[ i ]);
  2535. TheGameClient->destroyDrawable( m_placeIcon[ i ] );
  2536. }
  2537. m_placeIcon[ i ] = NULL;
  2538. } // end for i
  2539. TheTerrainVisual->removeAllBibs();
  2540. } // end destroyPlacementIcons
  2541. //-------------------------------------------------------------------------------------------------
  2542. /** User has clicked on a built item that requires placement in the world. We will
  2543. * record what that thing is so that the we can catch the next click in the world
  2544. * and try to place the object there */
  2545. //-------------------------------------------------------------------------------------------------
  2546. void InGameUI::placeBuildAvailable( const ThingTemplate *build, Drawable *buildDrawable )
  2547. {
  2548. if (build != NULL)
  2549. {
  2550. // if building something, no radius cursor, thankew
  2551. setRadiusCursorNone();
  2552. }
  2553. //
  2554. // if we're setting another place available, but we're somehow already in the placement
  2555. // mode, get out of it before we start a new one
  2556. //
  2557. if( m_pendingPlaceType != NULL && build != NULL )
  2558. placeBuildAvailable( NULL, NULL );
  2559. //
  2560. // keep a record of what we are trying to place, if we are already trying to
  2561. // place something, it is overwritten
  2562. //
  2563. m_pendingPlaceType = build;
  2564. //Keep the prev pending place for left click deselection prevention in alternate mouse mode.
  2565. //We want to keep our dozer selected after initiating construction.
  2566. setPreventLeftClickDeselectionInAlternateMouseModeForOneClick( m_pendingPlaceSourceObjectID != INVALID_ID );
  2567. m_pendingPlaceSourceObjectID = INVALID_ID;
  2568. Object *sourceObject = NULL;
  2569. if( buildDrawable )
  2570. sourceObject = buildDrawable->getObject();
  2571. if( sourceObject )
  2572. m_pendingPlaceSourceObjectID = sourceObject->getID();
  2573. //
  2574. // hack, change our cursor to at least something different ... also note that it's
  2575. // possible to not have the mouse yet, as some UI systems as part of initialization
  2576. // make sure that there isn't anything valid for to "place build"
  2577. //
  2578. if( TheMouse )
  2579. {
  2580. if( build )
  2581. {
  2582. m_mouseMode = MOUSEMODE_BUILD_PLACE;
  2583. m_mouseModeCursor = Mouse::CROSS;
  2584. Drawable *draw;
  2585. // capture the mouse for our window, windows is lame and changes it if we don't
  2586. TheMouse->capture();
  2587. // hack for changing cursor
  2588. setMouseCursor( Mouse::CROSS );
  2589. // deselect all drawables, otherwise they move to the place we click
  2590. ///@ todo when message stream order more formalized eliminate this
  2591. // TheInGameUI->deselectAllDrawables();
  2592. // create a drawble of what we are building to be "attached" at the cursor
  2593. draw = TheThingFactory->newDrawable( build, DRAWABLE_STATUS_NO_STATE_PARTICLES );
  2594. if (sourceObject)
  2595. {
  2596. if (TheGlobalData->m_timeOfDay == TIME_OF_DAY_NIGHT)
  2597. draw->setIndicatorColor(sourceObject->getControllingPlayer()->getPlayerNightColor());
  2598. else
  2599. draw->setIndicatorColor(sourceObject->getControllingPlayer()->getPlayerColor());
  2600. }
  2601. DEBUG_ASSERTCRASH( draw, ("Unable to create icon at cursor for placement '%s'\n",
  2602. build->getName().str()) );
  2603. //
  2604. // set the initial angle of the free floating building to the property from INI
  2605. // we have this so we can have the "cool" face the user until they click and
  2606. // pick an actual direction for placement
  2607. //
  2608. Real angle = build->getPlacementViewAngle();
  2609. // don't forget to take into account the current view angle
  2610. // angle += TheTacticalView->getAngle(); Don't do this - makes odd angled building placements. jba.
  2611. // set the angle in the icon we just created
  2612. draw->setOrientation( angle );
  2613. // set the build icon attached to the cursor to be "see-thru"
  2614. draw->setDrawableOpacity( placementOpacity );
  2615. // set the "icon" in the icon array at the first index
  2616. DEBUG_ASSERTCRASH( m_placeIcon[ 0 ] == NULL, ("placeBuildAvailable, build icon array is not empty!") );
  2617. m_placeIcon[ 0 ] = draw;
  2618. } // end if
  2619. else
  2620. {
  2621. if (m_mouseMode == MOUSEMODE_BUILD_PLACE)
  2622. {
  2623. m_mouseMode = MOUSEMODE_DEFAULT;
  2624. m_mouseModeCursor = Mouse::ARROW;
  2625. }
  2626. TheMouse->releaseCapture();
  2627. setMouseCursor( Mouse::ARROW );
  2628. setPlacementStart( NULL );
  2629. // if we have a place icons destroy them
  2630. destroyPlacementIcons();
  2631. if( sourceObject )
  2632. {
  2633. ProductionUpdateInterface *puInterface = sourceObject->getProductionUpdateInterface();
  2634. if( puInterface )
  2635. {
  2636. //Clear the special power mode for construction if we set it. Actually call it everytime
  2637. //rather than checking if it's set before clearing (cheaper).
  2638. puInterface->setSpecialPowerConstructionCommandButton( NULL );
  2639. }
  2640. }
  2641. } // end else
  2642. } // end if
  2643. } // end placeBuildAvailable
  2644. //-------------------------------------------------------------------------------------------------
  2645. /** Return the thing we're attempting to place */
  2646. //-------------------------------------------------------------------------------------------------
  2647. const ThingTemplate *InGameUI::getPendingPlaceType( void )
  2648. {
  2649. return m_pendingPlaceType;
  2650. }
  2651. //-------------------------------------------------------------------------------------------------
  2652. //-------------------------------------------------------------------------------------------------
  2653. const ObjectID InGameUI::getPendingPlaceSourceObjectID( void )
  2654. {
  2655. return m_pendingPlaceSourceObjectID;
  2656. } // end getPendingPlaceSourceObjectID
  2657. //-------------------------------------------------------------------------------------------------
  2658. /** Start the angle selection interface for selecting building angles when placing them */
  2659. //-------------------------------------------------------------------------------------------------
  2660. void InGameUI::setPlacementStart( const ICoord2D *start )
  2661. {
  2662. // if we have a start point we turn "on" the interface, otherwise we turn it "off"
  2663. if( start )
  2664. {
  2665. m_placeAnchorStart = *start;
  2666. m_placeAnchorEnd = *start;
  2667. m_placeAnchorInProgress = TRUE;
  2668. } // end if
  2669. else
  2670. m_placeAnchorInProgress = FALSE;
  2671. } // end setPlacementStart
  2672. //-------------------------------------------------------------------------------------------------
  2673. /** Set the end anchor for the angle build interface */
  2674. //-------------------------------------------------------------------------------------------------
  2675. void InGameUI::setPlacementEnd( const ICoord2D *end )
  2676. {
  2677. if( end )
  2678. m_placeAnchorEnd = *end;
  2679. } // end setPlacementEnd
  2680. //-------------------------------------------------------------------------------------------------
  2681. /** Is the angle selection interface for placing building at angles up? */
  2682. //-------------------------------------------------------------------------------------------------
  2683. Bool InGameUI::isPlacementAnchored( void )
  2684. {
  2685. return m_placeAnchorInProgress;
  2686. } // end isPlacementAnchored
  2687. //-------------------------------------------------------------------------------------------------
  2688. /** Get the start and end anchor points for the building angle selection interface */
  2689. //-------------------------------------------------------------------------------------------------
  2690. void InGameUI::getPlacementPoints( ICoord2D *start, ICoord2D *end )
  2691. {
  2692. if( start )
  2693. *start = m_placeAnchorStart;
  2694. if( end )
  2695. *end = m_placeAnchorEnd;
  2696. } // end getPlacementPoints
  2697. //-------------------------------------------------------------------------------------------------
  2698. /** Return the angle of the drawable at the cursor if any */
  2699. //-------------------------------------------------------------------------------------------------
  2700. Real InGameUI::getPlacementAngle( void )
  2701. {
  2702. if( m_placeIcon[ 0 ] )
  2703. return m_placeIcon[ 0 ]->getOrientation();
  2704. return 0.0f;
  2705. } // end getPlacementAngle
  2706. //-------------------------------------------------------------------------------------------------
  2707. /** Mark given Drawable as "selected". */
  2708. //-------------------------------------------------------------------------------------------------
  2709. void InGameUI::selectDrawable( Drawable *draw )
  2710. {
  2711. if( draw->isSelected() == FALSE )
  2712. {
  2713. m_frameSelectionChanged = TheGameLogic->getFrame();
  2714. // set the selection in the drawable
  2715. draw->friend_setSelected();
  2716. // add to our selected list
  2717. m_selectedDrawables.push_front( draw );
  2718. // we now have one more selected drawable
  2719. incrementSelectCount();
  2720. // evaluate whether our selection consists of exactly one angry mob
  2721. evaluateSoloNexus( draw );
  2722. // the control needs to update its context sensitive display now
  2723. TheControlBar->onDrawableSelected( draw );
  2724. } // end if
  2725. } // end selectDrawable
  2726. //-------------------------------------------------------------------------------------------------
  2727. /** Clear "selected" status of Drawable. */
  2728. //-------------------------------------------------------------------------------------------------
  2729. void InGameUI::deselectDrawable( Drawable *draw )
  2730. {
  2731. if( draw->isSelected() )
  2732. {
  2733. m_frameSelectionChanged = TheGameLogic->getFrame();
  2734. // clear the selected bit out of the drawable
  2735. draw->friend_clearSelected();
  2736. // find the drawable entry in our list
  2737. DrawableListIt findIt = std::find( m_selectedDrawables.begin(),
  2738. m_selectedDrawables.end(),
  2739. draw );
  2740. // sanity
  2741. DEBUG_ASSERTCRASH( findIt != m_selectedDrawables.end(),
  2742. ("deselectDrawable: Drawable not found in the selected drawable list '%s'\n",
  2743. draw->getTemplate()->getName().str()) );
  2744. // remove it from the selected drawable list
  2745. m_selectedDrawables.erase( findIt );
  2746. // keep out own internal count happy
  2747. decrementSelectCount();
  2748. // evaluate whether our selection consists of exactly one angry mob
  2749. evaluateSoloNexus();
  2750. // the control needs to update its context sensitive display now
  2751. TheControlBar->onDrawableDeselected( draw );
  2752. } // end if
  2753. } // end deselectDrawable
  2754. //-------------------------------------------------------------------------------------------------
  2755. /** Clear all drawables' "select" status */
  2756. //-------------------------------------------------------------------------------------------------
  2757. void InGameUI::deselectAllDrawables( Bool postMsg )
  2758. {
  2759. const DrawableList *selected = TheInGameUI->getAllSelectedDrawables();
  2760. // loop through all the selected drawables
  2761. for ( DrawableListCIt it = selected->begin(); it != selected->end(); )
  2762. {
  2763. // get drawable and increment iterator, we will invalidate it as we deselect
  2764. Drawable* draw = *it++;
  2765. // do the deselection
  2766. TheInGameUI->deselectDrawable( draw );
  2767. } // end while
  2768. // keep our list all tidy
  2769. m_selectedDrawables.clear();
  2770. // our selection can no longer consist of exactly one angry mob
  2771. m_soloNexusSelectedDrawableID = INVALID_DRAWABLE_ID;
  2772. ///@todo don't we want to not emit this message if there wasn't a group at all? (CBD)
  2773. /** @todo also, we probably are sending this message too much, we should come up with
  2774. some kind of "selections are dirty" status that we can check once per frame and send
  2775. the correct group info over the network ... could be tricky tho (or impossible) given
  2776. the order of operations of things happening in the code (CBD) */
  2777. if( postMsg )
  2778. {
  2779. GameMessage *groupMsg = TheMessageStream->appendMessage( GameMessage::MSG_DESTROY_SELECTED_GROUP );
  2780. //True deletes entire group.
  2781. groupMsg->appendBooleanArgument( true );
  2782. }
  2783. }
  2784. //-------------------------------------------------------------------------------------------------
  2785. /** Return the list of all the currently selected Drawable pointers. */
  2786. //-------------------------------------------------------------------------------------------------
  2787. const DrawableList *InGameUI::getAllSelectedDrawables( void ) const
  2788. {
  2789. return &m_selectedDrawables;
  2790. }
  2791. //-------------------------------------------------------------------------------------------------
  2792. /** Return the list of all the currently selected Drawable pointers. */
  2793. //-------------------------------------------------------------------------------------------------
  2794. const DrawableList *InGameUI::getAllSelectedLocalDrawables( void )
  2795. {
  2796. m_selectedLocalDrawables.clear();
  2797. for (DrawableList::const_iterator it = m_selectedDrawables.begin(); it != m_selectedDrawables.end(); ++it)
  2798. {
  2799. Drawable *draw = (*it);
  2800. if (draw && draw->getObject() && draw->getObject()->isLocallyControlled())
  2801. m_selectedLocalDrawables.push_back( draw );
  2802. }
  2803. return &m_selectedLocalDrawables;
  2804. }
  2805. //-------------------------------------------------------------------------------------------------
  2806. /** Return poiner to the first selected drawable, if any */
  2807. //-------------------------------------------------------------------------------------------------
  2808. Drawable *InGameUI::getFirstSelectedDrawable( void )
  2809. {
  2810. // sanity
  2811. if( m_selectedDrawables.empty() )
  2812. return NULL; // this is valid, nothing is selected
  2813. return m_selectedDrawables.front();
  2814. } // end getFirstSelectedDrawable
  2815. //-------------------------------------------------------------------------------------------------
  2816. /** Return true if the selected ID is in the drawable list */
  2817. //-------------------------------------------------------------------------------------------------
  2818. Bool InGameUI::isDrawableSelected( DrawableID idToCheck ) const
  2819. {
  2820. for( DrawableListCIt it = m_selectedDrawables.begin(); it != m_selectedDrawables.end(); ++it )
  2821. {
  2822. if( (*it)->getID() == idToCheck )
  2823. return TRUE;
  2824. } // end for
  2825. return FALSE;
  2826. } // end isDrawableSelected
  2827. // ------------------------------------------------------------------------------------------------
  2828. // ------------------------------------------------------------------------------------------------
  2829. Bool InGameUI::isAnySelectedKindOf( KindOfType kindOf ) const
  2830. {
  2831. Drawable *draw;
  2832. for( DrawableListCIt it = m_selectedDrawables.begin();
  2833. it != m_selectedDrawables.end();
  2834. ++it )
  2835. {
  2836. /** @todo, it seems like we might want to keep a list of drawable pointers so we
  2837. don't have to do this lookup ... it seems "tightly coupled" to me (CBD) */
  2838. // get the drawable from the ID
  2839. draw = *it;
  2840. if( draw && draw->isKindOf( kindOf ) )
  2841. return TRUE;
  2842. } // end for, it
  2843. return FALSE; // no selected objects are of the kind of type
  2844. } // end isAnySelectedKindOf
  2845. // ------------------------------------------------------------------------------------------------
  2846. // ------------------------------------------------------------------------------------------------
  2847. Bool InGameUI::isAllSelectedKindOf( KindOfType kindOf ) const
  2848. {
  2849. Drawable *draw;
  2850. for( DrawableListCIt it = m_selectedDrawables.begin();
  2851. it != m_selectedDrawables.end();
  2852. ++it )
  2853. {
  2854. /** @todo, it seems like we might want to keep a list of drawable pointers so we
  2855. don't have to do this lookup ... it seems "tightly coupled" to me (CBD) */
  2856. // get the drawable from the ID
  2857. draw = *it;
  2858. if( draw && draw->isKindOf( kindOf ) == FALSE )
  2859. return FALSE; // not all objects are of the kind of type
  2860. } // end for, it
  2861. return TRUE; // all objects have this kindof bit set in them
  2862. } // end isAllSelectedKindOf
  2863. //-------------------------------------------------------------------------------------------------
  2864. /** Set the input enabled/disabled */
  2865. //-------------------------------------------------------------------------------------------------
  2866. void InGameUI::setInputEnabled( Bool enable )
  2867. {
  2868. if(!enable)
  2869. setSelecting( FALSE );
  2870. Bool wasEnabled = m_inputEnabled;
  2871. m_inputEnabled = enable;
  2872. if (wasEnabled && !enable)
  2873. {
  2874. /*
  2875. when input is disabled, clear out all the special "modes" we can be in, since we can miss
  2876. the "exit mode" message during the cinematic. e.g., hold down the ctrl key when a cinematic
  2877. begins, then release it during the cinematic... since input is disabled, we never see the keyup
  2878. and thus think we're still in forceattack when its done, until you jiggle that key again.
  2879. (admittedly, this code will actually do the wrong thing if you were to hold down the ctrl
  2880. key thru the whole cinematic, but that's even more unlikely...)
  2881. */
  2882. setForceAttackMode( false ); // CTRL
  2883. setForceMoveMode( false ); // apparently unmapped in current CommandMap.ini
  2884. setWaypointMode( false ); // ALT
  2885. setPreferSelectionMode( false ); // SHIFT
  2886. setCameraRotateLeft( false ); // KP4
  2887. setCameraRotateRight( false ); // KP6
  2888. setCameraZoomIn( false ); // KP8
  2889. setCameraZoomOut( false ); // KP2
  2890. }
  2891. }
  2892. //-------------------------------------------------------------------------------------------------
  2893. /** Drawable is being destroyed, clean up any UI elements associated with it. */
  2894. //-------------------------------------------------------------------------------------------------
  2895. void InGameUI::disregardDrawable( Drawable *draw )
  2896. {
  2897. // make sure drawable is no longer selected
  2898. deselectDrawable( draw );
  2899. }
  2900. //-------------------------------------------------------------------------------------------------
  2901. /** This is called after the UI has been drawn. */
  2902. //-------------------------------------------------------------------------------------------------
  2903. void InGameUI::postDraw( void )
  2904. {
  2905. // render our display strings for the messages if on
  2906. if( m_messagesOn )
  2907. {
  2908. Int i, x, y;
  2909. Color dropColor;
  2910. UnsignedByte r, g, b, a;
  2911. x = m_messagePosition.x;
  2912. y = m_messagePosition.y;
  2913. for( i = MAX_UI_MESSAGES - 1; i >= 0; i-- )
  2914. {
  2915. if( m_uiMessages[ i ].displayString )
  2916. {
  2917. // make drop color black, but use the alpha setting of the fill color specified (for fading)
  2918. GameGetColorComponents( m_uiMessages[ i ].color, &r, &g, &b, &a );
  2919. dropColor = GameMakeColor( 0, 0, 0, a );
  2920. // draw the text
  2921. m_uiMessages[ i ].displayString->draw( x, y, m_uiMessages[ i ].color, dropColor );
  2922. // increment text spot to next location
  2923. GameFont *font = m_uiMessages[ i ].displayString->getFont();
  2924. y += font->height;
  2925. } //end if
  2926. } // end for i
  2927. } // end if
  2928. if( m_militarySubtitle )
  2929. {
  2930. ICoord2D pos;
  2931. pos.x = m_militarySubtitle->position.x;
  2932. pos.y = m_militarySubtitle->position.y;
  2933. Color dropColor;
  2934. UnsignedByte r, g, b, a;
  2935. GameGetColorComponents( m_militarySubtitle->color, &r, &g, &b, &a );
  2936. dropColor = GameMakeColor( 0, 0, 0, a );
  2937. for(Int i = 0; i <= m_militarySubtitle->currentDisplayString; i++)
  2938. {
  2939. m_militarySubtitle->displayStrings[i]->draw(pos.x,pos.y, m_militarySubtitle->color,dropColor );
  2940. Int height;
  2941. m_militarySubtitle->displayStrings[i]->getSize(NULL, &height);
  2942. pos.y += height;
  2943. }
  2944. if( m_militarySubtitle->blockDrawn )
  2945. {
  2946. ICoord2D size;
  2947. size.y = m_militarySubtitle->displayStrings[m_militarySubtitle->currentDisplayString]->getFont()->height;
  2948. size.x = size.y * 0.8f;
  2949. TheDisplay->drawFillRect(m_militarySubtitle->blockPos.x, m_militarySubtitle->blockPos.y, size.x, size.y, m_militarySubtitle->color);
  2950. }
  2951. }
  2952. // draw superweapon timers
  2953. // Also responsible for Eva saying "Superweapon is ready for launch"
  2954. // IMPORTANT: Don't bail out of this block early just because you don't
  2955. // want to display the timers -- Eva still needs to be checked
  2956. if (TheGameLogic->getFrame() > 0 )
  2957. {
  2958. // Int superweaponCount = 0;
  2959. Int startX = (Int)(m_superweaponPosition.x * TheDisplay->getWidth());
  2960. Int startY = (Int)(m_superweaponPosition.y * TheDisplay->getHeight());
  2961. Int bottomMargin = (Int)( (Real)TheTacticalView->getHeight() * 0.82f );
  2962. Bool marginExceeded = FALSE;
  2963. for (Int i=0; i<MAX_PLAYER_COUNT; ++i)
  2964. {
  2965. Color bgColor = GameMakeColor( 0, 0, 0, 255 );
  2966. for (SuperweaponMap::iterator mapIt = m_superweapons[i].begin(); mapIt != m_superweapons[i].end(); ++mapIt)
  2967. {
  2968. AsciiString templateName = mapIt->first;
  2969. for (SuperweaponList::iterator listIt = mapIt->second.begin(); listIt != mapIt->second.end(); ++listIt)
  2970. {
  2971. SuperweaponInfo *info = *listIt;
  2972. DEBUG_ASSERTCRASH(info, ("No superweapon info!"));
  2973. if (info && !info->m_hiddenByScript && !info->m_hiddenByScience)
  2974. {
  2975. //enforce bottom margin of tactical view
  2976. if ( startY >= bottomMargin)
  2977. {
  2978. UnicodeString ellipsis;
  2979. ellipsis.format(L"...");
  2980. info->setText( ellipsis, ellipsis );
  2981. info->setFont( m_superweaponReadyFont, m_superweaponNormalPointSize, m_superweaponNormalBold );
  2982. info->drawTime( startX, startY, m_superweaponFlashColor, bgColor );
  2983. marginExceeded = TRUE;
  2984. }
  2985. Object * owningObject = TheGameLogic->findObjectByID(info->m_id);
  2986. if (owningObject)
  2987. {
  2988. // We don't draw our timers until we are finished with construction.
  2989. // It is important that let the SpecialPowerUpdate is add its timer in its contructor,,
  2990. // since the science for it could be added before construction is finished,
  2991. // And thus the timer set to READY before the timer is first drawn, here
  2992. if ( owningObject->testStatus( OBJECT_STATUS_UNDER_CONSTRUCTION ))
  2993. continue;
  2994. SpecialPowerModuleInterface *module = owningObject->getSpecialPowerModule(info->getSpecialPowerTemplate());
  2995. if (module)
  2996. {
  2997. // found one - draw it
  2998. Bool isReady = module->isReady();
  2999. Int readySecs;
  3000. // IsReady includes disabledness, so if you have a 0 timer disabled super, you don't want
  3001. // the UnsignedInt to wrap around to hundreds of millions of seconds.
  3002. if( module->getReadyFrame() < TheGameLogic->getFrame() )
  3003. readySecs = 0;
  3004. else
  3005. readySecs = (module->getReadyFrame() - TheGameLogic->getFrame()) / LOGICFRAMES_PER_SECOND;
  3006. // Yes, integer math. We can't have float imprecision display 4:01 on a disabled superweapon.
  3007. // Only if we actually changed the ready status do we want to play an Eva event.
  3008. if ( isReady && !info->m_evaReadyPlayed )
  3009. {
  3010. if ( TheGameLogic->getFrame() > 0 )
  3011. {
  3012. SpecialPowerType type = module->getSpecialPowerTemplate()->getSpecialPowerType();
  3013. Player *localPlayer = ThePlayerList->getLocalPlayer();
  3014. if( type == SPECIAL_PARTICLE_UPLINK_CANNON || type == SUPW_SPECIAL_PARTICLE_UPLINK_CANNON || type == LAZR_SPECIAL_PARTICLE_UPLINK_CANNON )
  3015. {
  3016. if ( localPlayer == owningObject->getControllingPlayer() )
  3017. {
  3018. TheEva->setShouldPlay(EVA_SuperweaponReady_Own_ParticleCannon);
  3019. }
  3020. else if ( localPlayer->getRelationship(owningObject->getTeam()) != ENEMIES )
  3021. {
  3022. // Note: counting relationship NEUTRAL as ally. Not sure if this makes a difference???
  3023. TheEva->setShouldPlay(EVA_SuperweaponReady_Ally_ParticleCannon);
  3024. }
  3025. else
  3026. {
  3027. TheEva->setShouldPlay(EVA_SuperweaponReady_Enemy_ParticleCannon);
  3028. }
  3029. }
  3030. else if( type == SPECIAL_NEUTRON_MISSILE || type == NUKE_SPECIAL_NEUTRON_MISSILE || type == SUPW_SPECIAL_NEUTRON_MISSILE )
  3031. {
  3032. if ( localPlayer == owningObject->getControllingPlayer() )
  3033. {
  3034. TheEva->setShouldPlay(EVA_SuperweaponReady_Own_Nuke);
  3035. }
  3036. else if ( localPlayer->getRelationship(owningObject->getTeam()) != ENEMIES )
  3037. {
  3038. // Note: counting relationship NEUTRAL as ally. Not sure if this makes a difference???
  3039. TheEva->setShouldPlay(EVA_SuperweaponReady_Ally_Nuke);
  3040. }
  3041. else
  3042. {
  3043. TheEva->setShouldPlay(EVA_SuperweaponReady_Enemy_Nuke);
  3044. }
  3045. }
  3046. else if (type == SPECIAL_SCUD_STORM)
  3047. {
  3048. if ( localPlayer == owningObject->getControllingPlayer() )
  3049. {
  3050. TheEva->setShouldPlay(EVA_SuperweaponReady_Own_ScudStorm);
  3051. }
  3052. else if ( localPlayer->getRelationship(owningObject->getTeam()) != ENEMIES )
  3053. {
  3054. // Note: counting relationship NEUTRAL as ally. Not sure if this makes a difference???
  3055. TheEva->setShouldPlay(EVA_SuperweaponReady_Ally_ScudStorm);
  3056. }
  3057. else
  3058. {
  3059. TheEva->setShouldPlay(EVA_SuperweaponReady_Enemy_ScudStorm);
  3060. }
  3061. }
  3062. }
  3063. info->m_evaReadyPlayed = true;
  3064. }
  3065. else
  3066. {
  3067. if ( !isReady )
  3068. info->m_evaReadyPlayed = false; // Reset Eva for next time
  3069. }
  3070. // draw the text
  3071. if ( !m_superweaponHiddenByScript && !marginExceeded )
  3072. {
  3073. // Similarly, only checking timers is not truly indicitive of readyness.
  3074. Bool changeBolding = (readySecs != info->m_timestamp) || (isReady != info->m_ready) || info->m_forceUpdateText;
  3075. if (changeBolding)
  3076. {
  3077. if (isReady)
  3078. {
  3079. // go bold - we're good to go
  3080. info->setFont( m_superweaponReadyFont, m_superweaponReadyPointSize, m_superweaponReadyBold );
  3081. }
  3082. else
  3083. {
  3084. // if we were at 0, we've just fired - kill the bold
  3085. if (info->m_timestamp == 0)
  3086. {
  3087. info->setFont( m_superweaponNormalFont, m_superweaponNormalPointSize, m_superweaponNormalBold );
  3088. }
  3089. }
  3090. info->m_forceUpdateText = false;
  3091. info->m_ready = isReady;
  3092. info->m_timestamp = readySecs;
  3093. Int min = readySecs/60;
  3094. Int sec = readySecs - min*60;
  3095. AsciiString strIndex;
  3096. strIndex.format("GUI:%s", templateName.str());
  3097. UnicodeString name, time;
  3098. name.format(L"%ls: ", TheGameText->fetch(strIndex.str()).str());
  3099. time.format(L"%d:%2.2d", min, sec);
  3100. info->setText(name, time);
  3101. }
  3102. if (isReady)
  3103. {
  3104. if ( m_superweaponFlashDuration != 0.0f )
  3105. {
  3106. if ( TheGameLogic->getFrame() >= m_superweaponLastFlashFrame + (Int)(m_superweaponFlashDuration) )
  3107. {
  3108. m_superweaponUsedFlashColor = !m_superweaponUsedFlashColor;
  3109. m_superweaponLastFlashFrame = TheGameLogic->getFrame();
  3110. }
  3111. info->drawName( startX,
  3112. startY, (m_superweaponUsedFlashColor)?0:m_superweaponFlashColor, bgColor );
  3113. info->drawTime( startX,
  3114. startY, (m_superweaponUsedFlashColor)?0:m_superweaponFlashColor, bgColor );
  3115. }
  3116. else
  3117. {
  3118. info->drawName( startX, startY, 0, bgColor );
  3119. info->drawTime( startX, startY, 0, bgColor );
  3120. }
  3121. }
  3122. else
  3123. {
  3124. info->drawName( startX, startY, 0, bgColor );
  3125. info->drawTime( startX, startY, 0, bgColor );
  3126. }
  3127. // increment text spot to next location
  3128. startY += info->getHeight();
  3129. }
  3130. if (info->getSpecialPowerTemplate()->isSharedNSync())
  3131. break; // Wow, it is almost too easy!
  3132. // This prevents redundant timers for shared powers/superweapons
  3133. // No matter how many specialpowermodules register their timers with me,
  3134. // I will only draw the timer of the first valid one in my list,
  3135. // since they all have the same template, ans they all
  3136. // use the Player::getReadyFrame() functions to stay in sync.
  3137. }
  3138. }
  3139. }
  3140. }
  3141. }
  3142. }
  3143. }
  3144. // draw named timers
  3145. if (TheGameLogic->getFrame() > 0 && m_showNamedTimers)
  3146. {
  3147. // Int namedTimerCount = 0;
  3148. Bool reverseXDir = (m_namedTimerPosition.x >= 0.5f);
  3149. Int startX = (Int)(m_namedTimerPosition.x * TheDisplay->getWidth());
  3150. Int startY = (Int)(m_namedTimerPosition.y * TheDisplay->getHeight());
  3151. Color bgColor = GameMakeColor( 0, 0, 0, 255 );
  3152. for (NamedTimerMapIt mapIt = m_namedTimers.begin(); mapIt != m_namedTimers.end(); ++mapIt)
  3153. {
  3154. AsciiString timerName = mapIt->first;
  3155. NamedTimerInfo *info = mapIt->second;
  3156. DEBUG_ASSERTCRASH(info, ("No namedTimer info!"));
  3157. if (info)
  3158. {
  3159. // found one - draw it
  3160. UnicodeString line;
  3161. Int framesLeft = TheScriptEngine->getCounter(timerName)->value;
  3162. UnsignedInt readyFrame = TheGameLogic->getFrame();
  3163. if (framesLeft > 0)
  3164. readyFrame += framesLeft;
  3165. Int readySecs = (Int)(SECONDS_PER_LOGICFRAME_REAL * (readyFrame - TheGameLogic->getFrame()));
  3166. if ( (info->isCountdown && readySecs != info->timestamp) || (!info->isCountdown && framesLeft != info->timestamp) )
  3167. {
  3168. if (!readySecs && info->isCountdown)
  3169. {
  3170. // go bold - we're good to go
  3171. info->displayString->setFont( TheFontLibrary->getFont( m_namedTimerReadyFont,
  3172. TheGlobalLanguageData->adjustFontSize(m_namedTimerReadyPointSize), m_namedTimerReadyBold ) );
  3173. }
  3174. else
  3175. {
  3176. // if we were at 0, we've just fired - kill the bold
  3177. if (info->timestamp == 0 || info->isCountdown)
  3178. {
  3179. info->displayString->setFont( TheFontLibrary->getFont( m_namedTimerNormalFont,
  3180. TheGlobalLanguageData->adjustFontSize(m_namedTimerNormalPointSize), m_namedTimerNormalBold ) );
  3181. }
  3182. }
  3183. info->timestamp = readySecs;
  3184. Int min = readySecs/60;
  3185. Int sec = readySecs - min*60;
  3186. if (!info->isCountdown)
  3187. line.format(L"%s %d", info->timerText.str(), framesLeft);
  3188. else
  3189. {
  3190. if (sec >= 10)
  3191. line.format(L"%s %d:%d", info->timerText.str(), min, sec);
  3192. else
  3193. line.format(L"%s %d:0%d", info->timerText.str(), min, sec);
  3194. }
  3195. info->displayString->setText(line);
  3196. }
  3197. // draw the text
  3198. Int drawX = startX;
  3199. if (reverseXDir)
  3200. drawX -= info->displayString->getWidth();
  3201. if (!readySecs && info->isCountdown)
  3202. {
  3203. if ( m_namedTimerFlashDuration != 0.0f )
  3204. {
  3205. if ( TheGameLogic->getFrame() >= m_namedTimerLastFlashFrame + (Int)(m_namedTimerFlashDuration) )
  3206. {
  3207. m_namedTimerUsedFlashColor = !m_namedTimerUsedFlashColor;
  3208. m_namedTimerLastFlashFrame = TheGameLogic->getFrame();
  3209. }
  3210. info->displayString->draw( drawX, startY, (m_namedTimerUsedFlashColor)?info->color:m_namedTimerFlashColor, bgColor );
  3211. }
  3212. else
  3213. {
  3214. info->displayString->draw( drawX, startY, info->color, bgColor );
  3215. }
  3216. }
  3217. else
  3218. {
  3219. info->displayString->draw( drawX, startY, info->color, bgColor );
  3220. }
  3221. // increment text spot to next location
  3222. startY -= info->displayString->getFont()->height;
  3223. }
  3224. }
  3225. }
  3226. // draw RMB scroll anchor
  3227. if (TheLookAtTranslator && m_drawRMBScrollAnchor)
  3228. {
  3229. const ICoord2D* anchor = TheLookAtTranslator->getRMBScrollAnchor();
  3230. if (anchor)
  3231. {
  3232. static const Int w = 2;
  3233. static const Int h = 2;
  3234. static const Int r = 4; // ratio
  3235. static const Color mainColor = GameMakeColor(0, 255, 0, 255);
  3236. static const Color dropColor = GameMakeColor(0, 0, 0, 255);
  3237. TheDisplay->drawFillRect( anchor->x-w*r-1, anchor->y-h-1, w*2*r+3, h*2+3, dropColor );
  3238. TheDisplay->drawFillRect( anchor->x-w-1, anchor->y-h*r-1, w*2+3, h*2*r+3, dropColor );
  3239. TheDisplay->drawFillRect( anchor->x-w*r, anchor->y-h, w*2*r+1, h*2+1, mainColor );
  3240. TheDisplay->drawFillRect( anchor->x-w, anchor->y-h*r, w*2+1, h*2*r+1, mainColor );
  3241. }
  3242. }
  3243. //draw superweapon ready multipliers
  3244. TheControlBar->drawSpecialPowerShortcutMultiplierText();
  3245. } // end postDraw
  3246. //-------------------------------------------------------------------------------------------------
  3247. /** Expire a hint of the specified type with the corresponding hint index */
  3248. //-------------------------------------------------------------------------------------------------
  3249. void InGameUI::expireHint( HintType type, UnsignedInt hintIndex )
  3250. {
  3251. if( type == MOVE_HINT )
  3252. {
  3253. // sanity
  3254. if( hintIndex < 0 || hintIndex >= MAX_MOVE_HINTS )
  3255. return;
  3256. m_moveHint[ hintIndex ].sourceID = 0;
  3257. m_moveHint[ hintIndex ].frame = 0;
  3258. } // end if
  3259. else
  3260. {
  3261. // undefined hint type
  3262. DEBUG_CRASH(("undefined hint type"));
  3263. return;
  3264. } // end else
  3265. } // end expireHint
  3266. //-------------------------------------------------------------------------------------------------
  3267. /** Create the control user interface GUI */
  3268. //-------------------------------------------------------------------------------------------------
  3269. void InGameUI::createControlBar( void )
  3270. {
  3271. TheWindowManager->winCreateFromScript( AsciiString("ControlBar.wnd") );
  3272. HideControlBar();
  3273. /*
  3274. // hide all windows created from this layout
  3275. GameWindow *window = TheWindowManager->winGetWindowList();
  3276. for( ; window; window = window->winGetPrev() )
  3277. window->winHide( TRUE );
  3278. */
  3279. } // end createControlBar
  3280. //-------------------------------------------------------------------------------------------------
  3281. /** Create the replay control GUI */
  3282. //-------------------------------------------------------------------------------------------------
  3283. void InGameUI::createReplayControl( void )
  3284. {
  3285. m_replayWindow = TheWindowManager->winCreateFromScript( AsciiString("ReplayControl.wnd") );
  3286. /*
  3287. // hide all windows created from this layout
  3288. GameWindow *window = TheWindowManager->winGetWindowList();
  3289. for( ; window; window = window->winGetPrev() )
  3290. window->winHide( TRUE );
  3291. */
  3292. } // end createReplayControl
  3293. // ------------------------------------------------------------------------------------------------
  3294. // InGameUI::playMovie
  3295. // ------------------------------------------------------------------------------------------------
  3296. void InGameUI::playMovie( const AsciiString& movieName )
  3297. {
  3298. stopMovie();
  3299. m_videoStream = TheVideoPlayer->open( movieName );
  3300. if ( m_videoStream == NULL )
  3301. {
  3302. return;
  3303. }
  3304. m_currentlyPlayingMovie = movieName;
  3305. m_videoBuffer = TheDisplay->createVideoBuffer();
  3306. if ( m_videoBuffer == NULL ||
  3307. !m_videoBuffer->allocate( m_videoStream->width(),
  3308. m_videoStream->height())
  3309. )
  3310. {
  3311. stopMovie();
  3312. return;
  3313. }
  3314. }
  3315. // ------------------------------------------------------------------------------------------------
  3316. // ------------------------------------------------------------------------------------------------
  3317. void InGameUI::stopMovie( void )
  3318. {
  3319. delete m_videoBuffer;
  3320. m_videoBuffer = NULL;
  3321. if ( m_videoStream )
  3322. {
  3323. m_videoStream->close();
  3324. m_videoStream = NULL;
  3325. }
  3326. if (!m_currentlyPlayingMovie.isEmpty()) {
  3327. //TheScriptEngine->notifyOfCompletedVideo(m_currentlyPlayingMovie); // removing sync error source -MDC
  3328. m_currentlyPlayingMovie = AsciiString::TheEmptyString;
  3329. }
  3330. }
  3331. // ------------------------------------------------------------------------------------------------
  3332. // InGameUI::videoBuffer
  3333. // ------------------------------------------------------------------------------------------------
  3334. VideoBuffer* InGameUI::videoBuffer( void )
  3335. {
  3336. return m_videoBuffer;
  3337. }
  3338. // ------------------------------------------------------------------------------------------------
  3339. // InGameUI::playMovie
  3340. // ------------------------------------------------------------------------------------------------
  3341. void InGameUI::playCameoMovie( const AsciiString& movieName )
  3342. {
  3343. stopCameoMovie();
  3344. m_cameoVideoStream = TheVideoPlayer->open( movieName );
  3345. if ( m_cameoVideoStream == NULL )
  3346. {
  3347. return;
  3348. }
  3349. m_cameoVideoBuffer = TheDisplay->createVideoBuffer();
  3350. if ( m_cameoVideoBuffer == NULL ||
  3351. !m_cameoVideoBuffer->allocate( m_cameoVideoStream->width(),
  3352. m_cameoVideoStream->height())
  3353. )
  3354. {
  3355. stopCameoMovie();
  3356. return;
  3357. }
  3358. GameWindow *window = TheWindowManager->winGetWindowFromId(NULL,TheNameKeyGenerator->nameToKey( AsciiString("ControlBar.wnd:RightHUD") ));
  3359. WinInstanceData *winData = window->winGetInstanceData();
  3360. winData->setVideoBuffer(m_cameoVideoBuffer);
  3361. // window->winHide(FALSE);
  3362. }
  3363. // ------------------------------------------------------------------------------------------------
  3364. // ------------------------------------------------------------------------------------------------
  3365. void InGameUI::stopCameoMovie( void )
  3366. {
  3367. //RightHUD
  3368. //GameWindow *window = TheWindowManager->winGetWindowFromId(NULL,TheNameKeyGenerator->nameToKey( AsciiString("ControlBar.wnd:CameoMovieWindow") ));
  3369. GameWindow *window = TheWindowManager->winGetWindowFromId(NULL,TheNameKeyGenerator->nameToKey( AsciiString("ControlBar.wnd:RightHUD") ));
  3370. // window->winHide(FALSE);
  3371. WinInstanceData *winData = window->winGetInstanceData();
  3372. winData->setVideoBuffer(NULL);
  3373. delete m_cameoVideoBuffer;
  3374. m_cameoVideoBuffer = NULL;
  3375. if ( m_cameoVideoStream )
  3376. {
  3377. m_cameoVideoStream->close();
  3378. m_cameoVideoStream = NULL;
  3379. }
  3380. }
  3381. // ------------------------------------------------------------------------------------------------
  3382. // InGameUI::videoBuffer
  3383. // ------------------------------------------------------------------------------------------------
  3384. VideoBuffer* InGameUI::cameoVideoBuffer( void )
  3385. {
  3386. return m_cameoVideoBuffer;
  3387. }
  3388. //-------------------------------------------------------------------------------------------------
  3389. //-------------------------------------------------------------------------------------------------
  3390. void InGameUI::displayCantBuildMessage( LegalBuildCode lbc )
  3391. {
  3392. switch( lbc )
  3393. {
  3394. //---------------------------------------------------------------------------------------------
  3395. case LBC_RESTRICTED_TERRAIN:
  3396. TheInGameUI->message( "GUI:CantBuildRestrictedTerrain" );
  3397. break;
  3398. //---------------------------------------------------------------------------------------------
  3399. case LBC_NOT_FLAT_ENOUGH:
  3400. TheInGameUI->message( "GUI:CantBuildNotFlatEnough" );
  3401. break;
  3402. //---------------------------------------------------------------------------------------------
  3403. case LBC_OBJECTS_IN_THE_WAY:
  3404. TheInGameUI->message( "GUI:CantBuildObjectsInTheWay" );
  3405. break;
  3406. //---------------------------------------------------------------------------------------------
  3407. case LBC_TOO_CLOSE_TO_SUPPLIES:
  3408. TheInGameUI->message( "GUI:CantBuildTooCloseToSupplies" );
  3409. break;
  3410. //---------------------------------------------------------------------------------------------
  3411. case LBC_NO_CLEAR_PATH:
  3412. TheInGameUI->message( "GUI:CantBuildNoClearPath" );
  3413. break;
  3414. //---------------------------------------------------------------------------------------------
  3415. case LBC_SHROUD:
  3416. TheInGameUI->message( "GUI:CantBuildShroud" );
  3417. break;
  3418. //---------------------------------------------------------------------------------------------
  3419. case LBC_GENERIC_FAILURE:
  3420. default:
  3421. TheInGameUI->message( "GUI:CantBuildThere" );
  3422. break;
  3423. } // end switch
  3424. } // end displayCantBuildMessage
  3425. // ------------------------------------------------------------------------------------------------
  3426. // InGameUI::militarySubtitle
  3427. // ------------------------------------------------------------------------------------------------
  3428. void InGameUI::militarySubtitle( const AsciiString& label, Int duration )
  3429. {
  3430. // make sure we don't already have a subtitle up there
  3431. removeMilitarySubtitle();
  3432. // update our history
  3433. UpdateDiplomacyBriefingText(label, FALSE);
  3434. UnicodeString title = TheGameText->fetch(label);
  3435. // make sure we actually will be displaying something
  3436. if( title.isEmpty() || duration <= 0)
  3437. {
  3438. DEBUG_CRASH(("Trying to create a military subtitle but either title is empty (%ls) or duration is <= 0 (%d)",title.str(), duration));
  3439. return;
  3440. }
  3441. // we need some frame info to set our timings
  3442. UnsignedInt currLogicFrame = TheGameLogic->getFrame();
  3443. const int messageTimeout = currLogicFrame + (Int)(((Real)LOGICFRAMES_PER_SECOND * duration)/1000.0f);
  3444. // disable tooltips until this frame, cause we don't want to collide with the military subtitles.
  3445. TheInGameUI->disableTooltipsUntil(messageTimeout);
  3446. // calculate where this screen position should be since the position being passed in is based off 8x6
  3447. Coord2D multiplier;
  3448. multiplier.x = (float)TheDisplay->getWidth() / 800.0f;
  3449. multiplier.y = (float)TheDisplay->getHeight() / 600.0f;
  3450. // lets bring out the data structure!
  3451. m_militarySubtitle = NEW MilitarySubtitleData;
  3452. m_militarySubtitle->subtitle.set(title);
  3453. m_militarySubtitle->blockDrawn = TRUE;
  3454. m_militarySubtitle->blockBeginFrame = currLogicFrame;
  3455. m_militarySubtitle->lifetime = messageTimeout;
  3456. m_militarySubtitle->blockPos.x = m_militarySubtitle->position.x = m_militaryCaptionPosition.x * multiplier.x;
  3457. m_militarySubtitle->blockPos.y = m_militarySubtitle->position.y = m_militaryCaptionPosition.y * multiplier.y;
  3458. m_militarySubtitle->incrementOnFrame = currLogicFrame + (Int)(((Real)LOGICFRAMES_PER_SECOND * TheGlobalLanguageData->m_militaryCaptionDelayMS)/1000.0f);
  3459. m_militarySubtitle->index = 0;
  3460. for (int i = 1; i < MAX_SUBTITLE_LINES; i ++)
  3461. m_militarySubtitle->displayStrings[i] = NULL;
  3462. m_militarySubtitle->currentDisplayString = 0;
  3463. m_militarySubtitle->displayStrings[0] = TheDisplayStringManager->newDisplayString();
  3464. m_militarySubtitle->displayStrings[0]->reset();
  3465. m_militarySubtitle->displayStrings[0]->setFont( TheFontLibrary->getFont( m_militaryCaptionTitleFont,
  3466. TheGlobalLanguageData->adjustFontSize(m_militaryCaptionTitlePointSize), m_militaryCaptionTitleBold ) );
  3467. m_militarySubtitle->color = GameMakeColor(m_militaryCaptionColor.red, m_militaryCaptionColor.green, m_militaryCaptionColor.blue, m_militaryCaptionColor.alpha);
  3468. }
  3469. // ------------------------------------------------------------------------------------------------
  3470. // InGameUI::removeMilitarySubtitle
  3471. // ------------------------------------------------------------------------------------------------
  3472. void InGameUI::removeMilitarySubtitle( void )
  3473. {
  3474. // sanity (is there really such a thing in this world?)
  3475. if(!m_militarySubtitle)
  3476. return;
  3477. TheInGameUI->clearTooltipsDisabled();
  3478. // loop through and free up the display strings
  3479. for(Int i = 0; i <= m_militarySubtitle->currentDisplayString; i ++)
  3480. {
  3481. TheDisplayStringManager->freeDisplayString(m_militarySubtitle->displayStrings[i]);
  3482. m_militarySubtitle->displayStrings[i] = NULL;
  3483. }
  3484. //delete it man!
  3485. delete m_militarySubtitle;
  3486. m_militarySubtitle= NULL;
  3487. }
  3488. // ------------------------------------------------------------------------------------------------
  3489. // ------------------------------------------------------------------------------------------------
  3490. Bool InGameUI::areSelectedObjectsControllable() const
  3491. {
  3492. const DrawableList *selected = TheInGameUI->getAllSelectedDrawables();
  3493. // loop through all the selected drawables
  3494. const Drawable *draw;
  3495. for( DrawableListCIt it = selected->begin(); it != selected->end(); ++it )
  3496. {
  3497. // get this drawable
  3498. draw = *it;
  3499. // All selected objects will have the same local controller, so
  3500. // simply return the first one.
  3501. return draw->getObject()->isLocallyControlled();
  3502. }
  3503. // Nothing selected...
  3504. return FALSE;
  3505. }
  3506. //------------------------------------------------------------------------------
  3507. //Resets the camera to default zoom and orientation.
  3508. //------------------------------------------------------------------------------
  3509. void InGameUI::resetCamera()
  3510. {
  3511. ViewLocation currentView;
  3512. TheTacticalView->getLocation( &currentView );
  3513. TheTacticalView->resetCamera( &currentView.getPosition(), 1, 0.0f, 0.0f );
  3514. }
  3515. //------------------------------------------------------------------------------
  3516. //Checks to see if an object can interact with an object in a non-hostile manner. This is currently used by the selection
  3517. //translator to determine whether to do something to an object or select it instead based on the context of what is currently
  3518. //selected.
  3519. //------------------------------------------------------------------------------
  3520. Bool InGameUI::canSelectedObjectsNonAttackInteractWithObject( const Object *objectToInteractWith, SelectionRules rule ) const
  3521. {
  3522. for( int i = 1; i < NUM_ACTIONTYPES; i++ )
  3523. {
  3524. if( i != ACTIONTYPE_ATTACK_OBJECT )
  3525. {
  3526. if( canSelectedObjectsDoAction( (ActionType)i, objectToInteractWith, rule ) )
  3527. {
  3528. return TRUE;
  3529. }
  3530. }
  3531. }
  3532. return FALSE;
  3533. }
  3534. CanAttackResult InGameUI::getCanSelectedObjectsAttack( ActionType action, const Object *objectToInteractWith, SelectionRules rule, Bool additionalChecking ) const
  3535. {
  3536. //Kris: Aug 16, 2003
  3537. //John McDonald added this code back in Oct 09, 2002.
  3538. //Replaced it with palatable code.
  3539. //if( (objectToInteractWith == NULL) != (action == ACTIONTYPE_SET_RALLY_POINT)) <---BAD CODE
  3540. if( !objectToInteractWith && action != ACTIONTYPE_SET_RALLY_POINT || //No object to interact with (and not rally point mode)
  3541. objectToInteractWith && action == ACTIONTYPE_SET_RALLY_POINT ) //Object to interact with (and rally point mode)
  3542. {
  3543. //Sanity check OR can't set a rally point over an object.
  3544. return ATTACKRESULT_NOT_POSSIBLE;
  3545. }
  3546. // get selected list of drawables
  3547. const DrawableList *selected = TheInGameUI->getAllSelectedDrawables();
  3548. // set up counters for rule checking
  3549. Int count = 0;
  3550. CanAttackResult bestResult = ATTACKRESULT_NOT_POSSIBLE;
  3551. CanAttackResult worstResult = ATTACKRESULT_POSSIBLE;
  3552. // loop through all the selected drawables
  3553. Drawable *other;
  3554. for( DrawableListCIt it = selected->begin(); it != selected->end(); ++it )
  3555. {
  3556. // get this drawable
  3557. other = *it;
  3558. count++;
  3559. switch( action )
  3560. {
  3561. case ACTIONTYPE_ATTACK_OBJECT:
  3562. {
  3563. //additionalChecking is TRUE only if force attack mode is on.
  3564. CanAttackResult result = TheActionManager->getCanAttackObject( other->getObject(), objectToInteractWith, CMD_FROM_PLAYER,
  3565. additionalChecking ? ATTACK_NEW_TARGET_FORCED : ATTACK_NEW_TARGET );
  3566. if( result > bestResult )
  3567. {
  3568. //Best result is used for the rule: SELECTION_ANY
  3569. bestResult = result;
  3570. }
  3571. if( result < worstResult )
  3572. {
  3573. //Worst result is used for the rule: SELECTION_ALL
  3574. worstResult = result;
  3575. }
  3576. break;
  3577. }
  3578. case ACTIONTYPE_NONE:
  3579. case ACTIONTYPE_GET_REPAIRED_AT:
  3580. case ACTIONTYPE_DOCK_AT:
  3581. case ACTIONTYPE_GET_HEALED_AT:
  3582. case ACTIONTYPE_REPAIR_OBJECT:
  3583. case ACTIONTYPE_RESUME_CONSTRUCTION:
  3584. case ACTIONTYPE_COMBATDROP_INTO:
  3585. case ACTIONTYPE_ENTER_OBJECT:
  3586. case ACTIONTYPE_HIJACK_VEHICLE:
  3587. case ACTIONTYPE_SABOTAGE_BUILDING:
  3588. case ACTIONTYPE_CONVERT_OBJECT_TO_CARBOMB:
  3589. case ACTIONTYPE_CAPTURE_BUILDING:
  3590. case ACTIONTYPE_DISABLE_VEHICLE_VIA_HACKING:
  3591. #ifdef ALLOW_SURRENDER
  3592. case ACTIONTYPE_PICK_UP_PRISONER:
  3593. #endif
  3594. case ACTIONTYPE_STEAL_CASH_VIA_HACKING:
  3595. case ACTIONTYPE_DISABLE_BUILDING_VIA_HACKING:
  3596. case ACTIONTYPE_MAKE_DEFECTOR:
  3597. case ACTIONTYPE_SET_RALLY_POINT:
  3598. default:
  3599. DEBUG_CRASH( ("Called InGameUI::getCanSelectedObjectsAttack() with actiontype %d. Only accepts attack types! Should you be calling InGameUI::canSelectedObjectsDoAction() instead?") );
  3600. return ATTACKRESULT_INVALID_SHOT;
  3601. }
  3602. } // end for
  3603. if( count > 0 )
  3604. {
  3605. if( rule == SELECTION_ANY )
  3606. {
  3607. return bestResult;
  3608. }
  3609. return worstResult;
  3610. }
  3611. // no can do!
  3612. return ATTACKRESULT_NOT_POSSIBLE;
  3613. }
  3614. //------------------------------------------------------------------------------
  3615. //Wrapper function that checks a specific action.
  3616. //------------------------------------------------------------------------------
  3617. Bool InGameUI::canSelectedObjectsDoAction( ActionType action, const Object *objectToInteractWith, SelectionRules rule, Bool additionalChecking ) const
  3618. {
  3619. //Kris: Aug 16, 2003
  3620. //John McDonald added this code back in Oct 09, 2002. This code is SO wrong that it should
  3621. //be a firing offense. Strangely enough, this code has gone unnoticed for nearly a year
  3622. //and nearly two projects. I'm fixing this now by moving it to the rally point code...
  3623. //because it would be nice if a saboteur could actually sabotage a building via a
  3624. //commandbutton.
  3625. //if( (objectToInteractWith == NULL) != (action == ACTIONTYPE_SET_RALLY_POINT))
  3626. if( !objectToInteractWith && action != ACTIONTYPE_SET_RALLY_POINT || //No object to interact with (and not rally point mode)
  3627. objectToInteractWith && action == ACTIONTYPE_SET_RALLY_POINT ) //Object to interact with (and rally point mode)
  3628. {
  3629. //Sanity check OR can't set a rally point over an object.
  3630. return FALSE;
  3631. }
  3632. // get selected list of drawables
  3633. const DrawableList *selected = TheInGameUI->getAllSelectedDrawables();
  3634. // set up counters for rule checking
  3635. Int count = 0;
  3636. Int qualify = 0;
  3637. // loop through all the selected drawables
  3638. Drawable *other;
  3639. for( DrawableListCIt it = selected->begin(); it != selected->end(); ++it )
  3640. {
  3641. // get this drawable
  3642. other = *it;
  3643. count++;
  3644. Bool success = FALSE;
  3645. switch( action )
  3646. {
  3647. case ACTIONTYPE_NONE:
  3648. //However strange this might be, it is always possible to do "nothing"
  3649. //although I can't think of why this would be needed...
  3650. return TRUE;
  3651. case ACTIONTYPE_GET_REPAIRED_AT:
  3652. success = TheActionManager->canGetRepairedAt( other->getObject(), objectToInteractWith, CMD_FROM_PLAYER );
  3653. break;
  3654. case ACTIONTYPE_DOCK_AT:
  3655. success = TheActionManager->canDockAt( other->getObject(), objectToInteractWith, CMD_FROM_PLAYER );
  3656. break;
  3657. case ACTIONTYPE_GET_HEALED_AT:
  3658. success = TheActionManager->canGetHealedAt( other->getObject(), objectToInteractWith, CMD_FROM_PLAYER );
  3659. if( success )
  3660. {
  3661. ContainModuleInterface *contain = objectToInteractWith->getContain();
  3662. if( contain && contain->isHealContain() )
  3663. {
  3664. //This container is only used for the purposes of healing and we cannot
  3665. //enter it normally -- this is NOT a transport!
  3666. success = false;
  3667. }
  3668. }
  3669. break;
  3670. case ACTIONTYPE_REPAIR_OBJECT:
  3671. {
  3672. ObjectID currentRepairer = objectToInteractWith->getSoleHealingBenefactor();
  3673. success = ( TheActionManager->canRepairObject( other->getObject(), objectToInteractWith, CMD_FROM_PLAYER )
  3674. && ( currentRepairer == INVALID_ID || currentRepairer == other->getObject()->getID() ) );
  3675. // unless someone else is already healing it...
  3676. // please note that this add'l test is left out of canRepairObject() since canRepairObject
  3677. // gets called from within the Dozer/WorkerAIUpdates' stateMachines as they continue the repair process.
  3678. // This remains true.
  3679. break;
  3680. }
  3681. case ACTIONTYPE_RESUME_CONSTRUCTION:
  3682. success = TheActionManager->canResumeConstructionOf( other->getObject(), objectToInteractWith, CMD_FROM_PLAYER );
  3683. break;
  3684. case ACTIONTYPE_COMBATDROP_INTO:
  3685. success = TheActionManager->canEnterObject( other->getObject(), objectToInteractWith, CMD_FROM_PLAYER, COMBATDROP_INTO );
  3686. break;
  3687. case ACTIONTYPE_ENTER_OBJECT:
  3688. //additionalChecking is TRUE only if we want to check if transport is full first.
  3689. success = TheActionManager->canEnterObject( other->getObject(), objectToInteractWith, CMD_FROM_PLAYER, additionalChecking ? CHECK_CAPACITY : DONT_CHECK_CAPACITY );
  3690. break;
  3691. case ACTIONTYPE_ATTACK_OBJECT:
  3692. DEBUG_CRASH( ("Called InGameUI::canSelectedObjectsDoAction() with ACTIONTYPE_ATTACK_OBJECT. You must use InGameUI::getCanSelectedObjectsAttack() instead.") );
  3693. return FALSE;
  3694. case ACTIONTYPE_HIJACK_VEHICLE:
  3695. success = TheActionManager->canHijackVehicle( other->getObject(), objectToInteractWith, CMD_FROM_PLAYER );
  3696. break;
  3697. case ACTIONTYPE_SABOTAGE_BUILDING:
  3698. success = TheActionManager->canSabotageBuilding( other->getObject(), objectToInteractWith, CMD_FROM_PLAYER );
  3699. break;
  3700. case ACTIONTYPE_CONVERT_OBJECT_TO_CARBOMB:
  3701. success = TheActionManager->canConvertObjectToCarBomb( other->getObject(), objectToInteractWith, CMD_FROM_PLAYER );
  3702. break;
  3703. case ACTIONTYPE_CAPTURE_BUILDING:
  3704. success = TheActionManager->canCaptureBuilding( other->getObject(), objectToInteractWith, CMD_FROM_PLAYER );
  3705. break;
  3706. case ACTIONTYPE_DISABLE_VEHICLE_VIA_HACKING:
  3707. success = TheActionManager->canDisableVehicleViaHacking( other->getObject(), objectToInteractWith, CMD_FROM_PLAYER );
  3708. break;
  3709. #ifdef ALLOW_SURRENDER
  3710. case ACTIONTYPE_PICK_UP_PRISONER:
  3711. success = TheActionManager->canPickUpPrisoner( other->getObject(), objectToInteractWith, CMD_FROM_PLAYER );
  3712. break;
  3713. #endif
  3714. case ACTIONTYPE_STEAL_CASH_VIA_HACKING:
  3715. success = TheActionManager->canStealCashViaHacking( other->getObject(), objectToInteractWith, CMD_FROM_PLAYER );
  3716. break;
  3717. case ACTIONTYPE_DISABLE_BUILDING_VIA_HACKING:
  3718. success = TheActionManager->canDisableBuildingViaHacking( other->getObject(), objectToInteractWith, CMD_FROM_PLAYER );
  3719. break;
  3720. case ACTIONTYPE_MAKE_DEFECTOR:
  3721. success = TheActionManager->canMakeObjectDefector( other->getObject(), objectToInteractWith, CMD_FROM_PLAYER );
  3722. break;
  3723. case ACTIONTYPE_SET_RALLY_POINT:
  3724. {
  3725. Object *obj = other->getObject();
  3726. if (!obj) {
  3727. success = false;
  3728. break;
  3729. }
  3730. success = (obj->isKindOf(KINDOF_AUTO_RALLYPOINT) && obj->isLocallyControlled());
  3731. break;
  3732. }
  3733. }
  3734. if( success )
  3735. {
  3736. if( rule == SELECTION_ANY )
  3737. {
  3738. return TRUE;
  3739. }
  3740. ++qualify;
  3741. }
  3742. } // end for
  3743. //If the rule is all must qualify, do the check now and return success
  3744. //only if all the selected units qualified.
  3745. if( rule == SELECTION_ALL && count > 0 && qualify == count )
  3746. {
  3747. return TRUE;
  3748. }
  3749. // no can do!
  3750. return FALSE;
  3751. }
  3752. //------------------------------------------------------------------------------
  3753. Bool InGameUI::canSelectedObjectsDoSpecialPower( const CommandButton *command, const Object *objectToInteractWith, const Coord3D *position, SelectionRules rule, UnsignedInt commandOptions, Object* ignoreSelObj ) const
  3754. {
  3755. //Get the special power template.
  3756. const SpecialPowerTemplate *spTemplate = command->getSpecialPowerTemplate();
  3757. //Order of precendence:
  3758. //1) NO TARGET OR POS
  3759. //2) COMMAND_OPTION_NEED_OBJECT_TARGET
  3760. //3) NEED_TARGET_POS
  3761. Bool doAtPosition = BitTest( command->getOptions(), NEED_TARGET_POS );
  3762. Bool doAtObject = BitTest( command->getOptions(), COMMAND_OPTION_NEED_OBJECT_TARGET );
  3763. //Sanity checks
  3764. if( doAtObject && !objectToInteractWith )
  3765. {
  3766. return false;
  3767. }
  3768. if( doAtPosition && !position )
  3769. {
  3770. return false;
  3771. }
  3772. // get selected list of drawables
  3773. Drawable* ignoreSelDraw = ignoreSelObj ? ignoreSelObj->getDrawable() : NULL;
  3774. DrawableList tmpList;
  3775. if (ignoreSelDraw)
  3776. tmpList.push_back(ignoreSelDraw);
  3777. const DrawableList* selected = (tmpList.size() > 0) ? &tmpList : TheInGameUI->getAllSelectedDrawables();
  3778. // set up counters for rule checking
  3779. Int count = 0;
  3780. Int qualify = 0;
  3781. // loop through all the selected drawables
  3782. for( DrawableListCIt it = selected->begin(); it != selected->end(); ++it )
  3783. {
  3784. // get this drawable
  3785. Drawable* other = *it;
  3786. count++;
  3787. if( !doAtObject && !doAtPosition )
  3788. {
  3789. if( TheActionManager->canDoSpecialPower( other->getObject(), spTemplate, CMD_FROM_PLAYER, commandOptions ) )
  3790. {
  3791. //This is the no target version
  3792. if( rule == SELECTION_ANY )
  3793. {
  3794. return true;
  3795. }
  3796. qualify++;
  3797. }
  3798. }
  3799. else if( doAtObject )
  3800. {
  3801. if( TheActionManager->canDoSpecialPowerAtObject( other->getObject(), objectToInteractWith, CMD_FROM_PLAYER, spTemplate, commandOptions ) )
  3802. {
  3803. //This requires a object target
  3804. if( rule == SELECTION_ANY )
  3805. {
  3806. return true;
  3807. }
  3808. qualify++;
  3809. }
  3810. }
  3811. else if( doAtPosition )
  3812. {
  3813. if( TheActionManager->canDoSpecialPowerAtLocation( other->getObject(), position, CMD_FROM_PLAYER, spTemplate, objectToInteractWith, commandOptions ) )
  3814. {
  3815. //This requires a valid location.
  3816. if( rule == SELECTION_ANY )
  3817. {
  3818. return true;
  3819. }
  3820. qualify++;
  3821. }
  3822. }
  3823. }
  3824. if( rule == SELECTION_ALL && count > 0 && qualify == count )
  3825. {
  3826. return true;
  3827. }
  3828. return false;
  3829. }
  3830. //------------------------------------------------------------------------------
  3831. Bool InGameUI::canSelectedObjectsOverrideSpecialPowerDestination( const Coord3D *loc, SelectionRules rule, SpecialPowerType spType ) const
  3832. {
  3833. // set up counters for rule checking
  3834. Int count = 0;
  3835. Int qualify = 0;
  3836. // get selected list of drawables
  3837. const DrawableList *selected = TheInGameUI->getAllSelectedDrawables();
  3838. // loop through all the selected drawables
  3839. Drawable *other;
  3840. for( DrawableListCIt it = selected->begin(); it != selected->end(); ++it )
  3841. {
  3842. // get this drawable
  3843. other = *it;
  3844. count++;
  3845. if( TheActionManager->canOverrideSpecialPowerDestination( other->getObject(), loc, spType, CMD_FROM_PLAYER ) )
  3846. {
  3847. if( rule == SELECTION_ANY )
  3848. {
  3849. return true;
  3850. }
  3851. qualify++;
  3852. }
  3853. }
  3854. if( rule == SELECTION_ALL && count > 0 && qualify == count )
  3855. {
  3856. return true;
  3857. }
  3858. return false;
  3859. }
  3860. //------------------------------------------------------------------------------
  3861. Bool InGameUI::canSelectedObjectsEffectivelyUseWeapon( const CommandButton *command, const Object *objectToInteractWith, const Coord3D *position, SelectionRules rule ) const
  3862. {
  3863. //Get the special power template.
  3864. WeaponSlotType slot = command->getWeaponSlot();
  3865. //Order of precendence:
  3866. //1) NO TARGET OR POS
  3867. //2) COMMAND_OPTION_NEED_OBJECT_TARGET
  3868. //3) NEED_TARGET_POS
  3869. Bool doAtPosition = BitTest( command->getOptions(), NEED_TARGET_POS );
  3870. Bool doAtObject = BitTest( command->getOptions(), COMMAND_OPTION_NEED_OBJECT_TARGET );
  3871. //Sanity checks
  3872. if( doAtObject && !objectToInteractWith )
  3873. {
  3874. return false;
  3875. }
  3876. if( doAtPosition && !position )
  3877. {
  3878. return false;
  3879. }
  3880. // get selected list of drawables
  3881. const DrawableList *selected = TheInGameUI->getAllSelectedDrawables();
  3882. // set up counters for rule checking
  3883. Int count = 0;
  3884. Int qualify = 0;
  3885. // loop through all the selected drawables
  3886. Drawable *other;
  3887. for( DrawableListCIt it = selected->begin(); it != selected->end(); ++it )
  3888. {
  3889. // get this drawable
  3890. other = *it;
  3891. count++;
  3892. if( !doAtObject && !doAtPosition )
  3893. {
  3894. if( TheActionManager->canFireWeapon( other->getObject(), slot, CMD_FROM_PLAYER ) )
  3895. {
  3896. //This is the no target version
  3897. if( rule == SELECTION_ANY )
  3898. {
  3899. return true;
  3900. }
  3901. qualify++;
  3902. }
  3903. }
  3904. else if( doAtObject )
  3905. {
  3906. if( TheActionManager->canFireWeaponAtObject( other->getObject(), objectToInteractWith, CMD_FROM_PLAYER, slot ) )
  3907. {
  3908. //This requires a object target
  3909. if( rule == SELECTION_ANY )
  3910. {
  3911. return true;
  3912. }
  3913. qualify++;
  3914. }
  3915. }
  3916. else if( doAtPosition )
  3917. {
  3918. if( TheActionManager->canFireWeaponAtLocation( other->getObject(), position, CMD_FROM_PLAYER, slot, objectToInteractWith ) )
  3919. {
  3920. //This requires a valid location.
  3921. if( rule == SELECTION_ANY )
  3922. {
  3923. return true;
  3924. }
  3925. qualify++;
  3926. }
  3927. }
  3928. }
  3929. if( rule == SELECTION_ALL && count > 0 && qualify == count )
  3930. {
  3931. return true;
  3932. }
  3933. return false;
  3934. }
  3935. // ------------------------------------------------------------------------------------------------
  3936. Int InGameUI::selectAllUnitsByTypeAcrossRegion( IRegion2D *region, KindOfMaskType mustBeSet, KindOfMaskType mustBeClear )
  3937. {
  3938. KindOfSelectionData data;
  3939. Int newSelectionCount = 0;
  3940. Int oldSelectionCount = getAllSelectedDrawables()->size();
  3941. data.m_mustbeSet = mustBeSet;
  3942. data.m_mustbeClear = mustBeClear;
  3943. if (region)
  3944. {
  3945. TheTacticalView->iterateDrawablesInRegion(region, kindOfUnitSelection, (void *)&data);
  3946. newSelectionCount += data.newlySelectedDrawables.size();
  3947. }
  3948. else
  3949. {
  3950. // loop over the map
  3951. Drawable *temp = TheGameClient->firstDrawable();
  3952. while( temp )
  3953. {
  3954. if( kindOfUnitSelection( temp, (void *)&data) )
  3955. {
  3956. newSelectionCount ++;
  3957. }
  3958. temp = temp->getNextDrawable();
  3959. }
  3960. }
  3961. setDisplayedMaxWarning( FALSE );
  3962. if (newSelectionCount > 0)
  3963. {
  3964. // create selected message
  3965. GameMessage *teamMsg = TheMessageStream->appendMessage( GameMessage::MSG_CREATE_SELECTED_GROUP );
  3966. teamMsg->appendBooleanArgument( (oldSelectionCount == 0) ? TRUE : FALSE );
  3967. const Drawable *draw;
  3968. //Loop through each drawable add append it's objectID to the event.
  3969. for( DrawableListCIt it = data.newlySelectedDrawables.begin(); it != data.newlySelectedDrawables.end(); ++it )
  3970. {
  3971. draw = *it;
  3972. if( draw && draw->getObject() )
  3973. {
  3974. teamMsg->appendObjectIDArgument( draw->getObject()->getID() );
  3975. }
  3976. }
  3977. }
  3978. return newSelectionCount;
  3979. }
  3980. // ------------------------------------------------------------------------------------------------
  3981. /** Selects maching units on the screen */
  3982. // ------------------------------------------------------------------------------------------------
  3983. Int InGameUI::selectMatchingAcrossRegion( IRegion2D *region )
  3984. {
  3985. const DrawableList *selected = getAllSelectedDrawables();
  3986. /* loop through all the selected drawables and create a set of all the objects,
  3987. so that you only iterate once through each type of object
  3988. */
  3989. const Drawable *draw;
  3990. //std::set<AsciiString> drawableList;
  3991. std::set<const ThingTemplate*> drawableList;
  3992. Bool carBomb = FALSE;
  3993. for( DrawableListCIt it = selected->begin(); it != selected->end(); ++it )
  3994. {
  3995. // get this drawable
  3996. draw = *it;
  3997. if( draw && draw->getObject() && draw->getObject()->isLocallyControlled() )
  3998. {
  3999. // Use the Object's thing template, doing so will prevent wierdness for disguised vehicles.
  4000. drawableList.insert( draw->getObject()->getTemplate() );
  4001. if( draw->getObject()->testStatus( OBJECT_STATUS_IS_CARBOMB ) )
  4002. {
  4003. carBomb = TRUE;
  4004. }
  4005. }
  4006. }
  4007. if (drawableList.size() == 0)
  4008. return -1; // nothing useful selected to begin with - don't bother iterating
  4009. std::set<const ThingTemplate*>::iterator iter;
  4010. const ThingTemplate *templateName;
  4011. // now use the list to select across screen
  4012. MatchingUnitSelectionData data;
  4013. Int newSelectionCount = 0;
  4014. for( iter = drawableList.begin(); iter != drawableList.end(); ++iter )
  4015. {
  4016. // get this drawable
  4017. templateName = *iter;
  4018. data.templateToSelect = templateName;
  4019. data.isCarBomb = carBomb;
  4020. if (region)
  4021. newSelectionCount +=TheTacticalView->iterateDrawablesInRegion(region, similarUnitSelection, (void *)&data);
  4022. else
  4023. {
  4024. // loop over the map
  4025. Drawable *temp = TheGameClient->firstDrawable();
  4026. while( temp )
  4027. {
  4028. newSelectionCount += similarUnitSelection( temp, (void *)&data);
  4029. temp = temp->getNextDrawable();
  4030. }
  4031. }
  4032. setDisplayedMaxWarning( FALSE );
  4033. }
  4034. if (newSelectionCount > 0)
  4035. {
  4036. // create selected message
  4037. GameMessage *teamMsg = TheMessageStream->appendMessage( GameMessage::MSG_CREATE_SELECTED_GROUP_NO_SOUND );
  4038. // not creating a new team so pass in false
  4039. teamMsg->appendBooleanArgument( FALSE );
  4040. //Loop through each drawable add append it's objectID to the event.
  4041. for( DrawableListCIt it = data.newlySelectedDrawables.begin(); it != data.newlySelectedDrawables.end(); ++it )
  4042. {
  4043. draw = *it;
  4044. if( draw && draw->getObject() )
  4045. {
  4046. teamMsg->appendObjectIDArgument( draw->getObject()->getID() );
  4047. }
  4048. }
  4049. }
  4050. return newSelectionCount;
  4051. }
  4052. // ------------------------------------------------------------------------------------------------
  4053. Int InGameUI::selectAllUnitsByTypeAcrossScreen(KindOfMaskType mustBeSet, KindOfMaskType mustBeClear)
  4054. {
  4055. /// When implementing this, obey TheInGameUI->getMaxSelectCount() if it is > 0
  4056. IRegion2D region;
  4057. ICoord2D origin;
  4058. ICoord2D size;
  4059. TheTacticalView->getOrigin( &origin.x, &origin.y );
  4060. size.x = TheTacticalView->getWidth();
  4061. size.y = TheTacticalView->getHeight();
  4062. buildRegion( &origin, &size, &region );
  4063. Int numSelected = selectAllUnitsByTypeAcrossRegion(&region, mustBeSet, mustBeClear);
  4064. if (numSelected == -1)
  4065. {
  4066. UnicodeString message = TheGameText->fetch( "GUI:NothingSelected" );
  4067. TheInGameUI->message( message );
  4068. }
  4069. else if (numSelected == 0)
  4070. {
  4071. }
  4072. else
  4073. {
  4074. UnicodeString message = TheGameText->fetch( "GUI:SelectedAcrossScreen" );
  4075. TheInGameUI->message( message );
  4076. }
  4077. return numSelected;
  4078. }
  4079. // ------------------------------------------------------------------------------------------------
  4080. /** Selects maching units on the screen */
  4081. // ------------------------------------------------------------------------------------------------
  4082. Int InGameUI::selectMatchingAcrossScreen( void )
  4083. {
  4084. /// When implementing this, obey TheInGameUI->getMaxSelectCount() if it is > 0
  4085. IRegion2D region;
  4086. ICoord2D origin;
  4087. ICoord2D size;
  4088. TheTacticalView->getOrigin( &origin.x, &origin.y );
  4089. size.x = TheTacticalView->getWidth();
  4090. size.y = TheTacticalView->getHeight();
  4091. buildRegion( &origin, &size, &region );
  4092. Int numSelected = selectMatchingAcrossRegion(&region);
  4093. if (numSelected == -1)
  4094. {
  4095. UnicodeString message = TheGameText->fetch( "GUI:NothingSelected" );
  4096. TheInGameUI->message( message );
  4097. }
  4098. else if (numSelected == 0)
  4099. {
  4100. }
  4101. else
  4102. {
  4103. UnicodeString message = TheGameText->fetch( "GUI:SelectedAcrossScreen" );
  4104. TheInGameUI->message( message );
  4105. }
  4106. return numSelected;
  4107. }
  4108. //-------------------------------------------------------------------------------------------------
  4109. Int InGameUI::selectAllUnitsByTypeAcrossMap(KindOfMaskType mustBeSet, KindOfMaskType mustBeClear)
  4110. {
  4111. /// When implementing this, obey TheInGameUI->getMaxSelectCount() if it is > 0
  4112. Int numSelected = selectAllUnitsByTypeAcrossRegion(NULL, mustBeSet, mustBeClear);
  4113. if (numSelected == -1)
  4114. {
  4115. UnicodeString message = TheGameText->fetch( "GUI:NothingSelected" );
  4116. TheInGameUI->message( message );
  4117. }
  4118. else if (numSelected == 0)
  4119. {
  4120. Drawable *draw = TheInGameUI->getFirstSelectedDrawable();
  4121. if( !draw || !draw->getObject() || !draw->getObject()->isKindOf( KINDOF_STRUCTURE ) )
  4122. {
  4123. UnicodeString message = TheGameText->fetch( "GUI:SelectedAcrossMap" );
  4124. TheInGameUI->message( message );
  4125. }
  4126. }
  4127. else
  4128. {
  4129. UnicodeString message = TheGameText->fetch( "GUI:SelectedAcrossMap" );
  4130. TheInGameUI->message( message );
  4131. }
  4132. return numSelected;
  4133. }
  4134. //-------------------------------------------------------------------------------------------------
  4135. /** Selects matching units across map */
  4136. //-------------------------------------------------------------------------------------------------
  4137. Int InGameUI::selectMatchingAcrossMap()
  4138. {
  4139. /// When implementing this, obey TheInGameUI->getMaxSelectCount() if it is > 0
  4140. Int numSelected = selectMatchingAcrossRegion(NULL);
  4141. if (numSelected == -1)
  4142. {
  4143. UnicodeString message = TheGameText->fetch( "GUI:NothingSelected" );
  4144. TheInGameUI->message( message );
  4145. }
  4146. else if (numSelected == 0)
  4147. {
  4148. Drawable *draw = TheInGameUI->getFirstSelectedDrawable();
  4149. if( !draw || !draw->getObject() || !draw->getObject()->isKindOf( KINDOF_STRUCTURE ) )
  4150. {
  4151. UnicodeString message = TheGameText->fetch( "GUI:SelectedAcrossMap" );
  4152. TheInGameUI->message( message );
  4153. }
  4154. }
  4155. else
  4156. {
  4157. UnicodeString message = TheGameText->fetch( "GUI:SelectedAcrossMap" );
  4158. TheInGameUI->message( message );
  4159. }
  4160. return numSelected;
  4161. }
  4162. //-------------------------------------------------------------------------------------------------
  4163. Int InGameUI::selectAllUnitsByType(KindOfMaskType mustBeSet, KindOfMaskType mustBeClear)
  4164. {
  4165. /// When implementing this, obey TheInGameUI->getMaxSelectCount() if it is > 0
  4166. Int numSelected = selectAllUnitsByTypeAcrossScreen(mustBeSet, mustBeClear);
  4167. if (numSelected == -1)
  4168. {
  4169. return numSelected;
  4170. }
  4171. if (numSelected == 0)
  4172. {
  4173. Int numSelectedAcrossMap = selectAllUnitsByTypeAcrossMap(mustBeSet, mustBeClear);
  4174. return numSelectedAcrossMap;
  4175. }
  4176. return numSelected;
  4177. }
  4178. //-------------------------------------------------------------------------------------------------
  4179. /** Selects matching units, either on screen or across map. When called by pressing 'T',
  4180. their is not a way to tell if the game is supposed to select across the screen, or
  4181. across the map. For mouse clicks, i.e. Alt + click or double click, we can directly call
  4182. selectMatchingAcrossScreen or selectMatchingAcrossMap */
  4183. //-------------------------------------------------------------------------------------------------
  4184. Int InGameUI::selectUnitsMatchingCurrentSelection()
  4185. {
  4186. /// When implementing this, obey TheInGameUI->getMaxSelectCount() if it is > 0
  4187. Int numSelected = selectMatchingAcrossScreen();
  4188. if (numSelected == -1)
  4189. return numSelected;
  4190. if (numSelected == 0)
  4191. {
  4192. Int numSelectedAcrossMap = selectMatchingAcrossMap();
  4193. //if (numSelectedAcrossMap < 1)
  4194. //{
  4195. //UnicodeString message = TheGameText->fetch( "GUI:NothingSelected" );
  4196. //TheInGameUI->message( message );
  4197. //}
  4198. return numSelectedAcrossMap;
  4199. }
  4200. return numSelected;
  4201. }
  4202. //-----------------------------------------------------------------------------
  4203. /**
  4204. * Given an "anchor" point and the current mouse position (dest),
  4205. * construct a valid 2D bounding region.
  4206. */
  4207. //-----------------------------------------------------------------------------------
  4208. void InGameUI::buildRegion( const ICoord2D *anchor, const ICoord2D *dest, IRegion2D *region )
  4209. {
  4210. // build rectangular region defined by the drag selection
  4211. if (anchor->x < dest->x)
  4212. {
  4213. region->lo.x = anchor->x;
  4214. region->hi.x = dest->x;
  4215. }
  4216. else
  4217. {
  4218. region->lo.x = dest->x;
  4219. region->hi.x = anchor->x;
  4220. }
  4221. if (anchor->y < dest->y)
  4222. {
  4223. region->lo.y = anchor->y;
  4224. region->hi.y = dest->y;
  4225. }
  4226. else
  4227. {
  4228. region->lo.y = dest->y;
  4229. region->hi.y = anchor->y;
  4230. }
  4231. }
  4232. //-------------------------------------------------------------------------------------------------
  4233. /** Add a new floating text to our list */
  4234. //-------------------------------------------------------------------------------------------------
  4235. void InGameUI::addFloatingText(const UnicodeString& text,const Coord3D *pos, Color color)
  4236. {
  4237. if( TheGameLogic->getDrawIconUI() )
  4238. {
  4239. FloatingTextData *newFTD = newInstance( FloatingTextData );
  4240. newFTD->m_frameCount = 0;
  4241. newFTD->m_color = color;
  4242. newFTD->m_pos3D.x = pos->x;
  4243. newFTD->m_pos3D.z = pos->z;
  4244. newFTD->m_pos3D.y = pos->y;
  4245. newFTD->m_text = text;
  4246. newFTD->m_dString->setText(text);
  4247. if(m_floatingTextTimeOut <= 0)
  4248. newFTD->m_frameTimeOut = TheGameLogic->getFrame() + DEFAULT_FLOATING_TEXT_TIMEOUT;
  4249. else
  4250. newFTD->m_frameTimeOut = TheGameLogic->getFrame() + m_floatingTextTimeOut;
  4251. m_floatingTextList.push_front( newFTD ); // add to the list
  4252. }
  4253. }
  4254. //-------------------------------------------------------------------------------------------------
  4255. //-------------------------------------------------------------------------------------------------
  4256. #if defined(_DEBUG) || defined(_INTERNAL)
  4257. inline Bool isClose(Real a, Real b) { return fabs(a-b) <= 1.0f; }
  4258. inline Bool isClose(const Coord3D& a, const Coord3D& b)
  4259. {
  4260. return isClose(a.x, b.x) &&
  4261. isClose(a.y, b.y) &&
  4262. isClose(a.z, b.z);
  4263. }
  4264. void InGameUI::DEBUG_addFloatingText(const AsciiString& text, const Coord3D * pos, Color color)
  4265. {
  4266. const Int POINTSIZE = 8;
  4267. const Int LEADING = 0;
  4268. Coord3D posToUse = *pos;
  4269. try_again:
  4270. for (FloatingTextListIt it = m_floatingTextList.begin(); it != m_floatingTextList.end(); ++it)
  4271. {
  4272. if (isClose((*it)->m_pos3D, posToUse))
  4273. {
  4274. posToUse.z -= (POINTSIZE + LEADING);
  4275. goto try_again;
  4276. }
  4277. }
  4278. FloatingTextData *newFTD = newInstance( FloatingTextData );
  4279. newFTD->m_color = color;
  4280. newFTD->m_pos3D.x = posToUse.x;
  4281. newFTD->m_pos3D.y = posToUse.y;
  4282. newFTD->m_pos3D.z = posToUse.z;
  4283. UnicodeString translate;
  4284. translate.translate(text);
  4285. newFTD->m_text = translate;
  4286. newFTD->m_dString->setText(translate);
  4287. newFTD->m_dString->setFont(TheWindowManager->winFindFont( AsciiString("Arial"), POINTSIZE, FALSE ));
  4288. if(m_floatingTextTimeOut <= 0)
  4289. newFTD->m_frameTimeOut = TheGameLogic->getFrame() + DEFAULT_FLOATING_TEXT_TIMEOUT;
  4290. else
  4291. newFTD->m_frameTimeOut = TheGameLogic->getFrame() + m_floatingTextTimeOut;
  4292. m_floatingTextList.push_front( newFTD ); // add to the list
  4293. //DEBUG_LOG(("%s\n",text.str()));
  4294. }
  4295. #endif
  4296. //-------------------------------------------------------------------------------------------------
  4297. /** modify the position of our floating text */
  4298. //-------------------------------------------------------------------------------------------------
  4299. void InGameUI::updateFloatingText( void )
  4300. {
  4301. FloatingTextData *ftd; // pointer to our floating point data
  4302. UnsignedInt currLogicFrame = TheGameLogic->getFrame(); // the current logic frame
  4303. UnsignedByte r, g, b, a; // we'll need to break apart our color so we can modify the alpha
  4304. Int amount; // The amout we'll change the alpha
  4305. static UnsignedInt lastLogicFrameUpdate = currLogicFrame; // We need to make sure our current frame is different then our last frame we updated.
  4306. // only update the position if we're incrementing frames
  4307. if(lastLogicFrameUpdate == currLogicFrame)
  4308. return;
  4309. lastLogicFrameUpdate = currLogicFrame;
  4310. // Loop through our floating text list
  4311. for(FloatingTextListIt it = m_floatingTextList.begin(); it != m_floatingTextList.end();)
  4312. {
  4313. ftd = *it;
  4314. // move it up
  4315. ++ftd->m_frameCount;
  4316. // fade the text
  4317. if( currLogicFrame > ftd->m_frameTimeOut)
  4318. {
  4319. // modify the color
  4320. GameGetColorComponents(ftd->m_color, &r, &g, &b, &a);
  4321. amount = REAL_TO_INT( (currLogicFrame - ftd->m_frameTimeOut) * m_floatingTextMoveVanishRate);
  4322. if(a - amount < 0)
  4323. a = 0;
  4324. else
  4325. a -= amount;
  4326. ftd->m_color = GameMakeColor(r, g, b, a);
  4327. // if we have 0 alpha delete it
  4328. if( a <= 0)
  4329. {
  4330. it = m_floatingTextList.erase(it);
  4331. ftd->deleteInstance();
  4332. continue; // don't do the ++it below
  4333. }
  4334. }
  4335. // increase our itterator
  4336. ++it;
  4337. }
  4338. }
  4339. //-------------------------------------------------------------------------------------------------
  4340. /** Itterates through and draws each floating text */
  4341. //-------------------------------------------------------------------------------------------------
  4342. void InGameUI::drawFloatingText( void )
  4343. {
  4344. FloatingTextData *ftd;
  4345. // loop through and draw all the texts
  4346. for(FloatingTextListIt it = m_floatingTextList.begin(); it != m_floatingTextList.end(); ++it)
  4347. {
  4348. ftd = *it;
  4349. ICoord2D pos;
  4350. // get the local player's index
  4351. Int playerNdx = ThePlayerList->getLocalPlayer()->getPlayerIndex();
  4352. // which PartitionManager cells are we looking at?
  4353. Int pCX, pCY;
  4354. ThePartitionManager->worldToCell(ftd->m_pos3D.x, ftd->m_pos3D.y, &pCX, &pCY);
  4355. // translate it's 3d pos into a 2d screen pos
  4356. if( TheTacticalView->worldToScreen(&ftd->m_pos3D, &pos)
  4357. && ftd->m_dString
  4358. && ThePartitionManager->getShroudStatusForPlayer(playerNdx, pCX, pCY) == CELLSHROUD_CLEAR )
  4359. {
  4360. pos.y -= ftd->m_frameCount * m_floatingTextMoveUpSpeed;
  4361. Color dropColor;
  4362. UnsignedByte r, g, b, a;
  4363. Int width;
  4364. // make drop color black, but use the alpha setting of the fill color specified (for fading)
  4365. GameGetColorComponents( ftd->m_color, &r, &g, &b, &a );
  4366. dropColor = GameMakeColor( 0, 0, 0, a );
  4367. ftd->m_dString->getSize(&width, NULL);
  4368. // draw it!
  4369. ftd->m_dString->draw(pos.x - (width / 2), pos.y, ftd->m_color,dropColor);
  4370. }
  4371. }
  4372. }
  4373. //-------------------------------------------------------------------------------------------------
  4374. /** ittereate through and clear out the list of floating text */
  4375. //-------------------------------------------------------------------------------------------------
  4376. void InGameUI::clearFloatingText( void )
  4377. {
  4378. FloatingTextData *ftd;
  4379. // loop through and draw all the texts
  4380. for(FloatingTextListIt it = m_floatingTextList.begin(); it != m_floatingTextList.end();)
  4381. {
  4382. ftd = *it;
  4383. it = m_floatingTextList.erase(it);
  4384. ftd->deleteInstance();
  4385. }
  4386. }
  4387. //-------------------------------------------------------------------------------------------------
  4388. /** If we want to use the default text color, then we call this function */
  4389. //-------------------------------------------------------------------------------------------------
  4390. void InGameUI::popupMessage( const AsciiString& message, Int x, Int y, Int width, Bool pause, Bool pauseMusic)
  4391. {
  4392. popupMessage( message, x, y, width, m_popupMessageColor, pause, pauseMusic);
  4393. }
  4394. //-------------------------------------------------------------------------------------------------
  4395. /** initialize, and popup a message box to the user */
  4396. //-------------------------------------------------------------------------------------------------
  4397. void InGameUI::popupMessage( const AsciiString& identifier, Int x, Int y, Int width, Color textColor, Bool pause, Bool pauseMusic)
  4398. {
  4399. if(m_popupMessageData)
  4400. clearPopupMessageData();
  4401. UpdateDiplomacyBriefingText(identifier, FALSE);
  4402. UnicodeString message = TheGameText->fetch(identifier);
  4403. m_popupMessageData = newInstance( PopupMessageData );
  4404. m_popupMessageData->message = message;
  4405. // x and why are passed in as a percentage of the screen, convert to screen coords
  4406. if( x > 100 )
  4407. x = 100;
  4408. if( x < 0 )
  4409. x = 0;
  4410. if( y > 100 )
  4411. y = 100;
  4412. if( y < 0 )
  4413. y = 0;
  4414. m_popupMessageData->x = TheDisplay->getWidth() * (INT_TO_REAL(x) / 100);
  4415. m_popupMessageData->y = TheDisplay->getHeight() * (INT_TO_REAL(y) / 100);
  4416. // cap the lower limit of the width
  4417. if(width < 50)
  4418. width = 50;
  4419. m_popupMessageData->width = width;
  4420. m_popupMessageData->textColor = textColor;
  4421. m_popupMessageData->pause = pause;
  4422. m_popupMessageData->pauseMusic = pauseMusic;
  4423. if( pause )
  4424. TheGameLogic->setGamePaused(TRUE, pauseMusic);
  4425. m_popupMessageData->layout = TheWindowManager->winCreateLayout(AsciiString("InGamePopupMessage.wnd"));
  4426. m_popupMessageData->layout->runInit();
  4427. }
  4428. //-------------------------------------------------------------------------------------------------
  4429. /** take care of the logic of clearing the popupMessageData */
  4430. //-------------------------------------------------------------------------------------------------
  4431. void InGameUI::clearPopupMessageData( void )
  4432. {
  4433. if(!m_popupMessageData)
  4434. return;
  4435. if(m_popupMessageData->layout)
  4436. {
  4437. m_popupMessageData->layout->destroyWindows();
  4438. m_popupMessageData->layout->deleteInstance();
  4439. m_popupMessageData->layout = NULL;
  4440. }
  4441. if( m_popupMessageData->pause )
  4442. TheGameLogic->setGamePaused(FALSE, m_popupMessageData->pauseMusic);
  4443. m_popupMessageData->deleteInstance();
  4444. m_popupMessageData = NULL;
  4445. }
  4446. //-------------------------------------------------------------------------------------------------
  4447. //-------------------------------------------------------------------------------------------------
  4448. //-------------------------------------------------------------------------------------------------
  4449. //-------------------------------------------------------------------------------------------------
  4450. /** Floating Text Constructor */
  4451. //-------------------------------------------------------------------------------------------------
  4452. FloatingTextData::FloatingTextData(void)
  4453. {
  4454. // Added By Sadullah Nader
  4455. // Initializations missing and needed
  4456. m_color = 0;
  4457. m_frameCount = 0;
  4458. m_frameTimeOut = 0;
  4459. m_pos3D.zero();
  4460. m_text.clear();
  4461. //
  4462. m_dString = TheDisplayStringManager->newDisplayString();
  4463. }
  4464. //-------------------------------------------------------------------------------------------------
  4465. /** Floating Text Destructor */
  4466. //-------------------------------------------------------------------------------------------------
  4467. FloatingTextData::~FloatingTextData(void)
  4468. {
  4469. if(m_dString)
  4470. TheDisplayStringManager->freeDisplayString( m_dString );
  4471. m_dString = NULL;
  4472. }
  4473. ///////////////////////////////////////////////////////////////////////////////////////////////////
  4474. ///////////////////////////////////////////////////////////////////////////////////////////////////
  4475. // WORLD ANIMATION DATA ///////////////////////////////////////////////////////////////////////////
  4476. ///////////////////////////////////////////////////////////////////////////////////////////////////
  4477. ///////////////////////////////////////////////////////////////////////////////////////////////////
  4478. // ------------------------------------------------------------------------------------------------
  4479. // ------------------------------------------------------------------------------------------------
  4480. WorldAnimationData::WorldAnimationData( void )
  4481. {
  4482. m_anim = NULL;
  4483. m_worldPos.zero();
  4484. m_expireFrame = 0;
  4485. m_options = WORLD_ANIM_NO_OPTIONS;
  4486. m_zRisePerSecond = 0.0f;
  4487. } // end WorldAnimationData
  4488. // ------------------------------------------------------------------------------------------------
  4489. /** Add a 2D animation at a spot in the world */
  4490. // ------------------------------------------------------------------------------------------------
  4491. void InGameUI::addWorldAnimation( Anim2DTemplate *animTemplate,
  4492. const Coord3D *pos,
  4493. WorldAnimationOptions options,
  4494. Real durationInSeconds,
  4495. Real zRisePerSecond )
  4496. {
  4497. // sanity
  4498. if( animTemplate == NULL || pos == NULL || durationInSeconds <= 0.0f )
  4499. return;
  4500. // allocate a new world animation data struct
  4501. // (huh huh, he said "wad")
  4502. WorldAnimationData *wad = NEW WorldAnimationData;
  4503. if( wad == NULL )
  4504. return;
  4505. // allocate a new animation instance
  4506. Anim2D *anim = newInstance(Anim2D)( animTemplate, TheAnim2DCollection );
  4507. // assign all data
  4508. wad->m_anim = anim;
  4509. wad->m_expireFrame = TheGameLogic->getFrame() + (durationInSeconds * LOGICFRAMES_PER_SECOND);
  4510. wad->m_options = options;
  4511. wad->m_worldPos = *pos;
  4512. wad->m_zRisePerSecond = zRisePerSecond;
  4513. // add to list
  4514. m_worldAnimationList.push_front( wad );
  4515. } // end addWorldAnimation
  4516. // ------------------------------------------------------------------------------------------------
  4517. /** Delete all world animations */
  4518. // ------------------------------------------------------------------------------------------------
  4519. void InGameUI::clearWorldAnimations( void )
  4520. {
  4521. WorldAnimationData *wad;
  4522. // iterate through all entries and delete the animation data
  4523. for( WorldAnimationListIterator it = m_worldAnimationList.begin();
  4524. it != m_worldAnimationList.end(); /*empty*/ )
  4525. {
  4526. wad = *it;
  4527. if( wad )
  4528. {
  4529. // delete the animation instance
  4530. wad->m_anim->deleteInstance();
  4531. // delete the world animation data
  4532. delete wad;
  4533. } // end if
  4534. it = m_worldAnimationList.erase( it );
  4535. } // end for
  4536. } // end clearWorldAnimations
  4537. static const UnsignedInt FRAMES_BEFORE_EXPIRE_TO_FADE = LOGICFRAMES_PER_SECOND * 1;
  4538. // ------------------------------------------------------------------------------------------------
  4539. /** Update all world animations and draw the visible ones */
  4540. // ------------------------------------------------------------------------------------------------
  4541. void InGameUI::updateAndDrawWorldAnimations( void )
  4542. {
  4543. WorldAnimationData *wad;
  4544. // go through all animations
  4545. for( WorldAnimationListIterator it = m_worldAnimationList.begin();
  4546. it != m_worldAnimationList.end(); /*empty*/ )
  4547. {
  4548. // get data
  4549. wad = *it;
  4550. // update portion ... only when the game is in motion
  4551. if( wad && TheGameLogic->isGamePaused() == FALSE )
  4552. {
  4553. //
  4554. // see if it's time to expire this animation based on animation type and options or
  4555. // the expire frame
  4556. //
  4557. if( TheGameLogic->getFrame() >= wad->m_expireFrame ||
  4558. (BitTest( wad->m_options, WORLD_ANIM_PLAY_ONCE_AND_DESTROY ) &&
  4559. BitTest( wad->m_anim->getStatus(), ANIM_2D_STATUS_COMPLETE )) )
  4560. {
  4561. // delete this element and continue
  4562. wad->m_anim->deleteInstance();
  4563. delete wad;
  4564. it = m_worldAnimationList.erase( it );
  4565. continue;
  4566. } // end if
  4567. // update the Z value
  4568. if( wad->m_zRisePerSecond )
  4569. wad->m_worldPos.z += wad->m_zRisePerSecond / LOGICFRAMES_PER_SECOND;
  4570. } // end if
  4571. //
  4572. // don't bother going forward with the draw process if this location is shrouded for
  4573. // the local player
  4574. //
  4575. Int playerIndex = ThePlayerList->getLocalPlayer()->getPlayerIndex();
  4576. if( ThePartitionManager->getShroudStatusForPlayer( playerIndex, &wad->m_worldPos ) != CELLSHROUD_CLEAR )
  4577. {
  4578. ++it;
  4579. continue;
  4580. } // end if
  4581. // update translucency value
  4582. if( BitTest( wad->m_options, WORLD_ANIM_FADE_ON_EXPIRE ) )
  4583. {
  4584. // see if we should be setting the translucency value
  4585. UnsignedInt framesTillExpire = wad->m_expireFrame - TheGameLogic->getFrame();
  4586. if( framesTillExpire < FRAMES_BEFORE_EXPIRE_TO_FADE )
  4587. {
  4588. // compute alpha level so that we're totally gone by the expire frame
  4589. Real alpha = INT_TO_REAL( framesTillExpire ) / INT_TO_REAL( FRAMES_BEFORE_EXPIRE_TO_FADE );
  4590. wad->m_anim->setAlpha( alpha );
  4591. } // end if
  4592. } // end if
  4593. // project the point to screen space
  4594. ICoord2D screen;
  4595. if( TheTacticalView->worldToScreen( &wad->m_worldPos, &screen ) == TRUE )
  4596. {
  4597. UnsignedInt width = wad->m_anim->getCurrentFrameWidth();
  4598. UnsignedInt height = wad->m_anim->getCurrentFrameHeight();
  4599. // scale the width and height given the camera zoom level
  4600. Real zoomScale = TheTacticalView->getMaxZoom() / TheTacticalView->getZoom();
  4601. width *= zoomScale;
  4602. height *= zoomScale;
  4603. // adjust the screen position to draw so the image is centered at the location
  4604. screen.x -= width / 2;
  4605. screen.y -= height / 2;
  4606. // draw the animation
  4607. wad->m_anim->draw( screen.x, screen.y, width, height );
  4608. } // end if
  4609. // go to the next element in the list
  4610. ++it;
  4611. } // end for
  4612. } // end updateAndDrawWorldAnimations
  4613. Object *InGameUI::findIdleWorker( Object *obj)
  4614. {
  4615. if(!obj)
  4616. return NULL;
  4617. Int index = obj->getControllingPlayer()->getPlayerIndex();
  4618. if(m_idleWorkers[index].empty())
  4619. return NULL;
  4620. ObjectListIt it = m_idleWorkers[index].begin();
  4621. while(it != m_idleWorkers[index].end())
  4622. {
  4623. Object *itObj = *it;
  4624. if(itObj == obj)
  4625. {
  4626. return itObj;
  4627. break;
  4628. }
  4629. ++it;
  4630. }
  4631. return NULL;
  4632. }
  4633. void InGameUI::addIdleWorker( Object *obj )
  4634. {
  4635. if(!obj)
  4636. return;
  4637. if(findIdleWorker(obj))
  4638. return;
  4639. Int index = obj->getControllingPlayer()->getPlayerIndex();
  4640. m_idleWorkers[index].push_back(obj);
  4641. }
  4642. void InGameUI::removeIdleWorker( Object *obj, Int playerNumber )
  4643. {
  4644. if(!obj)
  4645. return;
  4646. if(playerNumber < 0 || playerNumber >= MAX_PLAYER_COUNT) // we're leaving the game, so this is all screwed
  4647. return;
  4648. if(m_idleWorkers[playerNumber].empty())
  4649. return;
  4650. ObjectListIt it = m_idleWorkers[playerNumber].begin();
  4651. while(it != m_idleWorkers[playerNumber].end())
  4652. {
  4653. Object *itObj = *it;
  4654. if(itObj == obj)
  4655. {
  4656. m_idleWorkers[playerNumber].erase(it);
  4657. return;
  4658. }
  4659. ++it;
  4660. }
  4661. return;
  4662. }
  4663. void InGameUI::selectNextIdleWorker( void )
  4664. {
  4665. Int index = ThePlayerList->getLocalPlayer()->getPlayerIndex();
  4666. if(m_idleWorkers[index].empty())
  4667. {
  4668. DEBUG_ASSERTCRASH(FALSE, ("InGameUI::selectNextIdleWorker We're trying to select a worker when our list is empty for player %ls", ThePlayerList->getLocalPlayer()->getPlayerDisplayName().str()));
  4669. return;
  4670. }
  4671. Object *selectThisObject = NULL;
  4672. if(getSelectCount() == 0 || getSelectCount() > 1)
  4673. {
  4674. selectThisObject = *m_idleWorkers[index].begin();
  4675. }
  4676. else
  4677. {
  4678. Drawable *selectedDrawable = TheInGameUI->getFirstSelectedDrawable();
  4679. ObjectListIt it = m_idleWorkers[index].begin();
  4680. while(it != m_idleWorkers[index].end())
  4681. {
  4682. Object *itObj = *it;
  4683. if(itObj == selectedDrawable->getObject())
  4684. {
  4685. ++it;
  4686. if(it != m_idleWorkers[index].end())
  4687. selectThisObject = *it;
  4688. else
  4689. selectThisObject = *m_idleWorkers[index].begin();
  4690. break;
  4691. }
  4692. ++it;
  4693. }
  4694. // if we had something selected that wasn't a worker, we'll get here
  4695. if(!selectThisObject)
  4696. selectThisObject = *m_idleWorkers[index].begin();
  4697. }
  4698. DEBUG_ASSERTCRASH(selectThisObject, ("InGameUI::selectNextIdleWorker Could not select the next IDLE worker"));
  4699. if(selectThisObject)
  4700. {
  4701. //If our idle worker is contained by anything, we need to select the container instead.
  4702. Object *containedBy = selectThisObject->getContainedBy();
  4703. if( containedBy )
  4704. {
  4705. selectThisObject = containedBy;
  4706. }
  4707. deselectAllDrawables();
  4708. GameMessage *teamMsg = TheMessageStream->appendMessage( GameMessage::MSG_CREATE_SELECTED_GROUP );
  4709. //New group or add to group? Passed in value is true if we are creating a new group.
  4710. teamMsg->appendBooleanArgument( TRUE );
  4711. teamMsg->appendObjectIDArgument( selectThisObject->getID() );
  4712. selectDrawable( selectThisObject->getDrawable() );
  4713. /*// removed becuase we're already playing a select sound... left in, just in case i"m wrong.
  4714. // play the units sound
  4715. const AudioEventRTS *soundEvent = selectThisObject->getTemplate()->getVoiceSelect();
  4716. if (soundEvent)
  4717. {
  4718. TheAudio->addAudioEvent( soundEvent );
  4719. }*/
  4720. // center on the unit
  4721. TheTacticalView->lookAt(selectThisObject->getPosition());
  4722. }
  4723. }
  4724. Int InGameUI::getIdleWorkerCount( void )
  4725. {
  4726. Int index = ThePlayerList->getLocalPlayer()->getPlayerIndex();
  4727. return m_idleWorkers[index].size();
  4728. }
  4729. void InGameUI::showIdleWorkerLayout( void )
  4730. {
  4731. if (!m_idleWorkerWin)
  4732. {
  4733. m_idleWorkerWin = TheWindowManager->winGetWindowFromId(NULL, TheNameKeyGenerator->nameToKey("ControlBar.wnd:ButtonIdleWorker"));
  4734. DEBUG_ASSERTCRASH(m_idleWorkerWin, ("InGameUI::showIdleWorkerLayout could not find IdleWorker.wnd to load "));
  4735. return;
  4736. }
  4737. m_idleWorkerWin->winEnable(TRUE);
  4738. m_currentIdleWorkerDisplay = getIdleWorkerCount();
  4739. // if(m_currentIdleWorkerDisplay < 1)
  4740. // GadgetButtonSetText(m_idleWorkerWin, UnicodeString::TheEmptyString);
  4741. // else
  4742. // {
  4743. // UnicodeString number;
  4744. // number.format(L"%d",m_currentIdleWorkerDisplay);
  4745. // GadgetButtonSetText(m_idleWorkerWin, number);
  4746. // }
  4747. }
  4748. void InGameUI::hideIdleWorkerLayout( void )
  4749. {
  4750. if(!m_idleWorkerWin)
  4751. return;
  4752. GadgetButtonSetText(m_idleWorkerWin, UnicodeString::TheEmptyString);
  4753. m_idleWorkerWin->winEnable(FALSE);
  4754. m_currentIdleWorkerDisplay = -1;
  4755. }
  4756. void InGameUI::updateIdleWorker( void )
  4757. {
  4758. Int idleCount = getIdleWorkerCount();
  4759. if(idleCount > 0 && m_currentIdleWorkerDisplay != idleCount && getInputEnabled())
  4760. showIdleWorkerLayout();
  4761. if((idleCount <= 0 && m_idleWorkerWin) || !getInputEnabled())
  4762. hideIdleWorkerLayout();
  4763. }
  4764. void InGameUI::resetIdleWorker( void )
  4765. {
  4766. if(m_idleWorkerWin)
  4767. {
  4768. GadgetButtonSetText(m_idleWorkerWin, UnicodeString::TheEmptyString);
  4769. }
  4770. m_currentIdleWorkerDisplay = -1;
  4771. for(Int i = 0; i < MAX_PLAYER_COUNT; ++i)
  4772. {
  4773. m_idleWorkers[i].clear();
  4774. }
  4775. }
  4776. void InGameUI::recreateControlBar( void )
  4777. {
  4778. GameWindow *win = TheWindowManager->winGetWindowFromId(NULL, TheNameKeyGenerator->nameToKey(AsciiString("ControlBar.wnd")));
  4779. if(win)
  4780. win->deleteInstance();
  4781. m_idleWorkerWin = NULL;
  4782. createControlBar();
  4783. if(TheControlBar)
  4784. {
  4785. delete TheControlBar;
  4786. TheControlBar = NEW ControlBar;
  4787. TheControlBar->init();
  4788. }
  4789. }
  4790. void InGameUI::disableTooltipsUntil(UnsignedInt frameNum)
  4791. {
  4792. if (frameNum > m_tooltipsDisabledUntil)
  4793. m_tooltipsDisabledUntil = frameNum;
  4794. }
  4795. void InGameUI::clearTooltipsDisabled()
  4796. {
  4797. m_tooltipsDisabledUntil = 0;
  4798. }
  4799. Bool InGameUI::areTooltipsDisabled() const
  4800. {
  4801. return (TheGameLogic->getFrame() < m_tooltipsDisabledUntil);
  4802. }
  4803. WindowMsgHandledType IdleWorkerSystem( GameWindow *window, UnsignedInt msg,
  4804. WindowMsgData mData1, WindowMsgData mData2 )
  4805. {
  4806. switch( msg )
  4807. {
  4808. //---------------------------------------------------------------------------------------------
  4809. case GWM_INPUT_FOCUS:
  4810. {
  4811. // if we're givin the opportunity to take the keyboard focus we must say we don't want it
  4812. if( mData1 == TRUE )
  4813. *(Bool *)mData2 = FALSE;
  4814. }
  4815. //---------------------------------------------------------------------------------------------
  4816. case GBM_SELECTED:
  4817. {
  4818. GameWindow *control = (GameWindow *)mData1;
  4819. static NameKeyType buttonSelectID = NAMEKEY( "IdleWorker.wnd:ButtonSelectNextIdleWorker" );
  4820. if (control && control->winGetWindowId() == buttonSelectID)
  4821. {
  4822. TheInGameUI->selectNextIdleWorker( );
  4823. }
  4824. break;
  4825. } // end button selected
  4826. //---------------------------------------------------------------------------------------------
  4827. default:
  4828. return MSG_IGNORED;
  4829. } // end switch( msg )
  4830. return MSG_HANDLED;
  4831. }