Persist title audio settings
This commit is contained in:
@@ -111,6 +111,7 @@ Godot 4 tactical RPG prototype inspired by classic turn-based Romance of the Thr
|
||||
- Campaign completion screen.
|
||||
- New campaign save reset.
|
||||
- Manual campaign checkpoint save/load.
|
||||
- Title screen offers start, load, and settings; BGM/SFX volume, sound toggles, and edge-scroll preference persist in `user://heros_settings.json`.
|
||||
- Victory and defeat result overlay.
|
||||
- Dialogue portrait slot with officer default image paths, optional per-line overrides, and speaker-initial fallback.
|
||||
- Initial AI-generated photorealistic officer portraits for Cao Cao, Xiahou Dun, Xiahou Yuan, Cao Ren, Dian Wei, Guo Jia, and Zhang He.
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -26,6 +26,8 @@ const THREAT_OVERLAY_COLOR := Color(0.76, 0.060, 0.035, 0.20)
|
||||
const THREAT_BORDER_COLOR := Color(0.86, 0.18, 0.10, 0.48)
|
||||
const BGM_MENU_PATH := "res://audio/bgm/menu_theme_placeholder.wav"
|
||||
const BGM_BATTLE_PATH := "res://audio/bgm/battle_loop_placeholder.wav"
|
||||
const SETTINGS_PATH := "user://heros_settings.json"
|
||||
const SETTINGS_SAVE_VERSION := 1
|
||||
const SFX_BOW_RELEASE_PATH := "res://audio/sfx/bow_release_01.wav"
|
||||
const SFX_DEFEAT_STING_PATH := "res://audio/sfx/defeat_sting_01.wav"
|
||||
const SFX_FIRE_SKILL_PATH := "res://audio/sfx/fire_skill_01.wav"
|
||||
@@ -44,6 +46,7 @@ const UI_FONT_BOLD_PATH := "res://art/ui/fonts/GowunBatang-Bold.ttf"
|
||||
const BGM_VOLUME_DB := -14.0
|
||||
const SFX_VOLUME_DB := -4.0
|
||||
const UI_SFX_VOLUME_DB := -8.0
|
||||
const AUDIO_MUTED_DB := -80.0
|
||||
const FLOATING_TEXT_LIFETIME := 1.0
|
||||
const FLOATING_TEXT_RISE := 42.0
|
||||
const OBJECTIVE_NOTICE_DURATION := 2.6
|
||||
@@ -471,11 +474,17 @@ var title_settings_panel: PanelContainer
|
||||
var title_bgm_toggle: CheckButton
|
||||
var title_sfx_toggle: CheckButton
|
||||
var title_edge_scroll_toggle: CheckButton
|
||||
var title_bgm_volume_slider: HSlider
|
||||
var title_sfx_volume_slider: HSlider
|
||||
var title_bgm_volume_value_label: Label
|
||||
var title_sfx_volume_value_label: Label
|
||||
var title_settings_back_button: Button
|
||||
var title_screen_visible := false
|
||||
var title_bgm_enabled := true
|
||||
var title_sfx_enabled := true
|
||||
var title_edge_scroll_enabled := true
|
||||
var title_bgm_volume_percent := 100
|
||||
var title_sfx_volume_percent := 100
|
||||
var log_box: RichTextLabel
|
||||
var dialogue_queue: Array = []
|
||||
var active_dialogue_lines: Array = []
|
||||
@@ -504,6 +513,7 @@ var unit_idle_time := 0.0
|
||||
|
||||
func _ready() -> void:
|
||||
texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR
|
||||
_load_title_settings()
|
||||
_create_hud()
|
||||
_create_audio()
|
||||
state.changed.connect(_on_state_changed)
|
||||
@@ -3069,7 +3079,7 @@ func _create_hud() -> void:
|
||||
|
||||
title_settings_panel = PanelContainer.new()
|
||||
title_settings_panel.visible = false
|
||||
title_settings_panel.custom_minimum_size = Vector2(432, 134)
|
||||
title_settings_panel.custom_minimum_size = Vector2(432, 236)
|
||||
_apply_panel_style(title_settings_panel, "hud_info")
|
||||
title_menu_column.add_child(title_settings_panel)
|
||||
|
||||
@@ -3077,22 +3087,93 @@ func _create_hud() -> void:
|
||||
title_settings_column.add_theme_constant_override("separation", 6)
|
||||
title_settings_panel.add_child(title_settings_column)
|
||||
|
||||
var title_settings_caption := Label.new()
|
||||
title_settings_caption.text = "환경 설정"
|
||||
title_settings_caption.custom_minimum_size = Vector2(392, 24)
|
||||
title_settings_caption.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
title_settings_caption.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
_apply_label_style(title_settings_caption, UI_OLD_BRONZE, 15)
|
||||
title_settings_column.add_child(title_settings_caption)
|
||||
|
||||
title_bgm_toggle = CheckButton.new()
|
||||
title_bgm_toggle.text = "배경음"
|
||||
title_bgm_toggle.custom_minimum_size = Vector2(392, 30)
|
||||
title_bgm_toggle.button_pressed = title_bgm_enabled
|
||||
title_bgm_toggle.tooltip_text = "배경 음악을 켜거나 끕니다."
|
||||
title_bgm_toggle.toggled.connect(_on_title_bgm_toggled)
|
||||
title_settings_column.add_child(title_bgm_toggle)
|
||||
|
||||
var title_bgm_volume_row := HBoxContainer.new()
|
||||
title_bgm_volume_row.custom_minimum_size = Vector2(392, 34)
|
||||
title_bgm_volume_row.add_theme_constant_override("separation", 8)
|
||||
title_settings_column.add_child(title_bgm_volume_row)
|
||||
|
||||
var title_bgm_volume_caption := Label.new()
|
||||
title_bgm_volume_caption.text = "배경음 크기"
|
||||
title_bgm_volume_caption.custom_minimum_size = Vector2(94, 30)
|
||||
title_bgm_volume_caption.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
_apply_label_style(title_bgm_volume_caption, UI_PARCHMENT_TEXT, 13)
|
||||
title_bgm_volume_row.add_child(title_bgm_volume_caption)
|
||||
|
||||
title_bgm_volume_slider = HSlider.new()
|
||||
title_bgm_volume_slider.min_value = 0.0
|
||||
title_bgm_volume_slider.max_value = 100.0
|
||||
title_bgm_volume_slider.step = 5.0
|
||||
title_bgm_volume_slider.value = float(title_bgm_volume_percent)
|
||||
title_bgm_volume_slider.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
title_bgm_volume_slider.custom_minimum_size = Vector2(216, 30)
|
||||
title_bgm_volume_slider.tooltip_text = "배경 음악의 크기를 조절합니다."
|
||||
title_bgm_volume_slider.value_changed.connect(_on_title_bgm_volume_changed)
|
||||
title_bgm_volume_row.add_child(title_bgm_volume_slider)
|
||||
|
||||
title_bgm_volume_value_label = Label.new()
|
||||
title_bgm_volume_value_label.custom_minimum_size = Vector2(48, 30)
|
||||
title_bgm_volume_value_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
||||
title_bgm_volume_value_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
_apply_label_style(title_bgm_volume_value_label, UI_PARCHMENT_TEXT, 13)
|
||||
title_bgm_volume_row.add_child(title_bgm_volume_value_label)
|
||||
|
||||
title_sfx_toggle = CheckButton.new()
|
||||
title_sfx_toggle.text = "효과음"
|
||||
title_sfx_toggle.custom_minimum_size = Vector2(392, 30)
|
||||
title_sfx_toggle.button_pressed = title_sfx_enabled
|
||||
title_sfx_toggle.tooltip_text = "버튼과 전투 효과음을 켜거나 끕니다."
|
||||
title_sfx_toggle.toggled.connect(_on_title_sfx_toggled)
|
||||
title_settings_column.add_child(title_sfx_toggle)
|
||||
|
||||
var title_sfx_volume_row := HBoxContainer.new()
|
||||
title_sfx_volume_row.custom_minimum_size = Vector2(392, 34)
|
||||
title_sfx_volume_row.add_theme_constant_override("separation", 8)
|
||||
title_settings_column.add_child(title_sfx_volume_row)
|
||||
|
||||
var title_sfx_volume_caption := Label.new()
|
||||
title_sfx_volume_caption.text = "효과음 크기"
|
||||
title_sfx_volume_caption.custom_minimum_size = Vector2(94, 30)
|
||||
title_sfx_volume_caption.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
_apply_label_style(title_sfx_volume_caption, UI_PARCHMENT_TEXT, 13)
|
||||
title_sfx_volume_row.add_child(title_sfx_volume_caption)
|
||||
|
||||
title_sfx_volume_slider = HSlider.new()
|
||||
title_sfx_volume_slider.min_value = 0.0
|
||||
title_sfx_volume_slider.max_value = 100.0
|
||||
title_sfx_volume_slider.step = 5.0
|
||||
title_sfx_volume_slider.value = float(title_sfx_volume_percent)
|
||||
title_sfx_volume_slider.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
title_sfx_volume_slider.custom_minimum_size = Vector2(216, 30)
|
||||
title_sfx_volume_slider.tooltip_text = "버튼과 전투 효과음의 크기를 조절합니다."
|
||||
title_sfx_volume_slider.value_changed.connect(_on_title_sfx_volume_changed)
|
||||
title_sfx_volume_row.add_child(title_sfx_volume_slider)
|
||||
|
||||
title_sfx_volume_value_label = Label.new()
|
||||
title_sfx_volume_value_label.custom_minimum_size = Vector2(48, 30)
|
||||
title_sfx_volume_value_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
||||
title_sfx_volume_value_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||
_apply_label_style(title_sfx_volume_value_label, UI_PARCHMENT_TEXT, 13)
|
||||
title_sfx_volume_row.add_child(title_sfx_volume_value_label)
|
||||
|
||||
title_edge_scroll_toggle = CheckButton.new()
|
||||
title_edge_scroll_toggle.text = "가장자리 스크롤"
|
||||
title_edge_scroll_toggle.custom_minimum_size = Vector2(392, 30)
|
||||
title_edge_scroll_toggle.button_pressed = title_edge_scroll_enabled
|
||||
title_edge_scroll_toggle.tooltip_text = "마우스를 전장 가장자리에 두면 화면을 움직입니다."
|
||||
title_edge_scroll_toggle.toggled.connect(_on_title_edge_scroll_toggled)
|
||||
@@ -3161,9 +3242,79 @@ func _create_hud() -> void:
|
||||
func _create_audio() -> void:
|
||||
bgm_player = AudioStreamPlayer.new()
|
||||
bgm_player.name = "BgmPlayer"
|
||||
bgm_player.volume_db = BGM_VOLUME_DB
|
||||
bgm_player.volume_db = _bgm_volume_db()
|
||||
bgm_player.finished.connect(_on_bgm_finished)
|
||||
add_child(bgm_player)
|
||||
_apply_audio_settings()
|
||||
|
||||
|
||||
func _load_title_settings() -> void:
|
||||
if not FileAccess.file_exists(SETTINGS_PATH):
|
||||
return
|
||||
var parsed = JSON.parse_string(FileAccess.get_file_as_string(SETTINGS_PATH))
|
||||
if not parsed is Dictionary:
|
||||
return
|
||||
title_bgm_enabled = bool(parsed.get("bgm_enabled", title_bgm_enabled))
|
||||
title_sfx_enabled = bool(parsed.get("sfx_enabled", title_sfx_enabled))
|
||||
title_edge_scroll_enabled = bool(parsed.get("edge_scroll_enabled", title_edge_scroll_enabled))
|
||||
title_bgm_volume_percent = _clamp_volume_percent(parsed.get("bgm_volume_percent", title_bgm_volume_percent))
|
||||
title_sfx_volume_percent = _clamp_volume_percent(parsed.get("sfx_volume_percent", title_sfx_volume_percent))
|
||||
|
||||
|
||||
func _save_title_settings() -> bool:
|
||||
var file := FileAccess.open(SETTINGS_PATH, FileAccess.WRITE)
|
||||
if file == null:
|
||||
push_warning("Unable to save title settings: %s" % SETTINGS_PATH)
|
||||
return false
|
||||
var payload := {
|
||||
"save_version": SETTINGS_SAVE_VERSION,
|
||||
"bgm_enabled": title_bgm_enabled,
|
||||
"bgm_volume_percent": title_bgm_volume_percent,
|
||||
"sfx_enabled": title_sfx_enabled,
|
||||
"sfx_volume_percent": title_sfx_volume_percent,
|
||||
"edge_scroll_enabled": title_edge_scroll_enabled
|
||||
}
|
||||
file.store_string(JSON.stringify(payload, "\t"))
|
||||
return true
|
||||
|
||||
|
||||
func _apply_audio_settings() -> void:
|
||||
if bgm_player != null:
|
||||
bgm_player.volume_db = _bgm_volume_db()
|
||||
if not title_bgm_enabled:
|
||||
bgm_player.stop()
|
||||
|
||||
|
||||
func _clamp_volume_percent(value) -> int:
|
||||
return mini(maxi(int(value), 0), 100)
|
||||
|
||||
|
||||
func _bgm_volume_db() -> float:
|
||||
return _volume_percent_to_db(BGM_VOLUME_DB, title_bgm_volume_percent)
|
||||
|
||||
|
||||
func _effective_sfx_volume_db(base_volume_db: float) -> float:
|
||||
return _volume_percent_to_db(base_volume_db, title_sfx_volume_percent)
|
||||
|
||||
|
||||
func _volume_percent_to_db(base_volume_db: float, percent: int) -> float:
|
||||
var clamped := _clamp_volume_percent(percent)
|
||||
if clamped <= 0:
|
||||
return AUDIO_MUTED_DB
|
||||
return base_volume_db + linear_to_db(float(clamped) / 100.0)
|
||||
|
||||
|
||||
func _refresh_title_volume_controls() -> void:
|
||||
if title_bgm_volume_slider != null:
|
||||
title_bgm_volume_slider.set_value_no_signal(float(title_bgm_volume_percent))
|
||||
title_bgm_volume_slider.editable = title_bgm_enabled
|
||||
if title_sfx_volume_slider != null:
|
||||
title_sfx_volume_slider.set_value_no_signal(float(title_sfx_volume_percent))
|
||||
title_sfx_volume_slider.editable = title_sfx_enabled
|
||||
if title_bgm_volume_value_label != null:
|
||||
title_bgm_volume_value_label.text = "%d%%" % title_bgm_volume_percent
|
||||
if title_sfx_volume_value_label != null:
|
||||
title_sfx_volume_value_label.text = "%d%%" % title_sfx_volume_percent
|
||||
|
||||
|
||||
func _on_bgm_finished() -> void:
|
||||
@@ -3181,6 +3332,7 @@ func _play_bgm(key: String) -> void:
|
||||
current_bgm_key = key
|
||||
bgm_player.stop()
|
||||
return
|
||||
bgm_player.volume_db = _bgm_volume_db()
|
||||
if current_bgm_key == key and bgm_player.playing:
|
||||
return
|
||||
var stream := _bgm_stream_for(key)
|
||||
@@ -3209,7 +3361,7 @@ func _play_sfx(key: String, volume_db := SFX_VOLUME_DB) -> void:
|
||||
var player := AudioStreamPlayer.new()
|
||||
player.name = "Sfx_%s" % key
|
||||
player.stream = stream
|
||||
player.volume_db = volume_db
|
||||
player.volume_db = _effective_sfx_volume_db(volume_db)
|
||||
player.finished.connect(player.queue_free)
|
||||
add_child(player)
|
||||
player.play()
|
||||
@@ -9880,11 +10032,12 @@ func _update_title_menu_state() -> void:
|
||||
if title_settings_back_button != null:
|
||||
title_settings_back_button.visible = title_settings_panel != null and title_settings_panel.visible
|
||||
if title_bgm_toggle != null:
|
||||
title_bgm_toggle.button_pressed = title_bgm_enabled
|
||||
title_bgm_toggle.set_pressed_no_signal(title_bgm_enabled)
|
||||
if title_sfx_toggle != null:
|
||||
title_sfx_toggle.button_pressed = title_sfx_enabled
|
||||
title_sfx_toggle.set_pressed_no_signal(title_sfx_enabled)
|
||||
if title_edge_scroll_toggle != null:
|
||||
title_edge_scroll_toggle.button_pressed = title_edge_scroll_enabled
|
||||
title_edge_scroll_toggle.set_pressed_no_signal(title_edge_scroll_enabled)
|
||||
_refresh_title_volume_controls()
|
||||
|
||||
|
||||
func _format_title_status_text(has_load: bool) -> String:
|
||||
@@ -9967,6 +10120,14 @@ func _on_title_settings_back_pressed() -> void:
|
||||
|
||||
|
||||
func _set_title_settings_visible(visible: bool) -> void:
|
||||
if title_status_label != null:
|
||||
title_status_label.visible = not visible
|
||||
if title_start_button != null:
|
||||
title_start_button.visible = not visible
|
||||
if title_load_button != null:
|
||||
title_load_button.visible = not visible
|
||||
if title_settings_button != null:
|
||||
title_settings_button.visible = not visible
|
||||
if title_settings_panel != null:
|
||||
title_settings_panel.visible = visible
|
||||
if title_settings_back_button != null:
|
||||
@@ -9979,6 +10140,8 @@ func _on_title_bgm_toggled(enabled: bool) -> void:
|
||||
_play_bgm(current_bgm_key if not current_bgm_key.is_empty() else "menu")
|
||||
elif bgm_player != null:
|
||||
bgm_player.stop()
|
||||
_apply_audio_settings()
|
||||
_save_title_settings()
|
||||
_update_title_menu_state()
|
||||
|
||||
|
||||
@@ -9986,15 +10149,32 @@ func _on_title_sfx_toggled(enabled: bool) -> void:
|
||||
title_sfx_enabled = enabled
|
||||
if title_sfx_enabled:
|
||||
_play_sfx("ui_click", UI_SFX_VOLUME_DB)
|
||||
_save_title_settings()
|
||||
_update_title_menu_state()
|
||||
|
||||
|
||||
func _on_title_edge_scroll_toggled(enabled: bool) -> void:
|
||||
title_edge_scroll_enabled = enabled
|
||||
_save_title_settings()
|
||||
_update_title_menu_state()
|
||||
queue_redraw()
|
||||
|
||||
|
||||
func _on_title_bgm_volume_changed(value: float) -> void:
|
||||
title_bgm_volume_percent = _clamp_volume_percent(value)
|
||||
_apply_audio_settings()
|
||||
_save_title_settings()
|
||||
_refresh_title_volume_controls()
|
||||
|
||||
|
||||
func _on_title_sfx_volume_changed(value: float) -> void:
|
||||
title_sfx_volume_percent = _clamp_volume_percent(value)
|
||||
_save_title_settings()
|
||||
_refresh_title_volume_controls()
|
||||
if title_sfx_enabled:
|
||||
_play_sfx("ui_click", UI_SFX_VOLUME_DB)
|
||||
|
||||
|
||||
func _update_top_archive_buttons() -> void:
|
||||
var prep_available := (
|
||||
not battle_started
|
||||
|
||||
@@ -4,14 +4,17 @@ const BattleSceneScript := preload("res://scripts/scenes/battle_scene.gd")
|
||||
|
||||
const SAVE_PATH := "user://campaign_save.json"
|
||||
const MANUAL_SAVE_PATH := "user://campaign_manual_save.json"
|
||||
const SETTINGS_PATH := "user://heros_settings.json"
|
||||
|
||||
|
||||
func _init() -> void:
|
||||
var save_backup := _backup_user_file(SAVE_PATH)
|
||||
var manual_backup := _backup_user_file(MANUAL_SAVE_PATH)
|
||||
var settings_backup := _backup_user_file(SETTINGS_PATH)
|
||||
var failures: Array[String] = []
|
||||
_clear_user_file(SAVE_PATH)
|
||||
_clear_user_file(MANUAL_SAVE_PATH)
|
||||
_clear_user_file(SETTINGS_PATH)
|
||||
|
||||
_check_title_menu_start_load_and_settings(failures)
|
||||
|
||||
@@ -19,6 +22,8 @@ func _init() -> void:
|
||||
failures.append("could not restore campaign save after title menu smoke")
|
||||
if not _restore_user_file(MANUAL_SAVE_PATH, manual_backup):
|
||||
failures.append("could not restore manual campaign save after title menu smoke")
|
||||
if not _restore_user_file(SETTINGS_PATH, settings_backup):
|
||||
failures.append("could not restore title settings after title menu smoke")
|
||||
|
||||
if failures.is_empty():
|
||||
print("title menu smoke ok")
|
||||
@@ -52,17 +57,32 @@ func _check_title_menu_start_load_and_settings(failures: Array[String]) -> void:
|
||||
failures.append("title settings panel should open from the title screen")
|
||||
if scene.title_bgm_toggle == null or scene.title_sfx_toggle == null or scene.title_edge_scroll_toggle == null:
|
||||
failures.append("title settings should expose BGM, SFX, and edge-scroll toggles")
|
||||
if scene.title_bgm_volume_slider == null or scene.title_sfx_volume_slider == null:
|
||||
failures.append("title settings should expose BGM and SFX volume sliders")
|
||||
scene._on_title_bgm_toggled(false)
|
||||
if scene.title_bgm_enabled:
|
||||
failures.append("BGM toggle should disable background music")
|
||||
scene._on_title_bgm_volume_changed(45.0)
|
||||
if scene.title_bgm_volume_percent != 45:
|
||||
failures.append("BGM volume slider should update the saved BGM volume percent")
|
||||
scene._on_title_sfx_toggled(false)
|
||||
if scene.title_sfx_enabled:
|
||||
failures.append("SFX toggle should disable sound effects")
|
||||
scene._on_title_sfx_volume_changed(35.0)
|
||||
if scene.title_sfx_volume_percent != 35:
|
||||
failures.append("SFX volume slider should update the saved SFX volume percent")
|
||||
scene._on_title_edge_scroll_toggled(false)
|
||||
if scene.title_edge_scroll_enabled:
|
||||
failures.append("edge-scroll toggle should disable edge scrolling")
|
||||
if scene._update_edge_scroll(0.25):
|
||||
failures.append("edge scrolling should not move while disabled in settings")
|
||||
var persisted_scene = BattleSceneScript.new()
|
||||
persisted_scene._ready()
|
||||
if persisted_scene.title_bgm_enabled or persisted_scene.title_sfx_enabled or persisted_scene.title_edge_scroll_enabled:
|
||||
failures.append("title settings toggles should persist into a new scene instance")
|
||||
if persisted_scene.title_bgm_volume_percent != 45 or persisted_scene.title_sfx_volume_percent != 35:
|
||||
failures.append("title settings volume values should persist into a new scene instance")
|
||||
persisted_scene.free()
|
||||
scene._on_title_settings_back_pressed()
|
||||
if scene.title_settings_panel != null and scene.title_settings_panel.visible:
|
||||
failures.append("settings back should hide the title settings panel")
|
||||
|
||||
Reference in New Issue
Block a user