diff --git a/Projects/Benchmarks/Benchmarks.csproj b/Projects/Benchmarks/Benchmarks.csproj
index ca226bba4..2720cb6da 100644
--- a/Projects/Benchmarks/Benchmarks.csproj
+++ b/Projects/Benchmarks/Benchmarks.csproj
@@ -11,7 +11,7 @@
-
+
diff --git a/Projects/Benchmarks/Benchmarks/Map/MapEntitiesSelectors.cs b/Projects/Benchmarks/Benchmarks/Map/MapEntitiesSelectors.cs
index 7348824c8..7c70d3056 100644
--- a/Projects/Benchmarks/Benchmarks/Map/MapEntitiesSelectors.cs
+++ b/Projects/Benchmarks/Benchmarks/Map/MapEntitiesSelectors.cs
@@ -14,7 +14,7 @@ namespace Benchmarks.EntitiesSelectors
public class MapEntitiesSelectors
{
private static readonly Sector sector = new();
- private static readonly Point3D[] locations = new[] { new Point3D(0, 0, 0), new Point3D(50, 50, 0) };
+ private static readonly Point3D[] locations = { new Point3D(0, 0, 0), new Point3D(50, 50, 0) };
public static Rectangle2D[] BoundsArray() => new[]
{
@@ -501,8 +501,8 @@ namespace Benchmarks.EntitiesSelectors
public class Sector
{
- public List BItems { get; set; } = new List();
- public List Mobiles { get; set; } = new List();
+ public List BItems { get; set; } = new();
+ public List Mobiles { get; set; } = new();
}
public struct BItemWhereHyper : NetFabric.Hyperlinq.IFunction
diff --git a/Projects/Benchmarks/Benchmarks/Map/MapItemSelectors.cs b/Projects/Benchmarks/Benchmarks/Map/MapItemSelectors.cs
index 8eb1758d2..43f5179c0 100644
--- a/Projects/Benchmarks/Benchmarks/Map/MapItemSelectors.cs
+++ b/Projects/Benchmarks/Benchmarks/Map/MapItemSelectors.cs
@@ -20,7 +20,7 @@ namespace Benchmarks.ItemSelectors
public class MapItemSelectors
{
private static readonly Sector sector = new();
- private static readonly Point3D[] locations = new[] { new Point3D(0, 0, 0), new Point3D(50, 50, 0) };
+ private static readonly Point3D[] locations = { new Point3D(0, 0, 0), new Point3D(50, 50, 0) };
public static Rectangle2D[] BoundsArray() => new[]
{
@@ -352,7 +352,7 @@ namespace Benchmarks.ItemSelectors
public class Sector
{
- public List BItems { get; set; } = new List();
+ public List BItems { get; set; } = new();
}
public struct BItemWhere : StructLinq.IFunction where T : BItem
diff --git a/Projects/Benchmarks/Benchmarks/Map/MapMobileSelectors.cs b/Projects/Benchmarks/Benchmarks/Map/MapMobileSelectors.cs
index c9932070f..a259981f0 100644
--- a/Projects/Benchmarks/Benchmarks/Map/MapMobileSelectors.cs
+++ b/Projects/Benchmarks/Benchmarks/Map/MapMobileSelectors.cs
@@ -2,7 +2,6 @@ using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using NetFabric.Hyperlinq;
using Server;
-using StructLinq;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -15,7 +14,7 @@ namespace Benchmarks.MobileSelectors
public class MapMobileSelectors
{
private static readonly Sector sector = new();
- private static readonly Point3D[] locations = new[] { new Point3D(0, 0, 0), new Point3D(50, 50, 0) };
+ private static readonly Point3D[] locations = { new Point3D(0, 0, 0), new Point3D(50, 50, 0) };
public static Rectangle2D[] BoundsArray() => new[]
{
@@ -224,10 +223,10 @@ namespace Benchmarks.MobileSelectors
{
public MobileDerived(Point3D location) : base(location) { }
}
-
+
public class Sector
{
- public List Mobiles { get; set; } = new List();
+ public List Mobiles { get; set; } = new();
}
public struct MobileWhereHyper : NetFabric.Hyperlinq.IFunction where T : Mobile
diff --git a/Projects/Benchmarks/Benchmarks/Map/MapMultiSelectors.cs b/Projects/Benchmarks/Benchmarks/Map/MapMultiSelectors.cs
index e5c6605f5..233d6b5e4 100644
--- a/Projects/Benchmarks/Benchmarks/Map/MapMultiSelectors.cs
+++ b/Projects/Benchmarks/Benchmarks/Map/MapMultiSelectors.cs
@@ -2,7 +2,6 @@ using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
using NetFabric.Hyperlinq;
using Server;
-using StructLinq;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -15,7 +14,7 @@ namespace Benchmarks.MultiSelectors
public class MapMultiSelectors
{
private static readonly Sector sector = new();
- private static readonly Point3D[] locations = new[] { new Point3D(0, 0, 0), new Point3D(50, 50, 0) };
+ private static readonly Point3D[] locations = { new Point3D(0, 0, 0), new Point3D(50, 50, 0) };
public static Rectangle2D[] BoundsArray() => new[]
{
@@ -291,10 +290,10 @@ namespace Benchmarks.MultiSelectors
public class Sector
{
- public List Multis { get; set; } = new List();
+ public List Multis { get; set; } = new();
}
- public struct MultiWhereHyper : NetFabric.Hyperlinq.IFunction
+ public struct MultiWhereHyper : IFunction
{
private readonly Rectangle2D bounds;
diff --git a/Projects/Benchmarks/Benchmarks/Map/MapMultiTilesSelectors.cs b/Projects/Benchmarks/Benchmarks/Map/MapMultiTilesSelectors.cs
index 553016f93..8f77aee25 100644
--- a/Projects/Benchmarks/Benchmarks/Map/MapMultiTilesSelectors.cs
+++ b/Projects/Benchmarks/Benchmarks/Map/MapMultiTilesSelectors.cs
@@ -1,8 +1,6 @@
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Jobs;
-using NetFabric.Hyperlinq;
using Server;
-using StructLinq;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -14,7 +12,7 @@ namespace Benchmarks.MultiTilesSelectors
public class MapMultiTilesSelectors
{
private static readonly Sector sector = new();
- private static readonly Point3D[] locations = new[] { new Point3D(0, 0, 0), new Point3D(50, 50, 0) };
+ private static readonly Point3D[] locations = { new Point3D(0, 0, 0), new Point3D(50, 50, 0) };
public static Rectangle2D[] BoundsArray() => new[]
{
@@ -55,7 +53,7 @@ namespace Benchmarks.MultiTilesSelectors
return toRet;
}
-
+
[Benchmark(Baseline = true)]
public int SelectMultiTilesLinq()
{
@@ -347,6 +345,6 @@ namespace Benchmarks.MultiTilesSelectors
public class Sector
{
- public List Multis { get; set; } = new List();
+ public List Multis { get; set; } = new();
}
}
diff --git a/Projects/Server.Tests/Server.Tests.csproj b/Projects/Server.Tests/Server.Tests.csproj
index 8c727c281..b310c4f69 100644
--- a/Projects/Server.Tests/Server.Tests.csproj
+++ b/Projects/Server.Tests/Server.Tests.csproj
@@ -3,8 +3,8 @@
false
-
-
+
+
diff --git a/Projects/Server.Tests/Tests/Collections/PooledRefQueueTests.cs b/Projects/Server.Tests/Tests/Collections/PooledRefQueueTests.cs
new file mode 100644
index 000000000..a9d3576b0
--- /dev/null
+++ b/Projects/Server.Tests/Tests/Collections/PooledRefQueueTests.cs
@@ -0,0 +1,102 @@
+using System;
+using Moq;
+using Server.Collections;
+using Server.Random;
+using Xunit;
+
+namespace Server.Tests;
+
+public sealed class PooledRefQueueTests : IDisposable
+{
+ public void Dispose() => RandomSources.SetRng(null);
+
+ private static void PrepareRng(int queueCount, int rngValue)
+ {
+ Mock mockRng = new Mock();
+ mockRng
+ .Setup(rng => rng.Next(It.IsAny()))
+ .Returns(
+ (int size) =>
+ {
+ Assert.Equal(queueCount, size);
+ return rngValue;
+ }
+ );
+
+ RandomSources.SetRng(mockRng.Object);
+ }
+
+ [Fact]
+ public void TestPeekRandom1()
+ {
+ // Random value for _head = 0, _tail = 5, _size = 5,
+ using var queue = PooledRefQueue.Create(10);
+ queue.Enqueue(0);
+ queue.Enqueue(1);
+ queue.Enqueue(2);
+ queue.Enqueue(3); // <-----
+ queue.Enqueue(4);
+ queue.Enqueue(5);
+
+ PrepareRng(6, 3);
+ Assert.Equal(3, queue.PeekRandom());
+ }
+
+ [Fact]
+ public void TestPeekRandom2()
+ {
+ // Random value for _head = 3, _tail = 10, _size = 7,
+ using var queue = PooledRefQueue.Create(10);
+ queue.Enqueue(0);
+ queue.Enqueue(1);
+ queue.Enqueue(2);
+
+ queue.Enqueue(3);
+ queue.Enqueue(4);
+ queue.Enqueue(5);
+ queue.Enqueue(6); // <---
+ queue.Enqueue(7);
+ queue.Enqueue(8);
+ queue.Enqueue(9);
+
+ queue.Dequeue();
+ queue.Dequeue();
+ queue.Dequeue();
+
+ PrepareRng(7, 3);
+ Assert.Equal(6, queue.PeekRandom());
+ }
+
+ [Theory]
+ [InlineData(3, 6)]
+ [InlineData(8, 11)]
+ [InlineData(6, 9)]
+ [InlineData(7, 10)]
+ public void TestPeekRandom3(int rngValue, int expectedIndex)
+ {
+ // Random value for _head = 3, _tail = 2, _size = 10,
+ using var queue = PooledRefQueue.Create(10);
+ queue.Enqueue(0);
+ queue.Enqueue(1);
+ queue.Enqueue(2);
+
+ queue.Enqueue(3);
+ queue.Enqueue(4);
+ queue.Enqueue(5);
+ queue.Enqueue(6);
+ queue.Enqueue(7);
+ queue.Enqueue(8);
+ queue.Enqueue(9);
+
+ queue.Dequeue();
+ queue.Dequeue();
+ queue.Dequeue();
+
+ queue.Enqueue(10);
+ queue.Enqueue(11);
+ queue.Enqueue(12);
+
+ PrepareRng(10, rngValue);
+ Assert.Equal(expectedIndex, queue.PeekRandom());
+ }
+}
diff --git a/Projects/Server/Collections/PooledRefQueue.cs b/Projects/Server/Collections/PooledRefQueue.cs
index 577e7f6f4..9c8ea81b0 100644
--- a/Projects/Server/Collections/PooledRefQueue.cs
+++ b/Projects/Server/Collections/PooledRefQueue.cs
@@ -180,6 +180,22 @@ namespace Server.Collections
return _array[_head];
}
+ public T PeekRandom()
+ {
+ if (_size == 0)
+ {
+ ThrowForEmptyQueue();
+ }
+
+ var index = _head + Utility.Random(_size);
+ if (index >= _array.Length)
+ {
+ index -= _array.Length;
+ }
+
+ return _array[index];
+ }
+
public bool TryPeek([MaybeNullWhen(false)] out T result)
{
if (_size == 0)
diff --git a/Projects/Server/Random/RandomSources.cs b/Projects/Server/Random/RandomSources.cs
index d99add499..49f8a6188 100644
--- a/Projects/Server/Random/RandomSources.cs
+++ b/Projects/Server/Random/RandomSources.cs
@@ -17,10 +17,14 @@ namespace Server.Random
{
public static class RandomSources
{
- private static IRandomSource m_Source;
- private static IRandomSource m_SecureSource;
+ private static IRandomSource _source;
+ private static IRandomSource _secureSource;
- public static IRandomSource Source => m_Source ??= new Xoshiro256PlusPlus();
- public static IRandomSource SecureSource => m_SecureSource ??= new SecureRandom();
+ public static IRandomSource Source => _source ??= new Xoshiro256PlusPlus();
+ public static IRandomSource SecureSource => _secureSource ??= new SecureRandom();
+
+ public static void SetRng(IRandomSource newSource) => _source = newSource;
+
+ public static void SetSecureRng(IRandomSource newSource) => _secureSource = newSource;
}
}
diff --git a/Projects/Server/Utilities/Utility.cs b/Projects/Server/Utilities/Utility.cs
index 7d16d62b0..30f25b577 100644
--- a/Projects/Server/Utilities/Utility.cs
+++ b/Projects/Server/Utilities/Utility.cs
@@ -920,6 +920,7 @@ namespace Server
return total + bonus;
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Shuffle(this IList list)
{
var count = list.Count;
@@ -930,6 +931,7 @@ namespace Server
}
}
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Shuffle(this Span list)
{
var count = list.Length;
@@ -1094,6 +1096,15 @@ namespace Server
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static double RandomDouble() => RandomSources.Source.NextDouble();
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ public static Point3D RandomPointIn(Rectangle2D rect, Map map)
+ {
+ var x = Random(rect.X, rect.Width);
+ var y = Random(rect.Y, rect.Height);
+
+ return new Point3D(x, y, map.GetAverageZ(x, y));
+ }
+
///
/// Random pink, blue, green, orange, red or yellow hue
///
diff --git a/Projects/UOContent.Tests/UOContent.Tests.csproj b/Projects/UOContent.Tests/UOContent.Tests.csproj
index fc960bae1..38f0fc1bd 100644
--- a/Projects/UOContent.Tests/UOContent.Tests.csproj
+++ b/Projects/UOContent.Tests/UOContent.Tests.csproj
@@ -3,8 +3,8 @@
false
-
-
+
+
diff --git a/Projects/UOContent/Engines/Quests/Study of the Solen Hive/NestArea.cs b/Projects/UOContent/Engines/Quests/Study of the Solen Hive/NestArea.cs
index bc93f6289..2a14b34db 100644
--- a/Projects/UOContent/Engines/Quests/Study of the Solen Hive/NestArea.cs
+++ b/Projects/UOContent/Engines/Quests/Study of the Solen Hive/NestArea.cs
@@ -1,5 +1,3 @@
-using System.Linq;
-
namespace Server.Engines.Quests.Naturalist
{
public class NestArea
@@ -38,7 +36,22 @@ namespace Server.Engines.Quests.Naturalist
m_Rects = rects;
}
- public static int NonSpecialCount => m_Areas.Count(area => !area.Special);
+ public static int NonSpecialCount
+ {
+ get
+ {
+ int count = 0;
+ foreach (var area in m_Areas)
+ {
+ if (!area.Special)
+ {
+ count++;
+ }
+ }
+
+ return count;
+ }
+ }
public bool Special { get; }
@@ -60,7 +73,15 @@ namespace Server.Engines.Quests.Naturalist
public static NestArea Find(Point3D p)
{
- return m_Areas.FirstOrDefault(area => area.Contains(p));
+ foreach (var area in m_Areas)
+ {
+ if (area.Contains(p))
+ {
+ return area;
+ }
+ }
+
+ return null;
}
public static NestArea GetByID(int id)
diff --git a/Projects/UOContent/Engines/Treasures of Tokuno/TreasuresOfTokuno.cs b/Projects/UOContent/Engines/Treasures of Tokuno/TreasuresOfTokuno.cs
index a4d2ede01..4e1ff18fb 100644
--- a/Projects/UOContent/Engines/Treasures of Tokuno/TreasuresOfTokuno.cs
+++ b/Projects/UOContent/Engines/Treasures of Tokuno/TreasuresOfTokuno.cs
@@ -151,7 +151,7 @@ namespace Server.Misc
pm.ToTTotalMonsterFame += (int)(bc.Fame * (1 + Math.Sqrt(pm.Luck) / 100));
- // This is the Exponentional regression with only 2 datapoints.
+ // This is the Exponential regression with only 2 data points.
// A log. func would also work, but it didn't make as much sense.
// This function isn't OSI exact being that I don't know OSI's func they used ;p
var x = pm.ToTTotalMonsterFame;
@@ -269,10 +269,8 @@ namespace Server.Mobiles
{
if (pm.ToTItemsTurnedIn >= TreasuresOfTokuno.ItemsPerReward)
{
- SayTo(
- pm,
- 1070980
- ); // Congratulations! You have turned in enough minor treasures to earn a greater reward.
+ // Congratulations! You have turned in enough minor treasures to earn a greater reward.
+ SayTo(pm, 1070980);
pm.CloseGump(); // Sanity
@@ -285,18 +283,16 @@ namespace Server.Mobiles
{
if (pm.ToTItemsTurnedIn == 0)
{
- SayTo(
- pm,
- 1071013
- ); // Bring me 10 of the lost treasures of Tokuno and I will reward you with a valuable item.
+ // Bring me 10 of the lost treasures of Tokuno and I will reward you with a valuable item.
+ SayTo(pm, 1071013);
}
else
{
SayTo(
pm,
- 1070981,
+ 1070981, // You have turned in ~1_COUNT~ minor artifacts. Turn in ~2_NUM~ to receive a reward.
$"{pm.ToTItemsTurnedIn}\t{TreasuresOfTokuno.ItemsPerReward}"
- ); // You have turned in ~1_COUNT~ minor artifacts. Turn in ~2_NUM~ to receive a reward.
+ );
}
var buttons = ToTTurnInGump.FindRedeemableItems(pm);
@@ -342,34 +338,26 @@ namespace Server.Gumps
{
private readonly Mobile m_Collector;
- public ToTTurnInGump(Mobile collector, List buttons) : base(
- 1071012,
- buttons.ToList()
- ) // Click a minor artifact to give it to Ihara Soko.
- =>
- m_Collector = collector;
+ // Click a minor artifact to give it to Ihara Soko.
+ public ToTTurnInGump(Mobile collector, List buttons)
+ : base(1071012, buttons) => m_Collector = collector;
- public static List FindRedeemableItems(Mobile m)
+ public static List FindRedeemableItems(Mobile m)
{
var pack = m.Backpack;
if (pack == null)
{
- return new List();
+ return new List();
}
- var buttons = new List();
+ var buttons = new List();
var items = pack.FindItemsByType(TreasuresOfTokuno.LesserArtifactsTotal);
for (var i = 0; i < items.Length; i++)
{
var item = items[i];
- if (item is ChestOfHeirlooms heirlooms && !heirlooms.Locked)
- {
- continue;
- }
-
- if (item is ChestOfHeirlooms ofHeirlooms && ofHeirlooms.TrapLevel != 10)
+ if (item is ChestOfHeirlooms heirlooms && (!heirlooms.Locked || heirlooms.TrapLevel != 10))
{
continue;
}
@@ -400,10 +388,8 @@ namespace Server.Gumps
if (++pm.ToTItemsTurnedIn >= TreasuresOfTokuno.ItemsPerReward)
{
- m_Collector.SayTo(
- pm,
- 1070980
- ); // Congratulations! You have turned in enough minor treasures to earn a greater reward.
+ // Congratulations! You have turned in enough minor treasures to earn a greater reward.
+ m_Collector.SayTo(pm, 1070980);
pm.CloseGump(); // Sanity
@@ -416,9 +402,9 @@ namespace Server.Gumps
{
m_Collector.SayTo(
pm,
- 1070981,
+ 1070981, // You have turned in ~1_COUNT~ minor artifacts. Turn in ~2_NUM~ to receive a reward.
$"{pm.ToTItemsTurnedIn}\t{TreasuresOfTokuno.ItemsPerReward}"
- ); // You have turned in ~1_COUNT~ minor artifacts. Turn in ~2_NUM~ to receive a reward.
+ );
var buttons = FindRedeemableItems(pm);
@@ -440,19 +426,17 @@ namespace Server.Gumps
if (pm.ToTItemsTurnedIn == 0)
{
- m_Collector.SayTo(
- pm,
- 1071013
- ); // Bring me 10 of the lost treasures of Tokuno and I will reward you with a valuable item.
+ // Bring me 10 of the lost treasures of Tokuno and I will reward you with a valuable item.
+ m_Collector.SayTo(pm, 1071013);
}
- else if (pm.ToTItemsTurnedIn < TreasuresOfTokuno.ItemsPerReward
- ) // This case should ALWAYS be true with this gump, jsut a sanity check
+ // This case should ALWAYS be true with this gump, just a sanity check
+ else if (pm.ToTItemsTurnedIn < TreasuresOfTokuno.ItemsPerReward)
{
m_Collector.SayTo(
pm,
- 1070981,
+ 1070981, // You have turned in ~1_COUNT~ minor artifacts. Turn in ~2_NUM~ to receive a reward.
$"{pm.ToTItemsTurnedIn}\t{TreasuresOfTokuno.ItemsPerReward}"
- ); // You have turned in ~1_COUNT~ minor artifacts. Turn in ~2_NUM~ to receive a reward.
+ );
}
else
{
@@ -611,11 +595,9 @@ namespace Server.Gumps
pm.ToTItemsTurnedIn -= TreasuresOfTokuno.ItemsPerReward;
m_Collector.SayTo(
pm,
- 1070984,
- item.Name == null || item.Name.Length <= 0
- ? $"#{item.LabelNumber}"
- : item.Name
- ); // You have earned the gratitude of the Empire. I have placed the ~1_OBJTYPE~ in your backpack.
+ 1070984, // You have earned the gratitude of the Empire. I have placed the ~1_OBJTYPE~ in your backpack.
+ item.Name?.Length > 0 ? item.Name : $"#{item.LabelNumber}"
+ );
}
else
{
@@ -634,19 +616,17 @@ namespace Server.Gumps
if (pm.ToTItemsTurnedIn == 0)
{
- m_Collector.SayTo(
- pm,
- 1071013
- ); // Bring me 10 of the lost treasures of Tokuno and I will reward you with a valuable item.
+ // Bring me 10 of the lost treasures of Tokuno and I will reward you with a valuable item.
+ m_Collector.SayTo(pm, 1071013);
}
- else if (pm.ToTItemsTurnedIn < TreasuresOfTokuno.ItemsPerReward
- ) // This and above case should ALWAYS be FALSE with this gump, jsut a sanity check
+ // This and above case should ALWAYS be FALSE with this gump, jsut a sanity check
+ else if (pm.ToTItemsTurnedIn < TreasuresOfTokuno.ItemsPerReward)
{
m_Collector.SayTo(
pm,
- 1070981,
+ 1070981, // You have turned in ~1_COUNT~ minor artifacts. Turn in ~2_NUM~ to receive a reward.
$"{pm.ToTItemsTurnedIn}\t{TreasuresOfTokuno.ItemsPerReward}"
- ); // You have turned in ~1_COUNT~ minor artifacts. Turn in ~2_NUM~ to receive a reward.
+ );
}
else
{
diff --git a/Projects/UOContent/Gumps/BaseImageTileButtonsGump.cs b/Projects/UOContent/Gumps/BaseImageTileButtonsGump.cs
index ad2dbcb5c..18602b84a 100644
--- a/Projects/UOContent/Gumps/BaseImageTileButtonsGump.cs
+++ b/Projects/UOContent/Gumps/BaseImageTileButtonsGump.cs
@@ -53,8 +53,8 @@ namespace Server.Gumps
{
}
- public BaseImageTileButtonsGump(TextDefinition header, ImageTileButtonInfo[] buttons) :
- base(10, 10) // Coords are 0, o on OSI, intentional difference
+ // Coords are 0, 0 on OSI, intentional difference
+ public BaseImageTileButtonsGump(TextDefinition header, ImageTileButtonInfo[] buttons) : base(10, 10)
{
Buttons = buttons;
AddPage(0);
diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2009/Engines/PumpkinPatch.cs b/Projects/UOContent/Holiday Stuff/Halloween/2009/Engines/PumpkinPatch.cs
index cf00181eb..93c797c3a 100644
--- a/Projects/UOContent/Holiday Stuff/Halloween/2009/Engines/PumpkinPatch.cs
+++ b/Projects/UOContent/Holiday Stuff/Halloween/2009/Engines/PumpkinPatch.cs
@@ -1,5 +1,4 @@
using System;
-using System.Linq;
using Server.Events.Halloween;
using Server.Items;
@@ -51,22 +50,23 @@ namespace Server.Engines.Events
var rect = m_PumpkinFields[i];
var spawncount = rect.Height * rect.Width / 20;
- var pumpkins = map.GetItemsInBounds(rect).OfType().Count();
+ var eable = map.GetItemsInBounds(rect);
+ var pumpkins = 0;
+ foreach (var p in eable)
+ {
+ if (pumpkins++ >= spawncount)
+ {
+ break;
+ }
+ }
+
+ eable.Free();
if (spawncount > pumpkins)
{
- new HalloweenPumpkin().MoveToWorld(RandomPointIn(rect, map), map);
+ new HalloweenPumpkin().MoveToWorld(Utility.RandomPointIn(rect, map), map);
}
}
}
-
- private static Point3D RandomPointIn(Rectangle2D rect, Map map)
- {
- var x = Utility.Random(rect.X, rect.Width);
- var y = Utility.Random(rect.Y, rect.Height);
- var z = map.GetAverageZ(x, y);
-
- return new Point3D(x, y, z);
- }
}
}
diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2012/Engines/PlayerZombies.cs b/Projects/UOContent/Holiday Stuff/Halloween/2012/Engines/PlayerZombies.cs
index c24d2572f..20fd8e5ea 100644
--- a/Projects/UOContent/Holiday Stuff/Halloween/2012/Engines/PlayerZombies.cs
+++ b/Projects/UOContent/Holiday Stuff/Halloween/2012/Engines/PlayerZombies.cs
@@ -115,8 +115,7 @@ namespace Server.Engines.Events
}
var map = Utility.RandomBool() ? Map.Trammel : Map.Felucca;
-
- var home = GetRandomPointInRect(m_Cemetaries.RandomElement(), map);
+ var home = Utility.RandomPointIn(m_Cemetaries.RandomElement(), map);
if (map.CanSpawnMobile(home))
{
@@ -131,14 +130,6 @@ namespace Server.Engines.Events
_deathQueue.Remove(player);
}
}
-
- private static Point3D GetRandomPointInRect(Rectangle2D rect, Map map)
- {
- var x = Utility.Random(rect.X, rect.Width);
- var y = Utility.Random(rect.Y, rect.Height);
-
- return new Point3D(x, y, map.GetAverageZ(x, y));
- }
}
[Serializable(0, false)]
diff --git a/Projects/UOContent/Items/Misc/Firebomb.cs b/Projects/UOContent/Items/Misc/Firebomb.cs
index 60433972e..1c7f9a602 100644
--- a/Projects/UOContent/Items/Misc/Firebomb.cs
+++ b/Projects/UOContent/Items/Misc/Firebomb.cs
@@ -1,8 +1,9 @@
using System;
using System.Collections.Generic;
-using System.Linq;
+using Server.Collections;
using Server.Network;
using Server.Spells;
+using Server.Spells.Fourth;
using Server.Targeting;
namespace Server.Items
@@ -10,6 +11,7 @@ namespace Server.Items
public class Firebomb : Item
{
private Mobile m_LitBy;
+ private Point3D _thrownFromLocation;
private int m_Ticks;
private TimerExecutionToken _timerToken;
private List m_Users;
@@ -138,24 +140,34 @@ namespace Server.Items
else if (RootParent == null)
{
var eable = Map.GetMobilesInRange(Location, 1);
- var toDamage = eable.ToList();
-
- eable.Free();
-
- for (var i = 0; i < toDamage.Count; ++i)
+ using var targets = PooledRefQueue.Create();
+ foreach (var m in eable)
{
- var victim = toDamage[i];
-
- if (m_LitBy == null || SpellHelper.ValidIndirectTarget(m_LitBy, victim) &&
- m_LitBy.CanBeHarmful(victim, false))
+ if (m_LitBy == null || SpellHelper.ValidIndirectTarget(m_LitBy, m) &&
+ m_LitBy.CanBeHarmful(m, false))
{
- m_LitBy?.DoHarmful(victim);
-
- AOS.Damage(victim, m_LitBy, Utility.Random(3) + 4, 0, 100, 0, 0, 0);
+ targets.Enqueue(m);
}
}
+ eable.Free();
- new FirebombField(m_LitBy, toDamage).MoveToWorld(Location, Map);
+ while (targets.Count > 0)
+ {
+ var victim = targets.Dequeue();
+ m_LitBy?.DoHarmful(victim);
+ AOS.Damage(victim, m_LitBy, Utility.Random(3) + 4, 0, 100, 0, 0, 0);
+ }
+
+ var loc = _thrownFromLocation;
+ var eastToWest = SpellHelper.GetEastToWest(loc, Location);
+ Effects.PlaySound(loc, Map, 0x20C);
+ var itemID = eastToWest ? 0x398C : 0x3996;
+
+ for (var i = -2; i <= 2; ++i)
+ {
+ var targetLoc = new Point3D(eastToWest ? loc.X + i : loc.X, eastToWest ? loc.Y : loc.Y + i, loc.Z);
+ new FireFieldSpell.FireFieldItem(itemID, targetLoc, m_LitBy, Map, TimeSpan.FromSeconds(9), i);
+ }
}
_timerToken.Cancel();
@@ -178,12 +190,12 @@ namespace Server.Items
}
SpellHelper.GetSurfaceTop(ref p);
- var loc = new Point3D(p);
+ _thrownFromLocation = new Point3D(p);
var map = Map;
from.RevealingAction();
- var to = p as IEntity ?? new Entity(Serial.Zero, loc, map);
+ var to = p as IEntity ?? new Entity(Serial.Zero, _thrownFromLocation, map);
Effects.SendMovingEffect(from, to, ItemID, 7, 0, false, false, Hue);
@@ -195,7 +207,7 @@ namespace Server.Items
return;
}
- MoveToWorld(loc, map);
+ MoveToWorld(_thrownFromLocation, map);
}
);
Internalize();
@@ -203,9 +215,7 @@ namespace Server.Items
private class ThrowTarget : Target
{
- public ThrowTarget(Firebomb bomb)
- : base(12, true, TargetFlags.None) =>
- Bomb = bomb;
+ public ThrowTarget(Firebomb bomb) : base(12, true, TargetFlags.None) => Bomb = bomb;
public Firebomb Bomb { get; }
@@ -215,93 +225,4 @@ namespace Server.Items
}
}
}
-
- public class FirebombField : Item
- {
- private readonly List m_Burning;
- private readonly DateTime m_Expire;
- private readonly Mobile m_LitBy;
- private TimerExecutionToken _timerToken;
-
- public FirebombField(Mobile litBy, List toDamage) : base(0x376A)
- {
- Movable = false;
- m_LitBy = litBy;
- m_Expire = Core.Now + TimeSpan.FromSeconds(10);
- m_Burning = toDamage;
- Timer.StartTimer(TimeSpan.FromSeconds(1.0), TimeSpan.FromSeconds(1.0), OnFirebombFieldTimerTick, out _timerToken);
- }
-
- public FirebombField(Serial serial) : base(serial)
- {
- }
-
- public override void Serialize(IGenericWriter writer)
- {
- // Don't serialize these...
- }
-
- public override void Deserialize(IGenericReader reader)
- {
- }
-
- public override bool OnMoveOver(Mobile m)
- {
- if (ItemID == 0x398C && m_LitBy == null ||
- SpellHelper.ValidIndirectTarget(m_LitBy, m) && m_LitBy.CanBeHarmful(m, false))
- {
- m_LitBy?.DoHarmful(m);
-
- AOS.Damage(m, m_LitBy, 2, 0, 100, 0, 0, 0);
- m.PlaySound(0x208);
-
- if (!m_Burning.Contains(m))
- {
- m_Burning.Add(m);
- }
- }
-
- return true;
- }
-
- private void OnFirebombFieldTimerTick()
- {
- if (Deleted)
- {
- _timerToken.Cancel();
- return;
- }
-
- if (ItemID == 0x376A)
- {
- ItemID = 0x398C;
- return;
- }
-
- for (var i = 0; i < m_Burning.Count;)
- {
- var victim = m_Burning[i];
-
- if (victim.Location == Location && victim.Map == Map &&
- (m_LitBy == null || SpellHelper.ValidIndirectTarget(m_LitBy, victim) &&
- m_LitBy.CanBeHarmful(victim, false)))
- {
- m_LitBy?.DoHarmful(victim);
-
- AOS.Damage(victim, m_LitBy, Utility.Random(3) + 4, 0, 100, 0, 0, 0);
- ++i;
- }
- else
- {
- m_Burning.RemoveAt(i);
- }
- }
-
- if (Core.Now >= m_Expire)
- {
- _timerToken.Cancel();
- Delete();
- }
- }
- }
}
diff --git a/Projects/UOContent/Items/Skill Items/Camping/Campfire.cs b/Projects/UOContent/Items/Skill Items/Camping/Campfire.cs
index 424199d15..54b4f0410 100644
--- a/Projects/UOContent/Items/Skill Items/Camping/Campfire.cs
+++ b/Projects/UOContent/Items/Skill Items/Camping/Campfire.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using Server.Mobiles;
namespace Server.Items
@@ -58,20 +57,26 @@ namespace Server.Items
switch (value)
{
case CampfireStatus.Burning:
- ItemID = 0xDE3;
- Light = LightType.Circle300;
- break;
+ {
+ ItemID = 0xDE3;
+ Light = LightType.Circle300;
+ break;
+ }
case CampfireStatus.Extinguishing:
- ItemID = 0xDE9;
- Light = LightType.Circle150;
- break;
+ {
+ ItemID = 0xDE9;
+ Light = LightType.Circle150;
+ break;
+ }
default:
- ItemID = 0xDEA;
- Light = LightType.ArchedWindowEast;
- ClearEntries();
- break;
+ {
+ ItemID = 0xDEA;
+ Light = LightType.ArchedWindowEast;
+ ClearEntries();
+ break;
+ }
}
}
}
@@ -111,8 +116,10 @@ namespace Server.Items
return;
}
- foreach (var entry in m_Entries.ToList())
+ for (var i = m_Entries.Count - 1; i >= 0; i--)
{
+ var entry = m_Entries[i];
+
if (!entry.Valid || entry.Player.NetState == null)
{
RemoveEntry(entry);
@@ -149,10 +156,13 @@ namespace Server.Items
return;
}
- foreach (var entry in m_Entries.ToList())
+ foreach (var entry in m_Entries)
{
- RemoveEntry(entry);
+ m_Table.Remove(entry.Player);
}
+
+ m_Entries.Clear();
+ m_Entries.TrimExcess();
}
public override void OnAfterDelete()
diff --git a/Projects/UOContent/Items/Skill Items/Misc/FireHorn.cs b/Projects/UOContent/Items/Skill Items/Misc/FireHorn.cs
index 6f35e8b82..edd2eeb4d 100644
--- a/Projects/UOContent/Items/Skill Items/Misc/FireHorn.cs
+++ b/Projects/UOContent/Items/Skill Items/Misc/FireHorn.cs
@@ -1,5 +1,5 @@
using System;
-using System.Linq;
+using Server.Collections;
using Server.Network;
using Server.Spells;
using Server.Targeting;
@@ -100,27 +100,23 @@ namespace Server.Items
true
);
- var eable = from.Map.GetMobilesInRange(new Point3D(loc), 2);
-
var playerVsPlayer = false;
- var targets = eable.Where(
- m =>
+ var eable = from.Map.GetMobilesInRange(loc, 2);
+
+ using var targets = PooledRefQueue.Create();
+ foreach (var m in eable)
+ {
+ if (from != m && SpellHelper.ValidIndirectTarget(from, m) && from.CanBeHarmful(m, false) &&
+ (!Core.AOS || from.InLOS(m)))
+ {
+ targets.Enqueue(m);
+
+ if (m.Player)
{
- if (from == m || !SpellHelper.ValidIndirectTarget(from, m) || !from.CanBeHarmful(m, false)
- || Core.AOS && !from.InLOS(m))
- {
- return false;
- }
-
- if (m.Player)
- {
- playerVsPlayer = true;
- }
-
- return true;
+ playerVsPlayer = true;
}
- )
- .ToList();
+ }
+ }
eable.Free();
@@ -178,9 +174,9 @@ namespace Server.Items
damage /= targets.Count;
}
- for (var i = 0; i < targets.Count; ++i)
+ while (targets.Count > 0)
{
- var m = targets[i];
+ var m = targets.Dequeue();
var toDeal = damage;
diff --git a/Projects/UOContent/Items/Weapons/Abilities/WhirlwindAttack.cs b/Projects/UOContent/Items/Weapons/Abilities/WhirlwindAttack.cs
index 32496a2a7..4b0004bac 100644
--- a/Projects/UOContent/Items/Weapons/Abilities/WhirlwindAttack.cs
+++ b/Projects/UOContent/Items/Weapons/Abilities/WhirlwindAttack.cs
@@ -1,5 +1,5 @@
using System;
-using System.Linq;
+using Server.Collections;
using Server.Spells;
namespace Server.Items
@@ -36,23 +36,30 @@ namespace Server.Items
attacker.FixedEffect(0x3728, 10, 15);
attacker.PlaySound(0x2A1);
- var targets = attacker.GetMobilesInRange(1)
- .Where(
- m =>
- m?.Deleted == false && m != defender && m != attacker &&
- SpellHelper.ValidIndirectTarget(attacker, m) &&
- m.Map == attacker.Map && m.Alive && attacker.CanSee(m) && attacker.CanBeHarmful(m) &&
- attacker.InRange(m, weapon.MaxRange) && attacker.InLOS(m)
- )
- .ToList();
+ var eable = attacker.GetMobilesInRange(1);
+ using var queue = PooledRefQueue.Create();
- if (targets.Count <= 0)
+ foreach (var m in eable)
+ {
+ if (m?.Deleted == false && m != defender && m != attacker &&
+ m.Map == attacker.Map && m.Alive &&
+ SpellHelper.ValidIndirectTarget(attacker, m) &&
+ attacker.CanSee(m) && attacker.CanBeHarmful(m) &&
+ attacker.InRange(m, weapon.MaxRange) && attacker.InLOS(m))
+ {
+ queue.Enqueue(m);
+ }
+ }
+
+ eable.Free();
+
+ if (queue.Count <= 0)
{
return;
}
var bushido = attacker.Skills.Bushido.Value;
- var damageBonus = 1.0 + Math.Pow(targets.Count * bushido / 60, 2) / 100;
+ var damageBonus = 1.0 + Math.Pow(queue.Count * bushido / 60, 2) / 100;
if (damageBonus > 2.0)
{
@@ -61,10 +68,9 @@ namespace Server.Items
attacker.RevealingAction();
- for (var i = 0; i < targets.Count; ++i)
+ while (queue.Count > 0)
{
- var m = targets[i];
-
+ var m = queue.Dequeue();
attacker.SendLocalizedMessage(1060161); // The whirling attack strikes a target!
m.SendLocalizedMessage(1060162); // You are struck by the whirling attack and take damage!
diff --git a/Projects/UOContent/Mobiles/Familiars/HordeMinion.cs b/Projects/UOContent/Mobiles/Familiars/HordeMinion.cs
index e1be900ac..96bb1a49d 100644
--- a/Projects/UOContent/Mobiles/Familiars/HordeMinion.cs
+++ b/Projects/UOContent/Mobiles/Familiars/HordeMinion.cs
@@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
-using System.Linq;
+using Server.Collections;
using Server.ContextMenus;
using Server.Gumps;
using Server.Items;
@@ -76,12 +76,24 @@ namespace Server.Mobiles
return;
}
- var eable = GetItemsInRange(2).Where(item => item.Movable && item.Stackable);
-
- var pickedUp = 0;
-
+ var eable = GetItemsInRange(2);
+ using var queue = PooledRefQueue- .Create();
foreach (var item in eable)
{
+ if (item.Movable && item.Stackable)
+ {
+ queue.Enqueue(item);
+ }
+ }
+
+ eable.Free();
+
+ var pickedUp = 3;
+
+ while (pickedUp > 0 && queue.Count > 0)
+ {
+ var item = queue.Dequeue();
+
if (!pack.CheckHold(this, item, false, true))
{
return;
@@ -97,11 +109,7 @@ namespace Server.Mobiles
}
Drop(this, Point3D.Zero);
-
- if (++pickedUp == 3)
- {
- break;
- }
+ pickedUp--;
}
}
diff --git a/Projects/UOContent/Mobiles/Familiars/ShadowWisp.cs b/Projects/UOContent/Mobiles/Familiars/ShadowWisp.cs
index 6ad276cc2..786c2d12b 100644
--- a/Projects/UOContent/Mobiles/Familiars/ShadowWisp.cs
+++ b/Projects/UOContent/Mobiles/Familiars/ShadowWisp.cs
@@ -1,5 +1,5 @@
using System;
-using System.Linq;
+using Server.Collections;
namespace Server.Mobiles
{
@@ -70,16 +70,20 @@ namespace Server.Mobiles
return;
}
- var list = GetMobilesInRange(5)
- .Where(
- m =>
- m.Player && m.Alive && !m.IsDeadBondedPet && m.Karma <= 0 && m.AccessLevel < AccessLevel.Counselor
- )
- .ToList();
-
- for (var i = 0; i < list.Count; ++i)
+ var eable = GetMobilesInRange(5);
+ using var queue = PooledRefQueue.Create();
+ foreach (var m in eable)
{
- var m = list[i];
+ if (m.Player && m.Alive && !m.IsDeadBondedPet && m.Karma <= 0 && m.AccessLevel < AccessLevel.Counselor)
+ {
+ queue.Enqueue(m);
+ }
+ }
+ eable.Free();
+
+ while (queue.Count > 0)
+ {
+ var m = queue.Dequeue();
var friendly = true;
for (var j = 0; friendly && j < caster.Aggressors.Count; ++j)
diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OrcBrute.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OrcBrute.cs
index 34c4c410a..69deb37ae 100644
--- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OrcBrute.cs
+++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Melee/OrcBrute.cs
@@ -1,4 +1,3 @@
-using System.Linq;
using Server.Items;
namespace Server.Mobiles
@@ -120,8 +119,16 @@ namespace Server.Mobiles
}
var eable = GetMobilesInRange(10);
+ var count = 0;
+ foreach (var m in eable)
+ {
+ if (++count == 10)
+ {
+ break;
+ }
+ }
- if (eable.Count() < 10)
+ if (count < 10)
{
BaseCreature orc = new SpawnedOrcishLord { Team = Team };
diff --git a/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerEternal.cs b/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerEternal.cs
index d7f064620..4b78c5108 100644
--- a/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerEternal.cs
+++ b/Projects/UOContent/Mobiles/Monsters/LBR/Meers/MeerEternal.cs
@@ -1,5 +1,5 @@
using System;
-using System.Linq;
+using Server.Collections;
namespace Server.Mobiles
{
@@ -84,45 +84,43 @@ namespace Server.Mobiles
private void DoAreaLeech_Finish()
{
var eable = GetMobilesInRange(6);
- var list = eable.Where(m => CanBeHarmful(m) && IsEnemy(m)).ToList();
+ using var queue = PooledRefQueue.Create();
+ foreach (var m in eable)
+ {
+ if (CanBeHarmful(m) && IsEnemy(m))
+ {
+ queue.Enqueue(m);
+ }
+ }
eable.Free();
- if (list.Count == 0)
+ if (queue.Count == 0)
{
Say(true, "Bah! You have escaped my grasp this time, mortal!");
+ return;
}
- else
+
+ double scalar = queue.Count switch
{
- double scalar;
+ 1 => 0.75,
+ 2 => 0.50,
+ _ => 0.25
+ };
- if (list.Count == 1)
- {
- scalar = 0.75;
- }
- else if (list.Count == 2)
- {
- scalar = 0.50;
- }
- else
- {
- scalar = 0.25;
- }
+ while (queue.Count > 0)
+ {
+ var m = queue.Dequeue();
- for (var i = 0; i < list.Count; ++i)
- {
- var m = list[i];
+ var damage = (int)(m.Hits * scalar) + Utility.RandomMinMax(-5, 5);
- var damage = (int)(m.Hits * scalar) + Utility.RandomMinMax(-5, 5);
+ m.MovingParticles(this, 0x36F4, 1, 0, false, false, 32, 0, 9535, 1, 0, (EffectLayer)255, 0x100);
+ m.MovingParticles(this, 0x0001, 1, 0, false, true, 32, 0, 9535, 9536, 0, (EffectLayer)255, 0);
- m.MovingParticles(this, 0x36F4, 1, 0, false, false, 32, 0, 9535, 1, 0, (EffectLayer)255, 0x100);
- m.MovingParticles(this, 0x0001, 1, 0, false, true, 32, 0, 9535, 9536, 0, (EffectLayer)255, 0);
-
- DoHarmful(m);
- Hits += AOS.Damage(m, this, Math.Max(damage, 1), 100, 0, 0, 0, 0);
- }
-
- Say(true, "If I cannot cleanse thy soul, I will destroy it!");
+ DoHarmful(m);
+ Hits += AOS.Damage(m, this, Math.Max(damage, 1), 100, 0, 0, 0, 0);
}
+
+ Say(true, "If I cannot cleanse thy soul, I will destroy it!");
}
private void DoFocusedLeech(Mobile combatant, string message)
@@ -172,20 +170,28 @@ namespace Server.Mobiles
switch (ability)
{
case 0:
- DoFocusedLeech(combatant, "Thine essence will fill my withering body with strength!");
- break;
+ {
+ DoFocusedLeech(combatant, "Thine essence will fill my withering body with strength!");
+ break;
+ }
case 1:
- DoFocusedLeech(
- combatant,
- "I rebuke thee, worm, and cleanse thy vile spirit of its tainted blood!"
- );
- break;
+ {
+ DoFocusedLeech(
+ combatant,
+ "I rebuke thee, worm, and cleanse thy vile spirit of its tainted blood!"
+ );
+ break;
+ }
case 2:
- DoFocusedLeech(combatant, "I devour your life's essence to strengthen my resolve!");
- break;
+ {
+ DoFocusedLeech(combatant, "I devour your life's essence to strengthen my resolve!");
+ break;
+ }
case 3:
- DoAreaLeech();
- break;
+ {
+ DoAreaLeech();
+ break;
+ }
// TODO: Resurrect ability
}
}
diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/BladeSpirits.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/BladeSpirits.cs
index 0f88661c9..374ae412e 100644
--- a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/BladeSpirits.cs
+++ b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/BladeSpirits.cs
@@ -1,5 +1,6 @@
using System;
-using System.Linq;
+using System.Buffers;
+using Server.Collections;
namespace Server.Mobiles
{
@@ -72,17 +73,28 @@ namespace Server.Mobiles
if (Core.SE && Summoned)
{
var eable = GetMobilesInRange(5);
- var spiritsOrVortexes = eable
- .Where(m => m is EnergyVortex or BladeSpirits && ((BaseCreature)m).Summoned)
- .ToList();
-
+ using var queue = PooledRefQueue.Create();
+ foreach (var m in eable)
+ {
+ if (m is EnergyVortex or BladeSpirits && ((BaseCreature)m).Summoned)
+ {
+ queue.Enqueue(m);
+ }
+ }
eable.Free();
- while (spiritsOrVortexes.Count > 6)
+ var amount = queue.Count - 6;
+ if (amount > 0)
{
- var random = spiritsOrVortexes.RandomElement();
- Dispel(random);
- spiritsOrVortexes.Remove(random);
+ var mobs = queue.ToPooledArray();
+ mobs.Shuffle();
+
+ while (amount > 0)
+ {
+ Dispel(mobs[amount--]);
+ }
+
+ ArrayPool.Shared.Return(mobs);
}
}
diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/EnergyVortex.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/EnergyVortex.cs
index f081cedff..145b85b6d 100644
--- a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/EnergyVortex.cs
+++ b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/EnergyVortex.cs
@@ -1,5 +1,6 @@
using System;
-using System.Linq;
+using System.Buffers;
+using Server.Collections;
namespace Server.Mobiles
{
@@ -78,17 +79,28 @@ namespace Server.Mobiles
if (Core.SE && Summoned)
{
var eable = GetMobilesInRange(5);
- var spiritsOrVortexes = eable
- .Where(m => m is EnergyVortex or BladeSpirits && ((BaseCreature)m).Summoned)
- .ToList();
-
+ using var queue = PooledRefQueue.Create();
+ foreach (var m in eable)
+ {
+ if (m is EnergyVortex or BladeSpirits && ((BaseCreature)m).Summoned)
+ {
+ queue.Enqueue(m);
+ }
+ }
eable.Free();
- while (spiritsOrVortexes.Count > 6)
+ var amount = queue.Count - 6;
+ if (amount > 0)
{
- var random = spiritsOrVortexes.RandomElement();
- Dispel(random);
- spiritsOrVortexes.Remove(random);
+ var mobs = queue.ToPooledArray();
+ mobs.Shuffle();
+
+ while (amount > 0)
+ {
+ Dispel(mobs[amount--]);
+ }
+
+ ArrayPool.Shared.Return(mobs);
}
}
diff --git a/Projects/UOContent/Mobiles/Special/Harrower.cs b/Projects/UOContent/Mobiles/Special/Harrower.cs
index cd6ba03c9..ad0779270 100644
--- a/Projects/UOContent/Mobiles/Special/Harrower.cs
+++ b/Projects/UOContent/Mobiles/Special/Harrower.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using Server.Items;
using Server.Spells;
@@ -570,8 +569,17 @@ namespace Server.Mobiles
return;
}
- var toTeleport = m_Owner.GetMobilesInRange(16)
- .FirstOrDefault(mob => mob != m_Owner && mob.Player && m_Owner.CanBeHarmful(mob) && m_Owner.CanSee(mob));
+ var eable = m_Owner.GetMobilesInRange(16);
+ Mobile toTeleport = null;
+ foreach (var m in eable)
+ {
+ if (m != m_Owner && m.Player && m_Owner.CanBeHarmful(m) && m_Owner.CanSee(m))
+ {
+ toTeleport = m;
+ break;
+ }
+ }
+ eable.Free();
if (toTeleport == null)
{
@@ -602,33 +610,31 @@ namespace Server.Mobiles
}
}
- var m = toTeleport;
+ var from = toTeleport.Location;
- var from = m.Location;
-
- m.Location = to;
+ toTeleport.Location = to;
SpellHelper.Turn(m_Owner, toTeleport);
SpellHelper.Turn(toTeleport, m_Owner);
- m.ProcessDelta();
+ toTeleport.ProcessDelta();
Effects.SendLocationParticles(
- EffectItem.Create(from, m.Map, EffectItem.DefaultDuration),
+ EffectItem.Create(from, toTeleport.Map, EffectItem.DefaultDuration),
0x3728,
10,
10,
2023
);
Effects.SendLocationParticles(
- EffectItem.Create(to, m.Map, EffectItem.DefaultDuration),
+ EffectItem.Create(to, toTeleport.Map, EffectItem.DefaultDuration),
0x3728,
10,
10,
5023
);
- m.PlaySound(0x1FE);
+ toTeleport.PlaySound(0x1FE);
m_Owner.Combatant = toTeleport;
}
diff --git a/Projects/UOContent/Multis/Houses/BaseHouse.cs b/Projects/UOContent/Multis/Houses/BaseHouse.cs
index 66cef968e..272bba2cf 100644
--- a/Projects/UOContent/Multis/Houses/BaseHouse.cs
+++ b/Projects/UOContent/Multis/Houses/BaseHouse.cs
@@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Server.Accounting;
+using Server.Collections;
using Server.ContextMenus;
using Server.Ethics;
using Server.Guilds;
@@ -3227,16 +3228,21 @@ namespace Server.Multis
private void FixLockdowns_Sandbox()
{
- var conts = LockDowns?.Where(item => item is Container).ToList();
-
- if (conts == null)
+ if (LockDowns?.Count > 0)
{
- return;
- }
+ using var queue = PooledRefQueue
- .Create();
+ foreach (var item in LockDowns)
+ {
+ if (item is Container)
+ {
+ queue.Enqueue(item);
+ }
+ }
- foreach (var cont in conts)
- {
- SetLockdown(cont, true, true);
+ while (queue.Count > 0)
+ {
+ SetLockdown(queue.Dequeue(), true, true);
+ }
}
}
diff --git a/Projects/UOContent/Skills/SpiritSpeak.cs b/Projects/UOContent/Skills/SpiritSpeak.cs
index 69b40fcf8..4a2ac0ab0 100644
--- a/Projects/UOContent/Skills/SpiritSpeak.cs
+++ b/Projects/UOContent/Skills/SpiritSpeak.cs
@@ -1,5 +1,4 @@
using System;
-using System.Linq;
using Server.Items;
using Server.Network;
using Server.Spells;
@@ -134,7 +133,15 @@ namespace Server.SkillHandlers
public override void OnCast()
{
var eable = Caster.GetItemsInRange(3);
- var toChannel = eable.FirstOrDefault(corpse => !corpse.Channeled);
+ Corpse toChannel = null;
+ foreach (var corpse in eable)
+ {
+ if (!corpse.Channeled)
+ {
+ toChannel = corpse;
+ break;
+ }
+ }
eable.Free();
var min = 1 + (int)(Caster.Skills.SpiritSpeak.Value * 0.25);
diff --git a/Projects/UOContent/Skills/Tracking/Tracking.cs b/Projects/UOContent/Skills/Tracking/Tracking.cs
index 8e691702a..4cd3f52ad 100644
--- a/Projects/UOContent/Skills/Tracking/Tracking.cs
+++ b/Projects/UOContent/Skills/Tracking/Tracking.cs
@@ -1,6 +1,5 @@
using System;
using System.Collections.Generic;
-using System.Linq;
using Server.Gumps;
using Server.Mobiles;
using Server.Network;
@@ -210,13 +209,18 @@ namespace Server.SkillHandlers
var range = 10 + (int)(from.Skills.Tracking.Value / 10);
- var list = from.GetMobilesInRange(range)
- .Where(
- m => m != from && (!Core.AOS || m.Alive) &&
- (!m.Hidden || m.AccessLevel == AccessLevel.Player || from.AccessLevel > m.AccessLevel) &&
- check(m) && CheckDifficulty(from, m)
- )
- .ToList();
+ var eable = from.GetMobilesInRange(range);
+ var list = new List();
+ foreach (var m in eable)
+ {
+ if (m != from && (!Core.AOS || m.Alive) &&
+ (!m.Hidden || m.AccessLevel == AccessLevel.Player || from.AccessLevel > m.AccessLevel) &&
+ check(m) && CheckDifficulty(from, m))
+ {
+ list.Add(m);
+ }
+ }
+ eable.Free();
if (list.Count > 0)
{
diff --git a/Projects/UOContent/Spells/Base/SpellHelper.cs b/Projects/UOContent/Spells/Base/SpellHelper.cs
index ea9ec4131..6cd1a8f5b 100644
--- a/Projects/UOContent/Spells/Base/SpellHelper.cs
+++ b/Projects/UOContent/Spells/Base/SpellHelper.cs
@@ -256,14 +256,14 @@ namespace Server.Spells
return false;
}
- public static bool GetEastToWest(IPoint3D from,IPoint3D target)
+ public static bool GetEastToWest(Point3D from, Point3D target)
{
var dx = from.X - target.X;
var dy = from.Y - target.Y;
var rx = (dx - dy) * 44;
var ry = (dx + dy) * 44;
- return (rx >= 0 && ry < 0) || (ry >= 0 && rx < 0);
+ return rx >= 0 && ry < 0 || ry >= 0 && rx < 0;
}
public static bool CanRevealCaster(Mobile m) => m is BaseCreature { Controlled: false };
diff --git a/Projects/UOContent/Spells/Bushido/MomentumStrike.cs b/Projects/UOContent/Spells/Bushido/MomentumStrike.cs
index 5513c5429..3fbe7b592 100644
--- a/Projects/UOContent/Spells/Bushido/MomentumStrike.cs
+++ b/Projects/UOContent/Spells/Bushido/MomentumStrike.cs
@@ -1,4 +1,5 @@
using System.Linq;
+using Server.Collections;
namespace Server.Spells.Bushido
{
@@ -21,12 +22,17 @@ namespace Server.Spells.Bushido
var weapon = attacker.Weapon;
- var targets = attacker.GetMobilesInRange(weapon.MaxRange)
- .Where(m => m != defender)
- .Where(m => m.Combatant == attacker)
- .ToList();
+ var eable = attacker.GetMobilesInRange(weapon.MaxRange);
+ using var queue = PooledRefQueue.Create();
+ foreach (var m in eable)
+ {
+ if (m != defender && m.Combatant == attacker)
+ {
+ queue.Enqueue(m);
+ }
+ }
- if (targets.Count <= 0)
+ if (queue.Count <= 0)
{
attacker.SendLocalizedMessage(1063123); // There are no valid targets to attack!
return;
@@ -37,7 +43,7 @@ namespace Server.Spells.Bushido
return;
}
- var target = targets.RandomElement();
+ Mobile target = queue.PeekRandom();
var damageBonus = attacker.Skills.Bushido.Value / 100.0;
@@ -47,7 +53,7 @@ namespace Server.Spells.Bushido
}
attacker.SendLocalizedMessage(1063171); // You transfer the momentum of your weapon into another enemy!
- target.SendLocalizedMessage(1063172); // You were hit by the momentum of a Samurai's weapon!
+ target!.SendLocalizedMessage(1063172); // You were hit by the momentum of a Samurai's weapon!
target.FixedParticles(0x37B9, 1, 4, 0x251D, 0, 0, EffectLayer.Waist);
diff --git a/Projects/UOContent/Spells/Chivalry/DispelEvil.cs b/Projects/UOContent/Spells/Chivalry/DispelEvil.cs
index d70ef6859..b09d6a2dd 100644
--- a/Projects/UOContent/Spells/Chivalry/DispelEvil.cs
+++ b/Projects/UOContent/Spells/Chivalry/DispelEvil.cs
@@ -1,5 +1,5 @@
using System;
-using System.Linq;
+using Server.Collections;
using Server.Items;
using Server.Mobiles;
using Server.Spells.Necromancy;
@@ -46,11 +46,21 @@ namespace Server.Spells.Chivalry
var chiv = Caster.Skills.Chivalry.Value;
- var targets = Caster.GetMobilesInRange(8)
- .Where(m => Caster != m && SpellHelper.ValidIndirectTarget(Caster, m) && Caster.CanBeHarmful(m, false));
-
- foreach (var m in targets)
+ var eable = Caster.GetMobilesInRange(8);
+ using var queue = PooledRefQueue.Create();
+ foreach (var m in eable)
{
+ if (Caster != m && SpellHelper.ValidIndirectTarget(Caster, m) && Caster.CanBeHarmful(m, false))
+ {
+ queue.Enqueue(m);
+ }
+ }
+ eable.Free();
+
+ while (queue.Count > 0)
+ {
+ var m = queue.Dequeue();
+
if (m is BaseCreature bc)
{
if (bc.Summoned && !bc.IsAnimatedDead)
diff --git a/Projects/UOContent/Spells/Eighth/Earthquake.cs b/Projects/UOContent/Spells/Eighth/Earthquake.cs
index ffa480feb..f821a49fc 100644
--- a/Projects/UOContent/Spells/Eighth/Earthquake.cs
+++ b/Projects/UOContent/Spells/Eighth/Earthquake.cs
@@ -1,5 +1,5 @@
using System;
-using System.Linq;
+using Server.Collections;
namespace Server.Spells.Eighth
{
@@ -37,14 +37,21 @@ namespace Server.Spells.Eighth
return;
}
- var targets = Caster.GetMobilesInRange(1 + (int)(Caster.Skills.Magery.Value / 15.0))
- .Where(
- m => Caster != m && SpellHelper.ValidIndirectTarget(Caster, m) && Caster.CanBeHarmful(m, false) &&
- (!Core.AOS || Caster.InLOS(m))
- );
-
- foreach (var m in targets)
+ var eable = Caster.GetMobilesInRange(1 + (int)(Caster.Skills.Magery.Value / 15.0));
+ using var queue = PooledRefQueue.Create();
+ foreach (var m in eable)
{
+ if (Caster != m && SpellHelper.ValidIndirectTarget(Caster, m) && Caster.CanBeHarmful(m, false) &&
+ (!Core.AOS || Caster.InLOS(m)))
+ {
+ queue.Enqueue(m);
+ }
+ }
+
+ while (queue.Count > 0)
+ {
+ var m = queue.Dequeue();
+
int damage;
if (Core.AOS)
diff --git a/Projects/UOContent/Spells/Fifth/PoisonField.cs b/Projects/UOContent/Spells/Fifth/PoisonField.cs
index 8a55f0d0b..80bada423 100644
--- a/Projects/UOContent/Spells/Fifth/PoisonField.cs
+++ b/Projects/UOContent/Spells/Fifth/PoisonField.cs
@@ -30,22 +30,21 @@ namespace Server.Spells.Fifth
if (SpellHelper.CheckTown(p, Caster) && CheckSequence())
{
SpellHelper.Turn(Caster, p);
-
SpellHelper.GetSurfaceTop(ref p);
- var eastToWest = SpellHelper.GetEastToWest(Caster.Location, p);
+ var loc = new Point3D(p);
+ var eastToWest = SpellHelper.GetEastToWest(Caster.Location, loc);
- Effects.PlaySound(new Point3D(p), Caster.Map, 0x20B);
+ Effects.PlaySound(loc, Caster.Map, 0x20B);
var itemID = eastToWest ? 0x3915 : 0x3922;
-
var duration = TimeSpan.FromSeconds(3 + Caster.Skills.Magery.Fixed / 25);
for (var i = -2; i <= 2; ++i)
{
- var loc = new Point3D(eastToWest ? p.X + i : p.X, eastToWest ? p.Y : p.Y + i, p.Z);
+ var targetLoc = new Point3D(eastToWest ? loc.X + i : loc.X, eastToWest ? loc.Y : loc.Y + i, loc.Z);
- new InternalItem(itemID, loc, Caster, Caster.Map, duration, i);
+ new InternalItem(itemID, targetLoc, Caster, Caster.Map, duration, i);
}
}
diff --git a/Projects/UOContent/Spells/Fourth/FireField.cs b/Projects/UOContent/Spells/Fourth/FireField.cs
index c3979af90..91ca1b8bb 100644
--- a/Projects/UOContent/Spells/Fourth/FireField.cs
+++ b/Projects/UOContent/Spells/Fourth/FireField.cs
@@ -33,28 +33,23 @@ namespace Server.Spells.Fourth
SpellHelper.GetSurfaceTop(ref p);
- var eastToWest = SpellHelper.GetEastToWest(Caster.Location, p);
+ var loc = new Point3D(p);
- Effects.PlaySound(new Point3D(p), Caster.Map, 0x20C);
+ var eastToWest = SpellHelper.GetEastToWest(Caster.Location, loc);
+
+ Effects.PlaySound(loc, Caster.Map, 0x20C);
var itemID = eastToWest ? 0x398C : 0x3996;
- TimeSpan duration;
-
- if (Core.AOS)
- {
- duration = TimeSpan.FromSeconds((15 + Caster.Skills.Magery.Fixed / 5.0) / 4.0);
- }
- else
- {
- duration = TimeSpan.FromSeconds(4.0 + Caster.Skills.Magery.Value * 0.5);
- }
+ var duration = Core.AOS
+ ? TimeSpan.FromSeconds((15 + Caster.Skills.Magery.Fixed / 5.0) / 4.0)
+ : TimeSpan.FromSeconds(4.0 + Caster.Skills.Magery.Value * 0.5);
for (var i = -2; i <= 2; ++i)
{
- var loc = new Point3D(eastToWest ? p.X + i : p.X, eastToWest ? p.Y : p.Y + i, p.Z);
+ var targetLoc = new Point3D(eastToWest ? loc.X + i : loc.X, eastToWest ? loc.Y : loc.Y + i, loc.Z);
- new FireFieldItem(itemID, loc, Caster, Caster.Map, duration, i);
+ new FireFieldItem(itemID, targetLoc, Caster, Caster.Map, duration, i);
}
}
diff --git a/Projects/UOContent/Spells/Seventh/EnergyField.cs b/Projects/UOContent/Spells/Seventh/EnergyField.cs
index bf5c5c019..5c5900a08 100644
--- a/Projects/UOContent/Spells/Seventh/EnergyField.cs
+++ b/Projects/UOContent/Spells/Seventh/EnergyField.cs
@@ -30,42 +30,35 @@ namespace Server.Spells.Seventh
if (SpellHelper.CheckTown(p, Caster) && CheckSequence())
{
SpellHelper.Turn(Caster, p);
-
SpellHelper.GetSurfaceTop(ref p);
- var eastToWest = SpellHelper.GetEastToWest(Caster.Location, p);
+ var loc = new Point3D(p);
- Effects.PlaySound(new Point3D(p), Caster.Map, 0x20B);
+ var eastToWest = SpellHelper.GetEastToWest(Caster.Location, loc);
- TimeSpan duration;
+ Effects.PlaySound(loc, Caster.Map, 0x20B);
- if (Core.AOS)
- {
- duration = TimeSpan.FromSeconds((15 + Caster.Skills.Magery.Fixed / 5) / 7.0);
- }
- else
- {
- // (28% of magery) + 2.0 seconds
- duration = TimeSpan.FromSeconds(Caster.Skills.Magery.Value * 0.28 + 2.0);
- }
+ TimeSpan duration = Core.AOS
+ ? TimeSpan.FromSeconds((15 + Caster.Skills.Magery.Fixed / 5) / 7.0)
+ : TimeSpan.FromSeconds(Caster.Skills.Magery.Value * 0.28 + 2.0);
var itemID = eastToWest ? 0x3946 : 0x3956;
for (var i = -2; i <= 2; ++i)
{
- var loc = new Point3D(eastToWest ? p.X + i : p.X, eastToWest ? p.Y : p.Y + i, p.Z);
- var canFit = SpellHelper.AdjustField(ref loc, Caster.Map, 12, false);
+ var targetLoc = new Point3D(eastToWest ? loc.X + i : loc.X, eastToWest ? loc.Y : loc.Y + i, loc.Z);
+ var canFit = SpellHelper.AdjustField(ref targetLoc, Caster.Map, 12, false);
if (!canFit)
{
continue;
}
- Item item = new InternalItem(loc, Caster.Map, duration, itemID, Caster);
+ Item item = new InternalItem(targetLoc, Caster.Map, duration, itemID, Caster);
item.ProcessDelta();
Effects.SendLocationParticles(
- EffectItem.Create(loc, Caster.Map, EffectItem.DefaultDuration),
+ EffectItem.Create(targetLoc, Caster.Map, EffectItem.DefaultDuration),
0x376A,
9,
10,
diff --git a/Projects/UOContent/Spells/Sixth/ParalyzeField.cs b/Projects/UOContent/Spells/Sixth/ParalyzeField.cs
index 8a018caba..0edd387b3 100644
--- a/Projects/UOContent/Spells/Sixth/ParalyzeField.cs
+++ b/Projects/UOContent/Spells/Sixth/ParalyzeField.cs
@@ -29,12 +29,12 @@ namespace Server.Spells.Sixth
if (SpellHelper.CheckTown(p, Caster) && CheckSequence())
{
SpellHelper.Turn(Caster, p);
-
SpellHelper.GetSurfaceTop(ref p);
- var eastToWest = SpellHelper.GetEastToWest(Caster.Location, p);
+ var loc = new Point3D(p);
+ var eastToWest = SpellHelper.GetEastToWest(Caster.Location, loc);
- Effects.PlaySound(new Point3D(p), Caster.Map, 0x20B);
+ Effects.PlaySound(loc, Caster.Map, 0x20B);
var itemID = eastToWest ? 0x3967 : 0x3979;
@@ -42,18 +42,18 @@ namespace Server.Spells.Sixth
for (var i = -2; i <= 2; ++i)
{
- var loc = new Point3D(eastToWest ? p.X + i : p.X, eastToWest ? p.Y : p.Y + i, p.Z);
+ var targetLoc = new Point3D(eastToWest ? loc.X + i : loc.X, eastToWest ? loc.Y : loc.Y + i, loc.Z);
- if (!SpellHelper.AdjustField(ref loc, Caster.Map, 12, false))
+ if (!SpellHelper.AdjustField(ref targetLoc, Caster.Map, 12, false))
{
continue;
}
- Item item = new InternalItem(Caster, itemID, loc, Caster.Map, duration);
+ Item item = new InternalItem(Caster, itemID, targetLoc, Caster.Map, duration);
item.ProcessDelta();
Effects.SendLocationParticles(
- EffectItem.Create(loc, Caster.Map, EffectItem.DefaultDuration),
+ EffectItem.Create(targetLoc, Caster.Map, EffectItem.DefaultDuration),
0x376A,
9,
10,
diff --git a/Projects/UOContent/Spells/Third/WallOfStone.cs b/Projects/UOContent/Spells/Third/WallOfStone.cs
index 98d239a07..094374a86 100644
--- a/Projects/UOContent/Spells/Third/WallOfStone.cs
+++ b/Projects/UOContent/Spells/Third/WallOfStone.cs
@@ -30,14 +30,16 @@ namespace Server.Spells.Third
SpellHelper.GetSurfaceTop(ref p);
- var eastToWest = SpellHelper.GetEastToWest(Caster.Location, p);
+ var loc = new Point3D(p);
- Effects.PlaySound(new Point3D(p), Caster.Map, 0x1F6);
+ var eastToWest = SpellHelper.GetEastToWest(Caster.Location, loc);
+
+ Effects.PlaySound(loc, Caster.Map, 0x1F6);
for (var i = -1; i <= 1; ++i)
{
- var loc = new Point3D(eastToWest ? p.X + i : p.X, eastToWest ? p.Y : p.Y + i, p.Z);
- var canFit = SpellHelper.AdjustField(ref loc, Caster.Map, 22, true);
+ var targetLoc = new Point3D(eastToWest ? p.X + i : p.X, eastToWest ? p.Y : p.Y + i, p.Z);
+ var canFit = SpellHelper.AdjustField(ref targetLoc, Caster.Map, 22, true);
// Effects.SendLocationParticles( EffectItem.Create( loc, Caster.Map, EffectItem.DefaultDuration ), 0x376A, 9, 10, 5025 );
@@ -46,7 +48,7 @@ namespace Server.Spells.Third
continue;
}
- Item item = new InternalItem(loc, Caster.Map, Caster);
+ Item item = new InternalItem(targetLoc, Caster.Map, Caster);
Effects.SendLocationParticles(item, 0x376A, 9, 10, 5025);