Revert "Bauen: begehbare Vorlagen (Kaserne, Wachhaus, Lagerraum) per Klick, ein Undo-Schritt"
This reverts commit 961e8c6ff6.
This commit is contained in:
parent
961e8c6ff6
commit
5e32c771fc
7 changed files with 0 additions and 277 deletions
|
|
@ -30,25 +30,6 @@ Ein Bauteil ist ein **ganz normales Bibliotheks-Objekt** (`bau_*`, Kategorie
|
||||||
| Dach & Giebel (groß 6 m) | Giebelwand 6 m, Satteldach 6×6, Pyramidendach 6×6 (Spitze 3 m) |
|
| Dach & Giebel (groß 6 m) | Giebelwand 6 m, Satteldach 6×6, Pyramidendach 6×6 (Spitze 3 m) |
|
||||||
| Treppe | Treppe 4 × 2 m, 3 m hoch (15 Stufen) |
|
| Treppe | Treppe 4 × 2 m, 3 m hoch (15 Stufen) |
|
||||||
|
|
||||||
## Begehbare Vorlagen
|
|
||||||
|
|
||||||
Die Pack-Gebäude Kaserne, Checkpoint und Container sind innen geschlossen
|
|
||||||
(einseitige Wände, Türen nur aufgemalt) — Spieler laufen dagegen, nicht hinein. Statt die
|
|
||||||
Pack-Modelle zu verbiegen, setzt der Bauen-Tab unter „Begehbare Vorlagen“ per Klick ein
|
|
||||||
fertiges Gebäude aus Bauteilen in die Bildmitte (`src/editor/bau_vorlagen.gd`, ein
|
|
||||||
Undo-Schritt, danach als Gruppe markiert):
|
|
||||||
|
|
||||||
| Vorlage | Größe | Ausstattung |
|
|
||||||
| --- | --- | --- |
|
|
||||||
| Wachhaus (Checkpoint) | 4 × 4 m | Tür vorn, Fenster seitlich, Flachdach |
|
|
||||||
| Kaserne | 6 × 12 m | Tür vorn + hinten, Fenster an den Längsseiten, Flachdach |
|
|
||||||
| Lagerraum (Container-Größe) | 4 × 6 m | Tür vorn, Schießscharten seitlich |
|
|
||||||
|
|
||||||
Alles sind normale `bau_*`-Objekte (Trimesh, Türen/Fenster echte Löcher), Quest-tauglich:
|
|
||||||
kein Innenausbau, ein Draw-Call je Teil. `tests/test_bau_vorlagen.gd` beweist per Strahl
|
|
||||||
durch die echte Kollision: Tür frei (0,5–2,1 m), Wand massiv, Boden und Dach da, ≥ 2,4 m
|
|
||||||
Stehhöhe, ein Undo nimmt das Gebäude zurück.
|
|
||||||
|
|
||||||
Fertige Pack-Objekte (Container, Hangar-Tore, Betonsperren) stehen NICHT im Bauen-Tab,
|
Fertige Pack-Objekte (Container, Hangar-Tore, Betonsperren) stehen NICHT im Bauen-Tab,
|
||||||
sondern wie alle Objekte in der Objekt-Bibliothek unten.
|
sondern wie alle Objekte in der Objekt-Bibliothek unten.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,110 +0,0 @@
|
||||||
extends RefCounted
|
|
||||||
class_name BauVorlagen
|
|
||||||
## Begehbare Gebäude-Vorlagen aus Bauteilen (Karte 25.10.: Kaserne, Checkpoint und
|
|
||||||
## Container aus dem Pack sind GESCHLOSSENE Hüllen — einseitige Wände, Türen nur
|
|
||||||
## aufgemalt, kein Innenraum; begehbar werden sie nur, wenn man sie neu baut).
|
|
||||||
##
|
|
||||||
## Eine Vorlage setzt ein ganzes Gebäude mit EINEM Klick (ein Undo-Schritt) als neue
|
|
||||||
## Gruppe. Jedes Teil läuft über dieselbe Snap-Logik wie ein Handklick (BuildSnap) und
|
|
||||||
## ist danach ein normales Objekt — verschieb-, dreh-, kopier- und löschbar, Türen und
|
|
||||||
## Fenster sind echte Löcher in der Trimesh-Kollision, Quest-tauglich (Flachdach aus
|
|
||||||
## Bodenplatten, keine Innenmöbel, ein Draw-Call je Teil).
|
|
||||||
##
|
|
||||||
## Zellen sind 2×2 m (BauKatalog.RASTER_M); die Vorlage steht mit der nord-west-Ecke
|
|
||||||
## auf der Zellecke unter dem Zielpunkt. Vorderseite = Seite mit kleinem z (Tür nach -Z).
|
|
||||||
|
|
||||||
const VORLAGEN := {
|
|
||||||
"wachhaus": {
|
|
||||||
"label": "Wachhaus (Checkpoint)", "nx": 2, "nz": 2, "tuer_vorne": 0, "tuer_hinten": -1,
|
|
||||||
"info": "4 × 4 m, Tür vorn, Fenster seitlich, Flachdach — Ersatz für den geschlossenen Pack-Checkpoint",
|
|
||||||
},
|
|
||||||
"kaserne": {
|
|
||||||
"label": "Kaserne", "nx": 3, "nz": 6, "tuer_vorne": 1, "tuer_hinten": 1,
|
|
||||||
"info": "6 × 12 m Schlafsaal, Tür vorn und hinten, Fenster an den Längsseiten, Flachdach",
|
|
||||||
},
|
|
||||||
"lagerraum": {
|
|
||||||
"label": "Lagerraum (Container-Größe)", "nx": 2, "nz": 3, "tuer_vorne": 0, "tuer_hinten": -1,
|
|
||||||
"info": "4 × 6 m, Tür vorn, Schießscharten an den Seiten — begehbarer Ersatz für den Container",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static func schluessel() -> Array:
|
|
||||||
return VORLAGEN.keys()
|
|
||||||
|
|
||||||
|
|
||||||
static func label(key: String) -> String:
|
|
||||||
return str((VORLAGEN.get(key, {}) as Dictionary).get("label", key))
|
|
||||||
|
|
||||||
|
|
||||||
## Klick-Schritte [oid, Treffpunkt(Vector3), Drehung°, (optional) true = Höhe fest] für die Vorlage `key` mit der
|
|
||||||
## Nordwest-Ecke bei (ox, oz) auf Höhe y. Reihenfolge = Bauordnung (Boden → Wände → Dach).
|
|
||||||
static func schritte(key: String, ox: float, oz: float, y: float) -> Array:
|
|
||||||
var v: Dictionary = VORLAGEN[key]
|
|
||||||
var nx := int(v["nx"])
|
|
||||||
var nz := int(v["nz"])
|
|
||||||
var r := BauKatalog.RASTER_M
|
|
||||||
var x1 := ox + nx * r
|
|
||||||
var z1 := oz + nz * r
|
|
||||||
var out: Array = []
|
|
||||||
for i in nx:
|
|
||||||
for j in nz:
|
|
||||||
out.append(["bau_fundament", Vector3(ox + r * (i + 0.5), y, oz + r * (j + 0.5)), 0.0])
|
|
||||||
var seite_oid := "bau_wand_scharte" if key == "lagerraum" else "bau_wand_fenster"
|
|
||||||
for i in nx: # Vorder- und Rückwand (entlang X)
|
|
||||||
var cx := ox + r * (i + 0.5)
|
|
||||||
out.append([_wand(i, int(v["tuer_vorne"]), "bau_wand_fenster" if nx > 2 and i != 1 else "bau_wand"),
|
|
||||||
Vector3(cx, y, oz + 0.1), 0.0])
|
|
||||||
out.append([_wand(i, int(v["tuer_hinten"]), "bau_wand"), Vector3(cx, y, z1 - 0.1), 0.0])
|
|
||||||
for j in nz: # Seitenwände (entlang Z): jede zweite Zelle mit Fenster/Scharte
|
|
||||||
var cz := oz + r * (j + 0.5)
|
|
||||||
var s := seite_oid if j % 2 == 1 else "bau_wand"
|
|
||||||
out.append([s, Vector3(ox + 0.1, y, cz), 90.0])
|
|
||||||
out.append([s, Vector3(x1 - 0.1, y, cz), 90.0])
|
|
||||||
# Flachdach: Bodenplatten auf die Wandoberkante
|
|
||||||
var oben := y + BauKatalog.WAND_H
|
|
||||||
for i in nx:
|
|
||||||
for j in nz:
|
|
||||||
out.append(["bau_boden", Vector3(ox + r * (i + 0.5), oben, oz + r * (j + 0.5)), 0.0, true])
|
|
||||||
return out
|
|
||||||
|
|
||||||
|
|
||||||
static func _wand(i: int, tuer_slot: int, sonst: String) -> String:
|
|
||||||
return "bau_wand_tuer" if i == tuer_slot else sonst
|
|
||||||
|
|
||||||
|
|
||||||
## Vorlage setzen: neue Gruppe, alle Teile über BuildSnap, EIN Undo-Eintrag.
|
|
||||||
## mitte = Zielpunkt (Bodenhöhe = mitte.y). history darf null sein (Tests).
|
|
||||||
## Rückgabe: {gid, indizes (Array[int] der neuen Objekte), uebersprungen (int)}.
|
|
||||||
static func setze(doc: Dictionary, history: Variant, key: String, mitte: Vector3) -> Dictionary:
|
|
||||||
var v: Dictionary = VORLAGEN[key]
|
|
||||||
var r := BauKatalog.RASTER_M
|
|
||||||
# Mitte der Grundfläche auf den Zielpunkt, Ecke auf die Zellecke runden
|
|
||||||
var ox := roundf((mitte.x - int(v["nx"]) * r * 0.5) / r) * r
|
|
||||||
var oz := roundf((mitte.z - int(v["nz"]) * r * 0.5) / r) * r
|
|
||||||
var vorher: Dictionary = history.snapshot(["objects", "group_names"]) if history != null else {}
|
|
||||||
var gid := GroupOps.next_group_id(doc)
|
|
||||||
GroupState.set_group_name(doc, gid, "%s %d" % [label(key), BauOps.gebaeude_zahl(doc) + 1])
|
|
||||||
var objects: Array = doc.get("objects", [])
|
|
||||||
var start := objects.size()
|
|
||||||
var gelaende := func(_x: float, _z: float) -> float: return mitte.y
|
|
||||||
var uebersprungen := 0
|
|
||||||
for st in schritte(key, ox, oz, mitte.y):
|
|
||||||
var teile := BuildSnap.sammle_teile(doc)
|
|
||||||
var snap := BuildSnap.berechne(str(st[0]), st[1] as Vector3, float(st[2]), teile, gelaende)
|
|
||||||
# Dachplatten über Zellen ohne Wand darunter: BuildSnap fände dort nur das
|
|
||||||
# Fundament als Ebene → Höhe der Vorlage erzwingen (= Wandoberkante).
|
|
||||||
if bool(snap["ok"]) and st.size() > 3 and bool(st[3]):
|
|
||||||
var p: Vector3 = snap["pos"]
|
|
||||||
snap["pos"] = Vector3(p.x, (st[1] as Vector3).y, p.z)
|
|
||||||
if bool(snap["ok"]):
|
|
||||||
BauOps.setze_teil(doc, str(st[0]), snap, gid, SymmetryCore.Mode.OFF)
|
|
||||||
else:
|
|
||||||
uebersprungen += 1
|
|
||||||
objects = doc.get("objects", [])
|
|
||||||
var indizes: Array = []
|
|
||||||
for i in range(start, objects.size()):
|
|
||||||
indizes.append(i)
|
|
||||||
if history != null:
|
|
||||||
history.commit("%s gebaut" % label(key), vorher, history.snapshot(["objects", "group_names"]))
|
|
||||||
return {"gid": gid, "indizes": indizes, "uebersprungen": uebersprungen}
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
uid://c47romnnk250k
|
|
||||||
|
|
@ -1794,25 +1794,6 @@ func _bau_neues_gebaeude() -> void:
|
||||||
_set_status("Neues Gebäude: das nächste Bauteil beginnt eine neue Gruppe.")
|
_set_status("Neues Gebäude: das nächste Bauteil beginnt eine neue Gruppe.")
|
||||||
|
|
||||||
|
|
||||||
## Begehbare Vorlage (Kaserne, Wachhaus, Lagerraum) in die Bildmitte setzen —
|
|
||||||
## EIN Undo-Schritt, danach ist das ganze Gebäude markiert (verschieben/drehen).
|
|
||||||
func _bau_vorlage_setzen(key: String) -> void:
|
|
||||||
if doc.is_empty():
|
|
||||||
return
|
|
||||||
if tool_mode != "objects":
|
|
||||||
_set_tool_mode("objects")
|
|
||||||
var ziel: Vector3 = camera.get("target")
|
|
||||||
ziel.y = SvmapDocument.terrain_height_at(doc, ziel.x, ziel.z)
|
|
||||||
var res := BauVorlagen.setze(doc, history, key, ziel)
|
|
||||||
_after_structure_change()
|
|
||||||
interaction.call("select_many", res["indizes"], true)
|
|
||||||
var hinweis := ""
|
|
||||||
if int(res["uebersprungen"]) > 0:
|
|
||||||
hinweis = " (%d Teile passten nicht ins Raster und fehlen)" % int(res["uebersprungen"])
|
|
||||||
_set_status("%s gesetzt (%d Teile)%s — Ziehen verschiebt, R dreht, Strg+Z nimmt es zurück." % [
|
|
||||||
BauVorlagen.label(key), (res["indizes"] as Array).size(), hinweis])
|
|
||||||
|
|
||||||
|
|
||||||
## Alle Teile des Gebäudes wählen, zu dem die Auswahl gehört.
|
## Alle Teile des Gebäudes wählen, zu dem die Auswahl gehört.
|
||||||
func _bau_gebaeude_waehlen() -> bool:
|
func _bau_gebaeude_waehlen() -> bool:
|
||||||
var sel: Array = interaction.selected_indices
|
var sel: Array = interaction.selected_indices
|
||||||
|
|
|
||||||
|
|
@ -27,12 +27,6 @@ func build() -> void:
|
||||||
add_child(_field_label(str(gruppe["label"])))
|
add_child(_field_label(str(gruppe["label"])))
|
||||||
add_child(_knopf_raster(gruppe["oids"] as Array))
|
add_child(_knopf_raster(gruppe["oids"] as Array))
|
||||||
add_child(_separator())
|
add_child(_separator())
|
||||||
add_child(_header_label("Begehbare Vorlagen"))
|
|
||||||
add_child(_muted_label("Pack-Kaserne, -Checkpoint und -Container sind innen geschlossen. Diese Vorlagen setzen ein begehbares Gebäude mit einem Klick in die Bildmitte (Tür, Fenster, Dach, Strg+Z nimmt alles zurück)."))
|
|
||||||
for key in BauVorlagen.schluessel():
|
|
||||||
add_child(_aktion("🏠 %s" % BauVorlagen.label(key), str(BauVorlagen.VORLAGEN[key]["info"]),
|
|
||||||
func() -> void: main.call("_bau_vorlage_setzen", key)))
|
|
||||||
add_child(_separator())
|
|
||||||
add_child(_header_label("Gebäude"))
|
add_child(_header_label("Gebäude"))
|
||||||
info_label = _muted_label("")
|
info_label = _muted_label("")
|
||||||
add_child(info_label)
|
add_child(info_label)
|
||||||
|
|
|
||||||
|
|
@ -1,121 +0,0 @@
|
||||||
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
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
uid://dj0e5rfsirlrc
|
|
||||||
Loading…
Reference in a new issue