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))