From 31c749058bd846cbfcdaba297a6f8eeb4e8ed2c4 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Thu, 13 Jul 2006 10:17:56 +0000 Subject: [PATCH] - 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) --- docs/rh-log.txt | 7 + src/c_dispatch.cpp | 4 +- src/d_dehacked.cpp | 6 + src/d_main.cpp | 10 + src/d_netinf.h | 11 +- src/d_netinfo.cpp | 60 ++-- src/d_player.h | 54 ++- src/g_doom/a_doomplayer.cpp | 105 +++--- src/g_doom/a_painelemental.cpp | 1 + src/g_game.cpp | 7 +- src/g_heretic/a_chicken.cpp | 18 +- src/g_heretic/a_hereticplayer.cpp | 56 ++-- src/g_hexen/a_clericplayer.cpp | 32 +- src/g_hexen/a_fighterplayer.cpp | 57 ++-- src/g_hexen/a_hexenglobal.h | 8 - src/g_hexen/a_mageplayer.cpp | 57 ++-- src/g_hexen/a_pig.cpp | 31 +- src/g_level.cpp | 2 +- src/g_raven/a_artiegg.cpp | 4 +- src/g_shared/a_action.cpp | 54 ++- src/g_shared/a_pickups.cpp | 15 +- src/g_strife/a_strifeplayer.cpp | 17 +- src/hu_scores.cpp | 7 +- src/info.cpp | 39 ++- src/info.h | 24 +- src/infodefaults.cpp | 27 ++ src/infomacros.h | 15 + src/m_cheat.cpp | 2 +- src/m_menu.cpp | 527 +++++++++++++++++++++--------- src/p_acs.cpp | 11 + src/p_acs.h | 1 + src/p_mobj.cpp | 97 +++--- src/p_user.cpp | 351 ++++++++++++++++++-- src/r_data.h | 2 +- src/r_defs.h | 2 +- src/r_draw.cpp | 168 +++++----- src/r_things.cpp | 324 +++++++++++------- src/s_advsound.cpp | 36 +- src/thingdef.cpp | 260 +++++++++++---- src/thingdef.h | 21 ++ src/thingdef_codeptr.cpp | 16 + src/version.h | 8 +- src/w_wad.cpp | 59 ++-- src/w_wad.h | 1 + wadsrc/sndinfo.txt | 1 + 45 files changed, 1762 insertions(+), 853 deletions(-) diff --git a/docs/rh-log.txt b/docs/rh-log.txt index 8c7a5fc0ca..ab36f01d4f 100644 --- a/docs/rh-log.txt +++ b/docs/rh-log.txt @@ -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 diff --git a/src/c_dispatch.cpp b/src/c_dispatch.cpp index 54de5e2757..b273644773 100644 --- a/src/c_dispatch.cpp +++ b/src/c_dispatch.cpp @@ -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); diff --git a/src/d_dehacked.cpp b/src/d_dehacked.cpp index bfc026b1b7..97c813cf01 100644 --- a/src/d_dehacked.cpp +++ b/src/d_dehacked.cpp @@ -1661,6 +1661,12 @@ static int PatchMisc (int dummy) health->MaxAmount = deh.MaxSoulsphere; } + APlayerPawn *player = static_cast (GetDefaultByName ("DoomPlayer")); + if (player != NULL) + { + player->health = deh.StartHealth; + } + // 0xDD means "enable infighting" if (infighting == 0xDD) { diff --git a/src/d_main.cpp b/src/d_main.cpp index 21ae355ad0..d1e97b1ede 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -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"); diff --git a/src/d_netinf.h b/src/d_netinf.h index a8b3709ec4..3612bf8410 100644 --- a/src/d_netinf.h +++ b/src/d_netinf.h @@ -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]; diff --git a/src/d_netinfo.cpp b/src/d_netinfo.cpp index 1c5092166c..e234fef48a 100644 --- a/src/d_netinfo.cpp +++ b/src/d_netinfo.cpp @@ -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,16 +120,25 @@ 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; } - return -1; } 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,24 +655,20 @@ void D_ReadUserInfoStrings (int i, byte **stream, bool update) break; case INFO_Skin: - info->skin = R_FindSkin (value); - if (gameinfo.gametype != GAME_Hexen) + info->skin = R_FindSkin (value, players[i].CurrentPlayerClass); + if (players[i].mo != NULL) { - if (players[i].mo != NULL && gameinfo.gametype != GAME_Hexen) - { - if (players[i].cls != NULL && - players[i].mo->state->sprite.index == - GetDefaultByType (players[i].cls)->SpawnState->sprite.index) - { // Only change the sprite if the player is using a standard one - players[i].mo->sprite = skins[info->skin].sprite; - players[i].mo->xscale = players[i].mo->yscale = skins[info->skin].scale; - } + if (players[i].cls != NULL && + players[i].mo->state->sprite.index == + GetDefaultByType (players[i].cls)->SpawnState->sprite.index) + { // Only change the sprite if the player is using a standard one + players[i].mo->sprite = skins[info->skin].sprite; + 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); } + // 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]); diff --git a/src/d_player.h b/src/d_player.h index d37253117c..68eeb4fd28 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -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 Skins; +}; + +extern TArray PlayerClasses; + #endif // __D_PLAYER_H__ diff --git a/src/g_doom/a_doomplayer.cpp b/src/g_doom/a_doomplayer.cpp index 139b85e504..e70422c9b9 100644 --- a/src/g_doom/a_doomplayer.cpp +++ b/src/g_doom/a_doomplayer.cpp @@ -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,24 +94,32 @@ 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 () { - AInventory *fist, *pistol, *bullets; + Super::GiveDefaultInventory (); - 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 - bullets = player->mo->FindInventory (PClass::FindClass ("Clip")); - if (bullets != NULL) + if (!Inventory) { - bullets->Amount = deh.StartBullets; // [RH] Used to be 50 + AInventory *fist, *pistol, *bullets; + + fist = player->mo->GiveInventoryType (PClass::FindClass ("Fist")); + pistol = player->mo->GiveInventoryType (PClass::FindClass ("Pistol")); + // Adding the pistol automatically adds bullets + bullets = player->mo->FindInventory (PClass::FindClass ("Clip")); + if (bullets != NULL) + { + bullets->Amount = deh.StartBullets; // [RH] Used to be 50 + } + player->ReadyWeapon = player->PendingWeapon = + static_cast (deh.StartBullets > 0 ? pistol : fist); } - player->ReadyWeapon = player->PendingWeapon = - static_cast (deh.StartBullets > 0 ? pistol : fist); } void A_FireScream (AActor *self) @@ -182,57 +187,33 @@ void A_PlayerScream (AActor *self) S_SoundID (self, chan, sound, 1, ATTN_NORM); } -AT_GAME_SET(DoomPlayer) + +//========================================================================== +// +// A_DoomSkinCheck1 +// +//========================================================================== + +void A_DoomSkinCheck1 (AActor *actor) { - // 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()->crouchsprite == 0) + if (actor->player != NULL && + skins[actor->player->userinfo.skin].othergame) { - 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; - } + actor->SetState (&ADoomPlayer::States[S_HTIC_DIE]); } - GetDefault()->crouchsprite = ADoomPlayer::States[S_CROUCH].sprite.index; } -//========================================================================== -// -// A_DoomSkinCheck1 -// -//========================================================================== - -void A_DoomSkinCheck1 (AActor *actor) -{ - if (actor->player != NULL && - skins[actor->player->userinfo.skin].game != GAME_Doom) - { - actor->SetState (&ADoomPlayer::States[S_HTIC_DIE]); - } -} - -//========================================================================== -// -// A_DoomSkinCheck2 -// -//========================================================================== - -void A_DoomSkinCheck2 (AActor *actor) -{ - if (actor->player != NULL && - skins[actor->player->userinfo.skin].game != GAME_Doom) - { - actor->SetState (&ADoomPlayer::States[S_HTIC_XDIE]); - } +//========================================================================== +// +// A_DoomSkinCheck2 +// +//========================================================================== + +void A_DoomSkinCheck2 (AActor *actor) +{ + if (actor->player != NULL && + skins[actor->player->userinfo.skin].othergame) + { + actor->SetState (&ADoomPlayer::States[S_HTIC_XDIE]); + } } diff --git a/src/g_doom/a_painelemental.cpp b/src/g_doom/a_painelemental.cpp index 21a68d0000..dc0ab72bf4 100644 --- a/src/g_doom/a_painelemental.cpp +++ b/src/g_doom/a_painelemental.cpp @@ -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 *); diff --git a/src/g_game.cpp b/src/g_game.cpp index 6db53fac9a..24be31458f 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -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) { diff --git a/src/g_heretic/a_chicken.cpp b/src/g_heretic/a_chicken.cpp index 812eb62632..b9e4d8047d 100644 --- a/src/g_heretic/a_chicken.cpp +++ b/src/g_heretic/a_chicken.cpp @@ -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 diff --git a/src/g_heretic/a_hereticplayer.cpp b/src/g_heretic/a_hereticplayer.cpp index e6237cee62..ada10be27c 100644 --- a/src/g_heretic/a_hereticplayer.cpp +++ b/src/g_heretic/a_hereticplayer.cpp @@ -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,19 +129,27 @@ 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 () { - AInventory *wand, *ammo; + Super::GiveDefaultInventory (); - 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 - ammo = player->mo->FindInventory (PClass::FindClass ("GoldWandAmmo")); - ammo->Amount = 50; - player->ReadyWeapon = player->PendingWeapon = static_cast (wand); + if (!Inventory) + { + AInventory *wand, *ammo; + + player->mo->GiveInventoryType (PClass::FindClass ("Staff")); + wand = player->mo->GiveInventoryType (PClass::FindClass ("GoldWand")); + // Adding the gold wand automatically adds its ammo + ammo = player->mo->FindInventory (PClass::FindClass ("GoldWandAmmo")); + ammo->Amount = 50; + player->ReadyWeapon = player->PendingWeapon = static_cast (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 (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]); } diff --git a/src/g_hexen/a_clericplayer.cpp b/src/g_hexen/a_clericplayer.cpp index 19166452d9..0b97be5b0c 100644 --- a/src/g_hexen/a_clericplayer.cpp +++ b/src/g_hexen/a_clericplayer.cpp @@ -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; - player->ReadyWeapon = player->PendingWeapon = static_cast - (GiveInventoryType (PClass::FindClass ("CWeapMace"))); + Super::GiveDefaultInventory (); + + if (!Inventory) + { + player->ReadyWeapon = player->PendingWeapon = static_cast + (GiveInventoryType (PClass::FindClass ("CWeapMace"))); + } GiveInventoryType (RUNTIME_CLASS(AHexenArmor)); AHexenArmor *armor = FindInventory(); @@ -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) diff --git a/src/g_hexen/a_fighterplayer.cpp b/src/g_hexen/a_fighterplayer.cpp index 1ca8975df4..f80fee1a76 100644 --- a/src/g_hexen/a_fighterplayer.cpp +++ b/src/g_hexen/a_fighterplayer.cpp @@ -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; - player->ReadyWeapon = player->PendingWeapon = static_cast - (GiveInventoryType (PClass::FindClass ("FWeapFist"))); + Super::GiveDefaultInventory (); + + if (!Inventory) + { + player->ReadyWeapon = player->PendingWeapon = static_cast + (GiveInventoryType (PClass::FindClass ("FWeapFist"))); + } GiveInventoryType (RUNTIME_CLASS(AHexenArmor)); AHexenArmor *armor = FindInventory(); @@ -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) { diff --git a/src/g_hexen/a_hexenglobal.h b/src/g_hexen/a_hexenglobal.h index 8ebb316661..0a16082f7b 100644 --- a/src/g_hexen/a_hexenglobal.h +++ b/src/g_hexen/a_hexenglobal.h @@ -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); }; diff --git a/src/g_hexen/a_mageplayer.cpp b/src/g_hexen/a_mageplayer.cpp index b68a7c4131..bd4d9a40e8 100644 --- a/src/g_hexen/a_mageplayer.cpp +++ b/src/g_hexen/a_mageplayer.cpp @@ -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; - player->ReadyWeapon = player->PendingWeapon = static_cast - (GiveInventoryType (PClass::FindClass ("MWeapWand"))); + Super::GiveDefaultInventory (); + + if (!Inventory) + { + player->ReadyWeapon = player->PendingWeapon = static_cast + (GiveInventoryType (PClass::FindClass ("MWeapWand"))); + } GiveInventoryType (RUNTIME_CLASS(AHexenArmor)); AHexenArmor *armor = FindInventory(); @@ -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) { diff --git a/src/g_hexen/a_pig.cpp b/src/g_hexen/a_pig.cpp index 8b52673a87..f4031e83f1 100644 --- a/src/g_hexen/a_pig.cpp +++ b/src/g_hexen/a_pig.cpp @@ -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) diff --git a/src/g_level.cpp b/src/g_level.cpp index 68242e781d..3ff4110fc7 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -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]; diff --git a/src/g_raven/a_artiegg.cpp b/src/g_raven/a_artiegg.cpp index c097b31c18..4237422f3c 100644 --- a/src/g_raven/a_artiegg.cpp +++ b/src/g_raven/a_artiegg.cpp @@ -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(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) { diff --git a/src/g_shared/a_action.cpp b/src/g_shared/a_action.cpp index 2926d02016..ee4230e719 100644 --- a/src/g_shared/a_action.cpp +++ b/src/g_shared/a_action.cpp @@ -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 (actor->x, actor->y, actor->z + actor->player->defaultviewheight); + AIceChunkHead *head = Spawn (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); diff --git a/src/g_shared/a_pickups.cpp b/src/g_shared/a_pickups.cpp index 55e3034818..013ffea08a 100644 --- a/src/g_shared/a_pickups.cpp +++ b/src/g_shared/a_pickups.cpp @@ -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(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(); - - 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(other)->GetMaxHealth() + player->stamina; if (player->morphTics) { max = MAXMORPHHEALTH; diff --git a/src/g_strife/a_strifeplayer.cpp b/src/g_strife/a_strifeplayer.cpp index e073deceef..1ef0a2b5f1 100644 --- a/src/g_strife/a_strifeplayer.cpp +++ b/src/g_strife/a_strifeplayer.cpp @@ -130,15 +130,24 @@ 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 () { - AWeapon *weapon; + Super::GiveDefaultInventory (); - player->health = GetDefault()->health; - weapon = static_cast(player->mo->GiveInventoryType (PClass::FindClass ("PunchDagger"))); - player->ReadyWeapon = player->PendingWeapon = weapon; + if (!Inventory) + { + AWeapon *weapon; + + player->health = GetDefault()->health; + weapon = static_cast(player->mo->GiveInventoryType (PClass::FindClass ("PunchDagger"))); + player->ReadyWeapon = player->PendingWeapon = weapon; + } } void AStrifePlayer::TweakSpeeds (int &forward, int &side) diff --git a/src/hu_scores.cpp b/src/hu_scores.cpp index ed106a5260..4cf3ba3049 100644 --- a/src/hu_scores.cpp +++ b/src/hu_scores.cpp @@ -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); } diff --git a/src/info.cpp b/src/info.cpp index 64675840b5..ef186db518 100644 --- a/src/info.cpp +++ b/src/info.cpp @@ -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++; diff --git a/src/info.h b/src/info.h index 57d831a713..996ccd72fd 100644 --- a/src/info.h +++ b/src/info.h @@ -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__ diff --git a/src/infodefaults.cpp b/src/infodefaults.cpp index 9028331b84..ebe2ef2180 100644 --- a/src/infodefaults.cpp +++ b/src/infodefaults.cpp @@ -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; + } } diff --git a/src/infomacros.h b/src/infomacros.h index 392a55a6a4..31ea79dd13 100644 --- a/src/infomacros.h +++ b/src/infomacros.h @@ -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__ diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index 5b213098c2..9258b1e39e 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -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); diff --git a/src/m_menu.cpp b/src/m_menu.cpp index 9eb54c11ce..e1468a0dfc 100644 --- a/src/m_menu.cpp +++ b/src/m_menu.cpp @@ -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 @@ -219,11 +222,13 @@ static oldmenu_t *TopLevelMenu; // The main menu everything hangs off of static DCanvas *FireScreen; static byte FireRemap[256]; -static char *genders[3] = { "male", "female", "other" }; -static const PClass *PlayerClass; -static FState *PlayerState; -static int PlayerTics; -static int PlayerRotation; +static char *genders[3] = { "male", "female", "other" }; +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,28 +1531,55 @@ 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) { - if (EpisodeNoSkill[0]) + int n = 0; + for (int i = 0; i < PlayerClasses.Size () && n < 7; i++) { - M_ChooseSkill(2); - } - else if (gameinfo.gametype & (GAME_Doom|GAME_Strife)) - { - M_SetupNextMenu (&NewDef); - } - else - { - M_SetupNextMenu (&HereticSkillMenu); + 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]) + { + M_ChooseSkill(2); + } + else if (gameinfo.gametype & (GAME_Doom|GAME_Strife)) + { + M_SetupNextMenu (&NewDef); + } else { - M_SetupNextMenu (&EpiDef); + M_SetupNextMenu (&HereticSkillMenu); } } + else + { + 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) + FPlayerClass *oldclass = PlayerClass; + + if (currentMenu == &ClassMenuDef) { - const PClass *oldclass = PlayerClass; + int item; - PickPlayerClass (); - if (PlayerClass != oldclass) - { - PlayerState = GetDefaultByType (PlayerClass)->SpawnState; - PlayerTics = PlayerState->GetTics(); - } - } + if (itemOn < ClassMenuDef.numitems-1) + item = itemOn; + else + item = (MenuTime>>2) % (ClassMenuDef.numitems-1); - if (--PlayerTics > 0) - return; - - 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(); } - PlayerTics = PlayerState->GetTics(); } static void M_PlayerSetupDrawer () @@ -1956,17 +2121,18 @@ 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()->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); + if (GetDefaultByType (PlayerClass->Type)->flags4 & MF4_NOSKIN || + players[consoleplayer].userinfo.PlayerClass == -1) { - 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); + 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; + // [GRB] Pick a class from player class list + if (PlayerClasses.Size () > 1) + { + pclass = players[consoleplayer].userinfo.PlayerClass; - if (nowtype < 0) + if (pclass < 0) { - nowtype = (MenuTime>>7) % 3; + pclass = (MenuTime>>7) % PlayerClasses.Size (); } - - PlayerClass = PClass::FindClass (classnames[nowtype]); } + + PlayerClass = &PlayerClasses[pclass]; } diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 007d97c9e3..1d6e38919b 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -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; } } diff --git a/src/p_acs.h b/src/p_acs.h index 97212dbc2a..a27d25319d 100644 --- a/src/p_acs.h +++ b/src/p_acs.h @@ -493,6 +493,7 @@ public: PCD_CHECKACTORINVENTORY, PCD_THINGCOUNTNAME, PCD_SPAWNSPOTFACING, + PCD_PLAYERCLASS, // [GRB] PCODE_COMMAND_COUNT }; diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 18d4ca3899..69711d1575 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -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 @@ -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,30 +3611,33 @@ void P_SpawnMapThing (mapthing2_t *mthing, int position) return; } - // Check current character classes with spawn flags - if (gameinfo.gametype == GAME_Hexen) - { - if (!multiplayer) - { // Single player - if ((mthing->flags & classFlags[players[consoleplayer].CurrentPlayerClass]) == 0) - { // Not for current class - return; + // Check class spawn masks. Now with player classes available + // this is enabled for all games. + if (!multiplayer) + { // Single player + int spawnmask = players[consoleplayer].GetSpawnClass(); + if (spawnmask != 0 && (mthing->flags & spawnmask) == 0) + { // Not for current class + return; + } + } + else if (!deathmatch) + { // Cooperative + mask = 0; + for (int i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i]) + { + int spawnmask = players[i].GetSpawnClass(); + if (spawnmask != 0) + mask |= spawnmask; + else + mask = -1; } } - else if (!deathmatch) - { // Cooperative - mask = 0; - for (int i = 0; i < MAXPLAYERS; i++) - { - if (playeringame[i]) - { - mask |= classFlags[players[i].CurrentPlayerClass]; - } - } - if ((mthing->flags & mask) == 0) - { - return; - } + if (mask != -1 && (mthing->flags & mask) == 0) + { + return; } } } diff --git a/src/p_user.cpp b/src/p_user.cpp index 852053a165..d6b09bf00a 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -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 PredictionTouchingSectorsBackup; +// [GRB] Custom player classes +TArray 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(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( + item->Amount + (di->amount ? di->amount : ((AInventory *)item->GetDefault ())->Amount), + 0, item->MaxAmount); + } + else + { + AInventory *item = static_cast(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(item)->AmmoGive1 = + static_cast(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 (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); - } - else - { - player->viewz = player->mo->z + player->viewheight + bob; + bob = 0; } + 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(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 diff --git a/src/r_data.h b/src/r_data.h index 867cafffdf..721cd0fe5a 100644 --- a/src/r_data.h +++ b/src/r_data.h @@ -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 diff --git a/src/r_defs.h b/src/r_defs.h index e40f6493f1..992641cac7 100644 --- a/src/r_defs.h +++ b/src/r_defs.h @@ -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 diff --git a/src/r_draw.cpp b/src/r_draw.cpp index f99d7beb1d..9936b0eb70 100644 --- a/src/r_draw.cpp +++ b/src/r_draw.cpp @@ -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,67 +1614,68 @@ 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 (gameinfo.gametype == GAME_Doom || gameinfo.gametype == GAME_Strife) { - if (skin->game == GAME_Doom) + // Build player sprite translation + s -= 0.23f; + v += 0.1f; + sdelta = 0.23f / range; + vdelta = -0.94112f / range; + + for (i = start; i <= end; i++) { - // Build player sprite translation - s -= 0.23f; - v += 0.1f; - sdelta = 0.23f / range; - vdelta = -0.94112f / range; - - for (i = start; i <= end; i++) - { - float uses, usev; - uses = clamp (s, 0.f, 1.f); - usev = clamp (v, 0.f, 1.f); - HSVtoRGB (&r, &g, &b, h, uses, usev); - table[i] = ColorMatcher.Pick ( - clamp ((int)(r * 255.f), 0, 255), - clamp ((int)(g * 255.f), 0, 255), - clamp ((int)(b * 255.f), 0, 255)); - s += sdelta; - v += vdelta; - } + float uses, usev; + uses = clamp (s, 0.f, 1.f); + usev = clamp (v, 0.f, 1.f); + HSVtoRGB (&r, &g, &b, h, uses, usev); + table[i] = ColorMatcher.Pick ( + clamp ((int)(r * 255.f), 0, 255), + clamp ((int)(g * 255.f), 0, 255), + clamp ((int)(b * 255.f), 0, 255)); + s += sdelta; + v += vdelta; } - else - { // This is not Doom, so it must be Heretic - float vdelta = 0.418916f / range; + } + else if (gameinfo.gametype == GAME_Heretic) + { + float vdelta = 0.418916f / range; - // Build player sprite translation - for (i = start; i <= end; i++) - { - v = vdelta * (float)(i - start) + basev - 0.2352937f; - v = clamp (v, 0.f, 1.f); - HSVtoRGB (&r, &g, &b, h, s, v); - table[i] = ColorMatcher.Pick ( - clamp ((int)(r * 255.f), 0, 255), - clamp ((int)(g * 255.f), 0, 255), - clamp ((int)(b * 255.f), 0, 255)); - } + // Build player sprite translation + for (i = start; i <= end; i++) + { + v = vdelta * (float)(i - start) + basev - 0.2352937f; + v = clamp (v, 0.f, 1.f); + HSVtoRGB (&r, &g, &b, h, s, v); + table[i] = ColorMatcher.Pick ( + clamp ((int)(r * 255.f), 0, 255), + clamp ((int)(g * 255.f), 0, 255), + clamp ((int)(b * 255.f), 0, 255)); + } - // Build rain/lifegem translation - table = &translationtables[TRANSLATION_PlayersExtra][player*256]; + // Build rain/lifegem translation + 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) - { - fighter = players[player].mo->IsKindOf (RUNTIME_CLASS(AFighterPlayer)); - } - else - { - fighter = players[player].userinfo.PlayerClass == 0; - } - if (fighter) + else if (gameinfo.gametype == GAME_Hexen) + { + 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,24 +1729,46 @@ 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) { - const PalEntry *base = &GPalette.BaseColors[i]; - float dummy; + 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 ( - clamp ((int)(r * 255.f), 0, 255), - clamp ((int)(g * 255.f), 0, 255), - clamp ((int)(b * 255.f), 0, 255)); + 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); + 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) { int picnum; diff --git a/src/r_things.cpp b/src/r_things.cpp index 0ef4e1c4a6..47ee2bf95e 100644 --- a/src/r_things.cpp +++ b/src/r_things.cpp @@ -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,71 +608,119 @@ void R_InitSkins (void) } } - if (skins[i].name[0] == 0) - sprintf (skins[i].name, "skin%d", i); - - // Now collect the sprite frames for this skin. If the sprite name was not - // specified, use whatever immediately follows the specifier lump. - if (intname == 0) + // [GRB] Assume Doom skin by default + if (!remove && basetype == NULL) { - char name[9]; - Wads.GetLumpName (name, base+1); - intname = *(DWORD *)name; + 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; + } } - int basens = Wads.GetLumpNamespace(base); - - for(int spr = 0; spr<2; spr++) + if (!remove) { - memset (sprtemp, 0xFFFF, sizeof(sprtemp)); - for (k = 0; k < MAX_SPRITE_FRAMES; ++k) - { - sprtemp[k].Flip = 0; - } - maxframe = -1; + skins[i].range0start = transtype->Meta.GetMetaInt (APMETA_ColorRange) & 0xff; + skins[i].range0end = transtype->Meta.GetMetaInt (APMETA_ColorRange) >> 8; - if (spr == 1) + remove = true; + for (j = 0; j < PlayerClasses.Size (); j++) { - if (crouchname !=0 && crouchname != intname) + 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)) { - intname = crouchname; + PlayerClasses[j].Skins.Push ((int)i); + remove = false; } - else + } + } + + if (!remove) + { + if (skins[i].name[0] == 0) + sprintf (skins[i].name, "skin%d", i); + + // Now collect the sprite frames for this skin. If the sprite name was not + // specified, use whatever immediately follows the specifier lump. + if (intname == 0) + { + char name[9]; + Wads.GetLumpName (name, base+1); + intname = *(DWORD *)name; + } + + int basens = Wads.GetLumpNamespace(base); + + for(int spr = 0; spr<2; spr++) + { + memset (sprtemp, 0xFFFF, sizeof(sprtemp)); + for (k = 0; k < MAX_SPRITE_FRAMES; ++k) { - skins[i].crouchsprite = -1; + sprtemp[k].Flip = 0; + } + maxframe = -1; + + if (spr == 1) + { + if (crouchname !=0 && crouchname != intname) + { + intname = crouchname; + } + else + { + skins[i].crouchsprite = -1; + break; + } + } + + for (k = base + 1; Wads.GetLumpNamespace(k) == basens; k++) + { + char lname[9]; + Wads.GetLumpName (lname, k); + if (*(DWORD *)lname == intname) + { + int picnum = TexMan.CreateTexture(k, FTexture::TEX_SkinSprite); + R_InstallSpriteLump (picnum, lname[4] - 'A', lname[5], false); + + if (lname[6]) + R_InstallSpriteLump (picnum, lname[6] - 'A', lname[7], true); + } + } + + if (spr == 0 && maxframe <= 0) + { + Printf (PRINT_BOLD, "Skin %s (#%d) has no frames. Removing.\n", skins[i].name, i); + remove = true; break; } + + Wads.GetLumpName (temp.name, base+1); + temp.name[4] = 0; + int sprno = (int)sprites.Push (temp); + if (spr==0) skins[i].sprite = sprno; + else skins[i].crouchsprite = sprno; + R_InstallSprite (sprno); } + } - for (k = base + 1; Wads.GetLumpNamespace(k) == basens; k++) - { - char lname[9]; - Wads.GetLumpName (lname, k); - if (*(DWORD *)lname == intname) - { - int picnum = TexMan.CreateTexture(k, FTexture::TEX_SkinSprite); - R_InstallSpriteLump (picnum, lname[4] - 'A', lname[5], false); - - if (lname[6]) - R_InstallSpriteLump (picnum, lname[6] - 'A', lname[7], true); - } - } - - 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; - } - - Wads.GetLumpName (temp.name, base+1); - temp.name[4] = 0; - int sprno = (int)sprites.Push (temp); - if (spr==0) skins[i].sprite = sprno; - 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 @@ -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) - return mid; + { + if (PlayerClasses[pclass].CheckSkin (mid)) + return mid; + 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,14 +853,11 @@ 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) { - while ((lump = Wads.FindLump ("S_SKIN", &lastlump, true)) != -1) - { - numskins++; - } + numskins++; } // [RH] Do some preliminary setup @@ -774,44 +865,49 @@ void R_InitSprites () 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 - } + 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++) - { - if (memcmp (sprites[i].name, deh.PlayerSprite, 4) == 0) + 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) { - skins[0].sprite = (int)i; - break; + for (j = 0; j < sprites.Size (); j++) + { + 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() diff --git a/src/s_advsound.cpp b/src/s_advsound.cpp index c654375872..cb2455a394 100644 --- a/src/s_advsound.cpp +++ b/src/s_advsound.cpp @@ -221,7 +221,7 @@ static int NumPlayerReserves; static bool DoneReserving; static bool PlayerClassesIsSorted; -static TArray PlayerClasses; +static TArray PlayerClassLookups; static TArray 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); diff --git a/src/thingdef.cpp b/src/thingdef.cpp index 07ff284244..defa486998 100644 --- a/src/thingdef.cpp +++ b/src/thingdef.cpp @@ -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; - } + unsigned int index = actor->GetClass ()->Meta.GetMetaInt (ACMETA_DropItems) - 1; - 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) }, diff --git a/src/thingdef.h b/src/thingdef.h index df44ccc0c7..5f1f1b505a 100644 --- a/src/thingdef.h +++ b/src/thingdef.h @@ -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 diff --git a/src/thingdef_codeptr.cpp b/src/thingdef_codeptr.cpp index 5ee1040c1e..35d198e8c1 100644 --- a/src/thingdef_codeptr.cpp +++ b/src/thingdef_codeptr.cpp @@ -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]); + } +} diff --git a/src/version.h b/src/version.h index 218986ddbc..1692e52f49 100644 --- a/src/version.h +++ b/src/version.h @@ -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 diff --git a/src/w_wad.cpp b/src/w_wad.cpp index d677a9974d..b955fe50f9 100644 --- a/src/w_wad.cpp +++ b/src/w_wad.cpp @@ -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); - } + 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) diff --git a/src/w_wad.h b/src/w_wad.h index 05162ca404..04e3690e21 100644 --- a/src/w_wad.h +++ b/src/w_wad.h @@ -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; diff --git a/wadsrc/sndinfo.txt b/wadsrc/sndinfo.txt index 357cefed09..420a0cb4f5 100644 --- a/wadsrc/sndinfo.txt +++ b/wadsrc/sndinfo.txt @@ -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