|
2 | 2 |
|
3 | 3 | from __future__ import annotations |
4 | 4 |
|
5 | | -from collections.abc import Callable |
6 | | -from typing import Final, Protocol |
7 | | - |
8 | 5 | import attrs |
9 | 6 | import tcod.console |
10 | 7 | import tcod.constants |
11 | 8 | import tcod.event |
12 | 9 | from tcod.event import KeySym |
13 | 10 |
|
14 | 11 | import g |
15 | | -import game.state_tools |
| 12 | +import game.menus |
16 | 13 | import game.world_tools |
17 | 14 | from game.components import Gold, Graphic, Position |
18 | | -from game.state import Pop, Push, Reset, State, StateResult |
| 15 | +from game.constants import DIRECTION_KEYS |
| 16 | +from game.state import Push, Reset, State, StateResult |
19 | 17 | from game.tags import IsItem, IsPlayer |
20 | 18 |
|
21 | | -DIRECTION_KEYS: Final = { |
22 | | - # Arrow keys |
23 | | - KeySym.LEFT: (-1, 0), |
24 | | - KeySym.RIGHT: (1, 0), |
25 | | - KeySym.UP: (0, -1), |
26 | | - KeySym.DOWN: (0, 1), |
27 | | - # Arrow key diagonals |
28 | | - KeySym.HOME: (-1, -1), |
29 | | - KeySym.END: (-1, 1), |
30 | | - KeySym.PAGEUP: (1, -1), |
31 | | - KeySym.PAGEDOWN: (1, 1), |
32 | | - # Keypad |
33 | | - KeySym.KP_4: (-1, 0), |
34 | | - KeySym.KP_6: (1, 0), |
35 | | - KeySym.KP_8: (0, -1), |
36 | | - KeySym.KP_2: (0, 1), |
37 | | - KeySym.KP_7: (-1, -1), |
38 | | - KeySym.KP_1: (-1, 1), |
39 | | - KeySym.KP_9: (1, -1), |
40 | | - KeySym.KP_3: (1, 1), |
41 | | - # VI keys |
42 | | - KeySym.h: (-1, 0), |
43 | | - KeySym.l: (1, 0), |
44 | | - KeySym.k: (0, -1), |
45 | | - KeySym.j: (0, 1), |
46 | | - KeySym.y: (-1, -1), |
47 | | - KeySym.b: (-1, 1), |
48 | | - KeySym.u: (1, -1), |
49 | | - KeySym.n: (1, 1), |
50 | | -} |
51 | | - |
52 | 19 |
|
53 | 20 | @attrs.define(eq=False) |
54 | 21 | class InGame(State): |
@@ -87,105 +54,19 @@ def on_draw(self, console: tcod.console.Console) -> None: |
87 | 54 | console.print(x=0, y=console.height - 1, string=text, fg=(255, 255, 255), bg=(0, 0, 0)) |
88 | 55 |
|
89 | 56 |
|
90 | | -class MenuItem(Protocol): |
91 | | - """Menu item protocol.""" |
92 | | - |
93 | | - __slots__ = () |
94 | | - |
95 | | - def on_event(self, event: tcod.event.Event) -> StateResult: |
96 | | - """Handle events passed to the menu item.""" |
97 | | - |
98 | | - def on_draw(self, console: tcod.console.Console, x: int, y: int, highlight: bool) -> None: |
99 | | - """Draw is item at the given position.""" |
100 | | - |
101 | | - |
102 | | -@attrs.define() |
103 | | -class SelectItem(MenuItem): |
104 | | - """Clickable menu item.""" |
105 | | - |
106 | | - label: str |
107 | | - callback: Callable[[], StateResult] |
108 | | - |
109 | | - def on_event(self, event: tcod.event.Event) -> StateResult: |
110 | | - """Handle events selecting this item.""" |
111 | | - match event: |
112 | | - case tcod.event.KeyDown(sym=sym) if sym in {KeySym.RETURN, KeySym.RETURN2, KeySym.KP_ENTER}: |
113 | | - return self.callback() |
114 | | - case tcod.event.MouseButtonUp(button=tcod.event.MouseButton.LEFT): |
115 | | - return self.callback() |
116 | | - case _: |
117 | | - return None |
118 | | - |
119 | | - def on_draw(self, console: tcod.console.Console, x: int, y: int, highlight: bool) -> None: |
120 | | - """Render this items label.""" |
121 | | - console.print(x, y, self.label, fg=(255, 255, 255), bg=(64, 64, 64) if highlight else (0, 0, 0)) |
122 | | - |
123 | | - |
124 | | -@attrs.define(eq=False) |
125 | | -class ListMenu(State): |
126 | | - """Simple list menu state.""" |
127 | | - |
128 | | - items: tuple[MenuItem, ...] |
129 | | - selected: int | None = 0 |
130 | | - x: int = 0 |
131 | | - y: int = 0 |
132 | | - |
133 | | - def on_event(self, event: tcod.event.Event) -> StateResult: |
134 | | - """Handle events for menus.""" |
135 | | - match event: |
136 | | - case tcod.event.Quit(): |
137 | | - raise SystemExit() |
138 | | - case tcod.event.KeyDown(sym=sym) if sym in DIRECTION_KEYS: |
139 | | - dx, dy = DIRECTION_KEYS[sym] |
140 | | - if dx != 0 or dy == 0: |
141 | | - return self.activate_selected(event) |
142 | | - if self.selected is not None: |
143 | | - self.selected += dy |
144 | | - self.selected %= len(self.items) |
145 | | - else: |
146 | | - self.selected = 0 if dy == 1 else len(self.items) - 1 |
147 | | - return None |
148 | | - case tcod.event.MouseMotion(position=(_, y)): |
149 | | - y -= self.y |
150 | | - self.selected = y if 0 <= y < len(self.items) else None |
151 | | - return None |
152 | | - case tcod.event.KeyDown(sym=KeySym.ESCAPE): |
153 | | - return self.on_cancel() |
154 | | - case tcod.event.MouseButtonUp(button=tcod.event.MouseButton.RIGHT): |
155 | | - return self.on_cancel() |
156 | | - case _: |
157 | | - return self.activate_selected(event) |
158 | | - |
159 | | - def activate_selected(self, event: tcod.event.Event) -> StateResult: |
160 | | - """Call the selected menu items callback.""" |
161 | | - if self.selected is not None: |
162 | | - return self.items[self.selected].on_event(event) |
163 | | - return None |
164 | | - |
165 | | - def on_cancel(self) -> StateResult: |
166 | | - """Handle escape or right click being pressed on menus.""" |
167 | | - return Pop() |
168 | | - |
169 | | - def on_draw(self, console: tcod.console.Console) -> None: |
170 | | - """Render the menu.""" |
171 | | - game.state_tools.draw_previous_state(self, console) |
172 | | - for i, item in enumerate(self.items): |
173 | | - item.on_draw(console, x=self.x, y=self.y + i, highlight=i == self.selected) |
174 | | - |
175 | | - |
176 | | -class MainMenu(ListMenu): |
| 57 | +class MainMenu(game.menus.ListMenu): |
177 | 58 | """Main/escape menu.""" |
178 | 59 |
|
179 | 60 | __slots__ = () |
180 | 61 |
|
181 | 62 | def __init__(self) -> None: |
182 | 63 | """Initialize the main menu.""" |
183 | 64 | items = [ |
184 | | - SelectItem("New game", self.new_game), |
185 | | - SelectItem("Quit", self.quit), |
| 65 | + game.menus.SelectItem("New game", self.new_game), |
| 66 | + game.menus.SelectItem("Quit", self.quit), |
186 | 67 | ] |
187 | 68 | if hasattr(g, "world"): |
188 | | - items.insert(0, SelectItem("Continue", self.continue_)) |
| 69 | + items.insert(0, game.menus.SelectItem("Continue", self.continue_)) |
189 | 70 |
|
190 | 71 | super().__init__( |
191 | 72 | items=tuple(items), |
|
0 commit comments