mirror of
https://github.com/modernuo/ModernUO
synced 2026-08-11 22:23:06 -04:00
299 lines
10 KiB
C#
299 lines
10 KiB
C#
/*************************************************************************
|
|
* ModernUO *
|
|
* Copyright 2019-2026 - ModernUO Development Team *
|
|
* Email: hi@modernuo.com *
|
|
* File: Map.ClientByDistanceEnumerator.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 <http://www.gnu.org/licenses/>. *
|
|
*************************************************************************/
|
|
|
|
using System;
|
|
using System.Runtime.CompilerServices;
|
|
using Server.Collections;
|
|
using Server.Network;
|
|
|
|
namespace Server;
|
|
|
|
public partial class Map
|
|
{
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public ClientDistanceEnumerable GetClientsInRangeByDistance(Point3D p) =>
|
|
GetClientsInRangeByDistance(p, Core.GlobalMaxUpdateRange);
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public ClientDistanceEnumerable GetClientsInRangeByDistance(Point3D p, int range) =>
|
|
GetClientsInRangeByDistance(p.m_X, p.m_Y, range);
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public ClientDistanceEnumerable GetClientsInRangeByDistance(Point2D p) =>
|
|
GetClientsInRangeByDistance(p, Core.GlobalMaxUpdateRange);
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public ClientDistanceEnumerable GetClientsInRangeByDistance(Point2D p, int range) =>
|
|
GetClientsInRangeByDistance(p.m_X, p.m_Y, range);
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public ClientDistanceEnumerable GetClientsInRangeByDistance(int x, int y, int range)
|
|
{
|
|
var clampedRange = Math.Max(0, range);
|
|
var edge = clampedRange * 2 + 1;
|
|
return GetClientsInBoundsByDistance(
|
|
new Rectangle2D(x - clampedRange, y - clampedRange, edge, edge),
|
|
new Point2D(x, y)
|
|
);
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public ClientDistanceEnumerable GetClientsInBoundsByDistance(Rectangle2D bounds, bool makeBoundsInclusive = false) =>
|
|
GetClientsInBoundsByDistance(bounds, new Point2D(bounds.X + bounds.Width / 2, bounds.Y + bounds.Height / 2), makeBoundsInclusive);
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private ClientDistanceEnumerable GetClientsInBoundsByDistance(
|
|
Rectangle2D bounds, Point2D center, bool makeBoundsInclusive = false
|
|
) => new(this, bounds, center, makeBoundsInclusive);
|
|
|
|
public ref struct ClientDistanceEnumerable
|
|
{
|
|
private readonly Map _map;
|
|
private readonly Rectangle2D _bounds;
|
|
private readonly Point2D _center;
|
|
private readonly bool _makeBoundsInclusive;
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public ClientDistanceEnumerable(Map map, Rectangle2D bounds, Point2D center, bool makeBoundsInclusive)
|
|
{
|
|
_map = map;
|
|
_bounds = bounds;
|
|
_center = center;
|
|
_makeBoundsInclusive = makeBoundsInclusive;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public ClientDistanceEnumerator GetEnumerator() => new(_map, _bounds, _center, _makeBoundsInclusive);
|
|
}
|
|
|
|
public ref struct ClientDistanceEnumerator
|
|
{
|
|
private Map _map;
|
|
private Point2D _center;
|
|
private Rectangle2D _bounds;
|
|
|
|
private int _sectorStartX;
|
|
private int _maxRing;
|
|
|
|
private int _ring; // -1 = uninitialized, then 0.._maxRing
|
|
private int _ringIndex; // Current index within the ring
|
|
|
|
private int _currentSectorX;
|
|
private int _currentSectorY;
|
|
|
|
private ref readonly ValueLinkList<NetState> _linkList;
|
|
private int _currentVersion;
|
|
private NetState _current;
|
|
|
|
private int _minDistance;
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public ClientDistanceEnumerator(Map map, Rectangle2D bounds, Point2D center, bool makeBoundsInclusive)
|
|
{
|
|
_map = map;
|
|
_center = center;
|
|
_bounds = makeBoundsInclusive
|
|
? new Rectangle2D(bounds.X, bounds.Y, bounds.Width + 1, bounds.Height + 1)
|
|
: bounds;
|
|
|
|
_current = null;
|
|
|
|
if (map != null)
|
|
{
|
|
var centerSectorX = center.m_X / SectorSize;
|
|
var centerSectorY = center.m_Y / SectorSize;
|
|
|
|
map.CalculateSectors(_bounds, out _sectorStartX, out var sectorStartY, out var sectorEndX, out var sectorEndY);
|
|
|
|
// Calculate max ring based on bounds
|
|
var dx = Math.Max(centerSectorX - _sectorStartX, sectorEndX - centerSectorX);
|
|
var dy = Math.Max(centerSectorY - sectorStartY, sectorEndY - centerSectorY);
|
|
_maxRing = Math.Max(dx, dy);
|
|
}
|
|
|
|
_ring = -1;
|
|
_ringIndex = -1;
|
|
_currentSectorX = 0;
|
|
_currentSectorY = 0;
|
|
|
|
_currentVersion = 0;
|
|
_minDistance = 0;
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
public bool MoveNext()
|
|
{
|
|
var map = _map;
|
|
|
|
if (map == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if (!Unsafe.IsNullRef(in _linkList) && _linkList.Version != _currentVersion)
|
|
{
|
|
throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion);
|
|
}
|
|
|
|
var current = _current;
|
|
|
|
while (true)
|
|
{
|
|
current = current?.Next;
|
|
|
|
while (current == null)
|
|
{
|
|
while (!TryNextSectorInRing(out _currentSectorX, out _currentSectorY))
|
|
{
|
|
// Current ring exhausted, try next ring
|
|
if (_ring >= _maxRing)
|
|
{
|
|
return false; // No more rings to search
|
|
}
|
|
|
|
_ring++;
|
|
_ringIndex = -1;
|
|
}
|
|
|
|
_linkList = ref map.GetRealSector(_currentSectorX, _currentSectorY).Clients;
|
|
_currentVersion = _linkList.Version;
|
|
current = _linkList._first;
|
|
|
|
if (current != null)
|
|
{
|
|
_minDistance = MinDistToSectorSqrt(_center.m_X, _center.m_Y, _currentSectorX, _currentSectorY);
|
|
}
|
|
}
|
|
|
|
var m = current.Mobile;
|
|
if (m?.Deleted == false && _bounds.Contains(m.Location))
|
|
{
|
|
_current = current;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
public (NetState Value, int MinDistance) Current
|
|
{
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
get => (_current, _minDistance);
|
|
}
|
|
|
|
private bool TryNextSectorInRing(out int sx, out int sy)
|
|
{
|
|
if (_ring == 0)
|
|
{
|
|
// Center sector
|
|
if (_ringIndex < 0)
|
|
{
|
|
_ringIndex = 0;
|
|
sx = _center.m_X / SectorSize;
|
|
sy = _center.m_Y / SectorSize;
|
|
return sx >= _sectorStartX;
|
|
}
|
|
|
|
sx = sy = 0;
|
|
return false;
|
|
}
|
|
|
|
var totalSectors = _ring * 8;
|
|
|
|
// Keep trying sectors in this ring until we find a valid one or exhaust the ring
|
|
while (true)
|
|
{
|
|
var nextIndex = _ringIndex + 1;
|
|
|
|
if (nextIndex >= totalSectors)
|
|
{
|
|
sx = sy = 0;
|
|
return false;
|
|
}
|
|
|
|
_ringIndex = nextIndex;
|
|
CalculatePositionFromIndex(nextIndex, out sx, out sy);
|
|
|
|
if (sx >= _sectorStartX)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private void CalculatePositionFromIndex(int index, out int x, out int y)
|
|
{
|
|
var centerSectorX = _center.m_X / SectorSize;
|
|
var centerSectorY = _center.m_Y / SectorSize;
|
|
|
|
var ringSize = _ring * 2;
|
|
var startX = centerSectorX - _ring;
|
|
var startY = centerSectorY - _ring;
|
|
|
|
if (index <= ringSize) // Top edge
|
|
{
|
|
x = startX + index;
|
|
y = startY;
|
|
}
|
|
else if (index <= ringSize * 2) // Right edge
|
|
{
|
|
x = startX + ringSize;
|
|
y = startY + (index - ringSize);
|
|
}
|
|
else if (index <= ringSize * 3) // Bottom edge
|
|
{
|
|
x = startX + ringSize - (index - ringSize * 2);
|
|
y = startY + ringSize;
|
|
}
|
|
else // Left edge
|
|
{
|
|
x = startX;
|
|
y = startY + ringSize - (index - ringSize * 3);
|
|
}
|
|
}
|
|
|
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
private static int MinDistToSectorSqrt(int cx, int cy, int sectorX, int sectorY)
|
|
{
|
|
var x0 = sectorX * SectorSize;
|
|
var y0 = sectorY * SectorSize;
|
|
var x1 = x0 + (SectorSize - 1);
|
|
var y1 = y0 + (SectorSize - 1);
|
|
|
|
var dx = 0;
|
|
if (cx < x0)
|
|
{
|
|
dx = x0 - cx;
|
|
}
|
|
else if (cx > x1)
|
|
{
|
|
dx = cx - x1;
|
|
}
|
|
|
|
var dy = 0;
|
|
if (cy < y0)
|
|
{
|
|
dy = y0 - cy;
|
|
}
|
|
else if (cy > y1)
|
|
{
|
|
dy = cy - y1;
|
|
}
|
|
|
|
return (int)Math.Sqrt(dx * dx + dy * dy);
|
|
}
|
|
}
|
|
}
|