nodes/hud.py¶
Part of Dodge the Creeps.
1"""HUD: anchored Label widgets for score / message / start prompt."""
2
3from __future__ import annotations
4
5from simvx.core import Input, Node, Signal, Timer, Vec2
6from simvx.core.ui import AnchorPreset, Control, Label
7
8
9class HUD(Control):
10 """Top-of-screen score, centred message, bottom restart prompt.
11
12 Faithful to upstream `hud.gd`: shows "Get Ready" briefly at game start,
13 "Game Over" then "Dodge the Creeps" + a restart prompt at game over,
14 and emits `start_game` when the player answers that prompt.
15 """
16
17 start_game = Signal()
18
19 MESSAGE_FADE_SEC = 2.0
20 GAME_OVER_HOLD_SEC = 1.0
21
22 def __init__(self, **kwargs):
23 super().__init__(name="HUD", **kwargs)
24 self.set_anchor_preset(AnchorPreset.FULL_RECT)
25
26 # Score label: top-centre, anchored to TOP_WIDE so it scales with the
27 # window width and stays centred horizontally.
28 self.score_label = self.add_child(Label("0", name="ScoreLabel"))
29 self.score_label.set_anchor_preset(AnchorPreset.TOP_WIDE)
30 self.score_label.font_size = 56
31 self.score_label.alignment = "center"
32 self.score_label.text_colour = (1.0, 1.0, 1.0, 1.0)
33 self.score_label.size = Vec2(0, 78)
34 self.score_label.margin_top = 12
35
36 # Splash + restart prompt rendered by Main.on_draw via fit-scaled
37 # draw_text (the Label widget's text_width doesn't handle newlines
38 # or wider-than-rect text, so it renders left-shifted at default size).
39 # We expose plain string state here; Main reads .message_text and
40 # .show_prompt to render them.
41 self.message_text: str = "Dodge the Creeps"
42 self.message_visible: bool = True
43 self.show_prompt: bool = True
44
45 self.message_timer = self.add_child(Timer(self.MESSAGE_FADE_SEC, one_shot=True, name="MessageTimer"))
46 self.message_timer.timeout.connect(self._hide_message)
47
48 def on_update(self, dt: float) -> None:
49 # The prompt is the only thing that arms a (re)start, so the HUD owns
50 # the input that answers it and reports back through `start_game`.
51 if self.show_prompt and (
52 Input.is_action_just_pressed("start_game") or Input.is_action_just_pressed("restart_click")
53 ):
54 self.start_game()
55
56 # ------------------------------------------------------------------
57 # Public API mirroring hud.gd
58 # ------------------------------------------------------------------
59
60 def update_score(self, score: int) -> None:
61 self.score_label.text = str(score)
62
63 def show_message(self, text: str) -> None:
64 self.message_text = text
65 self.message_visible = True
66 self.message_timer.start()
67
68 def show_game_over(self) -> Node:
69 """Run the `Game Over` → splash → restart-prompt sequence as a coroutine."""
70 return self.start_coroutine(self._game_over_sequence())
71
72 def _game_over_sequence(self):
73 from simvx.core import wait, wait_signal
74
75 self.show_message("Game Over")
76 yield from wait_signal(self.message_timer.timeout)
77
78 self.message_text = "Dodge the Creeps"
79 self.message_visible = True
80 yield from wait(self.GAME_OVER_HOLD_SEC)
81
82 self.show_prompt = True
83
84 def hide_start_prompt(self) -> None:
85 self.show_prompt = False
86
87 def _hide_message(self) -> None:
88 self.message_visible = False