extends SceneTree
## GATE — game sounds go through the SFX bus, whose low-pass filter takes the treble out
## (user 2026-10-06: "remove all the treble high notes from our sounds so they are
## mostly thudding sounding"). A bus effect, not re-encoded files: non-destructive,
## adjustable, and the files live in the overlay store.
## S1 res://default_bus_layout.tres loads: bus "SFX" exists, sends to Master
## S2 COZY, NOT MUFFLED (research 2026-10-06: harshness is the 2-5 kHz presence band
## and the air above ~7 kHz; the 1-8 kHz texture is where the "crunch" lives, and
## the old 700 Hz low-pass took it all): EQ6 cuts 3.2 kHz and 10 kHz, a high shelf
## ~7 kHz takes ~6 dB off the air, a gentle compressor softens peaks — and no
## low-pass below 2 kHz is left on the bus
## S3 EVERY AudioStreamPlayer3D in both player scenes plays on SFX (a new sound added
## on Master would skip the filter — this is what catches it)
## S4 Master ends in a limiter at <= -1 dB (true-peak headroom)
## S5 MICRO-VARIATION: the procedural player plays jump / land / light ONLY through
## _play_varied (pitch ± semitones, volume ±1.5 dB, re-rolled per play) — no raw
## .play() left on them; and every player sound has max_polyphony >= 2 (the
## default 1 cuts an overlapping land off). (Not AudioStreamRandomizer: one on
## Land crashed gate_step_up with unbounded recursion every run, cause not found.)
## S6 COZY VARIANTS (Kenney CC0, assets/sounds/player/cozy): every pool file loads;
## jump / land / footsteps play through _play_pool (a NO-REPEAT pick from 4-10
## variants + the jitter); footsteps are live again (they were commented out as
## "placeholder" — the metal steps); terrain vs hard surfaces pick their own pool
##
## Run: CI=true "$HYPER_TPS_GODOT" --headless --path . --script res://scenes/test/gate_sfx_bus.gd
var ok := true
var checks := 0
func _p(c: bool, m: String, got: Variant = "") -> void:
checks += 1
print(("ok " if c else "FAIL: ") + m + ("" if str(got) == "" else " (" + str(got) + ")"))
if not c:
ok = false
func _players(n: Node, out: Array) -> void:
if n is AudioStreamPlayer3D or n is AudioStreamPlayer or n is AudioStreamPlayer2D:
out.append(n)
for c in n.get_children():
_players(c, out)
func _init() -> void:
var layout := load("res://default_bus_layout.tres") as AudioBusLayout
_p(layout != null, "S1 res://default_bus_layout.tres loads")
if layout != null:
AudioServer.set_bus_layout(layout)
var i := AudioServer.get_bus_index("SFX")
_p(i > 0 and AudioServer.get_bus_send(i) == &"Master", "S1 bus SFX exists and sends to Master",
"index %d send %s" % [i, AudioServer.get_bus_send(i) if i > 0 else "-"])
var eq: AudioEffectEQ6 = null
var shelf: AudioEffectHighShelfFilter = null
var comp: AudioEffectCompressor = null
var lowpass := ""
for e in (AudioServer.get_bus_effect_count(i) if i > 0 else 0):
var fx := AudioServer.get_bus_effect(i, e)
if not AudioServer.is_bus_effect_enabled(i, e):
continue
if fx is AudioEffectEQ6: eq = fx
elif fx is AudioEffectHighShelfFilter: shelf = fx
elif fx is AudioEffectCompressor: comp = fx
elif fx is AudioEffectLowPassFilter and (fx as AudioEffectLowPassFilter).cutoff_hz < 2000.0:
lowpass = "%.0f Hz" % (fx as AudioEffectLowPassFilter).cutoff_hz
_p(eq != null and eq.get_band_gain_db(4) <= -2.0 and eq.get_band_gain_db(5) <= -3.0,
"S2 EQ6 cuts 3.2 kHz (<= -2 dB) and 10 kHz (<= -3 dB)",
("%.1f / %.1f dB" % [eq.get_band_gain_db(4), eq.get_band_gain_db(5)]) if eq != null else "no EQ6")
_p(shelf != null and shelf.cutoff_hz >= 5000.0 and shelf.cutoff_hz <= 9000.0 and shelf.gain <= 0.6,
"S2 a high shelf at 5-9 kHz takes the air down (gain <= 0.6)",
("%.0f Hz gain %.2f" % [shelf.cutoff_hz, shelf.gain]) if shelf != null else "no shelf")
_p(comp != null and comp.ratio >= 1.5 and comp.ratio <= 4.0 and comp.threshold <= -12.0,
"S2 a gentle compressor (ratio 1.5-4, threshold <= -12 dB)",
("ratio %.1f thr %.0f" % [comp.ratio, comp.threshold]) if comp != null else "no compressor")
_p(lowpass == "", "S2 no low-pass below 2 kHz left on SFX (it took the texture)", lowpass)
var lim := false
for e in AudioServer.get_bus_effect_count(0):
var fx := AudioServer.get_bus_effect(0, e)
if fx is AudioEffectHardLimiter and (fx as AudioEffectHardLimiter).ceiling_db <= -1.0:
lim = true
_p(lim, "S4 Master has a hard limiter at <= -1 dB")
for path in ["res://scenes/player/player.tscn", "res://scenes/player/player_procedural.tscn"]:
var inst := (load(path) as PackedScene).instantiate()
var found: Array = []
_players(inst, found)
var off: Array = []
for n in found:
if String(n.bus) != "SFX":
off.append("%s:%s" % [n.name, n.bus])
_p(found.size() > 0 and off.is_empty(), "S3 every sound in %s plays on SFX (%d)" % [path.get_file(), found.size()], off)
var mono: Array = []
for n in found:
if int(n.max_polyphony) < 2:
mono.append(String(n.name))
_p(mono.is_empty(), "S5 every sound in %s has max_polyphony >= 2" % path.get_file(), mono)
inst.free()
var src := FileAccess.get_file_as_string("res://scenes/player/player_procedural.gd")
var raw: Array = []
for snd in ["snd_jump", "snd_land", "snd_light"]:
if src.contains(snd + ".play()"):
raw.append(snd)
_p(raw.is_empty() and src.contains("func _play_varied(") and src.contains("pitch_scale") and src.contains("randf_range(-1.5, 1.5)"),
"S5 jump / land / light play only through _play_varied (pitch + volume jitter)", raw)
var cozy := ["jump_0", "jump_1", "jump_2", "jump_3", "land_0", "land_1", "land_2", "land_3", "land_4"]
for vi in 5: cozy.append("step_terrain_%d" % vi)
for vi in 10: cozy.append("step_hard_%d" % vi)
var unloaded: Array = []
for nm: String in cozy:
if not (load("res://assets/sounds/player/cozy/%s.ogg" % nm) is AudioStream):
unloaded.append(nm)
_p(unloaded.is_empty(), "S6 all %d cozy variants load" % cozy.size(), unloaded)
_p(src.contains("func _play_pool(") and src.contains("_play_pool(snd_jump") and src.contains("_play_pool(snd_land")
and src.contains("STEP_TERRAIN") and src.contains("STEP_HARD"),
"S6 jump / land / footsteps play from variant pools (terrain and hard surfaces)")
_p(not src.contains("# _footstep_sounds[randi()"), "S6 footsteps are live (no longer commented out)")
print("\n%d checks — %s" % [checks, "PASS" if ok else "FAIL"])
quit(0 if ok else 1)