Add briefing force preview markers
This commit is contained in:
@@ -33,7 +33,7 @@ Godot 4 tactical RPG prototype inspired by classic turn-based Romance of the Thr
|
|||||||
- Pre-battle Armory equipment changes, including unequipping gear back into stock, save immediately and sync roster equipment plus inventory stock, with portrait and item-icon rows plus hover change details.
|
- Pre-battle Armory equipment changes, including unequipping gear back into stock, save immediately and sync roster equipment plus inventory stock, with portrait and item-icon rows plus hover change details.
|
||||||
- Pre-battle Roster can move optional officers between sortie and reserve within scenario limits, using portrait rows, compact status badges, and hover details.
|
- Pre-battle Roster can move optional officers between sortie and reserve within scenario limits, using portrait rows, compact status badges, and hover details.
|
||||||
- Pre-battle Formation lets deployed officers swap starting positions inside scenario-defined cells, with portrait rows, readable zone badges, and hover placement instructions.
|
- Pre-battle Formation lets deployed officers swap starting positions inside scenario-defined cells, with portrait rows, readable zone badges, and hover placement instructions.
|
||||||
- Pre-battle briefing shows the campaign chapter, chapter battle number, location, victory/defeat summary, first-clear reward preview, generated battlefield thumbnail, and image-first tactical marker cards for lure lines and supply points.
|
- Pre-battle briefing shows the campaign chapter, chapter battle number, location, victory/defeat summary, first-clear reward preview, generated battlefield thumbnail, allied portrait/enemy unit force markers, and image-first tactical marker cards for lure lines and supply points.
|
||||||
- Pre-battle prep commands use compact icon buttons with active selection state and Korean hover labels for chapters, shop, talks, armory, roster, formation, and save records.
|
- Pre-battle prep commands use compact icon buttons with active selection state and Korean hover labels for chapters, shop, talks, armory, roster, formation, and save records.
|
||||||
- Battle, top-toolbar, and pre-battle command buttons now prefer generated high-resolution bronze/parchment icon art under `art/ui/icons` and generated lacquer/jade/cinnabar button surfaces under `art/ui/buttons`, with procedural icon drawing and flat button styles kept only as fallbacks.
|
- Battle, top-toolbar, and pre-battle command buttons now prefer generated high-resolution bronze/parchment icon art under `art/ui/icons` and generated lacquer/jade/cinnabar button surfaces under `art/ui/buttons`, with procedural icon drawing and flat button styles kept only as fallbacks.
|
||||||
- Battle maps blend generated terrain texture tiles for plains, forests, hills, wasteland, roads, water, villages, and castles over the high-resolution battlefield backdrop, with generated transparent feature overlays for road connections, water, villages, castles, and wasteland accents.
|
- Battle maps blend generated terrain texture tiles for plains, forests, hills, wasteland, roads, water, villages, and castles over the high-resolution battlefield backdrop, with generated transparent feature overlays for road connections, water, villages, castles, and wasteland accents.
|
||||||
|
|||||||
@@ -139,6 +139,9 @@ const BRIEFING_CAMP_THUMBNAIL_SIZE := Vector2(188, 86)
|
|||||||
const BRIEFING_TACTICAL_MARKER_CHIP_SIZE := Vector2(122, 26)
|
const BRIEFING_TACTICAL_MARKER_CHIP_SIZE := Vector2(122, 26)
|
||||||
const BRIEFING_TACTICAL_MARKER_ICON_SIZE := Vector2(22, 22)
|
const BRIEFING_TACTICAL_MARKER_ICON_SIZE := Vector2(22, 22)
|
||||||
const BRIEFING_TACTICAL_MARKER_MAX_COUNT := 3
|
const BRIEFING_TACTICAL_MARKER_MAX_COUNT := 3
|
||||||
|
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 OPENING_STORY_BEAT_CARD_SIZE := Vector2(122, 66)
|
const OPENING_STORY_BEAT_CARD_SIZE := Vector2(122, 66)
|
||||||
const OPENING_STORY_BEAT_IMAGE_SIZE := Vector2(112, 42)
|
const OPENING_STORY_BEAT_IMAGE_SIZE := Vector2(112, 42)
|
||||||
const CHAPTER_SEAL_BADGE_SIZE := Vector2(54, 54)
|
const CHAPTER_SEAL_BADGE_SIZE := Vector2(54, 54)
|
||||||
@@ -409,6 +412,7 @@ var briefing_camp_overview_row: HBoxContainer
|
|||||||
var briefing_camp_overview_texture: TextureRect
|
var briefing_camp_overview_texture: TextureRect
|
||||||
var briefing_camp_overview_fallback_label: Label
|
var briefing_camp_overview_fallback_label: Label
|
||||||
var briefing_camp_overview_label: Label
|
var briefing_camp_overview_label: Label
|
||||||
|
var briefing_force_preview_overlay: Control
|
||||||
var briefing_tactical_marker_list: VBoxContainer
|
var briefing_tactical_marker_list: VBoxContainer
|
||||||
var briefing_label: Label
|
var briefing_label: Label
|
||||||
var briefing_start_button: Button
|
var briefing_start_button: Button
|
||||||
@@ -3270,6 +3274,13 @@ func _create_hud() -> void:
|
|||||||
camp_thumbnail_stack.add_child(briefing_camp_overview_fallback_label)
|
camp_thumbnail_stack.add_child(briefing_camp_overview_fallback_label)
|
||||||
camp_thumbnail_stack.add_child(_make_map_lattice_overlay(BRIEFING_CAMP_THUMBNAIL_SIZE))
|
camp_thumbnail_stack.add_child(_make_map_lattice_overlay(BRIEFING_CAMP_THUMBNAIL_SIZE))
|
||||||
|
|
||||||
|
briefing_force_preview_overlay = Control.new()
|
||||||
|
briefing_force_preview_overlay.name = "BriefingForcePreviewOverlay"
|
||||||
|
briefing_force_preview_overlay.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||||
|
briefing_force_preview_overlay.custom_minimum_size = BRIEFING_CAMP_THUMBNAIL_SIZE
|
||||||
|
briefing_force_preview_overlay.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
camp_thumbnail_stack.add_child(briefing_force_preview_overlay)
|
||||||
|
|
||||||
briefing_camp_overview_label = Label.new()
|
briefing_camp_overview_label = Label.new()
|
||||||
briefing_camp_overview_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
briefing_camp_overview_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||||
briefing_camp_overview_label.custom_minimum_size = Vector2(238, 86)
|
briefing_camp_overview_label.custom_minimum_size = Vector2(238, 86)
|
||||||
@@ -10346,9 +10357,118 @@ func _update_briefing_camp_overview(briefing: Dictionary, prep_locked: bool) ->
|
|||||||
if briefing_camp_overview_fallback_label != null:
|
if briefing_camp_overview_fallback_label != null:
|
||||||
briefing_camp_overview_fallback_label.text = "비단 전장도"
|
briefing_camp_overview_fallback_label.text = "비단 전장도"
|
||||||
briefing_camp_overview_fallback_label.visible = not has_texture
|
briefing_camp_overview_fallback_label.visible = not has_texture
|
||||||
|
_rebuild_briefing_force_preview_overlay()
|
||||||
_rebuild_briefing_tactical_marker_list()
|
_rebuild_briefing_tactical_marker_list()
|
||||||
|
|
||||||
|
|
||||||
|
func _rebuild_briefing_force_preview_overlay() -> void:
|
||||||
|
if briefing_force_preview_overlay == null:
|
||||||
|
return
|
||||||
|
for child in briefing_force_preview_overlay.get_children():
|
||||||
|
briefing_force_preview_overlay.remove_child(child)
|
||||||
|
child.queue_free()
|
||||||
|
var allies := _briefing_force_preview_allies()
|
||||||
|
for index in range(allies.size()):
|
||||||
|
var ally: Dictionary = allies[index]
|
||||||
|
var icon := _make_briefing_force_preview_icon(ally, "ally", _format_briefing_force_unit_tooltip(ally, "아군"))
|
||||||
|
icon.name = "BriefingForcePreviewAllyIcon%d" % (index + 1)
|
||||||
|
icon.position = Vector2(8.0 + float(index) * 34.0, 46.0)
|
||||||
|
briefing_force_preview_overlay.add_child(icon)
|
||||||
|
var enemies := _briefing_force_preview_enemy_types()
|
||||||
|
for index in range(enemies.size()):
|
||||||
|
var enemy: Dictionary = enemies[index]
|
||||||
|
var icon := _make_briefing_force_preview_icon(enemy, "enemy", _format_briefing_force_unit_tooltip(enemy, "적세"))
|
||||||
|
icon.name = "BriefingForcePreviewEnemyIcon%d" % (index + 1)
|
||||||
|
icon.position = Vector2(148.0 - float(index) * 30.0, 8.0)
|
||||||
|
briefing_force_preview_overlay.add_child(icon)
|
||||||
|
|
||||||
|
|
||||||
|
func _briefing_force_preview_allies() -> Array[Dictionary]:
|
||||||
|
var result: Array[Dictionary] = []
|
||||||
|
for unit in state.get_controllable_player_units():
|
||||||
|
result.append(unit)
|
||||||
|
if result.size() >= BRIEFING_FORCE_PREVIEW_MAX_ALLIES:
|
||||||
|
break
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
func _briefing_force_preview_enemy_types() -> Array[Dictionary]:
|
||||||
|
var result: Array[Dictionary] = []
|
||||||
|
var seen_classes := {}
|
||||||
|
for unit in state.get_living_units(BattleState.TEAM_ENEMY):
|
||||||
|
var class_key := str(unit.get("class_id", _unit_class_display_name(unit))).strip_edges()
|
||||||
|
if class_key.is_empty():
|
||||||
|
class_key = str(unit.get("name", "enemy"))
|
||||||
|
if seen_classes.has(class_key):
|
||||||
|
continue
|
||||||
|
seen_classes[class_key] = true
|
||||||
|
result.append(unit)
|
||||||
|
if result.size() >= BRIEFING_FORCE_PREVIEW_MAX_ENEMY_TYPES:
|
||||||
|
break
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
func _make_briefing_force_preview_icon(unit: Dictionary, side: String, tooltip_text: String) -> PanelContainer:
|
||||||
|
var panel := PanelContainer.new()
|
||||||
|
panel.custom_minimum_size = BRIEFING_FORCE_PREVIEW_ICON_SIZE
|
||||||
|
panel.tooltip_text = tooltip_text
|
||||||
|
panel.set_meta("briefing_force_side", side)
|
||||||
|
_apply_panel_style(panel, "portrait")
|
||||||
|
var panel_style := panel.get_theme_stylebox("panel")
|
||||||
|
if panel_style != null:
|
||||||
|
panel_style.content_margin_left = 2
|
||||||
|
panel_style.content_margin_right = 2
|
||||||
|
panel_style.content_margin_top = 2
|
||||||
|
panel_style.content_margin_bottom = 2
|
||||||
|
|
||||||
|
var stack := Control.new()
|
||||||
|
stack.custom_minimum_size = BRIEFING_FORCE_PREVIEW_ICON_SIZE
|
||||||
|
stack.tooltip_text = tooltip_text
|
||||||
|
panel.add_child(stack)
|
||||||
|
|
||||||
|
var texture_rect := TextureRect.new()
|
||||||
|
texture_rect.name = "BriefingForcePreviewImage"
|
||||||
|
texture_rect.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||||
|
texture_rect.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||||||
|
texture_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
||||||
|
texture_rect.texture = _briefing_force_preview_texture(unit, side)
|
||||||
|
texture_rect.visible = texture_rect.texture != null
|
||||||
|
texture_rect.tooltip_text = tooltip_text
|
||||||
|
stack.add_child(texture_rect)
|
||||||
|
|
||||||
|
var fallback_label := Label.new()
|
||||||
|
fallback_label.set_anchors_preset(Control.PRESET_FULL_RECT)
|
||||||
|
fallback_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
fallback_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||||
|
fallback_label.text = _dialogue_portrait_initials(str(unit.get("name", "")))
|
||||||
|
fallback_label.visible = texture_rect.texture == null
|
||||||
|
fallback_label.tooltip_text = tooltip_text
|
||||||
|
_apply_label_style(fallback_label, UI_PARCHMENT_TEXT, 11)
|
||||||
|
stack.add_child(fallback_label)
|
||||||
|
_set_control_tree_tooltip(panel, tooltip_text)
|
||||||
|
return panel
|
||||||
|
|
||||||
|
|
||||||
|
func _briefing_force_preview_texture(unit: Dictionary, side: String) -> Texture2D:
|
||||||
|
var texture: Texture2D = null
|
||||||
|
if side == "ally":
|
||||||
|
texture = _load_portrait_texture(str(unit.get("portrait", "")))
|
||||||
|
if texture == null:
|
||||||
|
texture = _load_art_texture(str(unit.get("sprite", "")))
|
||||||
|
if texture == null:
|
||||||
|
texture = _load_unit_class_icon_texture(unit)
|
||||||
|
return texture
|
||||||
|
|
||||||
|
|
||||||
|
func _format_briefing_force_unit_tooltip(unit: Dictionary, side_label: String) -> String:
|
||||||
|
return "%s · %s\n%s · 품계 %d" % [
|
||||||
|
side_label,
|
||||||
|
str(unit.get("name", "부대")),
|
||||||
|
_unit_class_display_name(unit),
|
||||||
|
int(unit.get("level", 1))
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
func _rebuild_briefing_tactical_marker_list() -> void:
|
func _rebuild_briefing_tactical_marker_list() -> void:
|
||||||
if briefing_tactical_marker_list == null:
|
if briefing_tactical_marker_list == null:
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -3102,6 +3102,16 @@ func _check_scene_texture_loading(failures: Array[String]) -> void:
|
|||||||
for expected_marker in ["유인선", "북숲 보급고", "마을 보급"]:
|
for expected_marker in ["유인선", "북숲 보급고", "마을 보급"]:
|
||||||
if not briefing_marker_labels.has(expected_marker):
|
if not briefing_marker_labels.has(expected_marker):
|
||||||
failures.append("briefing tactical markers should include %s: %s" % [expected_marker, str(briefing_marker_labels)])
|
failures.append("briefing tactical markers should include %s: %s" % [expected_marker, str(briefing_marker_labels)])
|
||||||
|
var briefing_allies := scene._briefing_force_preview_allies()
|
||||||
|
if briefing_allies.size() < 2:
|
||||||
|
failures.append("briefing force preview should expose Cao Cao and Xiahou Dun")
|
||||||
|
var briefing_enemy_types := scene._briefing_force_preview_enemy_types()
|
||||||
|
if briefing_enemy_types.size() < 3:
|
||||||
|
failures.append("briefing force preview should expose representative enemy unit art")
|
||||||
|
for preview_unit in briefing_allies + briefing_enemy_types:
|
||||||
|
var preview_side := "ally" if str(preview_unit.get("team", "")) == BattleStateScript.TEAM_PLAYER else "enemy"
|
||||||
|
if scene._briefing_force_preview_texture(preview_unit, preview_side) == null:
|
||||||
|
failures.append("briefing force preview unit should resolve portrait or unit art: %s" % str(preview_unit.get("name", "")))
|
||||||
var bean: Dictionary = scene.state.get_item_def("bean")
|
var bean: Dictionary = scene.state.get_item_def("bean")
|
||||||
var bean_buy_detail := scene._format_shop_buy_item_detail_text("bean", bean, int(bean.get("price", 0)), 0)
|
var bean_buy_detail := scene._format_shop_buy_item_detail_text("bean", bean, int(bean.get("price", 0)), 0)
|
||||||
if not bean_buy_detail.contains("병력 +20") or not bean_buy_detail.contains("보유 0점"):
|
if not bean_buy_detail.contains("병력 +20") or not bean_buy_detail.contains("보유 0점"):
|
||||||
@@ -3586,6 +3596,17 @@ func _check_ancient_ui_theme(failures: Array[String]) -> void:
|
|||||||
scene._update_briefing_camp_overview(scene.state.get_briefing(), false)
|
scene._update_briefing_camp_overview(scene.state.get_briefing(), false)
|
||||||
if scene.briefing_camp_overview_fallback_label == null or scene.briefing_camp_overview_fallback_label.text != "비단 전장도":
|
if scene.briefing_camp_overview_fallback_label == null or scene.briefing_camp_overview_fallback_label.text != "비단 전장도":
|
||||||
failures.append("briefing map fallback should read as an old campaign map")
|
failures.append("briefing map fallback should read as an old campaign map")
|
||||||
|
if scene.briefing_force_preview_overlay == null:
|
||||||
|
failures.append("briefing map thumbnail should include a force preview overlay")
|
||||||
|
else:
|
||||||
|
var ally_icons: Array = scene.briefing_force_preview_overlay.find_children("BriefingForcePreviewAllyIcon*", "", false, false)
|
||||||
|
var enemy_icons: Array = scene.briefing_force_preview_overlay.find_children("BriefingForcePreviewEnemyIcon*", "", false, false)
|
||||||
|
if ally_icons.size() < 2:
|
||||||
|
failures.append("briefing force preview should show allied portrait icons")
|
||||||
|
if enemy_icons.size() < 3:
|
||||||
|
failures.append("briefing force preview should show representative enemy unit icons")
|
||||||
|
if not _has_descendant_texture(scene.briefing_force_preview_overlay):
|
||||||
|
failures.append("briefing force preview should render generated portrait/unit textures")
|
||||||
if scene.briefing_tactical_marker_list == null:
|
if scene.briefing_tactical_marker_list == null:
|
||||||
failures.append("briefing should include a tactical marker card list")
|
failures.append("briefing should include a tactical marker card list")
|
||||||
elif scene.briefing_tactical_marker_list.get_child_count() < 3:
|
elif scene.briefing_tactical_marker_list.get_child_count() < 3:
|
||||||
|
|||||||
Reference in New Issue
Block a user