nodes/particles.pyΒΆ

Part of Clear Code Zelda.

 1"""Particle effect node: plays a folder of frames once and self-destructs.
 2
 3Uses the same FolderSprite primitive as the player. Lifetime is computed
 4from frame count and FPS.
 5"""
 6
 7from __future__ import annotations
 8
 9from random import choice
10
11from support import import_folder
12
13from simvx.core import Node2D, Vec2
14
15from .anim_sprite import FolderSprite
16
17
18class ParticleEffect(Node2D):
19    """One-shot animated particle. Destroys itself when the animation finishes."""
20
21    def __init__(self, frames: list[str], position: Vec2, fps: float = 12.0, size: int = 64, **kwargs):
22        super().__init__(position=position, **kwargs)
23        self._frames = frames
24        self._fps = fps
25        self._size = size
26        self._sprite: FolderSprite | None = None
27
28    def on_ready(self):
29        self._sprite = self.add_child(
30            FolderSprite(
31                frames=self._frames,
32                fps=self._fps,
33                loop=False,
34                width=self._size,
35                height=self._size,
36                name="ParticleSprite",
37            )
38        )
39
40    def on_update(self, dt: float):
41        if self._sprite and self._sprite.finished:
42            self.destroy()
43
44
45class AnimationLibrary:
46    """Caches the per-effect frame lists. One instance lives on the level."""
47
48    def __init__(self):
49        self.frames: dict[str, list[str]] = {}
50        self._populate()
51
52    def _populate(self):
53        names = (
54            "flame",
55            "aura",
56            "heal",
57            "claw",
58            "slash",
59            "sparkle",
60            "leaf_attack",
61            "thunder",
62            "squid",
63            "raccoon",
64            "spirit",
65            "bamboo",
66            "leaf1",
67            "leaf2",
68            "leaf3",
69            "leaf4",
70            "leaf5",
71            "leaf6",
72        )
73        for n in names:
74            # Some effects live under a "frames" subfolder.
75            for sub in (f"{n}/frames", n):
76                fs = import_folder(sub)
77                if fs:
78                    self.frames[n] = fs
79                    break
80
81    def grass_leaf_frames(self) -> list[str]:
82        leaves = [self.frames[n] for n in ("leaf1", "leaf2", "leaf3", "leaf4", "leaf5", "leaf6") if n in self.frames]
83        return choice(leaves) if leaves else []
84
85    def get(self, name: str) -> list[str]:
86        return self.frames.get(name, [])