extends SceneTree
## Gate: a bot body says what it is, on sight.
##
## Every position body was the same grey box from the same _make_marker(id), so a
## room full of bots was a room full of identical boxes and there was no way to
## tell a map-bound bot from a session-bound one, or a bot with a running brain
## from an inert placeholder. The testbed exists to be LOOKED AT; markers that
## carry no information make it useless for the thing it is for.
##
## The mapping is chosen so that what you can see is what the test is about:
##
## colour CONTAINMENT — which lifetime this bot is bound to, i.e. exactly
## what a map change is supposed to prove. Change map, the red ones
## should go and the green ones should stay.
## emission A BRAIN IS RUNNING. Matte means brain=none, a placeholder body
## nothing is driving. Glowing means something is ticking it, so a
## glowing box that never moves is a real failure you can see.
## shape THE BODY KIND. One shape today because body=position is the only
## visible body; derived rather than hardcoded so body=player does
## not arrive looking identical.
## label the declaration in words, because colour alone cannot carry an id.
##
## ⚠ body=none bots have NO NODE, by design (spec/bot-testbed.md (e2)), so they
## are not here and cannot be. They are observable only through the state they
## write — beacon's state:bot:beacon:ticks. That is a real gap in the testbed and
## it is not this gate's job to hide it.
var ok := true
var _ran := false
func _p(c: bool, m: String) -> void:
print(("ok " if c else "FAIL: ") + m)
if not c:
ok = false
func _process(_delta: float) -> bool:
if _ran:
return true
_ran = true
_run()
print("GATE bot_marker: %s" % ("PASS" if ok else "FAIL"))
if not ok:
quit(1)
return true
func _mesh_of(marker: Node) -> MeshInstance3D:
for c in marker.get_children():
if c is MeshInstance3D:
return c as MeshInstance3D
return null
func _label_of(marker: Node) -> Label3D:
for c in marker.get_children():
if c is Label3D:
return c as Label3D
return null
func _mat_of(marker: Node) -> StandardMaterial3D:
var m := _mesh_of(marker)
if m == null:
return null
var mat: Variant = m.material_override
if mat == null and m.mesh != null:
mat = m.mesh.surface_get_material(0)
return mat as StandardMaterial3D
func _run() -> void:
var br: Node = root.get_node_or_null("BotRegistry")
_p(br != null, "BotRegistry autoload present")
if br == null:
return
# ⛔ ARITY IS CHECKED BEFORE CALLING. has_method() is true for the old
# one-argument _make_marker, and calling it with four raises a SCRIPT ERROR
# that aborts _run() where it stands — which on this gate's first run meant no
# FAIL was recorded and it printed PASS against a registry that had none of
# this. Same way gate_headless_map failed open. A gate that cannot fail is
# worse than no gate.
var argc := -1
for m: Dictionary in br.get_method_list():
if String(m.get("name", "")) == "_make_marker":
argc = (m.get("args", []) as Array).size()
break
_p(argc >= 4, "_make_marker takes the declaration (id, bindings, body, brain) — got %d args" % argc)
if argc < 4:
return
# The new signature carries the declaration. Called with it, a marker must
# come back differentiated; the old one took only an id and could not.
var m_map: Node = br._make_marker("b_map", ["map"], "position", "script_file")
var m_room: Node = br._make_marker("b_room", ["room"], "position", "none")
var m_sess: Node = br._make_marker("b_sess", ["session"], "position", "none")
for pair in [[m_map, "map"], [m_room, "room"], [m_sess, "session"]]:
_p(pair[0] != null, "%s-bound marker builds" % pair[1])
if m_map == null or m_room == null or m_sess == null:
return
# ---- colour carries containment ----
var c_map := _mat_of(m_map)
var c_room := _mat_of(m_room)
var c_sess := _mat_of(m_sess)
_p(c_map != null and c_room != null and c_sess != null,
"every marker carries a material (colour is settable at all)")
if c_map == null or c_room == null or c_sess == null:
return
_p(c_map.albedo_color != c_room.albedo_color,
"map and room bindings are different colours")
_p(c_room.albedo_color != c_sess.albedo_color,
"room and session bindings are different colours")
_p(c_map.albedo_color != c_sess.albedo_color,
"map and session bindings are different colours")
# ---- emission says a brain is running ----
_p(c_map.emission_enabled, "a bot WITH a brain emits light")
_p(not c_room.emission_enabled, "a bot with brain=none does not")
# SELF-CHECK: emission must follow the brain and not the binding, or the two
# assertions above would both pass on a marker that simply lit map-bound bots.
var m_room_brain: Node = br._make_marker("b_room2", ["room"], "position", "script_file")
var c_room_brain := _mat_of(m_room_brain)
_p(c_room_brain != null and c_room_brain.emission_enabled,
"self-check: a ROOM-bound bot with a brain also emits, so emission tracks the brain")
if c_room_brain != null:
_p(c_room_brain.albedo_color == c_room.albedo_color,
"self-check: and its colour still tracks the binding, not the brain")
# ---- shape is derived from the body ----
var mesh_map := _mesh_of(m_map)
_p(mesh_map != null and mesh_map.mesh != null, "the body has a mesh")
# ---- the label spells out the declaration ----
var lab := _label_of(m_map)
_p(lab != null, "the marker carries a Label3D")
if lab != null:
var t: String = lab.text
_p(t.contains("b_map"), "the label names the bot (%s)" % t)
_p(t.contains("map"), "and its binding")
_p(t.contains("position"), "and its body")
_p(t.contains("script_file"), "and its brain")
for n in [m_map, m_room, m_sess, m_room_brain]:
if n != null:
n.free()