scopedvr-map-editor/tests/test_modus.gd
2026-11-01 19:34:05 +01:00

183 lines
9.5 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

extends SceneTree
## Editor-Test (headless): Spielmodus, Conquest-Zonen, Hills, Modus-Spawns (V21–V24) — Neu-Dialog,
## Katalog, Panel mit Undo/Redo, Speichern/Laden. Ausführen:
## godot --headless --path . --script res://tests/test_modus.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 _signed(d: Dictionary) -> Dictionary:
var c := d.duplicate(true)
SvmapIO.sign_map(c)
return c
func _run_all() -> void:
print("Testgruppe: Katalog + Neu-Map")
var tdm := SvmapDocument.create_new("T", 512.0, "mittag")
_check(not tdm.has("game_mode") and not tdm.has("conquest_zones") and not tdm.has("hills") and not tdm.has("mode_spawns"), "TDM-Map ohne Zusatzfelder (altes Format)")
_check(SvmapIO.validate_map(_signed(tdm)).is_empty(), "TDM-Map gültig")
var cp := SvmapDocument.create_new("C", 512.0, "mittag", "conquest")
_check(cp["game_mode"] == "conquest" and (cp["conquest_zones"] as Array).size() == 3, "Conquest-Map mit 3 Start-Zonen")
_check(SvmapIO.validate_map(_signed(cp)).is_empty(), "Conquest-Map gültig: " + str(SvmapIO.validate_map(_signed(cp))))
var kh := SvmapDocument.create_new("K", 512.0, "mittag", "koth")
_check(kh["game_mode"] == "koth" and (kh["hills"] as Array).size() == 3 and not kh.has("conquest_zones"), "KotH-Map mit 3 Hills")
_check(SvmapIO.validate_map(_signed(kh)).is_empty(), "KotH-Map gültig: " + str(SvmapIO.validate_map(_signed(kh))))
var dmd := SvmapDocument.create_new("D", 512.0, "mittag", "dm")
_check(dmd["game_mode"] == "dm" and SvmapIO.validate_map(_signed(dmd)).is_empty(), "Deathmatch-Map gültig")
# Alt-Map (vor dem Umbau): "checkpoints" lädt als Conquest, speichert neu
var alt := cp.duplicate(true)
alt["game_mode"] = "checkpoints"
alt["checkpoints"] = alt["conquest_zones"]
alt.erase("conquest_zones")
var alt_signed := _signed(alt)
_check(SvmapIO.validate_map(alt_signed).is_empty(), "Alt-Map (checkpoints) ist gültig")
var alt_res := SvmapIO.parse_and_validate(JSON.stringify(alt_signed))
_check(alt_res["ok"] and alt_res["data"]["game_mode"] == "conquest" and alt_res["data"].has("conquest_zones") and not alt_res["data"].has("checkpoints"), "Alt-Map wird beim Laden zu Conquest")
var bad := cp.duplicate(true)
bad["conquest_zones"] = []
_check(not ModusKatalog.validate(bad, bad["bounds"]).is_empty(), "Modus ohne Zonen ungültig (V21)")
bad = kh.duplicate(true)
bad["hills"] = []
_check(not ModusKatalog.validate(bad, bad["bounds"]).is_empty(), "KotH ohne Hill ungültig (V21)")
bad = cp.duplicate(true)
bad["conquest_zones"][0]["pos"] = [9999.0, 0.0, 0.0]
_check(not ModusKatalog.validate(bad, bad["bounds"]).is_empty(), "Zone außerhalb ungültig (V22)")
bad = cp.duplicate(true)
bad["conquest_zones"][1]["id"] = "C1"
_check(not ModusKatalog.validate(bad, bad["bounds"]).is_empty(), "doppelte ID ungültig (V22)")
bad = cp.duplicate(true)
bad["game_mode"] = "ctf"
_check(not ModusKatalog.validate(bad, bad["bounds"]).is_empty(), "unbekannter Modus ungültig (V21)")
print("Testgruppe: Neu-Dialog")
var dlg: ConfirmationDialog = preload("res://core/ui/new_map_dialog.gd").new()
root.add_child(dlg)
_check(dlg.get_game_mode() == "tdm", "Dialog-Default TDM")
dlg.mode_option.select(1)
_check(dlg.get_game_mode() == "conquest", "Dialog liefert Conquest")
dlg.mode_option.select(2)
_check(dlg.get_game_mode() == "koth", "Dialog liefert King of the Hill")
dlg.mode_option.select(3)
_check(dlg.get_game_mode() == "dm", "Dialog liefert Deathmatch")
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("ModusTest", 512.0, "mittag")
main.history.doc = main.doc
main.call("_refresh_all")
var panel: Node = main.map_mod.sections_host().get_node_or_null("modus")
_check(panel != null, "Modus-Panel hängt im Map-Tab")
if panel == null:
quit(1)
return
_check(not panel.cp_box.visible, "TDM: Zonen-Bereich verborgen")
panel.set_mode("conquest")
_check(main.doc["game_mode"] == "conquest" and main.doc["conquest_zones"].size() == 3, "Wechsel auf Conquest setzt 3 Zonen")
_check(panel.cp_box.visible and panel.rows_box.get_child_count() == 3, "3 Zeilen im Panel")
_check(main.view_builder.zone_root != null and main.view_builder.zone_root.get_child_count() == 3, "3 Zonen im 3D-Bild")
var id: String = panel.add_zone(50.0, -30.0)
_check(id == "C4" and main.doc["conquest_zones"].size() == 4, "neue Zone C4")
panel.edit_zone("C4", {"radius_m": 25.0})
panel.edit_zone("C4", {"x": 80.0, "z": 10.0, "label": "Turm"})
var c4: Dictionary = main.doc["conquest_zones"][3]
_check(c4["radius_m"] == 25.0 and c4["pos"][0] == 80.0 and c4["pos"][2] == 10.0, "Radius + Position geändert")
panel.edit_zone("C4", {"radius_m": 500.0})
_check(main.doc["conquest_zones"][3]["radius_m"] == ModusKatalog.MAX_RADIUS, "Radius wird begrenzt")
_check(SvmapIO.validate_map(_signed(main.doc)).is_empty(), "Map gültig nach Änderungen")
panel.remove_zone("C4")
_check(main.doc["conquest_zones"].size() == 3, "Zone gelöscht")
main.history.undo()
main.call("_refresh_all")
_check(main.doc["conquest_zones"].size() == 4, "Undo: Zone zurück")
while main.history.can_undo():
main.history.undo()
main.call("_refresh_all")
_check(not main.doc.has("game_mode") and not main.doc.has("conquest_zones") and not panel.cp_box.visible, "Undo bis Start: Felder weg, UI nachgezogen")
main.history.redo()
main.call("_refresh_all")
_check(main.doc.get("game_mode", "") == "conquest", "Redo: Modus wieder da")
panel.set_mode("tdm")
_check(not main.doc.has("game_mode"), "Zurück auf TDM entfernt game_mode")
_check(main.view_builder.zone_root == null, "TDM: keine Zonen im Bild")
print("Testgruppe: King of the Hill")
panel.set_mode("koth")
_check(main.doc["game_mode"] == "koth" and main.doc["hills"].size() == 3, "KotH setzt 3 Hills")
_check(panel.rows_box.get_child_count() == 3 and main.view_builder.zone_root.get_child_count() == 3, "3 Hill-Zeilen und 3 Marker")
var first_id: String = main.doc["hills"][0]["id"]
panel.move_zone(first_id, 1)
_check(main.doc["hills"][1]["id"] == first_id, "Hill nach hinten geschoben (Reihenfolge)")
panel.move_zone(first_id, 5)
_check(main.doc["hills"][1]["id"] == first_id, "Verschieben über den Rand wirkt nicht")
var hid: String = panel.add_zone(10.0, 10.0)
_check(hid == "H4" and main.doc["hills"].size() == 4, "neuer Hill H4")
main.history.undo()
main.history.undo()
main.call("_refresh_all")
_check(main.doc["hills"][0]["id"] == first_id and main.doc["hills"].size() == 3, "Undo stellt Reihenfolge und Liste wieder her")
_check(SvmapIO.validate_map(_signed(main.doc)).is_empty(), "KotH-Map gültig")
print("Testgruppe: Modus-Spawns")
_check(not main.doc.has("mode_spawns"), "ohne Anlegen kein mode_spawns-Block")
panel.select_spawn_set("koth")
_check(SpawnOps.edit_set == "koth" and SpawnOps.count_points(main.doc, 0) == 0, "KotH-Satz leer, solange nichts angelegt")
panel.create_spawns("koth")
_check(main.doc.has("mode_spawns") and main.doc["mode_spawns"]["koth"]["0"]["points"].size() == 4, "Anlegen kopiert die 4 Basis-Spawns je Team")
_check(SpawnOps.count_points(main.doc, 0) == 4 and SpawnOps.count_points(main.doc, 1) == 4, "Spawn-Werkzeug sieht jetzt den KotH-Satz")
var kid := str(main.doc["mode_spawns"]["koth"]["0"]["points"][0]["id"])
_check(kid.begins_with("KA") and SpawnOps.next_id(main.doc, 0).begins_with("KA"), "IDs mit Modus-Präfix (%s)" % kid)
var added := SpawnOps.add_zone(main.doc, 0, 30.0, 30.0)
_check(main.doc["mode_spawns"]["koth"]["0"]["points"].size() == 5 and main.doc["spawn_zones"]["0"]["points"].size() == 4, "neuer Punkt landet nur im KotH-Satz")
_check(SvmapIO.validate_map(_signed(main.doc)).is_empty(), "Map mit eigenen Spawns gültig: " + str(SvmapIO.validate_map(_signed(main.doc))))
main.history.undo()
main.call("_refresh_all")
_check(not main.doc.has("mode_spawns"), "Undo entfernt den Spawn-Satz wieder")
panel.select_spawn_set("dm")
panel.create_spawns("dm")
_check(main.doc["mode_spawns"]["dm"].has("all") and main.doc["mode_spawns"]["dm"]["all"]["points"].size() == 8, "Deathmatch: beide Teams in einem Topf (8 Punkte)")
SpawnOps.add_zone(main.doc, 1, 40.0, 40.0)
_check(main.doc["mode_spawns"]["dm"]["all"]["points"].size() == 9 and not main.doc["mode_spawns"]["dm"].has("1"), "Deathmatch: Team-Werkzeug schreibt nach 'all'")
_check(SvmapIO.validate_map(_signed(main.doc)).is_empty(), "DM-Spawns gültig")
var bad_ms: Dictionary = main.doc.duplicate(true)
bad_ms["mode_spawns"]["dm"]["0"] = {"points": [{"id": "X", "pos": [0.0, 0.0, 0.0]}]}
_check(not ModusKatalog.validate(bad_ms, bad_ms["bounds"]).is_empty(), "V24: Team-Schlüssel im DM-Satz ungültig")
panel.remove_spawns("dm")
_check(not main.doc.has("mode_spawns") and SpawnOps.edit_set == "base", "Löschen entfernt den Satz und schaltet auf Basis zurück")
SpawnOps.edit_set = "base"
panel.set_mode("conquest")
var path := "user://modus_test.svmap"
var doc2 := _signed(main.doc)
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"]["game_mode"] == "conquest" and back["data"]["conquest_zones"].size() >= 3, "Laden: Modus + Zonen erhalten")
if failures == 0:
print("ALL OK: %d checks" % checks)
quit(0)
else:
print("FAILED: %d/%d checks" % [failures, checks])
quit(1)