From 0337e0cc62195858bed8999cf105fe787164a598 Mon Sep 17 00:00:00 2001 From: Wickedness Date: Sat, 20 Jun 2026 06:44:29 +0900 Subject: [PATCH] Add generated header to post-move commands --- docs/ARCHITECTURE.md | 2 ++ scripts/scenes/battle_scene.gd | 59 +++++++++++++++++++++++++++++----- tools/smoke_visual_assets.gd | 13 ++++++++ 3 files changed, 66 insertions(+), 8 deletions(-) diff --git a/docs/ARCHITECTURE.md b/docs/ARCHITECTURE.md index 4af9172..29bf7f1 100644 --- a/docs/ARCHITECTURE.md +++ b/docs/ARCHITECTURE.md @@ -66,6 +66,8 @@ The battle scene owns presentation feedback: briefing and result states use menu Movement audio is keyed from `BattleState.unit_motion_requested` rather than movement log text, so future log cleanup does not mute board feedback. +Board-attached post-move command panels use generated command-seal frames, a seal-numbered header, and a generated move icon so the local action palette reads as a visual control surface instead of a text menu. + Large tactical maps are presented through a fixed visible map window rather than by shrinking the whole board, including the opening chapter-one battle. `BattleScene` keeps a clamped `board_scroll_offset`, scrolls the board while the mouse is near or slightly outside the visible map edge, brightens the scrollable frame edge under the cursor, ignores board clicks outside the visible map window, and draws a small minimap plate only when the board actually exceeds the visible map view; the minimap shows terrain, unit dots, objective seals, and side-event markers, and clicking or dragging it recenters the tactical viewport, while compact maps keep the lower-right tiles unobstructed. Board-attached command panels are repositioned after map scrolling so post-move choices stay beside the moved unit. Battle start focuses the board on the deployed player group, pre-battle Formation can focus the board on the selected unit even before the battle opens, and action feedback can focus the board on off-screen cells before motion or combat effects play. The `New Campaign` button clears `user://campaign_save.json`, resets campaign state to the first scenario, and reloads the opening briefing. diff --git a/scripts/scenes/battle_scene.gd b/scripts/scenes/battle_scene.gd index d1ead6a..5099ba8 100644 --- a/scripts/scenes/battle_scene.gd +++ b/scripts/scenes/battle_scene.gd @@ -187,8 +187,9 @@ const BATTLE_UNIT_LIST_BADGE_SIZE := Vector2(72, 22) const SETTINGS_PANEL_SIZE := Vector2(420, 330) const LOCAL_COMMAND_BUTTON_SIZE := Vector2(92, 38) const LOCAL_COMMAND_CANCEL_BUTTON_SIZE := Vector2(196, 30) -const POST_MOVE_MENU_SIZE := Vector2(240, 146) -const POST_MOVE_TITLE_SIZE := Vector2(196, 22) +const LOCAL_COMMAND_HEADER_ICON_SIZE := Vector2(24, 24) +const POST_MOVE_MENU_SIZE := Vector2(240, 176) +const POST_MOVE_TITLE_SIZE := Vector2(126, 24) const POST_MOVE_PICKER_PANEL_SIZE := Vector2(336, 236) const POST_MOVE_PICKER_TITLE_SIZE := Vector2(296, 22) const POST_MOVE_PICKER_DETAIL_SIZE := Vector2(296, 32) @@ -1463,6 +1464,53 @@ func _make_side_info_chip_icon(icon_kind: String) -> TextureRect: return icon +func _make_local_command_header(icon_kind: String, title: String, tooltip: String) -> PanelContainer: + var panel := PanelContainer.new() + panel.name = "LocalCommandHeader" + panel.custom_minimum_size = Vector2(206, 38) + panel.tooltip_text = tooltip + _apply_panel_style(panel, "caption") + var stylebox := panel.get_theme_stylebox("panel") + if stylebox != null: + stylebox.content_margin_left = 7 + stylebox.content_margin_right = 7 + stylebox.content_margin_top = 7 + stylebox.content_margin_bottom = 7 + + var row := HBoxContainer.new() + row.name = "LocalCommandHeaderRow" + row.custom_minimum_size = Vector2(186, 24) + row.add_theme_constant_override("separation", 6) + row.tooltip_text = tooltip + panel.add_child(row) + + var seal := _make_seal_tile("령", 24) + seal.name = "LocalCommandHeaderSeal" + seal.tooltip_text = tooltip + row.add_child(seal) + + var icon := TextureRect.new() + icon.name = "LocalCommandHeaderIcon" + icon.custom_minimum_size = LOCAL_COMMAND_HEADER_ICON_SIZE + icon.expand_mode = TextureRect.EXPAND_IGNORE_SIZE + icon.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED + icon.texture = _make_toolbar_icon(icon_kind) + icon.tooltip_text = tooltip + row.add_child(icon) + + post_move_title_label = Label.new() + post_move_title_label.name = "LocalCommandHeaderTitle" + post_move_title_label.text = title + post_move_title_label.custom_minimum_size = POST_MOVE_TITLE_SIZE + post_move_title_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT + post_move_title_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + post_move_title_label.tooltip_text = tooltip + _apply_label_style(post_move_title_label, UI_OLD_BRONZE, LOCAL_COMMAND_TITLE_FONT_SIZE) + row.add_child(post_move_title_label) + _set_control_tree_tooltip(panel, tooltip) + return panel + + func _set_top_hud_chip_tooltip(chip: PanelContainer, tooltip: String) -> void: if chip == null: return @@ -3037,12 +3085,7 @@ func _create_hud() -> void: post_move_menu.add_child(post_move_column) post_move_column.add_child(_make_seal_ribbon(196, 10)) - post_move_title_label = Label.new() - post_move_title_label.text = "군령 선택" - post_move_title_label.custom_minimum_size = POST_MOVE_TITLE_SIZE - post_move_title_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER - _apply_label_style(post_move_title_label, UI_OLD_BRONZE, LOCAL_COMMAND_TITLE_FONT_SIZE) - post_move_column.add_child(post_move_title_label) + post_move_column.add_child(_make_local_command_header("move", "군령 선택", "이동 후 수행할 군령을 고릅니다.")) var post_move_grid := GridContainer.new() post_move_grid.columns = 2 diff --git a/tools/smoke_visual_assets.gd b/tools/smoke_visual_assets.gd index b26b363..9bb95e7 100644 --- a/tools/smoke_visual_assets.gd +++ b/tools/smoke_visual_assets.gd @@ -3604,6 +3604,19 @@ func _check_ancient_ui_theme(failures: Array[String]) -> void: if scene.post_move_title_label == null or scene.post_move_title_label.text != "군령 선택": failures.append("post-move command title should use order wording") _check_panel_uses_generated_texture(failures, scene.post_move_menu, "post-move command generated frame") + var post_move_header: PanelContainer = null + var post_move_header_seal: PanelContainer = null + var post_move_header_icon: TextureRect = null + if scene.post_move_menu != null: + post_move_header = scene.post_move_menu.find_child("LocalCommandHeader", true, false) as PanelContainer + post_move_header_seal = scene.post_move_menu.find_child("LocalCommandHeaderSeal", true, false) as PanelContainer + post_move_header_icon = scene.post_move_menu.find_child("LocalCommandHeaderIcon", true, false) as TextureRect + _check_panel_uses_generated_texture(failures, post_move_header, "post-move command generated header") + _check_panel_uses_generated_texture(failures, post_move_header_seal, "post-move command generated header seal") + if post_move_header_icon == null or post_move_header_icon.texture == null: + failures.append("post-move command header should show a generated move icon") + elif post_move_header_icon.texture.get_width() < 128 or post_move_header_icon.texture.get_height() < 128: + failures.append("post-move command header should use high-resolution generated move icon art") _check_local_command_icon_button(failures, scene.post_move_attack_button, "타격령", "attack") _check_local_command_icon_button(failures, scene.post_move_tactic_button, "책략첩", "tactic") _check_local_command_icon_button(failures, scene.post_move_item_button, "보급첩", "item")