From 6c64a4403cb322594e52ab30c89bd2a9edd6d4cb Mon Sep 17 00:00:00 2001 From: Boondorl Date: Sun, 14 Jan 2024 19:41:58 -0500 Subject: [PATCH] Improved ZScript interface for morphing Added getter and setter functions for handling whether or not the player fields should be gotten/set. Added MRF_KEEPARMOR flag to prevent stripping armor on morph. Optimized unmorphed Actor by setting it to NoInteraction and removing it from the blockmap and sector lists. --- .../actors/heretic/hereticartifacts.zs | 2 +- .../zscript/actors/inventory/powerups.zs | 6 +- wadsrc/static/zscript/actors/morph.zs | 108 ++++++++++++++---- .../zscript/actors/player/player_morph.zs | 52 +++++---- .../static/zscript/actors/raven/artitele.zs | 2 +- wadsrc/static/zscript/constants.zs | 1 + 6 files changed, 124 insertions(+), 47 deletions(-) diff --git a/wadsrc/static/zscript/actors/heretic/hereticartifacts.zs b/wadsrc/static/zscript/actors/heretic/hereticartifacts.zs index e52ca50418..b620b04b4f 100644 --- a/wadsrc/static/zscript/actors/heretic/hereticartifacts.zs +++ b/wadsrc/static/zscript/actors/heretic/hereticartifacts.zs @@ -68,7 +68,7 @@ Class ArtiTomeOfPower : PowerupGiver override bool Use(bool pickup) { - EMorphFlags mStyle = Owner.player ? Owner.player.MorphStyle : Owner.MorphFlags; + EMorphFlags mStyle = Owner.GetMorphStyle(); if (Owner.Alternative && (mStyle & MRF_UNDOBYTOMEOFPOWER)) { // Attempt to undo chicken. diff --git a/wadsrc/static/zscript/actors/inventory/powerups.zs b/wadsrc/static/zscript/actors/inventory/powerups.zs index da436dcdc7..76b0fea2d4 100644 --- a/wadsrc/static/zscript/actors/inventory/powerups.zs +++ b/wadsrc/static/zscript/actors/inventory/powerups.zs @@ -1928,12 +1928,10 @@ class PowerMorph : Powerup // Abort if owner is dead; their Die() method will // take care of any required unmorphing on death. - if (MorphedPlayer ? MorphedPlayer.Health <= 0 : Owner.Health <= 0) + if (Owner.player ? Owner.player.Health <= 0 : Owner.Health <= 0) return; - EMorphFlags mStyle = MorphedPlayer ? MorphedPlayer.MorphStyle : MorphStyle; - - Owner.Unmorph(Owner, force: mStyle & MRF_UNDOALWAYS); + Owner.Unmorph(Owner, force: Owner.GetMorphStyle() & MRF_UNDOALWAYS); MorphedPlayer = null; } } diff --git a/wadsrc/static/zscript/actors/morph.zs b/wadsrc/static/zscript/actors/morph.zs index 21b2767a90..187471b965 100644 --- a/wadsrc/static/zscript/actors/morph.zs +++ b/wadsrc/static/zscript/actors/morph.zs @@ -23,11 +23,16 @@ extend class Actor { + // Blockmap, sector, and no interaction are the only relevant flags but the old ones are kept around + // for legacy reasons. enum EPremorphProperty { - MPROP_SOLID = 1 << 1, - MPROP_SHOOTABLE = 1 << 2, - MPROP_INVIS = 1 << 6, + MPROP_SOLID = 1 << 1, + MPROP_SHOOTABLE = 1 << 2, + MPROP_NO_BLOCKMAP = 1 << 3, + MPROP_NO_SECTOR = 1 << 4, + MPROP_NO_INTERACTION = 1 << 5, + MPROP_INVIS = 1 << 6, } int UnmorphTime; @@ -35,6 +40,52 @@ extend class Actor class MorphExitFlash; EPremorphProperty PremorphProperties; + // Players still track these separately for legacy reasons. + void SetMorphStyle(EMorphFlags flags) + { + if (player) + player.MorphStyle = flags; + else + MorphFlags = flags; + } + + clearscope EMorphFlags GetMorphStyle() const + { + return player ? player.MorphStyle : MorphFlags; + } + + void SetMorphExitFlash(class flash) + { + if (player) + player.MorphExitFlash = flash; + else + MorphExitFlash = flash; + } + + clearscope class GetMorphExitFlash() const + { + return player ? player.MorphExitFlash : MorphExitFlash; + } + + void SetMorphTics(int dur) + { + if (player) + player.MorphTics = dur; + else + UnmorphTime = Level.Time + dur; + } + + clearscope int GetMorphTics() const + { + if (player) + return player.MorphTics; + + if (UnmorphTime <= 0) + return UnmorphTime; + + return UnmorphTime > Level.Time ? UnmorphTime - Level.Time : 0; + } + // This function doesn't return anything anymore since allowing so would be too volatile // for morphing management. Instead it's now a function that lets special actions occur // when a morphed Actor dies. @@ -113,8 +164,12 @@ extend class Actor Actor morphed = Spawn(spawnType, Pos, ALLOW_REPLACE); if (!MorphInto(morphed)) { - morphed.ClearCounters(); - morphed.Destroy(); + if (morphed) + { + morphed.ClearCounters(); + morphed.Destroy(); + } + return false; } @@ -146,30 +201,39 @@ extend class Actor morphed.CopyFriendliness(self, true); // Remove all armor. - for (Inventory item = morphed.Inv; item;) + if (!(style & MRF_KEEPARMOR)) { - Inventory next = item.Inv; - if (item is "Armor") - item.DepleteOrDestroy(); + for (Inventory item = morphed.Inv; item;) + { + Inventory next = item.Inv; + if (item is "Armor") + item.DepleteOrDestroy(); - item = next; + item = next; + } } - morphed.UnmorphTime = Level.Time + (duration ? duration : DEFMORPHTICS) + Random[morphmonst](); - morphed.MorphFlags = style; - morphed.MorphExitFlash = exitFlash; - morphed.PremorphProperties = (bSolid * MPROP_SOLID) | (bShootable * MPROP_SHOOTABLE) | (bInvisible * MPROP_INVIS); + morphed.SetMorphTics((duration ? duration : DEFMORPHTICS) + Random[morphmonst]()); + morphed.SetMorphStyle(style); + morphed.SetMorphExitFlash(exitFlash); + morphed.PremorphProperties = (bSolid * MPROP_SOLID) | (bShootable * MPROP_SHOOTABLE) + | (bNoBlockmap * MPROP_NO_BLOCKMAP) | (bNoSector * MPROP_NO_SECTOR) + | (bNoInteraction * MPROP_NO_INTERACTION) | (bInvisible * MPROP_INVIS); // This is just here for backwards compatibility as MorphedMonster used to be required. let morphMon = MorphedMonster(morphed); if (morphMon) { morphMon.UnmorphedMe = morphMon.Alternative; - morphMon.MorphStyle = morphMon.MorphFlags; + morphMon.MorphStyle = morphMon.GetMorphStyle(); morphMon.FlagsSave = morphMon.PremorphProperties; } Special = 0; + bNoInteraction = true; + A_ChangeLinkFlags(true, true); + + // Legacy bInvisible = true; bSolid = bShootable = false; @@ -219,7 +283,7 @@ extend class Actor // Didn't fit. if (!res) { - UnmorphTime = Level.Time + 5*TICRATE; // Next try in 5 seconds. + SetMorphTics(5 * TICRATE); return false; } } @@ -249,7 +313,11 @@ extend class Actor alt.Vel = Vel; alt.Score = Score; - if (TID && (MorphFlags & MRF_NEWTIDBEHAVIOUR)) + alt.bNoInteraction = (PremorphProperties & MPROP_NO_INTERACTION); + alt.A_ChangeLinkFlags((PremorphProperties & MPROP_NO_BLOCKMAP), (PremorphProperties & MPROP_NO_SECTOR)); + + EMorphFlags style = GetMorphStyle(); + if (TID && (style & MRF_NEWTIDBEHAVIOUR)) { alt.ChangeTID(TID); ChangeTID(0); @@ -262,14 +330,12 @@ extend class Actor alt.Tracer = Tracer; alt.Master = Master; alt.CopyFriendliness(self, true, false); - if (Health > 0 || (MorphFlags & MRF_UNDOBYDEATHSAVES)) + if (Health > 0 || (style & MRF_UNDOBYDEATHSAVES)) alt.Health = alt.SpawnHealth(); else alt.Health = Health; Special = 0; - bInvisible = true; - bSolid = bShootable = false; PostUnmorph(alt, false); // From is false here: Leaving the caller's body. alt.PostUnmorph(self, true); // True here: Entering this body from here. @@ -347,7 +413,7 @@ class MorphedMonster : Actor override bool UndoMonsterMorph(bool force) { Alternative = UnmorphedMe; - MorphFlags = MorphStyle; + SetMorphStyle(MorphStyle); PremorphProperties = FlagsSave; return super.UndoMonsterMorph(force); } diff --git a/wadsrc/static/zscript/actors/player/player_morph.zs b/wadsrc/static/zscript/actors/player/player_morph.zs index 594ddebfc0..581bd15368 100644 --- a/wadsrc/static/zscript/actors/player/player_morph.zs +++ b/wadsrc/static/zscript/actors/player/player_morph.zs @@ -110,7 +110,7 @@ extend class PlayerPawn { // Player is already a beast. if (bCanSuperMorph && spawnType == GetClass() - && player.MorphTics < duration - TICRATE + && GetMorphTics() < duration - TICRATE && !FindInventory("PowerWeaponLevel2", true)) { // Make a super chicken. @@ -126,7 +126,9 @@ extend class PlayerPawn let morphed = PlayerPawn(Spawn(spawnType, Pos, NO_REPLACE)); if (!MorphInto(morphed)) { - morphed.Destroy(); + if (morphed) + morphed.Destroy(); + return false; } @@ -155,29 +157,35 @@ extend class PlayerPawn } // special2 is no longer used here since Actors now have a proper field for it. - morphed.PremorphProperties = (bSolid * MPROP_SOLID) | (bShootable * MPROP_SHOOTABLE) | (bInvisible * MPROP_INVIS); + morphed.PremorphProperties = (bSolid * MPROP_SOLID) | (bShootable * MPROP_SHOOTABLE) + | (bNoBlockmap * MPROP_NO_BLOCKMAP) | (bNoSector * MPROP_NO_SECTOR) + | (bNoInteraction * MPROP_NO_INTERACTION) | (bInvisible * MPROP_INVIS); + morphed.bShadow |= bShadow; morphed.bNoGravity |= bNoGravity; morphed.bFly |= bFly; morphed.bGhost |= bGhost; // Remove all armor. - for (Inventory item = morphed.Inv; item;) + if (!(style & MRF_KEEPARMOR)) { - Inventory next = item.Inv; - if (item is "Armor") - item.DepleteOrDestroy(); + for (Inventory item = morphed.Inv; item;) + { + Inventory next = item.Inv; + if (item is "Armor") + item.DepleteOrDestroy(); - item = next; + item = next; + } } // Players store their morph behavior into their PlayerInfo unlike regular Actors which use the // morph properties. This is needed for backwards compatibility and to give the HUD info. let p = morphed.player; - p.MorphTics = duration; + morphed.SetMorphTics(duration); + morphed.SetMorphStyle(style); + morphed.SetMorphExitFlash(exitFlash); p.MorphedPlayerClass = spawnType; - p.MorphStyle = style; - p.MorphExitFlash = exitFlash; p.PremorphWeapon = p.ReadyWeapon; p.Health = morphed.Health; p.Vel = (0.0, 0.0); @@ -185,6 +193,10 @@ extend class PlayerPawn if (morphed.ViewHeight > p.ViewHeight && !p.DeltaViewHeight) p.DeltaViewHeight = p.GetDeltaViewHeight(); + bNoInteraction = true; + A_ChangeLinkFlags(true, true); + + // Legacy bSolid = bShootable = false; bInvisible = true; @@ -242,7 +254,7 @@ extend class PlayerPawn if (!res) { - player.MorphTics = 2 * TICRATE; + SetMorphTics(2 * TICRATE); return false; } } @@ -284,9 +296,12 @@ extend class PlayerPawn alt.bFly = bFly; alt.Vel = (0.0, 0.0, Vel.Z); + alt.bNoInteraction = (PremorphProperties & MPROP_NO_INTERACTION); + alt.A_ChangeLinkFlags((PremorphProperties & MPROP_NO_BLOCKMAP), (PremorphProperties & MPROP_NO_SECTOR)); + let p = alt.player; - class exitFlash = p.MorphExitFlash; - EMorphFlags style = p.MorphStyle; + class exitFlash = alt.GetMorphExitFlash(); + EMorphFlags style = alt.GetMorphStyle(); Weapon premorphWeap = p.PremorphWeapon; if (TID && (style & MRF_NEWTIDBEHAVIOUR)) @@ -295,10 +310,10 @@ extend class PlayerPawn ChangeTID(0); } - p.MorphTics = 0; + alt.SetMorphTics(0); + alt.SetMorphStyle(0); + alt.SetMorphExitFlash(null); p.MorphedPlayerClass = null; - p.MorphStyle = 0; - p.MorphExitFlash = null; p.PremorphWeapon = null; p.ViewHeight = alt.ViewHeight; p.Vel = (0.0, 0.0); @@ -347,9 +362,6 @@ extend class PlayerPawn alt.ClearFOVInterpolation(); alt.InitAllPowerupEffects(); - bInvisible = true; - bSolid = bShootable = false; - PostUnmorph(alt, false); // This body is no longer current. alt.PostUnmorph(self, true); // altmo body is current. diff --git a/wadsrc/static/zscript/actors/raven/artitele.zs b/wadsrc/static/zscript/actors/raven/artitele.zs index fcbb499c86..272ae86029 100644 --- a/wadsrc/static/zscript/actors/raven/artitele.zs +++ b/wadsrc/static/zscript/actors/raven/artitele.zs @@ -38,7 +38,7 @@ class ArtiTeleport : Inventory Owner.Teleport(dest, destAngle, TELF_SOURCEFOG | TELF_DESTFOG); bool canLaugh = Owner.player != null; - EMorphFlags mStyle = Owner.player ? Owner.player.MorphStyle : Owner.MorphFlags; + EMorphFlags mStyle = Owner.GetMorphStyle(); if (Owner.Alternative && (mStyle & MRF_UNDOBYCHAOSDEVICE)) { // Teleporting away will undo any morph effects (pig). diff --git a/wadsrc/static/zscript/constants.zs b/wadsrc/static/zscript/constants.zs index 71b0fb99df..e2178de505 100644 --- a/wadsrc/static/zscript/constants.zs +++ b/wadsrc/static/zscript/constants.zs @@ -232,6 +232,7 @@ enum EMorphFlags MRF_UNDOBYTIMEOUT = 0x00001000, MRF_UNDOALWAYS = 0x00002000, MRF_TRANSFERTRANSLATION = 0x00004000, + MRF_KEEPARMOR = 0x00008000, MRF_STANDARDUNDOING = MRF_UNDOBYTOMEOFPOWER | MRF_UNDOBYCHAOSDEVICE | MRF_UNDOBYTIMEOUT, };