mirror of
https://github.com/ZDoom/qzdoom.git
synced 2025-03-01 23:31:16 +00:00
- Added .txt files to the list of types (wad, zip, and pk3) that can be loaded without listing them after -file. - Fonts that are created by the ACS setfont command to wrap a texture now support animated textures. - FON2 fonts can now use their full palette for CR_UNTRANSLATED when drawn with the hardware 2D path instead of being restricted to the game palette. - Fixed: Toggling vid_vsync would reset the displayed fullscreen gamma to 1 on a Radeon 9000. - Added back the off-by-one palette handling, but in a much more limited scope than before. The skipped entry is assumed to always be at 248, and it is assumed that all Shader Model 1.4 cards suffer from this. That's because all SM1.4 cards are based on variants of the ATI R200 core, and the RV250 in a Radeon 9000 craps up like this. I see no reason to assume that other flavors of the R200 are any different. (Interesting note: With the Radeon 9000, D3DTADDRESS_CLAMP is an invalid address mode when using the debug Direct3D 9 runtime, but it works perfectly fine with the retail Direct3D 9 runtime.) (Insight: The R200 probably uses bytes for all its math inside pixel shaders. That would explain perfectly why I can't use constants greater than 1 with PS1.4 and why it can't do an exact mapping to every entry in the color palette. - Fixed: The software shaded drawer did not work for 2D, because its selected "color"map was replaced with the identitymap before being used. - Fixed: I cannot use Printf to output messages before the framebuffer was completely setup, meaning that Shader Model 1.4 cards could not change resolution. - I have decided to let remap palettes specify variable alpha values for their colors. D3DFB no longer forces them to 255. - Updated re2c to version 0.12.3. - Fixed: A_Wander used threshold as a timer, when it should have used reactiontime. - Fixed: A_CustomRailgun would not fire at all for actors without a target when the aim parameter was disabled. - Made the warp command work in multiplayer, again courtesy of Karate Chris. - Fixed: Trying to spawn a bot while not in a game made for a crashing time. (Patch courtesy of Karate Chris.) - Removed some floating point math from hu_scores.cpp that somebody's GCC gave warnings for (not mine, though). - Fixed: The SBarInfo drawbar command crashed if the sprite image was unavailable. - Fixed: FString::operator=(const char *) did not release its old buffer when being assigned to the null string. - The scanner no longer has an upper limit on the length of strings it accepts, though short strings will be faster than long ones. - Moved all the text scanning functions into a class. Mainly, this means that multiple script scanner states can be stored without being forced to do so recursively. I think I might be taking advantage of that in the near future. Possibly. Maybe. - Removed some potential buffer overflows from the decal parser. - Applied Blzut3's SBARINFO update #9: * Fixed: When using even length values in drawnumber it would cap to a 98 value instead of a 99 as intended. * The SBarInfo parser can now accept negatives for coordinates. This doesn't allow much right now, but later I plan to add better fullscreen hud support in which the negatives will be more useful. This also cleans up the source a bit since all calls for (x, y) coordinates are with the function getCoordinates(). - Added support for stencilling actors. - Added support for non-black colors specified with DTA_ColorOverlay to the software renderer. - Fixed: The inverse, gold, red, and green fixed colormaps each allocated space for 32 different colormaps, even though each only used the first one. - Added two new blending flags to make reverse subtract blending more useful: STYLEF_InvertSource and STYLEF_InvertOverlay. These invert the color that gets blended with the background, since that seems like a good idea for reverse subtraction. They also work with the other two blending operations. - Added subtract and reverse subtract blending operations to the renderer. Since the ERenderStyle enumeration was getting rather unwieldy, I converted it into a new FRenderStyle structure that lets each parameter of the blending equation be set separately. This simplified the set up for the blend quite a bit, and it means a number of new combinations are available by setting the parameters properly. SVN r710 (trunk)
2207 lines
57 KiB
C++
2207 lines
57 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,
|
|
};
|
|
|
|
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",
|
|
"gamemode",
|
|
"playerclass",
|
|
NULL
|
|
};
|
|
enum
|
|
{
|
|
SBARINFO_DRAWIMAGE,
|
|
SBARINFO_DRAWNUMBER,
|
|
SBARINFO_DRAWSWITCHABLEIMAGE,
|
|
SBARINFO_DRAWMUGSHOT,
|
|
SBARINFO_DRAWSELECTEDINVENTORY,
|
|
SBARINFO_DRAWINVENTORYBAR,
|
|
SBARINFO_DRAWBAR,
|
|
SBARINFO_DRAWGEM,
|
|
SBARINFO_DRAWSHADER,
|
|
SBARINFO_DRAWSTRING,
|
|
SBARINFO_GAMEMODE,
|
|
SBARINFO_PLAYERCLASS,
|
|
};
|
|
|
|
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 || !PClass::FindClass("Ammo")->IsAncestorOf(ammo)) //must be a kind of ammo
|
|
{
|
|
sc.ScriptError("'%s' is not a type of ammo.", sc.String);
|
|
}
|
|
}
|
|
else if(sc.Compare("ammocapacity"))
|
|
{
|
|
sc.MustGetToken(TK_Identifier);
|
|
cmd.setString(sc, sc.String, 0);
|
|
cmd.flags = DRAWNUMBER_AMMOCAPACITY;
|
|
const PClass* ammo = PClass::FindClass(sc.String);
|
|
if(ammo == NULL || !PClass::FindClass("Ammo")->IsAncestorOf(ammo)) //must be a kind of ammo
|
|
{
|
|
sc.ScriptError("'%s' is not a type of ammo.", sc.String);
|
|
}
|
|
}
|
|
else if(sc.Compare("frags"))
|
|
cmd.flags = DRAWNUMBER_FRAGS;
|
|
else 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("Exspected 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);
|
|
if(sc.CheckToken(',')) //hmm I guess we had a numberic flag in there.
|
|
{
|
|
cmd.flags = cmd.x;
|
|
cmd.x = cmd.y + (200 - this->height);
|
|
sc.MustGetToken(TK_IntConst);
|
|
cmd.y = sc.Number - (200 - this->height);
|
|
}
|
|
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 || !PClass::FindClass("Ammo")->IsAncestorOf(ammo)) //must be a kind of ammo
|
|
{
|
|
sc.ScriptError("'%s' is not a type of ammo.", sc.String);
|
|
}
|
|
}
|
|
else if(sc.Compare("frags"))
|
|
cmd.flags = DRAWNUMBER_FRAGS;
|
|
else 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 || !PClass::FindClass("Inventory")->IsAncestorOf(item)) //must be a kind of ammo
|
|
{
|
|
sc.ScriptError("'%s' is not a type of inventory item.", sc.String);
|
|
}
|
|
}
|
|
sc.MustGetToken(',');
|
|
sc.MustGetToken(TK_Identifier);
|
|
if(sc.Compare("horizontal"))
|
|
cmd.special2 += DRAWBAR_HORIZONTAL;
|
|
else if(!sc.Compare("vertical"))
|
|
sc.ScriptError("Unknown direction '%s'.", sc.String);
|
|
sc.MustGetToken(',');
|
|
if(sc.CheckToken(TK_Identifier))
|
|
{
|
|
if(!sc.Compare("reverse"))
|
|
{
|
|
sc.ScriptError("Exspected 'reverse', got '%s' instead.", sc.String);
|
|
}
|
|
cmd.special2 += DRAWBAR_REVERSE;
|
|
sc.MustGetToken(',');
|
|
}
|
|
this->getCoordinates(sc, cmd);
|
|
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("Unkown 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(',');
|
|
sc.MustGetToken(TK_IntConst);
|
|
if(sc.Number < 0)
|
|
sc.ScriptError("Left padding must be a positive number.");
|
|
cmd.special2 = sc.Number;
|
|
sc.MustGetToken(',');
|
|
sc.MustGetToken(TK_IntConst);
|
|
if(sc.Number < 0)
|
|
sc.ScriptError("Right padding must be a positive number.");
|
|
cmd.special3 = sc.Number;
|
|
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_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;
|
|
}
|
|
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::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,
|
|
};
|
|
|
|
//The next class allows us to draw bars
|
|
class FBarTexture : public FTexture
|
|
{
|
|
public:
|
|
~FBarTexture()
|
|
{
|
|
delete Pixels;
|
|
}
|
|
|
|
void Unload()
|
|
{
|
|
if(image != NULL)
|
|
{
|
|
image->Unload ();
|
|
}
|
|
}
|
|
|
|
const BYTE *GetColumn(unsigned int column, const Span **spans_out)
|
|
{
|
|
if (column > (unsigned int) Width)
|
|
{
|
|
column = Width;
|
|
}
|
|
image->GetColumn(column, spans_out);
|
|
return Pixels + column*Height;
|
|
}
|
|
|
|
const BYTE *GetPixels()
|
|
{
|
|
return Pixels;
|
|
}
|
|
|
|
FBarTexture(FTexture* bar, FTexture* bg, int value, bool horizontal, bool reverse)
|
|
{
|
|
value = clamp(value, 0, 100);
|
|
image = bar;
|
|
//width and height are supposed to be the end result, Width and Height are the input image. If that makes sense.
|
|
int width = Width = bar->GetWidth();
|
|
int height = Height = bar->GetHeight();
|
|
if(horizontal)
|
|
{
|
|
width = (int) (((double) width/100)*value);
|
|
}
|
|
Pixels = new BYTE[Width*Height];
|
|
memset(Pixels, 0, Width*Height); //Prevent garbage when using transparent images
|
|
bar->CopyToBlock(Pixels, Width, Height, 0, 0); //draw the bar
|
|
int run = bar->GetHeight() - (int) (((double) height/100)*value);
|
|
int visible = bar->GetHeight() - run;
|
|
if(bg == NULL || bg->GetWidth() != bar->GetWidth() || bg->GetHeight() != bar->GetHeight())
|
|
{
|
|
BYTE color0 = GPalette.Remap[0];
|
|
if(!horizontal)
|
|
{
|
|
if(!reverse) //remove offset if we are not reversing the direction.
|
|
{
|
|
visible = 0;
|
|
}
|
|
for(int i = 0;i < Width;i++)
|
|
{
|
|
memset(Pixels + i*Height + visible, color0, run);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(int i = reverse ? 0 : width;(reverse && i < Width - width) || (!reverse && i < Width);i++)
|
|
{
|
|
memset(Pixels + i*Height, color0, Height);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BYTE* PixelData = (BYTE*) bg->GetPixels();
|
|
if(!horizontal)
|
|
{
|
|
if(!reverse)
|
|
{
|
|
visible = 0;
|
|
}
|
|
for(int i = 0;i < width;i++)
|
|
{
|
|
memcpy(Pixels + i*Height + visible, PixelData + i*Height, run);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for(int i = reverse ? 0 : width;(reverse && i < Width - width) || (!reverse && i < Width);i++)
|
|
{
|
|
memcpy(Pixels + i*Height, PixelData + i*Height, Height);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
protected:
|
|
BYTE* Pixels;
|
|
FTexture* image;
|
|
};
|
|
|
|
//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");
|
|
bar = NULL;
|
|
}
|
|
|
|
SBarInfoCommand::~SBarInfoCommand()
|
|
{
|
|
if (bar != NULL)
|
|
{
|
|
delete bar;
|
|
}
|
|
subBlock.commands.Clear();
|
|
}
|
|
|
|
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) break; //don't draw anything.
|
|
bool horizontal = !!((cmd.special2 & DRAWBAR_HORIZONTAL));
|
|
bool reverse = !!((cmd.special2 & DRAWBAR_REVERSE));
|
|
int value = 0;
|
|
int max = 0;
|
|
if(cmd.flags == DRAWNUMBER_HEALTH)
|
|
{
|
|
value = health;
|
|
if(cmd.value < 0) //health shouldn't display negatives
|
|
{
|
|
value = 0;
|
|
}
|
|
if(!(cmd.special2 & DRAWBAR_COMPAREDEFAULTS))
|
|
{
|
|
AInventory* item = CPlayer->mo->FindInventory(PClass::FindClass(cmd.string[0])); //max comparer
|
|
if(item != NULL)
|
|
{
|
|
max = item->Amount;
|
|
}
|
|
else
|
|
{
|
|
max = 0;
|
|
}
|
|
}
|
|
else //default to the classes health
|
|
{
|
|
max = CPlayer->mo->GetDefault()->health;
|
|
}
|
|
}
|
|
else if(cmd.flags == DRAWNUMBER_ARMOR)
|
|
{
|
|
AInventory *armor = CPlayer->mo->FindInventory<ABasicArmor>();
|
|
value = armor != NULL ? armor->Amount : 0;
|
|
if(!((cmd.special2 & DRAWBAR_COMPAREDEFAULTS) == DRAWBAR_COMPAREDEFAULTS))
|
|
{
|
|
AInventory* item = CPlayer->mo->FindInventory(PClass::FindClass(cmd.string[0])); //max comparer
|
|
if(item != NULL)
|
|
{
|
|
max = item->Amount;
|
|
}
|
|
else
|
|
{
|
|
max = 0;
|
|
}
|
|
}
|
|
else //default to 100
|
|
{
|
|
max = 100;
|
|
}
|
|
}
|
|
else if(cmd.flags == DRAWNUMBER_AMMO1)
|
|
{
|
|
value = ammocount1;
|
|
if(ammo1 == NULL) //no ammo, 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*100)/max;
|
|
if(value > 100)
|
|
value = 100;
|
|
}
|
|
else
|
|
{
|
|
value = 0;
|
|
}
|
|
if(cmd.bar != NULL)
|
|
delete cmd.bar;
|
|
if (Images[cmd.sprite] != NULL)
|
|
{
|
|
if(cmd.special != -1)
|
|
cmd.bar = new FBarTexture(Images[cmd.sprite], Images[cmd.special], value, horizontal, reverse);
|
|
else
|
|
cmd.bar = new FBarTexture(Images[cmd.sprite], NULL, value, horizontal, reverse);
|
|
DrawImage(cmd.bar, cmd.x, cmd.y);
|
|
}
|
|
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_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;
|
|
}
|
|
}
|
|
}
|
|
|
|
//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)*(100/accuracy);level++);
|
|
int face = faceIndex + level*8;
|
|
DrawImage(Faces[face], x, y);
|
|
}
|
|
}
|
|
else //dead
|
|
{
|
|
if(!xdth || CPlayer->mo->health > -CPlayer->mo->GetDefault()->health)
|
|
{
|
|
DrawImage(Faces[ST_FACEDEAD], x, y);
|
|
}
|
|
else
|
|
{
|
|
if(faceIndex != ST_FACEXDEAD+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;
|
|
}
|