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