Expose enemy AI state in battle UI

This commit is contained in:
2026-06-20 10:24:29 +09:00
parent 428b91b59d
commit aadf8b2f98
2 changed files with 69 additions and 5 deletions

View File

@@ -7994,6 +7994,9 @@ func _hover_info_badge_lines(cell: Vector2i) -> Array[String]:
int(unit.get("max_mp", 0)) int(unit.get("max_mp", 0))
]) ])
var status_effects := state.get_status_effect_summary(str(unit.get("id", ""))) var status_effects := state.get_status_effect_summary(str(unit.get("id", "")))
var ai_hint := _unit_ai_behavior_hover_line(unit)
if not ai_hint.is_empty():
lines.append(ai_hint)
if not status_effects.is_empty(): if not status_effects.is_empty():
lines.append("상태: %s" % status_effects) lines.append("상태: %s" % status_effects)
else: else:
@@ -9539,14 +9542,57 @@ func _unit_ai_behavior_text(unit: Dictionary) -> String:
var behavior := str(unit.get("ai_behavior", "")).strip_edges().to_lower() var behavior := str(unit.get("ai_behavior", "")).strip_edges().to_lower()
if behavior == "guard": if behavior == "guard":
var guard_radius := int(unit.get("ai_guard_radius", 0)) var guard_radius := int(unit.get("ai_guard_radius", 0))
return "수비: 거점 경계 반경 %d" % guard_radius return "수비: 거점 경계 반경 %d 안에서 길목을 지킵니다." % guard_radius
if behavior == "sentry": if behavior == "sentry":
var sentry_radius := int(unit.get("ai_sentry_radius", 0)) var sentry_radius := int(unit.get("ai_sentry_radius", 0))
var wake_text := "기상" if bool(unit.get("ai_awake", false)) else "대기" var activation_turn := int(unit.get("ai_sentry_activation_turn", 0))
return "초병: 유인선 %s 반경 %d" % [wake_text, sentry_radius] if bool(unit.get("ai_awake", false)):
return "초병: 경계가 풀려 추격 중입니다. 유인 반경 %d" % sentry_radius
if activation_turn > 0:
return "초병: 유인 반경 %d 또는 제%d군령에 움직입니다." % [sentry_radius, activation_turn]
return "초병: 유인 반경 %d 안에 들어오면 움직입니다." % sentry_radius
return "" return ""
func _unit_ai_behavior_hover_line(unit: Dictionary) -> String:
if str(unit.get("team", "")) != BattleState.TEAM_ENEMY:
return ""
var behavior := str(unit.get("ai_behavior", "")).strip_edges().to_lower()
if behavior == "guard":
return "수비 · 거점 반경 %d" % int(unit.get("ai_guard_radius", 0))
if behavior == "sentry":
var sentry_radius := int(unit.get("ai_sentry_radius", 0))
if bool(unit.get("ai_awake", false)):
return "초병 경계 · 유인 반경 %d" % sentry_radius
var activation_turn := int(unit.get("ai_sentry_activation_turn", 0))
if activation_turn > 0:
return "초병 대기 · 유인 %d · 제%d군령" % [sentry_radius, activation_turn]
return "초병 대기 · 유인 반경 %d" % sentry_radius
return ""
func _unit_ai_behavior_badge_text(unit: Dictionary) -> String:
if str(unit.get("team", "")) != BattleState.TEAM_ENEMY:
return ""
var behavior := str(unit.get("ai_behavior", "")).strip_edges().to_lower()
if behavior == "guard":
return "수비"
if behavior == "sentry":
return "경계" if bool(unit.get("ai_awake", false)) else "대기"
return ""
func _unit_ai_behavior_badge_icon_kind(unit: Dictionary) -> String:
if str(unit.get("team", "")) != BattleState.TEAM_ENEMY:
return ""
var behavior := str(unit.get("ai_behavior", "")).strip_edges().to_lower()
if behavior == "guard":
return "formation"
if behavior == "sentry" and not bool(unit.get("ai_awake", false)):
return "wait"
return "threat"
func _unit_current_terrain_text(unit: Dictionary) -> String: func _unit_current_terrain_text(unit: Dictionary) -> String:
var cell: Vector2i = unit.get("pos", Vector2i(-1, -1)) var cell: Vector2i = unit.get("pos", Vector2i(-1, -1))
if not state.is_inside(cell): if not state.is_inside(cell):
@@ -13171,7 +13217,7 @@ func _battle_unit_action_icon_kind(unit: Dictionary) -> String:
if not bool(unit.get("alive", true)): if not bool(unit.get("alive", true)):
return "cancel" return "cancel"
if str(unit.get("team", "")) == BattleState.TEAM_ENEMY: if str(unit.get("team", "")) == BattleState.TEAM_ENEMY:
return "threat" return _unit_ai_behavior_badge_icon_kind(unit)
if not bool(unit.get("controllable", true)): if not bool(unit.get("controllable", true)):
return "formation" return "formation"
if bool(unit.get("acted", false)): if bool(unit.get("acted", false)):
@@ -13202,7 +13248,7 @@ func _battle_unit_action_state_text(unit: Dictionary) -> String:
if not bool(unit.get("alive", true)): if not bool(unit.get("alive", true)):
return "퇴각" return "퇴각"
if str(unit.get("team", "")) == BattleState.TEAM_ENEMY: if str(unit.get("team", "")) == BattleState.TEAM_ENEMY:
return "" return _unit_ai_behavior_badge_text(unit)
if not bool(unit.get("controllable", true)): if not bool(unit.get("controllable", true)):
return "호위" return "호위"
if bool(unit.get("acted", false)): if bool(unit.get("acted", false)):
@@ -13214,6 +13260,9 @@ func _battle_unit_action_state_tooltip(unit: Dictionary) -> String:
if not bool(unit.get("alive", true)): if not bool(unit.get("alive", true)):
return "전장을 이탈했습니다." return "전장을 이탈했습니다."
if str(unit.get("team", "")) == BattleState.TEAM_ENEMY: if str(unit.get("team", "")) == BattleState.TEAM_ENEMY:
var ai_text := _unit_ai_behavior_text(unit)
if not ai_text.is_empty():
return ai_text
return "적군 부대입니다." return "적군 부대입니다."
if not bool(unit.get("controllable", true)): if not bool(unit.get("controllable", true)):
return "호위 대상이라 직접 명령할 수 없습니다." return "호위 대상이라 직접 명령할 수 없습니다."

View File

@@ -4490,8 +4490,12 @@ func _check_battle_unit_list_panel(failures: Array[String]) -> void:
failures.append("enemy battle unit list should keep raw coordinates out of visible badges: %s" % enemy_visible_text) failures.append("enemy battle unit list should keep raw coordinates out of visible badges: %s" % enemy_visible_text)
if enemy_visible_text.contains("군령:") or enemy_visible_text.contains("직접 명령"): if enemy_visible_text.contains("군령:") or enemy_visible_text.contains("직접 명령"):
failures.append("enemy battle unit list rows should keep dense enemy detail in hover text: %s" % enemy_visible_text) failures.append("enemy battle unit list rows should keep dense enemy detail in hover text: %s" % enemy_visible_text)
if not (enemy_badge_text.contains("대기") or enemy_badge_text.contains("수비") or enemy_badge_text.contains("경계")):
failures.append("enemy battle unit list should expose compact AI state badges: %s" % enemy_badge_text)
if not enemy_tooltips.contains("군령: 적군 군기") or not enemy_tooltips.contains("클릭하면 위치로 이동"): if not enemy_tooltips.contains("군령: 적군 군기") or not enemy_tooltips.contains("클릭하면 위치로 이동"):
failures.append("enemy battle unit list tooltips should retain enemy detail: %s" % enemy_tooltips) failures.append("enemy battle unit list tooltips should retain enemy detail: %s" % enemy_tooltips)
if not enemy_tooltips.contains("유인 반경") or not enemy_tooltips.contains("거점 경계 반경"):
failures.append("enemy battle unit list tooltips should explain sentry and guard behavior: %s" % enemy_tooltips)
if not _has_descendant_texture(scene.battle_unit_list_rows): if not _has_descendant_texture(scene.battle_unit_list_rows):
failures.append("enemy battle unit list should show unit sprite artwork") failures.append("enemy battle unit list should show unit sprite artwork")
if _find_descendant_by_name(scene.battle_unit_list_rows, "BattleUnitListClassIcon") == null: if _find_descendant_by_name(scene.battle_unit_list_rows, "BattleUnitListClassIcon") == null:
@@ -4746,6 +4750,17 @@ func _check_hover_intent_badges(failures: Array[String]) -> void:
failures.append("enemy hover info badge should use generated threat icon metadata: %s" % str(enemy_hover_badge)) failures.append("enemy hover info badge should use generated threat icon metadata: %s" % str(enemy_hover_badge))
if str(enemy_hover_badge.get("panel", "")) != "hud_jade": if str(enemy_hover_badge.get("panel", "")) != "hud_jade":
failures.append("enemy hover info badge should use generated jade panel metadata: %s" % str(enemy_hover_badge)) failures.append("enemy hover info badge should use generated jade panel metadata: %s" % str(enemy_hover_badge))
var central_sentry: Dictionary = scene.state.get_unit("yellow_turban_5")
central_sentry["ai_awake"] = false
scene.hover_cell = central_sentry.get("pos", Vector2i(10, 6))
var sentry_hover_badge := scene._hover_info_badge()
var sentry_hover_text := str(sentry_hover_badge.get("lines", []))
if not sentry_hover_text.contains("초병 대기") or not sentry_hover_text.contains("제4군령"):
failures.append("sleeping sentry hover badge should expose lure timing compactly: %s" % str(sentry_hover_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("초병 경계"):
failures.append("awake sentry hover badge should show the changed AI state: %s" % str(awake_sentry_hover_badge))
scene.hover_cell = Vector2i(4, 2) scene.hover_cell = Vector2i(4, 2)
var event_hover_badge := scene._hover_info_badge() var event_hover_badge := scene._hover_info_badge()
if str(event_hover_badge.get("kind", "")) != "event" or not str(event_hover_badge.get("lines", [])).contains("표식 북숲 보급고"): if str(event_hover_badge.get("kind", "")) != "event" or not str(event_hover_badge.get("lines", [])).contains("표식 북숲 보급고"):