- Generalized Hexen's class-based spawning to be a property of the player class

so now it is available in all games.
- Replaced the call to A_FlameSnd in the HereticPlayer's burn death sequence
  with A_FireScream and defined *burndeath for Heretic.
- Added Grubber's custom player class support.


SVN r250 (trunk)
This commit is contained in:
Christoph Oelckers 2006-07-13 10:17:56 +00:00
parent 38a073626f
commit 31c749058b
45 changed files with 1762 additions and 853 deletions

View file

@ -1,3 +1,10 @@
July 13, 2006 (Changes by Graf Zahl)
- Generalized Hexen's class-based spawning to be a property of the player class
so now it is available in all games.
- Replaced the call to A_FlameSnd in the HereticPlayer's burn death sequence
with A_FireScream and defined *burndeath for Heretic.
- Added Grubber's custom player class support.
July 12, 2006
- Changed decorate replacement to be opt-in instead of opt-out. This allows for
greater flexibility in what can be replaced (replaced actors need not be ancestors

View file

@ -61,7 +61,9 @@ static const char *KeyConfCommands[] =
"addmenukey",
"addslotdefault",
"weaponsection",
"setslot"
"setslot",
"addplayerclass",
"clearplayerclasses"
};
static long ParseCommandLine (const char *args, int *argc, char **argv);

View file

@ -1661,6 +1661,12 @@ static int PatchMisc (int dummy)
health->MaxAmount = deh.MaxSoulsphere;
}
APlayerPawn *player = static_cast<APlayerPawn *> (GetDefaultByName ("DoomPlayer"));
if (player != NULL)
{
player->health = deh.StartHealth;
}
// 0xDD means "enable infighting"
if (infighting == 0xDD)
{

View file

@ -97,6 +97,7 @@ extern void M_RestoreMode ();
extern void M_SetDefaultMode ();
extern void R_ExecuteSetViewSize ();
extern void G_NewInit ();
extern void SetupPlayerClasses ();
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
@ -2017,9 +2018,18 @@ void D_DoomMain (void)
FActorInfo::StaticInit ();
// [GRB] Initialize player class list
SetupPlayerClasses ();
// [RH] Load custom key and weapon settings from WADs
D_LoadWadSettings ();
// [GRB] Check if someone used clearplayerclasses but not addplayerclass
if (PlayerClasses.Size () == 0)
{
I_FatalError ("No player classes defined");
}
FActorInfo::StaticGameSet ();
Printf ("Init DOOM refresh subsystem.\n");

View file

@ -47,18 +47,11 @@ enum
GENDER_NEUTER
};
enum
{
PLAYERCLASS_Random = -1,
PLAYERCLASS_Fighter,
PLAYERCLASS_Cleric,
PALYERCLASS_Mage
};
extern const char *PlayerClassNames[4];
int D_GenderToInt (const char *gender);
extern const char *GenderNames[3];
int D_PlayerClassToInt (const char *classname);
struct userinfo_s
{
char netname[MAXPLAYERNAME+1];

View file

@ -83,11 +83,6 @@ enum
INFO_PlayerClass,
};
const char *PlayerClassNames[4] =
{
"Random", "Fighter", "Cleric", "Mage"
};
const char *TeamNames[NUM_TEAMS] =
{
"Red", "Blue", "Green", "Gold"
@ -125,17 +120,26 @@ int D_GenderToInt (const char *gender)
return GENDER_MALE;
}
static int D_PlayerClassToInt (const char *classname)
int D_PlayerClassToInt (const char *classname)
{
for (int i = 1; i <= 3; ++i)
if (PlayerClasses.Size () > 1)
{
if (stricmp (PlayerClassNames[i], classname) == 0)
for (unsigned int i = 0; i < PlayerClasses.Size (); ++i)
{
return i-1;
const PClass *type = PlayerClasses[i].Type;
if (stricmp (type->Meta.GetMetaString (APMETA_DisplayName), classname) == 0)
{
return i;
}
}
return -1;
}
else
{
return 0;
}
}
void D_GetPlayerColor (int player, float *h, float *s, float *v)
{
@ -319,7 +323,7 @@ void D_SetupUserInfo ()
coninfo->aimdist = abs ((int)(autoaim * (float)ANGLE_1));
}
coninfo->color = color;
coninfo->skin = R_FindSkin (skin);
coninfo->skin = R_FindSkin (skin, 0);
coninfo->gender = D_GenderToInt (gender);
coninfo->neverswitch = neverswitchonpickup;
coninfo->MoveBob = (fixed_t)(65536.f * movebob);
@ -494,6 +498,8 @@ void D_WriteUserInfoStrings (int i, byte **stream, bool compact)
{
userinfo_t *info = &players[i].userinfo;
const PClass *type = PlayerClasses[info->PlayerClass].Type;
if (!compact)
{
sprintf (*((char **)stream),
@ -517,7 +523,8 @@ void D_WriteUserInfoStrings (int i, byte **stream, bool compact)
info->neverswitch,
(float)(info->MoveBob) / 65536.f,
(float)(info->StillBob) / 65536.f,
PlayerClassNames[info->PlayerClass+1]
info->PlayerClass == -1 ? "Random" :
type->Meta.GetMetaString (APMETA_DisplayName)
);
}
else
@ -545,7 +552,8 @@ void D_WriteUserInfoStrings (int i, byte **stream, bool compact)
info->neverswitch,
(float)(info->MoveBob) / 65536.f,
(float)(info->StillBob) / 65536.f,
PlayerClassNames[info->PlayerClass+1]
info->PlayerClass == -1 ? "Random" :
type->Meta.GetMetaString (APMETA_DisplayName)
);
}
}
@ -647,10 +655,8 @@ void D_ReadUserInfoStrings (int i, byte **stream, bool update)
break;
case INFO_Skin:
info->skin = R_FindSkin (value);
if (gameinfo.gametype != GAME_Hexen)
{
if (players[i].mo != NULL && gameinfo.gametype != GAME_Hexen)
info->skin = R_FindSkin (value, players[i].CurrentPlayerClass);
if (players[i].mo != NULL)
{
if (players[i].cls != NULL &&
players[i].mo->state->sprite.index ==
@ -660,11 +666,9 @@ void D_ReadUserInfoStrings (int i, byte **stream, bool update)
players[i].mo->xscale = players[i].mo->yscale = skins[info->skin].scale;
}
}
players[i].skin = &skins[info->skin];
// Rebuild translation in case the new skin uses a different range
// than the old one.
R_BuildPlayerTranslation (i);
}
if (StatusBar != NULL && i == StatusBar->GetPlayer())
{
StatusBar->SetFace (&skins[info->skin]);

View file

@ -45,6 +45,15 @@
//Added by MC:
#include "b_bot.h"
enum
{
APMETA_BASE = 0x95000,
APMETA_DisplayName, // display name (used in menus etc.)
APMETA_SoundClass, // sound class
APMETA_ColorRange, // skin color range
};
class player_s;
class APlayerPawn : public AActor
@ -64,8 +73,6 @@ public:
virtual void PlayAttacking2 ();
virtual void ThrowPoisonBag ();
virtual void GiveDefaultInventory ();
virtual const char *GetSoundClass ();
virtual fixed_t GetJumpZ ();
virtual void TweakSpeeds (int &forwardmove, int &sidemove);
virtual bool DoHealingRadius (APlayerPawn *other);
virtual void MorphPlayerThink ();
@ -75,6 +82,8 @@ public:
virtual void GiveDeathmatchInventory ();
virtual void FilterCoopRespawnInventory (APlayerPawn *oldplayer);
const char *GetSoundClass ();
enum EInvulState
{
INVUL_Start,
@ -88,8 +97,18 @@ public:
void BeginPlay ();
void Die (AActor *source, AActor *inflictor);
fixed_t JumpZ; // [GRB] Variable JumpZ
int crouchsprite;
int MaxHealth;
// [GRB] Player class properties
fixed_t JumpZ;
fixed_t ViewHeight;
fixed_t ForwardMove1, ForwardMove2;
fixed_t SideMove1, SideMove2;
int ScoreIcon;
int SpawnMask;
int GetMaxHealth() const;
};
class APlayerChunk : public APlayerPawn
@ -177,7 +196,6 @@ public:
float FOV; // current field of vision
fixed_t viewz; // focal origin above r.z
fixed_t viewheight; // base height above floor for viewz
fixed_t defaultviewheight; // The normal view height when standing
fixed_t deltaviewheight; // squat speed.
fixed_t bob; // bounded/scaled total momentum
@ -275,7 +293,6 @@ public:
fixed_t oldx;
fixed_t oldy;
FPlayerSkin *skin; // [RH] Sprite override
float BlendR; // [RH] Final blending values
float BlendG;
float BlendB;
@ -291,7 +308,7 @@ public:
fixed_t GetDeltaViewHeight() const
{
return (defaultviewheight + crouchviewdelta - viewheight) >> 3;
return (mo->ViewHeight + crouchviewdelta - viewheight) >> 3;
}
void Uncrouch()
@ -302,6 +319,8 @@ public:
crouching = 0;
crouchviewdelta = 0;
}
int GetSpawnClass();
};
typedef player_s player_t;
@ -321,4 +340,27 @@ void P_CheckPlayerSprites();
#define MAX_DN_ANGLE 56 // Max looking down angle
#define MAX_UP_ANGLE 32 // Max looking up angle
// [GRB] Custom player classes
enum
{
PCF_NOMENU = 1, // Hide in new game menu
};
class FPlayerClass
{
public:
FPlayerClass ();
FPlayerClass (const FPlayerClass &other);
~FPlayerClass ();
bool CheckSkin (int skin);
const PClass *Type;
DWORD Flags;
TArray<int> Skins;
};
extern TArray<FPlayerClass> PlayerClasses;
#endif // __D_PLAYER_H__

View file

@ -75,9 +75,6 @@ FState ADoomPlayer::States[] =
S_NORMAL (PLAY, 'W', 5, NULL , &States[S_HTIC_XDIE+8]),
S_NORMAL (PLAY, 'X', 5, NULL , &States[S_HTIC_XDIE+9]),
S_NORMAL (PLAY, 'Y', -1, NULL , NULL),
#define S_CROUCH (S_HTIC_XDIE+10) // only here so that the crouching sprite is entered into the sprite table.
S_NORMAL (PLYC, 'A', -1, NULL , NULL),
};
IMPLEMENT_ACTOR (ADoomPlayer, Doom, -1, 0)
@ -97,14 +94,21 @@ IMPLEMENT_ACTOR (ADoomPlayer, Doom, -1, 0)
PROP_MissileState (S_PLAY_ATK)
PROP_DeathState (S_PLAY_DIE)
PROP_XDeathState (S_PLAY_XDIE)
// [GRB]
PROP_PlayerPawn_ColorRange (112, 127)
PROP_PlayerPawn_DisplayName ("Marine")
PROP_PlayerPawn_CrouchSprite ("PLYC")
END_DEFAULTS
void ADoomPlayer::GiveDefaultInventory ()
{
Super::GiveDefaultInventory ();
if (!Inventory)
{
AInventory *fist, *pistol, *bullets;
player->health = deh.StartHealth; // [RH] Used to be MAXHEALTH
health = deh.StartHealth;
fist = player->mo->GiveInventoryType (PClass::FindClass ("Fist"));
pistol = player->mo->GiveInventoryType (PClass::FindClass ("Pistol"));
// Adding the pistol automatically adds bullets
@ -116,6 +120,7 @@ void ADoomPlayer::GiveDefaultInventory ()
player->ReadyWeapon = player->PendingWeapon =
static_cast<AWeapon *> (deh.StartBullets > 0 ? pistol : fist);
}
}
void A_FireScream (AActor *self)
{
@ -182,30 +187,6 @@ void A_PlayerScream (AActor *self)
S_SoundID (self, chan, sound, 1, ATTN_NORM);
}
AT_GAME_SET(DoomPlayer)
{
// Sets the crouching sprite.
// Exception: If the normal sprite is from a PWAD and the crouching sprite from ZDoom.pk3
// it is assumed that they don't match and the crouching sprite is disabled.
// This code is not executed when the player already has a crouch sprite (set by DECORATE.)
if (gameinfo.gametype == GAME_Doom && GetDefault<ADoomPlayer>()->crouchsprite == 0)
{
int spritenorm = Wads.CheckNumForName("PLAYA1", ns_sprites);
int spritecrouch = Wads.CheckNumForName("PLYCA1", ns_sprites);
if (spritenorm==-1 || spritecrouch ==-1) return;
int wadnorm = Wads.GetLumpFile(spritenorm);
int wadcrouch = Wads.GetLumpFile(spritenorm);
if (wadnorm > FWadCollection::IWAD_FILENUM && wadcrouch <= FWadCollection::IWAD_FILENUM)
{
// Question: Add an option / disable crouching or do what?
return;
}
}
GetDefault<ADoomPlayer>()->crouchsprite = ADoomPlayer::States[S_CROUCH].sprite.index;
}
//==========================================================================
//
@ -216,7 +197,7 @@ AT_GAME_SET(DoomPlayer)
void A_DoomSkinCheck1 (AActor *actor)
{
if (actor->player != NULL &&
skins[actor->player->userinfo.skin].game != GAME_Doom)
skins[actor->player->userinfo.skin].othergame)
{
actor->SetState (&ADoomPlayer::States[S_HTIC_DIE]);
}
@ -231,7 +212,7 @@ void A_DoomSkinCheck1 (AActor *actor)
void A_DoomSkinCheck2 (AActor *actor)
{
if (actor->player != NULL &&
skins[actor->player->userinfo.skin].game != GAME_Doom)
skins[actor->player->userinfo.skin].othergame)
{
actor->SetState (&ADoomPlayer::States[S_HTIC_XDIE]);
}

View file

@ -4,6 +4,7 @@
#include "p_local.h"
#include "a_doomglobal.h"
#include "a_action.h"
#include "thingdef.h"
void A_PainAttack (AActor *);
void A_PainDie (AActor *);

View file

@ -408,7 +408,12 @@ CCMD (useflechette)
if (m_Instigator == NULL)
return;
i = (m_Instigator->player->CurrentPlayerClass + 2) % 3;
if (m_Instigator->IsKindOf (PClass::FindClass (NAME_ClericPlayer)))
i = 0;
else if (m_Instigator->IsKindOf (PClass::FindClass (NAME_MagePlayer)))
i = 1;
else
i = 2;
for (j = 0; j < 3; ++j)
{

View file

@ -108,10 +108,8 @@ class AChickenPlayer : public APlayerPawn
{
DECLARE_ACTOR (AChickenPlayer, APlayerPawn)
public:
fixed_t GetJumpZ () { return FRACUNIT; }
void MorphPlayerThink ();
void ActivateMorphWeapon ();
void TweakSpeeds (int &forward, int &side);
};
FState AChickenPlayer::States[] =
@ -160,6 +158,14 @@ IMPLEMENT_ACTOR (AChickenPlayer, Heretic, -1, 0)
PROP_MissileState (S_CHICPLAY_ATK)
PROP_DeathState (S_CHICPLAY_DIE)
// [GRB]
PROP_PlayerPawn_JumpZ (FRACUNIT)
PROP_PlayerPawn_ViewHeight (21*FRACUNIT)
PROP_PlayerPawn_ForwardMove1 (FRACUNIT * 2500 / 2048)
PROP_PlayerPawn_ForwardMove2 (FRACUNIT * 2500 / 2048)
PROP_PlayerPawn_SideMove1 (FRACUNIT * 2500 / 2048)
PROP_PlayerPawn_SideMove2 (FRACUNIT * 2500 / 2048)
PROP_PainSound ("chicken/pain")
PROP_DeathSound ("chicken/death")
END_DEFAULTS
@ -180,7 +186,7 @@ void AChickenPlayer::MorphPlayerThink ()
}
if ((z <= floorz) && (pr_chickenplayerthink() < 32))
{ // Jump and noise
momz += GetJumpZ ();
momz += JumpZ;
if (PainState != NULL)
{
SetState (PainState);
@ -207,12 +213,6 @@ void AChickenPlayer::ActivateMorphWeapon ()
}
}
void AChickenPlayer::TweakSpeeds (int &forward, int &side)
{
forward = forward * 2500 / 2048;
side = side * 2500 / 2048;
}
// Chicken (non-player) -----------------------------------------------------
class AChicken : public AActor

View file

@ -5,6 +5,7 @@
#include "d_player.h"
#include "a_action.h"
#include "p_local.h"
#include "thingdef.h"
static FRandom pr_skullpop ("SkullPop");
@ -12,7 +13,7 @@ void A_Pain (AActor *);
void A_PlayerScream (AActor *);
void A_CheckSkullFloor (AActor *);
void A_CheckSkullDone (AActor *);
void A_FlameSnd (AActor *);
void A_FireScream (AActor *);
void A_HereticSkinCheck1 (AActor *);
void A_HereticSkinCheck2 (AActor *);
void A_XScream (AActor *);
@ -69,13 +70,13 @@ FState AHereticPlayer::States[] =
S_NORMAL (PLAY, 'Y', -1, NULL , NULL),
#define S_PLAY_FDTH (S_PLAY_XDIE+10)
S_BRIGHT (FDTH, 'A', 5, A_FlameSnd , &States[S_PLAY_FDTH+1]),
S_BRIGHT (FDTH, 'A', 5, A_FireScream , &States[S_PLAY_FDTH+1]),
S_BRIGHT (FDTH, 'B', 4, NULL , &States[S_PLAY_FDTH+2]),
S_BRIGHT (FDTH, 'C', 5, NULL , &States[S_PLAY_FDTH+3]),
S_BRIGHT (FDTH, 'D', 4, A_PlayerScream , &States[S_PLAY_FDTH+4]),
S_BRIGHT (FDTH, 'E', 5, NULL , &States[S_PLAY_FDTH+5]),
S_BRIGHT (FDTH, 'F', 4, NULL , &States[S_PLAY_FDTH+6]),
S_BRIGHT (FDTH, 'G', 5, A_FlameSnd , &States[S_PLAY_FDTH+7]),
S_BRIGHT (FDTH, 'G', 5, A_FireScream , &States[S_PLAY_FDTH+7]),
S_BRIGHT (FDTH, 'H', 4, NULL , &States[S_PLAY_FDTH+8]),
S_BRIGHT (FDTH, 'I', 5, NULL , &States[S_PLAY_FDTH+9]),
S_BRIGHT (FDTH, 'J', 4, NULL , &States[S_PLAY_FDTH+10]),
@ -128,13 +129,20 @@ IMPLEMENT_ACTOR (AHereticPlayer, Heretic, -1, 0)
PROP_DeathState (S_PLAY_DIE)
PROP_XDeathState (S_PLAY_XDIE)
PROP_BDeathState (S_PLAY_FDTH)
// [GRB]
PROP_PlayerPawn_ColorRange (225, 240)
PROP_PlayerPawn_DisplayName ("Corvus")
END_DEFAULTS
void AHereticPlayer::GiveDefaultInventory ()
{
Super::GiveDefaultInventory ();
if (!Inventory)
{
AInventory *wand, *ammo;
player->health = GetDefault()->health;
player->mo->GiveInventoryType (PClass::FindClass ("Staff"));
wand = player->mo->GiveInventoryType (PClass::FindClass ("GoldWand"));
// Adding the gold wand automatically adds its ammo
@ -142,6 +150,7 @@ void AHereticPlayer::GiveDefaultInventory ()
ammo->Amount = 50;
player->ReadyWeapon = player->PendingWeapon = static_cast<AWeapon *> (wand);
}
}
// The player's skull -------------------------------------------------------
@ -185,8 +194,16 @@ void A_SkullPop (AActor *actor)
APlayerPawn *mo;
player_t *player;
// [GRB] Parameterized version
const PClass *spawntype = NULL;
int index = CheckIndex (1, NULL);
if (index >= 0)
spawntype = PClass::FindClass((ENamedName)StateParameters[index]);
if (!spawntype || !spawntype->IsDescendantOf (RUNTIME_CLASS (APlayerChunk)))
spawntype = RUNTIME_CLASS (ABloodySkull);
actor->flags &= ~MF_SOLID;
mo = Spawn<ABloodySkull> (actor->x, actor->y, actor->z + 48*FRACUNIT);
mo = (APlayerPawn *)Spawn (spawntype, actor->x, actor->y, actor->z + 48*FRACUNIT);
//mo->target = actor;
mo->momx = pr_skullpop.Random2() << 9;
mo->momy = pr_skullpop.Random2() << 9;
@ -236,17 +253,6 @@ void A_CheckSkullDone (AActor *actor)
}
}
//----------------------------------------------------------------------------
//
// PROC A_FlameSnd
//
//----------------------------------------------------------------------------
void A_FlameSnd (AActor *actor)
{
S_Sound (actor, CHAN_BODY, "misc/burn", 1, ATTN_NORM); // Burn sound
}
//==========================================================================
//
// A_HereticSkinCheck1
@ -256,7 +262,7 @@ void A_FlameSnd (AActor *actor)
void A_HereticSkinCheck1 (AActor *actor)
{
if (actor->player != NULL &&
skins[actor->player->userinfo.skin].game == GAME_Doom)
skins[actor->player->userinfo.skin].othergame)
{
actor->SetState (&AHereticPlayer::States[S_DOOM_DIE]);
}
@ -271,7 +277,7 @@ void A_HereticSkinCheck1 (AActor *actor)
void A_HereticSkinCheck2 (AActor *actor)
{
if (actor->player != NULL &&
skins[actor->player->userinfo.skin].game == GAME_Doom)
skins[actor->player->userinfo.skin].othergame)
{
actor->SetState (&AHereticPlayer::States[S_DOOM_XDIE]);
}

View file

@ -106,17 +106,18 @@ IMPLEMENT_ACTOR (AClericPlayer, Hexen, -1, 0)
PROP_BDeathState (S_PLAY_C_FDTH)
PROP_IDeathState (S_CPLAY_ICE)
// [GRB]
PROP_PlayerPawn_JumpZ (FRACUNIT*39/4) // ~9.75
PROP_PlayerPawn_ViewHeight (48*FRACUNIT)
PROP_PlayerPawn_ColorRange (146, 163)
PROP_PlayerPawn_SpawnMask (MTF_CLERIC)
PROP_PlayerPawn_DisplayName ("Cleric")
PROP_PlayerPawn_SoundClass ("cleric")
PROP_PlayerPawn_ScoreIcon ("CLERFACE")
PROP_PainSound ("PlayerClericPain")
END_DEFAULTS
const char *AClericPlayer::GetSoundClass ()
{
if (player == NULL || player->userinfo.skin == 0)
{
return "cleric";
}
return Super::GetSoundClass ();
}
void AClericPlayer::PlayAttacking2 ()
{
SetState (MissileState);
@ -124,9 +125,13 @@ void AClericPlayer::PlayAttacking2 ()
void AClericPlayer::GiveDefaultInventory ()
{
player->health = GetDefault()->health;
Super::GiveDefaultInventory ();
if (!Inventory)
{
player->ReadyWeapon = player->PendingWeapon = static_cast<AWeapon *>
(GiveInventoryType (PClass::FindClass ("CWeapMace")));
}
GiveInventoryType (RUNTIME_CLASS(AHexenArmor));
AHexenArmor *armor = FindInventory<AHexenArmor>();
@ -137,11 +142,6 @@ void AClericPlayer::GiveDefaultInventory ()
armor->SlotsIncrement[3] = 20*FRACUNIT;
}
fixed_t AClericPlayer::GetJumpZ ()
{
return FRACUNIT*39/4; // ~9.75
}
void AClericPlayer::SpecialInvulnerabilityHandling (EInvulState state, fixed_t * pAlpha)
{
if (state == INVUL_Active)

View file

@ -104,17 +104,22 @@ IMPLEMENT_ACTOR (AFighterPlayer, Hexen, -1, 0)
PROP_BDeathState (S_PLAY_F_FDTH)
PROP_IDeathState (S_FPLAY_ICE)
// [GRB]
PROP_PlayerPawn_JumpZ (FRACUNIT*39/4) // ~9.75
PROP_PlayerPawn_ViewHeight (48*FRACUNIT)
PROP_PlayerPawn_ForwardMove1 (FRACUNIT * 0x1d / 0x19)
PROP_PlayerPawn_ForwardMove2 (FRACUNIT * 0x3c / 0x32)
PROP_PlayerPawn_SideMove1 (FRACUNIT * 0x1b / 0x18)
PROP_PlayerPawn_SideMove2 (FRACUNIT * 0x3b / 0x28) // The fighter is a very fast strafer when running!
PROP_PlayerPawn_ColorRange (246, 254)
PROP_PlayerPawn_SpawnMask (MTF_FIGHTER)
PROP_PlayerPawn_DisplayName ("Fighter")
PROP_PlayerPawn_SoundClass ("fighter")
PROP_PlayerPawn_ScoreIcon ("FITEFACE")
PROP_PainSound ("PlayerFighterPain")
END_DEFAULTS
const char *AFighterPlayer::GetSoundClass ()
{
if (player == NULL || player->userinfo.skin == 0)
{
return "fighter";
}
return Super::GetSoundClass ();
}
void AFighterPlayer::PlayAttacking2 ()
{
SetState (MissileState);
@ -122,9 +127,13 @@ void AFighterPlayer::PlayAttacking2 ()
void AFighterPlayer::GiveDefaultInventory ()
{
player->health = GetDefault()->health;
Super::GiveDefaultInventory ();
if (!Inventory)
{
player->ReadyWeapon = player->PendingWeapon = static_cast<AWeapon *>
(GiveInventoryType (PClass::FindClass ("FWeapFist")));
}
GiveInventoryType (RUNTIME_CLASS(AHexenArmor));
AHexenArmor *armor = FindInventory<AHexenArmor>();
@ -340,32 +349,6 @@ punchdone:
return;
}
void AFighterPlayer::TweakSpeeds (int &forward, int &side)
{
if ((unsigned int)(forward + 0x31ff) < 0x63ff)
{
forward = forward * 0x1d / 0x19;
}
else
{
forward = forward * 0x3c / 0x32;
}
if ((unsigned int)(side + 0x27ff) < 0x4fff)
{
side = side * 0x1b / 0x18;
}
else
{ // The fighter is a very fast strafer when running!
side = side * 0x3b / 0x28;
}
Super::TweakSpeeds (forward, side);
}
fixed_t AFighterPlayer::GetJumpZ ()
{
return FRACUNIT*39/4; // ~9.75
}
// Radius armor boost
bool AFighterPlayer::DoHealingRadius (APlayerPawn *other)
{

View file

@ -83,9 +83,6 @@ class AFighterPlayer : public APlayerPawn
public:
void PlayAttacking2 ();
void GiveDefaultInventory ();
void TweakSpeeds (int &forward, int &side);
const char *GetSoundClass ();
fixed_t GetJumpZ ();
bool DoHealingRadius (APlayerPawn *other);
};
@ -102,8 +99,6 @@ class AClericPlayer : public APlayerPawn
public:
void PlayAttacking2 ();
void GiveDefaultInventory ();
const char *GetSoundClass ();
fixed_t GetJumpZ ();
void SpecialInvulnerabilityHandling (EInvulState state, fixed_t * pAlpha);
};
@ -120,9 +115,6 @@ class AMagePlayer : public APlayerPawn
public:
void PlayAttacking2 ();
void GiveDefaultInventory ();
void TweakSpeeds (int &forward, int &side);
const char *GetSoundClass ();
fixed_t GetJumpZ ();
bool DoHealingRadius (APlayerPawn *other);
void SpecialInvulnerabilityHandling (EInvulState state, fixed_t * pAlpha);
};

View file

@ -103,17 +103,22 @@ IMPLEMENT_ACTOR (AMagePlayer, Hexen, -1, 0)
PROP_BDeathState (S_PLAY_M_FDTH)
PROP_IDeathState (S_MPLAY_ICE)
// [GRB]
PROP_PlayerPawn_JumpZ (FRACUNIT*39/4) // ~9.75
PROP_PlayerPawn_ViewHeight (48*FRACUNIT)
PROP_PlayerPawn_ForwardMove1 (FRACUNIT * 0x16 / 0x19)
PROP_PlayerPawn_ForwardMove2 (FRACUNIT * 0x2e / 0x32)
PROP_PlayerPawn_SideMove1 (FRACUNIT * 0x15 / 0x18)
PROP_PlayerPawn_SideMove2 (FRACUNIT * 0x25 / 0x28)
PROP_PlayerPawn_ColorRange (146, 163)
PROP_PlayerPawn_SpawnMask (MTF_MAGE)
PROP_PlayerPawn_DisplayName ("Mage")
PROP_PlayerPawn_SoundClass ("mage")
PROP_PlayerPawn_ScoreIcon ("MAGEFACE")
PROP_PainSound ("PlayerMagePain")
END_DEFAULTS
const char *AMagePlayer::GetSoundClass ()
{
if (player == NULL || player->userinfo.skin == 0)
{
return "mage";
}
return Super::GetSoundClass ();
}
void AMagePlayer::PlayAttacking2 ()
{
SetState (MissileState);
@ -121,9 +126,13 @@ void AMagePlayer::PlayAttacking2 ()
void AMagePlayer::GiveDefaultInventory ()
{
player->health = GetDefault()->health;
Super::GiveDefaultInventory ();
if (!Inventory)
{
player->ReadyWeapon = player->PendingWeapon = static_cast<AWeapon *>
(GiveInventoryType (PClass::FindClass ("MWeapWand")));
}
GiveInventoryType (RUNTIME_CLASS(AHexenArmor));
AHexenArmor *armor = FindInventory<AHexenArmor>();
@ -134,32 +143,6 @@ void AMagePlayer::GiveDefaultInventory ()
armor->SlotsIncrement[3] = 25*FRACUNIT;
}
void AMagePlayer::TweakSpeeds (int &forward, int &side)
{
if ((unsigned int)(forward + 0x31ff) < 0x63ff)
{
forward = forward * 0x16 / 0x19;
}
else
{
forward = forward * 0x2e / 0x32;
}
if ((unsigned int)(side + 0x27ff) < 0x4fff)
{
side = side * 0x15 / 0x18;
}
else
{
side = side * 0x25 / 0x28;
}
Super::TweakSpeeds (forward, side);
}
fixed_t AMagePlayer::GetJumpZ ()
{
return FRACUNIT*39/4; // ~9.75
}
// Radius mana boost
bool AMagePlayer::DoHealingRadius (APlayerPawn *other)
{

View file

@ -90,8 +90,6 @@ class APigPlayer : public APlayerPawn
{
DECLARE_ACTOR (APigPlayer, APlayerPawn)
public:
fixed_t GetJumpZ () { return 6*FRACUNIT; }
void TweakSpeeds (int &forward, int &side);
void MorphPlayerThink ();
void ActivateMorphWeapon ();
};
@ -148,31 +146,18 @@ IMPLEMENT_ACTOR (APigPlayer, Hexen, -1, 0)
PROP_DeathState (S_PIGPLAY_DIE1)
PROP_IDeathState (S_PIGPLAY_ICE)
// [GRB]
PROP_PlayerPawn_JumpZ (6*FRACUNIT)
PROP_PlayerPawn_ViewHeight (28*FRACUNIT)
PROP_PlayerPawn_ForwardMove1 (FRACUNIT * 0x18 / 0x19) // Yes, the pig is faster than a mage.
PROP_PlayerPawn_ForwardMove2 (FRACUNIT * 0x31 / 0x32)
PROP_PlayerPawn_SideMove1 (FRACUNIT * 0x17 / 0x18)
PROP_PlayerPawn_SideMove2 (FRACUNIT * 0x27 / 0x28)
PROP_PainSound ("PigPain")
PROP_DeathSound ("PigDeath")
END_DEFAULTS
void APigPlayer::TweakSpeeds (int &forward, int &side)
{
// Yes, the pig is faster than a mage.
if ((unsigned int)(forward + 0x31ff) < 0x63ff)
{
forward = forward * 0x18 / 0x19;
}
else
{
forward = forward * 0x31 / 0x32;
}
if ((unsigned int)(side + 0x27ff) < 0x4fff)
{
side = side * 0x17 / 0x18;
}
else
{
side = side * 0x27 / 0x28;
}
}
void APigPlayer::MorphPlayerThink ()
{
if (player->morphTics&15)

View file

@ -2923,7 +2923,7 @@ static void InitPlayerClasses ()
SinglePlayerClass[i] = players[i].userinfo.PlayerClass;
if (SinglePlayerClass[i] < 0 || !playeringame[i])
{
SinglePlayerClass[i] = (pr_classchoice() >> 6) % 3;
SinglePlayerClass[i] = (pr_classchoice()) % PlayerClasses.Size ();
}
players[i].cls = NULL;
players[i].CurrentPlayerClass = SinglePlayerClass[i];

View file

@ -76,7 +76,7 @@ bool P_MorphPlayer (player_t *p, const PClass *spawntype)
actor->renderflags |= RF_INVISIBLE;
p->morphTics = MORPHTICS;
p->health = morphed->health;
p->mo = static_cast<APlayerPawn *>(morphed);
p->mo = morphed;
p->momx = p->momy = 0;
morphed->ObtainInventory (actor);
// Remove all armor
@ -94,6 +94,7 @@ bool P_MorphPlayer (player_t *p, const PClass *spawntype)
{
p->camera = morphed;
}
morphed->ScoreIcon = actor->ScoreIcon; // [GRB]
return true;
}
@ -148,6 +149,7 @@ bool P_UndoPlayerMorph (player_t *player, bool force)
mo->flags3 = (mo->flags3 & ~MF3_GHOST) | (pmo->flags3 & MF3_GHOST);
player->morphTics = 0;
player->viewheight = mo->ViewHeight;
AInventory *level2 = mo->FindInventory (RUNTIME_CLASS(APowerWeaponLevel2));
if (level2 != NULL)
{

View file

@ -1,5 +1,6 @@
#include "actor.h"
#include "info.h"
#include "thingdef.h"
#include "p_conversation.h"
#include "p_lnspec.h"
#include "a_action.h"
#include "m_random.h"
@ -66,6 +67,55 @@ IMPLEMENT_ACTOR (AIceChunkHead, Any, -1, 0)
PROP_SpawnState (0)
END_DEFAULTS
//----------------------------------------------------------------------------
//
// PROC A_NoBlocking
//
//----------------------------------------------------------------------------
void A_NoBlocking (AActor *actor)
{
// [RH] Andy Baker's stealth monsters
if (actor->flags & MF_STEALTH)
{
actor->alpha = OPAQUE;
actor->visdir = 0;
}
actor->flags &= ~MF_SOLID;
// If the actor has a conversation that sets an item to drop, drop that.
if (actor->Conversation != NULL && actor->Conversation->DropType != NULL)
{
P_DropItem (actor, actor->Conversation->DropType, -1, 256);
actor->Conversation = NULL;
return;
}
actor->Conversation = NULL;
// If the actor has attached metadata for items to drop, drop those.
// Otherwise, call NoBlockingSet() and let it decide what to drop.
if (!actor->IsKindOf (RUNTIME_CLASS (APlayerPawn))) // [GRB]
{
FDropItem *di = GetDropItems(actor);
while (di != NULL)
{
if (di->Name != NAME_None)
{
const PClass *ti = PClass::FindClass(di->Name);
if (ti) P_DropItem (actor, ti, di->amount, di->probability);
}
di = di->Next;
}
}
else
{
actor->NoBlockingSet ();
}
}
//==========================================================================
//
// A_SetFloorClip
@ -224,7 +274,7 @@ void A_FreezeDeathChunks (AActor *actor)
}
if (actor->player)
{ // attach the player's view to a chunk of ice
AIceChunkHead *head = Spawn<AIceChunkHead> (actor->x, actor->y, actor->z + actor->player->defaultviewheight);
AIceChunkHead *head = Spawn<AIceChunkHead> (actor->x, actor->y, actor->z + actor->player->mo->ViewHeight);
head->momz = FixedDiv(head->z-actor->z, actor->height)<<2;
head->momx = pr_freeze.Random2 () << (FRACBITS-7);
head->momy = pr_freeze.Random2 () << (FRACBITS-7);

View file

@ -186,7 +186,7 @@ bool P_GiveBody (AActor *actor, int num)
if (player != NULL)
{
max = ((i_compatflags&COMPATF_DEHHEALTH)? 100 : deh.MaxHealth) + player->stamina;
max = static_cast<APlayerPawn*>(actor)->GetMaxHealth() + player->stamina;
if (player->morphTics)
{
max = MAXMORPHHEALTH;
@ -820,6 +820,7 @@ void AInventory::Hide ()
{
SetState (&States[S_HIDESPECIAL]);
tics = 1400;
if (ItemFlags & IF_PICKUPFLASH) tics += 30;
}
else
{
@ -1091,16 +1092,6 @@ IMPLEMENT_STATELESS_ACTOR (APowerupGiver, Any, -1, 0)
PROP_Inventory_PickupSound ("misc/p_pkup")
END_DEFAULTS
AT_GAME_SET(PowerupGiver)
{
APowerupGiver * giver = GetDefault<APowerupGiver>();
if (gameinfo.gametype & GAME_Raven)
{
giver->RespawnTics = 1400+30;
}
}
//===========================================================================
//
// AInventory :: DoRespawn
@ -1816,7 +1807,7 @@ bool AHealth::TryPickup (AActor *other)
PrevHealth = other->player->health;
if (max == 0)
{
max = ((i_compatflags&COMPATF_DEHHEALTH)? 100 : deh.MaxHealth) + player->stamina;
max = static_cast<APlayerPawn*>(other)->GetMaxHealth() + player->stamina;
if (player->morphTics)
{
max = MAXMORPHHEALTH;

View file

@ -130,9 +130,17 @@ IMPLEMENT_ACTOR (AStrifePlayer, Strife, -1, 0)
PROP_XDeathState (S_PLAY_XDIE)
PROP_BDeathState (S_PLAY_BURNDEATH)
PROP_EDeathState (S_PLAY_ZAPDEATH)
// [GRB]
PROP_PlayerPawn_ColorRange (96, 111)
PROP_PlayerPawn_DisplayName ("Rebel")
END_DEFAULTS
void AStrifePlayer::GiveDefaultInventory ()
{
Super::GiveDefaultInventory ();
if (!Inventory)
{
AWeapon *weapon;
@ -140,6 +148,7 @@ void AStrifePlayer::GiveDefaultInventory ()
weapon = static_cast<AWeapon *>(player->mo->GiveInventoryType (PClass::FindClass ("PunchDagger")));
player->ReadyWeapon = player->PendingWeapon = weapon;
}
}
void AStrifePlayer::TweakSpeeds (int &forward, int &side)
{

View file

@ -51,8 +51,6 @@
#include "i_input.h"
#include "templates.h"
static const char *FaceNames[3] = { "FITEFACE", "CLERFACE", "MAGEFACE" };
static void HU_DrawTeamScores (player_t *, player_t *[MAXPLAYERS]);
static void HU_DrawSingleScores (player_t *, player_t *[MAXPLAYERS]);
static void HU_DrawTimeRemaining (int y);
@ -244,10 +242,9 @@ static void HU_DrawPlayer (player_t *player, bool highlight, int x, int y, int h
screen->Clear (x, y, x + 24*CleanXfac, y + height, color);
if (gameinfo.gametype == GAME_Hexen &&
player->CurrentPlayerClass < 3)
if (player->mo->ScoreIcon > 0)
{
screen->DrawTexture (TexMan[FaceNames[player->CurrentPlayerClass]], x+(pack?20:32)*CleanXfac, y,
screen->DrawTexture (TexMan[player->mo->ScoreIcon], x+(pack?20:32)*CleanXfac, y,
DTA_CleanNoMove, true, TAG_DONE);
}

View file

@ -169,6 +169,24 @@ const PClass *FState::StaticFindStateOwner (const FState *state, const FActorInf
return NULL;
}
int GetSpriteIndex(const char * spritename)
{
for (unsigned i = 0; i < sprites.Size (); ++i)
{
if (strncmp (sprites[i].name, spritename, 4) == 0)
{
return (int)i;
}
}
spritedef_t temp;
strncpy (temp.name, spritename, 4);
temp.name[4] = 0;
temp.numframes = 0;
temp.spriteframes = 0;
return (int)sprites.Push (temp);
}
// Change sprite names to indices
static void ProcessStates (FState *states, int numstates)
{
@ -180,26 +198,7 @@ static void ProcessStates (FState *states, int numstates)
{
if (sprite == -1 || strncmp (sprites[sprite].name, states->sprite.name, 4) != 0)
{
unsigned int i;
sprite = -1;
for (i = 0; i < sprites.Size (); ++i)
{
if (strncmp (sprites[i].name, states->sprite.name, 4) == 0)
{
sprite = (int)i;
break;
}
}
if (sprite == -1)
{
spritedef_t temp;
strncpy (temp.name, states->sprite.name, 4);
temp.name[4] = 0;
temp.numframes = 0;
temp.spriteframes = 0;
sprite = (int)sprites.Push (temp);
}
sprite = GetSpriteIndex(states->sprite.name);
}
states->sprite.index = sprite;
states++;

View file

@ -166,11 +166,6 @@ struct FState
static const PClass *StaticFindStateOwner (const FState *state, const FActorInfo *info);
};
// A truly awful hack to get to the state that called an action function
// without knowing whether it has been called from a weapon or actor.
extern FState * CallingState;
int CheckIndex(int paramsize, FState ** pcallstate=NULL);
FArchive &operator<< (FArchive &arc, FState *&state);
@ -245,7 +240,12 @@ enum
ADEF_Obituary,
ADEF_HitObituary,
ADEF_Inventory_PickupMsg,
ADEF_LastString = ADEF_Inventory_PickupMsg,
// [GRB] Player class properties
ADEF_PlayerPawn_CrouchSprite,
ADEF_PlayerPawn_DisplayName,
ADEF_PlayerPawn_SoundClass,
ADEF_PlayerPawn_ScoreIcon,
ADEF_LastString = ADEF_PlayerPawn_ScoreIcon,
// The rest of the properties use their type field (upper 2 bits)
ADEF_XScale,
@ -352,6 +352,16 @@ enum
ADEF_Weapon_FlashState,
ADEF_Sigil_NumPieces,
// [GRB] Player class properties
ADEF_PlayerPawn_JumpZ,
ADEF_PlayerPawn_ViewHeight,
ADEF_PlayerPawn_ForwardMove1,
ADEF_PlayerPawn_ForwardMove2,
ADEF_PlayerPawn_SideMove1,
ADEF_PlayerPawn_SideMove2,
ADEF_PlayerPawn_ColorRange,
ADEF_PlayerPawn_SpawnMask,
// The following are not properties but affect how the list is parsed
ADEF_FirstCommand,
ADEF_SkipSuper = ADEF_FirstCommand, // Take defaults from AActor instead of superclass(es)
@ -424,6 +434,8 @@ private:
extern FDoomEdMap DoomEdMap;
int GetSpriteIndex(const char * spritename);
#include "infomacros.h"
#endif // __INFO_H__

View file

@ -128,6 +128,7 @@ static void ApplyActorDefault (int defnum, const char *datastr, int dataint)
AWeapon *const weapon = (AWeapon *)sgDefaults;
ASigil *const sigil = (ASigil *)sgDefaults;
AAmmo *const ammo = (AAmmo *)sgDefaults;
APlayerPawn *const player = (APlayerPawn *)sgDefaults;
switch (defnum)
{
@ -313,6 +314,32 @@ static void ApplyActorDefault (int defnum, const char *datastr, int dataint)
case ADEF_Weapon_AltHoldAtkState:weapon->AltHoldAtkState = datastate; break;
case ADEF_Weapon_FlashState: weapon->FlashState = datastate; break;
case ADEF_Sigil_NumPieces: sigil->NumPieces = dataint; break;
// [GRB] Player class properties
case ADEF_PlayerPawn_JumpZ: player->JumpZ = dataint; break;
case ADEF_PlayerPawn_ViewHeight: player->ViewHeight = dataint; break;
case ADEF_PlayerPawn_ForwardMove1: player->ForwardMove1 = dataint; break;
case ADEF_PlayerPawn_ForwardMove2: player->ForwardMove2 = dataint; break;
case ADEF_PlayerPawn_SideMove1: player->SideMove1 = dataint; break;
case ADEF_PlayerPawn_SideMove2: player->SideMove2 = dataint; break;
case ADEF_PlayerPawn_ColorRange: sgClass->Meta.SetMetaInt (APMETA_ColorRange, dataint); break;
case ADEF_PlayerPawn_CrouchSprite: player->crouchsprite = GetSpriteIndex(datastr); break;
case ADEF_PlayerPawn_SpawnMask: player->SpawnMask = dataint; break;
case ADEF_PlayerPawn_DisplayName:
sgClass->Meta.SetMetaString (APMETA_DisplayName, datastr);
break;
case ADEF_PlayerPawn_SoundClass:
sgClass->Meta.SetMetaString (APMETA_SoundClass, datastr);
break;
case ADEF_PlayerPawn_ScoreIcon:
player->ScoreIcon = TexMan.AddPatch (datastr);
if (player->ScoreIcon <= 0)
{
player->ScoreIcon = TexMan.AddPatch (datastr, ns_sprites);
}
break;
}
}

View file

@ -210,6 +210,11 @@ public:
#define PROP_Obituary(x) ADD_STRING_PROP(ADEF_Obituary,"\21",x)
#define PROP_HitObituary(x) ADD_STRING_PROP(ADEF_HitObituary,"\22",x)
#define PROP_Inventory_PickupMessage(x) ADD_STRING_PROP(ADEF_Inventory_PickupMsg,"\23",x)
// [GRB] Player class properties
#define PROP_PlayerPawn_CrouchSprite(x) ADD_STRING_PROP(ADEF_PlayerPawn_CrouchSprite,"\24",x)
#define PROP_PlayerPawn_DisplayName(x) ADD_STRING_PROP(ADEF_PlayerPawn_DisplayName,"\25",x)
#define PROP_PlayerPawn_SoundClass(x) ADD_STRING_PROP(ADEF_PlayerPawn_SoundClass,"\26",x)
#define PROP_PlayerPawn_ScoreIcon(x) ADD_STRING_PROP(ADEF_PlayerPawn_ScoreIcon,"\27",x)
#define PROP_XScale(x) ADD_BYTE_PROP(ADEF_XScale,x)
#define PROP_YScale(x) ADD_BYTE_PROP(ADEF_YScale,x)
@ -324,4 +329,14 @@ public:
#define PROP_Weapon_FlashState(x) ADD_BYTE_PROP(ADEF_Weapon_FlashState,x)
#define PROP_Sigil_NumPieces(x) ADD_BYTE_PROP(ADEF_Sigil_NumPieces,x)
// [GRB] Player class properties
#define PROP_PlayerPawn_JumpZ(x) ADD_LONG_PROP(ADEF_PlayerPawn_JumpZ,x)
#define PROP_PlayerPawn_ViewHeight(x) ADD_LONG_PROP(ADEF_PlayerPawn_ViewHeight,x)
#define PROP_PlayerPawn_ForwardMove1(x) ADD_LONG_PROP(ADEF_PlayerPawn_ForwardMove1,x)
#define PROP_PlayerPawn_ForwardMove2(x) ADD_LONG_PROP(ADEF_PlayerPawn_ForwardMove2,x)
#define PROP_PlayerPawn_SideMove1(x) ADD_LONG_PROP(ADEF_PlayerPawn_SideMove1,x)
#define PROP_PlayerPawn_SideMove2(x) ADD_LONG_PROP(ADEF_PlayerPawn_SideMove2,x)
#define PROP_PlayerPawn_ColorRange(x,y) ADD_LONG_PROP(ADEF_PlayerPawn_ColorRange,x|(y<<8))
#define PROP_PlayerPawn_SpawnMask(x) ADD_BYTE_PROP(ADEF_PlayerPawn_SpawnMask, x)
#endif //__INFOMACROS_H__

View file

@ -289,7 +289,7 @@ void cht_DoCheat (player_t *player, int cheat)
player->morphTics = 0;
}
player->health = player->mo->health = player->mo->GetDefault()->health;
player->viewheight = player->defaultviewheight;
player->viewheight = ((APlayerPawn *)player->mo->GetDefault())->ViewHeight;
player->mo->flags = player->mo->GetDefault()->flags;
player->mo->height = player->mo->GetDefault()->height;
player->mo->SetState (player->mo->SpawnState);

View file

@ -84,6 +84,7 @@ struct FSaveGameNode : public Node
// EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
void M_DrawSlider (int x, int y, float min, float max, float cur);
void R_GetPlayerTranslation (int color, FPlayerSkin *skin, BYTE *table);
// PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
@ -106,6 +107,7 @@ static void M_GameFiles (int choice);
static void M_ClearSaveStuff ();
static void SCClass (int choice);
static void M_ChooseClass (int choice);
static void M_FinishReadThis (int choice);
static void M_QuickSave ();
@ -129,6 +131,7 @@ static void M_DrawLoad ();
static void M_DrawSave ();
static void DrawClassMenu ();
static void DrawHexenSkillMenu ();
static void M_DrawClassMenu ();
static void M_DrawHereticMainMenu ();
static void M_DrawFiles ();
@ -151,6 +154,7 @@ static void M_PlayerNameChanged (FSaveGameNode *dummy);
static void M_SlidePlayerRed (int choice);
static void M_SlidePlayerGreen (int choice);
static void M_SlidePlayerBlue (int choice);
static void M_ChangeClass (int choice);
static void M_ChangeGender (int choice);
static void M_ChangeSkin (int choice);
static void M_ChangeAutoAim (int choice);
@ -179,7 +183,6 @@ bool OptionsActive;
static char tempstring[80];
static char underscore[2];
static int MenuPClass;
static FSaveGameNode *quickSaveSlot; // NULL = no quicksave slot picked!
static FSaveGameNode *lastSaveSlot; // Used for highlighting the most recently used slot in the menu
@ -220,10 +223,12 @@ static DCanvas *FireScreen;
static byte FireRemap[256];
static char *genders[3] = { "male", "female", "other" };
static const PClass *PlayerClass;
static FPlayerClass *PlayerClass;
static int PlayerSkin;
static FState *PlayerState;
static int PlayerTics;
static int PlayerRotation;
static BYTE PlayerTranslation[256];
static DCanvas *SavePic;
static brokenlines_t *SaveComment;
@ -298,6 +303,30 @@ static oldmenu_t ClassMenu =
0
};
//
// [GRB] CLASS SELECT
//
oldmenuitem_t ClassMenuItems[8] =
{
{1,1,0, NULL, M_ChooseClass },
{1,1,0, NULL, M_ChooseClass },
{1,1,0, NULL, M_ChooseClass },
{1,1,0, NULL, M_ChooseClass },
{1,1,0, NULL, M_ChooseClass },
{1,1,0, NULL, M_ChooseClass },
{1,1,0, NULL, M_ChooseClass },
{1,1,0, NULL, M_ChooseClass },
};
oldmenu_t ClassMenuDef =
{
0,
ClassMenuItems,
M_DrawClassMenu,
48,63,
0
};
//
// EPISODE SELECT
//
@ -416,8 +445,9 @@ static oldmenuitem_t PlayerSetupMenu[] =
{ 2,0,'r',NULL,M_SlidePlayerRed},
{ 2,0,'g',NULL,M_SlidePlayerGreen},
{ 2,0,'b',NULL,M_SlidePlayerBlue},
{ 2,0,'e',NULL,M_ChangeGender},
{ 2,0,'c',NULL,M_ChangeClass},
{ 2,0,'s',NULL,M_ChangeSkin},
{ 2,0,'e',NULL,M_ChangeGender},
{ 2,0,'a',NULL,M_ChangeAutoAim}
};
@ -1492,7 +1522,7 @@ void M_NewGame(int choice)
}
epi = 0;
if (gameinfo.gametype == GAME_Hexen)
if (gameinfo.gametype == GAME_Hexen && ClassMenuDef.numitems == 0)
{ // [RH] Make the default entry the last class the player used.
ClassMenu.lastOn = players[consoleplayer].userinfo.PlayerClass;
if (ClassMenu.lastOn < 0)
@ -1501,9 +1531,37 @@ void M_NewGame(int choice)
}
M_SetupNextMenu (&ClassMenu);
}
else
// [GRB] Class select
else if (ClassMenuDef.numitems > 1)
{
if (EpiDef.numitems <= 1)
ClassMenuDef.lastOn = ClassMenuDef.numitems - 1;
if (players[consoleplayer].userinfo.PlayerClass >= 0)
{
int n = 0;
for (int i = 0; i < PlayerClasses.Size () && n < 7; i++)
{
if (!(PlayerClasses[i].Flags & PCF_NOMENU))
{
if (i == players[consoleplayer].userinfo.PlayerClass)
{
ClassMenuDef.lastOn = n;
break;
}
n++;
}
}
}
PickPlayerClass ();
PlayerState = GetDefaultByType (PlayerClass->Type)->SeeState;
PlayerTics = PlayerState->GetTics();
if (FireScreen == NULL)
FireScreen = new DSimpleCanvas (144, 160);
M_SetupNextMenu (&ClassMenuDef);
}
else if (EpiDef.numitems <= 1)
{
if (EpisodeNoSkill[0])
{
@ -1523,7 +1581,6 @@ void M_NewGame(int choice)
M_SetupNextMenu (&EpiDef);
}
}
}
//==========================================================================
//
@ -1561,6 +1618,53 @@ static void DrawClassMenu(void)
screen->DrawTexture (TexMan[name], 174+24, 8+12, DTA_Clean, true, TAG_DONE);
}
// [GRB] Class select drawer
static void M_DrawClassMenu ()
{
int tit_y = 15;
if (ClassMenuDef.numitems > 4 && gameinfo.gametype & GAME_Raven)
tit_y = 2;
screen->DrawText (gameinfo.gametype == GAME_Doom ? CR_RED : CR_UNTRANSLATED,
160 - BigFont->StringWidth ("CHOOSE CLASS:")/2,
tit_y,
"CHOOSE CLASS:", DTA_Clean, true, TAG_DONE);
int x = (200-160)*CleanXfac+(SCREENWIDTH>>1);
int y = (ClassMenuDef.y-100)*CleanYfac+(SCREENHEIGHT>>1);
if (!FireScreen)
{
screen->Clear (x, y, x + 72 * CleanXfac, y + 80 * CleanYfac-1, 0);
}
else
{
FireScreen->Lock ();
M_RenderPlayerBackdrop ();
M_DrawPlayerBackdrop (x, y - 1);
FireScreen->Unlock ();
}
M_DrawFrame (x, y, 72*CleanXfac, 80*CleanYfac-1);
spriteframe_t *sprframe = &SpriteFrames[sprites[PlayerState->sprite.index].spriteframes + PlayerState->GetFrame()];
int scale = GetDefaultByType (PlayerClass->Type)->xscale + 1;
if (sprframe != NULL)
{
FTexture *tex = TexMan(sprframe->Texture[0]);
if (tex != NULL && tex->UseType != FTexture::TEX_Null)
{
screen->DrawTexture (tex,
x + 36*CleanXfac, y + 71*CleanYfac,
DTA_DestWidth, MulScale6 (tex->GetWidth() * CleanXfac, scale),
DTA_DestHeight, MulScale6 (tex->GetHeight() * CleanYfac, scale),
TAG_DONE);
}
}
}
//---------------------------------------------------------------------------
//
// PROC DrawSkillMenu
@ -1605,10 +1709,6 @@ void M_ChooseSkill (int choice)
gameskill = choice;
gamestate = gamestate == GS_FULLCONSOLE ? GS_HIDECONSOLE : gamestate;
if (gameinfo.gametype == GAME_Hexen)
{
playerclass = PlayerClassNames[MenuPClass+1];
}
G_DeferedInitNew (EpisodeMaps[epi]);
gamestate = gamestate == GS_FULLCONSOLE ? GS_HIDECONSOLE : gamestate;
M_ClearMenus ();
@ -1645,6 +1745,53 @@ void M_Episode (int choice)
M_SetupNextMenu (&HereticSkillMenu);
}
//==========================================================================
//
// Sets Hexen's skill menu according to player class
//
//==========================================================================
static void SetHexenSkillMenu (const char * pclass)
{
if (!stricmp(pclass, "fighter"))
{
HexenSkillMenu.x = 120;
HexenSkillItems[0].name = "SQUIRE";
HexenSkillItems[1].name = "KNIGHT";
HexenSkillItems[2].name = "WARRIOR";
HexenSkillItems[3].name = "BERSERKER";
HexenSkillItems[4].name = "TITAN";
}
else if (!stricmp(pclass, "cleric"))
{
HexenSkillMenu.x = 116;
HexenSkillItems[0].name = "ALTAR BOY";
HexenSkillItems[1].name = "ACOLYTE";
HexenSkillItems[2].name = "PRIEST";
HexenSkillItems[3].name = "CARDINAL";
HexenSkillItems[4].name = "POPE";
}
else if (!stricmp(pclass, "mage"))
{
HexenSkillMenu.x = 112;
HexenSkillItems[0].name = "APPRENTICE";
HexenSkillItems[1].name = "ENCHANTER";
HexenSkillItems[2].name = "SORCERER";
HexenSkillItems[3].name = "WARLOCK";
HexenSkillItems[4].name = "ARCHMAGE";
}
else
{
// Use Heretic's menu titles as default
HexenSkillMenu.x = HereticSkillMenu.x;
HexenSkillItems[0].name = HereticSkillItems[0].name;
HexenSkillItems[1].name = HereticSkillItems[1].name;
HexenSkillItems[2].name = HereticSkillItems[2].name;
HexenSkillItems[3].name = HereticSkillItems[3].name;
HexenSkillItems[4].name = HereticSkillItems[4].name;
}
}
//==========================================================================
//
// SCClass
@ -1658,44 +1805,14 @@ static void SCClass (int option)
M_StartMessage (GStrings("NEWGAME"), NULL, false);
return;
}
MenuPClass = option < 3 ? option : -1;
switch (MenuPClass)
{
case 0/*PCLASS_FIGHTER*/:
HexenSkillMenu.x = 120;
HexenSkillItems[0].name = "SQUIRE";
HexenSkillItems[1].name = "KNIGHT";
HexenSkillItems[2].name = "WARRIOR";
HexenSkillItems[3].name = "BERSERKER";
HexenSkillItems[4].name = "TITAN";
break;
case 1/*PCLASS_CLERIC*/:
HexenSkillMenu.x = 116;
HexenSkillItems[0].name = "ALTAR BOY";
HexenSkillItems[1].name = "ACOLYTE";
HexenSkillItems[2].name = "PRIEST";
HexenSkillItems[3].name = "CARDINAL";
HexenSkillItems[4].name = "POPE";
break;
case 2/*PCLASS_MAGE*/:
HexenSkillMenu.x = 112;
HexenSkillItems[0].name = "APPRENTICE";
HexenSkillItems[1].name = "ENCHANTER";
HexenSkillItems[2].name = "SORCERER";
HexenSkillItems[3].name = "WARLOCK";
HexenSkillItems[4].name = "ARCHMAGE";
break;
case -1/*random*/: // [RH]
// Since Hexen is "Heretic 2", use the Heretic skill
// names when not playing as a specific class.
HexenSkillMenu.x = HereticSkillMenu.x;
HexenSkillItems[0].name = HereticSkillItems[0].name;
HexenSkillItems[1].name = HereticSkillItems[1].name;
HexenSkillItems[2].name = HereticSkillItems[2].name;
HexenSkillItems[3].name = HereticSkillItems[3].name;
HexenSkillItems[4].name = HereticSkillItems[4].name;
break;
}
if (option == 3)
playerclass = "Random";
else
playerclass = PlayerClasses[option].Type->Meta.GetMetaString (APMETA_DisplayName);
SetHexenSkillMenu(playerclass);
if (EpiDef.numitems > 1)
{
M_SetupNextMenu (&EpiDef);
@ -1710,6 +1827,40 @@ static void SCClass (int option)
}
}
// [GRB]
static void M_ChooseClass (int choice)
{
if (netgame)
{
M_StartMessage (GStrings("NEWGAME"), NULL, false);
return;
}
playerclass = (choice < ClassMenuDef.numitems-1) ? ClassMenuItems[choice].name : "Random";
SetHexenSkillMenu(playerclass);
if (EpiDef.numitems > 1)
{
M_SetupNextMenu (&EpiDef);
}
else if (!EpisodeNoSkill[0])
{
M_SetupNextMenu (&HexenSkillMenu);
}
else if (gameinfo.gametype & (GAME_Doom|GAME_Strife))
{
M_SetupNextMenu (&NewDef);
}
else if (gameinfo.gametype == GAME_Hexen)
{
M_SetupNextMenu (&HexenSkillMenu);
}
else
{
M_SetupNextMenu (&HereticSkillMenu);
}
}
void M_Options (int choice)
{
@ -1847,9 +1998,11 @@ void M_PlayerSetup (void)
M_SetupNextMenu (&PSetupDef);
if (players[consoleplayer].mo != NULL)
{
PlayerClass = RUNTIME_TYPE(players[consoleplayer].mo);
PlayerClass = &PlayerClasses[players[consoleplayer].CurrentPlayerClass];
}
PlayerState = GetDefaultByType (PlayerClass)->SpawnState;
PlayerSkin = players[consoleplayer].userinfo.skin;
R_GetPlayerTranslation (players[consoleplayer].userinfo.color, &skins[PlayerSkin], PlayerTranslation);
PlayerState = GetDefaultByType (PlayerClass->Type)->SeeState;
PlayerTics = PlayerState->GetTics();
if (FireScreen == NULL)
FireScreen = new DSimpleCanvas (144, 160);
@ -1858,30 +2011,42 @@ void M_PlayerSetup (void)
static void M_PlayerSetupTicker (void)
{
// Based on code in f_finale.c
if (gameinfo.gametype == GAME_Hexen)
{
const PClass *oldclass = PlayerClass;
FPlayerClass *oldclass = PlayerClass;
PickPlayerClass ();
if (PlayerClass != oldclass)
if (currentMenu == &ClassMenuDef)
{
PlayerState = GetDefaultByType (PlayerClass)->SpawnState;
PlayerTics = PlayerState->GetTics();
}
}
int item;
if (--PlayerTics > 0)
return;
if (itemOn < ClassMenuDef.numitems-1)
item = itemOn;
else
item = (MenuTime>>2) % (ClassMenuDef.numitems-1);
if (PlayerState->GetTics() == -1 || PlayerState->GetNextState() == NULL)
{
PlayerState = GetDefaultByType (PlayerClass)->SeeState;
PlayerClass = &PlayerClasses[D_PlayerClassToInt (ClassMenuItems[item].name)];
}
else
{
PlayerState = PlayerState->GetNextState();
PickPlayerClass ();
}
if (PlayerClass != oldclass)
{
PlayerState = GetDefaultByType (PlayerClass->Type)->SeeState;
PlayerTics = PlayerState->GetTics();
PlayerSkin = R_FindSkin (skins[PlayerSkin].name, PlayerClass - &PlayerClasses[0]);
R_GetPlayerTranslation (players[consoleplayer].userinfo.color,
&skins[PlayerSkin], PlayerTranslation);
}
if (PlayerState->GetTics () != -1 && PlayerState->GetNextState () != NULL)
{
if (--PlayerTics > 0)
return;
PlayerState = PlayerState->GetNextState();
PlayerTics = PlayerState->GetTics();
}
}
static void M_PlayerSetupDrawer ()
@ -1957,16 +2122,17 @@ static void M_PlayerSetupDrawer ()
spriteframe_t *sprframe;
int scale;
if (gameinfo.gametype != GAME_Hexen)
if (GetDefaultByType (PlayerClass->Type)->flags4 & MF4_NOSKIN ||
players[consoleplayer].userinfo.PlayerClass == -1 ||
PlayerState->sprite.index != GetDefaultByType (PlayerClass->Type)->SpawnState->sprite.index)
{
sprframe =
&SpriteFrames[sprites[skins[players[consoleplayer].userinfo.skin].sprite].spriteframes + PlayerState->GetFrame()];
scale = skins[players[consoleplayer].userinfo.skin].scale + 1;
sprframe = &SpriteFrames[sprites[PlayerState->sprite.index].spriteframes + PlayerState->GetFrame()];
scale = GetDefaultByType (PlayerClass->Type)->xscale + 1;
}
else
{
sprframe = &SpriteFrames[sprites[PlayerState->sprite.index].spriteframes + PlayerState->GetFrame()];
scale = GetDefault<APlayerPawn>()->xscale + 1;
sprframe = &SpriteFrames[sprites[skins[PlayerSkin].sprite].spriteframes + PlayerState->GetFrame()];
scale = skins[PlayerSkin].scale + 1;
}
if (sprframe != NULL)
@ -1983,7 +2149,7 @@ static void M_PlayerSetupDrawer ()
(PSetupDef.y + LINEHEIGHT*3 + 57 - 104)*CleanYfac + (SCREENHEIGHT/2),
DTA_DestWidth, MulScale6 (tex->GetWidth() * CleanXfac, scale),
DTA_DestHeight, MulScale6 (tex->GetHeight() * CleanYfac, scale),
DTA_Translation, translationtables[TRANSLATION_Players] + consoleplayer*256,
DTA_Translation, PlayerTranslation,
TAG_DONE);
}
}
@ -2014,33 +2180,38 @@ static void M_PlayerSetupDrawer ()
M_DrawSlider (x, PSetupDef.y + LINEHEIGHT*3+yo, 0.0f, 255.0f, GPART(color));
M_DrawSlider (x, PSetupDef.y + LINEHEIGHT*4+yo, 0.0f, 255.0f, BPART(color));
// Draw gender setting
x = SmallFont->StringWidth ("Gender") + 8 + PSetupDef.x;
screen->DrawText (label, PSetupDef.x, PSetupDef.y + LINEHEIGHT*5+yo, "Gender", DTA_Clean, true, TAG_DONE);
// [GRB] Draw class setting
int pclass = players[consoleplayer].userinfo.PlayerClass;
x = SmallFont->StringWidth ("Class") + 8 + PSetupDef.x;
screen->DrawText (label, PSetupDef.x, PSetupDef.y + LINEHEIGHT*5+yo, "Class", DTA_Clean, true, TAG_DONE);
screen->DrawText (value, x, PSetupDef.y + LINEHEIGHT*5+yo,
genders[players[consoleplayer].userinfo.gender], DTA_Clean, true, TAG_DONE);
pclass == -1 ? "Random" : PlayerClasses[pclass].Type->Meta.GetMetaString (APMETA_DisplayName),
DTA_Clean, true, TAG_DONE);
// Draw skin setting
if (gameinfo.gametype != GAME_Hexen)
{
x = SmallFont->StringWidth ("Skin") + 8 + PSetupDef.x;
screen->DrawText (label, PSetupDef.x, PSetupDef.y + LINEHEIGHT*6+yo, "Skin", DTA_Clean, true, TAG_DONE);
screen->DrawText (value, x, PSetupDef.y + LINEHEIGHT*6+yo,
skins[players[consoleplayer].userinfo.skin].name, DTA_Clean, true, TAG_DONE);
if (GetDefaultByType (PlayerClass->Type)->flags4 & MF4_NOSKIN ||
players[consoleplayer].userinfo.PlayerClass == -1)
{
screen->DrawText (value, x, PSetupDef.y + LINEHEIGHT*6+yo, "Base", DTA_Clean, true, TAG_DONE);
}
else
{
x = SmallFont->StringWidth ("Class") + 8 + PSetupDef.x;
screen->DrawText (label, PSetupDef.x, PSetupDef.y + LINEHEIGHT*6+yo, "Class", DTA_Clean, true, TAG_DONE);
screen->DrawText (value, x, PSetupDef.y + LINEHEIGHT*6+yo,
PlayerClassNames[players[consoleplayer].userinfo.PlayerClass+1], DTA_Clean, true, TAG_DONE);
skins[PlayerSkin].name, DTA_Clean, true, TAG_DONE);
}
// Draw gender setting
x = SmallFont->StringWidth ("Gender") + 8 + PSetupDef.x;
screen->DrawText (label, PSetupDef.x, PSetupDef.y + LINEHEIGHT*7+yo, "Gender", DTA_Clean, true, TAG_DONE);
screen->DrawText (value, x, PSetupDef.y + LINEHEIGHT*7+yo,
genders[players[consoleplayer].userinfo.gender], DTA_Clean, true, TAG_DONE);
// Draw autoaim setting
x = SmallFont->StringWidth ("Autoaim") + 8 + PSetupDef.x;
screen->DrawText (label, PSetupDef.x, PSetupDef.y + LINEHEIGHT*7+yo, "Autoaim", DTA_Clean, true, TAG_DONE);
screen->DrawText (value, x, PSetupDef.y + LINEHEIGHT*7+yo,
screen->DrawText (label, PSetupDef.x, PSetupDef.y + LINEHEIGHT*8+yo, "Autoaim", DTA_Clean, true, TAG_DONE);
screen->DrawText (value, x, PSetupDef.y + LINEHEIGHT*8+yo,
autoaim == 0 ? "Never" :
autoaim <= 0.25 ? "Very Low" :
autoaim <= 0.5 ? "Low" :
@ -2261,6 +2432,45 @@ static void M_DrawPlayerBackdrop (int x, int y)
}
}
static void M_ChangeClass (int choice)
{
if (PlayerClasses.Size () == 1)
{
return;
}
int type = players[consoleplayer].userinfo.PlayerClass;
if (!choice)
type = (type < 0) ? (int)PlayerClasses.Size () - 1 : type - 1;
else
type = (type < (int)PlayerClasses.Size () - 1) ? type + 1 : -1;
cvar_set ("playerclass", type < 0 ? "Random" :
PlayerClasses[type].Type->Meta.GetMetaString (APMETA_DisplayName));
}
static void M_ChangeSkin (int choice)
{
if (GetDefaultByType (PlayerClass->Type)->flags4 & MF4_NOSKIN ||
players[consoleplayer].userinfo.PlayerClass == -1)
{
return;
}
do
{
if (!choice)
PlayerSkin = (PlayerSkin == 0) ? (int)numskins - 1 : PlayerSkin - 1;
else
PlayerSkin = (PlayerSkin < (int)numskins - 1) ? PlayerSkin + 1 : 0;
} while (!PlayerClass->CheckSkin (PlayerSkin));
R_GetPlayerTranslation (players[consoleplayer].userinfo.color, &skins[PlayerSkin], PlayerTranslation);
cvar_set ("skin", skins[PlayerSkin].name);
}
static void M_ChangeGender (int choice)
{
int gender = players[consoleplayer].userinfo.gender;
@ -2273,32 +2483,6 @@ static void M_ChangeGender (int choice)
cvar_set ("gender", genders[gender]);
}
static void M_ChangeSkin (int choice)
{
if (gameinfo.gametype != GAME_Hexen)
{
size_t skin = players[consoleplayer].userinfo.skin;
if (!choice)
skin = (skin == 0) ? numskins - 1 : skin - 1;
else
skin = (skin < numskins - 1) ? skin + 1 : 0;
cvar_set ("skin", skins[skin].name);
}
else
{
int type = players[consoleplayer].userinfo.PlayerClass;
if (!choice)
type = (type < 0) ? 2 : type - 1;
else
type = (type < 2) ? type + 1 : -1;
cvar_set ("playerclass", PlayerClassNames[type+1]);
}
}
static void M_ChangeAutoAim (int choice)
{
static const float ranges[] = { 0, 0.25, 0.5, 1, 2, 3, 5000 };
@ -2393,6 +2577,8 @@ static void SendNewColor (int red, int green, int blue)
sprintf (command, "color \"%02x %02x %02x\"", red, green, blue);
C_DoCommand (command);
R_GetPlayerTranslation (MAKERGB (red, green, blue), &skins[PlayerSkin], PlayerTranslation);
}
static void M_SlidePlayerRed (int choice)
@ -2966,7 +3152,8 @@ void M_Drawer ()
if (currentMenu->menuitems[i].fulltext)
{
int color = CR_UNTRANSLATED;
if (currentMenu == &EpiDef && gameinfo.gametype == GAME_Doom)
if ((currentMenu == &EpiDef || currentMenu == &ClassMenuDef) &&
gameinfo.gametype == GAME_Doom)
{
color = CR_RED;
}
@ -3136,7 +3323,7 @@ void M_Ticker (void)
whichSkull ^= 1;
skullAnimCounter = 8;
}
if (currentMenu == &PSetupDef)
if (currentMenu == &PSetupDef || currentMenu == &ClassMenuDef)
M_PlayerSetupTicker ();
}
@ -3215,6 +3402,55 @@ void M_Init (void)
}
M_OptInit ();
// [GRB] Set up player class menu
if (!(gameinfo.gametype == GAME_Hexen && PlayerClasses.Size () == 3 &&
PlayerClasses[0].Type->IsDescendantOf (PClass::FindClass (NAME_FighterPlayer)) &&
PlayerClasses[1].Type->IsDescendantOf (PClass::FindClass (NAME_ClericPlayer)) &&
PlayerClasses[2].Type->IsDescendantOf (PClass::FindClass (NAME_MagePlayer))))
{
int n = 0;
for (i = 0; i < PlayerClasses.Size () && n < 7; i++)
{
if (!(PlayerClasses[i].Flags & PCF_NOMENU))
{
ClassMenuItems[n].name =
PlayerClasses[i].Type->Meta.GetMetaString (APMETA_DisplayName);
n++;
}
}
if (n > 1)
{
ClassMenuItems[n].name = "Random";
ClassMenuDef.numitems = n+1;
}
else
{
if (n == 0)
{
ClassMenuItems[0].name =
PlayerClasses[0].Type->Meta.GetMetaString (APMETA_DisplayName);
}
ClassMenuDef.numitems = 1;
}
if (gameinfo.gametype & (GAME_Doom|GAME_Strife))
{
ClassMenuDef.x = 48;
ClassMenuDef.y = 63;
}
else
{
ClassMenuDef.x = 80;
ClassMenuDef.y = 50;
}
if (ClassMenuDef.numitems > 4)
{
ClassMenuDef.y -= LINEHEIGHT;
}
}
// [RH] Build a palette translation table for the player setup effect
if (gameinfo.gametype != GAME_Hexen)
{
@ -3235,25 +3471,18 @@ void M_Init (void)
static void PickPlayerClass ()
{
if (gameinfo.gametype & (GAME_Doom|GAME_Strife))
{
PlayerClass = PClass::FindClass (NAME_DoomPlayer);
}
else if (gameinfo.gametype == GAME_Heretic)
{
PlayerClass = PClass::FindClass (NAME_HereticPlayer);
}
else
{
static const ENamedName classnames[3] = { NAME_FighterPlayer, NAME_ClericPlayer, NAME_MagePlayer };
int pclass = 0;
int nowtype = players[consoleplayer].userinfo.PlayerClass;
if (nowtype < 0)
// [GRB] Pick a class from player class list
if (PlayerClasses.Size () > 1)
{
nowtype = (MenuTime>>7) % 3;
pclass = players[consoleplayer].userinfo.PlayerClass;
if (pclass < 0)
{
pclass = (MenuTime>>7) % PlayerClasses.Size ();
}
}
PlayerClass = PClass::FindClass (classnames[nowtype]);
}
PlayerClass = &PlayerClasses[pclass];
}

View file

@ -4606,6 +4606,17 @@ int DLevelScript::RunScript ()
}
sp -= 2;
break;
case PCD_PLAYERCLASS: // [GRB]
if (STACK(1) < 0 || STACK(1) >= MAXPLAYERS || !playeringame[STACK(1)])
{
STACK(1) = -1;
}
else
{
STACK(1) = players[STACK(1)].CurrentPlayerClass;
}
break;
}
}

View file

@ -493,6 +493,7 @@ public:
PCD_CHECKACTORINVENTORY,
PCD_THINGCOUNTNAME,
PCD_SPAWNSPOTFACING,
PCD_PLAYERCLASS, // [GRB]
PCODE_COMMAND_COUNT
};

View file

@ -52,6 +52,7 @@
#include "a_action.h"
#include "a_keys.h"
#include "p_conversation.h"
#include "thingdef.h"
// MACROS ------------------------------------------------------------------
@ -394,9 +395,13 @@ void AActor::Serialize (FArchive &arc)
SetShade (alphacolor);
if (player)
{
if (playeringame[player - players])
if (playeringame[player - players] &&
player->cls != NULL &&
state->sprite.index ==
GetDefaultByType (player->cls)->SpawnState->sprite.index)
{ // Give player back the skin
player->skin = &skins[player->userinfo.skin];
sprite = skins[player->userinfo.skin].sprite;
xscale = yscale = skins[player->userinfo.skin].scale;
}
if (Speed == 0)
{
@ -3327,22 +3332,9 @@ void P_SpawnPlayer (mapthing2_t *mthing, bool startenterscripts)
if (p->cls == NULL)
{
p->CurrentPlayerClass = 0;
if (gameinfo.gametype == GAME_Doom)
// [GRB] Pick a class from player class list
if (PlayerClasses.Size () > 1)
{
p->cls = PClass::FindClass (NAME_DoomPlayer);
}
else if (gameinfo.gametype == GAME_Heretic)
{
p->cls = PClass::FindClass (NAME_HereticPlayer);
}
else if (gameinfo.gametype == GAME_Strife)
{
p->cls = PClass::FindClass (NAME_StrifePlayer);
}
else
{
static const ENamedName classes[3] = { NAME_FighterPlayer, NAME_ClericPlayer, NAME_MagePlayer };
int type;
if (!deathmatch || !multiplayer)
@ -3354,12 +3346,16 @@ void P_SpawnPlayer (mapthing2_t *mthing, bool startenterscripts)
type = p->userinfo.PlayerClass;
if (type < 0)
{
type = pr_multiclasschoice() % 3;
type = pr_multiclasschoice() % PlayerClasses.Size ();
}
}
p->CurrentPlayerClass = type;
p->cls = PClass::FindClass (classes[type]);
}
else
{
p->CurrentPlayerClass = 0;
}
p->cls = PlayerClasses[p->CurrentPlayerClass].Type;
}
mobj = static_cast<APlayerPawn *>
@ -3380,6 +3376,10 @@ void P_SpawnPlayer (mapthing2_t *mthing, bool startenterscripts)
mobj->ObtainInventory (oldactor);
}
// [GRB] Reset skin
p->userinfo.skin = R_FindSkin (skins[p->userinfo.skin].name, p->CurrentPlayerClass);
StatusBar->SetFace (&skins[p->userinfo.skin]);
// [RH] Be sure the player has the right translation
R_BuildPlayerTranslation (playernum);
@ -3394,12 +3394,8 @@ void P_SpawnPlayer (mapthing2_t *mthing, bool startenterscripts)
mobj->id = playernum;
// [RH] Set player sprite based on skin
p->skin = &skins[p->userinfo.skin];
if (gameinfo.gametype != GAME_Hexen)
{
mobj->sprite = p->skin->sprite;
mobj->xscale = mobj->yscale = p->skin->scale;
}
mobj->sprite = skins[p->userinfo.skin].sprite;
mobj->xscale = mobj->yscale = skins[p->userinfo.skin].scale;
p->DesiredFOV = p->FOV = 90.f;
p->camera = p->mo;
@ -3410,7 +3406,7 @@ void P_SpawnPlayer (mapthing2_t *mthing, bool startenterscripts)
p->morphTics = 0;
p->extralight = 0;
p->fixedcolormap = 0;
p->viewheight = p->defaultviewheight = gameinfo.gametype == GAME_Hexen? 48*FRACUNIT : 41*FRACUNIT;
p->viewheight = mobj->ViewHeight;
p->inconsistant = 0;
p->attacker = NULL;
p->spreecount = 0;
@ -3505,7 +3501,7 @@ void P_SpawnMapThing (mapthing2_t *mthing, int position)
{
MTF_FIGHTER,
MTF_CLERIC,
MTF_MAGE
MTF_MAGE,
};
if (mthing->type == 0 || mthing->type == -1)
@ -3615,12 +3611,12 @@ void P_SpawnMapThing (mapthing2_t *mthing, int position)
return;
}
// Check current character classes with spawn flags
if (gameinfo.gametype == GAME_Hexen)
{
// Check class spawn masks. Now with player classes available
// this is enabled for all games.
if (!multiplayer)
{ // Single player
if ((mthing->flags & classFlags[players[consoleplayer].CurrentPlayerClass]) == 0)
int spawnmask = players[consoleplayer].GetSpawnClass();
if (spawnmask != 0 && (mthing->flags & spawnmask) == 0)
{ // Not for current class
return;
}
@ -3632,16 +3628,19 @@ void P_SpawnMapThing (mapthing2_t *mthing, int position)
{
if (playeringame[i])
{
mask |= classFlags[players[i].CurrentPlayerClass];
int spawnmask = players[i].GetSpawnClass();
if (spawnmask != 0)
mask |= spawnmask;
else
mask = -1;
}
}
if ((mthing->flags & mask) == 0)
if (mask != -1 && (mthing->flags & mask) == 0)
{
return;
}
}
}
}
if (pnum != -1)
{

View file

@ -48,6 +48,9 @@
#include "f_finale.h"
#include "c_console.h"
#include "doomdef.h"
#include "c_dispatch.h"
#include "tarray.h"
#include "thingdef.h"
static FRandom pr_healradius ("HealRadius");
@ -60,6 +63,133 @@ static player_t PredictionPlayerBackup;
static BYTE PredictionActorBackup[sizeof(AActor)];
static TArray<sector_t *> PredictionTouchingSectorsBackup;
// [GRB] Custom player classes
TArray<FPlayerClass> PlayerClasses;
FPlayerClass::FPlayerClass ()
{
Type = NULL;
Flags = 0;
}
FPlayerClass::FPlayerClass (const FPlayerClass &other)
{
Type = other.Type;
Flags = other.Flags;
Skins = other.Skins;
}
FPlayerClass::~FPlayerClass ()
{
}
bool FPlayerClass::CheckSkin (int skin)
{
for (int i = 0; i < Skins.Size (); i++)
{
if (Skins[i] == skin)
return true;
}
return false;
}
void SetupPlayerClasses ()
{
FPlayerClass newclass;
newclass.Flags = 0;
if (gameinfo.gametype == GAME_Doom)
{
newclass.Type = PClass::FindClass (NAME_DoomPlayer);
PlayerClasses.Push (newclass);
}
else if (gameinfo.gametype == GAME_Heretic)
{
newclass.Type = PClass::FindClass (NAME_HereticPlayer);
PlayerClasses.Push (newclass);
}
else if (gameinfo.gametype == GAME_Hexen)
{
newclass.Type = PClass::FindClass (NAME_FighterPlayer);
PlayerClasses.Push (newclass);
newclass.Type = PClass::FindClass (NAME_ClericPlayer);
PlayerClasses.Push (newclass);
newclass.Type = PClass::FindClass (NAME_MagePlayer);
PlayerClasses.Push (newclass);
}
else if (gameinfo.gametype == GAME_Strife)
{
newclass.Type = PClass::FindClass (NAME_StrifePlayer);
PlayerClasses.Push (newclass);
}
}
CCMD (clearplayerclasses)
{
if (ParsingKeyConf)
{
PlayerClasses.Clear ();
}
}
CCMD (addplayerclass)
{
if (ParsingKeyConf && argv.argc () > 1)
{
const PClass *ti = PClass::FindClass (argv[1]);
if (!ti)
{
I_FatalError ("Unknown player class '%s'", argv[1]);
}
else if (!ti->IsDescendantOf (RUNTIME_CLASS (APlayerPawn)))
{
I_FatalError ("Invalid player class '%s'", argv[1]);
}
else if (ti->Meta.GetMetaString (APMETA_DisplayName) == NULL)
{
I_FatalError ("Missing displayname for player class '%s'", argv[1]);
}
else if (ti->ActorInfo->GameFilter == GAME_Any ||
gameinfo.gametype & ti->ActorInfo->GameFilter)
{
FPlayerClass newclass;
newclass.Type = ti;
newclass.Flags = 0;
int arg = 2;
while (arg < argv.argc ())
{
if (!stricmp (argv[arg], "nomenu"))
{
newclass.Flags |= PCF_NOMENU;
}
else
{
I_FatalError ("Unknown flag '%s' for player class '%s'", argv[arg], argv[1]);
}
arg++;
}
PlayerClasses.Push (newclass);
}
}
}
CCMD (playerclasses)
{
for (int i = 0; i < PlayerClasses.Size (); i++)
{
Printf ("% 3d %s\n", i,
PlayerClasses[i].Type->Meta.GetMetaString (APMETA_DisplayName));
}
}
//
// Movement.
//
@ -79,7 +209,6 @@ player_s::player_s()
FOV(0),
viewz(0),
viewheight(0),
defaultviewheight(0),
deltaviewheight(0),
bob(0),
momx(0),
@ -146,7 +275,6 @@ player_s::player_s()
allround(0),
oldx(0),
oldy(0),
skin(0),
BlendR(0),
BlendG(0),
BlendB(0),
@ -224,28 +352,94 @@ void player_s::SetLogText (const char *text)
LogText = text;
}
int player_t::GetSpawnClass()
{
const PClass * type = PlayerClasses[players[consoleplayer].CurrentPlayerClass].Type;
return static_cast<APlayerPawn*>(GetDefaultByType(type))->SpawnMask;
}
//===========================================================================
//
// APlayerPawn
//
//===========================================================================
IMPLEMENT_ABSTRACT_ACTOR (APlayerPawn)
IMPLEMENT_STATELESS_ACTOR (APlayerPawn, Any, -1, 0)
PROP_SpawnHealth (100)
PROP_RadiusFixed (16)
PROP_HeightFixed (56)
PROP_Mass (100)
PROP_PainChance (255)
PROP_SpeedFixed (1)
// [GRB]
PROP_PlayerPawn_JumpZ (8*FRACUNIT)
PROP_PlayerPawn_ViewHeight (41*FRACUNIT)
PROP_PlayerPawn_ForwardMove1 (FRACUNIT)
PROP_PlayerPawn_ForwardMove2 (FRACUNIT)
PROP_PlayerPawn_SideMove1 (FRACUNIT)
PROP_PlayerPawn_SideMove2 (FRACUNIT)
PROP_PlayerPawn_ColorRange (0, 0)
PROP_PlayerPawn_SoundClass ("player")
END_DEFAULTS
IMPLEMENT_ABSTRACT_ACTOR (APlayerChunk)
void APlayerPawn::Serialize (FArchive &arc)
{
Super::Serialize (arc);
arc << JumpZ;
arc << JumpZ
<< MaxHealth
<< SpawnMask
<< ForwardMove1
<< ForwardMove2
<< SideMove1
<< SideMove2
<< ScoreIcon;
}
//===========================================================================
//
// APlayerPawn :: BeginPlay
//
//===========================================================================
void APlayerPawn::BeginPlay ()
{
Super::BeginPlay ();
ChangeStatNum (STAT_PLAYER);
// Check whether a PWADs normal sprite is to be combined with the base WADs
// crouch sprite. In such a case the sprites normally don't match and it is
// best to disable the crouch sprite.
if (crouchsprite > 0)
{
// This assumes that player sprites always exist in rotated form and
// that the front view is always a separate sprite. So far this is
// true for anything that exists.
FString normspritename = sprites[SpawnState->sprite.index].name;
FString crouchspritename = sprites[crouchsprite].name;
int spritenorm = Wads.CheckNumForName(normspritename + "A1", ns_sprites);
int spritecrouch = Wads.CheckNumForName(crouchspritename + "A1", ns_sprites);
if (spritenorm==-1 || spritecrouch ==-1)
{
// Sprites do not exist so it is best to disable the crouch sprite.
crouchsprite = 0;
return;
}
int wadnorm = Wads.GetLumpFile(spritenorm);
int wadcrouch = Wads.GetLumpFile(spritenorm);
if (wadnorm > FWadCollection::IWAD_FILENUM && wadcrouch <= FWadCollection::IWAD_FILENUM)
{
// Question: Add an option / disable crouching or do what?
crouchsprite = 0;
}
}
}
//===========================================================================
//
@ -591,20 +785,48 @@ void APlayerPawn::FilterCoopRespawnInventory (APlayerPawn *oldplayer)
PickNewWeapon (NULL);
}
//===========================================================================
//
// APlayerPawn :: GetSoundClass
//
//===========================================================================
const char *APlayerPawn::GetSoundClass ()
{
if (player != NULL &&
player->userinfo.skin != 0 &&
player->userinfo.skin >= PlayerClasses.Size () &&
(unsigned)player->userinfo.skin < numskins)
{
return skins[player->userinfo.skin].name;
}
return "player";
// [GRB]
const char *sclass = GetClass ()->Meta.GetMetaString (APMETA_SoundClass);
return sclass != NULL ? sclass : "player";
}
//===========================================================================
//
// APlayerPawn :: GetMaxHealth
//
// only needed because Boom screwed up Dehacked.
//
//===========================================================================
int APlayerPawn::GetMaxHealth() const
{
return MaxHealth > 0? MaxHealth : ((i_compatflags&COMPATF_DEHHEALTH)? 100 : deh.MaxHealth);
}
//===========================================================================
//
// Animations
//
//===========================================================================
void APlayerPawn::PlayIdle ()
{
if (state >= SeeState && state < MissileState)
if (InStateSequence(state, SeeState))
SetState (SpawnState);
}
@ -628,8 +850,55 @@ void APlayerPawn::ThrowPoisonBag ()
{
}
//===========================================================================
//
// APlayerPawn :: GiveDefaultInventory
//
//===========================================================================
void APlayerPawn::GiveDefaultInventory ()
{
// [GRB] Give inventory specified in DECORATE
player->health = GetDefault ()->health;
FDropItem *di = GetDropItems(this);
while (di)
{
const PClass *ti = PClass::FindClass (di->Name);
if (ti)
{
AInventory *item = FindInventory (ti);
if (item != NULL)
{
item->Amount = clamp<int>(
item->Amount + (di->amount ? di->amount : ((AInventory *)item->GetDefault ())->Amount),
0, item->MaxAmount);
}
else
{
AInventory *item = static_cast<AInventory *>(Spawn (ti, 0,0,0));
item->Amount = di->amount;
if (item->IsKindOf (RUNTIME_CLASS (AWeapon)))
{
// To allow better control any weapon is emptied of
// ammo before being given to the player.
static_cast<AWeapon*>(item)->AmmoGive1 =
static_cast<AWeapon*>(item)->AmmoGive2 = 0;
}
if (!item->TryPickup(this))
{
item->Destroy ();
item = NULL;
}
}
if (item != NULL && item->IsKindOf (RUNTIME_CLASS (AWeapon)))
{
player->ReadyWeapon = player->PendingWeapon = static_cast<AWeapon *> (item);
}
}
di = di->Next;
}
}
void APlayerPawn::MorphPlayerThink ()
@ -640,10 +909,11 @@ void APlayerPawn::ActivateMorphWeapon ()
{
}
fixed_t APlayerPawn::GetJumpZ ()
{
return 8*FRACUNIT;
}
//===========================================================================
//
// APlayerPawn :: Die
//
//===========================================================================
void APlayerPawn::Die (AActor *source, AActor *inflictor)
{
@ -700,8 +970,32 @@ void APlayerPawn::Die (AActor *source, AActor *inflictor)
}
}
//===========================================================================
//
// APlayerPawn :: TweakSpeeds
//
//===========================================================================
void APlayerPawn::TweakSpeeds (int &forward, int &side)
{
// [GRB]
if ((unsigned int)(forward + 0x31ff) < 0x63ff)
{
forward = FixedMul (forward, ForwardMove1);
}
else
{
forward = FixedMul (forward, ForwardMove2);
}
if ((unsigned int)(side + 0x27ff) < 0x4fff)
{
side = FixedMul (side, SideMove1);
}
else
{
side = FixedMul (side, SideMove2);
}
if ((player->Powers & PW_SPEED) && !player->morphTics)
{ // Adjust for a player with a speed artifact
forward = (3*forward)>>1;
@ -753,8 +1047,7 @@ void P_CheckPlayerSprites()
defyscale = skins[player->userinfo.skin].scale;
}
// FIXME: Handle skins
// Set the crouch sprite
if (player->crouchfactor < FRACUNIT*3/4)
{
@ -783,7 +1076,7 @@ void P_CheckPlayerSprites()
mo->yscale = player->crouchfactor < FRACUNIT*3/4 ? defyscale/2 : defyscale;
}
}
else
else // Set the normal sprite
{
if (mo->sprite == mo->crouchsprite)
{
@ -901,7 +1194,7 @@ void P_CalcHeight (player_t *player)
}
}
fixed_t defaultviewheight = player->defaultviewheight + player->crouchviewdelta;
fixed_t defaultviewheight = player->mo->ViewHeight + player->crouchviewdelta;
if (player->cheats & CF_NOMOMENTUM)
{
@ -959,12 +1252,9 @@ void P_CalcHeight (player_t *player)
if (player->morphTics)
{
player->viewz = player->mo->z + player->viewheight - (20 * FRACUNIT);
bob = 0;
}
else
{
player->viewz = player->mo->z + player->viewheight + bob;
}
if (player->mo->floorclip && player->playerstate != PST_DEAD
&& player->mo->z <= player->mo->floorz)
{
@ -1320,8 +1610,8 @@ void P_CrouchMove(player_t * player, int direction)
player->mo->height = savedheight;
player->crouchfactor = clamp<fixed_t>(player->crouchfactor, FRACUNIT/2, FRACUNIT);
player->viewheight = FixedMul(player->defaultviewheight, player->crouchfactor);
player->crouchviewdelta = player->viewheight - player->defaultviewheight;
player->viewheight = FixedMul(player->mo->ViewHeight, player->crouchfactor);
player->crouchviewdelta = player->viewheight - player->mo->ViewHeight;
}
//----------------------------------------------------------------------------
@ -1448,7 +1738,7 @@ void P_PlayerThink (player_t *player)
player->Uncrouch();
}
player->crouchoffset = -FixedMul(player->defaultviewheight, (FRACUNIT - player->crouchfactor));
player->crouchoffset = -FixedMul(player->mo->ViewHeight, (FRACUNIT - player->crouchfactor));
if (player->playerstate == PST_DEAD)
@ -1536,8 +1826,7 @@ void P_PlayerThink (player_t *player)
}
else if (!(dmflags & DF_NO_JUMP) && onground && !player->jumpTics)
{
fixed_t JumpZ = (player->mo->JumpZ > 0 ? player->mo->JumpZ : player->mo->GetJumpZ ()); // [GRB]
player->mo->momz += JumpZ*35/TICRATE;
player->mo->momz += player->mo->JumpZ * 35 / TICRATE;
S_Sound (player->mo, CHAN_BODY, "*jump", 1, ATTN_NORM);
player->mo->flags2 &= ~MF2_ONMOBJ;
player->jumpTics = 18*TICRATE/35;
@ -1792,7 +2081,6 @@ void player_s::Serialize (FArchive &arc)
<< DesiredFOV << FOV
<< viewz
<< viewheight
<< defaultviewheight
<< deltaviewheight
<< bob
<< momx
@ -1821,13 +2109,8 @@ void player_s::Serialize (FArchive &arc)
<< poisoner
<< attacker
<< extralight
<< fixedcolormap;
if (SaveVersion < 233)
{
int xviewshift;
arc << xviewshift;
}
arc << morphTics
<< fixedcolormap
<< morphTics
<< PremorphWeapon
<< chickenPeck
<< jumpTics

View file

@ -303,6 +303,6 @@ DWORD R_BlendForColormap (DWORD map); // [RH] return calculated blend for a col
extern byte *realcolormaps; // [RH] make the colormaps externally visible
extern size_t numfakecmaps;
int R_FindSkin (const char *name); // [RH] Find a skin
int R_FindSkin (const char *name, int pclass); // [RH] Find a skin
#endif

View file

@ -840,7 +840,7 @@ public:
byte range0start;
byte range0end;
byte scale;
byte game;
bool othergame; // [GRB]
int sprite;
int crouchsprite;
int namespc; // namespace for this skin

View file

@ -1599,16 +1599,13 @@ void R_InitTranslationTables ()
}
// [RH] Create a player's translation table based on a given mid-range color.
void R_BuildPlayerTranslation (int player)
// [GRB] Split to 2 functions (because of player setup menu)
static void R_CreatePlayerTranslation (float h, float s, float v, FPlayerSkin *skin, BYTE *table, BYTE *alttable)
{
byte *table = &translationtables[TRANSLATION_Players][player*256];
FPlayerSkin *skin = &skins[players[player].userinfo.skin];
int i;
byte start = skin->range0start;
byte end = skin->range0end;
float r, g, b;
float h, s, v;
float bases, basev;
float sdelta, vdelta;
float range;
@ -1617,28 +1614,28 @@ void R_BuildPlayerTranslation (int player)
// for the current game, then this is just an identity translation.
// Otherwise, it remaps the colors from the skin's original palette to
// the current one.
if (skin->game & gameinfo.gametype)
if (skin->othergame)
{
memcpy (table, OtherGameSkinRemap, 256);
}
else
{
for (i = 0; i < 256; ++i)
{
table[i] = i;
}
}
else
{
memcpy (table, OtherGameSkinRemap, 256);
}
// [GRB] Don't translate skins with color range 0-0 (APlayerPawn default)
if (start == 0 && end == 0)
return;
range = (float)(end-start+1);
D_GetPlayerColor (player, &h, &s, &v);
bases = s;
basev = v;
if (gameinfo.gametype != GAME_Hexen)
{
if (skin->game == GAME_Doom)
if (gameinfo.gametype == GAME_Doom || gameinfo.gametype == GAME_Strife)
{
// Build player sprite translation
s -= 0.23f;
@ -1660,8 +1657,8 @@ void R_BuildPlayerTranslation (int player)
v += vdelta;
}
}
else
{ // This is not Doom, so it must be Heretic
else if (gameinfo.gametype == GAME_Heretic)
{
float vdelta = 0.418916f / range;
// Build player sprite translation
@ -1677,7 +1674,8 @@ void R_BuildPlayerTranslation (int player)
}
// Build rain/lifegem translation
table = &translationtables[TRANSLATION_PlayersExtra][player*256];
if (alttable)
{
bases = MIN (bases*1.3f, 1.f);
basev = MIN (basev*1.3f, 1.f);
for (i = 145; i <= 168; i++)
@ -1685,31 +1683,19 @@ void R_BuildPlayerTranslation (int player)
s = MIN (bases, 0.8965f - 0.0962f*(float)(i - 161));
v = MIN (1.f, (0.2102f + 0.0489f*(float)(i - 144)) * basev);
HSVtoRGB (&r, &g, &b, h, s, v);
table[i] = ColorMatcher.Pick (
alttable[i] = ColorMatcher.Pick (
clamp ((int)(r * 255.f), 0, 255),
clamp ((int)(g * 255.f), 0, 255),
clamp ((int)(b * 255.f), 0, 255));
}
}
}
else
{ // This is Hexen we are playing
bool fighter;
if (players[player].mo != NULL)
else if (gameinfo.gametype == GAME_Hexen)
{
fighter = players[player].mo->IsKindOf (RUNTIME_CLASS(AFighterPlayer));
}
else
{
fighter = players[player].userinfo.PlayerClass == 0;
}
if (fighter)
if (memcmp (sprites[skin->sprite].name, "PLAY", 4) == 0)
{ // The fighter is different! He gets a brown hairy loincloth, but the other
// two have blue capes.
float vs[9] = { .28f, .32f, .38f, .42f, .47f, .5f, .58f, .71f, .83f };
start = 0xf6;
end = 0xfe;
// Build player sprite translation
//h = 45.f;
@ -1717,7 +1703,7 @@ void R_BuildPlayerTranslation (int player)
for (i = start; i <= end; i++)
{
HSVtoRGB (&r, &g, &b, h, s, vs[i-start]*basev);
HSVtoRGB (&r, &g, &b, h, s, vs[(i-start)*9/(int)range]*basev);
table[i] = ColorMatcher.Pick (
clamp ((int)(r * 255.f), 0, 255),
clamp ((int)(g * 255.f), 0, 255),
@ -1728,15 +1714,13 @@ void R_BuildPlayerTranslation (int player)
{
float ms[18] = { .95f, .96f, .89f, .97f, .97f, 1.f, 1.f, 1.f, .97f, .99f, .87f, .77f, .69f, .62f, .57f, .47f, .43f };
float mv[18] = { .16f, .19f, .22f, .25f, .31f, .35f, .38f, .41f, .47f, .54f, .60f, .65f, .71f, .77f, .83f, .89f, .94f, 1.f };
start = 0x92;
end = 0xa3;
// Build player sprite translation
v = MAX (0.1f, v);
for (i = start; i <= end; i++)
{
HSVtoRGB (&r, &g, &b, h, ms[i-start]*bases, mv[i-start]*basev);
HSVtoRGB (&r, &g, &b, h, ms[(i-start)*18/(int)range]*bases, mv[(i-start)*18/(int)range]*basev);
table[i] = ColorMatcher.Pick (
clamp ((int)(r * 255.f), 0, 255),
clamp ((int)(g * 255.f), 0, 255),
@ -1745,23 +1729,45 @@ void R_BuildPlayerTranslation (int player)
}
// Build lifegem translation
table = &translationtables[TRANSLATION_PlayersExtra][player*256];
start = 0xa4;
end = 0xb9;
for (i = start; i <= end; ++i)
if (alttable)
{
for (i = 164; i <= 185; ++i)
{
const PalEntry *base = &GPalette.BaseColors[i];
float dummy;
RGBtoHSV (base->r/255.f, base->g/255.f, base->b/255.f, &dummy, &s, &v);
HSVtoRGB (&r, &g, &b, h, s*bases, v*basev);
table[i] = ColorMatcher.Pick (
alttable[i] = ColorMatcher.Pick (
clamp ((int)(r * 255.f), 0, 255),
clamp ((int)(g * 255.f), 0, 255),
clamp ((int)(b * 255.f), 0, 255));
}
}
}
}
void R_BuildPlayerTranslation (int player)
{
float h, s, v;
D_GetPlayerColor (player, &h, &s, &v);
R_CreatePlayerTranslation (h, s, v,
&skins[players[player].userinfo.skin],
&translationtables[TRANSLATION_Players][player*256],
&translationtables[TRANSLATION_PlayersExtra][player*256]);
}
void R_GetPlayerTranslation (int color, FPlayerSkin *skin, BYTE *table)
{
float h, s, v;
RGBtoHSV (RPART(color)/255.f, GPART(color)/255.f, BPART(color)/255.f,
&h, &s, &v);
R_CreatePlayerTranslation (h, s, v, skin, table, NULL);
}
void R_DrawBorder (int x1, int y1, int x2, int y2)
{

View file

@ -423,9 +423,11 @@ void R_InitSkins (void)
int j, k, base;
int lastlump;
int aliasid;
bool remove;
const PClass *basetype, *transtype;
key[sizeof(key)-1] = 0;
i = 0;
i = PlayerClasses.Size () - 1;
lastlump = 0;
for (j = 0; j < NUMSKINSOUNDS; ++j)
@ -449,6 +451,9 @@ void R_InitSkins (void)
intname = 0;
crouchname = 0;
remove = false;
basetype = NULL;
// Data is stored as "key = data".
while (SC_GetString ())
{
@ -500,12 +505,61 @@ void R_InitSkins (void)
}
else if (0 == stricmp (key, "game"))
{
if (stricmp (sc_String, "heretic"))
if (gameinfo.gametype == GAME_Heretic)
basetype = PClass::FindClass (NAME_HereticPlayer);
else if (gameinfo.gametype == GAME_Strife)
basetype = PClass::FindClass (NAME_StrifePlayer);
else
basetype = PClass::FindClass (NAME_DoomPlayer);
transtype = basetype;
if (stricmp (sc_String, "heretic") == 0)
{
skins[i].game = GAME_Heretic;
skins[i].range0start = 225;
skins[i].range0end = 240;
if (gameinfo.gametype == GAME_Doom)
{
transtype = PClass::FindClass (NAME_HereticPlayer);
skins[i].othergame = true;
}
else if (gameinfo.gametype != GAME_Heretic)
{
remove = true;
}
}
else if (stricmp (sc_String, "strife") == 0)
{
if (gameinfo.gametype != GAME_Strife)
{
remove = true;
}
}
else
{
if (gameinfo.gametype == GAME_Heretic)
{
transtype = PClass::FindClass (NAME_DoomPlayer);
skins[i].othergame = true;
}
else if (gameinfo.gametype != GAME_Doom)
{
remove = true;
}
}
if (remove)
break;
}
else if (0 == stricmp (key, "class"))
{ // [GRB] Define the skin for a specific player class
int pclass = D_PlayerClassToInt (sc_String);
if (pclass < 0)
{
remove = true;
break;
}
basetype = transtype = PlayerClasses[pclass].Type;
}
else if (key[0] == '*')
{ // Player sound replacment (ZDoom extension)
@ -554,6 +608,47 @@ void R_InitSkins (void)
}
}
// [GRB] Assume Doom skin by default
if (!remove && basetype == NULL)
{
if (gameinfo.gametype == GAME_Doom)
{
basetype = transtype = PClass::FindClass (NAME_DoomPlayer);
}
else if (gameinfo.gametype == GAME_Heretic)
{
basetype = PClass::FindClass (NAME_HereticPlayer);
transtype = PClass::FindClass (NAME_DoomPlayer);
skins[i].othergame = true;
}
else
{
remove = true;
}
}
if (!remove)
{
skins[i].range0start = transtype->Meta.GetMetaInt (APMETA_ColorRange) & 0xff;
skins[i].range0end = transtype->Meta.GetMetaInt (APMETA_ColorRange) >> 8;
remove = true;
for (j = 0; j < PlayerClasses.Size (); j++)
{
const PClass *type = PlayerClasses[j].Type;
if (type->IsDescendantOf (basetype) &&
GetDefaultByType (type)->SpawnState->sprite.index == GetDefaultByType (basetype)->SpawnState->sprite.index &&
type->Meta.GetMetaInt (APMETA_ColorRange) == basetype->Meta.GetMetaInt (APMETA_ColorRange))
{
PlayerClasses[j].Skins.Push ((int)i);
remove = false;
}
}
}
if (!remove)
{
if (skins[i].name[0] == 0)
sprintf (skins[i].name, "skin%d", i);
@ -607,10 +702,8 @@ void R_InitSkins (void)
if (spr == 0 && maxframe <= 0)
{
Printf (PRINT_BOLD, "Skin %s (#%d) has no frames. Removing.\n", skins[i].name, i);
if (i < numskins-1)
memmove (&skins[i], &skins[i+1], sizeof(skins[0])*(numskins-i-1));
i--;
continue;
remove = true;
break;
}
Wads.GetLumpName (temp.name, base+1);
@ -620,6 +713,15 @@ void R_InitSkins (void)
else skins[i].crouchsprite = sprno;
R_InstallSprite (sprno);
}
}
if (remove)
{
if (i < numskins-1)
memmove (&skins[i], &skins[i+1], sizeof(skins[0])*(numskins-i-1));
i--;
continue;
}
// Register any sounds this skin provides
aliasid = 0;
@ -649,7 +751,7 @@ void R_InitSkins (void)
}
}
if (numskins > 1)
if (numskins > PlayerClasses.Size ())
{ // The sound table may have changed, so rehash it.
S_HashSounds ();
S_ShrinkPlayerSoundLists ();
@ -657,17 +759,17 @@ void R_InitSkins (void)
}
// [RH] Find a skin by name
int R_FindSkin (const char *name)
int R_FindSkin (const char *name, int pclass)
{
int min, max, mid;
int lexx;
if (stricmp ("base", name) == 0)
{
return 0;
return pclass;
}
min = 1;
min = PlayerClasses.Size ();
max = (int)numskins-1;
while (min <= max)
@ -675,13 +777,22 @@ int R_FindSkin (const char *name)
mid = (min + max)/2;
lexx = strnicmp (skins[mid].name, name, 16);
if (lexx == 0)
{
if (PlayerClasses[pclass].CheckSkin (mid))
return mid;
else if (lexx < 0)
min = mid + 1;
else
return pclass;
}
else if (lexx < 0)
{
min = mid + 1;
}
else
{
max = mid - 1;
}
return 0;
}
return pclass;
}
// [RH] List the names of all installed skins
@ -689,8 +800,8 @@ CCMD (skins)
{
int i;
for (i = 0; i < (int)numskins; i++)
Printf ("% 3d %s\n", i, skins[i].name);
for (i = PlayerClasses.Size ()-1; i < (int)numskins; i++)
Printf ("% 3d %s\n", i-PlayerClasses.Size ()+1, skins[i].name);
}
//
@ -726,28 +837,11 @@ static void R_CreateSkinTranslation (const char *palname)
//
void R_InitSprites ()
{
byte rangestart, rangeend;
int lump, lastlump;
unsigned int i;
unsigned int i, j;
clearbufshort (zeroarray, MAXWIDTH, 0);
if (gameinfo.gametype == GAME_Doom)
{
rangestart = 112;
rangeend = 127;
}
else if (gameinfo.gametype == GAME_Heretic)
{
rangestart = 225;
rangeend = 240;
}
else // Hexen - Not used, because we don't do skins with Hexen
{
rangestart = 146;
rangeend = 163;
}
// [RH] Create a standard translation to map skins between Heretic and Doom
if (gameinfo.gametype == GAME_Doom)
{
@ -759,59 +853,61 @@ void R_InitSprites ()
}
// [RH] Count the number of skins.
numskins = 1;
numskins = PlayerClasses.Size ();
lastlump = 0;
if (gameinfo.gametype != GAME_Hexen)
{
while ((lump = Wads.FindLump ("S_SKIN", &lastlump, true)) != -1)
{
numskins++;
}
}
// [RH] Do some preliminary setup
skins = new FPlayerSkin[numskins];
memset (skins, 0, sizeof(*skins) * numskins);
for (i = 0; i < numskins; i++)
{ // Assume Doom skin by default
skins[i].range0start = 112;
skins[i].range0end = 127;
skins[i].scale = 63;
skins[i].game = GAME_Doom;
const PClass *type = PlayerClasses[0].Type;
skins[i].range0start = type->Meta.GetMetaInt (APMETA_ColorRange) & 255;
skins[i].range0end = type->Meta.GetMetaInt (APMETA_ColorRange) >> 8;
skins[i].scale = GetDefaultByType (type)->xscale;
}
R_InitSpriteDefs ();
NumStdSprites = sprites.Size();
if (gameinfo.gametype != GAME_Hexen)
{
R_InitSkins (); // [RH] Finish loading skin data
}
// [RH] Set up base skin
strcpy (skins[0].name, "Base");
skins[0].face[0] = 'S';
skins[0].face[1] = 'T';
skins[0].face[2] = 'F';
skins[0].namespc = ns_global;
skins[0].scale = GetDefaultByName ("DoomPlayer")->xscale;
skins[0].game = gameinfo.gametype;
if (gameinfo.gametype == GAME_Heretic)
// [GRB] Each player class has its own base skin
for (i = 0; i < PlayerClasses.Size (); i++)
{
skins[0].range0start = 225;
skins[0].range0end = 240;
}
const PClass *basetype = PlayerClasses[i].Type;
for (i = 0; i < sprites.Size (); i++)
strcpy (skins[i].name, "Base");
skins[i].face[0] = 'S';
skins[i].face[1] = 'T';
skins[i].face[2] = 'F';
skins[i].range0start = basetype->Meta.GetMetaInt (APMETA_ColorRange) & 255;
skins[i].range0end = basetype->Meta.GetMetaInt (APMETA_ColorRange) >> 8;
skins[i].scale = GetDefaultByType (basetype)->xscale;
skins[i].sprite = GetDefaultByType (basetype)->SpawnState->sprite.index;
skins[i].namespc = ns_global;
PlayerClasses[i].Skins.Push (i);
if (memcmp (sprites[skins[i].sprite].name, "PLAY", 4) == 0)
{
if (memcmp (sprites[i].name, deh.PlayerSprite, 4) == 0)
for (j = 0; j < sprites.Size (); j++)
{
skins[0].sprite = (int)i;
if (memcmp (sprites[j].name, deh.PlayerSprite, 4) == 0)
{
skins[i].sprite = (int)j;
break;
}
}
}
}
// [RH] Sort the skins, but leave base as skin 0
qsort (&skins[1], numskins-1, sizeof(FPlayerSkin), skinsorter);
qsort (&skins[PlayerClasses.Size ()], numskins-PlayerClasses.Size (), sizeof(FPlayerSkin), skinsorter);
}
void R_DeinitSprites()

View file

@ -221,7 +221,7 @@ static int NumPlayerReserves;
static bool DoneReserving;
static bool PlayerClassesIsSorted;
static TArray<FPlayerClassLookup> PlayerClasses;
static TArray<FPlayerClassLookup> PlayerClassLookups;
static TArray<WORD> PlayerSounds;
static char DefPlayerClassName[MAX_SNDNAME+1];
@ -1121,7 +1121,7 @@ static int S_AddPlayerClass (const char *name)
memcpy (lookup.Name, name, namelen);
lookup.ListIndex[2] = lookup.ListIndex[1] = lookup.ListIndex[0] = 0xffff;
cnum = (int)PlayerClasses.Push (lookup);
cnum = (int)PlayerClassLookups.Push (lookup);
PlayerClassesIsSorted = false;
// The default player class is the first one added
@ -1147,9 +1147,9 @@ static int S_FindPlayerClass (const char *name)
{
unsigned int i;
for (i = 0; i < PlayerClasses.Size(); ++i)
for (i = 0; i < PlayerClassLookups.Size(); ++i)
{
if (stricmp (name, PlayerClasses[i].Name) == 0)
if (stricmp (name, PlayerClassLookups[i].Name) == 0)
{
return (int)i;
}
@ -1158,12 +1158,12 @@ static int S_FindPlayerClass (const char *name)
else
{
int min = 0;
int max = (int)(PlayerClasses.Size() - 1);
int max = (int)(PlayerClassLookups.Size() - 1);
while (min <= max)
{
int mid = (min + max) / 2;
int lexx = stricmp (PlayerClasses[mid].Name, name);
int lexx = stricmp (PlayerClassLookups[mid].Name, name);
if (lexx == 0)
{
return mid;
@ -1193,13 +1193,13 @@ static int S_AddPlayerGender (int classnum, int gender)
{
int index;
index = PlayerClasses[classnum].ListIndex[gender];
index = PlayerClassLookups[classnum].ListIndex[gender];
if (index == 0xffff)
{
WORD pushee = 0;
index = (int)PlayerSounds.Size ();
PlayerClasses[classnum].ListIndex[gender] = (WORD)index;
PlayerClassLookups[classnum].ListIndex[gender] = (WORD)index;
for (int i = NumPlayerReserves; i != 0; --i)
{
PlayerSounds.Push (pushee);
@ -1213,15 +1213,15 @@ static int S_AddPlayerGender (int classnum, int gender)
// S_ShrinkPlayerSoundLists
//
// Shrinks the arrays used by the player sounds to be just large enough
// and also sorts the PlayerClasses array.
// and also sorts the PlayerClassLookups array.
//==========================================================================
void S_ShrinkPlayerSoundLists ()
{
PlayerSounds.ShrinkToFit ();
PlayerClasses.ShrinkToFit ();
PlayerClassLookups.ShrinkToFit ();
qsort (&PlayerClasses[0], PlayerClasses.Size(),
qsort (&PlayerClassLookups[0], PlayerClassLookups.Size(),
sizeof(FPlayerClassLookup), SortPlayerClasses);
PlayerClassesIsSorted = true;
DefPlayerClass = S_FindPlayerClass (DefPlayerClassName);
@ -1269,14 +1269,14 @@ static int S_LookupPlayerSound (int classidx, int gender, int refid)
classidx = DefPlayerClass;
}
int listidx = PlayerClasses[classidx].ListIndex[gender];
int listidx = PlayerClassLookups[classidx].ListIndex[gender];
if (listidx == 0xffff)
{
int g;
for (g = 0; g < 3 && listidx == 0xffff; ++g)
{
listidx = PlayerClasses[classidx].ListIndex[g];
listidx = PlayerClassLookups[classidx].ListIndex[g];
}
if (g == 3)
{ // No sounds defined at all for this class (can this happen?)
@ -1428,8 +1428,8 @@ int S_FindSkinnedSound (AActor *actor, int refid)
}
else
{
pclass = gameinfo.gametype != GAME_Hexen
? "player" : "fighter";
pclass =
((APlayerPawn *)GetDefaultByType (PlayerClasses[0].Type))->GetSoundClass ();
gender = GENDER_MALE;
}
@ -1527,13 +1527,13 @@ CCMD (playersounds)
}
}
for (i = 0; i < PlayerClasses.Size(); ++i)
for (i = 0; i < PlayerClassLookups.Size(); ++i)
{
for (j = 0; j < 3; ++j)
{
if ((l = PlayerClasses[i].ListIndex[j]) != 0xffff)
if ((l = PlayerClassLookups[i].ListIndex[j]) != 0xffff)
{
Printf ("\n%s, %s:\n", PlayerClasses[i].Name, GenderNames[j]);
Printf ("\n%s, %s:\n", PlayerClassLookups[i].Name, GenderNames[j]);
for (k = 0; k < NumPlayerReserves; ++l, ++k)
{
Printf (" %-16s%s\n", reserveNames[k], S_sfx[PlayerSounds[l]].name);

View file

@ -208,6 +208,7 @@ static flagdef ActorFlags[]=
DEFINE_FLAG(MF4, NOEXTREMEDEATH, AActor, flags4),
DEFINE_FLAG(MF4, FRIGHTENED, AActor, flags4),
DEFINE_FLAG(MF4, NOBOUNCESOUND, AActor, flags4),
DEFINE_FLAG(MF4, NOSKIN, AActor, flags4),
DEFINE_FLAG(MF5, FASTER, AActor, flags5),
DEFINE_FLAG(MF5, FASTMELEE, AActor, flags5),
@ -500,6 +501,9 @@ ACTOR(CountdownArg)
ACTOR(CustomMeleeAttack)
ACTOR(Light)
ACTOR(Burst)
ACTOR(SkullPop)
ACTOR(CheckFloor)
ACTOR(CheckSkullDone)
ACTOR(RadiusThrust)
@ -606,6 +610,8 @@ AFuncDesc AFTable[]=
FUNC(A_CentaurDefend, NULL)
FUNC(A_BishopMissileWeave, NULL)
FUNC(A_CStaffMissileSlither, NULL)
FUNC(A_SkullPop, "m")
{"A_CheckPlayerDone", A_CheckSkullDone, NULL },
// useful functions from Strife
FUNC(A_Wander, NULL)
@ -675,6 +681,7 @@ AFuncDesc AFTable[]=
FUNC(A_JumpIf, "XL")
FUNC(A_KillMaster, NULL)
FUNC(A_KillChildren, NULL)
FUNC(A_CheckFloor, "L")
{"A_BasicAttack", A_ComboAttack, "ISMF" },
// Weapon only functions
@ -949,14 +956,6 @@ enum
};
struct FDropItem
{
FName Name;
int probability;
int amount;
FDropItem * Next;
};
static void FreeDropItemChain(FDropItem *chain)
{
while (chain != NULL)
@ -979,59 +978,17 @@ public:
}
};
FDropItemPtrArray DropItemList;
static FDropItemPtrArray DropItemList;
//----------------------------------------------------------------------------
//
// PROC A_NoBlocking
//
// (moved here so that it has access to FDropItemList's definition)
//
//----------------------------------------------------------------------------
void A_NoBlocking (AActor *actor)
FDropItem *GetDropItems(AActor * actor)
{
// [RH] Andy Baker's stealth monsters
if (actor->flags & MF_STEALTH)
{
actor->alpha = OPAQUE;
actor->visdir = 0;
}
actor->flags &= ~MF_SOLID;
unsigned int index = actor->GetClass ()->Meta.GetMetaInt (ACMETA_DropItems) - 1;
// If the actor has a conversation that sets an item to drop, drop that.
if (actor->Conversation != NULL && actor->Conversation->DropType != NULL)
{
P_DropItem (actor, actor->Conversation->DropType, -1, 256);
actor->Conversation = NULL;
return;
}
actor->Conversation = NULL;
// If the actor has attached metadata for items to drop, drop those.
// Otherwise, call NoBlockingSet() and let it decide what to drop.
if (index >= 0 && index < DropItemList.Size())
{
FDropItem *di = DropItemList[index];
while (di != NULL)
{
if (di->Name != NAME_None)
{
const PClass *ti = PClass::FindClass(di->Name);
if (ti) P_DropItem (actor, ti, di->amount, di->probability);
}
di = di->Next;
}
}
else
{
actor->NoBlockingSet ();
return DropItemList[index];
}
return NULL;
}
//==========================================================================
@ -1165,7 +1122,7 @@ FState ** FindState(AActor * actor, const PClass * type, const char * name)
}
else if (type->IsDescendantOf (RUNTIME_CLASS(ASwitchableDecoration)))
{
// These are the names that 2.1.0 will use
// These are the names that the code will use when custom labels are working.
if (!stricmp(name, "ACTIVE")) return &actor->SeeState;
if (!stricmp(name, "INACTIVE")) return &actor->MeleeState;
}
@ -2838,8 +2795,8 @@ static void ActorMeleeDamage (AActor *defaults, Baggage &bag)
//==========================================================================
static void ActorMeleeRange (AActor *defaults, Baggage &bag)
{
SC_MustGetNumber();
defaults->meleerange = sc_Number;
SC_MustGetFloat();
defaults->meleerange = fixed_t(sc_Float*FRACUNIT);
}
//==========================================================================
@ -3524,6 +3481,181 @@ static void PowerupType (APowerupGiver *defaults, Baggage &bag)
}
}
//==========================================================================
//
// [GRB] Special player properties
//
//==========================================================================
//==========================================================================
//
//==========================================================================
static void PlayerDisplayName (APlayerPawn *defaults, Baggage &bag)
{
SC_MustGetString ();
bag.Info->Class->Meta.SetMetaString (APMETA_DisplayName, sc_String);
}
//==========================================================================
//
//==========================================================================
static void PlayerSoundClass (APlayerPawn *defaults, Baggage &bag)
{
char tmp[256];
SC_MustGetString ();
sprintf (tmp, sc_String);
for (int i = 0; i < strlen (tmp); i++)
if (tmp[i] == ' ')
tmp[i] = '_';
bag.Info->Class->Meta.SetMetaString (APMETA_SoundClass, tmp);
}
//==========================================================================
//
//==========================================================================
static void PlayerColorRange (APlayerPawn *defaults, Baggage &bag)
{
int start, end;
SC_MustGetNumber ();
start = sc_Number;
SC_MustGetNumber ();
end = sc_Number;
if (start > end)
swap (start, end);
bag.Info->Class->Meta.SetMetaInt (APMETA_ColorRange, (start & 255) | ((end & 255) << 8));
}
//==========================================================================
//
//==========================================================================
static void PlayerJumpZ (APlayerPawn *defaults, Baggage &bag)
{
SC_MustGetFloat ();
defaults->JumpZ = FLOAT2FIXED (sc_Float);
}
//==========================================================================
//
//==========================================================================
static void PlayerSpawnClass (APlayerPawn *defaults, Baggage &bag)
{
SC_MustGetString ();
if (SC_Compare ("Any"))
defaults->SpawnMask = 0;
else if (SC_Compare ("Fighter"))
defaults->SpawnMask |= MTF_FIGHTER;
else if (SC_Compare ("Cleric"))
defaults->SpawnMask |= MTF_CLERIC;
else if (SC_Compare ("Mage"))
defaults->SpawnMask |= MTF_MAGE;
else if (IsNum(sc_String))
{
int val = strtol(sc_String, NULL, 0);
defaults->SpawnMask = (val*MTF_FIGHTER) & (MTF_FIGHTER|MTF_CLERIC|MTF_MAGE);
}
}
//==========================================================================
//
//==========================================================================
static void PlayerViewHeight (APlayerPawn *defaults, Baggage &bag)
{
SC_MustGetFloat ();
defaults->ViewHeight = FLOAT2FIXED (sc_Float);
}
//==========================================================================
//
//==========================================================================
static void PlayerForwardMove (APlayerPawn *defaults, Baggage &bag)
{
SC_MustGetFloat ();
defaults->ForwardMove1 = defaults->ForwardMove2 = FLOAT2FIXED (sc_Float);
if (SC_CheckFloat ())
defaults->ForwardMove2 = FLOAT2FIXED (sc_Float);
}
//==========================================================================
//
//==========================================================================
static void PlayerSideMove (APlayerPawn *defaults, Baggage &bag)
{
SC_MustGetFloat ();
defaults->SideMove1 = defaults->SideMove2 = FLOAT2FIXED (sc_Float);
if (SC_CheckFloat ())
defaults->SideMove2 = FLOAT2FIXED (sc_Float);
}
//==========================================================================
//
//==========================================================================
static void PlayerMaxHealth (APlayerPawn *defaults, Baggage &bag)
{
SC_MustGetNumber ();
defaults->MaxHealth = sc_Number;
}
//==========================================================================
//
//==========================================================================
static void PlayerScoreIcon (APlayerPawn *defaults, Baggage &bag)
{
SC_MustGetString ();
defaults->ScoreIcon = TexMan.AddPatch (sc_String);
if (defaults->ScoreIcon <= 0)
{
defaults->ScoreIcon = TexMan.AddPatch (sc_String, ns_sprites);
if (defaults->ScoreIcon <= 0)
{
Printf("Icon '%s' for '%s' not found\n", sc_String, bag.Info->Class->TypeName.GetChars ());
}
}
}
//==========================================================================
//
//==========================================================================
static void PlayerCrouchSprite (APlayerPawn *defaults, Baggage &bag)
{
SC_MustGetString ();
for (int i = 0; i < sc_StringLen; i++) sc_String[i] = toupper (sc_String[i]);
defaults->crouchsprite = GetSpriteIndex (sc_String);
}
//==========================================================================
//
// [GRB] Store start items in drop item list
//
//==========================================================================
static void PlayerStartItem (APlayerPawn *defaults, Baggage &bag)
{
// create a linked list of dropitems
if (!bag.DropItemSet)
{
bag.DropItemSet = true;
bag.DropItemList = NULL;
}
FDropItem * di=new FDropItem;
SC_MustGetString();
di->Name=strdup(sc_String);
di->probability=255;
di->amount=0;
if (SC_CheckNumber())
{
di->amount=sc_Number;
}
di->Next = bag.DropItemList;
bag.DropItemList = di;
}
//==========================================================================
//
//==========================================================================
@ -3625,6 +3757,18 @@ static const ActorProps props[] =
{ "pain", ActorPainState, RUNTIME_CLASS(AActor) },
{ "painchance", ActorPainChance, RUNTIME_CLASS(AActor) },
{ "painsound", ActorPainSound, RUNTIME_CLASS(AActor) },
{ "player.colorrange", (apf)PlayerColorRange, RUNTIME_CLASS(APlayerPawn) },
{ "player.crouchsprite", (apf)PlayerCrouchSprite, RUNTIME_CLASS(APlayerPawn) },
{ "player.displayname", (apf)PlayerDisplayName, RUNTIME_CLASS(APlayerPawn) },
{ "player.forwardmove", (apf)PlayerForwardMove, RUNTIME_CLASS(APlayerPawn) },
{ "player.jumpz", (apf)PlayerJumpZ, RUNTIME_CLASS(APlayerPawn) },
{ "player.maxhealth", (apf)PlayerMaxHealth, RUNTIME_CLASS(APlayerPawn) },
{ "player.scoreicon", (apf)PlayerScoreIcon, RUNTIME_CLASS(APlayerPawn) },
{ "player.sidemove", (apf)PlayerSideMove, RUNTIME_CLASS(APlayerPawn) },
{ "player.soundclass", (apf)PlayerSoundClass, RUNTIME_CLASS(APlayerPawn) },
{ "player.spawnclass", (apf)PlayerSpawnClass, RUNTIME_CLASS(APlayerPawn) },
{ "player.startitem", (apf)PlayerStartItem, RUNTIME_CLASS(APlayerPawn) },
{ "player.viewheight", (apf)PlayerViewHeight, RUNTIME_CLASS(APlayerPawn) },
{ "poisondamage", ActorPoisonDamage, RUNTIME_CLASS(AActor) },
{ "powerup.color", (apf)PowerupColor, RUNTIME_CLASS(APowerupGiver) },
{ "powerup.duration", (apf)PowerupDuration, RUNTIME_CLASS(APowerupGiver) },

View file

@ -21,10 +21,31 @@ public:
}
};
int ParseExpression (bool _not);
int EvalExpressionI (int id, AActor *self);
float EvalExpressionF (int id, AActor *self);
bool EvalExpressionN (int id, AActor *self);
struct FDropItem
{
FName Name;
int probability;
int amount;
FDropItem * Next;
};
FDropItem *GetDropItems(AActor * actor);
// A truly awful hack to get to the state that called an action function
// without knowing whether it has been called from a weapon or actor.
extern FState * CallingState;
int CheckIndex(int paramsize, FState ** pcallstate=NULL);
#endif

View file

@ -1658,3 +1658,19 @@ void A_Burst (AActor *actor)
actor->Destroy ();
}
//===========================================================================
//
// A_CheckFloor
// [GRB] Jumps if actor is standing on floor
//
//===========================================================================
void A_CheckFloor (AActor *self)
{
FState *CallingState;
int index = CheckIndex (1, &CallingState);
if (self->z <= self->floorz && index >= 0)
{
DoJump (self, CallingState, StateParameters[index]);
}
}

View file

@ -54,7 +54,7 @@
// Version identifier for network games.
// Bump it every time you do a release unless you're certain you
// didn't change anything that will affect sync.
#define NETGAMEVERSION 207
#define NETGAMEVERSION 208
// Version stored in the ini's [LastRun] section.
// Bump it if you made some configuration change that you want to
@ -73,8 +73,8 @@
// SAVEVER is the version of the information stored in level snapshots.
// Note that SAVEVER is not directly comparable to VERSION.
// SAVESIG should match SAVEVER.
#define SAVEVER 233
#define SAVESIG "ZDOOMSAVE233"
#define SAVEVER 234
#define SAVESIG "ZDOOMSAVE234"
// This is so that derivates can use the same savegame versions without worrying about engine compatibility
#define GAMESIG "ZDOOM"
@ -89,7 +89,7 @@
#endif
// MINSAVEVER is the minimum level snapshot version that can be loaded.
#define MINSAVEVER 232 // Used by 2.1.0
#define MINSAVEVER 234 // Used by 2.1.2
// The maximum length of one save game description for the menus.
#define SAVESTRINGSIZE 24

View file

@ -1636,6 +1636,31 @@ FMemLump FWadCollection::ReadLump (int lump)
return FMemLump (dest);
}
//==========================================================================
//
// SetLumpAddress
//
//==========================================================================
void FWadCollection::SetLumpAddress(LumpRecord *l)
{
// This file is inside a zip and has not been opened before.
// Position points to the start of the local file header, which we must
// read and skip so that we can get to the actual file data.
FZipLocalHeader localHeader;
int skiplen;
int address;
WadFileRecord *wad = Wads[l->wadnum];
address = wad->Tell();
wad->Seek (l->position, SEEK_SET);
wad->Read (&localHeader, sizeof(localHeader));
skiplen = LittleShort(localHeader.wFileNameSize) + LittleShort(localHeader.wExtraSize);
l->position += sizeof(localHeader) + skiplen;
l->flags &= ~LUMPF_NEEDFILESTART;
}
//==========================================================================
//
// OpenLumpNum
@ -1660,23 +1685,10 @@ FWadLump FWadCollection::OpenLumpNum (int lump)
if (l->flags & LUMPF_NEEDFILESTART)
{
// This file is inside a zip and has not been opened before.
// Position points to the start of the local file header, which we must
// read and skip so that we can get to the actual file data.
FZipLocalHeader localHeader;
int skiplen;
SetLumpAddress(l);
}
wad->Seek (l->position, SEEK_SET);
wad->Read (&localHeader, sizeof(localHeader));
skiplen = LittleShort(localHeader.wFileNameSize) + LittleShort(localHeader.wExtraSize);
l->position += sizeof(localHeader) + skiplen;
wad->Seek (skiplen, SEEK_CUR);
l->flags &= ~LUMPF_NEEDFILESTART;
}
else
{
wad->Seek (l->position, SEEK_SET);
}
if (l->flags & LUMPF_COMPRESSED)
{
@ -1728,20 +1740,7 @@ FWadLump *FWadCollection::ReopenLumpNum (int lump)
if (l->flags & LUMPF_NEEDFILESTART)
{
// This file is inside a zip and has not been opened before.
// Position points to the start of the local file header, which we must
// read and skip so that we can get to the actual file data.
FZipLocalHeader localHeader;
int skiplen;
int address;
address = wad->Tell();
wad->Seek (l->position, SEEK_SET);
wad->Read (&localHeader, sizeof(localHeader));
skiplen = LittleShort(localHeader.wFileNameSize) + LittleShort(localHeader.wExtraSize);
l->position += sizeof(localHeader) + skiplen;
l->flags &= ~LUMPF_NEEDFILESTART;
wad->Seek (address, SEEK_SET);
SetLumpAddress(l);
}
if (l->flags & LUMPF_COMPRESSED)

View file

@ -237,6 +237,7 @@ private:
static int STACK_ARGS lumpcmp(const void * a, const void * b);
void ScanForFlatHack (int startlump);
void RenameSprites (int startlump);
void SetLumpAddress(LumpRecord *l);
};
extern FWadCollection Wads;

View file

@ -529,6 +529,7 @@ $playersound player male *grunt plroof
$playersounddup player male *usefail *grunt
$playersounddup player male *land *grunt
$playersound player male *jump plrjmp
$playersound player male *burndeath hedat1
chicken/sight chicpai
chicken/pain chicpai