OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / gate_mod_chamfer.gd

134 lines · gd ref:
extends Node
## GATE — THE CHAMFER MODIFIER (#120 F2d; DCLevel BrushChamfer, after the cuts).
##   H1  a box shape + chamfer (0.1, 1 segment): its piece has 6 + 12 planes
##   H2  mirrored first: both halves chamfered, but NOT at the mirror seam — the top face
##       runs right up to it on both sides (no groove)
##   H3  editing the width moves the chamfer planes (the faces are re-keyed)
##   H4  off -> plain box; on -> chamfered; Ctrl+Z of the add -> plain box
##   H5  after a cut: the cut pieces are chamfered too (more planes than the cut alone)
##
## RUN AS A SCENE: CI=true "$HYPER_TPS_GODOT" --headless res://scenes/test/gate_mod_chamfer.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 < 9:
		print("  **FAIL** only %d checks ran -- setup died before the assertions" % checks)
		fails += 1
	print("\nGATE mod_chamfer: %s (%d checks, %d failed)" % ["PASS" if fails == 0 else "FAIL", checks, fails])
	get_tree().quit(0 if fails == 0 else 1)


func _box(at: Vector3, size: Vector3) -> String:
	var d: String = persist.record("shape.create", {"type": "box", "gv": 1, "params": {},
		"frame": SceneObjects.xform_to(Transform3D(Basis.IDENTITY, at)),
		"size": [size.x, size.y, size.z], "material_id": 2, "level_id": 0})
	persist.refold()
	return d


func _solids(dot: String) -> Array:
	var out: Array = []
	for b: int in (level.get_group_brush_ids(dot) as PackedInt32Array):
		var d: Dictionary = level.get_brush(b)
		if not bool(d.get("clearance", false)) and not bool(d.get("cutter", false)):
			out.append(b)
	return out


func _live_planes(b: int) -> int:
	var n := 0
	var pl: PackedFloat32Array = level.get_brush(b)["planes"]
	for i in pl.size() / 4:
		if FaceKit.face_polygon(pl, i).size() >= 3:
			n += 1
	return n


func _corner(b: int, at: Vector3) -> bool:
	return Array(FaceKit.corners(level.get_brush(b)["planes"])).any(func(v: Vector3) -> bool: return v.distance_to(at) < 1.0e-3)


func _run() -> void:
	persist = map.get("_persist")
	level = map.get("level")
	persist.refold()

	# ---- H1 ----
	var s := _box(Vector3(40, 0, 40), Vector3(1, 1, 1))
	var m: String = map.add_modifier("chamfer", s)
	var p1: int = _solids(s)[0]
	_ok("H1 a chamfered box: 6 + 12 faces", _live_planes(p1) == 18, _live_planes(p1))
	_ok("H1 ... its corner cut back: (40.1, 0, 40.1) is a corner", _corner(p1, Vector3(40.1, 0, 40.1)))

	# ---- H3 ----
	map.set_modifier(m, {"width": 0.2})
	_ok("H3 width 0.2 moves the chamfer: (40.2, 0, 40.2)", _corner(_solids(s)[0], Vector3(40.2, 0, 40.2))
		and not _corner(_solids(s)[0], Vector3(40.1, 0, 40.1)))

	# ---- H4 ----
	map.set_modifier(m, {}, false)
	_ok("H4 off: the plain box", _live_planes(_solids(s)[0]) == 6)
	map.set_modifier(m, {}, true)
	_ok("H4 on: chamfered again", _live_planes(_solids(s)[0]) == 18)

	# ---- H2 mirror + chamfer ----
	var t := _box(Vector3(60, 0, 60), Vector3(1, 1, 1))
	map.add_modifier("mirror", t)
	map.add_modifier("chamfer", t)
	var halves: Array = _solids(t)
	_ok("H2 two halves", halves.size() == 2)
	var seam_ok := true
	for b: int in halves:
		seam_ok = seam_ok and _corner(b, Vector3(60, 1, 60.1)) and _corner(b, Vector3(60, 1, 60.9))
	_ok("H2 no groove: the top face reaches the mirror seam on both halves", seam_ok)
	_ok("H2 the outer edges are chamfered: (61, 1, 60) gone", not halves.any(func(b: int) -> bool: return _corner(b, Vector3(61, 1, 60))))

	# ---- H5 a cut, then the chamfer ----
	var u := _box(Vector3(80, 0, 80), Vector3(2, 1, 2))
	var cutter := _box(Vector3(80.5, 0.5, 80.5), Vector3(1, 1, 1))
	persist.begin_gesture("Cut")
	persist.record("op.create", {"op": "subtract", "cutter": cutter, "targets": [u]})
	persist.end_gesture()
	persist.refold()
	var planes_cut := 0
	for b: int in _solids(u):
		planes_cut += _live_planes(b)
	map.add_modifier("chamfer", u)
	var planes_both := 0
	for b: int in _solids(u):
		planes_both += _live_planes(b)
	_ok("H5 the cut pieces are chamfered too", planes_both > planes_cut, [planes_cut, planes_both])

	# ---- H4 undo ----
	persist.undo_last()
	persist.refold()
	var planes_undone := 0
	for b: int in _solids(u):
		planes_undone += _live_planes(b)
	_ok("H4 Ctrl+Z of the add: back to the cut alone", planes_undone == planes_cut, [planes_undone, planes_cut])