Further morphing clean up

Players will now use their Alternative field to check if they're morphed instead of their MorphTics. This makes the current state of morphing more reliable, otherwise setting this to 0 manually without unmorphing could have very odd results. Both monsters and players consider 0 morph time to mean infinite now (previously this only applied to monsters). Player unmorphs no longer die in the case of a failed unmorph on death. Removed inventory swapping on player pointer substitution as it's too messy to do here.
This commit is contained in:
Boondorl 2024-01-01 11:50:01 -05:00 committed by Rachael Alexanderson
parent 30730647fe
commit 12dc5c1506
17 changed files with 37 additions and 45 deletions

View file

@ -479,7 +479,7 @@ FGameTexture *FMugShot::GetFace(player_t *player, const char *default_face, int
if (CurrentState != NULL)
{
int skin = player->userinfo.GetSkin();
const char *skin_face = (stateflags & FMugShot::CUSTOM) ? nullptr : (player->morphTics ? (GetDefaultByType(player->MorphedPlayerClass))->NameVar(NAME_Face).GetChars() : Skins[skin].Face.GetChars());
const char *skin_face = (stateflags & FMugShot::CUSTOM) ? nullptr : (player->mo->alternative != nullptr ? (GetDefaultByType(player->MorphedPlayerClass))->NameVar(NAME_Face).GetChars() : Skins[skin].Face.GetChars());
return CurrentState->GetCurrentFrameTexture(default_face, skin_face, level, angle);
}
return NULL;

View file

@ -145,7 +145,6 @@ xx(Reflection)
xx(CustomInventory)
xx(Inventory)
xx(StateProvider)
xx(ObtainInventory)
xx(CallTryPickup)
xx(QuestItem25)
xx(QuestItem28)

View file

@ -1149,7 +1149,7 @@ void FLevelLocals::UnSnapshotLevel(bool hubLoad)
// If this isn't the unmorphed original copy of a player, destroy it, because it's extra.
for (i = 0; i < MAXPLAYERS; ++i)
{
if (PlayerInGame(i) && Players[i]->morphTics && Players[i]->mo->alternative == pawn)
if (PlayerInGame(i) && Players[i]->mo->alternative == pawn)
{
break;
}

View file

@ -37,7 +37,7 @@ bool P_MorphActor(AActor *activator, AActor *victim, PClassActor *ptype, PClassA
bool P_UnmorphActor(AActor *activator, AActor *morphed, int flags, bool force)
{
IFVIRTUALPTR(morphed, AActor, UnMorph)
IFVIRTUALPTR(morphed, AActor, Unmorph)
{
VMValue params[] = { morphed, activator, flags, force };
int retval;

View file

@ -3115,7 +3115,7 @@ DEFINE_ACTION_FUNCTION(AActor, A_Pain)
PARAM_SELF_PROLOGUE(AActor);
// [RH] Vary player pain sounds depending on health (ala Quake2)
if (self->player && self->player->morphTics == 0)
if (self->player && self->alternative == nullptr)
{
const char *pain_amount;
FSoundID sfx_id = NO_SOUND;

View file

@ -329,8 +329,10 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags, FName MeansOf
VMValue params[] = { this };
VMCall(func, params, 1, nullptr, 0);
// Always kill the dummy Actor if it didn't unmorph, otherwise checking the morph flags.
if (realMo != nullptr && (!(morphStyle & MORPH_UNDOBYDEATH) || !(morphStyle & MORPH_UNDOBYDEATHSAVES)))
// Kill the dummy Actor if it didn't unmorph, otherwise checking the morph flags. Player pawns need
// to stay, otherwise they won't respawn correctly.
if (realMo != nullptr
&& ((alternative != nullptr && player == nullptr) || (alternative == nullptr && !(morphStyle & MORPH_UNDOBYDEATHSAVES))))
{
if (wasgibbed)
{
@ -460,7 +462,7 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags, FName MeansOf
++source->player->spreecount;
}
if (source->player->morphTics)
if (source->alternative != nullptr)
{ // Make a super chicken
source->GiveInventoryType (PClass::FindActor(NAME_PowerWeaponLevel2));
}
@ -1331,7 +1333,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
if (damage >= player->health && !telefragDamage
&& (G_SkillProperty(SKILLP_AutoUseHealth) || deathmatch)
&& !player->morphTics)
&& target->alternative == nullptr)
{ // Try to use some inventory health
P_AutoUseHealth (player, damage - player->health + 1);
}
@ -1465,7 +1467,7 @@ static int DamageMobj (AActor *target, AActor *inflictor, AActor *source, int da
// check for special fire damage or ice damage deaths
if (mod == NAME_Fire)
{
if (player && !player->morphTics)
if (player && target->alternative == nullptr)
{ // Check for flame death
if (!inflictor ||
((target->health > -50) && (damage > 25)) ||
@ -1799,7 +1801,7 @@ void P_PoisonDamage (player_t *player, AActor *source, int damage, bool playPain
}
if (damage >= player->health
&& (G_SkillProperty(SKILLP_AutoUseHealth) || deathmatch)
&& !player->morphTics)
&& target->alternative == nullptr)
{ // Try to use some inventory health
P_AutoUseHealth(player, damage - player->health+1);
}
@ -1829,7 +1831,7 @@ void P_PoisonDamage (player_t *player, AActor *source, int damage, bool playPain
else
{
target->special1 = damage;
if (player && !player->morphTics)
if (player && target->alternative == nullptr)
{ // Check for flame death
if ((player->poisontype == NAME_Fire) && (target->health > -50) && (damage > 25))
{

View file

@ -835,7 +835,7 @@ int P_GetRealMaxHealth(AActor *actor, int max)
{
max = actor->GetMaxHealth(true);
// [MH] First step in predictable generic morph effects
if (player->morphTics)
if (actor->alternative != nullptr)
{
if (player->MorphStyle & MORPH_FULLHEALTH)
{
@ -857,7 +857,7 @@ int P_GetRealMaxHealth(AActor *actor, int max)
else
{
// Bonus health should be added on top of the item's limit.
if (player->morphTics == 0 || (player->MorphStyle & MORPH_ADDSTAMINA))
if (actor->alternative == nullptr || (player->MorphStyle & MORPH_ADDSTAMINA))
{
max += actor->IntVar(NAME_BonusHealth);
}
@ -5225,14 +5225,6 @@ void PlayerPointerSubstitution(AActor* oldPlayer, AActor* newPlayer)
return;
}
// Swap over the inventory.
auto func = dyn_cast<PFunction>(newPlayer->GetClass()->FindSymbol(NAME_ObtainInventory, true));
if (func)
{
VMValue params[] = { newPlayer, oldPlayer };
VMCall(func->Variants[0].Implementation, params, 2, nullptr, 0);
}
// Go through player infos.
for (int i = 0; i < MAXPLAYERS; ++i)
{
@ -5314,11 +5306,10 @@ int MorphPointerSubstitution(AActor* from, AActor* to)
// Since the check is good, move the inventory items over. This should always be done when
// morphing to emulate Heretic/Hexen's behavior since those stored the inventory in their
// player structs.
auto func = dyn_cast<PFunction>(to->GetClass()->FindSymbol(NAME_ObtainInventory, true));
if (func)
IFVM(Actor, ObtainInventory)
{
VMValue params[] = { to, from };
VMCall(func->Variants[0].Implementation, params, 2, nullptr, 0);
VMCall(func, params, 2, nullptr, 0);
}
// Only change some gameplay-related pointers that we know we can safely swap to whatever

View file

@ -693,7 +693,7 @@ bool player_t::Resurrect()
P_BringUpWeapon(this);
}
if (morphTics)
if (mo->alternative != nullptr)
{
P_UnmorphActor(mo, mo);
}
@ -1172,7 +1172,7 @@ void P_CheckEnvironment(player_t *player)
P_PlayerOnSpecialFlat(player, P_GetThingFloorType(player->mo));
}
if (player->mo->Vel.Z <= -player->mo->FloatVar(NAME_FallingScreamMinSpeed) &&
player->mo->Vel.Z >= -player->mo->FloatVar(NAME_FallingScreamMaxSpeed) && !player->morphTics &&
player->mo->Vel.Z >= -player->mo->FloatVar(NAME_FallingScreamMaxSpeed) && player->mo->alternative == nullptr &&
player->mo->waterlevel == 0)
{
auto id = S_FindSkinnedSound(player->mo, S_FindSound("*falling"));

View file

@ -1406,7 +1406,7 @@ class Actor : Thinker native
bool grunted;
// [RH] only make noise if alive
if (self.health > 0 && self.player.morphTics == 0)
if (self.health > 0 && !Alternative)
{
grunted = false;
// Why should this number vary by gravity?

View file

@ -221,7 +221,7 @@ class ChickenPlayer : PlayerPawn
pspr.y = WEAPONTOP + player.chickenPeck / 2;
}
}
if (player.morphTics & 15)
if ((player.MorphTics ? player.MorphTics : Random[ChickenPlayerThink]()) & 15)
{
return;
}

View file

@ -145,7 +145,7 @@ class PigPlayer : PlayerPawn
override void MorphPlayerThink ()
{
if (player.morphTics & 15)
if ((player.MorphTics ? player.MorphTics : Random[PigPlayerThink]()) & 15)
{
return;
}

View file

@ -1933,7 +1933,7 @@ class PowerMorph : Powerup
EMorphFlags mStyle = MorphedPlayer ? MorphedPlayer.MorphStyle : MorphStyle;
Owner.Unmorph(Owner, 0, mStyle & MRF_UNDOALWAYS);
Owner.Unmorph(Owner, force: mStyle & MRF_UNDOALWAYS);
MorphedPlayer = null;
}
}

View file

@ -262,7 +262,7 @@ class Weapon : StateProvider
}
let psp = player.GetPSprite(PSP_WEAPON);
if (!psp) return;
if (player.morphTics || player.cheats & CF_INSTANTWEAPSWITCH)
if (Alternative || player.cheats & CF_INSTANTWEAPSWITCH)
{
psp.y = WEAPONBOTTOM;
}

View file

@ -94,7 +94,7 @@ extend class Actor
virtual bool CheckUnmorph()
{
return UnmorphTime <= Level.Time && Unmorph(self);
return UnmorphTime && UnmorphTime <= Level.Time && Unmorph(self, MRF_UNDOBYTIMEOUT);
}
//---------------------------------------------------------------------------

View file

@ -477,7 +477,7 @@ class PlayerPawn : Actor
let player = self.player;
if (!player) return;
if ((player.WeaponState & WF_DISABLESWITCH) || // Weapon changing has been disabled.
player.morphTics != 0) // Morphed classes cannot change weapons.
Alternative) // Morphed classes cannot change weapons.
{ // ...so throw away any pending weapon requests.
player.PendingWeapon = WP_NOCHANGE;
}
@ -651,7 +651,7 @@ class PlayerPawn : Actor
}
}
if (player.morphTics)
if (Alternative)
{
bob = 0;
}
@ -1072,7 +1072,7 @@ class PlayerPawn : Actor
virtual bool CanCrouch() const
{
return player.morphTics == 0 || bCrouchableMorph;
return !Alternative || bCrouchableMorph;
}
//----------------------------------------------------------------------------
@ -1238,7 +1238,7 @@ class PlayerPawn : Actor
side *= SideMove2;
}
if (!player.morphTics)
if (!Alternative)
{
double factor = 1.;
for(let it = Inv; it != null; it = it.Inv)
@ -1528,13 +1528,13 @@ class PlayerPawn : Actor
{
let player = self.player;
// Morph counter
if (player.morphTics)
if (Alternative)
{
if (player.chickenPeck)
{ // Chicken attack counter
player.chickenPeck -= 3;
}
if (!--player.morphTics)
if (player.MorphTics && !--player.MorphTics)
{ // Attempt to undo the chicken/pig
Unmorph(self, MRF_UNDOBYTIMEOUT);
}
@ -1661,7 +1661,7 @@ class PlayerPawn : Actor
player.jumpTics = 0;
}
}
if (player.morphTics && !(player.cheats & CF_PREDICTING))
if (Alternative && !(player.cheats & CF_PREDICTING))
{
MorphPlayerThink ();
}
@ -2042,9 +2042,9 @@ class PlayerPawn : Actor
Inventory item, next;
let p = player;
if (p.morphTics != 0)
if (Alternative)
{ // Undo morph
Unmorph(self, 0, true);
Unmorph(self, force: true);
}
// 'self' will be no longer valid from here on in case of an unmorph
let me = p.mo;

View file

@ -434,7 +434,7 @@ extend class PlayerPawn
int style = MRF_UNDOBYTOMEOFPOWER;
if (gameinfo.gametype == GAME_Hexen) style |= MRF_UNDOBYCHAOSDEVICE;
if (player.morphTics)
if (Alternative)
{
if (Unmorph (self))
{
@ -454,7 +454,7 @@ extend class PlayerPawn
virtual void CheatTakeWeaps()
{
if (player.morphTics || health <= 0)
if (Alternative || health <= 0)
{
return;
}

View file

@ -106,7 +106,7 @@ extend class PlayerPawn
if (!duration)
duration = DEFMORPHTICS;
if (player.MorphTics)
if (Alternative)
{
// Player is already a beast.
if (bCanSuperMorph && spawnType == GetClass()