extends RefCounted class_name Levels ## The workplaces, as data. Each is a Floorplan spec (see Floorplan.gd for the schema). ## ## The point of these being data and not four subclasses: a new site is authorable in ## minutes, they can be diffed, and a generator can later emit the same structure. ## ## They are deliberately NOT reskins. Each differs in the three things a player actually ## reads — the palette, the light, and what the walls are made of: ## ## SCRANTON ..... magnolia, grey carpet, drop ceiling, fluorescent troffers, daylight ## down one glazed wall. The baseline office. ## PAWNEE ....... civic beige and blue-grey, low partitions everywhere, a public ## counter, pinboards. Municipal, over-partitioned, no privacy. ## THE HOUSE .... timber floor, white walls, 3 m ceiling, pendant lights, a glass wall ## onto a pool. Nobody here has an office; they work at a dining table. ## THE BASEMENT . concrete, no windows at all, exposed services, bare strip lights, a ## wall of server racks. The light is green and everything is junk. ## ## Godot 4.7 GDScript 2.0. const ORDER := ["scranton", "pawnee", "house", "basement", "grocer", "backrooms", "store"] static func get_level(id: String) -> Dictionary: match id: "pawnee": return pawnee() "house": return house() "basement": return basement() "backrooms": return backrooms() "grocer": return grocer() "store": return record_store() return scranton() static func next_id(id: String) -> String: var i := ORDER.find(id) return ORDER[(i + 1) % ORDER.size()] if i >= 0 else ORDER[0] # ================================================================ LEVEL 01 static func scranton() -> Dictionary: return { "name": "SCRANTON BRANCH", "subtitle": "paper company · 2nd floor", "bounds": {"x0": -11.0, "x1": 11.0, "z0": -8.0, "z1": 8.0, "h": 2.75}, "palette": { "floor": Color(0.30, 0.30, 0.32), "wall": Color(0.80, 0.78, 0.72), "ceiling": Color(0.88, 0.88, 0.86), "trim": Color(0.13, 0.12, 0.13), "partition": Color(0.44, 0.44, 0.40), "wood": Color(0.42, 0.29, 0.17), "accent": Color(0.36, 0.42, 0.50), }, "floor_style": "carpet", "ceiling": "tile", "walls": [ # manager's office — half solid, half glass, so you can be watched through it {"a": "x", "at": -4.4, "from": -11.0, "to": -6.6, "door": -9.2}, {"a": "z", "at": -6.6, "from": -8.0, "to": -4.4, "style": "glass_top"}, # conference room {"a": "x", "at": -4.4, "from": -6.6, "to": -0.4, "door": -3.4}, {"a": "z", "at": -0.4, "from": -8.0, "to": -4.4}, # break room {"a": "z", "at": 5.4, "from": -1.2, "to": 8.0, "door": 2.2}, {"a": "x", "at": -1.2, "from": 5.4, "to": 11.0}, # copier alcove {"a": "z", "at": 3.6, "from": -1.6, "to": 1.0}, # reception screen {"a": "z", "at": -6.9, "from": 4.6, "to": 8.0, "door": 6.8}, ], "windows": [{"a": "z", "at": 11.0, "from": -8.0, "to": 8.0, "y0": 0.85, "y1": 2.35, "blinds": true}], "lights": {"xs": [-8.5, -4.6, -0.7, 3.2, 7.1], "zs": [-6.2, -2.6, 1.0, 4.6], "color": Color(1.0, 0.98, 0.93), "energy": 1.9, "style": "troffer"}, "sun": {"color": Color(0.82, 0.88, 1.0), "energy": 0.7, "rot": Vector3(-30, -104, 0)}, "env": {"bg": Color(0.55, 0.62, 0.72), "ambient": Color(0.50, 0.50, 0.50), "ambient_energy": 0.5}, "clusters": [Vector2(-4.6, -2.0), Vector2(-4.6, 2.8), Vector2(-0.4, -2.0), Vector2(-0.4, 2.8)], "cluster_gap": 0.78, "slabs": [ # reception counter {"size": Vector3(0.70, 1.10, 3.0), "at": Vector3(-9.9, 0.55, 5.6), "mat": "wood"}, {"size": Vector3(0.92, 0.08, 3.2), "at": Vector3(-9.9, 1.14, 5.6), "mat": "trim"}, {"size": Vector3(1.8, 1.10, 0.70), "at": Vector3(-9.6, 0.55, 4.45), "mat": "wood"}, # conference table + whiteboard {"size": Vector3(3.9, 0.09, 1.35), "at": Vector3(-3.5, 0.74, -6.2), "mat": "wood"}, {"size": Vector3(0.16, 0.72, 0.90), "at": Vector3(-5.1, 0.36, -6.2), "mat": "trim"}, {"size": Vector3(0.16, 0.72, 0.90), "at": Vector3(-1.9, 0.36, -6.2), "mat": "trim"}, {"size": Vector3(2.5, 1.25, 0.03), "at": Vector3(-3.5, 1.6, -7.93), "mat": "trim"}, {"size": Vector3(2.4, 1.15, 0.05), "at": Vector3(-3.5, 1.6, -7.90), "mat": "ceiling"}, # break room kitchen + tables {"size": Vector3(0.62, 0.88, 3.4), "at": Vector3(10.58, 0.44, 4.0), "mat": "wood"}, {"size": Vector3(0.70, 0.06, 3.5), "at": Vector3(10.58, 0.90, 4.0), "mat": "trim"}, {"size": Vector3(1.05, 0.07, 1.05), "at": Vector3(7.4, 0.73, 0.4), "mat": "ceiling"}, {"size": Vector3(0.12, 0.70, 0.12), "at": Vector3(7.4, 0.36, 0.4), "mat": "trim"}, {"size": Vector3(1.05, 0.07, 1.05), "at": Vector3(7.4, 0.73, 3.0), "mat": "ceiling"}, {"size": Vector3(0.12, 0.70, 0.12), "at": Vector3(7.4, 0.36, 3.0), "mat": "trim"}, # warehouse roller door {"size": Vector3(2.6, 2.4, 0.12), "at": Vector3(7.8, 1.2, -7.94), "mat": "trim"}, ], "props": [ {"glb": "store-column-white", "at": Vector3(-2.2, 0.0, -3.4)}, {"glb": "store-column-white", "at": Vector3(2.2, 0.0, -3.4)}, {"glb": "store-aircon", "at": Vector3(2.6, 2.35, -7.9)}, {"glb": "office-plant", "at": Vector3(-6.4, 0.0, 6.6)}, {"glb": "office-plant", "at": Vector3(2.9, 0.0, 6.4)}, {"glb": "vending-machine", "at": Vector3(6.3, 0.0, 7.4), "yaw": PI}, {"glb": "vending-machine", "at": Vector3(7.3, 0.0, 7.4), "yaw": PI}, {"glb": "microwave", "at": Vector3(10.55, 0.93, 5.1), "yaw": -PI / 2}, ], "chairs": [ {"at": Vector3(-7.6, 0.0, 5.5), "yaw": -PI / 2}, {"at": Vector3(-7.6, 0.0, 6.35), "yaw": -PI / 2}, {"at": Vector3(-7.6, 0.0, 7.2), "yaw": -PI / 2}, {"at": Vector3(-4.85, 0.0, -7.35), "yaw": 0.0}, {"at": Vector3(-3.95, 0.0, -7.35), "yaw": 0.0}, {"at": Vector3(-3.05, 0.0, -7.35), "yaw": 0.0}, {"at": Vector3(-2.15, 0.0, -7.35), "yaw": 0.0}, {"at": Vector3(-4.85, 0.0, -5.05), "yaw": PI}, {"at": Vector3(-3.95, 0.0, -5.05), "yaw": PI}, {"at": Vector3(-3.05, 0.0, -5.05), "yaw": PI}, {"at": Vector3(-2.15, 0.0, -5.05), "yaw": PI}, {"at": Vector3(-8.8, 0.0, -7.1), "yaw": 0.0}, {"at": Vector3(-9.6, 0.0, -5.1), "yaw": PI}, {"at": Vector3(8.0, 0.0, 1.13), "yaw": 2.0}, {"at": Vector3(7.7, 0.0, -0.49), "yaw": 4.1}, {"at": Vector3(6.5, 0.0, 0.56), "yaw": 0.2}, {"at": Vector3(8.0, 0.0, 3.73), "yaw": 2.0}, {"at": Vector3(7.7, 0.0, 2.11), "yaw": 4.1}, {"at": Vector3(6.5, 0.0, 3.16), "yaw": 0.2}, ], "copier": {"at": Vector3(3.0, 0.0, -0.3), "yaw": -PI / 2}, "smashables": [ {"glb": "filing-cabinet", "kind": "steel", "at": Vector2(4.5, -1.6)}, {"glb": "filing-cabinet", "kind": "steel", "at": Vector2(4.5, -0.9)}, {"glb": "filing-cabinet", "kind": "steel", "at": Vector2(4.5, -0.2)}, {"glb": "water-cooler", "kind": "steel", "at": Vector2(-6.2, 3.4)}, {"glb": "water-cooler", "kind": "steel", "at": Vector2(5.9, -0.6)}, {"glb": "cardboard-box", "kind": "cardboard", "at": Vector2(-2.3, 5.2)}, # an archive box is 1.35 m across, so a "couple of boxes dumped together" is a # PILE, not two of them side by side inside each other {"glb": "cardboard-box", "kind": "cardboard", "at": Vector2(1.6, 5.4)}, {"glb": "cardboard-box", "kind": "cardboard", "at": Vector2(1.74, 5.26), "sit_on": 0.40, "yaw": 0.10}, {"glb": "cardboard-box", "kind": "cardboard", "at": Vector2(6.4, -3.3)}, {"glb": "cardboard-box", "kind": "cardboard", "at": Vector2(6.52, -3.44), "sit_on": 0.40, "yaw": -0.08}, {"glb": "cardboard-box", "kind": "cardboard", "at": Vector2(-9.7, 1.2)}, {"glb": "wastebin", "kind": "plastic", "at": Vector2(-6.0, -1.0)}, {"glb": "wastebin", "kind": "plastic", "at": Vector2(1.0, 1.0)}, {"glb": "desk-phone", "kind": "plastic", "at": Vector2(-9.8, 5.0), "sit_on": 1.18}, {"glb": "rack", "kind": "wood", "at": Vector2(9.4, -6.6), "frozen": true}, {"glb": "rack", "kind": "wood", "at": Vector2(9.4, -4.3), "frozen": true}, {"glb": "turntable", "kind": "wood", "at": Vector2(6.9, -5.9)}, {"glb": "guillotine", "kind": "steel", "at": Vector2(4.5, 1.2)}, {"glb": "stapler", "kind": "steel", "at": Vector2(-5.25, -2.78), "sit_on": 0.75}, ], "records": [Vector2(6.2, -6.6), Vector2(7.0, -6.9), Vector2(7.8, -6.6)], "gauntlet": {"at": Vector3(0.0, 0.0, -3.9), "yaw": PI, "stand": Vector3(0.0, 0.0, -1.7), "shredder": Vector3(2.7, 0.0, -2.4)}, "spawn": {"at": Vector3(-8.6, 0.0, 7.2), "yaw": deg_to_rad(196.0)}, } # ================================================================ LEVEL 02 static func pawnee() -> Dictionary: return { "name": "PAWNEE CITY HALL", "subtitle": "parks & recreation · 4th floor", "bounds": {"x0": -10.0, "x1": 10.0, "z0": -7.0, "z1": 7.5, "h": 2.70}, "palette": { "floor": Color(0.34, 0.36, 0.40), "wall": Color(0.84, 0.80, 0.68), "ceiling": Color(0.90, 0.89, 0.85), "trim": Color(0.30, 0.22, 0.14), "partition": Color(0.52, 0.50, 0.42), "wood": Color(0.50, 0.34, 0.19), "accent": Color(0.16, 0.36, 0.28), }, "floor_style": "carpet", "ceiling": "tile", "walls": [ # the department head's office, and the meeting room beside it {"a": "x", "at": -3.4, "from": -10.0, "to": -5.6, "door": -7.0}, {"a": "z", "at": -5.6, "from": -7.0, "to": -3.4, "style": "glass_top"}, {"a": "x", "at": -3.4, "from": -5.6, "to": 0.4, "door": -1.6}, {"a": "z", "at": 0.4, "from": -7.0, "to": -3.4}, # records room, and the corridor wall that makes this place a warren {"a": "z", "at": 4.6, "from": -7.0, "to": -2.0, "door": -4.4}, {"a": "x", "at": -2.0, "from": 4.6, "to": 10.0}, # the public counter divides the front third from everything else {"a": "x", "at": 3.6, "from": -10.0, "to": 3.0, "door": -8.0, "style": "half"}, ], "windows": [{"a": "x", "at": 7.5, "from": -10.0, "to": 10.0, "y0": 0.95, "y1": 2.30, "blinds": true}], "lights": {"xs": [-7.5, -3.4, 0.7, 4.8, 8.4], "zs": [-5.2, -1.6, 2.0, 5.6], "color": Color(1.0, 0.96, 0.86), "energy": 1.8, "style": "troffer", "key_energy": 1.4}, "sun": {"color": Color(0.90, 0.88, 0.78), "energy": 0.55, "rot": Vector3(-34, 20, 0)}, "env": {"bg": Color(0.62, 0.66, 0.62), "ambient": Color(0.50, 0.48, 0.44), "ambient_energy": 0.52, "exposure": 1.02}, "clusters": [Vector2(-6.0, 0.4), Vector2(-1.4, 0.4), Vector2(2.6, 0.4)], "cluster_gap": 0.78, "slabs": [ # the public counter itself — the thing between you and the citizens {"size": Vector3(6.6, 1.06, 0.66), "at": Vector3(-1.2, 0.53, 5.4), "mat": "wood"}, {"size": Vector3(6.9, 0.07, 0.86), "at": Vector3(-1.2, 1.10, 5.4), "mat": "trim"}, {"size": Vector3(0.66, 1.06, 2.2), "at": Vector3(2.1, 0.53, 6.4), "mat": "wood"}, # meeting room table {"size": Vector3(2.9, 0.09, 1.25), "at": Vector3(-2.6, 0.74, -5.2), "mat": "wood"}, {"size": Vector3(0.14, 0.72, 0.84), "at": Vector3(-3.7, 0.36, -5.2), "mat": "trim"}, {"size": Vector3(0.14, 0.72, 0.84), "at": Vector3(-1.5, 0.36, -5.2), "mat": "trim"}, # pinboards — municipal offices are 40% pinboard by surface area {"size": Vector3(2.2, 1.20, 0.05), "at": Vector3(5.0, 1.65, -6.93), "mat": "accent"}, {"size": Vector3(2.3, 1.30, 0.03), "at": Vector3(5.0, 1.65, -6.90), "mat": "trim"}, {"size": Vector3(0.05, 1.20, 2.6), "at": Vector3(-9.93, 1.62, 2.0), "mat": "accent"}, {"size": Vector3(0.03, 1.30, 2.7), "at": Vector3(-9.90, 1.62, 2.0), "mat": "trim"}, # department head's desk {"size": Vector3(1.7, 0.08, 0.85), "at": Vector3(-7.4, 0.74, -5.4), "mat": "wood"}, {"size": Vector3(1.5, 0.66, 0.72), "at": Vector3(-7.4, 0.35, -5.4), "mat": "wood"}, ], "props": [ {"glb": "office-plant", "at": Vector3(-9.3, 0.0, 6.6)}, {"glb": "office-plant", "at": Vector3(3.2, 0.0, -2.6)}, {"glb": "office-plant", "at": Vector3(-9.3, 0.0, -1.0)}, {"glb": "sofa", "at": Vector3(-6.6, 0.0, 6.5), "yaw": PI}, {"glb": "vending-machine", "at": Vector3(9.2, 0.0, 6.6), "yaw": -PI / 2}, {"glb": "microwave", "at": Vector3(9.3, 0.90, 1.2), "yaw": -PI / 2}, {"glb": "store-aircon", "at": Vector3(-6.0, 2.30, -6.9)}, ], "chairs": [ {"at": Vector3(-7.4, 0.0, -4.2), "yaw": PI}, {"at": Vector3(-8.6, 0.0, -6.3), "yaw": 0.0}, {"at": Vector3(-3.5, 0.0, -6.3), "yaw": 0.0}, {"at": Vector3(-2.6, 0.0, -6.3), "yaw": 0.0}, {"at": Vector3(-1.7, 0.0, -6.3), "yaw": 0.0}, {"at": Vector3(-3.5, 0.0, -4.1), "yaw": PI}, {"at": Vector3(-2.6, 0.0, -4.1), "yaw": PI}, {"at": Vector3(-1.7, 0.0, -4.1), "yaw": PI}, {"at": Vector3(-4.0, 0.0, 6.4), "yaw": PI}, {"at": Vector3(0.4, 0.0, 6.5), "yaw": PI}, {"at": Vector3(1.2, 0.0, 6.5), "yaw": PI}, ], "copier": {"at": Vector3(6.4, 0.0, 2.4), "yaw": PI}, "smashables": [ # a cabinet is 0.80 m wide; a row of them 0.70 m apart is one cabinet {"glb": "filing-cabinet", "kind": "steel", "at": Vector2(5.4, -3.2)}, {"glb": "filing-cabinet", "kind": "steel", "at": Vector2(6.26, -3.2)}, {"glb": "filing-cabinet", "kind": "steel", "at": Vector2(7.12, -3.2)}, {"glb": "filing-cabinet", "kind": "steel", "at": Vector2(7.98, -3.2)}, {"glb": "water-cooler", "kind": "steel", "at": Vector2(3.9, 4.2)}, {"glb": "cardboard-box", "kind": "cardboard", "at": Vector2(8.6, -5.4)}, {"glb": "cardboard-box", "kind": "cardboard", "at": Vector2(8.74, -5.26), "sit_on": 0.40, "yaw": 0.12}, {"glb": "cardboard-box", "kind": "cardboard", "at": Vector2(-4.8, 3.0)}, {"glb": "wastebin", "kind": "plastic", "at": Vector2(-8.8, -3.9)}, {"glb": "wastebin", "kind": "plastic", "at": Vector2(4.4, 0.9)}, {"glb": "guillotine", "kind": "steel", "at": Vector2(6.4, 3.6), "sit_on": 0.0}, {"glb": "desk-phone", "kind": "plastic", "at": Vector2(-6.6, -5.4), "sit_on": 0.78}, {"glb": "crt-monitor", "kind": "glass", "at": Vector2(-7.6, -5.2), "sit_on": 0.78}, ], "records": [], "gauntlet": {"at": Vector3(7.2, 0.0, -5.8), "yaw": PI, "stand": Vector3(7.2, 0.0, -3.9), "shredder": Vector3(5.2, 0.0, -6.2)}, "spawn": {"at": Vector3(-8.0, 0.0, 6.6), "yaw": deg_to_rad(178.0)}, } # ================================================================ LEVEL 03 static func house() -> Dictionary: return { "name": "THE INCUBATOR", "subtitle": "seed round · someone's house", "bounds": {"x0": -10.0, "x1": 10.0, "z0": -8.0, "z1": 8.0, "h": 3.05}, "palette": { "floor": Color(0.46, 0.33, 0.20), "wall": Color(0.92, 0.91, 0.88), "ceiling": Color(0.95, 0.95, 0.94), "trim": Color(0.18, 0.17, 0.17), "partition": Color(0.72, 0.72, 0.70), "wood": Color(0.40, 0.27, 0.16), "accent": Color(0.22, 0.44, 0.62), }, "floor_style": "timber", "ceiling": "flat", "walls": [ # only two real walls: a bedroom-turned-office and the bathroom nobody cleans {"a": "z", "at": -4.6, "from": -8.0, "to": -2.4, "door": -4.0}, {"a": "x", "at": -2.4, "from": -10.0, "to": -4.6, "door": -7.4}, {"a": "z", "at": 4.0, "from": -8.0, "to": -5.2, "door": -6.6}, # the kitchen island's back wall {"a": "x", "at": -1.0, "from": 4.0, "to": 10.0, "style": "half"}, ], "windows": [{"a": "x", "at": 8.0, "from": -10.0, "to": 10.0, "y0": 0.35, "y1": 2.70, "blinds": false}], "lights": {"xs": [-6.5, -1.5, 3.5, 7.5], "zs": [-5.0, 0.0, 5.0], "color": Color(1.0, 0.90, 0.76), "energy": 2.4, "style": "pendant", "key_energy": 1.7}, "sun": {"color": Color(1.0, 0.94, 0.80), "energy": 1.5, "rot": Vector3(-46, -96, 0)}, "env": {"bg": Color(0.70, 0.80, 0.90), "ambient": Color(0.56, 0.55, 0.52), "ambient_energy": 0.62, "exposure": 1.05}, # nobody has a desk; they work at the big table "clusters": [Vector2(1.6, -1.4), Vector2(1.6, 2.6)], "cluster_gap": 0.80, "slabs": [ # kitchen island + counter {"size": Vector3(2.6, 0.92, 1.05), "at": Vector3(4.2, 0.46, 5.6), "mat": "wood"}, {"size": Vector3(2.8, 0.08, 1.20), "at": Vector3(4.2, 0.95, 5.6), "mat": "ceiling"}, {"size": Vector3(6.0, 0.90, 0.65), "at": Vector3(5.0, 0.45, 7.5), "mat": "ceiling"}, {"size": Vector3(6.2, 0.06, 0.75), "at": Vector3(5.0, 0.92, 7.5), "mat": "trim"}, # the long table everyone actually works at {"size": Vector3(1.5, 0.09, 4.6), "at": Vector3(-4.4, 0.75, 0.6), "mat": "wood"}, {"size": Vector3(0.12, 0.72, 0.12), "at": Vector3(-4.9, 0.36, -1.4), "mat": "trim"}, {"size": Vector3(0.12, 0.72, 0.12), "at": Vector3(-3.9, 0.36, 2.6), "mat": "trim"}, # whiteboard, obviously {"size": Vector3(0.05, 1.40, 3.0), "at": Vector3(-9.93, 1.75, 1.0), "mat": "ceiling"}, {"size": Vector3(0.03, 1.50, 3.1), "at": Vector3(-9.90, 1.75, 1.0), "mat": "trim"}, # a low bookshelf run {"size": Vector3(0.42, 0.90, 3.2), "at": Vector3(-9.7, 0.45, -5.0), "mat": "wood"}, # pool decking visible through the glass {"size": Vector3(7.0, 0.10, 16.0), "at": Vector3(12.6, -0.05, 0.0), "mat": "ceiling", "collide": false}, ], "props": [ {"glb": "sofa", "at": Vector3(-6.6, 0.0, 5.4), "yaw": -PI / 2}, {"glb": "sofa", "at": Vector3(-2.0, 0.0, 6.6), "yaw": PI}, {"glb": "office-plant", "at": Vector3(-0.6, 0.0, 3.2)}, {"glb": "office-plant", "at": Vector3(7.2, 0.0, -6.4)}, {"glb": "office-plant", "at": Vector3(-9.2, 0.0, 7.2)}, {"glb": "microwave", "at": Vector3(5.4, 0.95, 7.4), "yaw": PI}, {"glb": "server-rack", "at": Vector3(9.0, 0.0, -6.8), "yaw": -PI / 2}, ], "chairs": [ {"at": Vector3(-6.0, 0.0, -1.4), "yaw": -PI / 2}, {"at": Vector3(-6.0, 0.0, -0.2), "yaw": -PI / 2}, {"at": Vector3(-6.0, 0.0, 1.4), "yaw": -PI / 2}, {"at": Vector3(-6.0, 0.0, 2.6), "yaw": -PI / 2}, {"at": Vector3(-2.8, 0.0, -1.0), "yaw": PI / 2}, {"at": Vector3(-2.8, 0.0, 1.8), "yaw": PI / 2}, {"at": Vector3(4.2, 0.0, 4.2), "yaw": 0.0}, {"at": Vector3(5.2, 0.0, 4.2), "yaw": 0.0}, {"at": Vector3(-7.6, 0.0, -6.4), "yaw": 0.2}, ], "copier": {"at": Vector3(-8.6, 0.0, -3.4), "yaw": PI / 2}, "smashables": [ {"glb": "crt-monitor", "kind": "glass", "at": Vector2(-4.4, -2.2)}, {"glb": "cardboard-box", "kind": "cardboard", "at": Vector2(8.4, 2.2)}, {"glb": "cardboard-box", "kind": "cardboard", "at": Vector2(8.54, 2.06), "sit_on": 0.40, "yaw": -0.14}, {"glb": "cardboard-box", "kind": "cardboard", "at": Vector2(-8.2, 6.4)}, {"glb": "wastebin", "kind": "plastic", "at": Vector2(-2.6, -3.2)}, {"glb": "wastebin", "kind": "plastic", "at": Vector2(6.2, 3.0)}, {"glb": "water-cooler", "kind": "steel", "at": Vector2(1.0, 6.8)}, {"glb": "filing-cabinet", "kind": "steel", "at": Vector2(-9.3, -1.6)}, {"glb": "turntable", "kind": "wood", "at": Vector2(-9.4, -6.6)}, {"glb": "desk-phone", "kind": "plastic", "at": Vector2(4.0, 5.6), "sit_on": 0.99}, {"glb": "guillotine", "kind": "steel", "at": Vector2(-9.4, -3.4)}, ], "records": [Vector2(-8.4, -7.0), Vector2(-7.6, -7.0)], "gauntlet": {"at": Vector3(-7.2, 0.0, -7.2), "yaw": PI, "stand": Vector3(-7.2, 0.0, -5.2), "shredder": Vector3(-5.0, 0.0, -6.8)}, "spawn": {"at": Vector3(-1.0, 0.0, 7.0), "yaw": deg_to_rad(186.0)}, } # ================================================================ LEVEL 04 static func basement() -> Dictionary: return { "name": "SUB-LEVEL 4", "subtitle": "i.t. department · no windows", "bounds": {"x0": -12.0, "x1": 12.0, "z0": -6.0, "z1": 6.0, "h": 2.90}, "palette": { "floor": Color(0.22, 0.22, 0.23), "wall": Color(0.50, 0.53, 0.46), "ceiling": Color(0.20, 0.20, 0.21), "trim": Color(0.10, 0.10, 0.10), "partition": Color(0.34, 0.36, 0.32), "wood": Color(0.30, 0.20, 0.12), "accent": Color(0.52, 0.10, 0.09), }, "floor_style": "concrete", "ceiling": "flat", "walls": [ # the boss's glass box, bolted into a room that never wanted one {"a": "z", "at": 4.6, "from": -6.0, "to": 1.2, "door": -1.0, "style": "glass_top"}, {"a": "x", "at": 1.2, "from": 4.6, "to": 12.0}, # server room behind a security door {"a": "z", "at": -6.4, "from": -6.0, "to": 6.0, "door": 1.0}, # a stub wall someone put up and nobody took down {"a": "x", "at": -1.4, "from": -4.0, "to": -1.6}, ], "windows": [], # it is a basement "lights": {"xs": [-9.0, -4.5, 0.0, 4.5, 9.0], "zs": [-3.6, 0.4, 4.0], "color": Color(0.86, 0.96, 0.84), "energy": 2.0, "style": "strip", "key_energy": 1.1}, "env": {"bg": Color(0.06, 0.07, 0.06), "ambient": Color(0.32, 0.36, 0.32), "ambient_energy": 0.40, "exposure": 1.08}, "clusters": [Vector2(3.6, 0.0)], "cluster_gap": 0.82, "slabs": [ # exposed services, because there's no ceiling to hide them {"size": Vector3(24.0, 0.22, 0.22), "at": Vector3(0.0, 2.70, -3.0), "mat": "trim", "collide": false}, {"size": Vector3(24.0, 0.16, 0.16), "at": Vector3(0.0, 2.62, 1.4), "mat": "trim", "collide": false}, {"size": Vector3(24.0, 0.30, 0.10), "at": Vector3(0.0, 2.78, 4.2), "mat": "trim", "collide": false}, # boss's desk in the glass box {"size": Vector3(1.8, 0.08, 0.86), "at": Vector3(8.0, 0.74, 5.4), "mat": "wood"}, {"size": Vector3(1.6, 0.66, 0.74), "at": Vector3(8.0, 0.35, 5.4), "mat": "wood"}, # workbench along the back {"size": Vector3(5.0, 0.08, 0.80), "at": Vector3(-6.0, 0.90, -5.5), "mat": "trim"}, {"size": Vector3(0.10, 0.88, 0.70), "at": Vector3(-8.4, 0.44, -5.5), "mat": "trim"}, {"size": Vector3(0.10, 0.88, 0.70), "at": Vector3(-3.6, 0.44, -5.5), "mat": "trim"}, # the famous red door {"size": Vector3(1.10, 2.10, 0.10), "at": Vector3(1.0, 1.05, -6.35), "mat": "accent"}, ], "props": [ {"glb": "server-rack", "at": Vector3(-10.6, 0.0, -4.4), "yaw": PI / 2}, {"glb": "server-rack", "at": Vector3(-10.6, 0.0, -2.4), "yaw": PI / 2}, {"glb": "server-rack", "at": Vector3(-10.6, 0.0, -0.4), "yaw": PI / 2}, {"glb": "server-rack", "at": Vector3(-10.6, 0.0, 1.6), "yaw": PI / 2}, {"glb": "sofa", "at": Vector3(-4.0, 0.0, 3.4), "yaw": 0.15}, {"glb": "office-plant", "at": Vector3(11.2, 0.0, 5.4)}, {"glb": "vending-machine", "at": Vector3(-0.4, 0.0, 5.4), "yaw": PI}, {"glb": "microwave", "at": Vector3(-5.0, 0.95, -5.5), "yaw": PI}, ], "chairs": [ {"at": Vector3(8.0, 0.0, 4.1), "yaw": PI}, {"at": Vector3(6.6, 0.0, 5.9), "yaw": 0.3}, {"at": Vector3(-6.6, 0.0, -4.2), "yaw": PI}, {"at": Vector3(-1.0, 0.0, 2.2), "yaw": 1.2}, ], "copier": {"at": Vector3(0.4, 0.0, -4.4), "yaw": 0.0}, "smashables": [ {"glb": "crt-monitor", "kind": "glass", "at": Vector2(-7.0, -5.5), "sit_on": 0.94}, {"glb": "crt-monitor", "kind": "glass", "at": Vector2(-5.0, -4.6)}, {"glb": "crt-monitor", "kind": "glass", "at": Vector2(-8.4, -3.2)}, {"glb": "filing-cabinet", "kind": "steel", "at": Vector2(10.8, 3.0)}, {"glb": "filing-cabinet", "kind": "steel", "at": Vector2(10.8, 2.2)}, # nobody in an IT basement puts a box down on its own {"glb": "cardboard-box", "kind": "cardboard", "at": Vector2(-2.6, -4.8)}, {"glb": "cardboard-box", "kind": "cardboard", "at": Vector2(-2.46, -4.92), "sit_on": 0.40, "yaw": 0.09}, {"glb": "cardboard-box", "kind": "cardboard", "at": Vector2(-2.58, -4.70), "sit_on": 0.80, "yaw": -0.16}, {"glb": "cardboard-box", "kind": "cardboard", "at": Vector2(5.6, -4.8)}, {"glb": "cardboard-box", "kind": "cardboard", "at": Vector2(5.72, -4.94), "sit_on": 0.40, "yaw": -0.11}, {"glb": "cardboard-box", "kind": "cardboard", "at": Vector2(5.58, -4.68), "sit_on": 0.80, "yaw": 0.20}, {"glb": "wastebin", "kind": "plastic", "at": Vector2(2.0, 2.6)}, {"glb": "wastebin", "kind": "plastic", "at": Vector2(9.0, 4.4)}, {"glb": "water-cooler", "kind": "steel", "at": Vector2(0.6, 2.2)}, {"glb": "shredder", "kind": "plastic", "at": Vector2(-2.2, -2.0)}, {"glb": "desk-phone", "kind": "plastic", "at": Vector2(8.3, 5.4), "sit_on": 0.78}, {"glb": "turntable", "kind": "wood", "at": Vector2(-9.0, 4.4)}, {"glb": "guillotine", "kind": "steel", "at": Vector2(-6.0, -4.2)}, ], "records": [Vector2(-8.2, 5.0), Vector2(-7.5, 5.0)], "gauntlet": {"at": Vector3(-3.4, 0.0, -3.4), "yaw": PI, "stand": Vector3(-3.4, 0.0, -1.5), "shredder": Vector3(-2.2, 0.0, -2.0)}, "spawn": {"at": Vector3(1.0, 0.0, -5.2), "yaw": deg_to_rad(0.0)}, } # ================================================================ LEVEL 00 ## THE BACKROOMS. The only site whose walls are GENERATED rather than authored, which is ## both the honest test of the Floorplan spec and the right call for the fiction: this ## place is supposed to feel endless and samey, and a seeded RNG produces that better ## than a human placing slabs ever would. ## ## Layout follows the reference photographs rather than a maze algorithm — the Backrooms ## aren't corridors, they're big offset SLABS sitting in an open floor with gaps between ## them, so that's what this emits: partial wall runs along a coarse grid, plus pillars. ## A proper maze would be legible; this is deliberately not. ## ## Everything smashable here is INVISIBLE (see Dust.gd). You find it by walking into it. const BR_SEED := 0xBACC const BR_HALF := 19.0 const BR_H := 2.55 # low. lower than any real office. static func backrooms() -> Dictionary: var rng := RandomNumberGenerator.new() rng.seed = BR_SEED var walls: Array = [] # Wall runs on a 4.6 m lattice. Each line gets a few segments with gaps between, so # you can always get through somewhere but never see far. # Density matters more than it looks. The first pass put a run on every lattice line # in both axes and the result had NO sightline longer than a few metres — which kills # the entity outright, because a stalker you can never see can never be stared at. The # reference photographs are mostly open floor with occasional slabs, so: skip roughly # a third of the lines, and leave big gaps. var lines := [-13.8, -9.2, -4.6, 0.0, 4.6, 9.2, 13.8] for axis in ["x", "z"]: for at in lines: if rng.randf() < 0.34: continue # this line is simply open floor var cursor := -BR_HALF while cursor < BR_HALF - 2.0: var gap := rng.randf_range(4.5, 12.0) cursor += gap if cursor >= BR_HALF - 2.0: break var run := rng.randf_range(3.0, 7.5) var end: float = minf(cursor + run, BR_HALF) if end - cursor > 1.4: walls.append({"a": axis, "at": at, "from": cursor, "to": end}) cursor = end # freestanding pillars, because every Backrooms photo has one in the way var slabs: Array = [] for i in 16: var px := rng.randf_range(-BR_HALF + 2.0, BR_HALF - 2.0) var pz := rng.randf_range(-BR_HALF + 2.0, BR_HALF - 2.0) slabs.append({"size": Vector3(0.62, BR_H, 0.62), "at": Vector3(px, BR_H * 0.5, pz), "mat": "wall"}) # skirting-height trim run around the outer wall, the one detail the photos all share for s in [-1.0, 1.0]: slabs.append({"size": Vector3(BR_HALF * 2.0, 0.12, 0.06), "at": Vector3(0.0, 0.06, s * (BR_HALF - 0.03)), "mat": "trim", "collide": false}) slabs.append({"size": Vector3(0.06, 0.12, BR_HALF * 2.0), "at": Vector3(s * (BR_HALF - 0.03), 0.06, 0.0), "mat": "trim", "collide": false}) # a dense grid of buzzing troffers — the light IS the horror var lx: Array = [] var lz: Array = [] var v := -BR_HALF + 2.3 while v < BR_HALF: lx.append(v) lz.append(v) v += 3.05 # sparse, wrong office objects. All invisible until you dust them. var smash: Array = [] var kinds := [["office-chair", "plastic"], ["wastebin", "plastic"], ["cardboard-box", "cardboard"], ["filing-cabinet", "steel"], ["crt-monitor", "glass"], ["desk-phone", "plastic"], ["water-cooler", "steel"], ["office-plant", "wood"]] # dense enough that a single cloud finds you two or three things — the level is # a search, and a search with nothing in it is just a walk # Rejection sampling: the widest thing on this list is a 1.35 m cardboard box, so # anything landing inside 1.6 m of a spot already taken gets redrawn. Two props # spawned inside each other are a filing cabinet launched across the maze on frame # one, which in a level built on silence is not the surprise you wanted. var taken: Array = [] var tries := 0 while smash.size() < 78 and tries < 4000: tries += 1 var spot := Vector2(rng.randf_range(-BR_HALF + 2.5, BR_HALF - 2.5), rng.randf_range(-BR_HALF + 2.5, BR_HALF - 2.5)) var clear := true for t in taken: if (spot - (t as Vector2)).length() < 1.6: clear = false break if not clear: continue taken.append(spot) var k: Array = kinds[rng.randi() % kinds.size()] smash.append({ "glb": String(k[0]), "kind": String(k[1]), "at": spot, "yaw": rng.randf_range(0.0, TAU), }) return { "name": "LEVEL 0", "subtitle": "you noclipped out of reality", "invisible": true, # Dust.gd hides every smashable here "bounds": {"x0": -BR_HALF, "x1": BR_HALF, "z0": -BR_HALF, "z1": BR_HALF, "h": BR_H}, "palette": { # the whole point is that there is only one colour "floor": Color(0.52, 0.47, 0.30), "wall": Color(0.76, 0.72, 0.42), "ceiling": Color(0.80, 0.77, 0.55), "trim": Color(0.62, 0.58, 0.34), "partition": Color(0.74, 0.70, 0.40), "wood": Color(0.62, 0.55, 0.32), "accent": Color(0.70, 0.66, 0.38), }, "floor_style": "carpet", "ceiling": "tile", "walls": walls, "windows": [], # there is no outside "slabs": slabs, "lights": {"xs": lx, "zs": lz, "color": Color(1.0, 0.97, 0.80), "energy": 1.15, "style": "troffer", "key_energy": 0.9}, "env": {"bg": Color(0.30, 0.28, 0.16), "ambient": Color(0.62, 0.58, 0.36), "ambient_energy": 0.70, "exposure": 1.12}, "clusters": [], "props": [], "chairs": [], "smashables": smash, "records": [], "gauntlet": {"at": Vector3(0.0, 0.0, -2.2), "yaw": PI, "stand": Vector3(0.0, 0.0, 0.2), "shredder": Vector3(3.0, 0.0, -1.0)}, "spawn": {"at": Vector3(0.0, 0.0, 16.5), "yaw": PI}, } # ================================================================ THE GROCER ## A greengrocer, and the only site whose contents are mostly SOFT. ## ## Everything else in the game shatters; here the stock bursts. That one material change ## rewrites the level: produce squashes instead of breaking, a pyramid crushes its own ## bottom layer as it collapses, and the floor fills with juice you then slip in. The ## glass bottles behind the juice bar are the exception, and the contrast is the point — ## one shelf in the room still rewards a proper swing. ## ## Bright, clean, warm track lighting on a pale floor, because the whole level is about ## making a mess and a mess needs somewhere clean to happen. static func grocer() -> Dictionary: var stacks: Array = [] # Angled island displays. `at` is the TOP surface a pyramid piles onto; Main builds # the pyramid itself so the physics settles honestly rather than being placed. var islands := [ [Vector3(-5.4, 0.95, -2.2), "apple", 5], [Vector3(-5.4, 0.95, 1.4), "orange", 5], [Vector3(-2.0, 0.95, -2.2), "tomato", 5], [Vector3(-2.0, 0.95, 1.4), "apple", 4], [Vector3(1.4, 0.95, -2.2), "cabbage", 3], [Vector3(1.4, 0.95, 1.4), "orange", 4], [Vector3(4.8, 0.80, -0.4), "melon", 3], ] for i in islands: stacks.append({"at": i[0], "item": String(i[1]), "rows": int(i[2])}) var slabs: Array = [] # the island tables themselves: a plinth, a timber top, and a raised back edge so a # pyramid has something to fail against for i in islands: var p: Vector3 = i[0] var big: bool = String(i[1]) == "melon" var w := 2.4 if big else 1.5 var d := 1.9 if big else 1.5 var h: float = p.y slabs.append({"size": Vector3(w, h - 0.10, d), "at": Vector3(p.x, (h - 0.10) * 0.5, p.z), "mat": "trim"}) slabs.append({"size": Vector3(w + 0.14, 0.10, d + 0.14), "at": Vector3(p.x, h - 0.05, p.z), "mat": "wood"}) for s in [-1.0, 1.0]: slabs.append({"size": Vector3(w + 0.14, 0.10, 0.06), "at": Vector3(p.x, h + 0.05, p.z + s * (d * 0.5 + 0.04)), "mat": "wood"}) slabs.append({"size": Vector3(0.06, 0.10, d + 0.14), "at": Vector3(p.x + s * (w * 0.5 + 0.04), h + 0.05, p.z), "mat": "wood"}) # juice bar along the back, with shelving above it for the bottles slabs.append({"size": Vector3(7.0, 1.05, 0.75), "at": Vector3(-3.0, 0.525, -7.4), "mat": "trim"}) slabs.append({"size": Vector3(7.2, 0.09, 0.92), "at": Vector3(-3.0, 1.10, -7.4), "mat": "wood"}) for k in range(3): slabs.append({"size": Vector3(6.6, 0.06, 0.34), "at": Vector3(-3.0, 1.62 + k * 0.46, -7.72), "mat": "wood"}) # chiller run down the right wall slabs.append({"size": Vector3(0.80, 2.10, 8.0), "at": Vector3(10.4, 1.05, 1.0), "mat": "trim"}) for k in range(4): slabs.append({"size": Vector3(0.86, 0.05, 8.0), "at": Vector3(10.3, 0.55 + k * 0.46, 1.0), "mat": "ceiling"}) var smash: Array = [] # bottles on the bar shelves — the one thing in here that still wants a proper swing for k in range(3): for b in range(11): smash.append({"glb": "juice-bottle", "kind": "glass", "at": Vector2(-6.0 + b * 0.60, -7.72), "sit_on": 1.65 + k * 0.46}) for b in range(9): smash.append({"glb": "juice-bottle", "kind": "glass", "at": Vector2(-5.6 + b * 0.62, -7.4), "sit_on": 1.15}) # loose crates of stock on the floor for c in [Vector2(-8.6, 3.4), Vector2(-8.6, 4.4), Vector2(-8.0, 5.4), Vector2(7.4, -4.6), Vector2(8.2, -4.0), Vector2(7.8, 5.2)]: smash.append({"glb": "produce-crate", "kind": "wood", "at": c, "yaw": randf_range(0.0, TAU)}) # a few bananas and cabbages just lying about for b in [Vector2(-7.2, 0.4), Vector2(3.2, 4.2), Vector2(6.0, 2.0)]: smash.append({"glb": "produce-banana", "kind": "produce", "at": b}) for c in [Vector2(-9.4, -2.0), Vector2(2.4, -5.6)]: smash.append({"glb": "produce-cabbage", "kind": "produce", "at": c}) return { "name": "THE GREENGROCER", "subtitle": "everything here is soft except the bottles", "bounds": {"x0": -11.0, "x1": 11.0, "z0": -8.0, "z1": 8.0, "h": 3.30}, "palette": { "floor": Color(0.70, 0.68, 0.64), "wall": Color(0.90, 0.89, 0.85), "ceiling": Color(0.20, 0.20, 0.21), "trim": Color(0.15, 0.15, 0.16), "partition": Color(0.30, 0.44, 0.40), "wood": Color(0.52, 0.36, 0.20), "accent": Color(0.20, 0.48, 0.42), }, "floor_style": "lino", "ceiling": "flat", "walls": [], "windows": [{"a": "z", "at": 8.0, "from": -11.0, "to": 11.0, "y0": 0.35, "y1": 2.85, "blinds": false}], "lights": {"xs": [-8.0, -4.0, 0.0, 4.0, 8.0], "zs": [-6.0, -2.0, 2.0, 6.0], "color": Color(1.0, 0.94, 0.84), "energy": 2.4, "style": "strip", "key_energy": 1.8}, "sun": {"color": Color(1.0, 0.96, 0.88), "energy": 1.1, "rot": Vector3(-40, 8, 0)}, "env": {"bg": Color(0.76, 0.82, 0.88), "ambient": Color(0.60, 0.58, 0.55), "ambient_energy": 0.66, "exposure": 1.04}, "clusters": [], "chairs": [], "props": [ {"glb": "office-plant", "at": Vector3(-10.2, 0.0, 7.0)}, {"glb": "office-plant", "at": Vector3(9.6, 0.0, 7.0)}, # the bag roll. Its post stands on the floor; the station is the roll itself. {"glb": "bag-roll", "at": Vector3(-0.3, 0.0, 3.6), "yaw": PI}, ], # Brass shop scales hung down the aisles, dial at eye level and the dish at chest # height — where a real one hangs, where you look INTO the pan rather than at its # unlit underside, and where you keep walking into it. "scales": [ Vector3(-3.7, 1.58, -0.4), Vector3(-0.3, 1.58, -0.4), # the counter one hangs higher, so you can still see the bottle shelf Vector3(3.1, 1.58, -0.4), Vector3(-3.0, 1.88, -6.5), ], "stations": [ {"kind": "bags", "at": Vector3(-0.3, 1.1, 3.6), "label": "bag up the order"}, ], "slabs": slabs, "smashables": smash, "stacks": stacks, "records": [], "gauntlet": {"at": Vector3(-8.6, 0.0, -6.0), "yaw": PI, "stand": Vector3(-8.6, 0.0, -4.0), "shredder": Vector3(-6.4, 0.0, -5.4)}, "spawn": {"at": Vector3(0.0, 0.0, 6.4), "yaw": PI}, } # ================================================================ SITE 7 ## THE MONSTER ROBOT PARTY — the shop the whole game started as. ## ## The only site that is IMPORTED rather than authored. The live storefront at ## robotmonster.party/store/ is assembled procedurally from Postgres (the wowplatter ## virtual_* schema, served by recordgod), and this game's sites are data, so ## tools/import_store.py reads the real shop and emits assets/store/shop_floor.json: ## 35 racks, 278 bins carrying their real genre labels, and 21,159 records of stock. ## ## The shop's schema IS the cascade. virtual_crate.rack_id already records which rack ## holds which bin, so every crate arrives frozen and `supported_by` its rack — knock a ## rack over and the actual bins that actually sit on it actually go. None of that is ## authored; it comes off the shop floor. ## ## Re-run the importer when the real shop is rearranged and this level follows. const STORE_JSON := "res://assets/store/shop_floor.json" static func record_store() -> Dictionary: var raw := FileAccess.get_file_as_string(STORE_JSON) var data = JSON.parse_string(raw) if raw != "" else null if data == null: push_warning("[store] %s missing — run tools/import_store.py" % STORE_JSON) data = {"room": {"w": 6.66, "d": 9.51, "h": 2.89}, "smashables": [], "slabs": [], "spawn": {"at": [0.0, 3.0]}} var room: Dictionary = data.get("room", {}) var w: float = float(room.get("w", 6.66)) var dp: float = float(room.get("d", 9.51)) var h: float = float(room.get("h", 2.89)) # JSON has no Vector types — rehydrate into what Floorplan/Main expect. var smash: Array = [] for e in data.get("smashables", []): var s: Dictionary = e.duplicate() var a: Array = s["at"] s["at"] = Vector2(float(a[0]), float(a[1])) smash.append(s) var slabs: Array = [] for e in data.get("slabs", []): var sz: Array = e["size"] var at: Array = e["at"] slabs.append({"size": Vector3(float(sz[0]), float(sz[1]), float(sz[2])), "at": Vector3(float(at[0]), float(at[1]), float(at[2])), "mat": String(e.get("mat", "wood"))}) var sp: Array = data.get("spawn", {}).get("at", [0.0, 3.0]) # The interior walls between the four rooms, with a gap wherever a doorway pierces # one — the importer derives them from the shop's own portal graph. var walls: Array = [] for e in data.get("walls", []): var wl: Dictionary = {"a": String(e.get("a", "x")), "at": float(e.get("at", 0.0)), "from": float(e.get("from", 0.0)), "to": float(e.get("to", 0.0))} if e.has("door"): wl["door"] = float(e["door"]) walls.append(wl) # The street glazing goes on the entry hall's outer wall, because that is the way in. var windows: Array = [] for r in data.get("rooms", []): if not String(r.get("name", "")).begins_with("entry"): continue var at: Array = r["at"] var size: Array = r["size"] var rx0: float = float(at[0]) var rz0: float = float(at[1]) if absf(rz0 - (-dp * 0.5)) < 0.2: windows.append({"a": "z", "at": -dp * 0.5, "from": rx0 + 0.4, "to": rx0 + float(size[0]) - 0.4, "y0": 0.6, "y1": 2.3, "blinds": false}) # a light every ~3 m across whatever footprint the four rooms came out as var lx: Array = [] var lz: Array = [] var gx: float = -w * 0.5 + 1.6 while gx < w * 0.5 - 0.6: lx.append(gx) gx += 3.0 var gz: float = -dp * 0.5 + 1.6 while gz < dp * 0.5 - 0.6: lz.append(gz) gz += 3.0 return { "name": "MONSTER ROBOT PARTY", "subtitle": "your own shop. you know exactly what everything cost.", "bounds": {"x0": -w * 0.5, "x1": w * 0.5, "z0": -dp * 0.5, "z1": dp * 0.5, "h": h}, "palette": { "floor": Color(0.42, 0.31, 0.22), "wall": Color(0.93, 0.92, 0.90), "ceiling": Color(0.17, 0.16, 0.19), "trim": Color(0.19, 0.18, 0.21), "partition": Color(0.35, 0.20, 0.34), "wood": Color(0.52, 0.36, 0.20), "accent": Color(1.0, 0.36, 0.62), # the shop's own pink }, "floor_style": "timber", "ceiling": "flat", "walls": walls, "windows": windows, "lights": {"xs": lx, "zs": lz, "color": Color(1.0, 0.93, 0.86), "energy": 2.1, "style": "strip", "key_energy": 1.5}, "sun": {"color": Color(1.0, 0.95, 0.88), "energy": 0.9, "rot": Vector3(-38, 14, 0)}, "env": {"bg": Color(0.72, 0.76, 0.82), "ambient": Color(0.58, 0.55, 0.56), "ambient_energy": 0.62, "exposure": 1.02}, "clusters": [], "chairs": [], "props": [], "slabs": slabs, "smashables": smash, "stacks": [], # a few playable records loose on the counter side, for the ritual "records": [], "spawn": {"at": Vector3(float(sp[0]), 0.0, float(sp[1])), "yaw": PI}, }