qzdoom/src/g_shared/sbarinfo_parser.cpp
Christoph Oelckers 344f1072a6 - Fixed. The Firedemon was missing a game filter.
SBARINfO update:
- Added: disablegrin, disableouch, disablepain, and disablerampage flags to
  drawmugshot.
- Fixed: LowerHealthCap did not work properly.
- Fixed: Various bugs I noticed in the fullscreenoffsets code.

SVN r1122 (trunk)
2008-08-07 07:17:29 +00:00

1492 lines
42 KiB
C++

/*
** sbarinfo_parser.cpp
**
** Reads custom status bar definitions.
**
**---------------------------------------------------------------------------
** Copyright 2008 Braden Obrzut
** All rights reserved.
**
** Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions
** are met:
**
** 1. Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in the
** documentation and/or other materials provided with the distribution.
** 3. The name of the author may not be used to endorse or promote products
** derived from this software without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**---------------------------------------------------------------------------
**
*/
#include "doomtype.h"
#include "doomstat.h"
#include "sc_man.h"
#include "v_font.h"
#include "w_wad.h"
#include "d_player.h"
#include "sbar.h"
#include "sbarinfo.h"
#include "templates.h"
#include "m_random.h"
#include "gi.h"
#include "i_system.h"
SBarInfo *SBarInfoScript;
static const char *SBarInfoTopLevel[] =
{
"base",
"height",
"interpolatehealth",
"interpolatearmor",
"completeborder",
"monospacefonts",
"lowerhealthcap",
"statusbar",
"mugshot",
"createpopup",
NULL
};
static const char *StatusBars[] =
{
"none",
"fullscreen",
"normal",
"automap",
"inventory",
"inventoryfullscreen",
"popuplog",
"popupkeys",
"popupstatus",
NULL
};
static const char *SBarInfoRoutineLevel[] =
{
"drawimage",
"drawnumber",
"drawswitchableimage",
"drawmugshot",
"drawselectedinventory",
"drawinventorybar",
"drawbar",
"drawgem",
"drawshader",
"drawstring",
"drawkeybar",
"gamemode",
"playerclass",
"aspectratio",
"isselected",
"usessecondaryammo",
"hasweaponpiece",
"weaponammo", //event
"ininventory",
NULL
};
static void FreeSBarInfoScript()
{
if (SBarInfoScript != NULL)
{
delete SBarInfoScript;
SBarInfoScript = NULL;
}
}
void SBarInfo::Load()
{
if(Wads.CheckNumForName("SBARINFO") != -1)
{
Printf ("ParseSBarInfo: Loading custom status bar definition.\n");
int lastlump, lump;
lastlump = 0;
while((lump = Wads.FindLump("SBARINFO", &lastlump)) != -1)
{
if(SBarInfoScript == NULL)
SBarInfoScript = new SBarInfo(lump);
else //We now have to load multiple SBarInfo Lumps so the 2nd time we need to use this method instead.
SBarInfoScript->ParseSBarInfo(lump);
}
atterm(FreeSBarInfoScript);
}
}
//SBarInfo Script Reader
void SBarInfo::ParseSBarInfo(int lump)
{
gameType = gameinfo.gametype;
bool baseSet = false;
FScanner sc(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, true);
if (lump == -1)
sc.ScriptError("Lump '%s' not found", sc.String);
ParseSBarInfo(lump);
continue;
}
switch(sc.MustMatchString(SBarInfoTopLevel))
{
case SBARINFO_BASE:
baseSet = true;
if(!sc.CheckToken(TK_None))
sc.MustGetToken(TK_Identifier);
if(sc.Compare("Doom"))
gameType = GAME_Doom;
else if(sc.Compare("Heretic"))
gameType = GAME_Heretic;
else if(sc.Compare("Hexen"))
gameType = GAME_Hexen;
else if(sc.Compare("Strife"))
gameType = GAME_Strife;
else if(sc.Compare("None"))
gameType = GAME_Any;
else
sc.ScriptError("Bad game name: %s", sc.String);
sc.MustGetToken(';');
break;
case SBARINFO_HEIGHT:
sc.MustGetToken(TK_IntConst);
this->height = sc.Number;
sc.MustGetToken(';');
break;
case SBARINFO_INTERPOLATEHEALTH: //mimics heretics interpolated health values.
if(sc.CheckToken(TK_True))
{
interpolateHealth = true;
}
else
{
sc.MustGetToken(TK_False);
interpolateHealth = false;
}
if(sc.CheckToken(',')) //speed param
{
sc.MustGetToken(TK_IntConst);
this->interpolationSpeed = sc.Number;
}
sc.MustGetToken(';');
break;
case SBARINFO_INTERPOLATEARMOR: //Since interpolatehealth is such a popular command
if(sc.CheckToken(TK_True))
{
interpolateArmor = true;
}
else
{
sc.MustGetToken(TK_False);
interpolateArmor = false;
}
if(sc.CheckToken(',')) //speed
{
sc.MustGetToken(TK_IntConst);
this->armorInterpolationSpeed = sc.Number;
}
sc.MustGetToken(';');
break;
case SBARINFO_COMPLETEBORDER: //draws the border instead of an HOM
if(sc.CheckToken(TK_True))
{
completeBorder = true;
}
else
{
sc.MustGetToken(TK_False);
completeBorder = false;
}
sc.MustGetToken(';');
break;
case SBARINFO_MONOSPACEFONTS:
if(sc.CheckToken(TK_True))
{
sc.MustGetToken(',');
sc.MustGetToken(TK_StringConst);
spacingCharacter = sc.String[0];
}
else
{
sc.MustGetToken(TK_False);
spacingCharacter = '\0';
sc.MustGetToken(',');
sc.MustGetToken(TK_StringConst); //Don't tell anyone we're just ignoring this ;)
}
sc.MustGetToken(';');
break;
case SBARINFO_LOWERHEALTHCAP:
if(sc.CheckToken(TK_False))
{
lowerHealthCap = false;
}
else
{
sc.MustGetToken(TK_True);
lowerHealthCap = true;
}
sc.MustGetToken(';');
break;
case SBARINFO_STATUSBAR:
{
if(!baseSet) //If the user didn't explicitly define a base, do so now.
gameType = GAME_Any;
int barNum = 0;
if(!sc.CheckToken(TK_None))
{
sc.MustGetToken(TK_Identifier);
barNum = sc.MustMatchString(StatusBars);
}
this->huds[barNum] = SBarInfoBlock();
if(sc.CheckToken(','))
{
while(sc.CheckToken(TK_Identifier))
{
if(sc.Compare("forcescaled"))
{
this->huds[barNum].forceScaled = true;
}
else if(sc.Compare("fullscreenoffsets"))
{
this->huds[barNum].fullScreenOffsets = true;
}
else
{
sc.ScriptError("Unkown flag '%s'.", sc.String);
}
if(!sc.CheckToken('|') || !sc.CheckToken(','))
goto FinishStatusBar; //No more args so we must skip over anything else and go to the end.
}
sc.MustGetToken(TK_FloatConst);
this->huds[barNum].alpha = fixed_t(FRACUNIT * sc.Float);
}
FinishStatusBar:
sc.MustGetToken('{');
if(barNum == STBAR_AUTOMAP)
{
automapbar = true;
}
ParseSBarInfoBlock(sc, this->huds[barNum]);
break;
}
case SBARINFO_MUGSHOT:
{
sc.MustGetToken(TK_StringConst);
FMugShotState state(sc.String);
if(sc.CheckToken(',')) //first loop must be a comma
{
do
{
sc.MustGetToken(TK_Identifier);
if(sc.Compare("health"))
state.bUsesLevels = true;
else if(sc.Compare("health2"))
state.bUsesLevels = state.bHealth2 = true;
else if(sc.Compare("healthspecial"))
state.bUsesLevels = state.bHealthSpecial = true;
else if(sc.Compare("directional"))
state.bDirectional = true;
else
sc.ScriptError("Unknown MugShot state flag '%s'.", sc.String);
}
while(sc.CheckToken(',') || sc.CheckToken('|'));
}
ParseMugShotBlock(sc, state);
int index;
if((index = FindMugShotStateIndex(state.State)) != -1) //We already had this state, remove the old one.
{
MugShotStates.Delete(index);
}
MugShotStates.Push(state);
break;
}
case SBARINFO_CREATEPOPUP:
{
int pop = 0;
sc.MustGetToken(TK_Identifier);
if(sc.Compare("log"))
pop = 0;
else if(sc.Compare("keys"))
pop = 1;
else if(sc.Compare("status"))
pop = 2;
else
sc.ScriptError("Unkown popup: '%s'", sc.String);
Popup &popup = popups[pop];
sc.MustGetToken(',');
sc.MustGetToken(TK_IntConst);
popup.width = sc.Number;
sc.MustGetToken(',');
sc.MustGetToken(TK_IntConst);
popup.height = sc.Number;
sc.MustGetToken(',');
if(!sc.CheckToken(TK_None))
{
sc.MustGetToken(TK_Identifier);
if(sc.Compare("slideinbottom"))
{
popup.transition = TRANSITION_SLIDEINBOTTOM;
sc.MustGetToken(',');
sc.MustGetToken(TK_IntConst);
popup.speed = sc.Number;
}
else if(sc.Compare("fade"))
{
popup.transition = TRANSITION_FADE;
sc.MustGetToken(',');
sc.MustGetToken(TK_FloatConst);
popup.speed = fixed_t(FRACUNIT * (1.0 / (35.0 * sc.Float)));
sc.MustGetToken(',');
sc.MustGetToken(TK_FloatConst);
popup.speed2 = fixed_t(FRACUNIT * (1.0 / (35.0 * sc.Float)));
}
else
sc.ScriptError("Unkown transition type: '%s'", sc.String);
}
popup.init();
sc.MustGetToken(';');
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 if(sc.Compare("keyslot"))
{
cmd.flags = DRAWIMAGE_KEYSLOT;
sc.MustGetToken(TK_IntConst);
cmd.value = sc.Number;
}
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;
if(cmd.flags & DRAWIMAGE_KEYSLOT)
{
sc.MustGetToken(TK_IntConst);
cmd.special4 = sc.Number;
}
else
{
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("sigil"))
cmd.flags |= DRAWIMAGE_SIGIL;
else if(sc.Compare("translatable"))
{
cmd.flags |= DRAWIMAGE_TRANSLATABLE;
getImage = true;
}
else
{
//sc.CheckToken(TK_Identifier);
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_index = ((AInventory *)GetDefaultByType(item))->Icon;
cmd.image_index = -1;
}
}
if(getImage)
{
sc.MustGetToken(TK_StringConst);
cmd.image_index = newImage(sc.String);
cmd.sprite_index.SetInvalid();
}
sc.MustGetToken(',');
this->getCoordinates(sc, cmd, block.fullScreenOffsets);
if(sc.CheckToken(','))
{
sc.MustGetToken(TK_Identifier);
if(sc.Compare("center"))
cmd.flags |= DRAWIMAGE_OFFSET_CENTER;
else
sc.ScriptError("Expected 'center' got '%s' instead.", sc.String);
}
sc.MustGetToken(';');
break;
}
case SBARINFO_DRAWNUMBER:
cmd.special4 = cmd.special3 = -1;
sc.MustGetToken(TK_IntConst);
cmd.special = sc.Number;
sc.MustGetToken(',');
sc.MustGetToken(TK_Identifier);
cmd.font = V_GetFont(sc.String);
if(cmd.font == NULL)
sc.ScriptError("Unknown font '%s'.", sc.String);
sc.MustGetToken(',');
sc.MustGetToken(TK_Identifier);
cmd.translation = this->GetTranslation(sc, sc.String);
sc.MustGetToken(',');
if(sc.CheckToken(TK_IntConst))
{
cmd.value = sc.Number;
sc.MustGetToken(',');
}
else
{
sc.MustGetToken(TK_Identifier);
if(sc.Compare("health"))
cmd.flags = DRAWNUMBER_HEALTH;
else if(sc.Compare("armor"))
cmd.flags = DRAWNUMBER_ARMOR;
else if(sc.Compare("ammo1"))
cmd.flags = DRAWNUMBER_AMMO1;
else if(sc.Compare("ammo2"))
cmd.flags = DRAWNUMBER_AMMO2;
else if(sc.Compare("ammo")) //request the next string to be an ammo type
{
sc.MustGetToken(TK_Identifier);
cmd.setString(sc, sc.String, 0);
cmd.flags = DRAWNUMBER_AMMO;
const PClass* ammo = PClass::FindClass(sc.String);
if(ammo == NULL || !RUNTIME_CLASS(AAmmo)->IsAncestorOf(ammo)) //must be a kind of ammo
{
sc.ScriptError("'%s' is not a type of ammo.", sc.String);
}
}
else if(sc.Compare("ammocapacity"))
{
sc.MustGetToken(TK_Identifier);
cmd.setString(sc, sc.String, 0);
cmd.flags = DRAWNUMBER_AMMOCAPACITY;
const PClass* ammo = PClass::FindClass(sc.String);
if(ammo == NULL || !RUNTIME_CLASS(AAmmo)->IsAncestorOf(ammo)) //must be a kind of ammo
{
sc.ScriptError("'%s' is not a type of ammo.", sc.String);
}
}
else if(sc.Compare("frags"))
cmd.flags = DRAWNUMBER_FRAGS;
else if(sc.Compare("kills"))
cmd.flags |= DRAWNUMBER_KILLS;
else if(sc.Compare("monsters"))
cmd.flags |= DRAWNUMBER_MONSTERS;
else if(sc.Compare("items"))
cmd.flags |= DRAWNUMBER_ITEMS;
else if(sc.Compare("totalitems"))
cmd.flags |= DRAWNUMBER_TOTALITEMS;
else if(sc.Compare("secrets"))
cmd.flags |= DRAWNUMBER_SECRETS;
else if(sc.Compare("totalsecrets"))
cmd.flags |= DRAWNUMBER_TOTALSECRETS;
else if(sc.Compare("armorclass"))
cmd.flags |= DRAWNUMBER_ARMORCLASS;
else if(sc.Compare("globalvar"))
{
cmd.flags |= DRAWNUMBER_GLOBALVAR;
sc.MustGetToken(TK_IntConst);
if(sc.Number < 0 || sc.Number >= NUM_GLOBALVARS)
sc.ScriptError("Global variable number out of range: %d", sc.Number);
cmd.value = sc.Number;
}
else if(sc.Compare("globalarray")) //acts like variable[playernumber()]
{
cmd.flags |= DRAWNUMBER_GLOBALARRAY;
sc.MustGetToken(TK_IntConst);
if(sc.Number < 0 || sc.Number >= NUM_GLOBALVARS)
sc.ScriptError("Global variable number out of range: %d", sc.Number);
cmd.value = sc.Number;
}
else if(sc.Compare("poweruptime"))
{
cmd.flags |= DRAWNUMBER_POWERUPTIME;
sc.MustGetToken(TK_Identifier);
cmd.setString(sc, sc.String, 0);
const PClass* item = PClass::FindClass(sc.String);
if(item == NULL || !PClass::FindClass("PowerupGiver")->IsAncestorOf(item))
{
sc.ScriptError("'%s' is not a type of PowerupGiver.", sc.String);
}
}
else
{
cmd.flags = DRAWNUMBER_INVENTORY;
cmd.setString(sc, sc.String, 0);
const PClass* item = PClass::FindClass(sc.String);
if(item == NULL || !PClass::FindClass("Inventory")->IsAncestorOf(item)) //must be a kind of ammo
{
sc.ScriptError("'%s' is not a type of inventory item.", sc.String);
}
}
sc.MustGetToken(',');
}
while(sc.CheckToken(TK_Identifier))
{
if(sc.Compare("fillzeros"))
cmd.flags |= DRAWNUMBER_FILLZEROS;
else if(sc.Compare("whennotzero"))
cmd.flags |= DRAWNUMBER_WHENNOTZERO;
else
sc.ScriptError("Unknown flag '%s'.", sc.String);
if(!sc.CheckToken('|'))
sc.MustGetToken(',');
}
this->getCoordinates(sc, cmd, block.fullScreenOffsets);
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:
if(sc.CheckToken(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 if(sc.Compare("disablegrin"))
cmd.flags |= DRAWMUGSHOT_DISABLEGRIN;
else if(sc.Compare("disableouch"))
cmd.flags |= DRAWMUGSHOT_DISABLEOUCH;
else if(sc.Compare("disablepain"))
cmd.flags |= DRAWMUGSHOT_DISABLEPAIN;
else if(sc.Compare("disablerampage"))
cmd.flags |= DRAWMUGSHOT_DISABLERAMPAGE;
else
sc.ScriptError("Unknown flag '%s'.", sc.String);
if(!sc.CheckToken('|'))
sc.MustGetToken(',');
}
this->getCoordinates(sc, cmd, block.fullScreenOffsets);
sc.MustGetToken(';');
break;
case SBARINFO_DRAWSELECTEDINVENTORY:
{
bool alternateonempty = false;
while(true) //go until we get a font (non-flag)
{
sc.MustGetToken(TK_Identifier);
if(sc.Compare("alternateonempty"))
{
alternateonempty = true;
cmd.flags |= DRAWSELECTEDINVENTORY_ALTERNATEONEMPTY;
}
else if(sc.Compare("artiflash"))
{
cmd.flags |= DRAWSELECTEDINVENTORY_ARTIFLASH;
}
else if(sc.Compare("alwaysshowcounter"))
{
cmd.flags |= DRAWSELECTEDINVENTORY_ALWAYSSHOWCOUNTER;
}
else
{
cmd.font = V_GetFont(sc.String);
if(cmd.font == NULL)
sc.ScriptError("Unknown font '%s'.", sc.String);
sc.MustGetToken(',');
break;
}
if(!sc.CheckToken('|'))
sc.MustGetToken(',');
}
sc.MustGetToken(TK_IntConst);
cmd.x = sc.Number;
sc.MustGetToken(',');
sc.MustGetToken(TK_IntConst);
cmd.y = sc.Number - (200 - this->height);
cmd.special2 = cmd.x + 30;
cmd.special3 = cmd.y + 24;
cmd.translation = CR_GOLD;
if(sc.CheckToken(',')) //more font information
{
sc.MustGetToken(TK_IntConst);
cmd.special2 = sc.Number;
sc.MustGetToken(',');
sc.MustGetToken(TK_IntConst);
cmd.special3 = sc.Number - (200 - this->height);
if(sc.CheckToken(','))
{
sc.MustGetToken(TK_Identifier);
cmd.translation = this->GetTranslation(sc, sc.String);
if(sc.CheckToken(','))
{
sc.MustGetToken(TK_IntConst);
cmd.special4 = sc.Number;
}
}
}
if(alternateonempty)
{
sc.MustGetToken('{');
cmd.subBlock.fullScreenOffsets = block.fullScreenOffsets;
this->ParseSBarInfoBlock(sc, cmd.subBlock);
}
else
{
sc.MustGetToken(';');
}
break;
}
case SBARINFO_DRAWINVENTORYBAR:
sc.MustGetToken(TK_Identifier);
if(sc.Compare("Doom"))
cmd.special = GAME_Doom;
else if(sc.Compare("Heretic"))
cmd.special = GAME_Heretic;
else if(sc.Compare("Hexen"))
cmd.special = GAME_Hexen;
else if(sc.Compare("Strife"))
cmd.special = GAME_Strife;
else
sc.ScriptError("Unkown style '%s'.", sc.String);
sc.MustGetToken(',');
while(sc.CheckToken(TK_Identifier))
{
if(sc.Compare("alwaysshow"))
{
cmd.flags |= DRAWINVENTORYBAR_ALWAYSSHOW;
}
else if(sc.Compare("noartibox"))
{
cmd.flags |= DRAWINVENTORYBAR_NOARTIBOX;
}
else if(sc.Compare("noarrows"))
{
cmd.flags |= DRAWINVENTORYBAR_NOARROWS;
}
else if(sc.Compare("alwaysshowcounter"))
{
cmd.flags |= DRAWINVENTORYBAR_ALWAYSSHOWCOUNTER;
}
else
{
sc.ScriptError("Unknown flag '%s'.", sc.String);
}
if(!sc.CheckToken('|'))
sc.MustGetToken(',');
}
sc.MustGetToken(TK_IntConst);
cmd.value = sc.Number;
sc.MustGetToken(',');
sc.MustGetToken(TK_Identifier);
cmd.font = V_GetFont(sc.String);
if(cmd.font == NULL)
sc.ScriptError("Unknown font '%s'.", sc.String);
sc.MustGetToken(',');
this->getCoordinates(sc, cmd, block.fullScreenOffsets);
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.image_index = newImage(sc.String);
cmd.sprite_index.SetInvalid();
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 if(sc.Compare("poweruptime"))
{
cmd.flags |= DRAWNUMBER_POWERUPTIME;
sc.MustGetToken(TK_Identifier);
cmd.setString(sc, sc.String, 0);
const PClass* item = PClass::FindClass(sc.String);
if(item == NULL || !PClass::FindClass("PowerupGiver")->IsAncestorOf(item))
{
sc.ScriptError("'%s' is not a type of PowerupGiver.", sc.String);
}
}
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))
{
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(',');
while(sc.CheckToken(TK_Identifier))
{
if(sc.Compare("reverse"))
cmd.special2 += DRAWBAR_REVERSE;
else
sc.ScriptError("Unkown flag '%s'.", sc.String);
if(!sc.CheckToken('|'))
sc.MustGetToken(',');
}
this->getCoordinates(sc, cmd, block.fullScreenOffsets);
if(sc.CheckToken(',')) //border
{
sc.MustGetToken(TK_IntConst);
cmd.special3 = sc.Number;
}
sc.MustGetToken(';');
break;
case SBARINFO_DRAWGEM:
while(sc.CheckToken(TK_Identifier))
{
if(sc.Compare("wiggle"))
cmd.flags |= DRAWGEM_WIGGLE;
else if(sc.Compare("translatable"))
cmd.flags |= DRAWGEM_TRANSLATABLE;
else if(sc.Compare("armor"))
cmd.flags |= DRAWGEM_ARMOR;
else if(sc.Compare("reverse"))
cmd.flags |= DRAWGEM_REVERSE;
else
sc.ScriptError("Unknown drawgem flag '%s'.", sc.String);
if(!sc.CheckToken('|'))
sc.MustGetToken(',');
}
sc.MustGetToken(TK_StringConst); //chain
cmd.special = newImage(sc.String);
sc.MustGetToken(',');
sc.MustGetToken(TK_StringConst); //gem
cmd.image_index = newImage(sc.String);
cmd.sprite_index.SetInvalid();
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, block.fullScreenOffsets);
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, block.fullScreenOffsets);
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, block.fullScreenOffsets);
if(sc.CheckToken(',')) //spacing
{
sc.MustGetToken(TK_IntConst);
cmd.special = sc.Number;
}
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, block.fullScreenOffsets);
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 I'm removing this error to allow cross port compatiblity. If it doesn't know what a gamemode is lets just ignore it.
// sc.ScriptError("Unknown gamemode: %s", sc.String);
if(sc.CheckToken('{'))
break;
sc.MustGetToken(',');
}
cmd.subBlock.fullScreenOffsets = block.fullScreenOffsets;
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:
cmd.subBlock.fullScreenOffsets = block.fullScreenOffsets;
this->ParseSBarInfoBlock(sc, cmd.subBlock);
break;
case SBARINFO_ASPECTRATIO:
sc.MustGetToken(TK_StringConst);
if(sc.Compare("4:3"))
cmd.value = ASPECTRATIO_4_3;
else if(sc.Compare("16:9"))
cmd.value = ASPECTRATIO_16_9;
else if(sc.Compare("16:10"))
cmd.value = ASPECTRATIO_16_10;
else if(sc.Compare("5:4"))
cmd.value = ASPECTRATIO_5_4;
else
sc.ScriptError("Unkown aspect ratio: %s", sc.String);
sc.MustGetToken('{');
cmd.subBlock.fullScreenOffsets = block.fullScreenOffsets;
this->ParseSBarInfoBlock(sc, cmd.subBlock);
break;
case SBARINFO_ISSELECTED:
if(sc.CheckToken(TK_Identifier))
{
if(sc.Compare("not"))
{
cmd.flags |= SBARINFOEVENT_NOT;
}
else
sc.ScriptError("Expected 'not' got '%s' instead.", sc.String);
}
sc.MustGetToken(TK_StringConst);
for(int i = 0;i < 2;i++)
{
cmd.setString(sc, sc.String, i);
const PClass* item = PClass::FindClass(sc.String);
if(item == NULL || !RUNTIME_CLASS(AWeapon)->IsAncestorOf(item))
{
sc.ScriptError("'%s' is not a type of weapon.", sc.String);
}
if(sc.CheckToken(','))
sc.MustGetToken(TK_StringConst);
else
break;
}
sc.MustGetToken('{');
cmd.subBlock.fullScreenOffsets = block.fullScreenOffsets;
this->ParseSBarInfoBlock(sc, cmd.subBlock);
break;
case SBARINFO_USESSECONDARYAMMO:
if(sc.CheckToken(TK_Identifier))
{
if(sc.Compare("not"))
cmd.flags |= SBARINFOEVENT_NOT;
else
sc.ScriptError("Exspected 'not' got '%s' instead.", sc.String);
}
sc.MustGetToken('{');
cmd.subBlock.fullScreenOffsets = block.fullScreenOffsets;
this->ParseSBarInfoBlock(sc, cmd.subBlock);
break;
case SBARINFO_HASWEAPONPIECE:
{
sc.MustGetToken(TK_Identifier);
const PClass* weapon = PClass::FindClass(sc.String);
if(weapon == NULL || !RUNTIME_CLASS(AWeapon)->IsAncestorOf(weapon)) //must be a weapon
sc.ScriptError("%s is not a kind of weapon.", sc.String);
cmd.setString(sc, sc.String, 0);
sc.MustGetToken(',');
sc.MustGetToken(TK_IntConst);
if(sc.Number < 1)
sc.ScriptError("Weapon piece number can not be less than 1.");
cmd.value = sc.Number;
sc.MustGetToken('{');
cmd.subBlock.fullScreenOffsets = block.fullScreenOffsets;
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('{');
cmd.subBlock.fullScreenOffsets = block.fullScreenOffsets;
this->ParseSBarInfoBlock(sc, cmd.subBlock);
break;
case SBARINFO_ININVENTORY:
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* item = PClass::FindClass(sc.String);
if(item == NULL || !RUNTIME_CLASS(AInventory)->IsAncestorOf(item))
{
sc.ScriptError("'%s' is not a type of inventory item.", 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('{');
cmd.subBlock.fullScreenOffsets = block.fullScreenOffsets;
this->ParseSBarInfoBlock(sc, cmd.subBlock);
break;
}
block.commands.Push(cmd);
}
sc.MustGetToken('}');
}
void SBarInfo::ParseMugShotBlock(FScanner &sc, FMugShotState &state)
{
sc.MustGetToken('{');
while(!sc.CheckToken('}'))
{
FMugShotFrame frame;
bool multiframe = false;
if(sc.CheckToken('{'))
multiframe = true;
do
{
sc.MustGetToken(TK_Identifier);
if(strlen(sc.String) > 5)
sc.ScriptError("MugShot frames cannot exceed 5 characters.");
frame.Graphic.Push(sc.String);
}
while(multiframe && sc.CheckToken(','));
if(multiframe)
sc.MustGetToken('}');
frame.Delay = getSignedInteger(sc);
sc.MustGetToken(';');
state.Frames.Push(frame);
}
}
void SBarInfo::getCoordinates(FScanner &sc, SBarInfoCommand &cmd, bool fullScreenOffsets)
{
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);
if(!fullScreenOffsets)
cmd.y = (negative ? -sc.Number : sc.Number) - (200 - this->height);
else
cmd.y = negative ? -sc.Number : sc.Number;
}
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(patchname[0] == '\0' || stricmp(patchname, "nullimage") == 0)
{
return -1;
}
for(unsigned int i = 0;i < this->Images.Size();i++) //did we already load it?
{
if(stricmp(this->Images[i], patchname) == 0)
{
return i;
}
}
return this->Images.Push(patchname);
}
//converts a string into a tranlation.
EColorRange SBarInfo::GetTranslation(FScanner &sc, char* translation)
{
EColorRange returnVal = CR_UNTRANSLATED;
FString namedTranslation; //we must send in "[translation]"
const BYTE *trans_ptr;
namedTranslation.Format("[%s]", translation);
trans_ptr = (const BYTE *)(&namedTranslation[0]);
if((returnVal = V_ParseFontColor(trans_ptr, CR_UNTRANSLATED, CR_UNTRANSLATED)) == CR_UNDEFINED)
{
sc.ScriptError("Missing definition for color %s.", translation);
}
return returnVal;
}
SBarInfo::SBarInfo() //make results more predicable
{
Init();
}
SBarInfo::SBarInfo(int lumpnum)
{
Init();
ParseSBarInfo(lumpnum);
}
void SBarInfo::Init()
{
automapbar = false;
interpolateHealth = false;
interpolateArmor = false;
completeBorder = false;
lowerHealthCap = true;
interpolationSpeed = 8;
armorInterpolationSpeed = 8;
height = 0;
spacingCharacter = '\0';
}
SBarInfo::~SBarInfo()
{
for (size_t i = 0; i < NUMHUDS; ++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;
}
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;
image_index = 0;
sprite_index.SetInvalid();
translation = CR_UNTRANSLATED;
translation2 = CR_UNTRANSLATED;
translation3 = CR_UNTRANSLATED;
font = V_GetFont("CONFONT");
}
SBarInfoCommand::~SBarInfoCommand()
{
}
SBarInfoBlock::SBarInfoBlock()
{
forceScaled = false;
fullScreenOffsets = false;
alpha = FRACUNIT;
}
//Popup
Popup::Popup()
{
transition = TRANSITION_NONE;
height = 320;
width = 200;
speed = 0;
x = 320;
y = 200;
alpha = FRACUNIT;
opened = false;
moving = false;
}
void Popup::init()
{
x = width;
y = height;
if(transition == TRANSITION_SLIDEINBOTTOM)
{
x = 0;
}
else if(transition == TRANSITION_FADE)
{
alpha = 0;
x = 0;
y = 0;
}
}
void Popup::tick()
{
if(transition == TRANSITION_SLIDEINBOTTOM)
{
if(moving)
{
if(opened)
y -= clamp(height + (y - height), 1, speed);
else
y += clamp(height - y, 1, speed);
}
if(y != 0 && y != height)
moving = true;
else
moving = false;
}
else if(transition == TRANSITION_FADE)
{
if(moving)
{
if(opened)
alpha = clamp(alpha + speed, 0, FRACUNIT);
else
alpha = clamp(alpha - speed2, 0, FRACUNIT);
}
if(alpha == 0 || alpha == FRACUNIT)
moving = false;
else
moving = true;
}
else
{
if(opened)
{
y = 0;
x = 0;
}
else
{
y = height;
x = width;
}
moving = false;
}
}
bool Popup::isDoneMoving()
{
return !moving;
}
int Popup::getXOffset()
{
return x;
}
int Popup::getYOffset()
{
return y;
}
int Popup::getAlpha(int maxAlpha)
{
double a = (double) alpha / (double) FRACUNIT;
double b = (double) maxAlpha / (double) FRACUNIT;
return fixed_t((a * b) * FRACUNIT);
}
void Popup::open()
{
opened = true;
moving = true;
}
void Popup::close()
{
opened = false;
moving = true;
}