extends Node
## HEADLESS GATE — LIVE CUTS through the REAL journal path (docs/cutting-and-operations.md
## §9a): persist.record("op.*") -> GameState -> LevelPersistence._to_record -> ShapeFold
## (cutter pieces) + OpFold (brush.cut, folded last) -> DCLevel.
##
## What "live" has to mean, asserted by RAY through the level (solid = a hit):
## a cut opens a door in a box shape, and the cutter itself is not solid;
## moving the CUTTER moves the door; resizing the TARGET keeps the door;
## op.set changes subtract -> intersect; undo of the op gives the whole wall back
## and the cutter shape becomes solid again; a second peer folds the same pieces.
##
## RUN AS A SCENE (level_persistence.gd names GameState at compile time):
## "$HYPER_TPS_GODOT" --headless res://scenes/test/gate_op_journal.tscn
var fails := 0
var checks := 0
var level: Node3D = null
func _ok(what: String, cond: bool, note: String = "") -> void:
checks += 1
if not cond:
fails += 1
print(" %-64s %s %s" % [what, "OK" if cond else "**FAIL**", note])
func _ready() -> void:
await get_tree().process_frame
_run()
if checks < 16:
print(" **FAIL** only %d checks ran -- setup died before the assertions" % checks)
fails += 1
print("\nGATE op_journal: %s (%d checks, %d failed)" % ["PASS" if fails == 0 else "FAIL", checks, fails])
get_tree().quit(0 if fails == 0 else 1)
## Solid at (x, y) through the wall: a ray along -z from in front of it.
func _solid(lv: Node, x: float, y: float, z0 := 6.0, z1 := -6.0) -> bool:
return not (lv.raycast(Vector3(x, y, z0), Vector3(x, y, z1)) as Dictionary).is_empty()
## Does some live solid piece of group `g` contain `p` (n·p <= d for every plane)?
func _wall_contains(lv: Node, g: String, p: Vector3) -> bool:
for id: int in (lv.get_group_brush_ids(g) as PackedInt32Array):
var b: Dictionary = lv.get_brush(id)
if bool(b.get("cutter", false)) or bool(b.get("clearance", false)):
continue
var pl: PackedFloat32Array = b.get("planes", PackedFloat32Array())
var inside := true
for k in range(0, pl.size(), 4):
if Vector3(pl[k], pl[k + 1], pl[k + 2]).dot(p) > pl[k + 3] + 1e-4:
inside = false
break
if inside:
return true
return false
func _live_solid(lv: Node, g: String) -> int:
var n := 0
for id: int in (lv.get_group_brush_ids(g) as PackedInt32Array):
var b: Dictionary = lv.get_brush(id)
if not bool(b.get("clearance", false)) and not bool(b.get("cutter", false)):
n += 1
return n
func _keys(lv: Node) -> Array:
var out: Array = []
for id: int in (lv.get_live_brush_ids() as PackedInt32Array):
out.append(String(lv.get_brush_key(id)))
out.sort()
return out
func _box(persist: Node, origin: Vector3, size: Vector3) -> String:
return persist.record("shape.create", {"type": "box", "gv": 1, "params": {},
"frame": SceneObjects.xform_to(Transform3D(Basis.IDENTITY, origin)),
"size": [size.x, size.y, size.z], "material_id": 2, "level_id": 0})
func _run() -> void:
if not ClassDB.class_exists("DCLevel"):
_ok("DCLevel registered", false)
return
if not ClassDB.class_has_method("DCLevel", "get_cutter_brush_ids"):
_ok("this DCLevel build knows cutters (dc-terrain f0efefd+)", false)
return
var gs: Node = get_tree().root.get_node_or_null("/root/GameState")
gs.route("world:level:*", 1, "peer") # 1 == Channel.DURABLE
var keet: Node = get_tree().root.get_node_or_null("/root/KeetService")
if keet != null:
keet.current_member_id = "gate-author"
level = ClassDB.instantiate("DCLevel")
add_child(level)
var persist := LevelPersistence.new()
persist.level = level
add_child(persist)
print("-- a wall (box shape) and a cutter (box shape) --")
var wall: String = _box(persist, Vector3.ZERO, Vector3(8, 4, 1))
var cutter: String = _box(persist, Vector3(3, -1, -1), Vector3(2, 4, 3))
persist.refold()
_ok("before any op the cutter is a solid box", _solid(level, 4, 1, 6, 1.5))
print("-- op.create subtract: the door opens, the cutter stops being solid --")
persist.begin_gesture("Live cut")
var op: String = persist.record("op.create", {"op": "subtract", "cutter": cutter, "targets": [wall]})
persist.end_gesture()
_ok("op.create recorded", op != "")
var st: Dictionary = persist.refold()
_ok("nothing skipped", int(st.get("skipped", 0)) == 0, str(st))
_ok("the door is air (ray passes through)", not _solid(level, 4, 1))
_ok("the wall beside it is solid", _solid(level, 1, 1) and _solid(level, 7, 1))
_ok("the lintel above the door is solid", _solid(level, 4, 3.5))
_ok("the cutter is a cutter (1 cutter brush)", (level.get_cutter_brush_ids() as PackedInt32Array).size() == 1)
_ok("the wall is now several pieces, all in its group", _live_solid(level, wall) >= 3, "%d" % _live_solid(level, wall))
_ok("a cutter is pickable only with include_cutters",
not (level.raycast(Vector3(4, 1, 6), Vector3(4, 1, 1.5), false, true) as Dictionary).is_empty())
var n_cut := _live_solid(level, wall)
persist.refold()
persist._dirty = true
persist.refold()
_ok("a re-fold changes nothing", _live_solid(level, wall) == n_cut)
print("-- live: move the CUTTER, the door follows --")
persist.record("shape.xform", {"target": cutter,
"frame": SceneObjects.xform_to(Transform3D(Basis.IDENTITY, Vector3(5.5, -1, -1)))})
persist.refold()
_ok("the old door is solid again", _solid(level, 4, 1))
_ok("the new door (x 5.5..7.5) is air", not _solid(level, 6.5, 1))
print("-- live: resize the TARGET, it is re-cut --")
persist.record("shape.size", {"target": wall, "size": [10.0, 4.0, 1.0]})
persist.refold()
_ok("the door is still air", not _solid(level, 6.5, 1))
_ok("the wall reaches its new end (x 9.5)", _solid(level, 9.5, 1))
print("-- op.set: subtract -> intersect --")
persist.record("op.set", {"target": op, "op": "intersect"})
persist.refold()
_ok("intersect: only the overlap is solid", _solid(level, 6.5, 1) and not _solid(level, 1, 1))
print("-- a second peer folding the same journal names the same pieces --")
var level2: Node3D = ClassDB.instantiate("DCLevel")
add_child(level2)
var persist2 := LevelPersistence.new()
persist2.level = level2
add_child(persist2)
persist2.refold()
_ok("identical live key sets", _keys(level) == _keys(level2), "%d vs %d" % [_keys(level).size(), _keys(level2).size()])
print("-- undo the op: the wall is whole, the cutter solid again --")
persist.undo_op(op)
persist.refold()
# Not a ray: the cutter, solid again, stands exactly where the door was.
_ok("the wall's own piece fills where the door was", _wall_contains(level, wall, Vector3(6.5, 1, 0.5)))
_ok("no cutter brushes remain", (level.get_cutter_brush_ids() as PackedInt32Array).size() == 0)
_ok("the cutter shape is solid again", _live_solid(level, cutter) == 1)
_ok("the wall is one piece again", _live_solid(level, wall) == 1, "%d" % _live_solid(level, wall))
print("-- flatten refuses while a live op exists --")
persist.redo_op(op)
persist.refold()
var fl: Dictionary = persist.flatten_map()
_ok("flatten_map refused, nothing changed", fl.has("error") and String(fl["error"]).contains("live cuts"), str(fl))
_ok("labels: op kinds have History names", String(persist.KIND_LABELS.get("op.create", "")) == "Live cut")