Files
heros/tools/smoke_objective_progress.gd

279 lines
11 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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_contains(
failures,
"001 HUD progress",
_progress_text("res://data/scenarios/001_yellow_turbans.json", false),
"적군 격파 0/"
)
_check_contains(
failures,
"001 commander defeat risk",
_defeat_text("res://data/scenarios/001_yellow_turbans.json"),
"조조 건재"
)
_check_contains(
failures,
"001 turn pressure risk",
_defeat_text("res://data/scenarios/001_yellow_turbans.json"),
"잔여 군령 15"
)
_check_contains(
failures,
"002 gated victory progress",
_progress_text("res://data/scenarios/002_sishui_gate.json", false),
"제2군령에 전공 갱신"
)
_check_contains(
failures,
"002 pending enemy progress",
_progress_text("res://data/scenarios/002_sishui_gate.json", false),
"적군 격파 0/"
)
_check_contains(
failures,
"007 destination progress",
_progress_text("res://data/scenarios/007_xian_emperor_escort.json", false),
"전장 표식 (14, 5) · 미도달"
)
_check_contains(
failures,
"007 protected-unit risk progress",
_defeat_text("res://data/scenarios/007_xian_emperor_escort.json"),
"천자 사자 건재"
)
_check_contains(
failures,
"045 gated destination progress",
_progress_text("res://data/scenarios/045_fancheng_relief.json", false),
"전장 표식 (10, 5) · 미도달"
)
_check_event_gated_objective_markers(failures)
_check_event_gated_multi_cell_objective_markers(failures)
_check_turn_gated_objective_does_not_create_marker(failures)
_check_opening_battle_requires_castle_capture(failures)
_check_objective_update(failures)
_check_objective_notice(failures)
_check_terrain_recovery(failures)
if failures.is_empty():
print("objective progress smoke ok")
quit(0)
return
for failure in failures:
push_error(failure)
quit(1)
func _progress_text(path: String, include_defeat_risks: bool) -> String:
var state = BattleStateScript.new()
if not state.load_battle(path):
return "<load failed: %s>" % path
return state.get_objective_progress_text(include_defeat_risks)
func _defeat_text(path: String) -> String:
var state = BattleStateScript.new()
if not state.load_battle(path):
return "<load failed: %s>" % path
return state.get_defeat_progress_text()
func _check_objective_update(failures: Array[String]) -> void:
var state = BattleStateScript.new()
if not state.load_battle("res://data/scenarios/001_yellow_turbans.json"):
failures.append("objective update smoke could not load scenario")
return
var signal_values := []
var logs := []
state.objective_updated.connect(func(victory: String, defeat: String) -> void:
signal_values.append({"victory": victory, "defeat": defeat})
)
state.log_added.connect(func(message: String) -> void:
logs.append(message)
)
state._apply_objective_event({
"victory": "Defeat the new vanguard.",
"defeat": "Cao Cao is defeated."
})
if signal_values.size() != 1:
failures.append("objective update signal expected 1 emission, got %d" % signal_values.size())
else:
var signal_value: Dictionary = signal_values[0]
if str(signal_value.get("victory", "")) != "Defeat the new vanguard.":
failures.append("objective update signal victory mismatch: %s" % str(signal_value.get("victory", "")))
if str(signal_value.get("defeat", "")) != "Cao Cao is defeated.":
failures.append("objective update signal defeat mismatch: %s" % str(signal_value.get("defeat", "")))
_check_log_contains(failures, logs, "군령 갱신: Defeat the new vanguard.")
_check_log_contains(failures, logs, "패배 조건 갱신: Cao Cao is defeated.")
signal_values.clear()
logs.clear()
state._apply_objective_event({"defeat": "Supply train is lost."})
if signal_values.size() != 1:
failures.append("defeat-only objective signal expected 1 emission, got %d" % signal_values.size())
else:
var defeat_signal_value: Dictionary = signal_values[0]
if not str(defeat_signal_value.get("victory", "")).is_empty():
failures.append("defeat-only objective signal should not include victory: %s" % str(defeat_signal_value.get("victory", "")))
if str(defeat_signal_value.get("defeat", "")) != "Supply train is lost.":
failures.append("defeat-only objective signal mismatch: %s" % str(defeat_signal_value.get("defeat", "")))
_check_log_contains(failures, logs, "패배 조건 갱신: Supply train is lost.")
func _check_event_gated_objective_markers(failures: Array[String]) -> void:
var state = BattleStateScript.new()
if not state.load_battle("res://data/scenarios/013_wuchao_raid.json"):
failures.append("event-gated objective marker smoke could not load Wuchao raid")
return
var depot_cell := Vector2i(14, 4)
var initial_cells := state.get_objective_cells()
if not initial_cells.has(depot_cell):
failures.append("Wuchao depot marker missing before event opens victory condition: %s" % str(initial_cells))
var cao_cao: Dictionary = state.get_unit("cao_cao_ch13")
if cao_cao.is_empty():
failures.append("event-gated objective marker smoke missing Cao Cao")
return
cao_cao["pos"] = depot_cell
state._run_events("unit_reaches_tile", "player", 1, cao_cao)
var opened_cells := state.get_objective_cells()
if opened_cells.has(depot_cell):
failures.append("Wuchao depot marker should clear after event opens defeat-all objective: %s" % str(opened_cells))
func _check_event_gated_multi_cell_objective_markers(failures: Array[String]) -> void:
var state = BattleStateScript.new()
if not state.load_battle("res://data/scenarios/001_yellow_turbans.json"):
failures.append("multi-cell event-gated objective marker smoke could not load scenario")
return
state.battle_conditions["victory"] = {
"type": "all_units_defeated",
"team": "enemy",
"after_event": "multi_cell_gate"
}
state.battle_events.clear()
state.battle_events.append({
"id": "multi_cell_gate",
"once": true,
"when": {"type": "unit_reaches_tile", "team": "player", "cells": [[3, 2], [3, 3]]},
"actions": [{"type": "log", "text": "Gate opened."}]
})
var marker_cells := state.get_objective_cells()
if not marker_cells.has(Vector2i(3, 2)) or not marker_cells.has(Vector2i(3, 3)):
failures.append("multi-cell event-gated marker should include every event cell: %s" % str(marker_cells))
func _check_turn_gated_objective_does_not_create_marker(failures: Array[String]) -> void:
var state = BattleStateScript.new()
if not state.load_battle("res://data/scenarios/001_yellow_turbans.json"):
failures.append("turn-gated objective marker smoke could not load scenario")
return
state.battle_conditions["victory"] = {
"type": "all_units_defeated",
"team": "enemy",
"after_event": "turn_gate"
}
state.battle_events.clear()
state.battle_events.append({
"id": "turn_gate",
"once": true,
"when": {"type": "turn_start", "team": "player", "turn": 2},
"actions": [{"type": "log", "text": "Turn gate opened."}]
})
var marker_cells := state.get_objective_cells()
if not marker_cells.is_empty():
failures.append("turn-gated objective should not create map markers: %s" % str(marker_cells))
func _check_opening_battle_requires_castle_capture(failures: Array[String]) -> void:
var state = BattleStateScript.new()
if not state.load_battle("res://data/scenarios/001_yellow_turbans.json"):
failures.append("opening castle objective smoke could not load scenario")
return
var marker_cells := state.get_objective_cells()
for castle_cell in [Vector2i(12, 0), Vector2i(13, 0), Vector2i(12, 1), Vector2i(13, 1)]:
if not marker_cells.has(castle_cell):
failures.append("opening battle should mark castle capture cell %s: %s" % [str(castle_cell), str(marker_cells)])
for enemy in state.get_living_units(BattleStateScript.TEAM_ENEMY):
enemy["alive"] = false
state._check_battle_status()
if str(state.battle_status) != BattleStateScript.STATUS_ACTIVE:
failures.append("opening battle should not end before castle capture, got %s" % str(state.battle_status))
var cao_cao: Dictionary = state.get_unit("cao_cao")
cao_cao["pos"] = Vector2i(12, 1)
state._check_battle_status()
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))
func _check_log_contains(failures: Array[String], logs: Array, expected: String) -> void:
for log_entry in logs:
if str(log_entry) == expected:
return
failures.append("expected log `%s` in `%s`" % [expected, logs])
func _check_terrain_recovery(failures: Array[String]) -> void:
var state = BattleStateScript.new()
if not state.load_battle("res://data/scenarios/001_yellow_turbans.json"):
failures.append("terrain recovery smoke could not load scenario")
return
var cao_cao: Dictionary = state.get_unit("cao_cao")
cao_cao["pos"] = Vector2i(6, 5)
cao_cao["hp"] = 10
state._reset_team_actions(BattleStateScript.TEAM_PLAYER)
if int(cao_cao.get("hp", 0)) != 16:
failures.append("village terrain should recover 6 hp at phase start, got %d" % int(cao_cao.get("hp", 0)))
var boss: Dictionary = state.get_unit("yellow_turban_1")
boss["hp"] = 20
state._reset_team_actions(BattleStateScript.TEAM_ENEMY)
if int(boss.get("hp", 0)) != 28:
failures.append("castle terrain should recover 8 hp at phase start, got %d" % int(boss.get("hp", 0)))
func _check_objective_notice(failures: Array[String]) -> void:
var scene = BattleSceneScript.new()
scene._create_hud()
scene._on_objective_updated("Defeat the new vanguard.", "")
if scene.objective_notice_panel == null or not scene.objective_notice_panel.visible:
failures.append("objective notice panel should be visible after objective update")
elif not scene.objective_notice_label.text.contains("군령 갱신") 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)
scene._on_objective_updated("", "Supply train is lost.")
if scene.objective_notice_panel == null or not scene.objective_notice_panel.visible:
failures.append("objective notice panel should be visible after defeat update")
elif not scene.objective_notice_label.text.contains("군령 갱신") 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)
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")
if scene._format_log_entry_text("Defeat condition updated: Supply train is lost.") != "패배 조건 갱신: Supply train is lost.":
failures.append("defeat log display should use Korean risk wording")
if scene._format_log_entry_text("군령 갱신: Defeat the new vanguard.") != "군령 갱신: Defeat the new vanguard.":
failures.append("localized objective log should pass through")
if scene._format_log_entry_text("패배 조건 갱신: Supply train is lost.") != "패배 조건 갱신: Supply train is lost.":
failures.append("localized defeat log should pass through")
if scene.objective_notice_timer <= 0.0:
failures.append("objective notice timer should be active")
scene.free()
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])