diff --git a/README.md b/README.md index 36f168e..39ec57d 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,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. -- 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. - 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. diff --git a/scripts/scenes/battle_scene.gd b/scripts/scenes/battle_scene.gd index ddc3b7c..d29015e 100644 --- a/scripts/scenes/battle_scene.gd +++ b/scripts/scenes/battle_scene.gd @@ -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 BGM_MENU_PATH := "res://audio/bgm/menu_theme_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_SAVE_VERSION := 1 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 new_campaign_button: Button var title_screen_root: Control +var title_background_texture_rect: TextureRect +var title_portrait_texture_rect: TextureRect var title_panel: PanelContainer +var title_caption_label: Label +var title_subtitle_label: Label var title_start_button: Button var title_load_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) 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() 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_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.name = "TitleMenuPanel" - title_panel.position = Vector2(286, 76) + title_panel.position = Vector2(58, 76) title_panel.size = Vector2(708, 568) title_panel.custom_minimum_size = Vector2(708, 568) _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_seal_ribbon(616, 18)) - var title_caption := Label.new() - title_caption.text = "영웅전" - title_caption.custom_minimum_size = Vector2(616, 58) - title_caption.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER - title_caption.vertical_alignment = VERTICAL_ALIGNMENT_CENTER - _apply_label_style(title_caption, UI_PARCHMENT_TEXT, 34) - title_column.add_child(title_caption) + title_caption_label = Label.new() + title_caption_label.text = "조조전" + title_caption_label.custom_minimum_size = Vector2(616, 58) + title_caption_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + title_caption_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + _apply_label_style(title_caption_label, UI_PARCHMENT_TEXT, 34) + title_column.add_child(title_caption_label) - var title_subtitle := Label.new() - title_subtitle.text = "난세의 첫 군령" - title_subtitle.custom_minimum_size = Vector2(616, 34) - title_subtitle.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER - title_subtitle.vertical_alignment = VERTICAL_ALIGNMENT_CENTER - _apply_label_style(title_subtitle, UI_OLD_BRONZE, 17) - title_column.add_child(title_subtitle) + title_subtitle_label = Label.new() + title_subtitle_label.text = "난세를 여는 첫 군령" + title_subtitle_label.custom_minimum_size = Vector2(616, 34) + title_subtitle_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER + title_subtitle_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + _apply_label_style(title_subtitle_label, UI_OLD_BRONZE, 17) + title_column.add_child(title_subtitle_label) var title_body_panel := PanelContainer.new() title_body_panel.custom_minimum_size = Vector2(616, 314) diff --git a/tools/smoke_title_menu.gd b/tools/smoke_title_menu.gd index e585c72..063cfeb 100644 --- a/tools/smoke_title_menu.gd +++ b/tools/smoke_title_menu.gd @@ -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: 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: failures.append("briefing should stay hidden behind the title screen") 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() if scene.title_settings_panel == null or not scene.title_settings_panel.visible: 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: 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: