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

@@ -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);
}
@@ -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;
+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());
@@ -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<uint32>* StopFrames;
uint32 StateUpdateTimer;
} Transport;
//25 GAMEOBJECT_TYPE_FISHINGHOLE
struct
{
@@ -165,14 +194,15 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
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<uint32> 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<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_MO_TRANSPORT) return reinterpret_cast<Transport*>(this); else return nullptr; }
Transport const* ToTransport() const { if (GetGOInfo()->type == GAMEOBJECT_TYPE_MO_TRANSPORT) return reinterpret_cast<Transport const*>(this); else return nullptr; }
@@ -284,8 +318,11 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
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<GameObject>
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<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;
@@ -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;
+12 -36
View File
@@ -350,10 +350,9 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint32 flags) const
bool hasMovementAnimKit = false;
bool hasMeleeAnimKit = false;
uint32 stopFrameCount = 0;
std::vector<uint32> 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;
}
+5 -5
View File
@@ -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;
+7 -18
View File
@@ -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<Transport*>(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<Transport*>(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<Transport*>(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<Unit*>& /*v*/)
{
// @HACK: This is to prevent objects like deeprun tram from disappearing when player moves far from its spawn point while riding it
// But exclude stoppable elevators from this hack - they would be teleporting from one end to another
// if affected transports move so far horizontally that it causes them to run out of visibility range then you are out of luck
// fix visibility instead of adding hacks here
if (!target->IsDynTransport())
s64.insert(target->GetGUID());
}
template<>
inline void UpdateVisibilityOf_helper(GuidUnorderedSet& s64, Creature* target, std::set<Unit*>& v)
{
@@ -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);
+13 -6
View File
@@ -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;
+2 -2
View File
@@ -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)
+5 -4
View File
@@ -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());
}
+11 -2
View File
@@ -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);
@@ -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
@@ -30,7 +30,7 @@ void VisibleNotifier::SendToSelf()
{
// at this moment i_clientGUIDs have guids that not iterate at grid level checks
// but exist one case when this possible and object not out of range: transports
if (Transport* transport = i_player.GetTransport())
if (Transport* transport = dynamic_cast<Transport*>(i_player.GetTransport()))
{
for (Transport::PassengerSet::const_iterator itr = transport->GetPassengers().begin(); itr != transport->GetPassengers().end(); ++itr)
{
@@ -54,6 +54,9 @@ void VisibleNotifier::SendToSelf()
case TYPEID_DYNAMICOBJECT:
i_player.UpdateVisibilityOf((*itr)->ToDynObject(), i_data, i_visibleNow);
break;
case TYPEID_AREATRIGGER:
i_player.UpdateVisibilityOf((*itr)->ToAreaTrigger(), i_data, i_visibleNow);
break;
default:
break;
}
+13 -11
View File
@@ -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())
+4 -8
View File
@@ -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<Transport*>(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<Transport*>(player->GetTransport()))
for (WorldObject* passenger : transport->GetPassengers())
if (player != passenger && player->HaveAtClient(passenger))
passenger->BuildCreateUpdateBlockForPlayer(&data, player);
+43 -4
View File
@@ -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;
}
+6 -7
View File
@@ -88,13 +88,15 @@ struct TransportTemplate
struct TC_GAME_API TransportAnimation
{
TransportAnimation() : TotalTime(0) { }
std::map<uint32, TransportAnimationEntry const*> Path;
std::map<uint32, TransportRotationEntry const*> Rotations;
uint32 TotalTime;
TransportAnimationEntry const* GetAnimNode(uint32 time) const;
TransportRotationEntry const* GetAnimRotation(uint32 time) const;
TransportAnimationEntry const* GetPrevAnimNode(uint32 time) const;
TransportRotationEntry const* GetPrevAnimRotation(uint32 time) const;
TransportAnimationEntry const* GetNextAnimNode(uint32 time) const;
TransportRotationEntry const* GetNextAnimRotation(uint32 time) const;
};
@@ -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<uint32, TransportTemplate> _transportTemplates;
@@ -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
@@ -114,9 +114,9 @@ void WaypointMovementGenerator<Creature>::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);
@@ -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());
+5 -5
View File
@@ -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;
+1 -1
View File
@@ -1569,7 +1569,7 @@ public:
static bool HandleDebugTransportCommand(ChatHandler* handler, char const* args)
{
Transport* transport = handler->GetSession()->GetPlayer()->GetTransport();
Transport* transport = dynamic_cast<Transport*>(handler->GetSession()->GetPlayer()->GetTransport());
if (!transport)
return false;
+1 -1
View File
@@ -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<Transport*>(object->GetTransport()))
{
handler->PSendSysMessage(LANG_TRANSPORT_POSITION,
transport->GetGOInfo()->moTransport.mapID, object->GetTransOffsetX(), object->GetTransOffsetY(), object->GetTransOffsetZ(), object->GetTransOffsetO(),
+1 -1
View File
@@ -350,7 +350,7 @@ public:
Player* chr = handler->GetSession()->GetPlayer();
Map* map = chr->GetMap();
if (Transport* trans = chr->GetTransport())
if (Transport* trans = dynamic_cast<Transport*>(chr->GetTransport()))
{
ObjectGuid::LowType guid = sObjectMgr->GenerateCreatureSpawnId();
CreatureData& data = sObjectMgr->NewOrExistCreatureData(guid);
@@ -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))
@@ -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);
@@ -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;
}
};
@@ -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<Transport>::Find(_transport))
go->AddObjectToRemoveList();
if (Transport* go = HashMapHolder<Transport>::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<Transport>::Find(instance->GetGuidData(DATA_ICECROWN_GUNSHIP_BATTLE)))
otherTransport->EnableMovement(true);
me->GetTransport()->EnableMovement(true);
if (Transport* transport = dynamic_cast<Transport*>(me->GetTransport()))
transport->EnableMovement(true);
if (Creature* ship = me->FindNearestCreature(_teamInInstance == HORDE ? NPC_ORGRIMS_HAMMER : NPC_THE_SKYBREAKER, 200.0f))
{
@@ -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<Transport*>(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<Transport*>(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<Transport*>(me->GetTransport()))
orgrimsHammer->SummonPassenger(NPC_TELEPORT_PORTAL, OrgrimsHammerTeleportPortal, TEMPSUMMON_TIMED_DESPAWN, nullptr, 21000);
if (Transport* skybreaker = HashMapHolder<Transport>::Find(_instance->GetGuidData(DATA_ICECROWN_GUNSHIP_BATTLE)))
@@ -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<Transport*>(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<Transport*>(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<Transport*>(me->GetTransport()))
skybreaker->SummonPassenger(NPC_TELEPORT_PORTAL, SkybreakerTeleportPortal, TEMPSUMMON_TIMED_DESPAWN, nullptr, 21000);
if (Transport* orgrimsHammer = HashMapHolder<Transport>::Find(_instance->GetGuidData(DATA_ICECROWN_GUNSHIP_BATTLE)))
@@ -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<Transport*>(target->GetTransport()))
return transport->GetEntry() != uint32(team == HORDE ? GO_ORGRIMS_HAMMER_H : GO_THE_SKYBREAKER_A);
return true;
});
@@ -25,6 +25,7 @@
#include "Spell.h"
#include "SpellInfo.h"
#include "SpellMgr.h"
#include "Transport.h"
static std::vector<uint32> 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;
}
@@ -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();
}