Add Xiakou camp and Red Cliffs bridge

This commit is contained in:
2026-06-19 04:02:24 +09:00
parent b48ef639ea
commit 0073f049fe
4 changed files with 354 additions and 2 deletions

View File

@@ -32,6 +32,59 @@
"Jiangling's secured grain and ledgers support a steadier river approach, giving Cao Cao's officers confidence that the army can feed a fleet."
]
}
],
"camp_conversations": [
{
"id": "xiakou_harbor_council",
"group": "topic",
"label": "Harbor Council",
"speaker": "Cao Cao",
"summary": "Set the harbor road's purpose before Xiakou turns Liu Bei's flight into river diplomacy.",
"lines": [
{ "speaker": "Cao Cao", "side": "left", "text": "Xiakou is where roads become water. If Liu Bei reaches the docks cleanly, his flight earns ships." },
{ "speaker": "Guo Jia", "side": "right", "text": "Then we make the harbor answer to steel before it answers to envoys." },
{ "speaker": "Zhang He", "side": "right", "text": "The guards watch east as often as north. Men who wait for allies rarely stand firmly alone." }
]
},
{
"id": "xiakou_zhang_he_river_mouth",
"group": "officer",
"officer_id": "zhang_he",
"label": "Zhang He - River Mouth",
"summary": "Hear Zhang He's reading of Xiakou's harbor guard before the river campaign opens.",
"lines": [
{ "speaker": "Zhang He", "side": "right", "text": "The harbor guard is stretched between the road and the river. Push one side and the other will shout too late." },
{ "speaker": "Cao Cao", "side": "left", "text": "Then press the side that still thinks help is coming." }
]
},
{
"id": "xiakou_envoy_chase_beans",
"group": "topic",
"label": "Envoy Chase Beans",
"summary": "Fast Jiangling columns keep travel beans ready for the soldiers chasing Liu Bei's envoys.",
"campaign_flags": { "pressed_xiakou_pursuit": true },
"effects": [
{ "type": "grant_item", "item_id": "bean", "count": 1, "text": "The envoy chase beans add a Bean to the field supplies." }
],
"lines": [
{ "speaker": "Xiahou Dun", "side": "left", "text": "The riders chasing the envoys kept beans in their sleeves and river mud on their boots." },
{ "speaker": "Cao Cao", "side": "right", "text": "Give them to the men nearest the docks. Diplomacy runs poorly on an empty stomach." }
]
},
{
"id": "xiakou_storehouse_medicine",
"group": "topic",
"label": "Storehouse Medicine",
"summary": "Secured Jiangling stores send clean medicine into the Xiakou harbor road.",
"campaign_flags": { "secured_jiangling_storehouses": true },
"effects": [
{ "type": "grant_item", "item_id": "panacea", "count": 1, "text": "The Jiangling storehouse medicine adds a Panacea to the field supplies." }
],
"lines": [
{ "speaker": "Cao Ren", "side": "left", "text": "Jiangling's ledgers were not all grain. The medicine chests reached Xiakou under the same seal." },
{ "speaker": "Cao Cao", "side": "right", "text": "Good. A fleet campaign begins with men who can still stand on shore." }
]
}
]
},
"shop": {
@@ -55,7 +108,14 @@
"campaign_flags": { "secured_jiangling_storehouses": true },
"items": ["imperial_seal"]
}
]
],
"merchant": {
"name": "Xiakou Harbor Sutler",
"lines": [
"Xiakou sells rope, oars, and promises, my lord. Mine are the only goods that do not drift.",
"Buy before the harbor clerks decide whether courage should be counted by boat or by blade."
]
}
},
"roster": {
"max_units": 7,

View File

@@ -59,7 +59,7 @@
},
"roster": {
"max_units": 7,
"required_officers": ["cao_cao", "guo_jia"]
"required_officers": ["cao_cao", "guo_jia", "zhang_he"]
},
"formation": {
"cells": [[1, 3], [1, 4], [1, 5], [2, 2], [2, 3], [2, 4], [2, 5], [2, 6], [3, 2], [3, 3], [3, 4], [3, 5], [3, 6]]

View File

@@ -106,6 +106,10 @@ const JIANGLING_FAST_BEANS_CONVERSATION_ID := "jiangling_fast_column_beans"
const JIANGLING_CROSSING_MEDICINE_CONVERSATION_ID := "jiangling_crossing_medicine"
const XIAKOU_SCENARIO_ID := "028_xiakou_pursuit"
const XIAKOU_SCENARIO_PATH := "res://data/scenarios/028_xiakou_pursuit.json"
const XIAKOU_ENVOY_BEANS_CONVERSATION_ID := "xiakou_envoy_chase_beans"
const XIAKOU_STOREHOUSE_MEDICINE_CONVERSATION_ID := "xiakou_storehouse_medicine"
const RED_CLIFFS_SCENARIO_ID := "029_red_cliffs_fleet"
const RED_CLIFFS_SCENARIO_PATH := "res://data/scenarios/029_red_cliffs_fleet.json"
const SAVE_PATH := "user://campaign_save.json"
const MANUAL_SAVE_PATH := "user://campaign_manual_save.json"
@@ -142,6 +146,7 @@ func _init() -> void:
_check_xinye_post_battle_choice_bridges_to_changban(failures)
_check_changban_post_battle_choice_bridges_to_jiangling(failures)
_check_jiangling_post_battle_choice_bridges_to_xiakou(failures)
_check_xiakou_post_battle_choice_bridges_to_red_cliffs(failures)
_check_manual_checkpoint_reverts_claim(failures)
_check_completed_replay_cannot_claim(failures)
_check_conditional_camp_conversations(failures)
@@ -161,6 +166,7 @@ func _init() -> void:
_check_xinye_branch_camp_conversations(failures)
_check_changban_branch_camp_conversations(failures)
_check_jiangling_branch_camp_conversations(failures)
_check_xiakou_branch_camp_conversations(failures)
_check_talk_menu_closes_on_cancel_and_reload(failures)
if not _restore_user_file(SAVE_PATH, save_backup):
@@ -1324,6 +1330,25 @@ func _check_jiangling_post_battle_choice_bridges_to_xiakou(failures: Array[Strin
)
func _check_xiakou_post_battle_choice_bridges_to_red_cliffs(failures: Array[String]) -> void:
_check_xiakou_choice_branch_to_red_cliffs(
failures,
"press_red_cliffs_fleet",
"pressed_red_cliffs_fleet",
"secured_xiakou_harbor",
"war_drum",
"imperial_seal"
)
_check_xiakou_choice_branch_to_red_cliffs(
failures,
"secure_xiakou_harbor",
"secured_xiakou_harbor",
"pressed_red_cliffs_fleet",
"imperial_seal",
"war_drum"
)
func _check_wuchao_choice_branch_to_cangting(
failures: Array[String],
choice_id: String,
@@ -2419,6 +2444,79 @@ func _check_jiangling_choice_branch_to_xiakou(
scene.free()
func _check_xiakou_choice_branch_to_red_cliffs(
failures: Array[String],
choice_id: String,
expected_flag: String,
cleared_flag: String,
expected_shop_item_id: String,
hidden_shop_item_id: String
) -> void:
var scene = _new_prebattle_scene_for(failures, "Xiakou choice bridge %s" % choice_id, XIAKOU_SCENARIO_ID, XIAKOU_SCENARIO_PATH)
if scene == null:
return
if not scene.campaign_state.joined_officers.has("zhang_he"):
scene.campaign_state.joined_officers.append("zhang_he")
if not scene.state.load_battle(
XIAKOU_SCENARIO_PATH,
scene.campaign_state.get_roster_overrides(),
scene.campaign_state.get_inventory_snapshot(),
scene.campaign_state.get_flags_snapshot(),
scene.campaign_state.get_joined_officers_snapshot()
):
failures.append("Xiakou choice bridge %s could not reload Xiakou with Zhang He joined" % choice_id)
scene.free()
return
scene.state.battle_status = "victory"
var result: Dictionary = scene.campaign_state.apply_battle_result(scene.state)
if not bool(result.get("saved", false)):
failures.append("Xiakou choice bridge %s should save 028 victory" % choice_id)
scene.free()
return
if not scene.campaign_state.get_joined_officers_snapshot().has("zhang_he"):
failures.append("Xiakou choice bridge %s should keep Zhang He before Red Cliffs" % choice_id)
if bool(result.get("choice_applied", true)):
failures.append("Xiakou choice bridge %s should wait for a selected post-battle choice" % choice_id)
if scene.campaign_state.pending_post_battle_choice_scenario_id != XIAKOU_SCENARIO_ID:
failures.append("Xiakou choice bridge %s should mark Xiakou choice pending" % choice_id)
var choice := _find_choice_by_id(result.get("post_battle_choices", []), choice_id)
if choice.is_empty():
failures.append("Xiakou choice bridge missing choice: %s" % choice_id)
scene.free()
return
if not scene.campaign_state.try_apply_post_battle_choice(choice, XIAKOU_SCENARIO_ID):
failures.append("Xiakou choice bridge %s should save selected choice" % choice_id)
scene.free()
return
var flags: Dictionary = scene.campaign_state.get_flags_snapshot()
if flags.get(expected_flag, false) != true:
failures.append("Xiakou choice bridge %s should set %s" % [choice_id, expected_flag])
if flags.get(cleared_flag, true) != false:
failures.append("Xiakou choice bridge %s should clear %s" % [choice_id, cleared_flag])
if scene.campaign_state.current_scenario_id != RED_CLIFFS_SCENARIO_ID:
failures.append("Xiakou choice bridge %s should advance to Red Cliffs, got %s" % [choice_id, scene.campaign_state.current_scenario_id])
if not scene.state.load_battle(
RED_CLIFFS_SCENARIO_PATH,
scene.campaign_state.get_roster_overrides(),
scene.campaign_state.get_inventory_snapshot(),
flags,
scene.campaign_state.get_joined_officers_snapshot()
):
failures.append("Xiakou choice bridge %s could not load Red Cliffs" % choice_id)
scene.free()
return
if scene.state.get_unit("zhang_he_ch29").is_empty():
failures.append("Xiakou choice bridge %s should deploy Zhang He in Red Cliffs" % choice_id)
elif not scene.state.is_required_deployment("zhang_he_ch29"):
failures.append("Xiakou choice bridge %s should require Zhang He in Red Cliffs" % choice_id)
var shop_items: Array = scene.state.get_shop_item_ids()
if not shop_items.has(expected_shop_item_id):
failures.append("Xiakou choice bridge %s should expose shop item %s" % [choice_id, expected_shop_item_id])
if shop_items.has(hidden_shop_item_id):
failures.append("Xiakou choice bridge %s should hide shop item %s" % [choice_id, hidden_shop_item_id])
scene.free()
func _find_choice_by_id(choices, choice_id: String) -> Dictionary:
if typeof(choices) != TYPE_ARRAY:
return {}
@@ -3909,6 +4007,121 @@ func _check_jiangling_branch_camp_conversations(failures: Array[String]) -> void
crossings_scene.free()
func _check_xiakou_branch_camp_conversations(failures: Array[String]) -> void:
var base_scene = _new_prebattle_scene_for(failures, "Xiakou base camp", XIAKOU_SCENARIO_ID, XIAKOU_SCENARIO_PATH)
if base_scene != null:
if not base_scene.campaign_state.joined_officers.has("zhang_he"):
base_scene.campaign_state.joined_officers.append("zhang_he")
if not base_scene.state.load_battle(
XIAKOU_SCENARIO_PATH,
base_scene.campaign_state.get_roster_overrides(),
base_scene.campaign_state.get_inventory_snapshot(),
base_scene.campaign_state.get_flags_snapshot(),
base_scene.campaign_state.get_joined_officers_snapshot()
):
failures.append("Xiakou base camp should reload with Zhang He joined")
else:
if base_scene._camp_conversation_by_id("xiakou_harbor_council").is_empty():
failures.append("Xiakou should expose the harbor council conversation")
if base_scene._camp_conversation_by_id("xiakou_zhang_he_river_mouth").is_empty():
failures.append("Xiakou should expose Zhang He's river mouth conversation")
if not base_scene._camp_conversation_by_id(XIAKOU_ENVOY_BEANS_CONVERSATION_ID).is_empty():
failures.append("Xiakou envoy beans should be hidden without pressed Xiakou flag")
if not base_scene._camp_conversation_by_id(XIAKOU_STOREHOUSE_MEDICINE_CONVERSATION_ID).is_empty():
failures.append("Xiakou storehouse medicine should be hidden without secured Jiangling flag")
var merchant: Dictionary = base_scene.state.get_shop_merchant()
if str(merchant.get("name", "")) != "Xiakou Harbor Sutler":
failures.append("Xiakou shop merchant name should be present")
var merchant_lines: Array = merchant.get("lines", [])
if merchant_lines.size() < 2:
failures.append("Xiakou shop merchant should expose flavor lines")
if base_scene._format_talk_status_text(base_scene._camp_conversation_entries()) != "Camp conversations | 2 topics":
failures.append("Xiakou base talk status should summarize two non-supply topics")
base_scene.free()
var pursuit_scene = _new_prebattle_scene_for(
failures,
"Xiakou pressed pursuit camp",
XIAKOU_SCENARIO_ID,
XIAKOU_SCENARIO_PATH,
{"pressed_xiakou_pursuit": true}
)
if pursuit_scene != null:
if not pursuit_scene.campaign_state.joined_officers.has("zhang_he"):
pursuit_scene.campaign_state.joined_officers.append("zhang_he")
if not pursuit_scene.state.load_battle(
XIAKOU_SCENARIO_PATH,
pursuit_scene.campaign_state.get_roster_overrides(),
pursuit_scene.campaign_state.get_inventory_snapshot(),
pursuit_scene.campaign_state.get_flags_snapshot(),
pursuit_scene.campaign_state.get_joined_officers_snapshot()
):
failures.append("Xiakou pressed pursuit camp should reload with Zhang He joined")
else:
var envoy_beans: Dictionary = pursuit_scene._camp_conversation_by_id(XIAKOU_ENVOY_BEANS_CONVERSATION_ID)
if envoy_beans.is_empty():
failures.append("pressed Xiakou pursuit flag should expose Xiakou envoy beans")
if not pursuit_scene._camp_conversation_by_id(XIAKOU_STOREHOUSE_MEDICINE_CONVERSATION_ID).is_empty():
failures.append("pressed Xiakou pursuit flag should not expose Xiakou storehouse medicine")
if not envoy_beans.is_empty():
if pursuit_scene._format_talk_status_text(pursuit_scene._camp_conversation_entries()) != "Camp conversations | 3 topics | 1 supplies ready":
failures.append("Xiakou pursuit talk status should show one ready supply")
if not pursuit_scene._format_camp_conversation_button_text(envoy_beans).contains("Supply Bean"):
failures.append("Xiakou envoy beans button should show Bean supply")
var before_bean := int(pursuit_scene.campaign_state.get_inventory_snapshot().get("bean", 0))
pursuit_scene._apply_camp_conversation_effects(envoy_beans)
if int(pursuit_scene.campaign_state.get_inventory_snapshot().get("bean", 0)) != before_bean + 1:
failures.append("Xiakou envoy beans should add Bean to campaign inventory")
if int(pursuit_scene.state.get_inventory_snapshot().get("bean", 0)) != before_bean + 1:
failures.append("Xiakou envoy beans should refresh battle Bean inventory")
if not pursuit_scene.campaign_state.has_claimed_camp_conversation_effects(XIAKOU_SCENARIO_ID, XIAKOU_ENVOY_BEANS_CONVERSATION_ID):
failures.append("Xiakou envoy beans claim should be saved")
if pursuit_scene._format_talk_status_text(pursuit_scene._camp_conversation_entries()) != "Camp conversations | 3 topics | 1 claimed":
failures.append("Xiakou pursuit talk status should show claimed supply after claim")
pursuit_scene.free()
var storehouse_scene = _new_prebattle_scene_for(
failures,
"Xiakou secured Jiangling camp",
XIAKOU_SCENARIO_ID,
XIAKOU_SCENARIO_PATH,
{"secured_jiangling_storehouses": true}
)
if storehouse_scene != null:
if not storehouse_scene.campaign_state.joined_officers.has("zhang_he"):
storehouse_scene.campaign_state.joined_officers.append("zhang_he")
if not storehouse_scene.state.load_battle(
XIAKOU_SCENARIO_PATH,
storehouse_scene.campaign_state.get_roster_overrides(),
storehouse_scene.campaign_state.get_inventory_snapshot(),
storehouse_scene.campaign_state.get_flags_snapshot(),
storehouse_scene.campaign_state.get_joined_officers_snapshot()
):
failures.append("Xiakou secured Jiangling camp should reload with Zhang He joined")
else:
var storehouse_medicine: Dictionary = storehouse_scene._camp_conversation_by_id(XIAKOU_STOREHOUSE_MEDICINE_CONVERSATION_ID)
if storehouse_medicine.is_empty():
failures.append("secured Jiangling storehouses flag should expose Xiakou storehouse medicine")
if not storehouse_scene._camp_conversation_by_id(XIAKOU_ENVOY_BEANS_CONVERSATION_ID).is_empty():
failures.append("secured Jiangling storehouses flag should not expose Xiakou envoy beans")
if not storehouse_medicine.is_empty():
if storehouse_scene._format_talk_status_text(storehouse_scene._camp_conversation_entries()) != "Camp conversations | 3 topics | 1 supplies ready":
failures.append("Xiakou storehouse talk status should show one ready supply")
if not storehouse_scene._format_camp_conversation_button_text(storehouse_medicine).contains("Supply Panacea"):
failures.append("Xiakou storehouse medicine button should show Panacea supply")
var before_panacea := int(storehouse_scene.campaign_state.get_inventory_snapshot().get("panacea", 0))
storehouse_scene._apply_camp_conversation_effects(storehouse_medicine)
if int(storehouse_scene.campaign_state.get_inventory_snapshot().get("panacea", 0)) != before_panacea + 1:
failures.append("Xiakou storehouse medicine should add Panacea to campaign inventory")
if int(storehouse_scene.state.get_inventory_snapshot().get("panacea", 0)) != before_panacea + 1:
failures.append("Xiakou storehouse medicine should refresh battle Panacea inventory")
if not storehouse_scene.campaign_state.has_claimed_camp_conversation_effects(XIAKOU_SCENARIO_ID, XIAKOU_STOREHOUSE_MEDICINE_CONVERSATION_ID):
failures.append("Xiakou storehouse medicine claim should be saved")
if storehouse_scene._format_talk_status_text(storehouse_scene._camp_conversation_entries()) != "Camp conversations | 3 topics | 1 claimed":
failures.append("Xiakou storehouse talk status should show claimed supply after claim")
storehouse_scene.free()
func _check_talk_menu_closes_on_cancel_and_reload(failures: Array[String]) -> void:
var scene = _new_prebattle_scene(failures, "talk menu close guards")
if scene == null:

View File

@@ -1829,6 +1829,17 @@ func _check_battle_visual_data(failures: Array[String]) -> void:
failures.append("could not load Xiakou Pursuit data")
else:
_check_shop_items_unique(failures, xiakou_state, "028 base")
var xiakou_conversations: Array = xiakou_state.get_briefing().get("camp_conversations", [])
if xiakou_conversations.size() != 2:
failures.append("028 base briefing should expose exactly two camp conversations")
else:
_check_camp_conversation(failures, xiakou_conversations[0], "xiakou_harbor_council", "topic", "")
_check_camp_conversation(failures, xiakou_conversations[1], "xiakou_zhang_he_river_mouth", "officer", "zhang_he")
var xiakou_merchant := xiakou_state.get_shop_merchant()
if str(xiakou_merchant.get("name", "")) != "Xiakou Harbor Sutler":
failures.append("028 shop should expose Xiakou Harbor merchant name")
if (xiakou_merchant.get("lines", []) as Array).size() < 2:
failures.append("028 shop should expose merchant flavor lines")
if xiakou_state.get_unit("zhang_he_ch28").is_empty() or not xiakou_state.is_required_deployment("zhang_he_ch28"):
failures.append("028 should deploy Zhang He as a required officer")
_check_shop_item_visibility(failures, xiakou_state, "war_drum", false, "028 base")
@@ -1838,6 +1849,19 @@ func _check_battle_visual_data(failures: Array[String]) -> void:
if not xiakou_pursuit_state.load_battle("res://data/scenarios/028_xiakou_pursuit.json", {}, {}, {"pressed_xiakou_pursuit": true}, ["zhang_he"]):
failures.append("could not load Xiakou pressed pursuit data")
else:
var envoy_beans: Dictionary = _find_camp_conversation(
xiakou_pursuit_state.get_briefing().get("camp_conversations", []),
"xiakou_envoy_chase_beans"
)
if envoy_beans.is_empty():
failures.append("028 pressed Xiakou pursuit should expose envoy chase beans")
else:
_check_camp_conversation_effect(failures, envoy_beans, "bean", 1)
if not _find_camp_conversation(
xiakou_pursuit_state.get_briefing().get("camp_conversations", []),
"xiakou_storehouse_medicine"
).is_empty():
failures.append("028 pressed Xiakou pursuit should not expose storehouse medicine")
_check_shop_item_visibility(failures, xiakou_pursuit_state, "war_drum", true, "028 pressed Xiakou pursuit")
_check_shop_item_visibility(failures, xiakou_pursuit_state, "imperial_seal", false, "028 pressed Xiakou pursuit")
@@ -1845,6 +1869,19 @@ func _check_battle_visual_data(failures: Array[String]) -> void:
if not xiakou_store_state.load_battle("res://data/scenarios/028_xiakou_pursuit.json", {}, {}, {"secured_jiangling_storehouses": true}, ["zhang_he"]):
failures.append("could not load Xiakou secured Jiangling stores data")
else:
var storehouse_medicine: Dictionary = _find_camp_conversation(
xiakou_store_state.get_briefing().get("camp_conversations", []),
"xiakou_storehouse_medicine"
)
if storehouse_medicine.is_empty():
failures.append("028 secured Jiangling stores should expose storehouse medicine")
else:
_check_camp_conversation_effect(failures, storehouse_medicine, "panacea", 1)
if not _find_camp_conversation(
xiakou_store_state.get_briefing().get("camp_conversations", []),
"xiakou_envoy_chase_beans"
).is_empty():
failures.append("028 secured Jiangling stores should not expose envoy chase beans")
_check_shop_item_visibility(failures, xiakou_store_state, "imperial_seal", true, "028 secured Jiangling stores")
_check_shop_item_visibility(failures, xiakou_store_state, "war_drum", false, "028 secured Jiangling stores")
@@ -1855,6 +1892,48 @@ func _check_battle_visual_data(failures: Array[String]) -> void:
_check_shop_items_unique(failures, xiakou_old_flags_state, "028 old Jiangling setup flags")
_check_shop_item_visibility(failures, xiakou_old_flags_state, "war_drum", false, "028 old Jiangling setup flags")
_check_shop_item_visibility(failures, xiakou_old_flags_state, "imperial_seal", false, "028 old Jiangling setup flags")
if not _find_camp_conversation(
xiakou_old_flags_state.get_briefing().get("camp_conversations", []),
"xiakou_envoy_chase_beans"
).is_empty():
failures.append("028 old Jiangling setup flags should not expose envoy chase beans")
if not _find_camp_conversation(
xiakou_old_flags_state.get_briefing().get("camp_conversations", []),
"xiakou_storehouse_medicine"
).is_empty():
failures.append("028 old Jiangling setup flags should not expose storehouse medicine")
var red_cliffs_state = BattleStateScript.new()
if not red_cliffs_state.load_battle("res://data/scenarios/029_red_cliffs_fleet.json", {}, {}, {}, ["zhang_he"]):
failures.append("could not load Red Cliffs Fleet data")
else:
_check_shop_items_unique(failures, red_cliffs_state, "029 base")
if red_cliffs_state.get_unit("zhang_he_ch29").is_empty() or not red_cliffs_state.is_required_deployment("zhang_he_ch29"):
failures.append("029 should deploy Zhang He as a required officer")
_check_shop_item_visibility(failures, red_cliffs_state, "war_drum", false, "029 base")
_check_shop_item_visibility(failures, red_cliffs_state, "imperial_seal", false, "029 base")
var red_cliffs_pressed_state = BattleStateScript.new()
if not red_cliffs_pressed_state.load_battle("res://data/scenarios/029_red_cliffs_fleet.json", {}, {}, {"pressed_red_cliffs_fleet": true}, ["zhang_he"]):
failures.append("could not load Red Cliffs pressed fleet data")
else:
_check_shop_item_visibility(failures, red_cliffs_pressed_state, "war_drum", true, "029 pressed Red Cliffs fleet")
_check_shop_item_visibility(failures, red_cliffs_pressed_state, "imperial_seal", false, "029 pressed Red Cliffs fleet")
var red_cliffs_harbor_state = BattleStateScript.new()
if not red_cliffs_harbor_state.load_battle("res://data/scenarios/029_red_cliffs_fleet.json", {}, {}, {"secured_xiakou_harbor": true}, ["zhang_he"]):
failures.append("could not load Red Cliffs secured Xiakou harbor data")
else:
_check_shop_item_visibility(failures, red_cliffs_harbor_state, "imperial_seal", true, "029 secured Xiakou harbor")
_check_shop_item_visibility(failures, red_cliffs_harbor_state, "war_drum", false, "029 secured Xiakou harbor")
var red_cliffs_old_flags_state = BattleStateScript.new()
if not red_cliffs_old_flags_state.load_battle("res://data/scenarios/029_red_cliffs_fleet.json", {}, {}, {"pressed_xiakou_pursuit": true, "secured_jiangling_storehouses": true}, ["zhang_he"]):
failures.append("could not load Red Cliffs old Xiakou setup flag data")
else:
_check_shop_items_unique(failures, red_cliffs_old_flags_state, "029 old Xiakou setup flags")
_check_shop_item_visibility(failures, red_cliffs_old_flags_state, "war_drum", false, "029 old Xiakou setup flags")
_check_shop_item_visibility(failures, red_cliffs_old_flags_state, "imperial_seal", false, "029 old Xiakou setup flags")
for item_id in ["bronze_sword", "training_spear", "short_bow", "hand_axe", "iron_armor", "panacea"]:
var item := state.get_item_def(item_id)