diff --git a/game/reconstructed/btl4vid.cpp b/game/reconstructed/btl4vid.cpp index 4ff5262..471b6d0 100644 --- a/game/reconstructed/btl4vid.cpp +++ b/game/reconstructed/btl4vid.cpp @@ -197,24 +197,6 @@ void dplMainZone, dplDeathZone, sim_state, fogRed, fogGreen, fogBlue, fogNear, fogFar, 3 /* MissionStartingState */, 4 /* MissionEndingState */); - - // - // BRING-UP (task #52): ALSO give the LOCAL player the translocation - // SPHERE (the authentic wiring shows it only on OTHERS -- replicant - // branch above -- because peer player-attribute replication of - // SimulationState/DropZoneLocation isn't wired yet; both read - // uninitialised on a replicant). Driven by the local player's OWN - // SimulationState dial (pulsed to DropZoneAcquired->Translocated at - // respawn, btplayer.cpp) at its own DropZoneLocation -- so you see - // the warp collapse+expand around your reinsertion. Same control - // state (1 = DropZoneAcquired). Remove once player replication lands. - // - Point3D *local_drop = - (Point3D *)entity->GetAttributePointer("DropZoneLocation"); - if (local_drop) - new BTTranslocationRenderable( - entity, VideoRenderable::Watcher, GetMainView(), - sim_state, local_drop, 1); } break; } @@ -2106,33 +2088,48 @@ Logical } //===========================================================================// -// BTTranslocationRenderable -- the "blue warp" translocation sphere (task #52) +// The "blue warp" translocation sphere (task #52) // // Reconstructed from the engine's POVTranslocateRenderable (L4VIDRND.cpp:1749): -// a sphere (tsphere.bgf) that COLLAPSES onto the mech when the watched player -// enters the control state (DropZoneAcquired), holds while dead, then EXPANDS -// to reveal the reborn mech when the state leaves it (VehicleTranslocated). -// Rotates throughout. Loaded by FILENAME (not via the RES table) -- which is -// why every resource-name search missed it. Drawn direct from the render loop -// (BTDrawTranslocationSpheres, called beside BTDrawBeams), same accommodation -// the weapon beams + reticle use since our VideoComponent tree is partial. +// a sphere (tsphere.bgf) that COLLAPSES onto the respawn point then EXPANDS to +// reveal the reborn mech, rotating throughout. Loaded by FILENAME (not via the +// RES table) -- which is why every resource-name search missed it. Drawn direct +// from the render loop (BTDrawTranslocationSpheres, beside BTDrawBeams). +// +// SELF-CONTAINED ONE-SHOT: the engine keys this off the player's SimulationState +// dial, but in our reconstruction that dial ALSO drives the camera/POV + +// targeting, so pulsing it for the sphere regressed all of those (inside-view, +// no-fire). Instead the respawn path (btplayer.cpp) calls BTStartWarpEffect at +// the drop-zone origin and the effect plays its own collapse->expand -- touching +// nothing but the render. (The BTTranslocationRenderable objects the entity +// tree still builds for the replicant/POV wiring are now inert.) //===========================================================================// namespace { - // Timings + scales are the engine's #defines (L4VIDRND.cpp:1765-1772). - const float TLOC_COLLAPSE_TIME = 1.3f; - const float TLOC_COLLAPSE_START_SCALE = 100.0f; - const float TLOC_EXPAND_TIME = 1.0f; - const float TLOC_EXPAND_END_SCALE = 150.0f; - const float TLOC_ROTATE_RATE = 0.5f * 0.01745329222222f; // rad/frame + // Timings are the engine's #defines (L4VIDRND.cpp:1765-1772). The authentic + // scales are 100 (collapse start) / 150 (expand end); capped smaller here so + // the sphere -- which is centred on YOUR own reinsertion point -- does not + // envelop the camera and black out the view. Tunable via BT_WARP_SCALE. + const float TLOC_COLLAPSE_TIME = 1.3f; + const float TLOC_EXPAND_TIME = 1.0f; + const float TLOC_ROTATE_RATE = 0.5f * 0.01745329222222f; // rad/frame + float gWarpCollapseScale = 30.0f; // authentic 100 + float gWarpExpandScale = 40.0f; // authentic 150 - BTTranslocationRenderable *gTLocFx[64]; - int gTLocFxCount = 0; - d3d_OBJECT *gTLocSphere = 0; - int gTLocSphereTried = 0; + d3d_OBJECT *gTLocSphere = 0; + int gTLocSphereTried = 0; + + // The single active warp one-shot (one local player per node). + int gWarpPhase = 0; // 0 idle, 1 collapse, 2 expand + float gWarpT = 0.0f; + float gWarpX = 0.0f, gWarpY = 0.0f, gWarpZ = 0.0f; + float gWarpRot = 0.0f; } +// The renderable objects the entity tree builds for the translocation wiring are +// inert now (the warp is the self-contained one-shot below); the ctor/dtor just +// satisfy MakeEntityRenderables. BTTranslocationRenderable::BTTranslocationRenderable( - Entity *entity, int /*execution_type*/, dpl_VIEW * /*this_view*/, + Entity *entity, int, dpl_VIEW *, StateIndicator *effect_trigger, Point3D *drop_zone, int effect_control_state) : BTRenderableBase(entity), myWatchedEntity(entity), @@ -2144,42 +2141,43 @@ BTTranslocationRenderable::BTTranslocationRenderable( myRotateY(0.0f), mySphereVisible(false) { - // A player's render tree is rebuilt on respawn, so drop any prior (now - // stale) sphere for the SAME entity from the active walk -- only the newest - // steps + draws (the old renderable objects are owned/freed by their tree). - for (int i = 0; i < gTLocFxCount; ++i) - if (gTLocFx[i] != 0 && gTLocFx[i]->myWatchedEntity == entity) - { - gTLocFx[i] = gTLocFx[--gTLocFxCount]; - --i; - } - if (gTLocFxCount < 64) - gTLocFx[gTLocFxCount++] = this; - if (getenv("BT_TLOC_LOG")) - DEBUG_STREAM << "[tloc] renderable created for entity " << entity->GetEntityID() - << " control=" << myControlState << " (active=" << gTLocFxCount << ")\n" << std::flush; } BTTranslocationRenderable::~BTTranslocationRenderable() { - for (int i = 0; i < gTLocFxCount; ++i) - if (gTLocFx[i] == this) - { - gTLocFx[i] = gTLocFx[--gTLocFxCount]; - break; - } } // -// Step every active translocation sphere's state machine and draw the visible -// ones. Called from the render loop (L4VIDEO.cpp) with the frame's view matrix -// + dt, so the sphere occludes against the depth already laid down by the world. +// Kick the warp at a world position (called from the respawn path). Does NOT +// touch player state -- pure render. +// +void + BTStartWarpEffect(float x, float y, float z) +{ + if (const char *s = getenv("BT_WARP_SCALE")) + { + float v = (float)atof(s); + if (v > 0.0f) { gWarpCollapseScale = v; gWarpExpandScale = v * 1.33f; } + } + gWarpPhase = 1; // collapse + gWarpT = 0.0f; + gWarpX = x; gWarpY = y; gWarpZ = z; + gWarpRot = 0.0f; + if (getenv("BT_TLOC_LOG")) + DEBUG_STREAM << "[tloc] warp start at (" << x << "," << y << "," << z + << ") scale=" << gWarpCollapseScale << "/" << gWarpExpandScale << "\n" << std::flush; +} + +// +// Play the warp one-shot: tsphere.bgf COLLAPSES onto the respawn point (scale +// -> 1 over 1.3s) then EXPANDS to reveal the reborn mech (1 -> max over 1.0s), +// rotating. Alpha pass (beside the beams) so it blends + Z-tests vs the world. // void BTDrawTranslocationSpheres(LPDIRECT3DDEVICE9 device, const D3DXMATRIX *view, float dt, Time frame_time) { - if (gTLocFxCount == 0) + if (gWarpPhase == 0) return; if (gTLocSphere == 0 && !gTLocSphereTried) @@ -2191,100 +2189,47 @@ void << (gTLocSphere ? "OK" : "FAILED") << "\n" << std::flush; } if (gTLocSphere == 0) - return; - - const int log_on = getenv("BT_TLOC_LOG") ? 1 : 0; - static int s_logTick = 0; - const int log_now = log_on && ((++s_logTick % 30) == 1); - - for (int i = 0; i < gTLocFxCount; ++i) { - BTTranslocationRenderable *fx = gTLocFx[i]; - if (fx == 0 || fx->myTrigger == 0 || fx->myDropZone == 0) - continue; - - const unsigned st = fx->myTrigger->GetState(); - float scale = 1.0f; - - switch (fx->mySphereState) - { - case BTTranslocationRenderable::TLoc_Idle: - if (st == fx->myControlState) - { - fx->mySphereState = BTTranslocationRenderable::TLoc_Collapse; - fx->myTimer = 0.0f; - fx->mySphereVisible = true; - } - break; - - case BTTranslocationRenderable::TLoc_Collapse: - { - fx->myTimer += dt; - float left = 1.0f - (fx->myTimer / TLOC_COLLAPSE_TIME); // 1 -> 0 - if (left <= 0.0f) - { - scale = 1.0f; - fx->mySphereState = BTTranslocationRenderable::TLoc_Wait; - fx->myTimer = 0.0f; - } - else - scale = left * TLOC_COLLAPSE_START_SCALE + 1.0f; // 101 -> 1 - break; - } - - case BTTranslocationRenderable::TLoc_Wait: - scale = 1.0f; - if (st != fx->myControlState) // respawned/translocated - { - fx->mySphereState = BTTranslocationRenderable::TLoc_Expand; - fx->myTimer = 0.0f; - } - break; - - case BTTranslocationRenderable::TLoc_Expand: - { - fx->myTimer += dt; - float used = fx->myTimer / TLOC_EXPAND_TIME; // 0 -> 1 - if (used >= 1.0f) - { - scale = 1.0f; - fx->mySphereState = BTTranslocationRenderable::TLoc_Idle; - fx->mySphereVisible = false; - } - else - scale = used * TLOC_EXPAND_END_SCALE + 1.0f; // 1 -> 151 - break; - } - } - - if (log_now) - DEBUG_STREAM << "[tloc] fx=" << i << " trigger=" << st - << " ctrl=" << fx->myControlState << " state=" << fx->mySphereState - << " scale=" << scale << " vis=" << (int)fx->mySphereVisible - << " at=(" << fx->myDropZone->x << "," << fx->myDropZone->y - << "," << fx->myDropZone->z << ")\n" << std::flush; - - if (!fx->mySphereVisible) - continue; - - fx->myRotateY += TLOC_ROTATE_RATE; - - // world = scale * rotateY(myRotateY), translated to the drop zone. - const float s = scale; - const float c = cosf(fx->myRotateY); - const float sn = sinf(fx->myRotateY); - D3DXMATRIX m; - m._11 = s * c; m._12 = 0.0f; m._13 = -s * sn; m._14 = 0.0f; - m._21 = 0.0f; m._22 = s; m._23 = 0.0f; m._24 = 0.0f; - m._31 = s * sn; m._32 = 0.0f; m._33 = s * c; m._34 = 0.0f; - m._41 = (float)fx->myDropZone->x; - m._42 = (float)fx->myDropZone->y; - m._43 = (float)fx->myDropZone->z; - m._44 = 1.0f; - - gTLocSphere->SetLocalToWorld(m); - gTLocSphere->Draw(PASS_ALPHABLEND, view, frame_time); + gWarpPhase = 0; + return; } + + gWarpT += dt; + gWarpRot += TLOC_ROTATE_RATE; + + float scale; + if (gWarpPhase == 1) // collapse: (max+1) -> 1 + { + float left = 1.0f - (gWarpT / TLOC_COLLAPSE_TIME); + if (left <= 0.0f) { gWarpPhase = 2; gWarpT = 0.0f; scale = 1.0f; } + else scale = left * gWarpCollapseScale + 1.0f; + } + else // expand: 1 -> (max+1) + { + float used = gWarpT / TLOC_EXPAND_TIME; + if (used >= 1.0f) { gWarpPhase = 0; return; } // done + scale = used * gWarpExpandScale + 1.0f; + } + + if (getenv("BT_TLOC_LOG")) + { + static int s_lt = 0; + if ((++s_lt % 15) == 1) + DEBUG_STREAM << "[tloc] warp phase=" << gWarpPhase << " scale=" << scale + << " at=(" << gWarpX << "," << gWarpY << "," << gWarpZ << ")\n" << std::flush; + } + + const float s = scale; + const float c = cosf(gWarpRot); + const float sn = sinf(gWarpRot); + D3DXMATRIX m; + m._11 = s * c; m._12 = 0.0f; m._13 = -s * sn; m._14 = 0.0f; + m._21 = 0.0f; m._22 = s; m._23 = 0.0f; m._24 = 0.0f; + m._31 = s * sn; m._32 = 0.0f; m._33 = s * c; m._34 = 0.0f; + m._41 = gWarpX; m._42 = gWarpY; m._43 = gWarpZ; m._44 = 1.0f; + + gTLocSphere->SetLocalToWorld(m); + gTLocSphere->Draw(PASS_ALPHABLEND, view, frame_time); } //===========================================================================// diff --git a/game/reconstructed/btplayer.cpp b/game/reconstructed/btplayer.cpp index 148e3b6..ab0909a 100644 --- a/game/reconstructed/btplayer.cpp +++ b/game/reconstructed/btplayer.cpp @@ -204,11 +204,6 @@ static_assert(sizeof(BTPlayer::MakeMessage) == 0xD0, static const char *SelfDestructName = "self destruct"; // &DAT_00524b38 static const Scalar RespawnDelay = 5.0f; // death -> drop-zone hunt (@004c0830) - -// WARP (task #52): countdown from the respawn's DropZoneAcquired pulse to the -// VehicleTranslocated flip that makes the translocation sphere EXPAND (reveal). -// One local player per node, so a file-static holds it. <0 = idle. -static Scalar sWarpExpandTimer = -1.0f; static const Scalar TicksPerSecond = 1.0f; // (see note in PlayerSimulation) // @@ -759,19 +754,6 @@ void Player::PlayerSimulation(time_slice); // FUN_0042e168 - // WARP (task #52): once the collapse has had time to play, flip the - // SimulationState dial off DropZoneAcquired so the translocation sphere - // leaves its "wait" phase and EXPANDS (reveals the reborn mech). - if (sWarpExpandTimer > 0.0f) - { - sWarpExpandTimer -= time_slice; - if (sWarpExpandTimer <= 0.0f) - { - sWarpExpandTimer = -1.0f; - SetSimulationState(VehicleTranslocatedState); // state 2 -> expand - } - } - if ( (lastPerformance - lastConsoleUpdate) / TicksPerSecond >= CONSOLE_UPDATE_INTERVAL // _DAT_004c08fc || application->GetApplicationState() == Application::EndingMission // app+0x88 == 6 @@ -1068,22 +1050,16 @@ void // // WARP (task #52): fire the translocation sphere at our reinsertion. - // Publish the drop-zone position into our DropZoneLocation attribute (the - // sphere renderable draws there) and pulse the SimulationState dial to - // DropZoneAcquired -- state 1, the sphere's COLLAPSE trigger. - // PlayerSimulation flips it to VehicleTranslocated ~1.4 s later, which the - // sphere reads as "reincarnate" and EXPANDS to reveal the reborn mech. - // (Authentic trigger = the player's own SimulationState + DropZoneLocation - // attribute; the flip-timer stands in for the engine's +1 s drop-zone - // re-post choreography, which our sever-and-recreate respawn skips.) + // Self-contained render one-shot at the drop-zone position -- it does NOT + // touch the player's SimulationState (that dial drives the camera/POV + + // targeting in our reconstruction; pulsing it for the sphere trigger + // regressed all of those). The effect plays its own collapse->expand. // { - Point3D *drop_attr = (Point3D *)GetAttributePointer("DropZoneLocation"); - if (drop_attr != 0) - *drop_attr = message->dropZoneLocation.linearPosition; + const Point3D &p = message->dropZoneLocation.linearPosition; + extern void BTStartWarpEffect(float x, float y, float z); + BTStartWarpEffect((float)p.x, (float)p.y, (float)p.z); } - SetSimulationState(DropZoneAcquiredState); // state 1 -> collapse - sWarpExpandTimer = 1.4f; } else if (deathCount == message->deathCount) // param_2[0xe] == param_1[0x80] {