extends SceneTree const BattleSceneScript := preload("res://scripts/scenes/battle_scene.gd") func _init() -> void: var failures: Array[String] = [] var scene = BattleSceneScript.new() if not scene.state.load_battle("res://data/scenarios/001_yellow_turbans.json"): push_error("Could not load smoke scenario.") quit(1) return if not scene.campaign_state.load_campaign("res://data/campaign/campaign.json"): push_error("Could not load smoke campaign.") quit(1) return scene.campaign_state.start_new("001_yellow_turbans") scene.campaign_state.gold = 320 scene.battle_result_summary = { "saved": true, "rewards_applied": true, "already_completed": false, "pending_choice": false, "gold": 100, "items": ["bean", "bean"], "joined_officers": ["xiahou_yuan", "cao_ren"], "left_officers": [], "progression_events": [ {"type": "level_up", "name": "조조", "to_level": 2} ], "next_scenario_id": "002_sishui_gate", "next_scenario_title": "사수관 선봉전", "choice_applied": true, "choice_label": "" } var result_text: String = scene._format_battle_result_summary() _check_contains(failures, "reward section", result_text, "전리품") _check_contains(failures, "gold reward", result_text, "군자금 +100냥") _check_contains(failures, "item reward", result_text, "콩 2점") _check_contains(failures, "roster section", result_text, "합류: 하후연, 조인") _check_contains(failures, "growth section", result_text, "조조 품계 2") _check_contains(failures, "campaign section", result_text, "다음 행군: 사수관 선봉전") _check_contains(failures, "next camp section", result_text, "다음 군막") _check_contains(failures, "next camp location", result_text, "사수관, 초평 원년") _check_contains(failures, "next camp talk", result_text, "군막 회의 · 3건") _check_contains(failures, "next camp shop", result_text, "군상 물품 · 6종") _check_contains(failures, "next camp deploy", result_text, "출진 명부 · 2/4") _check_contains(failures, "next camp formation", result_text, "진형 배치 6칸") scene._create_hud() scene._rebuild_result_reward_strip() if scene.result_reward_strip == null or not scene.result_reward_strip.visible: failures.append("victory result should show a compact reward icon strip") elif scene.result_reward_strip.get_child_count() < 4: failures.append("reward icon strip should include gold, item, and two opening officer chips") var reward_tooltips := _collect_tooltips(scene.result_reward_strip) _check_contains(failures, "reward strip gold tooltip", reward_tooltips, "군자금 +100냥") _check_contains(failures, "reward strip item tooltip", reward_tooltips, "물자: 콩 2점") _check_contains(failures, "reward strip Xiahou Yuan tooltip", reward_tooltips, "합류: 하후연") _check_contains(failures, "reward strip Cao Ren tooltip", reward_tooltips, "합류: 조인") _check_contains(failures, "reward strip archer class tooltip", reward_tooltips, "병과: 궁병") _check_contains(failures, "reward strip infantry class tooltip", reward_tooltips, "병과: 보병") var gold_chip := _find_named_node(scene.result_reward_strip, "ResultRewardGoldChip") if gold_chip == null: failures.append("reward icon strip should show gold as a generated image chip") elif not _has_named_texture_rect(gold_chip, "ResultRewardGoldIcon"): failures.append("gold reward chip should use the generated gold badge art") if _count_named_nodes(scene.result_reward_strip, "ResultRewardClassBadge") < 2: failures.append("joined officer reward chips should show compact class crests") if not _has_texture_rect(scene.result_reward_strip): failures.append("reward icon strip should reuse item or officer artwork") scene.battle_result_summary = { "saved": false, "rewards_applied": false, "already_completed": false, "pending_choice": false, "gold": 0, "items": [], "joined_officers": [], "left_officers": [], "progression_events": [], "next_scenario_title": "사수관 선봉전", "choice_applied": true, "choice_label": "" } var failed_save_text: String = scene._format_battle_result_summary() _check_contains(failures, "failed rewards", failed_save_text, "아직 기록되지 않음") _check_contains(failures, "failed save", failed_save_text, "전기 봉인 실패") scene.battle_result_summary = { "saved": true, "pending_choice": true, "next_scenario_id": "002_sishui_gate", "next_scenario_title": "청주 평정전" } var pending_choice_text := scene._format_battle_result_summary() _check_contains( failures, "pending choice prompt", pending_choice_text, "응답을 고르십시오" ) _check_not_contains( failures, "pending choice should wait for response before next camp preview", pending_choice_text, "다음 군막" ) scene.battle_result_summary = { "saved": true, "already_completed": true, "next_scenario_title": "Current Battle" } _check_contains( failures, "replay rewards", scene._format_battle_result_summary(), "이미 수령" ) var choice_text: String = scene._format_post_battle_choice_reward_text({ "gold": 50, "items": ["war_axe"], "join_officers": ["dian_wei"] }) _check_contains(failures, "choice gold", choice_text, "+50냥") _check_contains(failures, "choice item", choice_text, "전부") _check_contains(failures, "choice join", choice_text, "합류 전위") if failures.is_empty(): scene.free() print("result summary smoke ok") quit(0) return scene.free() for failure in failures: push_error(failure) quit(1) func _check_contains(failures: Array[String], label: String, text: String, expected: String) -> void: if text.contains(expected): return failures.append("%s expected `%s` in `%s`" % [label, expected, text]) func _check_not_contains(failures: Array[String], label: String, text: String, unexpected: String) -> void: if not text.contains(unexpected): return failures.append("%s did not expect `%s` in `%s`" % [label, unexpected, text]) func _collect_tooltips(root: Node) -> String: var parts := [] if root is Control: var tooltip := (root as Control).tooltip_text if not tooltip.is_empty(): parts.append(tooltip) for child in root.get_children(): parts.append(_collect_tooltips(child)) return "\n".join(parts) func _has_texture_rect(root: Node) -> bool: if root is TextureRect and (root as TextureRect).texture != null: return true for child in root.get_children(): if _has_texture_rect(child): return true return false func _has_named_texture_rect(root: Node, node_name: String) -> bool: if root.name == node_name and root is TextureRect and (root as TextureRect).texture != null: return true for child in root.get_children(): if _has_named_texture_rect(child, node_name): return true return false func _find_named_node(root: Node, node_name: String) -> Node: if root.name == node_name: return root for child in root.get_children(): var found := _find_named_node(child, node_name) if found != null: return found return null func _count_named_nodes(root: Node, node_name: String) -> int: var count := 1 if root.name == node_name else 0 for child in root.get_children(): count += _count_named_nodes(child, node_name) return count