diff --git a/core.lua b/core.lua index ca644cac..64b84a64 100644 --- a/core.lua +++ b/core.lua @@ -197,6 +197,8 @@ function MP.reset_lobby_config(persist_ruleset_and_gamemode) forced_config = false, preview_disabled = false, legacy_smallworld = false, + -- Baseline off; start_lobby sets it on for standard-layer rulesets. + hide_score_until_played = false, } end MP.reset_lobby_config() @@ -217,6 +219,10 @@ function MP.reset_game_states() score = MP.INSANE_INT.empty(), score_text = "0", hands = 4, + hands_text = "4", + -- Whether an enemyInfo message has arrived this blind. Used to mask + -- the opponent's hands as "?" until we hear from them. + info_received = false, location = localize("loc_selecting"), skips = 0, lives = MP.LOBBY.config.starting_lives, diff --git a/lib/ghost_replay.lua b/lib/ghost_replay.lua index 8274ab54..e0a27539 100644 --- a/lib/ghost_replay.lua +++ b/lib/ghost_replay.lua @@ -60,6 +60,7 @@ function MP.GHOST.init_playback(ante) MP.GAME.enemy.score = score MP.GAME.enemy.score_text = MP.INSANE_INT.to_string(score) MP.GAME.enemy.hands = hands[1].hands_left or 0 + MP.GAME.enemy.info_received = true return true end return false @@ -97,6 +98,7 @@ function MP.GHOST.advance_hand() })) MP.GAME.enemy.hands = entry.hands_left or 0 + MP.GAME.enemy.info_received = true if MP.UI.juice_up_pvp_hud then MP.UI.juice_up_pvp_hud() end return true end diff --git a/localization/en-us.lua b/localization/en-us.lua index 99ddfdc3..82472859 100644 --- a/localization/en-us.lua +++ b/localization/en-us.lua @@ -1213,6 +1213,7 @@ return { b_opts_player_diff_deck = "Players have different decks", b_opts_random_loadout = "Randomize deck & stake", b_opts_normal_bosses = "Enable Boss Blind effects", + b_opts_hide_score = "Hide opponent score until you play", b_opts_timer = "Enable Timer", b_opts_disable_preview = "Disable Score Preview", b_opts_the_order = "Enable The Order", diff --git a/networking/action_handlers.lua b/networking/action_handlers.lua index 679e9a20..41874075 100644 --- a/networking/action_handlers.lua +++ b/networking/action_handlers.lua @@ -312,6 +312,12 @@ end local function action_start_blind(p) local first_player = p.firstPlayer + -- Reset the stored opponent score each blind so the first frame after we + -- play (which lifts the "???" mask) shows 0, not last blind's stale score. + MP.GAME.enemy.score = MP.INSANE_INT.empty() + MP.GAME.enemy.score_text = "0" + -- 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.pvp_reached = false MP.GAME.timer_started = false @@ -418,6 +424,8 @@ local function action_enemy_info(p) MP.GAME.enemy.hands = hands_left MP.GAME.enemy.skips = skips MP.GAME.enemy.lives = lives + -- We've now heard from the opponent this blind: unmask their hands count. + MP.GAME.enemy.info_received = true if MP.UI.juice_up_pvp_hud then MP.UI.juice_up_pvp_hud() end end diff --git a/ui/game/blind_hud.lua b/ui/game/blind_hud.lua index 136da888..d3adc4af 100644 --- a/ui/game/blind_hud.lua +++ b/ui/game/blind_hud.lua @@ -43,7 +43,7 @@ function MP.UI.update_blind_HUD(blind, reset, silent) -- Setup enemy hands G.HUD_blind:get_UIE_by_ID("dollars_to_be_earned").parent.parent.states.visible = false G.HUD_blind:get_UIE_by_ID("dollars_to_be_earned").config.object.config.string = - { { ref_table = MP.GAME.enemy, ref_value = "hands" } } + { { ref_table = MP.GAME.enemy, ref_value = "hands_text" } } G.HUD_blind:get_UIE_by_ID("dollars_to_be_earned").config.object:update_text() G.HUD_blind.alignment.offset.y = 0 @@ -198,6 +198,32 @@ function Blind:disable() end G.FUNCS.multiplayer_blind_chip_UI_scale = function(e) + -- Mask the opponent's hands as "?" until the first enemyInfo arrives this + -- blind (same gating as the hidden score), otherwise mirror the real count. + if + MP.LOBBY.config.hide_score_until_played + and MP.is_pvp_boss() + and not MP.GAME.enemy.info_received + then + MP.GAME.enemy.hands_text = "?" + else + MP.GAME.enemy.hands_text = tostring(MP.GAME.enemy.hands) + end + + -- Hide the opponent's score until we have played a hand this PvP blind, so + -- a player can't watch the enemy score before committing their own hand. + -- (The server also withholds the score, this is the matching display.) + -- Gated by the hide_score_until_played lobby option (on by default only on + -- standard-layer rulesets; host-toggleable in non-forcing lobbies). + if + MP.LOBBY.config.hide_score_until_played + and MP.is_pvp_boss() + and G.GAME.current_round + and G.GAME.current_round.hands_played == 0 + then + MP.GAME.enemy.score_text = "???" + return + end local new_score_text = MP.INSANE_INT.to_string(MP.GAME.enemy.score) if G.GAME.blind and MP.GAME.enemy.score and MP.GAME.enemy.score_text ~= new_score_text then if not MP.INSANE_INT.greater_than(MP.GAME.enemy.score, MP.INSANE_INT.create(0, G.E_SWITCH_POINT, 0)) then diff --git a/ui/lobby/_lobby_options/options_tab.lua b/ui/lobby/_lobby_options/options_tab.lua index 965d4f6d..563419fd 100644 --- a/ui/lobby/_lobby_options/options_tab.lua +++ b/ui/lobby/_lobby_options/options_tab.lua @@ -83,6 +83,11 @@ function MP.UI.create_lobby_options_tab() MP.ACTIONS.update_player_usernames() end), create_lobby_option_toggle("normal_bosses_toggle", "b_opts_normal_bosses", "normal_bosses"), + create_lobby_option_toggle( + "hide_score_until_played_toggle", + "b_opts_hide_score", + "hide_score_until_played" + ), }, } end diff --git a/ui/main_menu/play_button/play_button_callbacks.lua b/ui/main_menu/play_button/play_button_callbacks.lua index 22919570..f9c89770 100644 --- a/ui/main_menu/play_button/play_button_callbacks.lua +++ b/ui/main_menu/play_button/play_button_callbacks.lua @@ -110,6 +110,10 @@ function G.FUNCS.start_lobby(e) MP.LOBBY.config.multiplayer_jokers = MP.current_ruleset().multiplayer_content + -- Hide opponent score until you play: default on for standard-layer rulesets + -- only. Set before force_lobby_options() so a forcing ruleset can override it. + MP.LOBBY.config.hide_score_until_played = MP.current_ruleset().standard == true + MP.LOBBY.config.forced_config = MP.current_ruleset():force_lobby_options() MP.LOBBY.config.modifier_layers = MP.modifiers_serialize()