152 lines
5.3 KiB
GDScript
152 lines
5.3 KiB
GDScript
extends SceneTree
|
|
|
|
const BattleStateScript := preload("res://scripts/core/battle_state.gd")
|
|
const BattleSceneScript := preload("res://scripts/scenes/battle_scene.gd")
|
|
|
|
|
|
func _init() -> void:
|
|
var failures: Array[String] = []
|
|
_check_required_only_roster(failures)
|
|
_check_optional_roster(failures)
|
|
_check_roster_button_guard(failures)
|
|
_check_roster_visual_rows(failures)
|
|
|
|
if failures.is_empty():
|
|
print("roster choices smoke ok")
|
|
quit(0)
|
|
return
|
|
|
|
for failure in failures:
|
|
push_error(failure)
|
|
quit(1)
|
|
|
|
|
|
func _check_required_only_roster(failures: Array[String]) -> void:
|
|
var state = _loaded_state(failures, "required-only roster", "res://data/scenarios/001_yellow_turbans.json")
|
|
if state == null:
|
|
return
|
|
if not state.has_deployment_roster():
|
|
failures.append("001 should keep roster rules for required deployments")
|
|
if state.has_deployment_roster_choices():
|
|
failures.append("001 should not expose roster choices when every player unit is required")
|
|
if state.try_set_unit_deployed("cao_cao", false):
|
|
failures.append("001 should not allow Cao Cao to be moved to reserve")
|
|
if state.try_set_unit_deployed("xiahou_dun", false):
|
|
failures.append("001 should not allow Xiahou Dun to be moved to reserve")
|
|
|
|
|
|
func _check_optional_roster(failures: Array[String]) -> void:
|
|
var state = _loaded_state(failures, "optional roster", "res://data/scenarios/007_xian_emperor_escort.json")
|
|
if state == null:
|
|
return
|
|
if not state.has_deployment_roster_choices():
|
|
failures.append("007 should expose roster choices for optional deployed officers")
|
|
|
|
|
|
func _check_roster_button_guard(failures: Array[String]) -> void:
|
|
var scene = BattleSceneScript.new()
|
|
scene._create_hud()
|
|
scene.state.load_battle("res://data/scenarios/001_yellow_turbans.json")
|
|
scene.battle_started = false
|
|
scene.briefing_panel.visible = true
|
|
scene._on_roster_pressed()
|
|
if scene.roster_menu.visible:
|
|
failures.append("roster menu should not open when there are no roster choices")
|
|
scene.free()
|
|
|
|
|
|
func _check_roster_visual_rows(failures: Array[String]) -> void:
|
|
var scene = BattleSceneScript.new()
|
|
scene._create_hud()
|
|
if not scene.state.load_battle(
|
|
"res://data/scenarios/007_xian_emperor_escort.json",
|
|
{},
|
|
{},
|
|
{},
|
|
["dian_wei", "xiahou_yuan", "cao_ren"]
|
|
):
|
|
failures.append("007 could not load for roster visual rows")
|
|
scene.free()
|
|
return
|
|
scene._rebuild_roster_menu()
|
|
if scene.roster_list == null or scene.roster_list.get_child_count() < 5:
|
|
failures.append("roster visual rows should list required and optional officers")
|
|
else:
|
|
var visible_text := _collect_child_text(scene.roster_list)
|
|
var tooltip_text := _collect_child_tooltips(scene.roster_list)
|
|
if not visible_text.contains("조조") or not visible_text.contains("품계") or not visible_text.contains("출진"):
|
|
failures.append("roster rows should show compact name/status badges: %s" % visible_text)
|
|
if visible_text.contains("병장:") or visible_text.contains("청동검") or visible_text.contains("피갑"):
|
|
failures.append("roster rows should leave equipment detail to tooltips: %s" % visible_text)
|
|
if not tooltip_text.contains("병장:") or not tooltip_text.contains("필수 출진") or not tooltip_text.contains("호위 대상"):
|
|
failures.append("roster row tooltips should preserve equipment and lock detail: %s" % tooltip_text)
|
|
if not tooltip_text.contains("클릭하면 출진과 예비"):
|
|
failures.append("roster row tooltips should explain optional toggle behavior: %s" % tooltip_text)
|
|
if not _has_descendant_texture(scene.roster_list):
|
|
failures.append("roster rows should reuse officer portrait artwork")
|
|
var cao_cao: Dictionary = scene.state.get_unit("cao_cao_ch7")
|
|
if scene._format_roster_unit_button_text(cao_cao).contains("품계"):
|
|
failures.append("roster row button text should stay compact")
|
|
scene.free()
|
|
|
|
|
|
func _loaded_state(failures: Array[String], label: String, path: String):
|
|
var state = BattleStateScript.new()
|
|
if not state.load_battle(path):
|
|
failures.append("%s could not load scenario: %s" % [label, path])
|
|
return null
|
|
return state
|
|
|
|
|
|
func _collect_child_text(root: Node) -> String:
|
|
if root == null:
|
|
return ""
|
|
var parts: Array[String] = []
|
|
_collect_child_text_into(root, parts)
|
|
return "\n".join(parts)
|
|
|
|
|
|
func _collect_child_text_into(root: Node, parts: Array[String]) -> void:
|
|
if root == null:
|
|
return
|
|
if root is Button:
|
|
var button_text := str((root as Button).text).strip_edges()
|
|
if not button_text.is_empty():
|
|
parts.append(button_text)
|
|
elif root is Label:
|
|
var label_text := str((root as Label).text).strip_edges()
|
|
if not label_text.is_empty():
|
|
parts.append(label_text)
|
|
for child in root.get_children():
|
|
_collect_child_text_into(child, parts)
|
|
|
|
|
|
func _collect_child_tooltips(root: Node) -> String:
|
|
if root == null:
|
|
return ""
|
|
var parts: Array[String] = []
|
|
_collect_child_tooltips_into(root, parts)
|
|
return "\n".join(parts)
|
|
|
|
|
|
func _collect_child_tooltips_into(root: Node, parts: Array[String]) -> void:
|
|
if root == null:
|
|
return
|
|
if root is Control:
|
|
var tooltip := str((root as Control).tooltip_text).strip_edges()
|
|
if not tooltip.is_empty():
|
|
parts.append(tooltip)
|
|
for child in root.get_children():
|
|
_collect_child_tooltips_into(child, parts)
|
|
|
|
|
|
func _has_descendant_texture(root: Node) -> bool:
|
|
if root == null:
|
|
return false
|
|
if root is TextureRect and (root as TextureRect).texture != null:
|
|
return true
|
|
for child in root.get_children():
|
|
if _has_descendant_texture(child):
|
|
return true
|
|
return false
|