Add story beats to Xian Emperor Escort

This commit is contained in:
2026-06-19 16:40:22 +09:00
parent 04c3f9d615
commit 2d4a3a4a51
3 changed files with 156 additions and 2 deletions

View File

@@ -29,12 +29,14 @@ func _init() -> void:
_check_qingzhou_campaign_event_dialogue_structure(failures)
_check_puyang_raid_event_dialogue_structure(failures)
_check_dingtao_counterattack_event_dialogue_structure(failures)
_check_xian_emperor_escort_event_dialogue_structure(failures)
_check_opening_battle_story_beats(failures)
_check_sishui_gate_story_beats(failures)
_check_xingyang_ambush_story_beats(failures)
_check_qingzhou_campaign_story_beats(failures)
_check_puyang_raid_story_beats(failures)
_check_dingtao_counterattack_story_beats(failures)
_check_xian_emperor_escort_story_beats(failures)
_check_pixel_unit_helpers(failures)
if failures.is_empty():
@@ -507,6 +509,57 @@ func _check_dingtao_counterattack_event_dialogue_structure(failures: Array[Strin
failures.append("Dingtao Counterattack second charge event should clarify the final objective")
func _check_xian_emperor_escort_event_dialogue_structure(failures: Array[String]) -> void:
var state = BattleStateScript.new()
if not state.load_battle("res://data/scenarios/007_xian_emperor_escort.json"):
failures.append("could not load Xian Emperor Escort for event dialogue structure")
return
for event_id in [
"opening_dialogue",
"turn_3_rear_pressure",
"midroad_ambush",
"turn_5_road_marker",
"turn_6_envoy_pincher",
"turn_9_final_escort_pressure",
"li_jue_vanguard_falls",
"final_escort_rider_falls"
]:
var event := _event_by_id(state, event_id)
if event.is_empty():
failures.append("Xian Emperor Escort missing event: %s" % event_id)
continue
if not _event_has_dialogue_action(event):
failures.append("Xian Emperor Escort event should include contextual dialogue: %s" % event_id)
var li_jue_event := _event_by_id(state, "li_jue_vanguard_falls")
if not li_jue_event.is_empty():
var when: Dictionary = li_jue_event.get("when", {})
if str(when.get("type", "")) != "unit_defeated":
failures.append("Li Jue vanguard defeat event should trigger when the unit is defeated")
var unit_ids: Array = when.get("unit_ids", [])
if not unit_ids.has("li_jue_vanguard"):
failures.append("Li Jue vanguard defeat event should watch li_jue_vanguard")
if not _event_has_set_objective_action(li_jue_event):
failures.append("Li Jue vanguard defeat event should clarify the escort objective")
var final_rider_event := _event_by_id(state, "final_escort_rider_falls")
if not final_rider_event.is_empty():
var when: Dictionary = final_rider_event.get("when", {})
if str(when.get("type", "")) != "unit_defeated":
failures.append("Final escort rider defeat event should trigger when the unit is defeated")
var unit_ids: Array = when.get("unit_ids", [])
if not unit_ids.has("final_escort_rider"):
failures.append("Final escort rider defeat event should watch final_escort_rider")
if not _event_has_set_objective_action(final_rider_event):
failures.append("Final escort rider defeat event should clarify the final escort objective")
for objective_event_id in [
"turn_3_rear_pressure",
"midroad_ambush",
"turn_9_final_escort_pressure"
]:
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("Xian Emperor Escort 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"):
@@ -758,6 +811,60 @@ func _check_dingtao_counterattack_story_beats(failures: Array[String]) -> void:
failures.append("Lu Bu second charge break objective should still point to the center and remaining charge")
func _check_xian_emperor_escort_story_beats(failures: Array[String]) -> void:
var state = BattleStateScript.new()
if not state.load_battle("res://data/scenarios/007_xian_emperor_escort.json"):
failures.append("could not load Xian Emperor Escort 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, 3)
var cao_cao: Dictionary = state.get_unit("cao_cao_ch7")
cao_cao["pos"] = Vector2i(7, 4)
state._run_events("unit_reaches_tile", BattleStateScript.TEAM_PLAYER, 3, cao_cao)
state._run_events("turn_start", BattleStateScript.TEAM_PLAYER, 5)
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("Xian Emperor Escort should add dialogue beats for each major escort pressure phase")
var victory_text := str(state.objectives.get("victory", ""))
if not victory_text.contains("동도 호송로") or not victory_text.contains("마지막 잔당"):
failures.append("Xian Emperor Escort turn 9 event should update the victory objective for final escort pressure")
if not state._condition_gate_open({"after_event": "turn_9_final_escort_pressure"}):
failures.append("Xian Emperor Escort final objective gate should open after the turn 9 final pressure")
if int(state.get_unit("imperial_envoy_ch7").get("ai_target_priority", 0)) != 12:
failures.append("Xian Emperor Escort final pressure should keep the imperial envoy under threat")
var li_jue: Dictionary = state.get_unit("li_jue_vanguard")
if li_jue.is_empty():
failures.append("Xian Emperor Escort should include Li Jue vanguard for the road-opening story beat")
else:
li_jue["alive"] = false
state._run_events("unit_defeated", BattleStateScript.TEAM_ENEMY, 9, li_jue)
if dialogue_batches.size() < 6:
failures.append("Xian Emperor Escort should show dialogue when Li Jue vanguard falls")
var li_jue_objective_text := str(state.objectives.get("victory", ""))
if not li_jue_objective_text.contains("천자 사자") or not li_jue_objective_text.contains("동도 호송로"):
failures.append("Li Jue vanguard defeat objective should still point to the envoy and east road")
var final_rider: Dictionary = state.get_unit("final_escort_rider")
if final_rider.is_empty():
failures.append("Xian Emperor Escort should include final escort rider for the last pressure story beat")
else:
final_rider["alive"] = false
state._run_events("unit_defeated", BattleStateScript.TEAM_ENEMY, 9, final_rider)
if dialogue_batches.size() < 7:
failures.append("Xian Emperor Escort should show dialogue when final escort rider falls")
var final_objective_text := str(state.objectives.get("victory", ""))
if not final_objective_text.contains("동도 호송로") or not final_objective_text.contains("잔당"):
failures.append("Final escort rider defeat objective should still point to the east road and remaining enemies")
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:

View File

@@ -32,12 +32,12 @@ func _check_condition_cells(failures: Array[String]) -> void:
if not objective_cells.has(Vector2i(2, 3)) or not objective_cells.has(Vector2i(2, 4)):
failures.append("objective cells missing multi-cell destination: %s" % str(objective_cells))
var progress: String = state.get_objective_progress_text(false)
if not progress.contains("(3, 4), (3, 5): not reached"):
if not progress.contains("전장 표식 (3, 4), (3, 5) · 미도달"):
failures.append("multi-cell progress text mismatch before reach: %s" % progress)
var cao_cao: Dictionary = state.get_unit("cao_cao")
cao_cao["pos"] = Vector2i(2, 4)
progress = state.get_objective_progress_text(false)
if not progress.contains("(3, 4), (3, 5): reached"):
if not progress.contains("전장 표식 (3, 4), (3, 5) · 도달"):
failures.append("multi-cell progress text mismatch after reach: %s" % progress)
if not state._is_condition_met(state.battle_conditions["victory"]):
failures.append("multi-cell unit_reaches_tile condition should be met")