- implemented the status bar's DrawString function and ported the fullscreen HUD part of the Strife status bar to use the new functionality.

This commit is contained in:
Christoph Oelckers 2017-03-24 20:07:22 +01:00
parent ac518e23bf
commit 6760e01a0d
5 changed files with 424 additions and 229 deletions

View file

@ -1517,8 +1517,135 @@ void DBaseStatusBar::DrawString(FFont *font, const FString &cstring, double x, d
x -= static_cast<int> ((spacing)* cstring.Len()) / 2; x -= static_cast<int> ((spacing)* cstring.Len()) / 2;
break; break;
} }
const uint8_t* str = (const uint8_t*)cstring.GetChars();
const EColorRange boldTranslation = EColorRange(translation ? translation - 1 : NumTextColors - 1);
int fontcolor = translation;
double orgx = 0, orgy = 0;
if (fullscreenOffsets)
{
if (hud_scale)
{
shadowX *= (int)cleanScale.X;
shadowY *= (int)cleanScale.Y;
}
switch (screenalign & HMASK)
{
default: orgx = 0; break;
case HCENTER: orgx = screen->GetWidth() / 2; break;
case RIGHT: orgx = screen->GetWidth(); break;
}
switch (screenalign & VMASK)
{
default: orgy = 0; break;
case VCENTER: orgy = screen->GetHeight() / 2; break;
case BOTTOM: orgy = screen->GetHeight(); break;
}
if (screenalign == (RIGHT | TOP) && vid_fps) orgy += 10;
}
int ch;
while (ch = *str++, ch != '\0')
{
if (ch == ' ')
{
x += monospaced ? spacing : font->GetSpaceWidth() + spacing;
continue;
}
else if (ch == TEXTCOLOR_ESCAPE)
{
EColorRange newColor = V_ParseFontColor(str, translation, boldTranslation);
if (newColor != CR_UNDEFINED)
fontcolor = newColor;
continue;
}
int width;
FTexture* c = font->GetChar((unsigned char)ch, &width);
if (c == NULL) //missing character.
{
continue;
}
if (!monospaced) //If we are monospaced lets use the offset
x += (c->LeftOffset + 1); //ignore x offsets since we adapt to character size
double rx, ry, rw, rh;
rx = x + drawOffset.X;
ry = y + drawOffset.Y;
rw = c->GetScaledWidthDouble();
rh = c->GetScaledHeightDouble();
if (monospaced)
{
// align the character in the monospaced cell according to the general alignment to ensure that it gets positioned properly
// (i.e. right aligned text aligns to the right edge of the character and not the empty part of the cell.)
switch (align)
{
default:
break;
case ALIGN_CENTER:
rx -= (spacing) / 2;
break;
case ALIGN_RIGHT:
rx -= spacing;
break;
}
}
if (!fullscreenOffsets)
{
rx += ST_X;
ry += ST_Y - (Scaled ? VerticalResolution : 200) + RelTop;
if (Scaled)
screen->VirtualToRealCoords(rx, ry, rw, rh, HorizontalResolution, VerticalResolution, true);
else
{
ry += (200 - VerticalResolution);
}
}
else
{
if (hud_scale)
{
rx *= cleanScale.X;
ry *= cleanScale.Y;
rw *= cleanScale.X;
rh *= cleanScale.Y;
}
rx += orgx;
ry += orgy;
}
// This is not really such a great way to draw shadows because they can overlap with previously drawn characters.
// This may have to be changed to draw the shadow text up front separately.
if (shadowX != 0 || shadowY != 0)
{
screen->DrawChar(font, CR_UNTRANSLATED, rx + shadowX, ry + shadowY, ch,
DTA_DestWidthF, rw,
DTA_DestHeightF, rh,
DTA_Alpha, (Alpha * HR_SHADOW),
DTA_FillColor, 0,
TAG_DONE);
}
screen->DrawChar(font, fontcolor, rx, ry, ch,
DTA_DestWidthF, rw,
DTA_DestHeightF, rh,
DTA_Alpha, Alpha,
TAG_DONE);
if (!monospaced)
x += width + spacing - (c->LeftOffset + 1);
else
x += spacing;
}
} }
DEFINE_ACTION_FUNCTION(DBaseStatusBar, DrawString) DEFINE_ACTION_FUNCTION(DBaseStatusBar, DrawString)
{ {
PARAM_SELF_PROLOGUE(DBaseStatusBar); PARAM_SELF_PROLOGUE(DBaseStatusBar);
@ -1527,7 +1654,7 @@ DEFINE_ACTION_FUNCTION(DBaseStatusBar, DrawString)
PARAM_FLOAT(x); PARAM_FLOAT(x);
PARAM_FLOAT(y); PARAM_FLOAT(y);
PARAM_FLOAT(alpha); PARAM_FLOAT(alpha);
PARAM_BOOL(trans); PARAM_INT(trans);
PARAM_INT(ialign); PARAM_INT(ialign);
PARAM_INT(salign); PARAM_INT(salign);
PARAM_INT_DEF(spacing); PARAM_INT_DEF(spacing);
@ -1636,3 +1763,46 @@ DEFINE_ACTION_FUNCTION(DBaseStatusBar, GetGlobalACSArrayString)
PARAM_INT(index); PARAM_INT(index);
ACTION_RETURN_STRING(FBehavior::StaticLookupString(ACS_GlobalArrays[arrayno][index])); ACTION_RETURN_STRING(FBehavior::StaticLookupString(ACS_GlobalArrays[arrayno][index]));
} }
DEFINE_ACTION_FUNCTION(DBaseStatusBar, GetGlobalACSValue)
{
PARAM_PROLOGUE;
PARAM_INT(index);
ACTION_RETURN_INT(ACS_GlobalVars[index]);
}
DEFINE_ACTION_FUNCTION(DBaseStatusBar, GetGlobalACSArrayValue)
{
PARAM_PROLOGUE;
PARAM_INT(arrayno);
PARAM_INT(index);
ACTION_RETURN_INT(ACS_GlobalArrays[arrayno][index]);
}
enum ENumFlags
{
FNF_FILLZEROS,
FNF_WHENNOTZERO,
};
DEFINE_ACTION_FUNCTION(DBaseStatusBar, FormatNumber)
{
PARAM_PROLOGUE;
PARAM_INT(number);
PARAM_INT(minsize);
PARAM_INT(maxsize);
PARAM_INT(flags);
PARAM_STRING_DEF(prefix);
static int maxvals[] = { 1, 9, 99, 999, 9999, 99999, 999999, 9999999, 99999999, 999999999 };
if (number == 0 && (flags & FNF_WHENNOTZERO)) ACTION_RETURN_STRING("");
if (maxsize > 0 && maxsize < 10)
{
number = clamp(number, -maxvals[maxsize - 1], maxvals[maxsize]);
}
FString fmt;
if (minsize <= 1) fmt.Format("%s%d", prefix.GetChars(), number);
else if (flags & FNF_FILLZEROS) fmt.Format("%s%0*d", prefix.GetChars(), minsize, number);
else fmt.Format("%s%*d", prefix.GetChars(), minsize, number);
ACTION_RETURN_STRING(fmt);
}

View file

@ -490,7 +490,7 @@ FMaterial::FMaterial(FTexture * tx, bool expanded)
mSpriteU[1] = mSpriteV[1] = 1.f; mSpriteU[1] = mSpriteV[1] = 1.f;
FTexture *basetex = (tx->bWarped && gl.legacyMode)? tx : tx->GetRedirect(false); FTexture *basetex = (tx->bWarped && gl.legacyMode)? tx : tx->GetRedirect(false);
// allow the redirect only if the textute is not expanded or the scale matches. // allow the redirect only if the texture is not expanded or the scale matches.
if (!expanded || (tx->Scale.X == basetex->Scale.X && tx->Scale.Y == basetex->Scale.Y)) if (!expanded || (tx->Scale.X == basetex->Scale.X && tx->Scale.Y == basetex->Scale.Y))
{ {
mBaseLayer = ValidateSysTexture(basetex, expanded); mBaseLayer = ValidateSysTexture(basetex, expanded);

View file

@ -61,6 +61,35 @@ INDEXFONT_DOOM
9 STYSNUM9 9 STYSNUM9
} }
INDEXFONT_STRIFE_YELLOW
{
% INVFONY%
0 INVFONY0
1 INVFONY1
2 INVFONY2
3 INVFONY3
4 INVFONY4
5 INVFONY5
6 INVFONY6
7 INVFONY7
8 INVFONY8
9 INVFONY9
}
INDEXFONT_STRIFE_GREEN
{
% INVFONG%
0 INVFONG0
1 INVFONG1
2 INVFONG2
3 INVFONG3
4 INVFONG4
5 INVFONG5
6 INVFONG6
7 INVFONG7
8 INVFONG8
9 INVFONG9
}
// Doom and Chex intermissions use special text glyphs. The Raven and Strife // Doom and Chex intermissions use special text glyphs. The Raven and Strife
// games just use the standard big font. // games just use the standard big font.

View file

@ -46,19 +46,24 @@ class BaseStatusBar native ui
DI_SKIPREADY = 0x8, DI_SKIPREADY = 0x8,
DI_ALTICONFIRST = 0x10, DI_ALTICONFIRST = 0x10,
DI_TRANSLATABLE = 0x20, DI_TRANSLATABLE = 0x20,
DI_FORCESCALE = 0x40 DI_FORCESCALE = 0x40,
DI_DIM = 0x80,
}; };
enum IconType enum IconType
{ {
PLAYERICON = 1000, ITYPE_PLAYERICON = 1000,
AMMO1, ITYPE_AMMO1,
AMMO2, ITYPE_AMMO2,
ARMOR, ITYPE_ARMOR,
WEAPONICON, ITYPE_WEAPON,
SIGIL, ITYPE_SIGIL,
WEAPONSLOT, ITYPE_WEAPONSLOT,
SELECTEDINVENTORYICON, ITYPE_SELECTEDINVENTORY,
}
enum HexArmorType
{
HEXENARMOR_ARMOR, HEXENARMOR_ARMOR,
HEXENARMOR_SHIELD, HEXENARMOR_SHIELD,
HEXENARMOR_HELM, HEXENARMOR_HELM,
@ -67,35 +72,36 @@ class BaseStatusBar native ui
enum EAlign enum EAlign
{ {
TOP = 0, ALIGN_TOP = 0,
VCENTER = 1, ALIGN_VCENTER = 1,
BOTTOM = 2, ALIGN_BOTTOM = 2,
VOFFSET = 3, ALIGN_VOFFSET = 3,
VMASK = 3, ALIGN_VMASK = 3,
LEFT = 0, ALIGN_LEFT = 0,
HCENTER = 4, ALIGN_HCENTER = 4,
RIGHT = 8, ALIGN_RIGHT = 8,
HOFFSET = 12, ALIGN_HOFFSET = 12,
HMASK = 12, ALIGN_HMASK = 12,
CENTER = VCENTER|HCENTER, ALIGN_CENTER = ALIGN_VCENTER|ALIGN_HCENTER,
CENTER_BOTTOM = BOTTOM|HCENTER ALIGN_CENTER_BOTTOM = ALIGN_BOTTOM|ALIGN_HCENTER,
ALIGN_OFFSETS = ALIGN_HOFFSET|ALIGN_VOFFSET
}; };
enum ETextAlign enum ETextAlign
{ {
ALIGN_LEFT = 0, TEXT_LEFT = 0,
ALIGN_CENTER = 1, TEXT_CENTER = 1,
ALIGN_RIGHT = 2 TEXT_RIGHT = 2
}; };
enum SBGameModes enum SBGameModes
{ {
SINGLEPLAYER = 0x1, GAMEMODE_SINGLEPLAYER = 0x1,
COOPERATIVE = 0x2, GAMEMODE_COOPERATIVE = 0x2,
DEATHMATCH = 0x4, GAMEMODE_DEATHMATCH = 0x4,
TEAMGAME = 0x8 GAMEMODE_TEAMGAME = 0x8
}; };
enum AmmoModes enum AmmoModes
@ -112,6 +118,12 @@ class BaseStatusBar native ui
HUD_HorizCenter HUD_HorizCenter
} }
enum ENumFlags
{
FNF_FILLZEROS,
FNF_WHENNOTZERO,
}
const XHAIRSHRINKSIZE =(1./18); const XHAIRSHRINKSIZE =(1./18);
const XHAIRPICKUPSIZE = (2+XHAIRSHRINKSIZE); const XHAIRPICKUPSIZE = (2+XHAIRSHRINKSIZE);
@ -154,11 +166,13 @@ class BaseStatusBar native ui
virtual void SetMugShotState (String state_name, bool wait_till_done=false, bool reset=false) {} virtual void SetMugShotState (String state_name, bool wait_till_done=false, bool reset=false) {}
native void RefreshBackground () const; native void RefreshBackground () const;
// These functions are kept native solely for performance reasons. They get called repeatedly and can drag down performance easily if they get too slow.
native Inventory ValidateInvFirst (int numVisible) const; native Inventory ValidateInvFirst (int numVisible) const;
native static TextureID, bool GetInventoryIcon(Inventory item, int flags); native static TextureID, bool GetInventoryIcon(Inventory item, int flags);
native void DrawGraphic(TextureID texture, bool animate, Vector2 pos, double Alpha, bool translatable, bool dim, int imgAlign, int screenalign, bool alphamap, Vector2 box); native void DrawGraphic(TextureID texture, bool animate, Vector2 pos, double Alpha, bool translatable, bool dim, int imgAlign, int screenalign, bool alphamap, Vector2 box);
native void DrawString(Font font, String string, Vector2 pos , double Alpha, int translation, int align, int screenalign, int spacing=0, bool monospaced = false, int shadowX=0, int shadowY=0, int wrapwidth = -1, int linespacing = 4); native void DrawString(Font font, String string, Vector2 pos , double Alpha, int translation, int align, int screenalign, int spacing=0, bool monospaced = false, int shadowX=0, int shadowY=0, int wrapwidth = -1, int linespacing = 4);
native static String FormatNumber(int number, int minsize, int maxsize, int format, String prefix = "");
//============================================================================ //============================================================================
@ -247,6 +261,70 @@ class BaseStatusBar native ui
// These cannot be done in ZScript. // These cannot be done in ZScript.
native String GetGlobalACSString(int index); native String GetGlobalACSString(int index);
native String GetGlobalACSArrayString(int arrayno, int index); native String GetGlobalACSArrayString(int arrayno, int index);
native int GetGlobalACSValue(int index);
native int GetGlobalACSArrayValue(int arrayno, int index);
//============================================================================
//
// Convenience functions to retrieve some numbers
//
//============================================================================
int GetArmorAmount()
{
let armor = CPlayer.mo.FindInventory("BasicArmor");
return armor? armor.Amount : 0;
}
int GetMaxAmount(class<Inventory> item)
{
let it = CPlayer.mo.FindInventory(item);
return it? it.MaxAmount : GetDefaultByType(item).MaxAmount;
}
int GetArmorSavePercent()
{
double add = 0;
let harmor = HexenArmor(CPlayer.mo.FindInventory("HexenArmor"));
if(harmor != NULL)
{
add = harmor.Slots[0] + harmor.Slots[1] + harmor.Slots[2] + harmor.Slots[3] + harmor.Slots[4];
}
//Hexen counts basic armor also so we should too.
let armor = BasicArmor(CPlayer.mo.FindInventory("BasicArmor"));
if(armor != NULL)
{
add += armor.SavePercent * 100;
}
return int(add);
}
// Note that this retrieves the value in tics, not seconds like the equivalent SBARINFO function.
// The idea is to let the caller decide what to do with it instead of destroying accuracy here.
int GetAirTime()
{
if(CPlayer.mo.waterlevel < 3)
return level.airsupply;
else
return max(CPlayer.air_finished - level.time, 0);
}
int GetSelectedInventoryAmount()
{
if(CPlayer.mo.InvSel != NULL) return CPlayer.mo.InvSel.Amount;
return 0;
}
int GetKeyCount()
{
int num = 0;
for(Inventory item = CPlayer.mo.Inv;item != NULL;item = item.Inv)
{
if(item is "Key") num++;
}
return num;
}
//============================================================================ //============================================================================
// //
// various checker functions, based on SBARINFOs condition nodes. // various checker functions, based on SBARINFOs condition nodes.
@ -261,10 +339,10 @@ class BaseStatusBar native ui
bool CheckGameMode(int ValidModes) bool CheckGameMode(int ValidModes)
{ {
return (!multiplayer && (ValidModes & SINGLEPLAYER)) || return (!multiplayer && (ValidModes & GAMEMODE_SINGLEPLAYER)) ||
(deathmatch && (ValidModes & DEATHMATCH)) || (deathmatch && (ValidModes & GAMEMODE_DEATHMATCH)) ||
(multiplayer && !deathmatch && (ValidModes & COOPERATIVE)) || (multiplayer && !deathmatch && (ValidModes & GAMEMODE_COOPERATIVE)) ||
(teamplay && (ValidModes & TEAMGAME)); (teamplay && (ValidModes & GAMEMODE_TEAMGAME));
} }
//============================================================================ //============================================================================
@ -436,7 +514,7 @@ class BaseStatusBar native ui
// //
//============================================================================ //============================================================================
void DrawTexture(TextureID texture, Vector2 pos, bool animated = false, double alpha = 1.0, int screenalign = TOP|LEFT, Vector2 boxsize = (-1, -1), int itemAlign = TOP|LEFT, int flags = 0, Vector2 scale = (1., 1.) ) void DrawTexture(TextureID texture, Vector2 pos, bool animated = false, double alpha = 1.0, int screenalign = ALIGN_TOP|ALIGN_LEFT, Vector2 boxsize = (-1, -1), int itemAlign = ALIGN_TOP|ALIGN_LEFT, int flags = 0, Vector2 scale = (1., 1.) )
{ {
if (!texture.IsValid()) return; // nothing to draw if (!texture.IsValid()) return; // nothing to draw
@ -467,13 +545,12 @@ class BaseStatusBar native ui
else scale1 = min(scale1, scale2); else scale1 = min(scale1, scale2);
boxsize = texsize * scale1; boxsize = texsize * scale1;
screenAlign = TOP|LEFT; // anything else makes no sense here.
} }
else else
{ {
boxsize = texsize; boxsize = texsize;
} }
DrawGraphic(texture, animated, pos, Alpha, !!(flags & DI_TRANSLATABLE), false, itemAlign, screenAlign, false, boxsize); DrawGraphic(texture, animated, pos, Alpha, !!(flags & DI_TRANSLATABLE), !!(flags & DI_DIM), itemAlign, screenAlign, false, boxsize);
} }
//============================================================================ //============================================================================
@ -482,7 +559,7 @@ class BaseStatusBar native ui
// //
//============================================================================ //============================================================================
void DrawImage(String imagename, Vector2 pos, bool animated = false, double alpha = 1.0, int screenalign = TOP|LEFT, Vector2 boxsize = (-1, -1), int itemAlign = TOP|LEFT, int flags = 0, Vector2 scale = (1., 1.) ) void DrawImage(String imagename, Vector2 pos, bool animated = false, double alpha = 1.0, int screenalign = ALIGN_TOP|ALIGN_LEFT, Vector2 boxsize = (-1, -1), int itemAlign = ALIGN_TOP|ALIGN_LEFT, int flags = 0, Vector2 scale = (1., 1.) )
{ {
let tex = TexMan.CheckForTexture(imagename, TexMan.TYPE_MiscPatch); let tex = TexMan.CheckForTexture(imagename, TexMan.TYPE_MiscPatch);
DrawTexture(tex, pos, animated, screenalign, alpha, boxsize, itemAlign, flags, scale); DrawTexture(tex, pos, animated, screenalign, alpha, boxsize, itemAlign, flags, scale);
@ -494,36 +571,36 @@ class BaseStatusBar native ui
// //
//============================================================================ //============================================================================
void DrawIcon(int icontype, Vector2 pos, bool animated = false, double alpha = 1.0, int screenalign = TOP|LEFT, Vector2 boxsize = (-1, -1), int itemAlign = TOP|LEFT, int flags = 0) void DrawIcon(int icontype, Vector2 pos, bool animated = false, double alpha = 1.0, int screenalign = ALIGN_TOP|ALIGN_LEFT, Vector2 boxsize = (-1, -1), int itemAlign = ALIGN_TOP|ALIGN_LEFT, int flags = 0)
{ {
TextureID texture; TextureID texture;
Vector2 applyscale = (1, 1); Vector2 applyscale = (1, 1);
Inventory atype1, atype2; Inventory atype1, atype2;
switch (icontype) switch (icontype)
{ {
case PLAYERICON: case ITYPE_PLAYERICON:
texture = CPlayer.mo.ScoreIcon; texture = CPlayer.mo.ScoreIcon;
break; break;
case AMMO1: case ITYPE_AMMO1:
case AMMO2: case ITYPE_AMMO2:
[atype1, atype2] = GetCurrentAmmo(); [atype1, atype2] = GetCurrentAmmo();
[texture, applyscale] = GetIcon(icontype == AMMO1? atype1 : atype2, flags, true); [texture, applyscale] = GetIcon(icontype == ITYPE_AMMO1? atype1 : atype2, flags, true);
break; break;
case ARMOR: case ITYPE_ARMOR:
[texture, applyscale] = GetIcon(CPlayer.mo.FindInventory("BasicArmor"), flags, false); [texture, applyscale] = GetIcon(CPlayer.mo.FindInventory("BasicArmor"), flags, false);
break; break;
case WEAPONICON: case ITYPE_WEAPON:
[texture, applyscale] = GetIcon(CPlayer.ReadyWeapon, flags, false); [texture, applyscale] = GetIcon(CPlayer.ReadyWeapon, flags, false);
break; break;
case SIGIL: case ITYPE_SIGIL:
[texture, applyscale] = GetIcon(CPlayer.mo.FindInventory("Sigil"), flags, false); [texture, applyscale] = GetIcon(CPlayer.mo.FindInventory("Sigil"), flags, false);
break; break;
case SELECTEDINVENTORYICON: case ITYPE_SELECTEDINVENTORY:
if (CPlayer.mo.InvSel != NULL) if (CPlayer.mo.InvSel != NULL)
texture = CPlayer.mo.InvSel.Icon; texture = CPlayer.mo.InvSel.Icon;
break; break;
@ -537,7 +614,7 @@ class BaseStatusBar native ui
// //
//============================================================================ //============================================================================
void DrawHexenArmor(int armortype, String image, Vector2 pos, bool animated = false, double alpha = 1.0, int screenalign = TOP|LEFT, Vector2 boxsize = (-1, -1), int itemAlign = TOP|LEFT, int flags = 0) void DrawHexenArmor(int armortype, String image, Vector2 pos, bool animated = false, double alpha = 1.0, int screenalign = ALIGN_TOP|ALIGN_LEFT, Vector2 boxsize = (-1, -1), int itemAlign = ALIGN_TOP|ALIGN_LEFT, int flags = 0)
{ {
let harmor = HexenArmor(statusBar.CPlayer.mo.FindInventory("HexenArmor")); let harmor = HexenArmor(statusBar.CPlayer.mo.FindInventory("HexenArmor"));
if (harmor != NULL) if (harmor != NULL)
@ -561,7 +638,7 @@ class BaseStatusBar native ui
// //
//============================================================================ //============================================================================
void DrawInventoryIcon(class<Inventory> item, String image, Vector2 pos, bool animated = false, double alpha = 1.0, int screenalign = TOP|LEFT, Vector2 boxsize = (-1, -1), int itemAlign = TOP|LEFT, int flags = 0) void DrawInventoryIcon(class<Inventory> item, String image, Vector2 pos, bool animated = false, double alpha = 1.0, int screenalign = ALIGN_TOP|ALIGN_LEFT, Vector2 boxsize = (-1, -1), int itemAlign = ALIGN_TOP|ALIGN_LEFT, int flags = 0)
{ {
let texture = GetDefaultByType(item).Icon; let texture = GetDefaultByType(item).Icon;
if (texture.IsValid()) if (texture.IsValid())
@ -570,3 +647,82 @@ class BaseStatusBar native ui
} }
} }
} }
//============================================================================
//
// a generic value interpolator for status bar elements that can change
// gradually to their new value.
//
//============================================================================
class LinearValueInterpolator : Object
{
int mCurrentValue;
int mMaxChange;
static LinearValueInterpolator Create(int startval, int maxchange)
{
let v = new("LinearValueInterpolator");
v.mCurrentValue = startval;
v.mMaxChange = maxchange;
return v;
}
// This must be called peroiodically in the status bar's Tick function.
// Do not call this in the Draw function because that may skip some frames!
void Update(int destvalue)
{
if (mCurrentValue > destvalue)
{
mCurrentValue = max(destvalue, mCurrentValue - mMaxChange);
}
else
{
mCurrentValue = min(destvalue, mCurrentValue + mMaxChange);
}
}
// This must be called in the draw function to retrieve the value for output.
int GetValue()
{
return mCurrentValue;
}
}
class DynamicValueInterpolator : Object
{
int mCurrentValue;
int mMinChange;
double mChangeFactor;
static DynamicValueInterpolator Create(int startval, double changefactor, int minchange)
{
let v = new("DynamicValueInterpolator");
v.mCurrentValue = startval;
v.mMinChange = minchange;
v.mChangeFactor = changefactor;
return v;
}
// This must be called peroiodically in the status bar's Tick function.
// Do not call this in the Draw function because that may skip some frames!
void Update(int destvalue)
{
int diff = int(max(abs(destvalue - mCurrentValue) * mChangeFactor, mMinChange));
if (mCurrentValue > destvalue)
{
mCurrentValue = max(destvalue, mCurrentValue - diff);
}
else
{
mCurrentValue = min(destvalue, mCurrentValue + diff);
}
}
// This must be called in the draw function to retrieve the value for output.
int GetValue()
{
return mCurrentValue;
}
}

View file

@ -371,15 +371,16 @@ class StrifeStatusBar : BaseStatusBar
{ {
fullscreenoffsets = true; fullscreenoffsets = true;
// Draw health // Draw health
DrINumberOuter (CPlayer.health, 4, -10, false, 7);
DrawTexture(Images[imgMEDI], (14, -17), false, 1.0, BOTTOM|LEFT, itemalign: BOTTOM|HCENTER); DrawString("Indexfont_Strife_Green", FormatNumber(CPlayer.health, 3, 0, 0), (4, -10), 1., (CPlayer.health < CPlayer.mo.RunHealth)? Font.CR_BRICK : Font.CR_UNTRANSLATED, TEXT_LEFT, ALIGN_LEFT|ALIGN_BOTTOM, 7, true, 1, 1);
DrawTexture(Images[imgMEDI], (14, -17), false, 1.0, ALIGN_BOTTOM|ALIGN_LEFT, itemalign: ALIGN_BOTTOM|ALIGN_HCENTER);
// Draw armor // Draw armor
let armor = CPlayer.mo.FindInventory('BasicArmor'); let armor = CPlayer.mo.FindInventory('BasicArmor');
if (armor != NULL && armor.Amount != 0) if (armor != NULL && armor.Amount != 0)
{ {
DrINumberOuter (armor.Amount, 35, -10, false, 7); DrawString("Indexfont_Strife_Yellow", FormatNumber(armor.Amount, 3, 0, 0), (35, -10), 1., Font.CR_UNTRANSLATED, TEXT_LEFT, ALIGN_LEFT|ALIGN_BOTTOM, 7, true, 1, 1);
DrawTexture(armor.Icon, (45, -17), false, 1.0, BOTTOM|LEFT, itemalign: BOTTOM|HCENTER); DrawTexture(armor.Icon, (45, -17), false, 1.0, ALIGN_BOTTOM|ALIGN_LEFT, itemalign: ALIGN_BOTTOM|ALIGN_HCENTER);
} }
// Draw ammo // Draw ammo
@ -390,19 +391,19 @@ class StrifeStatusBar : BaseStatusBar
if (ammo1 != NULL) if (ammo1 != NULL)
{ {
// Draw primary ammo in the bottom-right corner // Draw primary ammo in the bottom-right corner
DrINumberOuter (ammo1.Amount, -23, -10, false, 7); DrawString("Indexfont_Strife_Green", FormatNumber(ammo1.Amount, 3, 0, 0), (-23, -10), 1., Font.CR_UNTRANSLATED, TEXT_LEFT, ALIGN_RIGHT|ALIGN_BOTTOM, 7, true, 1, 1);
DrawTexture(ammo1.Icon, (-14, -17), false, 1.0, BOTTOM|RIGHT, itemalign: BOTTOM|HCENTER); DrawTexture(ammo1.Icon, (-14, -17), false, 1.0, ALIGN_BOTTOM|ALIGN_RIGHT, itemalign: ALIGN_BOTTOM|ALIGN_HCENTER);
if (ammo2 != NULL && ammo1!=ammo2) if (ammo2 != NULL && ammo1!=ammo2)
{ {
// Draw secondary ammo just above the primary ammo // Draw secondary ammo just above the primary ammo
DrINumberOuter (ammo2.Amount, -23, -48, false, 7); DrawString("Indexfont_Strife_Green", FormatNumber(ammo1.Amount, 3, 0, 0), (-23, -48), 1., Font.CR_UNTRANSLATED, TEXT_LEFT, ALIGN_RIGHT|ALIGN_BOTTOM, 7, true, 1, 1);
DrawTexture(ammo1.Icon, (-14, -55), false, 1.0, BOTTOM|RIGHT, itemalign: BOTTOM|HCENTER); DrawTexture(ammo1.Icon, (-14, -55), false, 1.0, ALIGN_BOTTOM|ALIGN_RIGHT, itemalign: ALIGN_BOTTOM|ALIGN_HCENTER);
} }
} }
if (deathmatch) if (deathmatch)
{ // Draw frags (in DM) { // Draw frags (in DM)
DrBNumberOuterFont (CPlayer.fragcount, -44, 1); DrawString("BigFont", FormatNumber(CPlayer.FragCount, 3, 0, 0), (-44, 1), 1., Font.CR_UNTRANSLATED, TEXT_LEFT, ALIGN_RIGHT|ALIGN_TOP, 0, false, 2, 2);
} }
// Draw inventory // Draw inventory
@ -412,18 +413,10 @@ class StrifeStatusBar : BaseStatusBar
{ {
if (ItemFlash > 0) if (ItemFlash > 0)
{ {
vector2 size = TexMan.GetScaledSize(Images[CursorImage]); DrawTexture(Images[CursorImage], (-28, -15), true, 1.0, ALIGN_BOTTOM|ALIGN_RIGHT, ItemAlign:ALIGN_BOTTOM|ALIGN_RIGHT);
screen.DrawTexture (Images[CursorImage], true, -28, -15,
DTA_HUDRules, HUD_Normal,
DTA_LeftOffsetF, size.X,
DTA_TopOffsetF, size.Y,
DTA_Alpha, ItemFlash);
} }
DrINumberOuter (CPlayer.mo.InvSel.Amount, -51, -10, false, 7); DrawString("Indexfont_Strife_Yellow", FormatNumber(CPlayer.mo.InvSel.Amount, 3, 5, 0), (-23, -10), 1.0, Font.CR_UNTRANSLATED, TEXT_RIGHT, ALIGN_BOTTOM|ALIGN_RIGHT, 7, true, 1, 1);
screen.DrawTexture (CPlayer.mo.InvSel.Icon, true, -42, -17, DrawTexture(CPlayer.mo.InvSel.Icon, (-42, -17), true, 1.0, ALIGN_BOTTOM|ALIGN_RIGHT, ItemAlign:ALIGN_BOTTOM|ALIGN_HCENTER, CPlayer.mo.InvSel.Amount > 0 ? 0 : DI_DIM);
DTA_HUDRules, HUD_Normal,
DTA_CenterBottomOffset, true,
DTA_ColorOverlay, CPlayer.mo.InvSel.Amount > 0 ? 0 : Color(170, 0, 0, 0));
} }
} }
else else
@ -431,23 +424,20 @@ class StrifeStatusBar : BaseStatusBar
CPlayer.mo.InvFirst = ValidateInvFirst (6); CPlayer.mo.InvFirst = ValidateInvFirst (6);
int i = 0; int i = 0;
Inventory item; Inventory item;
Vector2 box = TexMan.GetScaledSize(Images[CursorImage]) - (4, 4); // Fit oversized icons into the box.
if (CPlayer.mo.InvFirst != NULL) if (CPlayer.mo.InvFirst != NULL)
{ {
for (item = CPlayer.mo.InvFirst; item != NULL && i < 6; item = item.NextInv()) for (item = CPlayer.mo.InvFirst; item != NULL && i < 6; item = item.NextInv())
{ {
if (item == CPlayer.mo.InvSel) if (item == CPlayer.mo.InvSel)
{ {
screen.DrawTexture (Images[CursorImage], true, -100+i*35, -21, DrawTexture(Images[CursorImage], (-90+i*35, -3), true, 0.75, ALIGN_CENTER_BOTTOM, ItemAlign:ALIGN_CENTER_BOTTOM);
DTA_HUDRules, HUD_HorizCenter,
DTA_Alpha, 0.75);
} }
if (item.Icon.isValid()) if (item.Icon.isValid())
{ {
screen.DrawTexture (item.Icon, true, -94 + i*35, -19, DrawTexture(item.Icon, (-90+i*35, -5), true, 0.75, ALIGN_CENTER_BOTTOM, box, ALIGN_CENTER_BOTTOM, CPlayer.mo.InvSel.Amount > 0 ? 0 : DI_DIM);
DTA_HUDRules, HUD_HorizCenter,
DTA_ColorOverlay, CPlayer.mo.InvSel.Amount > 0 ? 0 : Color(170, 0, 0, 0));
} }
DrINumberOuter (item.Amount, -89 + i*35, -10, true, 7); DrawString("Indexfont_Strife_Yellow", FormatNumber(item.Amount, 3, 5, 0), (-65 + i*35, -8), 1.0, Font.CR_UNTRANSLATED, TEXT_RIGHT, ALIGN_CENTER_BOTTOM, 7, true, 1, 1);
++i; ++i;
} }
} }
@ -480,14 +470,8 @@ class StrifeStatusBar : BaseStatusBar
{ {
case POP_Log: case POP_Log:
{ {
int seconds = Thinker.Tics2Seconds(level.time);
// Draw the latest log message. // Draw the latest log message.
buff = String.Format("%02d:%02d:%02d", screen.DrawText(SmallFont2, Font.CR_UNTRANSLATED, left + 210 * xscale, top + 8 * yscale, Level.TimeFormatted(),
seconds / 3600,
(seconds % 3600) / 60,
(seconds) % 60);
screen.DrawText(SmallFont2, Font.CR_UNTRANSLATED, left + 210 * xscale, top + 8 * yscale, buff,
DTA_CleanNoMove, true); DTA_CleanNoMove, true);
if (CPlayer.LogText.Length() > 0) if (CPlayer.LogText.Length() > 0)
@ -679,150 +663,6 @@ class StrifeStatusBar : BaseStatusBar
} }
//---------------------------------------------------------------------------
//
// PROC DrINumberOuter
//
// Draws a number outside the status bar, possibly scaled.
//
//---------------------------------------------------------------------------
void DrINumberOuter(int val, int x, int y, bool center, int w) const
{
bool negative = false;
x += w * 2;
if (val < 0)
{
negative = true;
val = -val;
}
else if (val == 0)
{
screen.DrawTexture(Images[imgINumbers], true, x + 1, y + 1,
DTA_FillColor, 0, DTA_Alpha, HR_SHADOW,
DTA_HUDRules, center ? HUD_HorizCenter : HUD_Normal);
screen.DrawTexture(Images[imgINumbers], true, x, y,
DTA_HUDRules, center ? HUD_HorizCenter : HUD_Normal);
return;
}
int oval = val;
int ox = x;
// First the shadow
while (val != 0)
{
screen.DrawTexture(Images[imgINumbers + val % 10], true, x + 1, y + 1,
DTA_FillColor, 0, DTA_Alpha, HR_SHADOW,
DTA_HUDRules, center ? HUD_HorizCenter : HUD_Normal);
x -= w;
val /= 10;
}
if (negative)
{
screen.DrawTexture(Images[imgNEGATIVE], true, x + 1, y + 1,
DTA_FillColor, 0, DTA_Alpha, HR_SHADOW,
DTA_HUDRules, center ? HUD_HorizCenter : HUD_Normal);
}
// Then the real deal
val = oval;
x = ox;
while (val != 0)
{
screen.DrawTexture(Images[imgINumbers + val % 10], true, x, y,
DTA_HUDRules, center ? HUD_HorizCenter : HUD_Normal);
x -= w;
val /= 10;
}
if (negative)
{
screen.DrawTexture(Images[imgNEGATIVE], true, x, y,
DTA_HUDRules, center ? HUD_HorizCenter : HUD_Normal);
}
}
//---------------------------------------------------------------------------
//
// PROC DrBNumberOuter
//
// Draws a three digit number using the real big font outside the status bar.
//
//---------------------------------------------------------------------------
void DrBNumberOuterFont(int val, int x, int y, int size = 3) const
{
int v;
bool negative = false;
TextureID pic;
int w = BigFont.GetCharWidth("0");
int ww = w;
if (w > 1)
{
w--;
}
int xpos = x + w / 2 + (size - 1)*w;
if (val == 0)
{
screen.DrawChar(BigFont, Font.CR_UNTRANSLATED, xpos - v / 2 + 2, y + 2, "0",
DTA_HUDRules, HUD_Normal,
DTA_Alpha, HR_SHADOW,
DTA_FillColor, 0);
screen.DrawChar(BigFont, Font.CR_UNTRANSLATED, xpos - v / 2, y, "0",
DTA_HUDRules, HUD_Normal);
return;
}
else if (val < 0)
{
negative = true;
val = -val;
}
int oval = val;
int oxpos = xpos;
// First the shadow
while (val != 0)
{
v = BigFont.GetCharWidth(int("0") + val % 10);
screen.DrawChar(BigFont, Font.CR_UNTRANSLATED, xpos - v / 2 + 2, y + 2,
DTA_HUDRules, HUD_Normal,
DTA_Alpha, HR_SHADOW,
DTA_FillColor, 0);
val /= 10;
xpos -= w;
}
if (negative)
{
v = BigFont.GetCharWidth("-");
screen.DrawChar(BigFont, Font.CR_UNTRANSLATED, xpos - v / 2 + 2, y + 2, "-",
DTA_HUDRules, HUD_Normal,
DTA_Alpha, HR_SHADOW,
DTA_FillColor, 0);
}
// Then the foreground number
val = oval;
xpos = oxpos;
while (val != 0)
{
v = BigFont.GetCharWidth(int("0") + val % 10);
screen.DrawChar(BigFont, Font.CR_UNTRANSLATED, xpos - v / 2, y, "0", DTA_HUDRules, HUD_Normal);
val /= 10;
xpos -= w;
}
if (negative)
{
v = BigFont.GetCharWidth("-");
screen.DrawChar(BigFont, Font.CR_UNTRANSLATED, xpos - v / 2, y, "-", DTA_HUDRules, HUD_Normal);
}
}
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// //
// PROC DrawImage // PROC DrawImage