#124 CORRECTION: aimed hull hits resolve the struck dz_* PANEL -- players were right
The 08-03 zone rule (struck SEGMENT's SKL dzone, ALWAYS -> every aimed torso hit = center torso) was wrong, and era players' pushback caught it. Byte-level proof: MAD_TOR.BGF zone-tags the hull PER PANEL (dz_utorso x36, dz_ltorso/rtorso x18, dz_dtorso x16, all four rear panels, searchlight); the dpl hit result kept GEOGROUP granularity (dplHitGeoGroup, T0); and the binary's segment->zone map @49db20 has NO runtime caller (raw call-scan: sole caller = CreateStreamedDamageZone, load time) -- no segment-level collapse mechanism exists. Oracle's night-10 'only LCT gets hits' audit was the BUG's fingerprint, not the pod's design. Fix: MechSegmentPick attributes the struck triangle to its draw op (index range) and takes the op's .DZM-bound zone -- the #87 armour-darkening bindings, the same authored patch->zone mapping that already paints the panels -- with the segment dzone as the untagged fallback. ZoneAimPoint now aims hull zones at their patch CENTROIDS (all hull zones previously shared the chest cull-center), which also upgrades the zone walker. Bench (2-node zone-walk vs spinning madcat, zonewalk_madcat.sh): every hull panel resolves individually -- utorso 11/12 in-zone, no L/R mirroring, all four rear panels register; misses are the panel facing the shooter mid-spin (correct geometry, not misattribution). Victim applied 254 hits spread across every panel family. Was 102/102 hull aims -> dtorso before the fix. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
98234c8b15
commit
1d3616cceb
@@ -1711,17 +1711,22 @@ int
|
||||
int triBestZone = -1;
|
||||
int triBestSeg = -1;
|
||||
int triHit = 0;
|
||||
d3d_OBJECT *triBestObj = 0; // #124-PATCH: winning object + tri
|
||||
size_t triBestK = 0; // (triangle ordinal in its IB)
|
||||
|
||||
// #124 RESOLVED against the authored data (2026-08-03): the pick's zone is
|
||||
// the struck DRAWN segment's own SKL dzone, ALWAYS -- the 1995 model (dpl
|
||||
// scene intersection -> DCS -> segment dzone; combat-damage.md "the #73
|
||||
// pick dig" [T0+T1]). The madcat's pickable skeleton authors ONE hull
|
||||
// piece (jointshakey -> dtorso) and no ltorso/rtorso/utorso/rear/door
|
||||
// segments -- aimed fire at any torso panel authentically designates
|
||||
// dtorso; the side/rear/upper panels are UNAIMED-path territory (missiles/
|
||||
// splash through the cylinder). The night-10 audit table is this model's
|
||||
// exact fingerprint. (An earlier shared-carrier -> cylinder override here
|
||||
// was an embellishment and is removed.)
|
||||
// #124 CORRECTED (2026-08-04, field pushback vindicated): the pick's zone
|
||||
// is the struck PATCH's authored dz_* tag, with the segment's SKL dzone as
|
||||
// the fallback for untagged patches. The earlier "struck segment's dzone,
|
||||
// ALWAYS" model collapsed the hull to dtorso -- but the art zone-tags the
|
||||
// hull PER PANEL (MAD_TOR carries dz_utorso/ltorso/rtorso/dtorso + all four
|
||||
// rear panels + searchlight across its patches), the dpl hit result kept
|
||||
// GEOGROUP granularity (dplHitInstance/DCS/GeoGroup/Geometry), and the
|
||||
// binary's segment->zone map (@49db20) has NO runtime caller (sole caller =
|
||||
// CreateStreamedDamageZone, load-time) -- there was nothing to do the
|
||||
// segment-level collapse with. Era players' "I stripped the left torso in
|
||||
// the pod" stands. The triangle -> patch attribution below reuses the .DZM
|
||||
// zone->material bindings (#87's armour darkening -- the same authored
|
||||
// mapping that already paints those panels per-zone).
|
||||
|
||||
std::map<int, MechRenderTree::SegPick>::iterator sp;
|
||||
for (sp = it->second.segPick.begin(); sp != it->second.segPick.end(); ++sp)
|
||||
@@ -1799,8 +1804,10 @@ int
|
||||
if (wt >= 0.0f && wt < triBestT)
|
||||
{
|
||||
triBestT = wt;
|
||||
triBestZone = sp->second.zone;
|
||||
triBestZone = sp->second.zone; // segment dzone (fallback)
|
||||
triBestSeg = sp->first;
|
||||
triBestObj = obj;
|
||||
triBestK = k; // first index of the tri
|
||||
triHit = 1;
|
||||
}
|
||||
}
|
||||
@@ -1824,8 +1831,31 @@ int
|
||||
if (triHit)
|
||||
{
|
||||
bestT = triBestT;
|
||||
bestZone = triBestZone; // the struck segment's dzone
|
||||
bestZone = triBestZone; // segment dzone unless a patch tags it
|
||||
hitAny = 1;
|
||||
// #124-PATCH: attribute the struck triangle to its DRAW OP (each op is
|
||||
// an index RANGE in the same IB the pick mesh copied) and take the
|
||||
// op's .DZM-bound zone -- the authored dz_* panel. dmgBinds is the
|
||||
// #87 armour mapping: {obj, op, zone} for every material a zone paints.
|
||||
if (triBestObj != 0)
|
||||
{
|
||||
const std::vector<MechRenderTree::DmgBind> &binds = it->second.dmgBinds;
|
||||
for (size_t b = 0; b < binds.size(); ++b)
|
||||
{
|
||||
if (binds[b].obj != triBestObj)
|
||||
continue;
|
||||
L4DRAWOP *op = triBestObj->GetDrawOp(binds[b].op);
|
||||
if (op == 0 || op->bgfPrimCount <= 0)
|
||||
continue;
|
||||
size_t first = (size_t)op->bgfStartIndex;
|
||||
size_t count = (size_t)op->bgfPrimCount * 3;
|
||||
if (triBestK >= first && triBestK < first + count)
|
||||
{
|
||||
bestZone = binds[b].zone; // the struck PANEL's zone
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
(void)triBestSeg;
|
||||
}
|
||||
|
||||
@@ -1910,6 +1940,53 @@ int
|
||||
std::map<Entity*, MechRenderTree>::iterator it = mMechRenderTrees.find(mech);
|
||||
if (it == mMechRenderTrees.end() || it->second.wrecked)
|
||||
return 0;
|
||||
|
||||
// #124-PATCH: a zone with .DZM-bound panel patches aims at the PATCH
|
||||
// CENTROID (the authored dz_* panel -- upper/left/right/rear torso are
|
||||
// distinct patch clusters on ONE torso object, so the old cull-center
|
||||
// answer aimed every hull zone at the same chest point). Centroid =
|
||||
// average of the bound ops' triangle vertices, world-transformed.
|
||||
{
|
||||
double acc[3] = { 0, 0, 0 };
|
||||
long nAcc = 0;
|
||||
const std::vector<MechRenderTree::DmgBind> &binds = it->second.dmgBinds;
|
||||
for (size_t b = 0; b < binds.size(); ++b)
|
||||
{
|
||||
if (binds[b].zone != zone || binds[b].obj == NULL)
|
||||
continue;
|
||||
d3d_OBJECT *obj = binds[b].obj;
|
||||
L4DRAWOP *op = obj->GetDrawOp(binds[b].op);
|
||||
if (op == 0 || op->bgfPrimCount <= 0)
|
||||
continue;
|
||||
BTPickMesh *pm = BTGetPickMesh(obj);
|
||||
if (pm == 0)
|
||||
continue;
|
||||
D3DXMATRIX l2w = obj->GetLocalToWorld();
|
||||
size_t first = (size_t)op->bgfStartIndex;
|
||||
size_t last = first + (size_t)op->bgfPrimCount * 3;
|
||||
if (last > pm->idx.size())
|
||||
continue;
|
||||
for (size_t k = first; k < last; ++k)
|
||||
{
|
||||
unsigned int vi = pm->idx[k];
|
||||
if ((size_t)vi * 3 + 2 >= pm->pos.size())
|
||||
continue;
|
||||
D3DXVECTOR3 lp(pm->pos[vi*3], pm->pos[vi*3+1], pm->pos[vi*3+2]);
|
||||
D3DXVECTOR3 wp;
|
||||
D3DXVec3TransformCoord(&wp, &lp, &l2w);
|
||||
acc[0] += wp.x; acc[1] += wp.y; acc[2] += wp.z;
|
||||
++nAcc;
|
||||
}
|
||||
}
|
||||
if (nAcc > 0)
|
||||
{
|
||||
out3[0] = (float)(acc[0] / nAcc);
|
||||
out3[1] = (float)(acc[1] / nAcc);
|
||||
out3[2] = (float)(acc[2] / nAcc);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
d3d_OBJECT *best = 0;
|
||||
std::map<int, MechRenderTree::SegPick>::iterator sp;
|
||||
for (sp = it->second.segPick.begin(); sp != it->second.segPick.end(); ++sp)
|
||||
|
||||
Reference in New Issue
Block a user