scopedvr-map-editor/src/editor/library_browser.gd

158 lines
6.1 KiB
GDScript

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)