extends Area3D # Вентиляционный поток: столб воздуха вверх из решётки. Пока игрок внутри, player.apply_vent() тянет его вверх # (вертикальная скорость растёт до VENT_MAX_RISE, горизонтальная сохраняется, двойной прыжок возвращается). # Эффект локальный (только на игроке), в сеть не уходит. Рисуется кодом: решётка на полу, мерцающий столб, тонкий свет. # Начало координат — центр решётки на полу; столб идёт вверх на height. @export var size := Vector2(6.0, 6.0) # сечение столба (X, Z), м @export var height := 14.0 # высота столба, м @export var color := Color(0.25, 0.95, 0.75) @export var hidden := false # скрытый поток (шахта-лифт): только тёмная решётка, без столба, щелей и света const SHADER := """ shader_type spatial; render_mode unshaded, blend_add, cull_disabled, depth_draw_never; uniform vec4 tint : source_color = vec4(0.45, 0.85, 0.35, 1.0); uniform float height = 14.0; varying vec3 lp; void vertex() { lp = VERTEX; } void fragment() { float y = lp.y / height; float bands = 0.55 + 0.45 * sin((lp.y * 0.9 - TIME * 7.0) + lp.x * 1.7 + lp.z * 2.3); float fade = smoothstep(0.0, 0.08, y) * (1.0 - smoothstep(0.55, 1.0, y)); ALBEDO = tint.rgb; ALPHA = clamp(bands * fade * 0.22, 0.0, 1.0); } """ func _ready() -> void: collision_layer = 0 collision_mask = 1 var cs := CollisionShape3D.new() var shape := BoxShape3D.new() shape.size = Vector3(size.x, height, size.y) cs.shape = shape cs.position.y = height * 0.5 add_child(cs) _build_visuals() func _physics_process(_delta: float) -> void: for b in get_overlapping_bodies(): if b.has_method("apply_vent"): b.apply_vent() func _build_visuals() -> void: # мерцающий столб var sh := Shader.new() sh.code = SHADER var sm := ShaderMaterial.new() sm.shader = sh sm.set_shader_parameter("tint", color) sm.set_shader_parameter("height", height) var bm := BoxMesh.new() bm.size = Vector3(size.x, height, size.y) var col := MeshInstance3D.new() col.mesh = bm col.material_override = sm col.position.y = height * 0.5 col.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF if not hidden: add_child(col) # решётка на полу: тёмная плита с светящимися щелями var dark := StandardMaterial3D.new() dark.albedo_color = Color(0.02, 0.025, 0.03) var glow := StandardMaterial3D.new() glow.albedo_color = Color(0, 0, 0) glow.emission_enabled = true glow.emission = color glow.emission_energy_multiplier = 2.2 var plate := BoxMesh.new() plate.size = Vector3(size.x - 0.3, 0.08, size.y - 0.3) _add(plate, dark, Vector3(0, 0.04, 0)) var n := 0 if hidden else int((size.x - 0.8) / 0.7) for i in n: var slit := BoxMesh.new() slit.size = Vector3(0.22, 0.03, size.y - 1.0) _add(slit, glow, Vector3(-(size.x - 0.8) * 0.5 + (i + 0.5) * (size.x - 0.8) / n, 0.095, 0)) if hidden: return # слабый свет над решёткой var l := OmniLight3D.new() l.light_color = color l.light_energy = 0.8 l.omni_range = maxf(size.x, size.y) * 1.8 l.position.y = 1.5 add_child(l) func _add(mesh: Mesh, mat: Material, pos: Vector3) -> void: var mi := MeshInstance3D.new() mi.mesh = mesh mi.material_override = mat mi.position = pos mi.cast_shadow = GeometryInstance3D.SHADOW_CASTING_SETTING_OFF add_child(mi)