2019-02-23 11:08:27 +00:00
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// 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
|
|
|
|
{
|
2024-01-01 02:41:03 +00:00
|
|
|
enum EPremorphProperty
|
|
|
|
{
|
|
|
|
MPROP_SOLID = 1 << 1,
|
|
|
|
MPROP_SHOOTABLE = 1 << 2,
|
|
|
|
MPROP_INVIS = 1 << 6,
|
|
|
|
}
|
|
|
|
|
|
|
|
int UnmorphTime;
|
|
|
|
EMorphFlags MorphFlags;
|
|
|
|
class<Actor> MorphExitFlash;
|
|
|
|
EPremorphProperty PremorphProperties;
|
|
|
|
|
|
|
|
// 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.
|
2019-02-23 11:08:27 +00:00
|
|
|
virtual Actor, int, int MorphedDeath()
|
|
|
|
{
|
2024-01-01 02:41:03 +00:00
|
|
|
if (MorphFlags & MRF_UNDOBYDEATH)
|
|
|
|
Unmorph(self, force: MorphFlags & MRF_UNDOBYDEATHFORCED);
|
|
|
|
|
2019-02-23 11:08:27 +00:00
|
|
|
return null, 0, 0;
|
|
|
|
}
|
|
|
|
|
2020-03-08 14:19:20 +00:00
|
|
|
// [MC] Called when an actor morphs, on both the previous form (!current) and present form (current).
|
|
|
|
virtual void PreMorph(Actor mo, bool current) {}
|
|
|
|
virtual void PostMorph(Actor mo, bool current) {}
|
|
|
|
virtual void PreUnmorph(Actor mo, bool current) {}
|
|
|
|
virtual void PostUnmorph(Actor mo, bool current) {}
|
2019-02-23 11:08:27 +00:00
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Main entry point
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2024-01-01 02:41:03 +00:00
|
|
|
virtual bool Morph(Actor activator, class<PlayerPawn> playerClass, class<Actor> monsterClass, int duration = 0, EMorphFlags style = 0, class<Actor> morphFlash = "TeleportFog", class<Actor> unmorphFlash = "TeleportFog")
|
2019-02-23 11:08:27 +00:00
|
|
|
{
|
2024-01-01 02:41:03 +00:00
|
|
|
if (player)
|
|
|
|
return player.mo.MorphPlayer(activator ? activator.player : null, playerClass, duration, style, morphFlash, unmorphFlash);
|
|
|
|
|
|
|
|
return MorphMonster(monsterClass, duration, style, morphFlash, unmorphFlash);
|
2019-02-23 11:08:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Action function variant whose arguments differ from the generic one.
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2024-01-01 02:41:03 +00:00
|
|
|
bool A_Morph(class<Actor> type, int duration = 0, EMorphFlags style = 0, class<Actor> morphFlash = "TeleportFog", class<Actor> unmorphFlash = "TeleportFog")
|
2019-02-23 11:08:27 +00:00
|
|
|
{
|
2024-01-01 02:41:03 +00:00
|
|
|
return Morph(self, (class<PlayerPawn>)(type), type, duration, style, morphFlash, unmorphFlash);
|
2019-02-23 11:08:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// Main entry point
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
2024-01-01 02:41:03 +00:00
|
|
|
virtual bool Unmorph(Actor activator, EMorphFlags flags = 0, bool force = false)
|
2019-02-23 11:08:27 +00:00
|
|
|
{
|
|
|
|
if (player)
|
2024-01-01 02:41:03 +00:00
|
|
|
return player.mo.UndoPlayerMorph(activator ? activator.player : null, flags, force);
|
|
|
|
|
|
|
|
return UndoMonsterMorph(force);
|
2019-02-23 11:08:27 +00:00
|
|
|
}
|
|
|
|
|
2024-01-01 02:41:03 +00:00
|
|
|
virtual bool CheckUnmorph()
|
|
|
|
{
|
|
|
|
return UnmorphTime <= Level.Time && Unmorph(self);
|
|
|
|
}
|
2019-02-23 11:08:27 +00:00
|
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// FUNC P_MorphMonster
|
|
|
|
//
|
|
|
|
// Returns true if the monster gets turned into a chicken/pig.
|
|
|
|
//
|
|
|
|
//---------------------------------------------------------------------------
|
|
|
|
|
2024-01-01 02:41:03 +00:00
|
|
|
virtual bool MorphMonster(class<Actor> spawnType, int duration, EMorphFlags style, class<Actor> enterFlash = "TeleportFog", class<Actor> exitFlash = "TeleportFog")
|
2019-02-23 11:08:27 +00:00
|
|
|
{
|
2024-01-01 02:41:03 +00:00
|
|
|
if (player || !spawnType || bDontMorph || !bIsMonster)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
Actor morphed = Spawn(spawnType, Pos, ALLOW_REPLACE);
|
|
|
|
if (!MorphInto(morphed))
|
2019-02-23 11:08:27 +00:00
|
|
|
{
|
2024-01-01 02:41:03 +00:00
|
|
|
morphed.ClearCounters();
|
|
|
|
morphed.Destroy();
|
2019-02-23 11:08:27 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-03-08 14:19:20 +00:00
|
|
|
// [MC] Notify that we're just about to start the transfer.
|
|
|
|
PreMorph(morphed, false); // False: No longer the current.
|
|
|
|
morphed.PreMorph(self, true); // True: Becoming this actor.
|
2020-02-15 22:12:45 +00:00
|
|
|
|
2019-02-23 11:08:27 +00:00
|
|
|
if ((style & MRF_TRANSFERTRANSLATION) && !morphed.bDontTranslate)
|
|
|
|
morphed.Translation = Translation;
|
2024-01-01 02:41:03 +00:00
|
|
|
|
2019-02-23 11:08:27 +00:00
|
|
|
morphed.Angle = Angle;
|
|
|
|
morphed.Alpha = Alpha;
|
|
|
|
morphed.RenderStyle = RenderStyle;
|
|
|
|
morphed.bShadow |= bShadow;
|
|
|
|
morphed.bGhost |= bGhost;
|
2024-01-01 02:41:03 +00:00
|
|
|
morphed.Score = Score;
|
|
|
|
morphed.ChangeTID(TID);
|
|
|
|
morphed.Special = Special;
|
|
|
|
for (int i; i < 5; ++i)
|
|
|
|
morphed.Args[i] = Args[i];
|
|
|
|
|
|
|
|
morphed.CopyFriendliness(self, true);
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
// This is just here for backwards compatibility as MorphedMonster used to be required.
|
|
|
|
let morphMon = MorphedMonster(morphed);
|
|
|
|
if (morphMon)
|
|
|
|
{
|
|
|
|
morphMon.UnmorphedMe = morphMon.Alternative;
|
|
|
|
morphMon.FlagsSave = morphMon.PremorphProperties;
|
|
|
|
}
|
|
|
|
|
|
|
|
ChangeTID(0);
|
|
|
|
Special = 0;
|
2019-02-23 11:08:27 +00:00
|
|
|
bInvisible = true;
|
2024-01-01 02:41:03 +00:00
|
|
|
bSolid = bShootable = false;
|
|
|
|
|
2020-03-08 14:19:20 +00:00
|
|
|
PostMorph(morphed, false);
|
|
|
|
morphed.PostMorph(self, true);
|
2024-01-01 02:41:03 +00:00
|
|
|
|
|
|
|
if (enterFlash)
|
|
|
|
{
|
|
|
|
Actor fog = Spawn(enterFlash, morphed.Pos.PlusZ(GameInfo.TELEFOGHEIGHT), ALLOW_REPLACE);
|
|
|
|
if (fog)
|
|
|
|
fog.Target = morphed;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
//
|
|
|
|
// FUNC P_UndoMonsterMorph
|
|
|
|
//
|
|
|
|
// Returns true if the monster unmorphs.
|
|
|
|
//
|
|
|
|
//----------------------------------------------------------------------------
|
|
|
|
|
|
|
|
virtual bool UndoMonsterMorph(bool force = false)
|
|
|
|
{
|
|
|
|
if (!UnmorphTime || !Alternative || bStayMorphed || Alternative.bStayMorphed)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
Alternative.SetOrigin(Pos, false);
|
|
|
|
if (!force && (PremorphProperties & MPROP_SOLID))
|
|
|
|
{
|
|
|
|
bool altSolid = Alternative.bSolid;
|
|
|
|
bool isSolid = bSolid;
|
|
|
|
bool isTouchy = bTouchy;
|
|
|
|
|
|
|
|
Alternative.bSolid = true;
|
|
|
|
bSolid = bTouchy = false;
|
|
|
|
|
|
|
|
bool res = Alternative.TestMobjLocation();
|
|
|
|
|
|
|
|
Alternative.bSolid = altSolid;
|
|
|
|
bSolid = isSolid;
|
|
|
|
bTouchy = isTouchy;
|
|
|
|
|
|
|
|
// Didn't fit.
|
|
|
|
if (!res)
|
|
|
|
{
|
|
|
|
UnmorphTime = Level.Time + 5*TICRATE; // Next try in 5 seconds.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Actor unmorphed = Alternative;
|
|
|
|
if (!MorphInto(unmorphed))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
PreUnmorph(unmorphed, false);
|
|
|
|
unmorphed.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;
|
|
|
|
|
|
|
|
unmorphed.ChangeTID(TID);
|
|
|
|
unmorphed.Special = Special;
|
|
|
|
for (int i; i < 5; ++i)
|
|
|
|
unmorphed.Args[i] = Args[i];
|
|
|
|
|
|
|
|
unmorphed.CopyFriendliness(self, true);
|
|
|
|
|
|
|
|
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.
|
|
|
|
|
|
|
|
if (MorphExitFlash)
|
|
|
|
{
|
|
|
|
Actor fog = Spawn(MorphExitFlash, unmorphed.Pos.PlusZ(GameInfo.TELEFOGHEIGHT), ALLOW_REPLACE);
|
|
|
|
if (fog)
|
|
|
|
fog.Target = unmorphed;
|
|
|
|
}
|
|
|
|
|
|
|
|
Destroy();
|
2019-02-23 11:08:27 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-01-01 02:41:03 +00:00
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
class MorphProjectile : Actor
|
|
|
|
{
|
|
|
|
class<PlayerPawn> PlayerClass;
|
|
|
|
class<Actor> MonsterClass, MorphFlash, UnmorphFlash;
|
|
|
|
int Duration, MorphStyle;
|
|
|
|
|
|
|
|
Default
|
|
|
|
{
|
|
|
|
Damage 1;
|
|
|
|
Projectile;
|
|
|
|
MorphProjectile.MorphFlash "TeleportFog";
|
|
|
|
MorphProjectile.UnmorphFlash "TeleportFog";
|
|
|
|
|
|
|
|
-ACTIVATEIMPACT
|
|
|
|
-ACTIVATEPCROSS
|
|
|
|
}
|
|
|
|
|
|
|
|
override int DoSpecialDamage(Actor victim, int dmg, Name dmgType)
|
|
|
|
{
|
|
|
|
victim.Morph(Target, PlayerClass, MonsterClass, Duration, MorphStyle, MorphFlash, UnmorphFlash);
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//===========================================================================
|
|
|
|
//
|
|
|
|
// This class is redundant as it's no longer necessary for monsters to
|
|
|
|
// morph but is kept here for compatibility. Its previous fields either exist
|
|
|
|
// in the base Actor now or are just a shell for the actual fields
|
|
|
|
// which either already existed and weren't used for some reason or needed a
|
|
|
|
// better name.
|
|
|
|
//
|
|
|
|
//===========================================================================
|
|
|
|
|
|
|
|
class MorphedMonster : Actor
|
|
|
|
{
|
|
|
|
Actor UnmorphedMe;
|
|
|
|
EMorphFlags MorphStyle;
|
|
|
|
EPremorphProperty FlagsSave;
|
|
|
|
|
|
|
|
Default
|
|
|
|
{
|
|
|
|
Monster;
|
|
|
|
|
|
|
|
+FLOORCLIP
|
|
|
|
-COUNTKILL
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure changes to these are propogated correctly when unmorphing. This is only
|
|
|
|
// set for monsters since originally players and this class were the only ones
|
|
|
|
// that were considered valid for morphing.
|
|
|
|
override bool UndoMonsterMorph(bool force)
|
|
|
|
{
|
|
|
|
Alternative = UnmorphedMe;
|
|
|
|
MorphFlags = MorphStyle;
|
|
|
|
PremorphProperties = FlagsSave;
|
|
|
|
return super.UndoMonsterMorph(force);
|
|
|
|
}
|
|
|
|
}
|