Fixed inconsistencies between player and monster morphing

The Tome of Power and Chaos Device will now work on non-players. The STAYMORPHED flag will now work on players.
This commit is contained in:
Boondorl 2024-01-01 01:31:25 -05:00 committed by Rachael Alexanderson
parent 2c09a443b4
commit 30730647fe
5 changed files with 116 additions and 77 deletions

View file

@ -330,7 +330,7 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags, FName MeansOf
VMCall(func, params, 1, nullptr, 0);
// Always kill the dummy Actor if it didn't unmorph, otherwise checking the morph flags.
if (realMo != nullptr && (alternative != nullptr || !(morphStyle & MORPH_UNDOBYDEATHSAVES)))
if (realMo != nullptr && (!(morphStyle & MORPH_UNDOBYDEATH) || !(morphStyle & MORPH_UNDOBYDEATHSAVES)))
{
if (wasgibbed)
{

View file

@ -66,28 +66,26 @@ Class ArtiTomeOfPower : PowerupGiver
Loop;
}
override bool Use (bool pickup)
override bool Use(bool pickup)
{
Playerinfo p = Owner.player;
if (p && p.morphTics && (p.MorphStyle & MRF_UNDOBYTOMEOFPOWER))
{ // Attempt to undo chicken
if (!p.mo.Unmorph (p.mo, MRF_UNDOBYTOMEOFPOWER))
{ // Failed
if (!(p.MorphStyle & MRF_FAILNOTELEFRAG))
{
Owner.DamageMobj (null, null, TELEFRAG_DAMAGE, 'Telefrag');
}
EMorphFlags mStyle = Owner.player ? Owner.player.MorphStyle : Owner.MorphFlags;
if (Owner.Alternative && (mStyle & MRF_UNDOBYTOMEOFPOWER))
{
// Attempt to undo chicken.
if (!Owner.Unmorph(Owner, MRF_UNDOBYTOMEOFPOWER))
{
if (!(mStyle & MRF_FAILNOTELEFRAG))
Owner.DamageMobj(null, null, TELEFRAG_DAMAGE, 'Telefrag');
}
else
{ // Succeeded
Owner.A_StartSound ("*evillaugh", CHAN_VOICE);
else if (Owner.player)
{
Owner.A_StartSound("*evillaugh", CHAN_VOICE);
}
return true;
}
else
{
return Super.Use (pickup);
}
return Super.Use(pickup);
}
}

View file

@ -40,8 +40,9 @@ extend class Actor
// when a morphed Actor dies.
virtual Actor, int, int MorphedDeath()
{
if (MorphFlags & MRF_UNDOBYDEATH)
Unmorph(self, force: MorphFlags & MRF_UNDOBYDEATHFORCED);
EMorphFlags mStyle = player ? player.MorphStyle : MorphFlags;
if (mStyle & MRF_UNDOBYDEATH)
Unmorph(self, force: mStyle & MRF_UNDOBYDEATHFORCED);
return null, 0, 0;
}
@ -106,7 +107,7 @@ extend class Actor
virtual bool MorphMonster(class<Actor> spawnType, int duration, EMorphFlags style, class<Actor> enterFlash = "TeleportFog", class<Actor> exitFlash = "TeleportFog")
{
if (player || !spawnType || bDontMorph || !bIsMonster)
if (player || !bIsMonster || !spawnType || bDontMorph || Health <= 0 || spawnType == GetClass())
return false;
Actor morphed = Spawn(spawnType, Pos, ALLOW_REPLACE);
@ -130,13 +131,30 @@ extend class Actor
morphed.bShadow |= bShadow;
morphed.bGhost |= bGhost;
morphed.Score = Score;
morphed.ChangeTID(TID);
morphed.Special = Special;
for (int i; i < 5; ++i)
morphed.Args[i] = Args[i];
if (TID && (style & MRF_NEWTIDBEHAVIOUR))
{
morphed.ChangeTID(TID);
ChangeTID(0);
}
morphed.Tracer = Tracer;
morphed.Master = Master;
morphed.CopyFriendliness(self, true);
// Remove all armor.
for (Inventory item = morphed.Inv; item;)
{
Inventory next = item.Inv;
if (item is "Armor")
item.DepleteOrDestroy();
item = next;
}
morphed.UnmorphTime = Level.Time + (duration ? duration : DEFMORPHTICS) + Random[morphmonst]();
morphed.MorphFlags = style;
morphed.MorphExitFlash = exitFlash;
@ -147,10 +165,10 @@ extend class Actor
if (morphMon)
{
morphMon.UnmorphedMe = morphMon.Alternative;
morphMon.MorphStyle = morphMon.MorphFlags;
morphMon.FlagsSave = morphMon.PremorphProperties;
}
ChangeTID(0);
Special = 0;
bInvisible = true;
bSolid = bShootable = false;
@ -178,22 +196,23 @@ extend class Actor
virtual bool UndoMonsterMorph(bool force = false)
{
if (!UnmorphTime || !Alternative || bStayMorphed || Alternative.bStayMorphed)
if (!Alternative || bStayMorphed || Alternative.bStayMorphed)
return false;
Alternative.SetOrigin(Pos, false);
Actor alt = Alternative;
alt.SetOrigin(Pos, false);
if (!force && (PremorphProperties & MPROP_SOLID))
{
bool altSolid = Alternative.bSolid;
bool altSolid = alt.bSolid;
bool isSolid = bSolid;
bool isTouchy = bTouchy;
Alternative.bSolid = true;
alt.bSolid = true;
bSolid = bTouchy = false;
bool res = Alternative.TestMobjLocation();
bool res = alt.TestMobjLocation();
Alternative.bSolid = altSolid;
alt.bSolid = altSolid;
bSolid = isSolid;
bTouchy = isTouchy;
@ -205,44 +224,64 @@ extend class Actor
}
}
Actor unmorphed = Alternative;
if (!MorphInto(unmorphed))
if (!MorphInto(alt))
return false;
PreUnmorph(unmorphed, false);
unmorphed.PreUnmorph(self, true);
PreUnmorph(alt, false);
alt.PreUnmorph(self, true);
unmorphed.Angle = Angle;
unmorphed.bShadow = bShadow;
unmorphed.bGhost = bGhost;
unmorphed.bSolid = (PremorphProperties & MPROP_SOLID);
unmorphed.bShootable = (PremorphProperties & MPROP_SHOOTABLE);
unmorphed.bInvisible = (PremorphProperties & MPROP_INVIS);
unmorphed.Vel = Vel;
unmorphed.Score = Score;
// Check to see if it had a powerup that caused it to morph.
for (Inventory item = alt.Inv; item;)
{
Inventory next = item.Inv;
if (item is "PowerMorph")
item.Destroy();
unmorphed.ChangeTID(TID);
unmorphed.Special = Special;
item = next;
}
alt.Angle = Angle;
alt.bShadow = bShadow;
alt.bGhost = bGhost;
alt.bSolid = (PremorphProperties & MPROP_SOLID);
alt.bShootable = (PremorphProperties & MPROP_SHOOTABLE);
alt.bInvisible = (PremorphProperties & MPROP_INVIS);
alt.Vel = Vel;
alt.Score = Score;
if (TID && (MorphFlags & MRF_NEWTIDBEHAVIOUR))
{
alt.ChangeTID(TID);
ChangeTID(0);
}
alt.Special = Special;
for (int i; i < 5; ++i)
unmorphed.Args[i] = Args[i];
alt.Args[i] = Args[i];
unmorphed.CopyFriendliness(self, true);
alt.Tracer = Tracer;
alt.Master = Master;
alt.CopyFriendliness(self, true, false);
if (Health > 0 || (MorphFlags & MRF_UNDOBYDEATHSAVES))
alt.Health = alt.SpawnHealth();
else
alt.Health = Health;
ChangeTID(0);
Special = 0;
bInvisible = true;
bSolid = bShootable = false;
PostUnmorph(unmorphed, false); // From is false here: Leaving the caller's body.
unmorphed.PostUnmorph(self, true); // True here: Entering this body from here.
PostUnmorph(alt, false); // From is false here: Leaving the caller's body.
alt.PostUnmorph(self, true); // True here: Entering this body from here.
if (MorphExitFlash)
{
Actor fog = Spawn(MorphExitFlash, unmorphed.Pos.PlusZ(GameInfo.TELEFOGHEIGHT), ALLOW_REPLACE);
Actor fog = Spawn(MorphExitFlash, alt.Pos.PlusZ(GameInfo.TELEFOGHEIGHT), ALLOW_REPLACE);
if (fog)
fog.Target = unmorphed;
fog.Target = alt;
}
ClearCounters();
Destroy();
return true;
}

View file

@ -97,8 +97,11 @@ extend class PlayerPawn
virtual bool MorphPlayer(PlayerInfo activator, class<PlayerPawn> spawnType, int duration, EMorphFlags style, class<Actor> enterFlash = "TeleportFog", class<Actor> exitFlash = "TeleportFog")
{
if (!spawnType || bDontMorph || player.Health <= 0 || (bInvulnerable && (player != activator || !(style & MRF_WHENINVULNERABLE))))
if (!player || !spawnType || bDontMorph || player.Health <= 0
|| (bInvulnerable && (player != activator || !(style & MRF_WHENINVULNERABLE))))
{
return false;
}
if (!duration)
duration = DEFMORPHTICS;
@ -210,7 +213,7 @@ extend class PlayerPawn
virtual bool UndoPlayerMorph(PlayerInfo activator, EMorphFlags unmorphFlags = 0, bool force = false)
{
if (!Alternative)
if (!Alternative || bStayMorphed || Alternative.bStayMorphed)
return false;
if (bInvulnerable
@ -222,18 +225,20 @@ extend class PlayerPawn
let alt = PlayerPawn(Alternative);
alt.SetOrigin(Pos, false);
// Test if there's room to unmorph.
if (!force && (special2 & MPROP_SOLID))
if (!force && (PremorphProperties & MPROP_SOLID))
{
bool altSolid = alt.bSolid;
bool isSolid = bSolid;
bool isTouchy = bTouchy;
alt.bSolid = true;
bSolid = false;
bSolid = bTouchy = false;
bool res = alt.TestMobjLocation();
alt.bSolid = altSolid;
bSolid = isSolid;
bTouchy = isTouchy;
if (!res)
{

View file

@ -23,35 +23,32 @@ class ArtiTeleport : Inventory
Loop;
}
override bool Use (bool pickup)
override bool Use(bool pickup)
{
Vector3 dest;
int destAngle;
double destAngle;
if (deathmatch)
{
[dest, destAngle] = level.PickDeathmatchStart();
}
[dest, destAngle] = Level.PickDeathmatchStart();
else
{
[dest, destAngle] = level.PickPlayerStart(Owner.PlayerNumber());
}
if (!level.useplayerstartz)
[dest, destAngle] = Level.PickPlayerStart(Owner.PlayerNumber());
if (!Level.UsePlayerStartZ)
dest.Z = ONFLOORZ;
Owner.Teleport (dest, destAngle, TELF_SOURCEFOG | TELF_DESTFOG);
bool canlaugh = true;
Playerinfo p = Owner.player;
if (p && p.morphTics && (p.MorphStyle & MRF_UNDOBYCHAOSDEVICE))
{ // Teleporting away will undo any morph effects (pig)
if (!p.mo.Unmorph(p.mo, MRF_UNDOBYCHAOSDEVICE) && (p.MorphStyle & MRF_FAILNOLAUGH))
{
canlaugh = false;
}
}
if (canlaugh)
{ // Full volume laugh
Owner.A_StartSound ("*evillaugh", CHAN_VOICE, CHANF_DEFAULT, 1., ATTN_NONE);
Owner.Teleport(dest, destAngle, TELF_SOURCEFOG | TELF_DESTFOG);
bool canLaugh = Owner.player != null;
EMorphFlags mStyle = Owner.player ? Owner.player.MorphStyle : Owner.MorphFlags;
if (Owner.Alternative && (mStyle & MRF_UNDOBYCHAOSDEVICE))
{
// Teleporting away will undo any morph effects (pig).
if (!Owner.Unmorph(Owner, MRF_UNDOBYCHAOSDEVICE) && (mStyle & MRF_FAILNOLAUGH))
canLaugh = false;
}
if (canLaugh)
Owner.A_StartSound("*evillaugh", CHAN_VOICE, attenuation: ATTN_NONE);
return true;
}