From 24fad7baf7b4b20cc25d14ee91d5176caf4463a2 Mon Sep 17 00:00:00 2001 From: Steven Wroblewski Date: Sun, 10 May 2026 21:47:14 +0200 Subject: [PATCH 1/5] assets(sfx): add sprint-22 character and ambient SFX placeholders --- assets/audio/sfx/character_pickup.ogg | 0 assets/audio/sfx/character_place.ogg | 0 assets/audio/sfx/character_tap.ogg | 0 assets/audio/sfx/ultrasound_heartbeat.ogg | 0 docs/audio-assets-sprint19.md | 14 ++++++++++++++ 5 files changed, 14 insertions(+) create mode 100644 assets/audio/sfx/character_pickup.ogg create mode 100644 assets/audio/sfx/character_place.ogg create mode 100644 assets/audio/sfx/character_tap.ogg create mode 100644 assets/audio/sfx/ultrasound_heartbeat.ogg diff --git a/assets/audio/sfx/character_pickup.ogg b/assets/audio/sfx/character_pickup.ogg new file mode 100644 index 0000000..e69de29 diff --git a/assets/audio/sfx/character_place.ogg b/assets/audio/sfx/character_place.ogg new file mode 100644 index 0000000..e69de29 diff --git a/assets/audio/sfx/character_tap.ogg b/assets/audio/sfx/character_tap.ogg new file mode 100644 index 0000000..e69de29 diff --git a/assets/audio/sfx/ultrasound_heartbeat.ogg b/assets/audio/sfx/ultrasound_heartbeat.ogg new file mode 100644 index 0000000..e69de29 diff --git a/docs/audio-assets-sprint19.md b/docs/audio-assets-sprint19.md index c5750b9..cf304dd 100644 --- a/docs/audio-assets-sprint19.md +++ b/docs/audio-assets-sprint19.md @@ -79,3 +79,17 @@ All CC0 or CC-BY from freesound.org. Replace placeholder 0-byte files with the d | `assets/audio/sfx/object_tap.ogg` | soft tap / click | search "soft tap" or "gentle click" | All files must be <1.5 s, child-friendly (no harsh/loud sounds), mono or stereo, 44100 Hz, OGG Vorbis. + +## Sprint 22 — Character & Ambient SFX + +All CC0 or CC-BY from freesound.org. Replace placeholder 0-byte files with the downloads below. + +| File | Description | Freesound suggestion | +|---|---|---| +| `assets/audio/sfx/ultrasound_heartbeat.ogg` | soft beep/blip ~1s, loops seamlessly | search "heartbeat beep soft" or "medical monitor beep" | +| `assets/audio/sfx/character_pickup.ogg` | happy soft squeak / whoosh | search "cartoon pickup soft" or "whoosh gentle" | +| `assets/audio/sfx/character_place.ogg` | gentle thud / landing | search "soft thud" or "gentle landing" | +| `assets/audio/sfx/character_tap.ogg` | short happy chime / pop | search "happy chime short" or "cartoon pop soft" | + +All files must be child-friendly (no harsh/loud sounds), mono or stereo, 44100 Hz, OGG Vorbis. +`ultrasound_heartbeat.ogg` must loop seamlessly (start and end points match). From aefd8349f6310d07a47bf66370d1b117bdb18993 Mon Sep 17 00:00:00 2001 From: Steven Wroblewski Date: Sun, 10 May 2026 21:48:38 +0200 Subject: [PATCH 2/5] feat(sfx): add character SFX keys to AudioManager._SFX_MAP --- scripts/autoload/AudioManager.gd | 3 +++ test/unit/test_audio_manager.gd | 6 ++++++ 2 files changed, 9 insertions(+) diff --git a/scripts/autoload/AudioManager.gd b/scripts/autoload/AudioManager.gd index 26617d4..2cbcec8 100644 --- a/scripts/autoload/AudioManager.gd +++ b/scripts/autoload/AudioManager.gd @@ -27,6 +27,9 @@ const _SFX_MAP: Dictionary = { "ambulance_siren": "res://assets/audio/sfx/ambulance_siren.ogg", "delivery_cheer": "res://assets/audio/sfx/delivery_cheer.ogg", "object_tap": "res://assets/audio/sfx/object_tap.ogg", + "character_pickup": "res://assets/audio/sfx/character_pickup.ogg", + "character_place": "res://assets/audio/sfx/character_place.ogg", + "character_tap": "res://assets/audio/sfx/character_tap.ogg", } var _current_floor: int = -1 diff --git a/test/unit/test_audio_manager.gd b/test/unit/test_audio_manager.gd index b60a702..b0b2444 100644 --- a/test/unit/test_audio_manager.gd +++ b/test/unit/test_audio_manager.gd @@ -83,3 +83,9 @@ func test_sfx_map_has_all_interactive_object_keys() -> void: assert_true(AudioManager._SFX_MAP.has("ambulance_siren")) assert_true(AudioManager._SFX_MAP.has("delivery_cheer")) assert_true(AudioManager._SFX_MAP.has("object_tap")) + + +func test_sfx_map_has_all_character_keys() -> void: + assert_true(AudioManager._SFX_MAP.has("character_pickup")) + assert_true(AudioManager._SFX_MAP.has("character_place")) + assert_true(AudioManager._SFX_MAP.has("character_tap")) From 18c982f77015dca9a43d9602ff18b533130b8fb0 Mon Sep 17 00:00:00 2001 From: Steven Wroblewski Date: Sun, 10 May 2026 21:49:40 +0200 Subject: [PATCH 3/5] feat(sfx): wire character pickup/tap/place SFX to AudioManager --- scripts/characters/character.gd | 3 +++ 1 file changed, 3 insertions(+) diff --git a/scripts/characters/character.gd b/scripts/characters/character.gd index de94f06..38d07b9 100644 --- a/scripts/characters/character.gd +++ b/scripts/characters/character.gd @@ -187,6 +187,7 @@ func _handle_outfit_tap() -> void: func _on_drag_picked_up(pos: Vector2) -> void: + AudioManager.play_sfx("character_pickup") _is_held = true _drag_start_position = pos set_animation_state("held") @@ -197,9 +198,11 @@ func _on_drag_released(pos: Vector2) -> void: _is_held = false var drag_distance: float = pos.distance_to(_drag_start_position) if drag_distance < _TAP_THRESHOLD: + AudioManager.play_sfx("character_tap") set_animation_state("idle") _handle_outfit_tap() return + AudioManager.play_sfx("character_place") set_animation_state("idle") if data == null or data.id.is_empty(): return From 80274b0294dfdd14ab9710264653606739ffb779 Mon Sep 17 00:00:00 2001 From: Steven Wroblewski Date: Sun, 10 May 2026 21:50:52 +0200 Subject: [PATCH 4/5] feat(sfx): add looping ambient heartbeat to UltrasoundMachine --- scripts/objects/ultrasound_machine.gd | 37 +++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/scripts/objects/ultrasound_machine.gd b/scripts/objects/ultrasound_machine.gd index bec57d6..8ab9a0c 100644 --- a/scripts/objects/ultrasound_machine.gd +++ b/scripts/objects/ultrasound_machine.gd @@ -1,4 +1,5 @@ ## UltrasoundMachine — displays a continuous heartbeat pulse on the screen. +## Plays looping ambient heartbeat audio when the ultrasound room is active. class_name UltrasoundMachine extends Node2D const BEAT_RISE_DURATION: float = 0.12 @@ -6,10 +7,46 @@ const BEAT_FALL_DURATION: float = 0.12 const BEAT_INTERVAL: float = 0.60 const BEAT_SCALE_PEAK: Vector2 = Vector2(1.5, 1.5) const BEAT_SCALE_REST: Vector2 = Vector2(1.0, 1.0) +const _HEARTBEAT_PATH: String = "res://assets/audio/sfx/ultrasound_heartbeat.ogg" + +@export var trigger_floor: int = 2 +@export var trigger_room: int = 0 + +var _audio: AudioStreamPlayer func _ready() -> void: _start_heartbeat_loop() + _setup_audio() + RoomNavigator.room_changed.connect(_on_room_changed) + + +func _exit_tree() -> void: + if RoomNavigator.room_changed.is_connected(_on_room_changed): + RoomNavigator.room_changed.disconnect(_on_room_changed) + + +func _setup_audio() -> void: + _audio = AudioStreamPlayer.new() + add_child(_audio) + if AudioServer.get_driver_name() == "Dummy": + return + if not ResourceLoader.exists(_HEARTBEAT_PATH): + return + var stream: AudioStreamOggVorbis = load(_HEARTBEAT_PATH) as AudioStreamOggVorbis + if stream == null: + return + stream.loop = true + _audio.stream = stream + + +func _on_room_changed(floor_index: int, room_index: int) -> void: + if _audio == null or _audio.stream == null: + return + if floor_index == trigger_floor and room_index == trigger_room: + _audio.play() + else: + _audio.stop() func _start_heartbeat_loop() -> void: From b7757a554847fcb37d84084d975ad645e7a41458 Mon Sep 17 00:00:00 2001 From: Steven Wroblewski Date: Sun, 10 May 2026 21:53:51 +0200 Subject: [PATCH 5/5] fix(sfx): duplicate OGG stream before setting loop to avoid shared resource mutation --- scripts/objects/ultrasound_machine.gd | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/scripts/objects/ultrasound_machine.gd b/scripts/objects/ultrasound_machine.gd index 8ab9a0c..177fd35 100644 --- a/scripts/objects/ultrasound_machine.gd +++ b/scripts/objects/ultrasound_machine.gd @@ -33,11 +33,15 @@ func _setup_audio() -> void: return if not ResourceLoader.exists(_HEARTBEAT_PATH): return - var stream: AudioStreamOggVorbis = load(_HEARTBEAT_PATH) as AudioStreamOggVorbis - if stream == null: + var base: AudioStream = load(_HEARTBEAT_PATH) as AudioStream + if base == null: return - stream.loop = true - _audio.stream = stream + var ogg: AudioStreamOggVorbis = base as AudioStreamOggVorbis + if ogg == null: + return + ogg = ogg.duplicate() as AudioStreamOggVorbis + ogg.loop = true + _audio.stream = ogg func _on_room_changed(floor_index: int, room_index: int) -> void: