OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / gate_brush_regroup.gd

135 lines · gd ref:
extends Node
## GATE — BRUSH OBJECTS: regroup, join, separate (#120 F1, docs/node-tree-and-stacks.md
## §2.1). Needs dc-terrain with Kind::BrushRegroup (brush.regroup, DCBrush::base_group).
##   R1  Ctrl+G with a LOOSE brush: it becomes an object of its own (group = its key) and
##       the new collection holds it; one gesture
##   R2  a split AFTER that keeps both halves in the object, so in the collection
##   R3  undo the split, then Ctrl+Z the Ctrl+G: the brush is loose again, no collection
##   R4  Ctrl+J joins two loose brushes into the ACTIVE one's object
##   R5  P separates one again: a new object, still in the collections the old one was in
##   R6  join / separate refuse a shape's pieces, writing nothing
##
## RUN AS A SCENE: "$HYPER_TPS_GODOT" --headless res://scenes/test/gate_brush_regroup.tscn

var fails := 0
var checks := 0
var map: Node3D
var persist: Node
var level: Node


func _ok(what: String, cond: bool, note: Variant = "") -> void:
	checks += 1
	if not cond:
		fails += 1
	print("  %-64s %s  %s" % [what, "OK" if cond else "**FAIL**", str(note)])


func _ready() -> void:
	var gs: Node = get_tree().root.get_node_or_null("/root/GameState")
	gs.route("world:level:*", 1, "peer")
	var keet: Node = get_tree().root.get_node_or_null("/root/KeetService")
	if keet != null:
		keet.current_member_id = "gate-author"
	get_tree().root.get_node("/root/GameMode").set_mode("build")
	map = (load("res://scenes/maps/map_dev_test.tscn") as PackedScene).instantiate()
	add_child(map)
	for i in 6:
		await get_tree().process_frame
	await _run()
	get_tree().root.get_node("/root/GameMode").set_mode("play")
	if checks < 16:
		print("  **FAIL** only %d checks ran -- setup died before the assertions" % checks)
		fails += 1
	print("\nGATE brush_regroup: %s (%d checks, %d failed)" % ["PASS" if fails == 0 else "FAIL", checks, fails])
	get_tree().quit(0 if fails == 0 else 1)


func _group(key: String) -> String:
	var id := int(level.brush_by_key(key))
	return String(level.get_brush_group(id)) if id > 0 else "<none>"


func _loose_keys() -> Array:
	var out: Array = []
	for b: int in (level.get_live_brush_ids() as PackedInt32Array):
		if String(level.get_brush_group(b)) == "" and not bool((level.get_brush(b) as Dictionary).get("clearance", false)):
			out.append(String(level.get_brush_key(b)))
	out.sort()
	return out


func _run() -> void:
	persist = map.get("_persist")
	level = map.get("level")
	persist.refold()
	var loose := _loose_keys()
	_ok("setup: three loose brushes", loose.size() >= 3, loose)
	if loose.size() < 3:
		return
	var k0 := String(loose[0])

	# ---- R1 ----
	map.select_brushes(PackedInt32Array([int(level.brush_by_key(k0))]))
	var n0: int = (persist.gestures() as Array).size()
	var col: String = CollectionOps.create_from_selection(map, "Walls")
	persist.refold()
	_ok("R1 the loose brush is an object of its own (group = its key)", _group(k0) == k0, _group(k0))
	var t: Dictionary = persist.collection_tree()["cols"]
	_ok("R1 the collection holds it", t.has(col) and t[col]["members"] == ["g:" + k0], t.get(col, {}))
	_ok("R1 ... as one gesture", (persist.gestures() as Array).size() == n0 + 1)

	# ---- R2 ----
	var id0 := int(level.brush_by_key(k0))
	map.select_brushes(PackedInt32Array([id0]))
	var c: Vector3 = map.selection_centre()
	persist.begin_gesture("Cut")
	var split_dot: String = persist.record("brush.split", {"brush_key": k0, "n": [1, 0, 0], "d": c.x})
	persist.end_gesture()
	persist.refold()
	_ok("R2 the split's back half is in the object too", _group(split_dot) == k0, _group(split_dot))
	_ok("R2 ... so the object has two brushes", (level.get_group_brush_ids(k0) as PackedInt32Array).size() == 2,
		(level.get_group_brush_ids(k0) as PackedInt32Array).size())

	# ---- R3 ----
	persist.undo_last()                        # the split
	persist.undo_last()                        # the Ctrl+G
	persist.refold()
	_ok("R3 Ctrl+Z: the brush is loose again", _group(k0) == "", _group(k0))
	_ok("R3 ... and the collection is gone", not (persist.collection_tree()["cols"] as Dictionary).has(col))

	# ---- R4 ----
	var k1 := String(loose[1])
	var k2 := String(loose[2])
	map.select_brushes(PackedInt32Array([int(level.brush_by_key(k1)), int(level.brush_by_key(k2))]))
	_ok("setup: the active brush is the last one picked", String(level.get_brush_key(int(map.get("_sel_brush")))) == k2)
	var moved := CollectionOps.join(map)
	persist.refold()
	_ok("R4 Ctrl+J: both brushes are in the active one's object", _group(k1) == k2 and _group(k2) == k2,
		"%s %s (%d moved)" % [_group(k1), _group(k2), moved])

	# ---- R5 ----
	map.select_brushes(PackedInt32Array([int(level.brush_by_key(k2))]))
	var col2: String = CollectionOps.create_from_selection(map, "Joined")    # the object is in a collection
	persist.refold()
	map.select_brushes(PackedInt32Array([int(level.brush_by_key(k1))]))
	var ng := CollectionOps.separate(map)
	persist.refold()
	_ok("R5 P: the brush is a new object", ng != "" and _group(k1) == ng and _group(k2) == k2,
		"%s %s %s" % [ng, _group(k1), _group(k2)])
	var m2: Array = persist.collection_tree()["cols"][col2]["members"]
	_ok("R5 ... still in the collection its source was in", m2.has("g:" + ng) and m2.has("g:" + k2), m2)

	# ---- R6 ----
	var shape: String = persist.record("shape.create", {"type": "box", "gv": 1, "params": {},
		"frame": SceneObjects.xform_to(Transform3D(Basis.IDENTITY, Vector3(20, 0, 20))),
		"size": [2, 1, 2], "material_id": 2, "level_id": 0})
	persist.refold()
	var piece: int = (level.get_group_brush_ids(shape) as PackedInt32Array)[0]
	map.select_brushes(PackedInt32Array([int(level.brush_by_key(k1)), piece]))
	var g6: int = (persist.gestures() as Array).size()
	_ok("R6 join refuses a shape's piece", CollectionOps.join(map) == -1)
	_ok("R6 separate refuses it too", CollectionOps.separate(map) == "")
	_ok("R6 ... and nothing was written", (persist.gestures() as Array).size() == g6)
	_ok("R6 the shape is still its own object", _group(String(level.get_brush_key(piece))) == shape)