extends Node const SETTINGS_PATH := "user://settings.cfg" const SETTINGS_SECTION := "audio" const SETTINGS_KEY := "ui_sounds_enabled" const PLAYER_COUNT := 8 const DEFAULT_VOLUME_DB := -12.0 const SOUND_PATHS := { "click": "res://assets/audio/kenney_interface_sounds/click_003.ogg", "select": "res://assets/audio/kenney_interface_sounds/select_003.ogg", "toggle": "res://assets/audio/kenney_interface_sounds/toggle_002.ogg", "eliminate": "res://assets/audio/kenney_interface_sounds/scratch_002.ogg", "open": "res://assets/audio/kenney_interface_sounds/open_001.ogg", "close": "res://assets/audio/kenney_interface_sounds/close_002.ogg", "correct": "res://assets/audio/kenney_interface_sounds/confirmation_002.ogg", "error": "res://assets/audio/kenney_interface_sounds/error_004.ogg", "back": "res://assets/audio/kenney_interface_sounds/back_002.ogg", "question": "res://assets/audio/kenney_interface_sounds/question_001.ogg" } var sounds := {} var players: Array[AudioStreamPlayer] = [] var sounds_enabled := true var next_player_index := 0 func _ready() -> void: _load_setting() _load_sounds() _build_player_pool() get_tree().node_added.connect(_on_node_added) call_deferred("_wire_existing_buttons") func play_sound(sound_name: String) -> void: if not sounds_enabled or not sounds.has(sound_name) or players.is_empty(): return var player := _next_available_player() player.stream = sounds[sound_name] player.play() func set_sounds_enabled(is_enabled: bool) -> void: if sounds_enabled == is_enabled: return sounds_enabled = is_enabled if not sounds_enabled: for player in players: player.stop() _save_setting() if sounds_enabled: play_sound("toggle") func are_sounds_enabled() -> bool: return sounds_enabled func set_button_sound(button: Button, sound_name: String) -> void: button.set_meta("ui_sound", sound_name) _wire_button(button) func _load_sounds() -> void: for sound_name in SOUND_PATHS: var stream := load(str(SOUND_PATHS[sound_name])) as AudioStream if stream != null: sounds[sound_name] = stream else: push_warning("Unable to load UI sound: %s" % SOUND_PATHS[sound_name]) func _build_player_pool() -> void: for _player_index in range(PLAYER_COUNT): var player := AudioStreamPlayer.new() player.volume_db = DEFAULT_VOLUME_DB add_child(player) players.append(player) func _next_available_player() -> AudioStreamPlayer: for player in players: if not player.playing: return player var player := players[next_player_index] next_player_index = (next_player_index + 1) % players.size() return player func _on_node_added(node: Node) -> void: if node is Button: _wire_button(node) func _wire_existing_buttons() -> void: _wire_node(get_tree().root) func _wire_node(node: Node) -> void: if not is_instance_valid(node): return if node is Button: _wire_button(node) for child in node.get_children(): _wire_node(child) func _wire_button(button: Button) -> void: if button.has_meta("ui_sound_wired"): return button.set_meta("ui_sound_wired", true) button.pressed.connect(_on_button_pressed.bind(button)) func _on_button_pressed(button: Button) -> void: var sound_name := str(button.get_meta("ui_sound", "click")) if sound_name != "none": play_sound(sound_name) func _load_setting() -> void: var config := ConfigFile.new() if config.load(SETTINGS_PATH) == OK: sounds_enabled = bool(config.get_value(SETTINGS_SECTION, SETTINGS_KEY, true)) func _save_setting() -> void: var config := ConfigFile.new() config.load(SETTINGS_PATH) config.set_value(SETTINGS_SECTION, SETTINGS_KEY, sounds_enabled) var save_error := config.save(SETTINGS_PATH) if save_error != OK: push_warning("Unable to save UI sound preference.")