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>
50 lines
1.5 KiB
GDScript
50 lines
1.5 KiB
GDScript
extends SceneTree
|
|
|
|
## Build every level in Levels.ORDER and report whether it's structurally sound:
|
|
## prop count, missing assets, and — the one that matters — whether anything is spawned
|
|
## inside anything else. A level that fires furniture across the room on frame one is
|
|
## broken whatever it looks like in a screenshot.
|
|
##
|
|
## Godot --headless --path game --script dev/probe_levels.gd
|
|
|
|
func _initialize() -> void:
|
|
var main: Node = (load("res://main.tscn") as PackedScene).instantiate()
|
|
get_root().add_child(main)
|
|
await process_frame
|
|
|
|
for id in Levels.ORDER:
|
|
main._load_level(id)
|
|
for i in 8:
|
|
await physics_frame
|
|
var plan = main.get("_plan")
|
|
var bodies := get_nodes_in_group("smashable").size()
|
|
var statics := _count_static(plan)
|
|
# let it run a full second and see if anything is still moving
|
|
for i in 60:
|
|
await physics_frame
|
|
var motion := 0.0
|
|
var worst := ""
|
|
var worst_v := 0.0
|
|
for n in get_nodes_in_group("smashable"):
|
|
var b := n as RigidBody3D
|
|
if b == null or b.freeze:
|
|
continue
|
|
var v := b.linear_velocity.length()
|
|
motion += v
|
|
if v > worst_v:
|
|
worst_v = v
|
|
worst = "%s at %s" % [b.name, b.global_position.snappedf(0.1)]
|
|
print("\n=== %-14s %s" % [id, plan.level_name()])
|
|
print(" smashables=%-4d static bodies=%-4d" % [bodies, statics])
|
|
print(" residual motion after 1s = %.2f m/s worst: %s" % [
|
|
motion, worst if worst != "" else "-"])
|
|
quit()
|
|
|
|
func _count_static(n: Node) -> int:
|
|
var c := 0
|
|
if n is StaticBody3D:
|
|
c += 1
|
|
for ch in n.get_children():
|
|
c += _count_static(ch)
|
|
return c
|