Files
BT412/scratchpad/punch_geom.py
T
arcattackandClaude Fable 5 8ed6184d65 Combat: AUTHENTIC weapon groups -- streamed per-mech button bindings + the real fire chain (task #5)
The recovered system: fire channels = LBE4ControlsManager buttonGroups
(0x40/0x45/0x46/0x47); default groups = the per-mech type-6 controls-map
resource in BTL4.RES, installed by the T0 CreateStreamedMappings the port
already called -- it needed only the TriggerState attribute (id 0x13 PINNED
to the binary value; fireImpulse@0x31C is the binary's TriggerState) and an
input feed.  Keyboard/harness now push press/release edges into the button
groups; the gBT*Trigger bypasses, per-type keyboard split and 1,0 pulse
hack are retired -- weapons sharing a button fire TOGETHER (madcat Trigger
= 4 weapons).  Myomers @4b9550/@4b95b8 misattribution corrected (they are
MechWeapon ConfigureMappables/ChooseButton).  Verified 2-node: kill through
the authentic chain (12 hits vs ~36 pre-groups).  Config-mode session
(regrouping UI) = the remaining stage, KB-scoped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 13:06:29 -05:00

94 lines
4.2 KiB
Python

#!/usr/bin/env python3
"""Dump the three sub-geometries of each PUNCH patch separately:
chunk1 (dmg_set mask), chunk2 (undmg2enbl DRAWN), chunk3 (dmg_clear).
Check: are chunk2 and chunk3 identical? bbox/plane relationships chunk1 vs chunk2."""
import struct, sys
import numpy as np
VSTRIDE = {0x80:12, 0x81:24, 0x82:28, 0x88:20, 0x89:32, 0x8A:36}
CONTAINERS = {0x40,0x41,0x42,0x46,0x48,0x70,0x10,0x20,0x30,0x3}
def parse(path):
d = open(path,"rb").read()
patches = []
def read_chunk(p, end):
if p+2 > end: return None
tw = struct.unpack_from("<H", d, p)[0]
cid = tw & 0x2fff
lw = 4 if (tw & 0x8000) else (2 if (tw & 0x4000) else 1)
if p+2+lw > end: return None
if lw==1: ln = d[p+2]
elif lw==2: ln = struct.unpack_from("<H", d, p+2)[0]
else: ln = struct.unpack_from("<I", d, p+2)[0]
body = p+2+lw
return cid, body, ln, body+ln
def walk(p, end, patch):
while p < end:
r = read_chunk(p, end)
if not r: break
cid, body, ln, nxt = r
if cid == 0x42:
patch = {"mtl":"", "punch":False, "geoms":[]}
patches.append(patch)
walk(body, nxt, patch)
elif cid == 0x2030 and patch is not None:
patch["mtl"] = d[body:body+ln].split(b'\0')[0].decode('latin1','ignore')
elif cid == 0x2037 and patch is not None:
if b"PUNCH" in d[body:body+ln]: patch["punch"] = True
elif cid in VSTRIDE and patch is not None:
st = VSTRIDE[cid]
n = ln // st
verts = []
for i in range(n):
o = body + i*st
verts.append(struct.unpack_from("<fff", d, o))
patch["geoms"].append({"verts":verts, "faces":[], "vt":cid})
elif cid == 0x47 and patch is not None and patch["geoms"]:
g = patch["geoms"][-1]
n = ln//4
idx = [struct.unpack_from("<I", d, body+i*4)[0] for i in range(n)]
for f in range(0,(n//3)*3,3):
g["faces"].append(tuple(idx[f:f+3]))
elif cid == 0x4d and patch is not None and patch["geoms"]:
g = patch["geoms"][-1]
ppf = d[body]; n=(ln-1)//4
idx=[struct.unpack_from("<I",d,body+1+i*4)[0] for i in range(n)]
for f in range(0,(n//ppf)*ppf,ppf):
poly=[idx[f+j] for j in range(ppf)]
for k in range(1,ppf-1):
g["faces"].append((poly[0],poly[k],poly[k+1]))
elif cid in CONTAINERS:
walk(body, nxt, patch)
p = nxt
walk(8, len(d), None)
return patches
def bbox(verts):
V = np.array(verts)
return V.min(0), V.max(0)
for path in sys.argv[1:]:
print("==", path)
for pi, pt in enumerate(parse(path)):
if not pt["punch"] or not pt["geoms"]: continue
print(" patch%d mtl=%s %d sub-geometries" % (pi, pt["mtl"][:40], len(pt["geoms"])))
for gi, g in enumerate(pt["geoms"]):
lo, hi = bbox(g["verts"])
print(" g%d: %d verts %d tris bbox [%7.3f %7.3f %7.3f] .. [%7.3f %7.3f %7.3f]"
% (gi, len(g["verts"]), len(g["faces"]), *lo, *hi))
if len(pt["geoms"]) == 3:
v2 = np.array(pt["geoms"][1]["verts"]); v3 = np.array(pt["geoms"][2]["verts"])
same = v2.shape == v3.shape and np.allclose(v2, v3, atol=1e-6)
f2 = pt["geoms"][1]["faces"]; f3 = pt["geoms"][2]["faces"]
print(" g1 vs g2(twin?): verts identical=%s faces identical=%s" % (same, f2==f3))
if not same and v2.shape == v3.shape:
dd = np.abs(v2-v3).max()
print(" max coord delta = %g" % dd)
# g0 vs g1 proximity: distance of each g1 vert to g0 bbox
v1 = np.array(pt["geoms"][0]["verts"])
lo0, hi0 = v1.min(0), v1.max(0)
lo1, hi1 = v2.min(0), v2.max(0)
print(" mask(g0) bbox vs drawn(g1) bbox: g0 contains g1: %s ; g1 contains g0: %s"
% (bool(np.all(lo0 <= lo1+1e-4) and np.all(hi0 >= hi1-1e-4)),
bool(np.all(lo1 <= lo0+1e-4) and np.all(hi1 >= hi0-1e-4))))