- added a GZDoom-style alternative HUD.

So far only implemented for Blood.
This commit is contained in:
Christoph Oelckers 2022-10-15 23:13:47 +02:00
parent a0f18c360a
commit ff71ab14ce
19 changed files with 1027 additions and 10 deletions

View file

@ -1003,6 +1003,7 @@ set (PCH_SOURCES
core/palette.cpp core/palette.cpp
core/zcompile.cpp core/zcompile.cpp
core/statusbar2.cpp core/statusbar2.cpp
core/shared_hud.cpp
core/gi.cpp core/gi.cpp
core/defparser.cpp core/defparser.cpp

View file

@ -9,3 +9,4 @@ xx(CreditsMenu)
xx(MultiMenu) xx(MultiMenu)
xx(RazeStatusBar) xx(RazeStatusBar)
xx(zoomsize) xx(zoomsize)
xx(AltHud)

197
source/core/shared_hud.cpp Normal file
View file

@ -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 <time.h>
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);
}
}

View file

@ -73,6 +73,7 @@ EXTERN_CVAR (Bool, am_showtotaltime)
EXTERN_CVAR (Bool, noisedebug) EXTERN_CVAR (Bool, noisedebug)
EXTERN_CVAR(Bool, vid_fps) EXTERN_CVAR(Bool, vid_fps)
EXTERN_CVAR(Bool, inter_subtitles) EXTERN_CVAR(Bool, inter_subtitles)
EXTERN_CVAR(Bool, hud_althud)
extern int setblocks; extern int setblocks;
@ -181,8 +182,15 @@ void drawMapTitle()
} }
} }
void DrawAltHUD(SummaryInfo* info);
void UpdateStatusBar(SummaryInfo* info) void UpdateStatusBar(SummaryInfo* info)
{ {
if (hud_althud)
{
DrawAltHUD(info);
return;
}
IFVIRTUALPTRNAME(StatusBar, NAME_RazeStatusBar, UpdateStatusBar) IFVIRTUALPTRNAME(StatusBar, NAME_RazeStatusBar, UpdateStatusBar)
{ {
VMValue params[] = { StatusBar, info }; VMValue params[] = { StatusBar, info };

View file

@ -147,7 +147,7 @@ void SetArmor(PLAYER* pPlayer, bool stat)
void SetKeys(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; pPlayer->hasKey[i] = stat;
if (stat) if (stat)
viewSetMessage(GStrings("TXTB_ALLKEYS")); viewSetMessage(GStrings("TXTB_ALLKEYS"));

View file

@ -382,3 +382,16 @@ digifont
"-}" #04573 "-}" #04573
} }
hudfont_blood
{
0 #09220
1 #09221
2 #09222
3 #09223
4 #09224
5 #09225
6 #09226
7 #09227
8 #09228
9 #09229
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 B

View file

@ -0,0 +1,2 @@
CellSize 4, 6 // This implies font sheets

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

View file

@ -35,6 +35,7 @@ version "4.9"
#include "zscript/usermapmenu.zs" #include "zscript/usermapmenu.zs"
#include "zscript/maptypes.zs" #include "zscript/maptypes.zs"
#include "zscript/coreactor.zs" #include "zscript/coreactor.zs"
#include "zscript/alt_hud.zs"
#include "zscript/games/duke/dukeactor.zs" #include "zscript/games/duke/dukeactor.zs"
#include "zscript/games/blood/bloodactor.zs" #include "zscript/games/blood/bloodactor.zs"
#include "zscript/games/sw/swactor.zs" #include "zscript/games/sw/swactor.zs"

View file

@ -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<String> armoricons;
Array<int> armortranslations;
Array<int> armorvalues;
Array<String> weaponicons;
Array<int> weapontranslations;
int weaponselect;
Array<String> ammoicons;
Array<int> ammotranslations;
Array<int> ammovalues;
Array<int> ammomaxvalues;
Array<int> ammoaltvalues;
int ammoselect;
Array<String> keyicons;
Array<int> keytranslations;
Array<String> inventoryicons;
Array<int> 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);
}
}
}

View file

@ -23,6 +23,26 @@ struct Blood native
kMaxPowerUps = 51, 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 void PlayIntroMusic();
native static bool OriginalLoadScreen(); // doing it generically would necessitate exporting the tile manage which we do not want. 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); native static void sndStartSample(int resid, int volume, int channel, bool loop = false, int chanflags = 0);

View file

@ -24,12 +24,15 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
class BloodStatusBar : RazeStatusBar 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 gPackIcons[] = { "PackIcon1", "PackIcon2", "PackIcon3", "PackIcon4", "PackIcon5" };
static const String packIcons2[] = { "Pack2Icon1", "Pack2Icon2", "Pack2Icon3", "Pack2Icon4", "Pack2Icon5" };
HUDFont smallf, tinyf; HUDFont smallf, tinyf;
int team_score[2], team_ticker[2]; // placeholders for MP display int team_score[2], team_ticker[2]; // placeholders for MP display
bool gBlueFlagDropped, gRedFlagDropped; // also placeholders until we know where MP will go. bool gBlueFlagDropped, gRedFlagDropped; // also placeholders until we know where MP will go.
override void Init() override void Init()
{ {
smallf = HUDFont.Create(SmallFont, 0, Mono_Off, 0, 0); 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) 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 float packScale[] = { 0.5f, 0.3f, 0.6f, 0.5f, 0.4f };
static const int packYoffs[] = { 0, 0, 0, -4, 0 }; static const int packYoffs[] = { 0, 0, 0, -4, 0 };
@ -630,9 +632,6 @@ class BloodStatusBar : RazeStatusBar
int DrawHUD2(BloodPlayer pPlayer) 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 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 }; 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]; int num = pPlayer.ammoCount[pPlayer.weaponAmmo];
if (pPlayer.weaponAmmo == 6) if (pPlayer.weaponAmmo == 6)
num /= 10; num /= 10;
if (ammoIcons[pPlayer.weaponAmmo]) if (gAmmoIcons[pPlayer.weaponAmmo])
{ {
let scale = ammoScale[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); 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]) if (pPlayer.hasKey[i + 1])
{ {
let tile = String.Format("HUDKEYICON%d", 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); 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]);
}
}
} }

View file

@ -207,4 +207,5 @@ class DukeCommonStatusBar : RazeStatusBar
} }
} }
} }

View file

@ -306,7 +306,7 @@ class DukeStatusBar : DukeCommonStatusBar
FullscreenHUD2(p); FullscreenHUD2(p);
DoLevelStats(tileHeight("HEALTHBOX") + 4, info); DoLevelStats(tileHeight("HEALTHBOX") + 4, info);
} }
else else
{ {
DrawInventory(p, 0, -28, DI_SCREEN_CENTER_BOTTOM); DrawInventory(p, 0, -28, DI_SCREEN_CENTER_BOTTOM);
DoLevelStats(2, info); DoLevelStats(2, info);

View file

@ -220,4 +220,15 @@ class RazeStatusBar : StatusBarCore
int left = (screen.GetWidth() - width) / 2; int left = (screen.GetWidth() - width) / 2;
screen.SetClipRect(left, 0, width, screen.GetHeight()); screen.SetClipRect(left, 0, width, screen.GetHeight());
} }
//============================================================================
//
//
//
//============================================================================
virtual void GetAllStats(HudStats stats)
{
stats.Clear();
}
} }