OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / gate_curve_journal.gd

123 lines · gd ref:
extends Node
## GATE — CURVES ARE ROOM STATE (#120 F5, CurveFold through LevelPersistence).
##   J1  create_curve: the curve is in curve_states, points as given (auto handles), one
##       gesture; the level is NOT re-folded and nothing is reported skipped
##   J2  set_curve_point moves one point (one curve.set); Ctrl+Z puts it back
##   J3  the whole point list, and closed, can be set
##   J4  remove_curve: gone; Ctrl+Z: back
##   J5  a point index past the end is skipped, never guessed at
##   J6  two arrival orders of the same records give the same curves
##   J7  the view draws it (a line strip + its points)
##   J8  a flatten keeps it, the same points
##
## RUN AS A SCENE: CI=true "$HYPER_TPS_GODOT" --headless res://scenes/test/gate_curve_journal.tscn

const CurveFold := preload("res://scenes/maps/curves/curve_fold.gd")

var fails := 0
var checks := 0
var map: Node3D
var persist: 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 < 13:
		print("  **FAIL** only %d checks ran -- setup died before the assertions" % checks)
		fails += 1
	print("\nGATE curve_journal: %s (%d checks, %d failed)" % ["PASS" if fails == 0 else "FAIL", checks, fails])
	get_tree().quit(0 if fails == 0 else 1)


func _p(dot: String, i: int) -> Vector3:
	return (map.curve_states()[dot]["points"][i] as Dictionary)["p"]


func _run() -> void:
	persist = map.get("_persist")
	persist.refold()
	var pts: Array = [CurveMath.point(Vector3(0, 1, 0), Vector3.FORWARD), CurveMath.point(Vector3(4, 2, 0)),
		CurveMath.point(Vector3(8, 1, 0), Vector3.BACK)]

	# ---- J1 ----
	var g0: int = (persist.gestures() as Array).size()
	var c: String = map.create_curve(pts)
	var lf: Dictionary = persist.last_fold()
	var st: Dictionary = map.curve_states()
	_ok("J1 the curve is room state", st.has(c), st.keys())
	_ok("J1 ... its points where they were placed", st.has(c) and _p(c, 1).is_equal_approx(Vector3(4, 2, 0)))
	_ok("J1 ... with auto handles (the first leaves along its normal)",
		st.has(c) and ((st[c]["points"][0] as Dictionary)["h_out"] as Vector3).normalized().is_equal_approx(Vector3.FORWARD))
	_ok("J1 one gesture", (persist.gestures() as Array).size() == g0 + 1)
	_ok("J1 the level was not re-folded, nothing skipped", String(lf.get("brush_fold", "")) == "skipped"
		and int(lf.get("skipped", 0)) == 0, lf)

	# ---- J7 ----
	_ok("J7 the view draws it: a strip and its points", map.curve_view_surfaces() == 2, map.curve_view_surfaces())

	# ---- J2 ----
	map.set_curve_point(c, 1, {"p": Vector3(4, 5, 0)})
	_ok("J2 a point drag moves one point", _p(c, 1).is_equal_approx(Vector3(4, 5, 0)) and _p(c, 0).is_equal_approx(Vector3(0, 1, 0)))
	persist.undo_last()
	persist.refold()
	_ok("J2 Ctrl+Z puts it back", _p(c, 1).is_equal_approx(Vector3(4, 2, 0)))

	# ---- J3 ----
	persist.begin_gesture("Edit")
	persist.record("curve.set", {"target": c, "points": CurveFold.to_wire([CurveMath.point(Vector3.ZERO), CurveMath.point(Vector3(1, 0, 0)),
		CurveMath.point(Vector3(1, 0, 1)), CurveMath.point(Vector3(0, 0, 1))]), "closed": true})
	persist.end_gesture()
	persist.refold()
	_ok("J3 four points, closed", (map.curve_states()[c]["points"] as Array).size() == 4 and bool(map.curve_states()[c]["closed"]))

	# ---- J5 ----
	persist.begin_gesture("Edit")
	persist.record("curve.set", {"target": c, "index": 9, "fields": {"p": [9, 9, 9]}})
	persist.end_gesture()
	persist.refold()
	_ok("J5 an index past the end is skipped", (map.curve_states()[c]["points"] as Array).size() == 4
		and not (map.curve_states()[c]["points"] as Array).any(func(q: Dictionary) -> bool: return (q["p"] as Vector3) == Vector3(9, 9, 9)))
	persist.undo_last()
	persist.undo_last()
	persist.refold()

	# ---- J4 ----
	map.remove_curve(c)
	_ok("J4 removed", not map.curve_states().has(c))
	persist.undo_last()
	persist.refold()
	_ok("J4 Ctrl+Z: back", map.curve_states().has(c))

	# ---- J6 ----
	var recs: Array = (persist.records(false) as Array).filter(func(r: Dictionary) -> bool: return CurveFold.handles(String(r["kind"])))
	var rev := recs.duplicate()
	rev.reverse()
	_ok("J6 another arrival order, the same curves", str(CurveFold.live(rev)) == str(CurveFold.live(recs)))

	# ---- J8 ----
	var before: Array = map.curve_states()[c]["points"]
	persist.flatten_map()
	var after: Array = []
	for d: String in map.curve_states():
		after = map.curve_states()[d]["points"]
	_ok("J8 a flatten keeps the curve, the same points", map.curve_states().size() == 1 and after.size() == before.size()
		and (after[1]["p"] as Vector3).is_equal_approx(before[1]["p"]), [before.size(), after.size()])