diff --git a/src/g_game.cpp b/src/g_game.cpp index 9be9b5455..27aff3abf 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1249,7 +1249,7 @@ void G_PlayerFinishLevel (int player, EFinishLevelType mode, int flags) if (p->morphTics != 0) { // Undo morph - P_UndoPlayerMorph (p, p, 0, true); + P_UnmorphActor(p->mo, p->mo, 0, true); } // Strip all current powers, unless moving in a hub and the power is okay to keep. diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index 631510106..d917df3d4 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -1,9 +1,6 @@ //----------------------------------------------------------------------------- // -// Copyright 1994-1996 Raven Software -// Copyright 1999-2016 Randy Heit -// Copyright 2002-2016 Christoph Oelckers -// Copyright 2005-2008 Martin Howe +// Copyright 2018 Christoph Oelckers // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by @@ -52,12 +49,11 @@ bool P_MorphActor(AActor *activator, AActor *victim, PClassActor *ptype, PClassA return false; } -bool P_UndoPlayerMorph(player_t *activator, player_t *player, int unmorphflag, bool force) +bool P_UnmorphActor(AActor *activator, AActor *morphed, int flags, bool force) { - if (!player->mo) return false; - IFVIRTUALPTR(player->mo, APlayerPawn, UndoPlayerMorph) + IFVIRTUALPTR(morphed, AActor, UnMorph) { - VMValue params[] = { player->mo, activator, unmorphflag, force }; + VMValue params[] = { morphed, activator, flags, force }; int retval; VMReturn ret(&retval); VMCall(func, params, countof(params), &ret, 1); @@ -66,186 +62,4 @@ bool P_UndoPlayerMorph(player_t *activator, player_t *player, int unmorphflag, b return false; } -//---------------------------------------------------------------------------- -// -// FUNC P_UndoMonsterMorph -// -// Returns true if the monster unmorphs. -// -//---------------------------------------------------------------------------- - -bool P_UndoMonsterMorph (AMorphedMonster *beast, bool force) -{ - AActor *actor; - - if (beast->UnmorphTime == 0 || - beast->UnmorphedMe == NULL || - beast->flags3 & MF3_STAYMORPHED || - beast->UnmorphedMe->flags3 & MF3_STAYMORPHED) - { - return false; - } - actor = beast->UnmorphedMe; - actor->SetOrigin (beast->Pos(), false); - actor->flags |= MF_SOLID; - beast->flags &= ~MF_SOLID; - ActorFlags6 beastflags6 = beast->flags6; - beast->flags6 &= ~MF6_TOUCHY; - if (!force && !P_TestMobjLocation (actor)) - { // Didn't fit - actor->flags &= ~MF_SOLID; - beast->flags |= MF_SOLID; - beast->flags6 = beastflags6; - beast->UnmorphTime = level.time + 5*TICRATE; // Next try in 5 seconds - return false; - } - actor->Angles.Yaw = beast->Angles.Yaw; - actor->target = beast->target; - actor->FriendPlayer = beast->FriendPlayer; - actor->flags = beast->FlagsSave & ~MF_JUSTHIT; - actor->flags = (actor->flags & ~(MF_FRIENDLY|MF_SHADOW)) | (beast->flags & (MF_FRIENDLY|MF_SHADOW)); - actor->flags3 = (actor->flags3 & ~(MF3_NOSIGHTCHECK|MF3_HUNTPLAYERS|MF3_GHOST)) - | (beast->flags3 & (MF3_NOSIGHTCHECK|MF3_HUNTPLAYERS|MF3_GHOST)); - actor->flags4 = (actor->flags4 & ~MF4_NOHATEPLAYERS) | (beast->flags4 & MF4_NOHATEPLAYERS); - if (!(beast->FlagsSave & MF_JUSTHIT)) - actor->renderflags &= ~RF_INVISIBLE; - actor->health = actor->SpawnHealth(); - actor->Vel = beast->Vel; - actor->tid = beast->tid; - actor->special = beast->special; - actor->Score = beast->Score; - memcpy (actor->args, beast->args, sizeof(actor->args)); - actor->AddToHash (); - beast->UnmorphedMe = NULL; - DObject::StaticPointerSubstitution (beast, actor); - PClassActor *exit_flash = beast->MorphExitFlash; - beast->Destroy (); - AActor *eflash = Spawn(exit_flash, beast->PosPlusZ(TELEFOGHEIGHT), ALLOW_REPLACE); - if (eflash) - eflash->target = actor; - return true; -} - -//---------------------------------------------------------------------------- -// -// FUNC P_MorphedDeath -// -// Unmorphs the actor if possible. -// Returns the unmorphed actor, the style with which they were morphed and the -// health (of the AActor, not the player_t) they last had before unmorphing. -// -//---------------------------------------------------------------------------- - -bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *morphedhealth) -{ - // May be a morphed player - if ((actor->player) && - (actor->player->morphTics) && - (actor->player->MorphStyle & MORPH_UNDOBYDEATH) && - (actor->player->mo) && - (actor->player->mo->alternative)) - { - AActor *realme = actor->player->mo->alternative; - int realstyle = actor->player->MorphStyle; - int realhealth = actor->health; - if (P_UndoPlayerMorph(actor->player, actor->player, 0, !!(actor->player->MorphStyle & MORPH_UNDOBYDEATHFORCED))) - { - *morphed = realme; - *morphedstyle = realstyle; - *morphedhealth = realhealth; - return true; - } - return false; - } - - // May be a morphed monster - if (actor->GetClass()->IsDescendantOf(RUNTIME_CLASS(AMorphedMonster))) - { - AMorphedMonster *fakeme = static_cast(actor); - AActor *realme = fakeme->UnmorphedMe; - if (realme != NULL) - { - if ((fakeme->UnmorphTime) && - (fakeme->MorphStyle & MORPH_UNDOBYDEATH)) - { - int realstyle = fakeme->MorphStyle; - int realhealth = fakeme->health; - if (P_UndoMonsterMorph(fakeme, !!(fakeme->MorphStyle & MORPH_UNDOBYDEATHFORCED))) - { - *morphed = realme; - *morphedstyle = realstyle; - *morphedhealth = realhealth; - return true; - } - } - if (realme->flags4 & MF4_BOSSDEATH) - { - realme->health = 0; // make sure that A_BossDeath considers it dead. - A_BossDeath(realme); - } - } - fakeme->flags3 |= MF3_STAYMORPHED; // moved here from AMorphedMonster::Die() - return false; - } - - // Not a morphed player or monster - return false; -} - - -// Morphed Monster (you must subclass this to do something useful) --------- - -IMPLEMENT_CLASS(AMorphedMonster, false, true) - -IMPLEMENT_POINTERS_START(AMorphedMonster) - IMPLEMENT_POINTER(UnmorphedMe) -IMPLEMENT_POINTERS_END - -DEFINE_FIELD(AMorphedMonster, UnmorphedMe) -DEFINE_FIELD(AMorphedMonster, UnmorphTime) -DEFINE_FIELD(AMorphedMonster, MorphStyle) -DEFINE_FIELD(AMorphedMonster, MorphExitFlash) - -void AMorphedMonster::Serialize(FSerializer &arc) -{ - Super::Serialize (arc); - arc("unmorphedme", UnmorphedMe) - ("unmorphtime", UnmorphTime) - ("morphstyle", MorphStyle) - ("morphexitflash", MorphExitFlash) - ("flagsave", FlagsSave); -} - -void AMorphedMonster::OnDestroy () -{ - if (UnmorphedMe != NULL) - { - UnmorphedMe->Destroy (); - } - Super::OnDestroy(); -} - -void AMorphedMonster::Die (AActor *source, AActor *inflictor, int dmgflags, FName MeansOfDeath) -{ - // Dead things don't unmorph -// flags3 |= MF3_STAYMORPHED; - // [MH] - // But they can now, so that line above has been - // moved into P_MorphedDeath() and is now set by - // that function if and only if it is needed. - Super::Die (source, inflictor, dmgflags, MeansOfDeath); - if (UnmorphedMe != NULL && (UnmorphedMe->flags & MF_UNMORPHED)) - { - UnmorphedMe->health = health; - UnmorphedMe->CallDie (source, inflictor, dmgflags, MeansOfDeath); - } -} - -void AMorphedMonster::Tick () -{ - if (UnmorphTime > level.time || !P_UndoMonsterMorph(this)) - { - Super::Tick(); - } -} diff --git a/src/g_shared/a_morph.h b/src/g_shared/a_morph.h index 4a7177301..00303533c 100644 --- a/src/g_shared/a_morph.h +++ b/src/g_shared/a_morph.h @@ -33,12 +33,8 @@ enum class PClass; class AActor; class player_t; -class AMorphedMonster; bool P_MorphActor(AActor *activator, AActor *victim, PClassActor *ptype, PClassActor *mtype, int duration, int style, PClassActor *enter_flash, PClassActor *exit_flash); - -bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag = 0, bool force = false); -bool P_UndoMonsterMorph (AMorphedMonster *beast, bool force = false); -bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *morphedhealth); +bool P_UnmorphActor(AActor *activator, AActor *morphed, int flags = 0, bool force = false); #endif //__A_MORPH__ diff --git a/src/g_shared/a_sharedglobal.h b/src/g_shared/a_sharedglobal.h index e2a033b07..a63056914 100644 --- a/src/g_shared/a_sharedglobal.h +++ b/src/g_shared/a_sharedglobal.h @@ -154,21 +154,4 @@ private: DEarthquake (); }; -class AMorphedMonster : public AActor -{ - DECLARE_CLASS (AMorphedMonster, AActor) - HAS_OBJECT_POINTERS -public: - void Tick (); - - void Serialize(FSerializer &arc); - void Die (AActor *source, AActor *inflictor, int dmgflags, FName MeansOfDeath) override; - void OnDestroy() override; - - TObjPtr UnmorphedMe; - int UnmorphTime, MorphStyle; - PClassActor *MorphExitFlash; - ActorFlags FlagsSave; -}; - #endif //__A_SHAREDGLOBAL_H__ diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 04db70d32..22ada7712 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -10331,24 +10331,7 @@ scriptwait: if (tag == 0) { - if (activator->player) - { - if (P_UndoPlayerMorph(activator->player, activator->player, 0, force)) - { - changes++; - } - } - else - { - if (activator->GetClass()->IsDescendantOf(RUNTIME_CLASS(AMorphedMonster))) - { - AMorphedMonster *morphed_actor = barrier_cast(activator); - if (P_UndoMonsterMorph(morphed_actor, force)) - { - changes++; - } - } - } + changes += P_UnmorphActor(activator, activator, 0, force); } else { @@ -10357,24 +10340,7 @@ scriptwait: while ( (actor = iterator.Next ()) ) { - if (actor->player) - { - if (P_UndoPlayerMorph(activator->player, actor->player, 0, force)) - { - changes++; - } - } - else - { - if (actor->GetClass()->IsDescendantOf(RUNTIME_CLASS(AMorphedMonster))) - { - AMorphedMonster *morphed_actor = static_cast(actor); - if (P_UndoMonsterMorph(morphed_actor, force)) - { - changes++; - } - } - } + changes += P_UnmorphActor(activator, actor, 0, force); } } diff --git a/src/p_interaction.cpp b/src/p_interaction.cpp index f40d01034..aba33a5cd 100644 --- a/src/p_interaction.cpp +++ b/src/p_interaction.cpp @@ -289,39 +289,41 @@ void AActor::Die (AActor *source, AActor *inflictor, int dmgflags, FName MeansOf { // Handle possible unmorph on death bool wasgibbed = (health < GetGibHealth()); - AActor *realthis = NULL; - int realstyle = 0; - int realhealth = 0; - if (P_MorphedDeath(this, &realthis, &realstyle, &realhealth)) + { - if (!(realstyle & MORPH_UNDOBYDEATHSAVES)) + IFVIRTUAL(AActor, MorphedDeath) { - if (wasgibbed) + AActor *realthis = NULL; + int realstyle = 0; + int realhealth = 0; + + VMValue params[] = { this }; + VMReturn returns[3]; + returns[0].PointerAt((void**)&realthis); + returns[1].IntAt(&realstyle); + returns[2].IntAt(&realhealth); + VMCall(func, params, 1, returns, 3); + + if (realthis && !(realstyle & MORPH_UNDOBYDEATHSAVES)) { - int realgibhealth = realthis->GetGibHealth(); - if (realthis->health >= realgibhealth) + if (wasgibbed) { - realthis->health = realgibhealth -1; // if morphed was gibbed, so must original be (where allowed)l + int realgibhealth = realthis->GetGibHealth(); + if (realthis->health >= realgibhealth) + { + realthis->health = realgibhealth - 1; // if morphed was gibbed, so must original be (where allowed)l + } } + realthis->CallDie(source, inflictor, dmgflags, MeansOfDeath); } - realthis->CallDie(source, inflictor, dmgflags, MeansOfDeath); + } - return; } // [SO] 9/2/02 -- It's rather funny to see an exploded player body with the invuln sparkle active :) effects &= ~FX_RESPAWNINVUL; //flags &= ~MF_INVINCIBLE; - if (debugfile && this->player) - { - static int dieticks[MAXPLAYERS]; // [ZzZombo] not used? Except if for peeking in debugger... - int pnum = int(this->player-players); - dieticks[pnum] = gametic; - fprintf(debugfile, "died (%d) on tic %d (%s)\n", pnum, gametic, - this->player->cheats&CF_PREDICTING ? "predicting" : "real"); - } - // [RH] Notify this actor's items. for (AInventory *item = Inventory; item != NULL; ) { diff --git a/src/p_user.cpp b/src/p_user.cpp index 579b33f0c..de67ac55e 100644 --- a/src/p_user.cpp +++ b/src/p_user.cpp @@ -695,7 +695,7 @@ bool player_t::Resurrect() if (morphTics) { - P_UndoPlayerMorph(this, this); + P_UnmorphActor(mo, mo); } // player is now alive. diff --git a/wadsrc/static/zscript/shared/morph.txt b/wadsrc/static/zscript/shared/morph.txt index ebb4a3437..4c6762125 100644 --- a/wadsrc/static/zscript/shared/morph.txt +++ b/wadsrc/static/zscript/shared/morph.txt @@ -1,5 +1,34 @@ +//----------------------------------------------------------------------------- +// +// Copyright 1994-1996 Raven Software +// Copyright 1999-2016 Randy Heit +// Copyright 2002-2018 Christoph Oelckers +// Copyright 2005-2008 Martin Howe +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see http://www.gnu.org/licenses/ +// +//----------------------------------------------------------------------------- +// + extend class Actor { + virtual Actor, int, int MorphedDeath() + { + return null, 0, 0; + } + + //=========================================================================== // // Main entry point @@ -10,7 +39,7 @@ extend class Actor { if (player != null && player.mo != null && playerclass != null) { - return player.mo.MorphPlayer(activator.player, playerclass, duration, style, morphflash, unmorphflash); + return player.mo.MorphPlayer(activator? activator.player : null, playerclass, duration, style, morphflash, unmorphflash); } else { @@ -38,7 +67,28 @@ extend class Actor return false; } + //=========================================================================== + // + // Main entry point + // + //=========================================================================== + bool UnMorph(Actor activator, int flags, bool force) + { + if (player) + { + return player.mo.UndoPlayerMorph(activator? activator.player : null, flags, force); + } + else + { + let morphed = MorphedMonster(self); + if (morphed) + return morphed.UndoMonsterMorph(force); + } + return false; + } + + //--------------------------------------------------------------------------- // // FUNC P_MorphMonster @@ -71,7 +121,7 @@ extend class Actor morphed.UnmorphTime = level.time + ((duration) ? duration : DEFMORPHTICS) + random[morphmonst](); morphed.MorphStyle = style; morphed.MorphExitFlash = (exit_flash) ? exit_flash : (class)("TeleportFog"); - //morphed.FlagsSave = bSolid * 2 + bShootable * 4 + bInvisible * 0x40; // The factors are for savegame compatibility + morphed.FlagsSave = bSolid * 2 + bShootable * 4 + bInvisible * 0x40; // The factors are for savegame compatibility morphed.special = special; morphed.args[0] = args[0]; @@ -501,9 +551,38 @@ extend class PlayerPawn return true; } - + //=========================================================================== + // + // + // + //=========================================================================== + + override Actor, int, int MorphedDeath() + { + // Voodoo dolls should not unmorph the real player here. + if ((player.mo == self) && + (player.morphTics) && + (player.MorphStyle & MRF_UNDOBYDEATH) && + (alternative)) + { + Actor realme = alternative; + int realstyle = player.MorphStyle; + int realhealth = health; + if (UndoPlayerMorph(player, 0, !!(player.MorphStyle & MRF_UNDOBYDEATHFORCED))) + { + return realme, realstyle, realhealth; + } + } + return null, 0, 0; + } } +//=========================================================================== +// +// +// +//=========================================================================== + class MorphProjectile : Actor { @@ -536,11 +615,18 @@ class MorphProjectile : Actor } -class MorphedMonster : Actor native +//=========================================================================== +// +// +// +//=========================================================================== + +class MorphedMonster : Actor { - native Actor UnmorphedMe; - native int UnmorphTime, MorphStyle; - native Class MorphExitFlash; + Actor UnmorphedMe; + int UnmorphTime, MorphStyle; + Class MorphExitFlash; + int FlagsSave; Default { @@ -548,5 +634,118 @@ class MorphedMonster : Actor native -COUNTKILL +FLOORCLIP } + + override void OnDestroy () + { + if (UnmorphedMe != NULL) + { + UnmorphedMe.Destroy (); + } + Super.OnDestroy(); + } + + override void Die (Actor source, Actor inflictor, int dmgflags, Name MeansOfDeath) + { + Super.Die (source, inflictor, dmgflags, MeansOfDeath); + if (UnmorphedMe != NULL && UnmorphedMe.bUnmorphed) + { + UnmorphedMe.health = health; + UnmorphedMe.Die (source, inflictor, dmgflags, MeansOfDeath); + } + } + + override void Tick () + { + if (UnmorphTime > level.time || !UndoMonsterMorph()) + { + Super.Tick(); + } + } + + //---------------------------------------------------------------------------- + // + // FUNC P_UndoMonsterMorph + // + // Returns true if the monster unmorphs. + // + //---------------------------------------------------------------------------- + + virtual bool UndoMonsterMorph(bool force = false) + { + if (UnmorphTime == 0 || UnmorphedMe == NULL || bStayMorphed || UnmorphedMe.bStayMorphed) + { + return false; + } + let unmorphed = UnmorphedMe; + unmorphed.SetOrigin (Pos, false); + unmorphed.bSolid = true; + bSolid = false; + bool save = bTouchy; + bTouchy = false; + if (!force && !unmorphed.TestMobjLocation ()) + { // Didn't fit + unmorphed.bSolid = false; + bSolid = true; + bTouchy = save; + UnmorphTime = level.time + 5*TICRATE; // Next try in 5 seconds + return false; + } + unmorphed.Angle = Angle; + unmorphed.target = target; + unmorphed.bShadow = bShadow; + unmorphed.bGhost = bGhost; + unmorphed.bSolid = !!(flagssave & 2); + unmorphed.bShootable = !!(flagssave & 4); + unmorphed.bInvisible = !!(flagssave & 0x40); + unmorphed.health = unmorphed.SpawnHealth(); + unmorphed.Vel = Vel; + unmorphed.ChangeTid(tid); + unmorphed.special = special; + unmorphed.Score = Score; + unmorphed.args[0] = args[0]; + unmorphed.args[1] = args[1]; + unmorphed.args[2] = args[2]; + unmorphed.args[3] = args[3]; + unmorphed.args[4] = args[4]; + unmorphed.CopyFriendliness (self, true); + UnmorphedMe = NULL; + Substitute(unmorphed); + Destroy (); + let eflash = Spawn(MorphExitFlash, Pos + (0, 0, gameinfo.TELEFOGHEIGHT), ALLOW_REPLACE); + if (eflash) + eflash.target = unmorphed; + return true; + } + + //=========================================================================== + // + // + // + //=========================================================================== + + override Actor, int, int MorphedDeath() + { + let realme = UnmorphedMe; + if (realme != NULL) + { + if ((UnmorphTime) && + (MorphStyle & MRF_UNDOBYDEATH)) + { + int realstyle = MorphStyle; + int realhealth = health; + if (UndoMonsterMorph(!!(MorphStyle & MRF_UNDOBYDEATHFORCED))) + { + return realme, realstyle, realhealth; + } + } + if (realme.bBossDeath) + { + realme.health = 0; // make sure that A_BossDeath considers it dead. + realme.A_BossDeath(); + } + } + return null, 0, 0; + } + }