extends Node ## Central Audio Manager singleton (autoload name: "AudioManager"). ## ## Pre-warms an AudioStreamPlayer pool for UI and game SFX. ## Provides convenience methods for standard sound effects and ## automatically hooks UI buttons for tactile click audio feedback. const STREAM_CLICK: AudioStream = preload("res://assets/audio/click.wav") const STREAM_COIN: AudioStream = preload("res://assets/audio/coin.wav") const STREAM_CHIME: AudioStream = preload("res://assets/audio/chime.wav") const STREAM_FANFARE: AudioStream = preload("res://assets/audio/fanfare.wav") const STREAM_BELL: AudioStream = preload("res://assets/audio/bell.wav") const STREAM_DOOR: AudioStream = preload("res://assets/audio/door_open.wav") const POOL_SIZE: int = 16 var _players: Array[AudioStreamPlayer] = [] var _player_index: int = 0 ## Number of active AudioStreamPlayers in the pool. var pool_size: int: get: return _players.size() func _ready() -> void: process_mode = Node.PROCESS_MODE_ALWAYS _init_pool() var tree := get_tree() if tree != null: tree.node_added.connect(_on_node_added) _hook_existing_buttons(tree.root) func _init_pool() -> void: if not _players.is_empty(): return for i in range(POOL_SIZE): var player := AudioStreamPlayer.new() player.name = "AudioPlayer_%d" % i player.bus = "Master" add_child(player) _players.append(player) ## Play an audio stream on the specified bus with volume and pitch settings. func play_sound(stream: AudioStream, bus: String = "Master", volume_db: float = 0.0, pitch_scale: float = 1.0) -> AudioStreamPlayer: if stream == null: return null if _players.is_empty(): _init_pool() var player: AudioStreamPlayer = _players[_player_index] _player_index = (_player_index + 1) % POOL_SIZE if player.playing: player.stop() player.stream = stream player.bus = bus player.volume_db = volume_db player.pitch_scale = pitch_scale player.play() return player ## Play UI button click with subtle random pitch variance. func play_click(volume_db: float = 0.0, pitch_variance: float = 0.05) -> AudioStreamPlayer: var pitch := 1.0 if pitch_variance > 0.0: pitch += randf_range(-pitch_variance, pitch_variance) return play_sound(STREAM_CLICK, "Master", volume_db, pitch) ## Play coin transaction sound (credits / marketplace / payouts). func play_coin(volume_db: float = 0.0) -> AudioStreamPlayer: return play_sound(STREAM_COIN, "Master", volume_db, 1.0) ## Play chime sound (successful world / chunk save). func play_chime(volume_db: float = 0.0) -> AudioStreamPlayer: return play_sound(STREAM_CHIME, "Master", volume_db, 1.0) ## Play victory / achievement fanfare. func play_fanfare(volume_db: float = 0.0) -> AudioStreamPlayer: return play_sound(STREAM_FANFARE, "Master", volume_db, 1.0) ## Play alert bell sound. func play_bell(volume_db: float = 0.0) -> AudioStreamPlayer: return play_sound(STREAM_BELL, "Master", volume_db, 1.0) ## Play door open transition sound. func play_door(volume_db: float = 0.0) -> AudioStreamPlayer: return play_sound(STREAM_DOOR, "Master", volume_db, 1.0) # --------------------------------------------------------------------------- # Universal Tactile UI Click Wiring # --------------------------------------------------------------------------- func _on_node_added(node: Node) -> void: if node is BaseButton: _hook_button(node as BaseButton) func _hook_existing_buttons(node: Node) -> void: if node is BaseButton: _hook_button(node as BaseButton) for child in node.get_children(): _hook_existing_buttons(child) func _hook_button(btn: BaseButton) -> void: var callable := Callable(self, "_on_button_pressed").bind(btn) if not btn.pressed.is_connected(callable): btn.pressed.connect(callable) func _on_button_pressed(btn: BaseButton) -> void: if not is_instance_valid(btn): return if btn.is_in_group("no_click_sound"): return if btn.is_visible_in_tree() and not btn.disabled: play_click()