diff --git a/dGame/dComponents/MovingPlatformComponent.cpp b/dGame/dComponents/MovingPlatformComponent.cpp index 9688d5e63..82d1b52a5 100644 --- a/dGame/dComponents/MovingPlatformComponent.cpp +++ b/dGame/dComponents/MovingPlatformComponent.cpp @@ -157,6 +157,18 @@ void MovingPlatformComponent::LoadConfigData() { if (m_Parent->HasVar(u"platformMoveTime")) simpleMover->mMoveTime = m_Parent->GetVar(u"platformMoveTime"); } simpleMover->mCurrentWaypointIndex = simpleMover->mStartAtEnd ? 1 : 0; + + // As a mover on its path (LWOPlatform::ResumeTravelling 0x00c86b90 and SetWaitingTime 0x00bdc860): unless + // startPathingOnLoad is false it waits at its waypoint (9), for the shortest wait as its waypoints have none. + // Live's scripts for the NJ spinners, which are simple movers that only move when used, stop them when they + // start up (L_NJ_SPINNER_SERVER: StopPathing), and the simple movers that wait for an event without such a script + // have startPathingOnLoad 0. + const bool startOnLoad = !m_Parent->HasVar(u"startPathingOnLoad") || m_Parent->GetVar(u"startPathingOnLoad"); + if (startOnLoad) { + simpleMover->mState = ePlatformStateFlag::Waiting | ePlatformStateFlag::ReachedDesiredWaypoint; + simpleMover->mTimeToWait = 0.1f; + simpleMover->mWaitSet = true; + } } const auto pathName = GeneralUtils::UTF16ToWTF8(m_PathName); @@ -313,17 +325,8 @@ void MovingPlatformComponent::OnCompleteQuickBuild() { // about 0.1 s later travelling (2) to its other end, where it stops without a serialization and // PlatformAtLastWaypoint tells the scripts. It is the client's LWOPlatform at work: active once its quick build // is complete (SendMessage 0x00bdc930), it waits the shortest wait (SetWaitingTime 0x00bdc860) and sets off - // (RunPlatform 0x00c86e10). - auto* const simpleMover = GetSimpleMoverSubComponent(); - simpleMover->mDirtyState = true; - const bool startOnLoad = !m_Parent->HasVar(u"startPathingOnLoad") || m_Parent->GetVar(u"startPathingOnLoad"); - if (!startOnLoad || (simpleMover->mState & ePlatformStateFlag::Travelling) != 0) return; - const auto generation = ++m_MoveGeneration; - m_Parent->AddCallbackTimer(0.1f, [this, generation] { - if (generation != m_MoveGeneration) return; - auto* const simpleMover = GetSimpleMoverSubComponent(); - if (simpleMover) SimpleMoverGotoWaypoint(simpleMover->mCurrentWaypointIndex == 0 ? 1 : 0); - }); + // (RunPlatform 0x00c86e10): RunSimpleMover. + GetSimpleMoverSubComponent()->mDirtyState = true; return; } @@ -694,6 +697,20 @@ NiPoint3 CalcRotatorAngularVelocity(const float rotX, const float rotY, const fl return NiPoint3(q[0] * scale, q[1] * scale, q[2] * scale); } +// LWOPlatform::RunPlatform 0x00c86e10 for a simple mover waiting at its waypoint: once the wait is over it sets off for +// its other end (LWOPlatform::IncrementWaitingTime 0x00bd4370, StartTravelling 0x0058b170). A quick build's does not +// run until it is built (LWOPlatform::SendMessage 0x00bdc930). Live: a built counterweight was serialized waiting (9) +// and about 0.1 s later travelling (2). +void MovingPlatformComponent::RunSimpleMover(const float deltaTime) { + auto* const simpleMover = GetSimpleMoverSubComponent(); + if (!UpdateActive(*simpleMover)) return; + if ((simpleMover->mState & ePlatformStateFlag::Waiting) == 0 || !simpleMover->mWaitSet) return; + simpleMover->mIdleTimeElapsed += deltaTime; + if (simpleMover->mIdleTimeElapsed < simpleMover->mTimeToWait) return; + simpleMover->mIdleTimeElapsed = 0.0f; + SimpleMoverGotoWaypoint(simpleMover->mCurrentWaypointIndex == 0 ? 1 : 0); +} + // LWOPlatform::RunPlatform 0x00c86e10 for a rotator: its wait never ends (IncrementWaitingTime 0x004a0390) and it never // arrives (HandleStoppedLogic 0x00bdc660 keeps the object's angular velocity at the rotator's); it has no linear // velocity (SetLinearVelocity 0x00bd1660). The client's physics turn the object at that velocity; the server turns its @@ -735,6 +752,10 @@ void MovingPlatformComponent::Run(const float deltaTime) { RunRotator(deltaTime); return; } + if (m_MoverSubComponentType == eMoverSubComponentType::simpleMover) { + RunSimpleMover(deltaTime); + return; + } if (!IsMover()) return; auto* mover = GetMoverSubComponent(); UpdateActive(*mover); diff --git a/dGame/dComponents/MovingPlatformComponent.h b/dGame/dComponents/MovingPlatformComponent.h index 1dfa72fae..6eb4e364e 100644 --- a/dGame/dComponents/MovingPlatformComponent.h +++ b/dGame/dComponents/MovingPlatformComponent.h @@ -171,12 +171,12 @@ public: /** * A simple mover (5), as the client's LWOPlatformSimpleMover: between a start point and that point moved by * platformMove (in the start rotation), taking platformMoveTime seconds, as GenerateSimpleMoverPath builds its two - * waypoint path. At an end it has stopped there (28), as any platform on a path run once. + * waypoint path. At an end it has stopped there (28), as any platform on a path run once. Like a mover it is made + * stopped at its waypoint (12) and, unless startPathingOnLoad is false, waits there (9, as live constructed the NJ + * counterweights) the shortest wait before it sets off for its other end, a quick build's once it is built. */ class SimpleMoverSubComponent final : public PlatformSubComponent { public: - // Live constructed the NJ counterweights waiting at their waypoint (9) - SimpleMoverSubComponent() { mState = ePlatformStateFlag::Waiting | ePlatformStateFlag::ReachedDesiredWaypoint; } eMoverSubComponentType GetType() const override { return eMoverSubComponentType::simpleMover; } // LWOPlatformSimpleMover::Deserialize: a dirty bit, then whether there is a starting point with its position and @@ -369,6 +369,8 @@ private: void SetupMoverPath(); // Puts the object where the mover is, without a serialization of its physics void MoveObject(); + // A simple mover's wait at its waypoint before it sets off by itself + void RunSimpleMover(float deltaTime); // A rotator's settings from the level config, and where it is on its path void SetupRotator(); // Turns a travelling rotator's object by its angular velocity diff --git a/tests/dGameTests/dComponentsTests/MovingPlatformComponentTests.cpp b/tests/dGameTests/dComponentsTests/MovingPlatformComponentTests.cpp index 93d49997e..908255243 100644 --- a/tests/dGameTests/dComponentsTests/MovingPlatformComponentTests.cpp +++ b/tests/dGameTests/dComponentsTests/MovingPlatformComponentTests.cpp @@ -575,3 +575,50 @@ TEST_F(MovingPlatformComponentTest, MoverKeepsItsVelocityThroughAWaitLikeLive) { EXPECT_EQ(mover->mPosition, NiPoint3(30.0f, 0.0f, 30.0f)); EXPECT_EQ(entity.GetPosition(), NiPoint3(30.0f, 0.0f, 30.0f)); } + +// A simple mover without a quick build runs from when it is loaded, as a mover does: waiting at its waypoint (9) for the +// shortest wait, then off to its other end, where it has stopped (28). With startPathingOnLoad false it stays as it was +// made (12), and so does one its script stops when it starts up (live's NJ spinner script, L_NJ_SPINNER_SERVER). +TEST_F(MovingPlatformComponentTest, SimpleMoverSetsOffOnLoadUnlessToldNotTo) { + info.lot = 16141; + info.pos = NiPoint3(10.0f, 20.0f, 30.0f); + info.rot = QuatUtils::IDENTITY; + { + Entity entity(288300744895900115, info); + entity.AddComponent(-1); + auto* const platform = entity.AddComponent(41, ""); + auto* const mover = platform->GetSimpleMoverSubComponent(); + EXPECT_EQ(mover->mState, 9u); + entity.Update(0.05f); + EXPECT_EQ(mover->mState, 9u); + entity.Update(0.06f); + EXPECT_EQ(mover->mState, 2u); + entity.Update(0.0f); + entity.Update(1.1f); + EXPECT_EQ(mover->mState, 28u); + EXPECT_EQ(mover->mCurrentWaypointIndex, 1u); + // Stopped at its end it does not set off again by itself + entity.Update(1.0f); + EXPECT_EQ(mover->mState, 28u); + } + { + Entity entity(288300744895900116, info); + auto* const platform = entity.AddComponent(41, ""); + auto* const mover = platform->GetSimpleMoverSubComponent(); + platform->StopPathing(); + EXPECT_EQ(mover->mState, 12u); + entity.Update(1.0f); + EXPECT_EQ(mover->mState, 12u); + EXPECT_EQ(mover->mCurrentWaypointIndex, 0u); + } + { + info.settings.Insert(u"startPathingOnLoad", false); + Entity entity(288300744895900117, info); + info.settings.values.clear(); + auto* const platform = entity.AddComponent(41, ""); + auto* const mover = platform->GetSimpleMoverSubComponent(); + EXPECT_EQ(mover->mState, 12u); + entity.Update(1.0f); + EXPECT_EQ(mover->mState, 12u); + } +}