diff options
| author | Shauren <shauren.trinity@gmail.com> | 2022-06-07 16:02:51 +0200 |
|---|---|---|
| committer | Shauren <shauren.trinity@gmail.com> | 2022-06-07 16:02:51 +0200 |
| commit | a2c1b699e6e4d30c752b65241bc23191920a86fd (patch) | |
| tree | 7e580185397acd299326a714da71406f15c30ddc /src/server/game/Maps | |
| parent | b9353041a6cce58d972b63d5138517274a1bb6d6 (diff) | |
Core/Transports: Path generation rewrite v2
* No TaxiPathNode row is skipped anymore (events/delays on first node now work)
* Fixed transport animation synchronization with client
* Fixed stoppable transports randomly resuming their path after relogging
Closes #27985
Diffstat (limited to 'src/server/game/Maps')
| -rw-r--r-- | src/server/game/Maps/TransportMgr.cpp | 533 | ||||
| -rw-r--r-- | src/server/game/Maps/TransportMgr.h | 95 |
2 files changed, 379 insertions, 249 deletions
diff --git a/src/server/game/Maps/TransportMgr.cpp b/src/server/game/Maps/TransportMgr.cpp index 5bcd0db73b0..3c8fcc3498a 100644 --- a/src/server/game/Maps/TransportMgr.cpp +++ b/src/server/game/Maps/TransportMgr.cpp @@ -29,12 +29,146 @@ #include "Spline.h" #include "Transport.h" -bool KeyFrame::IsStopFrame() const +TransportPathLeg::TransportPathLeg() = default; +TransportPathLeg::~TransportPathLeg() = default; +TransportPathLeg::TransportPathLeg(TransportPathLeg&&) noexcept = default; +TransportPathLeg& TransportPathLeg::operator=(TransportPathLeg&&) noexcept = default; + +TransportTemplate::TransportTemplate() = default; +TransportTemplate::~TransportTemplate() = default; +TransportTemplate::TransportTemplate(TransportTemplate&&) noexcept = default; +TransportTemplate& TransportTemplate::operator=(TransportTemplate&&) noexcept = default; + +Optional<Position> TransportTemplate::ComputePosition(uint32 time, TransportMovementState* moveState, size_t* legIndex) const { - return (Node->Flags & TAXI_PATH_NODE_FLAG_STOP) != 0; + time %= TotalPathTime; + + // find leg + auto legItr = PathLegs.begin(); + while (legItr->StartTimestamp + legItr->Duration <= time) + { + ++legItr; + + if (legItr == PathLegs.end()) + return {}; + } + + // find segment + uint32 prevSegmentTime = legItr->StartTimestamp; + auto segmentItr = legItr->Segments.begin(); + double distanceMoved = 0.0; + bool isOnPause = false; + for (; segmentItr != std::prev(legItr->Segments.end()); ++segmentItr) + { + if (time < segmentItr->SegmentEndArrivalTimestamp) + break; + + distanceMoved = segmentItr->DistanceFromLegStartAtEnd; + if (time < segmentItr->SegmentEndArrivalTimestamp + segmentItr->Delay) + { + isOnPause = true; + break; + } + + prevSegmentTime = segmentItr->SegmentEndArrivalTimestamp + segmentItr->Delay; + } + + if (!isOnPause) + distanceMoved += CalculateDistanceMoved( + double(time - prevSegmentTime) * 0.001, + double(segmentItr->SegmentEndArrivalTimestamp - prevSegmentTime) * 0.001, + segmentItr == legItr->Segments.begin(), + segmentItr == std::prev(legItr->Segments.end())); + + Movement::SplineBase::index_type splineIndex; + float splinePointProgress; + legItr->Spline->computeIndex(std::fmin(distanceMoved / legItr->Spline->length(), 1.0), splineIndex, splinePointProgress); + + G3D::Vector3 pos, dir; + legItr->Spline->evaluate_percent(splineIndex, splinePointProgress, pos); + legItr->Spline->evaluate_derivative(splineIndex, splinePointProgress, dir); + + if (moveState) + *moveState = isOnPause ? TransportMovementState::WaitingOnPauseWaypoint : TransportMovementState::Moving; + + if (legIndex) + *legIndex = std::distance(PathLegs.begin(), legItr); + + return Position(pos.x, pos.y, pos.z, std::atan2(dir.y, dir.x) + float(M_PI)); } -TransportTemplate::~TransportTemplate() = default; +double TransportTemplate::CalculateDistanceMoved(double timePassedInSegment, double segmentDuration, bool isFirstSegment, bool isLastSegment) const +{ + if (isFirstSegment) + { + if (!isLastSegment) + { + double accelerationTime = std::fmin(AccelerationTime, segmentDuration); + double segmentTimeAtFullSpeed = segmentDuration - accelerationTime; + if (timePassedInSegment <= segmentTimeAtFullSpeed) + { + return timePassedInSegment * Speed; + } + else + { + double segmentAccelerationTime = timePassedInSegment - segmentTimeAtFullSpeed; + double segmentAccelerationDistance = AccelerationRate * accelerationTime; + double segmentDistanceAtFullSpeed = segmentTimeAtFullSpeed * Speed; + return (2.0 * segmentAccelerationDistance - segmentAccelerationTime * AccelerationRate) * 0.5 * segmentAccelerationTime + segmentDistanceAtFullSpeed; + } + } + + return timePassedInSegment * Speed; + } + + if (isLastSegment) + { + if (!isFirstSegment) + { + if (timePassedInSegment <= std::fmin(AccelerationTime, segmentDuration)) + return AccelerationRate * timePassedInSegment * 0.5 * timePassedInSegment; + else + return (timePassedInSegment - AccelerationTime) * Speed + AccelerationDistance; + } + + return timePassedInSegment * Speed; + } + + double accelerationTime = std::fmin(segmentDuration * 0.5, AccelerationTime); + if (timePassedInSegment <= segmentDuration - accelerationTime) + { + if (timePassedInSegment <= accelerationTime) + return AccelerationRate * timePassedInSegment * 0.5 * timePassedInSegment; + else + return (timePassedInSegment - AccelerationTime) * Speed + AccelerationDistance; + } + else + { + double segmentTimeSpentAccelerating = timePassedInSegment - (segmentDuration - accelerationTime); + return (segmentDuration - 2 * accelerationTime) * Speed + + AccelerationRate * accelerationTime * 0.5 * accelerationTime + + (2.0 * AccelerationRate * accelerationTime - segmentTimeSpentAccelerating * AccelerationRate) * 0.5 * segmentTimeSpentAccelerating; + } +} + +uint32 TransportTemplate::GetNextPauseWaypointTimestamp(uint32 time) const +{ + auto legItr = PathLegs.begin(); + while (legItr->StartTimestamp + legItr->Duration <= time) + { + ++legItr; + + if (legItr == PathLegs.end()) + return time; + } + + auto segmentItr = legItr->Segments.begin(); + for (; segmentItr != std::prev(legItr->Segments.end()); ++segmentItr) + if (time < segmentItr->SegmentEndArrivalTimestamp + segmentItr->Delay) + break; + + return segmentItr->SegmentEndArrivalTimestamp + segmentItr->Delay; +} TransportMgr::TransportMgr() = default; @@ -87,12 +221,14 @@ void TransportMgr::LoadTransportTemplates() // paths are generated per template, saves us from generating it again in case of instanced transports TransportTemplate& transport = _transportTemplates[entry]; - transport.entry = entry; - GeneratePath(goInfo, &transport); + + std::set<uint32> mapsUsed; + + GeneratePath(goInfo, &transport, &mapsUsed); // transports in instance are only on one map - if (transport.inInstance) - _instanceTransports[*transport.mapsUsed.begin()].insert(entry); + if (transport.InInstance) + _instanceTransports[*mapsUsed.begin()].insert(entry); ++count; } while (result->NextRow()); @@ -203,239 +339,206 @@ public: Movement::PointsArray& _points; }; -void TransportMgr::GeneratePath(GameObjectTemplate const* goInfo, TransportTemplate* transport) +static void InitializeLeg(TransportPathLeg* leg, std::vector<TransportPathEvent>* outEvents, std::vector<TaxiPathNodeEntry const*> const& pathPoints, std::vector<TaxiPathNodeEntry const*> const& pauses, + std::vector<TaxiPathNodeEntry const*> const& events, GameObjectTemplate const* goInfo, uint32& totalTime) { - uint32 pathId = goInfo->moTransport.taxiPathID; - TaxiPathNodeList const& path = sTaxiPathNodesByPath[pathId]; - std::vector<KeyFrame>& keyFrames = transport->keyFrames; - Movement::PointsArray splinePath, allPoints; - bool mapChange = false; - for (size_t i = 0; i < path.size(); ++i) - allPoints.push_back(G3D::Vector3(path[i]->Loc.X, path[i]->Loc.Y, path[i]->Loc.Z)); - - // Add extra points to allow derivative calculations for all path nodes - allPoints.insert(allPoints.begin(), allPoints.front().lerp(allPoints[1], -0.2f)); - allPoints.push_back(allPoints.back().lerp(allPoints[allPoints.size() - 2], -0.2f)); - allPoints.push_back(allPoints.back().lerp(allPoints[allPoints.size() - 2], -1.0f)); - - SplineRawInitializer initer(allPoints); - TransportSpline orientationSpline; - orientationSpline.init_spline_custom(initer); - orientationSpline.initLengths(); - - for (size_t i = 0; i < path.size(); ++i) + Movement::PointsArray splinePath; + std::transform(pathPoints.begin(), pathPoints.end(), std::back_inserter(splinePath), [](TaxiPathNodeEntry const* node) { return Movement::Vector3(node->Loc.X, node->Loc.Y, node->Loc.Z); }); + SplineRawInitializer initer(splinePath); + leg->Spline = std::make_unique<TransportSpline>(); + leg->Spline->set_steps_per_segment(20); + leg->Spline->init_spline_custom(initer); + leg->Spline->initLengths(); + + leg->Segments.resize(pauses.size() + 1); + + auto legTimeAccelDecel = [&](double dist) { - if (!mapChange) - { - TaxiPathNodeEntry const* node_i = path[i]; - if (i != path.size() - 1 && (node_i->Flags & TAXI_PATH_NODE_FLAG_TELEPORT || node_i->ContinentID != path[i + 1]->ContinentID)) - { - keyFrames.back().Teleport = true; - mapChange = true; - } - else - { - KeyFrame k(node_i); - G3D::Vector3 h; - orientationSpline.evaluate_derivative(i + 1, 0.0f, h); - k.InitialOrientation = Position::NormalizeOrientation(std::atan2(h.y, h.x) + float(M_PI)); - - keyFrames.push_back(k); - splinePath.push_back(G3D::Vector3(node_i->Loc.X, node_i->Loc.Y, node_i->Loc.Z)); - transport->mapsUsed.insert(k.Node->ContinentID); - } - } + double speed = double(goInfo->moTransport.moveSpeed); + double accel = double(goInfo->moTransport.accelRate); + double accelDist = 0.5 * speed * speed / accel; + if (accelDist >= dist * 0.5) + return uint32(std::sqrt(dist / accel) * 2000.0); else - mapChange = false; - } + return uint32((dist - (accelDist + accelDist)) / speed * 1000.0 + speed / accel * 2000.0); + }; - if (splinePath.size() >= 2) + auto legTimeAccel = [&](double dist) { - // Remove special catmull-rom spline points - if (!keyFrames.front().IsStopFrame() && !keyFrames.front().Node->ArrivalEventID && !keyFrames.front().Node->DepartureEventID) - { - splinePath.erase(splinePath.begin()); - keyFrames.erase(keyFrames.begin()); - } - if (!keyFrames.back().IsStopFrame() && !keyFrames.back().Node->ArrivalEventID && !keyFrames.back().Node->DepartureEventID) - { - splinePath.pop_back(); - keyFrames.pop_back(); - } - } - - ASSERT(!keyFrames.empty()); - - if (transport->mapsUsed.size() > 1) + double speed = double(goInfo->moTransport.moveSpeed); + double accel = double(goInfo->moTransport.accelRate); + double accelDist = 0.5 * speed * speed / accel; + if (accelDist >= dist) + return uint32(std::sqrt((dist + dist) / accel) * 1000.0); + else + return uint32(((dist - accelDist) / speed + speed / accel) * 1000.0); + }; + + // Init segments + size_t pauseItr = 0; + size_t eventItr = 0; + double splineLengthToPreviousNode = 0.0; + uint32 delaySum = 0; + if (!pauses.empty()) { - for (std::set<uint32>::const_iterator itr = transport->mapsUsed.begin(); itr != transport->mapsUsed.end(); ++itr) - ASSERT(!sMapStore.LookupEntry(*itr)->Instanceable()); + for (; pauseItr < pauses.size(); ++pauseItr) + { + auto pausePointItr = std::find(pathPoints.begin(), pathPoints.end(), pauses[pauseItr]); + if (*pausePointItr == pathPoints.back()) // last point is a "fake" spline point, its position can never be reached so transport cannot stop there + break; - transport->inInstance = false; - } - else - transport->inInstance = sMapStore.LookupEntry(*transport->mapsUsed.begin())->Instanceable(); + for (; eventItr < events.size(); ++eventItr) + { + auto eventPointItr = std::find(pathPoints.begin(), pathPoints.end(), events[eventItr]); + if (eventPointItr > pausePointItr) + break; - // last to first is always "teleport", even for closed paths - keyFrames.back().Teleport = true; + double length = leg->Spline->length(std::distance(pathPoints.begin(), eventPointItr)) - splineLengthToPreviousNode; - const float speed = float(goInfo->moTransport.moveSpeed); - const float accel = float(goInfo->moTransport.accelRate); - const float accel_dist = 0.5f * speed * speed / accel; + uint32 splineTime = 0; + if (pauseItr) + splineTime = legTimeAccelDecel(length); + else + splineTime = legTimeAccel(length); - transport->accelTime = speed / accel; - transport->accelDist = accel_dist; + if ((*eventPointItr)->ArrivalEventID) + { + TransportPathEvent& event = outEvents->emplace_back(); + event.Timestamp = totalTime + splineTime + leg->Duration; + event.EventId = (*eventPointItr)->ArrivalEventID; + } - int32 firstStop = -1; - int32 lastStop = -1; + if ((*eventPointItr)->DepartureEventID) + { + TransportPathEvent& event = outEvents->emplace_back(); + event.Timestamp = totalTime + splineTime + leg->Duration + (pausePointItr == eventPointItr ? (*eventPointItr)->Delay * IN_MILLISECONDS : 0); + event.EventId = (*eventPointItr)->DepartureEventID; + } + } - // first cell is arrived at by teleportation :S - keyFrames[0].DistFromPrev = 0; - keyFrames[0].Index = 1; - if (keyFrames[0].IsStopFrame()) - { - firstStop = 0; - lastStop = 0; + double splineLengthToCurrentNode = leg->Spline->length(std::distance(pathPoints.begin(), pausePointItr)); + double length = splineLengthToCurrentNode - splineLengthToPreviousNode; + uint32 movementTime = 0; + if (pauseItr) + movementTime = legTimeAccelDecel(length); + else + movementTime = legTimeAccel(length); + + leg->Duration += movementTime; + leg->Segments[pauseItr].SegmentEndArrivalTimestamp = leg->Duration + delaySum; + leg->Segments[pauseItr].Delay = (*pausePointItr)->Delay * IN_MILLISECONDS; + leg->Segments[pauseItr].DistanceFromLegStartAtEnd = splineLengthToCurrentNode; + delaySum += (*pausePointItr)->Delay * IN_MILLISECONDS; + splineLengthToPreviousNode = splineLengthToCurrentNode; + } } - // find the rest of the distances between key points - // Every path segment has its own spline - size_t start = 0; - for (size_t i = 1; i < keyFrames.size(); ++i) + // Process events happening after last pause + for (; eventItr < events.size(); ++eventItr) { - if (keyFrames[i - 1].Teleport || i + 1 == keyFrames.size()) - { - size_t extra = !keyFrames[i - 1].Teleport ? 1 : 0; - std::shared_ptr<TransportSpline> spline = std::make_shared<TransportSpline>(); - spline->init_spline(&splinePath[start], i - start + extra, Movement::SplineBase::ModeCatmullrom); - spline->initLengths(); - for (size_t j = start; j < i + extra; ++j) - { - keyFrames[j].Index = j - start + 1; - keyFrames[j].DistFromPrev = float(spline->length(j - start, j + 1 - start)); - if (j > 0) - keyFrames[j - 1].NextDistFromPrev = keyFrames[j].DistFromPrev; - keyFrames[j].Spline = spline; - } - - if (keyFrames[i - 1].Teleport) - { - keyFrames[i].Index = i - start + 1; - keyFrames[i].DistFromPrev = 0.0f; - keyFrames[i - 1].NextDistFromPrev = 0.0f; - keyFrames[i].Spline = spline; - } + auto eventPointItr = std::find(pathPoints.begin(), pathPoints.end(), events[eventItr]); + if (*eventPointItr == pathPoints.back()) // last point is a "fake" spline node, events cannot happen there + break; + + double length = leg->Spline->length(std::distance(pathPoints.begin(), eventPointItr)) - splineLengthToPreviousNode; + uint32 splineTime = 0; + if (pauseItr) + splineTime = legTimeAccel(length); + else + splineTime = uint32(length / double(goInfo->moTransport.moveSpeed) * 1000.0); - start = i; + if ((*eventPointItr)->ArrivalEventID) + { + TransportPathEvent& event = outEvents->emplace_back(); + event.Timestamp = totalTime + splineTime + leg->Duration; + event.EventId = (*eventPointItr)->ArrivalEventID; } - if (keyFrames[i].IsStopFrame()) + if ((*eventPointItr)->DepartureEventID) { - // remember first stop frame - if (firstStop == -1) - firstStop = i; - lastStop = i; + TransportPathEvent& event = outEvents->emplace_back(); + event.Timestamp = totalTime + splineTime + leg->Duration; + event.EventId = (*eventPointItr)->DepartureEventID; } } - keyFrames.back().NextDistFromPrev = keyFrames.front().DistFromPrev; + // Add segment after last pause + double length = leg->Spline->length() - splineLengthToPreviousNode; + uint32 splineTime = 0; + if (pauseItr) + splineTime = legTimeAccel(length); + else + splineTime = uint32(length / double(goInfo->moTransport.moveSpeed) * 1000.0); - if (firstStop == -1 || lastStop == -1) - firstStop = lastStop = 0; + leg->StartTimestamp = totalTime; + leg->Duration += splineTime + delaySum; + leg->Segments[pauseItr].SegmentEndArrivalTimestamp = leg->Duration; + leg->Segments[pauseItr].Delay = 0; + leg->Segments[pauseItr].DistanceFromLegStartAtEnd = leg->Spline->length(); + totalTime += leg->Segments[pauseItr].SegmentEndArrivalTimestamp + leg->Segments[pauseItr].Delay; - // at stopping keyframes, we define distSinceStop == 0, - // and distUntilStop is to the next stopping keyframe. - // this is required to properly handle cases of two stopping frames in a row (yes they do exist) - float tmpDist = 0.0f; - for (size_t i = 0; i < keyFrames.size(); ++i) - { - int32 j = (i + lastStop) % keyFrames.size(); - if (keyFrames[j].IsStopFrame() || j == lastStop) - tmpDist = 0.0f; - else - tmpDist += keyFrames[j].DistFromPrev; - keyFrames[j].DistSinceStop = tmpDist; - } + for (TransportPathSegment& segment : leg->Segments) + segment.SegmentEndArrivalTimestamp += leg->StartTimestamp; - tmpDist = 0.0f; - for (int32 i = int32(keyFrames.size()) - 1; i >= 0; i--) - { - int32 j = (i + firstStop) % keyFrames.size(); - tmpDist += keyFrames[(j + 1) % keyFrames.size()].DistFromPrev; - keyFrames[j].DistUntilStop = tmpDist; - if (keyFrames[j].IsStopFrame() || j == firstStop) - tmpDist = 0.0f; - } + if (leg->Segments.size() > pauseItr + 1) + leg->Segments.resize(pauseItr + 1); +} + +void TransportMgr::GeneratePath(GameObjectTemplate const* goInfo, TransportTemplate* transport, std::set<uint32>* mapsUsed) +{ + uint32 pathId = goInfo->moTransport.taxiPathID; + TaxiPathNodeList const& path = sTaxiPathNodesByPath[pathId]; - for (size_t i = 0; i < keyFrames.size(); ++i) + transport->Speed = double(goInfo->moTransport.moveSpeed); + transport->AccelerationRate = double(goInfo->moTransport.accelRate); + transport->AccelerationTime = transport->Speed / transport->AccelerationRate; + transport->AccelerationDistance = 0.5 * transport->Speed * transport->Speed / transport->AccelerationRate; + + std::vector<TaxiPathNodeEntry const*> pathPoints; + std::vector<TaxiPathNodeEntry const*> pauses; + std::vector<TaxiPathNodeEntry const*> events; + TransportPathLeg* leg = &transport->PathLegs.emplace_back(); + leg->MapId = path[0]->ContinentID; + bool prevNodeWasTeleport = false; + uint32 totalTime = 0; + for (TaxiPathNodeEntry const* node : path) { - float total_dist = keyFrames[i].DistSinceStop + keyFrames[i].DistUntilStop; - if (total_dist < 2 * accel_dist) // won't reach full speed - { - if (keyFrames[i].DistSinceStop < keyFrames[i].DistUntilStop) // is still accelerating - { - // calculate accel+brake time for this short segment - float segment_time = 2.0f * std::sqrt((keyFrames[i].DistUntilStop + keyFrames[i].DistSinceStop) / accel); - // substract acceleration time - keyFrames[i].TimeTo = segment_time - std::sqrt(2 * keyFrames[i].DistSinceStop / accel); - } - else // slowing down - keyFrames[i].TimeTo = std::sqrt(2 * keyFrames[i].DistUntilStop / accel); - } - else if (keyFrames[i].DistSinceStop < accel_dist) // still accelerating (but will reach full speed) + if (node->ContinentID != leg->MapId || prevNodeWasTeleport) { - // calculate accel + cruise + brake time for this long segment - float segment_time = (keyFrames[i].DistUntilStop + keyFrames[i].DistSinceStop) / speed + (speed / accel); - // substract acceleration time - keyFrames[i].TimeTo = segment_time - std::sqrt(2 * keyFrames[i].DistSinceStop / accel); + InitializeLeg(leg, &transport->Events, pathPoints, pauses, events, goInfo, totalTime); + + leg = &transport->PathLegs.emplace_back(); + leg->MapId = node->ContinentID; + pathPoints.clear(); + pauses.clear(); + events.clear(); } - else if (keyFrames[i].DistUntilStop < accel_dist) // already slowing down (but reached full speed) - keyFrames[i].TimeTo = std::sqrt(2 * keyFrames[i].DistUntilStop / accel); - else // at full speed - keyFrames[i].TimeTo = (keyFrames[i].DistUntilStop / speed) + (0.5f * speed / accel); - } - // calculate tFrom times from tTo times - float segmentTime = 0.0f; - for (size_t i = 0; i < keyFrames.size(); ++i) - { - int32 j = (i + lastStop) % keyFrames.size(); - if (keyFrames[j].IsStopFrame() || j == lastStop) - segmentTime = keyFrames[j].TimeTo; - keyFrames[j].TimeFrom = segmentTime - keyFrames[j].TimeTo; - } + prevNodeWasTeleport = (node->Flags & TAXI_PATH_NODE_FLAG_TELEPORT) != 0; + pathPoints.push_back(node); + if (node->Flags & TAXI_PATH_NODE_FLAG_STOP) + pauses.push_back(node); - // calculate path times - keyFrames[0].ArriveTime = 0; - float curPathTime = 0.0f; - if (keyFrames[0].IsStopFrame()) - { - curPathTime = float(keyFrames[0].Node->Delay); - keyFrames[0].DepartureTime = uint32(curPathTime * IN_MILLISECONDS); + if (node->ArrivalEventID || node->DepartureEventID) + events.push_back(node); + + mapsUsed->insert(node->ContinentID); } - for (size_t i = 1; i < keyFrames.size(); ++i) + if (!leg->Spline) + InitializeLeg(leg, &transport->Events, pathPoints, pauses, events, goInfo, totalTime); + + if (mapsUsed->size() > 1) { - curPathTime += keyFrames[i - 1].TimeTo; - if (keyFrames[i].IsStopFrame()) - { - keyFrames[i].ArriveTime = uint32(curPathTime * IN_MILLISECONDS); - keyFrames[i - 1].NextArriveTime = keyFrames[i].ArriveTime; - curPathTime += float(keyFrames[i].Node->Delay); - keyFrames[i].DepartureTime = uint32(curPathTime * IN_MILLISECONDS); - } - else - { - curPathTime -= keyFrames[i].TimeTo; - keyFrames[i].ArriveTime = uint32(curPathTime * IN_MILLISECONDS); - keyFrames[i - 1].NextArriveTime = keyFrames[i].ArriveTime; - keyFrames[i].DepartureTime = keyFrames[i].ArriveTime; - } - } + for (uint32 mapId : *mapsUsed) + ASSERT(!sMapStore.LookupEntry(mapId)->Instanceable()); - keyFrames.back().NextArriveTime = keyFrames.back().DepartureTime; + transport->InInstance = false; + } + else + transport->InInstance = sMapStore.LookupEntry(*mapsUsed->begin())->Instanceable(); - transport->pathTime = keyFrames.back().DepartureTime; + transport->TotalPathTime = totalTime; } void TransportMgr::AddPathNodeToTransport(uint32 transportEntry, uint32 timeSeg, TransportAnimationEntry const* node) @@ -477,16 +580,22 @@ Transport* TransportMgr::CreateTransport(uint32 entry, ObjectGuid::LowType guid return nullptr; } + Optional<Position> startingPosition = tInfo->ComputePosition(0, nullptr, nullptr); + if (!startingPosition) + { + TC_LOG_ERROR("sql.sql", "Transport %u will not be loaded, failed to compute starting position", entry); + return nullptr; + } + // create transport... Transport* trans = new Transport(); // ...at first waypoint - TaxiPathNodeEntry const* startNode = tInfo->keyFrames.begin()->Node; - uint32 mapId = startNode->ContinentID; - float x = startNode->Loc.X; - float y = startNode->Loc.Y; - float z = startNode->Loc.Z; - float o = tInfo->keyFrames.begin()->InitialOrientation; + uint32 mapId = tInfo->PathLegs.front().MapId; + float x = startingPosition->GetPositionX(); + float y = startingPosition->GetPositionY(); + float z = startingPosition->GetPositionZ(); + float o = startingPosition->GetOrientation(); // initialize the gameobject base ObjectGuid::LowType guidLow = guid ? guid : ASSERT_NOTNULL(map)->GenerateLowGuid<HighGuid::Transport>(); @@ -500,7 +609,7 @@ Transport* TransportMgr::CreateTransport(uint32 entry, ObjectGuid::LowType guid if (MapEntry const* mapEntry = sMapStore.LookupEntry(mapId)) { - if (mapEntry->Instanceable() != tInfo->inInstance) + if (mapEntry->Instanceable() != tInfo->InInstance) { TC_LOG_ERROR("entities.transport", "Transport %u (name: %s) attempted creation in instance map (id: %u) but it is not an instanced transport!", entry, trans->GetName().c_str(), mapId); delete trans; @@ -525,7 +634,7 @@ void TransportMgr::SpawnContinentTransports() uint32 count = 0; for (auto itr = _transportSpawns.begin(); itr != _transportSpawns.end(); ++itr) - if (!ASSERT_NOTNULL(GetTransportTemplate(itr->second.TransportGameObjectId))->inInstance) + if (!ASSERT_NOTNULL(GetTransportTemplate(itr->second.TransportGameObjectId))->InInstance) if (CreateTransport(itr->second.TransportGameObjectId, itr->second.SpawnId, nullptr, itr->second.PhaseUseFlags, itr->second.PhaseId, itr->second.PhaseGroup)) ++count; diff --git a/src/server/game/Maps/TransportMgr.h b/src/server/game/Maps/TransportMgr.h index 92ce809faee..66f6d59e35c 100644 --- a/src/server/game/Maps/TransportMgr.h +++ b/src/server/game/Maps/TransportMgr.h @@ -19,11 +19,12 @@ #define TRANSPORTMGR_H #include "ObjectGuid.h" +#include "Optional.h" +#include "Position.h" #include <map> #include <memory> #include <unordered_map> -struct KeyFrame; struct GameObjectTemplate; struct TaxiPathNodeEntry; struct TransportAnimationEntry; @@ -38,49 +39,69 @@ namespace Movement } using TransportSpline = Movement::Spline<double>; -using KeyFrameVec = std::vector<KeyFrame>; -struct KeyFrame +enum class TransportMovementState : uint8 { - explicit KeyFrame(TaxiPathNodeEntry const* node) : Index(0), Node(node), InitialOrientation(0.0f), - DistSinceStop(-1.0f), DistUntilStop(-1.0f), DistFromPrev(-1.0f), TimeFrom(0.0f), TimeTo(0.0f), - Teleport(false), ArriveTime(0), DepartureTime(0), Spline(nullptr), NextDistFromPrev(0.0f), NextArriveTime(0) - { - } - - uint32 Index; - TaxiPathNodeEntry const* Node; - float InitialOrientation; - float DistSinceStop; - float DistUntilStop; - float DistFromPrev; - float TimeFrom; - float TimeTo; - bool Teleport; - uint32 ArriveTime; - uint32 DepartureTime; - std::shared_ptr<TransportSpline> Spline; - - // Data needed for next frame - float NextDistFromPrev; - uint32 NextArriveTime; - - bool IsTeleportFrame() const { return Teleport; } - bool IsStopFrame() const; + Moving, + WaitingOnPauseWaypoint +}; + +// Represents a segment within path leg between stops +struct TransportPathSegment +{ + uint32 SegmentEndArrivalTimestamp = 0; + uint32 Delay = 0; + double DistanceFromLegStartAtEnd = 0.0; +}; + +struct TransportPathEvent +{ + uint32 Timestamp = 0; + uint32 EventId = 0; +}; + +// Represents a contignuous part of transport path (without map changes or teleports) +struct TransportPathLeg +{ + TransportPathLeg(); + ~TransportPathLeg(); + + TransportPathLeg(TransportPathLeg const&) = delete; + TransportPathLeg(TransportPathLeg&&) noexcept; + TransportPathLeg& operator=(TransportPathLeg const&) = delete; + TransportPathLeg& operator=(TransportPathLeg&&) noexcept; + + uint32 MapId = 0; + std::unique_ptr<TransportSpline> Spline; + uint32 StartTimestamp = 0; + uint32 Duration = 0; + std::vector<TransportPathSegment> Segments; }; struct TransportTemplate { - TransportTemplate() : inInstance(false), pathTime(0), accelTime(0.0f), accelDist(0.0f), entry(0) { } + TransportTemplate(); ~TransportTemplate(); - std::set<uint32> mapsUsed; - bool inInstance; - uint32 pathTime; - KeyFrameVec keyFrames; - float accelTime; - float accelDist; - uint32 entry; + TransportTemplate(TransportTemplate const&) = delete; + TransportTemplate(TransportTemplate&&) noexcept; + TransportTemplate& operator=(TransportTemplate const&) = delete; + TransportTemplate& operator=(TransportTemplate&&) noexcept; + + uint32 TotalPathTime = 0; + double Speed = 0.0; + double AccelerationRate = 0.0; + double AccelerationTime = 0.0; + double AccelerationDistance = 0.0; + std::vector<TransportPathLeg> PathLegs; + std::vector<TransportPathEvent> Events; + + Optional<Position> ComputePosition(uint32 time, TransportMovementState* moveState, size_t* legIndex) const; + uint32 GetNextPauseWaypointTimestamp(uint32 time) const; + + double CalculateDistanceMoved(double timePassedInSegment, double segmentDuration, bool isFirstSegment, bool isLastSegment) const; + + bool InInstance = false; }; struct TC_GAME_API TransportAnimation @@ -142,7 +163,7 @@ class TC_GAME_API TransportMgr TransportMgr& operator=(TransportMgr const&) = delete; // Generates and precaches a path for transport to avoid generation each time transport instance is created - void GeneratePath(GameObjectTemplate const* goInfo, TransportTemplate* transport); + void GeneratePath(GameObjectTemplate const* goInfo, TransportTemplate* transport, std::set<uint32>* mapsUsed); void AddPathNodeToTransport(uint32 transportEntry, uint32 timeSeg, TransportAnimationEntry const* node); |
