Gauges: the CLOSEOUT wave -- radar pips, Myomers dedup, attribute parity, ledger sweep (tasks #14-#17)

TASK #17 -- AUTHENTIC RADAR SYMBOLOGY (the cross-blip stand-in retired).
The recon proved the 'missing pip raster set' never existed as BT game code:
pips are the ENGINE's L4GaugeImage vector-stroke system (T0 source in tree,
L4GAUIMA.cpp == FUN_0046f0c0 line-for-line), the 'pip table' is L4Warehouse::
gaugeImageBin keyed by Entity::resourceID, and BTL4.RES ships 110 type-0x12
shapes (every mech/vehicle/building/tree).  The six btl4rdr stubs are wired
to the real facilities: contacts draw their authentic model silhouettes with
LOD selection, the target gets the binary's 4px-inflated box highlight, and
player-name labels resolve via Mission::GetSmallNameBitmap (the prebuilt
64x16 egg rasters keyed by the player's bitmapindex).  DECODED: the invented
'VideoObject' was Entity::owningPlayer all along (+0x190; nameID =
playerBitmapIndex@0x1E0, target = BTPlayer::objectiveMech@0x284 -- new
bridge BTPlayerObjectiveMechOf); the 'LabelledEntity' class is Landmark
(cultural.h; label path dormant -- no landmark content ships, no runtime
landmarkID writer).  The L4GREND BT_DEV_GAUGES warehouse guard is removed
(its AV had a different culprit, below); resource type 18 corrected to
GaugeImageStream in decomp-reference (was mislabeled 'ModelList').

TASK #14 -- the Myomers ODR duplicate ELIMINATED: the powersub.cpp/hpp
'Myomers' (classID 0xBC3 -- actually Sensor) is retired whole; it duplicated
?DefaultData@Myomers@@ against the real class (dumpbin-verified) and /FORCE
picked the winner by link order.  The real Myomers (0xBC6) now chains
PoweredSubsystem's handler set (ids 4-8) and publishes its SEVEN binary
attributes (@00511588: SpeedEffect/Current/Recommended/Min/MaxSeekVoltage-
Index/SeekVoltage/OutputVoltage) -- the old empty unchained index starved
the Myomer engineering panel of every resolve.

TASK #16 -- attribute parity: MechWeapon publishes the FULL binary table
@0x511890 (11 entries; ids renumbered to binary truth -- the port aliases
had squatted the binary's DistanceToTarget/TargetWithinRange ids; the
streamed TriggerState 0x13 binding unchanged).  Binary names resolved two
TODO members: pipState -> estimatedReadyTime (attr 0x1A), and the EXT-model
flag is the binary's RearFiring (0x1B).  ThermalSight LightState published.
HUD (offset conflicts) + missile-side tables (id encoding suspect)
documented for a re-dump instead of publishing blind.
  THE CRASH THIS EXPOSED [T2, cdb-verified]: gotcha #11's dense-table gap
is a LATENT AV, not a guaranteed one -- the old table's 0x0D..0x12 gap
(task #5) survived on heap luck; the renumber reshuffled allocations and
Find() AV'd on a garbage entryName in WeaponCluster's PercentDone resolve.
Fixed with five named PAD entries (the mech.cpp attrPad idiom) + a
static_assert locking the pad base to PoweredSubsystem::NextAttributeID.
Gotcha #11 amended with the proof.

TASK #15 -- stale-ledger sweep: GAUGE_COMPOSITE ('composite not yet built',
Reservoir shadow, PlayerStatus/vehicleSubSystems 'remaining', valve-dormant-
until-0xBD3, sensor guard, the superseded 'Heat MFD near-static' reframe --
all banner-corrected), gauges-hud frontmatter, L4VB16 + powersub comments.

Verified live: 50/50 config attribute bindings resolve, 0 NULLs, 0 parse
skips, mech spawns and simulates 31/31 subsystems, no cross-blip fallbacks,
the pip cache fills through entity registration without the old guard.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-12 00:57:45 -05:00
co-authored by Claude Fable 5
parent 80cadc98c7
commit dd27238ceb
16 changed files with 309 additions and 384 deletions
+1 -1
View File
@@ -32,7 +32,7 @@ NOT `SearchList(clip_id, 16)` (which treats the arg as a LIST → walks garbage
|---|---|---|
| SkeletonStream | — | `.SKL` skeletons |
| DamageZoneStream | **0x14** | damage-zone records (Mech ctor reads count + array) |
| ModelList | **18** | model/mech names (`bhk1`, `madcat`, …) |
| GaugeImageStream | **18** (0x12) | radar PIP vector shapes (.GIM linelist; keyed by the entity model's resource id -- `bhk1`=1025, `madcat`=401, …; 110 ship in BTL4.RES). task #17 CORRECTION: the old "ModelList = model/mech names" label was wrong -- the names matched because pips are stored under their model's name/id. |
| AnimationResourceType | **16 (0x10)** | animation clips (lowercase: `blhrrl`=904, `blhrrr`=905, `blhwwli`=910, `blhrrli`=916) |
| damage-zone segment list | 0x14 | per-zone segment list |
| critical-subsystem segment | 0x1e | crit subsystem segments |
+1 -1
View File
@@ -6,8 +6,8 @@ source_sections: "docs/GAUGE_COMPOSITE.md (full map); CLAUDE.md §8"
related_topics: [reconstruction-gotchas, decomp-reference, subsystems, pod-hardware]
key_terms: [gauge, methodDescription, attribute-pointer, GaugeRenderer, MFD, PCC]
open_questions:
- "Condenser valve control gates on the owning BTPlayer game-mode flag (mech+0x190), likely off in basic missions"
- "Status-message queue (StatusMessagePool) is a NULL stub -> MessageBoard is empty"
- "HUD binary attr table @005110c0 offsets conflict with the port HUD layout on 3 slots (0x1D8/0x1EC/0x1F8) -- re-dump before publishing"
---
# Cockpit Gauges / MFD HUD
+10
View File
@@ -188,6 +188,16 @@ published attribute table MUST be a **dense prefix** from the parent's `NextAttr
AVs. Fill gaps with a shared read-only pad member. Same for a class's `<Name>AttributeID` enum.
[T2]
**PROVEN LIVE (task #16):** a gap does NOT necessarily crash immediately — the garbage slot's
`entryName` may happen to point at readable heap, so a gapped table can "work" for weeks (the
MechWeapon table's 0x0D..0x12 gap shipped in task #5 and survived on heap luck). ANY change
that reshuffles allocations (the task-#16 renumber did) can then fire the latent AV — observed
as `AttributeIndexSet::Find` crashing in `WeaponCluster::WeaponCluster("PercentDone")`. A
"passes the run" verification does NOT clear a gapped table; grep every pinned
`<Name>AttributeID = 0xNN` and check the parent chain actually reaches 0xNN-1, else pad
(mechweap.cpp now static_asserts its pad base against `PoweredSubsystem::NextAttributeID`).
[T2]
## 12. Frame-pacing trap — the binary assumes a LOCKED 60 fps (task #11)
The 1995 pod ran frame-locked; reconstructed per-frame logic can carry HIDDEN frame-rate
+31 -27
View File
@@ -1,9 +1,8 @@
# Gauge / MFD render pipeline — map + dev-composite plan
**Status:** pipeline MAPPED (workflow `gauge-pipeline-map`, 2026-07); the dev composite is **planned, not
yet built**. This is the execute-ready plan for making the pod's gauges/MFDs render + be VISUALLY testable
on a dev box — the "MFD compositing on dev" follow-up the platform profile gates (see §8 / the PLATFORM
PROFILE entry in CLAUDE.md). Companion: the platform-profile scaffold (`-platform pod|dev`).
**Status (updated 2026-07-12):** the dev composite is **BUILT AND LIVE** (Milestones A/B/C done —
BT_DEV_GAUGES / BT_DEV_GAUGES_DOCK; all 6 MFD surfaces composite; 50/50 attr bindings, 0 parse-skips).
The header below preserved the original PLAN framing; see the increments + Phase logs for what landed.
## The draw model — CONFIRMED (the good news)
@@ -443,18 +442,17 @@ fill the gaps (bind not-yet-needed ids to a shared read-only pad member).
Resource fetch = `renderer->warehousePointer->bitMapBin.Get/Release(name)` (FUN_00442aec/00442c12).
### Deferred / follow-ups
- `HeatSink/AmbientTemperature` maps to `ambientTemperature`@0x1D4 on the AGGREGATE HeatSink-bank class
(0xBBE, ctor @4ae8d0) which is `#if 0`'d — reviving it + its own table is a separate task (Heat `A`
readout stays 0 until then).
- Reservoir shadow: `Reservoir::coolantCapacity`@0x128 shadows the inherited `thermalCapacity` the gauge
reads → `Reservoir/CoolantCapacity` reads the base default; fix = delete the shadow + write thermalCapacity
in the Reservoir ctor (bundle with the Reservoir coolant-bar verification).
- `HeatSink/AmbientTemperature` — LANDED (AggregateHeatSink 0xBBE reconstructed, commit 16f5f54;
cleared the last NULL binding; the bank is also the ambient radiator since task #9).
- ✅ Reservoir shadow — LANDED (heatfamily_reslice.cpp now writes thermalCapacity from the resource
coolantCapacity; the shadow member is gone).
-**oneOfSeveralPixInt** DONE (increment 5). The Mech table now reaches 0x37 so `DuckState` resolves;
`Searchlight/LightOn` still needs a Searchlight `LightOn` table to make that button dynamic.
-**map** DONE (increment 6) — renders the view wedge. FOLLOW-UPS: the spatial-query contact
classification (0 contacts today) + the stubbed pip/name/video-object infrastructure (contacts + labels)
+ `SetTargetRange` (radar zoom, currently a 500m default).
- Remaining widgets (task #13): `PlayerStatus` (comm/score, 8 uses), `vehicleSubSystems` (26 uses).
-**map** DONE (increment 6) — view wedge + live contact blips; ✅ `SetTargetRange` LANDED (radar
zoom). REMAINING: the stubbed pip/name/video-object symbology (contacts draw the cross-blip stand-in,
no labels) — under reconstruction (task #17, 2026-07-12).
- Remaining widgets — BOTH LANDED: `PlayerStatus` (increment 7, commit b691863) and
`vehicleSubSystems` (see docs/VEHICLE_SUBSYSTEMS.md Phase 2).
### Remaining widgets — scope (assessed 2026-07; NOT quick registration wins)
-**PlayerStatus** DONE (increment 7; commit b691863) — reconstructed the whole family (mapped by the
@@ -469,11 +467,9 @@ fill the gaps (bind not-yet-needed ids to a shared read-only pad member).
Pixelmap8 returns NULL (the mech-outline recolor needs the DynamicMemoryStream read API + Pixmap pixel-copy
mapped) → the score/name-box/status-box render but not the recoloured mech schematic. Player resolve uses
`application->GetMissionPlayer()` (WinTesla-clean) instead of the binary's raw App+0x24 "Players"-node walk.
- **vehicleSubSystems** (26 uses -- the most-used unregistered widget) — no class declared; fully
from-scratch. Config: `vehicleSubSystems(qjak0.pcc..qjak7.pcc)` (8 subsystem-group bitmaps, no attribute
bindings -- reads the mech's subsystem roster internally). Find its Make/ctor/Execute via the .data
methodDescription table (the "vehicleSubSystems" string is a .data const, not inlined in the decomp).
A focused effort (~250-300 lines across 3-5 functions).
- **vehicleSubSystems** — LANDED (docs/VEHICLE_SUBSYSTEMS.md: 7 authentic engineering panels with
real aux-screen positions via the BTGetSubsystemAuxScreen bridge). The from-scratch scoping note above
is historical.
---
@@ -482,8 +478,10 @@ fill the gaps (bind not-yet-needed ids to a shared read-only pad member).
Goal (user directive: "all of this needs to work as the game originally played. no shortcuts"): make every
cockpit gauge read + reflect LIVE game state exactly as the 1995 pod. The `gauge-databinding-map` workflow
(5 surface maps + 2 adversarial verifies) mapped every binding → subsystem/member → published-vs-missing →
authentic value → fix. **KEY REFRAME (verified against the shipped .SUB data + the binary sim): the Heat MFD
is authentically NEAR-STATIC, not a live heat-accumulation display.** `currentTemperature =
authentic value → fix. **[SUPERSEDED 2026-07-11, tasks #9/#10: the reframe below was computed at the
DEGENERATE bring-up heat scale. At the authentic 1e7-unit economy the Heat MFD is FULLY DYNAMIC —
weapons 77→2000, condensers/generators 100-1400, bank ~600; see context/gauges-hud.md.]** The original
(superseded) reframe read: the Heat MFD is authentically NEAR-STATIC — `currentTemperature =
heatEnergy/thermalMass`; ThermalMass=1.39e6, so heatEnergy_init = 1.07e8 whose float32 ULP is 8.0 — a
3.5-heat laser shot ROUNDS TO ZERO (reaching Degradation 5000 needs ~2 billion shots). Coolant is
DAMAGE-gated (not fire), the valve moves only on player toggle, AmbientTemperature is a hardcoded 300. So the
@@ -567,7 +565,8 @@ AggregateHeatSink cleared the last, `HeatSink/AmbientTemperature`), and every co
built (`[gskip]`=0). Diagnostic `BT_GAUGE_SKIP_LOG` (GAUGREND.cpp, gated) logs each unregistered primitive the
dev-parse skips — the tool that pinned the last 3 (⚠ the gauge renderer builds LAZILY, so verification runs must wait
long enough for BuildConfigurationFile; a too-early kill shows [gparse]=0 and looks like "not built"). Remaining gauge
work is DATA FEEDS, not widgets: the valve/message CONTROL routes (0xBD3 SubsystemMessageManager, WAVE 8) and the
work is DATA FEEDS, not widgets — [UPDATE 2026-07-12: the 0xBD3 untangle landed (task #7) and the valve
CONTROL landed (task #13, live MoveValve via the rookie-role gate); only the message half remains]: the
status-message queue (StatusMessagePool) — deferred systems, cleanly marked. `SeekVoltageGraph::Execute` (@004c6934)
is a cluster-child (not config-called) — its 4 Seek* attributes are the only remaining publish, non-blocking.
@@ -590,8 +589,9 @@ is a cluster-child (not config-called) — its 4 Seek* attributes are the only r
6 condensers each read flow=0.166667 (=1/6).** ValveSetting→coolantFlowScale now authentic.
(3) **MoveValve** — the reconstructed @4afbe0 body was a MISLABEL (MechControlsMapper mode cycler);
disassembled the real handler @4ae464 (valveState cycle 1→5→50→0 + RecomputeCondenserValves) and replaced
it. DORMANT: its guard + message route go through the 0xBD3 SubsystemMessageManager (WAVE 8) → the valve
gauge shows the static authentic 1/N until 0xBD3 lands (no V-key bring-up stand-in added).
it. [UPDATE 2026-07-12: LIVE (task #13) — the guard was never the 0xBD3 messmgr; FUN_004ac9c8 is the
owning player's ROOKIE-ROLE lockout (roleClassIndex@0x274==0, task #12 bridge). MoveValve is registered
(id 4, table @0x50E52C), verified valveState 1→5 / flow 1/6→5/10; 'C' key + BT_VALVE_SLOT.]
-**Combat SCORING feed** (KILLS/SCORE) — DONE (commit 34aaa7d; scoring-feed-decode workflow). The
scoreboard read 0 for 4 reasons (none was handler logic — the ScoreMessage/VehicleDead/ScoreInflicted
handlers were already reconstructed): (1) no producer — added the posts (per-hit ScoreInflicted at the
@@ -618,10 +618,14 @@ is a cluster-child (not config-called) — its 4 Seek* attributes are the only r
### Phase 4 — DEEP/DEFERRED
Byte-exact re-base of the PoweredSubsystem:HeatSink LEAF (Sensor/Emitter/Myomers) → radarPercent heat penalty
(delete the sensor.cpp:287-288 guard) + the raw-offset cluster reads; radar pip/name symbology + staticEntities;
CycleDisplayMode→ModeManager mask (activates cmArmor/cmCrit/cmHeat schematic layers); numericSpeed units.
(delete the sensor.cpp:287-288 guard — ✅ LANDED: the guard is deleted and the leaf is byte-exact,
sensor.cpp:407-413) + the raw-offset cluster reads (partially locked since — MechWeapon byte-exact);
radar pip/name symbology + staticEntities (task #17, under reconstruction 2026-07-12);
CycleDisplayMode→ModeManager mask (machinery reconstructed, gauge-layer toggle unverified [T3]);
numericSpeed units (open).
### Authentically-static (do NOT "fix")
currentTemperature ~77 (huge thermalMass); Degradation/Failure temps (fixed threshold markers); AmbientTemperature
[currentTemperature "~77 static" REMOVED 2026-07-11 — dynamic at the authentic heat economy, tasks #9/#10.]
Degradation/Failure temps (fixed threshold markers); AmbientTemperature
300; MaxRunSpeed (per-mech constant); cmArmor/cmCrit all-green (undamaged solo player — BT is PvP-only);
CoolantMassLeakRate 0 on a pristine mech (damage-gated); Player RANK 1 in solo.
+9 -12
View File
@@ -437,18 +437,15 @@ void
//----------------------------------------------
Check(warehousePointer);
// DEV-COMPOSITE GUARD: the reconstructed BTL4GaugeRenderer's warehouse / gaugeImageBin
// is not fully wired (its internal chain reads garbage -> WarehouseBin::Find AVs).
// Skip the per-entity gauge-IMAGE caching when the gauge renderer is woken on a dev
// box (BT_DEV_GAUGES); the radar/secondary composite doesn't need per-mech images.
// Inert on the pod (the real warehouse is valid). FAITHFUL FOLLOW-UP: fix the
// BTL4GaugeRenderer warehouse reconstruction (likely a shadowed warehousePointer).
if (getenv("BT_DEV_GAUGES") == NULL)
{
((L4Warehouse*)warehousePointer)->gaugeImageBin.Get(
interesting_entity->GetResourceID()
);
}
// (task #17: the old DEV-COMPOSITE GUARD here -- "the reconstructed
// warehouse chain reads garbage / shadowed warehousePointer" -- described
// a bug fixed by later shadow sweeps; the BT ctor allocates a real
// L4Warehouse before the cfg parse and L4GaugeImage::Make is NULL-safe on
// missing type-0x12 resources. Guard removed; the radar pip cache now
// fills on dev boxes too.)
((L4Warehouse*)warehousePointer)->gaugeImageBin.Get(
interesting_entity->GetResourceID()
);
//----------------------------------------------
// Update gaugeRenderer
//----------------------------------------------
+4 -3
View File
@@ -207,9 +207,10 @@ void SVGA16::DrawDevSurface(LPDIRECT3DDEVICE9 device, int slot, int mask, int pa
// gauge buffer (masks/positions from content/GAUGE/L4GAUGE.CFG MechInit @4395):
// sec = radar/secondary (palette) Heat = UL Mfd2 = UC Comm = UR
// Mfd1 = LL Mfd3 = LR
// monoTint < 0 -> palette-expand; else the mono tint for that MFD plane. The MFD
// content today is mostly the authored cockpit frame art (btquad/btcomm/btheat) --
// the live widgets are BT-specific gauge classes not yet reconstructed (parse-skipped).
// monoTint < 0 -> palette-expand; else the mono tint for that MFD plane.
// (2026-07-12: the "widgets not yet reconstructed / parse-skipped" note that
// lived here is long superseded -- every config widget is registered and
// built, 0 parse-skips; see docs/GAUGE_COMPOSITE.md.)
//
struct BTGaugeSurfaceDesc
{
+81 -77
View File
@@ -63,6 +63,13 @@
//
#include <bt.hpp>
// task #17 (radar symbology): the "pip" system is the ENGINE's L4GaugeImage
// (vector-stroke shapes, resource type 0x12) cached in L4Warehouse::
// gaugeImageBin; name labels are the Mission's prebuilt egg bitmaps.
#include <L4GAUIMA.hpp> // L4GaugeImage -- the pip vector shapes (fwd shim)
#include <L4WRHOUS.hpp> // L4Warehouse::gaugeImageBin -- the pip table
#include <MISSION.hpp> // Mission::GetSmallNameBitmap -- the name labels
#include <PLAYER.hpp> // Player::playerBitmapIndex
#pragma hdrstop
#if !defined(BTL4RDR_HPP)
@@ -104,21 +111,20 @@ static int
//###########################################################################
//
// A tracked entity's "video object": carries the name-bitmap id used for the
// floating label and the entity's current weapons target. (BT engine concept;
// absent from the WinTesla Entity.)
// task #17 DECODED: the "video object" at *(entity+0x190) IS Entity::
// owningPlayer (ENTITY.h:589; resourceID is the adjacent +0x1BC) -- the radar
// reads its playerBitmapIndex (+0x1E0, the egg 'bitmapindex' key) for the
// name label and BTPlayer::objectiveMech (+0x284) for the target box. The
// old invented VideoObject class is retired.
//
class VideoObject
{
public:
int nameID;
Entity *target;
};
// The "pip" is the ENGINE's L4GaugeImage (L4GAUIMA.h -- FUN_0046f0c0 IS
// L4GaugeImage::Draw line-for-line), and the "pip table" is L4Warehouse::
// gaugeImageBin (WarehouseBinOf<L4GaugeImage>, warehouse+0x58) reached via
// GaugeRenderer::warehousePointer (+0x4C) -- keyed by Entity::resourceID
// (BTL4.RES ships 110 type-0x12 vector shapes: every mech/vehicle/building/
// tree). Thin facades keep the recovered draw-loop shape:
//
// One radar symbol ("pip") + the per-model lookup table that owns them.
//
class Pip
class Pip // == L4GaugeImage
{
public:
void
@@ -129,14 +135,25 @@ public:
int color,
AffineMatrix &transform,
int box_color
);
)
{
((L4GaugeImage *)this)->Draw(lod_index, meters_per_pixel,
view, color, transform, box_color);
}
};
class PipTable
class PipTable // == L4Warehouse
{
public:
Pip *
LookUpPip(int model_class_ID, int variant);
LookUpPip(ResourceDescription::ResourceID entity_resource_ID)
{
// FUN_004688ce(warehouse+0x58, entity+0x1BC, 0) -- the refcounting
// Get, exactly as the binary's per-frame loop calls it (the
// mission-end Purge reclaims; one vector shape per model).
return (Pip *)((L4Warehouse *)this)->gaugeImageBin.Get(
entity_resource_ID, False);
}
};
//
@@ -165,11 +182,10 @@ static PipTable *
GetPipTable(GaugeRenderer *renderer);
//
// Per-entity video object, name-bitmap cache, and the "labelled static entity"
// test (nav beacons etc. carry a name bitmap).
// Name-label + target resolution (task #17): the label bitmap comes from the
// owning Player's bitmapindex through the Mission's SMALL prebuilt-raster
// chain; the target from the owning BTPlayer's objectiveMech (bridge).
//
static VideoObject *
GetVideoObject(Entity *entity);
static BitMap *
LookUpNameBitmap(int name_ID);
static int
@@ -222,18 +238,17 @@ void
return;
}
VideoObject *video_object = GetVideoObject(entity); // *(entity+400)
if (video_object == NULL)
Player *owning_player = entity->GetOwningPlayer(); // *(entity+0x190)
if (owning_player == NULL)
{
nameBitmap = NULL;
nameBitmap = NULL; // unowned (dummy/effect): no label
}
else
{
//
// Resolve the entity's name id through the global name-bitmap cache.
//
int nameID = video_object->nameID; // *(vobj+0x1E0)
nameBitmap = LookUpNameBitmap(nameID); // cache vtbl+0x0C
// player+0x1E0 playerBitmapIndex -> the Mission's SMALL name-raster
// chain (prebuilt 64x16 egg bitmaps, loaded 1-based by
// Mission::LoadNameBitmaps).
nameBitmap = LookUpNameBitmap(owning_player->playerBitmapIndex);
}
//
@@ -790,7 +805,7 @@ void
}
Pip *pip = (pips != NULL)
? pips->LookUpPip(entity->GetClassID(), 0) // FUN_004688ce(pips+0x58,entity+0x1bc,0)
? pips->LookUpPip(entity->GetResourceID()) // FUN_004688ce(pips+0x58,entity+0x1bc,0)
: NULL;
if (pip != NULL)
{
@@ -875,7 +890,7 @@ void
}
Pip *pip = (pips != NULL)
? pips->LookUpPip(entity->GetClassID(), 0)
? pips->LookUpPip(entity->GetResourceID())
: NULL;
if (pip != NULL)
{
@@ -891,12 +906,9 @@ void
}
else if (entity->GetClassID() == 0xBB9) // MechClassID -- radar contacts are mechs
{
// BRING-UP: the authentic pip raster set (pips table) is not present
// in this engine build, so draw a simple cross blip at the contact's
// projected radar position (the target flashes in boxColor). Filtered
// to mechs so the ~300 dynamic world entities (effects/props) the grid
// carries don't clutter the display -- the real pip table would key the
// symbol off the model class, which is the proper follow-up.
// FALLBACK (kept, task #17): a mech whose model has no type-0x12
// pip resource (all shipped mechs HAVE one -- this covers dev
// content) still draws the cross blip at the projected position.
//
// Project delta = (entity - viewpoint) directly to radar pixels: the
// worldToView camera matrix bakes a scale that is wrong for a point
@@ -1000,28 +1012,9 @@ void
//###########################################################################
//###########################################################################
//
// FUN_0046f0c0 / FUN_004688ce -- radar pip symbol + per-model table.
//
void
Pip::Draw(
Scalar /*lod_index*/,
Scalar /*meters_per_pixel*/,
GraphicsView * /*view*/,
int /*color*/,
AffineMatrix & /*transform*/,
int /*box_color*/
)
{
// BEST-EFFORT: the pip raster set lives in the BT renderer's pip table,
// which is not present in this engine build.
}
Pip *
PipTable::LookUpPip(int /*model_class_ID*/, int /*variant*/)
{
return NULL;
}
// (task #17: the Pip::Draw / PipTable::LookUpPip stub bodies that lived here
// are retired -- both are inline facades over the engine's L4GaugeImage /
// L4Warehouse::gaugeImageBin now, see the class declarations above.)
//
// FUN_00417ab4 -- the gauge renderer's "operator entity" (the mech whose
@@ -1040,34 +1033,39 @@ static Entity *
}
//
// renderer+0x4C -- the BT pip table. Not present on the WinTesla renderer.
// renderer+0x4C -- GaugeRenderer::warehousePointer: the L4Warehouse whose
// gaugeImageBin (+0x58) is the pip table (task #17; the "not present on
// WinTesla" note was wrong -- the facility is engine T0, L4WRHOUS.h).
//
static PipTable *
GetPipTable(GaugeRenderer * /*renderer*/)
GetPipTable(GaugeRenderer *renderer)
{
return NULL;
return (renderer != NULL) ? (PipTable *)renderer->warehousePointer : NULL;
}
//
// *(entity+400) -- the entity's video object (name id + current target).
//
static VideoObject *
GetVideoObject(Entity * /*entity*/)
{
return NULL;
}
//
// The global name-bitmap cache (DAT_004efc94 App singleton, +0xC8 graphics).
// The "global name-bitmap cache" DECODED (task #17): DAT_004efc94+0xC8 is
// Application::currentMission, and the cache is the Mission's SMALL
// name-bitmap VChain (prebuilt 64x16 egg rasters keyed 1-based by the egg
// 'bitmapindex'). All T0 (MISSION.h / APP.h).
//
static BitMap *
LookUpNameBitmap(int /*name_ID*/)
LookUpNameBitmap(int name_ID)
{
return NULL;
Mission *mission = (application != NULL)
? application->GetCurrentMission() : NULL;
if (mission == NULL || name_ID <= 0)
{
return NULL;
}
return mission->GetSmallNameBitmap(name_ID);
}
//
// entity+0x21C -- a labelled entity's name id.
// entity+0x21C -- Landmark::landmarkID (cultural.h; the static loop's label
// key into the LARGE chain). DORMANT in shipped content: no runtime writer
// exists (the id was baked by the 1995 map tool) and no [landmarks] section
// exists in any egg -- kept faithful-but-inert [T1].
//
static int
GetNameID(Entity * /*entity*/)
@@ -1076,7 +1074,9 @@ static int
}
//
// FUN_0041a1a4 -- IsDerivedFrom(LabelledEntityClass).
// FUN_0041a1a4 vs 0x4e70c4 -- IsDerivedFrom("Landmark") (task #17 decode:
// the "LabelledEntityClass" IS the Landmark class, parent CulturalIcon).
// Dormant with GetNameID above (no landmark content ships).
//
static Logical
IsLabelledEntity(Entity * /*entity*/)
@@ -1090,8 +1090,12 @@ static Logical
static Entity *
GetTarget(Entity *owner)
{
VideoObject *video_object = (owner != NULL) ? GetVideoObject(owner) : NULL;
return (video_object != NULL) ? video_object->target : NULL;
// *(*(owner+0x190)+0x284) -- the owning BTPlayer's objectiveMech (the
// designated target; complete-type bridge, btplayer.cpp).
extern Entity *BTPlayerObjectiveMechOf(void *player);
Player *owning_player = (owner != NULL) ? owner->GetOwningPlayer() : NULL;
return (owning_player != NULL)
? BTPlayerObjectiveMechOf(owning_player) : NULL;
}
//
+16
View File
@@ -1492,3 +1492,19 @@ int BTPlayerRoleLocksAdvanced(void *owner_mech)
return 0; // dev-permissive [T3]
return (player->roleClassIndex == 0) ? 1 : 0; // player+0x274 == 0
}
//#############################################################################
// BTPlayerObjectiveMechOf -- complete-type bridge (task #17)
//
// The radar map's target resolve: *(*(owner+0x190)+0x284) = the owning
// BTPlayer's objectiveMech (the designated target; the same slot the
// power-routing valve gate reads past). btl4rdr.cpp can't include
// btplayer.hpp (TU type collisions), so it calls through this bridge.
//
Entity *BTPlayerObjectiveMechOf(void *player)
{
if (player == 0)
return 0;
return (Entity *)((BTPlayer *)player)->GetObjectiveMech(); // @0x284
}
+38 -7
View File
@@ -108,15 +108,46 @@ Simulation::AttributeIndexSet MechWeapon::AttributeIndex; // legacy empty member
// gauge data-binding wave: OutputVoltage/PercentDone -> rechargeLevel (1.0 ==
// ready; ComputeOutputVoltage/the charge state machine drive it, so both animate
// as the weapon recharges). Chained to PoweredSubsystem's dense index.
// task #16: the ID GAP between our parent chain (ends 0x0D) and the pinned
// binary ids MUST be padded -- see the enum note. Locked so a future parent
// publish shows up here instead of silently double-assigning ids:
static_assert((int)PoweredSubsystem::NextAttributeID
== (int)MechWeapon::MechWeaponPadFirstAttributeID,
"MechWeapon pad base != PoweredSubsystem::NextAttributeID -- re-count the pads");
const MechWeapon::IndexEntry
MechWeapon::AttributePointers[]=
{
// The authentic entry (binary table @0x511890): the streamed controls maps
// bind the pod fire buttons directly onto this Scalar -- fireImpulse IS the
// binary's "TriggerState" (the edge-detected fire channel destination).
ATTRIBUTE_ENTRY(MechWeapon, TriggerState, fireImpulse), // @0x31C id 0x13
ATTRIBUTE_ENTRY(MechWeapon, OutputVoltage, rechargeLevel), // @0x320 (charge/ready level)
ATTRIBUTE_ENTRY(MechWeapon, PercentDone, rechargeLevel) // @0x320 (recharge dial fraction)
// task #16: the FULL binary table @0x511890 (eleven entries) + the
// port-extra OutputVoltage alias. Every member offset matches the
// binary's (layout static_assert-locked); TriggerState stays the streamed
// fire-button binding (id 0x13 PINNED).
//
// The five PADS fill our chain-vs-binary id gap (0x0D..0x11) with valid,
// named, never-bound entries (the binary's own ids here are the powered/
// aux-screen family we don't publish); target = rechargeLevel, harmless.
{ (int)MechWeapon::MechWeaponPadFirstAttributeID + 0, "MechWeaponPad0D",
(Simulation::AttributePointer)&MechWeapon::rechargeLevel },
{ (int)MechWeapon::MechWeaponPadFirstAttributeID + 1, "MechWeaponPad0E",
(Simulation::AttributePointer)&MechWeapon::rechargeLevel },
{ (int)MechWeapon::MechWeaponPadFirstAttributeID + 2, "MechWeaponPad0F",
(Simulation::AttributePointer)&MechWeapon::rechargeLevel },
{ (int)MechWeapon::MechWeaponPadFirstAttributeID + 3, "MechWeaponPad10",
(Simulation::AttributePointer)&MechWeapon::rechargeLevel },
{ (int)MechWeapon::MechWeaponPadFirstAttributeID + 4, "MechWeaponPad11",
(Simulation::AttributePointer)&MechWeapon::rechargeLevel },
ATTRIBUTE_ENTRY(MechWeapon, PercentDone, rechargeLevel), // 0x12 @0x320
ATTRIBUTE_ENTRY(MechWeapon, TriggerState, fireImpulse), // 0x13 @0x31C
ATTRIBUTE_ENTRY(MechWeapon, DistanceToTarget, rangeToTarget), // 0x14 @0x324
ATTRIBUTE_ENTRY(MechWeapon, TargetWithinRange, targetWithinRange), // 0x15 @0x34C
ATTRIBUTE_ENTRY(MechWeapon, WeaponRange, effectiveRange), // 0x16 @0x328 (heat-degraded)
ATTRIBUTE_ENTRY(MechWeapon, PipPosition, pipPosition), // 0x17 @0x338
ATTRIBUTE_ENTRY(MechWeapon, PipColor, pipColor), // 0x18 @0x33C
ATTRIBUTE_ENTRY(MechWeapon, PipExtendedRange, pipExtendedRange), // 0x19 @0x348
ATTRIBUTE_ENTRY(MechWeapon, EstimatedReadyTime, estimatedReadyTime),// 0x1A @0x330
ATTRIBUTE_ENTRY(MechWeapon, RearFiring, usesExternalModel), // 0x1B @0x334
ATTRIBUTE_ENTRY(MechWeapon, WeaponState, weaponAlarm), // 0x1C @0x350
ATTRIBUTE_ENTRY(MechWeapon, OutputVoltage, rechargeLevel) // 0x1D (port alias)
};
MechWeapon::AttributeIndexSet&
@@ -303,7 +334,7 @@ MechWeapon::MechWeapon(
previousFireImpulse = 0.0f; // 0x3A4
recoil = 0.0f; // 0x3E8
useConfiguredPip = (usesExternalModel == 0); // 0x3E0
pipState = 0; // 0x330
estimatedReadyTime = 0; // 0x330 (binary attr "EstimatedReadyTime")
Check_Fpu();
}
+37 -5
View File
@@ -168,9 +168,38 @@ class CockpitHud;
// a few dense-index slots (Build sizes by max id) -- harmless.
// OutputVoltage/PercentDone are port gauge additions
// (name-resolved) renumbered after it.
TriggerStateAttributeID = 0x13,
OutputVoltageAttributeID, // 0x14
PercentDoneAttributeID, // 0x15
// task #16 (attribute parity): the FULL binary table @0x511890,
// ids renumbered to binary truth. PercentDone was 0x15 and the
// port-extra OutputVoltage alias squatted 0x14 -- both collided
// with the binary's DistanceToTarget/TargetWithinRange ids.
// (Numeric ids matter to the streamed type-6 maps -- which bind
// ONLY TriggerState 0x13, unchanged -- and to binary parity;
// gauge configs bind by NAME.)
//
// THE ID GAP MUST BE PADDED (task #16 crash): our reconstruction's
// parent chain ends at PoweredSubsystem::NextAttributeID == 0x0D
// (the binary's reaches 0x12 -- we publish fewer base attrs), and
// AttributeIndexSet::Build leaves the slots between UNINITIALIZED
// -- Find() strcmps EVERY slot, so a gap slot's garbage entryName
// is a latent AV (gotcha #11; the old pinned table survived on
// heap luck -- the renumber reshuffled allocations and it fired
// live in WeaponCluster's "PercentDone" resolve). Pads below fill
// ids 0x0D..0x11 (the mech.cpp attrPad idiom).
MechWeaponPadFirstAttributeID = 0x0D, // == PoweredSubsystem::NextAttributeID (locked below)
PercentDoneAttributeID = 0x12, // @0x320 rechargeLevel
TriggerStateAttributeID, // 0x13 @0x31C (PINNED -- the fire-button binding)
DistanceToTargetAttributeID, // 0x14 @0x324 rangeToTarget
TargetWithinRangeAttributeID, // 0x15 @0x34C
WeaponRangeAttributeID, // 0x16 @0x328 -- the HEAT-DEGRADED effectiveRange,
// not the configured max (binary truth)
PipPositionAttributeID, // 0x17 @0x338
PipColorAttributeID, // 0x18 @0x33C
PipExtendedRangeAttributeID, // 0x19 @0x348
EstimatedReadyTimeAttributeID, // 0x1A @0x330
RearFiringAttributeID, // 0x1B @0x334
WeaponStateAttributeID, // 0x1C @0x350 weaponAlarm
OutputVoltageAttributeID, // 0x1D PORT-EXTRA alias -> rechargeLevel (the
// engineering clusters bind it by name)
NextAttributeID
};
private:
@@ -361,8 +390,11 @@ class CockpitHud;
Scalar rangeToTarget; // @0x324 distance to current target (-1 if none)
Scalar effectiveRange; // @0x328 (1 - heatLoad) * weaponRange (heat-degraded)
Scalar weaponRange; // @0x32C resource WeaponRange (configured max)
int pipState; // @0x330 reset 0 (TODO: name -- pip/active state)
Logical usesExternalModel; // @0x334 resource model-name compare flag (best-effort)
int estimatedReadyTime; // @0x330 reset 0 (binary attr 0x1A "EstimatedReadyTime"
// names this slot -- resolves the old pipState TODO)
Logical usesExternalModel; // @0x334 the "EXT" model-name marker; the binary attr
// 0x1B names this slot "RearFiring" (an EXT/rear-mount
// weapon fires backward)
int pipPosition; // @0x338 resource PipPosition
RGBColor pipColor; // @0x33C resource PipColor (3 floats, init -1,-1,-1)
int pipExtendedRange; // @0x348 resource PipExtendedRange
+36 -6
View File
@@ -75,17 +75,47 @@ Derivation
"Myomers"
);
Receiver::MessageHandlerSet
Myomers::MessageHandlers;
// task #14: the REAL shared data. The old empty static
// MessageHandlers/AttributeIndex meant (a) no inherited message handlers
// (the powered ids 4-8 among them), (b) an EMPTY UNCHAINED attribute index --
// even CurrentTemperature/InputVoltage resolved NULL on Myomers instances, so
// the Myomer engineering panel's cluster children were silently blank. Both
// now chain PoweredSubsystem's sets, and the SEVEN binary attributes
// (IndexEntry table @00511588) are published. Function-local statics per the
// static-init-order rule (reconstruction-gotchas #9).
Receiver::MessageHandlerSet&
Myomers::GetMessageHandlers()
{
static Receiver::MessageHandlerSet messageHandlers(
PoweredSubsystem::GetMessageHandlers()); // copy-inherit (ids 4-8)
return messageHandlers;
}
Myomers::AttributeIndexSet
Myomers::AttributeIndex;
Myomers::AttributeIndexSet&
Myomers::GetAttributeIndex()
{
static const Myomers::IndexEntry entries[]=
{
ATTRIBUTE_ENTRY(Myomers, SpeedEffect, speedEffect), // 0x12 @0x31C
ATTRIBUTE_ENTRY(Myomers, CurrentSeekVoltageIndex, currentSeekVoltageIndex), // 0x13 @0x320
ATTRIBUTE_ENTRY(Myomers, RecommendedSeekVoltageIndex, recommendedSeekVoltageIndex), // 0x14 @0x324
ATTRIBUTE_ENTRY(Myomers, MinSeekVoltageIndex, minSeekVoltageIndex), // 0x15 @0x328
ATTRIBUTE_ENTRY(Myomers, MaxSeekVoltageIndex, maxSeekVoltageIndex), // 0x16 @0x32C
ATTRIBUTE_ENTRY(Myomers, SeekVoltage, seekVoltage), // 0x17 @0x330 (array base)
ATTRIBUTE_ENTRY(Myomers, OutputVoltage, outputVoltage) // 0x18 @0x344
};
static Myomers::AttributeIndexSet attributeIndex(
ELEMENTS(entries), entries,
PoweredSubsystem::GetAttributeIndex()
);
return attributeIndex;
}
Myomers::SharedData
Myomers::DefaultData(
&Myomers::ClassDerivations,
Myomers::MessageHandlers,
Myomers::AttributeIndex,
Myomers::GetMessageHandlers(),
Myomers::GetAttributeIndex(),
Myomers::StateCount
);
+6 -3
View File
@@ -111,7 +111,11 @@ class Mech;
public:
static Derivation ClassDerivations;
static SharedData DefaultData; // @0051154c
static Receiver::MessageHandlerSet MessageHandlers;
// task #14: accessor idiom (function-local statics) -- the old empty
// static MessageHandlers/AttributeIndex members starved the class of
// BOTH its own 7 binary attrs AND every inherited resolve (temps,
// InputVoltage) -> the Myomer engineering panel's children fed NULL.
static Receiver::MessageHandlerSet& GetMessageHandlers();
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Streamed model resource.
@@ -162,9 +166,8 @@ class Mech;
};
private:
static const IndexEntry AttributePointers[]; // @00511588
protected:
static AttributeIndexSet AttributeIndex;
static AttributeIndexSet& GetAttributeIndex(); // @00511588 table, task #14
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Test Class Support
+14 -163
View File
@@ -849,171 +849,19 @@ int
//###########################################################################
//###########################################################################
// Myomers
// (Myomers mislabel -- RETIRED, task #14)
//###########################################################################
//###########################################################################
//#############################################################################
// Shared Data Support
//
Myomers::SharedData
Myomers::DefaultData(
Myomers::GetClassDerivations(),
Myomers::GetMessageHandlers(),
Myomers::GetAttributeIndex(),
Myomers::StateCount
);
Derivation*
Myomers::GetClassDerivations()
{
static Derivation classDerivations(PoweredSubsystem::GetClassDerivations(), "Myomers");
return &classDerivations;
}
// The "Myomers" block that lived here (ctor @4b1d18, classID 0xBC3,
// outputVoltage/powered/voltageAvailable) was a MISLABEL of Sensor (real
// reconstruction: sensor.cpp). Its Myomers::DefaultData / ctor / dtor /
// TestInstance / MyomersSimulation / ResetToInitialState / HandleMessage /
// CreateStreamedSubsystem definitions ODR-DUPLICATED the REAL Myomers
// (myomers.cpp, classID 0xBC6) -- /FORCE picked the winner by link order,
// so which SharedData live Myomers instances got was link-order-dependent
// (the same archetype as the fixed Condenser stub dedup). Retired whole.
//
// @004b1d18 -- master myomer bundle registers a separate Performance and a
// status-flag bit (0x8); copies skip the live simulation.
//
Myomers::Myomers(
Mech *owner,
int subsystem_ID,
SubsystemResource *subsystem_resource,
SharedData &shared_data
):
PoweredSubsystem(owner, subsystem_ID, subsystem_resource, /*shared_data=*/DefaultData) // FUN_004b0f74(..,&DAT_0050fa1c,0,0)
{
outputVoltage = 1.0f; // this[0xc7]
powered = 0; // this[0xc8]
voltageAvailable = 0; // this[0xc9]
simulationFlags |= 0x8; // this[0xa] |= 8
if ((subsystem_resource->subsystemFlags & SegmentCopyMask) != 4) // (flags & 0xC) != 4
{
SetPerformance((Performance)&Myomers::MyomersSimulation); // PTR @0050fa94
}
}
//
// @004b1d90
//
Myomers::~Myomers()
{
Check(this);
BT_ClearMyomers((Mech *)owner); // *(this[0x34]+0x374) = 0
Check_Fpu();
}
Logical Myomers::TestClass(Mech &) { return True; }
Logical Myomers::TestInstance() const // @004b1e18
{
return IsDerivedFrom(*GetClassDerivations()); // FUN_0041a1a4(..,0x50fa2c)
}
//
// @004b1c4c -- compute the actuator drive voltage available to the locomotion
// model from the powering generator, throttled by heat damage and gated by
// the electrical state.
//
void
Myomers::MyomersSimulation(Scalar time_slice)
{
PoweredSubsystem::PoweredSubsystemSimulation(time_slice); // FUN_004b0bd0
Generator *source = (Generator *)voltageSource.Resolve(); // this[0x38] linked source
outputVoltage = RatedVoltageRef - source->MeasuredVoltage(); // 1.0 - *(source+0x158)
if (simulationState == 1) // this[0x10] @0x40
{
outputVoltage = 0.0f;
powered = 0;
}
else
{
powered = 1;
}
if (electricalStateAlarm.GetLevel() == Ready) // this[0x9e] == 4
{
voltageAvailable = 0;
}
else
{
voltageAvailable = 1;
outputVoltage = 0.0f;
}
//
// Heat damage (HeatSink heat-state @0x184) throttles the drive.
//
switch (heatAlarm.GetLevel()) // this+0x184 (heat-state inside heatAlarm@0x170+0x14)
{
case HeatSink::NormalHeat: // 0
powered = 1;
break;
case HeatSink::DegradationHeat: // 1
outputVoltage *= DegradedDriveScale; // *= 0.5
powered = 1;
break;
case HeatSink::FailureHeat: // 2
outputVoltage = 0.0f;
powered = 0;
break;
}
}
//
// @004b1c18 -- reset clears the myomer output state, then chains to the base.
//
void
Myomers::ResetToInitialState(Logical powered_flag)
{
if (powered_flag)
{
outputVoltage = 1.0f; // this[0xc7]
powered = 0; // this[0xc8]
voltageAvailable = 0; // this[0xc9]
}
PoweredSubsystem::ResetToInitialState(powered_flag); // FUN_004b0e6c
}
//
// @004b1bfc -- straight delegation to the base.
//
Logical
Myomers::HandleMessage(int message)
{
return PoweredSubsystem::HandleMessage(message); // FUN_004b0efc
}
//
// @004b1dcc -- no extra resource fields; just tags the record as Myomers.
//
int
Myomers::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( // FUN_004b13ac
model_file, model_name, subsystem_name,
subsystem_resource, subsystem_file, directories, passes
)
)
{
return False;
}
subsystem_resource->subsystemModelSize = 0x190;
subsystem_resource->classID = RegisteredClass::MyomersClassID; // 0x0bc3, resource+0x20
return True;
}
//###########################################################################
//###########################################################################
@@ -1509,8 +1357,11 @@ Subsystem *CreatePoweredSubsystem(Mech *owner, int id, void *seg)
// Bridge for the vehicleSubSystems gauge factory (btl4gau2.cpp). The engineering-
// screen assignment lives on PoweredSubsystem (auxScreenNumber / auxScreenPlacement
// / auxScreenLabel, resource +0x104/+0x108/+0x10C). Reading it from btl4gau2.cpp
// via a raw offset (sub+0x1dc) is wrong because the reconstructed heat-leaf branch
// is not byte-exact -- so the gauge factory calls this bridge, which casts through
// via a raw offset (sub+0x1dc) is wrong because raw offsets on our compiled
// objects are the databinding trap (2026-07-12: the "heat-leaf not byte-exact"
// wording is stale -- the leaf IS byte-exact since the sensor re-base,
// sensor.cpp:407-413 -- but the bridge stays: named members are the rule)
// -- so the gauge factory calls this bridge, which casts through
// the real PoweredSubsystem type and returns the NAMED fields. Returns False for
// non-PoweredSubsystem-derived subsystems (== the FUN_0041a1a4 / 0x50f4bc type
// filter the vehicleSubSystems Make applies before dispatch).
+7 -75
View File
@@ -296,81 +296,13 @@ class Generator;
static_assert(offsetof(PoweredSubsystem, thermalResistivityCoefficient)== 0x30C, "PoweredSubsystem::thermalResistivityCoefficient @0x30C");
static_assert(sizeof(PoweredSubsystem) == 0x31C, "sizeof(PoweredSubsystem) 0x31C");
};
//###########################################################################
//############################# Myomers #################################
//###########################################################################
//
// The mech's artificial-muscle bundle: a PoweredSubsystem whose output
// voltage (and hence the actuator drive available to the locomotion model)
// tracks the powering generator and is throttled back by heat damage.
//
// vtable @0050fb0c, ctor @004b1d18, dtor @004b1d90, shared data @0050fa1c.
// RegisteredClass::MyomersClassID == 0x0bc3.
//
class Myomers:
public PoweredSubsystem
{
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Shared Data Support
//
public:
static Derivation *GetClassDerivations(); // @0050fa2c
static SharedData DefaultData; // @0050fa1c
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Test Class Support
//
public:
static Logical
TestClass(Mech&);
Logical
TestInstance() const; // @004b1e18
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Construction and Destruction
//
public:
typedef PoweredSubsystem::SubsystemResource SubsystemResource;
Myomers(
Mech *owner,
int subsystem_ID,
SubsystemResource *subsystem_resource,
SharedData &shared_data = DefaultData
); // @004b1d18
~Myomers(); // @004b1d90
static int
CreateStreamedSubsystem( // @004b1dcc
NotationFile *model_file,
const char *model_name,
const char *subsystem_name,
SubsystemResource *subsystem_resource,
NotationFile *subsystem_file,
const ResourceDirectories *directories,
int passes = 1
);
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Simulation / overrides
//
public:
void
MyomersSimulation(Scalar time_slice); // @004b1c4c (Performance, PTR @0050fa94)
void
ResetToInitialState(Logical powered = True); // slot 10, @004b1c18
Logical
HandleMessage(int message); // slot 9, @004b1bfc (-> PoweredSubsystem)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Local data.
//
protected:
Scalar outputVoltage; // @0x31C init 1.0f -- drive available to the locomotion model
int powered; // @0x320 init 0 -- True while the generator is on-line
int voltageAvailable; // @0x324 init 0 -- True while electricalState == Ready
};
// (task #14: the "Myomers" class that lived here -- vtable 0050fb0c / ctor
// @4b1d18 / classID 0xBC3, members outputVoltage/powered/voltageAvailable --
// was a MISLABEL of **Sensor** (the real reconstruction is sensor.cpp/hpp;
// the real Myomers, classID 0xBC6, is myomers.cpp/hpp). Its definitions
// ODR-DUPLICATED ?DefaultData@Myomers@@ etc. against the real class, and
// /FORCE silently picked the winner by link order -- the real Myomers could
// receive this block's SharedData or vice versa. RETIRED whole.)
//###########################################################################
//############################# Generator ##############################
+17 -3
View File
@@ -52,14 +52,28 @@ Derivation
Receiver::MessageHandlerSet
ThermalSight::MessageHandlers;
ThermalSight::AttributeIndexSet
ThermalSight::AttributeIndex;
// task #16: publish the binary attr table @00511270 -- one entry,
// {LightState (id 4) -> thermalActive @0x1D8} (offset encoded 0x1D9 = 0x1D8|1).
// Function-local statics per the static-init-order rule.
ThermalSight::AttributeIndexSet&
ThermalSight::GetAttributeIndex()
{
static const ThermalSight::IndexEntry entries[]=
{
ATTRIBUTE_ENTRY(ThermalSight, LightState, thermalActive) // id 4 @0x1D8
};
static ThermalSight::AttributeIndexSet attributeIndex(
ELEMENTS(entries), entries,
PowerWatcher::GetAttributeIndex()
);
return attributeIndex;
}
ThermalSight::SharedData
ThermalSight::DefaultData( // @00511228
&ThermalSight::ClassDerivations,
ThermalSight::MessageHandlers,
ThermalSight::AttributeIndex,
ThermalSight::GetAttributeIndex(),
ThermalSight::StateCount
);
+1 -1
View File
@@ -109,7 +109,7 @@
private:
static const IndexEntry AttributePointers[]; // @00511270
protected:
static AttributeIndexSet AttributeIndex;
static AttributeIndexSet& GetAttributeIndex(); // @00511270 table (task #16)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Local data.