diff options
author | Shauren <shauren.trinity@gmail.com> | 2022-05-25 22:14:32 +0200 |
---|---|---|
committer | Shauren <shauren.trinity@gmail.com> | 2022-05-25 22:14:32 +0200 |
commit | 630b60eb0dcd3d9ce41582664ab822b049365431 (patch) | |
tree | b5d60fe41ca49846524ddbc7f9409194b76e7849 | |
parent | 3031fbb63bfb2df2aa041bd9c04c5c42b0c07c05 (diff) |
Core/GameObjects: Transport (type 11) improvements
* Fully synchronize serverside animation progress with client
* Implemented updating passenger positions on elevators
* Removed visibility hack for elevators that always forced CreateObject packet to be sent to client
33 files changed, 707 insertions, 310 deletions
diff --git a/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp b/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp index 5d10f1ab305..6d01620661a 100644 --- a/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp +++ b/src/server/game/Entities/AreaTrigger/AreaTrigger.cpp @@ -189,7 +189,7 @@ bool AreaTrigger::Create(uint32 areaTriggerCreatePropertiesId, Unit* caster, Uni } // movement on transport of areatriggers on unit is handled by themself - Transport* transport = m_movementInfo.transport.guid.IsEmpty() ? caster->GetTransport() : nullptr; + TransportBase* transport = m_movementInfo.transport.guid.IsEmpty() ? caster->GetTransport() : nullptr; if (transport) { float x, y, z, o; diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 3bb3e37389b..35876800ea3 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -1384,7 +1384,11 @@ void Creature::SaveToDB() return; } - uint32 mapId = GetTransport() ? GetTransport()->GetGOInfo()->moTransport.SpawnMap : GetMapId(); + uint32 mapId = GetMapId(); + if (TransportBase* transport = GetTransport()) + if (transport->GetMapIdForSpawning() >= 0) + mapId = transport->GetMapIdForSpawning(); + SaveToDB(mapId, data->spawnDifficulties); } diff --git a/src/server/game/Entities/DynamicObject/DynamicObject.cpp b/src/server/game/Entities/DynamicObject/DynamicObject.cpp index 2deee2146d9..9aeada30f46 100644 --- a/src/server/game/Entities/DynamicObject/DynamicObject.cpp +++ b/src/server/game/Entities/DynamicObject/DynamicObject.cpp @@ -111,7 +111,7 @@ bool DynamicObject::CreateDynamicObject(ObjectGuid::LowType guidlow, Unit* caste if (IsWorldObject()) setActive(true); //must before add to map to be put in world container - Transport* transport = caster->GetTransport(); + TransportBase* transport = caster->GetTransport(); if (transport) { float x, y, z, o; diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp index 16fd48efa0f..0122d0560b0 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -25,6 +25,7 @@ #include "CreatureAISelector.h" #include "DatabaseEnv.h" #include "DB2Stores.h" +#include "G3DPosition.hpp" #include "GameEventSender.h" #include "GameObjectAI.h" #include "GameObjectModel.h" @@ -116,6 +117,371 @@ QuaternionData QuaternionData::fromEulerAnglesZYX(float Z, float Y, float X) return QuaternionData(quat.x, quat.y, quat.z, quat.w); } +GameObjectTypeBase::CustomCommand::~CustomCommand() = default; + +namespace GameObjectType +{ +//11 GAMEOBJECT_TYPE_TRANSPORT +class Transport : public GameObjectTypeBase, public TransportBase +{ +public: + static constexpr Milliseconds PositionUpdateInterval = 50ms; + + explicit Transport(GameObject& owner) : GameObjectTypeBase(owner), _animationInfo(sTransportMgr->GetTransportAnimInfo(owner.GetGOInfo()->entry)), + _pathProgress(GameTime::GetGameTimeMS() % GetTransportPeriod()), _stateChangeTime(GameTime::GetGameTimeMS()), _stateChangeProgress(_pathProgress), + _autoCycleBetweenStopFrames(false) + { + GameObjectTemplate const* goInfo = _owner.GetGOInfo(); + if (goInfo->transport.Timeto2ndfloor > 0) + { + _stopFrames.push_back(goInfo->transport.Timeto2ndfloor); + if (goInfo->transport.Timeto3rdfloor > 0) + { + _stopFrames.push_back(goInfo->transport.Timeto3rdfloor); + if (goInfo->transport.Timeto4thfloor > 0) + { + _stopFrames.push_back(goInfo->transport.Timeto4thfloor); + if (goInfo->transport.Timeto5thfloor > 0) + { + _stopFrames.push_back(goInfo->transport.Timeto5thfloor); + if (goInfo->transport.Timeto6thfloor > 0) + { + _stopFrames.push_back(goInfo->transport.Timeto6thfloor); + if (goInfo->transport.Timeto7thfloor > 0) + { + _stopFrames.push_back(goInfo->transport.Timeto7thfloor); + if (goInfo->transport.Timeto8thfloor > 0) + { + _stopFrames.push_back(goInfo->transport.Timeto8thfloor); + if (goInfo->transport.Timeto9thfloor > 0) + { + _stopFrames.push_back(goInfo->transport.Timeto9thfloor); + if (goInfo->transport.Timeto10thfloor > 0) + _stopFrames.push_back(goInfo->transport.Timeto10thfloor); + } + } + } + } + } + } + } + } + + _positionUpdateTimer.Reset(PositionUpdateInterval); + } + + void Update(uint32 diff) override + { + if (!_animationInfo) + return; + + _positionUpdateTimer.Update(diff); + if (!_positionUpdateTimer.Passed()) + return; + + _positionUpdateTimer.Reset(PositionUpdateInterval); + + uint32 now = GameTime::GetGameTimeMS(); + uint32 period = GetTransportPeriod(); + uint32 newProgress = 0; + if (_stopFrames.empty()) + newProgress = now % period; + else + { + int32 stopTargetTime = 0; + if (_owner.GetGoState() == GO_STATE_TRANSPORT_ACTIVE) + stopTargetTime = 0; + else + stopTargetTime = _stopFrames[_owner.GetGoState() - GO_STATE_TRANSPORT_STOPPED]; + + if (now < uint32(*_owner.m_gameObjectData->Level)) + { + int32 timeToStop = _owner.m_gameObjectData->Level - _stateChangeTime; + float stopSourcePathPct = float(_stateChangeProgress) / float(period); + float stopTargetPathPct = float(stopTargetTime) / float(period); + float timeSinceStopProgressPct = float(now - _stateChangeTime) / float(timeToStop); + + float progressPct; + if (!_owner.HasDynamicFlag(GO_DYNFLAG_LO_INVERTED_MOVEMENT)) + { + if (_owner.GetGoState() == GO_STATE_TRANSPORT_ACTIVE) + stopTargetPathPct = 1.0f; + + float pathPctBetweenStops = stopTargetPathPct - stopSourcePathPct; + if (pathPctBetweenStops < 0.0f) + pathPctBetweenStops += 1.0f; + + progressPct = pathPctBetweenStops * timeSinceStopProgressPct + stopSourcePathPct; + if (progressPct > 1.0f) + progressPct = progressPct - 1.0f; + } + else + { + float pathPctBetweenStops = stopSourcePathPct - stopTargetPathPct; + if (pathPctBetweenStops < 0.0f) + pathPctBetweenStops += 1.0f; + + progressPct = stopSourcePathPct - pathPctBetweenStops * timeSinceStopProgressPct; + if (progressPct < 0.0f) + progressPct += 1.0f; + } + + newProgress = uint32(float(period) * progressPct) % period; + } + else + newProgress = stopTargetTime; + + if (int32(newProgress) == stopTargetTime && newProgress != _pathProgress) + { + uint32 eventId = [&]() + { + switch (_owner.GetGoState() - GO_STATE_TRANSPORT_ACTIVE) + { + case 0: + return _owner.GetGOInfo()->transport.Reached1stfloor; + case 1: + return _owner.GetGOInfo()->transport.Reached2ndfloor; + case 2: + return _owner.GetGOInfo()->transport.Reached3rdfloor; + case 3: + return _owner.GetGOInfo()->transport.Reached4thfloor; + case 4: + return _owner.GetGOInfo()->transport.Reached5thfloor; + case 5: + return _owner.GetGOInfo()->transport.Reached6thfloor; + case 6: + return _owner.GetGOInfo()->transport.Reached7thfloor; + case 7: + return _owner.GetGOInfo()->transport.Reached8thfloor; + case 8: + return _owner.GetGOInfo()->transport.Reached9thfloor; + case 9: + return _owner.GetGOInfo()->transport.Reached10thfloor; + default: + return 0u; + } + }(); + if (eventId) + GameEvents::Trigger(eventId, &_owner, nullptr); + + if (_autoCycleBetweenStopFrames) + { + GOState currentState = _owner.GetGoState(); + GOState newState; + if (currentState == GO_STATE_TRANSPORT_ACTIVE) + newState = GO_STATE_TRANSPORT_STOPPED; + else if (currentState - GO_STATE_TRANSPORT_ACTIVE == _stopFrames.size()) + newState = GOState(currentState - 1); + else if (_owner.HasDynamicFlag(GO_DYNFLAG_LO_INVERTED_MOVEMENT)) + newState = GOState(currentState - 1); + else + newState = GOState(currentState + 1); + + _owner.SetGoState(newState); + } + } + } + + if (_pathProgress == newProgress) + return; + + _pathProgress = newProgress; + + TransportAnimationEntry const* oldAnimation = _animationInfo->GetPrevAnimNode(newProgress); + TransportAnimationEntry const* newAnimation = _animationInfo->GetNextAnimNode(newProgress); + if (oldAnimation && newAnimation) + { + G3D::Matrix3 pathRotation = G3D::Quat(_owner.m_gameObjectData->ParentRotation->x, _owner.m_gameObjectData->ParentRotation->y, + _owner.m_gameObjectData->ParentRotation->z, _owner.m_gameObjectData->ParentRotation->w).toRotationMatrix(); + + G3D::Vector3 prev(oldAnimation->Pos.X, oldAnimation->Pos.Y, oldAnimation->Pos.Z); + G3D::Vector3 next(newAnimation->Pos.X, newAnimation->Pos.Y, newAnimation->Pos.Z); + + float animProgress = float(newProgress - oldAnimation->TimeIndex) / float(newAnimation->TimeIndex - oldAnimation->TimeIndex); + + G3D::Vector3 dst = prev.lerp(next, animProgress) * pathRotation; + + dst += PositionToVector3(&_owner.GetStationaryPosition()); + + _owner.GetMap()->GameObjectRelocation(&_owner, dst.x, dst.y, dst.z, _owner.GetOrientation()); + } + + TransportRotationEntry const* oldRotation = _animationInfo->GetPrevAnimRotation(newProgress); + TransportRotationEntry const* newRotation = _animationInfo->GetNextAnimRotation(newProgress); + if (oldRotation && newRotation) + { + G3D::Quat prev(oldRotation->Rot[0], oldRotation->Rot[1], oldRotation->Rot[2], oldRotation->Rot[3]); + G3D::Quat next(newRotation->Rot[0], newRotation->Rot[1], newRotation->Rot[2], newRotation->Rot[3]); + + float animProgress = float(newProgress - oldRotation->TimeIndex) / float(newRotation->TimeIndex - oldRotation->TimeIndex); + + G3D::Quat rotation = prev.slerp(next, animProgress); + + _owner.SetLocalRotation(rotation.x, rotation.y, rotation.z, rotation.w); + _owner.UpdateModelPosition(); + } + + // update progress marker for client + _owner.SetPathProgressForClient(float(_pathProgress) / float(period)); + } + + void OnStateChanged(GOState oldState, GOState newState) override + { + ASSERT(newState >= GO_STATE_TRANSPORT_ACTIVE); + + // transports without stop frames just keep animating in state 24 + if (_stopFrames.empty()) + { + if (newState != GO_STATE_TRANSPORT_ACTIVE) + _owner.SetGoState(GO_STATE_TRANSPORT_ACTIVE); + return; + } + + int32 stopPathProgress = 0; + + if (newState != GO_STATE_TRANSPORT_ACTIVE) + { + ASSERT(newState < GOState(GO_STATE_TRANSPORT_STOPPED + MAX_GO_STATE_TRANSPORT_STOP_FRAMES)); + uint32 stopFrame = newState - GO_STATE_TRANSPORT_STOPPED; + ASSERT(stopFrame < _stopFrames.size()); + stopPathProgress = _stopFrames[stopFrame]; + } + + _stateChangeTime = GameTime::GetGameTimeMS(); + _stateChangeProgress = _pathProgress; + uint32 timeToStop = std::abs(int32(_pathProgress) - stopPathProgress); + _owner.SetLevel(GameTime::GetGameTimeMS() + timeToStop); + _owner.SetPathProgressForClient(float(_pathProgress) / float(GetTransportPeriod())); + + if (oldState == GO_STATE_ACTIVE || oldState == newState) + { + // initialization + if (int32(_pathProgress) > stopPathProgress) + _owner.SetDynamicFlag(GO_DYNFLAG_LO_INVERTED_MOVEMENT); + else + _owner.RemoveDynamicFlag(GO_DYNFLAG_LO_INVERTED_MOVEMENT); + + return; + } + + int32 pauseTimesCount = _stopFrames.size(); + int32 newToOldStateDelta = newState - oldState; + if (newToOldStateDelta < 0) + newToOldStateDelta += pauseTimesCount + 1; + + int32 oldToNewStateDelta = oldState - newState; + if (oldToNewStateDelta < 0) + oldToNewStateDelta += pauseTimesCount + 1; + + if (oldToNewStateDelta < newToOldStateDelta) + _owner.SetDynamicFlag(GO_DYNFLAG_LO_INVERTED_MOVEMENT); + else + _owner.RemoveDynamicFlag(GO_DYNFLAG_LO_INVERTED_MOVEMENT); + } + + void OnRelocated() override + { + UpdatePassengerPositions(); + } + + void UpdatePassengerPositions() + { + for (WorldObject* passenger : _passengers) + { + float x, y, z, o; + passenger->m_movementInfo.transport.pos.GetPosition(x, y, z, o); + CalculatePassengerPosition(x, y, z, &o); + UpdatePassengerPosition(_owner.GetMap(), passenger, x, y, z, o, true); + } + } + + uint32 GetTransportPeriod() const + { + if (_animationInfo) + return _animationInfo->TotalTime; + + return 1; + } + + std::vector<uint32> const* GetPauseTimes() const + { + return &_stopFrames; + } + + ObjectGuid GetTransportGUID() const override { return _owner.GetGUID(); } + + float GetTransportOrientation() const override { return _owner.GetOrientation(); } + + void AddPassenger(WorldObject* passenger) override + { + if (!_owner.IsInWorld()) + return; + + if (_passengers.insert(passenger).second) + { + passenger->SetTransport(this); + passenger->m_movementInfo.transport.guid = GetTransportGUID(); + TC_LOG_DEBUG("entities.transport", "Object %s boarded transport %s.", passenger->GetName().c_str(), _owner.GetName().c_str()); + } + } + + TransportBase* RemovePassenger(WorldObject* passenger) override + { + if (_passengers.erase(passenger) > 0) + { + passenger->SetTransport(nullptr); + passenger->m_movementInfo.transport.Reset(); + TC_LOG_DEBUG("entities.transport", "Object %s removed from transport %s.", passenger->GetName().c_str(), _owner.GetName().c_str()); + + if (Player* plr = passenger->ToPlayer()) + plr->SetFallInformation(0, plr->GetPositionZ()); + } + + return this; + } + + void CalculatePassengerPosition(float& x, float& y, float& z, float* o) const override + { + TransportBase::CalculatePassengerPosition(x, y, z, o, _owner.GetPositionX(), _owner.GetPositionY(), _owner.GetPositionZ(), _owner.GetOrientation()); + } + + void CalculatePassengerOffset(float& x, float& y, float& z, float* o) const override + { + TransportBase::CalculatePassengerOffset(x, y, z, o, _owner.GetPositionX(), _owner.GetPositionY(), _owner.GetPositionZ(), _owner.GetOrientation()); + } + + int32 GetMapIdForSpawning() const override + { + return _owner.GetGOInfo()->transport.SpawnMap; + } + + void SetAutoCycleBetweenStopFrames(bool on) + { + _autoCycleBetweenStopFrames = on; + } + +private: + TransportAnimation const* _animationInfo; + uint32 _pathProgress; + uint32 _stateChangeTime; + uint32 _stateChangeProgress; + std::vector<uint32> _stopFrames; + bool _autoCycleBetweenStopFrames; + TimeTracker _positionUpdateTimer; + std::unordered_set<WorldObject*> _passengers; +}; + +SetTransportAutoCycleBetweenStopFrames::SetTransportAutoCycleBetweenStopFrames(bool on) : _on(on) +{ +} + +void SetTransportAutoCycleBetweenStopFrames::Execute(GameObjectTypeBase& type) const +{ + if (Transport* transport = dynamic_cast<Transport*>(&type)) + transport->SetAutoCycleBetweenStopFrames(_on); +} +} + GameObject::GameObject() : WorldObject(false), MapObject(), m_model(nullptr), m_goValue(), m_AI(nullptr), m_respawnCompatibilityMode(false), _animKitId(0), _worldEffectID(0) { @@ -154,10 +520,6 @@ GameObject::~GameObject() { delete m_AI; delete m_model; - if (m_goInfo && m_goInfo->type == GAMEOBJECT_TYPE_TRANSPORT) - delete m_goValue.Transport.StopFrames; - //if (m_uint32Values) // field array can be not exist if GameOBject not loaded - // CleanupsBeforeDelete(); } void GameObject::AIM_Destroy() @@ -388,37 +750,14 @@ bool GameObject::Create(uint32 entry, Map* map, Position const& pos, QuaternionD } case GAMEOBJECT_TYPE_TRANSPORT: { - m_goValue.Transport.AnimationInfo = sTransportMgr->GetTransportAnimInfo(goInfo->entry); - m_goValue.Transport.PathProgress = getMSTime(); - if (m_goValue.Transport.AnimationInfo) - m_goValue.Transport.PathProgress -= m_goValue.Transport.PathProgress % GetTransportPeriod(); // align to period - m_goValue.Transport.CurrentSeg = 0; - m_goValue.Transport.StateUpdateTimer = 0; - m_goValue.Transport.StopFrames = new std::vector<uint32>(); - if (goInfo->transport.Timeto2ndfloor > 0) - m_goValue.Transport.StopFrames->push_back(goInfo->transport.Timeto2ndfloor); - if (goInfo->transport.Timeto3rdfloor > 0) - m_goValue.Transport.StopFrames->push_back(goInfo->transport.Timeto3rdfloor); - if (goInfo->transport.Timeto4thfloor > 0) - m_goValue.Transport.StopFrames->push_back(goInfo->transport.Timeto4thfloor); - if (goInfo->transport.Timeto5thfloor > 0) - m_goValue.Transport.StopFrames->push_back(goInfo->transport.Timeto5thfloor); - if (goInfo->transport.Timeto6thfloor > 0) - m_goValue.Transport.StopFrames->push_back(goInfo->transport.Timeto6thfloor); - if (goInfo->transport.Timeto7thfloor > 0) - m_goValue.Transport.StopFrames->push_back(goInfo->transport.Timeto7thfloor); - if (goInfo->transport.Timeto8thfloor > 0) - m_goValue.Transport.StopFrames->push_back(goInfo->transport.Timeto8thfloor); - if (goInfo->transport.Timeto9thfloor > 0) - m_goValue.Transport.StopFrames->push_back(goInfo->transport.Timeto9thfloor); - if (goInfo->transport.Timeto10thfloor > 0) - m_goValue.Transport.StopFrames->push_back(goInfo->transport.Timeto10thfloor); + m_goTypeImpl = std::make_unique<GameObjectType::Transport>(*this); if (goInfo->transport.startOpen) - SetTransportState(GO_STATE_TRANSPORT_STOPPED, goInfo->transport.startOpen - 1); + SetGoState(GO_STATE_TRANSPORT_STOPPED); else - SetTransportState(GO_STATE_TRANSPORT_ACTIVE); + SetGoState(GO_STATE_TRANSPORT_ACTIVE); SetGoAnimProgress(animProgress); + setActive(true); break; } case GAMEOBJECT_TYPE_FISHINGNODE: @@ -555,6 +894,9 @@ void GameObject::Update(uint32 diff) } } + if (m_goTypeImpl) + m_goTypeImpl->Update(diff); + switch (m_lootState) { case GO_NOT_READY: @@ -576,53 +918,6 @@ void GameObject::Update(uint32 diff) SetLootState(GO_READY); break; } - case GAMEOBJECT_TYPE_TRANSPORT: - { - if (!m_goValue.Transport.AnimationInfo) - break; - - if (GetGoState() == GO_STATE_TRANSPORT_ACTIVE) - { - m_goValue.Transport.PathProgress += diff; - /* TODO: Fix movement in unloaded grid - currently GO will just disappear - uint32 timer = m_goValue.Transport.PathProgress % GetTransportPeriod(); - TransportAnimationEntry const* node = m_goValue.Transport.AnimationInfo->GetAnimNode(timer); - if (node && m_goValue.Transport.CurrentSeg != node->TimeSeg) - { - m_goValue.Transport.CurrentSeg = node->TimeSeg; - - G3D::Quat rotation; - if (TransportRotationEntry const* rot = m_goValue.Transport.AnimationInfo->GetAnimRotation(timer)) - rotation = G3D::Quat(rot->X, rot->Y, rot->Z, rot->W); - - G3D::Vector3 pos = rotation.toRotationMatrix() - * G3D::Matrix3::fromEulerAnglesZYX(GetOrientation(), 0.0f, 0.0f) - * G3D::Vector3(node->X, node->Y, node->Z); - - pos += G3D::Vector3(GetStationaryX(), GetStationaryY(), GetStationaryZ()); - - G3D::Vector3 src(GetPositionX(), GetPositionY(), GetPositionZ()); - - TC_LOG_DEBUG("misc", "Src: %s Dest: %s", src.toString().c_str(), pos.toString().c_str()); - - GetMap()->GameObjectRelocation(this, pos.x, pos.y, pos.z, GetOrientation()); - } - */ - - if (!m_goValue.Transport.StopFrames->empty()) - { - uint32 visualStateBefore = (m_goValue.Transport.StateUpdateTimer / 20000) & 1; - m_goValue.Transport.StateUpdateTimer += diff; - uint32 visualStateAfter = (m_goValue.Transport.StateUpdateTimer / 20000) & 1; - if (visualStateBefore != visualStateAfter) - { - ForceUpdateFieldChange(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::Level)); - ForceUpdateFieldChange(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::State)); - } - } - } - break; - } case GAMEOBJECT_TYPE_FISHINGNODE: { // fishing code (bobber ready) @@ -1164,7 +1459,12 @@ void GameObject::SaveToDB() return; } - SaveToDB(GetMapId(), data->spawnDifficulties); + uint32 mapId = GetMapId(); + if (TransportBase* transport = GetTransport()) + if (transport->GetMapIdForSpawning() >= 0) + mapId = transport->GetMapIdForSpawning(); + + SaveToDB(mapId, data->spawnDifficulties); } void GameObject::SaveToDB(uint32 mapid, std::vector<Difficulty> const& spawnDifficulties) @@ -1412,7 +1712,7 @@ bool GameObject::IsDynTransport() const if (!gInfo) return false; - return gInfo->type == GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT || (gInfo->type == GAMEOBJECT_TYPE_TRANSPORT && m_goValue.Transport.StopFrames->empty()); + return gInfo->type == GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT || gInfo->type == GAMEOBJECT_TYPE_TRANSPORT; } bool GameObject::IsDestructibleBuilding() const @@ -1734,8 +2034,9 @@ void GameObject::ActivateObject(GameObjectActions action, int32 param, WorldObje case GameObjectActions::GoTo8thFloor: case GameObjectActions::GoTo9thFloor: case GameObjectActions::GoTo10thFloor: + static_assert(int32(GO_STATE_TRANSPORT_ACTIVE) == int32(GameObjectActions::GoTo1stFloor)); if (GetGoType() == GAMEOBJECT_TYPE_TRANSPORT) - SetTransportState(GO_STATE_TRANSPORT_STOPPED, uint32(action) - uint32(GameObjectActions::GoTo1stFloor)); + SetGoState(GOState(action)); else TC_LOG_ERROR("spell", "Spell %d targeted non-transport gameobject for transport only action \"Go to Floor\" %d in effect %d", spellId, int32(action), effectIndex); break; @@ -2589,7 +2890,7 @@ void GameObject::SetLocalRotationAngles(float z_rot, float y_rot, float x_rot) QuaternionData GameObject::GetWorldRotation() const { QuaternionData localRotation = GetLocalRotation(); - if (Transport* transport = GetTransport()) + if (Transport* transport = dynamic_cast<Transport*>(GetTransport())) { QuaternionData worldRotation = transport->GetWorldRotation(); @@ -2781,9 +3082,14 @@ void GameObject::SetLootGenerationTime() void GameObject::SetGoState(GOState state) { + GOState oldState = GetGoState(); SetUpdateFieldValue(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::State), state); if (AI()) AI()->OnStateChanged(state); + + if (m_goTypeImpl) + m_goTypeImpl->OnStateChanged(oldState, state); + if (m_model && !IsTransport()) { if (!IsInWorld()) @@ -2798,39 +3104,6 @@ void GameObject::SetGoState(GOState state) } } -uint32 GameObject::GetTransportPeriod() const -{ - ASSERT(GetGOInfo()->type == GAMEOBJECT_TYPE_TRANSPORT); - if (m_goValue.Transport.AnimationInfo) - return m_goValue.Transport.AnimationInfo->TotalTime; - - return 0; -} - -void GameObject::SetTransportState(GOState state, uint32 stopFrame /*= 0*/) -{ - if (GetGoState() == state) - return; - - ASSERT(GetGOInfo()->type == GAMEOBJECT_TYPE_TRANSPORT); - ASSERT(state >= GO_STATE_TRANSPORT_ACTIVE); - if (state == GO_STATE_TRANSPORT_ACTIVE) - { - m_goValue.Transport.StateUpdateTimer = 0; - m_goValue.Transport.PathProgress = getMSTime(); - if (GetGoState() >= GO_STATE_TRANSPORT_STOPPED) - m_goValue.Transport.PathProgress += m_goValue.Transport.StopFrames->at(GetGoState() - GO_STATE_TRANSPORT_STOPPED); - SetGoState(GO_STATE_TRANSPORT_ACTIVE); - } - else - { - ASSERT(state < GOState(GO_STATE_TRANSPORT_STOPPED + MAX_GO_STATE_TRANSPORT_STOP_FRAMES)); - ASSERT(stopFrame < m_goValue.Transport.StopFrames->size()); - m_goValue.Transport.PathProgress = getMSTime() + m_goValue.Transport.StopFrames->at(stopFrame); - SetGoState(GOState(GO_STATE_TRANSPORT_STOPPED + stopFrame)); - } -} - void GameObject::SetDisplayId(uint32 displayid) { SetUpdateFieldValue(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::DisplayID), displayid); @@ -3029,6 +3302,32 @@ void GameObject::ClearUpdateMask(bool remove) Object::ClearUpdateMask(remove); } +std::vector<uint32> const* GameObject::GetPauseTimes() const +{ + if (GameObjectType::Transport const* transport = dynamic_cast<GameObjectType::Transport const*>(m_goTypeImpl.get())) + return transport->GetPauseTimes(); + + return nullptr; +} + +void GameObject::SetPathProgressForClient(float progress) +{ + DoWithSuppressingObjectUpdates([&]() + { + UF::ObjectData::Base dynflagMask; + dynflagMask.MarkChanged(&UF::ObjectData::DynamicFlags); + bool marked = (m_objectData->GetChangesMask() & dynflagMask.GetChangesMask()).IsAnySet(); + + uint32 dynamicFlags = GetDynamicFlags(); + dynamicFlags &= 0xFFFF; // remove high bits + dynamicFlags |= uint32(progress * 65535.0f) << 16; + ReplaceAllDynamicFlags(dynamicFlags); + + if (!marked) + const_cast<UF::ObjectData&>(*m_objectData).ClearChanged(&UF::ObjectData::DynamicFlags); + }); +} + void GameObject::GetRespawnPosition(float &x, float &y, float &z, float* ori /* = nullptr*/) const { if (m_goData) @@ -3047,6 +3346,31 @@ void GameObject::GetRespawnPosition(float &x, float &y, float &z, float* ori /* } } +TransportBase const* GameObject::ToTransportBase() const +{ + switch (GetGoType()) + { + case GAMEOBJECT_TYPE_TRANSPORT: + return static_cast<GameObjectType::Transport const*>(m_goTypeImpl.get()); + case GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT: + return static_cast<Transport const*>(this); + default: + break; + } + + return nullptr; +} + +void GameObject::AfterRelocation() +{ + UpdateModelPosition(); + UpdatePositionData(); + if (m_goTypeImpl) + m_goTypeImpl->OnRelocated(); + + UpdateObjectVisibility(false); +} + float GameObject::GetInteractionDistance() const { switch (GetGoType()) @@ -3315,6 +3639,12 @@ void GameObject::UpdateDynamicFlagsForNearbyPlayers() const Cell::VisitWorldObjects(this, deliverer, GetVisibilityRange()); } +void GameObject::HandleCustomTypeCommand(GameObjectTypeBase::CustomCommand const& command) const +{ + if (m_goTypeImpl) + command.Execute(*m_goTypeImpl); +} + void GameObject::CreateModel() { m_model = GameObjectModel::Create(std::make_unique<GameObjectModelOwnerImpl>(this), sWorld->GetDataPath()); diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index e47df17d3e0..e345f9a147d 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -25,11 +25,13 @@ #include "MapObject.h" #include "SharedDefines.h" +class GameObject; class GameObjectAI; class GameObjectModel; class Group; class OPvPCapturePoint; class Transport; +class TransportBase; class Unit; struct TransportAnimation; enum TriggerCastFlags : uint32; @@ -42,17 +44,44 @@ namespace WorldPackets } } -union GameObjectValue +// Base class for GameObject type specific implementations +class GameObjectTypeBase { - //11 GAMEOBJECT_TYPE_TRANSPORT - struct +public: + class TC_GAME_API CustomCommand { - uint32 PathProgress; - TransportAnimation const* AnimationInfo; - uint32 CurrentSeg; - std::vector<uint32>* StopFrames; - uint32 StateUpdateTimer; - } Transport; + public: + virtual ~CustomCommand(); + virtual void Execute(GameObjectTypeBase& type) const = 0; + }; + + explicit GameObjectTypeBase(GameObject& owner) : _owner(owner) { } + virtual ~GameObjectTypeBase() = default; + + virtual void Update([[maybe_unused]] uint32 diff) { } + virtual void OnStateChanged([[maybe_unused]] GOState oldState, [[maybe_unused]] GOState newState) { } + virtual void OnRelocated() { } + +protected: + GameObject& _owner; +}; + +namespace GameObjectType +{ +class TC_GAME_API SetTransportAutoCycleBetweenStopFrames : public GameObjectTypeBase::CustomCommand +{ +public: + explicit SetTransportAutoCycleBetweenStopFrames(bool on); + + void Execute(GameObjectTypeBase& type) const override; + +private: + bool _on; +}; +} + +union GameObjectValue +{ //25 GAMEOBJECT_TYPE_FISHINGHOLE struct { @@ -209,14 +238,15 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> void SetGoType(GameobjectTypes type) { SetUpdateFieldValue(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::TypeID), type); } GOState GetGoState() const { return GOState(*m_gameObjectData->State); } void SetGoState(GOState state); - virtual uint32 GetTransportPeriod() const; - void SetTransportState(GOState state, uint32 stopFrame = 0); uint32 GetGoArtKit() const { return m_gameObjectData->ArtKit; } void SetGoArtKit(uint32 artkit); uint8 GetGoAnimProgress() const { return m_gameObjectData->PercentHealth; } void SetGoAnimProgress(uint8 animprogress) { SetUpdateFieldValue(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::PercentHealth), animprogress); } static void SetGoArtKit(uint32 artkit, GameObject* go, ObjectGuid::LowType lowguid = UI64LIT(0)); + std::vector<uint32> const* GetPauseTimes() const; + void SetPathProgressForClient(float progress); + void EnableCollision(bool enable); void Use(Unit* user); @@ -313,6 +343,9 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> GameObjectModel* m_model; void GetRespawnPosition(float &x, float &y, float &z, float* ori = nullptr) const; + TransportBase* ToTransportBase() { return const_cast<TransportBase*>(const_cast<GameObject const*>(this)->ToTransportBase()); } + TransportBase const* ToTransportBase() const; + Transport* ToTransport() { if (GetGOInfo()->type == GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT) return reinterpret_cast<Transport*>(this); else return nullptr; } Transport const* ToTransport() const { if (GetGOInfo()->type == GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT) return reinterpret_cast<Transport const*>(this); else return nullptr; } @@ -320,8 +353,11 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> float GetStationaryY() const override { return m_stationaryPosition.GetPositionY(); } float GetStationaryZ() const override { return m_stationaryPosition.GetPositionZ(); } float GetStationaryO() const override { return m_stationaryPosition.GetOrientation(); } + Position const& GetStationaryPosition() const { return m_stationaryPosition; } void RelocateStationaryPosition(float x, float y, float z, float o) { m_stationaryPosition.Relocate(x, y, z, o); } + void AfterRelocation(); + float GetInteractionDistance() const; void UpdateModelPosition(); @@ -352,6 +388,8 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> void UpdateDynamicFlagsForNearbyPlayers() const; + void HandleCustomTypeCommand(GameObjectTypeBase::CustomCommand const& command) const; + UF::UpdateField<UF::GameObjectData, 0, TYPEID_GAMEOBJECT> m_gameObjectData; protected: @@ -383,7 +421,8 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject> GameObjectTemplate const* m_goInfo; GameObjectTemplateAddon const* m_goTemplateAddon; GameObjectData const* m_goData; - GameObjectValue m_goValue; + std::unique_ptr<GameObjectTypeBase> m_goTypeImpl; + GameObjectValue m_goValue; // TODO: replace with m_goTypeImpl int64 m_packedRotation; QuaternionData m_localRotation; diff --git a/src/server/game/Entities/GameObject/GameObjectData.h b/src/server/game/Entities/GameObject/GameObjectData.h index 9e7650115e4..46f87f8c9f4 100644 --- a/src/server/game/Entities/GameObject/GameObjectData.h +++ b/src/server/game/Entities/GameObject/GameObjectData.h @@ -1017,6 +1017,7 @@ struct GameObjectTemplate case GAMEOBJECT_TYPE_TRAP: return trap.GiganticAOI != 0; case GAMEOBJECT_TYPE_SPELL_FOCUS: return spellFocus.GiganticAOI != 0; case GAMEOBJECT_TYPE_GOOBER: return goober.GiganticAOI != 0; + case GAMEOBJECT_TYPE_TRANSPORT: return true; case GAMEOBJECT_TYPE_SPELLCASTER: return spellCaster.GiganticAOI != 0; case GAMEOBJECT_TYPE_FLAGSTAND: return flagStand.GiganticAOI != 0; case GAMEOBJECT_TYPE_FLAGDROP: return flagDrop.GiganticAOI != 0; diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index fef22d2af9a..5734afb9f3b 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -263,15 +263,8 @@ void Object::SendOutOfRangeForPlayer(Player* target) const void Object::BuildMovementUpdate(ByteBuffer* data, CreateObjectBits flags, Player* target) const { std::vector<uint32> const* PauseTimes = nullptr; - uint32 PauseTimesCount = 0; if (GameObject const* go = ToGameObject()) - { - if (go->GetGoType() == GAMEOBJECT_TYPE_TRANSPORT) - { - PauseTimes = go->GetGOValue()->Transport.StopFrames; - PauseTimesCount = PauseTimes->size(); - } - } + PauseTimes = go->GetPauseTimes(); data->WriteBit(flags.NoBirthAnim); data->WriteBit(flags.EnablePortals); @@ -384,7 +377,7 @@ void Object::BuildMovementUpdate(ByteBuffer* data, CreateObjectBits flags, Playe WorldPackets::Movement::CommonMovement::WriteCreateObjectSplineDataBlock(*unit->movespline, *data); } - *data << uint32(PauseTimesCount); + *data << uint32(PauseTimes ? PauseTimes->size() : 0); if (flags.Stationary) { @@ -399,18 +392,7 @@ void Object::BuildMovementUpdate(ByteBuffer* data, CreateObjectBits flags, Playe *data << ToUnit()->GetVictim()->GetGUID(); // CombatVictim if (flags.ServerTime) - { - GameObject const* go = ToGameObject(); - /** @TODO Use IsTransport() to also handle type 11 (TRANSPORT) - Currently grid objects are not updated if there are no nearby players, - this causes clients to receive different PathProgress - resulting in players seeing the object in a different position - */ - if (go && go->ToTransport()) // ServerTime - *data << uint32(go->GetGOValue()->Transport.PathProgress); - else - *data << uint32(GameTime::GetGameTimeMS()); - } + *data << uint32(GameTime::GetGameTimeMS()); if (flags.Vehicle) { @@ -430,7 +412,7 @@ void Object::BuildMovementUpdate(ByteBuffer* data, CreateObjectBits flags, Playe if (flags.Rotation) *data << uint64(ToGameObject()->GetPackedLocalRotation()); // Rotation - if (PauseTimesCount) + if (PauseTimes && !PauseTimes->empty()) data->append(PauseTimes->data(), PauseTimes->size()); if (flags.MovementTransport) @@ -885,19 +867,9 @@ void WorldObject::setActive(bool on) return; if (on) - { - if (GetTypeId() == TYPEID_UNIT) - map->AddToActive(ToCreature()); - else if (GetTypeId() == TYPEID_DYNAMICOBJECT) - map->AddToActive((DynamicObject*)this); - } + map->AddToActive(this); else - { - if (GetTypeId() == TYPEID_UNIT) - map->RemoveFromActive(ToCreature()); - else if (GetTypeId() == TYPEID_DYNAMICOBJECT) - map->RemoveFromActive((DynamicObject*)this); - } + map->RemoveFromActive(this); } void WorldObject::SetVisibilityDistanceOverride(VisibilityDistanceType type) @@ -922,7 +894,7 @@ void WorldObject::CleanupsBeforeDelete(bool /*finalCleanup*/) if (IsInWorld()) RemoveFromWorld(); - if (Transport* transport = GetTransport()) + if (TransportBase* transport = GetTransport()) transport->RemovePassenger(this); } @@ -999,7 +971,7 @@ bool WorldObject::_IsWithinDist(WorldObject const* obj, float dist2compare, bool Position const* thisOrTransport = this; Position const* objOrObjTransport = obj; - if (GetTransport() && obj->GetTransport() && obj->GetTransport()->GetGUID() == GetTransport()->GetGUID()) + if (GetTransport() && obj->GetTransport() && obj->GetTransport()->GetTransportGUID() == GetTransport()->GetTransportGUID()) { thisOrTransport = &m_movementInfo.transport.pos; objOrObjTransport = &obj->m_movementInfo.transport.pos; @@ -1843,7 +1815,7 @@ TempSummon* Map::SummonCreature(uint32 entry, Position const& pos, SummonPropert return nullptr; } - Transport* transport = summoner ? summoner->GetTransport() : nullptr; + TransportBase* transport = summoner ? summoner->GetTransport() : nullptr; if (transport) { float x, y, z, o; @@ -3606,7 +3578,7 @@ void WorldObject::RemoveFromObjectUpdate() ObjectGuid WorldObject::GetTransGUID() const { if (GetTransport()) - return GetTransport()->GetGUID(); + return GetTransport()->GetTransportGUID(); return ObjectGuid::Empty; } diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h index db57584c76a..acc4f688fe4 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -54,7 +54,7 @@ class SpellCastTargets; class SpellEffectInfo; class SpellInfo; class TempSummon; -class Transport; +class TransportBase; class Unit; class UpdateData; class WorldObject; @@ -676,7 +676,7 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation uint32 LastUsedScriptID; // Transports - Transport* GetTransport() const { return m_transport; } + TransportBase* GetTransport() const { return m_transport; } float GetTransOffsetX() const { return m_movementInfo.transport.pos.GetPositionX(); } float GetTransOffsetY() const { return m_movementInfo.transport.pos.GetPositionY(); } float GetTransOffsetZ() const { return m_movementInfo.transport.pos.GetPositionZ(); } @@ -685,7 +685,7 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation uint32 GetTransTime() const { return m_movementInfo.transport.time; } int8 GetTransSeat() const { return m_movementInfo.transport.seat; } virtual ObjectGuid GetTransGUID() const; - void SetTransport(Transport* t) { m_transport = t; } + void SetTransport(TransportBase* t) { m_transport = t; } MovementInfo m_movementInfo; @@ -728,8 +728,8 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation bool const m_isWorldObject; ZoneScript* m_zoneScript; - // transports - Transport* m_transport; + // transports (gameobjects only) + TransportBase* m_transport; virtual void ProcessPositionDataChanged(PositionFullTerrainStatus const& data); uint32 m_zoneId; diff --git a/src/server/game/Entities/Object/Updates/UpdateFields.cpp b/src/server/game/Entities/Object/Updates/UpdateFields.cpp index 82a1ca81712..591aeb657ac 100644 --- a/src/server/game/Entities/Object/Updates/UpdateFields.cpp +++ b/src/server/game/Entities/Object/Updates/UpdateFields.cpp @@ -4264,13 +4264,13 @@ void GameObjectData::WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fie data << float(ParentRotation->z); data << float(ParentRotation->w); data << int32(FactionTemplate); - data << int8(ViewerDependentValue<StateTag>::GetValue(this, owner, receiver)); + data << int8(State); data << int8(TypeID); data << uint8(PercentHealth); data << uint32(ArtKit); data << uint32(EnableDoodadSets.size()); data << uint32(CustomParam); - data << int32(ViewerDependentValue<LevelTag>::GetValue(this, owner, receiver)); + data << int32(Level); data << uint32(AnimGroupInstance); for (uint32 i = 0; i < EnableDoodadSets.size(); ++i) { @@ -4371,7 +4371,7 @@ void GameObjectData::WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool } if (changesMask[14]) { - data << int8(ViewerDependentValue<StateTag>::GetValue(this, owner, receiver)); + data << int8(State); } if (changesMask[15]) { @@ -4391,7 +4391,7 @@ void GameObjectData::WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool } if (changesMask[19]) { - data << int32(ViewerDependentValue<LevelTag>::GetValue(this, owner, receiver)); + data << int32(Level); } if (changesMask[20]) { diff --git a/src/server/game/Entities/Object/Updates/UpdateFields.h b/src/server/game/Entities/Object/Updates/UpdateFields.h index a9967827f7a..e3cfb83fa40 100644 --- a/src/server/game/Entities/Object/Updates/UpdateFields.h +++ b/src/server/game/Entities/Object/Updates/UpdateFields.h @@ -804,13 +804,11 @@ struct GameObjectData : public IsUpdateFieldStructureTag, public HasChangesMask< UpdateField<QuaternionData, 0, 12> ParentRotation; UpdateField<int32, 0, 13> FactionTemplate; UpdateField<int8, 0, 14> State; - struct StateTag : ViewerDependentValueTag<int8> {}; UpdateField<int8, 0, 15> TypeID; UpdateField<uint8, 0, 16> PercentHealth; UpdateField<uint32, 0, 17> ArtKit; UpdateField<uint32, 0, 18> CustomParam; UpdateField<int32, 0, 19> Level; - struct LevelTag : ViewerDependentValueTag<int32> {}; UpdateField<uint32, 0, 20> AnimGroupInstance; void WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fieldVisibilityFlags, GameObject const* owner, Player const* receiver) const; diff --git a/src/server/game/Entities/Object/Updates/ViewerDependentValues.h b/src/server/game/Entities/Object/Updates/ViewerDependentValues.h index 6d8ee14d100..755006262bd 100644 --- a/src/server/game/Entities/Object/Updates/ViewerDependentValues.h +++ b/src/server/game/Entities/Object/Updates/ViewerDependentValues.h @@ -26,6 +26,7 @@ #include "Player.h" #include "SpellInfo.h" #include "SpellMgr.h" +#include "Transport.h" #include "World.h" #include "WorldSession.h" @@ -85,11 +86,17 @@ public: dynFlags |= GO_DYNFLAG_LO_SPARKLE | GO_DYNFLAG_LO_HIGHLIGHT; break; case GAMEOBJECT_TYPE_TRANSPORT: + { + dynFlags = dynamicFlags & 0xFFFF; + pathProgress = dynamicFlags >> 16; + break; + } case GAMEOBJECT_TYPE_MAP_OBJ_TRANSPORT: { - if (uint32 transportPeriod = gameObject->GetTransportPeriod()) + Transport const* transport = gameObject->ToTransport(); + if (uint32 transportPeriod = transport->GetTransportPeriod()) { - float timer = float(gameObject->GetGOValue()->Transport.PathProgress % transportPeriod); + float timer = float(transport->GetTimer() % transportPeriod); pathProgress = uint16(timer / float(transportPeriod) * 65535.0f); } break; @@ -257,38 +264,6 @@ public: }; template<> -class ViewerDependentValue<UF::GameObjectData::LevelTag> -{ -public: - using value_type = UF::GameObjectData::LevelTag::value_type; - - static value_type GetValue(UF::GameObjectData const* gameObjectData, GameObject const* gameObject, Player const* /*receiver*/) - { - value_type level = gameObjectData->Level; - bool isStoppableTransport = gameObject->GetGoType() == GAMEOBJECT_TYPE_TRANSPORT && !gameObject->GetGOValue()->Transport.StopFrames->empty(); - return isStoppableTransport ? gameObject->GetGOValue()->Transport.PathProgress : level; - } -}; - -template<> -class ViewerDependentValue<UF::GameObjectData::StateTag> -{ -public: - using value_type = UF::GameObjectData::StateTag::value_type; - - static value_type GetValue(UF::GameObjectData const* gameObjectData, GameObject const* gameObject, Player const* /*receiver*/) - { - value_type state = gameObjectData->State; - bool isStoppableTransport = gameObject->GetGoType() == GAMEOBJECT_TYPE_TRANSPORT && !gameObject->GetGOValue()->Transport.StopFrames->empty(); - if (isStoppableTransport && gameObject->GetGoState() == GO_STATE_TRANSPORT_ACTIVE) - if ((gameObject->GetGOValue()->Transport.StateUpdateTimer / 20000) & 1) - state = GO_STATE_TRANSPORT_STOPPED; - - return state; - } -}; - -template<> class ViewerDependentValue<UF::ConversationData::LastLineEndTimeTag> { public: diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index bf699108081..701a433c7e3 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -1342,7 +1342,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati TC_LOG_DEBUG("maps", "Player '%s' (%s) using client without required expansion tried teleporting to non accessible map (MapID: %u)", GetName().c_str(), GetGUID().ToString().c_str(), mapid); - if (Transport* transport = GetTransport()) + if (TransportBase* transport = GetTransport()) { transport->RemovePassenger(this); RepopAtGraveyard(); // teleport to near graveyard if on transport, looks blizz like :) @@ -1364,7 +1364,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati DisableSpline(); GetMotionMaster()->Remove(EFFECT_MOTION_TYPE); - if (Transport* transport = GetTransport()) + if (TransportBase* transport = GetTransport()) { if (!(options & TELE_TO_NOT_LEAVE_TRANSPORT)) transport->RemovePassenger(this); @@ -1512,7 +1512,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati WorldPackets::Movement::TransferPending transferPending; transferPending.MapID = mapid; transferPending.OldMapPosition = GetPosition(); - if (Transport* transport = GetTransport()) + if (Transport* transport = dynamic_cast<Transport*>(GetTransport())) { transferPending.Ship.emplace(); transferPending.Ship->ID = transport->GetEntry(); @@ -20475,8 +20475,8 @@ void Player::SaveToDB(LoginDatabaseTransaction loginTransaction, CharacterDataba stmt->setFloat(index++, finiteAlways(GetTransOffsetZ())); stmt->setFloat(index++, finiteAlways(GetTransOffsetO())); ObjectGuid::LowType transLowGUID = UI64LIT(0); - if (GetTransport()) - transLowGUID = GetTransport()->GetGUID().GetCounter(); + if (Transport* transport = dynamic_cast<Transport*>(GetTransport())) + transLowGUID = transport->GetGUID().GetCounter(); stmt->setUInt64(index++, transLowGUID); std::ostringstream ss; @@ -20620,8 +20620,8 @@ void Player::SaveToDB(LoginDatabaseTransaction loginTransaction, CharacterDataba stmt->setFloat(index++, finiteAlways(GetTransOffsetZ())); stmt->setFloat(index++, finiteAlways(GetTransOffsetO())); ObjectGuid::LowType transLowGUID = UI64LIT(0); - if (GetTransport()) - transLowGUID = GetTransport()->GetGUID().GetCounter(); + if (Transport* transport = dynamic_cast<Transport*>(GetTransport())) + transLowGUID = transport->GetGUID().GetCounter(); stmt->setUInt64(index++, transLowGUID); std::ostringstream ss; @@ -24168,17 +24168,6 @@ inline void UpdateVisibilityOf_helper(GuidUnorderedSet& s64, T* target, std::set } template<> -inline void UpdateVisibilityOf_helper(GuidUnorderedSet& s64, GameObject* target, std::set<Unit*>& /*v*/) -{ - // @HACK: This is to prevent objects like deeprun tram from disappearing when player moves far from its spawn point while riding it - // But exclude stoppable elevators from this hack - they would be teleporting from one end to another - // if affected transports move so far horizontally that it causes them to run out of visibility range then you are out of luck - // fix visibility instead of adding hacks here - if (!target->IsDynTransport()) - s64.insert(target->GetGUID()); -} - -template<> inline void UpdateVisibilityOf_helper(GuidUnorderedSet& s64, Creature* target, std::set<Unit*>& v) { s64.insert(target->GetGUID()); diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp index 73e7e610e67..926ead58655 100644 --- a/src/server/game/Entities/Transport/Transport.cpp +++ b/src/server/game/Entities/Transport/Transport.cpp @@ -90,7 +90,7 @@ void TransportBase::UpdatePassengerPosition(Map* map, WorldObject* passenger, fl } Transport::Transport() : GameObject(), - _transportInfo(nullptr), _isMoving(true), _pendingStop(false), + _transportInfo(nullptr), _pathProgress(0), _isMoving(true), _pendingStop(false), _triggeredArrivalEvent(false), _triggeredDepartureEvent(false), _passengerTeleportItr(_passengers.begin()), _delayedAddModel(false), _delayedTeleport(false) { @@ -149,7 +149,7 @@ bool Transport::Create(ObjectGuid::LowType guidlow, uint32 entry, uint32 mapid, ReplaceAllFlags(GameObjectFlags(goOverride->Flags)); } - m_goValue.Transport.PathProgress = 0; + _pathProgress = 0; SetObjectScale(goinfo->size); SetPeriod(tInfo->pathTime); SetEntry(goinfo->entry); @@ -190,9 +190,9 @@ void Transport::Update(uint32 diff) return; if (IsMoving() || !_pendingStop) - m_goValue.Transport.PathProgress += diff; + _pathProgress += diff; - uint32 timer = m_goValue.Transport.PathProgress % GetTransportPeriod(); + uint32 timer = _pathProgress % GetTransportPeriod(); bool justStopped = false; // Set current waypoint @@ -216,9 +216,9 @@ void Transport::Update(uint32 diff) if (_pendingStop && GetGoState() != GO_STATE_READY) { SetGoState(GO_STATE_READY); - m_goValue.Transport.PathProgress = (m_goValue.Transport.PathProgress / GetTransportPeriod()); - m_goValue.Transport.PathProgress *= GetTransportPeriod(); - m_goValue.Transport.PathProgress += _currentFrame->ArriveTime; + _pathProgress = (_pathProgress / GetTransportPeriod()); + _pathProgress *= GetTransportPeriod(); + _pathProgress += _currentFrame->ArriveTime; } break; // its a stop frame and we are waiting } @@ -321,7 +321,7 @@ void Transport::AddPassenger(WorldObject* passenger) } } -void Transport::RemovePassenger(WorldObject* passenger) +Transport* Transport::RemovePassenger(WorldObject* passenger) { bool erased = false; if (_passengerTeleportItr != _passengers.end()) @@ -351,6 +351,8 @@ void Transport::RemovePassenger(WorldObject* passenger) plr->SetFallInformation(0, plr->GetPositionZ()); } } + + return this; } Creature* Transport::CreateNPCPassenger(ObjectGuid::LowType guid, CreatureData const* data) @@ -565,6 +567,11 @@ TempSummon* Transport::SummonPassenger(uint32 entry, Position const& pos, TempSu return summon; } +int32 Transport::GetMapIdForSpawning() const +{ + return GetGOInfo()->moTransport.SpawnMap; +} + void Transport::UpdatePosition(float x, float y, float z, float o) { bool newActive = GetMap()->IsGridLoaded(x, y); diff --git a/src/server/game/Entities/Transport/Transport.h b/src/server/game/Entities/Transport/Transport.h index 0433cacebcd..2166fade720 100644 --- a/src/server/game/Entities/Transport/Transport.h +++ b/src/server/game/Entities/Transport/Transport.h @@ -43,8 +43,8 @@ class TC_GAME_API Transport : public GameObject, public TransportBase void BuildUpdate(UpdateDataMapType& data_map) override; - void AddPassenger(WorldObject* passenger); - void RemovePassenger(WorldObject* passenger); + void AddPassenger(WorldObject* passenger) override; + Transport* RemovePassenger(WorldObject* passenger) override; PassengerSet const& GetPassengers() const { return _passengers; } Creature* CreateNPCPassenger(ObjectGuid::LowType guid, CreatureData const* data); @@ -68,21 +68,27 @@ class TC_GAME_API Transport : public GameObject, public TransportBase */ TempSummon* SummonPassenger(uint32 entry, Position const& pos, TempSummonType summonType, SummonPropertiesEntry const* properties = nullptr, uint32 duration = 0, Unit* summoner = nullptr, uint32 spellId = 0, uint32 vehId = 0); + ObjectGuid GetTransportGUID() const override { return GetGUID(); } + + float GetTransportOrientation() const override { return GetOrientation(); } + /// This method transforms supplied transport offsets into global coordinates void CalculatePassengerPosition(float& x, float& y, float& z, float* o = nullptr) const override { - TransportBase::CalculatePassengerPosition(x, y, z, o, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation()); + TransportBase::CalculatePassengerPosition(x, y, z, o, GetPositionX(), GetPositionY(), GetPositionZ(), GetTransportOrientation()); } /// This method transforms supplied global coordinates into local offsets void CalculatePassengerOffset(float& x, float& y, float& z, float* o = nullptr) const override { - TransportBase::CalculatePassengerOffset(x, y, z, o, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation()); + TransportBase::CalculatePassengerOffset(x, y, z, o, GetPositionX(), GetPositionY(), GetPositionZ(), GetTransportOrientation()); } - uint32 GetTransportPeriod() const override { return m_gameObjectData->Level; } + int32 GetMapIdForSpawning() const override; + + uint32 GetTransportPeriod() const { return m_gameObjectData->Level; } void SetPeriod(uint32 period) { SetLevel(period); } - uint32 GetTimer() const { return GetGOValue()->Transport.PathProgress; } + uint32 GetTimer() const { return _pathProgress; } KeyFrameVec const& GetKeyFrames() const { return _transportInfo->keyFrames; } @@ -118,6 +124,7 @@ class TC_GAME_API Transport : public GameObject, public TransportBase KeyFrameVec::const_iterator _currentFrame; KeyFrameVec::const_iterator _nextFrame; + uint32 _pathProgress; TimeTracker _positionChangeTimer; bool _isMoving; bool _pendingStop; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index b5660911c22..b78cfb76e25 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -11470,7 +11470,7 @@ ObjectGuid Unit::GetTransGUID() const if (GetVehicle()) return GetVehicleBase()->GetGUID(); if (GetTransport()) - return GetTransport()->GetGUID(); + return GetTransport()->GetTransportGUID(); return ObjectGuid::Empty; } @@ -12074,7 +12074,7 @@ void Unit::_EnterVehicle(Vehicle* vehicle, int8 seatId, AuraApplication const* a } ASSERT(!m_vehicle); - (void)vehicle->AddPassenger(this, seatId); + (void)vehicle->AddVehiclePassenger(this, seatId); } void Unit::ChangeSeat(int8 seatId, bool next) diff --git a/src/server/game/Entities/Vehicle/Vehicle.cpp b/src/server/game/Entities/Vehicle/Vehicle.cpp index 4279d472dc5..cab96560c1d 100644 --- a/src/server/game/Entities/Vehicle/Vehicle.cpp +++ b/src/server/game/Entities/Vehicle/Vehicle.cpp @@ -417,7 +417,7 @@ void Vehicle::InstallAccessory(uint32 entry, int8 seatId, bool minion, uint8 typ * @return true if it succeeds, false if it fails. */ -bool Vehicle::AddPassenger(Unit* unit, int8 seatId) +bool Vehicle::AddVehiclePassenger(Unit* unit, int8 seatId) { /// @Prevent adding passengers when vehicle is uninstalling. (Bad script in OnUninstall/OnRemovePassenger/PassengerBoarded hook.) if (_status == STATUS_UNINSTALLING) @@ -488,8 +488,12 @@ bool Vehicle::AddPassenger(Unit* unit, int8 seatId) * @param [in, out] unit The passenger to remove. */ -Vehicle* Vehicle::RemovePassenger(Unit* unit) +Vehicle* Vehicle::RemovePassenger(WorldObject* passenger) { + Unit* unit = passenger->ToUnit(); + if (!unit) + return nullptr; + if (unit->GetVehicle() != this) return nullptr; @@ -928,7 +932,7 @@ void VehicleJoinEvent::Abort(uint64) Target->RemovePendingEvent(this); /// @SPELL_AURA_CONTROL_VEHICLE auras can be applied even when the passenger is not (yet) on the vehicle. - /// When this code is triggered it means that something went wrong in @Vehicle::AddPassenger, and we should remove + /// When this code is triggered it means that something went wrong in @Vehicle::AddVehiclePassenger, and we should remove /// the aura manually. Target->GetBase()->RemoveAurasByType(SPELL_AURA_CONTROL_VEHICLE, Passenger->GetGUID()); } diff --git a/src/server/game/Entities/Vehicle/Vehicle.h b/src/server/game/Entities/Vehicle/Vehicle.h index b6d88793419..47c9d1e0d64 100644 --- a/src/server/game/Entities/Vehicle/Vehicle.h +++ b/src/server/game/Entities/Vehicle/Vehicle.h @@ -59,9 +59,8 @@ class TC_GAME_API Vehicle : public TransportBase VehicleSeatAddon const* GetSeatAddonForSeatOfPassenger(Unit const* passenger) const; uint8 GetAvailableSeatCount() const; - bool AddPassenger(Unit* passenger, int8 seatId = -1); - void EjectPassenger(Unit* passenger, Unit* controller); - Vehicle* RemovePassenger(Unit* passenger); + bool AddVehiclePassenger(Unit* unit, int8 seatId = -1); + Vehicle* RemovePassenger(WorldObject* passenger) override; void RelocatePassengers(); void RemoveAllPassengers(); bool IsVehicleInUse() const; @@ -92,6 +91,12 @@ class TC_GAME_API Vehicle : public TransportBase void InitMovementInfoForBase(); + ObjectGuid GetTransportGUID() const override { return GetBase()->GetGUID(); } + + float GetTransportOrientation() const override { return GetBase()->GetOrientation(); } + + void AddPassenger(WorldObject* /*passenger*/) override { ABORT_MSG("Vehicle cannot directly gain passengers without auras"); } + /// This method transforms supplied transport offsets into global coordinates void CalculatePassengerPosition(float& x, float& y, float& z, float* o /*= nullptr*/) const override { @@ -108,6 +113,8 @@ class TC_GAME_API Vehicle : public TransportBase GetBase()->GetPositionZ(), GetBase()->GetOrientation()); } + int32 GetMapIdForSpawning() const override { return GetBase()->GetMapId(); } + void RemovePendingEvent(VehicleJoinEvent* e); void RemovePendingEventsForSeat(int8 seatId); diff --git a/src/server/game/Entities/Vehicle/VehicleDefines.h b/src/server/game/Entities/Vehicle/VehicleDefines.h index 9d1e84bfb79..82261db0f0c 100644 --- a/src/server/game/Entities/Vehicle/VehicleDefines.h +++ b/src/server/game/Entities/Vehicle/VehicleDefines.h @@ -157,12 +157,20 @@ protected: virtual ~TransportBase() { } public: + virtual ObjectGuid GetTransportGUID() const = 0; + /// This method transforms supplied transport offsets into global coordinates virtual void CalculatePassengerPosition(float& x, float& y, float& z, float* o = nullptr) const = 0; /// This method transforms supplied global coordinates into local offsets virtual void CalculatePassengerOffset(float& x, float& y, float& z, float* o = nullptr) const = 0; + virtual float GetTransportOrientation() const = 0; + + virtual void AddPassenger(WorldObject* passenger) = 0; + + virtual TransportBase* RemovePassenger(WorldObject* passenger) = 0; + void UpdatePassengerPosition(Map* map, WorldObject* passenger, float x, float y, float z, float o, bool setHomePosition); static void CalculatePassengerPosition(float& x, float& y, float& z, float* o, float transX, float transY, float transZ, float transO) @@ -188,6 +196,8 @@ public: y = (iny - inx * std::tan(transO)) / (std::cos(transO) + std::sin(transO) * std::tan(transO)); x = (inx + iny * std::tan(transO)) / (std::cos(transO) + std::sin(transO) * std::tan(transO)); } + + virtual int32 GetMapIdForSpawning() const = 0; }; #endif diff --git a/src/server/game/Grids/Notifiers/GridNotifiers.cpp b/src/server/game/Grids/Notifiers/GridNotifiers.cpp index 84e035fb9ff..5941bdee7ec 100644 --- a/src/server/game/Grids/Notifiers/GridNotifiers.cpp +++ b/src/server/game/Grids/Notifiers/GridNotifiers.cpp @@ -30,7 +30,7 @@ void VisibleNotifier::SendToSelf() { // at this moment i_clientGUIDs have guids that not iterate at grid level checks // but exist one case when this possible and object not out of range: transports - if (Transport* transport = i_player.GetTransport()) + if (Transport* transport = dynamic_cast<Transport*>(i_player.GetTransport())) { for (Transport::PassengerSet::const_iterator itr = transport->GetPassengers().begin(); itr != transport->GetPassengers().end(); ++itr) { @@ -54,6 +54,9 @@ void VisibleNotifier::SendToSelf() case TYPEID_DYNAMICOBJECT: i_player.UpdateVisibilityOf((*itr)->ToDynObject(), i_data, i_visibleNow); break; + case TYPEID_AREATRIGGER: + i_player.UpdateVisibilityOf((*itr)->ToAreaTrigger(), i_data, i_visibleNow); + break; default: break; } diff --git a/src/server/game/Handlers/MovementHandler.cpp b/src/server/game/Handlers/MovementHandler.cpp index 4fd59ea3aa7..2c8f70db994 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -357,28 +357,30 @@ void WorldSession::HandleMovementOpcode(OpcodeClient opcode, MovementInfo& movem { if (!plrMover->GetTransport()) { - if (Transport* transport = plrMover->GetMap()->GetTransport(movementInfo.transport.guid)) - transport->AddPassenger(plrMover); + if (GameObject* go = plrMover->GetMap()->GetGameObject(movementInfo.transport.guid)) + if (TransportBase* transport = go->ToTransportBase()) + transport->AddPassenger(plrMover); } - else if (plrMover->GetTransport()->GetGUID() != movementInfo.transport.guid) + else if (plrMover->GetTransport()->GetTransportGUID() != movementInfo.transport.guid) { plrMover->GetTransport()->RemovePassenger(plrMover); - if (Transport* transport = plrMover->GetMap()->GetTransport(movementInfo.transport.guid)) - transport->AddPassenger(plrMover); + if (GameObject* go = plrMover->GetMap()->GetGameObject(movementInfo.transport.guid)) + { + if (TransportBase* transport = go->ToTransportBase()) + transport->AddPassenger(plrMover); + else + movementInfo.ResetTransport(); + } else movementInfo.ResetTransport(); } } if (!mover->GetTransport() && !mover->GetVehicle()) - { - GameObject* go = mover->GetMap()->GetGameObject(movementInfo.transport.guid); - if (!go || go->GetGoType() != GAMEOBJECT_TYPE_TRANSPORT) - movementInfo.transport.Reset(); - } + movementInfo.transport.Reset(); } else if (plrMover && plrMover->GetTransport()) // if we were on a transport, leave - plrMover->m_transport->RemovePassenger(plrMover); + plrMover->GetTransport()->RemovePassenger(plrMover); // fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map). if (opcode == CMSG_MOVE_FALL_LAND && plrMover && !plrMover->IsInFlight()) diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index ac15beaefb6..738d0d296c8 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -1265,9 +1265,7 @@ void Map::GameObjectRelocation(GameObject* go, float x, float y, float z, float else { go->Relocate(x, y, z, orientation); - go->UpdateModelPosition(); - go->UpdatePositionData(); - go->UpdateObjectVisibility(false); + go->AfterRelocation(); RemoveGameObjectFromMoveList(go); } @@ -1491,9 +1489,7 @@ void Map::MoveAllGameObjectsInMoveList() { // update pos go->Relocate(go->_newPosition); - go->UpdateModelPosition(); - go->UpdatePositionData(); - go->UpdateObjectVisibility(false); + go->AfterRelocation(); } else { @@ -3028,7 +3024,7 @@ void Map::SendInitSelf(Player* player) UpdateData data(player->GetMapId()); // attach to player data current transport data - if (Transport* transport = player->GetTransport()) + if (Transport* transport = dynamic_cast<Transport*>(player->GetTransport())) { transport->BuildCreateUpdateBlockForPlayer(&data, player); player->m_visibleTransports.insert(transport->GetGUID()); @@ -3038,7 +3034,7 @@ void Map::SendInitSelf(Player* player) player->BuildCreateUpdateBlockForPlayer(&data, player); // build other passengers at transport also (they always visible and marked as visible and will not send at visibility update at add to map - if (Transport* transport = player->GetTransport()) + if (Transport* transport = dynamic_cast<Transport*>(player->GetTransport())) for (WorldObject* passenger : transport->GetPassengers()) if (player != passenger && player->HaveAtClient(passenger)) passenger->BuildCreateUpdateBlockForPlayer(&data, player); diff --git a/src/server/game/Maps/TransportMgr.cpp b/src/server/game/Maps/TransportMgr.cpp index 871f43de6c1..5bcd0db73b0 100644 --- a/src/server/game/Maps/TransportMgr.cpp +++ b/src/server/game/Maps/TransportMgr.cpp @@ -447,6 +447,15 @@ void TransportMgr::AddPathNodeToTransport(uint32 transportEntry, uint32 timeSeg, animNode.Path[timeSeg] = node; } +void TransportMgr::AddPathRotationToTransport(uint32 transportEntry, uint32 timeSeg, TransportRotationEntry const* node) +{ + TransportAnimation& animNode = _transportAnimations[transportEntry]; + animNode.Rotations[timeSeg] = node; + + if (animNode.Path.empty() && animNode.TotalTime < timeSeg) + animNode.TotalTime = timeSeg; +} + Transport* TransportMgr::CreateTransport(uint32 entry, ObjectGuid::LowType guid /*= 0*/, Map* map /*= nullptr*/, uint8 phaseUseFlags /*= 0*/, uint32 phaseId /*= 0*/, uint32 phaseGroupId /*= 0*/) { // instance case, execute GetGameObjectEntry hook @@ -551,20 +560,50 @@ TransportSpawn const* TransportMgr::GetTransportSpawn(ObjectGuid::LowType spawnI return Trinity::Containers::MapGetValuePtr(_transportSpawns, spawnId); } -TransportAnimationEntry const* TransportAnimation::GetAnimNode(uint32 time) const +TransportAnimationEntry const* TransportAnimation::GetPrevAnimNode(uint32 time) const +{ + if (Path.empty()) + return nullptr; + + auto itr = Path.lower_bound(time); + if (itr != Path.begin()) + return std::prev(itr)->second; + + return Path.rbegin()->second; +} + +TransportRotationEntry const* TransportAnimation::GetPrevAnimRotation(uint32 time) const +{ + if (Rotations.empty()) + return nullptr; + + auto itr = Rotations.lower_bound(time); + if (itr != Rotations.begin()) + return std::prev(itr)->second; + + return Rotations.rbegin()->second; +} + +TransportAnimationEntry const* TransportAnimation::GetNextAnimNode(uint32 time) const { + if (Path.empty()) + return nullptr; + auto itr = Path.lower_bound(time); if (itr != Path.end()) return itr->second; - return nullptr; + return Path.begin()->second; } -TransportRotationEntry const* TransportAnimation::GetAnimRotation(uint32 time) const +TransportRotationEntry const* TransportAnimation::GetNextAnimRotation(uint32 time) const { + if (Rotations.empty()) + return nullptr; + auto itr = Rotations.lower_bound(time); if (itr != Rotations.end()) return itr->second; - return nullptr; + return Rotations.begin()->second; } diff --git a/src/server/game/Maps/TransportMgr.h b/src/server/game/Maps/TransportMgr.h index 58fa2ecc807..92ce809faee 100644 --- a/src/server/game/Maps/TransportMgr.h +++ b/src/server/game/Maps/TransportMgr.h @@ -91,8 +91,11 @@ struct TC_GAME_API TransportAnimation std::map<uint32, TransportRotationEntry const*> Rotations; uint32 TotalTime; - TransportAnimationEntry const* GetAnimNode(uint32 time) const; - TransportRotationEntry const* GetAnimRotation(uint32 time) const; + TransportAnimationEntry const* GetPrevAnimNode(uint32 time) const; + TransportRotationEntry const* GetPrevAnimRotation(uint32 time) const; + + TransportAnimationEntry const* GetNextAnimNode(uint32 time) const; + TransportRotationEntry const* GetNextAnimRotation(uint32 time) const; }; struct TransportSpawn @@ -143,10 +146,7 @@ class TC_GAME_API TransportMgr void AddPathNodeToTransport(uint32 transportEntry, uint32 timeSeg, TransportAnimationEntry const* node); - void AddPathRotationToTransport(uint32 transportEntry, uint32 timeSeg, TransportRotationEntry const* node) - { - _transportAnimations[transportEntry].Rotations[timeSeg] = node; - } + void AddPathRotationToTransport(uint32 transportEntry, uint32 timeSeg, TransportRotationEntry const* node); // Container storing transport templates std::unordered_map<uint32, TransportTemplate> _transportTemplates; diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index 20107d2e581..80bd1f58270 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -2820,7 +2820,7 @@ enum GameObjectFlags DEFINE_ENUM_FLAG(GameObjectFlags); -enum GameObjectDynamicLowFlags +enum GameObjectDynamicLowFlags : uint16 { GO_DYNFLAG_LO_HIDE_MODEL = 0x0002, // Object model is not shown with this flag GO_DYNFLAG_LO_ACTIVATE = 0x0004, // enables interaction with GO diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp index 937339fbcc5..f08a17407a1 100644 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp @@ -301,9 +301,9 @@ void WaypointMovementGenerator<Creature>::StartMove(Creature* owner, bool relaun owner->SetHomePosition(x, y, z, o); else { - if (Transport* trans = owner->GetTransport()) + if (TransportBase* trans = owner->GetTransport()) { - o -= trans->GetOrientation(); + o -= trans->GetTransportOrientation(); owner->SetTransportHomePosition(x, y, z, o); trans->CalculatePassengerPosition(x, y, z, &o); owner->SetHomePosition(x, y, z, o); diff --git a/src/server/game/Movement/Spline/MoveSplineInit.cpp b/src/server/game/Movement/Spline/MoveSplineInit.cpp index 4f68c03bcd8..fbbdc22c8d0 100644 --- a/src/server/game/Movement/Spline/MoveSplineInit.cpp +++ b/src/server/game/Movement/Spline/MoveSplineInit.cpp @@ -233,8 +233,8 @@ namespace Movement { if (Unit* vehicle = unit->GetVehicleBase()) angle -= vehicle->GetOrientation(); - else if (Transport* transport = unit->GetTransport()) - angle -= transport->GetOrientation(); + else if (TransportBase* transport = unit->GetTransport()) + angle -= transport->GetTransportOrientation(); } args.facing.angle = G3D::wrap(angle, 0.f, (float)G3D::twoPi()); diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index f03829e4642..69dd3e9fcee 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -1207,7 +1207,7 @@ public: static bool HandleDebugTransportCommand(ChatHandler* handler, std::string operation) { - Transport* transport = handler->GetPlayer()->GetTransport(); + Transport* transport = dynamic_cast<Transport*>(handler->GetPlayer()->GetTransport()); if (!transport) return false; diff --git a/src/server/scripts/Commands/cs_misc.cpp b/src/server/scripts/Commands/cs_misc.cpp index f944120ee3e..367b42e9ec1 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -283,7 +283,7 @@ public: zoneId, (zoneEntry ? zoneEntry->AreaName[handler->GetSessionDbcLocale()] : unknown), areaId, (areaEntry ? areaEntry->AreaName[handler->GetSessionDbcLocale()] : unknown), object->GetPositionX(), object->GetPositionY(), object->GetPositionZ(), object->GetOrientation()); - if (Transport* transport = object->GetTransport()) + if (Transport* transport = dynamic_cast<Transport*>(object->GetTransport())) handler->PSendSysMessage(LANG_TRANSPORT_POSITION, transport->GetGOInfo()->moTransport.SpawnMap, object->GetTransOffsetX(), object->GetTransOffsetY(), object->GetTransOffsetZ(), object->GetTransOffsetO(), transport->GetEntry(), transport->GetName().c_str()); diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp index e7198d0afff..e48c800b294 100644 --- a/src/server/scripts/Commands/cs_npc.cpp +++ b/src/server/scripts/Commands/cs_npc.cpp @@ -124,7 +124,7 @@ public: Player* chr = handler->GetSession()->GetPlayer(); Map* map = chr->GetMap(); - if (Transport* trans = chr->GetTransport()) + if (Transport* trans = dynamic_cast<Transport*>(chr->GetTransport())) { ObjectGuid::LowType guid = sObjectMgr->GenerateCreatureSpawnId(); CreatureData& data = sObjectMgr->NewOrExistCreatureData(guid); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp index 9927849fe6b..7cd4452a088 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp @@ -490,12 +490,15 @@ private: class ResetEncounterEvent : public BasicEvent { public: - ResetEncounterEvent(Unit* caster, uint32 spellId, ObjectGuid otherTransport) : _caster(caster), _spellId(spellId), _otherTransport(otherTransport) { } + ResetEncounterEvent(Unit* caster, uint32 spellId, ObjectGuid transport, ObjectGuid otherTransport) : _caster(caster), _spellId(spellId), + _transport(transport), _otherTransport(otherTransport) { } bool Execute(uint64, uint32) override { _caster->CastSpell(_caster, _spellId, true); - _caster->GetTransport()->AddObjectToRemoveList(); + + if (Transport* go = HashMapHolder<Transport>::Find(_transport)) + go->AddObjectToRemoveList(); if (Transport* go = HashMapHolder<Transport>::Find(_otherTransport)) go->AddObjectToRemoveList(); @@ -506,6 +509,7 @@ public: private: Unit* _caster; uint32 _spellId; + ObjectGuid _transport; ObjectGuid _otherTransport; }; @@ -688,7 +692,7 @@ class npc_gunship : public CreatureScript if (_summonedFirstMage) return; - if (me->GetTransport()->GetEntry() != uint32(_teamInInstance == HORDE ? GO_THE_SKYBREAKER_H : GO_ORGRIMS_HAMMER_A)) + if (me->GetTransport()->GetTransportGUID() != me->GetInstanceScript()->GetGuidData(DATA_ENEMY_GUNSHIP)) return; if (!me->HealthBelowPctDamaged(90, damage)) @@ -706,8 +710,8 @@ class npc_gunship : public CreatureScript _died = true; - bool isVictory = me->GetTransport()->GetEntry() == GO_THE_SKYBREAKER_H || me->GetTransport()->GetEntry() == GO_ORGRIMS_HAMMER_A; InstanceScript* instance = me->GetInstanceScript(); + bool isVictory = me->GetTransport()->GetTransportGUID() == instance->GetGuidData(DATA_ENEMY_GUNSHIP); instance->SetBossState(DATA_ICECROWN_GUNSHIP_BATTLE, isVictory ? DONE : FAIL); if (Creature* creature = me->FindNearestCreature(me->GetEntry() == NPC_ORGRIMS_HAMMER ? NPC_THE_SKYBREAKER : NPC_ORGRIMS_HAMMER, 200.0f)) { @@ -770,7 +774,9 @@ class npc_gunship : public CreatureScript if (Transport* otherTransport = HashMapHolder<Transport>::Find(instance->GetGuidData(DATA_ICECROWN_GUNSHIP_BATTLE))) otherTransport->EnableMovement(true); - me->GetTransport()->EnableMovement(true); + + if (Transport* transport = dynamic_cast<Transport*>(me->GetTransport())) + transport->EnableMovement(true); if (Creature* ship = me->FindNearestCreature(_teamInInstance == HORDE ? NPC_ORGRIMS_HAMMER : NPC_THE_SKYBREAKER, 200.0f)) { @@ -790,8 +796,8 @@ class npc_gunship : public CreatureScript else { uint32 teleportSpellId = _teamInInstance == HORDE ? SPELL_TELEPORT_PLAYERS_ON_RESET_H : SPELL_TELEPORT_PLAYERS_ON_RESET_A; - me->m_Events.AddEvent(new ResetEncounterEvent(me, teleportSpellId, me->GetInstanceScript()->GetGuidData(DATA_ENEMY_GUNSHIP)), - me->m_Events.CalculateTime(8s)); + me->m_Events.AddEventAtOffset(new ResetEncounterEvent(me, teleportSpellId, instance->GetGuidData(DATA_ICECROWN_GUNSHIP_BATTLE), + instance->GetGuidData(DATA_ENEMY_GUNSHIP)), 8s); } } @@ -841,7 +847,7 @@ struct npc_high_overlord_saurfang_igb : public ScriptedAI _instance(creature->GetInstanceScript()) { _controller.ResetSlots(HORDE); - _controller.SetTransport(creature->GetTransport()); + _controller.SetTransport(dynamic_cast<Transport*>(creature->GetTransport())); me->SetRegenerateHealth(false); me->m_CombatDistance = 70.0f; _firstMageCooldown = GameTime::Now() + 60s; @@ -951,7 +957,8 @@ struct npc_high_overlord_saurfang_igb : public ScriptedAI bool OnGossipSelect(Player* /*player*/, uint32 /*menuId*/, uint32 /*gossipListId*/) override { me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP); - me->GetTransport()->EnableMovement(true); + if (Transport* transport = dynamic_cast<Transport*>(me->GetTransport())) + transport->EnableMovement(true); _events.SetPhase(PHASE_INTRO); _events.ScheduleEvent(EVENT_INTRO_H_1, 5s, 0, PHASE_INTRO); _events.ScheduleEvent(EVENT_INTRO_H_2, 16s, 0, PHASE_INTRO); @@ -1019,7 +1026,7 @@ struct npc_high_overlord_saurfang_igb : public ScriptedAI _controller.SummonCreatures(SLOT_MAGE_1, SLOT_MAGE_2); _controller.SummonCreatures(SLOT_MARINE_1, Is25ManRaid() ? SLOT_MARINE_4 : SLOT_MARINE_2); _controller.SummonCreatures(SLOT_SERGEANT_1, Is25ManRaid() ? SLOT_SERGEANT_2 : SLOT_SERGEANT_1); - if (Transport* orgrimsHammer = me->GetTransport()) + if (Transport* orgrimsHammer = dynamic_cast<Transport*>(me->GetTransport())) orgrimsHammer->SummonPassenger(NPC_TELEPORT_PORTAL, OrgrimsHammerTeleportPortal, TEMPSUMMON_TIMED_DESPAWN, nullptr, 21000); if (Transport* skybreaker = HashMapHolder<Transport>::Find(_instance->GetGuidData(DATA_ICECROWN_GUNSHIP_BATTLE))) @@ -1094,7 +1101,7 @@ struct npc_muradin_bronzebeard_igb : public ScriptedAI _instance(creature->GetInstanceScript()) { _controller.ResetSlots(ALLIANCE); - _controller.SetTransport(creature->GetTransport()); + _controller.SetTransport(dynamic_cast<Transport*>(creature->GetTransport())); me->SetRegenerateHealth(false); me->m_CombatDistance = 70.0f; _firstMageCooldown = GameTime::Now() + 60s; @@ -1204,7 +1211,8 @@ struct npc_muradin_bronzebeard_igb : public ScriptedAI bool OnGossipSelect(Player* /*player*/, uint32 /*menuId*/, uint32 /*gossipListId*/) override { me->RemoveNpcFlag(UNIT_NPC_FLAG_GOSSIP); - me->GetTransport()->EnableMovement(true); + if (Transport* transport = dynamic_cast<Transport*>(me->GetTransport())) + transport->EnableMovement(true); _events.SetPhase(PHASE_INTRO); _events.ScheduleEvent(EVENT_INTRO_A_1, 5s); _events.ScheduleEvent(EVENT_INTRO_A_2, 10s, 0, PHASE_INTRO); @@ -1276,7 +1284,7 @@ struct npc_muradin_bronzebeard_igb : public ScriptedAI _controller.SummonCreatures(SLOT_MAGE_1, SLOT_MAGE_2); _controller.SummonCreatures(SLOT_MARINE_1, Is25ManRaid() ? SLOT_MARINE_4 : SLOT_MARINE_2); _controller.SummonCreatures(SLOT_SERGEANT_1, Is25ManRaid() ? SLOT_SERGEANT_2 : SLOT_SERGEANT_1); - if (Transport* skybreaker = me->GetTransport()) + if (Transport* skybreaker = dynamic_cast<Transport*>(me->GetTransport())) skybreaker->SummonPassenger(NPC_TELEPORT_PORTAL, SkybreakerTeleportPortal, TEMPSUMMON_TIMED_DESPAWN, nullptr, 21000); if (Transport* orgrimsHammer = HashMapHolder<Transport>::Find(_instance->GetGuidData(DATA_ICECROWN_GUNSHIP_BATTLE))) @@ -1402,7 +1410,7 @@ struct npc_gunship_boarding_addAI : public gunship_npc_AI float x, y, z, o; otherTransportPos.GetPosition(x, y, z, o); - Transport* myTransport = me->GetTransport(); + TransportBase* myTransport = me->GetTransport(); if (!myTransport) return; @@ -2010,7 +2018,7 @@ class spell_igb_burning_pitch_selector : public SpellScript targets.remove_if([team](WorldObject* target) -> bool { - if (Transport* transport = target->GetTransport()) + if (Transport* transport = dynamic_cast<Transport*>(target->GetTransport())) return transport->GetEntry() != uint32(team == HORDE ? GO_ORGRIMS_HAMMER_H : GO_THE_SKYBREAKER_A); return true; }); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/go_icecrown_citadel_teleport.cpp b/src/server/scripts/Northrend/IcecrownCitadel/go_icecrown_citadel_teleport.cpp index 7e1259fdcc4..72bdf4c087f 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/go_icecrown_citadel_teleport.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/go_icecrown_citadel_teleport.cpp @@ -72,7 +72,7 @@ class icecrown_citadel_teleport : public GameObjectScript // If the player is on the ship, Unit::NearTeleport() will try to keep the player on the ship, causing issues. // For that we simply always remove the player from the ship. - if (Transport* transport = player->GetTransport()) + if (TransportBase* transport = player->GetTransport()) transport->RemovePassenger(player); player->CastSpell(player, spell->Id, true); diff --git a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h index 8dd11837c8c..1f14b55b647 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h +++ b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel.h @@ -352,7 +352,7 @@ enum ICGameObjectsIds GO_SPIRIT_ALARM_3 = 201816, GO_SPIRIT_ALARM_4 = 201817, - // Lord Marrogar + // Lord Marrowgar GO_DOODAD_ICECROWN_ICEWALL02 = 201910, GO_ICEWALL = 201911, GO_LORD_MARROWGAR_S_ENTRANCE = 201857, diff --git a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp index cedebba8633..4a737757efb 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp @@ -536,7 +536,10 @@ class instance_icecrown_citadel : public InstanceMapScript case GO_LADY_DEATHWHISPER_ELEVATOR: LadyDeathwisperElevatorGUID = go->GetGUID(); if (GetBossState(DATA_LADY_DEATHWHISPER) == DONE) - go->SetTransportState(GO_STATE_TRANSPORT_ACTIVE); + { + go->SetGoState(GO_STATE_TRANSPORT_ACTIVE); + go->HandleCustomTypeCommand(GameObjectType::SetTransportAutoCycleBetweenStopFrames(true)); + } break; case GO_THE_SKYBREAKER_H: case GO_ORGRIMS_HAMMER_A: @@ -874,7 +877,10 @@ class instance_icecrown_citadel : public InstanceMapScript SetTeleporterState(teleporter, true); if (GameObject* elevator = instance->GetGameObject(LadyDeathwisperElevatorGUID)) - elevator->SetTransportState(GO_STATE_TRANSPORT_ACTIVE); + { + elevator->SetGoState(GO_STATE_TRANSPORT_ACTIVE); + elevator->HandleCustomTypeCommand(GameObjectType::SetTransportAutoCycleBetweenStopFrames(true)); + } SpawnGunship(); } |