M0: Bestandsaufnahme, Regelwerk, Architektur und Assetmatrix für Champions of Luneraria

Regelwerk trennt verbindliche Produktregeln, Arbeitsannahmen A-F und
eigene Detailentscheidungen. Assetmatrix wird aus den Manifesten erzeugt.
Baseline-Tests (Legacy) 209/33/463/206/1665 bestanden, Logs unter
docs/luneraria/logs/m0-baseline.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Martin Stickelbroeck 2026-09-27 20:04:36 +02:00
parent c2b447610c
commit 4b6a4f530e
10 changed files with 540 additions and 0 deletions

View file

@ -0,0 +1,26 @@
# Architektur- und Migrationsentscheidungen
Stand: 27. September 2026 (M0).
## Bestand (geprüft)
- **Werkzeuge:** Godot 4.7.2 (Fedora-Build; offizielle 4.7.2-Exportvorlagen unter `assets-source/phase6/export-templates`), Blender 5.2.2 LTS, Forward+ mit OpenGL-Kompatibilitätsmodus. Kein Engine-Update.
- **Repository:** Es gab kein Git. Angelegt wurde ein lokales Repository mit dem Baseline-Commit `c2b4476` auf `main` (unveränderter Stand 0.15.0, Phase 20A); die Arbeit läuft auf `luneraria/m1`. Screenshots und die Projekt-Doku außerhalb des Projektordners sind in `../backups/vor-luneraria-2026-09-27-docs-und-screenshots.tar.gz` gesichert. Keine uncommitteten fremden Änderungen; `../.mimocode` gehört zu einem fremden Werkzeug und bleibt unberührt. Ein Stand 20B existiert nicht. Keine CLAUDE.md.
- **Spiel:** eine Szene `scenes/main.tscn`, sieben Autoloads (`AssetLibrary`, `DataCatalog`, `GameState`, `EconomyManager`, `WaveManager`, `Preferences`, `Soundscape`), alle Oberflächen im Code. Kampf in `scripts/combat/`: Verteidiger gegen Angreifer je Lane, die Feindschaft hängt an `is_attacker`, die Lane an `team_id`.
- **Baseline-Tests** (`logs/m0-baseline/`): 209 · 33 · 463 · 206 · 1.665 bestanden.
## Entscheidungen
1. **Neuer Modus neben dem Legacy-Modus.** Die Saumwacht-Regeln (zwei Bretter, 8/20 Wellen, Kern-HP-Entscheid, Arbeiter/Funken) widersprechen den neuen Produktregeln. Sie werden nicht umgeschrieben, sondern als **Legacy-Modus** archiviert: `scenes/main.tscn` und die zugehörigen Tests bleiben lauffähig. Die neue Hauptszene `scenes/luneraria.tscn` startet Champions of Luneraria. Die bisherigen QA-Schalter (`--smoke-test`, `--faction-test`, `--expedition-test`) leitet sie in die Legacy-Szene um. So vermischen sich die Wirtschaftsmodelle nicht (Arbeitsannahme E), und die technischen Regressionen bleiben erhalten.
2. **Neue autoritative Simulation** in `scripts/luneraria/core/` als reine Datenklassen (`RefCounted`) ohne Szenenbaum. Der alte Kampf ist an zwei Bretter und `is_attacker` gebunden. Übernommen werden Einheitenwerte (`DataCatalog`), Schadensmatrix, Fähigkeitsregeln (auf Teamfeindschaft umgeschrieben), Modelle und die Animationsanbindung (`SkeletalFigure.update_combat`, `AnimatedFigure.pose`).
- `LuneBalance` (Resource) enthält alle Zahlen.
- `LuneMap` enthält Lane-Polylinien, 16 Türme und 2 Kerne, Aufmarschflächen, Sperrlogik und die Grotte. Die Karte entsteht per Konstruktion spiegelsymmetrisch zur Diagonale x = z: Team 1 ist das Spiegelbild von Team 0.
- `LuneCombat` rechnet einen festen Schritt in zwei Phasen, die Ergebnisse sind reihenfolgeunabhängig.
- `LuneMatch` verwaltet Teams, Spieler, Gold, Besitz mit eindeutigen IDs, Befehle, Runden, Abrechnung, Sieg und Ereignisprotokoll.
- `LuneWaves` erzeugt datengetriebene, unbegrenzte Wellen mit Mengengrenze.
- `LuneBot` nutzt dieselbe Befehlsschnittstelle wie der Mensch.
3. **Autoritative Matchinstanz von Anfang an.** Oberfläche und Bots senden nur Befehle (`LuneMatch.submit`), die beim Phasenwechsel atomar geprüft werden. Darstellung und Tests lesen denselben Zustand. Zufall kommt nur aus einer `RandomNumberGenerator`-Instanz mit Seed (Wellenrotation, Bots). Das ist die Grundlage für M4 (Host/Join), ohne Bit-Identität über Plattformen zu behaupten.
4. **Darstellung** in `scripts/luneraria/view/`: Kartenaufbau aus Bestandsmaterialien (Kluft-Shader, Kerne, Felsen, Bäume), neu erzeugte Türme (Blender-Generator), eine Einheitenansicht pro Kampfinstanz mit Positionsinterpolation zwischen den Simulationsschritten. Sichtbarkeit und Kamera beeinflussen die Simulation nicht.
5. **Oberfläche** in `scripts/luneraria/ui/` mit dem gemeinsamen `SaumwachtTheme` und seinen Bauteilen. Titel und Menü werden auf Champions of Luneraria umgestellt. Die Eingabeabschirmung aus 20A bleibt.
6. **Benutzereinstellungen:** Der Projektname wird zu „Champions of Luneraria“, dadurch ändert sich der `user://`-Pfad. `Preferences` übernimmt beim ersten Start einmalig die alte `preferences.cfg` aus „Saumwacht · Glutpakt“ und protokolliert das. Fehlt sie, gelten die Standardwerte.
7. **Tests:** Neue Tests liegen unter `tests/luneraria/` und `tools/luneraria/`, die Legacy-Tests bleiben unverändert und werden als „Legacy“ geführt. Neue Referenzen werden erst nach fachlicher Prüfung festgeschrieben.

View file

@ -0,0 +1,71 @@
# Asset-Wiederverwendungsmatrix
Erzeugt mit `python3 tools/luneraria/build_asset_matrix.py` aus den Laufzeitmanifesten. „Volk/Bund“ ist die Datenzuordnung. Die sieben Zielvölker (Menschen, Elfen, Zwerge, Trolle, Feen, Orks, Biestmenschen) haben noch **keine eigenen Modelle**; bestehende Wesen bleiben als eigene Bünde Teil der Welt. Als „vorläufig“ markierte Rollen sind Zuordnungen vorhandener Modelle, keine neu erstellten Völker.
| ID | Name | Pfad | Quelle | Rig / Clips | Dreiecke | bisherige Rolle | neue Rolle | Volk / Bund | Materialzustand | Eingriff |
| --- | --- | --- | --- | --- | ---: | --- | --- | --- | --- | --- |
| core_team0 | Saumkern · Team 0 | res://assets/models/core_team0.glb | assets-source/phase4/blend/core_team0.blend | starre Gelenkgruppen | 36652 | Saumkern Team 0 | Ancient Blau (SW) | — | HD, starre Teile | keiner |
| core_team1 | Saumkern · Team 1 | res://assets/models/core_team1.glb | assets-source/phase4/blend/core_team1.blend | starre Gelenkgruppen | 36848 | Saumkern Team 1 | Ancient Rot (NO) | — | HD, starre Teile | keiner |
| e01 | Splitterling | res://assets/models/enemy_e01.glb | assets-source/phase4/blend/e01.blend | starre Gelenkgruppen | 19228 | Wellengegner | Monsterwelle (teamgebunden, rotierend) | Riftbrut (neutral, pro Team eingefärbt) | HD, starre Teile | Teamkennzeichnung |
| e02 | Schalenläufer | res://assets/models/enemy_e02.glb | assets-source/phase4/blend/e02.blend | starre Gelenkgruppen | 20180 | Wellengegner | Monsterwelle | Riftbrut | HD, starre Teile | Teamkennzeichnung |
| e03 | Nebelrest | res://assets/models/enemy_e03.glb | assets-source/phase4/blend/e03.blend | starre Gelenkgruppen | 35898 | Wellengegner | Monsterwelle | Riftbrut | HD, starre Teile | Teamkennzeichnung |
| e04 | Fadenschütze | res://assets/models/enemy_e04.glb | assets-source/phase4/blend/e04.blend | starre Gelenkgruppen | 51050 | Wellengegner | Monsterwelle | Riftbrut | HD, starre Teile | Teamkennzeichnung |
| e05 | Bruchstampfer | res://assets/models/enemy_e05.glb | assets-source/phase4/blend/e05.blend | starre Gelenkgruppen | 62798 | Wellengegner | Monsterwelle (späte Variante) | Riftbrut | HD, starre Teile | Teamkennzeichnung |
| e06 | Knotenrufer | res://assets/models/enemy_e06.glb | assets-source/phase4/blend/e06.blend | starre Gelenkgruppen | 48336 | Wellengegner | Monsterwelle (Unterstützer) | Riftbrut | HD, starre Teile | Teamkennzeichnung |
| e07 | Bruchhirsch | res://assets/models/enemy_e07.glb | assets-source/phase4/blend/e07.blend | starre Gelenkgruppen | 33800 | Boss Welle 5 | Reserve (Wellen-Elite ab höheren Indizes) | Riftbrut | HD, starre Teile | offen |
| e08 | Riegelkoloss | res://assets/models/enemy_e08.glb | assets-source/phase4/blend/e08.blend | starre Gelenkgruppen | 81240 | Boss Welle 8 | Reserve (Wellen-Elite) | Riftbrut | HD, starre Teile | offen |
| e09 | Maskenrichter | res://assets/models/enemy_e09.glb | assets-source/phase4/blend/e09.blend | starre Gelenkgruppen | 57310 | Boss Welle 15 | Reserve | Riftbrut | HD, starre Teile | offen |
| e10 | Schlundkrone | res://assets/models/enemy_e10.glb | assets-source/phase4/blend/e10.blend | starre Gelenkgruppen | 64694 | Boss Welle 20 | M2: neutraler Grottenboss und entsandter Boss | Riftbrut / Grotte | HD, starre Teile | Bossdarstellung, Skalierung |
| g01 | Kohlenbeißer | res://assets/models/unit_g01.glb | assets-source/phase4/blend/g01.blend | starre Gelenkgruppen | 79780 | Glutpakt Nahkampf | Truppe Nahkampf | Glutkonstrukt / Glutpakt | HD, starre Teile | keiner |
| g02 | Schlackenklaue | res://assets/models/unit_g02.glb | assets-source/phase4/blend/g02.blend | starre Gelenkgruppen | 75624 | Glutpakt Nahkampf+ | Truppenaufwertung | Glutkonstrukt / Glutpakt | HD, starre Teile | keiner |
| g03 | Schlackenwacht | res://assets/models/unit_g03.glb | assets-source/phase4/blend/g03.blend | starre Gelenkgruppen | 67232 | Glutpakt Tank | Truppe Front | Glutkonstrukt / Glutpakt | HD, starre Teile | keiner |
| g04 | Basaltbastion | res://assets/characters/basaltbastion.glb | assets-source/phase15/basaltbastion.blend | 17 Knochen · 9 Clips | 17824 | Glutpakt Tank+ | Truppenaufwertung | Glutkonstrukt / Glutpakt | gewichtetes Skelett (phase15) | keiner |
| g05 | Funkenlanze | res://assets/characters/funkenlanze.glb | assets-source/phase18/funkenlanze.blend | 26 Knochen · 9 Clips | 24036 | Glutpakt Fernkampf | Truppe Fernkampf; M2 zusätzlich Held „Funkenlanze“ (vorläufig, mit Heldenmarkierung) | Glutkonstrukt / Glutpakt | gewichtetes Skelett (phase18) | Heldenkennzeichnung in der Darstellung |
| g06 | Glasballiste | res://assets/characters/glasballiste.glb | assets-source/phase19/glasballiste.blend | 20 Knochen · 9 Clips | 15784 | Glutpakt Fernkampf+ | Truppenaufwertung (Belagerung) | Glutkonstrukt / Glutpakt | gewichtetes Skelett (phase19) | keiner |
| g07 | Aschenwerfer | res://assets/models/unit_g07.glb | assets-source/phase4/blend/g07.blend | starre Gelenkgruppen | 81492 | Glutpakt Fläche | Truppe Fläche | Glutkonstrukt / Glutpakt | HD, starre Teile | keiner |
| g08 | Kraterrufer | res://assets/models/unit_g08.glb | assets-source/phase4/blend/g08.blend | starre Gelenkgruppen | 77130 | Glutpakt Fläche+ | Truppenaufwertung | Glutkonstrukt / Glutpakt | HD, starre Teile | keiner |
| g09 | Herdhüter | res://assets/models/unit_g09.glb | assets-source/phase4/blend/g09.blend | starre Gelenkgruppen | 84568 | Glutpakt Unterstützung | Truppe Unterstützung | Glutkonstrukt / Glutpakt | HD, starre Teile | keiner |
| g10 | Sonnenesse | res://assets/models/unit_g10.glb | assets-source/phase4/blend/g10.blend | starre Gelenkgruppen | 96706 | Glutpakt Unterstützung+ | Truppenaufwertung | Glutkonstrukt / Glutpakt | HD, starre Teile | keiner |
| m01 | Kesselwicht | res://assets/models/unit_m01.glb | assets-source/phase4/blend/m01.blend | starre Gelenkgruppen | 1284 | nicht spielbar, niedrige Detailstufe | Reserve für spätere Völker | Nietenkonvent (nicht spielbar) | niedrige Detailstufe | Neuaufbau nötig vor Nutzung |
| m02 | Druckhüter | res://assets/models/unit_m02.glb | assets-source/phase4/blend/m02.blend | starre Gelenkgruppen | 1360 | nicht spielbar, niedrige Detailstufe | Reserve für spätere Völker | Nietenkonvent (nicht spielbar) | niedrige Detailstufe | Neuaufbau nötig vor Nutzung |
| m03 | Scherschreiter | res://assets/models/unit_m03.glb | assets-source/phase4/blend/m03.blend | starre Gelenkgruppen | 932 | nicht spielbar, niedrige Detailstufe | Reserve für spätere Völker | Nietenkonvent (nicht spielbar) | niedrige Detailstufe | Neuaufbau nötig vor Nutzung |
| m04 | Sechsklingler | res://assets/models/unit_m04.glb | assets-source/phase4/blend/m04.blend | starre Gelenkgruppen | 1398 | nicht spielbar, niedrige Detailstufe | Reserve für spätere Völker | Nietenkonvent (nicht spielbar) | niedrige Detailstufe | Neuaufbau nötig vor Nutzung |
| m05 | Bolzenknecht | res://assets/models/unit_m05.glb | assets-source/phase4/blend/m05.blend | starre Gelenkgruppen | 1148 | nicht spielbar, niedrige Detailstufe | Reserve für spätere Völker | Nietenkonvent (nicht spielbar) | niedrige Detailstufe | Neuaufbau nötig vor Nutzung |
| m06 | Linienmeister | res://assets/models/unit_m06.glb | assets-source/phase4/blend/m06.blend | starre Gelenkgruppen | 1432 | nicht spielbar, niedrige Detailstufe | Reserve für spätere Völker | Nietenkonvent (nicht spielbar) | niedrige Detailstufe | Neuaufbau nötig vor Nutzung |
| m07 | Klangkolben | res://assets/models/unit_m07.glb | assets-source/phase4/blend/m07.blend | starre Gelenkgruppen | 740 | nicht spielbar, niedrige Detailstufe | Reserve für spätere Völker | Nietenkonvent (nicht spielbar) | niedrige Detailstufe | Neuaufbau nötig vor Nutzung |
| m08 | Resonanzramme | res://assets/models/unit_m08.glb | assets-source/phase4/blend/m08.blend | starre Gelenkgruppen | 1316 | nicht spielbar, niedrige Detailstufe | Reserve für spätere Völker | Nietenkonvent (nicht spielbar) | niedrige Detailstufe | Neuaufbau nötig vor Nutzung |
| m09 | Taktspindel | res://assets/models/unit_m09.glb | assets-source/phase4/blend/m09.blend | starre Gelenkgruppen | 1160 | nicht spielbar, niedrige Detailstufe | Reserve für spätere Völker | Nietenkonvent (nicht spielbar) | niedrige Detailstufe | Neuaufbau nötig vor Nutzung |
| m10 | Taktprälat | res://assets/models/unit_m10.glb | assets-source/phase4/blend/m10.blend | starre Gelenkgruppen | 946 | nicht spielbar, niedrige Detailstufe | Reserve für spätere Völker | Nietenkonvent (nicht spielbar) | niedrige Detailstufe | Neuaufbau nötig vor Nutzung |
| map_lane_segment | Lane-Segment | res://assets/models/map_lane_segment.glb | assets-source/phase4/blend/map_lane_segment.blend | starre Gelenkgruppen | 10700 | Lane-Kachel alt | nicht verwendet; Lanes neu erzeugt | — | HD, starre Teile | keiner |
| n01 | Saummaske | res://assets/models/unit_n01.glb | assets-source/phase4/blend/n01.blend | starre Gelenkgruppen | 1132 | nicht spielbar, niedrige Detailstufe | Reserve für spätere Völker | Nebelchor (nicht spielbar) | niedrige Detailstufe | Neuaufbau nötig vor Nutzung |
| n02 | Grenzmaske | res://assets/models/unit_n02.glb | assets-source/phase4/blend/n02.blend | starre Gelenkgruppen | 1156 | nicht spielbar, niedrige Detailstufe | Reserve für spätere Völker | Nebelchor (nicht spielbar) | niedrige Detailstufe | Neuaufbau nötig vor Nutzung |
| n03 | Fadentänzer | res://assets/models/unit_n03.glb | assets-source/phase4/blend/n03.blend | starre Gelenkgruppen | 934 | nicht spielbar, niedrige Detailstufe | Reserve für spätere Völker | Nebelchor (nicht spielbar) | niedrige Detailstufe | Neuaufbau nötig vor Nutzung |
| n04 | Saumzertrenner | res://assets/models/unit_n04.glb | assets-source/phase4/blend/n04.blend | starre Gelenkgruppen | 1360 | nicht spielbar, niedrige Detailstufe | Reserve für spätere Völker | Nebelchor (nicht spielbar) | niedrige Detailstufe | Neuaufbau nötig vor Nutzung |
| n05 | Fernflüsterer | res://assets/models/unit_n05.glb | assets-source/phase4/blend/n05.blend | starre Gelenkgruppen | 1118 | nicht spielbar, niedrige Detailstufe | Reserve für spätere Völker | Nebelchor (nicht spielbar) | niedrige Detailstufe | Neuaufbau nötig vor Nutzung |
| n06 | Fernrufer | res://assets/models/unit_n06.glb | assets-source/phase4/blend/n06.blend | starre Gelenkgruppen | 1498 | nicht spielbar, niedrige Detailstufe | Reserve für spätere Völker | Nebelchor (nicht spielbar) | niedrige Detailstufe | Neuaufbau nötig vor Nutzung |
| n07 | Glockenschlepper | res://assets/models/unit_n07.glb | assets-source/phase4/blend/n07.blend | starre Gelenkgruppen | 1274 | nicht spielbar, niedrige Detailstufe | Reserve für spätere Völker | Nebelchor (nicht spielbar) | niedrige Detailstufe | Neuaufbau nötig vor Nutzung |
| n08 | Hohlgeläut | res://assets/models/unit_n08.glb | assets-source/phase4/blend/n08.blend | starre Gelenkgruppen | 1482 | nicht spielbar, niedrige Detailstufe | Reserve für spätere Völker | Nebelchor (nicht spielbar) | niedrige Detailstufe | Neuaufbau nötig vor Nutzung |
| n09 | Maskenkantor | res://assets/models/unit_n09.glb | assets-source/phase4/blend/n09.blend | starre Gelenkgruppen | 1314 | nicht spielbar, niedrige Detailstufe | Reserve für spätere Völker | Nebelchor (nicht spielbar) | niedrige Detailstufe | Neuaufbau nötig vor Nutzung |
| n10 | Vielstimmenkantor | res://assets/models/unit_n10.glb | assets-source/phase4/blend/n10.blend | starre Gelenkgruppen | 1734 | nicht spielbar, niedrige Detailstufe | Reserve für spätere Völker | Nebelchor (nicht spielbar) | niedrige Detailstufe | Neuaufbau nötig vor Nutzung |
| s01 | Nadelspäher | res://assets/models/unit_s01.glb | assets-source/phase4/blend/s01.blend | starre Gelenkgruppen | 10340 | Söldner | Reserve Angriffschampion | Söldner | HD, starre Teile | offen |
| s02 | Rostschieber | res://assets/models/unit_s02.glb | assets-source/phase4/blend/s02.blend | starre Gelenkgruppen | 12040 | Söldner | M2: Angriffschampion Schutz | Söldner | HD, starre Teile | keiner |
| s03 | Dunstsänger | res://assets/models/unit_s03.glb | assets-source/phase4/blend/s03.blend | starre Gelenkgruppen | 7832 | Söldner | M2: Angriffschampion Druck auf Unterstützer | Söldner | HD, starre Teile | keiner |
| s04 | Keilbrecher | res://assets/models/unit_s04.glb | assets-source/phase4/blend/s04.blend | starre Gelenkgruppen | 10458 | Söldner | M2: Angriffschampion Belagerung | Söldner | HD, starre Teile | keiner |
| s05 | Risszehrer | res://assets/models/unit_s05.glb | assets-source/phase4/blend/s05.blend | starre Gelenkgruppen | 10440 | Söldner | Reserve Angriffschampion | Söldner | HD, starre Teile | offen |
| w01 | Dornhopser | res://assets/models/unit_w01.glb | assets-source/phase4/blend/w01.blend | starre Gelenkgruppen | 61376 | Wurzelbund Nahkampf | Truppe Nahkampf | Wurzelwesen / Wurzelbund | HD, starre Teile | keiner |
| w02 | Rankenschnapper | res://assets/models/unit_w02.glb | assets-source/phase4/blend/w02.blend | starre Gelenkgruppen | 78020 | Wurzelbund Nahkampf+ | Truppenaufwertung | Wurzelwesen / Wurzelbund | HD, starre Teile | keiner |
| w03 | Korkrücken | res://assets/models/unit_w03.glb | assets-source/phase4/blend/w03.blend | starre Gelenkgruppen | 84786 | Wurzelbund Tank | Truppe Front | Wurzelwesen / Wurzelbund | HD, starre Teile | keiner |
| w04 | Ringborke | res://assets/characters/ringborke.glb | assets-source/phase11/ringborke.blend | 27 Knochen · 9 Clips | 118064 | Wurzelbund Tank+ | Truppenaufwertung; M2 zusätzlich Held „Ringborke“ (vorläufig) | Wurzelwesen / Wurzelbund | gewichtetes Skelett (phase11) | Heldenkennzeichnung |
| w05 | Samenschleuder | res://assets/models/unit_w05.glb | assets-source/phase4/blend/w05.blend | starre Gelenkgruppen | 50536 | Wurzelbund Fernkampf | Truppe Fernkampf | Wurzelwesen / Wurzelbund | HD, starre Teile | keiner |
| w06 | Zapfenkrone | res://assets/models/unit_w06.glb | assets-source/phase4/blend/w06.blend | starre Gelenkgruppen | 62588 | Wurzelbund Fernkampf+ | Truppenaufwertung | Wurzelwesen / Wurzelbund | HD, starre Teile | keiner |
| w07 | Sporenkelch | res://assets/models/unit_w07.glb | assets-source/phase4/blend/w07.blend | starre Gelenkgruppen | 69164 | Wurzelbund Fläche | Truppe Fläche | Wurzelwesen / Wurzelbund | HD, starre Teile | keiner |
| w08 | Pilzkathedrale | res://assets/models/unit_w08.glb | assets-source/phase4/blend/w08.blend | starre Gelenkgruppen | 103748 | Wurzelbund Fläche+ | Truppenaufwertung | Wurzelwesen / Wurzelbund | HD, starre Teile | keiner |
| w09 | Tauhüter | res://assets/models/unit_w09.glb | assets-source/phase4/blend/w09.blend | starre Gelenkgruppen | 52040 | Wurzelbund Heiler | Truppe Unterstützung | Wurzelwesen / Wurzelbund | HD, starre Teile | keiner |
| w10 | Quellgeflecht | res://assets/characters/quellgeflecht.glb | assets-source/phase16/quellgeflecht.blend | 17 Knochen · 9 Clips | 14276 | Wurzelbund Heiler+ | Truppenaufwertung; M2 zusätzlich Held „Quellgeflecht“ (vorläufig) | Wurzelwesen / Wurzelbund | gewichtetes Skelett (phase16) | Heldenkennzeichnung |
| worker | Funkenleser | res://assets/characters/arbeiter.glb | assets-source/phase12/arbeiter.blend | 27 Knochen · 10 Clips | 7184 | Arbeiter/Funken | nicht im neuen Modus (Arbeitsannahme E); Modell bleibt erhalten | — | gewichtetes Skelett (phase12) | keiner |
| menu-stage | Menü-Diorama | res://assets/environment/menu-stage.glb | assets-source/phase20/build_menu_stage.py | statisch | 48660 | Hauptmenü Phase 20A | Hauptmenü (Titel angepasst) | — | Kluft-Shader | keiner |
| verdant-rift | Grüne Kluft (2-Bretter-Arena) | res://assets/environment/verdant-rift.glb | assets-source/phase10/verdant-rift.blend | statisch | — | Spielfeld Saumwacht | Legacy-Modus; Materialien/Shader für die neue Karte wiederverwendet | — | triplanare Kluft-Shader | keine neue Nutzung als Karte |
| compare-rock / compare-tree | Fels, Baum | res://assets/compare/ | assets-source/phase14/build_compare_props.py | statisch | 360 / 852 | Vergleichsszene | Kartendekoration Dschungel | — | einfach | keiner |
| Türme | — | (fehlt) | — | — | — | — | 16 Lanetürme/Basistürme | — | — | neu: Blender-Generator (M1) |
| Bossgrotte | — | (fehlt) | — | — | — | — | Grottenbau nahe Zentrum | — | — | neu (M2) |
| Emblem/Theme | Saumwacht-Theme | scripts/ui/theme/, assets/ui/ | Code / SVG | — | — | Menü 20A | Theme weiterverwendet, Titel/Emblem Luneraria | — | Vektor | Titelschrift/Emblem ergänzen |

View file

@ -0,0 +1,56 @@
# Champions of Luneraria · Regelwerk
Stand: 27. September 2026. Drei Ebenen, getrennt gehalten:
1. **Verbindliche Produktregeln** aus dem Masterauftrag. Änderungen nur per ausdrücklicher Entscheidung des Auftraggebers.
2. **Arbeitsannahmen A–F** aus dem Masterauftrag, als anpassbare Designentscheidungen formuliert. Es sind keine Nutzerzitate.
3. **Eigene Detailentscheidungen** dieser Umsetzung, anpassbar und mit Konfigurationsort.
## 1 · Verbindliche Produktregeln
- Titel: **Champions of Luneraria**.
- Zwei Teams mit je drei eigenständigen Spielerrollen.
- Drei zusammenhängende Lanes: Top, Mid, Bottom.
- Kämpfe laufen automatisch. Es gibt weder direkte Heldensteuerung noch manuelles Last-Hitting.
- Die Partie endet ausschließlich durch Zerstörung eines Ancient-Kerns. Es gibt keine maximale Rundenzahl, keinen Sieg nach einer Wellengrenze, keine Entscheidung nach Kern-HP und keinen Sieger per Zeitlimit.
- Werden beide Kerne im selben Simulationsschritt zerstört, ist das ein Unentschieden, unabhängig von Array-, Render- oder Netzwerkreihenfolge. Ein manueller Testabbruch oder technischer Abbruch ist kein Kern-Sieg.
- Monsterwellen werden mit jeder Welle stärker.
- Je Team und Lane zwei Lanetürme; zusätzliche Türme schützen den Ancient.
- Normale Truppen werden dauerhaft gekauft und aufgewertet; sie haben kein Levelsystem und keine Items.
- Helden werden gekauft, sammeln in der Partie Erfahrung und entwickeln sich über Items und Talente.
- Angriffschampions werden mit Gold als einmalige Verstärkung einer Angriffswelle gekauft.
- Besiegte Monster und Angriffschampions geben Gold.
- Eine gemeinsame Bossgrotte bindet Einheiten, die dafür ihre Lane verlassen.
- Nach erfolgreicher Eroberung verstärkt der Boss einmalig den Angriff des **siegreichen** Teams in der folgenden Runde, gegen das **gegnerische** Team. Er läuft zum gegnerischen Ancient.
- Der entsandte Boss gibt dem verteidigenden Team bei seinem Tod deutlich mehr Gold als ein normaler Gegner.
- Nach einem Bosssieg ist die Grotte drei vollständige Runden gesperrt. Beispiel: Sieg in Runde 6, Einsatz in Runde 7, Sperre 7–9, wieder bekämpfbar ab 10.
- Vorhandene Assets werden bevorzugt erhalten. Inhaltsziel: Elfen, Trolle, Zwerge, Menschen, Feen, Orks, Biestmenschen; bestehende Wesen und Bünde bleiben Teil der Welt.
- Ziel ist taktische Wiederspielbarkeit.
## 2 · Arbeitsannahmen (Masterauftrag, anpassbar)
- **A** Dauerhafte Truppen und Helden rücken mit den eigenen Monsterwellen über ihre Lane vor und greifen danach zulässige gegnerische Gebäude an.
- **B** Jeder Spieler hat eigenes Gold und eine eigene Armee. Seine Lane ist die anfängliche Zuständigkeit; Unterstützung anderer Lanes ist möglich. Das Team ist unabhängig von Volk und Bund.
- **C** Ein aktiver Held pro Spieler mit drei Itemplätzen; mehrere Helden zur Wahl. Normale Truppen bleiben relevant.
- **D** Genau zwei gemeinsame Basistürme pro Team: acht Türme je Team, sechzehn auf der Karte, plus zwei Ancients.
- **E** Die alte Arbeiter- und Funkenökonomie läuft im neuen Modus nicht. Gold finanziert Armee, Aufwertungen, Helden, Items und Angriffschampions.
- **F** Erster Modus: echte Sechs-Spieler-Simulation mit einem Menschen und fünf Bots. Netzwerk folgt als eigener Meilenstein.
## 3 · Detailentscheidungen dieser Umsetzung
Alle Zahlen stehen zentral in `data/luneraria/balance_default.tres` (Klasse `LuneBalance`).
| Thema | Entscheidung |
| --- | --- |
| Runde | Planung → Befehle werden atomar übernommen → automatischer Kampf → Abrechnung → nächste Planung. Die Wellennummer steigt unbegrenzt. |
| Simulation | Feste Schrittweite 1/20 s. Jeder Schritt läuft in zwei Phasen: erst werden alle Treffer, Heilungen und Effekte aus dem Zustand zu Schrittbeginn berechnet, dann gleichzeitig angewendet. Todesfälle eines Schritts gelten gemeinsam. So entsteht kein Vorteil aus der Verarbeitungsreihenfolge. |
| Aufmarsch | Armeen und Wellen starten jede Runde an festen, symmetrischen Aufmarschflächen ihrer Lane. Formationsplätze sind Offsets vor/quer zur Laufrichtung. Keine Front-Teleports. |
| Vormarsch | Ohne Ziel folgen Einheiten ihrer Lane. Sie kommen nicht an einem lebenden gegnerischen Turm ihrer Lane vorbei. Am Laneende greifen sie das nächste zulässige Basisgebäude an. |
| Gebäudesperren | Der innere Turm ist erst nach dem äußeren verwundbar. Die Basistürme sind verwundbar, sobald eine Lane des Teams ganz durchbrochen ist. Der Ancient ist erst verwundbar, wenn beide Basistürme gefallen sind. Die Sperre gilt für jeden Schaden (Fernkampf, Fläche, Fähigkeiten, Boss). Gesperrte Gebäude schießen weiter. |
| Kampfende | Eine Kampfphase endet, wenn keine beweglichen Einheiten mehr leben, wenn ein Kern fällt oder wenn das Zeitfenster abläuft (Standard 180 s). Das Zeitfenster beendet nur die Kampfphase, nie die Partie. Übrige temporäre Einheiten verschwinden ohne Belohnung, dauerhafte kehren zurück. Es gibt keinen Kernschaden durch den Abbruch. |
| Rückkehr | Dauerhafte Truppen und Helden kehren nach jeder Kampfphase vollständig geheilt zurück, auch wenn sie im Kampf gefallen sind. Schilde, Brand, Verlangsamungen, Stärkungen und Abklingzeiten werden auf Startwerte zurückgesetzt. Aufwertungen, Erfahrung, Items und Talente bleiben. |
| Gebäude | Türme und Kerne behalten Schaden; zerstörte Gebäude kehren nicht zurück. Gebäude skalieren nicht mit der Welle. |
| Belohnung | Pro gegnerischem Todesereignis genau eine Belohnung, egal wer tötet. Lane-Killgold geht zur Hälfte an den zuständigen Lane-Spieler des tötenden Teams und zur Hälfte gleichmäßig an alle drei. Der Rest der Ganzzahlteilung geht in stabiler Slotreihenfolge. Gebäude- und Boss-Kopfgelder werden gleichmäßig im Team verteilt. Dauerhafte Truppen und Helden geben kein Kopfgeld. |
| Laneverlust | Kein Spieler scheidet aus. Er baut weiter auf, verteidigt und unterstützt. |
| Pause | Offline darf die Planung auf den Menschen warten; Pause und Tempo 1×/2×/4× gelten nur lokal. |
| QA-Grenze | Testläufe dürfen eine Rundenobergrenze haben. Eine dort nicht beendete Partie wird als „Testlimit erreicht / Partie nicht abgeschlossen“ protokolliert, nie als Sieg oder Unentschieden. |

19
docs/luneraria/STATUS.md Normal file
View file

@ -0,0 +1,19 @@
# STATUS · Champions of Luneraria
Stand: 27. September 2026.
| Meilenstein | Zustand |
| --- | --- |
| M0 · Migration vorbereiten | **abgeschlossen**: Bestand, Sicherung (Git-Baseline `c2b4476` + Archiv), Regelwerk, Architektur, Assetmatrix, Baseline-Tests grün |
| M1 · Spielbarer Drei-Lane-Kern | in Arbeit |
| M2 · Mechanikdurchlauf | offen |
| M3 · Qualität und Balance | offen |
| M4 · Netzwerk und Pakete | offen |
## Blockiert
- Nativer Windows-11-Test: keine Windows-Hardware in dieser Umgebung.
## Nächster Schritt
M1: `LuneBalance`, `LuneMap`, `LuneCombat`, `LuneMatch` und Bots, dann Kartendarstellung, HUD und ein reproduzierbar simulierter Sieg.

View file

@ -0,0 +1,4 @@
Godot Engine v4.7.2.stable.fedora.ed1daf0bf - https://godotengine.org
ABILITY_EVENT_PASS: 33 checks; 0 failures
EXIT 0

View file

@ -0,0 +1,250 @@
Godot Engine v4.7.2.stable.fedora.ed1daf0bf - https://godotengine.org
ERROR: Attempting to initialize the wrong RID
at: get_or_null (./core/templates/rid_owner.h:240)
GDScript backtrace (most recent call first):
[0] instantiate_fighter (res://scripts/presentation/asset_library.gd:110)
[1] _place_core (res://scripts/presentation/menu_stage.gd:172)
[2] _ready (res://scripts/presentation/menu_stage.gd:61)
[3] _build_stage (res://scripts/ui/main_menu.gd:90)
[4] _ready (res://scripts/ui/main_menu.gd:62)
[5] _make_overlay (res://scripts/ui/hud.gd:492)
[6] _ready (res://scripts/ui/hud.gd:112)
ERROR: Parameter "mem" is null.
at: initialize_rid (./core/templates/rid_owner.h:284)
GDScript backtrace (most recent call first):
[0] instantiate_fighter (res://scripts/presentation/asset_library.gd:110)
[1] _place_core (res://scripts/presentation/menu_stage.gd:172)
[2] _ready (res://scripts/presentation/menu_stage.gd:61)
[3] _build_stage (res://scripts/ui/main_menu.gd:90)
[4] _ready (res://scripts/ui/main_menu.gd:62)
[5] _make_overlay (res://scripts/ui/hud.gd:492)
[6] _ready (res://scripts/ui/hud.gd:112)
ERROR: Attempting to use an uninitialized RID
at: get_or_null (./core/templates/rid_owner.h:247)
GDScript backtrace (most recent call first):
[0] instantiate_fighter (res://scripts/presentation/asset_library.gd:110)
[1] _place_core (res://scripts/presentation/menu_stage.gd:172)
[2] _ready (res://scripts/presentation/menu_stage.gd:61)
[3] _build_stage (res://scripts/ui/main_menu.gd:90)
[4] _ready (res://scripts/ui/main_menu.gd:62)
[5] _make_overlay (res://scripts/ui/hud.gd:492)
[6] _ready (res://scripts/ui/hud.gd:112)
ERROR: Parameter "m" is null.
at: mesh_set_blend_shape_count (servers/rendering/dummy/storage/mesh_storage.h:74)
GDScript backtrace (most recent call first):
[0] instantiate_fighter (res://scripts/presentation/asset_library.gd:110)
[1] _place_core (res://scripts/presentation/menu_stage.gd:172)
[2] _ready (res://scripts/presentation/menu_stage.gd:61)
[3] _build_stage (res://scripts/ui/main_menu.gd:90)
[4] _ready (res://scripts/ui/main_menu.gd:62)
[5] _make_overlay (res://scripts/ui/hud.gd:492)
[6] _ready (res://scripts/ui/hud.gd:112)
ERROR: Attempting to use an uninitialized RID
at: get_or_null (./core/templates/rid_owner.h:247)
GDScript backtrace (most recent call first):
[0] instantiate_fighter (res://scripts/presentation/asset_library.gd:110)
[1] _place_core (res://scripts/presentation/menu_stage.gd:172)
[2] _ready (res://scripts/presentation/menu_stage.gd:61)
[3] _build_stage (res://scripts/ui/main_menu.gd:90)
[4] _ready (res://scripts/ui/main_menu.gd:62)
[5] _make_overlay (res://scripts/ui/hud.gd:492)
[6] _ready (res://scripts/ui/hud.gd:112)
ERROR: Parameter "m" is null.
at: mesh_add_surface (servers/rendering/dummy/storage/mesh_storage.h:81)
GDScript backtrace (most recent call first):
[0] instantiate_fighter (res://scripts/presentation/asset_library.gd:110)
[1] _place_core (res://scripts/presentation/menu_stage.gd:172)
[2] _ready (res://scripts/presentation/menu_stage.gd:61)
[3] _build_stage (res://scripts/ui/main_menu.gd:90)
[4] _ready (res://scripts/ui/main_menu.gd:62)
[5] _make_overlay (res://scripts/ui/hud.gd:492)
[6] _ready (res://scripts/ui/hud.gd:112)
ERROR: Attempting to use an uninitialized RID
at: get_or_null (./core/templates/rid_owner.h:247)
GDScript backtrace (most recent call first):
[0] instantiate_fighter (res://scripts/presentation/asset_library.gd:110)
[1] _place_core (res://scripts/presentation/menu_stage.gd:172)
[2] _ready (res://scripts/presentation/menu_stage.gd:61)
[3] _build_stage (res://scripts/ui/main_menu.gd:90)
[4] _ready (res://scripts/ui/main_menu.gd:62)
[5] _make_overlay (res://scripts/ui/hud.gd:492)
[6] _ready (res://scripts/ui/hud.gd:112)
ERROR: Parameter "m" is null.
at: mesh_add_surface (servers/rendering/dummy/storage/mesh_storage.h:81)
GDScript backtrace (most recent call first):
[0] instantiate_fighter (res://scripts/presentation/asset_library.gd:110)
[1] _place_core (res://scripts/presentation/menu_stage.gd:172)
[2] _ready (res://scripts/presentation/menu_stage.gd:61)
[3] _build_stage (res://scripts/ui/main_menu.gd:90)
[4] _ready (res://scripts/ui/main_menu.gd:62)
[5] _make_overlay (res://scripts/ui/hud.gd:492)
[6] _ready (res://scripts/ui/hud.gd:112)
ERROR: Attempting to use an uninitialized RID
at: get_or_null (./core/templates/rid_owner.h:247)
GDScript backtrace (most recent call first):
[0] instantiate_fighter (res://scripts/presentation/asset_library.gd:110)
[1] _place_core (res://scripts/presentation/menu_stage.gd:172)
[2] _ready (res://scripts/presentation/menu_stage.gd:61)
[3] _build_stage (res://scripts/ui/main_menu.gd:90)
[4] _ready (res://scripts/ui/main_menu.gd:62)
[5] _make_overlay (res://scripts/ui/hud.gd:492)
[6] _ready (res://scripts/ui/hud.gd:112)
ERROR: Parameter "m" is null.
at: mesh_add_surface (servers/rendering/dummy/storage/mesh_storage.h:81)
GDScript backtrace (most recent call first):
[0] instantiate_fighter (res://scripts/presentation/asset_library.gd:110)
[1] _place_core (res://scripts/presentation/menu_stage.gd:172)
[2] _ready (res://scripts/presentation/menu_stage.gd:61)
[3] _build_stage (res://scripts/ui/main_menu.gd:90)
[4] _ready (res://scripts/ui/main_menu.gd:62)
[5] _make_overlay (res://scripts/ui/hud.gd:492)
[6] _ready (res://scripts/ui/hud.gd:112)
ERROR: Attempting to use an uninitialized RID
at: get_or_null (./core/templates/rid_owner.h:247)
GDScript backtrace (most recent call first):
[0] instantiate_fighter (res://scripts/presentation/asset_library.gd:110)
[1] _place_core (res://scripts/presentation/menu_stage.gd:172)
[2] _ready (res://scripts/presentation/menu_stage.gd:61)
[3] _build_stage (res://scripts/ui/main_menu.gd:90)
[4] _ready (res://scripts/ui/main_menu.gd:62)
[5] _make_overlay (res://scripts/ui/hud.gd:492)
[6] _ready (res://scripts/ui/hud.gd:112)
ERROR: Parameter "m" is null.
at: mesh_add_surface (servers/rendering/dummy/storage/mesh_storage.h:81)
GDScript backtrace (most recent call first):
[0] instantiate_fighter (res://scripts/presentation/asset_library.gd:110)
[1] _place_core (res://scripts/presentation/menu_stage.gd:172)
[2] _ready (res://scripts/presentation/menu_stage.gd:61)
[3] _build_stage (res://scripts/ui/main_menu.gd:90)
[4] _ready (res://scripts/ui/main_menu.gd:62)
[5] _make_overlay (res://scripts/ui/hud.gd:492)
[6] _ready (res://scripts/ui/hud.gd:112)
ERROR: Attempting to use an uninitialized RID
at: get_or_null (./core/templates/rid_owner.h:247)
GDScript backtrace (most recent call first):
[0] instantiate_fighter (res://scripts/presentation/asset_library.gd:110)
[1] _place_core (res://scripts/presentation/menu_stage.gd:172)
[2] _ready (res://scripts/presentation/menu_stage.gd:61)
[3] _build_stage (res://scripts/ui/main_menu.gd:90)
[4] _ready (res://scripts/ui/main_menu.gd:62)
[5] _make_overlay (res://scripts/ui/hud.gd:492)
[6] _ready (res://scripts/ui/hud.gd:112)
ERROR: Parameter "m" is null.
at: mesh_add_surface (servers/rendering/dummy/storage/mesh_storage.h:81)
GDScript backtrace (most recent call first):
[0] instantiate_fighter (res://scripts/presentation/asset_library.gd:110)
[1] _place_core (res://scripts/presentation/menu_stage.gd:172)
[2] _ready (res://scripts/presentation/menu_stage.gd:61)
[3] _build_stage (res://scripts/ui/main_menu.gd:90)
[4] _ready (res://scripts/ui/main_menu.gd:62)
[5] _make_overlay (res://scripts/ui/hud.gd:492)
[6] _ready (res://scripts/ui/hud.gd:112)
ERROR: Attempting to use an uninitialized RID
at: get_or_null (./core/templates/rid_owner.h:247)
GDScript backtrace (most recent call first):
[0] instantiate_fighter (res://scripts/presentation/asset_library.gd:110)
[1] _place_core (res://scripts/presentation/menu_stage.gd:172)
[2] _ready (res://scripts/presentation/menu_stage.gd:61)
[3] _build_stage (res://scripts/ui/main_menu.gd:90)
[4] _ready (res://scripts/ui/main_menu.gd:62)
[5] _make_overlay (res://scripts/ui/hud.gd:492)
[6] _ready (res://scripts/ui/hud.gd:112)
ERROR: Parameter "m" is null.
at: mesh_add_surface (servers/rendering/dummy/storage/mesh_storage.h:81)
GDScript backtrace (most recent call first):
[0] instantiate_fighter (res://scripts/presentation/asset_library.gd:110)
[1] _place_core (res://scripts/presentation/menu_stage.gd:172)
[2] _ready (res://scripts/presentation/menu_stage.gd:61)
[3] _build_stage (res://scripts/ui/main_menu.gd:90)
[4] _ready (res://scripts/ui/main_menu.gd:62)
[5] _make_overlay (res://scripts/ui/hud.gd:492)
[6] _ready (res://scripts/ui/hud.gd:112)
ERROR: Attempting to use an uninitialized RID
at: get_or_null (./core/templates/rid_owner.h:247)
GDScript backtrace (most recent call first):
[0] instantiate_fighter (res://scripts/presentation/asset_library.gd:110)
[1] _place_core (res://scripts/presentation/menu_stage.gd:172)
[2] _ready (res://scripts/presentation/menu_stage.gd:61)
[3] _build_stage (res://scripts/ui/main_menu.gd:90)
[4] _ready (res://scripts/ui/main_menu.gd:62)
[5] _make_overlay (res://scripts/ui/hud.gd:492)
[6] _ready (res://scripts/ui/hud.gd:112)
ERROR: Parameter "m" is null.
at: mesh_add_surface (servers/rendering/dummy/storage/mesh_storage.h:81)
GDScript backtrace (most recent call first):
[0] instantiate_fighter (res://scripts/presentation/asset_library.gd:110)
[1] _place_core (res://scripts/presentation/menu_stage.gd:172)
[2] _ready (res://scripts/presentation/menu_stage.gd:61)
[3] _build_stage (res://scripts/ui/main_menu.gd:90)
[4] _ready (res://scripts/ui/main_menu.gd:62)
[5] _make_overlay (res://scripts/ui/hud.gd:492)
[6] _ready (res://scripts/ui/hud.gd:112)
ERROR: Attempting to use an uninitialized RID
at: get_or_null (./core/templates/rid_owner.h:247)
GDScript backtrace (most recent call first):
[0] instantiate_fighter (res://scripts/presentation/asset_library.gd:110)
[1] _place_core (res://scripts/presentation/menu_stage.gd:172)
[2] _ready (res://scripts/presentation/menu_stage.gd:61)
[3] _build_stage (res://scripts/ui/main_menu.gd:90)
[4] _ready (res://scripts/ui/main_menu.gd:62)
[5] _make_overlay (res://scripts/ui/hud.gd:492)
[6] _ready (res://scripts/ui/hud.gd:112)
ERROR: Parameter "m" is null.
at: mesh_add_surface (servers/rendering/dummy/storage/mesh_storage.h:81)
GDScript backtrace (most recent call first):
[0] instantiate_fighter (res://scripts/presentation/asset_library.gd:110)
[1] _place_core (res://scripts/presentation/menu_stage.gd:172)
[2] _ready (res://scripts/presentation/menu_stage.gd:61)
[3] _build_stage (res://scripts/ui/main_menu.gd:90)
[4] _ready (res://scripts/ui/main_menu.gd:62)
[5] _make_overlay (res://scripts/ui/hud.gd:492)
[6] _ready (res://scripts/ui/hud.gd:112)
ERROR: Attempting to use an uninitialized RID
at: get_or_null (./core/templates/rid_owner.h:247)
GDScript backtrace (most recent call first):
[0] instantiate_fighter (res://scripts/presentation/asset_library.gd:110)
[1] _place_core (res://scripts/presentation/menu_stage.gd:172)
[2] _ready (res://scripts/presentation/menu_stage.gd:61)
[3] _build_stage (res://scripts/ui/main_menu.gd:90)
[4] _ready (res://scripts/ui/main_menu.gd:62)
[5] _make_overlay (res://scripts/ui/hud.gd:492)
[6] _ready (res://scripts/ui/hud.gd:112)
ERROR: Parameter "m" is null.
at: mesh_set_blend_shape_mode (servers/rendering/dummy/storage/mesh_storage.h:110)
GDScript backtrace (most recent call first):
[0] instantiate_fighter (res://scripts/presentation/asset_library.gd:110)
[1] _place_core (res://scripts/presentation/menu_stage.gd:172)
[2] _ready (res://scripts/presentation/menu_stage.gd:61)
[3] _build_stage (res://scripts/ui/main_menu.gd:90)
[4] _ready (res://scripts/ui/main_menu.gd:62)
[5] _make_overlay (res://scripts/ui/hud.gd:492)
[6] _ready (res://scripts/ui/hud.gd:112)
ERROR: Attempting to use an uninitialized RID
at: get_or_null (./core/templates/rid_owner.h:247)
GDScript backtrace (most recent call first):
[0] instantiate_fighter (res://scripts/presentation/asset_library.gd:110)
[1] _place_core (res://scripts/presentation/menu_stage.gd:172)
[2] _ready (res://scripts/presentation/menu_stage.gd:61)
[3] _build_stage (res://scripts/ui/main_menu.gd:90)
[4] _ready (res://scripts/ui/main_menu.gd:62)
[5] _make_overlay (res://scripts/ui/hud.gd:492)
[6] _ready (res://scripts/ui/hud.gd:112)
ERROR: Parameter "m" is null.
at: mesh_set_blend_shape_mode (servers/rendering/dummy/storage/mesh_storage.h:110)
GDScript backtrace (most recent call first):
[0] instantiate_fighter (res://scripts/presentation/asset_library.gd:110)
[1] _place_core (res://scripts/presentation/menu_stage.gd:172)
[2] _ready (res://scripts/presentation/menu_stage.gd:61)
[3] _build_stage (res://scripts/ui/main_menu.gd:90)
[4] _ready (res://scripts/ui/main_menu.gd:62)
[5] _make_overlay (res://scripts/ui/hud.gd:492)
[6] _ready (res://scripts/ui/hud.gd:112)
EXPEDITION_MATCH: Standard gegen Standard; wave 20; cores [669, 669]; outcome -1
EXPEDITION_MATCH: Standard gegen Lernmodus; wave 10; cores [2038, 0]; outcome 0
EXPEDITION_MATCH: Leeres Baufeld gegen Standard; wave 7; cores [0, 3000]; outcome 1
EXPEDITION_PASS: 206 checks; 0 failures; report /home/martin/.local/share/godot/app_userdata/Saumwacht · Glutpakt/phase8-verification.json
WARNING: 6 ObjectDB instances were leaked at exit (run with `--verbose` for details).
at: cleanup (core/object/object.cpp:2536)
ERROR: 1 RID allocations of type 'N13RendererDummy9DummyMeshE' were leaked at exit.
EXIT 0

View file

@ -0,0 +1,8 @@
Godot Engine v4.7.2.stable.fedora.ed1daf0bf - https://godotengine.org
FACTION_MATCH: Wurzelbund gegen Glutpakt wave 20 cores [1244, 1709]
FACTION_MATCH: Glutpakt gegen Wurzelbund wave 20 cores [1709, 1244]
FACTION_MATCH: Wurzelbund Spiegel wave 20 cores [1244, 1244]
FACTION_MATCH: Wurzelbund Kurzpartie wave 8 cores [3000, 3000]
FACTION_PASS: 1665 checks; 0 failures; /home/martin/.local/share/godot/app_userdata/Saumwacht · Glutpakt/phase10-verification.json
EXIT 0

View file

@ -0,0 +1,5 @@
Godot Engine v4.7.2.stable.fedora.ed1daf0bf - https://godotengine.org
EXPORT_SMOKE_PASS: 463 checks; 0 failures; engine 4.7.2-stable (fedora); cores [3000, 1035]
SMOKE_CAPTURES: /home/martin/.local/share/godot/app_userdata/Saumwacht · Glutpakt/phase8-smoke
EXIT 0

View file

@ -0,0 +1,4 @@
Godot Engine v4.7.2.stable.fedora.ed1daf0bf - https://godotengine.org
PASS: 209 checks across economy, combat and the full match lifecycle.
EXIT 0

View file

@ -0,0 +1,97 @@
#!/usr/bin/env python3
"""Asset reuse matrix for Champions of Luneraria, generated from the runtime manifests.
Run: python3 tools/luneraria/build_asset_matrix.py -> docs/luneraria/ASSETMATRIX.md
Roles marked "vorläufig" are provisional assignments of existing models, not new peoples.
"""
from pathlib import Path
import json
P = Path(__file__).resolve().parents[2]
phase4 = {a['id']: a for a in json.loads((P / 'assets/phase4_manifest.json').read_text())['assets']}
skeletal = {}
for name in ['phase11', 'phase12', 'phase15', 'phase16', 'phase18', 'phase19']:
for a in json.loads((P / f'assets/{name}_manifest.json').read_text())['assets']:
skeletal[a['id']] = (a, name)
# id -> (bisherige Rolle, neue Rolle, Volk/Bund-Zuordnung, Eingriff)
ROLES = {
'g01': ('Glutpakt Nahkampf', 'Truppe Nahkampf', 'Glutkonstrukt / Glutpakt', 'keiner'),
'g02': ('Glutpakt Nahkampf+', 'Truppenaufwertung', 'Glutkonstrukt / Glutpakt', 'keiner'),
'g03': ('Glutpakt Tank', 'Truppe Front', 'Glutkonstrukt / Glutpakt', 'keiner'),
'g04': ('Glutpakt Tank+', 'Truppenaufwertung', 'Glutkonstrukt / Glutpakt', 'keiner'),
'g05': ('Glutpakt Fernkampf', 'Truppe Fernkampf; M2 zusätzlich Held „Funkenlanze“ (vorläufig, mit Heldenmarkierung)', 'Glutkonstrukt / Glutpakt', 'Heldenkennzeichnung in der Darstellung'),
'g06': ('Glutpakt Fernkampf+', 'Truppenaufwertung (Belagerung)', 'Glutkonstrukt / Glutpakt', 'keiner'),
'g07': ('Glutpakt Fläche', 'Truppe Fläche', 'Glutkonstrukt / Glutpakt', 'keiner'),
'g08': ('Glutpakt Fläche+', 'Truppenaufwertung', 'Glutkonstrukt / Glutpakt', 'keiner'),
'g09': ('Glutpakt Unterstützung', 'Truppe Unterstützung', 'Glutkonstrukt / Glutpakt', 'keiner'),
'g10': ('Glutpakt Unterstützung+', 'Truppenaufwertung', 'Glutkonstrukt / Glutpakt', 'keiner'),
'w01': ('Wurzelbund Nahkampf', 'Truppe Nahkampf', 'Wurzelwesen / Wurzelbund', 'keiner'),
'w02': ('Wurzelbund Nahkampf+', 'Truppenaufwertung', 'Wurzelwesen / Wurzelbund', 'keiner'),
'w03': ('Wurzelbund Tank', 'Truppe Front', 'Wurzelwesen / Wurzelbund', 'keiner'),
'w04': ('Wurzelbund Tank+', 'Truppenaufwertung; M2 zusätzlich Held „Ringborke“ (vorläufig)', 'Wurzelwesen / Wurzelbund', 'Heldenkennzeichnung'),
'w05': ('Wurzelbund Fernkampf', 'Truppe Fernkampf', 'Wurzelwesen / Wurzelbund', 'keiner'),
'w06': ('Wurzelbund Fernkampf+', 'Truppenaufwertung', 'Wurzelwesen / Wurzelbund', 'keiner'),
'w07': ('Wurzelbund Fläche', 'Truppe Fläche', 'Wurzelwesen / Wurzelbund', 'keiner'),
'w08': ('Wurzelbund Fläche+', 'Truppenaufwertung', 'Wurzelwesen / Wurzelbund', 'keiner'),
'w09': ('Wurzelbund Heiler', 'Truppe Unterstützung', 'Wurzelwesen / Wurzelbund', 'keiner'),
'w10': ('Wurzelbund Heiler+', 'Truppenaufwertung; M2 zusätzlich Held „Quellgeflecht“ (vorläufig)', 'Wurzelwesen / Wurzelbund', 'Heldenkennzeichnung'),
'e01': ('Wellengegner', 'Monsterwelle (teamgebunden, rotierend)', 'Riftbrut (neutral, pro Team eingefärbt)', 'Teamkennzeichnung'),
'e02': ('Wellengegner', 'Monsterwelle', 'Riftbrut', 'Teamkennzeichnung'),
'e03': ('Wellengegner', 'Monsterwelle', 'Riftbrut', 'Teamkennzeichnung'),
'e04': ('Wellengegner', 'Monsterwelle', 'Riftbrut', 'Teamkennzeichnung'),
'e05': ('Wellengegner', 'Monsterwelle (späte Variante)', 'Riftbrut', 'Teamkennzeichnung'),
'e06': ('Wellengegner', 'Monsterwelle (Unterstützer)', 'Riftbrut', 'Teamkennzeichnung'),
'e07': ('Boss Welle 5', 'Reserve (Wellen-Elite ab höheren Indizes)', 'Riftbrut', 'offen'),
'e08': ('Boss Welle 8', 'Reserve (Wellen-Elite)', 'Riftbrut', 'offen'),
'e09': ('Boss Welle 15', 'Reserve', 'Riftbrut', 'offen'),
'e10': ('Boss Welle 20', 'M2: neutraler Grottenboss und entsandter Boss', 'Riftbrut / Grotte', 'Bossdarstellung, Skalierung'),
's01': ('Söldner', 'Reserve Angriffschampion', 'Söldner', 'offen'),
's02': ('Söldner', 'M2: Angriffschampion Schutz', 'Söldner', 'keiner'),
's03': ('Söldner', 'M2: Angriffschampion Druck auf Unterstützer', 'Söldner', 'keiner'),
's04': ('Söldner', 'M2: Angriffschampion Belagerung', 'Söldner', 'keiner'),
's05': ('Söldner', 'Reserve Angriffschampion', 'Söldner', 'offen'),
'core_team0': ('Saumkern Team 0', 'Ancient Blau (SW)', '—', 'keiner'),
'core_team1': ('Saumkern Team 1', 'Ancient Rot (NO)', '—', 'keiner'),
'worker': ('Arbeiter/Funken', 'nicht im neuen Modus (Arbeitsannahme E); Modell bleibt erhalten', '—', 'keiner'),
'map_lane_segment': ('Lane-Kachel alt', 'nicht verwendet; Lanes neu erzeugt', '—', 'keiner'),
}
for prefix, people in (('m', 'Nietenkonvent (nicht spielbar)'), ('n', 'Nebelchor (nicht spielbar)')):
for i in range(1, 11):
ROLES[f'{prefix}{i:02d}'] = ('nicht spielbar, niedrige Detailstufe', 'Reserve für spätere Völker', people, 'Neuaufbau nötig vor Nutzung')
rows = []
for asset_id in sorted(set(phase4) | set(skeletal)):
base = phase4.get(asset_id, {})
path = base.get('resource_path', '')
source = str(base.get('blend_path', '')).replace(str(P) + '/', '')
tris = base.get('triangles', '')
rig = 'starre Gelenkgruppen'
material = 'HD, starre Teile' if base.get('detail_level') == 'high' else 'niedrige Detailstufe'
if asset_id in skeletal:
record, manifest = skeletal[asset_id]
path = record['resource_path']
source = record.get('source', source)
tris = record['triangles']
rig = '%d Knochen · %d Clips' % (record['bones'], len(record['clips']))
material = 'gewichtetes Skelett (%s)' % manifest
old, new, people, work = ROLES.get(asset_id, ('?', 'offen', '?', 'Prüfung nötig'))
rows.append((asset_id, base.get('name') or base.get('display_name') or skeletal.get(asset_id, ({},))[0].get('name', ''), path, source, rig, tris, old, new, people, material, work))
extra = [
('menu-stage', 'Menü-Diorama', 'res://assets/environment/menu-stage.glb', 'assets-source/phase20/build_menu_stage.py', 'statisch', '48660', 'Hauptmenü Phase 20A', 'Hauptmenü (Titel angepasst)', '—', 'Kluft-Shader', 'keiner'),
('verdant-rift', 'Grüne Kluft (2-Bretter-Arena)', 'res://assets/environment/verdant-rift.glb', 'assets-source/phase10/verdant-rift.blend', 'statisch', '—', 'Spielfeld Saumwacht', 'Legacy-Modus; Materialien/Shader für die neue Karte wiederverwendet', '—', 'triplanare Kluft-Shader', 'keine neue Nutzung als Karte'),
('compare-rock / compare-tree', 'Fels, Baum', 'res://assets/compare/', 'assets-source/phase14/build_compare_props.py', 'statisch', '360 / 852', 'Vergleichsszene', 'Kartendekoration Dschungel', '—', 'einfach', 'keiner'),
('Türme', '—', '(fehlt)', '—', '—', '—', '—', '16 Lanetürme/Basistürme', '—', '—', 'neu: Blender-Generator (M1)'),
('Bossgrotte', '—', '(fehlt)', '—', '—', '—', '—', 'Grottenbau nahe Zentrum', '—', '—', 'neu (M2)'),
('Emblem/Theme', 'Saumwacht-Theme', 'scripts/ui/theme/, assets/ui/', 'Code / SVG', '—', '—', 'Menü 20A', 'Theme weiterverwendet, Titel/Emblem Luneraria', '—', 'Vektor', 'Titelschrift/Emblem ergänzen'),
]
lines = ['# Asset-Wiederverwendungsmatrix', '',
'Erzeugt mit `python3 tools/luneraria/build_asset_matrix.py` aus den Laufzeitmanifesten. „Volk/Bund“ ist die Datenzuordnung. Die sieben Zielvölker (Menschen, Elfen, Zwerge, Trolle, Feen, Orks, Biestmenschen) haben noch **keine eigenen Modelle**; bestehende Wesen bleiben als eigene Bünde Teil der Welt. Als „vorläufig“ markierte Rollen sind Zuordnungen vorhandener Modelle, keine neu erstellten Völker.', '',
'| ID | Name | Pfad | Quelle | Rig / Clips | Dreiecke | bisherige Rolle | neue Rolle | Volk / Bund | Materialzustand | Eingriff |',
'| --- | --- | --- | --- | --- | ---: | --- | --- | --- | --- | --- |']
for row in rows + extra:
lines.append('| ' + ' | '.join(str(x) for x in row) + ' |')
(P / 'docs/luneraria').mkdir(parents=True, exist_ok=True)
(P / 'docs/luneraria/ASSETMATRIX.md').write_text('\n'.join(lines) + '\n')
print('ASSET_MATRIX', len(rows) + len(extra), 'rows')