qzdoom/src/g_shared/sbarinfo.cpp
Christoph Oelckers 5ffda41c7e - Added Blzut3's SBARINFO submission.
SVN r613 (trunk)
2007-12-20 22:35:02 +00:00

1644 lines
43 KiB
C++

#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 "r_local.h"
#include "m_swap.h"
#include "a_keys.h"
#include "templates.h"
#include "i_system.h"
#include "sbarinfo.h"
#include "sc_man.h"
#define ST_FACETIME (TICRATE/2)
#define ST_PAINTIME (TICRATE)
#define ST_GRINTIME (TICRATE*2)
#define ST_RAMPAGETIME (TICRATE*2)
#define ST_XDTHTIME (TICRATE*(3/2))
#define ST_NUMFACES 80 //9 levels with 8 faces each, 1 god, 1 death, 6 xdeath
EXTERN_CVAR(Int, fraglimit)
SBarInfo SBarInfoScript;
enum //gametype flags
{
GAMETYPE_SINGLEPLAYER = 1,
GAMETYPE_COOPERATIVE = 2,
GAMETYPE_DEATHMATCH = 4,
GAMETYPE_TEAMGAME = 8,
};
enum //drawimage flags
{
DRAWIMAGE_PLAYERICON = 1,
DRAWIMAGE_AMMO1 = 2,
DRAWIMAGE_AMMO2 = 4,
DRAWIMAGE_TRANSLATABLE = 8,
DRAWIMAGE_WEAPONSLOT = 16,
DRAWIMAGE_SWITCHABLE_AND = 32,
DRAWIMAGE_INVULNERABILITY = 64,
DRAWIMAGE_OFFSET_CENTER = 128,
};
enum //drawnumber flags
{
DRAWNUMBER_HEALTH = 1,
DRAWNUMBER_ARMOR = 2,
DRAWNUMBER_AMMO1 = 4,
DRAWNUMBER_AMMO2 = 8,
DRAWNUMBER_AMMO = 16,
DRAWNUMBER_AMMOCAPACITY = 32,
DRAWNUMBER_FRAGS = 64,
DRAWNUMBER_INVENTORY = 128,
};
enum //drawbar flags (will go into special2)
{
DRAWBAR_HORIZONTAL = 1,
DRAWBAR_REVERSE = 2,
DRAWBAR_COMPAREDEFAULTS = 4,
};
enum //drawselectedinventory flags
{
DRAWSELECTEDINVENTORY_ALTERNATEONEMPTY = 1,
};
enum //drawinventorybar flags
{
DRAWINVENTORYBAR_ALWAYSSHOW = 1,
DRAWINVENTORYBAR_NOARTIBOX = 2,
};
static const char *SBarInfoTopLevel[] =
{
"base",
"height",
"interpolatehealth",
"statusbar",
NULL
};
enum
{
SBARINFO_BASE,
SBARINFO_HEIGHT,
SBARINFO_INTERPOLATEHEALTH,
SBARINFO_STATUSBAR,
};
static const char *StatusBars[] =
{
"none",
"fullscreen",
"normal",
"automap",
"inventory",
"inventoryfullscreen",
NULL
};
enum
{
STBAR_NONE,
STBAR_FULLSCREEN,
STBAR_NORMAL,
STBAR_AUTOMAP,
STBAR_INVENTORY,
STBAR_INVENTORYFULLSCREEN,
};
static const char *SBarInfoRoutineLevel[] =
{
"drawimage",
"drawnumber",
"drawswitchableimage",
"drawmugshot",
"drawselectedinventory",
"drawinventorybar",
"drawbar",
"gamemode",
"playerclass",
NULL
};
enum
{
SBARINFO_DRAWIMAGE,
SBARINFO_DRAWNUMBER,
SBARINFO_DRAWSWITCHABLEIMAGE,
SBARINFO_DRAWMUGSHOT,
SBARINFO_DRAWSELECTEDINVENTORY,
SBARINFO_DRAWINVENTORYBAR,
SBARINFO_DRAWBAR,
SBARINFO_GAMEMODE,
SBARINFO_PLAYERCLASS,
};
//Laz Bar Script Reader
int SBarInfo::ParseSBarInfo(int lump)
{
int stbar = GAME_Any;
SC_OpenLumpNum(lump, Wads.GetLumpFullName(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); //zip/pk3
//Do a normal wad lookup.
if (lump==-1 && strlen(sc_String) <= 8 && !strchr(sc_String, '/'))
lump = Wads.CheckNumForName(sc_String);
if (lump==-1)
SC_ScriptError("Lump '%s' not found", sc_String);
SC_SaveScriptState();
ParseSBarInfo(lump);
SC_RestoreScriptState();
continue;
}
switch(SC_MustMatchString(SBarInfoTopLevel))
{
case SBARINFO_BASE:
SC_MustGetToken(TK_Identifier);
if(SC_Compare("Doom"))
stbar = GAME_Doom;
else if(SC_Compare("Heretic"))
stbar = GAME_Heretic;
else if(SC_Compare("Hexen"))
stbar = GAME_Hexen;
else if(SC_Compare("Strife"))
stbar = GAME_Strife;
else if(SC_Compare("None"))
stbar = GAME_Any;
else
SC_ScriptError("Bad game name: %s", sc_String);
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_TokenMustBe(TK_False);
interpolateHealth = false;
}
if(SC_CheckToken(',')) //speed param
{
SC_MustGetToken(TK_IntConst);
this->interpolationSpeed = sc_Number;
}
SC_MustGetToken(';');
break;
case SBARINFO_STATUSBAR:
{
SC_MustGetToken(TK_Identifier);
int barNum = SC_MustMatchString(StatusBars);
SC_MustGetToken('{');
if(barNum == STBAR_AUTOMAP)
{
automapbar = true;
}
ParseSBarInfoBlock(this->huds[barNum]);
break;
}
}
}
SC_SetCMode(false);
SC_Close();
return stbar;
}
void SBarInfo::ParseSBarInfoBlock(SBarInfoBlock &block)
{
while(SC_CheckToken(TK_Identifier))
{
SBarInfoCommand cmd = *new SBarInfoCommand();
switch(cmd.type = SC_MustMatchString(SBarInfoRoutineLevel))
{
case SBARINFO_DRAWSWITCHABLEIMAGE:
SC_MustGetToken(TK_Identifier);
if(SC_Compare("weaponslot"))
{
cmd.flags = DRAWIMAGE_WEAPONSLOT;
SC_MustGetToken(TK_IntConst);
cmd.value = sc_Number;
}
else if(SC_Compare("invulnerable"))
{
cmd.flags = DRAWIMAGE_INVULNERABILITY;
}
else
{
cmd.setString(sc_String, 0);
const PClass* item = PClass::FindClass(sc_String);
if(item == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(item)) //must be a kind of Inventory
{
SC_ScriptError("'%s' is not a type of inventory item.", sc_String);
}
}
if(SC_CheckToken(TK_AndAnd))
{
cmd.flags += DRAWIMAGE_SWITCHABLE_AND;
SC_MustGetToken(TK_Identifier);
cmd.setString(sc_String, 1);
const PClass* item = PClass::FindClass(sc_String);
if(item == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(item)) //must be a kind of Inventory
{
SC_ScriptError("'%s' is not a type of inventory item.", sc_String);
}
SC_MustGetToken(',');
SC_MustGetToken(TK_StringConst);
cmd.special = newImage(sc_String);
SC_MustGetToken(',');
SC_MustGetToken(TK_StringConst);
cmd.special2 = newImage(sc_String);
SC_MustGetToken(',');
SC_MustGetToken(TK_StringConst);
cmd.special3 = newImage(sc_String);
SC_MustGetToken(',');
}
else
{
SC_MustGetToken(',');
SC_MustGetToken(TK_StringConst);
cmd.special = newImage(sc_String);
SC_MustGetToken(',');
}
case SBARINFO_DRAWIMAGE:
{
bool getImage = true;
if(SC_CheckToken(TK_Identifier))
{
getImage = false;
if(SC_Compare("playericon"))
cmd.flags += DRAWIMAGE_PLAYERICON;
else if(SC_Compare("ammoicon1"))
cmd.flags += DRAWIMAGE_AMMO1;
else if(SC_Compare("ammoicon2"))
cmd.flags += DRAWIMAGE_AMMO2;
else if(SC_Compare("translatable"))
{
cmd.flags += DRAWIMAGE_TRANSLATABLE;
getImage = true;
}
else
SC_ScriptError("Unknown imagetype '%s'.", sc_String);
}
if(getImage)
{
SC_MustGetToken(TK_StringConst);
cmd.sprite = newImage(sc_String);
}
SC_MustGetToken(',');
SC_MustGetToken(TK_IntConst);
cmd.x = sc_Number;
SC_MustGetToken(',');
SC_MustGetToken(TK_IntConst);
cmd.y = sc_Number - (200 - SBarInfoScript.height); //the position should be absolute on the screen.
if(SC_CheckToken(','))
{
SC_MustGetToken(TK_Identifier);
if(SC_Compare("center"))
cmd.flags += DRAWIMAGE_OFFSET_CENTER;
else
SC_ScriptError("Expected 'center' got '%s' instead.", sc_String);
}
SC_MustGetToken(';');
break;
}
case SBARINFO_DRAWNUMBER:
SC_MustGetToken(TK_IntConst);
cmd.special = sc_Number;
SC_MustGetToken(',');
SC_MustGetToken(TK_Identifier);
cmd.font = V_GetFont(sc_String);
SC_MustGetToken(',');
SC_MustGetToken(TK_Identifier);
cmd.translation = this->GetTranslation(sc_String);
SC_MustGetToken(',');
if(SC_CheckToken(TK_IntConst))
{
cmd.value = sc_Number;
SC_MustGetToken(',');
}
else
{
SC_MustGetToken(TK_Identifier);
if(SC_Compare("health"))
cmd.flags = DRAWNUMBER_HEALTH;
else if(SC_Compare("armor"))
cmd.flags = DRAWNUMBER_ARMOR;
else if(SC_Compare("ammo1"))
cmd.flags = DRAWNUMBER_AMMO1;
else if(SC_Compare("ammo2"))
cmd.flags = DRAWNUMBER_AMMO2;
else if(SC_Compare("ammo")) //request the next string to be an ammo type
{
SC_MustGetToken(TK_Identifier);
cmd.setString(sc_String, 0);
cmd.flags = DRAWNUMBER_AMMO;
const PClass* ammo = PClass::FindClass(sc_String);
if(ammo == NULL || !PClass::FindClass("Ammo")->IsAncestorOf(ammo)) //must be a kind of ammo
{
SC_ScriptError("'%s' is not a type of ammo.", sc_String);
}
}
else if(SC_Compare("ammocapacity"))
{
SC_MustGetToken(TK_Identifier);
cmd.setString(sc_String, 0);
cmd.flags = DRAWNUMBER_AMMOCAPACITY;
const PClass* ammo = PClass::FindClass(sc_String);
if(ammo == NULL || !PClass::FindClass("Ammo")->IsAncestorOf(ammo)) //must be a kind of ammo
{
SC_ScriptError("'%s' is not a type of ammo.", sc_String);
}
}
else if(SC_Compare("frags"))
cmd.flags = DRAWNUMBER_FRAGS;
else
{
cmd.flags = DRAWNUMBER_INVENTORY;
SC_MustGetToken(TK_Identifier);
cmd.setString(sc_String, 0);
const PClass* item = PClass::FindClass(sc_String);
if(item == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(item)) //must be a kind of ammo
{
SC_ScriptError("'%s' is not a type of inventory item.", sc_String);
}
}
SC_MustGetToken(',');
}
SC_MustGetToken(TK_IntConst);
cmd.x = sc_Number;
SC_MustGetToken(',');
SC_MustGetToken(TK_IntConst);
cmd.y = sc_Number - (200 - SBarInfoScript.height);
if(SC_CheckToken(','))
{
SC_MustGetToken(TK_IntConst);
cmd.special2 = sc_Number;
}
SC_MustGetToken(';');
break;
case SBARINFO_DRAWMUGSHOT:
SC_MustGetToken(TK_StringConst);
cmd.setString(sc_String, 0, 3, true);
SC_MustGetToken(',');
SC_MustGetToken(TK_IntConst); //accuracy
cmd.special = sc_Number;
SC_MustGetToken(',');
SC_MustGetToken(TK_IntConst); //xdeath face (could be later used as flags
cmd.special2 = sc_Number;
SC_MustGetToken(',');
SC_MustGetToken(TK_IntConst);
cmd.x = sc_Number;
SC_MustGetToken(',');
SC_MustGetToken(TK_IntConst);
cmd.y = sc_Number - (200 - SBarInfoScript.height);
SC_MustGetToken(';');
break;
case SBARINFO_DRAWSELECTEDINVENTORY:
{
bool alternateonempty = false;
SC_MustGetToken(TK_Identifier);
if(SC_Compare("alternateonempty"))
{
alternateonempty = true;
cmd.flags = DRAWSELECTEDINVENTORY_ALTERNATEONEMPTY;
SC_MustGetToken(',');
SC_MustGetToken(TK_Identifier);
}
cmd.font = V_GetFont(sc_String);
SC_MustGetToken(',');
SC_MustGetToken(TK_IntConst);
cmd.x = sc_Number;
SC_MustGetToken(',');
SC_MustGetToken(TK_IntConst);
cmd.y = sc_Number - (200 - SBarInfoScript.height);
cmd.special2 = cmd.x + 30;
cmd.special3 = cmd.y + 24;
cmd.translation = CR_GOLD;
if(SC_CheckToken(',')) //more font information
{
SC_MustGetToken(TK_IntConst);
cmd.special2 = sc_Number;
SC_MustGetToken(',');
SC_MustGetToken(TK_IntConst);
cmd.special3 = sc_Number - (200 - SBarInfoScript.height);
if(SC_CheckToken(','))
{
SC_MustGetToken(TK_Identifier);
cmd.translation = this->GetTranslation(sc_String);
if(SC_CheckToken(','))
{
SC_MustGetToken(TK_IntConst);
cmd.special4 = sc_Number;
}
}
}
if(alternateonempty)
{
SC_MustGetToken('{');
this->ParseSBarInfoBlock(cmd.subBlock);
}
else
{
SC_MustGetToken(';');
}
break;
}
case SBARINFO_DRAWINVENTORYBAR:
SC_MustGetToken(TK_Identifier);
if(SC_Compare("Heretic"))
{
cmd.special = GAME_Heretic;
}
if(SC_Compare("Doom") || SC_Compare("Heretic"))
{
SC_MustGetToken(',');
while(SC_CheckToken(TK_Identifier))
{
if(SC_Compare("alwaysshow"))
{
cmd.flags += DRAWINVENTORYBAR_ALWAYSSHOW;
}
else if(SC_Compare("noatribox"))
{
cmd.flags += DRAWINVENTORYBAR_NOARTIBOX;
}
else
{
SC_ScriptError("Unknown flag '%s'.", sc_String);
}
SC_MustGetToken(',');
}
SC_MustGetToken(TK_IntConst);
cmd.value = sc_Number;
SC_MustGetToken(',');
SC_MustGetToken(TK_Identifier);
cmd.font = V_GetFont(sc_String);
}
else
{
SC_ScriptError("Unkown style '%s'.", sc_String);
}
SC_MustGetToken(',');
SC_MustGetNumber();
cmd.x = sc_Number;
SC_MustGetToken(',');
SC_MustGetNumber();
cmd.y = sc_Number - (200 - SBarInfoScript.height);
cmd.special2 = cmd.x + 26;
cmd.special3 = cmd.y + 22;
cmd.translation = CR_GOLD;
if(SC_CheckToken(',')) //more font information
{
SC_MustGetToken(TK_IntConst);
cmd.special2 = sc_Number;
SC_MustGetToken(',');
SC_MustGetToken(TK_IntConst);
cmd.special3 = sc_Number - (200 - SBarInfoScript.height);
if(SC_CheckToken(','))
{
SC_MustGetToken(TK_Identifier);
cmd.translation = this->GetTranslation(sc_String);
if(SC_CheckToken(','))
{
SC_MustGetToken(TK_IntConst);
cmd.special4 = sc_Number;
}
}
}
SC_MustGetToken(';');
break;
case SBARINFO_DRAWBAR:
SC_MustGetToken(TK_StringConst);
cmd.sprite = newImage(sc_String);
SC_MustGetToken(',');
SC_MustGetToken(TK_StringConst);
cmd.special = newImage(sc_String);
SC_MustGetToken(',');
SC_MustGetToken(TK_Identifier); //yeah, this is the same as drawnumber, there might be a better way to copy it...
if(SC_Compare("health"))
{
cmd.flags = DRAWNUMBER_HEALTH;
if(SC_CheckToken(TK_Identifier)) //comparing reference
{
cmd.setString(sc_String, 0);
const PClass* item = PClass::FindClass(sc_String);
if(item == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(item)) //must be a kind of inventory
{
SC_ScriptError("'%s' is not a type of inventory item.", sc_String);
}
}
else
cmd.special2 = DRAWBAR_COMPAREDEFAULTS;
}
else if(SC_Compare("armor"))
{
cmd.flags = DRAWNUMBER_ARMOR;
if(SC_CheckToken(TK_Identifier))
{
cmd.setString(sc_String, 0);
const PClass* item = PClass::FindClass(sc_String);
if(item == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(item)) //must be a kind of inventory
{
SC_ScriptError("'%s' is not a type of inventory item.", sc_String);
}
}
else
cmd.special2 = DRAWBAR_COMPAREDEFAULTS;
}
else if(SC_Compare("ammo1"))
cmd.flags = DRAWNUMBER_AMMO1;
else if(SC_Compare("ammo2"))
cmd.flags = DRAWNUMBER_AMMO2;
else if(SC_Compare("ammo")) //request the next string to be an ammo type
{
SC_MustGetToken(TK_Identifier);
cmd.setString(sc_String, 0);
cmd.flags = DRAWNUMBER_AMMO;
const PClass* ammo = PClass::FindClass(sc_String);
if(ammo == NULL || !PClass::FindClass("Ammo")->IsAncestorOf(ammo)) //must be a kind of ammo
{
SC_ScriptError("'%s' is not a type of ammo.", sc_String);
}
}
else if(SC_Compare("frags"))
cmd.flags = DRAWNUMBER_FRAGS;
else
{
cmd.flags = DRAWNUMBER_INVENTORY;
cmd.setString(sc_String, 0);
const PClass* item = PClass::FindClass(sc_String);
if(item == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(item)) //must be a kind of ammo
{
SC_ScriptError("'%s' is not a type of inventory item.", sc_String);
}
}
SC_MustGetToken(',');
SC_MustGetToken(TK_Identifier);
if(SC_Compare("horizontal"))
cmd.special2 += DRAWBAR_HORIZONTAL;
else if(!SC_Compare("vertical"))
SC_ScriptError("Unknown direction '%s'.", sc_String);
SC_MustGetToken(',');
if(SC_CheckToken(TK_Identifier))
{
if(!SC_Compare("reverse"))
{
SC_ScriptError("Exspected 'reverse', got '%s' instead.", sc_String);
}
cmd.special2 += DRAWBAR_REVERSE;
SC_MustGetToken(',');
}
SC_MustGetToken(TK_IntConst);
cmd.x = sc_Number;
SC_MustGetToken(',');
SC_MustGetToken(TK_IntConst);
cmd.y = sc_Number - (200 - SBarInfoScript.height);
SC_MustGetToken(';');
break;
case SBARINFO_GAMEMODE:
while(SC_CheckToken(TK_Identifier))
{
if(SC_Compare("singleplayer"))
cmd.flags += GAMETYPE_SINGLEPLAYER;
else if(SC_Compare("cooperative"))
cmd.flags += GAMETYPE_COOPERATIVE;
else if(SC_Compare("deathmatch"))
cmd.flags += GAMETYPE_DEATHMATCH;
else if(SC_Compare("teamgame"))
cmd.flags += GAMETYPE_TEAMGAME;
else
SC_ScriptError("Unknown gamemode: %s", sc_String);
if(SC_CheckToken('{'))
break;
SC_MustGetToken(',');
}
this->ParseSBarInfoBlock(cmd.subBlock);
break;
case SBARINFO_PLAYERCLASS:
cmd.special = cmd.special2 = cmd.special3 = -1;
for(int i = 0;i < 3 && SC_CheckToken(TK_Identifier);i++) //up to 3 classes
{
for(unsigned int c = 0;c < PlayerClasses.Size();c++)
{
if(stricmp(sc_String, PlayerClasses[c].Type->Meta.GetMetaString(APMETA_DisplayName)) == 0)
{
if(i == 0)
cmd.special = c;
else if(i == 1)
cmd.special2 = c;
else //should be 2
cmd.special3 = c;
if(SC_CheckToken('{') || i == 2)
break;
SC_MustGetToken(',');
}
}
}
this->ParseSBarInfoBlock(cmd.subBlock);
break;
}
block.commands.Push(cmd);
}
SC_MustGetToken('}');
}
int SBarInfo::newImage(const char* patchname)
{
if(stricmp(patchname, "nullimage") == 0)
{
return -1;
}
// if(strlen(patchname) > 8)
// {
// SC_ScriptError("Graphic names can not be greater then 8 characters long.");
// }
for(unsigned int i = 0;i < this->Images.Size();i++) //did we already load it?
{
if(stricmp(this->Images[i], patchname) == 0)
{
return i;
}
}
char * name = new char[9];
memset(name, '\0', 9);
memcpy(name, patchname, 8);
this->Images.Push(name);
return this->Images.Size() - 1;
}
//converts a string into a tranlation.
EColorRange SBarInfo::GetTranslation(char* translation)
{
EColorRange returnVal = CR_UNTRANSLATED;
char* namedTranslation = new char[strlen(translation)+3]; //we must send in "[translation]"
sprintf(namedTranslation, "[%s]", translation);
if((returnVal = V_ParseFontColor((const BYTE*&) namedTranslation, CR_UNTRANSLATED, CR_UNTRANSLATED)) == CR_UNDEFINED)
{
SC_ScriptError("Missing definition for color %s.", translation);
}
return returnVal;
}
SBarInfo::SBarInfo() //make results more predicable
{
automapbar = false;
interpolateHealth = false;
interpolationSpeed = 8;
height = 0;
}
void SBarInfoCommand::setString(const char* source, int strnum, int maxlength, bool exact)
{
if(!exact)
{
if(maxlength != -1 && strlen(source) > (unsigned int) maxlength)
{
SC_ScriptError("%s is greater than %d characters.", source, maxlength);
return;
}
}
else
{
if(maxlength != -1 && strlen(source) != (unsigned int) maxlength)
{
SC_ScriptError("%s must be %d characters.", source, maxlength);
return;
}
}
this->string[strnum] = new char[strlen(source) + 1];
memset(this->string[strnum], '\0', strlen(source) + 1);
memcpy(this->string[strnum], source, strlen(source));
}
SBarInfoCommand::SBarInfoCommand() //sets the default values for more predicable behavior
{
type = 0;
special = 0;
special2 = 0;
special3 = 0;
special4 = 0;
flags = 0;
x = 0;
y = 0;
value = 0;
sprite = 0;
string[0] = "";
string[1] = "";
translation = CR_UNTRANSLATED;
font = V_GetFont("CONFONT");
}
enum
{
ST_FACENORMALRIGHT,
ST_FACENORMAL,
ST_FACENORMALLEFT,
ST_FACEPAINRIGHT,
ST_FACEPAINLEFT,
ST_FACEOUCH,
ST_FACEGRIN,
ST_FACEPAIN,
ST_FACEGOD = 72,
ST_FACEDEAD = 73,
ST_FACEXDEAD = 74,
};
enum
{
imgARTIBOX,
imgSELECTBOX,
imgINVLFGEM1,
imgINVLFGEM2,
imgINVRTGEM1,
imgINVRTGEM2,
};
//The next class allows us to draw bars
class FBarTexture : public FTexture
{
public:
void Unload()
{
if(image != NULL)
{
image->Unload ();
}
}
const BYTE *GetColumn(unsigned int column, const Span **spans_out)
{
if (column > (unsigned int) Width)
{
column = Width;
}
image->GetColumn(column, spans_out);
return Pixels + column*Height;
}
const BYTE *GetPixels()
{
return Pixels;
}
void PrepareTexture(FTexture* bar, FTexture* bg, int value, bool horizontal, bool reverse)
{
if(value < 0)
value = 0;
else if(value > 100)
value = 100;
image = bar;
//width and height are supposed to be the end result, Width and Height are the input image. If that makes sense.
int width = Width = bar->GetWidth();
int height = Height = bar->GetHeight();
if(horizontal)
{
width = (int) (((double) width/100)*value);
}
Pixels = new BYTE[Width*Height];
bar->CopyToBlock(Pixels, Width, Height, 0, 0); //draw the bar
int run = bar->GetHeight() - (int) (((double) height/100)*value);
int visible = bar->GetHeight() - run;
if(bg == NULL || bg->GetWidth() != bar->GetWidth() || bg->GetHeight() != bar->GetHeight())
{
BYTE color0 = GPalette.Remap[0];
if(!horizontal)
{
if(!reverse) //remove offset if we are not reversing the direction.
{
visible = 0;
}
for(int i = 0;i < Width;i++)
{
memset(Pixels + i*Height + visible, color0, run);
}
}
else
{
for(int i = reverse ? 0 : width;(reverse && i < Width - width) || (!reverse && i < Width);i++)
{
memset(Pixels + i*Height, color0, Height);
}
}
}
else
{
BYTE* PixelData = (BYTE*) bg->GetPixels();
if(!horizontal)
{
if(!reverse)
{
visible = 0;
}
for(int i = 0;i < width;i++)
{
memcpy(Pixels + i*Height + visible, PixelData + i*Height, run);
}
}
else
{
for(int i = reverse ? 0 : width;(reverse && i < Width - width) || (!reverse && i < Width);i++)
{
memcpy(Pixels + i*Height, PixelData + i*Height, Height);
}
}
}
}
protected:
BYTE* Pixels;
FTexture* image;
};
//SBarInfo Display
class FSBarInfo : public FBaseStatusBar
{
public:
FSBarInfo () : FBaseStatusBar (SBarInfoScript.height)
{
static const char *InventoryBarLumps[] =
{
"ARTIBOX", "SELECTBO", "INVGEML1",
"INVGEML2", "INVGEMR1", "INVGEMR2",
};
TArray<const char *> patchnames;
patchnames.Resize(SBarInfoScript.Images.Size()+6);
unsigned int i = 0;
for(i = 0;i < SBarInfoScript.Images.Size();i++)
{
patchnames[i] = SBarInfoScript.Images[i];
}
for(i = 0;i < 6;i++)
{
patchnames[i+SBarInfoScript.Images.Size()] = InventoryBarLumps[i];
}
invBarOffset = SBarInfoScript.Images.Size();
Images.Init(&patchnames[0], patchnames.Size());
drawingFont = V_GetFont("ConFont");
faceTimer = ST_FACETIME;
faceIndex = 0;
if(SBarInfoScript.interpolateHealth)
{
oldHealth = 0;
}
mugshotHealth = -1;
lastPrefix = new char[4];
weaponGrin = false;
}
~FSBarInfo ()
{
Images.Uninit();
Faces.Uninit();
}
void Draw (EHudState state)
{
FBaseStatusBar::Draw(state);
int hud = 2;
if(state == HUD_StatusBar)
{
if(SBarInfoScript.automapbar && automapactive)
{
hud = 3;
}
else
{
hud = 2;
}
}
else if(state == HUD_Fullscreen)
{
hud = 1;
}
else
{
hud = 0;
}
doCommands(SBarInfoScript.huds[hud]);
if(CPlayer->inventorytics > 0 && !(level.flags & LEVEL_NOINVENTORYBAR))
{
if(state == HUD_StatusBar)
doCommands(SBarInfoScript.huds[4]);
else if(state == HUD_Fullscreen)
doCommands(SBarInfoScript.huds[5]);
}
}
void NewGame ()
{
if (CPlayer != NULL)
{
AttachToPlayer (CPlayer);
}
}
void AttachToPlayer (player_t *player)
{
player_t *oldplayer = CPlayer;
FBaseStatusBar::AttachToPlayer(player);
if (oldplayer != CPlayer)
{
SetFace(&skins[CPlayer->userinfo.skin], "STF");
}
}
void Tick ()
{
FBaseStatusBar::Tick();
getNewFace(M_Random());
if(!SBarInfoScript.interpolateHealth)
{
oldHealth = CPlayer->health;
}
else
{
if(oldHealth > CPlayer->health)
{
oldHealth -= clamp((oldHealth - CPlayer->health) >> 2, 1, 8);
}
else if(oldHealth < CPlayer->health)
{
oldHealth += clamp((CPlayer->health - oldHealth) >> 2, 1, 8);
}
}
}
void SetFace (void *skn)
{
SetFace((FPlayerSkin *)skn, "STF");
}
void ReceivedWeapon (AWeapon *weapon)
{
weaponGrin = true;
}
private:
//code from doom_sbar.cpp but it should do fine.
void SetFace (FPlayerSkin *skin, char* defPrefix)
{
oldSkin = skin;
const char *nameptrs[ST_NUMFACES];
char names[ST_NUMFACES][9];
char prefix[4];
int i, j;
int namespc;
int facenum;
for (i = 0; i < ST_NUMFACES; i++)
{
nameptrs[i] = names[i];
}
if (skin->face[0] != 0)
{
prefix[0] = skin->face[0];
prefix[1] = skin->face[1];
prefix[2] = skin->face[2];
prefix[3] = 0;
namespc = skin->namespc;
}
else
{
prefix[0] = defPrefix[0];
prefix[1] = defPrefix[1];
prefix[2] = defPrefix[2];
prefix[3] = 0;
namespc = ns_global;
}
if(stricmp(prefix, lastPrefix) == 0)
{
return;
}
lastPrefix = prefix;
facenum = 0;
for (i = 0; i < 9; i++) //levels
{
for (j = 0; j < 3; j++)
{
sprintf (names[facenum++], "%sST%d%d", prefix, i, j);
}
sprintf (names[facenum++], "%sTR%d0", prefix, i); // turn right
sprintf (names[facenum++], "%sTL%d0", prefix, i); // turn left
sprintf (names[facenum++], "%sOUCH%d", prefix, i); // ouch!
sprintf (names[facenum++], "%sEVL%d", prefix, i); // evil grin ;)
sprintf (names[facenum++], "%sKILL%d", prefix, i); // pissed off
}
sprintf (names[facenum++], "%sGOD0", prefix);
sprintf (names[facenum++], "%sDEAD0", prefix);
for(i = 0;i < 6;i++) //xdeath
{
sprintf (names[facenum++], "%sXDTH%d", prefix, i);
}
Faces.Uninit ();
Faces.Init (nameptrs, ST_NUMFACES, namespc);
faceIndex = ST_FACENORMAL;
faceTimer = 0;
}
void doCommands(SBarInfoBlock &block)
{
//prepare ammo counts
AAmmo *ammo1, *ammo2;
int ammocount1, ammocount2;
GetCurrentAmmo(ammo1, ammo2, ammocount1, ammocount2);
int health = CPlayer->mo->health;
if(SBarInfoScript.interpolateHealth)
{
health = oldHealth;
}
for(unsigned int i = 0;i < block.commands.Size();i++)
{
SBarInfoCommand cmd = block.commands[i];
switch(cmd.type) //read and execute all the commands
{
case SBARINFO_DRAWSWITCHABLEIMAGE: //draw the alt image if we don't have the item else this is like a normal drawimage
{
int drawAlt = 0;
if((cmd.flags & DRAWIMAGE_WEAPONSLOT) == DRAWIMAGE_WEAPONSLOT) //weaponslots
{
drawAlt = 1; //draw off state until we know we have something.
for (int i = 0; i < MAX_WEAPONS_PER_SLOT; i++)
{
const PClass *weap = LocalWeapons.Slots[cmd.value].GetWeapon(i);
if(weap == NULL)
{
continue;
}
else if(CPlayer->mo->FindInventory(weap) != NULL)
{
drawAlt = 0;
break;
}
}
}
else if((cmd.flags & DRAWIMAGE_INVULNERABILITY) == DRAWIMAGE_INVULNERABILITY)
{
if(CPlayer->cheats&CF_GODMODE)
{
drawAlt = 1;
}
}
else //check the inventory items and draw selected sprite
{
AInventory* item = CPlayer->mo->FindInventory(PClass::FindClass(cmd.string[0]));
if(item == NULL)
drawAlt = 1;
if((cmd.flags & DRAWIMAGE_SWITCHABLE_AND) == DRAWIMAGE_SWITCHABLE_AND)
{
item = CPlayer->mo->FindInventory(PClass::FindClass(cmd.string[1]));
if(item != NULL && drawAlt == 0) //both
{
drawAlt = 0;
}
else if(item != NULL && drawAlt == 1) //2nd
{
drawAlt = 3;
}
else if(item == NULL && drawAlt == 0) //1st
{
drawAlt = 2;
}
}
}
if(drawAlt != 0) //draw 'off' image
{
if(cmd.special != -1 && drawAlt == 1)
DrawGraphic(Images[cmd.special], cmd.x, cmd.y, cmd.flags);
else if(cmd.special2 != -1 && drawAlt == 2)
DrawGraphic(Images[cmd.special2], cmd.x, cmd.y, cmd.flags);
else if(cmd.special3 != -1 && drawAlt == 3)
DrawGraphic(Images[cmd.special3], cmd.x, cmd.y, cmd.flags);
break;
}
}
case SBARINFO_DRAWIMAGE:
if((cmd.flags & DRAWIMAGE_PLAYERICON) == DRAWIMAGE_PLAYERICON)
DrawGraphic(TexMan[CPlayer->mo->ScoreIcon], cmd.x, cmd.y, cmd.flags);
else if((cmd.flags & DRAWIMAGE_AMMO1) == DRAWIMAGE_AMMO1)
{
if(ammo1 != NULL)
DrawGraphic(TexMan[ammo1->Icon], cmd.x, cmd.y, cmd.flags);
}
else if((cmd.flags & DRAWIMAGE_AMMO2) == DRAWIMAGE_AMMO2)
{
if(ammo2 != NULL)
DrawGraphic(TexMan[ammo2->Icon], cmd.x, cmd.y, cmd.flags);
}
else
{
DrawGraphic(Images[cmd.sprite], cmd.x, cmd.y, cmd.flags);
}
break;
case SBARINFO_DRAWNUMBER:
if(drawingFont != cmd.font)
{
drawingFont = cmd.font;
}
if(cmd.flags == DRAWNUMBER_HEALTH)
{
cmd.value = health;
if(cmd.value < 0) //health shouldn't display negatives
{
cmd.value = 0;
}
}
else if(cmd.flags == DRAWNUMBER_ARMOR)
{
AInventory *armor = CPlayer->mo->FindInventory<ABasicArmor>();
cmd.value = armor != NULL ? armor->Amount : 0;
}
else if(cmd.flags == DRAWNUMBER_AMMO1)
{
cmd.value = ammocount1;
if(ammo1 == NULL) //no ammo, do not draw
{
continue;
}
}
else if(cmd.flags == DRAWNUMBER_AMMO2)
{
cmd.value = ammocount2;
if(ammo2 == NULL) //no ammo, do not draw
{
continue;
}
}
else if(cmd.flags == DRAWNUMBER_AMMO)
{
const PClass* ammo = PClass::FindClass(cmd.string[0]);
AInventory* item = CPlayer->mo->FindInventory(ammo);
if(item != NULL)
{
cmd.value = item->Amount;
}
else
{
cmd.value = 0;
}
}
else if(cmd.flags == DRAWNUMBER_AMMOCAPACITY)
{
const PClass* ammo = PClass::FindClass(cmd.string[0]);
AInventory* item = CPlayer->mo->FindInventory(ammo);
if(item != NULL)
{
cmd.value = item->MaxAmount;
}
else
{
cmd.value = ((AInventory *)GetDefaultByType(ammo))->MaxAmount;
}
}
else if(cmd.flags == DRAWNUMBER_FRAGS)
cmd.value = CPlayer->fragcount;
else if(cmd.flags == DRAWNUMBER_INVENTORY)
{
AInventory* item = CPlayer->mo->FindInventory(PClass::FindClass(cmd.string[0]));
if(item != NULL)
{
cmd.value = item->Amount;
}
else
{
cmd.value = 0;
}
}
DrawNumber(cmd.value, cmd.special, cmd.x, cmd.y, cmd.translation, cmd.special2);
break;
case SBARINFO_DRAWMUGSHOT:
{
bool xdth = false;
if(cmd.special2 != 0)
xdth = true;
SetFace(oldSkin, cmd.string[0]);
DrawFace(cmd.special, xdth, cmd.x, cmd.y);
break;
}
case SBARINFO_DRAWSELECTEDINVENTORY:
if(CPlayer->mo->InvSel != NULL && !(level.flags & LEVEL_NOINVENTORYBAR))
{
DrawImage(TexMan(CPlayer->mo->InvSel->Icon), cmd.x, cmd.y, CPlayer->mo->InvSel->Amount > 0 ? NULL : DIM_MAP);
if(CPlayer->mo->InvSel->Amount != 1)
{
if(drawingFont != cmd.font)
{
drawingFont = cmd.font;
}
DrawNumber(CPlayer->mo->InvSel->Amount, 3, cmd.special2, cmd.special3, cmd.translation, cmd.special4);
}
}
else if((cmd.flags & DRAWSELECTEDINVENTORY_ALTERNATEONEMPTY) == DRAWSELECTEDINVENTORY_ALTERNATEONEMPTY)
{
doCommands(cmd.subBlock);
}
break;
case SBARINFO_DRAWINVENTORYBAR:
{
bool alwaysshow = false;
bool artibox = true;
if((cmd.flags & DRAWINVENTORYBAR_ALWAYSSHOW) == DRAWINVENTORYBAR_ALWAYSSHOW)
alwaysshow = true;
if((cmd.flags & DRAWINVENTORYBAR_NOARTIBOX) == DRAWINVENTORYBAR_NOARTIBOX)
artibox = false;
if(drawingFont != cmd.font)
{
drawingFont = cmd.font;
}
DrawInventoryBar(cmd.special, cmd.value, cmd.x, cmd.y, alwaysshow, cmd.special2, cmd.special3, cmd.translation, artibox);
break;
}
case SBARINFO_DRAWBAR:
{
if(cmd.sprite == -1) break; //don't draw anything.
bool horizontal = ((cmd.special2 & DRAWBAR_HORIZONTAL) == DRAWBAR_HORIZONTAL);
bool reverse = ((cmd.special2 & DRAWBAR_REVERSE) == DRAWBAR_REVERSE);
int value = 0;
int max = 0;
if(cmd.flags == DRAWNUMBER_HEALTH)
{
value = health;
if(cmd.value < 0) //health shouldn't display negatives
{
value = 0;
}
if(!((cmd.special2 & DRAWBAR_COMPAREDEFAULTS) == DRAWBAR_COMPAREDEFAULTS))
{
AInventory* item = CPlayer->mo->FindInventory(PClass::FindClass(cmd.string[0])); //max comparer
if(item != NULL)
{
max = item->Amount;
}
else
{
max = 0;
}
}
else //default to the classes health
{
max = CPlayer->mo->GetDefault()->health;
}
}
else if(cmd.flags == DRAWNUMBER_ARMOR)
{
AInventory *armor = CPlayer->mo->FindInventory<ABasicArmor>();
value = armor != NULL ? armor->Amount : 0;
if(!((cmd.special2 & DRAWBAR_COMPAREDEFAULTS) == DRAWBAR_COMPAREDEFAULTS))
{
AInventory* item = CPlayer->mo->FindInventory(PClass::FindClass(cmd.string[0])); //max comparer
if(item != NULL)
{
max = item->Amount;
}
else
{
max = 0;
}
}
else //default to 100
{
max = 100;
}
}
else if(cmd.flags == DRAWNUMBER_AMMO1)
{
value = ammocount1;
if(ammo1 == NULL) //no ammo, do not draw
{
continue;
}
max = ammo1->MaxAmount;
}
else if(cmd.flags == DRAWNUMBER_AMMO2)
{
value = ammocount2;
if(ammo2 == NULL) //no ammo, do not draw
{
continue;
}
max = ammo2->MaxAmount;
}
else if(cmd.flags == DRAWNUMBER_AMMO)
{
const PClass* ammo = PClass::FindClass(cmd.string[0]);
AInventory* item = CPlayer->mo->FindInventory(ammo);
if(item != NULL)
{
value = item->Amount;
max = item->MaxAmount;
}
else
{
value = 0;
}
}
else if(cmd.flags == DRAWNUMBER_FRAGS)
{
value = CPlayer->fragcount;
max = fraglimit;
}
else if(cmd.flags == DRAWNUMBER_INVENTORY)
{
AInventory* item = CPlayer->mo->FindInventory(PClass::FindClass(cmd.string[0]));
if(item != NULL)
{
value = item->Amount;
max = item->MaxAmount;
}
else
{
value = 0;
}
}
if(max != 0 || value < 0)
{
value = (value*100)/max;
if(value > 100)
value = 100;
}
else
{
value = 0;
}
FBarTexture* bar = new FBarTexture();
if(cmd.special != -1)
bar->PrepareTexture(Images[cmd.sprite], Images[cmd.special], value, horizontal, reverse);
else
bar->PrepareTexture(Images[cmd.sprite], NULL, value, horizontal, reverse);
DrawImage(bar, cmd.x, cmd.y);
break;
}
case SBARINFO_GAMEMODE:
if(((cmd.flags & GAMETYPE_SINGLEPLAYER) == GAMETYPE_SINGLEPLAYER && !multiplayer) ||
((cmd.flags & GAMETYPE_DEATHMATCH) == GAMETYPE_DEATHMATCH && deathmatch) ||
((cmd.flags & GAMETYPE_COOPERATIVE) == GAMETYPE_COOPERATIVE && multiplayer && !deathmatch) ||
((cmd.flags & GAMETYPE_TEAMGAME) == GAMETYPE_TEAMGAME && teamplay))
{
doCommands(cmd.subBlock);
}
break;
case SBARINFO_PLAYERCLASS:
int spawnClass = CPlayer->GetSpawnClass();
if(cmd.special == spawnClass || cmd.special2 == spawnClass || cmd.special3 == spawnClass)
{
doCommands(cmd.subBlock);
}
break;
}
}
}
//draws and image with the specified flags
void DrawGraphic(FTexture* texture, int x, int y, int flags)
{
if((flags & DRAWIMAGE_OFFSET_CENTER) == DRAWIMAGE_OFFSET_CENTER)
{
x -= (texture->GetWidth()/2)-texture->LeftOffset;
y -= (texture->GetHeight()/2)-texture->TopOffset;
}
if((flags & DRAWIMAGE_TRANSLATABLE) == DRAWIMAGE_TRANSLATABLE)
DrawImage(texture, x, y, translationtables[TRANSLATION_Players] + (CPlayer - players)*256);
else
DrawImage(texture, x, y);
}
void DrawString(const char* str, int x, int y, EColorRange translation, int spacing=0)
{
x += spacing;
while(*str != '\0')
{
int width = drawingFont->GetCharWidth((int) *str);
FTexture* character = drawingFont->GetChar((int) *str, &width);
if(character == NULL) //missing character.
{
str++;
continue;
}
x += (character->LeftOffset+1); //ignore x offsets since we adapt to character size
DrawImage(character, x, y, drawingFont->GetColorTranslation(translation));
x += width + spacing - (character->LeftOffset+1);
str++;
}
}
//draws the specified number up to len digits
void DrawNumber(int num, int len, int x, int y, EColorRange translation, int spacing=0)
{
FString value;
int maxval = (int) pow(10., len);
num = clamp(num, -maxval+1, maxval-1);
value.Format("%d", num);
x -= int(drawingFont->StringWidth(value)+(spacing * value.Len()));
DrawString(value, x, y, translation, spacing);
}
//draws the mug shot
void DrawFace(int accuracy, bool xdth, int x, int y)
{
if(CPlayer->health > 0)
{
if(faceIndex == ST_FACEGOD) //nothing fancy to do here
{
DrawImage(Faces[ST_FACEGOD], x, y);
}
else
{
int level = 0;
for(level = 0;CPlayer->health < (accuracy-level-1)*(100/accuracy);level++);
int face = faceIndex + level*8;
DrawImage(Faces[face], x, y);
}
}
else //dead
{
if(!xdth || CPlayer->mo->health > -CPlayer->mo->GetDefault()->health)
{
DrawImage(Faces[ST_FACEDEAD], x, y);
}
else
{
if(faceIndex != ST_FACEXDEAD+6 && faceIndex >= ST_FACEXDEAD) //animate
{
if(faceTimer == 0)
{
faceIndex++;
faceTimer = ST_XDTHTIME;
}
faceTimer--;
}
else if(faceIndex < ST_FACEXDEAD) //set to xdeath
{
faceIndex = ST_FACEXDEAD;
faceTimer = ST_XDTHTIME;
}
DrawImage(Faces[faceIndex], x, y);
}
}
}
//Does some face drawing logic.
void getNewFace(int number)
{
int i;
angle_t badguyangle;
angle_t diffang;
if(CPlayer->health > 0)
{
if(weaponGrin)
{
weaponGrin = false;
if(CPlayer->bonuscount)
{
faceTimer = ST_GRINTIME;
faceIndex = ST_FACEGRIN;
return;
}
}
// getting hurt
if (CPlayer->damagecount)
{
int damageAngle = ST_FACEPAIN;
if(CPlayer->attacker && CPlayer->attacker != CPlayer->mo)
{
if(CPlayer->mo != NULL)
{
//some more doom_sbar C&P
badguyangle = R_PointToAngle2(CPlayer->mo->x, CPlayer->mo->y, CPlayer->attacker->x, CPlayer->attacker->y);
if (badguyangle > CPlayer->mo->angle)
{
// whether right or left
diffang = badguyangle - CPlayer->mo->angle;
i = diffang > ANG180;
}
else
{
// whether left or right
diffang = CPlayer->mo->angle - badguyangle;
i = diffang <= ANG180;
} // confusing, aint it?
if(i && diffang >= ANG45)
{
damageAngle = ST_FACEPAINRIGHT;
}
else if(!i && diffang >= ANG45)
{
damageAngle = ST_FACEPAINLEFT;
}
}
}
faceTimer = ST_PAINTIME;
if (mugshotHealth != -1 && CPlayer->health - mugshotHealth > 20)
{
faceIndex = ST_FACEOUCH;
}
else
{
faceIndex = damageAngle;
}
return;
}
// invulnerability
if ((CPlayer->cheats & CF_GODMODE) || (CPlayer->mo != NULL && CPlayer->mo->flags2 & MF2_INVULNERABLE))
{
faceIndex = ST_FACEGOD;
faceTimer = 1;
}
if (faceTimer == 0)
{
faceIndex = (number % 3); //should generate 0, 1, or 2
faceTimer = ST_FACETIME;
}
faceTimer--;
mugshotHealth = CPlayer->health;
}
}
void DrawInventoryBar(int type, int num, int x, int y, bool alwaysshow,
int counterx, int countery, EColorRange translation, bool drawArtiboxes) //yes, there is some Copy & Paste here too
{
const AInventory *item;
int i;
// If the player has no artifacts, don't draw the bar
CPlayer->mo->InvFirst = ValidateInvFirst(num);
if(CPlayer->mo->InvFirst != NULL || alwaysshow)
{
for(item = CPlayer->mo->InvFirst, i = 0; item != NULL && i < num; item = item->NextInv(), ++i)
{
if(drawArtiboxes)
{
DrawImage (Images[invBarOffset + imgARTIBOX], x+i*31, y);
}
DrawImage (TexMan(item->Icon), x+i*31, y, item->Amount > 0 ? NULL : DIM_MAP);
if(item->Amount != 1)
{
DrawNumber(item->Amount, 3, counterx+i*31, countery, translation);
}
if(item == CPlayer->mo->InvSel)
{
if(type == GAME_Heretic)
{
DrawImage(Images[invBarOffset + imgSELECTBOX], x+i*31, y+29);
}
else
{
DrawImage(Images[invBarOffset + imgSELECTBOX], x+i*31, y);
}
}
}
for (; i < num && drawArtiboxes; ++i)
{
DrawImage (Images[invBarOffset + imgARTIBOX], x+i*31, y);
}
// Is there something to the left?
if (CPlayer->mo->FirstInv() != CPlayer->mo->InvFirst)
{
DrawImage (Images[!(gametic & 4) ?
invBarOffset + imgINVLFGEM1 : invBarOffset + imgINVLFGEM2], 38, 2);
}
// Is there something to the right?
if (item != NULL)
{
DrawImage (Images[!(gametic & 4) ?
invBarOffset + imgINVRTGEM1 : invBarOffset + imgINVRTGEM2], 269, 2);
}
}
}
FImageCollection Images;
FImageCollection Faces;
FPlayerSkin *oldSkin;
FFont *drawingFont;
char* lastPrefix;
bool weaponGrin;
int faceTimer;
int faceIndex;
int oldHealth;
int mugshotHealth;
unsigned int invBarOffset;
};
FBaseStatusBar *CreateCustomStatusBar ()
{
return new FSBarInfo;
}