extends Node ## Sound effects, played from the events the client already receives. ## ## Strictly a view concern: sound is triggered by server events arriving, never ## by anything the client decides for itself, so what you hear matches what ## actually happened. Nothing under src/sim/ knows this exists. ## Voices in the pool. Bullet-hell fire rates mean sounds overlap constantly; ## one player would cut its own tail off on every shot. const VOICES := 16 ## Identical sounds landing within this many frames are collapsed into one, so ## a boss ring hitting eight bullets at once is a bang rather than a wall of ## clipping. const DEDUPE_FRAMES := 2 var _voices: Array[AudioStreamPlayer] = [] var _next: int = 0 var _last_played: Dictionary = {} var _frame: int = 0 func _ready() -> void: for i in VOICES: var p := AudioStreamPlayer.new() p.bus = "Master" add_child(p) _voices.append(p) func _process(_delta: float) -> void: _frame += 1 func play(stream: AudioStream, volume_db: float = 0.0) -> void: if stream == null: return var last: int = _last_played.get(stream, -999) if _frame - last < DEDUPE_FRAMES: return _last_played[stream] = _frame # Round-robin rather than "find a free voice": with this many voices the # oldest is always the best one to steal, and it needs no search. var v := _voices[_next] _next = (_next + 1) % _voices.size() v.stream = stream v.volume_db = volume_db v.play()