3189 lines
69 KiB
C#
3189 lines
69 KiB
C#
#region Map Updates
|
|
/*
|
|
* Name: Map Updates
|
|
* Date: 20th August, 2015
|
|
* Author: Vorspire
|
|
* Testing: Punkte
|
|
*
|
|
* Test Methods: Stealth; 250+ clients connected, all moving randomly.
|
|
* Test Results: 35ms average latency (ping) under load.
|
|
*
|
|
* Notes:
|
|
* Until these updates become main-stream, they will remain differentiated
|
|
* by their preprocessor directives. When they are deemed stable enough,
|
|
* old code can be removed, along with the directives.
|
|
*/
|
|
|
|
/*
|
|
* Map_AllUpdates
|
|
*
|
|
* When defined, enables all updates listed below regardless of whether their
|
|
* preprocessor directives are defined.
|
|
*
|
|
* This can be used to compile your server with all of the updates enabled by
|
|
* adding a single preprocessor directive definition to your build solution.
|
|
*/
|
|
//#define Map_AllUpdates
|
|
|
|
/*
|
|
* Map_NewEnumerables
|
|
*
|
|
* When defined, enables a major update to the IPooledEnumerables factory.
|
|
*
|
|
* This update removes the need for enumerator instantiation and replaces
|
|
* them with simple, yet powerful Linq queries.
|
|
*
|
|
* In addition, the PooledEnumerable class is replaced with a compatible,
|
|
* single generic class template and takes advantage of the nature of
|
|
* nested static context to ensure that a buffer pool is available for
|
|
* each type of PooledEnumerable<T> result, where result is T.
|
|
* This update generally increases performance and reduces overall player
|
|
* connection latency.
|
|
*/
|
|
#define Map_NewEnumerables
|
|
|
|
/*
|
|
* UseMaxRange
|
|
*
|
|
* When defined, enables a minor update that forces Get*InRange methods to
|
|
* use Core.GlobalMaxUpdateRange, when no range is specified.
|
|
*
|
|
* By default, a constant range of 18 is used however, Core.GlobalMaxUpdateRange
|
|
* is usually greater than that with a default value of 24.
|
|
*
|
|
* This update will allow things such as Effects to be displayed to more players,
|
|
* as well as increasing the range of player sight.
|
|
*
|
|
* The benefits of this update appeal to players who choose to increase the
|
|
* dimensions of their game window beyond the client's limits.
|
|
* (This can also be beneficial for shards that mainly target the Enhanced client)
|
|
*/
|
|
#define Map_UseMaxRange
|
|
|
|
/*
|
|
* Map_PoolFixColumn
|
|
*
|
|
* When defined, enables aminor update that attempts to improve the performance
|
|
* of Item stack fixing.
|
|
*
|
|
* Item stack fixing is a feature that corrects the Z level of items that
|
|
* are stacked on a single tile.
|
|
*
|
|
* This update also uses linq to increase performance.
|
|
*/
|
|
#define Map_PoolFixColumn
|
|
|
|
/*
|
|
* Map_InternalProtection
|
|
*
|
|
* When defined, enables a minor update that protects the Internal Map from
|
|
* potential name changes and ensures that Maps can be correctly parsed by
|
|
* ID or Name without conflicts.
|
|
*
|
|
* In some cases where the AllMaps list is modified after all Maps have been
|
|
* defined, the Map names may be cached and that cache will become stale.
|
|
* This update removes the caching and uses Linq to improve performance.
|
|
*
|
|
* If you have issues with Map parsing where the Map returns null or an
|
|
* unexpected Map instance, try enabling this update.
|
|
*
|
|
* If your shard implements any kind of feature that modifies (adds or removes)
|
|
* the AllMaps list, then you should enable this update.
|
|
* If this update is not enabled in the case of the above context, issues can
|
|
* be verified by having your system generate a new Map instance and modifying
|
|
* the AllMaps list, then using [Props on an Item or Mobile and selecting the
|
|
* Map property from the Gump; The list of names may not be what you expect,
|
|
* in which case, enabling this update will fix it.
|
|
*/
|
|
#define Map_InternalProtection
|
|
#endregion
|
|
|
|
#region References
|
|
|
|
#region References
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
|
|
using Server.Items;
|
|
using Server.Network;
|
|
using Server.Targeting;
|
|
#endregion
|
|
|
|
#if Map_NewEnumerables || Map_PoolFixColumn || Map_InternalProtection || Map_AllUpdates
|
|
using System.Linq;
|
|
#endif
|
|
#endregion
|
|
|
|
namespace Server
|
|
{
|
|
[Flags]
|
|
public enum MapRules
|
|
{
|
|
None = 0x0000,
|
|
Internal = 0x0001, // Internal map (used for dragging, commodity deeds, etc)
|
|
FreeMovement = 0x0002, // Anyone can move over anyone else without taking stamina loss
|
|
BeneficialRestrictions = 0x0004, // Disallow performing beneficial actions on criminals/murderers
|
|
HarmfulRestrictions = 0x0008, // Disallow performing harmful actions on innocents
|
|
TrammelRules = FreeMovement | BeneficialRestrictions | HarmfulRestrictions,
|
|
FeluccaRules = None
|
|
}
|
|
|
|
public interface IPooledEnumerable : IEnumerable
|
|
{
|
|
void Free();
|
|
}
|
|
|
|
public interface IPooledEnumerable<out T> : IPooledEnumerable, IEnumerable<T>
|
|
{ }
|
|
|
|
public interface IPooledEnumerator<out T> : IEnumerator<T>
|
|
{
|
|
void Free();
|
|
}
|
|
|
|
#if Map_NewEnumerables || Map_AllUpdates
|
|
public static class PooledEnumeration
|
|
{
|
|
public delegate IEnumerable<T> Selector<out T>(Sector sector, Rectangle2D bounds);
|
|
|
|
public static Selector<NetState> ClientSelector { get; set; }
|
|
public static Selector<IEntity> EntitySelector { get; set; }
|
|
public static Selector<Mobile> MobileSelector { get; set; }
|
|
public static Selector<Item> ItemSelector { get; set; }
|
|
public static Selector<BaseMulti> MultiSelector { get; set; }
|
|
public static Selector<StaticTile[]> MultiTileSelector { get; set; }
|
|
|
|
static PooledEnumeration()
|
|
{
|
|
ClientSelector = SelectClients;
|
|
EntitySelector = SelectEntities;
|
|
MobileSelector = SelectMobiles;
|
|
ItemSelector = SelectItems;
|
|
MultiSelector = SelectMultis;
|
|
MultiTileSelector = SelectMultiTiles;
|
|
}
|
|
|
|
public static IEnumerable<NetState> SelectClients(Sector s, Rectangle2D bounds)
|
|
{
|
|
return s.Clients.Where(o => o != null && o.Mobile != null && !o.Mobile.Deleted && bounds.Contains(o.Mobile));
|
|
}
|
|
|
|
public static IEnumerable<IEntity> SelectEntities(Sector s, Rectangle2D bounds)
|
|
{
|
|
return
|
|
Enumerable.Empty<IEntity>()
|
|
.Union(s.Mobiles.Where(o => o != null && !o.Deleted))
|
|
.Union(s.Items.Where(o => o != null && !o.Deleted && o.Parent == null))
|
|
.Where(bounds.Contains);
|
|
}
|
|
|
|
public static IEnumerable<Mobile> SelectMobiles(Sector s, Rectangle2D bounds)
|
|
{
|
|
return s.Mobiles.Where(o => o != null && !o.Deleted && bounds.Contains(o));
|
|
}
|
|
|
|
public static IEnumerable<Item> SelectItems(Sector s, Rectangle2D bounds)
|
|
{
|
|
return s.Items.Where(o => o != null && !o.Deleted && o.Parent == null && bounds.Contains(o));
|
|
}
|
|
|
|
public static IEnumerable<BaseMulti> SelectMultis(Sector s, Rectangle2D bounds)
|
|
{
|
|
return s.Multis.Where(o => o != null && !o.Deleted && bounds.Contains(o.Location));
|
|
}
|
|
|
|
public static IEnumerable<StaticTile[]> SelectMultiTiles(Sector s, Rectangle2D bounds)
|
|
{
|
|
foreach (var o in s.Multis.Where(o => o != null && !o.Deleted))
|
|
{
|
|
var c = o.Components;
|
|
|
|
int x, y, xo, yo;
|
|
StaticTile[] t, r;
|
|
|
|
for (x = bounds.Start.X; x < bounds.End.X; x++)
|
|
{
|
|
xo = x - (o.X + c.Min.X);
|
|
|
|
if (xo < 0 || xo >= c.Width)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
for (y = bounds.Start.Y; y < bounds.End.Y; y++)
|
|
{
|
|
yo = y - (o.Y + c.Min.Y);
|
|
|
|
if (yo < 0 || yo >= c.Height)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
t = c.Tiles[xo][yo];
|
|
|
|
if (t.Length <= 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
r = new StaticTile[t.Length];
|
|
|
|
for (var i = 0; i < t.Length; i++)
|
|
{
|
|
r[i] = t[i];
|
|
r[i].Z += o.Z;
|
|
}
|
|
|
|
yield return r;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static Map.PooledEnumerable<NetState> GetClients(Map map, Rectangle2D bounds)
|
|
{
|
|
return Map.PooledEnumerable<NetState>.Instantiate(map, bounds, ClientSelector ?? SelectClients);
|
|
}
|
|
|
|
public static Map.PooledEnumerable<IEntity> GetEntities(Map map, Rectangle2D bounds)
|
|
{
|
|
return Map.PooledEnumerable<IEntity>.Instantiate(map, bounds, EntitySelector ?? SelectEntities);
|
|
}
|
|
|
|
public static Map.PooledEnumerable<Mobile> GetMobiles(Map map, Rectangle2D bounds)
|
|
{
|
|
return Map.PooledEnumerable<Mobile>.Instantiate(map, bounds, MobileSelector ?? SelectMobiles);
|
|
}
|
|
|
|
public static Map.PooledEnumerable<Item> GetItems(Map map, Rectangle2D bounds)
|
|
{
|
|
return Map.PooledEnumerable<Item>.Instantiate(map, bounds, ItemSelector ?? SelectItems);
|
|
}
|
|
|
|
public static Map.PooledEnumerable<BaseMulti> GetMultis(Map map, Rectangle2D bounds)
|
|
{
|
|
return Map.PooledEnumerable<BaseMulti>.Instantiate(map, bounds, MultiSelector ?? SelectMultis);
|
|
}
|
|
|
|
public static Map.PooledEnumerable<StaticTile[]> GetMultiTiles(Map map, Rectangle2D bounds)
|
|
{
|
|
return Map.PooledEnumerable<StaticTile[]>.Instantiate(map, bounds, MultiTileSelector ?? SelectMultiTiles);
|
|
}
|
|
|
|
public static IEnumerable<Sector> EnumerateSectors(Map map, Rectangle2D bounds)
|
|
{
|
|
if (map == null || map == Map.Internal)
|
|
{
|
|
yield break;
|
|
}
|
|
|
|
int x1 = bounds.Start.X, y1 = bounds.Start.Y, x2 = bounds.End.X, y2 = bounds.End.Y, xSector, ySector;
|
|
|
|
if (!Bound(map, ref x1, ref y1, ref x2, ref y2, out xSector, out ySector))
|
|
{
|
|
yield break;
|
|
}
|
|
|
|
Sector s;
|
|
|
|
var index = 0;
|
|
|
|
while (NextSector(map, x1, y1, x2, y2, ref index, ref xSector, ref ySector, out s))
|
|
{
|
|
yield return s;
|
|
}
|
|
}
|
|
|
|
public static bool Bound(Map map, ref int x1, ref int y1, ref int x2, ref int y2, out int xSector, out int ySector)
|
|
{
|
|
if (map == null || map == Map.Internal)
|
|
{
|
|
xSector = ySector = 0;
|
|
return false;
|
|
}
|
|
|
|
map.Bound(x1, y1, out x1, out y1);
|
|
map.Bound(x2 - 1, y2 - 1, out x2, out y2);
|
|
|
|
x1 >>= Map.SectorShift;
|
|
y1 >>= Map.SectorShift;
|
|
x2 >>= Map.SectorShift;
|
|
y2 >>= Map.SectorShift;
|
|
|
|
xSector = x1;
|
|
ySector = y1;
|
|
|
|
return true;
|
|
}
|
|
|
|
private static bool NextSector(
|
|
Map map,
|
|
int x1,
|
|
int y1,
|
|
int x2,
|
|
int y2,
|
|
ref int index,
|
|
ref int xSector,
|
|
ref int ySector,
|
|
out Sector s)
|
|
{
|
|
if (map == null)
|
|
{
|
|
s = null;
|
|
xSector = ySector = 0;
|
|
return false;
|
|
}
|
|
|
|
if (map == Map.Internal)
|
|
{
|
|
s = map.InvalidSector;
|
|
xSector = ySector = 0;
|
|
return false;
|
|
}
|
|
|
|
if (index++ > 0)
|
|
{
|
|
if (++ySector > y2)
|
|
{
|
|
ySector = y1;
|
|
|
|
if (++xSector > x2)
|
|
{
|
|
xSector = x1;
|
|
|
|
s = map.InvalidSector;
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
s = map.GetRealSector(xSector, ySector);
|
|
return true;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
[Parsable]
|
|
//[CustomEnum( new string[]{ "Felucca", "Trammel", "Ilshenar", "Malas", "Internal" } )]
|
|
public class Map : IComparable, IComparable<Map>
|
|
{
|
|
#region Compile-Time -> Run-Time Support
|
|
#if Map_NewEnumerables || Map_AllUpdates
|
|
public static readonly bool NewEnumerables = true;
|
|
#else
|
|
public static readonly bool NewEnumerables = false;
|
|
#endif
|
|
|
|
#if Map_UseMaxRange || Map_AllUpdates
|
|
public static readonly bool UseMaxRange = true;
|
|
#else
|
|
public static readonly bool UseMaxRange = false;
|
|
#endif
|
|
|
|
#if Map_PoolFixColumn || Map_AllUpdates
|
|
public static readonly bool PoolFixColumn = true;
|
|
#else
|
|
public static readonly bool PoolFixColumn = false;
|
|
#endif
|
|
|
|
#if Map_InternalProtection || Map_AllUpdates
|
|
public static readonly bool InternalProtection = true;
|
|
#else
|
|
public static readonly bool InternalProtection = false;
|
|
#endif
|
|
#endregion
|
|
|
|
public const int SectorSize = 16;
|
|
public const int SectorShift = 4;
|
|
public static int SectorActiveRange = 2;
|
|
|
|
private static readonly Map[] m_Maps = new Map[0x100];
|
|
|
|
public static Map[] Maps { get { return m_Maps; } }
|
|
|
|
public static Map Felucca { get { return m_Maps[0]; } }
|
|
public static Map Trammel { get { return m_Maps[1]; } }
|
|
public static Map Ilshenar { get { return m_Maps[2]; } }
|
|
public static Map Malas { get { return m_Maps[3]; } }
|
|
public static Map Tokuno { get { return m_Maps[4]; } }
|
|
public static Map TerMur { get { return m_Maps[5]; } }
|
|
public static Map Internal { get { return m_Maps[0x7F]; } }
|
|
public static Map Shatteringlands { get { return m_Maps[32]; } }
|
|
public static Map Sosaria { get { return m_Maps[33]; } }
|
|
public static Map Valaria { get { return m_Maps[34]; } }
|
|
public static Map Agearis { get { return m_Maps[35]; } }
|
|
|
|
private static readonly List<Map> m_AllMaps = new List<Map>();
|
|
|
|
public static List<Map> AllMaps { get { return m_AllMaps; } }
|
|
|
|
private readonly int m_MapID;
|
|
private readonly int m_MapIndex;
|
|
private readonly int m_FileIndex;
|
|
|
|
private readonly int m_Width;
|
|
private readonly int m_Height;
|
|
private readonly int m_SectorsWidth;
|
|
private readonly int m_SectorsHeight;
|
|
private readonly Dictionary<string, Region> m_Regions;
|
|
private Region m_DefaultRegion;
|
|
|
|
public int Season { get; set; }
|
|
|
|
private readonly Sector[][] m_Sectors;
|
|
private readonly Sector m_InvalidSector;
|
|
|
|
private TileMatrix m_Tiles;
|
|
|
|
#if Map_InternalProtection || Map_AllUpdates
|
|
public static string[] GetMapNames()
|
|
{
|
|
return m_Maps.Where(m => m != null).Select(m => m.Name).ToArray();
|
|
}
|
|
|
|
public static Map[] GetMapValues()
|
|
{
|
|
return m_Maps.Where(m => m != null).ToArray();
|
|
}
|
|
|
|
public static Map Parse(string value)
|
|
{
|
|
if (String.IsNullOrWhiteSpace(value))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (Insensitive.Equals(value, "Internal"))
|
|
{
|
|
return Internal;
|
|
}
|
|
|
|
int index;
|
|
|
|
if (!Int32.TryParse(value, out index))
|
|
{
|
|
return m_Maps.FirstOrDefault(m => m != null && Insensitive.Equals(m.Name, value));
|
|
}
|
|
|
|
if (index == 127)
|
|
{
|
|
return Internal;
|
|
}
|
|
|
|
return m_Maps.FirstOrDefault(m => m != null && m.MapIndex == index);
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return Name;
|
|
}
|
|
#else
|
|
private static string[] m_MapNames;
|
|
private static Map[] m_MapValues;
|
|
|
|
public static string[] GetMapNames()
|
|
{
|
|
CheckNamesAndValues();
|
|
return m_MapNames;
|
|
}
|
|
|
|
public static Map[] GetMapValues()
|
|
{
|
|
CheckNamesAndValues();
|
|
return m_MapValues;
|
|
}
|
|
|
|
public static Map Parse(string value)
|
|
{
|
|
CheckNamesAndValues();
|
|
|
|
for (int i = 0; i < m_MapNames.Length; ++i)
|
|
{
|
|
if (Insensitive.Equals(m_MapNames[i], value))
|
|
{
|
|
return m_MapValues[i];
|
|
}
|
|
}
|
|
|
|
int index;
|
|
|
|
if (int.TryParse(value, out index))
|
|
{
|
|
if (index >= 0 && index < m_Maps.Length && m_Maps[index] != null)
|
|
{
|
|
return m_Maps[index];
|
|
}
|
|
}
|
|
|
|
throw new ArgumentException("Invalid map name");
|
|
}
|
|
|
|
private static void CheckNamesAndValues()
|
|
{
|
|
if (m_MapNames != null && m_MapNames.Length == m_AllMaps.Count)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_MapNames = new string[m_AllMaps.Count];
|
|
m_MapValues = new Map[m_AllMaps.Count];
|
|
|
|
for (int i = 0; i < m_AllMaps.Count; ++i)
|
|
{
|
|
Map map = m_AllMaps[i];
|
|
|
|
m_MapNames[i] = map.Name;
|
|
m_MapValues[i] = map;
|
|
}
|
|
}
|
|
|
|
public override string ToString()
|
|
{
|
|
return Name;
|
|
}
|
|
#endif
|
|
|
|
public int GetAverageZ(int x, int y)
|
|
{
|
|
int z = 0, avg = 0, top = 0;
|
|
|
|
GetAverageZ(x, y, ref z, ref avg, ref top);
|
|
|
|
return avg;
|
|
}
|
|
|
|
public void GetAverageZ(int x, int y, ref int z, ref int avg, ref int top)
|
|
{
|
|
int zTop = Tiles.GetLandTile(x, y).Z;
|
|
int zLeft = Tiles.GetLandTile(x, y + 1).Z;
|
|
int zRight = Tiles.GetLandTile(x + 1, y).Z;
|
|
int zBottom = Tiles.GetLandTile(x + 1, y + 1).Z;
|
|
|
|
z = zTop;
|
|
if (zLeft < z)
|
|
{
|
|
z = zLeft;
|
|
}
|
|
if (zRight < z)
|
|
{
|
|
z = zRight;
|
|
}
|
|
if (zBottom < z)
|
|
{
|
|
z = zBottom;
|
|
}
|
|
|
|
top = zTop;
|
|
if (zLeft > top)
|
|
{
|
|
top = zLeft;
|
|
}
|
|
if (zRight > top)
|
|
{
|
|
top = zRight;
|
|
}
|
|
if (zBottom > top)
|
|
{
|
|
top = zBottom;
|
|
}
|
|
|
|
if (Math.Abs(zTop - zBottom) > Math.Abs(zLeft - zRight))
|
|
{
|
|
avg = FloorAverage(zLeft, zRight);
|
|
}
|
|
else
|
|
{
|
|
avg = FloorAverage(zTop, zBottom);
|
|
}
|
|
}
|
|
|
|
private static int FloorAverage(int a, int b)
|
|
{
|
|
int v = a + b;
|
|
|
|
if (v < 0)
|
|
{
|
|
--v;
|
|
}
|
|
|
|
return (v / 2);
|
|
}
|
|
|
|
#if Map_NewEnumerables || Map_AllUpdates
|
|
|
|
#region Get*InRange/Bounds
|
|
public IPooledEnumerable<IEntity> GetObjectsInRange(Point3D p)
|
|
{
|
|
#if Map_UseMaxRange || Map_AllUpdates
|
|
return GetObjectsInRange(p, Core.GlobalMaxUpdateRange);
|
|
#else
|
|
return GetObjectsInRange(p, 18);
|
|
#endif
|
|
}
|
|
|
|
public IPooledEnumerable<IEntity> GetObjectsInRange(Point3D p, int range)
|
|
{
|
|
return GetObjectsInBounds(new Rectangle2D(p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1));
|
|
}
|
|
|
|
public IPooledEnumerable<IEntity> GetObjectsInBounds(Rectangle2D bounds)
|
|
{
|
|
return PooledEnumeration.GetEntities(this, bounds);
|
|
}
|
|
|
|
public IPooledEnumerable<NetState> GetClientsInRange(Point3D p)
|
|
{
|
|
#if Map_UseMaxRange || Map_AllUpdates
|
|
return GetClientsInRange(p, Core.GlobalMaxUpdateRange);
|
|
#else
|
|
return GetClientsInRange(p, 18);
|
|
#endif
|
|
}
|
|
|
|
public IPooledEnumerable<NetState> GetClientsInRange(Point3D p, int range)
|
|
{
|
|
return GetClientsInBounds(new Rectangle2D(p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1));
|
|
}
|
|
|
|
public IPooledEnumerable<NetState> GetClientsInBounds(Rectangle2D bounds)
|
|
{
|
|
return PooledEnumeration.GetClients(this, bounds);
|
|
}
|
|
|
|
public IPooledEnumerable<Item> GetItemsInRange(Point3D p)
|
|
{
|
|
#if Map_UseMaxRange || Map_AllUpdates
|
|
return GetItemsInRange(p, Core.GlobalMaxUpdateRange);
|
|
#else
|
|
return GetItemsInRange(p, 18);
|
|
#endif
|
|
}
|
|
|
|
public IPooledEnumerable<Item> GetItemsInRange(Point3D p, int range)
|
|
{
|
|
return GetItemsInBounds(new Rectangle2D(p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1));
|
|
}
|
|
|
|
public IPooledEnumerable<Item> GetItemsInBounds(Rectangle2D bounds)
|
|
{
|
|
return PooledEnumeration.GetItems(this, bounds);
|
|
}
|
|
|
|
public IPooledEnumerable<Mobile> GetMobilesInRange(Point3D p)
|
|
{
|
|
#if Map_UseMaxRange || Map_AllUpdates
|
|
return GetMobilesInRange(p, Core.GlobalMaxUpdateRange);
|
|
#else
|
|
return GetMobilesInRange(p, 18);
|
|
#endif
|
|
}
|
|
|
|
public IPooledEnumerable<Mobile> GetMobilesInRange(Point3D p, int range)
|
|
{
|
|
return GetMobilesInBounds(new Rectangle2D(p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1));
|
|
}
|
|
|
|
public IPooledEnumerable<Mobile> GetMobilesInBounds(Rectangle2D bounds)
|
|
{
|
|
return PooledEnumeration.GetMobiles(this, bounds);
|
|
}
|
|
#endregion
|
|
|
|
public IPooledEnumerable<StaticTile[]> GetMultiTilesAt(int x, int y)
|
|
{
|
|
return PooledEnumeration.GetMultiTiles(this, new Rectangle2D(x, y, 1, 1));
|
|
}
|
|
#else
|
|
|
|
#region Get*InRange/Bounds
|
|
public IPooledEnumerable<IEntity> GetObjectsInRange(Point3D p)
|
|
{
|
|
#if Map_UseMaxRange || Map_AllUpdates
|
|
return GetObjectsInRange(p, Core.GlobalMaxUpdateRange);
|
|
#else
|
|
return GetObjectsInRange(p, 18);
|
|
#endif
|
|
}
|
|
|
|
public IPooledEnumerable<IEntity> GetObjectsInRange(Point3D p, int range)
|
|
{
|
|
if (this == Internal)
|
|
{
|
|
return NullEnumerable<IEntity>.Instance;
|
|
}
|
|
|
|
return
|
|
PooledEnumerable<IEntity>.Instantiate(
|
|
EntityEnumerator.Instantiate(this, new Rectangle2D(p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1)));
|
|
}
|
|
|
|
public IPooledEnumerable<IEntity> GetObjectsInBounds(Rectangle2D bounds)
|
|
{
|
|
if (this == Internal)
|
|
{
|
|
return NullEnumerable<IEntity>.Instance;
|
|
}
|
|
|
|
return PooledEnumerable<IEntity>.Instantiate(EntityEnumerator.Instantiate(this, bounds));
|
|
}
|
|
|
|
public IPooledEnumerable<NetState> GetClientsInRange(Point3D p)
|
|
{
|
|
#if Map_UseMaxRange || Map_AllUpdates
|
|
return GetClientsInRange(p, Core.GlobalMaxUpdateRange);
|
|
#else
|
|
return GetClientsInRange(p, 18);
|
|
#endif
|
|
}
|
|
|
|
public IPooledEnumerable<NetState> GetClientsInRange(Point3D p, int range)
|
|
{
|
|
if (this == Internal)
|
|
{
|
|
return NullEnumerable<NetState>.Instance;
|
|
}
|
|
|
|
return
|
|
PooledEnumerable<NetState>.Instantiate(
|
|
ClientEnumerator.Instantiate(this, new Rectangle2D(p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1)));
|
|
}
|
|
|
|
public IPooledEnumerable<NetState> GetClientsInBounds(Rectangle2D bounds)
|
|
{
|
|
if (this == Internal)
|
|
{
|
|
return NullEnumerable<NetState>.Instance;
|
|
}
|
|
|
|
return PooledEnumerable<NetState>.Instantiate(ClientEnumerator.Instantiate(this, bounds));
|
|
}
|
|
|
|
public IPooledEnumerable<Item> GetItemsInRange(Point3D p)
|
|
{
|
|
#if Map_UseMaxRange || Map_AllUpdates
|
|
return GetItemsInRange(p, Core.GlobalMaxUpdateRange);
|
|
#else
|
|
return GetItemsInRange(p, 18);
|
|
#endif
|
|
}
|
|
|
|
public IPooledEnumerable<Item> GetItemsInRange(Point3D p, int range)
|
|
{
|
|
if (this == Internal)
|
|
{
|
|
return NullEnumerable<Item>.Instance;
|
|
}
|
|
|
|
return
|
|
PooledEnumerable<Item>.Instantiate(
|
|
ItemEnumerator.Instantiate(this, new Rectangle2D(p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1)));
|
|
}
|
|
|
|
public IPooledEnumerable<Item> GetItemsInBounds(Rectangle2D bounds)
|
|
{
|
|
if (this == Internal)
|
|
{
|
|
return NullEnumerable<Item>.Instance;
|
|
}
|
|
|
|
return PooledEnumerable<Item>.Instantiate(ItemEnumerator.Instantiate(this, bounds));
|
|
}
|
|
|
|
public IPooledEnumerable<Mobile> GetMobilesInRange(Point3D p)
|
|
{
|
|
#if Map_UseMaxRange || Map_AllUpdates
|
|
return GetMobilesInRange(p, Core.GlobalMaxUpdateRange);
|
|
#else
|
|
return GetMobilesInRange(p, 18);
|
|
#endif
|
|
}
|
|
|
|
public IPooledEnumerable<Mobile> GetMobilesInRange(Point3D p, int range)
|
|
{
|
|
if (this == Internal)
|
|
{
|
|
return NullEnumerable<Mobile>.Instance;
|
|
}
|
|
|
|
return
|
|
PooledEnumerable<Mobile>.Instantiate(
|
|
MobileEnumerator.Instantiate(this, new Rectangle2D(p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1)));
|
|
}
|
|
|
|
public IPooledEnumerable<Mobile> GetMobilesInBounds(Rectangle2D bounds)
|
|
{
|
|
if (this == Internal)
|
|
{
|
|
return NullEnumerable<Mobile>.Instance;
|
|
}
|
|
|
|
return PooledEnumerable<Mobile>.Instantiate(MobileEnumerator.Instantiate(this, bounds));
|
|
}
|
|
#endregion
|
|
|
|
public IPooledEnumerable<StaticTile[]> GetMultiTilesAt(int x, int y)
|
|
{
|
|
if (this == Internal)
|
|
{
|
|
return NullEnumerable<StaticTile[]>.Instance;
|
|
}
|
|
|
|
Sector sector = GetSector(x, y);
|
|
|
|
if (sector.Multis.Count == 0)
|
|
{
|
|
return NullEnumerable<StaticTile[]>.Instance;
|
|
}
|
|
|
|
return PooledEnumerable<StaticTile[]>.Instantiate(MultiTileEnumerator.Instantiate(sector, new Point2D(x, y)));
|
|
}
|
|
#endif
|
|
|
|
#region CanFit
|
|
public bool CanFit(Point3D p, int height, bool checkBlocksFit)
|
|
{
|
|
return CanFit(p.m_X, p.m_Y, p.m_Z, height, checkBlocksFit, true, true);
|
|
}
|
|
|
|
public bool CanFit(Point3D p, int height, bool checkBlocksFit, bool checkMobiles)
|
|
{
|
|
return CanFit(p.m_X, p.m_Y, p.m_Z, height, checkBlocksFit, checkMobiles, true);
|
|
}
|
|
|
|
public bool CanFit(Point2D p, int z, int height, bool checkBlocksFit)
|
|
{
|
|
return CanFit(p.m_X, p.m_Y, z, height, checkBlocksFit, true, true);
|
|
}
|
|
|
|
public bool CanFit(Point3D p, int height)
|
|
{
|
|
return CanFit(p.m_X, p.m_Y, p.m_Z, height, false, true, true);
|
|
}
|
|
|
|
public bool CanFit(Point2D p, int z, int height)
|
|
{
|
|
return CanFit(p.m_X, p.m_Y, z, height, false, true, true);
|
|
}
|
|
|
|
public bool CanFit(int x, int y, int z, int height)
|
|
{
|
|
return CanFit(x, y, z, height, false, true, true);
|
|
}
|
|
|
|
public bool CanFit(int x, int y, int z, int height, bool checksBlocksFit)
|
|
{
|
|
return CanFit(x, y, z, height, checksBlocksFit, true, true);
|
|
}
|
|
|
|
public bool CanFit(int x, int y, int z, int height, bool checkBlocksFit, bool checkMobiles)
|
|
{
|
|
return CanFit(x, y, z, height, checkBlocksFit, checkMobiles, true);
|
|
}
|
|
|
|
public bool CanFit(int x, int y, int z, int height, bool checkBlocksFit, bool checkMobiles, bool requireSurface)
|
|
{
|
|
if (this == Internal)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (x < 0 || y < 0 || x >= m_Width || y >= m_Height)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int lowZ = 0, avgZ = 0, topZ = 0;
|
|
|
|
GetAverageZ(x, y, ref lowZ, ref avgZ, ref topZ);
|
|
|
|
var lt = Tiles.GetLandTile(x, y);
|
|
|
|
var landFlags = TileData.LandTable[lt.ID & TileData.MaxLandValue].Flags;
|
|
|
|
if ((landFlags & TileFlag.Impassable) != 0 && avgZ > z && (z + height) > lowZ)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var hasSurface = (landFlags & TileFlag.Impassable) == 0 && z == avgZ && !lt.Ignored;
|
|
|
|
var staticTiles = Tiles.GetStaticTiles(x, y, true);
|
|
|
|
bool surface, impassable;
|
|
|
|
foreach (var t in staticTiles)
|
|
{
|
|
ItemData id = TileData.ItemTable[t.ID & TileData.MaxItemValue];
|
|
surface = id.Surface;
|
|
impassable = id.Impassable;
|
|
|
|
if ((surface || impassable) && (t.Z + id.CalcHeight) > z && (z + height) > t.Z)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (surface && !impassable && z == (t.Z + id.CalcHeight))
|
|
{
|
|
hasSurface = true;
|
|
}
|
|
}
|
|
|
|
var sector = GetSector(x, y);
|
|
var items = sector.Items;
|
|
var mobs = sector.Mobiles;
|
|
|
|
foreach (var item in items)
|
|
{
|
|
if (!(item is BaseMulti) && item.ItemID <= TileData.MaxItemValue && item.AtWorldPoint(x, y))
|
|
{
|
|
var id = item.ItemData;
|
|
|
|
surface = id.Surface;
|
|
impassable = id.Impassable;
|
|
|
|
if ((surface || impassable || (checkBlocksFit && item.BlocksFit)) && (item.Z + id.CalcHeight) > z &&
|
|
(z + height) > item.Z)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (surface && !impassable && !item.Movable && z == (item.Z + id.CalcHeight))
|
|
{
|
|
hasSurface = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (checkMobiles)
|
|
{
|
|
foreach (var m in mobs)
|
|
{
|
|
if (m.Location.m_X == x && m.Location.m_Y == y && (m.AccessLevel == AccessLevel.Player || !m.Hidden))
|
|
{
|
|
if ((m.Z + 16) > z && (z + height) > m.Z)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return !requireSurface || hasSurface;
|
|
}
|
|
|
|
public bool CanFit(int x, int y, int z, int height, bool checkBlocksFit, bool checkMobiles, bool requireSurface, Mobile mob)
|
|
{
|
|
if (this == Internal)
|
|
return false;
|
|
|
|
if (x < 0 || y < 0 || x >= Width || y >= Height)
|
|
return false;
|
|
|
|
bool hasSurface = false;
|
|
bool checkmob = false;
|
|
bool canswim = false;
|
|
bool cantwalk = false;
|
|
|
|
if (mob != null)
|
|
{
|
|
checkmob = true;
|
|
canswim = mob.CanSwim;
|
|
cantwalk = mob.CantWalk;
|
|
}
|
|
|
|
LandTile lt = Tiles.GetLandTile(x, y);
|
|
int lowZ = 0, avgZ = 0, topZ = 0;
|
|
|
|
bool surface, impassable;
|
|
bool wet = false;
|
|
|
|
GetAverageZ(x, y, ref lowZ, ref avgZ, ref topZ);
|
|
TileFlag landFlags = TileData.LandTable[lt.ID & TileData.MaxLandValue].Flags;
|
|
|
|
impassable = (landFlags & TileFlag.Impassable) != 0;
|
|
|
|
if (checkmob)
|
|
{
|
|
wet = (landFlags & TileFlag.Wet) != 0;
|
|
// dont allow wateronly creatures on land
|
|
if (cantwalk && !wet)
|
|
impassable = true;
|
|
// allow water creatures on water
|
|
if (canswim && wet)
|
|
{
|
|
impassable = false;
|
|
}
|
|
}
|
|
|
|
|
|
if (impassable && avgZ > z && (z + height) > lowZ)
|
|
return false;
|
|
else if (!impassable && z == avgZ && !lt.Ignored)
|
|
hasSurface = true;
|
|
|
|
StaticTile[] staticTiles = Tiles.GetStaticTiles(x, y, true);
|
|
|
|
for (int i = 0; i < staticTiles.Length; ++i)
|
|
{
|
|
ItemData id = TileData.ItemTable[staticTiles[i].ID & TileData.MaxItemValue];
|
|
surface = id.Surface;
|
|
impassable = id.Impassable;
|
|
if (checkmob)
|
|
{
|
|
wet = (id.Flags & TileFlag.Wet) != 0;
|
|
// dont allow wateronly creatures on land
|
|
if (cantwalk && !wet)
|
|
impassable = true;
|
|
// allow water creatures on water
|
|
if (canswim && wet)
|
|
{
|
|
surface = true;
|
|
impassable = false;
|
|
}
|
|
}
|
|
|
|
if ((surface || impassable) && (staticTiles[i].Z + id.CalcHeight) > z && (z + height) > staticTiles[i].Z)
|
|
return false;
|
|
else if (surface && !impassable && z == (staticTiles[i].Z + id.CalcHeight))
|
|
hasSurface = true;
|
|
|
|
|
|
}
|
|
|
|
Sector sector = GetSector(x, y);
|
|
List<Item> items = sector.Items;
|
|
List<Mobile> mobs = sector.Mobiles;
|
|
|
|
for (int i = 0; i < items.Count; ++i)
|
|
{
|
|
Item item = items[i];
|
|
|
|
if (item.ItemID < 0x4000 && item.AtWorldPoint(x, y))
|
|
{
|
|
ItemData id = item.ItemData;
|
|
surface = id.Surface;
|
|
impassable = id.Impassable;
|
|
if (checkmob)
|
|
{
|
|
wet = (id.Flags & TileFlag.Wet) != 0;
|
|
// dont allow wateronly creatures on land
|
|
if (cantwalk && !wet)
|
|
impassable = true;
|
|
// allow water creatures on water
|
|
if (canswim && wet)
|
|
{
|
|
surface = true;
|
|
impassable = false;
|
|
}
|
|
}
|
|
|
|
if ((surface || impassable || (checkBlocksFit && item.BlocksFit)) && (item.Z + id.CalcHeight) > z && (z + height) > item.Z)
|
|
return false;
|
|
else if (surface && !impassable && !item.Movable && z == (item.Z + id.CalcHeight))
|
|
hasSurface = true;
|
|
}
|
|
}
|
|
|
|
if (checkMobiles)
|
|
{
|
|
for (int i = 0; i < mobs.Count; ++i)
|
|
{
|
|
Mobile m = mobs[i];
|
|
|
|
if (m.Location.X == x && m.Location.Y == y && (m.AccessLevel == AccessLevel.Player || !m.Hidden))
|
|
if ((m.Z + 16) > z && (z + height) > m.Z)
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return !requireSurface || hasSurface;
|
|
}
|
|
#endregion
|
|
|
|
#region CanSpawnMobile
|
|
public bool CanSpawnMobile(Point3D p)
|
|
{
|
|
return CanSpawnMobile(p.m_X, p.m_Y, p.m_Z);
|
|
}
|
|
|
|
public bool CanSpawnMobile(Point2D p, int z)
|
|
{
|
|
return CanSpawnMobile(p.m_X, p.m_Y, z);
|
|
}
|
|
|
|
public bool CanSpawnMobile(int x, int y, int z)
|
|
{
|
|
if (!Region.Find(new Point3D(x, y, z), this).AllowSpawn())
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return CanFit(x, y, z, 16);
|
|
}
|
|
#endregion
|
|
|
|
#region Find Item/Mobile
|
|
public TItem FindItem<TItem>(Point3D p, int range = 0) where TItem : Item
|
|
{
|
|
var eable = GetItemsInRange(p, range);
|
|
|
|
foreach (var item in eable)
|
|
{
|
|
if (item.GetType() == typeof(TItem))
|
|
{
|
|
eable.Free();
|
|
return item as TItem;
|
|
}
|
|
}
|
|
|
|
eable.Free();
|
|
return null;
|
|
}
|
|
|
|
public TMob FindMobile<TMob>(Point3D p, int range = 0) where TMob : Mobile
|
|
{
|
|
var eable = GetMobilesInRange(p, range);
|
|
|
|
foreach (var m in eable)
|
|
{
|
|
if (m.GetType() == typeof(TMob))
|
|
{
|
|
eable.Free();
|
|
return m as TMob;
|
|
}
|
|
}
|
|
|
|
eable.Free();
|
|
return null;
|
|
}
|
|
#endregion
|
|
|
|
#region Spawn Position
|
|
public Point3D GetSpawnPosition(Point3D center, int range)
|
|
{
|
|
for (int i = 0; i < 10; i++)
|
|
{
|
|
int x = center.X + (Utility.Random((range * 2) + 1) - range);
|
|
int y = center.Y + (Utility.Random((range * 2) + 1) - range);
|
|
int z = GetAverageZ(x, y);
|
|
|
|
if (CanSpawnMobile(new Point2D(x, y), center.Z))
|
|
return new Point3D(x, y, center.Z);
|
|
|
|
if (CanSpawnMobile(new Point2D(x, y), z))
|
|
return new Point3D(x, y, z);
|
|
}
|
|
|
|
return center;
|
|
}
|
|
|
|
public Point3D GetRandomSpawnPoint(Rectangle2D rec)
|
|
{
|
|
if (this == Internal)
|
|
return Point3D.Zero;
|
|
|
|
int x = Utility.RandomMinMax(rec.X, rec.X + rec.Width);
|
|
int y = Utility.RandomMinMax(rec.Y, rec.Y + rec.Height);
|
|
int z = GetAverageZ(x, y);
|
|
|
|
return new Point3D(x, y, z);
|
|
}
|
|
#endregion
|
|
|
|
private class ZComparer : IComparer<Item>
|
|
{
|
|
public static readonly ZComparer Default = new ZComparer();
|
|
|
|
public int Compare(Item x, Item y)
|
|
{
|
|
if (x == null || y == null)
|
|
return 0;
|
|
|
|
return x.Z.CompareTo(y.Z);
|
|
}
|
|
}
|
|
|
|
#if Map_PoolFixColumn || Map_AllUpdates
|
|
private static readonly Queue<List<Item>> _FixPool = new Queue<List<Item>>(128);
|
|
|
|
private static readonly List<Item> _EmptyFixItems = new List<Item>();
|
|
|
|
private static List<Item> AcquireFixItems(Map map, int x, int y)
|
|
{
|
|
if (map == null || map == Internal || x < 0 || x > map.Width || y < 0 || y > map.Height)
|
|
{
|
|
return _EmptyFixItems;
|
|
}
|
|
|
|
List<Item> pool = null;
|
|
|
|
lock (_FixPool)
|
|
{
|
|
if (_FixPool.Count > 0)
|
|
{
|
|
pool = _FixPool.Dequeue();
|
|
}
|
|
}
|
|
|
|
if (pool == null)
|
|
{
|
|
pool = new List<Item>(128); // Arbitrary limit
|
|
}
|
|
|
|
var eable = map.GetItemsInRange(new Point3D(x, y, 0), 0);
|
|
|
|
pool.AddRange(
|
|
eable.Where(item => item.ItemID <= TileData.MaxItemValue && !(item is BaseMulti))
|
|
.OrderBy(item => item.Z)
|
|
.Take(pool.Capacity));
|
|
|
|
eable.Free();
|
|
|
|
return pool;
|
|
}
|
|
|
|
private static void FreeFixItems(List<Item> pool)
|
|
{
|
|
if (pool == _EmptyFixItems)
|
|
{
|
|
return;
|
|
}
|
|
|
|
pool.Clear();
|
|
|
|
lock (_FixPool)
|
|
{
|
|
if (_FixPool.Count < 128)
|
|
{
|
|
_FixPool.Enqueue(pool);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void FixColumn(int x, int y)
|
|
{
|
|
var landTile = Tiles.GetLandTile(x, y);
|
|
var tiles = Tiles.GetStaticTiles(x, y, true);
|
|
|
|
int landZ = 0, landAvg = 0, landTop = 0;
|
|
|
|
GetAverageZ(x, y, ref landZ, ref landAvg, ref landTop);
|
|
|
|
var items = AcquireFixItems(this, x, y);
|
|
|
|
for (var i = 0; i < items.Count; i++)
|
|
{
|
|
var toFix = items[i];
|
|
|
|
if (!toFix.Movable)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var z = int.MinValue;
|
|
var currentZ = toFix.Z;
|
|
|
|
if (!landTile.Ignored && landAvg <= currentZ)
|
|
{
|
|
z = landAvg;
|
|
}
|
|
|
|
foreach (var tile in tiles)
|
|
{
|
|
var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue];
|
|
|
|
var checkZ = tile.Z;
|
|
var checkTop = checkZ + id.CalcHeight;
|
|
|
|
if (checkTop == checkZ && !id.Surface)
|
|
{
|
|
++checkTop;
|
|
}
|
|
|
|
if (checkTop > z && checkTop <= currentZ)
|
|
{
|
|
z = checkTop;
|
|
}
|
|
}
|
|
|
|
for (var j = 0; j < items.Count; ++j)
|
|
{
|
|
if (j == i)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
var item = items[j];
|
|
var id = item.ItemData;
|
|
|
|
var checkZ = item.Z;
|
|
var checkTop = checkZ + id.CalcHeight;
|
|
|
|
if (checkTop == checkZ && !id.Surface)
|
|
{
|
|
++checkTop;
|
|
}
|
|
|
|
if (checkTop > z && checkTop <= currentZ)
|
|
{
|
|
z = checkTop;
|
|
}
|
|
}
|
|
|
|
if (z != int.MinValue)
|
|
{
|
|
toFix.Location = new Point3D(toFix.X, toFix.Y, z);
|
|
}
|
|
}
|
|
|
|
FreeFixItems(items);
|
|
}
|
|
#else
|
|
public void FixColumn(int x, int y)
|
|
{
|
|
LandTile landTile = Tiles.GetLandTile(x, y);
|
|
|
|
int landZ = 0, landAvg = 0, landTop = 0;
|
|
GetAverageZ(x, y, ref landZ, ref landAvg, ref landTop);
|
|
|
|
StaticTile[] tiles = Tiles.GetStaticTiles(x, y, true);
|
|
|
|
List<Item> items = new List<Item>();
|
|
|
|
IPooledEnumerable<Item> eable = GetItemsInRange(new Point3D(x, y, 0), 0);
|
|
|
|
foreach (Item item in eable)
|
|
{
|
|
if (!(item is BaseMulti) && item.ItemID <= TileData.MaxItemValue)
|
|
{
|
|
items.Add(item);
|
|
|
|
if (items.Count > 100)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
eable.Free();
|
|
|
|
if (items.Count > 100)
|
|
{
|
|
return;
|
|
}
|
|
|
|
items.Sort(ZComparer.Default);
|
|
|
|
for (int i = 0; i < items.Count; ++i)
|
|
{
|
|
Item toFix = items[i];
|
|
|
|
if (!toFix.Movable)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
int z = int.MinValue;
|
|
int currentZ = toFix.Z;
|
|
|
|
if (!landTile.Ignored && landAvg <= currentZ)
|
|
{
|
|
z = landAvg;
|
|
}
|
|
|
|
for (int j = 0; j < tiles.Length; ++j)
|
|
{
|
|
StaticTile tile = tiles[j];
|
|
ItemData id = TileData.ItemTable[tile.ID & TileData.MaxItemValue];
|
|
|
|
int checkZ = tile.Z;
|
|
int checkTop = checkZ + id.CalcHeight;
|
|
|
|
if (checkTop == checkZ && !id.Surface)
|
|
{
|
|
++checkTop;
|
|
}
|
|
|
|
if (checkTop > z && checkTop <= currentZ)
|
|
{
|
|
z = checkTop;
|
|
}
|
|
}
|
|
|
|
for (int j = 0; j < items.Count; ++j)
|
|
{
|
|
if (j == i)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Item item = items[j];
|
|
ItemData id = item.ItemData;
|
|
|
|
int checkZ = item.Z;
|
|
int checkTop = checkZ + id.CalcHeight;
|
|
|
|
if (checkTop == checkZ && !id.Surface)
|
|
{
|
|
++checkTop;
|
|
}
|
|
|
|
if (checkTop > z && checkTop <= currentZ)
|
|
{
|
|
z = checkTop;
|
|
}
|
|
}
|
|
|
|
if (z != int.MinValue)
|
|
{
|
|
toFix.Location = new Point3D(toFix.X, toFix.Y, z);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
/// <summary>
|
|
/// Gets the highest surface that is lower than <paramref name="p" />.
|
|
/// </summary>
|
|
/// <param name="p">The reference point.</param>
|
|
/// <returns>A surface <typeparamref><name>IEntity</name></typeparamref> or <typeparamref><name>Item</name></typeparamref>.</returns>
|
|
public object GetTopSurface(Point3D p)
|
|
{
|
|
if (this == Internal)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
object surface = null;
|
|
int surfaceZ = int.MinValue;
|
|
|
|
LandTile lt = Tiles.GetLandTile(p.X, p.Y);
|
|
|
|
if (!lt.Ignored)
|
|
{
|
|
int avgZ = GetAverageZ(p.X, p.Y);
|
|
|
|
if (avgZ <= p.Z)
|
|
{
|
|
surface = lt;
|
|
surfaceZ = avgZ;
|
|
|
|
if (surfaceZ == p.Z)
|
|
{
|
|
return surface;
|
|
}
|
|
}
|
|
}
|
|
|
|
var staticTiles = Tiles.GetStaticTiles(p.X, p.Y, true);
|
|
|
|
foreach (var tile in staticTiles)
|
|
{
|
|
var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue];
|
|
|
|
if (id.Surface || (id.Flags & TileFlag.Wet) != 0)
|
|
{
|
|
int tileZ = tile.Z + id.CalcHeight;
|
|
|
|
if (tileZ > surfaceZ && tileZ <= p.Z)
|
|
{
|
|
surface = tile;
|
|
surfaceZ = tileZ;
|
|
|
|
if (surfaceZ == p.Z)
|
|
{
|
|
return surface;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var sector = GetSector(p.X, p.Y);
|
|
|
|
foreach (var item in sector.Items)
|
|
{
|
|
if (!(item is BaseMulti) && item.ItemID <= TileData.MaxItemValue && item.AtWorldPoint(p.X, p.Y) && !item.Movable)
|
|
{
|
|
var id = item.ItemData;
|
|
|
|
if (id.Surface || (id.Flags & TileFlag.Wet) != 0)
|
|
{
|
|
int itemZ = item.Z + id.CalcHeight;
|
|
|
|
if (itemZ > surfaceZ && itemZ <= p.Z)
|
|
{
|
|
surface = item;
|
|
surfaceZ = itemZ;
|
|
|
|
if (surfaceZ == p.Z)
|
|
{
|
|
return surface;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return surface;
|
|
}
|
|
|
|
public void Bound(int x, int y, out int newX, out int newY)
|
|
{
|
|
if (x < 0)
|
|
{
|
|
newX = 0;
|
|
}
|
|
else if (x >= m_Width)
|
|
{
|
|
newX = m_Width - 1;
|
|
}
|
|
else
|
|
{
|
|
newX = x;
|
|
}
|
|
|
|
if (y < 0)
|
|
{
|
|
newY = 0;
|
|
}
|
|
else if (y >= m_Height)
|
|
{
|
|
newY = m_Height - 1;
|
|
}
|
|
else
|
|
{
|
|
newY = y;
|
|
}
|
|
}
|
|
|
|
public Point2D Bound(Point2D p)
|
|
{
|
|
int x = p.m_X, y = p.m_Y;
|
|
|
|
if (x < 0)
|
|
{
|
|
x = 0;
|
|
}
|
|
else if (x >= m_Width)
|
|
{
|
|
x = m_Width - 1;
|
|
}
|
|
|
|
if (y < 0)
|
|
{
|
|
y = 0;
|
|
}
|
|
else if (y >= m_Height)
|
|
{
|
|
y = m_Height - 1;
|
|
}
|
|
|
|
return new Point2D(x, y);
|
|
}
|
|
|
|
public Map(int mapID, int mapIndex, int fileIndex, int width, int height, int season, string name, MapRules rules)
|
|
{
|
|
m_MapID = mapID;
|
|
m_MapIndex = mapIndex;
|
|
m_FileIndex = fileIndex;
|
|
m_Width = width;
|
|
m_Height = height;
|
|
|
|
Season = season;
|
|
Name = name;
|
|
Rules = rules;
|
|
|
|
m_Regions = new Dictionary<string, Region>(StringComparer.OrdinalIgnoreCase);
|
|
|
|
m_InvalidSector = new Sector(0, 0, this);
|
|
m_SectorsWidth = width >> SectorShift;
|
|
m_SectorsHeight = height >> SectorShift;
|
|
m_Sectors = new Sector[m_SectorsWidth][];
|
|
}
|
|
|
|
#region GetSector
|
|
public Sector GetSector(Point3D p)
|
|
{
|
|
return InternalGetSector(p.m_X >> SectorShift, p.m_Y >> SectorShift);
|
|
}
|
|
|
|
public Sector GetSector(Point2D p)
|
|
{
|
|
return InternalGetSector(p.m_X >> SectorShift, p.m_Y >> SectorShift);
|
|
}
|
|
|
|
public Sector GetSector(IPoint2D p)
|
|
{
|
|
return InternalGetSector(p.X >> SectorShift, p.Y >> SectorShift);
|
|
}
|
|
|
|
public Sector GetSector(int x, int y)
|
|
{
|
|
return InternalGetSector(x >> SectorShift, y >> SectorShift);
|
|
}
|
|
|
|
public Sector GetRealSector(int x, int y)
|
|
{
|
|
return InternalGetSector(x, y);
|
|
}
|
|
|
|
private Sector InternalGetSector(int x, int y)
|
|
{
|
|
if (x >= 0 && x < m_SectorsWidth && y >= 0 && y < m_SectorsHeight)
|
|
{
|
|
Sector[] xSectors = m_Sectors[x];
|
|
|
|
if (xSectors == null)
|
|
{
|
|
m_Sectors[x] = xSectors = new Sector[m_SectorsHeight];
|
|
}
|
|
|
|
Sector sec = xSectors[y];
|
|
|
|
if (sec == null)
|
|
{
|
|
xSectors[y] = sec = new Sector(x, y, this);
|
|
}
|
|
|
|
return sec;
|
|
}
|
|
else
|
|
{
|
|
return m_InvalidSector;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
public void ActivateSectors(int cx, int cy)
|
|
{
|
|
for (int x = cx - SectorActiveRange; x <= cx + SectorActiveRange; ++x)
|
|
{
|
|
for (int y = cy - SectorActiveRange; y <= cy + SectorActiveRange; ++y)
|
|
{
|
|
var sect = GetRealSector(x, y);
|
|
|
|
if (sect != m_InvalidSector)
|
|
{
|
|
sect.Activate();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void DeactivateSectors(int cx, int cy)
|
|
{
|
|
for (int x = cx - SectorActiveRange; x <= cx + SectorActiveRange; ++x)
|
|
{
|
|
for (int y = cy - SectorActiveRange; y <= cy + SectorActiveRange; ++y)
|
|
{
|
|
var sect = GetRealSector(x, y);
|
|
|
|
if (sect != m_InvalidSector && !PlayersInRange(sect, SectorActiveRange))
|
|
{
|
|
sect.Deactivate();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private bool PlayersInRange(Sector sect, int range)
|
|
{
|
|
for (int x = sect.X - range; x <= sect.X + range; ++x)
|
|
{
|
|
for (int y = sect.Y - range; y <= sect.Y + range; ++y)
|
|
{
|
|
var check = GetRealSector(x, y);
|
|
|
|
if (check != m_InvalidSector && check.Players.Count > 0)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public void OnClientChange(NetState oldState, NetState newState, Mobile m)
|
|
{
|
|
if (this != Internal)
|
|
{
|
|
GetSector(m).OnClientChange(oldState, newState);
|
|
}
|
|
}
|
|
|
|
public virtual void OnEnter(Mobile m)
|
|
{
|
|
if (this != Internal)
|
|
{
|
|
GetSector(m).OnEnter(m);
|
|
}
|
|
}
|
|
|
|
public virtual void OnEnter(Item item)
|
|
{
|
|
if (this == Internal)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GetSector(item).OnEnter(item);
|
|
|
|
if (item is BaseMulti)
|
|
{
|
|
var m = (BaseMulti)item;
|
|
var mcl = m.Components;
|
|
|
|
var start = GetMultiMinSector(item.Location, mcl);
|
|
var end = GetMultiMaxSector(item.Location, mcl);
|
|
|
|
AddMulti(m, start, end);
|
|
}
|
|
}
|
|
|
|
public virtual void OnLeave(Mobile m)
|
|
{
|
|
if (this != Internal)
|
|
{
|
|
GetSector(m).OnLeave(m);
|
|
}
|
|
}
|
|
|
|
public virtual void OnLeave(Item item)
|
|
{
|
|
if (this == Internal)
|
|
{
|
|
return;
|
|
}
|
|
|
|
GetSector(item).OnLeave(item);
|
|
|
|
if (item is BaseMulti)
|
|
{
|
|
var m = (BaseMulti)item;
|
|
var mcl = m.Components;
|
|
|
|
var start = GetMultiMinSector(item.Location, mcl);
|
|
var end = GetMultiMaxSector(item.Location, mcl);
|
|
|
|
RemoveMulti(m, start, end);
|
|
}
|
|
}
|
|
|
|
public void RemoveMulti(BaseMulti m, Sector start, Sector end)
|
|
{
|
|
if (this == Internal)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (int x = start.X; x <= end.X; ++x)
|
|
{
|
|
for (int y = start.Y; y <= end.Y; ++y)
|
|
{
|
|
InternalGetSector(x, y).OnMultiLeave(m);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void AddMulti(BaseMulti m, Sector start, Sector end)
|
|
{
|
|
if (this == Internal)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (int x = start.X; x <= end.X; ++x)
|
|
{
|
|
for (int y = start.Y; y <= end.Y; ++y)
|
|
{
|
|
InternalGetSector(x, y).OnMultiEnter(m);
|
|
}
|
|
}
|
|
}
|
|
|
|
public Sector GetMultiMinSector(Point3D loc, MultiComponentList mcl)
|
|
{
|
|
return GetSector(Bound(new Point2D(loc.m_X + mcl.Min.m_X, loc.m_Y + mcl.Min.m_Y)));
|
|
}
|
|
|
|
public Sector GetMultiMaxSector(Point3D loc, MultiComponentList mcl)
|
|
{
|
|
return GetSector(Bound(new Point2D(loc.m_X + mcl.Max.m_X, loc.m_Y + mcl.Max.m_Y)));
|
|
}
|
|
|
|
public virtual void OnMove(Point3D oldLocation, Mobile m)
|
|
{
|
|
if (this == Internal)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var oldSector = GetSector(oldLocation);
|
|
var newSector = GetSector(m.Location);
|
|
|
|
if (oldSector != newSector)
|
|
{
|
|
oldSector.OnLeave(m);
|
|
newSector.OnEnter(m);
|
|
}
|
|
}
|
|
|
|
public virtual void OnMove(Point3D oldLocation, Item item)
|
|
{
|
|
if (this == Internal)
|
|
{
|
|
return;
|
|
}
|
|
|
|
var oldSector = GetSector(oldLocation);
|
|
var newSector = GetSector(item.Location);
|
|
|
|
if (oldSector != newSector)
|
|
{
|
|
oldSector.OnLeave(item);
|
|
newSector.OnEnter(item);
|
|
}
|
|
|
|
if (item is BaseMulti)
|
|
{
|
|
var m = (BaseMulti)item;
|
|
var mcl = m.Components;
|
|
|
|
var start = GetMultiMinSector(item.Location, mcl);
|
|
var end = GetMultiMaxSector(item.Location, mcl);
|
|
|
|
var oldStart = GetMultiMinSector(oldLocation, mcl);
|
|
var oldEnd = GetMultiMaxSector(oldLocation, mcl);
|
|
|
|
if (oldStart != start || oldEnd != end)
|
|
{
|
|
RemoveMulti(m, oldStart, oldEnd);
|
|
AddMulti(m, start, end);
|
|
}
|
|
}
|
|
}
|
|
|
|
private readonly object tileLock = new object();
|
|
|
|
public TileMatrix Tiles
|
|
{
|
|
get
|
|
{
|
|
if (m_Tiles != null)
|
|
{
|
|
return m_Tiles;
|
|
}
|
|
|
|
lock (tileLock)
|
|
return m_Tiles ?? (m_Tiles = new TileMatrix(this, m_FileIndex, m_MapID, m_Width, m_Height));
|
|
}
|
|
}
|
|
|
|
public int MapID { get { return m_MapID; } }
|
|
|
|
public int MapIndex { get { return m_MapIndex; } }
|
|
|
|
public int Width { get { return m_Width; } }
|
|
|
|
public int Height { get { return m_Height; } }
|
|
|
|
public Dictionary<string, Region> Regions { get { return m_Regions; } }
|
|
|
|
public void RegisterRegion(Region reg)
|
|
{
|
|
string regName = reg.Name;
|
|
|
|
if (regName != null)
|
|
{
|
|
if (m_Regions.ContainsKey(regName))
|
|
{
|
|
Console.WriteLine("Warning: Duplicate region name '{0}' for map '{1}'", regName, Name);
|
|
}
|
|
else
|
|
{
|
|
m_Regions[regName] = reg;
|
|
}
|
|
}
|
|
}
|
|
|
|
public void UnregisterRegion(Region reg)
|
|
{
|
|
string regName = reg.Name;
|
|
|
|
if (regName != null)
|
|
{
|
|
m_Regions.Remove(regName);
|
|
}
|
|
}
|
|
|
|
public Region DefaultRegion
|
|
{
|
|
get { return m_DefaultRegion ?? (m_DefaultRegion = new Region(null, this, 0, new Rectangle3D[0])); }
|
|
set { m_DefaultRegion = value; }
|
|
}
|
|
|
|
public MapRules Rules { get; set; }
|
|
|
|
public Sector InvalidSector { get { return m_InvalidSector; } }
|
|
|
|
public string Name { get; set; }
|
|
|
|
#if Map_NewEnumerables || Map_AllUpdates
|
|
public class NullEnumerable<T> : IPooledEnumerable<T>
|
|
{
|
|
public static readonly NullEnumerable<T> Instance = new NullEnumerable<T>();
|
|
|
|
private readonly IEnumerable<T> _Empty;
|
|
|
|
private NullEnumerable()
|
|
{
|
|
_Empty = Enumerable.Empty<T>();
|
|
}
|
|
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
{
|
|
return _Empty.GetEnumerator();
|
|
}
|
|
|
|
public IEnumerator<T> GetEnumerator()
|
|
{
|
|
return _Empty.GetEnumerator();
|
|
}
|
|
|
|
public void Free()
|
|
{ }
|
|
}
|
|
|
|
public sealed class PooledEnumerable<T> : IPooledEnumerable<T>, IDisposable
|
|
{
|
|
private static readonly Queue<PooledEnumerable<T>> _Buffer = new Queue<PooledEnumerable<T>>(0x400);
|
|
|
|
public static PooledEnumerable<T> Instantiate(Map map, Rectangle2D bounds, PooledEnumeration.Selector<T> selector)
|
|
{
|
|
PooledEnumerable<T> e = null;
|
|
|
|
lock (((ICollection)_Buffer).SyncRoot)
|
|
{
|
|
if (_Buffer.Count > 0)
|
|
{
|
|
e = _Buffer.Dequeue();
|
|
}
|
|
}
|
|
|
|
var pool = PooledEnumeration.EnumerateSectors(map, bounds).SelectMany(s => selector(s, bounds));
|
|
|
|
if (e != null)
|
|
{
|
|
e._Pool.AddRange(pool);
|
|
}
|
|
else
|
|
{
|
|
e = new PooledEnumerable<T>(pool);
|
|
}
|
|
|
|
return e;
|
|
}
|
|
|
|
private bool _IsDisposed;
|
|
|
|
private List<T> _Pool = new List<T>(0x40);
|
|
|
|
private IEnumerable<T> InternalPool
|
|
{
|
|
get
|
|
{
|
|
var i = _Pool.Count;
|
|
|
|
while (--i >= 0)
|
|
{
|
|
if (i < _Pool.Count)
|
|
{
|
|
yield return _Pool[i];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public PooledEnumerable(IEnumerable<T> pool)
|
|
{
|
|
_Pool.AddRange(pool);
|
|
}
|
|
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
{
|
|
return InternalPool.GetEnumerator();
|
|
}
|
|
|
|
public IEnumerator<T> GetEnumerator()
|
|
{
|
|
return InternalPool.GetEnumerator();
|
|
}
|
|
|
|
public void Free()
|
|
{
|
|
if (_IsDisposed)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_Pool.Clear();
|
|
|
|
if (_Pool.Capacity > 0x100)
|
|
{
|
|
_Pool.Capacity = 0x100;
|
|
}
|
|
|
|
lock (((ICollection)_Buffer).SyncRoot)
|
|
{
|
|
_Buffer.Enqueue(this);
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_IsDisposed = true;
|
|
|
|
_Pool.Clear();
|
|
_Pool.TrimExcess();
|
|
_Pool = null;
|
|
}
|
|
}
|
|
#else
|
|
|
|
#region Enumerables
|
|
public class NullEnumerable<T> : IPooledEnumerable<T>
|
|
{
|
|
private readonly InternalEnumerator<T> m_Enumerator;
|
|
|
|
public static readonly NullEnumerable<T> Instance = new NullEnumerable<T>();
|
|
|
|
private NullEnumerable()
|
|
{
|
|
m_Enumerator = new InternalEnumerator<T>();
|
|
}
|
|
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
{
|
|
return m_Enumerator;
|
|
}
|
|
|
|
public IEnumerator<T> GetEnumerator()
|
|
{
|
|
return m_Enumerator;
|
|
}
|
|
|
|
public void Free()
|
|
{ }
|
|
|
|
private class InternalEnumerator<K> : IEnumerator<K>
|
|
{
|
|
public void Reset()
|
|
{ }
|
|
|
|
object IEnumerator.Current { get { return null; } }
|
|
public K Current { get { return default(K); } }
|
|
|
|
public bool MoveNext()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void IDisposable.Dispose()
|
|
{ }
|
|
}
|
|
}
|
|
|
|
private class PooledEnumerable<T> : IPooledEnumerable<T>, IDisposable
|
|
{
|
|
private IPooledEnumerator<T> m_Enumerator;
|
|
|
|
private static readonly Queue<PooledEnumerable<T>> m_InstancePool = new Queue<PooledEnumerable<T>>();
|
|
|
|
public static PooledEnumerable<T> Instantiate(IPooledEnumerator<T> etor)
|
|
{
|
|
PooledEnumerable<T> e = null;
|
|
|
|
lock (m_InstancePool)
|
|
{
|
|
if (m_InstancePool.Count > 0)
|
|
{
|
|
e = m_InstancePool.Dequeue();
|
|
e.m_Enumerator = etor;
|
|
}
|
|
}
|
|
|
|
if (e == null)
|
|
{
|
|
e = new PooledEnumerable<T>(etor);
|
|
}
|
|
|
|
return e;
|
|
}
|
|
|
|
private PooledEnumerable(IPooledEnumerator<T> etor)
|
|
{
|
|
m_Enumerator = etor;
|
|
}
|
|
|
|
IEnumerator IEnumerable.GetEnumerator()
|
|
{
|
|
if (m_Enumerator == null)
|
|
{
|
|
throw new ObjectDisposedException("PooledEnumerable", "GetEnumerator() called after Free()");
|
|
}
|
|
|
|
return m_Enumerator;
|
|
}
|
|
|
|
public IEnumerator<T> GetEnumerator()
|
|
{
|
|
if (m_Enumerator == null)
|
|
{
|
|
throw new ObjectDisposedException("PooledEnumerable", "GetEnumerator() called after Free()");
|
|
}
|
|
|
|
return m_Enumerator;
|
|
}
|
|
|
|
public void Free()
|
|
{
|
|
if (m_Enumerator != null)
|
|
{
|
|
m_Enumerator.Free();
|
|
m_Enumerator = null;
|
|
}
|
|
|
|
lock (m_InstancePool)
|
|
{
|
|
if (m_InstancePool.Count < 200) // Arbitrary
|
|
{
|
|
m_InstancePool.Enqueue(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
// Don't return disposed objects to the instance pool
|
|
//Free();
|
|
|
|
if (m_Enumerator != null)
|
|
{
|
|
m_Enumerator.Free();
|
|
m_Enumerator = null;
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Enumerators
|
|
private class ClientEnumerator : IPooledEnumerator<NetState>
|
|
{
|
|
private Map m_Map;
|
|
private Rectangle2D m_Bounds;
|
|
|
|
private int m_xSector, m_ySector;
|
|
private int m_xSectorStart, m_ySectorStart;
|
|
private int m_xSectorEnd, m_ySectorEnd;
|
|
private List<NetState> m_CurrentList;
|
|
private int m_CurrentIndex;
|
|
|
|
private static readonly Queue<ClientEnumerator> m_InstancePool = new Queue<ClientEnumerator>();
|
|
|
|
public static ClientEnumerator Instantiate(Map map, Rectangle2D bounds)
|
|
{
|
|
ClientEnumerator e = null;
|
|
|
|
lock (m_InstancePool)
|
|
{
|
|
if (m_InstancePool.Count > 0)
|
|
{
|
|
e = m_InstancePool.Dequeue();
|
|
|
|
e.m_Map = map;
|
|
e.m_Bounds = bounds;
|
|
}
|
|
}
|
|
|
|
if (e == null)
|
|
{
|
|
e = new ClientEnumerator(map, bounds);
|
|
}
|
|
|
|
e.Reset();
|
|
|
|
return e;
|
|
}
|
|
|
|
public void Free()
|
|
{
|
|
if (m_Map == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_Map = null;
|
|
|
|
lock (m_InstancePool)
|
|
{
|
|
if (m_InstancePool.Count < 200) // Arbitrary
|
|
{
|
|
m_InstancePool.Enqueue(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
private ClientEnumerator(Map map, Rectangle2D bounds)
|
|
{
|
|
m_Map = map;
|
|
m_Bounds = bounds;
|
|
}
|
|
|
|
public NetState Current { get { return m_CurrentList[m_CurrentIndex]; } }
|
|
|
|
object IEnumerator.Current { get { return m_CurrentList[m_CurrentIndex]; } }
|
|
|
|
void IDisposable.Dispose()
|
|
{ }
|
|
|
|
public bool MoveNext()
|
|
{
|
|
while (true)
|
|
{
|
|
++m_CurrentIndex;
|
|
|
|
if (m_CurrentIndex == m_CurrentList.Count)
|
|
{
|
|
++m_ySector;
|
|
|
|
if (m_ySector > m_ySectorEnd)
|
|
{
|
|
m_ySector = m_ySectorStart;
|
|
++m_xSector;
|
|
|
|
if (m_xSector > m_xSectorEnd)
|
|
{
|
|
m_CurrentIndex = -1;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
m_CurrentIndex = -1;
|
|
m_CurrentList = m_Map.InternalGetSector(m_xSector, m_ySector).Clients;
|
|
}
|
|
else
|
|
{
|
|
Mobile m = m_CurrentList[m_CurrentIndex].Mobile;
|
|
|
|
if (m != null && !m.Deleted && m_Bounds.Contains(m.Location))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
m_Map.Bound(m_Bounds.Start.m_X, m_Bounds.Start.m_Y, out m_xSectorStart, out m_ySectorStart);
|
|
m_Map.Bound(m_Bounds.End.m_X - 1, m_Bounds.End.m_Y - 1, out m_xSectorEnd, out m_ySectorEnd);
|
|
|
|
m_xSector = m_xSectorStart >>= SectorShift;
|
|
m_ySector = m_ySectorStart >>= SectorShift;
|
|
|
|
m_xSectorEnd >>= SectorShift;
|
|
m_ySectorEnd >>= SectorShift;
|
|
|
|
m_CurrentIndex = -1;
|
|
m_CurrentList = m_Map.InternalGetSector(m_xSector, m_ySector).Clients;
|
|
}
|
|
}
|
|
|
|
private class EntityEnumerator : IPooledEnumerator<IEntity>
|
|
{
|
|
private Map m_Map;
|
|
private Rectangle2D m_Bounds;
|
|
|
|
private int m_xSector, m_ySector;
|
|
private int m_xSectorStart, m_ySectorStart;
|
|
private int m_xSectorEnd, m_ySectorEnd;
|
|
private int m_Stage;
|
|
private IList m_CurrentList;
|
|
private int m_CurrentIndex;
|
|
|
|
private static readonly Queue<EntityEnumerator> m_InstancePool = new Queue<EntityEnumerator>();
|
|
|
|
public static EntityEnumerator Instantiate(Map map, Rectangle2D bounds)
|
|
{
|
|
EntityEnumerator e = null;
|
|
|
|
lock (m_InstancePool)
|
|
{
|
|
if (m_InstancePool.Count > 0)
|
|
{
|
|
e = m_InstancePool.Dequeue();
|
|
|
|
e.m_Map = map;
|
|
e.m_Bounds = bounds;
|
|
}
|
|
}
|
|
|
|
if (e == null)
|
|
{
|
|
e = new EntityEnumerator(map, bounds);
|
|
}
|
|
|
|
e.Reset();
|
|
|
|
return e;
|
|
}
|
|
|
|
public void Free()
|
|
{
|
|
if (m_Map == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_Map = null;
|
|
|
|
lock (m_InstancePool)
|
|
{
|
|
if (m_InstancePool.Count < 200) // Arbitrary
|
|
{
|
|
m_InstancePool.Enqueue(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
private EntityEnumerator(Map map, Rectangle2D bounds)
|
|
{
|
|
m_Map = map;
|
|
m_Bounds = bounds;
|
|
}
|
|
|
|
public IEntity Current { get { return (IEntity)m_CurrentList[m_CurrentIndex]; } }
|
|
|
|
object IEnumerator.Current { get { return m_CurrentList[m_CurrentIndex]; } }
|
|
|
|
void IDisposable.Dispose()
|
|
{ }
|
|
|
|
public bool MoveNext()
|
|
{
|
|
while (true)
|
|
{
|
|
++m_CurrentIndex;
|
|
|
|
if (m_CurrentIndex < 0 || m_CurrentIndex > m_CurrentList.Count)
|
|
{
|
|
// Sanity
|
|
Console.WriteLine("EntityEnumerator OOB: {0}", m_CurrentIndex);
|
|
return false;
|
|
}
|
|
|
|
if (m_CurrentIndex == m_CurrentList.Count)
|
|
{
|
|
++m_ySector;
|
|
|
|
if (m_ySector > m_ySectorEnd)
|
|
{
|
|
m_ySector = m_ySectorStart;
|
|
++m_xSector;
|
|
|
|
if (m_xSector > m_xSectorEnd)
|
|
{
|
|
if (m_Stage > 0)
|
|
{
|
|
m_CurrentIndex = -1;
|
|
return false;
|
|
}
|
|
++m_Stage;
|
|
m_xSector = m_xSectorStart >>= SectorShift;
|
|
m_ySector = m_ySectorStart >>= SectorShift;
|
|
}
|
|
}
|
|
|
|
m_CurrentIndex = -1;
|
|
|
|
if (m_Stage == 0)
|
|
{
|
|
m_CurrentList = m_Map.InternalGetSector(m_xSector, m_ySector).Items;
|
|
}
|
|
else
|
|
{
|
|
m_CurrentList = m_Map.InternalGetSector(m_xSector, m_ySector).Mobiles;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
IEntity e = (IEntity)m_CurrentList[m_CurrentIndex];
|
|
|
|
if (e.Deleted)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (e is Item)
|
|
{
|
|
Item item = (Item)e;
|
|
|
|
if (item.Parent != null)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if (m_Bounds.Contains(e.Location))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
m_Map.Bound(m_Bounds.Start.m_X, m_Bounds.Start.m_Y, out m_xSectorStart, out m_ySectorStart);
|
|
m_Map.Bound(m_Bounds.End.m_X - 1, m_Bounds.End.m_Y - 1, out m_xSectorEnd, out m_ySectorEnd);
|
|
|
|
m_xSector = m_xSectorStart >>= SectorShift;
|
|
m_ySector = m_ySectorStart >>= SectorShift;
|
|
|
|
m_xSectorEnd >>= SectorShift;
|
|
m_ySectorEnd >>= SectorShift;
|
|
|
|
m_CurrentIndex = -1;
|
|
m_Stage = 0;
|
|
m_CurrentList = m_Map.InternalGetSector(m_xSector, m_ySector).Items;
|
|
}
|
|
}
|
|
|
|
private class ItemEnumerator : IPooledEnumerator<Item>
|
|
{
|
|
private Map m_Map;
|
|
private Rectangle2D m_Bounds;
|
|
|
|
private int m_xSector, m_ySector;
|
|
private int m_xSectorStart, m_ySectorStart;
|
|
private int m_xSectorEnd, m_ySectorEnd;
|
|
private List<Item> m_CurrentList;
|
|
private int m_CurrentIndex;
|
|
|
|
private static readonly Queue<ItemEnumerator> m_InstancePool = new Queue<ItemEnumerator>();
|
|
|
|
public static ItemEnumerator Instantiate(Map map, Rectangle2D bounds)
|
|
{
|
|
ItemEnumerator e = null;
|
|
|
|
lock (m_InstancePool)
|
|
{
|
|
if (m_InstancePool.Count > 0)
|
|
{
|
|
e = m_InstancePool.Dequeue();
|
|
|
|
e.m_Map = map;
|
|
e.m_Bounds = bounds;
|
|
}
|
|
}
|
|
|
|
if (e == null)
|
|
{
|
|
e = new ItemEnumerator(map, bounds);
|
|
}
|
|
|
|
e.Reset();
|
|
|
|
return e;
|
|
}
|
|
|
|
public void Free()
|
|
{
|
|
if (m_Map == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_Map = null;
|
|
|
|
lock (m_InstancePool)
|
|
{
|
|
if (m_InstancePool.Count < 200) // Arbitrary
|
|
{
|
|
m_InstancePool.Enqueue(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
private ItemEnumerator(Map map, Rectangle2D bounds)
|
|
{
|
|
m_Map = map;
|
|
m_Bounds = bounds;
|
|
}
|
|
|
|
public Item Current { get { return m_CurrentList[m_CurrentIndex]; } }
|
|
|
|
object IEnumerator.Current { get { return m_CurrentList[m_CurrentIndex]; } }
|
|
|
|
void IDisposable.Dispose()
|
|
{ }
|
|
|
|
public bool MoveNext()
|
|
{
|
|
while (true)
|
|
{
|
|
++m_CurrentIndex;
|
|
|
|
if (m_CurrentIndex == m_CurrentList.Count)
|
|
{
|
|
++m_ySector;
|
|
|
|
if (m_ySector > m_ySectorEnd)
|
|
{
|
|
m_ySector = m_ySectorStart;
|
|
++m_xSector;
|
|
|
|
if (m_xSector > m_xSectorEnd)
|
|
{
|
|
m_CurrentIndex = -1;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
m_CurrentIndex = -1;
|
|
m_CurrentList = m_Map.InternalGetSector(m_xSector, m_ySector).Items;
|
|
}
|
|
else
|
|
{
|
|
Item item = m_CurrentList[m_CurrentIndex];
|
|
|
|
if (!item.Deleted && item.Parent == null && m_Bounds.Contains(item.Location))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
m_Map.Bound(m_Bounds.Start.m_X, m_Bounds.Start.m_Y, out m_xSectorStart, out m_ySectorStart);
|
|
m_Map.Bound(m_Bounds.End.m_X - 1, m_Bounds.End.m_Y - 1, out m_xSectorEnd, out m_ySectorEnd);
|
|
|
|
m_xSector = m_xSectorStart >>= SectorShift;
|
|
m_ySector = m_ySectorStart >>= SectorShift;
|
|
|
|
m_xSectorEnd >>= SectorShift;
|
|
m_ySectorEnd >>= SectorShift;
|
|
|
|
m_CurrentIndex = -1;
|
|
m_CurrentList = m_Map.InternalGetSector(m_xSector, m_ySector).Items;
|
|
}
|
|
}
|
|
|
|
private class MobileEnumerator : IPooledEnumerator<Mobile>
|
|
{
|
|
private Map m_Map;
|
|
private Rectangle2D m_Bounds;
|
|
|
|
private int m_xSector, m_ySector;
|
|
private int m_xSectorStart, m_ySectorStart;
|
|
private int m_xSectorEnd, m_ySectorEnd;
|
|
private List<Mobile> m_CurrentList;
|
|
private int m_CurrentIndex;
|
|
|
|
private static readonly Queue<MobileEnumerator> m_InstancePool = new Queue<MobileEnumerator>();
|
|
|
|
public static MobileEnumerator Instantiate(Map map, Rectangle2D bounds)
|
|
{
|
|
MobileEnumerator e = null;
|
|
|
|
lock (m_InstancePool)
|
|
{
|
|
if (m_InstancePool.Count > 0)
|
|
{
|
|
e = m_InstancePool.Dequeue();
|
|
|
|
e.m_Map = map;
|
|
e.m_Bounds = bounds;
|
|
}
|
|
}
|
|
|
|
if (e == null)
|
|
{
|
|
e = new MobileEnumerator(map, bounds);
|
|
}
|
|
|
|
e.Reset();
|
|
|
|
return e;
|
|
}
|
|
|
|
public void Free()
|
|
{
|
|
if (m_Map == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_Map = null;
|
|
|
|
lock (m_InstancePool)
|
|
{
|
|
if (m_InstancePool.Count < 200) // Arbitrary
|
|
{
|
|
m_InstancePool.Enqueue(this);
|
|
}
|
|
}
|
|
}
|
|
|
|
private MobileEnumerator(Map map, Rectangle2D bounds)
|
|
{
|
|
m_Map = map;
|
|
m_Bounds = bounds;
|
|
}
|
|
|
|
public Mobile Current { get { return m_CurrentList[m_CurrentIndex]; } }
|
|
|
|
object IEnumerator.Current { get { return m_CurrentList[m_CurrentIndex]; } }
|
|
|
|
void IDisposable.Dispose()
|
|
{ }
|
|
|
|
public bool MoveNext()
|
|
{
|
|
while (true)
|
|
{
|
|
++m_CurrentIndex;
|
|
|
|
if (m_CurrentIndex == m_CurrentList.Count)
|
|
{
|
|
++m_ySector;
|
|
|
|
if (m_ySector > m_ySectorEnd)
|
|
{
|
|
m_ySector = m_ySectorStart;
|
|
++m_xSector;
|
|
|
|
if (m_xSector > m_xSectorEnd)
|
|
{
|
|
m_CurrentIndex = -1;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
m_CurrentIndex = -1;
|
|
m_CurrentList = m_Map.InternalGetSector(m_xSector, m_ySector).Mobiles;
|
|
}
|
|
else
|
|
{
|
|
Mobile m = m_CurrentList[m_CurrentIndex];
|
|
|
|
if (!m.Deleted && m_Bounds.Contains(m.Location))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
m_Map.Bound(m_Bounds.Start.m_X, m_Bounds.Start.m_Y, out m_xSectorStart, out m_ySectorStart);
|
|
m_Map.Bound(m_Bounds.End.m_X - 1, m_Bounds.End.m_Y - 1, out m_xSectorEnd, out m_ySectorEnd);
|
|
|
|
m_xSector = m_xSectorStart >>= SectorShift;
|
|
m_ySector = m_ySectorStart >>= SectorShift;
|
|
|
|
m_xSectorEnd >>= SectorShift;
|
|
m_ySectorEnd >>= SectorShift;
|
|
|
|
m_CurrentIndex = -1;
|
|
m_CurrentList = m_Map.InternalGetSector(m_xSector, m_ySector).Mobiles;
|
|
}
|
|
}
|
|
|
|
private class MultiTileEnumerator : IPooledEnumerator<StaticTile[]>
|
|
{
|
|
private List<BaseMulti> m_List;
|
|
private Point2D m_Location;
|
|
private StaticTile[] m_Current;
|
|
private int m_Index;
|
|
|
|
private static readonly Queue<MultiTileEnumerator> m_InstancePool = new Queue<MultiTileEnumerator>();
|
|
|
|
public static MultiTileEnumerator Instantiate(Sector sector, Point2D loc)
|
|
{
|
|
MultiTileEnumerator e = null;
|
|
|
|
lock (m_InstancePool)
|
|
{
|
|
if (m_InstancePool.Count > 0)
|
|
{
|
|
e = m_InstancePool.Dequeue();
|
|
|
|
e.m_List = sector.Multis;
|
|
e.m_Location = loc;
|
|
}
|
|
}
|
|
|
|
if (e == null)
|
|
{
|
|
e = new MultiTileEnumerator(sector, loc);
|
|
}
|
|
|
|
e.Reset();
|
|
|
|
return e;
|
|
}
|
|
|
|
private MultiTileEnumerator(Sector sector, Point2D loc)
|
|
{
|
|
m_List = sector.Multis;
|
|
m_Location = loc;
|
|
}
|
|
|
|
public StaticTile[] Current { get { return m_Current; } }
|
|
|
|
object IEnumerator.Current { get { return m_Current; } }
|
|
|
|
void IDisposable.Dispose()
|
|
{ }
|
|
|
|
public bool MoveNext()
|
|
{
|
|
while (++m_Index < m_List.Count)
|
|
{
|
|
BaseMulti m = m_List[m_Index];
|
|
|
|
if (m != null && !m.Deleted)
|
|
{
|
|
MultiComponentList list = m.Components;
|
|
|
|
int xOffset = m_Location.m_X - (m.Location.m_X + list.Min.m_X);
|
|
int yOffset = m_Location.m_Y - (m.Location.m_Y + list.Min.m_Y);
|
|
|
|
if (xOffset >= 0 && xOffset < list.Width && yOffset >= 0 && yOffset < list.Height)
|
|
{
|
|
StaticTile[] tiles = list.Tiles[xOffset][yOffset];
|
|
|
|
if (tiles.Length > 0)
|
|
{
|
|
StaticTile[] copy = new StaticTile[tiles.Length];
|
|
|
|
for (int i = 0; i < copy.Length; ++i)
|
|
{
|
|
copy[i] = tiles[i];
|
|
copy[i].Z += m.Z;
|
|
}
|
|
|
|
m_Current = copy;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public void Free()
|
|
{
|
|
if (m_List == null)
|
|
{
|
|
return;
|
|
}
|
|
|
|
lock (m_InstancePool)
|
|
{
|
|
if (m_InstancePool.Count < 200) // Arbitrary
|
|
{
|
|
m_InstancePool.Enqueue(this);
|
|
}
|
|
|
|
m_List = null;
|
|
}
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
m_Current = null;
|
|
m_Index = -1;
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#endif
|
|
|
|
public Point3D GetPoint(object o, bool eye)
|
|
{
|
|
Point3D p;
|
|
|
|
if (o is Mobile)
|
|
{
|
|
p = ((Mobile)o).Location;
|
|
p.Z += 14; //eye ? 15 : 10;
|
|
}
|
|
else if (o is Item)
|
|
{
|
|
p = ((Item)o).GetWorldLocation();
|
|
p.Z += (((Item)o).ItemData.Height / 2) + 1;
|
|
}
|
|
else if (o is Point3D)
|
|
{
|
|
p = (Point3D)o;
|
|
}
|
|
else if (o is LandTarget)
|
|
{
|
|
p = ((LandTarget)o).Location;
|
|
|
|
int low = 0, avg = 0, top = 0;
|
|
|
|
GetAverageZ(p.X, p.Y, ref low, ref avg, ref top);
|
|
|
|
p.Z = top + 1;
|
|
}
|
|
else if (o is StaticTarget)
|
|
{
|
|
var st = (StaticTarget)o;
|
|
var id = TileData.ItemTable[st.ItemID & TileData.MaxItemValue];
|
|
|
|
p = new Point3D(st.X, st.Y, st.Z - id.CalcHeight + (id.Height / 2) + 1);
|
|
}
|
|
else if (o is IPoint3D)
|
|
{
|
|
p = new Point3D((IPoint3D)o);
|
|
}
|
|
else
|
|
{
|
|
Console.WriteLine("Warning: Invalid object ({0}) in line of sight", o);
|
|
|
|
p = Point3D.Zero;
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
#region Line Of Sight
|
|
private static int m_MaxLOSDistance = Core.GlobalMaxUpdateRange + 1;
|
|
|
|
public static int MaxLOSDistance { get { return m_MaxLOSDistance; } set { m_MaxLOSDistance = value; } }
|
|
|
|
public bool LineOfSight(Point3D org, Point3D dest)
|
|
{
|
|
if (this == Internal)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!Utility.InRange(org, dest, m_MaxLOSDistance))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
var end = dest;
|
|
|
|
if (org.X > dest.X || (org.X == dest.X && org.Y > dest.Y) || (org.X == dest.X && org.Y == dest.Y && org.Z > dest.Z))
|
|
{
|
|
var swap = org;
|
|
|
|
org = dest;
|
|
dest = swap;
|
|
}
|
|
|
|
if (org == dest)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
var xd = dest.m_X - org.m_X;
|
|
var yd = dest.m_Y - org.m_Y;
|
|
var zd = dest.m_Z - org.m_Z;
|
|
|
|
var zslp = Math.Sqrt(xd * xd + yd * yd);
|
|
|
|
double sq3d;
|
|
|
|
if (zd != 0)
|
|
{
|
|
sq3d = Math.Sqrt(zslp * zslp + zd * zd);
|
|
}
|
|
else
|
|
{
|
|
sq3d = zslp;
|
|
}
|
|
|
|
var rise = yd / sq3d;
|
|
var run = xd / sq3d;
|
|
|
|
zslp = zd / sq3d;
|
|
|
|
var x = (double)org.m_X;
|
|
var y = (double)org.m_Y;
|
|
var z = (double)org.m_Z;
|
|
|
|
Point3DList path = new Point3DList();
|
|
|
|
int ix, iy, iz;
|
|
Point3D p;
|
|
|
|
while (Utility.NumberBetween(x, dest.m_X, org.m_X, 0.5) && Utility.NumberBetween(y, dest.m_Y, org.m_Y, 0.5) &&
|
|
Utility.NumberBetween(z, dest.m_Z, org.m_Z, 0.5))
|
|
{
|
|
ix = (int)Math.Round(x);
|
|
iy = (int)Math.Round(y);
|
|
iz = (int)Math.Round(z);
|
|
|
|
if (path.Count > 0)
|
|
{
|
|
p = path.Last;
|
|
|
|
if (p.m_X != ix || p.m_Y != iy || p.m_Z != iz)
|
|
{
|
|
path.Add(ix, iy, iz);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
path.Add(ix, iy, iz);
|
|
}
|
|
|
|
x += run;
|
|
y += rise;
|
|
z += zslp;
|
|
}
|
|
|
|
if (path.Count == 0)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
p = path.Last;
|
|
|
|
if (p != dest)
|
|
{
|
|
path.Add(dest);
|
|
}
|
|
|
|
Point3D pTop = org, pBottom = dest;
|
|
|
|
Utility.FixPoints(ref pTop, ref pBottom);
|
|
|
|
int pathCount = path.Count;
|
|
int endTop = end.m_Z + 1;
|
|
|
|
int height, landZ, landAvg, landTop, pointTop, ltID;
|
|
bool contains;
|
|
Point3D point;
|
|
LandTile landTile;
|
|
ItemData id;
|
|
TileFlag flags;
|
|
StaticTile[] statics;
|
|
IPooledEnumerable<Item> eable;
|
|
|
|
for (var i = 0; i < pathCount; ++i)
|
|
{
|
|
point = path[i];
|
|
pointTop = point.m_Z + 1;
|
|
|
|
landTile = Tiles.GetLandTile(point.X, point.Y);
|
|
|
|
landZ = landAvg = landTop = 0;
|
|
|
|
GetAverageZ(point.m_X, point.m_Y, ref landZ, ref landAvg, ref landTop);
|
|
|
|
if (landZ <= pointTop && landTop >= point.m_Z &&
|
|
(point.m_X != end.m_X || point.m_Y != end.m_Y || landZ > endTop || landTop < end.m_Z) && !landTile.Ignored)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
statics = Tiles.GetStaticTiles(point.m_X, point.m_Y, true);
|
|
|
|
contains = false;
|
|
ltID = landTile.ID;
|
|
|
|
for (int j = 0; !contains && j < m_InvalidLandTiles.Length; ++j)
|
|
{
|
|
contains = ltID == m_InvalidLandTiles[j];
|
|
}
|
|
|
|
if (contains && statics.Length == 0)
|
|
{
|
|
eable = GetItemsInRange(point, 0);
|
|
|
|
foreach (var item in eable)
|
|
{
|
|
if (item.Visible)
|
|
{
|
|
contains = false;
|
|
}
|
|
|
|
if (!contains)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
eable.Free();
|
|
|
|
if (contains)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
foreach (var t in statics)
|
|
{
|
|
id = TileData.ItemTable[t.ID & TileData.MaxItemValue];
|
|
|
|
flags = id.Flags;
|
|
height = id.CalcHeight;
|
|
|
|
if (t.Z <= pointTop && t.Z + height >= point.Z && (flags & (TileFlag.Window | TileFlag.NoShoot)) != 0)
|
|
{
|
|
if (point.m_X != end.m_X || point.m_Y != end.m_Y || t.Z > endTop || t.Z + height < end.m_Z)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
var rect = new Rectangle2D(pTop.m_X, pTop.m_Y, (pBottom.m_X - pTop.m_X) + 1, (pBottom.m_Y - pTop.m_Y) + 1);
|
|
|
|
var area = GetItemsInBounds(rect);
|
|
|
|
try
|
|
{
|
|
int count;
|
|
bool found;
|
|
Point3D loc;
|
|
|
|
foreach (var i in area)
|
|
{
|
|
if (!i.Visible)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (i is BaseMulti || i.ItemID > TileData.MaxItemValue)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
id = i.ItemData;
|
|
|
|
flags = id.Flags;
|
|
|
|
if ((flags & (TileFlag.Window | TileFlag.NoShoot)) == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
height = id.CalcHeight;
|
|
|
|
found = false;
|
|
|
|
count = path.Count;
|
|
|
|
for (var j = 0; j < count; ++j)
|
|
{
|
|
point = path[j];
|
|
pointTop = point.m_Z + 1;
|
|
loc = i.Location;
|
|
|
|
if (loc.m_X == point.m_X && loc.m_Y == point.m_Y && loc.m_Z <= pointTop && loc.m_Z + height >= point.m_Z)
|
|
{
|
|
if (loc.m_X == end.m_X && loc.m_Y == end.m_Y && loc.m_Z <= endTop && loc.m_Z + height >= end.m_Z)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
found = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (found)
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
finally
|
|
{
|
|
area.Free();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public bool LineOfSight(object from, object dest)
|
|
{
|
|
if (from == dest || (from is Mobile && ((Mobile)from).AccessLevel > AccessLevel.Player))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if (dest is Item && from is Mobile && ((Item)dest).RootParent == from)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return LineOfSight(GetPoint(from, true), GetPoint(dest, false));
|
|
}
|
|
|
|
public bool LineOfSight(Mobile from, Point3D target)
|
|
{
|
|
if (from.AccessLevel > AccessLevel.Player)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
Point3D eye = from.Location;
|
|
|
|
eye.Z += 14;
|
|
|
|
return LineOfSight(eye, target);
|
|
}
|
|
|
|
public bool LineOfSight(Mobile from, Mobile to)
|
|
{
|
|
if (from == to || from.AccessLevel > AccessLevel.Player)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
Point3D eye = from.Location;
|
|
Point3D target = to.Location;
|
|
|
|
eye.Z += 14;
|
|
target.Z += 14;
|
|
|
|
return LineOfSight(eye, target);
|
|
}
|
|
#endregion
|
|
|
|
private static int[] m_InvalidLandTiles = {0x244};
|
|
|
|
public static int[] InvalidLandTiles { get { return m_InvalidLandTiles; } set { m_InvalidLandTiles = value; } }
|
|
|
|
public int CompareTo(Map other)
|
|
{
|
|
if (other == null)
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
return m_MapID.CompareTo(other.m_MapID);
|
|
}
|
|
|
|
public int CompareTo(object other)
|
|
{
|
|
return CompareTo(other as Map);
|
|
}
|
|
}
|
|
}
|