extends ExplosiveProjectile class_name HomingProjectile var weapon_source: Node var turn_speed: float = 5.0 var initial_aim_dir: Vector3 = Vector3.ZERO var cruise_speed: float = 60.0 var ignition_timer: float = 0.5 var ignited: bool = false var effects_started: bool = false func _setup_effects() -> void: pass # Wait until ignited func _start_homing_effects() -> void: if effects_started: return effects_started = true # Audio flight_sound = AudioStreamPlayer3D.new() AudioManager.configure_player(flight_sound, "swarm_flight") flight_sound.autoplay = true flight_sound.unit_size = 6.0 flight_sound.max_db = 0.0 flight_sound.max_distance = 45.0 flight_sound.add_to_group("acoustic_occluded") # muffles behind walls add_child(flight_sound) flight_sound.play() # Jet Fire Particles jet_sys = GPUParticles3D.new() var jet_mat = ParticleProcessMaterial.new() jet_mat.direction = Vector3(0, 0, 1) jet_mat.spread = 10.0 jet_mat.initial_velocity_min = 0.5 jet_mat.initial_velocity_max = 1.0 jet_mat.gravity = Vector3.ZERO var jet_mesh = BoxMesh.new() jet_mesh.size = Vector3(0.02, 0.02, 0.02) var mat = StandardMaterial3D.new() mat.albedo_color = Color(1.0, 0.6, 0.0) mat.emission_enabled = true mat.emission = Color(1.0, 0.5, 0.0) jet_mesh.material = mat jet_sys.draw_pass_1 = jet_mesh jet_sys.process_material = jet_mat jet_sys.amount = 16 jet_sys.lifetime = 0.15 jet_sys.position = Vector3(0, 0, 0.05) add_child(jet_sys) # Smoke Trail smoke_sys = GPUParticles3D.new() var smoke_mat = ParticleProcessMaterial.new() smoke_mat.direction = Vector3(0, 0, 1) smoke_mat.spread = 5.0 smoke_mat.initial_velocity_min = 0.0 smoke_mat.initial_velocity_max = 0.2 smoke_mat.gravity = Vector3(0, 0.5, 0) var grad = Gradient.new() grad.add_point(0.0, Color(0.5, 0.5, 0.5, 0.8)) grad.add_point(1.0, Color(0.8, 0.8, 0.8, 0.0)) var grad_tex = GradientTexture1D.new() grad_tex.gradient = grad smoke_mat.color_ramp = grad_tex var curve = Curve.new() curve.add_point(Vector2(0.0, 1.0)) curve.add_point(Vector2(1.0, 3.0)) var curve_tex = CurveTexture.new() curve_tex.curve = curve smoke_mat.scale_curve = curve_tex var smoke_mesh = QuadMesh.new() smoke_mesh.size = Vector2(0.08, 0.08) var s_mat = StandardMaterial3D.new() s_mat.transparency = BaseMaterial3D.TRANSPARENCY_ALPHA s_mat.vertex_color_use_as_albedo = true s_mat.albedo_texture = ExplosiveProjectile.get_smoke_texture() s_mat.billboard_mode = BaseMaterial3D.BILLBOARD_PARTICLES s_mat.billboard_keep_scale = true smoke_mesh.material = s_mat smoke_sys.draw_pass_1 = smoke_mesh smoke_sys.process_material = smoke_mat smoke_sys.amount = 32 smoke_sys.lifetime = 1.0 smoke_sys.position = Vector3(0, 0, 0.05) add_child(smoke_sys) func _physics_process(delta: float) -> void: if not ignited: ignition_timer -= delta if ignition_timer <= 0.0: ignited = true drop_gravity = 0.0 _start_homing_effects() if ignited and is_instance_valid(weapon_source): var target_dir: Vector3 = initial_aim_dir var is_painting = false var target: Vector3 = Vector3.ZERO if weapon_source.get("player") and weapon_source.player: is_painting = weapon_source.player.synced_is_targeting target = weapon_source.player.synced_homing_target_pos else: is_painting = weapon_source.get("is_targeting") if is_painting != null and is_painting: target = weapon_source.get("homing_target_pos") if is_painting and target != Vector3.ZERO: target_dir = (target - global_position).normalized() if target_dir != Vector3.ZERO: var current_dir = velocity.normalized() var new_dir = current_dir.lerp(target_dir, turn_speed * delta).normalized() # Accelerate to cruise speed var current_speed = velocity.length() var new_speed = move_toward(current_speed, cruise_speed, 80.0 * delta) velocity = new_dir * new_speed var up_vec = Vector3.UP if abs(new_dir.dot(up_vec)) > 0.99: up_vec = Vector3.RIGHT transform.basis = Basis.looking_at(new_dir, up_vec) super._physics_process(delta)