- палитра целиком (индиго, пурпур, ледяная бирюза): SOLID/NEON, SURFACES/GLOWS, туман, ambient, свет запекания, ворота, полосы, надписи; новое небо - запекание 384 сэмпла + огни у подножия башни - вентилятор: VENT_MAX_RISE 16, VENT_ACCEL 80; шахта-лифт без знака и без столба/света (hidden) - музыка Race 2: Poloz «On My Way to Hell» с 1:40 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
94 lines
3.6 KiB
GDScript
94 lines
3.6 KiB
GDScript
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)
|