Initial 100, capacity 300, world tree +20 per 10 game minutes. Each boundary pays registered upkeep demand before storing surplus; overflow is discarded and recorded, 0 stops the whole tower. Furnace offers are judged after generation and demand payment. New saves carry mana_reserve and Operations rollback includes it; legacy saves keep a 0..300 amount and reject others. Outage tests that relied on Mana 0 persisting now hold the outage with a disabled demand sink, since the world tree recovers at the next boundary. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
116 lines
7.1 KiB
GDScript
116 lines
7.1 KiB
GDScript
extends SceneTree
|
|
const ManaOutage = preload("res://tests/helpers/mana_outage.gd")
|
|
var checks := 0
|
|
var failures := 0
|
|
var runtime: Variant
|
|
var clock: Variant
|
|
var state: Variant
|
|
func expect(ok: bool, message: String) -> void:
|
|
checks += 1
|
|
if not ok: failures += 1; push_error(message)
|
|
func _initialize() -> void: call_deferred("run")
|
|
func build(id: StringName, origin: Vector3i) -> bool:
|
|
var result: CommandResult = runtime.submit_command(&"build_facility", {&"definition_id":id,&"origin":origin})
|
|
expect(result.is_success(), "normal build %s: %s" % [id,result.message])
|
|
if not result.is_success(): return false
|
|
clock.advance_minutes(runtime.definition_registry.get_facility(id).build_minutes)
|
|
return true
|
|
func run() -> void:
|
|
runtime=load("res://autoload/simulation_runtime.gd").new()
|
|
clock=load("res://autoload/game_clock.gd").new();state=load("res://autoload/game_state.gd").new()
|
|
clock.reset(1,6,0);state.reset();runtime.initialize(clock,state,42502);clock.set_paused(true)
|
|
if not build(&"reception",Vector3i(-6,0,0)) or not build(&"premium_room",Vector3i(-6,2,0)) or not build(&"guest_stairs",Vector3i(-7,1,0)) or not build(&"guest_stairs",Vector3i(-7,2,0)):
|
|
finish();return
|
|
var room_id: StringName = &""
|
|
for room: FacilityInstance in runtime.facility_service.get_instances():
|
|
if room.definition_id==&"premium_room": room_id=room.instance_id
|
|
expect(runtime.get_facility_access_report(room_id)[&"reachable"], "normal stairs make premium room reachable from entrance")
|
|
var spawn: CommandResult = runtime.submit_command(&"spawn_guest",{&"guest_id":&"premium_walker",&"guest_type_id":&"traveler",&"gold":1000,
|
|
&"options":{&"current_cell":Vector3i(-7,0,0),&"state":GuestRuntime.State.DECIDING,&"planned_exit_at":clock.absolute_minute+2000,
|
|
&"needs":{&"sleep":100,&"hunger":0,&"hygiene":0,&"medical":0,&"leisure":0,&"intimacy":0}}})
|
|
expect(spawn.is_success(), "guest enters deciding state without forcing target or route")
|
|
var used_stairs := false
|
|
var selected_room := false
|
|
var service_id: StringName = &""
|
|
var moving_save := {}
|
|
for minute in 40:
|
|
clock.advance_one_minute()
|
|
var guest: GuestRuntime = runtime.guest_service.get_guest(&"premium_walker")
|
|
if guest==null: break
|
|
selected_room = selected_room or guest.target_facility_id==room_id
|
|
if guest.current_cell==Vector3i(-7,1,0):
|
|
used_stairs=true
|
|
if moving_save.is_empty(): moving_save=runtime.create_snapshot()
|
|
if not guest.current_service_id.is_empty():
|
|
service_id=guest.current_service_id;break
|
|
expect(selected_room, "highest sleep need selects premium room autonomously")
|
|
expect(used_stairs, "guest physically traverses first staircase landing")
|
|
expect(not service_id.is_empty(), "guest arrival automatically queues and starts lodging")
|
|
if service_id.is_empty():
|
|
var guest: GuestRuntime=runtime.guest_service.get_guest(&"premium_walker")
|
|
print("Guest diagnostic: ", guest.create_snapshot() if guest!=null else {})
|
|
finish();return
|
|
var started: Dictionary=runtime.create_snapshot()
|
|
expect(not moving_save.is_empty() and runtime.restore_snapshot(moving_save), "actual stair movement save restores")
|
|
for minute in 40:
|
|
clock.advance_one_minute()
|
|
if not runtime.guest_service.get_guest(&"premium_walker").current_service_id.is_empty(): break
|
|
expect(runtime.create_snapshot()==started, "stair-save replay reproduces arrival and queued service exactly")
|
|
clock.advance_minutes(360)
|
|
var mid: Dictionary=runtime.create_snapshot()
|
|
expect(runtime.restore_snapshot(mid), "real guest and in-progress premium service restore together")
|
|
var elapsed: int=runtime.facility_service.get_service(service_id).elapsed_minutes
|
|
ManaOutage.hold(runtime, state)
|
|
clock.advance_minutes(15)
|
|
var paused: ServiceInstance=runtime.facility_service.get_service(service_id)
|
|
expect(paused.state==ServiceInstance.State.PAUSED and paused.elapsed_minutes==elapsed, "zero mana pauses actual guest lodging without consuming service time")
|
|
expect(runtime.economy_service.get_balance(&"GUEST_WALLET:premium_walker")==1000, "outage does not charge real lodging guest")
|
|
var disk_path := "user://saves/premium_guest_outage_%d" % Time.get_ticks_usec()
|
|
var outage: Dictionary=runtime.create_snapshot()
|
|
expect(SaveService.save_snapshot(disk_path,outage)[&"ok"], "write real guest outage snapshot to disposable save")
|
|
var saved := SaveService.load_snapshot(disk_path)
|
|
expect(saved[&"ok"] and runtime.restore_snapshot(saved[&"snapshot"]), "restore real guest, paused service and empty mana from disk")
|
|
SaveService.delete_save(disk_path)
|
|
ManaOutage.release(runtime, state)
|
|
|
|
for minute in 370:
|
|
clock.advance_one_minute()
|
|
if runtime.facility_service.get_service(service_id).state==ServiceInstance.State.COMPLETED: break
|
|
expect(runtime.facility_service.get_service(service_id).state==ServiceInstance.State.COMPLETED, "autonomous premium stay completes after restored continuation")
|
|
expect(runtime.economy_service.get_balance(&"GUEST_WALLET:premium_walker")==780, "real guest pays one premium lodging charge")
|
|
expect(runtime.restore_snapshot(runtime.create_snapshot()), "completed guest/service/path/economy crosslinks remain saveable")
|
|
clock.advance_minutes(maxi(1440-clock.absolute_minute,0))
|
|
expect(runtime.economy_service.validate_ledger()[&"ok"] and runtime.restore_snapshot(runtime.create_snapshot()), "autonomous lodging and upkeep survive midnight ledger close and restore")
|
|
capacity_flow()
|
|
finish()
|
|
func finish() -> void:
|
|
runtime.free();clock.free();state.free()
|
|
print("Premium guest flow: %d checks, %d failures" % [checks,failures]);quit(failures)
|
|
|
|
func capacity_flow() -> void:
|
|
clock.reset(1,6,0);state.reset();runtime.initialize(clock,state,42503);clock.set_paused(true)
|
|
if not build(&"reception",Vector3i(-6,0,0)) or not build(&"premium_room",Vector3i(-6,2,0)) or not build(&"guest_stairs",Vector3i(-7,1,0)) or not build(&"guest_stairs",Vector3i(-7,2,0)): return
|
|
var room_id: StringName=&""
|
|
for room: FacilityInstance in runtime.facility_service.get_instances():
|
|
if room.definition_id==&"premium_room": room_id=room.instance_id
|
|
for index in 3:
|
|
var id:=StringName("premium_capacity_%d" % index)
|
|
expect(runtime.submit_command(&"spawn_guest",{&"guest_id":id,&"guest_type_id":&"traveler",&"gold":1000,
|
|
&"options":{&"current_cell":Vector3i(-7-index,0,0),&"state":GuestRuntime.State.DECIDING,&"planned_exit_at":clock.absolute_minute+2000,
|
|
&"needs":{&"sleep":100,&"hunger":0,&"hygiene":0,&"medical":0,&"leisure":0,&"intimacy":0}}}).is_success(), "spawn autonomous capacity guest")
|
|
var maximum := 0
|
|
var saw_queue := false
|
|
var exceeded := false
|
|
for minute in 60:
|
|
clock.advance_one_minute()
|
|
var using: int=runtime.facility_service.get_active_services(room_id).size()
|
|
maximum=maxi(maximum,using)
|
|
exceeded=exceeded or using>2
|
|
if using==2:
|
|
for guest: GuestRuntime in runtime.guest_service.get_guests():
|
|
if guest.target_facility_id==room_id and guest.state==GuestRuntime.State.QUEUING: saw_queue=true
|
|
expect(maximum==2 and not exceeded, "three autonomous guests never exceed two simultaneous lodging users")
|
|
expect(saw_queue, "third guest waits separately from the two using the room")
|
|
var crowded: Dictionary=runtime.create_snapshot()
|
|
expect(runtime.restore_snapshot(crowded) and runtime.create_snapshot()==crowded, "occupied room and separate queue restore exactly")
|