extends Node2D ## World-space weather: bounded particle pool, slow transitions, no screen-space noise. const LIMIT := 190 var source: Control var intensity := 0.15 var target_intensity := 0.2 var wind := Vector2(18,6) var mode := "wind" var _rng := RandomNumberGenerator.new() var _time := 0.0 var _transition := 36.0 var _storm_timer := 40.0 var _flash := 0.0 var _emission := 0.0 var _particles: Array[Dictionary] = [] var _fog: Array[Sprite2D] = [] var _biome := 0 var _bolt := PackedVector2Array() func _ready() -> void: name = "SurfaceWeather" z_index = 80 texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR_WITH_MIPMAPS _rng.seed = source.progress.visit_seed ^ 0x57454154 _biome = int(source.world.data.landscape.index) mode = ["rain","leaves","rain","wind","dust","snow"][_biome] target_intensity = 0.38 if _biome in [2,5] else 0.18 var material := ShaderMaterial.new() material.shader = preload("res://resources/effects/cloud_mist.gdshader") for index in 12: var fog := Sprite2D.new() fog.texture = preload("res://assets/planets/cloud_mist.png") fog.material = material fog.position = Vector2(index%4*1100-1650,index/4*1100-1100) fog.rotation = _rng.randf_range(-PI,PI) fog.scale = Vector2.ONE*_rng.randf_range(650,1100)/fog.texture.get_width() fog.z_as_relative = false fog.z_index = 18 fog.modulate = Color(0.79,0.84,0.79,0.0) add_child(fog) _fog.append(fog) func _center() -> Vector2: return source.camera.get_screen_center_position() func _extent() -> Vector2: return Vector2(get_viewport_rect().size)/source.camera.zoom*0.5+Vector2(140,140) func _process(delta: float) -> void: if not is_instance_valid(source) or source._ending: return _time += delta _transition -= delta if _transition <= 0: _transition = _rng.randf_range(38,85) target_intensity = _rng.randf_range(0.12,0.85) if _biome in [2,4,5] else _rng.randf_range(0.0,0.58) intensity = move_toward(intensity,target_intensity,delta*0.018) var gust := 0.75+0.25*sin(_time*0.19)+0.12*sin(_time*0.57) wind = Vector2(22,7).rotated(sin(_time*0.045)*0.25)*(0.7+intensity*1.4)*gust if is_instance_valid(source.world.tree_canopy): source.world.tree_canopy.wind_material.set_shader_parameter("wind_direction",wind.normalized()) source.world.tree_canopy.wind_material.set_shader_parameter("wind_strength",0.5+intensity*1.4) for index in _fog.size(): var fog := _fog[index] fog.position += wind*delta*0.24 fog.position.x = wrapf(fog.position.x,-2350,2350) fog.position.y = wrapf(fog.position.y,-2100,2100) var h := terrain_height(fog.position) var valley := 1.0-smoothstep(0.42,0.65,h) fog.modulate.a = (0.035+intensity*0.14)*valley*(1.0 if _biome in [0,1,2,5] else 0.3) if _biome == 4: for fog in _fog: fog.modulate = Color(0.76,0.62,0.39,fog.modulate.a+intensity*0.04) _emission += delta*([13,12,34,8,24,34][_biome])*intensity while _emission >= 1: _emission -= 1 if _particles.size() < LIMIT: _spawn() var center := _center() var extent := _extent() for index in range(_particles.size()-1,-1,-1): var p: Dictionary = _particles[index] p.age += delta p.position += (p.velocity+wind)*delta if p.age > p.life or absf(p.position.x-center.x) > extent.x*1.3 or absf(p.position.y-center.y) > extent.y*1.3: _particles.remove_at(index) _flash = move_toward(_flash,0,delta*0.8) _storm_timer -= delta if mode == "rain" and intensity > 0.66 and _storm_timer <= 0: _storm_timer = _rng.randf_range(28,65) _flash = 0.045 var point := center+Vector2(_rng.randf_range(-extent.x*0.8,extent.x*0.8),-extent.y*0.7) _bolt = PackedVector2Array([point,point+Vector2(15,30),point+Vector2(4,45),point+Vector2(31,80)]) queue_redraw() func terrain_height(point: Vector2) -> float: var x := clampi(int((point.x+1920)/40),0,95) var y := clampi(int((point.y+1920)/40),0,95) return float(source.world.data.heights[y*96+x]) func _spawn() -> void: var extent := _extent() var point := _center()+Vector2(_rng.randf_range(-extent.x,extent.x),_rng.randf_range(-extent.y,extent.y)) var velocity := Vector2(-25,250) if mode == "rain" else Vector2(_rng.randf_range(-8,8),_rng.randf_range(7,22)) _particles.append({"position":point,"velocity":velocity,"age":0.0,"life":_rng.randf_range(3,6),"size":_rng.randf_range(1.1,2.8),"phase":_rng.randf_range(0,TAU)}) func _draw() -> void: for p in _particles: var alpha := sin(clampf(p.age/p.life,0,1)*PI) var point: Vector2 = p.position+Vector2(sin(_time*1.6+p.phase)*3,0) match mode: "rain": draw_line(point,point+Vector2(-2,12),Color(0.73,0.83,0.86,alpha*0.26),0.8,true) if terrain_height(point) < 0.32: draw_arc(point+Vector2(-2,12),p.size*1.5,0,TAU,12,Color(0.73,0.83,0.86,alpha*0.10),0.7,true) "snow": draw_circle(point,p.size,Color(0.91,0.96,0.97,alpha*0.55),true,-1,true) "leaves": draw_set_transform(point,sin(_time+p.phase),Vector2(1,0.45)) draw_circle(Vector2.ZERO,p.size,Color(0.61,0.43,0.19,alpha*0.55),true,-1,true) _draw_reset() _: draw_line(point,point+wind.normalized()*p.size*7,Color(0.80,0.72,0.53,alpha*0.11),1,true) var center := _center() var extent := _extent() # Very faint broad sun shafts, confined to damp wooded climates. if _biome in [0,1,2]: for index in 2: var start := center+Vector2(-extent.x*0.7+index*extent.x*0.9,-extent.y) draw_colored_polygon(PackedVector2Array([start,start+Vector2(50,0),start+Vector2(380,extent.y*2),start+Vector2(210,extent.y*2)]),Color(0.95,0.87,0.62,0.012*(1.0-intensity))) if _flash > 0: draw_rect(Rect2(center-extent,extent*2),Color(0.78,0.84,1.0,_flash)) draw_polyline(_bolt,Color(0.83,0.90,1.0,_flash*6),1.2,true) func _draw_reset() -> void: draw_set_transform(Vector2.ZERO)