Add story beats to Wan Castle Escape
This commit is contained in:
@@ -478,6 +478,10 @@
|
||||
{ "speaker": "Dian Wei", "text": "방패 뒤에 계십시오. 저들의 눈을 제 쪽으로 돌리겠습니다." }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "set_objective",
|
||||
"victory": "중간 매복을 뚫고 조앙을 지키며 서쪽 탈출로를 열어라."
|
||||
},
|
||||
{
|
||||
"type": "spawn_deployments",
|
||||
"deployments": [
|
||||
@@ -530,6 +534,11 @@
|
||||
{ "speaker": "Cao Ang", "display_speaker": "조앙", "text": "제 걱정으로 발을 늦추지 마십시오. 서쪽 길을 먼저 여십시오." }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "set_objective",
|
||||
"victory": "완성 협공을 버티며 조앙을 보호하고 서쪽 탈출로를 장악하라.",
|
||||
"defeat": "조조나 조앙이 퇴각하거나 제18군령이 시작되면 패한다."
|
||||
},
|
||||
{
|
||||
"type": "spawn_deployments",
|
||||
"deployments": [
|
||||
@@ -627,6 +636,63 @@
|
||||
"text": "마지막 추격대가 조조와 조앙을 함께 노린다."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "zhang_xiu_vanguard_falls",
|
||||
"once": true,
|
||||
"when": { "type": "unit_defeated", "team": "enemy", "unit_ids": ["zhang_xiu_vanguard"] },
|
||||
"actions": [
|
||||
{
|
||||
"type": "dialogue",
|
||||
"lines": [
|
||||
{ "speaker": "Zhang Xiu", "display_speaker": "장수", "side": "right", "text": "선봉이 무너져도 성 안의 길은 아직 우리 손에 있다. 서문을 막아라!" },
|
||||
{ "speaker": "Cao Ang", "display_speaker": "조앙", "side": "right", "text": "아버지, 앞길이 열립니다. 제가 뒤를 늦추겠습니다." },
|
||||
{ "speaker": "Cao Cao", "display_speaker": "조조", "side": "left", "text": "장수의 선봉을 꺾었다. 조앙을 잃지 말고 서쪽 탈출로까지 밀어라." }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "set_objective",
|
||||
"victory": "장수 선봉을 꺾었다. 조앙을 지키며 서쪽 탈출로를 열어라."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "jia_xu_strategist_falls",
|
||||
"once": true,
|
||||
"when": { "type": "unit_defeated", "team": "enemy", "unit_ids": ["jia_xu_strategist"] },
|
||||
"actions": [
|
||||
{
|
||||
"type": "dialogue",
|
||||
"lines": [
|
||||
{ "speaker": "Jia Xu", "display_speaker": "가후", "side": "right", "text": "계책의 앞줄이 흐트러졌습니다. 그렇다면 미련을 더 깊이 찌르십시오." },
|
||||
{ "speaker": "Dian Wei", "display_speaker": "전위", "side": "right", "text": "말로 묶는 자를 밀어냈습니다. 남은 것은 문 앞의 쇠뿐입니다." },
|
||||
{ "speaker": "Cao Cao", "display_speaker": "조조", "side": "left", "text": "가후의 그물이 잠시 벌어졌다. 서쪽으로 움직이고, 조앙을 후미에 남겨 두지 마라." }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "set_objective",
|
||||
"victory": "가후의 매복 전열을 흔들었다. 조앙을 지키며 서쪽 탈출로를 장악하라."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "west_gate_hunt_rider_falls",
|
||||
"once": true,
|
||||
"when": { "type": "unit_defeated", "team": "enemy", "unit_ids": ["west_gate_hunt_rider"] },
|
||||
"actions": [
|
||||
{
|
||||
"type": "dialogue",
|
||||
"lines": [
|
||||
{ "speaker": "Zhang Xiu", "display_speaker": "장수", "side": "right", "text": "서문 추격기가 꺾였다고? 남은 병력은 문을 버리지 마라!" },
|
||||
{ "speaker": "Cao Ang", "display_speaker": "조앙", "side": "right", "text": "서쪽 문이 흔들립니다. 지금 빠져나가야 합니다." },
|
||||
{ "speaker": "Cao Cao", "display_speaker": "조조", "side": "left", "text": "마지막 추격의 앞줄을 끊었다. 서쪽 탈출로를 지키며 남은 적을 정리하라." }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "set_objective",
|
||||
"victory": "서문 추격기를 꺾었다. 서쪽 탈출로를 지키며 남은 완성 추격대를 격파하라."
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"rewards": {
|
||||
|
||||
@@ -30,6 +30,7 @@ func _init() -> void:
|
||||
_check_puyang_raid_event_dialogue_structure(failures)
|
||||
_check_dingtao_counterattack_event_dialogue_structure(failures)
|
||||
_check_xian_emperor_escort_event_dialogue_structure(failures)
|
||||
_check_wan_castle_escape_event_dialogue_structure(failures)
|
||||
_check_opening_battle_story_beats(failures)
|
||||
_check_sishui_gate_story_beats(failures)
|
||||
_check_xingyang_ambush_story_beats(failures)
|
||||
@@ -37,6 +38,7 @@ func _init() -> void:
|
||||
_check_puyang_raid_story_beats(failures)
|
||||
_check_dingtao_counterattack_story_beats(failures)
|
||||
_check_xian_emperor_escort_story_beats(failures)
|
||||
_check_wan_castle_escape_story_beats(failures)
|
||||
_check_pixel_unit_helpers(failures)
|
||||
|
||||
if failures.is_empty():
|
||||
@@ -560,6 +562,71 @@ func _check_xian_emperor_escort_event_dialogue_structure(failures: Array[String]
|
||||
failures.append("Xian Emperor Escort event should update the active objective: %s" % objective_event_id)
|
||||
|
||||
|
||||
func _check_wan_castle_escape_event_dialogue_structure(failures: Array[String]) -> void:
|
||||
var state = BattleStateScript.new()
|
||||
if not state.load_battle("res://data/scenarios/008_wan_castle_escape.json"):
|
||||
failures.append("could not load Wan Castle Escape for event dialogue structure")
|
||||
return
|
||||
for event_id in [
|
||||
"opening_dialogue",
|
||||
"turn_2_gate_closes",
|
||||
"court_escape_lane",
|
||||
"remnant_scout_pressure",
|
||||
"midroad_trap",
|
||||
"turn_6_last_push",
|
||||
"turn_6_wan_crossfire",
|
||||
"turn_9_west_gate_hunt",
|
||||
"zhang_xiu_vanguard_falls",
|
||||
"jia_xu_strategist_falls",
|
||||
"west_gate_hunt_rider_falls"
|
||||
]:
|
||||
var event := _event_by_id(state, event_id)
|
||||
if event.is_empty():
|
||||
failures.append("Wan Castle Escape missing event: %s" % event_id)
|
||||
continue
|
||||
if not _event_has_dialogue_action(event):
|
||||
failures.append("Wan Castle Escape event should include contextual dialogue: %s" % event_id)
|
||||
var zhang_xiu_event := _event_by_id(state, "zhang_xiu_vanguard_falls")
|
||||
if not zhang_xiu_event.is_empty():
|
||||
var when: Dictionary = zhang_xiu_event.get("when", {})
|
||||
if str(when.get("type", "")) != "unit_defeated":
|
||||
failures.append("Zhang Xiu vanguard defeat event should trigger when the unit is defeated")
|
||||
var unit_ids: Array = when.get("unit_ids", [])
|
||||
if not unit_ids.has("zhang_xiu_vanguard"):
|
||||
failures.append("Zhang Xiu vanguard defeat event should watch zhang_xiu_vanguard")
|
||||
if not _event_has_set_objective_action(zhang_xiu_event):
|
||||
failures.append("Zhang Xiu vanguard defeat event should clarify the escape objective")
|
||||
var jia_xu_event := _event_by_id(state, "jia_xu_strategist_falls")
|
||||
if not jia_xu_event.is_empty():
|
||||
var when: Dictionary = jia_xu_event.get("when", {})
|
||||
if str(when.get("type", "")) != "unit_defeated":
|
||||
failures.append("Jia Xu strategist defeat event should trigger when the unit is defeated")
|
||||
var unit_ids: Array = when.get("unit_ids", [])
|
||||
if not unit_ids.has("jia_xu_strategist"):
|
||||
failures.append("Jia Xu strategist defeat event should watch jia_xu_strategist")
|
||||
if not _event_has_set_objective_action(jia_xu_event):
|
||||
failures.append("Jia Xu strategist defeat event should clarify the escape objective")
|
||||
var west_gate_event := _event_by_id(state, "west_gate_hunt_rider_falls")
|
||||
if not west_gate_event.is_empty():
|
||||
var when: Dictionary = west_gate_event.get("when", {})
|
||||
if str(when.get("type", "")) != "unit_defeated":
|
||||
failures.append("West gate hunt rider defeat event should trigger when the unit is defeated")
|
||||
var unit_ids: Array = when.get("unit_ids", [])
|
||||
if not unit_ids.has("west_gate_hunt_rider"):
|
||||
failures.append("West gate hunt rider defeat event should watch west_gate_hunt_rider")
|
||||
if not _event_has_set_objective_action(west_gate_event):
|
||||
failures.append("West gate hunt rider defeat event should clarify the final escape objective")
|
||||
for objective_event_id in [
|
||||
"turn_2_gate_closes",
|
||||
"midroad_trap",
|
||||
"turn_6_wan_crossfire",
|
||||
"turn_9_west_gate_hunt"
|
||||
]:
|
||||
var objective_event := _event_by_id(state, objective_event_id)
|
||||
if not objective_event.is_empty() and not _event_has_set_objective_action(objective_event):
|
||||
failures.append("Wan Castle Escape event should update the active objective: %s" % objective_event_id)
|
||||
|
||||
|
||||
func _check_opening_battle_story_beats(failures: Array[String]) -> void:
|
||||
var state = BattleStateScript.new()
|
||||
if not state.load_battle("res://data/scenarios/001_yellow_turbans.json"):
|
||||
@@ -865,6 +932,72 @@ func _check_xian_emperor_escort_story_beats(failures: Array[String]) -> void:
|
||||
failures.append("Final escort rider defeat objective should still point to the east road and remaining enemies")
|
||||
|
||||
|
||||
func _check_wan_castle_escape_story_beats(failures: Array[String]) -> void:
|
||||
var state = BattleStateScript.new()
|
||||
if not state.load_battle("res://data/scenarios/008_wan_castle_escape.json"):
|
||||
failures.append("could not load Wan Castle Escape for story beat checks")
|
||||
return
|
||||
|
||||
var dialogue_batches: Array = []
|
||||
state.dialogue_requested.connect(func(lines: Array) -> void:
|
||||
dialogue_batches.append(lines)
|
||||
)
|
||||
|
||||
state._run_events("turn_start", BattleStateScript.TEAM_ENEMY, 2)
|
||||
var cao_cao: Dictionary = state.get_unit("cao_cao_ch8")
|
||||
cao_cao["pos"] = Vector2i(8, 4)
|
||||
state._run_events("unit_reaches_tile", BattleStateScript.TEAM_PLAYER, 2, cao_cao)
|
||||
state._run_events("turn_start", BattleStateScript.TEAM_PLAYER, 6)
|
||||
state._run_events("turn_start", BattleStateScript.TEAM_ENEMY, 6)
|
||||
state._run_events("turn_start", BattleStateScript.TEAM_ENEMY, 9)
|
||||
|
||||
if dialogue_batches.size() < 5:
|
||||
failures.append("Wan Castle Escape should add dialogue beats for each major escape pressure phase")
|
||||
var victory_text := str(state.objectives.get("victory", ""))
|
||||
if not victory_text.contains("서쪽 탈출로") or not victory_text.contains("마지막 추격대"):
|
||||
failures.append("Wan Castle Escape turn 9 event should update the victory objective for the west gate hunt")
|
||||
if not state._condition_gate_open({"after_event": "turn_9_west_gate_hunt"}):
|
||||
failures.append("Wan Castle Escape final objective gate should open after the turn 9 west gate hunt")
|
||||
if int(state.get_unit("cao_ang_ch8").get("ai_target_priority", 0)) != 14:
|
||||
failures.append("Wan Castle Escape final pressure should keep Cao Ang under threat")
|
||||
|
||||
var zhang_xiu: Dictionary = state.get_unit("zhang_xiu_vanguard")
|
||||
if zhang_xiu.is_empty():
|
||||
failures.append("Wan Castle Escape should include Zhang Xiu vanguard for the trap story beat")
|
||||
else:
|
||||
zhang_xiu["alive"] = false
|
||||
state._run_events("unit_defeated", BattleStateScript.TEAM_ENEMY, 9, zhang_xiu)
|
||||
if dialogue_batches.size() < 6:
|
||||
failures.append("Wan Castle Escape should show dialogue when Zhang Xiu vanguard falls")
|
||||
var zhang_xiu_objective_text := str(state.objectives.get("victory", ""))
|
||||
if not zhang_xiu_objective_text.contains("조앙") or not zhang_xiu_objective_text.contains("서쪽 탈출로"):
|
||||
failures.append("Zhang Xiu vanguard defeat objective should still point to Cao Ang and the escape road")
|
||||
|
||||
var jia_xu: Dictionary = state.get_unit("jia_xu_strategist")
|
||||
if jia_xu.is_empty():
|
||||
failures.append("Wan Castle Escape should include Jia Xu strategist for the ambush story beat")
|
||||
else:
|
||||
jia_xu["alive"] = false
|
||||
state._run_events("unit_defeated", BattleStateScript.TEAM_ENEMY, 9, jia_xu)
|
||||
if dialogue_batches.size() < 7:
|
||||
failures.append("Wan Castle Escape should show dialogue when Jia Xu strategist falls")
|
||||
var jia_xu_objective_text := str(state.objectives.get("victory", ""))
|
||||
if not jia_xu_objective_text.contains("조앙") or not jia_xu_objective_text.contains("서쪽 탈출로"):
|
||||
failures.append("Jia Xu strategist defeat objective should still point to Cao Ang and the escape road")
|
||||
|
||||
var west_gate_rider: Dictionary = state.get_unit("west_gate_hunt_rider")
|
||||
if west_gate_rider.is_empty():
|
||||
failures.append("Wan Castle Escape should include west gate hunt rider for the final escape story beat")
|
||||
else:
|
||||
west_gate_rider["alive"] = false
|
||||
state._run_events("unit_defeated", BattleStateScript.TEAM_ENEMY, 9, west_gate_rider)
|
||||
if dialogue_batches.size() < 8:
|
||||
failures.append("Wan Castle Escape should show dialogue when west gate hunt rider falls")
|
||||
var west_gate_objective_text := str(state.objectives.get("victory", ""))
|
||||
if not west_gate_objective_text.contains("서쪽 탈출로") or not west_gate_objective_text.contains("완성 추격대"):
|
||||
failures.append("West gate hunt rider defeat objective should still point to the escape road and remaining pursuers")
|
||||
|
||||
|
||||
func _event_by_id(state, event_id: String) -> Dictionary:
|
||||
for event in state.battle_events:
|
||||
if typeof(event) == TYPE_DICTIONARY and str(event.get("id", "")) == event_id:
|
||||
|
||||
Reference in New Issue
Block a user