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
@@ -49,6 +49,9 @@
|
||||
#if !defined(MECHWEAP_HPP)
|
||||
# include <mechweap.hpp> // MechWeapon::GetClassDerivations (reticle pip)
|
||||
#endif
|
||||
#if !defined(MECHDMG_HPP)
|
||||
# include <mechdmg.hpp> // Mech__DamageZone::segmentIndex (the @004d097c dispatcher)
|
||||
#endif
|
||||
#if !defined(NOTATION_HPP)
|
||||
# include <notation.hpp>
|
||||
#endif
|
||||
@@ -368,6 +371,22 @@ HierarchicalDrawComponent*
|
||||
LinearMatrix offset_matrix;
|
||||
offset_matrix = segment->GetBaseOffset(); // [0x74]
|
||||
|
||||
// task #58 diagnostics (BT_SEG_DUMP): the draw-tree topology -- which
|
||||
// segment carries which joint, and who parents whom.
|
||||
if (getenv("BT_SEG_DUMP"))
|
||||
{
|
||||
int ji = segment->GetJointIndex();
|
||||
DEBUG_STREAM << "[seg] " << segment->GetIndex()
|
||||
<< " '" << (const char *)segment->GetName()
|
||||
<< "' parent=" << (segment->GetParent()
|
||||
? segment->GetParentIndex() : -1)
|
||||
<< " joint=" << ji
|
||||
<< " jtype=" << (ji == -1 ? -1
|
||||
: (int)joint_subsystem->GetJoint(ji)->GetJointType())
|
||||
<< " site=" << (int)(segment->IsSiteSegment() != 0)
|
||||
<< "\n" << std::flush;
|
||||
}
|
||||
|
||||
//
|
||||
// Parent renderable: root if the segment has no parent, else the
|
||||
// renderable already built for its parent segment.
|
||||
@@ -866,6 +885,116 @@ void BTRebuildMechModel(Entity *entity)
|
||||
|
||||
|
||||
//
|
||||
//#############################################################################
|
||||
// StartEntityEffectImplementation @004d097c (coverage-audit reconstruction)
|
||||
//#############################################################################
|
||||
//
|
||||
// The per-zone EFFECT DISPATCHER -- the target of the whole authentic chain:
|
||||
// MechDeathHandler's class-5 -> RendererManager::StartEntityEffect ->
|
||||
// Renderer::StartEntityEffectMessageHandler [T0: resolves the GameModel +
|
||||
// applies the ExplosionResourceTable graphic-state remap] -> this virtual.
|
||||
// The binary body (@004d097c, part_014.c:5839):
|
||||
// 1. resolve the zone -> its transform (+0x74, row 3 = the world position)
|
||||
// and its VIDEO INDEX (+0xc4->+0xc0) = the segment slot;
|
||||
// 2. look the entity up in the renderer's per-entity tree (renderer+0x3a4,
|
||||
// == our mMechRenderTrees) -> the segment's DCS = the effect SOCKET;
|
||||
// 3. SearchList(resource, VideoModelResourceType) -> walk the video-object
|
||||
// records; atoi(name) < 1000 WARNS (the authentic gate: only INDIE/psfx
|
||||
// ids attach per-zone), >= 1000 starts the effect ON the socket, tagged
|
||||
// with the owning player (+0x190 -> +0x1e0) for the stop-all sweep.
|
||||
// Port mapping: the socket attachment = BTStartPfxAttached (the emitter rides
|
||||
// the segment via BTResolveSegmentWorld each frame); the tag = the entity
|
||||
// (BTStopEntityPfx kills by entity on respawn). Layout access is by NAMED
|
||||
// members only (zone->segmentIndex; the binary micro-offsets differ on our
|
||||
// compiled classes -- the databinding trap).
|
||||
//
|
||||
void
|
||||
BTL4VideoRenderer::StartEntityEffectImplementation(
|
||||
Entity *entity,
|
||||
DamageZone *damage_zone,
|
||||
ResourceDescription::ResourceID resource_ID
|
||||
)
|
||||
{
|
||||
if (entity == NULL || damage_zone == NULL)
|
||||
{
|
||||
DEBUG_STREAM << "StartEntityEffectImplementation: no entity/zone" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// 1. the zone's segment slot (binary +0xc4->+0xc0; our named member)
|
||||
int seg_index = ((Mech__DamageZone *)damage_zone)->EffectSegmentIndex();
|
||||
|
||||
// 2. the segment's world position + frame (binary: zone transform +0x74)
|
||||
extern int BTResolveSegmentWorld(void *entity, int seg_index, float *pos3, float *rows9);
|
||||
float pos[3], rows[9];
|
||||
if (!BTResolveSegmentWorld(entity, seg_index, pos, rows))
|
||||
{
|
||||
DEBUG_STREAM << "StartEntityEffectImplementation: entity has no segment table" << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. the effect resource's VIDEO MODEL (type 10) -- the effect-number list
|
||||
Check(application);
|
||||
ResourceDescription *res = application->GetResourceFile()->SearchList(
|
||||
resource_ID, ResourceDescription::VideoModelResourceType);
|
||||
if (res == NULL)
|
||||
{
|
||||
DEBUG_STREAM << "StartEntityEffectImplementation: " << (long)resource_ID
|
||||
<< " has no video resource" << std::endl;
|
||||
return;
|
||||
}
|
||||
res->Lock();
|
||||
// VideoModel payload [T0 L4VIDEO.h L4VideoObject, RES byte-verified]:
|
||||
// int32 count + count x 32-byte records {char name[15]; pad; int type;
|
||||
// int modes; float blinkPeriod; float pctOn} -- atoi(name) = the effect id.
|
||||
const unsigned char *pay = (const unsigned char *)res->resourceAddress;
|
||||
int count = *(const int *)pay;
|
||||
if (count < 0 || count > 16)
|
||||
count = 0; // malformed -- refuse
|
||||
const unsigned char *rec = pay + 4;
|
||||
for (int i = 0; i < count; ++i, rec += 32)
|
||||
{
|
||||
char name[16];
|
||||
memcpy(name, rec, 15);
|
||||
name[15] = 0;
|
||||
int fx = atoi(name);
|
||||
if (fx < 1000)
|
||||
{
|
||||
// authentic gate (@004d097c: "< 1000" warns) -- per-zone effects
|
||||
// are INDIE/psfx-band only
|
||||
DEBUG_STREAM << "StartEntityEffectImplementation: non-INDIE effect "
|
||||
<< fx << " in zone effect list" << std::endl;
|
||||
continue;
|
||||
}
|
||||
extern void BTStartPfxAttached(int, void *, int, float, float, float, const float *);
|
||||
BTStartPfxAttached(fx - 1000, (void *)entity, seg_index,
|
||||
pos[0], pos[1], pos[2], rows);
|
||||
if (getenv("BT_DEATH_LOG"))
|
||||
DEBUG_STREAM << "[zonefx] entity " << entity->GetEntityID()
|
||||
<< " seg " << seg_index << " psfx " << (fx - 1000)
|
||||
<< " at(" << pos[0] << "," << pos[1] << "," << pos[2] << ")" << std::endl;
|
||||
}
|
||||
res->Unlock();
|
||||
}
|
||||
|
||||
//
|
||||
// StopAllEntityEffectsImplementation @004d0c14 -- the respawn cleanup: the
|
||||
// binary kills every effect tagged (playerIdx<<16 .. |0xffff); the port kills
|
||||
// every emitter tagged to the entity.
|
||||
//
|
||||
void
|
||||
BTL4VideoRenderer::StopAllEntityEffectsImplementation(Entity *entity)
|
||||
{
|
||||
if (entity == NULL)
|
||||
{
|
||||
DEBUG_STREAM << "StopAllEntityEffectsImplementation: no entity" << std::endl;
|
||||
return;
|
||||
}
|
||||
extern void BTStopEntityPfx(void *owner);
|
||||
BTStopEntityPfx((void *)entity);
|
||||
}
|
||||
|
||||
|
||||
//#############################################################################
|
||||
// SwapToWreck (ExplosionScripts effect 104, reconstructed)
|
||||
//#############################################################################
|
||||
|
||||
Reference in New Issue
Block a user