- scriptified the rest of the morph code.

This commit is contained in:
Christoph Oelckers 2018-11-24 10:47:42 +01:00
parent ac1bffc51b
commit bd84a60663
8 changed files with 237 additions and 277 deletions

View file

@ -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.

View file

@ -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<AMorphedMonster *>(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();
}
}

View file

@ -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__

View file

@ -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<AActor*> UnmorphedMe;
int UnmorphTime, MorphStyle;
PClassActor *MorphExitFlash;
ActorFlags FlagsSave;
};
#endif //__A_SHAREDGLOBAL_H__

View file

@ -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<AMorphedMonster *>(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<AMorphedMonster *>(actor);
if (P_UndoMonsterMorph(morphed_actor, force))
{
changes++;
}
}
}
changes += P_UnmorphActor(activator, actor, 0, force);
}
}

View file

@ -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; )
{

View file

@ -695,7 +695,7 @@ bool player_t::Resurrect()
if (morphTics)
{
P_UndoPlayerMorph(this, this);
P_UnmorphActor(mo, mo);
}
// player is now alive.

View file

@ -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<Actor>)("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<Actor> MorphExitFlash;
Actor UnmorphedMe;
int UnmorphTime, MorphStyle;
Class<Actor> 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;
}
}