42 time %= TotalPathTime;
51 auto segmentItr = leg->
Segments.begin();
52 double distanceMoved = 0.0;
53 bool isOnPause =
false;
54 for (; segmentItr != std::prev(leg->
Segments.end()); ++segmentItr)
56 if (time < segmentItr->SegmentEndArrivalTimestamp)
59 distanceMoved = segmentItr->DistanceFromLegStartAtEnd;
60 if (time < segmentItr->SegmentEndArrivalTimestamp + segmentItr->Delay)
66 prevSegmentTime = segmentItr->SegmentEndArrivalTimestamp + segmentItr->Delay;
70 distanceMoved += CalculateDistanceMoved(
71 double(time - prevSegmentTime) * 0.001,
72 double(segmentItr->SegmentEndArrivalTimestamp - prevSegmentTime) * 0.001,
74 segmentItr == std::prev(leg->
Segments.end()));
77 float splinePointProgress;
78 leg->
Spline->computeIndex(std::fmin(distanceMoved / leg->
Spline->length(), 1.0), splineIndex, splinePointProgress);
80 G3D::Vector3 pos, dir;
81 leg->
Spline->evaluate_percent(splineIndex, splinePointProgress, pos);
82 leg->
Spline->evaluate_derivative(splineIndex, splinePointProgress, dir);
88 *legIndex = std::distance(PathLegs.data(), leg);
90 return Position(pos.x, pos.y, pos.z, std::atan2(dir.y, dir.x) +
float(
M_PI));
96 while (legItr->StartTimestamp + legItr->Duration <= time)
113 auto segmentItr = leg->
Segments.begin();
114 for (; segmentItr != std::prev(leg->
Segments.end()); ++segmentItr)
115 if (time < segmentItr->SegmentEndArrivalTimestamp + segmentItr->Delay)
118 return segmentItr->SegmentEndArrivalTimestamp + segmentItr->Delay;
128 double segmentTimeAtFullSpeed = segmentDuration - accelerationTime;
129 if (timePassedInSegment <= segmentTimeAtFullSpeed)
131 return timePassedInSegment *
Speed;
135 double segmentAccelerationTime = timePassedInSegment - segmentTimeAtFullSpeed;
137 double segmentDistanceAtFullSpeed = segmentTimeAtFullSpeed *
Speed;
138 return (2.0 * segmentAccelerationDistance - segmentAccelerationTime *
AccelerationRate) * 0.5 * segmentAccelerationTime + segmentDistanceAtFullSpeed;
142 return timePassedInSegment *
Speed;
155 return timePassedInSegment *
Speed;
158 double accelerationTime = std::fmin(segmentDuration * 0.5,
AccelerationTime);
159 if (timePassedInSegment <= segmentDuration - accelerationTime)
161 if (timePassedInSegment <= accelerationTime)
168 double segmentTimeSpentAccelerating = timePassedInSegment - (segmentDuration - accelerationTime);
169 return (segmentDuration - 2 * accelerationTime) *
Speed
198 QueryResult result =
WorldDatabase.Query(
"SELECT entry FROM gameobject_template WHERE type = 15 ORDER BY entry ASC");
202 TC_LOG_INFO(
"server.loading",
">> Loaded 0 transport templates. DB table `gameobject_template` has no transports!");
210 Field* fields = result->Fetch();
213 if (goInfo ==
nullptr)
215 TC_LOG_ERROR(
"sql.sql",
"Transport {} has no associated GameObjectTemplate from `gameobject_template` , skipped.", entry);
221 TC_LOG_ERROR(
"sql.sql",
"Transport {} (name: {}) has an invalid path specified in `gameobject_template`.`Data0` ({}) field, skipped.", entry, goInfo->
name, goInfo->
moTransport.
taxiPathID);
234 }
while (result->NextRow());
262 Field* fields = result->Fetch();
270 if (!transportTemplate)
272 TC_LOG_ERROR(
"sql.sql",
"Table `transports` have transport (GUID: {} Entry: {}) with unknown gameobject `entry` set, skipped.", guid, entry);
278 TC_LOG_ERROR(
"sql.sql",
"Table `transports` have transport (GUID: {} Entry: {}) with unknown `phaseUseFlags` set, removed unknown value.", guid, entry);
284 TC_LOG_ERROR(
"sql.sql",
"Table `transports` have transport (GUID: {} Entry: {}) has both `phaseUseFlags` PHASE_USE_FLAGS_ALWAYS_VISIBLE and PHASE_USE_FLAGS_INVERSE,"
285 " removing PHASE_USE_FLAGS_INVERSE.", guid, entry);
286 phaseUseFlags &= ~PHASE_USE_FLAGS_INVERSE;
289 if (phaseGroupId && phaseId)
291 TC_LOG_ERROR(
"sql.sql",
"Table `transports` have transport (GUID: {} Entry: {}) with both `phaseid` and `phasegroup` set, `phasegroup` set to 0", guid, entry);
299 TC_LOG_ERROR(
"sql.sql",
"Table `transports` have transport (GUID: {} Entry: {}) with `phaseid` {} does not exist, set to 0", guid, entry, phaseId);
308 TC_LOG_ERROR(
"sql.sql",
"Table `transports` have transport (GUID: {} Entry: {}) with `phaseGroup` {} does not exist, set to 0", guid, entry, phaseGroupId);
323 }
while (result->NextRow());
334 void operator()(
uint8& mode,
bool& cyclic, std::vector<Movement::Vector3>& points,
int& lo,
int& hi)
const
339 std::ranges::transform(
_points, points.begin(), [](
TaxiPathNodeEntry const* node) { return Movement::Vector3(node->Loc.X, node->Loc.Y, node->Loc.Z); });
341 hi = points.size() - 2;
344 std::vector<TaxiPathNodeEntry const*>
const&
_points;
347static void InitializeLeg(
TransportPathLeg* leg, std::vector<TransportPathEvent>* outEvents, std::vector<TaxiPathNodeEntry const*>
const& pathPoints, std::vector<TaxiPathNodeEntry const*>
const& pauses,
350 leg->
Spline = std::make_unique<TransportSpline>();
351 leg->
Spline->set_steps_per_segment(20);
353 leg->
Spline->initLengths();
355 leg->
Segments.resize(pauses.size() + 1);
357 auto legTimeAccelDecel = [&](
double dist)
361 double accelDist = 0.5 * speed * speed / accel;
362 if (accelDist >= dist * 0.5)
363 return uint32(std::sqrt(dist / accel) * 2000.0);
365 return uint32((dist - (accelDist + accelDist)) / speed * 1000.0 + speed / accel * 2000.0);
368 auto legTimeAccel = [&](
double dist)
372 double accelDist = 0.5 * speed * speed / accel;
373 if (accelDist >= dist)
374 return uint32(std::sqrt((dist + dist) / accel) * 1000.0);
376 return uint32(((dist - accelDist) / speed + speed / accel) * 1000.0);
382 double splineLengthToPreviousNode = 0.0;
386 for (; pauseItr < pauses.size(); ++pauseItr)
388 auto pausePointItr = std::find(pathPoints.begin(), pathPoints.end(), pauses[pauseItr]);
389 if (*pausePointItr == pathPoints.back())
392 for (; eventItr < events.size(); ++eventItr)
394 auto eventPointItr = std::find(pathPoints.begin(), pathPoints.end(), events[eventItr]);
395 if (eventPointItr > pausePointItr)
398 double length = leg->
Spline->length(std::distance(pathPoints.begin(), eventPointItr)) - splineLengthToPreviousNode;
402 splineTime = legTimeAccelDecel(length);
404 splineTime = legTimeAccel(length);
406 if ((*eventPointItr)->ArrivalEventID)
410 event.EventId = (*eventPointItr)->ArrivalEventID;
413 if ((*eventPointItr)->DepartureEventID)
417 event.EventId = (*eventPointItr)->DepartureEventID;
421 double splineLengthToCurrentNode = leg->
Spline->length(std::distance(pathPoints.begin(), pausePointItr));
422 double length = splineLengthToCurrentNode - splineLengthToPreviousNode;
425 movementTime = legTimeAccelDecel(length);
427 movementTime = legTimeAccel(length);
430 leg->
Segments[pauseItr].SegmentEndArrivalTimestamp = leg->
Duration + delaySum;
432 leg->
Segments[pauseItr].DistanceFromLegStartAtEnd = splineLengthToCurrentNode;
434 splineLengthToPreviousNode = splineLengthToCurrentNode;
439 for (; eventItr < events.size(); ++eventItr)
441 auto eventPointItr = std::find(pathPoints.begin(), pathPoints.end(), events[eventItr]);
442 if (*eventPointItr == pathPoints.back())
445 double length = leg->
Spline->length(std::distance(pathPoints.begin(), eventPointItr)) - splineLengthToPreviousNode;
448 splineTime = legTimeAccel(length);
452 if ((*eventPointItr)->ArrivalEventID)
456 event.EventId = (*eventPointItr)->ArrivalEventID;
459 if ((*eventPointItr)->DepartureEventID)
463 event.EventId = (*eventPointItr)->DepartureEventID;
468 double length = leg->
Spline->length() - splineLengthToPreviousNode;
471 splineTime = legTimeAccel(length);
476 leg->
Duration += splineTime + delaySum;
479 leg->
Segments[pauseItr].DistanceFromLegStartAtEnd = leg->
Spline->length();
480 totalTime += leg->
Segments[pauseItr].SegmentEndArrivalTimestamp + leg->
Segments[pauseItr].Delay;
485 if (leg->
Segments.size() > pauseItr + 1)
499 std::vector<TaxiPathNodeEntry const*> pathPoints;
500 std::vector<TaxiPathNodeEntry const*> pauses;
501 std::vector<TaxiPathNodeEntry const*> events;
503 leg->
MapId = path[0]->ContinentID;
504 bool prevNodeWasTeleport =
false;
508 if (node->ContinentID != leg->
MapId || prevNodeWasTeleport)
510 if (prevNodeWasTeleport && !pathPoints.empty())
511 pathPoints.push_back(pathPoints.back());
515 leg = &transport->
PathLegs.emplace_back();
516 leg->
MapId = node->ContinentID;
524 pauses.push_back(node);
526 if (node->ArrivalEventID || node->DepartureEventID)
527 events.push_back(node);
529 pathPoints.push_back(node);
530 transport->
MapIds.insert(node->ContinentID);
536 if (transport->
MapIds.size() > 1)
549 animNode.
Path[timeSeg] = node;
566 entry =
instance->GetGameObjectEntry(0, entry);
574 TC_LOG_ERROR(
"sql.sql",
"Transport {} will not be loaded, `transport_template` missing", entry);
580 TC_LOG_ERROR(
"entities.transport",
"Transport {} attempted creation on map it has no path for {}!", entry, map->
GetId());
585 if (!startingPosition)
587 TC_LOG_ERROR(
"sql.sql",
"Transport {} will not be loaded, failed to compute starting position", entry);
595 float x = startingPosition->GetPositionX();
596 float y = startingPosition->GetPositionY();
597 float z = startingPosition->GetPositionZ();
598 float o = startingPosition->GetOrientation();
602 if (!trans->
Create(guidLow, entry, x, y, z, o))
630 CreateTransport(transport->TransportGameObjectId, map, transport->SpawnId, transport->PhaseUseFlags, transport->PhaseId, transport->PhaseGroup);
653 auto itr =
Path.lower_bound(time);
654 if (itr !=
Path.begin())
655 return std::prev(itr)->second;
657 return Path.rbegin()->second;
667 return std::prev(itr)->second;
677 auto itr =
Path.lower_bound(time);
678 if (itr !=
Path.end())
681 return Path.begin()->second;
DB2Storage< PhaseEntry > sPhaseStore("Phase.db2", &PhaseLoadInfo::Instance)
DB2Storage< MapEntry > sMapStore("Map.db2", &MapLoadInfo::Instance)
TaxiPathNodesByPath sTaxiPathNodesByPath
DB2Storage< TransportRotationEntry > sTransportRotationStore("TransportRotation.db2", &TransportRotationLoadInfo::Instance)
DB2Storage< TransportAnimationEntry > sTransportAnimationStore("TransportAnimation.db2", &TransportAnimationLoadInfo::Instance)
std::vector< TaxiPathNodeEntry const * > TaxiPathNodeList
@ TAXI_PATH_NODE_FLAG_TELEPORT
@ TAXI_PATH_NODE_FLAG_STOP
@ PHASE_USE_FLAGS_ALWAYS_VISIBLE
@ PHASE_USE_FLAGS_INVERSE
std::shared_ptr< ResultSet > QueryResult
DatabaseWorkerPool< WorldDatabaseConnection > WorldDatabase
Accessor to the world database.
#define TC_LOG_ERROR(filterType__, message__,...)
#define TC_LOG_INFO(filterType__, message__,...)
std::optional< T > Optional
Optional helper class to wrap optional values within.
uint32 GetMSTimeDiffToNow(uint32 oldMSTime)
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)
Class used to access individual fields of database query result.
uint64 GetUInt64() const noexcept
uint32 GetUInt32() const noexcept
uint8 GetUInt8() const noexcept
ObjectGuid::LowType GenerateLowGuid()
InstanceMap * ToInstanceMap()
static void InitDbPhaseShift(PhaseShift &phaseShift, uint8 phaseUseFlags, uint16 phaseId, uint32 phaseGroupId)
std::vector< TaxiPathNodeEntry const * > const & _points
SplineRawInitializer(std::vector< TaxiPathNodeEntry const * > const &points)
void operator()(uint8 &mode, bool &cyclic, std::vector< Movement::Vector3 > &points, int &lo, int &hi) const
std::unordered_map< ObjectGuid::LowType, TransportSpawn > _transportSpawns
void LoadTransportTemplates()
void CreateTransportsForMap(Map *map)
TransportAnimation const * GetTransportAnimInfo(uint32 entry) const
Transport * CreateTransport(uint32 entry, Map *map, ObjectGuid::LowType guid=0, uint8 phaseUseFlags=0, uint32 phaseId=0, uint32 phaseGroupId=0)
std::unordered_map< uint32, std::set< TransportSpawn * > > _transportsByMap
void LoadTransportAnimationAndRotation()
void LoadTransportSpawns()
static TransportMgr * instance()
TransportTemplate const * GetTransportTemplate(uint32 entry) const
void AddPathNodeToTransport(uint32 transportEntry, uint32 timeSeg, TransportAnimationEntry const *node)
void AddPathRotationToTransport(uint32 transportEntry, uint32 timeSeg, TransportRotationEntry const *node)
std::map< uint32, TransportAnimation > _transportAnimations
std::unordered_map< uint32, TransportTemplate > _transportTemplates
TransportSpawn const * GetTransportSpawn(ObjectGuid::LowType spawnId) const
void GeneratePath(GameObjectTemplate const *goInfo, TransportTemplate *transport)
bool Create(ObjectGuid::LowType guidlow, uint32 entry, float x, float y, float z, float ang)
PhaseShift & GetPhaseShift()
ZoneScript * m_zoneScript
virtual void SetMap(Map *map)
auto MapGetValuePtr(M &map, typename M::key_type const &key)
struct GameObjectTemplate::@197::@214 moTransport
TransportRotationEntry const * GetNextAnimRotation(uint32 time) const
TransportAnimationEntry const * GetNextAnimNode(uint32 time) const
TransportAnimationEntry const * GetPrevAnimNode(uint32 time) const
TransportRotationEntry const * GetPrevAnimRotation(uint32 time) const
std::map< uint32, TransportAnimationEntry const * > Path
std::map< uint32, TransportRotationEntry const * > Rotations
std::vector< TransportPathSegment > Segments
std::unique_ptr< TransportSpline > Spline
ObjectGuid::LowType SpawnId
uint32 TransportGameObjectId
uint32 GetNextPauseWaypointTimestamp(uint32 time) const
Optional< Position > ComputePosition(uint32 time, TransportMovementState *moveState, size_t *legIndex) const
std::set< uint32 > MapIds
std::vector< TransportPathEvent > Events
TransportPathLeg const * GetLegForTime(uint32 time) const
double AccelerationDistance
std::vector< TransportPathLeg > PathLegs
double CalculateDistanceMoved(double timePassedInSegment, double segmentDuration, bool isFirstSegment, bool isLastSegment) const