nodes/loot_pickup.pyΒΆ
Part of Dungeon Explorer.
1"""World item pickup: dropped by enemies, collected by player proximity."""
2
3import math
4
5from scripts.item import Item, Rarity
6
7from simvx.core import Node2D, Vec2
8
9from .loot_drop import LootDrop
10
11
12class LootPickup(Node2D):
13 """A world item that bobs and can be collected by walking near it."""
14
15 # Procedural world drop: rebuilt with the dungeon from its seed, never
16 # snapshotted (its path would be missing when a save is re-applied).
17 __save_persist__ = False
18
19 def __init__(self, item: Item, **kwargs):
20 super().__init__(name="LootPickup", **kwargs)
21 self.item = item
22 self._time = 0.0
23 self._base_y = 0.0
24 self._collected = False
25
26 def on_ready(self):
27 self._base_y = self.position.y
28
29 def on_update(self, dt: float):
30 self._time += dt
31 self.position = Vec2(self.position.x, self._base_y + math.sin(self._time * 6.28) * 4)
32
33 def try_collect(self, player_pos: Vec2, inventory) -> bool:
34 """Try to collect if player is within 20px. Returns True if collected."""
35 if self._collected:
36 return False
37 dx = player_pos.x - self.position.x
38 dy = player_pos.y - self.position.y
39 if dx * dx + dy * dy > 20 * 20:
40 return False
41 if inventory.is_full:
42 return False
43 inventory.add(self.item)
44 self._collected = True
45 # Spawn notification
46 if self.parent:
47 notif = LootDrop(f"+{self.item.display_name}", colour=self.item.colour)
48 notif.position = Vec2(self.position.x, self.position.y - 10)
49 self.parent.add_child(notif)
50 self.destroy()
51 return True
52
53 def on_draw(self, renderer):
54 colour = self.item.colour
55 r = self.item.rarity
56
57 if r <= Rarity.COMMON:
58 # Plain square
59 renderer.rect((-4, -4), (8, 8), fill=colour)
60 elif r == Rarity.UNCOMMON:
61 # Bordered square
62 renderer.rect((-5, -5), (10, 10), fill=(1.0, 1.0, 1.0, 0.3))
63 renderer.rect((-4, -4), (8, 8), fill=colour)
64 elif r == Rarity.RARE:
65 # Diamond shape
66 renderer.polygon([(0, -6), (-6, 0), (6, 0)], fill=colour)
67 renderer.polygon([(0, 6), (-6, 0), (6, 0)], fill=colour)
68 elif r == Rarity.EPIC:
69 # Star-ish shape (two overlapping triangles)
70 renderer.polygon([(0, -7), (-6, 4), (6, 4)], fill=colour)
71 renderer.polygon([(0, 7), (-6, -4), (6, -4)], fill=colour)
72 else:
73 # Legendary: pulsing star with glow
74 pulse = 0.7 + math.sin(self._time * 8.0) * 0.3
75 glow = (colour[0], colour[1], colour[2], 0.25 * pulse)
76 renderer.circle((0, 0), 8, fill=glow)
77 renderer.polygon([(0, -7), (-6, 4), (6, 4)], fill=colour)
78 renderer.polygon([(0, 7), (-6, -4), (6, -4)], fill=colour)