Torso: the TWIST goes LIVE -- electrical watchdog chain, centered crosshair, coherent controls (task #57/#58)
The MadCat torso twists, the view turns with it, and targeting follows. Three reconstruction fronts closed: THE ELECTRICAL WATCHDOG CHAIN (why the torso never powered up): - PowerWatcher::UpdateWatch reconstructed (@004b181c, the REAL registered Performance -- PTR @0050f5fc; Ghidra missed the fn start): the watchdog MIRRORS the watched subsystem's electrical level (+0x278), brownout downgrade when gen output <= minVoltage% x rated. @004b1804 relabeled ResetToInitialState (slot 10) -- the old "Simulation" tag was wrong. - The factory watcher-CONNECT pass reconstructed (vtable slot +0x38, @004aee2c/@004b1a40 byte-identical, recovered from raw exe bytes): watchedLink.Add(roster[watchedSubsystem]) on the master node. Was the SubProxy::Start() no-op -- every watchdog sat at 0 forever. - MinVoltageScale = 0.01 (a 10-byte x87 literal @0x4b1924; was 1.0f = permanent brownout) and PowerWatcher's Derivation chains its REAL base HeatWatcher (the HeatableSubsystem stand-in broke IsDerivedFrom for the whole Torso/Searchlight/ThermalSight family). - KB correction swept: derivation tag 0x50e604 = HEATWATCHER (not "HeatSink"); the btl4gaug heat-widget gate now tests it via the BTIsHeatWatcher bridge. THE CROSSHAIR (task #58 forensics, 6-agent workflow + live probes): - The VIEW is TORSO-MOUNTED: jointtorso -> jointeye -> siteeyepoint in every twist-capable .SKL; the camera + canopy ride the same hinge subtree through HingeRenderable's live matrix-stack compose -- ALREADY WORKING in the port. The crosshair stays screen-centered (center IS the boresight); the twist reads on the tape carets/compass/radar. - The real bug was the port's gBTAimX = tan(twist) slew (the falsified "body-mounted view" model): the camera already carried the twist, so the crosshair counter-slid to hull-forward and the fire ray with it. Deleted; the pick ray inherits the twist from the yawing eye basis. - Two instrumentation traps documented (chase-eye-as-default-camera, BT_FORCE_TORSO clobbering real joints -> the hook now only fills unresolved ones); an over-correcting explicit eye compose was added on those false readings and retired the same day. CONTROLS + REPLICATION: - Q/E spring-center on release (the axis is a twist-RATE demand; the old hold-deflection model drifted forever); X also zeroes the axis and pulses the authentic torso Recenter (@004b6918). M cycles control mode via the real CycleControlMode body. - Torso update-record DIRECTION fixed: engine truth is Write=serialize / Read=apply; @004b6a78 is the READ (was mislabeled Write) and the missing WRITE @004b6a1c recovered from raw disasm (recordLength 0x1C, twist/vel/rate at +0x10/14/18) -- kills the replicant's 0xCDCDCDCD -140-degree ghost twist. - Marching-ghost desync: 4 Standing-case guards zero stale reverse cycleSpeed (negative cadence passed the <= ZeroSpeed stop gate). - Kill credit rerouted to the OBSERVED killer (lastInflictingID -> killer's player link) -- kills count, target K/D populates. KB: subsystems.md (watcher chain), multiplayer.md (record direction), combat-damage.md + gauges-hud.md + cockpit-view.md (torso-mounted view re-correction), decomp-reference.md (new addresses + tag fix), open-questions.md (dead capability-roster loops 2-4, snapshot CD read). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5
parent
065c114590
commit
02cdfd6576
@@ -1494,12 +1494,31 @@ void BTPostKillScore(Entity *victim, Scalar damage) // Step 7: KILL (+ MP deat
|
||||
{
|
||||
return;
|
||||
}
|
||||
BTPlayer *local_player = (BTPlayer *)application->GetMissionPlayer();
|
||||
if (local_player != 0)
|
||||
// OBSERVED-KILL credit (MP KILLS fix, 2026-07-13; the symmetric twin of
|
||||
// BTPlayerCountObservedDeath): every node maintains LOCAL score copies,
|
||||
// so the kill must land on the KILLER's player-link object -- the same
|
||||
// object the PilotList gauge reads (the roster resolves pilots via the
|
||||
// mechs' GetPlayerLink()). The old GetMissionPlayer() dispatch credited
|
||||
// a copy the scoreboard never displays (kills stuck at 0 while deaths --
|
||||
// which already flow through the player link -- counted; user-reported).
|
||||
// Resolving the killer from the victim's lastInflictingID also credits
|
||||
// the REMOTE player's local copy when THEY score ("target's kills
|
||||
// display 0") -- each node witnesses the death transition and tallies
|
||||
// its own copies self-consistently.
|
||||
BTPlayer *killer_player = 0;
|
||||
if (application->GetHostManager() != 0)
|
||||
{
|
||||
// KillScore (scoreType 2): senderMechID MUST be the VICTIM (!= our mech) so
|
||||
// the local player is credited via `this->killCount++` -- if it were our own
|
||||
// mech, ScoreMessageHandler takes the suicide branch (award negated, no kill).
|
||||
Entity *killer = application->GetHostManager()->GetEntityPointer(
|
||||
((Mech *)victim)->lastInflictingID);
|
||||
extern int BTIsRegisteredMech(Entity *e);
|
||||
if (killer != 0 && killer != victim && BTIsRegisteredMech(killer))
|
||||
killer_player = (BTPlayer *)((Mech *)killer)->GetPlayerLink();
|
||||
}
|
||||
if (killer_player != 0)
|
||||
{
|
||||
// KillScore (scoreType 2): senderMechID MUST be the VICTIM (!= the
|
||||
// receiver's mech) so the handler credits `killCount++` -- a suicide
|
||||
// (killer == victim) never reaches here.
|
||||
BTPlayer::ScoreMessage kill(
|
||||
Player::ScoreMessageID, // 0x12
|
||||
sizeof(BTPlayer::ScoreMessage),
|
||||
@@ -1507,13 +1526,12 @@ void BTPostKillScore(Entity *victim, Scalar damage) // Step 7: KILL (+ MP deat
|
||||
0.0f, // scoreAward (killBonus; 0 for bring-up)
|
||||
damage, // damageAmount (killing-blow)
|
||||
victim->GetEntityID()); // senderMechID = victim
|
||||
local_player->Dispatch(&kill);
|
||||
killer_player->Dispatch(&kill);
|
||||
if (getenv("BT_SCORE_LOG"))
|
||||
DEBUG_STREAM << "[score] *** KILL *** localPlayer=" << (void *)local_player
|
||||
<< " killCount=" << local_player->GetKillCount()
|
||||
<< " deaths=" << local_player->GetDeaths()
|
||||
<< " score=" << (Scalar)local_player->GetScore()
|
||||
<< " rank=" << local_player->GetRanking() << "\n" << std::flush;
|
||||
DEBUG_STREAM << "[score] *** KILL *** killerPlayer=" << (void *)killer_player
|
||||
<< " killCount=" << killer_player->GetKillCount()
|
||||
<< " deaths=" << killer_player->GetDeaths()
|
||||
<< " score=" << (Scalar)killer_player->GetScore() << std::endl;
|
||||
}
|
||||
|
||||
// MP DEATH: credit a death to the VICTIM's own player. NULL for the solo
|
||||
|
||||
Reference in New Issue
Block a user