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:
@@ -1731,9 +1731,9 @@ namespace Game.Entities
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsInvisibleDueToDespawn()
|
public override bool IsInvisibleDueToDespawn(WorldObject seer)
|
||||||
{
|
{
|
||||||
if (base.IsInvisibleDueToDespawn())
|
if (base.IsInvisibleDueToDespawn(seer))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (IsAlive() || m_corpseRemoveTime > GameTime.GetGameTime())
|
if (IsAlive() || m_corpseRemoveTime > GameTime.GetGameTime())
|
||||||
|
|||||||
@@ -426,6 +426,40 @@ namespace Game.Entities
|
|||||||
if (m_goTypeImpl != null)
|
if (m_goTypeImpl != null)
|
||||||
m_goTypeImpl.Update(diff);
|
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)
|
switch (m_lootState)
|
||||||
{
|
{
|
||||||
case LootState.NotReady:
|
case LootState.NotReady:
|
||||||
@@ -1311,15 +1345,22 @@ namespace Game.Entities
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool IsInvisibleDueToDespawn()
|
public override bool IsInvisibleDueToDespawn(WorldObject seer)
|
||||||
{
|
{
|
||||||
if (base.IsInvisibleDueToDespawn())
|
if (base.IsInvisibleDueToDespawn(seer))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// Despawned
|
// Despawned
|
||||||
if (!IsSpawned())
|
if (!IsSpawned())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
if (m_perPlayerState != null)
|
||||||
|
{
|
||||||
|
PerPlayerState state = m_perPlayerState.LookupByKey(seer.GetGUID());
|
||||||
|
if (state != null && state.Despawned)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2798,6 +2839,29 @@ namespace Game.Entities
|
|||||||
return true;
|
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)
|
public void SetGoState(GameObjectState state)
|
||||||
{
|
{
|
||||||
GameObjectState oldState = GetGoState();
|
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)
|
public void SetDisplayId(uint displayid)
|
||||||
{
|
{
|
||||||
SetUpdateFieldValue(m_values.ModifyValue(m_gameObjectData).ModifyValue(m_gameObjectData.DisplayID), 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;
|
|| 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 override ushort GetAIAnimKitId() { return _animKitId; }
|
||||||
|
|
||||||
public uint GetWorldEffectID() { return _worldEffectID; }
|
public uint GetWorldEffectID() { return _worldEffectID; }
|
||||||
@@ -3516,6 +3612,8 @@ namespace Game.Entities
|
|||||||
ushort _animKitId;
|
ushort _animKitId;
|
||||||
uint _worldEffectID;
|
uint _worldEffectID;
|
||||||
|
|
||||||
|
Dictionary<ObjectGuid, PerPlayerState> m_perPlayerState;
|
||||||
|
|
||||||
GameObjectState m_prevGoState; // What state to set whenever resetting
|
GameObjectState m_prevGoState; // What state to set whenever resetting
|
||||||
|
|
||||||
Dictionary<uint, ObjectGuid> ChairListSlots = new();
|
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.Z);
|
||||||
data.WriteFloat(rotation.W);
|
data.WriteFloat(rotation.W);
|
||||||
data.WriteUInt32(FactionTemplate);
|
data.WriteUInt32(FactionTemplate);
|
||||||
data.WriteInt8(State);
|
data.WriteInt8(GetViewerGameObjectState(this, owner, receiver));
|
||||||
data.WriteInt8(TypeID);
|
data.WriteInt8(TypeID);
|
||||||
data.WriteUInt8(PercentHealth);
|
data.WriteUInt8(PercentHealth);
|
||||||
data.WriteUInt32(ArtKit);
|
data.WriteUInt32(ArtKit);
|
||||||
@@ -5104,7 +5104,7 @@ namespace Game.Entities
|
|||||||
}
|
}
|
||||||
if (changesMask[14])
|
if (changesMask[14])
|
||||||
{
|
{
|
||||||
data.WriteInt8(State);
|
data.WriteInt8(GetViewerGameObjectState(this, owner, receiver));
|
||||||
}
|
}
|
||||||
if (changesMask[15])
|
if (changesMask[15])
|
||||||
{
|
{
|
||||||
@@ -5167,6 +5167,11 @@ namespace Game.Entities
|
|||||||
|
|
||||||
return flags;
|
return flags;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sbyte GetViewerGameObjectState(GameObjectFieldData gameObjectData, GameObject gameObject, Player receiver)
|
||||||
|
{
|
||||||
|
return (sbyte)gameObject.GetGoStateFor(receiver.GetGUID());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public class DynamicObjectData : BaseUpdateData<DynamicObject>
|
public class DynamicObjectData : BaseUpdateData<DynamicObject>
|
||||||
|
|||||||
@@ -1218,7 +1218,7 @@ namespace Game.Entities
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (obj.IsInvisibleDueToDespawn())
|
if (obj.IsInvisibleDueToDespawn(this))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!CanDetect(obj, ignoreStealth, checkAlert))
|
if (!CanDetect(obj, ignoreStealth, checkAlert))
|
||||||
@@ -3038,7 +3038,7 @@ namespace Game.Entities
|
|||||||
|
|
||||||
public virtual bool IsNeverVisibleFor(WorldObject seer) { return !IsInWorld || IsDestroyedObject(); }
|
public virtual bool IsNeverVisibleFor(WorldObject seer) { return !IsInWorld || IsDestroyedObject(); }
|
||||||
public virtual bool IsAlwaysVisibleFor(WorldObject seer) { return false; }
|
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 IsAlwaysDetectableFor(WorldObject seer) { return false; }
|
||||||
|
|
||||||
public virtual bool LoadFromDB(ulong spawnId, Map map, bool addToMap, bool allowDuplicate) { return true; }
|
public virtual bool LoadFromDB(ulong spawnId, Map map, bool addToMap, bool allowDuplicate) { return true; }
|
||||||
|
|||||||
@@ -298,6 +298,8 @@ namespace Game
|
|||||||
}
|
}
|
||||||
else if (go.IsFullyLooted())
|
else if (go.IsFullyLooted())
|
||||||
go.SetLootState(LootState.JustDeactivated);
|
go.SetLootState(LootState.JustDeactivated);
|
||||||
|
|
||||||
|
go.OnLootRelease(player);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -167,4 +167,18 @@ namespace Game.Networking.Packets
|
|||||||
public ObjectGuid ActivatorGUID;
|
public ObjectGuid ActivatorGUID;
|
||||||
public uint SpellVisualID;
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user