vkdoom_m/src/g_shared/sbarinfo.cpp
Randy Heit d1e27e533f - Applied a modified version of Karate Chris's screenshot naming patch.
- Sbarinfo optimization: Creating and destroying bar textures every frame is
  a relatively expensive operation. We can skip the custom texture entirely
  and just draw the bars directly to the screen, using the clipping parameters
  for DrawTexture(). This also means bars are no longer limited to the game
  palette, and the bar itself has the same resolution as the screen.


SVN r731 (trunk)
2008-02-05 05:29:31 +00:00

2309 lines
61 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"
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)
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,
};
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,
};
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",
"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",
"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;
}
}
//Laz Bar 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:
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.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);
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
{
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);
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;
}
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);
}
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
sc.ScriptError("Unknown drawgem flag '%s'.", sc.String);
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;
}
// 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;
}
}
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;
interpolationSpeed = 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;
if(SBarInfoScript->interpolateHealth)
{
oldHealth = 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->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, 8);
}
else if(oldHealth < CPlayer->health)
{
oldHealth += clamp((CPlayer->health - oldHealth) >> 2, 1, 8);
}
}
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);
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)) //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))
{
ABasicArmor *armor = CPlayer->mo->FindInventory<ABasicArmor>();
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)
{
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_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_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)
{
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, 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;
}
}
if(max != 0 && value > 0)
{
value = (value << FRACBITS) / max;
if(value > FRACUNIT)
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 background
if (bg != NULL && bg->GetWidth() == fg->GetWidth() && bg->GetHeight() == fg->GetHeight())
{
screen->DrawTexture(bg, x, y,
DTA_DestWidth, w,
DTA_DestHeight, h,
TAG_DONE);
}
else
{
screen->Clear(x, y, x + w, y + h, GPalette.BlackIndex, 0);
}
// Calc clipping rect for foreground
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 foreground
screen->DrawTexture(fg, x, y,
DTA_DestWidth, w,
DTA_DestHeight, h,
DTA_ClipLeft, cx,
DTA_ClipTop, cy,
DTA_ClipRight, cr,
DTA_ClipBottom, cb,
TAG_DONE);
break;
}
case SBARINFO_DRAWGEM:
{
int value = 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;
}
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 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;
}