OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / gate_bot_smoothing.gd

79 lines · gd ref:
extends SceneTree
## Gate: bot markers glide instead of snapping (#93 §5, #80 "it snaps today").
##
## A position body is published as a realtime row at ~10 Hz (and patrol only publishes
## after moving a set distance), and the marker used to be SET to each row — a visible
## step every 100 ms on every peer. scenes/game/bot_marker.gd smooths toward the latest
## row with short extrapolation from the last two rows; the first row and jumps > 5 m
## snap (same threshold as player dead reckoning).
##
## Run: HYPERTPS_DAEMON_TCP=127.0.0.1:1 "$HYPER_TPS_GODOT" --headless --path . --script res://scenes/test/gate_bot_smoothing.gd

var ok := true
var checks := 0
var _ran := false


func _p(c: bool, m: String, got: Variant = "") -> void:
	checks += 1
	print(("ok   " if c else "FAIL: ") + m + ("" if str(got) == "" else "  (" + str(got) + ")"))
	if not c:
		ok = false


func _process(_d: float) -> bool:
	if _ran:
		return true
	_ran = true
	_run()
	if checks < 8:
		_p(false, "only %d checks ran" % checks)
	print("GATE bot_smoothing: %s (%d checks)" % ["PASS" if ok else "FAIL", checks])
	quit(0 if ok else 1)
	return true


func _run() -> void:
	var script: Script = load("res://scenes/game/bot_marker.gd")
	_p(script != null, "scenes/game/bot_marker.gd exists")
	if script == null:
		return
	var m := Node3D.new()
	m.set_script(script)
	root.add_child(m)

	print("-- first row snaps --")
	m.set_target(Vector3(1, 0, 1), 0.0)
	_p(m.position.is_equal_approx(Vector3(1, 0, 1)), "first target: placed exactly", m.position)

	print("-- later rows glide --")
	m.set_target(Vector3(2, 0, 1), 0.1)          # 1 m in 100 ms = 10 m/s
	m.step(1.0 / 60.0, 0.1)
	_p(m.position.x > 1.0 + 1e-3 and m.position.x < 2.0 - 1e-3, "one frame later: part-way, not at the row", m.position.x)
	var t := 0.1
	for i in 30:
		t += 1.0 / 60.0
		m.step(1.0 / 60.0, t)
	# 0.5 s after the row, extrapolation is capped at 0.15 s past it: x <= 2 + 10*0.15.
	_p(m.position.x > 1.9 and m.position.x <= 3.5 + 1e-3, "after 0.5 s: caught up, extrapolation bounded", m.position.x)

	print("-- a long jump snaps --")
	m.set_target(Vector3(20, 0, 1), t)
	_p(m.position.is_equal_approx(Vector3(20, 0, 1)), "> 5 m away: snapped, no slide across the map", m.position)

	print("-- a still bot stays still --")
	m.set_target(Vector3(20, 0, 1), t + 0.1)
	for i in 30:
		m.step(1.0 / 60.0, t + 0.1 + i / 60.0)
	_p(m.position.is_equal_approx(Vector3(20, 0, 1)), "same row twice: no drift", m.position)

	print("-- the registry drives markers through it --")
	var reg: Node = root.get_node_or_null("/root/BotRegistry")
	_p(reg != null, "BotRegistry autoload present")
	if reg == null:
		return
	var marker: Node3D = reg._make_marker("gate-smooth")
	_p(marker.has_method("set_target"), "_make_marker builds a smoothed marker")
	marker.queue_free()
	m.queue_free()