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

170 lines
5.9 KiB
GDScript

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)