Refine opening prologue presentation
This commit is contained in:
@@ -126,7 +126,7 @@ Godot 4 tactical RPG prototype inspired by classic turn-based Romance of the Thr
|
|||||||
- Campaign completion screen.
|
- Campaign completion screen.
|
||||||
- New campaign save reset.
|
- New campaign save reset.
|
||||||
- Manual campaign checkpoint save/load.
|
- 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.
|
- 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.
|
- 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.
|
- Initial AI-generated photorealistic officer portraits for Cao Cao, Xiahou Dun, Xiahou Yuan, Cao Ren, Dian Wei, Guo Jia, and Zhang He.
|
||||||
|
|||||||
@@ -36,7 +36,7 @@
|
|||||||
1. Load battle data.
|
1. Load battle data.
|
||||||
2. Hydrate deployments through `DataCatalog`.
|
2. Hydrate deployments through `DataCatalog`.
|
||||||
3. Dispatch battle-start scenario events.
|
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.
|
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.
|
6. Player selects a unit.
|
||||||
7. Briefing completion dispatches battle-begin events, including opening dialogue.
|
7. Briefing completion dispatches battle-begin events, including opening dialogue.
|
||||||
|
|||||||
@@ -2894,103 +2894,100 @@ func _create_hud() -> void:
|
|||||||
opening_prologue_background_texture_rect.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
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.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.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)
|
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 = TextureRect.new()
|
||||||
opening_prologue_scene_texture_rect.name = "OpeningStoryImage"
|
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.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||||
opening_prologue_scene_texture_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_COVERED
|
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.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.modulate = Color(0.96, 0.95, 0.92, 1.0)
|
||||||
opening_prologue_scene_texture_rect.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
opening_prologue_root.add_child(opening_prologue_scene_texture_rect)
|
||||||
opening_image_frame.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()
|
var opening_caption_row := HBoxContainer.new()
|
||||||
opening_caption_row.custom_minimum_size = Vector2(1060, 142)
|
opening_caption_row.custom_minimum_size = Vector2(1118, 122)
|
||||||
opening_caption_row.add_theme_constant_override("separation", 12)
|
opening_caption_row.add_theme_constant_override("separation", 16)
|
||||||
opening_shell.add_child(opening_caption_row)
|
opening_caption_panel.add_child(opening_caption_row)
|
||||||
|
|
||||||
var opening_title_column := VBoxContainer.new()
|
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_title_column.add_theme_constant_override("separation", 8)
|
||||||
opening_caption_row.add_child(opening_title_column)
|
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 = 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.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
opening_prologue_title_label.vertical_alignment = VERTICAL_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_title_column.add_child(opening_prologue_title_label)
|
||||||
|
|
||||||
opening_prologue_step_label = Label.new()
|
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.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
opening_prologue_step_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
opening_prologue_step_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||||
_apply_label_style(opening_prologue_step_label, UI_OLD_BRONZE, 14)
|
_apply_label_style(opening_prologue_step_label, UI_OLD_BRONZE, 14)
|
||||||
opening_title_column.add_child(opening_prologue_step_label)
|
opening_title_column.add_child(opening_prologue_step_label)
|
||||||
|
|
||||||
var opening_body_panel := PanelContainer.new()
|
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")
|
_apply_panel_style(opening_body_panel, "edict")
|
||||||
opening_caption_row.add_child(opening_body_panel)
|
opening_caption_row.add_child(opening_body_panel)
|
||||||
|
|
||||||
opening_prologue_body_label = Label.new()
|
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.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||||
opening_prologue_body_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
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)
|
opening_body_panel.add_child(opening_prologue_body_label)
|
||||||
|
|
||||||
var opening_button_column := VBoxContainer.new()
|
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_button_column.add_theme_constant_override("separation", 8)
|
||||||
opening_caption_row.add_child(opening_button_column)
|
opening_caption_row.add_child(opening_button_column)
|
||||||
|
|
||||||
opening_prologue_skip_button = Button.new()
|
opening_prologue_skip_button = Button.new()
|
||||||
opening_prologue_skip_button.text = "건너뛰기"
|
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.tooltip_text = "오프닝을 닫고 군막으로 이동합니다."
|
||||||
opening_prologue_skip_button.pressed.connect(_on_opening_prologue_skip_pressed)
|
opening_prologue_skip_button.pressed.connect(_on_opening_prologue_skip_pressed)
|
||||||
|
|
||||||
opening_prologue_next_button = Button.new()
|
opening_prologue_next_button = Button.new()
|
||||||
opening_prologue_next_button.text = "다음"
|
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.tooltip_text = "다음 이야기로 넘깁니다."
|
||||||
opening_prologue_next_button.pressed.connect(_on_opening_prologue_next_pressed)
|
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_next_button)
|
||||||
opening_button_column.add_child(opening_prologue_skip_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 = PanelContainer.new()
|
||||||
briefing_panel.visible = false
|
briefing_panel.visible = false
|
||||||
briefing_panel.position = Vector2(212, 16)
|
briefing_panel.position = Vector2(212, 16)
|
||||||
|
|||||||
@@ -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")
|
failures.append("title screen should start with an audible menu BGM stream")
|
||||||
elif scene.bgm_player.volume_db < -12.0:
|
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)
|
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:
|
if scene._bgm_stream_for("battle") == null:
|
||||||
failures.append("core UI, combat, and result SFX streams should load")
|
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:
|
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")
|
failures.append("title screen should show a high-resolution battlefield backdrop")
|
||||||
elif BattleSceneScript.TITLE_BACKGROUND_PATH == BattleSceneScript.DEFAULT_BATTLE_BACKGROUND_PATH:
|
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")
|
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:
|
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")
|
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:
|
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")
|
failures.append("opening prologue body text should have enough caption width for Korean copy")
|
||||||
var first_story_texture: Texture2D = null
|
var first_story_texture: Texture2D = null
|
||||||
|
|||||||
Reference in New Issue
Block a user