Pack-Kaserne/-Checkpoint/-Container sind innen geschlossen; die Vorlagen sind begehbare Gebäude aus bau_*-Teilen. Test beweist per Strahl durch die echte Kollision. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
121 lines
4.9 KiB
GDScript
121 lines
4.9 KiB
GDScript
extends SceneTree
|
|
## Suite: begehbare Gebäude-Vorlagen (Kaserne, Wachhaus, Lagerraum).
|
|
## Ausführen: godot4 --headless --path . --script res://tests/test_bau_vorlagen.gd
|
|
## Beweis „begehbar“: Strahlen durch die ECHTE Trimesh-Kollision aller gesetzten Teile —
|
|
## durch die Tür frei bis in die Raummitte, Wand daneben massiv, Raum hat Boden und Decke.
|
|
|
|
var failures := 0
|
|
var checks := 0
|
|
|
|
|
|
func _init() -> void:
|
|
_run_tests()
|
|
if failures == 0:
|
|
print("ALL OK: %d checks" % checks)
|
|
quit(0)
|
|
else:
|
|
print("FAILED: %d/%d checks" % [failures, checks])
|
|
quit(1)
|
|
|
|
|
|
func _check(cond: bool, label: String) -> void:
|
|
checks += 1
|
|
if cond:
|
|
print(" ok: %s" % label)
|
|
else:
|
|
failures += 1
|
|
print(" FAIL: %s" % label)
|
|
|
|
|
|
## Alle Kollisions-Dreiecke der Objekt-Indizes in Weltkoordinaten.
|
|
func _welt_dreiecke(doc: Dictionary, indizes: Array) -> PackedVector3Array:
|
|
var out := PackedVector3Array()
|
|
var cache := {}
|
|
for i in indizes:
|
|
var o: Dictionary = doc["objects"][i]
|
|
var oid := str(o["id"])
|
|
if not cache.has(oid):
|
|
cache[oid] = BauKatalog.mesh(oid).get_faces()
|
|
var b := Basis(Vector3.UP, deg_to_rad(float(o.get("rot_y_deg", 0.0))))
|
|
var p := Vector3(float(o["pos"][0]), float(o["pos"][1]), float(o["pos"][2]))
|
|
for f in (cache[oid] as PackedVector3Array):
|
|
out.append(b * f + p)
|
|
return out
|
|
|
|
|
|
func _trifft(tris: PackedVector3Array, origin: Vector3, dir: Vector3, laenge: float) -> bool:
|
|
for k in int(tris.size() / 3):
|
|
var t := SelectionMath.ray_tri(origin, dir, tris[k * 3], tris[k * 3 + 1], tris[k * 3 + 2])
|
|
if t >= 0.0 and t <= laenge:
|
|
return true
|
|
return false
|
|
|
|
|
|
func _run_tests() -> void:
|
|
var lib: Dictionary = SvmapIO._manifest_or({})["objects"]
|
|
for key in BauVorlagen.schluessel():
|
|
print("Vorlage ", key)
|
|
var v: Dictionary = BauVorlagen.VORLAGEN[key]
|
|
var doc := SvmapDocument.create_new("V", 256.0, "mittag")
|
|
doc["objects"] = [{"id": "barrier_001", "pos": [40.0, 0.0, 40.0], "scale": 1.0, "rot_y_deg": 0.0}]
|
|
var hist := ActionHistory.new()
|
|
hist.begin(doc)
|
|
var res := BauVorlagen.setze(doc, hist, key, Vector3(60.0, 0.0, 60.0))
|
|
var idx: Array = res["indizes"]
|
|
var nx := int(v["nx"])
|
|
var nz := int(v["nz"])
|
|
var erwartet := nx * nz * 2 + 2 * (nx + nz)
|
|
_check(int(res["uebersprungen"]) == 0, "%s: kein Teil abgelehnt" % key)
|
|
_check(idx.size() == erwartet, "%s: %d Teile (Soll %d)" % [key, idx.size(), erwartet])
|
|
var gruppen := {}
|
|
var alle_in_bib := true
|
|
for i in idx:
|
|
gruppen[GroupOps.group_id(doc["objects"][i])] = true
|
|
alle_in_bib = alle_in_bib and lib.has(str(doc["objects"][i]["id"]))
|
|
_check(gruppen.size() == 1 and gruppen.has(int(res["gid"])), "%s: alle Teile in EINER neuen Gruppe" % key)
|
|
_check(alle_in_bib, "%s: alle Teile stehen in der Bibliothek (Server/Client kennen sie)" % key)
|
|
_check(SvmapIO.validate_map(_signiert(doc)).is_empty(), "%s: Map mit Vorlage besteht die Validierung" % key)
|
|
|
|
# begehbar: Tür frei, Wand massiv
|
|
var tris := _welt_dreiecke(doc, idx)
|
|
var r := BauKatalog.RASTER_M
|
|
var ox := roundf((60.0 - nx * r * 0.5) / r) * r
|
|
var oz := roundf((60.0 - nz * r * 0.5) / r) * r
|
|
var tuer_x := ox + r * (int(v["tuer_vorne"]) + 0.5)
|
|
var mitte_z := oz + nz * r * 0.5
|
|
var laenge := mitte_z - (oz - 3.0)
|
|
for h in [0.5, 1.0, 1.8, 2.1]:
|
|
for dx in [-0.4, 0.0, 0.4]:
|
|
_check(not _trifft(tris, Vector3(tuer_x + dx, h, oz - 3.0), Vector3(0, 0, 1), laenge),
|
|
"%s: Tür frei bei Höhe %.1f, Versatz %.1f" % [key, h, dx])
|
|
# daneben (andere Zelle der Vorderwand) oder Seitenwand: massiv
|
|
var wand_x := ox + r * (1 - int(v["tuer_vorne"]) + 0.5) if nx > 1 else ox
|
|
_check(_trifft(tris, Vector3(wand_x, 1.0, oz - 3.0), Vector3(0, 0, 1), laenge),
|
|
"%s: Wand neben der Tür ist massiv" % key)
|
|
# Seitenwand (Zelle j=0 ist ohne Fenster)
|
|
_check(_trifft(tris, Vector3(ox - 3.0, 1.0, oz + r * 0.5), Vector3(1, 0, 0), 3.0 + nx * r * 0.5),
|
|
"%s: Seitenwand ist massiv" % key)
|
|
# Raum hat Boden (y≈0.2) und Decke (≥ 2.5 m Stehhöhe)
|
|
var cx := ox + nx * r * 0.5 + 0.3
|
|
_check(_trifft(tris, Vector3(cx, 2.0, mitte_z), Vector3.DOWN, 2.0), "%s: Boden unter dem Innenraum" % key)
|
|
_check(not _trifft(tris, Vector3(cx, 0.25, mitte_z), Vector3.UP, 2.4), "%s: ≥ 2,4 m Stehhöhe über dem Boden" % key)
|
|
_check(_trifft(tris, Vector3(cx, 0.25, mitte_z), Vector3.UP, 3.3), "%s: Dach schließt den Raum" % key)
|
|
# Hintertür (Kaserne): frei bis zur Raummitte von hinten
|
|
if int(v["tuer_hinten"]) >= 0:
|
|
var hx := ox + r * (int(v["tuer_hinten"]) + 0.5)
|
|
var z1 := oz + nz * r
|
|
_check(not _trifft(tris, Vector3(hx, 1.0, z1 + 3.0), Vector3(0, 0, -1), z1 + 3.0 - mitte_z),
|
|
"%s: Hintertür frei" % key)
|
|
|
|
# Undo/Redo: ein Schritt nimmt das ganze Gebäude zurück
|
|
var n_nach := (doc["objects"] as Array).size()
|
|
hist.undo()
|
|
_check((doc["objects"] as Array).size() == 1, "%s: ein Undo entfernt das ganze Gebäude" % key)
|
|
hist.redo()
|
|
_check((doc["objects"] as Array).size() == n_nach, "%s: Redo bringt es zurück" % key)
|
|
|
|
|
|
func _signiert(doc: Dictionary) -> Dictionary:
|
|
var d := doc.duplicate(true)
|
|
SvmapIO.sign_map(d)
|
|
return d
|