From 25465699f720529f442b2f22b87e45e9920b09d8 Mon Sep 17 00:00:00 2001 From: Shauren Date: Sat, 18 Jun 2022 21:36:17 +0200 Subject: [PATCH] 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 --- .../game/Entities/Creature/Creature.cpp | 6 +- .../Entities/DynamicObject/DynamicObject.cpp | 2 +- .../game/Entities/GameObject/GameObject.cpp | 569 +++++++++++++----- .../game/Entities/GameObject/GameObject.h | 65 +- .../game/Entities/GameObject/GameObjectData.h | 2 +- src/server/game/Entities/Object/Object.cpp | 48 +- src/server/game/Entities/Object/Object.h | 10 +- src/server/game/Entities/Player/Player.cpp | 25 +- .../game/Entities/Transport/Transport.cpp | 23 +- .../game/Entities/Transport/Transport.h | 19 +- src/server/game/Entities/Unit/Unit.cpp | 4 +- src/server/game/Entities/Vehicle/Vehicle.cpp | 9 +- src/server/game/Entities/Vehicle/Vehicle.h | 13 +- .../game/Entities/Vehicle/VehicleDefines.h | 10 + .../game/Grids/Notifiers/GridNotifiers.cpp | 5 +- src/server/game/Handlers/MovementHandler.cpp | 24 +- src/server/game/Maps/Map.cpp | 12 +- src/server/game/Maps/TransportMgr.cpp | 47 +- src/server/game/Maps/TransportMgr.h | 13 +- src/server/game/Miscellaneous/SharedDefines.h | 5 +- .../WaypointMovementGenerator.cpp | 4 +- .../game/Movement/Spline/MoveSplineInit.cpp | 4 +- src/server/game/Spells/SpellEffects.cpp | 10 +- src/server/scripts/Commands/cs_debug.cpp | 2 +- src/server/scripts/Commands/cs_misc.cpp | 2 +- src/server/scripts/Commands/cs_npc.cpp | 2 +- .../BlackwingDescent/boss_nefarians_end.cpp | 8 +- .../instance_blackwing_descent.cpp | 2 +- .../halls_of_origination.cpp | 5 +- .../boss_icecrown_gunship_battle.cpp | 38 +- .../icecrown_citadel_teleport.cpp | 6 + .../instance_icecrown_citadel.cpp | 10 +- 32 files changed, 681 insertions(+), 323 deletions(-) diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp index 5b7ad836e2..a3d70cd3f2 100644 --- a/src/server/game/Entities/Creature/Creature.cpp +++ b/src/server/game/Entities/Creature/Creature.cpp @@ -1279,7 +1279,11 @@ void Creature::SaveToDB() return; } - uint32 mapId = GetTransport() ? GetTransport()->GetGOInfo()->moTransport.mapID : GetMapId(); + uint32 mapId = GetMapId(); + if (TransportBase* transport = GetTransport()) + if (transport->GetMapIdForSpawning() >= 0) + mapId = transport->GetMapIdForSpawning(); + SaveToDB(mapId, data->spawnMask); } diff --git a/src/server/game/Entities/DynamicObject/DynamicObject.cpp b/src/server/game/Entities/DynamicObject/DynamicObject.cpp index cd5cf275c2..2fa697abb0 100644 --- a/src/server/game/Entities/DynamicObject/DynamicObject.cpp +++ b/src/server/game/Entities/DynamicObject/DynamicObject.cpp @@ -107,7 +107,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 673f2e5fcc..c30f1e5d72 100644 --- a/src/server/game/Entities/GameObject/GameObject.cpp +++ b/src/server/game/Entities/GameObject/GameObject.cpp @@ -21,6 +21,7 @@ #include "CellImpl.h" #include "CreatureAISelector.h" #include "DatabaseEnv.h" +#include "G3DPosition.hpp" #include "GameObjectAI.h" #include "GameEventSender.h" #include "GameObjectModel.h" @@ -59,6 +60,341 @@ 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: + // @todo: chronofy timer trackers for 4.x + //static constexpr Milliseconds PositionUpdateInterval = 50ms; + static constexpr uint8 PositionUpdateInterval = 50; + + 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); + } + } + } + + _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.GetUInt32Value(GAMEOBJECT_LEVEL))) + { + int32 timeToStop = _owner.GetInt32Value(GAMEOBJECT_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.HasFlag(GAMEOBJECT_DYNAMIC, 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; + 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.HasFlag(GAMEOBJECT_DYNAMIC, 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.GetFloatValue(GAMEOBJECT_PARENTROTATION + 0), _owner.GetFloatValue(GAMEOBJECT_PARENTROTATION + 1), + _owner.GetFloatValue(GAMEOBJECT_PARENTROTATION + 2), _owner.GetFloatValue(GAMEOBJECT_PARENTROTATION + 3)).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->X, oldRotation->Y, oldRotation->Z, oldRotation->W); + G3D::Quat next(newRotation->X, newRotation->Y, newRotation->Z, newRotation->W); + + 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.SetUInt32Value(GAMEOBJECT_LEVEL, GameTime::GetGameTimeMS() + timeToStop); + _owner.SetPathProgressForClient(float(_pathProgress) / float(GetTransportPeriod())); + + if (oldState == GO_STATE_ACTIVE || oldState == newState) + { + // initialization + if (int32(_pathProgress) > stopPathProgress) + _owner.SetFlag(GAMEOBJECT_DYNAMIC, GO_DYNFLAG_LO_INVERTED_MOVEMENT); + else + _owner.RemoveFlag(GAMEOBJECT_DYNAMIC, 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.SetFlag(GAMEOBJECT_DYNAMIC, GO_DYNFLAG_LO_INVERTED_MOVEMENT); + else + _owner.RemoveFlag(GAMEOBJECT_DYNAMIC, 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 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 _stopFrames; + bool _autoCycleBetweenStopFrames; + TimeTrackerSmall _positionUpdateTimer; + std::unordered_set _passengers; +}; + +SetTransportAutoCycleBetweenStopFrames::SetTransportAutoCycleBetweenStopFrames(bool on) : _on(on) +{ +} + +void SetTransportAutoCycleBetweenStopFrames::Execute(GameObjectTypeBase& type) const +{ + if (Transport* transport = dynamic_cast(&type)) + transport->SetAutoCycleBetweenStopFrames(_on); +} +} + void GameObjectTemplate::InitializeQueryData() { WorldPacket queryTemp; @@ -154,10 +490,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() @@ -364,32 +696,14 @@ bool GameObject::Create(ObjectGuid::LowType guidlow, uint32 name_id, Map* map, P break; 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(); - - if (goinfo->transport.Timeto2ndfloor > 0) - { - if (!goinfo->transport.startOpen) - m_goValue.Transport.StopFrames->push_back(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); + m_goTypeImpl = std::make_unique(*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: @@ -476,6 +790,9 @@ void GameObject::Update(uint32 diff) } } + if (m_goTypeImpl) + m_goTypeImpl->Update(diff); + switch (m_lootState) { case GO_NOT_READY: @@ -497,53 +814,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) - { - ForceValuesUpdateAtIndex(GAMEOBJECT_LEVEL); - ForceValuesUpdateAtIndex(GAMEOBJECT_BYTES_1); - } - } - } - break; - } case GAMEOBJECT_TYPE_FISHINGNODE: { // fishing code (bobber ready) @@ -1011,7 +1281,12 @@ void GameObject::SaveToDB() return; } - SaveToDB(GetMapId(), data->spawnMask); + uint32 mapId = GetMapId(); + if (TransportBase* transport = GetTransport()) + if (transport->GetMapIdForSpawning() >= 0) + mapId = transport->GetMapIdForSpawning(); + + SaveToDB(mapId, data->spawnMask); } void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask) @@ -1216,7 +1491,7 @@ bool GameObject::IsDynTransport() const if (!gInfo) return false; - return gInfo->type == GAMEOBJECT_TYPE_MO_TRANSPORT || (gInfo->type == GAMEOBJECT_TYPE_TRANSPORT && m_goValue.Transport.StopFrames->empty()); + return gInfo->type == GAMEOBJECT_TYPE_MO_TRANSPORT || gInfo->type == GAMEOBJECT_TYPE_TRANSPORT; } bool GameObject::IsDestructibleBuilding() const @@ -2278,7 +2553,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(GetTransport())) { QuaternionData worldRotation = transport->GetWorldRotation(); @@ -2475,9 +2750,14 @@ void GameObject::SetLootGenerationTime() void GameObject::SetGoState(GOState state) { + GOState oldState = GetGoState(); SetByteValue(GAMEOBJECT_BYTES_1, 0, state); if (AI()) AI()->OnStateChanged(state); + + if (m_goTypeImpl) + m_goTypeImpl->OnStateChanged(oldState, state); + if (m_model && !IsTransport()) { if (!IsInWorld()) @@ -2492,48 +2772,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 something that will nicely divide for GAMEOBJECT_DYNAMIC value calculation - return m_goValue.Transport.PathProgress; -} - -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()); - - uint32 stopFrameTime = m_goValue.Transport.StopFrames->at(stopFrame); - if (!stopFrameTime) // returning to a previous stop frame that has no travel time (e.g. stop frame at 0ms) - stopFrameTime = m_goValue.Transport.StopFrames->at(stopFrame + 1); - - m_goValue.Transport.PathProgress = getMSTime() + stopFrameTime; - SetGoState(GOState(GO_STATE_TRANSPORT_STOPPED + stopFrame)); - } - - ForceValuesUpdateAtIndex(GAMEOBJECT_LEVEL); - ForceValuesUpdateAtIndex(GAMEOBJECT_BYTES_1); -} - void GameObject::SetDisplayId(uint32 displayid) { SetUInt32Value(GAMEOBJECT_DISPLAYID, displayid); @@ -2632,7 +2870,6 @@ void GameObject::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* t if (!target) return; - bool isStoppableTransport = GetGoType() == GAMEOBJECT_TYPE_TRANSPORT && !m_goValue.Transport.StopFrames->empty(); bool forcedFlags = GetGoType() == GAMEOBJECT_TYPE_CHEST && GetGOInfo()->chest.groupLootRules && HasLootRecipient(); bool targetIsGM = target->IsGameMaster(); @@ -2655,8 +2892,7 @@ void GameObject::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* t if (index == GAMEOBJECT_DYNAMIC) { - uint16 dynFlags = 0; - int16 pathProgress = -1; + uint32 dynFlags = m_uint32Values[GAMEOBJECT_DYNAMIC]; switch (GetGoType()) { case GAMEOBJECT_TYPE_QUESTGIVER: @@ -2674,27 +2910,11 @@ void GameObject::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* t if (ActivateToQuest(target)) dynFlags |= GO_DYNFLAG_LO_SPARKLE; break; - case GAMEOBJECT_TYPE_TRANSPORT: - { - float timer = float(m_goValue.Transport.PathProgress % GetTransportPeriod()); - pathProgress = int16(timer / float(GetTransportPeriod()) * 65535.0f); - break; - } - case GAMEOBJECT_TYPE_MO_TRANSPORT: - { - if (uint32 transportPeriod = GetTransportPeriod()) - { - float timer = float(m_goValue.Transport.PathProgress % transportPeriod); - pathProgress = int16(timer / float(transportPeriod) * 65535.0f); - } - break; - } default: break; } - fieldBuffer << uint16(dynFlags); - fieldBuffer << int16(pathProgress); + fieldBuffer << uint32(dynFlags); } else if (index == GAMEOBJECT_FLAGS) { @@ -2705,27 +2925,6 @@ void GameObject::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* t fieldBuffer << goFlags; } - else if (index == GAMEOBJECT_LEVEL) - { - if (isStoppableTransport) - fieldBuffer << uint32(m_goValue.Transport.PathProgress); - else - fieldBuffer << m_uint32Values[index]; - } - else if (index == GAMEOBJECT_BYTES_1) - { - uint32 bytes1 = m_uint32Values[index]; - if (isStoppableTransport && GetGoState() == GO_STATE_TRANSPORT_ACTIVE) - { - if ((m_goValue.Transport.StateUpdateTimer / 20000) & 1) - { - bytes1 &= 0xFFFFFF00; - bytes1 |= GO_STATE_TRANSPORT_STOPPED; - } - } - - fieldBuffer << bytes1; - } else fieldBuffer << m_uint32Values[index]; // other cases } @@ -2735,6 +2934,27 @@ void GameObject::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* t data->append(fieldBuffer); } +std::vector const* GameObject::GetPauseTimes() const +{ + if (GameObjectType::Transport const* transport = dynamic_cast(m_goTypeImpl.get())) + return transport->GetPauseTimes(); + + return nullptr; +} + +void GameObject::SetPathProgressForClient(float progress) +{ + bool hasPendingUpdate = _changesMask.GetBit(GAMEOBJECT_DYNAMIC); + + uint32 dynamicFlags = GetUInt32Value(GAMEOBJECT_DYNAMIC); + dynamicFlags &= 0xFFFF; // remove high bits + dynamicFlags |= uint32(progress * 65535.0f) << 16; + UpdateUInt32Value(GAMEOBJECT_DYNAMIC, dynamicFlags); + + if (!hasPendingUpdate) + _changesMask.UnsetBit(GAMEOBJECT_DYNAMIC); +} + void GameObject::GetRespawnPosition(float &x, float &y, float &z, float* ori /* = nullptr*/) const { if (m_goData) @@ -2753,6 +2973,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(m_goTypeImpl.get()); + case GAMEOBJECT_TYPE_MO_TRANSPORT: + return static_cast(this); + default: + break; + } + + return nullptr; +} + +void GameObject::AfterRelocation() +{ + UpdateModelPosition(); + UpdatePositionData(); + if (m_goTypeImpl) + m_goTypeImpl->OnRelocated(); + + UpdateObjectVisibility(false); +} + float GameObject::GetInteractionDistance() const { switch (GetGoType()) @@ -2821,6 +3066,12 @@ private: GameObject* _owner; }; +void GameObject::HandleCustomTypeCommand(GameObjectTypeBase::CustomCommand const& command) const +{ + if (m_goTypeImpl) + command.Execute(*m_goTypeImpl); +} + GameObjectModel* GameObject::CreateModel() { return GameObjectModel::Create(Trinity::make_unique(this), sWorld->GetDataPath()); diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h index 4ea646e0a1..24a22123f6 100644 --- a/src/server/game/Entities/GameObject/GameObject.h +++ b/src/server/game/Entities/GameObject/GameObject.h @@ -25,27 +25,56 @@ #include "MapObject.h" #include "SharedDefines.h" +class GameObject; class GameObjectAI; class GameObjectModel; class Group; class OPvPCapturePoint; class SpellInfo; class Transport; +class TransportBase; class Unit; struct TransportAnimation; enum TriggerCastFlags : uint32; +// Base class for GameObject type specific implementations +class GameObjectTypeBase +{ +public: + class TC_GAME_API CustomCommand + { + 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 { - //11 GAMEOBJECT_TYPE_TRANSPORT - struct - { - uint32 PathProgress; - TransportAnimation const* AnimationInfo; - uint32 CurrentSeg; - std::vector* StopFrames; - uint32 StateUpdateTimer; - } Transport; //25 GAMEOBJECT_TYPE_FISHINGHOLE struct { @@ -165,14 +194,15 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject void SetGoType(GameobjectTypes type) { SetByteValue(GAMEOBJECT_BYTES_1, 1, type); } GOState GetGoState() const { return GOState(GetByteValue(GAMEOBJECT_BYTES_1, 0)); } void SetGoState(GOState state); - virtual uint32 GetTransportPeriod() const; - void SetTransportState(GOState state, uint32 stopFrame = 0); uint8 GetGoArtKit() const { return GetByteValue(GAMEOBJECT_BYTES_1, 2); } void SetGoArtKit(uint8 artkit); uint8 GetGoAnimProgress() const { return GetByteValue(GAMEOBJECT_BYTES_1, 3); } void SetGoAnimProgress(uint8 animprogress) { SetByteValue(GAMEOBJECT_BYTES_1, 3, animprogress); } static void SetGoArtKit(uint8 artkit, GameObject* go, ObjectGuid::LowType lowguid = 0); + std::vector const* GetPauseTimes() const; + void SetPathProgressForClient(float progress); + void PlayAnimKit(int32 animKit); void EnableCollision(bool enable); @@ -277,6 +307,10 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject GameObjectModel* m_model; void GetRespawnPosition(float &x, float &y, float &z, float* ori = nullptr) const; + TransportBase* ToTransportBase() { return const_cast(const_cast(this)->ToTransportBase()); } + TransportBase const* ToTransportBase() const; + + Transport* ToTransport() { if (GetGOInfo()->type == GAMEOBJECT_TYPE_MO_TRANSPORT) return reinterpret_cast(this); else return nullptr; } Transport const* ToTransport() const { if (GetGOInfo()->type == GAMEOBJECT_TYPE_MO_TRANSPORT) return reinterpret_cast(this); else return nullptr; } @@ -284,8 +318,11 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject float GetStationaryY() const override { if (GetGOInfo()->type != GAMEOBJECT_TYPE_MO_TRANSPORT) return m_stationaryPosition.GetPositionY(); return GetPositionY(); } float GetStationaryZ() const override { if (GetGOInfo()->type != GAMEOBJECT_TYPE_MO_TRANSPORT) return m_stationaryPosition.GetPositionZ(); return GetPositionZ(); } float GetStationaryO() const override { if (GetGOInfo()->type != GAMEOBJECT_TYPE_MO_TRANSPORT) return m_stationaryPosition.GetOrientation(); return 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(); @@ -301,6 +338,8 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject void AIM_Destroy(); bool AIM_Initialize(); + void HandleCustomTypeCommand(GameObjectTypeBase::CustomCommand const& command) const; + protected: GameObjectModel* CreateModel(); void UpdateModel(); // updates model in case displayId were changed @@ -331,7 +370,9 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject GameObjectTemplate const* m_goInfo; GameObjectTemplateAddon const* m_goTemplateAddon; GameObjectData const* m_goData; - GameObjectValue m_goValue; + std::unique_ptr 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 15f9c8e33e..71ce2a1826 100644 --- a/src/server/game/Entities/GameObject/GameObjectData.h +++ b/src/server/game/Entities/GameObject/GameObjectData.h @@ -609,7 +609,6 @@ struct GameObjectTemplate case GAMEOBJECT_TYPE_DUNGEON_DIFFICULTY: return true; case GAMEOBJECT_TYPE_TRAPDOOR: return true; case GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING: return true; - case GAMEOBJECT_TYPE_TRANSPORT: return true; default: return false; } } @@ -625,6 +624,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_CAPTURE_POINT: return capturePoint.GiganticAOI != 0; default: return false; diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp index 8306af553c..62162037bf 100644 --- a/src/server/game/Entities/Object/Object.cpp +++ b/src/server/game/Entities/Object/Object.cpp @@ -350,10 +350,9 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint32 flags) const bool hasMovementAnimKit = false; bool hasMeleeAnimKit = false; - uint32 stopFrameCount = 0; + std::vector const* PauseTimes = nullptr; if (GameObject const* go = ToGameObject()) - if (go->GetGoType() == GAMEOBJECT_TYPE_TRANSPORT) - stopFrameCount = go->GetGOValue()->Transport.StopFrames->size(); + PauseTimes = go->GetPauseTimes(); // Bit content data->WriteBit(flags & UPDATEFLAG_PLAY_HOVER_ANIM); @@ -364,7 +363,7 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint32 flags) const data->WriteBit(flags & UPDATEFLAG_SELF); data->WriteBit(flags & UPDATEFLAG_VEHICLE); data->WriteBit(flags & UPDATEFLAG_LIVING); - data->WriteBits(stopFrameCount, 24); + data->WriteBits(PauseTimes->size(), 24); data->WriteBit(flags & UPDATEFLAG_NO_BIRTH_ANIM); data->WriteBit(flags & UPDATEFLAG_GO_TRANSPORT_POSITION); data->WriteBit(flags & UPDATEFLAG_STATIONARY_POSITION); @@ -481,10 +480,8 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint32 flags) const data->FlushBits(); - // Data - if (GameObject const* go = ToGameObject()) - for (uint32 i = 0; i < stopFrameCount; ++i) - *data << uint32(go->GetGOValue()->Transport.StopFrames->at(i)); + if (PauseTimes && !PauseTimes->empty()) + data->append(PauseTimes->data(), PauseTimes->size()); if (flags & UPDATEFLAG_LIVING) { @@ -658,18 +655,7 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint32 flags) const } if (flags & UPDATEFLAG_TRANSPORT) - { - 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()) - *data << uint32(go->GetGOValue()->Transport.PathProgress); - else - *data << uint32(GameTime::GetGameTimeMS()); - } + *data << uint32(GameTime::GetGameTimeMS()); } void Object::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* target) const @@ -1203,19 +1189,9 @@ void WorldObject::setActive(bool on) return; if (on) - { - if (GetTypeId() == TYPEID_UNIT) - map->AddToActive(this->ToCreature()); - else if (GetTypeId() == TYPEID_DYNAMICOBJECT) - map->AddToActive((DynamicObject*)this); - } + map->AddToActive(this); else - { - if (GetTypeId() == TYPEID_UNIT) - map->RemoveFromActive(this->ToCreature()); - else if (GetTypeId() == TYPEID_DYNAMICOBJECT) - map->RemoveFromActive((DynamicObject*)this); - } + map->RemoveFromActive(this); } void WorldObject::SetFarVisible(bool on) @@ -1240,7 +1216,7 @@ void WorldObject::CleanupsBeforeDelete(bool /*finalCleanup*/) if (IsInWorld()) RemoveFromWorld(); - if (Transport* transport = GetTransport()) + if (TransportBase* transport = GetTransport()) transport->RemovePassenger(this); } @@ -1307,7 +1283,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().GetCounter() == GetTransport()->GetGUID().GetCounter()) + if (GetTransport() && obj->GetTransport() && obj->GetTransport()->GetTransportGUID() == GetTransport()->GetTransportGUID()) { thisOrTransport = &m_movementInfo.transport.pos; objOrObjTransport = &obj->m_movementInfo.transport.pos; @@ -2099,7 +2075,7 @@ TempSummon* Map::SummonCreature(uint32 entry, Position const& pos, SummonCreatur if (summonArgs.Summoner) PhasingHandler::InheritPhaseShift(summon, summonArgs.Summoner); - Transport* transport = summonArgs.Summoner ? summonArgs.Summoner->GetTransport() : nullptr; + TransportBase* transport = summonArgs.Summoner ? summonArgs.Summoner->GetTransport() : nullptr; if (transport) { float x, y, z, o; @@ -2776,7 +2752,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 4af128b7cc..26af0542f0 100644 --- a/src/server/game/Entities/Object/Object.h +++ b/src/server/game/Entities/Object/Object.h @@ -46,7 +46,7 @@ class InstanceScript; class Map; class Player; class TempSummon; -class Transport; +class TransportBase; class Unit; class UpdateData; class WorldObject; @@ -457,7 +457,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(); } @@ -466,7 +466,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; @@ -502,8 +502,8 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation const bool 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/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp index 660af35cb9..df634d355f 100644 --- a/src/server/game/Entities/Player/Player.cpp +++ b/src/server/game/Entities/Player/Player.cpp @@ -1449,7 +1449,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 teleport 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 :) @@ -1470,7 +1470,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati m_movementInfo.ResetJump(); DisableSpline(); - if (Transport* transport = GetTransport()) + if (TransportBase* transport = GetTransport()) { if (!(options & TELE_TO_NOT_LEAVE_TRANSPORT)) transport->RemovePassenger(this); @@ -1605,7 +1605,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati // send transfer packets WorldPackets::Movement::TransferPending transferPending; transferPending.MapID = mapid; - if (Transport* transport = GetTransport()) + if (Transport* transport = dynamic_cast(GetTransport())) { transferPending.Ship.emplace(); transferPending.Ship->ID = transport->GetEntry(); @@ -19525,8 +19525,8 @@ void Player::SaveToDB(CharacterDatabaseTransaction trans, bool create /* = false stmt->setFloat(index++, finiteAlways(GetTransOffsetZ())); stmt->setFloat(index++, finiteAlways(GetTransOffsetO())); ObjectGuid::LowType transLowGUID = 0; - if (GetTransport()) - transLowGUID = GetTransport()->GetGUID().GetCounter(); + if (Transport* transport = dynamic_cast(GetTransport())) + transLowGUID = transport->GetGUID().GetCounter(); stmt->setUInt32(index++, transLowGUID); std::ostringstream ss; @@ -19660,8 +19660,8 @@ void Player::SaveToDB(CharacterDatabaseTransaction trans, bool create /* = false stmt->setFloat(index++, finiteAlways(GetTransOffsetZ())); stmt->setFloat(index++, finiteAlways(GetTransOffsetO())); ObjectGuid::LowType transLowGUID = 0; - if (GetTransport()) - transLowGUID = GetTransport()->GetGUID().GetCounter(); + if (Transport* transport = dynamic_cast(GetTransport())) + transLowGUID = transport->GetGUID().GetCounter(); stmt->setUInt32(index++, transLowGUID); std::ostringstream ss; @@ -23028,17 +23028,6 @@ inline void UpdateVisibilityOf_helper(GuidUnorderedSet& s64, T* target, std::set s64.insert(target->GetGUID()); } -template<> -inline void UpdateVisibilityOf_helper(GuidUnorderedSet& s64, GameObject* target, std::set& /*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& v) { diff --git a/src/server/game/Entities/Transport/Transport.cpp b/src/server/game/Entities/Transport/Transport.cpp index 51d1f92bc3..3fa490fed5 100644 --- a/src/server/game/Entities/Transport/Transport.cpp +++ b/src/server/game/Entities/Transport/Transport.cpp @@ -85,7 +85,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) { @@ -142,7 +142,7 @@ bool Transport::Create(ObjectGuid::LowType guidlow, uint32 entry, uint32 mapid, SetUInt32Value(GAMEOBJECT_FLAGS, m_goTemplateAddon->flags); } - m_goValue.Transport.PathProgress = 0; + _pathProgress = 0; SetObjectScale(goinfo->size); SetPeriod(tInfo->pathTime); SetEntry(goinfo->entry); @@ -183,9 +183,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 @@ -209,9 +209,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 } @@ -314,7 +314,7 @@ void Transport::AddPassenger(WorldObject* passenger) } } -void Transport::RemovePassenger(WorldObject* passenger) +Transport* Transport::RemovePassenger(WorldObject* passenger) { bool erased = false; if (_passengerTeleportItr != _passengers.end()) @@ -344,6 +344,8 @@ void Transport::RemovePassenger(WorldObject* passenger) plr->SetFallInformation(0, plr->GetPositionZ()); } } + + return this; } Creature* Transport::CreateNPCPassenger(ObjectGuid::LowType guid, CreatureData const* data) @@ -555,6 +557,11 @@ TempSummon* Transport::SummonPassenger(uint32 entry, Position const& pos, TempSu return summon; } +int32 Transport::GetMapIdForSpawning() const +{ + return GetGOInfo()->moTransport.mapID; +} + 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 2743841cf1..7191b8d300 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 GetUInt32Value(GAMEOBJECT_LEVEL); } + int32 GetMapIdForSpawning() const override; + + uint32 GetTransportPeriod() const { return GetUInt32Value(GAMEOBJECT_LEVEL); } void SetPeriod(uint32 period) { SetUInt32Value(GAMEOBJECT_LEVEL, period); } - uint32 GetTimer() const { return GetGOValue()->Transport.PathProgress; } + uint32 GetTimer() const { return _pathProgress; } KeyFrameVec const& GetKeyFrames() const { return _transportInfo->keyFrames; } @@ -116,6 +122,7 @@ class TC_GAME_API Transport : public GameObject, public TransportBase KeyFrameVec::const_iterator _currentFrame; KeyFrameVec::const_iterator _nextFrame; + uint32 _pathProgress; TimeTrackerSmall _positionChangeTimer; bool _isMoving; bool _pendingStop; diff --git a/src/server/game/Entities/Unit/Unit.cpp b/src/server/game/Entities/Unit/Unit.cpp index 6e3eea8f8c..53becf51b0 100644 --- a/src/server/game/Entities/Unit/Unit.cpp +++ b/src/server/game/Entities/Unit/Unit.cpp @@ -12581,7 +12581,7 @@ ObjectGuid Unit::GetTransGUID() const if (GetVehicle()) return GetVehicleBase()->GetGUID(); if (GetTransport()) - return GetTransport()->GetGUID(); + return GetTransport()->GetTransportGUID(); return ObjectGuid::Empty; } @@ -13539,7 +13539,7 @@ void Unit::_EnterVehicle(Vehicle* vehicle, int8 seatId, AuraApplication const* a SetControlled(true, UNIT_STATE_ROOT); 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 153bfd00a1..aa56b7048e 100644 --- a/src/server/game/Entities/Vehicle/Vehicle.cpp +++ b/src/server/game/Entities/Vehicle/Vehicle.cpp @@ -455,7 +455,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) @@ -527,9 +527,10 @@ 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) { - if (unit->GetVehicle() != this) + Unit* unit = passenger->ToUnit(); + if (!unit) return nullptr; SeatMap::iterator seat = GetSeatIteratorForPassenger(unit); @@ -976,7 +977,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 ca9055d580..79796c994d 100644 --- a/src/server/game/Entities/Vehicle/Vehicle.h +++ b/src/server/game/Entities/Vehicle/Vehicle.h @@ -55,8 +55,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); - Vehicle* RemovePassenger(Unit* passenger); + bool AddVehiclePassenger(Unit* unit, int8 seatId = -1); + Vehicle* RemovePassenger(WorldObject* passenger) override; void RelocatePassengers(); void RemoveAllPassengers(); bool IsVehicleInUse() const; @@ -86,6 +86,12 @@ class TC_GAME_API Vehicle : public TransportBase SeatMap::iterator GetSeatIteratorForPassenger(Unit* passenger); void InitMovementInfoForBase(); + ObjectGuid GetTransportGUID() const override { return GetBase()->GetGUID(); } + + float GetTransportOrientation() const override { return GetBase()->GetOrientation(); } + + void AddPassenger(WorldObject* /*passenger*/) override { ASSERT("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 { @@ -102,6 +108,9 @@ 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 5064028c77..67bb3893bf 100644 --- a/src/server/game/Entities/Vehicle/VehicleDefines.h +++ b/src/server/game/Entities/Vehicle/VehicleDefines.h @@ -153,12 +153,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) @@ -184,6 +192,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 0899ade071..02fb61277e 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(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 23641859eb..76546e59a6 100644 --- a/src/server/game/Handlers/MovementHandler.cpp +++ b/src/server/game/Handlers/MovementHandler.cpp @@ -339,28 +339,30 @@ void WorldSession::HandleMovementOpcode(uint16 opcode, MovementInfo& movementInf { 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.guid.Clear(); - } + 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 == MSG_MOVE_FALL_LAND && plrMover && !plrMover->IsInFlight()) diff --git a/src/server/game/Maps/Map.cpp b/src/server/game/Maps/Map.cpp index 71ef08b881..d0d2ee7408 100644 --- a/src/server/game/Maps/Map.cpp +++ b/src/server/game/Maps/Map.cpp @@ -1258,9 +1258,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); } } @@ -1432,9 +1430,7 @@ void Map::MoveAllGameObjectsInMoveList() { // update pos go->Relocate(go->_newPosition); - go->UpdateModelPosition(); - go->UpdatePositionData(); - go->UpdateObjectVisibility(false); + go->AfterRelocation(); } else { @@ -2860,7 +2856,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(player->GetTransport())) { transport->BuildCreateUpdateBlockForPlayer(&data, player); player->m_visibleTransports.insert(transport->GetGUID()); @@ -2870,7 +2866,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(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 8d097eda92..b574a0f876 100644 --- a/src/server/game/Maps/TransportMgr.cpp +++ b/src/server/game/Maps/TransportMgr.cpp @@ -445,6 +445,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 624f9ffb1e..ed680698ff 100644 --- a/src/server/game/Maps/TransportMgr.h +++ b/src/server/game/Maps/TransportMgr.h @@ -88,13 +88,15 @@ struct TransportTemplate struct TC_GAME_API TransportAnimation { TransportAnimation() : TotalTime(0) { } - std::map Path; std::map 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; }; @@ -146,10 +148,7 @@ private: 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 _transportTemplates; diff --git a/src/server/game/Miscellaneous/SharedDefines.h b/src/server/game/Miscellaneous/SharedDefines.h index f376f1c55b..28f8d1d157 100644 --- a/src/server/game/Miscellaneous/SharedDefines.h +++ b/src/server/game/Miscellaneous/SharedDefines.h @@ -1855,13 +1855,14 @@ enum GameObjectFlags GO_FLAG_DESTROYED = 0x00000400 }; -enum GameObjectDynamicLowFlags +enum GameObjectDynamicLowFlags : uint16 { GO_DYNFLAG_LO_ACTIVATE = 0x01, // enables interaction with GO GO_DYNFLAG_LO_ANIMATE = 0x02, // possibly more distinct animation of GO GO_DYNFLAG_LO_NO_INTERACT = 0x04, // appears to disable interaction (not fully verified) GO_DYNFLAG_LO_SPARKLE = 0x08, // makes GO sparkle - GO_DYNFLAG_LO_STOPPED = 0x10 // Transport is stopped + GO_DYNFLAG_LO_STOPPED = 0x10, // Transport is stopped + GO_DYNFLAG_LO_INVERTED_MOVEMENT = 0x40, // GAMEOBJECT_TYPE_TRANSPORT only }; // client side GO show states diff --git a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp index 7f449f1a26..75690a4253 100755 --- a/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp +++ b/src/server/game/Movement/MovementGenerators/WaypointMovementGenerator.cpp @@ -114,9 +114,9 @@ void WaypointMovementGenerator::ProcessWaypointArrival(Creature* creat creature->SetHomePosition(x, y, z, o); else { - if (Transport* trans = creature->GetTransport()) + if (TransportBase* trans = creature->GetTransport()) { - o -= trans->GetOrientation(); + o -= trans->GetTransportOrientation(); creature->SetTransportHomePosition(x, y, z, o); trans->CalculatePassengerPosition(x, y, z, &o); creature->SetHomePosition(x, y, z, o); diff --git a/src/server/game/Movement/Spline/MoveSplineInit.cpp b/src/server/game/Movement/Spline/MoveSplineInit.cpp index fad1880d8e..fe2986d459 100644 --- a/src/server/game/Movement/Spline/MoveSplineInit.cpp +++ b/src/server/game/Movement/Spline/MoveSplineInit.cpp @@ -228,8 +228,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/game/Spells/SpellEffects.cpp b/src/server/game/Spells/SpellEffects.cpp index 41db5ef481..8913099acd 100644 --- a/src/server/game/Spells/SpellEffects.cpp +++ b/src/server/game/Spells/SpellEffects.cpp @@ -3779,12 +3779,12 @@ void Spell::EffectActivateObject(SpellEffIndex effIndex) case GameObjectActions::GoTo8thFloor: case GameObjectActions::GoTo9thFloor: case GameObjectActions::GoTo10thFloor: - { - // Cast is kind of loose but it'll do - if (Transport* transportTarget = gameObjTarget->ToTransport()) - transportTarget->SetTransportState(GO_STATE_TRANSPORT_STOPPED, uint32_t(action) - uint32(GameObjectActions::GoTo1stFloor)); + static_assert(int32(GO_STATE_TRANSPORT_ACTIVE) == int32(GameObjectActions::GoTo1stFloor)); + if (gameObjTarget->GetGoType() == GAMEOBJECT_TYPE_TRANSPORT) + gameObjTarget->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", m_spellInfo->Id, int32(action), int32(effIndex)); break; - } case GameObjectActions::None: TC_LOG_FATAL("spell", "Spell %d has action type NONE in effect %d", m_spellInfo->Id, int32(effIndex)); break; diff --git a/src/server/scripts/Commands/cs_debug.cpp b/src/server/scripts/Commands/cs_debug.cpp index adc3161005..c95294b4af 100644 --- a/src/server/scripts/Commands/cs_debug.cpp +++ b/src/server/scripts/Commands/cs_debug.cpp @@ -1569,7 +1569,7 @@ public: static bool HandleDebugTransportCommand(ChatHandler* handler, char const* args) { - Transport* transport = handler->GetSession()->GetPlayer()->GetTransport(); + Transport* transport = dynamic_cast(handler->GetSession()->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 9e5eefef80..1fa443ebd4 100644 --- a/src/server/scripts/Commands/cs_misc.cpp +++ b/src/server/scripts/Commands/cs_misc.cpp @@ -282,7 +282,7 @@ public: zoneId, (zoneEntry ? zoneEntry->AreaName : unknown), areaId, (areaEntry ? areaEntry->AreaName : unknown), object->GetPositionX(), object->GetPositionY(), object->GetPositionZ(), object->GetOrientation()); - if (Transport* transport = object->GetTransport()) + if (Transport* transport = dynamic_cast(object->GetTransport())) { handler->PSendSysMessage(LANG_TRANSPORT_POSITION, transport->GetGOInfo()->moTransport.mapID, object->GetTransOffsetX(), object->GetTransOffsetY(), object->GetTransOffsetZ(), object->GetTransOffsetO(), diff --git a/src/server/scripts/Commands/cs_npc.cpp b/src/server/scripts/Commands/cs_npc.cpp index cc466de177..934cc690bf 100644 --- a/src/server/scripts/Commands/cs_npc.cpp +++ b/src/server/scripts/Commands/cs_npc.cpp @@ -350,7 +350,7 @@ public: Player* chr = handler->GetSession()->GetPlayer(); Map* map = chr->GetMap(); - if (Transport* trans = chr->GetTransport()) + if (Transport* trans = dynamic_cast(chr->GetTransport())) { ObjectGuid::LowType guid = sObjectMgr->GenerateCreatureSpawnId(); CreatureData& data = sObjectMgr->NewOrExistCreatureData(guid); diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingDescent/boss_nefarians_end.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingDescent/boss_nefarians_end.cpp index c01f8d2c78..585ba049f7 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingDescent/boss_nefarians_end.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingDescent/boss_nefarians_end.cpp @@ -423,7 +423,7 @@ struct boss_nefarians_end : public BossAI if (events.IsInPhase(PHASE_TWO) && !_elevatorLowered) instance->SetData(DATA_RESET_ELEVATOR, events.GetTimeUntilEvent(EVENT_ELEVATOR_LOWERED)); else if (Transport* transport = GetElevator()) - transport->SetTransportState(GO_STATE_TRANSPORT_STOPPED, TRANSPORT_STOP_FRAME_RAISED); + transport->SetGoState(GOState(GO_STATE_TRANSPORT_STOPPED + TRANSPORT_STOP_FRAME_RAISED)); if (Creature* onyxia = instance->GetCreature(DATA_ONYXIA)) if (onyxia->IsAlive()) @@ -753,7 +753,7 @@ struct boss_nefarians_end : public BossAI break; case EVENT_LOWER_ELEVATOR: if (Transport* transport = GetElevator()) - transport->SetTransportState(GO_STATE_TRANSPORT_ACTIVE); + transport->SetGoState(GO_STATE_TRANSPORT_STOPPED); events.ScheduleEvent(EVENT_ELEVATOR_LOWERED, 9s, 0, PHASE_TWO); break; case EVENT_ELEVATOR_LOWERED: @@ -881,7 +881,7 @@ private: events.ScheduleEvent(EVENT_SAY_PHASE_THREE, 14s + 700ms); events.ScheduleEvent(EVENT_PREPARE_LANDING, 15s + 500ms, 0, PHASE_THREE); if (Transport* elevator = GetElevator()) - elevator->SetTransportState(GO_STATE_TRANSPORT_STOPPED, TRANSPORT_STOP_FRAME_RAISED); + elevator->SetGoState(GOState(GO_STATE_TRANSPORT_STOPPED + TRANSPORT_STOP_FRAME_RAISED)); for (ObjectGuid guid : summons) { @@ -1113,7 +1113,7 @@ struct npc_nefarians_end_lord_victor_nefarius : public PassiveAI case EVENT_RAISE_ELEVATOR: if (GameObject* elevator = _instance->GetGameObject(DATA_BLACKWING_ELEVATOR_ONYXIA)) if (Transport* transport = elevator->ToTransport()) - transport->SetTransportState(GO_STATE_TRANSPORT_STOPPED, TRANSPORT_STOP_FRAME_RAISED); + transport->SetGoState(GOState(GO_STATE_TRANSPORT_STOPPED + TRANSPORT_STOP_FRAME_RAISED)); break; case EVENT_CAST_TRANSFORM_VISUAL: if (Creature* stalker = me->FindNearestCreature(NPC_INVISIBLE_STALKER_CATACLYSM_BOSS, 1.0f)) diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingDescent/instance_blackwing_descent.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingDescent/instance_blackwing_descent.cpp index 8dafd11bb2..d4f72074f5 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingDescent/instance_blackwing_descent.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackwingDescent/instance_blackwing_descent.cpp @@ -465,7 +465,7 @@ class instance_blackwing_descent : public InstanceMapScript case EVENT_RAISE_ELEVATOR: if (GameObject* gameobject = GetGameObject(DATA_BLACKWING_ELEVATOR_ONYXIA)) if (Transport* transport = gameobject->ToTransport()) - transport->SetTransportState(GO_STATE_TRANSPORT_STOPPED, 1); + transport->SetGoState(GOState(GO_STATE_TRANSPORT_STOPPED + 1)); break; case EVENT_RESPAWN_LEFT_DWARVEN_GROUP: instance->SpawnGroupSpawn(SPAWN_GROUP_DWARVEN_SPIRITS_LEFT, true); diff --git a/src/server/scripts/Kalimdor/HallsOfOrigination/halls_of_origination.cpp b/src/server/scripts/Kalimdor/HallsOfOrigination/halls_of_origination.cpp index 82faf37ca4..0df9bcd1da 100644 --- a/src/server/scripts/Kalimdor/HallsOfOrigination/halls_of_origination.cpp +++ b/src/server/scripts/Kalimdor/HallsOfOrigination/halls_of_origination.cpp @@ -784,11 +784,14 @@ struct go_hoo_the_makers_lift_controller : public GameObjectAI if (!elevator) return true; + elevator->SetGoState(GOState(GO_STATE_TRANSPORT_STOPPED + action)); + + /* if (action == 0 && elevator->GetGoState() >= GO_STATE_TRANSPORT_ACTIVE + 2) elevator->SetTransportState(GO_STATE_TRANSPORT_ACTIVE); else elevator->SetTransportState(GO_STATE_TRANSPORT_STOPPED, action); - + */ return true; } }; 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 acba0e519d..7fdf0e3088 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/boss_icecrown_gunship_battle.cpp @@ -488,12 +488,16 @@ 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::Find(_transport)) + go->AddObjectToRemoveList(); + if (Transport* go = HashMapHolder::Find(_otherTransport)) go->AddObjectToRemoveList(); @@ -504,6 +508,7 @@ public: private: Unit* _caster; uint32 _spellId; + ObjectGuid _transport; ObjectGuid _otherTransport; }; @@ -686,7 +691,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)) @@ -704,8 +709,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)) { @@ -768,7 +773,8 @@ class npc_gunship : public CreatureScript if (Transport* otherTransport = HashMapHolder::Find(instance->GetGuidData(DATA_ICECROWN_GUNSHIP_BATTLE))) otherTransport->EnableMovement(true); - me->GetTransport()->EnableMovement(true); + if (Transport* transport = dynamic_cast(me->GetTransport())) + transport->EnableMovement(true); if (Creature* ship = me->FindNearestCreature(_teamInInstance == HORDE ? NPC_ORGRIMS_HAMMER : NPC_THE_SKYBREAKER, 200.0f)) { @@ -788,8 +794,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(8000)); + me->m_Events.AddEventAtOffset(new ResetEncounterEvent(me, teleportSpellId, instance->GetGuidData(DATA_ICECROWN_GUNSHIP_BATTLE), + instance->GetGuidData(DATA_ENEMY_GUNSHIP)), 8s); } } @@ -844,7 +850,7 @@ class npc_high_overlord_saurfang_igb : public CreatureScript _instance(creature->GetInstanceScript()) { _controller.ResetSlots(HORDE); - _controller.SetTransport(creature->GetTransport()); + _controller.SetTransport(dynamic_cast(creature->GetTransport())); me->setRegeneratingHealth(false); me->m_CombatDistance = 70.0f; _firstMageCooldown = GameTime::GetGameTime() + 60; @@ -954,7 +960,8 @@ class npc_high_overlord_saurfang_igb : public CreatureScript bool GossipSelect(Player* /*player*/, uint32 /*menuId*/, uint32 /*gossipListId*/) override { me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); - me->GetTransport()->EnableMovement(true); + if (Transport* transport = dynamic_cast(me->GetTransport())) + transport->EnableMovement(true); _events.SetPhase(PHASE_INTRO); _events.ScheduleEvent(EVENT_INTRO_H_1, 5000, 0, PHASE_INTRO); _events.ScheduleEvent(EVENT_INTRO_H_2, 16000, 0, PHASE_INTRO); @@ -1022,7 +1029,7 @@ class npc_high_overlord_saurfang_igb : public CreatureScript _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(me->GetTransport())) orgrimsHammer->SummonPassenger(NPC_TELEPORT_PORTAL, OrgrimsHammerTeleportPortal, TEMPSUMMON_TIMED_DESPAWN, nullptr, 21000); if (Transport* skybreaker = HashMapHolder::Find(_instance->GetGuidData(DATA_ICECROWN_GUNSHIP_BATTLE))) @@ -1108,7 +1115,7 @@ class npc_muradin_bronzebeard_igb : public CreatureScript _instance(creature->GetInstanceScript()) { _controller.ResetSlots(ALLIANCE); - _controller.SetTransport(creature->GetTransport()); + _controller.SetTransport(dynamic_cast(creature->GetTransport())); me->setRegeneratingHealth(false); me->m_CombatDistance = 70.0f; _firstMageCooldown = GameTime::GetGameTime() + 60; @@ -1218,7 +1225,8 @@ class npc_muradin_bronzebeard_igb : public CreatureScript bool GossipSelect(Player* /*player*/, uint32 /*menuId*/, uint32 /*gossipListId*/) override { me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); - me->GetTransport()->EnableMovement(true); + if (Transport* transport = dynamic_cast(me->GetTransport())) + transport->EnableMovement(true); _events.SetPhase(PHASE_INTRO); _events.ScheduleEvent(EVENT_INTRO_A_1, 5000); _events.ScheduleEvent(EVENT_INTRO_A_2, 10000, 0, PHASE_INTRO); @@ -1290,7 +1298,7 @@ class npc_muradin_bronzebeard_igb : public CreatureScript _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(me->GetTransport())) skybreaker->SummonPassenger(NPC_TELEPORT_PORTAL, SkybreakerTeleportPortal, TEMPSUMMON_TIMED_DESPAWN, nullptr, 21000); if (Transport* orgrimsHammer = HashMapHolder::Find(_instance->GetGuidData(DATA_ICECROWN_GUNSHIP_BATTLE))) @@ -1435,7 +1443,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; @@ -2154,7 +2162,7 @@ class spell_igb_burning_pitch_selector : public SpellScriptLoader targets.remove_if([team](WorldObject* target) -> bool { - if (Transport* transport = target->GetTransport()) + if (Transport* transport = dynamic_cast(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/icecrown_citadel_teleport.cpp b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel_teleport.cpp index 62ac0b5485..cc563b12f2 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel_teleport.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/icecrown_citadel_teleport.cpp @@ -25,6 +25,7 @@ #include "Spell.h" #include "SpellInfo.h" #include "SpellMgr.h" +#include "Transport.h" static std::vector const TeleportSpells = { @@ -65,6 +66,11 @@ class icecrown_citadel_teleport : public GameObjectScript return true; } + // 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 (TransportBase* transport = player->GetTransport()) + transport->RemovePassenger(player); + player->CastSpell(player, spell->Id, true); return true; } diff --git a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp index fded4b0877..275adb3ab7 100644 --- a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp +++ b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp @@ -435,7 +435,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: @@ -748,7 +751,10 @@ class instance_icecrown_citadel : public InstanceMapScript SetTeleporterState(teleporter, true); if (Transport* elevator = instance->GetTransport(LadyDeathwisperElevatorGUID)) - elevator->SetTransportState(GO_STATE_TRANSPORT_ACTIVE); + { + elevator->SetGoState(GO_STATE_TRANSPORT_ACTIVE); + elevator->HandleCustomTypeCommand(GameObjectType::SetTransportAutoCycleBetweenStopFrames(true)); + } SpawnGunship(); }