From a7d4d409a0a684e7d74424777f6b7f25fd61888b Mon Sep 17 00:00:00 2001 From: Boondorl Date: Fri, 14 Feb 2025 01:13:39 -0500 Subject: [PATCH] Added PlayerRespawning event Allows denying player respawns for better control on special game modes e.g. Last Man Standing. --- src/events.cpp | 34 +++++++++++++++++++++++++++++++++ src/events.h | 3 +++ src/g_game.cpp | 11 +++++++++-- src/g_level.cpp | 2 +- src/g_levellocals.h | 2 +- src/playsim/bots/b_game.cpp | 2 +- wadsrc/static/zscript/events.zs | 1 + 7 files changed, 50 insertions(+), 5 deletions(-) diff --git a/src/events.cpp b/src/events.cpp index e4d7d1d1af5..a45bacd8b79 100755 --- a/src/events.cpp +++ b/src/events.cpp @@ -871,6 +871,24 @@ void EventManager::PlayerSpawned(int num) handler->PlayerSpawned(num); } +bool EventManager::PlayerRespawning(int num) +{ + if (ShouldCallStatic(true)) + { + bool res = staticEventManager.PlayerRespawning(num); + if (!res) + return false; + } + + for (DStaticEventHandler* handler = FirstEventHandler; handler; handler = handler->next) + { + if (!handler->PlayerRespawning(num)) + return false; + } + + return true; +} + void EventManager::PlayerRespawned(int num) { if (ShouldCallStatic(true)) staticEventManager.PlayerRespawned(num); @@ -2131,6 +2149,22 @@ void DStaticEventHandler::PlayerSpawned(int num) } } +bool DStaticEventHandler::PlayerRespawning(int num) +{ + int res = true; + IFVIRTUAL(DStaticEventHandler, PlayerRespawning) + { + // don't create excessive DObjects if not going to be processed anyway + if (isEmpty(func)) return res; + FPlayerEvent e = { num, false }; + VMValue params[] = { (DStaticEventHandler*)this, &e }; + VMReturn returns[] = { &res }; + VMCall(func, params, 2, returns, 1); + } + + return res; +} + void DStaticEventHandler::PlayerRespawned(int num) { IFVIRTUAL(DStaticEventHandler, PlayerRespawned) diff --git a/src/events.h b/src/events.h index 4a73406f8b1..b1310a9b884 100644 --- a/src/events.h +++ b/src/events.h @@ -331,6 +331,7 @@ class DStaticEventHandler : public DObject // make it a part of normal GC proces // void PlayerEntered(int num, bool fromhub); void PlayerSpawned(int num); + bool PlayerRespawning(int num); void PlayerRespawned(int num); void PlayerDied(int num); void PlayerDisconnected(int num); @@ -524,6 +525,8 @@ struct EventManager void PlayerEntered(int num, bool fromhub); // this executes at the same time as ENTER scripts void PlayerSpawned(int num); + // Executes when a player is attempting to respawn. Does not include resurrect cheat. + bool PlayerRespawning(int num); // this executes when a player respawns. includes resurrect cheat. void PlayerRespawned(int num); // this executes when a player dies (partially duplicating worldthingdied, but whatever) diff --git a/src/g_game.cpp b/src/g_game.cpp index 7e8c5cb3d76..84a410fe20c 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -1153,7 +1153,7 @@ void G_Ticker () } if (players[i].playerstate == PST_REBORN || players[i].playerstate == PST_ENTER) { - primaryLevel->DoReborn(i, false); + primaryLevel->DoReborn(i); } } } @@ -1766,7 +1766,7 @@ void FLevelLocals::QueueBody (AActor *body) // G_DoReborn // EXTERN_CVAR(Bool, sv_singleplayerrespawn) -void FLevelLocals::DoReborn (int playernum, bool freshbot) +void FLevelLocals::DoReborn (int playernum, bool force) { if (!multiplayer && !(flags2 & LEVEL2_ALLOWRESPAWN) && !sv_singleplayerrespawn && !G_SkillProperty(SKILLP_PlayerRespawn)) @@ -1786,6 +1786,13 @@ void FLevelLocals::DoReborn (int playernum, bool freshbot) } else { + // Check if the player should be allowed to actually respawn first. + if (!force && players[playernum].playerstate == PST_REBORN && !localEventManager->PlayerRespawning(playernum)) + { + players[playernum].playerstate = PST_DEAD; + return; + } + bool isUnfriendly; PlayerSpawnPickClass(playernum); diff --git a/src/g_level.cpp b/src/g_level.cpp index bc0cd378af5..a1759cdceb3 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -838,7 +838,7 @@ void FLevelLocals::ChangeLevel(const char *levelname, int position, int inflags, player->mo->special1 = 0; } // ]] - DoReborn(i, false); + DoReborn(i, true); } } } diff --git a/src/g_levellocals.h b/src/g_levellocals.h index 30602d4d07a..2002e35b5ea 100644 --- a/src/g_levellocals.h +++ b/src/g_levellocals.h @@ -253,7 +253,7 @@ struct FLevelLocals // g_Game void PlayerReborn (int player); bool CheckSpot (int playernum, FPlayerStart *mthing); - void DoReborn (int playernum, bool freshbot); + void DoReborn (int playernum, bool force = false); void QueueBody (AActor *body); double PlayersRangeFromSpot (FPlayerStart *spot); FPlayerStart *SelectFarthestDeathmatchSpot (size_t selections); diff --git a/src/playsim/bots/b_game.cpp b/src/playsim/bots/b_game.cpp index 3863ac923b2..19f212a61e5 100644 --- a/src/playsim/bots/b_game.cpp +++ b/src/playsim/bots/b_game.cpp @@ -396,7 +396,7 @@ bool FCajunMaster::DoAddBot (FLevelLocals *Level, uint8_t *info, botskill_t skil else Printf ("%s joined the game\n", players[bnum].userinfo.GetName()); - Level->DoReborn (bnum, true); + Level->DoReborn (bnum); Level->localEventManager->PlayerEntered(bnum, false); return true; } diff --git a/wadsrc/static/zscript/events.zs b/wadsrc/static/zscript/events.zs index b1aaf30c84e..883e8c3604f 100644 --- a/wadsrc/static/zscript/events.zs +++ b/wadsrc/static/zscript/events.zs @@ -184,6 +184,7 @@ class StaticEventHandler : Object native play version("2.4") // virtual void PlayerEntered(PlayerEvent e) {} virtual void PlayerSpawned(PlayerEvent e) {} + virtual bool PlayerRespawning(PlayerEvent e) { return true; } virtual void PlayerRespawned(PlayerEvent e) {} virtual void PlayerDied(PlayerEvent e) {} virtual void PlayerDisconnected(PlayerEvent e) {}