/* * Darkflame Universe * Copyright 2023 */ #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 "Zone.h" MoverSubComponent::MoverSubComponent(const NiPoint3& startPos) { mPosition = NiPoint3::ZERO; mState = eMovementPlatformState::Stopped; mDesiredWaypointIndex = 0; // -1; mInReverse = false; mShouldStopAtDesiredWaypoint = false; mPercentBetweenPoints = 0.0f; mCurrentWaypointIndex = 0; mNextWaypointIndex = 0; //mCurrentWaypointIndex + 1; mIdleTimeElapsed = 0.0f; } void MoverSubComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) const { outBitStream->Write(true); outBitStream->Write(static_cast(mState)); outBitStream->Write(mDesiredWaypointIndex); outBitStream->Write(mShouldStopAtDesiredWaypoint); outBitStream->Write(mInReverse); outBitStream->Write(mPercentBetweenPoints); outBitStream->Write(mPosition.x); outBitStream->Write(mPosition.y); outBitStream->Write(mPosition.z); outBitStream->Write(mCurrentWaypointIndex); outBitStream->Write(mNextWaypointIndex); outBitStream->Write(mIdleTimeElapsed); outBitStream->Write(0.0f); // Move time elapsed } //------------- MovingPlatformComponent below -------------- MovingPlatformComponent::MovingPlatformComponent(Entity* parent, const std::string& pathName) : Component(parent) { m_MoverSubComponentType = eMoverSubComponentType::mover; m_MoverSubComponent = new MoverSubComponent(m_ParentEntity->GetDefaultPosition()); m_PathName = GeneralUtils::ASCIIToUTF16(pathName); m_Path = dZoneManager::Instance()->GetZone()->GetPath(pathName); m_NoAutoStart = false; if (!m_Path) { Game::logger->Log("MovingPlatformComponent", "Path not found: %s", pathName.c_str()); } } MovingPlatformComponent::~MovingPlatformComponent() { delete static_cast(m_MoverSubComponent); } void MovingPlatformComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate, unsigned int& flags) { // Here we don't serialize the moving platform to let the client simulate the movement if (!m_Serialize) { outBitStream->Write0(); outBitStream->Write0(); return; } outBitStream->Write1(); auto hasPath = !m_PathingStopped && !m_PathName.empty(); outBitStream->Write(hasPath); if (hasPath) { // Is on rail outBitStream->Write1(); outBitStream->Write(static_cast(m_PathName.size())); for (const auto& c : m_PathName) { outBitStream->Write(static_cast(c)); } // Starting point outBitStream->Write(0); // Reverse outBitStream->Write(false); } const auto hasPlatform = m_MoverSubComponent != nullptr; outBitStream->Write(hasPlatform); if (hasPlatform) { auto* mover = static_cast(m_MoverSubComponent); outBitStream->Write(static_cast(m_MoverSubComponentType)); if (m_MoverSubComponentType == eMoverSubComponentType::simpleMover) { // TODO } else { mover->Serialize(outBitStream, bIsInitialUpdate, flags); } } } 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; EntityManager::Instance()->SerializeEntity(m_ParentEntity); } 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() { //GameMessages::SendStartPathing(m_ParentEntity); 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_ParentEntity->GetPosition(); subComponent->mSpeed = 1.0f; subComponent->mWaitTime = 2.0f; targetPosition = m_ParentEntity->GetPosition() + NiPoint3(0.0f, 10.0f, 0.0f); } m_ParentEntity->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_ParentEntity->AddCallbackTimer(travelTime, [subComponent, this] { m_ParentEntity->GetScript()->OnWaypointReached(m_ParentEntity, subComponent->mNextWaypointIndex); }); m_ParentEntity->AddCallbackTimer(travelNext, [this] { ContinuePathing(); }); //GameMessages::SendPlatformResync(m_ParentEntity, UNASSIGNED_SYSTEM_ADDRESS); EntityManager::Instance()->SerializeEntity(m_ParentEntity); } void MovingPlatformComponent::ContinuePathing() { 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_ParentEntity->GetPosition(); subComponent->mSpeed = 1.0f; subComponent->mWaitTime = 2.0f; targetPosition = m_ParentEntity->GetPosition() + NiPoint3(0.0f, 10.0f, 0.0f); pathSize = 1; behavior = PathBehavior::Loop; } if (m_ParentEntity->GetLOT() == 9483) { behavior = PathBehavior::Bounce; } else { return; } if (subComponent->mCurrentWaypointIndex >= pathSize) { subComponent->mCurrentWaypointIndex = pathSize; switch (behavior) { case PathBehavior::Once: EntityManager::Instance()->SerializeEntity(m_ParentEntity); 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_ParentEntity, UNASSIGNED_SYSTEM_ADDRESS); if (subComponent->mCurrentWaypointIndex == subComponent->mDesiredWaypointIndex) { // TODO: Send event? StopPathing(); return; } m_ParentEntity->CancelCallbackTimers(); m_ParentEntity->AddCallbackTimer(subComponent->mWaitTime, [this] { SetMovementState(eMovementPlatformState::Moving); }); auto travelTime = Vector3::Distance(targetPosition, subComponent->mPosition) / subComponent->mSpeed + 1.5; if (m_ParentEntity->GetLOT() == 9483) { travelTime += 20; } const auto travelNext = subComponent->mWaitTime + travelTime; m_ParentEntity->AddCallbackTimer(travelTime, [subComponent, this] { m_ParentEntity->GetScript()->OnWaypointReached(m_ParentEntity, subComponent->mNextWaypointIndex); }); m_ParentEntity->AddCallbackTimer(travelNext, [this] { ContinuePathing(); }); EntityManager::Instance()->SerializeEntity(m_ParentEntity); } void MovingPlatformComponent::StopPathing() { //m_ParentEntity->CancelCallbackTimers(); auto* subComponent = static_cast(m_MoverSubComponent); m_PathingStopped = true; subComponent->mState = eMovementPlatformState::Stopped; subComponent->mDesiredWaypointIndex = -1; subComponent->mShouldStopAtDesiredWaypoint = false; EntityManager::Instance()->SerializeEntity(m_ParentEntity); //GameMessages::SendPlatformResync(m_ParentEntity, UNASSIGNED_SYSTEM_ADDRESS); } void MovingPlatformComponent::SetSerialized(bool value) { m_Serialize = value; } bool MovingPlatformComponent::GetNoAutoStart() const { 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_ParentEntity->SetPosition(waypoint.position); m_ParentEntity->SetRotation(waypoint.rotation); EntityManager::Instance()->SerializeEntity(m_ParentEntity); } size_t MovingPlatformComponent::GetLastWaypointIndex() const { return m_Path->pathWaypoints.size() - 1; } MoverSubComponent* MovingPlatformComponent::GetMoverSubComponent() const { return static_cast(m_MoverSubComponent); }