nodes/screen_transition.pyΒΆ

Part of GDQuest Open RPG.

 1"""Black overlay fade-in/out for scene transitions."""
 2
 3from __future__ import annotations
 4
 5from collections.abc import Callable
 6
 7from simvx.core import Node2D
 8
 9from .layout import viewport_size
10
11
12class ScreenTransition(Node2D):
13    """Renders a fullscreen black quad with tweenable alpha."""
14
15    FADE_TIME = 0.45
16
17    def __init__(self) -> None:
18        super().__init__()
19        self.alpha = 0.0
20        self._target = 0.0
21        self._dir = 0
22        self._on_complete: Callable[[], None] | None = None
23        # ScreenTransition draws on top of everything
24        self.z_index = 10000
25
26    def fade_out(self, callback: Callable[[], None] | None = None) -> None:
27        """Fade to black."""
28        self._target = 1.0
29        self._dir = 1
30        self._on_complete = callback
31
32    def fade_in(self, callback: Callable[[], None] | None = None) -> None:
33        """Fade from black back to transparent."""
34        self._target = 0.0
35        self._dir = -1
36        self._on_complete = callback
37
38    def on_update(self, dt: float) -> None:
39        if self._dir == 0:
40            return
41        # Persistent overlay added once: its `alpha` fade animates every frame from
42        # non-Property state, so dirty it each fading frame (it emits 0 ops at rest,
43        # so `dynamic` could miss the first 0->N upload without a structure change).
44        self.queue_redraw()
45        step = dt / self.FADE_TIME
46        if self._dir > 0:
47            self.alpha = min(self._target, self.alpha + step)
48        else:
49            self.alpha = max(self._target, self.alpha - step)
50        if self.alpha == self._target:
51            self._dir = 0
52            cb = self._on_complete
53            self._on_complete = None
54            if cb is not None:
55                cb()
56
57    def on_draw(self, renderer) -> None:
58        if self.alpha <= 0.0:
59            return
60        w, h = viewport_size(self)
61        renderer.draw_rect((0, 0), (w, h), colour=(0.0, 0.0, 0.0, float(self.alpha)), filled=True)