Warp: exact POVTranslocateRenderable replica (collapse/throb/reveal + world mask)
Stop approximating -- replicate POVTranslocateRenderable::Execute() behaviorally,
byte-for-byte on the constants and transitions (engine T0, L4VIDRND.cpp:1763-2076),
per the user's "replicate the effect exactly". The five prior piecemeal attempts
each fixed one facet and broke another because they left out the WHOLE mechanism.
Full state machine (btl4vid.cpp), driven by two decoupled events (NOT the
SimulationState trigger -- that dial also drives camera/POV+targeting, the f053535
regression; SetIsDead is a separate pure-render flag, verified safe):
- LOCAL DEATH -> InitialCollapse: scale (pct_left*100)+1 over 1.3s (world visible),
then SetIsDead(true) + WaitForReincarnate. (btplayer VehicleDeadMessageHandler,
deathCount==-1, local-guarded.)
- WaitForReincarnate: world BLACK (mask), scale 1, Lissajous throb
(cos(t*3.33), sin(t*2.5)) x2.0 -- the "dances around playfully".
- LOCAL RESPAWN -> ExpandReveal: SetIsDead(false) + scale (pct_used*150)+1 over
1.0s -- blasts open, world revealed ("shoots off into the distance"). (btplayer
DropZoneReplyMessageHandler, local-guarded.)
Draw EXACTLY as the engine (isDeathDraw -> drawAsSky): PASS_SKY, OPAQUE,
CULLMODE=CW, z-test ON (L4VIDEO.cpp:7526/7568-7570/7693). This kills the "glitchy
funnel" -- opaque = one write/pixel (no translucent double-blend), CW = one winding
of the double-sided mesh (no coincident z-fight), z-on = the expand shell is
occluded/revealed by the returning world. Placement = Scale * inverse(view) with
NO recenter (the mesh is authored off-origin so the eye sits inside, authentic);
dropped the wrong mCullCenter recenter and the Z-off overlay hack. Colour is the
"sky" ramp (per prior commit); no geometry spin (myRotateY is dead) -- swirl is the
texture scroll. SetIsDead reached via a BTSetWorldDead bridge (L4VIDRND.cpp).
STUCK-BLACK SAFETY (the one new risk of decoupling mask-on from mask-off): a 12s
WaitForReincarnate render-loop timeout un-masks if no respawn arrives, plus
BTWarpForceUnmask on the mission-ending + no-DropZones abort paths. Smoke-verified
2-node: collapse->wait(masked=1)->expand(masked=0), ends masked=0, no crash.
The peer-observer warp stays a world-anchored translucent alpha draw (port
extension; the authentic effect is POV-only).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
062c66999e
commit
a35f321ba1
@@ -314,6 +314,10 @@ void
|
||||
//
|
||||
if (GetSimulationState() == MissionEndingState)
|
||||
{
|
||||
// If our death raised the warp world-mask but the mission is tearing down
|
||||
// before we respawn, drop it now so the world can never stay black.
|
||||
if (this == (BTPlayer *)application->GetMissionPlayer())
|
||||
{ extern void BTWarpForceUnmask(); BTWarpForceUnmask(); }
|
||||
Check_Fpu();
|
||||
return;
|
||||
}
|
||||
@@ -357,6 +361,10 @@ void
|
||||
if (entity_manager == 0
|
||||
|| entity_manager->FindGroup("DropZones") == 0)
|
||||
{
|
||||
// No respawn possible -> the expand will never fire -> drop the warp
|
||||
// world-mask now so we don't stay black waiting for a reincarnation.
|
||||
if (this == (BTPlayer *)application->GetMissionPlayer())
|
||||
{ extern void BTWarpForceUnmask(); BTWarpForceUnmask(); }
|
||||
if (getenv("BT_SCORE_LOG"))
|
||||
DEBUG_STREAM << "[score] VehicleDead: no DropZones group -- "
|
||||
"respawn unavailable in this mission\n" << std::flush;
|
||||
@@ -386,6 +394,16 @@ void
|
||||
}
|
||||
deathPending = 1; // this+0x290
|
||||
|
||||
// WARP (task #52): our OWN death = the engine's Idle -> InitialCollapse (trigger
|
||||
// becomes == control state, POVTranslocateRenderable). Collapse the eye-centred
|
||||
// sphere 100x -> 1x, then it raises the SetIsDead world mask and throbs until the
|
||||
// respawn (DropZoneReply) releases the expand-reveal. LOCAL player only (POV).
|
||||
if (this == (BTPlayer *)application->GetMissionPlayer())
|
||||
{
|
||||
extern void BTStartWarpCollapsePOV();
|
||||
BTStartWarpCollapsePOV();
|
||||
}
|
||||
|
||||
if (getenv("BT_SCORE_LOG"))
|
||||
DEBUG_STREAM << "[score] VehicleDead(-1): death #" << (deathCount + 1)
|
||||
<< ", respawn hunt in 5s\n" << std::flush;
|
||||
@@ -1142,14 +1160,17 @@ void
|
||||
Mech *mech = (Mech *)playerVehicle;
|
||||
mech->Reset(message->dropZoneLocation, 1 /* True */); // FUN_0049fb74 (heal+move)
|
||||
|
||||
// WARP (task #52): the translocation sphere at our reinsertion. This is OUR
|
||||
// OWN respawn, so use the POV variant -- the sphere centres on and orients to
|
||||
// our eye (the authentic VTV-parented POVTranslocateRenderable), collapsing
|
||||
// through us and blasting back open. A self-contained render one-shot -- it
|
||||
// does NOT touch SimulationState (that dial drives the camera/POV + targeting;
|
||||
// pulsing it regressed all of those).
|
||||
extern void BTStartWarpEffectPOV();
|
||||
BTStartWarpEffectPOV();
|
||||
// WARP (task #52): our OWN respawn = the engine's WaitForReincarnate -> Expand
|
||||
// release (POVTranslocateRenderable): drop the SetIsDead world mask and blast
|
||||
// the eye-centred sphere open to reveal the reborn world. LOCAL player only (a
|
||||
// POV effect); a self-contained render one-shot that does NOT touch
|
||||
// SimulationState (that dial drives the camera/POV + targeting -- pulsing it
|
||||
// regressed all of those; SetIsDead is a separate pure-render flag).
|
||||
if (this == (BTPlayer *)application->GetMissionPlayer())
|
||||
{
|
||||
extern void BTStartWarpExpandPOV();
|
||||
BTStartWarpExpandPOV();
|
||||
}
|
||||
}
|
||||
Check_Fpu();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user