destroyulator/game/scripts/Levels.gd
Monster Robot Party 4bcd7b49d9 LANE7: four workplaces, built from data
Office.gd is gone. Floorplan.gd consumes a spec Dictionary — bounds, palette, walls
with doors and glazing styles, window runs with mullions and blinds, ceiling style,
light grid and style, slabs, static props, desk clusters, chairs, level-specific
smashables and spawn — and Levels.gd holds four of them.

Making this data rather than four subclasses means a site is authorable in minutes,
levels can be diffed, and a real generator can later emit the same structure (a BSP
split of the footprint -> rooms -> doors on shared walls -> fittings by room type).
That was the actual answer to "can we use this git to generate plans": the repo John
found is OpenSCAD SVG->STL for 3D printing, with no generation, no plan parsing, no
room polygons and GPL-3.0, so it can't help — but Office.gd was already most of a
parametric plan builder, and lifting its numbers out is the real path.

The four sites are deliberately not reskins; they differ in the three things a player
actually reads — palette, light, and what the walls are made of:

  SCRANTON      magnolia, grey carpet, drop ceiling, fluorescent troffers, daylight
                down one glazed wall. The baseline.
  PAWNEE        civic beige and blue-grey, low partitions everywhere, a public
                counter, pinboards. Municipal and over-partitioned.
  THE INCUBATOR timber floor, white walls, 3 m ceiling, PENDANT lights, glass wall
                onto a pool. Nobody has an office; they work at a dining table.
  SUB-LEVEL 4   concrete, NO windows at all, exposed services, bare strip lights, a
                wall of server racks. The light is green and everything is junk.

L cycles sites in-game; _load_level tears down the shell, rebuilds, re-registers task
stations and re-arms the shift.

tools/gen_level_props.py adds ten more procedural props. The filing cabinet is the
important one: it exports as a CARCASS plus a separate DRAWER, each with its own
floor-centre origin, so the Gauntlet can pull a drawer out as its own rigid body and
spill the files. Also file folder, desk phone, guillotine, shredder, server rack,
sofa, wastebin, stapler.

dev/probe_levels.gd builds every level and reports residual motion after a full
second. All four read 0.00 m/s.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-31 17:58:41 +10:00

429 lines
22 KiB
GDScript

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"]
static func get_level(id: String) -> Dictionary:
match id:
"pawnee": return pawnee()
"house": return house()
"basement": return basement()
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)},
{"glb": "cardboard-box", "kind": "cardboard", "at": Vector2(1.6, 5.4)},
{"glb": "cardboard-box", "kind": "cardboard", "at": Vector2(2.1, 4.7)},
{"glb": "cardboard-box", "kind": "cardboard", "at": Vector2(6.4, -3.3)},
{"glb": "cardboard-box", "kind": "cardboard", "at": Vector2(7.3, -3.8)},
{"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)},
],
"records": [Vector2(6.2, -6.6), Vector2(7.0, -6.9), Vector2(7.8, -6.6)],
"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": [
{"glb": "filing-cabinet", "kind": "steel", "at": Vector2(5.4, -3.2)},
{"glb": "filing-cabinet", "kind": "steel", "at": Vector2(6.1, -3.2)},
{"glb": "filing-cabinet", "kind": "steel", "at": Vector2(6.8, -3.2)},
{"glb": "filing-cabinet", "kind": "steel", "at": Vector2(7.5, -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(9.2, -4.8)},
{"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(-7.4, -5.4), "sit_on": 0.78},
{"glb": "crt-monitor", "kind": "glass", "at": Vector2(-7.6, -5.2), "sit_on": 0.78},
],
"records": [],
"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.9, 1.5)},
{"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},
],
"records": [Vector2(-8.4, -7.0), Vector2(-7.6, -7.0)],
"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)},
{"glb": "cardboard-box", "kind": "cardboard", "at": Vector2(-2.6, -4.8)},
{"glb": "cardboard-box", "kind": "cardboard", "at": Vector2(-2.0, -5.3)},
{"glb": "cardboard-box", "kind": "cardboard", "at": Vector2(-1.4, -4.6)},
{"glb": "cardboard-box", "kind": "cardboard", "at": Vector2(5.6, -4.8)},
{"glb": "cardboard-box", "kind": "cardboard", "at": Vector2(6.3, -5.2)},
{"glb": "cardboard-box", "kind": "cardboard", "at": Vector2(7.0, -4.6)},
{"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)},
],
"records": [Vector2(-8.2, 5.0), Vector2(-7.5, 5.0)],
"spawn": {"at": Vector3(1.0, 0.0, -5.2), "yaw": deg_to_rad(0.0)},
}