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.circle((0, 0), self.radius + 1.0, fill=(1.0, 1.0, 1.0, 0.45), segments=12)
57 renderer.circle((0, 0), self.radius, fill=self.colour, segments=12)
58 if self.aoe_radius > 0:
59 renderer.circle(
60 (0, 0), self.aoe_radius, stroke=(self.colour[0], self.colour[1], self.colour[2], 0.18), segments=18
61 )
62
63
64def archer_arrow(position: Vec2, direction: Vec2, damage: float) -> Projectile:
65 return Projectile(
66 kind="arrow",
67 position=position,
68 velocity=direction.normalized() * 520.0,
69 damage=damage,
70 max_distance=520.0,
71 radius=3.5,
72 pierce=3,
73 colour=GREEN,
74 )
75
76
77def mage_bolt(position: Vec2, direction: Vec2, damage: float) -> Projectile:
78 return Projectile(
79 kind="bolt",
80 position=position,
81 velocity=direction.normalized() * 360.0,
82 damage=damage,
83 max_distance=360.0,
84 radius=5.0,
85 pierce=1,
86 aoe_radius=42.0,
87 colour=BLUE,
88 )