diff --git a/README.md b/README.md index 18c0c46..65339a9 100644 --- a/README.md +++ b/README.md @@ -132,7 +132,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, Cao Cao portrait art, and a compact left-side command board with `처음부터`, `로드하기`, and `환경설정`; its generated command board, title buttons, and opening-story controls keep safer source-art proportions, linear filtering, and tile-fit 9-slice surfaces for QHD fullscreen scaling. 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 compact chapter-side story-ledger strip instead of exposed numeric cards, `기록 넘기기`/`군막으로 바로` story controls, and a bottom scroll caption panel. Title and in-game settings expose clearer background-music, combat-SFX, edge-scroll, and fullscreen display controls, include one-click audio recovery, and persist preferences 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 `환경설정`; its generated command board and title buttons keep safer source-art proportions, linear filtering, and tile-fit 9-slice surfaces for QHD fullscreen scaling, while story/briefing action buttons use crisp flat readable surfaces. 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 text-only chapter-side story ledger instead of exposed numeric cards or tick marks, `다음 기록`/`군막으로`/`전투 준비` story controls, and a bottom scroll caption panel. Briefing and right-side battle information panels now favor high-contrast readable paper/card surfaces over busy decorative frames. Title and in-game settings expose clearer background-music, combat-SFX, edge-scroll, and fullscreen display 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. diff --git a/scripts/scenes/battle_scene.gd b/scripts/scenes/battle_scene.gd index 05d8b03..70db1b0 100644 --- a/scripts/scenes/battle_scene.gd +++ b/scripts/scenes/battle_scene.gd @@ -231,6 +231,9 @@ const LOCAL_COMMAND_PANEL_BOARD_PADDING := 4.0 const UI_RICE_PAPER := Color(0.48, 0.46, 0.39, 0.98) const UI_RICE_PAPER_LIGHT := Color(0.62, 0.59, 0.50, 0.99) const UI_RICE_PAPER_DARK := Color(0.18, 0.17, 0.14, 0.98) +const UI_READABLE_PAPER := Color(0.66, 0.63, 0.53, 0.998) +const UI_READABLE_CARD := Color(0.58, 0.55, 0.47, 0.998) +const UI_HUD_READABLE := Color(0.060, 0.056, 0.048, 0.982) const UI_AGED_INK := Color(0.055, 0.060, 0.055, 1.0) const UI_DARK_LACQUER := Color(0.045, 0.038, 0.034, 0.988) const UI_LACQUER_EDGE := Color(0.025, 0.023, 0.020, 1.0) @@ -869,6 +872,34 @@ func _apply_panel_style(panel: PanelContainer, variant: String) -> void: var margin := 10 var shadow_size := 6 match variant: + "briefing_sheet": + fill = UI_READABLE_PAPER + border = Color(0.14, 0.11, 0.075, 1.0) + border_width = 5 + radius = 8 + margin = 18 + shadow_size = 18 + "briefing_card": + fill = UI_READABLE_CARD + border = Color(0.16, 0.13, 0.090, 1.0) + border_width = 3 + radius = 8 + margin = 14 + shadow_size = 8 + "briefing_header": + fill = Color(0.080, 0.030, 0.022, 0.995) + border = Color(0.48, 0.39, 0.25, 1.0) + border_width = 3 + radius = 8 + margin = 10 + shadow_size = 8 + "hud_readable": + fill = UI_HUD_READABLE + border = Color(0.42, 0.35, 0.24, 0.98) + border_width = 2 + radius = 8 + margin = 9 + shadow_size = 5 "paper": fill = Color(0.34, 0.32, 0.27, 0.997) border = UI_LACQUER_EDGE @@ -1143,6 +1174,10 @@ func _icon_button_texture_margin_for(button: Button) -> Vector2i: func _apply_generated_button_style(button: Button, important: bool = false) -> bool: + if bool(button.get_meta("flat_readable_button", false)): + button.set_meta("generated_button_texture", false) + button.set_meta("generated_icon_button_texture", false) + return false button.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR var icon_only := bool(button.get_meta("icon_only", false)) var title_button := bool(button.get_meta("title_menu_button", false)) @@ -1270,6 +1305,7 @@ func _apply_strong_text_legibility(control: Control, font_color: Color, font_siz func _apply_button_style(button: Button, important: bool = false) -> void: if button == null: return + button.texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR var title_button := bool(button.get_meta("title_menu_button", false)) var text_color := UI_TITLE_BUTTON_TEXT if title_button else UI_PARCHMENT_TEXT var disabled_text_color := Color(0.80, 0.75, 0.62, 1.0) if title_button else UI_DISABLED_TEXT @@ -2774,10 +2810,11 @@ func _make_opening_story_beat_strip() -> PanelContainer: opening_prologue_story_strip = HBoxContainer.new() opening_prologue_story_strip.name = "OpeningStoryLedgerMarks" - opening_prologue_story_strip.custom_minimum_size = Vector2(82, 24) + opening_prologue_story_strip.custom_minimum_size = Vector2(0, 24) opening_prologue_story_strip.size_flags_vertical = Control.SIZE_SHRINK_CENTER opening_prologue_story_strip.add_theme_constant_override("separation", 4) opening_prologue_story_strip.tooltip_text = panel.tooltip_text + opening_prologue_story_strip.visible = false rail_row.add_child(opening_prologue_story_strip) return panel @@ -2796,25 +2833,7 @@ func _rebuild_opening_story_beat_strip() -> void: if opening_prologue_progress_label != null: opening_prologue_progress_label.text = tooltip opening_prologue_progress_label.tooltip_text = "현재 기록: %s" % tooltip - for index in range(page_count): - var is_current := index == opening_prologue_index - var tick := _make_generated_ornament_panel( - "OpeningStoryLedgerTick%d" % (index + 1), - Vector2(18, 14) if is_current else Vector2(12, 10), - "command_seal" - ) - tick.name = "OpeningStoryLedgerTick%d" % (index + 1) - tick.size_flags_vertical = Control.SIZE_SHRINK_CENTER - tick.modulate = ( - Color(UI_PARCHMENT_TEXT.r, UI_PARCHMENT_TEXT.g, UI_PARCHMENT_TEXT.b, 0.92) - if is_current - else Color(UI_OLD_BRONZE.r, UI_OLD_BRONZE.g, UI_OLD_BRONZE.b, 0.42) - ) - tick.tooltip_text = _opening_story_record_text(index, page_count) - tick.set_meta("story_progress_tick", true) - tick.set_meta("current_story_beat", is_current) - tick.set_meta("quiet_story_ledger_mark", true) - opening_prologue_story_strip.add_child(tick) + opening_prologue_story_strip.visible = false func _opening_story_record_text(index: int, page_count: int) -> String: @@ -3411,7 +3430,7 @@ func _create_hud() -> void: var side_panel := PanelContainer.new() side_panel.position = SIDE_PANEL_POSITION side_panel.size = SIDE_PANEL_SIZE - _apply_panel_style(side_panel, "compact") + _apply_panel_style(side_panel, "hud_readable") root.add_child(side_panel) var side_column := VBoxContainer.new() @@ -3491,9 +3510,9 @@ func _create_hud() -> void: selected_label = Label.new() selected_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART - selected_label.custom_minimum_size = HUD_UNIT_SUMMARY_SIZE + selected_label.custom_minimum_size = Vector2(HUD_UNIT_SUMMARY_SIZE.x, 48) selected_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL - _apply_label_style(selected_label, UI_PARCHMENT_TEXT, 13) + _apply_label_style(selected_label, UI_PARCHMENT_TEXT, 15) hud_unit_info_column.add_child(selected_label) hud_unit_badge_row = HBoxContainer.new() @@ -3538,7 +3557,7 @@ func _create_hud() -> void: mission_detail_panel.visible = false mission_detail_panel.custom_minimum_size = Vector2(420, 134) mission_detail_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL - _apply_panel_style(mission_detail_panel, "bamboo_slips") + _apply_panel_style(mission_detail_panel, "hud_readable") side_column.add_child(mission_detail_panel) var mission_detail_row := HBoxContainer.new() @@ -3559,9 +3578,9 @@ func _create_hud() -> void: cell_info_panel = PanelContainer.new() cell_info_panel.name = "CellInfoChip" - cell_info_panel.custom_minimum_size = Vector2(420, 58) + cell_info_panel.custom_minimum_size = Vector2(420, 70) cell_info_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL - _apply_panel_style(cell_info_panel, "hud_info") + _apply_panel_style(cell_info_panel, "hud_readable") side_column.add_child(cell_info_panel) var cell_info_row := HBoxContainer.new() @@ -3573,16 +3592,16 @@ func _create_hud() -> void: cell_info_label = Label.new() cell_info_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART - cell_info_label.custom_minimum_size = Vector2(354, 38) + cell_info_label.custom_minimum_size = Vector2(354, 48) cell_info_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL - _apply_label_style(cell_info_label, UI_PARCHMENT_TEXT, 12) + _apply_label_style(cell_info_label, UI_PARCHMENT_TEXT, 14) cell_info_row.add_child(cell_info_label) forecast_panel = PanelContainer.new() forecast_panel.name = "ForecastChip" - forecast_panel.custom_minimum_size = Vector2(420, 66) + forecast_panel.custom_minimum_size = Vector2(420, 74) forecast_panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL - _apply_panel_style(forecast_panel, "hud_info") + _apply_panel_style(forecast_panel, "hud_readable") side_column.add_child(forecast_panel) var forecast_row := HBoxContainer.new() @@ -3594,15 +3613,15 @@ func _create_hud() -> void: forecast_label = Label.new() forecast_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART - forecast_label.custom_minimum_size = Vector2(354, 46) + forecast_label.custom_minimum_size = Vector2(354, 50) forecast_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL - _apply_label_style(forecast_label, UI_PARCHMENT_TEXT, 13) + _apply_label_style(forecast_label, UI_PARCHMENT_TEXT, 14) forecast_row.add_child(forecast_label) inventory_label = Label.new() inventory_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART inventory_label.custom_minimum_size = Vector2(420, 34) - _apply_label_style(inventory_label, UI_PARCHMENT_TEXT) + _apply_label_style(inventory_label, UI_PARCHMENT_TEXT, 14) side_column.add_child(inventory_label) var button_row := GridContainer.new() @@ -3996,10 +4015,10 @@ func _create_hud() -> void: opening_caption_row.add_child(opening_button_column) opening_prologue_skip_button = Button.new() - opening_prologue_skip_button.text = "군막으로 바로" + opening_prologue_skip_button.text = "군막으로" opening_prologue_skip_button.icon = _make_toolbar_icon("campaign") opening_prologue_skip_button.custom_minimum_size = Vector2(168, 52) - opening_prologue_skip_button.tooltip_text = "서문을 덮고 군막으로 바로 이동합니다." + opening_prologue_skip_button.tooltip_text = "서문을 덮고 군막으로 이동합니다." opening_prologue_skip_button.expand_icon = false opening_prologue_skip_button.add_theme_constant_override("icon_max_width", 22) opening_prologue_skip_button.add_theme_constant_override("h_separation", 8) @@ -4007,11 +4026,12 @@ func _create_hud() -> void: opening_prologue_skip_button.vertical_icon_alignment = VERTICAL_ALIGNMENT_CENTER opening_prologue_skip_button.add_theme_font_size_override("font_size", 14) opening_prologue_skip_button.set_meta("opening_story_button", true) + opening_prologue_skip_button.set_meta("flat_readable_button", true) _apply_button_style(opening_prologue_skip_button) opening_prologue_skip_button.pressed.connect(_on_opening_prologue_skip_pressed) opening_prologue_next_button = Button.new() - opening_prologue_next_button.text = "기록 넘기기" + opening_prologue_next_button.text = "다음 기록" opening_prologue_next_button.icon = _make_toolbar_icon("move") opening_prologue_next_button.custom_minimum_size = Vector2(168, 52) opening_prologue_next_button.tooltip_text = "다음 기록으로 장면을 넘깁니다." @@ -4022,6 +4042,7 @@ func _create_hud() -> void: opening_prologue_next_button.vertical_icon_alignment = VERTICAL_ALIGNMENT_CENTER opening_prologue_next_button.add_theme_font_size_override("font_size", 16) opening_prologue_next_button.set_meta("opening_story_button", true) + opening_prologue_next_button.set_meta("flat_readable_button", true) _apply_button_style(opening_prologue_next_button, true) opening_prologue_next_button.pressed.connect(_on_opening_prologue_next_pressed) opening_button_column.add_child(opening_prologue_next_button) @@ -4031,26 +4052,21 @@ func _create_hud() -> void: briefing_panel.visible = false briefing_panel.position = Vector2(212, 16) briefing_panel.size = Vector2(856, 688) - _apply_panel_style(briefing_panel, "paper") + _apply_panel_style(briefing_panel, "briefing_sheet") root.add_child(briefing_panel) var briefing_shell := HBoxContainer.new() briefing_shell.custom_minimum_size = Vector2(800, 632) - briefing_shell.add_theme_constant_override("separation", 10) + briefing_shell.add_theme_constant_override("separation", 0) briefing_panel.add_child(briefing_shell) - briefing_shell.add_child(_make_bamboo_gutter(22, 632)) var briefing_column := VBoxContainer.new() - briefing_column.add_theme_constant_override("separation", 5) + briefing_column.add_theme_constant_override("separation", 8) briefing_shell.add_child(briefing_column) - briefing_shell.add_child(_make_bamboo_gutter(22, 632)) - briefing_column.add_child(_make_scroll_rod(680, 6)) - briefing_column.add_child(_make_edict_title_strip(["", "", ""], 680, 24)) - briefing_column.add_child(_make_tablet_binding(680, 16)) var briefing_title_panel := PanelContainer.new() briefing_title_panel.custom_minimum_size = Vector2(680, 46) - _apply_panel_style(briefing_title_panel, "seal") + _apply_panel_style(briefing_title_panel, "briefing_header") briefing_column.add_child(briefing_title_panel) briefing_title_label = Label.new() @@ -4061,23 +4077,26 @@ func _create_hud() -> void: _apply_label_style(briefing_title_label, UI_PARCHMENT_TEXT, 22) briefing_title_panel.add_child(briefing_title_label) - briefing_seal_ribbon = _make_seal_ribbon(680, 18) + briefing_seal_ribbon = _make_seal_ribbon(680, 10) + briefing_seal_ribbon.modulate = Color(1.0, 1.0, 1.0, 0.42) briefing_column.add_child(briefing_seal_ribbon) briefing_location_label = Label.new() briefing_location_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART - briefing_location_label.custom_minimum_size = Vector2(680, 22) + briefing_location_label.custom_minimum_size = Vector2(680, 26) briefing_location_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER - _apply_label_style(briefing_location_label, UI_INK_WASH, 14) + _apply_label_style(briefing_location_label, UI_AGED_INK, 16) briefing_column.add_child(briefing_location_label) briefing_start_button = Button.new() briefing_start_button.text = "전투 시작" briefing_start_button.tooltip_text = "브리핑을 닫고 전투를 시작합니다." + briefing_start_button.custom_minimum_size = Vector2(680, 44) + briefing_start_button.add_theme_font_size_override("font_size", 18) + briefing_start_button.set_meta("flat_readable_button", true) briefing_start_button.pressed.connect(_on_begin_battle_pressed) briefing_column.add_child(briefing_start_button) - briefing_column.add_child(_make_scroll_rod(680, 5)) var briefing_objective_header := HBoxContainer.new() briefing_objective_header.add_theme_constant_override("separation", 8) briefing_column.add_child(briefing_objective_header) @@ -4097,40 +4116,33 @@ func _create_hud() -> void: briefing_objective_panel = PanelContainer.new() briefing_objective_panel.custom_minimum_size = Vector2(680, 124) - _apply_panel_style(briefing_objective_panel, "edict") + _apply_panel_style(briefing_objective_panel, "briefing_card") briefing_column.add_child(briefing_objective_panel) var briefing_objective_row := HBoxContainer.new() briefing_objective_row.add_theme_constant_override("separation", 8) briefing_objective_panel.add_child(briefing_objective_row) - briefing_objective_row.add_child(_make_bamboo_gutter(18, 104)) - briefing_objective_row.add_child(_make_edict_marker_stack(["", "", ""], 104)) - briefing_objective_label = Label.new() briefing_objective_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART - briefing_objective_label.custom_minimum_size = Vector2(500, 104) + briefing_objective_label.custom_minimum_size = Vector2(648, 96) briefing_objective_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL - _apply_label_style(briefing_objective_label, UI_AGED_INK, BRIEFING_OBJECTIVE_FONT_SIZE) + _apply_label_style(briefing_objective_label, UI_AGED_INK, 17) briefing_objective_row.add_child(briefing_objective_label) - briefing_objective_row.add_child(_make_edict_marker_stack(["", "", ""], 104)) - briefing_objective_row.add_child(_make_bamboo_gutter(18, 104)) - briefing_column.add_child(_make_ink_wash_rule(680, 8)) briefing_column.add_child(_make_section_caption("전장도와 군막 보고", 680)) briefing_camp_overview_panel = PanelContainer.new() briefing_camp_overview_panel.custom_minimum_size = Vector2(680, 108) - _apply_panel_style(briefing_camp_overview_panel, "silk_map") + _apply_panel_style(briefing_camp_overview_panel, "briefing_card") briefing_column.add_child(briefing_camp_overview_panel) var briefing_camp_overview_shell := HBoxContainer.new() briefing_camp_overview_shell.add_theme_constant_override("separation", 10) briefing_camp_overview_panel.add_child(briefing_camp_overview_shell) - briefing_camp_overview_shell.add_child(_make_edict_marker_stack(["", ""], 86)) briefing_camp_overview_row = HBoxContainer.new() - briefing_camp_overview_row.custom_minimum_size = Vector2(580, 92) + briefing_camp_overview_row.custom_minimum_size = Vector2(648, 92) briefing_camp_overview_row.add_theme_constant_override("separation", 10) briefing_camp_overview_row.size_flags_horizontal = Control.SIZE_EXPAND_FILL briefing_camp_overview_shell.add_child(briefing_camp_overview_row) @@ -4169,9 +4181,9 @@ func _create_hud() -> void: briefing_camp_overview_label = Label.new() briefing_camp_overview_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART - briefing_camp_overview_label.custom_minimum_size = Vector2(238, 86) + briefing_camp_overview_label.custom_minimum_size = Vector2(286, 86) briefing_camp_overview_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL - _apply_label_style(briefing_camp_overview_label, UI_AGED_INK, 14) + _apply_label_style(briefing_camp_overview_label, UI_AGED_INK, 15) briefing_camp_overview_row.add_child(briefing_camp_overview_label) briefing_tactical_marker_list = VBoxContainer.new() @@ -4180,23 +4192,17 @@ func _create_hud() -> void: briefing_tactical_marker_list.size_flags_vertical = Control.SIZE_SHRINK_CENTER briefing_tactical_marker_list.add_theme_constant_override("separation", 4) briefing_camp_overview_row.add_child(briefing_tactical_marker_list) - briefing_camp_overview_shell.add_child(_make_bamboo_gutter(14, 86)) - - briefing_column.add_child(_make_scroll_rod(680, 4)) var briefing_scroll := ScrollContainer.new() - briefing_scroll.custom_minimum_size = Vector2(680, 66) + briefing_scroll.custom_minimum_size = Vector2(680, 84) briefing_column.add_child(briefing_scroll) briefing_label = Label.new() briefing_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART briefing_label.custom_minimum_size = Vector2(656, 0) - _apply_label_style(briefing_label, UI_AGED_INK, 14) + _apply_label_style(briefing_label, UI_AGED_INK, 16) briefing_scroll.add_child(briefing_label) - briefing_column.add_child(_make_tablet_binding(680, 14)) - briefing_column.add_child(_make_edict_title_strip(["", "", ""], 680, 22)) - var prep_button_row := HBoxContainer.new() prep_button_row.add_theme_constant_override("separation", 8) briefing_column.add_child(prep_button_row) @@ -14692,9 +14698,9 @@ func _update_opening_prologue_page() -> void: _apply_opening_caption_text_style(opening_prologue_step_label, UI_OPENING_CAPTION_MUTED) if opening_prologue_next_button != null: var is_last_page := opening_prologue_index >= page_count - 1 - opening_prologue_next_button.text = "군막으로" if is_last_page else "기록 넘기기" + opening_prologue_next_button.text = "전투 준비" if is_last_page else "다음 기록" opening_prologue_next_button.icon = _make_toolbar_icon("campaign" if is_last_page else "move") - opening_prologue_next_button.tooltip_text = "군막으로 이동합니다." if is_last_page else "다음 기록으로 장면을 넘깁니다." + opening_prologue_next_button.tooltip_text = "군막으로 이동해 전투를 준비합니다." if is_last_page else "다음 기록으로 장면을 넘깁니다." _rebuild_opening_story_beat_strip() diff --git a/tools/smoke_title_menu.gd b/tools/smoke_title_menu.gd index ab98f73..4eccb0a 100644 --- a/tools/smoke_title_menu.gd +++ b/tools/smoke_title_menu.gd @@ -284,19 +284,8 @@ func _check_title_menu_start_load_and_settings(failures: Array[String]) -> void: if not opening_story_cards.is_empty(): failures.append("opening story ledger should not expose old numbered-card style nodes") var opening_story_ticks: Array = opening_story_strip.find_children("OpeningStoryLedgerTick*", "", true, false) - if opening_story_ticks.size() != 4: - failures.append("opening story ledger should expose four quiet progress ticks: %d" % opening_story_ticks.size()) - var current_count := 0 - var current_index := 0 - for opening_story_tick in opening_story_ticks: - var tick_node := opening_story_tick as Node - if not bool(tick_node.get_meta("story_progress_tick", false)): - failures.append("opening story tick should be marked as a quiet progress tick") - if bool(tick_node.get_meta("current_story_beat", false)): - current_count += 1 - current_index = opening_story_ticks.find(opening_story_tick) + 1 - if current_count != 1 or current_index != 1: - failures.append("opening story ledger should highlight the first story tick on entry") + if not opening_story_ticks.is_empty(): + failures.append("opening story ledger should avoid unreadable numeric/tick progress marks: %d" % opening_story_ticks.size()) var numeric_labels := opening_story_strip.find_children("*", "Label", true, false) for numeric_label in numeric_labels: if str((numeric_label as Label).text).strip_edges() in ["1", "2", "3", "4", "1/4", "2/4", "3/4", "4/4"]: @@ -307,11 +296,11 @@ func _check_title_menu_start_load_and_settings(failures: Array[String]) -> void: if opening_story_strip.custom_minimum_size.x > 340.0 or opening_story_strip.custom_minimum_size.y > 54.0: failures.append("opening story ledger should stay compact beside the chapter seal: %s" % str(opening_story_strip.custom_minimum_size)) _check_control_inside_design_view(failures, opening_story_strip as Control, "opening story ledger") - if scene.opening_prologue_next_button == null or scene.opening_prologue_next_button.text != "기록 넘기기": + if scene.opening_prologue_next_button == null or scene.opening_prologue_next_button.text != "다음 기록": failures.append("opening prologue next button should read as a story action") else: _check_opening_story_button_art(failures, scene.opening_prologue_next_button, "opening next") - if scene.opening_prologue_skip_button == null or scene.opening_prologue_skip_button.text != "군막으로 바로": + if scene.opening_prologue_skip_button == null or scene.opening_prologue_skip_button.text != "군막으로": failures.append("opening prologue skip button should read as a contextual camp shortcut") else: _check_opening_story_button_art(failures, scene.opening_prologue_skip_button, "opening skip") @@ -383,8 +372,13 @@ func _check_title_menu_start_load_and_settings(failures: Array[String]) -> void: var second_story_card: Node = null if scene.opening_prologue_root != null: second_story_card = scene.opening_prologue_root.find_child("OpeningStoryLedgerTick2", true, false) - if second_story_card == null or not bool(second_story_card.get_meta("current_story_beat", false)): - failures.append("opening story ledger should highlight the second tick after advancing") + if second_story_card != null: + failures.append("opening story ledger should not show a second tick after advancing") + var second_progress: Label = null + if scene.opening_prologue_root != null: + second_progress = scene.opening_prologue_root.find_child("OpeningStoryProgressScene", true, false) as Label + if second_progress == null or not second_progress.text.contains("둘째 기록") or not second_progress.text.contains("맹덕의 군령"): + failures.append("opening story ledger should update the text record after advancing") if scene.opening_prologue_title_label == null or scene.opening_prologue_title_label.text != "맹덕의 군령": failures.append("opening prologue should move from Cao Cao background to his decision") elif not scene.opening_prologue_body_label.text.contains("황건") or not scene.opening_prologue_body_label.text.contains("작은 군세"): @@ -550,24 +544,23 @@ func _check_opening_story_button_art(failures: Array[String], button: Button, co return if not bool(button.get_meta("opening_story_button", false)): failures.append("%s should use the compact opening-story button profile" % context) + if not bool(button.get_meta("flat_readable_button", false)): + failures.append("%s should use the flat readable story button style" % context) if button.texture_filter != CanvasItem.TEXTURE_FILTER_LINEAR: - failures.append("%s should use linear filtering so generated button art stays smooth at QHD" % context) + failures.append("%s should use linear filtering at QHD" % context) if button.expand_icon: failures.append("%s icon should not stretch in QHD fullscreen" % context) if button.get_theme_constant("icon_max_width") > 22: failures.append("%s icon should stay small beside Korean text" % context) var stylebox := button.get_theme_stylebox("normal") - if not (stylebox is StyleBoxTexture): - failures.append("%s should keep a generated button texture" % context) + if not (stylebox is StyleBoxFlat): + failures.append("%s should use a crisp flat style instead of a smeared generated texture" % context) return - var style := stylebox as StyleBoxTexture - if style.texture_margin_left < 24 or style.texture_margin_top < 10: - failures.append("%s generated button slices should preserve the ornamental frame at QHD: %d/%d" % [context, style.texture_margin_left, style.texture_margin_top]) - if style.axis_stretch_horizontal != StyleBoxTexture.AXIS_STRETCH_MODE_TILE_FIT or style.axis_stretch_vertical != StyleBoxTexture.AXIS_STRETCH_MODE_TILE_FIT: - failures.append("%s generated button surface should tile-fit its 9-slice center at QHD" % context) - _check_button_texture_slice_fit(failures, button, context) - if button.get_theme_font_size("font_size") > 17: - failures.append("%s should stay visually quiet in the story caption" % context) + var style := stylebox as StyleBoxFlat + if style.bg_color.a < 0.92: + failures.append("%s flat button should have a solid readable surface: %s" % [context, str(style.bg_color)]) + if button.get_theme_font_size("font_size") < 14: + failures.append("%s should keep readable Korean text in the story caption" % context) func _check_button_texture_slice_fit(failures: Array[String], button: Button, context: String) -> void: diff --git a/tools/smoke_visual_assets.gd b/tools/smoke_visual_assets.gd index bd21094..9cc508a 100644 --- a/tools/smoke_visual_assets.gd +++ b/tools/smoke_visual_assets.gd @@ -3634,21 +3634,18 @@ func _check_ancient_ui_theme(failures: Array[String]) -> void: for clutter in ["목표|", "주의|", "현황|", "경고|"]: if mission_text.contains(clutter): failures.append("mission panel should avoid old verbose marker `%s`: %s" % [clutter, mission_text]) - _check_panel_style_fill(failures, scene.briefing_panel, "briefing panel", Color(0.34, 0.32, 0.27, 0.997)) - _check_panel_style_frame(failures, scene.briefing_panel, "briefing panel", Color(0.025, 0.023, 0.020, 1.0), 16) - _check_panel_style_fill(failures, scene.briefing_objective_panel, "briefing objective edict panel", Color(0.56, 0.54, 0.47, 0.998)) - _check_panel_style_frame(failures, scene.briefing_objective_panel, "briefing objective edict panel", Color(0.065, 0.060, 0.052, 0.98), 8) - _check_panel_style_fill(failures, scene.briefing_camp_overview_panel, "briefing silk map panel", Color(0.32, 0.31, 0.25, 0.995)) - _check_panel_style_frame(failures, scene.briefing_camp_overview_panel, "briefing silk map panel", Color(0.065, 0.060, 0.052, 0.98), 5) + _check_panel_style_fill(failures, scene.briefing_panel, "briefing panel", Color(0.66, 0.63, 0.53, 0.998)) + _check_panel_style_frame(failures, scene.briefing_panel, "briefing panel", Color(0.14, 0.11, 0.075, 1.0), 5) + _check_panel_style_fill(failures, scene.briefing_objective_panel, "briefing objective readable panel", Color(0.58, 0.55, 0.47, 0.998)) + _check_panel_style_frame(failures, scene.briefing_objective_panel, "briefing objective readable panel", Color(0.16, 0.13, 0.090, 1.0), 3) + _check_panel_style_fill(failures, scene.briefing_camp_overview_panel, "briefing readable map panel", Color(0.58, 0.55, 0.47, 0.998)) + _check_panel_style_frame(failures, scene.briefing_camp_overview_panel, "briefing readable map panel", Color(0.16, 0.13, 0.090, 1.0), 3) _check_panel_style_fill(failures, scene.dialogue_panel, "dialogue panel", Color(0.045, 0.045, 0.040, 0.982)) _check_panel_style_frame(failures, scene.dialogue_panel, "dialogue panel", Color(0.18, 0.17, 0.14, 1.0), 9) _check_panel_style_fill(failures, scene.auto_end_turn_prompt_panel, "auto end turn prompt panel", Color(0.050, 0.045, 0.040, 0.992)) _check_panel_style_fill(failures, scene.dialogue_portrait_panel, "dialogue portrait panel", Color(0.025, 0.023, 0.020, 1.0)) _check_panel_style_fill(failures, scene.dialogue_speaker_panel, "dialogue speaker seal panel", Color(0.26, 0.035, 0.030, 0.995)) _check_panel_style_fill(failures, scene.dialogue_text_panel, "dialogue text scroll panel", Color(0.58, 0.55, 0.48, 0.996)) - _check_panel_uses_generated_texture(failures, scene.briefing_panel, "briefing generated frame") - _check_panel_uses_generated_texture(failures, scene.briefing_objective_panel, "briefing objective generated frame") - _check_panel_uses_generated_texture(failures, scene.briefing_camp_overview_panel, "briefing map generated frame") _check_panel_uses_generated_texture(failures, scene.dialogue_panel, "dialogue generated frame") _check_panel_uses_generated_texture(failures, scene.dialogue_text_panel, "dialogue text generated frame") _check_panel_uses_generated_texture(failures, scene.auto_end_turn_prompt_panel, "auto end turn generated frame") @@ -3672,18 +3669,13 @@ func _check_ancient_ui_theme(failures: Array[String]) -> void: _check_seal_ribbon(failures, scene.briefing_seal_ribbon, "briefing seal ribbon") _check_seal_ribbon(failures, scene.dialogue_seal_ribbon, "dialogue seal ribbon") _check_seal_ribbon(failures, scene.result_seal_ribbon, "result seal ribbon") - _check_bamboo_gutter_row(failures, scene.briefing_objective_panel, "briefing objective bamboo gutters") _check_bamboo_gutter_row(failures, scene.dialogue_text_panel, "dialogue scroll bamboo gutters") _check_bamboo_gutter_row(failures, scene.mission_detail_panel, "mission detail bamboo gutters") - _check_edict_marker_stack(failures, scene.briefing_objective_panel, "briefing objective edict markers") _check_edict_marker_stack(failures, scene.mission_detail_panel, "mission detail edict markers") - _check_panel_style_fill(failures, scene.cell_info_panel, "HUD terrain info panel", Color(0.045, 0.050, 0.048, 0.90)) - _check_panel_style_frame(failures, scene.cell_info_panel, "HUD terrain info panel", Color(0.20, 0.24, 0.21, 0.96), 2) - _check_panel_style_fill(failures, scene.forecast_panel, "HUD forecast info panel", Color(0.045, 0.050, 0.048, 0.90)) - _check_panel_style_frame(failures, scene.forecast_panel, "HUD forecast info panel", Color(0.20, 0.24, 0.21, 0.96), 2) - _check_panel_uses_generated_texture(failures, scene.mission_detail_panel, "mission detail generated frame") - _check_panel_uses_generated_texture(failures, scene.cell_info_panel, "HUD terrain generated frame") - _check_panel_uses_generated_texture(failures, scene.forecast_panel, "HUD forecast generated frame") + _check_panel_style_fill(failures, scene.cell_info_panel, "HUD terrain info panel", Color(0.060, 0.056, 0.048, 0.982)) + _check_panel_style_frame(failures, scene.cell_info_panel, "HUD terrain info panel", Color(0.42, 0.35, 0.24, 0.98), 2) + _check_panel_style_fill(failures, scene.forecast_panel, "HUD forecast info panel", Color(0.060, 0.056, 0.048, 0.982)) + _check_panel_style_frame(failures, scene.forecast_panel, "HUD forecast info panel", Color(0.42, 0.35, 0.24, 0.98), 2) if scene.cell_info_icon == null: failures.append("HUD terrain info panel should expose an icon chip") if scene.forecast_icon == null: @@ -3981,23 +3973,23 @@ func _check_opening_prologue_presentation(failures: Array[String]) -> void: if scene.opening_prologue_next_button == null: failures.append("opening prologue should expose a next button") else: - if scene.opening_prologue_next_button.text != "기록 넘기기": + if scene.opening_prologue_next_button.text != "다음 기록": failures.append("opening next button should use story-fluent Korean text") if scene.opening_prologue_next_button.icon == null: failures.append("opening next button should carry generated icon art") if scene.opening_prologue_next_button.expand_icon or scene.opening_prologue_next_button.get_theme_constant("icon_max_width") > 22: failures.append("opening next icon should stay capped so it cannot cover story text") - _check_button_uses_generated_surface(failures, scene.opening_prologue_next_button, "opening next") + _check_button_uses_flat_readable_surface(failures, scene.opening_prologue_next_button, "opening next") if scene.opening_prologue_skip_button == null: failures.append("opening prologue should expose a skip button") else: - if scene.opening_prologue_skip_button.text != "군막으로 바로": + if scene.opening_prologue_skip_button.text != "군막으로": failures.append("opening skip button should avoid modern skip-button wording") if scene.opening_prologue_skip_button.icon == null: failures.append("opening skip button should carry generated icon art") if scene.opening_prologue_skip_button.expand_icon or scene.opening_prologue_skip_button.get_theme_constant("icon_max_width") > 22: failures.append("opening skip icon should stay capped so it cannot cover story text") - _check_button_uses_generated_surface(failures, scene.opening_prologue_skip_button, "opening skip") + _check_button_uses_flat_readable_surface(failures, scene.opening_prologue_skip_button, "opening skip") var opening_ledger := _find_descendant_by_name(scene.opening_prologue_root, "OpeningStoryLedgerStrip") as PanelContainer if opening_ledger == null: failures.append("opening prologue should use a story ledger instead of numbered progress cards") @@ -4012,6 +4004,9 @@ func _check_opening_prologue_presentation(failures: Array[String]) -> void: var old_cards := opening_ledger.find_children("OpeningStoryBeatCard*", "", true, false) if not old_cards.is_empty(): failures.append("opening story ledger should not expose old numbered-card style nodes") + var tick_cards := opening_ledger.find_children("OpeningStoryLedgerTick*", "", true, false) + if not tick_cards.is_empty(): + failures.append("opening story ledger should not expose unreadable progress tick nodes") for index in range(BattleSceneScript.OPENING_PROLOGUE_PAGES.size()): scene.opening_prologue_index = index scene._update_opening_prologue_page() @@ -4025,7 +4020,7 @@ func _check_opening_prologue_presentation(failures: Array[String]) -> void: if scene.opening_prologue_next_button != null: scene.opening_prologue_index = BattleSceneScript.OPENING_PROLOGUE_PAGES.size() - 1 scene._update_opening_prologue_page() - if scene.opening_prologue_next_button.text != "군막으로" or scene.opening_prologue_next_button.icon == null: + if scene.opening_prologue_next_button.text != "전투 준비" or scene.opening_prologue_next_button.icon == null: failures.append("opening final page should switch the next control into camp-entry mode") scene.free() @@ -4460,6 +4455,25 @@ func _check_button_uses_generated_surface(failures: Array[String], button: Butto failures.append("%s generated button texture should reserve readable content margins" % context) +func _check_button_uses_flat_readable_surface(failures: Array[String], button: Button, context: String) -> void: + if button == null: + failures.append("%s button missing" % context) + return + if not bool(button.get_meta("flat_readable_button", false)): + failures.append("%s should be marked as a flat readable button" % context) + var stylebox := button.get_theme_stylebox("normal") + if not (stylebox is StyleBoxFlat): + failures.append("%s should use a crisp flat button surface" % context) + return + var style := stylebox as StyleBoxFlat + if style.bg_color.a < 0.92: + failures.append("%s flat button should have an opaque readable surface: %s" % [context, str(style.bg_color)]) + if style.border_color.a < 0.80 or style.get_border_width(SIDE_TOP) < 2: + failures.append("%s flat button should keep a clear bronze edge" % context) + if button.get_theme_font_size("font_size") < 14: + failures.append("%s flat button should keep readable Korean text" % context) + + func _check_button_texture_slice_for_control( failures: Array[String], button: Button,