diff options
| -rw-r--r-- | src/server/game/Entities/GameObject/GameObject.cpp | 109 | ||||
| -rw-r--r-- | src/server/game/Entities/GameObject/GameObject.h | 17 | ||||
| -rw-r--r-- | src/server/game/Entities/Object/Object.cpp | 13 | ||||
| -rw-r--r-- | src/server/game/Entities/Player/Player.cpp | 5 | ||||
| -rw-r--r-- | src/server/game/Globals/ObjectMgr.cpp | 7 | ||||
| -rw-r--r-- | src/server/scripts/Northrend/IcecrownCitadel/instance_icecrown_citadel.cpp | 10 |
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(); } |
