nodes/menu.pyΒΆ
Part of Q1K3.
1"""Title screen: the port's entry point and its pause screen.
2
3The game grabs the pointer for mouse-look, so it must never do that behind the
4player's back. The title screen owns that decision: it lists the controls,
5offers a keyboard route and a pointer-only route, and captures the pointer only
6once the player has picked one. Escape during play brings it back, which is
7also how the pointer is released.
8"""
9
10from __future__ import annotations
11
12from typing import TYPE_CHECKING
13
14from simvx.core import (
15 Anchor,
16 Button,
17 Control,
18 Label,
19 Panel,
20 VBoxContainer,
21)
22
23if TYPE_CHECKING: # pragma: no cover
24 from .root import Q1K3Root
25
26CONTROLS = [
27 ("MOVE", "W A S D or ARROWS"),
28 ("LOOK", "MOUSE"),
29 ("FIRE", "LEFT MOUSE"),
30 ("JUMP", "SPACE or RIGHT MOUSE"),
31 ("WEAPON", "Q / E or MOUSEWHEEL"),
32 ("MENU", "ESC"),
33]
34
35
36class TitleScreen(Control):
37 """Full-rect menu shown at boot and whenever Escape leaves the game.
38
39 Purely button-driven: UI events are routed even while the tree is paused,
40 which is what lets this screen un-pause the game it is covering.
41 """
42
43 def __init__(self, game: Q1K3Root) -> None:
44 super().__init__()
45 self.game = game
46 self.anchor = Anchor.FULL_RECT
47
48 backdrop = Panel(anchor=Anchor.FULL_RECT)
49 backdrop.bg_colour = (0.02, 0.02, 0.03, 0.85)
50 self.add_child(backdrop)
51
52 title = Label(text="Q1K3", font_size=72.0, anchor=Anchor.TOP_WIDE, size=(0, 0), inset=(0, 48, 0, 0))
53 title.alignment = "centre"
54 self.add_child(title)
55
56 tagline = Label(
57 text="A SIMVX PORT OF THE 13 KB QUAKE",
58 font_size=20.0,
59 anchor=Anchor.TOP_WIDE,
60 size=(0, 0),
61 inset=(0, 136, 0, 0),
62 )
63 tagline.alignment = "centre"
64 self.add_child(tagline)
65
66 rows = VBoxContainer(anchor=Anchor.CENTER, size=(520, 180), inset=(0, 0, 0, 60))
67 rows.alignment = "centre"
68 rows.separation = 6.0
69 # Every row is padded to the same width, so centring a monospaced font
70 # lines the two columns up without a grid container.
71 for action, keys in CONTROLS:
72 row = Label(text=f"{action:>6} {keys:<24}", font_size=18.0)
73 row.alignment = "centre"
74 rows.add_child(row)
75 self.add_child(rows)
76
77 buttons = VBoxContainer(anchor=Anchor.CENTER_BOTTOM, size=(440, 132), inset=(0, 0, 0, 48))
78 buttons.alignment = "centre"
79 buttons.separation = 12.0
80 play = Button(text="PLAY (mouse + keyboard)")
81 play.pressed.connect(lambda: self.game.start_game(pointer_only=False))
82 pointer = Button(text="TOUCH / MOUSE ONLY (on-screen controls)")
83 pointer.pressed.connect(lambda: self.game.start_game(pointer_only=True))
84 quit_button = Button(text="QUIT")
85 quit_button.pressed.connect(self.game.quit_game)
86 for button in (play, pointer, quit_button):
87 buttons.add_child(button)
88 self.add_child(buttons)