extends SceneTree ## Editor-Test (headless): Karte t_f72913d4 — Atmosphäre & Skybox. ## Ausführen: godot --headless --path . --script res://tests/test_skybox.gd ## Exit 0 = alles grün. ## ## T1 svmap-Validierung (V17): weather_sky optional; "sky_panorama"/ ## "prozedural" gültig, Quatsch/nicht-String → V17-Fehler; alte Karte ## ohne Feld validiert unverändert. ## T2 Map-Panel „Himmel“: doc-Roundtrip (schreiben = sky_panorama, ## „Standard“ = Feld ENTFERNEN, Auffüllen aus dem doc). ## T3 Editor-Vorschau (map_view_builder): weather_sky=sky_panorama → ## PanoramaSkyMaterial aus dem Client-Nachbar-Klon + ACES-Tonemap + ## Fog am Skybox-Horizont; ohne Feld → ProceduralSky (alte Maps ## unverändert). var failures := 0 var checks := 0 var main var map_panel func _initialize() -> void: _run_all.call_deferred() func _check(cond: bool, label: String) -> void: checks += 1 print("%s: %s" % ["ok " if cond else "FAIL", label]) if not cond: failures += 1 func _run_all() -> void: var scene: PackedScene = load("res://scenes/main.tscn") _check(scene != null, "scenes/main.tscn laedt") if scene == null: _finish() return main = scene.instantiate() root.add_child(main) await process_frame await process_frame map_panel = main.get("map_mod") _check(map_panel != null, "Map-Panel ist in main eingebaut") if map_panel == null: _finish() return await _t1_validierung() await _t2_panel() await _t3_vorschau() _finish() func _t1_validierung() -> void: print("== T1: svmap-Validierung weather_sky ==") # Alte Karte ohne Feld: unverändert gültig. var alte: Dictionary = main.doc.duplicate(true) alte.erase("weather_sky") SvmapIO.sign_map(alte) var f0: PackedStringArray = SvmapIO.validate_map(alte) _check(f0.is_empty(), "T1 Karte ohne weather_sky validiert ohne Fehler (%s)" % str(f0)) # Gültige Werte (mit frischer Prüfsumme — Feld fließt in die Payload). for wert in ["sky_panorama", "prozedural"]: var d: Dictionary = main.doc.duplicate(true) d["weather_sky"] = wert SvmapIO.sign_map(d) var f: PackedStringArray = SvmapIO.validate_map(d) _check(f.is_empty(), "T1 weather_sky='%s' validiert (%s)" % [wert, str(f)]) # Ungültige Werte → V17 (_validate_core ohne Prüfsumme, wie test_symmetry_core). for wert in ["panorama", 42]: var d2: Dictionary = main.doc.duplicate(true) d2["weather_sky"] = wert var f2: PackedStringArray = SvmapIO._validate_core(d2) var hat_v17 := false for e in f2: if str(e).begins_with("V17"): hat_v17 = true _check(hat_v17, "T1 weather_sky=%s → V17-Fehler (%s)" % [str(wert), str(f2)]) func _t2_panel() -> void: print("== T2: Map-Panel Himmel-Option ==") var prop_sky: OptionButton = map_panel.get("prop_sky") _check(prop_sky != null, "T2 prop_sky existiert") if prop_sky == null: return _check(prop_sky.item_count == 2, "T2 Himmel-Optionen: 2 Einträge (ist %d)" % prop_sky.item_count) _check(str(prop_sky.get_item_text(1)).contains("Pack-Skybox"), "T2 Eintrag 2 = Pack-Skybox (ist „%s“)" % str(prop_sky.get_item_text(1))) # doc mit Panorama → UI zeigt Pack-Skybox. main.doc["weather_sky"] = "sky_panorama" map_panel.call("refresh_values") _check(prop_sky.selected == 1, "T2 doc=sky_panorama → UI Pack-Skybox (ist %d)" % prop_sky.selected) # „Prozedural (Standard)“ → Feld ENTFERNEN (kein "prozedural"-Wert im doc). map_panel.call("_on_sky_changed", 0) _check(not main.doc.has("weather_sky"), "T2 Standard entfernt das Feld") _check(bool(main.get("dirty")), "T2 Änderung markiert dirty") # „Pack-Skybox“ → Feld = sky_panorama (Client-Vertrag, V17-Wert). map_panel.call("_on_sky_changed", 1) _check(str(main.doc.get("weather_sky", "")) == "sky_panorama", "T2 Pack-Skybox schreibt weather_sky=sky_panorama") # UI-Zustand nach externer doc-Änderung korrekt auffüllen. map_panel.call("refresh_values") _check(prop_sky.selected == 1, "T2 refresh_values liest das Feld (Index 1)") # Für T3-Baseline wieder auf Standard. map_panel.call("_on_sky_changed", 0) _check(not main.doc.has("weather_sky"), "T2 Aufräumen: Feld entfernt") func _t3_vorschau() -> void: print("== T3: Editor-Vorschau (map_view_builder) ==") var vb = main.view_builder _check(vb != null, "T3 view_builder vorhanden") if vb == null: return var bscript: Script = vb.get_script() var sky_pfad: String = bscript.panorama_sky_pfad() _check(not sky_pfad.is_empty(), "T3 Client-Nachbar-Klon gefunden (%s)" % sky_pfad) if sky_pfad.is_empty() or not FileAccess.file_exists(sky_pfad): print(" (Pack-Skybox im Nachbar-Klon nicht erreichbar — Panorama-Checks übersprungen)") return main.doc["weather_sky"] = "sky_panorama" vb.call("rebuild", main.doc, main.manifest) await process_frame var we := _welt_env(vb) _check(we != null, "T3 WorldEnvironment vorhanden") if we != null and we.environment != null and we.environment.sky != null: _check(we.environment.sky.sky_material is PanoramaSkyMaterial, "T3 weather_sky=sky_panorama → PanoramaSkyMaterial in der Vorschau") _check(we.environment.tonemap_mode == Environment.TONE_MAPPER_ACES, "T3 Vorschau tonemappt ACES") var wl: Dictionary = bscript.get_script_constant_map().get("WEATHER_LIGHT", {}) var preset: Dictionary = wl.get(str(main.doc.get("weather", "mittag")), {}) if preset.has("panorama_fog_color"): _check((we.environment.fog_light_color as Color).is_equal_approx( preset["panorama_fog_color"]), "T3 Fog am Skybox-Horizont (panorama_fog_color)") # Feld entfernen → Procedural (alte Maps laden unverändert). main.doc.erase("weather_sky") vb.call("rebuild", main.doc, main.manifest) await process_frame var we2 := _welt_env(vb) _check(we2 != null and we2.environment != null and we2.environment.sky != null and we2.environment.sky.sky_material is ProceduralSkyMaterial, "T3 ohne weather_sky → ProceduralSkyMaterial (alte Maps unverändert)") func _welt_env(vb) -> WorldEnvironment: for child in vb.get_children(): if child is WorldEnvironment: return child return null func _finish() -> void: print("--------------------------------------------------") print("CHECKS: %d FAILURES: %d" % [checks, failures]) quit(0 if failures == 0 else 1)