#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
+21
-15
@@ -102,21 +102,27 @@ the DCS = the struck SEGMENT → its `dzone` (the SKL segment→zone map, `GetSe
|
||||
`targetDamageZone`. The cylinder lottery (STEP 6) was only ever the UNAIMED path. The port's
|
||||
whole-mech box pick + "STEP-6 zone under the boresight" funnels AIMED fire through the unaimed
|
||||
lottery — which is exactly the night-6 report (#73): aim at the arm, get the spray.
|
||||
**COMPLETED 2026-08-03 (#124, the zone-walk matrix): the pick is now TRIANGLE-ACCURATE and the
|
||||
zone rule is settled against the authored skeleton [T0 data + T2 matrix].** The sphere
|
||||
approximation measurably mis-picked (aim dead-on dtorso -> picked rgun/ruleg; its own comments
|
||||
admitted a foot could be unhittable behind its own knee) — `MechSegmentPick` now runs a sphere
|
||||
PRE-FILTER then Moller-Trumbore nearest-hit over the threaded segments' posed meshes (CPU
|
||||
triangle cache from each object's own BGF buffers, `BTGetPickMesh`). **The zone is the struck
|
||||
segment's SKL dzone, ALWAYS**: the madcat's pickable skeleton authors 18 segments — hip,
|
||||
shoulders, guns, thighs, knees, ankles+toes, and ONE hull piece (`jointshakey` -> dtorso). No
|
||||
ltorso/rtorso/utorso/rear/door/searchlight segments exist, so aimed fire at any torso panel
|
||||
authentically designates CENTER TORSO — the side/rear/upper panels strip only via the UNAIMED
|
||||
path (missiles/splash/rams through the cylinder). A brief shared-carrier->cylinder override was
|
||||
an embellishment and was removed same-day. Final matrix (two-node, spinning target, servo
|
||||
walker): limbs 55/70 in-zone (legs/feet 6/6), hull-family aims -> dtorso 56/69, victim
|
||||
consumption matching. **The night-10 field audit table is this model's fingerprint** — 'only
|
||||
LCT gets hits' from aimed energy IS the pod's design. Bench: [[test-harness]] §zone-walk.
|
||||
**COMPLETED 2026-08-03 (#124, the zone-walk matrix): the pick is TRIANGLE-ACCURATE** [T2 matrix].
|
||||
The sphere approximation measurably mis-picked (aim dead-on dtorso -> picked rgun/ruleg) —
|
||||
`MechSegmentPick` runs a sphere PRE-FILTER then Moller-Trumbore nearest-hit over the threaded
|
||||
segments' posed meshes (CPU triangle cache from each object's own BGF buffers, `BTGetPickMesh`).
|
||||
**⚠ CORRECTED 2026-08-04 (field pushback from era players — they were RIGHT): the 08-03 zone rule
|
||||
("the struck segment's SKL dzone, ALWAYS" -> every aimed torso hit = CENTER TORSO) was WRONG.**
|
||||
The authentic rule: **the pick's zone is the struck PATCH's authored `dz_*` tag** — the art
|
||||
zone-tags the hull PER PANEL (`MAD_TOR.BGF` carries `dz_utorso`×36 / `dz_ltorso`×18 /
|
||||
`dz_rtorso`×18 / `dz_dtorso`×16 + all four rear panels + `dz_searchlight` across its patches
|
||||
[T1 bytes]); the dpl hit result kept GEOGROUP granularity (`dplHitGeoGroup` in the renderer's
|
||||
result block [T0]); and the binary's segment->zone map `@49db20` has NO runtime caller (sole
|
||||
caller = `CreateStreamedDamageZone`, load-time — raw call-scan [T1]), so no segment-level
|
||||
collapse mechanism even exists. Aimed fire in the pod stripped the exact hull panel under the
|
||||
reticle. The segment-level model looked airtight because LIMBS agree at both granularities (one
|
||||
zone per limb mesh) — the collapse only bit the hull, and Oracle's night-10 audit ("only LCT gets
|
||||
hits") was the BUG's fingerprint, not the pod's design. Port: `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 those
|
||||
panels), segment dzone as the untagged-patch fallback; `ZoneAimPoint` aims hull zones at their
|
||||
patch CENTROIDS so the zone walker can exercise per-panel. Bench: [[test-harness]] §zone-walk
|
||||
(madcat variant, scratchpad/night11/zonewalk_madcat.sh).
|
||||
**TWIST-SIGN VERIFIED (2026-08-04) [T2 live]:** the frame adapter's inferred angular flip
|
||||
(`SelectSlice: theta -= TorsoHeading()`, vs the binary's `+=` before the z-reflection) is
|
||||
CORRECT — bench: target torso pinned 0/+140°/−140° (`BT_FORCE_TWIST`, sim-level
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
#!/usr/bin/env bash
|
||||
# #124-PATCH verify: per-panel hull zone resolution vs a MADCAT target.
|
||||
# cured by the #124 triangle-pick work? The zone-walk matrix vs a LOKI target:
|
||||
# shooter servo-aims every zone (feet included) through the real reticle path,
|
||||
# 3 laser pulses each; read A's [walk] FIRE lines vs B's [dmghit] zone= lines.
|
||||
# Same rig as scratchpad/night10/zonewalk.sh, target vehicle=madcat, ~7 min tail.
|
||||
set -x
|
||||
. /c/git/bt411/scratchpad/night6/bench_common.sh
|
||||
cd /c/git/bt411/content || exit 1
|
||||
bt_assert_player_env
|
||||
taskkill //F //IM btl4.exe > /dev/null 2>&1
|
||||
sleep 2
|
||||
rm -f zwm_a.log zwm_b.log
|
||||
bt_expert_egg MP.EGG ZWM.EGG
|
||||
sed -i "s/^map=.*/map=grass/; s/^time=.*/time=day/" ZWM.EGG
|
||||
# pilot 1 (node A, shooter) = madcat (proven servo constraints);
|
||||
# pilot 2 (node B, target) = LOKI (the #92 chassis)
|
||||
python - << 'EOF'
|
||||
lines = open('ZWM.EGG').read().splitlines(True)
|
||||
n = 0
|
||||
for i, l in enumerate(lines):
|
||||
if l.startswith('vehicle='):
|
||||
n += 1
|
||||
lines[i] = 'vehicle=madcat\n' if n == 1 else 'vehicle=madcat\n'
|
||||
open('ZWM.EGG', 'w').writelines(lines)
|
||||
print('vehicles set:', n)
|
||||
EOF
|
||||
|
||||
BT_SPIN_SELF=15 BT_DMG_LOG=1 BT_DEATH_LOG=1 BT_MP_LOG=1 \
|
||||
bt_launch zwm_b.log ZWM.EGG 0x0C -net 1601
|
||||
sleep 2
|
||||
BT_ZONE_WALK=6 BT_PICK_LOG=1 BT_GOTO=enemy BT_GOTO_STOP=90 BT_KEY_NOFOCUS=1 \
|
||||
BT_DMG_LOG=1 BT_RANGE_LOG=1 BT_MP_LOG=1 \
|
||||
bt_launch zwm_a.log ZWM.EGG 0x03 -net 1501
|
||||
sleep 5
|
||||
python ../tools/btconsole.py ZWM.EGG 127.0.0.1:1501 127.0.0.1:1601 > zwm_relay.log 2>&1 &
|
||||
sleep 400 # ~7 min: enough for a full zone walk incl. both feet
|
||||
Reference in New Issue
Block a user