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;
|
||||
}
|
||||
|
||||
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())
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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; }
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user