Preview enemy reactions on move cells

This commit is contained in:
2026-06-20 13:01:14 +09:00
parent 48160decbb
commit a039ca6037
2 changed files with 76 additions and 2 deletions

View File

@@ -6542,6 +6542,14 @@ func _ai_zone_cells_for_unit(unit: Dictionary) -> Array[Vector2i]:
return cells
func _ai_zone_contains_cell(unit: Dictionary, cell: Vector2i) -> bool:
var radius := _ai_zone_radius_for_unit(unit)
var anchor := _ai_zone_anchor_for_unit(unit)
if radius < 0 or not state.is_inside(anchor) or not state.is_inside(cell):
return false
return absi(cell.x - anchor.x) + absi(cell.y - anchor.y) <= radius
func _ai_zone_anchor_for_unit(unit: Dictionary) -> Vector2i:
var behavior := str(unit.get("ai_behavior", "")).strip_edges().to_lower()
var fallback: Vector2i = unit.get("pos", Vector2i(-1, -1))
@@ -8618,10 +8626,49 @@ func _hover_intent_preview_badge(selected: Dictionary, target: Dictionary) -> Di
return _make_target_preview_badge("선택", "select")
return {}
if move_cells.has(hover_cell):
return _make_target_preview_badge("행군", "move")
return _move_cell_preview_badge(hover_cell)
return {}
func _move_cell_preview_badge(cell: Vector2i) -> Dictionary:
var sentry_names := _ai_reaction_unit_names_for_cell(cell, "sentry")
if not sentry_names.is_empty():
var lure_badge := _make_target_preview_badge_at("유인", "advance", cell)
lure_badge["tooltip"] = "초병 반응권: %s" % _compact_name_list(sentry_names, 2)
return lure_badge
var guard_names := _ai_reaction_unit_names_for_cell(cell, "guard")
if not guard_names.is_empty():
var guard_badge := _make_target_preview_badge_at("경계", "select", cell)
guard_badge["tooltip"] = "거점 경계권: %s" % _compact_name_list(guard_names, 2)
return guard_badge
var threat_names := state.get_threatening_unit_names(cell, BattleState.TEAM_ENEMY)
if not threat_names.is_empty():
var danger_badge := _make_target_preview_badge_at("위험", "danger", cell)
danger_badge["tooltip"] = "현재 적 공격권: %s" % _compact_name_list(threat_names, 2)
return danger_badge
return _make_target_preview_badge_at("행군", "move", cell)
func _ai_reaction_unit_names_for_cell(cell: Vector2i, behavior: String) -> Array[String]:
var names: Array[String] = []
var normalized_behavior := behavior.strip_edges().to_lower()
if normalized_behavior.is_empty() or not state.is_inside(cell):
return names
for unit in state.get_living_units(BattleState.TEAM_ENEMY):
if not bool(unit.get("deployed", true)):
continue
if str(unit.get("ai_behavior", "")).strip_edges().to_lower() != normalized_behavior:
continue
if normalized_behavior == "sentry" and bool(unit.get("ai_awake", false)):
continue
if not _ai_zone_contains_cell(unit, cell):
continue
var unit_name := str(unit.get("name", unit.get("id", "부대")))
if not names.has(unit_name):
names.append(unit_name)
return names
func _make_target_preview_badge(text: String, kind: String) -> Dictionary:
return _make_target_preview_badge_at(text, kind, hover_cell)

View File

@@ -4743,10 +4743,14 @@ func _check_hover_intent_badges(failures: Array[String]) -> void:
return
scene.battle_started = true
var enemy: Dictionary = scene.state.get_unit("yellow_turban_1")
enemy["pos"] = Vector2i(1, 5)
scene.state.select_unit("cao_cao")
scene._refresh_ranges()
var enemy_alive_states := {}
for foe in scene.state.get_living_units(BattleStateScript.TEAM_ENEMY):
enemy_alive_states[str(foe.get("id", ""))] = bool(foe.get("alive", false))
foe["alive"] = false
scene._refresh_ranges()
scene.hover_cell = Vector2i(2, 5)
var move_badge := scene._target_preview_badge()
if str(move_badge.get("text", "")) != "행군" or str(move_badge.get("kind", "")) != "move":
@@ -4757,6 +4761,19 @@ func _check_hover_intent_badges(failures: Array[String]) -> void:
failures.append("MOVE badge should use generated jade panel metadata: %s" % str(move_badge))
if not scene._hover_info_badge().is_empty():
failures.append("hover info badge should yield to active move intent badge")
for unit_id in enemy_alive_states.keys():
var restored_enemy: Dictionary = scene.state.get_unit(str(unit_id))
if not restored_enemy.is_empty():
restored_enemy["alive"] = bool(enemy_alive_states[unit_id])
scene._refresh_ranges()
enemy["pos"] = Vector2i(1, 5)
scene._refresh_ranges()
scene.hover_cell = Vector2i(2, 5)
var danger_move_badge := scene._target_preview_badge()
if str(danger_move_badge.get("text", "")) != "위험" or str(danger_move_badge.get("kind", "")) != "danger":
failures.append("moving into current enemy attack range should show DANGER badge: %s" % str(danger_move_badge))
if not str(danger_move_badge.get("tooltip", "")).contains("현재 적 공격권"):
failures.append("danger move badge should explain current enemy attack range: %s" % str(danger_move_badge))
scene.hover_cell = Vector2i(1, 7)
var select_badge := scene._target_preview_badge()
@@ -4830,6 +4847,11 @@ func _check_hover_intent_badges(failures: Array[String]) -> void:
failures.append("sleeping sentry hover badge should expose lure timing compactly: %s" % str(sentry_hover_badge))
if not sentry_hover_text.contains("유인 밖 4칸"):
failures.append("sleeping sentry hover badge should expose current lure distance: %s" % str(sentry_hover_badge))
var lure_move_badge := scene._move_cell_preview_badge(Vector2i(5, 6))
if str(lure_move_badge.get("text", "")) != "유인" or str(lure_move_badge.get("kind", "")) != "advance":
failures.append("move preview should mark cells that wake sleeping sentries: %s" % str(lure_move_badge))
if not str(lure_move_badge.get("tooltip", "")).contains("초병 반응권"):
failures.append("lure move badge should explain sentry reaction zone: %s" % str(lure_move_badge))
central_sentry["ai_awake"] = true
var awake_sentry_hover_badge := scene._hover_info_badge()
if not str(awake_sentry_hover_badge.get("lines", [])).contains("초병 경계"):
@@ -4856,6 +4878,11 @@ func _check_hover_intent_badges(failures: Array[String]) -> void:
var guard_hover_text := str(guard_hover_badge.get("lines", []))
if not guard_hover_text.contains("수비") or not guard_hover_text.contains("거점 밖 4칸"):
failures.append("guard hover badge should expose current guard-zone distance: %s" % str(guard_hover_badge))
var guard_move_badge := scene._move_cell_preview_badge(Vector2i(4, 5))
if str(guard_move_badge.get("text", "")) != "경계" or str(guard_move_badge.get("kind", "")) != "select":
failures.append("move preview should mark guard reaction cells: %s" % str(guard_move_badge))
if not str(guard_move_badge.get("tooltip", "")).contains("거점 경계권"):
failures.append("guard move badge should explain guard reaction zone: %s" % str(guard_move_badge))
var hovered_guard: Dictionary = scene._hovered_ai_zone_unit()
if str(hovered_guard.get("id", "")) != "yellow_turban_4":
failures.append("hovered guard should expose a drawable AI zone: %s" % str(hovered_guard))