Randoloot/scripts/planets/surface_weather.gd
2026-10-19 22:33:24 +02:00

171 lines
7.2 KiB
GDScript

extends Node2D
## World-space weather: bounded particle pool, slow transitions, no screen-space noise.
const LIMIT := 360
const CONDITIONS := preload("res://scripts/planets/surface_weather_conditions.gd")
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()
var condition := "clear"
var _darkness := 0.0
var _clouds: Node2D
var _fog_strength := 0.1
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)
_clouds = get_parent().get_node_or_null("SurfaceAtmosphere")
set_condition(CONDITIONS.pick(_biome,_rng), true)
var material := ShaderMaterial.new()
material.shader = preload("res://resources/effects/cloud_mist.gdshader")
for index in 20:
var fog := Sprite2D.new()
fog.texture = preload("res://assets/planets/cloud_mist.png")
fog.material = material
fog.position = Vector2(index%5*950-1900,index/5*950-1425)
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:
set_condition(CONDITIONS.pick(_biome,_rng))
intensity = move_toward(intensity,target_intensity,delta*0.028)
var gust := 0.75+0.25*sin(_time*0.19)+0.12*sin(_time*0.57)
var preset: Dictionary = CONDITIONS.DATA[condition]
_darkness = move_toward(_darkness,float(preset.darkness),delta*0.10)
_fog_strength = move_toward(_fog_strength,float(preset.fog),delta*0.08)
wind = Vector2(22,7).rotated(sin(_time*0.045)*0.25)*(0.7+intensity*1.4)*gust*float(preset.wind)
if is_instance_valid(_clouds): _clouds.set_weather(float(preset.clouds),_darkness,wind)
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)
var fog_color := Color(0.79,0.84,0.79).lerp(Color(0.20,0.26,0.32),_darkness*0.65)
fog_color.a = (0.02+_fog_strength*0.24)*valley*(1.0 if _biome in [0,1,2,5] else 0.3)
fog.modulate = fog_color
if _biome == 4:
for fog in _fog: fog.modulate = Color(0.76,0.62,0.39,fog.modulate.a+intensity*0.04)
var emission_rate := 170.0 if mode == "rain" else (85.0 if mode in ["snow","dust"] else 16.0)
_emission += delta*emission_rate*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.42)
_storm_timer -= delta
if condition == "storm" and intensity > 0.65 and _storm_timer <= 0:
trigger_lightning()
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(-70,460) 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(0.8,1.6) if mode == "rain" else _rng.randf_range(3,6),"kind":mode,"size":_rng.randf_range(1.1,2.8),"phase":_rng.randf_range(0,TAU)})
func _draw() -> void:
var tint := Color(0.05,0.08,0.14,_darkness*0.12)
if condition == "dust_storm": tint = Color(0.46,0.30,0.12,intensity*0.10)
draw_rect(Rect2(_center()-_extent(),_extent()*2),tint)
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 str(p.get("kind",mode)):
"rain":
draw_line(point,point+Vector2(-4,18),Color(0.73,0.83,0.90,alpha*0.48),0.9,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.65),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,minf(_flash*7,0.85)),2.0,true)
func _draw_reset() -> void:
draw_set_transform(Vector2.ZERO)
func set_condition(next: String, immediate := false) -> bool:
if not CONDITIONS.DATA.has(next): return false
condition = next
var preset: Dictionary = CONDITIONS.DATA[condition]
mode = str(preset.mode)
if mode == "wind": mode = "leaves" if _biome == 1 else ("dust" if _biome == 4 else "wind")
target_intensity = float(preset.intensity)
_transition = _rng.randf_range(45,95)
_storm_timer = _rng.randf_range(5,14)
if immediate:
intensity = target_intensity
_darkness = float(preset.darkness)
_fog_strength = float(preset.fog)
if is_instance_valid(_clouds): _clouds.set_weather(float(preset.clouds),float(preset.darkness),wind)
return true
func display_name() -> String:
return str(CONDITIONS.DATA[condition].name)
func trigger_lightning() -> void:
_storm_timer = _rng.randf_range(7,18)
_flash = 0.10
var center := _center()
var extent := _extent()
var point := center+Vector2(_rng.randf_range(-extent.x*0.65,extent.x*0.65),-extent.y*0.85)
_bolt = PackedVector2Array([point])
for step in range(1,9):
_bolt.append(point+Vector2(_rng.randf_range(-35,35)+step*8,step*30))