From 80ac981217a2ff04b07655e06b5bc45d1d35fa4c Mon Sep 17 00:00:00 2001 From: Mohammad Aloufi Date: Mon, 20 Apr 2026 15:05:44 +0300 Subject: [PATCH 01/10] Enable viewing other players' Yahtzee scoresheets --- server/games/yahtzee/game.py | 56 +++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 14 deletions(-) diff --git a/server/games/yahtzee/game.py b/server/games/yahtzee/game.py index 3f506b3f..118a9473 100644 --- a/server/games/yahtzee/game.py +++ b/server/games/yahtzee/game.py @@ -27,6 +27,7 @@ from ...game_utils.options import IntOption, option_field from ...messages.localization import Localization from server.core.ui.keybinds import KeybindState +from server.core.users.base import MenuItem # Scoring categories @@ -542,32 +543,45 @@ def _action_view_dice(self, player: Player, action_id: str) -> None: user.speak_l("yahtzee-your-dice", dice=dice_str) def _action_view_scoresheet(self, player: Player, action_id: str) -> None: - """View scoresheet.""" + """View scoresheet menu.""" user = self.get_user(player) if not user: return - # Show current player's scoresheet - current = self.current_player - if not current: + active_players = [p for p in self.players if not p.is_spectator] + if not active_players: return + + items = [MenuItem(text=p.name, id=p.id) for p in active_players] + self._show_transient_display( + player, + kind="scoresheet_player_select", + items=items, + multiletter=True, + ) - ytz_current: YahtzeePlayer = current # type: ignore + def _show_player_scoresheet(self, viewer: Player, target: Player) -> None: + """Show a specific player's scoresheet to the viewer.""" + user = self.get_user(viewer) + if not user: + return + + ytz_target: YahtzeePlayer = target # type: ignore locale = user.locale - lines = [Localization.get(locale, "yahtzee-scoresheet-header", player=current.name)] + lines = [Localization.get(locale, "yahtzee-scoresheet-header", player=target.name)] lines.append(Localization.get(locale, "yahtzee-scoresheet-upper")) for cat in UPPER_CATEGORIES: cat_name = Localization.get(locale, CATEGORY_NAMES[cat]) - score = ytz_current.scores.get(cat) + score = ytz_target.scores.get(cat) if score is not None: lines.append(f" {cat_name}: {score}") else: lines.append(f" {cat_name}: -") - upper_total = ytz_current.get_upper_total() - if ytz_current.upper_bonus_awarded: + upper_total = ytz_target.get_upper_total() + if ytz_target.upper_bonus_awarded: lines.append( Localization.get(locale, "yahtzee-scoresheet-upper-total-bonus", total=upper_total) ) @@ -586,18 +600,18 @@ def _action_view_scoresheet(self, player: Player, action_id: str) -> None: for cat in LOWER_CATEGORIES: cat_name = Localization.get(locale, CATEGORY_NAMES[cat]) - score = ytz_current.scores.get(cat) + score = ytz_target.scores.get(cat) if score is not None: lines.append(f" {cat_name}: {score}") else: lines.append(f" {cat_name}: -") - yahtzee_bonus = ytz_current.yahtzee_bonus_count * 100 + yahtzee_bonus = ytz_target.yahtzee_bonus_count * 100 lines.append( Localization.get( locale, "yahtzee-scoresheet-yahtzee-bonus", - count=ytz_current.yahtzee_bonus_count, + count=ytz_target.yahtzee_bonus_count, total=yahtzee_bonus, ) ) @@ -606,11 +620,25 @@ def _action_view_scoresheet(self, player: Player, action_id: str) -> None: Localization.get( locale, "yahtzee-scoresheet-grand-total", - total=ytz_current.get_total_score(), + total=ytz_target.get_total_score(), ) ) - self.status_box(player, lines) + self.status_box(viewer, lines) + + def _handle_transient_display_selection(self, player: Player, selection_id: str) -> None: + """Handle a selection from the shared transient display menu.""" + super()._handle_transient_display_selection(player, selection_id) + + state = self._get_transient_display_state(player) + if not state: + return + + if state.kind == "scoresheet_player_select": + self._close_transient_display(player) + target = self.get_player_by_id(selection_id) + if target: + self._show_player_scoresheet(player, target) def on_start(self) -> None: """Called when the game starts.""" From c86d6842a9d68ac2b6a00465618d8fb9960d8abd Mon Sep 17 00:00:00 2001 From: Mohammad Aloufi Date: Sun, 26 Apr 2026 20:01:21 +0300 Subject: [PATCH 02/10] Update Yahtzee keybinds: c views own sheet, shift+c views all sheets picker --- server/games/yahtzee/game.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/server/games/yahtzee/game.py b/server/games/yahtzee/game.py index 118a9473..76d7dd45 100644 --- a/server/games/yahtzee/game.py +++ b/server/games/yahtzee/game.py @@ -299,6 +299,15 @@ def create_turn_action_set(self, player: YahtzeePlayer) -> ActionSet: is_hidden="_is_view_scoresheet_hidden", ) ) + action_set.add( + Action( + id="view_all_scoresheets", + label=Localization.get(locale, "yahtzee-check-scoresheet"), + handler="_action_view_all_scoresheets", + is_enabled="_is_view_scoresheet_enabled", + is_hidden="_is_view_scoresheet_hidden", + ) + ) return action_set @@ -315,6 +324,7 @@ def setup_keybinds(self) -> None: # View actions self.define_keybind("d", "View dice", ["view_dice"], state=KeybindState.ACTIVE) self.define_keybind("c", "View scoresheet", ["view_scoresheet"], state=KeybindState.ACTIVE) + self.define_keybind("C", "View all scoresheets", ["view_all_scoresheets"], state=KeybindState.ACTIVE) # ========================================================================== # Declarative Action Callbacks @@ -543,7 +553,11 @@ def _action_view_dice(self, player: Player, action_id: str) -> None: user.speak_l("yahtzee-your-dice", dice=dice_str) def _action_view_scoresheet(self, player: Player, action_id: str) -> None: - """View scoresheet menu.""" + """View your own scoresheet.""" + self._show_player_scoresheet(player, player) + + def _action_view_all_scoresheets(self, player: Player, action_id: str) -> None: + """View scoresheet menu for all players.""" user = self.get_user(player) if not user: return From 87e099be40ade439485b537c4b6865f5036c4ebb Mon Sep 17 00:00:00 2001 From: Mohammad Aloufi Date: Sun, 26 Apr 2026 20:03:36 +0300 Subject: [PATCH 03/10] Fix fragile super() pattern in _handle_transient_display_selection --- server/games/yahtzee/game.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/server/games/yahtzee/game.py b/server/games/yahtzee/game.py index 76d7dd45..f028f07f 100644 --- a/server/games/yahtzee/game.py +++ b/server/games/yahtzee/game.py @@ -642,17 +642,15 @@ def _show_player_scoresheet(self, viewer: Player, target: Player) -> None: def _handle_transient_display_selection(self, player: Player, selection_id: str) -> None: """Handle a selection from the shared transient display menu.""" - super()._handle_transient_display_selection(player, selection_id) - state = self._get_transient_display_state(player) - if not state: - return - - if state.kind == "scoresheet_player_select": + if state and state.kind == "scoresheet_player_select": self._close_transient_display(player) target = self.get_player_by_id(selection_id) if target: self._show_player_scoresheet(player, target) + return + + super()._handle_transient_display_selection(player, selection_id) def on_start(self) -> None: """Called when the game starts.""" From 8df72ddca72525d97f7b3bba8353f75d7349cce9 Mon Sep 17 00:00:00 2001 From: Mohammad Aloufi Date: Sun, 26 Apr 2026 20:05:45 +0300 Subject: [PATCH 04/10] Add feedback when scoresheet player selection is not found --- server/games/yahtzee/game.py | 4 ++++ server/locales/en/main.ftl | 1 + 2 files changed, 5 insertions(+) diff --git a/server/games/yahtzee/game.py b/server/games/yahtzee/game.py index f028f07f..54a1ddb1 100644 --- a/server/games/yahtzee/game.py +++ b/server/games/yahtzee/game.py @@ -648,6 +648,10 @@ def _handle_transient_display_selection(self, player: Player, selection_id: str) target = self.get_player_by_id(selection_id) if target: self._show_player_scoresheet(player, target) + else: + user = self.get_user(player) + if user: + user.speak_l("action-player-not-found") return super()._handle_transient_display_selection(player, selection_id) diff --git a/server/locales/en/main.ftl b/server/locales/en/main.ftl index e82e4ea6..bf6e2a84 100644 --- a/server/locales/en/main.ftl +++ b/server/locales/en/main.ftl @@ -179,6 +179,7 @@ table-saved-destroying = Table saved! Returning to main menu. game-type-not-found = Game type no longer exists. # Action disabled reasons +action-player-not-found = Player not found. action-not-your-turn = It's not your turn. action-not-playing = The game hasn't started. action-spectator = Spectators cannot do this. From 30f316dd441b0e912ee0eef3bcf5cef6d28ac753 Mon Sep 17 00:00:00 2001 From: Mohammad Aloufi Date: Sun, 26 Apr 2026 20:07:10 +0300 Subject: [PATCH 05/10] Add feedback when there are no active players to pick from --- server/games/yahtzee/game.py | 1 + 1 file changed, 1 insertion(+) diff --git a/server/games/yahtzee/game.py b/server/games/yahtzee/game.py index 54a1ddb1..ad6ac220 100644 --- a/server/games/yahtzee/game.py +++ b/server/games/yahtzee/game.py @@ -564,6 +564,7 @@ def _action_view_all_scoresheets(self, player: Player, action_id: str) -> None: active_players = [p for p in self.players if not p.is_spectator] if not active_players: + user.speak_l("action-player-not-found") return items = [MenuItem(text=p.name, id=p.id) for p in active_players] From ddf5ff2c2ba17c3ec971c99f045cafaf5bf6f9fc Mon Sep 17 00:00:00 2001 From: Mohammad Aloufi Date: Sun, 26 Apr 2026 20:08:46 +0300 Subject: [PATCH 06/10] Fix trailing whitespace --- server/games/yahtzee/game.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/games/yahtzee/game.py b/server/games/yahtzee/game.py index ad6ac220..79cd5559 100644 --- a/server/games/yahtzee/game.py +++ b/server/games/yahtzee/game.py @@ -566,7 +566,7 @@ def _action_view_all_scoresheets(self, player: Player, action_id: str) -> None: if not active_players: user.speak_l("action-player-not-found") return - + items = [MenuItem(text=p.name, id=p.id) for p in active_players] self._show_transient_display( player, From f5f00cc0239372d11ef22dc205716bd10fd2e53b Mon Sep 17 00:00:00 2001 From: Mohammad Aloufi Date: Sun, 26 Apr 2026 20:10:17 +0300 Subject: [PATCH 07/10] Allow spectators to view dice and scoresheets in Yahtzee --- server/games/yahtzee/game.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/server/games/yahtzee/game.py b/server/games/yahtzee/game.py index 79cd5559..fe4e6758 100644 --- a/server/games/yahtzee/game.py +++ b/server/games/yahtzee/game.py @@ -321,10 +321,9 @@ def setup_keybinds(self) -> None: # Toggle dice (1-5 keys) - from DiceGameMixin self.setup_dice_keybinds() - # View actions - self.define_keybind("d", "View dice", ["view_dice"], state=KeybindState.ACTIVE) - self.define_keybind("c", "View scoresheet", ["view_scoresheet"], state=KeybindState.ACTIVE) - self.define_keybind("C", "View all scoresheets", ["view_all_scoresheets"], state=KeybindState.ACTIVE) + self.define_keybind("d", "View dice", ["view_dice"], state=KeybindState.ACTIVE, include_spectators=True) + self.define_keybind("c", "View scoresheet", ["view_scoresheet"], state=KeybindState.ACTIVE, include_spectators=True) + self.define_keybind("C", "View all scoresheets", ["view_all_scoresheets"], state=KeybindState.ACTIVE, include_spectators=True) # ========================================================================== # Declarative Action Callbacks @@ -554,6 +553,8 @@ def _action_view_dice(self, player: Player, action_id: str) -> None: def _action_view_scoresheet(self, player: Player, action_id: str) -> None: """View your own scoresheet.""" + if player.is_spectator: + return self._action_view_all_scoresheets(player, action_id) self._show_player_scoresheet(player, player) def _action_view_all_scoresheets(self, player: Player, action_id: str) -> None: From 98e33a39ed45a2fe57765421117cda7cfeeec100 Mon Sep 17 00:00:00 2001 From: Mohammad Aloufi Date: Sun, 26 Apr 2026 20:20:08 +0300 Subject: [PATCH 08/10] Fix shift+c keybind mapping for Yahtzee scoresheet picker --- server/games/yahtzee/game.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/server/games/yahtzee/game.py b/server/games/yahtzee/game.py index fe4e6758..83b79b31 100644 --- a/server/games/yahtzee/game.py +++ b/server/games/yahtzee/game.py @@ -323,7 +323,7 @@ def setup_keybinds(self) -> None: self.define_keybind("d", "View dice", ["view_dice"], state=KeybindState.ACTIVE, include_spectators=True) self.define_keybind("c", "View scoresheet", ["view_scoresheet"], state=KeybindState.ACTIVE, include_spectators=True) - self.define_keybind("C", "View all scoresheets", ["view_all_scoresheets"], state=KeybindState.ACTIVE, include_spectators=True) + self.define_keybind("shift+c", "View all scoresheets", ["view_all_scoresheets"], state=KeybindState.ACTIVE, include_spectators=True) # ========================================================================== # Declarative Action Callbacks From 07fb20b50904f52466f425add095de0a5cc89dc2 Mon Sep 17 00:00:00 2001 From: Mohammad Aloufi Date: Sun, 26 Apr 2026 20:29:18 +0300 Subject: [PATCH 09/10] Differentiate labels for scoresheet actions in menu --- server/games/yahtzee/game.py | 2 +- server/locales/en/yahtzee.ftl | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/server/games/yahtzee/game.py b/server/games/yahtzee/game.py index 83b79b31..7d1a2fde 100644 --- a/server/games/yahtzee/game.py +++ b/server/games/yahtzee/game.py @@ -302,7 +302,7 @@ def create_turn_action_set(self, player: YahtzeePlayer) -> ActionSet: action_set.add( Action( id="view_all_scoresheets", - label=Localization.get(locale, "yahtzee-check-scoresheet"), + label=Localization.get(locale, "yahtzee-check-all-scoresheets"), handler="_action_view_all_scoresheets", is_enabled="_is_view_scoresheet_enabled", is_hidden="_is_view_scoresheet_hidden", diff --git a/server/locales/en/yahtzee.ftl b/server/locales/en/yahtzee.ftl index 64c8c551..52d74ccd 100644 --- a/server/locales/en/yahtzee.ftl +++ b/server/locales/en/yahtzee.ftl @@ -48,6 +48,7 @@ yahtzee-continuing = Continuing turn. # Status checks yahtzee-check-scoresheet = Check scorecard +yahtzee-check-all-scoresheets = Check all scorecards yahtzee-view-dice = Check your dice yahtzee-your-dice = Your dice: { $dice }. yahtzee-your-dice-kept = Your dice: { $dice }. Keeping: { $kept } From e88d99df0c9c3509e1d2666952572cb669833d88 Mon Sep 17 00:00:00 2001 From: Mohammad Aloufi Date: Sun, 26 Apr 2026 21:49:36 +0300 Subject: [PATCH 10/10] Fix Yahtzee scoresheet picker showing scorecard on Escape --- server/games/yahtzee/game.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/server/games/yahtzee/game.py b/server/games/yahtzee/game.py index 7d1a2fde..02d32132 100644 --- a/server/games/yahtzee/game.py +++ b/server/games/yahtzee/game.py @@ -569,6 +569,8 @@ def _action_view_all_scoresheets(self, player: Player, action_id: str) -> None: return items = [MenuItem(text=p.name, id=p.id) for p in active_players] + back_label = Localization.get(user.locale, "back") + items.append(MenuItem(text=back_label, id="transient_display_back")) self._show_transient_display( player, kind="scoresheet_player_select", @@ -646,6 +648,9 @@ def _handle_transient_display_selection(self, player: Player, selection_id: str) """Handle a selection from the shared transient display menu.""" state = self._get_transient_display_state(player) if state and state.kind == "scoresheet_player_select": + if selection_id == "transient_display_back": + self._close_transient_display(player) + return self._close_transient_display(player) target = self.get_player_by_id(selection_id) if target: