Tracer pointer is no longer a safe candidate for storing player morph pointers. Instead, actors must have a new, non-manipulatable pointer. This fixes the following circumstances:

- Crashes occurred if a particular actor was a tracer to the player and the actor was not gone by the time the player unmorphs.
- Failed unmorphs occur if tracer was manipulated through means like A_RearrangePointers, etc.
This commit is contained in:
Major Cooke 2016-09-01 13:49:58 -05:00
parent 4993018520
commit c4357bd352
4 changed files with 39 additions and 26 deletions

View file

@ -1057,6 +1057,7 @@ public:
int skillrespawncount;
int TIDtoHate; // TID of things to hate (0 if none)
FNameNoInit Species; // For monster families
TObjPtr<AActor> alternative; // (Un)Morphed actors stored here. Those with the MF_UNMORPHED flag are the originals.
TObjPtr<AActor> tracer; // Thing being chased/attacked for tracers
TObjPtr<AActor> master; // Thing which spawned this one (prevents mutual attacks)
double Floorclip; // value to use for floor clipping

View file

@ -38,7 +38,7 @@ bool P_MorphPlayer (player_t *activator, player_t *p, PClassPlayerPawn *spawntyp
APlayerPawn *actor;
actor = p->mo;
if (actor == NULL)
if (actor == nullptr)
{
return false;
}
@ -55,7 +55,7 @@ bool P_MorphPlayer (player_t *activator, player_t *p, PClassPlayerPawn *spawntyp
if ((p->mo->GetClass() == spawntype)
&& (p->mo->PlayerFlags & PPF_CANSUPERMORPH)
&& (p->morphTics < (((duration) ? duration : MORPHTICS) - TICRATE))
&& (p->mo->FindInventory (RUNTIME_CLASS(APowerWeaponLevel2), true) == NULL))
&& (p->mo->FindInventory (RUNTIME_CLASS(APowerWeaponLevel2), true) == nullptr))
{ // Make a super chicken
p->mo->GiveInventoryType (RUNTIME_CLASS(APowerWeaponLevel2));
}
@ -65,7 +65,7 @@ bool P_MorphPlayer (player_t *activator, player_t *p, PClassPlayerPawn *spawntyp
{ // Dead players cannot morph
return false;
}
if (spawntype == NULL)
if (spawntype == nullptr)
{
return false;
}
@ -94,7 +94,8 @@ bool P_MorphPlayer (player_t *activator, player_t *p, PClassPlayerPawn *spawntyp
}
morphed->Angles.Yaw = actor->Angles.Yaw;
morphed->target = actor->target;
morphed->tracer = actor;
morphed->tracer = actor->tracer;
morphed->alternative = actor;
morphed->FriendPlayer = actor->FriendPlayer;
morphed->DesignatedTeam = actor->DesignatedTeam;
morphed->Score = actor->Score;
@ -113,7 +114,8 @@ bool P_MorphPlayer (player_t *activator, player_t *p, PClassPlayerPawn *spawntyp
morphed->flags2 |= actor->flags2 & MF2_FLY;
morphed->flags3 |= actor->flags3 & MF3_GHOST;
AActor *eflash = Spawn(((enter_flash) ? enter_flash : RUNTIME_CLASS(ATeleportFog)), actor->PosPlusZ(TELEFOGHEIGHT), ALLOW_REPLACE);
actor->player = NULL;
actor->player = nullptr;
actor->alternative = morphed;
actor->flags &= ~(MF_SOLID|MF_SHOOTABLE);
actor->flags |= MF_UNMORPHED;
actor->renderflags |= RF_INVISIBLE;
@ -129,7 +131,7 @@ bool P_MorphPlayer (player_t *activator, player_t *p, PClassPlayerPawn *spawntyp
p->Vel.X = p->Vel.Y = 0;
morphed->ObtainInventory (actor);
// Remove all armor
for (item = morphed->Inventory; item != NULL; )
for (item = morphed->Inventory; item != nullptr; )
{
AInventory *next = item->Inventory;
if (item->IsKindOf (RUNTIME_CLASS(AArmor)))
@ -182,7 +184,7 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag,
// because the level or game is ended while morphed,
// by the time it gets executed the morphed player
// pawn instance may have already been destroyed.
if (pmo == NULL || pmo->tracer == NULL)
if (pmo == nullptr || pmo->alternative == nullptr)
{
return false;
}
@ -197,7 +199,7 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag,
return false;
}
mo = barrier_cast<APlayerPawn *>(pmo->tracer);
mo = barrier_cast<APlayerPawn *>(pmo->alternative);
mo->SetOrigin (pmo->Pos(), false);
mo->flags |= MF_SOLID;
pmo->flags &= ~MF_SOLID;
@ -208,10 +210,14 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag,
player->morphTics = 2*TICRATE;
return false;
}
pmo->player = NULL;
// No longer using tracer as morph storage. That is what 'alternative' is for.
// If the tracer has changed on the morph, change the original too.
mo->target = pmo->target;
mo->tracer = pmo->tracer;
pmo->player = nullptr;
// Remove the morph power if the morph is being undone prematurely.
for (AInventory *item = pmo->Inventory, *next = NULL; item != NULL; item = next)
for (AInventory *item = pmo->Inventory, *next = nullptr; item != nullptr; item = next)
{
next = item->Inventory;
if (item->IsKindOf(RUNTIME_CLASS(APowerMorph)))
@ -252,10 +258,10 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag,
player->morphTics = 0;
player->MorphedPlayerClass = 0;
player->MorphStyle = 0;
player->MorphExitFlash = NULL;
player->MorphExitFlash = nullptr;
player->viewheight = mo->ViewHeight;
AInventory *level2 = mo->FindInventory (RUNTIME_CLASS(APowerWeaponLevel2), true);
if (level2 != NULL)
if (level2 != nullptr)
{
level2->Destroy ();
}
@ -310,31 +316,31 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag,
}
}
AActor *eflash = NULL;
if (exit_flash != NULL)
AActor *eflash = nullptr;
if (exit_flash != nullptr)
{
eflash = Spawn(exit_flash, pmo->Vec3Angle(20., mo->Angles.Yaw, TELEFOGHEIGHT), ALLOW_REPLACE);
if (eflash) eflash->target = mo;
}
mo->SetupWeaponSlots(); // Use original class's weapon slots.
beastweap = player->ReadyWeapon;
if (player->PremorphWeapon != NULL)
if (player->PremorphWeapon != nullptr)
{
player->PremorphWeapon->PostMorphWeapon ();
}
else
{
player->ReadyWeapon = player->PendingWeapon = NULL;
player->ReadyWeapon = player->PendingWeapon = nullptr;
}
if (correctweapon)
{ // Better "lose morphed weapon" semantics
PClassActor *morphweapon = PClass::FindActor(pmo->MorphWeapon);
if (morphweapon != NULL && morphweapon->IsDescendantOf(RUNTIME_CLASS(AWeapon)))
if (morphweapon != nullptr && morphweapon->IsDescendantOf(RUNTIME_CLASS(AWeapon)))
{
AWeapon *OriginalMorphWeapon = static_cast<AWeapon *>(mo->FindInventory (morphweapon));
if ((OriginalMorphWeapon != NULL) && (OriginalMorphWeapon->GivenAsMorphWeapon))
if ((OriginalMorphWeapon != nullptr) && (OriginalMorphWeapon->GivenAsMorphWeapon))
{ // You don't get to keep your morphed weapon.
if (OriginalMorphWeapon->SisterWeapon != NULL)
if (OriginalMorphWeapon->SisterWeapon != nullptr)
{
OriginalMorphWeapon->SisterWeapon->Destroy ();
}
@ -344,20 +350,21 @@ bool P_UndoPlayerMorph (player_t *activator, player_t *player, int unmorphflag,
}
else // old behaviour (not really useful now)
{ // Assumptions made here are no longer valid
if (beastweap != NULL)
if (beastweap != nullptr)
{ // You don't get to keep your morphed weapon.
if (beastweap->SisterWeapon != NULL)
if (beastweap->SisterWeapon != nullptr)
{
beastweap->SisterWeapon->Destroy ();
}
beastweap->Destroy ();
}
}
pmo->tracer = NULL;
mo->alternative = nullptr;
pmo->alternative = nullptr;
pmo->Destroy ();
// Restore playerclass armor to its normal amount.
AHexenArmor *hxarmor = mo->FindInventory<AHexenArmor>();
if (hxarmor != NULL)
if (hxarmor != nullptr)
{
hxarmor->Slots[4] = mo->GetClass()->HexenArmor[0];
}
@ -517,9 +524,9 @@ bool P_MorphedDeath(AActor *actor, AActor **morphed, int *morphedstyle, int *mor
(actor->player->morphTics) &&
(actor->player->MorphStyle & MORPH_UNDOBYDEATH) &&
(actor->player->mo) &&
(actor->player->mo->tracer))
(actor->player->mo->alternative))
{
AActor *realme = actor->player->mo->tracer;
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)))

View file

@ -389,6 +389,11 @@ void AActor::Serialize(FArchive &arc)
arc << SpriteRotation;
}
if (SaveVersion >= 4550)
{
arc << alternative;
}
{
FString tagstr;
if (arc.IsStoring() && Tag != NULL && Tag->Len() > 0) tagstr = *Tag;

View file

@ -76,7 +76,7 @@ const char *GetVersionString();
// Use 4500 as the base git save version, since it's higher than the
// SVN revision ever got.
#define SAVEVER 4549
#define SAVEVER 4550
#define SAVEVERSTRINGIFY2(x) #x
#define SAVEVERSTRINGIFY(x) SAVEVERSTRINGIFY2(x)