extends Node2D const TRAIL_INTERVAL := 0.035 const MAX_TRAIL_PARTICLES := 90 var _ship: CharacterBody2D var _thrust_strength := 0.0 var _engine_phase := 0.0 var _trail_emission_timer := 0.0 var _trail_particles: Array[Dictionary] = [] func _ready() -> void: _ship = get_parent() as CharacterBody2D set_as_top_level(true) z_index = -1 if is_instance_valid(_ship): global_position = _ship.global_position global_rotation = _ship.global_rotation func _process(delta: float) -> void: if not is_instance_valid(_ship): return global_position = _ship.global_position global_rotation = _ship.global_rotation _engine_phase += delta var thrust_active := Input.is_action_pressed("ship_thrust") if bool(_ship.get("_is_destroyed")): thrust_active = false var target_strength := 1.0 if thrust_active else 0.0 _thrust_strength = move_toward(_thrust_strength, target_strength, 5.5 * delta) if thrust_active: _trail_emission_timer += delta while _trail_emission_timer >= TRAIL_INTERVAL: _trail_emission_timer -= TRAIL_INTERVAL _spawn_trail_particle() _update_trail_particles(delta) queue_redraw() func _spawn_trail_particle() -> void: if _trail_particles.size() >= MAX_TRAIL_PARTICLES: return var aft := Vector2.DOWN.rotated(_ship.global_rotation) var side := Vector2(-aft.y, aft.x) var angle := randf_range(-0.18, 0.18) var particle_direction := aft.rotated(angle) var life := randf_range(0.45, 0.8) _trail_particles.append({ "position": _ship.global_position + aft * 21.0, "velocity": _ship.velocity + particle_direction * randf_range(30.0, 58.0) + side * randf_range(-6.0, 6.0), "age": 0.0, "life": life, "size": randf_range(1.5, 3.0), }) func _update_trail_particles(delta: float) -> void: for particle_index in range(_trail_particles.size() - 1, -1, -1): var particle := _trail_particles[particle_index] particle["position"] = particle["position"] + particle["velocity"] * delta particle["age"] = float(particle["age"]) + delta _trail_particles[particle_index] = particle if float(particle["age"]) >= float(particle["life"]): _trail_particles.remove_at(particle_index) func _draw() -> void: _draw_trail() if _thrust_strength <= 0.01: return var pulse := 0.82 + 0.18 * sin(_engine_phase * 25.0) var length := (28.0 + pulse * 14.0) * _thrust_strength var base_y := 22.0 var outer_tip := base_y + length var inner_tip := base_y + length * 0.76 var core_tip := base_y + length * 0.48 draw_colored_polygon(PackedVector2Array([ Vector2(-5.2, base_y), Vector2(-3.2, base_y + 6.0), Vector2(0.0, outer_tip), Vector2(3.2, base_y + 6.0), Vector2(5.2, base_y), ]), Color(0.04, 0.58, 1.0, 0.48 * _thrust_strength)) draw_colored_polygon(PackedVector2Array([ Vector2(-2.8, base_y), Vector2(-1.8, base_y + 4.0), Vector2(0.0, inner_tip), Vector2(1.8, base_y + 4.0), Vector2(2.8, base_y), ]), Color(0.24, 0.9, 1.0, 0.78 * _thrust_strength)) draw_colored_polygon(PackedVector2Array([ Vector2(-1.0, base_y), Vector2(0.0, core_tip), Vector2(1.0, base_y), ]), Color(0.88, 0.98, 1.0, 0.92 * _thrust_strength)) draw_circle(Vector2(0.0, base_y), 2.3, Color(0.42, 0.88, 1.0, 0.82 * _thrust_strength)) func _draw_trail() -> void: for particle in _trail_particles: var age := float(particle["age"]) var life := float(particle["life"]) var fade := 1.0 - clampf(age / life, 0.0, 1.0) var current_position: Vector2 = particle["position"] var particle_velocity: Vector2 = particle["velocity"] var particle_size := float(particle["size"]) var current_local := to_local(current_position) var tail_local := to_local(current_position - particle_velocity.normalized() * particle_size * 3.5) draw_line(tail_local, current_local, Color(0.22, 0.78, 1.0, fade * 0.52), particle_size * 0.72, true) draw_circle(current_local, particle_size * fade, Color(0.62, 0.94, 1.0, fade * 0.72))