From cf3f7de3af1658879f314f82930f57acca14fb5f Mon Sep 17 00:00:00 2001 From: Wickedness Date: Sat, 20 Jun 2026 13:26:58 +0900 Subject: [PATCH] Fix opening prologue text visibility --- scripts/scenes/battle_scene.gd | 91 +++++++++++++++++++++++++--------- tools/smoke_title_menu.gd | 18 +++++-- tools/smoke_visual_assets.gd | 32 ++++++++---- 3 files changed, 101 insertions(+), 40 deletions(-) diff --git a/scripts/scenes/battle_scene.gd b/scripts/scenes/battle_scene.gd index 150216c..cea91fb 100644 --- a/scripts/scenes/battle_scene.gd +++ b/scripts/scenes/battle_scene.gd @@ -261,6 +261,9 @@ const UI_BRUSH_RULE := Color(0.065, 0.060, 0.052, 0.98) const UI_TEXT_DARK_OUTLINE := Color(0.018, 0.012, 0.007, 0.88) const UI_TEXT_LIGHT_OUTLINE := Color(0.96, 0.88, 0.66, 0.56) const UI_TEXT_SHADOW := Color(0.010, 0.007, 0.004, 0.66) +const UI_OPENING_CAPTION_TITLE := Color(0.98, 0.93, 0.78, 1.0) +const UI_OPENING_CAPTION_BODY := Color(0.97, 0.95, 0.88, 1.0) +const UI_OPENING_CAPTION_MUTED := Color(0.78, 0.70, 0.52, 1.0) const MAP_SCROLL_FRAME_PADDING := 12.0 const SPEAKER_DISPLAY_NAMES := { "Cao Cao": "조조", @@ -800,7 +803,7 @@ func _panel_texture_spec(variant: String) -> Dictionary: return {"texture": "paper_scroll", "slice_margin": 108, "content_margin": 22} "silk_map", "bamboo_slips", "hud_info", "target_hint", "caption", "edict_compact": return {"texture": "hud_jade", "slice_margin": 46, "content_margin": 9} - "dialogue", "compact", "result_tablet", "cinematic_caption": + "dialogue", "compact", "result_tablet": return {"texture": "lacquer", "slice_margin": 96, "content_margin": 14} "story_caption": return {"texture": "story_scroll", "slice_margin": 126, "content_margin": 22} @@ -2161,6 +2164,25 @@ func _apply_label_style(label: Label, font_color: Color, font_size: int = 0) -> _apply_text_legibility(label, font_color, font_size) +func _apply_opening_caption_text_style(label: Label, font_color: Color, font_size: int = 0) -> void: + if label == null: + return + label.add_theme_color_override("font_color", font_color) + if font_size > 0: + label.add_theme_font_size_override("font_size", font_size) + _apply_control_font(label, true) + var resolved_size := font_size + if resolved_size <= 0: + resolved_size = label.get_theme_font_size("font_size") + var outline_size := 4 if resolved_size >= 18 else 3 + label.add_theme_color_override("font_outline_color", Color(0.0, 0.0, 0.0, 0.96)) + label.add_theme_constant_override("outline_size", outline_size) + label.add_theme_color_override("font_shadow_color", Color(0.0, 0.0, 0.0, 0.82)) + label.add_theme_constant_override("shadow_offset_x", 2) + label.add_theme_constant_override("shadow_offset_y", 2) + label.add_theme_constant_override("line_spacing", 4) + + func _fit_label_font_size_to_text(label: Label, text: String, base_size: int, min_size: int, available_size: Vector2 = Vector2.ZERO) -> int: if label == null: return base_size @@ -3706,10 +3728,10 @@ func _create_hud() -> void: var opening_bottom_dim := ColorRect.new() opening_bottom_dim.name = "OpeningBottomInkShade" - opening_bottom_dim.color = Color(0.020, 0.018, 0.015, 0.40) + opening_bottom_dim.color = Color(0.020, 0.018, 0.015, 0.34) opening_bottom_dim.mouse_filter = Control.MOUSE_FILTER_IGNORE - opening_bottom_dim.position = Vector2(0, 532) - opening_bottom_dim.size = Vector2(1280, 188) + opening_bottom_dim.position = Vector2(0, 486) + opening_bottom_dim.size = Vector2(1280, 234) opening_prologue_root.add_child(opening_bottom_dim) var opening_chapter_panel := PanelContainer.new() @@ -3731,62 +3753,80 @@ func _create_hud() -> void: var opening_caption_panel := PanelContainer.new() opening_caption_panel.name = "OpeningCaptionScroll" - opening_caption_panel.position = Vector2(58, 522) - opening_caption_panel.size = Vector2(1164, 144) - opening_caption_panel.custom_minimum_size = Vector2(1164, 144) - opening_caption_panel.modulate = Color(1.0, 1.0, 1.0, 0.995) + opening_caption_panel.position = Vector2(50, 500) + opening_caption_panel.size = Vector2(1180, 174) + opening_caption_panel.custom_minimum_size = Vector2(1180, 174) + opening_caption_panel.modulate = Color(1.0, 1.0, 1.0, 1.0) + opening_caption_panel.z_index = 30 _apply_panel_style(opening_caption_panel, "cinematic_caption") opening_prologue_root.add_child(opening_caption_panel) var opening_caption_row := HBoxContainer.new() - opening_caption_row.custom_minimum_size = Vector2(1110, 92) - opening_caption_row.add_theme_constant_override("separation", 18) + opening_caption_row.custom_minimum_size = Vector2(1136, 126) + opening_caption_row.size_flags_horizontal = Control.SIZE_EXPAND_FILL + opening_caption_row.size_flags_vertical = Control.SIZE_EXPAND_FILL + opening_caption_row.add_theme_constant_override("separation", 14) opening_caption_panel.add_child(opening_caption_row) var opening_title_column := VBoxContainer.new() - opening_title_column.custom_minimum_size = Vector2(226, 92) + opening_title_column.custom_minimum_size = Vector2(220, 126) opening_title_column.add_theme_constant_override("separation", 5) opening_caption_row.add_child(opening_title_column) - opening_title_column.add_child(_make_seal_ribbon(226, 10)) + opening_title_column.add_child(_make_seal_ribbon(220, 10)) opening_prologue_title_label = Label.new() - opening_prologue_title_label.custom_minimum_size = Vector2(226, 34) + opening_prologue_title_label.custom_minimum_size = Vector2(220, 42) opening_prologue_title_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER opening_prologue_title_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER - _apply_label_style(opening_prologue_title_label, UI_PARCHMENT_TEXT, 23) + _apply_opening_caption_text_style(opening_prologue_title_label, UI_OPENING_CAPTION_TITLE, 24) opening_title_column.add_child(opening_prologue_title_label) opening_prologue_step_label = Label.new() - opening_prologue_step_label.custom_minimum_size = Vector2(226, 20) + opening_prologue_step_label.custom_minimum_size = Vector2(220, 24) opening_prologue_step_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER opening_prologue_step_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER - _apply_label_style(opening_prologue_step_label, UI_OLD_BRONZE.lightened(0.54), 14) + _apply_opening_caption_text_style(opening_prologue_step_label, UI_OPENING_CAPTION_MUTED, 15) opening_title_column.add_child(opening_prologue_step_label) opening_prologue_body_label = Label.new() - opening_prologue_body_label.custom_minimum_size = Vector2(700, 92) + opening_prologue_body_label.custom_minimum_size = Vector2(740, 126) + opening_prologue_body_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL + opening_prologue_body_label.size_flags_vertical = Control.SIZE_EXPAND_FILL opening_prologue_body_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART opening_prologue_body_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER - _apply_label_style(opening_prologue_body_label, UI_PARCHMENT_TEXT, 18) + opening_prologue_body_label.clip_text = false + _apply_opening_caption_text_style(opening_prologue_body_label, UI_OPENING_CAPTION_BODY, 20) opening_caption_row.add_child(opening_prologue_body_label) var opening_button_column := VBoxContainer.new() - opening_button_column.custom_minimum_size = Vector2(154, 92) + opening_button_column.custom_minimum_size = Vector2(148, 126) opening_button_column.add_theme_constant_override("separation", 7) opening_caption_row.add_child(opening_button_column) opening_prologue_skip_button = Button.new() opening_prologue_skip_button.text = "건너뛰기" opening_prologue_skip_button.icon = _make_toolbar_icon("cancel") - opening_prologue_skip_button.custom_minimum_size = Vector2(154, 34) + opening_prologue_skip_button.custom_minimum_size = Vector2(148, 40) opening_prologue_skip_button.tooltip_text = "오프닝을 닫고 군막으로 이동합니다." + opening_prologue_skip_button.expand_icon = false + opening_prologue_skip_button.add_theme_constant_override("icon_max_width", 24) + opening_prologue_skip_button.add_theme_constant_override("h_separation", 8) + opening_prologue_skip_button.icon_alignment = HORIZONTAL_ALIGNMENT_LEFT + opening_prologue_skip_button.vertical_icon_alignment = VERTICAL_ALIGNMENT_CENTER + _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.icon = _make_toolbar_icon("move") - opening_prologue_next_button.custom_minimum_size = Vector2(154, 42) + opening_prologue_next_button.custom_minimum_size = Vector2(148, 48) opening_prologue_next_button.tooltip_text = "다음 이야기로 넘깁니다." + opening_prologue_next_button.expand_icon = false + opening_prologue_next_button.add_theme_constant_override("icon_max_width", 24) + opening_prologue_next_button.add_theme_constant_override("h_separation", 8) + opening_prologue_next_button.icon_alignment = HORIZONTAL_ALIGNMENT_LEFT + opening_prologue_next_button.vertical_icon_alignment = VERTICAL_ALIGNMENT_CENTER + _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) opening_button_column.add_child(opening_prologue_skip_button) @@ -13819,20 +13859,23 @@ func _update_opening_prologue_page() -> void: _set_fitted_label_text( opening_prologue_title_label, str(page.get("title", "")), - 23, + 24, 18, opening_prologue_title_label.custom_minimum_size ) + _apply_opening_caption_text_style(opening_prologue_title_label, UI_OPENING_CAPTION_TITLE) if opening_prologue_body_label != null: _set_fitted_label_text( opening_prologue_body_label, str(page.get("body", "")), - 18, - 15, + 20, + 17, opening_prologue_body_label.custom_minimum_size ) + _apply_opening_caption_text_style(opening_prologue_body_label, UI_OPENING_CAPTION_BODY) if opening_prologue_step_label != null: opening_prologue_step_label.text = "장면 %d/%d" % [opening_prologue_index + 1, page_count] + _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 "다음" diff --git a/tools/smoke_title_menu.gd b/tools/smoke_title_menu.gd index c17b4cd..49f39a6 100644 --- a/tools/smoke_title_menu.gd +++ b/tools/smoke_title_menu.gd @@ -263,25 +263,33 @@ func _check_title_menu_start_load_and_settings(failures: Array[String]) -> void: failures.append("opening prologue story artwork should use a wide cinematic frame") elif scene.opening_prologue_scene_texture_rect.custom_minimum_size.x < 1200 or scene.opening_prologue_scene_texture_rect.custom_minimum_size.y < 700: failures.append("opening prologue story artwork should fill the first screen instead of sitting in a small frame") - if scene.opening_prologue_body_label != null and scene.opening_prologue_body_label.custom_minimum_size.x < 680: + if scene.opening_prologue_body_label != null and scene.opening_prologue_body_label.custom_minimum_size.x < 730: failures.append("opening prologue body text should have enough caption width for Korean copy") if scene.opening_prologue_title_label != null: var opening_title_color: Color = scene.opening_prologue_title_label.get_theme_color("font_color") - if opening_title_color.get_luminance() < 0.55: + if opening_title_color.get_luminance() < 0.78: failures.append("opening prologue title should use bright caption text over the artwork") + if scene.opening_prologue_title_label.get_theme_constant("outline_size") < 3: + failures.append("opening prologue title should have a strong outline over the artwork") if scene.opening_prologue_body_label != null: var opening_body_color: Color = scene.opening_prologue_body_label.get_theme_color("font_color") - if opening_body_color.get_luminance() < 0.55: + if opening_body_color.get_luminance() < 0.86: failures.append("opening prologue body should use bright readable text over the artwork") + if scene.opening_prologue_body_label.get_theme_constant("outline_size") < 3: + failures.append("opening prologue body should keep a strong outline over generated art") var opening_caption: PanelContainer = null if scene.opening_prologue_root != null: opening_caption = scene.opening_prologue_root.find_child("OpeningCaptionScroll", true, false) as PanelContainer if opening_caption == null: failures.append("opening prologue should keep a bottom scroll caption over the artwork") - elif opening_caption.custom_minimum_size.y > 148.0 or opening_caption.position.y < 510.0: - failures.append("opening prologue caption should stay low and compact so the image leads: %s at %s" % [str(opening_caption.custom_minimum_size), str(opening_caption.position)]) + elif opening_caption.custom_minimum_size.y < 160.0 or opening_caption.custom_minimum_size.y > 188.0 or opening_caption.position.y < 492.0: + failures.append("opening prologue caption should be large enough for readable Korean while leaving the image dominant: %s at %s" % [str(opening_caption.custom_minimum_size), str(opening_caption.position)]) elif str(opening_caption.get_meta("panel_style_variant", "")) != "cinematic_caption": failures.append("opening prologue caption should use a dark cinematic panel so text remains visible") + elif bool(opening_caption.get_meta("generated_panel_texture", true)): + failures.append("opening prologue caption should use a flat high-contrast panel instead of a busy texture") + elif opening_caption.z_index < 20: + failures.append("opening prologue caption should render above the generated story image") var first_story_texture: Texture2D = null if scene.opening_prologue_scene_texture_rect != null: first_story_texture = scene.opening_prologue_scene_texture_rect.texture diff --git a/tools/smoke_visual_assets.gd b/tools/smoke_visual_assets.gd index 7ec8a72..25fa66a 100644 --- a/tools/smoke_visual_assets.gd +++ b/tools/smoke_visual_assets.gd @@ -3871,28 +3871,38 @@ func _check_opening_prologue_presentation(failures: Array[String]) -> void: if caption_panel == null: failures.append("opening prologue should build a cinematic caption panel") else: - if caption_panel.position.y < 500.0: + if caption_panel.position.y < 492.0: failures.append("opening caption should sit low enough to let generated story art dominate: %s" % str(caption_panel.position)) - if caption_panel.custom_minimum_size.y > 150.0: - failures.append("opening caption should stay compact instead of covering the story art: %s" % str(caption_panel.custom_minimum_size)) - _check_panel_uses_generated_texture(failures, caption_panel, "opening cinematic caption generated frame") - if str(caption_panel.get_meta("panel_style_variant", "")) != "cinematic_caption" or str(caption_panel.get_meta("panel_texture_key", "")) != "lacquer": - failures.append("opening caption should use a dark generated lacquer surface so text stays visible") - if scene.opening_prologue_title_label != null and scene.opening_prologue_title_label.get_theme_color("font_color").get_luminance() < 0.55: + if caption_panel.custom_minimum_size.y < 160.0 or caption_panel.custom_minimum_size.y > 188.0: + failures.append("opening caption should be large enough for readable Korean without covering the story art: %s" % str(caption_panel.custom_minimum_size)) + if str(caption_panel.get_meta("panel_style_variant", "")) != "cinematic_caption" or bool(caption_panel.get_meta("generated_panel_texture", true)): + failures.append("opening caption should use a flat high-contrast cinematic surface so text stays visible") + if caption_panel.z_index < 20: + failures.append("opening caption should render above the generated story image") + if scene.opening_prologue_title_label != null and scene.opening_prologue_title_label.get_theme_color("font_color").get_luminance() < 0.78: failures.append("opening title should use bright text over the story image") - if scene.opening_prologue_body_label != null and scene.opening_prologue_body_label.get_theme_color("font_color").get_luminance() < 0.55: - failures.append("opening body should use bright text over the story image") + if scene.opening_prologue_body_label != null: + if scene.opening_prologue_body_label.get_theme_color("font_color").get_luminance() < 0.86: + failures.append("opening body should use bright text over the story image") + if scene.opening_prologue_body_label.custom_minimum_size.x < 730.0: + failures.append("opening body should be wide enough for Korean opening text: %s" % str(scene.opening_prologue_body_label.custom_minimum_size)) + if scene.opening_prologue_body_label.get_theme_constant("outline_size") < 3: + failures.append("opening body should have a strong outline over generated art") if scene.opening_prologue_next_button == null: failures.append("opening prologue should expose a next button") else: 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") > 28: + 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") if scene.opening_prologue_skip_button == null: failures.append("opening prologue should expose a skip button") else: 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") > 28: + 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") for index in range(BattleSceneScript.OPENING_PROLOGUE_PAGES.size()): scene.opening_prologue_index = index @@ -3900,8 +3910,8 @@ func _check_opening_prologue_presentation(failures: Array[String]) -> void: var body_text: String = scene.opening_prologue_body_label.text if scene.opening_prologue_body_label != null else "" var body_area: Vector2 = scene.opening_prologue_body_label.custom_minimum_size if scene.opening_prologue_body_label != null else Vector2.ZERO var body_size: int = scene.opening_prologue_body_label.get_theme_font_size("font_size") if scene.opening_prologue_body_label != null else 0 - if body_size < 14 or body_size > 18: - failures.append("opening story body font should fit inside its compact caption range, got %d" % body_size) + if body_size < 17 or body_size > 20: + failures.append("opening story body font should stay readable inside the caption range, got %d" % body_size) elif scene._estimated_wrapped_text_height(body_text, body_area.x, body_size) > body_area.y: failures.append("opening story body should fit without clipping on page %d: size=%d area=%s" % [index + 1, body_size, str(body_area)]) if scene.opening_prologue_next_button != null: