aboutsummaryrefslogtreecommitdiff
path: root/src/server/game/Entities/GameObject
diff options
context:
space:
mode:
authorShauren <shauren.trinity@gmail.com>2022-10-21 22:11:00 +0200
committerShauren <shauren.trinity@gmail.com>2022-10-21 22:11:00 +0200
commit879c0cccfcb9224f9e15cbed926c57a4e010a070 (patch)
treebb01bada60e1c943b6981919945b1a91b4da1c11 /src/server/game/Entities/GameObject
parent43ac108a527fc593b4d5fa1fb06ff98e843d0b84 (diff)
Core/GameObjects: Implemented per player gameobject state and visibility for looted non-consumable chests
Diffstat (limited to 'src/server/game/Entities/GameObject')
-rw-r--r--src/server/game/Entities/GameObject/GameObject.cpp100
-rw-r--r--src/server/game/Entities/GameObject/GameObject.h16
2 files changed, 113 insertions, 3 deletions
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