// // 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); } } }