fix: Changes Map.Sector.Mobiles to link list & Fixes various related crash bugs (#1553)

### Summary

Eliminates `IPooledEnumerable<T>` and `eable.Free()` from `Map` for mobiles. This drastically simplifies code that iterates in range, for example:

```cs
foreach (var m in m.GetMobilesInRange(5))
{
}
```
The code above no longer requires an eable and calling `Free()`.

- [X] Fixed several locations where an NPC that was damaged would cause a server crash.
- [X] Removed an unnecessary allocation in guard fake calls (NPCs calling guards on you)
- [X] Fixes damage precision loss in Poison Strike Spell
- [X] BogThing no longer attempts to "search" for boglings to eat when it is at full health
This commit is contained in:
Kamron Batman 2023-10-29 22:42:46 -07:00 committed by GitHub
parent 27f0cec1fa
commit 28c06c1cc0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
71 changed files with 796 additions and 591 deletions

View file

@ -2488,21 +2488,24 @@ public class Item : IHued, IComparable<Item>, ISpawnable, IObjectPropertyListEnt
public Map.ItemBoundsEnumerable<T> GetItemsInRange<T>(int range) where T : Item =>
m_Map == null ? Map.ItemBoundsEnumerable<T>.Empty : m_Map.GetItemsInRange<T>(m_Parent == null ? m_Location : GetWorldLocation(), range);
public IPooledEnumerable<Mobile> GetMobilesInRange(int range)
{
var map = m_Map;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Map.MobileAtEnumerable<Mobile> GetMobilesAt() => GetMobilesAt<Mobile>();
return map?.GetMobilesInRange(m_Parent == null ? m_Location : GetWorldLocation(), range)
?? PooledEnumeration.NullEnumerable<Mobile>.Instance;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Map.MobileAtEnumerable<T> GetMobilesAt<T>() where T : Mobile =>
m_Map == null ? Map.MobileAtEnumerable<T>.Empty : m_Map.GetMobilesAt<T>(m_Parent == null ? m_Location : GetWorldLocation());
public IPooledEnumerable<NetState> GetClientsInRange(int range)
{
var map = m_Map;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Map.MobileBoundsEnumerable<Mobile> GetMobilesInRange(int range) => GetMobilesInRange<Mobile>(range);
return map.GetClientsInRange(m_Parent == null ? m_Location : GetWorldLocation(), range)
?? PooledEnumeration.NullEnumerable<NetState>.Instance;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Map.MobileBoundsEnumerable<T> GetMobilesInRange<T>(int range) where T : Mobile =>
m_Map == null ? Map.MobileBoundsEnumerable<T>.Empty : m_Map.GetMobilesInRange<T>(m_Parent == null ? m_Location : GetWorldLocation(), range);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public IPooledEnumerable<NetState> GetClientsInRange(int range) =>
m_Map.GetClientsInRange(m_Parent == null ? m_Location : GetWorldLocation(), range)
?? PooledEnumeration.NullEnumerable<NetState>.Instance;
public bool GetTempFlag(int flag) => ((LookupCompactInfo()?.m_TempFlags ?? 0) & flag) != 0;

View file

@ -0,0 +1,288 @@
/*************************************************************************
* ModernUO *
* Copyright 2019-2023 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: Map.MobileEnumerator.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;
namespace Server;
public partial class Map
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MobileAtEnumerable<Mobile> GetMobilesAt(Point3D p) => GetMobilesAt<Mobile>(p);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MobileAtEnumerable<T> GetMobilesAt<T>(Point3D p) where T : Mobile => GetMobilesAt<T>(new Point2D(p.X, p.Y));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MobileAtEnumerable<Mobile> GetMobilesAt(int x, int y) => GetMobilesAt<Mobile>(new Point2D(x, y));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MobileAtEnumerable<T> GetMobilesAt<T>(int x, int y) where T : Mobile => GetMobilesAt<T>(new Point2D(x, y));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MobileAtEnumerable<Mobile> GetMobilesAt(Point2D p) => GetMobilesAt<Mobile>(p);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MobileAtEnumerable<T> GetMobilesAt<T>(Point2D p) where T : Mobile => new(this, p);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MobileBoundsEnumerable<Mobile> GetMobilesInRange(Point3D p) => GetMobilesInRange<Mobile>(p);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MobileBoundsEnumerable<Mobile> GetMobilesInRange(Point3D p, int range) => GetMobilesInRange<Mobile>(p, range);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MobileBoundsEnumerable<T> GetMobilesInRange<T>(Point3D p) where T : Mobile => GetMobilesInRange<T>(p, Core.GlobalMaxUpdateRange);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MobileBoundsEnumerable<T> GetMobilesInRange<T>(Point3D p, int range) where T : Mobile =>
GetMobilesInRange<T>(p.m_X, p.m_Y, range);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MobileBoundsEnumerable<Mobile> GetMobilesInRange(Point2D p) => GetMobilesInRange<Mobile>(p);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MobileBoundsEnumerable<Mobile> GetMobilesInRange(Point2D p, int range) => GetMobilesInRange<Mobile>(p, range);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MobileBoundsEnumerable<T> GetMobilesInRange<T>(Point2D p) where T : Mobile => GetMobilesInRange<T>(p, Core.GlobalMaxUpdateRange);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MobileBoundsEnumerable<T> GetMobilesInRange<T>(Point2D p, int range) where T : Mobile =>
GetMobilesInRange<T>(p.m_X, p.m_Y, range);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MobileBoundsEnumerable<T> GetMobilesInRange<T>(int x, int y, int range) where T : Mobile =>
GetMobilesInBounds<T>(new Rectangle2D(x - range, y - range, range * 2 + 1, range * 2 + 1));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MobileBoundsEnumerable<Mobile> GetMobilesInBounds(Rectangle2D bounds) => GetMobilesInBounds<Mobile>(bounds);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MobileBoundsEnumerable<T> GetMobilesInBounds<T>(Rectangle2D bounds, bool makeBoundsInclusive = false) where T : Mobile =>
new(this, bounds, makeBoundsInclusive);
public ref struct MobileAtEnumerable<T> where T : Mobile
{
public static MobileAtEnumerable<T> Empty
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new();
}
private Map _map;
private Point2D _location;
public MobileAtEnumerable(Map map, Point2D loc)
{
_map = map;
_location = loc;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MobileAtEnumerator<T> GetEnumerator() => new(_map, _location);
}
public ref struct MobileAtEnumerator<T> where T : Mobile
{
private bool _started;
private Point2D _location;
private ref readonly ValueLinkList<Mobile> _linkList;
private int _version;
private T _current;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MobileAtEnumerator(Map map, Point2D loc)
{
_started = false;
_location = loc;
_linkList = ref map.GetRealSector(loc.m_X, loc.m_Y).Mobiles;
_version = 0;
_current = null;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
ref var loc = ref _location;
Mobile current;
if (!_started)
{
current = _linkList._first;
_started = true;
_version = _linkList.Version;
if (current is T { Deleted: false } o && o.X == loc.m_X && o.Y == loc.m_Y)
{
_current = o;
return true;
}
}
else if (_linkList.Version != _version)
{
throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion);
}
else
{
current = _current;
}
while (current != null)
{
current = current.Next;
if (current is T { Deleted: false } o && o.X == loc.m_X && o.Y == loc.m_Y)
{
_current = o;
return true;
}
}
return false;
}
public T Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _current;
}
}
public ref struct MobileBoundsEnumerable<T> where T : Mobile
{
public static MobileBoundsEnumerable<T> Empty
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => new(null, Rectangle2D.Empty, false);
}
private Map _map;
private Rectangle2D _bounds;
private bool _makeBoundsInclusive;
public MobileBoundsEnumerable(Map map, Rectangle2D bounds, bool makeBoundsInclusive)
{
_map = map;
_bounds = bounds;
_makeBoundsInclusive = makeBoundsInclusive;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MobileEnumerator<T> GetEnumerator() => new(_map, _bounds, _makeBoundsInclusive);
}
public ref struct MobileEnumerator<T> where T : Mobile
{
private Map _map;
private int _sectorStartX;
private int _sectorEndX;
private int _sectorEndY;
private Rectangle2D _bounds;
private int _currentSectorX;
private int _currentSectorY;
private ref readonly ValueLinkList<Mobile> _linkList;
private int _currentVersion;
private T _current;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public MobileEnumerator(Map map, Rectangle2D bounds, bool makeBoundsInclusive)
{
_map = map;
_bounds = bounds;
if (makeBoundsInclusive)
{
++bounds.Width;
++bounds.Height;
}
_bounds = bounds;
map.CalculateSectors(bounds, out _sectorStartX, out var _sectorStartY, out _sectorEndX, out _sectorEndY);
// We start the X sector one short because it gets incremented immediately in MoveNext()
_currentSectorX = _sectorStartX - 1;
_currentSectorY = _sectorStartY;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool MoveNext()
{
var map = _map;
if (map == null)
{
return false;
}
Mobile current = _current;
ref Rectangle2D bounds = ref _bounds;
var currentSectorX = _currentSectorX;
var currentSectorY = _currentSectorY;
var sectorEndX = _sectorEndX;
var sectorEndY = _sectorEndY;
while (true)
{
current = current?.Next;
while (current == null)
{
// Move to next sector
if (currentSectorX < sectorEndX)
{
_currentSectorX = ++currentSectorX;
}
else if (currentSectorY < sectorEndY)
{
_currentSectorX = currentSectorX = _sectorStartX;
_currentSectorY = ++currentSectorY;
}
else
{
// Ran out of sectors
return false;
}
_linkList = ref map.GetRealSector(currentSectorX, currentSectorY).Mobiles;
_currentVersion = _linkList.Version;
current = _linkList._first;
}
if (_linkList.Version != _currentVersion)
{
throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion);
}
if (current is T { Deleted: false } o && bounds.Contains(o.Location))
{
_current = o;
return true;
}
}
}
public T Current
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _current;
}
}
}

View file

@ -614,8 +614,10 @@ public sealed partial class Map : IComparable<Map>, ISpanFormattable, ISpanParsa
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void OnEnter(Mobile m) => OnEnter(m.Location, m);
public void OnEnter(Mobile m)
{
OnEnter(m.Location, m);
}
public void OnEnter(Point3D p, Mobile m)
{
@ -650,8 +652,10 @@ public sealed partial class Map : IComparable<Map>, ISpanFormattable, ISpanParsa
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void OnLeave(Mobile m) => OnLeave(m.Location, m);
public void OnLeave(Mobile m)
{
OnLeave(m.Location, m);
}
public void OnLeave(Point3D p, Mobile m)
{
@ -877,18 +881,6 @@ public sealed partial class Map : IComparable<Map>, ISpanFormattable, ISpanParsa
public IPooledEnumerable<NetState> GetClientsInBounds(Rectangle2D bounds) =>
PooledEnumeration.GetClients(this, bounds);
public IPooledEnumerable<Mobile> GetMobilesInRange(Point3D p) => GetMobilesInRange(p, Core.GlobalMaxUpdateRange);
public IPooledEnumerable<Mobile> GetMobilesInRange(Point3D p, int range) => GetMobilesInRange<Mobile>(p, range);
public IPooledEnumerable<T> GetMobilesInRange<T>(Point3D p, int range) where T : Mobile =>
GetMobilesInBounds<T>(new Rectangle2D(p.m_X - range, p.m_Y - range, range * 2 + 1, range * 2 + 1));
public IPooledEnumerable<Mobile> GetMobilesInBounds(Rectangle2D bounds) => GetMobilesInBounds<Mobile>(bounds);
public IPooledEnumerable<T> GetMobilesInBounds<T>(Rectangle2D bounds) where T : Mobile =>
PooledEnumeration.GetMobiles<T>(this, bounds);
public bool CanFit(
Point3D p, int height, bool checkBlocksFit = false, bool checkMobiles = true,
bool requireSurface = true
@ -981,10 +973,8 @@ public sealed partial class Map : IComparable<Map>, ISpanFormattable, ISpanParsa
if (checkMobiles)
{
for (var i = 0; i < mobs.Count; ++i)
foreach (var m in sector.Mobiles)
{
var m = mobs[i];
if (m.Location.m_X == x && m.Location.m_Y == y && (m.AccessLevel == AccessLevel.Player || !m.Hidden) &&
m.Z + 16 > z && z + height > m.Z)
{
@ -1443,7 +1433,7 @@ public sealed partial class Map : IComparable<Map>, ISpanFormattable, ISpanParsa
private bool m_Active;
private List<NetState> _clients;
private ValueLinkList<Item> _items;
private List<Mobile> _mobiles;
private ValueLinkList<Mobile> _mobiles;
private List<BaseMulti> _multis;
private List<Region> _regions;
@ -1459,7 +1449,7 @@ public sealed partial class Map : IComparable<Map>, ISpanFormattable, ISpanParsa
public List<BaseMulti> Multis => _multis ?? m_DefaultMultiList;
public List<Mobile> Mobiles => _mobiles ?? m_DefaultMobileList;
internal ref ValueLinkList<Mobile> Mobiles => ref _mobiles;
internal ref readonly ValueLinkList<Item> Items => ref _items;
@ -1490,7 +1480,7 @@ public sealed partial class Map : IComparable<Map>, ISpanFormattable, ISpanParsa
public void OnEnter(Mobile mob)
{
Utility.Add(ref _mobiles, mob);
_mobiles.AddLast(mob);
if (mob.NetState != null)
{
@ -1502,7 +1492,7 @@ public sealed partial class Map : IComparable<Map>, ISpanFormattable, ISpanParsa
public void OnLeave(Mobile mob)
{
Utility.Remove(ref _mobiles, mob);
_mobiles.Remove(mob);
if (mob.NetState != null)
{
@ -1547,7 +1537,7 @@ public sealed partial class Map : IComparable<Map>, ISpanFormattable, ISpanParsa
private void UpdateMobileRegions()
{
if (_mobiles != null)
if (_mobiles.Count > 0)
{
using var queue = PooledRefQueue<Mobile>.Create(_mobiles.Count);
foreach (var mob in _mobiles)
@ -1581,12 +1571,9 @@ public sealed partial class Map : IComparable<Map>, ISpanFormattable, ISpanParsa
item.OnSectorActivate();
}
if (_mobiles != null)
foreach (var mob in _mobiles)
{
foreach (var mob in _mobiles)
{
mob.OnSectorActivate();
}
mob.OnSectorActivate();
}
m_Active = true;
@ -1602,12 +1589,9 @@ public sealed partial class Map : IComparable<Map>, ISpanFormattable, ISpanParsa
item.OnSectorDeactivate();
}
if (_mobiles != null)
foreach (var mob in _mobiles)
{
foreach (var mob in _mobiles)
{
mob.OnSectorDeactivate();
}
mob.OnSectorDeactivate();
}
m_Active = false;

View file

@ -35,7 +35,6 @@ public static class PooledEnumeration
{
ClientSelector = SelectClients;
EntitySelector = SelectEntities;
MobileSelector = SelectMobiles<Mobile>;
MultiSelector = SelectMultis;
MultiTileSelector = SelectMultiTiles;
}
@ -65,13 +64,9 @@ public static class PooledEnumeration
public static IEnumerable<IEntity> SelectEntities(Map.Sector s, Rectangle2D bounds)
{
var entities = new List<IEntity>(s.Mobiles.Count + s.Items.Count);
for (int i = s.Mobiles.Count - 1; i >= 0; --i)
foreach (var mob in s.Mobiles)
{
Mobile mob = s.Mobiles[i];
if (mob is { Deleted: false } && bounds.Contains(mob.Location))
{
entities.Add(mob);
}
entities.Add(mob);
}
foreach (var item in s.Items)
@ -82,19 +77,6 @@ public static class PooledEnumeration
return entities;
}
public static IEnumerable<T> SelectMobiles<T>(Map.Sector s, Rectangle2D bounds) where T : Mobile
{
var entities = new List<T>(s.Mobiles.Count);
for (int i = s.Mobiles.Count - 1; i >= 0; --i)
{
if (s.Mobiles[i] is T { Deleted: false } mob && bounds.Contains(mob.Location))
{
entities.Add(mob);
}
}
return entities;
}
public static IEnumerable<BaseMulti> SelectMultis(Map.Sector s, Rectangle2D bounds)
{
var entities = new List<BaseMulti>(s.Multis.Count);
@ -169,12 +151,6 @@ public static class PooledEnumeration
public static PooledEnumerable<IEntity> GetEntities(Map map, Rectangle2D bounds) =>
PooledEnumerable<IEntity>.Instantiate(map, bounds, EntitySelector ?? SelectEntities);
public static PooledEnumerable<Mobile> GetMobiles(Map map, Rectangle2D bounds) =>
GetMobiles<Mobile>(map, bounds);
public static PooledEnumerable<T> GetMobiles<T>(Map map, Rectangle2D bounds) where T : Mobile =>
PooledEnumerable<T>.Instantiate(map, bounds, SelectMobiles<T>);
public static PooledEnumerable<BaseMulti> GetMultis(Map map, Rectangle2D bounds) =>
PooledEnumerable<BaseMulti>.Instantiate(map, bounds, MultiSelector ?? SelectMultis);

View file

@ -4154,10 +4154,8 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
if (oldSector != newSector)
{
using var queue = PooledRefQueue<IEntity>.Create(2048);
for (var i = 0; i < oldSector.Mobiles.Count; ++i)
foreach (var m in oldSector.Mobiles)
{
var m = oldSector.Mobiles[i];
if (m != this && m.X == oldX && m.Y == oldY && m.Z + 15 > oldZ && oldZ + 15 > m.Z)
{
queue.Enqueue(m);
@ -4189,10 +4187,8 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
}
}
for (var i = 0; i < newSector.Mobiles.Count; ++i)
foreach (var m in newSector.Mobiles)
{
var m = newSector.Mobiles[i];
if (m.X == x && m.Y == y && m.Z + 15 > newZ && newZ + 15 > m.Z)
{
queue.Enqueue(m);
@ -4227,9 +4223,8 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
else
{
using var queue = PooledRefQueue<(IEntity, byte)>.Create(2048);
for (var i = 0; i < oldSector.Mobiles.Count; ++i)
foreach (var m in oldSector.Mobiles)
{
var m = oldSector.Mobiles[i];
byte flag;
if (m != this && m.X == oldX && m.Y == oldY && m.Z + 15 > oldZ && oldZ + 15 > m.Z)
{
@ -8126,14 +8121,25 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
public Map.ItemBoundsEnumerable<T> GetItemsInRange<T>(int range) where T : Item =>
m_Map == null ? Map.ItemBoundsEnumerable<T>.Empty : m_Map.GetItemsInRange<T>(m_Location, range);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public IPooledEnumerable<IEntity> GetObjectsInRange(int range) =>
m_Map?.GetObjectsInRange(m_Location, range) ?? PooledEnumeration.NullEnumerable<IEntity>.Instance;
public IPooledEnumerable<Mobile> GetMobilesInRange(int range) => GetMobilesInRange<Mobile>(range);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Map.MobileAtEnumerable<Mobile> GetMobilesInRange() => GetMobilesInRange<Mobile>();
public IPooledEnumerable<T> GetMobilesInRange<T>(int range) where T : Mobile =>
m_Map?.GetMobilesInRange<T>(m_Location, range) ?? PooledEnumeration.NullEnumerable<T>.Instance;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Map.MobileAtEnumerable<T> GetMobilesInRange<T>() where T : Mobile =>
m_Map == null ? Map.MobileAtEnumerable<T>.Empty : m_Map.GetMobilesAt<T>(m_Location);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Map.MobileBoundsEnumerable<Mobile> GetMobilesInRange(int range) => GetMobilesInRange<Mobile>(range);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public Map.MobileBoundsEnumerable<T> GetMobilesInRange<T>(int range) where T : Mobile =>
m_Map == null ? Map.MobileBoundsEnumerable<T>.Empty : m_Map.GetMobilesInRange<T>(m_Location, range);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public IPooledEnumerable<NetState> GetClientsInRange(int range) =>
m_Map?.GetClientsInRange(m_Location, range) ?? PooledEnumeration.NullEnumerable<NetState>.Instance;

View file

@ -1175,10 +1175,8 @@ namespace Server.Commands
if (item is BaseDoor door)
{
var eable = maps[j].GetItemsInRange<BaseDoor>(loc, 1);
var itemType = door.GetType();
foreach (var link in eable)
foreach (var link in maps[j].GetItemsInRange<BaseDoor>(loc, 1))
{
if (link != item && link.Z == door.Z && link.GetType() == itemType)
{

View file

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using ModernUO.Serialization;
using Server.Collections;
using Server.Mobiles;
using Server.Network;
using Server.Spells;
@ -605,30 +606,35 @@ public partial class LeverPuzzleController : Item
PlayerSendASCII(m_Player, 1); // A speeding rock ...
PlaySounds(m_Player.Location, !m_Player.Female ? fs2 : ms2);
using var queue = PooledRefQueue<Mobile>.Create();
var j = Utility.Random(6, 10);
for (var i = 0; i < j; i++)
{
IEntity m_IEntity = new Entity(Serial.Zero, RandomPointIn(m_Player.Location, 10), m_Player.Map);
var entity = new Entity(Serial.Zero, RandomPointIn(m_Player.Location, 10), m_Player.Map);
var eable = m_IEntity.Map.GetMobilesInRange(m_IEntity.Location, 2);
var mobiles = new List<Mobile>();
mobiles.AddRange(eable);
for (var k = 0; k < mobiles.Count; k++)
// Queue the mobiles because we cannot modify while iterating through mobiles in range
foreach (var m in entity.Map.GetMobilesInRange(entity.Location, 2))
{
if (IsValidDamagable(mobiles[k]) && mobiles[k] != m_Player)
if (IsValidDamagable(m) && m != m_Player)
{
PlayEffect(m_Player, mobiles[k], Rock(), 8, true);
DoDamage(mobiles[k], 25, 30, false);
if (mobiles[k].Player)
{
POHMessage(mobiles[k], 2); // OUCH!
}
queue.Enqueue(m);
}
}
PlayEffect(m_Player, m_IEntity, Rock(), 8, false);
while (queue.Count > 0)
{
var m = queue.Dequeue();
PlayEffect(m_Player, m, Rock(), 8, true);
DoDamage(m, 25, 30, false);
if (m.Player)
{
POHMessage(m, 2); // OUCH!
}
}
PlayEffect(m_Player, entity, Rock(), 8, false);
}
}
}

View file

@ -1,5 +1,5 @@
using System;
using System.Collections.Generic;
using Server.Collections;
using Server.Factions;
using Server.Spells;
using Server.Targeting;
@ -90,20 +90,21 @@ namespace Server
private static void OnHit(Mobile from, Point3D origin, Map facet)
{
var eable = facet.GetMobilesInRange(origin, 12);
var targets = new List<Mobile>();
foreach (var m in eable)
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in facet.GetMobilesInRange(origin, 12))
{
if (from.CanBeHarmful(m, false) &&
m.InLOS(new Point3D(origin.X, origin.Y, origin.Z + 1)) &&
Faction.Find(m) != null)
{
targets.Add(from);
queue.Enqueue(from);
}
}
foreach (var mob in targets)
while (queue.Count > 0)
{
var mob = queue.Dequeue();
var damage = mob.Hits * 6 / 10;
if (!mob.Player && damage < 10)

View file

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using ModernUO.Serialization;
using Server.Collections;
using Server.Targeting;
namespace Server.Items;
@ -343,28 +344,13 @@ public abstract partial class BaseDoor : Item, ILockable, ITelekinesisable
}
}
var mobs = map.GetSector(x, y).Mobiles;
for (var i = 0; i < mobs.Count; ++i)
foreach (var m in map.GetMobilesAt(x, y))
{
var m = mobs[i];
if (m.Location.X == x && m.Location.Y == y)
// At the same location, not hidden, or is a player, alive, and within z-bounds - then cannot fit
if (m.Location.X == x && m.Location.Y == y &&
(!m.Hidden || m.AccessLevel == AccessLevel.Player) && m.Alive && m.Z + 16 > z && z + height > m.Z)
{
if (m.Hidden && m.AccessLevel > AccessLevel.Player)
{
continue;
}
if (!m.Alive)
{
continue;
}
if (m.Z + 16 > z && z + height > m.Z)
{
return false;
}
return false;
}
}

View file

@ -64,7 +64,7 @@ public partial class Acid : Item
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in GetMobilesInRange(0))
foreach (var m in GetMobilesAt())
{
if (m.AccessLevel == AccessLevel.Player &&
m.Alive && !m.IsDeadBondedPet && (m is not BaseCreature bc || bc.Controlled || bc.Summoned))

View file

@ -122,9 +122,8 @@ public partial class Firebomb : Item
}
else if (RootParent == null)
{
var eable = Map.GetMobilesInRange(Location, 1);
using var targets = PooledRefQueue<Mobile>.Create();
foreach (var m in eable)
foreach (var m in Map.GetMobilesInRange(Location, 1))
{
if (m_LitBy == null || SpellHelper.ValidIndirectTarget(m_LitBy, m) &&
m_LitBy.CanBeHarmful(m, false))

View file

@ -78,8 +78,7 @@ public partial class MorphItem : Item
public void Refresh()
{
var found = false;
var eable = GetMobilesInRange(CurrentRange);
foreach (var mob in eable)
foreach (var mob in GetMobilesInRange(CurrentRange))
{
if (!mob.Hidden || mob.AccessLevel <= AccessLevel.Player)
{

View file

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using ModernUO.Serialization;
using Server.Collections;
using Server.Spells;
using Server.Targeting;
@ -275,17 +276,24 @@ public abstract partial class BaseConflagrationPotion : BasePotion
return;
}
foreach (var m in _item.GetMobilesInRange(0))
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in _item.GetMobilesAt())
{
if (m.Z + 16 > _item.Z && _item.Z + 12 > m.Z && (!Core.AOS || m != from) &&
SpellHelper.ValidIndirectTarget(from, m) && from.CanBeHarmful(m, false))
{
from.DoHarmful(m);
AOS.Damage(m, from, _item.GetDamage(), 0, 100, 0, 0, 0);
m.PlaySound(0x208);
queue.Enqueue(m);
}
}
while (queue.Count > 0)
{
var m = queue.Dequeue();
from.DoHarmful(m);
AOS.Damage(m, from, _item.GetDamage(), 0, 100, 0, 0, 0);
m.PlaySound(0x208);
}
}
}
}

View file

@ -82,13 +82,8 @@ public abstract partial class BaseConfusionBlastPotion : BasePotion
foreach (var mobile in map.GetMobilesInRange(loc, Radius))
{
if (mobile is BaseCreature mon)
if (mobile is BaseCreature { Controlled: false, Summoned: false } mon)
{
if (mon.Controlled || mon.Summoned)
{
continue;
}
mon.Pacify(from, Core.Now + TimeSpan.FromSeconds(5.0)); // TODO check
}
}

View file

@ -98,10 +98,9 @@ public partial class FireHorn : Item
);
var playerVsPlayer = false;
var eable = from.Map.GetMobilesInRange(loc, 2);
using var targets = PooledRefQueue<Mobile>.Create();
foreach (var m in eable)
foreach (var m in from.Map.GetMobilesInRange(loc, 2))
{
if (from != m && SpellHelper.ValidIndirectTarget(from, m) && from.CanBeHarmful(m, false) &&
(!Core.AOS || from.InLOS(m)))

View file

@ -90,6 +90,8 @@ public partial class FlameSpurtTrap : BaseTrap
if (_spurt?.Deleted != false)
{
_spurt = new Static(0x3709);
// This MoveToWorld is generally not safe in a range loop, except we return after this on L100
_spurt.MoveToWorld(Location, Map);
Effects.PlaySound(GetWorldLocation(), Map, 0x309);

View file

@ -1,5 +1,6 @@
using System;
using ModernUO.Serialization;
using Server.Collections;
using Server.Spells;
namespace Server.Items;
@ -92,14 +93,21 @@ public partial class SpikeTrap : BaseTrap
Effects.SendLocationEffect(Location, Map, GetBaseID(Type) + 1, 18, 3, GetEffectHue());
Effects.PlaySound(Location, Map, 0x22C);
foreach (var mob in GetMobilesInRange(0))
var queue = PooledRefQueue<Mobile>.Create();
foreach (var mob in GetMobilesAt())
{
if (mob.Alive && !mob.IsDeadBondedPet)
if (mob.Alive && !mob.IsDeadBondedPet && mob.AccessLevel == AccessLevel.Player)
{
SpellHelper.Damage(TimeSpan.FromTicks(1), mob, mob, Utility.RandomMinMax(1, 6) * 6);
queue.Enqueue(mob);
}
}
while (queue.Count > 0)
{
var mob = queue.Dequeue();
SpellHelper.Damage(TimeSpan.FromTicks(1), mob, mob, Utility.RandomMinMax(1, 6) * 6);
}
Timer.StartTimer(TimeSpan.FromSeconds(1.0), OnSpikeExtended);
from.LocalOverheadMessage(MessageType.Regular, 0x22, 500852); // You stepped onto a spike trap!

View file

@ -1,5 +1,6 @@
using System;
using ModernUO.Serialization;
using Server.Collections;
using Server.Spells;
namespace Server.Items;
@ -94,13 +95,20 @@ public partial class StoneFaceTrap : BaseTrap
public virtual void TriggerDamage()
{
var queue = PooledRefQueue<Mobile>.Create();
foreach (var mob in GetMobilesInRange(1))
{
if (mob.Alive && !mob.IsDeadBondedPet && mob.AccessLevel == AccessLevel.Player)
{
SpellHelper.Damage(TimeSpan.FromTicks(1), mob, mob, Utility.Dice(3, 15, 0));
queue.Enqueue(mob);
}
}
while (queue.Count > 0)
{
var mob = queue.Dequeue();
SpellHelper.Damage(TimeSpan.FromTicks(1), mob, mob, Utility.Dice(3, 15, 0));
}
}
[AfterDeserialization]

View file

@ -39,8 +39,7 @@ namespace Server.Items
bool didEffect = false;
var eable = attacker.GetMobilesInRange(1);
foreach (var m in eable)
foreach (var m in attacker.GetMobilesInRange(1))
{
if (m?.Deleted == false && m != defender && m != attacker &&
SpellHelper.ValidIndirectTarget(attacker, m) &&

View file

@ -36,10 +36,9 @@ namespace Server.Items
attacker.FixedEffect(0x3728, 10, 15);
attacker.PlaySound(0x2A1);
var eable = attacker.GetMobilesInRange(1);
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in eable)
foreach (var m in attacker.GetMobilesInRange(1))
{
if (m?.Deleted == false && m != defender && m != attacker &&
m.Map == attacker.Map && m.Alive &&

View file

@ -1718,9 +1718,8 @@ public abstract partial class BaseWeapon : Item, IWeapon, IFactionItem, ICraftab
return 0;
}
var eable = defender.GetMobilesInRange<BaseCreature>(1);
var inPack = 1;
foreach (var m in eable)
foreach (var m in defender.GetMobilesInRange<BaseCreature>(1))
{
if (m != attacker && (m.PackInstinct & bc.PackInstinct) != 0 && (m.Controlled || m.Summoned) &&
master == (m.ControlMaster ?? m.SummonMaster) && m.Combatant == defender)
@ -1743,8 +1742,7 @@ public abstract partial class BaseWeapon : Item, IWeapon, IFactionItem, ICraftab
{
if (MirrorImage.HasClone(defender) && defender.Skills.Ninjitsu.Value / 150.0 > Utility.RandomDouble())
{
var eable = defender.GetMobilesInRange<Clone>(4);
foreach (var m in eable)
foreach (var m in defender.GetMobilesInRange<Clone>(4))
{
if (m?.Summoned == true && m.SummonMaster == defender)
{
@ -3570,11 +3568,8 @@ public abstract partial class BaseWeapon : Item, IWeapon, IFactionItem, ICraftab
return;
}
var range = Core.ML ? 5 : 10;
var eable = from.GetMobilesInRange(range);
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in eable)
foreach (var m in from.GetMobilesInRange(Core.ML ? 5 : 10))
{
if (from != m && defender != m && SpellHelper.ValidIndirectTarget(from, m)
&& from.CanBeHarmful(m, false) && (!Core.ML || from.InLOS(m)))

View file

@ -2604,9 +2604,7 @@ public abstract class BaseAI
Mobile enemySummonMob = null;
var enemySummonVal = double.MinValue;
var eable = map.GetMobilesInRange(m_Mobile.Location, iRange);
foreach (var m in eable)
foreach (var m in map.GetMobilesInRange(m_Mobile.Location, iRange))
{
if (m.Deleted || m.Blessed)
{
@ -2808,9 +2806,7 @@ public abstract class BaseAI
return;
}
var eable = m_Mobile.GetMobilesInRange(m_Mobile.RangePerception);
foreach (var trg in eable)
foreach (var trg in m_Mobile.GetMobilesInRange(m_Mobile.RangePerception))
{
if (trg != m_Mobile && trg.Player && trg.Alive && trg.Hidden && trg.AccessLevel == AccessLevel.Player &&
m_Mobile.InLOS(trg))

View file

@ -151,21 +151,21 @@ public class HealerAI : BaseAI
foreach (var m in m_Mobile.GetMobilesInRange(m_Mobile.RangePerception))
{
if (!m_Mobile.CanSee(m) || !(m is BaseCreature) || ((BaseCreature)m).Team != m_Mobile.Team)
if (!m_Mobile.CanSee(m) || m is not BaseCreature bc || bc.Team != m_Mobile.Team)
{
continue;
}
for (var i = 0; i < funcs.Length; ++i)
{
if (funcs[i](m))
if (funcs[i](bc))
{
var val = -m_Mobile.GetDistanceToSqrt(m);
var val = -m_Mobile.GetDistanceToSqrt(bc);
if (found == null || val > prio)
{
prio = val;
found = m;
found = bc;
}
break;

View file

@ -8,9 +8,8 @@ public abstract class AreaEffectMonsterAbility : MonsterAbility
public override void Trigger(MonsterAbilityTrigger trigger, BaseCreature source, Mobile target)
{
var eable = source.GetMobilesInRange(AreaRange);
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in eable)
foreach (var m in source.GetMobilesInRange(AreaRange))
{
if (CanEffectTarget(source, m))
{

View file

@ -3800,8 +3800,7 @@ namespace Server.Mobiles
}
using var queue = PooledRefQueue<Mobile>.Create();
var eable = master.GetMobilesInRange(3);
foreach (var m in eable)
foreach (var m in master.GetMobilesInRange(3))
{
if (m is BaseCreature
{ Controlled: true, ControlOrder: OrderType.Guard or OrderType.Follow or OrderType.Come } pet &&
@ -5346,9 +5345,8 @@ namespace Server.Mobiles
return;
}
var eable = GetMobilesInRange(AuraRange);
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in eable)
foreach (var m in GetMobilesInRange(AuraRange))
{
if (m != this && CanBeHarmful(m, false) && (Core.AOS || InLOS(m)) &&
(m is BaseCreature bc && (bc.Controlled || bc.Summoned || bc.Team != Team) || m.Player))

View file

@ -1,6 +1,5 @@
using System;
using ModernUO.Serialization;
using Server.Collections;
namespace Server.Mobiles;
@ -68,19 +67,13 @@ public partial class ShadowWispFamiliar : BaseFamiliar
return;
}
var eable = GetMobilesInRange(5);
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in eable)
foreach (var m in GetMobilesInRange(5))
{
if (m.Player && m.Alive && !m.IsDeadBondedPet && m.Karma <= 0 && m.AccessLevel < AccessLevel.Counselor)
if (!m.Player || !m.Alive || m.IsDeadBondedPet || m.Karma > 0 || m.AccessLevel >= AccessLevel.Counselor)
{
queue.Enqueue(m);
continue;
}
}
while (queue.Count > 0)
{
var m = queue.Dequeue();
var friendly = true;
for (var j = 0; friendly && j < caster.Aggressors.Count; ++j)

View file

@ -1,6 +1,7 @@
using ModernUO.Serialization;
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using Server.Collections;
using Server.Items;
using Server.Spells;
@ -131,13 +132,13 @@ namespace Server.Mobiles
return;
}
var list = new List<SavageShaman>();
using var queue = PooledRefQueue<BaseCreature>.Create();
foreach (var m in GetMobilesInRange(8))
{
if (m != this && m is SavageShaman ss)
{
list.Add(ss);
queue.Enqueue(ss);
}
}
@ -148,22 +149,104 @@ namespace Server.Mobiles
AIObject.NextMove = Core.TickCount + 1000;
}
if (list.Count >= 3)
if (queue.Count < 3)
{
for (var i = 0; i < list.Count; ++i)
{
var dancer = list[i];
dancer.Animate(111, 5, 1, true, false, 0); // Get down tonight...
if (dancer.AIObject != null)
{
dancer.AIObject.NextMove = Core.TickCount + 1000;
}
}
Timer.StartTimer(TimeSpan.FromSeconds(1.0), EndSavageDance);
return;
}
while (queue.Count > 0)
{
var dancer = queue.Dequeue();
dancer.Animate(111, 5, 1, true, false, 0); // Get down tonight...
if (dancer.AIObject != null)
{
dancer.AIObject.NextMove = Core.TickCount + 1000;
}
}
Timer.StartTimer(TimeSpan.FromSeconds(1.0), EndSavageDance);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool CanDoGreaterHeal(Mobile m, bool isFriendly) =>
isFriendly && !m.Poisoned && !MortalStrike.IsWounded(m) && CanBeBeneficial(m);
private void DoGreaterHeal(Mobile m)
{
DoBeneficial(m);
// Algorithm: (40% of magery) + (1-10)
var toHeal = (int)(Skills.Magery.Value * 0.4);
toHeal += Utility.Random(1, 10);
m.Heal(toHeal, this);
m.FixedParticles(0x376A, 9, 32, 5030, EffectLayer.Waist);
m.PlaySound(0x202);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool CanDoLightning(Mobile m, bool isFriendly) =>
!isFriendly && CanBeHarmful(m) && (!m.Hidden || m.AccessLevel == AccessLevel.Player);
private void DoLightning(Mobile m)
{
DoHarmful(m);
double damage;
if (Core.AOS)
{
var baseDamage = 6 + (int)(Skills.EvalInt.Value / 5.0);
damage = Utility.RandomMinMax(baseDamage, baseDamage + 3);
}
else
{
damage = Utility.Random(12, 9);
}
m.BoltEffect(0);
SpellHelper.Damage(TimeSpan.FromSeconds(0.25), m, this, damage, 0, 0, 0, 0, 100);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool CanDoPoison(Mobile m, bool isFriendly) =>
!isFriendly && CanBeHarmful(m) && (!m.Hidden || m.AccessLevel == AccessLevel.Player);
private void DoPoison(Mobile m)
{
DoHarmful(m);
m.Spell?.OnCasterHurt();
m.Paralyzed = false;
var total = Skills.Magery.Value + Skills.Poisoning.Value;
var dist = GetDistanceToSqrt(m);
if (dist >= 3.0)
{
total -= (dist - 3.0) * 10.0;
}
int level = total switch
{
>= 200.0 => Utility.Random(10) == 0 ? 3 : 2,
> 170.0 => 2,
> 130.0 => 1,
_ => 0
};
m.ApplyPoison(this, Poison.GetPoison(level));
m.FixedParticles(0x374A, 10, 15, 5021, EffectLayer.Waist);
m.PlaySound(0x474);
}
public void EndSavageDance()
@ -173,137 +256,47 @@ namespace Server.Mobiles
return;
}
var eable = GetMobilesInRange(8);
var rnd = Utility.Random(3);
switch (Utility.Random(3))
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in GetMobilesInRange(8))
{
case 0: /* greater heal */
{
foreach (var m in eable)
var isFriendly = m is Savage or SavageRider or SavageShaman or SavageRidgeback;
var shouldApply = rnd switch
{
0 => CanDoGreaterHeal(m, isFriendly),
1 => CanDoLightning(m, isFriendly),
2 => CanDoPoison(m, isFriendly),
};
if (shouldApply)
{
queue.Enqueue(m);
}
}
while (queue.Count > 0)
{
var m = queue.Dequeue();
switch (rnd)
{
case 0:
{
var isFriendly = m is Savage or SavageRider or SavageShaman or SavageRidgeback;
if (!isFriendly)
{
continue;
}
if (m.Poisoned || MortalStrike.IsWounded(m) || !CanBeBeneficial(m))
{
continue;
}
DoBeneficial(m);
// Algorithm: (40% of magery) + (1-10)
var toHeal = (int)(Skills.Magery.Value * 0.4);
toHeal += Utility.Random(1, 10);
m.Heal(toHeal, this);
m.FixedParticles(0x376A, 9, 32, 5030, EffectLayer.Waist);
m.PlaySound(0x202);
DoGreaterHeal(m);
break;
}
break;
}
case 1: /* lightning */
{
foreach (var m in eable)
case 1:
{
var isFriendly = m is Savage or SavageRider or SavageShaman or SavageRidgeback;
if (isFriendly)
{
continue;
}
if (!CanBeHarmful(m))
{
continue;
}
DoHarmful(m);
double damage;
if (Core.AOS)
{
var baseDamage = 6 + (int)(Skills.EvalInt.Value / 5.0);
damage = Utility.RandomMinMax(baseDamage, baseDamage + 3);
}
else
{
damage = Utility.Random(12, 9);
}
m.BoltEffect(0);
SpellHelper.Damage(TimeSpan.FromSeconds(0.25), m, this, damage, 0, 0, 0, 0, 100);
DoLightning(m);
break;
}
break;
}
case 2: /* poison */
{
foreach (var m in eable)
case 2:
{
var isFriendly = m is Savage or SavageRider or SavageShaman or SavageRidgeback;
if (isFriendly)
{
continue;
}
if (!CanBeHarmful(m))
{
continue;
}
DoHarmful(m);
m.Spell?.OnCasterHurt();
m.Paralyzed = false;
var total = Skills.Magery.Value + Skills.Poisoning.Value;
var dist = GetDistanceToSqrt(m);
if (dist >= 3.0)
{
total -= (dist - 3.0) * 10.0;
}
int level;
if (total >= 200.0 && Utility.Random(1, 100) <= 10)
{
level = 3;
}
else if (total > 170.0)
{
level = 2;
}
else if (total > 130.0)
{
level = 1;
}
else
{
level = 0;
}
m.ApplyPoison(this, Poison.GetPoison(level));
m.FixedParticles(0x374A, 10, 15, 5021, EffectLayer.Waist);
m.PlaySound(0x474);
DoPoison(m);
break;
}
break;
}
}
}
}
}

View file

@ -107,23 +107,19 @@ namespace Server.Mobiles
return;
}
var eable = GetMobilesInRange<OrcishLord>(10);
var count = 0;
foreach (var m in eable)
foreach (var m in GetMobilesInRange<OrcishLord>(10))
{
if (++count == 10)
{
break;
BaseCreature orc = new SpawnedOrcishLord { Team = Team };
// This MoveToWorld is safe since we return after executing and do not keep looping.
orc.MoveToWorld(map.GetRandomNearbyLocation(target.Location), map);
orc.Combatant = target;
return;
}
}
if (count < 10)
{
BaseCreature orc = new SpawnedOrcishLord { Team = Team };
orc.MoveToWorld(map.GetRandomNearbyLocation(target.Location), map);
orc.Combatant = target;
}
}
}
}

View file

@ -118,9 +118,7 @@ namespace Server.Mobiles
{
m_NextAbilityTime = Core.Now + TimeSpan.FromSeconds(Utility.RandomMinMax(10, 15));
var eable = GetMobilesInRange(8);
foreach (var m in eable)
foreach (var m in GetMobilesInRange(8))
{
if (m is not MeerWarrior || !IsFriend(m) || !CanBeBeneficial(m) || m.Hits >= m.HitsMax || m.Poisoned ||
MortalStrike.IsWounded(m))

View file

@ -81,9 +81,8 @@ namespace Server.Mobiles
private void DoAreaLeech_Finish()
{
var eable = GetMobilesInRange(6);
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in eable)
foreach (var m in GetMobilesInRange(6))
{
if (CanBeHarmful(m) && IsEnemy(m))
{

View file

@ -122,16 +122,15 @@ namespace Server.Mobiles
UndressItem(m, Layer.Pants);
UndressItem(m, Layer.Shirt);
m.SendLocalizedMessage(
1072197
); // The dryad's beauty makes your blood race. Your clothing is too confining.
// The dryad's beauty makes your blood race. Your clothing is too confining.
m.SendLocalizedMessage(1072197);
}
}
m_NextUndress = Core.Now + TimeSpan.FromMinutes(1);
}
public void UndressItem(Mobile m, Layer layer)
public static void UndressItem(Mobile m, Layer layer)
{
var item = m.FindItemOnLayer(layer);

View file

@ -1,6 +1,7 @@
using ModernUO.Serialization;
using System;
using System.Collections.Generic;
using Server.Collections;
using Server.Engines.CannedEvil;
using Server.Items;
using Server.Network;
@ -311,31 +312,19 @@ namespace Server.Mobiles
private void OnTick()
{
var toDamage = new List<Mobile>();
foreach (var m in GetMobilesInRange(0))
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in GetMobilesAt())
{
if (m is BaseCreature bc)
if ((m.Player || m is BaseCreature bc && (bc.Controlled || bc.Summoned))
&& m.Alive && !m.IsDeadBondedPet && m.CanBeDamaged())
{
if (!bc.Controlled && !bc.Summoned)
{
continue;
}
}
else if (!m.Player)
{
continue;
}
if (m.Alive && !m.IsDeadBondedPet && m.CanBeDamaged())
{
toDamage.Add(m);
queue.Enqueue(m);
}
}
for (var i = 0; i < toDamage.Count; ++i)
while (queue.Count > 0)
{
Damage(toDamage[i]);
Damage(queue.Dequeue());
}
++m_Ticks;

View file

@ -2,6 +2,7 @@ using System;
using Server.Engines.CannedEvil;
using Server.Items;
using ModernUO.Serialization;
using Server.Collections;
namespace Server.Mobiles;
@ -214,30 +215,30 @@ public partial class Meraktus : BaseChampion
public void Earthquake()
{
var eable = GetMobilesInRange(8);
foreach (var m in eable)
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in GetMobilesInRange(8))
{
if (m == this || !CanBeHarmful(m) || m.Deleted || !m.Player &&
!(m is BaseCreature creature && (creature.Controlled || creature.Summoned || creature.Team != Team)))
if (m.Deleted || m == this || !CanBeHarmful(m))
{
continue;
}
if (m.Player || m is BaseCreature bc && (bc.Controlled || bc.Summoned || bc.Team != Team))
{
queue.Enqueue(m);
}
}
while (queue.Count > 0)
{
var m = queue.Dequeue();
if (m is PlayerMobile pm && pm.Mounted)
{
pm.Mount.Rider = null;
}
var damage = (int)(m.Hits * 0.6);
if (damage < 10)
{
damage = 10;
}
else if (damage > 75)
{
damage = 75;
}
var damage = Math.Clamp((int)(m.Hits * 0.6), 10, 75);
DoHarmful(m);
AOS.Damage(m, this, damage, 100, 0, 0, 0, 0);

View file

@ -1,6 +1,5 @@
using ModernUO.Serialization;
using System;
using Server.Buffers;
using Server.Collections;
namespace Server.Mobiles
@ -69,28 +68,24 @@ namespace Server.Mobiles
{
if (Core.SE && Summoned)
{
var eable = GetMobilesInRange(5);
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in eable)
using var list = PooledRefList<Mobile>.Create();
foreach (var m in GetMobilesInRange(5))
{
if (m is EnergyVortex or BladeSpirits && ((BaseCreature)m).Summoned)
{
queue.Enqueue(m);
list.Add(m);
}
}
var amount = queue.Count - 6;
var amount = list.Count - 6;
if (amount > 0)
{
var mobs = queue.ToPooledArray();
mobs.Shuffle();
list.Shuffle();
while (amount > 0)
{
Dispel(mobs[amount--]);
Dispel(list[amount--]);
}
STArrayPool<Mobile>.Shared.Return(mobs, true);
}
}

View file

@ -1,6 +1,5 @@
using ModernUO.Serialization;
using System;
using Server.Buffers;
using Server.Collections;
namespace Server.Mobiles
@ -75,28 +74,24 @@ namespace Server.Mobiles
{
if (Core.SE && Summoned)
{
var eable = GetMobilesInRange(5);
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in eable)
using var list = PooledRefList<Mobile>.Create();
foreach (var m in GetMobilesInRange(5))
{
if (m is EnergyVortex or BladeSpirits && ((BaseCreature)m).Summoned)
{
queue.Enqueue(m);
list.Add(m);
}
}
var amount = queue.Count - 6;
var amount = list.Count - 6;
if (amount > 0)
{
var mobs = queue.ToPooledArray();
mobs.Shuffle();
list.Shuffle();
while (amount > 0)
{
Dispel(mobs[amount--]);
Dispel(list[amount--]);
}
STArrayPool<Mobile>.Shared.Return(mobs, true);
}
}

View file

@ -1,4 +1,5 @@
using ModernUO.Serialization;
using Server.Collections;
using Server.Engines.Plants;
using Server.Items;
@ -81,24 +82,33 @@ namespace Server.Mobiles
public void EatBoglings()
{
var eable = GetMobilesInRange<Bogling>(2);
var sound = true;
if (Hits >= HitsMax)
{
return;
}
foreach (var bogling in eable)
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var bogling in GetMobilesInRange<Bogling>(2))
{
if (Hits >= HitsMax)
{
break;
}
if (sound)
{
PlaySound(Utility.Random(0x3B, 2)); // Eat sound
sound = false;
}
Hits += bogling.Hits / 2;
bogling.Delete();
queue.Enqueue(bogling);
}
if (queue.Count <= 0)
{
return;
}
PlaySound(Utility.Random(0x3B, 2)); // Eat sound
while (queue.Count > 0)
{
queue.Dequeue().Delete();
}
}

View file

@ -1,4 +1,5 @@
using ModernUO.Serialization;
using Server.Collections;
using Server.Engines.Plants;
using Server.Items;
@ -120,9 +121,8 @@ namespace Server.Mobiles
Animate(10, 4, 1, true, false, 0);
var eable = target.GetMobilesInRange<Mobile>(8);
foreach (var m in eable)
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in target.GetMobilesInRange<Mobile>(8))
{
if (m == this || !(CanBeHarmful(m) || m.Player && m.Alive))
{
@ -134,6 +134,12 @@ namespace Server.Mobiles
continue;
}
queue.Enqueue(m);
}
while (queue.Count > 0)
{
var m = queue.Dequeue();
DoHarmful(m);
AOS.Damage(m, this, Utility.RandomMinMax(20, 25), true, 0, 0, 0, 100, 0);

View file

@ -856,9 +856,7 @@ namespace Server.Mobiles
if (Core.AOS)
{
var mobiles = Map.GetMobilesInRange(location, 0);
foreach (Mobile m in mobiles)
foreach (Mobile m in Map.GetMobilesAt(location))
{
if (m.Z >= location.Z && m.Z < location.Z + 16 && (!m.Hidden || m.AccessLevel == AccessLevel.Player))
{

View file

@ -138,18 +138,13 @@ public partial class Barracoon : BaseChampion
return;
}
var eable = GetMobilesInRange<BaseCreature>(10);
var rats = 0;
foreach (var m in eable)
foreach (var m in GetMobilesInRange<BaseCreature>(10))
{
if (m is Ratman or RatmanArcher or RatmanMage)
if (m is Ratman or RatmanArcher or RatmanMage && ++rats >= 16)
{
rats++;
if (rats >= 16)
{
return;
}
return;
}
}

View file

@ -529,9 +529,8 @@ public partial class Harrower : BaseCreature
return;
}
var eable = m_Owner.GetMobilesInRange(16);
Mobile toTeleport = null;
foreach (var m in eable)
foreach (var m in m_Owner.GetMobilesInRange(16))
{
if (m != m_Owner && m.Player && m_Owner.CanBeHarmful(m) && m_Owner.CanSee(m))
{

View file

@ -1,5 +1,6 @@
using System;
using ModernUO.Serialization;
using Server.Collections;
namespace Server.Mobiles;
@ -119,9 +120,8 @@ public partial class HarrowerTentacles : BaseCreature
return;
}
var eable = m_Owner.GetMobilesInRange<Mobile>(9);
foreach (var m in eable)
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in m_Owner.GetMobilesInRange<Mobile>(9))
{
if (m == m_Owner || !m_Owner.CanBeHarmful(m))
{
@ -138,6 +138,12 @@ public partial class HarrowerTentacles : BaseCreature
continue;
}
queue.Enqueue(m);
}
while (queue.Count > 0)
{
var m = queue.Dequeue();
m_Owner.DoHarmful(m);
m.FixedParticles(0x374A, 10, 15, 5013, 0x455, 0, EffectLayer.Waist);

View file

@ -1,5 +1,6 @@
using System;
using ModernUO.Serialization;
using Server.Collections;
using Server.Engines.CannedEvil;
using Server.Items;
@ -98,9 +99,8 @@ public partial class Rikktor : BaseChampion
PlaySound(0x2F3);
var eable = GetMobilesInRange<Mobile>(8);
foreach (var m in eable)
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in GetMobilesInRange<Mobile>(8))
{
if (m == this || !(CanBeHarmful(m) || m.Player && m.Alive))
{
@ -112,20 +112,18 @@ public partial class Rikktor : BaseChampion
continue;
}
var damage = m.Hits * 0.6;
queue.Enqueue(m);
}
if (damage < 10.0)
{
damage = 10.0;
}
else if (damage > 75.0)
{
damage = 75.0;
}
while (queue.Count > 0)
{
var m = queue.Dequeue();
var damage = Math.Clamp((int)(m.Hits * 0.6), 10, 75);
DoHarmful(m);
AOS.Damage(m, this, (int)damage, 100, 0, 0, 0, 0);
AOS.Damage(m, this, damage, 100, 0, 0, 0, 0);
if (m.Alive && m.Body.IsHuman && !m.Mounted)
{

View file

@ -1,5 +1,6 @@
using System;
using ModernUO.Serialization;
using Server.Collections;
using Server.Engines.CannedEvil;
using Server.Engines.Plants;
using Server.Items;
@ -152,9 +153,8 @@ public partial class Serado : BaseChampion
Animate(10, 4, 1, true, false, 0);
var eable = target.GetMobilesInRange<Mobile>(8);
foreach (var m in eable)
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in target.GetMobilesInRange<Mobile>(8))
{
if (m == this || !(CanBeHarmful(m) || m.Player && m.Alive))
{
@ -166,6 +166,12 @@ public partial class Serado : BaseChampion
continue;
}
queue.Enqueue(m);
}
while (queue.Count > 0)
{
var m = queue.Dequeue();
DoHarmful(m);
AOS.Damage(m, this, Utility.RandomMinMax(20, 25), true, 0, 0, 0, 100, 0);

View file

@ -1268,9 +1268,7 @@ namespace Server.Mobiles
}
else
{
var mobiles = e.Mobile.GetMobilesInRange<PlayerVendor>(2);
foreach (var m in mobiles)
foreach (var m in e.Mobile.GetMobilesInRange<PlayerVendor>(2))
{
if (m.CanSee(e.Mobile) && m.InLOS(e.Mobile))
{

View file

@ -151,12 +151,8 @@ namespace Server.Multis
mobiles.Clear();
var sector = map.GetSector(tileX, tileY);
for (var i = 0; i < sector.Mobiles.Count; ++i)
foreach (var m in map.GetMobilesAt(tileX, tileY))
{
var m = sector.Mobiles[i];
if (m.X == tileX && m.Y == tileY)
{
mobiles.Add(m);

View file

@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json.Serialization;
using Server.Mobiles;
using Server.Utilities;
@ -153,8 +152,15 @@ public class GuardedRegion : BaseRegion
public override void MakeGuard(Mobile focus)
{
var eable = focus.GetMobilesInRange<BaseGuard>(8);
var useGuard = eable.FirstOrDefault(m => m.Focus == null);
BaseGuard useGuard = null;
foreach (var m in focus.GetMobilesInRange<BaseGuard>(8))
{
if (m.Focus == null)
{
useGuard = m;
break;
}
}
if (useGuard == null)
{
@ -286,19 +292,8 @@ public class GuardedRegion : BaseRegion
if (fakeCall != null)
{
fakeCall.Say(
Utility.RandomList(
1007037,
501603,
1013037,
1013038,
1013039,
1013041,
1013042,
1013043,
1013052
)
);
fakeCall.Say(Utility.Random(1013037, 16));
MakeGuard(m);
timer.Stop();
m_GuardCandidates.Remove(m);
@ -319,9 +314,7 @@ public class GuardedRegion : BaseRegion
return;
}
var eable = Map.GetMobilesInRange(p, 14);
foreach (var m in eable)
foreach (var m in Map.GetMobilesInRange(p, 14))
{
if (IsGuardCandidate(m) &&
(!AllowReds && m.Kills >= 5 && m.Region.IsPartOf(this) || m_GuardCandidates.ContainsKey(m)))

View file

@ -58,27 +58,29 @@ namespace Server.SkillHandlers
if (range > 0)
{
var inRange = src.Map.GetMobilesInRange(p, range);
foreach (var trg in inRange)
foreach (var trg in src.Map.GetMobilesInRange(p, range))
{
if (trg.Hidden && src != trg)
if (!trg.Hidden || src == trg)
{
var ss = srcSkill + Utility.Random(21) - 10;
var ts = trg.Skills.Hiding.Value + Utility.Random(21) - 10;
if (src.AccessLevel >= trg.AccessLevel && (ss >= ts || inHouse && house.IsInside(trg)))
{
if (trg is ShadowKnight && (trg.X != p.X || trg.Y != p.Y))
{
continue;
}
trg.RevealingAction();
trg.SendLocalizedMessage(500814); // You have been revealed!
foundAnyone = true;
}
continue;
}
var ss = srcSkill + Utility.Random(21) - 10;
var ts = trg.Skills.Hiding.Value + Utility.Random(21) - 10;
if (src.AccessLevel < trg.AccessLevel || ss < ts && (!inHouse || !house.IsInside(trg)))
{
continue;
}
if (trg is ShadowKnight && (trg.X != p.X || trg.Y != p.Y))
{
continue;
}
trg.RevealingAction();
trg.SendLocalizedMessage(500814); // You have been revealed!
foundAnyone = true;
}
if (Faction.Find(src) != null)

View file

@ -60,8 +60,7 @@ namespace Server.SkillHandlers
{
if (!CombatOverride)
{
var eable = m.GetMobilesInRange(range);
foreach (var check in eable)
foreach (var check in m.GetMobilesInRange(range))
{
if (check.InLOS(m) && check.Combatant == m)
{

View file

@ -118,9 +118,8 @@ namespace Server.SkillHandlers
calmed = true;
m.SendLocalizedMessage(
500616
); // You hear lovely music, and forget to continue battling!
// You hear lovely music, and forget to continue battling!
m.SendLocalizedMessage(500616);
m.Combatant = null;
m.Warmode = false;
@ -132,9 +131,8 @@ namespace Server.SkillHandlers
if (!calmed)
{
from.SendLocalizedMessage(
1049648
); // You play hypnotic music, but there is nothing in range for you to calm.
// You play hypnotic music, but there is nothing in range for you to calm.
from.SendLocalizedMessage(1049648);
}
else
{
@ -216,9 +214,8 @@ namespace Server.SkillHandlers
{
from.SendLocalizedMessage(1049532); // You play hypnotic music, calming your target.
targ.SendLocalizedMessage(
500616
); // You hear lovely music, and forget to continue battling!
// You hear lovely music, and forget to continue battling!
targ.SendLocalizedMessage(500616);
}
}
}

View file

@ -210,20 +210,17 @@ namespace Server.SkillHandlers
from.SendGump(new TrackWhoGump(from, mobs, range));
from.SendLocalizedMessage(1018093); // Select the one you would like to track.
}
else if (type == 0)
{
from.SendLocalizedMessage(502991); // You see no evidence of animals in the area.
}
else if (type == 1)
{
from.SendLocalizedMessage(502993); // You see no evidence of creatures in the area.
}
else
{
if (type == 0)
{
from.SendLocalizedMessage(502991); // You see no evidence of animals in the area.
}
else if (type == 1)
{
from.SendLocalizedMessage(502993); // You see no evidence of creatures in the area.
}
else
{
from.SendLocalizedMessage(502995); // You see no evidence of people in the area.
}
from.SendLocalizedMessage(502995); // You see no evidence of people in the area.
}
}

View file

@ -21,9 +21,8 @@ namespace Server.Spells.Bushido
var weapon = attacker.Weapon;
var eable = attacker.GetMobilesInRange(weapon.MaxRange);
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in eable)
foreach (var m in attacker.GetMobilesInRange(weapon.MaxRange))
{
if (m != defender && m.Combatant == attacker)
{

View file

@ -46,9 +46,8 @@ namespace Server.Spells.Chivalry
var chiv = Caster.Skills.Chivalry.Value;
var eable = Caster.GetMobilesInRange(8);
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in eable)
foreach (var m in Caster.GetMobilesInRange(8))
{
if (Caster != m && SpellHelper.ValidIndirectTarget(Caster, m) && Caster.CanBeHarmful(m, false))
{

View file

@ -1,4 +1,5 @@
using System;
using Server.Collections;
using Server.Items;
namespace Server.Spells.Chivalry
@ -54,6 +55,7 @@ namespace Server.Spells.Chivalry
0
);
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in Caster.GetMobilesInRange(3))
{
if (Caster == m || !SpellHelper.ValidIndirectTarget(Caster, m) || !Caster.CanBeHarmful(m, false) ||
@ -62,6 +64,13 @@ namespace Server.Spells.Chivalry
continue;
}
queue.Enqueue(m);
}
while (queue.Count > 0)
{
var m = queue.Dequeue();
var damage = Math.Clamp(ComputePowerValue(10) + Utility.RandomMinMax(0, 2), 8, 24);
Caster.DoHarmful(m);

View file

@ -34,10 +34,9 @@ namespace Server.Spells.Chivalry
{
if (CheckSequence())
{
var eable = Caster.GetMobilesInRange(3);
using var pool = PooledRefQueue<Mobile>.Create();
foreach (var m in eable)
foreach (var m in Caster.GetMobilesInRange(3))
{
if (m is not BaseCreature { IsAnimatedDead: true } && Caster != m && m.InLOS(Caster) &&
Caster.CanBeBeneficial(m, false, true) && m is not Golem)

View file

@ -37,9 +37,8 @@ namespace Server.Spells.Eighth
return;
}
var eable = Caster.GetMobilesInRange(1 + (int)(Caster.Skills.Magery.Value / 15.0));
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in eable)
foreach (var m in Caster.GetMobilesInRange(1 + (int)(Caster.Skills.Magery.Value / 15.0)))
{
if (Caster != m && SpellHelper.ValidIndirectTarget(Caster, m) && Caster.CanBeHarmful(m, false) &&
(!Core.AOS || Caster.InLOS(m)))

View file

@ -243,17 +243,15 @@ namespace Server.Spells.Fifth
if (map != null && caster != null)
{
var eastToWest = m_Item.ItemID == 0x3915;
var eable = map.GetMobilesInBounds(
new Rectangle2D(
m_Item.X - (eastToWest ? 0 : 1),
m_Item.Y - (eastToWest ? 1 : 0),
eastToWest ? 1 : 2,
eastToWest ? 2 : 1
)
var bounds = new Rectangle2D(
m_Item.X - (eastToWest ? 0 : 1),
m_Item.Y - (eastToWest ? 1 : 0),
eastToWest ? 1 : 2,
eastToWest ? 2 : 1
);
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in eable)
foreach (var m in map.GetMobilesInBounds(bounds))
{
if (m.Z + 16 > m_Item.Z && m_Item.Z + 12 > m.Z && (!Core.AOS || m != caster) &&
SpellHelper.ValidIndirectTarget(caster, m) && caster.CanBeHarmful(m, false))

View file

@ -48,8 +48,7 @@ namespace Server.Spells.Fourth
pool.Enqueue(directTarget);
}
var eable = map.GetMobilesInRange(loc, 2);
foreach (var m in eable)
foreach (var m in map.GetMobilesInRange(loc, 2))
{
if (m != directTarget && AreaCanTarget(m, feluccaRules))
{

View file

@ -48,10 +48,8 @@ namespace Server.Spells.Fourth
return;
}
var eable = Caster.Map.GetMobilesInRange(loc, Core.AOS ? 2 : 3);
using var targets = PooledRefQueue<Mobile>.Create();
foreach (var m in eable)
foreach (var m in Caster.Map.GetMobilesInRange(loc, Core.AOS ? 2 : 3))
{
if (Caster.CanBeBeneficial(m, false))
{

View file

@ -244,7 +244,7 @@ namespace Server.Spells.Fourth
}
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in m_Item.GetMobilesInRange(0))
foreach (var m in m_Item.GetMobilesAt())
{
if (m.Z + 16 > m_Item.Z && m_Item.Z + 12 > m.Z && (!Core.AOS || m != caster) &&
SpellHelper.ValidIndirectTarget(caster, m) && caster.CanBeHarmful(m, false))
@ -256,10 +256,6 @@ namespace Server.Spells.Fourth
while (queue.Count > 0)
{
var m = queue.Dequeue();
if (m == null)
{
continue;
}
if (SpellHelper.CanRevealCaster(m))
{

View file

@ -41,6 +41,10 @@ namespace Server.Spells.Mysticism
Caster.PlaySound(0x64C);
var primarySkill = GetBaseSkill(Caster);
var secondarySkill = GetDamageSkill(Caster);
var cureChance = 10000 + (int)((primarySkill + secondarySkill) / 2 * 75);
using var pool = PooledRefQueue<Mobile>.Create();
pool.Enqueue(m);
@ -49,12 +53,7 @@ namespace Server.Spells.Mysticism
{
foreach (Mobile mob in Caster.Map.GetMobilesInRange(m.Location, 2))
{
if (mob == m)
{
continue;
}
if (casterParty.Contains(mob) && Caster.CanBeBeneficial(mob, false))
if (mob != m && casterParty.Contains(mob) && Caster.CanBeBeneficial(mob, false))
{
pool.Enqueue(mob);
if (pool.Count == 4)
@ -65,9 +64,6 @@ namespace Server.Spells.Mysticism
}
}
var primarySkill = GetBaseSkill(Caster);
var secondarySkill = GetDamageSkill(Caster);
var toHeal = ((int)((primarySkill + secondarySkill) / 4.0) + Utility.RandomMinMax(-3, 3)) / pool.Count;
while (pool.Count > 0)
@ -102,9 +98,9 @@ namespace Server.Spells.Mysticism
if (target.Poisoned)
{
var poisonLevel = target.Poison.Level + 1;
var chanceToCure = 10000 + (int)((primarySkill + secondarySkill) / 2 * 75) - poisonLevel * 1750;
var chanceToCure = cureChance - poisonLevel * 1750;
if (chanceToCure > Utility.Random(10000) && target.CurePoison(Caster))
if (chanceToCure > 10000 || chanceToCure > Utility.Random(10000) && target.CurePoison(Caster))
{
toHealMod -= (int)(toHeal * poisonLevel * 0.15);
}

View file

@ -1,4 +1,5 @@
using System;
using Server.Collections;
using Server.Engines.CannedEvil;
using Server.Engines.PartySystem;
using Server.Factions;
@ -94,14 +95,23 @@ namespace Server.Spells.Necromancy
if (map != null)
{
// Surprisingly, no sparkle type effects
// Cannot move a mobile while iterating mobiles in range, so use a queue
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in r.Spawn.GetMobilesInRange(Range))
{
if (IsValidTarget(m))
{
m.Location = GetNearestShrine(m);
queue.Enqueue(m);
}
}
while (queue.Count > 0)
{
var m = queue.Dequeue();
// Surprisingly, no sparkle type effects
m.Location = GetNearestShrine(m);
}
}
}

View file

@ -80,8 +80,7 @@ namespace Server.Spells.Necromancy
var cbc = Caster as BaseCreature;
var isMonster = cbc?.Controlled == false && (cbc.IsAnimatedDead || !cbc.Summoned);
var eable = m.GetMobilesInRange(2);
foreach (Mobile targ in eable)
foreach (Mobile targ in m.GetMobilesInRange(2))
{
if (targ == Caster || m == targ || !SpellHelper.ValidIndirectTarget(Caster, targ)
|| !Caster.CanBeHarmful(targ, false))

View file

@ -51,8 +51,7 @@ namespace Server.Spells.Necromancy
var cbc = Caster as BaseCreature;
var isMonster = cbc?.Controlled == false && (cbc.IsAnimatedDead || !cbc.Summoned);
var eable = Caster.GetMobilesInRange(Core.ML ? 4 : 5);
foreach (var targ in eable)
foreach (var targ in Caster.GetMobilesInRange(Core.ML ? 4 : 5))
{
if (targ == Caster
|| !Caster.InLOS(targ)
@ -106,7 +105,7 @@ namespace Server.Spells.Necromancy
double damage = Utility.RandomMinMax(30, 35);
damage *= 300 + m.Karma / 100 + GetDamageSkill(Caster) * 10;
damage *= 300 + m.Karma / 100.0 + GetDamageSkill(Caster) * 10;
damage /= 1000;
var sdiBonus = AosAttributes.GetValue(Caster, AosAttribute.SpellDamage);

View file

@ -35,11 +35,9 @@ namespace Server.Spells.Seventh
if (map != null)
{
using var pool = PooledRefQueue<Mobile>.Create();
var pvp = false;
var eable = map.GetMobilesInRange(loc, 2);
foreach (var m in eable)
using var pool = PooledRefQueue<Mobile>.Create();
foreach (var m in map.GetMobilesInRange(loc, 2))
{
if (Core.AOS && (m == Caster || !Caster.InLOS(m)) ||
!SpellHelper.ValidIndirectTarget(Caster, m) ||

View file

@ -35,11 +35,8 @@ namespace Server.Spells.Seventh
if (map != null)
{
var eable = map.GetMobilesInRange<BaseCreature>(new Point3D(p), 8);
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var bc in eable)
foreach (var bc in map.GetMobilesInRange<BaseCreature>(new Point3D(p), 8))
{
if (!(bc.IsDispellable && Caster.CanBeHarmful(bc, false)))
{

View file

@ -42,9 +42,8 @@ namespace Server.Spells.Seventh
if (map != null)
{
var eable = map.GetMobilesInRange(loc, 2);
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in eable)
foreach (var m in map.GetMobilesInRange(loc, 2))
{
if (Caster == m || !SpellHelper.ValidIndirectTarget(Caster, m) ||
!Caster.CanBeHarmful(m, false) || Core.AOS && !Caster.InLOS(m))

View file

@ -32,9 +32,7 @@ namespace Server.Spells.Sixth
if (map != null)
{
var eable = map.GetMobilesInRange(new Point3D(p), 2);
foreach (var m in eable)
foreach (var m in map.GetMobilesInRange(new Point3D(p), 2))
{
if (Core.AOS && (m == Caster || !SpellHelper.ValidIndirectTarget(Caster, m) || !Caster.CanSee(m) ||
!Caster.CanBeHarmful(m, false)))

View file

@ -29,12 +29,8 @@ namespace Server.Spells.Sixth
if (map != null)
{
var eable = map.GetMobilesInRange(
new Point3D(p),
1 + (int)(Caster.Skills.Magery.Value / 20.0)
);
foreach (var m in eable)
var range = 1 + (int)(Caster.Skills.Magery.Value / 20.0);
foreach (var m in map.GetMobilesInRange(new Point3D(p), range))
{
if (m is ShadowKnight && (m.X != p.X || m.Y != p.Y))
{

View file

@ -114,8 +114,7 @@ namespace Server.Spells.Spellweaving
private bool CheckArcanists()
{
var spellWeaving = Caster.Skills.Spellweaving.Value;
var eable = Caster.GetMobilesInRange(1);
foreach (var m in eable)
foreach (var m in Caster.GetMobilesInRange(1))
{
if (m != Caster && m is PlayerMobile && Caster.CanBeBeneficial(m, false) &&
Math.Abs(spellWeaving - m.Skills.Spellweaving.Value) <= 20)
@ -132,9 +131,7 @@ namespace Server.Spells.Spellweaving
// Everyone gets the Arcane Focus, power capped elsewhere
var pool = PooledRefQueue<Mobile>.Create();
var eable = Caster.GetMobilesInRange(1);
foreach (var m in eable)
foreach (var m in Caster.GetMobilesInRange(1))
{
if (m != Caster && m is PlayerMobile && Caster.CanBeBeneficial(m, false) &&
Math.Abs(spellWeaving - m.Skills.Spellweaving.Value) <= 20)

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using Server.Collections;
namespace Server.Spells.Spellweaving
{
@ -24,7 +25,6 @@ namespace Server.Spells.Spellweaving
{
Caster.PlaySound(0x5C6);
var range = 5 + FocusLevel;
var damage = 25 + FocusLevel;
var skill = Caster.Skills.Spellweaving.Value;
@ -34,9 +34,8 @@ namespace Server.Spells.Spellweaving
var fcMalus = FocusLevel + 1;
var ssiMalus = 2 * (FocusLevel + 1);
var eable = Caster.GetMobilesInRange(range);
foreach (var m in eable)
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in Caster.GetMobilesInRange(5 + FocusLevel))
{
if (Caster == m || !Caster.InLOS(m) || !SpellHelper.ValidIndirectTarget(Caster, m) ||
!Caster.CanBeHarmful(m, false))
@ -44,6 +43,12 @@ namespace Server.Spells.Spellweaving
continue;
}
queue.Enqueue(m);
}
while (queue.Count > 0)
{
var m = queue.Dequeue();
Caster.DoHarmful(m);
SpellHelper.Damage(this, m, damage, 0, 0, 100, 0, 0);

View file

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using Server.Collections;
namespace Server.Spells.Spellweaving
{
@ -38,12 +39,10 @@ namespace Server.Spells.Spellweaving
var pvpDamage = damage * (100 + Math.Min(sdiBonus, 15)) / 100;
var range = 2 + FocusLevel;
var duration = TimeSpan.FromSeconds(5 + FocusLevel);
var eable = Caster.GetMobilesInRange(range);
foreach (var m in eable)
using var queue = PooledRefQueue<Mobile>.Create();
foreach (var m in Caster.GetMobilesInRange(2 + FocusLevel))
{
if (Caster == m || !SpellHelper.ValidIndirectTarget(Caster, m) || !Caster.CanBeHarmful(m, false) ||
!Caster.InLOS(m))
@ -51,6 +50,12 @@ namespace Server.Spells.Spellweaving
continue;
}
queue.Enqueue(m);
}
while (queue.Count > 0)
{
var m = queue.Dequeue();
Caster.DoHarmful(m);
var oldSpell = m.Spell as Spell;