Improve opening battle pacing and objectives UI
This commit is contained in:
@@ -2,14 +2,14 @@
|
|||||||
"id": "001_yellow_turbans",
|
"id": "001_yellow_turbans",
|
||||||
"name": "영천 소전",
|
"name": "영천 소전",
|
||||||
"objectives": {
|
"objectives": {
|
||||||
"victory": "황건적 본대를 격파하고 성채를 장악하라.",
|
"victory": "성채를 장악하고 마지막 황건대까지 격파하라.",
|
||||||
"defeat": "조조가 퇴각하거나 제16턴이 시작되면 패한다."
|
"defeat": "조조가 퇴각하거나 제18턴이 시작되면 패한다."
|
||||||
},
|
},
|
||||||
"conditions": {
|
"conditions": {
|
||||||
"victory": {
|
"victory": {
|
||||||
"type": "all",
|
"type": "all",
|
||||||
"conditions": [
|
"conditions": [
|
||||||
{ "type": "all_units_defeated", "team": "enemy" },
|
{ "type": "all_units_defeated", "team": "enemy", "after_event": "turn_9_western_reserve" },
|
||||||
{
|
{
|
||||||
"type": "unit_reaches_tile",
|
"type": "unit_reaches_tile",
|
||||||
"label": "성채 장악",
|
"label": "성채 장악",
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
},
|
},
|
||||||
"defeat": [
|
"defeat": [
|
||||||
{ "type": "any_officer_defeated", "team": "player", "officer_ids": ["cao_cao"] },
|
{ "type": "any_officer_defeated", "team": "player", "officer_ids": ["cao_cao"] },
|
||||||
{ "type": "turn_reached", "turn": 16, "team": "player" }
|
{ "type": "turn_reached", "turn": 18, "team": "player" }
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"briefing": {
|
"briefing": {
|
||||||
@@ -30,7 +30,7 @@
|
|||||||
"lines": [
|
"lines": [
|
||||||
"황건의 무리가 영천 근방에 깃발을 세웠다.",
|
"황건의 무리가 영천 근방에 깃발을 세웠다.",
|
||||||
"조조와 하후돈은 난세의 불길이 번지기 전에 그 진형을 꺾어야 한다.",
|
"조조와 하후돈은 난세의 불길이 번지기 전에 그 진형을 꺾어야 한다.",
|
||||||
"마을과 성채를 점거한 부대는 차례가 시작될 때 병력을 회복한다."
|
"적은 마을과 성채에서 병력을 고치며, 늦게 닿는 원군까지 기다리고 있다."
|
||||||
],
|
],
|
||||||
"camp_dialogue": [
|
"camp_dialogue": [
|
||||||
{ "speaker": "Cao Cao", "display_speaker": "조조", "side": "left", "text": "영천은 마른 풀밭의 불씨와 같다. 결단이 빠르면 조정도 아직 질서가 돌아올 수 있음을 믿을 것이다." },
|
{ "speaker": "Cao Cao", "display_speaker": "조조", "side": "left", "text": "영천은 마른 풀밭의 불씨와 같다. 결단이 빠르면 조정도 아직 질서가 돌아올 수 있음을 믿을 것이다." },
|
||||||
@@ -196,6 +196,42 @@
|
|||||||
"level": 1,
|
"level": 1,
|
||||||
"pos": [10, 6],
|
"pos": [10, 6],
|
||||||
"base": { "hp": 32, "atk": 11, "def": 5 }
|
"base": { "hp": 32, "atk": 11, "def": 5 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"unit_id": "yellow_turban_9",
|
||||||
|
"name": "황건 보병",
|
||||||
|
"class_id": "infantry",
|
||||||
|
"team": "enemy",
|
||||||
|
"level": 1,
|
||||||
|
"pos": [5, 4],
|
||||||
|
"base": { "hp": 34, "atk": 9, "def": 6 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"unit_id": "yellow_turban_10",
|
||||||
|
"name": "황건 궁수",
|
||||||
|
"class_id": "archer",
|
||||||
|
"team": "enemy",
|
||||||
|
"level": 1,
|
||||||
|
"pos": [8, 2],
|
||||||
|
"base": { "hp": 26, "atk": 9, "def": 4 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"unit_id": "yellow_turban_11",
|
||||||
|
"name": "황건 유격병",
|
||||||
|
"class_id": "bandit",
|
||||||
|
"team": "enemy",
|
||||||
|
"level": 1,
|
||||||
|
"pos": [6, 6],
|
||||||
|
"base": { "hp": 32, "atk": 11, "def": 5 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"unit_id": "yellow_turban_12",
|
||||||
|
"name": "북숲 초병",
|
||||||
|
"class_id": "infantry",
|
||||||
|
"team": "enemy",
|
||||||
|
"level": 1,
|
||||||
|
"pos": [4, 2],
|
||||||
|
"base": { "hp": 34, "atk": 9, "def": 6 }
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"events": [
|
"events": [
|
||||||
@@ -312,6 +348,52 @@
|
|||||||
"text": "남쪽 약탈대가 조조의 본진을 노린다."
|
"text": "남쪽 약탈대가 조조의 본진을 노린다."
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "turn_9_western_reserve",
|
||||||
|
"once": true,
|
||||||
|
"when": { "type": "turn_start", "team": "enemy", "turn": 9 },
|
||||||
|
"actions": [
|
||||||
|
{ "type": "log", "text": "서쪽 숲길에서 마지막 황건대가 전장에 닿았다." },
|
||||||
|
{
|
||||||
|
"type": "spawn_deployments",
|
||||||
|
"deployments": [
|
||||||
|
{
|
||||||
|
"unit_id": "yellow_turban_reinforcement_5",
|
||||||
|
"name": "황건 보병",
|
||||||
|
"class_id": "infantry",
|
||||||
|
"team": "enemy",
|
||||||
|
"level": 1,
|
||||||
|
"pos": [5, 8],
|
||||||
|
"base": { "hp": 34, "atk": 10, "def": 6 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"unit_id": "yellow_turban_reinforcement_6",
|
||||||
|
"name": "황건 궁수",
|
||||||
|
"class_id": "archer",
|
||||||
|
"team": "enemy",
|
||||||
|
"level": 1,
|
||||||
|
"pos": [6, 8],
|
||||||
|
"base": { "hp": 26, "atk": 9, "def": 4 }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"unit_id": "yellow_turban_reinforcement_7",
|
||||||
|
"name": "황건 유격병",
|
||||||
|
"class_id": "bandit",
|
||||||
|
"team": "enemy",
|
||||||
|
"level": 1,
|
||||||
|
"pos": [3, 6],
|
||||||
|
"base": { "hp": 32, "atk": 11, "def": 5 }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "set_ai_target_priority",
|
||||||
|
"unit_id": "cao_cao",
|
||||||
|
"priority": 12,
|
||||||
|
"text": "서쪽 황건대가 조조의 깃발을 노린다."
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"rewards": {
|
"rewards": {
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ const FLOATING_TEXT_LIFETIME := 1.0
|
|||||||
const FLOATING_TEXT_RISE := 42.0
|
const FLOATING_TEXT_RISE := 42.0
|
||||||
const OBJECTIVE_NOTICE_DURATION := 2.6
|
const OBJECTIVE_NOTICE_DURATION := 2.6
|
||||||
const UNIT_MOVE_ANIMATION_DURATION := 0.18
|
const UNIT_MOVE_ANIMATION_DURATION := 0.18
|
||||||
const TARGET_PREVIEW_BADGE_SIZE := Vector2(92, 22)
|
const TARGET_PREVIEW_BADGE_SIZE := Vector2(104, 22)
|
||||||
const LOW_HP_WARNING_RATIO := 0.35
|
const LOW_HP_WARNING_RATIO := 0.35
|
||||||
const UNIT_STATUS_MARKER_RADIUS := 6.0
|
const UNIT_STATUS_MARKER_RADIUS := 6.0
|
||||||
const UNIT_STATUS_MARKER_GAP := 14.0
|
const UNIT_STATUS_MARKER_GAP := 14.0
|
||||||
@@ -204,6 +204,8 @@ var pending_move_unit_id := ""
|
|||||||
var pending_move_from_cell := Vector2i(-1, -1)
|
var pending_move_from_cell := Vector2i(-1, -1)
|
||||||
var pending_move_to_cell := Vector2i(-1, -1)
|
var pending_move_to_cell := Vector2i(-1, -1)
|
||||||
var show_threat_overlay := false
|
var show_threat_overlay := false
|
||||||
|
var mission_detail_collapsed := false
|
||||||
|
var briefing_objective_collapsed := false
|
||||||
var active_scenario_id := ""
|
var active_scenario_id := ""
|
||||||
var battle_started := false
|
var battle_started := false
|
||||||
var battle_result_applied := false
|
var battle_result_applied := false
|
||||||
@@ -215,6 +217,7 @@ var status_label: Label
|
|||||||
var objective_label: Label
|
var objective_label: Label
|
||||||
var campaign_status_label: Label
|
var campaign_status_label: Label
|
||||||
var mission_title_label: Label
|
var mission_title_label: Label
|
||||||
|
var mission_toggle_button: Button
|
||||||
var mission_detail_panel: PanelContainer
|
var mission_detail_panel: PanelContainer
|
||||||
var mission_detail_label: Label
|
var mission_detail_label: Label
|
||||||
var objective_notice_panel: PanelContainer
|
var objective_notice_panel: PanelContainer
|
||||||
@@ -234,6 +237,7 @@ var briefing_location_label: Label
|
|||||||
var briefing_seal_ribbon: HBoxContainer
|
var briefing_seal_ribbon: HBoxContainer
|
||||||
var briefing_objective_panel: PanelContainer
|
var briefing_objective_panel: PanelContainer
|
||||||
var briefing_objective_label: Label
|
var briefing_objective_label: Label
|
||||||
|
var briefing_objective_toggle_button: Button
|
||||||
var briefing_camp_overview_panel: PanelContainer
|
var briefing_camp_overview_panel: PanelContainer
|
||||||
var briefing_camp_overview_row: HBoxContainer
|
var briefing_camp_overview_row: HBoxContainer
|
||||||
var briefing_camp_overview_texture: TextureRect
|
var briefing_camp_overview_texture: TextureRect
|
||||||
@@ -878,10 +882,10 @@ func _make_map_lattice_overlay(size: Vector2) -> Control:
|
|||||||
overlay.add_child(horizontal)
|
overlay.add_child(horizontal)
|
||||||
|
|
||||||
var corner_specs := [
|
var corner_specs := [
|
||||||
{"text": "위", "pos": Vector2(4, 4)},
|
{"text": "", "pos": Vector2(4, 4)},
|
||||||
{"text": "명", "pos": Vector2(size.x - 24, 4)},
|
{"text": "", "pos": Vector2(size.x - 24, 4)},
|
||||||
{"text": "도", "pos": Vector2(4, size.y - 24)},
|
{"text": "", "pos": Vector2(4, size.y - 24)},
|
||||||
{"text": "인", "pos": Vector2(size.x - 24, size.y - 24)}
|
{"text": "", "pos": Vector2(size.x - 24, size.y - 24)}
|
||||||
]
|
]
|
||||||
for spec in corner_specs:
|
for spec in corner_specs:
|
||||||
var seal := _make_seal_tile(str(spec["text"]), 20)
|
var seal := _make_seal_tile(str(spec["text"]), 20)
|
||||||
@@ -1161,11 +1165,24 @@ func _create_hud() -> void:
|
|||||||
_apply_label_style(selected_label, UI_PARCHMENT_TEXT)
|
_apply_label_style(selected_label, UI_PARCHMENT_TEXT)
|
||||||
selected_row.add_child(selected_label)
|
selected_row.add_child(selected_label)
|
||||||
|
|
||||||
|
var mission_title_row := HBoxContainer.new()
|
||||||
|
mission_title_row.add_theme_constant_override("separation", 8)
|
||||||
|
side_column.add_child(mission_title_row)
|
||||||
|
|
||||||
mission_title_label = Label.new()
|
mission_title_label = Label.new()
|
||||||
mission_title_label.text = "전투 목표"
|
mission_title_label.text = "전투 목표"
|
||||||
|
mission_title_label.custom_minimum_size = Vector2(296, 28)
|
||||||
|
mission_title_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
mission_title_label.add_theme_font_size_override("font_size", 14)
|
mission_title_label.add_theme_font_size_override("font_size", 14)
|
||||||
_apply_label_style(mission_title_label, UI_OLD_BRONZE)
|
_apply_label_style(mission_title_label, UI_OLD_BRONZE)
|
||||||
side_column.add_child(mission_title_label)
|
mission_title_row.add_child(mission_title_label)
|
||||||
|
|
||||||
|
mission_toggle_button = Button.new()
|
||||||
|
mission_toggle_button.text = "접기"
|
||||||
|
mission_toggle_button.tooltip_text = "전투 목표 판을 접거나 펼칩니다."
|
||||||
|
mission_toggle_button.custom_minimum_size = Vector2(78, 28)
|
||||||
|
mission_toggle_button.pressed.connect(_on_mission_toggle_pressed)
|
||||||
|
mission_title_row.add_child(mission_toggle_button)
|
||||||
|
|
||||||
mission_detail_panel = PanelContainer.new()
|
mission_detail_panel = PanelContainer.new()
|
||||||
mission_detail_panel.custom_minimum_size = Vector2(420, 134)
|
mission_detail_panel.custom_minimum_size = Vector2(420, 134)
|
||||||
@@ -1178,7 +1195,7 @@ func _create_hud() -> void:
|
|||||||
mission_detail_panel.add_child(mission_detail_row)
|
mission_detail_panel.add_child(mission_detail_row)
|
||||||
|
|
||||||
mission_detail_row.add_child(_make_bamboo_gutter(12, 112))
|
mission_detail_row.add_child(_make_bamboo_gutter(12, 112))
|
||||||
mission_detail_row.add_child(_make_edict_marker_stack(["명", "승", "패"], 112))
|
mission_detail_row.add_child(_make_edict_marker_stack(["", "", ""], 112))
|
||||||
|
|
||||||
mission_detail_label = Label.new()
|
mission_detail_label = Label.new()
|
||||||
mission_detail_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
mission_detail_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||||
@@ -1186,7 +1203,7 @@ func _create_hud() -> void:
|
|||||||
mission_detail_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
mission_detail_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
_apply_label_style(mission_detail_label, UI_PARCHMENT_TEXT)
|
_apply_label_style(mission_detail_label, UI_PARCHMENT_TEXT)
|
||||||
mission_detail_row.add_child(mission_detail_label)
|
mission_detail_row.add_child(mission_detail_label)
|
||||||
mission_detail_row.add_child(_make_edict_marker_stack(["공", "징", "봉"], 112))
|
mission_detail_row.add_child(_make_edict_marker_stack(["", "", ""], 112))
|
||||||
mission_detail_row.add_child(_make_bamboo_gutter(12, 112))
|
mission_detail_row.add_child(_make_bamboo_gutter(12, 112))
|
||||||
|
|
||||||
cell_info_label = Label.new()
|
cell_info_label = Label.new()
|
||||||
@@ -1459,7 +1476,7 @@ func _create_hud() -> void:
|
|||||||
briefing_shell.add_child(briefing_column)
|
briefing_shell.add_child(briefing_column)
|
||||||
briefing_shell.add_child(_make_bamboo_gutter(22, 632))
|
briefing_shell.add_child(_make_bamboo_gutter(22, 632))
|
||||||
briefing_column.add_child(_make_scroll_rod(680, 6))
|
briefing_column.add_child(_make_scroll_rod(680, 6))
|
||||||
briefing_column.add_child(_make_edict_title_strip(["위", "군", "명"], 680, 24))
|
briefing_column.add_child(_make_edict_title_strip(["", "", ""], 680, 24))
|
||||||
briefing_column.add_child(_make_tablet_binding(680, 16))
|
briefing_column.add_child(_make_tablet_binding(680, 16))
|
||||||
|
|
||||||
var briefing_title_panel := PanelContainer.new()
|
var briefing_title_panel := PanelContainer.new()
|
||||||
@@ -1486,12 +1503,23 @@ func _create_hud() -> void:
|
|||||||
briefing_column.add_child(briefing_location_label)
|
briefing_column.add_child(briefing_location_label)
|
||||||
|
|
||||||
briefing_start_button = Button.new()
|
briefing_start_button = Button.new()
|
||||||
briefing_start_button.text = "닫고 전투 시작"
|
briefing_start_button.text = "전투 시작"
|
||||||
|
briefing_start_button.tooltip_text = "브리핑을 닫고 전투를 시작합니다."
|
||||||
briefing_start_button.pressed.connect(_on_begin_battle_pressed)
|
briefing_start_button.pressed.connect(_on_begin_battle_pressed)
|
||||||
briefing_column.add_child(briefing_start_button)
|
briefing_column.add_child(briefing_start_button)
|
||||||
|
|
||||||
briefing_column.add_child(_make_scroll_rod(680, 5))
|
briefing_column.add_child(_make_scroll_rod(680, 5))
|
||||||
briefing_column.add_child(_make_section_caption("승리 조건과 패배 조건", 680))
|
var briefing_objective_header := HBoxContainer.new()
|
||||||
|
briefing_objective_header.add_theme_constant_override("separation", 8)
|
||||||
|
briefing_column.add_child(briefing_objective_header)
|
||||||
|
briefing_objective_header.add_child(_make_section_caption("승리와 패배", 548))
|
||||||
|
|
||||||
|
briefing_objective_toggle_button = Button.new()
|
||||||
|
briefing_objective_toggle_button.text = "조건 접기"
|
||||||
|
briefing_objective_toggle_button.tooltip_text = "승리와 패배 조건을 접거나 펼칩니다."
|
||||||
|
briefing_objective_toggle_button.custom_minimum_size = Vector2(124, 28)
|
||||||
|
briefing_objective_toggle_button.pressed.connect(_on_briefing_objective_toggle_pressed)
|
||||||
|
briefing_objective_header.add_child(briefing_objective_toggle_button)
|
||||||
|
|
||||||
briefing_objective_panel = PanelContainer.new()
|
briefing_objective_panel = PanelContainer.new()
|
||||||
briefing_objective_panel.custom_minimum_size = Vector2(680, 104)
|
briefing_objective_panel.custom_minimum_size = Vector2(680, 104)
|
||||||
@@ -1503,7 +1531,7 @@ func _create_hud() -> void:
|
|||||||
briefing_objective_panel.add_child(briefing_objective_row)
|
briefing_objective_panel.add_child(briefing_objective_row)
|
||||||
|
|
||||||
briefing_objective_row.add_child(_make_bamboo_gutter(18, 84))
|
briefing_objective_row.add_child(_make_bamboo_gutter(18, 84))
|
||||||
briefing_objective_row.add_child(_make_edict_marker_stack(["명", "승", "패"], 84))
|
briefing_objective_row.add_child(_make_edict_marker_stack(["", "", ""], 84))
|
||||||
|
|
||||||
briefing_objective_label = Label.new()
|
briefing_objective_label = Label.new()
|
||||||
briefing_objective_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
briefing_objective_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||||
@@ -1511,7 +1539,7 @@ func _create_hud() -> void:
|
|||||||
briefing_objective_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
briefing_objective_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
_apply_label_style(briefing_objective_label, UI_AGED_INK, 15)
|
_apply_label_style(briefing_objective_label, UI_AGED_INK, 15)
|
||||||
briefing_objective_row.add_child(briefing_objective_label)
|
briefing_objective_row.add_child(briefing_objective_label)
|
||||||
briefing_objective_row.add_child(_make_edict_marker_stack(["공", "보", "상"], 84))
|
briefing_objective_row.add_child(_make_edict_marker_stack(["", "", ""], 84))
|
||||||
briefing_objective_row.add_child(_make_bamboo_gutter(18, 84))
|
briefing_objective_row.add_child(_make_bamboo_gutter(18, 84))
|
||||||
|
|
||||||
briefing_column.add_child(_make_ink_wash_rule(680, 8))
|
briefing_column.add_child(_make_ink_wash_rule(680, 8))
|
||||||
@@ -1525,7 +1553,7 @@ func _create_hud() -> void:
|
|||||||
var briefing_camp_overview_shell := HBoxContainer.new()
|
var briefing_camp_overview_shell := HBoxContainer.new()
|
||||||
briefing_camp_overview_shell.add_theme_constant_override("separation", 10)
|
briefing_camp_overview_shell.add_theme_constant_override("separation", 10)
|
||||||
briefing_camp_overview_panel.add_child(briefing_camp_overview_shell)
|
briefing_camp_overview_panel.add_child(briefing_camp_overview_shell)
|
||||||
briefing_camp_overview_shell.add_child(_make_edict_marker_stack(["도", "지"], 86))
|
briefing_camp_overview_shell.add_child(_make_edict_marker_stack(["", ""], 86))
|
||||||
|
|
||||||
briefing_camp_overview_row = HBoxContainer.new()
|
briefing_camp_overview_row = HBoxContainer.new()
|
||||||
briefing_camp_overview_row.custom_minimum_size = Vector2(580, 92)
|
briefing_camp_overview_row.custom_minimum_size = Vector2(580, 92)
|
||||||
@@ -1579,7 +1607,7 @@ func _create_hud() -> void:
|
|||||||
briefing_scroll.add_child(briefing_label)
|
briefing_scroll.add_child(briefing_label)
|
||||||
|
|
||||||
briefing_column.add_child(_make_tablet_binding(680, 14))
|
briefing_column.add_child(_make_tablet_binding(680, 14))
|
||||||
briefing_column.add_child(_make_edict_title_strip(["인", "출", "진"], 680, 22))
|
briefing_column.add_child(_make_edict_title_strip(["", "", ""], 680, 22))
|
||||||
|
|
||||||
var prep_button_row := HBoxContainer.new()
|
var prep_button_row := HBoxContainer.new()
|
||||||
prep_button_row.add_theme_constant_override("separation", 8)
|
prep_button_row.add_theme_constant_override("separation", 8)
|
||||||
@@ -1827,7 +1855,7 @@ func _create_hud() -> void:
|
|||||||
dialogue_row.add_child(dialogue_right_tassel)
|
dialogue_row.add_child(dialogue_right_tassel)
|
||||||
|
|
||||||
dialogue_column.add_child(_make_tablet_binding(DIALOGUE_TEXT_SIZE.x, 13))
|
dialogue_column.add_child(_make_tablet_binding(DIALOGUE_TEXT_SIZE.x, 13))
|
||||||
dialogue_column.add_child(_make_edict_title_strip(["군", "담", "록"], DIALOGUE_TEXT_SIZE.x, 22))
|
dialogue_column.add_child(_make_edict_title_strip(["", "", ""], DIALOGUE_TEXT_SIZE.x, 22))
|
||||||
|
|
||||||
dialogue_seal_ribbon = _make_seal_ribbon(DIALOGUE_TEXT_SIZE.x, 18)
|
dialogue_seal_ribbon = _make_seal_ribbon(DIALOGUE_TEXT_SIZE.x, 18)
|
||||||
dialogue_column.add_child(dialogue_seal_ribbon)
|
dialogue_column.add_child(dialogue_seal_ribbon)
|
||||||
@@ -1840,7 +1868,7 @@ func _create_hud() -> void:
|
|||||||
var dialogue_speaker_row := HBoxContainer.new()
|
var dialogue_speaker_row := HBoxContainer.new()
|
||||||
dialogue_speaker_row.add_theme_constant_override("separation", 8)
|
dialogue_speaker_row.add_theme_constant_override("separation", 8)
|
||||||
dialogue_speaker_panel.add_child(dialogue_speaker_row)
|
dialogue_speaker_panel.add_child(dialogue_speaker_row)
|
||||||
dialogue_speaker_row.add_child(_make_seal_tile("명", 24))
|
dialogue_speaker_row.add_child(_make_seal_tile("", 24))
|
||||||
|
|
||||||
dialogue_speaker_label = Label.new()
|
dialogue_speaker_label = Label.new()
|
||||||
dialogue_speaker_label.custom_minimum_size = Vector2(DIALOGUE_TEXT_SIZE.x - 88, 24)
|
dialogue_speaker_label.custom_minimum_size = Vector2(DIALOGUE_TEXT_SIZE.x - 88, 24)
|
||||||
@@ -1848,7 +1876,7 @@ func _create_hud() -> void:
|
|||||||
dialogue_speaker_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
dialogue_speaker_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||||
_apply_label_style(dialogue_speaker_label, UI_PARCHMENT_TEXT, 18)
|
_apply_label_style(dialogue_speaker_label, UI_PARCHMENT_TEXT, 18)
|
||||||
dialogue_speaker_row.add_child(dialogue_speaker_label)
|
dialogue_speaker_row.add_child(dialogue_speaker_label)
|
||||||
dialogue_speaker_row.add_child(_make_seal_tile("말", 24))
|
dialogue_speaker_row.add_child(_make_seal_tile("", 24))
|
||||||
|
|
||||||
dialogue_text_panel = PanelContainer.new()
|
dialogue_text_panel = PanelContainer.new()
|
||||||
dialogue_text_panel.custom_minimum_size = Vector2(DIALOGUE_TEXT_SIZE.x, 104)
|
dialogue_text_panel.custom_minimum_size = Vector2(DIALOGUE_TEXT_SIZE.x, 104)
|
||||||
@@ -1860,7 +1888,7 @@ func _create_hud() -> void:
|
|||||||
dialogue_text_panel.add_child(dialogue_text_row)
|
dialogue_text_panel.add_child(dialogue_text_row)
|
||||||
|
|
||||||
dialogue_text_row.add_child(_make_bamboo_gutter(18, DIALOGUE_TEXT_SIZE.y - 20))
|
dialogue_text_row.add_child(_make_bamboo_gutter(18, DIALOGUE_TEXT_SIZE.y - 20))
|
||||||
dialogue_text_row.add_child(_make_edict_marker_stack(["말", "문", "답"], DIALOGUE_TEXT_SIZE.y - 20))
|
dialogue_text_row.add_child(_make_edict_marker_stack(["", "", ""], DIALOGUE_TEXT_SIZE.y - 20))
|
||||||
|
|
||||||
dialogue_text_label = Label.new()
|
dialogue_text_label = Label.new()
|
||||||
dialogue_text_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
dialogue_text_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||||||
@@ -1869,7 +1897,7 @@ func _create_hud() -> void:
|
|||||||
dialogue_text_label.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
dialogue_text_label.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||||
_apply_label_style(dialogue_text_label, UI_AGED_INK, 17)
|
_apply_label_style(dialogue_text_label, UI_AGED_INK, 17)
|
||||||
dialogue_text_row.add_child(dialogue_text_label)
|
dialogue_text_row.add_child(dialogue_text_label)
|
||||||
dialogue_text_row.add_child(_make_edict_marker_stack(["록", "군", "인"], DIALOGUE_TEXT_SIZE.y - 20))
|
dialogue_text_row.add_child(_make_edict_marker_stack(["", "", ""], DIALOGUE_TEXT_SIZE.y - 20))
|
||||||
dialogue_text_row.add_child(_make_bamboo_gutter(18, DIALOGUE_TEXT_SIZE.y - 20))
|
dialogue_text_row.add_child(_make_bamboo_gutter(18, DIALOGUE_TEXT_SIZE.y - 20))
|
||||||
|
|
||||||
dialogue_column.add_child(_make_scroll_rod(DIALOGUE_TEXT_SIZE.x, 3))
|
dialogue_column.add_child(_make_scroll_rod(DIALOGUE_TEXT_SIZE.x, 3))
|
||||||
@@ -1925,7 +1953,7 @@ func _create_hud() -> void:
|
|||||||
var result_body_row := HBoxContainer.new()
|
var result_body_row := HBoxContainer.new()
|
||||||
result_body_row.add_theme_constant_override("separation", 8)
|
result_body_row.add_theme_constant_override("separation", 8)
|
||||||
result_body_panel.add_child(result_body_row)
|
result_body_panel.add_child(result_body_row)
|
||||||
result_body_row.add_child(_make_edict_marker_stack(["공", "상", "명"], 198))
|
result_body_row.add_child(_make_edict_marker_stack(["", "", ""], 198))
|
||||||
|
|
||||||
result_label = Label.new()
|
result_label = Label.new()
|
||||||
result_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT
|
result_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT
|
||||||
@@ -1936,7 +1964,7 @@ func _create_hud() -> void:
|
|||||||
result_label.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
result_label.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
||||||
_apply_label_style(result_label, UI_AGED_INK, 16)
|
_apply_label_style(result_label, UI_AGED_INK, 16)
|
||||||
result_body_row.add_child(result_label)
|
result_body_row.add_child(result_label)
|
||||||
result_body_row.add_child(_make_edict_marker_stack(["기", "록", "인"], 198))
|
result_body_row.add_child(_make_edict_marker_stack(["", "", ""], 198))
|
||||||
|
|
||||||
result_column.add_child(_make_scroll_rod(520, 3))
|
result_column.add_child(_make_scroll_rod(520, 3))
|
||||||
|
|
||||||
@@ -1961,6 +1989,7 @@ func _create_hud() -> void:
|
|||||||
tactic_button,
|
tactic_button,
|
||||||
item_button,
|
item_button,
|
||||||
equip_button,
|
equip_button,
|
||||||
|
mission_toggle_button,
|
||||||
threat_button,
|
threat_button,
|
||||||
end_turn_button,
|
end_turn_button,
|
||||||
post_move_attack_button,
|
post_move_attack_button,
|
||||||
@@ -1980,6 +2009,7 @@ func _create_hud() -> void:
|
|||||||
formation_button,
|
formation_button,
|
||||||
save_button,
|
save_button,
|
||||||
briefing_start_button,
|
briefing_start_button,
|
||||||
|
briefing_objective_toggle_button,
|
||||||
shop_buy_button,
|
shop_buy_button,
|
||||||
shop_sell_button,
|
shop_sell_button,
|
||||||
manual_save_button,
|
manual_save_button,
|
||||||
@@ -2358,6 +2388,9 @@ func _handle_key(event: InputEventKey) -> void:
|
|||||||
elif save_menu != null and save_menu.visible:
|
elif save_menu != null and save_menu.visible:
|
||||||
_hide_save_menu()
|
_hide_save_menu()
|
||||||
elif briefing_panel != null and briefing_panel.visible:
|
elif briefing_panel != null and briefing_panel.visible:
|
||||||
|
if _set_briefing_objective_collapsed(true):
|
||||||
|
_play_ui_cancel()
|
||||||
|
return
|
||||||
_on_begin_battle_pressed()
|
_on_begin_battle_pressed()
|
||||||
elif post_move_picker_panel != null and post_move_picker_panel.visible:
|
elif post_move_picker_panel != null and post_move_picker_panel.visible:
|
||||||
_on_post_move_picker_back_pressed()
|
_on_post_move_picker_back_pressed()
|
||||||
@@ -3041,7 +3074,7 @@ func _attack_target_marker_entries() -> Array[Dictionary]:
|
|||||||
"cell": cell,
|
"cell": cell,
|
||||||
"target_id": str(target.get("id", "")),
|
"target_id": str(target.get("id", "")),
|
||||||
"text": "%s %d%%" % [result_text, hit_chance],
|
"text": "%s %d%%" % [result_text, hit_chance],
|
||||||
"kind": "damage"
|
"kind": _attack_preview_badge_kind(preview)
|
||||||
})
|
})
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@@ -3718,7 +3751,24 @@ func _attack_target_preview_badge(selected: Dictionary, target: Dictionary) -> D
|
|||||||
|
|
||||||
var hit_chance := int(preview.get("hit_chance", 100))
|
var hit_chance := int(preview.get("hit_chance", 100))
|
||||||
var result_text := "퇴각" if bool(preview.get("would_defeat", false)) else "-%d" % int(preview.get("damage", 0))
|
var result_text := "퇴각" if bool(preview.get("would_defeat", false)) else "-%d" % int(preview.get("damage", 0))
|
||||||
return _make_target_preview_badge("%s %d%%" % [result_text, hit_chance], "damage")
|
var counter_text := _format_attack_counter_badge_suffix(preview)
|
||||||
|
return _make_target_preview_badge("%s %d%%%s" % [result_text, hit_chance, counter_text], _attack_preview_badge_kind(preview))
|
||||||
|
|
||||||
|
|
||||||
|
func _format_attack_counter_badge_suffix(preview: Dictionary) -> String:
|
||||||
|
if not bool(preview.get("counter_in_range", false)):
|
||||||
|
return ""
|
||||||
|
if bool(preview.get("counter_would_defeat", false)):
|
||||||
|
return " 반격!"
|
||||||
|
return " 반%d" % int(preview.get("counter_damage", 0))
|
||||||
|
|
||||||
|
|
||||||
|
func _attack_preview_badge_kind(preview: Dictionary) -> String:
|
||||||
|
if bool(preview.get("counter_would_defeat", false)):
|
||||||
|
return "danger"
|
||||||
|
if bool(preview.get("counter_in_range", false)):
|
||||||
|
return "counter"
|
||||||
|
return "damage"
|
||||||
|
|
||||||
|
|
||||||
func _skill_target_preview_badge(selected: Dictionary, target: Dictionary) -> Dictionary:
|
func _skill_target_preview_badge(selected: Dictionary, target: Dictionary) -> Dictionary:
|
||||||
@@ -3882,6 +3932,10 @@ func _support_preview_badge_kind(effect_text: String) -> String:
|
|||||||
func _target_preview_badge_color(kind: String) -> Color:
|
func _target_preview_badge_color(kind: String) -> Color:
|
||||||
if kind == "damage":
|
if kind == "damage":
|
||||||
return Color(1.0, 0.38, 0.26)
|
return Color(1.0, 0.38, 0.26)
|
||||||
|
if kind == "counter":
|
||||||
|
return Color(1.0, 0.64, 0.22)
|
||||||
|
if kind == "danger":
|
||||||
|
return Color(1.0, 0.18, 0.14)
|
||||||
if kind == "move":
|
if kind == "move":
|
||||||
return Color(0.38, 0.70, 1.0)
|
return Color(0.38, 0.70, 1.0)
|
||||||
if kind == "select":
|
if kind == "select":
|
||||||
@@ -4015,7 +4069,7 @@ func _format_objective_hud_text() -> String:
|
|||||||
if not defeat.is_empty():
|
if not defeat.is_empty():
|
||||||
lines.append("패배: %s" % defeat)
|
lines.append("패배: %s" % defeat)
|
||||||
if lines.is_empty():
|
if lines.is_empty():
|
||||||
return "승리 조건 대기"
|
return "목표 대기"
|
||||||
return _join_strings(lines, "\n")
|
return _join_strings(lines, "\n")
|
||||||
|
|
||||||
|
|
||||||
@@ -4206,7 +4260,11 @@ func _unit_current_terrain_text(unit: Dictionary) -> String:
|
|||||||
|
|
||||||
|
|
||||||
func _update_mission_panel() -> void:
|
func _update_mission_panel() -> void:
|
||||||
if mission_detail_label == null:
|
if mission_detail_panel != null:
|
||||||
|
mission_detail_panel.visible = not mission_detail_collapsed
|
||||||
|
if mission_toggle_button != null:
|
||||||
|
mission_toggle_button.text = "보기" if mission_detail_collapsed else "접기"
|
||||||
|
if mission_detail_label == null or mission_detail_collapsed:
|
||||||
return
|
return
|
||||||
mission_detail_label.text = _format_mission_panel_text()
|
mission_detail_label.text = _format_mission_panel_text()
|
||||||
|
|
||||||
@@ -4215,18 +4273,18 @@ func _format_mission_panel_text() -> String:
|
|||||||
var lines := []
|
var lines := []
|
||||||
var victory := str(state.objectives.get("victory", ""))
|
var victory := str(state.objectives.get("victory", ""))
|
||||||
if not victory.is_empty():
|
if not victory.is_empty():
|
||||||
_append_edict_clause(lines, "승리 조건", "목표", victory)
|
lines.append("승리: %s" % victory)
|
||||||
var defeat := str(state.objectives.get("defeat", ""))
|
var defeat := str(state.objectives.get("defeat", ""))
|
||||||
if not defeat.is_empty():
|
if not defeat.is_empty():
|
||||||
_append_edict_clause(lines, "패배 조건", "주의", defeat)
|
lines.append("패배: %s" % defeat)
|
||||||
var progress_lines := state.get_objective_progress_lines(false, false)
|
var progress_lines := state.get_objective_progress_lines(false, false)
|
||||||
if not progress_lines.is_empty():
|
if not progress_lines.is_empty():
|
||||||
_append_edict_clause(lines, "진행", "현황", _join_strings(progress_lines, " · "))
|
lines.append("진행: %s" % _join_strings(progress_lines, " · "))
|
||||||
var risk_lines := state.get_defeat_progress_lines()
|
var risk_lines := state.get_defeat_progress_lines()
|
||||||
if not risk_lines.is_empty():
|
if not risk_lines.is_empty():
|
||||||
_append_edict_clause(lines, "위험", "경고", _join_strings(risk_lines, " · "))
|
lines.append("주의: %s" % _join_strings(risk_lines, " · "))
|
||||||
if lines.is_empty():
|
if lines.is_empty():
|
||||||
return "군령\n 대기|아직 열린 군령이 없습니다."
|
return "열린 목표 없음"
|
||||||
return _join_strings(lines, "\n")
|
return _join_strings(lines, "\n")
|
||||||
|
|
||||||
|
|
||||||
@@ -4699,9 +4757,9 @@ func _format_hanja_number(value: int) -> String:
|
|||||||
func _on_objective_updated(victory: String, defeat: String) -> void:
|
func _on_objective_updated(victory: String, defeat: String) -> void:
|
||||||
var notice_lines := ["군령 갱신"]
|
var notice_lines := ["군령 갱신"]
|
||||||
if not victory.is_empty():
|
if not victory.is_empty():
|
||||||
_append_edict_clause(notice_lines, "승리 조건", "목표", victory)
|
notice_lines.append("목표: %s" % victory)
|
||||||
if not defeat.is_empty():
|
if not defeat.is_empty():
|
||||||
_append_edict_clause(notice_lines, "패배 조건", "주의", defeat)
|
notice_lines.append("주의: %s" % defeat)
|
||||||
if objective_notice_label != null:
|
if objective_notice_label != null:
|
||||||
objective_notice_label.text = "\n".join(notice_lines)
|
objective_notice_label.text = "\n".join(notice_lines)
|
||||||
if objective_notice_panel != null:
|
if objective_notice_panel != null:
|
||||||
@@ -5323,6 +5381,12 @@ func _on_threat_pressed() -> void:
|
|||||||
queue_redraw()
|
queue_redraw()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_mission_toggle_pressed() -> void:
|
||||||
|
_play_ui_click()
|
||||||
|
mission_detail_collapsed = not mission_detail_collapsed
|
||||||
|
_update_mission_panel()
|
||||||
|
|
||||||
|
|
||||||
func _on_restart_pressed() -> void:
|
func _on_restart_pressed() -> void:
|
||||||
if campaign_complete_screen:
|
if campaign_complete_screen:
|
||||||
return
|
return
|
||||||
@@ -5465,16 +5529,13 @@ func _format_briefing_objectives() -> String:
|
|||||||
var lines := []
|
var lines := []
|
||||||
var victory := str(state.objectives.get("victory", ""))
|
var victory := str(state.objectives.get("victory", ""))
|
||||||
if not victory.is_empty():
|
if not victory.is_empty():
|
||||||
lines.append("승리 조건")
|
lines.append("승리: %s" % victory)
|
||||||
lines.append(" %s" % victory)
|
|
||||||
var defeat := str(state.objectives.get("defeat", ""))
|
var defeat := str(state.objectives.get("defeat", ""))
|
||||||
if not defeat.is_empty():
|
if not defeat.is_empty():
|
||||||
lines.append("패배 조건")
|
lines.append("패배: %s" % defeat)
|
||||||
lines.append(" %s" % defeat)
|
|
||||||
var rewards_text := _format_briefing_rewards()
|
var rewards_text := _format_briefing_rewards()
|
||||||
if not rewards_text.is_empty():
|
if not rewards_text.is_empty():
|
||||||
lines.append("보상")
|
lines.append("보상: %s" % rewards_text)
|
||||||
lines.append(" %s" % rewards_text)
|
|
||||||
return _join_strings(lines, "\n")
|
return _join_strings(lines, "\n")
|
||||||
|
|
||||||
|
|
||||||
@@ -5682,8 +5743,10 @@ func _show_briefing() -> void:
|
|||||||
briefing_title_label.text = _format_briefing_title(briefing)
|
briefing_title_label.text = _format_briefing_title(briefing)
|
||||||
briefing_location_label.text = _format_briefing_location(briefing)
|
briefing_location_label.text = _format_briefing_location(briefing)
|
||||||
briefing_location_label.visible = not briefing_location_label.text.is_empty()
|
briefing_location_label.visible = not briefing_location_label.text.is_empty()
|
||||||
|
briefing_objective_collapsed = false
|
||||||
briefing_objective_label.text = _format_briefing_objectives()
|
briefing_objective_label.text = _format_briefing_objectives()
|
||||||
briefing_objective_label.visible = not briefing_objective_label.text.is_empty()
|
briefing_objective_label.visible = not briefing_objective_label.text.is_empty()
|
||||||
|
_update_briefing_objective_visibility()
|
||||||
briefing_label.text = body
|
briefing_label.text = body
|
||||||
briefing_panel.visible = true
|
briefing_panel.visible = true
|
||||||
_refresh_screen_backdrop_visibility()
|
_refresh_screen_backdrop_visibility()
|
||||||
@@ -5714,6 +5777,30 @@ func _show_briefing() -> void:
|
|||||||
save_button.disabled = prep_locked
|
save_button.disabled = prep_locked
|
||||||
|
|
||||||
|
|
||||||
|
func _on_briefing_objective_toggle_pressed() -> void:
|
||||||
|
_play_ui_click()
|
||||||
|
_set_briefing_objective_collapsed(not briefing_objective_collapsed)
|
||||||
|
|
||||||
|
|
||||||
|
func _set_briefing_objective_collapsed(collapsed: bool) -> bool:
|
||||||
|
if briefing_objective_label == null or briefing_objective_label.text.strip_edges().is_empty():
|
||||||
|
return false
|
||||||
|
if briefing_objective_collapsed == collapsed:
|
||||||
|
return false
|
||||||
|
briefing_objective_collapsed = collapsed
|
||||||
|
_update_briefing_objective_visibility()
|
||||||
|
return true
|
||||||
|
|
||||||
|
|
||||||
|
func _update_briefing_objective_visibility() -> void:
|
||||||
|
var has_text := briefing_objective_label != null and not briefing_objective_label.text.strip_edges().is_empty()
|
||||||
|
if briefing_objective_panel != null:
|
||||||
|
briefing_objective_panel.visible = has_text and not briefing_objective_collapsed
|
||||||
|
if briefing_objective_toggle_button != null:
|
||||||
|
briefing_objective_toggle_button.visible = has_text
|
||||||
|
briefing_objective_toggle_button.text = "조건 보기" if briefing_objective_collapsed else "조건 접기"
|
||||||
|
|
||||||
|
|
||||||
func _on_begin_battle_pressed() -> void:
|
func _on_begin_battle_pressed() -> void:
|
||||||
_play_ui_confirm()
|
_play_ui_confirm()
|
||||||
_start_battle_from_briefing()
|
_start_battle_from_briefing()
|
||||||
|
|||||||
@@ -29,7 +29,13 @@ func _init() -> void:
|
|||||||
failures,
|
failures,
|
||||||
"001 turn pressure risk",
|
"001 turn pressure risk",
|
||||||
_defeat_text("res://data/scenarios/001_yellow_turbans.json"),
|
_defeat_text("res://data/scenarios/001_yellow_turbans.json"),
|
||||||
"잔여 군령 15"
|
"잔여 군령 17"
|
||||||
|
)
|
||||||
|
_check_contains(
|
||||||
|
failures,
|
||||||
|
"001 late reserve gate",
|
||||||
|
_progress_text("res://data/scenarios/001_yellow_turbans.json", false),
|
||||||
|
"제9군령에 전공 갱신"
|
||||||
)
|
)
|
||||||
_check_contains(
|
_check_contains(
|
||||||
failures,
|
failures,
|
||||||
@@ -69,6 +75,7 @@ func _init() -> void:
|
|||||||
_check_objective_notice(failures)
|
_check_objective_notice(failures)
|
||||||
_check_terrain_recovery(failures)
|
_check_terrain_recovery(failures)
|
||||||
_check_briefing_battlefield_overview(failures)
|
_check_briefing_battlefield_overview(failures)
|
||||||
|
_check_opening_battle_extended_pressure(failures)
|
||||||
_check_opening_board_avoids_side_panel(failures)
|
_check_opening_board_avoids_side_panel(failures)
|
||||||
|
|
||||||
if failures.is_empty():
|
if failures.is_empty():
|
||||||
@@ -227,8 +234,14 @@ func _check_opening_battle_requires_castle_capture(failures: Array[String]) -> v
|
|||||||
var cao_cao: Dictionary = state.get_unit("cao_cao")
|
var cao_cao: Dictionary = state.get_unit("cao_cao")
|
||||||
cao_cao["pos"] = Vector2i(12, 1)
|
cao_cao["pos"] = Vector2i(12, 1)
|
||||||
state._check_battle_status()
|
state._check_battle_status()
|
||||||
|
if str(state.battle_status) != BattleStateScript.STATUS_ACTIVE:
|
||||||
|
failures.append("opening battle should wait for late reserve before victory, got %s" % str(state.battle_status))
|
||||||
|
state._run_events("turn_start", BattleStateScript.TEAM_ENEMY, 9)
|
||||||
|
for enemy in state.get_living_units(BattleStateScript.TEAM_ENEMY):
|
||||||
|
enemy["alive"] = false
|
||||||
|
state._check_battle_status()
|
||||||
if str(state.battle_status) != BattleStateScript.STATUS_VICTORY:
|
if str(state.battle_status) != BattleStateScript.STATUS_VICTORY:
|
||||||
failures.append("opening battle should end after enemies fall and castle is captured, got %s" % str(state.battle_status))
|
failures.append("opening battle should end after late reserve falls and castle is captured, got %s" % str(state.battle_status))
|
||||||
|
|
||||||
|
|
||||||
func _check_log_contains(failures: Array[String], logs: Array, expected: String) -> void:
|
func _check_log_contains(failures: Array[String], logs: Array, expected: String) -> void:
|
||||||
@@ -264,13 +277,33 @@ func _check_briefing_battlefield_overview(failures: Array[String]) -> void:
|
|||||||
return
|
return
|
||||||
var overview := scene._format_briefing_battlefield_overview_text()
|
var overview := scene._format_briefing_battlefield_overview_text()
|
||||||
_check_contains(failures, "001 briefing map size", overview, "14칸 x 10칸")
|
_check_contains(failures, "001 briefing map size", overview, "14칸 x 10칸")
|
||||||
_check_contains(failures, "001 briefing enemy count", overview, "적세 8명")
|
_check_contains(failures, "001 briefing enemy count", overview, "적세 12명")
|
||||||
_check_contains(failures, "001 briefing enemy classes", overview, "보병")
|
_check_contains(failures, "001 briefing enemy classes", overview, "보병")
|
||||||
_check_contains(failures, "001 briefing village recovery", overview, "마을 4칸 +6")
|
_check_contains(failures, "001 briefing village recovery", overview, "마을 4칸 +6")
|
||||||
_check_contains(failures, "001 briefing castle recovery", overview, "성채 4칸 +8")
|
_check_contains(failures, "001 briefing castle recovery", overview, "성채 4칸 +8")
|
||||||
scene.free()
|
scene.free()
|
||||||
|
|
||||||
|
|
||||||
|
func _check_opening_battle_extended_pressure(failures: Array[String]) -> void:
|
||||||
|
var state = BattleStateScript.new()
|
||||||
|
if not state.load_battle("res://data/scenarios/001_yellow_turbans.json"):
|
||||||
|
failures.append("opening battle pressure smoke could not load scenario")
|
||||||
|
return
|
||||||
|
if state.get_turn_limit() != 17:
|
||||||
|
failures.append("opening battle should allow play through turn 17, got limit %d" % state.get_turn_limit())
|
||||||
|
if state.get_living_units(BattleStateScript.TEAM_ENEMY).size() != 12:
|
||||||
|
failures.append("opening battle should start with 12 enemies, got %d" % state.get_living_units(BattleStateScript.TEAM_ENEMY).size())
|
||||||
|
if state._condition_gate_open({"after_event": "turn_9_western_reserve"}):
|
||||||
|
failures.append("opening battle final defeat-all condition should be gated until turn 9 reserve")
|
||||||
|
state._run_events("turn_start", BattleStateScript.TEAM_ENEMY, 4)
|
||||||
|
state._run_events("turn_start", BattleStateScript.TEAM_ENEMY, 7)
|
||||||
|
state._run_events("turn_start", BattleStateScript.TEAM_ENEMY, 9)
|
||||||
|
if not state._condition_gate_open({"after_event": "turn_9_western_reserve"}):
|
||||||
|
failures.append("opening battle final gate should open after turn 9 reserve event")
|
||||||
|
if state.get_living_units(BattleStateScript.TEAM_ENEMY).size() != 19:
|
||||||
|
failures.append("opening battle should field 19 enemies after late reserve, got %d" % state.get_living_units(BattleStateScript.TEAM_ENEMY).size())
|
||||||
|
|
||||||
|
|
||||||
func _check_opening_board_avoids_side_panel(failures: Array[String]) -> void:
|
func _check_opening_board_avoids_side_panel(failures: Array[String]) -> void:
|
||||||
var state = BattleStateScript.new()
|
var state = BattleStateScript.new()
|
||||||
if not state.load_battle("res://data/scenarios/001_yellow_turbans.json"):
|
if not state.load_battle("res://data/scenarios/001_yellow_turbans.json"):
|
||||||
@@ -293,12 +326,12 @@ func _check_objective_notice(failures: Array[String]) -> void:
|
|||||||
scene._on_objective_updated("Defeat the new vanguard.", "")
|
scene._on_objective_updated("Defeat the new vanguard.", "")
|
||||||
if scene.objective_notice_panel == null or not scene.objective_notice_panel.visible:
|
if scene.objective_notice_panel == null or not scene.objective_notice_panel.visible:
|
||||||
failures.append("objective notice panel should be visible after objective update")
|
failures.append("objective notice panel should be visible after objective update")
|
||||||
elif not scene.objective_notice_label.text.contains("군령 갱신") or not scene.objective_notice_label.text.contains("목표|Defeat the new vanguard."):
|
elif not scene.objective_notice_label.text.contains("군령 갱신") or not scene.objective_notice_label.text.contains("목표: Defeat the new vanguard."):
|
||||||
failures.append("objective notice label should use Korean order wording: %s" % scene.objective_notice_label.text)
|
failures.append("objective notice label should use Korean order wording: %s" % scene.objective_notice_label.text)
|
||||||
scene._on_objective_updated("", "Supply train is lost.")
|
scene._on_objective_updated("", "Supply train is lost.")
|
||||||
if scene.objective_notice_panel == null or not scene.objective_notice_panel.visible:
|
if scene.objective_notice_panel == null or not scene.objective_notice_panel.visible:
|
||||||
failures.append("objective notice panel should be visible after defeat update")
|
failures.append("objective notice panel should be visible after defeat update")
|
||||||
elif not scene.objective_notice_label.text.contains("군령 갱신") or not scene.objective_notice_label.text.contains("주의|Supply train is lost."):
|
elif not scene.objective_notice_label.text.contains("군령 갱신") or not scene.objective_notice_label.text.contains("주의: Supply train is lost."):
|
||||||
failures.append("objective notice label should use Korean risk wording: %s" % scene.objective_notice_label.text)
|
failures.append("objective notice label should use Korean risk wording: %s" % scene.objective_notice_label.text)
|
||||||
if scene._format_log_entry_text("Objective updated: Defeat the new vanguard.") != "군령 갱신: Defeat the new vanguard.":
|
if scene._format_log_entry_text("Objective updated: Defeat the new vanguard.") != "군령 갱신: Defeat the new vanguard.":
|
||||||
failures.append("objective log display should use Korean order wording")
|
failures.append("objective log display should use Korean order wording")
|
||||||
|
|||||||
@@ -95,6 +95,7 @@ func _init() -> void:
|
|||||||
_check_hud_focus_text(failures)
|
_check_hud_focus_text(failures)
|
||||||
_check_ancient_ui_theme(failures)
|
_check_ancient_ui_theme(failures)
|
||||||
_check_hover_intent_badges(failures)
|
_check_hover_intent_badges(failures)
|
||||||
|
_check_counter_attack_badge_variants(failures)
|
||||||
_check_action_button_disabled_reasons(failures)
|
_check_action_button_disabled_reasons(failures)
|
||||||
_check_terrain_and_unit_presentation(failures)
|
_check_terrain_and_unit_presentation(failures)
|
||||||
_check_objective_and_status_marker_helpers(failures)
|
_check_objective_and_status_marker_helpers(failures)
|
||||||
@@ -3085,19 +3086,24 @@ func _check_ancient_ui_theme(failures: Array[String]) -> void:
|
|||||||
scene.free()
|
scene.free()
|
||||||
return
|
return
|
||||||
var briefing_text := scene._format_briefing_objectives()
|
var briefing_text := scene._format_briefing_objectives()
|
||||||
for expected in ["승리 조건", "패배 조건", "보상"]:
|
scene.briefing_objective_label.text = briefing_text
|
||||||
|
scene._update_briefing_objective_visibility()
|
||||||
|
for expected in ["승리:", "패배:", "보상:"]:
|
||||||
if not briefing_text.contains(expected):
|
if not briefing_text.contains(expected):
|
||||||
failures.append("briefing objective parchment should use Korean heading `%s`: %s" % [expected, briefing_text])
|
failures.append("briefing objective parchment should use Korean heading `%s`: %s" % [expected, briefing_text])
|
||||||
for clutter in ["진행", "위험"]:
|
for clutter in ["승리 조건", "패배 조건", "진행", "위험"]:
|
||||||
if briefing_text.contains(clutter):
|
if briefing_text.contains(clutter):
|
||||||
failures.append("briefing objective parchment should stay concise without `%s`: %s" % [clutter, briefing_text])
|
failures.append("briefing objective parchment should stay concise without `%s`: %s" % [clutter, briefing_text])
|
||||||
var hud_objective := scene._format_objective_hud_text()
|
var hud_objective := scene._format_objective_hud_text()
|
||||||
if not hud_objective.contains("승리:") or not hud_objective.contains("패배:"):
|
if not hud_objective.contains("승리:") or not hud_objective.contains("패배:"):
|
||||||
failures.append("HUD objective should use concise Korean labels: %s" % hud_objective)
|
failures.append("HUD objective should use concise Korean labels: %s" % hud_objective)
|
||||||
var mission_text := scene._format_mission_panel_text()
|
var mission_text := scene._format_mission_panel_text()
|
||||||
for expected in ["승리 조건", "패배 조건", "진행", "위험", "목표|", "주의|", "현황|", "경고|"]:
|
for expected in ["승리:", "패배:", "진행:", "주의:"]:
|
||||||
if not mission_text.contains(expected):
|
if not mission_text.contains(expected):
|
||||||
failures.append("mission panel should use Korean campaign wording `%s`: %s" % [expected, mission_text])
|
failures.append("mission panel should use Korean campaign wording `%s`: %s" % [expected, mission_text])
|
||||||
|
for clutter in ["목표|", "주의|", "현황|", "경고|"]:
|
||||||
|
if mission_text.contains(clutter):
|
||||||
|
failures.append("mission panel should avoid old verbose marker `%s`: %s" % [clutter, mission_text])
|
||||||
_check_panel_style_fill(failures, scene.briefing_panel, "briefing panel", Color(0.54, 0.36, 0.14, 0.997))
|
_check_panel_style_fill(failures, scene.briefing_panel, "briefing panel", Color(0.54, 0.36, 0.14, 0.997))
|
||||||
_check_panel_style_frame(failures, scene.briefing_panel, "briefing panel", Color(0.035, 0.014, 0.008, 1.0), 16)
|
_check_panel_style_frame(failures, scene.briefing_panel, "briefing panel", Color(0.035, 0.014, 0.008, 1.0), 16)
|
||||||
_check_panel_style_fill(failures, scene.briefing_objective_panel, "briefing objective edict panel", Color(0.67, 0.49, 0.24, 0.998))
|
_check_panel_style_fill(failures, scene.briefing_objective_panel, "briefing objective edict panel", Color(0.67, 0.49, 0.24, 0.998))
|
||||||
@@ -3182,8 +3188,34 @@ func _check_ancient_ui_theme(failures: Array[String]) -> void:
|
|||||||
failures.append("dialogue continue button should use the project serif font override")
|
failures.append("dialogue continue button should use the project serif font override")
|
||||||
if scene.mission_title_label == null or scene.mission_title_label.text != "전투 목표":
|
if scene.mission_title_label == null or scene.mission_title_label.text != "전투 목표":
|
||||||
failures.append("mission panel title should use clear Korean wording")
|
failures.append("mission panel title should use clear Korean wording")
|
||||||
if scene.briefing_start_button == null or scene.briefing_start_button.text != "닫고 전투 시작":
|
if scene.mission_toggle_button == null or scene.mission_toggle_button.text != "접기":
|
||||||
failures.append("briefing start button should clearly close the briefing")
|
failures.append("mission objective panel should expose a close control")
|
||||||
|
else:
|
||||||
|
scene._on_mission_toggle_pressed()
|
||||||
|
if scene.mission_detail_panel == null or scene.mission_detail_panel.visible or scene.mission_toggle_button.text != "보기":
|
||||||
|
failures.append("mission objective panel should collapse cleanly")
|
||||||
|
scene._on_mission_toggle_pressed()
|
||||||
|
if scene.mission_detail_panel == null or not scene.mission_detail_panel.visible or scene.mission_toggle_button.text != "접기":
|
||||||
|
failures.append("mission objective panel should reopen cleanly")
|
||||||
|
if scene.briefing_start_button == null or scene.briefing_start_button.text != "전투 시작":
|
||||||
|
failures.append("briefing start button should clearly start after briefing")
|
||||||
|
if scene.briefing_objective_toggle_button == null or scene.briefing_objective_toggle_button.text != "조건 접기":
|
||||||
|
failures.append("briefing objective panel should expose a collapse button")
|
||||||
|
else:
|
||||||
|
scene._set_briefing_objective_collapsed(true)
|
||||||
|
if scene.briefing_objective_panel == null or scene.briefing_objective_panel.visible or scene.briefing_objective_toggle_button.text != "조건 보기":
|
||||||
|
failures.append("briefing objective panel should collapse cleanly")
|
||||||
|
scene._set_briefing_objective_collapsed(false)
|
||||||
|
if scene.briefing_objective_panel == null or not scene.briefing_objective_panel.visible or scene.briefing_objective_toggle_button.text != "조건 접기":
|
||||||
|
failures.append("briefing objective panel should reopen cleanly")
|
||||||
|
scene.briefing_panel.visible = true
|
||||||
|
scene.battle_started = false
|
||||||
|
var escape_event := InputEventKey.new()
|
||||||
|
escape_event.keycode = KEY_ESCAPE
|
||||||
|
scene._handle_key(escape_event)
|
||||||
|
if not scene.briefing_objective_collapsed or scene.battle_started:
|
||||||
|
failures.append("briefing Escape should collapse conditions before starting battle")
|
||||||
|
scene.briefing_panel.visible = false
|
||||||
if scene.shop_button == null or scene.shop_button.text != "군상":
|
if scene.shop_button == null or scene.shop_button.text != "군상":
|
||||||
failures.append("briefing shop button should use sutler wording")
|
failures.append("briefing shop button should use sutler wording")
|
||||||
if scene.talk_button == null or scene.talk_button.text != "군막 회의":
|
if scene.talk_button == null or scene.talk_button.text != "군막 회의":
|
||||||
@@ -3212,8 +3244,8 @@ func _check_ancient_ui_theme(failures: Array[String]) -> void:
|
|||||||
failures.append("targeting hint title should use mark foe wording")
|
failures.append("targeting hint title should use mark foe wording")
|
||||||
if scene.targeting_hint_back_button == null or scene.targeting_hint_back_button.text != "죽간 거두기":
|
if scene.targeting_hint_back_button == null or scene.targeting_hint_back_button.text != "죽간 거두기":
|
||||||
failures.append("targeting hint back button should use bamboo-slip wording")
|
failures.append("targeting hint back button should use bamboo-slip wording")
|
||||||
scene._on_objective_updated("동문에 조서를 꽂아라.", "조조가 퇴각하면 패전이다.")
|
scene._on_objective_updated("동문에 깃발을 세워라.", "조조가 퇴각하면 패전이다.")
|
||||||
if scene.objective_notice_label == null or not scene.objective_notice_label.text.contains("목표|") or not scene.objective_notice_label.text.contains("주의|"):
|
if scene.objective_notice_label == null or not scene.objective_notice_label.text.contains("목표:") or not scene.objective_notice_label.text.contains("주의:"):
|
||||||
failures.append("objective update notice should use Korean annotation wording")
|
failures.append("objective update notice should use Korean annotation wording")
|
||||||
scene._update_briefing_camp_overview(scene.state.get_briefing(), false)
|
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 != "비단 전장도":
|
if scene.briefing_camp_overview_fallback_label == null or scene.briefing_camp_overview_fallback_label.text != "비단 전장도":
|
||||||
@@ -3492,6 +3524,10 @@ func _check_hover_intent_badges(failures: Array[String]) -> void:
|
|||||||
var attack_badge := scene._target_preview_badge()
|
var attack_badge := scene._target_preview_badge()
|
||||||
if attack_badge.is_empty() or str(attack_badge.get("kind", "")) == "move" or str(attack_badge.get("text", "")) == "행군":
|
if attack_badge.is_empty() or str(attack_badge.get("kind", "")) == "move" or str(attack_badge.get("text", "")) == "행군":
|
||||||
failures.append("enemy hover should keep attack badge priority: %s" % str(attack_badge))
|
failures.append("enemy hover should keep attack badge priority: %s" % str(attack_badge))
|
||||||
|
if not str(attack_badge.get("text", "")).contains("반"):
|
||||||
|
failures.append("adjacent attack badge should expose counterattack risk: %s" % str(attack_badge))
|
||||||
|
if str(attack_badge.get("kind", "")) != "counter" and str(attack_badge.get("kind", "")) != "danger":
|
||||||
|
failures.append("counterable attack badge should use a counter danger color kind: %s" % str(attack_badge))
|
||||||
|
|
||||||
var cao_cao: Dictionary = scene.state.get_unit("cao_cao")
|
var cao_cao: Dictionary = scene.state.get_unit("cao_cao")
|
||||||
cao_cao["moved"] = true
|
cao_cao["moved"] = true
|
||||||
@@ -3506,6 +3542,10 @@ func _check_hover_intent_badges(failures: Array[String]) -> void:
|
|||||||
failures.append("MOVE badge should use a distinct color")
|
failures.append("MOVE badge should use a distinct color")
|
||||||
if scene._target_preview_badge_color("select") == default_color:
|
if scene._target_preview_badge_color("select") == default_color:
|
||||||
failures.append("SELECT badge should use a distinct color")
|
failures.append("SELECT badge should use a distinct color")
|
||||||
|
if scene._target_preview_badge_color("counter") == default_color:
|
||||||
|
failures.append("COUNTER badge should use a distinct color")
|
||||||
|
if scene._target_preview_badge_color("danger") == default_color:
|
||||||
|
failures.append("DANGER badge should use a distinct color")
|
||||||
var board_rect: Rect2 = scene._board_rect()
|
var board_rect: Rect2 = scene._board_rect()
|
||||||
var edge_badge_rect: Rect2 = scene._target_preview_badge_rect(Vector2i(13, 9))
|
var edge_badge_rect: Rect2 = scene._target_preview_badge_rect(Vector2i(13, 9))
|
||||||
if edge_badge_rect.position.x < board_rect.position.x or edge_badge_rect.position.y < board_rect.position.y:
|
if edge_badge_rect.position.x < board_rect.position.x or edge_badge_rect.position.y < board_rect.position.y:
|
||||||
@@ -3517,6 +3557,60 @@ func _check_hover_intent_badges(failures: Array[String]) -> void:
|
|||||||
scene.free()
|
scene.free()
|
||||||
|
|
||||||
|
|
||||||
|
func _check_counter_attack_badge_variants(failures: Array[String]) -> void:
|
||||||
|
var scene = BattleSceneScript.new()
|
||||||
|
if not scene.state.load_battle("res://data/scenarios/001_yellow_turbans.json"):
|
||||||
|
failures.append("could not load battle scene state for counter badge variants")
|
||||||
|
scene.free()
|
||||||
|
return
|
||||||
|
scene.battle_started = true
|
||||||
|
scene.state.select_unit("cao_cao")
|
||||||
|
|
||||||
|
var cao_cao: Dictionary = scene.state.get_unit("cao_cao")
|
||||||
|
var infantry: Dictionary = scene.state.get_unit("yellow_turban_2")
|
||||||
|
var archer: Dictionary = scene.state.get_unit("yellow_turban_3")
|
||||||
|
if cao_cao.is_empty() or infantry.is_empty() or archer.is_empty():
|
||||||
|
failures.append("counter badge variant smoke missing units")
|
||||||
|
scene.free()
|
||||||
|
return
|
||||||
|
|
||||||
|
infantry["pos"] = Vector2i(2, 3)
|
||||||
|
infantry["hp"] = 99
|
||||||
|
infantry["max_hp"] = 99
|
||||||
|
archer["pos"] = Vector2i(10, 8)
|
||||||
|
scene.hover_cell = Vector2i(2, 3)
|
||||||
|
var counter_badge := scene._target_preview_badge()
|
||||||
|
if str(counter_badge.get("kind", "")) != "counter" or not str(counter_badge.get("text", "")).contains("반"):
|
||||||
|
failures.append("melee target should expose counter badge: %s" % str(counter_badge))
|
||||||
|
scene.basic_attack_targeting = true
|
||||||
|
scene._refresh_ranges()
|
||||||
|
var marker_entries := scene._attack_target_marker_entries()
|
||||||
|
if marker_entries.is_empty() or str(marker_entries[0].get("kind", "")) != "counter":
|
||||||
|
failures.append("attack target marker should carry counter kind: %s" % str(marker_entries))
|
||||||
|
scene.basic_attack_targeting = false
|
||||||
|
|
||||||
|
infantry["pos"] = Vector2i(10, 8)
|
||||||
|
archer["pos"] = Vector2i(2, 3)
|
||||||
|
archer["hp"] = 99
|
||||||
|
archer["max_hp"] = 99
|
||||||
|
scene.hover_cell = Vector2i(2, 3)
|
||||||
|
var no_counter_badge := scene._target_preview_badge()
|
||||||
|
if str(no_counter_badge.get("kind", "")) != "damage" or str(no_counter_badge.get("text", "")).contains("반"):
|
||||||
|
failures.append("adjacent archer target should not expose counter badge: %s" % str(no_counter_badge))
|
||||||
|
|
||||||
|
archer["pos"] = Vector2i(10, 8)
|
||||||
|
infantry["pos"] = Vector2i(2, 3)
|
||||||
|
infantry["hp"] = 99
|
||||||
|
infantry["max_hp"] = 99
|
||||||
|
infantry["atk"] = 999
|
||||||
|
cao_cao["hp"] = 1
|
||||||
|
scene.hover_cell = Vector2i(2, 3)
|
||||||
|
var danger_badge := scene._target_preview_badge()
|
||||||
|
if str(danger_badge.get("kind", "")) != "danger" or not str(danger_badge.get("text", "")).contains("반격!"):
|
||||||
|
failures.append("lethal counter should expose danger badge: %s" % str(danger_badge))
|
||||||
|
scene.free()
|
||||||
|
|
||||||
|
|
||||||
func _check_terrain_and_unit_presentation(failures: Array[String]) -> void:
|
func _check_terrain_and_unit_presentation(failures: Array[String]) -> void:
|
||||||
var scene = BattleSceneScript.new()
|
var scene = BattleSceneScript.new()
|
||||||
if not scene.state.load_battle("res://data/scenarios/001_yellow_turbans.json"):
|
if not scene.state.load_battle("res://data/scenarios/001_yellow_turbans.json"):
|
||||||
|
|||||||
Reference in New Issue
Block a user