using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using ACE.Entity.Enum;
using ACE.Server.Physics.Managers;
using ACE.Server.WorldObjects;
namespace ACE.Server.Physics.Common
{
///
/// Player visibility tracking
/// Keeps track of which objects are currently known / visible to a player
///
public class ObjectMaint
{
///
/// The client automatically removes known objects if they remain outside visibility for this amount of time
///
public const float DestructionTime = 25.0f;
private static readonly ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
///
/// The owner of this ObjectMaint instance
/// This is who we are tracking object visibility for, ie. a Player
///
public PhysicsObj PhysicsObj { get; }
///
/// This list of objects that are known to a player
///
///
///
/// - When an object enters PVS / VisibleCell range of a player,
/// it is added to this list, and the VisibleObject list
///
/// - When an object exits PVS / VisibleCell range of a player,
/// it is removed from the VisibleObject list, and added to the destruction queue.
/// if it remains outside PVS for DestructionTime, the client automatically culls the object,
/// and it is removed from this list of known objects.
///
/// - This is only maintained for players.
///
private Dictionary KnownObjects { get; } = new Dictionary();
///
/// This list of objects that are currently within PVS / VisibleCell range
/// only maintained for players
///
private Dictionary VisibleObjects { get; } = new Dictionary();
///
/// Objects that were previously visible to the client,
/// but have been outside the PVS for less than 25 seconds
/// only maintained for players
///
private Dictionary DestructionQueue { get; } = new Dictionary();
///
/// A list of players that currently know about this object
/// This is maintained for all server-spawned WorldObjects, and is used for broadcasting
///
private Dictionary KnownPlayers { get; } = new Dictionary();
///
/// For monster and CombatPet FindNextTarget
/// - for monsters, contains players and combat pets
/// - for combat pets, contains monsters
///
private Dictionary VisibleTargets { get; } = new Dictionary();
///
/// Handles monsters targeting things they would not normally target
/// - For faction mobs, retaliate against same-faction players and combat pets
/// - For regular monsters, retaliate against faction mobs
/// - For regular monsters that do *not* have a FoeType, retaliate against monsters that are foes with this creature
///
private Dictionary RetaliateTargets { get; } = new Dictionary();
// Client structures -
// When client unloads a cell/landblock, but still knows about objects in those cells?
//public Dictionary LostCellTable;
//public List NullObjectTable;
//public Dictionary WeenieObjectTable;
//public List NullWeenieObjectTable;
public ObjectMaint() { }
public ObjectMaint(PhysicsObj obj)
{
PhysicsObj = obj;
}
public PhysicsObj GetKnownObject(uint objectGuid)
{
rwLock.EnterReadLock();
try
{
KnownObjects.TryGetValue(objectGuid, out var obj);
return obj;
}
finally
{
rwLock.ExitReadLock();
}
}
public int GetKnownObjectsCount()
{
rwLock.EnterReadLock();
try
{
return KnownObjects.Count;
}
finally
{
rwLock.ExitReadLock();
}
}
public bool KnownObjectsContainsKey(uint guid)
{
rwLock.EnterReadLock();
try
{
return KnownObjects.ContainsKey(guid);
}
finally
{
rwLock.ExitReadLock();
}
}
public bool KnownObjectsContainsValue(PhysicsObj value)
{
rwLock.EnterReadLock();
try
{
return KnownObjects.Values.Contains(value);
}
finally
{
rwLock.ExitReadLock();
}
}
public List> GetKnownObjectsWhere(Func, bool> predicate)
{
rwLock.EnterReadLock();
try
{
return KnownObjects.Where(predicate).ToList();
}
finally
{
rwLock.ExitReadLock();
}
}
public List GetKnownObjectsValues()
{
rwLock.EnterReadLock();
try
{
return KnownObjects.Values.ToList();
}
finally
{
rwLock.ExitReadLock();
}
}
public List GetKnownObjectsValuesWhere(Func predicate)
{
rwLock.EnterReadLock();
try
{
return KnownObjects.Values.Where(predicate).ToList();
}
finally
{
rwLock.ExitReadLock();
}
}
///
/// Adds an object to the list of known objects
/// only maintained for players
///
/// true if previously an unknown object
public bool AddKnownObject(PhysicsObj obj)
{
rwLock.EnterWriteLock();
try
{
if (KnownObjects.ContainsKey(obj.ID))
return false;
KnownObjects.TryAdd(obj.ID, obj);
// maintain KnownPlayers for both parties
if (obj.IsPlayer)
AddKnownPlayer(obj);
obj.ObjMaint.AddKnownPlayer(PhysicsObj);
return true;
}
finally
{
rwLock.ExitWriteLock();
}
}
///
/// Adds a list of objects to the known objects list
/// only maintained for players
///
/// A list of currently visible objects
/// The list of visible objects that were previously unknown
private List AddKnownObjects(IEnumerable objs)
{
var newObjs = new List();
foreach (var obj in objs)
{
if (AddKnownObject(obj))
newObjs.Add(obj);
}
return newObjs;
}
public bool RemoveKnownObject(PhysicsObj obj, bool inversePlayer = true)
{
rwLock.EnterWriteLock();
try
{
var result = KnownObjects.Remove(obj.ID, out _);
if (inversePlayer && PhysicsObj.IsPlayer)
obj.ObjMaint.RemoveKnownPlayer(PhysicsObj);
return result;
}
finally
{
rwLock.ExitWriteLock();
}
}
public int GetVisibleObjectsCount()
{
rwLock.EnterReadLock();
try
{
return VisibleObjects.Count;
}
finally
{
rwLock.ExitReadLock();
}
}
public bool VisibleObjectsContainsKey(uint key)
{
rwLock.EnterReadLock();
try
{
return VisibleObjects.ContainsKey(key);
}
finally
{
rwLock.ExitReadLock();
}
}
public List> GetVisibleObjectsWhere(Func, bool> predicate)
{
rwLock.EnterReadLock();
try
{
return VisibleObjects.Where(predicate).ToList();
}
finally
{
rwLock.ExitReadLock();
}
}
public List GetVisibleObjectsValues()
{
rwLock.EnterReadLock();
try
{
return VisibleObjects.Values.ToList();
}
finally
{
rwLock.ExitReadLock();
}
}
public List GetVisibleObjectsValuesWhere(Func predicate)
{
rwLock.EnterReadLock();
try
{
return VisibleObjects.Values.Where(predicate).ToList();
}
finally
{
rwLock.ExitReadLock();
}
}
public List GetVisibleObjectsValuesOfTypeCreature()
{
rwLock.EnterReadLock();
try
{
return VisibleObjects.Values.Select(v => v.WeenieObj.WorldObject).OfType().ToList();
}
finally
{
rwLock.ExitReadLock();
}
}
///
/// Returns a list of objects that are currently visible from a cell
///
public List GetVisibleObjects(ObjCell cell, VisibleObjectType type = VisibleObjectType.All)
{
rwLock.EnterReadLock();
try
{
if (PhysicsObj.CurLandblock == null || cell == null)
return new List();
// use PVS / VisibleCells for EnvCells not seen outside
// (mostly dungeons, also some large indoor areas ie. caves)
if (cell is EnvCell envCell)
return GetVisibleObjects(envCell, type);
// use current landblock + adjacents for outdoors,
// and envcells seen from outside (all buildings)
var visibleObjs = PhysicsObj.CurLandblock.GetServerObjects(true);
return ApplyFilter(visibleObjs, type).Where(i => i.ID != PhysicsObj.ID && (!(i.CurCell is EnvCell indoors) || indoors.SeenOutside)).ToList();
}
finally
{
rwLock.ExitReadLock();
}
}
///
/// Returns a list of objects that are currently visible from a dungeon cell
///
private List GetVisibleObjects(EnvCell cell, VisibleObjectType type)
{
var visibleObjs = new List();
// add objects from current cell
cell.AddObjectListTo(visibleObjs);
// add objects from visible cells
foreach (var envCell in cell.VisibleCells.Values)
{
if (envCell != null)
envCell.AddObjectListTo(visibleObjs);
}
// if SeenOutside, add objects from outdoor landblock
if (cell.SeenOutside)
{
var outsideObjs = PhysicsObj.CurLandblock.GetServerObjects(true).Where(i => !(i.CurCell is EnvCell indoors) || indoors.SeenOutside);
visibleObjs.AddRange(outsideObjs);
}
return ApplyFilter(visibleObjs, type).Where(i => !i.DatObject && i.ID != PhysicsObj.ID).Distinct().ToList();
}
public enum VisibleObjectType
{
All,
Players,
AttackTargets
};
private IEnumerable ApplyFilter(List objs, VisibleObjectType type)
{
IEnumerable results = objs;
if (type == VisibleObjectType.Players)
{
results = objs.Where(i => i.IsPlayer);
}
else if (type == VisibleObjectType.AttackTargets)
{
if (PhysicsObj.WeenieObj.IsCombatPet)
results = objs.Where(i => i.WeenieObj.IsMonster && i.WeenieObj.PlayerKillerStatus != PlayerKillerStatus.PK); // combat pets cannot attack pk-only creatures (ie. faction banners)
else if (PhysicsObj.WeenieObj.IsFactionMob)
results = objs.Where(i => i.IsPlayer || i.WeenieObj.IsCombatPet || i.WeenieObj.IsMonster && !i.WeenieObj.SameFaction(PhysicsObj));
else
{
// adding faction mobs here, even though they are retaliate-only, for inverse visible targets
results = objs.Where(i => i.IsPlayer || i.WeenieObj.IsCombatPet && PhysicsObj.WeenieObj.PlayerKillerStatus != PlayerKillerStatus.PK || i.WeenieObj.IsFactionMob || i.WeenieObj.PotentialFoe(PhysicsObj));
}
}
return results;
}
public static bool InitialClamp = true;
public static float InitialClamp_Dist = 112.5f;
public static float InitialClamp_DistSq = InitialClamp_Dist * InitialClamp_Dist;
///
/// Adds an object to the list of visible objects
/// only maintained for players
///
/// TRUE if object was previously not visible, and added to the visible list
public bool AddVisibleObject(PhysicsObj obj)
{
rwLock.EnterWriteLock();
try
{
if (VisibleObjects.ContainsKey(obj.ID))
return false;
if (InitialClamp && !KnownObjects.ContainsKey(obj.ID))
{
var distSq = PhysicsObj.Position.Distance2DSquared(obj.Position);
if (distSq > InitialClamp_DistSq)
return false;
}
//Console.WriteLine($"{PhysicsObj.Name}.AddVisibleObject({obj.Name})");
VisibleObjects.TryAdd(obj.ID, obj);
if (obj.WeenieObj.IsMonster)
obj.ObjMaint.AddVisibleTarget(PhysicsObj, false);
return true;
}
finally
{
rwLock.ExitWriteLock();
}
}
///
/// Add a list of visible objects - maintains both known and visible objects
/// only maintained for players
///
public List AddVisibleObjects(ICollection objs)
{
rwLock.EnterWriteLock();
try
{
var visibleAdded = new List();
foreach (var obj in objs)
{
if (AddVisibleObject(obj))
visibleAdded.Add(obj);
}
RemoveObjectsToBeDestroyed(objs);
return AddKnownObjects(visibleAdded);
}
finally
{
rwLock.ExitWriteLock();
}
}
///
/// Removes an object from the visible objects list
/// only run for players
/// and also removes the player from the object's visible targets list
///
public bool RemoveVisibleObject(PhysicsObj obj, bool inverseTarget = true)
{
rwLock.EnterWriteLock();
try
{
var removed = VisibleObjects.Remove(obj.ID, out _);
if (inverseTarget)
obj.ObjMaint.RemoveVisibleTarget(PhysicsObj);
return removed;
}
finally
{
rwLock.ExitWriteLock();
}
}
public int GetDestructionQueueCount()
{
rwLock.EnterReadLock();
try
{
return DestructionQueue.Count;
}
finally
{
rwLock.ExitReadLock();
}
}
public Dictionary GetDestructionQueueCopy()
{
rwLock.EnterReadLock();
try
{
var result = new Dictionary();
foreach (var kvp in DestructionQueue)
result[kvp.Key] = kvp.Value;
return result;
}
finally
{
rwLock.ExitReadLock();
}
}
///
/// Adds an object to the destruction queue
/// Called when an object exits the PVS range
/// only maintained for players
///
public bool AddObjectToBeDestroyed(PhysicsObj obj)
{
rwLock.EnterWriteLock();
try
{
RemoveVisibleObject(obj);
if (DestructionQueue.ContainsKey(obj))
return false;
DestructionQueue.TryAdd(obj, PhysicsTimer.CurrentTime + DestructionTime);
return true;
}
finally
{
rwLock.ExitWriteLock();
}
}
///
/// Adds a list of objects to the destruction queue
/// only maintained for players
///
public List AddObjectsToBeDestroyed(List objs)
{
rwLock.EnterWriteLock();
try
{
var queued = new List();
foreach (var obj in objs)
{
if (AddObjectToBeDestroyed(obj))
queued.Add(obj);
}
return queued;
}
finally
{
rwLock.ExitWriteLock();
}
}
///
/// Removes an object from the destruction queue if it has been invisible for less than 25s
/// this is only used for players
///
public bool RemoveObjectToBeDestroyed(PhysicsObj obj)
{
rwLock.EnterWriteLock();
try
{
double time = -1;
DestructionQueue.TryGetValue(obj, out time);
if (time != -1 && time > PhysicsTimer.CurrentTime)
{
DestructionQueue.Remove(obj, out _);
return true;
}
return false;
}
finally
{
rwLock.ExitWriteLock();
}
}
///
/// Removes objects from the destruction queue when they re-enter visibility within 25s
/// this is only used for players
///
private void RemoveObjectsToBeDestroyed(IEnumerable objs)
{
foreach (var obj in objs)
RemoveObjectToBeDestroyed(obj);
}
///
/// Removes any objects that have been in the destruction queue for more than 25s
/// this is only used for players
///
public List DestroyObjects()
{
rwLock.EnterWriteLock();
try
{
// find the list of objects that have been in the destruction queue > 25s
var expiredObjs = DestructionQueue.Where(kvp => kvp.Value <= PhysicsTimer.CurrentTime)
.Select(kvp => kvp.Key).ToList();
// remove expired objects from all lists
foreach (var expiredObj in expiredObjs)
RemoveObject(expiredObj);
return expiredObjs;
}
finally
{
rwLock.ExitWriteLock();
}
}
///
/// Removes an object after it has expired from the destruction queue, or it has been destroyed
///
public void RemoveObject(PhysicsObj obj, bool inverse = true)
{
rwLock.EnterWriteLock();
try
{
if (obj == null) return;
RemoveKnownObject(obj, inverse);
RemoveVisibleObject(obj, inverse);
DestructionQueue.Remove(obj, out _);
if (obj.IsPlayer)
RemoveKnownPlayer(obj);
RemoveVisibleTarget(obj);
RemoveRetaliateTarget(obj);
}
finally
{
rwLock.ExitWriteLock();
}
}
public int GetKnownPlayersCount()
{
rwLock.EnterReadLock();
try
{
return KnownPlayers.Count;
}
finally
{
rwLock.ExitReadLock();
}
}
public List> GetKnownPlayersWhere(Func, bool> predicate)
{
rwLock.EnterReadLock();
try
{
return KnownPlayers.Where(predicate).ToList();
}
finally
{
rwLock.ExitReadLock();
}
}
public List GetKnownPlayersValues()
{
rwLock.EnterReadLock();
try
{
return KnownPlayers.Values.ToList();
}
finally
{
rwLock.ExitReadLock();
}
}
public List GetKnownPlayersValuesAsPlayer()
{
rwLock.EnterReadLock();
try
{
return KnownPlayers.Values.Select(v => v.WeenieObj.WorldObject).OfType().ToList();
}
finally
{
rwLock.ExitReadLock();
}
}
public List GetKnownObjectsValuesAsCreature()
{
rwLock.EnterReadLock();
try
{
return KnownObjects.Values.Select(v => v.WeenieObj.WorldObject).OfType().ToList();
}
finally
{
rwLock.ExitReadLock();
}
}
///
/// Adds a player who currently knows about this object
/// - this is maintained for all server-spawned objects
///
/// when an object broadcasts, it sends network messages
/// to all players who currently know about this object
///
/// true if previously an unknown object
private bool AddKnownPlayer(PhysicsObj obj)
{
// only tracking players who know about this object
if (!obj.IsPlayer)
{
Console.WriteLine($"{PhysicsObj.Name}.ObjectMaint.AddKnownPlayer({obj.Name}): tried to add a non-player");
return false;
}
if (PhysicsObj.DatObject)
{
Console.WriteLine($"{PhysicsObj.Name}.ObjectMaint.AddKnownPlayer({obj.Name}): tried to add player for dat object");
return false;
}
//Console.WriteLine($"{PhysicsObj.Name} ({PhysicsObj.ID:X8}).ObjectMaint.AddKnownPlayer({obj.Name})");
// TryAdd for existing keys still modifies collection?
if (KnownPlayers.ContainsKey(obj.ID))
return false;
KnownPlayers.TryAdd(obj.ID, obj);
return true;
}
///
/// Adds a list of players known to this object
///
public List AddKnownPlayers(IEnumerable objs)
{
rwLock.EnterWriteLock();
try
{
var newObjs = new List();
foreach (var obj in objs)
{
if (AddKnownPlayer(obj))
newObjs.Add(obj);
}
return newObjs;
}
finally
{
rwLock.ExitWriteLock();
}
}
///
/// Removes a known player for this object
///
public bool RemoveKnownPlayer(PhysicsObj obj)
{
//Console.WriteLine($"{PhysicsObj.Name} ({PhysicsObj.ID:X8}).ObjectMaint.RemoveKnownPlayer({obj.Name})");
rwLock.EnterReadLock();
try
{
return KnownPlayers.Remove(obj.ID, out _);
}
finally
{
rwLock.ExitReadLock();
}
}
public int GetVisibleTargetsCount()
{
rwLock.EnterReadLock();
try
{
return VisibleTargets.Count;
}
finally
{
rwLock.ExitReadLock();
}
}
public int GetRetaliateTargetsCount()
{
rwLock.EnterReadLock();
try
{
return RetaliateTargets.Count;
}
finally
{
rwLock.ExitReadLock();
}
}
public bool VisibleTargetsContainsKey(uint key)
{
rwLock.EnterReadLock();
try
{
return VisibleTargets.ContainsKey(key);
}
finally
{
rwLock.ExitReadLock();
}
}
public bool RetaliateTargetsContainsKey(uint key)
{
rwLock.EnterReadLock();
try
{
return RetaliateTargets.ContainsKey(key);
}
finally
{
rwLock.ExitReadLock();
}
}
public List GetVisibleTargetsValues()
{
rwLock.EnterReadLock();
try
{
return VisibleTargets.Values.ToList();
}
finally
{
rwLock.ExitReadLock();
}
}
public List GetRetaliateTargetsValues()
{
rwLock.EnterReadLock();
try
{
return RetaliateTargets.Values.ToList();
}
finally
{
rwLock.ExitReadLock();
}
}
public List GetVisibleTargetsValuesOfTypeCreature()
{
rwLock.EnterReadLock();
try
{
return VisibleTargets.Values.Select(v => v.WeenieObj.WorldObject).OfType().ToList();
}
finally
{
rwLock.ExitReadLock();
}
}
///
/// For monster and CombatPet FindNextTarget
/// - for monsters, contains players and combat pets
/// - for combat pets, contains monsters
///
private bool AddVisibleTarget(PhysicsObj obj, bool clamp = true, bool foeType = false)
{
if (PhysicsObj.WeenieObj.IsCombatPet)
{
// only tracking monsters
if (!obj.WeenieObj.IsMonster)
{
Console.WriteLine($"{PhysicsObj.Name}.ObjectMaint.AddVisibleTarget({obj.Name}): tried to add a non-monster");
return false;
}
}
else if (PhysicsObj.WeenieObj.IsFactionMob)
{
// only tracking players, combat pets, and monsters of differing faction
if (!obj.IsPlayer && !obj.WeenieObj.IsCombatPet && (!obj.WeenieObj.IsMonster || PhysicsObj.WeenieObj.SameFaction(obj)))
{
Console.WriteLine($"{PhysicsObj.Name}.ObjectMaint.AddVisibleTarget({obj.Name}): tried to add a non-player / non-combat pet / non-opposing faction mob");
return false;
}
}
else
{
// handle special case:
// we want to select faction mobs for monsters inverse targets,
// but not add to the original monster
if (obj.WeenieObj.IsFactionMob)
{
obj.ObjMaint.AddVisibleTarget(PhysicsObj);
return false;
}
// handle special case:
// if obj has a FoeType of this creature, and this creature doesn't have a FoeType for obj,
// we only want to perform the inverse
if (obj.WeenieObj.FoeType != null && obj.WeenieObj.FoeType == PhysicsObj.WeenieObj.WorldObject?.CreatureType &&
(PhysicsObj.WeenieObj.FoeType == null || obj.WeenieObj.WorldObject != null && PhysicsObj.WeenieObj.FoeType != obj.WeenieObj.WorldObject.CreatureType))
{
obj.ObjMaint.AddVisibleTarget(PhysicsObj);
return false;
}
// only tracking players and combat pets
if (!obj.IsPlayer && !obj.WeenieObj.IsCombatPet && PhysicsObj.WeenieObj.FoeType == null)
{
Console.WriteLine($"{PhysicsObj.Name}.ObjectMaint.AddVisibleTarget({obj.Name}): tried to add a non-player / non-combat pet");
return false;
}
}
if (PhysicsObj.DatObject)
{
Console.WriteLine($"{PhysicsObj.Name}.ObjectMaint.AddVisibleTarget({obj.Name}): tried to add player for dat object");
return false;
}
if (clamp && InitialClamp && obj.IsPlayer && !obj.ObjMaint.KnownObjects.ContainsKey(obj.ID))
{
var distSq = PhysicsObj.Position.Distance2DSquared(obj.Position);
if (distSq > InitialClamp_DistSq)
return false;
}
// TryAdd for existing keys still modifies collection?
if (VisibleTargets.ContainsKey(obj.ID))
return false;
//Console.WriteLine($"{PhysicsObj.Name} ({PhysicsObj.ID:X8}).ObjectMaint.AddVisibleTarget({obj.Name})");
VisibleTargets.Add(obj.ID, obj);
// maintain inverse for monsters / combat pets
if (!obj.IsPlayer)
obj.ObjMaint.AddVisibleTarget(PhysicsObj);
return true;
}
///
/// For monster and CombatPet FindNextTarget
/// - for monsters, contains players and combat pets
/// - for combat pets, contains monsters
///
public List AddVisibleTargets(IEnumerable objs)
{
rwLock.EnterWriteLock();
try
{
var visibleAdded = new List();
foreach (var obj in objs)
{
if (AddVisibleTarget(obj))
visibleAdded.Add(obj);
}
return AddKnownPlayers(visibleAdded.Where(o => o.IsPlayer));
}
finally
{
rwLock.ExitWriteLock();
}
}
private bool RemoveVisibleTarget(PhysicsObj obj)
{
//Console.WriteLine($"{PhysicsObj.Name} ({PhysicsObj.ID:X8}).ObjectMaint.RemoveVisibleTarget({obj.Name})");
return VisibleTargets.Remove(obj.ID);
}
public List GetVisibleObjectsDist(ObjCell cell, VisibleObjectType type)
{
rwLock.EnterReadLock();
try
{
var visibleObjs = GetVisibleObjects(cell, type);
var dist = new List();
foreach (var obj in visibleObjs)
{
var distSq = PhysicsObj.Position.Distance2DSquared(obj.Position);
if (distSq <= InitialClamp_DistSq)
dist.Add(obj);
}
return dist;
}
finally
{
rwLock.ExitReadLock();
}
}
///
/// Adds a retaliate target for a monster that it would not normally attack
///
public void AddRetaliateTarget(PhysicsObj obj)
{
rwLock.EnterWriteLock();
try
{
if (RetaliateTargets.ContainsKey(obj.ID))
{
//Console.WriteLine($"{PhysicsObj.Name}.AddRetaliateTarget({obj.Name}) - retaliate target already exists");
return;
}
//Console.WriteLine($"{PhysicsObj.Name}.AddRetaliateTarget({obj.Name})");
RetaliateTargets.Add(obj.ID, obj);
// we're going to add retaliate targets to the list of visible targets as well,
// so that we don't have to traverse both VisibleTargets and RetaliateTargets
// in all of the logic based on VisibleTargets
if (VisibleTargets.ContainsKey(obj.ID))
{
//Console.WriteLine($"{PhysicsObj.Name}.AddRetaliateTarget({obj.Name}) - visible target already exists");
return;
}
VisibleTargets.Add(obj.ID, obj);
}
finally
{
rwLock.ExitWriteLock();
}
}
///
/// Called when a monster goes back to sleep
///
public void ClearRetaliateTargets()
{
rwLock.EnterWriteLock();
try
{
// remove retaliate targets from visible targets
foreach (var retaliateTarget in RetaliateTargets)
VisibleTargets.Remove(retaliateTarget.Key);
RetaliateTargets.Clear();
}
finally
{
rwLock.ExitWriteLock();
}
}
private bool RemoveRetaliateTarget(PhysicsObj obj)
{
return RetaliateTargets.Remove(obj.ID);
}
///
/// Clears all of the ObjMaint tables for an object
///
private void RemoveAllObjects()
{
KnownObjects.Clear();
VisibleObjects.Clear();
DestructionQueue.Clear();
KnownPlayers.Clear();
VisibleTargets.Clear();
RetaliateTargets.Clear();
}
///
/// The destructor cleans up all ObjMaint references
/// to this PhysicsObj
///
public void DestroyObject()
{
rwLock.EnterWriteLock();
try
{
foreach (var obj in KnownObjects.Values)
obj.ObjMaint.RemoveObject(PhysicsObj);
// we are maintaining the inverses here,
// so passing false to iterate with modifying these collections
foreach (var obj in KnownPlayers.Values)
obj.ObjMaint.RemoveObject(PhysicsObj, false);
foreach (var obj in VisibleTargets.Values)
obj.ObjMaint.RemoveObject(PhysicsObj, false);
foreach (var obj in RetaliateTargets.Values)
obj.ObjMaint.RemoveObject(PhysicsObj, false);
RemoveAllObjects();
ServerObjectManager.RemoveServerObject(PhysicsObj);
}
finally
{
rwLock.ExitWriteLock();
}
}
}
}