destroyulator/game/dev/probe_store.gd
Monster Robot Party e588508436 LANE10: the whole shop — four rooms, stitched by its own portal graph
The hip-hop room has stock (7 racks, 47 bins, 2,865 records); the entry hall and
back room are empty. Import all four anyway: the empty rooms are the connective
tissue, and they are the only floor in the building big enough to get an office
chair up to speed.

A portal pair is one doorway seen from both sides, so the importer walks the graph
and places each room by making its portal land exactly on its neighbour's. Every
pair here sits on parallel walls with opposite facings, so each join is a pure
translation — and when one isn't, it says so rather than quietly getting it wrong.
It then recentres the union, emits the interior walls with a gap at each doorway,
and puts the spawn in the entry hall, because that is the way in.

  hiphop-room <-> entry-hall <-> shop-floor <-> back-room
  12.96 x 15.41 m - 36 racks - 325 bins - 302 real genre labels - 24,024 records

Levels.record_store() now takes the walls and rooms from the import, hangs the
street glazing on the entry hall's outer wall, and lays a light grid across
whatever footprint the rooms came out as.

probe_store.gd also checks the new facts: five doorways exist, and the spawn has
nothing in it (excluding the player, who is supposed to be standing there — that
false positive was the probe's bug, not the level's).
All gates green: smoke clean, 7/7 sites 0.00 m/s, overlap CLEAN, all probes pass.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-04 02:48:39 +10:00

120 lines
3.6 KiB
GDScript

extends SceneTree
## SITE 7 contract — the imported shop:
## - it builds, with racks AND bins
## - the cascade came off the shop's own data: racks hold bins via `supported_by`
## - smashing one rack releases exactly ITS bins, and no others
## - the real genre labels survived the import
##
## Godot --headless --path game --script dev/probe_store.gd
func _initialize() -> void:
var main: Node = (load("res://main.tscn") as PackedScene).instantiate()
get_root().add_child(main)
await process_frame
main._load_level("store")
for i in 70:
await physics_frame
var racks: Array = []
var bins: Array = []
var labelled := 0
for n in get_nodes_in_group("smashable"):
var s := n as Smashable
if s == null:
continue
if s.supports.size() > 0:
racks.append(s)
if s.freeze:
bins.append(s)
if s.has_meta("label"):
labelled += 1
print("racks holding bins: %d frozen bins: %d labelled bins: %d" % [
racks.size(), bins.size(), labelled])
# --- the four rooms, and that you can actually walk between them -------------------
var spec: Dictionary = Levels.record_store()
var walls: Array = spec.get("walls", [])
var doors := 0
for w in walls:
if (w as Dictionary).has("door"):
doors += 1
print("interior walls: %d, of which %d have a doorway" % [walls.size(), doors])
if doors < 3:
print("FAIL: the rooms are walled off from each other (need a door per portal)")
quit(1)
return
# spawn must be standing in open air, not inside a wall or a rack
var sp: Vector3 = spec["spawn"]["at"]
var space := main.get_viewport().world_3d.direct_space_state
var q := PhysicsShapeQueryParameters3D.new()
var cap := CapsuleShape3D.new()
cap.radius = 0.34
cap.height = 1.7
q.shape = cap
q.transform = Transform3D(Basis.IDENTITY, sp + Vector3(0, 0.9, 0))
# the player is already standing here — that's the point, not an obstruction
var pl = main.get("_player")
if pl != null:
q.exclude = [(pl as CollisionObject3D).get_rid()]
var blocked := space.intersect_shape(q, 4)
var names := ""
for b in blocked:
var c = b.get("collider")
if c is Node:
names += " " + String((c as Node).name)
print("spawn at %s%d bodies in the way:%s" % [sp.snappedf(0.01), blocked.size(), names])
if blocked.size() > 0:
print("FAIL: spawn is inside geometry")
quit(1)
return
if racks.is_empty() or bins.is_empty():
print("FAIL: the shop did not import (no racks holding bins)")
quit(1)
return
if labelled < 20:
print("FAIL: the real genre labels did not survive the import")
quit(1)
return
# find the rack carrying the most bins and knock it over
var best: Smashable = racks[0]
for r in racks:
if r.supports.size() > best.supports.size():
best = r
var mine: Array = []
for b in best.supports:
if is_instance_valid(b):
mine.append(b)
var others: Array = []
for b in bins:
if is_instance_valid(b) and not mine.has(b):
others.append(b)
var others_frozen_before := 0
for b in others:
if (b as RigidBody3D).freeze:
others_frozen_before += 1
print("smashing '%s' — it carries %d bins" % [best.name, mine.size()])
best.smash(Vector3(0, 1, 0), 999.0)
await physics_frame
await physics_frame
var released := 0
for b in mine:
if is_instance_valid(b) and not (b as RigidBody3D).freeze:
released += 1
var others_still := 0
for b in others:
if is_instance_valid(b) and (b as RigidBody3D).freeze:
others_still += 1
print("its bins released: %d/%d other bins still frozen: %d/%d" % [
released, mine.size(), others_still, others_frozen_before])
if released == mine.size() and others_still == others_frozen_before:
print("STORE OK")
quit(0)
else:
print("FAIL: cascade released the wrong set of bins")
quit(1)