nodes/death_screen.pyΒΆ
Part of Dungeon Explorer.
1"""Death / respawn overlay: red vignette, stat summary, pulsing respawn prompt."""
2
3import math
4
5from simvx.core import Control, Property
6from simvx.core.input import MouseButton
7from simvx.core.ui.enums import AnchorPreset
8
9
10class DeathScreen(Control):
11 """Full-screen death overlay with red vignette and stats.
12
13 No back button. Esc / outside click is suppressed (player must press
14 Space or click anywhere after the 2 second reveal). The respawn action
15 is delivered via the ``on_respawn`` callback supplied at construction.
16 """
17
18 visible = Property(
19 False,
20 coerce=bool,
21 hint="Whether this node and its subtree are drawn",
22 on_change="_on_visible_changed",
23 )
24
25 # on_draw animates every frame off self._timer (fade-in alpha, staggered
26 # reveals, pulsing respawn prompt) -> retained 2D must re-run it each frame.
27 dynamic = True
28
29 def __init__(self, on_respawn=None, **kwargs):
30 super().__init__(name="DeathScreen", **kwargs)
31 self.set_anchor_preset(AnchorPreset.FULL_RECT)
32
33 self._on_respawn = on_respawn
34 self._timer = 0.0
35 self._player = None
36 self._dungeon_level = 1
37 self._gold_lost = 0
38
39 # -- Public API --
40
41 def set_context(self, player=None, dungeon_level: int = 1) -> None:
42 self._player = player
43 self._dungeon_level = dungeon_level
44
45 def show(self):
46 self._timer = 0.0
47 if self._player:
48 self._gold_lost = max(1, self._player.gold // 10)
49 self.show_overlay("blocking", dismiss=False)
50
51 # -- Frame update / input --
52
53 def on_update(self, dt: float):
54 self._timer += dt
55
56 def _on_gui_input(self, event):
57 if not self.visible:
58 return
59 # Space / Enter / mouse-press after 2s reveal = respawn.
60 if self._timer < 2.0:
61 return
62 if event.button == MouseButton.LEFT and event.pressed:
63 self._respawn()
64 event.handled = True
65 elif event.key and event.pressed and event.key in ("space", "enter", "return", "e"):
66 self._respawn()
67 event.handled = True
68
69 def _respawn(self):
70 self.cancel_requested.emit()
71 self.close_overlay()
72 if self._on_respawn:
73 self._on_respawn()
74
75 # -- Rendering --
76
77 def _screen_size(self) -> tuple[float, float]:
78 if self.tree is not None:
79 sw, sh = self.tree.screen_size
80 return float(sw), float(sh)
81 return 1280.0, 720.0
82
83 def on_draw(self, renderer):
84 if not self.visible:
85 return
86 sw, sh = self._screen_size()
87 alpha = min(1.0, self._timer / 1.0)
88
89 # Dark red background
90 renderer.draw_rect((0, 0), (sw, sh), colour=(0.12, 0.0, 0.0, 0.85 * alpha), filled=True)
91
92 # Red vignette edges
93 vig_a = 0.5 * alpha
94 vig_w = 80.0
95 renderer.draw_rect((0, 0), (vig_w, sh), colour=(0.3, 0.0, 0.0, vig_a), filled=True)
96 renderer.draw_rect((sw - vig_w, 0), (vig_w, sh), colour=(0.3, 0.0, 0.0, vig_a), filled=True)
97 renderer.draw_rect((0, 0), (sw, vig_w), colour=(0.3, 0.0, 0.0, vig_a), filled=True)
98 renderer.draw_rect((0, sh - vig_w), (sw, vig_w), colour=(0.3, 0.0, 0.0, vig_a), filled=True)
99
100 cx = sw / 2
101
102 if self._timer > 0.5:
103 text_a = min(1.0, (self._timer - 0.5) * 2.0)
104 renderer.draw_text("YOU DIED", (cx - 60 + 2, 202), scale=3.0, colour=(0.0, 0.0, 0.0, text_a * 0.5))
105 renderer.draw_text("YOU DIED", (cx - 60, 200), scale=3.0, colour=(0.8, 0.1, 0.1, text_a))
106
107 if self._timer > 1.5:
108 stat_a = min(1.0, (self._timer - 1.5) * 2.0)
109 sy = 300
110 renderer.draw_text("-- Final Stats --", (cx - 60, sy), scale=1.1, colour=(0.7, 0.5, 0.5, stat_a))
111 sy += 28
112 if self._player:
113 stats = [
114 (f"Level: {self._player.level}", (0.8, 0.8, 0.8, stat_a)),
115 (f"Dungeon Floor: {self._dungeon_level}", (0.8, 0.8, 0.8, stat_a)),
116 (f"Gold: {self._player.gold}", (1.0, 0.85, 0.2, stat_a)),
117 (f"Enemies Slain: {getattr(self._player, '_total_kills', 0)}", (0.8, 0.5, 0.5, stat_a)),
118 ]
119 for text, colour in stats:
120 renderer.draw_text(text, (cx - 70, sy), scale=1.0, colour=colour)
121 sy += 24
122
123 if self._timer > 2.0:
124 pulse = 0.5 + 0.5 * math.sin(self._timer * 3.0)
125 renderer.draw_text(
126 "Press SPACE to respawn at last save point",
127 (cx - 150, sh - 120),
128 scale=1.2,
129 colour=(0.7, 0.7, 0.7, pulse),
130 )