171 lines
7.2 KiB
GDScript
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))
|