P7: subsystem-tree alarm unification -- the whole PoweredSubsystem weapon/power subtree is byte-exact
The reconstruction modeled the binary's 0x54 AlarmIndicator (FUN_0041b9ec) with undersized
stand-ins (AlarmIndicator==ReconAlarm==4B; HeatAlarm==8B) across the entire subsystem tree, so
every field above an alarm sat at the wrong compiled offset. Retype every such stand-in to
GaugeAlarm54(0x54) and de-phantom each class against its ctor, so the whole PoweredSubsystem
subtree becomes byte-exact. An 8-agent read-only decomp-mapping workflow decoded every ctor
first; then hands-on implementation. static_assert-locked chain (verified vs the raw ctors):
HeatSink 0x1D0
-> PoweredSubsystem 0x31C retype electricalStateAlarm@0x264 + modeAlarm@0x2B8 @004b0f74
-> MechWeapon 0x3F0 retype weaponAlarm@0x350; delete 5 phantom tail fields @004b99a8
-> Emitter 0x478 delete outputVoltage/beamLengthRatio/firingArmed aliases
+ beamHit*/beamColor/beamHitData/energyRampTime phantoms;
retype beamOrientation EulerAngles->Quaternion(16B) @004bb120
-> PPC 0x478 (no own fields) @004bb888
PoweredSubsystem -> Sensor 0x328 (no alarm; 3 own fields) @004b1d18
PoweredSubsystem -> Myomers 0x358 (no alarm) @004b8fec
New systemic bug-class instances fixed (added to the CLAUDE.md checklist):
* alias field - Emitter outputVoltage==inherited rechargeLevel@0x320; beamLengthRatio==
beamScale.z@0x434; firingArmed==inherited useConfiguredPip@0x3E0
* phantom field - MechWeapon segmentReference/pipSegment/hasTarget/targetPoint/muzzlePoint
(past 0x3F0); Emitter beamHitPoint/beamImpact/beamImpactScalar/beamColor/
beamHitData/energyRampTime (binary writes inherited damageData/voltageScale
or the value is a method local)
Non-layout fixes required in the same wave:
* outputVoltage->rechargeLevel also in the compiled GAUSS.CPP:74/93 (external readers of the
removed Emitter field -- must grep EVERY TU, not just the class's own .cpp)
* MechWeapon::GetMuzzlePoint reimplemented faithfully (removed muzzlePoint collided with
Emitter's own fields at 0x3F0) via a BTResolveWeaponMuzzle void* bridge in mech4.cpp,
resolving the weapon's mount segment (inherited this+0xdc) through the owner segment table
* DetachFromVoltageSource fixed to set electricalStateAlarm not modeAlarm (raw @004b0e30
writes the 0x264 alarm)
The vehicleSubSystems aux-screen gauge raw reads (btl4gau2.cpp:868/952 at subsystem+0x2b8/+0x278)
and the Sensor RadarPercent path now read the CORRECT byte offsets (garbage under the short layout).
Verified: combat DESTROYED-in-8, 28 shots, 0 crashes, heat heatEnergy=1.34e7, every static_assert
lock passes, heapcheck-clean through construction (the phase the isolated PoweredSubsystem retype
had overflowed). LESSON: a factory-bridge runtime Check(sizeof<=alloc) does NOT fail the build --
only a static_assert sizeof lock catches alloc overflow at compile time.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
1356870e56
commit
9d82be46a1
@@ -160,8 +160,9 @@ MechWeapon::MechWeapon(
|
||||
|
||||
rangeToTarget = 0.0f; // 0x324
|
||||
targetWithinRange = False; // 0x34C
|
||||
segmentReference = segmentPageIndex;
|
||||
pipSegment = -1;
|
||||
// segmentReference/pipSegment were phantom tail members (past the binary 0x3F0 end);
|
||||
// removed. The muzzle segment index is the inherited base slot this+0xdc (used by
|
||||
// GetMuzzlePoint); the pip mount is gated by useConfiguredPip in DrawWeaponPip.
|
||||
|
||||
// usesExternalModel: True if the model name contains the marker substring
|
||||
// (DAT_00511aa2); selects whether the configured pip is drawn.
|
||||
@@ -358,17 +359,23 @@ void
|
||||
char *o = (char *)owner; // inherited MechSubsystem::owner (the Mech)
|
||||
if (o != 0)
|
||||
position = *(Point3D *)(o + 0x37c); // MECH_TARGET_POS
|
||||
else
|
||||
position = targetPoint;
|
||||
// (no owner -> leave position unchanged; the phantom `targetPoint` member is gone --
|
||||
// binary GetTargetPosition @004b9cbc unconditionally reads the owner target slot.)
|
||||
}
|
||||
|
||||
// Faithful FUN_004b9948 muzzle resolve, defined in mech4.cpp (a complete-Mech TU);
|
||||
// mechweap.cpp reaches the owner Mech only as a raw pointer, so it bridges via void*.
|
||||
extern void BTResolveWeaponMuzzle(void *ownerMech, int segIndex, Point3D &out);
|
||||
|
||||
//
|
||||
// @004b9cdc -- direction from a supplied point to the weapon mount (entity+0x37c).
|
||||
//
|
||||
void
|
||||
MechWeapon::GetVectorToWeapon(const Point3D &from, Vector3D &direction)
|
||||
{
|
||||
direction.Subtract(muzzlePoint, from); // FUN_00408644
|
||||
Point3D muzzle;
|
||||
GetMuzzlePoint(muzzle); // resolve live (phantom muzzlePoint member removed)
|
||||
direction.Subtract(muzzle, from); // FUN_00408644
|
||||
}
|
||||
|
||||
//
|
||||
@@ -378,11 +385,13 @@ void
|
||||
void
|
||||
MechWeapon::GetMuzzlePoint(Point3D &point)
|
||||
{
|
||||
// @004b9948 -- the shipped code resolves segmentReference against the owning
|
||||
// Mech's segment table (SegmentIterator) and transforms the segment origin to
|
||||
// world space. That transform is produced by the owning Mech's per-frame
|
||||
// segment update, which caches it in muzzlePoint; we return the cached value.
|
||||
point = muzzlePoint; // FUN_00424da8 result
|
||||
// @004b9948 -- resolve the weapon's mount segment (its index is the inherited
|
||||
// subsystem slot at this+0xdc, == binary param_1+0xdc) in the owning Mech's
|
||||
// segment table and transform it to world (FUN_00424da8). Reads live each call
|
||||
// (the phantom cached `muzzlePoint` member is removed -- it collided with the
|
||||
// Emitter subclass's own fields at 0x3F0+).
|
||||
int segIndex = *(int *)((char *)this + 0xdc); // inherited segment index (byte-exact base)
|
||||
BTResolveWeaponMuzzle(owner, segIndex, point); // mech4.cpp bridge (null-guarded)
|
||||
}
|
||||
|
||||
//
|
||||
@@ -422,7 +431,7 @@ void
|
||||
// CROSS-FAMILY (hud): the cockpit HUD manager AddElement entry point lives in
|
||||
// the HUD module; the submission is wired there. We retain the gating and
|
||||
// transform reference here.
|
||||
if (this->pipSegment != -1) // FUN_0041a1a4 guard
|
||||
if (useConfiguredPip) // this+0x3E0 (weapon draws a configured pip)
|
||||
{
|
||||
(void)transform;
|
||||
(void)pipPosition;
|
||||
|
||||
Reference in New Issue
Block a user