extends Node2D const TRAIL_INTERVAL := 0.025 const MAX_TRAIL_PARTICLES := 96 const PROFILES := { "szerszen":{"color":Color("5dbdff"),"nozzles":[Vector2(-6.5,26),Vector2(6.5,26)],"width":11.0}, "bastion":{"color":Color("ffb45c"),"nozzles":[Vector2(-22,24),Vector2(0,27),Vector2(22,24)],"width":13.0}, "wedrowiec":{"color":Color("55f1df"),"nozzles":[Vector2(-9.5,23),Vector2(0,27),Vector2(9.5,23)],"width":9.0}, "zmija":{"color":Color("bf7cff"),"nozzles":[Vector2(-13,23),Vector2(0,26),Vector2(13,23)],"width":11.0} } var _ship: CharacterBody2D var _thrust_strength := 0.0 var _engine_phase := 0.0 var _trail_emission_timer := 0.0 var _trail_particles: Array[Dictionary] = [] var _mini_boost := false var _boost_strength := 0.0 var _profile: Dictionary var _plumes: Array[ShaderMaterial] = [] var _rcs: Array[ShaderMaterial] = [] var _maneuver := Vector3.ZERO var _previous_position := Vector2.ZERO func _ready() -> void: _ship = get_parent() as CharacterBody2D set_as_top_level(true) z_index = -1 _profile = PROFILES[preload("res://scripts/game/ship_catalog.gd").for_path(GameState.selected_ship_path).asset] for nozzle in _profile.nozzles: _plumes.append(_add_plume(nozzle,0.0,float(_profile.width),90.0)) for port in [[Vector2(-10,-16),PI*0.5],[Vector2(10,-16),-PI*0.5],[Vector2(-15,12),PI*0.5],[Vector2(15,12),-PI*0.5],[Vector2(-7,-23),PI],[Vector2(7,-23),PI]]: _rcs.append(_add_plume(port[0],float(port[1]),6.0,36.0)) global_position = _ship.global_position global_rotation = _ship.global_rotation _previous_position = global_position func _add_plume(at: Vector2, angle: float, width: float, length: float) -> ShaderMaterial: var mount := Node2D.new() mount.position = at mount.rotation = angle add_child(mount) var quad := ColorRect.new() quad.mouse_filter = Control.MOUSE_FILTER_IGNORE quad.position = Vector2(-width*0.5,0) quad.size = Vector2(width,length) var material := ShaderMaterial.new() material.shader = preload("res://scripts/game/thruster_plume.gdshader") material.set_shader_parameter("plume_color",_profile.color) material.set_shader_parameter("phase",float(_plumes.size()+_rcs.size())*8.7) quad.material = material mount.add_child(quad) return material func _process(delta: float) -> void: if not is_instance_valid(_ship): return global_position = _ship.global_position global_rotation = _ship.global_rotation if global_position.distance_to(_previous_position) > 400: _trail_particles.clear() _previous_position = global_position _engine_phase += delta var active := not bool(_ship.get("_is_destroyed")) and not bool(_ship.get("is_docked")) _mini_boost = active and bool(_ship.call("is_mini_boosting")) var boosted := active and bool(_ship.call("is_boosting")) var thrust_active := active and (Input.is_action_pressed("ship_thrust") or boosted) var target_strength := (1.0 if thrust_active else 0.0) _thrust_strength = move_toward(_thrust_strength,target_strength,delta*(9.0 if thrust_active else 6.0)) _boost_strength = move_toward(_boost_strength,1.0 if boosted else 0.0,delta*7.0) if not active: _thrust_strength = 0.0 _trail_particles.clear() for plume in _plumes: plume.set_shader_parameter("power",_thrust_strength) plume.set_shader_parameter("boost",_boost_strength) var yaw := Input.get_axis("ship_turn_left","ship_turn_right") if active else 0.0 var strafe := Input.get_axis("ship_strafe_left","ship_strafe_right") if active else 0.0 var braking := 1.0 if active and Input.is_action_pressed("ship_brake") else 0.0 _maneuver = _maneuver.move_toward(Vector3(yaw,strafe,braking),delta*12.0) var powers := [maxf(_maneuver.x,0.0)+maxf(_maneuver.y,0.0),maxf(-_maneuver.x,0.0)+maxf(-_maneuver.y,0.0),maxf(-_maneuver.x,0.0)+maxf(_maneuver.y,0.0),maxf(_maneuver.x,0.0)+maxf(-_maneuver.y,0.0),_maneuver.z,_maneuver.z] for i in _rcs.size(): _rcs[i].set_shader_parameter("power",minf(0.85,float(powers[i]))) if thrust_active: _trail_emission_timer += delta var emitted := 0 while _trail_emission_timer >= TRAIL_INTERVAL and emitted < 8: _trail_emission_timer -= TRAIL_INTERVAL _spawn_trail_particle() emitted += 1 _trail_emission_timer = minf(_trail_emission_timer,TRAIL_INTERVAL) _update_trail_particles(delta) queue_redraw() func _spawn_trail_particle() -> void: for index in _profile.nozzles.size(): if _trail_particles.size() >= MAX_TRAIL_PARTICLES: break var aft := Vector2.DOWN.rotated(global_rotation) _trail_particles.append({"position":_ship.to_global(_profile.nozzles[index]),"velocity":_ship.velocity*0.12+aft*(42.0 if _mini_boost else 20.0),"age":0.0,"life":0.48 if _mini_boost else 0.28,"size":2.3 if _mini_boost else 1.5,"boost":_mini_boost,"lane":index}) func _update_trail_particles(delta: float) -> void: for i in range(_trail_particles.size()-1,-1,-1): var p := _trail_particles[i] p.position += p.velocity*delta p.age += delta if p.age >= p.life: _trail_particles.remove_at(i) func _draw() -> void: var previous := {} for p in _trail_particles: var fade := 1.0-float(p.age)/float(p.life) var point := to_local(p.position) var lane: int = p.lane if previous.has(lane): var prior: Vector2 = previous[lane] if point.distance_to(prior)<60: draw_line(prior,point,Color(_profile.color,fade*0.055),float(p.size)*4.0,true) draw_line(prior,point,Color(_profile.color,fade*0.25),float(p.size)*0.8,true) previous[lane] = point