From 1099127ee244ead313314ed62d7662175d9007d7 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 1 Jul 2026 19:02:15 +0000 Subject: [PATCH 1/5] Fix life loss from PvP timer firing after PvP has ended After endPvP, the opponent's startAnteTimer message could re-arm nemesis_timer_started, causing the timer to tick in the old-timer path and costing an extra life. Add a post_pvp guard that blocks timer starts until the player readies for the next blind. Co-Authored-By: Claude Opus 4.6 Claude-Session: https://claude.ai/code/session_016kQrzLNB5kdL18tfVP4gfj --- core.lua | 1 + networking/action_handlers.lua | 4 ++++ ui/game/functions.lua | 1 + 3 files changed, 6 insertions(+) diff --git a/core.lua b/core.lua index 64b84a64..a426a87e 100644 --- a/core.lua +++ b/core.lua @@ -215,6 +215,7 @@ function MP.reset_game_states() comeback_bonus_given = true, comeback_bonus = 0, end_pvp = false, + post_pvp = false, enemy = { score = MP.INSANE_INT.empty(), score_text = "0", diff --git a/networking/action_handlers.lua b/networking/action_handlers.lua index 88f3927f..b44b8028 100644 --- a/networking/action_handlers.lua +++ b/networking/action_handlers.lua @@ -319,6 +319,7 @@ local function action_start_blind(p) -- Re-mask the opponent's hands until the first enemyInfo of the new blind. MP.GAME.enemy.info_received = false MP.GAME.ready_blind = false + MP.GAME.post_pvp = false MP.GAME.pvp_reached = false MP.GAME.timer_started = false MP.GAME.nemesis_timer_started = false @@ -449,6 +450,7 @@ local function action_end_pvp(p) end end MP.GAME.end_pvp = true + MP.GAME.post_pvp = true MP.GAME.timer = MP.UTILS.timer_base() MP.GAME.timer_consumed = false MP.GAME.timer_started = false @@ -957,6 +959,8 @@ end -- Dual-call: dispatched from network (fromNemesis defaults to true) or self-triggered -- by MP.ACTIONS.start_ante_timer (passes fromNemesis = false explicitly). local function action_start_ante_timer(p) + if MP.GAME.post_pvp then return end + local time = p.time local from_nemesis = p.fromNemesis if from_nemesis == nil then from_nemesis = true end diff --git a/ui/game/functions.lua b/ui/game/functions.lua index cd018ac6..02d8bd24 100644 --- a/ui/game/functions.lua +++ b/ui/game/functions.lua @@ -20,6 +20,7 @@ function G.FUNCS.mp_toggle_ready(e) MP.GAME.pvp_reached = true if MP.GAME.ready_blind then + MP.GAME.post_pvp = false MP.ACTIONS.set_location("loc_ready") MP.ACTIONS.ready_blind(e) else From 4cdd24135c083fa0f9d74c57f79b2e79f6b69fa0 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 1 Jul 2026 19:37:41 +0000 Subject: [PATCH 2/5] Revert "Fix life loss from PvP timer firing after PvP has ended" This reverts commit 1099127ee244ead313314ed62d7662175d9007d7. --- core.lua | 1 - networking/action_handlers.lua | 4 ---- ui/game/functions.lua | 1 - 3 files changed, 6 deletions(-) diff --git a/core.lua b/core.lua index a426a87e..64b84a64 100644 --- a/core.lua +++ b/core.lua @@ -215,7 +215,6 @@ function MP.reset_game_states() comeback_bonus_given = true, comeback_bonus = 0, end_pvp = false, - post_pvp = false, enemy = { score = MP.INSANE_INT.empty(), score_text = "0", diff --git a/networking/action_handlers.lua b/networking/action_handlers.lua index b44b8028..88f3927f 100644 --- a/networking/action_handlers.lua +++ b/networking/action_handlers.lua @@ -319,7 +319,6 @@ local function action_start_blind(p) -- Re-mask the opponent's hands until the first enemyInfo of the new blind. MP.GAME.enemy.info_received = false MP.GAME.ready_blind = false - MP.GAME.post_pvp = false MP.GAME.pvp_reached = false MP.GAME.timer_started = false MP.GAME.nemesis_timer_started = false @@ -450,7 +449,6 @@ local function action_end_pvp(p) end end MP.GAME.end_pvp = true - MP.GAME.post_pvp = true MP.GAME.timer = MP.UTILS.timer_base() MP.GAME.timer_consumed = false MP.GAME.timer_started = false @@ -959,8 +957,6 @@ end -- Dual-call: dispatched from network (fromNemesis defaults to true) or self-triggered -- by MP.ACTIONS.start_ante_timer (passes fromNemesis = false explicitly). local function action_start_ante_timer(p) - if MP.GAME.post_pvp then return end - local time = p.time local from_nemesis = p.fromNemesis if from_nemesis == nil then from_nemesis = true end diff --git a/ui/game/functions.lua b/ui/game/functions.lua index 02d8bd24..cd018ac6 100644 --- a/ui/game/functions.lua +++ b/ui/game/functions.lua @@ -20,7 +20,6 @@ function G.FUNCS.mp_toggle_ready(e) MP.GAME.pvp_reached = true if MP.GAME.ready_blind then - MP.GAME.post_pvp = false MP.ACTIONS.set_location("loc_ready") MP.ACTIONS.ready_blind(e) else From 1aedefc439cc5f524c9fe3c9e69df63f35daa743 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 1 Jul 2026 19:39:11 +0000 Subject: [PATCH 3/5] Fix PvP timer causing life loss after PvP ends Tag startAnteTimer with isPvP when pressed during a PvP boss blind. The receiving client ignores isPvP timer starts if it's no longer in PvP, preventing the race condition where a PvP timer press arrives after endPvP has already cleared the PvP state. Co-Authored-By: Claude Opus 4.6 Claude-Session: https://claude.ai/code/session_016kQrzLNB5kdL18tfVP4gfj --- networking/action_handlers.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/networking/action_handlers.lua b/networking/action_handlers.lua index 88f3927f..1405edee 100644 --- a/networking/action_handlers.lua +++ b/networking/action_handlers.lua @@ -957,6 +957,8 @@ end -- Dual-call: dispatched from network (fromNemesis defaults to true) or self-triggered -- by MP.ACTIONS.start_ante_timer (passes fromNemesis = false explicitly). local function action_start_ante_timer(p) + if p.isPvP and not (MP.is_pvp_boss() and MP.is_layer_active("pvp_timer")) then return end + local time = p.time local from_nemesis = p.fromNemesis if from_nemesis == nil then from_nemesis = true end @@ -1290,9 +1292,11 @@ function MP.ACTIONS.request_nemesis_stats() end function MP.ACTIONS.start_ante_timer() + local is_pvp = MP.is_pvp_boss() and MP.is_layer_active("pvp_timer") Client.send({ action = "startAnteTimer", time = MP.GAME.timer, + isPvP = is_pvp or nil, }) action_start_ante_timer({ time = MP.GAME.timer, fromNemesis = false }) end From e20ab29de456d02399000b4cc5665df3443289a0 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 1 Jul 2026 19:42:35 +0000 Subject: [PATCH 4/5] Use end_pvp flag instead of is_pvp_boss to gate PvP timer starts is_pvp_boss() stays true briefly after PvP ends until the game state transitions. Check MP.GAME.end_pvp (set by endPvP handler) so the guard catches the race window where PvP is over but the blind hasn't changed yet. Co-Authored-By: Claude Opus 4.6 Claude-Session: https://claude.ai/code/session_016kQrzLNB5kdL18tfVP4gfj --- networking/action_handlers.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/networking/action_handlers.lua b/networking/action_handlers.lua index 1405edee..86766d0f 100644 --- a/networking/action_handlers.lua +++ b/networking/action_handlers.lua @@ -957,7 +957,7 @@ end -- Dual-call: dispatched from network (fromNemesis defaults to true) or self-triggered -- by MP.ACTIONS.start_ante_timer (passes fromNemesis = false explicitly). local function action_start_ante_timer(p) - if p.isPvP and not (MP.is_pvp_boss() and MP.is_layer_active("pvp_timer")) then return end + if p.isPvP and (MP.GAME.end_pvp or not MP.is_pvp_boss()) then return end local time = p.time local from_nemesis = p.fromNemesis From 5f9ecaa6612800ba6b4d660219a59f006097f049 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 1 Jul 2026 19:44:30 +0000 Subject: [PATCH 5/5] Also ignore PvP timer starts when player has 0 hands left If hands are exhausted the PvP round is effectively over even before endPvP arrives, so reject stale PvP timer presses in that state too. Co-Authored-By: Claude Opus 4.6 Claude-Session: https://claude.ai/code/session_016kQrzLNB5kdL18tfVP4gfj --- networking/action_handlers.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/networking/action_handlers.lua b/networking/action_handlers.lua index 86766d0f..ece83fde 100644 --- a/networking/action_handlers.lua +++ b/networking/action_handlers.lua @@ -957,7 +957,7 @@ end -- Dual-call: dispatched from network (fromNemesis defaults to true) or self-triggered -- by MP.ACTIONS.start_ante_timer (passes fromNemesis = false explicitly). local function action_start_ante_timer(p) - if p.isPvP and (MP.GAME.end_pvp or not MP.is_pvp_boss()) then return end + if p.isPvP and (MP.GAME.end_pvp or not MP.is_pvp_boss() or G.GAME.current_round.hands_left <= 0) then return end local time = p.time local from_nemesis = p.fromNemesis