nodes/projectile.pyΒΆ
Part of SNKRX.
1"""Player projectiles: pierce shots from archers, AOE bursts from mages.
2
3Projectiles are short-lived ``Node2D`` instances that walk a velocity vector
4and emit trail particles. They flag themselves ``alive=False`` when out of
5range or after collision; the arena destroys them in its post-update sweep.
6"""
7
8from __future__ import annotations
9
10from simvx.core import Node2D, Vec2
11
12from .colours import BLUE, FG, GREEN
13
14
15class Projectile(Node2D):
16 def __init__(
17 self,
18 *,
19 kind: str,
20 position: Vec2,
21 velocity: Vec2,
22 damage: float,
23 max_distance: float,
24 radius: float = 4.0,
25 pierce: int = 1,
26 aoe_radius: float = 0.0,
27 colour: tuple[float, float, float, float] = FG,
28 **kwargs,
29 ):
30 super().__init__(name="Projectile", position=Vec2(position), **kwargs)
31 self.kind = kind
32 self.velocity = Vec2(velocity)
33 self.damage = damage
34 self.max_distance = max_distance
35 self.radius = radius
36 self.pierce_left = pierce
37 self.aoe_radius = aoe_radius
38 self.colour = colour
39 self._travelled = 0.0
40 self._hit_set: set[int] = set()
41 self.alive = True
42
43 def update(self, dt: float):
44 if not self.alive or dt <= 0:
45 return
46 step = self.velocity * dt
47 self.position = self.position + step
48 self._travelled += step.length()
49 if self._travelled >= self.max_distance:
50 self.alive = False
51
52 def on_draw(self, renderer):
53 if not self.alive:
54 return
55 # Draw the projectile head
56 renderer.draw_circle(
57 (self.position.x, self.position.y),
58 self.radius + 1.0,
59 colour=(1.0, 1.0, 1.0, 0.45),
60 filled=True,
61 segments=12,
62 )
63 renderer.draw_circle(
64 (self.position.x, self.position.y),
65 self.radius,
66 colour=self.colour,
67 filled=True,
68 segments=12,
69 )
70 if self.aoe_radius > 0:
71 renderer.draw_circle(
72 (self.position.x, self.position.y),
73 self.aoe_radius,
74 colour=(self.colour[0], self.colour[1], self.colour[2], 0.18),
75 filled=False,
76 segments=18,
77 )
78
79
80def archer_arrow(position: Vec2, direction: Vec2, damage: float) -> Projectile:
81 return Projectile(
82 kind="arrow",
83 position=position,
84 velocity=direction.normalized() * 520.0,
85 damage=damage,
86 max_distance=520.0,
87 radius=3.5,
88 pierce=3,
89 colour=GREEN,
90 )
91
92
93def mage_bolt(position: Vec2, direction: Vec2, damage: float) -> Projectile:
94 return Projectile(
95 kind="bolt",
96 position=position,
97 velocity=direction.normalized() * 360.0,
98 damage=damage,
99 max_distance=360.0,
100 radius=5.0,
101 pierce=1,
102 aoe_radius=42.0,
103 colour=BLUE,
104 )