nodes/transition_overlay.py

Part of Dungeon Explorer.

 1"""Fade-to-black transition overlay."""
 2
 3from simvx.core import Node2D, Property, UpdateMode
 4
 5
 6class TransitionOverlay(Node2D):
 7    """Full-screen fade overlay for scene transitions."""
 8
 9    update_mode = Property(
10        UpdateMode.ALWAYS,
11        hint="Processing behaviour while the tree is paused",
12        on_change="_invalidate_update_mode_cache",
13    )
14
15    # on_draw renders a full-screen rect at self._alpha, which ramps every
16    # frame during a fade -> retained 2D must re-run it each frame.
17    dynamic = True
18
19    def __init__(self, **kwargs):
20        super().__init__(name="TransitionOverlay", **kwargs)
21        self._alpha = 0.0
22        self._phase = "idle"  # idle, out, callback, in
23        self._timer = 0.0
24        self._duration = 0.3
25        self._callback = None
26
27    @property
28    def is_transitioning(self) -> bool:
29        return self._phase != "idle"
30
31    def transition(self, callback, fade_duration: float = 0.3):
32        """Start a fade-out → callback → fade-in transition."""
33        if self._phase != "idle":
34            return
35        self._callback = callback
36        self._duration = fade_duration
37        self._phase = "out"
38        self._timer = 0.0
39        self._alpha = 0.0
40
41    def on_update(self, dt: float):
42        if self._phase == "idle":
43            return
44
45        self._timer += dt
46        if self._phase == "out":
47            self._alpha = min(1.0, self._timer / self._duration)
48            if self._timer >= self._duration:
49                self._phase = "callback"
50                self._timer = 0.0
51        elif self._phase == "callback":
52            if self._callback:
53                self._callback()
54                self._callback = None
55            self._phase = "in"
56            self._timer = 0.0
57        elif self._phase == "in":
58            self._alpha = max(0.0, 1.0 - self._timer / self._duration)
59            if self._timer >= self._duration:
60                self._alpha = 0.0
61                self._phase = "idle"
62
63    def on_draw(self, renderer):
64        if self._alpha > 0.001:
65            sw, sh = (1280, 720)
66            if self.tree:
67                sw, sh = self.tree.screen_size
68            renderer.draw_rect((0, 0), (sw, sh), colour=(0.0, 0.0, 0.0, self._alpha), filled=True)