Files
TheTree_mock/tests/ui/test_room_presentation.gd
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

172 lines
10 KiB
GDScript

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)