Core/GameObjects: Implemented per player gameobject state and visibility for looted non-consumable chests

Port From (https://github.com/TrinityCore/TrinityCore/commit/879c0cccfcb9224f9e15cbed926c57a4e010a070)
This commit is contained in:
hondacrx
2022-10-21 17:34:52 -04:00
parent cf7c183a51
commit 0b2621b348
6 changed files with 135 additions and 9 deletions
+2 -2
View File
@@ -1731,9 +1731,9 @@ namespace Game.Entities
return true;
}
public override bool IsInvisibleDueToDespawn()
public override bool IsInvisibleDueToDespawn(WorldObject seer)
{
if (base.IsInvisibleDueToDespawn())
if (base.IsInvisibleDueToDespawn(seer))
return true;
if (IsAlive() || m_corpseRemoveTime > GameTime.GetGameTime())
+108 -3
View File
@@ -426,6 +426,40 @@ namespace Game.Entities
if (m_goTypeImpl != null)
m_goTypeImpl.Update(diff);
if (m_perPlayerState != null)
{
foreach (var (guid, playerState) in m_perPlayerState.ToList())
{
if (playerState.ValidUntil > GameTime.GetSystemTime())
continue;
Player seer = Global.ObjAccessor.GetPlayer(this, guid);
bool needsStateUpdate = playerState.State != GetGoState();
bool despawned = playerState.Despawned;
m_perPlayerState.Remove(guid);
if (seer)
{
if (despawned)
{
seer.UpdateVisibilityOf(this);
}
else if (needsStateUpdate)
{
ObjectFieldData objMask = new();
GameObjectFieldData goMask = new();
goMask.MarkChanged(m_gameObjectData.State);
UpdateData udata = new(GetMapId());
BuildValuesUpdateForPlayerWithMask(udata, objMask.GetUpdateMask(), goMask.GetUpdateMask(), seer);
udata.BuildPacket(out UpdateObject packet);
seer.SendPacket(packet);
}
}
}
}
switch (m_lootState)
{
case LootState.NotReady:
@@ -1311,15 +1345,22 @@ namespace Game.Entities
return false;
}
public override bool IsInvisibleDueToDespawn()
public override bool IsInvisibleDueToDespawn(WorldObject seer)
{
if (base.IsInvisibleDueToDespawn())
if (base.IsInvisibleDueToDespawn(seer))
return true;
// Despawned
if (!IsSpawned())
return true;
if (m_perPlayerState != null)
{
PerPlayerState state = m_perPlayerState.LookupByKey(seer.GetGUID());
if (state != null && state.Despawned)
return true;
}
return false;
}
@@ -2797,7 +2838,30 @@ namespace Game.Entities
return true;
}
public void OnLootRelease(Player looter)
{
switch (GetGoType())
{
case GameObjectTypes.Chest:
{
GameObjectTemplate goInfo = GetGoInfo();
if (goInfo.Chest.consumable == 0 && goInfo.Chest.chestPersonalLoot != 0)
{
PerPlayerState perPlayerState = GetOrCreatePerPlayerStates()[looter.GetGUID()];
perPlayerState.ValidUntil = GameTime.GetSystemTime() + (goInfo.Chest.chestRestockTime != 0
? TimeSpan.FromSeconds(goInfo.Chest.chestRestockTime)
: TimeSpan.FromSeconds(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;
}
}
}
public void SetGoState(GameObjectState state)
{
GameObjectState oldState = GetGoState();
@@ -2822,6 +2886,30 @@ namespace Game.Entities
}
}
public GameObjectState GetGoStateFor(ObjectGuid viewer)
{
if (m_perPlayerState != null)
{
PerPlayerState state = m_perPlayerState.LookupByKey(viewer);
if (state != null && state.State.HasValue)
return state.State.Value;
}
return GetGoState();
}
void SetGoStateFor(GameObjectState state, Player viewer)
{
PerPlayerState perPlayerState = GetOrCreatePerPlayerStates()[viewer.GetGUID()];
perPlayerState.ValidUntil = GameTime.GetSystemTime() + TimeSpan.FromSeconds(m_respawnDelayTime);
perPlayerState.State = state;
GameObjectSetStateLocal setStateLocal = new();
setStateLocal.ObjectGUID = GetGUID();
setStateLocal.State = (byte)state;
viewer.SendPacket(setStateLocal);
}
public void SetDisplayId(uint displayid)
{
SetUpdateFieldValue(m_values.ModifyValue(m_gameObjectData).ModifyValue(m_gameObjectData.DisplayID), displayid);
@@ -3289,6 +3377,14 @@ namespace Game.Entities
|| m_goValue.CapturePoint.State == BattlegroundCapturePointState.HordeCaptured;
}
Dictionary<ObjectGuid, PerPlayerState> GetOrCreatePerPlayerStates()
{
if (m_perPlayerState == null)
m_perPlayerState = new();
return m_perPlayerState;
}
public override ushort GetAIAnimKitId() { return _animKitId; }
public uint GetWorldEffectID() { return _worldEffectID; }
@@ -3516,6 +3612,8 @@ namespace Game.Entities
ushort _animKitId;
uint _worldEffectID;
Dictionary<ObjectGuid, PerPlayerState> m_perPlayerState;
GameObjectState m_prevGoState; // What state to set whenever resetting
Dictionary<uint, ObjectGuid> ChairListSlots = new();
@@ -4024,4 +4122,11 @@ namespace Game.Entities
}
}
}
public class PerPlayerState
{
public DateTime ValidUntil = DateTime.MinValue;
public GameObjectState? State;
public bool Despawned;
}
}
@@ -4997,7 +4997,7 @@ namespace Game.Entities
data.WriteFloat(rotation.Z);
data.WriteFloat(rotation.W);
data.WriteUInt32(FactionTemplate);
data.WriteInt8(State);
data.WriteInt8(GetViewerGameObjectState(this, owner, receiver));
data.WriteInt8(TypeID);
data.WriteUInt8(PercentHealth);
data.WriteUInt32(ArtKit);
@@ -5104,7 +5104,7 @@ namespace Game.Entities
}
if (changesMask[14])
{
data.WriteInt8(State);
data.WriteInt8(GetViewerGameObjectState(this, owner, receiver));
}
if (changesMask[15])
{
@@ -5167,6 +5167,11 @@ namespace Game.Entities
return flags;
}
sbyte GetViewerGameObjectState(GameObjectFieldData gameObjectData, GameObject gameObject, Player receiver)
{
return (sbyte)gameObject.GetGoStateFor(receiver.GetGUID());
}
}
public class DynamicObjectData : BaseUpdateData<DynamicObject>
+2 -2
View File
@@ -1218,7 +1218,7 @@ namespace Game.Entities
return false;
}
if (obj.IsInvisibleDueToDespawn())
if (obj.IsInvisibleDueToDespawn(this))
return false;
if (!CanDetect(obj, ignoreStealth, checkAlert))
@@ -3038,7 +3038,7 @@ namespace Game.Entities
public virtual bool IsNeverVisibleFor(WorldObject seer) { return !IsInWorld || IsDestroyedObject(); }
public virtual bool IsAlwaysVisibleFor(WorldObject seer) { return false; }
public virtual bool IsInvisibleDueToDespawn() { return false; }
public virtual bool IsInvisibleDueToDespawn(WorldObject seer) { return false; }
public virtual bool IsAlwaysDetectableFor(WorldObject seer) { return false; }
public virtual bool LoadFromDB(ulong spawnId, Map map, bool addToMap, bool allowDuplicate) { return true; }
+2
View File
@@ -298,6 +298,8 @@ namespace Game
}
else if (go.IsFullyLooted())
go.SetLootState(LootState.JustDeactivated);
go.OnLootRelease(player);
}
else
{
@@ -167,4 +167,18 @@ namespace Game.Networking.Packets
public ObjectGuid ActivatorGUID;
public uint SpellVisualID;
}
class GameObjectSetStateLocal : ServerPacket
{
public GameObjectSetStateLocal() : base(ServerOpcodes.GameObjectSetStateLocal, ConnectionType.Instance) { }
public override void Write()
{
_worldPacket.WritePackedGuid(ObjectGUID);
_worldPacket.WriteUInt8(State);
}
public ObjectGuid ObjectGUID;
public byte State;
}
}