nodes/menu.pyΒΆ
Part of SNKRX.
1"""SNKRX title screen: neon block logo + start prompt."""
2
3from __future__ import annotations
4
5from simvx.core import Input, MouseButton, Node, Signal
6
7from .colours import BG2, BLUE, FG, GREEN, GREY, RED, YELLOW
8
9
10class TitleScreen(Node):
11 start = Signal()
12
13 # ``on_draw`` animates every frame from ``self._t`` (scrolling horizon
14 # stripes + pulsing prompt), so it genuinely produces new geometry per
15 # frame. Declare it dynamic rather than leaning on view-decoupling.
16 dynamic = True
17
18 def __init__(self, **kwargs):
19 super().__init__(name="TitleScreen", **kwargs)
20 self._t = 0.0
21
22 def on_update(self, dt: float):
23 self._t += dt
24 # Keyboard or click/tap: a touch-only player starts the same way.
25 if Input.is_action_just_pressed("start") or Input.is_mouse_button_just_pressed(MouseButton.LEFT):
26 self.start.emit()
27
28 def on_draw(self, renderer):
29 if self.tree is not None:
30 w, h = self.tree.screen_size
31 else:
32 w, h = 1280, 720
33 # Solid dark background
34 renderer.draw_rect((0, 0), (w, h), colour=(0.06, 0.07, 0.09, 1.0), filled=True)
35 # Subtle horizon stripes
36 for i in range(20):
37 y = (i * 38 + int(self._t * 14)) % h
38 renderer.draw_rect((0, y), (w, 1), colour=(0.12, 0.14, 0.18, 1.0), filled=True)
39
40 # Title: fake neon stack
41 title = "SNKRX"
42 for offset, c in [
43 ((6, 6), (RED[0], RED[1], RED[2], 0.45)),
44 ((-6, -6), (GREEN[0], GREEN[1], GREEN[2], 0.45)),
45 ((0, 0), FG),
46 ]:
47 tw = renderer.text_width(title, 14)
48 renderer.draw_text(title, (w // 2 - tw // 2 + offset[0], 180 + offset[1]), scale=14, colour=c)
49
50 # Subtitle
51 sub = "snake auto-battler arena roguelite"
52 sw = renderer.text_width(sub, 2)
53 renderer.draw_text(sub, (w // 2 - sw // 2, 320), scale=2, colour=GREY)
54
55 # Class chips
56 chips = [
57 ("WARRIOR", YELLOW, "melee swipe"),
58 ("ARCHER", GREEN, "pierce shot"),
59 ("MAGE", BLUE, "AOE bolt"),
60 ]
61 chip_w = 220
62 gap = 30
63 total = len(chips) * chip_w + (len(chips) - 1) * gap
64 x0 = w // 2 - total // 2
65 for i, (label, colour, desc) in enumerate(chips):
66 x = x0 + i * (chip_w + gap)
67 renderer.draw_rect((x, 380), (chip_w, 110), colour=BG2, filled=True)
68 renderer.draw_rect((x, 380), (chip_w, 110), colour=colour, filled=False, thickness=2.0)
69 lw = renderer.text_width(label, 3)
70 renderer.draw_text(label, (x + chip_w // 2 - lw // 2, 405), scale=3, colour=colour)
71 dw = renderer.text_width(desc, 2)
72 renderer.draw_text(desc, (x + chip_w // 2 - dw // 2, 450), scale=2, colour=FG)
73
74 # Controls strip
75 bottom = h - 56
76 renderer.draw_rect((0, bottom), (w, 56), colour=(0.85, 0.85, 0.88, 1.0), filled=True)
77 controls = "MOUSE / A D STEER CLICK / ENTER START ESC QUIT"
78 cw = renderer.text_width(controls, 2)
79 renderer.draw_text(controls, (w // 2 - cw // 2, bottom + 20), scale=2, colour=(0.10, 0.10, 0.12, 1.0))
80
81 # Pulse prompt
82 if int(self._t * 2) % 2 == 0:
83 prompt = "CLICK OR ENTER TO BEGIN"
84 pw = renderer.text_width(prompt, 4)
85 renderer.draw_text(prompt, (w // 2 - pw // 2, 560), scale=4, colour=YELLOW)