Style ancient briefing UI and add Ye surrender camp
This commit is contained in:
@@ -32,9 +32,116 @@
|
||||
"Negotiations have drawn several Yuan captains away from the palace, leaving the loyalists angry and isolated."
|
||||
]
|
||||
}
|
||||
],
|
||||
"camp_conversations": [
|
||||
{
|
||||
"id": "ye_surrender_palace_road_council",
|
||||
"group": "topic",
|
||||
"label": "Palace Road Council",
|
||||
"speaker": "Cao Cao",
|
||||
"summary": "Set the terms for Ye's final surrender signal.",
|
||||
"lines": [
|
||||
{
|
||||
"speaker": "Cao Cao",
|
||||
"side": "left",
|
||||
"text": "The city has opened its teeth. Now we decide whether the mouth speaks surrender or bites until it breaks."
|
||||
},
|
||||
{
|
||||
"speaker": "Guo Jia",
|
||||
"side": "right",
|
||||
"text": "A banner raised on the palace road will be seen by every frightened household. Mercy must arrive with discipline."
|
||||
},
|
||||
{
|
||||
"speaker": "Zhang He",
|
||||
"side": "right",
|
||||
"text": "The loyalists still listen for Yuan orders that will not come. Strike cleanly, and the silence will do half the work."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ye_surrender_zhang_he_last_loyalists",
|
||||
"group": "officer",
|
||||
"officer_id": "zhang_he",
|
||||
"label": "Zhang He - Last Loyalists",
|
||||
"speaker": "Zhang He",
|
||||
"summary": "Read the palace loyalists before the surrender signal.",
|
||||
"lines": [
|
||||
{
|
||||
"speaker": "Zhang He",
|
||||
"side": "right",
|
||||
"text": "The men ahead are not guarding Ye. They are guarding the last moment when they can call themselves loyal."
|
||||
},
|
||||
{
|
||||
"speaker": "Cao Cao",
|
||||
"side": "left",
|
||||
"text": "Then we leave them only two names: citizen, or corpse."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ye_surrender_pressure_rations",
|
||||
"campaign_flags": { "forced_ye_surrender": true },
|
||||
"group": "topic",
|
||||
"label": "Pressure Rations",
|
||||
"summary": "Claim compact rations prepared for the direct palace road assault.",
|
||||
"lines": [
|
||||
{
|
||||
"speaker": "Xiahou Dun",
|
||||
"side": "right",
|
||||
"text": "The men who pushed the gate brought forward dry beans. No ceremony, no cart, just food that survives dust."
|
||||
},
|
||||
{
|
||||
"speaker": "Cao Cao",
|
||||
"side": "left",
|
||||
"text": "Good. A forced surrender leaves little time for kitchens."
|
||||
}
|
||||
],
|
||||
"effects": [
|
||||
{
|
||||
"type": "grant_item",
|
||||
"item_id": "bean",
|
||||
"count": 1,
|
||||
"text": "The pressure rations add a Bean to the field supplies."
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "ye_surrender_defector_medicine",
|
||||
"campaign_flags": { "negotiated_yuan_defections": true },
|
||||
"group": "topic",
|
||||
"label": "Defector Medicine",
|
||||
"summary": "Claim a medicine bundle handed over by Yuan defectors.",
|
||||
"lines": [
|
||||
{
|
||||
"speaker": "Guo Jia",
|
||||
"side": "left",
|
||||
"text": "The defectors brought a medicine bundle and the names of two palace guards. One gift heals men, the other opens doors."
|
||||
},
|
||||
{
|
||||
"speaker": "Cao Ren",
|
||||
"side": "right",
|
||||
"text": "The cloth bears a Yuan store mark, but the seal has been cut away."
|
||||
}
|
||||
],
|
||||
"effects": [
|
||||
{
|
||||
"type": "grant_item",
|
||||
"item_id": "panacea",
|
||||
"count": 1,
|
||||
"text": "The defector medicine adds a Panacea to the field supplies."
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"shop": {
|
||||
"merchant": {
|
||||
"name": "Ye Palace Road Sutler",
|
||||
"lines": [
|
||||
"The palace road is bad for banners and excellent for prices.",
|
||||
"Buy what you need before the surrender bell. After that, every cellar in Ye will claim it was loyal all along."
|
||||
]
|
||||
},
|
||||
"items": [
|
||||
"bean",
|
||||
"wine",
|
||||
|
||||
@@ -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]]
|
||||
|
||||
@@ -69,14 +69,17 @@ const POST_MOVE_PICKER_PANEL_SIZE := Vector2(320, 210)
|
||||
const TARGETING_HINT_PANEL_SIZE := Vector2(240, 94)
|
||||
const LOCAL_COMMAND_PANEL_GAP := 8.0
|
||||
const LOCAL_COMMAND_PANEL_BOARD_PADDING := 4.0
|
||||
const UI_RICE_PAPER := Color(0.77, 0.67, 0.48, 0.96)
|
||||
const UI_RICE_PAPER_DARK := Color(0.48, 0.34, 0.18, 0.96)
|
||||
const UI_AGED_INK := Color(0.10, 0.065, 0.04, 1.0)
|
||||
const UI_DARK_LACQUER := Color(0.18, 0.055, 0.035, 0.96)
|
||||
const UI_CINNABAR := Color(0.48, 0.055, 0.035, 1.0)
|
||||
const UI_OLD_BRONZE := Color(0.72, 0.52, 0.22, 1.0)
|
||||
const UI_MUTED_JADE := Color(0.20, 0.36, 0.28, 1.0)
|
||||
const UI_PARCHMENT_TEXT := Color(0.94, 0.84, 0.62, 1.0)
|
||||
const UI_RICE_PAPER := Color(0.66, 0.55, 0.36, 0.97)
|
||||
const UI_RICE_PAPER_LIGHT := Color(0.76, 0.64, 0.43, 0.98)
|
||||
const UI_RICE_PAPER_DARK := Color(0.34, 0.22, 0.11, 0.98)
|
||||
const UI_AGED_INK := Color(0.09, 0.055, 0.032, 1.0)
|
||||
const UI_DARK_LACQUER := Color(0.12, 0.045, 0.025, 0.98)
|
||||
const UI_LACQUER_EDGE := Color(0.055, 0.025, 0.016, 1.0)
|
||||
const UI_CINNABAR := Color(0.52, 0.045, 0.025, 1.0)
|
||||
const UI_CINNABAR_DARK := Color(0.32, 0.025, 0.016, 1.0)
|
||||
const UI_OLD_BRONZE := Color(0.78, 0.58, 0.27, 1.0)
|
||||
const UI_MUTED_JADE := Color(0.16, 0.30, 0.23, 1.0)
|
||||
const UI_PARCHMENT_TEXT := Color(0.95, 0.82, 0.55, 1.0)
|
||||
const UI_DISABLED_TEXT := Color(0.48, 0.40, 0.29, 1.0)
|
||||
|
||||
var state: BattleState = BattleStateScript.new()
|
||||
@@ -118,6 +121,7 @@ var inventory_label: Label
|
||||
var briefing_panel: PanelContainer
|
||||
var briefing_title_label: Label
|
||||
var briefing_location_label: Label
|
||||
var briefing_objective_panel: PanelContainer
|
||||
var briefing_objective_label: Label
|
||||
var briefing_camp_overview_row: HBoxContainer
|
||||
var briefing_camp_overview_texture: TextureRect
|
||||
@@ -164,7 +168,9 @@ var dialogue_portrait_panel: PanelContainer
|
||||
var dialogue_portrait_texture: TextureRect
|
||||
var dialogue_portrait_label: Label
|
||||
var dialogue_column: VBoxContainer
|
||||
var dialogue_speaker_panel: PanelContainer
|
||||
var dialogue_speaker_label: Label
|
||||
var dialogue_text_panel: PanelContainer
|
||||
var dialogue_text_label: Label
|
||||
var dialogue_progress_label: Label
|
||||
var dialogue_previous_button: Button
|
||||
@@ -304,34 +310,64 @@ func _apply_panel_style(panel: PanelContainer, variant: String) -> void:
|
||||
return
|
||||
var fill := UI_DARK_LACQUER
|
||||
var border := UI_OLD_BRONZE
|
||||
var border_width := 2
|
||||
var radius := 1
|
||||
var border_width := 3
|
||||
var radius := 0
|
||||
var margin := 10
|
||||
var shadow_size := 6
|
||||
match variant:
|
||||
"paper":
|
||||
fill = UI_RICE_PAPER
|
||||
border = UI_RICE_PAPER_DARK
|
||||
margin = 12
|
||||
border_width = 3
|
||||
margin = 14
|
||||
shadow_size = 8
|
||||
"edict":
|
||||
fill = UI_RICE_PAPER_LIGHT
|
||||
border = UI_AGED_INK
|
||||
border_width = 3
|
||||
margin = 10
|
||||
shadow_size = 4
|
||||
"seal":
|
||||
fill = UI_CINNABAR
|
||||
border = UI_OLD_BRONZE
|
||||
border_width = 2
|
||||
margin = 8
|
||||
"speaker_seal":
|
||||
fill = UI_CINNABAR_DARK
|
||||
border = UI_OLD_BRONZE
|
||||
border_width = 2
|
||||
margin = 8
|
||||
"portrait":
|
||||
fill = UI_AGED_INK
|
||||
fill = UI_LACQUER_EDGE
|
||||
border = UI_OLD_BRONZE
|
||||
border_width = 3
|
||||
margin = 3
|
||||
"dialogue":
|
||||
fill = UI_DARK_LACQUER
|
||||
border = UI_OLD_BRONZE
|
||||
border_width = 3
|
||||
margin = 12
|
||||
shadow_size = 10
|
||||
"speech_scroll":
|
||||
fill = UI_RICE_PAPER_LIGHT
|
||||
border = UI_RICE_PAPER_DARK
|
||||
border_width = 2
|
||||
margin = 12
|
||||
shadow_size = 2
|
||||
"notice":
|
||||
fill = Color(0.20, 0.09, 0.035, 0.96)
|
||||
fill = Color(0.18, 0.065, 0.032, 0.97)
|
||||
border = UI_CINNABAR
|
||||
border_width = 2
|
||||
margin = 8
|
||||
"map":
|
||||
fill = UI_AGED_INK
|
||||
fill = Color(0.20, 0.12, 0.065, 1.0)
|
||||
border = UI_OLD_BRONZE
|
||||
border_width = 3
|
||||
margin = 3
|
||||
"compact":
|
||||
fill = Color(0.16, 0.075, 0.04, 0.96)
|
||||
fill = Color(0.13, 0.065, 0.035, 0.97)
|
||||
border = UI_OLD_BRONZE
|
||||
border_width = 2
|
||||
margin = 7
|
||||
shadow_size = 4
|
||||
_:
|
||||
@@ -340,7 +376,7 @@ func _apply_panel_style(panel: PanelContainer, variant: String) -> void:
|
||||
|
||||
|
||||
func _make_button_style(fill: Color, border: Color, border_width: int) -> StyleBoxFlat:
|
||||
return _make_panel_style(fill, border, border_width, 1, 8, 0)
|
||||
return _make_panel_style(fill, border, border_width, 0, 8, 0)
|
||||
|
||||
|
||||
func _apply_button_style(button: Button, important: bool = false) -> void:
|
||||
@@ -378,6 +414,15 @@ func _make_separator(width: float = 0.0, height: float = 2.0) -> ColorRect:
|
||||
return separator
|
||||
|
||||
|
||||
func _make_ornament_bar(width: float = 4.0, height: float = 0.0) -> ColorRect:
|
||||
var bar := ColorRect.new()
|
||||
bar.color = UI_CINNABAR
|
||||
bar.custom_minimum_size = Vector2(width, height)
|
||||
bar.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
bar.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
return bar
|
||||
|
||||
|
||||
func _create_hud() -> void:
|
||||
var layer := CanvasLayer.new()
|
||||
add_child(layer)
|
||||
@@ -776,16 +821,23 @@ func _create_hud() -> void:
|
||||
|
||||
briefing_column.add_child(_make_separator(680, 2))
|
||||
|
||||
var briefing_objective_panel := PanelContainer.new()
|
||||
briefing_objective_panel = PanelContainer.new()
|
||||
briefing_objective_panel.custom_minimum_size = Vector2(680, 92)
|
||||
_apply_panel_style(briefing_objective_panel, "compact")
|
||||
_apply_panel_style(briefing_objective_panel, "edict")
|
||||
briefing_column.add_child(briefing_objective_panel)
|
||||
|
||||
var briefing_objective_row := HBoxContainer.new()
|
||||
briefing_objective_row.add_theme_constant_override("separation", 8)
|
||||
briefing_objective_panel.add_child(briefing_objective_row)
|
||||
|
||||
briefing_objective_row.add_child(_make_ornament_bar(5, 72))
|
||||
|
||||
briefing_objective_label = Label.new()
|
||||
briefing_objective_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
briefing_objective_label.custom_minimum_size = Vector2(656, 72)
|
||||
_apply_label_style(briefing_objective_label, UI_PARCHMENT_TEXT, 15)
|
||||
briefing_objective_panel.add_child(briefing_objective_label)
|
||||
briefing_objective_label.custom_minimum_size = Vector2(632, 72)
|
||||
briefing_objective_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
_apply_label_style(briefing_objective_label, UI_AGED_INK, 15)
|
||||
briefing_objective_row.add_child(briefing_objective_label)
|
||||
|
||||
briefing_camp_overview_row = HBoxContainer.new()
|
||||
briefing_camp_overview_row.custom_minimum_size = Vector2(680, 84)
|
||||
@@ -1042,11 +1094,11 @@ func _create_hud() -> void:
|
||||
dialogue_panel.position = DIALOGUE_PANEL_POSITION
|
||||
dialogue_panel.size = DIALOGUE_PANEL_SIZE
|
||||
dialogue_panel.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
_apply_panel_style(dialogue_panel, "compact")
|
||||
_apply_panel_style(dialogue_panel, "dialogue")
|
||||
root.add_child(dialogue_panel)
|
||||
|
||||
dialogue_row = HBoxContainer.new()
|
||||
dialogue_row.add_theme_constant_override("separation", 18)
|
||||
dialogue_row.add_theme_constant_override("separation", 14)
|
||||
dialogue_panel.add_child(dialogue_row)
|
||||
|
||||
dialogue_portrait_panel = PanelContainer.new()
|
||||
@@ -1078,9 +1130,9 @@ func _create_hud() -> void:
|
||||
dialogue_column.add_theme_constant_override("separation", 8)
|
||||
dialogue_row.add_child(dialogue_column)
|
||||
|
||||
var dialogue_speaker_panel := PanelContainer.new()
|
||||
dialogue_speaker_panel = PanelContainer.new()
|
||||
dialogue_speaker_panel.custom_minimum_size = Vector2(DIALOGUE_TEXT_SIZE.x, 32)
|
||||
_apply_panel_style(dialogue_speaker_panel, "seal")
|
||||
_apply_panel_style(dialogue_speaker_panel, "speaker_seal")
|
||||
dialogue_column.add_child(dialogue_speaker_panel)
|
||||
|
||||
dialogue_speaker_label = Label.new()
|
||||
@@ -1089,17 +1141,24 @@ func _create_hud() -> void:
|
||||
_apply_label_style(dialogue_speaker_label, UI_PARCHMENT_TEXT, 18)
|
||||
dialogue_speaker_panel.add_child(dialogue_speaker_label)
|
||||
|
||||
var dialogue_text_panel := PanelContainer.new()
|
||||
dialogue_text_panel = PanelContainer.new()
|
||||
dialogue_text_panel.custom_minimum_size = Vector2(DIALOGUE_TEXT_SIZE.x, 104)
|
||||
_apply_panel_style(dialogue_text_panel, "paper")
|
||||
_apply_panel_style(dialogue_text_panel, "speech_scroll")
|
||||
dialogue_column.add_child(dialogue_text_panel)
|
||||
|
||||
var dialogue_text_row := HBoxContainer.new()
|
||||
dialogue_text_row.add_theme_constant_override("separation", 8)
|
||||
dialogue_text_panel.add_child(dialogue_text_row)
|
||||
|
||||
dialogue_text_row.add_child(_make_ornament_bar(4, DIALOGUE_TEXT_SIZE.y - 20))
|
||||
|
||||
dialogue_text_label = Label.new()
|
||||
dialogue_text_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||
dialogue_text_label.custom_minimum_size = Vector2(DIALOGUE_TEXT_SIZE.x - 24, DIALOGUE_TEXT_SIZE.y - 20)
|
||||
dialogue_text_label.custom_minimum_size = Vector2(DIALOGUE_TEXT_SIZE.x - 42, DIALOGUE_TEXT_SIZE.y - 20)
|
||||
dialogue_text_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
dialogue_text_label.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||
_apply_label_style(dialogue_text_label, UI_AGED_INK, 17)
|
||||
dialogue_text_panel.add_child(dialogue_text_label)
|
||||
dialogue_text_row.add_child(dialogue_text_label)
|
||||
|
||||
var dialogue_control_row := HBoxContainer.new()
|
||||
dialogue_control_row.add_theme_constant_override("separation", 8)
|
||||
@@ -1113,14 +1172,14 @@ func _create_hud() -> void:
|
||||
dialogue_control_row.add_child(dialogue_progress_label)
|
||||
|
||||
dialogue_previous_button = Button.new()
|
||||
dialogue_previous_button.text = "Prev"
|
||||
dialogue_previous_button.text = "Prior"
|
||||
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 = "Next"
|
||||
dialogue_continue_button.text = "Continue"
|
||||
dialogue_continue_button.custom_minimum_size = Vector2(120, 32)
|
||||
dialogue_continue_button.pressed.connect(_advance_dialogue)
|
||||
dialogue_control_row.add_child(dialogue_continue_button)
|
||||
@@ -3829,10 +3888,10 @@ func _render_dialogue_line() -> void:
|
||||
|
||||
func _update_dialogue_controls() -> void:
|
||||
if dialogue_progress_label != null:
|
||||
dialogue_progress_label.text = "%d / %d" % [active_dialogue_index + 1, active_dialogue_lines.size()]
|
||||
dialogue_progress_label.text = "Scroll %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 = "Close" if active_dialogue_index >= active_dialogue_lines.size() - 1 else "Next"
|
||||
dialogue_continue_button.text = "Close" if active_dialogue_index >= active_dialogue_lines.size() - 1 else "Continue"
|
||||
|
||||
|
||||
func _apply_dialogue_side(side: String) -> void:
|
||||
@@ -4266,7 +4325,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 = "Camp"
|
||||
briefing_camp_overview_fallback_label.text = "War Map"
|
||||
briefing_camp_overview_fallback_label.visible = not has_texture
|
||||
|
||||
|
||||
|
||||
@@ -62,6 +62,10 @@ const YE_SIEGE_PRESSED_CONVERSATION_ID := "ye_siege_ladder_rations"
|
||||
const YE_SIEGE_SPLIT_CONVERSATION_ID := "ye_siege_defector_medicine"
|
||||
const YE_SURRENDER_SCENARIO_ID := "017_ye_surrender"
|
||||
const YE_SURRENDER_SCENARIO_PATH := "res://data/scenarios/017_ye_surrender.json"
|
||||
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 SAVE_PATH := "user://campaign_save.json"
|
||||
const MANUAL_SAVE_PATH := "user://campaign_manual_save.json"
|
||||
|
||||
@@ -87,6 +91,7 @@ func _init() -> void:
|
||||
_check_cangting_post_battle_choice_bridges_to_ye(failures)
|
||||
_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_manual_checkpoint_reverts_claim(failures)
|
||||
_check_completed_replay_cannot_claim(failures)
|
||||
_check_conditional_camp_conversations(failures)
|
||||
@@ -95,6 +100,7 @@ func _init() -> void:
|
||||
_check_cangting_branch_camp_conversations(failures)
|
||||
_check_ye_branch_camp_conversations(failures)
|
||||
_check_ye_siege_branch_camp_conversations(failures)
|
||||
_check_ye_surrender_branch_camp_conversations(failures)
|
||||
_check_talk_menu_closes_on_cancel_and_reload(failures)
|
||||
|
||||
if not _restore_user_file(SAVE_PATH, save_backup):
|
||||
@@ -1028,6 +1034,8 @@ func _check_ye_siege_post_battle_choice_bridges_to_surrender(failures: Array[Str
|
||||
"force_ye_surrender",
|
||||
"forced_ye_surrender",
|
||||
"negotiated_yuan_defections",
|
||||
YE_SURRENDER_PRESSURE_CONVERSATION_ID,
|
||||
YE_SURRENDER_DEFECTOR_CONVERSATION_ID,
|
||||
"war_drum",
|
||||
"imperial_seal"
|
||||
)
|
||||
@@ -1036,6 +1044,27 @@ func _check_ye_siege_post_battle_choice_bridges_to_surrender(failures: Array[Str
|
||||
"negotiate_yuan_defections",
|
||||
"negotiated_yuan_defections",
|
||||
"forced_ye_surrender",
|
||||
YE_SURRENDER_DEFECTOR_CONVERSATION_ID,
|
||||
YE_SURRENDER_PRESSURE_CONVERSATION_ID,
|
||||
"imperial_seal",
|
||||
"war_drum"
|
||||
)
|
||||
|
||||
|
||||
func _check_ye_surrender_post_battle_choice_bridges_to_northern_pursuit(failures: Array[String]) -> void:
|
||||
_check_ye_surrender_choice_branch_to_northern_pursuit(
|
||||
failures,
|
||||
"pursue_yuan_shang",
|
||||
"pursued_yuan_shang",
|
||||
"stabilized_ye",
|
||||
"war_drum",
|
||||
"imperial_seal"
|
||||
)
|
||||
_check_ye_surrender_choice_branch_to_northern_pursuit(
|
||||
failures,
|
||||
"stabilize_ye",
|
||||
"stabilized_ye",
|
||||
"pursued_yuan_shang",
|
||||
"imperial_seal",
|
||||
"war_drum"
|
||||
)
|
||||
@@ -1253,6 +1282,8 @@ func _check_ye_siege_choice_branch_to_surrender(
|
||||
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:
|
||||
@@ -1313,6 +1344,10 @@ func _check_ye_siege_choice_branch_to_surrender(
|
||||
failures.append("Ye Siege choice bridge %s should deploy Zhang He in Ye Surrender" % choice_id)
|
||||
elif not scene.state.is_required_deployment("zhang_he_ch17"):
|
||||
failures.append("Ye Siege choice bridge %s should require Zhang He in Ye Surrender" % choice_id)
|
||||
if scene._camp_conversation_by_id(expected_conversation_id).is_empty():
|
||||
failures.append("Ye Siege 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 Siege 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 Siege choice bridge %s should expose shop item %s" % [choice_id, expected_shop_item_id])
|
||||
@@ -1321,6 +1356,79 @@ func _check_ye_siege_choice_branch_to_surrender(
|
||||
scene.free()
|
||||
|
||||
|
||||
func _check_ye_surrender_choice_branch_to_northern_pursuit(
|
||||
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, "Ye Surrender choice bridge %s" % choice_id, YE_SURRENDER_SCENARIO_ID, YE_SURRENDER_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(
|
||||
YE_SURRENDER_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("Ye Surrender choice bridge %s could not reload Ye Surrender 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("Ye Surrender choice bridge %s should save 017 victory" % choice_id)
|
||||
scene.free()
|
||||
return
|
||||
if not scene.campaign_state.get_joined_officers_snapshot().has("zhang_he"):
|
||||
failures.append("Ye Surrender choice bridge %s should keep Zhang He before Northern Pursuit" % choice_id)
|
||||
if bool(result.get("choice_applied", true)):
|
||||
failures.append("Ye Surrender choice bridge %s should wait for a selected post-battle choice" % choice_id)
|
||||
if scene.campaign_state.pending_post_battle_choice_scenario_id != YE_SURRENDER_SCENARIO_ID:
|
||||
failures.append("Ye Surrender choice bridge %s should mark Ye Surrender choice pending" % choice_id)
|
||||
var choice := _find_choice_by_id(result.get("post_battle_choices", []), choice_id)
|
||||
if choice.is_empty():
|
||||
failures.append("Ye Surrender choice bridge missing choice: %s" % choice_id)
|
||||
scene.free()
|
||||
return
|
||||
if not scene.campaign_state.try_apply_post_battle_choice(choice, YE_SURRENDER_SCENARIO_ID):
|
||||
failures.append("Ye Surrender 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("Ye Surrender choice bridge %s should set %s" % [choice_id, expected_flag])
|
||||
if flags.get(cleared_flag, true) != false:
|
||||
failures.append("Ye Surrender choice bridge %s should clear %s" % [choice_id, cleared_flag])
|
||||
if scene.campaign_state.current_scenario_id != NORTHERN_PURSUIT_SCENARIO_ID:
|
||||
failures.append("Ye Surrender choice bridge %s should advance to Northern Pursuit, got %s" % [choice_id, scene.campaign_state.current_scenario_id])
|
||||
if not scene.state.load_battle(
|
||||
NORTHERN_PURSUIT_SCENARIO_PATH,
|
||||
scene.campaign_state.get_roster_overrides(),
|
||||
scene.campaign_state.get_inventory_snapshot(),
|
||||
flags,
|
||||
scene.campaign_state.get_joined_officers_snapshot()
|
||||
):
|
||||
failures.append("Ye Surrender choice bridge %s could not load Northern Pursuit" % choice_id)
|
||||
scene.free()
|
||||
return
|
||||
if scene.state.get_unit("zhang_he_ch18").is_empty():
|
||||
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)
|
||||
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])
|
||||
if shop_items.has(hidden_shop_item_id):
|
||||
failures.append("Ye Surrender 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 {}
|
||||
@@ -1832,6 +1940,88 @@ func _check_ye_siege_branch_camp_conversations(failures: Array[String]) -> void:
|
||||
split_scene.free()
|
||||
|
||||
|
||||
func _check_ye_surrender_branch_camp_conversations(failures: Array[String]) -> void:
|
||||
var base_scene = _new_prebattle_scene_for(failures, "Ye Surrender base camp", YE_SURRENDER_SCENARIO_ID, YE_SURRENDER_SCENARIO_PATH)
|
||||
if base_scene != null:
|
||||
if base_scene._camp_conversation_by_id("ye_surrender_palace_road_council").is_empty():
|
||||
failures.append("Ye Surrender should expose the palace road council conversation")
|
||||
if base_scene._camp_conversation_by_id("ye_surrender_zhang_he_last_loyalists").is_empty():
|
||||
failures.append("Ye Surrender should expose Zhang He's last loyalists conversation")
|
||||
if not base_scene._camp_conversation_by_id(YE_SURRENDER_PRESSURE_CONVERSATION_ID).is_empty():
|
||||
failures.append("Ye Surrender pressure rations should be hidden without forced surrender flag")
|
||||
if not base_scene._camp_conversation_by_id(YE_SURRENDER_DEFECTOR_CONVERSATION_ID).is_empty():
|
||||
failures.append("Ye Surrender defector medicine should be hidden without negotiated defections flag")
|
||||
var merchant: Dictionary = base_scene.state.get_shop_merchant()
|
||||
if str(merchant.get("name", "")) != "Ye Palace Road Sutler":
|
||||
failures.append("Ye Surrender shop merchant name should be present")
|
||||
var merchant_lines: Array = merchant.get("lines", [])
|
||||
if merchant_lines.size() < 2:
|
||||
failures.append("Ye Surrender shop merchant should expose flavor lines")
|
||||
if base_scene._format_talk_status_text(base_scene._camp_conversation_entries()) != "Camp conversations | 2 topics":
|
||||
failures.append("Ye Surrender base talk status should summarize two non-supply topics")
|
||||
base_scene.free()
|
||||
|
||||
var forced_scene = _new_prebattle_scene_for(
|
||||
failures,
|
||||
"Ye Surrender forced camp",
|
||||
YE_SURRENDER_SCENARIO_ID,
|
||||
YE_SURRENDER_SCENARIO_PATH,
|
||||
{"forced_ye_surrender": true}
|
||||
)
|
||||
if forced_scene != null:
|
||||
var pressure_rations: Dictionary = forced_scene._camp_conversation_by_id(YE_SURRENDER_PRESSURE_CONVERSATION_ID)
|
||||
if pressure_rations.is_empty():
|
||||
failures.append("forced Ye surrender flag should expose pressure rations")
|
||||
if not forced_scene._camp_conversation_by_id(YE_SURRENDER_DEFECTOR_CONVERSATION_ID).is_empty():
|
||||
failures.append("forced Ye surrender flag should not expose defector medicine")
|
||||
if not pressure_rations.is_empty():
|
||||
if forced_scene._format_talk_status_text(forced_scene._camp_conversation_entries()) != "Camp conversations | 3 topics | 1 supplies ready":
|
||||
failures.append("Ye Surrender forced talk status should show one ready supply")
|
||||
if not forced_scene._format_camp_conversation_button_text(pressure_rations).contains("Supply Bean"):
|
||||
failures.append("Ye Surrender pressure rations button should show Bean supply")
|
||||
var before_bean := int(forced_scene.campaign_state.get_inventory_snapshot().get("bean", 0))
|
||||
forced_scene._apply_camp_conversation_effects(pressure_rations)
|
||||
if int(forced_scene.campaign_state.get_inventory_snapshot().get("bean", 0)) != before_bean + 1:
|
||||
failures.append("Ye Surrender pressure rations should add Bean to campaign inventory")
|
||||
if int(forced_scene.state.get_inventory_snapshot().get("bean", 0)) != before_bean + 1:
|
||||
failures.append("Ye Surrender pressure rations should refresh battle Bean inventory")
|
||||
if not forced_scene.campaign_state.has_claimed_camp_conversation_effects(YE_SURRENDER_SCENARIO_ID, YE_SURRENDER_PRESSURE_CONVERSATION_ID):
|
||||
failures.append("Ye Surrender pressure rations claim should be saved")
|
||||
if forced_scene._format_talk_status_text(forced_scene._camp_conversation_entries()) != "Camp conversations | 3 topics | 1 claimed":
|
||||
failures.append("Ye Surrender forced talk status should show claimed supply after claim")
|
||||
forced_scene.free()
|
||||
|
||||
var negotiated_scene = _new_prebattle_scene_for(
|
||||
failures,
|
||||
"Ye Surrender negotiated camp",
|
||||
YE_SURRENDER_SCENARIO_ID,
|
||||
YE_SURRENDER_SCENARIO_PATH,
|
||||
{"negotiated_yuan_defections": true}
|
||||
)
|
||||
if negotiated_scene != null:
|
||||
var defector_medicine: Dictionary = negotiated_scene._camp_conversation_by_id(YE_SURRENDER_DEFECTOR_CONVERSATION_ID)
|
||||
if defector_medicine.is_empty():
|
||||
failures.append("negotiated Yuan defections flag should expose Ye Surrender defector medicine")
|
||||
if not negotiated_scene._camp_conversation_by_id(YE_SURRENDER_PRESSURE_CONVERSATION_ID).is_empty():
|
||||
failures.append("negotiated Yuan defections flag should not expose pressure rations")
|
||||
if not defector_medicine.is_empty():
|
||||
if negotiated_scene._format_talk_status_text(negotiated_scene._camp_conversation_entries()) != "Camp conversations | 3 topics | 1 supplies ready":
|
||||
failures.append("Ye Surrender negotiated talk status should show one ready supply")
|
||||
if not negotiated_scene._format_camp_conversation_button_text(defector_medicine).contains("Supply Panacea"):
|
||||
failures.append("Ye Surrender defector medicine button should show Panacea supply")
|
||||
var before_panacea := int(negotiated_scene.campaign_state.get_inventory_snapshot().get("panacea", 0))
|
||||
negotiated_scene._apply_camp_conversation_effects(defector_medicine)
|
||||
if int(negotiated_scene.campaign_state.get_inventory_snapshot().get("panacea", 0)) != before_panacea + 1:
|
||||
failures.append("Ye Surrender defector medicine should add Panacea to campaign inventory")
|
||||
if int(negotiated_scene.state.get_inventory_snapshot().get("panacea", 0)) != before_panacea + 1:
|
||||
failures.append("Ye Surrender defector medicine should refresh battle Panacea inventory")
|
||||
if not negotiated_scene.campaign_state.has_claimed_camp_conversation_effects(YE_SURRENDER_SCENARIO_ID, YE_SURRENDER_DEFECTOR_CONVERSATION_ID):
|
||||
failures.append("Ye Surrender defector medicine claim should be saved")
|
||||
if negotiated_scene._format_talk_status_text(negotiated_scene._camp_conversation_entries()) != "Camp conversations | 3 topics | 1 claimed":
|
||||
failures.append("Ye Surrender negotiated talk status should show claimed supply after claim")
|
||||
negotiated_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:
|
||||
|
||||
@@ -949,6 +949,85 @@ func _check_battle_visual_data(failures: Array[String]) -> void:
|
||||
).is_empty():
|
||||
failures.append("016 old Ye flags should not expose defector medicine")
|
||||
|
||||
var ye_surrender_state = BattleStateScript.new()
|
||||
if not ye_surrender_state.load_battle("res://data/scenarios/017_ye_surrender.json"):
|
||||
failures.append("could not load Ye Surrender camp data")
|
||||
else:
|
||||
_check_shop_items_unique(failures, ye_surrender_state, "017 base")
|
||||
var ye_surrender_conversations: Array = ye_surrender_state.get_briefing().get("camp_conversations", [])
|
||||
if ye_surrender_conversations.size() != 2:
|
||||
failures.append("017 base briefing should expose exactly two camp conversations")
|
||||
else:
|
||||
_check_camp_conversation(failures, ye_surrender_conversations[0], "ye_surrender_palace_road_council", "topic", "")
|
||||
_check_camp_conversation(failures, ye_surrender_conversations[1], "ye_surrender_zhang_he_last_loyalists", "officer", "zhang_he")
|
||||
var ye_surrender_merchant := ye_surrender_state.get_shop_merchant()
|
||||
if str(ye_surrender_merchant.get("name", "")) != "Ye Palace Road Sutler":
|
||||
failures.append("017 shop should expose Ye Palace Road merchant name")
|
||||
if (ye_surrender_merchant.get("lines", []) as Array).size() < 2:
|
||||
failures.append("017 shop should expose merchant flavor lines")
|
||||
_check_shop_item_visibility(failures, ye_surrender_state, "war_drum", false, "017 base")
|
||||
_check_shop_item_visibility(failures, ye_surrender_state, "imperial_seal", false, "017 base")
|
||||
_check_shop_item_visibility(failures, ye_surrender_state, "iron_armor", true, "017 base")
|
||||
|
||||
var ye_surrender_forced_state = BattleStateScript.new()
|
||||
if not ye_surrender_forced_state.load_battle("res://data/scenarios/017_ye_surrender.json", {}, {}, {"forced_ye_surrender": true}):
|
||||
failures.append("could not load Ye Surrender forced camp data")
|
||||
else:
|
||||
_check_shop_items_unique(failures, ye_surrender_forced_state, "017 forced Ye surrender")
|
||||
var pressure_rations: Dictionary = _find_camp_conversation(
|
||||
ye_surrender_forced_state.get_briefing().get("camp_conversations", []),
|
||||
"ye_surrender_pressure_rations"
|
||||
)
|
||||
if pressure_rations.is_empty():
|
||||
failures.append("017 forced Ye surrender flag should expose pressure rations")
|
||||
else:
|
||||
_check_camp_conversation_effect(failures, pressure_rations, "bean", 1)
|
||||
if not _find_camp_conversation(
|
||||
ye_surrender_forced_state.get_briefing().get("camp_conversations", []),
|
||||
"ye_surrender_defector_medicine"
|
||||
).is_empty():
|
||||
failures.append("017 forced Ye surrender flag should not expose defector medicine")
|
||||
_check_shop_item_visibility(failures, ye_surrender_forced_state, "war_drum", true, "017 forced Ye surrender")
|
||||
_check_shop_item_visibility(failures, ye_surrender_forced_state, "imperial_seal", false, "017 forced Ye surrender")
|
||||
|
||||
var ye_surrender_negotiated_state = BattleStateScript.new()
|
||||
if not ye_surrender_negotiated_state.load_battle("res://data/scenarios/017_ye_surrender.json", {}, {}, {"negotiated_yuan_defections": true}):
|
||||
failures.append("could not load Ye Surrender negotiated camp data")
|
||||
else:
|
||||
_check_shop_items_unique(failures, ye_surrender_negotiated_state, "017 negotiated Yuan defections")
|
||||
var surrender_defector_medicine: Dictionary = _find_camp_conversation(
|
||||
ye_surrender_negotiated_state.get_briefing().get("camp_conversations", []),
|
||||
"ye_surrender_defector_medicine"
|
||||
)
|
||||
if surrender_defector_medicine.is_empty():
|
||||
failures.append("017 negotiated Yuan defections flag should expose defector medicine")
|
||||
else:
|
||||
_check_camp_conversation_effect(failures, surrender_defector_medicine, "panacea", 1)
|
||||
if not _find_camp_conversation(
|
||||
ye_surrender_negotiated_state.get_briefing().get("camp_conversations", []),
|
||||
"ye_surrender_pressure_rations"
|
||||
).is_empty():
|
||||
failures.append("017 negotiated Yuan defections flag should not expose pressure rations")
|
||||
_check_shop_item_visibility(failures, ye_surrender_negotiated_state, "war_drum", false, "017 negotiated Yuan defections")
|
||||
_check_shop_item_visibility(failures, ye_surrender_negotiated_state, "imperial_seal", true, "017 negotiated Yuan defections")
|
||||
|
||||
var ye_surrender_old_flags_state = BattleStateScript.new()
|
||||
if not ye_surrender_old_flags_state.load_battle("res://data/scenarios/017_ye_surrender.json", {}, {}, {"pressed_ye_siege": true, "split_yuan_heirs": true}):
|
||||
failures.append("could not load Ye Surrender old Ye Siege flag camp data")
|
||||
else:
|
||||
_check_shop_item_visibility(failures, ye_surrender_old_flags_state, "war_drum", false, "017 old Ye Siege flags")
|
||||
_check_shop_item_visibility(failures, ye_surrender_old_flags_state, "imperial_seal", false, "017 old Ye Siege flags")
|
||||
if not _find_camp_conversation(
|
||||
ye_surrender_old_flags_state.get_briefing().get("camp_conversations", []),
|
||||
"ye_surrender_pressure_rations"
|
||||
).is_empty():
|
||||
failures.append("017 old Ye Siege flags should not expose pressure rations")
|
||||
if not _find_camp_conversation(
|
||||
ye_surrender_old_flags_state.get_briefing().get("camp_conversations", []),
|
||||
"ye_surrender_defector_medicine"
|
||||
).is_empty():
|
||||
failures.append("017 old Ye Siege flags should not expose defector 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():
|
||||
@@ -1124,10 +1203,20 @@ func _check_ancient_ui_theme(failures: Array[String]) -> void:
|
||||
for expected in ["War Edict:", "Ruin If:", "War Report:", "Ill Omen:", "Spoils:"]:
|
||||
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.77, 0.67, 0.48, 0.96))
|
||||
_check_panel_style_fill(failures, scene.dialogue_panel, "dialogue panel", Color(0.16, 0.075, 0.04, 0.96))
|
||||
_check_panel_style_fill(failures, scene.dialogue_portrait_panel, "dialogue portrait panel", Color(0.10, 0.065, 0.04, 1.0))
|
||||
_check_panel_style_fill(failures, scene.result_panel, "result panel", Color(0.77, 0.67, 0.48, 0.96))
|
||||
_check_panel_style_fill(failures, scene.briefing_panel, "briefing panel", Color(0.66, 0.55, 0.36, 0.97))
|
||||
_check_panel_style_frame(failures, scene.briefing_panel, "briefing panel", Color(0.34, 0.22, 0.11, 0.98), 3)
|
||||
_check_panel_style_fill(failures, scene.briefing_objective_panel, "briefing objective edict panel", Color(0.76, 0.64, 0.43, 0.98))
|
||||
_check_panel_style_frame(failures, scene.briefing_objective_panel, "briefing objective edict panel", Color(0.09, 0.055, 0.032, 1.0), 3)
|
||||
_check_panel_style_fill(failures, scene.dialogue_panel, "dialogue panel", Color(0.12, 0.045, 0.025, 0.98))
|
||||
_check_panel_style_frame(failures, scene.dialogue_panel, "dialogue panel", Color(0.78, 0.58, 0.27, 1.0), 3)
|
||||
_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 != "Continue":
|
||||
failures.append("dialogue continue button should use ancient continue text")
|
||||
if scene.dialogue_previous_button == null or scene.dialogue_previous_button.text != "Prior":
|
||||
failures.append("dialogue previous button should use ancient prior text")
|
||||
_check_panel_style_fill(failures, scene.result_panel, "result panel", Color(0.66, 0.55, 0.36, 0.97))
|
||||
scene.free()
|
||||
|
||||
|
||||
@@ -1143,6 +1232,22 @@ func _check_panel_style_fill(failures: Array[String], panel: PanelContainer, lab
|
||||
failures.append("%s fill mismatch: %s" % [label, str(style.bg_color)])
|
||||
|
||||
|
||||
func _check_panel_style_frame(failures: Array[String], panel: PanelContainer, label: String, expected_border: Color, expected_width: int) -> void:
|
||||
if panel == null:
|
||||
failures.append("%s missing" % label)
|
||||
return
|
||||
var style := panel.get_theme_stylebox("panel") as StyleBoxFlat
|
||||
if style == null:
|
||||
failures.append("%s should use a flat styled panel" % label)
|
||||
return
|
||||
if not _colors_nearly_equal(style.border_color, expected_border):
|
||||
failures.append("%s border mismatch: %s" % [label, str(style.border_color)])
|
||||
if style.get_border_width(SIDE_LEFT) != expected_width:
|
||||
failures.append("%s border width mismatch: %d" % [label, style.get_border_width(SIDE_LEFT)])
|
||||
if style.get_corner_radius(CORNER_TOP_LEFT) != 0:
|
||||
failures.append("%s should use square old-frame corners" % label)
|
||||
|
||||
|
||||
func _colors_nearly_equal(left: Color, right: Color) -> bool:
|
||||
return (
|
||||
absf(left.r - right.r) < 0.01
|
||||
|
||||
Reference in New Issue
Block a user