From 4c793194a9388a735061cbc9805e2f38afeeb284 Mon Sep 17 00:00:00 2001 From: Wickedness Date: Sat, 20 Jun 2026 16:17:26 +0900 Subject: [PATCH] Make briefing markers focus the map --- README.md | 2 +- scripts/scenes/battle_scene.gd | 45 +++++++++++++++++++++++++++- tools/smoke_chapter_one_polish.gd | 49 +++++++++++++++++++++++++++++++ 3 files changed, 94 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ac21bc4..18c0c46 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,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 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 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 with compact tactical summaries plus full hover hints. +- 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 with compact tactical summaries, full hover hints, and click-to-focus map guidance. - 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. - Post-move command boards, tactic pickers, and item pickers use generated command/item icons, compact option rows, and richer hover guidance for move-attack, stratagem, supply, wait, and cancel decisions instead of dense inline text. - Target preview badges, minimap hover badges, and battlefield target markers include generated jade panel surfaces plus action icons for move, attack, threat, support, objective, terrain, and item outcomes. diff --git a/scripts/scenes/battle_scene.gd b/scripts/scenes/battle_scene.gd index 56e22c8..05d8b03 100644 --- a/scripts/scenes/battle_scene.gd +++ b/scripts/scenes/battle_scene.gd @@ -12807,13 +12807,15 @@ func _briefing_tactical_marker_entries() -> Array[Dictionary]: var marker_cells: Array = cells cell_count = marker_cells.size() var badge_kind := _event_marker_badge_kind(label, kind) + var focus_cell := _representative_focus_cell(cells) result.append({ "label": label, "kind": kind, "badge": badge_kind, "tile": _event_marker_tile_marker_key(label, kind), "summary": _briefing_tactical_marker_summary(label, badge_kind, cell_count, hint), - "hint": hint + "hint": hint, + "focus_cell": focus_cell }) if result.size() >= BRIEFING_TACTICAL_MARKER_MAX_COUNT: break @@ -12826,8 +12828,18 @@ func _make_briefing_tactical_marker_chip(entry: Dictionary) -> PanelContainer: var hint_text := str(entry.get("hint", "")).strip_edges() var tooltip_detail := hint_text if not hint_text.is_empty() else summary_text var tooltip := "%s\n%s" % [label_text, tooltip_detail] + var focus_cell: Vector2i = entry.get("focus_cell", Vector2i(-1, -1)) + if state.is_inside(focus_cell): + tooltip += "\n클릭하면 전장도에서 해당 표식을 봅니다." var chip := PanelContainer.new() chip.custom_minimum_size = BRIEFING_TACTICAL_MARKER_CHIP_SIZE + chip.mouse_filter = Control.MOUSE_FILTER_STOP + chip.set_meta("briefing_marker_label", label_text) + chip.set_meta("briefing_marker_kind", str(entry.get("kind", ""))) + if state.is_inside(focus_cell): + chip.set_meta("focus_cell", focus_cell) + chip.mouse_default_cursor_shape = Control.CURSOR_POINTING_HAND + chip.gui_input.connect(_on_briefing_tactical_marker_gui_input.bind(chip)) _apply_panel_style(chip, "caption") var chip_style := chip.get_theme_stylebox("panel") if chip_style != null: @@ -12839,6 +12851,7 @@ func _make_briefing_tactical_marker_chip(entry: Dictionary) -> PanelContainer: var row := HBoxContainer.new() row.add_theme_constant_override("separation", 5) row.tooltip_text = tooltip + row.mouse_filter = Control.MOUSE_FILTER_IGNORE chip.add_child(row) row.add_child(_make_briefing_tactical_marker_icon(entry, tooltip)) @@ -12848,6 +12861,7 @@ func _make_briefing_tactical_marker_chip(entry: Dictionary) -> PanelContainer: text_column.size_flags_horizontal = Control.SIZE_EXPAND_FILL text_column.add_theme_constant_override("separation", -2) text_column.tooltip_text = tooltip + text_column.mouse_filter = Control.MOUSE_FILTER_IGNORE row.add_child(text_column) var title_label := Label.new() @@ -12855,6 +12869,7 @@ func _make_briefing_tactical_marker_chip(entry: Dictionary) -> PanelContainer: title_label.clip_text = true title_label.text = label_text title_label.tooltip_text = tooltip + title_label.mouse_filter = Control.MOUSE_FILTER_IGNORE _apply_label_style(title_label, UI_PARCHMENT_TEXT, 12) text_column.add_child(title_label) @@ -12863,6 +12878,7 @@ func _make_briefing_tactical_marker_chip(entry: Dictionary) -> PanelContainer: detail_label.clip_text = true detail_label.text = summary_text detail_label.tooltip_text = tooltip + detail_label.mouse_filter = Control.MOUSE_FILTER_IGNORE _apply_label_style(detail_label, UI_OLD_BRONZE, 9) text_column.add_child(detail_label) @@ -12870,10 +12886,35 @@ func _make_briefing_tactical_marker_chip(entry: Dictionary) -> PanelContainer: return chip +func _on_briefing_tactical_marker_gui_input(event: InputEvent, chip: Control) -> void: + if not (event is InputEventMouseButton): + return + var mouse_event := event as InputEventMouseButton + if mouse_event.button_index != MOUSE_BUTTON_LEFT or not mouse_event.pressed: + return + if _focus_board_on_briefing_marker_chip(chip): + _play_ui_click() + get_viewport().set_input_as_handled() + + +func _focus_board_on_briefing_marker_chip(chip: Control) -> bool: + if chip == null or battle_started: + return false + var focus_cell: Vector2i = chip.get_meta("focus_cell", Vector2i(-1, -1)) + if not state.is_inside(focus_cell): + return false + hover_cell = focus_cell + _focus_board_on_cell(focus_cell, true, true) + _update_hud() + queue_redraw() + return true + + func _make_briefing_tactical_marker_icon(entry: Dictionary, tooltip: String) -> Control: var icon_stack := Control.new() icon_stack.custom_minimum_size = BRIEFING_TACTICAL_MARKER_ICON_SIZE icon_stack.tooltip_text = tooltip + icon_stack.mouse_filter = Control.MOUSE_FILTER_IGNORE var tile_texture := _load_tile_marker_texture(str(entry.get("tile", "select"))) if tile_texture != null: var tile_rect := TextureRect.new() @@ -12881,6 +12922,7 @@ func _make_briefing_tactical_marker_icon(entry: Dictionary, tooltip: String) -> tile_rect.expand_mode = TextureRect.EXPAND_IGNORE_SIZE tile_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED tile_rect.modulate = Color(1.0, 1.0, 1.0, 0.42) + tile_rect.mouse_filter = Control.MOUSE_FILTER_IGNORE tile_rect.texture = tile_texture tile_rect.tooltip_text = tooltip icon_stack.add_child(tile_rect) @@ -12890,6 +12932,7 @@ func _make_briefing_tactical_marker_icon(entry: Dictionary, tooltip: String) -> badge_rect.set_anchors_preset(Control.PRESET_FULL_RECT) badge_rect.expand_mode = TextureRect.EXPAND_IGNORE_SIZE badge_rect.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED + badge_rect.mouse_filter = Control.MOUSE_FILTER_IGNORE badge_rect.texture = badge_texture badge_rect.tooltip_text = tooltip icon_stack.add_child(badge_rect) diff --git a/tools/smoke_chapter_one_polish.gd b/tools/smoke_chapter_one_polish.gd index 647bcec..4adb974 100644 --- a/tools/smoke_chapter_one_polish.gd +++ b/tools/smoke_chapter_one_polish.gd @@ -32,6 +32,7 @@ func _init() -> void: _check_opening_battle_tactical_spacing(failures) _check_opening_battle_log_location_labels(failures) _check_opening_battle_marker_hints(failures) + _check_opening_battle_briefing_marker_focus(failures) _check_opening_battle_lure_line_ai_reaction(failures) _check_opening_battle_post_battle_bridge(failures) _check_sishui_gate_camp_context(failures) @@ -923,6 +924,42 @@ func _check_opening_battle_marker_hints(failures: Array[String]) -> void: scene.free() +func _check_opening_battle_briefing_marker_focus(failures: Array[String]) -> void: + var scene = BattleSceneScript.new() + scene._ready() + scene._load_scenario("001_yellow_turbans") + scene._show_briefing() + if scene.briefing_tactical_marker_list == null or scene.briefing_tactical_marker_list.get_child_count() < 3: + failures.append("opening briefing should expose tactical marker chips before battle") + scene.free() + return + for expected_label in ["유인선", "북숲 보급고", "마을 보급"]: + var chip := _find_briefing_marker_chip_by_label(scene.briefing_tactical_marker_list, expected_label) + if chip == null: + failures.append("opening briefing marker chip missing: %s" % expected_label) + continue + var focus_cell: Vector2i = chip.get_meta("focus_cell", Vector2i(-1, -1)) + if not scene.state.is_inside(focus_cell): + failures.append("opening briefing marker chip should carry focus cell for %s: %s" % [expected_label, str(focus_cell)]) + if not chip.tooltip_text.contains("클릭하면 전장도"): + failures.append("opening briefing marker chip should explain clickable map focus for %s: %s" % [expected_label, chip.tooltip_text]) + if chip.mouse_filter == Control.MOUSE_FILTER_IGNORE: + failures.append("opening briefing marker chip should receive pointer input for %s" % expected_label) + var village_chip := _find_briefing_marker_chip_by_label(scene.briefing_tactical_marker_list, "마을 보급") + if village_chip != null: + var village_focus: Vector2i = village_chip.get_meta("focus_cell", Vector2i(-1, -1)) + var previous_offset: Vector2 = scene.board_scroll_offset + if not scene._focus_board_on_briefing_marker_chip(village_chip): + failures.append("clicking briefing marker chip should focus the battlefield marker") + elif not scene._cell_is_in_visible_bounds(village_focus, 0): + failures.append("briefing marker focus should bring the marker into the map view") + elif scene.board_scroll_offset.distance_squared_to(previous_offset) <= 0.01: + failures.append("briefing marker chip should move the large map toward the selected marker") + elif scene.hover_cell != village_focus: + failures.append("briefing marker chip should update hover focus cell") + scene.free() + + func _check_opening_battle_lure_line_ai_reaction(failures: Array[String]) -> void: var state = BattleStateScript.new() if not state.load_battle("res://data/scenarios/001_yellow_turbans.json"): @@ -2088,6 +2125,18 @@ func _find_command_notice_chip_by_key(root: Node, key: String) -> Control: return null +func _find_briefing_marker_chip_by_label(root: Node, label: String) -> Control: + if root == null: + return null + if root is Control and str(root.get_meta("briefing_marker_label", "")) == label: + return root as Control + for child in root.get_children(): + var found := _find_briefing_marker_chip_by_label(child, label) + if found != null: + return found + return null + + func _collect_tooltips_into(root: Node, parts: Array[String]) -> void: if root == null: return