192 lines
8.8 KiB
GDScript
192 lines
8.8 KiB
GDScript
extends SceneTree
|
|
|
|
const BattleStateScript := preload("res://scripts/core/battle_state.gd")
|
|
|
|
|
|
func _init() -> void:
|
|
var failures: Array[String] = []
|
|
var state = BattleStateScript.new()
|
|
if not state.load_battle("res://data/scenarios/001_yellow_turbans.json"):
|
|
push_error("Could not load AI priority smoke scenario.")
|
|
quit(1)
|
|
return
|
|
|
|
_check_priority_action(state, failures)
|
|
_check_priority_changes_enemy_targeting(state, failures)
|
|
_check_reinforcement_priority_events(failures)
|
|
_check_guard_ai_behavior(failures)
|
|
_check_sentry_ai_behavior(failures)
|
|
|
|
if failures.is_empty():
|
|
print("event ai priority smoke ok")
|
|
quit(0)
|
|
return
|
|
|
|
for failure in failures:
|
|
push_error(failure)
|
|
quit(1)
|
|
|
|
|
|
func _check_priority_action(state, failures: Array[String]) -> void:
|
|
var logs: Array[String] = []
|
|
var feedback: Array[Dictionary] = []
|
|
state.log_added.connect(func(message: String) -> void:
|
|
logs.append(message)
|
|
)
|
|
state.combat_feedback_requested.connect(func(unit_id: String, text: String, kind: String) -> void:
|
|
feedback.append({"unit_id": unit_id, "text": text, "kind": kind})
|
|
)
|
|
|
|
state._execute_event_action({
|
|
"type": "set_ai_target_priority",
|
|
"unit_id": "xiahou_dun",
|
|
"priority": 9,
|
|
"text": "하후돈에게 적 시선이 몰립니다."
|
|
})
|
|
|
|
var target: Dictionary = state.get_unit("xiahou_dun")
|
|
if int(target.get("ai_target_priority", 0)) != 9:
|
|
failures.append("AI target priority did not update on event action")
|
|
if not logs.has("하후돈에게 적 시선이 몰립니다."):
|
|
failures.append("AI priority event log missing")
|
|
if not _feedback_has(feedback, "xiahou_dun", "표적", "priority"):
|
|
failures.append("AI priority feedback missing")
|
|
|
|
|
|
func _check_priority_changes_enemy_targeting(state, failures: Array[String]) -> void:
|
|
var enemy: Dictionary = state.get_unit("yellow_turban_1")
|
|
var target: Dictionary = state._find_nearest_target(enemy, "player")
|
|
if str(target.get("id", "")) != "xiahou_dun":
|
|
failures.append("AI priority did not affect nearest target scoring: %s" % str(target.get("id", "")))
|
|
|
|
|
|
func _check_reinforcement_priority_events(failures: Array[String]) -> void:
|
|
var state = BattleStateScript.new()
|
|
if not state.load_battle("res://data/scenarios/001_yellow_turbans.json"):
|
|
failures.append("Could not load stage 1 priority event scenario")
|
|
return
|
|
|
|
var logs: Array[String] = []
|
|
var feedback: Array[Dictionary] = []
|
|
state.log_added.connect(func(message: String) -> void:
|
|
logs.append(message)
|
|
)
|
|
state.combat_feedback_requested.connect(func(unit_id: String, text: String, kind: String) -> void:
|
|
feedback.append({"unit_id": unit_id, "text": text, "kind": kind})
|
|
)
|
|
|
|
state._run_events("turn_start", "enemy", 4)
|
|
if int(state.get_unit("xiahou_dun").get("ai_target_priority", 0)) != 9:
|
|
failures.append("Turn 4 reinforcement did not pressure Xiahou Dun")
|
|
if not logs.has("동쪽 잔병이 하후돈의 선봉을 노린다."):
|
|
failures.append("Turn 4 reinforcement priority log missing")
|
|
if not _feedback_has(feedback, "xiahou_dun", "표적", "priority"):
|
|
failures.append("Turn 4 reinforcement priority feedback missing")
|
|
for unit_id in ["yellow_turban_reinforcement_1", "yellow_turban_reinforcement_2", "yellow_turban_reinforcement_2b"]:
|
|
if not _feedback_has(feedback, unit_id, "증원", "reinforcement"):
|
|
failures.append("Turn 4 reinforcement should show arrival feedback for %s" % unit_id)
|
|
|
|
state._run_events("turn_start", "enemy", 7)
|
|
if int(state.get_unit("cao_cao").get("ai_target_priority", 0)) != 11:
|
|
failures.append("Turn 7 reinforcement did not pressure Cao Cao")
|
|
if not logs.has("남쪽 약탈대가 조조의 본진을 노린다."):
|
|
failures.append("Turn 7 reinforcement priority log missing")
|
|
if not _feedback_has(feedback, "cao_cao", "표적", "priority"):
|
|
failures.append("Turn 7 reinforcement priority feedback missing")
|
|
for unit_id in ["yellow_turban_reinforcement_3", "yellow_turban_reinforcement_4", "yellow_turban_reinforcement_4b"]:
|
|
if not _feedback_has(feedback, unit_id, "증원", "reinforcement"):
|
|
failures.append("Turn 7 reinforcement should show arrival feedback for %s" % unit_id)
|
|
|
|
|
|
func _check_guard_ai_behavior(failures: Array[String]) -> void:
|
|
var state = BattleStateScript.new()
|
|
if not state.load_battle("res://data/scenarios/001_yellow_turbans.json"):
|
|
failures.append("Could not load stage 1 guard AI scenario")
|
|
return
|
|
|
|
var guard: Dictionary = state.get_unit("yellow_turban_7")
|
|
if str(guard.get("ai_behavior", "")) != "guard":
|
|
failures.append("castle defender should load guard AI behavior")
|
|
if guard.get("ai_guard_anchor", Vector2i(-1, -1)) != Vector2i(19, 2):
|
|
failures.append("castle defender should keep its guard anchor: %s" % str(guard.get("ai_guard_anchor", null)))
|
|
if int(guard.get("ai_guard_radius", 0)) != 3:
|
|
failures.append("castle defender should keep its guard radius")
|
|
|
|
var movement_cells: Array[Vector2i] = state._ai_guard_movement_cells(guard, state._movement_range_for_unit(guard))
|
|
if state._ai_guard_is_engaged(guard, movement_cells):
|
|
failures.append("guard should not engage distant opening player positions")
|
|
state._enemy_take_action(guard)
|
|
if guard.get("pos", Vector2i.ZERO) != Vector2i(19, 2):
|
|
failures.append("guard should hold position instead of chasing distant targets: %s" % str(guard.get("pos", null)))
|
|
|
|
guard["pos"] = Vector2i(15, 2)
|
|
var displaced_return := state._best_ai_guard_return_cell(guard, state._ai_guard_movement_cells(guard, state._movement_range_for_unit(guard)))
|
|
if state._manhattan(displaced_return, Vector2i(19, 2)) >= state._manhattan(guard["pos"], Vector2i(19, 2)):
|
|
failures.append("displaced guard should choose a cell closer to its anchor: %s" % str(displaced_return))
|
|
state._enemy_take_action(guard)
|
|
if state._manhattan(guard.get("pos", Vector2i.ZERO), Vector2i(19, 2)) >= 4:
|
|
failures.append("displaced guard should step back toward its anchor: %s" % str(guard.get("pos", null)))
|
|
|
|
guard["pos"] = Vector2i(19, 2)
|
|
state.get_unit("cao_cao")["pos"] = Vector2i(18, 2)
|
|
movement_cells = state._ai_guard_movement_cells(guard, state._movement_range_for_unit(guard))
|
|
if not state._ai_guard_is_engaged(guard, movement_cells):
|
|
failures.append("guard should engage a player unit inside its guard radius")
|
|
var target: Dictionary = state._find_nearest_ai_guard_target(guard, "player")
|
|
if str(target.get("id", "")) != "cao_cao":
|
|
failures.append("guard target search should prefer targets inside the guard zone: %s" % str(target.get("id", "")))
|
|
|
|
|
|
func _check_sentry_ai_behavior(failures: Array[String]) -> void:
|
|
var state = BattleStateScript.new()
|
|
if not state.load_battle("res://data/scenarios/001_yellow_turbans.json"):
|
|
failures.append("Could not load stage 1 sentry AI scenario")
|
|
return
|
|
|
|
var sentry: Dictionary = state.get_unit("yellow_turban_5")
|
|
if str(sentry.get("ai_behavior", "")) != "sentry":
|
|
failures.append("central raider should load sentry AI behavior")
|
|
if sentry.get("ai_sentry_anchor", Vector2i(-1, -1)) != Vector2i(10, 6):
|
|
failures.append("central raider should keep its sentry anchor: %s" % str(sentry.get("ai_sentry_anchor", null)))
|
|
if int(sentry.get("ai_sentry_radius", 0)) != 5:
|
|
failures.append("central raider should keep its sentry radius")
|
|
if int(sentry.get("ai_sentry_activation_turn", 0)) != 4:
|
|
failures.append("central raider should keep delayed activation turn")
|
|
|
|
state._enemy_take_action(sentry)
|
|
if bool(sentry.get("ai_awake", false)):
|
|
failures.append("sentry should not wake while opening player positions are distant")
|
|
if sentry.get("pos", Vector2i.ZERO) != Vector2i(10, 6):
|
|
failures.append("sleeping sentry should hold its watch post: %s" % str(sentry.get("pos", null)))
|
|
|
|
state.get_unit("cao_cao")["pos"] = Vector2i(5, 6)
|
|
state._enemy_take_action(sentry)
|
|
if not bool(sentry.get("ai_awake", false)):
|
|
failures.append("sentry should wake when a player enters the lure line")
|
|
if sentry.get("pos", Vector2i.ZERO) == Vector2i(10, 6):
|
|
failures.append("woken sentry should start pressing the lure-line target")
|
|
|
|
var delayed_state = BattleStateScript.new()
|
|
if not delayed_state.load_battle("res://data/scenarios/001_yellow_turbans.json"):
|
|
failures.append("Could not load delayed sentry AI scenario")
|
|
return
|
|
var delayed_sentry: Dictionary = delayed_state.get_unit("yellow_turban_2")
|
|
delayed_state._enemy_take_action(delayed_sentry)
|
|
if bool(delayed_sentry.get("ai_awake", false)):
|
|
failures.append("delayed sentry should not wake before its activation turn")
|
|
delayed_state.turn_number = 5
|
|
delayed_state._enemy_take_action(delayed_sentry)
|
|
if not bool(delayed_sentry.get("ai_awake", false)):
|
|
failures.append("delayed sentry should wake on its activation turn")
|
|
|
|
|
|
func _feedback_has(feedback: Array[Dictionary], expected_unit_id: String, expected_text: String, expected_kind: String) -> bool:
|
|
for entry in feedback:
|
|
if (
|
|
str(entry.get("unit_id", "")) == expected_unit_id
|
|
and str(entry.get("text", "")) == expected_text
|
|
and str(entry.get("kind", "")) == expected_kind
|
|
):
|
|
return true
|
|
return false
|