264 lines
10 KiB
GDScript
264 lines
10 KiB
GDScript
extends "res://core/ui/panels/editor_panel.gd"
|
||
## Map-Eigenschaften (aus main.gd ausgezogen, Karte t_eaccbfed U1 — Logik
|
||
## unverändert): Name/Autor/Beschreibung/Version/Wetter + Terrain-Info.
|
||
## Eingaben schreiben LIVE ins doc (Live-Bindung wie vorher) und markieren
|
||
## dirty — die Signale wurden vorher in _build_right_panel verdrahtet, sie
|
||
## hängen jetzt hier.
|
||
|
||
var prop_name: LineEdit
|
||
var prop_author: LineEdit
|
||
var prop_desc: LineEdit
|
||
var prop_version: SpinBox
|
||
var prop_weather: OptionButton
|
||
var prop_sky: OptionButton
|
||
var sky_preview: TextureRect # Vorschau der gewählten Skybox (obere Panorama-Hälfte)
|
||
var prop_water: HSlider
|
||
var terrain_info: Label
|
||
|
||
var _sections: VBoxContainer # Inhalt des Map-Tabs (im Scroll-Bereich)
|
||
var _loading_ui_values := false # verhindert dirty beim Auffüllen der Felder
|
||
|
||
## Karte t_28fdef03: Slider-Stellung für „Kein Wasser“ (Meta-Vertrag für
|
||
## Tests/Verdrahtung). Slider unterhalb davon (−10 m) = Wasser AUS — das doc-
|
||
## Feld wird dann ENTFERNT (Legacy-sauber, kein Wert 0 versehentlich gespeichert).
|
||
const KEIN_WASSER_WERT := -10.0
|
||
|
||
|
||
static func spec() -> Dictionary:
|
||
return {"titel": "Map", "gruppe": "rechts", "order": 30}
|
||
|
||
|
||
func panel_title() -> String:
|
||
return "Map-Eigenschaften"
|
||
|
||
|
||
## Bau (aus der alten main.gd/_build_right_panel übernommen, Logik identisch):
|
||
## Name/Autor/Beschreibung/Version/Wetter + Terrain-Info. Die Signale hängen
|
||
## jetzt hier (statt in main.gd) — der doc-Datenfluss ist derselbe.
|
||
func build() -> void:
|
||
add_child(_field_label("Name"))
|
||
prop_name = LineEdit.new()
|
||
prop_name.max_length = 64
|
||
add_child(prop_name)
|
||
|
||
add_child(_field_label("Autor"))
|
||
prop_author = LineEdit.new()
|
||
add_child(prop_author)
|
||
|
||
add_child(_field_label("Beschreibung"))
|
||
prop_desc = LineEdit.new()
|
||
add_child(prop_desc)
|
||
|
||
add_child(_field_label("Version"))
|
||
prop_version = SpinBox.new()
|
||
prop_version.min_value = 1
|
||
prop_version.max_value = 99999
|
||
prop_version.step = 1
|
||
add_child(prop_version)
|
||
|
||
add_child(_field_label("Wetter"))
|
||
prop_weather = OptionButton.new()
|
||
# U7: Klartext-Tooltip am Wetter-Auswahlfeld.
|
||
prop_weather.tooltip_text = "Licht und Stimmung der Karte (Tag/Abend/Regen/Nacht)"
|
||
prop_weather.add_item("Mittag (sonnig, klar)")
|
||
prop_weather.add_item("Abend (warmes Licht)")
|
||
prop_weather.add_item("Regen (grau, kühl)")
|
||
prop_weather.add_item("Nacht (dunkel)")
|
||
add_child(prop_weather)
|
||
|
||
add_child(_field_label("Himmel"))
|
||
prop_sky = OptionButton.new()
|
||
# Karte Atmosphäre/Skybox: Panorama-Skybox aus dem Pack als Map-Option.
|
||
prop_sky.tooltip_text = "Himmel der Karte: Prozedural (Standard, lässt die Karte unverändert) oder eine Skybox aus dem Katalog. Der Viewport zeigt sie sofort; Strg+Z macht die Wahl rückgängig."
|
||
prop_sky.add_item("Prozedural (Standard)")
|
||
for e in SkyboxKatalog.EINTRAEGE:
|
||
prop_sky.add_item(str(e["name"]))
|
||
add_child(prop_sky)
|
||
sky_preview = TextureRect.new()
|
||
sky_preview.custom_minimum_size = Vector2(0, 80)
|
||
sky_preview.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
||
sky_preview.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_COVERED
|
||
sky_preview.tooltip_text = "Vorschau: oberer Teil der gewählten Skybox"
|
||
add_child(sky_preview)
|
||
|
||
# Wasser (Karte t_28fdef03): Wasserhöhe der Map — Senken unter der Höhe
|
||
# füllen sich automatisch. Ganz links = „Kein Wasser“ (Feld entfernen,
|
||
# alte Maps unverändert). Die Live-Vorschau nutzt dasselbe Material wie
|
||
# der Client (SvmapWasser.make_material — Identitäts-Vertrag).
|
||
add_child(_field_label("Wasserhöhe"))
|
||
prop_water = HSlider.new()
|
||
prop_water.min_value = -10.0
|
||
prop_water.max_value = 100.0
|
||
prop_water.step = 0.25
|
||
prop_water.value = KEIN_WASSER_WERT
|
||
prop_water.custom_minimum_size = UIScale.v2(0, 32)
|
||
prop_water.tooltip_text = "Wasser: ganz links = kein Wasser, sonst Höhe der Wasseroberfläche über dem Terrain. Senken unter dieser Höhe füllen sich mit Wasser."
|
||
prop_water.set_meta("kein_wasser_value", KEIN_WASSER_WERT)
|
||
add_child(prop_water)
|
||
|
||
add_child(_separator())
|
||
add_child(_header_label("Terrain"))
|
||
terrain_info = Label.new()
|
||
terrain_info.text = ""
|
||
terrain_info.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART
|
||
add_child(terrain_info)
|
||
|
||
# Signale (Änderungen → Dokument + Ansicht) — wie vorher in main.gd.
|
||
prop_name.text_changed.connect(_on_prop_changed.bind("name"))
|
||
prop_author.text_changed.connect(_on_prop_changed.bind("author"))
|
||
prop_desc.text_changed.connect(_on_prop_changed.bind("description"))
|
||
prop_version.value_changed.connect(_on_version_changed)
|
||
prop_weather.item_selected.connect(_on_weather_changed)
|
||
prop_sky.item_selected.connect(_on_sky_changed)
|
||
prop_water.value_changed.connect(_on_water_changed)
|
||
_sections = wrap_in_scroll()
|
||
|
||
|
||
## Hier hängen die Abschnitte der Registry-Gruppe "map" (Spawn-Zonen, Test-Start,
|
||
## Symmetrie) unter den Map-Eigenschaften.
|
||
func sections_host() -> Control:
|
||
return _sections
|
||
|
||
|
||
func _on_prop_changed(text: String, field: String) -> void:
|
||
if _loading_ui_values or main.doc.is_empty():
|
||
return
|
||
var info: Dictionary = main.doc["info"]
|
||
match field:
|
||
"name":
|
||
info["name"] = text
|
||
"author":
|
||
info["author"] = text
|
||
"description":
|
||
info["description"] = text
|
||
main.dirty = true
|
||
main.call("_refresh_file_label")
|
||
|
||
|
||
func _on_version_changed(value: float) -> void:
|
||
if _loading_ui_values or main.doc.is_empty():
|
||
return
|
||
main.doc["info"]["version"] = int(value)
|
||
main.dirty = true
|
||
main.call("_refresh_file_label")
|
||
|
||
|
||
func _on_weather_changed(index: int) -> void:
|
||
if _loading_ui_values or main.doc.is_empty():
|
||
return
|
||
var weathers := ["mittag", "abend", "regen", "nacht"]
|
||
main.doc["weather"] = weathers[index]
|
||
main.dirty = true
|
||
main.call("_refresh_file_label")
|
||
# Licht/Stimmung im Viewport sofort anpassen
|
||
main.view_builder.call("rebuild", main.doc, main.manifest)
|
||
main.call("_sync_grid_to_builder")
|
||
main.call("_set_status", "Wetter geändert auf „%s“." % weathers[index])
|
||
|
||
|
||
## Skybox-Auswahl: Eintrag → doc["weather_sky"] = Katalog-ID (ein Speicherfeld,
|
||
## checksum-neutral wie tint/gebaeude). „Prozedural (Standard)“ = Index 0 → Feld
|
||
## ENTFERNEN (alte Maps unverändert). Mit Undo/Redo (History-Block weather_sky).
|
||
## Sofort-Neuaufbau wie Wetter — der Viewport zeigt exakt, was der Client zeigt.
|
||
func _on_sky_changed(index: int) -> void:
|
||
if _loading_ui_values or main.doc.is_empty():
|
||
return
|
||
var neu := "" if index <= 0 else str(SkyboxKatalog.EINTRAEGE[index - 1]["id"])
|
||
var alt := str(main.doc.get("weather_sky", ""))
|
||
if neu == alt:
|
||
return
|
||
var before := {"weather_sky": (alt if main.doc.has("weather_sky") else null)}
|
||
if neu == "":
|
||
main.doc.erase("weather_sky")
|
||
else:
|
||
main.doc["weather_sky"] = neu
|
||
var after := {"weather_sky": (neu if neu != "" else null)}
|
||
var name := "Prozedural (Standard)" if neu == "" else str(SkyboxKatalog.eintrag(neu)["name"])
|
||
main.history.commit("Himmel: %s" % name, before, after)
|
||
main.dirty = true
|
||
main.call("_refresh_file_label")
|
||
main.call("_refresh_history_buttons")
|
||
main.view_builder.call("rebuild", main.doc, main.manifest)
|
||
main.call("_sync_grid_to_builder")
|
||
_update_sky_preview(neu)
|
||
var hinweis := ""
|
||
if neu != "" and SkyboxKatalog.panorama_textur(neu) == null:
|
||
hinweis = " Achtung: Textur nicht gefunden (Client-Ordner neben dem Editor fehlt) — Vorschau zeigt den Standard-Himmel."
|
||
main.call("_set_status", "Himmel geändert auf „%s“.%s" % [name, hinweis])
|
||
|
||
|
||
func _update_sky_preview(id: String) -> void:
|
||
sky_preview.texture = SkyboxKatalog.vorschau(id) if id != "" else null
|
||
sky_preview.visible = sky_preview.texture != null
|
||
|
||
|
||
## Wasser (Karte t_28fdef03): Slider → doc["water_level"]. Ganz links
|
||
## („Kein Wasser“, KEIN_WASSER_WERT) = Feld ENTFERNEN (Legacy-sauber).
|
||
## Sofort-Neuaufbau wie Wetter/Himmel — die Vorschau zeigt die Fläche live
|
||
## (dasselbe Material wie der Client).
|
||
func _on_water_changed(value: float) -> void:
|
||
if _loading_ui_values or main.doc.is_empty():
|
||
return
|
||
if value <= KEIN_WASSER_WERT + 0.0001:
|
||
main.doc.erase("water_level")
|
||
main.call("_set_status", "Wasser entfernt (kein Wasser in dieser Karte).")
|
||
else:
|
||
var terrain: Dictionary = main.doc.get("terrain", {})
|
||
var ok := true
|
||
if not terrain.is_empty():
|
||
# Range-Bewacher (V20-Band): sinnlose Werte gar nicht erst
|
||
# ins doc schreiben — der Slider klemmt am Höhenbereich.
|
||
var hmin := float(terrain.get("min_height_m", -5.0))
|
||
var hmax := float(terrain.get("max_height_m", 500.0))
|
||
var geklemmt := clampf(value, hmin - 10.0, hmax + 10.0)
|
||
if geklemmt != value:
|
||
ok = false
|
||
value = geklemmt
|
||
prop_water.set_value_no_signal(geklemmt)
|
||
main.call("_set_status", "Wasserhöhe an den Terrain-Höhenbereich geklemmt (%.1f m)." % geklemmt)
|
||
if ok or value != KEIN_WASSER_WERT:
|
||
main.doc["water_level"] = value
|
||
main.dirty = true
|
||
main.call("_refresh_file_label")
|
||
main.view_builder.call("rebuild", main.doc, main.manifest)
|
||
main.call("_sync_grid_to_builder")
|
||
|
||
|
||
## Felder aus dem doc auffüllen (aus main.gd _refresh_all-Anfang ausgezogen).
|
||
func refresh_values() -> void:
|
||
_loading_ui_values = true
|
||
var info: Dictionary = main.doc.get("info", {})
|
||
prop_name.text = str(info.get("name", ""))
|
||
prop_author.text = str(info.get("author", ""))
|
||
prop_desc.text = str(info.get("description", ""))
|
||
prop_version.value = float(info.get("version", 1))
|
||
var weathers := ["mittag", "abend", "regen", "nacht"]
|
||
var weather := str(main.doc.get("weather", "mittag"))
|
||
prop_weather.select(maxi(0, weathers.find(weather)))
|
||
# Himmel: Auswahl ↔ doc["weather_sky"] (Katalog-ID; unbekannt/leer → Standard).
|
||
var sky_id := str(main.doc.get("weather_sky", ""))
|
||
var sky_idx := 0
|
||
for i in SkyboxKatalog.EINTRAEGE.size():
|
||
if SkyboxKatalog.EINTRAEGE[i]["id"] == sky_id:
|
||
sky_idx = i + 1
|
||
prop_sky.select(sky_idx)
|
||
_update_sky_preview(sky_id if sky_idx > 0 else "")
|
||
|
||
var terrain: Dictionary = main.doc.get("terrain", {})
|
||
terrain_info.text = "Größe: %.0f × %.0f m\nAuflösung: %d × %d Höhenpunkte\nHöhenbereich: %.0f bis %.0f m" % [
|
||
float(terrain.get("size_x_m", 0.0)), float(terrain.get("size_z_m", 0.0)),
|
||
int(terrain.get("resolution", 0)),
|
||
int(terrain.get("resolution", 0)),
|
||
float(terrain.get("min_height_m", 0.0)), float(terrain.get("max_height_m", 0.0))]
|
||
# Wasser (Karte t_28fdef03): Regler aus dem doc füllen (ohne dirty).
|
||
var wl: Variant = main.doc.get("water_level", null)
|
||
prop_water.set_value_no_signal(
|
||
float(wl) if (wl is float or wl is int) else KEIN_WASSER_WERT)
|
||
# Loadout-Bereich (Registry-Gruppe "map") aus dem doc nachziehen.
|
||
var modus_panel: Node = _sections.get_node_or_null("modus") if _sections != null else null
|
||
if modus_panel != null:
|
||
modus_panel.call("refresh_values")
|
||
var loadout_panel: Node = _sections.get_node_or_null("loadout") if _sections != null else null
|
||
if loadout_panel != null:
|
||
loadout_panel.call("refresh_values")
|
||
_loading_ui_values = false
|