Destructible props LIVE: revive StateInstanceSwitchRenderable (Gitea issue #3)

Playtest report: trucks play a collision sound on impact but never change
state -- no explosion swap, no destroyed model, collision volume persists.

Investigation (BT_CULT_LOG census + BT_GOTO ram harness): the SIM was never
broken.  ARENA1 map-streams 124 CulturalIcon entities, each with a damage
zone, explosion resource and removeOnDeath; the mech crunch dispatch
(ProcessCollision -> BTDispatchCollisionDamage) reaches them; ~19 walking
bumps burn the zone -> CulturalIcon::TakeDamageMessageHandler spawns the
Explosion (psfx effect 1008 observed), posts the delayed BurningState and
deletes the collision boxes.  All of that ran correctly and invisibly.

The ACTUAL bug: the 2007 WinTesla port fully stubbed
StateInstanceSwitchRenderable ('STUBBED: DPL RB 1/14/07') -- ctor never
registered on the state dial, Execute never toggled anything -- so the
intact->rubble visual swap on BurningState NEVER fired, for every cultural
icon in the game.  The destroyed truck kept its intact model: exactly the
reported 'no state change'.

Revival (D3D9-native, 1995 semantics verbatim):
- StateInstanceSwitchRenderable now controls the draw COMPONENT via the
  SetDrawObj in-place drawable swap (the mech RemakeEntity mechanism):
  visible = captured d3d_OBJECT, hidden = NULL.  Initial state in the ctor,
  AddVideoWatcher on the SimulationState dial (SetState fires video
  watchers), toggle only on a real change.
- L4VIDEO cultural case passes the draw component + object (the dead
  dpl_INSTANCE param is gone); [video] Object hides at BurningState,
  Rubble shows.
- REQUIRED: cultural objects stay OUT of static-mesh consolidation
  (RecurseStaticObject, same exclusion class as banded LODs/shadows) --
  a merged static draws forever regardless of DrawObj.
- Permanent env diag BT_CULT_LOG: creation census (zones/explosion/flags/
  pos), TakeDamage trace, death transition, [cultvis] switch actions.

Verified live (BT_GOTO=-620,-328 ram, ARENA1): 19 crunches -> DYING ->
explosion 1008 at the icon -> [cultvis] HIDE x2 (intact) + SHOW (rubble)
-> contacts cease, mech walks into the former footprint.  Boot + render
un-regressed (statics consolidate as before minus the 124 icons).

KB: context/rendering.md new section (the full chain + the consolidation
gotcha).  MP replicant path shares the same SetState watcher (cross-pod
verification pending -- noted in the issue).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
arcattack
2026-07-19 09:23:02 -05:00
co-authored by Claude Fable 5
parent 0bcba26213
commit 53ac927d4b
5 changed files with 177 additions and 89 deletions
+34
View File
@@ -73,6 +73,16 @@ void
{
Check(damage_message);
// BT_CULT_LOG (issue #3): does damage ever reach a cultural icon?
if (getenv("BT_CULT_LOG"))
{
DEBUG_STREAM << "[cult] TakeDamage entity=" << (long)GetEntityID()
<< " type=" << (int)damage_message->damageData.damageType
<< " amount=" << (float)damage_message->damageData.damageAmount
<< " zone=" << damage_message->damageZone
<< " state=" << GetSimulationState()
<< "\n" << std::flush;
}
if (damage_message->damageZone == -1 )
{
@@ -144,6 +154,15 @@ void
Explosion::Make(&exp_message);
Register_Object(new_exp);
// BT_CULT_LOG (issue #3): the death transition fired
if (getenv("BT_CULT_LOG"))
{
DEBUG_STREAM << "[cult] DYING entity=" << (long)GetEntityID()
<< " explosion res=" << explosion_ID
<< " delay=" << (float)timeDelay
<< " -> WaitingToBurn\n" << std::flush;
}
Message dummy_message(SetBurningStateMessageID, sizeof(Message));
Check(application);
Time now = Now();
@@ -335,6 +354,21 @@ CulturalIcon::CulturalIcon(
}
SetValidFlag();
dmg_res->Unlock();
// BT_CULT_LOG: per-icon creation census (issue #3 -- truck destruction)
if (getenv("BT_CULT_LOG"))
{
DEBUG_STREAM << "[cult] made icon entity=" << (long)GetEntityID()
<< " res=" << resourceID
<< " zones=" << damageZoneCount
<< " explode=" << explosionResourceID
<< " crunch=" << crunchExplosionResourceID
<< " removeOnDeath=" << (int)RemoveCollisionVolumeOnDeath()
<< " stopping=" << (int)IsStoppingCollisionVolume()
<< " pos=(" << localOrigin.linearPosition.x
<< "," << localOrigin.linearPosition.y
<< "," << localOrigin.linearPosition.z << ")"
<< "\n" << std::flush;
}
Check_Fpu();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~