extends SceneTree ## Editor-Test (headless): Map-Loadout (V18) — Katalog, Validierung, Panel mit ## Undo/Redo und Speichern/Laden. Ausführen: ## godot --headless --path . --script res://tests/test_loadout.gd var failures := 0 var checks := 0 var main: Control func _initialize() -> void: _run_all.call_deferred() func _check(cond: bool, label: String) -> void: checks += 1 if cond: print(" ok: %s" % label) else: failures += 1 print(" FAIL: %s" % label) func _run_all() -> void: _test_katalog() await _test_panel() if failures == 0: print("ALL OK: %d checks" % checks) quit(0) else: print("FAILED: %d/%d checks" % [failures, checks]) quit(1) func _test_katalog() -> void: print("Testgruppe: Katalog + Validierung") _check(LoadoutKatalog.waffen_ids().size() >= 50, "Katalog hat Waffen") _check(LoadoutKatalog.hat_waffe("m40") and LoadoutKatalog.hat_waffe("pistol1"), "m40 + pistol1 bekannt") _check(LoadoutKatalog.validate(null).is_empty(), "fehlend gültig") _check(LoadoutKatalog.validate({"mode": "player"}).is_empty(), "player gültig") _check(not LoadoutKatalog.validate({"mode": "fixed"}).is_empty(), "fixed ohne Primär ungültig") _check(not LoadoutKatalog.validate({"mode": "fixed", "primary": "nix"}).is_empty(), "unbekannte Waffe ungültig") _check(not LoadoutKatalog.validate({"mode": "x"}).is_empty(), "unbekannter Modus ungültig") _check(LoadoutKatalog.validate({"mode": "fixed", "primary": "sr1", "secondary": ""}).is_empty(), "fixed ohne Sekundär gültig") _check(not LoadoutKatalog.validate({"mode": "fixed", "primary": "sr1", "attachments": {"ar1": ""}}).is_empty(), "Aufsatz für Fremdwaffe ungültig") _check(not LoadoutKatalog.validate({"mode": "fixed", "primary": "sr1", "attachments": {"sr1": "sight=nix"}}).is_empty(), "unbekanntes Teil ungültig") var sel := LoadoutKatalog.auswahl("sr1", "") sel["muzzle"] = "muzzle_suppressor_01" var wire := LoadoutKatalog.encode("sr1", sel) _check(wire == "muzzle=muzzle_suppressor_01", "encode nur Abweichungen: " + wire) _check(LoadoutKatalog.pruefe_aufsaetze("sr1", wire) == "", "encodierter Text gültig") # Rotpunktvisier statt Zielfernrohr: wählbar, gültig, deutscher Name var rot := LoadoutKatalog.auswahl("sr1", "") rot["sight"] = "sight_collimator" var rot_wire := LoadoutKatalog.encode("sr1", rot) _check(rot_wire == "sight=sight_collimator", "Rotpunkt-Draht: " + rot_wire) _check(LoadoutKatalog.validate({"mode": "fixed", "primary": "sr1", "secondary": "", "attachments": {"sr1": rot_wire}}).is_empty(), "Rotpunkt im festen Loadout gültig") _check(LoadoutKatalog.auswahl("sr1", rot_wire)["sight"] == "sight_collimator", "Rotpunkt Rundlauf") _check(LoadoutKatalog.teil_name("sight_collimator") == "Rotpunktvisier", "Rotpunkt-Name deutsch") _check(LoadoutKatalog.teil_name("") == "— nichts —", "leerer Slot benannt") func _test_panel() -> void: print("Testgruppe: Panel") var scene: PackedScene = load("res://core/scenes/main.tscn") main = scene.instantiate() root.add_child(main) await process_frame await process_frame main.doc = SvmapDocument.create_new("LoadoutTest", 512.0, "mittag") main.history.doc = main.doc main.call("_refresh_all") var panel: Node = main.map_mod.sections_host().get_node_or_null("loadout") _check(panel != null, "Loadout-Panel hängt im Map-Tab") if panel == null: return _check(not panel.is_fixed() and not main.doc.has("loadout"), "Start: Spieler-Loadout, kein Block") _check(not panel.fixed_box.visible, "Waffen-Auswahl verborgen") panel.set_fixed(true) _check(panel.is_fixed() and main.doc["loadout"]["primary"] == "m40", "fest: M40 als Start") _check(panel.fixed_box.visible, "Waffen-Auswahl sichtbar") panel.set_weapons("sr1", "") _check(main.doc["loadout"]["primary"] == "sr1" and main.doc["loadout"]["secondary"] == "", "Waffen gesetzt, Sekundär leer") panel.set_part("sr1", "muzzle", "muzzle_suppressor_01") _check(main.doc["loadout"]["attachments"].get("sr1", "") == "muzzle=muzzle_suppressor_01", "Aufsatz gespeichert") panel.set_part("ar1", "muzzle", "muzzle_suppressor_01") _check(not main.doc["loadout"]["attachments"].has("ar1"), "Aufsatz für Nicht-Loadout-Waffe ignoriert") _check(SvmapIO.validate_map(_signed(main.doc)).is_empty(), "Map mit Loadout besteht V1–V18") # Rotpunktvisier: in der Auswahl deutsch benannt, wird gespeichert, besteht die Prüfung var visier := panel.attach_box.find_child("Teil_sr1_sight", true, false) as OptionButton var eintraege: Array[String] = [] if visier != null: for i in visier.item_count: eintraege.append(visier.get_item_text(i)) _check(eintraege.has("Rotpunktvisier"), "Rotpunktvisier in der Visier-Auswahl") panel.set_part("sr1", "sight", "sight_collimator") _check(main.doc["loadout"]["attachments"]["sr1"] == "muzzle=muzzle_suppressor_01;sight=sight_collimator", "Rotpunkt gespeichert") _check(SvmapIO.validate_map(_signed(main.doc)).is_empty(), "Map mit Rotpunkt besteht V1–V18") main.history.undo() main.call("_refresh_all") panel.set_weapons("ar1", "") _check(not main.doc["loadout"]["attachments"].has("sr1"), "Waffenwechsel verwirft Aufsätze der alten Waffe") # Undo/Redo main.history.undo() main.call("_refresh_all") _check(main.doc["loadout"]["primary"] == "sr1" and main.doc["loadout"]["attachments"].has("sr1"), "Undo: Waffenwechsel zurück") main.history.undo() main.history.undo() main.history.undo() main.call("_refresh_all") _check(not main.doc.has("loadout") and not panel.fixed_box.visible, "Undo bis Start: Block weg, UI nachgezogen") main.history.redo() main.call("_refresh_all") _check(main.doc.has("loadout") and panel.is_fixed(), "Redo: fest wieder da") # Zurück auf Spieler-Loadout entfernt den Block panel.set_fixed(false) _check(not main.doc.has("loadout"), "Spieler-Loadout entfernt den Block") # Granaten + Minen: Undo, Spieler-Modus, Validierung _check(not panel.vorrat_check.button_pressed and not panel.vorrat_box.visible, "Vorrat aus: Server-Standard") panel.set_vorrat_aktiv(true) _check(main.doc["loadout"]["mode"] == "player" and main.doc["loadout"]["mines"] == 2, "Vorrat an: Block im Spieler-Modus, Standard 2 Minen") _check(panel.vorrat_box.visible and panel.minen_spin.value == 2, "Eingabefelder sichtbar") panel.set_granaten("he1", 3) panel.set_minen(1) _check(main.doc["loadout"]["grenades"]["he1"] == 3 and main.doc["loadout"]["mines"] == 1, "Anzahlen gespeichert") panel.set_granaten("smoke1", 0) _check(not main.doc["loadout"]["grenades"].has("smoke1"), "0 entfernt die Art") main.history.undo() main.call("_refresh_all") _check(main.doc["loadout"]["grenades"]["smoke1"] == 1 and panel.granaten_spins["smoke1"].value == 1, "Undo holt die Art zurück, UI folgt") panel.granaten_spins["blend1"].value = 4 _check(main.doc["loadout"]["grenades"]["blend1"] == 4, "Eingabefeld schreibt den Wert") _check(LoadoutKatalog.validate(main.doc["loadout"]).is_empty(), "Vorrat gültig") panel.set_fixed(true) _check(panel.is_fixed() and main.doc["loadout"]["mines"] == 1, "Wechsel auf fest behält den Vorrat") panel.set_fixed(false) _check(main.doc["loadout"]["mode"] == "player" and main.doc["loadout"]["mines"] == 1, "Wechsel auf Spieler behält den Vorrat") _check(not LoadoutKatalog.validate({"mode": "player", "mines": 4}).is_empty(), "zu viele Minen ungültig") _check(not LoadoutKatalog.validate({"mode": "player", "grenades": {"he1": 6}}).is_empty(), "zu viele Granaten ungültig") _check(not LoadoutKatalog.validate({"mode": "player", "grenades": {"nix": 1}}).is_empty(), "unbekannte Granate ungültig") _check(not LoadoutKatalog.validate({"mode": "player", "grenades": 3}).is_empty(), "grenades ohne Objekt ungültig") panel.set_vorrat_aktiv(false) _check(not main.doc.has("loadout"), "Vorrat aus im Spieler-Modus entfernt den Block") # Speichern/Laden panel.set_fixed(true) panel.set_weapons("sr2", "pistol2") var path := "user://loadout_test.svmap" var doc2: Dictionary = main.doc.duplicate(true) SvmapIO.sign_map(doc2) var res := SvmapIO.save_to_file(path, doc2) _check(res["ok"], "Speichern ok: " + str(res["errors"])) var back := SvmapIO.load_from_file(path) _check(back["ok"] and back["data"]["loadout"]["primary"] == "sr2", "Laden: Loadout bleibt erhalten") panel.set_vorrat_aktiv(true) panel.set_minen(3) var doc3: Dictionary = main.doc.duplicate(true) SvmapIO.sign_map(doc3) res = SvmapIO.save_to_file(path, doc3) back = SvmapIO.load_from_file(path) _check(res["ok"] and back["ok"] and back["data"]["loadout"]["mines"] == 3, "Laden: Vorrat bleibt erhalten") func _signed(d: Dictionary) -> Dictionary: var c := d.duplicate(true) SvmapIO.sign_map(c) return c