diff --git a/Projects/Server/Diagnostics/PacketProfile.cs b/Projects/Server/Diagnostics/PacketProfile.cs index 34e4ce86e..cacf1846e 100644 --- a/Projects/Server/Diagnostics/PacketProfile.cs +++ b/Projects/Server/Diagnostics/PacketProfile.cs @@ -43,7 +43,6 @@ public class PacketSendProfile : BasePacketProfile public static IEnumerable Profiles => _profiles.Values; - [MethodImpl(MethodImplOptions.Synchronized)] public static PacketSendProfile Acquire(int packetId) { if (!_profiles.TryGetValue(packetId, out var prof)) @@ -78,7 +77,6 @@ public class PacketReceiveProfile : BasePacketProfile public static IEnumerable Profiles => _profiles.Values; - [MethodImpl(MethodImplOptions.Synchronized)] public static PacketReceiveProfile Acquire(int packetId) { if (!_profiles.TryGetValue(packetId, out var prof)) diff --git a/Projects/Server/Items/Item.cs b/Projects/Server/Items/Item.cs index 055995644..03492e9dc 100644 --- a/Projects/Server/Items/Item.cs +++ b/Projects/Server/Items/Item.cs @@ -3518,11 +3518,8 @@ public class Item : IHued, IComparable, ISpawnable, IObjectPropertyListEnt } } - var tiles = map.Tiles.GetStaticTiles(x, y, true); - - for (var i = 0; i < tiles.Length; ++i) + foreach (var tile in map.Tiles.GetStaticAndMultiTiles(x, y)) { - var tile = tiles[i]; var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; if (!id.Surface) @@ -3576,9 +3573,8 @@ public class Item : IHued, IComparable, ISpawnable, IObjectPropertyListEnt var surfaceZ = z; - for (var i = 0; i < tiles.Length; ++i) + foreach (var tile in map.Tiles.GetStaticAndMultiTiles(x, y)) { - var tile = tiles[i]; var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; var checkZ = tile.Z; @@ -3681,9 +3677,8 @@ public class Item : IHued, IComparable, ISpawnable, IObjectPropertyListEnt return false; } - for (var i = 0; i < tiles.Length; ++i) + foreach (var tile in map.Tiles.GetStaticAndMultiTiles(x, y)) { - var tile = tiles[i]; var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; var checkZ = tile.Z; diff --git a/Projects/Server/Maps/Map.MultiEnumerator.cs b/Projects/Server/Maps/Map.MultiEnumerator.cs index f28f4b5a0..d7e9f9eaf 100644 --- a/Projects/Server/Maps/Map.MultiEnumerator.cs +++ b/Projects/Server/Maps/Map.MultiEnumerator.cs @@ -97,7 +97,6 @@ public partial class Map public ref struct MultiSectorEnumerator where T : BaseMulti { - private Point2D _location; private readonly Span _list; private int _index; private T _current; @@ -105,8 +104,6 @@ public partial class Map [MethodImpl(MethodImplOptions.AggressiveInlining)] public MultiSectorEnumerator(Map map, Point2D loc) { - _location = loc; - _list = map == null ? Span.Empty : CollectionsMarshal.AsSpan(map.GetSector(loc.m_X, loc.m_Y).Multis); @@ -118,8 +115,6 @@ public partial class Map [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { - ref var loc = ref _location; - while ((uint)_index < (uint)_list.Length) { var current = _list[_index++]; diff --git a/Projects/Server/Maps/Map.MultiTileEnumerator.cs b/Projects/Server/Maps/Map.MultiTileEnumerator.cs deleted file mode 100644 index 90fb0555b..000000000 --- a/Projects/Server/Maps/Map.MultiTileEnumerator.cs +++ /dev/null @@ -1,121 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2023 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: Map.MultiTileEnumerator.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Runtime.CompilerServices; -using Server.Items; - -namespace Server; - -public partial class Map -{ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MultiTilesAtEnumerable GetMultiTilesAt(int x, int y) => GetMultiTilesAt(new Point2D(x, y)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MultiTilesAtEnumerable GetMultiTilesAt(Point2D p) => new(this, p); - - public ref struct MultiTilesAtEnumerable - { - public static MultiTilesAtEnumerable Empty - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => new(); - } - - private readonly Map _map; - private readonly Point2D _location; - - public MultiTilesAtEnumerable(Map map, Point2D loc) - { - _map = map; - _location = loc; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MultiTilesAtEnumerator GetEnumerator() => new(_map, _location); - } - - public ref struct MultiTilesAtEnumerator - { - private Point2D _location; - private MultiSectorEnumerator _multis; - private BaseMulti _currentMulti; - private StaticTile[] _current; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MultiTilesAtEnumerator(Map map, Point2D loc) - { - _multis = (map == null ? MultiSectorEnumerable.Empty : map.GetMultisInSector(loc)).GetEnumerator(); - - _current = null; - _location = loc; - _currentMulti = null; - } - - private bool SetStaticTiles() - { - var mcl = _currentMulti.Components; - var x = _location.X; - var xo = x - (_currentMulti.X + mcl.Min.X); - - var y = _location.Y; - if (xo < 0 || xo >= mcl.Width) - { - return false; - } - - var yo = y - (_currentMulti.Y + mcl.Min.Y); - if (yo < 0 || yo >= mcl.Height) - { - return false; - } - - var t = mcl.Tiles[xo][yo]; - - // TODO: Remove the allocation. - var r = new StaticTile[t.Length]; - - for (var i = 0; i < t.Length; i++) - { - r[i] = t[i]; - r[i].Z += _currentMulti.Z; - } - - _current = r; - return true; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { - while (_multis.MoveNext()) - { - _currentMulti = _multis.Current; - if (SetStaticTiles()) - { - return true; - } - } - - return false; - } - - public StaticTile[] Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _current; - } - } -} diff --git a/Projects/Server/Maps/Map.StaticTileEnumerator.cs b/Projects/Server/Maps/Map.StaticTileEnumerator.cs new file mode 100644 index 000000000..de3252f68 --- /dev/null +++ b/Projects/Server/Maps/Map.StaticTileEnumerator.cs @@ -0,0 +1,138 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2023 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: Map.StaticTileEnumerator.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System.Runtime.CompilerServices; +using Server.Items; + +namespace Server; + +public partial class Map +{ + public ref struct StaticTileEnumerable + { + public static StaticTileEnumerable Empty + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new(); + } + + private readonly Map _map; + private readonly Point2D _location; + private readonly bool _includeStatics; + private readonly bool _includeMultis; + + public StaticTileEnumerable(Map map, Point2D loc, bool includeStatics = true, bool includeMultis = true) + { + _map = map; + _location = loc; + _includeStatics = includeStatics; + _includeMultis = includeMultis; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public StaticTileEnumerator GetEnumerator() => new(_map, _location, _includeStatics, _includeMultis); + } + + public ref struct StaticTileEnumerator + { + private readonly Map _map; + private readonly Point2D _point; + + private StaticTile[] _tiles; + private MultiSectorEnumerator _multis; + private BaseMulti _currentMulti; + + private int _index; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public StaticTileEnumerator(Map map, Point2D p, bool includeStatics, bool includeMultis) + { + _map = map; + _point = p; + + if (_map == null) + { + return; + } + + if (includeStatics) + { + var tiles = map.Tiles.GetStaticBlock(p.X >> SectorShift, p.Y >> SectorShift); + _tiles = tiles[p.X & 0x7][p.Y & 0x7]; + _index = -1; + } + + _multis = includeMultis + ? _map.GetMultisInSector(p).GetEnumerator() + : MultiSectorEnumerable.Empty.GetEnumerator(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool SetMulti() + { + ref var multis = ref _multis; + ref readonly var p = ref _point; + + if (multis.MoveNext()) + { + var multi = multis.Current; + _currentMulti = multi; + var components = multi!.Components; + var location = multi!.Location; + + int offsetX = p.X - location.X - components.Min.X; + int offsetY = p.Y - location.Y - components.Min.Y; + + if (offsetX >= 0 && offsetY >= 0 && offsetX < components.Width && offsetY < components.Height) + { + _tiles = multi.Components.Tiles[offsetX][offsetY]; + _index = -1; + return SetTile(); + } + } + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool SetTile() => _tiles != null && ++_index < _tiles.Length; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() => _map != null && (SetTile() || SetMulti()); + + public StaticTile Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + if (_currentMulti == null) + { + return _tiles[_index]; + } + + var location = _currentMulti.Location; + ref readonly var tile = ref _tiles[_index]; + return new StaticTile + { + m_ID = tile.m_ID, + X = tile.m_X, + Y = tile.m_Y, + Z = tile.m_Z + location.Z, + m_Hue = tile.m_Hue + }; + } + } + } +} diff --git a/Projects/Server/Maps/Map.cs b/Projects/Server/Maps/Map.cs index f2a752c32..21721f856 100644 --- a/Projects/Server/Maps/Map.cs +++ b/Projects/Server/Maps/Map.cs @@ -51,7 +51,6 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa private readonly int m_SectorsWidth; - private readonly object tileLock = new(); private Region m_DefaultRegion; private string m_Name; @@ -89,21 +88,7 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa public int Season { get; set; } - public TileMatrix Tiles - { - get - { - if (m_Tiles == null) - { - lock (tileLock) - { - m_Tiles = new TileMatrix(this, m_FileIndex, MapID, Width, Height); - } - } - - return m_Tiles; - } - } + public TileMatrix Tiles => m_Tiles ??= new TileMatrix(this, m_FileIndex, MapID, Width, Height); public int MapID { get; } @@ -325,7 +310,6 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa public void FixColumn(int x, int y) { var landTile = Tiles.GetLandTile(x, y); - var tiles = Tiles.GetStaticTiles(x, y, true); GetAverageZ(x, y, out _, out var landAvg, out _); @@ -349,7 +333,7 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa z = landAvg; } - foreach (var tile in tiles) + foreach (var tile in Tiles.GetStaticAndMultiTiles(x, y)) { var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; @@ -400,33 +384,6 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa STArrayPool.Shared.Return(items, true); } - /* This could probably be re-implemented if necessary (perhaps via an ITile interface?). - public List GetTilesAt( Point2D p, bool items, bool land, bool statics ) - { - List list = new List(); - - if (this == Internal) - return list; - - if (land) - list.Add( Tiles.GetLandTile( p.m_X, p.m_Y ) ); - - if (statics) - list.AddRange( Tiles.GetStaticTiles( p.m_X, p.m_Y, true ) ); - - if (items) - { - Sector sector = GetSector( p ); - - foreach ( Item item in sector.Items ) - if (item.AtWorldPoint( p.m_X, p.m_Y )) - list.Add( new StaticTile( (ushort)item.ItemID, (sbyte) item.Z ) ); - } - - return list; - } - */ - /// /// Gets the highest surface that is lower than . /// @@ -460,11 +417,8 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa } } - var staticTiles = Tiles.GetStaticTiles(p.X, p.Y, true); - - for (var i = 0; i < staticTiles.Length; i++) + foreach (var tile in Tiles.GetStaticAndMultiTiles(p.X, p.Y)) { - var tile = staticTiles[i]; var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; if (id.Surface || id.Wet) @@ -862,14 +816,14 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa return p; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool CanFit( - Point3D p, int height, bool checkBlocksFit = false, bool checkMobiles = true, - bool requireSurface = true + Point3D p, int height, bool checkBlocksFit = false, bool checkMobiles = true, bool requireSurface = true ) => CanFit(p.m_X, p.m_Y, p.m_Z, height, checkBlocksFit, checkMobiles, requireSurface); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool CanFit( - Point2D p, int z, int height, bool checkBlocksFit = false, bool checkMobiles = true, - bool requireSurface = true + Point2D p, int z, int height, bool checkBlocksFit = false, bool checkMobiles = true, bool requireSurface = true ) => CanFit(p.m_X, p.m_Y, z, height, checkBlocksFit, checkMobiles, requireSurface); public bool CanFit( @@ -903,29 +857,26 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa hasSurface = true; } - var staticTiles = Tiles.GetStaticTiles(x, y, true); - bool surface, impassable; - for (var i = 0; i < staticTiles.Length; ++i) + foreach (var tile in Tiles.GetStaticAndMultiTiles(x, y)) { - var id = TileData.ItemTable[staticTiles[i].ID & TileData.MaxItemValue]; + var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; surface = id.Surface; impassable = id.Impassable; - if ((surface || impassable) && staticTiles[i].Z + id.CalcHeight > z && z + height > staticTiles[i].Z) + if ((surface || impassable) && tile.Z + id.CalcHeight > z && z + height > tile.Z) { return false; } - if (surface && !impassable && z == staticTiles[i].Z + id.CalcHeight) + if (surface && !impassable && z == tile.Z + id.CalcHeight) { hasSurface = true; } } var sector = GetSector(x, y); - var mobs = sector.Mobiles; foreach (var item in sector.Items) { @@ -1122,8 +1073,6 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa return false; */ - var statics = Tiles.GetStaticTiles(point.m_X, point.m_Y, true); - var contains = false; var ltID = landTile.ID; @@ -1132,26 +1081,11 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa contains = ltID == InvalidLandTiles[j]; } - if (contains && statics.Length == 0) - { - foreach (Item item in GetItemsInRange(point, 0)) - { - if (item.Visible) - { - contains = false; - break; - } - } + bool foundStatic = false; - if (contains) - { - return false; - } - } - - for (var j = 0; j < statics.Length; ++j) + foreach (var t in Tiles.GetStaticAndMultiTiles(point.m_X, point.m_Y)) { - var t = statics[j]; + foundStatic = true; var id = TileData.ItemTable[t.ID & TileData.MaxItemValue]; @@ -1168,6 +1102,23 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa return false; } } + + if (contains && !foundStatic) + { + foreach (Item item in GetItemsAt(point)) + { + if (item.Visible) + { + contains = false; + break; + } + } + + if (contains) + { + 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); diff --git a/Projects/Server/MultiData.cs b/Projects/Server/MultiData.cs index bd272293c..c1d7c8acc 100644 --- a/Projects/Server/MultiData.cs +++ b/Projects/Server/MultiData.cs @@ -1,3 +1,18 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2023 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: MultiData.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + using System; using System.Buffers; using System.Collections.Generic; @@ -508,13 +523,9 @@ public sealed class MultiComponentList oldTiles = Tiles[vx][vy]; var newTiles = new StaticTile[oldTiles.Length + 1]; + Array.Copy(oldTiles, newTiles, oldTiles.Length); - for (var i = 0; i < oldTiles.Length; ++i) - { - newTiles[i] = oldTiles[i]; - } - - newTiles[oldTiles.Length] = new StaticTile((ushort)itemID, (sbyte)z); + newTiles[^1] = new StaticTile((ushort)itemID, (sbyte)z); Tiles[vx][vy] = newTiles; @@ -574,16 +585,8 @@ public sealed class MultiComponentList if (tile.Z == z && tile.Height >= minHeight) { var newTiles = new StaticTile[oldTiles.Length - 1]; - - for (var j = 0; j < i; ++j) - { - newTiles[j] = oldTiles[j]; - } - - for (var j = i + 1; j < oldTiles.Length; ++j) - { - newTiles[j - 1] = oldTiles[j]; - } + Array.Copy(oldTiles, newTiles, i); + Array.Copy(oldTiles, i + 1, newTiles, i, oldTiles.Length - i - 1); Tiles[vx][vy] = newTiles; @@ -601,16 +604,8 @@ public sealed class MultiComponentList TileData.ItemTable[tile.ItemId & TileData.MaxItemValue].Height >= minHeight) { var newList = new MultiTileEntry[oldList.Length - 1]; - - for (var j = 0; j < i; ++j) - { - newList[j] = oldList[j]; - } - - for (var j = i + 1; j < oldList.Length; ++j) - { - newList[j - 1] = oldList[j]; - } + Array.Copy(oldList, newList, i); + Array.Copy(oldList, i + 1, newList, i, oldList.Length - i - 1); List = newList; @@ -636,16 +631,8 @@ public sealed class MultiComponentList if (tile.ID == itemID && tile.Z == z) { var newTiles = new StaticTile[oldTiles.Length - 1]; - - for (var j = 0; j < i; ++j) - { - newTiles[j] = oldTiles[j]; - } - - for (var j = i + 1; j < oldTiles.Length; ++j) - { - newTiles[j - 1] = oldTiles[j]; - } + Array.Copy(oldTiles, newTiles, i); + Array.Copy(oldTiles, i + 1, newTiles, i, oldTiles.Length - i - 1); Tiles[vx][vy] = newTiles; @@ -663,16 +650,8 @@ public sealed class MultiComponentList tile.OffsetZ == (short)z) { var newList = new MultiTileEntry[oldList.Length - 1]; - - for (var j = 0; j < i; ++j) - { - newList[j] = oldList[j]; - } - - for (var j = i + 1; j < oldList.Length; ++j) - { - newList[j - 1] = oldList[j]; - } + Array.Copy(oldList, newList, i); + Array.Copy(oldList, i + 1, newList, i, oldList.Length - i - 1); List = newList; diff --git a/Projects/Server/TileList.cs b/Projects/Server/TileList.cs index 3d26e74bb..440d17750 100644 --- a/Projects/Server/TileList.cs +++ b/Projects/Server/TileList.cs @@ -16,6 +16,7 @@ using System; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using Server.Buffers; namespace Server; @@ -34,10 +35,8 @@ public class TileList } TryResize(tiles.Length); - for (var i = 0; i < tiles.Length; ++i) - { - _tiles[Count++] = tiles[i]; - } + tiles.CopyTo(_tiles.AsSpan(Count)); + Count += tiles.Length; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -46,43 +45,42 @@ public class TileList public void Add(StaticTile tile) { TryResize(1); - _tiles[Count] = tile; - ++Count; + _tiles[Count++] = tile; } public void Add(ushort id, byte x, byte y, sbyte z, short hue = 0) { TryResize(1); - ref var tile = ref _tiles[Count]; + ref var tile = ref _tiles[Count++]; tile.m_ID = id; tile.m_X = x; tile.m_Y = y; tile.m_Z = z; tile.m_Hue = hue; - ++Count; } public void Add(ushort id, sbyte z) { TryResize(1); - _tiles[Count].m_ID = id; - _tiles[Count].m_Z = z; - ++Count; + ref var tile = ref _tiles[Count++]; + tile.m_ID = id; + tile.m_X = 0; + tile.m_Y = 0; + tile.m_Z = z; + tile.m_Hue = 0; } private void TryResize(int length) { - _tiles ??= new StaticTile[length]; + _tiles ??= STArrayPool.Shared.Rent(length); - if (Count + length > _tiles.Length) + var newLength = Count + length; + if (newLength > _tiles.Length) { var old = _tiles; - _tiles = new StaticTile[old.Length * 2]; - - for (var i = 0; i < old.Length; ++i) - { - _tiles[i] = old[i]; - } + _tiles = STArrayPool.Shared.Rent(newLength); + old.CopyTo(_tiles.AsSpan()); + STArrayPool.Shared.Return(old); } } @@ -93,9 +91,11 @@ public class TileList return _emptyTiles; } - Array.Resize(ref _tiles, Count); - var tiles = _tiles; + var tiles = new StaticTile[Count]; + _tiles.AsSpan(0, Count).CopyTo(tiles); + // Cleanup + STArrayPool.Shared.Return(_tiles); _tiles = null; Count = 0; diff --git a/Projects/Server/TileMatrix/TileMatrix.cs b/Projects/Server/TileMatrix/TileMatrix.cs index 1e7ec7a7b..c52053f58 100644 --- a/Projects/Server/TileMatrix/TileMatrix.cs +++ b/Projects/Server/TileMatrix/TileMatrix.cs @@ -15,9 +15,11 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Threading; using Server.Logging; namespace Server; @@ -158,7 +160,6 @@ public class TileMatrix public StaticTile[][][] EmptyStaticBlock => _emptyStaticBlock; - [MethodImpl(MethodImplOptions.Synchronized)] public void SetStaticBlock(int x, int y, StaticTile[][][] value) { if (x < 0 || y < 0 || x >= BlockWidth || y >= BlockHeight) @@ -173,7 +174,6 @@ public class TileMatrix _staticPatches[x][y >> 5] |= 1 << (y & 0x1F); } - [MethodImpl(MethodImplOptions.Synchronized)] public StaticTile[][][] GetStaticBlock(int x, int y) { if (x < 0 || y < 0 || x >= BlockWidth || y >= BlockHeight || DataStream == null || IndexStream == null) @@ -187,31 +187,28 @@ public class TileMatrix if (tiles == null) { - lock (_fileShare) + for (var i = 0; tiles == null && i < _fileShare.Count; ++i) { - for (var i = 0; tiles == null && i < _fileShare.Count; ++i) + var shared = _fileShare[i]; + + lock (shared) { - var shared = _fileShare[i]; - - lock (shared) + if (x < shared.BlockWidth && y < shared.BlockHeight) { - if (x < shared.BlockWidth && y < shared.BlockHeight) + var theirTiles = shared._staticTiles[x]; + + if (theirTiles != null) { - var theirTiles = shared._staticTiles[x]; + tiles = theirTiles[y]; + } - if (theirTiles != null) + if (tiles != null) + { + var theirBits = shared._staticPatches[x]; + + if (theirBits != null && (theirBits[y >> 5] & (1 << (y & 0x1F))) != 0) { - tiles = theirTiles[y]; - } - - if (tiles != null) - { - var theirBits = shared._staticPatches[x]; - - if (theirBits != null && (theirBits[y >> 5] & (1 << (y & 0x1F))) != 0) - { - tiles = null; - } + tiles = null; } } } @@ -226,44 +223,15 @@ public class TileMatrix return tiles; } - public StaticTile[] GetStaticTiles(int x, int y) - { - var tiles = GetStaticBlock(x >> 3, y >> 3); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.StaticTileEnumerable GetStaticTiles(int x, int y) => new(_map, new Point2D(x, y), includeMultis: false); - return tiles[x & 0x7][y & 0x7]; - } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.StaticTileEnumerable GetStaticAndMultiTiles(int x, int y) => new(_map, new Point2D(x, y)); - private readonly TileList m_TilesList = new(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.StaticTileEnumerable GetMultiTiles(int x, int y) => new(_map, new Point2D(x, y), false); - [MethodImpl(MethodImplOptions.Synchronized)] - public StaticTile[] GetStaticTiles(int x, int y, bool multis) - { - var tiles = GetStaticBlock(x >> 3, y >> 3); - - if (!multis) - { - return tiles[x & 0x7][y & 0x7]; - } - - var any = false; - - foreach (var multiTiles in _map.GetMultiTilesAt(x, y)) - { - any = true; - m_TilesList.AddRange(multiTiles); - } - - if (!any) - { - return tiles[x & 0x7][y & 0x7]; - } - - m_TilesList.AddRange(tiles[x & 0x7][y & 0x7]); - - return m_TilesList.ToArray(); - } - - [MethodImpl(MethodImplOptions.Synchronized)] public void SetLandBlock(int x, int y, LandTile[] value) { if (x < 0 || y < 0 || x >= BlockWidth || y >= BlockHeight) @@ -278,7 +246,6 @@ public class TileMatrix _landPatches[x][y >> 5] |= 1 << (y & 0x1F); } - [MethodImpl(MethodImplOptions.Synchronized)] public LandTile[] GetLandBlock(int x, int y) { if (x < 0 || y < 0 || x >= BlockWidth || y >= BlockHeight || MapStream == null) @@ -295,32 +262,26 @@ public class TileMatrix return tiles; } - lock (_fileShare) + for (var i = 0; tiles == null && i < _fileShare.Count; ++i) { - for (var i = 0; tiles == null && i < _fileShare.Count; ++i) + var shared = _fileShare[i]; + + if (x < shared.BlockWidth && y < shared.BlockHeight) { - var shared = _fileShare[i]; + var theirTiles = shared._landTiles[x]; - lock (shared) + if (theirTiles != null) { - if (x < shared.BlockWidth && y < shared.BlockHeight) + tiles = theirTiles[y]; + } + + if (tiles != null) + { + var theirBits = shared._landPatches[x]; + + if (theirBits != null && (theirBits[y >> 5] & (1 << (y & 0x1F))) != 0) { - var theirTiles = shared._landTiles[x]; - - if (theirTiles != null) - { - tiles = theirTiles[y]; - } - - if (tiles != null) - { - var theirBits = shared._landPatches[x]; - - if (theirBits != null && (theirBits[y >> 5] & (1 << (y & 0x1F))) != 0) - { - tiles = null; - } - } + tiles = null; } } } @@ -344,7 +305,6 @@ public class TileMatrix private StaticTile[] m_TileBuffer = new StaticTile[128]; - [MethodImpl(MethodImplOptions.Synchronized)] private unsafe StaticTile[][][] ReadStaticBlock(int x, int y) { try @@ -440,7 +400,6 @@ public class TileMatrix throw new Exception("No assemblies were loaded, therefore we cannot load TileMatrix."); } - [MethodImpl(MethodImplOptions.Synchronized)] private unsafe LandTile[] ReadLandBlock(int x, int y) { try diff --git a/Projects/Server/TileMatrix/TileMatrixPatch.cs b/Projects/Server/TileMatrix/TileMatrixPatch.cs index 952d155a5..415d542f7 100644 --- a/Projects/Server/TileMatrix/TileMatrixPatch.cs +++ b/Projects/Server/TileMatrix/TileMatrixPatch.cs @@ -15,7 +15,6 @@ using System; using System.IO; -using System.Runtime.CompilerServices; namespace Server; @@ -72,7 +71,6 @@ public class TileMatrixPatch } } - [MethodImpl(MethodImplOptions.Synchronized)] private unsafe int PatchLand(TileMatrix matrix, string dataPath, string indexPath) { using var fsData = new FileStream(dataPath, FileMode.Open, FileAccess.Read, FileShare.Read); @@ -100,7 +98,6 @@ public class TileMatrixPatch return count; } - [MethodImpl(MethodImplOptions.Synchronized)] private unsafe int PatchStatics(TileMatrix matrix, string dataPath, string indexPath, string lookupPath) { using var fsData = new FileStream(dataPath, FileMode.Open, FileAccess.Read, FileShare.Read); diff --git a/Projects/UOContent/Engines/ConPVP/Games/BombingRun.cs b/Projects/UOContent/Engines/ConPVP/Games/BombingRun.cs index d73e730c3..6c511a8ba 100644 --- a/Projects/UOContent/Engines/ConPVP/Games/BombingRun.cs +++ b/Projects/UOContent/Engines/ConPVP/Games/BombingRun.cs @@ -462,30 +462,10 @@ namespace Server.Engines.ConPVP return; } - var statics = Map.Tiles.GetStaticTiles(point.X, point.Y, true); - - if (landTile.ID == 0x244 && statics.Length == 0) // 0x244 = invalid land tile + var foundStatics = false; + foreach (var t in Map.Tiles.GetStaticAndMultiTiles(point.X, point.Y)) { - var empty = true; - foreach (var item in Map.GetItemsAt(point)) - { - if (item != this) - { - empty = false; - break; - } - } - - if (empty) - { - HitObject(point, landTop, 0); - return; - } - } - - for (var j = 0; j < statics.Length; j++) - { - var t = statics[j]; + foundStatics = true; var id = TileData.ItemTable[t.ID & TileData.MaxItemValue]; height = id.CalcHeight; @@ -506,6 +486,25 @@ namespace Server.Engines.ConPVP return; } } + + if (landTile.ID == 0x244 && foundStatics) // 0x244 = invalid land tile + { + var empty = true; + foreach (var item in Map.GetItemsAt(point)) + { + if (item != this) + { + empty = false; + break; + } + } + + if (empty) + { + HitObject(point, landTop, 0); + return; + } + } } var rect = new Rectangle2D(pTop.X, pTop.Y, pBottom.X - pTop.X + 1, pBottom.Y - pTop.Y + 1); @@ -640,14 +639,10 @@ namespace Server.Engines.ConPVP var myZ = Map?.GetAverageZ(X, Y) ?? 0; - var statics = Map?.Tiles?.GetStaticTiles(X, Y, true); - - if (statics != null) + if (Map?.Tiles != null) { - for (var j = 0; j < statics.Length; j++) + foreach (var t in Map.Tiles.GetStaticAndMultiTiles(X, Y)) { - var t = statics[j]; - var id = TileData.ItemTable[t.ID & TileData.MaxItemValue]; height = id.CalcHeight; diff --git a/Projects/UOContent/Engines/Craft/Core/CraftItem.cs b/Projects/UOContent/Engines/Craft/Core/CraftItem.cs index b88325d14..7f1085a8e 100644 --- a/Projects/UOContent/Engines/Craft/Core/CraftItem.cs +++ b/Projects/UOContent/Engines/Craft/Core/CraftItem.cs @@ -402,12 +402,10 @@ namespace Server.Engines.Craft var vx = from.X + x; var vy = from.Y + y; - var tiles = map.Tiles.GetStaticTiles(vx, vy, true); - - for (var i = 0; i < tiles.Length; ++i) + foreach (var tile in map.Tiles.GetStaticAndMultiTiles(vx, vy)) { - var z = tiles[i].Z; - var id = tiles[i].ID; + var z = tile.Z; + var id = tile.ID; if (z + 16 > from.Z && from.Z + 16 > z && Find(id, itemIDs)) { diff --git a/Projects/UOContent/Engines/Craft/DefBlacksmithy.cs b/Projects/UOContent/Engines/Craft/DefBlacksmithy.cs index 75fa1ed26..58001d606 100644 --- a/Projects/UOContent/Engines/Craft/DefBlacksmithy.cs +++ b/Projects/UOContent/Engines/Craft/DefBlacksmithy.cs @@ -67,19 +67,17 @@ public class DefBlacksmithy : CraftSystem { for (var y = -range; (!anvil || !forge) && y <= range; ++y) { - var tiles = map.Tiles.GetStaticTiles(from.X + x, from.Y + y, true); - - for (var i = 0; (!anvil || !forge) && i < tiles.Length; ++i) + foreach (var tile in map.Tiles.GetStaticAndMultiTiles(from.X + x, from.Y + y)) { - var id = tiles[i].ID; + var id = tile.ID; var isAnvil = id is 4015 or 4016 or 11733 or 11734; var isForge = id is 4017 or >= 6522 and <= 6569 or 11736; if (isAnvil || isForge) { - if (from.Z + 16 < tiles[i].Z || tiles[i].Z + 16 < from.Z || - !from.InLOS(new Point3D(from.X + x, from.Y + y, tiles[i].Z + tiles[i].Height / 2 + 1))) + if (from.Z + 16 < tile.Z || tile.Z + 16 < from.Z || + !from.InLOS(new Point3D(from.X + x, from.Y + y, tile.Z + tile.Height / 2 + 1))) { continue; } diff --git a/Projects/UOContent/Engines/Pathing/Movement.cs b/Projects/UOContent/Engines/Pathing/Movement.cs index 78f7ad336..b1919369e 100644 --- a/Projects/UOContent/Engines/Pathing/Movement.cs +++ b/Projects/UOContent/Engines/Pathing/Movement.cs @@ -1,4 +1,6 @@ +using System; using System.Collections.Generic; +using System.Runtime.CompilerServices; using Server.Items; using Server.Mobiles; @@ -177,15 +179,15 @@ namespace Server.Movement return moveIsOk; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool CheckMovement(Mobile m, Direction d, out int newZ) => CheckMovement(m, m.Map, m.Location, d, out newZ); private static bool IsOk( - bool ignoreDoors, bool ignoreSpellFields, int ourZ, int ourTop, StaticTile[] tiles, List items + bool ignoreDoors, bool ignoreSpellFields, int ourZ, int ourTop, Map map, int x, int y, List items ) { - for (var i = 0; i < tiles.Length; ++i) + foreach (var check in map.Tiles.GetStaticAndMultiTiles(x, y)) { - var check = tiles[i]; var itemData = TileData.ItemTable[check.ID & TileData.MaxItemValue]; if (itemData.ImpassableSurface) @@ -247,7 +249,6 @@ namespace Server.Movement var cantWalk = m.CantWalk; var canSwim = m.CanSwim; - var tiles = map.Tiles.GetStaticTiles(x, y, true); var landTile = map.Tiles.GetLandTile(x, y); var flags = TileData.LandTable[landTile.ID & TileData.MaxLandValue].Flags; var impassable = (flags & TileFlag.Impassable) != 0; @@ -269,9 +270,8 @@ namespace Server.Movement int testTop; - for (var i = 0; i < tiles.Length; ++i) + foreach (var tile in map.Tiles.GetStaticAndMultiTiles(x, y)) { - var tile = tiles[i]; var itemData = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; if (m.Flying && itemData.Name.InsensitiveEquals("hover over")) @@ -348,23 +348,14 @@ namespace Server.Movement continue; } - var landCheck = itemZ; - - if (itemData.Height >= StepHeight) - { - landCheck += StepHeight; - } - else - { - landCheck += itemData.Height; - } + var landCheck = itemZ + Math.Min(itemData.Height, StepHeight); if (considerLand && landCheck < landCenter && landCenter > ourZ && testTop > landZ) { continue; } - if (IsOk(ignoreDoors, ignoreSpellFields, ourZ, testTop, tiles, items)) + if (IsOk(ignoreDoors, ignoreSpellFields, ourZ, testTop, map, x, y, items)) { newZ = ourZ; moveIsOk = true; @@ -429,23 +420,14 @@ namespace Server.Movement continue; } - var landCheck = itemZ; - - if (itemData.Height >= StepHeight) - { - landCheck += StepHeight; - } - else - { - landCheck += itemData.Height; - } + var landCheck = itemZ + Math.Min(itemData.Height, StepHeight); if (considerLand && landCheck < landCenter && landCenter > ourZ && testTop > landZ) { continue; } - if (IsOk(ignoreDoors, ignoreSpellFields, ourZ, testTop, tiles, items)) + if (IsOk(ignoreDoors, ignoreSpellFields, ourZ, testTop, map, x, y, items)) { newZ = ourZ; moveIsOk = true; @@ -476,7 +458,7 @@ namespace Server.Movement } } - if (shouldCheck && IsOk(ignoreDoors, ignoreSpellFields, landCenter, testTop, tiles, items)) + if (shouldCheck && IsOk(ignoreDoors, ignoreSpellFields, landCenter, testTop, map, x, y, items)) { newZ = landCenter; moveIsOk = true; @@ -498,6 +480,7 @@ namespace Server.Movement return moveIsOk; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool CanMoveOver(Mobile m, Mobile t) => !t.Alive || !m.Alive || t.IsDeadBondedPet || m.IsDeadBondedPet || t.Hidden && t.AccessLevel > AccessLevel.Player; @@ -529,11 +512,8 @@ namespace Server.Movement isSet = true; } - var staticTiles = map.Tiles.GetStaticTiles(xCheck, yCheck, true); - - for (var i = 0; i < staticTiles.Length; ++i) + foreach (var tile in map.Tiles.GetStaticAndMultiTiles(xCheck, yCheck)) { - var tile = staticTiles[i]; var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; var calcTop = tile.Z + id.CalcHeight; @@ -599,38 +579,55 @@ namespace Server.Movement } } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void Offset(Direction d, ref int x, ref int y) { switch (d & Direction.Mask) { case Direction.North: - --y; - break; + { + --y; + break; + } case Direction.South: - ++y; - break; + { + ++y; + break; + } case Direction.West: - --x; - break; + { + --x; + break; + } case Direction.East: - ++x; - break; + { + ++x; + break; + } case Direction.Right: - ++x; - --y; - break; + { + ++x; + --y; + break; + } case Direction.Left: - --x; - ++y; - break; + { + --x; + ++y; + break; + } case Direction.Down: - ++x; - ++y; - break; + { + ++x; + ++y; + break; + } case Direction.Up: - --x; - --y; - break; + { + --x; + --y; + break; + } } } } diff --git a/Projects/UOContent/Engines/Spawners/Spawner.cs b/Projects/UOContent/Engines/Spawners/Spawner.cs index 517a7779d..203187c6d 100644 --- a/Projects/UOContent/Engines/Spawners/Spawner.cs +++ b/Projects/UOContent/Engines/Spawners/Spawner.cs @@ -61,12 +61,8 @@ namespace Server.Engines.Spawners return true; } - var staticTiles = map.Tiles.GetStaticTiles(x, y, true); - - for (var i = 0; i < staticTiles.Length; ++i) + foreach (var staticTile in map.Tiles.GetStaticAndMultiTiles(x, y)) { - var staticTile = staticTiles[i]; - if (staticTile.Z == z && TileData.ItemTable[staticTile.ID & TileData.MaxItemValue].Wet) { return true; diff --git a/Projects/UOContent/Items/Addons/BaseAddon.cs b/Projects/UOContent/Items/Addons/BaseAddon.cs index cdd80f1ed..f771e4315 100644 --- a/Projects/UOContent/Items/Addons/BaseAddon.cs +++ b/Projects/UOContent/Items/Addons/BaseAddon.cs @@ -217,11 +217,8 @@ namespace Server.Items return false; } - var tiles = map.Tiles.GetStaticTiles(x, y, true); - - for (var i = 0; i < tiles.Length; ++i) + foreach (var t in map.Tiles.GetStaticAndMultiTiles(x, y)) { - var t = tiles[i]; var id = TileData.ItemTable[t.ID & TileData.MaxItemValue]; if ((id.Flags & TileFlag.Wall) != 0 && z + 16 > t.Z && t.Z + t.Height > z) diff --git a/Projects/UOContent/Items/Misc/InteriorDecorator.cs b/Projects/UOContent/Items/Misc/InteriorDecorator.cs index af0f172ea..5b5305655 100644 --- a/Projects/UOContent/Items/Misc/InteriorDecorator.cs +++ b/Projects/UOContent/Items/Misc/InteriorDecorator.cs @@ -328,13 +328,10 @@ public partial class InteriorDecorator : Item return int.MinValue; } - var tiles = map.Tiles.GetStaticTiles(item.X, item.Y, true); - var z = int.MinValue; - for (var i = 0; i < tiles.Length; ++i) + foreach (var tile in map.Tiles.GetStaticAndMultiTiles(item.X, item.Y)) { - var tile = tiles[i]; var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; var top = tile.Z; // Confirmed : no height checks here diff --git a/Projects/UOContent/Misc/DoorGenerator.cs b/Projects/UOContent/Misc/DoorGenerator.cs index e43224b96..1a3a495d0 100644 --- a/Projects/UOContent/Misc/DoorGenerator.cs +++ b/Projects/UOContent/Misc/DoorGenerator.cs @@ -431,12 +431,8 @@ namespace Server public static bool IsEastFrame(int x, int y, int z) { - var tiles = m_Map.Tiles.GetStaticTiles(x, y); - - for (var i = 0; i < tiles.Length; ++i) + foreach (var tile in m_Map.Tiles.GetStaticTiles(x, y)) { - var tile = tiles[i]; - if (tile.Z == z && IsEastFrame(tile.ID)) { return true; @@ -448,12 +444,8 @@ namespace Server public static bool IsSouthFrame(int x, int y, int z) { - var tiles = m_Map.Tiles.GetStaticTiles(x, y); - - for (var i = 0; i < tiles.Length; ++i) + foreach (var tile in m_Map.Tiles.GetStaticTiles(x, y)) { - var tile = tiles[i]; - if (tile.Z == z && IsSouthFrame(tile.ID)) { return true; @@ -510,12 +502,8 @@ namespace Server var vx = rx + region.X; var vy = ry + region.Y; - var tiles = m_Map.Tiles.GetStaticTiles(vx, vy); - - for (var i = 0; i < tiles.Length; ++i) + foreach (var tile in m_Map.Tiles.GetStaticTiles(vx, vy)) { - var tile = tiles[i]; - var id = tile.ID; var z = tile.Z; diff --git a/Projects/UOContent/Multis/Boats/BaseBoat.cs b/Projects/UOContent/Multis/Boats/BaseBoat.cs index b4908b279..3e6fc2455 100644 --- a/Projects/UOContent/Multis/Boats/BaseBoat.cs +++ b/Projects/UOContent/Multis/Boats/BaseBoat.cs @@ -1462,10 +1462,9 @@ namespace Server.Multis } var landTile = map.Tiles.GetLandTile(tx, ty); - var tiles = map.Tiles.GetStaticTiles(tx, ty, true); var hasWater = landTile.Z == p.Z && - (landTile.ID >= 168 && landTile.ID <= 171 || landTile.ID >= 310 && landTile.ID <= 311); + landTile.ID is >= 168 and <= 171 or >= 310 and <= 311; // int z = p.Z; @@ -1476,9 +1475,8 @@ namespace Server.Multis // if (!landTile.Ignored && top > landZ && landTop > z) // return false; - for (var i = 0; i < tiles.Length; ++i) + foreach (var tile in map.Tiles.GetStaticAndMultiTiles(tx, ty)) { - var tile = tiles[i]; var isWater = tile.ID >= 0x1796 && tile.ID <= 0x17B2; if (tile.Z == p.Z && isWater) diff --git a/Projects/UOContent/Multis/Houses/HouseFoundation.cs b/Projects/UOContent/Multis/Houses/HouseFoundation.cs index f7847732a..c79267ba7 100644 --- a/Projects/UOContent/Multis/Houses/HouseFoundation.cs +++ b/Projects/UOContent/Multis/Houses/HouseFoundation.cs @@ -125,7 +125,7 @@ namespace Server.Multis SetInitialState(); } - return m_Current.Components; + return m_Current!.Components; } } diff --git a/Projects/UOContent/Multis/Houses/HousePlacement.cs b/Projects/UOContent/Multis/Houses/HousePlacement.cs index d1aa89bba..d97f734ab 100644 --- a/Projects/UOContent/Multis/Houses/HousePlacement.cs +++ b/Projects/UOContent/Multis/Houses/HousePlacement.cs @@ -137,8 +137,6 @@ namespace Server.Multis var landTile = map.Tiles.GetLandTile(tileX, tileY); var landID = landTile.ID & TileData.MaxLandValue; - var oldTiles = map.Tiles.GetStaticTiles(tileX, tileY, true); - items.Clear(); foreach (var item in map.GetItemsAt(tileX, tileY)) @@ -202,13 +200,12 @@ namespace Server.Multis hasSurface = true; } - for (var j = 0; j < oldTiles.Length; ++j) + foreach (var tile in map.Tiles.GetStaticAndMultiTiles(tileX, tileY)) { - var oldTile = oldTiles[j]; - var id = TileData.ItemTable[oldTile.ID & TileData.MaxItemValue]; + var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; if ((id.Impassable || id.Surface && !id.Background) && - addTileTop > oldTile.Z && oldTile.Z + id.CalcHeight > addTileZ) + addTileTop > tile.Z && tile.Z + id.CalcHeight > addTileZ) { return HousePlacementResult.BadStatic; // Broke rule #2 } @@ -341,11 +338,8 @@ namespace Server.Multis } } - var tiles = map.Tiles.GetStaticTiles(borderPoint.X, borderPoint.Y, true); - - for (var j = 0; j < tiles.Length; ++j) + foreach (var tile in map.Tiles.GetStaticAndMultiTiles(borderPoint.X, borderPoint.Y)) { - var tile = tiles[j]; var id = TileData.ItemTable[tile.ID & TileData.MaxItemValue]; if (id.Impassable || id.Surface && !id.Background && tile.Z + id.CalcHeight > center.Z + 2) diff --git a/Projects/UOContent/Network/Packets/IncomingTargetingPackets.cs b/Projects/UOContent/Network/Packets/IncomingTargetingPackets.cs index a6bdbdec0..988aa50c8 100644 --- a/Projects/UOContent/Network/Packets/IncomingTargetingPackets.cs +++ b/Projects/UOContent/Network/Packets/IncomingTargetingPackets.cs @@ -83,10 +83,6 @@ public static class IncomingTargetingPackets } else { - var tiles = map.Tiles.GetStaticTiles(x, y, !t.DisallowMultis); - - var valid = false; - if (state.HighSeas) { var id = TileData.ItemTable[graphic & TileData.MaxItemValue]; @@ -97,14 +93,19 @@ public static class IncomingTargetingPackets } int hue = 0; + var valid = false; - for (var i = 0; !valid && i < tiles.Length; ++i) + var eable = t.DisallowMultis + ? map.Tiles.GetStaticTiles(x, y) + : map.Tiles.GetStaticAndMultiTiles(x, y); + + foreach (var tile in eable) { - var tile = tiles[i]; if (tile.Z == z && tile.ID == graphic) { valid = true; hue = tile.Hue; + break; } } diff --git a/Projects/UOContent/Spells/Spellweaving/ArcaneCircle.cs b/Projects/UOContent/Spells/Spellweaving/ArcaneCircle.cs index 473087a32..8becc9600 100644 --- a/Projects/UOContent/Spells/Spellweaving/ArcaneCircle.cs +++ b/Projects/UOContent/Spells/Spellweaving/ArcaneCircle.cs @@ -77,11 +77,8 @@ namespace Server.Spells.Spellweaving return true; } - var tiles = map.Tiles.GetStaticTiles(location.X, location.Y); // Static Tiles - - for (var i = 0; i < tiles.Length; ++i) + foreach (var t in map.Tiles.GetStaticTiles(location.X, location.Y)) { - var t = tiles[i]; var id = TileData.ItemTable[t.ID & TileData.MaxItemValue]; var tand = t.ID;