nodes/effects.pyΒΆ
Part of Tanks of Freedom.
1"""Short-lived visual effects: capture flash, attack flash, explosions.
2
3Each effect is a Node2D that lives for a fixed lifetime, then destroys itself.
4Owners spawn and forget; the world tree garbage-collects.
5"""
6
7from __future__ import annotations
8
9from simvx.core import Node2D
10from simvx.core.animation.sprite import Sprite2D
11
12from .data import PLAYER_COLOUR, TILE_H, TILE_W
13from .textures import make_explosion, make_range_overlay
14
15
16class FlashOverlay(Node2D):
17 """Brief tinted rectangle pinned to a cell: used for capture / damage feedback."""
18
19 def __init__(
20 self,
21 *,
22 cell,
23 tile_map,
24 colour=(1.0, 1.0, 0.4, 0.8),
25 duration: float = 0.5,
26 w: int = TILE_W,
27 h: int = TILE_H,
28 **kwargs,
29 ):
30 super().__init__(**kwargs)
31 wx, wy = tile_map.map_to_world(cell)
32 self.position = (wx, wy)
33 self._duration = duration
34 self._t = 0.0
35
36 # A diamond overlay tinted to the requested colour.
37 rgba = tuple(int(c * 255) for c in (colour[0], colour[1], colour[2]))
38 rgba = (*rgba, int(colour[3] * 255))
39 self._sprite = Sprite2D(
40 texture=make_range_overlay(rgba),
41 width=w,
42 height=h,
43 filter="nearest",
44 )
45 self.add_child(self._sprite)
46
47 def on_update(self, dt: float) -> None:
48 self._t += dt
49 u = min(1.0, self._t / self._duration)
50 # Fade alpha out
51 col = self._sprite.colour
52 self._sprite.colour = (col[0], col[1], col[2], max(0.0, 1.0 - u))
53 if u >= 1.0:
54 self.destroy()
55
56
57class ExplosionEffect(Node2D):
58 """Animated explosion at a cell. ~0.4s long."""
59
60 DURATION = 0.4
61
62 def __init__(self, *, cell, tile_map, **kwargs):
63 super().__init__(**kwargs)
64 wx, wy = tile_map.map_to_world(cell)
65 self.position = (wx, wy - TILE_H * 0.4)
66 self._t = 0.0
67 self._sprite = Sprite2D(
68 texture=make_explosion(0.0),
69 width=44,
70 height=44,
71 filter="nearest",
72 )
73 self.add_child(self._sprite)
74
75 def on_update(self, dt: float) -> None:
76 self._t += dt
77 u = min(1.0, self._t / self.DURATION)
78 # Refresh texture each frame for the procedural explosion look.
79 self._sprite.texture = make_explosion(u)
80 if u >= 1.0:
81 self.destroy()
82
83
84def capture_flash(world, cell, owner: int) -> None:
85 """Spawn a player-tinted capture flash at ``cell`` (briefly highlights the building)."""
86 col = PLAYER_COLOUR[owner]
87 flash = FlashOverlay(
88 cell=cell,
89 tile_map=world.tile_map,
90 colour=(col[0], col[1], col[2], 0.85),
91 duration=0.6,
92 )
93 world.tile_map.add_child(flash)
94
95
96def damage_flash(world, cell) -> None:
97 flash = FlashOverlay(
98 cell=cell,
99 tile_map=world.tile_map,
100 colour=(1.0, 0.25, 0.2, 0.85),
101 duration=0.35,
102 )
103 world.tile_map.add_child(flash)
104
105
106def explosion(world, cell) -> None:
107 fx = ExplosionEffect(cell=cell, tile_map=world.tile_map)
108 world.tile_map.add_child(fx)