mirror of
https://github.com/ZDoom/gzdoom-last-svn.git
synced 2025-05-30 17:00:48 +00:00
- updated to ZDoom r755. git-svn-id: http://mancubus.net/svn/hosted/gzdoom/trunk@24 b0f79afe-0144-0410-b225-9a4edf0717df
2414 lines
64 KiB
C++
2414 lines
64 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"
|
|
#include "gi.h"
|
|
#include "r_translate.h"
|
|
#include "r_main.h"
|
|
|
|
static FRandom pr_chainwiggle; //use the same method of chain wiggling as heretic.
|
|
|
|
#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 82 //9 levels with 8 faces each, 3 god, 1 death, 6 xdeath
|
|
#define ARTIFLASH_OFFSET (invBarOffset+6)
|
|
|
|
EXTERN_CVAR(Int, fraglimit)
|
|
EXTERN_CVAR(Int, screenblocks)
|
|
|
|
SBarInfo *SBarInfoScript;
|
|
|
|
enum //statusbar flags
|
|
{
|
|
STATUSBARFLAG_FORCESCALED = 1,
|
|
};
|
|
|
|
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_INVENTORYICON = 8,
|
|
DRAWIMAGE_TRANSLATABLE = 16,
|
|
DRAWIMAGE_WEAPONSLOT = 32,
|
|
DRAWIMAGE_SWITCHABLE_AND = 64,
|
|
DRAWIMAGE_INVULNERABILITY = 128,
|
|
DRAWIMAGE_OFFSET_CENTER = 256,
|
|
DRAWIMAGE_ARMOR = 512,
|
|
DRAWIMAGE_WEAPONICON = 1024,
|
|
};
|
|
|
|
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,
|
|
DRAWNUMBER_KILLS = 256,
|
|
DRAWNUMBER_MONSTERS = 512,
|
|
DRAWNUMBER_ITEMS = 1024,
|
|
DRAWNUMBER_TOTALITEMS = 2048,
|
|
DRAWNUMBER_SECRETS = 4096,
|
|
DRAWNUMBER_TOTALSECRETS = 8192,
|
|
DRAWNUMBER_ARMORCLASS = 16384,
|
|
};
|
|
|
|
enum //drawbar flags (will go into special2)
|
|
{
|
|
DRAWBAR_HORIZONTAL = 1,
|
|
DRAWBAR_REVERSE = 2,
|
|
DRAWBAR_COMPAREDEFAULTS = 4,
|
|
};
|
|
|
|
enum //drawselectedinventory flags
|
|
{
|
|
DRAWSELECTEDINVENTORY_ALTERNATEONEMPTY = 1,
|
|
DRAWSELECTEDINVENTORY_ARTIFLASH = 2,
|
|
DRAWSELECTEDINVENTORY_ALWAYSSHOWCOUNTER = 4,
|
|
};
|
|
|
|
enum //drawinventorybar flags
|
|
{
|
|
DRAWINVENTORYBAR_ALWAYSSHOW = 1,
|
|
DRAWINVENTORYBAR_NOARTIBOX = 2,
|
|
DRAWINVENTORYBAR_NOARROWS = 4,
|
|
DRAWINVENTORYBAR_ALWAYSSHOWCOUNTER = 8,
|
|
};
|
|
|
|
enum //drawgem flags
|
|
{
|
|
DRAWGEM_WIGGLE = 1,
|
|
DRAWGEM_TRANSLATABLE = 2,
|
|
DRAWGEM_ARMOR = 4,
|
|
DRAWGEM_REVERSE = 8,
|
|
};
|
|
|
|
enum //drawshader flags
|
|
{
|
|
DRAWSHADER_VERTICAL = 1,
|
|
DRAWSHADER_REVERSE = 2,
|
|
};
|
|
|
|
enum //drawmugshot flags
|
|
{
|
|
DRAWMUGSHOT_XDEATHFACE = 1,
|
|
DRAWMUGSHOT_ANIMATEDGODMODE = 2,
|
|
};
|
|
|
|
enum //drawkeybar flags
|
|
{
|
|
DRAWKEYBAR_VERTICAL = 1,
|
|
};
|
|
|
|
enum //event flags
|
|
{
|
|
SBARINFOEVENT_NOT = 1,
|
|
SBARINFOEVENT_OR = 2,
|
|
SBARINFOEVENT_AND = 4,
|
|
};
|
|
|
|
static const char *SBarInfoTopLevel[] =
|
|
{
|
|
"base",
|
|
"height",
|
|
"interpolatehealth",
|
|
"interpolatearmor",
|
|
"completeborder",
|
|
"statusbar",
|
|
NULL
|
|
};
|
|
enum
|
|
{
|
|
SBARINFO_BASE,
|
|
SBARINFO_HEIGHT,
|
|
SBARINFO_INTERPOLATEHEALTH,
|
|
SBARINFO_INTERPOLATEARMOR,
|
|
SBARINFO_COMPLETEBORDER,
|
|
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",
|
|
"drawgem",
|
|
"drawshader",
|
|
"drawstring",
|
|
"drawkeybar",
|
|
"gamemode",
|
|
"playerclass",
|
|
"weaponammo", //event
|
|
NULL
|
|
};
|
|
enum
|
|
{
|
|
SBARINFO_DRAWIMAGE,
|
|
SBARINFO_DRAWNUMBER,
|
|
SBARINFO_DRAWSWITCHABLEIMAGE,
|
|
SBARINFO_DRAWMUGSHOT,
|
|
SBARINFO_DRAWSELECTEDINVENTORY,
|
|
SBARINFO_DRAWINVENTORYBAR,
|
|
SBARINFO_DRAWBAR,
|
|
SBARINFO_DRAWGEM,
|
|
SBARINFO_DRAWSHADER,
|
|
SBARINFO_DRAWSTRING,
|
|
SBARINFO_DRAWKEYBAR,
|
|
SBARINFO_GAMEMODE,
|
|
SBARINFO_PLAYERCLASS,
|
|
SBARINFO_WEAPONAMMO,
|
|
};
|
|
|
|
void FreeSBarInfoScript()
|
|
{
|
|
if (SBarInfoScript != NULL)
|
|
{
|
|
delete SBarInfoScript;
|
|
SBarInfoScript = NULL;
|
|
}
|
|
}
|
|
|
|
//SBarInfo Script Reader
|
|
void SBarInfo::ParseSBarInfo(int lump)
|
|
{
|
|
FScanner sc(lump, Wads.GetLumpFullName(lump));
|
|
gameType = GAME_Any;
|
|
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 && sc.StringLen <= 8 && !strchr(sc.String, '/'))
|
|
lump = Wads.CheckNumForName(sc.String);
|
|
if (lump == -1)
|
|
sc.ScriptError("Lump '%s' not found", sc.String);
|
|
ParseSBarInfo(lump);
|
|
continue;
|
|
}
|
|
switch(sc.MustMatchString(SBarInfoTopLevel))
|
|
{
|
|
case SBARINFO_BASE:
|
|
if(!sc.CheckToken(TK_None))
|
|
sc.MustGetToken(TK_Identifier);
|
|
if(sc.Compare("Doom"))
|
|
gameType = GAME_Doom;
|
|
else if(sc.Compare("Heretic"))
|
|
gameType = GAME_Heretic;
|
|
else if(sc.Compare("Hexen"))
|
|
gameType = GAME_Hexen;
|
|
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);
|
|
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_STATUSBAR:
|
|
{
|
|
int barNum = 0;
|
|
if(!sc.CheckToken(TK_None))
|
|
{
|
|
sc.MustGetToken(TK_Identifier);
|
|
barNum = sc.MustMatchString(StatusBars);
|
|
}
|
|
while(sc.CheckToken(','))
|
|
{
|
|
sc.MustGetToken(TK_Identifier);
|
|
if(sc.Compare("forcescaled"))
|
|
{
|
|
this->huds[barNum].forceScaled = true;
|
|
}
|
|
else
|
|
{
|
|
sc.ScriptError("Unkown flag '%s'.", sc.String);
|
|
}
|
|
}
|
|
sc.MustGetToken('{');
|
|
if(barNum == STBAR_AUTOMAP)
|
|
{
|
|
automapbar = true;
|
|
}
|
|
ParseSBarInfoBlock(sc, this->huds[barNum]);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void SBarInfo::ParseSBarInfoBlock(FScanner &sc, SBarInfoBlock &block)
|
|
{
|
|
while(sc.CheckToken(TK_Identifier))
|
|
{
|
|
SBarInfoCommand cmd;
|
|
|
|
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, 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, 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("armoricon"))
|
|
cmd.flags += DRAWIMAGE_ARMOR;
|
|
else if(sc.Compare("weaponicon"))
|
|
cmd.flags += DRAWIMAGE_WEAPONICON;
|
|
else if(sc.Compare("translatable"))
|
|
{
|
|
cmd.flags += DRAWIMAGE_TRANSLATABLE;
|
|
getImage = true;
|
|
}
|
|
else
|
|
{
|
|
cmd.flags += DRAWIMAGE_INVENTORYICON;
|
|
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);
|
|
}
|
|
cmd.sprite = ((AInventory *)GetDefaultByType(item))->Icon;
|
|
}
|
|
}
|
|
if(getImage)
|
|
{
|
|
sc.MustGetToken(TK_StringConst);
|
|
cmd.sprite = newImage(sc.String);
|
|
}
|
|
sc.MustGetToken(',');
|
|
this->getCoordinates(sc, cmd);
|
|
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:
|
|
cmd.special4 = cmd.special3 = -1;
|
|
sc.MustGetToken(TK_IntConst);
|
|
cmd.special = sc.Number;
|
|
sc.MustGetToken(',');
|
|
sc.MustGetToken(TK_Identifier);
|
|
cmd.font = V_GetFont(sc.String);
|
|
if(cmd.font == NULL)
|
|
sc.ScriptError("Unknown font '%s'.", sc.String);
|
|
sc.MustGetToken(',');
|
|
sc.MustGetToken(TK_Identifier);
|
|
cmd.translation = this->GetTranslation(sc, 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, sc.String, 0);
|
|
cmd.flags = DRAWNUMBER_AMMO;
|
|
const PClass* ammo = PClass::FindClass(sc.String);
|
|
if(ammo == NULL || !RUNTIME_CLASS(AAmmo)->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, sc.String, 0);
|
|
cmd.flags = DRAWNUMBER_AMMOCAPACITY;
|
|
const PClass* ammo = PClass::FindClass(sc.String);
|
|
if(ammo == NULL || !RUNTIME_CLASS(AAmmo)->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 if(sc.Compare("kills"))
|
|
cmd.flags += DRAWNUMBER_KILLS;
|
|
else if(sc.Compare("monsters"))
|
|
cmd.flags += DRAWNUMBER_MONSTERS;
|
|
else if(sc.Compare("items"))
|
|
cmd.flags += DRAWNUMBER_ITEMS;
|
|
else if(sc.Compare("totalitems"))
|
|
cmd.flags += DRAWNUMBER_TOTALITEMS;
|
|
else if(sc.Compare("secrets"))
|
|
cmd.flags += DRAWNUMBER_SECRETS;
|
|
else if(sc.Compare("totalsecrets"))
|
|
cmd.flags += DRAWNUMBER_TOTALSECRETS;
|
|
else if(sc.Compare("armorclass"))
|
|
cmd.flags += DRAWNUMBER_ARMORCLASS;
|
|
else
|
|
{
|
|
cmd.flags = DRAWNUMBER_INVENTORY;
|
|
sc.MustGetToken(TK_Identifier);
|
|
cmd.setString(sc, 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(',');
|
|
}
|
|
this->getCoordinates(sc, cmd);
|
|
if(sc.CheckToken(','))
|
|
{
|
|
bool needsComma = false;
|
|
if(sc.CheckToken(TK_IntConst)) //font spacing
|
|
{
|
|
cmd.special2 = sc.Number;
|
|
needsComma = true;
|
|
}
|
|
if(!needsComma || sc.CheckToken(',')) //2nd coloring for "low-on" value
|
|
{
|
|
sc.MustGetToken(TK_Identifier);
|
|
cmd.translation2 = this->GetTranslation(sc, sc.String);
|
|
sc.MustGetToken(',');
|
|
sc.MustGetToken(TK_IntConst);
|
|
cmd.special3 = sc.Number;
|
|
if(sc.CheckToken(',')) //3rd coloring for "high-on" value
|
|
{
|
|
sc.MustGetToken(TK_Identifier);
|
|
cmd.translation3 = this->GetTranslation(sc, sc.String);
|
|
sc.MustGetToken(',');
|
|
sc.MustGetToken(TK_IntConst);
|
|
cmd.special4 = sc.Number;
|
|
}
|
|
}
|
|
}
|
|
sc.MustGetToken(';');
|
|
break;
|
|
case SBARINFO_DRAWMUGSHOT:
|
|
sc.MustGetToken(TK_StringConst);
|
|
cmd.setString(sc, sc.String, 0, 3, true);
|
|
sc.MustGetToken(',');
|
|
sc.MustGetToken(TK_IntConst); //accuracy
|
|
if(sc.Number < 1 || sc.Number > 9)
|
|
sc.ScriptError("Expected a number between 1 and 9, got %d instead.", sc.Number);
|
|
cmd.special = sc.Number;
|
|
sc.MustGetToken(',');
|
|
while(sc.CheckToken(TK_Identifier))
|
|
{
|
|
if(sc.Compare("xdeathface"))
|
|
cmd.flags += DRAWMUGSHOT_XDEATHFACE;
|
|
else if(sc.Compare("animatedgodmode"))
|
|
cmd.flags += DRAWMUGSHOT_ANIMATEDGODMODE;
|
|
else
|
|
sc.ScriptError("Unknown flag '%s'.", sc.String);
|
|
if(!sc.CheckToken('|'))
|
|
sc.MustGetToken(',');
|
|
}
|
|
this->getCoordinates(sc, cmd);
|
|
sc.MustGetToken(';');
|
|
break;
|
|
case SBARINFO_DRAWSELECTEDINVENTORY:
|
|
{
|
|
bool alternateonempty = false;
|
|
while(true) //go until we get a font (non-flag)
|
|
{
|
|
sc.MustGetToken(TK_Identifier);
|
|
if(sc.Compare("alternateonempty"))
|
|
{
|
|
alternateonempty = true;
|
|
cmd.flags += DRAWSELECTEDINVENTORY_ALTERNATEONEMPTY;
|
|
}
|
|
else if(sc.Compare("artiflash"))
|
|
{
|
|
cmd.flags += DRAWSELECTEDINVENTORY_ARTIFLASH;
|
|
}
|
|
else if(sc.Compare("alwaysshowcounter"))
|
|
{
|
|
cmd.flags += DRAWSELECTEDINVENTORY_ALWAYSSHOWCOUNTER;
|
|
}
|
|
else
|
|
{
|
|
cmd.font = V_GetFont(sc.String);
|
|
if(cmd.font == NULL)
|
|
sc.ScriptError("Unknown font '%s'.", sc.String);
|
|
sc.MustGetToken(',');
|
|
break;
|
|
}
|
|
if(!sc.CheckToken('|'))
|
|
sc.MustGetToken(',');
|
|
}
|
|
sc.MustGetToken(TK_IntConst);
|
|
cmd.x = sc.Number;
|
|
sc.MustGetToken(',');
|
|
sc.MustGetToken(TK_IntConst);
|
|
cmd.y = sc.Number - (200 - this->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 - this->height);
|
|
if(sc.CheckToken(','))
|
|
{
|
|
sc.MustGetToken(TK_Identifier);
|
|
cmd.translation = this->GetTranslation(sc, sc.String);
|
|
if(sc.CheckToken(','))
|
|
{
|
|
sc.MustGetToken(TK_IntConst);
|
|
cmd.special4 = sc.Number;
|
|
}
|
|
}
|
|
}
|
|
if(alternateonempty)
|
|
{
|
|
sc.MustGetToken('{');
|
|
this->ParseSBarInfoBlock(sc, 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("noartibox"))
|
|
{
|
|
cmd.flags += DRAWINVENTORYBAR_NOARTIBOX;
|
|
}
|
|
else if(sc.Compare("noarrows"))
|
|
{
|
|
cmd.flags += DRAWINVENTORYBAR_NOARROWS;
|
|
}
|
|
else if(sc.Compare("alwaysshowcounter"))
|
|
{
|
|
cmd.flags += DRAWINVENTORYBAR_ALWAYSSHOWCOUNTER;
|
|
}
|
|
else
|
|
{
|
|
sc.ScriptError("Unknown flag '%s'.", sc.String);
|
|
}
|
|
if(!sc.CheckToken('|'))
|
|
sc.MustGetToken(',');
|
|
}
|
|
sc.MustGetToken(TK_IntConst);
|
|
cmd.value = sc.Number;
|
|
sc.MustGetToken(',');
|
|
sc.MustGetToken(TK_Identifier);
|
|
cmd.font = V_GetFont(sc.String);
|
|
if(cmd.font == NULL)
|
|
sc.ScriptError("Unknown font '%s'.", sc.String);
|
|
}
|
|
else
|
|
{
|
|
sc.ScriptError("Unkown style '%s'.", sc.String);
|
|
}
|
|
sc.MustGetToken(',');
|
|
this->getCoordinates(sc, cmd);
|
|
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 - this->height);
|
|
if(sc.CheckToken(','))
|
|
{
|
|
sc.MustGetToken(TK_Identifier);
|
|
cmd.translation = this->GetTranslation(sc, 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, 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, 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, sc.String, 0);
|
|
cmd.flags = DRAWNUMBER_AMMO;
|
|
const PClass* ammo = PClass::FindClass(sc.String);
|
|
if(ammo == NULL || !RUNTIME_CLASS(AAmmo)->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 if(sc.Compare("kills"))
|
|
cmd.flags = DRAWNUMBER_KILLS;
|
|
else if(sc.Compare("items"))
|
|
cmd.flags = DRAWNUMBER_ITEMS;
|
|
else if(sc.Compare("secrets"))
|
|
cmd.flags = DRAWNUMBER_SECRETS;
|
|
else
|
|
{
|
|
cmd.flags = DRAWNUMBER_INVENTORY;
|
|
cmd.setString(sc, sc.String, 0);
|
|
const PClass* item = PClass::FindClass(sc.String);
|
|
if(item == NULL || !RUNTIME_CLASS(AInventory)->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(',');
|
|
}
|
|
this->getCoordinates(sc, cmd);
|
|
if(sc.CheckToken(',')) //border
|
|
{
|
|
sc.MustGetToken(TK_IntConst);
|
|
cmd.special3 = sc.Number;
|
|
}
|
|
sc.MustGetToken(';');
|
|
break;
|
|
case SBARINFO_DRAWGEM:
|
|
while(sc.CheckToken(TK_Identifier))
|
|
{
|
|
if(sc.Compare("wiggle"))
|
|
cmd.flags += DRAWGEM_WIGGLE;
|
|
else if(sc.Compare("translatable"))
|
|
cmd.flags += DRAWGEM_TRANSLATABLE;
|
|
else if(sc.Compare("armor"))
|
|
cmd.flags += DRAWGEM_ARMOR;
|
|
else if(sc.Compare("reverse"))
|
|
cmd.flags += DRAWGEM_REVERSE;
|
|
else
|
|
sc.ScriptError("Unknown drawgem flag '%s'.", sc.String);
|
|
if(!sc.CheckToken('|'))
|
|
sc.MustGetToken(',');
|
|
}
|
|
sc.MustGetToken(TK_StringConst); //chain
|
|
cmd.special = newImage(sc.String);
|
|
sc.MustGetToken(',');
|
|
sc.MustGetToken(TK_StringConst); //gem
|
|
cmd.sprite = newImage(sc.String);
|
|
sc.MustGetToken(',');
|
|
cmd.special2 = this->getSignedInteger(sc);
|
|
sc.MustGetToken(',');
|
|
cmd.special3 = this->getSignedInteger(sc);
|
|
sc.MustGetToken(',');
|
|
sc.MustGetToken(TK_IntConst);
|
|
if(sc.Number < 0)
|
|
sc.ScriptError("Chain size must be a positive number.");
|
|
cmd.special4 = sc.Number;
|
|
sc.MustGetToken(',');
|
|
this->getCoordinates(sc, cmd);
|
|
sc.MustGetToken(';');
|
|
break;
|
|
case SBARINFO_DRAWSHADER:
|
|
sc.MustGetToken(TK_IntConst);
|
|
cmd.special = sc.Number;
|
|
if(sc.Number < 1)
|
|
sc.ScriptError("Width must be greater than 1.");
|
|
sc.MustGetToken(',');
|
|
sc.MustGetToken(TK_IntConst);
|
|
cmd.special2 = sc.Number;
|
|
if(sc.Number < 1)
|
|
sc.ScriptError("Height must be greater than 1.");
|
|
sc.MustGetToken(',');
|
|
sc.MustGetToken(TK_Identifier);
|
|
if(sc.Compare("vertical"))
|
|
cmd.flags += DRAWSHADER_VERTICAL;
|
|
else if(!sc.Compare("horizontal"))
|
|
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.flags += DRAWSHADER_REVERSE;
|
|
sc.MustGetToken(',');
|
|
}
|
|
this->getCoordinates(sc, cmd);
|
|
sc.MustGetToken(';');
|
|
break;
|
|
case SBARINFO_DRAWSTRING:
|
|
sc.MustGetToken(TK_Identifier);
|
|
cmd.font = V_GetFont(sc.String);
|
|
if(cmd.font == NULL)
|
|
sc.ScriptError("Unknown font '%s'.", sc.String);
|
|
sc.MustGetToken(',');
|
|
sc.MustGetToken(TK_Identifier);
|
|
cmd.translation = this->GetTranslation(sc, sc.String);
|
|
sc.MustGetToken(',');
|
|
sc.MustGetToken(TK_StringConst);
|
|
cmd.setString(sc, sc.String, 0, -1, false);
|
|
sc.MustGetToken(',');
|
|
this->getCoordinates(sc, cmd);
|
|
sc.MustGetToken(';');
|
|
break;
|
|
case SBARINFO_DRAWKEYBAR:
|
|
sc.MustGetToken(TK_IntConst);
|
|
cmd.value = sc.Number;
|
|
sc.MustGetToken(',');
|
|
sc.MustGetToken(TK_Identifier);
|
|
if(sc.Compare("vertical"))
|
|
cmd.flags += DRAWKEYBAR_VERTICAL;
|
|
else if(!sc.Compare("horizontal"))
|
|
sc.ScriptError("Unknown direction '%s'.", sc.String);
|
|
sc.MustGetToken(',');
|
|
sc.MustGetToken(TK_IntConst);
|
|
cmd.special = sc.Number;
|
|
sc.MustGetToken(',');
|
|
this->getCoordinates(sc, cmd);
|
|
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(sc, 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
|
|
{
|
|
bool foundClass = false;
|
|
for(unsigned int c = 0;c < PlayerClasses.Size();c++)
|
|
{
|
|
if(stricmp(sc.String, PlayerClasses[c].Type->Meta.GetMetaString(APMETA_DisplayName)) == 0)
|
|
{
|
|
foundClass = true;
|
|
if(i == 0)
|
|
cmd.special = PlayerClasses[c].Type->ClassIndex;
|
|
else if(i == 1)
|
|
cmd.special2 = PlayerClasses[c].Type->ClassIndex;
|
|
else //should be 2
|
|
cmd.special3 = PlayerClasses[c].Type->ClassIndex;
|
|
break;
|
|
}
|
|
}
|
|
if(!foundClass)
|
|
sc.ScriptError("Unkown PlayerClass '%s'.", sc.String);
|
|
if(sc.CheckToken('{') || i == 2)
|
|
goto FinishPlayerClass;
|
|
sc.MustGetToken(',');
|
|
}
|
|
FinishPlayerClass:
|
|
this->ParseSBarInfoBlock(sc, cmd.subBlock);
|
|
break;
|
|
case SBARINFO_WEAPONAMMO:
|
|
sc.MustGetToken(TK_Identifier);
|
|
if(sc.Compare("not"))
|
|
{
|
|
cmd.flags += SBARINFOEVENT_NOT;
|
|
sc.MustGetToken(TK_Identifier);
|
|
}
|
|
for(int i = 0;i < 2;i++)
|
|
{
|
|
cmd.setString(sc, sc.String, i);
|
|
const PClass* ammo = PClass::FindClass(sc.String);
|
|
if(ammo == NULL || !RUNTIME_CLASS(AAmmo)->IsAncestorOf(ammo)) //must be a kind of ammo
|
|
{
|
|
sc.ScriptError("'%s' is not a type of ammo.", sc.String);
|
|
}
|
|
if(sc.CheckToken(TK_OrOr))
|
|
{
|
|
cmd.flags += SBARINFOEVENT_OR;
|
|
sc.MustGetToken(TK_Identifier);
|
|
}
|
|
else if(sc.CheckToken(TK_AndAnd))
|
|
{
|
|
cmd.flags += SBARINFOEVENT_AND;
|
|
sc.MustGetToken(TK_Identifier);
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
sc.MustGetToken('{');
|
|
this->ParseSBarInfoBlock(sc, cmd.subBlock);
|
|
break;
|
|
}
|
|
block.commands.Push(cmd);
|
|
}
|
|
sc.MustGetToken('}');
|
|
}
|
|
|
|
void SBarInfo::getCoordinates(FScanner &sc, SBarInfoCommand &cmd)
|
|
{
|
|
bool negative = false;
|
|
negative = sc.CheckToken('-');
|
|
sc.MustGetToken(TK_IntConst);
|
|
cmd.x = negative ? -sc.Number : sc.Number;
|
|
sc.MustGetToken(',');
|
|
negative = sc.CheckToken('-');
|
|
sc.MustGetToken(TK_IntConst);
|
|
cmd.y = (negative ? -sc.Number : sc.Number) - (200 - this->height);
|
|
}
|
|
|
|
int SBarInfo::getSignedInteger(FScanner &sc)
|
|
{
|
|
if(sc.CheckToken('-'))
|
|
{
|
|
sc.MustGetToken(TK_IntConst);
|
|
return -sc.Number;
|
|
}
|
|
else
|
|
{
|
|
sc.MustGetToken(TK_IntConst);
|
|
return sc.Number;
|
|
}
|
|
}
|
|
|
|
int SBarInfo::newImage(const char* patchname)
|
|
{
|
|
if(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);
|
|
}
|
|
|
|
//converts a string into a tranlation.
|
|
EColorRange SBarInfo::GetTranslation(FScanner &sc, char* translation)
|
|
{
|
|
EColorRange returnVal = CR_UNTRANSLATED;
|
|
FString namedTranslation; //we must send in "[translation]"
|
|
const BYTE *trans_ptr;
|
|
namedTranslation.Format("[%s]", translation);
|
|
trans_ptr = (const BYTE *)(&namedTranslation[0]);
|
|
if((returnVal = V_ParseFontColor(trans_ptr, CR_UNTRANSLATED, CR_UNTRANSLATED)) == CR_UNDEFINED)
|
|
{
|
|
sc.ScriptError("Missing definition for color %s.", translation);
|
|
}
|
|
return returnVal;
|
|
}
|
|
|
|
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;
|
|
interpolationSpeed = 8;
|
|
armorInterpolationSpeed = 8;
|
|
height = 0;
|
|
}
|
|
|
|
SBarInfo::~SBarInfo()
|
|
{
|
|
for (size_t i = 0; i < countof(huds); ++i)
|
|
{
|
|
huds[i].commands.Clear();
|
|
}
|
|
}
|
|
|
|
void SBarInfoCommand::setString(FScanner &sc, 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;
|
|
}
|
|
}
|
|
string[strnum] = source;
|
|
}
|
|
|
|
enum
|
|
{
|
|
ST_FACENORMALRIGHT,
|
|
ST_FACENORMAL,
|
|
ST_FACENORMALLEFT,
|
|
ST_FACEPAINRIGHT,
|
|
ST_FACEPAINLEFT,
|
|
ST_FACEOUCH,
|
|
ST_FACEGRIN,
|
|
ST_FACEPAIN,
|
|
ST_FACEGOD = 72,
|
|
ST_FACEGODRIGHT = 73, //switch the roles of 0 and 1 because 0 is taken by Doom.
|
|
ST_FACEGODLEFT = 74,
|
|
ST_FACEDEAD = 75,
|
|
ST_FACEXDEAD = 76,
|
|
};
|
|
|
|
enum
|
|
{
|
|
imgARTIBOX,
|
|
imgSELECTBOX,
|
|
imgINVLFGEM1,
|
|
imgINVLFGEM2,
|
|
imgINVRTGEM1,
|
|
imgINVRTGEM2,
|
|
};
|
|
|
|
//Used for shading
|
|
class FBarShader : public FTexture
|
|
{
|
|
public:
|
|
FBarShader(bool vertical, bool reverse) //make an alpha map
|
|
{
|
|
int i;
|
|
|
|
Width = vertical ? 2 : 256;
|
|
Height = vertical ? 256 : 2;
|
|
CalcBitSize();
|
|
|
|
// Fill the column/row with shading values.
|
|
// Vertical shaders have have minimum alpha at the top
|
|
// and maximum alpha at the bottom, unless flipped by
|
|
// setting reverse to true. Horizontal shaders are just
|
|
// the opposite.
|
|
if (vertical)
|
|
{
|
|
if (!reverse)
|
|
{
|
|
for (i = 0; i < 256; ++i)
|
|
{
|
|
Pixels[i] = i;
|
|
Pixels[256+i] = i;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < 256; ++i)
|
|
{
|
|
Pixels[i] = 255 - i;
|
|
Pixels[256+i] = 255 -i;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!reverse)
|
|
{
|
|
for (i = 0; i < 256; ++i)
|
|
{
|
|
Pixels[i*2] = 255 - i;
|
|
Pixels[i*2+1] = 255 - i;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (i = 0; i < 256; ++i)
|
|
{
|
|
Pixels[i*2] = i;
|
|
Pixels[i*2+1] = i;
|
|
}
|
|
}
|
|
}
|
|
DummySpan[0].TopOffset = 0;
|
|
DummySpan[0].Length = vertical ? 256 : 1;
|
|
DummySpan[1].TopOffset = 0;
|
|
DummySpan[1].Length = 0;
|
|
}
|
|
|
|
const BYTE *GetColumn(unsigned int column, const Span **spans_out)
|
|
{
|
|
if (spans_out != NULL)
|
|
{
|
|
*spans_out = DummySpan;
|
|
}
|
|
return Pixels + (column & WidthMask) * 256;
|
|
}
|
|
|
|
const BYTE *GetPixels()
|
|
{
|
|
return Pixels;
|
|
}
|
|
|
|
void Unload()
|
|
{
|
|
}
|
|
|
|
private:
|
|
BYTE Pixels[512];
|
|
Span DummySpan[2];
|
|
};
|
|
|
|
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;
|
|
translation = CR_UNTRANSLATED;
|
|
translation2 = CR_UNTRANSLATED;
|
|
translation3 = CR_UNTRANSLATED;
|
|
font = V_GetFont("CONFONT");
|
|
}
|
|
|
|
SBarInfoCommand::~SBarInfoCommand()
|
|
{
|
|
}
|
|
|
|
SBarInfoBlock::SBarInfoBlock()
|
|
{
|
|
forceScaled = false;
|
|
}
|
|
|
|
//SBarInfo Display
|
|
class FSBarInfo : public FBaseStatusBar
|
|
{
|
|
public:
|
|
FSBarInfo () : FBaseStatusBar (SBarInfoScript->height),
|
|
shader_horz_normal(false, false),
|
|
shader_horz_reverse(false, true),
|
|
shader_vert_normal(true, false),
|
|
shader_vert_reverse(true, true)
|
|
{
|
|
static const char *InventoryBarLumps[] =
|
|
{
|
|
"ARTIBOX", "SELECTBO", "INVGEML1",
|
|
"INVGEML2", "INVGEMR1", "INVGEMR2",
|
|
"USEARTIA", "USEARTIB", "USEARTIC", "USEARTID",
|
|
};
|
|
TArray<const char *> patchnames;
|
|
patchnames.Resize(SBarInfoScript->Images.Size()+10);
|
|
unsigned int i = 0;
|
|
for(i = 0;i < SBarInfoScript->Images.Size();i++)
|
|
{
|
|
patchnames[i] = SBarInfoScript->Images[i];
|
|
}
|
|
for(i = 0;i < 10;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;
|
|
rampageTimer = 0;
|
|
faceIndex = 0;
|
|
oldHealth = 0;
|
|
oldArmor = 0;
|
|
mugshotHealth = -1;
|
|
lastPrefix = "";
|
|
weaponGrin = false;
|
|
chainWiggle = 0;
|
|
artiflash = 4;
|
|
}
|
|
|
|
~FSBarInfo ()
|
|
{
|
|
Images.Uninit();
|
|
Faces.Uninit();
|
|
}
|
|
|
|
void Draw (EHudState state)
|
|
{
|
|
FBaseStatusBar::Draw(state);
|
|
int hud = 2;
|
|
if(state == HUD_StatusBar)
|
|
{
|
|
if(SBarInfoScript->completeBorder) //Fill the statusbar with the border before we draw.
|
|
{
|
|
FTexture *b = TexMan[gameinfo.border->b];
|
|
R_DrawBorder(viewwindowx, viewwindowy + realviewheight + b->GetHeight(), viewwindowx + realviewwidth, SCREENHEIGHT);
|
|
if(screenblocks == 10)
|
|
screen->FlatFill(viewwindowx, viewwindowy + realviewheight, viewwindowx + realviewwidth, viewwindowy + realviewheight + b->GetHeight(), b, true);
|
|
}
|
|
if(SBarInfoScript->automapbar && automapactive)
|
|
{
|
|
hud = 3;
|
|
}
|
|
else
|
|
{
|
|
hud = 2;
|
|
}
|
|
}
|
|
else if(state == HUD_Fullscreen)
|
|
{
|
|
hud = 1;
|
|
}
|
|
else
|
|
{
|
|
hud = 0;
|
|
}
|
|
if(SBarInfoScript->huds[hud].forceScaled) //scale the statusbar
|
|
{
|
|
SetScaled(true);
|
|
setsizeneeded = true;
|
|
}
|
|
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();
|
|
if(level.time & 1)
|
|
chainWiggle = pr_chainwiggle() & 1;
|
|
getNewFace(M_Random());
|
|
if(!SBarInfoScript->interpolateHealth)
|
|
{
|
|
oldHealth = CPlayer->health;
|
|
}
|
|
else
|
|
{
|
|
if(oldHealth > CPlayer->health)
|
|
{
|
|
oldHealth -= clamp((oldHealth - CPlayer->health) >> 2, 1, SBarInfoScript->interpolationSpeed);
|
|
}
|
|
else if(oldHealth < CPlayer->health)
|
|
{
|
|
oldHealth += clamp((CPlayer->health - oldHealth) >> 2, 1, SBarInfoScript->interpolationSpeed);
|
|
}
|
|
}
|
|
AInventory *armor = CPlayer->mo->FindInventory<ABasicArmor>();
|
|
if(armor == NULL)
|
|
{
|
|
oldArmor = 0;
|
|
}
|
|
else
|
|
{
|
|
if(!SBarInfoScript->interpolateArmor)
|
|
{
|
|
oldArmor = armor->Amount;
|
|
}
|
|
else
|
|
{
|
|
if(oldArmor > armor->Amount)
|
|
{
|
|
oldArmor -= clamp((oldArmor - armor->Amount) >> 2, 1, SBarInfoScript->armorInterpolationSpeed);
|
|
}
|
|
else if(oldArmor < armor->Amount)
|
|
{
|
|
oldArmor += clamp((armor->Amount - oldArmor) >> 2, 1, SBarInfoScript->armorInterpolationSpeed);
|
|
}
|
|
}
|
|
}
|
|
if(artiflash)
|
|
{
|
|
artiflash--;
|
|
}
|
|
}
|
|
|
|
void SetFace (void *skn)
|
|
{
|
|
SetFace((FPlayerSkin *)skn, "STF");
|
|
}
|
|
|
|
void ReceivedWeapon (AWeapon *weapon)
|
|
{
|
|
weaponGrin = true;
|
|
}
|
|
|
|
void FlashItem(const PClass *itemtype)
|
|
{
|
|
artiflash = 4;
|
|
}
|
|
private:
|
|
//code from doom_sbar.cpp but it should do fine.
|
|
void SetFace (FPlayerSkin *skin, const 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
|
|
}
|
|
for (i = 0; i < 3; i++)
|
|
sprintf (names[facenum++], "%sGOD%d", prefix, i);
|
|
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);
|
|
ABasicArmor *armor = CPlayer->mo->FindInventory<ABasicArmor>();
|
|
int health = CPlayer->mo->health;
|
|
int armorAmount = armor != NULL ? armor->Amount : 0;
|
|
if(SBarInfoScript->interpolateHealth)
|
|
{
|
|
health = oldHealth;
|
|
}
|
|
if(SBarInfoScript->interpolateArmor)
|
|
{
|
|
armorAmount = oldArmor;
|
|
}
|
|
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)) //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))
|
|
{
|
|
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 || item->Amount == 0)
|
|
drawAlt = 1;
|
|
if((cmd.flags & DRAWIMAGE_SWITCHABLE_AND))
|
|
{
|
|
item = CPlayer->mo->FindInventory(PClass::FindClass(cmd.string[1]));
|
|
if((item != NULL && item->Amount != 0) && drawAlt == 0) //both
|
|
{
|
|
drawAlt = 0;
|
|
}
|
|
else if((item != NULL && item->Amount != 0) && drawAlt == 1) //2nd
|
|
{
|
|
drawAlt = 3;
|
|
}
|
|
else if((item == NULL || item->Amount == 0) && 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))
|
|
DrawGraphic(TexMan[CPlayer->mo->ScoreIcon], cmd.x, cmd.y, cmd.flags);
|
|
else if((cmd.flags & DRAWIMAGE_AMMO1))
|
|
{
|
|
if(ammo1 != NULL)
|
|
DrawGraphic(TexMan[ammo1->Icon], cmd.x, cmd.y, cmd.flags);
|
|
}
|
|
else if((cmd.flags & DRAWIMAGE_AMMO2))
|
|
{
|
|
if(ammo2 != NULL)
|
|
DrawGraphic(TexMan[ammo2->Icon], cmd.x, cmd.y, cmd.flags);
|
|
}
|
|
else if((cmd.flags & DRAWIMAGE_ARMOR))
|
|
{
|
|
if(armor != NULL && armor->Amount != 0)
|
|
DrawGraphic(TexMan(armor->Icon), cmd.x, cmd.y, cmd.flags);
|
|
}
|
|
else if((cmd.flags & DRAWIMAGE_WEAPONICON))
|
|
{
|
|
AWeapon *weapon = CPlayer->ReadyWeapon;
|
|
if(weapon != NULL && weapon->Icon > 0)
|
|
{
|
|
DrawGraphic(TexMan[weapon->Icon], cmd.x, cmd.y, cmd.flags);
|
|
}
|
|
}
|
|
else if((cmd.flags & DRAWIMAGE_INVENTORYICON))
|
|
{
|
|
DrawGraphic(TexMan[cmd.sprite], 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)
|
|
{
|
|
cmd.value = armorAmount;
|
|
}
|
|
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_KILLS)
|
|
cmd.value = level.killed_monsters;
|
|
else if(cmd.flags == DRAWNUMBER_MONSTERS)
|
|
cmd.value = level.total_monsters;
|
|
else if(cmd.flags == DRAWNUMBER_ITEMS)
|
|
cmd.value = level.found_items;
|
|
else if(cmd.flags == DRAWNUMBER_TOTALITEMS)
|
|
cmd.value = level.total_items;
|
|
else if(cmd.flags == DRAWNUMBER_SECRETS)
|
|
cmd.value = level.found_secrets;
|
|
else if(cmd.flags == DRAWNUMBER_TOTALSECRETS)
|
|
cmd.value = level.total_secrets;
|
|
else if(cmd.flags == DRAWNUMBER_ARMORCLASS)
|
|
{
|
|
AHexenArmor *harmor = CPlayer->mo->FindInventory<AHexenArmor>();
|
|
if(harmor != NULL)
|
|
{
|
|
cmd.value = harmor->Slots[0] + harmor->Slots[1] +
|
|
harmor->Slots[2] + harmor->Slots[3] + harmor->Slots[4];
|
|
}
|
|
//Hexen counts basic armor also so we should too.
|
|
if(armor != NULL)
|
|
{
|
|
cmd.value += armor->SavePercent;
|
|
}
|
|
cmd.value /= (5*FRACUNIT);
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
if(cmd.special3 != -1 && cmd.value <= cmd.special3) //low
|
|
DrawNumber(cmd.value, cmd.special, cmd.x, cmd.y, cmd.translation2, cmd.special2);
|
|
else if(cmd.special4 != -1 && cmd.value >= cmd.special4) //high
|
|
DrawNumber(cmd.value, cmd.special, cmd.x, cmd.y, cmd.translation3, cmd.special2);
|
|
else
|
|
DrawNumber(cmd.value, cmd.special, cmd.x, cmd.y, cmd.translation, cmd.special2);
|
|
break;
|
|
case SBARINFO_DRAWMUGSHOT:
|
|
{
|
|
bool xdth = false;
|
|
bool animatedgodmode = false;
|
|
if(cmd.flags & DRAWMUGSHOT_XDEATHFACE)
|
|
xdth = true;
|
|
if(cmd.flags & DRAWMUGSHOT_ANIMATEDGODMODE)
|
|
animatedgodmode = true;
|
|
SetFace(oldSkin, cmd.string[0].GetChars());
|
|
DrawFace(cmd.special, xdth, animatedgodmode, cmd.x, cmd.y);
|
|
break;
|
|
}
|
|
case SBARINFO_DRAWSELECTEDINVENTORY:
|
|
if(CPlayer->mo->InvSel != NULL && !(level.flags & LEVEL_NOINVENTORYBAR))
|
|
{
|
|
if((cmd.flags & DRAWSELECTEDINVENTORY_ARTIFLASH) && artiflash)
|
|
{
|
|
DrawDimImage(Images[ARTIFLASH_OFFSET+(4-artiflash)], cmd.x, cmd.y, CPlayer->mo->InvSel->Amount <= 0);
|
|
}
|
|
else
|
|
{
|
|
DrawDimImage(TexMan(CPlayer->mo->InvSel->Icon), cmd.x, cmd.y, CPlayer->mo->InvSel->Amount <= 0);
|
|
}
|
|
if((cmd.flags & DRAWSELECTEDINVENTORY_ALWAYSSHOWCOUNTER) || 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))
|
|
{
|
|
doCommands(cmd.subBlock);
|
|
}
|
|
break;
|
|
case SBARINFO_DRAWINVENTORYBAR:
|
|
{
|
|
bool alwaysshow = false;
|
|
bool artibox = true;
|
|
bool noarrows = false;
|
|
bool alwaysshowcounter = false;
|
|
if((cmd.flags & DRAWINVENTORYBAR_ALWAYSSHOW))
|
|
alwaysshow = true;
|
|
if((cmd.flags & DRAWINVENTORYBAR_NOARTIBOX))
|
|
artibox = false;
|
|
if((cmd.flags & DRAWINVENTORYBAR_NOARROWS))
|
|
noarrows = true;
|
|
if((cmd.flags & DRAWINVENTORYBAR_ALWAYSSHOWCOUNTER))
|
|
alwaysshowcounter = true;
|
|
if(drawingFont != cmd.font)
|
|
{
|
|
drawingFont = cmd.font;
|
|
}
|
|
DrawInventoryBar(cmd.special, cmd.value, cmd.x, cmd.y, alwaysshow, cmd.special2, cmd.special3, cmd.translation, artibox, noarrows, alwaysshowcounter);
|
|
break;
|
|
}
|
|
case SBARINFO_DRAWBAR:
|
|
{
|
|
if(cmd.sprite == -1 || Images[cmd.sprite] == NULL)
|
|
break; //don't draw anything.
|
|
bool horizontal = !!((cmd.special2 & DRAWBAR_HORIZONTAL));
|
|
bool reverse = !!((cmd.special2 & DRAWBAR_REVERSE));
|
|
fixed_t value = 0;
|
|
int max = 0;
|
|
if(cmd.flags == DRAWNUMBER_HEALTH)
|
|
{
|
|
value = health;
|
|
if(value < 0) //health shouldn't display negatives
|
|
{
|
|
value = 0;
|
|
}
|
|
if(!(cmd.special2 & 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 class's health
|
|
{
|
|
max = CPlayer->mo->GetDefault()->health;
|
|
}
|
|
}
|
|
else if(cmd.flags == DRAWNUMBER_ARMOR)
|
|
{
|
|
value = armorAmount;
|
|
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, draw as empty
|
|
{
|
|
value = 0;
|
|
max = 1;
|
|
}
|
|
else
|
|
max = ammo1->MaxAmount;
|
|
}
|
|
else if(cmd.flags == DRAWNUMBER_AMMO2)
|
|
{
|
|
value = ammocount2;
|
|
if(ammo2 == NULL) //no ammo, draw as empty
|
|
{
|
|
value = 0;
|
|
max = 1;
|
|
}
|
|
else
|
|
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_KILLS)
|
|
{
|
|
value = level.killed_monsters;
|
|
max = level.total_monsters;
|
|
}
|
|
else if(cmd.flags == DRAWNUMBER_ITEMS)
|
|
{
|
|
value = level.found_items;
|
|
max = level.total_items;
|
|
}
|
|
else if(cmd.flags == DRAWNUMBER_SECRETS)
|
|
{
|
|
value = level.found_secrets;
|
|
max = level.total_secrets;
|
|
}
|
|
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;
|
|
}
|
|
}
|
|
value = max - value; //invert since the new drawing method requires drawing the bg on the fg.
|
|
if(max != 0 && value > 0)
|
|
{
|
|
value = (value << FRACBITS) / max;
|
|
if(value > FRACUNIT)
|
|
value = FRACUNIT;
|
|
}
|
|
else if(max == 0 && value <= 0)
|
|
{
|
|
value = FRACUNIT;
|
|
}
|
|
else
|
|
{
|
|
value = 0;
|
|
}
|
|
assert(Images[cmd.sprite] != NULL);
|
|
|
|
FTexture *fg = Images[cmd.sprite];
|
|
FTexture *bg = (cmd.special != -1) ? Images[cmd.special] : NULL;
|
|
int x, y, w, h;
|
|
int cx, cy, cw, ch, cr, cb;
|
|
|
|
// Calc real screen coordinates for bar
|
|
x = cmd.x + ST_X;
|
|
y = cmd.y + ST_Y;
|
|
w = fg->GetWidth();
|
|
h = fg->GetHeight();
|
|
if (Scaled)
|
|
{
|
|
screen->VirtualToRealCoordsInt(x, y, w, h, 320, 200, true);
|
|
}
|
|
|
|
//Draw the whole foreground
|
|
screen->DrawTexture(fg, x, y,
|
|
DTA_DestWidth, w,
|
|
DTA_DestHeight, h,
|
|
TAG_DONE);
|
|
|
|
// Calc clipping rect for background
|
|
cx = cmd.x + ST_X + cmd.special3;
|
|
cy = cmd.y + ST_Y + cmd.special3;
|
|
cw = fg->GetWidth() - cmd.special3 * 2;
|
|
ch = fg->GetHeight() - cmd.special3 * 2;
|
|
if (Scaled)
|
|
{
|
|
screen->VirtualToRealCoordsInt(cx, cy, cw, ch, 320, 200, true);
|
|
}
|
|
if (horizontal)
|
|
{
|
|
if (reverse)
|
|
{ // left to right
|
|
cr = cx + FixedMul(cw, value);
|
|
}
|
|
else
|
|
{ // right to left
|
|
cr = cx + cw;
|
|
cx += FixedMul(cw, FRACUNIT - value);
|
|
}
|
|
cb = cy + ch;
|
|
}
|
|
else
|
|
{
|
|
if (reverse)
|
|
{ // bottom to top
|
|
cb = cy + ch;
|
|
cy += FixedMul(ch, FRACUNIT - value);
|
|
}
|
|
else
|
|
{ // top to bottom
|
|
cb = cy + FixedMul(ch, value);
|
|
}
|
|
cr = cx + cw;
|
|
}
|
|
|
|
// Draw background
|
|
if (bg != NULL && bg->GetWidth() == fg->GetWidth() && bg->GetHeight() == fg->GetHeight())
|
|
{
|
|
screen->DrawTexture(bg, x, y,
|
|
DTA_DestWidth, w,
|
|
DTA_DestHeight, h,
|
|
DTA_ClipLeft, cx,
|
|
DTA_ClipTop, cy,
|
|
DTA_ClipRight, cr,
|
|
DTA_ClipBottom, cb,
|
|
TAG_DONE);
|
|
}
|
|
else
|
|
{
|
|
screen->Clear(cx, cy, cr, cb, GPalette.BlackIndex, 0);
|
|
}
|
|
break;
|
|
}
|
|
case SBARINFO_DRAWGEM:
|
|
{
|
|
int value = (cmd.flags & DRAWGEM_ARMOR) ? armorAmount : health;
|
|
int max = 100;
|
|
bool wiggle = false;
|
|
bool translate = !!(cmd.flags & DRAWGEM_TRANSLATABLE);
|
|
if(max != 0 || value < 0)
|
|
{
|
|
value = (value*100)/max;
|
|
if(value > 100)
|
|
value = 100;
|
|
}
|
|
else
|
|
{
|
|
value = 0;
|
|
}
|
|
value = (cmd.flags & DRAWGEM_REVERSE) ? 100 - value : value;
|
|
if(health != CPlayer->health)
|
|
{
|
|
wiggle = !!(cmd.flags & DRAWGEM_WIGGLE);
|
|
}
|
|
DrawGem(Images[cmd.special], Images[cmd.sprite], value, cmd.x, cmd.y, cmd.special2, cmd.special3, cmd.special4+1, wiggle, translate);
|
|
break;
|
|
}
|
|
case SBARINFO_DRAWSHADER:
|
|
{
|
|
FBarShader *const shaders[4] =
|
|
{
|
|
&shader_horz_normal, &shader_horz_reverse,
|
|
&shader_vert_normal, &shader_vert_reverse
|
|
};
|
|
bool vertical = !!(cmd.flags & DRAWSHADER_VERTICAL);
|
|
bool reverse = !!(cmd.flags & DRAWSHADER_REVERSE);
|
|
screen->DrawTexture (shaders[(vertical << 1) + reverse], ST_X+cmd.x, ST_Y+cmd.y,
|
|
DTA_DestWidth, cmd.special,
|
|
DTA_DestHeight, cmd.special2,
|
|
DTA_Bottom320x200, Scaled,
|
|
DTA_AlphaChannel, true,
|
|
DTA_FillColor, 0,
|
|
TAG_DONE);
|
|
break;
|
|
}
|
|
case SBARINFO_DRAWSTRING:
|
|
if(drawingFont != cmd.font)
|
|
{
|
|
drawingFont = cmd.font;
|
|
}
|
|
DrawString(cmd.string[0], cmd.x - drawingFont->StringWidth(cmd.string[0]), cmd.y, cmd.translation);
|
|
break;
|
|
case SBARINFO_DRAWKEYBAR:
|
|
{
|
|
bool vertical = !!(cmd.flags & DRAWKEYBAR_VERTICAL);
|
|
AInventory *item = CPlayer->mo->Inventory;
|
|
if(item == NULL)
|
|
break;
|
|
for(int i = 0;i < cmd.value;i++)
|
|
{
|
|
while(item->Icon <= 0 || item->GetClass() == RUNTIME_CLASS(AKey) || !item->IsKindOf(RUNTIME_CLASS(AKey)))
|
|
{
|
|
item = item->Inventory;
|
|
if(item == NULL)
|
|
goto FinishDrawKeyBar;
|
|
}
|
|
if(!vertical)
|
|
DrawImage(TexMan[item->Icon], cmd.x+(cmd.special*i), cmd.y);
|
|
else
|
|
DrawImage(TexMan[item->Icon], cmd.x, cmd.y+(cmd.special*i));
|
|
item = item->Inventory;
|
|
if(item == NULL)
|
|
break;
|
|
}
|
|
FinishDrawKeyBar:
|
|
break;
|
|
}
|
|
case SBARINFO_GAMEMODE:
|
|
if(((cmd.flags & GAMETYPE_SINGLEPLAYER) && !multiplayer) ||
|
|
((cmd.flags & GAMETYPE_DEATHMATCH) && deathmatch) ||
|
|
((cmd.flags & GAMETYPE_COOPERATIVE) && multiplayer && !deathmatch) ||
|
|
((cmd.flags & GAMETYPE_TEAMGAME) && teamplay))
|
|
{
|
|
doCommands(cmd.subBlock);
|
|
}
|
|
break;
|
|
case SBARINFO_PLAYERCLASS:
|
|
{
|
|
int spawnClass = CPlayer->cls->ClassIndex;
|
|
if(cmd.special == spawnClass || cmd.special2 == spawnClass || cmd.special3 == spawnClass)
|
|
{
|
|
doCommands(cmd.subBlock);
|
|
}
|
|
break;
|
|
}
|
|
case SBARINFO_WEAPONAMMO:
|
|
if(CPlayer->ReadyWeapon != NULL)
|
|
{
|
|
const PClass *AmmoType1 = CPlayer->ReadyWeapon->AmmoType1;
|
|
const PClass *AmmoType2 = CPlayer->ReadyWeapon->AmmoType2;
|
|
const PClass *IfAmmo1 = PClass::FindClass(cmd.string[0]);
|
|
const PClass *IfAmmo2 = (cmd.flags & SBARINFOEVENT_OR) || (cmd.flags & SBARINFOEVENT_AND) ?
|
|
PClass::FindClass(cmd.string[1]) : NULL;
|
|
bool usesammo1 = (AmmoType1 != NULL);
|
|
bool usesammo2 = (AmmoType2 != NULL);
|
|
if(!(cmd.flags & SBARINFOEVENT_NOT) && !usesammo1 && !usesammo2) //if the weapon doesn't use ammo don't go though the trouble.
|
|
{
|
|
doCommands(cmd.subBlock);
|
|
break;
|
|
}
|
|
//Or means only 1 ammo type needs to match and means both need to match.
|
|
if((cmd.flags & SBARINFOEVENT_OR) || (cmd.flags & SBARINFOEVENT_AND))
|
|
{
|
|
bool match1 = ((usesammo1 && (AmmoType1 == IfAmmo1 || AmmoType1 == IfAmmo2)) || !usesammo1);
|
|
bool match2 = ((usesammo2 && (AmmoType2 == IfAmmo1 || AmmoType2 == IfAmmo2)) || !usesammo2);
|
|
if(((cmd.flags & SBARINFOEVENT_OR) && (match1 || match2)) || ((cmd.flags & SBARINFOEVENT_AND) && (match1 && match2)))
|
|
{
|
|
if(!(cmd.flags & SBARINFOEVENT_NOT))
|
|
doCommands(cmd.subBlock);
|
|
}
|
|
else if(cmd.flags & SBARINFOEVENT_NOT)
|
|
{
|
|
doCommands(cmd.subBlock);
|
|
}
|
|
}
|
|
else //Every thing here could probably be one long if statement but then it would be more confusing.
|
|
{
|
|
if((usesammo1 && (AmmoType1 == IfAmmo1)) || (usesammo2 && (AmmoType2 == IfAmmo1)))
|
|
{
|
|
if(!(cmd.flags & SBARINFOEVENT_NOT))
|
|
doCommands(cmd.subBlock);
|
|
}
|
|
else if(cmd.flags & SBARINFOEVENT_NOT)
|
|
{
|
|
doCommands(cmd.subBlock);
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//draws an image with the specified flags
|
|
void DrawGraphic(FTexture* texture, int x, int y, int flags)
|
|
{
|
|
if((flags & DRAWIMAGE_OFFSET_CENTER))
|
|
{
|
|
x -= (texture->GetWidth()/2)-texture->LeftOffset;
|
|
y -= (texture->GetHeight()/2)-texture->TopOffset;
|
|
}
|
|
if((flags & DRAWIMAGE_TRANSLATABLE))
|
|
DrawImage(texture, x, y, getTranslation());
|
|
else
|
|
DrawImage(texture, x, y);
|
|
}
|
|
|
|
void DrawString(const char* str, int x, int y, EColorRange translation, int spacing=0)
|
|
{
|
|
x += spacing;
|
|
while(*str != '\0')
|
|
{
|
|
if(*str == ' ')
|
|
{
|
|
x += drawingFont->GetSpaceWidth();
|
|
str++;
|
|
continue;
|
|
}
|
|
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) ceil(pow(10., len))-1;
|
|
num = clamp(num, -maxval, maxval);
|
|
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, bool animatedgodmode, int x, int y)
|
|
{
|
|
if(CPlayer->health > 0)
|
|
{
|
|
if(faceIndex == ST_FACEGOD || faceIndex == ST_FACEGODLEFT || faceIndex == ST_FACEGODRIGHT) //nothing fancy to do here
|
|
{
|
|
if(animatedgodmode)
|
|
DrawImage(Faces[faceIndex], x, y);
|
|
else
|
|
DrawImage(Faces[ST_FACEGOD], x, y);
|
|
}
|
|
else
|
|
{
|
|
int level = 0;
|
|
for(level = 0;CPlayer->health < (accuracy-level-1)*(CPlayer->mo->GetMaxHealth()/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+5 && 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;
|
|
}
|
|
if((CPlayer->cmd.ucmd.buttons & (BT_ATTACK|BT_ALTATTACK)) && !(CPlayer->cheats & (CF_FROZEN | CF_TOTALLYFROZEN)))
|
|
{
|
|
if(rampageTimer == ST_RAMPAGETIME)
|
|
{
|
|
faceIndex = ST_FACEPAIN;
|
|
faceTimer = 1;
|
|
}
|
|
else
|
|
{
|
|
rampageTimer++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
rampageTimer = 0;
|
|
}
|
|
// invulnerability
|
|
if (((CPlayer->cheats & CF_GODMODE) || (CPlayer->mo != NULL && CPlayer->mo->flags2 & MF2_INVULNERABLE)) &&
|
|
(faceTimer == 0 || faceIndex <= 2))
|
|
{
|
|
faceIndex = ST_FACEGOD + (number % 3);
|
|
faceTimer = ST_FACETIME;
|
|
}
|
|
|
|
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, bool noArrows, bool alwaysshowcounter)
|
|
{ //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);
|
|
}
|
|
DrawDimImage (TexMan(item->Icon), x+i*31, y, item->Amount <= 0);
|
|
if(alwaysshowcounter || 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 (!noArrows && CPlayer->mo->FirstInv() != CPlayer->mo->InvFirst)
|
|
{
|
|
DrawImage (Images[!(gametic & 4) ?
|
|
invBarOffset + imgINVLFGEM1 : invBarOffset + imgINVLFGEM2], x-12, y);
|
|
}
|
|
// Is there something to the right?
|
|
if (!noArrows && item != NULL)
|
|
{
|
|
DrawImage (Images[!(gametic & 4) ?
|
|
invBarOffset + imgINVRTGEM1 : invBarOffset + imgINVRTGEM2], x+num*31+2, y);
|
|
}
|
|
}
|
|
}
|
|
|
|
//draws heretic/hexen style life gems
|
|
void DrawGem(FTexture* chain, FTexture* gem, int value, int x, int y, int padleft, int padright, int chainsize,
|
|
bool wiggle, bool translate)
|
|
{
|
|
if(value > 100)
|
|
value = 100;
|
|
else if(value < 0)
|
|
value = 0;
|
|
if(wiggle)
|
|
y += chainWiggle;
|
|
int gemWidth = gem->GetWidth();
|
|
int chainWidth = chain->GetWidth();
|
|
int offset = (int) (((double) (chainWidth-padleft-padright)/100)*value);
|
|
if(chain != NULL)
|
|
{
|
|
DrawImage(chain, x+(offset%chainsize), y);
|
|
}
|
|
if(gem != NULL)
|
|
DrawImage(gem, x+padleft+offset, y, translate ? getTranslation() : NULL);
|
|
}
|
|
|
|
FRemapTable* getTranslation()
|
|
{
|
|
if(gameinfo.gametype & GAME_Raven)
|
|
return translationtables[TRANSLATION_PlayersExtra][int(CPlayer - players)];
|
|
return translationtables[TRANSLATION_Players][int(CPlayer - players)];
|
|
}
|
|
|
|
FImageCollection Images;
|
|
FImageCollection Faces;
|
|
FPlayerSkin *oldSkin;
|
|
FFont *drawingFont;
|
|
FString lastPrefix;
|
|
bool weaponGrin;
|
|
int faceTimer;
|
|
int rampageTimer;
|
|
int faceIndex;
|
|
int oldHealth;
|
|
int oldArmor;
|
|
int mugshotHealth;
|
|
int chainWiggle;
|
|
int artiflash;
|
|
unsigned int invBarOffset;
|
|
FBarShader shader_horz_normal;
|
|
FBarShader shader_horz_reverse;
|
|
FBarShader shader_vert_normal;
|
|
FBarShader shader_vert_reverse;
|
|
};
|
|
|
|
FBaseStatusBar *CreateCustomStatusBar ()
|
|
{
|
|
return new FSBarInfo;
|
|
}
|