From 8febd9f91788a04f278e188e3d49294a4238fb2c Mon Sep 17 00:00:00 2001 From: Wickedness Date: Sat, 20 Jun 2026 12:45:35 +0900 Subject: [PATCH] Improve opening prologue caption readability --- scripts/scenes/battle_scene.gd | 68 +++++++++++++++++----------------- tools/smoke_title_menu.gd | 18 +++++++-- tools/smoke_visual_assets.gd | 8 +++- 3 files changed, 54 insertions(+), 40 deletions(-) diff --git a/scripts/scenes/battle_scene.gd b/scripts/scenes/battle_scene.gd index 6064f1c..7d40ebe 100644 --- a/scripts/scenes/battle_scene.gd +++ b/scripts/scenes/battle_scene.gd @@ -47,26 +47,26 @@ const SETTINGS_SAVE_VERSION := 1 const OPENING_PROLOGUE_PAGES := [ { "title": "황건의 난", - "teaser": "황건의 난으로 고을이 불타자, 조조는 난세의 첫 군령을 결심한다.", - "body": "황건의 난이 일어나자 누런 머릿수건을 맨 무리가 고을을 불태우고 길을 끊었다. 젊은 조조는 무너지는 질서와 굶주린 백성을 보며 더는 관망할 수 없음을 깨달았다.", + "teaser": "황건의 난이 번지며 백성이 연기 속에서 고향을 떠난다.", + "body": "중평 원년, 황건의 난이 영천 일대까지 번지자 마을은 연기와 피난민으로 뒤덮였다. 조조는 이 소식을 듣고 난세가 이미 문 앞까지 왔음을 깨닫는다.", "image": OPENING_PROLOGUE_REBELLION_PATH }, { - "title": "맹덕의 결심", - "teaser": "분노를 삼킨 조조는 작은 군세라도 직접 세워 길을 열기로 한다.", - "body": "황건의 봉기가 고을을 삼키자 조조는 더는 조정의 명만 기다리지 않았다. 그는 작은 군세라도 직접 세워 백성이 빠져나갈 길을 열기로 했다.", + "title": "맹덕의 군령", + "teaser": "조조가 지도를 펼치고 영천으로 향할 첫 군령을 내린다.", + "body": "조조는 성문 밖 황건의 연기를 바라보며 더 기다리지 않기로 했다. 그는 지도 위에 군령패를 놓고, 흩어진 병사와 작은 군세를 모아 영천으로 향할 준비를 명한다.", "image": OPENING_PROLOGUE_RESOLVE_PATH }, { - "title": "하후돈의 합류", - "teaser": "가문의 친척 하후돈이 창을 들고 찾아와 조조의 첫 군령에 선다.", - "body": "가문의 친척 하후돈은 먼저 창을 들고 조조의 길에 섰다. 두 사람은 큰 명성보다 무너지는 고을을 구하는 첫 군령을 함께 택했다.", + "title": "하후돈의 맹세", + "teaser": "가문의 친척 하후돈이 창을 세우고 조조의 군문에 선다.", + "body": "출정 전, 하후돈이 창을 세우고 조조에게 합류를 청했다. 두 사람은 가문의 이름보다 눈앞의 백성을 구하는 첫 군령을 먼저 하기로 맹세한다.", "image": OPENING_PROLOGUE_XIAHOU_DUN_PATH }, { - "title": "영천으로", - "teaser": "영천 들판에서 조조와 하후돈은 황건적의 깃발을 마주한다.", - "body": "영천 들판에서 조조와 하후돈은 황건적의 깃발을 마주했다. 마을과 성채를 기대어 버티는 적을 꺾고, 백성이 빠져나갈 길을 열어야 한다.", + "title": "영천의 적진", + "teaser": "조조와 하후돈이 영천 들판에서 황건적의 진을 바라본다.", + "body": "영천 들판 너머, 황건적은 마을과 성채를 등지고 진을 펼쳤다. 조조와 하후돈은 병력을 정비하고, 성급히 들이치지 말고 적을 끌어내며 길을 열기로 한다.", "image": OPENING_PROLOGUE_YINGCHUAN_PATH } ] @@ -3728,61 +3728,61 @@ func _create_hud() -> void: var opening_caption_panel := PanelContainer.new() opening_caption_panel.name = "OpeningCaptionScroll" - opening_caption_panel.position = Vector2(64, 548) - opening_caption_panel.size = Vector2(1152, 112) - opening_caption_panel.custom_minimum_size = Vector2(1152, 112) - opening_caption_panel.modulate = Color(0.96, 0.95, 0.91, 0.99) - _apply_panel_style(opening_caption_panel, "story_caption") + 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) + _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(1100, 72) - opening_caption_row.add_theme_constant_override("separation", 16) + opening_caption_row.custom_minimum_size = Vector2(1110, 92) + opening_caption_row.add_theme_constant_override("separation", 18) opening_caption_panel.add_child(opening_caption_row) var opening_title_column := VBoxContainer.new() - opening_title_column.custom_minimum_size = Vector2(214, 72) - opening_title_column.add_theme_constant_override("separation", 4) + opening_title_column.custom_minimum_size = Vector2(226, 92) + 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(214, 10)) + opening_title_column.add_child(_make_seal_ribbon(226, 10)) opening_prologue_title_label = Label.new() - opening_prologue_title_label.custom_minimum_size = Vector2(214, 32) + opening_prologue_title_label.custom_minimum_size = Vector2(226, 34) 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_AGED_INK, 22) + _apply_label_style(opening_prologue_title_label, UI_PARCHMENT_TEXT, 23) opening_title_column.add_child(opening_prologue_title_label) opening_prologue_step_label = Label.new() - opening_prologue_step_label.custom_minimum_size = Vector2(214, 18) + opening_prologue_step_label.custom_minimum_size = Vector2(226, 20) 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_TARNISHED_BRONZE, 14) + _apply_label_style(opening_prologue_step_label, UI_OLD_BRONZE.lightened(0.54), 14) opening_title_column.add_child(opening_prologue_step_label) opening_prologue_body_label = Label.new() - opening_prologue_body_label.custom_minimum_size = Vector2(700, 72) + opening_prologue_body_label.custom_minimum_size = Vector2(700, 92) 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_AGED_INK, 17) + _apply_label_style(opening_prologue_body_label, UI_PARCHMENT_TEXT, 18) opening_caption_row.add_child(opening_prologue_body_label) var opening_button_column := VBoxContainer.new() - opening_button_column.custom_minimum_size = Vector2(150, 72) - opening_button_column.add_theme_constant_override("separation", 4) + opening_button_column.custom_minimum_size = Vector2(154, 92) + 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(150, 30) + opening_prologue_skip_button.custom_minimum_size = Vector2(154, 34) opening_prologue_skip_button.tooltip_text = "오프닝을 닫고 군막으로 이동합니다." 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(150, 38) + opening_prologue_next_button.custom_minimum_size = Vector2(154, 42) opening_prologue_next_button.tooltip_text = "다음 이야기로 넘깁니다." opening_prologue_next_button.pressed.connect(_on_opening_prologue_next_pressed) opening_button_column.add_child(opening_prologue_next_button) @@ -13602,7 +13602,7 @@ func _update_opening_prologue_page() -> void: _set_fitted_label_text( opening_prologue_title_label, str(page.get("title", "")), - 22, + 23, 18, opening_prologue_title_label.custom_minimum_size ) @@ -13610,8 +13610,8 @@ func _update_opening_prologue_page() -> void: _set_fitted_label_text( opening_prologue_body_label, str(page.get("body", "")), - 17, - 14, + 18, + 15, opening_prologue_body_label.custom_minimum_size ) if opening_prologue_step_label != null: diff --git a/tools/smoke_title_menu.gd b/tools/smoke_title_menu.gd index f61ef2d..c17b4cd 100644 --- a/tools/smoke_title_menu.gd +++ b/tools/smoke_title_menu.gd @@ -265,13 +265,23 @@ func _check_title_menu_start_load_and_settings(failures: Array[String]) -> void: 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: 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: + failures.append("opening prologue title should use bright caption text 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: + failures.append("opening prologue body should use bright readable text over the artwork") 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 > 124.0 or opening_caption.position.y < 540.0: + 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 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") var first_story_texture: Texture2D = null if scene.opening_prologue_scene_texture_rect != null: first_story_texture = scene.opening_prologue_scene_texture_rect.texture @@ -283,17 +293,17 @@ func _check_title_menu_start_load_and_settings(failures: Array[String]) -> void: second_story_card = scene.opening_prologue_root.find_child("OpeningStoryBeatCard2", true, false) if second_story_card == null or not bool(second_story_card.get_meta("current_story_beat", false)): failures.append("opening story strip should highlight the second beat after advancing") - if scene.opening_prologue_title_label == null or scene.opening_prologue_title_label.text != "맹덕의 결심": + 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("작은 군세"): failures.append("opening prologue decision page should explain why Cao Cao raises troops") scene._on_opening_prologue_next_pressed() - if scene.opening_prologue_title_label == null or scene.opening_prologue_title_label.text != "하후돈의 합류": + if scene.opening_prologue_title_label == null or scene.opening_prologue_title_label.text != "하후돈의 맹세": failures.append("opening prologue should then show Xiahou Dun joining Cao Cao") elif not scene.opening_prologue_body_label.text.contains("하후돈") or not scene.opening_prologue_body_label.text.contains("첫 군령"): failures.append("opening prologue Xiahou Dun page should link the relationship to the first command") scene._on_opening_prologue_next_pressed() - if scene.opening_prologue_title_label == null or scene.opening_prologue_title_label.text != "영천으로": + if scene.opening_prologue_title_label == null or scene.opening_prologue_title_label.text != "영천의 적진": failures.append("opening prologue should end by pointing toward Yingchuan") var prologue_guard := 0 while scene._is_opening_prologue_visible() and prologue_guard < 8: diff --git a/tools/smoke_visual_assets.gd b/tools/smoke_visual_assets.gd index 895c001..c7c7a1f 100644 --- a/tools/smoke_visual_assets.gd +++ b/tools/smoke_visual_assets.gd @@ -3876,8 +3876,12 @@ func _check_opening_prologue_presentation(failures: Array[String]) -> void: 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_texture_key", "")) != "story_scroll": - failures.append("opening caption should use the generated story scroll surface") + 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: + 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_next_button == null: failures.append("opening prologue should expose a next button") else: