diff --git a/dGame/dComponents/MovingPlatformComponent.cpp b/dGame/dComponents/MovingPlatformComponent.cpp index 5c06d355..11294c2a 100644 --- a/dGame/dComponents/MovingPlatformComponent.cpp +++ b/dGame/dComponents/MovingPlatformComponent.cpp @@ -27,7 +27,7 @@ PlatformSubComponent::PlatformSubComponent(MovingPlatformComponent* parentCompon m_InReverse = false; m_ShouldStopAtDesiredWaypoint = false; - m_PercentBetweenPoints = 0.0f; + m_PercentUntilNextWaypoint = 0.0f; m_CurrentWaypointIndex = 0; m_NextWaypointIndex = 0; @@ -35,6 +35,80 @@ PlatformSubComponent::PlatformSubComponent(MovingPlatformComponent* parentCompon m_IdleTimeElapsed = 0.0f; } +void PlatformSubComponent::AdvanceToNextWaypoint() { + uint32_t numWaypoints = m_Path->pathWaypoints.size(); + m_CurrentWaypointIndex = m_NextWaypointIndex; + uint32_t nextWaypointIndex = m_CurrentWaypointIndex; + if (numWaypoints <= nextWaypointIndex) { + PathBehavior behavior = m_Path->pathBehavior; + if (behavior == PathBehavior::Once) { + nextWaypointIndex = m_Path->pathWaypoints.size() - 1; + } else if (behavior == PathBehavior::Bounce) { + nextWaypointIndex = m_Path->pathWaypoints.size() - 2; + m_InReverse = true; + } else { + m_NextWaypointIndex = 0; + } + } + m_NextWaypointIndex = nextWaypointIndex; +} + +void PlatformSubComponent::AdvanceToNextReverseWaypoint() { + uint32_t numWaypoints = m_Path->pathWaypoints.size(); + m_CurrentWaypointIndex = m_NextWaypointIndex; + int32_t nextWaypointIndex = m_CurrentWaypointIndex; + if (nextWaypointIndex < 0) { + PathBehavior behavior = m_Path->pathBehavior; + if (behavior == PathBehavior::Once) { + nextWaypointIndex = 0; + } else if (behavior == PathBehavior::Bounce) { + nextWaypointIndex = 1; + m_InReverse = false; + } else { + nextWaypointIndex = m_Path->pathWaypoints.size() - 1; + } + } + m_NextWaypointIndex = nextWaypointIndex; +} + +void PlatformSubComponent::SetupPath(const std::string& pathName, uint32_t startingWaypointIndex, bool startsInReverse) { + m_Path = Game::zoneManager->GetZone()->GetPath(pathName); + if (!m_Path) { + Game::logger->Log("MovingPlatformComponent", "Failed to find path (%s)", pathName.c_str()); + return; + } + m_InReverse = startsInReverse; + m_CurrentWaypointIndex = startingWaypointIndex; + m_TimeBasedMovement = m_Path->movingPlatform.timeBasedMovement; +} + +const PathWaypoint PlatformSubComponent::GetNextWaypoint() const { + DluAssert(m_Path != nullptr); + if (m_NextWaypointIndex >= m_Path->pathWaypoints.size()) return PathWaypoint(); + return m_Path->pathWaypoints.at(m_NextWaypointIndex); +} + +const PathWaypoint PlatformSubComponent::GetCurrentWaypoint() const { + DluAssert(m_Path != nullptr); + if (m_CurrentWaypointIndex >= m_Path->pathWaypoints.size()) return PathWaypoint(); + return m_Path->pathWaypoints.at(m_CurrentWaypointIndex); +} + +float PlatformSubComponent::CalculateSpeed() const { + float speed; + if (m_TimeBasedMovement) { + float unitizedDirection = 1.0f / (GetNextWaypoint().position - GetCurrentWaypoint().position).Length(); + speed = unitizedDirection / GetCurrentWaypoint().movingPlatform.speed; + } else { + speed = (GetNextWaypoint().movingPlatform.speed - GetCurrentWaypoint().movingPlatform.speed) * m_PercentUntilNextWaypoint + GetCurrentWaypoint().movingPlatform.speed; + } + return speed; +} + +NiPoint3 PlatformSubComponent::CalculateLinearVelocity() { + return (GetNextWaypoint().position - GetCurrentWaypoint().position).Unitize() * CalculateSpeed(); +} + void PlatformSubComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { outBitStream->Write(bIsInitialUpdate || m_IsDirty); if (!(bIsInitialUpdate || m_IsDirty)) return; @@ -42,7 +116,7 @@ void PlatformSubComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsIn outBitStream->Write(m_DesiredWaypointIndex); outBitStream->Write(m_ShouldStopAtDesiredWaypoint); outBitStream->Write(m_InReverse); - outBitStream->Write(m_PercentBetweenPoints); + outBitStream->Write(m_PercentUntilNextWaypoint); outBitStream->Write(m_Position.x); outBitStream->Write(m_Position.y); outBitStream->Write(m_Position.z); @@ -145,8 +219,6 @@ void SimpleMoverPlatformSubComponent::LoadConfigData() { ); m_PlatformMove = platformMove; m_MoveTime = m_ParentComponent->GetParent()->GetVar(u"platformMoveTime"); - // idk either. client does it! - m_StartAtEnd = m_ParentComponent->GetParent()->GetVar(u"attached_path_start") != 0; m_StartAtEnd = m_ParentComponent->GetParent()->GetVar(u"platformStartAtEnd"); } @@ -197,6 +269,8 @@ void MovingPlatformComponent::LoadConfigData() { if (m_Parent->GetVar(u"platformIsRotater")) { AddMovingPlatform(); } + m_StartingWaypointIndex = m_Parent->GetVar(u"attached_path_start"); + m_StartsIsInReverse = false; m_DirtyPathInfo = true; } diff --git a/dGame/dComponents/MovingPlatformComponent.h b/dGame/dComponents/MovingPlatformComponent.h index 05f98d4f..94157c33 100644 --- a/dGame/dComponents/MovingPlatformComponent.h +++ b/dGame/dComponents/MovingPlatformComponent.h @@ -15,6 +15,7 @@ #include "Component.h" #include "eReplicaComponentType.h" +class PathWaypoint; class Path; /** @@ -54,10 +55,20 @@ public: virtual void ResumePathing(); virtual void StopPathing(); virtual void Update(float deltaTime); + float CalculateSpeed() const; + const PathWaypoint GetNextWaypoint() const; + const PathWaypoint GetCurrentWaypoint() const; + void SetupPath(const std::string& pathName, uint32_t startingWaypointIndex, bool startsInReverse); + void AdvanceToNextWaypoint(); + void AdvanceToNextReverseWaypoint(); + NiPoint3 CalculateLinearVelocity(); protected: #ifdef _MOVING_PLATFORM_TEST public: + void _SetPath(const Path* path) { + m_Path = path; + } #endif MovingPlatformComponent* m_ParentComponent = nullptr; @@ -66,10 +77,10 @@ public: */ uint32_t m_State = eMovementPlatformState::Stopped | eMovementPlatformState::ReachedDesiredWaypoint; int32_t m_DesiredWaypointIndex = 0; - float m_PercentBetweenPoints = 0; + float m_PercentUntilNextWaypoint = 0; NiPoint3 m_Position; - uint32_t m_CurrentWaypointIndex; - uint32_t m_NextWaypointIndex; + int32_t m_CurrentWaypointIndex; + int32_t m_NextWaypointIndex; float m_IdleTimeElapsed = 0; float m_Speed = 0; float m_WaitTime = 0; @@ -80,6 +91,7 @@ public: NiPoint3 m_LinearVelocity; NiPoint3 m_AngularVelocity; bool m_TimeBasedMovement = false; + const Path* m_Path = nullptr; }; class MoverPlatformSubComponent : public PlatformSubComponent { @@ -209,10 +221,10 @@ public: static_assert(std::is_base_of::value, "MovingPlatform must derive from PlatformSubComponent"); auto hasPlatform = std::find_if(m_Platforms.begin(), m_Platforms.end(), [](const std::unique_ptr& platform) { return platform->GetPlatformType() == MovingPlatform::SubComponentType; - }) != m_Platforms.end(); - if (!hasPlatform) { - m_Platforms.push_back(std::make_unique(this, std::forward(arguments)...)); - } + }) != m_Platforms.end(); + if (!hasPlatform) { + m_Platforms.push_back(std::make_unique(this, std::forward(arguments)...)); + } } int32_t GetComponentId() const { return componentId; } @@ -220,8 +232,8 @@ public: #ifdef _MOVING_PLATFORM_TEST /** * Only used for testing. Do not call in production code. Let the constructor take care of this. - * - * @param platformSubComponent + * + * @param platformSubComponent */ void _AddPlatformSubComponent(std::unique_ptr platformSubComponent) { m_Platforms.push_back(std::move(platformSubComponent)); @@ -230,7 +242,7 @@ public: void _SetPath(const std::u16string& path) { m_PathName = path; m_DirtyPathInfo = true; - } +} #endif private: /** diff --git a/tests/dGameTests/dComponentsTests/MovingPlatformComponentTests.cpp b/tests/dGameTests/dComponentsTests/MovingPlatformComponentTests.cpp index d215a134..79f370be 100644 --- a/tests/dGameTests/dComponentsTests/MovingPlatformComponentTests.cpp +++ b/tests/dGameTests/dComponentsTests/MovingPlatformComponentTests.cpp @@ -7,6 +7,7 @@ #define _MOVING_PLATFORM_TEST #include "MovingPlatformComponent.h" #undef _MOVING_PLATFORM_TEST +#include "Zone.h" #include "SimplePhysicsComponent.h" #include "eReplicaComponentType.h" @@ -15,8 +16,22 @@ protected: std::unique_ptr baseEntity; CBITSTREAM; uint32_t flags = 0; + Path path; void SetUp() override { SetUpDependencies(); + path.movingPlatform.timeBasedMovement = false; + path.pathBehavior = PathBehavior::Once; + PathWaypoint waypointStart; + waypointStart.position = NiPoint3(1, 2, 3); + waypointStart.rotation = NiQuaternion(4, 5, 6, 7); + + PathWaypoint waypointEnd; + waypointEnd.position = NiPoint3(4, 5, 6); + waypointEnd.rotation = NiQuaternion(7, 8, 9, 10); + + path.pathWaypoints.push_back(waypointStart); + path.pathWaypoints.push_back(waypointEnd); + baseEntity = std::make_unique(15, GameDependenciesTest::info); baseEntity->SetVar(u"dbonly", false); baseEntity->SetVar(u"platformMoveX", 23); @@ -33,7 +48,7 @@ protected: auto moverPlatformSubComponent = std::make_unique(movingPlatformComponent); moverPlatformSubComponent->m_State = eMovementPlatformState::Stopped | eMovementPlatformState::ReachedDesiredWaypoint; moverPlatformSubComponent->m_DesiredWaypointIndex = 1; - moverPlatformSubComponent->m_PercentBetweenPoints = 2; + moverPlatformSubComponent->m_PercentUntilNextWaypoint = 2; moverPlatformSubComponent->m_Position = NiPoint3(3, 4, 5); moverPlatformSubComponent->m_CurrentWaypointIndex = 6; moverPlatformSubComponent->m_NextWaypointIndex = 7; @@ -46,7 +61,7 @@ protected: auto rotatorPlatformSubComponent = std::make_unique(movingPlatformComponent); rotatorPlatformSubComponent->m_State = eMovementPlatformState::Travelling; rotatorPlatformSubComponent->m_DesiredWaypointIndex = 12; - rotatorPlatformSubComponent->m_PercentBetweenPoints = 13; + rotatorPlatformSubComponent->m_PercentUntilNextWaypoint = 13; rotatorPlatformSubComponent->m_Position = NiPoint3(14, 15, 16); rotatorPlatformSubComponent->m_CurrentWaypointIndex = 17; rotatorPlatformSubComponent->m_NextWaypointIndex = 18; @@ -59,7 +74,7 @@ protected: auto simpleMoverPlatformSubComponent = std::make_unique(movingPlatformComponent, NiPoint3(), true); simpleMoverPlatformSubComponent->m_State = eMovementPlatformState::Waiting | eMovementPlatformState::ReachedDesiredWaypoint | eMovementPlatformState::ReachedFinalWaypoint; simpleMoverPlatformSubComponent->m_DesiredWaypointIndex = 23; - simpleMoverPlatformSubComponent->m_PercentBetweenPoints = 24; + simpleMoverPlatformSubComponent->m_PercentUntilNextWaypoint = 24; simpleMoverPlatformSubComponent->m_CurrentWaypointIndex = 28; simpleMoverPlatformSubComponent->m_NextWaypointIndex = 29; simpleMoverPlatformSubComponent->m_IdleTimeElapsed = 30; @@ -280,3 +295,69 @@ TEST_F(MovingPlatformComponentTests, MovingPlatformConstructionTest) { TEST_F(MovingPlatformComponentTests, MovingPlatformSerializationTest) { TestSerialization(); } + +TEST_F(MovingPlatformComponentTests, MovingPlatformSubComponentPathAdvanceForwardTest) { + MoverPlatformSubComponent moverPlatformSubComponent(nullptr); + moverPlatformSubComponent._SetPath(&path); + moverPlatformSubComponent.m_CurrentWaypointIndex = 0; + moverPlatformSubComponent.m_NextWaypointIndex = 1; + moverPlatformSubComponent.m_InReverse = false; + moverPlatformSubComponent.AdvanceToNextWaypoint(); + ASSERT_EQ(moverPlatformSubComponent.m_CurrentWaypointIndex, 1); + ASSERT_EQ(moverPlatformSubComponent.m_NextWaypointIndex, 0); + ASSERT_FALSE(moverPlatformSubComponent.m_InReverse); + moverPlatformSubComponent.AdvanceToNextWaypoint(); + ASSERT_EQ(moverPlatformSubComponent.m_CurrentWaypointIndex, 1); + ASSERT_EQ(moverPlatformSubComponent.m_NextWaypointIndex, 0); + ASSERT_FALSE(moverPlatformSubComponent.m_InReverse); + path.pathBehavior = PathBehavior::Bounce; + moverPlatformSubComponent.AdvanceToNextWaypoint(); + ASSERT_EQ(moverPlatformSubComponent.m_CurrentWaypointIndex, 0); + ASSERT_EQ(moverPlatformSubComponent.m_NextWaypointIndex, 1); + ASSERT_TRUE(moverPlatformSubComponent.m_InReverse); +} + +TEST_F(MovingPlatformComponentTests, MovingPlatformSubComponentPathAdvanceReverseTest) { + MoverPlatformSubComponent moverPlatformSubComponent(nullptr); + moverPlatformSubComponent._SetPath(&path); + moverPlatformSubComponent.m_CurrentWaypointIndex = 1; + moverPlatformSubComponent.m_NextWaypointIndex = 0; + moverPlatformSubComponent.m_InReverse = true; + moverPlatformSubComponent.AdvanceToNextReverseWaypoint(); + ASSERT_EQ(moverPlatformSubComponent.m_CurrentWaypointIndex, 0); + ASSERT_EQ(moverPlatformSubComponent.m_NextWaypointIndex, 0); + ASSERT_TRUE(moverPlatformSubComponent.m_InReverse); + path.pathBehavior = PathBehavior::Bounce; + moverPlatformSubComponent.m_CurrentWaypointIndex = 1; + moverPlatformSubComponent.m_NextWaypointIndex = 0; + moverPlatformSubComponent.AdvanceToNextReverseWaypoint(); + ASSERT_EQ(moverPlatformSubComponent.m_CurrentWaypointIndex, 0); + ASSERT_EQ(moverPlatformSubComponent.m_NextWaypointIndex, 1); + ASSERT_TRUE(moverPlatformSubComponent.m_InReverse); + moverPlatformSubComponent.AdvanceToNextReverseWaypoint(); + ASSERT_EQ(moverPlatformSubComponent.m_CurrentWaypointIndex, 1); + ASSERT_EQ(moverPlatformSubComponent.m_NextWaypointIndex, 0); + ASSERT_FALSE(moverPlatformSubComponent.m_InReverse); +} + +TEST_F(MovingPlatformComponentTests, MovingPlatformMoverSpeedCalculationTest) { + MoverPlatformSubComponent moverPlatformSubComponent(nullptr); + path.pathWaypoints.at(0).position = NiPoint3(99.296440, 419.293335, 207.219498); + path.pathWaypoints.at(0).movingPlatform.speed = 16.0f; + + path.pathWaypoints.at(1).position = NiPoint3(141.680099, 419.990051, 208.680450); + path.pathWaypoints.at(1).movingPlatform.speed = 16.0f; + path.pathBehavior = PathBehavior::Bounce; + moverPlatformSubComponent._SetPath(&path); + moverPlatformSubComponent.m_Speed = 16.0f; + moverPlatformSubComponent.m_TimeBasedMovement = false; + moverPlatformSubComponent.m_InReverse = false; + moverPlatformSubComponent.m_CurrentWaypointIndex = 0; + moverPlatformSubComponent.m_NextWaypointIndex = 1; + ASSERT_EQ(moverPlatformSubComponent.CalculateSpeed(), 16.0f); + NiPoint3 r = moverPlatformSubComponent.CalculateLinearVelocity(); + ASSERT_FLOAT_EQ(r.x, 15.988346099854); + ASSERT_FLOAT_EQ(r.y, 0.26282161474228); + ASSERT_FLOAT_EQ(r.z, 0.5511137843132); + moverPlatformSubComponent.AdvanceToNextWaypoint(); +}