nodes/pickup.pyΒΆ
Part of Q1K3.
1"""Pickups for Q1K3 port: health, weapons, ammo, key."""
2
3from __future__ import annotations
4
5import math
6from typing import TYPE_CHECKING
7
8from simvx.core import Material, MeshInstance3D, Node3D, Vec3
9
10from . import audio, meshes, textures
11from .weapon import GrenadeLauncher, Nailgun
12
13if TYPE_CHECKING: # pragma: no cover
14 from .root import Q1K3Root
15
16
17class Pickup(Node3D):
18 """Base pickup. Pulses + rotates; despawns on player proximity."""
19
20 pickup_radius = 40.0
21 tex_id = textures.TEX_HEALTH
22 body_scale = (12, 12, 12)
23
24 def __init__(self, game: Q1K3Root, pos: Vec3) -> None:
25 super().__init__()
26 self.game = game
27 self.position = pos
28 self._spin = 0.0
29 self._dead = False
30 # Centre the cube so bottom sits roughly on top of the floor (no clip).
31 self._inst = MeshInstance3D(
32 mesh=meshes.cube(),
33 material=Material(
34 albedo_map=textures.get(type(self).tex_id),
35 roughness=0.4,
36 metallic=0.0,
37 ),
38 scale=type(self).body_scale,
39 position=(0, type(self).body_scale[1] / 2, 0),
40 )
41 self.add_child(self._inst)
42
43 def on_update(self, dt: float) -> None:
44 if self._dead:
45 return
46 # Bob + spin
47 self._spin += dt * 1.5
48 from simvx.core.math.types import Quat
49
50 self._inst.rotation = Quat.from_axis_angle(Vec3(0, 1, 0), self._spin)
51 self._inst.position = Vec3(0, math.sin(self._spin * 2) * 2, 0)
52
53 player = self.game.player
54 if player and not player._dead:
55 d = self.position - player.p
56 if d.length() < type(self).pickup_radius:
57 self._on_pickup()
58
59 def _on_pickup(self) -> None:
60 if self._dead:
61 return
62 self._dead = True
63 self.game.play_sfx(audio.sfx_pickup())
64 self.game.queue_remove(self)
65
66
67class HealthPickup(Pickup):
68 tex_id = textures.TEX_HEALTH
69
70 def _on_pickup(self):
71 self.game.player._health = min(100, self.game.player._health + 25)
72 super()._on_pickup()
73
74
75class NailgunPickup(Pickup):
76 tex_id = textures.TEX_PICKUP_NAILGUN
77 body_scale = (14, 8, 22)
78
79 def _on_pickup(self):
80 self.game.player.add_weapon(Nailgun())
81 super()._on_pickup()
82
83
84class GrenadeLauncherPickup(Pickup):
85 tex_id = textures.TEX_GRENADES
86 body_scale = (14, 10, 22)
87
88 def _on_pickup(self):
89 self.game.player.add_weapon(GrenadeLauncher())
90 super()._on_pickup()
91
92
93class NailsPickup(Pickup):
94 tex_id = textures.TEX_NAILS
95 body_scale = (14, 8, 14)
96
97 def _on_pickup(self):
98 added = self.game.player.add_ammo(Nailgun, 50)
99 if not added:
100 return # don't consume if player doesn't own the weapon
101 super()._on_pickup()
102
103
104class GrenadesPickup(Pickup):
105 tex_id = textures.TEX_GRENADES
106 body_scale = (14, 8, 14)
107
108 def _on_pickup(self):
109 added = self.game.player.add_ammo(GrenadeLauncher, 10)
110 if not added:
111 return
112 super()._on_pickup()
113
114
115class KeyPickup(Pickup):
116 tex_id = textures.TEX_KEY_DOOR
117 body_scale = (8, 14, 4)
118
119 def _on_pickup(self):
120 self.game.show_message("YOU GOT THE KEY!")
121 self.game.has_key = True
122 super()._on_pickup()