Files
BT412/scratchpad/cop_coverage.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

61 lines
3.0 KiB
Python

import struct, math, numpy as np
from PIL import Image
data=open("content/VIDEO/GEO/BLX_COP.BGF","rb").read()
verts=[]; faces=[]
def addpoly(idx,b):
for k in range(1,len(idx)-1): faces.append((b+idx[0],b+idx[k],b+idx[k+1]))
curbase=[0]
def parse(p,end):
while p+3<=end:
tw=struct.unpack_from("<H",data,p)[0]; p+=2
cid=tw&0x2fff; lw=4 if(tw&0x8000)else(2 if(tw&0x4000)else 1)
if p+lw>end:return
ln=data[p] if lw==1 else(struct.unpack_from("<H",data,p)[0] if lw==2 else struct.unpack_from("<I",data,p)[0])
p+=lw
if p+ln>end:return
if cid==0x88:
curbase[0]=len(verts)
for i in range(ln//20):
o=p+i*20; x,y,z=struct.unpack_from("<fff",data,o); verts.append((x,y,z))
elif cid==0x47:
n=ln//4; idx=[struct.unpack_from("<I",data,p+i*4)[0] for i in range(n)]
for f in range(0,(n//3)*3,3): faces.append((curbase[0]+idx[f],curbase[0]+idx[f+1],curbase[0]+idx[f+2]))
elif cid==0x4d:
if ln>=1:
ppf=data[p]; n=(ln-1)//4
idx=[struct.unpack_from("<I",data,p+1+i*4)[0] for i in range(n)]
for f in range(0,(n//ppf)*ppf,ppf): addpoly([idx[f+j] for j in range(ppf)],curbase[0])
if cid in {0x3,0x70,0x40,0x41,0x42,0x46,0x48,0x10,0x20,0x30}: parse(p,p+ln)
p+=ln
if cid==0x5:break
parse(8,len(data))
V=np.array(verts,dtype=float)
def cover(eye,look,fov=95,W=360,H=280):
eye=np.array(eye,float); fwd=np.array(look,float)-eye; fwd/=np.linalg.norm(fwd)
up=np.array([0,1,0.0]); right=np.cross(fwd,up); right/=np.linalg.norm(right); upv=np.cross(right,fwd)
f=(H/2)/math.tan(math.radians(fov)/2); img=np.zeros((H,W))
for (a,b,c) in faces:
tri=V[[a,b,c]]
cam=[(np.dot(pt-eye,right),np.dot(pt-eye,upv),np.dot(pt-eye,fwd)) for pt in tri]
if any(cz<=0.03 for _,_,cz in cam):continue
pts=[(W/2+f*cx/cz,H/2-f*cy/cz) for cx,cy,cz in cam]
xs=[p[0] for p in pts];ys=[p[1] for p in pts]
mnx=max(int(min(xs)),0);mxx=min(int(max(xs))+1,W-1);mny=max(int(min(ys)),0);mxy=min(int(max(ys))+1,H-1)
if mnx>=mxx or mny>=mxy:continue
(x0,y0),(x1,y1),(x2,y2)=pts
den=((y1-y2)*(x0-x2)+(x2-x1)*(y0-y2))
if abs(den)<1e-9:continue
ys2,xs2=np.mgrid[mny:mxy+1,mnx:mxx+1];xf=xs2+.5;yf=ys2+.5
l0=((y1-y2)*(xf-x2)+(x2-x1)*(yf-y2))/den;l1=((y2-y0)*(xf-x2)+(x0-x2)*(yf-y2))/den;l2=1-l0-l1
m=(l0>=0)&(l1>=0)&(l2>=0)
img[ys2[m],xs2[m]]=1
return (img*255).astype(np.uint8)
# eye near origin (rear), try forward -Z and +Z, and from just inside each end
tiles=[]
for name,eye,look in (("O->-Z",(0,-0.3,0),(0,-0.3,-4)),("O->+Z",(0,-0.3,0),(0,-0.3,4)),
("rear-Z",(0,-0.3,-0.9),(0,-0.3,-4)),("rear+Z",(0,-0.3,-3.7),(0,-0.3,-1))):
c=cover(eye,look); tiles.append(np.stack([c,c,c],axis=-1))
Image.fromarray(np.concatenate(tiles,axis=1)).save("scratchpad/cop_cover.png")
print("saved cop_cover.png [O->-Z | O->+Z | rear-Z | rear+Z] (white=covered by a face, black=open)")