Wreck sink (quadratic burial) + ldbr debris field -- the 1996 script completed

Answers "does the wreck fade away?": YES, by sinking.  FUN_00456410 (the 1996
sink renderable) computes offsetY = rate * t^2; the hulk's authored rate is
-0.025 -> the ~7-unit hulk is fully underground ~17s after the kill.  The
script also pairs the standing hulk with the LDBR strewn-debris field (12x13u
flat scatter), parented together and sinking together.

Also verified from the mesh data: BLHDBR (1537 verts -- more than the intact
torso) IS the authored Blackhawk wreck: the classic standing-leg-in-rubble
sculpt.  The "just a leg standing there" report is the authentic art.
(THRDBR -- the mesh the 1996 script hardcoded -- parses to ZERO vertices;
more evidence the hardcode was an unfinished dev shortcut.)

Implementation: SwapToWreck adds the ldbr piece; TickWreck applies the
quadratic sink per frame (driven from the dead mech's UpdateDeathState),
hides both pieces at burial and reports it so the wreck-smoke re-arm stops
with the wreck.  DPLStaticChildRenderable::SetOffsetTranslation added
(Execute re-reads OrientationMatrix per frame -- same in-place idiom as
SetDrawObj).

Lifecycle verified live: kill -> 'blhdbr.bgf' + ldbr debris -> smoke re-arm
@10s -> wreck buried @~17s -> smoke stops.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-08 15:55:01 -05:00
co-authored by Claude Fable 5
parent c0fa6bf21a
commit 5236d4263a
5 changed files with 166 additions and 9 deletions
+80 -8
View File
@@ -796,9 +796,15 @@ void
}
//
// Hide the body; hang the hulk on the tree root (identity offset -- the
// root renderable already pushes the wreck's localToWorld, so the hulk
// sits at the mech's ground position with its death yaw).
// The strewn-debris field that accompanies the standing hulk (the 1996
// script pairs them: thrdbr + ldbr, parented together, sinking together).
//
d3d_OBJECT *debris = d3d_OBJECT::LoadObject(GetDevice(), "ldbr.bgf");
//
// Hide the body; hang the wreck pieces on the tree root (identity offset --
// the root renderable already pushes the wreck's localToWorld, so they sit
// at the mech's ground position with its death yaw).
//
for (std::map<int, HierarchicalDrawComponent*>::iterator r =
render_tree.segRenderable.begin();
@@ -807,22 +813,88 @@ void
if (r->second != NULL)
r->second->SetDrawObj(NULL);
}
if (hulk != NULL && render_tree.rootRenderable != NULL)
if (render_tree.rootRenderable != NULL)
{
dpl_ISECT_MODE isect_mode;
LinearMatrix identity(True);
new DPLStaticChildRenderable(
victim, false /* main zone */, hulk,
isect_mode, INTERSECT_ALL, identity, render_tree.rootRenderable);
if (hulk != NULL)
render_tree.wreckHulk = new DPLStaticChildRenderable(
victim, false /* main zone */, hulk,
isect_mode, INTERSECT_ALL, identity, render_tree.rootRenderable);
if (debris != NULL)
render_tree.wreckDebris = new DPLStaticChildRenderable(
victim, false /* main zone */, debris,
isect_mode, INTERSECT_ALL, identity, render_tree.rootRenderable);
}
render_tree.wrecked = 1;
render_tree.wrecked = 1;
render_tree.wreckAge = 0.0f;
DEBUG_STREAM << "[BTrender] wreck swap: victim -> '"
<< (hulk_name[0] ? hulk_name : "gendbr.bgf")
<< (hulk ? "'" : "' (LOAD FAILED -- body hidden only)")
<< (debris ? " + ldbr debris" : "")
<< "\n" << std::flush;
}
//
//#############################################################################
// TickWreck -- the wreck's quadratic SINK (the 1996 burial)
//#############################################################################
//
// FUN_00456410 (the 1996 sink renderable): offsetY = rate * t^2, hulk rate
// -0.025 (armed 0.25s after the boom by a sweep trigger). The ~7-unit hulk is
// fully underground ~17s after the kill -- the wreck visual "fades away" by
// burial; the ENTITY (sim/collision) stays, per the wreck-stays rule. Once
// buried, the pieces are hidden and the sink stops.
//
int
BTL4VideoRenderer::TickWreck(Entity *victim, float dt)
{
std::map<Entity*, MechRenderTree>::iterator tree_it =
mMechRenderTrees.find(victim);
if (tree_it == mMechRenderTrees.end())
return 1; // no tree yet -- not buried
MechRenderTree &render_tree = tree_it->second;
if (!render_tree.wrecked)
return 1; // not swapped yet
if (render_tree.wreckHulk == NULL && render_tree.wreckDebris == NULL)
return 0; // already buried
render_tree.wreckAge += dt;
float sink = -0.025f * render_tree.wreckAge * render_tree.wreckAge; // the authored rate
if (sink < -8.0f)
{
// fully buried -> hide + stop ticking
if (render_tree.wreckHulk) render_tree.wreckHulk->SetDrawObj(NULL);
if (render_tree.wreckDebris) render_tree.wreckDebris->SetDrawObj(NULL);
render_tree.wreckHulk = NULL;
render_tree.wreckDebris = NULL;
DEBUG_STREAM << "[BTrender] wreck buried (sink complete)\n" << std::flush;
return 0;
}
if (render_tree.wreckHulk)
render_tree.wreckHulk->SetOffsetTranslation(0.0f, sink, 0.0f);
if (render_tree.wreckDebris)
render_tree.wreckDebris->SetOffsetTranslation(0.0f, sink, 0.0f);
return 1;
}
//
// Sim-side bridge (UpdateDeathState drives the sink each dead frame).
//
int BTWreckSinkTick(Entity *victim, float dt)
{
if (victim == NULL || application == NULL)
return 1;
BTL4VideoRenderer *renderer =
(BTL4VideoRenderer *)application->GetVideoRenderer();
if (renderer == NULL)
return 1;
return renderer->TickWreck(victim, dt);
}
//
// Engine-side bridge (the ExplosionClassID dispatch calls this on effect 104).
//