scopedvr-map-editor/src/ui/panels/bauen_panel.gd
slyphax 961e8c6ff6 Bauen: begehbare Vorlagen (Kaserne, Wachhaus, Lagerraum) per Klick, ein Undo-Schritt
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>
2026-10-25 22:00:49 +01:00

107 lines
4.7 KiB
GDScript

extends "res://src/ui/panels/editor_panel.gd"
## Bauen-Tab: Bauteile für eigene Gebäude (Fundament, Wände, Dach, Treppe). Fertige
## Pack-Objekte (Container, Hangar-Tore, Sperren …) gehören in die Objekt-Bibliothek
## unten, NICHT hierher. Ein Klick auf ein Teil startet
## das Platzieren (Vorschau rastet an Zelle/Kante/Ebene, siehe build_snap.gd); jedes
## gesetzte Teil ist ein normales Objekt und gehört zum laufenden Gebäude (Gruppe).
## Gebäude kopieren/löschen/auswählen läuft über die Knöpfe unten — alles Undo-fähig.
var bau_buttons: Dictionary = {} # oid → Button
var info_label: Label
var _group: ButtonGroup
static func spec() -> Dictionary:
return {"titel": "Bauen", "gruppe": "rechts", "order": 47}
func panel_title() -> String:
return "Bauen"
func build() -> void:
_group = ButtonGroup.new()
_group.allow_unpress = true
add_child(_muted_label("Wähle ein Teil und klicke in die Karte. Wände rasten an die nächste Zellkante, Böden und Dächer in die Zellmitte (Raster 2 m). Klick auf die Oberkante einer Wand stapelt das nächste Geschoss, Klick auf die Seite ersetzt das Teil (z. B. Wand → Tür). R dreht, Esc beendet."))
for gruppe in BauKatalog.GRUPPEN:
add_child(_field_label(str(gruppe["label"])))
add_child(_knopf_raster(gruppe["oids"] as Array))
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"))
info_label = _muted_label("")
add_child(info_label)
add_child(_aktion("🏠 Neues Gebäude beginnen", "Das nächste Teil gehört zu einem neuen Gebäude (neue Gruppe).",
func() -> void: main.call("_bau_neues_gebaeude")))
add_child(_aktion("🔲 Ganzes Gebäude auswählen", "Wählt alle Teile des Gebäudes, zu dem das markierte Teil gehört — zum Verschieben, Drehen, Kopieren.",
func() -> void: main.call("_bau_gebaeude_waehlen")))
add_child(_aktion("📄 Gebäude kopieren", "Kopiert das ganze Gebäude als neues Gebäude (Strg+Z nimmt es zurück).",
func() -> void: main.call("_bau_gebaeude_kopieren")))
add_child(_aktion("🗑 Gebäude löschen", "Löscht alle Teile des Gebäudes (Strg+Z holt es zurück).",
func() -> void: main.call("_bau_gebaeude_loeschen")))
refresh_info()
wrap_in_scroll()
func _knopf_raster(oids: Array) -> GridContainer:
var grid := GridContainer.new()
grid.columns = 2
grid.add_theme_constant_override("h_separation", UIScale.px(8))
grid.add_theme_constant_override("v_separation", UIScale.px(8))
var lib: Dictionary = main.manifest.get("objects", {})
for oid in oids:
var btn := Button.new()
btn.toggle_mode = true
btn.button_group = _group
btn.custom_minimum_size = UIScale.v2(148, 44)
btn.clip_text = true
var poly := int((lib.get(oid, {}) as Dictionary).get("poly", 0))
btn.text = BauKatalog.label(oid)
btn.tooltip_text = "%s — %s\n%d Dreiecke" % [BauKatalog.label(oid),
str((BauKatalog.KINDS[oid] as Dictionary).get("info", "")), poly]
btn.pressed.connect(_on_teil_gedrueckt.bind(str(oid), btn))
bau_buttons[str(oid)] = btn
grid.add_child(btn)
return grid
func _aktion(text: String, tip: String, cb: Callable) -> Button:
var b := Button.new()
b.text = text
b.tooltip_text = tip
b.custom_minimum_size = UIScale.v2(0, 44)
b.pressed.connect(cb)
return b
func _on_teil_gedrueckt(oid: String, btn: Button) -> void:
if not btn.button_pressed:
main.interaction.call("cancel_placement")
return
main.call("_on_bauteil_gewaehlt", oid)
## Teil-Markierung an den laufenden Platzier-Zustand koppeln (Esc/Abbruch löst sie).
func sync_platzierung() -> void:
var aktiv := str(main.interaction.get("place_oid")) if bool(main.interaction.get("placing")) else ""
for oid in bau_buttons:
(bau_buttons[oid] as Button).set_pressed_no_signal(oid == aktiv)
## Info-Zeile: aktuelles Gebäude (Name, Teile, Dreiecke) bzw. Gesamtzahl.
func refresh_info() -> void:
if info_label == null or main == null or main.doc.is_empty():
return
var gebaeude := BauOps.gebaeude_gruppen(main.doc)
var text := "%d Gebäude, %d Bauteile in der Map." % [gebaeude.size(), BauOps.teile_zahl(main.doc)]
var gid := int(main.interaction.get("build_group_id"))
for g in gebaeude:
if int(g["gid"]) == gid:
text = "Aktuell: %s — %d Teile, %d Dreiecke.\n%s" % [g["name"], (g["indizes"] as Array).size(), int(g["dreiecke"]), text]
break
info_label.text = text