From ff71ab14ce6bc3e9c7d1cee0bfb69d6ccaa1a47d Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 15 Oct 2022 23:13:47 +0200 Subject: [PATCH] - added a GZDoom-style alternative HUD. So far only implemented for Blood. --- source/CMakeLists.txt | 1 + source/core/namedef_custom.h | 1 + source/core/shared_hud.cpp | 197 +++++ source/core/statusbar2.cpp | 8 + source/games/blood/src/messages.cpp | 2 +- wadsrc/static/filter/blood/fontdefs.txt | 13 + wadsrc/static/fonts/indexfont/0030.png | Bin 0 -> 230 bytes wadsrc/static/fonts/indexfont/font.inf | 2 + wadsrc/static/graphics/invgeml1.png | Bin 0 -> 249 bytes wadsrc/static/graphics/invgeml2.png | Bin 0 -> 251 bytes wadsrc/static/graphics/invgemr1.png | Bin 0 -> 245 bytes wadsrc/static/graphics/invgemr2.png | Bin 0 -> 247 bytes wadsrc/static/zscript.txt | 1 + wadsrc/static/zscript/alt_hud.zs | 672 ++++++++++++++++++ .../static/zscript/games/blood/bloodgame.zs | 20 + wadsrc/static/zscript/games/blood/ui/sbar.zs | 106 ++- wadsrc/static/zscript/games/duke/ui/sbar.zs | 1 + wadsrc/static/zscript/games/duke/ui/sbar_d.zs | 2 +- wadsrc/static/zscript/statusbar.zs | 11 + 19 files changed, 1027 insertions(+), 10 deletions(-) create mode 100644 source/core/shared_hud.cpp create mode 100644 wadsrc/static/fonts/indexfont/0030.png create mode 100644 wadsrc/static/fonts/indexfont/font.inf create mode 100644 wadsrc/static/graphics/invgeml1.png create mode 100644 wadsrc/static/graphics/invgeml2.png create mode 100644 wadsrc/static/graphics/invgemr1.png create mode 100644 wadsrc/static/graphics/invgemr2.png create mode 100644 wadsrc/static/zscript/alt_hud.zs diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index c19260fab..d7e77606d 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -1003,6 +1003,7 @@ set (PCH_SOURCES core/palette.cpp core/zcompile.cpp core/statusbar2.cpp + core/shared_hud.cpp core/gi.cpp core/defparser.cpp diff --git a/source/core/namedef_custom.h b/source/core/namedef_custom.h index 116d76851..feb9521a2 100644 --- a/source/core/namedef_custom.h +++ b/source/core/namedef_custom.h @@ -9,3 +9,4 @@ xx(CreditsMenu) xx(MultiMenu) xx(RazeStatusBar) xx(zoomsize) +xx(AltHud) diff --git a/source/core/shared_hud.cpp b/source/core/shared_hud.cpp new file mode 100644 index 000000000..cbc867a9b --- /dev/null +++ b/source/core/shared_hud.cpp @@ -0,0 +1,197 @@ +/* +** Enhanced heads up 'overlay' for fullscreen +** +**--------------------------------------------------------------------------- +** Copyright 2003-2008 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "v_video.h" +#include "gi.h" +#include "filesystem.h" +#include "sc_man.h" +#include "d_net.h" +#include "cmdlib.h" +#include "vm.h" +#include "v_draw.h" +#include "printf.h" +#include "v_font.h" +#include "mapinfo.h" +#include "base_sbar.h" + +#include + + +CVAR(Int,hud_althudscale, 0, CVAR_ARCHIVE) // Scale the hud to 640x400? +CVAR(Bool,hud_althud, false, CVAR_ARCHIVE) // Enable/Disable the alternate HUD + +CVAR(Int, am_showtotaltime, 0, CVAR_ARCHIVE); // Show time on automap +CVAR(Int, am_showtime, 0, CVAR_ARCHIVE); // Show time on automap + + // These are intentionally not the same as in the automap! +CVAR (Bool, hud_showsecrets, true,CVAR_ARCHIVE); // Show secrets on HUD +CVAR (Bool, hud_showkills, true,CVAR_ARCHIVE); // Show monster stats on HUD +CVAR (Bool, hud_showstats, false, CVAR_ARCHIVE); // for stamina and accuracy. +CVAR (Bool, hud_showscore, false, CVAR_ARCHIVE); // for user maintained score +CVAR (Bool, hud_showweapons, true, CVAR_ARCHIVE); // Show weapons collected +CVAR (Int , hud_showammo, 2, CVAR_ARCHIVE); // Show ammo collected +CVAR (Int , hud_showtime, 0, CVAR_ARCHIVE); // Show time on HUD +CVAR (Int , hud_showtimestat, 0, CVAR_ARCHIVE); // Show time on HUD as statistics widget +CVAR (Int , hud_timecolor, CR_GOLD,CVAR_ARCHIVE); // Color of in-game time on HUD + +CVAR (Int, hud_ammo_order, 0, CVAR_ARCHIVE); // ammo image and text order +CVAR (Int, hud_ammo_red, 25, CVAR_ARCHIVE) // ammo percent less than which status is red +CVAR (Int, hud_ammo_yellow, 50, CVAR_ARCHIVE) // ammo percent less is yellow more green +CVAR (Int, hud_health_red, 25, CVAR_ARCHIVE) // health amount less than which status is red +CVAR (Int, hud_health_yellow, 50, CVAR_ARCHIVE) // health amount less than which status is yellow +CVAR (Int, hud_health_green, 100, CVAR_ARCHIVE) // health amount above is blue, below is green +CVAR (Int, hud_armor_red, 25, CVAR_ARCHIVE) // armor amount less than which status is red +CVAR (Int, hud_armor_yellow, 50, CVAR_ARCHIVE) // armor amount less than which status is yellow +CVAR (Int, hud_armor_green, 100, CVAR_ARCHIVE) // armor amount above is blue, below is green + +CVAR (Bool, hud_berserk_health, true, CVAR_ARCHIVE); // when found berserk pack instead of health box +CVAR (Bool, hud_showangles, false, CVAR_ARCHIVE) // show player's pitch, yaw, roll + +CVAR (Int, hudcolor_titl, CR_YELLOW, CVAR_ARCHIVE) // color of automap title +CVAR (Int, hudcolor_time, CR_RED, CVAR_ARCHIVE) // color of level/hub time +CVAR (Int, hudcolor_ltim, CR_ORANGE, CVAR_ARCHIVE) // color of single level time +CVAR (Int, hudcolor_ttim, CR_GOLD, CVAR_ARCHIVE) // color of total time +CVAR (Int, hudcolor_xyco, CR_GREEN, CVAR_ARCHIVE) // color of coordinates + +CVAR (Int, hudcolor_statnames, CR_RED, CVAR_ARCHIVE) // For the letters before the stats +CVAR (Int, hudcolor_stats, CR_GREEN, CVAR_ARCHIVE) // For the stats values themselves + + +CVAR(Bool, map_point_coordinates, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // show player or map coordinates? + +DObject* AltHud; +extern DStatusBarCore* StatusBar; + +//--------------------------------------------------------------------------- +// +// Create Alternative HUD +// +//--------------------------------------------------------------------------- + +/* +CUSTOM_CVAR(Bool, hud_althud_forceinternal, false, CVAR_ARCHIVE | CVAR_NOINITCALL) +{ + CreateAltHUD(); +} +*/ + +static DObject* DoCreateAltHUD(const FName classname) +{ + if (classname == NAME_None) + return nullptr; + + const auto cls = PClass::FindClass(classname); + if (!cls) + { + Printf(TEXTCOLOR_RED "Unknown alternative HUD class \"%s\"\n", classname.GetChars()); + return nullptr; + } + + if (!cls->IsDescendantOf(NAME_AltHud)) + { + Printf(TEXTCOLOR_RED "Alternative HUD class \"%s\" is not derived from AltHud\n", classname.GetChars()); + return nullptr; + } + + const auto althud = cls->CreateNew(); + + IFVIRTUALPTRNAME(althud, NAME_AltHud, Init) + { + VMValue params[] = { althud }; + VMCall(func, params, countof(params), nullptr, 0); + } + + return althud; +} + +void CreateAltHUD() +{ + if (AltHud) + { + GC::DelSoftRoot(AltHud); + AltHud->Destroy(); + AltHud = nullptr; + } + + /* + if (!hud_althud_forceinternal) + AltHud = DoCreateAltHUD(gameinfo.althudclass); + */ + + if (!AltHud) + AltHud = DoCreateAltHUD(NAME_AltHud); + + assert(AltHud); + GC::AddSoftRoot(AltHud); +} + + +//--------------------------------------------------------------------------- +// +// draw the HUD +// +//--------------------------------------------------------------------------- +EXTERN_CVAR(Bool, hud_aspectscale) +//EXTERN_CVAR(Bool, hud_oldscale) +EXTERN_CVAR(Float, hud_scalefactor) + +void DrawAltHUD(SummaryInfo *info) +{ + int hudwidth; + int hudheight; + + if (!AltHud) CreateAltHUD(); + + /* + if (hud_oldscale) + { + int scale = GetUIScale(twod, hud_althudscale); + hudwidth = twod->GetWidth() / scale; + hudheight = twod->GetHeight() / scale; + } + else + */ + { + hudwidth = int(640 / hud_scalefactor); + hudheight = hudwidth * twod->GetHeight() / twod->GetWidth(); + } + if (hud_aspectscale) hudheight = hudheight * 5 / 6; + + + IFVIRTUALPTRNAME(AltHud, NAME_AltHud, Draw) + { + VMValue params[] = { AltHud, StatusBar, info, hudwidth, hudheight }; + VMCall(func, params, countof(params), nullptr, 0); + } +} + diff --git a/source/core/statusbar2.cpp b/source/core/statusbar2.cpp index 71ddedd17..f65769808 100644 --- a/source/core/statusbar2.cpp +++ b/source/core/statusbar2.cpp @@ -73,6 +73,7 @@ EXTERN_CVAR (Bool, am_showtotaltime) EXTERN_CVAR (Bool, noisedebug) EXTERN_CVAR(Bool, vid_fps) EXTERN_CVAR(Bool, inter_subtitles) +EXTERN_CVAR(Bool, hud_althud) extern int setblocks; @@ -181,8 +182,15 @@ void drawMapTitle() } } +void DrawAltHUD(SummaryInfo* info); + void UpdateStatusBar(SummaryInfo* info) { + if (hud_althud) + { + DrawAltHUD(info); + return; + } IFVIRTUALPTRNAME(StatusBar, NAME_RazeStatusBar, UpdateStatusBar) { VMValue params[] = { StatusBar, info }; diff --git a/source/games/blood/src/messages.cpp b/source/games/blood/src/messages.cpp index 47306b650..79309682f 100644 --- a/source/games/blood/src/messages.cpp +++ b/source/games/blood/src/messages.cpp @@ -147,7 +147,7 @@ void SetArmor(PLAYER* pPlayer, bool stat) void SetKeys(PLAYER* pPlayer, bool stat) { - for (int i = 1; i <= 6; i++) + for (int i = 1; i <= 7; i++) pPlayer->hasKey[i] = stat; if (stat) viewSetMessage(GStrings("TXTB_ALLKEYS")); diff --git a/wadsrc/static/filter/blood/fontdefs.txt b/wadsrc/static/filter/blood/fontdefs.txt index 21f8607f0..126c7b75b 100644 --- a/wadsrc/static/filter/blood/fontdefs.txt +++ b/wadsrc/static/filter/blood/fontdefs.txt @@ -382,3 +382,16 @@ digifont "-}" #04573 } +hudfont_blood +{ + 0 #09220 + 1 #09221 + 2 #09222 + 3 #09223 + 4 #09224 + 5 #09225 + 6 #09226 + 7 #09227 + 8 #09228 + 9 #09229 +} diff --git a/wadsrc/static/fonts/indexfont/0030.png b/wadsrc/static/fonts/indexfont/0030.png new file mode 100644 index 0000000000000000000000000000000000000000..1e4547d5b98eb9ea696381e368f2f55837b4d7a1 GIT binary patch literal 230 zcmeAS@N?(olHy`uVBq!ia0vp^8bHj(!3-qZ+H@p=6k~CayA#8@b22Z19F}xPUq=Rp zjs4tz5?O(Ko&cW^*XIBKflLO55FnnrIP5HtVkrso3;qujchFk-1<2%WyHsnrrUIk`BP`_bF_fTWb=O`=+S z{u6o%=X3>>oW7K_ep_!w<;!XL!uy_ls%Ex*w$?CcZNdK{#n0`BS6pJAD8v|?!}JVj PIs=2JtDnm{r-UW|j3i04 literal 0 HcmV?d00001 diff --git a/wadsrc/static/fonts/indexfont/font.inf b/wadsrc/static/fonts/indexfont/font.inf new file mode 100644 index 000000000..c7afafabc --- /dev/null +++ b/wadsrc/static/fonts/indexfont/font.inf @@ -0,0 +1,2 @@ +CellSize 4, 6 // This implies font sheets + diff --git a/wadsrc/static/graphics/invgeml1.png b/wadsrc/static/graphics/invgeml1.png new file mode 100644 index 0000000000000000000000000000000000000000..60ed485ed5b98556ba7dfb3632e9afae132e1bd4 GIT binary patch literal 249 zcmeAS@N?(olHy`uVBq!ia0vp^96-#&!3HGb=lz)rq!f}pf_xbms#F;m8k!jxe*Op2 zFBuq04Hy_+B``2p&0t^<&z}^3)D5VXv%n*=n1O*?2!t6g-L3lr6qGD+jVKAuPb(=; zEJ|f4NX*PD(aTFMQ83Xn&@(hQ&YPbMR21du;uxY4+}d}LlfjUKMfc$IZ4Zq9if=z& zRdGal(s6-9JgXvIl$AIds_xG9^;&b3|F8SarpY2ZlO`QHlc64%QZRQxLYL7B<FGiMkr!b88RX}YZIzcGDs=(YzcC#{nvHBOl9F)yJ$BAvDPh~Vq$ rkIxlPyr>W6ta6z+Wr3Rv=Yjl^sBEuI4^-=b_Aq$5`njxgN@xNAUQ$zm literal 0 HcmV?d00001 diff --git a/wadsrc/static/graphics/invgemr1.png b/wadsrc/static/graphics/invgemr1.png new file mode 100644 index 0000000000000000000000000000000000000000..a7ed50a46441935978d4ce929dc1cff12372322e GIT binary patch literal 245 zcmeAS@N?(olHy`uVBq!ia0vp^oIuRO!3HEZ#7tidq!f}pf_xbms#F;m8k!jxe*Op2 zFBuq04Hy_+B``2p&0t^<&z}^3)D5VXv%n*=n1O*?2!t6g-L3lr6qGD+jVKAuPb(=; zEJ|f4NX*PD(aTFMQ83Xn&@(hQ&YPbMR21gv;uxY4Tzht-AcG=@%SGMB31S>?_gn0- zbx#vf{K5ExEirA~s*BqXD1SDz(R$FyH6yS|Ca#0^jll23c7`=BOISY2wu^1}EyP&P kX!hu^9^03rJzESj3ig<$$oEVC02((GJ!-|!Z&elZQ`>k3)`Md(tQlHgTd3)&t;ucLK6T-hEK!* literal 0 HcmV?d00001 diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index 51559ff3d..9350ba327 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -35,6 +35,7 @@ version "4.9" #include "zscript/usermapmenu.zs" #include "zscript/maptypes.zs" #include "zscript/coreactor.zs" +#include "zscript/alt_hud.zs" #include "zscript/games/duke/dukeactor.zs" #include "zscript/games/blood/bloodactor.zs" #include "zscript/games/sw/swactor.zs" diff --git a/wadsrc/static/zscript/alt_hud.zs b/wadsrc/static/zscript/alt_hud.zs new file mode 100644 index 000000000..ad8597f65 --- /dev/null +++ b/wadsrc/static/zscript/alt_hud.zs @@ -0,0 +1,672 @@ +/* +** Enhanced heads up 'overlay' for fullscreen +** +**--------------------------------------------------------------------------- +** Copyright 2003-2008 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +struct HudStats +{ + String healthicon; + int healthtranslation; + int healthvalue; + + Array armoricons; + Array armortranslations; + Array armorvalues; + + Array weaponicons; + Array weapontranslations; + int weaponselect; + + Array ammoicons; + Array ammotranslations; + Array ammovalues; + Array ammomaxvalues; + Array ammoaltvalues; + int ammoselect; + + Array keyicons; + Array keytranslations; + + Array inventoryicons; + Array inventoryamounts; // negative values can be used for special states (-1: "ON", -2: "OFF", -3: "AUTO") + int inventoryselect; + + StatsPrintInfo info; + + void Clear() + { + healthicon = ""; + healthvalue = 0; + healthtranslation = 0; + armoricons.Clear(); + armortranslations.Clear(); + armorvalues.Clear(); + + ammoicons.Clear(); + ammotranslations.Clear(); + ammovalues.Clear(); + ammomaxvalues.Clear(); + ammoaltvalues.Clear(); + + weaponicons.Clear(); + weapontranslations.Clear(); + + keyicons.Clear(); + keytranslations.Clear(); + + inventoryicons.Clear(); + inventoryamounts.Clear(); + } +}; + + +class AltHud ui +{ + TextureID invgem_left, invgem_right; + int hudwidth, hudheight; + int statspace; + Font HudFont; // The font for the health and armor display + Font IndexFont; // The font for the inventory indices + Font StatFont; + const POWERUPICONSIZE = 32; + HudStats currentStats; // must be filled in by the status bar. + + + virtual void Init() + { + HudFont = BigFont; // Strife doesn't have anything nice so use the standard font + if (Raze.isBlood()) HudFont = Font.GetFont("HUDFONT_BLOOD"); + IndexFont = Font.GetFont("INDEXFONT"); + if (IndexFont == NULL) IndexFont = ConFont; // Emergency fallback + if (!Raze.isNamWW2GI()) + StatFont = SmallFont; + else + StatFont = ConFont; + + invgem_left = TexMan.CheckForTexture("INVGEML1", TexMan.Type_MiscPatch); + invgem_right = TexMan.CheckForTexture("INVGEMR1", TexMan.Type_MiscPatch); + statspace = StatFont.StringWidth("Ac:"); + } + + //--------------------------------------------------------------------------- + // + // Draws an image into a box with its bottom center at the bottom + // center of the box. The image is scaled down if it doesn't fit + // + //--------------------------------------------------------------------------- + + void DrawImageToBox(TextureID tex, int x, int y, int w, int h, double trans = 0.75, bool animate = false) + { + double scale1, scale2; + + if (tex) + { + let texsize = TexMan.GetScaledSize(tex); + + if (w < texsize.X) scale1 = w / texsize.X; + else scale1 = 1.0; + if (h < texsize.Y) scale2 = h / texsize.Y; + else scale2 = 1.0; + scale1 = min(scale1, scale2); + if (scale2 < scale1) scale1=scale2; + + x += w >> 1; + y += h; + + w = (int)(texsize.X * scale1); + h = (int)(texsize.Y * scale1); + + screen.DrawTexture(tex, animate, x, y, + DTA_KeepRatio, true, + DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, trans, + DTA_DestWidth, w, DTA_DestHeight, h, DTA_CenterBottomOffset, 1); + + } + } + + + //--------------------------------------------------------------------------- + // + // Draws a text but uses a fixed width for all characters + // + //--------------------------------------------------------------------------- + + void DrawHudText(Font fnt, int color, String text, int x, int y, double trans = 0.75) + { + int zerowidth = fnt.GetCharWidth("0"); + screen.DrawText(fnt, color, x, y-fnt.GetHeight(), text, DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, + DTA_KeepRatio, true, DTA_Alpha, trans, DTA_Monospace, MONO_CellCenter, DTA_Spacing, zerowidth); + } + + + //--------------------------------------------------------------------------- + // + // Draws a number with a fixed width for all digits + // + //--------------------------------------------------------------------------- + + void DrawHudNumber(Font fnt, int color, int num, int x, int y, double trans = 0.75) + { + DrawHudText(fnt, color, String.Format("%d", num), x, y, trans); + } + + //--------------------------------------------------------------------------- + // + // Draws a time string as hh:mm:ss + // + //--------------------------------------------------------------------------- + + virtual void DrawTimeString(Font fnt, int color, int seconds, int x, int y, double trans = 0.75) + { + String s = String.Format("%02i:%02i:%02i", seconds / 3600, (seconds % 3600) / 60, seconds % 60); + int length = 8 * fnt.GetCharWidth("0"); + DrawHudText(fnt, color, s, x-length, y, trans); + } + + //=========================================================================== + // + // draw the status (number of kills etc) + // + //=========================================================================== + + virtual void DrawStatLine(int x, in out int y, String prefix, String text) + { + y -= StatFont.GetHeight()-1; + screen.DrawText(StatFont, Font.CR_UNTRANSLATED, x, y, prefix, + DTA_KeepRatio, true, + DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, 0.75); + + screen.DrawText(StatFont, Font.CR_UNTRANSLATED, x+statspace, y, text, + DTA_KeepRatio, true, + DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, 0.75); + } + + virtual void DrawStatus(SummaryInfo stats, int x, int y) + { + //if (!deathmatch) + { + if (hud_showsecrets && stats.maxsecrets > 0) + { + String prefix = String.Format("%sS:", currentStats.info.letterColor); + String text = String.Format("%s%d/%d", stats.secrets >= stats.maxsecrets ? currentStats.info.completeColor : currentStats.info.standardColor, stats.secrets, stats.maxsecrets); + if (stats.supersecrets > 0) text.AppendFormat("+%d", stats.supersecrets); + + DrawStatLine(x, y, prefix, text); + } + + if (hud_showkills && stats.maxkills != -1) + { + String prefix; + String text; + + if (stats.maxkills == -3) prefix = String.Format("%sF:", currentStats.info.letterColor); + else prefix = String.Format("%sK:", currentStats.info.letterColor); + + if (stats.maxkills == -3) text = String.Format("%s%d", currentStats.info.standardColor, stats.kills); + else if (stats.maxkills == -2) text = String.Format("%s%d", currentStats.info.standardColor, stats.kills); + else text = String.Format("%s%d/%d", stats.kills == stats.maxkills ? currentStats.info.completeColor : currentStats.info.standardColor, stats.kills, stats.maxkills); + + DrawStatLine(x, y, prefix, text); + } + + if (hud_showtimestat) + { + String prefix = String.Format("%sT:", currentStats.info.letterColor); + String text; + let seconds = stats.time / 1000; + + if (seconds >= 3600) + text = String.Format("%s%02i:%02i:%02i", currentStats.info.standardColor, seconds / 3600, (seconds % 3600) / 60, seconds % 60); + else + text = String.Format("%s%02i:%02i", currentStats.info.standardColor, seconds / 60, seconds % 60); + DrawStatLine(x, y, prefix, text); + } + } + } + + //=========================================================================== + // + // draw health + // + //=========================================================================== + + virtual void DrawHealth(int x, int y) + { + int health = currentStats.healthvalue; + + // decide on the color first + int fontcolor = + health < hud_health_red ? Font.CR_RED : + health < hud_health_yellow ? Font.CR_GOLD : + health <= hud_health_green ? Font.CR_GREEN : + Font.CR_BLUE; + + DrawImageToBox(TexMan.CheckForTexture(currentStats.healthicon), x, y, 31, 17, 0.75, true); + DrawHudNumber(HudFont, fontcolor, health, x + 33, y + 17); + } + + //=========================================================================== + // + // Draw Armor. + // very similar to drawhealth, but adapted to handle Hexen armor too + // + //=========================================================================== + + virtual void DrawArmor(int xx, int y) + { + // Todo: need to figure out how to display Blood's 3 armors without blowing the layout + + int x = xx; + for(int i = 0; i < currentStats.armoricons.Size(); i++) + { + int ap = currentStats.armorvalues[i]; + + if (ap) + { + // decide on color + int fontcolor = + ap < hud_armor_red ? Font.CR_RED : + ap < hud_armor_yellow ? Font.CR_GOLD : + ap <= hud_armor_green ? Font.CR_GREEN : + Font.CR_BLUE; + + DrawImageToBox(TexMan.CheckForTexture(currentStats.armoricons[i]), x, y, 31, 17, 0.75, true); + DrawHudNumber(HudFont, fontcolor, ap, x + 33, y + 17); + x += 60; + } + } + } + + //=========================================================================== + // + // KEYS + // + //=========================================================================== + + //--------------------------------------------------------------------------- + // + // Draw one key + // + //--------------------------------------------------------------------------- + + virtual bool DrawOneKey(int x, int y, int keyindex) + { + TextureID icon = TexMan.CheckForTexture(currentstats.keyicons[keyindex]); + + if (icon.isValid()) + { + DrawImageToBox(icon, x, y, 8, 10); + return true; + } + return false; + } + + //--------------------------------------------------------------------------- + // + // Draw all keys + // + //--------------------------------------------------------------------------- + + virtual int DrawKeys(int x, int y) + { + int yo = y; + int xo = x; + int i; + int c = 0; + + // Go through the list in reverse order of definition, because we start at the right. + for(int i = currentStats.keyicons.Size() - 1; i >= 0; i--) + { + if (DrawOneKey(x - 9, y, i)) + { + x -= 9; + if (++c >= 10) + { + x = xo; + y -= 11; + c = 0; + } + } + } + if (x == xo && y != yo) y += 11; // undo the last wrap if the current line is empty. + return y - 11; + } + + //--------------------------------------------------------------------------- + // + // Drawing Ammo helpers + // + //--------------------------------------------------------------------------- + + static int GetDigitCount(int value) + { + int digits = 0; + + do + { + value /= 10; + ++digits; + } + while (0 != value); + + return digits; + } + + int, int GetAmmoTextLengths() + { + int tammomax = 0, tammocur = 0; + for(int i = 0; i < currentStats.ammovalues.Size(); i++) + { + int ammomax = currentstats.ammomaxvalues[i], ammocur = currentstats.ammovalues[i]; + tammocur = MAX(ammocur, tammocur); + tammomax = MAX(ammomax, tammomax); + } + return GetDigitCount(tammocur), GetDigitCount(tammomax); + } + + //--------------------------------------------------------------------------- + // + // Drawing Ammo + // + //--------------------------------------------------------------------------- + + virtual int DrawAmmo(int x, int y) + { + int ammocurlen = 0; + int ammomaxlen = 0; + [ammocurlen, ammomaxlen] = GetAmmoTextLengths(); + + String buf = String.Format("%0*d/%0*d", ammocurlen, 0, ammomaxlen, 0); + int def_width = ConFont.StringWidth(buf); + int yadd = ConFont.GetHeight(); + + int xtext = x - def_width; + int ximage = x; + + if (hud_ammo_order > 0) + { + xtext -= 24; + ximage -= 20; + } + else + { + ximage -= def_width + 20; + } + + + // Go through the list in reverse order of definition, because we start at the right. + for(int i = currentStats.ammoicons.Size() - 1; i >= 0; i--) + { + int curammo = currentStats.ammovalues[i]; + int maxammo = currentStats.ammomaxvalues[i]; + + double trans= i == currentstats.ammoselect ? 0.75 : 0.375; + + // buf = String.Format("%d/%d", ammo, maxammo); + buf = String.Format("%*d/%*d", ammocurlen, curammo, ammomaxlen, maxammo); + + int tex_width= clamp(ConFont.StringWidth(buf) - def_width, 0, 1000); + + int fontcolor=( !maxammo ? Font.CR_GRAY : + curammo < ( (maxammo * hud_ammo_red) / 100) ? Font.CR_RED : + curammo < ( (maxammo * hud_ammo_yellow) / 100) ? Font.CR_GOLD : Font.CR_GREEN ); + + DrawHudText(ConFont, fontcolor, buf, xtext-tex_width, y+yadd, trans); + DrawImageToBox(TexMan.CheckForTexture(currentStats.ammoicons[i]), ximage, y, 16, 8, trans); + y-=10; + } + return y; + } + + //--------------------------------------------------------------------------- + // + // Drawing weapons + // + //--------------------------------------------------------------------------- + + virtual void DrawOneWeapon(int x, in out int y, int weapon) + { + double trans = weapon == currentstats.weaponselect? 0.85 : 0.4; + + TextureID picnum = TexMan.CheckForTexture(currentstats.weaponicons[weapon]); + + if (picnum.isValid()) + { + // don't draw tall sprites too small. + int w, h; + [w, h] = TexMan.GetSize(picnum); + int rh; + if (w > h) rh = 8; + else + { + rh = 16; + y -= 8; + } + DrawImageToBox(picnum, x-24, y, 20, rh, trans); + y-=10; + } + } + + + virtual void DrawWeapons(int x, int y) + { + // Go through the list in reverse order of definition, because we start at the right. + for(int i = currentStats.weaponicons.Size() - 1; i >= 0; i--) + { + DrawOneWeapon(x, y, i); + } + } + + //--------------------------------------------------------------------------- + // + // Draw the Inventory + // + //--------------------------------------------------------------------------- + + virtual void DrawInventory(int x,int y) + { + int numitems = (hudwidth - 2*x) / 32; + int i; + + int item = 0;//StatusBar.ValidateInvFirst(numitems); + if(item != 0) + { + screen.DrawTexture(invgem_left, true, x-10, y, + DTA_KeepRatio, true, + DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, 0.4); + } + + int itemcount = currentstats.inventoryicons.Size(); + for(i = 0; i < itemcount;) + { + int amount = currentstats.inventoryamounts[i]; + double trans = i == currentstats.inventoryselect ? 1.0 : 0.4; + + DrawImageToBox(TexMan.CheckForTexture(currentstats.inventoryicons[i]), x, y, 19, 25, trans, true); + if (amount > 0) + { + String buffer = String.Format("%d", amount); + + int xx = amount >= 100? 18 : 22; + screen.DrawText(IndexFont, Font.CR_GOLD, x+xx, y+20, buffer, + DTA_KeepRatio, true, + DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, trans); + } + + x+=32; + i++; + if (item + i == numitems) break; + } + if(i < itemcount) + { + screen.DrawTexture(invgem_right, true, x-10, y, + DTA_KeepRatio, true, + DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight, DTA_Alpha, 0.4); + } + } + + //--------------------------------------------------------------------------- + // + // PROC DrawCoordinates + // + //--------------------------------------------------------------------------- + + void DrawCoordinateEntry(int xpos, int ypos, String coordstr) + { + screen.DrawText(StatFont, hudcolor_xyco, xpos, ypos, coordstr, + DTA_KeepRatio, true, + DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight); + } + + virtual void DrawCoordinates(bool withmapname) + { + /* todo when everything else works. + Vector3 pos = (0, 0, 0); + String coordstr; + int h = StatFont.GetHeight(); + + + int xpos = hudwidth - StatFont.StringWidth("X: -00000")-6; + int ypos = 18; + + if (withmapname) + { + let font = generic_ui? NewSmallFont : StatFont.CanPrint(Level.LevelName)? StatFont : OriginalSmallFont; + int hh = font.GetHeight(); + + screen.DrawText(font, hudcolor_titl, hudwidth - 6 - font.StringWidth(Level.MapName), ypos, Level.MapName, + DTA_KeepRatio, true, + DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight); + + screen.DrawText(font, hudcolor_titl, hudwidth - 6 - font.StringWidth(Level.LevelName), ypos + hh, Level.LevelName, + DTA_KeepRatio, true, + DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight); + + ypos += 2 * hh + h; + } + + DrawCoordinateEntry(xpos, ypos, String.Format("X: %.0f", pos.X)); + ypos += h; + DrawCoordinateEntry(xpos, ypos, String.Format("Y: %.0f", pos.Y)); + ypos += h; + DrawCoordinateEntry(xpos, ypos, String.Format("Z: %.0f", pos.Z)); + ypos += h; + + if (hud_showangles) + { + DrawCoordinateEntry(xpos, ypos, String.Format("Y: %.0f", Actor.Normalize180(mo.Angle))); + ypos += h; + DrawCoordinateEntry(xpos, ypos, String.Format("P: %.0f", Actor.Normalize180(mo.Pitch))); + ypos += h; + DrawCoordinateEntry(xpos, ypos, String.Format("R: %.0f", Actor.Normalize180(mo.Roll))); + } + */ + } + + //--------------------------------------------------------------------------- + // + // main drawer + // + //--------------------------------------------------------------------------- + + virtual void DrawInGame(SummaryInfo summary) + { + // No HUD in the title level! + if (gamestate == GS_TITLELEVEL) return; + + DrawStatus(summary, 5, hudheight-50); + DrawHealth(5, hudheight - 45); + DrawArmor(5, hudheight-20); + + int y = DrawKeys(hudwidth-4, hudheight-10); + y = DrawAmmo(hudwidth-5, y); + if (hud_showweapons) DrawWeapons(hudwidth - 5, y); + DrawInventory(200, hudheight - 28); + } + + //--------------------------------------------------------------------------- + // + // automap drawer + // + //--------------------------------------------------------------------------- + + virtual void DrawAutomap(SummaryInfo summary) + { + let font = generic_ui? NewSmallFont : StatFont; + + int fonth = font.GetHeight() + 1; + int bottom = hudheight - 1; + +/* + if (am_showtotaltime) + { + DrawTimeString(font, hudcolor_ttim, curentstats.totaltime / 1000, hudwidth-2, bottom, 1); + bottom -= fonth; + } +*/ + + if (am_showtime) + { + let seconds = summary.time / 1000; + DrawTimeString(font, hudcolor_ltim, seconds, hudwidth-2, bottom, 1); + } + let lev = currentlevel; + let amstr = String.Format("%s: \034%c%s", lev.GetLabelName(), hudcolor_titl + 65, lev.DisplayName()); + + font = generic_ui? NewSmallFont : StatFont.CanPrint(amstr)? StatFont : OriginalSmallFont; + + screen.DrawText(font, Font.CR_BRICK, 2, hudheight - fonth - 1, amstr, + DTA_KeepRatio, true, + DTA_VirtualWidth, hudwidth, DTA_VirtualHeight, hudheight); + } + + //--------------------------------------------------------------------------- + // + // main drawer + // + //--------------------------------------------------------------------------- + + virtual void Draw(RazeStatusBar stbar, SummaryInfo summary, int w, int h) + { + hudwidth = w; + hudheight = h; + stbar.GetAllStats(currentStats); + if (automapMode != am_full) + { + DrawInGame(summary); + } + else + { + DrawAutomap(summary); + } + } + +} diff --git a/wadsrc/static/zscript/games/blood/bloodgame.zs b/wadsrc/static/zscript/games/blood/bloodgame.zs index 9d34c3bbf..da7fbe400 100644 --- a/wadsrc/static/zscript/games/blood/bloodgame.zs +++ b/wadsrc/static/zscript/games/blood/bloodgame.zs @@ -23,6 +23,26 @@ struct Blood native kMaxPowerUps = 51, }; + enum EWeapon + { + kWeapNone = 0, + kWeapPitchFork = 1, + kWeapFlareGun = 2, + kWeapShotgun = 3, + kWeapTommyGun = 4, + kWeapNapalm = 5, + kWeapDynamite = 6, + kWeapSpraycan = 7, + kWeapTeslaCannon = 8, + kWeapLifeLeech = 9, + kWeapVoodooDoll = 10, + kWeapProximity = 11, + kWeapRemote = 12, + kWeapBeast = 13, + kWeapMax = 14, + }; + + native static void PlayIntroMusic(); native static bool OriginalLoadScreen(); // doing it generically would necessitate exporting the tile manage which we do not want. native static void sndStartSample(int resid, int volume, int channel, bool loop = false, int chanflags = 0); diff --git a/wadsrc/static/zscript/games/blood/ui/sbar.zs b/wadsrc/static/zscript/games/blood/ui/sbar.zs index 085f2a0ff..654ca702f 100644 --- a/wadsrc/static/zscript/games/blood/ui/sbar.zs +++ b/wadsrc/static/zscript/games/blood/ui/sbar.zs @@ -24,12 +24,15 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. class BloodStatusBar : RazeStatusBar { + static const String gAmmoIcons[] = { "", "AmmoIcon1", "AmmoIcon2", "AmmoIcon3", "AmmoIcon4", "AmmoIcon5", "AmmoIcon6", "AmmoIcon7", "AmmoIcon8", "AmmoIcon9", "AmmoIcon10", "AmmoIcon11" }; static const String gPackIcons[] = { "PackIcon1", "PackIcon2", "PackIcon3", "PackIcon4", "PackIcon5" }; + static const String packIcons2[] = { "Pack2Icon1", "Pack2Icon2", "Pack2Icon3", "Pack2Icon4", "Pack2Icon5" }; HUDFont smallf, tinyf; int team_score[2], team_ticker[2]; // placeholders for MP display bool gBlueFlagDropped, gRedFlagDropped; // also placeholders until we know where MP will go. + override void Init() { smallf = HUDFont.Create(SmallFont, 0, Mono_Off, 0, 0); @@ -282,7 +285,6 @@ class BloodStatusBar : RazeStatusBar void DrawPackItemInStatusBar2(BloodPlayer pPlayer, int x, int y, int x2, int y2, double nScale) { - static const String packIcons2[] = { "Pack2Icon1", "Pack2Icon2", "Pack2Icon3", "Pack2Icon4", "Pack2Icon5" }; static const float packScale[] = { 0.5f, 0.3f, 0.6f, 0.5f, 0.4f }; static const int packYoffs[] = { 0, 0, 0, -4, 0 }; @@ -630,9 +632,6 @@ class BloodStatusBar : RazeStatusBar int DrawHUD2(BloodPlayer pPlayer) { - static const String ammoIcons[] = { "", "AmmoIcon1", "AmmoIcon2", "AmmoIcon3", "AmmoIcon4", "AmmoIcon5", "AmmoIcon6", - "AmmoIcon7", "AmmoIcon8", "AmmoIcon9", "AmmoIcon10", "AmmoIcon11" }; - static const float ammoScale[] = { 0, 0.5f, 0.8f, 0.7f, 0.5f, 0.7f, 0.5f, 0.3f, 0.3f, 0.6f, 0.5f, 0.45f }; static const int ammoYoffs[] = { 0, 0, 0, 3, -6, 2, 4, -6, -6, -6, 2, 2 }; @@ -663,10 +662,10 @@ class BloodStatusBar : RazeStatusBar int num = pPlayer.ammoCount[pPlayer.weaponAmmo]; if (pPlayer.weaponAmmo == 6) num /= 10; - if (ammoIcons[pPlayer.weaponAmmo]) + if (gAmmoIcons[pPlayer.weaponAmmo]) { let scale = ammoScale[pPlayer.weaponAmmo]; - DrawImage(ammoIcons[pPlayer.weaponAmmo], (304 - 320, -8 + ammoYoffs[pPlayer.weaponAmmo]), DI_ITEM_RELCENTER, scale:(scale, scale)); + DrawImage(gAmmoIcons[pPlayer.weaponAmmo], (304 - 320, -8 + ammoYoffs[pPlayer.weaponAmmo]), DI_ITEM_RELCENTER, scale:(scale, scale)); } bool reloadableWeapon = pPlayer.curWeapon == 3 && !pPlayer.powerupCheck(Blood.kPwUpTwoGuns); @@ -684,12 +683,14 @@ class BloodStatusBar : RazeStatusBar } } - for (int i = 0; i < 6; i++) + int k7 = pPlayer.hasKey[7]? -10 : 0; + + for (int i = 0; i < 7; i++) { if (pPlayer.hasKey[i + 1]) { let tile = String.Format("HUDKEYICON%d", i + 1); - DrawImage(tile, (-60 + 10 * i, 170 - 200), DI_ITEM_RELCENTER, scale:(0.25, 0.25)); + DrawImage(tile, (-60 + 10 * i + k7, 170 - 200), DI_ITEM_RELCENTER, scale:(0.25, 0.25)); } } @@ -752,5 +753,94 @@ class BloodStatusBar : RazeStatusBar drawMultiHUD(pPlayer, nGameType); } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + override void GetAllStats(HudStats stats) + { + stats.Clear(); + stats.info.fontscale = 1.; + stats.info.screenbottomspace = 200; + stats.info.letterColor = TEXTCOLOR_DARKRED; + stats.info.standardColor = TEXTCOLOR_DARKGRAY; + stats.info.statfont = SmallFont; + stats.info.completeColor = TEXTCOLOR_DARKGREEN; + stats.info.spacing = SmallFont.GetHeight() + 2; + + let pPlayer = Blood.GetViewPlayer(); + stats.healthicon = "HealthIcon"; + stats.healthvalue = pPlayer.GetHealth() >> 4; + + if (pPlayer.armor[1]) + { + stats.armoricons.Push("Armor1Icon"); + stats.armorvalues.Push(pPlayer.armor[1] >> 4); + } + if (pPlayer.armor[0]) + { + stats.armoricons.Push("Armor3Icon"); + stats.armorvalues.Push(pPlayer.armor[0] >> 4); + } + if (pPlayer.armor[2]) + { + stats.armoricons.Push("Armor2Icon"); + stats.armorvalues.Push(pPlayer.armor[2] >> 4); + } + + for (int i = 0; i < 7; i++) + { + if (pPlayer.hasKey[i + 1]) + { + stats.keyicons.Push(String.Format("HUDKEYICON%d", i + 1)); + } + } + + for (int i = 0; i < 5; i++) + { + if (i == pPlayer.packItemId) stats.inventoryselect = stats.inventoryicons.Size(); + stats.inventoryicons.Push(packIcons2[i]); + stats.inventoryamounts.Push(pPlayer.packSlots[i].curAmount); + } + + // only show those weapons which are not their same ammo. + static const String weaponIcons[] = { "", "" /* pitchfork */, "#00524", "#00559", "#00558", "", "", "#00539", "#00800", "", "", "" }; + + for(int i = 0; i < 11; i++) + { + int weaponnum = i + 1; + if (pPlayer.hasweapon[weaponnum] && weaponIcons[weaponnum] != "") + { + if (pPlayer.curweapon == weaponnum) + { + stats.weaponselect = stats.weaponicons.Size(); + } + stats.weaponicons.Push(weaponIcons[weaponnum]); + } + } + + static const int ammoOrder[] = { 1, 2, 3, 4, 5, 10, 11, 6, 7, 8, 9 }; + static const int maxammocount[] = { 0, 100, 100, 500, 100, 50, 288, 250, 100, 100, 50, 50 }; + + for(int i = 0; i < 11; i++) + { + int ammonum = ammoorder[i]; + if (pPlayer.weaponammo == ammonum) + { + stats.ammoselect = stats.ammoicons.Size(); + } + stats.ammoicons.Push(gAmmoIcons[ammonum]); + int num = pPlayer.ammoCount[ammonum]; + if (ammonum == 6) + num /= 10; + stats.ammovalues.Push(num); + stats.ammomaxvalues.Push(maxammocount[ammonum]); + } + } + + } diff --git a/wadsrc/static/zscript/games/duke/ui/sbar.zs b/wadsrc/static/zscript/games/duke/ui/sbar.zs index 3825c7838..abac147fd 100644 --- a/wadsrc/static/zscript/games/duke/ui/sbar.zs +++ b/wadsrc/static/zscript/games/duke/ui/sbar.zs @@ -207,4 +207,5 @@ class DukeCommonStatusBar : RazeStatusBar } } + } \ No newline at end of file diff --git a/wadsrc/static/zscript/games/duke/ui/sbar_d.zs b/wadsrc/static/zscript/games/duke/ui/sbar_d.zs index 47ff61d19..b28a0ba64 100644 --- a/wadsrc/static/zscript/games/duke/ui/sbar_d.zs +++ b/wadsrc/static/zscript/games/duke/ui/sbar_d.zs @@ -306,7 +306,7 @@ class DukeStatusBar : DukeCommonStatusBar FullscreenHUD2(p); DoLevelStats(tileHeight("HEALTHBOX") + 4, info); } - else + else { DrawInventory(p, 0, -28, DI_SCREEN_CENTER_BOTTOM); DoLevelStats(2, info); diff --git a/wadsrc/static/zscript/statusbar.zs b/wadsrc/static/zscript/statusbar.zs index 4085f00e2..30bd2e26b 100644 --- a/wadsrc/static/zscript/statusbar.zs +++ b/wadsrc/static/zscript/statusbar.zs @@ -220,4 +220,15 @@ class RazeStatusBar : StatusBarCore int left = (screen.GetWidth() - width) / 2; screen.SetClipRect(left, 0, width, screen.GetHeight()); } + + //============================================================================ + // + // + // + //============================================================================ + + virtual void GetAllStats(HudStats stats) + { + stats.Clear(); + } }