diff --git a/scripts/scenes/battle_scene.gd b/scripts/scenes/battle_scene.gd index ee9cce9..aab5c62 100644 --- a/scripts/scenes/battle_scene.gd +++ b/scripts/scenes/battle_scene.gd @@ -1145,6 +1145,25 @@ func _configure_top_tool_button(button: Button, node_name: String, icon_kind: St button.add_theme_font_size_override("font_size", 1) +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 + button.name = node_name + button.text = label + button.tooltip_text = tooltip + button.custom_minimum_size = minimum_size + button.icon = _make_toolbar_icon(icon_kind) + button.expand_icon = false + button.icon_alignment = HORIZONTAL_ALIGNMENT_LEFT + button.vertical_icon_alignment = VERTICAL_ALIGNMENT_CENTER + button.alignment = HORIZONTAL_ALIGNMENT_CENTER + button.add_theme_constant_override("h_separation", 12) + button.add_theme_font_size_override("font_size", 16 if important else 14) + button.set_meta("title_menu_button", true) + button.set_meta("command_icon", icon_kind) + button.set_meta("command_label", label) + + func _configure_prep_menu_button(button: Button, node_name: String, icon_kind: String, label: String, hint: String) -> void: if button == null: return @@ -4100,23 +4119,39 @@ func _create_hud() -> void: title_menu_column.add_child(title_status_label) title_start_button = Button.new() - title_start_button.text = "처음부터 시작" - title_start_button.custom_minimum_size = Vector2(462, 44) - title_start_button.tooltip_text = "새 전기를 열고 첫 전장부터 시작합니다." + _configure_title_menu_button( + title_start_button, + "TitleStartButton", + "new", + "처음부터 시작", + "새 전기를 열고 첫 전장부터 시작합니다.", + Vector2(462, 46), + true + ) title_start_button.pressed.connect(_on_title_start_pressed) title_menu_column.add_child(title_start_button) title_load_button = Button.new() - title_load_button.text = "게임 로드" - title_load_button.custom_minimum_size = Vector2(462, 44) - title_load_button.tooltip_text = "저장된 전기를 불러옵니다." + _configure_title_menu_button( + title_load_button, + "TitleLoadButton", + "load", + "게임 로드", + "저장된 전기를 불러옵니다.", + Vector2(462, 46) + ) title_load_button.pressed.connect(_on_title_load_pressed) title_menu_column.add_child(title_load_button) title_settings_button = Button.new() - title_settings_button.text = "환경 설정" - title_settings_button.custom_minimum_size = Vector2(462, 44) - title_settings_button.tooltip_text = "소리와 전장 조작 옵션을 바꿉니다." + _configure_title_menu_button( + title_settings_button, + "TitleSettingsButton", + "settings", + "환경 설정", + "소리와 전장 조작 옵션을 바꿉니다.", + Vector2(462, 46) + ) title_settings_button.pressed.connect(_on_title_settings_pressed) title_menu_column.add_child(title_settings_button) @@ -4139,21 +4174,38 @@ func _create_hud() -> void: title_load_column.add_child(title_load_caption) title_load_auto_button = Button.new() - title_load_auto_button.custom_minimum_size = Vector2(422, 38) - title_load_auto_button.tooltip_text = "자동으로 봉인된 진행 기록을 엽니다." + _configure_title_menu_button( + title_load_auto_button, + "TitleLoadAutoButton", + "campaign", + "자동 기록 없음", + "자동으로 봉인된 진행 기록을 엽니다.", + Vector2(422, 38) + ) title_load_auto_button.pressed.connect(_on_title_load_auto_pressed) title_load_column.add_child(title_load_auto_button) title_load_manual_button = Button.new() - title_load_manual_button.custom_minimum_size = Vector2(422, 38) - title_load_manual_button.tooltip_text = "군막에서 직접 남긴 수동 봉인을 엽니다." + _configure_title_menu_button( + title_load_manual_button, + "TitleLoadManualButton", + "save", + "수동 봉인 없음", + "군막에서 직접 남긴 수동 봉인을 엽니다.", + Vector2(422, 38) + ) title_load_manual_button.pressed.connect(_on_title_load_manual_pressed) title_load_column.add_child(title_load_manual_button) title_load_back_button = Button.new() - title_load_back_button.text = "뒤로" - title_load_back_button.custom_minimum_size = Vector2(422, 34) - title_load_back_button.tooltip_text = "처음 화면 선택으로 돌아갑니다." + _configure_title_menu_button( + title_load_back_button, + "TitleLoadBackButton", + "cancel", + "뒤로", + "처음 화면 선택으로 돌아갑니다.", + Vector2(422, 34) + ) title_load_back_button.pressed.connect(_on_title_load_back_pressed) title_load_column.add_child(title_load_back_button) @@ -4252,9 +4304,14 @@ func _create_hud() -> void: title_sfx_volume_row.add_child(title_sfx_volume_value_label) title_audio_reset_button = Button.new() - title_audio_reset_button.text = "소리 켜기" - title_audio_reset_button.custom_minimum_size = Vector2(422, 32) - title_audio_reset_button.tooltip_text = "배경음과 효과음을 켜고 음량을 기본값으로 되돌린 뒤 확인음을 재생합니다." + _configure_title_menu_button( + title_audio_reset_button, + "TitleAudioResetButton", + "status", + "소리 켜기", + "배경음과 효과음을 켜고 음량을 기본값으로 되돌린 뒤 확인음을 재생합니다.", + Vector2(422, 32) + ) title_audio_reset_button.pressed.connect(_on_title_audio_reset_pressed) title_settings_column.add_child(title_audio_reset_button) @@ -4267,9 +4324,14 @@ func _create_hud() -> void: title_settings_column.add_child(title_edge_scroll_toggle) title_settings_back_button = Button.new() - title_settings_back_button.text = "설정 닫기" - title_settings_back_button.custom_minimum_size = Vector2(462, 34) - title_settings_back_button.tooltip_text = "환경 설정을 접습니다." + _configure_title_menu_button( + title_settings_back_button, + "TitleSettingsBackButton", + "cancel", + "설정 닫기", + "환경 설정을 접습니다.", + Vector2(462, 34) + ) title_settings_back_button.pressed.connect(_on_title_settings_back_pressed) title_menu_column.add_child(title_settings_back_button) diff --git a/tools/smoke_title_menu.gd b/tools/smoke_title_menu.gd index bf8f494..f739f82 100644 --- a/tools/smoke_title_menu.gd +++ b/tools/smoke_title_menu.gd @@ -123,16 +123,22 @@ func _check_title_menu_start_load_and_settings(failures: Array[String]) -> void: 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") + else: + _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") elif not scene.title_load_button.disabled: failures.append("Game Load should be disabled when no save exists") + else: + _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") + else: + _check_title_menu_button_art(failures, scene.title_settings_button, "settings", "title settings") scene._on_title_settings_pressed() if scene.title_settings_panel == null or not scene.title_settings_panel.visible: @@ -169,6 +175,8 @@ func _check_title_menu_start_load_and_settings(failures: Array[String]) -> void: persisted_scene.free() if scene.title_audio_reset_button == null or scene.title_audio_reset_button.text != "소리 켜기": failures.append("title settings should expose a one-click audio recovery button") + else: + _check_title_menu_button_art(failures, scene.title_audio_reset_button, "status", "title audio reset") 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") @@ -284,10 +292,14 @@ func _check_title_menu_start_load_and_settings(failures: Array[String]) -> void: failures.append("load-slot selection should enable the automatic campaign save") elif not scene.title_load_auto_button.text.contains("자동 기록"): failures.append("automatic load slot should be labeled in Korean") + else: + _check_title_menu_button_art(failures, scene.title_load_auto_button, "campaign", "title automatic load slot") if scene.title_load_manual_button == null or scene.title_load_manual_button.disabled: failures.append("load-slot selection should enable the manual checkpoint save") elif not scene.title_load_manual_button.text.contains("수동 봉인"): failures.append("manual load slot should be labeled in Korean") + else: + _check_title_menu_button_art(failures, scene.title_load_manual_button, "save", "title manual load slot") scene._show_title_menu() if scene.title_load_panel != null and scene.title_load_panel.visible: failures.append("showing the title menu again should reset the load-slot panel") @@ -342,6 +354,31 @@ func _check_title_menu_start_load_and_settings(failures: Array[String]) -> void: scene.free() +func _check_title_menu_button_art(failures: Array[String], button: Button, expected_icon: String, context: String) -> void: + if button == null: + failures.append("%s button missing" % context) + return + if not bool(button.get_meta("title_menu_button", false)): + failures.append("%s should be configured as a generated title menu button" % context) + if str(button.get_meta("command_icon", "")) != expected_icon: + failures.append("%s should keep command icon metadata `%s`, got `%s`" % [context, expected_icon, str(button.get_meta("command_icon", ""))]) + if button.icon == null: + failures.append("%s should expose generated icon art" % context) + elif button.icon.get_width() < 128 or button.icon.get_height() < 128: + failures.append("%s should use high-resolution generated icon art, got %dx%d" % [context, button.icon.get_width(), button.icon.get_height()]) + var stylebox := button.get_theme_stylebox("normal") + if not (stylebox is StyleBoxTexture): + failures.append("%s should use a generated button texture surface" % context) + return + var style := stylebox as StyleBoxTexture + if style.texture == null: + failures.append("%s generated button surface is missing its texture" % context) + elif style.texture.get_width() < 512 or style.texture.get_height() < 192: + failures.append("%s generated button texture should keep high-resolution source pixels: %dx%d" % [context, style.texture.get_width(), style.texture.get_height()]) + if str(button.tooltip_text).strip_edges().is_empty(): + failures.append("%s should describe its command through a tooltip" % context) + + func _backup_user_file(path: String) -> Dictionary: if not FileAccess.file_exists(path): return {"exists": false, "text": ""}