349 lines
21 KiB
Markdown
349 lines
21 KiB
Markdown
# ScopedVR Map Editor
|
||
|
||
Standalone-PC-App zum Bauen von ScopedVR-Maps. Technisches Fundament: Godot 4.x
|
||
(MIT-Lizenz, lizenzfrei, keine Fremd-Abhängigkeit). Creator sehen/brauchen
|
||
Godot NIE — die App ist das Produkt (PLAN.md, Entscheidung 14).
|
||
|
||
## ⭐ Chef-Grundsatz (Sly, 15.10.): MAXIMALE NUTZERFREUNDLICHKEIT
|
||
> „Der soll auf jeden Fall so nutzerfreundlich sein, wie es nur geht, damit
|
||
> echt richtig easy ist Maps zu bauen und es auch Spaß macht!"
|
||
|
||
Konkret heißt das für JEDE Editor-Karte:
|
||
- Deutsch, große Buttons, Klartext-Beschriftungen — kein Fachchinesisch.
|
||
- Klick statt Konsole: gewünschte Aktion per Maus erreichbar (ziehen, drehen,
|
||
klicken), ohne Tastenkürzel auswendig zu lernen (Kürzel dürfen optional sein).
|
||
- Sofortiges visuelles Feedback (Objekt leuchtet beim Anfassen, Vorschau
|
||
vor dem Platzieren, Undo mit STRG+Z).
|
||
- Die App hilft: ungültige Aktionen erklären WARUM („Spawn-Zone fehlt noch
|
||
für Team Rot — klicke auf den Boden, um sie zu setzen").
|
||
- Test-Benchmark: Sly baut ohne Anleitung in < 10 Minuten eine kleine Map.
|
||
|
||
## Philosophie (Entscheidung 20 — GESCHLOSSENER Baukasten)
|
||
- Objekte kommen ausschließlich aus der Spiel-Asset-Bibliothek (geprüfte Poly-Budgets).
|
||
- KEINE freien Mesh-Imports. Konsistenz + Performance statt unbegrenzter Freiheit.
|
||
- Performance-Prüfung ist eingebaut (Objekt-Count-/Poly-Limits), nicht optional.
|
||
|
||
## MVP-Werkzeuge (Reihenfolge = Karten-Kette)
|
||
1. **Terrain** — Höhenfeld bearbeiten (heben/senken/glätten), Sniper-Distanzen 100–800 m (Entscheidung 7).
|
||
2. **Objekt-Platzierung** — Bibliothek-Browser, platzieren/drehen/verschieben.
|
||
3. **Spawn-Zonen** — pro Team (TDM), Validierung gegen Regeln.
|
||
4. **Map-Metadaten** — Name, Autor, Version, Wetter-Preset (mittag/abend/regen/nacht, siehe Weather-System 9ebe8ef/a296986).
|
||
5. **Performance-Check** — Objekt-Count/Poly-Budget gegen die FPS-Baseline.
|
||
6. **Export** — `.svmap`-Paket (Format: docs/map_format.md im scopedvr-match-server Repo, Karte t_74976e9f).
|
||
|
||
## Architektur (MVP)
|
||
- Godot 4.x Desktop-App (die Editor-UI IST Godot-Control-UI — pragmatisches MVP).
|
||
- Map-Datenmodell = 1:1 das svmap-Format (kein internes Zwischenformat → kein Konverter).
|
||
- Speichern/Laden direkt auf .svmap; Export = Kopie + Validierung.
|
||
|
||
## Milestone-Zuordnung
|
||
M2 (siehe PLAN.md §6): Map-Paket-Format → Editor-MVP → erste offizielle Map
|
||
(offenes Gelände) → Map-Repo/Release-Workflow über Forgejo (M3).
|
||
|
||
## Status (M2, Karte t_40d855d0 — UX-Fundament FERTIG)
|
||
|
||
UX-Fundament auf dem App-Skelett (t_5badf371), nach dem Chef-Grundsatz gebaut:
|
||
|
||
*(Unverändert darunter: svmap laden/speichern V1–V14 mit Prüfsummen-Interop,
|
||
Neue-Map-Dialog mit 4 Pflicht-Spawns pro Team, Orbit-Viewport mit Gitter/
|
||
Boden/Wetter-Stimmung — siehe Commit 84bc081 und `svmap_io.gd`.)*
|
||
|
||
- **Undo/Redo (`src/editor/action_history.gd`):** zentrale ActionHistory über
|
||
die svmap-Ober-Blöcke (`objects`/`terrain`/`spawn_zones` — tiefe Kopien vor
|
||
und nach jeder Änderung, merge_tags gegen Mausrad-Tick-Fluten, Max-Stufe 100).
|
||
Strg+Z / Strg+Y UND große Buttons „↩ Rückgängig / ↪ Wiederholen" oben.
|
||
Spätere Werkzeuge melden sich mit `snapshot(keys)` vor und
|
||
`commit(desc, before, after)` nach der Änderung an — Terrain-Strokes und
|
||
Spawn-Zonen landen damit automatisch in derselben History.
|
||
- **Objekt-Platzierung mit Ghost-Preview (`src/editor/interaction.gd`):**
|
||
Bibliothek links (deutsche Namen + Poly-Zahl), Klick startet den
|
||
Platzier-Modus — eine transparente Vorschau schwebt am Cursor auf dem Boden
|
||
(Höhe kommt aus dem Terrain), Klick setzt, ESC bricht ab (Rechte
|
||
Maustaste = Flug, das Objekt bleibt in der Hand — Karte t_2470de89),
|
||
Mausrad dreht die Vorschau VOR dem Setzen (15°-Raster). Modus bleibt an
|
||
für schnelles Mehrfachsetzen. Poly-Budget wird VOR dem Platzieren geprüft.
|
||
- **Auswahl-Anfassen:** Klick wählt ein Objekt (orange + Emission), Ziehen
|
||
verschiebt es (auf Terrain-Höhe geclampt in der Map), R-Taste ODER Mausrad
|
||
dreht (15°-Raster, Shift+R zurück), Entf löscht. Auch über den Objekt-Baum
|
||
wählbar (beide Wege bleiben synchron). Panel „Auswahl“ rechts zeigt Position/
|
||
Richtung + Bedienhinweise.
|
||
- **Helfende Meldungen:** Statusleiste unten erklärt nach jeder Aktion den
|
||
NÄCHSTEN Schritt („Klicke in die Karte“, „Strg+Z holt es zurück“,
|
||
Budget-Warnung ab 80 %). Fehler-Dialoge bleiben Klartext, keine Godot-Texte
|
||
im sichtbaren UI.
|
||
- **Deutsche, große UI:** Schrift 16 px Basis (≥ 14 px überall), Buttons/
|
||
Klickflächen ≥ 40 px Höhe, Mausrad-Konflikt gelöst: Rad dreht Objekt/
|
||
Vorschau, wenn eines aktiv ist — sonst zoomt es die Kamera.
|
||
- **Tests:** `tests/test_ux.gd` (History-Roundtrips setzen/löschen/verschieben/
|
||
drehen, Merge-Tag-Verhalten, Alias-Sicherheit der Snapshots, Kapazität,
|
||
Terrain-Höhen-Abfrage, Platzhalter-Meshes).
|
||
Lauf: `godot --headless --path . --script res://tests/test_ux.gd`
|
||
|
||
**Bekannte Falle (15.10. live passiert):** Nach `git pull` mit NEUEN
|
||
`class_name`-Skripten (z. B. `action_history.gd`) muss einmal
|
||
`godot --headless --import` laufen, bevor die GUI gestartet wird — sonst ist
|
||
der globale Klassen-Cache (`.godot/`) alt, `main.gd` wirft „Identifier not
|
||
declared" und das Fenster startet ohne UI. Symptom: Parse-Errors im App-Log
|
||
trotz grünem Test-Lauf (der ja selbst mit importiertem Cache lief).
|
||
|
||
**Bibliothek:** `library/objects.json` ist die Editor-Kopie der offiziellen
|
||
Registry aus dem Match-Server-Repo — neue Objekte DORT registrieren und hier
|
||
synchron ziehen (IDs sind der Vertrag, Entscheidung 20).
|
||
|
||
## Status (M2, Karte t_3a998ae4 — Objekt-Bibliothek FERTIG)
|
||
|
||
Auf dem Terrain-Werkzeug (t_9aba04f1) aufgebaut, nach dem Chef-Grundsatz:
|
||
|
||
- **Bibliothek-Browser mit Gruppen (`src/editor/library_browser.gd`):** Die
|
||
11 etablierten Objekte aus `library/objects.json`, sortiert in deutsche
|
||
Klapp-Gruppen (Kisten & Barrieren / Bauwerke / Natur + automatisch
|
||
„Sonstiges" für neue, noch ungruppierte IDs). Jeder Button zeigt ein
|
||
128-px-Thumbnail (`library/thumbs/*.png` — einmalig mit
|
||
`xvfb-run -a godot4 --rendering-driver opengl3 --path . --script
|
||
res://tools/gen_thumbs.gd` gerendert und committed; fehlt ein PNG, fällt
|
||
der Button sauber aufs Text-Layout zurück). Deutsche Namen wohnen jetzt in
|
||
`LibraryBrowser` (ohne UI → headless testbar).
|
||
- **Snap to Ground (`src/editor/object_ops.gd`):** Ein Klick auf
|
||
„⚡ Alles auf den Boden setzen" holt ALLE schwebenden Objekte auf die
|
||
Terrain-Höhe (z. B. nach dem Terrain-Formen) — EIN Undo-Eintrag für die
|
||
ganze Aktion. Objekte, die schon auf dem Boden stehen, bleiben unangetastet.
|
||
- **Raster-Snap (1 m):** CheckButton in den Objekt-Werkzeugen — beim
|
||
Platzieren UND Ziehen rasten X/Z auf ganze Meter (Höhe folgt weiter dem
|
||
Terrain). AUS = freies Platzieren wie bisher.
|
||
- **svmap-Roundtrip:** Objekte (Bibliotheks-ID + Pos/Rot/Scale) überleben
|
||
Speichern/Laden identisch, save→load→save bleibt byte-identisch
|
||
(`tests/test_library.gd`).
|
||
- **Tests:** `tests/test_library.gd` (Bibliothek laden, Gruppen komplett/
|
||
überschneidungsfrei, Thumbnails 128×128, Raster-Snap, Snap-to-Ground + je
|
||
Undo für setzen/drehen/verschieben/löschen über ALLE 11 Objekte,
|
||
svmap-Roundtrip mit Objekten + Poly-Budget aus dem Katalog).
|
||
- **Desktop-Smoke:** `godot … -- --library-demo [ordner] [--library-demo-quit]`
|
||
fährt Bibliothek/Platzierung/Schweben/Snap programmatisch durch und
|
||
speichert Screenshots.
|
||
|
||
**Noch offen (folgende Karten der MVP-Kette):** Performance-Check-UI,
|
||
Export-Workflow.
|
||
|
||
## Status (M2, Karte t_c4162c5d — Spawn-Zonen FERTIG)
|
||
|
||
Auf der Objekt-Bibliothek (t_3a998ae4) aufgebaut, nach dem Chef-Grundsatz:
|
||
|
||
- **Spawn-Zonen malen (`src/editor/spawn_tool.gd`):** Werkzeug
|
||
„🚩 Spawn-Zonen malen“ — Team Blau 🔵 / Team Rot 🔴 (TDM-Konvention:
|
||
IDs A…/B…, mapformat § spawn_zones), Klick setzt eine farbige flache
|
||
Kreis-Zone auf dem Terrain (Vorschau-Kreis folgt der Maus), Radius
|
||
2–30 m per Regler. Zone anklicken → auswählen + ziehen (Terrain-Höhe
|
||
bleibt), Entf löscht, Strg+Z nimmt JEDE Operation zurück (jede Zone =
|
||
EIN History-Eintrag, Radius-Slider-Ticks sind merge-gekoppelt).
|
||
- **Live-Validierung (`src/editor/spawn_ops.gd`):** Block „Spawn-Check“
|
||
rechts bleibt STÄNDIG sichtbar und spiegelt exakt die Loader-Regeln
|
||
(V11: ≥ 4 Spawn-Punkte pro Team · V7: Objekt-IDs in der Bibliothek) —
|
||
z. B. „✅ Spawns Team Blau: 4 · ❌ Spawns Team Rot: 2 (mindestens 4
|
||
nötig)“ + Klartext-Hinweis, WIE die Zone ergänzt wird. Fehler zeigt der
|
||
Editor VOR dem Export, nicht erst beim Speichern.
|
||
- **Map-Fertig-Check:** Button „✅ Karte prüfen“ oben — Klartext-Liste
|
||
✅/❌ (Name, Autor, beide Teams, Objekt-IDs, Poly-Budget) in einem
|
||
Fenster; fehlt etwas, wackelt (Shake-Tween) der Button und die
|
||
Statusleiste sagt „Map ist noch nicht bereit — siehe Warnungen“.
|
||
Der harte Gate bleibt das Speichern (`SvmapIO.save_to_file` —
|
||
dieselbe `_validate_core`-Funktion, EINE Implementierung, kein Drift).
|
||
- **Export-Signierung:** unverändert der eine Code-Pfad — Editor-Speichern
|
||
ruft `SvmapIO.sign_map` (SHA-256 über kanonisierte Payload). Abnahme mit
|
||
ECHTEM Server-Loader: Editor-Export durch `SvmapLoader.parse_and_validate`
|
||
aus scopedvr-match-server gejagt → ok=true inkl. V13-Prüfsummen-Check
|
||
(kein Copy-Paste-Drift, Interop bewiesen).
|
||
- **Radius-Persistenz:** Der Zonen-Radius ist KEIN Formatfeld (spawn_zones
|
||
speichert Punkte) — er wohnt als `design_notes.spawn_radius_m` in der
|
||
Map (Loader prüft design_notes nicht → rundtrip-sicher). Zonen sitzen
|
||
AUF dem Terrain (`terrain_height_at`), egal wie hügelig die Map ist.
|
||
- **Tests:** `tests/test_spawns.gd` (52 Checks: setzen/ändern/löschen +
|
||
je Undo/Redo mit Radius, Merge-Tags, Live-Status ✅/❌, ready_report
|
||
grün/rot, Export→Re-Import identisch + byte-identischer Roundtrip,
|
||
Zone auf Terrain-Höhe). Parse-Check: 23 Scripts, 0 Fehler.
|
||
- **Desktop-Smoke:** `godot … -- --spawns-demo [ordner] [--spawns-demo-quit]`
|
||
fährt Zonen-Übersicht → Live-Check-Alarm → Undo → „Karte prüfen“
|
||
programmatisch durch und speichert Screenshots.
|
||
|
||
## Status (M2, Karte t_7567a06f — Objekt-Ops: Duplizieren + Hoch/Runter + Spiegeln)
|
||
|
||
Auf dem UX-Fundament (t_40d855d0) und der Objekt-Bibliothek (t_3a998ae4)
|
||
aufgebaut, nach dem Chef-Grundsatz (Klick statt Konsole, deutsche UI,
|
||
Undo für alles):
|
||
|
||
- **Duplizieren (`⧉ Duplizieren`-Button + Strg+D):** Die Kopie des
|
||
ausgewählten Objekts hängt +2 m in X versetzt daneben (Rasterversatz,
|
||
Karte t_c46d31c2; Füßpunkt-Höhe unverändert, mit Raster-Snap eingerastet,
|
||
wenn an) und ist DIREKT angewählt — sie lässt sich gleich
|
||
weiterziehen/drehen/löschen. Drehung, Größe und Höhe werden übernommen.
|
||
Bei Mehrfachauswahl werden ALLE Objekte kopiert (EIN Undo-Schritt).
|
||
Poly-Budget und Objekt-Limit werden VOR dem Kopieren geprüft
|
||
(Klartext-Meldung, was zu tun ist). EIN Undo-Schritt.
|
||
- **Anheben/Absenken (`▲ Anheben / ▼ Absenken` + Bild↑/Bild↓):** Objekte
|
||
ändern die Höhe in 0,25-m-Schritten. MEHRERE Klicks in Serie = EIN
|
||
Undo-Schritt. Design-Entscheidungen, dokumentiert:
|
||
- Die Höhe wohnt direkt in `object.pos.y` — das svmap-Format erlaubt
|
||
freie y-Werte innerhalb der Match-Grenzen (Loader-Regel V9), deshalb
|
||
ist **KEIN Extra-Feld im Dateiformat nötig** und die Loader-Kompatibilität
|
||
bleibt unangetastet (bewiesen in `tests/test_ux.gd`: Erhöhtes Objekt
|
||
überlebt gespeichern → laden intakt (Höhe bleibt exakt)).
|
||
- **Absenken / VERSENKEN (Karte t_e166e656):** Absenken geht FREI unter die
|
||
Terrain-Höhe — Steine dürfen halb im Boden stecken (Sly-Wunsch).
|
||
Untergrenze ist die Match-Grenze `bounds.min.y`; die Auswahl-Anzeige und
|
||
die Statusleiste zeigen die Versenk-Tiefe („1,00 m im Boden versenkt“).
|
||
„Auf Boden setzen“ holt ein versenktes Objekt wieder exakt auf das
|
||
Terrain.
|
||
- **Anheben** hat als Obergrenze die Match-Grenze `bounds.max.y` — höher
|
||
passt der Server nicht (V9).
|
||
- **Ziehen behält die Höhe:** Ein angehobenes Objekt schwebt beim
|
||
Verschieben in derselben Höhe über dem (neuen) Boden weiter; bodennah
|
||
bleibt bodennah.
|
||
- Die Auswahl-Anzeige zeigt die Höhe mit („+0,25 m über dem Boden“).
|
||
- **Spiegeln (`⇄ Links↔Rechts` / `⇅ Vor↔Zurück`):** Das Objekt wird AN
|
||
SEINER STelle gespiegelt — Position bleibt, die Blickrichtung dreht:
|
||
- links↔rechts (Welt-X-Spiegelung): `rot_y_deg → −rot`
|
||
- vor↔zurück (Welt-Z-Spiegelung): `rot_y_deg → 180° − rot`
|
||
(Herleitung im Quellcode: gespiegelter Richtungs-Vektor = gespiegelter
|
||
Winkel; jede Spiegelung ist dadurch selbstinvers — zweimal spiegeln =
|
||
Ausgangszustand.) Die Rotation wird mitgespiegelt, wie bestellt.
|
||
KIPPEN wird mitgespiegelt (Karte t_e166e656): Achsenspiegelung =
|
||
Konjugation der Basis `M·B·M` — X-Kippen bleibt bei X-Spiegelung,
|
||
Z-Kippen dreht das Vorzeichen (und umgekehrt); Punktspiegelung =
|
||
`Ry(180°)·B`. Für reines Y-Drehen ergeben beide Regeln exakt die alten
|
||
Zahlenwerte (Doku: docs/map_format.md im Match-Server-Repo).
|
||
Bewusst NICHT um die Kartemitte gespiegelt: das würde das Objekt quer
|
||
über die Map springen lassen — überraschend für Einsteiger. Klartext
|
||
in Buttons + Tooltip.
|
||
- **Alles über ActionHistory:** Snapshot vor/nach JEDE Aktion
|
||
(Duplizieren = EIN Schritt, Anheben-Serie = EIN Schritt via Merge-Tag,
|
||
Spiegeln = EIN Schritt). Statusleiste erklärt nach jeder Aktion den
|
||
nächsten Schritt und die Grenzen auf Deutsch.
|
||
- **Tests:** `tests/test_ux.gd` auf 90 Checks erweitert (42 neu):
|
||
Duplizieren (Raster/ohne Raster/Undo-Roundtrip/Index-Schutz), Anheben/
|
||
Absenken (Schrittweite, Boden-Stopp, Deckel bounds.max, versteckt-Regel,
|
||
Merge-Tags), Spiegeln (X/Z-Mapping, Selbstinvers, Position unverändert),
|
||
svmap-Roundtrip mit angehobener Höhe. `godot … --quit-after` smoke:
|
||
UI baut alle neuen Buttons fehlerfrei.
|
||
## Status (M2, Karte t_f6f29d86 — Auswahl-System FERTIG)
|
||
|
||
Mehrfachauswahl + Gruppieren + Gruppen-Verschieben, auf dem UX-Fundament
|
||
(t_40d855d0) aufgebaut, nach dem Chef-Grundsatz:
|
||
|
||
- **Mehrfachauswahl (`src/editor/interaction.gd`):** Umschalt+Klick fügt
|
||
ein Objekt hinzu/entfernt es, Rahmen-Aufziehen (Rubberband) über leeren
|
||
Boden wählt ALLE Objekte im Rahmen (Umschalt + Rahmen = zusätzliche zur
|
||
Auswahl). Loser Klick auf leere Fläche hebt die Auswahl auf. ALLE
|
||
markierten Objekte färben sich orange, das Auswahl-Panel rechts listet
|
||
sie namentlich auf (ab 9: „… und N weitere“). Projektions-Mathe liegt
|
||
in `src/editor/selection_math.gd` (headless getestet).
|
||
- **Gruppieren:** Buttons „🔗 Gruppieren / ⛓ Gruppe lösen“ im Auswahl-Panel
|
||
+ Kürzel Strg+G / Strg+Umschalt+G. Eine Gruppe braucht ≥ 2 Objekte.
|
||
Gruppen-Mitglieder ziehen und drehen GEMEINSAM — ein Griff an ein
|
||
Gruppen-Mitglied nimmt automatisch die ganze Gruppe (und markiert sie),
|
||
die relative Anordnung bleibt exakt erhalten. Drehen geschieht um den
|
||
Gruppen-Mittelpunkt. „Gruppe lösen“ befreit ALLE Mitglieder (auch die
|
||
nicht mitgewählten), Strg+Z baut sie wieder zusammen.
|
||
- **ENTSCHIEDUNG Gruppen-Persistenz:** Gruppen-Zugehörigkeit liegt als
|
||
OPTIONALES Objekt-Feld `"group"` (int ≥ 0, abwesend = ungruppiert)
|
||
DIREKT im svmap-Format — nicht als Editor-Sitzungsstatus. Damit
|
||
überleben Gruppen Speichern/Laden UND Undo/Redo (ActionHistory kopiert
|
||
den objects-Block tief, das Feld reist mit). Loader-Kompatibilität
|
||
nachgewiesen (scopedvr-match-server `src/maps/svmap_loader.gd`):
|
||
V4 prüft nur Pflichtfelder, unbekannte Objekt-Keys werden nicht
|
||
abgelehnt, die SHA-256-Prüfsumme läuft über das ganze kanonisierte
|
||
Dict (Feld reist automatisch mit). Alte Maps ohne Feld bleiben 1:1
|
||
ladbar, `format_version` bleibt 1 — KEIN Version-Bump, KEINE
|
||
Server-Loader-Änderung nötig. Format-Doku-Anpassung (map_format.md im
|
||
Server-Repo) als Folge-Karte.
|
||
- **Gruppen-Verschieben + Undo:** Ziehen einer Mehrfachauswahl oder Gruppe
|
||
bewegt ALLE beteiligten Objekte (Terrain-Clamp PRO Objekt: X/Z bleiben
|
||
in der Map, Y = Terrain-Höhe an der neuen Stelle) — EINE Aktion =
|
||
EIN ActionHistory-Schritt (auch gemeinsames Drehen, Mausrad-Ticks
|
||
derselben Auswahl sind merge-gekoppelt). Löschen löscht die ganze
|
||
Auswahl in EINEM Schritt.
|
||
- **Tests:** `tests/test_ux.gd` erweitert auf 52 Checks (Gruppen-
|
||
gruppieren/lösen/IDs, gemeinsames Verschieben mit Terrain-Clamp,
|
||
Drehen um die Mitte mit Anordnungs-Erhalt, EIN-Schritt-Undo/Redo,
|
||
svmap-Roundtrip inkl. Prüfsumme + alte-Map-Kompatibilität,
|
||
Rubberband-Projektion inkl. Kamera-Rückseiten-Filter). Komplette
|
||
`run_tests.sh`-Suite grün (206 Checks).
|
||
- **Desktop-Smoke:** `godot … -- --groups-demo [ordner] [--groups-demo-quit]`
|
||
fährt Mehrfachauswahl → Gruppieren → gemeinsames Ziehen/Drehen →
|
||
Undo programmatisch durch und speichert Screenshots.
|
||
|
||
## Karte testen (M2, Karte t_b84af40f — FERTIG)
|
||
|
||
Button „▶ Karte testen“ oben (auch im Datei-Menü) startet die AKTUELLE Map —
|
||
auch ungespeicherte! — sofort als Spiel in einem EIGENEN Fenster. Der Editor
|
||
bleibt offen. Im Spiel: WASD laufen, Maus umsehen, Shift sprinten, Leertaste
|
||
springen, ESC beendet das Test-Fenster sauber (kein Prozess-Kill nötig).
|
||
|
||
- **Temp-Export (`src/editor/test_run.gd`):** Die Map wird nach
|
||
`user://testrun/<name>.svmap` exportiert (deterministischer Name aus dem
|
||
Map-Namen, erneuter Testlauf überschreibt) — über dieselbe
|
||
`SvmapIO.save_to_file`-Validierung wie das echte Speichern. Ist die Map
|
||
noch nicht lauffähig (z. B. Spawn-Zonen fehlen), kommt der Klartext-Block
|
||
mit den fehlenden Punkten, GESTARTET wird nichts.
|
||
- **Client-Suche (erste Treffer-Zeile gewinnt):**
|
||
1. Konfigurationsdatei `user://scopedvr_editor.cfg`:
|
||
```ini
|
||
[test]
|
||
client_dir="/pfad/zu/scopedvr-client"
|
||
godot_path="/pfad/zu/godot" ; nur nötig, wenn der Editor NICHT über Godot läuft
|
||
```
|
||
2. Klon neben dem Editor: `<Editor-Ordner>/../scopedvr-client`
|
||
(erkennbar an `project.godot`).
|
||
- **Start:** Der Client läuft als eigener Godot-Prozess
|
||
(`godot --path <client> -- --testmap <map.svmap> --mock-master`) — kein
|
||
Master, kein Match-Server, alles lokal. Godot-Binary = das, unter dem der
|
||
Editor selbst läuft.
|
||
- **Tests:** `tests/test_testrun.gd` (14 Checks: Temp-Export mit Roundtrip,
|
||
ungültige Map blockiert, Client-Suche inkl. Konfig-Vorrang, Godot-Findung,
|
||
exakte Argumentliste). Parse-Check umfasst `test_run.gd`.
|
||
|
||
## Status (M2, Karte t_c46d31c2 — Tastatur-Shortcuts 1/3: Navigation + Auswahl + Löschen FERTIG)
|
||
|
||
Tastenkürzel-Karte 1 von 3 (Sly-Freigabe 16.10., finale Belegung).
|
||
**`docs/tasten.md` ist jetzt die deutsche Source of truth** für ALLE
|
||
Shortcut-Karten — Karten 2/3 ergänzen dort.
|
||
|
||
- **Konventionen (alle Karten):** Tastaturcode-basiert — InputMap-Actions mit
|
||
PHYSISCHEN `KEY_*`-Konstanten (`src/editor/shortcut_keys.gd`, registriert in
|
||
`main._ready`, idempotent). Physische Codes = deutsche (QWERTZ) und
|
||
Windows-Layouts identisch, keine OS-Hardcodes. Jede Objekt-Operation =
|
||
EIN ActionHistory-Eintrag (Ausnahme: reine Kamera-Aktionen).
|
||
- **Freelook-Flug:** Rechte Maustaste GEDACHT halten = Umsehen + Flug
|
||
(W/S/A/D + Leertaste hoch/Shift runter, Strg = ×3, Alt = ×0,3, Mausrad =
|
||
Grundgeschwindigkeit DAUERHAFT). Umschalt+F = Flug dauerhaft togglen.
|
||
Orbit bleibt Default —
|
||
beim Flug-Exit werden Ziel/Winkel/Abstand aus Flugposition/Blickrichtung
|
||
zurückgerechnet (kein Kamera-Sprung, getestet). Mathe pur in
|
||
`src/editor/fly_math.gd`, Kamera dünn (`src/viewport/orbit_camera.gd`).
|
||
Konflikt-Auflösung: RMB hat ABSOLUTEN Vorrang für den Flug — in JEDEM
|
||
Werkzeugzustand (Terrain, Bauen, Spawn, Platzieren; Karte t_2470de89,
|
||
kein `rmb_filter` mehr), Mausrad im Flug gehört dem Tempo (interaction
|
||
blockt).
|
||
- **F** = Auswahl ins Bild zentrieren — Orbit-Ziel wandert auf den Auswahl-
|
||
Mittelpunkt (Anker + halbe Mesh-Höhe, `SelectionMath.focus_center`); im
|
||
Flug dreht sich nur die Blickrichtung.
|
||
- **Esc** = Prioritätskette Werkzeug verlassen → Ghost-Preview abbrechen →
|
||
Auswahl aufheben, als pure, headless-getestete Funktion
|
||
(`src/editor/shortcut_policy.gd`).
|
||
- **Entf (DELETE)** = Auswahl SOFORT löschen ohne Rückfrage (Sly-Yolo) —
|
||
wirkt auf Szene-Auswahl UND Objektbaum-Markierung (Baum und Szene sind
|
||
synchron), Strg+Z deckt es ab. War schon mehrfach-fähig, jetzt
|
||
getestet (EIN Undo-Schritt holt ALLE zurück).
|
||
- **Strg+D** = Auswahl duplizieren: Kopie(n) +2 m in X (Rasterversatz),
|
||
Füßpunkt unverändert; bei Mehrfachauswahl ALLE Objekte mit EINEM
|
||
Undo-Schritt, Budget/Limit gesammelt VORHER geprüft
|
||
(`ObjectOps.duplicate_objects`).
|
||
- **Strg+LMB** = Mehrfachauswahl umschalten (Objektbaum: SELECT_MULTI +
|
||
native Strg+Klick-Logik, spiegelt sich in die Viewport-Auswahl und
|
||
umgekehrt). Bestandsaktionen (Löschen, Duplizieren, Ziehen, Gruppieren)
|
||
wirken auf die GANZE Mehrfachauswahl.
|
||
- **Tests:** `tests/test_shortcuts.gd` NEU (70 Checks: InputMap-Actions
|
||
inkl. Idempotenz, Freelook-Mathe, Esc-Prioritäten, Duplizieren +2 m X
|
||
mit Undo-Roundtrip, Entf-Mehrfachauswahl gegen die ECHTE interaction.gd,
|
||
Focus-Mittelpunkt, Kamera Flug-Roundtrip). Alle 8 Suiten grün
|
||
(571 Checks), Builder-Export 21/0, Parse-Check aller geänderten Scripts
|
||
(`tools/parse_check_shortcuts.sh`).
|
||
|