From 45b8c3963889b04ef1d8f6231f995dd343181fe9 Mon Sep 17 00:00:00 2001 From: Wickedness Date: Sun, 21 Jun 2026 05:30:22 +0900 Subject: [PATCH] Add enemy inspection detail from overview --- scripts/scenes/battle_scene.gd | 71 +++++++++++++++++++++++++---- tools/capture_readability_frames.gd | 45 +++++++++++++++--- tools/smoke_visual_assets.gd | 19 ++++++++ 3 files changed, 120 insertions(+), 15 deletions(-) diff --git a/scripts/scenes/battle_scene.gd b/scripts/scenes/battle_scene.gd index 92573c4..a0bb94a 100644 --- a/scripts/scenes/battle_scene.gd +++ b/scripts/scenes/battle_scene.gd @@ -512,6 +512,7 @@ var side_unit_overview_enemy_button: Button var side_unit_overview_status_label: Label var side_unit_overview_rows: VBoxContainer var side_unit_overview_mode := "ally" +var side_inspected_unit_id := "" var cell_info_panel: PanelContainer var cell_info_icon: TextureRect var cell_info_label: Label @@ -5981,12 +5982,17 @@ func _handle_board_click(screen_position: Vector2) -> void: if selected_unit.is_empty(): if _can_select(clicked_unit): + _clear_side_inspected_unit(false) selected_skill_id = "" selected_item_id = "" basic_attack_targeting = false _clear_pending_move_state() if state.select_unit(clicked_unit["id"]): _play_ui_click() + elif not clicked_unit.is_empty(): + _inspect_side_unit(str(clicked_unit.get("id", ""))) + elif not side_inspected_unit_id.is_empty(): + _clear_side_inspected_unit() return if not selected_item_id.is_empty(): @@ -6162,6 +6168,9 @@ func _handle_cancel_input() -> bool: _update_hud() queue_redraw() return true + if not side_inspected_unit_id.is_empty(): + _clear_side_inspected_unit() + return true if not selected_skill_id.is_empty() or not selected_item_id.is_empty() or basic_attack_targeting: _play_ui_cancel() selected_skill_id = "" @@ -9207,6 +9216,8 @@ func _target_preview_detail_badge_rect(cell: Vector2i, tooltip: String, anchor_r func _hover_info_badge() -> Dictionary: if _is_input_locked() or not state.is_inside(hover_cell): return {} + if not side_inspected_unit_id.is_empty(): + return {} if (post_move_menu != null and post_move_menu.visible) or (post_move_picker_panel != null and post_move_picker_panel.visible): return {} if not _target_preview_badge().is_empty(): @@ -10524,19 +10535,21 @@ func _update_hud() -> void: _update_hud_unit_badges({}, selected_label.tooltip_text) _update_hud_unit_resource_bars({}, selected_label.tooltip_text) else: - var hovered_unit := _hovered_unit_for_hud_portrait() - _update_hud_unit_portrait(hovered_unit) - if hovered_unit.is_empty(): + var inspected_unit := _side_inspected_unit() + var focus_unit := inspected_unit if not inspected_unit.is_empty() else _hovered_unit_for_hud_portrait() + _update_hud_unit_portrait(focus_unit) + if focus_unit.is_empty(): selected_label.text = _format_empty_hud_focus_summary_text() selected_label.tooltip_text = _format_empty_hud_focus_detail_text() _update_hud_unit_badges({}, selected_label.tooltip_text) _update_hud_unit_resource_bars({}, selected_label.tooltip_text) else: - var hover_detail := _format_unit_focus_text(hovered_unit, "Hover") - selected_label.text = _format_unit_focus_summary_text(hovered_unit, "Hover") - selected_label.tooltip_text = hover_detail - _update_hud_unit_badges(hovered_unit, hover_detail) - _update_hud_unit_resource_bars(hovered_unit, hover_detail) + var focus_prefix := "Inspect" if not inspected_unit.is_empty() else "Hover" + var focus_detail := _format_unit_focus_text(focus_unit, focus_prefix) + selected_label.text = _format_unit_focus_summary_text(focus_unit, focus_prefix) + selected_label.tooltip_text = focus_detail + _update_hud_unit_badges(focus_unit, focus_detail) + _update_hud_unit_resource_bars(focus_unit, focus_detail) _update_wait_button({}) _update_tactic_button({}) _hide_tactic_menu() @@ -10580,6 +10593,7 @@ func _update_hud() -> void: func _should_show_side_unit_overview(selected: Dictionary) -> bool: return ( selected.is_empty() + and _side_inspected_unit().is_empty() and battle_started and not campaign_complete_screen and state.battle_status == BattleState.STATUS_ACTIVE @@ -10674,6 +10688,39 @@ func _format_side_unit_overview_status_tooltip(units: Array[Dictionary]) -> Stri return "아군 %d부대\n명령할 장수가 없을 때 이곳에서 전장 상황을 훑어봅니다.\n아군 행을 누르면 장수를 선택합니다." % units.size() +func _side_inspected_unit() -> Dictionary: + if side_inspected_unit_id.is_empty(): + return {} + var unit := state.get_unit(side_inspected_unit_id) + if unit.is_empty() or not bool(unit.get("alive", true)): + return {} + return unit + + +func _inspect_side_unit(unit_id: String) -> void: + var unit := state.get_unit(unit_id) + if unit.is_empty() or not bool(unit.get("alive", true)): + return + side_inspected_unit_id = unit_id + var unit_cell: Vector2i = unit.get("pos", Vector2i(-1, -1)) + if state.is_inside(unit_cell): + hover_cell = unit_cell + _focus_board_on_cell(unit_cell, true, true) + _play_ui_click() + _update_hud() + queue_redraw() + + +func _clear_side_inspected_unit(play_sound := true) -> void: + if side_inspected_unit_id.is_empty(): + return + side_inspected_unit_id = "" + if play_sound: + _play_ui_cancel() + _update_hud() + queue_redraw() + + func _make_side_unit_overview_row(unit: Dictionary) -> HBoxContainer: var unit_id := str(unit.get("id", "")) var tooltip_text := _format_battle_unit_list_row_tooltip(unit) @@ -10764,8 +10811,10 @@ func _on_side_unit_overview_row_pressed(unit_id: String) -> void: return var unit_cell: Vector2i = unit.get("pos", Vector2i(-1, -1)) if state.is_inside(unit_cell): + hover_cell = unit_cell _focus_board_on_cell(unit_cell, true, true) if str(unit.get("team", "")) == BattleState.TEAM_PLAYER and _can_select(unit) and _can_select_from_battle_unit_list(): + _clear_side_inspected_unit(false) selected_skill_id = "" selected_item_id = "" basic_attack_targeting = false @@ -10777,7 +10826,8 @@ func _on_side_unit_overview_row_pressed(unit_id: String) -> void: _refresh_ranges() _play_ui_confirm() else: - _play_ui_click() + _inspect_side_unit(unit_id) + return _update_hud() queue_redraw() @@ -11078,6 +11128,8 @@ func _unit_focus_prefix_text(prefix: String) -> String: return "군기" if prefix == "Hover": return "관측" + if prefix == "Inspect": + return "정찰" return prefix @@ -12310,6 +12362,7 @@ func _on_state_changed() -> void: selected_item_id = "" _clear_pending_move_state() else: + side_inspected_unit_id = "" var selected := state.get_selected_unit() if not selected.is_empty(): _focus_board_on_cell(selected.get("pos", Vector2i(-1, -1))) diff --git a/tools/capture_readability_frames.gd b/tools/capture_readability_frames.gd index 20fd01c..fe7b437 100644 --- a/tools/capture_readability_frames.gd +++ b/tools/capture_readability_frames.gd @@ -102,30 +102,34 @@ func _capture_viewport_set(spec: Dictionary, output_dir: String, captures: Array await _settle() await _capture_frame(scene, output_dir, label, "07_battle_idle", captures, failures) + _prepare_enemy_inspection_frame(scene) + await _settle() + await _capture_frame(scene, output_dir, label, "08_battle_enemy_inspection", captures, failures) + _prepare_auto_attack_hint_frame(scene) await _settle() - await _capture_frame(scene, output_dir, label, "08_battle_auto_attack_hint", captures, failures) + await _capture_frame(scene, output_dir, label, "09_battle_auto_attack_hint", captures, failures) scene._update_threat_button() scene._on_command_hint_button_mouse_entered(scene.threat_button) await _settle() - await _capture_frame(scene, output_dir, label, "09_battle_threat_hint", captures, failures) + await _capture_frame(scene, output_dir, label, "10_battle_threat_hint", captures, failures) _prepare_post_move_action_frame(scene, "") await _settle() - await _capture_frame(scene, output_dir, label, "10_post_move_actions", captures, failures) + await _capture_frame(scene, output_dir, label, "11_post_move_actions", captures, failures) _prepare_post_move_action_frame(scene, "tactic") await _settle() - await _capture_frame(scene, output_dir, label, "11_post_move_tactic_picker", captures, failures) + await _capture_frame(scene, output_dir, label, "12_post_move_tactic_picker", captures, failures) _prepare_post_move_action_frame(scene, "item") await _settle() - await _capture_frame(scene, output_dir, label, "12_post_move_item_picker", captures, failures) + await _capture_frame(scene, output_dir, label, "13_post_move_item_picker", captures, failures) _prepare_combat_result_frame(scene) await _settle() - await _capture_frame(scene, output_dir, label, "13_battle_combat_result", captures, failures) + await _capture_frame(scene, output_dir, label, "14_battle_combat_result", captures, failures) scene.queue_free() await _settle() @@ -159,6 +163,35 @@ func _prepare_auto_attack_hint_frame(scene) -> void: scene.queue_redraw() +func _prepare_enemy_inspection_frame(scene) -> void: + scene._hide_command_hint() + scene._hide_post_move_menu() + scene._hide_post_move_picker() + scene._hide_tactic_menu() + scene._hide_item_menu() + scene._hide_equip_menu() + scene._clear_pending_move_state() + scene.selected_skill_id = "" + scene.selected_item_id = "" + scene.basic_attack_targeting = false + scene.state.clear_selection() + scene.side_unit_overview_mode = "enemy" + scene.side_inspected_unit_id = "" + var enemy: Dictionary = scene.state.get_unit("yellow_turban_1") + if enemy.is_empty(): + return + enemy["alive"] = true + var enemy_cell: Vector2i = enemy.get("pos", Vector2i(-1, -1)) + if not scene.state.is_inside(enemy_cell): + enemy_cell = Vector2i(10, 4) + enemy["pos"] = enemy_cell + scene.hover_cell = enemy_cell + scene._focus_board_on_cell(enemy_cell, true, true) + scene._on_side_unit_overview_row_pressed("yellow_turban_1") + scene._update_hud() + scene.queue_redraw() + + func _prepare_post_move_action_frame(scene, picker_mode: String) -> void: scene._hide_command_hint() scene._hide_post_move_menu() diff --git a/tools/smoke_visual_assets.gd b/tools/smoke_visual_assets.gd index 497eab4..99bf936 100644 --- a/tools/smoke_visual_assets.gd +++ b/tools/smoke_visual_assets.gd @@ -3625,6 +3625,25 @@ func _check_hud_focus_text(failures: Array[String]) -> void: failures.append("enemy overview row click should focus without selecting enemy units: %s" % scene.state.selected_unit_id) if not scene.move_cells.is_empty(): failures.append("enemy overview row click should not leave player movement ranges visible") + if scene.side_inspected_unit_id != "yellow_turban_1": + failures.append("enemy overview row click should open an inspection card: %s" % scene.side_inspected_unit_id) + if scene.side_unit_overview_panel == null or scene.side_unit_overview_panel.visible: + failures.append("enemy inspection should replace the overview list with a unit detail card") + if scene.hud_unit_focus_row == null or not scene.hud_unit_focus_row.visible: + failures.append("enemy inspection should show the focused unit detail card") + if scene.selected_label == null or not scene.selected_label.text.contains("정찰:"): + failures.append("enemy inspection card should be labeled as scout detail: %s" % ("" if scene.selected_label == null else scene.selected_label.text)) + if scene.hud_command_grid != null and scene.hud_command_grid.visible: + failures.append("enemy inspection should not expose player command buttons") + if not scene._hover_info_badge().is_empty(): + failures.append("enemy inspection should keep duplicate map hover badges hidden") + scene._handle_cancel_input() + if scene.side_inspected_unit_id != "": + failures.append("cancel should close enemy inspection: %s" % scene.side_inspected_unit_id) + if scene.side_unit_overview_panel == null or not scene.side_unit_overview_panel.visible: + failures.append("canceling enemy inspection should return to the overview list") + if scene.side_unit_overview_enemy_button == null or not scene.side_unit_overview_enemy_button.button_pressed: + failures.append("canceling enemy inspection should preserve the enemy overview tab") scene._on_side_unit_overview_tab_pressed("ally") if scene.inventory_label == null or scene.inventory_label.text != "군수 대기": failures.append("empty battle inventory line should not show noisy stock text: %s" % ("" if scene.inventory_label == null else scene.inventory_label.text))