diff --git a/README.md b/README.md index 3009437..cdd1aa8 100644 --- a/README.md +++ b/README.md @@ -129,7 +129,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, opens with a large generated Yellow Turban story feature panel plus four generated opening story previews on the title menu, leads new campaigns through 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, exposes the same audio/edge-scroll settings from the top in-game toolbar, includes a one-click audio recovery control, 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, opens with a wide generated Yellow Turban story feature panel plus four generated opening story previews in framed artwork cards, leads new campaigns through 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, exposes the same audio/edge-scroll settings from the top in-game toolbar, includes a one-click audio recovery control, 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/scripts/scenes/battle_scene.gd b/scripts/scenes/battle_scene.gd index 734e7ec..44089f0 100644 --- a/scripts/scenes/battle_scene.gd +++ b/scripts/scenes/battle_scene.gd @@ -145,7 +145,11 @@ const BRIEFING_FORCE_PREVIEW_ICON_SIZE := Vector2(32, 32) const BRIEFING_FORCE_PREVIEW_MAX_ALLIES := 2 const BRIEFING_FORCE_PREVIEW_MAX_ENEMY_TYPES := 4 const TITLE_STORY_FEATURE_SIZE := Vector2(616, 122) -const TITLE_STORY_FEATURE_IMAGE_SIZE := Vector2(238, 96) +const TITLE_STORY_FEATURE_IMAGE_SIZE := Vector2(314, 96) +const TITLE_STORY_PREVIEW_STRIP_SIZE := Vector2(616, 86) +const TITLE_STORY_PREVIEW_CARD_SIZE := Vector2(140, 66) +const TITLE_STORY_PREVIEW_IMAGE_SIZE := Vector2(132, 44) +const TITLE_STORY_PREVIEW_LABEL_SIZE := Vector2(132, 15) const OPENING_STORY_BEAT_CARD_SIZE := Vector2(122, 66) const OPENING_STORY_BEAT_IMAGE_SIZE := Vector2(112, 42) const COMMAND_NOTICE_ICON_SIZE := Vector2(32, 32) @@ -1988,13 +1992,13 @@ func _make_title_story_feature_panel() -> PanelContainer: var row := HBoxContainer.new() row.custom_minimum_size = Vector2(584, 104) - row.add_theme_constant_override("separation", 10) + row.add_theme_constant_override("separation", 8) row.tooltip_text = tooltip panel.add_child(row) var image_stack := PanelContainer.new() image_stack.name = "TitleStoryFeatureImageFrame" - image_stack.custom_minimum_size = Vector2(252, 104) + image_stack.custom_minimum_size = Vector2(326, 104) image_stack.tooltip_text = tooltip _apply_panel_style(image_stack, "caption") row.add_child(image_stack) @@ -2010,14 +2014,14 @@ func _make_title_story_feature_panel() -> PanelContainer: image_stack.add_child(image) var text_column := VBoxContainer.new() - text_column.custom_minimum_size = Vector2(320, 104) + text_column.custom_minimum_size = Vector2(250, 104) text_column.size_flags_horizontal = Control.SIZE_EXPAND_FILL text_column.add_theme_constant_override("separation", 4) text_column.tooltip_text = tooltip row.add_child(text_column) var title_row := HBoxContainer.new() - title_row.custom_minimum_size = Vector2(320, 26) + title_row.custom_minimum_size = Vector2(250, 26) title_row.add_theme_constant_override("separation", 6) title_row.tooltip_text = tooltip text_column.add_child(title_row) @@ -2026,7 +2030,7 @@ func _make_title_story_feature_panel() -> PanelContainer: var title_label := Label.new() title_label.name = "TitleStoryFeatureTitle" title_label.text = title - title_label.custom_minimum_size = Vector2(284, 26) + title_label.custom_minimum_size = Vector2(214, 26) title_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER title_label.tooltip_text = tooltip _apply_label_style(title_label, UI_PARCHMENT_TEXT, 18) @@ -2035,7 +2039,7 @@ func _make_title_story_feature_panel() -> PanelContainer: var body_label := Label.new() body_label.name = "TitleStoryFeatureBody" body_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART - body_label.custom_minimum_size = Vector2(320, 68) + body_label.custom_minimum_size = Vector2(250, 68) body_label.tooltip_text = tooltip _apply_label_style(body_label, UI_AGED_INK, 13) _set_fitted_label_text(body_label, body, 13, 10, body_label.custom_minimum_size) @@ -2048,11 +2052,11 @@ func _make_title_story_feature_panel() -> PanelContainer: func _make_title_story_preview_strip() -> PanelContainer: var panel := PanelContainer.new() panel.name = "TitleStoryPreviewStrip" - panel.custom_minimum_size = Vector2(616, 78) + panel.custom_minimum_size = TITLE_STORY_PREVIEW_STRIP_SIZE _apply_panel_style(panel, "hud_info") var row := HBoxContainer.new() - row.custom_minimum_size = Vector2(584, 58) + row.custom_minimum_size = Vector2(584, 66) row.add_theme_constant_override("separation", 8) panel.add_child(row) @@ -2062,32 +2066,44 @@ func _make_title_story_preview_strip() -> PanelContainer: return panel -func _make_title_story_preview_card(title: String, image_path: String, index: int) -> VBoxContainer: - var card := VBoxContainer.new() +func _make_title_story_preview_card(title: String, image_path: String, index: int) -> PanelContainer: + var card := PanelContainer.new() card.name = "TitleStoryPreviewCard%d" % index - card.custom_minimum_size = Vector2(140, 58) - card.add_theme_constant_override("separation", 3) + card.custom_minimum_size = TITLE_STORY_PREVIEW_CARD_SIZE card.tooltip_text = "%d. %s" % [index, title] + _apply_panel_style(card, "caption") + var card_style := card.get_theme_stylebox("panel") + if card_style != null: + card_style.content_margin_left = 4 + card_style.content_margin_right = 4 + card_style.content_margin_top = 3 + card_style.content_margin_bottom = 3 + + var column := VBoxContainer.new() + column.custom_minimum_size = Vector2(132, 59) + column.add_theme_constant_override("separation", 2) + column.tooltip_text = card.tooltip_text + card.add_child(column) var texture_rect := TextureRect.new() texture_rect.name = "TitleStoryPreviewImage" - texture_rect.custom_minimum_size = Vector2(140, 38) + texture_rect.custom_minimum_size = TITLE_STORY_PREVIEW_IMAGE_SIZE texture_rect.expand_mode = TextureRect.EXPAND_IGNORE_SIZE texture_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_COVERED texture_rect.texture = _load_art_texture(image_path) texture_rect.modulate = Color(0.96, 0.94, 0.88, 0.94) texture_rect.tooltip_text = card.tooltip_text - card.add_child(texture_rect) + column.add_child(texture_rect) var label := Label.new() label.name = "TitleStoryPreviewLabel" label.text = title - label.custom_minimum_size = Vector2(140, 17) + label.custom_minimum_size = TITLE_STORY_PREVIEW_LABEL_SIZE label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER label.tooltip_text = card.tooltip_text _apply_label_style(label, UI_OLD_BRONZE, 10) - card.add_child(label) + column.add_child(label) return card @@ -3841,9 +3857,9 @@ func _create_hud() -> void: title_panel = PanelContainer.new() title_panel.name = "TitleMenuPanel" - title_panel.position = Vector2(58, 24) - title_panel.size = Vector2(708, 670) - title_panel.custom_minimum_size = Vector2(708, 670) + title_panel.position = Vector2(58, 18) + title_panel.size = Vector2(708, 682) + title_panel.custom_minimum_size = Vector2(708, 682) _apply_panel_style(title_panel, "paper") title_screen_root.add_child(title_panel) diff --git a/tools/smoke_title_menu.gd b/tools/smoke_title_menu.gd index 4f8cf04..aa2b501 100644 --- a/tools/smoke_title_menu.gd +++ b/tools/smoke_title_menu.gd @@ -85,6 +85,8 @@ func _check_title_menu_start_load_and_settings(failures: Array[String]) -> void: var feature_body := title_story_feature.find_child("TitleStoryFeatureBody", true, false) as Label if feature_image == null or feature_image.texture == null: failures.append("title story feature should load generated opening artwork") + elif feature_image.custom_minimum_size.x < 300.0: + failures.append("title story feature should lead with a wide generated artwork frame: %s" % str(feature_image.custom_minimum_size)) if feature_title == null or feature_title.text != "황건의 난": failures.append("title story feature should name the first opening beat") if feature_body == null or not feature_body.text.contains("조조") or not feature_body.text.contains("황건의 난"): @@ -99,10 +101,14 @@ func _check_title_menu_start_load_and_settings(failures: Array[String]) -> void: if story_cards.size() != 4: failures.append("title story preview should expose four story beats: %d" % story_cards.size()) for story_card in story_cards: + if not story_card is PanelContainer: + failures.append("title story preview cards should use generated panel frames") var story_image := (story_card as Node).find_child("TitleStoryPreviewImage", true, false) as TextureRect var story_label := (story_card as Node).find_child("TitleStoryPreviewLabel", true, false) as Label if story_image == null or story_image.texture == null: failures.append("title story preview card should load generated artwork") + elif story_image.custom_minimum_size.y < 42.0: + failures.append("title story preview image should stay large enough to read as artwork: %s" % str(story_image.custom_minimum_size)) if story_label == null or story_label.text.strip_edges().is_empty(): failures.append("title story preview card should name its story beat") if scene.briefing_panel == null or scene.briefing_panel.visible: