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:
parent
27f0cec1fa
commit
28c06c1cc0
71 changed files with 796 additions and 591 deletions
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
288
Projects/Server/Maps/Map.MobileEnumerator.cs
Normal file
288
Projects/Server/Maps/Map.MobileEnumerator.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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!
|
||||
|
|
|
|||
|
|
@ -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]
|
||||
|
|
|
|||
|
|
@ -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) &&
|
||||
|
|
|
|||
|
|
@ -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 &&
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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) ||
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue