aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/server/game/Entities/Creature/Creature.cpp4
-rw-r--r--src/server/game/Entities/Creature/Creature.h2
-rw-r--r--src/server/game/Entities/GameObject/GameObject.cpp100
-rw-r--r--src/server/game/Entities/GameObject/GameObject.h16
-rw-r--r--src/server/game/Entities/Object/Object.cpp2
-rw-r--r--src/server/game/Entities/Object/Object.h10
-rw-r--r--src/server/game/Entities/Object/Updates/UpdateFields.cpp4
-rw-r--r--src/server/game/Entities/Object/Updates/UpdateFields.h1
-rw-r--r--src/server/game/Entities/Object/Updates/ViewerDependentValues.h12
-rw-r--r--src/server/game/Handlers/LootHandler.cpp2
-rw-r--r--src/server/game/Server/Packets/GameObjectPackets.cpp8
-rw-r--r--src/server/game/Server/Packets/GameObjectPackets.h11
-rw-r--r--src/server/game/Server/Protocol/Opcodes.cpp2
-rw-r--r--src/server/scripts/Maelstrom/Stonecore/boss_slabhide.cpp2
-rw-r--r--src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_forgemaster_garfrost.cpp2
15 files changed, 161 insertions, 17 deletions
diff --git a/src/server/game/Entities/Creature/Creature.cpp b/src/server/game/Entities/Creature/Creature.cpp
index a30c7d1129f..907c30ecaea 100644
--- a/src/server/game/Entities/Creature/Creature.cpp
+++ b/src/server/game/Entities/Creature/Creature.cpp
@@ -1967,9 +1967,9 @@ bool Creature::hasInvolvedQuest(uint32 quest_id) const
return true;
}
-bool Creature::IsInvisibleDueToDespawn() const
+bool Creature::IsInvisibleDueToDespawn(WorldObject const* seer) const
{
- if (Unit::IsInvisibleDueToDespawn())
+ if (Unit::IsInvisibleDueToDespawn(seer))
return true;
if (IsAlive() || isDying() || m_corpseRemoveTime > GameTime::GetGameTime())
diff --git a/src/server/game/Entities/Creature/Creature.h b/src/server/game/Entities/Creature/Creature.h
index beb74bb7526..1e8c6044a13 100644
--- a/src/server/game/Entities/Creature/Creature.h
+++ b/src/server/game/Entities/Creature/Creature.h
@@ -436,7 +436,7 @@ class TC_GAME_API Creature : public Unit, public GridObject<Creature>, public Ma
uint16 m_LootMode; // Bitmask (default: LOOT_MODE_DEFAULT) that determines what loot will be lootable
- bool IsInvisibleDueToDespawn() const override;
+ bool IsInvisibleDueToDespawn(WorldObject const* seer) const override;
bool CanAlwaysSee(WorldObject const* obj) const override;
private:
diff --git a/src/server/game/Entities/GameObject/GameObject.cpp b/src/server/game/Entities/GameObject/GameObject.cpp
index 4dffcf2652e..1e50e56f2a3 100644
--- a/src/server/game/Entities/GameObject/GameObject.cpp
+++ b/src/server/game/Entities/GameObject/GameObject.cpp
@@ -901,6 +901,44 @@ void GameObject::Update(uint32 diff)
if (m_goTypeImpl)
m_goTypeImpl->Update(diff);
+ if (m_perPlayerState)
+ {
+ for (auto itr = m_perPlayerState->begin(); itr != m_perPlayerState->end(); )
+ {
+ if (itr->second.ValidUntil > GameTime::GetSystemTime())
+ {
+ ++itr;
+ continue;
+ }
+
+ Player* seer = ObjectAccessor::GetPlayer(*this, itr->first);
+ bool needsStateUpdate = itr->second.State != GetGoState();
+ bool despawned = itr->second.Despawned;
+
+ itr = m_perPlayerState->erase(itr);
+
+ if (seer)
+ {
+ if (despawned)
+ {
+ seer->UpdateVisibilityOf(this);
+ }
+ else if (needsStateUpdate)
+ {
+ UF::ObjectData::Base objMask;
+ UF::GameObjectData::Base goMask;
+ goMask.MarkChanged(&UF::GameObjectData::State);
+
+ UpdateData udata(GetMapId());
+ BuildValuesUpdateForPlayerWithMask(&udata, objMask.GetChangesMask(), goMask.GetChangesMask(), seer);
+ WorldPacket packet;
+ udata.BuildPacket(&packet);
+ seer->SendDirectMessage(&packet);
+ }
+ }
+ }
+ }
+
switch (m_lootState)
{
case GO_NOT_READY:
@@ -1795,15 +1833,20 @@ bool GameObject::IsAlwaysVisibleFor(WorldObject const* seer) const
return false;
}
-bool GameObject::IsInvisibleDueToDespawn() const
+bool GameObject::IsInvisibleDueToDespawn(WorldObject const* seer) const
{
- if (WorldObject::IsInvisibleDueToDespawn())
+ if (WorldObject::IsInvisibleDueToDespawn(seer))
return true;
// Despawned
if (!isSpawned())
return true;
+ if (m_perPlayerState)
+ if (PerPlayerState const* state = Trinity::Containers::MapGetValuePtr(*m_perPlayerState, seer->GetGUID()))
+ if (state->Despawned)
+ return true;
+
return false;
}
@@ -3189,6 +3232,29 @@ bool GameObject::IsFullyLooted() const
return true;
}
+void GameObject::OnLootRelease(Player* looter)
+{
+ switch (GetGoType())
+ {
+ case GAMEOBJECT_TYPE_CHEST:
+ {
+ GameObjectTemplate const* goInfo = GetGOInfo();
+ if (!goInfo->chest.consumable && goInfo->chest.chestPersonalLoot)
+ {
+ PerPlayerState& perPlayerState = GetOrCreatePerPlayerStates()[looter->GetGUID()];
+ perPlayerState.ValidUntil = GameTime::GetSystemTime() + (goInfo->chest.chestRestockTime
+ ? Seconds(goInfo->chest.chestRestockTime)
+ : Seconds(m_respawnDelayTime)); // not hiding this object permanently to prevent infinite growth of m_perPlayerState
+ // while also maintaining some sort of cheater protection (not getting rid of entries on logout)
+ perPlayerState.Despawned = true;
+
+ looter->UpdateVisibilityOf(this);
+ }
+ break;
+ }
+ }
+}
+
void GameObject::SetGoState(GOState state)
{
GOState oldState = GetGoState();
@@ -3213,6 +3279,28 @@ void GameObject::SetGoState(GOState state)
}
}
+GOState GameObject::GetGoStateFor(ObjectGuid const& viewer) const
+{
+ if (m_perPlayerState)
+ if (PerPlayerState const* state = Trinity::Containers::MapGetValuePtr(*m_perPlayerState, viewer))
+ if (state->State)
+ return *state->State;
+
+ return GetGoState();
+}
+
+void GameObject::SetGoStateFor(GOState state, Player const* viewer)
+{
+ PerPlayerState& perPlayerState = GetOrCreatePerPlayerStates()[viewer->GetGUID()];
+ perPlayerState.ValidUntil = GameTime::GetSystemTime() + Seconds(m_respawnDelayTime);
+ perPlayerState.State = state;
+
+ WorldPackets::GameObject::GameObjectSetStateLocal setStateLocal;
+ setStateLocal.ObjectGUID = GetGUID();
+ setStateLocal.State = state;
+ viewer->SendDirectMessage(setStateLocal.Write());
+}
+
void GameObject::SetDisplayId(uint32 displayid)
{
SetUpdateFieldValue(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::DisplayID), displayid);
@@ -3690,6 +3778,14 @@ bool GameObject::CanInteractWithCapturePoint(Player const* target) const
|| m_goValue.CapturePoint.State == WorldPackets::Battleground::BattlegroundCapturePointState::HordeCaptured;
}
+std::unordered_map<ObjectGuid, GameObject::PerPlayerState>& GameObject::GetOrCreatePerPlayerStates()
+{
+ if (!m_perPlayerState)
+ m_perPlayerState = std::make_unique<std::unordered_map<ObjectGuid, PerPlayerState>>();
+
+ return *m_perPlayerState;
+}
+
class GameObjectModelOwnerImpl : public GameObjectModelOwnerBase
{
public:
diff --git a/src/server/game/Entities/GameObject/GameObject.h b/src/server/game/Entities/GameObject/GameObject.h
index 7d7d0563532..6341d9ecaea 100644
--- a/src/server/game/Entities/GameObject/GameObject.h
+++ b/src/server/game/Entities/GameObject/GameObject.h
@@ -237,6 +237,8 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
void SetGoType(GameobjectTypes type) { SetUpdateFieldValue(m_values.ModifyValue(&GameObject::m_gameObjectData).ModifyValue(&UF::GameObjectData::TypeID), type); }
GOState GetGoState() const { return GOState(*m_gameObjectData->State); }
void SetGoState(GOState state);
+ GOState GetGoStateFor(ObjectGuid const& viewer) const;
+ void SetGoStateFor(GOState state, Player const* viewer);
uint32 GetGoArtKit() const { return m_gameObjectData->ArtKit; }
void SetGoArtKit(uint32 artkit);
uint8 GetGoAnimProgress() const { return m_gameObjectData->PercentHealth; }
@@ -261,6 +263,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
void RemoveLootMode(uint16 lootMode) { m_LootMode &= ~lootMode; }
void ResetLootMode() { m_LootMode = LOOT_MODE_DEFAULT; }
bool IsFullyLooted() const;
+ void OnLootRelease(Player* looter);
void AddToSkillupList(ObjectGuid const& PlayerGuidLow) { m_SkillupList.insert(PlayerGuidLow); }
bool IsInSkillupList(ObjectGuid const& playerGuid) const
@@ -301,7 +304,7 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
bool IsNeverVisibleFor(WorldObject const* seer) const override;
bool IsAlwaysVisibleFor(WorldObject const* seer) const override;
- bool IsInvisibleDueToDespawn() const override;
+ bool IsInvisibleDueToDespawn(WorldObject const* seer) const override;
uint8 GetLevelForTarget(WorldObject const* target) const override;
@@ -446,5 +449,16 @@ class TC_GAME_API GameObject : public WorldObject, public GridObject<GameObject>
bool m_respawnCompatibilityMode;
uint16 _animKitId;
uint32 _worldEffectID;
+
+ struct PerPlayerState
+ {
+ SystemTimePoint ValidUntil = SystemTimePoint::min();
+ Optional<GOState> State;
+ bool Despawned = false;
+ };
+
+ std::unique_ptr<std::unordered_map<ObjectGuid, PerPlayerState>> m_perPlayerState;
+
+ std::unordered_map<ObjectGuid, PerPlayerState>& GetOrCreatePerPlayerStates();
};
#endif
diff --git a/src/server/game/Entities/Object/Object.cpp b/src/server/game/Entities/Object/Object.cpp
index 451ed8cf0dc..8815f3d5416 100644
--- a/src/server/game/Entities/Object/Object.cpp
+++ b/src/server/game/Entities/Object/Object.cpp
@@ -1521,7 +1521,7 @@ bool WorldObject::CanSeeOrDetect(WorldObject const* obj, bool ignoreStealth, boo
return false;
}
- if (obj->IsInvisibleDueToDespawn())
+ if (obj->IsInvisibleDueToDespawn(this))
return false;
if (!CanDetect(obj, ignoreStealth, checkAlert))
diff --git a/src/server/game/Entities/Object/Object.h b/src/server/game/Entities/Object/Object.h
index 550b8e5588b..b17be4c1822 100644
--- a/src/server/game/Entities/Object/Object.h
+++ b/src/server/game/Entities/Object/Object.h
@@ -750,11 +750,12 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation
void SetLocationInstanceId(uint32 _instanceId) { m_InstanceId = _instanceId; }
virtual bool CanNeverSee(WorldObject const* obj) const;
- virtual bool IsNeverVisibleFor(WorldObject const* /*seer*/) const { return !IsInWorld() || IsDestroyedObject(); }
- virtual bool IsAlwaysVisibleFor(WorldObject const* /*seer*/) const { return false; }
- virtual bool IsInvisibleDueToDespawn() const { return false; }
+ virtual bool CanAlwaysSee([[maybe_unused]] WorldObject const* /*obj*/) const { return false; }
+ virtual bool IsNeverVisibleFor([[maybe_unused]] WorldObject const* seer) const { return !IsInWorld() || IsDestroyedObject(); }
+ virtual bool IsAlwaysVisibleFor([[maybe_unused]] WorldObject const* seer) const { return false; }
+ virtual bool IsInvisibleDueToDespawn([[maybe_unused]] WorldObject const* seer) const { return false; }
//difference from IsAlwaysVisibleFor: 1. after distance check; 2. use owner or charmer as seer
- virtual bool IsAlwaysDetectableFor(WorldObject const* /*seer*/) const { return false; }
+ virtual bool IsAlwaysDetectableFor([[maybe_unused]] WorldObject const* seer) const { return false; }
private:
Map* m_currMap; // current object's Map location
@@ -771,7 +772,6 @@ class TC_GAME_API WorldObject : public Object, public WorldLocation
virtual bool _IsWithinDist(WorldObject const* obj, float dist2compare, bool is3D, bool incOwnRadius = true, bool incTargetRadius = true) const;
- virtual bool CanAlwaysSee(WorldObject const* /*obj*/) const { return false; }
bool CanDetect(WorldObject const* obj, bool ignoreStealth, bool checkAlert = false) const;
bool CanDetectInvisibilityOf(WorldObject const* obj) const;
bool CanDetectStealthOf(WorldObject const* obj, bool checkAlert = false) const;
diff --git a/src/server/game/Entities/Object/Updates/UpdateFields.cpp b/src/server/game/Entities/Object/Updates/UpdateFields.cpp
index c9f7d991d67..c38670e91ff 100644
--- a/src/server/game/Entities/Object/Updates/UpdateFields.cpp
+++ b/src/server/game/Entities/Object/Updates/UpdateFields.cpp
@@ -4270,7 +4270,7 @@ void GameObjectData::WriteCreate(ByteBuffer& data, EnumFlag<UpdateFieldFlag> fie
data << float(ParentRotation->z);
data << float(ParentRotation->w);
data << int32(FactionTemplate);
- data << int8(State);
+ data << int8(ViewerDependentValue<StateTag>::GetValue(this, owner, receiver));
data << int8(TypeID);
data << uint8(PercentHealth);
data << uint32(ArtKit);
@@ -4377,7 +4377,7 @@ void GameObjectData::WriteUpdate(ByteBuffer& data, Mask const& changesMask, bool
}
if (changesMask[14])
{
- data << int8(State);
+ data << int8(ViewerDependentValue<StateTag>::GetValue(this, owner, receiver));
}
if (changesMask[15])
{
diff --git a/src/server/game/Entities/Object/Updates/UpdateFields.h b/src/server/game/Entities/Object/Updates/UpdateFields.h
index 41f47ffa22f..1d6d1499df8 100644
--- a/src/server/game/Entities/Object/Updates/UpdateFields.h
+++ b/src/server/game/Entities/Object/Updates/UpdateFields.h
@@ -807,6 +807,7 @@ struct GameObjectData : public IsUpdateFieldStructureTag, public HasChangesMask<
UpdateField<QuaternionData, 0, 12> ParentRotation;
UpdateField<int32, 0, 13> FactionTemplate;
UpdateField<int8, 0, 14> State;
+ struct StateTag : ViewerDependentValueTag<int8> {};
UpdateField<int8, 0, 15> TypeID;
UpdateField<uint8, 0, 16> PercentHealth;
UpdateField<uint32, 0, 17> ArtKit;
diff --git a/src/server/game/Entities/Object/Updates/ViewerDependentValues.h b/src/server/game/Entities/Object/Updates/ViewerDependentValues.h
index dfbf1d554c9..fdcd7a08a15 100644
--- a/src/server/game/Entities/Object/Updates/ViewerDependentValues.h
+++ b/src/server/game/Entities/Object/Updates/ViewerDependentValues.h
@@ -303,6 +303,18 @@ public:
};
template<>
+class ViewerDependentValue<UF::GameObjectData::StateTag>
+{
+public:
+ using value_type = UF::GameObjectData::StateTag::value_type;
+
+ static value_type GetValue(UF::GameObjectData const* /*gameObjectData*/, GameObject const* gameObject, Player const* receiver)
+ {
+ return gameObject->GetGoStateFor(receiver->GetGUID());
+ }
+};
+
+template<>
class ViewerDependentValue<UF::ConversationData::LastLineEndTimeTag>
{
public:
diff --git a/src/server/game/Handlers/LootHandler.cpp b/src/server/game/Handlers/LootHandler.cpp
index fdaf064dd74..40d92fc2a22 100644
--- a/src/server/game/Handlers/LootHandler.cpp
+++ b/src/server/game/Handlers/LootHandler.cpp
@@ -297,6 +297,8 @@ void WorldSession::DoLootRelease(Loot* loot)
}
else if (go->IsFullyLooted())
go->SetLootState(GO_JUST_DEACTIVATED);
+
+ go->OnLootRelease(player);
}
else
{
diff --git a/src/server/game/Server/Packets/GameObjectPackets.cpp b/src/server/game/Server/Packets/GameObjectPackets.cpp
index 4e6cc5d7324..fd15e7264cf 100644
--- a/src/server/game/Server/Packets/GameObjectPackets.cpp
+++ b/src/server/game/Server/Packets/GameObjectPackets.cpp
@@ -87,3 +87,11 @@ WorldPacket const* WorldPackets::GameObject::GameObjectPlaySpellVisual::Write()
return &_worldPacket;
}
+
+WorldPacket const* WorldPackets::GameObject::GameObjectSetStateLocal::Write()
+{
+ _worldPacket << ObjectGUID;
+ _worldPacket << uint8(State);
+
+ return &_worldPacket;
+}
diff --git a/src/server/game/Server/Packets/GameObjectPackets.h b/src/server/game/Server/Packets/GameObjectPackets.h
index 91d795a15ae..3bd71aba0ab 100644
--- a/src/server/game/Server/Packets/GameObjectPackets.h
+++ b/src/server/game/Server/Packets/GameObjectPackets.h
@@ -142,6 +142,17 @@ namespace WorldPackets
ObjectGuid ActivatorGUID;
int32 SpellVisualID = 0;
};
+
+ class GameObjectSetStateLocal final : public ServerPacket
+ {
+ public:
+ GameObjectSetStateLocal() : ServerPacket(SMSG_GAME_OBJECT_SET_STATE_LOCAL, 16 + 1) { }
+
+ WorldPacket const* Write() override;
+
+ ObjectGuid ObjectGUID;
+ uint8 State = 0;
+ };
}
}
#endif // GOPackets_h__
diff --git a/src/server/game/Server/Protocol/Opcodes.cpp b/src/server/game/Server/Protocol/Opcodes.cpp
index 3c3226e16e8..75c8222b069 100644
--- a/src/server/game/Server/Protocol/Opcodes.cpp
+++ b/src/server/game/Server/Protocol/Opcodes.cpp
@@ -1281,7 +1281,7 @@ void OpcodeTable::Initialize()
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GAME_OBJECT_PLAY_SPELL_VISUAL, STATUS_NEVER, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GAME_OBJECT_PLAY_SPELL_VISUAL_KIT, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GAME_OBJECT_RESET_STATE, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
- DEFINE_SERVER_OPCODE_HANDLER(SMSG_GAME_OBJECT_SET_STATE_LOCAL, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
+ DEFINE_SERVER_OPCODE_HANDLER(SMSG_GAME_OBJECT_SET_STATE_LOCAL, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GAME_OBJECT_UI_LINK, STATUS_NEVER, CONNECTION_TYPE_INSTANCE);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GAME_SPEED_SET, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
DEFINE_SERVER_OPCODE_HANDLER(SMSG_GAME_TIME_SET, STATUS_UNHANDLED, CONNECTION_TYPE_REALM);
diff --git a/src/server/scripts/Maelstrom/Stonecore/boss_slabhide.cpp b/src/server/scripts/Maelstrom/Stonecore/boss_slabhide.cpp
index e6d56e6de51..8c0f1c056c3 100644
--- a/src/server/scripts/Maelstrom/Stonecore/boss_slabhide.cpp
+++ b/src/server/scripts/Maelstrom/Stonecore/boss_slabhide.cpp
@@ -537,7 +537,7 @@ class BehindObjectCheck
bool operator()(WorldObject* unit)
{
for (std::list<GameObject*>::const_iterator itr = objectList.begin(); itr != objectList.end(); ++itr)
- if (!(*itr)->IsInvisibleDueToDespawn() && (*itr)->IsInBetween(caster, unit, 1.5f))
+ if (!(*itr)->IsInvisibleDueToDespawn(unit) && (*itr)->IsInBetween(caster, unit, 1.5f))
return true;
return false;
}
diff --git a/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_forgemaster_garfrost.cpp b/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_forgemaster_garfrost.cpp
index 36984217ccc..d34a99d67b6 100644
--- a/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_forgemaster_garfrost.cpp
+++ b/src/server/scripts/Northrend/FrozenHalls/PitOfSaron/boss_forgemaster_garfrost.cpp
@@ -274,7 +274,7 @@ private:
{
for (std::list<GameObject*>::const_iterator itr = blockList.begin(); itr != blockList.end(); ++itr)
{
- if (!(*itr)->IsInvisibleDueToDespawn())
+ if (!(*itr)->IsInvisibleDueToDespawn(target))
{
if ((*itr)->IsInBetween(caster, target, 4.0f))
{