fix: Cleans up LINQ calls. (#965)
- [X] Removes several `ToList()` uses with `PooledRefQueue` - [X] Adds a `PeekRandom` to PooledRefQueue - [X] Updates EV/BS so they dispel each other in a more efficient manner. - [X] Fixes Firebomb so it works like a normal firefield. - [X] Fixes field spells so they aren't unnecessarily using a Point3D ref more than necessary. - [X] Removes extra allocation in campfire by using reverse loop. - [X] Removes other LINQ calls that aren't needed.
This commit is contained in:
parent
daf89686d1
commit
23532db603
40 changed files with 585 additions and 447 deletions
|
|
@ -11,7 +11,7 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
|
||||
<PackageReference Include="NetFabric.Hyperlinq" Version="3.0.0-beta48" />
|
||||
<PackageReference Include="StructLinq" Version="0.27.0" />
|
||||
<PackageReference Include="StructLinq" Version="0.27.1" />
|
||||
<ProjectReference Include="..\Server\Server.csproj" />
|
||||
<ProjectReference Include="..\UOContent\UOContent.csproj" />
|
||||
</ItemGroup>
|
||||
|
|
|
|||
|
|
@ -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<BItem> BItems { get; set; } = new List<BItem>();
|
||||
public List<Mobile> Mobiles { get; set; } = new List<Mobile>();
|
||||
public List<BItem> BItems { get; set; } = new();
|
||||
public List<Mobile> Mobiles { get; set; } = new();
|
||||
}
|
||||
|
||||
public struct BItemWhereHyper : NetFabric.Hyperlinq.IFunction<BItem, bool>
|
||||
|
|
|
|||
|
|
@ -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<BItem> BItems { get; set; } = new List<BItem>();
|
||||
public List<BItem> BItems { get; set; } = new();
|
||||
}
|
||||
|
||||
public struct BItemWhere<T> : StructLinq.IFunction<BItem, bool> where T : BItem
|
||||
|
|
|
|||
|
|
@ -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<Mobile> Mobiles { get; set; } = new List<Mobile>();
|
||||
public List<Mobile> Mobiles { get; set; } = new();
|
||||
}
|
||||
|
||||
public struct MobileWhereHyper<T> : NetFabric.Hyperlinq.IFunction<Mobile, bool> where T : Mobile
|
||||
|
|
|
|||
|
|
@ -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<BaseMulti> Multis { get; set; } = new List<BaseMulti>();
|
||||
public List<BaseMulti> Multis { get; set; } = new();
|
||||
}
|
||||
|
||||
public struct MultiWhereHyper : NetFabric.Hyperlinq.IFunction<BaseMulti, bool>
|
||||
public struct MultiWhereHyper : IFunction<BaseMulti, bool>
|
||||
{
|
||||
private readonly Rectangle2D bounds;
|
||||
|
||||
|
|
|
|||
|
|
@ -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<BaseMulti> Multis { get; set; } = new List<BaseMulti>();
|
||||
public List<BaseMulti> Multis { get; set; } = new();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="Moq" Version="4.16.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
||||
<PackageReference Include="Moq" Version="4.17.2" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
|
||||
<PackageReference Include="Zlib.Bindings" Version="1.8.0" />
|
||||
|
|
|
|||
102
Projects/Server.Tests/Tests/Collections/PooledRefQueueTests.cs
Normal file
102
Projects/Server.Tests/Tests/Collections/PooledRefQueueTests.cs
Normal file
|
|
@ -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<IRandomSource> mockRng = new Mock<IRandomSource>();
|
||||
mockRng
|
||||
.Setup(rng => rng.Next(It.IsAny<int>()))
|
||||
.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<int>.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<int>.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<int>.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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -920,6 +920,7 @@ namespace Server
|
|||
return total + bonus;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Shuffle<T>(this IList<T> list)
|
||||
{
|
||||
var count = list.Count;
|
||||
|
|
@ -930,6 +931,7 @@ namespace Server
|
|||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Shuffle<T>(this Span<T> 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));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Random pink, blue, green, orange, red or yellow hue
|
||||
/// </summary>
|
||||
|
|
|
|||
|
|
@ -3,8 +3,8 @@
|
|||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
|
||||
<PackageReference Include="Moq" Version="4.16.1" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
||||
<PackageReference Include="Moq" Version="4.17.2" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" />
|
||||
<ProjectReference Include="..\Server\Server.csproj" />
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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<ToTTurnInGump>(); // 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<ItemTileButtonInfo> buttons) : base(
|
||||
1071012,
|
||||
buttons.ToList<ImageTileButtonInfo>()
|
||||
) // 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<ImageTileButtonInfo> buttons)
|
||||
: base(1071012, buttons) => m_Collector = collector;
|
||||
|
||||
public static List<ItemTileButtonInfo> FindRedeemableItems(Mobile m)
|
||||
public static List<ImageTileButtonInfo> FindRedeemableItems(Mobile m)
|
||||
{
|
||||
var pack = m.Backpack;
|
||||
if (pack == null)
|
||||
{
|
||||
return new List<ItemTileButtonInfo>();
|
||||
return new List<ImageTileButtonInfo>();
|
||||
}
|
||||
|
||||
var buttons = new List<ItemTileButtonInfo>();
|
||||
var buttons = new List<ImageTileButtonInfo>();
|
||||
|
||||
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<ToTTurnInGump>(); // 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
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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<HalloweenPumpkin>().Count();
|
||||
var eable = map.GetItemsInBounds<HalloweenPumpkin>(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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<Mobile> 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<Mobile>.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<Mobile> m_Burning;
|
||||
private readonly DateTime m_Expire;
|
||||
private readonly Mobile m_LitBy;
|
||||
private TimerExecutionToken _timerToken;
|
||||
|
||||
public FirebombField(Mobile litBy, List<Mobile> 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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<Mobile>.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;
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Mobile>.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!
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Item>.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--;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Mobile>.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)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
using System.Linq;
|
||||
using Server.Items;
|
||||
|
||||
namespace Server.Mobiles
|
||||
|
|
@ -120,8 +119,16 @@ namespace Server.Mobiles
|
|||
}
|
||||
|
||||
var eable = GetMobilesInRange<OrcishLord>(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 };
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Mobile>.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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Mobile>.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<Mobile>.Shared.Return(mobs);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Mobile>.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<Mobile>.Shared.Return(mobs);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<Item>.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Corpse>(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);
|
||||
|
|
|
|||
|
|
@ -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<Mobile>();
|
||||
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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -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<Mobile>.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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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<Mobile>.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)
|
||||
|
|
|
|||
|
|
@ -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<Mobile>.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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue