InGameUI.cpp 183 KB

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