Files
aetherbound-guild/runtime/tests/p9_7/three_round_continuation_runtime_test.gd
T

213 lines
9.1 KiB
GDScript

extends SceneTree
const MainScene = preload("res://main.tscn")
const LegacyService = preload("res://p9_3/core_loop_service.gd")
const LegacyState = preload("res://p9_3/core_loop_state.gd")
const P97SaveService = preload("res://p9_7/save_service.gd")
const VIEWPORT_SIZE := Vector2i(844, 390)
const ROUND_STEPS := [
{"market": 5, "recruit": "P97-M5-R2", "route": "P97-R5-Q0"},
{"market": 6, "recruit": "P97-M6-R2", "route": "P97-R6-Q0"},
{"market": 7, "recruit": "P97-M7-R3", "route": "P97-R7-Q0"},
]
var failures: Array[String] = []
var parsed_touches := 0
var capture_root := ""
var game
var save_service = P97SaveService.new()
func _initialize() -> void:
call_deferred("run_test")
func run_test() -> void:
capture_root = OS.get_environment("ABG_P9_7_CONTINUATION_CAPTURE_ROOT")
expect(save_service.clear().ok, "test-owned v2 save state clears before the opening handoff")
root.content_scale_mode = Window.CONTENT_SCALE_MODE_CANVAS_ITEMS
root.content_scale_aspect = Window.CONTENT_SCALE_ASPECT_EXPAND
root.content_scale_size = VIEWPORT_SIZE
root.size = VIEWPORT_SIZE
game = MainScene.instantiate()
game.auto_boot = true
game.p93_experience_enabled = true
root.add_child(game)
await settle_frames(5)
expect(game.p93_experience != null, "main starts the accepted P9.3-P9.6 controller when no v2 save exists")
if game.p93_experience == null:
await finish()
return
game.p93_experience.persistence_enabled = false
game.p93_experience.service = complete_p96()
game.p93_experience.returned_from_battle = true
game.p93_experience._render_current()
await settle_frames()
expect(find_control(game, "P96FinalHeadline") != null and find_control(game, "P97EnterMarketButton") != null, "settled Rift Tide Hall exposes one Windglass continuation")
await capture("01-p96-windglass-handoff")
await tap_named("P97EnterMarketButton")
await settle_frames(4)
var experience = game.p97_experience
expect(experience != null and game.p93_experience == null, "handoff replaces the old controller with P9.7")
if experience == null:
await finish()
return
experience.battle_auto_advance = false
expect(int(experience.market.state.current_market) == 5 and int(experience.market.state.cleared_rounds) == 4, "handoff preserves the exact settled P9.6 state")
await capture("02-market5-entry")
for step in ROUND_STEPS:
await play_round(experience, step)
expect(int(experience.market.state.current_market) == 8 and int(experience.market.state.cleared_rounds) == 7, "three rounds reach Market 8")
expect(experience.market.state.relay_restored and experience.market.state.continuation_locked, "Round 7 restores the Relay and opens the explicit threshold")
expect(experience.market.state.route_history == ["P97-R5-Q0", "P97-R6-Q0", "P97-R7-Q0"], "all three committed routes remain ordered")
expect(experience.market.state.battle_settlement_receipts.size() == 3, "three battle settlements commit exactly once")
expect(experience.market.state.company.size() == 6 and experience.market.state.deployed_recruit_ids.size() == 6 and experience.market.state.line_capacity == 7, "Company, Line and post-Round-6 capacity remain distinct")
await capture("09-relay-restored")
var loaded: Dictionary = save_service.load_snapshot()
expect(loaded.ok and int(loaded.source_version) == 2 and loaded.snapshot == experience.market.state.snapshot(), "Market 8 save reloads without reroll or offline progress")
var saved_snapshot: Dictionary = experience.market.state.snapshot()
game.queue_free()
await settle_frames(4)
game = MainScene.instantiate()
game.auto_boot = true
game.p93_experience_enabled = true
root.add_child(game)
await settle_frames(6)
expect(game.p97_experience != null and game.p93_experience == null, "main routes a valid v2 save directly to P9.7")
if game.p97_experience != null:
expect(game.p97_experience.market.state.snapshot() == saved_snapshot, "reopened Market 8 preserves Company, HP, Coin, gear and route history")
expect(find_control(game, "P97RelayTitle") != null, "reopened Market 8 renders the restored Relay")
await capture("10-market8-reloaded")
await finish()
func play_round(experience, step: Dictionary) -> void:
var market_index := int(step.market)
var recruit_id := String(step.recruit)
var route_id := String(step.route)
expect(int(experience.market.state.current_market) == market_index, "Market %d opens before its preparation" % market_index)
await tap_named("P97RecruitTarget_%s" % recruit_id)
await tap_named("P97RecruitTarget_%s" % recruit_id)
expect(recruit_id in experience.market.state.reserve_recruit_ids, "Market %d hire stages %s in Reserve" % [market_index, recruit_id])
if market_index == 5:
await tap_named("P97EquipmentTarget_P97-M5-EQ-021")
await tap_named("P97EquipmentTarget_P97-M5-EQ-021")
await tap_named("P97CompanyTarget_%s" % recruit_id)
expect(experience.market.state.equipped_by_recruit[recruit_id] == "P97-M5-EQ-021-ITEM", "Market 5 equips the purchased Threatglass to the new Recruit")
await tap_named("P97CompanyTarget_%s" % recruit_id)
await tap_named("P97CompanyTarget_%s" % recruit_id)
await tap_named("P97CommitLineButton")
expect(recruit_id in experience.market.state.deployed_recruit_ids and recruit_id not in experience.market.state.reserve_recruit_ids, "Market %d commits %s to the Line" % [market_index, recruit_id])
await capture("%02d-market%d-prepared" % [market_index - 2, market_index])
await tap_named("P97RouteTarget_%s" % route_id)
await tap_named("P97RouteTarget_%s" % route_id)
expect(experience.battle_active and experience.battle.state.route_id == route_id, "Market %d starts the chosen route" % market_index)
var guard := 0
while not experience.battle.state.terminal and guard < 2200:
experience.advance_battle_ticks(1)
if guard % 8 == 0:
await process_frame
guard += 1
await settle_frames()
expect(guard < 2200 and experience.battle.state.outcome == "victory", "Round %d reaches deterministic victory" % market_index)
await capture("%02d-round%d-result" % [market_index - 1, market_index])
await tap_named("P97SettleButton")
expect(experience.market.state.battle_settlement_receipts.size() == market_index - 4, "Round %d settlement commits once" % market_index)
if experience.market.state.recovery_required:
expect(find_control(game, "P97RecoveryButton") != null, "Round %d exposes recovery for fallen members" % market_index)
await tap_named("P97RecoveryButton")
expect(not experience.market.state.recovery_required, "Round %d recovery restores the next decision state" % market_index)
await settle_frames()
func tap_named(node_name: String) -> void:
var control := find_control(game, node_name)
expect(control != null, "%s exists before parsed touch" % node_name)
if control == null:
return
var point := root.get_final_transform() * control.get_global_rect().get_center()
var press := InputEventScreenTouch.new()
press.index = 0
press.position = point
press.pressed = true
Input.parse_input_event(press)
parsed_touches += 1
await process_frame
var release := InputEventScreenTouch.new()
release.index = 0
release.position = point
release.pressed = false
Input.parse_input_event(release)
await settle_frames()
func find_control(root_node: Node, node_name: String) -> Control:
return root_node.find_child(node_name, true, false) as Control
func capture(name: String) -> void:
if capture_root.is_empty():
return
DirAccess.make_dir_recursive_absolute(capture_root)
await settle_frames()
await RenderingServer.frame_post_draw
var error := root.get_texture().get_image().save_png(capture_root.path_join(name + ".png"))
expect(error == OK, "%s capture writes" % name)
await process_frame
func complete_p96():
var service = LegacyService.new()
service.enter_hall()
service.focus_offer("Farstring")
service.hire("Farstring")
complete_legacy_battle(service, "open_road")
service.focus_offer("Kindhand")
service.hire("Kindhand")
complete_legacy_battle(service, "crosswind")
service.focus_equipment("EQ-002")
service.equip_focused("Hearthguard")
complete_legacy_battle(service, "bell_breach")
service.choose_rift_tide_plan("press_on")
complete_legacy_battle(service, "rift_tide")
return service
func complete_legacy_battle(service, encounter_id: String) -> void:
service.select_encounter(encounter_id)
service.start_battle()
var guard := 0
while service.state.phase == LegacyState.Phase.BATTLE and guard < 4000:
service.advance_battle_ticks(1)
guard += 1
service.claim_and_return()
func settle_frames(count := 2) -> void:
for _index in range(count):
await process_frame
func finish() -> void:
if game != null and is_instance_valid(game):
game.queue_free()
game = null
expect(save_service.clear().ok, "test-owned v2 save state clears after the reload proof")
await settle_frames(120)
if failures.is_empty():
print("ABG_P9_7_THREE_ROUND_CONTINUATION_OK touch=%d fallback=0 direct=0 handoff=p9.6_to_market5 rounds=5,6,7 routes=steady,steady,steady settlements=3 company=6 line=6/7 relay=true market8=reload_exact v2_resume=direct" % parsed_touches)
quit(0)
else:
for failure in failures:
push_error(failure)
quit(1)
func expect(condition: bool, message: String) -> void:
if not condition:
failures.append("P9.7 three-round continuation: %s" % message)