From 3299a29c446ea13ee110f75f527acc179c78ed72 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Sun, 28 Aug 2016 16:14:24 +0200 Subject: [PATCH 1/3] - added CheckClass ACS function. --- src/p_acs.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/p_acs.cpp b/src/p_acs.cpp index 5f08f0152..a2b5275cb 100644 --- a/src/p_acs.cpp +++ b/src/p_acs.cpp @@ -4449,6 +4449,8 @@ enum EACSFunctions -106 : KickFromGame(2), */ + ACSF_CheckClass = 200, + // ZDaemon ACSF_GetTeamScore = 19620, // (int team) ACSF_SetTeamScore, // (int team, int value) @@ -6028,6 +6030,12 @@ doplaysound: if (funcIndex == ACSF_PlayActorSound) return false; } + case ACSF_CheckClass: + { + const char *clsname = FBehavior::StaticLookupString(args[0]); + return !!PClass::FindActor(clsname); + } + default: break; } From 49930185206141a5d0219ebbbf405cae88c82f0e Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Wed, 31 Aug 2016 09:18:59 +0200 Subject: [PATCH 2/3] - fixed: Actor velocity requires an upper limit to prevent uncontrolled accumulation, as can happen when multiple exploding and pushable objects overlap. The value 5000 was chosen because it is high enough to not occur under regular circumstances and small enough to prevent severe slowdowns. In the old fixed point code the lack of such a check just caused random overflows. --- src/p_mobj.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 8971efe25..f5791d079 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -1806,6 +1806,11 @@ double P_XYMovement (AActor *mo, DVector2 scroll) mo->Vel.X *= fac; mo->Vel.Y *= fac; } + const double VELOCITY_THRESHOLD = 5000; // don't let it move faster than this. Fixed point overflowed at 32768 but that's too much to make this safe. + if (mo->Vel.LengthSquared() >= VELOCITY_THRESHOLD*VELOCITY_THRESHOLD) + { + mo->Vel.MakeResize(VELOCITY_THRESHOLD); + } move = mo->Vel; // [RH] Carrying sectors didn't work with low speeds in BOOM. This is // because BOOM relied on the speed being fast enough to accumulate From c4357bd352b5f8ca0a490a75ae4c6f5ebed77ad5 Mon Sep 17 00:00:00 2001 From: Major Cooke Date: Thu, 1 Sep 2016 13:49:58 -0500 Subject: [PATCH 3/3] 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. --- src/actor.h | 1 + src/g_shared/a_morph.cpp | 57 ++++++++++++++++++++++------------------ src/p_mobj.cpp | 5 ++++ src/version.h | 2 +- 4 files changed, 39 insertions(+), 26 deletions(-) diff --git a/src/actor.h b/src/actor.h index 154d14289..b895d1e2a 100644 --- a/src/actor.h +++ b/src/actor.h @@ -1057,6 +1057,7 @@ public: int skillrespawncount; int TIDtoHate; // TID of things to hate (0 if none) FNameNoInit Species; // For monster families + TObjPtr alternative; // (Un)Morphed actors stored here. Those with the MF_UNMORPHED flag are the originals. TObjPtr tracer; // Thing being chased/attacked for tracers TObjPtr master; // Thing which spawned this one (prevents mutual attacks) double Floorclip; // value to use for floor clipping diff --git a/src/g_shared/a_morph.cpp b/src/g_shared/a_morph.cpp index 98af9ff25..57271f768 100644 --- a/src/g_shared/a_morph.cpp +++ b/src/g_shared/a_morph.cpp @@ -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(pmo->tracer); + mo = barrier_cast(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(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(); - 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))) diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index f5791d079..6d8ce4418 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -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; diff --git a/src/version.h b/src/version.h index cffa25bdd..57dacaa00 100644 --- a/src/version.h +++ b/src/version.h @@ -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)