"stuff is invisible have to release dust or gas or something to coat items to see them to destroy them? and you inhale some of it too" THE SITE (Levels.backrooms) Mono-yellow, 2.55 m ceiling, damp carpet, a dense grid of buzzing troffers, no windows and no exit. THE WALLS ARE GENERATED — this is the site that proves the Floorplan spec can be emitted rather than authored: a seeded RNG lays partial wall runs on a 4.6 m lattice with random gaps, plus 22 freestanding pillars. It follows the reference photos rather than a maze algorithm on purpose — the Backrooms aren't corridors, they're big offset slabs in an open floor, and a proper maze would be legible. Legible is wrong. THE MECHANIC (Dust.gd) — it argues with itself, which is the whole point. Everything smashable here is invisible but SOLID; you find things by walking into them. RMB with the extinguisher throws a cloud of powder. Anything it touches is coated, glows faintly out of all that yellow for 7 seconds, then settles back into nothing. And you breathe it. Every cloud puts a little in your lungs, and the haze that builds is on YOUR side of the glass. So: spray more to see individual objects, and see the room less. Past 72% you cough — camera jolt, screen bloom, and a rage tick, because of course it does. Intended rhythm is spray -> switch to something heavy -> break it before the coating settles -> spray again, coughing. Pairs with TOTAL DESTRUCTION, since the level IS a search. Tuning that mattered: the first cloud was a wall of big blocky quads that hid the very thing it was revealing (150 finer, shorter-lived particles now), and a coated object has to POP out of a room that is entirely one colour, so the coat is near-white and faintly self-lit rather than a subtle tint. 78 objects, not 34 — a search with nothing in it is just a walk. Bug found while testing: `x as Smashable` on a freed object is itself an error in Godot 4, so the validity check has to come BEFORE the cast — destroying anything mid-level was spewing "trying to cast a freed object" every frame. dev/probe_backrooms.gd verifies the whole loop headlessly, including that the mechanic does NOT leak into the other four sites when you leave. All five sites still settle to 0.00 m/s. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
554 lines
27 KiB
GDScript
554 lines
27 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", "backrooms"]
|
|
|
|
static func get_level(id: String) -> Dictionary:
|
|
match id:
|
|
"pawnee": return pawnee()
|
|
"house": return house()
|
|
"basement": return basement()
|
|
"backrooms": return backrooms()
|
|
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)},
|
|
{"glb": "guillotine", "kind": "steel", "at": Vector2(4.5, 1.2)},
|
|
{"glb": "stapler", "kind": "steel", "at": Vector2(-4.6, -2.9), "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": [
|
|
{"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": [],
|
|
"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.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},
|
|
{"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)},
|
|
{"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)},
|
|
{"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.
|
|
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:
|
|
var cursor := -BR_HALF
|
|
while cursor < BR_HALF - 2.0:
|
|
var gap := rng.randf_range(1.6, 6.5)
|
|
cursor += gap
|
|
if cursor >= BR_HALF - 2.0:
|
|
break
|
|
var run := rng.randf_range(3.2, 9.0)
|
|
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 22:
|
|
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
|
|
for i in 78:
|
|
var k: Array = kinds[rng.randi() % kinds.size()]
|
|
smash.append({
|
|
"glb": String(k[0]), "kind": String(k[1]),
|
|
"at": Vector2(rng.randf_range(-BR_HALF + 2.5, BR_HALF - 2.5),
|
|
rng.randf_range(-BR_HALF + 2.5, BR_HALF - 2.5)),
|
|
"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},
|
|
}
|