From b10eb520b9fc3ee04d23f30f42ec8ae13e4d7adf Mon Sep 17 00:00:00 2001 From: Wickedness Date: Sat, 20 Jun 2026 04:42:39 +0900 Subject: [PATCH] Refine opening prologue presentation --- README.md | 2 +- docs/ARCHITECTURE.md | 2 +- scripts/scenes/battle_scene.gd | 91 ++++++++++++++++------------------ tools/smoke_title_menu.gd | 23 ++++++++- 4 files changed, 67 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index 2f86735..82533f5 100644 --- a/README.md +++ b/README.md @@ -126,7 +126,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 and Cao Cao portrait art, offers start/load/settings, leads new campaigns through a generated four-panel Cao Cao decision and Xiahou Dun opening prologue with wide story artwork plus a bottom caption panel, exposes the same audio/edge-scroll settings from the top in-game toolbar, and persists BGM/SFX volume, sound toggles, and edge-scroll preference in `user://heros_settings.json`. +- Cinematic title screen uses a dedicated ancient-war-camp dawn backdrop and Cao Cao portrait art, offers start/load/settings, leads new campaigns through a generated four-panel Cao Cao decision and Xiahou Dun opening prologue with full-screen story artwork plus a bottom scroll caption panel, exposes the same audio/edge-scroll settings from the top in-game toolbar, and persists BGM/SFX volume, sound toggles, and edge-scroll preference 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/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 36c6621..a6fd80f 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -36,7 +36,7 @@ 1. Load battle data. 2. Hydrate deployments through `DataCatalog`. 3. Dispatch battle-start scenario events. -4. New campaigns first show a short title-to-briefing prologue using wide generated story artwork with a bottom caption panel, then continue into the first scenario briefing. +4. New campaigns first show a short title-to-briefing prologue using full-screen generated story artwork with a bottom scroll caption panel, then continue into the first scenario briefing. 5. Show scenario briefing with the campaign chapter header, battle header, objective summary, chapter overview, selectable camp Talk conversations, manual checkpoint menu, and optional pre-battle shop, Armory, Roster, and Formation setup. 6. Player selects a unit. 7. Briefing completion dispatches battle-begin events, including opening dialogue. diff --git a/scripts/scenes/battle_scene.gd b/scripts/scenes/battle_scene.gd index 196d867..cf78e45 100644 --- a/scripts/scenes/battle_scene.gd +++ b/scripts/scenes/battle_scene.gd @@ -2894,103 +2894,100 @@ func _create_hud() -> void: opening_prologue_background_texture_rect.expand_mode = TextureRect.EXPAND_IGNORE_SIZE opening_prologue_background_texture_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_COVERED opening_prologue_background_texture_rect.texture = _load_art_texture(OPENING_PROLOGUE_REBELLION_PATH) - opening_prologue_background_texture_rect.modulate = Color(0.78, 0.78, 0.74, 0.98) + opening_prologue_background_texture_rect.modulate = Color(0.58, 0.58, 0.56, 1.0) opening_prologue_root.add_child(opening_prologue_background_texture_rect) - var opening_dim := ColorRect.new() - opening_dim.name = "OpeningInkWash" - opening_dim.color = Color(0.030, 0.016, 0.008, 0.62) - opening_dim.mouse_filter = Control.MOUSE_FILTER_IGNORE - opening_dim.set_anchors_preset(Control.PRESET_FULL_RECT) - opening_prologue_root.add_child(opening_dim) - - var opening_panel := PanelContainer.new() - opening_panel.name = "OpeningProloguePanel" - opening_panel.position = Vector2(82, 44) - opening_panel.size = Vector2(1116, 632) - opening_panel.custom_minimum_size = Vector2(1116, 632) - _apply_panel_style(opening_panel, "lacquer") - opening_prologue_root.add_child(opening_panel) - - var opening_shell := VBoxContainer.new() - opening_shell.custom_minimum_size = Vector2(1060, 576) - opening_shell.add_theme_constant_override("separation", 10) - opening_panel.add_child(opening_shell) - opening_shell.add_child(_make_scroll_rod(1060, 6)) - - var opening_image_frame := PanelContainer.new() - opening_image_frame.custom_minimum_size = Vector2(1060, 396) - _apply_panel_style(opening_image_frame, "command_seal") - opening_shell.add_child(opening_image_frame) - opening_prologue_scene_texture_rect = TextureRect.new() opening_prologue_scene_texture_rect.name = "OpeningStoryImage" - opening_prologue_scene_texture_rect.custom_minimum_size = Vector2(1032, 372) + opening_prologue_scene_texture_rect.mouse_filter = Control.MOUSE_FILTER_IGNORE + opening_prologue_scene_texture_rect.set_anchors_preset(Control.PRESET_FULL_RECT) + opening_prologue_scene_texture_rect.custom_minimum_size = Vector2(1280, 720) opening_prologue_scene_texture_rect.expand_mode = TextureRect.EXPAND_IGNORE_SIZE opening_prologue_scene_texture_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_COVERED opening_prologue_scene_texture_rect.texture = _load_art_texture(OPENING_PROLOGUE_REBELLION_PATH) - opening_prologue_scene_texture_rect.modulate = Color(0.98, 0.96, 0.91, 0.98) - opening_prologue_scene_texture_rect.mouse_filter = Control.MOUSE_FILTER_IGNORE - opening_image_frame.add_child(opening_prologue_scene_texture_rect) + opening_prologue_scene_texture_rect.modulate = Color(0.96, 0.95, 0.92, 1.0) + opening_prologue_root.add_child(opening_prologue_scene_texture_rect) + + var opening_top_dim := ColorRect.new() + opening_top_dim.name = "OpeningTopInkShade" + opening_top_dim.color = Color(0.020, 0.017, 0.014, 0.18) + opening_top_dim.mouse_filter = Control.MOUSE_FILTER_IGNORE + opening_top_dim.position = Vector2(0, 0) + opening_top_dim.size = Vector2(1280, 122) + opening_prologue_root.add_child(opening_top_dim) + + var opening_bottom_dim := ColorRect.new() + opening_bottom_dim.name = "OpeningBottomInkShade" + opening_bottom_dim.color = Color(0.020, 0.014, 0.010, 0.42) + opening_bottom_dim.mouse_filter = Control.MOUSE_FILTER_IGNORE + opening_bottom_dim.position = Vector2(0, 456) + opening_bottom_dim.size = Vector2(1280, 264) + opening_prologue_root.add_child(opening_bottom_dim) + + var opening_caption_panel := PanelContainer.new() + opening_caption_panel.name = "OpeningCaptionScroll" + opening_caption_panel.position = Vector2(56, 500) + opening_caption_panel.size = Vector2(1168, 170) + opening_caption_panel.custom_minimum_size = Vector2(1168, 170) + _apply_panel_style(opening_caption_panel, "paper") + opening_prologue_root.add_child(opening_caption_panel) var opening_caption_row := HBoxContainer.new() - opening_caption_row.custom_minimum_size = Vector2(1060, 142) - opening_caption_row.add_theme_constant_override("separation", 12) - opening_shell.add_child(opening_caption_row) + opening_caption_row.custom_minimum_size = Vector2(1118, 122) + opening_caption_row.add_theme_constant_override("separation", 16) + opening_caption_panel.add_child(opening_caption_row) var opening_title_column := VBoxContainer.new() - opening_title_column.custom_minimum_size = Vector2(236, 142) + opening_title_column.custom_minimum_size = Vector2(248, 122) opening_title_column.add_theme_constant_override("separation", 8) opening_caption_row.add_child(opening_title_column) - opening_title_column.add_child(_make_seal_ribbon(236, 18)) + opening_title_column.add_child(_make_seal_ribbon(248, 18)) opening_prologue_title_label = Label.new() - opening_prologue_title_label.custom_minimum_size = Vector2(236, 64) + opening_prologue_title_label.custom_minimum_size = Vector2(248, 62) 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, 26) + _apply_label_style(opening_prologue_title_label, UI_PARCHMENT_TEXT, 27) opening_title_column.add_child(opening_prologue_title_label) opening_prologue_step_label = Label.new() - opening_prologue_step_label.custom_minimum_size = Vector2(236, 30) + opening_prologue_step_label.custom_minimum_size = Vector2(248, 30) 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, 14) opening_title_column.add_child(opening_prologue_step_label) var opening_body_panel := PanelContainer.new() - opening_body_panel.custom_minimum_size = Vector2(562, 142) + opening_body_panel.custom_minimum_size = Vector2(640, 122) _apply_panel_style(opening_body_panel, "edict") opening_caption_row.add_child(opening_body_panel) opening_prologue_body_label = Label.new() - opening_prologue_body_label.custom_minimum_size = Vector2(526, 110) + opening_prologue_body_label.custom_minimum_size = Vector2(602, 88) 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, 20) + _apply_label_style(opening_prologue_body_label, UI_AGED_INK, 19) opening_body_panel.add_child(opening_prologue_body_label) var opening_button_column := VBoxContainer.new() - opening_button_column.custom_minimum_size = Vector2(226, 142) + opening_button_column.custom_minimum_size = Vector2(184, 122) 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.custom_minimum_size = Vector2(226, 42) + opening_prologue_skip_button.custom_minimum_size = Vector2(184, 38) 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.custom_minimum_size = Vector2(226, 48) + opening_prologue_next_button.custom_minimum_size = Vector2(184, 50) 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) opening_button_column.add_child(opening_prologue_skip_button) - opening_shell.add_child(_make_scroll_rod(1060, 5)) - briefing_panel = PanelContainer.new() briefing_panel.visible = false briefing_panel.position = Vector2(212, 16) diff --git a/tools/smoke_title_menu.gd b/tools/smoke_title_menu.gd index e51e37a..46b6829 100644 --- a/tools/smoke_title_menu.gd +++ b/tools/smoke_title_menu.gd @@ -45,8 +45,25 @@ func _check_title_menu_start_load_and_settings(failures: Array[String]) -> void: failures.append("title screen should start with an audible menu BGM stream") elif scene.bgm_player.volume_db < -12.0: failures.append("title menu BGM should be loud enough to hear by default: %.1fdb" % scene.bgm_player.volume_db) - if scene._sfx_stream_for("ui_click") == null or scene._sfx_stream_for("slash") == null or scene._sfx_stream_for("victory_sting") == null: - failures.append("core UI, combat, and result SFX streams should load") + if scene._bgm_stream_for("battle") == null: + failures.append("battle BGM stream should load before combat starts") + for sfx_key in [ + "ui_click", + "menu_confirm", + "menu_cancel", + "footstep", + "slash", + "bow_release", + "hit_heavy", + "guard_block", + "skill_cast", + "fire_skill", + "item_pickup", + "victory_sting", + "defeat_sting" + ]: + if scene._sfx_stream_for(sfx_key) == null: + failures.append("SFX stream should load: %s" % sfx_key) if scene.title_background_texture_rect == null or scene.title_background_texture_rect.texture == null: failures.append("title screen should show a high-resolution battlefield backdrop") elif BattleSceneScript.TITLE_BACKGROUND_PATH == BattleSceneScript.DEFAULT_BATTLE_BACKGROUND_PATH: @@ -126,6 +143,8 @@ func _check_title_menu_start_load_and_settings(failures: Array[String]) -> void: failures.append("opening prologue should show generated story artwork") elif scene.opening_prologue_scene_texture_rect.custom_minimum_size.x <= scene.opening_prologue_scene_texture_rect.custom_minimum_size.y: 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 < 500: failures.append("opening prologue body text should have enough caption width for Korean copy") var first_story_texture: Texture2D = null