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, Mouse, 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.just_pressed("start") or Input.is_mouse_button_just_pressed(Mouse.LEFT):
26 self.start.emit()
27
28 def on_draw(self, renderer):
29 w, h = self.viewport_size
30 # Solid dark background
31 renderer.rect((0, 0), (w, h), fill=(0.06, 0.07, 0.09, 1.0))
32 # Subtle horizon stripes
33 for i in range(20):
34 y = (i * 38 + int(self._t * 14)) % h
35 renderer.rect((0, y), (w, 1), fill=(0.12, 0.14, 0.18, 1.0))
36
37 # Title: fake neon stack
38 title = "SNKRX"
39 for offset, c in [
40 ((6, 6), (RED[0], RED[1], RED[2], 0.45)),
41 ((-6, -6), (GREEN[0], GREEN[1], GREEN[2], 0.45)),
42 ((0, 0), FG),
43 ]:
44 tw = renderer.text_width(title, 14)
45 renderer.text(title, (w // 2 - tw // 2 + offset[0], 180 + offset[1]), fill=c, scale=14)
46
47 # Subtitle
48 sub = "snake auto-battler arena roguelite"
49 sw = renderer.text_width(sub, 2)
50 renderer.text(sub, (w // 2 - sw // 2, 320), fill=GREY, scale=2)
51
52 # Class chips
53 chips = [
54 ("WARRIOR", YELLOW, "melee swipe"),
55 ("ARCHER", GREEN, "pierce shot"),
56 ("MAGE", BLUE, "AOE bolt"),
57 ]
58 chip_w = 220
59 gap = 30
60 total = len(chips) * chip_w + (len(chips) - 1) * gap
61 x0 = w // 2 - total // 2
62 for i, (label, colour, desc) in enumerate(chips):
63 x = x0 + i * (chip_w + gap)
64 renderer.rect((x, 380), (chip_w, 110), fill=BG2)
65 renderer.rect((x, 380), (chip_w, 110), stroke=colour, width=2.0)
66 lw = renderer.text_width(label, 3)
67 renderer.text(label, (x + chip_w // 2 - lw // 2, 405), fill=colour, scale=3)
68 dw = renderer.text_width(desc, 2)
69 renderer.text(desc, (x + chip_w // 2 - dw // 2, 450), fill=FG, scale=2)
70
71 # Controls strip
72 bottom = h - 56
73 renderer.rect((0, bottom), (w, 56), fill=(0.85, 0.85, 0.88, 1.0))
74 controls = "MOUSE / A D STEER CLICK / ENTER START ESC QUIT"
75 cw = renderer.text_width(controls, 2)
76 renderer.text(controls, (w // 2 - cw // 2, bottom + 20), fill=(0.10, 0.10, 0.12, 1.0), scale=2)
77
78 # Pulse prompt
79 if int(self._t * 2) % 2 == 0:
80 prompt = "CLICK OR ENTER TO BEGIN"
81 pw = renderer.text_width(prompt, 4)
82 renderer.text(prompt, (w // 2 - pw // 2, 560), fill=YELLOW, scale=4)