destroyulator/game/dev/probe_roomba.gd
Monster Robot Party 32b556ff02 LANE9: the Roomba — company property that eats your evidence
A robot vacuum (scripts/Roomba.gd, kind=steel) docks near spawn, lifeless, and
deploys the moment the first thing in the site breaks: the cleaner arrives because
there is mess. It patrols, sniffs out debris within 5 m, and eats it — each shard
shrinks into the intake, beeps smugly, and takes 2 points off your score. Dent it
(crowbar and up — the matrix already knew) and it panics and flees at 2.7x cruise.
A sledgehammer genuinely kills it, at which point the payslip bills you for a
destroyed Roomba, because it was company property and you were seen.

Voice is three synthesized chirps, no assets. Not spawned in LEVEL 0 — nothing
cute lives in the Backrooms. Docked = genuinely asleep, so probe_levels stays 0.00.

dev/probe_roomba.gd measures the contract: docked at spawn, wakes on first smash,
shard dropped in its path is eaten within 5 s and the score goes down. ROOMBA OK.
probe_levels 6x 0.00 m/s - probe_overlap CLEAN.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 00:00:58 +10:00

76 lines
2.4 KiB
GDScript

extends SceneTree
## The Roomba contract, measured:
## 1. docked at spawn — contributes nothing to residual motion (probe_levels covers
## the number; here we assert it is genuinely asleep and un-awake)
## 2. wakes on the first smash in the site
## 3. eats debris: a shard dropped in front of it is gone within a few seconds,
## and the score went DOWN for it
##
## Godot --headless --path game --script dev/probe_roomba.gd
func _initialize() -> void:
var main: Node = (load("res://main.tscn") as PackedScene).instantiate()
get_root().add_child(main)
await process_frame
for i in 70:
await physics_frame
var roombas := get_nodes_in_group("roomba")
if roombas.size() != 1:
print("FAIL: expected 1 roomba in scranton, got %d" % roombas.size())
quit(1)
return
var r: Roomba = roombas[0]
print("docked: awake=%s sleeping=%s at %s" % [r._awake, r.sleeping, r.global_position.snappedf(0.01)])
if r._awake:
print("FAIL: roomba should be docked until the first smash")
quit(1)
return
# smash the nearest smashable that isn't the roomba -> it must wake
var victim: Smashable = null
var best := INF
for n in get_nodes_in_group("smashable"):
if n is Roomba or not (n is Smashable) or (n as RigidBody3D).freeze:
continue
var d: float = (n.global_position - r.global_position).length_squared()
if d < best:
best = d
victim = n
victim.smash(Vector3(0, 2, 0), 999.0)
await physics_frame
if not r._awake:
print("FAIL: roomba did not wake on the first smash")
quit(1)
return
print("woke on first smash: OK")
# drop a fresh piece of debris directly in its path and count down
var score_before: int = main.get("_score")
var shard := RigidBody3D.new()
shard.add_to_group("debris")
var col := CollisionShape3D.new()
var box := BoxShape3D.new()
box.size = Vector3(0.06, 0.06, 0.06)
col.shape = box
shard.add_child(col)
main.get("_world").add_child(shard)
shard.global_position = r.global_position + Vector3(0.05, 0.12, 0.0)
var eaten := false
for i in 300: # up to 5 s
await physics_frame
if not is_instance_valid(shard) or shard.is_queued_for_deletion():
eaten = true
break
var score_after: int = main.get("_score")
print("shard eaten=%s score %d -> %d roomba _eaten=%d" % [
eaten, score_before, score_after, r._eaten])
if eaten and r._eaten >= 1:
print("ROOMBA OK")
quit(0)
else:
print("FAIL: shard survived the roomba for 5 s")
quit(1)