diff --git a/game/reconstructed/mech4.cpp b/game/reconstructed/mech4.cpp index ae60ad5..e01a0b8 100644 --- a/game/reconstructed/mech4.cpp +++ b/game/reconstructed/mech4.cpp @@ -1607,9 +1607,19 @@ void const void *parent = (pseg != 0) ? (const void *)pseg->GetParent() : 0; int parentIdx = (pseg != 0) ? pseg->GetParentIndex() : -99; JointSubsystem *js = sm->GetJointSubsystem(); + // #148: the SHOOTER's live torso twist AT THIS INSTANT. The + // [torso-copy] probe samples every 120th call, so its "first + // non-zero" tells you when it first SAMPLED, not when the twist + // started -- that is exactly the artifact that made the earlier + // "the peer had no twist to carry" reading look right. Read the + // cell directly instead, so twistDelta and the twist that should + // be driving it are on the SAME line. + extern Scalar *BTGetTorsoTwistAddr(Subsystem *torso); + Scalar *twp = BTGetTorsoTwistAddr(sm->GetTorsoSubsystem()); DEBUG_STREAM << "[launchframe] " << (sm->GetInstance() == Entity::ReplicantInstance ? "REPLICANT" : "master ") + << " liveTwist=" << (twp != 0 ? (float)*twp : -99.0f) << " seg=" << muzzle_seg << " segResolved=" << haveFrame << " segYaw=" << segYaw << " bodyYaw=" << bodyYaw << " twistDelta=" << dYaw @@ -7940,7 +7950,33 @@ void continue; if (i != 0) // slot 0 = the mapper (task #7) ++subsystemsPresent; - if (!subsystem->IsNonReplicantExecutable()) + // #148 -- THE INSTANCE BRANCH. Entity::Perform (ENTITY.cpp:733-793, + // real engine source [T0]) picks the predicate by instance: + // if (GetInstance() != ReplicantInstance) IsNonReplicantExecutable() + // else IsReplicantExecutable() + // and they differ exactly on the replicant case (SIMULATE.h:195-206): + // NonReplicant: (flags & DontExecuteFlag) == 0 + // Replicant : (flags & DontExecuteFlag) == 0 + // || lastUpdate >= lastPerformance + // `ExecuteOnUpdate()` SETS DontExecuteFlag -- it means "do not tick me + // every frame, tick me when an UPDATE ARRIVES". This loop used the + // NonReplicant predicate for every mech, so on a REPLICANT any + // ExecuteOnUpdate subsystem never ran at all, no matter how many + // records arrived for it. + // + // Measured (scratchpad/night13/missileframe2.sh): the peer's copy + // TORSO received its first record at log line 205 but its Performance + // did not run until line 1014 -- ~800 lines of arriving twist data + // integrated by nobody, so the peer's torso sat at 0 and its missiles + // launched along the body facing (the tail of #141). The records + // themselves were fine: they are sent on RATE CHANGE (the sweep's + // direction flips -- atUpd +/-2.39 with rate flipping sign), and the + // peer dead-reckons `atUpd + rate * elapsed` between them. + const Logical execOK = + (GetInstance() != Entity::ReplicantInstance) + ? subsystem->IsNonReplicantExecutable() + : subsystem->IsReplicantExecutable(); + if (!execOK) continue; // The controls-mapping subsystem (roster slot 0 via Mech::SetMapping diff --git a/game/reconstructed/torso.cpp b/game/reconstructed/torso.cpp index 2482ca8..98ddab6 100644 --- a/game/reconstructed/torso.cpp +++ b/game/reconstructed/torso.cpp @@ -823,6 +823,13 @@ void << " vel=" << twistVelocity << " lastUpd=" << lastUpdateTime << " now=" << GetCurrentTime() + // #148: ComputeTargetTwist ends in Min(limitLeft)/Max(limitRight). + // If the COPY's limits never loaded they are 0/0, which pins + // targetTwist to EXACTLY 0 no matter what the record carried -- + // which is what a peer stuck at zero twist would look like. + << " limL=" << horizontalLimitLeft + << " limR=" << horizontalLimitRight + << " enab=" << (int)horizontalEnabled << " copy=" << (int)isDamagedCopy << std::endl; } }