Files
TheTree_mock/tests/ui/test_chapel_gameplay.gd
T
Takeshi b84bade045 Save The Tree implementation and acceptance work in progress
Preserve production code, regression fixtures, audio assets and provenance, test infrastructure, and the original-completion plan and progress ledger. Final game and Windows acceptance remain pending; excluded local artifacts and native experiments are not part of this snapshot.
2026-10-11 15:58:00 +09:00

98 lines
6.8 KiB
GDScript

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)