levelbuild texture names mean the same picture on every base level (matched by pixels, missing village textures copied over in the java builder, clear error otherwise), editor texture choices follow the base

This commit is contained in:
MineTec committed 2026-11-07 15:27:08 +01:00
1 parent d56f3c604f
commit 987b0104fa
14 files changed
+534 -42

No files matched your search

+45 -2
View File
@@ -13,7 +13,8 @@ ambient occlusion) and object placement (strats by type name, parameters from th
Coordinates (as in the game / for strats): x east, y up, z = grid row
(positive). 1 unit = 1 grid cell. The base level's grid is 96x96 (x,z in 0..96).
Angles in degrees (0..360) about the y axis.
Texture names (TEX/FLAT below) are German keys and part of the level script API.
Texture names (TEX/FLAT below) are German keys and part of the level script API; TEX indices are
village indices, other bases (--base) map a name to the same picture or reject it.
Docs: ../notes/levelbau.md
"""
import argparse
@@ -97,6 +98,21 @@ def to_exp(p):
return (p[0], p[1], -p[2])
def _tex_fingerprints(pages, texs):
"""Per texture entry: hash of page format + pixel rectangle, None for flat colours."""
cache, out = {}, []
for t in texs:
pg = t['page']
if t['flags'] & 1 or pg < 0 or pg >= len(pages) or t['u1'] < t['u0'] or t['v1'] < t['v0']:
out.append(None)
continue
if pg not in cache:
cache[pg] = G.page_rgba(pages[pg])
a = cache[pg][t['v0']:t['v1'] + 1, t['u0']:t['u1'] + 1]
out.append(hashlib.md5(bytes([pages[pg]['flags'] & 0x7f]) + str(a.shape).encode() + a.tobytes()).hexdigest())
return out
class Level:
"""Construction kit. All methods take game coordinates (x, z = grid cell, y up)."""
@@ -150,6 +166,29 @@ class Level:
self.protos[nm] = [tuple(s)]
self.foreign_params[nm] = prm
def _base_names(self):
"""TEX names on this base: same picture (page format + pixels) at the village index or elsewhere
(Java: levelbuild.BaseTextures)."""
if getattr(self, '_names', None) is None:
if os.path.abspath(self.base) == os.path.abspath(BASE):
self._names = dict(TEX)
else:
vp, vt = G.parse_text(G.wad_get(G.wad_read(BASE), 'TEXT'))
own = _tex_fingerprints(self.pages, self.texs)
vfp = _tex_fingerprints(vp, vt)
first = {}
for i, f in enumerate(own):
if f is not None and (f not in first or (self.texs[i]['flags'] == 0x104
and self.texs[first[f]]['flags'] != 0x104)):
first[f] = i
self._names = {}
for n, i in TEX.items():
if i < len(own) and own[i] is not None and own[i] == vfp[i]:
self._names[n] = i
elif vfp[i] in first:
self._names[n] = first[vfp[i]]
return self._names
def types(self):
return sorted(self.protos)
@@ -162,7 +201,11 @@ class Level:
FLAT.setdefault(key, t)
t = key
if t in TEX:
return TEX[t]
names = self._base_names()
if t not in names:
raise KeyError('texture %r is a village texture the base level does not have (use an index, a flat '
'colour, or the Java builder, which copies it over)' % t)
return names[t]
if t in FLAT:
if t not in self.flats_used:
self.flats_used[t] = len(self.texs) + len(self.flats_used)