282 lines
12 KiB
GDScript
282 lines
12 KiB
GDScript
extends "res://core/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_mode_flatten: Button
|
||
var terrain_mode_ramp: Button
|
||
var terrain_falloff_label: Label
|
||
var terrain_falloff_slider: HSlider
|
||
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
|
||
var terrain_paint_hint: Label # Malen: Hinweis auf die Raster-Feinheit (24.10.)
|
||
var terrain_weich_label: Label # Malen: Kante hart ↔ weich (25.10.)
|
||
var terrain_weich_slider: HSlider
|
||
|
||
|
||
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 formen: heben, senken, glätten, einebnen, Rampe (F1)"
|
||
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(8)) # U7-Raster
|
||
add_child(terrain_panel)
|
||
|
||
terrain_panel.add_child(_field_label("Pinsel:"))
|
||
var mode_row := GridContainer.new()
|
||
mode_row.columns = 3
|
||
mode_row.add_theme_constant_override("h_separation", UIScale.px(8)) # U7-Raster
|
||
mode_row.add_theme_constant_override("v_separation", UIScale.px(8))
|
||
terrain_panel.add_child(mode_row)
|
||
var mode_group := ButtonGroup.new()
|
||
terrain_mode_raise = _terrain_mode_button("Heben ⬆", mode_group,
|
||
"Pinsel-Modus Heben: Boden unter dem Pinsel anheben (Taste 1)")
|
||
terrain_mode_lower = _terrain_mode_button("Senken ⬇", mode_group,
|
||
"Pinsel-Modus Senken: Boden unter dem Pinsel absenken (Taste 2)")
|
||
terrain_mode_smooth = _terrain_mode_button("Glätten ≈", mode_group,
|
||
"Pinsel-Modus Glätten: Höhenunterschiede verrutschen (Taste 3)")
|
||
terrain_mode_paint = _terrain_mode_button("Malen 🖌", mode_group,
|
||
"Pinsel-Modus Malen: Material auf den Boden malen (Taste 4)")
|
||
terrain_mode_flatten = _terrain_mode_button("Einebnen ▬", mode_group,
|
||
"Pinsel-Modus Einebnen: zieht den Boden auf die Höhe, die beim Strichstart unter dem Cursor war")
|
||
terrain_mode_ramp = _terrain_mode_button("Rampe ◢", mode_group,
|
||
"Pinsel-Modus Rampe: Klick = Start, ziehen und loslassen = Ende — Höhe läuft gleichmäßig dazwischen (Breite = Pinselgröße)")
|
||
terrain_mode_raise.button_pressed = true
|
||
terrain_mode_flatten.toggled.connect(_on_terrain_mode_changed)
|
||
terrain_mode_ramp.toggled.connect(_on_terrain_mode_changed)
|
||
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_flatten)
|
||
mode_row.add_child(terrain_mode_ramp)
|
||
# Malen hat seit 25.10. einen eigenen Tab (malen_panel.gd); der Modus-Knopf
|
||
# bleibt unsichtbar in der ButtonGroup (Taste 4, Malen-Tab-Knopf).
|
||
terrain_mode_paint.visible = false
|
||
terrain_panel.add_child(terrain_mode_paint)
|
||
|
||
# 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(8)) # U7-Raster
|
||
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 = 1.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)
|
||
|
||
# Pinsel-Kante (Falloff, Karte 25.10.): ein Regler hart ↔ weich für alle
|
||
# Höhen-Pinsel (Malen hat seinen eigenen Kanten-Regler weiter unten).
|
||
terrain_falloff_label = _field_label("Kante: 50 % weich")
|
||
terrain_panel.add_child(terrain_falloff_label)
|
||
terrain_falloff_slider = HSlider.new()
|
||
terrain_falloff_slider.min_value = 0.0
|
||
terrain_falloff_slider.max_value = 100.0
|
||
terrain_falloff_slider.step = 5.0
|
||
terrain_falloff_slider.value = 50.0
|
||
terrain_falloff_slider.custom_minimum_size = UIScale.v2(0, 32)
|
||
terrain_falloff_slider.tooltip_text = "Links = harte Kante (breiter Kern), rechts = weich auslaufend"
|
||
terrain_falloff_slider.value_changed.connect(_on_terrain_falloff_changed)
|
||
terrain_panel.add_child(terrain_falloff_slider)
|
||
|
||
|
||
|
||
func _on_terrain_btn_pressed() -> void:
|
||
if terrain_edit_btn.button_pressed:
|
||
if terrain_mode_paint.button_pressed:
|
||
terrain_mode_raise.button_pressed = true # Malen hat seinen eigenen Tab
|
||
main.call("_set_tool_mode", "terrain")
|
||
refresh_panels()
|
||
else:
|
||
main.call("_set_tool_mode", "objects")
|
||
|
||
|
||
func _terrain_mode_button(text: String, group: ButtonGroup, hinweis: String) -> Button:
|
||
var btn := Button.new()
|
||
btn.text = text
|
||
btn.toggle_mode = true
|
||
btn.button_group = group
|
||
btn.custom_minimum_size = UIScale.v2(0, 40)
|
||
btn.tooltip_text = hinweis # U7: Werkzeugname + Taste im Tooltip
|
||
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
|
||
if terrain_mode_flatten != null and terrain_mode_flatten.button_pressed:
|
||
return TerrainField.Mode.FLATTEN
|
||
if terrain_mode_ramp != null and terrain_mode_ramp.button_pressed:
|
||
return TerrainField.Mode.RAMP
|
||
return TerrainField.Mode.RAISE
|
||
|
||
|
||
## Mode-Buttons (ButtonGroup) → Pinsel-Modus; Palette nur im Malen-Modus.
|
||
func _on_terrain_mode_changed(_pressed: bool) -> void:
|
||
var paint := _selected_terrain_mode() == TerrainField.Mode.PAINT
|
||
if terrain_palette != null:
|
||
terrain_palette.visible = paint
|
||
if terrain_paint_hint != null:
|
||
terrain_paint_hint.text = paint_hint_text(main.doc.get("terrain", {}) if main.doc is Dictionary else {})
|
||
refresh_panels()
|
||
if main.terrain_tool != null:
|
||
main.terrain_tool.call("set_brush_mode", _selected_terrain_mode())
|
||
|
||
|
||
## Welche Bedienflächen sind sichtbar (Terrain-Tab bzw. Malen-Tab), welcher
|
||
## Start-Knopf steht gedrückt — Terrain und Malen teilen das Pinsel-Werkzeug.
|
||
func refresh_panels() -> void:
|
||
if terrain_panel == null:
|
||
return
|
||
var aktiv: bool = str(main.get("tool_mode")) == "terrain"
|
||
var paint := _selected_terrain_mode() == TerrainField.Mode.PAINT
|
||
terrain_edit_btn.set_pressed_no_signal(aktiv and not paint)
|
||
var malen = main.get("malen_mod")
|
||
if malen != null:
|
||
malen.malen_edit_btn.set_pressed_no_signal(aktiv and paint)
|
||
if aktiv:
|
||
_show_own_tab(malen if paint else self)
|
||
|
||
|
||
func _show_own_tab(tab: Control) -> void:
|
||
var tabs = main.get("ui_right_panel")
|
||
if tabs is TabContainer and tab != null and tab.get_parent() == tabs:
|
||
(tabs as TabContainer).current_tab = tab.get_index()
|
||
|
||
|
||
## Hinweistext Malen-Modus (pure, testbar): feinste Mal-Einheit = Abstand
|
||
## zweier Rasterpunkte der Karte (size / (resolution − 1)).
|
||
static func paint_hint_text(terrain: Dictionary) -> String:
|
||
var res := SvmapIO.material_resolution(terrain) # feines Material-Raster
|
||
var size := maxf(float(terrain.get("size_x_m", 0.0)), float(terrain.get("size_z_m", 0.0)))
|
||
if res < 2 or size <= 0.0:
|
||
return "Malen setzt das Material zellenweise — Stärke gibt es hier nicht."
|
||
var m := size / float(res - 1)
|
||
var m_text := str(int(roundf(m))) if absf(m - roundf(m)) < 0.05 \
|
||
else String.num(m, 1).replace(".", ",")
|
||
return "Malen setzt das Material zellenweise: feinste Einheit auf dieser Karte ≈ %s m. Stärke gibt es hier nicht." % m_text
|
||
|
||
|
||
## 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)
|
||
var malen = main.get("malen_mod")
|
||
if malen != null:
|
||
malen.set_radius_silent(value)
|
||
if main.terrain_tool != null:
|
||
main.terrain_tool.call("set_radius", value)
|
||
|
||
|
||
func _on_terrain_falloff_changed(value: float) -> void:
|
||
terrain_falloff_label.text = "Kante: %d %% weich" % int(value) if value > 0.0 else "Kante: hart"
|
||
if main.terrain_tool != null:
|
||
main.terrain_tool.call("set_falloff", value / 100.0)
|
||
|
||
|
||
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))
|