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 /src/server/game/Entities/GameObject | |
| 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
Diffstat (limited to 'src/server/game/Entities/GameObject')
| -rw-r--r-- | src/server/game/Entities/GameObject/GameObject.cpp | 560 | ||||
| -rw-r--r-- | src/server/game/Entities/GameObject/GameObject.h | 63 | ||||
| -rw-r--r-- | src/server/game/Entities/GameObject/GameObjectData.h | 1 |
3 files changed, 497 insertions, 127 deletions
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; |
