extends Node
## GATE — Delete acts on the SELECTION (2026-10-05: "delete currently works on nothing").
##
## The key went to _delete_aimed(), which never read the selection: it deleted
## whatever was under the cursor AT KEYPRESS, so a brush you clicked was not what
## Delete removed. Now delete_selection(): selected objects, else selected brushes,
## else (nothing selected) the aimed brush.
##
## 1. a selected plain brush is deleted (brush.remove), and Ctrl+Z restores it
## 2. selecting ONE piece of a parametric shape deletes the WHOLE shape
## (shape.remove — pieces are read-only), and Ctrl+Z restores it
## 3. both are one gesture each
##
## RUN AS A SCENE (level_persistence.gd names GameState at compile time):
## "$HYPER_TPS_GODOT" --headless res://scenes/test/gate_delete_selection.tscn
var fails := 0
var checks := 0
func _ok(what: String, cond: bool, note: String = "") -> void:
checks += 1
if not cond:
fails += 1
print(" %-62s %s %s" % [what, "OK" if cond else "**FAIL**", note])
func _box(lo: Vector3, hi: Vector3) -> Array:
return [1, 0, 0, hi.x, -1, 0, 0, -lo.x, 0, 1, 0, hi.y, 0, -1, 0, -lo.y, 0, 0, 1, hi.z, 0, 0, -1, -lo.z]
func _live(level: Node, id: int) -> bool:
var b: Dictionary = level.get_brush(id)
return not b.is_empty() and not bool(b.get("tombstoned", false))
func _live_in_group(level: Node, g: String) -> int:
var n := 0
for id: int in (level.get_group_brush_ids(g) as PackedInt32Array):
if _live(level, id):
n += 1
return n
func _ready() -> void:
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"
var map: Node = (load("res://scenes/maps/map_test.tscn") as PackedScene).instantiate()
add_child(map)
for i in 4:
await get_tree().process_frame
_run(map)
if checks < 9:
print(" **FAIL** only %d checks ran -- setup died before the assertions" % checks)
fails += 1
print("\nGATE delete_selection: %s (%d checks, %d failed)" % ["PASS" if fails == 0 else "FAIL", checks, fails])
get_tree().quit(0 if fails == 0 else 1)
func _run(map: Node) -> void:
var level: Node = map.get("level")
var persist: Node = map.get("_persist")
_ok("map_test has a level and a journal", level != null and persist != null)
if level == null or persist == null:
return
print("-- 1. a selected plain brush --")
var dot: String = persist.record("brush.create", {"planes": _box(Vector3(200, 0, 200), Vector3(202, 2, 202)),
"proj": [], "material_id": 0, "level_id": 0, "sky": false})
persist.refold()
var id: int = level.brush_by_key(dot)
_ok("the brush exists", _live(level, id))
map.select_brushes(PackedInt32Array([id]))
var g0: String = persist.last_gesture()
map.delete_selection()
persist.refold()
_ok("Delete removed the SELECTED brush (aim is irrelevant)", not _live(level, id))
_ok("as one new gesture", persist.last_gesture() != g0)
persist.undo_last()
persist.refold()
_ok("Ctrl+Z brings it back", _live(level, level.brush_by_key(dot)))
print("-- 2. one piece of a parametric shape --")
var sdot: String = persist.record("shape.create", {"type": "stairs", "gv": 1, "params": {"steps": 5},
"frame": SceneObjects.xform_to(Transform3D(Basis.IDENTITY, Vector3(300, 0, 300))),
"size": [5.0, 2.0, 2.0], "material_id": 0, "level_id": 0})
persist.refold()
var pieces: PackedInt32Array = level.get_group_brush_ids(sdot)
_ok("the shape has its pieces", _live_in_group(level, sdot) == 6, "%d" % _live_in_group(level, sdot))
map.select_brushes(PackedInt32Array([pieces[0]]))
map.delete_selection()
persist.refold()
_ok("Delete on ONE piece removed the WHOLE shape", _live_in_group(level, sdot) == 0, "%d" % _live_in_group(level, sdot))
var kinds: Array = persist.records().map(func(r: Dictionary) -> String: return String(r["kind"]))
_ok("via shape.remove (not one brush.remove per piece)", kinds.has("shape.remove") and kinds.count("brush.remove") == 1,
str(kinds))
persist.undo_last()
persist.refold()
_ok("Ctrl+Z brings the shape back", _live_in_group(level, sdot) == 6, "%d" % _live_in_group(level, sdot))