- scriptified the methods of APowerMorph.

- made some changes to PowerMorph to better deal with recursive calls from UndoPlayerMorph. The flag hackery was only needed because the 'alternative' pointers were cleared far too late.
This commit is contained in:
Christoph Oelckers 2017-01-01 19:23:43 +01:00
parent 2d2963ead4
commit 66cc68606f
5 changed files with 88 additions and 86 deletions

View file

@ -1759,7 +1759,6 @@ DEFINE_FIELD(APowerMorph, MorphFlash)
DEFINE_FIELD(APowerMorph, UnMorphFlash) DEFINE_FIELD(APowerMorph, UnMorphFlash)
DEFINE_FIELD(APowerMorph, MorphStyle) DEFINE_FIELD(APowerMorph, MorphStyle)
DEFINE_FIELD(APowerMorph, MorphedPlayer) DEFINE_FIELD(APowerMorph, MorphedPlayer)
DEFINE_FIELD(APowerMorph, bInUndoMorph)
//=========================================================================== //===========================================================================
// //
@ -1777,77 +1776,3 @@ void APowerMorph::Serialize(FSerializer &arc)
("morphedplayer", MorphedPlayer); ("morphedplayer", MorphedPlayer);
} }
//===========================================================================
//
// APowerMorph :: InitEffect
//
//===========================================================================
void APowerMorph::InitEffect( )
{
Super::InitEffect();
if (Owner != nullptr && Owner->player != nullptr && PlayerClass != nullptr)
{
player_t *realplayer = Owner->player; // Remember the identity of the player
if (P_MorphPlayer(realplayer, realplayer, PlayerClass, INT_MAX/*INDEFINITELY*/, MorphStyle, MorphFlash, UnMorphFlash))
{
Owner = realplayer->mo; // Replace the new owner in our owner; safe because we are not attached to anything yet
ItemFlags |= IF_CREATECOPYMOVED; // Let the caller know the "real" owner has changed (to the morphed actor)
MorphedPlayer = realplayer; // Store the player identity (morphing clears the unmorphed actor's "player" field)
}
else // morph failed - give the caller an opportunity to fail the pickup completely
{
ItemFlags |= IF_INITEFFECTFAILED; // Let the caller know that the activation failed (can fail the pickup if appropriate)
}
}
}
//===========================================================================
//
// APowerMorph :: EndEffect
//
//===========================================================================
void APowerMorph::EndEffect( )
{
Super::EndEffect();
// Abort if owner already destroyed or unmorphed
if (Owner == nullptr || MorphedPlayer == nullptr)
{
return;
}
// Abort if owner is dead; their Die() method will
// take care of any required unmorphing on death.
if (MorphedPlayer->health <= 0)
{
return;
}
// Unmorph if possible
if (!bInUndoMorph)
{
int savedMorphTics = MorphedPlayer->morphTics;
P_UndoPlayerMorph (MorphedPlayer, MorphedPlayer, 0, !!(MorphedPlayer->MorphStyle & MORPH_UNDOALWAYS));
// Abort if unmorph failed; in that case,
// set the usual retry timer and return.
if (MorphedPlayer != NULL && MorphedPlayer->morphTics)
{
// Transfer retry timeout
// to the powerup's timer.
EffectTics = MorphedPlayer->morphTics;
// Reload negative morph tics;
// use actual value; it may
// be in use for animation.
MorphedPlayer->morphTics = savedMorphTics;
// Try again some time later
return;
}
}
// Unmorph suceeded
MorphedPlayer = NULL;
}

View file

@ -228,18 +228,12 @@ class APowerMorph : public APowerup
public: public:
virtual void Serialize(FSerializer &arc) override; virtual void Serialize(FSerializer &arc) override;
void SetNoCallUndoMorph() { bInUndoMorph = true; }
// Variables // Variables
PClassPlayerPawn *PlayerClass; PClassPlayerPawn *PlayerClass;
PClassActor *MorphFlash, *UnMorphFlash; PClassActor *MorphFlash, *UnMorphFlash;
int MorphStyle; int MorphStyle;
player_t *MorphedPlayer; player_t *MorphedPlayer;
bool bInUndoMorph; // Because P_UndoPlayerMorph() can call EndEffect recursively
protected:
virtual void InitEffect () override;
virtual void EndEffect () override;
}; };
#endif //__A_ARTIFACTS_H__ #endif //__A_ARTIFACTS_H__

View file

@ -154,6 +154,18 @@ bool P_MorphPlayer (player_t *activator, player_t *p, PClassPlayerPawn *spawntyp
return true; return true;
} }
DEFINE_ACTION_FUNCTION(_PlayerInfo, MorphPlayer)
{
PARAM_SELF_STRUCT_PROLOGUE(player_t);
PARAM_POINTER(victim, player_t);
PARAM_CLASS(spawntype, APlayerPawn);
PARAM_INT(duration);
PARAM_INT(style);
PARAM_CLASS_DEF(enter_flash, AActor);
PARAM_CLASS_DEF(exit_flash, AActor);
ACTION_RETURN_BOOL(P_MorphPlayer(self, victim, spawntype, duration, style, enter_flash, exit_flash));
}
//---------------------------------------------------------------------------- //----------------------------------------------------------------------------
// //
// FUNC P_UndoPlayerMorph // FUNC P_UndoPlayerMorph
@ -204,14 +216,15 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag,
mo->target = pmo->target; mo->target = pmo->target;
mo->tracer = pmo->tracer; mo->tracer = pmo->tracer;
pmo->player = nullptr; pmo->player = nullptr;
mo->alternative = pmo->alternative = nullptr;
// Remove the morph power if the morph is being undone prematurely. // Remove the morph power if the morph is being undone prematurely.
auto pmtype = PClass::FindActor("PowerMorph");
for (AInventory *item = pmo->Inventory, *next = nullptr; item != nullptr; item = next) for (AInventory *item = pmo->Inventory, *next = nullptr; item != nullptr; item = next)
{ {
next = item->Inventory; next = item->Inventory;
if (item->IsKindOf(RUNTIME_CLASS(APowerMorph))) if (item->IsKindOf(pmtype))
{ {
static_cast<APowerMorph *>(item)->SetNoCallUndoMorph();
item->Destroy(); item->Destroy();
} }
} }
@ -348,8 +361,6 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag,
beastweap->Destroy (); beastweap->Destroy ();
} }
} }
mo->alternative = nullptr;
pmo->alternative = nullptr;
pmo->Destroy (); pmo->Destroy ();
// Restore playerclass armor to its normal amount. // Restore playerclass armor to its normal amount.
AHexenArmor *hxarmor = mo->FindInventory<AHexenArmor>(); AHexenArmor *hxarmor = mo->FindInventory<AHexenArmor>();

View file

@ -251,6 +251,7 @@ FWeaponSlots weapons;
*/ */
native bool MorphPlayer(playerinfo p, Class<PlayerPawn> spawntype, int duration, int style, Class<Actor> enter_flash = null, Class<Actor> exit_flash = null);
native bool UndoPlayerMorph(playerinfo player, int unmorphflag = 0, bool force = false); native bool UndoPlayerMorph(playerinfo player, int unmorphflag = 0, bool force = false);
native bool PoisonPlayer(Actor poisoner, Actor source, int poison); native bool PoisonPlayer(Actor poisoner, Actor source, int poison);
native void PoisonDamage(Actor source, int damage, bool playPainSound); native void PoisonDamage(Actor source, int damage, bool playPainSound);

View file

@ -430,11 +430,82 @@ class PowerMorph : Powerup native
native Class<Actor> MorphFlash, UnMorphFlash; native Class<Actor> MorphFlash, UnMorphFlash;
native int MorphStyle; native int MorphStyle;
native PlayerInfo MorphedPlayer; native PlayerInfo MorphedPlayer;
native bool bInUndoMorph;
Default Default
{ {
Powerup.Duration -40; Powerup.Duration -40;
} }
//===========================================================================
//
// InitEffect
//
//===========================================================================
override void InitEffect()
{
Super.InitEffect();
if (Owner != null && Owner.player != null && PlayerClass != null)
{
let realplayer = Owner.player; // Remember the identity of the player
if (realplayer.MorphPlayer(realplayer, PlayerClass, 0x7fffffff/*INDEFINITELY*/, MorphStyle, MorphFlash, UnMorphFlash))
{
Owner = realplayer.mo; // Replace the new owner in our owner; safe because we are not attached to anything yet
bCreateCopyMoved = true; // Let the caller know the "real" owner has changed (to the morphed actor)
MorphedPlayer = realplayer; // Store the player identity (morphing clears the unmorphed actor's "player" field)
}
else // morph failed - give the caller an opportunity to fail the pickup completely
{
bInitEffectFailed = true; // Let the caller know that the activation failed (can fail the pickup if appropriate)
}
}
}
//===========================================================================
//
// EndEffect
//
//===========================================================================
override void EndEffect()
{
Super.EndEffect();
// Abort if owner already destroyed or unmorphed
if (Owner == null || MorphedPlayer == null || Owner.alternative == null)
{
return;
}
// Abort if owner is dead; their Die() method will
// take care of any required unmorphing on death.
if (MorphedPlayer.health <= 0)
{
return;
}
int savedMorphTics = MorphedPlayer.morphTics;
MorphedPlayer.UndoPlayerMorph (MorphedPlayer, 0, !!(MorphedPlayer.MorphStyle & MRF_UNDOALWAYS));
// Abort if unmorph failed; in that case,
// set the usual retry timer and return.
if (MorphedPlayer != null && MorphedPlayer.morphTics)
{
// Transfer retry timeout
// to the powerup's timer.
EffectTics = MorphedPlayer.morphTics;
// Reload negative morph tics;
// use actual value; it may
// be in use for animation.
MorphedPlayer.morphTics = savedMorphTics;
// Try again some time later
return;
}
// Unmorph suceeded
MorphedPlayer = null;
}
} }