extends Node2D ## Ground wash stays in world space after emission, independently of the lander. const LIMIT := 220 var source: Control var weather: Node2D var _rng := RandomNumberGenerator.new() var _particles: Array[Dictionary] = [] var _soft: GradientTexture2D var _shadow: Sprite2D var _emission := 0.0 var _tree_timer := 0.0 var _near_trees := false var _surface_kind := "dust" var _ground := Color("b5a17a") func _ready() -> void: name = "SurfaceFlightEffects" z_index = 21 texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR _rng.seed = source.progress.visit_seed ^ 0x44555354 _ground = (source.world.data.landscape.sand as Color).lerp(source.world.data.landscape.low,0.25) var gradient := Gradient.new() gradient.set_color(0,Color.WHITE) gradient.set_color(1,Color(1,1,1,0)) _soft = GradientTexture2D.new() _soft.gradient = gradient _soft.width = 64 _soft.height = 64 _soft.fill = GradientTexture2D.FILL_RADIAL _soft.fill_from = Vector2(0.5,0.5) _soft.fill_to = Vector2(0.5,1) _shadow = Sprite2D.new() _shadow.name = "LanderShadow" _shadow.texture = source.lander.texture _shadow.scale = source.lander.scale*0.76 _shadow.z_index = -1 var material := ShaderMaterial.new() material.shader = preload("res://resources/effects/lander_shadow.gdshader") _shadow.material = material add_child(_shadow) func surface_kind(point: Vector2) -> String: if int(source.world.data.landscape.index) == 5: return "snow" var h: float = weather.terrain_height(point) if h < 0.29 or (int(source.world.data.landscape.index) == 2 and h < 0.37): return "spray" return "rock" if h > 0.64 else "dust" func _process(delta: float) -> void: if not is_instance_valid(source): return visible = not source._ending if source._ending: return var pose: Transform2D = source.lander.global_transform pose.origin = source._previous_position.lerp(source.lander.position,Engine.get_physics_interpolation_fraction()) var point := pose.origin _shadow.position = point+Vector2(48,64) _shadow.rotation = pose.get_rotation() _shadow.visible = source.lander.visible _tree_timer -= delta if _tree_timer <= 0: _tree_timer = 0.4 _near_trees = false for tree in source.world.data.trees: if point.distance_squared_to(tree.position) < 170*170: _near_trees = true break _surface_kind = surface_kind(point) var thrust: bool = source.engine_thrust_active() var brake: bool = not source._dialog.visible and Input.is_action_pressed("ship_brake") and source._motion.velocity.length() > 12 var boost: bool = source._motion.boost_active and thrust if thrust or brake: var rate := 90.0 if boost else (46.0 if thrust else 30.0) if _surface_kind == "rock": rate *= 0.45 _emission += delta*rate while _emission >= 1: _emission -= 1 if _particles.size() < LIMIT: _emit(point,pose.get_rotation(),boost) else: _emission = 0 for index in range(_particles.size()-1,-1,-1): var particle: Dictionary = _particles[index] particle.age += delta particle.position += (particle.velocity+weather.wind*0.7)*delta particle.velocity *= exp(-delta*1.3) if particle.age > particle.life: _particles.remove_at(index) queue_redraw() func _emit(point: Vector2, angle: float, boost: bool) -> void: var kind := _surface_kind if _near_trees and kind == "dust" and _rng.randf() < 0.20: kind = "leaf" var nozzle := Vector2(_rng.randf_range(-29,29),_rng.randf_range(22,42)).rotated(angle) var scatter := Vector2(_rng.randf_range(-48,48),_rng.randf_range(20,65)).rotated(angle) _particles.append({"position":point+nozzle,"velocity":scatter+source._motion.velocity*0.1,"age":0.0,"life":_rng.randf_range(1.5,2.8),"size":_rng.randf_range(15,25)*(1.55 if boost else 1.0),"kind":kind,"angle":_rng.randf_range(-PI,PI)}) func _draw() -> void: for particle in _particles: var t := clampf(particle.age/particle.life,0,1) var alpha := sin(t*PI) var kind: String = particle.kind var radius: float = particle.size*(1.0+t*1.9) var color := _ground color.a = alpha*0.32 if kind == "rock": color = Color(0.65,0.65,0.59,alpha*0.19) if kind == "snow": color = Color(0.88,0.95,0.95,alpha*0.38) if kind == "spray": color = Color(0.66,0.83,0.80,alpha*0.30) if kind == "leaf": draw_set_transform(particle.position,particle.angle+t*2,Vector2(1,0.45)) draw_circle(Vector2.ZERO,2.5,Color(0.49,0.35,0.17,alpha*0.7),true,-1,true) draw_set_transform(Vector2.ZERO) else: draw_texture_rect(_soft,Rect2(particle.position-Vector2.ONE*radius,Vector2.ONE*radius*2),false,color) if kind in ["spray","snow"]: draw_circle(particle.position+Vector2(4,-7)*t,1.4,Color(color,alpha*0.38),true,-1,true)