Compare commits

..

8 commits

Author SHA1 Message Date
Martin Stickelbroeck
c15533831a M4: Spiel läuft vollständig über die autoritative Sitzung; M4 pausiert
Das echte Spiel umging die Sitzung bisher bei Bereitschaft, Startaufstellung
und Rückgängig, ließ Bots den Gast-Sitz planen und den Gast selbst simulieren.
Jetzt rechnet nur der Host; Gäste zeigen den Kampf aus Momentaufnahmen.
Dazu Protokollversion, Wiedereinstieg über den Anzeigenamen, verbindliches
Bereit, Entwurfs-Privatsphäre und Bot-Planung einmal pro Runde.

Geprüft: test_lune_net 43/43, net_rounds.py 12/12 (5 Runden, Wiedereinstieg,
gleiche Prüfsummen), Regeln 91/91, M2 73/73, verify_m1 38/38, verify_m2 14/14.
Offen und eingefroren: OpenGL-Modus, nativer Windows-11-Test, Demo-Neubau.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 23:00:08 +02:00
Martin Stickelbroeck
214ed5a1cc M4: autoritative Host/Join-Sitzung, Protokolltests, Loopback und Demos
LuneSession hält den autoritativen Matchzustand; Clients senden nur
Absichten. Fremde Sitze, veraltete Befehle und Doppelkäufe sind geprüft,
Disconnect führt zur Bot-Übernahme statt zum Kernverlust. Exporte und
entpackbare Demos 0.16.0-m4 für Linux und Windows.
2026-09-28 00:57:02 +02:00
Martin Stickelbroeck
faf0fbdef0 M3: Balance, Kartenprops, sieben Volksrepräsentanten und Abnahme
Bots planen Items/Champions vor der Armee und helfen deterministisch,
damit Spiegelmatches fair bleiben. Boss und Profile neu abgestimmt;
Balance-, Stress- und Volks-Werkzeuge sowie Kartenprops ergänzt.
2026-09-28 00:41:05 +02:00
Martin Stickelbroeck
d7c3a03e23 M2: QA-Matrix und Status nachgetragen
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 22:33:52 +02:00
Martin Stickelbroeck
f53859a298 M2: Helden, Items, Talente, Angriffschampions und Bossgrotte
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>
2026-09-27 22:33:11 +02:00
Martin Stickelbroeck
c27a0097c3 M1: spielbarer Drei-Lane-Kern mit Karte, HUD, Menü und Abnahme
Hauptszene scenes/luneraria.tscn, Saumwacht bleibt über --legacy und die
alten QA-Schalter erreichbar. Karte aus Bestandsassets plus neuem Turm,
HUD mit Planung, Lanes und Minimap, Menü umbenannt, Einstellungen werden
aus dem alten Benutzerordner übernommen. Simulation 6,6x schneller
(Raster, Caches, faire Staffelung) und exakt spiegelsymmetrisch.
Regeln 91/91, echte Anwendung 38/38, Legacy unverändert grün.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 21:37:23 +02:00
Martin Stickelbroeck
b565546c37 M1: autoritativer Drei-Lane-Kern (Karte, Kampf, Partie, Wellen, Bots)
LuneMap spiegelsymmetrisch mit 16 Türmen und 2 Ancients, LuneCombat mit
reihenfolgeunabhängigem Zweiphasenschritt und Gebäudesperren für jede
Schadensquelle, LuneMatch mit Planungsentwürfen, atomarer Übernahme,
Einmal-Belohnung und Kernsieg/Unentschieden, endlose LuneWaves, Bots über
dieselbe Befehlsschnittstelle. 87 Kernprüfungen bestanden.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 20:57:37 +02:00
Martin Stickelbroeck
4b6a4f530e 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>
2026-09-27 20:04:36 +02:00
256 changed files with 31611 additions and 22 deletions

View file

@ -0,0 +1,158 @@
"""Boss grotto for Champions of Luneraria: a rock island in the river with a crescent of
cliffs, a cave mouth and violet crystals. Symmetric across the map diagonal, so both
entrances (towards Blau and towards Rot) are equal. Stone uses ENV_* material names.
Run: ALSOFT_DRIVERS=null blender -b --python-exit-code 1 --python assets-source/luneraria/build_grotto.py
Local Godot frame: +x/+z are the map axes; the map diagonal (x = z) is the mirror line.
"""
from pathlib import Path
import bpy, bmesh, json, hashlib, struct
from math import sin, cos, pi, sqrt
from mathutils import Vector, noise
P = Path(__file__).resolve().parents[2]
SRC = Path(__file__).resolve().parent
OUT = P / 'assets/luneraria'
OUT.mkdir(parents=True, exist_ok=True)
bpy.ops.wm.read_factory_settings(use_empty=True)
bpy.context.preferences.filepaths.save_version = 0
parts = []
def G(x, y, z):
return Vector((x, -z, y))
def material(name, color, rough=.9, metal=0.0, emission=0.0):
m = bpy.data.materials.new(name)
m.diffuse_color = (*color, 1)
m.use_nodes = True
bs = m.node_tree.nodes.get('Principled BSDF')
bs.inputs['Base Color'].default_value = (*color, 1)
bs.inputs['Roughness'].default_value = rough
bs.inputs['Metallic'].default_value = metal
if emission:
bs.inputs['Emission Color'].default_value = (*color, 1)
bs.inputs['Emission Strength'].default_value = emission
return m
M = {
'cliff': material('ENV_cliff', (.133, .153, .138)),
'soil': material('ENV_soil', (.10, .15, .065), .97),
'limestone': material('ENV_limestone', (.283, .246, .178)),
'dark': material('GR_Shadow', (.02, .02, .03), 1.0),
'crystal': material('GR_Crystal', (.62, .38, 1.0), .15, 0, 3.0),
}
def obj(name, vs, fs, kind, smooth=False):
me = bpy.data.meshes.new(name)
me.from_pydata([tuple(v) for v in vs], [], fs)
me.update()
me.materials.append(M[kind])
for p in me.polygons:
p.use_smooth = smooth
ob = bpy.data.objects.new(name, me)
bpy.context.collection.objects.link(ob)
parts.append(ob)
return ob
def island(name, radius, top, bottom, seed, kind='soil'):
sides, levels = 64, 6
vs, fs = [], []
for li in range(levels):
y = top - (top - bottom) * li / (levels - 1)
for k in range(sides):
a = 2 * pi * k / sides
# Radius depends on |angle - 45 deg| so the island is symmetric across x = z.
m = abs(((a - pi / 4) + pi) % (2 * pi) - pi)
r = radius * (1 + .08 * noise.noise(Vector((cos(m) * 2, li * .5, seed)))) * (1 - .06 * li / levels)
vs.append(G(cos(a) * r, y, sin(a) * r))
for li in range(levels - 1):
for k in range(sides):
a, b = li * sides + k, li * sides + (k + 1) % sides
fs.append((a, a + sides, b + sides, b))
fs += [tuple(range(sides)), tuple(reversed(range((levels - 1) * sides, levels * sides)))]
return obj(name, vs, fs, kind)
def boulder(name, center, size, seed, kind='cliff'):
bm = bmesh.new()
bmesh.ops.create_icosphere(bm, subdivisions=2, radius=1.0)
c = G(*center)
for v in bm.verts:
d = v.co.normalized()
r = 1 + .32 * noise.noise(d * 1.8 + Vector((seed, seed * .5, seed * .3)))
gx, gy, gz = d.x * r * size[0], d.z * r * size[1], -d.y * r * size[2]
if gy < -.25 * size[1]:
gy = -.25 * size[1]
v.co = c + Vector((gx, -gz, gy))
me = bpy.data.meshes.new(name)
bm.to_mesh(me)
bm.free()
me.materials.append(M[kind])
ob = bpy.data.objects.new(name, me)
bpy.context.collection.objects.link(ob)
parts.append(ob)
return ob
def gem(name, center, height, radius, tilt, kind='crystal'):
cx, cy, cz = center
tip = G(cx + tilt[0], cy + height, cz + tilt[1])
base = [G(cx + cos(2 * pi * k / 6) * radius, cy + height * .25, cz + sin(2 * pi * k / 6) * radius) for k in range(6)]
root = G(cx, cy - .1, cz)
vs = [tip] + base + [root]
fs = [(0, 1 + (k + 1) % 6, 1 + k) for k in range(6)] + [(7, 1 + k, 1 + (k + 1) % 6) for k in range(6)]
return obj(name, vs, fs, kind)
def mirror(p):
return (p[2], p[1], p[0])
island('Island', 7.2, .35, -1.2, 3)
# Crescent of cliffs on the far side from Mid (towards -x-z), open towards both entrances.
ring = []
for k in range(9):
a = pi + pi / 4 + (k - 4) * .30
ring.append((cos(a) * 5.6, 0.0, sin(a) * 5.6, 1.5 + (1.2 if abs(k - 4) <= 1 else .5 * (4 - abs(k - 4)) / 4)))
for i, (x, y, z, h) in enumerate(ring):
boulder('Cliff_%d' % i, (x, y + h * .45, z), (1.6, h, 1.4), 10 + min(i, 8 - i))
# Cave mouth: dark recess under an arch in the middle of the crescent.
a = pi + pi / 4
boulder('Cave_Shadow', (cos(a) * 4.6, .8, sin(a) * 4.6), (1.4, 1.1, .7), 31, 'dark')
boulder('Arch_Top', (cos(a) * 4.8, 3.1, sin(a) * 4.8), (2.2, .8, 1.3), 32)
# Violet crystals, mirrored pairs so neither entrance looks different.
for i, (x, z, h, r) in enumerate([(-2.2, -3.6, 1.8, .32), (-3.9, -1.2, 1.4, .28), (-1.0, -4.3, 1.1, .22), (-3.2, -3.2, 2.3, .4)]):
for p in {(x, 0.3, z), mirror((x, 0.3, z))}:
gem('Crystal_%d_%d' % (i, int(p[0] * 10)), p, h, r, (.12 * (1 if p[0] < p[2] else -1), .08))
# Entrance stones: symmetric pair of limestone steps facing Blau (+x) and Rot (+z)... see manifest.
for p in [(4.6, .18, 1.2), mirror((4.6, .18, 1.2))]:
boulder('Step_%d' % int(p[0] * 10), p, (1.1, .25, .8), 40, 'limestone')
bpy.ops.object.select_all(action='DESELECT')
for ob in parts:
ob.select_set(True)
bpy.context.view_layer.objects.active = parts[0]
bpy.ops.object.join()
grotto = bpy.context.object
grotto.name = 'BossGrotto'
bm = bmesh.new()
bm.from_mesh(grotto.data)
bmesh.ops.triangulate(bm, faces=list(bm.faces))
bm.to_mesh(grotto.data)
bm.free()
bpy.ops.wm.save_as_mainfile(filepath=str(SRC / 'grotto.blend'))
bpy.ops.export_scene.gltf(filepath=str(OUT / 'grotto.glb'), export_format='GLB', use_selection=True,
export_materials='EXPORT', export_yup=True, export_animations=False)
raw = (OUT / 'grotto.glb').read_bytes()
doc = json.loads(raw[20:20 + struct.unpack_from('<I', raw, 12)[0]])
triangles = sum(doc['accessors'][q['indices']]['count'] // 3 for m in doc['meshes'] for q in m['primitives'])
(OUT / 'grotto_manifest.json').write_text(json.dumps({'assets': [{
'id': 'boss_grotto', 'resource_path': 'res://assets/luneraria/grotto.glb', 'source': 'assets-source/luneraria/grotto.blend',
'triangles': triangles, 'sha256': hashlib.sha256(raw).hexdigest(),
'materials': [m['name'] for m in doc['materials']]}]}, indent=2) + '\n')
print('GROTTO_BUILD', triangles, 'triangles', flush=True)

View file

@ -0,0 +1,248 @@
"""Map dressing for Champions of Luneraria: river bridge, curb stone, trees, rocks, banner
and edge cliffs, exported as separate named meshes in one GLB. Godot instances them with
MultiMeshes. Stone, bark and leaves use ENV_* names so the Kluft shaders (with wind on
foliage) apply at runtime.
Run: ALSOFT_DRIVERS=null blender -b --python-exit-code 1 --python assets-source/luneraria/build_map_props.py
Godot frame per prop: y up, prop origin at ground level; the bridge spans local +x.
"""
from pathlib import Path
import bpy, bmesh, json, hashlib, struct
from math import sin, cos, pi
from mathutils import Vector, noise
P = Path(__file__).resolve().parents[2]
SRC = Path(__file__).resolve().parent
OUT = P / 'assets/luneraria'
bpy.ops.wm.read_factory_settings(use_empty=True)
bpy.context.preferences.filepaths.save_version = 0
def G(x, y, z):
return Vector((x, -z, y))
def material(name, color, rough=.9, metal=0.0, emission=0.0):
m = bpy.data.materials.new(name)
m.diffuse_color = (*color, 1)
m.use_nodes = True
bs = m.node_tree.nodes.get('Principled BSDF')
bs.inputs['Base Color'].default_value = (*color, 1)
bs.inputs['Roughness'].default_value = rough
bs.inputs['Metallic'].default_value = metal
if emission:
bs.inputs['Emission Color'].default_value = (*color, 1)
bs.inputs['Emission Strength'].default_value = emission
return m
M = {
'limestone': material('ENV_limestone', (.283, .246, .178)),
'paving': material('ENV_paving', (.19, .185, .15), .92),
'cliff': material('ENV_cliff', (.133, .153, .138)),
'bark': material('ENV_bark', (.087, .056, .032), .93),
'leaf_dark': material('ENV_leaf_dark', (.031, .07, .04), .89),
'leaf_mid': material('ENV_leaf_mid', (.084, .144, .054), .85),
'leaf_light': material('ENV_leaf_light', (.258, .305, .091), .79),
'soil': material('ENV_soil', (.10, .15, .065), .97),
'brass': material('ENV_brass', (.515, .366, .153), .45, .7),
'cloth': material('PR_Banner', (.9, .9, .9), .8),
}
groups = {}
def add(group, name, vs, fs, kind, smooth=False):
me = bpy.data.meshes.new(name)
me.from_pydata([tuple(v) for v in vs], [], fs)
me.update()
me.materials.append(M[kind])
for p in me.polygons:
p.use_smooth = smooth
ob = bpy.data.objects.new(name, me)
bpy.context.collection.objects.link(ob)
groups.setdefault(group, []).append(ob)
return ob
def box(group, name, c, size, kind, yaw=0.0):
cx, cy, cz = c
hx, hy, hz = size[0] / 2, size[1] / 2, size[2] / 2
vs = []
for dy in (-1, 1):
for dz in (-1, 1):
for dx in (-1, 1):
x, z = dx * hx, dz * hz
vs.append(G(cx + x * cos(yaw) - z * sin(yaw), cy + dy * hy, cz + x * sin(yaw) + z * cos(yaw)))
return add(group, name, vs, [(0, 1, 3, 2), (4, 6, 7, 5), (0, 4, 5, 1), (2, 3, 7, 6), (0, 2, 6, 4), (1, 5, 7, 3)], kind)
def lathe(group, name, profile, sides, kind, c=(0, 0, 0), jitter=0.0, seed=0.0, smooth=False):
cx, cy, cz = c
vs, fs = [], []
for r, y in profile:
for k in range(sides):
a = 2 * pi * k / sides
rr = r * (1 + jitter * noise.noise(Vector((cos(a) * 2, y * 1.3, sin(a) * 2 + seed))))
vs.append(G(cx + cos(a) * rr, cy + y, cz + sin(a) * rr))
n = len(profile)
for j in range(n - 1):
for k in range(sides):
a, b = j * sides + k, j * sides + (k + 1) % sides
fs.append((a, b, b + sides, a + sides))
fs += [tuple(reversed(range(sides))), tuple(range((n - 1) * sides, n * sides))]
return add(group, name, vs, fs, kind, smooth)
def blob(group, name, c, size, kind, seed, subdiv=2, amp=.3, floor=None, smooth=False):
bm = bmesh.new()
bmesh.ops.create_icosphere(bm, subdivisions=subdiv, radius=1.0)
cc = G(*c)
for v in bm.verts:
d = v.co.normalized()
r = 1 + amp * noise.noise(d * 1.7 + Vector((seed, seed * .7, seed * .3)))
gx, gy, gz = d.x * r * size[0], d.z * r * size[1], -d.y * r * size[2]
if floor is not None and gy < floor * size[1]:
gy = floor * size[1]
v.co = cc + Vector((gx, -gz, gy))
me = bpy.data.meshes.new(name)
bm.to_mesh(me)
bm.free()
me.materials.append(M[kind])
for p in me.polygons:
p.use_smooth = smooth
ob = bpy.data.objects.new(name, me)
bpy.context.collection.objects.link(ob)
groups.setdefault(group, []).append(ob)
return ob
def tube(group, name, points, radii, kind, sides=8, steps=10, seed=0.0, gnarl=0.0):
pts = [G(*p) for p in points]
vs, fs = [], []
for j in range(steps + 1):
t = j / steps
seg = min(len(pts) - 2, int(t * (len(pts) - 1)))
u = t * (len(pts) - 1) - seg
p = pts[seg].lerp(pts[seg + 1], u)
tangent = (pts[seg + 1] - pts[seg]).normalized()
ref = Vector((0, 0, 1)) if abs(tangent.z) < .9 else Vector((1, 0, 0))
a1 = tangent.cross(ref).normalized()
a2 = tangent.cross(a1)
rs = min(len(radii) - 2, int(t * (len(radii) - 1)))
ru = t * (len(radii) - 1) - rs
radius = radii[rs] * (1 - ru) + radii[rs + 1] * ru
for k in range(sides):
ang = 2 * pi * k / sides
w = 1 + gnarl * noise.noise(Vector((cos(ang), t * 5, sin(ang) + seed)))
vs.append(p + (a1 * cos(ang) + a2 * sin(ang)) * radius * w)
for j in range(steps):
for k in range(sides):
a, b = j * sides + k, j * sides + (k + 1) % sides
fs.append((a, b, b + sides, a + sides))
fs += [tuple(reversed(range(sides))), tuple(range(steps * sides, (steps + 1) * sides))]
return add(group, name, vs, fs, kind, True)
# ---------------------------------------------------------------- bridge (spans local x, 11 m long, 7 m wide)
# The simulation is flat: the deck sits at ground level so units walk on it, not inside it.
L, Wd = 11.0, 7.0
for i in range(12):
x = -L / 2 + (i + .5) * L / 12
box('Bridge', 'Deck_%d' % i, (x, .05, 0), (L / 12 + .02, .1, Wd), 'paving')
for s_ in (-1, 1):
box('Bridge', 'Parapet_%d_%d' % (i, s_), (x, .42, s_ * (Wd / 2 - .22)), (L / 12 + .03, .62, .38), 'limestone')
for x in (-L / 2 + .4, -1.8, 1.8, L / 2 - .4):
for s_ in (-1, 1):
lathe('Bridge', 'Post_%d_%d' % (int(x * 10), s_), [(.32, 0), (.32, 1.05), (.26, 1.15), (.2, 1.3)], 6, 'limestone', (x, 0, s_ * (Wd / 2 - .22)))
lathe('Bridge', 'Cap_%d_%d' % (int(x * 10), s_), [(.22, 0), (.22, .1), (.14, .16)], 6, 'brass', (x, 1.3, s_ * (Wd / 2 - .22)))
for x in (-2.6, 2.6):
for s_ in (-1, 1):
box('Bridge', 'Pier_%d_%d' % (int(x), s_), (x, -.6, s_ * (Wd / 2 + .15)), (1.1, 1.4, .5), 'limestone')
# ---------------------------------------------------------------- curb stone (placed along lane edges)
box('Curb', 'Curb', (0, .12, 0), (1.3, .26, .42), 'limestone')
# ---------------------------------------------------------------- trees
for v, (h, seed) in enumerate([(3.8, 1.0), (4.6, 2.0), (3.2, 3.0)]):
group = 'Pine_%d' % v
tube(group, 'Trunk', [(0, 0, 0), (0, h * .55, 0), (0, h, 0)], [.24, .17, .08], 'bark', 8, 6, seed, .1)
for k in range(5):
y = h * (.28 + k * .15)
r = (1.35 - k * .22) * (1 + .08 * v)
lathe(group, 'Cone_%d' % k, [(r, y), (r * .75, y + .35), (.05, y + h * .26)], 9, ('leaf_dark', 'leaf_mid')[k % 2], (0, 0, 0), .12, seed + k)
for v, (h, seed) in enumerate([(3.4, 4.0), (4.0, 5.0), (2.8, 6.0)]):
group = 'Oak_%d' % v
tube(group, 'Trunk', [(0, 0, 0), (.15, h * .5, .05), (-.1, h * .85, 0)], [.34, .24, .15], 'bark', 9, 8, seed, .15)
for k in range(3):
ang = k * 2.1 + seed
tube(group, 'Branch_%d' % k, [(0, h * .55, 0), (cos(ang) * .9, h * .78, sin(ang) * .9)], [.13, .06], 'bark', 6, 4, seed + k, .1)
for k in range(9):
ang = k * 2.4 + seed
rr = .55 + .5 * ((k * .618) % 1.0)
s = .95 - .2 * ((k * .37) % 1.0)
blob(group, 'Leaves_%d' % k, (cos(ang) * rr, h * .88 + .5 * ((k * .53) % 1.0), sin(ang) * rr), (s, s * .78, s),
('leaf_mid', 'leaf_light', 'leaf_dark')[k % 3], seed * 10 + k, 2, .28)
for k in range(4):
ang = k * 1.57 + seed
tube(group, 'Root_%d' % k, [(0, .25, 0), (cos(ang) * .7, .05, sin(ang) * .7)], [.14, .05], 'bark', 6, 4, seed + k)
# ---------------------------------------------------------------- rocks and shrubs
for v, (size, seed) in enumerate([((1.2, .8, 1.0), 7.0), ((1.8, 1.1, 1.3), 8.0), ((.8, .5, .7), 9.0)]):
group = 'Rock_%d' % v
blob(group, 'Rock', (0, size[1] * .3, 0), size, 'cliff', seed, 2, .34, -.35)
blob(group, 'Moss', (size[0] * .15, size[1] * .82, 0), (size[0] * .55, size[1] * .25, size[2] * .5), 'leaf_dark', seed + 1, 1, .3, -.2)
for k in range(2):
blob(group, 'Pebble_%d' % k, (size[0] * (.9 + .3 * k), .1, size[2] * (.5 - k)), (.28, .18, .24), 'limestone', seed + 3 + k, 1, .3, -.3)
for v in range(2):
blob('Shrub_%d' % v, 'Shrub', (0, .3, 0), (.8 + .3 * v, .55, .7 + .2 * v), ('leaf_mid', 'leaf_dark')[v], 11 + v, 2, .35, -.3)
# ---------------------------------------------------------------- banner (team colour at runtime via PR_Banner)
lathe('Banner', 'Pole', [(.07, 0), (.07, 4.2), (.1, 4.3), (.02, 4.5)], 6, 'brass')
vs, fs = [], []
for j in range(6):
for i in range(4):
x = .08 + i * .35
y = 4.1 - j * .45
z = .08 * sin(i * 1.3 + j * .6)
vs.append(G(x, y - (.25 * (i / 3) if j == 5 else 0), z))
for j in range(5):
for i in range(3):
a = j * 4 + i
fs.append((a, a + 1, a + 5, a + 4))
add('Banner', 'Cloth', vs, fs, 'cloth', True)
# ---------------------------------------------------------------- edge cliffs (a 12 m wall segment, local x)
for i in range(7):
x = -6 + i * 2
h = 3.4 + 1.6 * noise.noise(Vector((i * .7, 3.1, 0)))
blob('EdgeCliff', 'Block_%d' % i, (x, h * .45, 0), (1.6, h * .6, 1.8), 'cliff', 20 + i, 2, .3, -.5)
blob('EdgeCliff', 'Grass_%d' % i, (x, h * .85, -.3), (1.2, .35, 1.3), 'leaf_mid', 30 + i, 1, .3, -.2)
# Join each group into one mesh object per prop and triangulate.
exported = []
for group, objects in groups.items():
bpy.ops.object.select_all(action='DESELECT')
for ob in objects:
ob.select_set(True)
bpy.context.view_layer.objects.active = objects[0]
bpy.ops.object.join()
joined = bpy.context.object
joined.name = group
bm = bmesh.new()
bm.from_mesh(joined.data)
bmesh.ops.triangulate(bm, faces=list(bm.faces))
bm.to_mesh(joined.data)
bm.free()
exported.append(joined)
bpy.ops.object.select_all(action='DESELECT')
for ob in exported:
ob.select_set(True)
bpy.ops.wm.save_as_mainfile(filepath=str(SRC / 'map_props.blend'))
bpy.ops.export_scene.gltf(filepath=str(OUT / 'map_props.glb'), export_format='GLB', use_selection=True,
export_materials='EXPORT', export_yup=True, export_animations=False)
raw = (OUT / 'map_props.glb').read_bytes()
doc = json.loads(raw[20:20 + struct.unpack_from('<I', raw, 12)[0]])
counts = {m['name']: sum(doc['accessors'][q['indices']]['count'] // 3 for q in m['primitives']) for m in doc['meshes']}
(OUT / 'map_props_manifest.json').write_text(json.dumps({'assets': [{'id': 'map_props', 'resource_path': 'res://assets/luneraria/map_props.glb',
'source': 'assets-source/luneraria/map_props.blend', 'props': counts, 'sha256': hashlib.sha256(raw).hexdigest()}]}, indent=2) + '\n')
print('MAP_PROPS_BUILD', counts, flush=True)

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,144 @@
"""Lane tower for Champions of Luneraria: stone plinth, tapered shaft with brass bands,
crenellated platform and a hovering crystal. The crystal material (TW_Crystal) is tinted
per team at runtime; stone surfaces use the Kluft material names (ENV_*).
Run: ALSOFT_DRIVERS=null blender -b --python-exit-code 1 --python assets-source/luneraria/build_tower.py
Godot coordinates are written through G(x, y, z) (x right, y up, z towards the viewer).
"""
from pathlib import Path
import bpy, bmesh, json, hashlib, struct
from math import sin, cos, pi
from mathutils import Vector, noise
P = Path(__file__).resolve().parents[2]
SRC = Path(__file__).resolve().parent
OUT = P / 'assets/luneraria'
OUT.mkdir(parents=True, exist_ok=True)
bpy.ops.wm.read_factory_settings(use_empty=True)
bpy.context.preferences.filepaths.save_version = 0
parts = []
def G(x, y, z):
return Vector((x, -z, y))
def material(name, color, rough=.9, metal=0.0, emission=0.0):
m = bpy.data.materials.new(name)
m.diffuse_color = (*color, 1)
m.use_nodes = True
bs = m.node_tree.nodes.get('Principled BSDF')
bs.inputs['Base Color'].default_value = (*color, 1)
bs.inputs['Roughness'].default_value = rough
bs.inputs['Metallic'].default_value = metal
if emission:
bs.inputs['Emission Color'].default_value = (*color, 1)
bs.inputs['Emission Strength'].default_value = emission
return m
M = {
'stone': material('ENV_limestone', (.283, .246, .178)),
'dark': material('ENV_cliff', (.133, .153, .138)),
'paving': material('ENV_paving', (.162, .171, .136), .92),
'brass': material('ENV_brass', (.515, .366, .153), .45, .7),
'crystal': material('TW_Crystal', (.55, .8, 1.0), .15, 0, 2.5),
}
def obj(name, vs, fs, kind, smooth=False):
me = bpy.data.meshes.new(name)
me.from_pydata([tuple(v) for v in vs], [], fs)
me.update()
me.materials.append(M[kind])
for p in me.polygons:
p.use_smooth = smooth
ob = bpy.data.objects.new(name, me)
bpy.context.collection.objects.link(ob)
parts.append(ob)
return ob
def lathe(name, profile, sides, kind, center=(0, 0, 0), rotation=0.0, jitter=0.0, seed=0):
cx, cy, cz = center
vs, fs = [], []
for j, (r, y) in enumerate(profile):
for k in range(sides):
a = 2 * pi * k / sides + rotation
rr = r * (1 + jitter * noise.noise(Vector((cos(a) * 3, y * 2, sin(a) * 3 + seed))))
vs.append(G(cx + cos(a) * rr, cy + y, cz + sin(a) * rr))
n = len(profile)
for j in range(n - 1):
for k in range(sides):
a, b = j * sides + k, j * sides + (k + 1) % sides
fs.append((a, b, b + sides, a + sides))
fs += [tuple(reversed(range(sides))), tuple(range((n - 1) * sides, n * sides))]
return obj(name, vs, fs, kind)
def box(name, center, size, yaw, kind):
cx, cy, cz = center
hx, hy, hz = size[0] / 2, size[1] / 2, size[2] / 2
vs = []
for dy in (-1, 1):
for dz in (-1, 1):
for dx in (-1, 1):
x, z = dx * hx, dz * hz
vs.append(G(cx + x * cos(yaw) - z * sin(yaw), cy + dy * hy, cz + x * sin(yaw) + z * cos(yaw)))
return obj(name, vs, [(0, 1, 3, 2), (4, 6, 7, 5), (0, 4, 5, 1), (2, 3, 7, 6), (0, 2, 6, 4), (1, 5, 7, 3)], kind)
# Plinth: two stepped octagons of dark stone with a paving top.
lathe('Plinth', [(1.65, 0.0), (1.65, .28), (1.45, .34), (1.45, .52), (1.3, .58)], 8, 'dark', rotation=pi / 8, jitter=.03, seed=1)
lathe('Plinth_Top', [(1.28, .57), (1.28, .62)], 8, 'paving', rotation=pi / 8)
# Shaft: tapered, slightly irregular limestone blocks.
shaft = [(1.02, .6), (.98, 1.4), (.9, 2.4), (.84, 3.2), (.9, 3.4)]
lathe('Shaft', shaft, 10, 'stone', rotation=pi / 10, jitter=.025, seed=2)
for h in (1.05, 2.25, 3.25):
r = .99 - (h - .6) * .05
lathe('Band_%d' % int(h * 10), [(r + .06, h - .07), (r + .06, h + .07)], 10, 'brass', rotation=pi / 10)
# Platform with crenellations.
lathe('Platform', [(.95, 3.35), (1.25, 3.6), (1.25, 3.85), (1.1, 3.9)], 10, 'stone', rotation=pi / 10, jitter=.02, seed=3)
for k in range(10):
a = 2 * pi * (k + .5) / 10
box('Merlon_%d' % k, (cos(a) * 1.08, 4.08, sin(a) * 1.08), (.42, .42, .26), -a + pi / 2, 'stone')
# Crystal cradle: four brass prongs and a hovering bipyramid crystal.
for k in range(4):
a = pi / 4 + k * pi / 2
pts = [(cos(a) * .55, 3.9, sin(a) * .55), (cos(a) * .42, 4.55, sin(a) * .42), (cos(a) * .22, 5.0, sin(a) * .22)]
for i in range(2):
p0, p1 = Vector(pts[i]), Vector(pts[i + 1])
mid = (p0 + p1) / 2
d = p1 - p0
box('Prong_%d_%d' % (k, i), tuple(mid), (.09, d.length + .05, .09), 0, 'brass')
crystal = [G(0, 5.95, 0)] + [G(cos(2 * pi * k / 6) * .36, 5.05, sin(2 * pi * k / 6) * .36) for k in range(6)] + [G(0, 4.45, 0)]
cf = [(0, 1 + k, 1 + (k + 1) % 6) for k in range(6)] + [(7, 1 + (k + 1) % 6, 1 + k) for k in range(6)]
obj('Crystal', crystal, cf, 'crystal')
# A small door and window slits keep the scale readable from the game camera.
box('Door', (0, .95, 1.0), (.5, .75, .12), 0, 'dark')
for k, h in enumerate((1.7, 2.7)):
box('Slit_%d' % k, (0, h, .93 - (h - 1.7) * .05), (.14, .42, .1), 0, 'dark')
bpy.ops.object.select_all(action='DESELECT')
for ob in parts:
ob.select_set(True)
bpy.context.view_layer.objects.active = parts[0]
bpy.ops.object.join()
tower = bpy.context.object
tower.name = 'LaneTower'
bm = bmesh.new()
bm.from_mesh(tower.data)
bmesh.ops.triangulate(bm, faces=list(bm.faces))
bm.to_mesh(tower.data)
bm.free()
bpy.ops.wm.save_as_mainfile(filepath=str(SRC / 'tower.blend'))
bpy.ops.export_scene.gltf(filepath=str(OUT / 'tower.glb'), export_format='GLB', use_selection=True,
export_materials='EXPORT', export_yup=True, export_animations=False)
raw = (OUT / 'tower.glb').read_bytes()
doc = json.loads(raw[20:20 + struct.unpack_from('<I', raw, 12)[0]])
triangles = sum(doc['accessors'][q['indices']]['count'] // 3 for m in doc['meshes'] for q in m['primitives'])
(P / 'assets/luneraria/tower_manifest.json').write_text(json.dumps({'assets': [{
'id': 'lane_tower', 'resource_path': 'res://assets/luneraria/tower.glb', 'source': 'assets-source/luneraria/tower.blend',
'triangles': triangles, 'height': 5.95, 'sha256': hashlib.sha256(raw).hexdigest(),
'materials': [m['name'] for m in doc['materials']]}]}, indent=2) + '\n')
print('TOWER_BUILD', triangles, 'triangles', flush=True)

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,245 @@
{
"figures": [
{
"id": "p_mensch",
"source_parts": 84,
"exported_clips": [
"ability",
"attack_a",
"attack_b",
"death",
"hit",
"idle",
"start",
"stop",
"walk"
],
"skins": 1,
"materials": [
"LP_Mensch_Cloth",
"LP_Mensch_Skin",
"LP_Mensch_Metal",
"LP_Mensch_TrimMetal",
"LP_Mensch_Leather",
"LP_Mensch_Dark",
"LP_Mensch_ClothAccent"
],
"max_wrist_contact_error_m": 0.0,
"worst_sample": [
"walk",
1.1,
"R"
],
"samples": 696,
"contact_validation": "Analytic two-bone arm IK against the grip bones, baked at 30 Hz for all nine clips."
},
{
"id": "p_elf",
"source_parts": 89,
"exported_clips": [
"ability",
"attack_a",
"attack_b",
"death",
"hit",
"idle",
"start",
"stop",
"walk"
],
"skins": 1,
"materials": [
"LP_Elf_Leather",
"LP_Elf_Skin",
"LP_Elf_Cloth",
"LP_Elf_ClothAccent",
"LP_Elf_TrimMetal",
"LP_Elf_Dark",
"LP_Elf_Hair",
"LP_Elf_Glow",
"LP_Elf_Wood"
],
"max_wrist_contact_error_m": 0.0,
"worst_sample": [
"walk",
1.1,
"R"
],
"samples": 696,
"contact_validation": "Analytic two-bone arm IK against the grip bones, baked at 30 Hz for all nine clips."
},
{
"id": "p_zwerg",
"source_parts": 91,
"exported_clips": [
"ability",
"attack_a",
"attack_b",
"death",
"hit",
"idle",
"start",
"stop",
"walk"
],
"skins": 1,
"materials": [
"LP_Zwerg_Cloth",
"LP_Zwerg_Skin",
"LP_Zwerg_Leather",
"LP_Zwerg_Metal",
"LP_Zwerg_TrimMetal",
"LP_Zwerg_Dark",
"LP_Zwerg_Hair",
"LP_Zwerg_Bone",
"LP_Zwerg_Wood",
"LP_Zwerg_Glow"
],
"max_wrist_contact_error_m": 0.0,
"worst_sample": [
"walk",
1.1,
"R"
],
"samples": 696,
"contact_validation": "Analytic two-bone arm IK against the grip bones, baked at 30 Hz for all nine clips."
},
{
"id": "p_troll",
"source_parts": 72,
"exported_clips": [
"ability",
"attack_a",
"attack_b",
"death",
"hit",
"idle",
"start",
"stop",
"walk"
],
"skins": 1,
"materials": [
"LP_Troll_Skin",
"LP_Troll_Leather",
"LP_Troll_Bone",
"LP_Troll_Stone",
"LP_Troll_EyeGlow",
"LP_Troll_Hair",
"LP_Troll_Wood"
],
"max_wrist_contact_error_m": 0.06613055871718698,
"worst_sample": [
"ability",
1.1,
"L"
],
"samples": 696,
"contact_validation": "Analytic two-bone arm IK against the grip bones, baked at 30 Hz for all nine clips."
},
{
"id": "p_fee",
"source_parts": 72,
"exported_clips": [
"ability",
"attack_a",
"attack_b",
"death",
"hit",
"idle",
"start",
"stop",
"walk"
],
"skins": 1,
"materials": [
"LP_Fee_ClothAccent",
"LP_Fee_Skin",
"LP_Fee_Cloth",
"LP_Fee_TrimMetal",
"LP_Fee_Dark",
"LP_Fee_Hair",
"LP_Fee_Glow",
"LP_Fee_Wood"
],
"max_wrist_contact_error_m": 0.0,
"worst_sample": [
"walk",
1.1,
"R"
],
"samples": 696,
"contact_validation": "Analytic two-bone arm IK against the grip bones, baked at 30 Hz for all nine clips."
},
{
"id": "p_ork",
"source_parts": 84,
"exported_clips": [
"ability",
"attack_a",
"attack_b",
"death",
"hit",
"idle",
"start",
"stop",
"walk"
],
"skins": 1,
"materials": [
"LP_Ork_Skin",
"LP_Ork_Leather",
"LP_Ork_Metal",
"LP_Ork_TrimMetal",
"LP_Ork_Bone",
"LP_Ork_Fur",
"LP_Ork_Dark",
"LP_Ork_Hair",
"LP_Ork_Wood"
],
"max_wrist_contact_error_m": 0.0,
"worst_sample": [
"walk",
1.1,
"R"
],
"samples": 696,
"contact_validation": "Analytic two-bone arm IK against the grip bones, baked at 30 Hz for all nine clips."
},
{
"id": "p_biest",
"source_parts": 82,
"exported_clips": [
"ability",
"attack_a",
"attack_b",
"death",
"hit",
"idle",
"start",
"stop",
"walk"
],
"skins": 1,
"materials": [
"LP_Biest_Fur",
"LP_Biest_FurLight",
"LP_Biest_Leather",
"LP_Biest_Metal",
"LP_Biest_Cloth",
"LP_Biest_Bone",
"LP_Biest_Dark",
"LP_Biest_EyeGlow",
"LP_Biest_Skin"
],
"max_wrist_contact_error_m": 0.0,
"worst_sample": [
"walk",
1.1,
"R"
],
"samples": 696,
"contact_validation": "Analytic two-bone arm IK against the grip bones, baked at 30 Hz for all nine clips."
}
]
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cdyqe8uxp766t"
path="res://.godot/imported/ChatGPT-Bild 27. Sept. 2026, 19_46_52-1.png-2d037606185a25aabd1841674c9a8676.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/ChatGPT-Bild 27. Sept. 2026, 19_46_52-1.png"
dest_files=["res://.godot/imported/ChatGPT-Bild 27. Sept. 2026, 19_46_52-1.png-2d037606185a25aabd1841674c9a8676.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View file

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://cm5ok6wwo5k4c"
path="res://.godot/imported/ChatGPT-Bild 27. Sept. 2026, 19_46_52-2.png-ad15328ef82598f8d29650086961c8e2.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/ChatGPT-Bild 27. Sept. 2026, 19_46_52-2.png"
dest_files=["res://.godot/imported/ChatGPT-Bild 27. Sept. 2026, 19_46_52-2.png-ad15328ef82598f8d29650086961c8e2.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

View file

@ -0,0 +1,40 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://bjtulyxjl22dy"
path="res://.godot/imported/ChatGPT-Bild 27. Sept. 2026, 19_46_53-3.png-7e556551f7b3efde4bca6a6f73eb8860.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/ChatGPT-Bild 27. Sept. 2026, 19_46_53-3.png"
dest_files=["res://.godot/imported/ChatGPT-Bild 27. Sept. 2026, 19_46_53-3.png-7e556551f7b3efde4bca6a6f73eb8860.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1

BIN
assets/luneraria/grotto.glb Normal file

Binary file not shown.

View file

@ -0,0 +1,45 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://g2wd2l3wkvb7"
path="res://.godot/imported/grotto.glb-415ef169549c859f990847e218fcf14a.scn"
[deps]
source_file="res://assets/luneraria/grotto.glb"
dest_files=["res://.godot/imported/grotto.glb-415ef169549c859f990847e218fcf14a.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
mesh_library/use_node_names_as_mesh_names=false
array_mesh/deduplicate_surfaces=true
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
gltf/texture_map_mode=1

View file

@ -0,0 +1,18 @@
{
"assets": [
{
"id": "boss_grotto",
"resource_path": "res://assets/luneraria/grotto.glb",
"source": "assets-source/luneraria/grotto.blend",
"triangles": 1888,
"sha256": "92e7a4b4c98ebafb167523fb2816d91e997add201bf223ab6e9c8326bf77daf9",
"materials": [
"ENV_soil",
"ENV_cliff",
"GR_Shadow",
"GR_Crystal",
"ENV_limestone"
]
}
]
}

Binary file not shown.

View file

@ -0,0 +1,45 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://bdygvqh1lru4a"
path="res://.godot/imported/map_props.glb-ee8d7dcb8819cf2777bc63c68f327e1d.scn"
[deps]
source_file="res://assets/luneraria/map_props.glb"
dest_files=["res://.godot/imported/map_props.glb-ee8d7dcb8819cf2777bc63c68f327e1d.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
mesh_library/use_node_names_as_mesh_names=false
array_mesh/deduplicate_surfaces=true
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
gltf/texture_map_mode=1

View file

@ -0,0 +1,27 @@
{
"assets": [
{
"id": "map_props",
"resource_path": "res://assets/luneraria/map_props.glb",
"source": "assets-source/luneraria/map_props.blend",
"props": {
"Pole": 74,
"Deck_0": 1088,
"Curb": 12,
"Block_0": 700,
"Trunk.003": 1270,
"Trunk.004": 1270,
"Trunk.005": 1270,
"Trunk": 358,
"Trunk.001": 358,
"Trunk.002": 358,
"Rock": 140,
"Rock.001": 140,
"Rock.002": 140,
"Shrub": 80,
"Shrub.001": 80
},
"sha256": "d997546df3d537dae03ff5d8925f0af9457c3cc5ee63b6f20018baa994e8ff9d"
}
]
}

Binary file not shown.

View file

@ -0,0 +1,45 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://vlty7tphfgnx"
path="res://.godot/imported/p_biest.glb-8e5e6e1fa11c916fb2bef94a33cda329.scn"
[deps]
source_file="res://assets/luneraria/peoples/p_biest.glb"
dest_files=["res://.godot/imported/p_biest.glb-8e5e6e1fa11c916fb2bef94a33cda329.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
mesh_library/use_node_names_as_mesh_names=false
array_mesh/deduplicate_surfaces=true
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
gltf/texture_map_mode=1

Binary file not shown.

View file

@ -0,0 +1,45 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://pw4ristm1wtt"
path="res://.godot/imported/p_elf.glb-d890d18acc6c79e79d4b8598982a53f2.scn"
[deps]
source_file="res://assets/luneraria/peoples/p_elf.glb"
dest_files=["res://.godot/imported/p_elf.glb-d890d18acc6c79e79d4b8598982a53f2.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
mesh_library/use_node_names_as_mesh_names=false
array_mesh/deduplicate_surfaces=true
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
gltf/texture_map_mode=1

Binary file not shown.

View file

@ -0,0 +1,45 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://biourdwo5b8lq"
path="res://.godot/imported/p_fee.glb-84ebd920f33b8ac4eaaa887665f7685d.scn"
[deps]
source_file="res://assets/luneraria/peoples/p_fee.glb"
dest_files=["res://.godot/imported/p_fee.glb-84ebd920f33b8ac4eaaa887665f7685d.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
mesh_library/use_node_names_as_mesh_names=false
array_mesh/deduplicate_surfaces=true
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
gltf/texture_map_mode=1

Binary file not shown.

View file

@ -0,0 +1,45 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://irnp3eyfc180"
path="res://.godot/imported/p_mensch.glb-39d67a910d0ef6afd640de8d0f7f2922.scn"
[deps]
source_file="res://assets/luneraria/peoples/p_mensch.glb"
dest_files=["res://.godot/imported/p_mensch.glb-39d67a910d0ef6afd640de8d0f7f2922.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
mesh_library/use_node_names_as_mesh_names=false
array_mesh/deduplicate_surfaces=true
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
gltf/texture_map_mode=1

Binary file not shown.

View file

@ -0,0 +1,45 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://ch346bo30gv7u"
path="res://.godot/imported/p_ork.glb-2fc1770fe3cbd6425271bd6ed96455c7.scn"
[deps]
source_file="res://assets/luneraria/peoples/p_ork.glb"
dest_files=["res://.godot/imported/p_ork.glb-2fc1770fe3cbd6425271bd6ed96455c7.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
mesh_library/use_node_names_as_mesh_names=false
array_mesh/deduplicate_surfaces=true
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
gltf/texture_map_mode=1

Binary file not shown.

View file

@ -0,0 +1,45 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://cgmg6jsstbn0w"
path="res://.godot/imported/p_troll.glb-1e8b28d4cdbb22a6f14981dad1c5577b.scn"
[deps]
source_file="res://assets/luneraria/peoples/p_troll.glb"
dest_files=["res://.godot/imported/p_troll.glb-1e8b28d4cdbb22a6f14981dad1c5577b.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
mesh_library/use_node_names_as_mesh_names=false
array_mesh/deduplicate_surfaces=true
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
gltf/texture_map_mode=1

Binary file not shown.

View file

@ -0,0 +1,45 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://cl7r1ec10jtmg"
path="res://.godot/imported/p_zwerg.glb-e90f71dd7991679fd78ec39f4df7c5be.scn"
[deps]
source_file="res://assets/luneraria/peoples/p_zwerg.glb"
dest_files=["res://.godot/imported/p_zwerg.glb-e90f71dd7991679fd78ec39f4df7c5be.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
mesh_library/use_node_names_as_mesh_names=false
array_mesh/deduplicate_surfaces=true
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
gltf/texture_map_mode=1

View file

@ -0,0 +1,333 @@
{
"assets": [
{
"id": "p_mensch",
"name": "Ritter",
"people": "Menschen",
"klasse": "Ritter",
"rolle": "front",
"resource_path": "res://assets/luneraria/peoples/p_mensch.glb",
"triangles": 17480,
"mesh_parts": 1,
"parts": [
"MenschBody"
],
"skeletal": true,
"bones": 22,
"clips": [
"idle",
"start",
"walk",
"stop",
"attack_a",
"attack_b",
"hit",
"ability",
"death"
],
"attack_cycle": [
"attack_a",
"attack_b",
"attack_a",
"ability"
],
"contact_seconds": 0.9,
"bounds_godot": {
"min": [
-0.40699902176856995,
-0.01069701835513115,
-0.27477124333381653
],
"max": [
0.42016029357910156,
1.9657350778579712,
0.6130658388137817
]
},
"sha256": "218b6d020a0d70e6693e6b61c9eb2d3090e8365b23afe675268a9d0084bacd73",
"source": "assets-source/luneraria/peoples/p_mensch.blend"
},
{
"id": "p_elf",
"name": "Waldl\u00e4uferin",
"people": "Elfen",
"klasse": "Waldl\u00e4uferin",
"rolle": "ranged",
"resource_path": "res://assets/luneraria/peoples/p_elf.glb",
"triangles": 18502,
"mesh_parts": 1,
"parts": [
"ElfBody"
],
"skeletal": true,
"bones": 24,
"clips": [
"idle",
"start",
"walk",
"stop",
"attack_a",
"attack_b",
"hit",
"ability",
"death"
],
"attack_cycle": [
"attack_a",
"attack_b",
"attack_a",
"ability"
],
"contact_seconds": 0.9,
"bounds_godot": {
"min": [
-0.274183988571167,
0.00043252474279142916,
-0.5287083387374878
],
"max": [
0.29548221826553345,
2.230403423309326,
1.1226369142532349
]
},
"sha256": "9c155a1cf3868185cbabdc4ee26fd23e53006d4a093c2f2d623eb42317da02a6",
"source": "assets-source/luneraria/peoples/p_elf.blend"
},
{
"id": "p_zwerg",
"name": "Schildtr\u00e4ger",
"people": "Zwerge",
"klasse": "Schildtr\u00e4ger",
"rolle": "front",
"resource_path": "res://assets/luneraria/peoples/p_zwerg.glb",
"triangles": 20214,
"mesh_parts": 1,
"parts": [
"ZwergBody"
],
"skeletal": true,
"bones": 22,
"clips": [
"idle",
"start",
"walk",
"stop",
"attack_a",
"attack_b",
"hit",
"ability",
"death"
],
"attack_cycle": [
"attack_a",
"attack_b",
"attack_a",
"ability"
],
"contact_seconds": 0.9,
"bounds_godot": {
"min": [
-0.3604458272457123,
-0.018512045964598656,
-0.17344079911708832
],
"max": [
0.4053660035133362,
1.3439915180206299,
0.4210769236087799
]
},
"sha256": "bb6706d13f8cf28d3142a919f66d69192167577d769a5de781a4ee7ac0b42559",
"source": "assets-source/luneraria/peoples/p_zwerg.blend"
},
{
"id": "p_troll",
"name": "Steinhauer",
"people": "Trolle",
"klasse": "Steinhauer",
"rolle": "splash",
"resource_path": "res://assets/luneraria/peoples/p_troll.glb",
"triangles": 14686,
"mesh_parts": 1,
"parts": [
"TrollBody"
],
"skeletal": true,
"bones": 22,
"clips": [
"idle",
"start",
"walk",
"stop",
"attack_a",
"attack_b",
"hit",
"ability",
"death"
],
"attack_cycle": [
"attack_a",
"attack_b",
"attack_a",
"ability"
],
"contact_seconds": 0.9,
"bounds_godot": {
"min": [
-0.5339592695236206,
-0.03151940554380417,
-0.2999359965324402
],
"max": [
0.49503999948501587,
2.198559045791626,
1.255454421043396
]
},
"sha256": "1c250068745bd7305569fd7b4db2848b86fea486dc5af139798939141597c259",
"source": "assets-source/luneraria/peoples/p_troll.blend"
},
{
"id": "p_fee",
"name": "Lichtheilerin",
"people": "Feen",
"klasse": "Lichtheilerin",
"rolle": "support",
"resource_path": "res://assets/luneraria/peoples/p_fee.glb",
"triangles": 16744,
"mesh_parts": 1,
"parts": [
"FeeBody"
],
"skeletal": true,
"bones": 24,
"clips": [
"idle",
"start",
"walk",
"stop",
"attack_a",
"attack_b",
"hit",
"ability",
"death"
],
"attack_cycle": [
"attack_a",
"attack_b",
"attack_a",
"ability"
],
"contact_seconds": 0.9,
"bounds_godot": {
"min": [
-0.35030224919319153,
0.31140172481536865,
-0.1565617471933365
],
"max": [
0.35030224919319153,
1.6979674100875854,
0.2717953622341156
]
},
"sha256": "f084d1f922d1a19d421b38e8ed68e944582dd0a1c4768e310773eca084f3b236",
"source": "assets-source/luneraria/peoples/p_fee.blend"
},
{
"id": "p_ork",
"name": "Kriegsh\u00e4uptling",
"people": "Orks",
"klasse": "Kriegsh\u00e4uptling",
"rolle": "melee",
"resource_path": "res://assets/luneraria/peoples/p_ork.glb",
"triangles": 15890,
"mesh_parts": 1,
"parts": [
"OrkBody"
],
"skeletal": true,
"bones": 22,
"clips": [
"idle",
"start",
"walk",
"stop",
"attack_a",
"attack_b",
"hit",
"ability",
"death"
],
"attack_cycle": [
"attack_a",
"attack_b",
"attack_a",
"ability"
],
"contact_seconds": 0.9,
"bounds_godot": {
"min": [
-0.5301840901374817,
-0.0254998579621315,
-0.18939527869224548
],
"max": [
0.5364153385162354,
2.0665111541748047,
0.760603666305542
]
},
"sha256": "6d9552f8c4e5fdfbba2a9a933a861c4a59b7dc3ab406e7070a7e2553e7b39bff",
"source": "assets-source/luneraria/peoples/p_ork.blend"
},
{
"id": "p_biest",
"name": "Klauenj\u00e4ger",
"people": "Biestmenschen",
"klasse": "Klauenj\u00e4ger",
"rolle": "melee",
"resource_path": "res://assets/luneraria/peoples/p_biest.glb",
"triangles": 16858,
"mesh_parts": 1,
"parts": [
"BiestBody"
],
"skeletal": true,
"bones": 24,
"clips": [
"idle",
"start",
"walk",
"stop",
"attack_a",
"attack_b",
"hit",
"ability",
"death"
],
"attack_cycle": [
"attack_a",
"attack_b",
"attack_a",
"ability"
],
"contact_seconds": 0.9,
"bounds_godot": {
"min": [
-0.4814721643924713,
0.009468089789152145,
-0.5130493640899658
],
"max": [
0.4814721643924713,
2.072967052459717,
0.6682941913604736
]
},
"sha256": "d990197e3030d34455f8ad070a1f047cccea03d1dcccc81950a4b4b692a5f2d1",
"source": "assets-source/luneraria/peoples/p_biest.blend"
}
]
}

BIN
assets/luneraria/tower.glb Normal file

Binary file not shown.

View file

@ -0,0 +1,45 @@
[remap]
importer="scene"
importer_version=1
type="PackedScene"
uid="uid://fqj2uftgmgig"
path="res://.godot/imported/tower.glb-367b5fb07a0c6d34829755c1c6ffa2f0.scn"
[deps]
source_file="res://assets/luneraria/tower.glb"
dest_files=["res://.godot/imported/tower.glb-367b5fb07a0c6d34829755c1c6ffa2f0.scn"]
[params]
nodes/root_type=""
nodes/root_name=""
nodes/root_script=null
mesh_library/use_node_names_as_mesh_names=false
array_mesh/deduplicate_surfaces=true
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_name_suffixes=true
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
materials/extract=0
materials/extract_format=0
materials/extract_path=""
_subresources={}
gltf/naming_version=2
gltf/embedded_image_handling=1
gltf/texture_map_mode=1

View file

@ -0,0 +1,19 @@
{
"assets": [
{
"id": "lane_tower",
"resource_path": "res://assets/luneraria/tower.glb",
"source": "assets-source/luneraria/tower.blend",
"triangles": 648,
"height": 5.95,
"sha256": "52de81cafa3712efb78413d28d16fe112330d27af0e66a8c9b31fde7d6dd1442",
"materials": [
"ENV_cliff",
"ENV_paving",
"ENV_limestone",
"ENV_brass",
"TW_Crystal"
]
}
]
}

View file

@ -0,0 +1,17 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" width="512" height="512">
<defs>
<linearGradient id="stone" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#1f2733"/><stop offset="1" stop-color="#0c1016"/></linearGradient>
<linearGradient id="moon" x1="0" y1="0" x2="1" y2="1"><stop offset="0" stop-color="#fbf1d4"/><stop offset="1" stop-color="#c9a562"/></linearGradient>
<linearGradient id="gem" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#d8f0ff"/><stop offset=".55" stop-color="#5aa8ff"/><stop offset="1" stop-color="#27508a"/></linearGradient>
<radialGradient id="halo" cx=".5" cy=".46" r=".5"><stop offset="0" stop-color="#9fd0ff" stop-opacity=".45"/><stop offset="1" stop-color="#9fd0ff" stop-opacity="0"/></radialGradient>
</defs>
<circle cx="256" cy="256" r="232" fill="url(#stone)" stroke="#c99a58" stroke-width="12"/>
<circle cx="256" cy="256" r="204" fill="none" stroke="#6b4f2e" stroke-width="4"/>
<circle cx="256" cy="236" r="160" fill="url(#halo)"/>
<path d="M300 70 A186 186 0 1 0 300 442 A214 214 0 0 1 300 70 Z" fill="url(#moon)" stroke="#fff4d8" stroke-width="4"/>
<path d="M256 150 L300 236 L256 360 L212 236 Z" fill="url(#gem)" stroke="#eaf6ff" stroke-width="5" stroke-linejoin="round"/>
<path d="M212 236 L256 262 L300 236 M256 150 L256 360" fill="none" stroke="#eaf6ff" stroke-width="2.5" opacity=".8"/>
<path d="M256 70 l10 22 l22 10 l-22 10 l-10 22 l-10 -22 l-22 -10 l22 -10 Z" fill="#f6e2b0"/>
<path d="M120 400 Q190 372 256 400 T392 400" fill="none" stroke="#6fd3b4" stroke-width="7" stroke-linecap="round" opacity=".85"/>
<path d="M150 432 L178 404 M362 432 L334 404" stroke="#c99a58" stroke-width="6" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -0,0 +1,43 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://dq03newjr6neo"
path="res://.godot/imported/luneraria.svg-12b34911b283c50c0fc77d4b9fb43e87.ctex"
metadata={
"vram_texture": false
}
[deps]
source_file="res://assets/ui/emblems/luneraria.svg"
dest_files=["res://.godot/imported/luneraria.svg-12b34911b283c50c0fc77d4b9fb43e87.ctex"]
[params]
compress/mode=0
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=1
svg/scale=1.0
editor/scale_with_editor_scale=false
editor/convert_colors_with_editor_theme=false

View file

@ -0,0 +1,6 @@
[gd_resource type="Resource" script_class="LuneBalance" load_steps=2 format=3]
[ext_resource type="Script" path="res://scripts/luneraria/core/lune_balance.gd" id="1"]
[resource]
script = ExtResource("1")

View file

@ -0,0 +1,92 @@
# Installation, Start, Steuerung und Export
## Start aus dem Quellprojekt (Fedora 44)
```bash
cd saumwacht-godot
godot --path . # Champions of Luneraria (Hauptszene scenes/luneraria.tscn)
godot --path . -- --legacy # archivierter Saumwacht-Modus (scenes/main.tscn)
```
Voraussetzung ist Godot 4.7.2. Blender wird nur zum Neuerzeugen von Assets gebraucht, nicht zum Spielen.
## Eine Partie
1. **Neue Partie** → Bund (Glutpakt oder Wurzelbund) und deine Lane (Top, Mid, Bottom) wählen → **Partie beginnen**. Du spielst in Team Blau (Südwest) mit zwei Bot-Verbündeten gegen drei Bots in Team Rot.
2. **Planung:** Truppen kaufen (Knöpfe oder 1–5), mit T/M/B einer Lane zuweisen, mit ↑ aufwerten, mit × zurückgeben. Käufe aus derselben Planung werden voll erstattet, ältere mit 75 %. **Startaufstellung** kauft Front, Nahkampf und Fernkampf. **Bereit** (Leertaste) übernimmt alle Befehle verbindlich.
3. **Kampf:** Er läuft automatisch. Deine Truppen rücken mit der eigenen Monsterwelle über ihre Lane vor. Leertaste oder P pausiert, Tempo 1×/2×/4×.
4. **Abrechnung:** Getötete Monster und Gebäude bringen Gold, dazu kommt das Rundeneinkommen. Deine Truppen kehren geheilt zurück. Türme und Kerne behalten ihren Schaden.
5. Die Partie endet erst, wenn ein Ancient fällt. Es gibt kein Rundenlimit.
## Steuerung
| Eingabe | Wirkung |
| --- | --- |
| 1–5 | Truppe kaufen (Planung) |
| Leertaste | Bereit / im Kampf Pause |
| P | Pause |
| WASD, Pfeiltasten, mittlere Maustaste ziehen | Kamera verschieben |
| Mausrad | Zoom |
| Tab / Lane-Knöpfe links | Kamera auf die nächste Lane |
| O | Kartenübersicht |
| Minimap klicken/ziehen | Kamera springt dorthin |
| F1 | Optionen und Steuerung |
| M / F11 | Ton / Vollbild |
## Prüfungen
```bash
godot --headless --path . res://tests/luneraria/test_lune_core.tscn # Regeln M1 (91, inkl. Spiegeltest)
godot --headless --path . res://tests/luneraria/test_lune_m2.tscn # Regeln M2 (73)
godot --headless --path . res://tests/luneraria/test_lune_net.tscn # Netzwerkprotokoll (43)
python3 tools/luneraria/net_rounds.py # Host + 2 Gäste, 5 Runden, Wiedereinstieg
godot --path . --resolution 1280x720 res://tools/luneraria/verify_m1.tscn # echte Bedienung, volle Partie (38)
godot --path . --resolution 1920x1080 res://tools/luneraria/verify_m2.tscn # Helden, Items, Grotte, Boss (15)
godot --headless --path . res://tools/luneraria/sim_match.tscn -- --seeds=1,2,3 --limit=80
godot --headless --path . res://tools/luneraria/balance_run.tscn -- --compositions=20 --limit=60 --out=res://docs/luneraria/logs/m3/balance.json
godot --headless --path . res://tools/luneraria/balance_run.tscn -- --matchups --limit=60 --out=res://docs/luneraria/logs/m3/matchups.json
python3 tools/luneraria/analyze_balance.py docs/luneraria/logs/m3/balance.json
godot --headless --path . res://tools/luneraria/stress_run.tscn # Wellen bis 10^6, Langlauf
godot --path . res://tools/luneraria/people_lineup.tscn -- --out=res://docs/luneraria/screenshots
godot --path . --resolution 1920x1080 res://tools/luneraria/lune_benchmark.tscn
godot --path . --resolution 1920x1080 res://tools/luneraria/lune_review.tscn # Aufnahmen
# Legacy (Saumwacht) unverändert:
godot --headless --path . res://tests/test_runner.tscn
godot --headless --path . -- --smoke-test
godot --headless --path . -- --faction-test
godot --headless --path . -- --expedition-test
```
`verify_m1` und `verify_m2` brauchen ein Fenster (echte Maus-/Tastaturereignisse), nicht `--headless`.
## Assets neu erzeugen
```bash
ALSOFT_DRIVERS=null blender -b --python-exit-code 1 --python assets-source/luneraria/build_tower.py
ALSOFT_DRIVERS=null blender -b --python-exit-code 1 --python assets-source/luneraria/build_grotto.py
ALSOFT_DRIVERS=null blender -b --python-exit-code 1 --python assets-source/luneraria/build_map_props.py
ALSOFT_DRIVERS=null blender -b --python-exit-code 1 --python assets-source/luneraria/build_peoples.py
python3 tools/luneraria/build_asset_matrix.py
godot --headless --editor --import --path .
```
## Export
```bash
python3 tools/luneraria/build_luneraria_demo.py --check-only
python3 tools/luneraria/build_luneraria_demo.py
```
Erzeugt `../dist/luneraria/ChampionsOfLuneraria-Linux-x64-<ver>.zip` und `…-Windows-x64-<ver>.zip` (Produktname Champions of Luneraria). Alte Saumwacht-Releases unter `../dist/Saumwacht-*.zip` bleiben unverändert. `tools/build_demo.py` baut weiterhin Saumwacht 0.15.0.
**Netzwerk (experimentell, pausiert):** Im Setup *Hosten* oder *Beitreten* mit Anzeigename, Adresse und Port (Standard 24571). Der Host ist autoritativ und rechnet den Kampf; Gäste sehen ihn über Momentaufnahmen. Freie Plätze sind Bots, bis jemand beitritt. Tempo, Pause und Revanche bestimmt der Host. „Bereit“ ist verbindlich. Ein Disconnect wird von einem Bot übernommen und ist kein Kernverlust; wer sich mit demselben Anzeigenamen wieder verbindet, bekommt seinen Platz zurück. Getestet sind Protokollchecks und ein lokaler Loopback über fünf Runden mit Wiedereinstieg – keine verifizierte Internetpartie auf sechs Rechnern, kein OpenGL- und kein Windows-11-Test. Kein Matchmaking, keine Hostmigration.
```bash
godot --headless --path . res://tools/luneraria/net_pair.tscn -- --role=host --rounds=4 &
godot --headless --path . res://tools/luneraria/net_pair.tscn -- --role=client --name=Gast --rounds=4
# mit Fenster und Gast-Aufnahmen: ohne --headless, Host mit --steps=0, Gast mit --shots=<Ordner>
```
## Benutzereinstellungen
Der neue Projektname legt einen neuen Benutzerordner an (`~/.local/share/godot/app_userdata/Champions of Luneraria`). Beim ersten Start übernimmt `Preferences` einmalig die `preferences.cfg` aus „Saumwacht · Glutpakt“. Die alte Datei bleibt unverändert; fehlt sie, gelten die Standardwerte.

View file

@ -0,0 +1,28 @@
# 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.
8. **M3-Nachtrag:** Bots planen Items/Champions vor der Armee und helfen deterministisch (kein geteilter `rng`), damit Spiegelmatches bit-symmetrisch bleiben. Kartenprops stammen aus `map_props.glb` (MultiMesh, ENV_*-Kluftshader). Volksrepräsentanten `p_*` sind Asset- und Datenziele ohne Matcheinbindung; siehe [VOELKER.md](VOELKER.md). Balance- und Stresswerkzeuge liegen unter `tools/luneraria/`.
9. **M4-Nachtrag:** `scripts/luneraria/net/lune_session.gd` (Node) kapselt ENet Host/Join. Der Host besitzt `LuneMatch`, rechnet jeden Kampf und sendet je Sitz den öffentlichen Zustand plus nur dessen eigenen Entwurf (`rpc_state`), zum Kampfbeginn die Einheitenliste (`rpc_combat_begin`), danach Momentaufnahmen (`rpc_frame`, 20/s, `PackedFloat32Array`). Der Client baut daraus ein reines Anzeige-Abbild von `LuneCombat`, auf dem nie eine Regel läuft. Clients senden nur Befehle (`rpc_hello` mit Protokollversion, `rpc_submit`, `rpc_ready`). Peer-ID → Sitz, ein Peer ein Sitz. Disconnect: Bot-Übernahme, kein Kernverlust, keine Hostmigration; Wiedereinstieg über den Anzeigenamen. `LuneGame` spricht offline direkt mit `LuneMatch`, im Netzspiel ausschließlich mit der Sitzung. Protokolltests `tests/luneraria/test_lune_net.gd`, Mehr-Runden-Loopback `tools/luneraria/net_pair.tscn` mit Treiber `tools/luneraria/net_rounds.py`. Export-Presets `Luneraria-Linux` / `Luneraria-Windows`, Demo-Bau `tools/luneraria/build_luneraria_demo.py`.

View file

@ -0,0 +1,80 @@
# Asset-Wiederverwendungsmatrix
Erzeugt mit `python3 tools/luneraria/build_asset_matrix.py` aus den Laufzeitmanifesten. „Volk/Bund“ ist die Datenzuordnung. Die sieben Zielvölker haben ab M3 **eigene Repräsentantenmodelle** (`p_*`); sie sind noch nicht als spielbare Fraktionen eingebunden. 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 | neutraler Grottenboss und entsandter Boss (×1,7) | Riftbrut / Grotte | HD, starre Teile | umgesetzt (M2) |
| 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; zusätzlich Held „Funkenlanze“ (vorläufig; Goldring, Namensschild, ×1,25) | Glutkonstrukt / Glutpakt | gewichtetes Skelett (phase18) | umgesetzt (M2) |
| 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 | Angriffschampion Schutz (Rostschieber) | Söldner | HD, starre Teile | umgesetzt (M2) |
| s03 | Dunstsänger | res://assets/models/unit_s03.glb | assets-source/phase4/blend/s03.blend | starre Gelenkgruppen | 7832 | Söldner | Angriffschampion Druck auf Unterstützer (Dunstsänger) | Söldner | HD, starre Teile | umgesetzt (M2) |
| s04 | Keilbrecher | res://assets/models/unit_s04.glb | assets-source/phase4/blend/s04.blend | starre Gelenkgruppen | 10458 | Söldner | Angriffschampion Belagerung (Keilbrecher) | Söldner | HD, starre Teile | umgesetzt (M2) |
| 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; zusätzlich Held „Ringborke“ (vorläufig) | Wurzelwesen / Wurzelbund | gewichtetes Skelett (phase11) | umgesetzt (M2) |
| 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; zusätzlich Held „Quellgeflecht“ (vorläufig) | Wurzelwesen / Wurzelbund | gewichtetes Skelett (phase16) | umgesetzt (M2) |
| 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 |
| p_biest | Klauenjäger | res://assets/luneraria/peoples/p_biest.glb | assets-source/luneraria/peoples/p_biest.blend | 24 Knochen · 9 Clips | 16858 | — | Volksrepräsentant Nahkampf (Klauenjäger) | Biestmenschen | gewichtetes Skelett (peoples, LP_*-Materialien) | neu (M3) |
| p_elf | Waldläuferin | res://assets/luneraria/peoples/p_elf.glb | assets-source/luneraria/peoples/p_elf.blend | 24 Knochen · 9 Clips | 18502 | — | Volksrepräsentant Fernkampf (Waldläuferin) | Elfen | gewichtetes Skelett (peoples, LP_*-Materialien) | neu (M3) |
| p_fee | Lichtheilerin | res://assets/luneraria/peoples/p_fee.glb | assets-source/luneraria/peoples/p_fee.blend | 24 Knochen · 9 Clips | 16744 | — | Volksrepräsentant Unterstützung (Lichtheilerin) | Feen | gewichtetes Skelett (peoples, LP_*-Materialien) | neu (M3) |
| p_mensch | Ritter | res://assets/luneraria/peoples/p_mensch.glb | assets-source/luneraria/peoples/p_mensch.blend | 22 Knochen · 9 Clips | 17480 | — | Volksrepräsentant Front (Ritter) | Menschen | gewichtetes Skelett (peoples, LP_*-Materialien) | neu (M3) |
| p_ork | Kriegshäuptling | res://assets/luneraria/peoples/p_ork.glb | assets-source/luneraria/peoples/p_ork.blend | 22 Knochen · 9 Clips | 15890 | — | Volksrepräsentant Nahkampf (Kriegshäuptling) | Orks | gewichtetes Skelett (peoples, LP_*-Materialien) | neu (M3) |
| p_troll | Steinhauer | res://assets/luneraria/peoples/p_troll.glb | assets-source/luneraria/peoples/p_troll.blend | 22 Knochen · 9 Clips | 14686 | — | Volksrepräsentant Fläche (Steinhauer) | Trolle | gewichtetes Skelett (peoples, LP_*-Materialien) | neu (M3) |
| p_zwerg | Schildträger | res://assets/luneraria/peoples/p_zwerg.glb | assets-source/luneraria/peoples/p_zwerg.blend | 22 Knochen · 9 Clips | 20214 | — | Volksrepräsentant Front (Schildträger) | Zwerge | gewichtetes Skelett (peoples, LP_*-Materialien) | neu (M3) |
| 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 |
| 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) |
| map_props | Kartenprops (Bäume, Felsen, Büsche, Brücken, Bordsteine, Klippen, Banner) | res://assets/luneraria/map_props.glb | assets-source/luneraria/build_map_props.py | statisch | MultiMesh | — | 3D-Kartendekoration der drei Lanes, Brücken über die Kluft, Klampterrahmen | — | ENV_*-Kluftshader | neu (M3) |
| 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 |
| compare-rock / compare-tree | Fels, Baum (Alt) | res://assets/compare/ | assets-source/phase14/build_compare_props.py | statisch | 360 / 852 | Vergleichsszene | ab M3 durch map_props ersetzt | — | einfach | abgelöst (M3) |

119
docs/luneraria/BALANCE.md Normal file
View file

@ -0,0 +1,119 @@
# Balancekonfiguration und Auswertung
Alle Werte stehen in `scripts/luneraria/core/lune_balance.gd`. Die aktive Standardinstanz ist `data/luneraria/balance_default.tres`, die derzeit alle Klassenwerte übernimmt.
## Startwerte (M1) und Begründung
| Wert | Stand | Begründung |
| --- | ---: | --- |
| Startgold | 220 | Wie im Saumwacht-Einstieg; reicht für die Startaufstellung (180/185) plus Rest. |
| Grundeinkommen | 70 + 4 je Runde, Obergrenze +80 | Etwa eine Truppe pro Runde; der Zuwachs hält mit den Wellen mit, ohne endlos zu steigen. |
| Versorgungsgrenze | 12 Truppen je Spieler | Begrenzt aktive Einheiten; später investiert man in Aufwertungen statt in Masse. |
| Verkauf | 75 % (in derselben Planung 100 %) | Aus Saumwacht übernommen; die Rücknahme im selben Planungsschritt ist ein Bedienkomfort. |
| Wellen | 3 Monster je Lane und Team, +1 alle 3 Runden, max. 7 | Pro Runde höchstens 42 Monster auf der Karte. |
| Wellenstärke | HP ×(1 + 0,11·r + 0,0015·r²), Schaden ×(1 + 0,07·r + 0,0015·r²) | Stetig und quadratisch leicht zunehmend, ohne Obergrenze; bei r = 10⁶ endlich (Test). |
| Monsterkopfgeld | Katalogwert ×2 ×(1 + 0,04·r) | Monster sind die Hauptgoldquelle; wächst langsamer als die Stärke. |
| Türme | außen 6.000 / innen 7.500 / Basis 9.000 HP, 90 / 120 Schaden, Reichweite 8,6 m | Eine Welle allein kommt nicht durch. Die Reichweite liegt über der größten Einheitenreichweite (6,5 m + Gebäuderadius), sonst wären Türme ungestraft auszuschießen. |
| Ancient | 12.000 HP | Braucht eine voll durchbrochene Basis und mehrere Runden Druck. |
| Gebäudekopfgeld | 150 / 210 / 270 | Belohnt Durchbrüche gleichmäßig im Team. |
| Kampffenster | 180 s | In 91 Bot-Kampfphasen (8 Seeds) dauerte die längste 130 s, im Mittel 68 s; keine erreichte das Fenster. Es greift nur bei festgefahrenen Kämpfen. |
| Marschtempo | Katalogtempo ×1,6 | Eine Lane (~95 m) ist in ~30 s durchquerbar, damit Angriffe die Basis innerhalb eines Kampfes erreichen. |
## Auswertung M1 (reine Bot-Partien, 1 Mensch = Bot)
[sim-8-seeds.json](logs/m1/sim-8-seeds.json): acht Seeds mit gemischten Bünden und fünf Bot-Profilen (ausgewogen, verteidigung, druck, fernkampf, teamhilfe). Die Ergebnisse stehen in [STATUS.md](STATUS.md).
- Alle acht Partien enden regulär durch einen Ancient-Fall, nach 8–15 Wellen (Blau 5, Rot 3). 83 Kampfphasen enden, weil alle beweglichen Einheiten gefallen sind, 8 durch einen Kernfall, keine durch das Zeitfenster.
- **Exakter Spiegel** (sechsmal derselbe Bund, dasselbe deterministische Profil): Die Partie bleibt über 60 Runden Bit für Bit symmetrisch. Sie wird aber nicht entschieden, weil sich gleich starke Armeen und Wellen gegenseitig aufheben. Das ist der erwartbare Grenzfall identischen Spiels. Kein Timer oder Gebäudeverfall erzwingt ein Ende. Heldenwachstum, Angriffschampions und Boss (M2) liefern die beabsichtigten Asymmetrien.
- Kleine Botstichproben belegen keine ausgewogene Spielerfahrung. Die Bünde und Profile sind noch nicht getrennt ausgewertet (M3).
## M2 · Helden, Items, Champions, Boss
Werte in `scripts/luneraria/core/lune_content.gd`, Begründungen im [Regelwerk](REGELWERK.md) §4.
| Wert | Stand | Begründung |
| --- | ---: | --- |
| Heldenpreis | 260 | Mehr als das Startgold: Ein Held kommt frühestens in Runde 2 und ersetzt keine frühe Armee. |
| Heldenwerte | Ringborke 2.200 HP / 34, Quellgeflecht 1.300 / 28, Funkenlanze 950 / 46 | Aus den Truppenwerten der Aufwertungsstufe mit etwa 1,4-fachem Leben abgeleitet. |
| Stufen | 10; +9 % Leben, +7 % Schaden je Stufe | In 12 Bot-Partien erreichen Helden bis Partieende Stufe 3–6, Talentstufe 2 also erst spät. |
| Items | 180–260 | Ein Item kostet etwa eine aufgewertete Truppe. |
| Champions | 150/150/180, Leben ×2,2, Schaden ×1,6 der Söldnerwerte, Kopfgeld 70 + 6·r | Einmalige Welle etwa zum Preis von zwei Truppen. |
| Grottenboss | 4.200 HP × Wellenstärke, 55 Schaden | Nur mit großer Expedition schaffbar (Szenarien unten). |
| Entsandter Boss | 5.200 HP × Wellenstärke, 70 Schaden, Kopfgeld 520 + 60·r | Das Kopfgeld liegt deutlich über einem Turm (150–270) und über einem Monster (~10–30). |
### Bot-Serie (12 Seeds, [sim-12-seeds.json](logs/m2/sim-12-seeds.json))
- Alle 12 Partien enden regulär nach 7–10 Wellen (Blau 7, Rot 5), keine Kampfphase im Zeitfenster; die längste dauerte 128 s.
- Bossversuche: 36 gescheitert, 11 erobert, 7 entsandt (die übrigen Partien endeten vorher).
- **Befund:** Der Bossjäger-Bot versucht es in jeder Partie ab Runde 5, scheitert bis Runde 7 und siegt in Runde 8. Das Muster ist zu gleichförmig; der Bot schätzt seine Stärke nicht ein (M3).
- Siegquote des Teams mit dem jeweiligen Profil: fernkampf 11/12, druck 9/12, teamhilfe 7/12, verteidigung 5/12, bossjagd 3/12, ausgewogen 1/12. **Vorsicht:** Jede Partie enthält jedes Profil genau einmal, und die Paare fernkampf/ausgewogen, druck/bossjagd und verteidigung/teamhilfe stehen immer in verschiedenen Teams. Die Zahlen sind daher nicht unabhängig; eine saubere Auswertung mit variierter Teamzusammensetzung folgt in M3.
### Bossstrategie im direkten Vergleich ([boss_scenarios.gd](../../tools/luneraria/boss_scenarios.gd))
Identischer Stand nach Runde 7, danach vier Runden mit gewöhnlichen Bots ohne Bossjagd. Wert = eingesetzter Armeewert (Blau / Rot). ΔHP und ΔGold jeweils Blau / Rot über die vier Runden.
| Seed | Szenario | Wert | Grotte | Mid-Türme Blau verloren | Bossrunde | Gebäudeschaden Rot durch Boss | Kopfgeld für Rot | ΔHP Gebäude | ΔGold | Sieger |
| --- | --- | ---: | --- | ---: | ---: | ---: | ---: | ---: | ---: | --- |
| 3 | kein_versuch | 0 | none | 0 | — | 0 | — | -1.436 / -50.521 | 284 / -235 | Blau |
| 3 | gescheitert | 210 | failed | 0 | — | 0 | — | -1.754 / -50.257 | 284 / -235 | Blau |
| 3 | erfolgreich | 1000 | won | 2 | 9 | 33.219 | 1060 | -15.023 / -35.384 | 543 / 794 | Blau |
| 3 | umkaempft | 1000 / 995 | failed | 0 | — | 0 | — | -1.435 / -51.276 | 494 / -185 | Blau |
| 3 | ueberinvestition | 2495 | won | 2 | 9 | 25.392 | 1060 | -68.295 / -25.392 | -250 / 437 | Rot |
| 7 | kein_versuch | 0 | none | 0 | — | 0 | — | -9.222 / -5.573 | 174 / 139 | offen |
| 7 | gescheitert | 180 | failed | 0 | — | 0 | — | -9.548 / -4.660 | 174 / 139 | offen |
| 7 | erfolgreich | 930 | won | 2 | 9 | 22.793 | 1060 | -67.737 / -22.793 | -302 / 319 | Rot |
| 7 | umkaempft | 930 / 865 | failed | 0 | — | 0 | — | -9.855 / -4.660 | 174 / 139 | offen |
| 7 | ueberinvestition | 2580 | won | 2 | 9 | 20.685 | 1060 | -67.737 / -20.685 | -320 / 277 | Rot |
**Deutung:**
- Eine gescheiterte kleine Expedition kostet wenig.
- In der umkämpften Grotte reiben sich beide Expeditionen auf; kein Team erhält den Anspruch.
- Eine erfolgreiche Eroberung zieht die Mid-Lane so weit ab, dass Blau dort beide Türme verliert. Der Boss richtet danach 20.000–33.000 HP Gebäudeschaden an, stirbt und bringt Rot 1.060 Gold.
- Überinvestition kippt die Partie in beiden Seeds zu Rot, und auch die normale Eroberung verliert bei Seed 7.
- Die Bossstrategie ist damit riskant und nicht dominant. Nach diesen Stichproben wiegt der Boss die geschwächte Lane eher zu wenig auf: ein Balancepunkt für M3 (z. B. mehr Bosswirkung oder schwächerer Grottenboss). Es gibt weder eine kostenlose Sofortrückkehr noch eine Doppelbelohnung (Tests); das Goldrisiko steht im Kopfgeld offen angekündigt.
## M3 · Qualität und Balance
### Geänderte Werte und Bot-Regeln
| Thema | Vorher | Nachher | Begründung |
| --- | --- | --- | --- |
| Keilbrecher Belagerungsfaktor | ×2,5 | ×1,8 | Belagerungs-Champion dominierte Gebäude zu stark. |
| Grottenboss HP | 4.200 | 3.600 | Expeditionen scheiterten zu oft vor Runde 8. |
| Entsandter Boss | 5.200 HP / 70 Schaden | 6.800 HP / 88 Schaden | Die Belohnung wiegt die geschwächte Lane nun besser auf. |
| Bot Item-/Championkauf | nach der Armee | **vor** der Armee (Reserve 110 bzw. 40 Gold) | Die Roster-Schleife verzehrte die gesamte Reserve; Helden blieben ohne Items, Champions wurden nie gebucht (`test_lune_m2` bots_m2). |
| Bot Lane-Hilfe | geteilter Zufall (`rng`) | deterministisch `(Runde + Laneindex) % Periode` | Der geteilte Zufall brach die Spiegelsymmetrie (`test_lune_core` mirror_fairness). |
| Bossjagd-Schwelle | Lanchester 2,0 | 1,35 (+1,25 je Fehlversuch) | Frühere Versuche erst ab Runde 8; Ziel: sinnvolle Risikoabschätzung. |
| Expeditionstärke | 6 Truppen + Held | 5 Truppen + Held | Weniger Lane-Abzug bei der Jagd. |
| Ausgewogen-Held | ab Runde 3 | ab Runde 4 | Reines „ausgewogen“ schlug in Spiegel-Duellen alle Spezialprofile. |
| Verteidigung | kein Champion, 40 % Front | Champion Schutz, 15 % Lane-Hilfe | Reines Verteidigen gewann zu selten (14 %). |
### Bot-Serie (20 Zusammensetzungen × Seitentausch, [balance-step4.json](logs/m3/balance-step4.json))
- 40 Partien, alle regulär durch Ancient-Fall nach 9–14 Wellen (Median 11). Kein Testlimit, kein Zeitfenster-Kampf.
- **Seitentausch 20/20:** dieselbe Zusammensetzung gewinnt beide Male. Keine Seitenbevorzugung.
- Profile (Siege/Einsätze): verteidigung 64 %, ausgewogen 58 %, fernkampf 54 %, druck 53 %, teamhilfe 46 %, bossjagd 24 %.
- Bünde: Glutpakt 55 % / Wurzelbund 45 %.
- Boss: 20 Erfolge, 8 Entsendungen. Heldenstufe am Ende (Median) 4.
### Reine Profil-Duelle ([matchups-step4.json](logs/m3/matchups-step4.json))
15 Paare × 2 Seiten = 30 Partien. Seitentausch 15/15 konsistent.
| Profil | Siege | Duellkette |
| --- | ---: | --- |
| ausgewogen | 10/10 | schlägt alle Spezialprofile |
| verteidigung | 8/10 | verliert nur gegen ausgewogen |
| teamhilfe | 6/10 | schlägt druck, fernkampf, bossjagd |
| fernkampf | 4/10 | schlägt druck, bossjagd |
| druck | 2/10 | schlägt bossjagd |
| bossjagd | 0/10 | verliert gegen jedes Spezialprofil |
**Befund:** In reinen 3-gleichen Spiegeln ergibt sich eine lineare Kette, kein Zyklus. In gemischten Sechser-Aufstellungen (die eigentliche Partie) liegen alle Profile im Band 24–64 %. Kleine Botstichproben beweisen keine ausgewogene Spielerfahrung; menschliche Spieltests bleiben offen. Bossjagd ist als reines Teamprofil schwach, in Mischteams aber spielbar (24 %).
### Belastungstests ([stress.json](logs/m3/stress.json))
- 17/17 Prüfungen, keine Fehler.
- Wellenindizes 10², 10³, 10⁴, 10⁶ enden regulär über Kernfall; Monster-HP endlich (bei 10⁶ ≈ 7,2·10¹¹), kein NaN, kein Arrayfehler, mobile Einheiten gedeckelt (120).
- Langlauf 200 Runden: „Testlimit erreicht / Partie nicht abgeschlossen“ (korrekt, kein Unentschieden). Objektzahl stabil (1706 → 1853), Speicher 52 → 53 MB. Rundenzeit 1,05 s → 2,01 s (wachsende Wellenmenge, kein Leck).

View file

@ -0,0 +1,87 @@
# QA-Matrix
Testumgebung aller Läufe: Fedora Linux 44 (KDE Plasma, XWayland), AMD Ryzen 5 5600X, NVIDIA GeForce RTX 5060 Ti (Vulkan 1.4.351), Godot 4.7.2 (Fedora-Build), Forward+. **Windows 11 ist nicht geprüft:** Es gibt keine Windows-Hardware in dieser Umgebung.
Legende: ✅ automatisiert geprüft · 👁 in echten Aufnahmen visuell geprüft · ⏳ offen (Meilenstein) · — nicht zutreffend.
## Abnahmepunkte aus dem Masterauftrag (§15)
| # | Anforderung | Stand | Beleg |
| --- | --- | --- | --- |
| 1 | Zwei Teams, sechs Eigentümerrollen, drei Lanes, 16 Türme, 2 Kerne | ✅ M1 | `test_lune_core` · structure |
| 2 | Turmreihenfolge, Basisschutz, Kernsieg; gleichzeitige Kernzerstörung ohne Reihenfolgevorteil | ✅ M1 | `test_lune_core` · gates, victory_and_draw, mirror_fairness |
| 3 | Fortsetzung nach Welle 20, hohe Indizes ohne Final-Wave-Logik, Fehler oder Objektwachstum | ✅ M1 (Wellen bis 10⁶, Runde 21; Menge ≤ 7 je Lane) | `test_lune_core` · endless_waves |
| 4 | Gold exakt einmal, Besitzprüfung, keine Reset-/Despawn-/Beschwörungsfarmen | ✅ M1 (Monster, Gebäude, Truppentod ohne Kopfgeld); Beschwörungen gibt es noch nicht | `test_lune_core` · rewards, commands |
| 5 | Heilung und Persistenz nach der Runde; neue Partie ohne übernommene Werte | ✅ M1 Truppen · ✅ M2 Helden (XP, Stufe, Talente, Items bleiben; Rückkehr geheilt, auch nach dem Tod; neue Partie ohne Held) | `test_lune_core` · persistence; `test_lune_m2` · xp_and_persistence |
| 6 | Truppen ohne Heldeninventar; Talente, drei Itemplätze, Stapeln | ✅ M2 | `test_lune_m2` · heroes_and_items |
| 7 | Verstärkungskauf n → Einsatz n+1, richtige Lane und richtiges Team | ✅ M2 (öffentliche Ankündigung, keine Verschiebung nach Übernahme, Verbrauch nach n+1, Kopfgeld, Belagerungsziel) | `test_lune_m2` · champions |
| 8 | Grottenabzug schwächt die Lane für die ganze Kampfphase | ✅ M2 | `test_lune_m2` · grotto_withdrawal_and_contest |
| 9 | Beide Expeditionen, Scheitern, Erfolg, genau eine Entsendung, Kopfgeld, Verfügbarkeit ab Runde 10 | ✅ M2 (Sieg in Runde 6 → Einsatz 7 → gesperrt 7–9 → frei ab 10; wiederholtes Signal ohne Wirkung) | `test_lune_m2` · boss_*; Szenarien `boss_scenarios` |
| 10 | Laneverlust, Phasenende, Pause, Neustart, Rückkehr ins Menü | ✅ M1/M2 | `verify_m1`, `test_lune_core` · victory (timeout), `test_lune_m2` · lane_loss |
| 11 | Bots ohne Sonderressourcen und ohne Sicht auf private Befehle | ✅ Bots nutzen dieselben Befehle ohne Sondergold; sie lesen nur den eigenen Entwurf, veröffentlichte Gebäudestände, den öffentlichen Bosszustand und Ankündigungen (Code-Durchsicht, `LuneBot`) | `test_lune_core` · commands; `test_lune_m2` · bots_m2 |
| 12 | Netzwerk: doppelte, veraltete und fremde Befehle; synchroner Phasenwechsel, Disconnect | ✅ Protokoll + Mehr-Runden-Loopback (5 Runden, Wiedereinstieg) | `test_lune_net` 43/43 · `net_rounds.py` 12/12 |
## M3 · Qualität und Balance
| Nachweis | Stand | Beleg |
| --- | --- | --- |
| Mehrere Seeds und gemischte Teamzusammensetzungen, Seitentausch | ✅ 20 Zusammensetzungen × 2 Seiten, 20/20 konsistent | `balance_run` · `logs/m3/balance-step4.json` |
| Gegenstrategien / Profil-Duelle | ✅ 15 Paare, 15/15 Seitenfair; lineare Kette (Befund) | `balance_run --matchups` · `matchups-step4.json` |
| Hohe Wellenindizes und Langlauf | ✅ 10²–10⁶ ohne Fehler; 200 Runden = Testlimit, keine Objektflut | `stress_run` · `logs/m3/stress.json` |
| Bot kauft Items und Angriffschampions | ✅ Regression `test_lune_m2` bots_m2 (war 0 Buchungen) | `test_lune_m2` 73/73 |
| Spiegelsymmetrie mit erweiterten Bots | ✅ glutpakt/wurzelbund je 12 Runden bit-symmetrisch | `test_lune_core` mirror_fairness |
| Karte mit echten Props (Bäume, Felsen, Brücken, Bordsteine, Banner) | 👁 Übersicht, Kampf Top/Mid, Planung | `screenshots/overview-*.png`, `combat-*.png` |
| Sieben Volksrepräsentanten (Modelle + Clips) | 👁 Ruhe, Kontakt, Laufen, Fähigkeit, Tod, Nahaufnahme | `screenshots/peoples-*.png`, `people_lineup` |
| Assetmatrix mit `p_*` und map_props | ✅ 74 Zeilen | `python3 tools/luneraria/build_asset_matrix.py` |
## Echte Anwendung (M1)
`tools/luneraria/verify_m1.tscn`, 38 Prüfungen mit echten Maus- und Tastaturereignissen:
- Titel und Projektname, Lane- und Bundwahl per Klick, Start.
- Eingabeschutz während der Überblendung: Mausrad, 1 und Leertaste erreichen das Spiel nicht.
- Kauf, Startaufstellung, Rückgängig, Laneeinteilung und die Leertaste übernehmen die Planung; im Kampf wird nichts gekauft.
- Truppen kämpfen auf ihrer Lane; Pause hält die Kampfuhr an; Tempo schaltet um.
- Abrechnung und Rückkehr der Truppen.
- HUD innerhalb des Fensters bei 1280 × 720 und 1920 × 1080.
- Menü, neue Partie ohne Altlasten, eine vollständige Partie bis zum Ancient-Fall mit Live-Aufnahme der Belagerung, Ergebnisbildschirm, Rematch.
- Vier Menü-/Partiewechsel ohne wachsende Objektzahl.
Nur die Kampfuhr wird dabei beschleunigt (mehr feste Schritte pro Bild), die Regeln bleiben unverändert.
## Echte Anwendung (M2)
`tools/luneraria/verify_m2.tscn`, 15 Prüfungen. Teil 1 bedient das HUD mit echten Klicks: Reiter, Held anwerben, Held-Lane, Truppe kaufen und in die Grotte schicken, Verstärkungsseite, Rückgängig. Teil 2 spielt eine gewöhnliche Partie ohne Sondergold bis zum Ancient-Fall; der menschliche Platz wird vom Bot-Planer über dieselben Befehle gespielt. Aufgenommen: Held mit Items, Held im Feld, Grottenkampf, Ankündigung, Bossentsendung.
Weitere M2-Belege: `test_lune_m2` 73/73, darunter der Nachweis, dass die alte Arbeiter- und Funkenökonomie nicht mitläuft (Arbeitsannahme E). M2-App nach M3-Botfix erneut 15/15 (bzw. 14/14, wenn die Partie vor einer Bossentsendung endet).
## M4 · Netzwerk und Pakete
| Nachweis | Stand | Beleg |
| --- | --- | --- |
| Sechs Slots, Host/Join Direktverbindung, Bots für freie Plätze | ✅ | `LuneSession`, Menü „Hosten/Beitreten“ |
| Fremder Sitz / fremde UID / veralteter Befehl / Doppelkauf | ✅ | `test_lune_net` 24/24 |
| Disconnect ≠ Kernzerstörung, Bot-Übernahme | ✅ | `test_lune_net` disconnect |
| Zwei-Prozess-Loopback (lokal, nicht Internet) | ✅ 5 Runden, echtes `LuneGame` auf beiden Seiten | `tools/luneraria/net_rounds.py` · `logs/m4/net-rounds.json` |
| Gleicher öffentlicher Zustand nach jeder Abrechnung | ✅ 5/5 Prüfsummen gleich | `net-rounds.json` |
| Client rechnet keinen Kampf, zeigt Host-Momentaufnahmen | ✅ 0 lokale Schritte · 👁 zwei Fenster | `net-rounds.json`, `screenshots/m4/` |
| Nur eigener Planungsentwurf beim Client | ✅ | `test_lune_net` ready_commits_and_privacy |
| Linux-Export + entpackbare Demo | ✅ 0.16.0-m4 | `../dist/luneraria/ChampionsOfLuneraria-Linux-x64-0.16.0-m4.zip` |
| Windows-Export + entpackbare Demo | ✅ Dateien erzeugt | `../dist/luneraria/ChampionsOfLuneraria-Windows-x64-0.16.0-m4.zip` |
| Nativer Windows-11-Lauf | ⏳ blockiert | keine Windows-Hardware; Export oder Wine wäre kein Nativtest |
| Mehr-Runden-Phasensync im Zwei-Fenster-Spiel | ✅ headless 5 Runden · 👁 zwei Fenster, 2 Runden Echtzeit | `net-rounds.json`, `screenshots/m4/` |
| Wiedereinstieg als Mensch (neben Bot-Übernahme) | ✅ derselbe Sitz über den Anzeigenamen, auch im Kampf | `test_lune_net` hello_and_rejoin · `net-rounds.json` |
| Kompatibilitätsmodus (OpenGL) | ⏳ offen, M4 pausiert | — |
| Sechs Menschen auf mehreren Rechnern / Internet | ⏳ nicht geprüft | nur lokaler Loopback |
## Legacy-Regressionen (Saumwacht)
Unverändert über die Umleitung der alten QA-Schalter: 209 · 33 · 463 · 206 · 1.665 · Menü 20A 68. Logs: `logs/m0-baseline/`, `logs/m1/legacy-*.log`.
## Plattformen
| Plattform | Stand |
| --- | --- |
| Fedora 44, Forward+ | ✅ Quellprojekt (Tests, Aufnahmen, Leistung, Balance, Stress) |
| Fedora 44, Kompatibilitätsmodus (OpenGL) | ⏳ offen (für Saumwacht 0.15.0 geprüft, für Luneraria noch nicht; M4 pausiert) |
| Windows 11 | ⏳ nicht geprüft (keine Hardware). Ein exportiertes Paket oder ein Wine-Lauf wäre kein nativer Test. |

View file

@ -0,0 +1,72 @@
# 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. |
## 4 · Detailentscheidungen M2 (anpassbar, Werte in `scripts/luneraria/core/lune_content.gd`)
| Thema | Entscheidung |
| --- | --- |
| Helden | Drei Helden aus vorhandenen Skelettmodellen, als **vorläufige Rollenzuordnung**: Ringborke (Front/Schutz), Quellgeflecht (Heilung), Funkenlanze (Fernkampf). Jeder Spieler kann einen aktiven Helden anwerben, unabhängig vom Bund, für 260 Gold. Damit ist ein Held frühestens ab Runde 2 erschwinglich. Ein Held aus derselben Planung lässt sich voll erstatten, danach bleibt er bis Partieende. |
| Erfahrung | Nicht an den letzten Treffer gebunden. Jeder Held, der an einer Kampfphase teilnimmt, erhält 40 + 6 × Runde. Jeder gegnerische Tod im Umkreis von 12 m (Gebäude 14 m) wird zu gleichen Teilen auf alle Helden in der Nähe verteilt: Monster 12 + Runde, Champion 30 + 2 × Runde, Boss 220, Truppe/Held 15, Gebäude 60. So erhalten auch Heiler in der zweiten Reihe Erfahrung. Stufen 1–10, je Stufe +9 % Leben und +7 % Schaden. |
| Talente | Auf Stufe 3 und 6 je eine endgültige Wahl zwischen zwei Richtungen: Eigen- gegen Gruppenschutz oder Einzelziel gegen Fläche, je Held eigene Optionen. |
| Items | Sechs dauerhafte Items, drei Plätze. Jedes Item höchstens einmal pro Held. Kristallsplitter setzt Heldenstufe 3 voraus. Verkauf bringt 50 %, ein Kauf aus derselben Planung wird voll erstattet. Boni verschiedener Items addieren sich; die Schadensminderung ist auf 35 %, das Angriffstempo auf +50 % begrenzt. |
| Reihenfolge im Kampf | Primärtreffer → Grundfähigkeit → Talent → Item-Effekt. Item- und Talenteffekte lösen keine weiteren Treffer-Effekte aus, Heilungen lösen nichts aus; dadurch sind Schleifen ausgeschlossen. Normale Truppen haben kein Inventar. |
| Angriffschampions | Rostschieber (Schutz, 150 Gold), Dunstsänger (Druck auf Unterstützer, 150), Keilbrecher (Belagerung, ×1,8 gegen Gebäude, 180). Ein Kauf in Runde n wird für Runde n+1 auf eine Lane gebucht, höchstens zwei Buchungen pro Runde. Bis zum Ende der Planung lässt sich eine Buchung voll erstatten, danach ist sie weder verschiebbar noch erstattbar. Buchungen werden zu Beginn von n+1 öffentlich angekündigt. Der Champion kämpft einmal und verschwindet. Die Stärke skaliert mit der Einsatzrunde, das Kopfgeld beträgt 70 + 6 × Runde und wird wie Monstergold verteilt. |
| Grotte · Zuweisung | Die Zuweisung „G“ gilt für genau eine Runde. Die Einheiten fehlen ihrer Lane die ganze Kampfphase, starten an festen, gespiegelten Grotteneingängen und kehren bei der Abrechnung auf die Lane ihres Besitzers zurück. In Sperrrunden ist die Zuweisung abgelehnt. |
| Grotte · Kampf | Der neutrale Boss ruht, solange Expeditionen beider Teams leben; die Expeditionen kämpfen zuerst gegeneinander. Erst wenn höchstens ein Team übrig ist, erwacht der Boss mit vollen Lebenspunkten. Tod aller Expeditionen oder Ablauf des Zeitfensters: kein Anspruch. Der nächste Versuch trifft einen vollständig neuen Boss. Todesfälle in der Grotte bringen kein Gold. |
| Anspruch | Genau ein Anspruch pro Sieg; ein wiederholtes Abrechnungssignal ändert nichts. Ziel-Lane: die Lane des beitragenden Spielers mit dem höchsten eingesetzten Armeewert in der Grotte (Held einschließlich Items); bei Gleichstand der niedrigere Slot. Dieser Spieler kann die Lane in der folgenden Planung ändern; ohne Eingabe gilt seine eigene Lane. |
| Entsendung | Automatisch in der folgenden Runde, für das siegreiche Team, gegen den gegnerischen Ancient. Gebäudesperren gelten unverändert. Stärke und Kopfgeld (520 + 60 × Runde) richten sich nach der Einsatzrunde. Das Kopfgeld geht bei seinem Tod gleichmäßig an das verteidigende Team. Überlebt er, verschwindet er ohne Belohnung. |
| Sperre | `next_available_round = Siegrunde + 4`. Beispiel: Sieg in Runde 6, Einsatz in Runde 7, gesperrt in 7–9, bekämpfbar ab Runde 10. Die Sperre hängt am Grottensieg, nicht am Tod des entsandten Bosses. |

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

@ -0,0 +1,129 @@
# STATUS · Champions of Luneraria
Stand: 28. September 2026 · Branches `luneraria/m1` (M0/M1), `luneraria/m2`, `luneraria/m3`, `luneraria/m4` · Luneraria-Demo 0.16.0-m4.
| Meilenstein | Zustand |
| --- | --- |
| M0 · Migration vorbereiten | **abgeschlossen** |
| M1 · Spielbarer Drei-Lane-Kern | **abgeschlossen** |
| M2 · Mechanikdurchlauf | **abgeschlossen** |
| M3 · Qualität und Balance | **abgeschlossen** (siehe unten) |
| M4 · Netzwerk und Pakete | **pausiert** (Host/Join, Mehr-Runden-Loopback, Wiedereinstieg; offen: OpenGL, Windows 11) |
| G1–G6 · Karte, Grafik, HUD | **als Nächstes** (Auftrag vom 28. September 2026) |
## M1 · abgeschlossen
**Implementiert**
- Autoritative Partie mit sechs Spielerrollen (ein Mensch, fünf Bots), drei spiegelsymmetrischen Lanes, 16 Türmen und zwei Ancients.
- Gebäudesperren für jede Schadensquelle.
- Vorrückende Armeen mit eigenen, unbegrenzt wachsenden Monsterwellen.
- Planung mit atomarer Übernahme, Besitz-IDs, Einmal-Belohnung mit verlustfreier Restverteilung, Rückkehr und Heilung der Truppen, persistenter Gebäudeschaden.
- Partieende nur durch Kernfall; bei gleichzeitigem Kernfall Unentschieden.
- Bots über dieselbe Befehlsschnittstelle.
- 3D-Karte aus Bestandsassets plus neuem Turm (Blender), Einheiten mit den vorhandenen Modellen und Animationen.
- HUD mit Planung, Laneübersicht, Minimap, Ergebnis; Titelmenü auf Champions of Luneraria umgestellt.
- Übernahme der alten Benutzereinstellungen. Legacy-Modus bleibt unter `--legacy` und den alten QA-Schaltern erreichbar.
**Automatisiert geprüft**
- Regeln 91/91, darunter der exakte Spiegeltest über 12 Runden.
- Echte Anwendung 38/38, mit einer vollständigen Partie bis zum Ancient-Fall.
- Acht Bot-Seeds enden regulär nach 8–15 Wellen.
- Legacy 209 · 33 · 463 · 206 · 1.665 · 68 unverändert.
**Visuell geprüft:** Menü, Planung (720p/1080p), Kampf Mid/Top, Übersicht, Belagerung eines Ancients, Ergebnis (`screenshots/`).
**Leistung** (RTX 5060 Ti, Ryzen 5 5600X, Forward+, VSync aus, 2×): 114 bewegliche Einheiten und 18 Gebäude. 1080p: 7,6 ms/Bild (p95 10,5 ms), GPU 3,1 ms, ~3.170 Draw Calls, Simulationsschritt 2,4 ms (vor der Optimierung 15,8 ms). Es gibt keine allgemeine FPS-Zusage.
**Ungetestet:** Windows 11, Kompatibilitätsmodus für Luneraria, menschliche Spieltests.
## M2 · abgeschlossen
**Implementiert**
- Drei Helden (vorläufige Rollen der vorhandenen Skelettmodelle Ringborke, Quellgeflecht, Funkenlanze) mit Erfahrung aus Gefechtsnähe und Rundenteilnahme, Stufen, je zwei Talentstufen mit echten Aufgabenwechseln und drei Itemplätzen für sechs Items.
- Drei Angriffschampions (Schutz, Druck auf Unterstützer, Belagerung) mit Buchung n → n+1 und öffentlicher Ankündigung.
- Bossgrotte mit ruhendem Boss bei zwei Expeditionen, Scheitern, Eroberung, genau einer Entsendung für das siegreiche Team gegen den gegnerischen Ancient, hohem Kopfgeld für die Verteidiger und Sperre ab `Sieg + 4`.
- Wahl der Ziel-Lane durch den beitragenden Spieler; Grottenaufträge gelten für genau eine Runde.
- HUD mit Reitern Truppen / Held / Verstärkung, Heldenporträt aus dem echten Modell, Grottenstatus, Ankündigungen und Minimap-Grotte.
- Neues Grotten-Asset (Blender). Bots mit Helden, Items, Talenten, Champions und Bossjagd.
**Automatisiert geprüft**
- Regeln M2 73/73 und M1 91/91 (mit Spiegeltest).
- Echte Anwendung M2 15/15 und M1 38/38.
- 12 Bot-Seeds enden regulär nach 7–10 Wellen.
- Szenariovergleich der Bossstrategie (2 Seeds × 5 Varianten).
**Visuell geprüft:** Held mit Items im HUD, Held im Feld, Grottenkampf, Ankündigung, Bossentsendung (`screenshots/m2-*`).
**Ungetestet:** menschliche Spieltests der neuen Systeme, Windows 11.
## M3 · abgeschlossen
**Implementiert**
- Bot-Priorisierung: Items und Angriffschampions werden **vor** der Armee geplant (Reserve), damit Helden Ausrüstung erhalten und Champions überhaupt gebucht werden. Regression `bots_m2` gefixt.
- Deterministische Lane-Hilfe der Bots statt geteiltem Zufall — erhält die exakte Spiegelsymmetrie.
- Bossbalance: Grottenboss 3.600 HP, entsandter Boss 6.800 HP / 88 Schaden, Belagerungsfaktor 1,8. Bossjäger senden 5 Truppen + Held und bewerten Expeditionen per öffentlicher Lanchester-Schätzung.
- Profilbalance: verteidigung erhält Champion und Teamhilfe; ausgewogen startet den Held eine Runde später; teamhilfe hilft seltener.
- 3D-Kartenvisuals: `map_props.glb` (Blender-Generator) mit Kiefern, Eichen, Felsen, Büschen, Brücken über die Kluft, Bordsteinen, Klamprahmen und Team-Bannern an den Ancients. Alte Compare-Props abgelöst.
- Sieben Volksrepräsentanten (`p_mensch` … `p_biest`) mit gewichtetem Skelett, 9 Clips, LP_*-Materialien, Audit und Sichtprüfung. Entwicklungsplan in [VOELKER.md](VOELKER.md). Noch nicht spielbar im Match.
- Balancewerkzeuge: `balance_run` (Zusammensetzungen, Seitentausch, reine Duelle), `analyze_balance.py`, `stress_run`, `people_lineup`.
**Automatisiert geprüft**
- Regeln: Core 91/91 (inkl. Spiegel) und M2 73/73 nach den Bot-Änderungen.
- Echte Anwendung: M1 38/38 und M2 15/15 (nach Fix der Item-Ausrüstung).
- Balance 40 Partien: Seitentausch 20/20, Profile 24–64 %, Bünde 55/45, alle regulär nach 9–14 Wellen.
- Profil-Duelle 30 Partien: Seitentausch 15/15.
- Stress 17/17: Wellen bis 10⁶, Langlauf 200 Runden als „Testlimit erreicht“.
- Legacy unverändert.
**Visuell geprüft:** Kartenübersicht mit Props und Brücken, Kampf Top/Mid, Planung mit Helden-Items (Funkenlanze Stufe 3, Glutklinge), sieben Völker in Ruhe/Kontakt/Laufen/Fähigkeit/Tod und Nahaufnahme (`screenshots/peoples-*`).
**Bekannte Befunde**
- Reine 3-gleichen Profil-Duelle ergeben eine lineare Kette (ausgewogen > verteidigung > teamhilfe > fernkampf > druck > bossjagd), keinen Zyklus. In gemischten Teams sind alle Profile im Band 24–64 %.
- Bossjagd als reines Teamprofil 0/10, in Mischteams 24 %. Die Risikoabschätzung ist besser geworden, die Jagd bleibt teuer.
## Bekannt
- Ein exakt gespiegeltes Match bleibt unentschieden, wird also nicht beendet (Testlimit). Das ist der erwartbare Grenzfall identischen Spiels; Asymmetrie kommt aus Helden, Champions und Boss.
- Die Monsterwellen sind nur über Teamring und Lichtfarbe dem Team zugeordnet.
- Beim Beenden meldet Godot eine noch belegte Ressource (Hintergrundladen der Menübühne); ohne Folgen im Spiel.
- Die sieben Volksrepräsentanten sind noch nicht spielbar (Daten und Modelle liegen vor, Plan in VOELKER.md).
## Blockiert
- Nativer Windows-11-Test: keine Windows-Hardware.
- Kompatibilitätsmodus (OpenGL) für Luneraria: erst mit M4-Exporten gezielt prüfen.
## M4 · pausiert
Auf Entscheidung des Auftraggebers ruht M4 seit dem 28. September 2026 zugunsten der Grafik- und Kartenphasen G1–G6. Während dieser Phasen gibt es keine Netzwerk-Featurearbeit; die Netzwerktests laufen nur als Regression mit.
**Implementiert**
- `LuneSession` (autoritativ): Der Host besitzt `LuneMatch` und rechnet jeden Kampf. Clients senden nur Absichten (Kauf, Aufwertung, Zuweisung, Rückgängig, Bereit) und zeigen den Kampf aus Host-Momentaufnahmen (20/s, interpoliert). Ein Client rechnet keinen einzigen Kampfschritt.
- Das echte Spiel (`LuneGame`) läuft vollständig über die Sitzung: Bereitschaft, Startaufstellung und Rückgängig gehen an den Host; Bots planen nur freie Sitze und höchstens einmal pro Runde; Tempo und Pause bestimmt der Host.
- „Bereit“ ist verbindlich: Danach nimmt der Host für diesen Sitz keine Planungsbefehle mehr an.
- Privatsphäre: Jeder Sitz erhält nur den eigenen Planungsentwurf, von allen anderen den übernommenen Stand.
- Verbindungsaufbau mit Protokollprüfung (`PROTOCOL` 4). Sitzplätze an Peer-IDs, ein Peer ein Sitz.
- Disconnect → Bot-Übernahme, nie Kernverlust, keine Hostmigration. **Wiedereinstieg:** Wer sich mit demselben Anzeigenamen erneut verbindet, erhält seinen alten Sitz zurück, auch mitten im Kampf (vollständiger Abgleich inkl. laufender Einheiten). Neue Gäste nehmen reservierte Sitze nur als letzte Möglichkeit.
- Übernahme eines Bot-Sitzes in der Planung: Der noch nicht übernommene Bot-Entwurf verfällt, das Gold gehört dem Menschen.
- Revanche startet der Host; Gäste starten automatisch neu. Verbindungsverlust führt den Gast mit Meldung ins Menü.
- Host/Join im Setup-Menü (Direktverbindung, Standardport 24571), freie Plätze als Bots.
- Exporte `Luneraria-Linux` und `Luneraria-Windows`, entpackbare Demos 0.16.0-m4 unter `../dist/luneraria/` (Stand vor dem Sitzungsumbau; nicht neu gebaut).
**Automatisiert geprüft**
- Protokoll `test_lune_net` 43/43: fremder Sitz, fremde UID, veralteter Befehl, Doppelkauf, Disconnect ohne Kernschaden, Protokollversion, Wiedereinstieg auf denselben Sitz, Reservierung gegen neue Gäste, Übernahme mit verfallendem Bot-Entwurf, Bot plant einmal pro Runde, Bereit ist verbindlich, nur eigener Entwurf im Zustand.
- Mehr-Runden-Loopback mit drei Prozessen (`python3 tools/luneraria/net_rounds.py`) 12/12: Host und Gast spielen fünf Runden mit dem echten `LuneGame`; nach jeder Abrechnung ist die Prüfsumme des öffentlichen Zustands gleich; der Gast rechnet 0 Kampfschritte und erhält Momentaufnahmen; Gast A trennt im Kampf der Runde 2, der Bot übernimmt, Gast B kehrt mit demselben Namen auf Slot 1 zurück (`logs/m4/net-rounds.json`).
- Regressionen nach dem Umbau: Regeln 91/91, M2 73/73, echte Anwendung M1 38/38 und M2 14/14 (Partie endete vor einer Bossentsendung).
**Visuell geprüft:** Host und Gast in zwei Fenstern mit Grafik, Kampf in Echtzeit. Gastsicht in Planung und Kampf von Runde 1 und 2 (`screenshots/m4/`). Runde 1: 872, Runde 2: 730 empfangene Momentaufnahmen, Prüfsummen gleich.
**Offen (eingefroren)**
- Kompatibilitätsmodus (OpenGL) für Luneraria.
- Nativer Windows-11-Test (blockiert: keine Hardware).
- Neubau der Demo-Pakete mit der umgebauten Sitzung.
- Partie mit sechs Menschen auf mehreren Rechnern bzw. über das Internet (bisher nur lokaler Loopback).
- Wiedereinstieg ist nur an den Anzeigenamen gebunden (keine Konten); Übertragungsrate bei vielen Gästen über das Internet ist nicht gemessen.
- Öffentliches Matchmaking, Accounts, Ranglisten: außerhalb des Umfangs.
## Nächster Schritt
Phasen G1–G6 (Karte, Ancients/Türme/Grotte, HUD, Umgebung, Licht/Material, Einheitendarstellung) nach dem Grafikauftrag vom 28. September 2026. M4 ruht; beim Wiederaufnehmen zuerst OpenGL-Sichtprüfung, Windows-11-Protokoll auf fremder Hardware und Neubau der Demos.

40
docs/luneraria/VOELKER.md Normal file
View file

@ -0,0 +1,40 @@
# Völker und Repräsentanten
Stand: 28. September 2026 (M3). Trennung der Daten: **Volk** · **Bund** · **Klasse** · **Kampfrolle**. Ein Volk ist kein eigener Kampfmechanismus; Rollen kommen aus dem gemeinsamen Truppen-/Heldenkatalog.
## Repräsentanten M3
Jedes der sieben Zielvölker besitzt genau ein eigenständiges Blender-Modell mit gewichtetem Skelett (22–24 Knochen, 9 Clips: idle, start, walk, stop, attack_a, attack_b, hit, ability, death). Erzeugt mit `assets-source/luneraria/build_peoples.py`, exportiert nach `assets/luneraria/peoples/p_*.glb`, Manifest `assets/luneraria/peoples_manifest.json`, Prüfbefund `assets-source/luneraria/peoples-audit.json`.
| Volk | ID | Name | Klasse | Kampfrolle | Besonderheit |
| --- | --- | --- | --- | --- | --- |
| Menschen | `p_mensch` | Ritter | Ritter | Front | Schild + Schwert, Plattenrüstung |
| Elfen | `p_elf` | Waldläuferin | Waldläuferin | Fernkampf | Bogen, leichte Rüstung, spitze Ohren |
| Zwerge | `p_zwerg` | Schildträger | Schildträger | Front | Vollbart, Hornhelm, Rundschild |
| Trolle | `p_troll` | Steinhauer | Steinhauer | Fläche | massive Keule, Felsplatten |
| Feen | `p_fee` | Lichtheilerin | Lichtheilerin | Unterstützung | Flügel, Glüheffekte |
| Orks | `p_ork` | Kriegshäuptling | Kriegshäuptling | Nahkampf | Krallenwaffe, Fell |
| Biestmenschen | `p_biest` | Klauenjäger | Klauenjäger | Nahkampf | Bestienanatomie, Klauen |
Sichtprüfung: `screenshots/peoples-ruhe.png`, `-kontakt`, `-laufen`, `-faehigkeit`, `-tod`, `-detail-a/b`. Werkzeug: `tools/luneraria/people_lineup.tscn`.
## Was noch nicht implementiert ist
- Keine der sieben Figuren ist derzeit **spielbar**. Die laufenden Bünde Glutpakt und Wurzelbund behalten ihre vorhandenen Modelle (Glutkonstrukte, Wurzelwesen).
- Es gibt **keine** Volkswahl im Menü und **keine** volksspezifischen Boni oder Fähigkeiten.
- Die Repräsentanten sind Bestandteil der Assetmatrix und der Entwicklungsplanung, nicht des Matchrosters.
## Entwicklungsplan (nach M4)
1. **Volksdaten in den Katalog:** `people`, `bund`, `klasse`, `rolle` als getrennte Felder in `DataCatalog` / `LuneContent`. Einheitentypen verweisen auf ein Volk, ein Bund bleibt eine spielbare Fraktion mit eigener Aufwertungslinie.
2. **Spielbare Vertretung pro Volk:** je ein normaler Truppentyp und ein Aufwertungsschritt aus den Repräsentanten-Animationen, übernommen in die bestehenden Rollen Front / Nahkampf / Fernkampf / Fläche / Unterstützung. Kein eigenes Kampfsystem pro Volk.
3. **Bündekopplung:** Jeder Bund öffnet eine Teilmenge der Völker (z. B. Wurzelbund: Elfen, Feen, Biestmenschen; Glutpakt: Menschen, Orks, Zwerge, Trolle). Volk formt Startboni und Heldenauswahl, nicht die Grundregeln.
4. **Helden pro Volk:** je ein Held aus dem Repräsentantenmodell mit zwei Talentrichtungen, analog zu Ringborke / Quellgeflecht / Funkenlanze. Vorläufige Bestandshelden bleiben erhalten.
5. **Balancierung über Rollen, nicht Sammelboni:** Volkssynergien sind höchstens eine kleine, sichtbare Verstärkung (z. B. Formationsschild), nie der einzige Hebel.
6. **Inhaltliche Ausweitung:** weitere Klassen pro Volk (Aufwertungen, Champion-Varianten) erst nach stabilen sechs Rollen und laufendem Netzwerkmodus.
## Bestandsschutz
- Ausgearbeitete Modelle (Ringborke, Funkenleser, Basaltbastion, Quellgeflecht, Funkenlanze, Glasballiste, Kernkristalle) bleiben unverändert erhalten.
- Nietenkonvent (`m*`) und Nebelchor (`n*`) bleiben als Reserve in der Assetmatrix und werden nicht als Völker ausgegeben.
- Die LP_*-Materialien der Völkermodelle laufen über denselben Kluft-/Sanctuary-Pfad wie die übrigen Figuren (`asset_library.gd`).

View file

@ -0,0 +1,4 @@
{
"checks": 91,
"failures": []
}

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,10 @@
{
"checks": 38,
"failures": [],
"object_counts": [
3408.0,
3404.0,
3404.0,
3406.0
]
}

View file

@ -0,0 +1,8 @@
Godot Engine v4.7.2.stable.fedora.ed1daf0bf - https://godotengine.org
Vulkan 1.4.351 - Forward+ - Using Device #0: NVIDIA - NVIDIA GeForce RTX 5060 Ti
LUNE_BENCH {"buildings":18,"cpu":"AMD Ryzen 5 5600X 6-Core Processor","draw_calls_mean":3175.86281588448,"engine":"4.7.2-stable (fedora)","frame_ms_mean":7.22646841155234,"frame_ms_p95":10.294,"frames":1108,"gpu":"NVIDIA GeForce RTX 5060 Ti","gpu_ms_mean":2.09521750809914,"mobile_units_at_start":114,"mobile_units_mean_measured":108.60559566787,"renderer":"forward_plus","scene_build_ms":198.002,"sim_speed":"2x (40 steps/s)","sim_step_ms":2.37125,"static_mem_mb":120.703545570374,"video_mem_mb":658.710235595703,"window":"(1280, 720)"}
WARNING: 24 ObjectDB instances were leaked at exit (run with `--verbose` for details).
at: cleanup (core/object/object.cpp:2536)
ERROR: 1 resources still in use at exit (run with --verbose for details).
at: clear (core/io/resource.cpp:822)

View file

@ -0,0 +1,8 @@
Godot Engine v4.7.2.stable.fedora.ed1daf0bf - https://godotengine.org
Vulkan 1.4.351 - Forward+ - Using Device #0: NVIDIA - NVIDIA GeForce RTX 5060 Ti
LUNE_BENCH {"buildings":18,"cpu":"AMD Ryzen 5 5600X 6-Core Processor","draw_calls_mean":3172.21948912015,"engine":"4.7.2-stable (fedora)","frame_ms_mean":7.56970955534533,"frame_ms_p95":10.505,"frames":1057,"gpu":"NVIDIA GeForce RTX 5060 Ti","gpu_ms_mean":3.06410596431599,"mobile_units_at_start":114,"mobile_units_mean_measured":108.593188268685,"renderer":"forward_plus","scene_build_ms":221.213,"sim_speed":"2x (40 steps/s)","sim_step_ms":2.403405,"static_mem_mb":120.915734291077,"video_mem_mb":765.283081054688,"window":"(1920, 1080)"}
WARNING: 24 ObjectDB instances were leaked at exit (run with `--verbose` for details).
at: cleanup (core/object/object.cpp:2536)
ERROR: 1 resources still in use at exit (run with --verbose for details).
at: clear (core/io/resource.cpp:822)

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:97)
[4] _ready (res://scripts/ui/main_menu.gd:69)
[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:97)
[4] _ready (res://scripts/ui/main_menu.gd:69)
[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:97)
[4] _ready (res://scripts/ui/main_menu.gd:69)
[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:97)
[4] _ready (res://scripts/ui/main_menu.gd:69)
[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:97)
[4] _ready (res://scripts/ui/main_menu.gd:69)
[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:97)
[4] _ready (res://scripts/ui/main_menu.gd:69)
[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:97)
[4] _ready (res://scripts/ui/main_menu.gd:69)
[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:97)
[4] _ready (res://scripts/ui/main_menu.gd:69)
[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:97)
[4] _ready (res://scripts/ui/main_menu.gd:69)
[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:97)
[4] _ready (res://scripts/ui/main_menu.gd:69)
[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:97)
[4] _ready (res://scripts/ui/main_menu.gd:69)
[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:97)
[4] _ready (res://scripts/ui/main_menu.gd:69)
[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:97)
[4] _ready (res://scripts/ui/main_menu.gd:69)
[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:97)
[4] _ready (res://scripts/ui/main_menu.gd:69)
[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:97)
[4] _ready (res://scripts/ui/main_menu.gd:69)
[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:97)
[4] _ready (res://scripts/ui/main_menu.gd:69)
[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:97)
[4] _ready (res://scripts/ui/main_menu.gd:69)
[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:97)
[4] _ready (res://scripts/ui/main_menu.gd:69)
[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:97)
[4] _ready (res://scripts/ui/main_menu.gd:69)
[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:97)
[4] _ready (res://scripts/ui/main_menu.gd:69)
[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:97)
[4] _ready (res://scripts/ui/main_menu.gd:69)
[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:97)
[4] _ready (res://scripts/ui/main_menu.gd:69)
[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:97)
[4] _ready (res://scripts/ui/main_menu.gd:69)
[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:97)
[4] _ready (res://scripts/ui/main_menu.gd:69)
[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/Champions of Luneraria/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/Champions of Luneraria/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/Champions of Luneraria/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,5 @@
Godot Engine v4.7.2.stable.fedora.ed1daf0bf - https://godotengine.org
Vulkan 1.4.351 - Forward+ - Using Device #0: NVIDIA - NVIDIA GeForce RTX 5060 Ti
MENU_VERIFY_PASS: 68 checks; 0 failures
EXIT 0

View file

@ -0,0 +1,9 @@
Godot Engine v4.7.2.stable.fedora.ed1daf0bf - https://godotengine.org
LUNE_FULL_MATCH: seed 2 -> Blau after 8 rounds
LUNE_CORE_PASS: 91 checks; 0 failures
WARNING: 10 ObjectDB instances were leaked at exit (run with `--verbose` for details).
at: cleanup (core/object/object.cpp:2536)
ERROR: 1 resources still in use at exit (run with --verbose for details).
at: clear (core/io/resource.cpp:822)
EXIT 0

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,106 @@
Godot Engine v4.7.2.stable.fedora.ed1daf0bf - https://godotengine.org
R01 cleared 46.7s buildings [9, 9] lost [] gold [123, 173, 123, 128, 123, 128]
R02 cleared 53.7s buildings [9, 9] lost [] gold [140, 105, 115, 125, 125, 150]
R03 cleared 55.5s buildings [9, 9] lost [] gold [160, 170, 150, 125, 160, 175]
R04 cleared 69.1s buildings [8, 9] lost ["blau_top_outer"] gold [182, 142, 132, 182, 172, 257]
R05 cleared 74.3s buildings [8, 9] lost [] gold [178, 183, 163, 133, 153, 153]
R06 cleared 70.1s buildings [7, 8] lost ["rot_mid_outer", "blau_top_inner"] gold [203, 223, 183, 243, 253, 203]
R07 cleared 73.3s buildings [7, 7] lost ["rot_mid_inner"] gold [270, 235, 270, 170, 195, 205]
R08 cleared 79.9s buildings [6, 7] lost ["blau_base_1"] gold [245, 185, 165, 275, 245, 280]
R09 core 86.9s buildings [4, 7] lost ["blau_base_2", "blau_ancient"] gold [57, 106, 66, 171, 176, 176]
SIM_MATCH seed=1 Sieg Rot after 9 rounds (Ancient von Blau zerstört.) in 7091 ms
R01 cleared 46.7s buildings [9, 9] lost [] gold [128, 123, 128, 173, 128, 123]
R02 cleared 53.7s buildings [9, 9] lost [] gold [125, 125, 150, 105, 125, 115]
R03 cleared 54.7s buildings [9, 9] lost [] gold [125, 160, 175, 165, 165, 115]
R04 cleared 73.1s buildings [9, 8] lost ["rot_top_outer"] gold [182, 172, 257, 137, 157, 187]
R05 cleared 81.9s buildings [8, 7] lost ["blau_mid_outer", "rot_top_inner"] gold [203, 223, 223, 218, 183, 193]
R06 cleared 67.3s buildings [8, 7] lost [] gold [158, 178, 133, 138, 153, 133]
R07 cleared 74.7s buildings [7, 6] lost ["blau_mid_inner", "rot_base_1"] gold [260, 285, 310, 225, 235, 255]
R08 core 92.9s buildings [7, 3] lost ["rot_bot_outer", "rot_base_2", "rot_ancient"] gold [207, 197, 217, 79, 105, 64]
SIM_MATCH seed=2 Sieg Blau after 8 rounds (Ancient von Rot zerstört.) in 6499 ms
R01 cleared 41.9s buildings [9, 9] lost [] gold [173, 128, 123, 128, 123, 173]
R02 cleared 46.7s buildings [9, 9] lost [] gold [105, 125, 115, 135, 140, 105]
R03 cleared 59.8s buildings [9, 9] lost [] gold [165, 165, 115, 175, 160, 170]
R04 cleared 61.4s buildings [9, 9] lost [] gold [137, 157, 187, 137, 182, 142]
R05 cleared 54.9s buildings [9, 9] lost [] gold [168, 133, 143, 133, 178, 183]
R06 cleared 57.9s buildings [9, 9] lost [] gold [163, 173, 173, 173, 153, 173]
R07 cleared 58.3s buildings [9, 9] lost [] gold [180, 185, 225, 185, 205, 185]
R08 cleared 61.9s buildings [8, 9] lost ["blau_bot_outer"] gold [200, 200, 155, 250, 255, 250]
R09 cleared 71.3s buildings [8, 9] lost [] gold [213, 218, 188, 198, 168, 198]
R10 cleared 74.2s buildings [7, 9] lost ["blau_bot_inner"] gold [215, 250, 215, 300, 265, 300]
R11 cleared 83.7s buildings [5, 9] lost ["blau_top_outer", "blau_base_2"] gold [228, 218, 218, 348, 358, 413]
R12 core 86.9s buildings [1, 9] lost ["blau_mid_outer", "blau_top_inner", "blau_base_1", "blau_ancient"] gold [101, 106, 133, 348, 318, 323]
SIM_MATCH seed=3 Sieg Rot after 12 rounds (Ancient von Blau zerstört.) in 9910 ms
R01 cleared 44.6s buildings [9, 9] lost [] gold [128, 123, 173, 123, 128, 123]
R02 cleared 46.5s buildings [9, 9] lost [] gold [135, 140, 105, 115, 125, 125]
R03 cleared 55.1s buildings [9, 9] lost [] gold [175, 160, 170, 150, 125, 160]
R04 cleared 63.1s buildings [9, 9] lost [] gold [137, 182, 142, 132, 132, 122]
R05 cleared 62.3s buildings [9, 9] lost [] gold [133, 178, 183, 163, 173, 153]
R06 cleared 59.7s buildings [9, 9] lost [] gold [173, 153, 173, 133, 213, 148]
R07 cleared 72.1s buildings [9, 8] lost ["rot_bot_outer"] gold [235, 255, 235, 160, 190, 165]
R08 cleared 62.5s buildings [8, 8] lost ["blau_top_outer"] gold [180, 180, 180, 205, 210, 215]
R09 cleared 74.8s buildings [8, 7] lost ["rot_bot_inner"] gold [268, 263, 268, 238, 223, 178]
R10 cleared 80.3s buildings [8, 7] lost [] gold [235, 205, 235, 210, 265, 185]
R11 core 117.5s buildings [7, 4] lost ["blau_mid_outer", "rot_base_2", "rot_base_1", "rot_ancient"] gold [288, 348, 303, 223, 178, 178]
SIM_MATCH seed=4 Sieg Blau after 11 rounds (Ancient von Rot zerstört.) in 9299 ms
R01 cleared 42.1s buildings [9, 9] lost [] gold [123, 128, 123, 128, 173, 128]
R02 cleared 50.3s buildings [9, 9] lost [] gold [115, 125, 125, 150, 105, 125]
R03 cleared 55.1s buildings [9, 9] lost [] gold [150, 125, 160, 175, 165, 165]
R04 cleared 66.9s buildings [9, 9] lost [] gold [132, 132, 122, 207, 137, 157]
R05 cleared 60.3s buildings [9, 9] lost [] gold [163, 173, 153, 198, 168, 133]
R06 cleared 61.9s buildings [9, 9] lost [] gold [133, 213, 148, 178, 163, 173]
R07 cleared 87.3s buildings [9, 8] lost ["rot_top_outer"] gold [210, 240, 215, 180, 180, 185]
R08 cleared 89.3s buildings [9, 8] lost [] gold [205, 210, 165, 165, 200, 200]
R09 cleared 80.3s buildings [8, 7] lost ["blau_bot_outer", "rot_top_inner"] gold [308, 293, 303, 218, 263, 268]
R10 cleared 84.1s buildings [8, 7] lost [] gold [220, 195, 195, 185, 190, 185]
R11 cleared 84.8s buildings [7, 6] lost ["blau_bot_inner", "rot_base_1"] gold [363, 343, 358, 323, 313, 308]
R12 cleared 83.7s buildings [7, 6] lost [] gold [267, 237, 237, 232, 272, 232]
R13 cleared 95.7s buildings [6, 5] lost ["rot_mid_outer", "blau_base_2"] gold [303, 338, 333, 388, 393, 393]
R14 cleared 82.1s buildings [6, 5] lost [] gold [390, 295, 305, 310, 320, 375]
R15 core 130.3s buildings [6, 3] lost ["rot_base_2", "rot_ancient"] gold [411, 511, 291, 216, 311, 351]
SIM_MATCH seed=5 Sieg Blau after 15 rounds (Ancient von Rot zerstört.) in 14879 ms
R01 cleared 48.3s buildings [9, 9] lost [] gold [128, 173, 128, 123, 128, 123]
R02 cleared 52.5s buildings [9, 9] lost [] gold [150, 105, 125, 115, 135, 140]
R03 cleared 60.7s buildings [9, 9] lost [] gold [175, 165, 165, 115, 175, 160]
R04 cleared 64.5s buildings [9, 9] lost [] gold [207, 137, 157, 187, 137, 182]
R05 cleared 59.1s buildings [9, 9] lost [] gold [198, 168, 133, 143, 133, 178]
R06 cleared 58.5s buildings [9, 8] lost ["rot_mid_outer"] gold [228, 213, 223, 173, 173, 153]
R07 cleared 72.7s buildings [8, 8] lost ["blau_top_outer"] gold [230, 155, 165, 275, 235, 255]
R08 cleared 86.1s buildings [7, 7] lost ["rot_mid_inner", "blau_top_inner"] gold [285, 245, 250, 230, 250, 250]
R09 cleared 72.4s buildings [7, 6] lost ["rot_bot_outer"] gold [218, 248, 273, 243, 203, 203]
R10 cleared 74.8s buildings [7, 6] lost [] gold [195, 220, 215, 270, 245, 250]
R11 cleared 92.9s buildings [7, 5] lost ["rot_bot_inner"] gold [288, 343, 278, 283, 218, 278]
R12 core 89.1s buildings [7, 2] lost ["rot_base_2", "rot_base_1", "rot_ancient"] gold [328, 288, 348, 108, 163, 87]
SIM_MATCH seed=6 Sieg Blau after 12 rounds (Ancient von Rot zerstört.) in 10244 ms
R01 cleared 41.3s buildings [9, 9] lost [] gold [123, 128, 123, 173, 123, 128]
R02 cleared 51.5s buildings [9, 9] lost [] gold [115, 135, 140, 105, 115, 125]
R03 cleared 52.5s buildings [9, 9] lost [] gold [115, 175, 160, 170, 150, 125]
R04 cleared 57.7s buildings [9, 9] lost [] gold [187, 137, 182, 142, 132, 132]
R05 cleared 57.5s buildings [9, 9] lost [] gold [143, 133, 178, 183, 163, 173]
R06 cleared 61.7s buildings [9, 9] lost [] gold [173, 173, 153, 173, 133, 213]
R07 cleared 61.1s buildings [9, 9] lost [] gold [225, 185, 205, 185, 160, 190]
R08 cleared 57.5s buildings [9, 9] lost [] gold [155, 200, 205, 200, 155, 160]
R09 cleared 72.9s buildings [9, 9] lost [] gold [188, 218, 193, 218, 188, 173]
R10 cleared 72.1s buildings [8, 9] lost ["blau_bot_outer"] gold [215, 250, 240, 280, 285, 265]
R11 cleared 84.4s buildings [5, 9] lost ["blau_mid_outer", "blau_top_outer", "blau_bot_inner"] gold [218, 218, 238, 398, 453, 408]
R12 core 98.9s buildings [0, 9] lost ["blau_mid_inner", "blau_top_inner", "blau_base_2", "blau_base_1", "blau_ancient"] gold [156, 101, 123, 463, 563, 528]
SIM_MATCH seed=7 Sieg Rot after 12 rounds (Ancient von Blau zerstört.) in 10269 ms
R01 cleared 41.9s buildings [9, 9] lost [] gold [173, 123, 128, 123, 128, 173]
R02 cleared 47.5s buildings [9, 9] lost [] gold [105, 115, 125, 125, 150, 105]
R03 cleared 55.5s buildings [9, 9] lost [] gold [170, 150, 125, 160, 175, 165]
R04 cleared 55.1s buildings [9, 9] lost [] gold [142, 132, 132, 122, 207, 137]
R05 cleared 54.5s buildings [9, 9] lost [] gold [183, 163, 173, 153, 198, 168]
R06 cleared 57.7s buildings [9, 9] lost [] gold [173, 133, 213, 148, 178, 163]
R07 cleared 58.7s buildings [9, 9] lost [] gold [185, 160, 190, 165, 180, 180]
R08 cleared 54.9s buildings [9, 9] lost [] gold [200, 155, 160, 165, 165, 200]
R09 cleared 66.5s buildings [9, 8] lost ["rot_top_outer"] gold [268, 238, 223, 178, 168, 213]
R10 cleared 87.5s buildings [9, 7] lost ["rot_top_inner"] gold [280, 280, 335, 210, 200, 215]
R11 cleared 87.7s buildings [9, 6] lost ["rot_bot_outer"] gold [323, 403, 303, 238, 218, 228]
R12 core 103.0s buildings [9, 1] lost ["rot_base_1", "rot_mid_outer", "rot_bot_inner", "rot_base_2", "rot_ancient"] gold [418, 493, 458, 143, 113, 118]
SIM_MATCH seed=8 Sieg Blau after 12 rounds (Ancient von Rot zerstört.) in 10274 ms
WARNING: 6 ObjectDB instances were leaked at exit (run with `--verbose` for details).
at: cleanup (core/object/object.cpp:2536)
ERROR: 1 resources still in use at exit (run with --verbose for details).
at: clear (core/io/resource.cpp:822)
EXIT 0

View file

@ -0,0 +1,15 @@
Godot Engine v4.7.2.stable.fedora.ed1daf0bf - https://godotengine.org
Vulkan 1.4.351 - Forward+ - Using Device #0: NVIDIA - NVIDIA GeForce RTX 5060 Ti
M1_MENU_READY_MS 733
M1_SHOT planning-1280x720
M1_SHOT planning-1920x1080
M1_SHOT combat-mid-live
M1_SHOT settlement
M1_SHOT combat-top
M1_SHOT overview-combat
M1_SHOT core-siege
M1_SHOT result
M1_MATCH_RESULT Ancient von Rot zerstört. after 10 waves
M1_VERIFY_PASS: 38 checks; 0 failures
EXIT 0

View file

@ -0,0 +1,19 @@
{
"boss_log": [
{
"chooser": 4,
"event": "won",
"round": 12,
"team": 1
}
],
"checks": 14,
"failures": [],
"rounds": 12,
"seen": [
"announce",
"hero_field",
"hero_items",
"grotto"
]
}

View file

@ -0,0 +1,4 @@
{
"checks": 73,
"failures": []
}

View file

@ -0,0 +1,85 @@
[
{
"blau_gold_delta": 284,
"blau_hp_after_r8": 0.0,
"blau_hp_delta": -1436.5,
"blau_mid_towers_lost_r8": 0,
"claim_team": -1,
"grotto": "none",
"phase": "vorbei",
"rot_gold_delta": -235,
"rot_hp_delta": -50521.0198,
"rot_sent_value": 0,
"scenario": "kein_versuch",
"sent_value": 0,
"winner": 0
},
{
"blau_gold_delta": 284,
"blau_hp_after_r8": -317.199999999997,
"blau_hp_delta": -1753.7,
"blau_mid_towers_lost_r8": 0,
"claim_team": -1,
"grotto": "failed",
"phase": "vorbei",
"rot_gold_delta": -235,
"rot_hp_delta": -50257.2862,
"rot_sent_value": 0,
"scenario": "gescheitert",
"sent_value": 210,
"winner": 0
},
{
"blau_gold_delta": 543,
"blau_hp_after_r8": -13227.8841,
"blau_hp_delta": -15022.5341,
"blau_mid_towers_lost_r8": 2,
"boss_bounty_to_rot": 1060,
"boss_killed": true,
"boss_round": 9,
"claim_team": 0,
"grotto": "won",
"phase": "vorbei",
"rot_gold_delta": 794,
"rot_hp_delta": -35384.1102,
"rot_hp_lost_boss_round": 33218.6196,
"rot_sent_value": 0,
"scenario": "erfolgreich",
"sent_value": 1000,
"winner": 0
},
{
"blau_gold_delta": 494,
"blau_hp_after_r8": -401.375,
"blau_hp_delta": -1434.51880000001,
"blau_mid_towers_lost_r8": 0,
"claim_team": -1,
"grotto": "failed",
"phase": "vorbei",
"rot_gold_delta": -185,
"rot_hp_delta": -51276.0262,
"rot_sent_value": 995,
"scenario": "umkaempft",
"sent_value": 1000,
"winner": 0
},
{
"blau_gold_delta": -250,
"blau_hp_after_r8": -36433.06827,
"blau_hp_delta": -68294.9678,
"blau_mid_towers_lost_r8": 2,
"boss_bounty_to_rot": 1060,
"boss_killed": true,
"boss_round": 9,
"claim_team": 0,
"grotto": "won",
"phase": "vorbei",
"rot_gold_delta": 437,
"rot_hp_delta": -25391.9446,
"rot_hp_lost_boss_round": 25391.9446,
"rot_sent_value": 0,
"scenario": "ueberinvestition",
"sent_value": 2495,
"winner": 1
}
]

View file

@ -0,0 +1,85 @@
[
{
"blau_gold_delta": 174,
"blau_hp_after_r8": -3771.781,
"blau_hp_delta": -9222.031,
"blau_mid_towers_lost_r8": 0,
"claim_team": -1,
"grotto": "none",
"phase": "läuft",
"rot_gold_delta": 139,
"rot_hp_delta": -5573.1798,
"rot_sent_value": 0,
"scenario": "kein_versuch",
"sent_value": 0,
"winner": -2
},
{
"blau_gold_delta": 174,
"blau_hp_after_r8": -4097.3806,
"blau_hp_delta": -9547.6306,
"blau_mid_towers_lost_r8": 0,
"claim_team": -1,
"grotto": "failed",
"phase": "läuft",
"rot_gold_delta": 139,
"rot_hp_delta": -4659.805,
"rot_sent_value": 0,
"scenario": "gescheitert",
"sent_value": 180,
"winner": -2
},
{
"blau_gold_delta": -302,
"blau_hp_after_r8": -16247.2932,
"blau_hp_delta": -67736.578,
"blau_mid_towers_lost_r8": 2,
"boss_bounty_to_rot": 1060,
"boss_killed": true,
"boss_round": 9,
"claim_team": 0,
"grotto": "won",
"phase": "vorbei",
"rot_gold_delta": 319,
"rot_hp_delta": -22792.746,
"rot_hp_lost_boss_round": 22792.746,
"rot_sent_value": 0,
"scenario": "erfolgreich",
"sent_value": 930,
"winner": 1
},
{
"blau_gold_delta": 174,
"blau_hp_after_r8": -4404.387,
"blau_hp_delta": -9854.637,
"blau_mid_towers_lost_r8": 0,
"claim_team": -1,
"grotto": "failed",
"phase": "läuft",
"rot_gold_delta": 139,
"rot_hp_delta": -4659.805,
"rot_sent_value": 865,
"scenario": "umkaempft",
"sent_value": 930,
"winner": -2
},
{
"blau_gold_delta": -320,
"blau_hp_after_r8": -38337.2773,
"blau_hp_delta": -67736.578,
"blau_mid_towers_lost_r8": 2,
"boss_bounty_to_rot": 1060,
"boss_killed": true,
"boss_round": 9,
"claim_team": 0,
"grotto": "won",
"phase": "vorbei",
"rot_gold_delta": 277,
"rot_hp_delta": -20684.7692,
"rot_hp_lost_boss_round": 20684.7692,
"rot_sent_value": 0,
"scenario": "ueberinvestition",
"sent_value": 2580,
"winner": 1
}
]

View file

@ -0,0 +1,9 @@
Godot Engine v4.7.2.stable.fedora.ed1daf0bf - https://godotengine.org
LUNE_FULL_MATCH: seed 2 -> Blau after 7 rounds
LUNE_CORE_PASS: 91 checks; 0 failures
WARNING: 10 ObjectDB instances were leaked at exit (run with `--verbose` for details).
at: cleanup (core/object/object.cpp:2536)
ERROR: 1 resources still in use at exit (run with --verbose for details).
at: clear (core/io/resource.cpp:822)
EXIT 0

View file

@ -0,0 +1,8 @@
Godot Engine v4.7.2.stable.fedora.ed1daf0bf - https://godotengine.org
LUNE_M2_PASS: 65 checks; 0 failures
WARNING: 14 ObjectDB instances were leaked at exit (run with `--verbose` for details).
at: cleanup (core/object/object.cpp:2536)
ERROR: 1 resources still in use at exit (run with --verbose for details).
at: clear (core/io/resource.cpp:822)
EXIT 0

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,15 @@
Godot Engine v4.7.2.stable.fedora.ed1daf0bf - https://godotengine.org
Vulkan 1.4.351 - Forward+ - Using Device #0: NVIDIA - NVIDIA GeForce RTX 5060 Ti
M1_MENU_READY_MS 792
M1_SHOT planning-1280x720
M1_SHOT planning-1920x1080
M1_SHOT combat-mid-live
M1_SHOT settlement
M1_SHOT combat-top
M1_SHOT overview-combat
M1_SHOT core-siege
M1_SHOT result
M1_MATCH_RESULT Ancient von Rot zerstört. after 7 waves
M1_VERIFY_PASS: 38 checks; 0 failures
EXIT 0

View file

@ -0,0 +1,16 @@
Godot Engine v4.7.2.stable.fedora.ed1daf0bf - https://godotengine.org
Vulkan 1.4.351 - Forward+ - Using Device #0: NVIDIA - NVIDIA GeForce RTX 5060 Ti
M2APP_SHOT m2-hud-hero-round1
M2APP_SHOT m2-grotto-fight
M2APP_SHOT m2-hero-field
M2APP_SHOT m2-hero-items
M2APP_SHOT m2-announcements
M2APP_SHOT m2-boss-deployed
M2APP_BOSS_LOG [{ "round": 5, "event": "failed" }, { "round": 6, "event": "failed" }, { "round": 7, "event": "failed" }, { "round": 8, "event": "won", "team": 1, "chooser": 4 }, { "round": 9, "event": "deployed", "team": 1, "lane": "top" }, { "round": 12, "event": "won", "team": 1, "chooser": 4 }]
M2APP_VERIFY_PASS: 15 checks; 0 failures
WARNING: 8 ObjectDB instances were leaked at exit (run with `--verbose` for details).
at: cleanup (core/object/object.cpp:2536)
ERROR: 1 resources still in use at exit (run with --verbose for details).
at: clear (core/io/resource.cpp:822)
EXIT 0

View file

@ -0,0 +1,6 @@
Partien 40 · Ergebnisse {'blau': 20, 'rot': 20} · Testlimit 0 · Zeitfenster-Kämpfe 1 · längster Kampf 180 s
Wellen bis Ende: min 7 · median 9 · max 14
Seitentausch: dieselbe Zusammensetzung gewinnt beide Male in 20/20 Paaren
Profile (Siege/Einsätze): {'ausgewogen': '24/48 (50 %)', 'bossjagd': '2/34 (6 %)', 'druck': '28/34 (82 %)', 'fernkampf': '26/48 (54 %)', 'teamhilfe': '28/48 (58 %)', 'verteidigung': '12/28 (43 %)'}
Bünde: {'glutpakt': '68/124 (55 %)', 'wurzelbund': '52/116 (45 %)'}
Boss: {'won': 18, 'deployed': 10, 'failed': 63} · Heldenstufen am Ende (Median): 4

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,26 @@
Godot Engine v4.7.2.stable.fedora.ed1daf0bf - https://godotengine.org
BALANCE 00 ["ausgewogen", "druck", "teamhilfe"] | ["teamhilfe", "verteidigung", "ausgewogen"] -> blau (7) / swapped rot (8)
BALANCE 01 ["bossjagd", "bossjagd", "teamhilfe"] | ["fernkampf", "teamhilfe", "fernkampf"] -> rot (8) / swapped blau (8)
BALANCE 02 ["bossjagd", "ausgewogen", "teamhilfe"] | ["druck", "ausgewogen", "verteidigung"] -> rot (9) / swapped blau (9)
BALANCE 03 ["fernkampf", "druck", "ausgewogen"] | ["druck", "fernkampf", "verteidigung"] -> blau (13) / swapped rot (13)
BALANCE 04 ["ausgewogen", "teamhilfe", "fernkampf"] | ["ausgewogen", "verteidigung", "fernkampf"] -> blau (9) / swapped rot (9)
BALANCE 05 ["teamhilfe", "fernkampf", "verteidigung"] | ["fernkampf", "bossjagd", "bossjagd"] -> blau (8) / swapped rot (8)
BALANCE 06 ["bossjagd", "ausgewogen", "teamhilfe"] | ["fernkampf", "teamhilfe", "ausgewogen"] -> rot (7) / swapped blau (8)
BALANCE 07 ["ausgewogen", "ausgewogen", "ausgewogen"] | ["teamhilfe", "teamhilfe", "ausgewogen"] -> rot (12) / swapped blau (12)
BALANCE 08 ["teamhilfe", "ausgewogen", "bossjagd"] | ["druck", "ausgewogen", "druck"] -> rot (10) / swapped blau (10)
BALANCE 09 ["verteidigung", "druck", "druck"] | ["bossjagd", "ausgewogen", "teamhilfe"] -> blau (9) / swapped rot (9)
BALANCE 10 ["druck", "ausgewogen", "ausgewogen"] | ["teamhilfe", "bossjagd", "ausgewogen"] -> blau (9) / swapped rot (9)
BALANCE 11 ["bossjagd", "fernkampf", "fernkampf"] | ["teamhilfe", "verteidigung", "teamhilfe"] -> rot (8) / swapped blau (8)
BALANCE 12 ["fernkampf", "fernkampf", "teamhilfe"] | ["teamhilfe", "ausgewogen", "fernkampf"] -> blau (8) / swapped rot (9)
BALANCE 13 ["teamhilfe", "druck", "fernkampf"] | ["fernkampf", "druck", "ausgewogen"] -> rot (12) / swapped blau (12)
BALANCE 14 ["bossjagd", "druck", "fernkampf"] | ["bossjagd", "bossjagd", "bossjagd"] -> blau (8) / swapped rot (8)
BALANCE 15 ["fernkampf", "fernkampf", "verteidigung"] | ["druck", "fernkampf", "ausgewogen"] -> rot (13) / swapped blau (13)
BALANCE 16 ["teamhilfe", "verteidigung", "verteidigung"] | ["teamhilfe", "verteidigung", "ausgewogen"] -> rot (14) / swapped blau (14)
BALANCE 17 ["fernkampf", "verteidigung", "fernkampf"] | ["druck", "fernkampf", "bossjagd"] -> blau (10) / swapped rot (10)
BALANCE 18 ["verteidigung", "ausgewogen", "bossjagd"] | ["druck", "teamhilfe", "druck"] -> rot (8) / swapped blau (8)
BALANCE 19 ["druck", "teamhilfe", "teamhilfe"] | ["fernkampf", "verteidigung", "bossjagd"] -> blau (7) / swapped rot (7)
WARNING: 16 ObjectDB instances were leaked at exit (run with `--verbose` for details).
at: cleanup (core/object/object.cpp:2536)
ERROR: 1 resources still in use at exit (run with --verbose for details).
at: clear (core/io/resource.cpp:822)

View file

@ -0,0 +1,6 @@
Partien 40 · Ergebnisse {'blau': 19, 'rot': 21} · Testlimit 0 · Zeitfenster-Kämpfe 1 · längster Kampf 180 s
Wellen bis Ende: min 7 · median 12 · max 14
Seitentausch: dieselbe Zusammensetzung gewinnt beide Male in 19/20 Paaren
Profile (Siege/Einsätze): {'ausgewogen': '26/48 (54 %)', 'bossjagd': '9/34 (26 %)', 'druck': '22/34 (65 %)', 'fernkampf': '29/48 (60 %)', 'teamhilfe': '28/48 (58 %)', 'verteidigung': '6/28 (21 %)'}
Bünde: {'glutpakt': '69/124 (56 %)', 'wurzelbund': '51/116 (44 %)'}
Boss: {} · Heldenstufen am Ende (Median): 5

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,26 @@
Godot Engine v4.7.2.stable.fedora.ed1daf0bf - https://godotengine.org
BALANCE 00 ["ausgewogen", "druck", "teamhilfe"] | ["teamhilfe", "verteidigung", "ausgewogen"] -> blau (7) / swapped rot (8)
BALANCE 01 ["bossjagd", "bossjagd", "teamhilfe"] | ["fernkampf", "teamhilfe", "fernkampf"] -> rot (10) / swapped blau (9)
BALANCE 02 ["bossjagd", "ausgewogen", "teamhilfe"] | ["druck", "ausgewogen", "verteidigung"] -> blau (9) / swapped rot (9)
BALANCE 03 ["fernkampf", "druck", "ausgewogen"] | ["druck", "fernkampf", "verteidigung"] -> blau (13) / swapped rot (13)
BALANCE 04 ["ausgewogen", "teamhilfe", "fernkampf"] | ["ausgewogen", "verteidigung", "fernkampf"] -> blau (9) / swapped rot (9)
BALANCE 05 ["teamhilfe", "fernkampf", "verteidigung"] | ["fernkampf", "bossjagd", "bossjagd"] -> blau (12) / swapped rot (12)
BALANCE 06 ["bossjagd", "ausgewogen", "teamhilfe"] | ["fernkampf", "teamhilfe", "ausgewogen"] -> rot (9) / swapped rot (9)
BALANCE 07 ["ausgewogen", "ausgewogen", "ausgewogen"] | ["teamhilfe", "teamhilfe", "ausgewogen"] -> rot (12) / swapped blau (12)
BALANCE 08 ["teamhilfe", "ausgewogen", "bossjagd"] | ["druck", "ausgewogen", "druck"] -> rot (13) / swapped blau (13)
BALANCE 09 ["verteidigung", "druck", "druck"] | ["bossjagd", "ausgewogen", "teamhilfe"] -> rot (9) / swapped blau (9)
BALANCE 10 ["druck", "ausgewogen", "ausgewogen"] | ["teamhilfe", "bossjagd", "ausgewogen"] -> blau (13) / swapped rot (13)
BALANCE 11 ["bossjagd", "fernkampf", "fernkampf"] | ["teamhilfe", "verteidigung", "teamhilfe"] -> blau (11) / swapped rot (11)
BALANCE 12 ["fernkampf", "fernkampf", "teamhilfe"] | ["teamhilfe", "ausgewogen", "fernkampf"] -> blau (8) / swapped rot (9)
BALANCE 13 ["teamhilfe", "druck", "fernkampf"] | ["fernkampf", "druck", "ausgewogen"] -> rot (12) / swapped blau (12)
BALANCE 14 ["bossjagd", "druck", "fernkampf"] | ["bossjagd", "bossjagd", "bossjagd"] -> blau (12) / swapped rot (12)
BALANCE 15 ["fernkampf", "fernkampf", "verteidigung"] | ["druck", "fernkampf", "ausgewogen"] -> rot (13) / swapped blau (13)
BALANCE 16 ["teamhilfe", "verteidigung", "verteidigung"] | ["teamhilfe", "verteidigung", "ausgewogen"] -> rot (14) / swapped blau (14)
BALANCE 17 ["fernkampf", "verteidigung", "fernkampf"] | ["druck", "fernkampf", "bossjagd"] -> blau (13) / swapped rot (13)
BALANCE 18 ["verteidigung", "ausgewogen", "bossjagd"] | ["druck", "teamhilfe", "druck"] -> rot (9) / swapped blau (9)
BALANCE 19 ["druck", "teamhilfe", "teamhilfe"] | ["fernkampf", "verteidigung", "bossjagd"] -> blau (7) / swapped rot (7)
WARNING: 18 ObjectDB instances were leaked at exit (run with `--verbose` for details).
at: cleanup (core/object/object.cpp:2536)
ERROR: 1 resources still in use at exit (run with --verbose for details).
at: clear (core/io/resource.cpp:822)

View file

@ -0,0 +1,6 @@
Partien 40 · Ergebnisse {'blau': 20, 'rot': 20} · Testlimit 0 · Zeitfenster-Kämpfe 3 · längster Kampf 180 s
Wellen bis Ende: min 7 · median 10 · max 14
Seitentausch: dieselbe Zusammensetzung gewinnt beide Male in 20/20 Paaren
Profile (Siege/Einsätze): {'ausgewogen': '26/48 (54 %)', 'bossjagd': '10/34 (29 %)', 'druck': '24/34 (71 %)', 'fernkampf': '28/48 (58 %)', 'teamhilfe': '28/48 (58 %)', 'verteidigung': '4/28 (14 %)'}
Bünde: {'glutpakt': '64/124 (52 %)', 'wurzelbund': '56/116 (48 %)'}
Boss: {'won': 21, 'deployed': 12, 'failed': 2} · Heldenstufen am Ende (Median): 3

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,26 @@
Godot Engine v4.7.2.stable.fedora.ed1daf0bf - https://godotengine.org
BALANCE 00 ["ausgewogen", "druck", "teamhilfe"] | ["teamhilfe", "verteidigung", "ausgewogen"] -> blau (7) / swapped rot (8)
BALANCE 01 ["bossjagd", "bossjagd", "teamhilfe"] | ["fernkampf", "teamhilfe", "fernkampf"] -> rot (9) / swapped blau (8)
BALANCE 02 ["bossjagd", "ausgewogen", "teamhilfe"] | ["druck", "ausgewogen", "verteidigung"] -> blau (10) / swapped rot (10)
BALANCE 03 ["fernkampf", "druck", "ausgewogen"] | ["druck", "fernkampf", "verteidigung"] -> blau (13) / swapped rot (13)
BALANCE 04 ["ausgewogen", "teamhilfe", "fernkampf"] | ["ausgewogen", "verteidigung", "fernkampf"] -> blau (9) / swapped rot (9)
BALANCE 05 ["teamhilfe", "fernkampf", "verteidigung"] | ["fernkampf", "bossjagd", "bossjagd"] -> blau (11) / swapped rot (11)
BALANCE 06 ["bossjagd", "ausgewogen", "teamhilfe"] | ["fernkampf", "teamhilfe", "ausgewogen"] -> rot (9) / swapped blau (9)
BALANCE 07 ["ausgewogen", "ausgewogen", "ausgewogen"] | ["teamhilfe", "teamhilfe", "ausgewogen"] -> rot (12) / swapped blau (12)
BALANCE 08 ["teamhilfe", "ausgewogen", "bossjagd"] | ["druck", "ausgewogen", "druck"] -> rot (11) / swapped blau (11)
BALANCE 09 ["verteidigung", "druck", "druck"] | ["bossjagd", "ausgewogen", "teamhilfe"] -> rot (9) / swapped blau (9)
BALANCE 10 ["druck", "ausgewogen", "ausgewogen"] | ["teamhilfe", "bossjagd", "ausgewogen"] -> blau (12) / swapped rot (12)
BALANCE 11 ["bossjagd", "fernkampf", "fernkampf"] | ["teamhilfe", "verteidigung", "teamhilfe"] -> blau (10) / swapped rot (10)
BALANCE 12 ["fernkampf", "fernkampf", "teamhilfe"] | ["teamhilfe", "ausgewogen", "fernkampf"] -> blau (8) / swapped rot (9)
BALANCE 13 ["teamhilfe", "druck", "fernkampf"] | ["fernkampf", "druck", "ausgewogen"] -> rot (12) / swapped blau (12)
BALANCE 14 ["bossjagd", "druck", "fernkampf"] | ["bossjagd", "bossjagd", "bossjagd"] -> blau (10) / swapped rot (10)
BALANCE 15 ["fernkampf", "fernkampf", "verteidigung"] | ["druck", "fernkampf", "ausgewogen"] -> rot (13) / swapped blau (13)
BALANCE 16 ["teamhilfe", "verteidigung", "verteidigung"] | ["teamhilfe", "verteidigung", "ausgewogen"] -> rot (14) / swapped blau (14)
BALANCE 17 ["fernkampf", "verteidigung", "fernkampf"] | ["druck", "fernkampf", "bossjagd"] -> rot (9) / swapped blau (9)
BALANCE 18 ["verteidigung", "ausgewogen", "bossjagd"] | ["druck", "teamhilfe", "druck"] -> rot (8) / swapped blau (8)
BALANCE 19 ["druck", "teamhilfe", "teamhilfe"] | ["fernkampf", "verteidigung", "bossjagd"] -> blau (7) / swapped rot (7)
WARNING: 26 ObjectDB instances were leaked at exit (run with `--verbose` for details).
at: cleanup (core/object/object.cpp:2536)
ERROR: 1 resources still in use at exit (run with --verbose for details).
at: clear (core/io/resource.cpp:822)

View file

@ -0,0 +1,6 @@
Partien 40 · Ergebnisse {'rot': 20, 'blau': 20} · Testlimit 0 · Zeitfenster-Kämpfe 0 · längster Kampf 135 s
Wellen bis Ende: min 9 · median 11 · max 14
Seitentausch: dieselbe Zusammensetzung gewinnt beide Male in 20/20 Paaren
Profile (Siege/Einsätze): {'ausgewogen': '28/48 (58 %)', 'bossjagd': '8/34 (24 %)', 'druck': '18/34 (53 %)', 'fernkampf': '26/48 (54 %)', 'teamhilfe': '22/48 (46 %)', 'verteidigung': '18/28 (64 %)'}
Bünde: {'glutpakt': '68/124 (55 %)', 'wurzelbund': '52/116 (45 %)'}
Boss: {'won': 24, 'deployed': 16} · Heldenstufen am Ende (Median): 4

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,6 @@
Partien 40 · Ergebnisse {'rot': 20, 'blau': 20} · Testlimit 0 · Zeitfenster-Kämpfe 0 · längster Kampf 157 s
Wellen bis Ende: min 9 · median 11 · max 14
Seitentausch: dieselbe Zusammensetzung gewinnt beide Male in 20/20 Paaren
Profile (Siege/Einsätze): {'ausgewogen': '28/48 (58 %)', 'bossjagd': '8/34 (24 %)', 'druck': '18/34 (53 %)', 'fernkampf': '26/48 (54 %)', 'teamhilfe': '22/48 (46 %)', 'verteidigung': '18/28 (64 %)'}
Bünde: {'glutpakt': '68/124 (55 %)', 'wurzelbund': '52/116 (45 %)'}
Boss: {'won': 20, 'deployed': 8} · Heldenstufen am Ende (Median): 4

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,14 @@
Partien 30 · Ergebnisse {'blau': 15, 'rot': 15} · Testlimit 0 · Zeitfenster-Kämpfe 4 · längster Kampf 180 s
Wellen bis Ende: min 6 · median 10 · max 16
Seitentausch: dieselbe Zusammensetzung gewinnt beide Male in 15/15 Paaren
Profile (Siege/Einsätze): {'ausgewogen': '24/30 (80 %)', 'bossjagd': '0/30 (0 %)', 'druck': '12/30 (40 %)', 'fernkampf': '18/30 (60 %)', 'teamhilfe': '30/30 (100 %)', 'verteidigung': '6/30 (20 %)'}
Bünde: {'glutpakt': '30/60 (50 %)', 'wurzelbund': '60/120 (50 %)'}
Boss: {'won': 8, 'deployed': 4} · Heldenstufen am Ende (Median): 4
Duelle (Siege Zeile gegen Spalte, je 2 Partien mit Seitentausch):
ausgew bossja druck fernka teamhi vertei gesamt
ausgewogen - 2 2 2 0 2 8/10
bossjagd 0 - 0 0 0 0 0/10
druck 0 2 - 0 0 2 4/10
fernkampf 0 2 2 - 0 2 6/10
teamhilfe 2 2 2 2 - 2 10/10
verteidigung 0 2 0 0 0 - 2/10

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,21 @@
Godot Engine v4.7.2.stable.fedora.ed1daf0bf - https://godotengine.org
BALANCE 00 ["ausgewogen", "ausgewogen", "ausgewogen"] | ["verteidigung", "verteidigung", "verteidigung"] -> blau (11) / swapped rot (11)
BALANCE 01 ["ausgewogen", "ausgewogen", "ausgewogen"] | ["druck", "druck", "druck"] -> blau (13) / swapped rot (13)
BALANCE 02 ["ausgewogen", "ausgewogen", "ausgewogen"] | ["fernkampf", "fernkampf", "fernkampf"] -> blau (14) / swapped rot (14)
BALANCE 03 ["ausgewogen", "ausgewogen", "ausgewogen"] | ["teamhilfe", "teamhilfe", "teamhilfe"] -> rot (8) / swapped blau (8)
BALANCE 04 ["ausgewogen", "ausgewogen", "ausgewogen"] | ["bossjagd", "bossjagd", "bossjagd"] -> blau (9) / swapped rot (9)
BALANCE 05 ["verteidigung", "verteidigung", "verteidigung"] | ["druck", "druck", "druck"] -> rot (16) / swapped blau (16)
BALANCE 06 ["verteidigung", "verteidigung", "verteidigung"] | ["fernkampf", "fernkampf", "fernkampf"] -> rot (12) / swapped blau (12)
BALANCE 07 ["verteidigung", "verteidigung", "verteidigung"] | ["teamhilfe", "teamhilfe", "teamhilfe"] -> rot (8) / swapped blau (8)
BALANCE 08 ["verteidigung", "verteidigung", "verteidigung"] | ["bossjagd", "bossjagd", "bossjagd"] -> blau (10) / swapped rot (10)
BALANCE 09 ["druck", "druck", "druck"] | ["fernkampf", "fernkampf", "fernkampf"] -> rot (12) / swapped blau (12)
BALANCE 10 ["druck", "druck", "druck"] | ["teamhilfe", "teamhilfe", "teamhilfe"] -> rot (6) / swapped blau (6)
BALANCE 11 ["druck", "druck", "druck"] | ["bossjagd", "bossjagd", "bossjagd"] -> blau (10) / swapped rot (10)
BALANCE 12 ["fernkampf", "fernkampf", "fernkampf"] | ["teamhilfe", "teamhilfe", "teamhilfe"] -> rot (8) / swapped blau (8)
BALANCE 13 ["fernkampf", "fernkampf", "fernkampf"] | ["bossjagd", "bossjagd", "bossjagd"] -> blau (9) / swapped rot (9)
BALANCE 14 ["teamhilfe", "teamhilfe", "teamhilfe"] | ["bossjagd", "bossjagd", "bossjagd"] -> blau (6) / swapped rot (6)
WARNING: 10 ObjectDB instances were leaked at exit (run with `--verbose` for details).
at: cleanup (core/object/object.cpp:2536)
ERROR: 1 resources still in use at exit (run with --verbose for details).
at: clear (core/io/resource.cpp:822)

View file

@ -0,0 +1,14 @@
Partien 30 · Ergebnisse {'blau': 15, 'rot': 15} · Testlimit 0 · Zeitfenster-Kämpfe 0 · längster Kampf 169 s
Wellen bis Ende: min 9 · median 10 · max 14
Seitentausch: dieselbe Zusammensetzung gewinnt beide Male in 15/15 Paaren
Profile (Siege/Einsätze): {'ausgewogen': '30/30 (100 %)', 'bossjagd': '0/30 (0 %)', 'druck': '6/30 (20 %)', 'fernkampf': '12/30 (40 %)', 'teamhilfe': '18/30 (60 %)', 'verteidigung': '24/30 (80 %)'}
Bünde: {'glutpakt': '30/60 (50 %)', 'wurzelbund': '60/120 (50 %)'}
Boss: {'won': 10, 'deployed': 2} · Heldenstufen am Ende (Median): 5
Duelle (Siege Zeile gegen Spalte, je 2 Partien mit Seitentausch):
ausgew bossja druck fernka teamhi vertei gesamt
ausgewogen - 2 2 2 2 2 10/10
bossjagd 0 - 0 0 0 0 0/10
druck 0 2 - 0 0 0 2/10
fernkampf 0 2 2 - 0 0 4/10
teamhilfe 0 2 2 2 - 0 6/10
verteidigung 0 2 2 2 2 - 8/10

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,14 @@
Partien 30 · Ergebnisse {'blau': 15, 'rot': 15} · Testlimit 0 · Zeitfenster-Kämpfe 0 · längster Kampf 169 s
Wellen bis Ende: min 9 · median 10 · max 14
Seitentausch: dieselbe Zusammensetzung gewinnt beide Male in 15/15 Paaren
Profile (Siege/Einsätze): {'ausgewogen': '30/30 (100 %)', 'bossjagd': '0/30 (0 %)', 'druck': '6/30 (20 %)', 'fernkampf': '12/30 (40 %)', 'teamhilfe': '18/30 (60 %)', 'verteidigung': '24/30 (80 %)'}
Bünde: {'glutpakt': '30/60 (50 %)', 'wurzelbund': '60/120 (50 %)'}
Boss: {'won': 4, 'deployed': 2} · Heldenstufen am Ende (Median): 5
Duelle (Siege Zeile gegen Spalte, je 2 Partien mit Seitentausch):
ausgew bossja druck fernka teamhi vertei gesamt
ausgewogen - 2 2 2 2 2 10/10
bossjagd 0 - 0 0 0 0 0/10
druck 0 2 - 0 0 0 2/10
fernkampf 0 2 2 - 0 0 4/10
teamhilfe 0 2 2 2 - 0 6/10
verteidigung 0 2 2 2 2 - 8/10

Some files were not shown because too many files have changed in this diff Show more