qzdoom/src/g_statusbar/sbarinfo.cpp
Christoph Oelckers 08b3c38304 - simplified the parameters of the HUD draw functions by moving all booleans into one flags word and ordering them so that the less likely ones to be used can be made optional.
- got rid of the image list in the Doom status bar. The cost of the texture lookup is mostly irrelevant here so clearer and shorter code is preferrable.
- moved the box fitting code from DrawTexture into the native function to have all coordinate calculations in one place which is necessary to implement proper alignment default handling. Without higher level functions altering positioning the default can be set to automatic alignment determination, i.e. the value's sign decides where something is placed. Of course for special cases this can be overridden.
- use ANIMDEFS to animate the inventory arrow,
2017-03-26 18:41:24 +02:00

1637 lines
42 KiB
C++

/*
** sbarinfo_display.cpp
**
** Contains all functions required for the display of custom statusbars.
**
**---------------------------------------------------------------------------
** Copyright 2008 Braden Obrzut
** 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 "doomtype.h"
#include "doomstat.h"
#include "v_font.h"
#include "v_video.h"
#include "sbar.h"
#include "r_defs.h"
#include "w_wad.h"
#include "m_random.h"
#include "d_player.h"
#include "st_stuff.h"
#include "m_swap.h"
#include "a_keys.h"
#include "templates.h"
#include "i_system.h"
#include "sbar.h"
#include "sbarinfo.h"
#include "gi.h"
#include "r_data/r_translate.h"
#include "g_level.h"
#include "v_palette.h"
#include "p_acs.h"
#include "gstrings.h"
#include "cmdlib.h"
#include "g_levellocals.h"
#define ARTIFLASH_OFFSET (statusBar->invBarOffset+6)
enum
{
imgARTIBOX,
imgSELECTBOX,
imgCURSOR,
imgINVLFGEM1,
imgINVRTGEM1,
};
EXTERN_CVAR(Int, fraglimit)
EXTERN_CVAR(Int, screenblocks)
EXTERN_CVAR(Bool, vid_fps)
class DSBarInfo;
static double nulclip[] = { 0,0,0,0 };
////////////////////////////////////////////////////////////////////////////////
/**
* This class is used to help prevent errors that may occur from adding or
* subtracting from coordinates.
*
* In order to provide the maximum flexibility, coordinates are packed into
* an int with one bit reserved for relCenter.
*/
class SBarInfoCoordinate
{
public:
SBarInfoCoordinate &Add(int add)
{
value += add;
return *this;
}
int Coordinate() const { return value; }
bool RelCenter() const { return relCenter; }
void Set(int coord, bool center) { value = coord; relCenter = center; }
void SetCoord(int coord) { value = coord; }
void SetRelCenter(bool center) { relCenter = center; }
int operator* () const { return Coordinate(); }
SBarInfoCoordinate operator+ (int add) const { return SBarInfoCoordinate(*this).Add(add); }
SBarInfoCoordinate operator+ (const SBarInfoCoordinate &other) const { return SBarInfoCoordinate(*this).Add(other.Coordinate()); }
SBarInfoCoordinate operator- (int sub) const { return SBarInfoCoordinate(*this).Add(-sub); }
SBarInfoCoordinate operator- (const SBarInfoCoordinate &other) const { return SBarInfoCoordinate(*this).Add(-other.Coordinate()); }
void operator+= (int add) { Add(add); }
void operator-= (int sub) { Add(-sub); }
protected:
unsigned relCenter:1;
int value:31;
};
class SBarInfoMainBlock;
////////////////////////////////////////////////////////////////////////////////
/* There are three major classes here. The SBarInfoCommand is our root class.
* SBarInfoCommandFlowControl would be the base class for command which
* implements a sub-block. And SBarInfoMainBlock which is the root for a
* single hud (so all commands are held inside a MainBlock at some point).
*
* A MainBlock can be passed NULL for the first argument of the Draw function.
*/
class SBarInfoCommand
{
public:
enum Offset
{
TOP = 0x1,
VMIDDLE = 0x2,
BOTTOM = 0x4,
LEFT = 0x10,
RIGHT = 0x20,
HMIDDLE = 0x40,
CENTER = VMIDDLE|HMIDDLE,
CENTER_BOTTOM = BOTTOM|HMIDDLE
};
SBarInfoCommand(SBarInfo *script) : script(script) {}
virtual ~SBarInfoCommand() {}
virtual void Draw(const SBarInfoMainBlock *block, const DSBarInfo *statusBar)=0;
virtual void Parse(FScanner &sc, bool fullScreenOffsets)=0;
virtual void Reset() {}
virtual void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged) {}
SBarInfo *GetScript() { return script; }
protected:
void GetCoordinates(FScanner &sc, bool fullScreenOffsets, SBarInfoCoordinate &x, SBarInfoCoordinate &y)
{
bool negative = false;
bool relCenter = false;
SBarInfoCoordinate *coords[2] = {&x, &y};
for(int i = 0;i < 2;i++)
{
negative = false;
relCenter = false;
if(i > 0)
sc.MustGetToken(',');
// [-]INT center
negative = sc.CheckToken('-');
sc.MustGetToken(TK_IntConst);
coords[i]->Set(negative ? -sc.Number : sc.Number, false);
if(sc.CheckToken('+'))
{
sc.MustGetToken(TK_Identifier);
if(!sc.Compare("center"))
sc.ScriptError("Expected 'center' but got '%s' instead.", sc.String);
relCenter = true;
}
if(fullScreenOffsets)
{
coords[i]->SetRelCenter(relCenter);
}
}
//if(!fullScreenOffsets)
// y.SetCoord((negative ? -sc.Number : sc.Number) - (200 - script->height));
}
EColorRange GetTranslation(FScanner &sc)
{
if (!sc.CheckToken(TK_Null)) sc.MustGetToken(TK_Identifier);
EColorRange returnVal = CR_UNTRANSLATED;
FString namedTranslation; //we must send in "[translation]"
const uint8_t *trans_ptr;
namedTranslation.Format("[%s]", sc.String);
trans_ptr = (const uint8_t *)(&namedTranslation[0]);
if((returnVal = V_ParseFontColor(trans_ptr, CR_UNTRANSLATED, CR_UNTRANSLATED)) == CR_UNDEFINED)
{
sc.ScriptError("Missing definition for color %s.", sc.String);
}
return returnVal;
}
SBarInfo *script;
};
class SBarInfoCommandFlowControl : public SBarInfoCommand
{
public:
SBarInfoCommandFlowControl(SBarInfo *script) : SBarInfoCommand(script), truth(false) {}
void Draw(const SBarInfoMainBlock *block, const DSBarInfo *statusBar)
{
for(auto command : commands[truth])
command->Draw(block, statusBar);
}
int NumCommands() const { return commands[truth].Size(); }
void Parse(FScanner &sc, bool fullScreenOffsets)
{
ParseBlock(commands[1], sc, fullScreenOffsets);
if(sc.CheckToken(TK_Else))
ParseBlock(commands[0], sc, fullScreenOffsets);
}
void Reset()
{
for(unsigned int i = 0;i < 2;i++)
{
for(auto command : commands[i])
command->Reset();
}
}
void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged)
{
for(auto command : commands[truth])
command->Tick(block, statusBar, hudChanged);
}
protected:
void SetTruth(bool truth, const SBarInfoMainBlock *block, const DSBarInfo *statusBar)
{
// If there is no change we don't need to do anything. Do note
// that this should not change more than once per tick. If it does
// there may be cosmetic problems.
if(this->truth == truth)
return;
this->truth = truth;
if(block != NULL)
Tick(block, statusBar, true);
}
void Negate()
{
swapvalues(commands[0], commands[1]);
}
private:
void ParseBlock(TDeletingArray<SBarInfoCommand *> &commands, FScanner &sc, bool fullScreenOffsets)
{
if(sc.CheckToken('{'))
{
while(SBarInfoCommand *cmd = NextCommand(sc))
{
cmd->Parse(sc, fullScreenOffsets);
commands.Push(cmd);
}
}
else
{
if(SBarInfoCommand *cmd = NextCommand(sc))
{
cmd->Parse(sc, fullScreenOffsets);
commands.Push(cmd);
}
else
sc.ScriptError("Missing command for flow control statement.");
}
}
SBarInfoCommand *NextCommand(FScanner &sc);
TDeletingArray<SBarInfoCommand *> commands[2];
bool truth;
};
class SBarInfoNegatableFlowControl : public SBarInfoCommandFlowControl
{
public:
SBarInfoNegatableFlowControl(SBarInfo *script) : SBarInfoCommandFlowControl(script) {}
void Parse(FScanner &sc, bool fullScreenOffsets)
{
bool negate = false;
if(sc.CheckToken(TK_Identifier))
{
if(sc.Compare("not"))
negate = true;
else
sc.UnGet();
}
ParseNegatable(sc, fullScreenOffsets);
SBarInfoCommandFlowControl::Parse(sc, fullScreenOffsets);
if(negate)
Negate();
}
virtual void ParseNegatable(FScanner &sc, bool fullScreenOffsets) {}
};
class SBarInfoMainBlock : public SBarInfoCommandFlowControl
{
public:
SBarInfoMainBlock(SBarInfo *script) : SBarInfoCommandFlowControl(script),
alpha(1.), currentAlpha(1.), forceScaled(false),
fullScreenOffsets(false)
{
SetTruth(true, NULL, NULL);
}
double Alpha() const { return currentAlpha; }
// Same as Draw but takes into account ForceScaled and temporarily sets the scaling if needed.
void DrawAux(const SBarInfoMainBlock *block, DSBarInfo *statusBar, int xOffset, int yOffset, double alpha);
// Silence hidden overload warning since this is a special use class.
using SBarInfoCommandFlowControl::Draw;
void Draw(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, int xOffset, int yOffset, double alpha)
{
this->xOffset = xOffset;
this->yOffset = yOffset;
this->currentAlpha = this->alpha * alpha;
SBarInfoCommandFlowControl::Draw(this, statusBar);
}
bool ForceScaled() const { return forceScaled; }
bool FullScreenOffsets() const { return fullScreenOffsets; }
void Parse(FScanner &sc, bool fullScreenOffsets)
{
this->fullScreenOffsets = fullScreenOffsets;
if(sc.CheckToken(','))
{
while(sc.CheckToken(TK_Identifier))
{
if(sc.Compare("forcescaled"))
forceScaled = true;
else if(sc.Compare("fullscreenoffsets"))
this->fullScreenOffsets = true;
else
sc.ScriptError("Unkown flag '%s'.", sc.String);
if(!sc.CheckToken('|') && !sc.CheckToken(','))
{
SBarInfoCommandFlowControl::Parse(sc, this->fullScreenOffsets);
return;
}
}
sc.MustGetToken(TK_FloatConst);
alpha = sc.Float;
}
SBarInfoCommandFlowControl::Parse(sc, this->fullScreenOffsets);
}
void Tick(const SBarInfoMainBlock *block, const DSBarInfo *statusBar, bool hudChanged) { SBarInfoCommandFlowControl::Tick(this, statusBar, hudChanged); }
int XOffset() const { return xOffset; }
int YOffset() const { return yOffset; }
protected:
double alpha;
double currentAlpha;
bool forceScaled;
bool fullScreenOffsets;
int xOffset;
int yOffset;
};
////////////////////////////////////////////////////////////////////////////////
SBarInfo *SBarInfoScript[2] = {NULL,NULL};
enum //Key words
{
SBARINFO_BASE,
SBARINFO_HEIGHT,
SBARINFO_INTERPOLATEHEALTH,
SBARINFO_INTERPOLATEARMOR,
SBARINFO_COMPLETEBORDER,
SBARINFO_MONOSPACEFONTS,
SBARINFO_LOWERHEALTHCAP,
SBARINFO_RESOLUTION,
SBARINFO_STATUSBAR,
SBARINFO_MUGSHOT,
SBARINFO_CREATEPOPUP,
};
enum //Bar types
{
STBAR_NONE,
STBAR_FULLSCREEN,
STBAR_NORMAL,
STBAR_AUTOMAP,
STBAR_INVENTORY,
STBAR_INVENTORYFULLSCREEN,
STBAR_POPUPLOG,
STBAR_POPUPKEYS,
STBAR_POPUPSTATUS,
};
static const char *SBarInfoTopLevel[] =
{
"base",
"height",
"interpolatehealth",
"interpolatearmor",
"completeborder",
"monospacefonts",
"lowerhealthcap",
"resolution",
"statusbar",
"mugshot",
"createpopup",
NULL
};
static const char *StatusBars[] =
{
"none",
"fullscreen",
"normal",
"automap",
"inventory",
"inventoryfullscreen",
"popuplog",
"popupkeys",
"popupstatus",
NULL
};
static void FreeSBarInfoScript()
{
for(int i = 0;i < 2;i++)
{
if (SBarInfoScript[i] != NULL)
{
delete SBarInfoScript[i];
SBarInfoScript[i] = NULL;
}
}
}
void SBarInfo::Load()
{
FreeSBarInfoScript();
MugShotStates.Clear();
if(gameinfo.statusbar.IsNotEmpty())
{
int lump = Wads.CheckNumForFullName(gameinfo.statusbar, true);
if(lump != -1)
{
if (!batchrun) Printf ("ParseSBarInfo: Loading default status bar definition.\n");
if(SBarInfoScript[SCRIPT_DEFAULT] == NULL)
SBarInfoScript[SCRIPT_DEFAULT] = new SBarInfo(lump);
else
SBarInfoScript[SCRIPT_DEFAULT]->ParseSBarInfo(lump);
}
}
if(Wads.CheckNumForName("SBARINFO") != -1)
{
if (!batchrun) Printf ("ParseSBarInfo: Loading custom status bar definition.\n");
int lastlump, lump;
lastlump = 0;
while((lump = Wads.FindLump("SBARINFO", &lastlump)) != -1)
{
if(SBarInfoScript[SCRIPT_CUSTOM] == NULL)
SBarInfoScript[SCRIPT_CUSTOM] = new SBarInfo(lump);
else //We now have to load multiple SBarInfo Lumps so the 2nd time we need to use this method instead.
SBarInfoScript[SCRIPT_CUSTOM]->ParseSBarInfo(lump);
}
}
atterm(FreeSBarInfoScript);
}
//SBarInfo Script Reader
void SBarInfo::ParseSBarInfo(int lump)
{
gameType = gameinfo.gametype;
bool baseSet = false;
FScanner sc(lump);
sc.SetCMode(true);
while(sc.CheckToken(TK_Identifier) || sc.CheckToken(TK_Include))
{
if(sc.TokenType == TK_Include)
{
sc.MustGetToken(TK_StringConst);
int lump = Wads.CheckNumForFullName(sc.String, true);
if (lump == -1)
sc.ScriptError("Lump '%s' not found", sc.String);
ParseSBarInfo(lump);
continue;
}
int baselump = -2;
switch(sc.MustMatchString(SBarInfoTopLevel))
{
case SBARINFO_BASE:
baseSet = true;
if(!sc.CheckToken(TK_None))
sc.MustGetToken(TK_Identifier);
if(sc.Compare("Doom"))
{
baselump = Wads.CheckNumForFullName("sbarinfo/doom.txt", true);
}
else if(sc.Compare("Heretic"))
{
baselump = Wads.CheckNumForFullName("sbarinfo/heretic.txt", true);
}
else if(sc.Compare("Hexen"))
{
baselump = Wads.CheckNumForFullName("sbarinfo/hexen.txt", true);
}
else if(sc.Compare("Strife"))
gameType = GAME_Strife;
else if(sc.Compare("None"))
gameType = GAME_Any;
else
sc.ScriptError("Bad game name: %s", sc.String);
// If one of the standard status bar should be loaded, baselump has been set to a different value.
if (baselump != -2)
{
if(baselump == -1)
{
sc.ScriptError("Standard %s status bar not found.", sc.String);
}
else if (Wads.GetLumpFile(baselump) > 0)
{
I_FatalError("File %s is overriding core lump sbarinfo/%s.txt.",
Wads.GetWadFullName(Wads.GetLumpFile(baselump)), sc.String);
}
ParseSBarInfo(baselump);
}
sc.MustGetToken(';');
break;
case SBARINFO_HEIGHT:
sc.MustGetToken(TK_IntConst);
this->height = sc.Number;
sc.MustGetToken(';');
break;
case SBARINFO_INTERPOLATEHEALTH: //mimics heretics interpolated health values.
if(sc.CheckToken(TK_True))
{
interpolateHealth = true;
}
else
{
sc.MustGetToken(TK_False);
interpolateHealth = false;
}
if(sc.CheckToken(',')) //speed param
{
sc.MustGetToken(TK_IntConst);
this->interpolationSpeed = sc.Number;
}
sc.MustGetToken(';');
break;
case SBARINFO_INTERPOLATEARMOR: //Since interpolatehealth is such a popular command
if(sc.CheckToken(TK_True))
{
interpolateArmor = true;
}
else
{
sc.MustGetToken(TK_False);
interpolateArmor = false;
}
if(sc.CheckToken(',')) //speed
{
sc.MustGetToken(TK_IntConst);
this->armorInterpolationSpeed = sc.Number;
}
sc.MustGetToken(';');
break;
case SBARINFO_COMPLETEBORDER: //draws the border instead of an HOM
if(sc.CheckToken(TK_True))
{
completeBorder = true;
}
else
{
sc.MustGetToken(TK_False);
completeBorder = false;
}
sc.MustGetToken(';');
break;
case SBARINFO_MONOSPACEFONTS:
if(sc.CheckToken(TK_True))
{
sc.MustGetToken(',');
sc.MustGetToken(TK_StringConst);
spacingCharacter = sc.String[0];
}
else
{
sc.MustGetToken(TK_False);
spacingCharacter = '\0';
sc.MustGetToken(',');
sc.MustGetToken(TK_StringConst); //Don't tell anyone we're just ignoring this ;)
}
if(sc.CheckToken(','))
{
// Character alignment
sc.MustGetToken(TK_Identifier);
if(sc.Compare("left"))
spacingAlignment = ALIGN_LEFT;
else if(sc.Compare("center"))
spacingAlignment = ALIGN_CENTER;
else if(sc.Compare("right"))
spacingAlignment = ALIGN_RIGHT;
else
sc.ScriptError("Unknown alignment '%s'.", sc.String);
}
sc.MustGetToken(';');
break;
case SBARINFO_LOWERHEALTHCAP:
if(sc.CheckToken(TK_False))
{
lowerHealthCap = false;
}
else
{
sc.MustGetToken(TK_True);
lowerHealthCap = true;
}
sc.MustGetToken(';');
break;
case SBARINFO_RESOLUTION:
sc.MustGetToken(TK_IntConst);
_resW = sc.Number;
sc.MustGetToken(',');
sc.MustGetToken(TK_IntConst);
_resH = sc.Number;
sc.MustGetToken(';');
break;
case SBARINFO_STATUSBAR:
{
if(!baseSet) //If the user didn't explicitly define a base, do so now.
gameType = GAME_Any;
int barNum = 0;
if(!sc.CheckToken(TK_None))
{
sc.MustGetToken(TK_Identifier);
barNum = sc.MustMatchString(StatusBars);
}
if (this->huds[barNum] != NULL)
{
delete this->huds[barNum];
}
this->huds[barNum] = new SBarInfoMainBlock(this);
if(barNum == STBAR_AUTOMAP)
{
automapbar = true;
}
this->huds[barNum]->Parse(sc, false);
break;
}
case SBARINFO_MUGSHOT:
{
sc.MustGetToken(TK_StringConst);
FMugShotState state(sc.String);
if(sc.CheckToken(',')) //first loop must be a comma
{
do
{
sc.MustGetToken(TK_Identifier);
if(sc.Compare("health"))
state.bUsesLevels = true;
else if(sc.Compare("health2"))
state.bUsesLevels = state.bHealth2 = true;
else if(sc.Compare("healthspecial"))
state.bUsesLevels = state.bHealthSpecial = true;
else if(sc.Compare("directional"))
state.bDirectional = true;
else
sc.ScriptError("Unknown MugShot state flag '%s'.", sc.String);
}
while(sc.CheckToken(',') || sc.CheckToken('|'));
}
ParseMugShotBlock(sc, state);
int index;
if((index = FindMugShotStateIndex(state.State)) != -1) //We already had this state, remove the old one.
{
MugShotStates.Delete(index);
}
MugShotStates.Push(state);
break;
}
case SBARINFO_CREATEPOPUP:
{
int pop = 0;
sc.MustGetToken(TK_Identifier);
if(sc.Compare("log"))
pop = 0;
else if(sc.Compare("keys"))
pop = 1;
else if(sc.Compare("status"))
pop = 2;
else
sc.ScriptError("Unkown popup: '%s'", sc.String);
Popup &popup = popups[pop];
sc.MustGetToken(',');
sc.MustGetToken(TK_IntConst);
popup.width = sc.Number;
sc.MustGetToken(',');
sc.MustGetToken(TK_IntConst);
popup.height = sc.Number;
sc.MustGetToken(',');
if(!sc.CheckToken(TK_None))
{
sc.MustGetToken(TK_Identifier);
if(sc.Compare("slideinbottom"))
{
popup.transition = Popup::TRANSITION_SLIDEINBOTTOM;
sc.MustGetToken(',');
sc.MustGetToken(TK_IntConst);
popup.ispeed = sc.Number;
}
else if(sc.Compare("pushup"))
{
popup.transition = Popup::TRANSITION_PUSHUP;
sc.MustGetToken(',');
sc.MustGetToken(TK_IntConst);
popup.ispeed = sc.Number;
}
else if(sc.Compare("fade"))
{
popup.transition = Popup::TRANSITION_FADE;
sc.MustGetToken(',');
sc.MustGetToken(TK_FloatConst);
popup.speed = 1.0 / (35.0 * sc.Float);
sc.MustGetToken(',');
sc.MustGetToken(TK_FloatConst);
popup.speed2 = 1.0 / (35.0 * sc.Float);
}
else
sc.ScriptError("Unkown transition type: '%s'", sc.String);
}
popup.init();
sc.MustGetToken(';');
break;
}
}
}
}
void SBarInfo::ParseMugShotBlock(FScanner &sc, FMugShotState &state)
{
sc.MustGetToken('{');
while(!sc.CheckToken('}'))
{
FMugShotFrame frame;
bool multiframe = false;
if(sc.CheckToken('{'))
multiframe = true;
do
{
sc.MustGetToken(TK_Identifier);
if(strlen(sc.String) > 5)
sc.ScriptError("MugShot frames cannot exceed 5 characters.");
frame.Graphic.Push(sc.String);
}
while(multiframe && sc.CheckToken(','));
if(multiframe)
sc.MustGetToken('}');
bool negative = sc.CheckToken('-');
sc.MustGetToken(TK_IntConst);
frame.Delay = (negative ? -1 : 1)*sc.Number;
sc.MustGetToken(';');
state.Frames.Push(frame);
}
}
void SBarInfo::ResetHuds()
{
for(int i = 0;i < NUMHUDS;i++)
huds[i]->Reset();
}
int SBarInfo::newImage(const char *patchname)
{
if(patchname[0] == '\0' || stricmp(patchname, "nullimage") == 0)
{
return -1;
}
for(unsigned int i = 0;i < this->Images.Size();i++) //did we already load it?
{
if(stricmp(this->Images[i], patchname) == 0)
{
return i;
}
}
return this->Images.Push(patchname);
}
SBarInfo::SBarInfo() //make results more predicable
{
Init();
}
SBarInfo::SBarInfo(int lumpnum)
{
Init();
ParseSBarInfo(lumpnum);
}
void SBarInfo::Init()
{
automapbar = false;
interpolateHealth = false;
interpolateArmor = false;
completeBorder = false;
lowerHealthCap = true;
interpolationSpeed = 8;
armorInterpolationSpeed = 8;
height = 0;
spacingCharacter = '\0';
spacingAlignment = ALIGN_CENTER;
_resW = 320;
_resH = 200;
for(unsigned int i = 0;i < NUMHUDS;i++)
huds[i] = new SBarInfoMainBlock(this);
}
SBarInfo::~SBarInfo()
{
for (size_t i = 0; i < NUMHUDS; ++i)
{
delete huds[i];
// huds[i].commands.Clear();
}
}
//Popup
Popup::Popup() : transition(TRANSITION_NONE), opened(false), moving(false),
height(320), width(200), ispeed(0), speed(0), speed2(0), alpha(1.), x(320),
y(200), displacementX(0), displacementY(0)
{
}
void Popup::init()
{
x = width;
y = height;
switch(transition)
{
case TRANSITION_SLIDEINBOTTOM:
case TRANSITION_PUSHUP:
x = 0;
break;
case TRANSITION_FADE:
alpha = 0;
x = 0;
y = 0;
break;
default:
break;
}
}
void Popup::tick()
{
switch(transition)
{
case TRANSITION_SLIDEINBOTTOM:
case TRANSITION_PUSHUP:
if(moving)
{
int oldY = y;
if(opened)
y -= clamp(height + (y - height), 1, ispeed);
else
y += clamp(height - y, 1, ispeed);
if(transition == TRANSITION_PUSHUP)
displacementY += y - oldY;
}
if(y != 0 && y != height)
moving = true;
else
moving = false;
break;
case TRANSITION_FADE:
if(moving)
{
if(opened)
alpha = clamp(alpha + speed, 0., 1.);
else
alpha = clamp(alpha - speed2, 0., 1.);
}
if(alpha == 0 || alpha == 1.)
moving = false;
else
moving = true;
break;
default:
if(opened)
{
y = 0;
x = 0;
}
else
{
y = height;
x = width;
}
moving = false;
break;
}
}
bool Popup::isDoneMoving()
{
return !moving;
}
int Popup::getXOffset()
{
return x;
}
int Popup::getYOffset()
{
return y;
}
double Popup::getAlpha(double maxAlpha)
{
return alpha * maxAlpha;
}
int Popup::getXDisplacement()
{
return displacementX;
}
int Popup::getYDisplacement()
{
return displacementY;
}
void Popup::open()
{
opened = true;
moving = true;
}
void Popup::close()
{
opened = false;
moving = true;
}
////////////////////////////////////////////////////////////////////////////////
inline void adjustRelCenter(bool relX, bool relY, const double &x, const double &y, double &outX, double &outY, double ScaleX, double ScaleY)
{
if(relX)
outX = x + (SCREENWIDTH/(ScaleX*2));
else
outX = x;
if(relY)
outY = y + (SCREENHEIGHT/(ScaleY*2));
else
outY = y;
}
class DSBarInfo
{
public:
DSBarInfo (DBaseStatusBar *wrapper, SBarInfo *script=NULL) :
ammo1(NULL), ammo2(NULL), ammocount1(0), ammocount2(0), armor(NULL),
pendingPopup(DBaseStatusBar::POP_None), currentPopup(DBaseStatusBar::POP_None), lastHud(-1),
scalingWasForced(false), lastInventoryBar(NULL), lastPopup(NULL)
{
this->script = script;
this->wrapper = wrapper;
static const char *InventoryBarLumps[] =
{
"ARTIBOX", "SELECTBO", "INVCURS", "INVGEML1", "INVGEMR1",
"USEARTIA", "USEARTIB", "USEARTIC", "USEARTID",
};
TArray<const char *> patchnames;
patchnames.Resize(script->Images.Size()+10);
unsigned int i = 0;
for(i = 0;i < script->Images.Size();i++)
{
patchnames[i] = script->Images[i];
}
for(i = 0;i < 10;i++)
{
patchnames[i+script->Images.Size()] = InventoryBarLumps[i];
}
invBarOffset = script->Images.Size();
Images.Init(&patchnames[0], patchnames.Size());
}
~DSBarInfo ()
{
Images.Uninit();
}
void _SetScaled(bool scaled)
{
Scaled = scaled;
}
void _AttachToPlayer(player_t *player)
{
CPlayer = player;
}
void _Draw (EHudState state)
{
int hud = STBAR_NORMAL;
if(state == HUD_StatusBar)
{
if(script->automapbar && automapactive)
{
hud = STBAR_AUTOMAP;
}
else
{
hud = STBAR_NORMAL;
}
}
else if(state == HUD_Fullscreen)
{
hud = STBAR_FULLSCREEN;
}
else
{
hud = STBAR_NONE;
}
if(script->huds[hud]->ForceScaled()) //scale the statusbar
{
if(script->huds[hud]->FullScreenOffsets())
wrapper->ForceHUDScale(true);
else if(!Scaled)
{
scalingWasForced = true;
wrapper->SetScaled(true, true);
setsizeneeded = true;
}
}
if (CPlayer->ReadyWeapon != NULL)
{
ammo1 = CPlayer->ReadyWeapon->Ammo1;
ammo2 = CPlayer->ReadyWeapon->Ammo2;
if (ammo1 == NULL)
{
ammo1 = ammo2;
ammo2 = NULL;
}
}
else
{
ammo1 = ammo2 = NULL;
}
ammocount1 = ammo1 != NULL ? ammo1->Amount : 0;
ammocount2 = ammo2 != NULL ? ammo2->Amount : 0;
//prepare ammo counts
armor = CPlayer->mo->FindInventory(NAME_BasicArmor);
if(state != HUD_AltHud)
{
if(hud != lastHud)
{
script->huds[hud]->Tick(NULL, this, true);
// Restore scaling if need be.
if(scalingWasForced)
{
scalingWasForced = false;
wrapper->SetScaled(false);
setsizeneeded = true;
}
}
if(currentPopup != DBaseStatusBar::POP_None && !script->huds[hud]->FullScreenOffsets())
script->huds[hud]->Draw(NULL, this, script->popups[currentPopup-1].getXDisplacement(), script->popups[currentPopup-1].getYDisplacement(), 1.);
else
script->huds[hud]->Draw(NULL, this, 0, 0, 1.);
lastHud = hud;
// Handle inventory bar drawing
if(CPlayer->inventorytics > 0 && !(level.flags & LEVEL_NOINVENTORYBAR) && (state == HUD_StatusBar || state == HUD_Fullscreen))
{
SBarInfoMainBlock *inventoryBar = state == HUD_StatusBar ? script->huds[STBAR_INVENTORY] : script->huds[STBAR_INVENTORYFULLSCREEN];
if(inventoryBar != lastInventoryBar)
inventoryBar->Tick(NULL, this, true);
// No overlay? Lets cancel it.
if(inventoryBar->NumCommands() == 0)
CPlayer->inventorytics = 0;
else
inventoryBar->DrawAux(NULL, this, 0, 0, 1.);
}
}
// Handle popups
if(currentPopup != DBaseStatusBar::POP_None)
{
int popbar = 0;
if(currentPopup == DBaseStatusBar::POP_Log)
popbar = STBAR_POPUPLOG;
else if(currentPopup == DBaseStatusBar::POP_Keys)
popbar = STBAR_POPUPKEYS;
else if(currentPopup == DBaseStatusBar::POP_Status)
popbar = STBAR_POPUPSTATUS;
if(script->huds[popbar] != lastPopup)
{
lastPopup = script->huds[popbar];
lastPopup->Tick(NULL, this, true);
}
script->huds[popbar]->DrawAux(NULL, this, script->popups[currentPopup-1].getXOffset(), script->popups[currentPopup-1].getYOffset(), script->popups[currentPopup-1].getAlpha());
}
else
lastPopup = NULL;
// Reset hud scale
wrapper->ForceHUDScale(false);
}
void _NewGame ()
{
// Reset the huds
script->ResetHuds();
lastHud = -1; // Reset
}
bool _MustDrawLog (EHudState state)
{
return script->huds[STBAR_POPUPLOG]->NumCommands() == 0;
}
void _Tick ()
{
if(currentPopup != DBaseStatusBar::POP_None)
{
script->popups[currentPopup-1].tick();
if(script->popups[currentPopup-1].opened == false && script->popups[currentPopup-1].isDoneMoving())
{
currentPopup = pendingPopup;
if(currentPopup != DBaseStatusBar::POP_None)
script->popups[currentPopup-1].open();
}
if (lastPopup != NULL) lastPopup->Tick(NULL, this, false);
}
if(lastHud != -1)
script->huds[lastHud]->Tick(NULL, this, false);
if(lastInventoryBar != NULL && CPlayer->inventorytics > 0)
lastInventoryBar->Tick(NULL, this, false);
}
// void DSBarInfo::FlashItem(const PClass *itemtype) - Is defined with CommandDrawSelectedInventory
void _FlashItem(const PClass *itemtype);
void _ShowPop(int popnum)
{
if(popnum != currentPopup)
{
pendingPopup = popnum;
}
else
pendingPopup = DBaseStatusBar::POP_None;
if(currentPopup != DBaseStatusBar::POP_None)
script->popups[currentPopup-1].close();
else
{
currentPopup = pendingPopup;
pendingPopup = DBaseStatusBar::POP_None;
if(currentPopup != DBaseStatusBar::POP_None)
script->popups[currentPopup-1].open();
}
}
//draws an image with the specified flags
void DrawGraphic(FTexture* texture, SBarInfoCoordinate x, SBarInfoCoordinate y, int xOffset, int yOffset, double Alpha, bool fullScreenOffsets, bool translate=false, bool dim=false, int offsetflags=0, bool alphaMap=false, int forceWidth=-1, int forceHeight=-1, const double *clip = nulclip, bool clearDontDraw=false) const
{
if (texture == NULL)
return;
double dx = *x;
double dy = *y;
if((offsetflags & SBarInfoCommand::CENTER) == SBarInfoCommand::CENTER)
{
if (forceWidth < 0) dx -= (texture->GetScaledWidthDouble()/2.0)-texture->GetScaledLeftOffsetDouble();
else dx -= forceWidth*(0.5-(texture->GetScaledLeftOffsetDouble()/texture->GetScaledWidthDouble()));
//Unoptimalized ^^formula is dx -= forceWidth/2.0-(texture->GetScaledLeftOffsetDouble()*forceWidth/texture->GetScaledWidthDouble());
if (forceHeight < 0) dy -= (texture->GetScaledHeightDouble()/2.0)-texture->GetScaledTopOffsetDouble();
else dy -= forceHeight*(0.5-(texture->GetScaledTopOffsetDouble()/texture->GetScaledHeightDouble()));
}
dx += xOffset;
dy += yOffset;
double w, h;
if(!fullScreenOffsets)
{
double tmp = 0;
int barW = wrapper->HorizontalResolution, barH = wrapper->VerticalResolution;
dx += wrapper->ST_X;
dy += wrapper->ST_Y - (Scaled ? barH : 200) + script->height;
w = forceWidth < 0 ? texture->GetScaledWidthDouble() : forceWidth;
h = forceHeight < 0 ? texture->GetScaledHeightDouble() : forceHeight;
double dcx = clip[0] == 0 ? 0 : dx + clip[0] - texture->GetScaledLeftOffsetDouble();
double dcy = clip[1] == 0 ? 0 : dy + clip[1] - texture->GetScaledTopOffsetDouble();
double dcr = clip[2] == 0 ? INT_MAX : dx + w - clip[2] - texture->GetScaledLeftOffsetDouble();
double dcb = clip[3] == 0 ? INT_MAX : dy + h - clip[3] - texture->GetScaledTopOffsetDouble();
if(Scaled)
{
if(clip[0] != 0 || clip[1] != 0)
{
screen->VirtualToRealCoords(dcx, dcy, tmp, tmp, barW, barH, true);
if (clip[0] == 0) dcx = 0;
if (clip[1] == 0) dcy = 0;
}
if(clip[2] != 0 || clip[3] != 0 || clearDontDraw)
screen->VirtualToRealCoords(dcr, dcb, tmp, tmp, barW, barH, true);
screen->VirtualToRealCoords(dx, dy, w, h, barW, barH, true);
}
else
{
dy += 200 - barH;
dcy += 200 - barH;
dcb += 200 - barH;
}
if(clearDontDraw)
screen->Clear(static_cast<int>(MAX<double>(dx, dcx)), static_cast<int>(MAX<double>(dy, dcy)), static_cast<int>(MIN<double>(dcr,w+MAX<double>(dx, dcx))), static_cast<int>(MIN<double>(dcb,MAX<double>(dy, dcy)+h)), GPalette.BlackIndex, 0);
else
{
if(alphaMap)
{
screen->DrawTexture(texture, dx, dy,
DTA_DestWidthF, w,
DTA_DestHeightF, h,
DTA_ClipLeft, static_cast<int>(dcx),
DTA_ClipTop, static_cast<int>(dcy),
DTA_ClipRight, static_cast<int>(MIN<double>(INT_MAX, dcr)),
DTA_ClipBottom, static_cast<int>(MIN<double>(INT_MAX, dcb)),
DTA_TranslationIndex, translate ? GetTranslation() : 0,
DTA_ColorOverlay, dim ? DIM_OVERLAY : 0,
DTA_CenterBottomOffset, (offsetflags & SBarInfoCommand::CENTER_BOTTOM) == SBarInfoCommand::CENTER_BOTTOM,
DTA_Alpha, Alpha,
DTA_AlphaChannel, alphaMap,
DTA_FillColor, 0,
TAG_DONE);
}
else
{
screen->DrawTexture(texture, dx, dy,
DTA_DestWidthF, w,
DTA_DestHeightF, h,
DTA_ClipLeft, static_cast<int>(dcx),
DTA_ClipTop, static_cast<int>(dcy),
DTA_ClipRight, static_cast<int>(MIN<double>(INT_MAX, dcr)),
DTA_ClipBottom, static_cast<int>(MIN<double>(INT_MAX, dcb)),
DTA_TranslationIndex, translate ? GetTranslation() : 0,
DTA_ColorOverlay, dim ? DIM_OVERLAY : 0,
DTA_CenterBottomOffset, (offsetflags & SBarInfoCommand::CENTER_BOTTOM) == SBarInfoCommand::CENTER_BOTTOM,
DTA_Alpha, Alpha,
TAG_DONE);
}
}
}
else
{
double rx, ry, rcx=0, rcy=0, rcr=INT_MAX, rcb=INT_MAX;
DVector2 Scale = wrapper->GetHUDScale();
adjustRelCenter(x.RelCenter(), y.RelCenter(), dx, dy, rx, ry, Scale.X, Scale.Y);
// Translation: No high res.
bool xright = *x < 0 && !x.RelCenter();
bool ybot = *y < 0 && !y.RelCenter();
w = (forceWidth < 0 ? texture->GetScaledWidthDouble() : forceWidth);
h = (forceHeight < 0 ? texture->GetScaledHeightDouble() : forceHeight);
if(vid_fps && rx < 0 && ry >= 0)
ry += 10;
rx *= Scale.X;
ry *= Scale.Y;
w *= Scale.X;
h *= Scale.Y;
if(xright)
rx = SCREENWIDTH + rx;
if(ybot)
ry = SCREENHEIGHT + ry;
// Check for clipping
if(clip[0] != 0 || clip[1] != 0 || clip[2] != 0 || clip[3] != 0)
{
rcx = clip[0] == 0 ? 0 : rx+((clip[0] - texture->GetScaledLeftOffsetDouble())*Scale.X);
rcy = clip[1] == 0 ? 0 : ry+((clip[1] - texture->GetScaledTopOffsetDouble())*Scale.Y);
rcr = clip[2] == 0 ? INT_MAX : rx+w-((clip[2] + texture->GetScaledLeftOffsetDouble())*Scale.X);
rcb = clip[3] == 0 ? INT_MAX : ry+h-((clip[3] + texture->GetScaledTopOffsetDouble())*Scale.Y);
}
if(clearDontDraw)
screen->Clear(static_cast<int>(rcx), static_cast<int>(rcy), static_cast<int>(MIN<double>(rcr, rcx+w)), static_cast<int>(MIN<double>(rcb, rcy+h)), GPalette.BlackIndex, 0);
else
{
if(alphaMap)
{
screen->DrawTexture(texture, rx, ry,
DTA_DestWidthF, w,
DTA_DestHeightF, h,
DTA_ClipLeft, static_cast<int>(rcx),
DTA_ClipTop, static_cast<int>(rcy),
DTA_ClipRight, static_cast<int>(rcr),
DTA_ClipBottom, static_cast<int>(rcb),
DTA_TranslationIndex, translate ? GetTranslation() : 0,
DTA_ColorOverlay, dim ? DIM_OVERLAY : 0,
DTA_CenterBottomOffset, (offsetflags & SBarInfoCommand::CENTER_BOTTOM) == SBarInfoCommand::CENTER_BOTTOM,
DTA_Alpha, Alpha,
DTA_AlphaChannel, alphaMap,
DTA_FillColor, 0,
TAG_DONE);
}
else
{
screen->DrawTexture(texture, rx, ry,
DTA_DestWidthF, w,
DTA_DestHeightF, h,
DTA_ClipLeft, static_cast<int>(rcx),
DTA_ClipTop, static_cast<int>(rcy),
DTA_ClipRight, static_cast<int>(rcr),
DTA_ClipBottom, static_cast<int>(rcb),
DTA_TranslationIndex, translate ? GetTranslation() : 0,
DTA_ColorOverlay, dim ? DIM_OVERLAY : 0,
DTA_CenterBottomOffset, (offsetflags & SBarInfoCommand::CENTER_BOTTOM) == SBarInfoCommand::CENTER_BOTTOM,
DTA_Alpha, Alpha,
TAG_DONE);
}
}
}
}
void DrawString(FFont *font, const char* cstring, SBarInfoCoordinate x, SBarInfoCoordinate y, int xOffset, int yOffset, double Alpha, bool fullScreenOffsets, EColorRange translation, int spacing=0, bool drawshadow=false, int shadowX=2, int shadowY=2) const
{
x += spacing;
double ax = *x;
double ay = *y;
DVector2 Scale;
const uint8_t* str = (const uint8_t*) cstring;
const EColorRange boldTranslation = EColorRange(translation ? translation - 1 : NumTextColors - 1);
int fontcolor = translation;
if(fullScreenOffsets)
{
Scale = wrapper->GetHUDScale();
adjustRelCenter(x.RelCenter(), y.RelCenter(), *x, *y, ax, ay, Scale.X, Scale.Y);
}
else
{
Scale = { 1.,1. };
}
while(*str != '\0')
{
if(*str == ' ')
{
if(script->spacingCharacter == '\0')
ax += font->GetSpaceWidth();
else
ax += font->GetCharWidth((unsigned char) script->spacingCharacter);
str++;
continue;
}
else if(*str == TEXTCOLOR_ESCAPE)
{
EColorRange newColor = V_ParseFontColor(++str, translation, boldTranslation);
if(newColor != CR_UNDEFINED)
fontcolor = newColor;
continue;
}
int width;
if(script->spacingCharacter == '\0') //No monospace?
width = font->GetCharWidth((unsigned char) *str);
else
width = font->GetCharWidth((unsigned char) script->spacingCharacter);
FTexture* c = font->GetChar((unsigned char) *str, &width);
if(c == NULL) //missing character.
{
str++;
continue;
}
int character = (unsigned char)*str;
if(script->spacingCharacter == '\0') //If we are monospaced lets use the offset
ax += (c->LeftOffset+1); //ignore x offsets since we adapt to character size
double rx, ry, rw, rh;
rx = ax + xOffset;
ry = ay + yOffset;
rw = c->GetScaledWidthDouble();
rh = c->GetScaledHeightDouble();
if(script->spacingCharacter != '\0')
{
double spacingSize = font->GetCharWidth((unsigned char) script->spacingCharacter);
switch(script->spacingAlignment)
{
default:
break;
case SBarInfo::ALIGN_CENTER:
rx += (spacingSize/2)-(rw/2);
break;
case SBarInfo::ALIGN_RIGHT:
rx += spacingSize-rw;
break;
}
}
if(!fullScreenOffsets)
{
int barW = wrapper->HorizontalResolution, barH = wrapper->VerticalResolution;
rx += wrapper->ST_X;
ry += wrapper->ST_Y - (Scaled ? barH : 200) + script->height;
if(Scaled)
screen->VirtualToRealCoords(rx, ry, rw, rh, barW, barH, true);
else
{
ry += (200 - barH);
}
}
else
{
if(vid_fps && ax < 0 && ay >= 0)
ry += 10;
bool xright = rx < 0;
bool ybot = ry < 0;
rx *= Scale.X;
ry *= Scale.Y;
rw *= Scale.X;
rh *= Scale.Y;
if(xright)
rx = SCREENWIDTH + rx;
if(ybot)
ry = SCREENHEIGHT + ry;
}
if(drawshadow)
{
double salpha = (Alpha *HR_SHADOW);
double srx = rx + (shadowX*Scale.X);
double sry = ry + (shadowY*Scale.Y);
screen->DrawChar(font, CR_UNTRANSLATED, srx, sry, character,
DTA_DestWidthF, rw,
DTA_DestHeightF, rh,
DTA_Alpha, salpha,
DTA_FillColor, 0,
TAG_DONE);
}
screen->DrawChar(font, fontcolor, rx, ry, character,
DTA_DestWidthF, rw,
DTA_DestHeightF, rh,
DTA_Alpha, Alpha,
TAG_DONE);
if(script->spacingCharacter == '\0')
ax += width + spacing - (c->LeftOffset+1);
else //width gets changed at the call to GetChar()
ax += font->GetCharWidth((unsigned char) script->spacingCharacter) + spacing;
str++;
}
}
uint32_t GetTranslation() const
{
if(gameinfo.gametype & GAME_Raven)
return TRANSLATION(TRANSLATION_PlayersExtra, int(CPlayer - players));
return TRANSLATION(TRANSLATION_Players, int(CPlayer - players));
}
AInventory *ammo1, *ammo2;
int ammocount1, ammocount2;
AInventory *armor;
FImageCollection Images;
unsigned int invBarOffset;
player_t *CPlayer = nullptr;
DBaseStatusBar *wrapper;
bool Scaled;
private:
SBarInfo *script;
int pendingPopup;
int currentPopup;
int lastHud;
bool scalingWasForced;
SBarInfoMainBlock *lastInventoryBar;
SBarInfoMainBlock *lastPopup;
};
void SBarInfoMainBlock::DrawAux(const SBarInfoMainBlock *block, DSBarInfo *statusBar, int xOffset, int yOffset, double alpha)
{
// Popups can also be forced to scale
bool rescale = false;
if(ForceScaled())
{
if(FullScreenOffsets())
{
rescale = true;
statusBar->wrapper->ForceHUDScale(true);
}
else if(!statusBar->Scaled)
{
rescale = true;
statusBar->wrapper->SetScaled(true, true);
}
}
Draw(block, statusBar, xOffset, yOffset, alpha);
if(rescale)
{
if(FullScreenOffsets())
statusBar->wrapper->ForceHUDScale(false);
else
statusBar->wrapper->SetScaled(false);
}
}
#include "sbarinfo_commands.cpp"
//==========================================================================
//
// This routes all access through a scripted class because a native
// side class of an entire scripted hierarchy would be a major obstacle.
//
//==========================================================================
DEFINE_ACTION_FUNCTION(DSBarInfo, Destroy)
{
PARAM_SELF_STRUCT_PROLOGUE(DSBarInfo);
delete self;
return 0;
}
DEFINE_ACTION_FUNCTION(DSBarInfo, SetScaled)
{
PARAM_SELF_STRUCT_PROLOGUE(DSBarInfo);
PARAM_BOOL(scale);
self->_SetScaled(scale);
return 0;
}
DEFINE_ACTION_FUNCTION(DSBarInfo, AttachToPlayer)
{
PARAM_SELF_STRUCT_PROLOGUE(DSBarInfo);
PARAM_POINTER(player, player_t);
self->_AttachToPlayer(player);
return 0;
}
DEFINE_ACTION_FUNCTION(DSBarInfo, Draw)
{
PARAM_SELF_STRUCT_PROLOGUE(DSBarInfo);
PARAM_INT(State);
self->_Draw((EHudState)State);
return 0;
}
DEFINE_ACTION_FUNCTION(DSBarInfo, NewGame)
{
PARAM_SELF_STRUCT_PROLOGUE(DSBarInfo);
self->_NewGame();
return 0;
}
DEFINE_ACTION_FUNCTION(DSBarInfo, MustDrawLog)
{
PARAM_SELF_STRUCT_PROLOGUE(DSBarInfo);
PARAM_INT(State);
ACTION_RETURN_BOOL(self->_MustDrawLog((EHudState)State));
}
DEFINE_ACTION_FUNCTION(DSBarInfo, Tick)
{
PARAM_SELF_STRUCT_PROLOGUE(DSBarInfo);
self->_Tick();
return 0;
}
DEFINE_ACTION_FUNCTION(DSBarInfo, FlashItem)
{
PARAM_SELF_STRUCT_PROLOGUE(DSBarInfo);
PARAM_CLASS(w, AInventory);
self->_FlashItem(w);
return 0;
}
DEFINE_ACTION_FUNCTION(DSBarInfo, ShowPop)
{
PARAM_SELF_STRUCT_PROLOGUE(DSBarInfo);
PARAM_INT(State);
self->_ShowPop(State);
return 0;
}
DBaseStatusBar *CreateCustomStatusBar(int scriptno)
{
auto script = SBarInfoScript[scriptno];
if (script == NULL)
I_FatalError("Tried to create a status bar with no script!");
auto sbar = (DBaseStatusBar*)PClass::FindClass("SBarInfoWrapper")->CreateNew();
auto core = new DSBarInfo(sbar, script);
sbar->PointerVar<DSBarInfo>("core") = core;
sbar->SetSize(script->height, script->_resW, script->_resH);
core->_SetScaled(sbar->Scaled);
sbar->CompleteBorder = script->completeBorder;
return sbar;
}