183 lines
9.5 KiB
GDScript
183 lines
9.5 KiB
GDScript
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://src/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://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)
|