Files
aetherbound-guild/runtime/tests/p9_0r6_preview_capture_test.gd

143 lines
5.1 KiB
GDScript

extends SceneTree
const MainScene = preload("res://main.tscn")
const STATES := ["board_guarded", "board_tempo", "board_full_line", "review_split", "battle_crush", "battle_tempo", "battle_rear", "result_covered", "result_gap"]
const VIEWPORT_SIZE := Vector2i(844, 390)
var output_root := ""
var failures: Array[String] = []
func _initialize() -> void:
call_deferred("run_test")
func run_test() -> void:
output_root = OS.get_environment("ABG_P9_0R6_CAPTURE_ROOT")
if output_root.is_empty():
output_root = "/tmp/abg-p9-0r6-preview"
DirAccess.make_dir_recursive_absolute(output_root)
var viewport := SubViewport.new()
viewport.size = VIEWPORT_SIZE
viewport.render_target_update_mode = SubViewport.UPDATE_ALWAYS
root.add_child(viewport)
var game := MainScene.instantiate()
game.auto_boot = false
game.audio_enabled = false
viewport.add_child(game)
await settle(12)
game.locale = "zh_CN"
game.text_scale = 1.0
game.reduced_motion = false
game.muted = true
for index in range(STATES.size()):
apply_state(game, STATES[index])
game.render()
await settle(7)
verify_interactive_bounds(game, Rect2(Vector2.ZERO, Vector2(VIEWPORT_SIZE)), STATES[index])
await RenderingServer.frame_post_draw
var image := viewport.get_texture().get_image()
var path := output_root.path_join("%02d-%s.png" % [index + 1, STATES[index]])
var error := image.save_png(path)
if error != OK:
failures.append("capture %s failed with %s" % [STATES[index], error])
game.queue_free()
viewport.queue_free()
await settle(120)
if failures.is_empty():
print("ABG_P9_0R6_PREVIEW_CAPTURE_OK frames=9 viewport=844x390 locale=zh_CN min_touch=44 root=%s" % output_root)
call_deferred("finish", 0)
else:
for failure in failures:
push_error(failure)
call_deferred("finish", 1)
func apply_state(game: Control, state_name: String) -> void:
var profile := "guarded"
if state_name in ["board_tempo", "battle_tempo", "result_covered"]:
profile = "tempo"
elif state_name in ["board_full_line", "review_split", "battle_rear"]:
profile = "full_line"
prepare_profile(game, profile)
match state_name:
"board_guarded", "board_tempo", "board_full_line":
game.page = game.AppPage.ROUTE_BOARD
"review_split":
game.route_choice = "R6-03"
game.page = game.AppPage.ROUTE_REVIEW
"battle_crush":
prepare_battle(game, "R6-01", 2, 90, 100, 120)
game.emit_battle_feedback(2, "front", -10, "胫甲抗碎", "brace answered", "medium")
game.battle_feedback_clock = 0.16
"battle_tempo":
prepare_battle(game, "R6-02", 1, 100, 100, 90)
game.emit_battle_feedback(1, "enemy", -30, "晨钟抢速", "tempo counter", "medium")
game.battle_feedback_clock = 0.16
"battle_rear":
prepare_battle(game, "R6-03", 2, 100, 65, 120)
game.emit_battle_feedback(2, "rear", -35, "后排分击", "rear split", "medium")
game.battle_feedback_clock = 0.16
"result_covered":
prepare_result(game, "R6-02", 15, 82, 100, 6.00)
"result_gap":
prepare_profile(game, "guarded")
prepare_result(game, "R6-03", 18, 100, 65, 7.80)
func prepare_profile(game: Control, profile: String) -> void:
game.clear_experience()
game.front_choice = "Hearthguard"
game.rear_choice = "Farstring"
game.adaptation_choice = "EQ-061" if profile == "tempo" else "EQ-002"
game.adaptation_committed = true
game.guild_committed = true
game.guild_choice = "recruit_rear" if profile == "full_line" else "retain_item"
game.retained_item = "" if profile == "full_line" else game.adaptation_choice
game.added_rear = "Kindhand" if profile == "full_line" else ""
game.coin = 6 if profile == "full_line" else 8
func prepare_battle(game: Control, route_id: String, phase: int, front_health: int, rear_health: int, enemy_health: int) -> void:
game.page = game.AppPage.FOURTH_BATTLE
game.route_choice = route_id
game.route_committed = true
game.battle_playing = false
game.battle_phase = phase
game.battle_clock = 0.22
game.front_hp = front_health
game.rear_hp = rear_health
game.enemy_hp = enemy_health
func prepare_result(game: Control, route_id: String, result_coin: int, front_health: int, rear_health: int, result_time: float) -> void:
game.page = game.AppPage.SECOND_CYCLE_RESULT
game.route_choice = route_id
game.route_committed = true
game.route_reward_committed = true
game.coin = result_coin
game.front_hp = front_health
game.rear_hp = rear_health
game.enemy_hp = 0
game.fourth_result_time = result_time
func verify_interactive_bounds(node: Node, bounds: Rect2, label: String) -> void:
if node is BaseButton and node.is_visible_in_tree():
var rect: Rect2 = node.get_global_rect()
if rect.position.x < -0.5 or rect.position.y < -0.5 or rect.end.x > bounds.end.x + 0.5 or rect.end.y > bounds.end.y + 0.5:
failures.append("%s button %s outside viewport: %s" % [label, node.name, rect])
if rect.size.x < 44.0 or rect.size.y < 44.0:
failures.append("%s button %s below 44px: %s" % [label, node.name, rect.size])
for child in node.get_children():
verify_interactive_bounds(child, bounds, label)
func finish(exit_code: int) -> void:
quit(exit_code)
func settle(frames: int) -> void:
for frame in range(frames):
await process_frame