Add cinematic title artwork

This commit is contained in:
2026-06-19 23:20:51 +09:00
parent 3615b64196
commit e64b86a363
3 changed files with 81 additions and 17 deletions

View File

@@ -111,7 +111,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.
- Title screen offers start, load, and settings; BGM/SFX volume, sound toggles, and edge-scroll preference persist in `user://heros_settings.json`. - Cinematic title screen uses the battlefield backdrop and Cao Cao portrait art, offers start/load/settings, 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 with officer default image paths, optional per-line overrides, and speaker-initial fallback. - Dialogue portrait slot with officer default image paths, optional per-line overrides, and speaker-initial 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.

View File

@@ -26,6 +26,8 @@ const THREAT_OVERLAY_COLOR := Color(0.76, 0.060, 0.035, 0.20)
const THREAT_BORDER_COLOR := Color(0.86, 0.18, 0.10, 0.48) const THREAT_BORDER_COLOR := Color(0.86, 0.18, 0.10, 0.48)
const BGM_MENU_PATH := "res://audio/bgm/menu_theme_placeholder.wav" const BGM_MENU_PATH := "res://audio/bgm/menu_theme_placeholder.wav"
const BGM_BATTLE_PATH := "res://audio/bgm/battle_loop_placeholder.wav" const BGM_BATTLE_PATH := "res://audio/bgm/battle_loop_placeholder.wav"
const TITLE_BACKGROUND_PATH := "res://art/backgrounds/battlefield_frontier.png"
const TITLE_PORTRAIT_PATH := "res://art/portraits/cao_cao.png"
const SETTINGS_PATH := "user://heros_settings.json" const SETTINGS_PATH := "user://heros_settings.json"
const SETTINGS_SAVE_VERSION := 1 const SETTINGS_SAVE_VERSION := 1
const SFX_BOW_RELEASE_PATH := "res://audio/sfx/bow_release_01.wav" const SFX_BOW_RELEASE_PATH := "res://audio/sfx/bow_release_01.wav"
@@ -465,7 +467,11 @@ var top_load_button: Button
var restart_button: Button var restart_button: Button
var new_campaign_button: Button var new_campaign_button: Button
var title_screen_root: Control var title_screen_root: Control
var title_background_texture_rect: TextureRect
var title_portrait_texture_rect: TextureRect
var title_panel: PanelContainer var title_panel: PanelContainer
var title_caption_label: Label
var title_subtitle_label: Label
var title_start_button: Button var title_start_button: Button
var title_load_button: Button var title_load_button: Button
var title_settings_button: Button var title_settings_button: Button
@@ -2997,15 +3003,63 @@ func _create_hud() -> void:
title_screen_root.set_anchors_preset(Control.PRESET_FULL_RECT) title_screen_root.set_anchors_preset(Control.PRESET_FULL_RECT)
root.add_child(title_screen_root) root.add_child(title_screen_root)
title_background_texture_rect = TextureRect.new()
title_background_texture_rect.name = "TitleBattlefieldBackdrop"
title_background_texture_rect.mouse_filter = Control.MOUSE_FILTER_IGNORE
title_background_texture_rect.set_anchors_preset(Control.PRESET_FULL_RECT)
title_background_texture_rect.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
title_background_texture_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_COVERED
title_background_texture_rect.texture = _load_art_texture(TITLE_BACKGROUND_PATH)
title_screen_root.add_child(title_background_texture_rect)
var title_dim := ColorRect.new() var title_dim := ColorRect.new()
title_dim.name = "TitleInkWash" title_dim.name = "TitleInkWash"
title_dim.color = Color(0.025, 0.016, 0.010, 0.92) title_dim.color = Color(0.025, 0.016, 0.010, 0.48)
title_dim.mouse_filter = Control.MOUSE_FILTER_IGNORE
title_dim.set_anchors_preset(Control.PRESET_FULL_RECT) title_dim.set_anchors_preset(Control.PRESET_FULL_RECT)
title_screen_root.add_child(title_dim) title_screen_root.add_child(title_dim)
var title_menu_shadow := ColorRect.new()
title_menu_shadow.name = "TitleMenuInkPanel"
title_menu_shadow.position = Vector2(0, 0)
title_menu_shadow.size = Vector2(790, 720)
title_menu_shadow.color = Color(0.030, 0.018, 0.010, 0.58)
title_menu_shadow.mouse_filter = Control.MOUSE_FILTER_IGNORE
title_screen_root.add_child(title_menu_shadow)
var title_portrait_shadow := ColorRect.new()
title_portrait_shadow.name = "TitlePortraitLacquerField"
title_portrait_shadow.position = Vector2(770, 36)
title_portrait_shadow.size = Vector2(430, 628)
title_portrait_shadow.color = Color(0.045, 0.018, 0.010, 0.56)
title_portrait_shadow.mouse_filter = Control.MOUSE_FILTER_IGNORE
title_screen_root.add_child(title_portrait_shadow)
title_portrait_texture_rect = TextureRect.new()
title_portrait_texture_rect.name = "TitleCaoCaoPortrait"
title_portrait_texture_rect.position = Vector2(804, 48)
title_portrait_texture_rect.size = Vector2(388, 606)
title_portrait_texture_rect.mouse_filter = Control.MOUSE_FILTER_IGNORE
title_portrait_texture_rect.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
title_portrait_texture_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_COVERED
title_portrait_texture_rect.texture = _load_art_texture(TITLE_PORTRAIT_PATH)
title_portrait_texture_rect.modulate = Color(1.0, 0.95, 0.86, 0.94)
title_screen_root.add_child(title_portrait_texture_rect)
var title_portrait_caption := Label.new()
title_portrait_caption.name = "TitlePortraitCaption"
title_portrait_caption.text = "조조"
title_portrait_caption.position = Vector2(818, 604)
title_portrait_caption.size = Vector2(350, 34)
title_portrait_caption.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
title_portrait_caption.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
title_portrait_caption.mouse_filter = Control.MOUSE_FILTER_IGNORE
_apply_label_style(title_portrait_caption, UI_OLD_BRONZE, 18)
title_screen_root.add_child(title_portrait_caption)
title_panel = PanelContainer.new() title_panel = PanelContainer.new()
title_panel.name = "TitleMenuPanel" title_panel.name = "TitleMenuPanel"
title_panel.position = Vector2(286, 76) title_panel.position = Vector2(58, 76)
title_panel.size = Vector2(708, 568) title_panel.size = Vector2(708, 568)
title_panel.custom_minimum_size = Vector2(708, 568) title_panel.custom_minimum_size = Vector2(708, 568)
_apply_panel_style(title_panel, "paper") _apply_panel_style(title_panel, "paper")
@@ -3018,21 +3072,21 @@ func _create_hud() -> void:
title_column.add_child(_make_scroll_rod(616, 5)) title_column.add_child(_make_scroll_rod(616, 5))
title_column.add_child(_make_seal_ribbon(616, 18)) title_column.add_child(_make_seal_ribbon(616, 18))
var title_caption := Label.new() title_caption_label = Label.new()
title_caption.text = "영웅" title_caption_label.text = "조조"
title_caption.custom_minimum_size = Vector2(616, 58) title_caption_label.custom_minimum_size = Vector2(616, 58)
title_caption.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER title_caption_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
title_caption.vertical_alignment = VERTICAL_ALIGNMENT_CENTER title_caption_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
_apply_label_style(title_caption, UI_PARCHMENT_TEXT, 34) _apply_label_style(title_caption_label, UI_PARCHMENT_TEXT, 34)
title_column.add_child(title_caption) title_column.add_child(title_caption_label)
var title_subtitle := Label.new() title_subtitle_label = Label.new()
title_subtitle.text = "난세 첫 군령" title_subtitle_label.text = "난세를 여는 첫 군령"
title_subtitle.custom_minimum_size = Vector2(616, 34) title_subtitle_label.custom_minimum_size = Vector2(616, 34)
title_subtitle.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER title_subtitle_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
title_subtitle.vertical_alignment = VERTICAL_ALIGNMENT_CENTER title_subtitle_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
_apply_label_style(title_subtitle, UI_OLD_BRONZE, 17) _apply_label_style(title_subtitle_label, UI_OLD_BRONZE, 17)
title_column.add_child(title_subtitle) title_column.add_child(title_subtitle_label)
var title_body_panel := PanelContainer.new() var title_body_panel := PanelContainer.new()
title_body_panel.custom_minimum_size = Vector2(616, 314) title_body_panel.custom_minimum_size = Vector2(616, 314)

View File

@@ -41,6 +41,14 @@ func _check_title_menu_start_load_and_settings(failures: Array[String]) -> void:
if scene.title_screen_root == null or not scene.title_screen_root.visible: if scene.title_screen_root == null or not scene.title_screen_root.visible:
failures.append("title screen should be visible on startup") failures.append("title screen should be visible on startup")
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")
if scene.title_portrait_texture_rect == null or scene.title_portrait_texture_rect.texture == null:
failures.append("title screen should show Cao Cao portrait art")
if scene.title_panel == null or scene.title_panel.position.x > 100.0:
failures.append("title menu panel should sit on the left side of the cinematic title screen")
if scene.title_caption_label == null or scene.title_caption_label.text != "조조전":
failures.append("title screen should present the campaign title in Korean")
if scene.briefing_panel == null or scene.briefing_panel.visible: if scene.briefing_panel == null or scene.briefing_panel.visible:
failures.append("briefing should stay hidden behind the title screen") failures.append("briefing should stay hidden behind the title screen")
if scene.title_start_button == null or scene.title_start_button.text != "처음부터 시작": if scene.title_start_button == null or scene.title_start_button.text != "처음부터 시작":
@@ -55,6 +63,8 @@ func _check_title_menu_start_load_and_settings(failures: Array[String]) -> void:
scene._on_title_settings_pressed() scene._on_title_settings_pressed()
if scene.title_settings_panel == null or not scene.title_settings_panel.visible: if scene.title_settings_panel == null or not scene.title_settings_panel.visible:
failures.append("title settings panel should open from the title screen") failures.append("title settings panel should open from the title screen")
if scene.title_start_button != null and scene.title_start_button.visible:
failures.append("title settings should replace the main title commands while open")
if scene.title_bgm_toggle == null or scene.title_sfx_toggle == null or scene.title_edge_scroll_toggle == null: if scene.title_bgm_toggle == null or scene.title_sfx_toggle == null or scene.title_edge_scroll_toggle == null:
failures.append("title settings should expose BGM, SFX, and edge-scroll toggles") failures.append("title settings should expose BGM, SFX, and edge-scroll toggles")
if scene.title_bgm_volume_slider == null or scene.title_sfx_volume_slider == null: if scene.title_bgm_volume_slider == null or scene.title_sfx_volume_slider == null: