Audio manager implementation
This commit is contained in:
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+2
-1
@@ -23,8 +23,9 @@ config/icon="res://assets/icon.svg"
|
|||||||
|
|
||||||
# ===== AUTOLOAD INITIALIZATION ORDER =====
|
# ===== AUTOLOAD INITIALIZATION ORDER =====
|
||||||
# Order is critical! Dependencies must load before dependents.
|
# Order is critical! Dependencies must load before dependents.
|
||||||
# Layer 0: Event system (no dependencies)
|
# Layer 0: Core systems (no dependencies)
|
||||||
EventBus="*res://scripts/event_bus.gd"
|
EventBus="*res://scripts/event_bus.gd"
|
||||||
|
AudioManager="*res://scripts/autoload/audio_manager.gd"
|
||||||
# Layer 1: Core managers (depend on EventBus only)
|
# Layer 1: Core managers (depend on EventBus only)
|
||||||
GameManager="*res://scripts/game_manager.gd"
|
GameManager="*res://scripts/game_manager.gd"
|
||||||
SaveManager="*res://scripts/save_manager.gd"
|
SaveManager="*res://scripts/save_manager.gd"
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
extends Node
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# AudioManager — singleton for playing sound effects
|
||||||
|
# Uses a pool of AudioStreamPlayers to allow overlapping sounds
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
const POOL_SIZE := 8
|
||||||
|
|
||||||
|
# Preloaded sound effects
|
||||||
|
var _sounds: Dictionary = {
|
||||||
|
"enemy_hit": preload("res://assets/audio/sfx/enemy_hit.wav"),
|
||||||
|
"player_hit": preload("res://assets/audio/sfx/player_hit.wav"),
|
||||||
|
"player_shoot": preload("res://assets/audio/sfx/player_shoot.wav"),
|
||||||
|
"explosion": preload("res://assets/audio/sfx/explosion.wav"),
|
||||||
|
"pickup_scrap": preload("res://assets/audio/sfx/pickup_scrap.wav"),
|
||||||
|
"item_pickup": preload("res://assets/audio/sfx/item_pickup.wav"),
|
||||||
|
"ui_click": preload("res://assets/audio/sfx/ui_click.wav"),
|
||||||
|
"ui_select": preload("res://assets/audio/sfx/ui_select.wav"),
|
||||||
|
}
|
||||||
|
|
||||||
|
var _pool: Array[AudioStreamPlayer] = []
|
||||||
|
var _pool_index: int = 0
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
process_mode = Node.PROCESS_MODE_ALWAYS
|
||||||
|
for i in POOL_SIZE:
|
||||||
|
var player := AudioStreamPlayer.new()
|
||||||
|
player.bus = "Master"
|
||||||
|
add_child(player)
|
||||||
|
_pool.append(player)
|
||||||
|
|
||||||
|
func play(sound_name: String) -> void:
|
||||||
|
if not _sounds.has(sound_name):
|
||||||
|
push_warning("AudioManager: Unknown sound '%s'" % sound_name)
|
||||||
|
return
|
||||||
|
|
||||||
|
var player := _pool[_pool_index]
|
||||||
|
_pool_index = (_pool_index + 1) % POOL_SIZE
|
||||||
|
|
||||||
|
player.stream = _sounds[sound_name]
|
||||||
|
player.play()
|
||||||
@@ -24,5 +24,6 @@ func _physics_process(delta: float) -> void:
|
|||||||
|
|
||||||
func _on_body_entered(body: Node) -> void:
|
func _on_body_entered(body: Node) -> void:
|
||||||
if body.is_in_group("player"):
|
if body.is_in_group("player"):
|
||||||
|
AudioManager.play("pickup_scrap")
|
||||||
RunManager.earn_credits(value)
|
RunManager.earn_credits(value)
|
||||||
queue_free()
|
queue_free()
|
||||||
@@ -86,6 +86,7 @@ func get_slow_factor() -> float:
|
|||||||
func take_damage(amount: float) -> void:
|
func take_damage(amount: float) -> void:
|
||||||
current_health -= amount
|
current_health -= amount
|
||||||
_flash_timer = 0.1
|
_flash_timer = 0.1
|
||||||
|
AudioManager.play("enemy_hit")
|
||||||
if current_health <= 0.0:
|
if current_health <= 0.0:
|
||||||
_die()
|
_die()
|
||||||
|
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ func _draw() -> void:
|
|||||||
func _on_body_entered(body: Node) -> void:
|
func _on_body_entered(body: Node) -> void:
|
||||||
if not body.is_in_group("player") or item == null:
|
if not body.is_in_group("player") or item == null:
|
||||||
return
|
return
|
||||||
|
AudioManager.play("item_pickup")
|
||||||
RunManager.collect_item(item) # Triggers item_collected signal -> player_stats recalculates
|
RunManager.collect_item(item) # Triggers item_collected signal -> player_stats recalculates
|
||||||
if item.heal_on_pickup > 0.0:
|
if item.heal_on_pickup > 0.0:
|
||||||
body.stats.heal(item.heal_on_pickup)
|
body.stats.heal(item.heal_on_pickup)
|
||||||
|
|||||||
@@ -68,6 +68,7 @@ func _attempt_purchase() -> void:
|
|||||||
return
|
return
|
||||||
|
|
||||||
RunManager.spend_credits(price)
|
RunManager.spend_credits(price)
|
||||||
|
AudioManager.play("item_pickup")
|
||||||
|
|
||||||
var player := get_tree().get_first_node_in_group("player") as Node2D
|
var player := get_tree().get_first_node_in_group("player") as Node2D
|
||||||
if player == null:
|
if player == null:
|
||||||
|
|||||||
@@ -94,6 +94,7 @@ func _handle_shooting(delta: float) -> void:
|
|||||||
_shoot_timer = 1.0 / stats.fire_rate
|
_shoot_timer = 1.0 / stats.fire_rate
|
||||||
|
|
||||||
func _fire(direction: Vector2) -> void:
|
func _fire(direction: Vector2) -> void:
|
||||||
|
AudioManager.play("player_shoot")
|
||||||
# Overclock: every 3rd shot deals 0 damage
|
# Overclock: every 3rd shot deals 0 damage
|
||||||
var is_zeroed_shot := false
|
var is_zeroed_shot := false
|
||||||
if stats.overclock:
|
if stats.overclock:
|
||||||
@@ -201,6 +202,7 @@ func take_damage(amount: float) -> void:
|
|||||||
|
|
||||||
stats.current_health -= actual
|
stats.current_health -= actual
|
||||||
_iframe_timer = IFRAME_DURATION
|
_iframe_timer = IFRAME_DURATION
|
||||||
|
AudioManager.play("player_hit")
|
||||||
EventBus.player_health_changed.emit(stats.current_health, stats.max_health)
|
EventBus.player_health_changed.emit(stats.current_health, stats.max_health)
|
||||||
|
|
||||||
# Reactive effects on taking damage
|
# Reactive effects on taking damage
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ func _on_body_entered(body: Node) -> void:
|
|||||||
# Explosive — AoE damage to all enemies in radius
|
# Explosive — AoE damage to all enemies in radius
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
func _explode() -> void:
|
func _explode() -> void:
|
||||||
|
AudioManager.play("explosion")
|
||||||
var space := get_world_2d().direct_space_state
|
var space := get_world_2d().direct_space_state
|
||||||
var query := PhysicsShapeQueryParameters2D.new()
|
var query := PhysicsShapeQueryParameters2D.new()
|
||||||
var circle := CircleShape2D.new()
|
var circle := CircleShape2D.new()
|
||||||
|
|||||||
@@ -82,13 +82,16 @@ func _input(event: InputEvent) -> void:
|
|||||||
|
|
||||||
if event.is_action_pressed("ui_up"):
|
if event.is_action_pressed("ui_up"):
|
||||||
_cursor = wrapi(_cursor - 1, 0, maxi(1, _items.size()))
|
_cursor = wrapi(_cursor - 1, 0, maxi(1, _items.size()))
|
||||||
|
AudioManager.play("ui_select")
|
||||||
_refresh_labels()
|
_refresh_labels()
|
||||||
get_viewport().set_input_as_handled()
|
get_viewport().set_input_as_handled()
|
||||||
elif event.is_action_pressed("ui_down"):
|
elif event.is_action_pressed("ui_down"):
|
||||||
_cursor = wrapi(_cursor + 1, 0, maxi(1, _items.size()))
|
_cursor = wrapi(_cursor + 1, 0, maxi(1, _items.size()))
|
||||||
|
AudioManager.play("ui_select")
|
||||||
_refresh_labels()
|
_refresh_labels()
|
||||||
get_viewport().set_input_as_handled()
|
get_viewport().set_input_as_handled()
|
||||||
elif event.is_action_pressed("ui_accept"):
|
elif event.is_action_pressed("ui_accept"):
|
||||||
|
AudioManager.play("ui_click")
|
||||||
_equip_selected()
|
_equip_selected()
|
||||||
get_viewport().set_input_as_handled()
|
get_viewport().set_input_as_handled()
|
||||||
elif event.is_action_pressed("ui_cancel"):
|
elif event.is_action_pressed("ui_cancel"):
|
||||||
|
|||||||
@@ -113,24 +113,24 @@ func _build_ui() -> void:
|
|||||||
|
|
||||||
var dep_btn := Button.new()
|
var dep_btn := Button.new()
|
||||||
dep_btn.text = "DEPOSIT\n(ON HAND → BANK)"
|
dep_btn.text = "DEPOSIT\n(ON HAND → BANK)"
|
||||||
dep_btn.pressed.connect(_on_deposit)
|
dep_btn.pressed.connect(func(): AudioManager.play("ui_click"); _on_deposit())
|
||||||
action_row.add_child(dep_btn)
|
action_row.add_child(dep_btn)
|
||||||
|
|
||||||
var wth_btn := Button.new()
|
var wth_btn := Button.new()
|
||||||
wth_btn.text = "WITHDRAW\n(BANK → ON HAND)"
|
wth_btn.text = "WITHDRAW\n(BANK → ON HAND)"
|
||||||
wth_btn.pressed.connect(_on_withdraw)
|
wth_btn.pressed.connect(func(): AudioManager.play("ui_click"); _on_withdraw())
|
||||||
action_row.add_child(wth_btn)
|
action_row.add_child(wth_btn)
|
||||||
|
|
||||||
# Close
|
# Close
|
||||||
var close_btn := Button.new()
|
var close_btn := Button.new()
|
||||||
close_btn.text = "CLOSE"
|
close_btn.text = "CLOSE"
|
||||||
close_btn.pressed.connect(close)
|
close_btn.pressed.connect(func(): AudioManager.play("ui_click"); close())
|
||||||
vbox.add_child(close_btn)
|
vbox.add_child(close_btn)
|
||||||
|
|
||||||
func _add_btn(parent: Node, text: String, cb: Callable) -> void:
|
func _add_btn(parent: Node, text: String, cb: Callable) -> void:
|
||||||
var b := Button.new()
|
var b := Button.new()
|
||||||
b.text = text
|
b.text = text
|
||||||
b.pressed.connect(cb)
|
b.pressed.connect(func(): AudioManager.play("ui_click"); cb.call())
|
||||||
parent.add_child(b)
|
parent.add_child(b)
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ func _add_button(parent: Node, text: String, callback: Callable) -> void:
|
|||||||
var btn := Button.new()
|
var btn := Button.new()
|
||||||
btn.text = text
|
btn.text = text
|
||||||
btn.custom_minimum_size = Vector2(240, 48)
|
btn.custom_minimum_size = Vector2(240, 48)
|
||||||
btn.pressed.connect(callback)
|
btn.pressed.connect(func(): AudioManager.play("ui_click"); callback.call())
|
||||||
parent.add_child(btn)
|
parent.add_child(btn)
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ func _show_victory() -> void:
|
|||||||
var btn := Button.new()
|
var btn := Button.new()
|
||||||
btn.text = "RETURN TO HUB"
|
btn.text = "RETURN TO HUB"
|
||||||
btn.position = Vector2(-80, 70)
|
btn.position = Vector2(-80, 70)
|
||||||
btn.pressed.connect(_fade_out_then_hub)
|
btn.pressed.connect(func(): AudioManager.play("ui_click"); _fade_out_then_hub())
|
||||||
_root.add_child(btn)
|
_root.add_child(btn)
|
||||||
|
|
||||||
_fade_in()
|
_fade_in()
|
||||||
|
|||||||
@@ -7,5 +7,6 @@ func _ready() -> void:
|
|||||||
|
|
||||||
func _on_body_entered(body: Node) -> void:
|
func _on_body_entered(body: Node) -> void:
|
||||||
if body.is_in_group("player") and part != null:
|
if body.is_in_group("player") and part != null:
|
||||||
|
AudioManager.play("item_pickup")
|
||||||
UpgradeManager.acquire_part(part)
|
UpgradeManager.acquire_part(part)
|
||||||
queue_free()
|
queue_free()
|
||||||
|
|||||||
Reference in New Issue
Block a user