scopedvr-map-core/ui/panels/bauen_panel.gd
2026-11-01 19:31:33 +01:00

118 lines
5.2 KiB
GDScript

extends "res://core/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 geschoss_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 (oder Bild↑/Bild↓: festes Geschoss, dann sitzen Decken mitten im Raum), 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("Höhe / Geschoss"))
geschoss_label = _muted_label(main.interaction.call("bau_geschoss_text"))
add_child(geschoss_label)
var hoehe := HBoxContainer.new()
hoehe.add_theme_constant_override("separation", UIScale.px(8))
for spec in [["▲ Höher (Bild↑)", 1], ["▼ Tiefer (Bild↓)", -1]]:
var hb := _aktion(str(spec[0]), "Festes Geschoss wählen: Böden und Decken sitzen dann überall im Raum auf dieser Höhe (je 3 m). Ganz unten = automatisch.",
func() -> void:
main.interaction.call("bau_geschoss_aendern", int(spec[1]))
geschoss_label.text = main.interaction.call("bau_geschoss_text"))
hb.size_flags_horizontal = Control.SIZE_EXPAND_FILL
hoehe.add_child(hb)
add_child(hoehe)
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)
if geschoss_label != null:
geschoss_label.text = main.interaction.call("bau_geschoss_text")
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