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