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
This commit is contained in:
Shauren authored and Ovahlord committed 2022-06-18 21:36:17 +02:00
1 parent b596675c3b
commit 25465699f7
32 files changed
+681 -323

No files matched your search

+410 -159
View File
@@ -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<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;
TimeTrackerSmall _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);
}
}
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<uint32>();
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<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:
@@ -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<Transport*>(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<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)
{
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<GameObjectType::Transport const*>(m_goTypeImpl.get());
case GAMEOBJECT_TYPE_MO_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())
@@ -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<GameObjectModelOwnerImpl>(this), sWorld->GetDataPath());