diff --git a/README.md b/README.md index cfc7593..b110c2a 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ Godot 4 tactical RPG prototype inspired by classic turn-based Romance of the Thr - Unit selection. - Move, attack, wait, end turn. - Automatic end-turn prompt appears after all controllable allies finish acting, using a styled command-complete confirmation. -- Ally, enemy, and objective phase changes raise compact command notices with generated visual icon chips in the active battle HUD. +- Ally, enemy, objective phase changes, and battle-start tactical reminders raise compact command notices with generated visual icon chips, short marker summaries, and hover details in the active battle HUD. - Enemy AI with movement, damage-aware physical attacks, multiple MP tactic choices, scenario target priorities, and sentry-awake state that events can retune mid-battle. - Hover tile and unit info with class, movement type, AGI, movement, range, and equipped gear. - Active battle unit list opens from the top toolbar with ally/enemy tabs, portrait or sprite rows, compact HP/status/zone badges, and hover combat details. diff --git a/scripts/scenes/battle_scene.gd b/scripts/scenes/battle_scene.gd index a49f39c..0c9640a 100644 --- a/scripts/scenes/battle_scene.gd +++ b/scripts/scenes/battle_scene.gd @@ -11344,9 +11344,10 @@ func _format_battle_start_marker_summary() -> String: if label.is_empty(): continue var kind := str(marker.get("kind", "event")).strip_edges().to_lower() + var hint := str(marker.get("hint", "")).strip_edges() if not _battle_start_marker_is_tactical(label, kind): continue - var text := _battle_start_marker_text(label, kind) + var text := _battle_start_marker_text(label, kind, hint) if text.is_empty() or seen.has(text): continue seen[text] = true @@ -11362,12 +11363,16 @@ func _battle_start_marker_is_tactical(label: String, kind: String) -> bool: return label.contains("유인") or label.contains("보급") or label.contains("마을") or label.contains("숲") -func _battle_start_marker_text(label: String, kind: String) -> String: +func _battle_start_marker_text(label: String, kind: String, hint: String = "") -> String: + var normalized_label := label if kind == "tactic" and not label.contains("유인"): - return "유인 %s" % label - if kind == "supply" and not label.contains("보급"): - return "보급 %s" % label - return label + normalized_label = "유인 %s" % label + elif kind == "supply" and not label.contains("보급"): + normalized_label = "보급 %s" % label + var summary := _briefing_marker_summary_from_hint(hint) + if summary.is_empty(): + return normalized_label + return "%s-%s" % [normalized_label, summary] func _show_command_notice(title: String, body: String) -> void: @@ -11545,11 +11550,17 @@ func _command_notice_icon_entry_tooltip(entry: Dictionary, title: String, body: "status": detail = _first_notice_body_line(body, ["주의", "퇴각", "위험", "패"]) "tactic": - detail = _first_notice_body_line(body, ["유인", "표식"]) + detail = _battle_start_marker_tooltip_detail_for_key(key) + if detail.is_empty(): + detail = _first_notice_body_line(body, ["유인", "표식"]) "supply": - detail = _first_notice_body_line(body, ["보급", "마을"]) + detail = _battle_start_marker_tooltip_detail_for_key(key) + if detail.is_empty(): + detail = _first_notice_body_line(body, ["보급", "마을"]) "recover": - detail = _first_notice_body_line(body, ["회복", "마을"]) + detail = _battle_start_marker_tooltip_detail_for_key(key) + if detail.is_empty(): + detail = _first_notice_body_line(body, ["회복", "마을"]) "gold": detail = _first_notice_body_line(body, ["군자금"]) "threat", "forecast": @@ -11563,6 +11574,37 @@ func _command_notice_icon_entry_tooltip(entry: Dictionary, title: String, body: return "%s\n%s" % [label_text, _short_preview_text(detail, 110)] +func _battle_start_marker_tooltip_detail_for_key(key: String) -> String: + var parts: Array[String] = [] + var seen := {} + for marker in state.get_event_markers(): + var label := str(marker.get("label", "")).strip_edges() + var kind := str(marker.get("kind", "event")).strip_edges().to_lower() + var hint := str(marker.get("hint", "")).strip_edges() + if label.is_empty() or hint.is_empty(): + continue + if not _battle_start_marker_matches_notice_key(label, kind, hint, key): + continue + var text := "%s: %s" % [label, hint] + if seen.has(text): + continue + seen[text] = true + parts.append(text) + return _join_strings(parts, "\n") + + +func _battle_start_marker_matches_notice_key(label: String, kind: String, hint: String, key: String) -> bool: + match key: + "tactic": + return kind == "tactic" or label.contains("유인") or hint.contains("유인") or hint.contains("끌어낼") + "supply": + return kind == "supply" or label.contains("보급") or label.contains("숲") or hint.contains("보급") or hint.contains("콩") + "recover": + return kind == "recover" or label.contains("마을") or hint.contains("회복") or hint.contains("치료소") + _: + return false + + func _first_notice_body_line(body: String, keywords: Array[String]) -> String: for line in body.split("\n", false): var normalized_line := str(line).strip_edges() diff --git a/tools/smoke_chapter_one_polish.gd b/tools/smoke_chapter_one_polish.gd index a2612e6..0344c85 100644 --- a/tools/smoke_chapter_one_polish.gd +++ b/tools/smoke_chapter_one_polish.gd @@ -577,6 +577,8 @@ func _check_battle_start_notice_sequence(failures: Array[String]) -> void: failures.append("battle start notice should summarize objective and risk: %s" % start_notice_text) elif not start_notice_text.contains("표식:") or not start_notice_text.contains("유인선") or not start_notice_text.contains("북숲 보급고") or not start_notice_text.contains("마을 보급"): failures.append("battle start notice should summarize tactical event markers without making the player hunt for them: %s" % start_notice_text) + elif not start_notice_text.contains("앞줄 유인") or not start_notice_text.contains("숨은 콩") or not start_notice_text.contains("회복 보급"): + failures.append("battle start notice should carry compact tactical marker hints: %s" % start_notice_text) elif start_notice_text.contains("(20") or start_notice_text.contains("(21") or start_notice_text.contains(",1"): failures.append("battle start notice should not expose raw grid coordinates: %s" % start_notice_text) if scene.objective_notice_icon_strip == null or scene.objective_notice_icon_strip.get_child_count() < 3: @@ -585,6 +587,9 @@ func _check_battle_start_notice_sequence(failures: Array[String]) -> void: var notice_icon_tooltips := _collect_tooltips(scene.objective_notice_icon_strip) if not notice_icon_tooltips.contains("목표:") or not notice_icon_tooltips.contains("주의:") or not notice_icon_tooltips.contains("유인선") or not notice_icon_tooltips.contains("북숲 보급고") or not notice_icon_tooltips.contains("마을 보급"): failures.append("battle start notice icon hover should explain objective, risk, and tactical markers: %s" % notice_icon_tooltips) + for expected_hint in ["끌어낼", "콩 보급", "치료소"]: + if not notice_icon_tooltips.contains(expected_hint): + failures.append("battle start notice icon hover should preserve marker hint `%s`: %s" % [expected_hint, notice_icon_tooltips]) if _count_descendants_by_name(scene.objective_notice_icon_strip, "CommandNoticeTileMarkerImage") < 3: failures.append("battle start marker notice chips should layer generated map marker art behind objective/tactic/supply icons") scene.free()