THE HEADLINE: the port's death sweep was a total no-op, and fixing it required
fixing the arg gate in the same commit -- otherwise corpses refill their ammo.
STEP 2 -- the authentic dispatch shape:
* engine/MUNGA/SUBSYSTM.h: Subsystem::DeathShutdown gains the binary's
universal base body { DeathReset(c) } (@004ad10e). It was empty, and NO
port class overrode it, so everything the 1995 game does at death was
skipped entirely.
* mech4.cpp death sweep now passes 0, not 1 (the binary's @0049fe0c arg) --
the wreck shape: alarms/state settle, nothing refills.
* ARG PROPAGATION (mandatory once the sweep runs): AmmoBin::DeathReset is now
arg-gated exactly as @004bd26c -- refill only when arg != 0, ammoAlarm
unconditional. Ignoring the arg was harmless only while the sweep was
dead; with it restored, every corpse would have re-armed. Also forwarded
in PoweredSubsystem / HeatSink / Condenser / HeatableSubsystem / Generator
(each binary body forwards it -- verified addresses in the plan).
STEP 4 -- coverage for 5 classes that had an authentic slot-10 body but no
DeathReset, so the respawn sweep fell through to the empty base:
Torso (this is Gitea #70 -- "loosing torso twist function after a death"),
Gyroscope, Myomers, HUD, Seeker.
STEPS 0/1 -- observability + the David chain:
* Three unconditional matchlog rows: DEAD_NOTIFY (mech + resolved link),
PLAYER_LINK (player <-> vehicle), RESPAWN (mode/alive/zones/subsys/pos).
A respawn was previously INVISIBLE in the matchlog -- night 3's analysis
had to infer them from ammo arithmetic.
* Mech::PlayerLinkMessageHandler + the death dispatch site: when the engine's
one-shot registry lookup misses (no null check, no retry -> the whole
death/respawn cycle silently swallowed), recover the SAME object via the
reverse link the binary's own respawn branch walks (player+0x1FC ==
playerVehicle). Complete-type TU, no raw offsets.
* deathPending cleared in the first-spawn branch (a latch carried in would
permanently kill every later respawn -- the #57 class).
VERIFIED on the 2-pod rig (madcat vs thor, forced kills, 5 death/respawn cycles
per pod): build clean, ZERO new /FORCE unresolved externs from the 5 new
overrides; refill lines appear ONLY immediately before a Mech::Reset and NEVER
between a death and the next respawn (the corpse-refill regression this commit
had to pre-empt); all three probe rows present in both pods' matchlogs; every
DEAD_NOTIFY carried a non-null link.
DELIBERATELY DEFERRED (documented, not forgotten): the mechsub.cpp rename pair
(ResetToInitialState -> GenerateFault, ClearStatus -> the root reset @004ac22c),
deleting HeatableSubsystem::ResetToInitialState, and deleting RespawnRepair.
Those need the HeatableSubsystem vtable-slot-10 pre-flight the plan calls for,
and we have no binary image here to dump the vtable from -- guessing at it
risks the vptr-alias trap. Next session with the decomp shards open.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
478 lines
18 KiB
C++
478 lines
18 KiB
C++
//===========================================================================//
|
|
// File: hud.cpp //
|
|
// Project: BattleTech //
|
|
// Contents: Cockpit Heads-Up Display subsystem //
|
|
// (reticle / torso-horizon gimbal / threat & status read-outs) //
|
|
//---------------------------------------------------------------------------//
|
|
// Copyright (C) 1995, Virtual World Entertainment, Inc. All Rights reserved //
|
|
// PROPRIETARY AND CONFIDENTIAL //
|
|
//===========================================================================//
|
|
//
|
|
// RECONSTRUCTED from the shipped binary (Ghidra pseudo-C, recovered shard
|
|
// part_013.c). Cluster @004b77bc..@004b8568. Each non-trivial method cites
|
|
// its @ADDR. Hex float constants converted to decimal where known
|
|
// (0x44960000 = 1200.0, 0x43fa0000 = 500.0, 500.0 = max torso slew/sec).
|
|
//
|
|
// Helper-function name mapping (engine internals referenced by the decomp):
|
|
// FUN_004b18a4 PowerWatcher base constructor (powersub.cpp)
|
|
// FUN_004b198c PowerWatcher::CreateStreamedSubsystem (powersub.cpp)
|
|
// FUN_004b181c PowerWatcher base per-frame update (powersub.cpp)
|
|
// FUN_004b1804 PowerWatcher::ResetToInitialState (slot10) (powersub.cpp)
|
|
// FUN_004b1930 ~PowerWatcher (powersub.cpp)
|
|
// FUN_0041b9ec AlarmIndicator(levels)
|
|
// FUN_0041baa4 ~AlarmIndicator
|
|
// FUN_0041bbd8 AlarmIndicator::SetLevel(n)
|
|
// FUN_0041a1a4 IsDerivedFrom(classDerivations)
|
|
// FUN_00404118 NotationFile::ReadScalar(file,name,&dest)
|
|
// FUN_00408440 Point3D::operator= / copy
|
|
// FUN_004dbb24 diagnostic stream << (error reporting)
|
|
// FUN_004022d0 operator delete / global free
|
|
// FUN_004b7ed4 HUD::UpdateFlicker
|
|
//
|
|
#include <bt.hpp>
|
|
#pragma hdrstop
|
|
|
|
#if !defined(HUD_HPP)
|
|
# include <hud.hpp>
|
|
#endif
|
|
#if !defined(APP_HPP)
|
|
# include <app.hpp>
|
|
#endif
|
|
#if !defined(TESTBT_HPP)
|
|
# include <testbt.hpp>
|
|
#endif
|
|
|
|
// BASE-CHAIN RE-BASE witness: HUD is the only currently-LIVE Watcher-branch class
|
|
// (HUD : PowerWatcher). After the +156 re-base, its first own field must land at
|
|
// 0x1D8 -- the same base end as Torso::currentTwist -- which also corrects HUD's
|
|
// own torso+0x1D8 read. A friend so it can offsetof the protected field.
|
|
struct HUDLayoutCheck
|
|
{
|
|
static_assert(offsetof(HUD, flickerRate) == 0x1D8, "HUD first own field must be at 0x1D8 after the Watcher-branch re-base");
|
|
};
|
|
|
|
//
|
|
// Tuning constants observed as read-only float globals adjacent to the
|
|
// HudSimulation body (.rdata, recovered from section_dump.txt).
|
|
//
|
|
static const Point3D HudZeroVector(0.0f, 0.0f, 0.0f); // DAT_004e0f74/78/7c
|
|
static const Scalar SegmentTempLimit = 0.0f; // _DAT_004b7ec4 (heat threshold for HUD page visibility)
|
|
static const Scalar TargetTempLimit = 0.0f; // _DAT_004b7ec8
|
|
static const Scalar RangeBias = 0.0f; // _DAT_004b7ecc
|
|
static const Scalar MaxTorsoSlew = 500.0f; // _DAT_004b7ed0
|
|
static const Scalar FlickerFloor = 0.0f; // _DAT_004b7f90
|
|
|
|
//
|
|
// Cross-family helper (definition lives in the mech game layer; declared here
|
|
// so this reconstructed body links without editing mech.hpp). Returns the
|
|
// owning Mech's segment-flags word (Mech +segmentFlags). See CROSS-FAMILY NEEDS.
|
|
//
|
|
extern LWord BT_GetSegmentFlags(Mech *owner);
|
|
|
|
//
|
|
// Resource-parse error reporter (original FUN_004dbb24 + abort path). Routes
|
|
// through the shared recon DebugStream warning channel.
|
|
//
|
|
static void ReportError(const char *subsystem_name, const char *message)
|
|
{
|
|
DebugStream << "HUD: " << subsystem_name << ": " << message << "\n";
|
|
}
|
|
|
|
//###########################################################################
|
|
// Shared Data Support
|
|
//
|
|
// DefaultData @00511078 / class derivation chain @00511088. Mirrors the
|
|
// engine's accessor-form shared-data construction (cf. PoweredSubsystem).
|
|
//
|
|
HUD::SharedData
|
|
HUD::DefaultData( // @00511078
|
|
HUD::GetClassDerivations(),
|
|
HUD::GetMessageHandlers(),
|
|
HUD::GetAttributeIndex(),
|
|
HUD::StateCount
|
|
);
|
|
|
|
Derivation*
|
|
HUD::GetClassDerivations() // @00511088
|
|
{
|
|
static Derivation classDerivations(
|
|
PowerWatcher::GetClassDerivations(),
|
|
"HUD"
|
|
);
|
|
return &classDerivations;
|
|
}
|
|
|
|
//###########################################################################
|
|
// Construction -- @004b7f94
|
|
//
|
|
// Chains to the PowerWatcher ctor (FUN_004b18a4) with HUD::DefaultData
|
|
// (&DAT_00511078), installs the HUD vtable (PTR_FUN_00511510), primes the
|
|
// gauge read-out block, builds the 2-level status AlarmIndicator (@0x238) and
|
|
// -- unless this is a damaged copy segment ((segmentFlags & 0xC) != 0) and the
|
|
// "has-performance" flag (0x100) is set -- registers HudSimulation as the
|
|
// active Performance (this[7..9] <- PTR_FUN_00511170). Finally it copies the
|
|
// streamed FlickerRate / HorizontalMovementPerSecond / HorizontalLimit into
|
|
// place and snapshots the torso/target read-outs from the linked entity
|
|
// (owner Mech +0x438).
|
|
//
|
|
HUD::HUD(
|
|
Mech *owner,
|
|
int subsystem_ID,
|
|
SubsystemResource *subsystem_resource,
|
|
SharedData &shared_data
|
|
):
|
|
PowerWatcher(owner, subsystem_ID, subsystem_resource, shared_data)
|
|
{
|
|
Check(owner);
|
|
Check_Pointer(subsystem_resource);
|
|
|
|
// (member names re-based 2026-07-19 per the re-dumped attr table @005110b8;
|
|
// the ctor @004b7f94 zeroes [0x76..0x7b] then sets [0x7d]=1 / [0x7e]=0 --
|
|
// note [0x7c]=@0x1F0 is left uninitialized in the binary)
|
|
flickerRate = 0.0f; // @0x1D8 (overwritten below from resource)
|
|
rotationOfTorsoHorizontal = 0.0f; // @0x1DC
|
|
torsoLimitRight = 0.0f; // @0x1E0
|
|
torsoLimitLeft = 0.0f; // @0x1E4
|
|
torsoSpeed = 0.0f; // @0x1E8
|
|
rangeToTarget = 0.0f; // @0x1EC
|
|
scratch1F0 = 0; // @0x1F0 (binary leaves it uninitialized; zeroed here)
|
|
visible = True; // @0x1F4 = 1 ("Visible" starts shown)
|
|
lockFlag = False; // @0x1F8 = 0 ("Lock" starts un-locked)
|
|
hotBoxVector = HudZeroVector; // @0x1FC
|
|
threatVector = HudZeroVector; // @0x208
|
|
compassHeading = 0.0f; // @0x214
|
|
blinkTimer = 0.0f; // @0x218
|
|
flickerTimer = 0.0f; // @0x21C
|
|
blinkState = True; // @0x224 = 1
|
|
transitionFlag = False; // @0x22C
|
|
previousHorizontal = 0.0f; // @0x230
|
|
|
|
statusAlarm.Initialize(2); // FUN_0041b9ec(this+0x238, 2)
|
|
|
|
freeAimSlew = 0.0f; // @0x28C (mapper free-aim demand)
|
|
scratch290 = 0.0f; // @0x290
|
|
horizontalTorsoOffset = 0.0f; // @0x294
|
|
|
|
if (((BT_GetSegmentFlags(owner) & 0xC) == 0) &&
|
|
((BT_GetSegmentFlags(owner) & 0x100) != 0))
|
|
{
|
|
SetPerformance(&HUD::HudSimulation); // this[7..9] <- PTR_FUN_00511170
|
|
}
|
|
|
|
flickerRate = subsystem_resource->flickerRate; // @0x1D8 <- res +0xF4
|
|
horizontalMovementPerSecond = subsystem_resource->horizontalMovementPerSecond; // @0x298 <- res +0xFC
|
|
horizontalLimit = subsystem_resource->horizontalLimit; // @0x29C <- res +0x100
|
|
|
|
// owner+0x438 is the torso-orientation source -- a Mech RAW OFFSET that does not
|
|
// hold a valid pointer in the reconstructed Mech layout (and isn't linked yet at
|
|
// subsystem-construction time), so dereferencing it crashes. Default the
|
|
// torso/twist readouts to 0; HudSimulation refreshes them per-frame once the link
|
|
// is live. TODO: wire a real Mech torso-orientation-source accessor.
|
|
// (binary snapshot @004b7f94: [0x77]@0x1DC <- linked+0x1D8 twist, [0x78]@0x1E0
|
|
// <- +0x1DC limitR, [0x79]@0x1E4 <- +0x1E0 limitL, [0x7a]@0x1E8 <- +0x1E8 speed)
|
|
linkedEntity = 0; // @0x234
|
|
rotationOfTorsoHorizontal = 0.0f;
|
|
torsoLimitRight = 0.0f;
|
|
torsoLimitLeft = 0.0f;
|
|
torsoSpeed = 0.0f;
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
//
|
|
// Destruction -- @004b816c (vtable slot0). Reinstalls the vtable, clears the
|
|
// owning Mech's cached HUD back-reference (*(owner + 0x374) = 0), tears down
|
|
// the status AlarmIndicator and chains to ~PowerWatcher (FUN_004b1930);
|
|
// frees the block when the deleting-dtor bit is set.
|
|
//
|
|
HUD::~HUD()
|
|
{
|
|
Check(this);
|
|
// *(this->owner + 0x374) = 0;
|
|
// (original FUN_0041baa4 finalised the AlarmIndicator; HeatAlarm needs no teardown)
|
|
Check_Fpu();
|
|
}
|
|
|
|
//###########################################################################
|
|
// TestInstance -- @004b81b8
|
|
//
|
|
Logical
|
|
HUD::TestInstance() const
|
|
{
|
|
return IsDerivedFrom(*GetClassDerivations()); // FUN_0041a1a4(**this[3], 0x00511088)
|
|
}
|
|
|
|
Logical
|
|
HUD::TestClass(Mech &)
|
|
{
|
|
return True; // BEST-EFFORT (family convention; no distinct body captured)
|
|
}
|
|
|
|
//###########################################################################
|
|
// SetThreatVector -- @004b7810
|
|
//
|
|
// Copies a Point3D into threatVector (@0x208). Renamed 2026-07-19 (Gitea
|
|
// #10): the re-dumped attr table @005110b8 binds "ThreatVector" (attr 12) to
|
|
// @0x208 -- the old SetCompassHeading label was the one-slot transcription
|
|
// shift (the compass is the Scalar @0x214). No port caller.
|
|
//
|
|
void
|
|
HUD::SetThreatVector(const Point3D &threat)
|
|
{
|
|
threatVector = threat; // FUN_00408440(this+0x208, &threat)
|
|
}
|
|
|
|
//###########################################################################
|
|
// UpdateFlicker -- @004b7ed4
|
|
//
|
|
// Decays flickerActive (@0x2A0... actually horizontalTorsoOffset region @0x294)
|
|
// toward FlickerFloor by horizontalMovementPerSecond*time, clamping at the
|
|
// floor, and reports whether motion is still in progress. Used to give the
|
|
// torso-horizon needle a damped settle.
|
|
//
|
|
Logical
|
|
HUD::UpdateFlicker(Scalar time_slice)
|
|
{
|
|
Scalar step = horizontalMovementPerSecond * time_slice;
|
|
|
|
if (FlickerFloor < horizontalTorsoOffset)
|
|
{
|
|
horizontalTorsoOffset -= step;
|
|
if (horizontalTorsoOffset < FlickerFloor)
|
|
horizontalTorsoOffset = FlickerFloor;
|
|
}
|
|
if (horizontalTorsoOffset < FlickerFloor)
|
|
{
|
|
horizontalTorsoOffset += step;
|
|
if (horizontalTorsoOffset < FlickerFloor) // (decompiled clamp, verbatim)
|
|
horizontalTorsoOffset = FlickerFloor;
|
|
}
|
|
return (horizontalTorsoOffset != FlickerFloor) ? True : False;
|
|
}
|
|
|
|
//###########################################################################
|
|
// ResetToInitialState -- @004b77bc (slot 10)
|
|
//
|
|
// On a powered reset clears the blink/horizontal-slew accumulators, then
|
|
// chains to PowerWatcher::ResetToInitialState (FUN_004b1804).
|
|
//
|
|
void
|
|
HUD::ResetToInitialState(Logical powered)
|
|
{
|
|
if (powered)
|
|
{
|
|
blinkTimer = 0.0f; // @0x218
|
|
transitionFlag = False; // @0x22C
|
|
flickerTimer = 0.0f; // @0x21C
|
|
previousHorizontal = 0.0f; // @0x230
|
|
}
|
|
horizontalTorsoOffset = 0.0f; // @0x294
|
|
freeAimSlew = 0.0f; // @0x28C (mapper free-aim demand)
|
|
scratch290 = 0.0f; // @0x290
|
|
|
|
PowerWatcher::ResetToInitialState(True); // FUN_004b1804 (base override is parameterless)
|
|
(void)powered;
|
|
}
|
|
|
|
//###########################################################################
|
|
// HandleMessage -- @004b7780 (slot 9)
|
|
//
|
|
// BEST-EFFORT: the vtable installs a HUD-specific HandleMessage at @004b7780,
|
|
// but no body was captured in the recovered shards. Chains to the PowerWatcher
|
|
// handler otherwise.
|
|
//
|
|
// ATTRIBUTION CORRECTION (#61, 2026-07-25): this comment used to claim "a handler
|
|
// table @00511180 binds the message ToggleLamp to FUN_004b838c" as if it were
|
|
// HUD's. It is NOT -- @0051117C is **Searchlight's** message entry
|
|
// {3, "ToggleLamp" @00511440, @004b838c}; @00511180 is merely its name+handler
|
|
// pair (the entry base is 4 bytes lower). HUD's own data ends with its
|
|
// Performance triple at @00511170 (@004b7830, HudSimulation). See
|
|
// searchlight.cpp and context/decomp-reference.md for the table evidence.
|
|
//
|
|
Logical
|
|
HUD::HandleMessage(int message)
|
|
{
|
|
return PowerWatcher::HandleMessage(message); // TODO: verify @004b7780
|
|
}
|
|
|
|
//###########################################################################
|
|
// HudSimulation -- @004b7830 (Performance, PTR @00511170)
|
|
//
|
|
// The per-frame HUD update (torso-horizon gimbal + reticle/threat read-outs).
|
|
// High-level behaviour recovered from the decomp; offsets retained so a human
|
|
// can finish the member naming:
|
|
//
|
|
// 1. Base update: PowerWatcher base tick (FUN_004b181c).
|
|
// 2. Heat / voltage gating of the HUD page via the inherited state levels:
|
|
// watched-voltage alarm level @0x198 (2 -> off-but-visible, 4 -> ready)
|
|
// heat-state level @0x140 (0/1 -> visible, 2 -> blank)
|
|
// The status AlarmIndicator @0x238 is driven to 0/1 accordingly and a
|
|
// composite "page visible" flag is formed.
|
|
// 3. Blink: blinkTimer (@0x218) accumulates time and is compared against
|
|
// flickerRate (@0x1D8); on expiry blinkState (@0x224) toggles and the
|
|
// timer resets. When the blink is in its OFF phase the page is hidden.
|
|
// 4. Engine/start state of the host (mech +0x128 roster[0] state @0x198):
|
|
// 0 -> set HUD flag bit 0x1 on the graphic, clear bit 0x2
|
|
// 3 -> set bit 0x2, clear bit 0x1 (and gate on mech +0x410)
|
|
// writes the resulting "active" flag to *(mech +0x374).
|
|
// 5. Target lock + range: if a target entity (mech +0x388) exists and the
|
|
// own/target zone-damage limits pass (:5619-5634, the Lock rule), sets
|
|
// lockFlag (@0x1F8, "Lock"); rangeToTarget (@0x1EC, "RangeToTarget") is
|
|
// the distance between the HUD anchor (mech +0x37C) and the target
|
|
// (+0x100), sliding at 500 m/s (:5652-5670), default 1200.0 with no
|
|
// target; the compass Scalar @0x214 = yaw euler[0] + torso twist (:5676).
|
|
// 6. Torso-horizon slew: horizontalTorsoOffset (@0x294) is moved toward the
|
|
// commanded torso heading at up to MaxTorsoSlew (500/sec), clamped to
|
|
// +/- horizontalLimit (@0x29C), then written to the graphic at
|
|
// mech +0x36C. The flicker helper (@004b7ed4) damps the settle.
|
|
//
|
|
void
|
|
HUD::HudSimulation(Scalar time_slice)
|
|
{
|
|
Check(this);
|
|
|
|
PowerWatcher::Simulation(time_slice); // FUN_004b181c (base per-frame tick)
|
|
|
|
//
|
|
// --- Heat / voltage gating of the HUD page ----------------------------
|
|
//
|
|
Logical pageVisible = (HeatWatcherFlag() != 1); // this[0x40] != 1
|
|
|
|
switch (WatchedVoltageLevel()) // @0x198
|
|
{
|
|
case 2: statusAlarm.SetLevel(0); break; // off but page shown
|
|
case 4: statusAlarm.SetLevel(1); break; // ready
|
|
default: statusAlarm.SetLevel(1); pageVisible = False; break;
|
|
}
|
|
|
|
switch (HeatStateLevel()) // @0x140
|
|
{
|
|
case 0: statusAlarm.SetLevel(1); break;
|
|
case 1: statusAlarm.SetLevel(0); break;
|
|
case 2: statusAlarm.SetLevel(1); pageVisible = False; break;
|
|
}
|
|
|
|
//
|
|
// --- Blink ------------------------------------------------------------
|
|
//
|
|
if (transitionFlag == 0)
|
|
{
|
|
blinkTimer += time_slice;
|
|
if (blinkTimer != flickerRate)
|
|
{
|
|
blinkTimer = 0.0f;
|
|
blinkState = (blinkState == 0);
|
|
}
|
|
}
|
|
else if (transitionFlag == 1)
|
|
{
|
|
blinkState = True;
|
|
}
|
|
pageVisible = (pageVisible && blinkState) ? True : False;
|
|
|
|
//
|
|
// --- (remaining torso-horizon / target / slew logic; see header) ------
|
|
// Faithful offsets are preserved in the recovered shard part_013.c
|
|
// @004b7830; reproduced here in summarised form -- a human should expand
|
|
// the reticle/threat-vector math against the original source.
|
|
//
|
|
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
// CreateStreamedSubsystem -- HUD @004b81d4
|
|
//
|
|
// Chains to PowerWatcher::CreateStreamedSubsystem (FUN_004b198c), stamps the
|
|
// resource (classID 0x0BD6 @ +0x20, model size 0x104 @ +0x24), resolves the
|
|
// "SegmentPageName" (when passes==1 it seeds FlickerRate := -1.0 and copies
|
|
// the segment index from +0x28 to +0xF8; on later passes it restores +0x28
|
|
// from +0xF8, erroring "missing SegmentPageName!" if unset), then reads the
|
|
// three HUD scalars, erroring if any is absent/zero:
|
|
// "FlickerRate" -> +0xF4
|
|
// "HorizontalMovementPerSecond" -> +0xFC
|
|
// "HorizontalLimit" -> +0x100
|
|
//
|
|
int
|
|
HUD::CreateStreamedSubsystem(
|
|
NotationFile *model_file,
|
|
const char *model_name,
|
|
const char *subsystem_name,
|
|
SubsystemResource *subsystem_resource,
|
|
NotationFile *subsystem_file,
|
|
const ResourceDirectories *directories,
|
|
int passes
|
|
)
|
|
{
|
|
if (
|
|
!PowerWatcher::CreateStreamedSubsystem( // FUN_004b198c
|
|
model_file, model_name, subsystem_name,
|
|
subsystem_resource, subsystem_file, directories, passes
|
|
)
|
|
)
|
|
{
|
|
return False;
|
|
}
|
|
|
|
subsystem_resource->subsystemModelSize = 0x104; // +0x24
|
|
subsystem_resource->classID = (RegisteredClass::ClassID)0x0BD6; // HUDClassID, +0x20
|
|
|
|
if (passes == 1)
|
|
{
|
|
subsystem_resource->flickerRate = -1.0f; // +0xF4
|
|
subsystem_resource->segmentPageIndex = subsystem_resource->segmentIndex; // +0xF8 <- +0x28
|
|
}
|
|
if ((subsystem_resource->segmentPageIndex == -1) && (1 < passes))
|
|
{
|
|
ReportError(subsystem_name, "missing SegmentPageName!");
|
|
return False;
|
|
}
|
|
if (1 < passes)
|
|
{
|
|
subsystem_resource->segmentIndex = subsystem_resource->segmentPageIndex; // +0x28 <- +0xF8
|
|
}
|
|
|
|
if (!model_file->GetEntry(subsystem_name, "FlickerRate", &subsystem_resource->flickerRate)
|
|
&& subsystem_resource->flickerRate == 0.0f)
|
|
{
|
|
ReportError(subsystem_name, "missing FlickerRate!");
|
|
return False;
|
|
}
|
|
if (!model_file->GetEntry(subsystem_name, "HorizontalMovementPerSecond",
|
|
&subsystem_resource->horizontalMovementPerSecond)
|
|
&& subsystem_resource->horizontalMovementPerSecond == 0.0f)
|
|
{
|
|
ReportError(subsystem_name, "missing HorizontalMovementPerSecond!");
|
|
return False;
|
|
}
|
|
if (!model_file->GetEntry(subsystem_name, "HorizontalLimit",
|
|
&subsystem_resource->horizontalLimit)
|
|
&& subsystem_resource->horizontalLimit == 0.0f)
|
|
{
|
|
ReportError(subsystem_name, "missing HorizontalLimit!");
|
|
return False;
|
|
}
|
|
|
|
Check_Fpu();
|
|
return True;
|
|
}
|
|
|
|
//===========================================================================//
|
|
// WAVE 2 factory bridge -- HUD (factory case 0xBD6, "MechTech" label).
|
|
//===========================================================================//
|
|
Subsystem *CreateHUDSubsystem(Mech *owner, int id, void *seg)
|
|
{
|
|
Check(sizeof(HUD) <= 0x2a4);
|
|
return (Subsystem *) new (Memory::Allocate(0x2a4))
|
|
HUD(owner, id, (HUD::SubsystemResource *)seg, HUD::DefaultData);
|
|
}
|
|
|
|
//
|
|
// DeathReset (#55 step 4) -- the respawn sweep's entry for the HUD.
|
|
//
|
|
void
|
|
HUD::DeathReset(int reset_command)
|
|
{
|
|
ResetToInitialState(reset_command != 0); // @004b77bc
|
|
}
|