extends SceneTree
## GATE — the Bot editor tab (user, 2026-10-04: "level editor / bot editor tabs").
## T1 the top bar carries Level editor / Bot editor tabs; Level is the default
## T2 the Bot editor tab switches the workspace: the Bots panel shows, the level
## panels (Tools, Tool settings) hide, the shared ones (Object, Outliner) stay;
## the bar's level-only parts (snap, Brush menu) hide
## T3 "Place bot" places a bot with the chosen brain + body as ONE gesture, selects
## it, and the panel lists it; selecting a list row selects that bot
## T4 in the Bot editor brush keys do nothing (N = new box starts no operator)
## T5 back to the Level editor restores the level panels and the box key
##
## Run: CI=true HYPERTPS_DAEMON_TCP=127.0.0.1:1 "$HYPER_TPS_GODOT" --headless --path . --script res://scenes/test/gate_bot_editor.gd
var ok := true
var checks := 0
var _frame := 0
var _map: Node
var _dock: Node
var _step := 0
var _wait := 0
var _dot := ""
func _p(c: bool, m: String, got: Variant = "") -> void:
checks += 1
print(("ok " if c else "FAIL: ") + m + ("" if str(got) == "" else " (" + str(got) + ")"))
if not c:
ok = false
func _key(code: int) -> InputEventKey:
var e := InputEventKey.new()
e.physical_keycode = code
e.keycode = code
e.pressed = true
return e
func _shown(id: String) -> bool:
var rec: Dictionary = _dock._panels.get(id, {})
if rec.is_empty():
return false
return ((rec["section"] as Control).get_parent() as Control).visible
func _process(_d: float) -> bool:
_frame += 1
if _frame == 1:
_dock = root.get_node("/root/BuildDock")
_dock.set_workspace("level") # pin; restored at the end
_map = (load("res://scenes/maps/map_flat.tscn") as PackedScene).instantiate()
root.add_child(_map)
return false
if _frame < 30:
return false
if _wait > 0:
_wait -= 1
return false
_step += 1
match _step:
1: _t1_t2()
2: _t3()
3: _t3_list()
4: _t4_t5()
_:
_dock.set_workspace("level")
print("\n%d checks — %s" % [checks, "PASS" if ok else "FAIL"])
quit(0 if ok else 1)
return true
_wait = 3
return false
func _t1_t2() -> void:
root.get_node("/root/GameMode").set_mode("build")
var top: Node = _map.get("_top_bar")
_p(top != null and top.workspace_tab("level") != null and top.workspace_tab("bot") != null, "T1 the top bar has both tabs")
_p(_dock.workspace() == "level" and top.workspace_tab("level").button_pressed, "T1 Level editor is the default")
_p(_shown("tools") and not _shown("bots"), "T1 level panels show, the Bots panel does not")
top.workspace_tab("bot").emit_signal("pressed")
_p(_dock.workspace() == "bot", "T2 the Bot editor tab switches the workspace", _dock.workspace())
_p(_shown("bots"), "T2 the Bots panel shows")
_p(not _shown("tools") and not _shown("toolsettings"), "T2 the level panels hide")
_p(_shown("object") and _shown("outliner") and _shown("topbar"), "T2 the shared panels stay")
_p(not (top.snap_button() as Control).visible, "T2 the bar's level-only controls hide")
var brush_hidden := false
var bar: MenuBar = top.get("_bar")
for i in bar.get_menu_count():
if bar.get_menu_title(i) == "Object": # Blender's Object menu (was "Brush")
brush_hidden = bar.is_menu_hidden(i)
_p(brush_hidden, "T2 and the Object menu")
_p(top.workspace_tab("bot").button_pressed and not top.workspace_tab("level").button_pressed, "T2 the tab shows which is active")
func _t3() -> void:
var panel: Node = _map.get("_bot_panel")
panel.get("_brain").select(BrainCatalogue.ids().find("patrol"))
panel.get("_body").select(PrefabCatalogue.BOT_BODIES.find("hopper"))
_dot = panel.place_selected()
_p(_dot != "", "T3 Place bot placed one", _dot)
var props: Dictionary = _map.object_props(_dot)
_p(String(props.get("brain", "")) == "patrol" and String(props.get("body", "")) == "hopper",
"T3 with the chosen brain and body", props)
_p(String(_map.get("_sel_object")) == _dot, "T3 and it is selected (the Object panel edits it)")
var g: Dictionary = _map.get("_persist").gestures()[0]
_p(String(g["label"]) == "Add bot" and int(g["count"]) == 2, "T3 as one gesture", "%s / %d" % [g["label"], int(g["count"])])
func _t3_list() -> void:
var panel: Node = _map.get("_bot_panel")
panel.refresh()
_p(panel.rows().has(_dot), "T3 the panel lists it", panel.rows())
var other: String = _map.place_bot("pathwalker", "crawler")
panel.refresh()
var lst: ItemList = panel.get("_list")
lst.deselect_all()
lst.select(panel.rows().find(_dot))
panel._apply_list_selection()
_p(String(_map.get("_sel_object")) == _dot, "T3 selecting its row selects that bot", _map.get("_sel_object"))
_p(other != "", "T3 (a second bot for the list)")
## A level-only key that needs NO aim (headless gates aim at nothing, so "new box"
## would prove nothing): 3 = face select mode. Ignored in the Bot editor, and — the
## control — honoured in the Level editor, so the check can fail for the right reason.
func _t4_t5() -> void:
var before := String(_map.get("_sel_mode"))
var want := "face" if before != "face" else "brush"
var k := _key(KEY_3 if want == "face" else KEY_4)
_map._unhandled_key_input(k)
_p(String(_map.get("_sel_mode")) == before, "T4 in the Bot editor a brush key does nothing", _map.get("_sel_mode"))
_dock.set_workspace("level")
_p(_shown("tools") and not _shown("bots"), "T5 the Level editor restores its panels")
_p(((_map.get("_top_bar")).snap_button() as Control).visible, "T5 and the bar's level controls")
_map._unhandled_key_input(k)
_p(String(_map.get("_sel_mode")) == want, "T5 and the same key works there (control)", _map.get("_sel_mode"))