Files
CosmicClash/Game/scripts/audio_manager.gd
T
2026-09-01 23:19:48 +01:00

87 lines
2.3 KiB
GDScript

extends Node
# Dependency-free audio foundation. Authored assets can replace these tones
# later without changing gameplay call sites or the multiplayer event flow.
const SAMPLE_RATE := 44100
const MAX_INTENSITY := 1.0
var enabled := true
func bind_tree_buttons(root: Node) -> void:
if root == null:
return
for node in root.find_children("*", "BaseButton", true, false):
bind_button(node as BaseButton)
func bind_button(button: BaseButton) -> void:
if button == null:
return
var callback := Callable(self, "play_ui_click")
if not button.pressed.is_connected(callback):
button.pressed.connect(callback)
func play_ui_click() -> void:
_play_tone(880.0, 0.045, 0.10)
func play_countdown(count: int) -> void:
if count <= 0:
_play_tone(1046.5, 0.12, 0.18)
else:
_play_tone(countdown_frequency(count), 0.08, 0.14)
func play_impact(intensity: float) -> void:
var amount := clamp_intensity(intensity)
if amount <= 0.0:
return
_play_tone(150.0 + 180.0 * amount, 0.06 + 0.08 * amount, 0.08 + 0.18 * amount)
func play_goal() -> void:
_play_tone(523.25, 0.22, 0.22)
_play_tone(783.99, 0.30, 0.18)
static func clamp_intensity(value: float) -> float:
if not is_finite(value):
return 0.0
return clampf(value, 0.0, MAX_INTENSITY)
static func countdown_frequency(count: int) -> float:
return 440.0 + float(clampi(count, 1, 9)) * 55.0
func _play_tone(frequency: float, duration: float, volume: float) -> void:
if not enabled or frequency <= 0.0 or duration <= 0.0 or volume <= 0.0:
return
var stream := AudioStreamWAV.new()
stream.format = AudioStreamWAV.FORMAT_16_BITS
stream.mix_rate = SAMPLE_RATE
stream.stereo = false
stream.data = _tone_data(frequency, duration, volume)
var player := AudioStreamPlayer.new()
player.stream = stream
add_child(player)
player.finished.connect(player.queue_free)
player.play()
func _tone_data(frequency: float, duration: float, volume: float) -> PackedByteArray:
var frames := maxi(1, int(duration * SAMPLE_RATE))
var data := PackedByteArray()
data.resize(frames * 2)
for index in frames:
var envelope := minf(1.0, float(index) / 256.0) * minf(1.0, float(frames - index) / 1024.0)
var sample := int(sin(TAU * frequency * float(index) / SAMPLE_RATE) * volume * envelope * 32767.0)
if sample < 0:
sample += 65536
data[index * 2] = sample & 0xff
data[index * 2 + 1] = (sample >> 8) & 0xff
return data