154 lines
5.6 KiB
GDScript
154 lines
5.6 KiB
GDScript
extends SceneTree
|
|
## Identitäts-Vertrag, QUEST-CLIENT-SEITE (Karte t_1da96275): dieselbe
|
|
## Probe-Map wie tests/test_playtest_identitaet.gd, aber im echten
|
|
## Client-Kontext (SvmapWorld + TestmapPlayer — der Quest-3-Weg).
|
|
## Wird ÜBER das Editor-Repo mit dem Client-Projekt gefahren:
|
|
## SVR_IDENTITAET_MAP=/tmp/svr_identitaet_probe.svmap \
|
|
## godot4 --headless --path <scopedvr-client> -s <abs>/tests/test_identitaet_client.gd
|
|
## Druckt dieselben `IDENT <name>=<wert>`-Zeilen; der Runner diffed beide.
|
|
##
|
|
## BEWUSST 1:1 gespiegelt zu test_playtest_identitaet.gd — gleiche Start-
|
|
## Punkte, gleiche Frame-Zahlen, gleiche Entscheidungsregeln. Nur die
|
|
## Welt-/Spieler-Klassen sind die des Clients.
|
|
|
|
const HALF_H := 0.9
|
|
const TUER_AZIMUT_DEG := 202.5
|
|
const GEB_TUER := Vector2(-16.0, -16.0)
|
|
const GEB_HALF := Vector2(8.94, 3.54)
|
|
|
|
var fails: Array[String] = []
|
|
|
|
|
|
func _initialize() -> void:
|
|
_run.call_deferred()
|
|
|
|
|
|
func _check(cond: bool, what: String) -> void:
|
|
if cond:
|
|
print("IDENT ok: ", what)
|
|
else:
|
|
fails.append(what)
|
|
print("IDENT FAIL: ", what)
|
|
|
|
|
|
func _run() -> void:
|
|
var map_path := OS.get_environment("SVR_IDENTITAET_MAP")
|
|
if map_path.is_empty():
|
|
map_path = "/tmp/svr_identitaet_probe.svmap"
|
|
if not FileAccess.file_exists(map_path):
|
|
printerr("IDENT FAIL: Probe-Map fehlt (%s)." % map_path)
|
|
quit(1)
|
|
return
|
|
var doc_v: Variant = JSON.parse_string(FileAccess.get_file_as_string(map_path))
|
|
if not (doc_v is Dictionary):
|
|
printerr("IDENT FAIL: Probe-Map nicht lesbar.")
|
|
quit(1)
|
|
return
|
|
var doc: Dictionary = doc_v
|
|
|
|
# --- Client-Aufbau: SvmapWorld (Kollider + Visuals) + TestmapPlayer ------
|
|
var welt: Node3D = SvmapWorld.build_from_data(doc, SvmapWorld.load_manifest())
|
|
root.add_child(welt)
|
|
var player: CharacterBody3D = TestmapPlayer.new()
|
|
welt.add_child(player)
|
|
await physics_frame
|
|
await physics_frame
|
|
|
|
# 1) Stufe 0.3 m (Terrain) — erklommen.
|
|
var r := await _lauf(player, Vector3(-8.0, 0.0, 20.0), Vector3(1, 0, 0), 300)
|
|
var st30 := "BLOCK"
|
|
if r.max_feet >= 0.25 and r.end.x > 1.0:
|
|
st30 = "KLIMM"
|
|
_ident("stufe_terrain_030", "%s %.2f" % [st30, r.max_feet])
|
|
_check(st30 == "KLIMM", "Stufe 0.3 m erklommen (max feet %.2f, x %.2f)" % [r.max_feet, r.end.x])
|
|
|
|
# 2) Hohes Hindernis 0.85 m (barrel_001, Box-Kollider) — blockt: über der
|
|
# Step-Up-Grenze 0.45 m gibt es KEIN Klettern (Karte t_f9adf97c).
|
|
r = await _lauf(player, Vector3(0.5, 0.0, 20.0), Vector3(1, 0, 0), 240)
|
|
var st80 := "KLIMM"
|
|
if r.end.x < 1.55 and r.max_feet < 0.55:
|
|
st80 = "BLOCK"
|
|
_ident("wand_barrel", "%s %.2f x=%.2f" % [st80, r.max_feet, r.end.x])
|
|
_check(st80 == "BLOCK", "Hindernis 0.85 m blockt (max feet %.2f, x %.2f)" % [r.max_feet, r.end.x])
|
|
|
|
# 3) Kisten-Stufe 0.334 m (box_006) — erklommen (Step-Up über Objekt).
|
|
r = await _lauf(player, Vector3(-24.0, 0.0, 8.0), Vector3(1, 0, 0), 260)
|
|
var box := "BLOCK"
|
|
if r.max_feet >= 0.28:
|
|
box = "KLIMM"
|
|
_ident("kisten_stufe", "%s %.2f" % [box, r.max_feet])
|
|
_check(box == "KLIMM", "Kiste 0.334 m erklommen (max feet %.2f)" % r.max_feet)
|
|
|
|
# 4) Wand (barracks_001-Trimesh) — blockt.
|
|
r = await _lauf(player, Vector3(20.0, 0.0, 31.5), Vector3(0, 0, -1), 300)
|
|
var wand := "DURCH"
|
|
if r.end.z > 29.3:
|
|
wand = "BLOCK"
|
|
_ident("wand_barracks", "%s z=%.2f" % [wand, r.end.z])
|
|
_check(wand == "BLOCK", "Kaserne blockt (Ende z=%.2f)" % r.end.z)
|
|
|
|
# 5) Tür (building_001) — durchlässig, Spieler landet IM Gebäude.
|
|
var outward := Vector3(cos(deg_to_rad(TUER_AZIMUT_DEG)), 0.0,
|
|
sin(deg_to_rad(TUER_AZIMUT_DEG)))
|
|
var start := Vector3(GEB_TUER.x + outward.x * 12.0, 0.0,
|
|
GEB_TUER.y + outward.z * 12.0)
|
|
r = await _lauf(player, start, -outward, 600)
|
|
var drin := absf(r.end.x - GEB_TUER.x) < GEB_HALF.x - 0.3 \
|
|
and absf(r.end.z - GEB_TUER.y) < GEB_HALF.y - 0.3
|
|
_ident("tuer_building", "%s %.2f %.2f" % ["DRIN" if drin else "DRAUSSEN", r.end.x, r.end.z])
|
|
_check(drin, "Tür building_001 durchlässig (Ende %.2f, %.2f)" % [r.end.x, r.end.z])
|
|
|
|
# 6) Terrain-Höhe unter den Füßen (Strahl abwärts, exakt).
|
|
_hoehe(welt, Vector3(-10.0, 10.0, 0.0), "terrain_hoehe_a", 0.0)
|
|
_hoehe(welt, Vector3(4.0, 10.0, 20.0), "terrain_hoehe_b", 0.3)
|
|
_hoehe(welt, Vector3(10.0, 12.0, -28.0), "terrain_hoehe_c", 1.1)
|
|
|
|
if fails.is_empty():
|
|
print("IDENT CLIENT: ALLE OK")
|
|
quit(0)
|
|
else:
|
|
print("IDENT CLIENT: FAILED %d" % fails.size())
|
|
quit(1)
|
|
|
|
|
|
func _ident(name: String, wert: String) -> void:
|
|
print("IDENT %s=%s" % [name, wert])
|
|
|
|
|
|
## Autopilot-Lauf — 1:1 zur Editor-Seite: Start y=3.0 (Fall-Settle),
|
|
## dann Richtung fahren, max Füß-Höhe + Endposition messen.
|
|
func _lauf(player: CharacterBody3D, start_xz: Vector3, dir: Vector3,
|
|
frames: int) -> Dictionary:
|
|
player.auto_move_dir = Vector3.ZERO
|
|
player.velocity = Vector3.ZERO
|
|
player.global_position = Vector3(start_xz.x, 3.0, start_xz.z)
|
|
for i in 45:
|
|
await physics_frame
|
|
player.auto_move_dir = dir
|
|
var max_feet := -INF
|
|
for i in frames:
|
|
await physics_frame
|
|
var feet := player.global_position.y - HALF_H
|
|
max_feet = maxf(max_feet, feet)
|
|
if feet < -5.0:
|
|
break
|
|
player.auto_move_dir = Vector3.ZERO
|
|
for i in 10:
|
|
await physics_frame
|
|
return {"end": Vector3(player.global_position), "max_feet": max_feet}
|
|
|
|
|
|
## Boden-Strahl an einem Punkt → IDENT-Zeile + Soll-Prüfung (±1 cm).
|
|
func _hoehe(welt: Node3D, p: Vector3, name: String, soll: float) -> void:
|
|
var raum := welt.get_world_3d().direct_space_state
|
|
var hit := raum.intersect_ray(PhysicsRayQueryParameters3D.create(
|
|
p, p + Vector3(0, -30, 0)))
|
|
var y := INF
|
|
if not hit.is_empty():
|
|
y = (hit["position"] as Vector3).y
|
|
if y == INF:
|
|
_ident(name, "KEIN_TREFFER")
|
|
_check(false, name + ": kein Boden-Treffer")
|
|
return
|
|
_ident(name, "%.2f" % y)
|
|
_check(absf(y - soll) <= 0.01, "%s: %.3f ≈ Soll %.2f" % [name, y, soll])
|