Improve Xingyang ambush battle pacing
This commit is contained in:
@@ -2,14 +2,27 @@
|
||||
"id": "003_xingyang_ambush",
|
||||
"name": "형양 매복전",
|
||||
"objectives": {
|
||||
"victory": "서영의 후군을 격파하라.",
|
||||
"defeat": "조조가 퇴각하거나 제11턴에 이르면 패한다."
|
||||
"victory": "서영의 매복진을 돌파하고 동쪽 길목을 장악하라.",
|
||||
"defeat": "조조가 퇴각하거나 제17턴이 시작되면 패한다."
|
||||
},
|
||||
"conditions": {
|
||||
"victory": { "type": "all_units_defeated", "team": "enemy", "after_event": "turn_3_ambush" },
|
||||
"victory": {
|
||||
"type": "all",
|
||||
"conditions": [
|
||||
{ "type": "all_units_defeated", "team": "enemy", "after_event": "turn_9_forest_blockade" },
|
||||
{
|
||||
"type": "unit_reaches_tile",
|
||||
"label": "동쪽 길목",
|
||||
"team": "player",
|
||||
"unit_ids": ["cao_cao_ch3", "xiahou_dun_ch3", "xiahou_yuan_ch3", "cao_ren_ch3"],
|
||||
"officer_ids": ["cao_cao", "xiahou_dun", "xiahou_yuan", "cao_ren"],
|
||||
"cells": [[10, 3], [11, 3], [10, 4], [11, 4]]
|
||||
}
|
||||
]
|
||||
},
|
||||
"defeat": [
|
||||
{ "type": "any_officer_defeated", "team": "player", "officer_ids": ["cao_cao"] },
|
||||
{ "type": "turn_reached", "turn": 11, "team": "player" }
|
||||
{ "type": "turn_reached", "turn": 17, "team": "player" }
|
||||
]
|
||||
},
|
||||
"briefing": {
|
||||
@@ -18,7 +31,8 @@
|
||||
"lines": [
|
||||
"동탁이 낙양을 버리고 천자를 끼고 서쪽으로 달아났다.",
|
||||
"조조의 선봉은 형양 부근에서 적의 후군을 따라붙는다.",
|
||||
"앞길은 좁고 숲은 깊다. 전열을 흩뜨리지 말고 수풀의 기척을 살펴라."
|
||||
"앞길은 좁고 숲은 깊다. 전열을 흩뜨리지 말고 수풀의 기척을 살펴라.",
|
||||
"동쪽 길목을 빼앗고 숲 뒤의 봉쇄병까지 무너뜨려야 추격로가 열린다."
|
||||
],
|
||||
"conditional_lines": [
|
||||
{
|
||||
@@ -133,7 +147,7 @@
|
||||
]
|
||||
},
|
||||
"roster": {
|
||||
"max_units": 3,
|
||||
"max_units": 4,
|
||||
"required_officers": ["cao_cao"]
|
||||
},
|
||||
"formation": {
|
||||
@@ -148,8 +162,8 @@
|
||||
"GGGFFRRGHHGG",
|
||||
"GGGGGRRGHHGG",
|
||||
"RRRRRRRRRRRR",
|
||||
"GGGGFRRFGGGG",
|
||||
"GGGHFRRFGGGG",
|
||||
"GGGGFRRFTTGG",
|
||||
"GGGHFRRFTTGG",
|
||||
"GGGHHRRGFFGG",
|
||||
"GGGGGRRGFFGG",
|
||||
"GGWWGRRGGGGG"
|
||||
@@ -217,6 +231,24 @@
|
||||
"level": 3,
|
||||
"pos": [8, 2],
|
||||
"base": { "hp": 30, "atk": 11, "def": 5 }
|
||||
},
|
||||
{
|
||||
"unit_id": "forest_spearman",
|
||||
"name": "숲길 창병",
|
||||
"class_id": "infantry",
|
||||
"team": "enemy",
|
||||
"level": 3,
|
||||
"pos": [6, 1],
|
||||
"base": { "hp": 38, "atk": 12, "def": 7 }
|
||||
},
|
||||
{
|
||||
"unit_id": "rearguard_cavalry_screen",
|
||||
"name": "후군 기병",
|
||||
"class_id": "cavalry",
|
||||
"team": "enemy",
|
||||
"level": 3,
|
||||
"pos": [10, 5],
|
||||
"base": { "hp": 40, "atk": 14, "def": 7 }
|
||||
}
|
||||
],
|
||||
"events": [
|
||||
@@ -289,8 +321,8 @@
|
||||
},
|
||||
{
|
||||
"type": "set_objective",
|
||||
"victory": "서영과 모든 매복병을 격파하라.",
|
||||
"defeat": "조조가 퇴각하거나 제11턴에 이르면 패한다."
|
||||
"victory": "서영의 매복진을 밀어내고 동쪽 길목을 장악하라.",
|
||||
"defeat": "조조가 퇴각하거나 제17턴이 시작되면 패한다."
|
||||
},
|
||||
{
|
||||
"type": "spawn_deployments",
|
||||
@@ -341,8 +373,8 @@
|
||||
},
|
||||
{
|
||||
"type": "set_objective",
|
||||
"victory": "서영과 드러난 매복병을 격파하라.",
|
||||
"defeat": "조조가 퇴각하거나 제11턴에 이르면 패한다."
|
||||
"victory": "드러난 매복병을 무너뜨리고 동쪽 길목을 장악하라.",
|
||||
"defeat": "조조가 퇴각하거나 제17턴이 시작되면 패한다."
|
||||
},
|
||||
{
|
||||
"type": "spawn_deployments",
|
||||
@@ -382,6 +414,103 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "turn_6_slope_archers",
|
||||
"once": true,
|
||||
"when": { "type": "turn_start", "team": "enemy", "turn": 6 },
|
||||
"actions": [
|
||||
{ "type": "log", "text": "숲 뒤 비탈에서 서영의 궁병대가 퇴로를 끊으려 내려온다." },
|
||||
{
|
||||
"type": "spawn_deployments",
|
||||
"deployments": [
|
||||
{
|
||||
"unit_id": "slope_archer_north",
|
||||
"name": "비탈 궁병",
|
||||
"class_id": "archer",
|
||||
"team": "enemy",
|
||||
"level": 3,
|
||||
"pos": [10, 1],
|
||||
"base": { "hp": 28, "atk": 12, "def": 4 }
|
||||
},
|
||||
{
|
||||
"unit_id": "slope_spearman",
|
||||
"name": "비탈 창병",
|
||||
"class_id": "infantry",
|
||||
"team": "enemy",
|
||||
"level": 3,
|
||||
"pos": [9, 1],
|
||||
"base": { "hp": 36, "atk": 12, "def": 7 }
|
||||
},
|
||||
{
|
||||
"unit_id": "slope_archer_south",
|
||||
"name": "비탈 궁병",
|
||||
"class_id": "archer",
|
||||
"team": "enemy",
|
||||
"level": 3,
|
||||
"pos": [10, 7],
|
||||
"base": { "hp": 28, "atk": 12, "def": 4 }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "set_ai_target_priority",
|
||||
"unit_id": "xiahou_dun_ch3",
|
||||
"priority": 8,
|
||||
"text": "비탈 궁병대가 하후돈의 선봉을 노린다."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "turn_9_forest_blockade",
|
||||
"once": true,
|
||||
"when": { "type": "turn_start", "team": "enemy", "turn": 9 },
|
||||
"actions": [
|
||||
{ "type": "log", "text": "동쪽 길목에서 서영의 마지막 봉쇄병이 나타난다." },
|
||||
{
|
||||
"type": "set_objective",
|
||||
"victory": "마지막 봉쇄병을 격파하고 동쪽 길목을 장악하라.",
|
||||
"defeat": "조조가 퇴각하거나 제17턴이 시작되면 패한다."
|
||||
},
|
||||
{
|
||||
"type": "spawn_deployments",
|
||||
"deployments": [
|
||||
{
|
||||
"unit_id": "forest_blockade_cavalry",
|
||||
"name": "봉쇄 기병",
|
||||
"class_id": "cavalry",
|
||||
"team": "enemy",
|
||||
"level": 3,
|
||||
"pos": [11, 2],
|
||||
"base": { "hp": 42, "atk": 15, "def": 7 }
|
||||
},
|
||||
{
|
||||
"unit_id": "forest_blockade_guard",
|
||||
"name": "봉쇄 창병",
|
||||
"class_id": "infantry",
|
||||
"team": "enemy",
|
||||
"level": 3,
|
||||
"pos": [11, 5],
|
||||
"base": { "hp": 38, "atk": 12, "def": 8 }
|
||||
},
|
||||
{
|
||||
"unit_id": "forest_blockade_archer",
|
||||
"name": "봉쇄 궁병",
|
||||
"class_id": "archer",
|
||||
"team": "enemy",
|
||||
"level": 3,
|
||||
"pos": [11, 7],
|
||||
"base": { "hp": 30, "atk": 12, "def": 4 }
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "set_ai_target_priority",
|
||||
"unit_id": "cao_cao_ch3",
|
||||
"priority": 11,
|
||||
"text": "봉쇄병이 조조의 깃발을 노리고 길목을 닫는다."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "turn_5_regroup_pressure",
|
||||
"once": true,
|
||||
|
||||
@@ -55,6 +55,18 @@ func _init() -> void:
|
||||
_progress_text("res://data/scenarios/002_sishui_gate.json", false),
|
||||
"관문 장악"
|
||||
)
|
||||
_check_contains(
|
||||
failures,
|
||||
"003 gated victory progress",
|
||||
_progress_text("res://data/scenarios/003_xingyang_ambush.json", false),
|
||||
"제9군령에 전공 갱신"
|
||||
)
|
||||
_check_contains(
|
||||
failures,
|
||||
"003 road capture progress",
|
||||
_progress_text("res://data/scenarios/003_xingyang_ambush.json", false),
|
||||
"동쪽 길목"
|
||||
)
|
||||
_check_contains(
|
||||
failures,
|
||||
"007 destination progress",
|
||||
@@ -78,6 +90,7 @@ func _init() -> void:
|
||||
_check_turn_gated_objective_does_not_create_marker(failures)
|
||||
_check_opening_battle_requires_castle_capture(failures)
|
||||
_check_sishui_gate_extended_pressure(failures)
|
||||
_check_xingyang_ambush_extended_pressure(failures)
|
||||
_check_objective_update(failures)
|
||||
_check_objective_notice(failures)
|
||||
_check_terrain_recovery(failures)
|
||||
@@ -360,6 +373,68 @@ func _check_sishui_gate_extended_pressure(failures: Array[String]) -> void:
|
||||
scene.free()
|
||||
|
||||
|
||||
func _check_xingyang_ambush_extended_pressure(failures: Array[String]) -> void:
|
||||
var state = BattleStateScript.new()
|
||||
if not state.load_battle("res://data/scenarios/003_xingyang_ambush.json", {}, {}, {"pursued_dong_zhuo": true}):
|
||||
failures.append("Xingyang Ambush pressure smoke could not load scenario")
|
||||
return
|
||||
if state.get_turn_limit() != 16:
|
||||
failures.append("Xingyang Ambush should allow play through turn 16, got limit %d" % state.get_turn_limit())
|
||||
if state.get_living_units(BattleStateScript.TEAM_ENEMY).size() != 6:
|
||||
failures.append("Xingyang Ambush should start with 6 enemies, got %d" % state.get_living_units(BattleStateScript.TEAM_ENEMY).size())
|
||||
if state._condition_gate_open({"after_event": "turn_9_forest_blockade"}):
|
||||
failures.append("Xingyang Ambush final defeat-all condition should be gated until turn 9 blockade")
|
||||
var marker_cells := state.get_objective_cells()
|
||||
for road_cell in [Vector2i(10, 3), Vector2i(11, 3), Vector2i(10, 4), Vector2i(11, 4)]:
|
||||
if not marker_cells.has(road_cell):
|
||||
failures.append("Xingyang Ambush should mark road capture cell %s: %s" % [str(road_cell), str(marker_cells)])
|
||||
if state.get_objective_cell_label(road_cell) != "동쪽 길목":
|
||||
failures.append("Xingyang Ambush road marker should expose label at %s" % str(road_cell))
|
||||
var recovery_summaries := state.get_recovery_terrain_summaries()
|
||||
var has_waystation_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)) == 6:
|
||||
has_waystation_recovery = true
|
||||
if not has_waystation_recovery:
|
||||
failures.append("Xingyang Ambush should expose roadside recovery terrain: %s" % str(recovery_summaries))
|
||||
state._run_events("turn_start", BattleStateScript.TEAM_ENEMY, 3)
|
||||
state._run_events("turn_start", BattleStateScript.TEAM_ENEMY, 6)
|
||||
state._run_events("turn_start", BattleStateScript.TEAM_ENEMY, 9)
|
||||
if not state._condition_gate_open({"after_event": "turn_9_forest_blockade"}):
|
||||
failures.append("Xingyang Ambush final gate should open after turn 9 blockade event")
|
||||
if state.get_living_units(BattleStateScript.TEAM_ENEMY).size() != 15:
|
||||
failures.append("Xingyang Ambush pursuit branch should field 15 enemies after blockade, got %d" % state.get_living_units(BattleStateScript.TEAM_ENEMY).size())
|
||||
for enemy in state.get_living_units(BattleStateScript.TEAM_ENEMY):
|
||||
enemy["alive"] = false
|
||||
state._check_battle_status()
|
||||
if str(state.battle_status) != BattleStateScript.STATUS_ACTIVE:
|
||||
failures.append("Xingyang Ambush should not end before east road capture, got %s" % str(state.battle_status))
|
||||
var cao_cao: Dictionary = state.get_unit("cao_cao_ch3")
|
||||
cao_cao["pos"] = Vector2i(10, 3)
|
||||
state._check_battle_status()
|
||||
if str(state.battle_status) != BattleStateScript.STATUS_VICTORY:
|
||||
failures.append("Xingyang Ambush should end after blockade falls and east road is captured, got %s" % str(state.battle_status))
|
||||
var regroup_state = BattleStateScript.new()
|
||||
if not regroup_state.load_battle("res://data/scenarios/003_xingyang_ambush.json", {}, {}, {"regrouped_after_sishui": true}):
|
||||
failures.append("Xingyang Ambush regroup pressure smoke could not load scenario")
|
||||
return
|
||||
regroup_state._run_events("turn_start", BattleStateScript.TEAM_ENEMY, 3)
|
||||
regroup_state._run_events("turn_start", BattleStateScript.TEAM_ENEMY, 6)
|
||||
regroup_state._run_events("turn_start", BattleStateScript.TEAM_ENEMY, 9)
|
||||
if regroup_state.get_living_units(BattleStateScript.TEAM_ENEMY).size() != 14:
|
||||
failures.append("Xingyang Ambush regroup branch should field 14 enemies after blockade, got %d" % regroup_state.get_living_units(BattleStateScript.TEAM_ENEMY).size())
|
||||
var scene = BattleSceneScript.new()
|
||||
if not scene.state.load_battle("res://data/scenarios/003_xingyang_ambush.json"):
|
||||
failures.append("Xingyang Ambush briefing smoke could not load scenario")
|
||||
scene.free()
|
||||
return
|
||||
var overview := scene._format_briefing_battlefield_overview_text()
|
||||
_check_contains(failures, "003 briefing map size", overview, "12칸 x 9칸")
|
||||
_check_contains(failures, "003 briefing enemy count", overview, "적세 6명")
|
||||
_check_contains(failures, "003 briefing village recovery", overview, "마을 4칸 +6")
|
||||
scene.free()
|
||||
|
||||
|
||||
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"):
|
||||
|
||||
@@ -45,7 +45,7 @@ func _init() -> void:
|
||||
_check_contains(failures, "next camp location", result_text, "사수관, 초평 원년")
|
||||
_check_contains(failures, "next camp talk", result_text, "군막 회의 · 3건")
|
||||
_check_contains(failures, "next camp shop", result_text, "군상 물품 · 6종")
|
||||
_check_contains(failures, "next camp deploy", result_text, "출진 명부 · 2/3")
|
||||
_check_contains(failures, "next camp deploy", result_text, "출진 명부 · 2/4")
|
||||
_check_contains(failures, "next camp formation", result_text, "진형 배치 6칸")
|
||||
|
||||
scene.battle_result_summary = {
|
||||
|
||||
Reference in New Issue
Block a user