extends SceneTree const Presentation := preload("res://scripts/ui/room_presentation.gd") var failures := 0 var checks := 0 class Store extends RefCounted: var rooms: Array = [] var requests: Dictionary = {} func get_instances() -> Array: return rooms func get_instance(id: StringName) -> Variant: for room: Variant in rooms: if room.instance_id == id: return room return null func get_request(id: StringName) -> Variant: return requests.get(id) class Guests extends RefCounted: var people: Dictionary = {} func get_guest(id: StringName) -> Variant: return people.get(id) class Fixture extends RefCounted: var facility_service := Store.new() var guest_service := Guests.new() var world_service := WorldService.new() var elevators := ElevatorService.new() func _initialize() -> void: call_deferred("run") func expect(ok: bool, message: String) -> void: checks += 1 if not ok: failures += 1 push_error(message) func run() -> void: root.get_node("GameClock").set_paused(true) var dining_cells: Array[Vector3i] = [Vector3i(0, 0, 0), Vector3i(1, 0, 0), Vector3i(2, 0, 0), Vector3i(3, 0, 0), Vector3i(0, 1, 0), Vector3i(1, 1, 0), Vector3i(2, 1, 0), Vector3i(3, 1, 0)] var kitchen_cells: Array[Vector3i] = [Vector3i(0, 0, 1), Vector3i(1, 0, 1), Vector3i(2, 0, 1), Vector3i(3, 0, 1), Vector3i(0, 1, 1), Vector3i(1, 1, 1), Vector3i(2, 1, 1), Vector3i(3, 1, 1)] var room := FacilityInstance.new().configure(&"room", &"cafeteria", {&"occupied_cells": dining_cells, &"capacity": 99, &"customer_capacity": 6}) # Isolated presentation component fixture. No Runtime hire/attendance, # physical route, normal business, or production kitchen acceptance is claimed. expect(room.operation_state == FacilityInstance.OperationState.INACTIVE and Presentation.label(Presentation.describe(room), 3) == "未稼働", "unlinked component cafeteria is honestly inactive") var dependencies := FacilityService.new() var kitchen_definition: FacilityDefinition = load("res://data/facilities/kitchen.tres") var kitchen := FacilityInstance.new().configure(&"presentation_kitchen", &"kitchen", { &"origin": Vector3i(0, 0, WorldService.LAYER_BACK), &"occupied_cells": kitchen_cells, &"staff_min": kitchen_definition.staff_min, &"staff_max": kitchen_definition.staff_max, &"active_staff_ids": [&"component_cook"], &"access_available": true, &"power_available": true, &"operator_open": true, }) expect(dependencies.register_instance(room).is_success(), "register same cafeteria before owned service fixture") expect(dependencies.register_instance(kitchen).is_success(), "register controlled staffed BACK kitchen component") expect(dependencies.set_operational_status(room.instance_id, {&"related_facility_id": kitchen.instance_id}, 0).is_success(), "component uses public single kitchen link") expect(room.operation_state == FacilityInstance.OperationState.OPERATING and room.related_facility_id == kitchen.instance_id and kitchen.definition_id == &"kitchen" and kitchen.origin.z == WorldService.LAYER_BACK, "valid single BACK kitchen enables the original presentation fixture") var service := ServiceInstance.new() service.guest_ids.assign([&"f", &"a", &"c", &"e", &"b", &"d"]) room.active_services[&"service"] = service var data := Presentation.describe(room) expect(data[&"count"] == 6 and data[&"limit"] == 6, "people capacity is independent of storage/service capacity 99") expect(data[&"people"][0] == &"a" and "満員" in Presentation.label(data, 3) and "他3人" in Presentation.label(data, 3), "stable slots and honest overflow count") var before := room.create_snapshot() for id: StringName in [&"standard_room", &"cafeteria", &"reception"]: for extent: Vector2 in [Vector2(16,24), Vector2(32,48), Vector2(96,48)]: var rect := Rect2(Vector2(4,8), extent) var points := Presentation.slots(rect, 1, id) for i: int in points.size(): var body := Rect2(points[i]-Vector2(8,18), Vector2(16,18)) expect(rect.encloses(body), "person stays inside narrow/large room") for j: int in range(i): expect(not body.intersects(Rect2(points[j]-Vector2(8,18), Vector2(16,18))), "no overlapping or repeated use slots") for scale: float in [0.5,1.0,2.0,4.0]: var chapel_rect := Rect2(Vector2(16,32),Vector2(64,72)*scale) var chapel_slots := Presentation.slots(chapel_rect,scale,&"chapel") expect(chapel_slots.size()==8,"chapel reserves exactly eight visible visitor positions") for i in chapel_slots.size(): var body := Rect2(chapel_slots[i]-Vector2(8,18)*scale,Vector2(16,18)*scale) expect(chapel_rect.encloses(body) and body.position.y>=chapel_rect.position.y+30*scale,"chapel visitors remain inside room and below altar") for j in range(i): expect(not body.intersects(Rect2(chapel_slots[j]-Vector2(8,18)*scale,Vector2(16,18)*scale)),"chapel visitor bodies do not overlap at zoom levels") var rotated_chapel := Rect2(Vector2.ZERO,Vector2(48,96)) var rotated_slots := Presentation.slots(rotated_chapel,1,&"chapel") expect(rotated_slots.size()==8,"rotated chapel still accommodates eight visible visitors") for i in rotated_slots.size(): var body := Rect2(rotated_slots[i]-Vector2(8,18),Vector2(16,18)) expect(rotated_chapel.encloses(body) and body.position.y>=30,"rotated visitor stays below altar and inside room") for j in range(i): expect(not body.intersects(Rect2(rotated_slots[j]-Vector2(8,18),Vector2(16,18))),"rotated chapel visitors do not overlap") expect(room.create_snapshot() == before, "presentation leaves authoritative room data untouched") room.set_operational_conditions({&"operator_open": false}, 0) expect(Presentation.label(Presentation.describe(room), 3) == "未稼働", "closed room is inactive") room.set_operational_conditions({&"suspension_reason": &"RAIDED"}, 0) room.set_operational_conditions({&"operator_open": true}, 0) expect(Presentation.label(Presentation.describe(room), 3) == "休止・摘発", "reopening cannot clear raid suspension") var fixture := Fixture.new() fixture.facility_service.rooms = [room] fixture.world_service.reset({&"min_x": -3, &"max_x": 3, &"min_y": 0, &"max_y": 3}) for x: int in range(-3, 4): fixture.world_service.set_walkable(Vector3i(x,0,0), true) for i: int in 9: var id := StringName("guest_%02d" % i) var guest := GuestRuntime.new() guest.guest_id = id guest.current_cell = Vector3i.ZERO guest.state = GuestRuntime.State.QUEUING fixture.guest_service.people[id] = guest var request := ServiceRequest.new() request.guest_ids.assign([id]) fixture.facility_service.requests[id] = request room.queue_entries.append(id) var preview := WorldPreview.new() preview.size = Vector2(1100,600) preview.set_runtime(fixture) preview._prepare_room_presentations() preview._prepare_queues() var positions: Dictionary = preview._queue_positions.duplicate() expect(positions.size() == 7 and preview._queue_hidden.size() == 2, "bounded walkable lane uses seven distinct positions and summarizes overflow") var distinct: Dictionary = {} for at: Vector2 in positions.values(): distinct[at] = true expect(distinct.size() == positions.size(), "waiting people never repeat the same visible position") preview._prepare_queues() expect(positions == preview._queue_positions, "repeated frame preserves queue order and positions") for i: int in 9: fixture.guest_service.people[StringName("guest_%02d" % i)].current_cell = Vector3i(-1 if i < 4 else 1,0,0) preview._prepare_queues() distinct.clear() for at: Vector2 in preview._queue_positions.values(): distinct[at] = true expect(distinct.size() == preview._queue_positions.size(), "neighboring entrances share a corridor without overlapping their queues") expect(preview._queue_positions.size()+preview._queue_hidden.size() == 9, "neighboring queues retain every actual waiting guest") for guest: Variant in fixture.guest_service.people.values(): guest.current_cell = Vector3i.ZERO preview.layer_mode = WorldPreview.LayerMode.BACK preview._prepare_queues() expect(preview._queue_positions.is_empty() and preview._queue_hidden.is_empty(), "layer switch removes front queues") preview.layer_mode = WorldPreview.LayerMode.FRONT room.queue_entries.clear() var path := PathService.new() var walkable: Array[Vector3i] = fixture.world_service.get_walkable_cells() walkable.append(Vector3i(0,1,0)) path.rebuild_graph(walkable) fixture.elevators.configure(path) var config := {&"shaft_id": &"transport.shaft.0.0.0.2", &"module_ids": [&"module"], &"stops": [Vector3i.ZERO,Vector3i(0,1,0)], &"width": 2, &"capacity": 4, &"travel_minutes_per_floor": 10, &"access_id": &""} expect(fixture.elevators.synchronize([config],0)[&"ok"], "real elevator fixture initializes") for i: int in 9: expect(fixture.elevators.request_trip(StringName("guest/guest_%02d" % i),Vector3i.ZERO,Vector3i(0,1,0),0)[&"ok"], "real trip enters landing queue") preview._prepare_queues() expect(preview._queue_positions.size()+preview._queue_hidden.size() == 9, "landing represents every queued rider") expect(not preview._queue_positions.values().has(preview.cell_to_canvas(Vector3i.ZERO)), "waiting people stay outside the car footprint") fixture.elevators.advance_one_minute(1) preview._prepare_queues() var report: Dictionary = fixture.elevators.get_report() if report[&"shafts"].is_empty(): preview.free() quit(1) return var car: Dictionary = report[&"shafts"][0] expect(car[&"boarded"].size() == 4, "real elevator boards up to its capacity") for actor: StringName in car[&"boarded"]: expect(not preview._queue_positions.has(String(actor)) and not preview._queue_hidden.has(String(actor)), "boarded person is absent from landing display") var snapshot := fixture.elevators.create_snapshot() var queue_before: Dictionary = preview._queue_positions.duplicate() expect(fixture.elevators.restore_snapshot(snapshot), "elevator snapshot restores") preview._prepare_queues() expect(queue_before == preview._queue_positions, "restored elevator preserves visible landing positions") var initial_zoom := preview.zoom_level preview.focus_operating_area() expect(preview.zoom_level > initial_zoom and preview.canvas_to_cell(preview.cell_to_canvas(Vector3i.ZERO)) == Vector3i.ZERO, "initial framing enlarges the opening area without changing hit testing") preview.reset_view() expect(preview.zoom_level == 1.0 and preview.pan_offset == Vector2.ZERO, "Home overview remains available after opening focus") preview.free() print("PASS: %d room presentation checks; failures=%d" % [checks, failures]) quit(failures)