OverviewFilesCommitsIssuesDocsReleasesPeers

hyper-tps / scenes / test / gate_teleporter_ground.gd

78 lines · gd ref:
extends SceneTree
## GATE — every teleporter stands ON the ground once the map has streamed (user
## 2026-10-06: "our teleporters are not properly situated on the terrain").
##
## Measured 2026-10-06 (_probe_arrival): map_mountain's return pad 117 m in the air,
## map_geiss's 12.7 m, map_geiss_terraces' 11.8 m INSIDE the rock. A map loads with a
## camera at its first spawn point (as a player would arrive), streams SETTLE_S, then a
## ray down from each teleporter must find ground within STAND_TOL below its origin
## (the pad's base), and it must not be under a surface.
##
## ⚠ WINDOWED, one map per run:
##   env -u CI HYPERTPS_DAEMON_TCP=127.0.0.1:1 DISPLAY=:0 "$HYPER_TPS_GODOT" --path . \
##       --script res://scenes/test/gate_teleporter_ground.gd -- map_mountain

const SETTLE_S := 20.0
const STAND_TOL := 0.3
const ProbeWindow := preload("res://scenes/test/probe_window.gd")
const _Arrival := preload("res://scenes/player/arrival_ground.gd")

var _ws := ""
var _name := ""
var _map: Node
var _cam: Camera3D
var _start := 0


func _initialize() -> void:
	var a := OS.get_cmdline_user_args()
	_name = a[0] if a.size() > 0 else "map_mountain"
	_ws = ProbeWindow.free_workspace()
	_cam = Camera3D.new()
	_cam.far = 20000.0
	root.add_child(_cam)
	_cam.current = true
	_map = (load("res://scenes/maps/%s.tscn" % _name) as PackedScene).instantiate()
	root.add_child(_map)


func _physics_process(_d: float) -> bool:
	if not ProbeWindow.drawing(root):
		ProbeWindow.move_to(_ws, Vector2i(1600, 900))
	if _start == 0:
		if Engine.get_physics_frames() < 2:
			return false
		var sp := get_nodes_in_group("spawn_points")
		var at: Vector3 = (sp[0] as Node3D).global_position if not sp.is_empty() else Vector3.ZERO
		_cam.look_at_from_position(at + Vector3(0, 1.7, 0), at + Vector3(10, 1.7, 0.01))
		_start = Time.get_ticks_msec()
		return false
	if (Time.get_ticks_msec() - _start) / 1000.0 < SETTLE_S:
		return false
	var space := root.get_world_3d().direct_space_state
	var bad := 0
	var n := 0
	for t in _map.find_children("*", "Area3D", true, false):
		if not ("target_map" in t):
			continue
		n += 1
		var p: Vector3 = (t as Node3D).global_position
		var dq := PhysicsRayQueryParameters3D.create(p + Vector3(0, 0.5, 0), p - Vector3(0, 400, 0))
		dq.hit_back_faces = false
		var down := space.intersect_ray(dq)
		var aq := PhysicsRayQueryParameters3D.create(p + Vector3(0, 300, 0), p + Vector3(0, 0.5, 0))
		aq.hit_back_faces = false
		var above := space.intersect_ray(aq)
		var gap: float = p.y - (down["position"] as Vector3).y if not down.is_empty() else INF
		var buried := down.is_empty() and not above.is_empty()
		var ok := not buried and gap >= -0.05 and gap <= STAND_TOL
		if not ok:
			bad += 1
		print("  %s %s at %s: %s" % ["ok  " if ok else "FAIL", t.name, p.snapped(Vector3.ONE * 0.01),
			"BURIED (surface above at %.1f)" % (above["position"] as Vector3).y if buried
			else ("%.2f m above the ground" % gap if gap != INF else "NO GROUND within 400 m")])
	var pass_ := n > 0 and bad == 0
	print("GATE teleporter_ground %s: %s (%d teleporters, %d off the ground)" % [_name, "PASS" if pass_ else "FAIL", n, bad])
	quit(0 if pass_ else 1)
	return true