From 972f0c61c67a288dcb7af1c18a46ce383e6c046a Mon Sep 17 00:00:00 2001 From: TheLeBerton Date: Sat, 29 Aug 2026 00:37:51 +0200 Subject: [PATCH 1/6] REFACTOR relic scoring loop into ScoringPipeline --- .../src/mastermind_overlay/run/gamestate.py | 7 +++---- .../mastermind_overlay/scoring/pipeline.py | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+), 4 deletions(-) create mode 100644 packages/mastermind-overlay/src/mastermind_overlay/scoring/pipeline.py diff --git a/packages/mastermind-overlay/src/mastermind_overlay/run/gamestate.py b/packages/mastermind-overlay/src/mastermind_overlay/run/gamestate.py index e748b43..1523c75 100644 --- a/packages/mastermind-overlay/src/mastermind_overlay/run/gamestate.py +++ b/packages/mastermind-overlay/src/mastermind_overlay/run/gamestate.py @@ -2,6 +2,7 @@ from mastermind_kernel.feedback import CodeFeedback from mastermind_overlay.relics.relic import Relic +from mastermind_overlay.scoring.pipeline import ScoringPipeline from mastermind_overlay.scoring.points import Points, apply_multiplier, compute_points @@ -17,6 +18,7 @@ def __init__(self, max_turns: int, target_score: int, relics: list[Relic]) -> No self._guessed_feedback: list[CodeFeedback] = [] self._score: int = 0 self._relics: list[Relic] = relics + self._pipeline: ScoringPipeline = ScoringPipeline(relics) @property def current_peg(self) -> int: @@ -67,10 +69,7 @@ def submit_guess(self) -> None: feedback: CodeFeedback = self._secret_code.feedback(self._current_code) self._guessed_feedback.append(feedback) - mult: int = 1 - points: Points = compute_points(feedback) - for relic in self._relics: - points, mult = relic.on_guess(points, mult, feedback) + points, mult = self._pipeline.run(compute_points(feedback), 1, feedback) self._score += apply_multiplier(points, mult) self._current_turn += 1 diff --git a/packages/mastermind-overlay/src/mastermind_overlay/scoring/pipeline.py b/packages/mastermind-overlay/src/mastermind_overlay/scoring/pipeline.py new file mode 100644 index 0000000..f8eb573 --- /dev/null +++ b/packages/mastermind-overlay/src/mastermind_overlay/scoring/pipeline.py @@ -0,0 +1,19 @@ +from typing import Protocol, Sequence + +from mastermind_kernel.feedback import CodeFeedback +from mastermind_overlay.scoring.points import Points + + +class ScoringStage(Protocol): + def on_guess(self, points: Points, mult: int, feedback: CodeFeedback) -> tuple[Points, int]: ... + + +class ScoringPipeline: + def __init__(self, stages: Sequence[ScoringStage]) -> None: + self._stages: Sequence[ScoringStage] = stages + + + def run(self, points: Points, mult: int, feedback: CodeFeedback) -> tuple[Points, int]: + for stage in self._stages: + points, mult = stage.on_guess(points, mult, feedback) + return points, mult From ec1ff72af725122bc34e4d820937cddfb7df3bec Mon Sep 17 00:00:00 2001 From: TheLeBerton Date: Sat, 29 Aug 2026 00:58:06 +0200 Subject: [PATCH 2/6] ADD event bus with round-started and shop-entered notifications --- .../src/mastermind_overlay/events/__init__.py | 0 .../src/mastermind_overlay/events/bus.py | 19 +++++++++++++++++++ .../events/catalog/__init__.py | 0 .../events/catalog/round_started.py | 8 ++++++++ .../events/catalog/shop_entered.py | 6 ++++++ .../src/mastermind_overlay/run/run_state.py | 8 ++++++++ .../src/mastermind_shell/screens/shop.py | 2 ++ 7 files changed, 43 insertions(+) create mode 100644 packages/mastermind-overlay/src/mastermind_overlay/events/__init__.py create mode 100644 packages/mastermind-overlay/src/mastermind_overlay/events/bus.py create mode 100644 packages/mastermind-overlay/src/mastermind_overlay/events/catalog/__init__.py create mode 100644 packages/mastermind-overlay/src/mastermind_overlay/events/catalog/round_started.py create mode 100644 packages/mastermind-overlay/src/mastermind_overlay/events/catalog/shop_entered.py diff --git a/packages/mastermind-overlay/src/mastermind_overlay/events/__init__.py b/packages/mastermind-overlay/src/mastermind_overlay/events/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/packages/mastermind-overlay/src/mastermind_overlay/events/bus.py b/packages/mastermind-overlay/src/mastermind_overlay/events/bus.py new file mode 100644 index 0000000..e6bcce6 --- /dev/null +++ b/packages/mastermind-overlay/src/mastermind_overlay/events/bus.py @@ -0,0 +1,19 @@ +from collections.abc import Callable +from typing import Any, TypeVar + + +E = TypeVar("E") + +class EventBus: + def __init__(self) -> None: + self._handlers: dict[type, list[Callable[[Any], None]]] = {} + + + def subscribe(self, event_type: type[E], handler: Callable[[E], None]) -> None: + self._handlers.setdefault(event_type, []).append(handler) + + + def publish(self, event: object) -> None: + for handler in self._handlers.get(type(event), []): + handler(event) + diff --git a/packages/mastermind-overlay/src/mastermind_overlay/events/catalog/__init__.py b/packages/mastermind-overlay/src/mastermind_overlay/events/catalog/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/packages/mastermind-overlay/src/mastermind_overlay/events/catalog/round_started.py b/packages/mastermind-overlay/src/mastermind_overlay/events/catalog/round_started.py new file mode 100644 index 0000000..02e0c3c --- /dev/null +++ b/packages/mastermind-overlay/src/mastermind_overlay/events/catalog/round_started.py @@ -0,0 +1,8 @@ +from dataclasses import dataclass + +from mastermind_overlay.run.round_tier import RoundTier + + +@dataclass(frozen=True) +class RoundStarted: + tier: RoundTier diff --git a/packages/mastermind-overlay/src/mastermind_overlay/events/catalog/shop_entered.py b/packages/mastermind-overlay/src/mastermind_overlay/events/catalog/shop_entered.py new file mode 100644 index 0000000..4b0b775 --- /dev/null +++ b/packages/mastermind-overlay/src/mastermind_overlay/events/catalog/shop_entered.py @@ -0,0 +1,6 @@ +from dataclasses import dataclass + + +@dataclass(frozen=True) +class ShopEntered: + pass diff --git a/packages/mastermind-overlay/src/mastermind_overlay/run/run_state.py b/packages/mastermind-overlay/src/mastermind_overlay/run/run_state.py index bfde852..a196430 100644 --- a/packages/mastermind-overlay/src/mastermind_overlay/run/run_state.py +++ b/packages/mastermind-overlay/src/mastermind_overlay/run/run_state.py @@ -1,4 +1,6 @@ from mastermind_overlay.economy.currency import reward_for +from mastermind_overlay.events.bus import EventBus +from mastermind_overlay.events.catalog.round_started import RoundStarted from mastermind_overlay.relics.relic import Relic from mastermind_overlay.run.gamestate import GameState from mastermind_overlay.run.round_tier import RoundTier @@ -17,6 +19,7 @@ def __init__(self) -> None: self._max_relics: int = 5 self._currency: int = 0 self._tier_idx: int = 0 + self._events: EventBus = EventBus() @property @@ -35,6 +38,10 @@ def relics(self) -> list[Relic]: def tier(self) -> RoundTier: return self._TIER_ORDER[self._tier_idx] + @property + def events(self) -> EventBus: + return self._events + @property def max_relics(self) -> int: return self._max_relics @@ -55,6 +62,7 @@ def reward_round(self, state: GameState) -> None: def new_round(self) -> GameState: stage_base = self._BASE_TARGET_SCORE * self._TARGET_SCORE_GROWTH ** (self._stage - 1) target = round(stage_base * self.tier.multiplier) + self._events.publish(RoundStarted(self.tier)) return GameState(self._BASE_MAX_TURNS, target, self._relics) diff --git a/packages/mastermind-shell/src/mastermind_shell/screens/shop.py b/packages/mastermind-shell/src/mastermind_shell/screens/shop.py index 2ab144b..a03edaf 100644 --- a/packages/mastermind-shell/src/mastermind_shell/screens/shop.py +++ b/packages/mastermind-shell/src/mastermind_shell/screens/shop.py @@ -1,6 +1,7 @@ import random from typing import override +from mastermind_overlay.events.catalog.shop_entered import ShopEntered from mastermind_overlay.relics.catalog import RELICS from mastermind_overlay.relics.relic import Relic from mastermind_overlay.run.gamestate import GameState @@ -30,6 +31,7 @@ def __init__(self, queue: ScreenQueue, renderer: Renderer, run_state: RunState, self._next_state: GameState = next_state self._offer: list[Relic] = generate_offer(run_state.relics) self._cursor: int = 0 + run_state.events.publish(ShopEntered()) @override From 0be8b4aef2eb7f21316466b03899e155abe8a6f5 Mon Sep 17 00:00:00 2001 From: TheLeBerton Date: Sat, 29 Aug 2026 01:07:35 +0200 Subject: [PATCH 3/6] ADD BossMutator base class and empty catalog folder --- .../boss_mutators/__init__.py | 0 .../boss_mutators/catalog/__init__.py | 0 .../boss_mutators/mutator.py | 25 +++++++++++++++++++ 3 files changed, 25 insertions(+) create mode 100644 packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/__init__.py create mode 100644 packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/catalog/__init__.py create mode 100644 packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/mutator.py diff --git a/packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/__init__.py b/packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/catalog/__init__.py b/packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/catalog/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/mutator.py b/packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/mutator.py new file mode 100644 index 0000000..bc4798e --- /dev/null +++ b/packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/mutator.py @@ -0,0 +1,25 @@ +from abc import ABC, abstractmethod + +from mastermind_kernel.colors import PegColor +from mastermind_kernel.feedback import CodeFeedback + +from mastermind_overlay.run.gamestate import GameState + + +class BossMutator(ABC): + @property + @abstractmethod + def description(self) -> str: ... + + + def transform_feedback(self, feedback: CodeFeedback) -> CodeFeedback: + return feedback + + + def allowed_colors(self) -> set[PegColor] | None: + return None + + + def on_round_start(self, state: GameState) -> None: + pass + From 5bee03746738df23ea847c6776667b8d7644c810 Mon Sep 17 00:00:00 2001 From: TheLeBerton Date: Sat, 29 Aug 2026 01:44:22 +0200 Subject: [PATCH 4/6] ADD ColorBan boss mutator and wire into GameState and RunState --- .../boss_mutators/catalog/__init__.py | 8 ++++++ .../boss_mutators/catalog/color_ban.py | 26 +++++++++++++++++++ .../boss_mutators/mutator.py | 6 ++++- .../src/mastermind_overlay/run/gamestate.py | 25 +++++++++++++++++- .../src/mastermind_overlay/run/run_state.py | 11 +++++++- 5 files changed, 73 insertions(+), 3 deletions(-) create mode 100644 packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/catalog/color_ban.py diff --git a/packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/catalog/__init__.py b/packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/catalog/__init__.py index e69de29..46cbbdf 100644 --- a/packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/catalog/__init__.py +++ b/packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/catalog/__init__.py @@ -0,0 +1,8 @@ +from mastermind_overlay.boss_mutators.catalog.color_ban import ColorBan +from mastermind_overlay.boss_mutators.mutator import BossMutator + + +BOSS_MUTATORS: list[type[BossMutator]] = [ + ColorBan, +] + diff --git a/packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/catalog/color_ban.py b/packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/catalog/color_ban.py new file mode 100644 index 0000000..c49e1cc --- /dev/null +++ b/packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/catalog/color_ban.py @@ -0,0 +1,26 @@ +import random +from typing import override + +from mastermind_kernel.colors import PegColor + +from mastermind_overlay.boss_mutators.mutator import BossMutator + + +class ColorBan(BossMutator): + def __init__(self) -> None: + self._banned_color: PegColor = random.choice(list(PegColor)) + + @property + def banned_color(self) -> PegColor: + return self._banned_color + + @property + @override + def description(self) -> str: + return f"{self._banned_color.name.title()} is banned this round" + + + def allowed_colors(self) -> set[PegColor] | None: + return set(PegColor) - {self._banned_color} + + diff --git a/packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/mutator.py b/packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/mutator.py index bc4798e..f15c97f 100644 --- a/packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/mutator.py +++ b/packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/mutator.py @@ -1,9 +1,13 @@ +from __future__ import annotations + from abc import ABC, abstractmethod +from typing import TYPE_CHECKING from mastermind_kernel.colors import PegColor from mastermind_kernel.feedback import CodeFeedback -from mastermind_overlay.run.gamestate import GameState +if TYPE_CHECKING: + from mastermind_overlay.run.gamestate import GameState class BossMutator(ABC): diff --git a/packages/mastermind-overlay/src/mastermind_overlay/run/gamestate.py b/packages/mastermind-overlay/src/mastermind_overlay/run/gamestate.py index 1523c75..b7a72d0 100644 --- a/packages/mastermind-overlay/src/mastermind_overlay/run/gamestate.py +++ b/packages/mastermind-overlay/src/mastermind_overlay/run/gamestate.py @@ -4,15 +4,18 @@ from mastermind_overlay.relics.relic import Relic from mastermind_overlay.scoring.pipeline import ScoringPipeline from mastermind_overlay.scoring.points import Points, apply_multiplier, compute_points +from mastermind_overlay.boss_mutators.mutator import BossMutator class GameState: - def __init__(self, max_turns: int, target_score: int, relics: list[Relic]) -> None: + def __init__(self, max_turns: int, target_score: int, relics: list[Relic], mutator: BossMutator | None = None) -> None: self._max_turns: int = max_turns self._target_score: int = target_score self._current_turn: int = 0 self._current_peg: int = 0 + self._mutator: BossMutator | None = mutator self._current_code: Code = Code.blank() + self._sanitize_current_code() self._secret_code: Code = Code.random() self._guessed_codes: list[Code] = [] self._guessed_feedback: list[CodeFeedback] = [] @@ -80,6 +83,25 @@ def add_turn(self) -> None: def cycle_color(self, direction: int) -> None: self._current_code.cycle(self._current_peg, direction) + self._enforce_allowed_color(self._current_peg, direction) + + + def _enforce_allowed_color(self, peg: int, direction: int = 1) -> None: + if self._mutator is None: + return + + allowed = self._mutator.allowed_colors() + if allowed is None: + return + + while self._current_code[peg] not in allowed: + self._current_code.cycle(peg, direction) + + + def _sanitize_current_code(self) -> None: + for peg in range(len(self._current_code)): + self._enforce_allowed_color(peg) + def cycle_current_square(self, direction: int) -> None: if direction > 0: @@ -91,6 +113,7 @@ def cycle_current_square(self, direction: int) -> None: def reset_current_guess(self) -> None: self._current_code = Code.blank() + self._sanitize_current_code() self._current_peg = 0 def reset(self) -> None: diff --git a/packages/mastermind-overlay/src/mastermind_overlay/run/run_state.py b/packages/mastermind-overlay/src/mastermind_overlay/run/run_state.py index a196430..4bdb0c2 100644 --- a/packages/mastermind-overlay/src/mastermind_overlay/run/run_state.py +++ b/packages/mastermind-overlay/src/mastermind_overlay/run/run_state.py @@ -1,9 +1,13 @@ +import random + from mastermind_overlay.economy.currency import reward_for from mastermind_overlay.events.bus import EventBus from mastermind_overlay.events.catalog.round_started import RoundStarted from mastermind_overlay.relics.relic import Relic from mastermind_overlay.run.gamestate import GameState from mastermind_overlay.run.round_tier import RoundTier +from mastermind_overlay.boss_mutators.catalog import BOSS_MUTATORS +from mastermind_overlay.boss_mutators.mutator import BossMutator class RunState: @@ -62,8 +66,13 @@ def reward_round(self, state: GameState) -> None: def new_round(self) -> GameState: stage_base = self._BASE_TARGET_SCORE * self._TARGET_SCORE_GROWTH ** (self._stage - 1) target = round(stage_base * self.tier.multiplier) + mutator: BossMutator | None = self._pick_mutator() if self.tier.is_final else None self._events.publish(RoundStarted(self.tier)) - return GameState(self._BASE_MAX_TURNS, target, self._relics) + return GameState(self._BASE_MAX_TURNS, target, self._relics, mutator) + + + def _pick_mutator(self) -> BossMutator: + return random.choice(BOSS_MUTATORS)() def spend(self, amount: int) -> None: From bbec6eb297b9ba9a2fa8ca9d22ef39c16ea23a66 Mon Sep 17 00:00:00 2001 From: TheLeBerton Date: Sat, 29 Aug 2026 01:58:39 +0200 Subject: [PATCH 5/6] ADD white-peg-hiding boss mutator --- .../boss_mutators/catalog/__init__.py | 2 ++ .../boss_mutators/catalog/white_peg_blind.py | 17 +++++++++++++++++ .../src/mastermind_overlay/run/gamestate.py | 7 +++++-- 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/catalog/white_peg_blind.py diff --git a/packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/catalog/__init__.py b/packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/catalog/__init__.py index 46cbbdf..11dd8e6 100644 --- a/packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/catalog/__init__.py +++ b/packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/catalog/__init__.py @@ -1,8 +1,10 @@ from mastermind_overlay.boss_mutators.catalog.color_ban import ColorBan +from mastermind_overlay.boss_mutators.catalog.white_peg_blind import WhitePegBlind from mastermind_overlay.boss_mutators.mutator import BossMutator BOSS_MUTATORS: list[type[BossMutator]] = [ ColorBan, + WhitePegBlind, ] diff --git a/packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/catalog/white_peg_blind.py b/packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/catalog/white_peg_blind.py new file mode 100644 index 0000000..8b2309a --- /dev/null +++ b/packages/mastermind-overlay/src/mastermind_overlay/boss_mutators/catalog/white_peg_blind.py @@ -0,0 +1,17 @@ +from typing import override + +from mastermind_kernel.feedback import CodeFeedback + +from mastermind_overlay.boss_mutators.mutator import BossMutator + + +class WhitePegBlind(BossMutator): + @property + @override + def description(self) -> str: + return "White pegs are hidden this round" + + + @override + def transform_feedback(self, feedback: CodeFeedback) -> CodeFeedback: + return feedback._replace(white_pegs=0) diff --git a/packages/mastermind-overlay/src/mastermind_overlay/run/gamestate.py b/packages/mastermind-overlay/src/mastermind_overlay/run/gamestate.py index b7a72d0..8ea4186 100644 --- a/packages/mastermind-overlay/src/mastermind_overlay/run/gamestate.py +++ b/packages/mastermind-overlay/src/mastermind_overlay/run/gamestate.py @@ -3,7 +3,7 @@ from mastermind_overlay.relics.relic import Relic from mastermind_overlay.scoring.pipeline import ScoringPipeline -from mastermind_overlay.scoring.points import Points, apply_multiplier, compute_points +from mastermind_overlay.scoring.points import apply_multiplier, compute_points from mastermind_overlay.boss_mutators.mutator import BossMutator @@ -70,7 +70,10 @@ def target_score(self) -> int: def submit_guess(self) -> None: self._guessed_codes.append(self._current_code) feedback: CodeFeedback = self._secret_code.feedback(self._current_code) - self._guessed_feedback.append(feedback) + displayed_feedback = feedback + if self._mutator is not None: + displayed_feedback = self._mutator.transform_feedback(feedback) + self._guessed_feedback.append(displayed_feedback) points, mult = self._pipeline.run(compute_points(feedback), 1, feedback) self._score += apply_multiplier(points, mult) From 1c3047ab6d1711d03c2da14cff97c44d882fef1e Mon Sep 17 00:00:00 2001 From: TheLeBerton Date: Sat, 29 Aug 2026 02:21:09 +0200 Subject: [PATCH 6/6] ADD boss announcement screen and wire mutator reveal into shop exit --- .../src/mastermind_overlay/run/gamestate.py | 5 ++++ .../curses_backend/renderer.py | 15 ++++++++++ .../src/mastermind_shell/engine/renderer.py | 1 + .../screens/boss_announcement.py | 30 +++++++++++++++++++ .../src/mastermind_shell/screens/shop.py | 5 ++++ 5 files changed, 56 insertions(+) create mode 100644 packages/mastermind-shell/src/mastermind_shell/screens/boss_announcement.py diff --git a/packages/mastermind-overlay/src/mastermind_overlay/run/gamestate.py b/packages/mastermind-overlay/src/mastermind_overlay/run/gamestate.py index 8ea4186..1efbac2 100644 --- a/packages/mastermind-overlay/src/mastermind_overlay/run/gamestate.py +++ b/packages/mastermind-overlay/src/mastermind_overlay/run/gamestate.py @@ -67,6 +67,11 @@ def game_over(self) -> bool: def target_score(self) -> int: return self._target_score + @property + def mutator(self) -> BossMutator | None: + return self._mutator + + def submit_guess(self) -> None: self._guessed_codes.append(self._current_code) feedback: CodeFeedback = self._secret_code.feedback(self._current_code) diff --git a/packages/mastermind-shell/src/mastermind_shell/curses_backend/renderer.py b/packages/mastermind-shell/src/mastermind_shell/curses_backend/renderer.py index 816416d..998cf12 100644 --- a/packages/mastermind-shell/src/mastermind_shell/curses_backend/renderer.py +++ b/packages/mastermind-shell/src/mastermind_shell/curses_backend/renderer.py @@ -242,3 +242,18 @@ def _draw_shop_cards(self, items: list[tuple[str, str, int]], selected: int, rel self._stdscr.addstr(y_pos + card_height - 2, x + (card_width - len(price_str)) // 2, price_str, text_attr) x += card_width + 1 + + + def render_boss_announcement(self, title: str, description: str) -> None: + self._stdscr.clear() + self._stdscr.addstr( + curses.LINES // 2, + (curses.COLS - len(title)) // 2, + title, + curses.A_STANDOUT) + self._stdscr.addstr( + curses.LINES // 2 + 1, + (curses.COLS - len(description)) // 2, + description, + curses.A_STANDOUT) + self._stdscr.refresh() diff --git a/packages/mastermind-shell/src/mastermind_shell/engine/renderer.py b/packages/mastermind-shell/src/mastermind_shell/engine/renderer.py index da48ab3..9a2ee46 100644 --- a/packages/mastermind-shell/src/mastermind_shell/engine/renderer.py +++ b/packages/mastermind-shell/src/mastermind_shell/engine/renderer.py @@ -13,3 +13,4 @@ def render_gameplay(self, state: GameState, stage: int, tier_label: str, relics: def render_win(self, correct_guess_str: str) -> None: ... def render_loose(self, correct_guess_str: str) -> None: ... def render_shop(self, currency: int, offer: list[Relic], extra_guess_price: int, selected: int, owned_relics: list[Relic], max_relics: int) -> None: ... + def render_boss_announcement(self, title: str, description: str) -> None: ... diff --git a/packages/mastermind-shell/src/mastermind_shell/screens/boss_announcement.py b/packages/mastermind-shell/src/mastermind_shell/screens/boss_announcement.py new file mode 100644 index 0000000..6a2e1fd --- /dev/null +++ b/packages/mastermind-shell/src/mastermind_shell/screens/boss_announcement.py @@ -0,0 +1,30 @@ +from typing import override + +from mastermind_overlay.boss_mutators.mutator import BossMutator +from mastermind_shell.engine.input_event import InputEvent +from mastermind_shell.engine.renderer import Renderer +from mastermind_shell.engine.screen import Screen +from mastermind_shell.engine.screen_queue import ScreenQueue +from mastermind_shell.engine.transition import ScreenTransition + + +class BossAnnouncementScreen(Screen): + def __init__(self, queue: ScreenQueue, renderer: Renderer, mutator: BossMutator) -> None: + super().__init__(queue, True) + self._renderer: Renderer = renderer + self._mutator: BossMutator = mutator + + + @override + def handle_input(self, event: InputEvent) -> None: + self._queue.push(ScreenTransition.pop()) + + + @override + def update(self) -> None: + pass + + + @override + def render(self) -> None: + self._renderer.render_boss_announcement("BOSS ROUND", self._mutator.description) diff --git a/packages/mastermind-shell/src/mastermind_shell/screens/shop.py b/packages/mastermind-shell/src/mastermind_shell/screens/shop.py index a03edaf..f41e30a 100644 --- a/packages/mastermind-shell/src/mastermind_shell/screens/shop.py +++ b/packages/mastermind-shell/src/mastermind_shell/screens/shop.py @@ -12,6 +12,7 @@ from mastermind_shell.engine.screen import Screen from mastermind_shell.engine.screen_queue import ScreenQueue from mastermind_shell.engine.transition import ScreenTransition +from mastermind_shell.screens.boss_announcement import BossAnnouncementScreen _OFFER_SIZE: int = 3 _EXTRA_GUESS_PRICE: int = 8 @@ -53,6 +54,10 @@ def handle_input(self, event: InputEvent) -> None: self._buy_extra_guess() else: self._queue.push(ScreenTransition.pop()) + if self._next_state.mutator is not None: + self._queue.push(ScreenTransition.push( + BossAnnouncementScreen(self._queue, self._renderer, self._next_state.mutator) + )) if event == InputEvent.QUIT: self._queue.push(ScreenTransition.pop())