mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-11-24 04:51:19 +00:00
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:
parent
4993018520
commit
c4357bd352
4 changed files with 39 additions and 26 deletions
|
@ -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
|
||||
|
|
|
@ -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)))
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue