Per-pilot mech paint: wire the color/badge/patch substitution end-to-end

- Mech::resourceNameA/B/C -> real CString members (the binary's 16-byte
  CStringRepresentation; deep-copy bind from the MakeMessage = FUN_00402a98,
  implicit member dtors) + paint-name accessors
- SetupMaterialSubstitutionList reads the real egg names ([paint] log);
  TearDown clears the callback first (FUN_004d11e8)
- dpl_SetMaterialNameCallback is real now (L4VIDEO registry); bgfload
  MaterialResolver::resolve() applies it to every material name -- the
  port analogue of the dpl board rewriting names at load
- MP_BHMC.EGG: color=Red -> Crimson (vehicletable has no Red; binary Fail()ed)

Verified live 2-node MP: crimson MadCat with hip hazard stripes + yellow VGL
leg emblems; white Blackhawk + emblems; replicants painted on both nodes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-17 09:50:23 -05:00
co-authored by Claude Fable 5
parent dda517b65c
commit e0474ff92a
10 changed files with 382 additions and 53 deletions
+26 -16
View File
@@ -2128,23 +2128,25 @@ void
//
// Look up this mech's colour / badge / patch codes from the table, using
// the egg-supplied names carried on the entity (badge=resourceNameA @0x844,
// color=resourceNameB @0x848, patch=resourceNameC @0x84c).
// color=resourceNameB @0x848, patch=resourceNameC @0x84c). The binary
// reads the backing text straight off the ref-counted name objects
// (*(char**)(*(int*)(mech+0x848)+8) etc.); the reconstructed Mech now
// carries them as real CString members deep-copied from the MakeMessage,
// exposed via the paint-name accessors.
//
// The recovered code read these directly off the BattleTech mech egg
// (Mech::vehicleColor / vehicleBadge / vehiclePatch) and Fail()ed on a miss.
// TODO(bring-up): the reconstructed Mech carries those names as ref-counted
// creation-name objects (resourceNameA/B/C) whose backing string is the
// transient MakeMessage buffer -- not safely readable here yet, and the
// attribute-index path ("VehicleColor"/...) is not wired, so it returns
// garbage that FindNote then deref-crashed on (btl4vid.cpp:808). Follow the
// RP analogue (RPL4VID.cpp:1562) which simply tolerates a missing colour/
// badge: a NULL egg name or a table miss leaves veh_* == NULL and the
// placeholder substitution below drops to default materials. Re-wire the
// real egg names (a named Mech accessor) once the Mech layout is mapped.
// DEVIATION (documented): on a table miss the binary Fail()ed hard
// ("Exiting" @BTL4VID.CPP:0xbeb/0xbf2/0xbf9). We follow the RP analogue
// (RPL4VID.cpp:1562) and tolerate it -- a NULL/missing name leaves veh_* ==
// NULL and that placeholder expands empty (base materials) -- so a sparse
// test egg degrades to the old gray-metal look instead of killing the pod.
//
const char *egg_color = NULL; // [0x848] resourceNameB (color)
const char *egg_badge = NULL; // [0x844] resourceNameA (badge)
const char *egg_patch = NULL; // [0x84c] resourceNameC (patch)
Mech *mech = (Mech *)entity; // caller dispatched on MechClassID
const char *egg_color = mech->GetVehicleColorName(); // [0x848] resourceNameB
const char *egg_badge = mech->GetVehicleBadgeName(); // [0x844] resourceNameA
const char *egg_patch = mech->GetVehiclePatchName(); // [0x84c] resourceNameC
if (egg_color && !*egg_color) egg_color = NULL;
if (egg_badge && !*egg_badge) egg_badge = NULL;
if (egg_patch && !*egg_patch) egg_patch = NULL;
const char *veh_color = NULL, *veh_badge = NULL, *veh_patch = NULL;
if (egg_color && !veh_tbl->GetEntry("color", egg_color, &veh_color)) // @0051d94e
@@ -2166,6 +2168,14 @@ void
veh_patch = NULL;
}
DEBUG_STREAM << "[paint] mech egg color='" << (egg_color ? egg_color : "<none>")
<< "' badge='" << (egg_badge ? egg_badge : "<none>")
<< "' patch='" << (egg_patch ? egg_patch : "<none>")
<< "' -> codes color=" << (veh_color ? veh_color : "-")
<< " badge=" << (veh_badge ? veh_badge : "-")
<< " patch=" << (veh_patch ? veh_patch : "-")
<< " serno=" << gSerno << "\n" << std::flush;
//
// Generic substitution list, then expand placeholders per entry.
//
@@ -2257,6 +2267,7 @@ void
void
BTL4VideoRenderer::TearDownMaterialSubstitutionList()
{
dpl_SetMaterialNameCallback(NULL); // FUN_0049664c(0) -- first, as the binary does
if (materialSubstitutionList != NULL)
{
for (NameList::Entry *entry = materialSubstitutionList->GetFirstEntry();
@@ -2269,7 +2280,6 @@ void
delete materialSubstitutionList;
materialSubstitutionList = NULL;
}
// dpl_SetMaterialNameCallback(NULL);
}
//===========================================================================//