class_name Weapon extends Node3D @export var def: WeaponDef var shooter: PhysicsBody3D # Resolved in _ready(): nearest ancestor body var _cooldown := 0.0 @onready var _muzzle: Marker3D = %Muzzle func _ready() -> void: # Fallback: nearest ancestor body is the shooter (hand-placed weapons). var n := get_parent() while shooter == null and n != null: shooter = n as PhysicsBody3D n = n.get_parent() func _physics_process(delta: float) -> void: _cooldown = maxf(_cooldown - delta, 0.0) func try_fire() -> bool: if _cooldown > 0.0 or def == null or def.ammo == null: return false _cooldown = def.fire_interval var p: Projectile = def.ammo.projectile_scene.instantiate() get_tree().current_scene.add_child(p) if shooter != null: p.add_collision_exception_with(shooter) p.launch(def.ammo, _muzzle.global_transform, def.muzzle_speed_scale) return true