extends "res://core/ui/panels/editor_panel.gd" ## Malen-Tab (25.10., UI-Aufräumen): Material-Palette mit Vorschau-Kacheln, ## Pinselgröße und Kante. Malen ist technisch weiter der Pinsel-Modus PAINT des ## Terrain-Werkzeugs (terrain_tool.gd) — die Mode-Buttons und die Pinsel-Logik ## wohnen in terrain_panel.gd, dieses Panel hält nur die Mal-Widgets. main.gd ## reicht die Felder unter den alten Namen (terrain_palette …) weiter. const ARRAYS_PATH := "res://assets/textures/terrain/terrain_arrays.res" const MATERIAL_NAMES: Array[String] = [ "Steppe", "Sand", "Fels", "Sattgras", "Vertrocknet", "Moos", "Erde", "Pfad", "Umgegraben", "Schotter", "Lehm", "Sand hell", "Sand rost", "Basalt", "Geröll", "Schlamm", "Schnee", "Matschschnee", "Asche", "Stoppeln", ] var malen_edit_btn: Button var malen_panel: VBoxContainer var palette: VBoxContainer var mat_buttons: Array = [] var radius_label: Label var radius_slider: HSlider var weich_label: Label var weich_slider: HSlider var paint_hint: Label var _syncing := false static func spec() -> Dictionary: return {"titel": "Malen", "gruppe": "rechts", "order": 45} func panel_title() -> String: return "Malen" func build() -> void: malen_edit_btn = Button.new() malen_edit_btn.text = "🖌 Malen" malen_edit_btn.custom_minimum_size = UIScale.v2(0, 48) malen_edit_btn.toggle_mode = true malen_edit_btn.tooltip_text = "Material auf den Boden malen (Taste 4)" malen_edit_btn.pressed.connect(_on_malen_btn_pressed) add_child(malen_edit_btn) malen_panel = VBoxContainer.new() malen_panel.add_theme_constant_override("separation", UIScale.px(8)) add_child(malen_panel) palette = VBoxContainer.new() palette.add_theme_constant_override("separation", UIScale.px(4)) palette.visible = false malen_panel.add_child(palette) palette.add_child(_field_label("Material:")) var grid := GridContainer.new() grid.columns = 3 grid.size_flags_horizontal = Control.SIZE_EXPAND_FILL grid.add_theme_constant_override("h_separation", UIScale.px(6)) grid.add_theme_constant_override("v_separation", UIScale.px(6)) var scroll := ScrollContainer.new() scroll.custom_minimum_size = UIScale.v2(0, 340) scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED grid.size_flags_horizontal = Control.SIZE_EXPAND_FILL scroll.add_child(grid) palette.add_child(scroll) var previews := _material_previews() var group := ButtonGroup.new() for mat_id in SvmapIO.MATERIAL_IDS.size(): var btn := Button.new() btn.text = MATERIAL_NAMES[mat_id] if mat_id < MATERIAL_NAMES.size() else str(SvmapIO.MATERIAL_IDS[mat_id]) btn.custom_minimum_size = UIScale.v2(92, 104) btn.size_flags_horizontal = Control.SIZE_EXPAND_FILL btn.toggle_mode = true btn.button_group = group btn.clip_text = true btn.icon_alignment = HORIZONTAL_ALIGNMENT_CENTER btn.vertical_icon_alignment = VERTICAL_ALIGNMENT_TOP btn.expand_icon = true btn.add_theme_constant_override("icon_max_width", UIScale.px(76)) if mat_id < previews.size() and previews[mat_id] != null: btn.icon = previews[mat_id] btn.tooltip_text = "Malen: %s" % btn.text if mat_id == 0: btn.button_pressed = true btn.toggled.connect(_on_material_changed.bind(mat_id)) mat_buttons.append(btn) grid.add_child(btn) radius_label = _field_label("Pinsel-Größe: 12 m") malen_panel.add_child(radius_label) radius_slider = HSlider.new() radius_slider.min_value = 1.0 radius_slider.max_value = 64.0 radius_slider.step = 1.0 radius_slider.value = 12.0 radius_slider.custom_minimum_size = UIScale.v2(0, 32) radius_slider.value_changed.connect(_on_radius_changed) malen_panel.add_child(radius_slider) weich_label = _field_label("Kante: 40 % weich") malen_panel.add_child(weich_label) weich_slider = HSlider.new() weich_slider.min_value = 0.0 weich_slider.max_value = 100.0 weich_slider.step = 5.0 weich_slider.value = 40.0 weich_slider.custom_minimum_size = UIScale.v2(0, 32) weich_slider.tooltip_text = "Links = harte Kante, rechts = weicher Übergang" weich_slider.value_changed.connect(_on_weich_changed) malen_panel.add_child(weich_slider) paint_hint = _muted_label("") malen_panel.add_child(paint_hint) ## Vorschaubilder der 20 Materialien aus dem Textur-Array (Albedo-Layer). ## Fehlt das Array oder ein Layer: Kachel bleibt ohne Bild (nie ein Absturz). func _material_previews() -> Array: var out: Array = [] if not ResourceLoader.exists(ARRAYS_PATH): return out var arr = load(ARRAYS_PATH) if arr == null or not ("albedo_images" in arr): return out for img in arr.albedo_images: var tex: Texture2D = null if img is Image and not (img as Image).is_empty(): var copy: Image = (img as Image).duplicate() if copy.is_compressed(): copy.decompress() copy.resize(96, 96, Image.INTERPOLATE_BILINEAR) tex = ImageTexture.create_from_image(copy) out.append(tex) return out func _on_malen_btn_pressed() -> void: var t = main.terrain_mod if malen_edit_btn.button_pressed: t.terrain_mode_paint.button_pressed = true main.call("_set_tool_mode", "terrain") t.refresh_panels() else: main.call("_set_tool_mode", "objects") func _on_material_changed(pressed: bool, mat_id: int) -> void: if pressed and main.terrain_tool != null: main.terrain_tool.call("set_paint_material", mat_id) func _on_radius_changed(value: float) -> void: radius_label.text = "Pinsel-Größe: %d m" % int(value) if _syncing: return var t = main.terrain_mod if t != null and t.terrain_radius_slider != null: t.terrain_radius_slider.value = value # → set_radius über dessen Handler ## Terrain-Tab → Malen: Radius spiegeln, ohne Rückkopplung. func set_radius_silent(value: float) -> void: _syncing = true radius_slider.value = value _syncing = false func _on_weich_changed(value: float) -> void: weich_label.text = "Kante: %d %% weich" % int(value) if value > 0.0 else "Kante: hart" if main.terrain_tool != null: main.terrain_tool.call("set_weichheit", value / 100.0)