nodes/hud.pyΒΆ
Part of Tanks of Freedom.
1"""HUD: top status bar + right-side info panel + end-turn button."""
2
3from __future__ import annotations
4
5from simvx.core import Node2D, Signal
6from simvx.core.ui.widgets import Button, Label
7
8from .data import (
9 BUILDING_STATS,
10 HUD_HEIGHT,
11 INFO_PANEL_W,
12 PLAYER_COLOUR,
13 PLAYER_NAME,
14 UNIT_STATS,
15 WINDOW_HEIGHT,
16 WINDOW_WIDTH,
17)
18
19
20class Hud(Node2D):
21 def __init__(self, **kwargs):
22 super().__init__(name="Hud", **kwargs)
23 self.end_turn_pressed = Signal()
24 self.new_game_pressed = Signal()
25
26 # Label children so we can update them live.
27 self._turn_label = Label("Turn 1: BLUE")
28 self._turn_label.position = (16, 16)
29 self._turn_label.size = (300, 28)
30 self._turn_label.font_size = 22.0
31 self._turn_label.text_colour = (1.0, 1.0, 1.0, 1.0)
32 self.add_child(self._turn_label)
33
34 self._status_label = Label("")
35 self._status_label.position = (340, 18)
36 self._status_label.size = (700, 22)
37 self._status_label.font_size = 16.0
38 self._status_label.text_colour = (0.9, 0.92, 0.95, 1.0)
39 self.add_child(self._status_label)
40
41 # End-turn button (top right of the top bar).
42 self._end_btn = Button("End Turn")
43 self._end_btn.position = (WINDOW_WIDTH - INFO_PANEL_W - 130, 12)
44 self._end_btn.size = (120, 36)
45 self._end_btn.font_size = 16.0
46 self._end_btn.pressed.connect(lambda: self.end_turn_pressed())
47 self.add_child(self._end_btn)
48
49 # Right-hand info panel labels (unit selection / building info).
50 panel_x = WINDOW_WIDTH - INFO_PANEL_W + 12
51 self._panel_title = Label("")
52 self._panel_title.position = (panel_x, HUD_HEIGHT + 14)
53 self._panel_title.size = (INFO_PANEL_W - 24, 28)
54 self._panel_title.font_size = 22.0
55 self._panel_title.text_colour = (1.0, 1.0, 1.0, 1.0)
56 self.add_child(self._panel_title)
57
58 self._panel_lines: list[Label] = []
59 for i in range(8):
60 lbl = Label("")
61 lbl.position = (panel_x, HUD_HEIGHT + 50 + i * 22)
62 lbl.size = (INFO_PANEL_W - 24, 22)
63 lbl.font_size = 14.0
64 lbl.text_colour = (0.85, 0.88, 0.92, 1.0)
65 self.add_child(lbl)
66 self._panel_lines.append(lbl)
67
68 self._victory_label = Label("")
69 self._victory_label.position = (WINDOW_WIDTH / 2 - 220, WINDOW_HEIGHT / 2 - 30)
70 self._victory_label.size = (440, 60)
71 self._victory_label.font_size = 36.0
72 self._victory_label.text_colour = (1.0, 1.0, 1.0, 1.0)
73 self._victory_label.alignment = "center"
74 self.add_child(self._victory_label)
75
76 self._restart_btn = Button("Play Again")
77 self._restart_btn.position = (WINDOW_WIDTH / 2 - 75, WINDOW_HEIGHT / 2 + 30)
78 self._restart_btn.size = (150, 40)
79 self._restart_btn.font_size = 16.0
80 self._restart_btn.visible = False
81 self._restart_btn.pressed.connect(lambda: self.new_game_pressed())
82 self.add_child(self._restart_btn)
83
84 # ----------------------------------------------------------- updates
85 def set_turn(self, player: int, turn_no: int) -> None:
86 col = PLAYER_COLOUR[player]
87 self._turn_label.text = f"Turn {turn_no}: {PLAYER_NAME[player]}"
88 self._turn_label.text_colour = (col[0], col[1], col[2], 1.0)
89
90 def set_status(self, msg: str) -> None:
91 self._status_label.text = msg
92
93 def set_unit_panel(self, unit) -> None:
94 if unit is None:
95 self._panel_title.text = ""
96 for lbl in self._panel_lines:
97 lbl.text = ""
98 return
99 stats = UNIT_STATS[unit.type]
100 col = PLAYER_COLOUR[unit.owner]
101 self._panel_title.text = stats["name"]
102 self._panel_title.text_colour = (col[0], col[1], col[2], 1.0)
103 lines = [
104 f"Owner : {PLAYER_NAME[unit.owner]}",
105 f"HP : {unit.life} / {unit.max_life}",
106 f"Attack : {stats['attack']}",
107 f"AP : {unit.ap} / {unit.max_ap}",
108 f"Capture : {'yes' if unit.can_capture else 'no'}",
109 "",
110 "Click highlighted tile to move",
111 "Click red tile to attack",
112 ]
113 for lbl, text in zip(self._panel_lines, lines, strict=False):
114 lbl.text = text
115 for lbl in self._panel_lines[len(lines) :]:
116 lbl.text = ""
117
118 def set_building_panel(self, building) -> None:
119 if building is None:
120 return
121 stats = BUILDING_STATS[building.type]
122 col = PLAYER_COLOUR[building.owner]
123 self._panel_title.text = stats["name"]
124 self._panel_title.text_colour = (col[0], col[1], col[2], 1.0)
125 lines = [
126 f"Owner : {PLAYER_NAME[building.owner]}",
127 f"Role : {'win condition' if building.is_hq else 'territory'}",
128 "",
129 "Capture by ending an infantry",
130 "move on this tile.",
131 ]
132 for lbl, text in zip(self._panel_lines, lines, strict=False):
133 lbl.text = text
134 for lbl in self._panel_lines[len(lines) :]:
135 lbl.text = ""
136
137 def show_victory(self, winner: int) -> None:
138 col = PLAYER_COLOUR[winner]
139 self._victory_label.text = f"{PLAYER_NAME[winner]} WINS"
140 self._victory_label.text_colour = (col[0], col[1], col[2], 1.0)
141 self._restart_btn.visible = True
142
143 def hide_victory(self) -> None:
144 self._victory_label.text = ""
145 self._restart_btn.visible = False
146
147 # -------------------------------------------------------------- draw
148 def on_draw(self, renderer) -> None:
149 # Top bar
150 renderer.draw_rect((0, 0), (WINDOW_WIDTH, HUD_HEIGHT), colour=(0.10, 0.13, 0.18, 0.95), filled=True)
151 renderer.draw_rect((0, HUD_HEIGHT - 2), (WINDOW_WIDTH, 2), colour=(0.30, 0.40, 0.55, 1.0), filled=True)
152 # Right info panel
153 renderer.draw_rect(
154 (WINDOW_WIDTH - INFO_PANEL_W, HUD_HEIGHT),
155 (INFO_PANEL_W, WINDOW_HEIGHT - HUD_HEIGHT),
156 colour=(0.10, 0.13, 0.18, 0.92),
157 filled=True,
158 )
159 renderer.draw_rect(
160 (WINDOW_WIDTH - INFO_PANEL_W, HUD_HEIGHT),
161 (2, WINDOW_HEIGHT - HUD_HEIGHT),
162 colour=(0.30, 0.40, 0.55, 1.0),
163 filled=True,
164 )