#include "doomtype.h" #include "doomstat.h" #include "v_font.h" #include "v_video.h" #include "sbar.h" #include "r_defs.h" #include "w_wad.h" #include "m_random.h" #include "d_player.h" #include "st_stuff.h" #include "r_local.h" #include "m_swap.h" #include "a_keys.h" #include "templates.h" #include "i_system.h" #include "sbarinfo.h" #include "sc_man.h" #include "gi.h" #include "r_translate.h" static FRandom pr_chainwiggle; //use the same method of chain wiggling as heretic. #define ST_FACETIME (TICRATE/2) #define ST_PAINTIME (TICRATE) #define ST_GRINTIME (TICRATE*2) #define ST_RAMPAGETIME (TICRATE*2) #define ST_XDTHTIME (TICRATE*(3/2)) #define ST_NUMFACES 82 //9 levels with 8 faces each, 3 god, 1 death, 6 xdeath #define ARTIFLASH_OFFSET (invBarOffset+6) EXTERN_CVAR(Int, fraglimit) SBarInfo *SBarInfoScript; enum //statusbar flags { STATUSBARFLAG_FORCESCALED = 1, }; enum //gametype flags { GAMETYPE_SINGLEPLAYER = 1, GAMETYPE_COOPERATIVE = 2, GAMETYPE_DEATHMATCH = 4, GAMETYPE_TEAMGAME = 8, }; enum //drawimage flags { DRAWIMAGE_PLAYERICON = 1, DRAWIMAGE_AMMO1 = 2, DRAWIMAGE_AMMO2 = 4, DRAWIMAGE_INVENTORYICON = 8, DRAWIMAGE_TRANSLATABLE = 16, DRAWIMAGE_WEAPONSLOT = 32, DRAWIMAGE_SWITCHABLE_AND = 64, DRAWIMAGE_INVULNERABILITY = 128, DRAWIMAGE_OFFSET_CENTER = 256, DRAWIMAGE_ARMOR = 512, DRAWIMAGE_WEAPONICON = 1024, }; enum //drawnumber flags { DRAWNUMBER_HEALTH = 1, DRAWNUMBER_ARMOR = 2, DRAWNUMBER_AMMO1 = 4, DRAWNUMBER_AMMO2 = 8, DRAWNUMBER_AMMO = 16, DRAWNUMBER_AMMOCAPACITY = 32, DRAWNUMBER_FRAGS = 64, DRAWNUMBER_INVENTORY = 128, DRAWNUMBER_KILLS = 256, DRAWNUMBER_MONSTERS = 512, DRAWNUMBER_ITEMS = 1024, DRAWNUMBER_TOTALITEMS = 2048, DRAWNUMBER_SECRETS = 4096, DRAWNUMBER_TOTALSECRETS = 8192, }; enum //drawbar flags (will go into special2) { DRAWBAR_HORIZONTAL = 1, DRAWBAR_REVERSE = 2, DRAWBAR_COMPAREDEFAULTS = 4, }; enum //drawselectedinventory flags { DRAWSELECTEDINVENTORY_ALTERNATEONEMPTY = 1, DRAWSELECTEDINVENTORY_ARTIFLASH = 2, DRAWSELECTEDINVENTORY_ALWAYSSHOWCOUNTER = 4, }; enum //drawinventorybar flags { DRAWINVENTORYBAR_ALWAYSSHOW = 1, DRAWINVENTORYBAR_NOARTIBOX = 2, DRAWINVENTORYBAR_NOARROWS = 4, DRAWINVENTORYBAR_ALWAYSSHOWCOUNTER = 8, }; enum //drawgem flags { DRAWGEM_WIGGLE = 1, DRAWGEM_TRANSLATABLE = 2, }; enum //drawshader flags { DRAWSHADER_VERTICAL = 1, DRAWSHADER_REVERSE = 2, }; enum //drawmugshot flags { DRAWMUGSHOT_XDEATHFACE = 1, DRAWMUGSHOT_ANIMATEDGODMODE = 2, }; enum //drawkeybar flags { DRAWKEYBAR_VERTICAL = 1, }; enum //event flags { SBARINFOEVENT_NOT = 1, SBARINFOEVENT_OR = 2, SBARINFOEVENT_AND = 4, }; static const char *SBarInfoTopLevel[] = { "base", "height", "interpolatehealth", "statusbar", NULL }; enum { SBARINFO_BASE, SBARINFO_HEIGHT, SBARINFO_INTERPOLATEHEALTH, SBARINFO_STATUSBAR, }; static const char *StatusBars[] = { "none", "fullscreen", "normal", "automap", "inventory", "inventoryfullscreen", NULL }; enum { STBAR_NONE, STBAR_FULLSCREEN, STBAR_NORMAL, STBAR_AUTOMAP, STBAR_INVENTORY, STBAR_INVENTORYFULLSCREEN, }; static const char *SBarInfoRoutineLevel[] = { "drawimage", "drawnumber", "drawswitchableimage", "drawmugshot", "drawselectedinventory", "drawinventorybar", "drawbar", "drawgem", "drawshader", "drawstring", "drawkeybar", "gamemode", "playerclass", "weaponammo", //event NULL }; enum { SBARINFO_DRAWIMAGE, SBARINFO_DRAWNUMBER, SBARINFO_DRAWSWITCHABLEIMAGE, SBARINFO_DRAWMUGSHOT, SBARINFO_DRAWSELECTEDINVENTORY, SBARINFO_DRAWINVENTORYBAR, SBARINFO_DRAWBAR, SBARINFO_DRAWGEM, SBARINFO_DRAWSHADER, SBARINFO_DRAWSTRING, SBARINFO_DRAWKEYBAR, SBARINFO_GAMEMODE, SBARINFO_PLAYERCLASS, SBARINFO_WEAPONAMMO, }; void FreeSBarInfoScript() { if (SBarInfoScript != NULL) { delete SBarInfoScript; SBarInfoScript = NULL; } } //Laz Bar Script Reader void SBarInfo::ParseSBarInfo(int lump) { FScanner sc(lump, Wads.GetLumpFullName(lump)); gameType = GAME_Any; sc.SetCMode(true); while(sc.CheckToken(TK_Identifier) || sc.CheckToken(TK_Include)) { if(sc.TokenType == TK_Include) { sc.MustGetToken(TK_StringConst); int lump = Wads.CheckNumForFullName(sc.String); //zip/pk3 //Do a normal wad lookup. if (lump == -1 && sc.StringLen <= 8 && !strchr(sc.String, '/')) lump = Wads.CheckNumForName(sc.String); if (lump == -1) sc.ScriptError("Lump '%s' not found", sc.String); ParseSBarInfo(lump); continue; } switch(sc.MustMatchString(SBarInfoTopLevel)) { case SBARINFO_BASE: sc.MustGetToken(TK_Identifier); if(sc.Compare("Doom")) gameType = GAME_Doom; else if(sc.Compare("Heretic")) gameType = GAME_Heretic; else if(sc.Compare("Hexen")) gameType = GAME_Hexen; else if(sc.Compare("Strife")) gameType = GAME_Strife; else if(sc.Compare("None")) gameType = GAME_Any; else sc.ScriptError("Bad game name: %s", sc.String); sc.MustGetToken(';'); break; case SBARINFO_HEIGHT: sc.MustGetToken(TK_IntConst); this->height = sc.Number; sc.MustGetToken(';'); break; case SBARINFO_INTERPOLATEHEALTH: //mimics heretics interpolated health values. if(sc.CheckToken(TK_True)) { interpolateHealth = true; } else { sc.TokenMustBe(TK_False); interpolateHealth = false; } if(sc.CheckToken(',')) //speed param { sc.MustGetToken(TK_IntConst); this->interpolationSpeed = sc.Number; } sc.MustGetToken(';'); break; case SBARINFO_STATUSBAR: { sc.MustGetToken(TK_Identifier); int barNum = sc.MustMatchString(StatusBars); while(sc.CheckToken(',')) { sc.MustGetToken(TK_Identifier); if(sc.Compare("forcescaled")) { this->huds[barNum].forceScaled = true; } else { sc.ScriptError("Unkown flag '%s'.", sc.String); } } sc.MustGetToken('{'); if(barNum == STBAR_AUTOMAP) { automapbar = true; } ParseSBarInfoBlock(sc, this->huds[barNum]); break; } } } } void SBarInfo::ParseSBarInfoBlock(FScanner &sc, SBarInfoBlock &block) { while(sc.CheckToken(TK_Identifier)) { SBarInfoCommand cmd; switch(cmd.type = sc.MustMatchString(SBarInfoRoutineLevel)) { case SBARINFO_DRAWSWITCHABLEIMAGE: sc.MustGetToken(TK_Identifier); if(sc.Compare("weaponslot")) { cmd.flags = DRAWIMAGE_WEAPONSLOT; sc.MustGetToken(TK_IntConst); cmd.value = sc.Number; } else if(sc.Compare("invulnerable")) { cmd.flags = DRAWIMAGE_INVULNERABILITY; } else { cmd.setString(sc, sc.String, 0); const PClass* item = PClass::FindClass(sc.String); if(item == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(item)) //must be a kind of Inventory { sc.ScriptError("'%s' is not a type of inventory item.", sc.String); } } if(sc.CheckToken(TK_AndAnd)) { cmd.flags += DRAWIMAGE_SWITCHABLE_AND; sc.MustGetToken(TK_Identifier); cmd.setString(sc, sc.String, 1); const PClass* item = PClass::FindClass(sc.String); if(item == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(item)) //must be a kind of Inventory { sc.ScriptError("'%s' is not a type of inventory item.", sc.String); } sc.MustGetToken(','); sc.MustGetToken(TK_StringConst); cmd.special = newImage(sc.String); sc.MustGetToken(','); sc.MustGetToken(TK_StringConst); cmd.special2 = newImage(sc.String); sc.MustGetToken(','); sc.MustGetToken(TK_StringConst); cmd.special3 = newImage(sc.String); sc.MustGetToken(','); } else { sc.MustGetToken(','); sc.MustGetToken(TK_StringConst); cmd.special = newImage(sc.String); sc.MustGetToken(','); } case SBARINFO_DRAWIMAGE: { bool getImage = true; if(sc.CheckToken(TK_Identifier)) { getImage = false; if(sc.Compare("playericon")) cmd.flags += DRAWIMAGE_PLAYERICON; else if(sc.Compare("ammoicon1")) cmd.flags += DRAWIMAGE_AMMO1; else if(sc.Compare("ammoicon2")) cmd.flags += DRAWIMAGE_AMMO2; else if(sc.Compare("armoricon")) cmd.flags += DRAWIMAGE_ARMOR; else if(sc.Compare("weaponicon")) cmd.flags += DRAWIMAGE_WEAPONICON; else if(sc.Compare("translatable")) { cmd.flags += DRAWIMAGE_TRANSLATABLE; getImage = true; } else { cmd.flags += DRAWIMAGE_INVENTORYICON; const PClass* item = PClass::FindClass(sc.String); if(item == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(item)) //must be a kind of Inventory { sc.ScriptError("'%s' is not a type of inventory item.", sc.String); } cmd.sprite = ((AInventory *)GetDefaultByType(item))->Icon; } } if(getImage) { sc.MustGetToken(TK_StringConst); cmd.sprite = newImage(sc.String); } sc.MustGetToken(','); this->getCoordinates(sc, cmd); if(sc.CheckToken(',')) { sc.MustGetToken(TK_Identifier); if(sc.Compare("center")) cmd.flags += DRAWIMAGE_OFFSET_CENTER; else sc.ScriptError("Expected 'center' got '%s' instead.", sc.String); } sc.MustGetToken(';'); break; } case SBARINFO_DRAWNUMBER: cmd.special4 = cmd.special3 = -1; sc.MustGetToken(TK_IntConst); cmd.special = sc.Number; sc.MustGetToken(','); sc.MustGetToken(TK_Identifier); cmd.font = V_GetFont(sc.String); if(cmd.font == NULL) sc.ScriptError("Unknown font '%s'.", sc.String); sc.MustGetToken(','); sc.MustGetToken(TK_Identifier); cmd.translation = this->GetTranslation(sc, sc.String); sc.MustGetToken(','); if(sc.CheckToken(TK_IntConst)) { cmd.value = sc.Number; sc.MustGetToken(','); } else { sc.MustGetToken(TK_Identifier); if(sc.Compare("health")) cmd.flags = DRAWNUMBER_HEALTH; else if(sc.Compare("armor")) cmd.flags = DRAWNUMBER_ARMOR; else if(sc.Compare("ammo1")) cmd.flags = DRAWNUMBER_AMMO1; else if(sc.Compare("ammo2")) cmd.flags = DRAWNUMBER_AMMO2; else if(sc.Compare("ammo")) //request the next string to be an ammo type { sc.MustGetToken(TK_Identifier); cmd.setString(sc, sc.String, 0); cmd.flags = DRAWNUMBER_AMMO; const PClass* ammo = PClass::FindClass(sc.String); if(ammo == NULL || !RUNTIME_CLASS(AAmmo)->IsAncestorOf(ammo)) //must be a kind of ammo { sc.ScriptError("'%s' is not a type of ammo.", sc.String); } } else if(sc.Compare("ammocapacity")) { sc.MustGetToken(TK_Identifier); cmd.setString(sc, sc.String, 0); cmd.flags = DRAWNUMBER_AMMOCAPACITY; const PClass* ammo = PClass::FindClass(sc.String); if(ammo == NULL || !RUNTIME_CLASS(AAmmo)->IsAncestorOf(ammo)) //must be a kind of ammo { sc.ScriptError("'%s' is not a type of ammo.", sc.String); } } else if(sc.Compare("frags")) cmd.flags = DRAWNUMBER_FRAGS; else if(sc.Compare("kills")) cmd.flags += DRAWNUMBER_KILLS; else if(sc.Compare("monsters")) cmd.flags += DRAWNUMBER_MONSTERS; else if(sc.Compare("items")) cmd.flags += DRAWNUMBER_ITEMS; else if(sc.Compare("totalitems")) cmd.flags += DRAWNUMBER_TOTALITEMS; else if(sc.Compare("secrets")) cmd.flags += DRAWNUMBER_SECRETS; else if(sc.Compare("totalsecrets")) cmd.flags += DRAWNUMBER_TOTALSECRETS; else { cmd.flags = DRAWNUMBER_INVENTORY; sc.MustGetToken(TK_Identifier); cmd.setString(sc, sc.String, 0); const PClass* item = PClass::FindClass(sc.String); if(item == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(item)) //must be a kind of ammo { sc.ScriptError("'%s' is not a type of inventory item.", sc.String); } } sc.MustGetToken(','); } this->getCoordinates(sc, cmd); if(sc.CheckToken(',')) { bool needsComma = false; if(sc.CheckToken(TK_IntConst)) //font spacing { cmd.special2 = sc.Number; needsComma = true; } if(!needsComma || sc.CheckToken(',')) //2nd coloring for "low-on" value { sc.MustGetToken(TK_Identifier); cmd.translation2 = this->GetTranslation(sc, sc.String); sc.MustGetToken(','); sc.MustGetToken(TK_IntConst); cmd.special3 = sc.Number; if(sc.CheckToken(',')) //3rd coloring for "high-on" value { sc.MustGetToken(TK_Identifier); cmd.translation3 = this->GetTranslation(sc, sc.String); sc.MustGetToken(','); sc.MustGetToken(TK_IntConst); cmd.special4 = sc.Number; } } } sc.MustGetToken(';'); break; case SBARINFO_DRAWMUGSHOT: sc.MustGetToken(TK_StringConst); cmd.setString(sc, sc.String, 0, 3, true); sc.MustGetToken(','); sc.MustGetToken(TK_IntConst); //accuracy if(sc.Number < 1 || sc.Number > 9) sc.ScriptError("Expected a number between 1 and 9, got %d instead.", sc.Number); cmd.special = sc.Number; sc.MustGetToken(','); while(sc.CheckToken(TK_Identifier)) { if(sc.Compare("xdeathface")) cmd.flags += DRAWMUGSHOT_XDEATHFACE; else if(sc.Compare("animatedgodmode")) cmd.flags += DRAWMUGSHOT_ANIMATEDGODMODE; else sc.ScriptError("Unknown flag '%s'.", sc.String); sc.MustGetToken(','); } this->getCoordinates(sc, cmd); sc.MustGetToken(';'); break; case SBARINFO_DRAWSELECTEDINVENTORY: { bool alternateonempty = false; while(true) //go until we get a font (non-flag) { sc.MustGetToken(TK_Identifier); if(sc.Compare("alternateonempty")) { alternateonempty = true; cmd.flags += DRAWSELECTEDINVENTORY_ALTERNATEONEMPTY; } else if(sc.Compare("artiflash")) { cmd.flags += DRAWSELECTEDINVENTORY_ARTIFLASH; } else if(sc.Compare("alwaysshowcounter")) { cmd.flags += DRAWSELECTEDINVENTORY_ALWAYSSHOWCOUNTER; } else { cmd.font = V_GetFont(sc.String); if(cmd.font == NULL) sc.ScriptError("Unknown font '%s'.", sc.String); sc.MustGetToken(','); break; } sc.MustGetToken(','); } sc.MustGetToken(TK_IntConst); cmd.x = sc.Number; sc.MustGetToken(','); sc.MustGetToken(TK_IntConst); cmd.y = sc.Number - (200 - this->height); cmd.special2 = cmd.x + 30; cmd.special3 = cmd.y + 24; cmd.translation = CR_GOLD; if(sc.CheckToken(',')) //more font information { sc.MustGetToken(TK_IntConst); cmd.special2 = sc.Number; sc.MustGetToken(','); sc.MustGetToken(TK_IntConst); cmd.special3 = sc.Number - (200 - this->height); if(sc.CheckToken(',')) { sc.MustGetToken(TK_Identifier); cmd.translation = this->GetTranslation(sc, sc.String); if(sc.CheckToken(',')) { sc.MustGetToken(TK_IntConst); cmd.special4 = sc.Number; } } } if(alternateonempty) { sc.MustGetToken('{'); this->ParseSBarInfoBlock(sc, cmd.subBlock); } else { sc.MustGetToken(';'); } break; } case SBARINFO_DRAWINVENTORYBAR: sc.MustGetToken(TK_Identifier); if(sc.Compare("Heretic")) { cmd.special = GAME_Heretic; } if(sc.Compare("Doom") || sc.Compare("Heretic")) { sc.MustGetToken(','); while(sc.CheckToken(TK_Identifier)) { if(sc.Compare("alwaysshow")) { cmd.flags += DRAWINVENTORYBAR_ALWAYSSHOW; } else if(sc.Compare("noartibox")) { cmd.flags += DRAWINVENTORYBAR_NOARTIBOX; } else if(sc.Compare("noarrows")) { cmd.flags += DRAWINVENTORYBAR_NOARROWS; } else if(sc.Compare("alwaysshowcounter")) { cmd.flags += DRAWINVENTORYBAR_ALWAYSSHOWCOUNTER; } else { sc.ScriptError("Unknown flag '%s'.", sc.String); } sc.MustGetToken(','); } sc.MustGetToken(TK_IntConst); cmd.value = sc.Number; sc.MustGetToken(','); sc.MustGetToken(TK_Identifier); cmd.font = V_GetFont(sc.String); if(cmd.font == NULL) sc.ScriptError("Unknown font '%s'.", sc.String); } else { sc.ScriptError("Unkown style '%s'.", sc.String); } sc.MustGetToken(','); this->getCoordinates(sc, cmd); cmd.special2 = cmd.x + 26; cmd.special3 = cmd.y + 22; cmd.translation = CR_GOLD; if(sc.CheckToken(',')) //more font information { sc.MustGetToken(TK_IntConst); cmd.special2 = sc.Number; sc.MustGetToken(','); sc.MustGetToken(TK_IntConst); cmd.special3 = sc.Number - (200 - this->height); if(sc.CheckToken(',')) { sc.MustGetToken(TK_Identifier); cmd.translation = this->GetTranslation(sc, sc.String); if(sc.CheckToken(',')) { sc.MustGetToken(TK_IntConst); cmd.special4 = sc.Number; } } } sc.MustGetToken(';'); break; case SBARINFO_DRAWBAR: sc.MustGetToken(TK_StringConst); cmd.sprite = newImage(sc.String); sc.MustGetToken(','); sc.MustGetToken(TK_StringConst); cmd.special = newImage(sc.String); sc.MustGetToken(','); sc.MustGetToken(TK_Identifier); //yeah, this is the same as drawnumber, there might be a better way to copy it... if(sc.Compare("health")) { cmd.flags = DRAWNUMBER_HEALTH; if(sc.CheckToken(TK_Identifier)) //comparing reference { cmd.setString(sc, sc.String, 0); const PClass* item = PClass::FindClass(sc.String); if(item == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(item)) //must be a kind of inventory { sc.ScriptError("'%s' is not a type of inventory item.", sc.String); } } else cmd.special2 = DRAWBAR_COMPAREDEFAULTS; } else if(sc.Compare("armor")) { cmd.flags = DRAWNUMBER_ARMOR; if(sc.CheckToken(TK_Identifier)) { cmd.setString(sc, sc.String, 0); const PClass* item = PClass::FindClass(sc.String); if(item == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(item)) //must be a kind of inventory { sc.ScriptError("'%s' is not a type of inventory item.", sc.String); } } else cmd.special2 = DRAWBAR_COMPAREDEFAULTS; } else if(sc.Compare("ammo1")) cmd.flags = DRAWNUMBER_AMMO1; else if(sc.Compare("ammo2")) cmd.flags = DRAWNUMBER_AMMO2; else if(sc.Compare("ammo")) //request the next string to be an ammo type { sc.MustGetToken(TK_Identifier); cmd.setString(sc, sc.String, 0); cmd.flags = DRAWNUMBER_AMMO; const PClass* ammo = PClass::FindClass(sc.String); if(ammo == NULL || !RUNTIME_CLASS(AAmmo)->IsAncestorOf(ammo)) //must be a kind of ammo { sc.ScriptError("'%s' is not a type of ammo.", sc.String); } } else if(sc.Compare("frags")) cmd.flags = DRAWNUMBER_FRAGS; else if(sc.Compare("kills")) cmd.flags = DRAWNUMBER_KILLS; else if(sc.Compare("items")) cmd.flags = DRAWNUMBER_ITEMS; else if(sc.Compare("secrets")) cmd.flags = DRAWNUMBER_SECRETS; else { cmd.flags = DRAWNUMBER_INVENTORY; cmd.setString(sc, sc.String, 0); const PClass* item = PClass::FindClass(sc.String); if(item == NULL || !RUNTIME_CLASS(AInventory)->IsAncestorOf(item)) //must be a kind of ammo { sc.ScriptError("'%s' is not a type of inventory item.", sc.String); } } sc.MustGetToken(','); sc.MustGetToken(TK_Identifier); if(sc.Compare("horizontal")) cmd.special2 += DRAWBAR_HORIZONTAL; else if(!sc.Compare("vertical")) sc.ScriptError("Unknown direction '%s'.", sc.String); sc.MustGetToken(','); if(sc.CheckToken(TK_Identifier)) { if(!sc.Compare("reverse")) { sc.ScriptError("Exspected 'reverse', got '%s' instead.", sc.String); } cmd.special2 += DRAWBAR_REVERSE; sc.MustGetToken(','); } this->getCoordinates(sc, cmd); if(sc.CheckToken(',')) //border { sc.MustGetToken(TK_IntConst); cmd.special3 = sc.Number; } sc.MustGetToken(';'); break; case SBARINFO_DRAWGEM: while(sc.CheckToken(TK_Identifier)) { if(sc.Compare("wiggle")) cmd.flags += DRAWGEM_WIGGLE; else if(sc.Compare("translatable")) cmd.flags += DRAWGEM_TRANSLATABLE; else sc.ScriptError("Unknown drawgem flag '%s'.", sc.String); sc.MustGetToken(','); } sc.MustGetToken(TK_StringConst); //chain cmd.special = newImage(sc.String); sc.MustGetToken(','); sc.MustGetToken(TK_StringConst); //gem cmd.sprite = newImage(sc.String); sc.MustGetToken(','); cmd.special2 = this->getSignedInteger(sc); sc.MustGetToken(','); cmd.special3 = this->getSignedInteger(sc); sc.MustGetToken(','); sc.MustGetToken(TK_IntConst); if(sc.Number < 0) sc.ScriptError("Chain size must be a positive number."); cmd.special4 = sc.Number; sc.MustGetToken(','); this->getCoordinates(sc, cmd); sc.MustGetToken(';'); break; case SBARINFO_DRAWSHADER: sc.MustGetToken(TK_IntConst); cmd.special = sc.Number; if(sc.Number < 1) sc.ScriptError("Width must be greater than 1."); sc.MustGetToken(','); sc.MustGetToken(TK_IntConst); cmd.special2 = sc.Number; if(sc.Number < 1) sc.ScriptError("Height must be greater than 1."); sc.MustGetToken(','); sc.MustGetToken(TK_Identifier); if(sc.Compare("vertical")) cmd.flags += DRAWSHADER_VERTICAL; else if(!sc.Compare("horizontal")) sc.ScriptError("Unknown direction '%s'.", sc.String); sc.MustGetToken(','); if(sc.CheckToken(TK_Identifier)) { if(!sc.Compare("reverse")) { sc.ScriptError("Exspected 'reverse', got '%s' instead.", sc.String); } cmd.flags += DRAWSHADER_REVERSE; sc.MustGetToken(','); } this->getCoordinates(sc, cmd); sc.MustGetToken(';'); break; case SBARINFO_DRAWSTRING: sc.MustGetToken(TK_Identifier); cmd.font = V_GetFont(sc.String); if(cmd.font == NULL) sc.ScriptError("Unknown font '%s'.", sc.String); sc.MustGetToken(','); sc.MustGetToken(TK_Identifier); cmd.translation = this->GetTranslation(sc, sc.String); sc.MustGetToken(','); sc.MustGetToken(TK_StringConst); cmd.setString(sc, sc.String, 0, -1, false); sc.MustGetToken(','); this->getCoordinates(sc, cmd); sc.MustGetToken(';'); break; case SBARINFO_DRAWKEYBAR: sc.MustGetToken(TK_IntConst); cmd.value = sc.Number; sc.MustGetToken(','); sc.MustGetToken(TK_Identifier); if(sc.Compare("vertical")) cmd.flags += DRAWKEYBAR_VERTICAL; else if(!sc.Compare("horizontal")) sc.ScriptError("Unknown direction '%s'.", sc.String); sc.MustGetToken(','); sc.MustGetToken(TK_IntConst); cmd.special = sc.Number; sc.MustGetToken(','); this->getCoordinates(sc, cmd); sc.MustGetToken(';'); break; case SBARINFO_GAMEMODE: while(sc.CheckToken(TK_Identifier)) { if(sc.Compare("singleplayer")) cmd.flags += GAMETYPE_SINGLEPLAYER; else if(sc.Compare("cooperative")) cmd.flags += GAMETYPE_COOPERATIVE; else if(sc.Compare("deathmatch")) cmd.flags += GAMETYPE_DEATHMATCH; else if(sc.Compare("teamgame")) cmd.flags += GAMETYPE_TEAMGAME; else sc.ScriptError("Unknown gamemode: %s", sc.String); if(sc.CheckToken('{')) break; sc.MustGetToken(','); } this->ParseSBarInfoBlock(sc, cmd.subBlock); break; case SBARINFO_PLAYERCLASS: cmd.special = cmd.special2 = cmd.special3 = -1; for(int i = 0;i < 3 && sc.CheckToken(TK_Identifier);i++) //up to 3 classes { bool foundClass = false; for(unsigned int c = 0;c < PlayerClasses.Size();c++) { if(stricmp(sc.String, PlayerClasses[c].Type->Meta.GetMetaString(APMETA_DisplayName)) == 0) { foundClass = true; if(i == 0) cmd.special = PlayerClasses[c].Type->ClassIndex; else if(i == 1) cmd.special2 = PlayerClasses[c].Type->ClassIndex; else //should be 2 cmd.special3 = PlayerClasses[c].Type->ClassIndex; break; } } if(!foundClass) sc.ScriptError("Unkown PlayerClass '%s'.", sc.String); if(sc.CheckToken('{') || i == 2) goto FinishPlayerClass; sc.MustGetToken(','); } FinishPlayerClass: this->ParseSBarInfoBlock(sc, cmd.subBlock); break; case SBARINFO_WEAPONAMMO: sc.MustGetToken(TK_Identifier); if(sc.Compare("not")) { cmd.flags += SBARINFOEVENT_NOT; sc.MustGetToken(TK_Identifier); } for(int i = 0;i < 2;i++) { cmd.setString(sc, sc.String, i); const PClass* ammo = PClass::FindClass(sc.String); if(ammo == NULL || !RUNTIME_CLASS(AAmmo)->IsAncestorOf(ammo)) //must be a kind of ammo { sc.ScriptError("'%s' is not a type of ammo.", sc.String); } if(sc.CheckToken(TK_OrOr)) { cmd.flags += SBARINFOEVENT_OR; sc.MustGetToken(TK_Identifier); } else if(sc.CheckToken(TK_AndAnd)) { cmd.flags += SBARINFOEVENT_AND; sc.MustGetToken(TK_Identifier); } else break; } sc.MustGetToken('{'); this->ParseSBarInfoBlock(sc, cmd.subBlock); break; } block.commands.Push(cmd); } sc.MustGetToken('}'); } void SBarInfo::getCoordinates(FScanner &sc, SBarInfoCommand &cmd) { bool negative = false; negative = sc.CheckToken('-'); sc.MustGetToken(TK_IntConst); cmd.x = negative ? -sc.Number : sc.Number; sc.MustGetToken(','); negative = sc.CheckToken('-'); sc.MustGetToken(TK_IntConst); cmd.y = (negative ? -sc.Number : sc.Number) - (200 - this->height); } int SBarInfo::getSignedInteger(FScanner &sc) { if(sc.CheckToken('-')) { sc.MustGetToken(TK_IntConst); return -sc.Number; } else { sc.MustGetToken(TK_IntConst); return sc.Number; } } int SBarInfo::newImage(const char* patchname) { if(stricmp(patchname, "nullimage") == 0) { return -1; } // if(strlen(patchname) > 8) // { // sc.ScriptError("Graphic names can not be greater then 8 characters long."); // } for(unsigned int i = 0;i < this->Images.Size();i++) //did we already load it? { if(stricmp(this->Images[i], patchname) == 0) { return i; } } return this->Images.Push(patchname); } //converts a string into a tranlation. EColorRange SBarInfo::GetTranslation(FScanner &sc, char* translation) { EColorRange returnVal = CR_UNTRANSLATED; FString namedTranslation; //we must send in "[translation]" const BYTE *trans_ptr; namedTranslation.Format("[%s]", translation); trans_ptr = (const BYTE *)(&namedTranslation[0]); if((returnVal = V_ParseFontColor(trans_ptr, CR_UNTRANSLATED, CR_UNTRANSLATED)) == CR_UNDEFINED) { sc.ScriptError("Missing definition for color %s.", translation); } return returnVal; } SBarInfo::SBarInfo() //make results more predicable { Init(); } SBarInfo::SBarInfo(int lumpnum) { Init(); ParseSBarInfo(lumpnum); } void SBarInfo::Init() { automapbar = false; interpolateHealth = false; interpolationSpeed = 8; height = 0; } SBarInfo::~SBarInfo() { for (size_t i = 0; i < countof(huds); ++i) { huds[i].commands.Clear(); } } void SBarInfoCommand::setString(FScanner &sc, const char* source, int strnum, int maxlength, bool exact) { if(!exact) { if(maxlength != -1 && strlen(source) > (unsigned int) maxlength) { sc.ScriptError("%s is greater than %d characters.", source, maxlength); return; } } else { if(maxlength != -1 && strlen(source) != (unsigned int) maxlength) { sc.ScriptError("%s must be %d characters.", source, maxlength); return; } } string[strnum] = source; } enum { ST_FACENORMALRIGHT, ST_FACENORMAL, ST_FACENORMALLEFT, ST_FACEPAINRIGHT, ST_FACEPAINLEFT, ST_FACEOUCH, ST_FACEGRIN, ST_FACEPAIN, ST_FACEGOD = 72, ST_FACEGODRIGHT = 73, //switch the roles of 0 and 1 because 0 is taken by Doom. ST_FACEGODLEFT = 74, ST_FACEDEAD = 75, ST_FACEXDEAD = 76, }; enum { imgARTIBOX, imgSELECTBOX, imgINVLFGEM1, imgINVLFGEM2, imgINVRTGEM1, imgINVRTGEM2, }; //Used for shading class FBarShader : public FTexture { public: FBarShader(bool vertical, bool reverse) //make an alpha map { int i; Width = vertical ? 2 : 256; Height = vertical ? 256 : 2; CalcBitSize(); // Fill the column/row with shading values. // Vertical shaders have have minimum alpha at the top // and maximum alpha at the bottom, unless flipped by // setting reverse to true. Horizontal shaders are just // the opposite. if (vertical) { if (!reverse) { for (i = 0; i < 256; ++i) { Pixels[i] = i; Pixels[256+i] = i; } } else { for (i = 0; i < 256; ++i) { Pixels[i] = 255 - i; Pixels[256+i] = 255 -i; } } } else { if (!reverse) { for (i = 0; i < 256; ++i) { Pixels[i*2] = 255 - i; Pixels[i*2+1] = 255 - i; } } else { for (i = 0; i < 256; ++i) { Pixels[i*2] = i; Pixels[i*2+1] = i; } } } DummySpan[0].TopOffset = 0; DummySpan[0].Length = vertical ? 256 : 1; DummySpan[1].TopOffset = 0; DummySpan[1].Length = 0; } const BYTE *GetColumn(unsigned int column, const Span **spans_out) { if (spans_out != NULL) { *spans_out = DummySpan; } return Pixels + (column & WidthMask) * 256; } const BYTE *GetPixels() { return Pixels; } void Unload() { } private: BYTE Pixels[512]; Span DummySpan[2]; }; SBarInfoCommand::SBarInfoCommand() //sets the default values for more predicable behavior { type = 0; special = 0; special2 = 0; special3 = 0; special4 = 0; flags = 0; x = 0; y = 0; value = 0; sprite = 0; translation = CR_UNTRANSLATED; translation2 = CR_UNTRANSLATED; translation3 = CR_UNTRANSLATED; font = V_GetFont("CONFONT"); } SBarInfoCommand::~SBarInfoCommand() { } SBarInfoBlock::SBarInfoBlock() { forceScaled = false; } //SBarInfo Display class FSBarInfo : public FBaseStatusBar { public: FSBarInfo () : FBaseStatusBar (SBarInfoScript->height), shader_horz_normal(false, false), shader_horz_reverse(false, true), shader_vert_normal(true, false), shader_vert_reverse(true, true) { static const char *InventoryBarLumps[] = { "ARTIBOX", "SELECTBO", "INVGEML1", "INVGEML2", "INVGEMR1", "INVGEMR2", "USEARTIA", "USEARTIB", "USEARTIC", "USEARTID", }; TArray 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(); 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(); cmd.value = armor != NULL ? armor->Amount : 0; } else if(cmd.flags == DRAWNUMBER_AMMO1) { cmd.value = ammocount1; if(ammo1 == NULL) //no ammo, do not draw { continue; } } else if(cmd.flags == DRAWNUMBER_AMMO2) { cmd.value = ammocount2; if(ammo2 == NULL) //no ammo, do not draw { continue; } } else if(cmd.flags == DRAWNUMBER_AMMO) { const PClass* ammo = PClass::FindClass(cmd.string[0]); AInventory* item = CPlayer->mo->FindInventory(ammo); if(item != NULL) { cmd.value = item->Amount; } else { cmd.value = 0; } } else if(cmd.flags == DRAWNUMBER_AMMOCAPACITY) { const PClass* ammo = PClass::FindClass(cmd.string[0]); AInventory* item = CPlayer->mo->FindInventory(ammo); if(item != NULL) { cmd.value = item->MaxAmount; } else { cmd.value = ((AInventory *)GetDefaultByType(ammo))->MaxAmount; } } else if(cmd.flags == DRAWNUMBER_FRAGS) cmd.value = CPlayer->fragcount; else if(cmd.flags == DRAWNUMBER_KILLS) cmd.value = level.killed_monsters; else if(cmd.flags == DRAWNUMBER_MONSTERS) cmd.value = level.total_monsters; else if(cmd.flags == DRAWNUMBER_ITEMS) cmd.value = level.found_items; else if(cmd.flags == DRAWNUMBER_TOTALITEMS) cmd.value = level.total_items; else if(cmd.flags == DRAWNUMBER_SECRETS) cmd.value = level.found_secrets; else if(cmd.flags == DRAWNUMBER_TOTALSECRETS) cmd.value = level.total_secrets; else if(cmd.flags == DRAWNUMBER_INVENTORY) { AInventory* item = CPlayer->mo->FindInventory(PClass::FindClass(cmd.string[0])); if(item != NULL) { cmd.value = item->Amount; } else { cmd.value = 0; } } if(cmd.special3 != -1 && cmd.value <= cmd.special3) //low DrawNumber(cmd.value, cmd.special, cmd.x, cmd.y, cmd.translation2, cmd.special2); else if(cmd.special4 != -1 && cmd.value >= cmd.special4) //high DrawNumber(cmd.value, cmd.special, cmd.x, cmd.y, cmd.translation3, cmd.special2); else DrawNumber(cmd.value, cmd.special, cmd.x, cmd.y, cmd.translation, cmd.special2); break; case SBARINFO_DRAWMUGSHOT: { bool xdth = false; bool animatedgodmode = false; if(cmd.flags & DRAWMUGSHOT_XDEATHFACE) xdth = true; if(cmd.flags & DRAWMUGSHOT_ANIMATEDGODMODE) animatedgodmode = true; SetFace(oldSkin, cmd.string[0].GetChars()); DrawFace(cmd.special, xdth, animatedgodmode, cmd.x, cmd.y); break; } case SBARINFO_DRAWSELECTEDINVENTORY: if(CPlayer->mo->InvSel != NULL && !(level.flags & LEVEL_NOINVENTORYBAR)) { if((cmd.flags & DRAWSELECTEDINVENTORY_ARTIFLASH) && artiflash) { DrawDimImage(Images[ARTIFLASH_OFFSET+(4-artiflash)], cmd.x, cmd.y, CPlayer->mo->InvSel->Amount <= 0); } else { DrawDimImage(TexMan(CPlayer->mo->InvSel->Icon), cmd.x, cmd.y, CPlayer->mo->InvSel->Amount <= 0); } if((cmd.flags & DRAWSELECTEDINVENTORY_ALWAYSSHOWCOUNTER) || CPlayer->mo->InvSel->Amount != 1) { if(drawingFont != cmd.font) { drawingFont = cmd.font; } DrawNumber(CPlayer->mo->InvSel->Amount, 3, cmd.special2, cmd.special3, cmd.translation, cmd.special4); } } else if((cmd.flags & DRAWSELECTEDINVENTORY_ALTERNATEONEMPTY)) { doCommands(cmd.subBlock); } break; case SBARINFO_DRAWINVENTORYBAR: { bool alwaysshow = false; bool artibox = true; bool noarrows = false; bool alwaysshowcounter = false; if((cmd.flags & DRAWINVENTORYBAR_ALWAYSSHOW)) alwaysshow = true; if((cmd.flags & DRAWINVENTORYBAR_NOARTIBOX)) artibox = false; if((cmd.flags & DRAWINVENTORYBAR_NOARROWS)) noarrows = true; if((cmd.flags & DRAWINVENTORYBAR_ALWAYSSHOWCOUNTER)) alwaysshowcounter = true; if(drawingFont != cmd.font) { drawingFont = cmd.font; } DrawInventoryBar(cmd.special, cmd.value, cmd.x, cmd.y, alwaysshow, cmd.special2, cmd.special3, cmd.translation, artibox, noarrows, alwaysshowcounter); break; } case SBARINFO_DRAWBAR: { if(cmd.sprite == -1 || Images[cmd.sprite] == NULL) break; //don't draw anything. bool horizontal = !!((cmd.special2 & DRAWBAR_HORIZONTAL)); bool reverse = !!((cmd.special2 & DRAWBAR_REVERSE)); fixed_t value = 0; int max = 0; if(cmd.flags == DRAWNUMBER_HEALTH) { value = health; if(value < 0) //health shouldn't display negatives { value = 0; } if(!(cmd.special2 & DRAWBAR_COMPAREDEFAULTS)) { AInventory* item = CPlayer->mo->FindInventory(PClass::FindClass(cmd.string[0])); //max comparer if(item != NULL) { max = item->Amount; } else { max = 0; } } else //default to the class's health { max = CPlayer->mo->GetDefault()->health; } } else if(cmd.flags == DRAWNUMBER_ARMOR) { AInventory *armor = CPlayer->mo->FindInventory(); value = armor != NULL ? armor->Amount : 0; if(!((cmd.special2 & DRAWBAR_COMPAREDEFAULTS) == DRAWBAR_COMPAREDEFAULTS)) { AInventory* item = CPlayer->mo->FindInventory(PClass::FindClass(cmd.string[0])); //max comparer if(item != NULL) { max = item->Amount; } else { max = 0; } } else //default to 100 { max = 100; } } else if(cmd.flags == DRAWNUMBER_AMMO1) { value = ammocount1; if(ammo1 == NULL) //no ammo, draw as empty { value = 0; max = 1; } else max = ammo1->MaxAmount; } else if(cmd.flags == DRAWNUMBER_AMMO2) { value = ammocount2; if(ammo2 == NULL) //no ammo, draw as empty { value = 0; max = 1; } else max = ammo2->MaxAmount; } else if(cmd.flags == DRAWNUMBER_AMMO) { const PClass* ammo = PClass::FindClass(cmd.string[0]); AInventory* item = CPlayer->mo->FindInventory(ammo); if(item != NULL) { value = item->Amount; max = item->MaxAmount; } else { value = 0; } } else if(cmd.flags == DRAWNUMBER_FRAGS) { value = CPlayer->fragcount; max = fraglimit; } else if(cmd.flags == DRAWNUMBER_KILLS) { value = level.killed_monsters; max = level.total_monsters; } else if(cmd.flags == DRAWNUMBER_ITEMS) { value = level.found_items; max = level.total_items; } else if(cmd.flags == DRAWNUMBER_SECRETS) { value = level.found_secrets; max = level.total_secrets; } else if(cmd.flags == DRAWNUMBER_INVENTORY) { AInventory* item = CPlayer->mo->FindInventory(PClass::FindClass(cmd.string[0])); if(item != NULL) { value = item->Amount; max = item->MaxAmount; } else { value = 0; } } if(max != 0 && value > 0) { value = (value << FRACBITS) / max; if(value > FRACUNIT) value = FRACUNIT; } else { value = 0; } assert(Images[cmd.sprite] != NULL); FTexture *fg = Images[cmd.sprite]; FTexture *bg = (cmd.special != -1) ? Images[cmd.special] : NULL; int x, y, w, h; int cx, cy, cw, ch, cr, cb; // Calc real screen coordinates for bar x = cmd.x + ST_X; y = cmd.y + ST_Y; w = fg->GetWidth(); h = fg->GetHeight(); if (Scaled) { screen->VirtualToRealCoordsInt(x, y, w, h, 320, 200, true); } // Draw background if (bg != NULL && bg->GetWidth() == fg->GetWidth() && bg->GetHeight() == fg->GetHeight()) { screen->DrawTexture(bg, x, y, DTA_DestWidth, w, DTA_DestHeight, h, TAG_DONE); } else { screen->Clear(x, y, x + w, y + h, GPalette.BlackIndex, 0); } // Calc clipping rect for foreground cx = cmd.x + ST_X + cmd.special3; cy = cmd.y + ST_Y + cmd.special3; cw = fg->GetWidth() - cmd.special3 * 2; ch = fg->GetHeight() - cmd.special3 * 2; if (Scaled) { screen->VirtualToRealCoordsInt(cx, cy, cw, ch, 320, 200, true); } if (horizontal) { if (!reverse) { // left to right cr = cx + FixedMul(cw, value); } else { // right to left cr = cx + cw; cx += FixedMul(cw, FRACUNIT - value); } cb = cy + ch; } else { if (!reverse) { // bottom to top cb = cy + ch; cy += FixedMul(ch, FRACUNIT - value); } else { // top to bottom cb = cy + FixedMul(ch, value); } cr = cx + cw; } // Draw foreground screen->DrawTexture(fg, x, y, DTA_DestWidth, w, DTA_DestHeight, h, DTA_ClipLeft, cx, DTA_ClipTop, cy, DTA_ClipRight, cr, DTA_ClipBottom, cb, TAG_DONE); break; } case SBARINFO_DRAWGEM: { int value = health; int max = 100; bool wiggle = false; bool translate = !!(cmd.flags & DRAWGEM_TRANSLATABLE); if(max != 0 || value < 0) { value = (value*100)/max; if(value > 100) value = 100; } else { value = 0; } if(health != CPlayer->health) { wiggle = !!(cmd.flags & DRAWGEM_WIGGLE); } DrawGem(Images[cmd.special], Images[cmd.sprite], value, cmd.x, cmd.y, cmd.special2, cmd.special3, cmd.special4+1, wiggle, translate); break; } case SBARINFO_DRAWSHADER: { FBarShader *const shaders[4] = { &shader_horz_normal, &shader_horz_reverse, &shader_vert_normal, &shader_vert_reverse }; bool vertical = !!(cmd.flags & DRAWSHADER_VERTICAL); bool reverse = !!(cmd.flags & DRAWSHADER_REVERSE); screen->DrawTexture (shaders[(vertical << 1) + reverse], ST_X+cmd.x, ST_Y+cmd.y, DTA_DestWidth, cmd.special, DTA_DestHeight, cmd.special2, DTA_Bottom320x200, Scaled, DTA_AlphaChannel, true, DTA_FillColor, 0, TAG_DONE); break; } case SBARINFO_DRAWSTRING: if(drawingFont != cmd.font) { drawingFont = cmd.font; } DrawString(cmd.string[0], cmd.x - drawingFont->StringWidth(cmd.string[0]), cmd.y, cmd.translation); break; case SBARINFO_DRAWKEYBAR: { bool vertical = !!(cmd.flags & DRAWKEYBAR_VERTICAL); AInventory *item = CPlayer->mo->Inventory; if(item == NULL) break; for(int i = 0;i < cmd.value;i++) { while(item->Icon <= 0 || item->GetClass() == RUNTIME_CLASS(AKey) || !item->IsKindOf(RUNTIME_CLASS(AKey))) { item = item->Inventory; if(item == NULL) goto FinishDrawKeyBar; } if(!vertical) DrawImage(TexMan[item->Icon], cmd.x+(cmd.special*i), cmd.y); else DrawImage(TexMan[item->Icon], cmd.x, cmd.y+(cmd.special*i)); item = item->Inventory; if(item == NULL) break; } FinishDrawKeyBar: break; } case SBARINFO_GAMEMODE: if(((cmd.flags & GAMETYPE_SINGLEPLAYER) && !multiplayer) || ((cmd.flags & GAMETYPE_DEATHMATCH) && deathmatch) || ((cmd.flags & GAMETYPE_COOPERATIVE) && multiplayer && !deathmatch) || ((cmd.flags & GAMETYPE_TEAMGAME) && teamplay)) { doCommands(cmd.subBlock); } break; case SBARINFO_PLAYERCLASS: { int spawnClass = CPlayer->cls->ClassIndex; if(cmd.special == spawnClass || cmd.special2 == spawnClass || cmd.special3 == spawnClass) { doCommands(cmd.subBlock); } break; } case SBARINFO_WEAPONAMMO: if(CPlayer->ReadyWeapon != NULL) { const PClass *AmmoType1 = CPlayer->ReadyWeapon->AmmoType1; const PClass *AmmoType2 = CPlayer->ReadyWeapon->AmmoType2; const PClass *IfAmmo1 = PClass::FindClass(cmd.string[0]); const PClass *IfAmmo2 = (cmd.flags & SBARINFOEVENT_OR) || (cmd.flags & SBARINFOEVENT_AND) ? PClass::FindClass(cmd.string[1]) : NULL; bool usesammo1 = (AmmoType1 != NULL); bool usesammo2 = (AmmoType2 != NULL); if(!(cmd.flags & SBARINFOEVENT_NOT) && !usesammo1 && !usesammo2) //if the weapon doesn't use ammo don't go though the trouble. { doCommands(cmd.subBlock); break; } //Or means only 1 ammo type needs to match and means both need to match. if((cmd.flags & SBARINFOEVENT_OR) || (cmd.flags & SBARINFOEVENT_AND)) { bool match1 = ((usesammo1 && (AmmoType1 == IfAmmo1 || AmmoType1 == IfAmmo2)) || !usesammo1); bool match2 = ((usesammo2 && (AmmoType2 == IfAmmo1 || AmmoType2 == IfAmmo2)) || !usesammo2); if(((cmd.flags & SBARINFOEVENT_OR) && (match1 || match2)) || ((cmd.flags & SBARINFOEVENT_AND) && (match1 && match2))) { if(!(cmd.flags & SBARINFOEVENT_NOT)) doCommands(cmd.subBlock); } else if(cmd.flags & SBARINFOEVENT_NOT) { doCommands(cmd.subBlock); } } else //Every thing here could probably be one long if statement but then it would be more confusing. { if((usesammo1 && (AmmoType1 == IfAmmo1)) || (usesammo2 && (AmmoType2 == IfAmmo1))) { if(!(cmd.flags & SBARINFOEVENT_NOT)) doCommands(cmd.subBlock); } else if(cmd.flags & SBARINFOEVENT_NOT) { doCommands(cmd.subBlock); } } } break; } } } //draws an image with the specified flags void DrawGraphic(FTexture* texture, int x, int y, int flags) { if((flags & DRAWIMAGE_OFFSET_CENTER)) { x -= (texture->GetWidth()/2)-texture->LeftOffset; y -= (texture->GetHeight()/2)-texture->TopOffset; } if((flags & DRAWIMAGE_TRANSLATABLE)) DrawImage(texture, x, y, getTranslation()); else DrawImage(texture, x, y); } void DrawString(const char* str, int x, int y, EColorRange translation, int spacing=0) { x += spacing; while(*str != '\0') { if(*str == ' ') { x += drawingFont->GetSpaceWidth(); str++; continue; } int width = drawingFont->GetCharWidth((int) *str); FTexture* character = drawingFont->GetChar((int) *str, &width); if(character == NULL) //missing character. { str++; continue; } x += (character->LeftOffset+1); //ignore x offsets since we adapt to character size DrawImage(character, x, y, drawingFont->GetColorTranslation(translation)); x += width + spacing - (character->LeftOffset+1); str++; } } //draws the specified number up to len digits void DrawNumber(int num, int len, int x, int y, EColorRange translation, int spacing=0) { FString value; int maxval = (int) ceil(pow(10., len))-1; num = clamp(num, -maxval, maxval); value.Format("%d", num); x -= int(drawingFont->StringWidth(value)+(spacing * value.Len())); DrawString(value, x, y, translation, spacing); } //draws the mug shot void DrawFace(int accuracy, bool xdth, bool animatedgodmode, int x, int y) { if(CPlayer->health > 0) { if(faceIndex == ST_FACEGOD || faceIndex == ST_FACEGODLEFT || faceIndex == ST_FACEGODRIGHT) //nothing fancy to do here { if(animatedgodmode) DrawImage(Faces[faceIndex], x, y); else DrawImage(Faces[ST_FACEGOD], x, y); } else { int level = 0; for(level = 0;CPlayer->health < (accuracy-level-1)*(CPlayer->mo->GetMaxHealth()/accuracy);level++); int face = faceIndex + level*8; DrawImage(Faces[face], x, y); } } else //dead { if(!xdth || CPlayer->mo->health > -CPlayer->mo->GetDefault()->health) { DrawImage(Faces[ST_FACEDEAD], x, y); } else { if(faceIndex != ST_FACEXDEAD+5 && faceIndex >= ST_FACEXDEAD) //animate { if(faceTimer == 0) { faceIndex++; faceTimer = ST_XDTHTIME; } faceTimer--; } else if(faceIndex < ST_FACEXDEAD) //set to xdeath { faceIndex = ST_FACEXDEAD; faceTimer = ST_XDTHTIME; } DrawImage(Faces[faceIndex], x, y); } } } //Does some face drawing logic. void getNewFace(int number) { int i; angle_t badguyangle; angle_t diffang; if(CPlayer->health > 0) { if(weaponGrin) { weaponGrin = false; if(CPlayer->bonuscount) { faceTimer = ST_GRINTIME; faceIndex = ST_FACEGRIN; return; } } // getting hurt if (CPlayer->damagecount) { int damageAngle = ST_FACEPAIN; if(CPlayer->attacker && CPlayer->attacker != CPlayer->mo) { if(CPlayer->mo != NULL) { //some more doom_sbar C&P badguyangle = R_PointToAngle2(CPlayer->mo->x, CPlayer->mo->y, CPlayer->attacker->x, CPlayer->attacker->y); if (badguyangle > CPlayer->mo->angle) { // whether right or left diffang = badguyangle - CPlayer->mo->angle; i = diffang > ANG180; } else { // whether left or right diffang = CPlayer->mo->angle - badguyangle; i = diffang <= ANG180; } // confusing, aint it? if(i && diffang >= ANG45) { damageAngle = ST_FACEPAINRIGHT; } else if(!i && diffang >= ANG45) { damageAngle = ST_FACEPAINLEFT; } } } faceTimer = ST_PAINTIME; if (mugshotHealth != -1 && CPlayer->health - mugshotHealth > 20) { faceIndex = ST_FACEOUCH; } else { faceIndex = damageAngle; } return; } if((CPlayer->cmd.ucmd.buttons & (BT_ATTACK|BT_ALTATTACK)) && !(CPlayer->cheats & (CF_FROZEN | CF_TOTALLYFROZEN))) { if(rampageTimer == ST_RAMPAGETIME) { faceIndex = ST_FACEPAIN; faceTimer = 1; } else { rampageTimer++; } } else { rampageTimer = 0; } // invulnerability if (((CPlayer->cheats & CF_GODMODE) || (CPlayer->mo != NULL && CPlayer->mo->flags2 & MF2_INVULNERABLE)) && (faceTimer == 0 || faceIndex <= 2)) { faceIndex = ST_FACEGOD + (number % 3); faceTimer = ST_FACETIME; } if (faceTimer == 0) { faceIndex = (number % 3); //should generate 0, 1, or 2 faceTimer = ST_FACETIME; } faceTimer--; mugshotHealth = CPlayer->health; } } void DrawInventoryBar(int type, int num, int x, int y, bool alwaysshow, int counterx, int countery, EColorRange translation, bool drawArtiboxes, bool noArrows, bool alwaysshowcounter) { //yes, there is some Copy & Paste here too const AInventory *item; int i; // If the player has no artifacts, don't draw the bar CPlayer->mo->InvFirst = ValidateInvFirst(num); if(CPlayer->mo->InvFirst != NULL || alwaysshow) { for(item = CPlayer->mo->InvFirst, i = 0; item != NULL && i < num; item = item->NextInv(), ++i) { if(drawArtiboxes) { DrawImage (Images[invBarOffset + imgARTIBOX], x+i*31, y); } DrawDimImage (TexMan(item->Icon), x+i*31, y, item->Amount <= 0); if(alwaysshowcounter || item->Amount != 1) { DrawNumber(item->Amount, 3, counterx+i*31, countery, translation); } if(item == CPlayer->mo->InvSel) { if(type == GAME_Heretic) { DrawImage(Images[invBarOffset + imgSELECTBOX], x+i*31, y+29); } else { DrawImage(Images[invBarOffset + imgSELECTBOX], x+i*31, y); } } } for (; i < num && drawArtiboxes; ++i) { DrawImage (Images[invBarOffset + imgARTIBOX], x+i*31, y); } // Is there something to the left? if (!noArrows && CPlayer->mo->FirstInv() != CPlayer->mo->InvFirst) { DrawImage (Images[!(gametic & 4) ? invBarOffset + imgINVLFGEM1 : invBarOffset + imgINVLFGEM2], x-12, y); } // Is there something to the right? if (!noArrows && item != NULL) { DrawImage (Images[!(gametic & 4) ? invBarOffset + imgINVRTGEM1 : invBarOffset + imgINVRTGEM2], x+num*31+2, y); } } } //draws heretic/hexen style life gems void DrawGem(FTexture* chain, FTexture* gem, int value, int x, int y, int padleft, int padright, int chainsize, bool wiggle, bool translate) { if(value > 100) value = 100; else if(value < 0) value = 0; if(wiggle) y += chainWiggle; int gemWidth = gem->GetWidth(); int chainWidth = chain->GetWidth(); int offset = (int) (((double) (chainWidth-padleft-padright)/100)*value); if(chain != NULL) { DrawImage(chain, x+(offset%chainsize), y); } if(gem != NULL) DrawImage(gem, x+padleft+offset, y, translate ? getTranslation() : NULL); } FRemapTable* getTranslation() { if(gameinfo.gametype & GAME_Raven) return translationtables[TRANSLATION_PlayersExtra][int(CPlayer - players)]; return translationtables[TRANSLATION_Players][int(CPlayer - players)]; } FImageCollection Images; FImageCollection Faces; FPlayerSkin *oldSkin; FFont *drawingFont; FString lastPrefix; bool weaponGrin; int faceTimer; int rampageTimer; int faceIndex; int oldHealth; int mugshotHealth; int chainWiggle; int artiflash; unsigned int invBarOffset; FBarShader shader_horz_normal; FBarShader shader_horz_reverse; FBarShader shader_vert_normal; FBarShader shader_vert_reverse; }; FBaseStatusBar *CreateCustomStatusBar () { return new FSBarInfo; }