From cce1bad04214bc54222c5a07ec2a94da82269fd0 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sat, 24 Nov 2018 08:39:35 +0100 Subject: [PATCH] - testing and cleanup of scripted morph code. --- src/d_net.cpp | 4 +- src/d_player.h | 2 +- src/g_shared/a_morph.cpp | 372 +----------------- src/m_cheat.cpp | 45 +-- src/m_cheat.h | 2 +- wadsrc/static/zscript/inventory/powerups.txt | 4 +- wadsrc/static/zscript/raven/artitele.txt | 2 +- wadsrc/static/zscript/shared/morph.txt | 13 +- wadsrc/static/zscript/shared/player.txt | 8 +- wadsrc/static/zscript/shared/player_cheat.txt | 27 ++ 10 files changed, 80 insertions(+), 399 deletions(-) diff --git a/src/d_net.cpp b/src/d_net.cpp index 85a093cb83..37969fa1de 100644 --- a/src/d_net.cpp +++ b/src/d_net.cpp @@ -2536,10 +2536,10 @@ void Net_DoCommand (int type, uint8_t **stream, int player) case DEM_MORPHEX: { s = ReadString (stream); - const char *msg = cht_Morph (players + player, PClass::FindActor (s), false); + FString msg = cht_Morph (players + player, PClass::FindActor (s), false); if (player == consoleplayer) { - Printf ("%s\n", *msg != '\0' ? msg : "Morph failed."); + Printf ("%s\n", msg[0] != '\0' ? msg.GetChars() : "Morph failed."); } } break; diff --git a/src/d_player.h b/src/d_player.h index 9341310e69..009af57fa1 100644 --- a/src/d_player.h +++ b/src/d_player.h @@ -445,7 +445,7 @@ public: int morphTics = 0; // player is a chicken/pig if > 0 PClassActor *MorphedPlayerClass = nullptr; // [MH] (for SBARINFO) class # for this player instance when morphed int MorphStyle = 0; // which effects to apply for this player instance when morphed - PClassActor *MorphExitFlash = nullptr; // flash to apply when demorphing (cache of value given to P_MorphPlayer) + PClassActor *MorphExitFlash = nullptr; // flash to apply when demorphing (cache of value given to MorphPlayer) TObjPtr PremorphWeapon = nullptr; // ready weapon before morphing int chickenPeck = 0; // chicken peck countdown int jumpTics = 0; // delay the next jump for a moment diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index a5216a226d..7cc62b0ebf 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -41,368 +41,32 @@ static FRandom pr_morphmonst ("MorphMonster"); bool P_MorphPlayer(player_t *activator, player_t *p, PClassActor *spawntype, int duration, int style, PClassActor *enter_flash, PClassActor *exit_flash) { + if (!p->mo) return false; + IFVIRTUALPTR(p->mo, APlayerPawn, MorphPlayer) + { + VMValue params[] = { p->mo, activator, spawntype, duration, style, enter_flash, exit_flash }; + int retval; + VMReturn ret(&retval); + VMCall(func, params, countof(params), &ret, 1); + return !!retval; + } return false; } bool P_UndoPlayerMorph(player_t *activator, player_t *player, int unmorphflag, bool force) { + if (!player->mo) return false; + IFVIRTUALPTR(player->mo, APlayerPawn, MorphPlayer) + { + VMValue params[] = { player->mo, activator, unmorphflag, force }; + int retval; + VMReturn ret(&retval); + VMCall(func, params, countof(params), &ret, 1); + return !!retval; + } return false; } -#if 0 -void EndAllPowerupEffects(AInventory *item); -void InitAllPowerupEffects(AInventory *item); - -//--------------------------------------------------------------------------- -// -// FUNC P_MorphPlayer -// -// Returns true if the player gets turned into a chicken/pig. -// -// TODO: Allow morphed players to receive weapon sets (not just one weapon), -// since they have their own weapon slots now. -// -//--------------------------------------------------------------------------- - -bool P_MorphPlayer (player_t *activator, player_t *p, PClassActor *spawntype, int duration, int style, PClassActor *enter_flash, PClassActor *exit_flash) -{ - AInventory *item; - APlayerPawn *morphed; - APlayerPawn *actor; - - actor = p->mo; - if (actor == nullptr) - { - return false; - } - if (actor->flags3 & MF3_DONTMORPH) - { - return false; - } - if ((p->mo->flags2 & MF2_INVULNERABLE) && ((p != activator) || (!(style & MORPH_WHENINVULNERABLE)))) - { // Immune when invulnerable unless this is a power we activated - return false; - } - if (p->morphTics) - { // Player is already a beast - if ((p->mo->GetClass() == spawntype) - && (p->mo->PlayerFlags & PPF_CANSUPERMORPH) - && (p->morphTics < (((duration) ? duration : MORPHTICS) - TICRATE)) - && (p->mo->FindInventory (PClass::FindActor(NAME_PowerWeaponLevel2), true) == nullptr)) - { // Make a super chicken - p->mo->GiveInventoryType (PClass::FindActor(NAME_PowerWeaponLevel2)); - } - return false; - } - if (p->health <= 0) - { // Dead players cannot morph - return false; - } - if (spawntype == nullptr) - { - return false; - } - if (!spawntype->IsDescendantOf (RUNTIME_CLASS(APlayerPawn))) - { - return false; - } - if (spawntype == p->mo->GetClass()) - { - return false; - } - - morphed = static_cast(Spawn (spawntype, actor->Pos(), NO_REPLACE)); - EndAllPowerupEffects(actor->Inventory); - DObject::StaticPointerSubstitution (actor, morphed); - if ((style & MORPH_TRANSFERTRANSLATION) && !(morphed->flags2 & MF2_DONTTRANSLATE)) - { - morphed->Translation = actor->Translation; - } - if ((actor->tid != 0) && (style & MORPH_NEWTIDBEHAVIOUR)) - { - morphed->tid = actor->tid; - morphed->AddToHash (); - actor->RemoveFromHash (); - actor->tid = 0; - } - morphed->Angles.Yaw = actor->Angles.Yaw; - morphed->target = actor->target; - morphed->tracer = actor->tracer; - morphed->alternative = actor; - morphed->FriendPlayer = actor->FriendPlayer; - morphed->DesignatedTeam = actor->DesignatedTeam; - morphed->Score = actor->Score; - p->PremorphWeapon = p->ReadyWeapon; - morphed->special2 = actor->flags & ~MF_JUSTHIT; - morphed->player = p; - if (actor->renderflags & RF_INVISIBLE) - { - morphed->special2 |= MF_JUSTHIT; - } - if (morphed->ViewHeight > p->viewheight && p->deltaviewheight == 0) - { // If the new view height is higher than the old one, start moving toward it. - p->deltaviewheight = p->GetDeltaViewHeight(); - } - morphed->flags |= actor->flags & (MF_SHADOW|MF_NOGRAVITY); - morphed->flags2 |= actor->flags2 & MF2_FLY; - morphed->flags3 |= actor->flags3 & MF3_GHOST; - AActor *eflash = Spawn(((enter_flash) ? enter_flash : PClass::FindActor("TeleportFog")), actor->PosPlusZ(TELEFOGHEIGHT), ALLOW_REPLACE); - actor->player = nullptr; - actor->alternative = morphed; - actor->flags &= ~(MF_SOLID|MF_SHOOTABLE); - actor->flags |= MF_UNMORPHED; - actor->renderflags |= RF_INVISIBLE; - p->morphTics = (duration) ? duration : MORPHTICS; - - // [MH] Used by SBARINFO to speed up face drawing - p->MorphedPlayerClass = spawntype; - - p->MorphStyle = style; - p->MorphExitFlash = (exit_flash) ? exit_flash : PClass::FindActor("TeleportFog"); - p->health = morphed->health; - p->mo = morphed; - p->Vel.X = p->Vel.Y = 0; - morphed->ObtainInventory (actor); - // Remove all armor - for (item = morphed->Inventory; item != nullptr; ) - { - AInventory *next = item->Inventory; - if (item->IsKindOf (PClass::FindActor(NAME_Armor))) - { - item->DepleteOrDestroy(); - } - item = next; - } - InitAllPowerupEffects(morphed->Inventory); - morphed->ActivateMorphWeapon (); - if (p->camera == actor) - { - p->camera = morphed; - } - morphed->ScoreIcon = actor->ScoreIcon; // [GRB] - if (eflash) - eflash->target = p->mo; - return true; -} - -//---------------------------------------------------------------------------- -// -// FUNC P_UndoPlayerMorph -// -//---------------------------------------------------------------------------- - -bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag, bool force) -{ - AWeapon *beastweap; - APlayerPawn *mo; - APlayerPawn *pmo; - - pmo = player->mo; - // [MH] - // Checks pmo as well; the PowerMorph destroyer will - // try to unmorph the player; if the destroyer runs - // because the level or game is ended while morphed, - // by the time it gets executed the morphed player - // pawn instance may have already been destroyed. - if (pmo == nullptr || pmo->alternative == nullptr) - { - return false; - } - - bool DeliberateUnmorphIsOkay = !!(MORPH_STANDARDUNDOING & unmorphflag); - - if ((pmo->flags2 & MF2_INVULNERABLE) // If the player is invulnerable - && ((player != activator) // and either did not decide to unmorph, - || (!((player->MorphStyle & MORPH_WHENINVULNERABLE) // or the morph style does not allow it - || (DeliberateUnmorphIsOkay))))) // (but standard morph styles always allow it), - { // Then the player is immune to the unmorph. - return false; - } - - mo = barrier_cast(pmo->alternative); - mo->SetOrigin (pmo->Pos(), false); - mo->flags |= MF_SOLID; - pmo->flags &= ~MF_SOLID; - if (!force && !P_TestMobjLocation (mo)) - { // Didn't fit - mo->flags &= ~MF_SOLID; - pmo->flags |= MF_SOLID; - player->morphTics = 2*TICRATE; - return false; - } - // No longer using tracer as morph storage. That is what 'alternative' is for. - // If the tracer has changed on the morph, change the original too. - mo->target = pmo->target; - mo->tracer = pmo->tracer; - pmo->player = nullptr; - mo->alternative = pmo->alternative = nullptr; - - // Remove the morph power if the morph is being undone prematurely. - auto pmtype = PClass::FindActor("PowerMorph"); - for (AInventory *item = pmo->Inventory, *next = nullptr; item != nullptr; item = next) - { - next = item->Inventory; - if (item->IsKindOf(pmtype)) - { - item->Destroy(); - } - } - EndAllPowerupEffects(pmo->Inventory); - mo->ObtainInventory (pmo); - DObject::StaticPointerSubstitution (pmo, mo); - if ((pmo->tid != 0) && (player->MorphStyle & MORPH_NEWTIDBEHAVIOUR)) - { - mo->tid = pmo->tid; - mo->AddToHash (); - } - mo->Angles.Yaw = pmo->Angles.Yaw; - mo->player = player; - mo->reactiontime = 18; - mo->flags = ActorFlags::FromInt (pmo->special2) & ~MF_JUSTHIT; - mo->Vel.X = mo->Vel.Y = 0; - player->Vel.Zero(); - mo->Vel.Z = pmo->Vel.Z; - mo->floorz = pmo->floorz; - if (!(pmo->special2 & MF_JUSTHIT)) - { - mo->renderflags &= ~RF_INVISIBLE; - } - mo->flags = (mo->flags & ~(MF_SHADOW|MF_NOGRAVITY)) | (pmo->flags & (MF_SHADOW|MF_NOGRAVITY)); - mo->flags2 = (mo->flags2 & ~MF2_FLY) | (pmo->flags2 & MF2_FLY); - mo->flags3 = (mo->flags3 & ~MF3_GHOST) | (pmo->flags3 & MF3_GHOST); - mo->Score = pmo->Score; - InitAllPowerupEffects(mo->Inventory); - - PClassActor *exit_flash = player->MorphExitFlash; - bool correctweapon = !!(player->MorphStyle & MORPH_LOSEACTUALWEAPON); - bool undobydeathsaves = !!(player->MorphStyle & MORPH_UNDOBYDEATHSAVES); - - player->morphTics = 0; - player->MorphedPlayerClass = 0; - player->MorphStyle = 0; - player->MorphExitFlash = nullptr; - player->viewheight = mo->ViewHeight; - AInventory *level2 = mo->FindInventory (PClass::FindActor(NAME_PowerWeaponLevel2), true); - if (level2 != nullptr) - { - level2->Destroy (); - } - - if ((player->health > 0) || undobydeathsaves) - { - player->health = mo->health = mo->SpawnHealth(); - } - else // killed when morphed so stay dead - { - mo->health = player->health; - } - - player->mo = mo; - if (player->camera == pmo) - { - player->camera = mo; - } - - // [MH] - // If the player that was morphed is the one - // taking events, reset up the face, if any; - // this is only needed for old-skool skins - // and for the original DOOM status bar. - if (player == &players[consoleplayer]) - { - FName face = pmo->Face; - if (face != NAME_None) - { - // Assume root-level base skin to begin with - size_t skinindex = 0; - // If a custom skin was in use, then reload it - // or else the base skin for the player class. - if ((unsigned int)player->userinfo.GetSkin() >= PlayerClasses.Size () && - (unsigned)player->userinfo.GetSkin() < Skins.Size()) - { - - skinindex = player->userinfo.GetSkin(); - } - else if (PlayerClasses.Size () > 1) - { - const PClass *whatami = player->mo->GetClass(); - for (unsigned int i = 0; i < PlayerClasses.Size (); ++i) - { - if (PlayerClasses[i].Type == whatami) - { - skinindex = i; - break; - } - } - } - } - } - - AActor *eflash = nullptr; - if (exit_flash != nullptr) - { - eflash = Spawn(exit_flash, pmo->Vec3Angle(20., mo->Angles.Yaw, TELEFOGHEIGHT), ALLOW_REPLACE); - if (eflash) eflash->target = mo; - } - mo->SetupWeaponSlots(); // Use original class's weapon slots. - beastweap = player->ReadyWeapon; - if (player->PremorphWeapon != nullptr) - { - player->PremorphWeapon-> P ostMorphWeapon (); - } - else - { - player->ReadyWeapon = player->PendingWeapon = nullptr; - } - if (correctweapon) - { // Better "lose morphed weapon" semantics - PClassActor *morphweapon = PClass::FindActor(pmo->MorphWeapon); - if (morphweapon != nullptr && morphweapon->IsDescendantOf(NAME_Weapon)) - { - AWeapon *OriginalMorphWeapon = static_cast(mo->FindInventory (morphweapon)); - if ((OriginalMorphWeapon != nullptr) && (OriginalMorphWeapon->GivenAsMorphWeapon)) - { // You don't get to keep your morphed weapon. - if (OriginalMorphWeapon->SisterWeapon != nullptr) - { - OriginalMorphWeapon->SisterWeapon->Destroy (); - } - OriginalMorphWeapon->Destroy (); - } - } - } - else // old behaviour (not really useful now) - { // Assumptions made here are no longer valid - if (beastweap != nullptr) - { // You don't get to keep your morphed weapon. - if (beastweap->SisterWeapon != nullptr) - { - beastweap->SisterWeapon->Destroy (); - } - beastweap->Destroy (); - } - } - pmo->Destroy (); - // Restore playerclass armor to its normal amount. - auto hxarmor = mo->FindInventory(NAME_HexenArmor); - if (hxarmor != nullptr) - { - double *Slots = (double*)hxarmor->ScriptVar(NAME_Slots, nullptr); - Slots[4] = mo->HexenArmor[0]; - } - return true; -} - -DEFINE_ACTION_FUNCTION(_PlayerInfo, UndoPlayerMorph) -{ - PARAM_SELF_STRUCT_PROLOGUE(player_t); - PARAM_POINTER_NOT_NULL(player, player_t); - PARAM_INT(unmorphflag); - PARAM_BOOL(force); - ACTION_RETURN_BOOL(P_UndoPlayerMorph(player, self, unmorphflag, force)); -} - -#endif - //--------------------------------------------------------------------------- // // FUNC P_MorphMonster diff --git a/src/m_cheat.cpp b/src/m_cheat.cpp index 768802693e..b64d047594 100644 --- a/src/m_cheat.cpp +++ b/src/m_cheat.cpp @@ -90,10 +90,17 @@ void cht_DoCheat (player_t *player, int cheat) }; PClassActor *type; AInventory *item; + FString smsg; const char *msg = ""; char msgbuild[32]; int i; + // No cheating when not having a pawn attached. + if (player->mo == nullptr) + { + return; + } + switch (cheat) { case CHT_IDDQD: @@ -188,7 +195,8 @@ void cht_DoCheat (player_t *player, int cheat) break; case CHT_MORPH: - msg = cht_Morph (player, PClass::FindActor (gameinfo.gametype == GAME_Heretic ? NAME_ChickenPlayer : NAME_PigPlayer), true); + smsg = cht_Morph (player, PClass::FindActor (gameinfo.gametype == GAME_Heretic ? NAME_ChickenPlayer : NAME_PigPlayer), true); + msg = smsg.GetChars(); break; case CHT_NOTARGET: @@ -546,32 +554,17 @@ void cht_DoCheat (player_t *player, int cheat) Printf ("%s cheats: %s\n", player->userinfo.GetName(), msg); } -const char *cht_Morph (player_t *player, PClassActor *morphclass, bool quickundo) +FString cht_Morph(player_t *player, PClassActor *morphclass, bool quickundo) { - if (player->mo == NULL) - { - return ""; - } - auto oldclass = player->mo->GetClass(); + if (player->mo == nullptr) return ""; - // Set the standard morph style for the current game - int style = MORPH_UNDOBYTOMEOFPOWER; - if (gameinfo.gametype == GAME_Hexen) style |= MORPH_UNDOBYCHAOSDEVICE; - - if (player->morphTics) + IFVIRTUALPTR(player->mo, APlayerPawn, CheatMorph) { - if (P_UndoPlayerMorph (player, player)) - { - if (!quickundo && oldclass != morphclass && P_MorphPlayer (player, player, morphclass, 0, style)) - { - return GStrings("TXT_STRANGER"); - } - return GStrings("TXT_NOTSTRANGE"); - } - } - else if (P_MorphPlayer (player, player, morphclass, 0, style)) - { - return GStrings("TXT_STRANGE"); + FString message; + VMReturn msgret(&message); + VMValue params[3] = { player->mo, morphclass, quickundo }; + VMCall(func, params, 3, nullptr, 0); + return message; } return ""; } @@ -602,8 +595,6 @@ void cht_SetInv(player_t *player, const char *string, int amount, bool beyond) void cht_Give (player_t *player, const char *name, int amount) { - if (player->mo == nullptr) return; - IFVIRTUALPTR(player->mo, APlayerPawn, CheatGive) { FString namestr = name; @@ -614,8 +605,6 @@ void cht_Give (player_t *player, const char *name, int amount) void cht_Take (player_t *player, const char *name, int amount) { - if (player->mo == nullptr) return; - IFVIRTUALPTR(player->mo, APlayerPawn, CheatTake) { FString namestr = name; diff --git a/src/m_cheat.h b/src/m_cheat.h index c6ebcb1a16..6701bddc47 100644 --- a/src/m_cheat.h +++ b/src/m_cheat.h @@ -15,6 +15,6 @@ void cht_Give (player_t *player, const char *item, int amount=1); void cht_Take (player_t *player, const char *item, int amount=1); void cht_SetInv(player_t *player, const char *item, int amount = 1, bool beyondMax = false); void cht_Suicide (player_t *player); -const char *cht_Morph (player_t *player, PClassActor *morphclass, bool quickundo); +FString cht_Morph (player_t *player, PClassActor *morphclass, bool quickundo); #endif diff --git a/wadsrc/static/zscript/inventory/powerups.txt b/wadsrc/static/zscript/inventory/powerups.txt index 5ef03d4209..bcfdca31a7 100644 --- a/wadsrc/static/zscript/inventory/powerups.txt +++ b/wadsrc/static/zscript/inventory/powerups.txt @@ -1893,7 +1893,7 @@ class PowerMorph : Powerup if (Owner != null && Owner.player != null && PlayerClass != null) { let realplayer = Owner.player; // Remember the identity of the player - if (realplayer.MorphPlayer(realplayer, PlayerClass, 0x7fffffff/*INDEFINITELY*/, MorphStyle, MorphFlash, UnMorphFlash)) + if (realplayer.mo.MorphPlayer(realplayer, PlayerClass, 0x7fffffff/*INDEFINITELY*/, MorphStyle, MorphFlash, UnMorphFlash)) { Owner = realplayer.mo; // Replace the new owner in our owner; safe because we are not attached to anything yet bCreateCopyMoved = true; // Let the caller know the "real" owner has changed (to the morphed actor) @@ -1930,7 +1930,7 @@ class PowerMorph : Powerup } int savedMorphTics = MorphedPlayer.morphTics; - MorphedPlayer.UndoPlayerMorph (MorphedPlayer, 0, !!(MorphedPlayer.MorphStyle & MRF_UNDOALWAYS)); + MorphedPlayer.mo.UndoPlayerMorph (MorphedPlayer, 0, !!(MorphedPlayer.MorphStyle & MRF_UNDOALWAYS)); MorphedPlayer = null; } diff --git a/wadsrc/static/zscript/raven/artitele.txt b/wadsrc/static/zscript/raven/artitele.txt index 77a233dbe5..0e65fe051f 100644 --- a/wadsrc/static/zscript/raven/artitele.txt +++ b/wadsrc/static/zscript/raven/artitele.txt @@ -42,7 +42,7 @@ class ArtiTeleport : Inventory Playerinfo p = Owner.player; if (p && p.morphTics && (p.MorphStyle & MRF_UNDOBYCHAOSDEVICE)) { // Teleporting away will undo any morph effects (pig) - if (!p.UndoPlayerMorph (p, MRF_UNDOBYCHAOSDEVICE) && (p.MorphStyle & MRF_FAILNOLAUGH)) + if (!p.mo.UndoPlayerMorph (p, MRF_UNDOBYCHAOSDEVICE) && (p.MorphStyle & MRF_FAILNOLAUGH)) { canlaugh = false; } diff --git a/wadsrc/static/zscript/shared/morph.txt b/wadsrc/static/zscript/shared/morph.txt index 137fd9c0eb..e643623fa2 100644 --- a/wadsrc/static/zscript/shared/morph.txt +++ b/wadsrc/static/zscript/shared/morph.txt @@ -72,7 +72,7 @@ extend class PlayerPawn player.ReadyWeapon = Weapon(GiveInventoryType (morphweaponcls)); if (player.ReadyWeapon != null) { - player.ReadyWeapon.GivenAsMorphWeapon = true; // flag is used only by new beastweap semantics in P_UndoPlayerMorph + player.ReadyWeapon.GivenAsMorphWeapon = true; // flag is used only by new beastweap semantics in UndoPlayerMorph } } if (player.ReadyWeapon != null) @@ -91,7 +91,7 @@ extend class PlayerPawn //--------------------------------------------------------------------------- // - // FUNC P_MorphPlayer + // MorphPlayer // // Returns true if the player gets turned into a chicken/pig. // @@ -216,7 +216,7 @@ extend class PlayerPawn //---------------------------------------------------------------------------- // - // FUNC P_UndoPlayerMorph + // FUNC UndoPlayerMorph // //---------------------------------------------------------------------------- @@ -279,13 +279,13 @@ extend class PlayerPawn altmo.bSolid = !!(special2 & 2); altmo.bShootable = !!(special2 & 4); altmo.bInvisible = !!(special2 & 0x40); - altmo.Vel.XY = (0, 0); + altmo.Vel = (0, 0, Vel.Z); player.Vel = (0, 0); - altmo.Vel.Z = Vel.Z; altmo.floorz = floorz; altmo.bShadow = bShadow; altmo.bNoGravity = bNoGravity; altmo.bGhost = bGhost; + altmo.bUnmorphed = false; altmo.Score = Score; altmo.InitAllPowerupEffects(); @@ -427,7 +427,8 @@ class MorphProjectile : Actor { if (target.player) { - target.player.MorphPlayer (NULL, PlayerClass, Duration, MorphStyle, MorphFlash, UnMorphFlash); + // Voodoo dolls forward this to the real player + target.player.mo.MorphPlayer (NULL, PlayerClass, Duration, MorphStyle, MorphFlash, UnMorphFlash); } else { diff --git a/wadsrc/static/zscript/shared/player.txt b/wadsrc/static/zscript/shared/player.txt index dc5d92512d..2d6ef3cac1 100644 --- a/wadsrc/static/zscript/shared/player.txt +++ b/wadsrc/static/zscript/shared/player.txt @@ -1111,7 +1111,7 @@ class PlayerPawn : Actor native } if (!--player.morphTics) { // Attempt to undo the chicken/pig - player.UndoPlayerMorph(player, MRF_UNDOBYTIMEOUT); + player.mo.UndoPlayerMorph(player, MRF_UNDOBYTIMEOUT); } } } @@ -1477,18 +1477,18 @@ struct PlayerInfo native play // this is what internally is known as player_t native readonly @UserCmd original_cmd; - bool MorphPlayer(playerinfo p, Class spawntype, int duration, int style, Class enter_flash = null, Class exit_flash = null) + // The actual implementation is on PlayerPawn where it can be overridden. Use that directly in the future. + deprecated("3.7") bool MorphPlayer(playerinfo p, Class spawntype, int duration, int style, Class enter_flash = null, Class exit_flash = null) { if (mo != null) { - // The actual implementation is on PlayerPawn where it can be overridden. return mo.MorphPlayer(p, spawntype, duration, style, enter_flash, exit_flash); } return false; } // This somehow got its arguments mixed up. 'self' should have been the player to be unmorphed, not the activator - bool UndoPlayerMorph(playerinfo player, int unmorphflag = 0, bool force = false) + deprecated("3.7") bool UndoPlayerMorph(playerinfo player, int unmorphflag = 0, bool force = false) { if (player.mo != null) { diff --git a/wadsrc/static/zscript/shared/player_cheat.txt b/wadsrc/static/zscript/shared/player_cheat.txt index 949c89a3c4..d450ee447b 100644 --- a/wadsrc/static/zscript/shared/player_cheat.txt +++ b/wadsrc/static/zscript/shared/player_cheat.txt @@ -399,4 +399,31 @@ extend class PlayerPawn } return; } + + virtual String CheatMorph(class morphClass, bool quickundo) + { + let oldclass = GetClass(); + + // Set the standard morph style for the current game + int style = MRF_UNDOBYTOMEOFPOWER; + if (gameinfo.gametype == GAME_Hexen) style |= MRF_UNDOBYCHAOSDEVICE; + + if (player.morphTics) + { + if (UndoPlayerMorph (player)) + { + if (!quickundo && oldclass != morphclass && MorphPlayer (player, morphclass, 0, style)) + { + return StringTable.Localize("TXT_STRANGER"); + } + return StringTable.Localize("TXT_NOTSTRANGE"); + } + } + else if (MorphPlayer (player, morphclass, 0, style)) + { + return StringTable.Localize("TXT_STRANGE"); + } + return ""; + } + }