feat(m77-c): battle SFX (contract BAT 1-60), region-1 battle/boss music and beat audio wiring

This commit is contained in:
2026-09-15 00:17:21 +08:00
parent eb324ecdc3
commit 92e7cfdedd
374 changed files with 8861 additions and 76 deletions
+27 -2
View File
@@ -240,6 +240,15 @@ var risk_commitment_receipt: Dictionary = {}
var battle_operation_id := ""
var battle_paused := false
var battle_speed := 1.0
const BATTLE_EVENT_AUDIO := {
"telegraph": "enemy_forecast_begin",
"guard": "guard_absorb",
"ranged": "ranged_release",
"magic": "area_resolve",
"healing": "heal_small",
"damage": "melee_impact_heavy",
"guarded_recovery": "status_benefit_apply",
}
var battle_beats_played := 0
var battle_beat_kinds: Dictionary = {}
var battle_notice := ""
@@ -3007,6 +3016,7 @@ func cancel_risk_commitment() -> void:
func begin_battle() -> void:
_audio_sfx("battle_begin")
_audio_sfx("battle_start_commit")
if risk_selected_id.is_empty():
show_risk_board(guild_company)
@@ -3052,7 +3062,7 @@ func begin_battle() -> void:
func show_battle_opening(company := {}, auto_focus := true) -> void:
_audio_enter("battle_opening", "run")
_audio_enter("battle_opening", _battle_music_family(company if company is Dictionary else guild_company))
_clear_battle_playback_timer()
battle_playback_generation += 1
battle_opening_mode = true
@@ -3524,6 +3534,14 @@ func _battle_profession_parent(profession_id: String) -> String:
return found.get_string() if found != null else ""
func _battle_music_family(company: Dictionary) -> String:
var battle: Dictionary = Dictionary(Dictionary(company.get("active_contract", {})).get("battle_state", {}))
for enemy_id in Array(battle.get("enemy_ids", [])):
if String(enemy_id).begins_with("BO-"):
return "boss"
return "battle"
func _apply_battle_actor_pose(portrait: TextureRect, recruit: Dictionary, battle: Dictionary, ally: bool) -> void:
# M77-B: persistent down pose for knocked-out allies, plus a per-event
# lunge/target read. Large motion is suppressed by the reduced-motion
@@ -3649,7 +3667,7 @@ func _battle_receipt_for_company(company: Dictionary, battle: Dictionary) -> Dic
func show_battle(company := {}, auto_play := false, resume_requires_review := false) -> void:
_audio_enter("battle", "run")
_audio_enter("battle", _battle_music_family(guild_company))
battle_opening_mode = false
battle_opening_state = ""
battle_opening_handoff_receipt = {}
@@ -3835,6 +3853,10 @@ func _build_battle_live_overlay(company: Dictionary, battle: Dictionary, view_da
art.set_beat(live_event_key, live_kind, live_source_anchor, live_target_anchor, int(view_event.get("amount", 0)))
battle_beats_played += 1
battle_beat_kinds[live_kind] = int(battle_beat_kinds.get(live_kind, 0)) + 1
if BATTLE_EVENT_AUDIO.has(live_kind):
_audio_sfx(String(BATTLE_EVENT_AUDIO[live_kind]))
if live_kind == "knockout":
_audio_sfx("recruit_knockout_confirmed")
battle_live_canvas.add_child(art)
var overlay := Control.new()
overlay.name = "BattleLiveLayout"
@@ -4067,6 +4089,7 @@ func _battle_pause_log_text(battle: Dictionary) -> String:
func _battle_pause_resume() -> void:
_audio_sfx("battle_resume")
if current_screen != "battle_pause_controls":
return
current_screen = "battle"
@@ -4703,6 +4726,7 @@ func _battle_live_toggle_inspect() -> void:
func _battle_live_open_retreat_entry() -> void:
_audio_sfx("retreat_available")
if battle_live_state in ["action_committed", "event_resolving", "invalid_event_data", "snapshot_retrying", "loading_resume", "outcome_locked", "interrupted_background", "no_resumable_battle"]:
return
if battle_live_state != "paused":
@@ -5065,6 +5089,7 @@ func toggle_battle_pause() -> void:
func cycle_battle_speed() -> void:
_audio_sfx("speed_up")
if battle_live_state in ["outcome_locked", "invalid_event_data", "snapshot_retrying", "loading_resume", "interrupted_background", "no_resumable_battle", "action_committed", "event_resolving", "inspect_handoff", "pause_controls_handoff"]:
return
battle_playback_generation += 1