diff --git a/scripts/scenes/battle_scene.gd b/scripts/scenes/battle_scene.gd index c39236c..4356128 100644 --- a/scripts/scenes/battle_scene.gd +++ b/scripts/scenes/battle_scene.gd @@ -89,6 +89,7 @@ const BGM_VOLUME_DB := -8.0 const SFX_VOLUME_DB := -2.0 const UI_SFX_VOLUME_DB := -4.0 const AUDIO_MUTED_DB := -80.0 +const MAX_RECENT_SFX_KEYS := 32 const FLOATING_TEXT_LIFETIME := 1.0 const FLOATING_TEXT_RISE := 42.0 const OBJECTIVE_NOTICE_DURATION := 2.6 @@ -631,6 +632,7 @@ var missing_portrait_paths := {} var bgm_player: AudioStreamPlayer var current_bgm_key := "" var audio_stream_cache := {} +var recent_sfx_keys: Array[String] = [] var last_announced_battle_status := "" var floating_texts: Array[Dictionary] = [] var objective_notice_timer := 0.0 @@ -4818,6 +4820,7 @@ func _play_sfx(key: String, volume_db := SFX_VOLUME_DB) -> void: var stream := _sfx_stream_for(key) if stream == null: return + _remember_sfx_key(key) var player := AudioStreamPlayer.new() player.name = "Sfx_%s" % key player.stream = stream @@ -4830,6 +4833,15 @@ func _play_sfx(key: String, volume_db := SFX_VOLUME_DB) -> void: player.play() +func _remember_sfx_key(key: String) -> void: + var normalized_key := key.strip_edges() + if normalized_key.is_empty(): + return + recent_sfx_keys.append(normalized_key) + while recent_sfx_keys.size() > MAX_RECENT_SFX_KEYS: + recent_sfx_keys.pop_front() + + func _sfx_stream_for(key: String) -> AudioStream: if key == "bow_release": return _audio_stream_from_path(SFX_BOW_RELEASE_PATH) @@ -4950,7 +4962,8 @@ func _handle_board_click(screen_position: Vector2) -> void: selected_item_id = "" basic_attack_targeting = false _clear_pending_move_state() - state.select_unit(clicked_unit["id"]) + if state.select_unit(clicked_unit["id"]): + _play_ui_click() return if not selected_item_id.is_empty(): @@ -4964,7 +4977,8 @@ func _handle_board_click(screen_position: Vector2) -> void: if not clicked_unit.is_empty() and clicked_unit.get("team", "") == selected_unit.get("team", "") and _can_select(clicked_unit): selected_item_id = "" basic_attack_targeting = false - state.select_unit(clicked_unit["id"]) + if state.select_unit(clicked_unit["id"]): + _play_ui_click() return if not selected_skill_id.is_empty(): @@ -4979,7 +4993,8 @@ func _handle_board_click(screen_position: Vector2) -> void: selected_skill_id = "" selected_item_id = "" basic_attack_targeting = false - state.select_unit(clicked_unit["id"]) + if state.select_unit(clicked_unit["id"]): + _play_ui_click() return if not clicked_unit.is_empty() and clicked_unit.get("team", "") != selected_unit.get("team", ""): @@ -5003,7 +5018,8 @@ func _handle_board_click(screen_position: Vector2) -> void: selected_item_id = "" basic_attack_targeting = false _clear_pending_move_state() - state.select_unit(clicked_unit["id"]) + if state.select_unit(clicked_unit["id"]): + _play_ui_click() return var selected_start_cell: Vector2i = selected_unit.get("pos", Vector2i(-1, -1)) @@ -5028,7 +5044,8 @@ func _handle_board_click(screen_position: Vector2) -> void: selected_item_id = "" basic_attack_targeting = false _clear_pending_move_state() - state.select_unit(clicked_unit["id"]) + if state.select_unit(clicked_unit["id"]): + _play_ui_click() func _has_pending_move() -> bool: @@ -8924,6 +8941,7 @@ func _handle_minimap_mouse_button(event: InputEventMouseButton) -> bool: return false if inside_map_body: minimap_dragging = true + _play_ui_click() _scroll_board_to_minimap_position(event.position) return true if minimap_dragging: diff --git a/tools/smoke_post_move_action_flow.gd b/tools/smoke_post_move_action_flow.gd index 5da199e..f1504bd 100644 --- a/tools/smoke_post_move_action_flow.gd +++ b/tools/smoke_post_move_action_flow.gd @@ -12,6 +12,7 @@ func _init() -> void: _check_scene_post_move_edge_positioning(failures) _check_scene_post_move_scrolled_view_positioning(failures) _check_scene_post_move_menu_tracks_map_scroll(failures) + _check_scene_input_audio_cues(failures) _check_scene_post_move_text_fit(failures) _check_scene_post_move_blocked_command_labels(failures) _check_scene_post_move_tactic_picker_flow(failures) @@ -315,6 +316,46 @@ func _check_scene_post_move_menu_tracks_map_scroll(failures: Array[String]) -> v scene.free() +func _check_scene_input_audio_cues(failures: Array[String]) -> void: + var scene = BattleSceneScript.new() + scene._create_hud() + scene._create_audio() + if not scene.state.load_battle("res://data/scenarios/001_yellow_turbans.json"): + failures.append("could not load opening battle for input audio cues") + scene.free() + return + scene.battle_started = true + scene.campaign_complete_screen = false + scene.briefing_panel.visible = false + scene.result_panel.visible = false + scene._clear_pending_move_state() + + var cao_cao: Dictionary = scene.state.get_unit("cao_cao") + var cao_cell: Vector2i = cao_cao.get("pos", Vector2i(-1, -1)) + scene.recent_sfx_keys.clear() + scene._handle_board_click(_screen_for_cell(cao_cell)) + if not scene.recent_sfx_keys.has("ui_click"): + failures.append("selecting a unit from the map should request a UI click sound: %s" % str(scene.recent_sfx_keys)) + + scene.recent_sfx_keys.clear() + var minimap_press := InputEventMouseButton.new() + minimap_press.button_index = MOUSE_BUTTON_LEFT + minimap_press.pressed = true + minimap_press.position = scene._minimap_map_rect().get_center() + if not scene._handle_minimap_mouse_button(minimap_press): + failures.append("minimap press should be handled for audio cue validation") + if not scene.recent_sfx_keys.has("ui_click"): + failures.append("clicking the minimap should request a UI click sound: %s" % str(scene.recent_sfx_keys)) + + scene.title_sfx_enabled = false + scene.state.clear_selection() + scene.recent_sfx_keys.clear() + scene._handle_board_click(_screen_for_cell(cao_cell)) + if not scene.recent_sfx_keys.is_empty(): + failures.append("disabled SFX should suppress input audio requests: %s" % str(scene.recent_sfx_keys)) + scene.free() + + func _check_scene_post_move_text_fit(failures: Array[String]) -> void: var scene = BattleSceneScript.new() scene._create_hud()