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)