OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / gate_scene_boxes_base.gd

112 lines · gd ref:
extends Node
## GATE — a level map's SCENE BOXES become BASE BRUSHES at load (2026-10-05: "the
## editor cannot select the created elements in the tscn starter map").
##
## map_test's geometry was StaticBody3D + BoxShape3D + CSGBox3D nodes: no journal
## identity, so a click logged "not level geometry" and nothing could select, move or
## delete them. Now each becomes a base brush in the DCLevel (key "i:1:<n>", level 1,
## in node-NAME order so the keys are stable), and the node goes. Edits are ordinary
## journal records against those keys; the rewind keeps base brushes live.
##
##   1. no box StaticBody3D left under the map; 8 base brushes, keys in name order
##   2. Platform1's brush is where Platform1 was; the rotated Ramp keeps its rotation
##   3. a child that is not collision (HubLabel) survives the conversion
##   4. a real ray onto Platform2 picks its brush (level body)
##   5. brush.remove on a base key deletes it; Ctrl+Z restores it
##
## RUN AS A SCENE: "$HYPER_TPS_GODOT" --headless res://scenes/test/gate_scene_boxes_base.tscn

const NAMES := ["Ground", "NorthBridge", "NorthHub", "Platform1", "Platform2", "Platform3", "Ramp", "Wall1"]

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 _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"
	var map: Node3D = (load("res://scenes/maps/map_dev_test.tscn") as PackedScene).instantiate()
	add_child(map)
	for i in 6:
		await get_tree().physics_frame
	_run(map)
	if checks < 12:
		print("  **FAIL** only %d checks ran -- setup died before the assertions" % checks)
		fails += 1
	print("\nGATE scene_boxes_base: %s (%d checks, %d failed)" % ["PASS" if fails == 0 else "FAIL", checks, fails])
	get_tree().quit(0 if fails == 0 else 1)


func _live(level: Node, key: String) -> bool:
	var id: int = level.brush_by_key(key)
	var b: Dictionary = level.get_brush(id) if id > 0 else {}
	return not b.is_empty() and not bool(b.get("tombstoned", false))


func _run(map: Node3D) -> void:
	var level: Node = map.get("level")
	var persist: Node = map.get("_persist")
	_ok("map_test has its level and journal", level != null and persist != null)
	if level == null or persist == null:
		return
	var left: Array = []
	for c in map.get_children():
		if c is StaticBody3D:
			left.append(c.name)
	_ok("no scene box StaticBody3D left under the map", left.is_empty(), str(left))
	var keys_ok := true
	for i in NAMES.size():
		if not _live(level, "i:1:%d" % i):
			keys_ok = false
	_ok("8 live base brushes i:1:0..7", keys_ok and not _live(level, "i:1:8"))

	var p1: Dictionary = level.get_brush(level.brush_by_key("i:1:%d" % NAMES.find("Platform1")))
	var mn: Vector3 = p1.get("aabb_min", Vector3.INF)
	var mx: Vector3 = p1.get("aabb_max", Vector3.INF)
	# Platform1: origin (-10, 3, -5), BoxShape3D 8 x 0.5 x 8.
	_ok("Platform1's brush is where Platform1 was", mn.is_equal_approx(Vector3(-14, 2.75, -9))
		and mx.is_equal_approx(Vector3(-6, 3.25, -1)), "%s .. %s" % [mn, mx])
	var ramp: Dictionary = level.get_brush(level.brush_by_key("i:1:%d" % NAMES.find("Ramp")))
	var pl: PackedFloat32Array = ramp.get("planes", PackedFloat32Array())
	var tilted := false
	for k in range(0, pl.size(), 4):
		var n := Vector3(pl[k], pl[k + 1], pl[k + 2])
		if absf(n.y) > 0.05 and absf(n.y) < 0.995:
			tilted = true
	_ok("the rotated Ramp keeps its rotation (tilted planes)", pl.size() == 24 and tilted, "%d floats" % pl.size())

	var label: Node = map.find_child("HubLabel", true, false)
	_ok("HubLabel (not collision) survived the conversion", label != null and label.is_inside_tree())
	_ok("and kept its place above the hub", label != null and (label as Node3D).global_position.y > 0.4,
		str((label as Node3D).global_position) if label != null else "")

	# Platform2: origin (10, 6, 5), 6 x 0.5 x 6 -> top at y = 6.25.
	var q := PhysicsRayQueryParameters3D.create(Vector3(10, 20, 5), Vector3(10, -20, 5))
	var hit: Dictionary = map.get_world_3d().direct_space_state.intersect_ray(q)
	var p2id: int = level.brush_by_key("i:1:%d" % NAMES.find("Platform2"))
	_ok("a real ray onto Platform2 hits the LEVEL body", not hit.is_empty() and hit.get("collider") == level,
		str(hit.get("collider")))
	_ok("and picks Platform2's brush", map._brush_from_hit(hit) == p2id, "%d vs %d" % [map._brush_from_hit(hit), p2id])

	var key2 := "i:1:%d" % NAMES.find("Platform2")
	_ok("Platform2 is live before the delete", _live(level, key2))
	map.select_brushes(PackedInt32Array([p2id]))
	map.delete_selection()
	persist.refold()
	_ok("Delete removes a base brush (Platform2)", not _live(level, key2))
	_ok("via brush.remove on its base key", persist.records().any(func(r: Dictionary) -> bool:
		return String(r["kind"]) == "brush.remove" and String(r.get("brush_key", "")) == key2))
	persist.undo_last()
	persist.refold()
	_ok("Ctrl+Z restores it", _live(level, key2))