diff --git a/Projects/Server/Items/BaseMulti.cs b/Projects/Server/Items/BaseMulti.cs index 4de43e03d..38f162889 100644 --- a/Projects/Server/Items/BaseMulti.cs +++ b/Projects/Server/Items/BaseMulti.cs @@ -116,6 +116,47 @@ public abstract partial class BaseMulti : Item [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool Contains(Item item) => item.Map == Map && Contains(item.X, item.Y); + public bool Intersects(Rectangle2D bounds) + { + if (bounds.X > Location.X + Components.Max.X || bounds.X + bounds.Width < Location.X + Components.Min.X) + { + return false; + } + + if (bounds.Y > Location.Y + Components.Max.Y || bounds.Y + bounds.Height < Location.Y + Components.Min.Y) + { + return false; + } + + int minX = Math.Max(bounds.X, Location.X + Components.Min.X); + int maxX = Math.Min(bounds.X + bounds.Width, Location.X + Components.Max.X); + int minY = Math.Max(bounds.Y, Location.Y + Components.Min.Y); + int maxY = Math.Min(bounds.Y + bounds.Height, Location.Y + Components.Max.Y); + + for (int x = minX; x <= maxX; x++) + { + for (int y = minY; y <= maxY; y++) + { + int offsetX = x - Location.X - Components.Min.X; + int offsetY = y - Location.Y - Components.Min.Y; + + if (offsetX < 0 || offsetY < 0 || offsetX >= Components.Width || offsetY >= Components.Height) + { + continue; + } + + // TODO: Use a ref struct + var tiles = Components.Tiles[offsetX][offsetY]; + if (tiles.Length > 0) + { + return true; + } + } + } + + return false; + } + public override void Serialize(IGenericWriter writer) { base.Serialize(writer); diff --git a/Projects/Server/Maps/Map.MultiEnumerator.cs b/Projects/Server/Maps/Map.MultiEnumerator.cs index 9c3499ca7..f28f4b5a0 100644 --- a/Projects/Server/Maps/Map.MultiEnumerator.cs +++ b/Projects/Server/Maps/Map.MultiEnumerator.cs @@ -14,6 +14,7 @@ *************************************************************************/ using System; +using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Server.Items; @@ -26,22 +27,23 @@ public partial class Map public static ref readonly SectorMultiValueLinkList EmptyMultiLinkList => ref _emptyMultiLinkList; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MultiAtEnumerable GetMultisAt(Point3D p) => GetMultisAt(p); + public MultiSectorEnumerable GetMultisInSector(Point3D p) => GetMultisInSector(p); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MultiAtEnumerable GetMultisAt(Point3D p) where T : BaseMulti => GetMultisAt(new Point2D(p.X, p.Y)); + public MultiSectorEnumerable GetMultisInSector(Point2D p) => GetMultisInSector(p); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MultiAtEnumerable GetMultisAt(int x, int y) => GetMultisAt(new Point2D(x, y)); + public MultiSectorEnumerable GetMultisInSector(int x, int y) => GetMultisInSector(x, y); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MultiAtEnumerable GetMultisAt(int x, int y) where T : BaseMulti => GetMultisAt(new Point2D(x, y)); + public MultiSectorEnumerable GetMultisInSector(Point3D p) where T : BaseMulti => + new(this, new Point2D(p.X, p.Y)); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MultiAtEnumerable GetMultisAt(Point2D p) => GetMultisAt(p); + public MultiSectorEnumerable GetMultisInSector(Point2D p) where T : BaseMulti => new(this, p); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MultiAtEnumerable GetMultisAt(Point2D p) where T : BaseMulti => new(this, p); + public MultiSectorEnumerable GetMultisInSector(int x, int y) where T : BaseMulti => new(this, new Point2D(x, y)); [MethodImpl(MethodImplOptions.AggressiveInlining)] public MultiBoundsEnumerable GetMultisInRange(Point3D p) => GetMultisInRange(p); @@ -74,34 +76,26 @@ public partial class Map GetMultisInBounds(new Rectangle2D(x - range, y - range, range * 2 + 1, range * 2 + 1)); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MultiBoundsEnumerable GetMultisInBounds(Rectangle2D bounds) => GetMultisInBounds(bounds); + public MultiBoundsEnumerable GetMultisInBounds(Rectangle2D bounds, bool makeBoundsInclusive = false) => + GetMultisInBounds(bounds, makeBoundsInclusive); [MethodImpl(MethodImplOptions.AggressiveInlining)] public MultiBoundsEnumerable GetMultisInBounds(Rectangle2D bounds, bool makeBoundsInclusive = false) where T : BaseMulti => new(this, bounds, makeBoundsInclusive); - public ref struct MultiAtEnumerable where T : BaseMulti + public ref struct MultiSectorEnumerable(Map map, Point2D loc) where T : BaseMulti { - public static MultiAtEnumerable Empty + public static MultiSectorEnumerable Empty { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => new(); } - private readonly Map _map; - private readonly Point2D _location; - - public MultiAtEnumerable(Map map, Point2D loc) - { - _map = map; - _location = loc; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MultiAtEnumerator GetEnumerator() => new(_map, _location); + public MultiSectorEnumerator GetEnumerator() => new(map, loc); } - public ref struct MultiAtEnumerator where T : BaseMulti + public ref struct MultiSectorEnumerator where T : BaseMulti { private Point2D _location; private readonly Span _list; @@ -109,12 +103,13 @@ public partial class Map private T _current; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MultiAtEnumerator(Map map, Point2D loc) + public MultiSectorEnumerator(Map map, Point2D loc) { _location = loc; + _list = map == null ? Span.Empty - : CollectionsMarshal.AsSpan(map.GetRealSector(loc.m_X, loc.m_Y).Multis); + : CollectionsMarshal.AsSpan(map.GetSector(loc.m_X, loc.m_Y).Multis); _index = 0; _current = null; @@ -128,7 +123,7 @@ public partial class Map while ((uint)_index < (uint)_list.Length) { var current = _list[_index++]; - if (current is T { Deleted: false } o && o.X == loc.m_X && o.Y == loc.m_Y) + if (current is T { Deleted: false } o) { _current = o; return true; @@ -165,10 +160,10 @@ public partial class Map } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MultiEnumerator GetEnumerator() => new(_map, _bounds, _makeBoundsInclusive); + public MultiBoundsEnumerator GetEnumerator() => new(_map, _bounds, _makeBoundsInclusive); } - public ref struct MultiEnumerator where T : BaseMulti + public ref struct MultiBoundsEnumerator where T : BaseMulti { private readonly Map _map; private readonly int _sectorStartX; @@ -183,8 +178,10 @@ public partial class Map private T _current; private int _index; + private HashSet _dupes; + [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MultiEnumerator(Map map, Rectangle2D bounds, bool makeBoundsInclusive) + public MultiBoundsEnumerator(Map map, Rectangle2D bounds, bool makeBoundsInclusive) { _map = map; _bounds = bounds; @@ -213,8 +210,11 @@ public partial class Map while ((uint)_index < (uint)_list.Length) { var current = _list[_index++]; - if (current is T { Deleted: false } o && bounds.Contains(o.Location)) + _dupes ??= new HashSet(); + + if (current is T { Deleted: false } o && bounds.Contains(o.Location) && !_dupes.Contains(o.Serial)) { + _dupes.Add(o.Serial); _current = o; return true; } diff --git a/Projects/Server/Maps/Map.MultiTileEnumerator.cs b/Projects/Server/Maps/Map.MultiTileEnumerator.cs index 8d2cda3a2..90fb0555b 100644 --- a/Projects/Server/Maps/Map.MultiTileEnumerator.cs +++ b/Projects/Server/Maps/Map.MultiTileEnumerator.cs @@ -50,14 +50,14 @@ public partial class Map public ref struct MultiTilesAtEnumerator { private Point2D _location; - private MultiAtEnumerator _multis; + private MultiSectorEnumerator _multis; private BaseMulti _currentMulti; private StaticTile[] _current; [MethodImpl(MethodImplOptions.AggressiveInlining)] public MultiTilesAtEnumerator(Map map, Point2D loc) { - _multis = (map == null ? MultiAtEnumerable.Empty : map.GetMultisAt(loc)).GetEnumerator(); + _multis = (map == null ? MultiSectorEnumerable.Empty : map.GetMultisInSector(loc)).GetEnumerator(); _current = null; _location = loc; diff --git a/Projects/Server/Mobiles/Mobile.cs b/Projects/Server/Mobiles/Mobile.cs index 478b3affd..d7336a169 100644 --- a/Projects/Server/Mobiles/Mobile.cs +++ b/Projects/Server/Mobiles/Mobile.cs @@ -6943,6 +6943,16 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro item.SendInfoTo(ns); } } + + var range = new Rectangle2D(m_Location.X - Core.GlobalMaxUpdateRange, + m_Location.Y - Core.GlobalMaxUpdateRange, + Core.GlobalMaxUpdateRange * 2 + 1, + Core.GlobalMaxUpdateRange * 2 + 1); + + foreach (var multi in m_Map.GetMultisInBounds(range)) + { + multi.SendInfoTo(ns); + } } public void UpdateRegion() diff --git a/Projects/UOContent/Multis/Boats/BaseBoat.cs b/Projects/UOContent/Multis/Boats/BaseBoat.cs index d787e166c..b4908b279 100644 --- a/Projects/UOContent/Multis/Boats/BaseBoat.cs +++ b/Projects/UOContent/Multis/Boats/BaseBoat.cs @@ -243,7 +243,7 @@ namespace Server.Multis public static BaseBoat FindBoatAt(Point3D loc, Map map) { - foreach (var boat in map.GetMultisAt(loc)) + foreach (var boat in map.GetMultisInSector(loc)) { if (boat.Contains(loc.X, loc.Y)) { diff --git a/Projects/UOContent/Multis/Houses/BaseHouse.cs b/Projects/UOContent/Multis/Houses/BaseHouse.cs index c97d580e9..0ab52ba32 100644 --- a/Projects/UOContent/Multis/Houses/BaseHouse.cs +++ b/Projects/UOContent/Multis/Houses/BaseHouse.cs @@ -1401,7 +1401,7 @@ namespace Server.Multis return null; } - foreach (var house in map.GetMultisAt(loc)) + foreach (var house in map.GetMultisInSector(loc)) { if (house.IsInside(loc, height)) { @@ -1482,8 +1482,8 @@ namespace Server.Multis var mcl = Components; - var x = p.X - (X + mcl.Min.X); - var y = p.Y - (Y + mcl.Min.Y); + var x = p.X - X - mcl.Min.X; + var y = p.Y - Y - mcl.Min.Y; if (x < 0 || x >= mcl.Width || y < 0 || y >= mcl.Height) { @@ -1495,6 +1495,7 @@ namespace Server.Multis return true; } + // TODO: Use ref struct var tiles = mcl.Tiles[x][y]; for (var j = 0; j < tiles.Length; ++j) diff --git a/Projects/UOContent/Multis/Houses/HousePlacement.cs b/Projects/UOContent/Multis/Houses/HousePlacement.cs index 61d568580..d1aa89bba 100644 --- a/Projects/UOContent/Multis/Houses/HousePlacement.cs +++ b/Projects/UOContent/Multis/Houses/HousePlacement.cs @@ -374,7 +374,7 @@ namespace Server.Multis { var yardPoint = yard[i]; - foreach (var house in map.GetMultisAt(yardPoint)) + foreach (var house in map.GetMultisInSector(yardPoint)) { if (house.Contains(yard[i])) { diff --git a/Projects/UOContent/Spells/Base/SpellHelper.cs b/Projects/UOContent/Spells/Base/SpellHelper.cs index b0b50912f..5f80855fa 100644 --- a/Projects/UOContent/Spells/Base/SpellHelper.cs +++ b/Projects/UOContent/Spells/Base/SpellHelper.cs @@ -158,7 +158,7 @@ namespace Server.Spells return false; } - foreach (var multi in map.GetMultisAt(p)) + foreach (var multi in map.GetMultisInSector(p)) { if (multi is BaseHouse bh) {