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 x, y = self.position.x, self.position.y
55 colour = self.item.colour
56 r = self.item.rarity
57
58 if r <= Rarity.COMMON:
59 # Plain square
60 renderer.draw_rect((x - 4, y - 4), (8, 8), colour=colour, filled=True)
61 elif r == Rarity.UNCOMMON:
62 # Bordered square
63 renderer.draw_rect((x - 5, y - 5), (10, 10), colour=(1.0, 1.0, 1.0, 0.3), filled=True)
64 renderer.draw_rect((x - 4, y - 4), (8, 8), colour=colour, filled=True)
65 elif r == Rarity.RARE:
66 # Diamond shape
67 renderer.fill_triangle(x, y - 6, x - 6, y, x + 6, y, colour=colour)
68 renderer.fill_triangle(x, y + 6, x - 6, y, x + 6, y, colour=colour)
69 elif r == Rarity.EPIC:
70 # Star-ish shape (two overlapping triangles)
71 renderer.fill_triangle(x, y - 7, x - 6, y + 4, x + 6, y + 4, colour=colour)
72 renderer.fill_triangle(x, y + 7, x - 6, y - 4, x + 6, y - 4, colour=colour)
73 else:
74 # Legendary: pulsing star with glow
75 pulse = 0.7 + math.sin(self._time * 8.0) * 0.3
76 glow = (colour[0], colour[1], colour[2], 0.25 * pulse)
77 renderer.draw_circle((x, y), 8, colour=glow, filled=True)
78 renderer.fill_triangle(x, y - 7, x - 6, y + 4, x + 6, y + 4, colour=colour)
79 renderer.fill_triangle(x, y + 7, x - 6, y - 4, x + 6, y - 4, colour=colour)