diff --git a/data/scenarios/018_northern_pursuit.json b/data/scenarios/018_northern_pursuit.json index eb0dace..c1dd708 100644 --- a/data/scenarios/018_northern_pursuit.json +++ b/data/scenarios/018_northern_pursuit.json @@ -32,9 +32,116 @@ "Ye's administration now feeds the road behind Cao Cao, giving the pursuit fewer gaps for Yuan loyalists to exploit." ] } + ], + "camp_conversations": [ + { + "id": "northern_pursuit_road_council", + "group": "topic", + "label": "Northern Road Council", + "speaker": "Cao Cao", + "summary": "Set the pursuit order before Yuan Shang's banners disappear north.", + "lines": [ + { + "speaker": "Cao Cao", + "side": "left", + "text": "Yuan Shang still carries banners, but a banner on a fleeing road is only cloth unless men believe it can return." + }, + { + "speaker": "Guo Jia", + "side": "right", + "text": "Then the first blow must fall on belief. His baggage, his messengers, and the captains who still count tomorrow." + }, + { + "speaker": "Zhang He", + "side": "right", + "text": "The northern road narrows after the hill bend. If the rearguard forms there, they can only retreat through their own carts." + } + ] + }, + { + "id": "northern_pursuit_zhang_he_retreat_road", + "group": "officer", + "officer_id": "zhang_he", + "label": "Zhang He - Retreat Road", + "speaker": "Zhang He", + "summary": "Read Yuan Shang's retreat road before the chase begins.", + "lines": [ + { + "speaker": "Zhang He", + "side": "right", + "text": "Yuan Shang's men are marching by household loyalty, not command order. Their slowest wagons will decide where courage ends." + }, + { + "speaker": "Cao Cao", + "side": "left", + "text": "Then we chase the wagons and let the courage arrive late." + } + ] + }, + { + "id": "northern_pursuit_hard_march_beans", + "campaign_flags": { "pursued_yuan_shang": true }, + "group": "topic", + "label": "Hard March Beans", + "summary": "Claim compact rations carried by the fastest pursuit column.", + "lines": [ + { + "speaker": "Xiahou Yuan", + "side": "right", + "text": "The vanguard kept beans under the saddle cloths. They taste of leather, but they keep a man moving." + }, + { + "speaker": "Cao Cao", + "side": "left", + "text": "For a hard march, that is finer fare than a full table reached too late." + } + ], + "effects": [ + { + "type": "grant_item", + "item_id": "bean", + "count": 1, + "text": "The hard march beans add a Bean to the field supplies." + } + ] + }, + { + "id": "northern_pursuit_ji_supply_medicine", + "campaign_flags": { "stabilized_ye": true }, + "group": "topic", + "label": "Ji Supply Medicine", + "summary": "Claim medicine delivered along the steadied Ji Province road.", + "lines": [ + { + "speaker": "Cao Ren", + "side": "right", + "text": "Ji Province carts reached us with medicine sealed in plain boxes. No banners, no panic, just useful order." + }, + { + "speaker": "Guo Jia", + "side": "left", + "text": "A quiet road is a physician with wheels." + } + ], + "effects": [ + { + "type": "grant_item", + "item_id": "panacea", + "count": 1, + "text": "The Ji supply medicine adds a Panacea to the field supplies." + } + ] + } ] }, "shop": { + "merchant": { + "name": "Northern Road Sutler", + "lines": [ + "The road north buys speed first and comfort never.", + "Spend before the dust rises, my lord. A fleeing heir leaves broken carts, not orderly markets." + ] + }, "items": [ "bean", "wine", diff --git a/data/scenarios/019_nanpi_pressure.json b/data/scenarios/019_nanpi_pressure.json index 036b825..51cf885 100644 --- a/data/scenarios/019_nanpi_pressure.json +++ b/data/scenarios/019_nanpi_pressure.json @@ -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]] diff --git a/scripts/scenes/battle_scene.gd b/scripts/scenes/battle_scene.gd index 9df2861..1b80b61 100644 --- a/scripts/scenes/battle_scene.gd +++ b/scripts/scenes/battle_scene.gd @@ -531,7 +531,7 @@ func _create_hud() -> void: selected_row.add_child(selected_label) mission_title_label = Label.new() - mission_title_label.text = "War Order" + mission_title_label.text = "Battle Mandate" mission_title_label.add_theme_font_size_override("font_size", 14) _apply_label_style(mission_title_label, UI_OLD_BRONZE) side_column.add_child(mission_title_label) @@ -650,7 +650,7 @@ func _create_hud() -> void: post_move_menu.add_child(post_move_column) post_move_title_label = Label.new() - post_move_title_label.text = "Choose Action" + post_move_title_label.text = "Issue Order" post_move_title_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER _apply_label_style(post_move_title_label, UI_OLD_BRONZE, 14) post_move_column.add_child(post_move_title_label) @@ -663,7 +663,7 @@ func _create_hud() -> void: post_move_attack_button = Button.new() post_move_attack_button.text = "Attack" - post_move_attack_button.tooltip_text = "Choose an enemy target from this unit's current position." + post_move_attack_button.tooltip_text = "Mark an enemy from this unit's current position." post_move_attack_button.pressed.connect(_on_post_move_attack_pressed) post_move_grid.add_child(post_move_attack_button) @@ -686,7 +686,7 @@ func _create_hud() -> void: post_move_grid.add_child(post_move_wait_button) post_move_cancel_button = Button.new() - post_move_cancel_button.text = "Cancel Move" + post_move_cancel_button.text = "Recall Step" post_move_cancel_button.tooltip_text = "Return to the starting cell." post_move_cancel_button.pressed.connect(_on_post_move_cancel_pressed) post_move_column.add_child(post_move_cancel_button) @@ -704,7 +704,7 @@ func _create_hud() -> void: post_move_picker_panel.add_child(picker_column) post_move_picker_title_label = Label.new() - post_move_picker_title_label.text = "Choose" + post_move_picker_title_label.text = "Select Writ" post_move_picker_title_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER _apply_label_style(post_move_picker_title_label, UI_OLD_BRONZE, 14) picker_column.add_child(post_move_picker_title_label) @@ -730,13 +730,13 @@ func _create_hud() -> void: picker_column.add_child(picker_button_row) post_move_picker_back_button = Button.new() - post_move_picker_back_button.text = "Back" - post_move_picker_back_button.tooltip_text = "Return to the action menu." + post_move_picker_back_button.text = "Return" + post_move_picker_back_button.tooltip_text = "Return to the order menu." post_move_picker_back_button.pressed.connect(_on_post_move_picker_back_pressed) picker_button_row.add_child(post_move_picker_back_button) post_move_picker_cancel_move_button = Button.new() - post_move_picker_cancel_move_button.text = "Cancel Move" + post_move_picker_cancel_move_button.text = "Recall Step" post_move_picker_cancel_move_button.tooltip_text = "Right-click also returns to the starting cell." post_move_picker_cancel_move_button.pressed.connect(_on_post_move_picker_cancel_move_pressed) picker_button_row.add_child(post_move_picker_cancel_move_button) @@ -754,7 +754,7 @@ func _create_hud() -> void: targeting_hint_panel.add_child(targeting_column) targeting_hint_title_label = Label.new() - targeting_hint_title_label.text = "Choose Target" + targeting_hint_title_label.text = "Mark Foe" targeting_hint_title_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER _apply_label_style(targeting_hint_title_label, UI_OLD_BRONZE, 14) targeting_column.add_child(targeting_hint_title_label) @@ -770,13 +770,13 @@ func _create_hud() -> void: targeting_column.add_child(targeting_button_row) targeting_hint_back_button = Button.new() - targeting_hint_back_button.text = "Back" - targeting_hint_back_button.tooltip_text = "Return to the action menu." + targeting_hint_back_button.text = "Return" + targeting_hint_back_button.tooltip_text = "Return to the order menu." targeting_hint_back_button.pressed.connect(_on_targeting_back_pressed) targeting_button_row.add_child(targeting_hint_back_button) targeting_hint_cancel_move_button = Button.new() - targeting_hint_cancel_move_button.text = "Cancel Move" + targeting_hint_cancel_move_button.text = "Recall Step" targeting_hint_cancel_move_button.tooltip_text = "Return to the starting cell." targeting_hint_cancel_move_button.pressed.connect(_on_targeting_cancel_move_pressed) targeting_button_row.add_child(targeting_hint_cancel_move_button) @@ -896,12 +896,12 @@ func _create_hud() -> void: prep_button_row.add_child(chapter_button) shop_button = Button.new() - shop_button.text = "Market" + shop_button.text = "Sutler" shop_button.pressed.connect(_on_shop_pressed) prep_button_row.add_child(shop_button) talk_button = Button.new() - talk_button.text = "Council" + talk_button.text = "War Council" talk_button.pressed.connect(_on_talk_pressed) prep_button_row.add_child(talk_button) @@ -916,12 +916,12 @@ func _create_hud() -> void: prep_button_row.add_child(roster_button) formation_button = Button.new() - formation_button.text = "Array" + formation_button.text = "Battle Array" formation_button.pressed.connect(_on_formation_pressed) prep_button_row.add_child(formation_button) save_button = Button.new() - save_button.text = "Record" + save_button.text = "War Record" save_button.pressed.connect(_on_save_pressed) prep_button_row.add_child(save_button) @@ -1085,7 +1085,7 @@ func _create_hud() -> void: save_action_row.add_child(manual_load_button) var begin_button := Button.new() - begin_button.text = "Take Field" + begin_button.text = "Enter Field" begin_button.pressed.connect(_on_begin_battle_pressed) briefing_column.add_child(begin_button) @@ -1172,14 +1172,14 @@ func _create_hud() -> void: dialogue_control_row.add_child(dialogue_progress_label) dialogue_previous_button = Button.new() - dialogue_previous_button.text = "Recall" + dialogue_previous_button.text = "Earlier Slip" dialogue_previous_button.disabled = true dialogue_previous_button.custom_minimum_size = Vector2(88, 32) dialogue_previous_button.pressed.connect(_retreat_dialogue) dialogue_control_row.add_child(dialogue_previous_button) dialogue_continue_button = Button.new() - dialogue_continue_button.text = "Advance" + dialogue_continue_button.text = "Next Slip" dialogue_continue_button.custom_minimum_size = Vector2(120, 32) dialogue_continue_button.pressed.connect(_advance_dialogue) dialogue_control_row.add_child(dialogue_continue_button) @@ -2929,8 +2929,8 @@ func _update_hud() -> void: func _format_objective_hud_text() -> String: var text := String(state.objectives.get("victory", "Defeat all enemies.")) if text.is_empty(): - return "War Order" - return "War Order: %s" % text + return "Battle Mandate" + return "Battle Mandate: %s" % text func _format_unit_focus_text(unit: Dictionary, prefix: String) -> String: @@ -3095,16 +3095,16 @@ func _format_mission_panel_text() -> String: lines.append("Edict: %s" % victory) var progress_lines := state.get_objective_progress_lines(false, false) if not progress_lines.is_empty(): - lines.append("War Report:") + lines.append("Battle Register:") for progress_line in progress_lines: lines.append(" %s" % progress_line) var risk_lines := state.get_defeat_progress_lines() if not risk_lines.is_empty(): - lines.append("Ill Omen:") + lines.append("Dire Portents:") for risk_line in risk_lines: lines.append(" %s" % risk_line) if lines.is_empty(): - return "No war order." + return "No battle mandate." return _join_strings(lines, "\n") @@ -3479,7 +3479,7 @@ func _update_result_panel() -> void: result_restart_button.visible = false if next_battle_button != null: next_battle_button.visible = false - result_label.text = "%s Chronicle Complete\nAll current scrolls cleared.\nTreasury %dG\nBegin a new chronicle to replay." % [ + result_label.text = "%s Chronicle Complete\nAll current scrolls cleared.\nWar Chest %dG\nBegin a new chronicle to replay." % [ campaign_state.campaign_title, campaign_state.gold ] @@ -3535,16 +3535,24 @@ func _on_log_added(message: String) -> void: _play_log_sfx(message) if log_box == null: return - log_box.append_text(message + "\n") + log_box.append_text(_format_log_entry_text(message) + "\n") log_box.scroll_to_line(max(0, log_box.get_line_count() - 1)) +func _format_log_entry_text(message: String) -> String: + if message.begins_with("Objective updated:"): + return "Mandate revised:%s" % message.substr("Objective updated:".length()) + if message.begins_with("Defeat condition updated:"): + return "Omen revised:%s" % message.substr("Defeat condition updated:".length()) + return message + + func _on_objective_updated(victory: String, defeat: String) -> void: - var notice_lines := ["New War Order"] + var notice_lines := ["New Battle Mandate"] if not victory.is_empty(): - notice_lines.append(victory) + notice_lines.append("Mandate: %s" % victory) if not defeat.is_empty(): - notice_lines.append(defeat) + notice_lines.append("Omen: %s" % defeat) if objective_notice_label != null: objective_notice_label.text = "\n".join(notice_lines) if objective_notice_panel != null: @@ -3891,7 +3899,7 @@ func _update_dialogue_controls() -> void: dialogue_progress_label.text = "Slip %d / %d" % [active_dialogue_index + 1, active_dialogue_lines.size()] if dialogue_previous_button != null: dialogue_previous_button.disabled = active_dialogue_index <= 0 - dialogue_continue_button.text = "Seal" if active_dialogue_index >= active_dialogue_lines.size() - 1 else "Advance" + dialogue_continue_button.text = "Seal Scroll" if active_dialogue_index >= active_dialogue_lines.size() - 1 else "Next Slip" func _apply_dialogue_side(side: String) -> void: @@ -4266,27 +4274,27 @@ func _format_briefing_objectives() -> String: var text := "" var victory := str(state.objectives.get("victory", "")) if not victory.is_empty(): - text = "War Edict: %s" % victory + text = "Mandate of Victory: %s" % victory var defeat := str(state.objectives.get("defeat", "")) if not defeat.is_empty(): if not text.is_empty(): text += "\n" - text += "Ruin If: %s" % defeat + text += "Omen of Ruin: %s" % defeat var progress_lines := state.get_objective_progress_lines(false, false) if not progress_lines.is_empty(): if not text.is_empty(): text += "\n" - text += "War Report: %s" % _join_strings(progress_lines, ", ") + text += "Battle Register: %s" % _join_strings(progress_lines, ", ") var risk_lines := state.get_defeat_progress_lines() if not risk_lines.is_empty(): if not text.is_empty(): text += "\n" - text += "Ill Omen: %s" % _join_strings(risk_lines, ", ") + text += "Dire Portents: %s" % _join_strings(risk_lines, ", ") var rewards_text := _format_briefing_rewards() if not rewards_text.is_empty(): if not text.is_empty(): text += "\n" - text += "Spoils: %s" % rewards_text + text += "Spoils Ledger: %s" % rewards_text return text @@ -4325,7 +4333,7 @@ func _update_briefing_camp_overview(briefing: Dictionary, prep_locked: bool) -> briefing_camp_overview_texture.texture = texture briefing_camp_overview_texture.visible = has_texture if briefing_camp_overview_fallback_label != null: - briefing_camp_overview_fallback_label.text = "War Map" + briefing_camp_overview_fallback_label.text = "Silk War Map" briefing_camp_overview_fallback_label.visible = not has_texture @@ -4334,7 +4342,7 @@ func _format_briefing_camp_overview_text(briefing: Dictionary, prep_locked: bool var location := str(briefing.get("location", "")).strip_edges() if not location.is_empty(): parts.append(location) - parts.append("Treasury %dG" % campaign_state.gold) + parts.append("War Chest %dG" % campaign_state.gold) var talk_text := _format_briefing_talk_overview_text() if not talk_text.is_empty(): parts.append(talk_text) @@ -4348,9 +4356,9 @@ func _format_briefing_camp_overview_text(briefing: Dictionary, prep_locked: bool if not formation_text.is_empty(): parts.append(formation_text) if not state.get_controllable_player_units().is_empty(): - parts.append("Arsenal ready") + parts.append("Armory stocked") if prep_locked: - parts.append("Replay camp sealed") + parts.append("Replay scroll sealed") return _join_strings(parts, " - ") @@ -4361,7 +4369,7 @@ func _format_briefing_talk_overview_text() -> String: var supply_counts := _camp_conversation_supply_counts(conversations) var ready := int(supply_counts.get("ready", 0)) var claimed := int(supply_counts.get("claimed", 0)) - var parts := ["Council %d" % conversations.size()] + var parts := ["War Council %d" % conversations.size()] if ready > 0: parts.append("%d supply" % ready) if claimed > 0: @@ -4375,8 +4383,8 @@ func _format_briefing_shop_overview_text() -> String: if buy_count <= 0 and sell_count <= 0: return "" if sell_count > 0: - return "Market %d goods, %d sale" % [buy_count, sell_count] - return "Market %d goods" % buy_count + return "Sutler %d goods, %d sale" % [buy_count, sell_count] + return "Sutler %d goods" % buy_count func _format_briefing_roster_overview_text() -> String: @@ -4385,15 +4393,15 @@ func _format_briefing_roster_overview_text() -> String: var deployed_count := state.get_deployed_player_count() var max_units := state.get_deployment_max_units() if max_units > 0: - return "Muster %d/%d" % [deployed_count, max_units] - return "Muster %d" % deployed_count + return "Muster Roll %d/%d" % [deployed_count, max_units] + return "Muster Roll %d" % deployed_count func _format_briefing_formation_overview_text() -> String: var formation_count := state.get_formation_cells().size() if formation_count <= 0: return "" - return "Array %d tiles" % formation_count + return "Battle Array %d tiles" % formation_count func _camp_conversation_supply_counts(conversations: Array) -> Dictionary: @@ -5085,7 +5093,7 @@ func _format_current_checkpoint_text() -> String: var title := campaign_state.get_scenario_title(campaign_state.current_scenario_id) if title.is_empty(): title = campaign_state.get_scenario_title(campaign_state.get_start_scenario_id()) - return "Current Seal: %s - %d/%d sealed - Treasury %dG" % [ + return "Current Seal: %s - %d/%d sealed - War Chest %dG" % [ title, campaign_state.completed_scenarios.size(), campaign_state.get_scenario_count(), @@ -5101,7 +5109,7 @@ func _format_manual_checkpoint_text() -> String: if title.is_empty(): title = str(summary.get("current_scenario_id", "Unknown")) var pending_suffix := " - council pending" if bool(summary.get("pending_choice", false)) else "" - return "Manual Seal: %s - %d/%d sealed - Treasury %dG%s" % [ + return "Manual Seal: %s - %d/%d sealed - War Chest %dG%s" % [ title, int(summary.get("completed_count", 0)), int(summary.get("total_count", 0)), @@ -5488,7 +5496,7 @@ func _rebuild_shop_menu() -> void: _clear_shop_list() if shop_status_label != null: var mode_text := "Offer" if shop_sell_mode else "Acquire" - shop_status_label.text = "%s - Treasury %dG - %s" % [ + shop_status_label.text = "%s - War Chest %dG - %s" % [ mode_text, campaign_state.gold, _format_inventory_status_text() @@ -6058,7 +6066,7 @@ func _format_battle_result_summary() -> String: var next_title := str(battle_result_summary.get("next_scenario_title", "")) if not next_title.is_empty(): next_text = "Next March: %s" % next_title - sections.append("Chronicle\n Treasury: %dG\n %s\n %s" % [ + sections.append("Chronicle\n War Chest: %dG\n %s\n %s" % [ campaign_state.gold, saved_text, next_text @@ -6106,18 +6114,18 @@ func _format_next_camp_preview_text(next_scenario_id: String) -> String: parts.append(location) var talk_count := _briefing_camp_conversation_count(briefing) if talk_count > 0: - parts.append("Council %d" % talk_count) + parts.append("War Council %d" % talk_count) var shop_count := preview_state.get_shop_item_ids().size() if shop_count > 0: - parts.append("Market %d goods" % shop_count) + parts.append("Sutler %d goods" % shop_count) if preview_state.has_deployment_roster(): - parts.append("Muster %d/%d" % [ + parts.append("Muster Roll %d/%d" % [ preview_state.get_deployed_player_count(), preview_state.get_deployment_max_units() ]) var formation_count := preview_state.get_formation_cells().size() if formation_count > 0: - parts.append("Array %d tiles" % formation_count) + parts.append("Battle Array %d tiles" % formation_count) return _join_strings(parts, " - ") @@ -6373,11 +6381,11 @@ func _update_post_move_menu() -> void: if post_move_attack_button != null: var attack_blocked := not _has_basic_attack_target(pending_move_unit_id) post_move_attack_button.disabled = attack_blocked - post_move_attack_button.tooltip_text = "No enemy is in range after this move." if attack_blocked else "Choose an enemy target from this unit's current position." + post_move_attack_button.tooltip_text = "No enemy is in range after this move." if attack_blocked else "Mark an enemy from this unit's current position." if post_move_tactic_button != null: var skill_blocked := not _unit_has_usable_skill_target(pending_move_unit_id) post_move_tactic_button.disabled = skill_blocked - post_move_tactic_button.tooltip_text = "No tactic has a valid target after this move." if skill_blocked else "Choose a tactic from this unit's current position." + post_move_tactic_button.tooltip_text = "No tactic has a valid target after this move." if skill_blocked else "Select a tactic writ from this unit's current position." if post_move_item_button != null: var item_blocked := not _unit_has_usable_item_target(pending_move_unit_id) post_move_item_button.disabled = item_blocked @@ -6523,7 +6531,7 @@ func _post_move_picker_detail_text() -> String: return "Pick a tactic, then click a highlighted target." if post_move_picker_mode == "item": return "Pick an item, then click a highlighted ally." - return "Choose an option." + return "Select a writ." func _rebuild_post_move_tactic_picker(selected: Dictionary) -> void: @@ -6541,7 +6549,7 @@ func _rebuild_post_move_tactic_picker(selected: Dictionary) -> void: skill_button.text = _format_post_move_tactic_button_text(skill_id, skill, selected) var disabled_reason := _post_move_tactic_disabled_reason(selected, skill_id, skill) skill_button.disabled = not disabled_reason.is_empty() - skill_button.tooltip_text = disabled_reason if not disabled_reason.is_empty() else "Choose this tactic." + skill_button.tooltip_text = disabled_reason if not disabled_reason.is_empty() else "Select this tactic writ." _apply_button_style(skill_button) skill_button.pressed.connect(_on_post_move_tactic_option_pressed.bind(skill_id)) post_move_picker_list.add_child(skill_button) @@ -6565,7 +6573,7 @@ func _rebuild_post_move_item_picker(selected: Dictionary) -> void: _apply_item_button_icon(item_button, item) var disabled_reason := _post_move_item_disabled_reason(selected, item_id, count) item_button.disabled = not disabled_reason.is_empty() - item_button.tooltip_text = disabled_reason if not disabled_reason.is_empty() else "Choose this item." + item_button.tooltip_text = disabled_reason if not disabled_reason.is_empty() else "Select this supply." _apply_button_style(item_button) item_button.pressed.connect(_on_post_move_item_option_pressed.bind(item_id)) post_move_picker_list.add_child(item_button) @@ -6759,8 +6767,8 @@ func _update_targeting_hint_panel() -> void: if targeting_hint_detail_label != null: targeting_hint_detail_label.text = _targeting_hint_detail_text() if targeting_hint_back_button != null: - targeting_hint_back_button.text = "Back" - targeting_hint_back_button.tooltip_text = "Return to the action menu." if _has_pending_move() else "Clear target selection." + targeting_hint_back_button.text = "Return" + targeting_hint_back_button.tooltip_text = "Return to the order menu." if _has_pending_move() else "Clear target selection." if targeting_hint_cancel_move_button != null: targeting_hint_cancel_move_button.visible = _has_pending_move() targeting_hint_cancel_move_button.disabled = not _has_pending_move() @@ -6779,14 +6787,14 @@ func _position_targeting_hint_panel(selected: Dictionary) -> void: func _targeting_hint_title_text() -> String: if basic_attack_targeting: - return "Select Attack Target" + return "Mark Foe" if not selected_skill_id.is_empty(): var skill := state.get_skill_def(selected_skill_id) - return "Target: %s" % str(skill.get("name", selected_skill_id)) + return "Mark: %s" % str(skill.get("name", selected_skill_id)) if not selected_item_id.is_empty(): var item := state.get_item_def(selected_item_id) - return "Target: %s" % str(item.get("name", selected_item_id)) - return "Choose Target" + return "Mark: %s" % str(item.get("name", selected_item_id)) + return "Mark Target" func _targeting_hint_detail_text() -> String: @@ -6805,7 +6813,7 @@ func _targeting_hint_detail_text() -> String: if markers.is_empty(): return "No valid item target." return "%d item target%s. Click a marked unit." % [markers.size(), "" if markers.size() == 1 else "s"] - return "Choose a highlighted target." + return "Mark a highlighted target." func _on_targeting_back_pressed() -> void: diff --git a/tools/smoke_camp_conversation_rewards.gd b/tools/smoke_camp_conversation_rewards.gd index a143d15..890e705 100644 --- a/tools/smoke_camp_conversation_rewards.gd +++ b/tools/smoke_camp_conversation_rewards.gd @@ -66,6 +66,10 @@ const YE_SURRENDER_PRESSURE_CONVERSATION_ID := "ye_surrender_pressure_rations" const YE_SURRENDER_DEFECTOR_CONVERSATION_ID := "ye_surrender_defector_medicine" const NORTHERN_PURSUIT_SCENARIO_ID := "018_northern_pursuit" const NORTHERN_PURSUIT_SCENARIO_PATH := "res://data/scenarios/018_northern_pursuit.json" +const NORTHERN_PURSUIT_HARD_MARCH_CONVERSATION_ID := "northern_pursuit_hard_march_beans" +const NORTHERN_PURSUIT_JI_SUPPLY_CONVERSATION_ID := "northern_pursuit_ji_supply_medicine" +const NANPI_SCENARIO_ID := "019_nanpi_pressure" +const NANPI_SCENARIO_PATH := "res://data/scenarios/019_nanpi_pressure.json" const SAVE_PATH := "user://campaign_save.json" const MANUAL_SAVE_PATH := "user://campaign_manual_save.json" @@ -92,6 +96,7 @@ func _init() -> void: _check_ye_post_battle_choice_bridges_to_ye_siege(failures) _check_ye_siege_post_battle_choice_bridges_to_surrender(failures) _check_ye_surrender_post_battle_choice_bridges_to_northern_pursuit(failures) + _check_northern_pursuit_post_battle_choice_bridges_to_nanpi(failures) _check_manual_checkpoint_reverts_claim(failures) _check_completed_replay_cannot_claim(failures) _check_conditional_camp_conversations(failures) @@ -101,6 +106,7 @@ func _init() -> void: _check_ye_branch_camp_conversations(failures) _check_ye_siege_branch_camp_conversations(failures) _check_ye_surrender_branch_camp_conversations(failures) + _check_northern_pursuit_branch_camp_conversations(failures) _check_talk_menu_closes_on_cancel_and_reload(failures) if not _restore_user_file(SAVE_PATH, save_backup): @@ -1057,6 +1063,8 @@ func _check_ye_surrender_post_battle_choice_bridges_to_northern_pursuit(failures "pursue_yuan_shang", "pursued_yuan_shang", "stabilized_ye", + NORTHERN_PURSUIT_HARD_MARCH_CONVERSATION_ID, + NORTHERN_PURSUIT_JI_SUPPLY_CONVERSATION_ID, "war_drum", "imperial_seal" ) @@ -1065,6 +1073,27 @@ func _check_ye_surrender_post_battle_choice_bridges_to_northern_pursuit(failures "stabilize_ye", "stabilized_ye", "pursued_yuan_shang", + NORTHERN_PURSUIT_JI_SUPPLY_CONVERSATION_ID, + NORTHERN_PURSUIT_HARD_MARCH_CONVERSATION_ID, + "imperial_seal", + "war_drum" + ) + + +func _check_northern_pursuit_post_battle_choice_bridges_to_nanpi(failures: Array[String]) -> void: + _check_northern_pursuit_choice_branch_to_nanpi( + failures, + "press_north_to_nanpi", + "pressed_north_to_nanpi", + "secured_ji_province", + "war_drum", + "imperial_seal" + ) + _check_northern_pursuit_choice_branch_to_nanpi( + failures, + "secure_ji_province", + "secured_ji_province", + "pressed_north_to_nanpi", "imperial_seal", "war_drum" ) @@ -1361,6 +1390,8 @@ func _check_ye_surrender_choice_branch_to_northern_pursuit( choice_id: String, expected_flag: String, cleared_flag: String, + expected_conversation_id: String, + hidden_conversation_id: String, expected_shop_item_id: String, hidden_shop_item_id: String ) -> void: @@ -1421,6 +1452,10 @@ func _check_ye_surrender_choice_branch_to_northern_pursuit( failures.append("Ye Surrender choice bridge %s should deploy Zhang He in Northern Pursuit" % choice_id) elif not scene.state.is_required_deployment("zhang_he_ch18"): failures.append("Ye Surrender choice bridge %s should require Zhang He in Northern Pursuit" % choice_id) + if scene._camp_conversation_by_id(expected_conversation_id).is_empty(): + failures.append("Ye Surrender choice bridge %s should expose %s" % [choice_id, expected_conversation_id]) + if not scene._camp_conversation_by_id(hidden_conversation_id).is_empty(): + failures.append("Ye Surrender choice bridge %s should hide %s" % [choice_id, hidden_conversation_id]) var shop_items: Array = scene.state.get_shop_item_ids() if not shop_items.has(expected_shop_item_id): failures.append("Ye Surrender choice bridge %s should expose shop item %s" % [choice_id, expected_shop_item_id]) @@ -1429,6 +1464,79 @@ func _check_ye_surrender_choice_branch_to_northern_pursuit( scene.free() +func _check_northern_pursuit_choice_branch_to_nanpi( + 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, "Northern Pursuit choice bridge %s" % choice_id, NORTHERN_PURSUIT_SCENARIO_ID, NORTHERN_PURSUIT_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( + NORTHERN_PURSUIT_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("Northern Pursuit choice bridge %s could not reload Northern Pursuit 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("Northern Pursuit choice bridge %s should save 018 victory" % choice_id) + scene.free() + return + if not scene.campaign_state.get_joined_officers_snapshot().has("zhang_he"): + failures.append("Northern Pursuit choice bridge %s should keep Zhang He before Nanpi" % choice_id) + if bool(result.get("choice_applied", true)): + failures.append("Northern Pursuit choice bridge %s should wait for a selected post-battle choice" % choice_id) + if scene.campaign_state.pending_post_battle_choice_scenario_id != NORTHERN_PURSUIT_SCENARIO_ID: + failures.append("Northern Pursuit choice bridge %s should mark Northern Pursuit choice pending" % choice_id) + var choice := _find_choice_by_id(result.get("post_battle_choices", []), choice_id) + if choice.is_empty(): + failures.append("Northern Pursuit choice bridge missing choice: %s" % choice_id) + scene.free() + return + if not scene.campaign_state.try_apply_post_battle_choice(choice, NORTHERN_PURSUIT_SCENARIO_ID): + failures.append("Northern Pursuit 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("Northern Pursuit choice bridge %s should set %s" % [choice_id, expected_flag]) + if flags.get(cleared_flag, true) != false: + failures.append("Northern Pursuit choice bridge %s should clear %s" % [choice_id, cleared_flag]) + if scene.campaign_state.current_scenario_id != NANPI_SCENARIO_ID: + failures.append("Northern Pursuit choice bridge %s should advance to Nanpi, got %s" % [choice_id, scene.campaign_state.current_scenario_id]) + if not scene.state.load_battle( + NANPI_SCENARIO_PATH, + scene.campaign_state.get_roster_overrides(), + scene.campaign_state.get_inventory_snapshot(), + flags, + scene.campaign_state.get_joined_officers_snapshot() + ): + failures.append("Northern Pursuit choice bridge %s could not load Nanpi" % choice_id) + scene.free() + return + if scene.state.get_unit("zhang_he_ch19").is_empty(): + failures.append("Northern Pursuit choice bridge %s should deploy Zhang He in Nanpi" % choice_id) + elif not scene.state.is_required_deployment("zhang_he_ch19"): + failures.append("Northern Pursuit choice bridge %s should require Zhang He in Nanpi" % choice_id) + var shop_items: Array = scene.state.get_shop_item_ids() + if not shop_items.has(expected_shop_item_id): + failures.append("Northern Pursuit choice bridge %s should expose shop item %s" % [choice_id, expected_shop_item_id]) + if shop_items.has(hidden_shop_item_id): + failures.append("Northern Pursuit 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 {} @@ -2022,6 +2130,88 @@ func _check_ye_surrender_branch_camp_conversations(failures: Array[String]) -> v negotiated_scene.free() +func _check_northern_pursuit_branch_camp_conversations(failures: Array[String]) -> void: + var base_scene = _new_prebattle_scene_for(failures, "Northern Pursuit base camp", NORTHERN_PURSUIT_SCENARIO_ID, NORTHERN_PURSUIT_SCENARIO_PATH) + if base_scene != null: + if base_scene._camp_conversation_by_id("northern_pursuit_road_council").is_empty(): + failures.append("Northern Pursuit should expose the road council conversation") + if base_scene._camp_conversation_by_id("northern_pursuit_zhang_he_retreat_road").is_empty(): + failures.append("Northern Pursuit should expose Zhang He's retreat road conversation") + if not base_scene._camp_conversation_by_id(NORTHERN_PURSUIT_HARD_MARCH_CONVERSATION_ID).is_empty(): + failures.append("Northern Pursuit hard march beans should be hidden without pursued Yuan Shang flag") + if not base_scene._camp_conversation_by_id(NORTHERN_PURSUIT_JI_SUPPLY_CONVERSATION_ID).is_empty(): + failures.append("Northern Pursuit Ji supply medicine should be hidden without stabilized Ye flag") + var merchant: Dictionary = base_scene.state.get_shop_merchant() + if str(merchant.get("name", "")) != "Northern Road Sutler": + failures.append("Northern Pursuit shop merchant name should be present") + var merchant_lines: Array = merchant.get("lines", []) + if merchant_lines.size() < 2: + failures.append("Northern Pursuit shop merchant should expose flavor lines") + if base_scene._format_talk_status_text(base_scene._camp_conversation_entries()) != "Camp conversations | 2 topics": + failures.append("Northern Pursuit base talk status should summarize two non-supply topics") + base_scene.free() + + var pursued_scene = _new_prebattle_scene_for( + failures, + "Northern Pursuit pursued camp", + NORTHERN_PURSUIT_SCENARIO_ID, + NORTHERN_PURSUIT_SCENARIO_PATH, + {"pursued_yuan_shang": true} + ) + if pursued_scene != null: + var hard_march_beans: Dictionary = pursued_scene._camp_conversation_by_id(NORTHERN_PURSUIT_HARD_MARCH_CONVERSATION_ID) + if hard_march_beans.is_empty(): + failures.append("pursued Yuan Shang flag should expose Northern Pursuit hard march beans") + if not pursued_scene._camp_conversation_by_id(NORTHERN_PURSUIT_JI_SUPPLY_CONVERSATION_ID).is_empty(): + failures.append("pursued Yuan Shang flag should not expose Ji supply medicine") + if not hard_march_beans.is_empty(): + if pursued_scene._format_talk_status_text(pursued_scene._camp_conversation_entries()) != "Camp conversations | 3 topics | 1 supplies ready": + failures.append("Northern Pursuit pursued talk status should show one ready supply") + if not pursued_scene._format_camp_conversation_button_text(hard_march_beans).contains("Supply Bean"): + failures.append("Northern Pursuit hard march beans button should show Bean supply") + var before_bean := int(pursued_scene.campaign_state.get_inventory_snapshot().get("bean", 0)) + pursued_scene._apply_camp_conversation_effects(hard_march_beans) + if int(pursued_scene.campaign_state.get_inventory_snapshot().get("bean", 0)) != before_bean + 1: + failures.append("Northern Pursuit hard march beans should add Bean to campaign inventory") + if int(pursued_scene.state.get_inventory_snapshot().get("bean", 0)) != before_bean + 1: + failures.append("Northern Pursuit hard march beans should refresh battle Bean inventory") + if not pursued_scene.campaign_state.has_claimed_camp_conversation_effects(NORTHERN_PURSUIT_SCENARIO_ID, NORTHERN_PURSUIT_HARD_MARCH_CONVERSATION_ID): + failures.append("Northern Pursuit hard march beans claim should be saved") + if pursued_scene._format_talk_status_text(pursued_scene._camp_conversation_entries()) != "Camp conversations | 3 topics | 1 claimed": + failures.append("Northern Pursuit pursued talk status should show claimed supply after claim") + pursued_scene.free() + + var stabilized_scene = _new_prebattle_scene_for( + failures, + "Northern Pursuit stabilized camp", + NORTHERN_PURSUIT_SCENARIO_ID, + NORTHERN_PURSUIT_SCENARIO_PATH, + {"stabilized_ye": true} + ) + if stabilized_scene != null: + var ji_supply_medicine: Dictionary = stabilized_scene._camp_conversation_by_id(NORTHERN_PURSUIT_JI_SUPPLY_CONVERSATION_ID) + if ji_supply_medicine.is_empty(): + failures.append("stabilized Ye flag should expose Northern Pursuit Ji supply medicine") + if not stabilized_scene._camp_conversation_by_id(NORTHERN_PURSUIT_HARD_MARCH_CONVERSATION_ID).is_empty(): + failures.append("stabilized Ye flag should not expose hard march beans") + if not ji_supply_medicine.is_empty(): + if stabilized_scene._format_talk_status_text(stabilized_scene._camp_conversation_entries()) != "Camp conversations | 3 topics | 1 supplies ready": + failures.append("Northern Pursuit stabilized talk status should show one ready supply") + if not stabilized_scene._format_camp_conversation_button_text(ji_supply_medicine).contains("Supply Panacea"): + failures.append("Northern Pursuit Ji supply medicine button should show Panacea supply") + var before_panacea := int(stabilized_scene.campaign_state.get_inventory_snapshot().get("panacea", 0)) + stabilized_scene._apply_camp_conversation_effects(ji_supply_medicine) + if int(stabilized_scene.campaign_state.get_inventory_snapshot().get("panacea", 0)) != before_panacea + 1: + failures.append("Northern Pursuit Ji supply medicine should add Panacea to campaign inventory") + if int(stabilized_scene.state.get_inventory_snapshot().get("panacea", 0)) != before_panacea + 1: + failures.append("Northern Pursuit Ji supply medicine should refresh battle Panacea inventory") + if not stabilized_scene.campaign_state.has_claimed_camp_conversation_effects(NORTHERN_PURSUIT_SCENARIO_ID, NORTHERN_PURSUIT_JI_SUPPLY_CONVERSATION_ID): + failures.append("Northern Pursuit Ji supply medicine claim should be saved") + if stabilized_scene._format_talk_status_text(stabilized_scene._camp_conversation_entries()) != "Camp conversations | 3 topics | 1 claimed": + failures.append("Northern Pursuit stabilized talk status should show claimed supply after claim") + stabilized_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: diff --git a/tools/smoke_objective_progress.gd b/tools/smoke_objective_progress.gd index b3d831f..e1ac944 100644 --- a/tools/smoke_objective_progress.gd +++ b/tools/smoke_objective_progress.gd @@ -211,13 +211,17 @@ func _check_objective_notice(failures: Array[String]) -> void: scene._on_objective_updated("Defeat the new vanguard.", "") if scene.objective_notice_panel == null or not scene.objective_notice_panel.visible: failures.append("objective notice panel should be visible after objective update") - elif not scene.objective_notice_label.text.contains("Defeat the new vanguard."): - failures.append("objective notice label missing updated objective: %s" % scene.objective_notice_label.text) + elif not scene.objective_notice_label.text.contains("New Battle Mandate") or not scene.objective_notice_label.text.contains("Mandate: Defeat the new vanguard."): + failures.append("objective notice label should use battle mandate wording: %s" % scene.objective_notice_label.text) scene._on_objective_updated("", "Supply train is lost.") if scene.objective_notice_panel == null or not scene.objective_notice_panel.visible: failures.append("objective notice panel should be visible after defeat update") - elif not scene.objective_notice_label.text.contains("Supply train is lost."): - failures.append("objective notice label missing updated defeat condition: %s" % scene.objective_notice_label.text) + elif not scene.objective_notice_label.text.contains("New Battle Mandate") or not scene.objective_notice_label.text.contains("Omen: Supply train is lost."): + failures.append("objective notice label should use omen wording: %s" % scene.objective_notice_label.text) + if scene._format_log_entry_text("Objective updated: Defeat the new vanguard.") != "Mandate revised: Defeat the new vanguard.": + failures.append("objective log display should use mandate wording") + if scene._format_log_entry_text("Defeat condition updated: Supply train is lost.") != "Omen revised: Supply train is lost.": + failures.append("defeat log display should use omen wording") if scene.objective_notice_timer <= 0.0: failures.append("objective notice timer should be active") scene.free() diff --git a/tools/smoke_result_summary.gd b/tools/smoke_result_summary.gd index 5c87466..cb2da36 100644 --- a/tools/smoke_result_summary.gd +++ b/tools/smoke_result_summary.gd @@ -43,10 +43,10 @@ func _init() -> void: _check_contains(failures, "campaign section", result_text, "Next March: Sishui Gate") _check_contains(failures, "next camp section", result_text, "Next Encampment") _check_contains(failures, "next camp location", result_text, "Sishui Gate, 190 CE") - _check_contains(failures, "next camp talk", result_text, "Council 3") - _check_contains(failures, "next camp shop", result_text, "Market 6 goods") - _check_contains(failures, "next camp deploy", result_text, "Muster 2/3") - _check_contains(failures, "next camp formation", result_text, "Array 6 tiles") + _check_contains(failures, "next camp talk", result_text, "War Council 3") + _check_contains(failures, "next camp shop", result_text, "Sutler 6 goods") + _check_contains(failures, "next camp deploy", result_text, "Muster Roll 2/3") + _check_contains(failures, "next camp formation", result_text, "Battle Array 6 tiles") scene.battle_result_summary = { "saved": false, diff --git a/tools/smoke_visual_assets.gd b/tools/smoke_visual_assets.gd index 3fce1ec..94bfaeb 100644 --- a/tools/smoke_visual_assets.gd +++ b/tools/smoke_visual_assets.gd @@ -1028,6 +1028,85 @@ func _check_battle_visual_data(failures: Array[String]) -> void: ).is_empty(): failures.append("017 old Ye Siege flags should not expose defector medicine") + var northern_pursuit_state = BattleStateScript.new() + if not northern_pursuit_state.load_battle("res://data/scenarios/018_northern_pursuit.json"): + failures.append("could not load Northern Pursuit camp data") + else: + _check_shop_items_unique(failures, northern_pursuit_state, "018 base") + var northern_pursuit_conversations: Array = northern_pursuit_state.get_briefing().get("camp_conversations", []) + if northern_pursuit_conversations.size() != 2: + failures.append("018 base briefing should expose exactly two camp conversations") + else: + _check_camp_conversation(failures, northern_pursuit_conversations[0], "northern_pursuit_road_council", "topic", "") + _check_camp_conversation(failures, northern_pursuit_conversations[1], "northern_pursuit_zhang_he_retreat_road", "officer", "zhang_he") + var northern_pursuit_merchant := northern_pursuit_state.get_shop_merchant() + if str(northern_pursuit_merchant.get("name", "")) != "Northern Road Sutler": + failures.append("018 shop should expose Northern Road merchant name") + if (northern_pursuit_merchant.get("lines", []) as Array).size() < 2: + failures.append("018 shop should expose merchant flavor lines") + _check_shop_item_visibility(failures, northern_pursuit_state, "war_drum", false, "018 base") + _check_shop_item_visibility(failures, northern_pursuit_state, "imperial_seal", false, "018 base") + _check_shop_item_visibility(failures, northern_pursuit_state, "iron_armor", true, "018 base") + + var northern_pursuit_hard_state = BattleStateScript.new() + if not northern_pursuit_hard_state.load_battle("res://data/scenarios/018_northern_pursuit.json", {}, {}, {"pursued_yuan_shang": true}): + failures.append("could not load Northern Pursuit hard march camp data") + else: + _check_shop_items_unique(failures, northern_pursuit_hard_state, "018 pursued Yuan Shang") + var hard_march_beans: Dictionary = _find_camp_conversation( + northern_pursuit_hard_state.get_briefing().get("camp_conversations", []), + "northern_pursuit_hard_march_beans" + ) + if hard_march_beans.is_empty(): + failures.append("018 pursued Yuan Shang flag should expose hard march beans") + else: + _check_camp_conversation_effect(failures, hard_march_beans, "bean", 1) + if not _find_camp_conversation( + northern_pursuit_hard_state.get_briefing().get("camp_conversations", []), + "northern_pursuit_ji_supply_medicine" + ).is_empty(): + failures.append("018 pursued Yuan Shang flag should not expose Ji supply medicine") + _check_shop_item_visibility(failures, northern_pursuit_hard_state, "war_drum", true, "018 pursued Yuan Shang") + _check_shop_item_visibility(failures, northern_pursuit_hard_state, "imperial_seal", false, "018 pursued Yuan Shang") + + var northern_pursuit_stabilized_state = BattleStateScript.new() + if not northern_pursuit_stabilized_state.load_battle("res://data/scenarios/018_northern_pursuit.json", {}, {}, {"stabilized_ye": true}): + failures.append("could not load Northern Pursuit stabilized camp data") + else: + _check_shop_items_unique(failures, northern_pursuit_stabilized_state, "018 stabilized Ye") + var ji_supply_medicine: Dictionary = _find_camp_conversation( + northern_pursuit_stabilized_state.get_briefing().get("camp_conversations", []), + "northern_pursuit_ji_supply_medicine" + ) + if ji_supply_medicine.is_empty(): + failures.append("018 stabilized Ye flag should expose Ji supply medicine") + else: + _check_camp_conversation_effect(failures, ji_supply_medicine, "panacea", 1) + if not _find_camp_conversation( + northern_pursuit_stabilized_state.get_briefing().get("camp_conversations", []), + "northern_pursuit_hard_march_beans" + ).is_empty(): + failures.append("018 stabilized Ye flag should not expose hard march beans") + _check_shop_item_visibility(failures, northern_pursuit_stabilized_state, "war_drum", false, "018 stabilized Ye") + _check_shop_item_visibility(failures, northern_pursuit_stabilized_state, "imperial_seal", true, "018 stabilized Ye") + + var northern_pursuit_old_flags_state = BattleStateScript.new() + if not northern_pursuit_old_flags_state.load_battle("res://data/scenarios/018_northern_pursuit.json", {}, {}, {"forced_ye_surrender": true, "negotiated_yuan_defections": true}): + failures.append("could not load Northern Pursuit old Ye Surrender flag camp data") + else: + _check_shop_item_visibility(failures, northern_pursuit_old_flags_state, "war_drum", false, "018 old Ye Surrender flags") + _check_shop_item_visibility(failures, northern_pursuit_old_flags_state, "imperial_seal", false, "018 old Ye Surrender flags") + if not _find_camp_conversation( + northern_pursuit_old_flags_state.get_briefing().get("camp_conversations", []), + "northern_pursuit_hard_march_beans" + ).is_empty(): + failures.append("018 old Ye Surrender flags should not expose hard march beans") + if not _find_camp_conversation( + northern_pursuit_old_flags_state.get_briefing().get("camp_conversations", []), + "northern_pursuit_ji_supply_medicine" + ).is_empty(): + failures.append("018 old Ye Surrender flags should not expose Ji supply medicine") + for item_id in ["bronze_sword", "training_spear", "short_bow", "hand_axe", "iron_armor", "panacea"]: var item := state.get_item_def(item_id) if item.is_empty(): @@ -1101,7 +1180,7 @@ func _check_scene_texture_loading(failures: Array[String]) -> void: if not merchant_notice.contains("Camp Merchant") or not merchant_notice.contains("Roadside stores"): failures.append("shop merchant notice should include name and flavor lines") var camp_overview := scene._format_briefing_camp_overview_text(scene.state.get_briefing(), false) - for expected in ["Yingchuan, 184 CE", "Treasury 0G", "Council 3", "1 supply", "Market 2 goods", "Muster 2/2", "Array 4 tiles", "Arsenal ready"]: + for expected in ["Yingchuan, 184 CE", "War Chest 0G", "War Council 3", "1 supply", "Sutler 2 goods", "Muster Roll 2/2", "Battle Array 4 tiles", "Armory stocked"]: if not camp_overview.contains(expected): failures.append("camp overview should include %s: %s" % [expected, camp_overview]) var bean: Dictionary = scene.state.get_item_def("bean") @@ -1200,7 +1279,7 @@ func _check_ancient_ui_theme(failures: Array[String]) -> void: scene.free() return var briefing_text := scene._format_briefing_objectives() - for expected in ["War Edict:", "Ruin If:", "War Report:", "Ill Omen:", "Spoils:"]: + for expected in ["Mandate of Victory:", "Omen of Ruin:", "Battle Register:", "Dire Portents:", "Spoils Ledger:"]: if not briefing_text.contains(expected): failures.append("briefing objective parchment should use ancient heading `%s`: %s" % [expected, briefing_text]) _check_panel_style_fill(failures, scene.briefing_panel, "briefing panel", Color(0.66, 0.55, 0.36, 0.97)) @@ -1212,10 +1291,31 @@ func _check_ancient_ui_theme(failures: Array[String]) -> void: _check_panel_style_fill(failures, scene.dialogue_portrait_panel, "dialogue portrait panel", Color(0.055, 0.025, 0.016, 1.0)) _check_panel_style_fill(failures, scene.dialogue_speaker_panel, "dialogue speaker seal panel", Color(0.32, 0.025, 0.016, 1.0)) _check_panel_style_fill(failures, scene.dialogue_text_panel, "dialogue text scroll panel", Color(0.76, 0.64, 0.43, 0.98)) - if scene.dialogue_continue_button == null or scene.dialogue_continue_button.text != "Advance": - failures.append("dialogue continue button should use ancient advance text") - if scene.dialogue_previous_button == null or scene.dialogue_previous_button.text != "Recall": - failures.append("dialogue previous button should use ancient recall text") + if scene.dialogue_continue_button == null or scene.dialogue_continue_button.text != "Next Slip": + failures.append("dialogue continue button should use ancient slip text") + if scene.dialogue_previous_button == null or scene.dialogue_previous_button.text != "Earlier Slip": + failures.append("dialogue previous button should use ancient slip text") + if scene.mission_title_label == null or scene.mission_title_label.text != "Battle Mandate": + failures.append("mission panel title should use battle mandate wording") + if scene.shop_button == null or scene.shop_button.text != "Sutler": + failures.append("briefing shop button should use sutler wording") + if scene.talk_button == null or scene.talk_button.text != "War Council": + failures.append("briefing talk button should use war council wording") + if scene.formation_button == null or scene.formation_button.text != "Battle Array": + failures.append("briefing formation button should use battle array wording") + if scene.post_move_title_label == null or scene.post_move_title_label.text != "Issue Order": + failures.append("post-move command title should use order wording") + if scene.post_move_cancel_button == null or scene.post_move_cancel_button.text != "Recall Step": + failures.append("post-move cancel button should use recall step wording") + if scene.post_move_picker_back_button == null or scene.post_move_picker_back_button.text != "Return": + failures.append("post-move picker back button should use return wording") + if scene.targeting_hint_title_label == null or scene.targeting_hint_title_label.text != "Mark Foe": + failures.append("targeting hint title should use mark foe wording") + if scene.targeting_hint_back_button == null or scene.targeting_hint_back_button.text != "Return": + failures.append("targeting hint back button should use return wording") + scene._update_briefing_camp_overview(scene.state.get_briefing(), false) + if scene.briefing_camp_overview_fallback_label == null or scene.briefing_camp_overview_fallback_label.text != "Silk War Map": + failures.append("briefing map fallback should read as an old campaign map") _check_panel_style_fill(failures, scene.result_panel, "result panel", Color(0.66, 0.55, 0.36, 0.97)) scene.free()