aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/server/game/Entities/GameObject/GameObject.cpp109
-rw-r--r--src/server/game/Entities/GameObject/GameObject.h17
-rw-r--r--src/server/game/Entities/Object/Object.cpp13
-rw-r--r--src/server/game/Entities/Player/Player.cpp5
-rw-r--r--src/server/game/Globals/ObjectMgr.cpp7
-rw-r--r--src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp10
6 files changed, 136 insertions, 25 deletions
diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp
index d134740d1cd..864524373f7 100644
--- a/src/server/game/Entities/GameObject/GameObject.cpp
+++ b/src/server/game/Entities/GameObject/GameObject.cpp
@@ -69,6 +69,8 @@ GameObject::~GameObject()
{
delete m_AI;
delete m_model;
+ if (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();
}
@@ -239,13 +241,29 @@ bool GameObject::Create(uint32 guidlow, uint32 name_id, Map* map, uint32 /*phase
SetUInt32Value(GAMEOBJECT_PARENTROTATION, m_goInfo->building.destructibleData);
break;
case GAMEOBJECT_TYPE_TRANSPORT:
- SetUInt32Value(GAMEOBJECT_LEVEL, goinfo->transport.pause);
- SetGoState(goinfo->transport.startOpen ? GO_STATE_ACTIVE : GO_STATE_READY);
- SetGoAnimProgress(animprogress);
- m_goValue.Transport.PathProgress = 0;
+ {
m_goValue.Transport.AnimationInfo = sTransportMgr->GetTransportAnimInfo(goinfo->entry);
+ m_goValue.Transport.PathProgress = (getMSTime() / GetTransportPeriod());
+ m_goValue.Transport.PathProgress *= GetTransportPeriod();
m_goValue.Transport.CurrentSeg = 0;
+ m_goValue.Transport.StateUpdateTimer = 0;
+ m_goValue.Transport.StopFrames = new std::vector<uint32>();
+ if (goinfo->transport.stopFrame1 > 0)
+ m_goValue.Transport.StopFrames->push_back(goinfo->transport.stopFrame1);
+ if (goinfo->transport.stopFrame2 > 0)
+ m_goValue.Transport.StopFrames->push_back(goinfo->transport.stopFrame3);
+ if (goinfo->transport.stopFrame3 > 0)
+ m_goValue.Transport.StopFrames->push_back(goinfo->transport.stopFrame3);
+ if (goinfo->transport.stopFrame4 > 0)
+ m_goValue.Transport.StopFrames->push_back(goinfo->transport.stopFrame4);
+ if (goinfo->transport.startOpen)
+ SetTransportState(GO_STATE_TRANSPORT_STOPPED, goinfo->transport.startOpen - 1);
+ else
+ SetTransportState(GO_STATE_TRANSPORT_ACTIVE);
+
+ SetGoAnimProgress(animprogress);
break;
+ }
case GAMEOBJECT_TYPE_FISHINGNODE:
SetGoAnimProgress(0);
break;
@@ -309,11 +327,11 @@ void GameObject::Update(uint32 diff)
if (!m_goValue.Transport.AnimationInfo)
break;
- if (GetGoState() == GO_STATE_READY)
+ 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 % m_goValue.Transport.AnimationInfo->TotalTime;
+ uint32 timer = m_goValue.Transport.PathProgress % GetTransportPeriod();
TransportAnimationEntry const* node = m_goValue.Transport.AnimationInfo->GetAnimNode(timer);
if (node && m_goValue.Transport.CurrentSeg != node->TimeSeg)
{
@@ -333,6 +351,18 @@ void GameObject::Update(uint32 diff)
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;
}
@@ -934,7 +964,7 @@ bool GameObject::IsDynTransport() const
if (!gInfo)
return false;
- return gInfo->type == GAMEOBJECT_TYPE_MO_TRANSPORT || (gInfo->type == GAMEOBJECT_TYPE_TRANSPORT && !gInfo->transport.pause);
+ return gInfo->type == GAMEOBJECT_TYPE_MO_TRANSPORT || (gInfo->type == GAMEOBJECT_TYPE_TRANSPORT && m_goValue.Transport.StopFrames->empty());
}
bool GameObject::IsDestructibleBuilding() const
@@ -2079,6 +2109,38 @@ 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() + m_goValue.Transport.StopFrames->at(GetGoState() - GO_STATE_TRANSPORT_STOPPED);
+ SetGoState(GO_STATE_TRANSPORT_ACTIVE);
+ }
+ else
+ {
+ ASSERT(state < 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)
{
SetUInt32Value(GAMEOBJECT_DISPLAYID, displayid);
@@ -2175,6 +2237,7 @@ 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();
@@ -2217,9 +2280,18 @@ 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:
- pathProgress = int16(float(m_goValue.Transport.PathProgress) / float(GetUInt32Value(GAMEOBJECT_LEVEL)) * 65535.0f);
+ {
+ float timer = float(m_goValue.Transport.PathProgress % GetUInt32Value(GAMEOBJECT_LEVEL));
+ pathProgress = int16(timer / float(GetUInt32Value(GAMEOBJECT_LEVEL)) * 65535.0f);
break;
+ }
default:
break;
}
@@ -2236,6 +2308,27 @@ void GameObject::BuildValuesUpdate(uint8 updateType, ByteBuffer* data, Player* t
fieldBuffer << flags;
}
+ 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
}
diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h
index 4d945b54e6b..bb6a862ad4a 100644
--- a/src/server/game/Entities/GameObject/GameObject.h
+++ b/src/server/game/Entities/GameObject/GameObject.h
@@ -193,11 +193,17 @@ struct GameObjectTemplate
//11 GAMEOBJECT_TYPE_TRANSPORT
struct
{
- uint32 pause; //0
+ int32 stopFrame1; //0
uint32 startOpen; //1
uint32 autoCloseTime; //2 secs till autoclose = autoCloseTime / 0x10000
uint32 pause1EventID; //3
uint32 pause2EventID; //4
+ uint32 mapId; //5
+ int32 stopFrame2; //6
+ uint32 unknown;
+ int32 stopFrame3; //8
+ uint32 unknown2;
+ int32 stopFrame4; //10
} transport;
//12 GAMEOBJECT_TYPE_AREADAMAGE
struct
@@ -552,6 +558,8 @@ union GameObjectValue
uint32 PathProgress;
TransportAnimation const* AnimationInfo;
uint32 CurrentSeg;
+ std::vector<uint32>* StopFrames;
+ uint32 StateUpdateTimer;
} Transport;
//25 GAMEOBJECT_TYPE_FISHINGHOLE
struct
@@ -582,10 +590,13 @@ enum GOState
{
GO_STATE_ACTIVE = 0, // show in world as used and not reset (closed door open)
GO_STATE_READY = 1, // show in world as ready (closed door close)
- GO_STATE_ACTIVE_ALTERNATIVE = 2 // show in world as used in alt way and not reset (closed door open by cannon fire)
+ GO_STATE_ACTIVE_ALTERNATIVE = 2, // show in world as used in alt way and not reset (closed door open by cannon fire)
+ GO_STATE_TRANSPORT_ACTIVE = 24,
+ GO_STATE_TRANSPORT_STOPPED = 25
};
#define MAX_GO_STATE 3
+#define MAX_GO_STATE_TRANSPORT_STOP_FRAMES 9
// from `gameobject`
struct GameObjectData
@@ -720,6 +731,8 @@ class GameObject : public WorldObject, public GridObject<GameObject>, public Map
void SetGoType(GameobjectTypes type) { SetByteValue(GAMEOBJECT_BYTES_1, 1, type); }
GOState GetGoState() const { return GOState(GetByteValue(GAMEOBJECT_BYTES_1, 0)); }
void SetGoState(GOState state);
+ 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); }
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index 95d4be25630..38ad803d177 100644
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -346,7 +346,11 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint16 flags) const
bool hasSpline = false;
bool hasSplineElevation = false;
- uint32 unkLoopCounter = 0;
+ uint32 stopFrameCount = 0;
+ if (GameObject const* go = ToGameObject())
+ if (go->GetGoType() == GAMEOBJECT_TYPE_TRANSPORT)
+ stopFrameCount = go->GetGOValue()->Transport.StopFrames->size();
+
// Bit content
data->WriteBit(0);
data->WriteBit(0);
@@ -356,7 +360,7 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint16 flags) const
data->WriteBit(flags & UPDATEFLAG_SELF);
data->WriteBit(flags & UPDATEFLAG_VEHICLE);
data->WriteBit(flags & UPDATEFLAG_LIVING);
- data->WriteBits(unkLoopCounter, 24);
+ data->WriteBits(stopFrameCount, 24);
data->WriteBit(0);
data->WriteBit(flags & UPDATEFLAG_GO_TRANSPORT_POSITION);
data->WriteBit(flags & UPDATEFLAG_STATIONARY_POSITION);
@@ -471,8 +475,9 @@ void Object::BuildMovementUpdate(ByteBuffer* data, uint16 flags) const
data->FlushBits();
// Data
- for (uint32 i = 0; i < unkLoopCounter; ++i)
- *data << uint32(0);
+ if (GameObject const* go = ToGameObject())
+ for (uint32 i = 0; i < stopFrameCount; ++i)
+ *data << uint32(go->GetGOValue()->Transport.StopFrames->at(i));
if (flags & UPDATEFLAG_LIVING)
{
diff --git a/src/server/game/Entities/Player/Player.cpp b/src/server/game/Entities/Player/Player.cpp
index 964d826394c..0b7f358a1d3 100644
--- a/src/server/game/Entities/Player/Player.cpp
+++ b/src/server/game/Entities/Player/Player.cpp
@@ -23046,7 +23046,10 @@ template<>
inline void UpdateVisibilityOf_helper(GuidSet& 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
- if ((target->GetGOInfo()->type != GAMEOBJECT_TYPE_TRANSPORT))
+ // 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());
}
diff --git a/src/server/game/Globals/ObjectMgr.cpp b/src/server/game/Globals/ObjectMgr.cpp
index 0bb49260244..49049356fe0 100644
--- a/src/server/game/Globals/ObjectMgr.cpp
+++ b/src/server/game/Globals/ObjectMgr.cpp
@@ -2050,8 +2050,11 @@ void ObjectMgr::LoadGameobjects()
uint32 go_state = fields[13].GetUInt8();
if (go_state >= MAX_GO_STATE)
{
- TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid `state` (%u) value, skip", guid, data.id, go_state);
- continue;
+ if (gInfo->type != GAMEOBJECT_TYPE_TRANSPORT || go_state > GO_STATE_TRANSPORT_ACTIVE + MAX_GO_STATE_TRANSPORT_STOP_FRAMES)
+ {
+ TC_LOG_ERROR("sql.sql", "Table `gameobject` has gameobject (GUID: %u Entry: %u) with invalid `state` (%u) value, skip", guid, data.id, go_state);
+ continue;
+ }
}
data.go_state = GOState(go_state);
diff --git a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp
index 4d86293665a..fb257995842 100644
--- a/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp
+++ b/src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp
@@ -490,10 +490,7 @@ class instance_icecrown_citadel : public InstanceMapScript
case GO_LADY_DEATHWHISPER_ELEVATOR:
LadyDeathwisperElevatorGUID = go->GetGUID();
if (GetBossState(DATA_LADY_DEATHWHISPER) == DONE)
- {
- go->SetUInt32Value(GAMEOBJECT_LEVEL, 0);
- go->SetGoState(GO_STATE_READY);
- }
+ go->SetTransportState(GO_STATE_TRANSPORT_ACTIVE);
break;
case GO_THE_SKYBREAKER_H:
case GO_ORGRIMS_HAMMER_A:
@@ -800,10 +797,7 @@ class instance_icecrown_citadel : public InstanceMapScript
SetTeleporterState(teleporter, true);
if (GameObject* elevator = instance->GetGameObject(LadyDeathwisperElevatorGUID))
- {
- elevator->SetUInt32Value(GAMEOBJECT_LEVEL, 0);
- elevator->SetGoState(GO_STATE_READY);
- }
+ elevator->SetTransportState(GO_STATE_TRANSPORT_ACTIVE);
SpawnGunship();
}