From 4806cef211d7871308bfb3b99fa9b41213e02c67 Mon Sep 17 00:00:00 2001 From: Wickedness Date: Sat, 20 Jun 2026 11:05:37 +0900 Subject: [PATCH] Simplify title menu layout --- README.md | 4 +- scripts/scenes/battle_scene.gd | 186 +++++++++++++++++---------------- tools/smoke_title_menu.gd | 84 ++++++--------- tools/smoke_visual_assets.gd | 7 +- 4 files changed, 132 insertions(+), 149 deletions(-) diff --git a/README.md b/README.md index c432cb8..3875490 100644 --- a/README.md +++ b/README.md @@ -131,7 +131,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. -- Cinematic title screen uses a dedicated ancient-war-camp dawn backdrop and Cao Cao portrait art, offers start/load/settings, opens with a wide generated Yellow Turban story feature panel plus four generated opening story previews in framed artwork cards, leads new campaigns through a generated four-panel Yellow Turban rebellion, Cao Cao resolve, Xiahou Dun meeting, and Yingchuan contact prologue with full-screen story artwork, a chapter seal, a highlighted four-beat story strip, and a bottom scroll caption panel, exposes the same audio/edge-scroll settings from the top in-game toolbar, includes a one-click audio recovery control, and persists BGM/SFX volume, sound toggles, and edge-scroll preference in `user://heros_settings.json`. +- Cinematic title screen uses a dedicated ancient-war-camp dawn backdrop, Cao Cao portrait art, and a compact left-side command board with `처음부터`, `로드하기`, and `환경설정`; new campaigns then move into a generated four-panel Yellow Turban rebellion, Cao Cao resolve, Xiahou Dun meeting, and Yingchuan contact prologue with full-screen story artwork, a chapter seal, a highlighted four-beat story strip, and a bottom scroll caption panel. Title and in-game settings expose clearer background-music, combat-SFX, and edge-scroll controls, include one-click audio recovery, and persist preferences in `user://heros_settings.json`. - Victory and defeat result overlay. - Dialogue portrait slot stays fixed across speaker and narration lines, with officer default image paths, optional per-line overrides, and speaker-initial or record fallback. - Initial AI-generated photorealistic officer portraits for Cao Cao, Xiahou Dun, Xiahou Yuan, Cao Ren, Dian Wei, Guo Jia, and Zhang He. @@ -139,7 +139,7 @@ Godot 4 tactical RPG prototype inspired by classic turn-based Romance of the Thr - Battle HUD portrait thumbnails show the selected unit, or a hovered unit when nothing is selected. - Selected and hovered unit focus text shows combat role, movement type, current terrain move cost, terrain bonuses, core stats, and status effects. - Dialogue lines can mark speaking side for text alignment inside a fixed visual-novel-style dialogue panel with stable portrait, progress, and previous-line controls. -- Longer procedural draft menu/battle BGM plus UI, movement, attack, tactic, item, victory, and defeat SFX. +- Longer procedural draft menu/battle BGM plus UI, movement, attack, tactic, item, victory, and defeat SFX, with a quieter 75% default volume so first launch is less harsh. - Floating combat text for damage, recovery, misses, support effects, poison ticks, action locks, level-ups, and promotions. - Short unit slide animation for board movement. - Board attacks now request class-specific motion profiles, with infantry strikes, archer traces, cavalry charges, command flashes, and heavy warrior/bandit impacts drawn by the battle scene. diff --git a/scripts/scenes/battle_scene.gd b/scripts/scenes/battle_scene.gd index f61210f..a870f94 100644 --- a/scripts/scenes/battle_scene.gd +++ b/scripts/scenes/battle_scene.gd @@ -88,6 +88,7 @@ const UI_FONT_BOLD_PATH := "res://art/ui/fonts/GowunBatang-Bold.ttf" const BGM_VOLUME_DB := -8.0 const SFX_VOLUME_DB := -2.0 const UI_SFX_VOLUME_DB := -4.0 +const DEFAULT_AUDIO_VOLUME_PERCENT := 75 const AUDIO_MUTED_DB := -80.0 const MAX_RECENT_SFX_KEYS := 32 const FLOATING_TEXT_LIFETIME := 1.0 @@ -197,6 +198,7 @@ const COMMAND_HINT_ICON_SIZE := Vector2(30, 30) const COMMAND_HINT_TITLE_SIZE := Vector2(184, 22) const COMMAND_HINT_DETAIL_SIZE := Vector2(208, 32) const PREP_MENU_BUTTON_SIZE := Vector2(48, 38) +const PANEL_CLOSE_BUTTON_SIZE := Vector2(36, 28) const BATTLE_UNIT_LIST_PANEL_SIZE := Vector2(420, 360) const BATTLE_UNIT_LIST_ROW_SIZE := Vector2(372, 58) const BATTLE_UNIT_LIST_ICON_SIZE := Vector2(46, 46) @@ -622,8 +624,8 @@ 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 title_bgm_volume_percent := DEFAULT_AUDIO_VOLUME_PERCENT +var title_sfx_volume_percent := DEFAULT_AUDIO_VOLUME_PERCENT var log_box: RichTextLabel var dialogue_queue: Array = [] var active_dialogue_lines: Array = [] @@ -1238,6 +1240,25 @@ func _configure_top_tool_button(button: Button, node_name: String, icon_kind: St _connect_command_hint_button(button) +func _configure_panel_close_button(button: Button, node_name: String, hint: String) -> void: + if button == null: + return + button.name = node_name + button.text = "" + button.tooltip_text = _format_local_command_tooltip("닫기", hint) + button.custom_minimum_size = PANEL_CLOSE_BUTTON_SIZE + button.size_flags_horizontal = Control.SIZE_SHRINK_CENTER + button.size_flags_vertical = Control.SIZE_SHRINK_CENTER + button.icon = _make_toolbar_icon("cancel") + button.expand_icon = true + button.add_theme_font_size_override("font_size", 1) + button.set_meta("icon_only", true) + button.set_meta("command_icon", "cancel") + button.set_meta("command_label", "닫기") + button.set_meta("command_hint", hint) + _apply_button_style(button) + + func _configure_title_menu_button(button: Button, node_name: String, icon_kind: String, label: String, tooltip: String, minimum_size: Vector2, important: bool = false) -> void: if button == null: return @@ -2866,7 +2887,7 @@ func _create_hud() -> void: top_tool_row.add_child(battle_unit_list_button) top_settings_button = Button.new() - _configure_top_tool_button(top_settings_button, "TopSettingsButton", "settings", "환경 설정\n배경음, 효과음, 가장자리 스크롤을 조절합니다.") + _configure_top_tool_button(top_settings_button, "TopSettingsButton", "settings", "환경 설정\n배경 음악, 전투 효과음, 가장자리 스크롤을 조절합니다.") top_settings_button.pressed.connect(_on_settings_pressed) top_tool_row.add_child(top_settings_button) @@ -2933,10 +2954,7 @@ func _create_hud() -> void: battle_unit_header_row.add_child(battle_unit_list_title_label) var battle_unit_close_button := Button.new() - battle_unit_close_button.text = "닫기" - battle_unit_close_button.tooltip_text = "군세 목록을 닫습니다." - battle_unit_close_button.custom_minimum_size = Vector2(76, 28) - _apply_button_style(battle_unit_close_button) + _configure_panel_close_button(battle_unit_close_button, "BattleUnitListCloseButton", "군세 목록을 닫습니다.") battle_unit_close_button.pressed.connect(_hide_battle_unit_list_panel) battle_unit_header_row.add_child(battle_unit_close_button) @@ -3005,19 +3023,16 @@ func _create_hud() -> void: settings_header_row.add_child(settings_title_label) var settings_close_button := Button.new() - settings_close_button.text = "닫기" - settings_close_button.tooltip_text = "환경 설정을 닫습니다." - settings_close_button.custom_minimum_size = Vector2(76, 28) - _apply_button_style(settings_close_button) + _configure_panel_close_button(settings_close_button, "SettingsCloseButton", "환경 설정을 닫습니다.") settings_close_button.pressed.connect(_hide_settings_panel) settings_header_row.add_child(settings_close_button) settings_column.add_child(_make_seal_ribbon(374, 12)) settings_bgm_toggle = CheckButton.new() - settings_bgm_toggle.text = "배경음" + settings_bgm_toggle.text = "배경 음악" settings_bgm_toggle.custom_minimum_size = Vector2(374, 30) - settings_bgm_toggle.tooltip_text = "배경 음악을 켜거나 끕니다." + settings_bgm_toggle.tooltip_text = "군막과 전투에서 흐르는 음악을 켜거나 끕니다." settings_bgm_toggle.toggled.connect(_on_settings_bgm_toggled) settings_column.add_child(settings_bgm_toggle) @@ -3027,7 +3042,7 @@ func _create_hud() -> void: settings_column.add_child(settings_bgm_volume_row) var settings_bgm_volume_caption := Label.new() - settings_bgm_volume_caption.text = "배경음 크기" + settings_bgm_volume_caption.text = "음악 크기" settings_bgm_volume_caption.custom_minimum_size = Vector2(94, 30) settings_bgm_volume_caption.vertical_alignment = VERTICAL_ALIGNMENT_CENTER _apply_label_style(settings_bgm_volume_caption, UI_PARCHMENT_TEXT, 13) @@ -3039,7 +3054,7 @@ func _create_hud() -> void: settings_bgm_volume_slider.step = 5.0 settings_bgm_volume_slider.size_flags_horizontal = Control.SIZE_EXPAND_FILL settings_bgm_volume_slider.custom_minimum_size = Vector2(194, 30) - settings_bgm_volume_slider.tooltip_text = "배경 음악의 크기를 조절합니다." + settings_bgm_volume_slider.tooltip_text = "군막과 전투 음악의 크기를 조절합니다." settings_bgm_volume_slider.value_changed.connect(_on_settings_bgm_volume_changed) settings_bgm_volume_row.add_child(settings_bgm_volume_slider) @@ -3051,9 +3066,9 @@ func _create_hud() -> void: settings_bgm_volume_row.add_child(settings_bgm_volume_value_label) settings_sfx_toggle = CheckButton.new() - settings_sfx_toggle.text = "효과음" + settings_sfx_toggle.text = "전투 효과음" settings_sfx_toggle.custom_minimum_size = Vector2(374, 30) - settings_sfx_toggle.tooltip_text = "버튼과 전투 효과음을 켜거나 끕니다." + settings_sfx_toggle.tooltip_text = "선택, 이동, 공격, 책략 효과음을 켜거나 끕니다." settings_sfx_toggle.toggled.connect(_on_settings_sfx_toggled) settings_column.add_child(settings_sfx_toggle) @@ -3063,7 +3078,7 @@ func _create_hud() -> void: settings_column.add_child(settings_sfx_volume_row) var settings_sfx_volume_caption := Label.new() - settings_sfx_volume_caption.text = "효과음 크기" + settings_sfx_volume_caption.text = "효과 크기" settings_sfx_volume_caption.custom_minimum_size = Vector2(94, 30) settings_sfx_volume_caption.vertical_alignment = VERTICAL_ALIGNMENT_CENTER _apply_label_style(settings_sfx_volume_caption, UI_PARCHMENT_TEXT, 13) @@ -3075,7 +3090,7 @@ func _create_hud() -> void: settings_sfx_volume_slider.step = 5.0 settings_sfx_volume_slider.size_flags_horizontal = Control.SIZE_EXPAND_FILL settings_sfx_volume_slider.custom_minimum_size = Vector2(194, 30) - settings_sfx_volume_slider.tooltip_text = "버튼과 전투 효과음의 크기를 조절합니다." + settings_sfx_volume_slider.tooltip_text = "선택과 전투 효과음의 크기를 조절합니다." settings_sfx_volume_slider.value_changed.connect(_on_settings_sfx_volume_changed) settings_sfx_volume_row.add_child(settings_sfx_volume_slider) @@ -3089,7 +3104,7 @@ func _create_hud() -> void: settings_audio_reset_button = Button.new() settings_audio_reset_button.text = "소리 켜기" settings_audio_reset_button.custom_minimum_size = Vector2(374, 32) - settings_audio_reset_button.tooltip_text = "배경음과 효과음을 켜고 음량을 기본값으로 되돌린 뒤 확인음을 재생합니다." + settings_audio_reset_button.tooltip_text = "군막 음악과 전투 효과음을 켜고 기본 음량으로 되돌린 뒤 확인음을 재생합니다." settings_audio_reset_button.pressed.connect(_on_settings_audio_reset_pressed) settings_column.add_child(settings_audio_reset_button) @@ -4297,17 +4312,17 @@ func _create_hud() -> void: var title_menu_shadow := ColorRect.new() title_menu_shadow.name = "TitleMenuInkPanel" - title_menu_shadow.position = Vector2(0, 0) - title_menu_shadow.size = Vector2(790, 720) - title_menu_shadow.color = Color(0.030, 0.018, 0.010, 0.58) + title_menu_shadow.position = Vector2(0, 28) + title_menu_shadow.size = Vector2(560, 608) + title_menu_shadow.color = Color(0.030, 0.018, 0.010, 0.50) title_menu_shadow.mouse_filter = Control.MOUSE_FILTER_IGNORE title_screen_root.add_child(title_menu_shadow) title_menu_surface_texture_rect = TextureRect.new() title_menu_surface_texture_rect.name = "TitleGeneratedCommandBoard" - title_menu_surface_texture_rect.position = Vector2(4, -88) - title_menu_surface_texture_rect.size = Vector2(806, 900) - title_menu_surface_texture_rect.custom_minimum_size = Vector2(806, 900) + title_menu_surface_texture_rect.position = Vector2(28, 32) + title_menu_surface_texture_rect.size = Vector2(520, 586) + title_menu_surface_texture_rect.custom_minimum_size = Vector2(520, 586) title_menu_surface_texture_rect.mouse_filter = Control.MOUSE_FILTER_IGNORE title_menu_surface_texture_rect.expand_mode = TextureRect.EXPAND_IGNORE_SIZE title_menu_surface_texture_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_COVERED @@ -4318,16 +4333,16 @@ func _create_hud() -> void: var title_portrait_shadow := ColorRect.new() title_portrait_shadow.name = "TitlePortraitLacquerField" - title_portrait_shadow.position = Vector2(770, 36) - title_portrait_shadow.size = Vector2(430, 628) + title_portrait_shadow.position = Vector2(646, 34) + title_portrait_shadow.size = Vector2(512, 628) title_portrait_shadow.color = Color(0.045, 0.018, 0.010, 0.56) title_portrait_shadow.mouse_filter = Control.MOUSE_FILTER_IGNORE title_screen_root.add_child(title_portrait_shadow) title_portrait_texture_rect = TextureRect.new() title_portrait_texture_rect.name = "TitleCaoCaoPortrait" - title_portrait_texture_rect.position = Vector2(804, 48) - title_portrait_texture_rect.size = Vector2(388, 606) + title_portrait_texture_rect.position = Vector2(684, 50) + title_portrait_texture_rect.size = Vector2(436, 606) title_portrait_texture_rect.mouse_filter = Control.MOUSE_FILTER_IGNORE title_portrait_texture_rect.expand_mode = TextureRect.EXPAND_IGNORE_SIZE title_portrait_texture_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_COVERED @@ -4338,8 +4353,8 @@ func _create_hud() -> void: var title_portrait_caption := Label.new() title_portrait_caption.name = "TitlePortraitCaption" title_portrait_caption.text = "조조" - title_portrait_caption.position = Vector2(818, 604) - title_portrait_caption.size = Vector2(350, 34) + title_portrait_caption.position = Vector2(708, 604) + title_portrait_caption.size = Vector2(386, 34) title_portrait_caption.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT title_portrait_caption.vertical_alignment = VERTICAL_ALIGNMENT_CENTER title_portrait_caption.mouse_filter = Control.MOUSE_FILTER_IGNORE @@ -4348,22 +4363,22 @@ func _create_hud() -> void: title_panel = PanelContainer.new() title_panel.name = "TitleMenuPanel" - title_panel.position = Vector2(40, 8) - title_panel.size = Vector2(730, 704) - title_panel.custom_minimum_size = Vector2(730, 704) + title_panel.position = Vector2(58, 78) + title_panel.size = Vector2(462, 486) + title_panel.custom_minimum_size = Vector2(462, 486) _apply_panel_style(title_panel, "transparent_overlay") title_screen_root.add_child(title_panel) var title_column := VBoxContainer.new() title_column.alignment = BoxContainer.ALIGNMENT_CENTER - title_column.add_theme_constant_override("separation", 5) + title_column.add_theme_constant_override("separation", 8) title_panel.add_child(title_column) - title_column.add_child(_make_scroll_rod(646, 5)) - title_column.add_child(_make_seal_ribbon(646, 18)) + title_column.add_child(_make_scroll_rod(430, 5)) + title_column.add_child(_make_seal_ribbon(430, 18)) title_caption_label = Label.new() title_caption_label.text = "조조전" - title_caption_label.custom_minimum_size = Vector2(646, 42) + title_caption_label.custom_minimum_size = Vector2(430, 48) title_caption_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER title_caption_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER _apply_label_style(title_caption_label, UI_PARCHMENT_TEXT, 34) @@ -4371,31 +4386,24 @@ func _create_hud() -> void: title_subtitle_label = Label.new() title_subtitle_label.text = "난세를 여는 첫 군령" - title_subtitle_label.custom_minimum_size = Vector2(646, 22) + title_subtitle_label.custom_minimum_size = Vector2(430, 24) title_subtitle_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER title_subtitle_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER _apply_label_style(title_subtitle_label, UI_OLD_BRONZE, 17) title_column.add_child(title_subtitle_label) - title_column.add_child(_make_title_story_feature_panel()) - title_column.add_child(_make_title_story_preview_strip()) - var title_body_panel := PanelContainer.new() - title_body_panel.custom_minimum_size = Vector2(646, 232) + title_body_panel.custom_minimum_size = Vector2(430, 330) _apply_panel_style(title_body_panel, "command_seal") title_column.add_child(title_body_panel) - var title_body_row := HBoxContainer.new() - title_body_row.add_theme_constant_override("separation", 10) - title_body_panel.add_child(title_body_row) - title_body_row.add_child(_make_edict_marker_stack(["", "", ""], 208)) - var title_menu_column := VBoxContainer.new() + title_menu_column.alignment = BoxContainer.ALIGNMENT_CENTER title_menu_column.add_theme_constant_override("separation", 10) - title_body_row.add_child(title_menu_column) + title_body_panel.add_child(title_menu_column) title_status_label = Label.new() - title_status_label.custom_minimum_size = Vector2(462, 44) + title_status_label.custom_minimum_size = Vector2(390, 42) title_status_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER title_status_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER title_status_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART @@ -4407,9 +4415,9 @@ func _create_hud() -> void: title_start_button, "TitleStartButton", "new", - "처음부터 시작", + "처음부터", "새 전기를 열고 첫 전장부터 시작합니다.", - Vector2(462, 46), + Vector2(390, 58), true ) title_start_button.pressed.connect(_on_title_start_pressed) @@ -4420,9 +4428,9 @@ func _create_hud() -> void: title_load_button, "TitleLoadButton", "load", - "게임 로드", + "로드하기", "저장된 전기를 불러옵니다.", - Vector2(462, 46) + Vector2(390, 58) ) title_load_button.pressed.connect(_on_title_load_pressed) title_menu_column.add_child(title_load_button) @@ -4432,16 +4440,16 @@ func _create_hud() -> void: title_settings_button, "TitleSettingsButton", "settings", - "환경 설정", + "환경설정", "소리와 전장 조작 옵션을 바꿉니다.", - Vector2(462, 46) + Vector2(390, 58) ) title_settings_button.pressed.connect(_on_title_settings_pressed) title_menu_column.add_child(title_settings_button) title_load_panel = PanelContainer.new() title_load_panel.visible = false - title_load_panel.custom_minimum_size = Vector2(462, 188) + title_load_panel.custom_minimum_size = Vector2(390, 188) _apply_panel_style(title_load_panel, "hud_info") title_menu_column.add_child(title_load_panel) @@ -4451,7 +4459,7 @@ func _create_hud() -> void: var title_load_caption := Label.new() title_load_caption.text = "불러올 기록" - title_load_caption.custom_minimum_size = Vector2(422, 24) + title_load_caption.custom_minimum_size = Vector2(350, 24) title_load_caption.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER title_load_caption.vertical_alignment = VERTICAL_ALIGNMENT_CENTER _apply_label_style(title_load_caption, UI_OLD_BRONZE, 15) @@ -4464,7 +4472,7 @@ func _create_hud() -> void: "campaign", "자동 기록 없음", "자동으로 봉인된 진행 기록을 엽니다.", - Vector2(422, 38) + Vector2(350, 38) ) title_load_auto_button.pressed.connect(_on_title_load_auto_pressed) title_load_column.add_child(title_load_auto_button) @@ -4476,7 +4484,7 @@ func _create_hud() -> void: "save", "수동 봉인 없음", "군막에서 직접 남긴 수동 봉인을 엽니다.", - Vector2(422, 38) + Vector2(350, 38) ) title_load_manual_button.pressed.connect(_on_title_load_manual_pressed) title_load_column.add_child(title_load_manual_button) @@ -4488,14 +4496,14 @@ func _create_hud() -> void: "cancel", "뒤로", "처음 화면 선택으로 돌아갑니다.", - Vector2(422, 34) + Vector2(350, 34) ) title_load_back_button.pressed.connect(_on_title_load_back_pressed) title_load_column.add_child(title_load_back_button) title_settings_panel = PanelContainer.new() title_settings_panel.visible = false - title_settings_panel.custom_minimum_size = Vector2(462, 268) + title_settings_panel.custom_minimum_size = Vector2(390, 276) _apply_panel_style(title_settings_panel, "hud_info") title_menu_column.add_child(title_settings_panel) @@ -4505,28 +4513,28 @@ func _create_hud() -> void: var title_settings_caption := Label.new() title_settings_caption.text = "환경 설정" - title_settings_caption.custom_minimum_size = Vector2(422, 24) + title_settings_caption.custom_minimum_size = Vector2(350, 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(422, 30) + title_bgm_toggle.text = "군막 음악" + title_bgm_toggle.custom_minimum_size = Vector2(350, 30) title_bgm_toggle.button_pressed = title_bgm_enabled - title_bgm_toggle.tooltip_text = "배경 음악을 켜거나 끕니다." + 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(422, 34) + title_bgm_volume_row.custom_minimum_size = Vector2(350, 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.text = "음악 크기" + title_bgm_volume_caption.custom_minimum_size = Vector2(82, 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) @@ -4537,8 +4545,8 @@ func _create_hud() -> void: 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.custom_minimum_size = Vector2(172, 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) @@ -4550,21 +4558,21 @@ func _create_hud() -> void: 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(422, 30) + title_sfx_toggle.text = "전투 효과음" + title_sfx_toggle.custom_minimum_size = Vector2(350, 30) title_sfx_toggle.button_pressed = title_sfx_enabled - title_sfx_toggle.tooltip_text = "버튼과 전투 효과음을 켜거나 끕니다." + 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(422, 34) + title_sfx_volume_row.custom_minimum_size = Vector2(350, 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.text = "효과 크기" + title_sfx_volume_caption.custom_minimum_size = Vector2(82, 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) @@ -4575,8 +4583,8 @@ func _create_hud() -> void: 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.custom_minimum_size = Vector2(172, 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) @@ -4593,15 +4601,15 @@ func _create_hud() -> void: "TitleAudioResetButton", "status", "소리 켜기", - "배경음과 효과음을 켜고 음량을 기본값으로 되돌린 뒤 확인음을 재생합니다.", - Vector2(422, 32) + "군막 음악과 전투 효과음을 켜고 기본 음량으로 되돌린 뒤 확인음을 재생합니다.", + Vector2(350, 32) ) title_audio_reset_button.pressed.connect(_on_title_audio_reset_pressed) title_settings_column.add_child(title_audio_reset_button) title_edge_scroll_toggle = CheckButton.new() title_edge_scroll_toggle.text = "가장자리 스크롤" - title_edge_scroll_toggle.custom_minimum_size = Vector2(422, 30) + title_edge_scroll_toggle.custom_minimum_size = Vector2(350, 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) @@ -4614,13 +4622,13 @@ func _create_hud() -> void: "cancel", "설정 닫기", "환경 설정을 접습니다.", - Vector2(462, 34) + Vector2(390, 34) ) title_settings_back_button.pressed.connect(_on_title_settings_back_pressed) + title_settings_back_button.visible = false title_menu_column.add_child(title_settings_back_button) - title_body_row.add_child(_make_edict_marker_stack(["", "", ""], 222)) - title_column.add_child(_make_scroll_rod(646, 4)) + title_column.add_child(_make_scroll_rod(430, 4)) for hud_button in [ restart_button, @@ -12928,7 +12936,7 @@ func _update_settings_button() -> void: top_settings_button, "환경 설정", not can_show, - "배경음, 효과음, 가장자리 스크롤을 조절합니다." if can_show else "타이틀 화면에서는 왼쪽 메뉴의 환경 설정을 사용합니다." + "배경 음악, 전투 효과음, 가장자리 스크롤을 조절합니다." if can_show else "타이틀 화면에서는 왼쪽 메뉴의 환경 설정을 사용합니다." ) if not can_show: _hide_settings_panel() @@ -13781,8 +13789,8 @@ func _on_title_audio_reset_pressed() -> void: func _restore_audio_defaults(play_preview: bool) -> void: title_bgm_enabled = true title_sfx_enabled = true - title_bgm_volume_percent = 100 - title_sfx_volume_percent = 100 + title_bgm_volume_percent = DEFAULT_AUDIO_VOLUME_PERCENT + title_sfx_volume_percent = DEFAULT_AUDIO_VOLUME_PERCENT _apply_audio_settings() _save_title_settings() _refresh_title_volume_controls() diff --git a/tools/smoke_title_menu.gd b/tools/smoke_title_menu.gd index d4ba899..a48b6ba 100644 --- a/tools/smoke_title_menu.gd +++ b/tools/smoke_title_menu.gd @@ -72,6 +72,8 @@ func _check_title_menu_start_load_and_settings(failures: Array[String]) -> void: failures.append("title screen should show Cao Cao portrait art") if scene.title_panel == null or scene.title_panel.position.x > 100.0: failures.append("title menu panel should sit on the left side of the cinematic title screen") + elif scene.title_portrait_texture_rect != null and scene.title_panel.position.x + scene.title_panel.size.x >= scene.title_portrait_texture_rect.position.x: + failures.append("title menu commands should stay clearly to the left of Cao Cao portrait") if scene.title_menu_surface_texture_rect == null or scene.title_menu_surface_texture_rect.texture == null: failures.append("title screen should use a generated command-board surface behind the menu") else: @@ -79,8 +81,8 @@ func _check_title_menu_start_load_and_settings(failures: Array[String]) -> void: failures.append("title command board should carry generated surface metadata") if scene.title_menu_surface_texture_rect.texture.get_width() < 1024 or scene.title_menu_surface_texture_rect.texture.get_height() < 1024: failures.append("title command board should keep high-resolution generated pixels") - if scene.title_menu_surface_texture_rect.custom_minimum_size.y < 860.0: - failures.append("title command board should extend beyond the viewport for a full ceremonial frame") + if scene.title_menu_surface_texture_rect.custom_minimum_size.x > 620.0 or scene.title_menu_surface_texture_rect.custom_minimum_size.y > 660.0: + failures.append("title command board should stay compact so the three commands are not clipped: %s" % str(scene.title_menu_surface_texture_rect.custom_minimum_size)) if scene.title_panel != null and str(scene.title_panel.get_meta("panel_style_variant", "")) != "transparent_overlay": failures.append("title menu panel should be a transparent content layer over generated art") if scene.title_caption_label == null or scene.title_caption_label.text != "조조전": @@ -88,71 +90,47 @@ func _check_title_menu_start_load_and_settings(failures: Array[String]) -> void: var title_story_feature: Node = null if scene.title_screen_root != null: title_story_feature = scene.title_screen_root.find_child("TitleStoryFeaturePanel", true, false) - if title_story_feature == null: - failures.append("title screen should feature a large generated opening story panel") - else: - var feature_image := title_story_feature.find_child("TitleStoryFeatureImage", true, false) as TextureRect - var feature_caption := title_story_feature.find_child("TitleStoryFeatureCaption", true, false) as PanelContainer - var feature_title := title_story_feature.find_child("TitleStoryFeatureTitle", true, false) as Label - var feature_body := title_story_feature.find_child("TitleStoryFeatureBody", true, false) as Label - if feature_image == null or feature_image.texture == null: - failures.append("title story feature should load generated opening artwork") - elif feature_image.custom_minimum_size.x < 600.0 or feature_image.custom_minimum_size.y < 160.0: - failures.append("title story feature should lead with a wide generated artwork frame: %s" % str(feature_image.custom_minimum_size)) - if feature_caption == null or not bool(feature_caption.get_meta("generated_panel_texture", false)): - failures.append("title story feature should use a generated caption command plate") - if feature_title == null or feature_title.text != "황건의 난": - failures.append("title story feature should name the first opening beat") - if feature_body == null or not feature_body.text.contains("조조") or not feature_body.text.contains("황건의 난"): - failures.append("title story feature should summarize the first opening beat with a short Korean teaser") - var feature_frame := title_story_feature.find_child("TitleStoryFeatureImageFrame", true, false) as PanelContainer - if feature_frame == null or not bool(feature_frame.get_meta("generated_panel_texture", false)): - failures.append("title story feature artwork should sit inside a generated frame") + if title_story_feature != null: + failures.append("title screen should not crowd the first menu with opening story feature artwork") var title_story_strip: Node = null if scene.title_screen_root != null: title_story_strip = scene.title_screen_root.find_child("TitleStoryPreviewStrip", true, false) - if title_story_strip == null: - failures.append("title screen should preview the four generated opening story panels") - else: - var story_cards: Array = title_story_strip.find_children("TitleStoryPreviewCard*", "", true, false) - if story_cards.size() != 4: - failures.append("title story preview should expose four story beats: %d" % story_cards.size()) - for story_card in story_cards: - if not story_card is PanelContainer: - failures.append("title story preview cards should use generated panel frames") - elif not bool((story_card as PanelContainer).get_meta("generated_panel_texture", false)): - failures.append("title story preview card should use a generated panel texture") - var story_frame := (story_card as Node).find_child("TitleStoryPreviewImageFrame", true, false) as PanelContainer - var story_image := (story_card as Node).find_child("TitleStoryPreviewImage", true, false) as TextureRect - var story_label := (story_card as Node).find_child("TitleStoryPreviewLabel", true, false) as Label - if story_frame == null or not bool(story_frame.get_meta("generated_panel_texture", false)): - failures.append("title story preview image should sit inside a generated frame") - if story_image == null or story_image.texture == null: - failures.append("title story preview card should load generated artwork") - elif story_image.custom_minimum_size.y < 64.0: - failures.append("title story preview image should stay large enough to read as artwork: %s" % str(story_image.custom_minimum_size)) - if story_label == null or story_label.text.strip_edges().is_empty(): - failures.append("title story preview card should name its story beat") + if title_story_strip != null: + failures.append("title screen should keep opening story previews inside the prologue, not the first menu") if scene.briefing_panel == null or scene.briefing_panel.visible: failures.append("briefing should stay hidden behind the title screen") - if scene.title_start_button == null or scene.title_start_button.text != "처음부터 시작": - failures.append("title screen should expose Start from Beginning") + if scene.title_start_button == null or scene.title_start_button.text != "처음부터": + failures.append("title screen should expose `처음부터`") else: + if not scene.title_start_button.visible: + failures.append("title start command should be visible on first screen") + if scene.title_start_button.custom_minimum_size.y < 54.0: + failures.append("title start command should be tall enough to read clearly") _check_title_menu_button_art(failures, scene.title_start_button, "new", "title start") - if scene.title_load_button == null or scene.title_load_button.text != "게임 로드": - failures.append("title screen should expose Game Load") + if scene.title_load_button == null or scene.title_load_button.text != "로드하기": + failures.append("title screen should expose `로드하기`") elif not scene.title_load_button.disabled: - failures.append("Game Load should be disabled when no save exists") + failures.append("Load should be disabled when no save exists") else: + if not scene.title_load_button.visible: + failures.append("title load command should be visible on first screen") + if scene.title_load_button.custom_minimum_size.y < 54.0: + failures.append("title load command should be tall enough to read clearly") _check_title_menu_button_art(failures, scene.title_load_button, "load", "title load") if scene.title_load_panel == null: failures.append("title screen should create a load-slot selection panel") elif scene.title_load_panel.visible: failures.append("load-slot selection panel should stay hidden until Game Load is pressed") - if scene.title_settings_button == null or scene.title_settings_button.text != "환경 설정": - failures.append("title screen should expose Settings") + if scene.title_settings_button == null or scene.title_settings_button.text != "환경설정": + failures.append("title screen should expose `환경설정`") else: + if not scene.title_settings_button.visible: + failures.append("title settings command should be visible on first screen") + if scene.title_settings_button.custom_minimum_size.y < 54.0: + failures.append("title settings command should be tall enough to read clearly") _check_title_menu_button_art(failures, scene.title_settings_button, "settings", "title settings") + if scene.title_settings_back_button != null and scene.title_settings_back_button.visible: + failures.append("title settings back command should stay hidden until settings are open") scene._on_title_settings_pressed() if scene.title_settings_panel == null or not scene.title_settings_panel.visible: @@ -194,7 +172,7 @@ func _check_title_menu_start_load_and_settings(failures: Array[String]) -> void: scene._on_title_audio_reset_pressed() if not scene.title_bgm_enabled or not scene.title_sfx_enabled: failures.append("title audio recovery should re-enable BGM and SFX") - if scene.title_bgm_volume_percent != 100 or scene.title_sfx_volume_percent != 100: + if scene.title_bgm_volume_percent != BattleSceneScript.DEFAULT_AUDIO_VOLUME_PERCENT or scene.title_sfx_volume_percent != BattleSceneScript.DEFAULT_AUDIO_VOLUME_PERCENT: failures.append("title audio recovery should restore audible volume defaults") if scene.bgm_player == null or scene.bgm_player.stream == null or scene.current_bgm_key.is_empty(): failures.append("title audio recovery should leave a playable BGM stream selected") @@ -373,7 +351,7 @@ func _check_title_menu_start_load_and_settings(failures: Array[String]) -> void: scene._on_settings_audio_reset_pressed() if not scene.title_bgm_enabled or not scene.title_sfx_enabled: failures.append("in-game audio recovery should re-enable BGM and SFX") - if scene.title_bgm_volume_percent != 100 or scene.title_sfx_volume_percent != 100: + if scene.title_bgm_volume_percent != BattleSceneScript.DEFAULT_AUDIO_VOLUME_PERCENT or scene.title_sfx_volume_percent != BattleSceneScript.DEFAULT_AUDIO_VOLUME_PERCENT: failures.append("in-game audio recovery should restore audible volume defaults") scene._hide_settings_panel() scene._on_new_campaign_pressed() diff --git a/tools/smoke_visual_assets.gd b/tools/smoke_visual_assets.gd index 1507034..a9acee9 100644 --- a/tools/smoke_visual_assets.gd +++ b/tools/smoke_visual_assets.gd @@ -3273,11 +3273,8 @@ func _check_core_text_visibility(failures: Array[String]) -> void: _check_readable_text_control(failures, scene.title_caption_label, "title campaign name", 26) _check_readable_text_control(failures, scene.title_subtitle_label, "title subtitle", 13) var title_feature: Node = scene.title_screen_root.find_child("TitleStoryFeaturePanel", true, false) if scene.title_screen_root != null else null - if title_feature == null: - failures.append("title text visibility check could not find story feature panel") - else: - _check_readable_text_control(failures, title_feature.find_child("TitleStoryFeatureTitle", true, false) as Control, "title story feature title", 14) - _check_readable_text_control(failures, title_feature.find_child("TitleStoryFeatureBody", true, false) as Control, "title story feature body", 10) + if title_feature != null: + failures.append("title text visibility should keep story artwork out of the first menu") _check_readable_text_control(failures, scene.title_start_button, "title start button", 12) _check_readable_text_control(failures, scene.title_load_button, "title load button", 12) _check_readable_text_control(failures, scene.title_settings_button, "title settings button", 12)