gzdoom/wadsrc/static/zscript/ui/statusbar/strife_sbar.zs
Kevin Caccamo b6f0c4071d
Fix the health bar on Strife status bar (#1080)
* Fix the health bar on Strife status bar

Now, if the player's health is above 100, the green health bar won't be shortened any more.

* Fix the bar properly

Now, the blue and green bars don't overlap. Also, health above 100 goes from right to left, like in Strife: Veteran Edition.
2020-08-08 01:26:59 -04:00

603 lines
14 KiB
Text

class StrifeStatusBar : BaseStatusBar
{
// Number of tics to move the popscreen up and down.
const POP_TIME = (Thinker.TICRATE/8);
// Popscreen height when fully extended
const POP_HEIGHT = 104;
// Number of tics to scroll keys left
const KEY_TIME = (Thinker.TICRATE/3);
enum eImg
{
imgINVCURS,
imgCURSOR01,
imgINVPOP,
imgINVPOP2,
imgINVPBAK,
imgINVPBAK2,
imgFONY0,
imgFONY1,
imgFONY2,
imgFONY3,
imgFONY4,
imgFONY5,
imgFONY6,
imgFONY7,
imgFONY8,
imgFONY9,
imgFONY_PERCENT,
imgNEGATIVE,
};
TextureID Images[imgNEGATIVE + 1];
int CursorImage;
int CurrentPop, PendingPop, PopHeight, PopHeightChange;
int KeyPopPos, KeyPopScroll;
HUDFont mYelFont, mGrnFont, mBigFont;
override void Init()
{
static const Name strifeLumpNames[] =
{
"INVCURS", "CURSOR01", "INVPOP", "INVPOP2",
"INVPBAK", "INVPBAK2",
"INVFONY0", "INVFONY1", "INVFONY2", "INVFONY3", "INVFONY4",
"INVFONY5", "INVFONY6", "INVFONY7", "INVFONY8", "INVFONY9",
"INVFONY%", ""
};
Super.Init();
SetSize(32, 320, 200);
Reset();
for(int i = 0; i <= imgNEGATIVE; i++)
{
Images[i] = TexMan.CheckForTexture(strifeLumpNames[i], TexMan.TYPE_MiscPatch);
}
CursorImage = Images[imgINVCURS].IsValid() ? imgINVCURS : imgCURSOR01;
mYelFont = HUDFont.Create("Indexfont_Strife_Yellow", 7, Mono_CellLeft, 1, 1);
mGrnFont = HUDFont.Create("Indexfont_Strife_Green", 7, Mono_CellLeft, 1, 1);
mBigFont = HUDFont.Create("BigFont", 0, Mono_Off, 2, 2);
}
override void NewGame ()
{
Super.NewGame();
Reset ();
}
override int GetProtrusion(double scaleratio) const
{
return 10;
}
override void Draw (int state, double TicFrac)
{
Super.Draw (state, TicFrac);
if (state == HUD_StatusBar)
{
BeginStatusBar();
DrawMainBar (TicFrac);
}
else
{
if (state == HUD_Fullscreen)
{
BeginHUD();
DrawFullScreenStuff ();
}
// Draw pop screen (log, keys, and status)
if (CurrentPop != POP_None && PopHeight < 0)
{
// This uses direct low level draw commands and would otherwise require calling
// BeginStatusBar(true);
DrawPopScreen (screen.GetHeight(), TicFrac);
}
}
}
override void ShowPop (int popnum)
{
Super.ShowPop(popnum);
if (popnum == CurrentPop || (popnum == POP_LOG && MustDrawLog(0)))
{
if (popnum == POP_Keys)
{
Inventory item;
KeyPopPos += 10;
KeyPopScroll = 280;
int i = 0;
for (item = CPlayer.mo.Inv; item != NULL; item = item.Inv)
{
if (item is "Key")
{
if (i == KeyPopPos)
{
return;
}
i++;
}
}
}
PendingPop = POP_None;
// Do not scroll keys horizontally when dropping the popscreen
KeyPopScroll = 0;
KeyPopPos -= 10;
}
else
{
KeyPopPos = 0;
PendingPop = popnum;
}
}
override bool MustDrawLog(int state)
{
// Tell the base class to draw the log if the pop screen won't be displayed.
return generic_ui || log_vgafont;
}
void Reset ()
{
CurrentPop = POP_None;
PendingPop = POP_NoChange;
PopHeight = 0;
KeyPopPos = 0;
KeyPopScroll = 0;
}
override void Tick ()
{
Super.Tick ();
PopHeightChange = 0;
if (PendingPop != POP_NoChange)
{
if (PopHeight < 0)
{
PopHeightChange = POP_HEIGHT / POP_TIME;
PopHeight += POP_HEIGHT / POP_TIME;
}
else
{
CurrentPop = PendingPop;
PendingPop = POP_NoChange;
}
}
else
{
if (CurrentPop == POP_None)
{
PopHeight = 0;
}
else if (PopHeight > -POP_HEIGHT)
{
PopHeight -= POP_HEIGHT / POP_TIME;
if (PopHeight < -POP_HEIGHT)
{
PopHeight = -POP_HEIGHT;
}
else
{
PopHeightChange = -POP_HEIGHT / POP_TIME;
}
}
if (KeyPopScroll > 0)
{
KeyPopScroll -= 280 / KEY_TIME;
if (KeyPopScroll < 0)
{
KeyPopScroll = 0;
}
}
}
}
private void FillBar(double x, double y, double start, double stopp, Color color1, Color color2)
{
Fill(color1, x, y, (stopp-start)*2, 1);
Fill(color2, x, y+1, (stopp-start)*2, 1);
}
protected void DrawHealthBar(int health, int x, int y)
{
Color green1 = Color(255, 180, 228, 128); // light green
Color green2 = Color(255, 128, 180, 80); // dark green
Color blue1 = Color(255, 196, 204, 252); // light blue
Color blue2 = Color(255, 148, 152, 200); // dark blue
Color gold1 = Color(255, 224, 188, 0); // light gold
Color gold2 = Color(255, 208, 128, 0); // dark gold
Color red1 = Color(255, 216, 44, 44); // light red
Color red2 = Color(255, 172, 28, 28); // dark red
if (health == 999)
{
FillBar (x, y, 0, 100, gold1, gold2);
}
else
{
if (health <= 100)
{
if (health <= 10)
{
FillBar (x, y, 0, health, red1, red2);
}
else if (health <= 20)
{
FillBar (x, y, 0, health, gold1, gold2);
}
else
{
FillBar (x, y, 0, health, green1, green2);
}
//FillBar (x, y, health, 100, 0, 0);
}
else
{
int stopp = 200 - health;
FillBar (x, y, 0, stopp, green1, green2);
FillBar (x + stopp * 2, y, stopp, 100, blue1, blue2);
}
}
}
protected void DrawMainBar (double TicFrac)
{
Inventory item;
int i;
// Pop screen (log, keys, and status)
if (CurrentPop != POP_None && PopHeight < 0)
{
double tmp, h;
[tmp, tmp, h] = StatusbarToRealCoords(0, 0, 8);
DrawPopScreen (int(GetTopOfStatusBar() - h), TicFrac);
}
DrawImage("INVBACK", (0, 168), DI_ITEM_OFFSETS);
DrawImage("INVTOP", (0, 160), DI_ITEM_OFFSETS);
// Health
DrawString(mGrnFont, FormatNumber(CPlayer.health, 3, 5), (79, 162), DI_TEXT_ALIGN_RIGHT);
int points;
if (CPlayer.cheats & CF_GODMODE)
{
points = 999;
}
else
{
points = min(CPlayer.health, 200);
}
DrawHealthBar (points, 49, 172);
DrawHealthBar (points, 49, 175);
// Armor
item = CPlayer.mo.FindInventory('BasicArmor');
if (item != NULL && item.Amount > 0)
{
DrawInventoryIcon(item, (2, 177), DI_ITEM_OFFSETS);
DrawString(mYelFont, FormatNumber(item.Amount, 3, 5), (27, 191), DI_TEXT_ALIGN_RIGHT);
}
// Ammo
Inventory ammo1 = GetCurrentAmmo ();
if (ammo1 != NULL)
{
DrawString(mGrnFont, FormatNumber(ammo1.Amount, 3, 5), (311, 162), DI_TEXT_ALIGN_RIGHT);
DrawInventoryIcon (ammo1, (290, 181), DI_ITEM_OFFSETS);
}
// Sigil
item = CPlayer.mo.FindInventory('Sigil');
if (item != NULL)
{
DrawInventoryIcon (item, (253, 175), DI_ITEM_OFFSETS);
}
// Inventory
CPlayer.inventorytics = 0;
CPlayer.mo.InvFirst = ValidateInvFirst (6);
i = 0;
for (item = CPlayer.mo.InvFirst; item != NULL && i < 6; item = item.NextInv())
{
int flags = item.Amount <= 0? DI_ITEM_OFFSETS|DI_DIM : DI_ITEM_OFFSETS;
if (item == CPlayer.mo.InvSel)
{
DrawTexture (Images[CursorImage], (42 + TICRATE*i, 180), flags, 1. - itemflashFade);
}
DrawInventoryIcon (item, (48 + TICRATE*i, 182), flags);
DrawString(mYelFont, FormatNumber(item.Amount, 3, 5), (75 + TICRATE*i, 191), DI_TEXT_ALIGN_RIGHT);
i++;
}
}
protected void DrawFullScreenStuff ()
{
// Draw health (use red color if health is below the run health threashold.)
DrawString(mGrnFont, FormatNumber(CPlayer.health, 3), (4, -10), DI_TEXT_ALIGN_LEFT, (CPlayer.health < CPlayer.mo.RunHealth)? Font.CR_BRICK : Font.CR_UNTRANSLATED);
DrawImage("I_MDKT", (14, -17));
// Draw armor
let armor = CPlayer.mo.FindInventory('BasicArmor');
if (armor != NULL && armor.Amount != 0)
{
DrawString(mYelFont, FormatNumber(armor.Amount, 3), (35, -10));
DrawInventoryIcon(armor, (45, -17));
}
// Draw ammo
Inventory ammo1, ammo2;
int ammocount1, ammocount2;
[ammo1, ammo2, ammocount1, ammocount2] = GetCurrentAmmo ();
if (ammo1 != NULL)
{
// Draw primary ammo in the bottom-right corner
DrawString(mGrnFont, FormatNumber(ammo1.Amount, 3), (-23, -10));
DrawInventoryIcon(ammo1, (-14, -17));
if (ammo2 != NULL && ammo1!=ammo2)
{
// Draw secondary ammo just above the primary ammo
DrawString(mGrnFont, FormatNumber(ammo2.Amount, 3), (-23, -48));
DrawInventoryIcon(ammo2, (-14, -55));
}
}
if (deathmatch)
{ // Draw frags (in DM)
DrawString(mBigFont, FormatNumber(CPlayer.FragCount, 3), (4, 1));
}
// Draw inventory
if (CPlayer.inventorytics == 0)
{
if (CPlayer.mo.InvSel != null)
{
if (itemflashFade > 0)
{
DrawTexture(Images[CursorImage], (-42, -15));
}
DrawString(mYelFont, FormatNumber(CPlayer.mo.InvSel.Amount, 3, 5), (-30, -10), DI_TEXT_ALIGN_RIGHT);
DrawInventoryIcon(CPlayer.mo.InvSel, (-42, -17), DI_DIMDEPLETED);
}
}
else
{
CPlayer.mo.InvFirst = ValidateInvFirst (6);
int i = 0;
Inventory item;
Vector2 box = TexMan.GetScaledSize(Images[CursorImage]) - (4, 4); // Fit oversized icons into the box.
if (CPlayer.mo.InvFirst != NULL)
{
for (item = CPlayer.mo.InvFirst; item != NULL && i < 6; item = item.NextInv())
{
if (item == CPlayer.mo.InvSel)
{
DrawTexture(Images[CursorImage], (-90+i*TICRATE, -3), DI_SCREEN_CENTER_BOTTOM, 0.75);
}
if (item.Icon.isValid())
{
DrawInventoryIcon(item, (-90+i*TICRATE, -5), DI_SCREEN_CENTER_BOTTOM|DI_DIMDEPLETED, 0.75);
}
DrawString(mYelFont, FormatNumber(item.Amount, 3, 5), (-72 + i*TICRATE, -8), DI_TEXT_ALIGN_RIGHT|DI_SCREEN_CENTER_BOTTOM);
++i;
}
}
}
}
protected void DrawPopScreen (int bottom, double TicFrac)
{
String buff;
String label;
int i;
Inventory item;
int xscale, yscale, left, top;
int bars = (CurrentPop == POP_Status) ? imgINVPOP : imgINVPOP2;
int back = (CurrentPop == POP_Status) ? imgINVPBAK : imgINVPBAK2;
// Extrapolate the height of the popscreen for smoother movement
int height = clamp (PopHeight + int(TicFrac * PopHeightChange), -POP_HEIGHT, 0);
xscale = CleanXfac;
yscale = CleanYfac;
left = screen.GetWidth()/2 - 160*CleanXfac;
top = bottom + height * yscale;
screen.DrawTexture (Images[back], true, left, top, DTA_CleanNoMove, true, DTA_Alpha, 0.75);
screen.DrawTexture (Images[bars], true, left, top, DTA_CleanNoMove, true);
switch (CurrentPop)
{
case POP_Log:
{
// Draw the latest log message.
screen.DrawText(SmallFont2, Font.CR_UNTRANSLATED, left + 210 * xscale, top + 8 * yscale, Level.TimeFormatted(),
DTA_CleanNoMove, true);
if (CPlayer.LogText.Length() > 0)
{
let text = Stringtable.Localize(CPlayer.LogText);
BrokenLines lines = SmallFont2.BreakLines(text, 272);
for (i = 0; i < lines.Count(); ++i)
{
screen.DrawText(SmallFont2, Font.CR_UNTRANSLATED, left + 24 * xscale, top + (18 + i * 12)*yscale,
lines.StringAt(i), DTA_CleanNoMove, true);
}
}
break;
}
case POP_Keys:
// List the keys the player has.
int pos, endpos, leftcol;
int clipleft, clipright;
pos = KeyPopPos;
endpos = pos + 10;
leftcol = 20;
clipleft = left + 17*xscale;
clipright = left + (320-17)*xscale;
if (KeyPopScroll > 0)
{
// Extrapolate the scroll position for smoother scrolling
int scroll = MAX (0, KeyPopScroll - int(TicFrac * (280./KEY_TIME)));
pos -= 10;
leftcol = leftcol - 280 + scroll;
}
i = 0;
for (item = CPlayer.mo.Inv; i < endpos && item != NULL; item = item.Inv)
{
if (!(item is "Key"))
continue;
if (i < pos)
{
i++;
continue;
}
label = item.GetTag();
int colnum = ((i-pos) / 5) & (KeyPopScroll > 0 ? 3 : 1);
int rownum = (i % 5) * 18;
screen.DrawTexture (item.Icon, true,
left + (colnum * 140 + leftcol)*xscale,
top + (6 + rownum)*yscale,
DTA_CleanNoMove, true,
DTA_ClipLeft, clipleft,
DTA_ClipRight, clipright);
screen.DrawText (SmallFont2, Font.CR_UNTRANSLATED,
left + (colnum * 140 + leftcol + 17)*xscale,
top + (11 + rownum)*yscale,
label,
DTA_CleanNoMove, true,
DTA_ClipLeft, clipleft,
DTA_ClipRight, clipright);
i++;
}
break;
case POP_Status:
// Show miscellaneous status items.
// Print stats
DrINumber2 (CPlayer.mo.accuracy, left+268*xscale, top+28*yscale, 7*xscale, imgFONY0);
DrINumber2 (CPlayer.mo.stamina, left+268*xscale, top+52*yscale, 7*xscale, imgFONY0);
// How many keys does the player have?
i = 0;
for (item = CPlayer.mo.Inv; item != NULL; item = item.Inv)
{
if (item is "Key")
{
i++;
}
}
DrINumber2 (i, left+268*xscale, top+76*yscale, 7*xscale, imgFONY0);
// Does the player have a communicator?
item = CPlayer.mo.FindInventory ("Communicator");
if (item != NULL)
{
screen.DrawTexture (item.Icon, true,
left + 280*xscale,
top + 74*yscale,
DTA_CleanNoMove, true);
}
// How much ammo does the player have?
static const class<Ammo> AmmoList[] = {
"ClipOfBullets",
"PoisonBolts",
"ElectricBolts",
"HEGrenadeRounds",
"PhosphorusGrenadeRounds",
"MiniMissiles",
"EnergyPod"};
static const int AmmoY[] = {19, 35, 43, 59, 67, 75, 83};
for (i = 0; i < 7; ++i)
{
item = CPlayer.mo.FindInventory (AmmoList[i]);
if (item == NULL)
{
DrINumber2 (0, left+206*xscale, top+AmmoY[i] * yscale, 7*xscale, imgFONY0);
DrINumber2 (GetDefaultByType(AmmoList[i]).MaxAmount, left+239*xscale, top+AmmoY[i] * yscale, 7*xscale, imgFONY0);
}
else
{
DrINumber2 (item.Amount, left+206*xscale, top+AmmoY[i] * yscale, 7*xscale, imgFONY0);
DrINumber2 (item.MaxAmount, left+239*xscale, top+AmmoY[i] * yscale, 7*xscale, imgFONY0);
}
}
// What weapons does the player have?
static const class<Weapon> WeaponList[] =
{
"StrifeCrossbow",
"AssaultGun",
"FlameThrower",
"MiniMissileLauncher",
"StrifeGrenadeLauncher",
"Mauler"
};
static const int WeaponX[] = {23, 21, 57, 20, 55, 52};
static const int WeaponY[] = {19, 41, 50, 64, 20, 75};
for (i = 0; i < 6; ++i)
{
item = CPlayer.mo.FindInventory (WeaponList[i]);
if (item != NULL)
{
screen.DrawTexture (item.Icon, true,
left + WeaponX[i] * xscale,
top + WeaponY[i] * yscale,
DTA_CleanNoMove, true,
DTA_LeftOffset, 0,
DTA_TopOffset, 0);
}
}
break;
}
}
void DrINumber2 (int val, int x, int y, int width, int imgBase) const
{
x -= width;
if (val == 0)
{
screen.DrawTexture (Images[imgBase], true, x, y, DTA_CleanNoMove, true);
}
else
{
while (val != 0)
{
screen.DrawTexture (Images[imgBase+val%10], true, x, y, DTA_CleanNoMove, true);
val /= 10;
x -= width;
}
}
}
}