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:
co-authored by
Claude Fable 5
parent
80cadc98c7
commit
dd27238ceb
@@ -32,7 +32,7 @@ NOT `SearchList(clip_id, 16)` (which treats the arg as a LIST → walks garbage
|
|||||||
|---|---|---|
|
|---|---|---|
|
||||||
| SkeletonStream | — | `.SKL` skeletons |
|
| SkeletonStream | — | `.SKL` skeletons |
|
||||||
| DamageZoneStream | **0x14** | damage-zone records (Mech ctor reads count + array) |
|
| 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) |
|
| AnimationResourceType | **16 (0x10)** | animation clips (lowercase: `blhrrl`=904, `blhrrr`=905, `blhwwli`=910, `blhrrli`=916) |
|
||||||
| damage-zone segment list | 0x14 | per-zone segment list |
|
| damage-zone segment list | 0x14 | per-zone segment list |
|
||||||
| critical-subsystem segment | 0x1e | crit subsystem segments |
|
| critical-subsystem segment | 0x1e | crit subsystem segments |
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ source_sections: "docs/GAUGE_COMPOSITE.md (full map); CLAUDE.md §8"
|
|||||||
related_topics: [reconstruction-gotchas, decomp-reference, subsystems, pod-hardware]
|
related_topics: [reconstruction-gotchas, decomp-reference, subsystems, pod-hardware]
|
||||||
key_terms: [gauge, methodDescription, attribute-pointer, GaugeRenderer, MFD, PCC]
|
key_terms: [gauge, methodDescription, attribute-pointer, GaugeRenderer, MFD, PCC]
|
||||||
open_questions:
|
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"
|
- "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
|
# Cockpit Gauges / MFD HUD
|
||||||
|
|||||||
@@ -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.
|
AVs. Fill gaps with a shared read-only pad member. Same for a class's `<Name>AttributeID` enum.
|
||||||
[T2]
|
[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)
|
## 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
|
The 1995 pod ran frame-locked; reconstructed per-frame logic can carry HIDDEN frame-rate
|
||||||
|
|||||||
+31
-27
@@ -1,9 +1,8 @@
|
|||||||
# Gauge / MFD render pipeline — map + dev-composite plan
|
# Gauge / MFD render pipeline — map + dev-composite plan
|
||||||
|
|
||||||
**Status:** pipeline MAPPED (workflow `gauge-pipeline-map`, 2026-07); the dev composite is **planned, not
|
**Status (updated 2026-07-12):** the dev composite is **BUILT AND LIVE** (Milestones A/B/C done —
|
||||||
yet built**. This is the execute-ready plan for making the pod's gauges/MFDs render + be VISUALLY testable
|
BT_DEV_GAUGES / BT_DEV_GAUGES_DOCK; all 6 MFD surfaces composite; 50/50 attr bindings, 0 parse-skips).
|
||||||
on a dev box — the "MFD compositing on dev" follow-up the platform profile gates (see §8 / the PLATFORM
|
The header below preserved the original PLAN framing; see the increments + Phase logs for what landed.
|
||||||
PROFILE entry in CLAUDE.md). Companion: the platform-profile scaffold (`-platform pod|dev`).
|
|
||||||
|
|
||||||
## The draw model — CONFIRMED (the good news)
|
## 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).
|
Resource fetch = `renderer->warehousePointer->bitMapBin.Get/Release(name)` (FUN_00442aec/00442c12).
|
||||||
|
|
||||||
### Deferred / follow-ups
|
### Deferred / follow-ups
|
||||||
- `HeatSink/AmbientTemperature` maps to `ambientTemperature`@0x1D4 on the AGGREGATE HeatSink-bank class
|
- ✅ `HeatSink/AmbientTemperature` — LANDED (AggregateHeatSink 0xBBE reconstructed, commit 16f5f54;
|
||||||
(0xBBE, ctor @4ae8d0) which is `#if 0`'d — reviving it + its own table is a separate task (Heat `A`
|
cleared the last NULL binding; the bank is also the ambient radiator since task #9).
|
||||||
readout stays 0 until then).
|
- ✅ Reservoir shadow — LANDED (heatfamily_reslice.cpp now writes thermalCapacity from the resource
|
||||||
- Reservoir shadow: `Reservoir::coolantCapacity`@0x128 shadows the inherited `thermalCapacity` the gauge
|
coolantCapacity; the shadow member is gone).
|
||||||
reads → `Reservoir/CoolantCapacity` reads the base default; fix = delete the shadow + write thermalCapacity
|
|
||||||
in the Reservoir ctor (bundle with the Reservoir coolant-bar verification).
|
|
||||||
- ✅ **oneOfSeveralPixInt** DONE (increment 5). The Mech table now reaches 0x37 so `DuckState` resolves;
|
- ✅ **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.
|
`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
|
- ✅ **map** DONE (increment 6) — view wedge + live contact blips; ✅ `SetTargetRange` LANDED (radar
|
||||||
classification (0 contacts today) + the stubbed pip/name/video-object infrastructure (contacts + labels)
|
zoom). REMAINING: the stubbed pip/name/video-object symbology (contacts draw the cross-blip stand-in,
|
||||||
+ `SetTargetRange` (radar zoom, currently a 500m default).
|
no labels) — under reconstruction (task #17, 2026-07-12).
|
||||||
- Remaining widgets (task #13): `PlayerStatus` (comm/score, 8 uses), `vehicleSubSystems` (26 uses).
|
- ✅ 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)
|
### Remaining widgets — scope (assessed 2026-07; NOT quick registration wins)
|
||||||
- ✅ **PlayerStatus** DONE (increment 7; commit b691863) — reconstructed the whole family (mapped by the
|
- ✅ **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
|
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
|
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.
|
`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
|
- ✅ **vehicleSubSystems** — LANDED (docs/VEHICLE_SUBSYSTEMS.md: 7 authentic engineering panels with
|
||||||
from-scratch. Config: `vehicleSubSystems(qjak0.pcc..qjak7.pcc)` (8 subsystem-group bitmaps, no attribute
|
real aux-screen positions via the BTGetSubsystemAuxScreen bridge). The from-scratch scoping note above
|
||||||
bindings -- reads the mech's subsystem roster internally). Find its Make/ctor/Execute via the .data
|
is historical.
|
||||||
methodDescription table (the "vehicleSubSystems" string is a .data const, not inlined in the decomp).
|
|
||||||
A focused effort (~250-300 lines across 3-5 functions).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -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
|
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
|
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 →
|
(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
|
authentic value → fix. **[SUPERSEDED 2026-07-11, tasks #9/#10: the reframe below was computed at the
|
||||||
is authentically NEAR-STATIC, not a live heat-accumulation display.** `currentTemperature =
|
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
|
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
|
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
|
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
|
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
|
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
|
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)
|
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.
|
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.
|
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);
|
(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
|
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
|
it. [UPDATE 2026-07-12: LIVE (task #13) — the guard was never the 0xBD3 messmgr; FUN_004ac9c8 is the
|
||||||
gauge shows the static authentic 1/N until 0xBD3 lands (no V-key bring-up stand-in added).
|
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
|
- ✅ **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
|
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
|
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
|
### Phase 4 — DEEP/DEFERRED
|
||||||
Byte-exact re-base of the PoweredSubsystem:HeatSink LEAF (Sensor/Emitter/Myomers) → radarPercent heat penalty
|
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;
|
(delete the sensor.cpp:287-288 guard — ✅ LANDED: the guard is deleted and the leaf is byte-exact,
|
||||||
CycleDisplayMode→ModeManager mask (activates cmArmor/cmCrit/cmHeat schematic layers); numericSpeed units.
|
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")
|
### 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);
|
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.
|
CoolantMassLeakRate 0 on a pristine mech (damage-gated); Player RANK 1 in solo.
|
||||||
|
|||||||
@@ -437,18 +437,15 @@ void
|
|||||||
//----------------------------------------------
|
//----------------------------------------------
|
||||||
Check(warehousePointer);
|
Check(warehousePointer);
|
||||||
|
|
||||||
// DEV-COMPOSITE GUARD: the reconstructed BTL4GaugeRenderer's warehouse / gaugeImageBin
|
// (task #17: the old DEV-COMPOSITE GUARD here -- "the reconstructed
|
||||||
// is not fully wired (its internal chain reads garbage -> WarehouseBin::Find AVs).
|
// warehouse chain reads garbage / shadowed warehousePointer" -- described
|
||||||
// Skip the per-entity gauge-IMAGE caching when the gauge renderer is woken on a dev
|
// a bug fixed by later shadow sweeps; the BT ctor allocates a real
|
||||||
// box (BT_DEV_GAUGES); the radar/secondary composite doesn't need per-mech images.
|
// L4Warehouse before the cfg parse and L4GaugeImage::Make is NULL-safe on
|
||||||
// Inert on the pod (the real warehouse is valid). FAITHFUL FOLLOW-UP: fix the
|
// missing type-0x12 resources. Guard removed; the radar pip cache now
|
||||||
// BTL4GaugeRenderer warehouse reconstruction (likely a shadowed warehousePointer).
|
// fills on dev boxes too.)
|
||||||
if (getenv("BT_DEV_GAUGES") == NULL)
|
((L4Warehouse*)warehousePointer)->gaugeImageBin.Get(
|
||||||
{
|
interesting_entity->GetResourceID()
|
||||||
((L4Warehouse*)warehousePointer)->gaugeImageBin.Get(
|
);
|
||||||
interesting_entity->GetResourceID()
|
|
||||||
);
|
|
||||||
}
|
|
||||||
//----------------------------------------------
|
//----------------------------------------------
|
||||||
// Update gaugeRenderer
|
// Update gaugeRenderer
|
||||||
//----------------------------------------------
|
//----------------------------------------------
|
||||||
|
|||||||
@@ -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):
|
// gauge buffer (masks/positions from content/GAUGE/L4GAUGE.CFG MechInit @4395):
|
||||||
// sec = radar/secondary (palette) Heat = UL Mfd2 = UC Comm = UR
|
// sec = radar/secondary (palette) Heat = UL Mfd2 = UC Comm = UR
|
||||||
// Mfd1 = LL Mfd3 = LR
|
// Mfd1 = LL Mfd3 = LR
|
||||||
// monoTint < 0 -> palette-expand; else the mono tint for that MFD plane. The MFD
|
// monoTint < 0 -> palette-expand; else the mono tint for that MFD plane.
|
||||||
// content today is mostly the authored cockpit frame art (btquad/btcomm/btheat) --
|
// (2026-07-12: the "widgets not yet reconstructed / parse-skipped" note that
|
||||||
// the live widgets are BT-specific gauge classes not yet reconstructed (parse-skipped).
|
// lived here is long superseded -- every config widget is registered and
|
||||||
|
// built, 0 parse-skips; see docs/GAUGE_COMPOSITE.md.)
|
||||||
//
|
//
|
||||||
struct BTGaugeSurfaceDesc
|
struct BTGaugeSurfaceDesc
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -63,6 +63,13 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include <bt.hpp>
|
#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
|
#pragma hdrstop
|
||||||
|
|
||||||
#if !defined(BTL4RDR_HPP)
|
#if !defined(BTL4RDR_HPP)
|
||||||
@@ -104,21 +111,20 @@ static int
|
|||||||
//###########################################################################
|
//###########################################################################
|
||||||
|
|
||||||
//
|
//
|
||||||
// A tracked entity's "video object": carries the name-bitmap id used for the
|
// task #17 DECODED: the "video object" at *(entity+0x190) IS Entity::
|
||||||
// floating label and the entity's current weapons target. (BT engine concept;
|
// owningPlayer (ENTITY.h:589; resourceID is the adjacent +0x1BC) -- the radar
|
||||||
// absent from the WinTesla Entity.)
|
// 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
|
// The "pip" is the ENGINE's L4GaugeImage (L4GAUIMA.h -- FUN_0046f0c0 IS
|
||||||
{
|
// L4GaugeImage::Draw line-for-line), and the "pip table" is L4Warehouse::
|
||||||
public:
|
// gaugeImageBin (WarehouseBinOf<L4GaugeImage>, warehouse+0x58) reached via
|
||||||
int nameID;
|
// GaugeRenderer::warehousePointer (+0x4C) -- keyed by Entity::resourceID
|
||||||
Entity *target;
|
// (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 // == L4GaugeImage
|
||||||
//
|
|
||||||
class Pip
|
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
void
|
void
|
||||||
@@ -129,14 +135,25 @@ public:
|
|||||||
int color,
|
int color,
|
||||||
AffineMatrix &transform,
|
AffineMatrix &transform,
|
||||||
int box_color
|
int box_color
|
||||||
);
|
)
|
||||||
|
{
|
||||||
|
((L4GaugeImage *)this)->Draw(lod_index, meters_per_pixel,
|
||||||
|
view, color, transform, box_color);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class PipTable
|
class PipTable // == L4Warehouse
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Pip *
|
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);
|
GetPipTable(GaugeRenderer *renderer);
|
||||||
|
|
||||||
//
|
//
|
||||||
// Per-entity video object, name-bitmap cache, and the "labelled static entity"
|
// Name-label + target resolution (task #17): the label bitmap comes from the
|
||||||
// test (nav beacons etc. carry a name bitmap).
|
// 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 *
|
static BitMap *
|
||||||
LookUpNameBitmap(int name_ID);
|
LookUpNameBitmap(int name_ID);
|
||||||
static int
|
static int
|
||||||
@@ -222,18 +238,17 @@ void
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
VideoObject *video_object = GetVideoObject(entity); // *(entity+400)
|
Player *owning_player = entity->GetOwningPlayer(); // *(entity+0x190)
|
||||||
if (video_object == NULL)
|
if (owning_player == NULL)
|
||||||
{
|
{
|
||||||
nameBitmap = NULL;
|
nameBitmap = NULL; // unowned (dummy/effect): no label
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
//
|
// player+0x1E0 playerBitmapIndex -> the Mission's SMALL name-raster
|
||||||
// Resolve the entity's name id through the global name-bitmap cache.
|
// chain (prebuilt 64x16 egg bitmaps, loaded 1-based by
|
||||||
//
|
// Mission::LoadNameBitmaps).
|
||||||
int nameID = video_object->nameID; // *(vobj+0x1E0)
|
nameBitmap = LookUpNameBitmap(owning_player->playerBitmapIndex);
|
||||||
nameBitmap = LookUpNameBitmap(nameID); // cache vtbl+0x0C
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -790,7 +805,7 @@ void
|
|||||||
}
|
}
|
||||||
|
|
||||||
Pip *pip = (pips != NULL)
|
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;
|
: NULL;
|
||||||
if (pip != NULL)
|
if (pip != NULL)
|
||||||
{
|
{
|
||||||
@@ -875,7 +890,7 @@ void
|
|||||||
}
|
}
|
||||||
|
|
||||||
Pip *pip = (pips != NULL)
|
Pip *pip = (pips != NULL)
|
||||||
? pips->LookUpPip(entity->GetClassID(), 0)
|
? pips->LookUpPip(entity->GetResourceID())
|
||||||
: NULL;
|
: NULL;
|
||||||
if (pip != NULL)
|
if (pip != NULL)
|
||||||
{
|
{
|
||||||
@@ -891,12 +906,9 @@ void
|
|||||||
}
|
}
|
||||||
else if (entity->GetClassID() == 0xBB9) // MechClassID -- radar contacts are mechs
|
else if (entity->GetClassID() == 0xBB9) // MechClassID -- radar contacts are mechs
|
||||||
{
|
{
|
||||||
// BRING-UP: the authentic pip raster set (pips table) is not present
|
// FALLBACK (kept, task #17): a mech whose model has no type-0x12
|
||||||
// in this engine build, so draw a simple cross blip at the contact's
|
// pip resource (all shipped mechs HAVE one -- this covers dev
|
||||||
// projected radar position (the target flashes in boxColor). Filtered
|
// content) still draws the cross blip at the projected position.
|
||||||
// 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.
|
|
||||||
//
|
//
|
||||||
// Project delta = (entity - viewpoint) directly to radar pixels: the
|
// Project delta = (entity - viewpoint) directly to radar pixels: the
|
||||||
// worldToView camera matrix bakes a scale that is wrong for a point
|
// worldToView camera matrix bakes a scale that is wrong for a point
|
||||||
@@ -1000,28 +1012,9 @@ void
|
|||||||
//###########################################################################
|
//###########################################################################
|
||||||
//###########################################################################
|
//###########################################################################
|
||||||
|
|
||||||
//
|
// (task #17: the Pip::Draw / PipTable::LookUpPip stub bodies that lived here
|
||||||
// FUN_0046f0c0 / FUN_004688ce -- radar pip symbol + per-model table.
|
// are retired -- both are inline facades over the engine's L4GaugeImage /
|
||||||
//
|
// L4Warehouse::gaugeImageBin now, see the class declarations above.)
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// FUN_00417ab4 -- the gauge renderer's "operator entity" (the mech whose
|
// 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 *
|
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).
|
// The "global name-bitmap cache" DECODED (task #17): DAT_004efc94+0xC8 is
|
||||||
//
|
// Application::currentMission, and the cache is the Mission's SMALL
|
||||||
static VideoObject *
|
// name-bitmap VChain (prebuilt 64x16 egg rasters keyed 1-based by the egg
|
||||||
GetVideoObject(Entity * /*entity*/)
|
// 'bitmapindex'). All T0 (MISSION.h / APP.h).
|
||||||
{
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// The global name-bitmap cache (DAT_004efc94 App singleton, +0xC8 graphics).
|
|
||||||
//
|
//
|
||||||
static BitMap *
|
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
|
static int
|
||||||
GetNameID(Entity * /*entity*/)
|
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
|
static Logical
|
||||||
IsLabelledEntity(Entity * /*entity*/)
|
IsLabelledEntity(Entity * /*entity*/)
|
||||||
@@ -1090,8 +1090,12 @@ static Logical
|
|||||||
static Entity *
|
static Entity *
|
||||||
GetTarget(Entity *owner)
|
GetTarget(Entity *owner)
|
||||||
{
|
{
|
||||||
VideoObject *video_object = (owner != NULL) ? GetVideoObject(owner) : NULL;
|
// *(*(owner+0x190)+0x284) -- the owning BTPlayer's objectiveMech (the
|
||||||
return (video_object != NULL) ? video_object->target : NULL;
|
// 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|||||||
@@ -1492,3 +1492,19 @@ int BTPlayerRoleLocksAdvanced(void *owner_mech)
|
|||||||
return 0; // dev-permissive [T3]
|
return 0; // dev-permissive [T3]
|
||||||
return (player->roleClassIndex == 0) ? 1 : 0; // player+0x274 == 0
|
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
|
||||||
|
}
|
||||||
|
|||||||
@@ -108,15 +108,46 @@ Simulation::AttributeIndexSet MechWeapon::AttributeIndex; // legacy empty member
|
|||||||
// gauge data-binding wave: OutputVoltage/PercentDone -> rechargeLevel (1.0 ==
|
// gauge data-binding wave: OutputVoltage/PercentDone -> rechargeLevel (1.0 ==
|
||||||
// ready; ComputeOutputVoltage/the charge state machine drive it, so both animate
|
// ready; ComputeOutputVoltage/the charge state machine drive it, so both animate
|
||||||
// as the weapon recharges). Chained to PoweredSubsystem's dense index.
|
// 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
|
const MechWeapon::IndexEntry
|
||||||
MechWeapon::AttributePointers[]=
|
MechWeapon::AttributePointers[]=
|
||||||
{
|
{
|
||||||
// The authentic entry (binary table @0x511890): the streamed controls maps
|
// task #16: the FULL binary table @0x511890 (eleven entries) + the
|
||||||
// bind the pod fire buttons directly onto this Scalar -- fireImpulse IS the
|
// port-extra OutputVoltage alias. Every member offset matches the
|
||||||
// binary's "TriggerState" (the edge-detected fire channel destination).
|
// binary's (layout static_assert-locked); TriggerState stays the streamed
|
||||||
ATTRIBUTE_ENTRY(MechWeapon, TriggerState, fireImpulse), // @0x31C id 0x13
|
// fire-button binding (id 0x13 PINNED).
|
||||||
ATTRIBUTE_ENTRY(MechWeapon, OutputVoltage, rechargeLevel), // @0x320 (charge/ready level)
|
//
|
||||||
ATTRIBUTE_ENTRY(MechWeapon, PercentDone, rechargeLevel) // @0x320 (recharge dial fraction)
|
// 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&
|
MechWeapon::AttributeIndexSet&
|
||||||
@@ -303,7 +334,7 @@ MechWeapon::MechWeapon(
|
|||||||
previousFireImpulse = 0.0f; // 0x3A4
|
previousFireImpulse = 0.0f; // 0x3A4
|
||||||
recoil = 0.0f; // 0x3E8
|
recoil = 0.0f; // 0x3E8
|
||||||
useConfiguredPip = (usesExternalModel == 0); // 0x3E0
|
useConfiguredPip = (usesExternalModel == 0); // 0x3E0
|
||||||
pipState = 0; // 0x330
|
estimatedReadyTime = 0; // 0x330 (binary attr "EstimatedReadyTime")
|
||||||
|
|
||||||
Check_Fpu();
|
Check_Fpu();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -168,9 +168,38 @@ class CockpitHud;
|
|||||||
// a few dense-index slots (Build sizes by max id) -- harmless.
|
// a few dense-index slots (Build sizes by max id) -- harmless.
|
||||||
// OutputVoltage/PercentDone are port gauge additions
|
// OutputVoltage/PercentDone are port gauge additions
|
||||||
// (name-resolved) renumbered after it.
|
// (name-resolved) renumbered after it.
|
||||||
TriggerStateAttributeID = 0x13,
|
// task #16 (attribute parity): the FULL binary table @0x511890,
|
||||||
OutputVoltageAttributeID, // 0x14
|
// ids renumbered to binary truth. PercentDone was 0x15 and the
|
||||||
PercentDoneAttributeID, // 0x15
|
// 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
|
NextAttributeID
|
||||||
};
|
};
|
||||||
private:
|
private:
|
||||||
@@ -361,8 +390,11 @@ class CockpitHud;
|
|||||||
Scalar rangeToTarget; // @0x324 distance to current target (-1 if none)
|
Scalar rangeToTarget; // @0x324 distance to current target (-1 if none)
|
||||||
Scalar effectiveRange; // @0x328 (1 - heatLoad) * weaponRange (heat-degraded)
|
Scalar effectiveRange; // @0x328 (1 - heatLoad) * weaponRange (heat-degraded)
|
||||||
Scalar weaponRange; // @0x32C resource WeaponRange (configured max)
|
Scalar weaponRange; // @0x32C resource WeaponRange (configured max)
|
||||||
int pipState; // @0x330 reset 0 (TODO: name -- pip/active state)
|
int estimatedReadyTime; // @0x330 reset 0 (binary attr 0x1A "EstimatedReadyTime"
|
||||||
Logical usesExternalModel; // @0x334 resource model-name compare flag (best-effort)
|
// 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
|
int pipPosition; // @0x338 resource PipPosition
|
||||||
RGBColor pipColor; // @0x33C resource PipColor (3 floats, init -1,-1,-1)
|
RGBColor pipColor; // @0x33C resource PipColor (3 floats, init -1,-1,-1)
|
||||||
int pipExtendedRange; // @0x348 resource PipExtendedRange
|
int pipExtendedRange; // @0x348 resource PipExtendedRange
|
||||||
|
|||||||
@@ -75,17 +75,47 @@ Derivation
|
|||||||
"Myomers"
|
"Myomers"
|
||||||
);
|
);
|
||||||
|
|
||||||
Receiver::MessageHandlerSet
|
// task #14: the REAL shared data. The old empty static
|
||||||
Myomers::MessageHandlers;
|
// 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::AttributeIndexSet&
|
||||||
Myomers::AttributeIndex;
|
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::SharedData
|
||||||
Myomers::DefaultData(
|
Myomers::DefaultData(
|
||||||
&Myomers::ClassDerivations,
|
&Myomers::ClassDerivations,
|
||||||
Myomers::MessageHandlers,
|
Myomers::GetMessageHandlers(),
|
||||||
Myomers::AttributeIndex,
|
Myomers::GetAttributeIndex(),
|
||||||
Myomers::StateCount
|
Myomers::StateCount
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -111,7 +111,11 @@ class Mech;
|
|||||||
public:
|
public:
|
||||||
static Derivation ClassDerivations;
|
static Derivation ClassDerivations;
|
||||||
static SharedData DefaultData; // @0051154c
|
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.
|
// Streamed model resource.
|
||||||
@@ -162,9 +166,8 @@ class Mech;
|
|||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static const IndexEntry AttributePointers[]; // @00511588
|
|
||||||
protected:
|
protected:
|
||||||
static AttributeIndexSet AttributeIndex;
|
static AttributeIndexSet& GetAttributeIndex(); // @00511588 table, task #14
|
||||||
|
|
||||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
// Test Class Support
|
// Test Class Support
|
||||||
|
|||||||
+14
-163
@@ -849,171 +849,19 @@ int
|
|||||||
|
|
||||||
//###########################################################################
|
//###########################################################################
|
||||||
//###########################################################################
|
//###########################################################################
|
||||||
// Myomers
|
// (Myomers mislabel -- RETIRED, task #14)
|
||||||
//###########################################################################
|
//###########################################################################
|
||||||
//###########################################################################
|
//###########################################################################
|
||||||
|
|
||||||
//#############################################################################
|
|
||||||
// Shared Data Support
|
|
||||||
//
|
//
|
||||||
Myomers::SharedData
|
// The "Myomers" block that lived here (ctor @4b1d18, classID 0xBC3,
|
||||||
Myomers::DefaultData(
|
// outputVoltage/powered/voltageAvailable) was a MISLABEL of Sensor (real
|
||||||
Myomers::GetClassDerivations(),
|
// reconstruction: sensor.cpp). Its Myomers::DefaultData / ctor / dtor /
|
||||||
Myomers::GetMessageHandlers(),
|
// TestInstance / MyomersSimulation / ResetToInitialState / HandleMessage /
|
||||||
Myomers::GetAttributeIndex(),
|
// CreateStreamedSubsystem definitions ODR-DUPLICATED the REAL Myomers
|
||||||
Myomers::StateCount
|
// (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.
|
||||||
Derivation*
|
|
||||||
Myomers::GetClassDerivations()
|
|
||||||
{
|
|
||||||
static Derivation classDerivations(PoweredSubsystem::GetClassDerivations(), "Myomers");
|
|
||||||
return &classDerivations;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// @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-
|
// Bridge for the vehicleSubSystems gauge factory (btl4gau2.cpp). The engineering-
|
||||||
// screen assignment lives on PoweredSubsystem (auxScreenNumber / auxScreenPlacement
|
// screen assignment lives on PoweredSubsystem (auxScreenNumber / auxScreenPlacement
|
||||||
// / auxScreenLabel, resource +0x104/+0x108/+0x10C). Reading it from btl4gau2.cpp
|
// / auxScreenLabel, resource +0x104/+0x108/+0x10C). Reading it from btl4gau2.cpp
|
||||||
// via a raw offset (sub+0x1dc) is wrong because the reconstructed heat-leaf branch
|
// via a raw offset (sub+0x1dc) is wrong because raw offsets on our compiled
|
||||||
// is not byte-exact -- so the gauge factory calls this bridge, which casts through
|
// 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
|
// the real PoweredSubsystem type and returns the NAMED fields. Returns False for
|
||||||
// non-PoweredSubsystem-derived subsystems (== the FUN_0041a1a4 / 0x50f4bc type
|
// non-PoweredSubsystem-derived subsystems (== the FUN_0041a1a4 / 0x50f4bc type
|
||||||
// filter the vehicleSubSystems Make applies before dispatch).
|
// filter the vehicleSubSystems Make applies before dispatch).
|
||||||
|
|||||||
@@ -296,81 +296,13 @@ class Generator;
|
|||||||
static_assert(offsetof(PoweredSubsystem, thermalResistivityCoefficient)== 0x30C, "PoweredSubsystem::thermalResistivityCoefficient @0x30C");
|
static_assert(offsetof(PoweredSubsystem, thermalResistivityCoefficient)== 0x30C, "PoweredSubsystem::thermalResistivityCoefficient @0x30C");
|
||||||
static_assert(sizeof(PoweredSubsystem) == 0x31C, "sizeof(PoweredSubsystem) 0x31C");
|
static_assert(sizeof(PoweredSubsystem) == 0x31C, "sizeof(PoweredSubsystem) 0x31C");
|
||||||
};
|
};
|
||||||
|
// (task #14: the "Myomers" class that lived here -- vtable 0050fb0c / ctor
|
||||||
//###########################################################################
|
// @4b1d18 / classID 0xBC3, members outputVoltage/powered/voltageAvailable --
|
||||||
//############################# Myomers #################################
|
// 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
|
||||||
// The mech's artificial-muscle bundle: a PoweredSubsystem whose output
|
// /FORCE silently picked the winner by link order -- the real Myomers could
|
||||||
// voltage (and hence the actuator drive available to the locomotion model)
|
// receive this block's SharedData or vice versa. RETIRED whole.)
|
||||||
// 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
|
|
||||||
};
|
|
||||||
|
|
||||||
//###########################################################################
|
//###########################################################################
|
||||||
//############################# Generator ##############################
|
//############################# Generator ##############################
|
||||||
|
|||||||
@@ -52,14 +52,28 @@ Derivation
|
|||||||
Receiver::MessageHandlerSet
|
Receiver::MessageHandlerSet
|
||||||
ThermalSight::MessageHandlers;
|
ThermalSight::MessageHandlers;
|
||||||
|
|
||||||
ThermalSight::AttributeIndexSet
|
// task #16: publish the binary attr table @00511270 -- one entry,
|
||||||
ThermalSight::AttributeIndex;
|
// {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::SharedData
|
||||||
ThermalSight::DefaultData( // @00511228
|
ThermalSight::DefaultData( // @00511228
|
||||||
&ThermalSight::ClassDerivations,
|
&ThermalSight::ClassDerivations,
|
||||||
ThermalSight::MessageHandlers,
|
ThermalSight::MessageHandlers,
|
||||||
ThermalSight::AttributeIndex,
|
ThermalSight::GetAttributeIndex(),
|
||||||
ThermalSight::StateCount
|
ThermalSight::StateCount
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -109,7 +109,7 @@
|
|||||||
private:
|
private:
|
||||||
static const IndexEntry AttributePointers[]; // @00511270
|
static const IndexEntry AttributePointers[]; // @00511270
|
||||||
protected:
|
protected:
|
||||||
static AttributeIndexSet AttributeIndex;
|
static AttributeIndexSet& GetAttributeIndex(); // @00511270 table (task #16)
|
||||||
|
|
||||||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
// Local data.
|
// Local data.
|
||||||
|
|||||||
Reference in New Issue
Block a user