Improve White Horse relief battle pacing

This commit is contained in:
2026-06-19 14:57:32 +09:00
parent 4e84d83f3f
commit ed23b2bdf0
3 changed files with 297 additions and 13 deletions

View File

@@ -151,6 +151,18 @@ func _init() -> void:
_progress_text("res://data/scenarios/009_xiapi_siege.json", false),
"하비 포위선"
)
_check_contains(
failures,
"010 gated victory progress",
_progress_text("res://data/scenarios/010_white_horse_relief.json", false),
"제9군령에 전공 갱신"
)
_check_contains(
failures,
"010 decoy line progress",
_progress_text("res://data/scenarios/010_white_horse_relief.json", false),
"백마 유인선"
)
_check_contains(
failures,
"045 gated destination progress",
@@ -169,6 +181,7 @@ func _init() -> void:
_check_xian_emperor_escort_extended_pressure(failures)
_check_wan_castle_escape_extended_pressure(failures)
_check_xiapi_siege_extended_pressure(failures)
_check_white_horse_relief_extended_pressure(failures)
_check_objective_update(failures)
_check_objective_notice(failures)
_check_terrain_recovery(failures)
@@ -1016,6 +1029,111 @@ func _check_xiapi_siege_extended_pressure(failures: Array[String]) -> void:
scene.free()
func _check_white_horse_relief_extended_pressure(failures: Array[String]) -> void:
var state = BattleStateScript.new()
if not state.load_battle("res://data/scenarios/010_white_horse_relief.json"):
failures.append("White Horse Relief pressure smoke could not load scenario")
return
if state.get_turn_limit() != 17:
failures.append("White Horse Relief should allow play through turn 17, got limit %d" % state.get_turn_limit())
if state.get_living_units(BattleStateScript.TEAM_ENEMY).size() != 9:
failures.append("White Horse Relief should start with 9 enemies, got %d" % state.get_living_units(BattleStateScript.TEAM_ENEMY).size())
_assert_required_unit(failures, state, "guo_jia_ch10", "White Horse Relief Guo Jia")
_assert_required_unit(failures, state, "xiahou_dun_ch10", "White Horse Relief Xiahou Dun")
if state._condition_gate_open({"after_event": "turn_9_yuan_main_pressure"}):
failures.append("White Horse Relief final defeat-all condition should be gated until turn 9 Yuan main pressure")
var early_state = BattleStateScript.new()
if not early_state.load_battle("res://data/scenarios/010_white_horse_relief.json"):
failures.append("White Horse Relief early victory smoke could not load scenario")
return
var early_cao_cao: Dictionary = early_state.get_unit("cao_cao_ch10")
early_cao_cao["pos"] = Vector2i(8, 4)
for enemy in early_state.get_living_units(BattleStateScript.TEAM_ENEMY):
enemy["alive"] = false
early_state._check_battle_status()
if str(early_state.battle_status) != BattleStateScript.STATUS_ACTIVE:
failures.append("White Horse Relief should not end before turn 9 Yuan main pressure, got %s" % str(early_state.battle_status))
var marker_cells := state.get_objective_cells()
for decoy_cell in [Vector2i(8, 4), Vector2i(9, 4), Vector2i(8, 5), Vector2i(9, 5)]:
if not marker_cells.has(decoy_cell):
failures.append("White Horse Relief should mark decoy line cell %s: %s" % [str(decoy_cell), str(marker_cells)])
if state.get_objective_cell_label(decoy_cell) != "백마 유인선":
failures.append("White Horse Relief marker should expose decoy line label at %s" % str(decoy_cell))
var recovery_summaries := state.get_recovery_terrain_summaries()
var has_gate_recovery := false
var has_village_recovery := false
for summary in recovery_summaries:
if str(summary.get("name", "")) == "성채" and int(summary.get("count", 0)) == 4 and int(summary.get("heal", 0)) == 8:
has_gate_recovery = true
if str(summary.get("name", "")) == "마을" and int(summary.get("count", 0)) == 4 and int(summary.get("heal", 0)) == 6:
has_village_recovery = true
if not has_gate_recovery:
failures.append("White Horse Relief should expose ferry recovery terrain: %s" % str(recovery_summaries))
if not has_village_recovery:
failures.append("White Horse Relief should expose reserve camp recovery terrain: %s" % str(recovery_summaries))
state._run_events("turn_start", BattleStateScript.TEAM_ENEMY, 2)
_assert_unit_at(failures, state, "yan_liang_rider_north", Vector2i(8, 2), "White Horse Relief turn 2 north rider")
_assert_unit_at(failures, state, "yan_liang_rider_south", Vector2i(8, 6), "White Horse Relief turn 2 south rider")
state._run_events("turn_start", BattleStateScript.TEAM_PLAYER, 3)
if not state._condition_gate_open({"after_event": "turn_3_decoy_line"}):
failures.append("White Horse Relief decoy line event should fire on turn 3")
_assert_unit_at(failures, state, "decoy_cutoff_guard", Vector2i(7, 4), "White Horse Relief turn 3 cutoff guard")
_assert_unit_at(failures, state, "decoy_cutoff_archer", Vector2i(7, 5), "White Horse Relief turn 3 cutoff archer")
if state._condition_gate_open({"after_event": "turn_9_yuan_main_pressure"}):
failures.append("White Horse Relief final gate should still be closed after decoy line event")
state._run_events("turn_start", BattleStateScript.TEAM_ENEMY, 6)
if int(state.get_unit("xiahou_dun_ch10").get("ai_target_priority", 0)) != 10:
failures.append("White Horse Relief turn 6 pressure should target Xiahou Dun")
_assert_unit_at(failures, state, "yuan_wing_rider", Vector2i(6, 1), "White Horse Relief turn 6 rider")
_assert_unit_at(failures, state, "yuan_wing_guard", Vector2i(6, 8), "White Horse Relief turn 6 guard")
_assert_unit_at(failures, state, "yuan_wing_archer", Vector2i(9, 8), "White Horse Relief turn 6 archer")
state._run_events("turn_start", BattleStateScript.TEAM_ENEMY, 9)
if not state._condition_gate_open({"after_event": "turn_9_yuan_main_pressure"}):
failures.append("White Horse Relief final gate should open after turn 9 Yuan main pressure")
_assert_unit_at(failures, state, "yuan_main_rider", Vector2i(13, 3), "White Horse Relief turn 9 rider")
_assert_unit_at(failures, state, "yuan_main_guard", Vector2i(13, 5), "White Horse Relief turn 9 guard")
_assert_unit_at(failures, state, "yuan_main_archer", Vector2i(13, 7), "White Horse Relief turn 9 archer")
if state.get_living_units(BattleStateScript.TEAM_ENEMY).size() != 19:
failures.append("White Horse Relief should field 19 enemies after Yuan main pressure, got %d" % state.get_living_units(BattleStateScript.TEAM_ENEMY).size())
if int(state.get_unit("cao_cao_ch10").get("ai_target_priority", 0)) != 13:
failures.append("White Horse Relief turn 9 Yuan main pressure should target Cao Cao")
if int(state.get_unit("xiahou_dun_ch10").get("ai_target_priority", 0)) != 13:
failures.append("White Horse Relief turn 9 Yuan main pressure should target Xiahou Dun")
for enemy in state.get_living_units(BattleStateScript.TEAM_ENEMY):
enemy["alive"] = false
var cao_cao: Dictionary = state.get_unit("cao_cao_ch10")
cao_cao["pos"] = Vector2i(7, 4)
state._check_battle_status()
if str(state.battle_status) != BattleStateScript.STATUS_ACTIVE:
failures.append("White Horse Relief should not end before the decoy line is held, got %s" % str(state.battle_status))
cao_cao["pos"] = Vector2i(8, 4)
state._check_battle_status()
if str(state.battle_status) != BattleStateScript.STATUS_VICTORY:
failures.append("White Horse Relief should end after Yuan pressure falls and decoy line is held, got %s" % str(state.battle_status))
var pressed_state = BattleStateScript.new()
if not pressed_state.load_battle("res://data/scenarios/010_white_horse_relief.json", {}, {}, {"pressed_northern_campaign": true}):
failures.append("White Horse Relief pressed branch smoke could not load scenario")
return
pressed_state._run_events("turn_start", BattleStateScript.TEAM_ENEMY, 2)
pressed_state._run_events("turn_start", BattleStateScript.TEAM_PLAYER, 3)
pressed_state._run_events("turn_start", BattleStateScript.TEAM_ENEMY, 4)
pressed_state._run_events("turn_start", BattleStateScript.TEAM_ENEMY, 6)
pressed_state._run_events("turn_start", BattleStateScript.TEAM_ENEMY, 9)
if pressed_state.get_living_units(BattleStateScript.TEAM_ENEMY).size() != 20:
failures.append("White Horse Relief pressed branch should field 20 enemies after Yuan main pressure, got %d" % pressed_state.get_living_units(BattleStateScript.TEAM_ENEMY).size())
var scene = BattleSceneScript.new()
if not scene.state.load_battle("res://data/scenarios/010_white_horse_relief.json"):
failures.append("White Horse Relief briefing smoke could not load scenario")
scene.free()
return
var overview := scene._format_briefing_battlefield_overview_text()
_check_contains(failures, "010 briefing map size", overview, "15칸 x 10칸")
_check_contains(failures, "010 briefing enemy count", overview, "적세 9명")
_check_contains(failures, "010 briefing village recovery", overview, "마을 4칸 +6")
_check_contains(failures, "010 briefing ferry recovery", overview, "성채 4칸 +8")
scene.free()
func _assert_unit_at(failures: Array[String], state, unit_id: String, expected_cell: Vector2i, label: String) -> void:
var unit: Dictionary = state.get_unit(unit_id)
if unit.is_empty() or not bool(unit.get("alive", false)) or not bool(unit.get("deployed", true)):
@@ -1025,6 +1143,15 @@ func _assert_unit_at(failures: Array[String], state, unit_id: String, expected_c
failures.append("%s should be at %s, got %s" % [label, str(expected_cell), str(unit.get("pos", Vector2i(-999, -999)))])
func _assert_required_unit(failures: Array[String], state, unit_id: String, label: String) -> void:
var unit: Dictionary = state.get_unit(unit_id)
if unit.is_empty() or not bool(unit.get("alive", false)) or not bool(unit.get("deployed", true)):
failures.append("%s should deploy %s" % [label, unit_id])
return
if not bool(unit.get("required_deployment", false)):
failures.append("%s should be required deployment: %s" % [label, unit_id])
func _check_opening_board_avoids_side_panel(failures: Array[String]) -> void:
var state = BattleStateScript.new()
if not state.load_battle("res://data/scenarios/001_yellow_turbans.json"):