diff --git a/README.md b/README.md index 719eea8..b5caf2b 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, 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, 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 `환경설정`; 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 Korean story-ledger strip instead of exposed numeric cards, 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`. - 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 9268d8d..4b7567e 100644 --- a/scripts/scenes/battle_scene.gd +++ b/scripts/scenes/battle_scene.gd @@ -166,8 +166,8 @@ const TITLE_STORY_PREVIEW_IMAGE_FRAME_SIZE := Vector2(140, 74) const TITLE_STORY_PREVIEW_IMAGE_SIZE := Vector2(132, 66) const TITLE_STORY_PREVIEW_LABEL_SIZE := Vector2(140, 16) const TITLE_MENU_ICON_MAX_WIDTH := 42 -const OPENING_STORY_PROGRESS_CARD_SIZE := Vector2(50, 24) -const OPENING_STORY_PROGRESS_KNOT_SIZE := Vector2(18, 18) +const OPENING_STORY_PROGRESS_LEDGER_SIZE := Vector2(356, 56) +const OPENING_STORY_PROGRESS_SEAL_SIZE := Vector2(38, 38) const COMMAND_NOTICE_ICON_SIZE := Vector2(32, 32) const COMMAND_NOTICE_MAX_ICONS := 5 const CHAPTER_SEAL_BADGE_SIZE := Vector2(54, 54) @@ -601,6 +601,7 @@ var opening_prologue_scene_texture_rect: TextureRect var opening_prologue_title_label: Label var opening_prologue_body_label: Label var opening_prologue_step_label: Label +var opening_prologue_progress_label: Label var opening_prologue_story_strip: HBoxContainer var opening_prologue_next_button: Button var opening_prologue_skip_button: Button @@ -1110,11 +1111,13 @@ func _text_button_texture_margin_for(button: Button, title_button: bool, opening var target := _control_texture_target_size(button, Vector2(148.0, 44.0)) var max_x := maxi(8, int(floor(target.x * 0.5)) - 3) var max_y := maxi(6, int(floor(target.y * 0.5)) - 3) - var desired_x := int(floor(target.x * (0.15 if not title_button else 0.13))) - var desired_y := int(floor(target.y * (0.30 if not opening_story_button else 0.24))) + var desired_x := int(floor(target.x * (0.14 if not title_button else 0.12))) + var desired_y := int(floor(target.y * (0.22 if not opening_story_button else 0.18))) + var cap_x := 44 if opening_story_button else (60 if title_button else 72) + var cap_y := 12 if opening_story_button else (18 if title_button else 24) return Vector2i( - mini(max_x, maxi(16, mini(92, desired_x))), - mini(max_y, maxi(8, mini(42, desired_y))) + mini(max_x, maxi(16, mini(cap_x, desired_x))), + mini(max_y, maxi(8, mini(cap_y, desired_y))) ) @@ -2696,38 +2699,72 @@ func _make_title_story_preview_card(title: String, image_path: String, index: in func _make_opening_story_beat_strip() -> PanelContainer: var panel := PanelContainer.new() - panel.name = "OpeningStoryBeatStrip" - panel.position = Vector2(900, 28) - panel.size = Vector2(318, 42) - panel.custom_minimum_size = Vector2(318, 42) - panel.modulate = Color(1.0, 1.0, 1.0, 0.88) + panel.name = "OpeningStoryLedgerStrip" + panel.position = Vector2(876, 22) + panel.size = OPENING_STORY_PROGRESS_LEDGER_SIZE + panel.custom_minimum_size = OPENING_STORY_PROGRESS_LEDGER_SIZE + panel.modulate = Color(1.0, 1.0, 1.0, 0.92) + panel.tooltip_text = "지금 펼쳐진 서문 기록입니다." _apply_panel_style(panel, "caption") var panel_style := panel.get_theme_stylebox("panel") if panel_style != null: - panel_style.content_margin_left = 8 - panel_style.content_margin_right = 8 - panel_style.content_margin_top = 7 - panel_style.content_margin_bottom = 7 + panel_style.content_margin_left = 9 + panel_style.content_margin_right = 9 + panel_style.content_margin_top = 8 + panel_style.content_margin_bottom = 8 var rail_row := HBoxContainer.new() rail_row.name = "OpeningStoryProgressRail" - rail_row.custom_minimum_size = Vector2(298, 26) - rail_row.add_theme_constant_override("separation", 8) + rail_row.custom_minimum_size = Vector2(334, 40) + rail_row.add_theme_constant_override("separation", 9) + rail_row.tooltip_text = panel.tooltip_text panel.add_child(rail_row) + var seal := _make_generated_ornament_panel("OpeningStoryProgressSeal", OPENING_STORY_PROGRESS_SEAL_SIZE, "command_seal") + seal.tooltip_text = panel.tooltip_text + var seal_icon := TextureRect.new() + seal_icon.name = "OpeningStoryProgressIcon" + seal_icon.custom_minimum_size = Vector2(24, 24) + seal_icon.size_flags_vertical = Control.SIZE_SHRINK_CENTER + seal_icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE + seal_icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED + seal_icon.texture = _make_toolbar_icon("campaign") + seal_icon.tooltip_text = panel.tooltip_text + seal.add_child(seal_icon) + rail_row.add_child(seal) + + var text_column := VBoxContainer.new() + text_column.name = "OpeningStoryProgressText" + text_column.custom_minimum_size = Vector2(198, 40) + text_column.add_theme_constant_override("separation", 2) + text_column.tooltip_text = panel.tooltip_text + rail_row.add_child(text_column) + var rail_label := Label.new() rail_label.name = "OpeningStoryProgressCaption" - rail_label.text = "전개" - rail_label.custom_minimum_size = Vector2(42, 24) - rail_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + rail_label.text = "영천으로" + rail_label.custom_minimum_size = Vector2(196, 18) + rail_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT rail_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + rail_label.tooltip_text = panel.tooltip_text _apply_label_style(rail_label, UI_TITLE_MUTED_TEXT, 12) - rail_row.add_child(rail_label) + text_column.add_child(rail_label) + + opening_prologue_progress_label = Label.new() + opening_prologue_progress_label.name = "OpeningStoryProgressScene" + opening_prologue_progress_label.custom_minimum_size = Vector2(196, 20) + opening_prologue_progress_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT + opening_prologue_progress_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + opening_prologue_progress_label.tooltip_text = panel.tooltip_text + _apply_label_style(opening_prologue_progress_label, UI_PARCHMENT_TEXT, 14) + text_column.add_child(opening_prologue_progress_label) opening_prologue_story_strip = HBoxContainer.new() - opening_prologue_story_strip.name = "OpeningStoryBeatRow" - opening_prologue_story_strip.custom_minimum_size = Vector2(238, 24) - opening_prologue_story_strip.add_theme_constant_override("separation", 0) + opening_prologue_story_strip.name = "OpeningStoryLedgerMarks" + opening_prologue_story_strip.custom_minimum_size = Vector2(78, 18) + opening_prologue_story_strip.size_flags_vertical = Control.SIZE_SHRINK_CENTER + opening_prologue_story_strip.add_theme_constant_override("separation", 5) + opening_prologue_story_strip.tooltip_text = panel.tooltip_text rail_row.add_child(opening_prologue_story_strip) return panel @@ -2738,58 +2775,39 @@ func _rebuild_opening_story_beat_strip() -> void: for child in opening_prologue_story_strip.get_children(): opening_prologue_story_strip.remove_child(child) child.queue_free() - for index in range(OPENING_PROLOGUE_PAGES.size()): - var page: Dictionary = OPENING_PROLOGUE_PAGES[index] - opening_prologue_story_strip.add_child(_make_opening_story_beat_card(page, index, index == opening_prologue_index)) + var page_count := OPENING_PROLOGUE_PAGES.size() + var page: Dictionary = OPENING_PROLOGUE_PAGES[opening_prologue_index] if opening_prologue_index >= 0 and opening_prologue_index < page_count else {} + var record_text := _opening_story_record_text(opening_prologue_index, page_count) + var page_title := str(page.get("title", "서문")) + var tooltip := "%s · %s" % [record_text, page_title] + 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 tick := ColorRect.new() + tick.name = "OpeningStoryLedgerTick%d" % (index + 1) + tick.custom_minimum_size = Vector2(22, 5) if index == opening_prologue_index else Vector2(12, 4) + tick.size_flags_vertical = Control.SIZE_SHRINK_CENTER + tick.color = ( + Color(UI_PARCHMENT_TEXT.r, UI_PARCHMENT_TEXT.g, UI_PARCHMENT_TEXT.b, 0.92) + if index == opening_prologue_index + 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", index == opening_prologue_index) + opening_prologue_story_strip.add_child(tick) -func _make_opening_story_beat_card(page: Dictionary, index: int, is_current: bool) -> PanelContainer: - var title := str(page.get("title", "장면")) - var tooltip := "장면 %d. %s" % [index + 1, title] - var card := PanelContainer.new() - card.name = "OpeningStoryBeatCard%d" % (index + 1) - card.custom_minimum_size = OPENING_STORY_PROGRESS_CARD_SIZE - card.tooltip_text = tooltip - card.set_meta("current_story_beat", is_current) - card.set_meta("story_progress_knot", true) - _apply_panel_style(card, "transparent_overlay") - var card_style := card.get_theme_stylebox("panel") - if card_style != null: - card_style.content_margin_left = 0 - card_style.content_margin_right = 0 - card_style.content_margin_top = 0 - card_style.content_margin_bottom = 0 - card.modulate = Color(1.0, 1.0, 1.0, 1.0) if is_current else Color(0.70, 0.68, 0.60, 0.78) - - var row := HBoxContainer.new() - row.custom_minimum_size = OPENING_STORY_PROGRESS_CARD_SIZE - row.add_theme_constant_override("separation", 3) - row.tooltip_text = tooltip - card.add_child(row) - - var thread := ColorRect.new() - thread.name = "OpeningStoryBeatThread" - thread.custom_minimum_size = Vector2(22, 3) - thread.size_flags_vertical = Control.SIZE_SHRINK_CENTER - thread.color = Color(UI_OLD_BRONZE.r, UI_OLD_BRONZE.g, UI_OLD_BRONZE.b, 0.0 if index == 0 else (0.82 if is_current else 0.42)) - thread.tooltip_text = tooltip - row.add_child(thread) - - var seal := _make_generated_ornament_panel("OpeningStoryBeatSeal", OPENING_STORY_PROGRESS_KNOT_SIZE, "command_seal") - seal.name = "OpeningStoryBeatSeal" - seal.tooltip_text = tooltip - seal.modulate = Color(1.0, 0.95, 0.78, 1.0) if is_current else Color(0.62, 0.58, 0.48, 0.82) - if is_current: - var inner := ColorRect.new() - inner.name = "OpeningStoryBeatCurrentGlow" - inner.color = Color(UI_SEAL_RED.r, UI_SEAL_RED.g, UI_SEAL_RED.b, 0.46) - inner.position = Vector2(5, 5) - inner.size = Vector2(8, 8) - inner.mouse_filter = Control.MOUSE_FILTER_IGNORE - seal.add_child(inner) - row.add_child(seal) - _set_control_tree_tooltip(card, tooltip) - return card +func _opening_story_record_text(index: int, page_count: int) -> String: + if page_count <= 0: + return "서문" + if index >= page_count - 1: + return "마지막 기록" + var names := ["첫 기록", "둘째 기록", "셋째 기록"] + if index >= 0 and index < names.size(): + return names[index] + return "이어진 기록" func _make_map_lattice_overlay(size: Vector2) -> Control: @@ -3862,17 +3880,17 @@ func _create_hud() -> void: opening_caption_readability_mat.name = "OpeningCaptionTextBacking" opening_caption_readability_mat.color = Color(0.0, 0.0, 0.0, 0.62) opening_caption_readability_mat.mouse_filter = Control.MOUSE_FILTER_IGNORE - opening_caption_readability_mat.position = Vector2(38, 488) - opening_caption_readability_mat.size = Vector2(1204, 198) + opening_caption_readability_mat.position = Vector2(30, 488) + opening_caption_readability_mat.size = Vector2(1220, 198) opening_caption_readability_mat.z_as_relative = false opening_caption_readability_mat.z_index = 54 opening_prologue_root.add_child(opening_caption_readability_mat) var opening_caption_panel := PanelContainer.new() opening_caption_panel.name = "OpeningCaptionScroll" - 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.position = Vector2(42, 500) + opening_caption_panel.size = Vector2(1196, 174) + opening_caption_panel.custom_minimum_size = Vector2(1196, 174) opening_caption_panel.modulate = Color(1.0, 1.0, 1.0, 1.0) opening_caption_panel.z_as_relative = false opening_caption_panel.z_index = 68 @@ -3880,7 +3898,7 @@ func _create_hud() -> void: opening_prologue_root.add_child(opening_caption_panel) var opening_caption_row := HBoxContainer.new() - opening_caption_row.custom_minimum_size = Vector2(1136, 126) + opening_caption_row.custom_minimum_size = Vector2(1152, 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) @@ -3907,7 +3925,7 @@ func _create_hud() -> void: opening_title_column.add_child(opening_prologue_step_label) opening_prologue_body_label = Label.new() - opening_prologue_body_label.custom_minimum_size = Vector2(740, 126) + opening_prologue_body_label.custom_minimum_size = Vector2(732, 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 @@ -3917,18 +3935,18 @@ func _create_hud() -> void: opening_caption_row.add_child(opening_prologue_body_label) var opening_button_column := VBoxContainer.new() - opening_button_column.custom_minimum_size = Vector2(148, 126) - opening_button_column.add_theme_constant_override("separation", 7) + opening_button_column.custom_minimum_size = Vector2(170, 126) + opening_button_column.add_theme_constant_override("separation", 8) 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(148, 40) - opening_prologue_skip_button.tooltip_text = "전기를 접고 군막으로 이동합니다." + opening_prologue_skip_button.custom_minimum_size = Vector2(170, 42) + opening_prologue_skip_button.tooltip_text = "서문을 접고 군막으로 이동합니다." opening_prologue_skip_button.expand_icon = false - opening_prologue_skip_button.add_theme_constant_override("icon_max_width", 20) - opening_prologue_skip_button.add_theme_constant_override("h_separation", 7) + opening_prologue_skip_button.add_theme_constant_override("icon_max_width", 22) + 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 opening_prologue_skip_button.add_theme_font_size_override("font_size", 15) @@ -3937,13 +3955,13 @@ func _create_hud() -> void: 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(148, 48) - opening_prologue_next_button.tooltip_text = "다음 장면을 펼칩니다." + opening_prologue_next_button.custom_minimum_size = Vector2(170, 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", 20) - opening_prologue_next_button.add_theme_constant_override("h_separation", 7) + opening_prologue_next_button.add_theme_constant_override("icon_max_width", 22) + 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 opening_prologue_next_button.add_theme_font_size_override("font_size", 16) @@ -14277,13 +14295,13 @@ func _update_opening_prologue_page() -> void: ) _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] + opening_prologue_step_label.text = _opening_story_record_text(opening_prologue_index, 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 "이어 보기" + 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 32ddb3f..6fcb272 100644 --- a/tools/smoke_title_menu.gd +++ b/tools/smoke_title_menu.gd @@ -252,50 +252,50 @@ func _check_title_menu_start_load_and_settings(failures: Array[String]) -> void: failures.append("opening prologue should expose title and story text") elif not scene.opening_prologue_body_label.text.contains("조조") or not scene.opening_prologue_body_label.text.contains("황건의 난"): failures.append("opening prologue should first explain Cao Cao before the Yellow Turban battle") - if scene.opening_prologue_step_label == null or not scene.opening_prologue_step_label.text.begins_with("장면 ") or not scene.opening_prologue_step_label.text.ends_with("/4"): - failures.append("opening prologue should give Cao Cao, decision, Xiahou Dun, and Yingchuan their own beats") + if scene.opening_prologue_step_label == null or scene.opening_prologue_step_label.text != "첫 기록": + failures.append("opening prologue should describe the current record without bare numeric counters") if scene.opening_prologue_root == null or scene.opening_prologue_root.find_child("OpeningChapterSeal", true, false) == null: failures.append("opening prologue should anchor the generated story art with a chapter seal") var opening_story_strip: Node = null if scene.opening_prologue_root != null: - opening_story_strip = scene.opening_prologue_root.find_child("OpeningStoryBeatStrip", true, false) + opening_story_strip = scene.opening_prologue_root.find_child("OpeningStoryLedgerStrip", true, false) if opening_story_strip == null: - failures.append("opening prologue should show a compact four-beat story progress rail") + failures.append("opening prologue should show a compact story ledger in the upper-right corner") else: var progress_caption := opening_story_strip.find_child("OpeningStoryProgressCaption", true, false) as Label - if progress_caption == null or progress_caption.text != "전개": - failures.append("opening story progress rail should use a subtle Korean caption instead of exposed numeric cards") + if progress_caption == null or progress_caption.text != "영천으로": + failures.append("opening story ledger should use a natural Korean chapter caption") + var progress_scene := opening_story_strip.find_child("OpeningStoryProgressScene", true, false) as Label + if progress_scene == null or not progress_scene.text.contains("첫 기록") or not progress_scene.text.contains("황건의 난"): + failures.append("opening story ledger should name the current record and scene") var opening_story_cards: Array = opening_story_strip.find_children("OpeningStoryBeatCard*", "", true, false) - if opening_story_cards.size() != 4: - failures.append("opening story progress rail should expose four story knots: %d" % opening_story_cards.size()) + 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_card in opening_story_cards: - var card_node := opening_story_card as Node - var beat_seal := card_node.find_child("OpeningStoryBeatSeal", true, false) as PanelContainer - var beat_thread := card_node.find_child("OpeningStoryBeatThread", true, false) as ColorRect - if not bool(card_node.get_meta("story_progress_knot", false)): - failures.append("opening story beat should be rendered as a compact progress knot") - if beat_seal == null or not bool(beat_seal.get_meta("generated_panel_texture", false)): - failures.append("opening story beat should use a generated knot seal") - if beat_thread == null: - failures.append("opening story beat should join knots with a subdued thread") - var numeric_labels := card_node.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"]: - failures.append("opening story progress should not expose bare numeric labels") - if bool(card_node.get_meta("current_story_beat", false)): + 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_cards.find(opening_story_card) + 1 + current_index = opening_story_ticks.find(opening_story_tick) + 1 if current_count != 1 or current_index != 1: - failures.append("opening story strip should highlight the first story beat on entry") - if opening_story_strip.custom_minimum_size.x > 360.0 or opening_story_strip.custom_minimum_size.y > 56.0: - failures.append("opening story progress rail should stay compact in the upper-right corner: %s" % str(opening_story_strip.custom_minimum_size)) - if scene.opening_prologue_next_button == null or scene.opening_prologue_next_button.text != "이어 보기": + failures.append("opening story ledger should highlight the first story tick on entry") + 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"]: + failures.append("opening story ledger should not expose bare numeric labels") + if opening_story_strip.custom_minimum_size.x > 380.0 or opening_story_strip.custom_minimum_size.y > 62.0: + failures.append("opening story ledger should stay compact in the upper-right corner: %s" % str(opening_story_strip.custom_minimum_size)) + 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") @@ -362,9 +362,9 @@ func _check_title_menu_start_load_and_settings(failures: Array[String]) -> void: failures.append("opening prologue should change story artwork per page") var second_story_card: Node = null if scene.opening_prologue_root != null: - second_story_card = scene.opening_prologue_root.find_child("OpeningStoryBeatCard2", true, false) + 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 strip should highlight the second beat after advancing") + failures.append("opening story ledger should highlight the second tick 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("작은 군세"): diff --git a/tools/smoke_visual_assets.gd b/tools/smoke_visual_assets.gd index fc93e2b..854867c 100644 --- a/tools/smoke_visual_assets.gd +++ b/tools/smoke_visual_assets.gd @@ -3961,19 +3961,32 @@ 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 != "전기 넘기기": + 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") > 28: + 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") if scene.opening_prologue_skip_button == null: failures.append("opening prologue should expose a skip button") else: + 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") > 28: + 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") + 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") + else: + if opening_ledger.custom_minimum_size.x > 380.0 or opening_ledger.custom_minimum_size.y > 62.0: + failures.append("opening story ledger should stay compact at QHD scale: %s" % str(opening_ledger.custom_minimum_size)) + 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") for index in range(BattleSceneScript.OPENING_PROLOGUE_PAGES.size()): scene.opening_prologue_index = index scene._update_opening_prologue_page()