Drei Helden mit Nähe-/Teilnahme-XP, Talenten und drei Itemplätzen, drei Angriffschampions mit Buchung n -> n+1, Bossgrotte mit Streitfall, ruhendem Boss, idempotenter Eroberung, einmaliger Entsendung für das Siegerteam, hohem Kopfgeld und Sperre bis Sieg+4. HUD-Reiter, Porträt aus echtem Modell, Grotten-Asset, Bots für alle Systeme. Regeln M2 73/73, M1 91/91, App M2 15/15 und M1 38/38, Bossszenarien dokumentiert. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
98 lines
8.4 KiB
Python
98 lines
8.4 KiB
Python
#!/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; zusätzlich Held „Funkenlanze“ (vorläufig; Goldring, Namensschild, ×1,25)', 'Glutkonstrukt / Glutpakt', 'umgesetzt (M2)'),
|
||
'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; zusätzlich Held „Ringborke“ (vorläufig)', 'Wurzelwesen / Wurzelbund', 'umgesetzt (M2)'),
|
||
'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; zusätzlich Held „Quellgeflecht“ (vorläufig)', 'Wurzelwesen / Wurzelbund', 'umgesetzt (M2)'),
|
||
'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', 'neutraler Grottenboss und entsandter Boss (×1,7)', 'Riftbrut / Grotte', 'umgesetzt (M2)'),
|
||
's01': ('Söldner', 'Reserve Angriffschampion', 'Söldner', 'offen'),
|
||
's02': ('Söldner', 'Angriffschampion Schutz (Rostschieber)', 'Söldner', 'umgesetzt (M2)'),
|
||
's03': ('Söldner', 'Angriffschampion Druck auf Unterstützer (Dunstsänger)', 'Söldner', 'umgesetzt (M2)'),
|
||
's04': ('Söldner', 'Angriffschampion Belagerung (Keilbrecher)', 'Söldner', 'umgesetzt (M2)'),
|
||
'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'),
|
||
('lane_tower', 'Laneturm', 'res://assets/luneraria/tower.glb', 'assets-source/luneraria/build_tower.py', 'statisch', '648', '—', '16 Lanetürme und Basistürme, Kristall zur Laufzeit in Teamfarbe', '—', 'ENV_*-Kluftshader, Kristall emissiv', 'neu (M1)'),
|
||
('boss_grotto', 'Bossgrotte', 'res://assets/luneraria/grotto.glb', 'assets-source/luneraria/build_grotto.py', 'statisch', '1888', '—', 'Felsinsel mit Höhle auf der Symmetrieachse, zwei gespiegelte Zugänge', '—', 'ENV_*-Kluftshader, violette Kristalle', 'neu (M2)'),
|
||
('luneraria-emblem', 'Emblem Luneraria', 'res://assets/ui/emblems/luneraria.svg', 'SVG', '—', '—', '—', 'Titel und Menü', '—', 'Vektor', 'neu (M1)'),
|
||
('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')
|