/* * Darkflame Universe * Copyright 2019 */ #include "MovingPlatformComponent.h" #include "BitStream.h" #include "GeneralUtils.h" #include "dZoneManager.h" #include "EntityManager.h" #include "dLogger.h" #include "GameMessages.h" #include "CppScripts.h" #include "SimplePhysicsComponent.h" #include "CDClientManager.h" #include "CDMovingPlatformComponentTable.h" #include "Zone.h" //------------- PlatformSubComponent begin -------------- PlatformSubComponent::PlatformSubComponent(MovingPlatformComponent* parentComponent) { m_Position = NiPoint3::ZERO; m_ParentComponent = parentComponent; m_State = eMovementPlatformState::Stopped | eMovementPlatformState::ReachedDesiredWaypoint; m_DesiredWaypointIndex = 0; m_InReverse = false; m_ShouldStopAtDesiredWaypoint = false; m_PercentUntilNextWaypoint = 0.0f; m_CurrentWaypointIndex = 0; m_NextWaypointIndex = 0; 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; outBitStream->Write(m_State); outBitStream->Write(m_DesiredWaypointIndex); outBitStream->Write(m_ShouldStopAtDesiredWaypoint); outBitStream->Write(m_InReverse); outBitStream->Write(m_PercentUntilNextWaypoint); outBitStream->Write(m_Position.x); outBitStream->Write(m_Position.y); outBitStream->Write(m_Position.z); outBitStream->Write(m_CurrentWaypointIndex); outBitStream->Write(m_NextWaypointIndex); outBitStream->Write(m_IdleTimeElapsed); outBitStream->Write(m_MoveTimeElapsed); if (!bIsInitialUpdate) m_IsDirty = false; } void PlatformSubComponent::StartPathing() { m_State |= eMovementPlatformState::Travelling; m_State &= ~eMovementPlatformState::Stopped; m_State &= ~eMovementPlatformState::Waiting; } void PlatformSubComponent::ResumePathing() { if (m_State & eMovementPlatformState::Stopped && (m_State & eMovementPlatformState::ReachedDesiredWaypoint) == 0) { StartPathing(); } if (m_State & eMovementPlatformState::Travelling == 0) { m_State |= eMovementPlatformState::Waiting; m_State &= ~eMovementPlatformState::Stopped; m_State &= ~eMovementPlatformState::Travelling; } else { m_State &= eMovementPlatformState::Waiting; m_State &= eMovementPlatformState::Travelling; m_State &= eMovementPlatformState::Stopped; // Set the velocities } } void PlatformSubComponent::StopPathing() { m_State |= eMovementPlatformState::Stopped; m_State &= ~eMovementPlatformState::Travelling; m_State &= ~eMovementPlatformState::Waiting; m_LinearVelocity = NiPoint3::ZERO; m_AngularVelocity = NiPoint3::ZERO; } void PlatformSubComponent::Update(float deltaTime) { if (m_TimeBasedMovement && m_State & eMovementPlatformState::Travelling) { m_MoveTimeElapsed += deltaTime; } if (m_State == 0) return; } //------------- PlatformSubComponent end -------------- //------------- MoverPlatformSubComponent begin -------------- MoverPlatformSubComponent::MoverPlatformSubComponent(MovingPlatformComponent* parentComponent) : PlatformSubComponent(parentComponent) { } //------------- MoverPlatformSubComponent end -------------- //------------- RotatorPlatformSubComponent begin -------------- RotatorPlatformSubComponent::RotatorPlatformSubComponent(MovingPlatformComponent* parentComponent) : PlatformSubComponent(parentComponent) { } //------------- RotatorPlatformSubComponent end -------------- //------------- SimpleMoverPlatformSubComponent begin -------------- void SimpleMoverPlatformSubComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { outBitStream->Write(bIsInitialUpdate || m_DirtyStartingPoint); if (bIsInitialUpdate || m_DirtyStartingPoint) { outBitStream->Write(m_HasStartingPoint); if (m_HasStartingPoint) { outBitStream->Write(m_StartingPoint.x); outBitStream->Write(m_StartingPoint.y); outBitStream->Write(m_StartingPoint.z); outBitStream->Write(m_StartingRotation.w); outBitStream->Write(m_StartingRotation.x); outBitStream->Write(m_StartingRotation.y); outBitStream->Write(m_StartingRotation.z); } if (!bIsInitialUpdate) m_DirtyStartingPoint = false; } outBitStream->Write(bIsInitialUpdate || m_IsDirty); if (bIsInitialUpdate || m_IsDirty) { outBitStream->Write(m_State); outBitStream->Write(m_CurrentWaypointIndex); outBitStream->Write(m_InReverse); if (!bIsInitialUpdate) m_IsDirty = false; } } void SimpleMoverPlatformSubComponent::LoadConfigData() { if (m_ParentComponent->GetParent()->GetVar(u"dbonly")) return; NiPoint3 platformMove( m_ParentComponent->GetParent()->GetVar(u"platformMoveX"), m_ParentComponent->GetParent()->GetVar(u"platformMoveY"), m_ParentComponent->GetParent()->GetVar(u"platformMoveZ") ); m_PlatformMove = platformMove; m_MoveTime = m_ParentComponent->GetParent()->GetVar(u"platformMoveTime"); m_StartAtEnd = m_ParentComponent->GetParent()->GetVar(u"platformStartAtEnd"); } void SimpleMoverPlatformSubComponent::LoadDataFromTemplate() { if (!m_ParentComponent->GetParent()->GetVar(u"dbonly")) return; auto* movingPlatformTable = CDClientManager::Instance().GetTable(); if (movingPlatformTable == nullptr) return; const auto& platformEntry = movingPlatformTable->GetPlatformEntry(m_ParentComponent->GetComponentId()); if (!platformEntry || !platformEntry->platformIsSimpleMover) return; NiPoint3 platformMove = platformEntry->platformMove; float moveTime = platformEntry->moveTime; m_PlatformMove = platformMove; m_MoveTime = moveTime; } SimpleMoverPlatformSubComponent::SimpleMoverPlatformSubComponent(MovingPlatformComponent* parentComponent, const NiPoint3& platformMove, const bool startsInReverse) : PlatformSubComponent(parentComponent) { m_PlatformMove = platformMove; m_InReverse = startsInReverse; m_HasStartingPoint = true; m_DirtyStartingPoint = true; m_IsDirty = true; m_StartingPoint = m_ParentComponent->GetParent()->GetPosition(); m_StartingRotation = m_ParentComponent->GetParent()->GetRotation(); } //------------- SimpleMoverPlatformSubComponent end -------------- //------------- MovingPlatformComponent begin -------------- MovingPlatformComponent::MovingPlatformComponent(Entity* parent, const std::string& pathName) : Component(parent) { } void MovingPlatformComponent::LoadDataFromTemplate() { std::for_each(m_Platforms.begin(), m_Platforms.end(), [](const std::unique_ptr& platform) { platform->LoadDataFromTemplate(); }); } void MovingPlatformComponent::LoadConfigData() { if (m_Parent->GetVar(u"platformIsSimpleMover")) { AddMovingPlatform(NiPoint3::ZERO, false); } if (m_Parent->GetVar(u"platformIsMover")) { AddMovingPlatform(); } if (m_Parent->GetVar(u"platformIsRotater")) { AddMovingPlatform(); } m_StartingWaypointIndex = m_Parent->GetVar(u"attached_path_start"); m_StartsIsInReverse = false; m_DirtyPathInfo = true; } void MovingPlatformComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { // For some reason we need to write this here instead of later on. outBitStream->Write(!m_Platforms.empty()); outBitStream->Write(bIsInitialUpdate || m_DirtyPathInfo); if (bIsInitialUpdate || m_DirtyPathInfo) { outBitStream->Write(!m_PathName.empty()); if (!m_PathName.empty()) { outBitStream->Write(static_cast(m_PathName.size())); for (const auto& c : m_PathName) { outBitStream->Write(static_cast(c)); } outBitStream->Write(m_StartingWaypointIndex); outBitStream->Write(m_StartsIsInReverse); } if (!bIsInitialUpdate) m_DirtyPathInfo = false; } if (m_Platforms.empty()) return; for (const auto& platform : m_Platforms) { outBitStream->Write1(); // Has platform to write outBitStream->Write(platform->GetPlatformType()); platform->Serialize(outBitStream, bIsInitialUpdate); } outBitStream->Write0(); // No more platforms to write } void MovingPlatformComponent::OnRebuildInitilized() { StopPathing(); } void MovingPlatformComponent::OnCompleteRebuild() { if (m_NoAutoStart) return; StartPathing(); } void MovingPlatformComponent::SetMovementState(eMovementPlatformState value) { // auto* subComponent = static_cast(m_MoverSubComponent); // subComponent->mState = value; // Game::entityManager->SerializeEntity(m_Parent); } void MovingPlatformComponent::GotoWaypoint(uint32_t index, bool stopAtWaypoint) { // auto* subComponent = static_cast(m_MoverSubComponent); // subComponent->mDesiredWaypointIndex = index; // subComponent->mNextWaypointIndex = index; // subComponent->mShouldStopAtDesiredWaypoint = stopAtWaypoint; // StartPathing(); } void MovingPlatformComponent::StartPathing() { std::for_each(m_Platforms.begin(), m_Platforms.end(), [](const std::unique_ptr& platform) { platform->StartPathing(); }); // state == Travelling // //GameMessages::SendStartPathing(m_Parent); // m_PathingStopped = false; // auto* subComponent = static_cast(m_MoverSubComponent); // subComponent->mShouldStopAtDesiredWaypoint = true; // subComponent->mState = eMovementPlatformState::Stationary; // NiPoint3 targetPosition; // if (m_Path != nullptr) { // const auto& currentWaypoint = m_Path->pathWaypoints[subComponent->mCurrentWaypointIndex]; // const auto& nextWaypoint = m_Path->pathWaypoints[subComponent->mNextWaypointIndex]; // subComponent->mPosition = currentWaypoint.position; // subComponent->mSpeed = currentWaypoint.movingPlatform.speed; // subComponent->mWaitTime = currentWaypoint.movingPlatform.wait; // targetPosition = nextWaypoint.position; // } else { // subComponent->mPosition = m_Parent->GetPosition(); // subComponent->mSpeed = 1.0f; // subComponent->mWaitTime = 2.0f; // targetPosition = m_Parent->GetPosition() + NiPoint3(0.0f, 10.0f, 0.0f); // } // m_Parent->AddCallbackTimer(subComponent->mWaitTime, [this] { // SetMovementState(eMovementPlatformState::Moving); // }); // const auto travelTime = Vector3::Distance(targetPosition, subComponent->mPosition) / subComponent->mSpeed + 1.5f; // const auto travelNext = subComponent->mWaitTime + travelTime; // m_Parent->AddCallbackTimer(travelTime, [subComponent, this] { // for (CppScripts::Script* script : CppScripts::GetEntityScripts(m_Parent)) { // script->OnWaypointReached(m_Parent, subComponent->mNextWaypointIndex); // } // }); // m_Parent->AddCallbackTimer(travelNext, [this] { // ContinuePathing(); // }); // //GameMessages::SendPlatformResync(m_Parent, UNASSIGNED_SYSTEM_ADDRESS); // Game::entityManager->SerializeEntity(m_Parent); } void MovingPlatformComponent::ContinuePathing() { // state == Travelling // auto* subComponent = static_cast(m_MoverSubComponent); // subComponent->mState = eMovementPlatformState::Stationary; // subComponent->mCurrentWaypointIndex = subComponent->mNextWaypointIndex; // NiPoint3 targetPosition; // uint32_t pathSize; // PathBehavior behavior; // if (m_Path != nullptr) { // const auto& currentWaypoint = m_Path->pathWaypoints[subComponent->mCurrentWaypointIndex]; // const auto& nextWaypoint = m_Path->pathWaypoints[subComponent->mNextWaypointIndex]; // subComponent->mPosition = currentWaypoint.position; // subComponent->mSpeed = currentWaypoint.movingPlatform.speed; // subComponent->mWaitTime = currentWaypoint.movingPlatform.wait; // + 2; // pathSize = m_Path->pathWaypoints.size() - 1; // behavior = static_cast(m_Path->pathBehavior); // targetPosition = nextWaypoint.position; // } else { // subComponent->mPosition = m_Parent->GetPosition(); // subComponent->mSpeed = 1.0f; // subComponent->mWaitTime = 2.0f; // targetPosition = m_Parent->GetPosition() + NiPoint3(0.0f, 10.0f, 0.0f); // pathSize = 1; // behavior = PathBehavior::Loop; // } // if (m_Parent->GetLOT() == 9483) { // behavior = PathBehavior::Bounce; // } else { // return; // } // if (subComponent->mCurrentWaypointIndex >= pathSize) { // subComponent->mCurrentWaypointIndex = pathSize; // switch (behavior) { // case PathBehavior::Once: // Game::entityManager->SerializeEntity(m_Parent); // return; // case PathBehavior::Bounce: // subComponent->mInReverse = true; // break; // case PathBehavior::Loop: // subComponent->mNextWaypointIndex = 0; // break; // default: // break; // } // } else if (subComponent->mCurrentWaypointIndex == 0) { // subComponent->mInReverse = false; // } // if (subComponent->mInReverse) { // subComponent->mNextWaypointIndex = subComponent->mCurrentWaypointIndex - 1; // } else { // subComponent->mNextWaypointIndex = subComponent->mCurrentWaypointIndex + 1; // } // /* // subComponent->mNextWaypointIndex = 0; // subComponent->mCurrentWaypointIndex = 1; // */ // //GameMessages::SendPlatformResync(m_Parent, UNASSIGNED_SYSTEM_ADDRESS); // if (subComponent->mCurrentWaypointIndex == subComponent->mDesiredWaypointIndex) { // // TODO: Send event? // StopPathing(); // return; // } // m_Parent->CancelCallbackTimers(); // m_Parent->AddCallbackTimer(subComponent->mWaitTime, [this] { // SetMovementState(eMovementPlatformState::Moving); // }); // auto travelTime = Vector3::Distance(targetPosition, subComponent->mPosition) / subComponent->mSpeed + 1.5; // if (m_Parent->GetLOT() == 9483) { // travelTime += 20; // } // const auto travelNext = subComponent->mWaitTime + travelTime; // m_Parent->AddCallbackTimer(travelTime, [subComponent, this] { // for (CppScripts::Script* script : CppScripts::GetEntityScripts(m_Parent)) { // script->OnWaypointReached(m_Parent, subComponent->mNextWaypointIndex); // } // }); // m_Parent->AddCallbackTimer(travelNext, [this] { // ContinuePathing(); // }); // Game::entityManager->SerializeEntity(m_Parent); } void MovingPlatformComponent::StopPathing() { // state == Stopped //m_Parent->CancelCallbackTimers(); // auto* subComponent = static_cast(m_MoverSubComponent); // m_PathingStopped = true; // subComponent->mState = eMovementPlatformState::Stopped; // subComponent->mDesiredWaypointIndex = -1; // subComponent->mShouldStopAtDesiredWaypoint = false; // Game::entityManager->SerializeEntity(m_Parent); //GameMessages::SendPlatformResync(m_Parent, UNASSIGNED_SYSTEM_ADDRESS); } bool MovingPlatformComponent::GetNoAutoStart() const { return false; // return m_NoAutoStart; } void MovingPlatformComponent::SetNoAutoStart(const bool value) { // m_NoAutoStart = value; } void MovingPlatformComponent::WarpToWaypoint(size_t index) { // const auto& waypoint = m_Path->pathWaypoints[index]; // m_Parent->SetPosition(waypoint.position); // m_Parent->SetRotation(waypoint.rotation); // Game::entityManager->SerializeEntity(m_Parent); } size_t MovingPlatformComponent::GetLastWaypointIndex() const { return 0; // return m_Path->pathWaypoints.size() - 1; } //------------- MovingPlatformComponent end --------------