THE ENTITY (Entity.gd) borrows from three places and each borrowing does a job:
SLENDERMAN it does not move while you look at it, and has closed the distance every
time you look back. Looking is ALSO what builds the static — so the safe
option (keep it in view) kills you slowly and the fast option (run) lets
it catch up. That's the trap, and it's why Slender worked.
XENOMORPH it hunts SOUND, and the thing you came here to do is smash furniture.
Every break and every puff of powder drops a marker it walks toward.
Playing well is what feeds it. A loud enough noise overrides the freeze,
because staring is a valid answer to a stalker and not a valid answer to
something that just heard a sledgehammer hit a filing cabinet.
BLAIR WITCH the level wraps: walk far enough and you come out of the opposite wall
into the same room, because every room here IS the same room. A wrap is
also when it repositions, so "I've been here before" and "it's already
here" land in the same second.
It can't be fought. Getting caught doesn't kill you — it costs a lungful, a spike of
rage, and the knowledge that it can do that whenever it likes.
Deliberately unresolved design: 2.5 m, faceless, arms past the knees, cranium swept
backwards. Slenderman from the front, something else in profile. A silhouette your brain
finishes beats a monster it recognises. Blair Witch stick totems hang in the maze.
THE TAPE (Dread.gd) — one shader: grain, scanlines, chroma split, a wandering tracking
tear, an edge that breathes when it's near but unseen, and Slender interference driven
straight off the stare. The static IS the read-out; there's no meter for it.
TWO BUGS WORTH THE COMMIT MESSAGE
1. `seen_now` was a pure view-cone test with NO line of sight, so it built static
through walls — and in a maze that's most of the time.
2. Fixing that exposed the real one: the generated maze was so dense there was no
sightline longer than a few metres ANYWHERE, which kills the entity outright, since
a stalker you can never see can never be stared at. Thinned the generator — skip a
third of the lattice lines, much bigger gaps — which also matches the reference
photographs far better. They're mostly open floor with occasional slabs, not
corridors.
dev/probe_horror.gd verifies all of it headlessly, including hunting for a clear
sightline first (otherwise the Slender test silently tests nothing).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
561 lines
28 KiB
GDScript
561 lines
28 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.
|
|
# 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
|
|
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},
|
|
}
|