OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / game / bot_marker.gd

55 lines · gd ref:
extends Node3D
## A bot's visible body that GLIDES to its published position (#93 §5; #80 "it snaps").
##
## Position bodies publish a realtime row at ~10 Hz (patrol only after moving a set
## distance) and the marker used to be SET to each row: a visible step every row, on
## every peer, including the one simulating it. Now each row is a TARGET:
##   - the first row, and any jump over SNAP_M, places the marker exactly (spawn,
##     teleport, map entry — sliding across the map would be wrong);
##   - otherwise the marker eases toward target + velocity * (time since the row),
##     velocity estimated from the last two rows, extrapolation capped at
##     EXTRAPOLATE_S so a bot that stopped publishing does not wander off.
## The brains keep their own position state (pathwalker _pos, patrol p) and never
## read this node back, so smoothing it cannot feed lag into their logic.

const SNAP_M := 5.0           # same threshold as player dead reckoning
const RATE := 15.0            # 1/s: ~67 ms time constant against a 100 ms row interval
const EXTRAPOLATE_S := 0.15

var _has_target := false
var _target := Vector3.ZERO
var _vel := Vector3.ZERO
var _t_row := 0.0


static func _now() -> float:
	return Time.get_ticks_msec() / 1000.0


## A new published position. `now` (seconds) is for gates; play uses the clock.
func set_target(pos: Vector3, now: float = -1.0) -> void:
	var t := now if now >= 0.0 else _now()
	if not _has_target or global_position.distance_to(pos) > SNAP_M:
		_has_target = true
		_target = pos
		_vel = Vector3.ZERO
		_t_row = t
		global_position = pos
		return
	var dt := t - _t_row
	_vel = (pos - _target) / dt if dt > 0.02 else Vector3.ZERO
	_target = pos
	_t_row = t


func step(delta: float, now: float) -> void:
	if not _has_target:
		return
	var ahead := clampf(now - _t_row, 0.0, EXTRAPOLATE_S)
	var want := _target + _vel * ahead
	global_position = global_position.lerp(want, 1.0 - exp(-RATE * delta))


func _process(delta: float) -> void:
	step(delta, _now())