extends SceneTree
## GATE — the Object panel + object.set_prop end to end, on a real map_flat.
## P1 the panel is in the dock, and says so when nothing is selected;
## P2 selecting an object builds one widget per catalogue property;
## P3 a commit writes ONE object.set_prop record, labelled "Set <Label>" in History,
## and the node changes through the FOLD (not a local poke);
## P4 an unchanged value writes nothing;
## P5 a slider DRAG previews without writing and writes once when it ends; a track
## click / wheel step writes once;
## P6 one Ctrl+Z (undo_gesture) takes the change back;
## P7 every kind: colour (block tint), bool, string (teleporter label → its sign),
## vec3 (teleporter destination);
## P8 deselecting empties the panel.
##
## Run: CI=true HYPERTPS_DAEMON_TCP=127.0.0.1:1 "$HYPER_TPS_GODOT" --headless --path . --script res://scenes/test/gate_object_panel.gd
var ok := true
var checks := 0
var _frame := 0
var _map: Node
var _persist: Node
var _dock: Node
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 _process(_d: float) -> bool:
_frame += 1
if _frame == 1:
_dock = root.get_node("/root/BuildDock")
_map = (load("res://scenes/maps/map_flat.tscn") as PackedScene).instantiate()
root.add_child(_map)
return false
if _frame < 30:
return false
_run()
print("\n%d checks — %s" % [checks, "PASS" if ok else "FAIL"])
quit(0 if ok else 1)
return true
func _set_props() -> int:
var n := 0
for r: Dictionary in _persist.records():
if String(r.get("kind", "")) == "object.set_prop":
n += 1
return n
func _panel() -> Node:
return _map.get("_object_panel")
func _run() -> void:
_persist = _map.get("_persist")
var panel: Node = _panel()
# ---- P1 ----
_p(_dock._panels.has("object"), "P1 the Object panel is registered in the dock")
_p(panel != null, "P1 the map holds it")
if panel == null:
return
panel.refresh()
_p(String(panel._title.text).begins_with("Select an object"), "P1 empty selection says so", panel._title.text)
# ---- P2 ----
var at := Transform3D(Basis(), Vector3(3, 2, 3))
var light: String = _map.create_object("light.point", at)
_p(light != "", "P2 a light is created", light)
_map.select_object(light)
panel.refresh()
_p(String(panel._title.text).begins_with("light.point"), "P2 the panel shows the selected light", panel._title.text)
var ids: Array = panel._widgets.keys()
_p(ids == ["color", "energy", "range", "shadows"], "P2 one widget per catalogue prop", ids)
var node := _map.object_node(light) as OmniLight3D
# ---- P3 ----
var before := _set_props()
_p(panel.commit("energy", 6.0), "P3 commit returns true")
_p(_set_props() == before + 1, "P3 exactly one object.set_prop record", _set_props() - before)
_p(is_equal_approx(node.light_energy, 6.0), "P3 the light changed (via the fold)", node.light_energy)
var newest: Dictionary = _persist.gestures()[0]
_p(String(newest["label"]) == "Set Brightness", "P3 History names it", newest["label"])
_p(int(newest["count"]) == 1, "P3 one record in the gesture", newest["count"])
# ---- P4 ----
before = _set_props()
_p(not panel.commit("energy", 6.0), "P4 an unchanged value is not a write")
_p(_set_props() == before, "P4 and no record", _set_props() - before)
# ---- P5 ----
panel.refresh()
var sl := panel.widget("range") as HSlider
before = _set_props()
panel._on_drag_started("range")
sl.value = 20.0
sl.value = 30.0
_p(_set_props() == before, "P5 mid-drag writes nothing", _set_props() - before)
_p(is_equal_approx(node.omni_range, 30.0), "P5 but previews on the node", node.omni_range)
panel._on_drag_ended(true, "range")
_p(_set_props() == before + 1, "P5 the drag's end writes once", _set_props() - before)
_p(is_equal_approx(float(_map.object_props(light)["range"]), 30.0), "P5 with the final value",
_map.object_props(light)["range"])
before = _set_props()
sl.value = 40.0 # a track click / wheel step: not dragging
_p(_set_props() == before + 1, "P5 a click on the track writes once", _set_props() - before)
# ---- P6 ----
_persist.undo_gesture(_persist.newest_live_gesture())
_persist.refold()
_p(is_equal_approx(node.omni_range, 30.0), "P6 one undo takes the last change back", node.omni_range)
_persist.undo_gesture(_persist.newest_live_gesture())
_persist.refold()
_p(is_equal_approx(node.omni_range, 8.0), "P6 and the one before, to the default", node.omni_range)
# ---- P7 ----
_p(panel.commit("shadows", false), "P7 bool commit")
_p(not node.shadow_enabled, "P7 shadows off", node.shadow_enabled)
_p(panel.commit("color", [0.2, 0.5, 1.0]), "P7 colour commit")
_p(node.light_color.is_equal_approx(Color(0.2, 0.5, 1.0)), "P7 light colour", node.light_color)
var block: String = _map.create_object("block.bevelled", Transform3D(Basis(), Vector3(20, 5, 0)))
_map.select_object(block)
panel.refresh()
_p(panel._widgets.keys() == ["tint"], "P7 the block shows its colour only", panel._widgets.keys())
panel.commit("tint", [0.9, 0.1, 0.1])
var mi := _map.object_node(block).get_node("Mesh") as MeshInstance3D
_p((mi.material_override as StandardMaterial3D).albedo_color.is_equal_approx(Color(0.9, 0.1, 0.1)),
"P7 block tinted", (mi.material_override as StandardMaterial3D).albedo_color)
var tp: String = _map.create_object("teleporter", Transform3D(Basis(), Vector3(0, 0, 30)))
_map.select_object(tp)
panel.refresh()
var le := panel.widget("label") as LineEdit
le.text = "Roof"
le.text_submitted.emit("Roof")
var tnode: Node = _map.object_node(tp)
_p(String((tnode.get_node("Label3D") as Label3D).text) == "Roof", "P7 the label reaches the sign",
(tnode.get_node("Label3D") as Label3D).text)
_p(String(_persist.gestures()[0]["label"]) == "Set Label", "P7 History: Set Label", _persist.gestures()[0]["label"])
var boxes: Array = panel.widget("destination")
(boxes[1] as SpinBox).value = 12.5
_p((tnode.get("destination") as Vector3).is_equal_approx(Vector3(0, 12.5, 0)), "P7 destination from the number boxes",
tnode.get("destination"))
_p(panel._widgets["destination"].has("here"), "P7 vec3 has a Use my position button")
# ---- P9: a model's panel — name / collision / scale, never the hash ----
var md: String = _map.create_object("model", Transform3D(Basis(), Vector3(0, 0, -20)))
_map.select_object(md)
panel.refresh()
_p(panel._widgets.keys() == ["name", "collision", "scale"], "P9 model: name, collision, scale (asset hidden)",
panel._widgets.keys())
var ob := panel.widget("collision") as OptionButton
_p(ob != null and ob.item_count == 6 and ob.get_item_text(ob.selected) == "Automatic",
"P9 collision is a dropdown, showing the default", ob.get_item_text(ob.selected) if ob != null else "?")
ob.select(5)
ob.item_selected.emit(5)
_p(String(_map.object_props(md)["collision"]) == "none", "P9 choosing None writes collision=none",
_map.object_props(md)["collision"])
_p(String(_persist.gestures()[0]["label"]) == "Set Collision", "P9 History: Set Collision")
_p((panel.widget("scale") as HSlider).exp_edit, "P9 scale slider is exponential (0.01–100)")
# ---- P8 ----
_map.select_object("")
panel.refresh()
_p(String(panel._title.text).begins_with("Select an object") and panel._widgets.is_empty(),
"P8 deselecting empties the panel", panel._title.text)