scopedvr-map-core/editor/playtest/test_start.gd
2026-11-01 19:31:33 +01:00

92 lines
3.6 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

extends RefCounted
class_name TestStart
## Test-Startpunkt (Karte t_96e3c4ee): der Punkt, an dem „▶ Karte testen“ (F5)
## den Spieler absetzt — KEIN Spiel-Spawnpunkt (taucht nicht in doc,
## Spawn-Checks/Analysen oder Validator auf, ändert keine Zonen).
##
## Bewusste Entscheidungen dieser Karte:
## - Der Punkt lebt NUR editor-seitig (Sidecar, test_start_store.gd) — das
## .svmap-Format (Validator V1–V16, Client/Server) bleibt UNBERÜHRT.
## - Gespeichert wird nur x/z. Die Höhe kommt IMMER automatisch aus dem
## Terrain an der Stelle (SvmapDocument.terrain_height_at) — Terrain
## formen lässt den Punkt nicht schweben und versinken.
## - Fallback-Reihenfolge F5 (je mit Klartext-Hinweis): gesetzter Test-Start
## → erster Team-Spawnpunkt (Team Blau, wie bisher) → Kartenmitte.
## - Trifft der Klick ein Objekt/Bauteil: landet der Punkt auf dem BODEN
## DARUNTER (nie im Objekt schwebend) — das Tool meldet es im Klartext.
##
## Alles pure Statics (kein UI, kein Node-Zugriff) → headless testbar in
## tests/test_playtest_spawn.gd.
## Kein Punkt gesetzt (Sentinel-Typ für Variant-Vergleiche in main.gd).
## Start-Punkt für den Testlauf auflösen (Fallback-Kette). test_point ist
## Vector3 (nur x/z zählen) oder null. Rückgabe:
## {pos: Vector3 (y = Terrainhöhe), quelle: "test"|"team"|"mitte",
## status: Klartext für die Statuszeile}
static func resolve_start(doc: Dictionary, test_point: Variant) -> Dictionary:
if test_point != null:
var p: Vector3 = test_point
var y := SvmapDocument.terrain_height_at(doc, p.x, p.z)
return {
"pos": Vector3(p.x, y, p.z),
"quelle": "test",
"status": "Start an deinem gesetzten Test-Startpunkt (%.0f, %.0f, Höhe automatisch)." % [p.x, p.z],
}
# Erster Team-Spawnpunkt (Team Blau = „0“) — exakt wie bisher (t_1cdc1a72).
var zones: Dictionary = doc.get("spawn_zones", {})
var team0 = zones.get("0", {})
if team0 is Dictionary:
var points: Array = (team0 as Dictionary).get("points", [])
if not points.is_empty() and points[0] is Dictionary:
var pos = (points[0] as Dictionary).get("pos", [])
if pos is Array and (pos as Array).size() >= 3:
return {
"pos": Vector3(float(pos[0]), float(pos[1]), float(pos[2])),
"quelle": "team",
"status": "Kein Test-Start gesetzt — Start am ersten Team-Blau-Spawnpunkt.",
}
# Kartenmitte aus den Bounds.
var bounds: Dictionary = doc.get("bounds", {})
var mn: Array = bounds.get("min", [-50.0, -10.0, -50.0])
var mx: Array = bounds.get("max", [50.0, 80.0, 50.0])
var cx := (float(mn[0]) + float(mx[0])) * 0.5
var cz := (float(mn[2]) + float(mx[2])) * 0.5
return {
"pos": Vector3(cx, SvmapDocument.terrain_height_at(doc, cx, cz), cz),
"quelle": "mitte",
"status": "Weder Test-Start noch Team-Spawnpunkte — Start in der Kartenmitte.",
}
## Strahl → erster Treffer auf einer AABB (Slab-Methode) oder -1.0.
## Für „Klick trifft Bauteil“ im Editor-Tool und „Fadenkreuz trifft Hindernis“
## im Testlauf (kein Physik-Server im Editor/SubViewport).
## Rückgabe: Distanz t >= 0 (origin + dir * t) oder -1.0 = kein Treffer.
static func ray_aabb(origin: Vector3, dir: Vector3, box: AABB) -> float:
var d := dir
if d.length_squared() < 0.000000001:
return -1.0
d = d.normalized()
var tmin := 0.0
var tmax := INF
for axis in 3:
var o := origin[axis]
var dv := d[axis]
var lo := box.position[axis]
var hi := box.end[axis]
if absf(dv) < 0.0000001:
if o < lo or o > hi:
return -1.0
continue
var t1 := (lo - o) / dv
var t2 := (hi - o) / dv
if t1 > t2:
var tmp := t1
t1 = t2
t2 = tmp
tmin = maxf(tmin, t1)
tmax = minf(tmax, t2)
if tmin > tmax:
return -1.0
return tmin