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()