extends RefCounted class_name LibraryBrowser ## Objekt-Bibliothek des Editors (Karte t_3a998ae4): deutscher Name, Gruppen ## für den Bibliothek-Browser und Thumbnail-Laden (128-px-PNGs, einmalig per ## Headless-Render erzeugt — tools/gen_thumbs.gd). ## ## Bewusst OHNE UI: reine Daten/Funktionen, damit die Tests (tests/test_library.gd) ## headless alles prüfen können. Die IDs sind der Vertrag (Entscheidung 20) — ## Gruppen/Namen leben HIER im Editor (die objects.json bleibt 1:1 die Kopie ## der Server-Registry, damit ein Sync sie nicht verwüstet). ## Deutsche Anzeige-Namen für Bibliotheks-IDs (IDs sind der Vertrag, Entscheidung 20) const NAMES := { "crate_wood": "Holzkiste", "crate_metal": "Metallkiste", "wall_concrete": "Betonmauer", "barrier_sandbag": "Sandsack-Sperre", "bunker_concrete": "Bunker", "watchtower_wood": "Wachturm", "fence_metal": "Zaun", "rock_large": "Großer Felsen", "boulder_small": "Kleiner Felsen", "tree_pine": "Nadelbaum", "bush_small": "Strauch", "bruecke_holz": "Brücke (Holz)", "hoehleneingang": "Höhleneingang", "felsbogen": "Felsbogen", # Natur-Update (Karte t_7cf80244): kuratierte CC0-Packs (Quaternius # MegaKit, Kyle's Rock/Mushroom) — Namen aus stage/kuration.json. "baum_kiefer_1": "Kiefer (schlank)", "baum_kiefer_2": "Kiefer (kräftig)", "baum_kiefer_3": "Kiefer (mittel)", "baum_kiefer_4": "Kiefer (hoch)", "baum_laub_1": "Laubbaum (rund)", "baum_laub_2": "Laubbaum (breit)", "baum_laub_3": "Laubbaum (schlank)", "baum_laub_4": "Laubbaum (klein)", "baum_tot_1": "Totholz-Baum", "fels_moos_block_gross": "Moosfelsen (groß)", "fels_moos_block_hoch": "Moosfelsen (hoch)", "fels_moos_platte": "Moosfelsen (Platte)", "fels_moos_scharte": "Moosfelsen (klein)", "fels_trocken_block": "Trockenfelsen (hoch)", "fels_trocken_platte": "Trockenfelsen (Platte)", "fels_trocken_klein": "Trockenfelsen (klein)", "stein_mittel_1": "Großer Stein 1", "stein_mittel_2": "Großer Stein 2", "stein_kiesel_1": "Kieselstein", "strauch_gruen": "Strauch", "strauch_blumen": "Blühender Strauch", "pflanze_farn": "Farn", "pflanze_klee": "Klee-Pflanze", "blume_gross_1": "Blume (weiß)", "blume_gross_2": "Blume (hoch)", "blumen_strauss": "Blumenstrauß", "pflanze_niedrig": "Bodendecker", "pflanze_teppich": "Moospolster", "pflanze_teppich_gross": "Moospolster (groß)", "gras_kurz": "Kurzes Gras", "gras_hoch": "Hohes Gras", "gras_bueschel": "Grasbüschel", "gras_wispy_hoch": "Wispy-Gras (hoch)", "pilz_fliegenpilz": "Fliegenpilz", "pilz_pfifferling": "Pfifferling", "pilz_steinpilz": "Steinpilz", "pilz_shiitake": "Shiitake", "pilz_morchel": "Morchel", "pilz_austernseitling": "Austernseitling", } ## Gruppen für den Bibliothek-Browser (Anzeige-Reihenfolge = Reihenfolge hier). ## Natur-Update (t_7cf80244): die alte Gruppe "natur" ist in thematische ## Gruppen aufgelöst (die 4 Alt-Objekte wanderten mit). const GROUP_ORDER := ["kisten", "bauwerke", "bäume", "felsen", "pflanzen", "gras", "pilze", "sonstiges"] const GROUP_LABELS := { "kisten": "Kisten & Barrieren", "bauwerke": "Bauwerke", "bäume": "Bäume", "felsen": "Felsen", "pflanzen": "Pflanzen", "gras": "Gras", "pilze": "Pilze", "sonstiges": "Sonstiges", } const GROUP_IDS := { "kisten": ["crate_wood", "crate_metal", "barrier_sandbag", "wall_concrete"], "bauwerke": ["bunker_concrete", "watchtower_wood", "fence_metal", "bruecke_holz", "hoehleneingang", "felsbogen"], "bäume": ["tree_pine", "baum_kiefer_1", "baum_kiefer_2", "baum_kiefer_3", "baum_kiefer_4", "baum_laub_1", "baum_laub_2", "baum_laub_3", "baum_laub_4", "baum_tot_1"], "felsen": ["rock_large", "boulder_small", "fels_moos_block_gross", "fels_moos_block_hoch", "fels_moos_platte", "fels_moos_scharte", "fels_trocken_block", "fels_trocken_platte", "fels_trocken_klein", "stein_mittel_1", "stein_mittel_2", "stein_kiesel_1"], "pflanzen": ["bush_small", "strauch_gruen", "strauch_blumen", "pflanze_farn", "pflanze_klee", "blume_gross_1", "blume_gross_2", "blumen_strauss", "pflanze_niedrig", "pflanze_teppich", "pflanze_teppich_gross"], "gras": ["gras_kurz", "gras_hoch", "gras_bueschel", "gras_wispy_hoch"], "pilze": ["pilz_fliegenpilz", "pilz_pfifferling", "pilz_steinpilz", "pilz_shiitake", "pilz_morchel", "pilz_austernseitling"], } static func object_label(oid: String) -> String: return NAMES.get(oid, oid.capitalize()) ## Gruppe einer ID (Objekte, die keine festen Gruppe haben → "sonstiges"). static func group_of(oid: String) -> String: for key in GROUP_IDS: if (GROUP_IDS[key] as Array).has(oid): return str(key) return "sonstiges" ## Gruppen für den Browser, geprüft gegen das Manifest: nur IDs, die es in ## der objects.json WIRKLICH gibt, landen in der Liste (und unbekannte, ## noch nicht gruppierte IDs wandern nach "Sonstiges" — neue Bibliotheks- ## Einträge brechen die UI also nicht). static func groups_in_order(manifest: Dictionary) -> Array: var lib: Dictionary = manifest.get("objects", {}) var by_group := {} for oid in lib.keys(): var key := group_of(str(oid)) if not by_group.has(key): by_group[key] = [] (by_group[key] as Array).append(str(oid)) var out: Array = [] for key in GROUP_ORDER: var ids: Array = (by_group.get(key, []) as Array) ids.sort() if ids.is_empty() and key != "sonstiges": continue out.append({"key": key, "label": GROUP_LABELS[key], "ids": ids}) return out ## Thumbnail-Pfad (128-px-PNG, tools/gen_thumbs.gd erzeugt sie einmalig). static func thumb_path(oid: String) -> String: return "res://library/thumbs/%s.png" % oid ## Thumbnail als Textur laden — direkt über Image (kein Import-System nötig, ## geht damit auch frisch nach git pull ohne Import), auf Anzeige-Größe ## runtergerechnet. null = noch kein Thumbnail → Button fällt aufs Text-Layout ## zurück (Fehler sieht man nicht, man sieht einfach nur kein Bild). static func load_thumb(oid: String, size_px := 40) -> Texture2D: var path := thumb_path(oid) if not FileAccess.file_exists(path): return null var img := Image.load_from_file(path) if img == null: return null if img.get_width() != size_px or img.get_height() != size_px: img.resize(size_px, size_px, Image.INTERPOLATE_LANCZOS) return ImageTexture.create_from_image(img)