diff --git a/game/reconstructed/mech4.cpp b/game/reconstructed/mech4.cpp index 91df319..32498bd 100644 --- a/game/reconstructed/mech4.cpp +++ b/game/reconstructed/mech4.cpp @@ -790,8 +790,26 @@ void EntitySegment *seg = m->GetSegment(segIndex); // owner+0x300 table, GetNth(index) if (seg != 0) { - AffineMatrix mw; - mw.Multiply(seg->GetSegmentToEntity(), m->localToWorld); // segment -> world (== mech4 gun-port path) + // #141 -- THE BINARY GOES THROUGH FUN_00424da8, AND SO MUST WE. + // @004b9948 ends in `FUN_00424da8(owner, segment, out)`, which is + // JointedMover::GetSegmentToWorld instruction-for-instruction: + // iVar1 = FUN_00417ab4(param_1 + 0x31c); // GetJointSubsystem() + // if (*(int *)(iVar1 + 0xfc) != 0) { // AreJointsModified() + // ...walk owner+0x300 setting seg+0xc = 1... // ModifySegment() + // *(int *)(iVar1 + 0xfc) = 0; // ModifyJoints(False) + // } + // FUN_0040b104(out, FUN_004244dc(seg), owner+0xd0); // x localToWorld + // So in the 1995 image EVERY muzzle query performs the joints->segments + // refresh. This port hand-composed GetSegmentToEntity() x localToWorld + // and skipped it -- and GetSegmentToEntity only recomputes when + // segmentModified is already set (SEGMENT.cpp:262), so it returned a + // stale cache. On the MASTER that was invisible (the render pass + // refreshes the local mech every frame); on a REPLICANT nothing did, so + // peer muzzles sat at the BIND POSE -- the missile launched along the + // leg facing (#141). Use the engine accessor; do NOT force the dirty + // flag, the binary does not. + LinearMatrix mw; + m->GetSegmentToWorld(*seg, &mw); out = mw; // Point3D = matrix W_Axis translation } else @@ -1534,14 +1552,14 @@ void // GetSegmentToWorld mark every segment dirty so the whole // chain re-derives from the CURRENT joint angles. Costs one // segment-table walk per salvo. - // MEASURED: this alone takes the replicant from 0% to 64% of - // salvos carrying the twist. Also forcing every per-joint - // `jointModified` flag (GetSegmentToParent's own gate, - // SEGMENT.cpp:196) was tried and moved the number by NOTHING - // -- 64% either way -- so the residual 36% is a different - // cause, not per-joint cache staleness. Kept the cheap form. - if (JointSubsystem *jsf = sm->GetJointSubsystem()) - jsf->ModifyJoints(True); + // NO forced dirty flag here. An earlier pass set + // ModifyJoints(True) before this read; it bought 64% of + // salvos but it is NOT what the binary does -- @00424da8 + // tests AreJointsModified() and never sets it. The authentic + // refresh happens in the MUZZLE query (GetMuzzlePoint -> + // @00424da8), which the launcher calls just above this, so by + // the time we compose the launch frame the segment cache is + // already current. See BTResolveWeaponMuzzle. LinearMatrix mw; sm->GetSegmentToWorld(*seg, &mw); mw.GetFromAxis(X_Axis, &ax);