mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-29 23:33:00 +00:00
3e91d38582
- This had broken several mods that relied on the inventory being in place before the switch, which was the intended way to begin with.
421 lines
11 KiB
Text
421 lines
11 KiB
Text
//-----------------------------------------------------------------------------
|
|
//
|
|
// 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
|
|
{
|
|
// 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_NO_BLOCKMAP = 1 << 3,
|
|
MPROP_NO_SECTOR = 1 << 4,
|
|
MPROP_NO_INTERACTION = 1 << 5,
|
|
MPROP_INVIS = 1 << 6,
|
|
}
|
|
|
|
native int UnmorphTime;
|
|
native int MorphFlags;
|
|
native class<Actor> MorphExitFlash;
|
|
native int 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<Actor> flash)
|
|
{
|
|
if (player)
|
|
player.MorphExitFlash = flash;
|
|
else
|
|
MorphExitFlash = flash;
|
|
}
|
|
|
|
clearscope class<Actor> 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.
|
|
virtual Actor, int, int MorphedDeath()
|
|
{
|
|
EMorphFlags mStyle = GetMorphStyle();
|
|
if (mStyle & MRF_UNDOBYDEATH)
|
|
Unmorph(self, force: mStyle & MRF_UNDOBYDEATHFORCED);
|
|
|
|
return null, 0, 0;
|
|
}
|
|
|
|
// [MC] Called when an actor morphs, on both the previous form (!current) and present form (current).
|
|
// Due to recent changes, these are now called internally instead of within the virtuals.
|
|
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) {}
|
|
|
|
//===========================================================================
|
|
//
|
|
// Main entry point
|
|
//
|
|
//===========================================================================
|
|
|
|
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")
|
|
{
|
|
if (player)
|
|
return player.mo.MorphPlayer(activator ? activator.player : null, playerClass, duration, style, morphFlash, unmorphFlash);
|
|
|
|
return MorphMonster(monsterClass, duration, style, morphFlash, unmorphFlash);
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// Action function variant whose arguments differ from the generic one.
|
|
//
|
|
//===========================================================================
|
|
|
|
bool A_Morph(class<Actor> type, int duration = 0, EMorphFlags style = 0, class<Actor> morphFlash = "TeleportFog", class<Actor> unmorphFlash = "TeleportFog")
|
|
{
|
|
return Morph(self, (class<PlayerPawn>)(type), type, duration, style, morphFlash, unmorphFlash);
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// Main entry point
|
|
//
|
|
//===========================================================================
|
|
|
|
virtual bool Unmorph(Actor activator, EMorphFlags flags = 0, bool force = false)
|
|
{
|
|
if (player)
|
|
return player.mo.UndoPlayerMorph(activator ? activator.player : null, flags, force);
|
|
|
|
return UndoMonsterMorph(force);
|
|
}
|
|
|
|
virtual bool CheckUnmorph()
|
|
{
|
|
return UnmorphTime && UnmorphTime <= Level.Time && Unmorph(self, MRF_UNDOBYTIMEOUT);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// FUNC P_MorphMonster
|
|
//
|
|
// Returns true if the monster gets turned into a chicken/pig.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
virtual bool MorphMonster(class<Actor> spawnType, int duration, EMorphFlags style, class<Actor> enterFlash = "TeleportFog", class<Actor> exitFlash = "TeleportFog")
|
|
{
|
|
if (player || !bIsMonster || !spawnType || bDontMorph || Health <= 0 || spawnType == GetClass())
|
|
return false;
|
|
|
|
Actor morphed = Spawn(spawnType, Pos, ALLOW_REPLACE);
|
|
if (!MorphInto(morphed))
|
|
{
|
|
if (morphed)
|
|
{
|
|
morphed.ClearCounters();
|
|
morphed.Destroy();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
// [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.
|
|
|
|
if ((style & MRF_TRANSFERTRANSLATION) && !morphed.bDontTranslate)
|
|
morphed.Translation = Translation;
|
|
|
|
morphed.Angle = Angle;
|
|
morphed.Alpha = Alpha;
|
|
morphed.RenderStyle = RenderStyle;
|
|
morphed.bShadow |= bShadow;
|
|
morphed.bGhost |= bGhost;
|
|
morphed.Score = Score;
|
|
morphed.Special = Special;
|
|
for (int i; i < 5; ++i)
|
|
morphed.Args[i] = Args[i];
|
|
|
|
if (TID && (style & MRF_NEWTIDBEHAVIOUR))
|
|
{
|
|
morphed.ChangeTID(TID);
|
|
ChangeTID(0);
|
|
}
|
|
|
|
morphed.Tracer = Tracer;
|
|
morphed.Master = Master;
|
|
morphed.CopyFriendliness(self, true);
|
|
|
|
// Remove all armor.
|
|
if (!(style & MRF_KEEPARMOR))
|
|
{
|
|
for (Inventory item = morphed.Inv; item;)
|
|
{
|
|
Inventory next = item.Inv;
|
|
if (item is "Armor")
|
|
item.DepleteOrDestroy();
|
|
|
|
item = next;
|
|
}
|
|
}
|
|
|
|
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.GetMorphStyle();
|
|
morphMon.FlagsSave = morphMon.PremorphProperties;
|
|
}
|
|
|
|
Special = 0;
|
|
bNoInteraction = true;
|
|
A_ChangeLinkFlags(true, true);
|
|
|
|
// Legacy
|
|
bInvisible = true;
|
|
bSolid = bShootable = false;
|
|
|
|
PostMorph(morphed, false);
|
|
morphed.PostMorph(self, true);
|
|
|
|
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 (!Alternative || bStayMorphed || Alternative.bStayMorphed)
|
|
return false;
|
|
|
|
Actor alt = Alternative;
|
|
alt.SetOrigin(Pos, false);
|
|
if (!force && (PremorphProperties & MPROP_SOLID))
|
|
{
|
|
bool altSolid = alt.bSolid;
|
|
bool isSolid = bSolid;
|
|
bool isTouchy = bTouchy;
|
|
|
|
alt.bSolid = true;
|
|
bSolid = bTouchy = false;
|
|
|
|
bool res = alt.TestMobjLocation();
|
|
|
|
alt.bSolid = altSolid;
|
|
bSolid = isSolid;
|
|
bTouchy = isTouchy;
|
|
|
|
// Didn't fit.
|
|
if (!res)
|
|
{
|
|
SetMorphTics(5 * TICRATE);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!MorphInto(alt))
|
|
return false;
|
|
|
|
PreUnmorph(alt, false);
|
|
alt.PreUnmorph(self, true);
|
|
|
|
// Check to see if it had a powerup that caused it to morph.
|
|
for (Inventory item = alt.Inv; item;)
|
|
{
|
|
Inventory next = item.Inv;
|
|
if (item is "PowerMorph")
|
|
item.Destroy();
|
|
|
|
item = next;
|
|
}
|
|
|
|
alt.Angle = Angle;
|
|
alt.bShadow = bShadow;
|
|
alt.bGhost = bGhost;
|
|
alt.bSolid = (PremorphProperties & MPROP_SOLID);
|
|
alt.bShootable = (PremorphProperties & MPROP_SHOOTABLE);
|
|
alt.bInvisible = (PremorphProperties & MPROP_INVIS);
|
|
alt.Vel = Vel;
|
|
alt.Score = Score;
|
|
|
|
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);
|
|
}
|
|
|
|
alt.Special = Special;
|
|
for (int i; i < 5; ++i)
|
|
alt.Args[i] = Args[i];
|
|
|
|
alt.Tracer = Tracer;
|
|
alt.Master = Master;
|
|
alt.CopyFriendliness(self, true, false);
|
|
if (Health > 0 || (style & MRF_UNDOBYDEATHSAVES))
|
|
alt.Health = alt.SpawnHealth();
|
|
else
|
|
alt.Health = Health;
|
|
|
|
Special = 0;
|
|
|
|
PostUnmorph(alt, false); // From is false here: Leaving the caller's body.
|
|
alt.PostUnmorph(self, true); // True here: Entering this body from here.
|
|
|
|
if (MorphExitFlash)
|
|
{
|
|
Actor fog = Spawn(MorphExitFlash, alt.Pos.PlusZ(GameInfo.TELEFOGHEIGHT), ALLOW_REPLACE);
|
|
if (fog)
|
|
fog.Target = alt;
|
|
}
|
|
|
|
ClearCounters();
|
|
Destroy();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
//
|
|
//
|
|
//===========================================================================
|
|
|
|
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;
|
|
SetMorphStyle(MorphStyle);
|
|
PremorphProperties = FlagsSave;
|
|
return super.UndoMonsterMorph(force);
|
|
}
|
|
}
|