//
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
//
namespace MUnique.OpenMU.GameLogic;
using MUnique.OpenMU.Pathfinding;
///
/// A two-dimensional map of buckets.
///
/// The type of objects which should be hold.
internal class BucketMap
{
private const int DefaultListCapacity = 4;
private readonly Bucket[] _list;
///
/// Initializes a new instance of the class.
///
public BucketMap()
{
this.BucketSideLength = 4;
this._list = new Bucket[0x100 / this.BucketSideLength * 0x100 / this.BucketSideLength];
this.InitList(DefaultListCapacity);
}
///
/// Initializes a new instance of the class.
///
/// Length of the side.
/// if set to true the all lists are created now, and not by demand.
/// The list capacity.
/// Length of the bucket side.
/// SideLength must be a multiple of BucketSideLength.
public BucketMap(int sideLength, bool createLists, int listCapacity, int bucketSideLength)
{
if (sideLength % bucketSideLength != 0)
{
throw new ArgumentException($"SideLength ({sideLength}) must be a multiple of BucketSideLength ({bucketSideLength}).");
}
this.BucketSideLength = bucketSideLength;
this.SideLength = sideLength / bucketSideLength;
this._list = new Bucket[this.SideLength * this.SideLength];
if (createLists)
{
this.InitList(listCapacity);
}
}
///
/// Gets or sets the length of a side of the map. This value should be a multiple of .
///
public int SideLength { get; set; }
///
/// Gets or sets the length of the bucket side.
///
///
/// The length of the bucket side.
///
public int BucketSideLength { get; set; }
///
/// Gets or sets the at the specified coordinates.
///
///
/// The .
///
/// The coordinates.
/// The at the specified coordinates.
public Bucket this[Point point]
{
get => this._list[this.GetListIndex(point)];
set => this._list[this.GetListIndex(point)] = value;
}
///
/// Gets the items which are in range of the specified coordinates and range.
///
/// The coordinates.
/// The maximum range.
/// The items which are in range of the specified coordinate and range.
public IEnumerable GetInRange(Point point, int range)
{
var result = new List();
var buckets = this.GetBucketsInRange(point, range);
foreach (var bucket in buckets)
{
result.AddRange(bucket.OfType().Where(obj => obj.IsInRange(point, range)));
}
return result;
}
///
/// Gets the buckets in the specified range of the specified coordinates.
///
/// The coordinates.
/// The range.
/// The buckets in the specified range of the specified coordinate.
public IEnumerable> GetBucketsInRange(Point point, int range)
{
int maxX = Math.Min(point.X + range, (this.SideLength - 1) * this.BucketSideLength) / this.BucketSideLength;
int maxY = Math.Min(point.Y + range, (this.SideLength - 1) * this.BucketSideLength) / this.BucketSideLength;
int minX = Math.Max(point.X - range, 0) / this.BucketSideLength;
int minY = Math.Max(point.Y - range, 0) / this.BucketSideLength;
for (int i = minX; maxX >= i; ++i)
{
for (int j = minY; maxY >= j; ++j)
{
yield return this._list[i + (j * this.SideLength)];
}
}
}
private int GetListIndex(Point point) => (point.X / this.BucketSideLength) + ((point.Y / this.BucketSideLength) * this.SideLength);
private void InitList(int listCapacity)
{
for (int i = 0; i < this._list.Length; ++i)
{
this._list[i] = new Bucket(listCapacity);
}
}
}