May 18, 2008 (SBarInfo Update #20)

- Added: hasweaponpiece command to check for custom weapon pieces.
- Added: usessecondaryammo command to check if the current weapon has a second
  ammo type.
- Most of SBarInfo's mugshot scripting can be used with the default Doom status
  bar.
- Fixed: By default drawmugshot would never come out of normal god mode state.
  In addition the state change to and from god mode was not quite as responsive
  as the original code.

SVN r980 (trunk)
This commit is contained in:
Christoph Oelckers 2008-05-18 15:48:03 +00:00
parent b8c21495d5
commit 35ea94c014
10 changed files with 342 additions and 333 deletions

View file

@ -1,3 +1,13 @@
May 18, 2008 (SBarInfo Update #20)
- Added: hasweaponpiece command to check for custom weapon pieces.
- Added: usessecondaryammo command to check if the current weapon has a second
ammo type.
- Most of SBarInfo's mugshot scripting can be used with the default Doom status
bar.
- Fixed: By default drawmugshot would never come out of normal god mode state.
In addition the state change to and from god mode was not quite as responsive
as the original code.
May 17, 2008 (Changes by Graf Zahl)
- Fixed: When FTextureManager::CheckForTexture finds a matching NULL texture
it should always return 0, not the actual texture's index.

View file

@ -14,6 +14,7 @@
#include "templates.h"
#include "i_system.h"
#include "r_translate.h"
#include "sbarinfo.h"
#define ST_EVILGRINCOUNT (2*TICRATE)
@ -26,7 +27,6 @@
EXTERN_CVAR (Bool, vid_fps)
class DDoomStatusBar : public DBaseStatusBar
{
DECLARE_CLASS(DDoomStatusBar, DBaseStatusBar)
@ -51,7 +51,12 @@ public:
BigHeight = tex->GetHeight();
DoCommonInit ();
bEvilGrin = false;
bDamageFaceActive = false;
CurrentState = NULL;
RampageTimer = 0;
LastDamageAngle = 1;
}
~DDoomStatusBar ()
@ -68,63 +73,6 @@ public:
}
}
void SetFace (void *skn)
{
const char *nameptrs[ST_NUMFACES];
char names[ST_NUMFACES][9];
char prefix[4];
int i, j;
int namespc;
int facenum;
FPlayerSkin *skin = (FPlayerSkin *)skn;
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] = 'S';
prefix[1] = 'T';
prefix[2] = 'F';
prefix[3] = 0;
namespc = ns_global;
}
facenum = 0;
for (i = 0; i < ST_NUMPAINFACES; i++)
{
for (j = 0; j < ST_NUMSTRAIGHTFACES; 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);
Faces.Uninit ();
Faces.Init (nameptrs, ST_NUMFACES, namespc);
FaceIndex = 0;
FaceCount = 0;
OldFaceIndex = -1;
}
void AddFaceToImageCollection (void *skn, FImageCollection *images)
{
AddFaceToImageCollectionActual (skn, images, true);
@ -161,7 +109,26 @@ public:
{
DBaseStatusBar::Tick ();
RandomNumber = M_Random ();
UpdateFace ();
//Do some stuff related to the mug shot that has to be done at 35fps
if(CurrentState != NULL)
{
CurrentState->tick();
if(CurrentState->finished)
CurrentState = NULL;
}
if((CPlayer->cmd.ucmd.buttons & (BT_ATTACK|BT_ALTATTACK)) && !(CPlayer->cheats & (CF_FROZEN | CF_TOTALLYFROZEN)))
{
if(RampageTimer != ST_RAMPAGEDELAY)
{
RampageTimer++;
}
}
else
{
RampageTimer = 0;
}
FaceHealth = CPlayer->health;
}
void Draw (EHudState state)
@ -183,7 +150,6 @@ public:
OldKeys = -1;
memset (OldAmmo, 255, sizeof(OldAmmo));
memset (OldMaxAmmo, 255, sizeof(OldMaxAmmo));
OldFaceIndex = -1;
OldHealth = -1;
OldArmor = -1;
OldActiveAmmo = -1;
@ -199,6 +165,20 @@ public:
}
}
//See sbarinfo_display.cpp
void SetMugShotState (const char* stateName, bool waitTillDone=false)
{
MugShotState *state = (MugShotState *) FindMugShotState(stateName);
if(state != CurrentState)
{
if(!waitTillDone || CurrentState == NULL || CurrentState->finished)
{
CurrentState = state;
state->reset();
}
}
}
private:
struct FDoomStatusBarTexture : public FTexture
{
@ -255,8 +235,6 @@ private:
StatusBarTex.Unload ();
SB_state = screen->GetPageCount ();
FaceLastAttackDown = -1;
FacePriority = 0;
}
void DrawMainBar ()
@ -463,38 +441,6 @@ private:
}
}
void DrawFace ()
{
// If a player has an inventory item selected, it takes the place of the
// face, for lack of a better place to put it.
if (OldFaceIndex != FaceIndex)
{
FaceRefresh = screen->GetPageCount ();
OldFaceIndex = FaceIndex;
}
if (FaceRefresh || (CPlayer->mo->InvSel != NULL && !(level.flags & LEVEL_NOINVENTORYBAR)))
{
if (FaceRefresh)
{
FaceRefresh--;
}
DrawPartialImage (&StatusBarTex, 142, 37);
if (CPlayer->mo->InvSel == NULL || (level.flags & LEVEL_NOINVENTORYBAR))
{
DrawImage (Faces[FaceIndex], 143, 0);
}
else
{
DrawDimImage (TexMan(CPlayer->mo->InvSel->Icon), 144, 0, CPlayer->mo->InvSel->Amount <= 0);
if (CPlayer->mo->InvSel->Amount != 1)
{
DrSmallNumber (CPlayer->mo->InvSel->Amount, 165, 24);
}
OldFaceIndex = -1;
}
}
}
void DrawKeys ()
{
AInventory *item;
@ -796,73 +742,34 @@ private:
bEvilGrin = true;
}
//
// This is a not-very-pretty routine which handles the face states
// and their timing. The precedence of expressions is:
// dead > evil grin > turned head > straight ahead
//
void UpdateFace ()
int UpdateState ()
{
int i;
angle_t badguyangle;
angle_t diffang;
if (FacePriority < 10)
if(CPlayer->health > 0)
{
// dead
if (CPlayer->health <= 0)
{
FacePriority = 9;
FaceIndex = ST_DEADFACE;
FaceCount = 1;
}
}
if (FacePriority < 9)
{
if (CPlayer->bonuscount)
{
// picking up bonus
if(bEvilGrin)
{
// evil grin if just picked up weapon
if(CurrentState == NULL)
bEvilGrin = false;
FacePriority = 8;
FaceCount = ST_EVILGRINCOUNT;
FaceIndex = CalcPainOffset() + ST_EVILGRINOFFSET;
}
}
else
else if(CPlayer->bonuscount)
{
// This happens when a weapon is added to the inventory
// by other means than being picked up.
bEvilGrin = false;
SetMugShotState("grin", false);
return 0;
}
}
if (FacePriority < 8)
if (CPlayer->damagecount)
{
if (CPlayer->damagecount
&& CPlayer->attacker
&& CPlayer->attacker != CPlayer->mo)
int damageAngle = 1;
if(CPlayer->attacker && CPlayer->attacker != CPlayer->mo)
{
// being attacked
FacePriority = 7;
if (FaceHealth != -9999 && FaceHealth - CPlayer->health > ST_MUCHPAIN)
if(CPlayer->mo != NULL)
{
FaceCount = ST_TURNCOUNT;
FaceIndex = CalcPainOffset() + ST_OUCHOFFSET;
FacePriority = 8;
}
else if (CPlayer->mo != NULL)
{
badguyangle = R_PointToAngle2(CPlayer->mo->x,
CPlayer->mo->y,
CPlayer->attacker->x,
CPlayer->attacker->y);
//The next 12 lines is from the Doom statusbar code.
badguyangle = R_PointToAngle2(CPlayer->mo->x, CPlayer->mo->y, CPlayer->attacker->x, CPlayer->attacker->y);
if(badguyangle > CPlayer->mo->angle)
{
// whether right or left
@ -875,95 +782,108 @@ private:
diffang = CPlayer->mo->angle - badguyangle;
i = diffang <= ANG180;
} // confusing, aint it?
FaceCount = ST_TURNCOUNT;
FaceIndex = CalcPainOffset();
if (diffang < ANG45)
if(i && diffang >= ANG45)
{
// head-on
FaceIndex += ST_RAMPAGEOFFSET;
damageAngle = 0;
}
else if (i)
else if(!i && diffang >= ANG45)
{
// turn face right
FaceIndex += ST_TURNOFFSET;
damageAngle = 2;
}
}
}
const char* stateName = new char[5];
if (FaceHealth != -1 && CPlayer->health - FaceHealth > 20)
stateName = "ouch";
else
stateName = "pain";
char* fullStateName = new char[sizeof(stateName)+sizeof((const char*) CPlayer->LastDamageType) + 1];
sprintf(fullStateName, "%s.%s", stateName, (const char*) CPlayer->LastDamageType);
if(FindMugShotState(fullStateName) != NULL)
SetMugShotState(fullStateName);
else
SetMugShotState(stateName);
bDamageFaceActive = !(CurrentState == NULL);
LastDamageAngle = damageAngle;
return damageAngle;
}
if(bDamageFaceActive)
{
if(CurrentState == NULL)
bDamageFaceActive = false;
else
{
// turn face left
FaceIndex += ST_TURNOFFSET+1;
}
}
}
}
if (FacePriority < 7)
{
// getting hurt because of your own damn stupidity
if (CPlayer->damagecount)
{
if (OldHealth != -1 && CPlayer->health - OldHealth > ST_MUCHPAIN)
{
FacePriority = 7;
FaceCount = ST_TURNCOUNT;
FaceIndex = CalcPainOffset() + ST_OUCHOFFSET;
}
const char* stateName = new char[5];
if (FaceHealth != -1 && CPlayer->health - FaceHealth > 20)
stateName = "ouch";
else
{
FacePriority = 6;
FaceCount = ST_TURNCOUNT;
FaceIndex = CalcPainOffset() + ST_RAMPAGEOFFSET;
stateName = "pain";
char* fullStateName = new char[sizeof(stateName)+sizeof((const char*) CPlayer->LastDamageType) + 1];
sprintf(fullStateName, "%s.%s", stateName, (const char*) CPlayer->LastDamageType);
if(FindMugShotState(fullStateName) != NULL)
SetMugShotState(fullStateName);
else
SetMugShotState(stateName);
return LastDamageAngle;
}
}
if(RampageTimer == ST_RAMPAGEDELAY)
{
SetMugShotState("rampage", true);
return 0;
}
}
if (FacePriority < 6)
if(!bEvilGrin)
{
// rapid firing
if ((CPlayer->cmd.ucmd.buttons & (BT_ATTACK|BT_ALTATTACK)) && !(CPlayer->cheats & (CF_FROZEN | CF_TOTALLYFROZEN)))
{
if (FaceLastAttackDown == -1)
FaceLastAttackDown = ST_RAMPAGEDELAY;
else if (!--FaceLastAttackDown)
{
FacePriority = 5;
FaceIndex = CalcPainOffset() + ST_RAMPAGEOFFSET;
FaceCount = 1;
FaceLastAttackDown = 1;
if((CPlayer->cheats & CF_GODMODE) || (CPlayer->mo != NULL && CPlayer->mo->flags2 & MF2_INVULNERABLE))
SetMugShotState("god");
else
SetMugShotState("normal");
}
}
else
{
FaceLastAttackDown = -1;
const char* stateName = new char[7];
stateName = "death";
//new string the size of stateName and the damage type put together
char* fullStateName = new char[sizeof(stateName)+sizeof((const char*) CPlayer->LastDamageType) + 1];
sprintf(fullStateName, "%s.%s", stateName, (const char*) CPlayer->LastDamageType);
if(FindMugShotState(fullStateName) != NULL)
SetMugShotState(fullStateName);
else
SetMugShotState(stateName);
}
return 0;
}
if (FacePriority < 5)
void DrawFace ()
{
// invulnerability
if ((CPlayer->cheats & CF_GODMODE)
|| (CPlayer->mo != NULL && CPlayer->mo->flags2 & MF2_INVULNERABLE))
if (CPlayer->mo->InvSel == NULL || (level.flags & LEVEL_NOINVENTORYBAR))
{
FacePriority = 4;
FaceIndex = ST_GODFACE;
FaceCount = 1;
int angle = UpdateState();
int level = 0;
for(level = 0;CPlayer->health < (4-level)*(CPlayer->mo->GetMaxHealth()/5);level++);
if(CurrentState != NULL)
{
FString defaultFace = "STF";
FPlayerSkin *skin = &skins[CPlayer->morphTics ? CPlayer->MorphedPlayerClass : CPlayer->userinfo.skin];
FTexture *face = CurrentState->getCurrentFrameTexture(defaultFace, skin, level, angle);
if (face != NULL)
{
DrawPartialImage (&StatusBarTex, 142, 37);
DrawImage (face, 143, 0);
}
}
// look left or look right if the facecount has timed out
if (!FaceCount)
{
FaceIndex = CalcPainOffset() + (RandomNumber % 3);
FaceCount = ST_STRAIGHTFACECOUNT;
FacePriority = 0;
}
FaceCount--;
FaceHealth = CPlayer->health;
else
{
DrawDimImage (TexMan(CPlayer->mo->InvSel->Icon), 144, 0, CPlayer->mo->InvSel->Amount <= 0);
if (CPlayer->mo->InvSel->Amount != 1)
{
DrSmallNumber (CPlayer->mo->InvSel->Amount, 165, 24);
}
}
}
enum
@ -995,15 +915,11 @@ private:
};
FImageCollection Images;
FImageCollection Faces;
int BigWidth;
int BigHeight;
int FaceIndex;
int FaceCount;
int RandomNumber;
int OldFaceIndex;
BYTE OldArms[6];
int OldKeys;
int OldAmmo[4];
@ -1012,9 +928,6 @@ private:
int OldArmor;
int OldActiveAmmo;
int OldFrags;
int FaceHealth;
int FaceLastAttackDown;
int FacePriority;
char HealthRefresh;
char ArmorRefresh;
@ -1026,7 +939,13 @@ private:
char FaceRefresh;
char KeysRefresh;
//Mugshot
MugShotState *CurrentState;
int RampageTimer;
int LastDamageAngle;
int FaceHealth;
bool bEvilGrin;
bool bDamageFaceActive;
};
IMPLEMENT_CLASS(DDoomStatusBar)

View file

@ -2,26 +2,6 @@
#include "a_weaponpiece.h"
#include "doomstat.h"
// an internal class to hold the information for player class independent weapon piece handling
class AWeaponHolder : public AInventory
{
DECLARE_ACTOR(AWeaponHolder, AInventory)
public:
int PieceMask;
const PClass * PieceWeapon;
void Serialize (FArchive &arc)
{
Super::Serialize(arc);
arc << PieceMask << PieceWeapon;
}
};
IMPLEMENT_STATELESS_ACTOR (AWeaponHolder, Any, -1, 0)
PROP_Flags (MF_NOBLOCKMAP|MF_NOSECTOR)
PROP_Inventory_FlagsSet (IF_UNDROPPABLE)

View file

@ -16,3 +16,20 @@ public:
const PClass * WeaponClass;
TObjPtr<AWeapon> FullWeapon;
};
// an internal class to hold the information for player class independent weapon piece handling
// [BL] Needs to be available for SBarInfo to check weaponpieces
class AWeaponHolder : public AInventory
{
DECLARE_ACTOR(AWeaponHolder, AInventory)
public:
int PieceMask;
const PClass * PieceWeapon;
void Serialize (FArchive &arc)
{
Super::Serialize(arc);
arc << PieceMask << PieceWeapon;
}
};

View file

@ -218,6 +218,7 @@ public:
virtual void ShowPop (int popnum);
virtual void ReceivedWeapon (AWeapon *weapon);
virtual bool MustDrawLog(EHudState state);
virtual void SetMugShotState (const char* stateName, bool waitTillDone=false) {}
void DrawLog();
protected:

View file

@ -346,6 +346,8 @@ enum //Bar key words
SBARINFO_PLAYERCLASS,
SBARINFO_ASPECTRATIO,
SBARINFO_ISSELECTED,
SBARINFO_USESSECONDARYAMMO,
SBARINFO_HASWEAPONPIECE,
SBARINFO_WEAPONAMMO,
SBARINFO_ININVENTORY,
};

View file

@ -52,6 +52,7 @@
#include "gi.h"
#include "r_translate.h"
#include "r_main.h"
#include "a_weaponpiece.h"
static FRandom pr_chainwiggle; //use the same method of chain wiggling as heretic.
@ -1134,6 +1135,33 @@ void DSBarInfo::doCommands(SBarInfoBlock &block, int xOffset, int yOffset, int a
}
}
break;
case SBARINFO_USESSECONDARYAMMO:
if((CPlayer->ReadyWeapon->AmmoType2 != NULL && !(cmd.flags & SBARINFOEVENT_NOT)) ||
(CPlayer->ReadyWeapon->AmmoType2 == NULL && cmd.flags & SBARINFOEVENT_NOT))
{
doCommands(cmd.subBlock, xOffset, yOffset, alpha);
}
break;
case SBARINFO_HASWEAPONPIECE:
{
AInventory *inv;
AWeaponHolder *hold;
const PClass *weapon = PClass::FindClass(cmd.string[0]);
for(inv = CPlayer->mo->Inventory;inv != NULL;inv=inv->Inventory)
{
if(inv->IsKindOf(RUNTIME_CLASS(AWeaponHolder)))
{
hold = static_cast<AWeaponHolder*>(inv);
if(hold->PieceWeapon == weapon)
{
if(hold->PieceMask & (1 << (cmd.value-1)))
doCommands(cmd.subBlock, xOffset, yOffset, alpha);
break;
}
}
}
break;
}
case SBARINFO_WEAPONAMMO:
if(CPlayer->ReadyWeapon != NULL)
{
@ -1146,7 +1174,7 @@ void DSBarInfo::doCommands(SBarInfoBlock &block, int xOffset, int yOffset, int a
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, xOffset, yOffset);
doCommands(cmd.subBlock, xOffset, yOffset, alpha);
break;
}
//Or means only 1 ammo type needs to match and means both need to match.
@ -1333,6 +1361,7 @@ int DSBarInfo::updateState(bool xdth, bool animatedgodmode)
{
if(weaponGrin)
{
if(currentState == NULL)
weaponGrin = false;
if(CPlayer->bonuscount)
{
@ -1414,15 +1443,18 @@ int DSBarInfo::updateState(bool xdth, bool animatedgodmode)
return 0;
}
if(!weaponGrin)
{
if((CPlayer->cheats & CF_GODMODE) || (CPlayer->mo != NULL && CPlayer->mo->flags2 & MF2_INVULNERABLE))
{
if(animatedgodmode)
SetMugShotState("godanimated", true);
SetMugShotState("godanimated");
else
SetMugShotState("god", true);
SetMugShotState("god");
}
else
SetMugShotState("normal", true);
SetMugShotState("normal");
}
}
else
{
@ -1469,6 +1501,10 @@ void DSBarInfo::DrawInventoryBar(int type, int num, int x, int y, int xOffset, i
{
DrawGraphic(Images[invBarOffset + imgSELECTBOX], x+i*31, y+29, xOffset, yOffset, alpha);
}
else if(type == GAME_Hexen)
{
DrawGraphic(Images[invBarOffset + imgSELECTBOX], x+i*31, y-1, xOffset, yOffset, alpha);
}
else
{
DrawGraphic(Images[invBarOffset + imgSELECTBOX], x+i*31, y, xOffset, yOffset, alpha);

View file

@ -95,6 +95,8 @@ static const char *SBarInfoRoutineLevel[] =
"playerclass",
"aspectratio",
"isselected",
"usessecondaryammo",
"hasweaponpiece",
"weaponammo", //event
"ininventory",
NULL
@ -705,7 +707,11 @@ void SBarInfo::ParseSBarInfoBlock(FScanner &sc, SBarInfoBlock &block)
{
cmd.special = GAME_Heretic;
}
if(sc.Compare("Doom") || sc.Compare("Heretic"))
else if(sc.Compare("Hexen"))
{
cmd.special = GAME_Hexen;
}
if(sc.Compare("Doom") || sc.Compare("Heretic") || sc.Compare("Hexen"))
{
sc.MustGetToken(',');
while(sc.CheckToken(TK_Identifier))
@ -1053,6 +1059,33 @@ void SBarInfo::ParseSBarInfoBlock(FScanner &sc, SBarInfoBlock &block)
sc.MustGetToken('{');
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('{');
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('{');
this->ParseSBarInfoBlock(sc, cmd.subBlock);
break;
}
case SBARINFO_WEAPONAMMO:
sc.MustGetToken(TK_Identifier);
if(sc.Compare("not"))

View file

@ -5334,10 +5334,7 @@ int DLevelScript::RunScript ()
}
case PCD_SETMUGSHOTSTATE:
if(StatusBar != NULL && StatusBar->IsKindOf(RUNTIME_CLASS(DSBarInfo)))
{
static_cast<DSBarInfo*>(StatusBar)->SetMugShotState(FBehavior::StaticLookupString(STACK(1)));
}
StatusBar->SetMugShotState(FBehavior::StaticLookupString(STACK(1)));
break;
case PCD_CHECKPLAYERCAMERA:

View file

@ -31,3 +31,17 @@ HUDFONT_RAVEN
8 IN8
9 IN9
}
INDEXFONT_RAVEN
{
0 SMALLIN0
1 SMALLIN1
2 SMALLIN2
3 SMALLIN3
4 SMALLIN4
5 SMALLIN5
6 SMALLIN6
7 SMALLIN7
8 SMALLIN8
9 SMALLIN9
}