diff --git a/src/ui/help_text.gd b/src/ui/help_text.gd index 6be373d..c3f5643 100644 --- a/src/ui/help_text.gd +++ b/src/ui/help_text.gd @@ -9,70 +9,74 @@ extends RefCounted static func text() -> String: var out := "" - out += _header("Navigation (Kamera)") + out += _header("Kamera & Steuerung") out += _table(_rows([ ["Mittlere Maustaste ziehen", "Orbit: um die Karte drehen"], - ["Mittlere Maustaste + Umschalt ziehen", "Pan: Karte verschieben"], - ["Mausrad", "Zoom (aus, während ein Objekt gedreht wird)"], - ["Rechte Maustaste GEDACHT halten", "Freelook-Flug: Umsehen + W vor / S zurück / A links / D rechts; Leertaste hoch / Shift runter (Shooter-Konvention) — funktioniert in JEDEM Werkzeug (Terrain, Bauen, Platzieren), die rechte Maustaste hat immer Vorrang"], - ["Strg halten (im Flug)", "schneller fliegen (×3)"], - ["Alt halten (im Flug)", "langsamer fliegen (×0,3)"], - ["Mausrad (im Flug)", "Grundgeschwindigkeit dauerhaft ändern (2–300 m/s)"], + ["Mittlere Maustaste + Umschalt", "Pan: Karte verschieben"], + ["Mausrad", "Zoom"], + ["Rechte Maustaste halten", "Freelook-Flug in jedem Werkzeug: Maus = Umsehen, W/A/S/D = Bewegen, Leertaste hoch, Umschalt runter"], + ["Strg / Alt (im Flug)", "schneller (×3) / langsamer (×0,3); Mausrad ändert die Grundgeschwindigkeit"], ["Umschalt+F", "Flugmodus dauerhaft an/aus"], ["F", "Auswahl ins Bild zentrieren"], + ["L", "Höhenlinien fixieren (sonst nur im Terrain-Werkzeug)"], + ["H", "diese Hilfe an/aus (Esc schließt auch)"], ])) - out += _header("Auswahl") + out += _header("Objekte auswählen & platzieren (F2)") out += _table(_rows([ - ["Linksklick", "Objekt auswählen (ersetzt die Auswahl)"], - ["Strg+Klick (Viewport + Baum)", "Objekt zur Auswahl hinzufügen/entfernen (Mehrfachauswahl)"], - ["Umschalt+Klick", "wie Strg+Klick (Mehrfachauswahl umschalten)"], - ["Ziehen über leeren Boden", "Rahmen aufziehen: alle Objekte im Rahmen wählen"], - ["Umschalt + Rahmen", "Rahmen-Auswahl zur bestehenden Auswahl hinzufügen"], - ["Esc", "Werkzeug abbrechen → Greifen zurückrollen → Ghost abbrechen → Auswahl aufheben"], - ["G", "Auswahl greifen (hängt an der Maus; Klick setzt, Esc rollt zurück)"], - ])) - out += _header("Objekt-Manipulation") - out += _table(_rows([ - ["R", "90° drehen (Umschalt+R = −90°, ganze Gruppe dreht mit; Mausrad: 15° fein)"], - ["Strg+R / Strg+Umschalt+R", "Kippen um X (+15° / −15°) — auch beim Platzieren (Karte t_e166e656)"], - ["Alt+R / Alt+Umschalt+R", "Kippen um Z (+15° / −15°) — auch beim Platzieren (Karte t_e166e656)"], - ["Pfeiltasten", "Auswahl im 2-m-Raster verschieben (Kamera-nächste Weltachse)"], - ["Bild↑ / Bild↓", "+0,25 m / −0,25 m anheben/absenken — Absenken UNTER den Boden erlaubt (Versenken, z. B. Steine)"], - ["Strg+Bild↑", "Auswahl aufs Terrain/Boden absetzen"], - ["Strg halten", "temporärer Snap-Zwang (rastet auf ganze Meter)"], - ["Strg+D", "Auswahl duplizieren (+2 m versetzt)"], - ["Entf", "Auswahl sofort löschen (Strg+Z holt zurück)"], + ["Linksklick", "Objekt wählen; Umschalt/Strg+Klick: Mehrfachauswahl"], + ["Rahmen aufziehen", "alle Objekte im Rahmen wählen (Umschalt: hinzufügen)"], + ["Bibliothek: Klick / Doppelklick", "Objekt auswählen / Platzieren starten; Klick in die Karte setzt, Esc bricht ab"], + ["G", "greifen: Objekt hängt an der Maus (Klick setzt, Esc zurück)"], + ["Pfeiltasten", "im 2-m-Raster verschieben"], + ["Bild↑ / Bild↓", "0,25 m anheben / absenken; Strg+Bild↑ = auf den Boden setzen"], + ["Strg halten", "Snap-Zwang auf ganze Meter"], + ["Strg+D", "duplizieren"], + ["Entf", "löschen"], ["Strg+G / Strg+Umschalt+G", "Gruppieren / Gruppe lösen"], + ["Esc", "abbrechen, dann Auswahl aufheben"], ])) - out += _header("Terrain-Werkzeug (F1)") + out += _header("Objekte verschieben, drehen, kippen (Werkzeugleiste links oben in der 3D-Ansicht)") out += _table(_rows([ - ["1 / 2 / 3 / 4", "Pinselmodus: Heben / Senken / Glätten / Malen"], - ["[ / ]", "Pinselgröße: Klein (4 m) / Mittel (12 m) / Groß (32 m) zyklisch"], - ["Linksklick-Ziehen", "Boden formen bzw. malen"], - ["Strg+Linksklick-Ziehen", "Gegenteil: senken (bei Heben) / heben (bei Senken) / Steppe zurückmalen"], + ["Verschieben", "Pfeile am Objekt ziehen (X rot, Y grün, Z blau); Mausrad ändert die Schrittweite"], + ["Drehen", "Ringe am Objekt ziehen: um alle drei Achsen, in 15°-Schritten; R = 90° um Y (Umschalt+R = −90°)"], + ["Kippen", "Strg+R / Strg+Umschalt+R = um X ±15°; Alt+R / Alt+Umschalt+R = um Z ±15° (auch beim Platzieren)"], + ["Spiegeln", "an der Stelle links↔rechts oder vor↔zurück (Panel Auswahl)"], + ["Strg+Z / Strg+Y", "jeder Zug ist ein Undo-Schritt"], ])) - out += _header("Datei & Undo") + out += _header("Terrain formen (F1)") + out += _table(_rows([ + ["1 / 2 / 3", "Heben / Senken / Glätten"], + ["5 / 6", "Einebnen (Höhe vom Strichstart) / Rampe (Start klicken, ziehen, loslassen)"], + ["[ / ]", "Pinselgröße Klein / Mittel / Groß"], + ["Linksklick ziehen", "Boden formen; Strg+Klick = Gegenteil (Heben ↔ Senken)"], + ["Tablet/Controller", "Stärke folgt dem Druck (Maus = voll)"], + ])) + out += _header("Malen (4)") + out += _table(_rows([ + ["Material wählen", "Tab Malen: Kacheln mit Vorschau"], + ["Linksklick ziehen", "Material auf den Boden malen; Strg+Klick malt Steppe zurück"], + ["Kante", "Regler: hart ↔ weich auslaufend"], + ])) + out += _header("Bauen (F3)") + out += _table(_rows([ + ["Linksklick", "Bauteil aufs 2-m-Raster setzen, auch auf andere Teile (stapeln)"], + ["R / Pfeile / Bild↑↓", "drehen / verschieben / anheben"], + ["Umschalt+Klick", "Bauteil auswählen; Entf löscht"], + ])) + out += _header("Spawn-Zonen (F4) und Test-Start (T)") + out += _table(_rows([ + ["F4: Linksklick", "Zone setzen; Ziehen verschiebt, Entf löscht; Team und Radius im Tab Prüfen"], + ["T: Linksklick", "Test-Startpunkt setzen (nur für F5, kein Spawn)"], + ["F5", "▶ Karte testen"], + ])) + out += _header("Datei & Rückgängig") out += _table(_rows([ ["Strg+Z", "Rückgängig"], - ["Strg+Y oder Strg+Umschalt+Z", "Wiederherstellen (Redo)"], - ["Strg+S", "Map speichern (.svmap, bekannter Pfad)"], - ["Strg+Umschalt+S", "Map speichern unter …"], - ["Strg+O", "Map öffnen"], - ["Strg+N", "Neue Map"], - ["Datei → 🔄 Updates prüfen", "Selbst-Update: prüft auf neue Version; Banner → „Jetzt updaten“ (lädt, importiert, startet neu)"], + ["Strg+Y / Strg+Umschalt+Z", "Wiederherstellen"], + ["Strg+S / Strg+Umschalt+S", "Speichern / Speichern unter"], + ["Strg+O / Strg+N", "Öffnen / Neue Map"], + ["Datei → Updates prüfen", "prüft auf eine neue Version; Banner „Jetzt updaten“ lädt und startet neu"], ])) - out += _header("Werkzeuge & Tests") - out += _table(_rows([ - ["F1", "Werkzeug: Terrain formen"], - ["F2", "Werkzeug: Objekte platzieren/auswählen"], - ["F3", "Werkzeug: Bauen (Häuser)"], - ["F4", "Werkzeug: Spawn-Zonen malen"], - ["T", "Werkzeug: Test-Startpunkt setzen — Klick in die Karte = dort startet F5 (KEIN Spiel-Spawnpunkt; im Testlauf versetzt T dich sofort zum Blickpunkt)"], - ["F5", "▶ Karte testen (Start: Test-Start → erster Team-Spawnpunkt → Kartenmitte)"], - ["H", "Diese Kurzhilfe an/aus (Esc schließt auch)"], - ["L", "Höhenlinien FIXIEREN (immer sichtbar/unsichtbar) — ohne Fix erscheinen sie nur im Terrain-Werkzeug (F1)"], - ])) - out += "[color=#a8b6ad]Vor „Neue Map“ und „Öffnen“ fragt der Editor nach, wenn noch ungespeicherte Änderungen da sind. Die Statuszeile unten zeigt immer das aktive Werkzeug.[/color]\n" return out diff --git a/src/ui/main.gd b/src/ui/main.gd index 52f8097..8aa925b 100644 --- a/src/ui/main.gd +++ b/src/ui/main.gd @@ -79,6 +79,8 @@ var status_mod # src/ui/panels/status_bar.gd var selection_mod # src/ui/panels/selection_panel.gd var map_mod # src/ui/panels/map_panel.gd var terrain_mod # src/ui/panels/terrain_panel.gd +var malen_mod # src/ui/panels/malen_panel.gd (Malen-Tab) +var bauen_mod # src/ui/panels/bauen_panel.gd (Bauen-Tab) var check_mod # src/ui/panels/check_panel.gd var tree_mod # src/ui/panels/scene_tree_panel.gd var assets_mod # src/ui/panels/assets_panel.gd @@ -391,6 +393,10 @@ func _panel_group_built(gruppe: String, tabs: TabContainer) -> void: map_mod = child "terrain": terrain_mod = child + "malen": + malen_mod = child + "bauen": + bauen_mod = child "check": check_mod = child sel_label = selection_mod.sel_label @@ -398,9 +404,16 @@ func _panel_group_built(gruppe: String, tabs: TabContainer) -> void: prop_name = map_mod.prop_name spawn_status = check_mod.spawn_status terrain_info = map_mod.terrain_info - build_edit_btn = terrain_mod.build_edit_btn - build_panel = terrain_mod.build_panel - build_kind_buttons = terrain_mod.build_kind_buttons + build_edit_btn = bauen_mod.build_edit_btn + build_panel = bauen_mod.build_panel + build_kind_buttons = bauen_mod.build_kind_buttons + # Malen-Widgets wohnen im Malen-Tab; das Terrain-Modul hält die + # alten Namen als Aliase (Tests/Werkzeug-Vertrag). + terrain_mod.terrain_palette = malen_mod.palette + terrain_mod.terrain_mat_buttons = malen_mod.mat_buttons + terrain_mod.terrain_weich_label = malen_mod.weich_label + terrain_mod.terrain_weich_slider = malen_mod.weich_slider + terrain_mod.terrain_paint_hint = malen_mod.paint_hint # Test-/Werkzeug-Vertrag (wie vor der U1-Aufteilung): die Widgets # wohnen in den Modulen, der Koordinator hält die alten Namen # als Bequemlichkeits-Referenzen — nichts wird doppelt gebaut. @@ -536,7 +549,7 @@ func _build_dialogs_and_overlays() -> void: overlay_box.add_theme_constant_override("separation", UIScale.px(6)) help_overlay.add_child(overlay_box) help_overlay_title = Label.new() - help_overlay_title.text = "Kurzhilfe — alle Tasten auf einen Blick (H oder Esc schließt)" + help_overlay_title.text = "Hilfe — Werkzeuge, Steuerung und Tastenkürzel (H oder Esc schließt)" help_overlay_title.add_theme_font_size_override("font_size", UIScale.heading_font_size()) help_overlay_title.add_theme_color_override("font_color", get_theme_color("accent", "Editor")) overlay_box.add_child(help_overlay_title) @@ -1756,7 +1769,9 @@ func _base_tool_status() -> void: ## Namen hier (dünne Weiterleitung) — der Werkzeug-Vertrag ist unverändert. func _on_terrain_btn_pressed() -> void: - if terrain_mod.terrain_edit_btn.button_pressed: + if tool_mode != "terrain": + if terrain_mod.terrain_mode_paint.button_pressed: + terrain_mod.terrain_mode_raise.button_pressed = true _set_tool_mode("terrain") else: _set_tool_mode("objects") @@ -1814,10 +1829,9 @@ func _enter_terrain_mode() -> void: _sync_terrain_button() return tool_mode = "terrain" - terrain_mod.terrain_panel.visible = true help_label.text = "Linksklick-Ziehen: Boden formen oder malen · Strg+Linksklick: Gegenteil / Steppe zurück ·\n1/2/3/4: Heben/Senken/Glätten/Malen · [ / ]: Pinselgröße Klein/Mittel/Groß ·\nPinselgröße & Material rechts · Rechte Maustaste: Flug · Strg+Z: letzter Strich weg" - terrain_mod.terrain_edit_btn.button_pressed = true terrain_tool.call("set_brush_mode", terrain_mod._selected_terrain_mode()) + terrain_mod.refresh_panels() _set_status("Terrain-Modus an: LINKSKLICK-Ziehen formt den Boden, im Malen-Modus färbt er das Gelände (Steppe/Sand/Fels …). Regler rechts stellen Pinsel und Stärke ein.") @@ -1826,6 +1840,7 @@ func _leave_terrain_mode() -> void: terrain_tool.call("deactivate") if terrain_mod != null: terrain_mod.terrain_panel.visible = false + malen_mod.malen_panel.visible = false _sync_terrain_button() @@ -1888,6 +1903,8 @@ func _enter_build_mode() -> void: build_panel.visible = true help_label.text = "Linksklick: Bauteil setzen (auch auf andere Teile = stapeln) · Umschalt+Klick: Bauteil auswählen ·\nR: drehen (90°) · Pfeiltasten: im 2-m-Raster verschieben · Bild↑/↓: anheben/absenken · Strg+Bild↑: auf Boden · Entf: löschen · Mittlere Maustaste: Kamera · Strg+Z: zurück" build_edit_btn.button_pressed = true + if ui_right_panel is TabContainer and bauen_mod != null: + (ui_right_panel as TabContainer).current_tab = bauen_mod.get_index() build_tool.call("activate") _set_status("Bauen-Modus an: Bauteil rechts anklicken, dann in die Karte klicken — alles rastet aufs 2-m-Raster.") @@ -1905,7 +1922,7 @@ func _sync_build_button() -> void: func _sync_terrain_button() -> void: - terrain_mod.terrain_edit_btn.button_pressed = tool_mode == "terrain" + terrain_mod.refresh_panels() func _terrain_brush_size_step(up: bool) -> void: @@ -1959,6 +1976,7 @@ func _recheck_terrain_mode() -> void: terrain_tool.call("deactivate") tool_mode = "objects" terrain_mod.terrain_panel.visible = false + malen_mod.malen_panel.visible = false _sync_terrain_button() _update_tool_label() # Karte t_89c0ec95: Dezenter Terrain-Rückschalter = ebenfalls ein diff --git a/src/ui/panels/bauen_panel.gd b/src/ui/panels/bauen_panel.gd new file mode 100644 index 0000000..2a2c5e0 --- /dev/null +++ b/src/ui/panels/bauen_panel.gd @@ -0,0 +1,63 @@ +extends "res://src/ui/panels/editor_panel.gd" +## Bauen-Tab (25.10., UI-Aufräumen): Bauteil-Auswahl für das Bauen-Werkzeug +## (Fundamente, Wände, Dächer …). Vorher im Terrain-Tab. Die Werkzeug-Logik +## bleibt in main.gd/build_tool.gd; main reicht die Felder unter den alten +## Namen (build_edit_btn, build_panel, build_kind_buttons) weiter. + +var build_edit_btn: Button +var build_panel: VBoxContainer +var build_kind_buttons: Dictionary = {} # kind_id → Button + + +static func spec() -> Dictionary: + return {"titel": "Bauen", "gruppe": "rechts", "order": 47} + + +func panel_title() -> String: + return "Bauen" + + +func build() -> void: + 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 im 2-Meter-Raster bauen (F3)" + 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(8)) + 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(8)) + build_grid.add_theme_constant_override("v_separation", UIScale.px(8)) + 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) + + +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) diff --git a/src/ui/panels/malen_panel.gd b/src/ui/panels/malen_panel.gd new file mode 100644 index 0000000..6189f0b --- /dev/null +++ b/src/ui/panels/malen_panel.gd @@ -0,0 +1,171 @@ +extends "res://src/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)) + malen_panel.visible = false + 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) diff --git a/src/ui/panels/registry.gd b/src/ui/panels/registry.gd index 01a016a..5c5222e 100644 --- a/src/ui/panels/registry.gd +++ b/src/ui/panels/registry.gd @@ -34,6 +34,11 @@ const PANELS: Array = [ "skript": "res://src/ui/panels/map_panel.gd"}, {"id": "terrain", "titel": "Terrain", "gruppe": "rechts", "order": 40, "skript": "res://src/ui/panels/terrain_panel.gd"}, + # 25.10. UI-Aufräumen: Malen und Bauen hängen nicht mehr im Terrain-Tab. + {"id": "malen", "titel": "Malen", "gruppe": "rechts", "order": 45, + "skript": "res://src/ui/panels/malen_panel.gd"}, + {"id": "bauen", "titel": "Bauen", "gruppe": "rechts", "order": 47, + "skript": "res://src/ui/panels/bauen_panel.gd"}, {"id": "check", "titel": "Prüfen", "gruppe": "rechts", "order": 50, "skript": "res://src/ui/panels/check_panel.gd"}, {"id": "teststart", "titel": "Test-Start", "gruppe": "rechts", "order": 60, diff --git a/src/ui/panels/terrain_panel.gd b/src/ui/panels/terrain_panel.gd index fbb8f3d..1a5724a 100644 --- a/src/ui/panels/terrain_panel.gd +++ b/src/ui/panels/terrain_panel.gd @@ -28,11 +28,6 @@ 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 -# --- 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} @@ -43,7 +38,7 @@ func build() -> void: 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.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) @@ -52,7 +47,7 @@ func build() -> void: terrain_panel.visible = false add_child(terrain_panel) - terrain_panel.add_child(_field_label("Pinsel-Werkzeug:")) + 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 @@ -81,52 +76,12 @@ func build() -> void: 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) mode_row.add_child(terrain_mode_flatten) mode_row.add_child(terrain_mode_ramp) - - # 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(8)) # U7-Raster - terrain_palette.add_child(mat_row) - var mat_group := ButtonGroup.new() - # Karte t_ab408127: 20 Slots in scrollbarer Raster-Palette (Spec: „like - # Bibliothek") — 4 Spalten, ~2 sichtbare Reihen, scrollbar nach unten. - # Thumbnails folgen, sobald die Texturen in assets/textures/terrain/ liegen - # (gen_terrain_thumbs) — bis dahin Button-Farbfeld via icon (Default). - var mat_grid := GridContainer.new() - mat_grid.columns = 4 - mat_grid.add_theme_constant_override("h_separation", UIScale.px(4)) - mat_grid.add_theme_constant_override("v_separation", UIScale.px(4)) - var mat_scroll := ScrollContainer.new() - mat_scroll.custom_minimum_size = UIScale.v2(0, 176) # ~3 Reihen a 48 px + Separation - mat_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED - mat_scroll.add_child(mat_grid) - terrain_palette.add_child(mat_scroll) - var mat_labels := [ - "🏜 Steppe", "🏖 Sand", "🪨 Fels", "🌱 Sattgras", - "🍂 Vertrocknet", "🍀 Moos", "🟫 Erde", "🛤 Pfad", - "⛏ Umgegraben", "🧱 Schotter", "🟤 Lehm", "🏜 Sand hell", - "🏜 Sand rost", "🗻 Basalt", "⛰ Geröll", "🌧 Schlamm", - "❄️ Schnee", "🥶 Matschschnee", "🌑 Asche", "🌾 Stoppeln", - ] - for mat_id in SvmapIO.MATERIAL_IDS.size(): - var mat_btn := Button.new() - mat_btn.text = str(mat_labels[mat_id]) if mat_id < mat_labels.size() else str(SvmapIO.MATERIAL_IDS[mat_id]) - mat_btn.custom_minimum_size = UIScale.v2(0, 48) - mat_btn.toggle_mode = true - mat_btn.button_group = mat_group - mat_btn.tooltip_text = "Malen: %s (Slot %d)" % [str(SvmapIO.MATERIAL_IDS[mat_id]), 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_grid.add_child(mat_btn) + # 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:")) @@ -181,92 +136,14 @@ func build() -> void: terrain_falloff_slider.value_changed.connect(_on_terrain_falloff_changed) terrain_panel.add_child(terrain_falloff_slider) - # Malen kennt keine Stärke: das Material wird zellenweise gesetzt - # (Material-Schicht = ein Wert pro Rasterpunkt, weiche Übergänge macht - # der Splat-Renderer). Statt eines wirkungslosen Reglers steht im - # Malen-Modus die echte Feinheit da (Sly-Befund 22.10.: „Stärke ändert nichts“). - terrain_paint_hint = _muted_label("") - terrain_paint_hint.visible = false - terrain_panel.add_child(terrain_paint_hint) - - # Kante beim Malen (Karte 25.10., Sly): 0 % = harter Rand, 100 % = der ganze - # Pinsel läuft weich aus (gestreut, der Shader glättet die Streuung). - terrain_weich_label = _field_label("Kante: 40 % weich") - terrain_weich_label.visible = false - terrain_panel.add_child(terrain_weich_label) - terrain_weich_slider = HSlider.new() - terrain_weich_slider.min_value = 0.0 - terrain_weich_slider.max_value = 100.0 - terrain_weich_slider.step = 5.0 - terrain_weich_slider.value = 40.0 - terrain_weich_slider.custom_minimum_size = UIScale.v2(0, 32) - terrain_weich_slider.tooltip_text = "Links = harte Kante, rechts = weicher Übergang" - terrain_weich_slider.visible = false - terrain_weich_slider.value_changed.connect(_on_terrain_weich_changed) - terrain_panel.add_child(terrain_weich_slider) - - terrain_panel.add_child(_muted_label( - "Stärke folgt dem Stift-Druck bzw. dem Controller-Trigger (Maus = voll). " - + "Rampe: Start klicken, ziehen, loslassen. Einebnen: Höhe am Strichstart wird zum Ziel.")) - 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 Steppe (Grundboden) 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(8)) # U7-Raster - 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(8)) # U7-Raster - build_grid.add_theme_constant_override("v_separation", UIScale.px(8)) # U7-Raster - 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: + 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") @@ -300,22 +177,36 @@ 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_strength_slider != null: - terrain_strength_slider.visible = not paint - terrain_strength_label.visible = not paint - if terrain_falloff_slider != null: - terrain_falloff_slider.visible = not paint - terrain_falloff_label.visible = not paint - if terrain_weich_slider != null: - terrain_weich_slider.visible = paint - terrain_weich_label.visible = paint if terrain_paint_hint != null: - terrain_paint_hint.visible = paint 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_panel.visible = aktiv and not paint + terrain_edit_btn.set_pressed_no_signal(aktiv and not paint) + var malen = main.get("malen_mod") + if malen != null: + malen.malen_panel.visible = aktiv and paint + 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: @@ -329,12 +220,6 @@ static func paint_hint_text(terrain: Dictionary) -> String: return "Malen setzt das Material zellenweise: feinste Einheit auf dieser Karte ≈ %s m. Stärke gibt es hier nicht." % m_text -## 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: @@ -357,16 +242,13 @@ func _on_terrain_size_preset(pressed: bool, radius_m: float) -> void: 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_weich_changed(value: float) -> void: - terrain_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) - - 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: diff --git a/src/ui/panels/toolbar.gd b/src/ui/panels/toolbar.gd index 7b80a85..941ab78 100644 --- a/src/ui/panels/toolbar.gd +++ b/src/ui/panels/toolbar.gd @@ -163,6 +163,14 @@ func build() -> void: size_popup.index_pressed.connect(func(index: int) -> void: main.call("_on_ui_size_selected", index)) add_child(size_menu) + var help_btn := Button.new() + help_btn.text = "❓ Hilfe" + help_btn.name = "hilfe" + help_btn.custom_minimum_size = UIScale.v2(110, 40) + help_btn.tooltip_text = "Werkzeuge, Steuerung und Tastenkürzel (H)" + help_btn.pressed.connect(func() -> void: main.call("_toggle_help_overlay")) + add_child(help_btn) + title_label = Label.new() title_label.text = " %s" % main.APP_TITLE title_label.add_theme_font_size_override("font_size", UIScale.heading_font_size()) diff --git a/tests/probe_mal_weich_render.gd.uid b/tests/probe_mal_weich_render.gd.uid new file mode 100644 index 0000000..dddd14f --- /dev/null +++ b/tests/probe_mal_weich_render.gd.uid @@ -0,0 +1 @@ +uid://w5p21ia5bryo diff --git a/tests/probe_steppe_tiling.gd.uid b/tests/probe_steppe_tiling.gd.uid new file mode 100644 index 0000000..053c4ed --- /dev/null +++ b/tests/probe_steppe_tiling.gd.uid @@ -0,0 +1 @@ +uid://badptran2lnax diff --git a/tests/test_pinsel_werkzeuge.gd.uid b/tests/test_pinsel_werkzeuge.gd.uid new file mode 100644 index 0000000..f01625b --- /dev/null +++ b/tests/test_pinsel_werkzeuge.gd.uid @@ -0,0 +1 @@ +uid://d3lv8invgk6kv