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
@@ -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;
|
||||
}
|
||||
|
||||
//
|
||||
|
||||
Reference in New Issue
Block a user