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/Entities | |
| 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/Entities')
| -rw-r--r-- | src/server/game/Entities/Object/Updates/ViewerDependentValues.h | 14 | ||||
| -rw-r--r-- | src/server/game/Entities/Transport/Transport.cpp | 276 | ||||
| -rw-r--r-- | src/server/game/Entities/Transport/Transport.h | 28 |
3 files changed, 106 insertions, 212 deletions
diff --git a/src/server/game/Entities/Object/Updates/ViewerDependentValues.h b/src/server/game/Entities/Object/Updates/ViewerDependentValues.h index 755006262bd..564b31a9b86 100644 --- a/src/server/game/Entities/Object/Updates/ViewerDependentValues.h +++ b/src/server/game/Entities/Object/Updates/ViewerDependentValues.h @@ -26,7 +26,6 @@ #include "Player.h" #include "SpellInfo.h" #include "SpellMgr.h" -#include "Transport.h" #include "World.h" #include "WorldSession.h" @@ -86,21 +85,12 @@ public: dynFlags |= GO_DYNFLAG_LO_SPARKLE | GO_DYNFLAG_LO_HIGHLIGHT; break; case GAMEOBJECT_TYPE_TRANSPORT: + case GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT: { dynFlags = dynamicFlags & 0xFFFF; pathProgress = dynamicFlags >> 16; break; } - case GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT: - { - Transport const* transport = gameObject->ToTransport(); - if (uint32 transportPeriod = transport->GetTransportPeriod()) - { - float timer = float(transport->GetTimer() % transportPeriod); - pathProgress = uint16(timer / float(transportPeriod) * 65535.0f); - } - break; - } case GAMEOBJECT_TYPE_CAPTURE_POINT: if (!gameObject->CanInteractWithCapturePoint(receiver)) dynFlags |= GO_DYNFLAG_LO_NO_INTERACT; @@ -111,7 +101,7 @@ public: break; } - dynamicFlags = (pathProgress << 16) | dynFlags; + dynamicFlags = (uint32(pathProgress) << 16) | uint32(dynFlags); } return dynamicFlags; diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp index 926ead58655..906fd3793a0 100644 --- a/src/server/game/Entities/Transport/Transport.cpp +++ b/src/server/game/Entities/Transport/Transport.cpp @@ -19,8 +19,10 @@ #include "Cell.h" #include "CellImpl.h" #include "Common.h" +#include "DB2Stores.h" #include "GameEventSender.h" #include "GameObjectAI.h" +#include "GameTime.h" #include "Log.h" #include "MapManager.h" #include "ObjectMgr.h" @@ -31,6 +33,7 @@ #include "Totem.h" #include "UpdateData.h" #include "Vehicle.h" +#include <boost/dynamic_bitset.hpp> #include <G3D/Vector3.h> #include <sstream> @@ -90,9 +93,8 @@ void TransportBase::UpdatePassengerPosition(Map* map, WorldObject* passenger, fl } Transport::Transport() : GameObject(), - _transportInfo(nullptr), _pathProgress(0), _isMoving(true), _pendingStop(false), - _triggeredArrivalEvent(false), _triggeredDepartureEvent(false), - _passengerTeleportItr(_passengers.begin()), _delayedAddModel(false), _delayedTeleport(false) + _transportInfo(nullptr), _movementState(TransportMovementState::Moving), _eventsToTrigger(std::make_unique<boost::dynamic_bitset<uint8>>()), + _currentPathLeg(0), _pathProgress(0), _delayedAddModel(false) { m_updateFlag.ServerTime = true; m_updateFlag.Stationary = true; @@ -136,12 +138,7 @@ bool Transport::Create(ObjectGuid::LowType guidlow, uint32 entry, uint32 mapid, } _transportInfo = tInfo; - - // initialize waypoints - _nextFrame = tInfo->keyFrames.begin(); - _currentFrame = _nextFrame++; - _triggeredArrivalEvent = false; - _triggeredDepartureEvent = false; + _eventsToTrigger->resize(tInfo->Events.size(), true); if (GameObjectOverride const* goOverride = GetGameObjectOverride()) { @@ -149,14 +146,16 @@ bool Transport::Create(ObjectGuid::LowType guidlow, uint32 entry, uint32 mapid, ReplaceAllFlags(GameObjectFlags(goOverride->Flags)); } - _pathProgress = 0; + _pathProgress = !goinfo->moTransport.allowstopping ? getMSTime() /*might be called before world update loop begins, don't use GameTime*/ % tInfo->TotalPathTime : 0; + SetPathProgressForClient(float(_pathProgress) / float(tInfo->TotalPathTime)); SetObjectScale(goinfo->size); - SetPeriod(tInfo->pathTime); + SetPeriod(tInfo->TotalPathTime); SetEntry(goinfo->entry); SetDisplayId(goinfo->displayId); SetGoState(!goinfo->moTransport.allowstopping ? GO_STATE_READY : GO_STATE_ACTIVE); SetGoType(GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT); SetGoAnimProgress(animprogress); + SetUpdateFieldValue(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::SpawnTrackingStateAnimID), sDB2Manager.GetEmptyAnimStateID()); SetName(goinfo->name); SetLocalRotation(0.0f, 0.0f, 0.0f, 1.0f); SetParentRotation(QuaternionData()); @@ -179,77 +178,93 @@ void Transport::CleanupsBeforeDelete(bool finalCleanup /*= true*/) void Transport::Update(uint32 diff) { - uint32 const positionUpdateDelay = 200; + constexpr Milliseconds positionUpdateDelay = 200ms; if (AI()) AI()->UpdateAI(diff); else if (!AIM_Initialize()) TC_LOG_ERROR("entities.transport", "Could not initialize GameObjectAI for Transport"); - if (GetKeyFrames().size() <= 1) - return; + sScriptMgr->OnTransportUpdate(this, diff); + + _positionChangeTimer.Update(diff); - if (IsMoving() || !_pendingStop) + uint32 cycleId = _pathProgress / GetTransportPeriod(); + if (!GetGOInfo()->moTransport.allowstopping) + _pathProgress = GameTime::GetGameTimeMS(); + else if (!_requestStopTimestamp || _requestStopTimestamp > _pathProgress + diff) _pathProgress += diff; + else + _pathProgress = *_requestStopTimestamp; + + if (_pathProgress / GetTransportPeriod() != cycleId) + { + // reset cycle + _eventsToTrigger->set(); + } + + SetPathProgressForClient(float(_pathProgress) / float(GetTransportPeriod())); uint32 timer = _pathProgress % GetTransportPeriod(); - bool justStopped = false; - // Set current waypoint - // Desired outcome: _currentFrame->DepartureTime < timer < _nextFrame->ArriveTime - // ... arrive | ... delay ... | departure - // event / event / - for (;;) + size_t eventToTriggerIndex = _eventsToTrigger->find_first(); + if (eventToTriggerIndex != boost::dynamic_bitset<uint8>::npos) { - if (timer >= _currentFrame->ArriveTime) + while (eventToTriggerIndex < _transportInfo->Events.size() && _transportInfo->Events[eventToTriggerIndex].Timestamp < timer) { - if (!_triggeredArrivalEvent) - { - DoEventIfAny(*_currentFrame, false); - _triggeredArrivalEvent = true; - } + GameEvents::Trigger(_transportInfo->Events[eventToTriggerIndex].EventId, this, this); + _eventsToTrigger->set(eventToTriggerIndex, false); + ++eventToTriggerIndex; + } + } + + TransportMovementState moveState; + size_t legIndex; + if (Optional<Position> newPosition = _transportInfo->ComputePosition(timer, &moveState, &legIndex)) + { + bool justStopped = _movementState == TransportMovementState::Moving && moveState != TransportMovementState::Moving; + _movementState = moveState; - if (timer < _currentFrame->DepartureTime) + if (justStopped) + { + if (_requestStopTimestamp && GetGoState() != GO_STATE_READY) { - justStopped = IsMoving(); - SetMoving(false); - if (_pendingStop && GetGoState() != GO_STATE_READY) - { - SetGoState(GO_STATE_READY); - _pathProgress = (_pathProgress / GetTransportPeriod()); - _pathProgress *= GetTransportPeriod(); - _pathProgress += _currentFrame->ArriveTime; - } - break; // its a stop frame and we are waiting + SetGoState(GO_STATE_READY); + SetDynamicFlag(GO_DYNFLAG_LO_STOPPED); } } - if (timer >= _currentFrame->DepartureTime && !_triggeredDepartureEvent) + if (legIndex != _currentPathLeg) { - DoEventIfAny(*_currentFrame, true); // departure event - _triggeredDepartureEvent = true; + _currentPathLeg = legIndex; + TeleportTransport(_transportInfo->PathLegs[legIndex].MapId, newPosition->GetPositionX(), newPosition->GetPositionY(), newPosition->GetPositionZ(), newPosition->GetOrientation()); + return; } - // not waiting anymore - SetMoving(true); - - // Enable movement - if (GetGOInfo()->moTransport.allowstopping) - SetGoState(GO_STATE_ACTIVE); - - if (timer >= _currentFrame->DepartureTime && timer < _currentFrame->NextArriveTime) - break; // found current waypoint - - MoveToNextWaypoint(); - - sScriptMgr->OnRelocate(this, _currentFrame->Node->NodeIndex, _currentFrame->Node->ContinentID, _currentFrame->Node->Loc.X, _currentFrame->Node->Loc.Y, _currentFrame->Node->Loc.Z); - - TC_LOG_DEBUG("entities.transport", "Transport %u (%s) moved to node %u %u %f %f %f", GetEntry(), GetName().c_str(), _currentFrame->Node->NodeIndex, _currentFrame->Node->ContinentID, _currentFrame->Node->Loc.X, _currentFrame->Node->Loc.Y, _currentFrame->Node->Loc.Z); - - // Departure event - if (_currentFrame->IsTeleportFrame()) - if (TeleportTransport(_nextFrame->Node->ContinentID, _nextFrame->Node->Loc.X, _nextFrame->Node->Loc.Y, _nextFrame->Node->Loc.Z, _nextFrame->InitialOrientation)) - return; // Update more in new map thread + // set position + if (_positionChangeTimer.Passed()) + { + _positionChangeTimer.Reset(positionUpdateDelay); + if (_movementState == TransportMovementState::Moving || justStopped) + UpdatePosition(newPosition->GetPositionX(), newPosition->GetPositionY(), newPosition->GetPositionZ(), newPosition->GetOrientation()); + else + { + /* There are four possible scenarios that trigger loading/unloading passengers: + 1. transport moves from inactive to active grid + 2. the grid that transport is currently in becomes active + 3. transport moves from active to inactive grid + 4. the grid that transport is currently in unloads + */ + bool gridActive = GetMap()->IsGridLoaded(GetPositionX(), GetPositionY()); + + if (_staticPassengers.empty() && gridActive) // 2. + LoadStaticPassengers(); + else if (!_staticPassengers.empty() && !gridActive) + // 4. - if transports stopped on grid edge, some passengers can remain in active grids + // unload all static passengers otherwise passengers won't load correctly when the grid that transport is currently in becomes active + UnloadStaticPassengers(); + } + } } // Add model to map after we are fully done with moving maps @@ -259,49 +274,10 @@ void Transport::Update(uint32 diff) if (m_model) GetMap()->InsertGameObjectModel(*m_model); } - - // Set position - _positionChangeTimer.Update(diff); - if (_positionChangeTimer.Passed()) - { - _positionChangeTimer.Reset(positionUpdateDelay); - if (IsMoving()) - { - float t = !justStopped ? CalculateSegmentPos(float(timer) * 0.001f) : 1.0f; - G3D::Vector3 pos, dir; - _currentFrame->Spline->evaluate_percent(_currentFrame->Index, t, pos); - _currentFrame->Spline->evaluate_derivative(_currentFrame->Index, t, dir); - UpdatePosition(pos.x, pos.y, pos.z, std::atan2(dir.y, dir.x) + float(M_PI)); - } - else if (justStopped) - UpdatePosition(_currentFrame->Node->Loc.X, _currentFrame->Node->Loc.Y, _currentFrame->Node->Loc.Z, _currentFrame->InitialOrientation); - else - { - /* There are four possible scenarios that trigger loading/unloading passengers: - 1. transport moves from inactive to active grid - 2. the grid that transport is currently in becomes active - 3. transport moves from active to inactive grid - 4. the grid that transport is currently in unloads - */ - bool gridActive = GetMap()->IsGridLoaded(GetPositionX(), GetPositionY()); - - if (_staticPassengers.empty() && gridActive) // 2. - LoadStaticPassengers(); - else if (!_staticPassengers.empty() && !gridActive) - // 4. - if transports stopped on grid edge, some passengers can remain in active grids - // unload all static passengers otherwise passengers won't load correctly when the grid that transport is currently in becomes active - UnloadStaticPassengers(); - } - } - - sScriptMgr->OnTransportUpdate(this, diff); } void Transport::DelayedUpdate(uint32 /*diff*/) { - if (GetKeyFrames().size() <= 1) - return; - DelayedTeleportTransport(); } @@ -323,23 +299,7 @@ void Transport::AddPassenger(WorldObject* passenger) Transport* Transport::RemovePassenger(WorldObject* passenger) { - bool erased = false; - if (_passengerTeleportItr != _passengers.end()) - { - PassengerSet::iterator itr = _passengers.find(passenger); - if (itr != _passengers.end()) - { - if (itr == _passengerTeleportItr) - ++_passengerTeleportItr; - - _passengers.erase(itr); - erased = true; - } - } - else - erased = _passengers.erase(passenger) > 0; - - if (erased || _staticPassengers.erase(passenger)) // static passenger can remove itself in case of grid unload + if (_passengers.erase(passenger) || _staticPassengers.erase(passenger)) // static passenger can remove itself in case of grid unload { passenger->SetTransport(nullptr); passenger->m_movementInfo.transport.Reset(); @@ -574,6 +534,8 @@ int32 Transport::GetMapIdForSpawning() const void Transport::UpdatePosition(float x, float y, float z, float o) { + sScriptMgr->OnRelocate(this, GetMapId(), x, y, z); + bool newActive = GetMap()->IsGridLoaded(x, y); Cell oldCell(GetPositionX(), GetPositionY()); @@ -633,50 +595,16 @@ void Transport::EnableMovement(bool enabled) if (!GetGOInfo()->moTransport.allowstopping) return; - _pendingStop = !enabled; -} - -void Transport::MoveToNextWaypoint() -{ - // Clear events flagging - _triggeredArrivalEvent = false; - _triggeredDepartureEvent = false; - - // Set frames - _currentFrame = _nextFrame++; - if (_nextFrame == GetKeyFrames().end()) - _nextFrame = GetKeyFrames().begin(); -} - -float Transport::CalculateSegmentPos(float now) -{ - KeyFrame const& frame = *_currentFrame; - const float speed = float(m_goInfo->moTransport.moveSpeed); - const float accel = float(m_goInfo->moTransport.accelRate); - float timeSinceStop = frame.TimeFrom + (now - (1.0f/IN_MILLISECONDS) * frame.DepartureTime); - float timeUntilStop = frame.TimeTo - (now - (1.0f/IN_MILLISECONDS) * frame.DepartureTime); - float segmentPos, dist; - float accelTime = _transportInfo->accelTime; - float accelDist = _transportInfo->accelDist; - // calculate from nearest stop, less confusing calculation... - if (timeSinceStop < timeUntilStop) + if (!enabled) { - if (timeSinceStop < accelTime) - dist = 0.5f * accel * timeSinceStop * timeSinceStop; - else - dist = accelDist + (timeSinceStop - accelTime) * speed; - segmentPos = dist - frame.DistSinceStop; + _requestStopTimestamp = (_pathProgress / GetTransportPeriod()) * GetTransportPeriod() + _transportInfo->GetNextPauseWaypointTimestamp(_pathProgress); } else { - if (timeUntilStop < _transportInfo->accelTime) - dist = 0.5f * accel * timeUntilStop * timeUntilStop; - else - dist = accelDist + (timeUntilStop - accelTime) * speed; - segmentPos = frame.DistUntilStop - dist; + _requestStopTimestamp.reset(); + SetGoState(GO_STATE_ACTIVE); + RemoveDynamicFlag(GO_DYNFLAG_LO_STOPPED); } - - return segmentPos / frame.NextDistFromPrev; } bool Transport::TeleportTransport(uint32 newMapid, float x, float y, float z, float o) @@ -685,7 +613,7 @@ bool Transport::TeleportTransport(uint32 newMapid, float x, float y, float z, fl if (oldMap->GetId() != newMapid) { - _delayedTeleport = true; + _delayedTeleport.emplace(newMapid, x, y, z, o); UnloadStaticPassengers(); return true; } @@ -720,20 +648,20 @@ void Transport::DelayedTeleportTransport() if (!_delayedTeleport) return; - _delayedTeleport = false; - Map* newMap = sMapMgr->CreateBaseMap(_nextFrame->Node->ContinentID); + Map* newMap = sMapMgr->CreateBaseMap(_delayedTeleport->GetMapId()); GetMap()->RemoveFromMap<Transport>(this, false); SetMap(newMap); - float x = _nextFrame->Node->Loc.X, - y = _nextFrame->Node->Loc.Y, - z = _nextFrame->Node->Loc.Z, - o =_nextFrame->InitialOrientation; + float x = _delayedTeleport->GetPositionX(), + y = _delayedTeleport->GetPositionY(), + z = _delayedTeleport->GetPositionZ(), + o = _delayedTeleport->GetOrientation(); - for (_passengerTeleportItr = _passengers.begin(); _passengerTeleportItr != _passengers.end();) - { - WorldObject* obj = (*_passengerTeleportItr++); + _delayedTeleport.reset(); + PassengerSet passengersToTeleport = _passengers; + for (WorldObject* obj : passengersToTeleport) + { float destX, destY, destZ, destO; obj->m_movementInfo.transport.pos.GetPosition(destX, destY, destZ, destO); TransportBase::CalculatePassengerPosition(destX, destY, destZ, &destO, x, y, z, o); @@ -741,7 +669,7 @@ void Transport::DelayedTeleportTransport() switch (obj->GetTypeId()) { case TYPEID_PLAYER: - if (!obj->ToPlayer()->TeleportTo(_nextFrame->Node->ContinentID, destX, destY, destZ, destO, TELE_TO_NOT_LEAVE_TRANSPORT)) + if (!obj->ToPlayer()->TeleportTo(newMap->GetId(), destX, destY, destZ, destO, TELE_TO_NOT_LEAVE_TRANSPORT, newMap->GetInstanceId())) RemovePassenger(obj); break; case TYPEID_DYNAMICOBJECT: @@ -769,23 +697,15 @@ void Transport::UpdatePassengerPositions(PassengerSet const& passengers) } } -void Transport::DoEventIfAny(KeyFrame const& node, bool departure) -{ - if (uint32 eventid = departure ? node.Node->DepartureEventID : node.Node->ArrivalEventID) - { - TC_LOG_DEBUG("maps.script", "Taxi %s event %u of node %u of %s path", departure ? "departure" : "arrival", eventid, node.Node->NodeIndex, GetName().c_str()); - GameEvents::Trigger(eventid, this, this); - } -} - void Transport::BuildUpdate(UpdateDataMapType& data_map) { Map::PlayerList const& players = GetMap()->GetPlayers(); if (players.isEmpty()) return; - for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr) - BuildFieldsUpdate(itr->GetSource(), data_map); + for (MapReference const& playerReference : players) + if (playerReference.GetSource()->IsInPhase(this)) + BuildFieldsUpdate(playerReference.GetSource(), data_map); ClearUpdateMask(true); } diff --git a/src/server/game/Entities/Transport/Transport.h b/src/server/game/Entities/Transport/Transport.h index 2166fade720..f5e5a30e369 100644 --- a/src/server/game/Entities/Transport/Transport.h +++ b/src/server/game/Entities/Transport/Transport.h @@ -21,6 +21,7 @@ #include "GameObject.h" #include "TransportMgr.h" #include "VehicleDefines.h" +#include <boost/dynamic_bitset_fwd.hpp> struct CreatureData; struct SummonPropertiesEntry; @@ -90,8 +91,6 @@ class TC_GAME_API Transport : public GameObject, public TransportBase void SetPeriod(uint32 period) { SetLevel(period); } uint32 GetTimer() const { return _pathProgress; } - KeyFrameVec const& GetKeyFrames() const { return _transportInfo->keyFrames; } - void UpdatePosition(float x, float y, float z, float o); //! Needed when transport moves from inactive to active grid @@ -104,41 +103,26 @@ class TC_GAME_API Transport : public GameObject, public TransportBase void SetDelayedAddModelToMap() { _delayedAddModel = true; } - TransportTemplate const* GetTransportTemplate() const { return _transportInfo; } - std::string GetDebugInfo() const override; private: - void MoveToNextWaypoint(); - float CalculateSegmentPos(float perc); bool TeleportTransport(uint32 newMapid, float x, float y, float z, float o); void DelayedTeleportTransport(); void UpdatePassengerPositions(PassengerSet const& passengers); - void DoEventIfAny(KeyFrame const& node, bool departure); - - //! Helpers to know if stop frame was reached - bool IsMoving() const { return _isMoving; } - void SetMoving(bool val) { _isMoving = val; } TransportTemplate const* _transportInfo; - - KeyFrameVec::const_iterator _currentFrame; - KeyFrameVec::const_iterator _nextFrame; + TransportMovementState _movementState; + std::unique_ptr<boost::dynamic_bitset<uint8>> _eventsToTrigger; + size_t _currentPathLeg; + Optional<uint32> _requestStopTimestamp; uint32 _pathProgress; TimeTracker _positionChangeTimer; - bool _isMoving; - bool _pendingStop; - - //! These are needed to properly control events triggering only once for each frame - bool _triggeredArrivalEvent; - bool _triggeredDepartureEvent; PassengerSet _passengers; - PassengerSet::iterator _passengerTeleportItr; PassengerSet _staticPassengers; bool _delayedAddModel; - bool _delayedTeleport; + Optional<WorldLocation> _delayedTeleport; }; #endif |
