extends SceneTree const FixtureCleanup := preload("res://tests/helpers/ui_audio_fixture_cleanup.gd") var checks:=0 var failures:=0 func expect(ok:bool,label:String)->void: checks+=1 if not ok:failures+=1;push_error(label) func _initialize()->void:call_deferred("run") func cleric_id() -> StringName: return &"staff_uma" func donation_price() -> int: return 20 func recruit_cleric(main: Control, _runtime: Variant) -> void: main.staff_candidate_buttons[cleric_id()].pressed.emit() func verify_effect(_runtime: Variant, _chapel: FacilityInstance) -> void: pass func verify_after_start(_runtime: Variant, _chapel: FacilityInstance) -> void: pass func verify_completion(_runtime: Variant, _chapel: FacilityInstance, _service_id: StringName) -> void: pass func run()->void: var runtime:Variant=root.get_node("SimulationRuntime") var clock:Variant=root.get_node("GameClock") var state:Variant=root.get_node("GameState") clock.reset(1,6,0);state.reset();runtime.initialize(clock,state,42607);clock.set_paused(true) var original:Dictionary=runtime.create_snapshot() var legacy:=original.duplicate(true) legacy[&"systems"][&"runtime"][&"catalog_manifest"][&"manifest_version"]=1 legacy[&"systems"][&"runtime"][&"catalog_manifest"][&"staff_candidate_ids"].erase("staff_uma") expect(runtime.restore_snapshot(legacy),"exact old twenty-candidate manifest loads without losing progress") var malformed:=legacy.duplicate(true) malformed[&"systems"][&"runtime"][&"catalog_manifest"][&"staff_candidate_ids"].erase("staff_akane") var intact:Dictionary=runtime.create_snapshot() expect(not runtime.restore_snapshot(malformed) and runtime.create_snapshot()==intact,"arbitrary incomplete legacy catalog is rejected atomically") malformed=legacy.duplicate(true) malformed[&"systems"][&"runtime"][&"staff"][&"staff"]=[42] expect(not runtime.restore_snapshot(malformed) and runtime.create_snapshot()==intact,"legacy malformed staff rows are rejected without a script type error") var main:Control=load("res://scenes/main/main.tscn").instantiate();root.add_child(main) runtime.configure_save_policy({&"enabled":false,&"daily_checkpoint_enabled":false}) await process_frame;await process_frame expect(main.MVP_FACILITY_IDS.has(&"chapel"),"chapel is exposed in normal construction palette") main.call("_select_build_definition",&"chapel");main.call("_on_map_cell_clicked",Vector3i(-6,0,0));clock.advance_minutes(180) var chapel:FacilityInstance for f:FacilityInstance in runtime.facility_service.get_instances(): if f.definition_id==&"chapel":chapel=f expect(chapel!=null,"normal construction UI creates chapel") if chapel==null: var early_cleanup: Dictionary = await FixtureCleanup.free_owner_and_wait(self, main) if not bool(early_cleanup.get(&"passed", false)): push_error("Chapel ordinary owner cleanup failed: %s" % str(early_cleanup)) quit(96) return quit(failures) return expect(chapel.state==FacilityInstance.State.NO_STAFF,"chapel waits for cleric") recruit_cleric(main, runtime) expect(runtime.staff_service.has_staff(cleric_id()),"normal hire control recruits cleric") var panel:StaffManagementPanel=main.find_child("StaffManagementPanel",true,false) panel.refresh();panel.select_staff(cleric_id()) expect(panel.staff_detail_label.text.contains("聖職者"),"staff UI labels new role") for i in panel.facility_option.item_count: if panel.facility_option.get_item_metadata(i)==chapel.instance_id: panel.facility_option.select(i);panel.facility_option.item_selected.emit(i) panel.apply_button.pressed.emit();clock.advance_minutes(20) expect(chapel.active_staff_ids.has(cleric_id()) and chapel.state==FacilityInstance.State.OPEN,"assigned cleric walks to workplace and opens chapel") verify_effect(runtime, chapel) expect(runtime.submit_command(&"spawn_guest",{&"guest_id":&"worship_walker",&"guest_type_id":&"traveler",&"gold":100,&"options":{&"current_cell":Vector3i(-7,0,0),&"state":GuestRuntime.State.DECIDING,&"planned_exit_at":clock.absolute_minute+300,&"needs":{&"sleep":0,&"hunger":0,&"hygiene":0,&"medical":0,&"leisure":100,&"intimacy":0}}}).is_success(),"controlled arrival without forced destination or service") var service_id:StringName=&"" for minute in 30: clock.advance_one_minute() var g:GuestRuntime=runtime.guest_service.get_guest(&"worship_walker") if g!=null and not g.current_service_id.is_empty():service_id=g.current_service_id;break expect(not service_id.is_empty(),"guest autonomously selects, reaches and begins worship") if not service_id.is_empty(): verify_after_start(runtime, chapel) var midpoint:Dictionary=runtime.create_snapshot() expect(runtime.restore_snapshot(midpoint),"normal cleric, visitor and worship save together") for minute in 61: clock.advance_one_minute() if runtime.facility_service.get_service(service_id).state==ServiceInstance.State.COMPLETED:break var completed := int(runtime.facility_service.get_instance(chapel.instance_id).lifetime_statistics.get(&"services_completed", 0)) expect(runtime.facility_service.get_service(service_id).state==ServiceInstance.State.COMPLETED and completed>0 and state.get_resource(&"faith")==completed and runtime.progression_service.metrics[&"faith_lifetime"]==completed,"each completed worship contributes exactly one Faith, including other autonomous visitors") expect(runtime.economy_service.get_balance(&"GUEST_WALLET:worship_walker")==100-donation_price(),"actual worshipper pays quoted donation exactly once") verify_completion(runtime, chapel, service_id) expect(runtime.restore_snapshot(runtime.create_snapshot()),"completed chapel gameplay remains saveable") main.navigation_buttons[&"business"].pressed.emit() var business:BusinessManagementPanel=main.find_child("BusinessManagementPanel",true,false) business.refresh() for i in business.facility_option.item_count: if business.facility_option.get_item_metadata(i)==chapel.instance_id: business.facility_option.select(i);business.facility_option.item_selected.emit(i) expect(business.quote_label.text.contains("%dG" % donation_price()) and business.effect_label.text.contains("信仰+1"),"business UI explains donation price and per-completion faith") if OS.get_environment("THE_TREE_CHAPEL_CAPTURE")=="1": root.size=Vector2i(1440,900) await process_frame;await process_frame;await RenderingServer.frame_post_draw expect(root.get_texture().get_image().save_png(ProjectSettings.globalize_path(OS.get_environment("THE_TREE_CHAPEL_CAPTURE_PATH") if not OS.get_environment("THE_TREE_CHAPEL_CAPTURE_PATH").is_empty() else "res://artifacts/facility-dimensions-2026-09-29/chapel-gameplay.png"))==OK,"capture native chapel business screen") var cleanup: Dictionary = await FixtureCleanup.free_owner_and_wait(self, main) if not bool(cleanup.get(&"passed", false)): push_error("Chapel ordinary owner cleanup failed: %s" % str(cleanup)) quit(96) return print("Chapel gameplay: %d checks, %d failures"%[checks,failures]);quit(failures)