213 lines
9.1 KiB
GDScript
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)
|