scopedvr-map-editor/src/ui/panels/terrain_panel.gd

279 lines
11 KiB
GDScript

extends "res://src/ui/panels/editor_panel.gd"
## Terrain-Werkzeug-Panel (aus main.gd ausgezogen, Karte t_eaccbfed U1 —
## Logik unverändert, Karte t_9aba04f1 + t_603ec1a8): großer Modus-Button +
## Pinsel-Modi, Material-Palette, Größe/Stärke-Regler. Werkzeug-Ein/Aus bleibt
## im Koordinator (main.gd _enter/_leave terrain), das Panel hält nur die
## Widgets und die Regler-Verdrahtung.
var terrain_edit_btn: Button
var terrain_panel: VBoxContainer
var terrain_mode_raise: Button
var terrain_mode_lower: Button
var terrain_mode_smooth: Button
var terrain_mode_paint: Button
var terrain_palette: VBoxContainer
var terrain_mat_buttons: Array = [] # Gras/Sand/Fels (ButtonGroup)
var terrain_size_btn_small: Button
var terrain_size_btn_medium: Button
var terrain_size_btn_large: Button
var terrain_radius_slider: HSlider
var terrain_radius_label: Label
var terrain_strength_slider: HSlider
var terrain_strength_label: Label
# --- Bauen (Karte t_97ea59c5) — im selben Werkzeug-Tab (aus main.gd ausgezogen)
var build_edit_btn: Button
var build_panel: VBoxContainer
var build_kind_buttons: Dictionary = {} # kind_id → Button
static func spec() -> Dictionary:
return {"titel": "Terrain", "gruppe": "rechts", "order": 40}
func build() -> void:
terrain_edit_btn = Button.new()
terrain_edit_btn.text = "⛰ Terrain formen"
terrain_edit_btn.custom_minimum_size = UIScale.v2(0, 48)
terrain_edit_btn.toggle_mode = true
terrain_edit_btn.tooltip_text = "Boden heben, senken und glätten — direkt in der Karte malen"
terrain_edit_btn.pressed.connect(_on_terrain_btn_pressed)
add_child(terrain_edit_btn)
terrain_panel = VBoxContainer.new()
terrain_panel.add_theme_constant_override("separation", UIScale.px(6))
terrain_panel.visible = false
add_child(terrain_panel)
terrain_panel.add_child(_field_label("Pinsel-Werkzeug:"))
var mode_row := HBoxContainer.new()
mode_row.add_theme_constant_override("separation", UIScale.px(6))
terrain_panel.add_child(mode_row)
var mode_group := ButtonGroup.new()
terrain_mode_raise = _terrain_mode_button("Heben ⬆", mode_group)
terrain_mode_lower = _terrain_mode_button("Senken ⬇", mode_group)
terrain_mode_smooth = _terrain_mode_button("Glätten ≈", mode_group)
terrain_mode_paint = _terrain_mode_button("Malen 🖌", mode_group)
terrain_mode_raise.button_pressed = true
terrain_mode_raise.toggled.connect(_on_terrain_mode_changed)
terrain_mode_lower.toggled.connect(_on_terrain_mode_changed)
terrain_mode_smooth.toggled.connect(_on_terrain_mode_changed)
terrain_mode_paint.toggled.connect(_on_terrain_mode_changed)
mode_row.add_child(terrain_mode_raise)
mode_row.add_child(terrain_mode_lower)
mode_row.add_child(terrain_mode_smooth)
mode_row.add_child(terrain_mode_paint)
# Material-Palette (Karte t_603ec1a8): nur sichtbar im Malen-Modus.
terrain_palette = VBoxContainer.new()
terrain_palette.add_theme_constant_override("separation", UIScale.px(4))
terrain_palette.visible = false
terrain_panel.add_child(terrain_palette)
terrain_palette.add_child(_field_label("Material:"))
var mat_row := HBoxContainer.new()
mat_row.add_theme_constant_override("separation", UIScale.px(6))
terrain_palette.add_child(mat_row)
var mat_group := ButtonGroup.new()
var mat_labels := ["🌿 Gras", "🏖 Sand", "🪨 Fels"]
for mat_id in SvmapIO.MATERIAL_IDS.size():
var mat_btn := Button.new()
mat_btn.text = str(mat_labels[mat_id])
mat_btn.custom_minimum_size = UIScale.v2(0, 40)
mat_btn.toggle_mode = true
mat_btn.button_group = mat_group
mat_btn.tooltip_text = "Malen: %s" % str(SvmapIO.MATERIAL_IDS[mat_id])
if mat_id == 0:
mat_btn.button_pressed = true
mat_btn.toggled.connect(_on_terrain_material_changed.bind(mat_id))
terrain_mat_buttons.append(mat_btn)
mat_row.add_child(mat_btn)
# Pinselgrößen-Presets (Karte t_603ec1a8): drei große Buttons + freier Slider.
terrain_panel.add_child(_field_label("Pinselgröße:"))
var size_row := HBoxContainer.new()
size_row.add_theme_constant_override("separation", UIScale.px(6))
terrain_panel.add_child(size_row)
var size_group := ButtonGroup.new()
terrain_size_btn_small = _terrain_size_button("Klein\n(4 m)", size_group, 4.0)
terrain_size_btn_medium = _terrain_size_button("Mittel\n(12 m)", size_group, 12.0)
terrain_size_btn_large = _terrain_size_button("Groß\n(32 m)", size_group, 32.0)
# Kein Signal: Der Handler greift auf terrain_radius_slider zu, der erst
# unten erzeugt wird — beim Panel-Bau wäre das ein Crash (GUI-Start-Bug
# 15.10.). Der Slider startet unten passend mit 12 m (= Mittel).
terrain_size_btn_medium.set_pressed_no_signal(true)
size_row.add_child(terrain_size_btn_small)
size_row.add_child(terrain_size_btn_medium)
size_row.add_child(terrain_size_btn_large)
terrain_radius_label = _field_label("Pinsel-Größe: 12 m")
terrain_panel.add_child(terrain_radius_label)
terrain_radius_slider = HSlider.new()
terrain_radius_slider.min_value = 2.0
terrain_radius_slider.max_value = 64.0
terrain_radius_slider.step = 1.0
terrain_radius_slider.value = 12.0
terrain_radius_slider.custom_minimum_size = UIScale.v2(0, 32)
terrain_radius_slider.value_changed.connect(_on_terrain_radius_changed)
terrain_panel.add_child(terrain_radius_slider)
terrain_strength_label = _field_label("Stärke: 8 m pro Sekunde")
terrain_panel.add_child(terrain_strength_label)
terrain_strength_slider = HSlider.new()
terrain_strength_slider.min_value = 1.0
terrain_strength_slider.max_value = 20.0
terrain_strength_slider.step = 0.5
terrain_strength_slider.value = 8.0
terrain_strength_slider.custom_minimum_size = UIScale.v2(0, 32)
terrain_strength_slider.value_changed.connect(_on_terrain_strength_changed)
terrain_panel.add_child(terrain_strength_slider)
terrain_panel.add_child(_muted_label(
"Linksklick-Ziehen formt bzw. malt (je nach Werkzeug), Strg+Linksklick macht das Gegenteil — im Malen-Modus malt Strg+Klick Gras zurück. "
+ "Jeder Strich ist EIN Rückgängig-Schritt (Strg+Z). Rechte Maustaste = Flug, ESC beendet den Terrain-Modus."))
# --- Bauen (Karte t_97ea59c5): Modular-Builder-Pieces im App-Skelett ---
# (aus main.gd ausgezogen — liegt mit im Terrain-Tab, Werkzeug-Zusammenhang)
build_edit_btn = Button.new()
build_edit_btn.text = "🏠 Bauen (Häuser)"
build_edit_btn.custom_minimum_size = UIScale.v2(0, 48)
build_edit_btn.toggle_mode = true
build_edit_btn.tooltip_text = "Fundamente, Wände, Dächer, Treppen und Türen bauen — im 2-Meter-Raster, wie Bauklötze"
build_edit_btn.pressed.connect(_on_build_btn_pressed)
add_child(build_edit_btn)
build_panel = VBoxContainer.new()
build_panel.add_theme_constant_override("separation", UIScale.px(6))
build_panel.visible = false
add_child(build_panel)
build_panel.add_child(_field_label("Bauteil:"))
var build_grid := GridContainer.new()
build_grid.columns = 2
build_grid.add_theme_constant_override("h_separation", UIScale.px(6))
build_grid.add_theme_constant_override("v_separation", UIScale.px(6))
build_panel.add_child(build_grid)
var build_group := ButtonGroup.new()
for kind_id in BuildOps.KINDS.keys():
var kind_btn := Button.new()
kind_btn.text = str(BuildOps.KINDS[kind_id]["label"])
kind_btn.toggle_mode = true
kind_btn.button_group = build_group
kind_btn.custom_minimum_size = UIScale.v2(148, 44)
kind_btn.clip_text = true
kind_btn.tooltip_text = "%s — %s" % [str(BuildOps.KINDS[kind_id]["label"]), str(BuildOps.KINDS[kind_id].get("info", ""))]
kind_btn.pressed.connect(_on_build_kind_pressed.bind(str(kind_id)))
build_kind_buttons[str(kind_id)] = kind_btn
build_grid.add_child(kind_btn)
(build_kind_buttons["fundament"] as Button).set_pressed_no_signal(true)
build_panel.add_child(_muted_label(
"Linksklick setzt das Bauteil aufs 2-m-Raster — auch AUF andere Teile "
+ "(Wände aufs Fundament stapeln, Dach auf die Wände). Jedes Bauteil ist "
+ "EIN Rückgängig-Schritt (Strg+Z). ESC beendet den Bauen-Modus."))
func _on_build_btn_pressed() -> void:
if build_edit_btn.button_pressed:
main.call("_set_tool_mode", "bauen")
else:
main.call("_set_tool_mode", "objects")
func _on_build_kind_pressed(kind_id: String) -> void:
main.call("_on_build_kind_pressed", kind_id)
func _on_terrain_btn_pressed() -> void:
if terrain_edit_btn.button_pressed:
main.call("_set_tool_mode", "terrain")
else:
main.call("_set_tool_mode", "objects")
func _terrain_mode_button(text: String, group: ButtonGroup) -> Button:
var btn := Button.new()
btn.text = text
btn.toggle_mode = true
btn.button_group = group
btn.custom_minimum_size = UIScale.v2(0, 40)
return btn
func _selected_terrain_mode() -> int:
if terrain_mode_lower.button_pressed:
return TerrainField.Mode.LOWER
if terrain_mode_smooth.button_pressed:
return TerrainField.Mode.SMOOTH
if terrain_mode_paint.button_pressed:
return TerrainField.Mode.PAINT
return TerrainField.Mode.RAISE
## Mode-Buttons (ButtonGroup) → Pinsel-Modus; Palette nur im Malen-Modus.
func _on_terrain_mode_changed(_pressed: bool) -> void:
if terrain_palette != null:
terrain_palette.visible = _selected_terrain_mode() == TerrainField.Mode.PAINT
if main.terrain_tool != null:
main.terrain_tool.call("set_brush_mode", _selected_terrain_mode())
## Material-Palette (Karte t_603ec1a8): gewähltes Material an den Pinsel.
func _on_terrain_material_changed(pressed: bool, mat_id: int) -> void:
if pressed and main.terrain_tool != null:
main.terrain_tool.call("set_paint_material", mat_id)
## Pinselgrößen-Presets (Karte t_603ec1a8): setzen den Radius-Slider
## (der Label-Text + Ring-Optik hängen am value_changed-Signal).
func _terrain_size_button(text: String, group: ButtonGroup, radius_m: float) -> Button:
var btn := Button.new()
btn.text = text
btn.toggle_mode = true
btn.button_group = group
btn.custom_minimum_size = UIScale.v2(0, 48)
btn.tooltip_text = "Pinselradius %d m" % int(radius_m)
btn.toggled.connect(_on_terrain_size_preset.bind(radius_m))
return btn
func _on_terrain_size_preset(pressed: bool, radius_m: float) -> void:
# Null-Check als Schutznetz: beim Panel-Bau existiert der Slider noch
# nicht (Programmatic-Pressed darf den Handler nie früh treffen).
if pressed and terrain_radius_slider != null:
terrain_radius_slider.value = radius_m
func _on_terrain_radius_changed(value: float) -> void:
terrain_radius_label.text = "Pinsel-Größe: %d m" % int(value)
if main.terrain_tool != null:
main.terrain_tool.call("set_radius", value)
func _on_terrain_strength_changed(value: float) -> void:
terrain_strength_label.text = "Stärke: %.1f m pro Sekunde" % value
if main.terrain_tool != null:
main.terrain_tool.call("set_strength", value)
## Terrain-Pinsel-Größe per [ / ] (Karte t_55257792): durch die 3 Presets
## Klein/Mittel/Groß zyklisch — der Preset-Button wird mit gesetzt (set_pressed
## feuert toggled → Slider + Label + Pinssel-Ring wandern mit).
## (Aus main.gd ausgezogen, Logik unverändert; Koordinator ruft weiter.)
func terrain_brush_size_step(up: bool) -> void:
if terrain_radius_slider == null:
return
var next := MoveMath.brush_next_preset(float(terrain_radius_slider.value), up)
var btn: Button = null
match next:
4.0:
btn = terrain_size_btn_small
12.0:
btn = terrain_size_btn_medium
32.0:
btn = terrain_size_btn_large
if btn != null:
btn.set_pressed(true) # feuert _on_terrain_size_preset → Slider/Label
else:
terrain_radius_slider.value = next
main.terrain_tool.call("set_radius", next)
main.call("_set_status", "Pinsel-Größe: %d m (mit [ / ] durch Klein/Mittel/Groß zyklisch)." % int(next))