extends SceneTree const BattleService = preload("res://p10_3/deep_channels_battle_service.gd") const Controller = preload("res://p10_3/runtime_controller.gd") const Fixture = preload("res://tests/p10_3/deep_channels_fixture.gd") const Main = preload("res://main.gd") const P102Controller = preload("res://p10_2/runtime_controller.gd") const P102Save = preload("res://p10_2/save_v8_service.gd") const SaveV9 = preload("res://p10_3/save_v9_service.gd") const VIEWPORT := Vector2i(844, 390) var experience var failures: Array[String] = [] func _initialize() -> void: call_deferred("run_test") func run_test() -> void: root.size = VIEWPORT root.content_scale_mode = Window.CONTENT_SCALE_MODE_CANVAS_ITEMS root.content_scale_aspect = Window.CONTENT_SCALE_ASPECT_EXPAND root.content_scale_size = VIEWPORT experience = Controller.new() experience.persistence_enabled = false experience.battle_auto_advance = false experience.locale = "zh_CN" experience.size = Vector2(VIEWPORT) experience.configure_entry(Fixture.new().migrated_snapshot()) root.add_child(experience) await settle_frames(12) expect(experience.p103_mode, "Save v9 activates the P10.3 runtime") expect(experience.screen == Controller.P103_SCREEN_MARKET, "Market 5 resumes as the first deep-channel screen") expect(experience.find_child("P103DeepFerry", true, false) != null, "Market 5 renders the deep-channel ferry workspace") expect(experience.find_children("P102RouteTarget_*", "Button", true, false).size() == 3, "Market 5 exposes three route posts") var starting_order: Array = Array(experience.region_market.state.current_run.deployed_recruit_ids).duplicate() if starting_order.size() >= 2: experience._commit_line_drag(String(starting_order[-1]), String(starting_order[0])) await settle_frames() expect(experience.region_market.state.current_run.deployed_recruit_ids != starting_order, "deployment drag commits an atomic new order") await clear_round(5, 0, "channel_thread_adjacent", "P103ChannelThread") expect(experience.screen == Controller.P103_SCREEN_MARKET, "Round 5 settlement opens Market 6") await clear_round(6, 1, "safe_index_advanced", "P103SafeIndexPulse") expect(experience.screen == Controller.P103_SCREEN_ARTIFACT, "Round 6 settlement opens the Artifact shrine") expect(experience.find_children("P103ArtifactTarget_*", "Button", true, false).size() == 3, "Artifact shrine exposes three opposed laws") expect(experience.find_child("P103ArtifactShrine", true, false) != null, "Artifact choice remains inside the ferry world") experience.choose_p103_artifact("AR-012") await settle_frames() expect(experience.screen == Controller.P103_SCREEN_PROMOTION, "Artifact choice opens the eligible promotion anvil") expect(experience.find_children("P103PromotionTarget_*", "Button", true, false).size() == 2, "promotion anvil exposes two deterministic branches") expect(experience.find_child("P103PromotionAnvil", true, false) != null, "promotion choice remains inside the ferry world") experience.choose_p103_promotion("PF-A13") await settle_frames() expect(experience.screen == Controller.P103_SCREEN_MARKET, "promotion returns to playable Market 7") await clear_round(7, 2, "long_company_opening", "P103ArtifactPulse") var run: Dictionary = experience.region_market.state.current_run expect(experience.screen == Controller.P103_SCREEN_MARKET_8, "Round 7 settlement reaches visible Market 8") expect(run.phase == "market_8" and run.market_index == 8 and run.cleared_rounds == 7, "Market 8 is the exact continuation boundary") expect(run.active_artifact_ids == ["AR-012"], "Market 8 visibly preserves Long Company") expect(_company_has_profession(run, "PF-A13"), "Market 8 visibly preserves Fieldmender") expect(experience.find_child("P103DeepFerry", true, false) != null, "Market 8 preserves the restored ferry") var encoded: Dictionary = SaveV9.new().encode_snapshot(experience.region_market.state.snapshot(), 50) var decoded: Dictionary = SaveV9.new().decode_text(String(encoded.get("text", "")), 50) expect(encoded.ok and decoded.ok and decoded.snapshot == experience.region_market.state.snapshot(), "visible Market 8 roundtrips through Save v9") var market_eight_snapshot: Dictionary = experience.region_market.state.snapshot() experience.queue_free() await settle_frames(18) var game = Main.new() game.auto_boot = false game.p100_persistence_enabled = false root.add_child(game) await settle_frames() game.p102_experience = P102Controller.new() game.p102_experience.persistence_enabled = false game.p102_experience.size = Vector2(VIEWPORT) game.p102_experience.configure_entry(Fixture.new().p102_market_5_snapshot()) game.add_child(game.p102_experience) await settle_frames(12) expect(is_instance_valid(game.p103_handoff_button), "main exposes one Continue Deep Channels command on stable P10.2 Market 5") if is_instance_valid(game.p103_handoff_button): game.p103_handoff_button.emit_signal(&"pressed") await settle_frames(12) expect(is_instance_valid(game.p103_experience), "main hands stable P10.2 Market 5 to the P10.3 runtime") if is_instance_valid(game.p103_experience): expect(game.p103_experience.p103_mode and int(game.p103_experience.region_market.state.current_run.market_index) == 5, "main handoff migrates into exact P10.3 Market 5") game.queue_free() await settle_frames(18) _cleanup_save_files() var p102_path := ProjectSettings.globalize_path("user://aetherbound_guild_p10_2.json") var p103_path := ProjectSettings.globalize_path("user://aetherbound_guild_p10_3.json") var wrote_v8 := P102Save.new().write_slot(p102_path, Fixture.new().p102_market_5_snapshot(), 60, "") var wrote_v9 := SaveV9.new().write_slot(p103_path, market_eight_snapshot, 61, "") expect(wrote_v8.ok and wrote_v9.ok, "resume priority fixture writes valid Save v8 and Save v9 slots") var resumed = Main.new() resumed.auto_boot = true resumed.p100_experience_enabled = true resumed.p100_persistence_enabled = true root.add_child(resumed) await settle_frames(18) expect(is_instance_valid(resumed.p103_experience), "main prioritizes Save v9 when Save v8 also exists") expect(not is_instance_valid(resumed.p102_experience), "Save v9 priority does not mount the older P10.2 runtime") if is_instance_valid(resumed.p103_experience): expect(resumed.p103_experience.screen == Controller.P103_SCREEN_MARKET_8, "Save v9 priority restores exact Market 8") resumed.queue_free() await settle_frames(45) _cleanup_save_files() if failures.is_empty(): print("ABG_P10_3_CONTINUOUS_RUNTIME_OK markets=5,6,7,8 rounds=5,6,7 artifact=AR-012 promotion=PF-A13 order=drag widow=event_bound sluice=event_bound artifact_effect=event_bound ferry=world_first save_v9=stable main=handoff resume=v9_priority") quit(0) else: for failure in failures: push_error(failure) quit(1) func clear_round(market_index: int, risk: int, event_kind: String, effect_node: String) -> void: expect(int(experience.region_market.state.current_run.market_index) == market_index, "Round %d starts at its matching Market" % market_index) var route: Dictionary = experience.region_market.state.current_run.market_board.battle_offers[risk] var plan_id := String(BattleService.PREFERRED_PLAN[String(route.route_id)]) experience.tap_region_route(String(route.offer_id)) experience.commit_region_route() await settle_frames() expect(experience.screen == Controller.P103_SCREEN_PLAN, "Round %d opens the four-plan workspace" % market_index) expect(experience.find_children("P102PlanTarget_*", "Button", true, false).size() == 4, "Round %d presents four direct plan targets" % market_index) experience.choose_region_plan(plan_id) experience.advance_region_battle_ticks(999) await settle_frames() expect(experience.screen == Controller.P103_SCREEN_BATTLE and experience.region_battle_complete, "Round %d reaches its visible result" % market_index) expect(int(experience.region_event_counts.get(event_kind, 0)) == 1, "Round %d consumes %s exactly once" % [market_index, event_kind]) expect(experience.find_child(effect_node, true, false) != null, "Round %d binds %s to a world effect" % [market_index, event_kind]) experience.settle_region_battle() await settle_frames() func _company_has_profession(run: Dictionary, profession_id: String) -> bool: for recruit_id in run.company: if String(run.recruit_records[recruit_id].profession_id) == profession_id: return true return false func _cleanup_save_files() -> void: for save_name in ["user://aetherbound_guild_p10_2.json", "user://aetherbound_guild_p10_3.json"]: for suffix in ["", ".bak", ".candidate"]: var path := ProjectSettings.globalize_path(save_name + suffix) if FileAccess.file_exists(path): DirAccess.remove_absolute(path) func settle_frames(count := 5) -> void: for _index in count: await process_frame func expect(condition: bool, message: String) -> void: if not condition: failures.append("P10.3 continuous runtime: %s" % message)