mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-12-02 08:53:42 +00:00
6c64a4403c
Added getter and setter functions for handling whether or not the player fields should be gotten/set. Added MRF_KEEPARMOR flag to prevent stripping armor on morph. Optimized unmorphed Actor by setting it to NoInteraction and removing it from the blockmap and sector lists.
378 lines
9.7 KiB
Text
378 lines
9.7 KiB
Text
extend class PlayerPawn
|
|
{
|
|
//===========================================================================
|
|
//
|
|
// InitAllPowerupEffects
|
|
//
|
|
// Calls InitEffect() on every Powerup in the inventory list. Since these
|
|
// functions can be overridden it's safest to store what's next in the item
|
|
// list before calling it.
|
|
//
|
|
//===========================================================================
|
|
|
|
void InitAllPowerupEffects()
|
|
{
|
|
for (Inventory item = Inv; item;)
|
|
{
|
|
Inventory next = item.Inv;
|
|
let power = Powerup(item);
|
|
if (power)
|
|
power.InitEffect();
|
|
|
|
item = next;
|
|
}
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
// EndAllPowerupEffects
|
|
//
|
|
// Calls EndEffect() on every Powerup in the inventory list.
|
|
//
|
|
//===========================================================================
|
|
|
|
void EndAllPowerupEffects()
|
|
{
|
|
for (Inventory item = Inv; item;)
|
|
{
|
|
Inventory next = item.Inv;
|
|
let power = Powerup(item);
|
|
if (power)
|
|
power.EndEffect();
|
|
|
|
item = next;
|
|
}
|
|
}
|
|
|
|
//===========================================================================
|
|
//
|
|
//
|
|
//
|
|
//===========================================================================
|
|
|
|
virtual void ActivateMorphWeapon()
|
|
{
|
|
if (player.ReadyWeapon)
|
|
{
|
|
let psp = player.GetPSprite(PSP_WEAPON);
|
|
psp.y = WEAPONTOP;
|
|
player.ReadyWeapon.ResetPSprite(psp);
|
|
}
|
|
|
|
class<Weapon> morphWeapCls = MorphWeapon;
|
|
if (!morphWeapCls)
|
|
{
|
|
player.ReadyWeapon = null;
|
|
}
|
|
else
|
|
{
|
|
player.ReadyWeapon = Weapon(FindInventory(morphWeapCls));
|
|
if (!player.ReadyWeapon)
|
|
{
|
|
player.ReadyWeapon = Weapon(GiveInventoryType(morphWeapCls));
|
|
if (player.ReadyWeapon)
|
|
player.ReadyWeapon.GivenAsMorphWeapon = true; // Flag is used only by new morphWeap semantics in UndoPlayerMorph
|
|
}
|
|
|
|
if (player.ReadyWeapon)
|
|
player.SetPSprite(PSP_WEAPON, player.ReadyWeapon.GetReadyState());
|
|
}
|
|
|
|
if (player.ReadyWeapon)
|
|
player.SetPSprite(PSP_FLASH, null);
|
|
|
|
player.PendingWeapon = WP_NOCHANGE;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// 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.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
virtual bool MorphPlayer(PlayerInfo activator, class<PlayerPawn> spawnType, int duration, EMorphFlags style, class<Actor> enterFlash = "TeleportFog", class<Actor> exitFlash = "TeleportFog")
|
|
{
|
|
if (!player || !spawnType || bDontMorph || player.Health <= 0
|
|
|| (bInvulnerable && (player != activator || !(style & MRF_WHENINVULNERABLE))))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!duration)
|
|
duration = DEFMORPHTICS;
|
|
|
|
if (Alternative)
|
|
{
|
|
// Player is already a beast.
|
|
if (bCanSuperMorph && spawnType == GetClass()
|
|
&& GetMorphTics() < duration - TICRATE
|
|
&& !FindInventory("PowerWeaponLevel2", true))
|
|
{
|
|
// Make a super chicken.
|
|
GiveInventoryType("PowerWeaponLevel2");
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
if (spawnType == GetClass())
|
|
return false;
|
|
|
|
let morphed = PlayerPawn(Spawn(spawnType, Pos, NO_REPLACE));
|
|
if (!MorphInto(morphed))
|
|
{
|
|
if (morphed)
|
|
morphed.Destroy();
|
|
|
|
return false;
|
|
}
|
|
|
|
PreMorph(morphed, false);
|
|
morphed.PreMorph(self, true);
|
|
|
|
morphed.EndAllPowerupEffects();
|
|
|
|
if ((style & MRF_TRANSFERTRANSLATION) && !morphed.bDontTranslate)
|
|
morphed.Translation = Translation;
|
|
|
|
morphed.Angle = Angle;
|
|
morphed.Pitch = Pitch; // Allow pitch here since mouse look in GZDoom is far more common than Heretic/Hexen.
|
|
morphed.Target = Target;
|
|
morphed.Tracer = Tracer;
|
|
morphed.Master = Master;
|
|
morphed.FriendPlayer = FriendPlayer;
|
|
morphed.DesignatedTeam = DesignatedTeam;
|
|
morphed.Score = Score;
|
|
morphed.ScoreIcon = ScoreIcon;
|
|
morphed.Health = morphed.SpawnHealth();
|
|
if (TID && (style & MRF_NEWTIDBEHAVIOUR))
|
|
{
|
|
morphed.ChangeTid(TID);
|
|
ChangeTid(0);
|
|
}
|
|
|
|
// special2 is no longer used here since Actors now have a proper field for it.
|
|
morphed.PremorphProperties = (bSolid * MPROP_SOLID) | (bShootable * MPROP_SHOOTABLE)
|
|
| (bNoBlockmap * MPROP_NO_BLOCKMAP) | (bNoSector * MPROP_NO_SECTOR)
|
|
| (bNoInteraction * MPROP_NO_INTERACTION) | (bInvisible * MPROP_INVIS);
|
|
|
|
morphed.bShadow |= bShadow;
|
|
morphed.bNoGravity |= bNoGravity;
|
|
morphed.bFly |= bFly;
|
|
morphed.bGhost |= bGhost;
|
|
|
|
// 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;
|
|
}
|
|
}
|
|
|
|
// Players store their morph behavior into their PlayerInfo unlike regular Actors which use the
|
|
// morph properties. This is needed for backwards compatibility and to give the HUD info.
|
|
let p = morphed.player;
|
|
morphed.SetMorphTics(duration);
|
|
morphed.SetMorphStyle(style);
|
|
morphed.SetMorphExitFlash(exitFlash);
|
|
p.MorphedPlayerClass = spawnType;
|
|
p.PremorphWeapon = p.ReadyWeapon;
|
|
p.Health = morphed.Health;
|
|
p.Vel = (0.0, 0.0);
|
|
// If the new view height is higher than the old one, start moving toward it.
|
|
if (morphed.ViewHeight > p.ViewHeight && !p.DeltaViewHeight)
|
|
p.DeltaViewHeight = p.GetDeltaViewHeight();
|
|
|
|
bNoInteraction = true;
|
|
A_ChangeLinkFlags(true, true);
|
|
|
|
// Legacy
|
|
bSolid = bShootable = false;
|
|
bInvisible = true;
|
|
|
|
morphed.ClearFOVInterpolation();
|
|
morphed.InitAllPowerupEffects();
|
|
morphed.ActivateMorphWeapon();
|
|
|
|
PostMorph(morphed, false); // No longer the current body
|
|
morphed.PostMorph(self, true); // This is the current body
|
|
|
|
if (enterFlash)
|
|
{
|
|
Actor fog = Spawn(enterFlash, morphed.Pos.PlusZ(GameInfo.TelefogHeight), ALLOW_REPLACE);
|
|
if (fog)
|
|
fog.Target = morphed;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//----------------------------------------------------------------------------
|
|
//
|
|
// FUNC UndoPlayerMorph
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
virtual bool UndoPlayerMorph(PlayerInfo activator, EMorphFlags unmorphFlags = 0, bool force = false)
|
|
{
|
|
if (!Alternative || bStayMorphed || Alternative.bStayMorphed)
|
|
return false;
|
|
|
|
if (bInvulnerable
|
|
&& (player != activator || (!(player.MorphStyle & MRF_WHENINVULNERABLE) && !(unmorphFlags & MRF_STANDARDUNDOING))))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
let alt = PlayerPawn(Alternative);
|
|
alt.SetOrigin(Pos, false);
|
|
// Test if there's room to unmorph.
|
|
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;
|
|
|
|
if (!res)
|
|
{
|
|
SetMorphTics(2 * TICRATE);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
if (!MorphInto(alt))
|
|
return false;
|
|
|
|
PreUnmorph(alt, false); // This body's about to be left.
|
|
alt.PreUnmorph(self, true); // This one's about to become current.
|
|
|
|
alt.EndAllPowerupEffects();
|
|
|
|
// Remove the morph power if the morph is being undone prematurely.
|
|
for (Inventory item = alt.Inv; item;)
|
|
{
|
|
Inventory next = item.Inv;
|
|
if (item is "PowerMorph")
|
|
item.Destroy();
|
|
|
|
item = next;
|
|
}
|
|
|
|
alt.Angle = Angle;
|
|
alt.Pitch = Pitch;
|
|
alt.Target = Target;
|
|
alt.Tracer = Tracer;
|
|
alt.Master = Master;
|
|
alt.FriendPlayer = FriendPlayer;
|
|
alt.DesignatedTeam = DesignatedTeam;
|
|
alt.Score = Score;
|
|
alt.ScoreIcon = ScoreIcon;
|
|
alt.ReactionTime = 18;
|
|
alt.bSolid = (PremorphProperties & MPROP_SOLID);
|
|
alt.bShootable = (PremorphProperties & MPROP_SHOOTABLE);
|
|
alt.bInvisible = (PremorphProperties & MPROP_INVIS);
|
|
alt.bShadow = bShadow;
|
|
alt.bNoGravity = bNoGravity;
|
|
alt.bGhost = bGhost;
|
|
alt.bFly = bFly;
|
|
alt.Vel = (0.0, 0.0, Vel.Z);
|
|
|
|
alt.bNoInteraction = (PremorphProperties & MPROP_NO_INTERACTION);
|
|
alt.A_ChangeLinkFlags((PremorphProperties & MPROP_NO_BLOCKMAP), (PremorphProperties & MPROP_NO_SECTOR));
|
|
|
|
let p = alt.player;
|
|
class<Actor> exitFlash = alt.GetMorphExitFlash();
|
|
EMorphFlags style = alt.GetMorphStyle();
|
|
Weapon premorphWeap = p.PremorphWeapon;
|
|
|
|
if (TID && (style & MRF_NEWTIDBEHAVIOUR))
|
|
{
|
|
alt.ChangeTid(TID);
|
|
ChangeTID(0);
|
|
}
|
|
|
|
alt.SetMorphTics(0);
|
|
alt.SetMorphStyle(0);
|
|
alt.SetMorphExitFlash(null);
|
|
p.MorphedPlayerClass = null;
|
|
p.PremorphWeapon = null;
|
|
p.ViewHeight = alt.ViewHeight;
|
|
p.Vel = (0.0, 0.0);
|
|
if (p.Health > 0 || (style & MRF_UNDOBYDEATHSAVES))
|
|
p.Health = alt.Health = alt.SpawnHealth();
|
|
else
|
|
alt.Health = p.Health;
|
|
|
|
Inventory level2 = alt.FindInventory("PowerWeaponLevel2", true);
|
|
if (level2)
|
|
level2.Destroy();
|
|
|
|
let morphWeap = p.ReadyWeapon;
|
|
if (premorphWeap)
|
|
{
|
|
premorphWeap.PostMorphWeapon();
|
|
}
|
|
else
|
|
{
|
|
p.ReadyWeapon = null;
|
|
p.PendingWeapon = WP_NOCHANGE;
|
|
p.Refire = 0;
|
|
}
|
|
|
|
if (style & MRF_LOSEACTUALWEAPON)
|
|
{
|
|
// Improved "lose morph weapon" semantics.
|
|
class<Weapon> morphWeapCls = MorphWeapon;
|
|
if (morphWeapCls)
|
|
{
|
|
let originalMorphWeapon = Weapon(alt.FindInventory(morphWeapCls));
|
|
if (originalMorphWeapon && originalMorphWeapon.GivenAsMorphWeapon)
|
|
originalMorphWeapon.Destroy();
|
|
}
|
|
}
|
|
else if (morphWeap) // Old behaviour (not really useful now).
|
|
{
|
|
morphWeap.Destroy();
|
|
}
|
|
|
|
// Reset the base AC of the player's Hexen armor back to its default.
|
|
let hexArmor = HexenArmor(alt.FindInventory("HexenArmor"));
|
|
if (hexArmor)
|
|
hexArmor.Slots[4] = alt.HexenArmor[0];
|
|
|
|
alt.ClearFOVInterpolation();
|
|
alt.InitAllPowerupEffects();
|
|
|
|
PostUnmorph(alt, false); // This body is no longer current.
|
|
alt.PostUnmorph(self, true); // altmo body is current.
|
|
|
|
if (exitFlash)
|
|
{
|
|
Actor fog = Spawn(exitFlash, alt.Vec3Angle(20.0, alt.Angle, GameInfo.TelefogHeight), ALLOW_REPLACE);
|
|
if (fog)
|
|
fog.Target = alt;
|
|
}
|
|
|
|
Destroy();
|
|
return true;
|
|
}
|
|
}
|