Files
TeslaRel410/restoration/source410/BT/SENSOR.CPP
T
CydandClaude Fable 5 80ac2943a0 BT410 5.3.118: the census closes -- the authoring-side CreateStreamedSubsystem family, and the Fail count reaches ZERO
The last eight stubs plus the two chain layers they actually need
(PoweredSubsystem @004b13ac and ProjectileWeapon @004bc7cc were DECLARED in
our headers all along -- only their definitions were missing).  These are
the notation->stream converters the 1995 authoring pipeline ran; missions
read prebuilt streams and never call them, so the verification standard is
compile + link + the raw decomp's key fingerprints -- and those fingerprints
convicted the donors three more times:

- The heat chain: Condenser, Reservoir AND PoweredSubsystem all chain ONE
  shared parser (@004ae150, reconstructed in full from the decomp -- the
  BT411 donor's body was its own TODO stub with a miscited address).  It
  reads the five thermal scalars and the conduction-graph link (the
  linkedSinkIndex our wire-format work dump-verified from the runtime side,
  with the +2 segment-table bias), and HeatSink adds no keys of its own.
- The Emitter's SeekVoltage LADDER is a notation ENTRY LIST walked in file
  order (curve rungs in sequence, the RecommendedIndex entry by name); the
  donor flattened it to one keyed read, which would author one-rung ladders
  against the runtime's real five-rung ones.  MakeEntryList/GetFirstEntry
  is the engine's own idiom.
- PipColor and MuzzleVelocity parse through the engine's authentic
  Convert_From_Ascii overloads (RGBColor / Vector3D); the donors' sscanf
  substitutes were port drift.  Explosion and alarm models resolve as model
  FAMILIES (ModelListResourceType == the literal 1 in the binary; the donor
  comment calling it GameModelResourceType was wrong-name-right-number).

Entry reads target the SUBSYSTEM notation file -- the decomp's fifth
argument at every site; several donors wrote model_file.  Class IDs stamp
through OUR reader's enum symbols (they parse the real BTL4.RES streams
today, and the 3000-base enum lands exactly on the binary's 0xBBB.. ids;
0x0BCD is the weapon-base stamp every concrete leaf overwrites).

Stubs: NONE.  Every function in restoration/source410 now has a body.
Mission soak clean.  Remaining reconstruction is depth, not holes: the
mech4/mech3 archaeology (IntegrateMotion, the master-perf disasm, the
stream builders that seed the 0xC/0x100 flags), HUD blocks 4-6, and the
live networked/rig verifications queued for when a pod is available.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 11:22:12 -05:00

276 lines
7.5 KiB
C++

//===========================================================================//
// File: sensor.cpp //
// Project: BattleTech Brick: Mech subsystems //
// Contents: Sensor -- the radar / targeting subsystem //
//---------------------------------------------------------------------------//
// Copyright (C) 1995, Virtual World Entertainment, Inc. //
// All Rights reserved worldwide //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
//===========================================================================//
#include <bt.hpp>
#pragma hdrstop
#if !defined(SENSOR_HPP)
# include <sensor.hpp>
#endif
#if !defined(MECH_HPP)
# include <mech.hpp>
#endif
//
//#############################################################################
// Shared data support
//#############################################################################
//
Derivation
Sensor::ClassDerivations(
PoweredSubsystem::ClassDerivations,
"Sensor"
);
//
//#############################################################################
// Attribute table -- the AUTHENTIC surviving SENSOR.HPP enum
// (RadarPercent/SelfTest/BadVoltage off PoweredSubsystem::NextAttributeID =
// 0x0F). The cockpit map() binds "Avionics/RadarPercent".
//#############################################################################
//
const Sensor::IndexEntry
Sensor::AttributePointers[]=
{
ATTRIBUTE_ENTRY(Sensor, RadarPercent, radarPercent),
ATTRIBUTE_ENTRY(Sensor, SelfTest, selfTest),
ATTRIBUTE_ENTRY(Sensor, BadVoltage, badVoltage)
};
Sensor::AttributeIndexSet
Sensor::AttributeIndex(
ELEMENTS(Sensor::AttributePointers),
Sensor::AttributePointers,
PoweredSubsystem::AttributeIndex
);
Sensor::SharedData
Sensor::DefaultData(
Sensor::ClassDerivations,
Subsystem::MessageHandlers,
Sensor::AttributeIndex,
Subsystem::StateCount
);
//
//#############################################################################
// The radar / targeting subsystem. A non-replicant (master) instance runs the
// per-frame SensorSimulation.
//#############################################################################
//
Sensor::Sensor(
Mech *owner,
int subsystem_ID,
SubsystemResource *subsystem_resource
):
PoweredSubsystem(owner, subsystem_ID, subsystem_resource, DefaultData)
{
Check(owner);
Check_Pointer(subsystem_resource);
radarPercent = 1.0f; // RadarBaseline
selfTest = False;
badVoltage = False;
//
// Install the per-frame performance unless the owner is a replicant copy
// (which is driven by console updates instead).
//
if (owner->GetInstance() != Entity::ReplicantInstance)
{
SetPerformance(&Sensor::SensorSimulation);
}
Check_Fpu();
}
//
//#############################################################################
//#############################################################################
//
Sensor::~Sensor()
{
}
//
//#############################################################################
//#############################################################################
//
Logical
Sensor::TestClass(Mech &)
{
return True;
}
Logical
Sensor::TestInstance() const
{
return IsDerivedFrom(ClassDerivations);
}
//
//#############################################################################
//#############################################################################
//
void
Sensor::ResetToInitialState()
{
Check(this);
radarPercent = 1.0f; // RadarBaseline
selfTest = False;
badVoltage = False;
PoweredSubsystem::ResetToInitialState(True);
}
//
//#############################################################################
// Per-frame radar / targeting update. Not yet reconstructed -- fires only once
// the mech is ticking.
//#############################################################################
//
void
Sensor::SensorSimulation(Scalar time_slice)
{
Check(this);
//
// The authentic per-frame sensor update (binary @004b1c4c): the
// PoweredSubsystem step first, then radarPercent = baseline - structural
// damage, gated by the electrical Ready state and the heat state.
// (Still deferred: the novice-mode HeatModelOff gate -- the player
// experience switch -- joins with the player-link accessor wave.)
//
PoweredSubsystemSimulation(time_slice);
radarPercent = 1.0f - GetSubsystemDamageLevel(); // RadarBaseline - zoneDamage
if (radarPercent < 0.0f)
{
radarPercent = 0.0f;
}
selfTest = True;
if (GetVoltageState() == Ready)
{
badVoltage = False;
}
else
{
badVoltage = True;
radarPercent = 0.0f;
}
switch (GetHeatState())
{
case NormalHeat:
selfTest = True;
break;
case DegradationHeat:
radarPercent *= 0.5f; // HeatDegradationScale
selfTest = True;
break;
case FailureHeat:
radarPercent = 0.0f;
selfTest = False;
break;
default:
break;
}
{
//
// One-shot confirmation that the engine's per-frame entity/roster tick
// (Entity::PerformAndWatch) has engaged -- i.e. the mission reached
// RunningMission and the mech is being simulated each frame.
//
static int firstTick = 0;
if (!firstTick && getenv("BT_MECH_LOG"))
{
firstTick = 1;
DEBUG_STREAM << "[tick] roster live (first Sensor frame), radarPercent="
<< radarPercent << " voltState=" << GetVoltageState()
<< endl << flush;
}
}
Check_Fpu();
}
//
//#############################################################################
// Damage / death.
//#############################################################################
//
void
Sensor::TakeDamage(Damage &damage)
{
Check(this);
PoweredSubsystem::TakeDamage(damage);
}
void
Sensor::DeathReset(Logical full_recovery)
{
Check(this);
//
// The base heal FIRST (private zone + DestroyedState + status level --
// a crit-killed radar must come back), then the sensor re-baseline.
//
MechSubsystem::DeathReset(full_recovery);
ResetToInitialState();
}
//
//#############################################################################
// CreateStreamedSubsystem Model-load-time construction. Not yet reconstructed
// (see MECHSUB.NOTES.md -- the 4.10 subsystem stream format).
//#############################################################################
//
//
//#############################################################################
// CreateStreamedSubsystem -- binary @004b1dcc: chain the powered-record
// parser, stamp classID 0x0BC3 / size 0x190. The Sensor record is an empty
// extension of the PoweredSubsystem record.
//#############################################################################
//
int
Sensor::CreateStreamedSubsystem(
NotationFile *model_file,
const char *model_name,
const char *subsystem_name,
SubsystemResource *subsystem_resource,
NotationFile *subsystem_file,
const ResourceDirectories *directories,
int passes
)
{
if (
!PoweredSubsystem::CreateStreamedSubsystem(
model_file, model_name, subsystem_name,
subsystem_resource, subsystem_file, directories, passes
)
)
{
return False;
}
subsystem_resource->subsystemModelSize = 0x190;
subsystem_resource->classID = RegisteredClass::SensorClassID;
Check_Fpu();
return True;
}