extends SceneTree ## Component and read-only rendering checks; never OS or same-demand proof. const Presentation := preload("res://scripts/ui/room_presentation.gd") const Stream := preload("res://tests/fixtures/p23-03/diagnostic_stream.gd") var checks := 0 var failures := 0 func _initialize() -> void: call_deferred("run") func expect(value: bool, message: String) -> void: checks += 1 if not value: failures += 1 push_error("P23 life observation: " + message) func run() -> void: var layout := OS.get_environment("P23_LAYOUT") var output_directory := OS.get_environment("P23_OUTPUT") if layout in ["A", "B"] and not output_directory.is_empty(): var comparison := preload("res://tests/fixtures/p23-03/layout_comparison.gd").new() expect(await comparison.run(self, output_directory, layout), "native layout " + layout) print("P23 life observation: %d checks, %d failures" % [checks, failures]) quit(0 if failures == 0 else 1) return var clock: Variant = root.get_node("GameClock") clock.set_paused(true) # Isolated real typed components test state labels, without claiming gameplay. var labels: Dictionary = {} for state: int in FacilityInstance.State.values(): var room := FacilityInstance.new().configure(&"component", &"reception") room.state = state as FacilityInstance.State var description := Presentation.describe(room) expect(description[&"state_name"] == FacilityInstance.State.keys()[state], "enum name preserved") var text := Presentation.business_label(description) expect(not text.is_empty() and not labels.has(text), "distinct visible business reason") labels[text] = true for reason: StringName in [&"RAIDED", &"DESTROYED"]: var room := FacilityInstance.new().configure(&"component", &"reception", {&"suspension_reason": reason}) var description := Presentation.describe(room) expect(description[&"operation"] == FacilityInstance.OperationState.SUSPENDED, "actual suspension operation") var text := Presentation.business_label(description) expect(not labels.has(text), "raid and damage have separate visible labels") labels[text] = true var room := FacilityInstance.new().configure(&"component", &"reception", {&"customer_capacity": 3}) var service := ServiceInstance.new() service.guest_ids.assign([&"b", &"a"]) room.active_services[&"service"] = service room.queue_entries.assign([&"queue-only"]) var description := Presentation.describe(room) expect(description[&"people"] == [&"a", &"b"] and description[&"count"] == 2, "queue entries are not room occupants") description[&"people"].clear() expect(service.guest_ids.size() == 2, "presentation owns its arrays") var runtime: Variant = root.get_node("SimulationRuntime") var view := WorldPreview.new() view.size = Vector2(1440, 810) view.set_runtime(runtime) root.add_child(view) # R13's four same-cell MOVING guests. Component fixture only, no Runtime edit. var crowd: Array = [] var offsets := [0, 16, -16, 8] for i: int in 4: crowd.append({&"row": {&"key": "guest/os-p23-%03d" % (12+i), &"center": Vector2(108+offsets[i], 118-4*(i%2)), &"at": Vector2(84+offsets[i], 103-4*(i%2)), &"scale": 3.0, &"mode": &"idle", &"frame": 0, &"flip": false, &"state": GuestRuntime.State.MOVING, &"cell": Vector3i(1,1,0)}, &"bounds": Rect2(80,74,56,88), &"transit": false}) crowd.sort_custom(func(a: Dictionary, b: Dictionary) -> bool: return a[&"row"][&"center"].x < b[&"row"][&"center"].x) # _life_pack uses the view's cell dimensions for the unchanged feet formula. # The x-capacity assertion is independent of the current default world size. expect(view._life_pack(crowd, 3).is_empty(), "four cores cannot fit 72px in 56px") var saved_size := view.size var saved_zoom := view.zoom_level var dim: Vector2 = view._cell_dimensions() view.zoom_level *= 64.0/dim.x var fitted: Array = view._life_pack(crowd, 2) expect(fitted.size() == 4, "all four R13 occupants fit at p=2") for row: Dictionary in fitted: expect(row[&"scale"] == 2.0 and row[&"cell"] == Vector3i(1,1,0), "fitting preserves cell and uses p=2") var regroup: Array = crowd.slice(0, 2).duplicate(true) for i: int in 2: regroup[i][&"row"][&"center"] = Vector2(108, 118+26*i) regroup[i][&"bounds"] = Rect2(80,74,20,130) var regrouped: Array = view._life_fitted_groups(regroup) expect(regrouped.size() == 2, "scale change splits vertical component") for group: Array in regrouped: expect(group.size() == 1 and group[0][&"row"][&"scale"] == 2.0, "regroup avoids stale p=1 fit") var saturated: Array = [] for i: int in 10: var member: Dictionary = crowd[0].duplicate(true) member[&"row"][&"key"] = "guest/saturation-%02d" % i member[&"row"][&"center"] = Vector2(108,118) saturated.append(member) var saturated_groups: Array = view._life_fitted_groups(saturated) expect(saturated_groups.size() == 1, "saturation keeps one vertical component") expect(view._life_pack(saturated_groups[0], 1).is_empty(), "ten cores cannot fit 60px in 56px") expect(view._life_pack(saturated_groups[0].slice(0,9), 1).size() == 9, "nine cores fit without losing identities") var aggregate_model := {&"guests": [], &"staff": [], &"actor_drawings": [], &"cabins": []} var aggregate_rows: Array = [] for member: Dictionary in saturated: var row: Dictionary = member[&"row"].duplicate(true) row[&"riding"] = true aggregate_rows.append(row) aggregate_model[&"guests"].append({&"id": String(row[&"key"]).trim_prefix("guest/"), &"cell": Vector3i(1,1,0)}) aggregate_model[&"cabins"].append({&"rect": Rect2(76,70,64,96), &"passengers": aggregate_rows}) view._fit_life_actors(aggregate_model) expect(aggregate_model[&"cabins"][0][&"passengers"].size() == 9, "aggregation draws all nine fitting members") var aggregate: Dictionary = aggregate_model[&"actor_aggregations"][0] expect(aggregate[&"members"].size() == 10 and aggregate[&"drawn_keys"].size() == 9, "aggregation retains every identity") expect(aggregate[&"text"] == "+1" and aggregate[&"at"] == Vector2(80,74), "actual aggregation badge position and text") view.zoom_level = saved_zoom view.size = saved_size var before: PackedByteArray = var_to_bytes(runtime.create_snapshot()) var tracks: PackedByteArray = var_to_bytes([view._actor_tracks, view._elevator_tracks]) var model := view._build_life_observation() expect(var_to_bytes(runtime.create_snapshot()) == before, "builder does not mutate Runtime") expect(var_to_bytes([view._actor_tracks, view._elevator_tracks]) == tracks, "builder does not advance visual tracks") expect(model.has(&"rooms") and model.has(&"landing_badges") and model.has(&"elevators") and model.has(&"staff") and model.has(&"goods") and model.has(&"badges"), "all six category fields exist") expect(model[&"traffic"].size() > 0 and not view.show_grid, "normal view observes real walkable traffic") await process_frame await process_frame var report := view.get_life_observation_report() expect(not report.is_empty(), "actual drawing published an observation") expect(report.get(&"observation_serial", -1) == report.get(&"overlay_consumed_serial", -2), "LifeOverlay consumed this observation") expect(report.get(&"overlay_passes", []) == [&"traffic", &"actors"], "traffic drawn before actors") var indices: Array = report.get(&"overlay_child_indices", []) expect(indices.size() == 3 and indices[0] < indices[1] and indices[1] < indices[2], "LifeOverlay above inactive art and below lens") # The getter exposes the last actual draw and must not create another model. var original: PackedByteArray = var_to_bytes(report) if report.has(&"traffic"): report[&"traffic"].clear() expect(var_to_bytes(view.get_life_observation_report()) == original, "nested report arrays are isolated") report.clear() expect(var_to_bytes(view.get_life_observation_report()) == original, "public report is a deep copy") var output := OS.get_environment("P23_LIFE_DIAGNOSTIC_ROOT") if not output.is_empty(): var stream := Stream.new() expect(stream.open(output), "fresh sparse diagnostic stream") expect(stream.capture("initial", runtime, view), "whole typed state and draw report capture") stream.close() view.free() print("P23 life observation: %d checks, %d failures" % [checks, failures]) quit(0 if failures == 0 else 1)