From 00d2b43ce0ceffcf92ababac5384b59605b3e178 Mon Sep 17 00:00:00 2001 From: Boondorl Date: Wed, 16 Jul 2025 14:26:02 -0400 Subject: [PATCH] Remember statnum when travelling Also cleans up the functions a little. --- src/g_level.cpp | 36 ++++--------- src/playsim/dthinker.cpp | 86 +++++++++++++++++++------------ src/playsim/dthinker.h | 2 + wadsrc/static/zscript/doombase.zs | 1 + 4 files changed, 65 insertions(+), 60 deletions(-) diff --git a/src/g_level.cpp b/src/g_level.cpp index f44be4f84..aa74aeee2 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1666,7 +1666,6 @@ void FLevelLocals::StartTravel() while ((th = it.Next()) != nullptr) { - // Already processed this Thinker. if (th->ObjectFlags & OF_Travelling) continue; @@ -1679,7 +1678,7 @@ void FLevelLocals::StartTravel() // No voodoo dolls allowed. if (mo->player->mo != mo) { - mo->ChangeStatNum(STAT_PLAYER); + mo->ChangeStatNum(mo->GetStatNum()); continue; } @@ -1758,32 +1757,17 @@ int FLevelLocals::FinishTravel() toCallBack.Push(th); th->ObjectFlags &= ~OF_Travelling; + th->ChangeStatNum(th->GetStatNum()); + auto mo = dyn_cast(th); if (mo == nullptr) - { - if (th->IsKindOf(NAME_Bot)) - th->ChangeStatNum(STAT_BOT); - else - th->ChangeStatNum(STAT_DEFAULT); - continue; - } - - // Check the actual pawn type here since its player field isn't guaranteed - // to be set e.g. unmorphed Actors. - const bool player = mo->IsKindOf(NAME_PlayerPawn); - if (player) - mo->ChangeStatNum(STAT_PLAYER); - else if (mo->IsKindOf(NAME_Inventory) && mo->PointerVar(NAME_Owner) != nullptr) - mo->ChangeStatNum(STAT_INVENTORY); - else - mo->ChangeStatNum(STAT_DEFAULT); mo->flags2 &= ~MF2_BLASTED; mo->ClearFOVInterpolation(); LinkActorToLevel(*mo); - if (!player || mo->player == nullptr) + if (mo->player == nullptr || !mo->IsKindOf(NAME_PlayerPawn)) { // Do some basic relinking. Modders will figure out what to do with it // in the callback. @@ -1806,16 +1790,16 @@ int FLevelLocals::FinishTravel() const int pNum = PlayerNum(mo->player); // This will be whatever previous pawn was in the level for this player, be it from a snapshot // or a map spawn. - auto doll = mo->player->mo; - assert(mo != doll); + auto mapDoll = mo->player->mo; + assert(mo != mapDoll); auto start = PickPlayerStart(pNum, 0); if (start == nullptr) { - if (doll != nullptr) + if (mapDoll != nullptr) { Printf(TEXTCOLOR_RED "No player %d start to travel to\n", pNum + 1); - mo->SetOrigin(doll->Pos(), true); + mo->SetOrigin(mapDoll->Pos(), true); } else if (failNum <= 0) { @@ -1824,12 +1808,10 @@ int FLevelLocals::FinishTravel() } } - auto mapDoll = doll; - // Find the actual spawn location taking hub entrances into account. This player // is only meant to be short-lived so don't fire off any events unless there truly // was no other player spawned beforehand (can happen in co-op). - doll = SpawnPlayer(start, pNum, SPF_TEMPPLAYER); + auto doll = SpawnPlayer(start, pNum, SPF_TEMPPLAYER); if (doll != nullptr) { if (!(changeflags & CHANGELEVEL_KEEPFACING)) diff --git a/src/playsim/dthinker.cpp b/src/playsim/dthinker.cpp index cee15b99b..bf8bdf33a 100644 --- a/src/playsim/dthinker.cpp +++ b/src/playsim/dthinker.cpp @@ -783,12 +783,12 @@ int FThinkerList::ProfileThinkers(FThinkerList *dest) // //========================================================================== -DThinker::~DThinker () +DThinker::~DThinker() { assert(NextThinker == nullptr && PrevThinker == nullptr); } -void DThinker::OnDestroy () +void DThinker::OnDestroy() { assert((NextThinker != nullptr && PrevThinker != nullptr) || (NextThinker == nullptr && PrevThinker == nullptr)); @@ -796,13 +796,32 @@ void DThinker::OnDestroy () { Remove(); } + _statNum = -1; Super::OnDestroy(); } void DThinker::Serialize(FSerializer &arc) { Super::Serialize(arc); - arc("level", Level); + arc("level", Level) + ("statnum", _statNum); +} + +//========================================================================== +// +// +// +//========================================================================== + +static int GetStatNum(DThinker* self) +{ + return self->GetStatNum(); +} + +DEFINE_ACTION_FUNCTION_NATIVE(DThinker, GetStatNum, GetStatNum) +{ + PARAM_SELF_PROLOGUE(DThinker); + ACTION_RETURN_INT(self->GetStatNum()); } //========================================================================== @@ -838,11 +857,16 @@ void DThinker::Remove() // //========================================================================== -void DThinker::PostBeginPlay () +void DThinker::PostBeginPlay() { } -DEFINE_ACTION_FUNCTION(DThinker, PostBeginPlay) +static void NativePostBeginPlay(DThinker* self) +{ + self->PostBeginPlay(); +} + +DEFINE_ACTION_FUNCTION_NATIVE(DThinker, PostBeginPlay, NativePostBeginPlay) { PARAM_SELF_PROLOGUE(DThinker); self->PostBeginPlay(); @@ -853,15 +877,9 @@ void DThinker::CallPostBeginPlay() { ObjectFlags |= OF_Spawned; IFVIRTUAL(DThinker, PostBeginPlay) - { - // Without the type cast this picks the 'void *' assignment... - VMValue params[1] = { (DObject*)this }; - VMCall(func, params, 1, nullptr, 0); - } + VMCallVoid(func, this); else - { PostBeginPlay(); - } } //========================================================================== @@ -880,7 +898,7 @@ void DThinker::PostSerialize() // //========================================================================== -DThinker *FLevelLocals::FirstThinker (int statnum) +DThinker *FLevelLocals::FirstThinker(int statnum) { return Thinkers.FirstThinker(statnum); } @@ -896,26 +914,27 @@ DThinker* FLevelLocals::FirstClientsideThinker(int statnum) // //========================================================================== -void DThinker::ChangeStatNum (int statnum) +void DThinker::ChangeStatNum(int statnum) { if ((unsigned)statnum > MAX_STATNUM) - { statnum = MAX_STATNUM; - } Remove(); if (IsClientside()) Level->ClientsideThinkers.Link(this, statnum); else Level->Thinkers.Link(this, statnum); + // Let us relink it back properly when we're done travelling. + if (statnum != STAT_TRAVELLING) + _statNum = statnum; } -static void ChangeStatNum(DThinker *thinker, int statnum) +static void ChangeStatNum(DThinker *self, int statnum) { // Wait until after these Thinkers are processed to allow modifying them, // otherwise relinking and callbacks will be completely messed up. This can // only happen from map objects spawning in intentionally trying to mess with // the list. - if (thinker->ObjectFlags & OF_Travelling) + if (self->ObjectFlags & OF_Travelling) { Printf(TEXTCOLOR_RED "Travelling Thinkers cannot have their statnum changed\n"); return; @@ -923,7 +942,7 @@ static void ChangeStatNum(DThinker *thinker, int statnum) // This will always break Actors, they should use STAT_TRAVELLING instead to // transition between levels. - if (statnum == STAT_STATIC && thinker->IsKindOf(NAME_Actor)) + if (statnum == STAT_STATIC && self->IsKindOf(NAME_Actor)) { Printf(TEXTCOLOR_RED "Actors cannot be added to STAT_STATIC\n"); return; @@ -938,17 +957,17 @@ static void ChangeStatNum(DThinker *thinker, int statnum) return; } // These should be handled by the owning Actor, otherwise they'll lose them and become useless anyway. - if (thinker->IsKindOf(NAME_Inventory) && thinker->PointerVar(NAME_Owner) != nullptr) + if (self->IsKindOf(NAME_Inventory) && self->PointerVar(NAME_Owner) != nullptr) { Printf(TEXTCOLOR_RED "Owned Inventory items must travel with their owner on level change\n"); return; } - if (thinker->IsKindOf(NAME_Bot)) + if (self->IsKindOf(NAME_Bot)) { Printf(TEXTCOLOR_RED "Bot Thinkers must travel with their owner on level change\n"); return; } - auto mo = dyn_cast(thinker); + auto mo = dyn_cast(self); if (mo != nullptr && (mo->flags & MF_UNMORPHED)) { Printf(TEXTCOLOR_RED "Unmorphed Actors must travel with their owner on level change\n"); @@ -956,7 +975,7 @@ static void ChangeStatNum(DThinker *thinker, int statnum) } // These need to be locked down since they have native fields that won't be cleared // properly at the moment. - auto cls = thinker->GetClass()->NativeClass(); + auto cls = self->GetClass()->NativeClass(); if (cls->TypeName != NAME_Thinker && cls->TypeName != NAME_Actor) { Printf(TEXTCOLOR_RED "Native thinkers cannot travel\n"); @@ -969,14 +988,13 @@ static void ChangeStatNum(DThinker *thinker, int statnum) return; } - thinker->ChangeStatNum(statnum); + self->ChangeStatNum(statnum); } DEFINE_ACTION_FUNCTION_NATIVE(DThinker, ChangeStatNum, ChangeStatNum) { PARAM_SELF_PROLOGUE(DThinker); PARAM_INT(stat); - ChangeStatNum(self, stat); return 0; } @@ -1043,11 +1061,16 @@ CCMD(profilethinkers) // //========================================================================== -void DThinker::Tick () +void DThinker::Tick() { } -DEFINE_ACTION_FUNCTION(DThinker, Tick) +static void NativeTick(DThinker* self) +{ + self->Tick(); +} + +DEFINE_ACTION_FUNCTION_NATIVE(DThinker, Tick, NativeTick) { PARAM_SELF_PROLOGUE(DThinker); self->Tick(); @@ -1057,12 +1080,9 @@ DEFINE_ACTION_FUNCTION(DThinker, Tick) void DThinker::CallTick() { IFVIRTUAL(DThinker, Tick) - { - // Without the type cast this picks the 'void *' assignment... - VMValue params[1] = { (DObject*)this }; - VMCall(func, params, 1, nullptr, 0); - } - else Tick(); + VMCallVoid(func, this); + else + Tick(); } //========================================================================== diff --git a/src/playsim/dthinker.h b/src/playsim/dthinker.h index 163119200..c585e4118 100644 --- a/src/playsim/dthinker.h +++ b/src/playsim/dthinker.h @@ -113,6 +113,7 @@ public: size_t PropagateMark(); void ChangeStatNum (int statnum); + inline int GetStatNum() const { return _statNum; } private: void Remove(); @@ -123,6 +124,7 @@ private: friend class DObject; friend class FDoomSerializer; + int8_t _statNum = -1; DThinker *NextThinker = nullptr, *PrevThinker = nullptr; public: diff --git a/wadsrc/static/zscript/doombase.zs b/wadsrc/static/zscript/doombase.zs index b83ecb1fe..0d1fa09fd 100644 --- a/wadsrc/static/zscript/doombase.zs +++ b/wadsrc/static/zscript/doombase.zs @@ -242,6 +242,7 @@ class Thinker : Object native play virtual native void PostBeginPlay(); virtual void OnLoad() {} native void ChangeStatNum(int stat); + native clearscope int GetStatNum() const; static clearscope int Tics2Seconds(int tics) {