diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 08b49e256..5e8c8ad9b 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -3,7 +3,7 @@ "isRoot": true, "tools": { "modernuoschemagenerator": { - "version": "2.12.20", + "version": "2.13.0", "commands": [ "ModernUOSchemaGenerator" ] diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index c2f1c3b2f..15d42fceb 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -23,7 +23,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 # avoid shallow clone so nbgv can do its work. - - name: Install .NET 9 + - name: Install .NET uses: actions/setup-dotnet@v4 with: global-json-file: global.json @@ -54,20 +54,14 @@ jobs: - container: ubuntu:jammy name: Ubuntu 22 packageManager: apt - - container: ubuntu:focal - name: Ubuntu 20 + - container: debian:trixie + name: Debian 13 packageManager: apt - container: debian:bookworm name: Debian 12 packageManager: apt - - container: debian:bullseye - name: Debian 11 - packageManager: apt - - container: fedora:39 - name: Fedora 39 - packageManager: dnf - - container: fedora:40 - name: Fedora 40 + - container: fedora:42 + name: Fedora 42 packageManager: dnf - container: quay.io/centos/centos:stream9 name: CentOS 9 Stream @@ -86,7 +80,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 # avoid shallow clone so nbgv can do its work. - - name: Install .NET 9 + - name: Install .NET uses: actions/setup-dotnet@v4 with: global-json-file: global.json diff --git a/.github/workflows/create-release.yml b/.github/workflows/create-release.yml index 0e8334e59..aa02e4489 100644 --- a/.github/workflows/create-release.yml +++ b/.github/workflows/create-release.yml @@ -14,7 +14,7 @@ jobs: with: fetch-depth: 0 # avoid shallow clone so nbgv can do its work. token: ${{ secrets.PERSONAL_ACCESS_TOKEN }} - - name: Install .NET 9 + - name: Install .NET uses: actions/setup-dotnet@v4 with: global-json-file: global.json diff --git a/Directory.Build.props b/Directory.Build.props index a9eae192f..57f39a926 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -3,9 +3,9 @@ Kamron Batman ModernUO - 2019-2024 - net9.0 - 13 + 2019-2025 + net10.0 + 14 true true NU1603 @@ -62,11 +62,11 @@ latest - + - + - 3.7.115 + 3.9.50 all diff --git a/Projects/Server.Tests/Server.Tests.csproj b/Projects/Server.Tests/Server.Tests.csproj index 8da175d26..0f913dd48 100644 --- a/Projects/Server.Tests/Server.Tests.csproj +++ b/Projects/Server.Tests/Server.Tests.csproj @@ -5,9 +5,9 @@ Server.Tests - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Projects/Server.Tests/Tests/Buffers/SpanReaderTests.cs b/Projects/Server.Tests/Tests/Buffers/SpanReaderTests.cs new file mode 100644 index 000000000..9a68576c8 --- /dev/null +++ b/Projects/Server.Tests/Tests/Buffers/SpanReaderTests.cs @@ -0,0 +1,597 @@ +using System; +using System.Buffers; +using System.IO; +using Xunit; + +namespace Server.Tests; + +public class SpanReaderTests +{ + [Fact] + public void TestReadByte() + { + ReadOnlySpan buffer = [0x12, 0x34]; + var reader = new SpanReader(buffer); + + Assert.Equal(0x12, reader.ReadByte()); + Assert.Equal(0x34, reader.ReadByte()); + Assert.Equal(2, reader.Position); + } + + [Fact] + public void TestReadByteAtEnd() + { + Assert.Throws( + () => + { + ReadOnlySpan buffer = [0x12]; + var reader = new SpanReader(buffer); + reader.ReadByte(); + reader.ReadByte(); + } + ); + } + + [Fact] + public void TestReadBoolean() + { + ReadOnlySpan buffer = [0, 1, 2, 255]; + var reader = new SpanReader(buffer); + + Assert.False(reader.ReadBoolean()); + Assert.True(reader.ReadBoolean()); + Assert.True(reader.ReadBoolean()); + Assert.True(reader.ReadBoolean()); + Assert.Equal(4, reader.Position); + } + + [Fact] + public void TestReadSByte() + { + ReadOnlySpan buffer = [0xFF, 0x7F]; + var reader = new SpanReader(buffer); + + Assert.Equal(-1, reader.ReadSByte()); + Assert.Equal(127, reader.ReadSByte()); + Assert.Equal(2, reader.Position); + } + + [Fact] + public void TestReadInt16BigEndian() + { + ReadOnlySpan buffer = [0x12, 0x34]; + var reader = new SpanReader(buffer); + + Assert.Equal(0x1234, reader.ReadInt16()); + Assert.Equal(2, reader.Position); + } + + [Fact] + public void TestReadInt16LittleEndian() + { + ReadOnlySpan buffer = [0x34, 0x12]; + var reader = new SpanReader(buffer); + + Assert.Equal(0x1234, reader.ReadInt16LE()); + Assert.Equal(2, reader.Position); + } + + [Fact] + public void TestReadInt16AtEnd() + { + Assert.Throws( + () => + { + ReadOnlySpan buffer = [0x12]; + var reader = new SpanReader(buffer); + reader.ReadInt16(); + } + ); + } + + [Fact] + public void TestReadUInt16BigEndian() + { + ReadOnlySpan buffer = [0x12, 0x34]; + var reader = new SpanReader(buffer); + + Assert.Equal((ushort)0x1234, reader.ReadUInt16()); + Assert.Equal(2, reader.Position); + } + + [Fact] + public void TestReadUInt16LittleEndian() + { + ReadOnlySpan buffer = [0x34, 0x12]; + var reader = new SpanReader(buffer); + + Assert.Equal((ushort)0x1234, reader.ReadUInt16LE()); + Assert.Equal(2, reader.Position); + } + + [Fact] + public void TestReadInt32BigEndian() + { + ReadOnlySpan buffer = [0x12, 0x34, 0x56, 0x78]; + var reader = new SpanReader(buffer); + + Assert.Equal(0x12345678, reader.ReadInt32()); + Assert.Equal(4, reader.Position); + } + + [Fact] + public void TestReadInt32AtEnd() + { + Assert.Throws( + () => + { + ReadOnlySpan buffer = [0x12, 0x34, 0x56]; + var reader = new SpanReader(buffer); + reader.ReadInt32(); + } + ); + } + + [Fact] + public void TestReadUInt32BigEndian() + { + ReadOnlySpan buffer = [0x12, 0x34, 0x56, 0x78]; + var reader = new SpanReader(buffer); + + Assert.Equal(0x12345678u, reader.ReadUInt32()); + Assert.Equal(4, reader.Position); + } + + [Fact] + public void TestReadUInt32LittleEndian() + { + ReadOnlySpan buffer = [0x78, 0x56, 0x34, 0x12]; + var reader = new SpanReader(buffer); + + Assert.Equal(0x12345678u, reader.ReadUInt32LE()); + Assert.Equal(4, reader.Position); + } + + [Fact] + public void TestReadInt64BigEndian() + { + ReadOnlySpan buffer = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]; + var reader = new SpanReader(buffer); + + Assert.Equal(0x123456789ABCDEF0L, reader.ReadInt64()); + Assert.Equal(8, reader.Position); + } + + [Fact] + public void TestReadInt64AtEnd() + { + Assert.Throws( + () => + { + ReadOnlySpan buffer = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE]; + var reader = new SpanReader(buffer); + reader.ReadInt64(); + } + ); + } + + [Fact] + public void TestReadUInt64BigEndian() + { + ReadOnlySpan buffer = [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]; + var reader = new SpanReader(buffer); + + Assert.Equal(0x123456789ABCDEF0UL, reader.ReadUInt64()); + Assert.Equal(8, reader.Position); + } + + [Fact] + public void TestReadAsciiString() + { + ReadOnlySpan buffer = "Hello"u8; + var reader = new SpanReader(buffer); + + var result = reader.ReadAscii(); + + Assert.Equal("Hello", result); + Assert.Equal(5, reader.Position); + } + + [Fact] + public void TestReadAsciiStringWithNull() + { + ReadOnlySpan buffer = "Hel\0o"u8; + var reader = new SpanReader(buffer); + + var result = reader.ReadAscii(); + + Assert.Equal("Hel", result); + Assert.Equal(4, reader.Position); + } + + [Fact] + public void TestReadAsciiStringFixedLength() + { + ReadOnlySpan buffer = "Hello\0\0\0\0\0"u8; + var reader = new SpanReader(buffer); + + var result = reader.ReadAscii(10); + + Assert.Equal("Hello", result); + Assert.Equal(10, reader.Position); + } + + [Fact] + public void TestReadAsciiStringFixedLengthTooLarge() + { + Assert.Throws( + () => + { + ReadOnlySpan buffer = "Hel"u8; + var reader = new SpanReader(buffer); + reader.ReadAscii(10); + } + ); + } + + [Fact] + public void TestReadAsciiSafe() + { + ReadOnlySpan buffer = [(byte)'H', (byte)'e', 0xFF, (byte)'l', (byte)'o']; + var reader = new SpanReader(buffer); + + var result = reader.ReadAsciiSafe(); + + Assert.Equal("He?lo", result); + Assert.Equal(5, reader.Position); + } + + [Fact] + public void TestReadUTF8String() + { + ReadOnlySpan buffer = "Hello"u8; + var reader = new SpanReader(buffer); + + var result = reader.ReadUTF8(); + + Assert.Equal("Hello", result); + Assert.Equal(5, reader.Position); + } + + [Fact] + public void TestReadUTF8StringWithNull() + { + ReadOnlySpan buffer = "Hi\0Bye"u8; + var reader = new SpanReader(buffer); + + var result = reader.ReadUTF8(); + + Assert.Equal("Hi", result); + Assert.Equal(3, reader.Position); + } + + [Fact] + public void TestReadLittleUniString() + { + ReadOnlySpan buffer = "H\0i\0"u8; + var reader = new SpanReader(buffer); + + var result = reader.ReadLittleUni(); + + Assert.Equal("Hi", result); + Assert.Equal(4, reader.Position); + } + + [Fact] + public void TestReadLittleUniStringWithNull() + { + ReadOnlySpan buffer = "H\0i\0\0\0X\0"u8; + var reader = new SpanReader(buffer); + + var result = reader.ReadLittleUni(); + + Assert.Equal("Hi", result); + Assert.Equal(5, reader.Position); + } + + [Fact] + public void TestReadLittleUniStringFixedLength() + { + ReadOnlySpan buffer = "H\0i\0\0\0\0\0"u8; + var reader = new SpanReader(buffer); + + var result = reader.ReadLittleUni(4); + + Assert.Equal("Hi", result); + Assert.Equal(8, reader.Position); + } + + [Fact] + public void TestReadLittleUniSafe() + { + ReadOnlySpan buffer = [(byte)'H', 0, 0xFF, 0xD8, (byte)'i', 0]; + var reader = new SpanReader(buffer); + + var result = reader.ReadLittleUniSafe(); + + Assert.Equal("H\uFFFDi", result); + Assert.Equal(6, reader.Position); + } + + [Fact] + public void TestReadBigUniString() + { + ReadOnlySpan buffer = "\0H\0i"u8; + var reader = new SpanReader(buffer); + + var result = reader.ReadBigUni(); + + Assert.Equal("Hi", result); + Assert.Equal(4, reader.Position); + } + + [Fact] + public void TestReadBigUniStringWithNull() + { + ReadOnlySpan buffer = "\0H\0i\0\0\0X"u8; + var reader = new SpanReader(buffer); + + var result = reader.ReadBigUni(); + + Assert.Equal("Hi", result); + Assert.Equal(5, reader.Position); + } + + [Fact] + public void TestReadBigUniStringFixedLength() + { + ReadOnlySpan buffer = "\0H\0i\0\0\0\0"u8; + var reader = new SpanReader(buffer); + + var result = reader.ReadBigUni(4); + + Assert.Equal("Hi", result); + Assert.Equal(8, reader.Position); + } + + [Fact] + public void TestReadBigUniSafe() + { + ReadOnlySpan buffer = [0, (byte)'H', 0xD8, 0xFF, 0, (byte)'i']; + var reader = new SpanReader(buffer); + + var result = reader.ReadBigUniSafe(); + + Assert.Equal("H\uFFFDi", result); + Assert.Equal(6, reader.Position); + } + + [Fact] + public void TestSeekBegin() + { + ReadOnlySpan buffer = [0x01, 0x02, 0x03, 0x04, 0x05]; + var reader = new SpanReader(buffer); + + reader.ReadByte(); + reader.ReadByte(); + + var pos = reader.Seek(0, SeekOrigin.Begin); + + Assert.Equal(0, pos); + Assert.Equal(0, reader.Position); + Assert.Equal(0x01, reader.ReadByte()); + } + + [Fact] + public void TestSeekCurrent() + { + ReadOnlySpan buffer = [0x01, 0x02, 0x03, 0x04, 0x05]; + var reader = new SpanReader(buffer); + + reader.ReadByte(); + var pos = reader.Seek(2, SeekOrigin.Current); + + Assert.Equal(3, pos); + Assert.Equal(3, reader.Position); + Assert.Equal(0x04, reader.ReadByte()); + } + + [Fact] + public void TestSeekEnd() + { + ReadOnlySpan buffer = [0x01, 0x02, 0x03, 0x04, 0x05]; + var reader = new SpanReader(buffer); + + var pos = reader.Seek(-2, SeekOrigin.End); + + Assert.Equal(3, pos); + Assert.Equal(3, reader.Position); + Assert.Equal(0x04, reader.ReadByte()); + } + + [Fact] + public void TestSeekNegativeThrows() + { + Assert.Throws( + () => + { + ReadOnlySpan buffer = [0x01, 0x02, 0x03]; + var reader = new SpanReader(buffer); + reader.Seek(-1, SeekOrigin.Begin); + } + ); + } + + [Fact] + public void TestSeekBeyondEndThrows() + { + Assert.Throws( + () => + { + ReadOnlySpan buffer = [0x01, 0x02, 0x03]; + var reader = new SpanReader(buffer); + reader.Seek(10, SeekOrigin.Begin); + } + ); + } + + [Fact] + public void TestRead() + { + ReadOnlySpan buffer = [0x01, 0x02, 0x03, 0x04, 0x05]; + var reader = new SpanReader(buffer); + + Span dest = stackalloc byte[3]; + var bytesRead = reader.Read(dest); + + Assert.Equal(3, bytesRead); + Assert.Equal(3, reader.Position); + AssertThat.Equal(dest, [0x01, 0x02, 0x03]); + } + + [Fact] + public void TestReadPartial() + { + ReadOnlySpan buffer = [0x01, 0x02, 0x03]; + var reader = new SpanReader(buffer); + + Span dest = stackalloc byte[5]; + var bytesRead = reader.Read(dest); + + Assert.Equal(3, bytesRead); + Assert.Equal(3, reader.Position); + AssertThat.Equal(dest[..3], [0x01, 0x02, 0x03]); + } + + [Fact] + public void TestReadEmpty() + { + ReadOnlySpan buffer = [0x01, 0x02, 0x03]; + var reader = new SpanReader(buffer); + + Span dest = []; + var bytesRead = reader.Read(dest); + + Assert.Equal(0, bytesRead); + Assert.Equal(0, reader.Position); + } + + [Fact] + public void TestReadAtEnd() + { + ReadOnlySpan buffer = [0x01, 0x02, 0x03]; + var reader = new SpanReader(buffer); + + reader.Seek(3, SeekOrigin.Begin); + + Span dest = stackalloc byte[5]; + var bytesRead = reader.Read(dest); + + Assert.Equal(0, bytesRead); + Assert.Equal(3, reader.Position); + } + + [Fact] + public void TestLength() + { + ReadOnlySpan buffer = [0x01, 0x02, 0x03, 0x04, 0x05]; + var reader = new SpanReader(buffer); + + Assert.Equal(5, reader.Length); + } + + [Fact] + public void TestPosition() + { + ReadOnlySpan buffer = [0x01, 0x02, 0x03]; + var reader = new SpanReader(buffer); + + Assert.Equal(0, reader.Position); + + reader.ReadByte(); + Assert.Equal(1, reader.Position); + + reader.ReadUInt16(); + Assert.Equal(3, reader.Position); + } + + [Fact] + public void TestRemaining() + { + ReadOnlySpan buffer = [0x01, 0x02, 0x03, 0x04, 0x05]; + var reader = new SpanReader(buffer); + + Assert.Equal(5, reader.Remaining); + + reader.ReadByte(); + Assert.Equal(4, reader.Remaining); + + reader.ReadUInt16(); + Assert.Equal(2, reader.Remaining); + + reader.ReadUInt16(); + Assert.Equal(0, reader.Remaining); + } + + [Fact] + public void TestBuffer() + { + ReadOnlySpan buffer = [0x01, 0x02, 0x03]; + var reader = new SpanReader(buffer); + + var bufferProperty = reader.Buffer; + + Assert.Equal(3, bufferProperty.Length); + AssertThat.Equal(bufferProperty, buffer); + } + + [Fact] + public void TestReadStringEmptyFixedLength() + { + ReadOnlySpan buffer = [0x01, 0x02, 0x03]; + var reader = new SpanReader(buffer); + + var result = reader.ReadAscii(0); + + Assert.Equal("", result); + Assert.Equal(0, reader.Position); + } + + [Fact] + public void TestReadMultipleStringsWithNullTerminators() + { + ReadOnlySpan buffer = "AB\0CD\0EF"u8; + var reader = new SpanReader(buffer); + + Assert.Equal("AB", reader.ReadAscii()); + Assert.Equal("CD", reader.ReadAscii()); + Assert.Equal("EF", reader.ReadAscii()); + Assert.Equal(8, reader.Position); + } + + [Fact] + public void TestReadLittleUniOddByteCount() + { + // If buffer has odd number of bytes, the last byte should be ignored + ReadOnlySpan buffer = [(byte)'H', 0, (byte)'i', 0, 0xFF]; + var reader = new SpanReader(buffer); + + var result = reader.ReadLittleUni(); + + Assert.Equal("Hi", result); + Assert.Equal(4, reader.Position); + } + + [Fact] + public void TestReadBigUniOddByteCount() + { + // If buffer has odd number of bytes, the last byte should be ignored + ReadOnlySpan buffer = [0, (byte)'H', 0, (byte)'i', 0xFF]; + var reader = new SpanReader(buffer); + + var result = reader.ReadBigUni(); + + Assert.Equal("Hi", result); + Assert.Equal(4, reader.Position); + } +} diff --git a/Projects/Server.Tests/Tests/Buffers/SpanWriterTests.cs b/Projects/Server.Tests/Tests/Buffers/SpanWriterTests.cs index 0aeffe574..a0e7ed18c 100644 --- a/Projects/Server.Tests/Tests/Buffers/SpanWriterTests.cs +++ b/Projects/Server.Tests/Tests/Buffers/SpanWriterTests.cs @@ -1,63 +1,591 @@ using System; using System.Buffers; +using System.IO; using Xunit; -namespace Server.Tests +namespace Server.Tests; + +public class SpanWriterTests { - public class SpanWriterTests + [Fact] + public unsafe void TestSpanWriterResizes() { - [Fact] - public unsafe void TestSpanWriterResizes() - { - Span smallStack = stackalloc byte[8]; - using var writer = new SpanWriter(smallStack, true); - writer.Write(0x1024L); - writer.Write(0x1024L); + Span smallStack = stackalloc byte[8]; + var writer = new SpanWriter(smallStack, true); + writer.Write(0x1024L); + writer.Write(0x1024L); - var span = writer.RawBuffer; - fixed (byte* spanPtr = span) + var span = writer.RawBuffer; + fixed (byte* spanPtr = span) + { + fixed (byte* stackPtr = smallStack) { - fixed (byte* stackPtr = smallStack) - { - Assert.True(spanPtr != stackPtr); - } + Assert.True(spanPtr != stackPtr); } - - Assert.True(span.Length > smallStack.Length); } - [Fact] - public unsafe void TestSpanWriterOnlyStackAlloc() - { - Span smallStack = stackalloc byte[8]; - using var writer = new SpanWriter(smallStack, true); - writer.Write(0x1024L); + Assert.True(span.Length > smallStack.Length); + writer.Dispose(); + } - var span = writer.RawBuffer; - fixed (byte* spanPtr = span) + [Fact] + public unsafe void TestSpanWriterOnlyStackAlloc() + { + Span smallStack = stackalloc byte[8]; + var writer = new SpanWriter(smallStack, true); + writer.Write(0x1024L); + + var span = writer.RawBuffer; + fixed (byte* spanPtr = span) + { + fixed (byte* stackPtr = smallStack) { - fixed (byte* stackPtr = smallStack) - { - Assert.True(spanPtr == stackPtr); - } + Assert.True(spanPtr == stackPtr); } - - Assert.True(span.Length == smallStack.Length); - AssertThat.Equal(smallStack, stackalloc byte[] { 0, 0, 0, 0, 0, 0, 0x10, 0x24 }); } - [Fact] - public void TestSpanWriterNoResizeThrows() - { - Assert.Throws( - () => - { - Span smallStack = stackalloc byte[8]; - using var writer = new SpanWriter(smallStack); - writer.Write(0x1024L); - writer.Write(0x1024L); - } - ); - } + Assert.Equal(8, span.Length); + AssertThat.Equal(smallStack, [0, 0, 0, 0, 0, 0, 0x10, 0x24]); + } + + [Fact] + public void TestSpanWriterNoResizeThrows() + { + Assert.Throws( + () => + { + Span smallStack = stackalloc byte[8]; + var writer = new SpanWriter(smallStack); + writer.Write(0x1024L); + writer.Write(0x1024L); + } + ); + } + + [Fact] + public void TestWriteBool() + { + Span buffer = stackalloc byte[2]; + var writer = new SpanWriter(buffer); + + writer.Write(true); + writer.Write(false); + + Assert.Equal(2, writer.Position); + AssertThat.Equal(writer.Span, [1, 0]); + } + + [Fact] + public void TestWriteByte() + { + Span buffer = stackalloc byte[2]; + var writer = new SpanWriter(buffer); + + writer.Write((byte)0x12); + writer.Write((byte)0x34); + + Assert.Equal(2, writer.Position); + AssertThat.Equal(writer.Span, [0x12, 0x34]); + } + + [Fact] + public void TestWriteSByte() + { + Span buffer = stackalloc byte[2]; + var writer = new SpanWriter(buffer); + + writer.Write((sbyte)-1); + writer.Write((sbyte)127); + + Assert.Equal(2, writer.Position); + AssertThat.Equal(writer.Span, [0xFF, 0x7F]); + } + + [Fact] + public void TestWriteInt16BigEndian() + { + Span buffer = stackalloc byte[2]; + var writer = new SpanWriter(buffer); + + writer.Write((short)0x1234); + + Assert.Equal(2, writer.Position); + AssertThat.Equal(writer.Span, [0x12, 0x34]); + } + + [Fact] + public void TestWriteInt16LittleEndian() + { + Span buffer = stackalloc byte[2]; + var writer = new SpanWriter(buffer); + + writer.WriteLE((short)0x1234); + + Assert.Equal(2, writer.Position); + AssertThat.Equal(writer.Span, [0x34, 0x12]); + } + + [Fact] + public void TestWriteUInt16BigEndian() + { + Span buffer = stackalloc byte[2]; + var writer = new SpanWriter(buffer); + + writer.Write((ushort)0x1234); + + Assert.Equal(2, writer.Position); + AssertThat.Equal(writer.Span, [0x12, 0x34]); + } + + [Fact] + public void TestWriteUInt16LittleEndian() + { + Span buffer = stackalloc byte[2]; + var writer = new SpanWriter(buffer); + + writer.WriteLE((ushort)0x1234); + + Assert.Equal(2, writer.Position); + AssertThat.Equal(writer.Span, [0x34, 0x12]); + } + + [Fact] + public void TestWriteInt32BigEndian() + { + Span buffer = stackalloc byte[4]; + var writer = new SpanWriter(buffer); + + writer.Write(0x12345678); + + Assert.Equal(4, writer.Position); + AssertThat.Equal(writer.Span, [0x12, 0x34, 0x56, 0x78]); + } + + [Fact] + public void TestWriteInt32LittleEndian() + { + Span buffer = stackalloc byte[4]; + var writer = new SpanWriter(buffer); + + writer.WriteLE(0x12345678); + + Assert.Equal(4, writer.Position); + AssertThat.Equal(writer.Span, [0x78, 0x56, 0x34, 0x12]); + } + + [Fact] + public void TestWriteUInt32BigEndian() + { + Span buffer = stackalloc byte[4]; + var writer = new SpanWriter(buffer); + + writer.Write(0x12345678u); + + Assert.Equal(4, writer.Position); + AssertThat.Equal(writer.Span, [0x12, 0x34, 0x56, 0x78]); + } + + [Fact] + public void TestWriteUInt32LittleEndian() + { + Span buffer = stackalloc byte[4]; + var writer = new SpanWriter(buffer); + + writer.WriteLE(0x12345678u); + + Assert.Equal(4, writer.Position); + AssertThat.Equal(writer.Span, [0x78, 0x56, 0x34, 0x12]); + } + + [Fact] + public void TestWriteInt64BigEndian() + { + Span buffer = stackalloc byte[8]; + var writer = new SpanWriter(buffer); + + writer.Write(0x123456789ABCDEF0L); + + Assert.Equal(8, writer.Position); + AssertThat.Equal(writer.Span, [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]); + } + + [Fact] + public void TestWriteUInt64BigEndian() + { + Span buffer = stackalloc byte[8]; + var writer = new SpanWriter(buffer); + + writer.Write(0x123456789ABCDEF0UL); + + Assert.Equal(8, writer.Position); + AssertThat.Equal(writer.Span, [0x12, 0x34, 0x56, 0x78, 0x9A, 0xBC, 0xDE, 0xF0]); + } + + [Fact] + public void TestWriteSpan() + { + Span buffer = stackalloc byte[6]; + var writer = new SpanWriter(buffer); + + ReadOnlySpan data = [0x01, 0x02, 0x03]; + writer.Write(data); + writer.Write(data); + + Assert.Equal(6, writer.Position); + AssertThat.Equal(writer.Span, [0x01, 0x02, 0x03, 0x01, 0x02, 0x03]); + } + + [Fact] + public void TestWriteAsciiChar() + { + Span buffer = stackalloc byte[2]; + var writer = new SpanWriter(buffer); + + writer.WriteAscii('A'); + writer.WriteAscii('B'); + + Assert.Equal(2, writer.Position); + AssertThat.Equal(writer.Span, "AB"u8); + } + + [Fact] + public void TestWriteAsciiString() + { + Span buffer = stackalloc byte[5]; + var writer = new SpanWriter(buffer); + + writer.WriteAscii("Hello"); + + Assert.Equal(5, writer.Position); + AssertThat.Equal(writer.Span, "Hello"u8); + } + + [Fact] + public void TestWriteAsciiStringNull() + { + Span buffer = stackalloc byte[6]; + var writer = new SpanWriter(buffer); + + writer.WriteAsciiNull("Hello"); + + Assert.Equal(6, writer.Position); + AssertThat.Equal(writer.Span, "Hello\0"u8); + } + + [Fact] + public void TestWriteAsciiStringFixedLength() + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + + writer.WriteAscii("Hello", 10); + + Assert.Equal(10, writer.Position); + AssertThat.Equal(writer.Span, "Hello\0\0\0\0\0"u8); + } + + [Fact] + public void TestWriteUTF8String() + { + Span buffer = stackalloc byte[5]; + var writer = new SpanWriter(buffer); + + writer.WriteUTF8("Hello"); + + Assert.Equal(5, writer.Position); + AssertThat.Equal(writer.Span, "Hello"u8); + } + + [Fact] + public void TestWriteUTF8StringNull() + { + Span buffer = stackalloc byte[6]; + var writer = new SpanWriter(buffer); + + writer.WriteUTF8Null("Hello"); + + Assert.Equal(6, writer.Position); + AssertThat.Equal(writer.Span, "Hello\0"u8); + } + + [Fact] + public void TestWriteLittleUniString() + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + + writer.WriteLittleUni("Hello"); + + Assert.Equal(10, writer.Position); + AssertThat.Equal(writer.Span, "H\0e\0l\0l\0o\0"u8); + } + + [Fact] + public void TestWriteLittleUniStringNull() + { + Span buffer = stackalloc byte[12]; + var writer = new SpanWriter(buffer); + + writer.WriteLittleUniNull("Hello"); + + Assert.Equal(12, writer.Position); + AssertThat.Equal(writer.Span, "H\0e\0l\0l\0o\0\0\0"u8); + } + + [Fact] + public void TestWriteBigUniString() + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + + writer.WriteBigUni("Hello"); + + Assert.Equal(10, writer.Position); + AssertThat.Equal(writer.Span, "\0H\0e\0l\0l\0o"u8); + } + + [Fact] + public void TestWriteBigUniStringNull() + { + Span buffer = stackalloc byte[12]; + var writer = new SpanWriter(buffer); + + writer.WriteBigUniNull("Hello"); + + Assert.Equal(12, writer.Position); + AssertThat.Equal(writer.Span, "\0H\0e\0l\0l\0o\0\0"u8); + } + + [Fact] + public void TestClear() + { + Span buffer = stackalloc byte[10]; + buffer.Fill(0xFF); + var writer = new SpanWriter(buffer); + + writer.Write((byte)0x01); + writer.Clear(5); + writer.Write((byte)0x02); + + Assert.Equal(7, writer.Position); + AssertThat.Equal(writer.Span, [0x01, 0, 0, 0, 0, 0, 0x02]); + } + + [Fact] + public void TestSeekBegin() + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + + writer.Write((byte)0x01); + writer.Write((byte)0x02); + writer.Write((byte)0x03); + + var pos = writer.Seek(1, SeekOrigin.Begin); + + Assert.Equal(1, pos); + Assert.Equal(1, writer.Position); + Assert.Equal(3, writer.BytesWritten); + } + + [Fact] + public void TestSeekCurrent() + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + + writer.Write((byte)0x01); + writer.Write((byte)0x02); + + var pos = writer.Seek(2, SeekOrigin.Current); + + Assert.Equal(4, pos); + Assert.Equal(4, writer.Position); + } + + [Fact] + public void TestSeekEnd() + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + + writer.Write((byte)0x01); + writer.Write((byte)0x02); + writer.Write((byte)0x03); + + var pos = writer.Seek(-1, SeekOrigin.End); + + Assert.Equal(2, pos); + Assert.Equal(2, writer.Position); + } + + [Fact] + public void TestSeekNegativeThrows() + { + Assert.Throws( + () => + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + writer.Seek(-1, SeekOrigin.Begin); + } + ); + } + + [Fact] + public void TestSeekBeyondCapacityNoResizeThrows() + { + Assert.Throws( + () => + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + writer.Seek(20, SeekOrigin.Begin); + } + ); + } + + [Fact] + public void TestSeekBeyondCapacityWithResize() + { + var writer = new SpanWriter(10, true); + + var pos = writer.Seek(20, SeekOrigin.Begin); + + Assert.Equal(20, pos); + Assert.True(writer.Capacity >= 20); + writer.Dispose(); + } + + [Fact] + public void TestEnsureCapacityNoResize() + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + + writer.EnsureCapacity(10); + Assert.Equal(10, writer.Capacity); + } + + [Fact] + public void TestEnsureCapacityNoResizeThrows() + { + Assert.Throws( + () => + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + writer.EnsureCapacity(20); + } + ); + } + + [Fact] + public void TestEnsureCapacityWithResize() + { + var writer = new SpanWriter(10, true); + + writer.EnsureCapacity(50); + + Assert.True(writer.Capacity >= 50); + writer.Dispose(); + } + + [Fact] + public void TestToSpanWithStackAlloc() + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + + writer.Write((byte)0x01); + writer.Write((byte)0x02); + writer.Write((byte)0x03); + + using var owner = writer.ToSpan(); + + Assert.Equal(3, owner.Span.Length); + AssertThat.Equal(owner.Span, [0x01, 0x02, 0x03]); + } + + [Fact] + public void TestToSpanWithRentedBuffer() + { + var writer = new SpanWriter(10); + + writer.Write((byte)0x01); + writer.Write((byte)0x02); + writer.Write((byte)0x03); + + using var owner = writer.ToSpan(); + + Assert.Equal(3, owner.Span.Length); + AssertThat.Equal(owner.Span, [0x01, 0x02, 0x03]); + } + + [Fact] + public void TestToSpanEmpty() + { + var writer = new SpanWriter(10); + + using var owner = writer.ToSpan(); + + Assert.Equal(0, owner.Span.Length); + } + + [Fact] + public void TestBytesWritten() + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + + Assert.Equal(0, writer.BytesWritten); + + writer.Write((byte)0x01); + Assert.Equal(1, writer.BytesWritten); + + writer.Write((ushort)0x0203); + Assert.Equal(3, writer.BytesWritten); + + writer.Seek(1, SeekOrigin.Begin); + Assert.Equal(3, writer.BytesWritten); + + writer.Write((byte)0xFF); + Assert.Equal(3, writer.BytesWritten); + } + + [Fact] + public void TestCapacity() + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + + Assert.Equal(10, writer.Capacity); + } + + [Fact] + public void TestRawBuffer() + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + + writer.Write((byte)0x01); + + var raw = writer.RawBuffer; + Assert.Equal(10, raw.Length); + Assert.Equal(0x01, raw[0]); + } + + [Fact] + public void TestSpan() + { + Span buffer = stackalloc byte[10]; + var writer = new SpanWriter(buffer); + + writer.Write((byte)0x01); + writer.Write((byte)0x02); + + var span = writer.Span; + Assert.Equal(2, span.Length); + AssertThat.Equal(span, [0x01, 0x02]); } } diff --git a/Projects/Server.Tests/Tests/Items/ContainerTests.cs b/Projects/Server.Tests/Tests/Items/ContainerTests.cs index 0b258a717..8c165f5f6 100644 --- a/Projects/Server.Tests/Tests/Items/ContainerTests.cs +++ b/Projects/Server.Tests/Tests/Items/ContainerTests.cs @@ -8,12 +8,14 @@ namespace Server.Tests; [Collection("Sequential Server Tests")] public class ContainerTests { - [Fact] - public void TestFindItemsByType() + [Theory] + [InlineData(typeof(Container))] + [InlineData(typeof(Item))] + public void TestFindItemsByType(Type itemType) { var staticSerial = (Serial)0x3; - var container = new Container((Serial)0x1); + var container = itemType.CreateInstance((Serial)0x1); container.AddItem(new Item((Serial)0x2)); container.AddItem(new Static(staticSerial)); @@ -27,18 +29,20 @@ public class ContainerTests Assert.Equal(staticSerial, staticItem.Serial); } - [Fact] - public void TestFindItemsByTypeNested() + [Theory] + [InlineData(typeof(Container))] + [InlineData(typeof(Item))] + public void TestFindItemsByTypeNested(Type itemType) { var static1 = new Static((Serial)0x3); var static2 = new Static((Serial)0x6); - var container = new Container((Serial)0x1); + var container = itemType.CreateInstance((Serial)0x1); container.AddItem(new Item((Serial)0x2)); - var container2 = new Container((Serial)0x4); + var container2 = itemType.CreateInstance((Serial)0x4); container.AddItem(container2); - var container3 = new Container((Serial)0x5); + var container3 = itemType.CreateInstance((Serial)0x5); container2.AddItem(container3); container3.AddItem(static2); @@ -55,12 +59,14 @@ public class ContainerTests Assert.Equal(static2, statics[1]); } - [Fact] - public void TestFindItemsByTypeNotMatching() + [Theory] + [InlineData(typeof(Container))] + [InlineData(typeof(Item))] + public void TestFindItemsByTypeNotMatching(Type itemType) { - var container = new Container((Serial)0x1); + var container = itemType.CreateInstance((Serial)0x1); container.AddItem(new Item((Serial)0x2)); - var container2 = new Container((Serial)0x4); + var container2 = itemType.CreateInstance((Serial)0x4); container.AddItem(container2); container2.AddItem(new Item((Serial)0x5)); @@ -73,10 +79,12 @@ public class ContainerTests Assert.Null(staticItem); } - [Fact] - public void TestFindItemsByTypeShouldThrowWhenModified() + [Theory] + [InlineData(typeof(Container))] + [InlineData(typeof(Item))] + public void TestFindItemsByTypeShouldThrowWhenModified(Type itemType) { - var container = new Container((Serial)0x1); + var container = itemType.CreateInstance((Serial)0x1); container.AddItem(new Item((Serial)0x2)); var staticItem = new Static((Serial)0x3); container.AddItem(staticItem); @@ -96,10 +104,12 @@ public class ContainerTests ); } - [Fact] - public void TestEnumerateItemsByTypeWhenModified() + [Theory] + [InlineData(typeof(Container))] + [InlineData(typeof(Item))] + public void TestEnumerateItemsByTypeWhenModified(Type itemType) { - var container = new Container((Serial)0x1); + var container = itemType.CreateInstance((Serial)0x1); var item1 = new Item((Serial)0x2); container.AddItem(item1); diff --git a/Projects/Server.Tests/Tests/Maps/ClientEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/ClientEnumeratorTests.cs new file mode 100644 index 000000000..b2eb644ea --- /dev/null +++ b/Projects/Server.Tests/Tests/Maps/ClientEnumeratorTests.cs @@ -0,0 +1,549 @@ +using System; +using System.Collections.Generic; +using System.Net.Sockets; +using Server.Accounting; +using Server.Network; +using Xunit; + +namespace Server.Tests.Tests.Maps; + +[Collection("Sequential Server Tests")] +public class ClientEnumeratorTests +{ + [Fact] + public void ClientEnumerator_FiltersByBoundsAndOrder() + { + var map = Map.Felucca; + var rect = new Rectangle2D(100, 100, 32, 32); + + var clients = new (NetState, Mobile)[3]; + try + { + clients[0] = CreateClientWithMobile(map, new Point3D(105, 105, 0)); + clients[1] = CreateClientWithMobile(map, new Point3D(130, 130, 0)); + clients[2] = CreateClientWithMobile(map, new Point3D(90, 90, 0)); + + var found = new List(); + foreach (var ns in map.GetClientsInBounds(rect)) + { + found.Add(ns); + } + + Assert.Equal(2, found.Count); + Assert.All(found, ns => Assert.True(rect.Contains(ns.Mobile.Location))); + Assert.Equal(new[] { clients[0].Item1, clients[1].Item1 }, found); + } + finally + { + DeleteAll(clients); + } + } + + [Fact] + public void ClientEnumerator_SkipsNullMobiles() + { + var map = Map.Felucca; + var rect = new Rectangle2D(200, 200, 16, 16); + + var clients = new (NetState, Mobile)[3]; + try + { + clients[0] = CreateClientWithMobile(map, new Point3D(205, 205, 0)); + clients[1] = CreateClientWithMobile(map, new Point3D(206, 205, 0)); + clients[2] = CreateClientWithMobile(map, new Point3D(207, 205, 0)); + + // Remove the mobile from the second client + clients[1].Item2.Delete(); + clients[1].Item1.Mobile = null; + + var found = new List(); + foreach (var ns in map.GetClientsInBounds(rect)) + { + found.Add(ns); + } + + Assert.Equal(2, found.Count); + Assert.Equal(new[] { clients[0].Item1, clients[2].Item1 }, found); + } + finally + { + DeleteAll(clients); + } + } + + [Fact] + public void ClientEnumerator_RespectsMakeBoundsInclusiveFlag() + { + var map = Map.Felucca; + var rect = new Rectangle2D(300, 300, 1, 1); + + var clients = new (NetState, Mobile)[1]; + try + { + clients[0] = CreateClientWithMobile(map, new Point3D(301, 301, 0)); + + var enumerator = map.GetClientsInBounds(rect, makeBoundsInclusive: true).GetEnumerator(); + + Assert.True(enumerator.MoveNext()); + Assert.Equal(clients[0].Item1, enumerator.Current); + } + finally + { + DeleteAll(clients); + } + } + + [Fact] + public void ClientEnumerator_MapNullYieldsEmpty() + { + var enumerator = new Map.ClientBoundsEnumerable(null, Rectangle2D.Empty, false).GetEnumerator(); + Assert.False(enumerator.MoveNext()); + } + + [Fact] + public void ClientEnumerator_ThrowsOnVersionChange() + { + var map = Map.Felucca; + var rect = new Rectangle2D(400, 400, 16, 16); + + var clients = new[] + { + CreateClientWithMobile(map, new Point3D(405, 405, 0)), + CreateClientWithMobile(map, new Point3D(406, 405, 0)) + }; + + try + { + var enumerator = map.GetClientsInBounds(rect).GetEnumerator(); + Assert.True(enumerator.MoveNext()); + + clients[1].Item2.Delete(); + + // Ref structs cannot be captured in lambdas, so we test the exception directly + var exceptionThrown = false; + try + { + enumerator.MoveNext(); + } + catch (InvalidOperationException) + { + exceptionThrown = true; + } + + Assert.True(exceptionThrown, "Expected InvalidOperationException when collection version changes"); + } + finally + { + DeleteAll(clients); + } + } + + [Fact] + public void ClientEnumerator_StepsAcrossSectors() + { + var map = Map.Felucca; + var rect = new Rectangle2D(500, 500, Map.SectorSize * 2, Map.SectorSize * 2); + + var clients = new[] + { + CreateClientWithMobile(map, new Point3D(rect.X + 1, rect.Y + 1, 0)), + CreateClientWithMobile(map, new Point3D(rect.X + Map.SectorSize + 1, rect.Y + 1, 0)), + CreateClientWithMobile(map, new Point3D(rect.X + Map.SectorSize + 1, rect.Y + Map.SectorSize + 1, 0)) + }; + + try + { + var result = new List(); + foreach (var ns in map.GetClientsInBounds(rect)) + { + result.Add(ns); + } + + Assert.Equal(new[] { clients[0].Item1, clients[1].Item1, clients[2].Item1 }, result); + } + finally + { + DeleteAll(clients); + } + } + + [Fact] + public void ClientEnumerator_MapBoundsAreClamped() + { + var map = Map.Felucca; + var width = map.Width; + var height = map.Height; + + var rect = new Rectangle2D(width - Map.SectorSize - 2, height - Map.SectorSize - 2, Map.SectorSize * 2, Map.SectorSize * 2); + + var clients = new[] + { + CreateClientWithMobile(map, new Point3D(width - 2, height - 2, 0)) + }; + + try + { + var enumerator = map.GetClientsInBounds(rect).GetEnumerator(); + + Assert.True(enumerator.MoveNext()); + Assert.Equal(clients[0].Item1, enumerator.Current); + Assert.False(enumerator.MoveNext()); + } + finally + { + DeleteAll(clients); + } + } + + [Fact] + public void ClientAtEnumerator_FiltersExactLocation() + { + var map = Map.Felucca; + var location = new Point3D(600, 600, 0); + var differentLocation = new Point3D(601, 600, 0); + + var clients = new (NetState, Mobile)[3]; + try + { + clients[0] = CreateClientWithMobile(map, location); + clients[1] = CreateClientWithMobile(map, location); + clients[2] = CreateClientWithMobile(map, differentLocation); + + var found = new List(); + foreach (var ns in map.GetClientsAt(location)) + { + found.Add(ns); + } + + Assert.Equal(2, found.Count); + Assert.All(found, ns => + { + Assert.NotNull(ns.Mobile); + Assert.Equal(location.X, ns.Mobile.X); + Assert.Equal(location.Y, ns.Mobile.Y); + }); + Assert.Contains(clients[0].Item1, found); + Assert.Contains(clients[1].Item1, found); + } + finally + { + DeleteAll(clients); + } + } + + [Fact] + public void ClientAtEnumerator_SkipsNullMobiles() + { + var map = Map.Felucca; + var location = new Point3D(650, 650, 0); + + var clients = new (NetState, Mobile)[3]; + try + { + clients[0] = CreateClientWithMobile(map, location); + clients[1] = CreateClientWithMobile(map, location); + clients[2] = CreateClientWithMobile(map, location); + + // Remove the mobile from the second client + clients[1].Item2.Delete(); + clients[1].Item1.Mobile = null; + + var found = new List(); + foreach (var ns in map.GetClientsAt(location)) + { + found.Add(ns); + } + + Assert.Equal(2, found.Count); + Assert.Equal(new[] { clients[0].Item1, clients[2].Item1 }, found); + } + finally + { + DeleteAll(clients); + } + } + + [Fact] + public void ClientAtEnumerator_MapNullYieldsEmpty() + { + var enumerator = new Map.ClientAtEnumerable(null, new Point2D(0, 0)).GetEnumerator(); + Assert.False(enumerator.MoveNext()); + } + + [Fact] + public void ClientAtEnumerator_ThrowsOnVersionChange() + { + var map = Map.Felucca; + var location = new Point3D(750, 750, 0); + + var clients = new[] + { + CreateClientWithMobile(map, location), + CreateClientWithMobile(map, location) + }; + + try + { + var enumerator = map.GetClientsAt(location).GetEnumerator(); + Assert.True(enumerator.MoveNext()); + + clients[1].Item2.Delete(); + + // Ref structs cannot be captured in lambdas, so we test the exception directly + var exceptionThrown = false; + try + { + enumerator.MoveNext(); + } + catch (InvalidOperationException) + { + exceptionThrown = true; + } + + Assert.True(exceptionThrown, "Expected InvalidOperationException when collection version changes"); + } + finally + { + DeleteAll(clients); + } + } + + [Fact] + public void ClientAtEnumerator_UsesDifferentPoint3DOverloads() + { + var map = Map.Felucca; + var location = new Point3D(800, 800, 5); + + var clients = new (NetState, Mobile)[1]; + try + { + clients[0] = CreateClientWithMobile(map, location); + + // Test Point3D overload + var found1 = new List(); + foreach (var ns in map.GetClientsAt(location)) + { + found1.Add(ns); + } + + // Test (int, int) overload - should find the same client (Z is ignored) + var found2 = new List(); + foreach (var ns in map.GetClientsAt(location.X, location.Y)) + { + found2.Add(ns); + } + + // Test Point2D overload + var found3 = new List(); + foreach (var ns in map.GetClientsAt(new Point2D(location.X, location.Y))) + { + found3.Add(ns); + } + + Assert.Single(found1); + Assert.Equal(clients[0].Item1, found1[0]); + Assert.Equal(found1, found2); + Assert.Equal(found1, found3); + } + finally + { + DeleteAll(clients); + } + } + + [Fact] + public void ClientEnumerator_GetClientsInRange() + { + var map = Map.Felucca; + var center = new Point3D(900, 900, 0); + var range = 5; + + var clients = new (NetState, Mobile)[3]; + try + { + clients[0] = CreateClientWithMobile(map, new Point3D(902, 902, 0)); // Within range + clients[1] = CreateClientWithMobile(map, new Point3D(898, 898, 0)); // Within range + clients[2] = CreateClientWithMobile(map, new Point3D(910, 910, 0)); // Outside range (906+ is outside) + + var found = new List(); + foreach (var ns in map.GetClientsInRange(center, range)) + { + found.Add(ns); + } + + // GetClientsInRange uses a bounding rectangle, not circular distance + // Range of 5 means rectangle from (895, 895) to (905, 905) + Assert.Equal(2, found.Count); + Assert.Contains(clients[0].Item1, found); + Assert.Contains(clients[1].Item1, found); + } + finally + { + DeleteAll(clients); + } + } + + [Fact] + public void ClientEnumerator_DeletedMobilesAreSkipped() + { + var map = Map.Felucca; + var rect = new Rectangle2D(1000, 1000, 16, 16); + + var clients = new (NetState, Mobile)[3]; + try + { + clients[0] = CreateClientWithMobile(map, new Point3D(1005, 1005, 0)); + clients[1] = CreateClientWithMobile(map, new Point3D(1006, 1005, 0)); + clients[2] = CreateClientWithMobile(map, new Point3D(1007, 1005, 0)); + + // Delete the mobile (but not the NetState) + clients[1].Item2.Delete(); + + var found = new List(); + foreach (var ns in map.GetClientsInBounds(rect)) + { + found.Add(ns); + } + + // Should skip the client whose mobile was deleted + Assert.Equal(2, found.Count); + Assert.Contains(clients[0].Item1, found); + Assert.Contains(clients[2].Item1, found); + } + finally + { + DeleteAll(clients); + } + } + + private class MockAccount : IAccount + { + public int TotalGold { get; } + public int TotalPlat { get; } + public bool DepositGold(int amount) => throw new NotImplementedException(); + public bool DepositPlat(int amount) => throw new NotImplementedException(); + public bool WithdrawGold(int amount) => throw new NotImplementedException(); + public bool WithdrawPlat(int amount) => throw new NotImplementedException(); + public long GetTotalGold() => throw new NotImplementedException(); + public int CompareTo(IAccount other) => throw new NotImplementedException(); + public string Username { get; } + public string Email { get; set; } + public AccessLevel AccessLevel { get; set; } + public int Length { get; } + public int Limit { get; set; } = 6; // Default to 6 character slots + public int Count { get; } + + private readonly Dictionary _mobiles = new(); + public Mobile this[int index] + { + get => _mobiles.GetValueOrDefault(index); + set => _mobiles[index] = value; + } + + public DateTime Created { get; set; } + public Serial Serial { get; } + public void Deserialize(IGenericReader reader) => throw new NotImplementedException(); + public byte SerializedThread { get; set; } + public int SerializedPosition { get; set; } + public int SerializedLength { get; set; } + public void Serialize(IGenericWriter writer) => throw new NotImplementedException(); + public bool Deleted { get; } + public void Delete() => throw new NotImplementedException(); + public bool TrySetUsername(string username) => throw new NotImplementedException(); + public void SetPassword(string password) => throw new NotImplementedException(); + public bool CheckPassword(string password) => throw new NotImplementedException(); + } + + private static (NetState, Mobile) CreateClientWithMobile(Map map, Point3D location) + { + var socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + var ns = new NetState(socket); + + // Assign a mock account to avoid null reference issues + ns.Account = new MockAccount(); + + // Use a unique serial for each mobile + var serial = World.NewMobile; + var mobile = new Mobile(serial); + mobile.DefaultMobileInit(); + + // Set the NetState on the mobile BEFORE moving it to the world + // so the sector's client list gets updated properly + ns.Mobile = mobile; + mobile.NetState = ns; + + mobile.MoveToWorld(location, map); + return (ns, mobile); + } + + [Fact] + public void ClientEnumerator_ZeroRangeReturnsOnlyCenter() + { + var map = Map.Felucca; + var center = new Point3D(950, 950, 0); + const int range = 0; + + var clients = new (NetState, Mobile)[2]; + try + { + clients[0] = CreateClientWithMobile(map, center); // Exact center + clients[1] = CreateClientWithMobile(map, new Point3D(951, 950, 0)); // 1 tile away + + var found = new List(); + foreach (var ns in map.GetClientsInRange(center, range)) + { + found.Add(ns); + } + + Assert.Single(found); + Assert.Equal(clients[0].Item1, found[0]); + } + finally + { + DeleteAll(clients); + } + } + + [Fact] + public void ClientEnumerator_NegativeRangeCreates1x1Bounds() + { + var map = Map.Felucca; + var center = new Point3D(1050, 1050, 0); + const int range = -5; + + var clients = new (NetState, Mobile)[2]; + try + { + clients[0] = CreateClientWithMobile(map, center); + clients[1] = CreateClientWithMobile(map, new Point3D(1051, 1050, 0)); // 1 tile away + + var found = new List(); + foreach (var ns in map.GetClientsInRange(center, range)) + { + found.Add(ns); + } + + // With negative range creating a 1x1 bounds, only exact center matches + Assert.Single(found); + Assert.Equal(clients[0].Item1, found[0]); + } + finally + { + DeleteAll(clients); + } + } + + private static void DeleteAll((NetState, Mobile)[] clients) + { + for (var i = 0; i < clients.Length; i++) + { + if (clients[i].Item1 != null) + { + clients[i].Item1.Mobile = null; + clients[i].Item1.Disconnect("Test cleanup"); + } + clients[i].Item2?.Delete(); + } + } +} + diff --git a/Projects/Server.Tests/Tests/Maps/ItemByDistanceEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/ItemByDistanceEnumeratorTests.cs new file mode 100644 index 000000000..fe19c76fb --- /dev/null +++ b/Projects/Server.Tests/Tests/Maps/ItemByDistanceEnumeratorTests.cs @@ -0,0 +1,605 @@ +using System; +using System.Collections.Generic; +using Xunit; + +namespace Server.Tests.Tests.Maps; + +[Collection("Sequential Server Tests")] +public class ItemByDistanceEnumeratorTests +{ + [Fact] + public void ItemByDistanceEnumerator_ReturnsNearbyItems() + { + var map = Map.Felucca; + var center = new Point3D(100, 100, 0); + const int range = 5; + + var Items = new TestItem[3]; + try + { + Items[0] = CreateItem(map, new Point3D(102, 102, 0)); // Within range + Items[1] = CreateItem(map, new Point3D(98, 98, 0)); // Within range + Items[2] = CreateItem(map, new Point3D(110, 110, 0)); // Outside range + + var found = new List(); + foreach (var (Item, _) in map.GetItemsInRangeByDistance(center, range)) + { + found.Add(Item); + } + + Assert.Equal(2, found.Count); + Assert.Contains(Items[0], found); + Assert.Contains(Items[1], found); + Assert.DoesNotContain(Items[2], found); + } + finally + { + DeleteAll(Items); + } + } + + [Fact] + public void ItemByDistanceEnumerator_DeletedItemsAreSkipped() + { + var map = Map.Felucca; + var center = new Point3D(200, 200, 0); + const int range = 5; + + var Items = new TestItem[3]; + try + { + Items[0] = CreateItem(map, new Point3D(202, 202, 0)); + Items[1] = CreateItem(map, new Point3D(203, 202, 0)); + Items[2] = CreateItem(map, new Point3D(204, 202, 0)); + + Items[1].Delete(); + + var found = new List(); + foreach (var (Item, _) in map.GetItemsInRangeByDistance(center, range)) + { + found.Add(Item); + } + + Assert.Equal(2, found.Count); + Assert.Contains(Items[0], found); + Assert.Contains(Items[2], found); + Assert.DoesNotContain(Items[1], found); + } + finally + { + DeleteAll(Items); + } + } + + [Fact] + public void ItemByDistanceEnumerator_ReturnsMinDistance() + { + var map = Map.Felucca; + var center = new Point3D(300, 300, 0); + const int range = 10; + + var Items = new TestItem[2]; + try + { + Items[0] = CreateItem(map, new Point3D(305, 305, 0)); + Items[1] = CreateItem(map, new Point3D(302, 302, 0)); + + var foundWithDistance = new List<(Item, int)>(); + foreach (var result in map.GetItemsInRangeByDistance(center, range)) + { + foundWithDistance.Add(result); + } + + Assert.Equal(2, foundWithDistance.Count); + // Each Item should have a non-negative min distance + Assert.All(foundWithDistance, item => Assert.True(item.Item2 >= 0)); + } + finally + { + DeleteAll(Items); + } + } + + [Fact] + public void ItemByDistanceEnumerator_OrderedBySector() + { + var map = Map.Felucca; + var center = new Point3D(400, 400, 0); + const int range = Map.SectorSize * 2; + + var Items = new TestItem[3]; + try + { + // Place Items in different sectors + Items[0] = CreateItem(map, new Point3D(center.X + 2, center.Y + 2, 0)); + Items[1] = CreateItem(map, new Point3D(center.X + Map.SectorSize + 2, center.Y + 2, 0)); + Items[2] = CreateItem(map, new Point3D(center.X + 2, center.Y + Map.SectorSize + 2, 0)); + + var found = new List(); + foreach (var (Item, _) in map.GetItemsInRangeByDistance(center, range)) + { + found.Add(Item); + } + + Assert.Equal(3, found.Count); + Assert.Contains(Items[0], found); + Assert.Contains(Items[1], found); + Assert.Contains(Items[2], found); + } + finally + { + DeleteAll(Items); + } + } + + [Fact] + public void ItemByDistanceEnumerator_MapNullYieldsEmpty() + { + var center = new Point2D(0, 0); + var bounds = new Rectangle2D(center.m_X - 10, center.m_Y - 10, 21, 21); + var enumerator = new Map.ItemDistanceEnumerable(null, bounds, center, false).GetEnumerator(); + Assert.False(enumerator.MoveNext()); + } + + [Fact] + public void ItemByDistanceEnumerator_ThrowsOnVersionChange() + { + var map = Map.Felucca; + var center = new Point3D(500, 500, 0); + const int range = 5; + + var Items = new[] + { + CreateItem(map, new Point3D(502, 502, 0)), + CreateItem(map, new Point3D(503, 502, 0)) + }; + + try + { + var enumerator = map.GetItemsInRangeByDistance(center, range).GetEnumerator(); + Assert.True(enumerator.MoveNext()); + + Items[1].Delete(); + + // Ref structs cannot be captured in lambdas, so we test the exception directly + var exceptionThrown = false; + try + { + enumerator.MoveNext(); + } + catch (InvalidOperationException) + { + exceptionThrown = true; + } + + Assert.True(exceptionThrown, "Expected InvalidOperationException when collection version changes"); + } + finally + { + DeleteAll(Items); + } + } + + [Fact] + public void ItemByDistanceEnumerator_ZeroRangeReturnsOnlyCenter() + { + var map = Map.Felucca; + var center = new Point3D(600, 600, 0); + const int range = 0; + + var Items = new TestItem[2]; + try + { + Items[0] = CreateItem(map, center); // Exact center + Items[1] = CreateItem(map, new Point3D(601, 600, 0)); // 1 tile away + + var found = new List(); + foreach (var (Item, _) in map.GetItemsInRangeByDistance(center, range)) + { + found.Add(Item); + } + + Assert.Single(found); + Assert.Equal(Items[0], found[0]); + } + finally + { + DeleteAll(Items); + } + } + + [Fact] + public void ItemByDistanceEnumerator_FiltersByType() + { + var map = Map.Felucca; + var center = new Point3D(700, 700, 0); + const int range = 5; + + var testItem2 = new TestItem2(World.NewItem); + var testItem = new TestItem(World.NewItem); + + try + { + testItem2.MoveToWorld(new Point3D(702, 702, 0), map); + testItem.MoveToWorld(new Point3D(703, 702, 0), map); + + var foundPlayers = new List(); + foreach (var (Item, _) in map.GetItemsInRangeByDistance(center, range)) + { + foundPlayers.Add(Item); + } + + Assert.Single(foundPlayers); + Assert.Equal(testItem2, foundPlayers[0]); + } + finally + { + testItem2.Delete(); + testItem.Delete(); + } + } + + [Fact] + public void ItemByDistanceEnumerator_UsesDifferentPointOverloads() + { + var map = Map.Felucca; + var center = new Point3D(800, 800, 5); + const int range = 5; + + var Items = new TestItem[1]; + try + { + Items[0] = CreateItem(map, new Point3D(802, 802, 0)); + + // Test Point3D overload + var found1 = new List(); + foreach (var (Item, _) in map.GetItemsInRangeByDistance(center, range)) + { + found1.Add(Item); + } + + // Test (int, int) overload + var found2 = new List(); + foreach (var (Item, _) in map.GetItemsInRangeByDistance(center.X, center.Y, range)) + { + found2.Add(Item); + } + + // Test Point2D overload + var found3 = new List(); + foreach (var (Item, _) in map.GetItemsInRangeByDistance(new Point2D(center.X, center.Y), range)) + { + found3.Add(Item); + } + + Assert.Single(found1); + Assert.Equal(Items[0], found1[0]); + Assert.Equal(found1, found2); + Assert.Equal(found1, found3); + } + finally + { + DeleteAll(Items); + } + } + + [Fact] + public void ItemByDistanceEnumerator_RingTraversal() + { + var map = Map.Felucca; + var center = new Point3D(900, 900, 0); + const int range = Map.SectorSize * 2; + + var Items = new TestItem[4]; + try + { + // Place Items in different rings around the center + Items[0] = CreateItem(map, center); // Ring 0 (center sector) + Items[1] = CreateItem(map, new Point3D(center.X + Map.SectorSize, center.Y, 0)); // Ring 1 + Items[2] = CreateItem(map, new Point3D(center.X, center.Y + Map.SectorSize, 0)); // Ring 1 + Items[3] = CreateItem(map, new Point3D(center.X + Map.SectorSize * 2 - 1, center.Y, 0)); // Ring 2 + + var found = new List(); + var distances = new List(); + foreach (var (Item, minDistance) in map.GetItemsInRangeByDistance(center, range)) + { + found.Add(Item); + distances.Add(minDistance); + } + + // All Items should be found + Assert.Equal(4, found.Count); + Assert.Contains(Items[0], found); + Assert.Contains(Items[1], found); + Assert.Contains(Items[2], found); + Assert.Contains(Items[3], found); + + // Items should be processed by sector distance (ring-based) + // The center Item should have distance 0 + var centerIndex = found.IndexOf(Items[0]); + Assert.Equal(0, distances[centerIndex]); + } + finally + { + DeleteAll(Items); + } + } + + [Fact] + public void ItemByDistanceEnumerator_MapBoundsAreClamped() + { + var map = Map.Felucca; + var width = map.Width; + var height = map.Height; + + var center = new Point3D(width - 2, height - 2, 0); + const int range = Map.SectorSize * 2; + + var Items = new TestItem[1]; + try + { + Items[0] = CreateItem(map, center); + + var found = new List(); + foreach (var (Item, _) in map.GetItemsInRangeByDistance(center, range)) + { + found.Add(Item); + } + + Assert.Single(found); + Assert.Equal(Items[0], found[0]); + } + finally + { + DeleteAll(Items); + } + } + + [Fact] + public void ItemByDistanceEnumerator_NegativeRangeIsZero() + { + var map = Map.Felucca; + var center = new Point3D(1000, 1000, 0); + const int range = -5; + + var Items = new TestItem[2]; + try + { + Items[0] = CreateItem(map, center); + Items[1] = CreateItem(map, new Point3D(1001, 1000, 0)); // 1 tile away + + var found = new List(); + foreach (var (Item, _) in map.GetItemsInRangeByDistance(center, range)) + { + found.Add(Item); + } + + // With negative range creating a 1x1 bounds, only exact center matches + Assert.Single(found); + Assert.Equal(Items[0], found[0]); + } + finally + { + DeleteAll(Items); + } + } + + [Fact] + public void ItemByDistanceEnumerator_MultipleRings() + { + var map = Map.Felucca; + var center = new Point3D(1100, 1100, 0); + const int range = Map.SectorSize * 3; + + var Items = new List(); + try + { + // Create a grid of Items across multiple sectors + for (var ringOffset = 0; ringOffset <= 2; ringOffset++) + { + for (var side = 0; side < 4; side++) + { + var offset = ringOffset * Map.SectorSize; + Point3D pos = side switch + { + 0 => new Point3D(center.X + offset, center.Y, 0), + 1 => new Point3D(center.X, center.Y + offset, 0), + 2 => new Point3D(center.X - offset, center.Y, 0), + _ => new Point3D(center.X, center.Y - offset, 0) + }; + Items.Add(CreateItem(map, pos)); + } + } + + var found = new List(); + foreach (var (Item, _) in map.GetItemsInRangeByDistance(center, range)) + { + found.Add(Item); + } + + // Should find all Items within range + Assert.True(found.Count > 0); + Assert.All(found, Item => + { + var dx = Item.X - center.X; + var dy = Item.Y - center.Y; + var distSq = dx * dx + dy * dy; + Assert.True(distSq <= range * range); + }); + } + finally + { + foreach (var Item in Items) + { + Item?.Delete(); + } + } + } + + private static TestItem CreateItem(Map map, Point3D location) + { + var Item = new TestItem(World.NewItem); + Item.MoveToWorld(location, map); + return Item; + } + + private static void DeleteAll(TestItem[] Items) + { + for (var i = 0; i < Items.Length; i++) + { + Items[i]?.Delete(); + } + } + + [Fact] + public void ItemByDistanceEnumerator_Bounds_FindsItemsInBounds() + { + var map = Map.Felucca; + var bounds = new Rectangle2D(100, 100, 50, 50); + + var Items = new TestItem[3]; + try + { + // Item inside bounds + Items[0] = CreateItem(map, new Point3D(120, 120, 0)); + // Item at edge of bounds + Items[1] = CreateItem(map, new Point3D(149, 149, 0)); + // Item outside bounds + Items[2] = CreateItem(map, new Point3D(200, 200, 0)); + + var found = new List(); + foreach (var (Item, _) in map.GetItemsInBoundsByDistance(bounds)) + { + found.Add(Item); + } + + Assert.Equal(2, found.Count); + Assert.Contains(Items[0], found); + Assert.Contains(Items[1], found); + Assert.DoesNotContain(Items[2], found); + } + finally + { + DeleteAll(Items); + } + } + + [Fact] + public void ItemByDistanceEnumerator_Bounds_MakeBoundsInclusive() + { + var map = Map.Felucca; + var bounds = new Rectangle2D(100, 100, 50, 50); + + var Items = new TestItem[2]; + try + { + // Item at edge (inclusive) + Items[0] = CreateItem(map, new Point3D(149, 149, 0)); + // Item just outside edge (will be included with makeBoundsInclusive) + Items[1] = CreateItem(map, new Point3D(150, 150, 0)); + + var foundWithoutInclusive = new List(); + foreach (var (Item, _) in map.GetItemsInBoundsByDistance(bounds)) + { + foundWithoutInclusive.Add(Item); + } + + var foundWithInclusive = new List(); + foreach (var (Item, _) in map.GetItemsInBoundsByDistance(bounds, true)) + { + foundWithInclusive.Add(Item); + } + + Assert.Single(foundWithoutInclusive); + Assert.Contains(Items[0], foundWithoutInclusive); + + Assert.Equal(2, foundWithInclusive.Count); + Assert.Contains(Items[0], foundWithInclusive); + Assert.Contains(Items[1], foundWithInclusive); + } + finally + { + DeleteAll(Items); + } + } + + [Fact] + public void ItemByDistanceEnumerator_Bounds_ReturnsMinDistance() + { + var map = Map.Felucca; + var bounds = new Rectangle2D(300, 300, 20, 20); + + var Items = new TestItem[2]; + try + { + Items[0] = CreateItem(map, new Point3D(305, 305, 0)); + Items[1] = CreateItem(map, new Point3D(315, 315, 0)); + + var foundWithDistance = new List<(Item, int)>(); + foreach (var result in map.GetItemsInBoundsByDistance(bounds)) + { + foundWithDistance.Add(result); + } + + Assert.Equal(2, foundWithDistance.Count); + Assert.All(foundWithDistance, item => Assert.True(item.Item2 >= 0)); + } + finally + { + DeleteAll(Items); + } + } + + [Fact] + public void ItemByDistanceEnumerator_Bounds_OrdersByProximityToCenter() + { + var map = Map.Felucca; + var bounds = new Rectangle2D(500, 500, 64, 64); + + var Items = new TestItem[3]; + try + { + // Place Items at different distances from center + Items[0] = CreateItem(map, new Point3D(532, 532, 0)); // At center + Items[1] = CreateItem(map, new Point3D(548, 532, 0)); // 16 tiles away + Items[2] = CreateItem(map, new Point3D(563, 563, 0)); // Far corner + + var found = new List<(Item, int)>(); + foreach (var result in map.GetItemsInBoundsByDistance(bounds)) + { + found.Add(result); + } + + Assert.Equal(3, found.Count); + + // Verify ordering by distance - closer Items should be found earlier (lower minDistance) + var Item0Index = found.FindIndex(x => x.Item1 == Items[0]); + var Item1Index = found.FindIndex(x => x.Item1 == Items[1]); + var Item2Index = found.FindIndex(x => x.Item1 == Items[2]); + + // The minDistance should increase (or stay the same) as we go through the list + Assert.True(found[Item0Index].Item2 <= found[Item1Index].Item2); + Assert.True(found[Item1Index].Item2 <= found[Item2Index].Item2); + } + finally + { + DeleteAll(Items); + } + } + + // Test implementation of Item + private class TestItem : Item + { + public TestItem(Serial serial) : base(serial) + { + } + } + + private class TestItem2 : Item + { + public TestItem2(Serial serial) : base(serial) + { + } + } +} + diff --git a/Projects/Server.Tests/Tests/Maps/ItemEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/ItemEnumeratorTests.cs new file mode 100644 index 000000000..1a9495008 --- /dev/null +++ b/Projects/Server.Tests/Tests/Maps/ItemEnumeratorTests.cs @@ -0,0 +1,484 @@ +using System; +using System.Collections.Generic; +using Server.Items; +using Xunit; + +namespace Server.Tests.Tests.Maps; + +[Collection("Sequential Server Tests")] +public class ItemEnumeratorTests +{ + [Fact] + public void ItemEnumerator_FiltersByBoundsAndOrder() + { + var map = Map.Felucca; + var rect = new Rectangle2D(100, 100, 32, 32); + + var items = new Item[3]; + try + { + items[0] = CreateItem(map, new Point3D(105, 105, 0)); + items[1] = CreateItem(map, new Point3D(130, 130, 0)); + items[2] = CreateItem(map, new Point3D(90, 90, 0)); + + var found = new List(); + foreach (var item in map.GetItemsInBounds(rect)) + { + found.Add(item); + } + + Assert.Equal(2, found.Count); + Assert.All(found, item => Assert.True(rect.Contains(item.Location))); + Assert.Equal(new[] { items[0], items[1] }, found); + } + finally + { + DeleteAll(items); + } + } + + [Fact] + public void ItemEnumerator_DeletedItemsAreSkipped() + { + var map = Map.Felucca; + var rect = new Rectangle2D(200, 200, 16, 16); + + var items = new Item[3]; + try + { + items[0] = CreateItem(map, new Point3D(205, 205, 0)); + items[1] = CreateItem(map, new Point3D(206, 205, 0)); + items[2] = CreateItem(map, new Point3D(207, 205, 0)); + + items[1].Delete(); + + var found = new List(); + foreach (var item in map.GetItemsInBounds(rect)) + { + found.Add(item); + } + + Assert.Equal(new[] { items[0], items[2] }, found); + } + finally + { + DeleteAll(items); + } + } + + [Fact] + public void ItemEnumerator_ItemsWithParentAreSkipped() + { + var map = Map.Felucca; + var rect = new Rectangle2D(250, 250, 16, 16); + + var items = new Item[2]; + var container = new Container(0xE75); + try + { + items[0] = CreateItem(map, new Point3D(255, 255, 0)); + items[1] = CreateItem(map, new Point3D(256, 255, 0)); + container.MoveToWorld(new Point3D(255, 255, 0), map); + + // Move items[1] into the container - it should be skipped + items[1].Parent = container; + + var found = new List(); + foreach (var item in map.GetItemsInBounds(rect)) + { + found.Add(item); + } + + // Should only find items[0] and container, not items[1] (which has a parent) + Assert.Equal(2, found.Count); + Assert.Contains(items[0], found); + Assert.Contains(container, found); + Assert.DoesNotContain(items[1], found); + } + finally + { + DeleteAll(items); + container?.Delete(); + } + } + + [Fact] + public void ItemEnumerator_RespectsMakeBoundsInclusiveFlag() + { + var map = Map.Felucca; + var rect = new Rectangle2D(300, 300, 1, 1); + + var items = new Item[1]; + try + { + items[0] = CreateItem(map, new Point3D(301, 301, 0)); + + var enumerator = map.GetItemsInBounds(rect, makeBoundsInclusive: true).GetEnumerator(); + + Assert.True(enumerator.MoveNext()); + Assert.Equal(items[0], enumerator.Current); + } + finally + { + DeleteAll(items); + } + } + + [Fact] + public void ItemEnumerator_MapNullYieldsEmpty() + { + var enumerator = new Map.ItemEnumerator(null, Rectangle2D.Empty, false); + Assert.False(enumerator.MoveNext()); + } + + [Fact] + public void ItemEnumerator_ThrowsOnVersionChange() + { + var map = Map.Felucca; + var rect = new Rectangle2D(400, 400, 16, 16); + + var items = new[] + { + CreateItem(map, new Point3D(405, 405, 0)), + CreateItem(map, new Point3D(406, 405, 0)) + }; + + try + { + var enumerator = map.GetItemsInBounds(rect).GetEnumerator(); + Assert.True(enumerator.MoveNext()); + + items[1].Delete(); + + // Ref structs cannot be captured in lambdas, so we test the exception directly + var exceptionThrown = false; + try + { + enumerator.MoveNext(); + } + catch (InvalidOperationException) + { + exceptionThrown = true; + } + + Assert.True(exceptionThrown, "Expected InvalidOperationException when collection version changes"); + } + finally + { + DeleteAll(items); + } + } + + [Fact] + public void ItemEnumerator_StepsAcrossSectors() + { + var map = Map.Felucca; + var rect = new Rectangle2D(500, 500, Map.SectorSize * 2, Map.SectorSize * 2); + + var items = new[] + { + CreateItem(map, new Point3D(rect.X + 1, rect.Y + 1, 0)), + CreateItem(map, new Point3D(rect.X + Map.SectorSize + 1, rect.Y + 1, 0)), + CreateItem(map, new Point3D(rect.X + Map.SectorSize + 1, rect.Y + Map.SectorSize + 1, 0)) + }; + + try + { + var result = new List(); + foreach (var item in map.GetItemsInBounds(rect)) + { + result.Add(item); + } + + Assert.Equal(items, result); + } + finally + { + DeleteAll(items); + } + } + + [Fact] + public void ItemEnumerator_MapBoundsAreClamped() + { + var map = Map.Felucca; + var width = map.Width; + var height = map.Height; + + var rect = new Rectangle2D(width - Map.SectorSize - 2, height - Map.SectorSize - 2, Map.SectorSize * 2, Map.SectorSize * 2); + + var items = new[] + { + CreateItem(map, new Point3D(width - 2, height - 2, 0)) + }; + + try + { + var enumerator = map.GetItemsInBounds(rect).GetEnumerator(); + + Assert.True(enumerator.MoveNext()); + Assert.Equal(items[0], enumerator.Current); + Assert.False(enumerator.MoveNext()); + } + finally + { + DeleteAll(items); + } + } + + [Fact] + public void ItemAtEnumerator_FiltersExactLocation() + { + var map = Map.Felucca; + var location = new Point3D(600, 600, 0); + + var items = new Item[3]; + try + { + items[0] = CreateItem(map, location); + items[1] = CreateItem(map, location); + items[2] = CreateItem(map, new Point3D(601, 600, 0)); // Different location + + var found = new List(); + foreach (var item in map.GetItemsAt(location)) + { + found.Add(item); + } + + Assert.Equal(2, found.Count); + Assert.Contains(items[0], found); + Assert.Contains(items[1], found); + Assert.DoesNotContain(items[2], found); + } + finally + { + DeleteAll(items); + } + } + + [Fact] + public void ItemAtEnumerator_DeletedItemsAreSkipped() + { + var map = Map.Felucca; + var location = new Point3D(650, 650, 0); + + var items = new Item[3]; + try + { + items[0] = CreateItem(map, location); + items[1] = CreateItem(map, location); + items[2] = CreateItem(map, location); + + items[1].Delete(); + + var found = new List(); + foreach (var item in map.GetItemsAt(location)) + { + found.Add(item); + } + + Assert.Equal(new[] { items[0], items[2] }, found); + } + finally + { + DeleteAll(items); + } + } + + [Fact] + public void ItemAtEnumerator_ItemsWithParentAreSkipped() + { + var map = Map.Felucca; + var location = new Point3D(700, 700, 0); + + var items = new Item[2]; + var container = new Container(0xE75); + try + { + items[0] = CreateItem(map, location); + items[1] = CreateItem(map, location); + container.MoveToWorld(location, map); + + // Move items[1] into the container - it should be skipped + items[1].Parent = container; + + var found = new List(); + foreach (var item in map.GetItemsAt(location)) + { + found.Add(item); + } + + Assert.Equal(2, found.Count); + Assert.Contains(items[0], found); + Assert.Contains(container, found); + Assert.DoesNotContain(items[1], found); + } + finally + { + DeleteAll(items); + container?.Delete(); + } + } + + [Fact] + public void ItemAtEnumerator_MapNullYieldsEmpty() + { + var enumerator = new Map.ItemAtEnumerator(null, new Point2D(0, 0)); + Assert.False(enumerator.MoveNext()); + } + + [Fact] + public void ItemAtEnumerator_ThrowsOnVersionChange() + { + var map = Map.Felucca; + var location = new Point3D(750, 750, 0); + + var items = new[] + { + CreateItem(map, location), + CreateItem(map, location) + }; + + try + { + var enumerator = map.GetItemsAt(location).GetEnumerator(); + Assert.True(enumerator.MoveNext()); + + items[1].Delete(); + + // Ref structs cannot be captured in lambdas, so we test the exception directly + var exceptionThrown = false; + try + { + enumerator.MoveNext(); + } + catch (InvalidOperationException) + { + exceptionThrown = true; + } + + Assert.True(exceptionThrown, "Expected InvalidOperationException when collection version changes"); + } + finally + { + DeleteAll(items); + } + } + + [Fact] + public void ItemAtEnumerator_UsesDifferentPoint3DOverloads() + { + var map = Map.Felucca; + var location = new Point3D(800, 800, 5); + + var items = new Item[1]; + try + { + items[0] = CreateItem(map, location); + + // Test Point3D overload + var found1 = new List(); + foreach (var item in map.GetItemsAt(location)) + { + found1.Add(item); + } + + // Test (int, int) overload - should find the same item (Z is ignored) + var found2 = new List(); + foreach (var item in map.GetItemsAt(location.X, location.Y)) + { + found2.Add(item); + } + + // Test Point2D overload + var found3 = new List(); + foreach (var item in map.GetItemsAt(new Point2D(location.X, location.Y))) + { + found3.Add(item); + } + + Assert.Single(found1); + Assert.Equal(items[0], found1[0]); + Assert.Equal(found1, found2); + Assert.Equal(found1, found3); + } + finally + { + DeleteAll(items); + } + } + + [Fact] + public void ItemEnumerator_ZeroRangeReturnsOnlyCenter() + { + var map = Map.Felucca; + var center = new Point3D(850, 850, 0); + const int range = 0; + + var items = new Item[2]; + try + { + items[0] = CreateItem(map, center); // Exact center + items[1] = CreateItem(map, new Point3D(851, 850, 0)); // 1 tile away + + var found = new List(); + foreach (var item in map.GetItemsInRange(center, range)) + { + found.Add(item); + } + + Assert.Single(found); + Assert.Equal(items[0], found[0]); + } + finally + { + DeleteAll(items); + } + } + + [Fact] + public void ItemEnumerator_NegativeRangeCreates1x1Bounds() + { + var map = Map.Felucca; + var center = new Point3D(900, 900, 0); + const int range = -5; + + var items = new Item[2]; + try + { + items[0] = CreateItem(map, center); + items[1] = CreateItem(map, new Point3D(901, 900, 0)); // 1 tile away + + var found = new List(); + foreach (var item in map.GetItemsInRange(center, range)) + { + found.Add(item); + } + + // With negative range creating a 1x1 bounds, only exact center matches + Assert.Single(found); + Assert.Equal(items[0], found[0]); + } + finally + { + DeleteAll(items); + } + } + + private static Item CreateItem(Map map, Point3D location) + { + var item = new Item(0x1); + item.Movable = false; + item.MoveToWorld(location, map); + return item; + } + + private static void DeleteAll(Item[] items) + { + for (var i = 0; i < items.Length; i++) + { + items[i]?.Delete(); + } + } +} + diff --git a/Projects/Server.Tests/Tests/Maps/MobileByDistanceEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/MobileByDistanceEnumeratorTests.cs new file mode 100644 index 000000000..0a84da9e6 --- /dev/null +++ b/Projects/Server.Tests/Tests/Maps/MobileByDistanceEnumeratorTests.cs @@ -0,0 +1,608 @@ +using System; +using System.Collections.Generic; +using Xunit; + +namespace Server.Tests.Tests.Maps; + +[Collection("Sequential Server Tests")] +public class MobileByDistanceEnumeratorTests +{ + [Fact] + public void MobileByDistanceEnumerator_ReturnsNearbyMobiles() + { + var map = Map.Felucca; + var center = new Point3D(100, 100, 0); + const int range = 5; + + var mobiles = new TestMobile[3]; + try + { + mobiles[0] = CreateMobile(map, new Point3D(102, 102, 0)); // Within range + mobiles[1] = CreateMobile(map, new Point3D(98, 98, 0)); // Within range + mobiles[2] = CreateMobile(map, new Point3D(110, 110, 0)); // Outside range + + var found = new List(); + foreach (var (mobile, _) in map.GetMobilesInRangeByDistance(center, range)) + { + found.Add(mobile); + } + + Assert.Equal(2, found.Count); + Assert.Contains(mobiles[0], found); + Assert.Contains(mobiles[1], found); + Assert.DoesNotContain(mobiles[2], found); + } + finally + { + DeleteAll(mobiles); + } + } + + [Fact] + public void MobileByDistanceEnumerator_DeletedMobilesAreSkipped() + { + var map = Map.Felucca; + var center = new Point3D(200, 200, 0); + const int range = 5; + + var mobiles = new TestMobile[3]; + try + { + mobiles[0] = CreateMobile(map, new Point3D(202, 202, 0)); + mobiles[1] = CreateMobile(map, new Point3D(203, 202, 0)); + mobiles[2] = CreateMobile(map, new Point3D(204, 202, 0)); + + mobiles[1].Delete(); + + var found = new List(); + foreach (var (mobile, _) in map.GetMobilesInRangeByDistance(center, range)) + { + found.Add(mobile); + } + + Assert.Equal(2, found.Count); + Assert.Contains(mobiles[0], found); + Assert.Contains(mobiles[2], found); + Assert.DoesNotContain(mobiles[1], found); + } + finally + { + DeleteAll(mobiles); + } + } + + [Fact] + public void MobileByDistanceEnumerator_ReturnsMinDistance() + { + var map = Map.Felucca; + var center = new Point3D(300, 300, 0); + const int range = 10; + + var mobiles = new TestMobile[2]; + try + { + mobiles[0] = CreateMobile(map, new Point3D(305, 305, 0)); + mobiles[1] = CreateMobile(map, new Point3D(302, 302, 0)); + + var foundWithDistance = new List<(Mobile, int)>(); + foreach (var result in map.GetMobilesInRangeByDistance(center, range)) + { + foundWithDistance.Add(result); + } + + Assert.Equal(2, foundWithDistance.Count); + // Each mobile should have a non-negative min distance + Assert.All(foundWithDistance, item => Assert.True(item.Item2 >= 0)); + } + finally + { + DeleteAll(mobiles); + } + } + + [Fact] + public void MobileByDistanceEnumerator_OrderedBySector() + { + var map = Map.Felucca; + var center = new Point3D(400, 400, 0); + const int range = Map.SectorSize * 2; + + var mobiles = new TestMobile[3]; + try + { + // Place mobiles in different sectors + mobiles[0] = CreateMobile(map, new Point3D(center.X + 2, center.Y + 2, 0)); + mobiles[1] = CreateMobile(map, new Point3D(center.X + Map.SectorSize + 2, center.Y + 2, 0)); + mobiles[2] = CreateMobile(map, new Point3D(center.X + 2, center.Y + Map.SectorSize + 2, 0)); + + var found = new List(); + foreach (var (mobile, _) in map.GetMobilesInRangeByDistance(center, range)) + { + found.Add(mobile); + } + + Assert.Equal(3, found.Count); + Assert.Contains(mobiles[0], found); + Assert.Contains(mobiles[1], found); + Assert.Contains(mobiles[2], found); + } + finally + { + DeleteAll(mobiles); + } + } + + [Fact] + public void MobileByDistanceEnumerator_MapNullYieldsEmpty() + { + var center = new Point2D(0, 0); + var bounds = new Rectangle2D(center.m_X - 10, center.m_Y - 10, 21, 21); + var enumerator = new Map.MobileDistanceEnumerable(null, bounds, center, false).GetEnumerator(); + Assert.False(enumerator.MoveNext()); + } + + [Fact] + public void MobileByDistanceEnumerator_ThrowsOnVersionChange() + { + var map = Map.Felucca; + var center = new Point3D(500, 500, 0); + const int range = 5; + + var mobiles = new[] + { + CreateMobile(map, new Point3D(502, 502, 0)), + CreateMobile(map, new Point3D(503, 502, 0)) + }; + + try + { + var enumerator = map.GetMobilesInRangeByDistance(center, range).GetEnumerator(); + Assert.True(enumerator.MoveNext()); + + mobiles[1].Delete(); + + // Ref structs cannot be captured in lambdas, so we test the exception directly + var exceptionThrown = false; + try + { + enumerator.MoveNext(); + } + catch (InvalidOperationException) + { + exceptionThrown = true; + } + + Assert.True(exceptionThrown, "Expected InvalidOperationException when collection version changes"); + } + finally + { + DeleteAll(mobiles); + } + } + + [Fact] + public void MobileByDistanceEnumerator_ZeroRangeReturnsOnlyCenter() + { + var map = Map.Felucca; + var center = new Point3D(600, 600, 0); + const int range = 0; + + var mobiles = new TestMobile[2]; + try + { + mobiles[0] = CreateMobile(map, center); // Exact center + mobiles[1] = CreateMobile(map, new Point3D(601, 600, 0)); // 1 tile away + + var found = new List(); + foreach (var (mobile, _) in map.GetMobilesInRangeByDistance(center, range)) + { + found.Add(mobile); + } + + Assert.Single(found); + Assert.Equal(mobiles[0], found[0]); + } + finally + { + DeleteAll(mobiles); + } + } + + [Fact] + public void MobileByDistanceEnumerator_FiltersByType() + { + var map = Map.Felucca; + var center = new Point3D(700, 700, 0); + const int range = 5; + + var player = new TestPlayerMobile(World.NewMobile); + var npc = new TestMobile(World.NewMobile); + + try + { + player.DefaultMobileInit(); + npc.DefaultMobileInit(); + player.MoveToWorld(new Point3D(702, 702, 0), map); + npc.MoveToWorld(new Point3D(703, 702, 0), map); + + var foundPlayers = new List(); + foreach (var (mobile, _) in map.GetMobilesInRangeByDistance(center, range)) + { + foundPlayers.Add(mobile); + } + + Assert.Single(foundPlayers); + Assert.Equal(player, foundPlayers[0]); + } + finally + { + player.Delete(); + npc.Delete(); + } + } + + [Fact] + public void MobileByDistanceEnumerator_UsesDifferentPointOverloads() + { + var map = Map.Felucca; + var center = new Point3D(800, 800, 5); + const int range = 5; + + var mobiles = new TestMobile[1]; + try + { + mobiles[0] = CreateMobile(map, new Point3D(802, 802, 0)); + + // Test Point3D overload + var found1 = new List(); + foreach (var (mobile, _) in map.GetMobilesInRangeByDistance(center, range)) + { + found1.Add(mobile); + } + + // Test (int, int) overload + var found2 = new List(); + foreach (var (mobile, _) in map.GetMobilesInRangeByDistance(center.X, center.Y, range)) + { + found2.Add(mobile); + } + + // Test Point2D overload + var found3 = new List(); + foreach (var (mobile, _) in map.GetMobilesInRangeByDistance(new Point2D(center.X, center.Y), range)) + { + found3.Add(mobile); + } + + Assert.Single(found1); + Assert.Equal(mobiles[0], found1[0]); + Assert.Equal(found1, found2); + Assert.Equal(found1, found3); + } + finally + { + DeleteAll(mobiles); + } + } + + [Fact] + public void MobileByDistanceEnumerator_RingTraversal() + { + var map = Map.Felucca; + var center = new Point3D(900, 900, 0); + const int range = Map.SectorSize * 2; + + var mobiles = new TestMobile[4]; + try + { + // Place mobiles in different rings around the center + mobiles[0] = CreateMobile(map, center); // Ring 0 (center sector) + mobiles[1] = CreateMobile(map, new Point3D(center.X + Map.SectorSize, center.Y, 0)); // Ring 1 + mobiles[2] = CreateMobile(map, new Point3D(center.X, center.Y + Map.SectorSize, 0)); // Ring 1 + mobiles[3] = CreateMobile(map, new Point3D(center.X + Map.SectorSize * 2 - 1, center.Y, 0)); // Ring 2 + + var found = new List(); + var distances = new List(); + foreach (var (mobile, minDistance) in map.GetMobilesInRangeByDistance(center, range)) + { + found.Add(mobile); + distances.Add(minDistance); + } + + // All mobiles should be found + Assert.Equal(4, found.Count); + Assert.Contains(mobiles[0], found); + Assert.Contains(mobiles[1], found); + Assert.Contains(mobiles[2], found); + Assert.Contains(mobiles[3], found); + + // Mobiles should be processed by sector distance (ring-based) + // The center mobile should have distance 0 + var centerIndex = found.IndexOf(mobiles[0]); + Assert.Equal(0, distances[centerIndex]); + } + finally + { + DeleteAll(mobiles); + } + } + + [Fact] + public void MobileByDistanceEnumerator_MapBoundsAreClamped() + { + var map = Map.Felucca; + var width = map.Width; + var height = map.Height; + + var center = new Point3D(width - 2, height - 2, 0); + const int range = Map.SectorSize * 2; + + var mobiles = new TestMobile[1]; + try + { + mobiles[0] = CreateMobile(map, center); + + var found = new List(); + foreach (var (mobile, _) in map.GetMobilesInRangeByDistance(center, range)) + { + found.Add(mobile); + } + + Assert.Single(found); + Assert.Equal(mobiles[0], found[0]); + } + finally + { + DeleteAll(mobiles); + } + } + + [Fact] + public void MobileByDistanceEnumerator_NegativeRangeIsZero() + { + var map = Map.Felucca; + var center = new Point3D(1000, 1000, 0); + const int range = -5; + + var mobiles = new TestMobile[2]; + try + { + mobiles[0] = CreateMobile(map, center); + mobiles[1] = CreateMobile(map, new Point3D(1001, 1000, 0)); + + var found = new List(); + foreach (var (mobile, _) in map.GetMobilesInRangeByDistance(center, range)) + { + found.Add(mobile); + } + + // With negative range creating a 1x1 bounds, only exact center matches + Assert.Single(found); + Assert.Equal(mobiles[0], found[0]); + } + finally + { + DeleteAll(mobiles); + } + } + + [Fact] + public void MobileByDistanceEnumerator_MultipleRings() + { + var map = Map.Felucca; + var center = new Point3D(1100, 1100, 0); + const int range = Map.SectorSize * 3; + + var mobiles = new List(); + try + { + // Create a grid of mobiles across multiple sectors + for (var ringOffset = 0; ringOffset <= 2; ringOffset++) + { + for (var side = 0; side < 4; side++) + { + var offset = ringOffset * Map.SectorSize; + Point3D pos = side switch + { + 0 => new Point3D(center.X + offset, center.Y, 0), + 1 => new Point3D(center.X, center.Y + offset, 0), + 2 => new Point3D(center.X - offset, center.Y, 0), + _ => new Point3D(center.X, center.Y - offset, 0) + }; + mobiles.Add(CreateMobile(map, pos)); + } + } + + var found = new List(); + foreach (var (mobile, _) in map.GetMobilesInRangeByDistance(center, range)) + { + found.Add(mobile); + } + + // Should find all mobiles within range + Assert.True(found.Count > 0); + Assert.All(found, mobile => + { + var dx = mobile.X - center.X; + var dy = mobile.Y - center.Y; + var distSq = dx * dx + dy * dy; + Assert.True(distSq <= range * range); + }); + } + finally + { + foreach (var mobile in mobiles) + { + mobile?.Delete(); + } + } + } + + private static TestMobile CreateMobile(Map map, Point3D location) + { + var mobile = new TestMobile(World.NewMobile); + mobile.DefaultMobileInit(); + mobile.MoveToWorld(location, map); + return mobile; + } + + private static void DeleteAll(TestMobile[] mobiles) + { + for (var i = 0; i < mobiles.Length; i++) + { + mobiles[i]?.Delete(); + } + } + + [Fact] + public void MobileByDistanceEnumerator_Bounds_FindsMobilesInBounds() + { + var map = Map.Felucca; + var bounds = new Rectangle2D(100, 100, 50, 50); + + var mobiles = new TestMobile[3]; + try + { + // Mobile inside bounds + mobiles[0] = CreateMobile(map, new Point3D(120, 120, 0)); + // Mobile at edge of bounds + mobiles[1] = CreateMobile(map, new Point3D(149, 149, 0)); + // Mobile outside bounds + mobiles[2] = CreateMobile(map, new Point3D(200, 200, 0)); + + var found = new List(); + foreach (var (mobile, _) in map.GetMobilesInBoundsByDistance(bounds)) + { + found.Add(mobile); + } + + Assert.Equal(2, found.Count); + Assert.Contains(mobiles[0], found); + Assert.Contains(mobiles[1], found); + Assert.DoesNotContain(mobiles[2], found); + } + finally + { + DeleteAll(mobiles); + } + } + + [Fact] + public void MobileByDistanceEnumerator_Bounds_MakeBoundsInclusive() + { + var map = Map.Felucca; + var bounds = new Rectangle2D(100, 100, 50, 50); + + var mobiles = new TestMobile[2]; + try + { + // Mobile at edge (inclusive) + mobiles[0] = CreateMobile(map, new Point3D(149, 149, 0)); + // Mobile just outside edge (will be included with makeBoundsInclusive) + mobiles[1] = CreateMobile(map, new Point3D(150, 150, 0)); + + var foundWithoutInclusive = new List(); + foreach (var (mobile, _) in map.GetMobilesInBoundsByDistance(bounds)) + { + foundWithoutInclusive.Add(mobile); + } + + var foundWithInclusive = new List(); + foreach (var (mobile, _) in map.GetMobilesInBoundsByDistance(bounds, true)) + { + foundWithInclusive.Add(mobile); + } + + Assert.Single(foundWithoutInclusive); + Assert.Contains(mobiles[0], foundWithoutInclusive); + + Assert.Equal(2, foundWithInclusive.Count); + Assert.Contains(mobiles[0], foundWithInclusive); + Assert.Contains(mobiles[1], foundWithInclusive); + } + finally + { + DeleteAll(mobiles); + } + } + + [Fact] + public void MobileByDistanceEnumerator_Bounds_ReturnsMinDistance() + { + var map = Map.Felucca; + var bounds = new Rectangle2D(300, 300, 20, 20); + + var mobiles = new TestMobile[2]; + try + { + mobiles[0] = CreateMobile(map, new Point3D(305, 305, 0)); + mobiles[1] = CreateMobile(map, new Point3D(315, 315, 0)); + + var foundWithDistance = new List<(Mobile, int)>(); + foreach (var result in map.GetMobilesInBoundsByDistance(bounds)) + { + foundWithDistance.Add(result); + } + + Assert.Equal(2, foundWithDistance.Count); + Assert.All(foundWithDistance, item => Assert.True(item.Item2 >= 0)); + } + finally + { + DeleteAll(mobiles); + } + } + + [Fact] + public void MobileByDistanceEnumerator_Bounds_OrdersByProximityToCenter() + { + var map = Map.Felucca; + var bounds = new Rectangle2D(500, 500, 64, 64); + + var mobiles = new TestMobile[3]; + try + { + // Place mobiles at different distances from center + mobiles[0] = CreateMobile(map, new Point3D(532, 532, 0)); // At center + mobiles[1] = CreateMobile(map, new Point3D(548, 532, 0)); // 16 tiles away + mobiles[2] = CreateMobile(map, new Point3D(563, 563, 0)); // Far corner + + var found = new List<(Mobile, int)>(); + foreach (var result in map.GetMobilesInBoundsByDistance(bounds)) + { + found.Add(result); + } + + Assert.Equal(3, found.Count); + + // Verify ordering by distance - closer mobiles should be found earlier (lower minDistance) + var mobile0Index = found.FindIndex(x => x.Item1 == mobiles[0]); + var mobile1Index = found.FindIndex(x => x.Item1 == mobiles[1]); + var mobile2Index = found.FindIndex(x => x.Item1 == mobiles[2]); + + // The minDistance should increase (or stay the same) as we go through the list + Assert.True(found[mobile0Index].Item2 <= found[mobile1Index].Item2); + Assert.True(found[mobile1Index].Item2 <= found[mobile2Index].Item2); + } + finally + { + DeleteAll(mobiles); + } + } + + // Test implementation of Mobile + private class TestMobile : Mobile + { + public TestMobile(Serial serial) : base(serial) + { + } + } + + private class TestPlayerMobile : Mobile + { + public TestPlayerMobile(Serial serial) : base(serial) + { + } + } +} + diff --git a/Projects/Server.Tests/Tests/Maps/MobileEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/MobileEnumeratorTests.cs new file mode 100644 index 000000000..5df3d4b69 --- /dev/null +++ b/Projects/Server.Tests/Tests/Maps/MobileEnumeratorTests.cs @@ -0,0 +1,263 @@ +using System; +using System.Collections.Generic; +using Xunit; + +namespace Server.Tests.Tests.Maps; + +[Collection("Sequential Server Tests")] +public class MobileEnumeratorTests +{ + [Fact] + public void MobileEnumerator_FiltersByBoundsAndOrder() + { + var map = Map.Felucca; + var rect = new Rectangle2D(100, 100, 32, 32); + + var mobiles = new Mobile[3]; + try + { + mobiles[0] = CreateMobile(map, new Point3D(105, 105, 0)); + mobiles[1] = CreateMobile(map, new Point3D(130, 130, 0)); + mobiles[2] = CreateMobile(map, new Point3D(90, 90, 0)); + + var found = new List(); + foreach (var m in map.GetMobilesInBounds(rect)) + { + found.Add(m); + } + + Assert.Equal(2, found.Count); + Assert.All(found, m => Assert.True(rect.Contains(m.Location))); + Assert.Equal(new[] { mobiles[0], mobiles[1] }, found); + } + finally + { + DeleteAll(mobiles); + } + } + + [Fact] + public void MobileEnumerator_DeletedMobilesAreSkipped() + { + var map = Map.Felucca; + var rect = new Rectangle2D(200, 200, 16, 16); + + var mobiles = new Mobile[3]; + try + { + mobiles[0] = CreateMobile(map, new Point3D(205, 205, 0)); + mobiles[1] = CreateMobile(map, new Point3D(206, 205, 0)); + mobiles[2] = CreateMobile(map, new Point3D(207, 205, 0)); + + mobiles[1].Delete(); + + var found = new List(); + foreach (var m in map.GetMobilesInBounds(rect)) + { + found.Add(m); + } + + Assert.Equal(new[] { mobiles[0], mobiles[2] }, found); + } + finally + { + DeleteAll(mobiles); + } + } + + [Fact] + public void MobileEnumerator_RespectsMakeBoundsInclusiveFlag() + { + var map = Map.Felucca; + var rect = new Rectangle2D(300, 300, 1, 1); + + var mobiles = new Mobile[1]; + try + { + mobiles[0] = CreateMobile(map, new Point3D(301, 301, 0)); + + var enumerator = map.GetMobilesInBounds(rect, makeBoundsInclusive: true).GetEnumerator(); + + Assert.True(enumerator.MoveNext()); + Assert.Equal(mobiles[0], enumerator.Current); + } + finally + { + DeleteAll(mobiles); + } + } + + [Fact] + public void MobileEnumerator_MapNullYieldsEmpty() + { + var enumerator = new Map.MobileEnumerator(null, Rectangle2D.Empty, false); + Assert.False(enumerator.MoveNext()); + } + + [Fact] + public void MobileEnumerator_ThrowsOnVersionChange() + { + var map = Map.Felucca; + var rect = new Rectangle2D(400, 400, 16, 16); + + var mobiles = new[] + { + CreateMobile(map, new Point3D(405, 405, 0)), + CreateMobile(map, new Point3D(406, 405, 0)) + }; + + try + { + var enumerator = map.GetMobilesInBounds(rect).GetEnumerator(); + Assert.True(enumerator.MoveNext()); + + mobiles[1].Delete(); + + var exceptionThrown = false; + try + { + enumerator.MoveNext(); + } + catch (InvalidOperationException) + { + exceptionThrown = true; + } + + Assert.True(exceptionThrown, "Expected InvalidOperationException when collection version changes"); + } + finally + { + DeleteAll(mobiles); + } + } + + [Fact] + public void MobileEnumerator_StepsAcrossSectors() + { + var map = Map.Felucca; + var rect = new Rectangle2D(500, 500, Map.SectorSize * 2, Map.SectorSize * 2); + + var mobiles = new[] + { + CreateMobile(map, new Point3D(rect.X + 1, rect.Y + 1, 0)), + CreateMobile(map, new Point3D(rect.X + Map.SectorSize + 1, rect.Y + 1, 0)), + CreateMobile(map, new Point3D(rect.X + Map.SectorSize + 1, rect.Y + Map.SectorSize + 1, 0)) + }; + + try + { + var result = new List(); + foreach (var m in map.GetMobilesInBounds(rect)) + { + result.Add(m); + } + + Assert.Equal(mobiles, result); + } + finally + { + DeleteAll(mobiles); + } + } + + [Fact] + public void MobileEnumerator_MapBoundsAreClamped() + { + var map = Map.Felucca; + var width = map.Width; + var height = map.Height; + + var rect = new Rectangle2D(width - Map.SectorSize - 2, height - Map.SectorSize - 2, Map.SectorSize * 2, Map.SectorSize * 2); + + var mobiles = new[] + { + CreateMobile(map, new Point3D(width - 2, height - 2, 0)) + }; + + try + { + var enumerator = map.GetMobilesInBounds(rect).GetEnumerator(); + + Assert.True(enumerator.MoveNext()); + Assert.Equal(mobiles[0], enumerator.Current); + Assert.False(enumerator.MoveNext()); + } + finally + { + DeleteAll(mobiles); + } + } + + [Fact] + public void MobileEnumerator_ZeroRangeReturnsOnlyCenter() + { + var map = Map.Felucca; + var center = new Point3D(700, 700, 0); + const int range = 0; + + var mobiles = new Mobile[2]; + try + { + mobiles[0] = CreateMobile(map, center); // Exact center + mobiles[1] = CreateMobile(map, new Point3D(701, 700, 0)); // 1 tile away + + var found = new List(); + foreach (var mobile in map.GetMobilesInRange(center, range)) + { + found.Add(mobile); + } + + Assert.Single(found); + Assert.Equal(mobiles[0], found[0]); + } + finally + { + DeleteAll(mobiles); + } + } + + [Fact] + public void MobileEnumerator_NegativeRangeCreates1x1Bounds() + { + var map = Map.Felucca; + var center = new Point3D(750, 750, 0); + const int range = -5; + + var mobiles = new Mobile[2]; + try + { + mobiles[0] = CreateMobile(map, center); + mobiles[1] = CreateMobile(map, new Point3D(751, 750, 0)); // 1 tile away + + var found = new List(); + foreach (var mobile in map.GetMobilesInRange(center, range)) + { + found.Add(mobile); + } + + // With negative range creating a 1x1 bounds, only exact center matches + Assert.Single(found); + Assert.Equal(mobiles[0], found[0]); + } + finally + { + DeleteAll(mobiles); + } + } + + private static Mobile CreateMobile(Map map, Point3D location) + { + var mobile = new Mobile(World.NewMobile); + mobile.DefaultMobileInit(); + mobile.MoveToWorld(location, map); + return mobile; + } + + private static void DeleteAll(Mobile[] mobiles) + { + for (var i = 0; i < mobiles.Length; i++) + { + mobiles[i]?.Delete(); + } + } +} diff --git a/Projects/Server.Tests/Tests/Maps/MultiEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/MultiEnumeratorTests.cs new file mode 100644 index 000000000..d99f7bbad --- /dev/null +++ b/Projects/Server.Tests/Tests/Maps/MultiEnumeratorTests.cs @@ -0,0 +1,412 @@ +using System; +using System.Collections.Generic; +using Server.Items; +using Xunit; + +namespace Server.Tests.Tests.Maps; + +[Collection("Sequential Server Tests")] +public class MultiEnumeratorTests +{ + [Fact] + public void MultiEnumerator_FiltersByBoundsAndOrder() + { + var map = Map.Felucca; + var rect = new Rectangle2D(100, 100, 32, 32); + + var multis = new TestMulti[3]; + try + { + multis[0] = CreateMulti(map, new Point3D(105, 105, 0)); + multis[1] = CreateMulti(map, new Point3D(130, 130, 0)); + multis[2] = CreateMulti(map, new Point3D(90, 90, 0)); + + var found = new List(); + foreach (var multi in map.GetMultisInBounds(rect)) + { + found.Add(multi); + } + + Assert.Equal(2, found.Count); + Assert.All(found, multi => Assert.True(rect.Contains(multi.Location))); + Assert.Equal(new[] { multis[0], multis[1] }, found); + } + finally + { + DeleteAll(multis); + } + } + + [Fact] + public void MultiEnumerator_DeletedMultisAreSkipped() + { + var map = Map.Felucca; + var rect = new Rectangle2D(200, 200, 16, 16); + + var multis = new TestMulti[3]; + try + { + multis[0] = CreateMulti(map, new Point3D(205, 205, 0)); + multis[1] = CreateMulti(map, new Point3D(206, 205, 0)); + multis[2] = CreateMulti(map, new Point3D(207, 205, 0)); + + multis[1].Delete(); + + var found = new List(); + foreach (var multi in map.GetMultisInBounds(rect)) + { + found.Add(multi); + } + + Assert.Equal(new[] { multis[0], multis[2] }, found); + } + finally + { + DeleteAll(multis); + } + } + + [Fact] + public void MultiEnumerator_RespectsMakeBoundsInclusiveFlag() + { + var map = Map.Felucca; + var rect = new Rectangle2D(300, 300, 1, 1); + + var multis = new TestMulti[1]; + try + { + multis[0] = CreateMulti(map, new Point3D(301, 301, 0)); + + var enumerator = map.GetMultisInBounds(rect, makeBoundsInclusive: true).GetEnumerator(); + + Assert.True(enumerator.MoveNext()); + Assert.Equal(multis[0], enumerator.Current); + } + finally + { + DeleteAll(multis); + } + } + + [Fact] + public void MultiEnumerator_MapNullYieldsEmpty() + { + var enumerator = new Map.MultiBoundsEnumerable(null, Rectangle2D.Empty, false).GetEnumerator(); + Assert.False(enumerator.MoveNext()); + } + + [Fact] + public void MultiEnumerator_ThrowsOnVersionChange() + { + var map = Map.Felucca; + var rect = new Rectangle2D(400, 400, 16, 16); + + var multis = new[] + { + CreateMulti(map, new Point3D(405, 405, 0)), + CreateMulti(map, new Point3D(406, 405, 0)) + }; + + try + { + var enumerator = map.GetMultisInBounds(rect).GetEnumerator(); + Assert.True(enumerator.MoveNext()); + + multis[1].Delete(); + + // Ref structs cannot be captured in lambdas, so we test the exception directly + var exceptionThrown = false; + try + { + enumerator.MoveNext(); + } + catch (InvalidOperationException) + { + exceptionThrown = true; + } + + Assert.True(exceptionThrown, "Expected InvalidOperationException when collection version changes"); + } + finally + { + DeleteAll(multis); + } + } + + [Fact] + public void MultiEnumerator_StepsAcrossSectors() + { + var map = Map.Felucca; + var rect = new Rectangle2D(500, 500, Map.SectorSize * 2, Map.SectorSize * 2); + + var multis = new[] + { + CreateMulti(map, new Point3D(rect.X + 1, rect.Y + 1, 0)), + CreateMulti(map, new Point3D(rect.X + Map.SectorSize + 1, rect.Y + 1, 0)), + CreateMulti(map, new Point3D(rect.X + Map.SectorSize + 1, rect.Y + Map.SectorSize + 1, 0)) + }; + + try + { + var result = new List(); + foreach (var multi in map.GetMultisInBounds(rect)) + { + result.Add(multi); + } + + Assert.Equal(multis, result); + } + finally + { + DeleteAll(multis); + } + } + + [Fact] + public void MultiEnumerator_MapBoundsAreClamped() + { + var map = Map.Felucca; + var width = map.Width; + var height = map.Height; + + var rect = new Rectangle2D(width - Map.SectorSize - 2, height - Map.SectorSize - 2, Map.SectorSize * 2, Map.SectorSize * 2); + + var multis = new[] + { + CreateMulti(map, new Point3D(width - 2, height - 2, 0)) + }; + + try + { + var enumerator = map.GetMultisInBounds(rect).GetEnumerator(); + + Assert.True(enumerator.MoveNext()); + Assert.Equal(multis[0], enumerator.Current); + Assert.False(enumerator.MoveNext()); + } + finally + { + DeleteAll(multis); + } + } + + [Fact] + public void MultiEnumerator_GetMultisInRange() + { + var map = Map.Felucca; + var center = new Point3D(600, 600, 0); + var range = 5; + + var multis = new TestMulti[3]; + try + { + multis[0] = CreateMulti(map, new Point3D(602, 602, 0)); // Within range + multis[1] = CreateMulti(map, new Point3D(598, 598, 0)); // Within range + multis[2] = CreateMulti(map, new Point3D(610, 610, 0)); // Outside range + + var found = new List(); + foreach (var multi in map.GetMultisInRange(center, range)) + { + found.Add(multi); + } + + Assert.Equal(2, found.Count); + Assert.Contains(multis[0], found); + Assert.Contains(multis[1], found); + Assert.DoesNotContain(multis[2], found); + } + finally + { + DeleteAll(multis); + } + } + + [Fact] + public void MultiSectorEnumerator_FiltersToSingleSector() + { + var map = Map.Felucca; + var location = new Point3D(700, 700, 0); + + var multis = new TestMulti[2]; + try + { + multis[0] = CreateMulti(map, location); + multis[1] = CreateMulti(map, new Point3D(location.X + 1, location.Y, 0)); + + var found = new List(); + foreach (var multi in map.GetMultisInSector(location)) + { + found.Add(multi); + } + + // Both should be in the same sector + Assert.Equal(2, found.Count); + Assert.Contains(multis[0], found); + Assert.Contains(multis[1], found); + } + finally + { + DeleteAll(multis); + } + } + + [Fact] + public void MultiSectorEnumerator_DeletedMultisAreSkipped() + { + var map = Map.Felucca; + var location = new Point3D(750, 750, 0); + + var multis = new TestMulti[3]; + try + { + multis[0] = CreateMulti(map, location); + multis[1] = CreateMulti(map, new Point3D(location.X + 1, location.Y, 0)); + multis[2] = CreateMulti(map, new Point3D(location.X + 2, location.Y, 0)); + + multis[1].Delete(); + + var found = new List(); + foreach (var multi in map.GetMultisInRange(location, 10)) + { + found.Add(multi); + } + + Assert.Equal(new[] { multis[0], multis[2] }, found); + } + finally + { + DeleteAll(multis); + } + } + + [Fact] + public void MultiSectorEnumerator_MapNullYieldsEmpty() + { + var enumerator = new Map.MultiSectorEnumerable(null, new Point2D(0, 0)).GetEnumerator(); + Assert.False(enumerator.MoveNext()); + } + + [Fact] + public void MultiSectorEnumerator_UsesDifferentPointOverloads() + { + var map = Map.Felucca; + var location = new Point3D(800, 800, 5); + + var multis = new TestMulti[1]; + try + { + multis[0] = CreateMulti(map, location); + + // Test Point3D overload + var found1 = new List(); + foreach (var multi in map.GetMultisInSector(location)) + { + found1.Add(multi); + } + + // Test (int, int) overload + var found2 = new List(); + foreach (var multi in map.GetMultisInSector(location.X, location.Y)) + { + found2.Add(multi); + } + + // Test Point2D overload + var found3 = new List(); + foreach (var multi in map.GetMultisInSector(new Point2D(location.X, location.Y))) + { + found3.Add(multi); + } + + Assert.Single(found1); + Assert.Equal(multis[0], found1[0]); + Assert.Equal(found1, found2); + Assert.Equal(found1, found3); + } + finally + { + DeleteAll(multis); + } + } + + [Fact] + public void MultiEnumerator_ZeroRangeReturnsOnlyCenter() + { + var map = Map.Felucca; + var center = new Point3D(800, 800, 0); + const int range = 0; + + var multis = new TestMulti[2]; + try + { + multis[0] = CreateMulti(map, center); // Exact center + multis[1] = CreateMulti(map, new Point3D(801, 800, 0)); // 1 tile away + + var found = new List(); + foreach (var multi in map.GetMultisInRange(center, range)) + { + found.Add(multi); + } + + Assert.Single(found); + Assert.Equal(multis[0], found[0]); + } + finally + { + DeleteAll(multis); + } + } + + [Fact] + public void MultiEnumerator_NegativeRangeCreates1x1Bounds() + { + var map = Map.Felucca; + var center = new Point3D(850, 850, 0); + const int range = -5; + + var multis = new TestMulti[2]; + try + { + multis[0] = CreateMulti(map, center); + multis[1] = CreateMulti(map, new Point3D(851, 850, 0)); // 1 tile away + + var found = new List(); + foreach (var multi in map.GetMultisInRange(center, range)) + { + found.Add(multi); + } + + // With negative range creating a 1x1 bounds, only exact center matches + Assert.Single(found); + Assert.Equal(multis[0], found[0]); + } + finally + { + DeleteAll(multis); + } + } + + private static TestMulti CreateMulti(Map map, Point3D location) + { + var multi = new TestMulti(); + multi.MoveToWorld(location, map); + return multi; + } + + private static void DeleteAll(TestMulti[] multis) + { + for (var i = 0; i < multis.Length; i++) + { + multis[i]?.Delete(); + } + } + + // Test implementation of BaseMulti + private class TestMulti : BaseMulti + { + public TestMulti() : base(0x1) + { + } + } +} + diff --git a/Projects/Server.Tests/Tests/Maps/StaticTileEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/StaticTileEnumeratorTests.cs new file mode 100644 index 000000000..6e9f3c345 --- /dev/null +++ b/Projects/Server.Tests/Tests/Maps/StaticTileEnumeratorTests.cs @@ -0,0 +1,319 @@ +using System.Collections.Generic; +using Server.Items; +using Xunit; + +namespace Server.Tests.Tests.Maps; + +[Collection("Sequential Server Tests")] +public class StaticTileEnumeratorTests +{ + [Fact] + public void StaticTileEnumerator_MapNullYieldsEmpty() + { + var enumerator = new Map.StaticTileEnumerable(null, new Point2D(0, 0)).GetEnumerator(); + Assert.False(enumerator.MoveNext()); + } + + [Fact] + public void StaticTileEnumerator_EmptyLocationYieldsEmpty() + { + var map = Map.Felucca; + var location = new Point2D(100, 100); + + var tiles = new List(); + foreach (var tile in new Map.StaticTileEnumerable(map, location, includeStatics: true, includeMultis: false)) + { + tiles.Add(tile); + } + + // Since we don't have actual map files loaded, this should be empty + Assert.Empty(tiles); + } + + [Fact] + public void StaticTileEnumerator_IncludeStaticsOnlyWorks() + { + var map = Map.Felucca; + var location = new Point2D(200, 200); + + TestMulti multi = null; + try + { + // Create a multi at the location + multi = CreateMultiWithComponents(map, new Point3D(200, 200, 0)); + + // Get tiles with statics only (no multis) + var tiles = new List(); + foreach (var tile in new Map.StaticTileEnumerable(map, location, includeStatics: true, includeMultis: false)) + { + tiles.Add(tile); + } + + // Should not include multi tiles + Assert.Empty(tiles); + } + finally + { + multi?.Delete(); + } + } + + [Fact] + public void StaticTileEnumerator_IncludeMultisOnlyWorks() + { + var map = Map.Felucca; + var location = new Point2D(300, 300); + + TestMulti multi = null; + try + { + // Create a multi at the location with components + multi = CreateMultiWithComponents(map, new Point3D(300, 300, 0)); + + // Get tiles with multis only (no statics) + var tiles = new List(); + foreach (var tile in new Map.StaticTileEnumerable(map, location, includeStatics: false, includeMultis: true)) + { + tiles.Add(tile); + } + + // Should include multi tiles + Assert.NotEmpty(tiles); + } + finally + { + multi?.Delete(); + } + } + + [Fact] + public void StaticTileEnumerator_IncludeBothStaticsAndMultisWorks() + { + var map = Map.Felucca; + var location = new Point2D(400, 400); + + TestMulti multi = null; + try + { + // Create a multi at the location + multi = CreateMultiWithComponents(map, new Point3D(400, 400, 0)); + + // Get all tiles (statics and multis) + var tiles = new List(); + foreach (var tile in new Map.StaticTileEnumerable(map, location, includeStatics: true, includeMultis: true)) + { + tiles.Add(tile); + } + + // Should include multi tiles (statics would be empty without map files) + Assert.NotEmpty(tiles); + } + finally + { + multi?.Delete(); + } + } + + [Fact] + public void StaticTileEnumerator_MultiTileZOffsetApplied() + { + var map = Map.Felucca; + var location = new Point2D(500, 500); + var multiZ = 10; + + TestMulti multi = null; + try + { + // Create a multi at Z=10 + multi = CreateMultiWithComponents(map, new Point3D(500, 500, multiZ)); + + // Get multi tiles + var tiles = new List(); + foreach (var tile in new Map.StaticTileEnumerable(map, location, includeStatics: false, includeMultis: true)) + { + tiles.Add(tile); + } + + // All tiles should have Z offset by the multi's Z position + Assert.NotEmpty(tiles); + Assert.All(tiles, tile => Assert.True(tile.Z >= multiZ)); + } + finally + { + multi?.Delete(); + } + } + + [Fact] + public void StaticTileEnumerator_TileMatrixGetStaticTilesWorks() + { + var map = Map.Felucca; + var x = 600; + var y = 600; + + // Test the TileMatrix.GetStaticTiles method + var tiles = new List(); + foreach (var tile in map.Tiles.GetStaticTiles(x, y)) + { + tiles.Add(tile); + } + + // Without map files loaded, should be empty + Assert.Empty(tiles); + } + + [Fact] + public void StaticTileEnumerator_TileMatrixGetStaticAndMultiTilesWorks() + { + var map = Map.Felucca; + var x = 700; + var y = 700; + + TestMulti multi = null; + try + { + // Create a multi at the location + multi = CreateMultiWithComponents(map, new Point3D(700, 700, 0)); + + // Test the TileMatrix.GetStaticAndMultiTiles method + var tiles = new List(); + foreach (var tile in map.Tiles.GetStaticAndMultiTiles(x, y)) + { + tiles.Add(tile); + } + + // Should include multi tiles + Assert.NotEmpty(tiles); + } + finally + { + multi?.Delete(); + } + } + + [Fact] + public void StaticTileEnumerator_TileMatrixGetMultiTilesWorks() + { + var map = Map.Felucca; + var x = 800; + var y = 800; + + TestMulti multi = null; + try + { + // Create a multi at the location + multi = CreateMultiWithComponents(map, new Point3D(800, 800, 0)); + + // Test the TileMatrix.GetMultiTiles method + var tiles = new List(); + foreach (var tile in map.Tiles.GetMultiTiles(x, y)) + { + tiles.Add(tile); + } + + // Should include multi tiles + Assert.NotEmpty(tiles); + } + finally + { + multi?.Delete(); + } + } + + [Fact] + public void StaticTileEnumerator_MultipleMultisAtSameLocation() + { + var map = Map.Felucca; + var location = new Point2D(900, 900); + + var multis = new TestMulti[2]; + try + { + // Create two multis at the same location + multis[0] = CreateMultiWithComponents(map, new Point3D(900, 900, 0)); + multis[1] = CreateMultiWithComponents(map, new Point3D(900, 900, 5)); + + // Get all multi tiles + var tiles = new List(); + foreach (var tile in new Map.StaticTileEnumerable(map, location, includeStatics: false, includeMultis: true)) + { + tiles.Add(tile); + } + + // Should include tiles from both multis + Assert.NotEmpty(tiles); + // We expect at least tiles from both multis + Assert.True(tiles.Count >= 2); + } + finally + { + multis[0]?.Delete(); + multis[1]?.Delete(); + } + } + + [Fact] + public void StaticTileEnumerator_EmptyReturnsCorrectly() + { + var enumerator = Map.StaticTileEnumerable.Empty.GetEnumerator(); + Assert.False(enumerator.MoveNext()); + } + + [Fact] + public void StaticTileEnumerator_DeletedMultiSkipped() + { + var map = Map.Felucca; + var location = new Point2D(1000, 1000); + + var multis = new TestMulti[2]; + try + { + // Create two multis + multis[0] = CreateMultiWithComponents(map, new Point3D(1000, 1000, 0)); + multis[1] = CreateMultiWithComponents(map, new Point3D(1000, 1000, 5)); + + // Delete the first multi + multis[0].Delete(); + + // Get all multi tiles + var tiles = new List(); + foreach (var tile in new Map.StaticTileEnumerable(map, location, includeStatics: false, includeMultis: true)) + { + tiles.Add(tile); + } + + // Should only include tiles from the second multi + Assert.NotEmpty(tiles); + Assert.All(tiles, tile => Assert.True(tile.Z >= 5)); + } + finally + { + multis[0]?.Delete(); + multis[1]?.Delete(); + } + } + + private static TestMulti CreateMultiWithComponents(Map map, Point3D location) + { + var multi = new TestMulti(World.NewItem); + multi.MoveToWorld(location, map); + return multi; + } + + private class TestMulti : BaseMulti + { + public TestMulti(Serial serial) : base(serial) + { + } + + public override MultiComponentList Components => DefaultComponents; + + private static readonly MultiComponentList DefaultComponents = new( + [ + new MultiTileEntry(0x1, 0, 0, 0, 0x0), + new MultiTileEntry(0x2, 1, 0, 0, 0x0) + ] + ); + } +} + diff --git a/Projects/Server.Tests/Tests/Utility/HtmlEscapeTests.cs b/Projects/Server.Tests/Tests/Utility/HtmlEscapeTests.cs new file mode 100644 index 000000000..089852974 --- /dev/null +++ b/Projects/Server.Tests/Tests/Utility/HtmlEscapeTests.cs @@ -0,0 +1,242 @@ +using System; +using System.Linq; +using Xunit; + +namespace Server.Tests; + +/// +/// Tests for the Html.EscapeHtml extension methods. +/// These tests verify correct HTML entity escaping and edge cases. +/// +public class HtmlEscapeTests +{ + [Theory(DisplayName = "No escaping needed")] + [InlineData("")] + [InlineData("Hello World")] + [InlineData("Plain text without special characters")] + [InlineData("123456789")] + [InlineData("!@#$%^*()_+-=[]{}|;:,.?")] + public void EscapeHtml_NoSpecialCharacters_ReturnsUnchanged(string input) + { + var result = input.EscapeHtml(); + Assert.Equal(input, result); + } + + [Theory(DisplayName = "Single character escaping")] + [InlineData("<", "<")] + [InlineData(">", ">")] + [InlineData("&", "&")] + [InlineData("\"", """)] + [InlineData("'", "'")] + public void EscapeHtml_SingleSpecialCharacter_EscapesCorrectly(string input, string expected) + { + var result = input.EscapeHtml(); + Assert.Equal(expected, result); + } + + [Theory(DisplayName = "Multiple instances of same character")] + [InlineData("<><>", "<><>")] + [InlineData("&&&&", "&&&&")] + [InlineData("\"\"\"", """"")] + [InlineData("'''", "'''")] + [InlineData(">>>", ">>>")] + public void EscapeHtml_MultipleSpecialCharacters_EscapesAll(string input, string expected) + { + var result = input.EscapeHtml(); + Assert.Equal(expected, result); + } + + [Theory(DisplayName = "Mixed content")] + [InlineData("Hello ", "Hello <world>")] + [InlineData("
Hello
", "<div>Hello</div>")] + [InlineData("Tom & Jerry", "Tom & Jerry")] + [InlineData("He said \"hello\"", "He said "hello"")] + [InlineData("It's a test", "It's a test")] + public void EscapeHtml_MixedContent_EscapesMixedSpecialCharacters(string input, string expected) + { + var result = input.EscapeHtml(); + Assert.Equal(expected, result); + } + + [Theory(DisplayName = "Starting with special character")] + [InlineData("World", ">World")] + [InlineData("&Start", "&Start")] + [InlineData("\"Quote", ""Quote")] + [InlineData("'Apostrophe", "'Apostrophe")] + public void EscapeHtml_StartsWithSpecialCharacter_EscapesStart(string input, string expected) + { + var result = input.EscapeHtml(); + Assert.Equal(expected, result); + } + + [Theory(DisplayName = "Ending with special character")] + [InlineData("Hello<", "Hello<")] + [InlineData("World>", "World>")] + [InlineData("End&", "End&")] + [InlineData("Quote\"", "Quote"")] + [InlineData("Test'", "Test'")] + public void EscapeHtml_EndsWithSpecialCharacter_EscapesEnd(string input, string expected) + { + var result = input.EscapeHtml(); + Assert.Equal(expected, result); + } + + [Theory(DisplayName = "Complex mixed scenarios")] + [InlineData("

Hello & goodbye

", "<p>Hello & goodbye</p>")] + [InlineData("<already>", "&lt;already&gt;")] + [InlineData("", "<tag attr="value" data='test'>")] + [InlineData("ac&d\"e'f", "a<b>c&d"e'f")] + [InlineData(" ", "&nbsp;")] + public void EscapeHtml_ComplexScenarios_EscapesAllSpecialCharacters(string input, string expected) + { + var result = input.EscapeHtml(); + Assert.Equal(expected, result); + } + + [Fact(DisplayName = "Null string input")] + public void EscapeHtml_NullString_ReturnsEmpty() + { + string? input = null; + var result = input.EscapeHtml(); + Assert.Empty(result); + } + + [Fact(DisplayName = "Empty string input")] + public void EscapeHtml_EmptyString_ReturnsEmpty() + { + var result = "".EscapeHtml(); + Assert.Empty(result); + } + + [Fact(DisplayName = "Only special characters")] + public void EscapeHtml_OnlySpecialCharacters_EscapesAll() + { + const string input = "<>&\"'"; + const string expected = "<>&"'"; + var result = input.EscapeHtml(); + Assert.Equal(expected, result); + } + + [Fact(DisplayName = "Ampersand must be escaped first")] + public void EscapeHtml_AmpersandFirst_PreventDoubleEscaping() + { + // This is critical: & must be escaped to & + // If we're not careful, we could double-escape already-escaped content + const string input = "<"; + const string expected = "&lt;"; + var result = input.EscapeHtml(); + Assert.Equal(expected, result); + } + + [Theory(DisplayName = "ReadOnlySpan overload - no special characters")] + [InlineData("Hello World")] + [InlineData("Plain text")] + public void EscapeHtml_ReadOnlySpan_NoSpecialCharacters_ReturnsUnchanged(string input) + { + var result = input.AsSpan().EscapeHtml(); + Assert.Equal(input, result); + } + + [Theory(DisplayName = "ReadOnlySpan overload - with special characters")] + [InlineData("
", "<div>")] + [InlineData("Tom & Jerry", "Tom & Jerry")] + public void EscapeHtml_ReadOnlySpan_WithSpecialCharacters_EscapesCorrectly(string input, string expected) + { + var result = input.AsSpan().EscapeHtml(); + Assert.Equal(expected, result); + } + + [Theory(DisplayName = "ReadOnlySpan overload - empty input")] + [InlineData("")] + public void EscapeHtml_ReadOnlySpan_Empty_ReturnsEmpty(string input) + { + var result = input.AsSpan().EscapeHtml(); + Assert.Empty(result); + } + + [Fact(DisplayName = "Consecutive special characters")] + public void EscapeHtml_ConsecutiveSpecialCharacters_EscapesAll() + { + const string input = "<<>>&&\"\"''"; + const string expected = "<<>>&&""''"; + var result = input.EscapeHtml(); + Assert.Equal(expected, result); + } + + [Fact(DisplayName = "Special characters with single normal character between")] + public void EscapeHtml_SpecialCharactersWithGaps_EscapesAll() + { + const string input = "b&c\"d'e"; + const string expected = "<a>b&c"d'e"; + var result = input.EscapeHtml(); + Assert.Equal(expected, result); + } + + [Fact(DisplayName = "HTML tags")] + public void EscapeHtml_HtmlTags_EscapesTagBrackets() + { + var input = "Hello"; + var expected = "<html><body>Hello</body></html>"; + var result = input.EscapeHtml(); + Assert.Equal(expected, result); + } + + [Fact(DisplayName = "HTML attributes with mixed quotes")] + public void EscapeHtml_HtmlAttributesWithQuotes_EscapesCorrectly() + { + var input = ""; + var expected = "<a href="test" data='value'>"; + var result = input.EscapeHtml(); + Assert.Equal(expected, result); + } + + [Theory(DisplayName = "Whitespace handling")] + [InlineData(" spaces ", " spaces ")] + [InlineData("\ttabs\t", "\ttabs\t")] + [InlineData("\nnewlines\n", "\nnewlines\n")] + public void EscapeHtml_Whitespace_PreservedAsIs(string input, string expected) + { + var result = input.EscapeHtml(); + Assert.Equal(expected, result); + } + + [Fact(DisplayName = "Performance: long string without special characters")] + public void EscapeHtml_LongStringNoSpecialCharacters_ReturnsQuickly() + { + var input = new string('a', 10000); + var result = input.EscapeHtml(); + Assert.Equal(input, result); + } + + [Fact(DisplayName = "Performance: long string with special characters")] + public void EscapeHtml_LongStringWithSpecialCharacters_HandlesCorrectly() + { + var input = $"Start{new string('<', 100)}End{new string('&', 100)}Final"; + var expected = + $"Start{string.Join("", Enumerable.Repeat("<", 100))}End{string.Join("", Enumerable.Repeat("&", 100))}Final"; + + var result = input.EscapeHtml(); + Assert.Equal(expected, result); + } + + [Fact(DisplayName = "Unicode characters")] + public void EscapeHtml_UnicodeCharacters_PreservedWithSpecialCharsEscaped() + { + const string input = "Hello 世界 & 🎉"; + const string expected = "Hello 世界 <test> & 🎉"; + var result = input.EscapeHtml(); + Assert.Equal(expected, result); + } + + [Fact(DisplayName = "String overload matches ReadOnlySpan overload")] + public void EscapeHtml_StringVsReadOnlySpan_ProduceSameResult() + { + const string input = "
Tom & Jerry 'in' \"quotes\"
"; + + var resultString = input.EscapeHtml(); + var resultSpan = input.AsSpan().EscapeHtml(); + + Assert.Equal(resultString, resultSpan); + } +} diff --git a/Projects/Server/Buffers/SpanReader.cs b/Projects/Server/Buffers/SpanReader.cs index 31c5fc51d..dd35e8702 100644 --- a/Projects/Server/Buffers/SpanReader.cs +++ b/Projects/Server/Buffers/SpanReader.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2023 - ModernUO Development Team * + * Copyright 2019-2025 - ModernUO Development Team * * Email: hi@modernuo.com * * File: SpanReader.cs * * * @@ -14,7 +14,6 @@ *************************************************************************/ using System.Buffers.Binary; -using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; using System.Text; @@ -45,7 +44,7 @@ public ref struct SpanReader { if (Position >= Length) { - throw new OutOfMemoryException(); + throw new EndOfStreamException("Cannot read past the end of the buffer."); } return _buffer[Position++]; @@ -62,7 +61,7 @@ public ref struct SpanReader { if (!BinaryPrimitives.TryReadInt16BigEndian(_buffer[Position..], out var value)) { - throw new OutOfMemoryException(); + throw new EndOfStreamException("Cannot read past the end of the buffer."); } Position += 2; @@ -74,7 +73,7 @@ public ref struct SpanReader { if (!BinaryPrimitives.TryReadInt16LittleEndian(_buffer[Position..], out var value)) { - throw new OutOfMemoryException(); + throw new EndOfStreamException("Cannot read past the end of the buffer."); } Position += 2; @@ -86,7 +85,7 @@ public ref struct SpanReader { if (!BinaryPrimitives.TryReadUInt16BigEndian(_buffer[Position..], out var value)) { - throw new OutOfMemoryException(); + throw new EndOfStreamException("Cannot read past the end of the buffer."); } Position += 2; @@ -98,7 +97,7 @@ public ref struct SpanReader { if (!BinaryPrimitives.TryReadUInt16LittleEndian(_buffer[Position..], out var value)) { - throw new OutOfMemoryException(); + throw new EndOfStreamException("Cannot read past the end of the buffer."); } Position += 2; @@ -110,7 +109,7 @@ public ref struct SpanReader { if (!BinaryPrimitives.TryReadInt32BigEndian(_buffer[Position..], out var value)) { - throw new OutOfMemoryException(); + throw new EndOfStreamException("Cannot read past the end of the buffer."); } Position += 4; @@ -122,7 +121,7 @@ public ref struct SpanReader { if (!BinaryPrimitives.TryReadUInt32BigEndian(_buffer[Position..], out var value)) { - throw new OutOfMemoryException(); + throw new EndOfStreamException("Cannot read past the end of the buffer."); } Position += 4; @@ -134,7 +133,7 @@ public ref struct SpanReader { if (!BinaryPrimitives.TryReadUInt32LittleEndian(_buffer[Position..], out var value)) { - throw new OutOfMemoryException(); + throw new EndOfStreamException("Cannot read past the end of the buffer."); } Position += 4; @@ -146,7 +145,7 @@ public ref struct SpanReader { if (!BinaryPrimitives.TryReadInt64BigEndian(_buffer[Position..], out var value)) { - throw new OutOfMemoryException(); + throw new EndOfStreamException("Cannot read past the end of the buffer."); } Position += 8; @@ -158,7 +157,7 @@ public ref struct SpanReader { if (!BinaryPrimitives.TryReadUInt64BigEndian(_buffer[Position..], out var value)) { - throw new OutOfMemoryException(); + throw new EndOfStreamException("Cannot read past the end of the buffer."); } Position += 8; @@ -173,9 +172,9 @@ public ref struct SpanReader return ""; } - int byteLength = encoding.GetByteLengthForEncoding(); + var byteLength = encoding.GetByteLengthForEncoding(); - bool isFixedLength = fixedLength > -1; + var isFixedLength = fixedLength > -1; var remaining = Remaining; int size; @@ -184,7 +183,7 @@ public ref struct SpanReader size = fixedLength * byteLength; if (size > Remaining) { - throw new OutOfMemoryException(); + throw new EndOfStreamException("Cannot read past the end of the buffer."); } } else @@ -255,46 +254,28 @@ public ref struct SpanReader [MethodImpl(MethodImplOptions.AggressiveInlining)] public int Seek(int offset, SeekOrigin origin) { - Debug.Assert( - origin != SeekOrigin.End || offset <= 0, - "Attempting to seek to a position beyond capacity using SeekOrigin.End" - ); - - Debug.Assert( - origin != SeekOrigin.End || offset >= -_buffer.Length, - "Attempting to seek to a negative position using SeekOrigin.End" - ); - - Debug.Assert( - origin != SeekOrigin.Begin || offset >= 0, - "Attempting to seek to a negative position using SeekOrigin.Begin" - ); - - Debug.Assert( - origin != SeekOrigin.Begin || offset <= _buffer.Length, - "Attempting to seek to a position beyond the capacity using SeekOrigin.Begin" - ); - - Debug.Assert( - origin != SeekOrigin.Current || Position + offset >= 0, - "Attempting to seek to a negative position using SeekOrigin.Current" - ); - - Debug.Assert( - origin != SeekOrigin.Current || Position + offset <= _buffer.Length, - "Attempting to seek to a position beyond the capacity using SeekOrigin.Current" - ); - - return Position = Math.Max(0, origin switch + var newPosition = origin switch { SeekOrigin.Current => Position + offset, SeekOrigin.End => _buffer.Length + offset, _ => offset // Begin - }); + }; + + if (newPosition < 0) + { + throw new ArgumentOutOfRangeException(nameof(offset), "Seek operation would result in a negative position."); + } + + if (newPosition > _buffer.Length) + { + throw new ArgumentOutOfRangeException(nameof(offset), $"Cannot seek to position {newPosition} beyond buffer length {_buffer.Length}."); + } + + return Position = newPosition; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int Read(Span bytes) + public int Read(scoped Span bytes) { if (bytes.Length == 0) { diff --git a/Projects/Server/Buffers/SpanWriter.cs b/Projects/Server/Buffers/SpanWriter.cs index 4ce423c5b..ccdaddd38 100644 --- a/Projects/Server/Buffers/SpanWriter.cs +++ b/Projects/Server/Buffers/SpanWriter.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2023 - ModernUO Development Team * + * Copyright 2019-2025 - ModernUO Development Team * * Email: hi@modernuo.com * * File: SpanWriter.cs * * * @@ -14,7 +14,6 @@ *************************************************************************/ using System.Buffers.Binary; -using System.Diagnostics; using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -56,14 +55,14 @@ public ref struct SpanWriter public Span RawBuffer => _buffer; /** - * Converts the writer to a Span using a SpanOwner. - * If the buffer was stackalloc, it will be copied to a rented buffer. - * Otherwise the existing rented buffer is used. - * - * Note: - * Do not use the SpanWriter after calling this method. - * This method will effectively dispose of the SpanWriter and is therefore considered terminal. - */ + * Converts the writer to a Span using a SpanOwner. + * If the buffer was stackalloc, it will be copied to a rented buffer. + * Otherwise the existing rented buffer is used. + * + * Note: + * Do not use the SpanWriter after calling this method. + * This method will effectively dispose of the SpanWriter and is therefore considered terminal. + */ [MethodImpl(MethodImplOptions.AggressiveInlining)] public SpanOwner ToSpan() { @@ -117,11 +116,11 @@ public ref struct SpanWriter private void Grow(int additionalCapacity) { var newSize = Math.Max(BytesWritten + additionalCapacity, _buffer.Length * 2); - byte[] poolArray = STArrayPool.Shared.Rent(newSize); + var poolArray = STArrayPool.Shared.Rent(newSize); _buffer[..BytesWritten].CopyTo(poolArray); - byte[] toReturn = _arrayToReturnToPool; + var toReturn = _arrayToReturnToPool; _buffer = _arrayToReturnToPool = poolArray; if (toReturn != null) { @@ -136,7 +135,7 @@ public ref struct SpanWriter { if (!_resize) { - throw new OutOfMemoryException(); + throw new InvalidOperationException("Buffer is full and resizing is disabled."); } Grow(count); @@ -151,7 +150,7 @@ public ref struct SpanWriter { if (!_resize) { - throw new OutOfMemoryException(); + throw new InvalidOperationException("Buffer is full and resizing is disabled."); } Grow(capacity - BytesWritten); @@ -400,46 +399,25 @@ public ref struct SpanWriter [MethodImpl(MethodImplOptions.AggressiveInlining)] public int Seek(int offset, SeekOrigin origin) { - Debug.Assert( - origin != SeekOrigin.End || _resize || offset <= 0, - "Attempting to seek to a position beyond capacity using SeekOrigin.End without resize" - ); - - Debug.Assert( - origin != SeekOrigin.End || offset >= -_buffer.Length, - - "Attempting to seek to a negative position using SeekOrigin.End" - ); - - Debug.Assert( - origin != SeekOrigin.Begin || offset >= 0, - "Attempting to seek to a negative position using SeekOrigin.Begin" - ); - - Debug.Assert( - origin != SeekOrigin.Begin || _resize || offset <= _buffer.Length, - "Attempting to seek to a position beyond the capacity using SeekOrigin.Begin without resize" - ); - - Debug.Assert( - origin != SeekOrigin.Current || _position + offset >= 0, - "Attempting to seek to a negative position using SeekOrigin.Current" - ); - - Debug.Assert( - origin != SeekOrigin.Current || _resize || _position + offset <= _buffer.Length, - "Attempting to seek to a position beyond the capacity using SeekOrigin.Current without resize" - ); - - var newPosition = Math.Max(0, origin switch + var newPosition = origin switch { SeekOrigin.Current => _position + offset, SeekOrigin.End => BytesWritten + offset, _ => offset // Begin - }); + }; + + if (newPosition < 0) + { + throw new ArgumentOutOfRangeException(nameof(offset), "Seek operation would result in a negative position."); + } if (newPosition > _buffer.Length) { + if (!_resize) + { + throw new InvalidOperationException($"Cannot seek to position {newPosition} beyond buffer capacity {_buffer.Length} when resizing is disabled."); + } + Grow(newPosition - _buffer.Length + 1); } @@ -449,7 +427,7 @@ public ref struct SpanWriter [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() { - byte[] toReturn = _arrayToReturnToPool; + var toReturn = _arrayToReturnToPool; this = default; // for safety, to avoid using pooled array if this instance is erroneously appended to again if (toReturn != null) { @@ -478,7 +456,7 @@ public ref struct SpanWriter [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() { - byte[] toReturn = _arrayToReturnToPool; + var toReturn = _arrayToReturnToPool; this = default; if (_length > 0) { diff --git a/Projects/Server/Items/BaseMulti.SectorMultiLinkList.cs b/Projects/Server/Items/BaseMulti.SectorMultiLinkList.cs deleted file mode 100644 index 973e1bf4c..000000000 --- a/Projects/Server/Items/BaseMulti.SectorMultiLinkList.cs +++ /dev/null @@ -1,503 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2023 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: BaseMulti.SectorMultiLinkList.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System; -using System.Runtime.CompilerServices; -using Server.Collections; - -namespace Server.Items; - -// Adds support for the specific value link list on sectors for multis, separate from items -public partial class BaseMulti -{ - // Sectors, specifically for multis - public BaseMulti SectorMultiNext { get; set; } - public BaseMulti SectorMultiPrevious { get; set; } - public bool OnSectorMultiLinkList { get; set; } -} - -public struct SectorMultiValueLinkList -{ - public int Count { get; internal set; } - internal BaseMulti _first; - internal BaseMulti _last; - - public int Version { get; private set; } - - public void Remove(BaseMulti node) - { - if (node == null) - { - return; - } - - if (!node.OnSectorMultiLinkList) - { - throw new ArgumentException("Attempted to remove a node that is not on the list."); - } - - if (node.SectorMultiPrevious == null) - { - // If SectorMultiPrevious is null, then it is the first element. - if (_first != node) - { - throw new ArgumentException("Attempted to remove a node that is not on the list."); - } - - if (_first == _last) - { - _last = null; - _first = null; - } - else - { - _first = node.SectorMultiNext; - } - - if (node.SectorMultiNext != null) - { - node.SectorMultiNext.SectorMultiPrevious = null; - } - } - else - { - node.SectorMultiPrevious.SectorMultiNext = node.SectorMultiNext; - - // If SectorMultiNext is null, then it is the last element. - if (node.SectorMultiNext == null) - { - _last = node.SectorMultiPrevious; - } - else - { - node.SectorMultiNext.SectorMultiPrevious = node.SectorMultiPrevious; - } - } - - node.SectorMultiNext = null; - node.SectorMultiPrevious = null; - node.OnSectorMultiLinkList = false; - Count--; - Version++; - - if (Count < 0) - { - throw new Exception("Count is negative!"); - } - } - - // Remove all entries before this node, not including this node. - public void RemoveAllBefore(BaseMulti e) - { - if (e == null) - { - return; - } - - if (!e.OnSectorMultiLinkList) - { - throw new ArgumentException("Attempted to remove nodes before a node that is not on the list."); - } - - if (e.SectorMultiPrevious == null) - { - return; - } - - var current = e.SectorMultiPrevious; - e.SectorMultiPrevious = null; - - while (current != null) - { - var SectorMultiPrevious = current.SectorMultiPrevious; - - current.OnSectorMultiLinkList = false; - current.SectorMultiNext = null; - current.SectorMultiPrevious = null; - Count--; - - if (Count < 0) - { - throw new Exception("Count is negative!"); - } - - current = SectorMultiPrevious; - } - - _first = e; - Version++; - } - - // Remove all entries after this node, not including this node. - public void RemoveAllAfter(BaseMulti e) - { - if (e == null) - { - return; - } - - if (!e.OnSectorMultiLinkList) - { - throw new ArgumentException("Attempted to remove nodes after a node that is not on the list."); - } - - if (e.SectorMultiNext == null) - { - return; - } - - var current = e.SectorMultiNext; - e.SectorMultiNext = null; - - while (current != null) - { - var SectorMultiNext = current.SectorMultiNext; - - current.OnSectorMultiLinkList = false; - current.SectorMultiNext = null; - current.SectorMultiPrevious = null; - Count--; - - if (Count < 0) - { - throw new Exception("Count is negative!"); - } - - current = SectorMultiNext; - } - - _last = e; - Version++; - } - - public void AddLast(BaseMulti e) - { - if (e == null) - { - return; - } - - if (e.OnSectorMultiLinkList) - { - throw new ArgumentException("Attempted to add a node that is already on a list."); - } - - if (_last != null) - { - AddAfter(_last, e); - } - else - { - _first = e; - _last = e; - Count = 1; - Version++; - e.OnSectorMultiLinkList = true; - } - } - - public void AddFirst(BaseMulti e) - { - if (e == null) - { - return; - } - - if (e.OnSectorMultiLinkList) - { - throw new ArgumentException("Attempted to add a node that is already on a list."); - } - - if (_first != null) - { - AddBefore(_first, e); - } - else - { - _first = e; - _last = e; - Count = 1; - Version++; - e.OnSectorMultiLinkList = true; - } - } - - public void AddBefore(BaseMulti existing, BaseMulti node) - { - if (node == null) - { - return; - } - - ArgumentNullException.ThrowIfNull(existing); - - if (!existing.OnSectorMultiLinkList) - { - throw new ArgumentException($"Argument '{nameof(existing)}' must be a node on a list."); - } - - if (node.OnSectorMultiLinkList) - { - throw new ArgumentException("Attempted to add a node that is already on a list."); - } - - node.SectorMultiNext = existing; - node.SectorMultiPrevious = existing.SectorMultiPrevious; - - if (existing.SectorMultiPrevious != null) - { - existing.SectorMultiPrevious.SectorMultiNext = node; - } - else - { - _first = node; - } - - existing.SectorMultiPrevious = node; - node.OnSectorMultiLinkList = true; - Count++; - Version++; - } - - public void AddAfter(BaseMulti existing, BaseMulti node) - { - if (node == null) - { - return; - } - - ArgumentNullException.ThrowIfNull(existing); - - if (!existing.OnSectorMultiLinkList) - { - throw new ArgumentException($"Argument '{nameof(existing)}' must be a node on a list."); - } - - if (node.OnSectorMultiLinkList) - { - throw new ArgumentException("Attempted to add a node that is already on a list."); - } - - node.SectorMultiPrevious = existing; - node.SectorMultiNext = existing.SectorMultiNext; - - if (existing.SectorMultiNext != null) - { - existing.SectorMultiNext.SectorMultiPrevious = node; - } - else - { - _last = node; - } - - existing.SectorMultiNext = node; - node.OnSectorMultiLinkList = true; - Count++; - Version++; - } - - public void RemoveAll() - { - var current = _first; - while (current != null) - { - var SectorMultiNext = current.SectorMultiNext; - - current.OnSectorMultiLinkList = false; - current.SectorMultiNext = null; - current.SectorMultiPrevious = null; - current = SectorMultiNext; - } - - _first = null; - _last = null; - Count = 0; - Version++; - } - - public void AddLast(ref SectorMultiValueLinkList otherList, BaseMulti start, BaseMulti end) - { - // Should we check if start and end actually exist on the other list? - if (otherList.Count == 0 || otherList.Count == 1 && (start != end || otherList._first != start)) - { - throw new ArgumentException("Attempted to add nodes that are not on the specified linklist."); - } - - if (start.SectorMultiPrevious != null) - { - start.SectorMultiPrevious.SectorMultiNext = end.SectorMultiNext; - } - else - { - // Start is first - otherList._first = end.SectorMultiNext; - } - - if (end.SectorMultiNext != null) - { - end.SectorMultiNext.SectorMultiPrevious = start.SectorMultiPrevious; - } - else - { - otherList._last = start.SectorMultiPrevious; - } - - var count = 1; - var current = start; - - // Assume start and end are in the right order, or bad things happen (crash). - while (current != end) - { - count++; - current = current.SectorMultiNext; - } - - otherList.Count -= count; - - if (otherList.Count < 0) - { - throw new Exception("Count is negative!"); - } - - if (_last != null) - { - _last.SectorMultiNext = start; - start.SectorMultiPrevious = _last; - } - else - { - _first = start; - } - - _last = end; - Count += count; - Version++; - } - - public BaseMulti[] ToArray() - { - var arr = new BaseMulti[Count]; - - var index = 0; - foreach (var t in this) - { - arr[index++] = t; - } - - return arr; - } - - public ref struct SectorMultiValueListEnumerator - { - private bool _started; - private BaseMulti _current; - private ref readonly SectorMultiValueLinkList _linkList; - private int _version; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public SectorMultiValueListEnumerator(in SectorMultiValueLinkList linkList) - { - _linkList = ref linkList; - _started = false; - _current = null; - _version = 0; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { - if (!_started) - { - _current = _linkList._first; - _started = true; - _version = _linkList.Version; - } - else if (_linkList.Version != _version) - { - throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); - } - else - { - _current = _current.SectorMultiNext; - } - - return _current != null; - } - - public BaseMulti Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _current; - } - } - - public ref struct DescendingSectorMultiValueListEnumerator - { - private bool _started; - private BaseMulti _current; - private ref readonly SectorMultiValueLinkList _linkList; - private int _version; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public DescendingSectorMultiValueListEnumerator(in SectorMultiValueLinkList linkList) - { - _linkList = ref linkList; - _started = false; - _current = null; - _version = 0; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { - if (!_started) - { - _current = _linkList._last; - _started = true; - _version = _linkList.Version; - } - else if (_linkList.Version != _version) - { - throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); - } - else - { - _current = _current.SectorMultiPrevious; - } - - return _current != null; - } - - public BaseMulti Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => _current; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public DescendingSectorMultiValueListEnumerator GetEnumerator() => this; - } -} - -public static class SectorMultiValueLinkListExt -{ - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static SectorMultiValueLinkList.SectorMultiValueListEnumerator GetEnumerator(this in SectorMultiValueLinkList linkList) - => new(in linkList); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static SectorMultiValueLinkList.DescendingSectorMultiValueListEnumerator ByDescending(this in SectorMultiValueLinkList linkList) - => new(in linkList); -} diff --git a/Projects/Server/Items/Container.cs b/Projects/Server/Items/Container.cs index 3481d182c..d258d3e45 100644 --- a/Projects/Server/Items/Container.cs +++ b/Projects/Server/Items/Container.cs @@ -1,3 +1,18 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2025 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: Container.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + using System; using System.Collections.Generic; using System.IO; @@ -25,7 +40,7 @@ public partial class Container : Item private int m_TotalItems; private int m_TotalWeight; - private int _version; + internal int _version; [SerializableField(3)] [SerializedCommandProperty(AccessLevel.GameMaster)] @@ -281,16 +296,6 @@ public partial class Container : Item return true; } - private static void SetSaveFlag(ref SaveFlag flags, SaveFlag toSet, bool setIf) - { - if (setIf) - { - flags |= toSet; - } - } - - private static bool GetSaveFlag(SaveFlag flags, SaveFlag toGet) => (flags & toGet) != 0; - [AfterDeserialization] private void AfterDeserialization() { @@ -425,8 +430,8 @@ public partial class Container : Item public virtual bool TryDropItems(Mobile from, bool sendFullMessage, params ReadOnlySpan droppedItems) { - var dropItems = new List(); - var stackItems = new List(); + using var dropItems = PooledRefQueue.Create(); + using var stackItems = PooledRefQueue.Create(); var extraItems = 0; var extraWeight = 0; @@ -446,7 +451,7 @@ public partial class Container : Item if (item is not Container && CheckHold(from, dropped, false, false, 0, extraWeight) && item.CanStackWith(dropped)) { - stackItems.Add(new ItemStackEntry(item, dropped)); + stackItems.Enqueue(new ItemStackEntry(item, dropped)); extraWeight += (int)Math.Ceiling(item.Weight * (item.Amount + dropped.Amount)) - item.PileWeight; // extra weight delta, do not need TotalWeight as we do not have hybrid stackable container types stacked = true; @@ -456,7 +461,7 @@ public partial class Container : Item if (!stacked && CheckHold(from, dropped, false, true, extraItems, extraWeight)) { - dropItems.Add(dropped); + dropItems.Enqueue(dropped); extraItems++; extraWeight += dropped.TotalWeight + dropped.PileWeight; } @@ -464,14 +469,15 @@ public partial class Container : Item if (dropItems.Count + stackItems.Count == droppedItems.Length) // All good { - for (var i = 0; i < dropItems.Count; i++) + while (dropItems.Count > 0) { - DropItem(dropItems[i]); + DropItem(dropItems.Dequeue()); } - for (var i = 0; i < stackItems.Count; i++) + while (stackItems.Count > 0) { - stackItems[i].m_StackItem.StackWith(from, stackItems[i].m_DropItem, false); + var stackItem = stackItems.Dequeue(); + stackItem.m_StackItem.StackWith(from, stackItem.m_DropItem, false); } return true; diff --git a/Projects/Server/Items/Container.Enumerable.cs b/Projects/Server/Items/Item.Enumerable.cs similarity index 85% rename from Projects/Server/Items/Container.Enumerable.cs rename to Projects/Server/Items/Item.Enumerable.cs index fc652c636..c909efcc4 100644 --- a/Projects/Server/Items/Container.Enumerable.cs +++ b/Projects/Server/Items/Item.Enumerable.cs @@ -1,8 +1,8 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2023 - ModernUO Development Team * + * Copyright 2019-2025 - ModernUO Development Team * * Email: hi@modernuo.com * - * File: Container.Enumerable.cs * + * File: Item.Enumerable.cs * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -18,13 +18,13 @@ using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Server.Collections; -namespace Server.Items; +namespace Server; -public partial class Container +public partial class Item { /// /// Performs a breadth-first search through all the s and - /// nested s within this . + /// nested s within this . /// /// /// DO NOT consume, delete, or move items while iterating with any FindItemByType or FindItems overloads @@ -42,8 +42,8 @@ public partial class Container /// Type of objects being searched for /// /// Optional: If true, the search will recursively - /// check any nested s; otherwise, nested - /// s will not be searched. + /// check any nested s; otherwise, nested + /// s will not be searched. /// /// /// Optional: A predicate to check if the @@ -71,7 +71,7 @@ public partial class Container /// /// Safely enumerates items using a breadth-first search through all the s and - /// nested s within this . + /// nested s within this . /// /// /// Use EnumerateItemsByType for situations where the item might be manipulated, consumed, or moved. @@ -92,8 +92,8 @@ public partial class Container /// Type of objects being searched for /// /// Optional: If true, the search will recursively - /// check any nested s; otherwise, nested - /// s will not be searched. + /// check any nested s; otherwise, nested + /// s will not be searched. /// /// /// Optional: A predicate to check if the @@ -203,30 +203,31 @@ public partial class Container public ref struct FindItemsByTypeEnumerator where T : Item { private const string InvalidOperation_EnumFailedVersion = - "Container was modified after enumerator was instantiated. Use Container.EnumerateItems method instead for safe enumerations."; + "Item was modified after enumerator was instantiated. Use Item.EnumerateItems method instead for safe enumerations."; - private PooledRefQueue _containers; + private PooledRefQueue _containers; private Span _items; private int _index; private T _current; private readonly bool _recurse; private readonly Predicate _predicate; - private Container _currentContainer; + private Item _currentContainer; private int _version; - public FindItemsByTypeEnumerator(Container container, bool recurse, Predicate predicate) + public FindItemsByTypeEnumerator(Item container, bool recurse, Predicate predicate) { - _containers = PooledRefQueue.Create(_recurse ? 64 : 0); + _containers = PooledRefQueue.Create(_recurse ? 64 : 0); if (container != null) { - if (container.m_Items != null) + var items = container.LookupItems(); + if (items != null) { - _items = CollectionsMarshal.AsSpan(container.m_Items); + _items = CollectionsMarshal.AsSpan(items); } _currentContainer = container; - _version = container._version; + _version = container.LookupContainerVersion(); } _current = default; @@ -244,9 +245,9 @@ public partial class Container while (_containers.TryDequeue(out var c)) { _currentContainer = c; - _items = CollectionsMarshal.AsSpan(c.m_Items); + _items = CollectionsMarshal.AsSpan(c.LookupItems()); _index = 0; - _version = c._version; + _version = c.LookupContainerVersion(); if (SetNextItem()) { @@ -260,7 +261,7 @@ public partial class Container [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool SetNextItem() { - if (_version != _currentContainer._version) + if (_version != _currentContainer.LookupContainerVersion()) { throw new InvalidOperationException(InvalidOperation_EnumFailedVersion); } @@ -268,14 +269,14 @@ public partial class Container while (_index < _items.Length) { Item item = _items[_index++]; - if (_recurse && item is Container { m_Items.Count: > 0 } c) + if (_recurse && item.LookupItems() is { Count: > 0 } items) { - _containers.Enqueue(c); + _containers.Enqueue(item); } if (item is T t && _predicate?.Invoke(t) != false) { - if (_version != _currentContainer._version) + if (_version != _currentContainer.LookupContainerVersion()) { throw new InvalidOperationException(InvalidOperation_EnumFailedVersion); } diff --git a/Projects/Server/Items/Item.cs b/Projects/Server/Items/Item.cs index 9703fc55f..fc07d6573 100644 --- a/Projects/Server/Items/Item.cs +++ b/Projects/Server/Items/Item.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2024 - ModernUO Development Team * + * Copyright 2019-2025 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Item.cs * * * @@ -192,7 +192,7 @@ public enum ExpandFlag Spawner = 0x100 } -public class Item : IHued, IComparable, ISpawnable, IObjectPropertyListEntity, IValueLinkListNode +public partial class Item : IHued, IComparable, ISpawnable, IObjectPropertyListEntity, IValueLinkListNode { private static readonly ILogger logger = LogFactory.GetLogger(typeof(Item)); @@ -534,6 +534,8 @@ public class Item : IHued, IComparable, ISpawnable, IObjectPropertyListEnt public List Items => LookupItems() ?? EmptyItems; + public int LookupContainerVersion() => (this as Container)?._version ?? LookupCompactInfo()?.Version ?? 0; + [CommandProperty(AccessLevel.GameMaster)] public IEntity RootParent { @@ -1696,11 +1698,11 @@ public class Item : IHued, IComparable, ISpawnable, IObjectPropertyListEnt { if (this is Container cont) { - return cont.m_Items ?? (cont.m_Items = new List()); + return cont.m_Items ??= new List(); } var info = AcquireCompactInfo(); - return info.m_Items ?? (info.m_Items = new List()); + return info.m_Items ??= new List(); } private void SetFlag(ImplFlag flag, bool value) @@ -2471,15 +2473,6 @@ public class Item : IHued, IComparable, ISpawnable, IObjectPropertyListEnt { } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void SetSaveFlag(ref SaveFlag flags, SaveFlag toSet, bool setIf) - { - if (setIf) - { - flags |= toSet; - } - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] private static bool GetSaveFlag(SaveFlag flags, SaveFlag toGet) => (flags & toGet) != 0; @@ -3195,9 +3188,13 @@ public class Item : IHued, IComparable, ISpawnable, IObjectPropertyListEnt item.Map = m_Map; var items = AcquireItems(); - items.Add(item); + if (this is not Container) + { + AcquireCompactInfo().Version++; + } + if (!item.IsVirtualItem) { UpdateTotal(item, TotalType.Gold, item.TotalGold); @@ -3360,6 +3357,11 @@ public class Item : IHued, IComparable, ISpawnable, IObjectPropertyListEnt if (items.Remove(item)) { + if (this is not Container) + { + AcquireCompactInfo().Version++; + } + item.SendRemovePacket(); if (!item.IsVirtualItem) @@ -4321,6 +4323,8 @@ public class Item : IHued, IComparable, ISpawnable, IObjectPropertyListEnt public int m_TempFlags; public double m_Weight = -1; + + public int Version; } [Flags] diff --git a/Projects/Server/Maps/Map.ClientByDistanceEnumerator.cs b/Projects/Server/Maps/Map.ClientByDistanceEnumerator.cs new file mode 100644 index 000000000..64c7b3851 --- /dev/null +++ b/Projects/Server/Maps/Map.ClientByDistanceEnumerator.cs @@ -0,0 +1,299 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2025 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: Map.ClientByDistanceEnumerator.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Runtime.CompilerServices; +using Server.Collections; +using Server.Network; + +namespace Server; + +public partial class Map +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ClientDistanceEnumerable GetClientsInRangeByDistance(Point3D p) => + GetClientsInRangeByDistance(p, Core.GlobalMaxUpdateRange); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ClientDistanceEnumerable GetClientsInRangeByDistance(Point3D p, int range) => + GetClientsInRangeByDistance(p.m_X, p.m_Y, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ClientDistanceEnumerable GetClientsInRangeByDistance(Point2D p) => + GetClientsInRangeByDistance(p, Core.GlobalMaxUpdateRange); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ClientDistanceEnumerable GetClientsInRangeByDistance(Point2D p, int range) => + GetClientsInRangeByDistance(p.m_X, p.m_Y, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ClientDistanceEnumerable GetClientsInRangeByDistance(int x, int y, int range) + { + var clampedRange = Math.Max(0, range); + var edge = clampedRange * 2 + 1; + return GetClientsInBoundsByDistance( + new Rectangle2D(x - clampedRange, y - clampedRange, edge, edge), + new Point2D(x, y) + ); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ClientDistanceEnumerable GetClientsInBoundsByDistance(Rectangle2D bounds, bool makeBoundsInclusive = false) => + GetClientsInBoundsByDistance(bounds, new Point2D(bounds.X + bounds.Width / 2, bounds.Y + bounds.Height / 2), makeBoundsInclusive); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ClientDistanceEnumerable GetClientsInBoundsByDistance( + Rectangle2D bounds, Point2D center, bool makeBoundsInclusive = false + ) => new(this, bounds, center, makeBoundsInclusive); + + public ref struct ClientDistanceEnumerable + { + private readonly Map _map; + private readonly Rectangle2D _bounds; + private readonly Point2D _center; + private readonly bool _makeBoundsInclusive; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ClientDistanceEnumerable(Map map, Rectangle2D bounds, Point2D center, bool makeBoundsInclusive) + { + _map = map; + _bounds = bounds; + _center = center; + _makeBoundsInclusive = makeBoundsInclusive; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ClientDistanceEnumerator GetEnumerator() => new(_map, _bounds, _center, _makeBoundsInclusive); + } + + public ref struct ClientDistanceEnumerator + { + private Map _map; + private Point2D _center; + private Rectangle2D _bounds; + + private int _sectorStartX; + private int _maxRing; + + private int _ring; // -1 = uninitialized, then 0.._maxRing + private int _ringIndex; // Current index within the ring + + private int _currentSectorX; + private int _currentSectorY; + + private ref readonly ValueLinkList _linkList; + private int _currentVersion; + private NetState _current; + + private int _minDistance; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ClientDistanceEnumerator(Map map, Rectangle2D bounds, Point2D center, bool makeBoundsInclusive) + { + _map = map; + _center = center; + _bounds = makeBoundsInclusive + ? new Rectangle2D(bounds.X, bounds.Y, bounds.Width + 1, bounds.Height + 1) + : bounds; + + _current = null; + + if (map != null) + { + var centerSectorX = center.m_X / SectorSize; + var centerSectorY = center.m_Y / SectorSize; + + map.CalculateSectors(_bounds, out _sectorStartX, out var sectorStartY, out var sectorEndX, out var sectorEndY); + + // Calculate max ring based on bounds + var dx = Math.Max(centerSectorX - _sectorStartX, sectorEndX - centerSectorX); + var dy = Math.Max(centerSectorY - sectorStartY, sectorEndY - centerSectorY); + _maxRing = Math.Max(dx, dy); + } + + _ring = -1; + _ringIndex = -1; + _currentSectorX = 0; + _currentSectorY = 0; + + _currentVersion = 0; + _minDistance = 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + var map = _map; + + if (map == null) + { + return false; + } + + if (!Unsafe.IsNullRef(in _linkList) && _linkList.Version != _currentVersion) + { + throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); + } + + NetState current = _current; + + while (true) + { + current = current?.Next; + + while (current == null) + { + while (!TryNextSectorInRing(out _currentSectorX, out _currentSectorY)) + { + // Current ring exhausted, try next ring + if (_ring >= _maxRing) + { + return false; // No more rings to search + } + + _ring++; + _ringIndex = -1; + } + + _linkList = ref map.GetRealSector(_currentSectorX, _currentSectorY).Clients; + _currentVersion = _linkList.Version; + current = _linkList._first; + + if (current != null) + { + _minDistance = MinDistToSectorSqrt(_center.m_X, _center.m_Y, _currentSectorX, _currentSectorY); + } + } + + var m = current.Mobile; + if (m?.Deleted == false && _bounds.Contains(m.Location)) + { + _current = current; + return true; + } + } + } + + public (NetState Value, int MinDistance) Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (_current, _minDistance); + } + + private bool TryNextSectorInRing(out int sx, out int sy) + { + if (_ring == 0) + { + // Center sector + if (_ringIndex < 0) + { + _ringIndex = 0; + sx = _center.m_X / SectorSize; + sy = _center.m_Y / SectorSize; + return sx >= _sectorStartX; + } + + sx = sy = 0; + return false; + } + + var totalSectors = _ring * 8; + + // Keep trying sectors in this ring until we find a valid one or exhaust the ring + while (true) + { + var nextIndex = _ringIndex + 1; + + if (nextIndex >= totalSectors) + { + sx = sy = 0; + return false; + } + + _ringIndex = nextIndex; + CalculatePositionFromIndex(nextIndex, out sx, out sy); + + if (sx >= _sectorStartX) + { + return true; + } + } + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void CalculatePositionFromIndex(int index, out int x, out int y) + { + var centerSectorX = _center.m_X / SectorSize; + var centerSectorY = _center.m_Y / SectorSize; + + var ringSize = _ring * 2; + var startX = centerSectorX - _ring; + var startY = centerSectorY - _ring; + + if (index <= ringSize) // Top edge + { + x = startX + index; + y = startY; + } + else if (index <= ringSize * 2) // Right edge + { + x = startX + ringSize; + y = startY + (index - ringSize); + } + else if (index <= ringSize * 3) // Bottom edge + { + x = startX + ringSize - (index - ringSize * 2); + y = startY + ringSize; + } + else // Left edge + { + x = startX; + y = startY + ringSize - (index - ringSize * 3); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int MinDistToSectorSqrt(int cx, int cy, int sectorX, int sectorY) + { + var x0 = sectorX * SectorSize; + var y0 = sectorY * SectorSize; + var x1 = x0 + (SectorSize - 1); + var y1 = y0 + (SectorSize - 1); + + var dx = 0; + if (cx < x0) + { + dx = x0 - cx; + } + else if (cx > x1) + { + dx = cx - x1; + } + + var dy = 0; + if (cy < y0) + { + dy = y0 - cy; + } + else if (cy > y1) + { + dy = cy - y1; + } + + return (int)Math.Sqrt(dx * dx + dy * dy); + } + } +} diff --git a/Projects/Server/Maps/Map.ClientEnumerator.cs b/Projects/Server/Maps/Map.ClientEnumerator.cs index ae53010a1..921affec9 100644 --- a/Projects/Server/Maps/Map.ClientEnumerator.cs +++ b/Projects/Server/Maps/Map.ClientEnumerator.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2023 - ModernUO Development Team * + * Copyright 2019-2025 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Map.ClientEnumerator.cs * * * @@ -46,8 +46,12 @@ public partial class Map GetClientsInRange(p.m_X, p.m_Y, range); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ClientBoundsEnumerable GetClientsInRange(int x, int y, int range) => - GetClientsInBounds(new Rectangle2D(x - range, y - range, range * 2 + 1, range * 2 + 1)); + public ClientBoundsEnumerable GetClientsInRange(int x, int y, int range) + { + var clampedRange = Math.Max(0, range); + var edge = clampedRange * 2 + 1; + return GetClientsInBounds(new Rectangle2D(x - clampedRange, y - clampedRange, edge, edge)); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ClientBoundsEnumerable GetClientsInBounds(Rectangle2D bounds, bool makeBoundsInclusive = false) => @@ -76,6 +80,7 @@ public partial class Map public ref struct ClientAtEnumerator { + private readonly Map _map; private bool _started; private Point2D _location; private ref readonly ValueLinkList _linkList; @@ -85,9 +90,15 @@ public partial class Map [MethodImpl(MethodImplOptions.AggressiveInlining)] public ClientAtEnumerator(Map map, Point2D loc) { + _map = map; _started = false; _location = loc; - _linkList = ref map.GetSector(loc.m_X, loc.m_Y).Clients; + + if (map != null) + { + _linkList = ref map.GetSector(loc.m_X, loc.m_Y).Clients; + } + _version = 0; _current = null; } @@ -95,6 +106,11 @@ public partial class Map [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { + if (_map == null) + { + return false; + } + ref var loc = ref _location; NetState current; Mobile m; @@ -105,7 +121,7 @@ public partial class Map _started = true; _version = _linkList.Version; - m = current.Mobile; + m = current?.Mobile; if (m?.Deleted == false && m.X == loc.m_X && m.Y == loc.m_Y) { _current = current; @@ -125,7 +141,7 @@ public partial class Map { current = current.Next; - m = current.Mobile; + m = current?.Mobile; if (m?.Deleted == false && m.X == loc.m_X && m.Y == loc.m_Y) { _current = current; @@ -163,10 +179,10 @@ public partial class Map } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MobileEnumerator GetEnumerator() => new(_map, _bounds, _makeBoundsInclusive); + public ClientBoundsEnumerator GetEnumerator() => new(_map, _bounds, _makeBoundsInclusive); } - public ref struct MobileEnumerator + public ref struct ClientBoundsEnumerator { private readonly Map _map; private readonly int _sectorStartX; @@ -182,7 +198,7 @@ public partial class Map private NetState _current; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MobileEnumerator(Map map, Rectangle2D bounds, bool makeBoundsInclusive) + public ClientBoundsEnumerator(Map map, Rectangle2D bounds, bool makeBoundsInclusive) { _map = map; _bounds = bounds; @@ -220,7 +236,6 @@ public partial class Map throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); } - Mobile m; NetState current = _current; ref Rectangle2D bounds = ref _bounds; var currentSectorX = _currentSectorX; @@ -255,7 +270,7 @@ public partial class Map current = _linkList._first; } - m = current.Mobile; + var m = current.Mobile; if (m?.Deleted == false && bounds.Contains(m.Location)) { _current = current; diff --git a/Projects/Server/Maps/Map.ItemByDistanceEnumerator.cs b/Projects/Server/Maps/Map.ItemByDistanceEnumerator.cs new file mode 100644 index 000000000..74174d9e0 --- /dev/null +++ b/Projects/Server/Maps/Map.ItemByDistanceEnumerator.cs @@ -0,0 +1,325 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2025 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: Map.ItemByDistanceEnumerator.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Runtime.CompilerServices; +using Server.Collections; + +namespace Server; + +public partial class Map +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ItemDistanceEnumerable GetItemsInRangeByDistance(Point3D p) => + GetItemsInRangeByDistance(p, Core.GlobalMaxUpdateRange); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ItemDistanceEnumerable GetItemsInRangeByDistance(Point3D p, int range) => + GetItemsInRangeByDistance(p, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ItemDistanceEnumerable GetItemsInRangeByDistance(Point3D p) where T : Item => + GetItemsInRangeByDistance(p, Core.GlobalMaxUpdateRange); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ItemDistanceEnumerable GetItemsInRangeByDistance(Point3D p, int range) where T : Item => + GetItemsInRangeByDistance(p.m_X, p.m_Y, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ItemDistanceEnumerable GetItemsInRangeByDistance(Point2D p) => + GetItemsInRangeByDistance(p, Core.GlobalMaxUpdateRange); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ItemDistanceEnumerable GetItemsInRangeByDistance(Point2D p, int range) => + GetItemsInRangeByDistance(p, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ItemDistanceEnumerable GetItemsInRangeByDistance(Point2D p) where T : Item => + GetItemsInRangeByDistance(p, Core.GlobalMaxUpdateRange); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ItemDistanceEnumerable GetItemsInRangeByDistance(Point2D p, int range) where T : Item => + GetItemsInRangeByDistance(p.m_X, p.m_Y, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ItemDistanceEnumerable GetItemsInRangeByDistance(int x, int y, int range) => + GetItemsInRangeByDistance(x, y, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ItemDistanceEnumerable GetItemsInRangeByDistance(int x, int y, int range) where T : Item + { + var clampedRange = Math.Max(0, range); + var edge = clampedRange * 2 + 1; + return GetItemsInBoundsByDistance( + new Rectangle2D(x - clampedRange, y - clampedRange, edge, edge), + new Point2D(x, y) + ); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ItemDistanceEnumerable GetItemsInBoundsByDistance(Rectangle2D bounds, bool makeBoundsInclusive = false) => + GetItemsInBoundsByDistance(bounds, makeBoundsInclusive); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ItemDistanceEnumerable GetItemsInBoundsByDistance(Rectangle2D bounds, bool makeBoundsInclusive = false) + where T : Item => + GetItemsInBoundsByDistance( + bounds, + new Point2D(bounds.X + bounds.Width / 2, bounds.Y + bounds.Height / 2), + makeBoundsInclusive + ); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private ItemDistanceEnumerable GetItemsInBoundsByDistance( + Rectangle2D bounds, Point2D center, bool makeBoundsInclusive = false + ) where T : Item => new(this, bounds, center, makeBoundsInclusive); + + public ref struct ItemDistanceEnumerable where T : Item + { + private readonly Map _map; + private readonly Rectangle2D _bounds; + private readonly Point2D _center; + private readonly bool _makeBoundsInclusive; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ItemDistanceEnumerable(Map map, Rectangle2D bounds, Point2D center, bool makeBoundsInclusive) + { + _map = map; + _bounds = bounds; + _center = center; + _makeBoundsInclusive = makeBoundsInclusive; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ItemDistanceEnumerator GetEnumerator() => new(_map, _bounds, _center, _makeBoundsInclusive); + } + + public ref struct ItemDistanceEnumerator where T : Item + { + private Map _map; + private Point2D _center; + private Rectangle2D _bounds; + + private int _sectorStartX; + private int _maxRing; + + private int _ring; // -1 = uninitialized, then 0.._maxRing + private int _ringIndex; // Current index within the ring + + private int _currentSectorX; + private int _currentSectorY; + + private ref readonly ValueLinkList _linkList; + private int _currentVersion; + private T _current; + + private int _minDistance; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ItemDistanceEnumerator(Map map, Rectangle2D bounds, Point2D center, bool makeBoundsInclusive) + { + _map = map; + _center = center; + _bounds = makeBoundsInclusive + ? new Rectangle2D(bounds.X, bounds.Y, bounds.Width + 1, bounds.Height + 1) + : bounds; + + _current = null; + + if (map != null) + { + var centerSectorX = center.m_X / SectorSize; + var centerSectorY = center.m_Y / SectorSize; + + map.CalculateSectors(_bounds, out _sectorStartX, out var sectorStartY, out var sectorEndX, out var sectorEndY); + + // Calculate max ring based on bounds + var dx = Math.Max(centerSectorX - _sectorStartX, sectorEndX - centerSectorX); + var dy = Math.Max(centerSectorY - sectorStartY, sectorEndY - centerSectorY); + _maxRing = Math.Max(dx, dy); + } + + _ring = -1; + _ringIndex = -1; + _currentSectorX = 0; + _currentSectorY = 0; + + _currentVersion = 0; + _minDistance = 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + var map = _map; + + if (map == null) + { + return false; + } + + if (!Unsafe.IsNullRef(in _linkList) && _linkList.Version != _currentVersion) + { + throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); + } + + Item current = _current; + + while (true) + { + current = current?.Next; + + while (current == null) + { + while (!TryNextSectorInRing(out _currentSectorX, out _currentSectorY)) + { + // Current ring exhausted, try next ring + if (_ring >= _maxRing) + { + return false; // No more rings to search + } + + _ring++; + _ringIndex = -1; + } + + _linkList = ref map.GetRealSector(_currentSectorX, _currentSectorY).Items; + _currentVersion = _linkList.Version; + current = _linkList._first; + + if (current != null) + { + _minDistance = MinDistToSectorSqrt(_center.m_X, _center.m_Y, _currentSectorX, _currentSectorY); + } + } + + if (current is T { Deleted: false } o && _bounds.Contains(o.Location)) + { + _current = o; + return true; + } + } + } + + public (T Value, int MinDistance) Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (_current, _minDistance); + } + private bool TryNextSectorInRing(out int sx, out int sy) + { + if (_ring == 0) + { + // Center sector + if (_ringIndex < 0) + { + _ringIndex = 0; + sx = _center.m_X / SectorSize; + sy = _center.m_Y / SectorSize; + return sx >= _sectorStartX; + } + + sx = sy = 0; + return false; + } + + var totalSectors = _ring * 8; + + // Keep trying sectors in this ring until we find a valid one or exhaust the ring + while (true) + { + var nextIndex = _ringIndex + 1; + + if (nextIndex >= totalSectors) + { + sx = sy = 0; + return false; + } + + _ringIndex = nextIndex; + CalculatePositionFromIndex(nextIndex, out sx, out sy); + + if (sx >= _sectorStartX) + { + return true; + } + } + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void CalculatePositionFromIndex(int index, out int x, out int y) + { + var centerSectorX = _center.m_X / SectorSize; + var centerSectorY = _center.m_Y / SectorSize; + + var ringSize = _ring * 2; + var startX = centerSectorX - _ring; + var startY = centerSectorY - _ring; + + if (index <= ringSize) // Top edge + { + x = startX + index; + y = startY; + } + else if (index <= ringSize * 2) // Right edge + { + x = startX + ringSize; + y = startY + (index - ringSize); + } + else if (index <= ringSize * 3) // Bottom edge + { + x = startX + ringSize - (index - ringSize * 2); + y = startY + ringSize; + } + else // Left edge + { + x = startX; + y = startY + ringSize - (index - ringSize * 3); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int MinDistToSectorSqrt(int cx, int cy, int sectorX, int sectorY) + { + var x0 = sectorX * SectorSize; + var y0 = sectorY * SectorSize; + var x1 = x0 + (SectorSize - 1); + var y1 = y0 + (SectorSize - 1); + + var dx = 0; + if (cx < x0) + { + dx = x0 - cx; + } + else if (cx > x1) + { + dx = cx - x1; + } + + var dy = 0; + if (cy < y0) + { + dy = y0 - cy; + } + else if (cy > y1) + { + dy = cy - y1; + } + + return (int)Math.Sqrt(dx * dx + dy * dy); + } + } +} diff --git a/Projects/Server/Maps/Map.ItemEnumerator.cs b/Projects/Server/Maps/Map.ItemEnumerator.cs index 4bf8f4202..f70c45172 100644 --- a/Projects/Server/Maps/Map.ItemEnumerator.cs +++ b/Projects/Server/Maps/Map.ItemEnumerator.cs @@ -69,8 +69,12 @@ public partial class Map GetItemsInRange(p.m_X, p.m_Y, range); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ItemBoundsEnumerable GetItemsInRange(int x, int y, int range) where T : Item => - GetItemsInBounds(new Rectangle2D(x - range, y - range, range * 2 + 1, range * 2 + 1)); + public ItemBoundsEnumerable GetItemsInRange(int x, int y, int range) where T : Item + { + var clampedRange = Math.Max(0, range); + var edge = clampedRange * 2 + 1; + return GetItemsInBounds(new Rectangle2D(x - clampedRange, y - clampedRange, edge, edge)); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ItemBoundsEnumerable GetItemsInBounds(Rectangle2D bounds) => GetItemsInBounds(bounds); diff --git a/Projects/Server/Maps/Map.MobileByDistanceEnumerator.cs b/Projects/Server/Maps/Map.MobileByDistanceEnumerator.cs new file mode 100644 index 000000000..71449e550 --- /dev/null +++ b/Projects/Server/Maps/Map.MobileByDistanceEnumerator.cs @@ -0,0 +1,320 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2025 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: Map.MobileByDistanceEnumerator.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Runtime.CompilerServices; +using Server.Collections; + +namespace Server; + +public partial class Map +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MobileDistanceEnumerable GetMobilesInRangeByDistance(Point3D p) => + GetMobilesInRangeByDistance(p, Core.GlobalMaxUpdateRange); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MobileDistanceEnumerable GetMobilesInRangeByDistance(Point3D p, int range) => + GetMobilesInRangeByDistance(p, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MobileDistanceEnumerable GetMobilesInRangeByDistance(Point3D p) where T : Mobile => + GetMobilesInRangeByDistance(p, Core.GlobalMaxUpdateRange); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MobileDistanceEnumerable GetMobilesInRangeByDistance(Point3D p, int range) where T : Mobile => + GetMobilesInRangeByDistance(p.m_X, p.m_Y, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MobileDistanceEnumerable GetMobilesInRangeByDistance(Point2D p) => + GetMobilesInRangeByDistance(p, Core.GlobalMaxUpdateRange); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MobileDistanceEnumerable GetMobilesInRangeByDistance(Point2D p, int range) => + GetMobilesInRangeByDistance(p, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MobileDistanceEnumerable GetMobilesInRangeByDistance(Point2D p) where T : Mobile => + GetMobilesInRangeByDistance(p, Core.GlobalMaxUpdateRange); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MobileDistanceEnumerable GetMobilesInRangeByDistance(Point2D p, int range) where T : Mobile => + GetMobilesInRangeByDistance(p.m_X, p.m_Y, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MobileDistanceEnumerable GetMobilesInRangeByDistance(int x, int y, int range) => + GetMobilesInRangeByDistance(x, y, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MobileDistanceEnumerable GetMobilesInRangeByDistance(int x, int y, int range) where T : Mobile + { + var clampedRange = Math.Max(0, range); + var edge = clampedRange * 2 + 1; + return GetMobilesInBoundsByDistance( + new Rectangle2D(x - clampedRange, y - clampedRange, edge, edge), + new Point2D(x, y) + ); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MobileDistanceEnumerable GetMobilesInBoundsByDistance(Rectangle2D bounds, bool makeBoundsInclusive = false) => + GetMobilesInBoundsByDistance(bounds, makeBoundsInclusive); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MobileDistanceEnumerable GetMobilesInBoundsByDistance(Rectangle2D bounds, bool makeBoundsInclusive = false) where T : Mobile => + GetMobilesInBoundsByDistance(bounds, new Point2D(bounds.X + bounds.Width / 2, bounds.Y + bounds.Height / 2), makeBoundsInclusive); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private MobileDistanceEnumerable GetMobilesInBoundsByDistance( + Rectangle2D bounds, Point2D center, bool makeBoundsInclusive = false + ) where T : Mobile => new(this, bounds, center, makeBoundsInclusive); + + public ref struct MobileDistanceEnumerable where T : Mobile + { + private readonly Map _map; + private readonly Rectangle2D _bounds; + private readonly Point2D _center; + private readonly bool _makeBoundsInclusive; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MobileDistanceEnumerable(Map map, Rectangle2D bounds, Point2D center, bool makeBoundsInclusive) + { + _map = map; + _bounds = bounds; + _center = center; + _makeBoundsInclusive = makeBoundsInclusive; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MobileDistanceEnumerator GetEnumerator() => new(_map, _bounds, _center, _makeBoundsInclusive); + } + + public ref struct MobileDistanceEnumerator where T : Mobile + { + private Map _map; + private Point2D _center; + private Rectangle2D _bounds; + + private int _sectorStartX; + private int _maxRing; + + private int _ring; // -1 = uninitialized, then 0.._maxRing + private int _ringIndex; // Current index within the ring + + private int _currentSectorX; + private int _currentSectorY; + + private ref readonly ValueLinkList _linkList; + private int _currentVersion; + private T _current; + + private int _minDistance; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public MobileDistanceEnumerator(Map map, Rectangle2D bounds, Point2D center, bool makeBoundsInclusive) + { + _map = map; + _center = center; + _bounds = makeBoundsInclusive + ? new Rectangle2D(bounds.X, bounds.Y, bounds.Width + 1, bounds.Height + 1) + : bounds; + + _current = null; + + if (map != null) + { + var centerSectorX = center.m_X / SectorSize; + var centerSectorY = center.m_Y / SectorSize; + + map.CalculateSectors(_bounds, out _sectorStartX, out var sectorStartY, out var sectorEndX, out var sectorEndY); + + // Calculate max ring based on bounds + var dx = Math.Max(centerSectorX - _sectorStartX, sectorEndX - centerSectorX); + var dy = Math.Max(centerSectorY - sectorStartY, sectorEndY - centerSectorY); + _maxRing = Math.Max(dx, dy); + } + + _ring = -1; + _ringIndex = -1; + _currentSectorX = 0; + _currentSectorY = 0; + + _currentVersion = 0; + _minDistance = 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + var map = _map; + + if (map == null) + { + return false; + } + + if (!Unsafe.IsNullRef(in _linkList) && _linkList.Version != _currentVersion) + { + throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); + } + + Mobile current = _current; + + while (true) + { + current = current?.Next; + + while (current == null) + { + while (!TryNextSectorInRing(out _currentSectorX, out _currentSectorY)) + { + // Current ring exhausted, try next ring + if (_ring >= _maxRing) + { + return false; // No more rings to search + } + + _ring++; + _ringIndex = -1; + } + + _linkList = ref map.GetRealSector(_currentSectorX, _currentSectorY).Mobiles; + _currentVersion = _linkList.Version; + current = _linkList._first; + + if (current != null) + { + _minDistance = MinDistToSectorSqrt(_center.m_X, _center.m_Y, _currentSectorX, _currentSectorY); + } + } + + if (current is T { Deleted: false } o && _bounds.Contains(o.Location)) + { + _current = o; + return true; + } + } + } + + public (T Value, int MinDistance) Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => (_current, _minDistance); + } + private bool TryNextSectorInRing(out int sx, out int sy) + { + if (_ring == 0) + { + // Center sector + if (_ringIndex < 0) + { + _ringIndex = 0; + sx = _center.m_X / SectorSize; + sy = _center.m_Y / SectorSize; + return sx >= _sectorStartX; + } + + sx = sy = 0; + return false; + } + + var totalSectors = _ring * 8; + + // Keep trying sectors in this ring until we find a valid one or exhaust the ring + while (true) + { + var nextIndex = _ringIndex + 1; + + if (nextIndex >= totalSectors) + { + sx = sy = 0; + return false; + } + + _ringIndex = nextIndex; + CalculatePositionFromIndex(nextIndex, out sx, out sy); + + if (sx >= _sectorStartX) + { + return true; + } + } + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void CalculatePositionFromIndex(int index, out int x, out int y) + { + var centerSectorX = _center.m_X / SectorSize; + var centerSectorY = _center.m_Y / SectorSize; + + var ringSize = _ring * 2; + var startX = centerSectorX - _ring; + var startY = centerSectorY - _ring; + + if (index <= ringSize) // Top edge + { + x = startX + index; + y = startY; + } + else if (index <= ringSize * 2) // Right edge + { + x = startX + ringSize; + y = startY + (index - ringSize); + } + else if (index <= ringSize * 3) // Bottom edge + { + x = startX + ringSize - (index - ringSize * 2); + y = startY + ringSize; + } + else // Left edge + { + x = startX; + y = startY + ringSize - (index - ringSize * 3); + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static int MinDistToSectorSqrt(int cx, int cy, int sectorX, int sectorY) + { + var x0 = sectorX * SectorSize; + var y0 = sectorY * SectorSize; + var x1 = x0 + (SectorSize - 1); + var y1 = y0 + (SectorSize - 1); + + var dx = 0; + if (cx < x0) + { + dx = x0 - cx; + } + else if (cx > x1) + { + dx = cx - x1; + } + + var dy = 0; + if (cy < y0) + { + dy = y0 - cy; + } + else if (cy > y1) + { + dy = cy - y1; + } + + return (int)Math.Sqrt(dx * dx + dy * dy); + } + } +} diff --git a/Projects/Server/Maps/Map.MobileEnumerator.cs b/Projects/Server/Maps/Map.MobileEnumerator.cs index cac64ce3a..01def1015 100644 --- a/Projects/Server/Maps/Map.MobileEnumerator.cs +++ b/Projects/Server/Maps/Map.MobileEnumerator.cs @@ -69,8 +69,12 @@ public partial class Map GetMobilesInRange(p.m_X, p.m_Y, range); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MobileBoundsEnumerable GetMobilesInRange(int x, int y, int range) where T : Mobile => - GetMobilesInBounds(new Rectangle2D(x - range, y - range, range * 2 + 1, range * 2 + 1)); + public MobileBoundsEnumerable GetMobilesInRange(int x, int y, int range) where T : Mobile + { + var clampedRange = Math.Max(0, range); + var edge = clampedRange * 2 + 1; + return GetMobilesInBounds(new Rectangle2D(x - clampedRange, y - clampedRange, edge, edge)); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public MobileBoundsEnumerable GetMobilesInBounds(Rectangle2D bounds) => GetMobilesInBounds(bounds); diff --git a/Projects/Server/Maps/Map.MultiEnumerator.cs b/Projects/Server/Maps/Map.MultiEnumerator.cs index bc1f7b018..2069d4c94 100644 --- a/Projects/Server/Maps/Map.MultiEnumerator.cs +++ b/Projects/Server/Maps/Map.MultiEnumerator.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2023 - ModernUO Development Team * + * Copyright 2019-2025 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Map.MultiEnumerator.cs * * * @@ -17,15 +17,13 @@ using System; using System.Collections.Generic; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using Server.Collections; using Server.Items; namespace Server; public partial class Map { - private static SectorMultiValueLinkList _emptyMultiLinkList = new(); - public static ref readonly SectorMultiValueLinkList EmptyMultiLinkList => ref _emptyMultiLinkList; - [MethodImpl(MethodImplOptions.AggressiveInlining)] public MultiSectorEnumerable GetMultisInSector(Point3D p) => GetMultisInSector(p); @@ -72,8 +70,12 @@ public partial class Map GetMultisInRange(p.m_X, p.m_Y, range); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MultiBoundsEnumerable GetMultisInRange(int x, int y, int range) where T : BaseMulti => - GetMultisInBounds(new Rectangle2D(x - range, y - range, range * 2 + 1, range * 2 + 1)); + public MultiBoundsEnumerable GetMultisInRange(int x, int y, int range) where T : BaseMulti + { + var clampedRange = Math.Max(0, range); + var edge = clampedRange * 2 + 1; + return GetMultisInBounds(new Rectangle2D(x - clampedRange, y - clampedRange, edge, edge)); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public MultiBoundsEnumerable GetMultisInBounds(Rectangle2D bounds, bool makeBoundsInclusive = false) => @@ -83,6 +85,8 @@ public partial class Map public MultiBoundsEnumerable GetMultisInBounds(Rectangle2D bounds, bool makeBoundsInclusive = false) where T : BaseMulti => new(this, bounds, makeBoundsInclusive); + private static readonly HashSet _sharedDupes = []; + public ref struct MultiSectorEnumerable(Map map, Point2D loc) where T : BaseMulti { public static MultiSectorEnumerable Empty @@ -98,27 +102,42 @@ public partial class Map public ref struct MultiSectorEnumerator where T : BaseMulti { private readonly Span _list; + private readonly int _version; + private readonly Sector _sector; private int _index; private T _current; [MethodImpl(MethodImplOptions.AggressiveInlining)] public MultiSectorEnumerator(Map map, Point2D loc) { - _list = map == null - ? Span.Empty - : CollectionsMarshal.AsSpan(map.GetSector(loc.m_X, loc.m_Y).Multis); + if (map == null) + { + _list = Span.Empty; + _sector = null; + _version = 0; + } + else + { + _sector = map.GetSector(loc.m_X, loc.m_Y); + _list = CollectionsMarshal.AsSpan(_sector.Multis); + _version = _sector.MultisVersion; + } - _index = 0; + _index = -1; _current = null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { - while ((uint)_index < (uint)_list.Length) + if (_sector != null && _version != _sector.MultisVersion) { - var current = _list[_index++]; - if (current is T { Deleted: false } o) + throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); + } + + while (++_index < _list.Length) + { + if (_list[_index] is T { Deleted: false } o) { _current = o; return true; @@ -160,24 +179,27 @@ public partial class Map public ref struct MultiBoundsEnumerator where T : BaseMulti { - private readonly Map _map; - private readonly int _sectorStartX; - private readonly int _sectorEndX; - private readonly int _sectorEndY; + private Map _map; + private int _sectorStartX; + private int _sectorEndX; + private int _sectorEndY; private Rectangle2D _bounds; private int _currentSectorX; private int _currentSectorY; - private Span _list; + private Span _currentList; + private int _currentIndex; + private int _currentVersion; + private Sector _currentSector; private T _current; - private int _index; - private HashSet _dupes; [MethodImpl(MethodImplOptions.AggressiveInlining)] public MultiBoundsEnumerator(Map map, Rectangle2D bounds, bool makeBoundsInclusive) { + _sharedDupes.Clear(); + _map = map; _bounds = bounds; @@ -196,62 +218,78 @@ public partial class Map // We start the X sector one short because it gets incremented immediately in MoveNext() _currentSectorX = _sectorStartX - 1; _currentSectorY = _sectorStartY; - _index = 0; } + + _currentList = default; + _currentIndex = -1; + _currentVersion = 0; + _currentSector = null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool GetMulti() + public bool MoveNext() { - ref Rectangle2D bounds = ref _bounds; + var map = _map; - while ((uint)_index < (uint)_list.Length) + if (map == null) { - var current = _list[_index++]; - _dupes ??= new HashSet(); - - if (current is T { Deleted: false } o && bounds.Contains(o.Location) && !_dupes.Contains(o.Serial)) - { - _dupes.Add(o.Serial); - _current = o; - return true; - } + return false; } - return false; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private bool GetSector() - { + ref Rectangle2D bounds = ref _bounds; var currentSectorX = _currentSectorX; var currentSectorY = _currentSectorY; var sectorEndX = _sectorEndX; var sectorEndY = _sectorEndY; - // Move to next sector - if (currentSectorX < sectorEndX) + while (true) { - _currentSectorX = ++currentSectorX; - } - else if (currentSectorY < sectorEndY) - { - _currentSectorX = currentSectorX = _sectorStartX; - _currentSectorY = ++currentSectorY; - } - else - { - // Ran out of sectors - return false; - } + // Try to advance in the current list + if (_currentList.Length > 0) + { + if (_currentVersion != _currentSector.MultisVersion) + { + throw new InvalidOperationException(CollectionThrowStrings.InvalidOperation_EnumFailedVersion); + } - _list = CollectionsMarshal.AsSpan(_map.GetRealSector(currentSectorX, currentSectorY).Multis); - return GetMulti(); + while (++_currentIndex < _currentList.Length) + { + var item = _currentList[_currentIndex]; + if (item is T { Deleted: false } o && bounds.Contains(o.Location)) + { + // Multis can span multiple sectors, so we need to deduplicate + if (_sharedDupes.Add(o.Serial)) + { + _current = o; + return true; + } + } + } + } + + // Move to next sector + if (currentSectorX < sectorEndX) + { + _currentSectorX = ++currentSectorX; + } + else if (currentSectorY < sectorEndY) + { + _currentSectorX = currentSectorX = _sectorStartX; + _currentSectorY = ++currentSectorY; + } + else + { + // Ran out of sectors + return false; + } + + _currentSector = map.GetRealSector(currentSectorX, currentSectorY); + _currentList = CollectionsMarshal.AsSpan(_currentSector.Multis); + _currentVersion = _currentSector.MultisVersion; + _currentIndex = -1; + } } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() => _map != null && (GetMulti() || GetSector()); - public T Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Projects/Server/Maps/Map.cs b/Projects/Server/Maps/Map.cs index 26ab156b8..69361f855 100644 --- a/Projects/Server/Maps/Map.cs +++ b/Projects/Server/Maps/Map.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2023 - ModernUO Development Team * + * Copyright 2019-2025 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Map.cs * * * @@ -1355,11 +1355,13 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa { // TODO: Can we avoid this? private static readonly List m_DefaultRectList = new(); + private static readonly List m_DefaultMultiList = new(); private bool m_Active; private ValueLinkList _clients; private ValueLinkList _items; private ValueLinkList _mobiles; - private List _multis = new(); + private List _multis; + private int _multisVersion; private List _regions; public Sector(int x, int y, Map owner) @@ -1372,9 +1374,11 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa public List Regions => _regions ?? m_DefaultRectList; - internal List Multis => _multis; + internal List Multis => _multis ?? m_DefaultMultiList; - internal ref ValueLinkList Mobiles => ref _mobiles; + internal int MultisVersion => _multisVersion; + + internal ref readonly ValueLinkList Mobiles => ref _mobiles; internal ref readonly ValueLinkList Items => ref _items; @@ -1503,12 +1507,17 @@ public sealed partial class Map : IComparable, ISpanFormattable, ISpanParsa public void OnMultiEnter(BaseMulti multi) { + _multis ??= new List(); _multis.Add(multi); + _multisVersion++; } public void OnMultiLeave(BaseMulti multi) { - _multis.Remove(multi); + if (_multis?.Remove(multi) == true) + { + _multisVersion++; + } } public void Activate() diff --git a/Projects/Server/Mobiles/Mobile.Enumerators.cs b/Projects/Server/Mobiles/Mobile.Enumerators.cs new file mode 100644 index 000000000..4ca864222 --- /dev/null +++ b/Projects/Server/Mobiles/Mobile.Enumerators.cs @@ -0,0 +1,63 @@ +using System.Runtime.CompilerServices; + +namespace Server; + +public partial class Mobile +{ + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.ItemAtEnumerable GetItemsAt() => + m_Map == null ? Map.ItemAtEnumerable.Empty : m_Map.GetItemsAt(m_Location); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.ItemAtEnumerable GetItemsAt() where T : Item => + m_Map == null ? Map.ItemAtEnumerable.Empty : m_Map.GetItemsAt(m_Location); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.ItemBoundsEnumerable GetItemsInRange(int range) => GetItemsInRange(range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.ItemBoundsEnumerable GetItemsInRange(int range) where T : Item => + m_Map == null ? Map.ItemBoundsEnumerable.Empty : m_Map.GetItemsInRange(m_Location, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.MobileAtEnumerable GetMobilesInRange() => GetMobilesInRange(); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.MobileAtEnumerable GetMobilesInRange() where T : Mobile => + m_Map == null ? Map.MobileAtEnumerable.Empty : m_Map.GetMobilesAt(m_Location); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.MobileBoundsEnumerable GetMobilesInRange(int range) => GetMobilesInRange(range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.MobileBoundsEnumerable GetMobilesInRange(int range) where T : Mobile => + m_Map == null ? Map.MobileBoundsEnumerable.Empty : m_Map.GetMobilesInRange(m_Location, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.ClientAtEnumerable GetClientsAt() => + m_Map == null ? Map.ClientAtEnumerable.Empty : Map.GetClientsAt(m_Location); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.ClientBoundsEnumerable GetClientsInRange(int range) => + m_Map == null ? Map.ClientBoundsEnumerable.Empty : Map.GetClientsInRange(m_Location, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.ItemDistanceEnumerable GetItemsInRangeByDistance(int range) => + GetItemsInRangeByDistance(range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.ItemDistanceEnumerable GetItemsInRangeByDistance(int range) where T : Item => + m_Map == null ? default : m_Map.GetItemsInRangeByDistance(m_Location, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.MobileDistanceEnumerable GetMobilesInRangeByDistance(int range) => + GetMobilesInRangeByDistance(range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.MobileDistanceEnumerable GetMobilesInRangeByDistance(int range) where T : Mobile => + m_Map == null ? default : m_Map.GetMobilesInRangeByDistance(m_Location, range); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Map.ClientDistanceEnumerable GetClientsInRangeByDistance(int range) => + m_Map == null ? default : m_Map.GetClientsInRangeByDistance(m_Location, range); +} diff --git a/Projects/Server/Mobiles/Mobile.cs b/Projects/Server/Mobiles/Mobile.cs index f217b0fe1..035d96945 100644 --- a/Projects/Server/Mobiles/Mobile.cs +++ b/Projects/Server/Mobiles/Mobile.cs @@ -2737,8 +2737,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro const int cacheLength = OutgoingMobilePackets.MobileMovingPacketCacheByteLength; - Span mobileMovingCache = stackalloc byte[cacheLength]; - mobileMovingCache.Clear(); + Span mobileMovingCache = stackalloc byte[cacheLength].InitializePacket(); var ourState = m_NetState; @@ -2887,6 +2886,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro Span deadBuffer = stackalloc byte[OutgoingMobilePackets.BondedStatusPacketLength].InitializePacket(); Span removeEntity = stackalloc byte[OutgoingEntityPackets.RemoveEntityLength].InitializePacket(); Span hitsPacket = stackalloc byte[OutgoingMobilePackets.MobileAttributePacketLength].InitializePacket(); + mobileMovingCache.InitializePacket(); foreach (var state in Map.GetClientsInRange(m_Location)) { @@ -3723,38 +3723,6 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro { } - public double GetDistanceToSqrt(Point3D p) - { - var xDelta = m_Location.m_X - p.m_X; - var yDelta = m_Location.m_Y - p.m_Y; - - return Math.Sqrt(xDelta * xDelta + yDelta * yDelta); - } - - public double GetDistanceToSqrt(Mobile m) - { - var xDelta = m_Location.m_X - m.m_Location.m_X; - var yDelta = m_Location.m_Y - m.m_Location.m_Y; - - return Math.Sqrt(xDelta * xDelta + yDelta * yDelta); - } - - public double GetDistanceToSqrt(Point2D p) - { - var xDelta = m_Location.m_X - p.X; - var yDelta = m_Location.m_Y - p.Y; - - return Math.Sqrt(xDelta * xDelta + yDelta * yDelta); - } - - public double GetDistanceToSqrt(IPoint2D p) - { - var xDelta = m_Location.m_X - p.X; - var yDelta = m_Location.m_Y - p.Y; - - return Math.Sqrt(xDelta * xDelta + yDelta * yDelta); - } - public virtual void AggressiveAction(Mobile aggressor) => AggressiveAction(aggressor, false); public virtual void AggressiveAction(Mobile aggressor, bool criminal) @@ -4360,7 +4328,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro const int cacheLength = OutgoingMobilePackets.MobileMovingPacketCacheByteLength; Span mobileMovingCache = stackalloc byte[cacheLength]; - mobileMovingCache.Clear(); + mobileMovingCache.InitializePacket(); while (moveClientQueue.Count > 0) { @@ -6995,12 +6963,9 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro flags |= 0x04; } } - else + else if (m_Poison != null) { - if (m_Poison != null) - { - flags |= 0x04; - } + flags |= 0x04; } if (m_Blessed || m_YellowHealthbar) @@ -8067,43 +8032,6 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro return -1; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Map.ItemAtEnumerable GetItemsAt() => - m_Map == null ? Map.ItemAtEnumerable.Empty : m_Map.GetItemsAt(m_Location); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Map.ItemAtEnumerable GetItemsAt() where T : Item => - m_Map == null ? Map.ItemAtEnumerable.Empty : m_Map.GetItemsAt(m_Location); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Map.ItemBoundsEnumerable GetItemsInRange(int range) => GetItemsInRange(range); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Map.ItemBoundsEnumerable GetItemsInRange(int range) where T : Item => - m_Map == null ? Map.ItemBoundsEnumerable.Empty : m_Map.GetItemsInRange(m_Location, range); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Map.MobileAtEnumerable GetMobilesInRange() => GetMobilesInRange(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Map.MobileAtEnumerable GetMobilesInRange() where T : Mobile => - m_Map == null ? Map.MobileAtEnumerable.Empty : m_Map.GetMobilesAt(m_Location); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Map.MobileBoundsEnumerable GetMobilesInRange(int range) => GetMobilesInRange(range); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Map.MobileBoundsEnumerable GetMobilesInRange(int range) where T : Mobile => - m_Map == null ? Map.MobileBoundsEnumerable.Empty : m_Map.GetMobilesInRange(m_Location, range); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Map.ClientAtEnumerable GetClientsAt() => - m_Map == null ? Map.ClientAtEnumerable.Empty : Map.GetClientsAt(m_Location); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public Map.ClientBoundsEnumerable GetClientsInRange(int range) => - m_Map == null ? Map.ClientBoundsEnumerable.Empty : Map.GetClientsInRange(m_Location, range); - public void SayTo(Mobile to, bool ascii, string text) => PrivateOverheadMessage(MessageType.Regular, SpeechHue, ascii, text, to.NetState); @@ -8318,7 +8246,7 @@ public partial class Mobile : IHued, IComparable, ISpawnable, IObjectPro Region.OnDidHarmful(this, target); target.Region.OnGotHarmful(this, target); - if (!indirect) + if (!indirect && !ChangingCombatant) { Combatant = target; } diff --git a/Projects/Server/Network/Packets/OutgoingMobilePackets.cs b/Projects/Server/Network/Packets/OutgoingMobilePackets.cs index 2e41e656d..9345b6be9 100644 --- a/Projects/Server/Network/Packets/OutgoingMobilePackets.cs +++ b/Projects/Server/Network/Packets/OutgoingMobilePackets.cs @@ -25,8 +25,10 @@ public static class OutgoingMobilePackets public const int BondedStatusPacketLength = 11; public const int DeathAnimationPacketLength = 13; public const int MobileMovingPacketLength = 17; - public const int MobileMovingPacketCacheHeight = 7 * 2; // 7 notoriety, 2 client versions - public const int MobileMovingPacketCacheByteLength = MobileMovingPacketLength * MobileMovingPacketCacheHeight; + + // Mobile Moving Packet plus 2 bytes for regular/stygian flags + public const int MobileMovingPacketCacheByteLength = MobileMovingPacketLength + 2; + public const int AttributeMaximum = 100; public const int MobileAttributePacketLength = 9; public const int MobileAttributesPacketLength = 17; @@ -99,27 +101,26 @@ public static class OutgoingMobilePackets ns.Send(span); } - public static void CreateMobileMoving(Span buffer, Mobile m, int noto, bool stygianAbyss) + public static void CreateMobileMoving(Span buffer, Mobile m, int noto, byte packetFlags) { - if (buffer[0] != 0) + if (buffer[0] == 0) { - return; + var loc = m.Location; + var hue = m.SolidHueOverride >= 0 ? m.SolidHueOverride : m.Hue; + + var writer = new SpanWriter(buffer); + writer.Write((byte)0x77); // Packet ID + writer.Write(m.Serial); + writer.Write((short)m.Body); + writer.Write((short)loc.m_X); + writer.Write((short)loc.m_Y); + writer.Write((sbyte)loc.m_Z); + writer.Write((byte)m.Direction); + writer.Write((short)hue); } - var loc = m.Location; - var hue = m.SolidHueOverride >= 0 ? m.SolidHueOverride : m.Hue; - - var writer = new SpanWriter(buffer); - writer.Write((byte)0x77); // Packet ID - writer.Write(m.Serial); - writer.Write((short)m.Body); - writer.Write((short)loc.m_X); - writer.Write((short)loc.m_Y); - writer.Write((sbyte)loc.m_Z); - writer.Write((byte)m.Direction); - writer.Write((short)hue); - writer.Write((byte)m.GetPacketFlags(stygianAbyss)); - writer.Write((byte)noto); + buffer[15] = packetFlags; + buffer[16] = (byte)noto; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -134,7 +135,8 @@ public static class OutgoingMobilePackets } Span buffer = stackalloc byte[MobileMovingPacketLength].InitializePacket(); - CreateMobileMoving(buffer, target, noto, ns.StygianAbyss); + var packetFlags = (byte)target.GetPacketFlags(ns.StygianAbyss); + CreateMobileMoving(buffer, target, noto, packetFlags); ns.Send(buffer); } @@ -142,8 +144,6 @@ public static class OutgoingMobilePackets public static void SendMobileMovingUsingCache(this NetState ns, Span cache, Mobile source, Mobile target) => ns.SendMobileMovingUsingCache(cache, target, Notoriety.Compute(source, target)); - // Requires a buffer of 14 packets, 17 bytes per packet (238 bytes). - // Requires cache to have the first byte of each packet initially zeroed. public static void SendMobileMovingUsingCache(this NetState ns, Span cache, Mobile target, int noto) { if (ns.CannotSendPackets()) @@ -151,13 +151,15 @@ public static class OutgoingMobilePackets return; } - var stygianAbyss = ns.StygianAbyss; - // Indexes 0-6 for pre-SA, and 7-13 for SA - var row = noto + (stygianAbyss ? 6 : -1); - var buffer = cache.Slice(row * MobileMovingPacketLength, MobileMovingPacketLength); - CreateMobileMoving(buffer, target, noto, stygianAbyss); + // Cache the packet flags for regular/stygian if the packet hasn't been built yet + if (cache[0] == 0) + { + cache[17] = (byte)target.GetPacketFlags(false); + cache[18] = (byte)target.GetPacketFlags(true); + } - ns.Send(buffer); + CreateMobileMoving(cache, target, noto, ns.StygianAbyss ? cache[18] : cache[17]); + ns.Send(cache[..17]); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Projects/Server/Regions/Region.cs b/Projects/Server/Regions/Region.cs index a53e6466f..1c71756a4 100644 --- a/Projects/Server/Regions/Region.cs +++ b/Projects/Server/Regions/Region.cs @@ -547,10 +547,29 @@ public class Region : IComparable, IValueLinkListNode public virtual bool AcceptsSpawnsFrom(Region region) => AllowSpawn() && (region == this || Parent?.AcceptsSpawnsFrom(region) == true); + public PooledRefList GetPlayersPooled() + { + var list = PooledRefList.Create(); + for (var i = 0; i < Sectors?.Length; i++) + { + var sector = Sectors[i]; + + foreach (var ns in sector.Clients) + { + var player = ns.Mobile; + if (player?.Deleted == false && player.Region.IsPartOf(this)) + { + list.Add(ns.Mobile); + } + } + } + + return list; + } + public List GetPlayers() { - var list = new List(); - + List list = []; for (var i = 0; i < Sectors?.Length; i++) { var sector = Sectors[i]; @@ -609,6 +628,25 @@ public class Region : IComparable, IValueLinkListNode return list; } + public PooledRefList GetMobilesPooled() + { + var list = PooledRefList.Create(); + for (var i = 0; i < Sectors?.Length; i++) + { + var sector = Sectors[i]; + + foreach (var mobile in sector.Mobiles) + { + if (mobile.Region.IsPartOf(this)) + { + list.Add(mobile); + } + } + } + + return list; + } + public int GetMobileCount() { var count = 0; @@ -649,6 +687,26 @@ public class Region : IComparable, IValueLinkListNode return list; } + public PooledRefList GetItemsPooled() + { + var list = PooledRefList.Create(); + + for (var i = 0; i < Sectors?.Length; i++) + { + var sector = Sectors[i]; + + foreach (var item in sector.Items) + { + if (Find(item.Location, item.Map).IsPartOf(this)) + { + list.Add(item); + } + } + } + + return list; + } + public int GetItemCount() { var count = 0; diff --git a/Projects/Server/Server.csproj b/Projects/Server/Server.csproj index 9142059c6..d9da8166d 100644 --- a/Projects/Server/Server.csproj +++ b/Projects/Server/Server.csproj @@ -37,10 +37,10 @@ - + - + diff --git a/Projects/Server/Utilities/Html.cs b/Projects/Server/Utilities/Html.cs index 91d2b6f3b..907b90812 100644 --- a/Projects/Server/Utilities/Html.cs +++ b/Projects/Server/Utilities/Html.cs @@ -14,236 +14,270 @@ *************************************************************************/ using System; +using System.Buffers; using System.Runtime.CompilerServices; -using System.Text; using Server.Buffers; +using Server.Text; namespace Server; +public enum TextAlignment : byte +{ + Left = 0, + Center = 1, + Right = 2 +} + public static class Html { [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RawInterpolatedStringHandler Color( - scoped ref RawInterpolatedStringHandler textHandler, - ReadOnlySpan color, - int size = -1, byte fontStyle = 0 - ) + public static string Center(this string input) => Center(input.AsSpan()); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Center(this ReadOnlySpan input) => $"
{input}
"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Center(ref RawInterpolatedStringHandler input) { - var handler = textHandler.Text.Color(color, size, fontStyle); - textHandler.Clear(); - return handler; + var str = input.Text.Center(); + input.Clear(); + return str; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RawInterpolatedStringHandler Color( - this ReadOnlySpan text, - ReadOnlySpan color, - int size = -1, - byte fontStyle = 0 + public static string Center(this string input, int color) => Center(input.AsSpan(), color); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Center(this string input, ReadOnlySpan color) => Center(input.AsSpan(), color); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Center(this ReadOnlySpan input, int color) => + $"
{input}
"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Center(this ReadOnlySpan input, ReadOnlySpan color) => + $"
{input}
"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Center(ref RawInterpolatedStringHandler input, int color) + { + var str = input.Text.Center(color); + input.Clear(); + return str; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Center(ref RawInterpolatedStringHandler input, ReadOnlySpan color) + { + var str = input.Text.Center(color); + input.Clear(); + return str; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Color(this string input, int color) => Color(input.AsSpan(), color); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Color(this string input, ReadOnlySpan color) => Color(input.AsSpan(), color); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Color(this ReadOnlySpan input, int color) => $"{input}"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Color(this ReadOnlySpan input, ReadOnlySpan color) => + $"{input}"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Color(ref RawInterpolatedStringHandler input, int color) + { + var str = input.Text.Color(color); + input.Clear(); + return str; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Color(ref RawInterpolatedStringHandler input, ReadOnlySpan color) + { + var str = input.Text.Color(color); + input.Clear(); + return str; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Right(this string input) => Right(input.AsSpan()); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Right(this ReadOnlySpan input) => $"{input}"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Right(ref RawInterpolatedStringHandler input) + { + var str = input.Text.Right(); + input.Clear(); + return str; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Right(this string input, int color) => Right(input.AsSpan(), color); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Right(this string input, ReadOnlySpan color) => Right(input.AsSpan(), color); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Right(this ReadOnlySpan input, int color) => + $"{input}"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Right(this ReadOnlySpan input, ReadOnlySpan color) => + $"{input}"; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Right(ref RawInterpolatedStringHandler input, int color) + { + var str = input.Text.Right(color); + input.Clear(); + return str; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string Right(ref RawInterpolatedStringHandler input, ReadOnlySpan color) + { + var str = input.Text.Right(color); + input.Clear(); + return str; + } + + private static readonly SearchValues _htmlSearchValues = SearchValues.Create('<', '>', '&', '"', '\''); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static string EscapeHtml(this string input) + { + if (string.IsNullOrEmpty(input)) + { + return input ?? ""; + } + + return EscapeHtml(input.AsSpan()); + } + + public static string EscapeHtml(this ReadOnlySpan input) + { + if (input.IsEmpty) + { + return string.Empty; + } + + int indexOfAny = input.IndexOfAny(_htmlSearchValues); + if (indexOfAny < 0) + { + return input.ToString(); + } + + using var builder = ValueStringBuilder.Create(input.Length * 2); + int lastIndex = 0; + + while (indexOfAny >= 0) + { + if (indexOfAny > lastIndex) + { + builder.Append(input[lastIndex..indexOfAny]); + } + + char c = input[indexOfAny]; + var replacement = c switch + { + '&' => "&", + '<' => "<", + '>' => ">", + '"' => """, + '\'' => "'" + }; + builder.Append(replacement); + + lastIndex = indexOfAny + 1; + indexOfAny = input[lastIndex..].IndexOfAny(_htmlSearchValues); + if (indexOfAny < 0) + { + break; + } + + indexOfAny += lastIndex; + } + + if (lastIndex < input.Length) + { + builder.Append(input[lastIndex..]); + } + + var result = builder.ToString(); + builder.Dispose(); + return result; + } + + public static string Build( + ReadOnlySpan text, ReadOnlySpan color = default, int size = -1, byte fontStyle = 0, + TextAlignment align = TextAlignment.Left ) { - if (color != Span.Empty) + var arr = STArrayPool.Shared.Rent(BuildCharCount(text, color)); + var bytesWritten = Build(text, arr.AsSpan(), color, size, fontStyle, align); + + var result = arr.AsSpan(0, bytesWritten).ToString(); + STArrayPool.Shared.Return(arr); + + return result; + } + + public static int BuildCharCount(ReadOnlySpan text, ReadOnlySpan color) => 61 + text.Length + color.Length; + + public static int Build( + ReadOnlySpan text, Span dest, ReadOnlySpan color = default, int size = -1, byte fontStyle = 0, + TextAlignment align = TextAlignment.Left + ) + { + using var builder = new ValueStringBuilder(dest); + if (align == TextAlignment.Right) { + builder.Append(""); + } + else if (align == TextAlignment.Center) + { + builder.Append("
"); + } + + if (color.Length > 0 || size > -1 || fontStyle > 0) + { + builder.Append(" 0) + { + builder.Append($" COLOR={color}"); + } + if (size > -1) { - if (fontStyle > 0) - { - return $"{text}"; - } - - return $"{text}"; + builder.Append($" SIZE={size}"); } if (fontStyle > 0) { - return $"{text}"; + builder.Append($" STYLE={fontStyle}"); } - - return $"{text}"; + builder.Append($">{text}"); } - - if (size > -1) + else { - if (fontStyle > 0) - { - return $"{text}"; - } - - return $"{text}"; + builder.Append(text); } - if (fontStyle > 0) + if (align == TextAlignment.Right) { - return $"{text}"; + builder.Append(""); } - - return $"{text}"; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RawInterpolatedStringHandler Color( - this ReadOnlySpan text, - int color, - int size = -1, - byte fontStyle = 0 - ) - { - if (color > -1) + else if (align == TextAlignment.Center) { - return text.Color($"#{color:X6}", size, fontStyle); + builder.Append("
"); } - return text.Color((ReadOnlySpan)default, size, fontStyle); + return builder.Length; } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RawInterpolatedStringHandler Color( - scoped ref RawInterpolatedStringHandler textHandler, - int color, - int size = -1, - byte fontStyle = 0 - ) - { - var handler = textHandler.Text.Color(color, size, fontStyle); - textHandler.Clear(); - return handler; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Color( - this string text, - ReadOnlySpan color, - int size = -1, - byte fontStyle = 0 - ) - { - var textHandler = ((ReadOnlySpan)text).Color(color, size, fontStyle); - var str = textHandler.Text.ToString(); - textHandler.Clear(); - return str; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Color( - this string text, - int color, - int size = -1, - byte fontStyle = 0 - ) - { - var textHandler = ((ReadOnlySpan)text).Color(color, size, fontStyle); - var str = textHandler.Text.ToString(); - textHandler.Clear(); - return str; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Center( - this string text, int color, int size = -1, byte fontStyle = 0 - ) - { - var handler = Center((ReadOnlySpan)text, color, size, fontStyle); - var str = handler.Text.ToString(); - handler.Clear(); - - return str; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Center( - this string text, ReadOnlySpan color, int size = -1, byte fontStyle = 0 - ) - { - var handler = Center((ReadOnlySpan)text, color, size, fontStyle); - var str = handler.Text.ToString(); - handler.Clear(); - - return str; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Center(this string text) => text.Center(-1); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RawInterpolatedStringHandler Center(this ReadOnlySpan text) => Center(text, -1); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RawInterpolatedStringHandler Center( - this ReadOnlySpan text, int color, int size = -1, byte fontStyle = 0 - ) => Color($"
{text}
", color, size, fontStyle); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RawInterpolatedStringHandler Center( - this ReadOnlySpan text, ReadOnlySpan color, int size = -1, byte fontStyle = 0 - ) => Color($"
{text}
", color, size, fontStyle); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RawInterpolatedStringHandler Center( - scoped ref RawInterpolatedStringHandler textHandler, int color = -1, int size = -1, byte fontStyle = 0 - ) - { - var handler = textHandler.Text.Center(color, size, fontStyle); - textHandler.Clear(); - return handler; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Right( - this string text, int color, int size = -1, byte fontStyle = 0 - ) - { - var handler = Right((ReadOnlySpan)text, color, size, fontStyle); - var str = handler.Text.ToString(); - handler.Clear(); - - return str; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Right( - this string text, ReadOnlySpan color, int size = -1, byte fontStyle = 0 - ) - { - var handler = Right((ReadOnlySpan)text, color, size, fontStyle); - var str = handler.Text.ToString(); - handler.Clear(); - - return str; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Right(this string text) => text.Right(-1); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RawInterpolatedStringHandler Right( - scoped ref RawInterpolatedStringHandler textHandler, int color = -1, int size = -1, byte fontStyle = 0 - ) - { - var handler = textHandler.Text.Right(color, size, fontStyle); - textHandler.Clear(); - return handler; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RawInterpolatedStringHandler Right( - this ReadOnlySpan text, int color, int size = -1, byte fontStyle = 0 - ) => Color($"{text}", color, size, fontStyle); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RawInterpolatedStringHandler Right( - this ReadOnlySpan text, ReadOnlySpan color, int size = -1, byte fontStyle = 0 - ) => Color($"{text}", color, size, fontStyle); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RawInterpolatedStringHandler Right(this ReadOnlySpan text) => text.Right(-1); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string EscapeHtml(this string input) => - new StringBuilder(input.Length).Append(input) - .Replace("<", "<") - .Replace(">", ">") - .Replace("&", "&") - .Replace("\"", """) - .Replace("'", "'") - .ToString(); } diff --git a/Projects/Server/Utilities/Utility.cs b/Projects/Server/Utilities/Utility.cs index 6ef4aa72c..d405fabe8 100644 --- a/Projects/Server/Utilities/Utility.cs +++ b/Projects/Server/Utilities/Utility.cs @@ -1479,4 +1479,98 @@ public static partial class Utility return DateTime.SpecifyKind(local - tz.GetUtcOffset(local), DateTimeKind.Utc); } + + public static string FormatTimeCompact(this TimeSpan ts, bool showSeconds = false) + { + using var sb = ValueStringBuilder.Create(); + if (ts.Days >= 1) + { + sb.Append($"{ts.Days}d"); + } + + if (sb.Length > 0) + { + sb.Append($" {ts.Hours}h"); + } + else if (ts.Hours >= 1) + { + sb.Append($"{ts.Hours}h"); + } + + if (sb.Length > 0) + { + sb.Append($" {ts.Minutes}m"); + } + else if (ts.Minutes >= 1) + { + sb.Append($"{ts.Minutes}m"); + } + + if (showSeconds) + { + if (sb.Length > 0) + { + sb.Append($" {ts.Seconds}s"); + } + else if (ts.Seconds >= 1) + { + sb.Append($"{ts.Seconds}s"); + } + } + else if (sb.Length == 0) + { + sb.Append("0m"); + } + + return sb.ToString(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double GetDistanceToSqrt(this IEntity entity, Point2D p) => GetDistanceToSqrt(entity.Location, p); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double GetDistanceToSqrt(this IEntity entity, Point3D p) => GetDistanceToSqrt(entity.Location, p); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static double GetDistanceToSqrt(this IEntity from, IEntity to) => GetDistanceToSqrt(from.Location, to.Location); + + public static double GetDistanceToSqrt(this IEntity entity, IPoint2D p) + { + var xDelta = entity.X - p.X; + var yDelta = entity.Y - p.Y; + + return Math.Sqrt(xDelta * xDelta + yDelta * yDelta); + } + + public static double GetDistanceToSqrt(this Point2D from, Point2D to) + { + var xDelta = from.m_X - to.m_X; + var yDelta = from.m_Y - to.m_Y; + + return Math.Sqrt(xDelta * xDelta + yDelta * yDelta); + } + + public static double GetDistanceToSqrt(this Point2D from, Point3D to) + { + var xDelta = from.m_X - to.m_X; + var yDelta = from.m_Y - to.m_Y; + + return Math.Sqrt(xDelta * xDelta + yDelta * yDelta); + } + + public static double GetDistanceToSqrt(this Point3D from, Point2D to) + { + var xDelta = from.m_X - to.m_X; + var yDelta = from.m_Y - to.m_Y; + + return Math.Sqrt(xDelta * xDelta + yDelta * yDelta); + } + + public static double GetDistanceToSqrt(this Point3D from, Point3D to) + { + var xDelta = from.m_X - to.m_X; + var yDelta = from.m_Y - to.m_Y; + + return Math.Sqrt(xDelta * xDelta + yDelta * yDelta); + } } diff --git a/Projects/UOContent.Tests/Tests/Gumps/TestGumps/DynamicTestGump.cs b/Projects/UOContent.Tests/Tests/Gumps/TestGumps/DynamicTestGump.cs index 9ad3e0ae2..998e4c537 100644 --- a/Projects/UOContent.Tests/Tests/Gumps/TestGumps/DynamicTestGump.cs +++ b/Projects/UOContent.Tests/Tests/Gumps/TestGumps/DynamicTestGump.cs @@ -27,7 +27,7 @@ public class DynamicTestGump : DynamicGump builder.AddItem(218, 95, 0xCB0); builder.AddHtml(30, 30, 150, 75, "
Wilt thou sanctify the resurrection of:
"); - builder.AddHtml(30, 70, 150, 25, $"
{_petName}
", true); + builder.AddHtml(30, 70, 150, 25, _petName, align: TextAlignment.Center, background: true); builder.AddButton(40, 105, 0x81A, 0x81B, 0x1); // Okay builder.AddButton(110, 105, 0x819, 0x818, 0x2); // Cancel diff --git a/Projects/UOContent.Tests/Tests/Gumps/TestGumps/StaticLayoutTestGump.cs b/Projects/UOContent.Tests/Tests/Gumps/TestGumps/StaticLayoutTestGump.cs index 85f5ec732..7e1859161 100644 --- a/Projects/UOContent.Tests/Tests/Gumps/TestGumps/StaticLayoutTestGump.cs +++ b/Projects/UOContent.Tests/Tests/Gumps/TestGumps/StaticLayoutTestGump.cs @@ -35,6 +35,6 @@ public class StaticLayoutTestGump : StaticGump protected override void BuildStrings(ref GumpStringsBuilder builder) { - builder.SetHtmlTextCentered("petName", _petName); + builder.SetHtmlText("petName", _petName, align: TextAlignment.Center); } } diff --git a/Projects/UOContent.Tests/Tests/Gumps/TestGumps/StaticTestGump.cs b/Projects/UOContent.Tests/Tests/Gumps/TestGumps/StaticTestGump.cs index ce616116e..ea67720f5 100644 --- a/Projects/UOContent.Tests/Tests/Gumps/TestGumps/StaticTestGump.cs +++ b/Projects/UOContent.Tests/Tests/Gumps/TestGumps/StaticTestGump.cs @@ -24,7 +24,7 @@ public class StaticTestGump : StaticGump builder.AddItem(218, 95, 0xCB0); builder.AddHtml(30, 30, 150, 75, "
Wilt thou sanctify the resurrection of:
"); - builder.AddHtml(30, 70, 150, 25, "
Test
", true); + builder.AddHtml(30, 70, 150, 25, "Test", align: TextAlignment.Center, background: true); builder.AddButton(40, 105, 0x81A, 0x81B, 0x1); // Okay builder.AddButton(110, 105, 0x819, 0x818, 0x2); // Cancel diff --git a/Projects/UOContent.Tests/Tests/Skills/TrackingTests.cs b/Projects/UOContent.Tests/Tests/Skills/TrackingTests.cs new file mode 100644 index 000000000..1c436fd49 --- /dev/null +++ b/Projects/UOContent.Tests/Tests/Skills/TrackingTests.cs @@ -0,0 +1,299 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Server; +using Server.Mobiles; +using Server.SkillHandlers; +using Xunit; + +namespace UOContent.Tests; + +[Collection("Sequential UOContent Tests")] +public class TrackingTests +{ + /// + /// Tests that tracking correctly finds the closest mobiles when there are more than 12 available. + /// This validates the GetClosestMobs logic, especially the early exit optimization. + /// + [Fact] + public void Tracking_FindsClosestMobiles_WhenManyAvailable() + { + var map = Map.Felucca; + var center = new Point3D(1000, 1000, 0); + + var tracker = CreatePlayerMobile(map, center); + tracker.Skills.Tracking.BaseFixedPoint = 1000; // 100.0 skill = 110 range + + var mobiles = new List(); + + try + { + // Create 20 animals at various distances + // First 12 should be the closest ones we find + for (var i = 0; i < 20; i++) + { + var distance = i + 1; // Distance from 1 to 20 + var location = new Point3D(center.X + distance, center.Y, 0); + var animal = CreateAnimal(map, location); + mobiles.Add(animal); + } + + // Invoke tracking through the skill system + // We can't directly test GetClosestMobs since it's private, but we can verify + // the behavior by checking what the gump would show + + // Use reflection to test the private method + var method = typeof(TrackWhoGump).GetMethod( + "GetClosestMobs", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static + ); + + Assert.NotNull(method); + + var range = Math.Clamp(10 + (int)tracker.Skills.Tracking.Value, 0, 100); + var result = (Mobile[])method.Invoke(null, new object[] { tracker, range, 0 }); // 0 = animals + + // Should return at most 12 mobiles + Assert.True(result.Length <= 12); + + // Should return the 12 closest (distances 1-12) + Assert.Equal(12, result.Length); + + // Verify they are sorted by distance + for (var i = 0; i < result.Length - 1; i++) + { + var dist1 = result[i].GetDistanceToSqrt(center); + var dist2 = result[i + 1].GetDistanceToSqrt(center); + Assert.True(dist1 <= dist2, $"Mobiles not sorted: {dist1} > {dist2}"); + } + + // Verify the first mobile is the closest (distance 1) + Assert.Equal(mobiles[0], result[0]); + + // Verify the last mobile is at distance 12 + Assert.Equal(mobiles[11], result[11]); + + // Verify mobile at distance 13 is NOT included + Assert.DoesNotContain(mobiles[12], result); + } + finally + { + tracker?.Delete(); + foreach (var mob in mobiles) + { + mob?.Delete(); + } + } + } + + /// + /// Tests that tracking correctly handles the case where there are exactly 12 mobiles available. + /// + [Fact] + public void Tracking_FindsAllMobiles_WhenExactly12Available() + { + var map = Map.Felucca; + var center = new Point3D(2000, 2000, 0); + + var tracker = CreatePlayerMobile(map, center); + tracker.Skills.Tracking.BaseFixedPoint = 1000; // 100.0 skill = 110 range + + var mobiles = new List(); + + try + { + // Create exactly 12 animals + for (var i = 0; i < 12; i++) + { + var distance = i + 1; + var location = new Point3D(center.X + distance, center.Y, 0); + var animal = CreateAnimal(map, location); + mobiles.Add(animal); + } + + var method = typeof(TrackWhoGump).GetMethod( + "GetClosestMobs", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static + ); + + var range = Math.Clamp(10 + (int)tracker.Skills.Tracking.Value, 0, 100); + var result = (Mobile[])method.Invoke(null, new object[] { tracker, range, 0 }); + + Assert.Equal(12, result.Length); + + // Verify all mobiles are included + foreach (var mob in mobiles) + { + Assert.Contains(mob, result); + } + } + finally + { + tracker?.Delete(); + foreach (var mob in mobiles) + { + mob?.Delete(); + } + } + } + + /// + /// Tests that tracking correctly handles the case where there are fewer than 12 mobiles available. + /// + [Fact] + public void Tracking_FindsAllMobiles_WhenFewerThan12Available() + { + var map = Map.Felucca; + var center = new Point3D(3000, 3000, 0); + + var tracker = CreatePlayerMobile(map, center); + tracker.Skills.Tracking.BaseFixedPoint = 1000; + + var mobiles = new List(); + + try + { + // Create only 5 animals + for (var i = 0; i < 5; i++) + { + var distance = i + 1; + var location = new Point3D(center.X + distance, center.Y, 0); + var animal = CreateAnimal(map, location); + mobiles.Add(animal); + } + + var method = typeof(TrackWhoGump).GetMethod( + "GetClosestMobs", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static + ); + + var range = Math.Clamp(10 + (int)tracker.Skills.Tracking.Value, 0, 100); + var result = (Mobile[])method.Invoke(null, new object[] { tracker, range, 0 }); + + Assert.Equal(5, result.Length); + + // Verify all mobiles are included + foreach (var mob in mobiles) + { + Assert.Contains(mob, result); + } + } + finally + { + tracker?.Delete(); + foreach (var mob in mobiles) + { + mob?.Delete(); + } + } + } + + /// + /// Tests that the early exit optimization works correctly when mobiles in farther sectors + /// are closer than mobiles in nearer sectors (worst case for the optimization). + /// + [Fact] + public void Tracking_EarlyExitWorksCorrectly_WithFarSectorNearMobiles() + { + var map = Map.Felucca; + // Use a location that puts mobiles in different sectors + var center = new Point3D(1500, 1500, 0); + + var tracker = CreatePlayerMobile(map, center); + tracker.Skills.Tracking.BaseFixedPoint = 1000; + + var mobiles = new List(); + + try + { + // Create 15 animals where some farther ones might be in closer proximity + // but in different sectors + for (var i = 0; i < 15; i++) + { + var distance = i + 1; + // Alternate between X and Y to potentially cross sector boundaries + var location = i % 2 == 0 + ? new Point3D(center.X + distance, center.Y, 0) + : new Point3D(center.X, center.Y + distance, 0); + var animal = CreateAnimal(map, location); + mobiles.Add(animal); + } + + var method = typeof(TrackWhoGump).GetMethod( + "GetClosestMobs", + System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static + ); + + var range = Math.Clamp(10 + (int)tracker.Skills.Tracking.Value, 0, 100); + var result = (Mobile[])method.Invoke(null, new object[] { tracker, range, 0 }); + + Assert.True(result.Length <= 12); + + // Get the actual 12 closest by brute force + var allDistances = mobiles + .Select(m => (Mobile: m, Distance: m.GetDistanceToSqrt(center))) + .OrderBy(x => x.Distance) + .ToList(); + + var expected12Closest = allDistances.Take(12).ToList(); + + // The result should match the actual 12 closest + Assert.Equal(expected12Closest.Count, result.Length); + + // Verify all returned mobiles are in the expected 12 closest + foreach (var mob in result) + { + Assert.Contains(mob, expected12Closest.Select(x => x.Mobile)); + } + + // Verify they are sorted by distance (main invariant) + for (var i = 0; i < result.Length - 1; i++) + { + var dist1 = result[i].GetDistanceToSqrt(center); + var dist2 = result[i + 1].GetDistanceToSqrt(center); + Assert.True(dist1 <= dist2, $"Mobiles not sorted by distance: {dist1} > {dist2}"); + } + + // Verify we got the closest mobiles (not just any 12) + var maxResultDistance = result.Max(m => m.GetDistanceToSqrt(center)); + var minExcludedDistance = allDistances.Skip(12).Any() + ? allDistances.Skip(12).Min(x => x.Distance) + : double.MaxValue; + Assert.True(maxResultDistance <= minExcludedDistance, + $"Found a closer excluded mobile: max in result={maxResultDistance}, min excluded={minExcludedDistance}"); + } + finally + { + tracker?.Delete(); + foreach (var mob in mobiles) + { + mob?.Delete(); + } + } + } + + private static PlayerMobile CreatePlayerMobile(Map map, Point3D location) + { + var mobile = new PlayerMobile(World.NewMobile); + mobile.DefaultMobileInit(); + mobile.MoveToWorld(location, map); + return mobile; + } + + private static TestAnimal CreateAnimal(Map map, Point3D location) + { + var animal = new TestAnimal(World.NewMobile); + animal.DefaultMobileInit(); + animal.MoveToWorld(location, map); + return animal; + } + + private class TestAnimal : BaseCreature + { + public TestAnimal(Serial serial) : base(serial) + { + Body = 0xD8; // Llama body - an animal body + } + } +} + diff --git a/Projects/UOContent.Tests/UOContent.Tests.csproj b/Projects/UOContent.Tests/UOContent.Tests.csproj index 967f997cd..dca5343eb 100644 --- a/Projects/UOContent.Tests/UOContent.Tests.csproj +++ b/Projects/UOContent.Tests/UOContent.Tests.csproj @@ -4,9 +4,9 @@ Debug;Release;Analyze - + - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Projects/UOContent/Accounting/Account.cs b/Projects/UOContent/Accounting/Account.cs index c9e85de2c..c33e459f2 100644 --- a/Projects/UOContent/Accounting/Account.cs +++ b/Projects/UOContent/Accounting/Account.cs @@ -850,40 +850,6 @@ public partial class Account : IAccount, IComparable return true; } - /// - /// Deserializes a list of string values from an xml element. Null values are not added to the list. - /// - /// The XmlElement from which to deserialize. - /// String list. Value will never be null. - private static string[] LoadAccessCheck(XmlElement node) - { - string[] stringList; - var accessCheck = node["accessCheck"]; - - if (accessCheck != null) - { - var list = new List(); - - foreach (XmlElement ip in accessCheck.GetElementsByTagName("ip")) - { - var text = Utility.GetText(ip, null); - - if (text != null) - { - list.Add(text); - } - } - - stringList = list.ToArray(); - } - else - { - stringList = []; - } - - return stringList; - } - /// /// Deserializes a list of IPAddress values from an xml element. /// diff --git a/Projects/UOContent/Accounting/Security/AccountSecurity.cs b/Projects/UOContent/Accounting/Security/AccountSecurity.cs index 22860d370..355137ab7 100644 --- a/Projects/UOContent/Accounting/Security/AccountSecurity.cs +++ b/Projects/UOContent/Accounting/Security/AccountSecurity.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2023 - ModernUO Development Team * + * Copyright 2019-2025 - ModernUO Development Team * * Email: hi@modernuo.com * * File: AccountSecurity.cs * * * @@ -15,56 +15,55 @@ using System; -namespace Server.Accounting.Security -{ - public enum PasswordProtectionAlgorithm - { - // Obsolete algorithms from RunUO. These are not secure! - // They are included for password upgrades only. - None, - MD5, - SHA1, +namespace Server.Accounting.Security; - // Supported algorithms - SHA2, // ServUO compatibility - PBKDF2, - Argon2 // Recommended algorithm for real security. +public enum PasswordProtectionAlgorithm +{ + // Obsolete algorithms from RunUO. These are not secure! + // They are included for password upgrades only. + None, + MD5, + SHA1, + + // Supported algorithms + SHA2, // ServUO compatibility + PBKDF2, + Argon2 // Recommended algorithm for real security. +} + +public static class AccountSecurity +{ + public static PasswordProtectionAlgorithm CurrentAlgorithm { get; set; } + + public static IPasswordProtection CurrentPasswordProtection => GetPasswordProtection(CurrentAlgorithm); + + public static void Configure() + { + CurrentAlgorithm = + ServerConfiguration.GetOrUpdateSetting( + "accountSecurity.encryptionAlgorithm", + PasswordProtectionAlgorithm.Argon2 + ); + + if (CurrentAlgorithm < PasswordProtectionAlgorithm.SHA2) + { + throw new Exception($"Security: {CurrentAlgorithm} is obsolete and not secure. Do not use it."); + } } - public static class AccountSecurity + public static IPasswordProtection GetPasswordProtection(PasswordProtectionAlgorithm algorithm) { - public static PasswordProtectionAlgorithm CurrentAlgorithm { get; set; } - - public static IPasswordProtection CurrentPasswordProtection => GetPasswordProtection(CurrentAlgorithm); - - public static void Configure() + var passwordProtection = algorithm switch { - CurrentAlgorithm = - ServerConfiguration.GetOrUpdateSetting( - "accountSecurity.encryptionAlgorithm", - PasswordProtectionAlgorithm.Argon2 - ); + PasswordProtectionAlgorithm.MD5 => HashAlgorithmPasswordProtection.MD5Instance, + PasswordProtectionAlgorithm.SHA1 => HashAlgorithmPasswordProtection.SHA1Instance, + PasswordProtectionAlgorithm.SHA2 => HashAlgorithmPasswordProtection.SHA2Instance, + PasswordProtectionAlgorithm.PBKDF2 => PBKDF2PasswordProtection.Instance, + PasswordProtectionAlgorithm.Argon2 => Argon2PasswordProtection.Instance, + PasswordProtectionAlgorithm.None => throw new Exception("Do not use PasswordProtectionAlgorithm.None"), + _ => throw new Exception("No algorithm") + }; - if (CurrentAlgorithm < PasswordProtectionAlgorithm.SHA2) - { - throw new Exception($"Security: {CurrentAlgorithm} is obsolete and not secure. Do not use it."); - } - } - - public static IPasswordProtection GetPasswordProtection(PasswordProtectionAlgorithm algorithm) - { - var passwordProtection = algorithm switch - { - PasswordProtectionAlgorithm.MD5 => HashAlgorithmPasswordProtection.MD5Instance, - PasswordProtectionAlgorithm.SHA1 => HashAlgorithmPasswordProtection.SHA1Instance, - PasswordProtectionAlgorithm.SHA2 => HashAlgorithmPasswordProtection.SHA2Instance, - PasswordProtectionAlgorithm.PBKDF2 => PBKDF2PasswordProtection.Instance, - PasswordProtectionAlgorithm.Argon2 => Argon2PasswordProtection.Instance, - PasswordProtectionAlgorithm.None => throw new Exception("Do not use PasswordProtectionAlgorithm.None"), - _ => throw new Exception("No algorithm") - }; - - return passwordProtection; - } + return passwordProtection; } } diff --git a/Projects/UOContent/Accounting/Security/Argon2PasswordProtection.cs b/Projects/UOContent/Accounting/Security/Argon2PasswordProtection.cs index f49d2a040..6d3214a48 100644 --- a/Projects/UOContent/Accounting/Security/Argon2PasswordProtection.cs +++ b/Projects/UOContent/Accounting/Security/Argon2PasswordProtection.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2023 - ModernUO Development Team * + * Copyright 2019-2025 - ModernUO Development Team * * Email: hi@modernuo.com * * File: Argon2PasswordProtection.cs * * * @@ -15,18 +15,17 @@ using System.Security.Cryptography; -namespace Server.Accounting.Security +namespace Server.Accounting.Security; + +public class Argon2PasswordProtection : IPasswordProtection { - public class Argon2PasswordProtection : IPasswordProtection - { - public static IPasswordProtection Instance = new Argon2PasswordProtection(); + public static IPasswordProtection Instance = new Argon2PasswordProtection(); - private readonly Argon2PasswordHasher m_PasswordHasher = new(rng: BuiltInSecureRng.Generator); + private readonly Argon2PasswordHasher m_PasswordHasher = new(rng: RandomNumberGenerator.Create()); - public string EncryptPassword(string plainPassword) => - m_PasswordHasher.Hash(plainPassword); + public string EncryptPassword(string plainPassword) => + m_PasswordHasher.Hash(plainPassword); - public bool ValidatePassword(string encryptedPassword, string plainPassword) => - m_PasswordHasher.Verify(encryptedPassword, plainPassword); - } + public bool ValidatePassword(string encryptedPassword, string plainPassword) => + m_PasswordHasher.Verify(encryptedPassword, plainPassword); } diff --git a/Projects/UOContent/Accounting/Security/HashAlgorithmPasswordProtection.cs b/Projects/UOContent/Accounting/Security/HashAlgorithmPasswordProtection.cs index 978adbbf8..f50888675 100644 --- a/Projects/UOContent/Accounting/Security/HashAlgorithmPasswordProtection.cs +++ b/Projects/UOContent/Accounting/Security/HashAlgorithmPasswordProtection.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2023 - ModernUO Development Team * + * Copyright 2019-2025 - ModernUO Development Team * * Email: hi@modernuo.com * * File: HashAlgorithmPasswordProtection.cs * * * @@ -17,24 +17,23 @@ using System; using System.Security.Cryptography; using Server.Text; -namespace Server.Accounting.Security +namespace Server.Accounting.Security; + +public class HashAlgorithmPasswordProtection : IPasswordProtection { - public class HashAlgorithmPasswordProtection : IPasswordProtection + public static IPasswordProtection MD5Instance = new HashAlgorithmPasswordProtection(MD5.Create()); + public static IPasswordProtection SHA1Instance = new HashAlgorithmPasswordProtection(SHA1.Create()); + public static IPasswordProtection SHA2Instance = new HashAlgorithmPasswordProtection(SHA512.Create()); + private readonly HashAlgorithm _hashAlgorithm; + + public HashAlgorithmPasswordProtection(HashAlgorithm hashAlgorithm) => _hashAlgorithm = hashAlgorithm; + + public string EncryptPassword(string plainPassword) { - public static IPasswordProtection MD5Instance = new HashAlgorithmPasswordProtection(MD5.Create()); - public static IPasswordProtection SHA1Instance = new HashAlgorithmPasswordProtection(SHA1.Create()); - public static IPasswordProtection SHA2Instance = new HashAlgorithmPasswordProtection(SHA512.Create()); - private readonly HashAlgorithm _hashAlgorithm; - - public HashAlgorithmPasswordProtection(HashAlgorithm hashAlgorithm) => _hashAlgorithm = hashAlgorithm; - - public string EncryptPassword(string plainPassword) - { - byte[] bytes = plainPassword.AsSpan(0, Math.Min(256, plainPassword.Length)).GetBytesAscii(); - return _hashAlgorithm.ComputeHash(bytes).ToHexString(); - } - - public bool ValidatePassword(string encryptedPassword, string plainPassword) => - EncryptPassword(plainPassword) == encryptedPassword; + byte[] bytes = plainPassword.AsSpan(0, Math.Min(256, plainPassword.Length)).GetBytesAscii(); + return _hashAlgorithm.ComputeHash(bytes).ToHexString(); } + + public bool ValidatePassword(string encryptedPassword, string plainPassword) => + EncryptPassword(plainPassword) == encryptedPassword; } diff --git a/Projects/UOContent/Accounting/Security/PBKDF2PasswordProtection.cs b/Projects/UOContent/Accounting/Security/PBKDF2PasswordProtection.cs index e9fd8b05f..74f999a3f 100644 --- a/Projects/UOContent/Accounting/Security/PBKDF2PasswordProtection.cs +++ b/Projects/UOContent/Accounting/Security/PBKDF2PasswordProtection.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2023 - ModernUO Development Team * + * Copyright 2019-2025 - ModernUO Development Team * * Email: hi@modernuo.com * * File: PBKDF2PasswordProtection.cs * * * @@ -18,42 +18,43 @@ using System.Buffers.Binary; using System.Security.Cryptography; using Server.Text; -namespace Server.Accounting.Security +namespace Server.Accounting.Security; + +public class PBKDF2PasswordProtection : IPasswordProtection { - public class PBKDF2PasswordProtection : IPasswordProtection + private const ushort m_MinIterations = 1024; + private const ushort m_MaxIterations = 1536; + private const int m_SaltSize = 8; + private const int m_HashSize = 32; + private const int m_OutputSize = 2 + m_SaltSize + m_HashSize; + public static readonly IPasswordProtection Instance = new PBKDF2PasswordProtection(); + + public string EncryptPassword(string plainPassword) { - private const ushort m_MinIterations = 1024; - private const ushort m_MaxIterations = 1536; - private const int m_SaltSize = 8; - private const int m_HashSize = 32; - private const int m_OutputSize = 2 + m_SaltSize + m_HashSize; - public static readonly IPasswordProtection Instance = new PBKDF2PasswordProtection(); + Span output = stackalloc byte[m_OutputSize]; + var iterations = Utility.RandomMinMax(m_MinIterations, m_MaxIterations); + BinaryPrimitives.WriteUInt16LittleEndian(output[..2], (ushort)iterations); - public string EncryptPassword(string plainPassword) - { - Span output = stackalloc byte[m_OutputSize]; - var iterations = Utility.RandomMinMax(m_MinIterations, m_MaxIterations); - BinaryPrimitives.WriteUInt16LittleEndian(output[..2], (ushort)iterations); + var salt = output.Slice(2, m_SaltSize); + RandomNumberGenerator.Fill(salt); - var rfc2898 = new Rfc2898DeriveBytes(plainPassword, m_SaltSize, iterations, HashAlgorithmName.SHA256); - rfc2898.Salt.CopyTo(output.Slice(2, m_SaltSize)); - rfc2898.GetBytes(m_HashSize).CopyTo(output[(m_SaltSize + 2)..]); + var hash = output.Slice(2 + m_SaltSize, m_HashSize); + Rfc2898DeriveBytes.Pbkdf2(plainPassword, salt, hash, iterations, HashAlgorithmName.SHA256); - return output.ToHexString(); - } + return output.ToHexString(); + } - public bool ValidatePassword(string encryptedPassword, string plainPassword) - { - Span encryptedBytes = stackalloc byte[m_OutputSize]; - encryptedPassword.GetBytes(encryptedBytes); + public bool ValidatePassword(string encryptedPassword, string plainPassword) + { + Span encryptedBytes = stackalloc byte[m_OutputSize]; + encryptedPassword.GetBytes(encryptedBytes); - var iterations = BinaryPrimitives.ReadUInt16LittleEndian(encryptedBytes[..2]); - var salt = encryptedBytes.Slice(2, m_SaltSize); + var iterations = BinaryPrimitives.ReadUInt16LittleEndian(encryptedBytes[..2]); + var salt = encryptedBytes.Slice(2, m_SaltSize); - ReadOnlySpan hash = - new Rfc2898DeriveBytes(plainPassword, salt.ToArray(), iterations, HashAlgorithmName.SHA256).GetBytes(m_HashSize); + Span hash = stackalloc byte[m_HashSize]; + Rfc2898DeriveBytes.Pbkdf2(plainPassword, salt, hash, iterations, HashAlgorithmName.SHA256); - return hash.SequenceEqual(encryptedBytes[(m_SaltSize + 2)..]); - } + return hash.SequenceEqual(encryptedBytes[(m_SaltSize + 2)..]); } } diff --git a/Projects/UOContent/Commands/Generic/Commands/DesignInsert.cs b/Projects/UOContent/Commands/Generic/Commands/DesignInsert.cs index 345ff37c9..c8d294a8f 100644 --- a/Projects/UOContent/Commands/Generic/Commands/DesignInsert.cs +++ b/Projects/UOContent/Commands/Generic/Commands/DesignInsert.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Server.Collections; using Server.Gumps; using Server.Items; using Server.Multis; @@ -112,7 +113,7 @@ namespace Server.Commands.Generic if (okay) { - var foundations = new List(); + using var foundations = PooledRefQueue.Create(); flushToLog = list.Count > 20; for (var i = 0; i < list.Count; ++i) @@ -127,7 +128,7 @@ namespace Server.Commands.Generic if (!foundations.Contains(house)) { - foundations.Add(house); + foundations.Enqueue(house); } break; @@ -146,9 +147,9 @@ namespace Server.Commands.Generic } } - foreach (var house in foundations) + while (foundations.Count > 0) { - house.Delta(ItemDelta.Update); + foundations.Dequeue().Delta(ItemDelta.Update); } } else diff --git a/Projects/UOContent/Commands/Generic/Implementors/RegionCommandImplementor.cs b/Projects/UOContent/Commands/Generic/Implementors/RegionCommandImplementor.cs index c866e41dc..eb4a3f8d9 100644 --- a/Projects/UOContent/Commands/Generic/Implementors/RegionCommandImplementor.cs +++ b/Projects/UOContent/Commands/Generic/Implementors/RegionCommandImplementor.cs @@ -33,8 +33,10 @@ namespace Server.Commands.Generic if (mobiles) { - foreach (var mob in reg.GetMobiles()) + using var mobileList = reg.GetMobilesPooled(); + for (var i = 0; i < mobileList.Count; i++) { + var mob = mobileList[i]; if (BaseCommand.IsAccessible(from, mob) && ext.IsValid(mob)) { list.Add(mob); @@ -44,7 +46,8 @@ namespace Server.Commands.Generic if (items) { - foreach (var item in reg.GetItems()) + using var itemList = reg.GetItemsPooled(); + foreach (var item in itemList) { if (BaseCommand.IsAccessible(from, item) && ext.IsValid(item)) { diff --git a/Projects/UOContent/Commands/HelpInfo.cs b/Projects/UOContent/Commands/HelpInfo.cs index 0361fbadc..f9686f37e 100644 --- a/Projects/UOContent/Commands/HelpInfo.cs +++ b/Projects/UOContent/Commands/HelpInfo.cs @@ -361,7 +361,7 @@ public static class HelpInfo builder.AddPage(); builder.AddBackground(0, 0, _width, _height, 5054); - builder.AddHtml(10, 10, _width - 20, 20, _info.Name.Center(0xFF0000)); + builder.AddHtml(10, 10, _width - 20, 20, _info.Name, color: "#FF0000", align: TextAlignment.Center); using var sb = ValueStringBuilder.Create(); @@ -412,7 +412,7 @@ public static class HelpInfo } sb.Append(_info.Description); - builder.AddHtml(10, 40, _width - 20, _height - 80, sb.ToString(), false, true); + builder.AddHtml(10, 40, _width - 20, _height - 80, sb.AsSpan(true), background: false, scrollbar: true); } } } diff --git a/Projects/UOContent/Engines/Bulk Orders/BaseBOD.cs b/Projects/UOContent/Engines/Bulk Orders/BaseBOD.cs index 20aa0a3ec..a95ca63b5 100644 --- a/Projects/UOContent/Engines/Bulk Orders/BaseBOD.cs +++ b/Projects/UOContent/Engines/Bulk Orders/BaseBOD.cs @@ -14,11 +14,9 @@ namespace Server.Engines.BulkOrders _material = material; } - public BaseBOD() : base(Core.AOS ? 0x2258 : 0x14EF) - { - Weight = 1.0; - LootType = LootType.Blessed; - } + public BaseBOD() : base(Core.AOS ? 0x2258 : 0x14EF) => LootType = LootType.Blessed; + + public override double DefaultWeight => 1.0; public abstract bool Complete { get; } diff --git a/Projects/UOContent/Engines/Bulk Orders/Books/BOBGump.cs b/Projects/UOContent/Engines/Bulk Orders/Books/BOBGump.cs index 28ee596c2..7be389e49 100644 --- a/Projects/UOContent/Engines/Bulk Orders/Books/BOBGump.cs +++ b/Projects/UOContent/Engines/Bulk Orders/Books/BOBGump.cs @@ -6,603 +6,598 @@ using Server.Mobiles; using Server.Network; using Server.Prompts; -namespace Server.Engines.BulkOrders +namespace Server.Engines.BulkOrders; + +public class BOBGump : DynamicGump { - public class BOBGump : Gump + private const int LabelColor = 0x7FFF; + + private readonly PlayerMobile _from; + private int _page; + + public BulkOrderBook Book { get; } + public List List { get; private set; } + public override bool Singleton => true; + + public BOBGump(PlayerMobile from, BulkOrderBook book) : base(12, 24) { - private const int LabelColor = 0x7FFF; - private readonly BulkOrderBook _book; - private readonly PlayerMobile _from; - private readonly List _list; + _from = from; + Book = book; + } - private int _page; + public void ResetList() => List = null; - public override bool Singleton => true; - - public BOBGump(PlayerMobile from, BulkOrderBook book, int page = 0, List list = null) : base(12, 24) + protected override void BuildLayout(ref DynamicGumpBuilder builder) + { + if (List == null) { - _from = from; - _book = book; - _page = page; + List = new List(Book.Entries.Count); - if (list == null) + for (var i = 0; i < Book.Entries.Count; ++i) { - list = new List(book.Entries.Count); - - for (var i = 0; i < book.Entries.Count; ++i) - { - var entry = book.Entries[i]; - - if (CheckFilter(entry)) - { - list.Add(entry); - } - } - } - - _list = list; - - var index = GetIndexForPage(page); - var count = GetCountForIndex(index); - - var tableIndex = 0; - - var pv = book.RootParent as PlayerVendor; - - var canDrop = book.IsChildOf(from.Backpack); - var canBuy = pv != null; - var canPrice = canDrop || canBuy; - - if (canBuy) - { - var vi = pv.GetVendorItem(book); - - canBuy = vi?.IsForSale == false; - } - - var width = 600; - - if (!canPrice) - { - width = 516; - } - - X = (624 - width) / 2; - - AddPage(0); - - AddBackground(10, 10, width, 439, 5054); - AddImageTiled(18, 20, width - 17, 420, 2624); - - if (canPrice) - { - AddImageTiled(573, 64, 24, 352, 200); - AddImageTiled(493, 64, 78, 352, 1416); - } - - if (canDrop) - { - AddImageTiled(24, 64, 32, 352, 1416); - } - - AddImageTiled(58, 64, 36, 352, 200); - AddImageTiled(96, 64, 133, 352, 1416); - AddImageTiled(231, 64, 80, 352, 200); - AddImageTiled(313, 64, 100, 352, 1416); - AddImageTiled(415, 64, 76, 352, 200); - - for (var i = index; i < index + count && i >= 0 && i < list.Count; ++i) - { - var entry = list[i]; - - if (!CheckFilter(entry)) - { - continue; - } - - AddImageTiled(24, 94 + tableIndex * 32, canPrice ? 573 : 489, 2, 2624); - tableIndex += entry is BOBLargeEntry largeEntry ? largeEntry.Entries.Length : 1; - } - - AddAlphaRegion(18, 20, width - 17, 420); - AddImage(5, 5, 10460); - AddImage(width - 15, 5, 10460); - AddImage(5, 424, 10460); - AddImage(width - 15, 424, 10460); - - AddHtmlLocalized(canPrice ? 266 : 224, 32, 200, 32, 1062220, LabelColor); // Bulk Order Book - AddHtmlLocalized(63, 64, 200, 32, 1062213, LabelColor); // Type - AddHtmlLocalized(147, 64, 200, 32, 1062214, LabelColor); // Item - AddHtmlLocalized(246, 64, 200, 32, 1062215, LabelColor); // Quality - AddHtmlLocalized(336, 64, 200, 32, 1062216, LabelColor); // Material - AddHtmlLocalized(429, 64, 200, 32, 1062217, LabelColor); // Amount - - AddButton(35, 32, 4005, 4007, 1); - AddHtmlLocalized(70, 32, 200, 32, 1062476, LabelColor); // Set Filter - - var f = from.UseOwnFilter ? from.BOBFilter : book.Filter; - - if (f.IsDefault) - { - AddHtmlLocalized(canPrice ? 470 : 386, 32, 120, 32, 1062475, 16927); // Using No Filter - } - else if (from.UseOwnFilter) - { - AddHtmlLocalized(canPrice ? 470 : 386, 32, 120, 32, 1062451, 16927); // Using Your Filter - } - else - { - AddHtmlLocalized(canPrice ? 470 : 386, 32, 120, 32, 1062230, 16927); // Using Book Filter - } - - AddButton(375, 416, 4017, 4018, 0); - AddHtmlLocalized(410, 416, 120, 20, 1011441, LabelColor); // EXIT - - if (canDrop) - { - AddHtmlLocalized(26, 64, 50, 32, 1062212, LabelColor); // Drop - } - - if (canPrice) - { - AddHtmlLocalized(516, 64, 200, 32, 1062218, LabelColor); // Price - - if (canBuy) - { - AddHtmlLocalized(576, 64, 200, 32, 1062219, LabelColor); // Buy - } - else - { - AddHtmlLocalized(576, 64, 200, 32, 1062227, LabelColor); // Set - - AddButton(450, 416, 4005, 4007, 4); - AddHtml(485, 416, 120, 20, "Price all"); - } - } - - tableIndex = 0; - - if (page > 0) - { - AddButton(75, 416, 4014, 4016, 2); - AddHtmlLocalized(110, 416, 150, 20, 1011067, LabelColor); // Previous page - } - - if (GetIndexForPage(page + 1) < list.Count) - { - AddButton(225, 416, 4005, 4007, 3); - AddHtmlLocalized(260, 416, 150, 20, 1011066, LabelColor); // Next page - } - - for (var i = index; i < index + count && i >= 0 && i < list.Count; ++i) - { - var entry = list[i]; - - if (!CheckFilter(entry)) - { - continue; - } - - if (entry is BOBLargeEntry largeEntry) - { - var y = 96 + tableIndex * 32; - - if (canDrop) - { - AddButton(35, y + 2, 5602, 5606, 5 + i * 2); - } - - if (canDrop || canBuy && entry.Price > 0) - { - AddButton(579, y + 2, 2117, 2118, 6 + i * 2); - AddLabel(495, y, 1152, entry.Price.ToString()); - } - - AddHtmlLocalized(61, y, 50, 32, 1062225, LabelColor); // Large - - for (var j = 0; j < largeEntry.Entries.Length; ++j) - { - var sub = largeEntry.Entries[j]; - - AddHtmlLocalized(103, y, 130, 32, sub.Number, LabelColor); - - if (entry.RequireExceptional) - { - AddHtmlLocalized(235, y, 80, 20, 1060636, LabelColor); // exceptional - } - else - { - AddHtmlLocalized(235, y, 80, 20, 1011542, LabelColor); // normal - } - - var name = GetMaterialName(entry.Material, entry.DeedType, sub.ItemType); - - if (name.Number > 0) - { - AddHtmlLocalized(316, y, 100, 20, name, LabelColor); - } - else - { - AddLabel(316, y, 1152, name); - } - - AddLabel(421, y, 1152, $"{sub.AmountCur} / {entry.AmountMax}"); - - ++tableIndex; - y += 32; - } - } - else - { - var smallEntry = (BOBSmallEntry)entry; - - var y = 96 + tableIndex++ * 32; - - if (canDrop) - { - AddButton(35, y + 2, 5602, 5606, 5 + i * 2); - } - - if (canDrop || canBuy && smallEntry.Price > 0) - { - AddButton(579, y + 2, 2117, 2118, 6 + i * 2); - AddLabel(495, y, 1152, smallEntry.Price.ToString()); - } - - AddHtmlLocalized(61, y, 50, 32, 1062224, LabelColor); // Small - - AddHtmlLocalized(103, y, 130, 32, smallEntry.Number, LabelColor); - - if (smallEntry.RequireExceptional) - { - AddHtmlLocalized(235, y, 80, 20, 1060636, LabelColor); // exceptional - } - else - { - AddHtmlLocalized(235, y, 80, 20, 1011542, LabelColor); // normal - } - - var name = GetMaterialName(smallEntry.Material, smallEntry.DeedType, smallEntry.ItemType); - - if (name.Number > 0) - { - AddHtmlLocalized(316, y, 100, 20, name, LabelColor); - } - else - { - AddLabel(316, y, 1152, name); - } - - AddLabel(421, y, 1152, $"{smallEntry.AmountCur} / {smallEntry.AmountMax}"); - } - } - } - - public bool CheckFilter(IBOBEntry entry) - { - if (entry is BOBLargeEntry largeEntry) - { - return CheckFilter( - entry.Material, - entry.AmountMax, - true, - entry.RequireExceptional, - entry.DeedType, - largeEntry.Entries.Length > 0 ? largeEntry.Entries[0].ItemType : null - ); - } - - if (entry is BOBSmallEntry smallEntry) - { - return CheckFilter( - entry.Material, - entry.AmountMax, - false, - entry.RequireExceptional, - entry.DeedType, - smallEntry.ItemType - ); - } - - return false; - } - - public bool CheckFilter( - BulkMaterialType mat, int amountMax, bool isLarge, bool reqExc, BODType deedType, - Type itemType - ) - { - var f = _from.UseOwnFilter ? _from.BOBFilter : _book.Filter; - - if (f.IsDefault) - { - return true; - } - - if (f.Quality == 1 && reqExc) - { - return false; - } - - if (f.Quality == 2 && !reqExc) - { - return false; - } - - if (f.Quantity == 1 && amountMax != 10) - { - return false; - } - - if (f.Quantity == 2 && amountMax != 15) - { - return false; - } - - if (f.Quantity == 3 && amountMax != 20) - { - return false; - } - - if (f.Type == 1 && isLarge) - { - return false; - } - - if (f.Type == 2 && !isLarge) - { - return false; - } - - return f.Material switch - { - 1 => deedType == BODType.Smith, - 2 => deedType == BODType.Tailor, - 3 => mat == BulkMaterialType.None && BGTClassifier.Classify(deedType, itemType) == BulkGenericType.Iron, - 4 => mat == BulkMaterialType.DullCopper, - 5 => mat == BulkMaterialType.ShadowIron, - 6 => mat == BulkMaterialType.Copper, - 7 => mat == BulkMaterialType.Bronze, - 8 => mat == BulkMaterialType.Gold, - 9 => mat == BulkMaterialType.Agapite, - 10 => mat == BulkMaterialType.Verite, - 11 => mat == BulkMaterialType.Valorite, - 12 => mat == BulkMaterialType.None && BGTClassifier.Classify(deedType, itemType) == BulkGenericType.Cloth, - 13 => mat == BulkMaterialType.None && BGTClassifier.Classify(deedType, itemType) == BulkGenericType.Leather, - 14 => mat == BulkMaterialType.Spined, - 15 => mat == BulkMaterialType.Horned, - 16 => mat == BulkMaterialType.Barbed, - _ => true - }; - } - - public int GetIndexForPage(int page) - { - var index = 0; - - while (page-- > 0) - { - index += GetCountForIndex(index); - } - - return index; - } - - public int GetCountForIndex(int index) - { - var slots = 0; - var count = 0; - - var list = _list; - - for (var i = index; i >= 0 && i < list.Count; ++i) - { - var entry = list[i]; + var entry = Book.Entries[i]; if (CheckFilter(entry)) { - var add = entry is BOBLargeEntry largeEntry ? largeEntry.Entries.Length : 1; - - if (slots + add > 10) - { - break; - } - - slots += add; + List.Add(entry); } - - ++count; } - - return count; } - public int GetPageForIndex(int index, int sizeDropped) + var index = GetIndexForPage(_page); + var count = GetCountForIndex(index); + + var tableIndex = 0; + + var canDrop = Book.IsChildOf(_from.Backpack); + var pv = Book.RootParent as PlayerVendor; + var canBuy = pv != null; + var canPrice = canDrop || canBuy; + + if (canBuy) { - if (index <= 0) + var vi = pv.GetVendorItem(Book); + + canBuy = vi?.IsForSale == false; + } + + var width = 600; + + if (!canPrice) + { + width = 516; + } + + X = (624 - width) / 2; + + builder.AddPage(); + + builder.AddBackground(10, 10, width, 439, 5054); + builder.AddImageTiled(18, 20, width - 17, 420, 2624); + + if (canPrice) + { + builder.AddImageTiled(573, 64, 24, 352, 200); + builder.AddImageTiled(493, 64, 78, 352, 1416); + } + + if (canDrop) + { + builder.AddImageTiled(24, 64, 32, 352, 1416); + } + + builder.AddImageTiled(58, 64, 36, 352, 200); + builder.AddImageTiled(96, 64, 133, 352, 1416); + builder.AddImageTiled(231, 64, 80, 352, 200); + builder.AddImageTiled(313, 64, 100, 352, 1416); + builder.AddImageTiled(415, 64, 76, 352, 200); + + for (var i = index; i < index + count && i >= 0 && i < List.Count; ++i) + { + var entry = List[i]; + + if (!CheckFilter(entry)) { - return 0; + continue; } - var count = 0; - var page = 0; - int i; + builder.AddImageTiled(24, 94 + tableIndex * 32, canPrice ? 573 : 489, 2, 2624); + tableIndex += entry is BOBLargeEntry largeEntry ? largeEntry.Entries.Length : 1; + } - var list = _list; - for (i = 0; i < index && i < list.Count; i++) + builder.AddAlphaRegion(18, 20, width - 17, 420); + builder.AddImage(5, 5, 10460); + builder.AddImage(width - 15, 5, 10460); + builder.AddImage(5, 424, 10460); + builder.AddImage(width - 15, 424, 10460); + + builder.AddHtmlLocalized(canPrice ? 266 : 224, 32, 200, 32, 1062220, LabelColor); // Bulk Order Book + builder.AddHtmlLocalized(63, 64, 200, 32, 1062213, LabelColor); // Type + builder.AddHtmlLocalized(147, 64, 200, 32, 1062214, LabelColor); // Item + builder.AddHtmlLocalized(246, 64, 200, 32, 1062215, LabelColor); // Quality + builder.AddHtmlLocalized(336, 64, 200, 32, 1062216, LabelColor); // Material + builder.AddHtmlLocalized(429, 64, 200, 32, 1062217, LabelColor); // Amount + + builder.AddButton(35, 32, 4005, 4007, 1); + builder.AddHtmlLocalized(70, 32, 200, 32, 1062476, LabelColor); // Set Filter + + var f = _from.UseOwnFilter ? _from.BOBFilter : Book.Filter; + + if (f.IsDefault) + { + builder.AddHtmlLocalized(canPrice ? 470 : 386, 32, 120, 32, 1062475, 16927); // Using No Filter + } + else if (_from.UseOwnFilter) + { + builder.AddHtmlLocalized(canPrice ? 470 : 386, 32, 120, 32, 1062451, 16927); // Using Your Filter + } + else + { + builder.AddHtmlLocalized(canPrice ? 470 : 386, 32, 120, 32, 1062230, 16927); // Using Book Filter + } + + builder.AddButton(375, 416, 4017, 4018, 0); + builder.AddHtmlLocalized(410, 416, 120, 20, 1011441, LabelColor); // EXIT + + if (canDrop) + { + builder.AddHtmlLocalized(26, 64, 50, 32, 1062212, LabelColor); // Drop + } + + if (canPrice) + { + builder.AddHtmlLocalized(516, 64, 200, 32, 1062218, LabelColor); // Price + + if (canBuy) + { + builder.AddHtmlLocalized(576, 64, 200, 32, 1062219, LabelColor); // Buy + } + else + { + builder.AddHtmlLocalized(576, 64, 200, 32, 1062227, LabelColor); // Set + + builder.AddButton(450, 416, 4005, 4007, 4); + builder.AddHtml(485, 416, 120, 20, "Price all"); + } + } + + tableIndex = 0; + + if (_page > 0) + { + builder.AddButton(75, 416, 4014, 4016, 2); + builder.AddHtmlLocalized(110, 416, 150, 20, 1011067, LabelColor); // Previous page + } + + if (GetIndexForPage(_page + 1) < List.Count) + { + builder.AddButton(225, 416, 4005, 4007, 3); + builder.AddHtmlLocalized(260, 416, 150, 20, 1011066, LabelColor); // Next page + } + + for (var i = index; i < index + count && i >= 0 && i < List.Count; ++i) + { + var entry = List[i]; + + if (!CheckFilter(entry)) + { + continue; + } + + if (entry is BOBLargeEntry largeEntry) + { + var y = 96 + tableIndex * 32; + + if (canDrop) + { + builder.AddButton(35, y + 2, 5602, 5606, 5 + i * 2); + } + + if (canDrop || canBuy && entry.Price > 0) + { + builder.AddButton(579, y + 2, 2117, 2118, 6 + i * 2); + builder.AddLabel(495, y, 1152, entry.Price.ToString()); + } + + builder.AddHtmlLocalized(61, y, 50, 32, 1062225, LabelColor); // Large + + for (var j = 0; j < largeEntry.Entries.Length; ++j) + { + var sub = largeEntry.Entries[j]; + + builder.AddHtmlLocalized(103, y, 130, 32, sub.Number, LabelColor); + + builder.AddHtmlLocalized( + 235, + y, + 80, + 20, + entry.RequireExceptional + ? 1060636 // exceptional + : 1011542, // normal + LabelColor + ); + + var name = GetMaterialName(entry.Material, entry.DeedType, sub.ItemType); + + name.AddHtmlText( + ref builder, + 316, + y, + 100, + 20, + false, + false, + 1152, + LabelColor + ); + + builder.AddLabel(421, y, 1152, $"{sub.AmountCur} / {entry.AmountMax}"); + + ++tableIndex; + y += 32; + } + } + else + { + var smallEntry = (BOBSmallEntry)entry; + + var y = 96 + tableIndex++ * 32; + + if (canDrop) + { + builder.AddButton(35, y + 2, 5602, 5606, 5 + i * 2); + } + + if (canDrop || canBuy && smallEntry.Price > 0) + { + builder.AddButton(579, y + 2, 2117, 2118, 6 + i * 2); + builder.AddLabel(495, y, 1152, $"{smallEntry.Price}"); + } + + builder.AddHtmlLocalized(61, y, 50, 32, 1062224, LabelColor); // Small + + builder.AddHtmlLocalized(103, y, 130, 32, smallEntry.Number, LabelColor); + + builder.AddHtmlLocalized( + 235, + y, + 80, + 20, + smallEntry.RequireExceptional + ? 1060636 // exceptional + : 1011542, // normal + LabelColor + ); + + var name = GetMaterialName(smallEntry.Material, smallEntry.DeedType, smallEntry.ItemType); + + name.AddHtmlText( + ref builder, + 316, + y, + 100, + 20, + false, + false, + 1152, + LabelColor + ); + + builder.AddLabel(421, y, 1152, $"{smallEntry.AmountCur} / {smallEntry.AmountMax}"); + } + } + } + + public bool CheckFilter(IBOBEntry entry) + { + if (entry is BOBLargeEntry largeEntry) + { + return CheckFilter( + entry.Material, + entry.AmountMax, + true, + entry.RequireExceptional, + entry.DeedType, + largeEntry.Entries.Length > 0 ? largeEntry.Entries[0].ItemType : null + ); + } + + if (entry is BOBSmallEntry smallEntry) + { + return CheckFilter( + entry.Material, + entry.AmountMax, + false, + entry.RequireExceptional, + entry.DeedType, + smallEntry.ItemType + ); + } + + return false; + } + + public bool CheckFilter( + BulkMaterialType mat, int amountMax, bool isLarge, bool reqExc, BODType deedType, + Type itemType + ) + { + var f = _from.UseOwnFilter ? _from.BOBFilter : Book.Filter; + + if (f.IsDefault) + { + return true; + } + + if (f.Quality == 1 && reqExc) + { + return false; + } + + if (f.Quality == 2 && !reqExc) + { + return false; + } + + if (f.Quantity == 1 && amountMax != 10) + { + return false; + } + + if (f.Quantity == 2 && amountMax != 15) + { + return false; + } + + if (f.Quantity == 3 && amountMax != 20) + { + return false; + } + + if (f.Type == 1 && isLarge) + { + return false; + } + + if (f.Type == 2 && !isLarge) + { + return false; + } + + return f.Material switch + { + 1 => deedType == BODType.Smith, + 2 => deedType == BODType.Tailor, + 3 => mat == BulkMaterialType.None && BGTClassifier.Classify(deedType, itemType) == BulkGenericType.Iron, + 4 => mat == BulkMaterialType.DullCopper, + 5 => mat == BulkMaterialType.ShadowIron, + 6 => mat == BulkMaterialType.Copper, + 7 => mat == BulkMaterialType.Bronze, + 8 => mat == BulkMaterialType.Gold, + 9 => mat == BulkMaterialType.Agapite, + 10 => mat == BulkMaterialType.Verite, + 11 => mat == BulkMaterialType.Valorite, + 12 => mat == BulkMaterialType.None && BGTClassifier.Classify(deedType, itemType) == BulkGenericType.Cloth, + 13 => mat == BulkMaterialType.None && BGTClassifier.Classify(deedType, itemType) == BulkGenericType.Leather, + 14 => mat == BulkMaterialType.Spined, + 15 => mat == BulkMaterialType.Horned, + 16 => mat == BulkMaterialType.Barbed, + _ => true + }; + } + + public int GetIndexForPage(int page) + { + var index = 0; + + while (page-- > 0) + { + index += GetCountForIndex(index); + } + + return index; + } + + public int GetCountForIndex(int index) + { + var slots = 0; + var count = 0; + + var list = List; + + for (var i = index; i >= 0 && i < list.Count; ++i) + { + var entry = list[i]; + + if (CheckFilter(entry)) + { + var add = entry is BOBLargeEntry largeEntry ? largeEntry.Entries.Length : 1; + + if (slots + add > 10) + { + break; + } + + slots += add; + } + + ++count; + } + + return count; + } + + public int GetPageForIndex(int index, int sizeDropped) + { + if (index <= 0) + { + return 0; + } + + var count = 0; + var page = 0; + int i; + + var list = List; + for (i = 0; i < index && i < list.Count; i++) + { + var entry = list[i]; + if (!CheckFilter(entry)) + { + continue; + } + + var add = entry is BOBLargeEntry largeEntry ? largeEntry.Entries.Length : 1; + count += add; + if (count > 10) + { + page++; + count = add; + } + } + + /* now we are on the page of the bod preceding the dropped one. + * next step: checking whether we have to remain where we are. + * The counter i needs to be incremented as the bod to this very moment + * has not yet been removed from m_List */ + i++; + + /* if, for instance, a big bod of size 6 has been removed, smaller bods + * might fall back into this page. Depending on their sizes, the page needs + * to be adjusted accordingly. This is done now. + */ + if (count + sizeDropped > 10) + { + while (i < list.Count && count <= 10) { var entry = list[i]; - if (!CheckFilter(entry)) + if (CheckFilter(entry)) { - continue; + count += entry is BOBLargeEntry largeEntry ? largeEntry.Entries.Length : 1; } - var add = entry is BOBLargeEntry largeEntry ? largeEntry.Entries.Length : 1; - count += add; - if (count > 10) - { - page++; - count = add; - } + i++; } - /* now we are on the page of the bod preceding the dropped one. - * next step: checking whether we have to remain where we are. - * The counter i needs to be incremented as the bod to this very moment - * has not yet been removed from m_List */ - i++; - - /* if, for instance, a big bod of size 6 has been removed, smaller bods - * might fall back into this page. Depending on their sizes, the page needs - * to be adjusted accordingly. This is done now. - */ - if (count + sizeDropped > 10) + if (count > 10) { - while (i < list.Count && count <= 10) - { - var entry = list[i]; - if (CheckFilter(entry)) - { - count += entry is BOBLargeEntry largeEntry ? largeEntry.Entries.Length : 1; - } - - i++; - } - - if (count > 10) - { - page++; - } + page++; } - - return page; } - public TextDefinition GetMaterialName(BulkMaterialType mat, BODType type, Type itemType) + return page; + } + + public static TextDefinition GetMaterialName(BulkMaterialType mat, BODType type, Type itemType) => + type switch { - switch (type) + BODType.Smith => mat switch { - case BODType.Smith: - { - switch (mat) - { - case BulkMaterialType.None: return 1062226; - case BulkMaterialType.DullCopper: return 1018332; - case BulkMaterialType.ShadowIron: return 1018333; - case BulkMaterialType.Copper: return 1018334; - case BulkMaterialType.Bronze: return 1018335; - case BulkMaterialType.Gold: return 1018336; - case BulkMaterialType.Agapite: return 1018337; - case BulkMaterialType.Verite: return 1018338; - case BulkMaterialType.Valorite: return 1018339; - } - - break; - } - case BODType.Tailor: - { - switch (mat) - { - case BulkMaterialType.None: - { - if (itemType.IsSubclassOf(typeof(BaseArmor)) || itemType.IsSubclassOf(typeof(BaseShoes))) - { - return 1062235; - } - - return 1044286; - } - case BulkMaterialType.Spined: return 1062236; - case BulkMaterialType.Horned: return 1062237; - case BulkMaterialType.Barbed: return 1062238; - } - - break; - } - } - - return "Invalid"; - } - - public override void SendTo(NetState ns) - { - ns.CloseGump(); - - base.SendTo(ns); - } - - public override void OnResponse(NetState sender, in RelayInfo info) - { - var index = info.ButtonID; - - switch (index) + BulkMaterialType.None => 1062226, + BulkMaterialType.DullCopper => 1018332, + BulkMaterialType.ShadowIron => 1018333, + BulkMaterialType.Copper => 1018334, + BulkMaterialType.Bronze => 1018335, + BulkMaterialType.Gold => 1018336, + BulkMaterialType.Agapite => 1018337, + BulkMaterialType.Verite => 1018338, + BulkMaterialType.Valorite => 1018339, + _ => 1062226 + }, + BODType.Tailor => mat switch { - case 0: // EXIT + BulkMaterialType.Spined => 1062236, + BulkMaterialType.Horned => 1062237, + BulkMaterialType.Barbed => 1062238, + _ when itemType.IsSubclassOf(typeof(BaseArmor)) || + itemType.IsSubclassOf(typeof(BaseShoes)) => 1062235, + _ => 1044286 + }, + _ => TextDefinition.Empty + }; + + public override void SendTo(NetState ns) + { + ns.CloseGump(); + + base.SendTo(ns); + } + + public override void OnResponse(NetState sender, in RelayInfo info) + { + var index = info.ButtonID; + + switch (index) + { + case 0: // EXIT + { + break; + } + case 1: // Set Filter + { + _from.SendGump(new BOBFilterGump(_from, Book)); + + break; + } + case 2: // Previous page + { + if (_page > 0) + { + _page--; + _from.SendGump(this); + } + + return; + } + case 3: // Next page + { + if (GetIndexForPage(_page + 1) < List.Count) + { + _page++; + _from.SendGump(this); + } + + break; + } + case 4: // Price all + { + if (Book.IsChildOf(_from.Backpack)) + { + _from.Prompt = new SetPricePrompt(this, null); + _from.SendMessage("Type in a price for all deeds in the book:"); + } + + break; + } + default: + { + index -= 5; + + var type = index % 2; + index /= 2; + + if (index < 0 || index >= List.Count) { break; } - case 1: // Set Filter - { - _from.SendGump(new BOBFilterGump(_from, _book)); + var bobEntry = List[index]; + + if (!Book.Entries.Contains(bobEntry)) + { + _from.SendLocalizedMessage(1062382); // The deed selected is not available. break; } - case 2: // Previous page + + if (Book.IsChildOf(_from.Backpack)) { - if (_page > 0) - { - _from.SendGump(new BOBGump(_from, _book, _page - 1, _list)); - } - - return; - } - case 3: // Next page - { - if (GetIndexForPage(_page + 1) < _list.Count) - { - _from.SendGump(new BOBGump(_from, _book, _page + 1, _list)); - } - - break; - } - case 4: // Price all - { - if (_book.IsChildOf(_from.Backpack)) - { - _from.Prompt = new SetPricePrompt(_book, null, _page, _list); - _from.SendMessage("Type in a price for all deeds in the book:"); - } - - break; - } - default: - { - index -= 5; - - var type = index % 2; - index /= 2; - - if (index < 0 || index >= _list.Count) - { - break; - } - - var bobEntry = _list[index]; - - if (!_book.Entries.Contains(bobEntry)) - { - _from.SendLocalizedMessage(1062382); // The deed selected is not available. - break; - } - if (type == 0) // Drop { - if (_book.IsChildOf(_from.Backpack)) - { - var item = bobEntry.Reconstruct(); + var item = bobEntry.Reconstruct(); - var pack = _from.Backpack; - if (pack?.CheckHold( + var pack = _from.Backpack; + if (pack?.CheckHold( _from, item, true, @@ -610,147 +605,126 @@ namespace Server.Engines.BulkOrders 0, item.PileWeight + item.TotalWeight ) != true) + { + _from.SendLocalizedMessage(503204); // You do not have room in your backpack for this + ResetList(); + _from.SendGump(this); + } + else + { + var sizeOfDroppedBod = bobEntry is BOBLargeEntry entry ? entry.Entries.Length : 1; + + _from.AddToBackpack(item); + + // The bulk order deed has been placed in your backpack. + _from.SendLocalizedMessage(1045152); + + Book.RemoveEntry(bobEntry); + + if (Book.Entries.Count / 5 < Book.ItemCount) { - _from.SendLocalizedMessage(503204); // You do not have room in your backpack for this - _from.SendGump(new BOBGump(_from, _book, _page)); + Book.ItemCount--; + Book.InvalidateItems(); + } + + if (Book.Entries.Count > 0) + { + _page = GetPageForIndex(index, sizeOfDroppedBod); + ResetList(); + _from.SendGump(this); } else { - if (_book.IsChildOf(_from.Backpack)) - { - var sizeOfDroppedBod = bobEntry is BOBLargeEntry entry ? entry.Entries.Length : 1; - - _from.AddToBackpack(item); - - // The bulk order deed has been placed in your backpack. - _from.SendLocalizedMessage(1045152); - - _book.Entries.Remove(bobEntry); - _book.InvalidateProperties(); - - if (_book.Entries.Count / 5 < _book.ItemCount) - { - _book.ItemCount--; - _book.InvalidateItems(); - } - - if (_book.Entries.Count > 0) - { - _page = GetPageForIndex(index, sizeOfDroppedBod); - _from.SendGump(new BOBGump(_from, _book, _page)); - } - else - { - _from.SendLocalizedMessage(1062381); // The book is empty. - } - } + _from.SendLocalizedMessage(1062381); // The book is empty. } } } else // Set Price | Buy { - if (_book.IsChildOf(_from.Backpack)) - { - _from.Prompt = new SetPricePrompt(_book, bobEntry, _page, _list); - _from.SendLocalizedMessage(1062383); // Type in a price for the deed: - } - else if (_book.RootParent is PlayerVendor pv) - { - var vi = pv.GetVendorItem(_book); + _from.Prompt = new SetPricePrompt(this, bobEntry); + _from.SendLocalizedMessage(1062383); // Type in a price for the deed: + } + } + else if (Book.RootParent is PlayerVendor pv) + { + var vi = pv.GetVendorItem(Book); - if (vi?.IsForSale != false) - { - return; - } - - var sizeOfDroppedBod = bobEntry is BOBLargeEntry largeEntry ? largeEntry.Entries.Length : 1; - var price = bobEntry.Price; - - if (price == 0) - { - _from.SendLocalizedMessage(1062382); // The deed selected is not available. - } - else - { - if (_book.Entries.Count > 0) - { - _page = GetPageForIndex(index, sizeOfDroppedBod); - _from.SendGump(new BODBuyGump(_from, _book, bobEntry, _page, price)); - } - else - { - _from.SendLocalizedMessage(1062381); // The book is emptz - } - } - } + if (vi?.IsForSale != false) + { + return; } - break; + var sizeOfDroppedBod = bobEntry is BOBLargeEntry largeEntry ? largeEntry.Entries.Length : 1; + var price = bobEntry.Price; + + if (price == 0) + { + _from.SendLocalizedMessage(1062382); // The deed selected is not available. + } + else if (Book.Entries.Count > 0) + { + _page = GetPageForIndex(index, sizeOfDroppedBod); + _from.SendGump(new BODBuyGump(this, bobEntry, price)); + } + else + { + _from.SendLocalizedMessage(1062381); // The book is empty + } } - } + + break; + } + } + } + + private class SetPricePrompt : Prompt + { + private readonly BOBGump _gump; + private readonly IBOBEntry _entry; + + public SetPricePrompt(BOBGump gump, IBOBEntry entry) + { + _gump = gump; + _entry = entry; } - private class SetPricePrompt : Prompt + public override void OnResponse(Mobile from, string text) { - private readonly BulkOrderBook m_Book; - private readonly IBOBEntry m_Entry; - private readonly List m_List; - private readonly int m_Page; - - public SetPricePrompt(BulkOrderBook book, IBOBEntry entry, int page, List list) + if (_entry != null && !_gump.Book.Entries.Contains(_entry)) { - m_Book = book; - m_Entry = entry; - m_Page = page; - m_List = list; + from.SendLocalizedMessage(1062382); // The deed selected is not available. + return; } - public override void OnResponse(Mobile from, string text) + var price = Utility.ToInt32(text); + + if (price is < 0 or > 250000000) { - if (m_Entry != null && !m_Book.Entries.Contains(m_Entry)) - { - from.SendLocalizedMessage(1062382); // The deed selected is not available. - return; - } + from.SendLocalizedMessage(1062390); // The price you requested is outrageous! + return; + } - var price = Utility.ToInt32(text); + if (_entry == null) + { + for (var i = 0; i < _gump.List.Count; ++i) + { + var entry = _gump.List[i]; - if (price is < 0 or > 250000000) - { - from.SendLocalizedMessage(1062390); // The price you requested is outrageous! - } - else if (m_Entry == null) - { - for (var i = 0; i < m_List.Count; ++i) + if (!_gump.Book.Entries.Contains(entry)) { - var entry = m_List[i]; - - if (!m_Book.Entries.Contains(entry)) - { - continue; - } - - entry.Price = price; + continue; } - // Deed price set. - from.SendLocalizedMessage(1062384); - - if (from is PlayerMobile mobile) - { - mobile.SendGump(new BOBGump(mobile, m_Book, m_Page, m_List)); - } - } - else - { - m_Entry.Price = price; - from.SendLocalizedMessage(1062384); // Deed price set. - if (from is PlayerMobile mobile) - { - mobile.SendGump(new BOBGump(mobile, m_Book, m_Page, m_List)); - } + entry.Price = price; } } + else + { + _entry.Price = price; + } + + from.SendLocalizedMessage(1062384); // Deed price set. + from.SendGump(_gump); } } } diff --git a/Projects/UOContent/Engines/Bulk Orders/Books/BODBuyGump.cs b/Projects/UOContent/Engines/Bulk Orders/Books/BODBuyGump.cs index 06ab0f72d..7f1120b8f 100644 --- a/Projects/UOContent/Engines/Bulk Orders/Books/BODBuyGump.cs +++ b/Projects/UOContent/Engines/Bulk Orders/Books/BODBuyGump.cs @@ -3,135 +3,142 @@ using Server.Items; using Server.Mobiles; using Server.Network; -namespace Server.Engines.BulkOrders +namespace Server.Engines.BulkOrders; + +public class BODBuyGump : StaticGump { - public class BODBuyGump : Gump + private BOBGump _gump; + private readonly IBOBEntry _entry; + private readonly int _price; + + public BODBuyGump(BOBGump gump, IBOBEntry entry, int price) : base(100, 200) { - private readonly BulkOrderBook m_Book; - private readonly IBOBEntry m_Entry; - private readonly PlayerMobile m_From; - private readonly int m_Page; - private readonly int m_Price; + _gump = gump; + _entry = entry; + _price = price; + } - public BODBuyGump(PlayerMobile from, BulkOrderBook book, IBOBEntry entry, int page, int price) : base(100, 200) + protected override void BuildLayout(ref StaticGumpBuilder builder) + { + builder.AddPage(); + + builder.AddBackground(100, 10, 300, 150, 5054); + + builder.AddHtmlLocalized(125, 20, 250, 24, 1019070); // You have agreed to purchase: + builder.AddHtmlLocalized(125, 45, 250, 24, 1045151); // a bulk order deed + + builder.AddHtmlLocalized(125, 70, 250, 24, 1019071); // for the amount of: + builder.AddLabelPlaceholder(125, 95, 0, "price"); + + builder.AddButton(250, 130, 4005, 4007, 1); + builder.AddHtmlLocalized(282, 130, 100, 24, 1011012); // CANCEL + + builder.AddButton(120, 130, 4005, 4007, 2); + builder.AddHtmlLocalized(152, 130, 100, 24, 1011036); // OKAY + } + + protected override void BuildStrings(ref GumpStringsBuilder builder) + { + builder.SetStringSlot("price", $"{_price:N0}"); + } + + public override void OnResponse(NetState sender, in RelayInfo info) + { + if (sender.Mobile is not PlayerMobile pm) { - m_From = from; - m_Book = book; - m_Entry = entry; - m_Price = price; - m_Page = page; - - AddPage(0); - - AddBackground(100, 10, 300, 150, 5054); - - AddHtmlLocalized(125, 20, 250, 24, 1019070); // You have agreed to purchase: - AddHtmlLocalized(125, 45, 250, 24, 1045151); // a bulk order deed - - AddHtmlLocalized(125, 70, 250, 24, 1019071); // for the amount of: - AddLabel(125, 95, 0, price.ToString()); - - AddButton(250, 130, 4005, 4007, 1); - AddHtmlLocalized(282, 130, 100, 24, 1011012); // CANCEL - - AddButton(120, 130, 4005, 4007, 2); - AddHtmlLocalized(152, 130, 100, 24, 1011036); // OKAY + return; } - public override void OnResponse(NetState sender, in RelayInfo info) + if (info.ButtonID != 2) { - if (info.ButtonID != 2) - { - m_From.SendLocalizedMessage(503207); // Cancelled purchase. - return; - } + pm.SendLocalizedMessage(503207); // Cancelled purchase. + return; + } - if (m_Book.RootParent is not PlayerVendor pv) - { - m_From.SendLocalizedMessage(1062382); // The deed selected is not available. - return; - } + var book = _gump.Book; - if (!m_Book.Entries.Contains(m_Entry)) - { - pv.SayTo(m_From, 1062382); // The deed selected is not available. - return; - } + if (book.RootParent is not PlayerVendor pv) + { + pm.SendLocalizedMessage(1062382); // The deed selected is not available. + return; + } - var price = 0; + if (!book.Entries.Contains(_entry)) + { + pv.SayTo(pm, 1062382); // The deed selected is not available. + return; + } - if (pv.GetVendorItem(m_Book)?.IsForSale == false) - { - price = m_Entry.Price; - } + var price = 0; - if (price != m_Price) - { - pv.SayTo( - m_From, - "The price has been been changed. If you like, you may offer to purchase the item again." - ); - return; - } + if (pv.GetVendorItem(book)?.IsForSale == false) + { + price = _entry.Price; + } - if (price == 0) - { - pv.SayTo(m_From, 1062382); // The deed selected is not available. - return; - } + if (price != _price) + { + pv.SayTo( + pm, + "The price has been been changed. If you like, you may offer to purchase the item again." + ); + return; + } - var item = m_Entry.Reconstruct(); + if (price == 0) + { + pv.SayTo(pm, 1062382); // The deed selected is not available. + return; + } - pv.Say(m_From.Name); + var item = _entry.Reconstruct(); - var pack = m_From.Backpack; + pv.Say(pm.Name); - if (pack?.CheckHold( - m_From, + var pack = pm.Backpack; + + if (pack?.CheckHold( + pm, item, true, true, 0, item.PileWeight + item.TotalWeight ) != true) + { + pv.SayTo(pm, 503204); // You do not have room in your backpack for this + pm.SendGump(_gump); + item.Delete(); + } + else if (pack.ConsumeTotal(typeof(Gold), price) || Banker.Withdraw(pm, price)) + { + book.RemoveEntry(_entry); + pv.HoldGold += price; + pm.AddToBackpack(item); + + // The bulk order deed has been placed in your backpack. + pm.SendLocalizedMessage(1045152); + + if (book.Entries.Count / 5 < book.ItemCount) { - pv.SayTo(m_From, 503204); // You do not have room in your backpack for this - m_From.SendGump(new BOBGump(m_From, m_Book, m_Page)); - item.Delete(); + book.ItemCount--; + book.InvalidateItems(); + } + + if (book.Entries.Count > 0) + { + _gump.ResetList(); + pm.SendGump(_gump); } else { - if (pack.ConsumeTotal(typeof(Gold), price) || Banker.Withdraw(m_From, price)) - { - m_Book.RemoveEntry(m_Entry); - m_Book.InvalidateProperties(); - pv.HoldGold += price; - m_From.AddToBackpack(item); - - // The bulk order deed has been placed in your backpack. - m_From.SendLocalizedMessage(1045152); - - if (m_Book.Entries.Count / 5 < m_Book.ItemCount) - { - m_Book.ItemCount--; - m_Book.InvalidateItems(); - } - - if (m_Book.Entries.Count > 0) - { - m_From.SendGump(new BOBGump(m_From, m_Book, m_Page)); - } - else - { - m_From.SendLocalizedMessage(1062381); // The book is empty. - } - } - else - { - pv.SayTo(m_From, 503205); // You cannot afford this item. - item.Delete(); - } + pm.SendLocalizedMessage(1062381); // The book is empty. } } + else + { + pv.SayTo(pm, 503205); // You cannot afford this item. + item.Delete(); + } } } diff --git a/Projects/UOContent/Engines/Bulk Orders/Books/BulkOrderBook.cs b/Projects/UOContent/Engines/Bulk Orders/Books/BulkOrderBook.cs index 4e67ca620..a0c17c575 100644 --- a/Projects/UOContent/Engines/Bulk Orders/Books/BulkOrderBook.cs +++ b/Projects/UOContent/Engines/Bulk Orders/Books/BulkOrderBook.cs @@ -40,7 +40,6 @@ public partial class BulkOrderBook : Item, ISecurable [Constructible] public BulkOrderBook() : base(0x2259) { - Weight = 1.0; LootType = LootType.Blessed; _entries = []; @@ -49,6 +48,8 @@ public partial class BulkOrderBook : Item, ISecurable _level = SecureLevel.CoOwners; } + public override double DefaultWeight => 1.0; + public override void OnAfterDuped(Item newItem) { if (newItem is not BulkOrderBook book) diff --git a/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs b/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs index 1aca46e31..032ece7f0 100755 --- a/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs +++ b/Projects/UOContent/Engines/CannedEvil/ChampionSpawn.cs @@ -1364,6 +1364,8 @@ public class ChampionSpawnRegion : BaseRegion global = Math.Max(global, 1 + Spawn.Level); //This is a guesstimate. TODO: Verify & get exact values // OSI testing: at 2 red skulls, light = 0x3 ; 1 red = 0x3.; 3 = 8; 9 = 0xD 8 = 0xD 12 = 0x12 10 = 0xD } + private static readonly HashSet _addresses = []; + public override void OnEnter(Mobile m) { if (!m.Player || m.AccessLevel != AccessLevel.Player || Spawn.Active) @@ -1371,8 +1373,6 @@ public class ChampionSpawnRegion : BaseRegion return; } - Region parent = Parent ?? this; - if (Spawn.ReadyToActivate) { Spawn.Start(); @@ -1384,27 +1384,29 @@ public class ChampionSpawnRegion : BaseRegion return; } - List players = parent.GetPlayers(); - List addresses = new List(); + using var players = (Parent ?? this).GetPlayersPooled(); + for (var i = 0; i < players.Count; i++) { - if (players[i].AccessLevel == AccessLevel.Player && players[i].NetState != null && - !addresses.Contains(players[i].NetState.Address) && !((PlayerMobile)players[i]).Young) + var player = players[i]; + if (player.AccessLevel == AccessLevel.Player && player.NetState != null && !((PlayerMobile)player).Young) { - addresses.Add(players[i].NetState.Address); + _addresses.Add(player.NetState.Address); } } - if (addresses.Count >= 15) + if (_addresses.Count >= 15) { - foreach (Mobile player in players) + for (var i = 0; i < players.Count; i++) { - player.SendMessage(0x20, Spawn.BroadcastMessage); + players[i].SendMessage(0x20, Spawn.BroadcastMessage); } Spawn.ActivatedByProximity = true; Spawn.BeginRestart(TimeSpan.FromMinutes(5.0)); } + + _addresses.Clear(); } public override bool OnMoveInto(Mobile m, Direction d, Point3D newLocation, Point3D oldLocation) diff --git a/Projects/UOContent/Engines/CannedEvil/GenChamps.cs b/Projects/UOContent/Engines/CannedEvil/GenChamps.cs index 22a7f03a0..3d94a9fc3 100644 --- a/Projects/UOContent/Engines/CannedEvil/GenChamps.cs +++ b/Projects/UOContent/Engines/CannedEvil/GenChamps.cs @@ -14,7 +14,7 @@ *************************************************************************/ using System; -using System.Collections.Generic; +using Server.Collections; using Server.Logging; namespace Server.Engines.CannedEvil; @@ -67,18 +67,18 @@ public static class ChampionGenerator */ //We assume that all champion spawns are generated here. - List spawns = []; + using var spawns = PooledRefQueue.Create(); foreach (Item item in World.Items.Values) { if (item is ChampionSpawn spawn) { - spawns.Add(spawn); + spawns.Enqueue(spawn); } } - for (int i = spawns.Count - 1; i >= 0; i--) + while (spawns.Count > 0) { - spawns[i].Delete(); + spawns.Dequeue().Delete(); } Process(DungeonLocations); diff --git a/Projects/UOContent/Engines/ConPVP/Gumps/AcceptTeamGump.cs b/Projects/UOContent/Engines/ConPVP/Gumps/AcceptTeamGump.cs index d7a93a578..f8b7af812 100644 --- a/Projects/UOContent/Engines/ConPVP/Gumps/AcceptTeamGump.cs +++ b/Projects/UOContent/Engines/ConPVP/Gumps/AcceptTeamGump.cs @@ -5,6 +5,7 @@ using System.Text; using Server.Gumps; using Server.Mobiles; using Server.Network; +using Server.Text; namespace Server.Engines.ConPVP { @@ -82,7 +83,7 @@ namespace Server.Engines.ConPVP AddImage(215, -43, 0xEE40); - var sb = new StringBuilder(); + using var sb = ValueStringBuilder.Create(128); if (tourney.TourneyType == TourneyType.FreeForAll) { @@ -117,12 +118,13 @@ namespace Server.Engines.ConPVP if (tourney.EventController != null) { - sb.Append(' ').Append(tourney.EventController.Title); + sb.Append(' '); + sb.Append(tourney.EventController.Title); } sb.Append(" Tournament Invitation"); - AddBorderedText(22, 22, 294, 20, sb.ToString().Center(), LabelColor32, BlackColor32); + AddBorderedText(22, 22, 294, 20, sb.AsSpan().Center(), LabelColor32, BlackColor32); AddBorderedText( 22, diff --git a/Projects/UOContent/Engines/ConPVP/Gumps/ArenaGump.cs b/Projects/UOContent/Engines/ConPVP/Gumps/ArenaGump.cs index 67f8a0047..98b95964d 100644 --- a/Projects/UOContent/Engines/ConPVP/Gumps/ArenaGump.cs +++ b/Projects/UOContent/Engines/ConPVP/Gumps/ArenaGump.cs @@ -189,7 +189,7 @@ namespace Server.Engines.ConPVP AddBorderedText(x + 5, y + 5, 325 - 5, sb.ToString(), color, 0); x += 325; - AddBorderedText(x, y + 5, 40, ar.Spectators.ToString().Center(), color, 0); + AddBorderedText(x, y + 5, 40, Html.Center($"{ar.Spectators}"), color, 0); } } diff --git a/Projects/UOContent/Engines/ConPVP/Gumps/ConfirmSignupGump.cs b/Projects/UOContent/Engines/ConPVP/Gumps/ConfirmSignupGump.cs index 376042f72..9e6c45622 100644 --- a/Projects/UOContent/Engines/ConPVP/Gumps/ConfirmSignupGump.cs +++ b/Projects/UOContent/Engines/ConPVP/Gumps/ConfirmSignupGump.cs @@ -7,6 +7,7 @@ using Server.Gumps; using Server.Mobiles; using Server.Network; using Server.Targeting; +using Server.Text; namespace Server.Engines.ConPVP { @@ -92,7 +93,7 @@ namespace Server.Engines.ConPVP AddImage(215, -43, 0xEE40); // AddImage( 330, 141, 0x8BA ); - var sb = new StringBuilder(); + using var sb = ValueStringBuilder.Create(128); if (tourney.TourneyType == TourneyType.FreeForAll) { @@ -127,12 +128,13 @@ namespace Server.Engines.ConPVP if (tourney.EventController != null) { - sb.Append(' ').Append(tourney.EventController.Title); + sb.Append(' '); + sb.Append(tourney.EventController.Title); } sb.Append(" Tournament Signup"); - AddBorderedText(22, 22, 294, 20, sb.ToString().Center(), LabelColor32, BlackColor32); + AddBorderedText(22, 22, 294, 20, sb.AsSpan().Center(), LabelColor32, BlackColor32); AddBorderedText( 22, 50, diff --git a/Projects/UOContent/Engines/ConPVP/Gumps/LadderGump.cs b/Projects/UOContent/Engines/ConPVP/Gumps/LadderGump.cs index 088d26789..db6ed89d0 100644 --- a/Projects/UOContent/Engines/ConPVP/Gumps/LadderGump.cs +++ b/Projects/UOContent/Engines/ConPVP/Gumps/LadderGump.cs @@ -125,7 +125,7 @@ namespace Server.Engines.ConPVP height - 12 - 2 - 18, 400, 20, - $"Top {lc} of {m_List.Count:N0} duelists, page {page + 1} of {(lc + 14) / 15}".Color(0xFFC000) + Html.Color($"Top {lc} of {m_List.Count:N0} duelists, page {page + 1} of {(lc + 14) / 15}", 0xFFC000) ); AddColumnHeader(75, "Rank"); @@ -174,7 +174,7 @@ namespace Server.Engines.ConPVP // AddImageTiled( 21, y + 6, width, 8, 0x2617 ); AddImageTiled(x + 3, y + 4, width, 11, 0x806); - AddBorderedText(x, y, 115, level.ToString().Center(), 0xFFFFFF, 0); + AddBorderedText(x, y, 115, Html.Center($"{level}"), 0xFFFFFF, 0); x += 115; var mob = entry.Mobile; @@ -189,10 +189,10 @@ namespace Server.Engines.ConPVP AddBorderedText(x + 5, y, 115 - 5, mob.Name, 0xFFFFFF, 0); x += 115; - AddBorderedText(x, y, 60, entry.Wins.ToString().Center(), 0xFFFFFF, 0); + AddBorderedText(x, y, 60, Html.Center($"{entry.Wins}"), 0xFFFFFF, 0); x += 60; - AddBorderedText(x, y, 60, entry.Losses.ToString().Center(), 0xFFFFFF, 0); + AddBorderedText(x, y, 60, Html.Center($"{entry.Losses}"), 0xFFFFFF, 0); x += 60; // AddBorderedText( 292 + 15, y, 115 - 30, String.Format( "{0}
/
{1}
", entry.Wins, entry.Losses ), 0xFFC000, 0 ); diff --git a/Projects/UOContent/Engines/ConPVP/Gumps/TournamentBracketGump.cs b/Projects/UOContent/Engines/ConPVP/Gumps/TournamentBracketGump.cs index ae7d3daac..436832d72 100644 --- a/Projects/UOContent/Engines/ConPVP/Gumps/TournamentBracketGump.cs +++ b/Projects/UOContent/Engines/ConPVP/Gumps/TournamentBracketGump.cs @@ -5,6 +5,7 @@ using System.Text; using Server.Gumps; using Server.Mobiles; using Server.Network; +using Server.Text; namespace Server.Engines.ConPVP { @@ -51,7 +52,7 @@ namespace Server.Engines.ConPVP AddPage(0); AddBackground(0, 0, 300, 300, 9380); - var sb = new StringBuilder(); + using var sb = ValueStringBuilder.Create(128); if (tourney.TourneyType == TourneyType.FreeForAll) { @@ -86,12 +87,13 @@ namespace Server.Engines.ConPVP if (tourney.EventController != null) { - sb.Append(' ').Append(tourney.EventController.Title); + sb.Append(' '); + sb.Append(tourney.EventController.Title); } sb.Append(" Tournament Bracket"); - AddHtml(25, 35, 250, 20, sb.ToString().Center()); + AddHtml(25, 35, 250, 20, sb.AsSpan().Center()); AddRightArrow(25, 53, ToButtonID(0, 4), "Rules"); AddRightArrow(25, 71, ToButtonID(0, 1), "Participants"); @@ -278,7 +280,7 @@ namespace Server.Engines.ConPVP ?? new List(tourney.Participants); AddLeftArrow(25, 11, ToButtonID(0, 0)); - AddHtml(25, 35, 250, 20, $"{pList.Count} Participant{(pList.Count == 1 ? "" : "s")}".Center()); + AddHtml(25, 35, 250, 20, Html.Center($"{pList.Count} Participant{(pList.Count == 1 ? "" : "s")}")); StartPage(out var index, out var count, out var y, 12); @@ -347,7 +349,7 @@ namespace Server.Engines.ConPVP AddHtml(25, y, 200, 20, "Log:"); y += 20; - var sb = new StringBuilder(); + using var sb = ValueStringBuilder.Create(); for (var i = 0; i < part.Log.Count; ++i) { @@ -364,7 +366,7 @@ namespace Server.Engines.ConPVP sb.Append("Nothing logged yet."); } - AddHtml(25, y, 250, 150, sb.ToString().Color(BlackColor32), false, true); + AddHtml(25, y, 250, 150, sb.AsSpan().Color(BlackColor32), false, true); break; } diff --git a/Projects/UOContent/Engines/Doom/GenGauntlet.cs b/Projects/UOContent/Engines/Doom/GenGauntlet.cs index 5c4c4edf2..96c7a6f74 100644 --- a/Projects/UOContent/Engines/Doom/GenGauntlet.cs +++ b/Projects/UOContent/Engines/Doom/GenGauntlet.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using Server.Collections; using Server.Items; using Server.Mobiles; @@ -272,18 +272,23 @@ namespace Server.Engines.Doom FacialHairHue = 0x482 }; - var items = new List(dealer.Items); + using var toDelete = PooledRefQueue.Create(); - for (var i = 0; i < items.Count; ++i) + for (var i = 0; i < dealer.Items.Count; ++i) { - var item = items[i]; + var item = dealer.Items[i]; if (item.Layer is not Layer.ShopBuy and not Layer.ShopResale and not Layer.ShopSell) { - item.Delete(); + toDelete.Enqueue(item); } } + while (toDelete.Count > 0) + { + toDelete.Dequeue().Delete(); + } + dealer.AddItem(new FloppyHat(1)); dealer.AddItem(new Robe(1)); dealer.AddItem(new LanternOfSouls()); diff --git a/Projects/UOContent/Engines/Doom/LeverPuzzle/LeverPuzzleController.cs b/Projects/UOContent/Engines/Doom/LeverPuzzle/LeverPuzzleController.cs index 05d5dc6dc..9f4e9c028 100644 --- a/Projects/UOContent/Engines/Doom/LeverPuzzle/LeverPuzzleController.cs +++ b/Projects/UOContent/Engines/Doom/LeverPuzzle/LeverPuzzleController.cs @@ -667,12 +667,13 @@ public partial class LeverPuzzleController : Item protected override void OnTick() { ticks++; - var mobiles = m_Controller._lampRoom.GetMobiles(); + using var mobiles = m_Controller._lampRoom.GetMobilesPooled(); if (ticks >= 71 || m_Controller._lampRoom.GetPlayerCount() == 0) { - foreach (var mobile in mobiles) + for (var i = 0; i < mobiles.Count; i++) { + var mobile = mobiles[i]; if (mobile?.Deleted == false && !mobile.IsDeadBondedPet) { mobile.Kill(); @@ -689,33 +690,36 @@ public partial class LeverPuzzleController : Item level++; } - foreach (var mobile in mobiles) + for (var i = 0; i < mobiles.Count; i++) { - if (IsValidDamagable(mobile)) + var mobile = mobiles[i]; + if (!IsValidDamagable(mobile)) { - if (ticks % 2 == 0 && level == 5) + continue; + } + + if (ticks % 2 == 0 && level == 5) + { + if (mobile.Player) { - if (mobile.Player) + mobile.Say(1062092); + if (AniSafe(mobile)) { - mobile.Say(1062092); - if (AniSafe(mobile)) - { - mobile.Animate(32, 5, 1, true, false, 0); - } + mobile.Animate(32, 5, 1, true, false, 0); } - - DoDamage(mobile, 15, 20, true); } - if (Utility.Random((int)(level & ~0xfffffffc), 3) == 3) - { - mobile.ApplyPoison(mobile, PA2[level]); - } + DoDamage(mobile, 15, 20, true); + } - if (ticks % 12 == 0 && level > 0 && mobile.Player) - { - mobile.SendLocalizedMessage(PA[level][0], null, PA[level][1]); - } + if (Utility.Random((int)(level & ~0xfffffffc), 3) == 3) + { + mobile.ApplyPoison(mobile, PA2[level]); + } + + if (ticks % 12 == 0 && level > 0 && mobile.Player) + { + mobile.SendLocalizedMessage(PA[level][0], null, PA[level][1]); } } diff --git a/Projects/UOContent/Engines/Factions/Gumps/ElectionManagementGump.cs b/Projects/UOContent/Engines/Factions/Gumps/ElectionManagementGump.cs index 5c9a6fad5..723864983 100644 --- a/Projects/UOContent/Engines/Factions/Gumps/ElectionManagementGump.cs +++ b/Projects/UOContent/Engines/Factions/Gumps/ElectionManagementGump.cs @@ -32,7 +32,7 @@ public class ElectionManagementGump : Gump AddHtml(145, 35, 100, 20, (candidate.Mobile == null ? "null" : candidate.Mobile.Name).Color(LabelColor)); AddHtml(45, 55, 100, 20, "Vote Count:".Color(LabelColor)); - AddHtml(145, 55, 100, 20, candidate.Votes.ToString().Color(LabelColor)); + AddHtml(145, 55, 100, 20, Html.Color($"{candidate.Votes}", LabelColor)); AddButton(12, 73, 4005, 4007, 1); AddHtml(45, 75, 100, 20, "Drop Candidate".Color(LabelColor)); @@ -88,9 +88,9 @@ public class ElectionManagementGump : Gump AddHtml(x + 2, 140 + idx * 20, 150, 20, mobile.Name.Color(LabelColor)); x += 150; } - else if (obj is IPAddress) + else if (obj is IPAddress ip) { - AddHtml(x, 140 + idx * 20, 100, 20, obj.ToString().Center(LabelColor)); + AddHtml(x, 140 + idx * 20, 100, 20, Html.Center($"{ip}", LabelColor)); x += 100; } else if (obj is DateTime time) @@ -106,7 +106,7 @@ public class ElectionManagementGump : Gump } else if (obj is int i1) { - AddHtml(x, 140 + idx * 20, 60, 20, $"{i1}%".Center(LabelColor)); + AddHtml(x, 140 + idx * 20, 60, 20, Html.Center($"{i1}%", LabelColor)); x += 60; } } @@ -120,7 +120,7 @@ public class ElectionManagementGump : Gump AddHtml(10, 10, 268, 20, "Election Management".Center(LabelColor)); AddHtml(45, 35, 100, 20, "Current State:".Color(LabelColor)); - AddHtml(145, 35, 100, 20, election.State.ToString().Color(LabelColor)); + AddHtml(145, 35, 100, 20, Html.Color($"{election.State}", LabelColor)); AddButton(12, 53, 4005, 4007, 1); AddHtml(45, 55, 100, 20, "Transition Time:".Color(LabelColor)); @@ -147,7 +147,7 @@ public class ElectionManagementGump : Gump AddButton(13, 118 + i * 20, 4005, 4007, 2 + i); AddHtml(47, 120 + i * 20, 150, 20, mob.Name.Color(LabelColor)); - AddHtml(195, 120 + i * 20, 80, 20, cd.Votes.ToString().Center(LabelColor)); + AddHtml(195, 120 + i * 20, 80, 20, Html.Center($"{cd.Votes}", LabelColor)); } } } @@ -200,4 +200,4 @@ public class ElectionManagementGump : Gump } } } -} \ No newline at end of file +} diff --git a/Projects/UOContent/Engines/Factions/Items/Traps/BaseFactionTrapDeed.cs b/Projects/UOContent/Engines/Factions/Items/Traps/BaseFactionTrapDeed.cs index 2104d2eb3..6a5afe534 100644 --- a/Projects/UOContent/Engines/Factions/Items/Traps/BaseFactionTrapDeed.cs +++ b/Projects/UOContent/Engines/Factions/Items/Traps/BaseFactionTrapDeed.cs @@ -10,7 +10,6 @@ public abstract class BaseFactionTrapDeed : Item, ICraftable public BaseFactionTrapDeed(int itemID = 0x14F0) : base(itemID) { - Weight = 1.0; LootType = LootType.Blessed; } @@ -18,6 +17,8 @@ public abstract class BaseFactionTrapDeed : Item, ICraftable { } + public override double DefaultWeight => 1.0; + public abstract Type TrapType { get; } [CommandProperty(AccessLevel.GameMaster)] @@ -117,4 +118,4 @@ public abstract class BaseFactionTrapDeed : Item, ICraftable m_Faction = Faction.ReadReference(reader); } -} \ No newline at end of file +} diff --git a/Projects/UOContent/Engines/Factions/Mobiles/Guards/GuardAI.cs b/Projects/UOContent/Engines/Factions/Mobiles/Guards/GuardAI.cs index 804ddbef7..6caaf9dfc 100644 --- a/Projects/UOContent/Engines/Factions/Mobiles/Guards/GuardAI.cs +++ b/Projects/UOContent/Engines/Factions/Mobiles/Guards/GuardAI.cs @@ -1,5 +1,5 @@ using System; -using System.Collections.Generic; +using Server.Collections; using Server.Factions.AI; using Server.Items; using Server.Mobiles; @@ -234,26 +234,26 @@ namespace Server.Factions public Mobile FindDispelTarget(bool activeOnly) { - if (m_Mobile.Deleted || m_Mobile.Int < 95 || CanDispel(m_Mobile) || m_Mobile.AutoDispel) + if (Mobile.Deleted || Mobile.Int < 95 || CanDispel(Mobile) || Mobile.AutoDispel) { return null; } if (activeOnly) { - var aggressed = m_Mobile.Aggressed; - var aggressors = m_Mobile.Aggressors; + var aggressed = Mobile.Aggressed; + var aggressors = Mobile.Aggressors; Mobile active = null; var activePrio = 0.0; - var comb = m_Mobile.Combatant; + var comb = Mobile.Combatant; - if (comb?.Deleted == false && comb.Alive && !comb.IsDeadBondedPet && m_Mobile.InRange(comb, 12) && + if (comb?.Deleted == false && comb.Alive && !comb.IsDeadBondedPet && Mobile.InRange(comb, 12) && CanDispel(comb)) { active = comb; - activePrio = m_Mobile.GetDistanceToSqrt(comb); + activePrio = Mobile.GetDistanceToSqrt(comb); if (activePrio <= 2) { @@ -266,9 +266,9 @@ namespace Server.Factions var info = aggressed[i]; var m = info.Defender; - if (m != comb && m.Combatant == m_Mobile && m_Mobile.InRange(m, 12) && CanDispel(m)) + if (m != comb && m.Combatant == Mobile && Mobile.InRange(m, 12) && CanDispel(m)) { - var prio = m_Mobile.GetDistanceToSqrt(m); + var prio = Mobile.GetDistanceToSqrt(m); if (active == null || prio < activePrio) { @@ -288,9 +288,9 @@ namespace Server.Factions var info = aggressors[i]; var m = info.Attacker; - if (m != comb && m.Combatant == m_Mobile && m_Mobile.InRange(m, 12) && CanDispel(m)) + if (m != comb && m.Combatant == Mobile && Mobile.InRange(m, 12) && CanDispel(m)) { - var prio = m_Mobile.GetDistanceToSqrt(m); + var prio = Mobile.GetDistanceToSqrt(m); if (active == null || prio < activePrio) { @@ -308,26 +308,26 @@ namespace Server.Factions return active; } - var map = m_Mobile.Map; + var map = Mobile.Map; if (map != null) { Mobile active = null, inactive = null; double actPrio = 0.0, inactPrio = 0.0; - var comb = m_Mobile.Combatant; + var comb = Mobile.Combatant; if (comb?.Deleted == false && comb.Alive && !comb.IsDeadBondedPet && CanDispel(comb)) { active = inactive = comb; - actPrio = inactPrio = m_Mobile.GetDistanceToSqrt(comb); + actPrio = inactPrio = Mobile.GetDistanceToSqrt(comb); } - foreach (var m in m_Mobile.GetMobilesInRange(12)) + foreach (var m in Mobile.GetMobilesInRange(12)) { - if (m != m_Mobile && CanDispel(m)) + if (m != Mobile && CanDispel(m)) { - var prio = m_Mobile.GetDistanceToSqrt(m); + var prio = Mobile.GetDistanceToSqrt(m); if (inactive == null || prio < inactPrio) { @@ -335,7 +335,7 @@ namespace Server.Factions inactPrio = prio; } - if ((m_Mobile.Combatant == m || m.Combatant == m_Mobile) && (active == null || prio < actPrio)) + if ((Mobile.Combatant == m || m.Combatant == Mobile) && (active == null || prio < actPrio)) { active = m; actPrio = prio; @@ -350,7 +350,7 @@ namespace Server.Factions } public bool CanDispel(Mobile m) => - m is BaseCreature creature && creature.Summoned && m_Mobile.CanBeHarmful(creature, false) && + m is BaseCreature creature && creature.Summoned && Mobile.CanBeHarmful(creature, false) && !creature.IsAnimatedDead; public void RunTo(Mobile m) @@ -364,14 +364,14 @@ namespace Server.Factions } else {*/ - if (!m_Mobile.InRange(m, m_Mobile.RangeFight)) + if (!Mobile.InRange(m, Mobile.RangeFight)) { if (!MoveTo(m, true, 1)) { OnFailedMove(); } } - else if (m_Mobile.InRange(m, m_Mobile.RangeFight - 1)) + else if (Mobile.InRange(m, Mobile.RangeFight - 1)) { RunFrom(m); } @@ -381,7 +381,7 @@ namespace Server.Factions public void RunFrom(Mobile m) { - Run((m_Mobile.GetDirectionTo(m) - 4) & Direction.Mask); + Run((Mobile.GetDirectionTo(m) - 4) & Direction.Mask); } public void OnFailedMove() @@ -393,36 +393,33 @@ namespace Server.Factions new TeleportSpell( m_Mobile, null ).Cast(); - m_Mobile.DebugSay( "I am stuck, I'm going to try teleporting away" ); + DebugSay( "I am stuck, I'm going to try teleporting away" ); } else*/ - if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"My move is blocked, so I am going to attack {m_Mobile.FocusMob.Name}"); - } + this.DebugSayFormatted($"My move is blocked, so I am going to attack {Mobile.FocusMob.Name}"); - m_Mobile.Combatant = m_Mobile.FocusMob; + Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; } - else if (m_Mobile.Debug) + else { - m_Mobile.DebugSay("I am stuck"); + DebugSay("I am stuck"); } } public void Run(Direction d) { - if (m_Mobile.Spell?.IsCasting == true || m_Mobile.Paralyzed || m_Mobile.Frozen || - m_Mobile.DisallowAllMoves) + if (Mobile.Spell?.IsCasting == true || Mobile.Paralyzed || Mobile.Frozen || + Mobile.DisallowAllMoves) { return; } - m_Mobile.Direction = d | Direction.Running; + Mobile.Direction = d | Direction.Running; - if (!DoMove(m_Mobile.Direction, true)) + if (!DoMove(Mobile.Direction, true)) { OnFailedMove(); } @@ -430,7 +427,7 @@ namespace Server.Factions public override bool Think() { - if (m_Mobile.Deleted) + if (Mobile.Deleted) { return false; } @@ -438,32 +435,32 @@ namespace Server.Factions var combatant = m_Guard.Combatant; if (combatant?.Deleted != false || !combatant.Alive || combatant.IsDeadBondedPet || - !m_Mobile.CanSee(combatant) || !m_Mobile.CanBeHarmful(combatant, false) || combatant.Map != m_Mobile.Map) + !Mobile.CanSee(combatant) || !Mobile.CanBeHarmful(combatant, false) || combatant.Map != Mobile.Map) { // Our combatant is deleted, dead, hidden, or we cannot hurt them // Try to find another combatant - if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - m_Mobile.Combatant = combatant = m_Mobile.FocusMob; - m_Mobile.FocusMob = null; + Mobile.Combatant = combatant = Mobile.FocusMob; + Mobile.FocusMob = null; } else { - m_Mobile.Combatant = combatant = null; + Mobile.Combatant = combatant = null; } } - if (combatant != null && (!m_Mobile.InLOS(combatant) || !m_Mobile.InRange(combatant, 12))) + if (combatant != null && (!Mobile.InLOS(combatant) || !Mobile.InRange(combatant, 12))) { - if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - m_Mobile.Combatant = combatant = m_Mobile.FocusMob; - m_Mobile.FocusMob = null; + Mobile.Combatant = combatant = Mobile.FocusMob; + Mobile.FocusMob = null; } - else if (!m_Mobile.InRange(combatant, 36)) + else if (!Mobile.InRange(combatant, 36)) { - m_Mobile.Combatant = combatant = null; + Mobile.Combatant = combatant = null; } } @@ -544,7 +541,7 @@ namespace Server.Factions Action = ActionType.Combat; } - m_Mobile.SetCurrentSpeedToActive(); + Mobile.SetCurrentSpeedToActive(); m_Guard.Warmode = true; RunTo(toFollow); @@ -556,7 +553,7 @@ namespace Server.Factions Action = ActionType.Wander; } - m_Mobile.SetCurrentSpeedToPassive(); + Mobile.SetCurrentSpeedToPassive(); m_Guard.Warmode = false; WalkRandomInHome(2, 2, 1); @@ -582,9 +579,9 @@ namespace Server.Factions } } - var spell = m_Mobile.Spell as Spell; + var spell = Mobile.Spell as Spell; - if (spell == null && Core.TickCount - m_Mobile.NextSpellTime >= 0) + if (spell == null && Core.TickCount - Mobile.NextSpellTime >= 0) { var toRelease = DateTime.MinValue; @@ -695,41 +692,42 @@ namespace Server.Factions var dexMod = GetStatMod(m_Guard, StatType.Dex); var intMod = GetStatMod(m_Guard, StatType.Int); - var types = new List(); + using var spellTypes = PooledRefQueue.Create(); if (strMod <= 0) { - types.Add(typeof(StrengthSpell)); + spellTypes.Enqueue(typeof(StrengthSpell)); } if (dexMod <= 0 && IsAllowed(GuardAI.Melee)) { - types.Add(typeof(AgilitySpell)); + spellTypes.Enqueue(typeof(AgilitySpell)); } if (intMod <= 0 && IsAllowed(GuardAI.Magic)) { - types.Add(typeof(CunningSpell)); + spellTypes.Enqueue(typeof(CunningSpell)); } if (IsAllowed(GuardAI.Bless)) { - if (types.Count > 1) + if (spellTypes.Count > 1) { spell = new BlessSpell(m_Guard); } - else if (types.Count == 1) + else if (spellTypes.Count == 1) { - spell = types[0].CreateInstance(m_Guard, null); + spell = spellTypes.Dequeue().CreateInstance(m_Guard, null); } } - else if (types.Count > 0) + else if (spellTypes.Count > 0) { - if (types[0] == typeof(StrengthSpell)) + var spellType = spellTypes.Dequeue(); + if (spellType == typeof(StrengthSpell)) { UseItemByType(typeof(BaseStrengthPotion)); } - else if (types[0] == typeof(AgilitySpell)) + else if (spellType == typeof(AgilitySpell)) { UseItemByType(typeof(BaseAgilityPotion)); } @@ -749,30 +747,30 @@ namespace Server.Factions var dexMod = GetStatMod(combatant, StatType.Dex); var intMod = GetStatMod(combatant, StatType.Int); - var types = new List(); + using var spellTypes = PooledRefQueue.Create(); if (strMod >= 0) { - types.Add(typeof(WeakenSpell)); + spellTypes.Enqueue(typeof(WeakenSpell)); } if (dexMod >= 0 && IsAllowed(GuardAI.Melee)) { - types.Add(typeof(ClumsySpell)); + spellTypes.Enqueue(typeof(ClumsySpell)); } if (intMod >= 0 && IsAllowed(GuardAI.Magic)) { - types.Add(typeof(FeeblemindSpell)); + spellTypes.Enqueue(typeof(FeeblemindSpell)); } - if (types.Count > 1) + if (spellTypes.Count > 1) { spell = new CurseSpell(m_Guard); } - else if (types.Count == 1) + else if (spellTypes.Count == 1) { - spell = types[0].CreateInstance(m_Guard, null); + spell = spellTypes.Dequeue().CreateInstance(m_Guard, null); } } } diff --git a/Projects/UOContent/Engines/Harvest/Lumberjacking.cs b/Projects/UOContent/Engines/Harvest/Lumberjacking.cs index b9275cda0..9640f68d8 100644 --- a/Projects/UOContent/Engines/Harvest/Lumberjacking.cs +++ b/Projects/UOContent/Engines/Harvest/Lumberjacking.cs @@ -175,6 +175,8 @@ namespace Server.Engines.Harvest } } + public override object GetLock(Mobile from, Item tool, HarvestDefinition def, object toHarvest) => this; + public override void OnHarvestStarted(Mobile from, Item tool, HarvestDefinition def, object toHarvest) { base.OnHarvestStarted(from, tool, def, toHarvest); diff --git a/Projects/UOContent/Engines/Harvest/Mining.cs b/Projects/UOContent/Engines/Harvest/Mining.cs index e79de9fed..dd7f5c747 100644 --- a/Projects/UOContent/Engines/Harvest/Mining.cs +++ b/Projects/UOContent/Engines/Harvest/Mining.cs @@ -435,6 +435,8 @@ namespace Server.Engines.Harvest } } + public override object GetLock(Mobile from, Item tool, HarvestDefinition def, object toHarvest) => this; + public override bool BeginHarvesting(Mobile from, Item tool) { if (!base.BeginHarvesting(from, tool)) diff --git a/Projects/UOContent/Engines/Help/PageResponseGump.cs b/Projects/UOContent/Engines/Help/PageResponseGump.cs index 106eb4131..ec00f5849 100644 --- a/Projects/UOContent/Engines/Help/PageResponseGump.cs +++ b/Projects/UOContent/Engines/Help/PageResponseGump.cs @@ -25,7 +25,7 @@ public sealed class PageResponseGump : StaticGump //
Ultima Online Help Response
builder.AddHtmlLocalized(150, 40, 360, 40, 1062610); - builder.AddHtml(80, 90, 480, 290, $"{_name} tells {_from.Name}: {_text}", true, true); + builder.AddHtml(80, 90, 480, 290, $"{_name} tells {_from.Name}: {_text}", background: true, scrollbar: true); // Clicking the OKAY button will remove the response you have received. builder.AddHtmlLocalized(80, 390, 480, 40, 1062611); diff --git a/Projects/UOContent/Engines/Help/SpeechLogGump.cs b/Projects/UOContent/Engines/Help/SpeechLogGump.cs index 54a6aa6f3..eae679656 100644 --- a/Projects/UOContent/Engines/Help/SpeechLogGump.cs +++ b/Projects/UOContent/Engines/Help/SpeechLogGump.cs @@ -46,7 +46,7 @@ namespace Server.Engines.Help 10, 280, 20, - $"SPEECH LOG - {playerName} ({playerAccount.FixHtmlFormattable()})".Center(0xA0A0FF) + Html.Center($"SPEECH LOG - {playerName} ({playerAccount.FixHtmlFormattable()})", 0xA0A0FF) ); var lastPage = (log.Count - 1) / MaxEntriesPerPage; diff --git a/Projects/UOContent/Engines/ML Quests/Mobiles/BoonCollector.cs b/Projects/UOContent/Engines/ML Quests/Mobiles/BoonCollector.cs index 6dd4e4782..86754c5e5 100644 --- a/Projects/UOContent/Engines/ML Quests/Mobiles/BoonCollector.cs +++ b/Projects/UOContent/Engines/ML Quests/Mobiles/BoonCollector.cs @@ -118,8 +118,7 @@ public abstract partial class DoneQuestCollector : BaseCreature, IRaceChanger } else { - var conversation = new List(); - conversation.AddRange(Incomplete); + List conversation = [..Incomplete]; var context = MLQuestSystem.GetContext(pm); diff --git a/Projects/UOContent/Engines/ML Quests/Objectives/DeliverObjective.cs b/Projects/UOContent/Engines/ML Quests/Objectives/DeliverObjective.cs index e8d689028..c50ed99e7 100644 --- a/Projects/UOContent/Engines/ML Quests/Objectives/DeliverObjective.cs +++ b/Projects/UOContent/Engines/ML Quests/Objectives/DeliverObjective.cs @@ -1,5 +1,5 @@ using System; -using System.Collections.Generic; +using Server.Collections; using Server.Gumps; using Server.Items; using Server.Logging; @@ -46,7 +46,7 @@ namespace Server.Engines.MLQuests.Objectives return; } - var delivery = new List(); + using var delivery = PooledRefQueue.Create(); for (var i = 0; i < Amount; ++i) { @@ -54,7 +54,7 @@ namespace Server.Engines.MLQuests.Objectives if (item != null) { - delivery.Add(item); + delivery.Enqueue(item); if (item.Stackable && Amount > 1) { @@ -64,9 +64,9 @@ namespace Server.Engines.MLQuests.Objectives } } - foreach (var item in delivery) + while (delivery.Count > 0) { - pack.DropItem(item); // Confirmed: on OSI items are added even if your pack is full + pack.DropItem(delivery.Dequeue()); // Confirmed: on OSI items are added even if your pack is full } } diff --git a/Projects/UOContent/Engines/Pathing/FastAStarAlgorithm.cs b/Projects/UOContent/Engines/Pathing/FastAStarAlgorithm.cs index cd3e4cca4..7154dcb22 100644 --- a/Projects/UOContent/Engines/Pathing/FastAStarAlgorithm.cs +++ b/Projects/UOContent/Engines/Pathing/FastAStarAlgorithm.cs @@ -1,352 +1,297 @@ -using System.Collections; +/************************************************************************* + * ModernUO * + * Copyright 2019-2025 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: FastAStarAlgorithm.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ************************************************************************/ + +using System; using Server.Mobiles; using CalcMoves = Server.Movement.Movement; using MoveImpl = Server.Movement.MovementImpl; +using System.Collections.Generic; +using System.Runtime.CompilerServices; -namespace Server.PathAlgorithms.FastAStar +namespace Server.PathAlgorithms.FastAStar; + +public class FastAStarAlgorithm : PathAlgorithm { - public struct PathNode + private struct PathNode { - public int cost, total; - public int parent, next, prev; + public int cost; + public int total; + public int parent; public int z; } - public class FastAStarAlgorithm : PathAlgorithm + private const int MaxDepth = 300; + private const int AreaSize = 38; + + private const int NodeCount = AreaSize * AreaSize * PlaneCount; + + private const int PlaneOffset = 128; + private const int PlaneCount = 13; + private const int PlaneHeight = 20; + public static readonly PathAlgorithm Instance = new FastAStarAlgorithm(); + + private static readonly Direction[] _path = new Direction[AreaSize * AreaSize]; + private static readonly PathNode[] _nodes = new PathNode[NodeCount]; + private static readonly byte[] _nodeStates = new byte[NodeCount]; + private static readonly int[] _successors = new int[8]; + private static readonly PriorityQueue _openQueue = new(); + + private static int _xOffset; + private static int _yOffset; + + private Point3D _goal; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int Heuristic(int x, int y, int z) { - private const int MaxDepth = 300; - private const int AreaSize = 38; + x -= _goal.X - _xOffset; + y -= _goal.Y - _yOffset; + z -= _goal.Z; - private const int NodeCount = AreaSize * AreaSize * PlaneCount; + x *= 11; + y *= 11; - private const int PlaneOffset = 128; - private const int PlaneCount = 13; - private const int PlaneHeight = 20; - public static PathAlgorithm Instance = new FastAStarAlgorithm(); + return x * x + y * y + z * z; + } - private static readonly Direction[] _path = new Direction[AreaSize * AreaSize]; - private static readonly PathNode[] _nodes = new PathNode[NodeCount]; - private static readonly BitArray _touched = new(NodeCount); - private static readonly BitArray _onOpen = new(NodeCount); - private static readonly int[] _successors = new int[8]; + public override bool CheckCondition(Mobile m, Map map, Point3D start, Point3D goal) => + Utility.InRange(start, goal, AreaSize); - private static int _xOffset; - private static int _yOffset; - private static int _openList; - - private Point3D _goal; - - public int Heuristic(int x, int y, int z) + public override Direction[] Find(Mobile m, Map map, Point3D start, Point3D goal) + { + if (!Utility.InRange(start, goal, AreaSize)) { - x -= _goal.X - _xOffset; - y -= _goal.Y - _yOffset; - z -= _goal.Z; - - x *= 11; - y *= 11; - - return x * x + y * y + z * z; - } - - public override bool CheckCondition(Mobile m, Map map, Point3D start, Point3D goal) => - Utility.InRange(start, goal, AreaSize); - - private void RemoveFromChain(int node) - { - if (node is < 0 or >= NodeCount) - { - return; - } - - if (!_touched[node] || !_onOpen[node]) - { - return; - } - - var prev = _nodes[node].prev; - var next = _nodes[node].next; - - if (_openList == node) - { - _openList = next; - } - - if (prev != -1) - { - _nodes[prev].next = next; - } - - if (next != -1) - { - _nodes[next].prev = prev; - } - - _nodes[node].prev = -1; - _nodes[node].next = -1; - } - - private void AddToChain(int node) - { - if (node is < 0 or >= NodeCount) - { - return; - } - - RemoveFromChain(node); - - if (_openList != -1) - { - _nodes[_openList].prev = node; - } - - _nodes[node].next = _openList; - _nodes[node].prev = -1; - - _openList = node; - - _touched[node] = true; - _onOpen[node] = true; - } - - public override Direction[] Find(Mobile m, Map map, Point3D start, Point3D goal) - { - if (!Utility.InRange(start, goal, AreaSize)) - { - return null; - } - - _touched.SetAll(false); - _onOpen.SetAll(false); - - _goal = goal; - - _xOffset = (start.X + goal.X - AreaSize) / 2; - _yOffset = (start.Y + goal.Y - AreaSize) / 2; - - var fromNode = GetIndex(start.X, start.Y, start.Z); - var destNode = GetIndex(goal.X, goal.Y, goal.Z); - - _openList = fromNode; - - _nodes[_openList].cost = 0; - _nodes[_openList].total = Heuristic(start.X - _xOffset, start.Y - _yOffset, start.Z); - _nodes[_openList].parent = -1; - _nodes[_openList].next = -1; - _nodes[_openList].prev = -1; - _nodes[_openList].z = start.Z; - - _onOpen[_openList] = true; - _touched[_openList] = true; - - var bc = m as BaseCreature; - - int backtrack = 0, depth = 0; - - var path = _path; - - while (_openList != -1) - { - var bestNode = FindBest(_openList); - - if (++depth > MaxDepth) - { - break; - } - - if (bc != null) - { - MoveImpl.AlwaysIgnoreDoors = bc.CanOpenDoors; - MoveImpl.IgnoreMovableImpassables = bc.CanMoveOverObstacles; - } - - MoveImpl.Goal = goal; - - var vals = _successors; - var count = GetSuccessors(bestNode, m, map); - - MoveImpl.AlwaysIgnoreDoors = false; - MoveImpl.IgnoreMovableImpassables = false; - MoveImpl.Goal = Point3D.Zero; - - if (count == 0) - { - break; - } - - for (var i = 0; i < count; ++i) - { - var newNode = vals[i]; - - var wasTouched = _touched[newNode]; - - if (wasTouched) - { - continue; - } - - var newCost = _nodes[bestNode].cost + 1; - var newTotal = newCost + Heuristic( - newNode % AreaSize, - newNode / AreaSize % AreaSize, - _nodes[newNode].z - ); - - _nodes[newNode].parent = bestNode; - _nodes[newNode].cost = newCost; - _nodes[newNode].total = newTotal; - - if (_onOpen[newNode]) - { - continue; - } - - AddToChain(newNode); - - if (newNode != destNode) - { - continue; - } - - var pathCount = 0; - var parent = _nodes[newNode].parent; - - while (parent != -1) - { - path[pathCount++] = GetDirection( - parent % AreaSize, - parent / AreaSize % AreaSize, - newNode % AreaSize, - newNode / AreaSize % AreaSize - ); - newNode = parent; - parent = _nodes[newNode].parent; - - if (newNode == fromNode) - { - break; - } - } - - var dirs = new Direction[pathCount]; - - while (pathCount > 0) - { - dirs[backtrack++] = path[--pathCount]; - } - - return dirs; - } - } - return null; } - private int GetIndex(int x, int y, int z) + Array.Clear(_nodeStates); + + _goal = goal; + + _xOffset = (start.X + goal.X - AreaSize) / 2; + _yOffset = (start.Y + goal.Y - AreaSize) / 2; + + var fromNode = GetIndex(start.X, start.Y, start.Z); + var destNode = GetIndex(goal.X, goal.Y, goal.Z); + + _nodes[fromNode].cost = 0; + _nodes[fromNode].total = Heuristic(start.X - _xOffset, start.Y - _yOffset, start.Z); + _nodes[fromNode].parent = -1; + _nodes[fromNode].z = start.Z; + + _openQueue.Enqueue(fromNode, _nodes[fromNode].total); + _nodeStates[fromNode] = 1; + + var bc = m as BaseCreature; + + int backtrack = 0, depth = 0; + + var path = _path; + + while (_openQueue.Count > 0) { - x -= _xOffset; - y -= _yOffset; - z += PlaneOffset; - z /= PlaneHeight; - - return x + y * AreaSize + z * AreaSize * AreaSize; - } - - private int FindBest(int node) - { - var least = _nodes[node].total; - var leastNode = node; - - while (node != -1) + if (++depth > MaxDepth) { - if (_nodes[node].total < least) - { - least = _nodes[node].total; - leastNode = node; - } - - node = _nodes[node].next; + break; } - RemoveFromChain(leastNode); + if (!_openQueue.TryDequeue(out var bestNode, out var bestTotal)) + { + break; + } - _touched[leastNode] = true; - _onOpen[leastNode] = false; + // Duplicate, lower priority + if (_nodeStates[bestNode] == 2 || _nodes[bestNode].total != bestTotal) + { + continue; + } - return leastNode; - } + _nodeStates[bestNode] = 2; - public int GetSuccessors(int p, Mobile m, Map map) - { - var px = p % AreaSize; - var py = p / AreaSize % AreaSize; - var pz = _nodes[p].z; + if (bc != null) + { + MoveImpl.AlwaysIgnoreDoors = bc.CanOpenDoors; + MoveImpl.IgnoreMovableImpassables = bc.CanMoveOverObstacles; + } - var p3D = new Point3D(px + _xOffset, py + _yOffset, pz); + MoveImpl.Goal = goal; var vals = _successors; - var count = 0; + var count = GetSuccessors(bestNode, m, map); - for (var i = 0; i < 8; ++i) + MoveImpl.AlwaysIgnoreDoors = false; + MoveImpl.IgnoreMovableImpassables = false; + MoveImpl.Goal = Point3D.Zero; + + if (count == 0) { - int x; - int y; - switch (i) - { - default: // 0 - x = 0; - y = -1; - break; - case 1: - x = 1; - y = -1; - break; - case 2: - x = 1; - y = 0; - break; - case 3: - x = 1; - y = 1; - break; - case 4: - x = 0; - y = 1; - break; - case 5: - x = -1; - y = 1; - break; - case 6: - x = -1; - y = 0; - break; - case 7: - x = -1; - y = -1; - break; - } + continue; + } - x += px; - y += py; + for (var i = 0; i < count; ++i) + { + var newNode = vals[i]; - if (x is < 0 or >= AreaSize || y is < 0 or >= AreaSize) + // Skip if the node is already closed + if (_nodeStates[newNode] == 2) { continue; } - if (CalcMoves.CheckMovement(m, map, p3D, (Direction)i, out var z)) - { - var idx = GetIndex(x + _xOffset, y + _yOffset, z); + var isDiagonal = i % 2 == 1; + var moveCost = isDiagonal ? 14 : 10; + var newCost = _nodes[bestNode].cost + moveCost; + var newTotal = newCost + Heuristic( + newNode % AreaSize, + newNode / AreaSize % AreaSize, + _nodes[newNode].z + ); - if (idx >= 0 && idx < NodeCount) + if (_nodeStates[newNode] == 0 || newTotal < _nodes[newNode].total) + { + _nodes[newNode].parent = bestNode; + _nodes[newNode].cost = newCost; + _nodes[newNode].total = newTotal; + + // Requeue (duplicates allowed), and mark as open + _openQueue.Enqueue(newNode, newTotal); + _nodeStates[newNode] = 1; + } + + if (newNode != destNode) + { + continue; + } + + var pathCount = 0; + var parent = _nodes[newNode].parent; + + while (parent != -1) + { + path[pathCount++] = GetDirection( + parent % AreaSize, + parent / AreaSize % AreaSize, + newNode % AreaSize, + newNode / AreaSize % AreaSize + ); + newNode = parent; + parent = _nodes[newNode].parent; + + if (newNode == fromNode) { - _nodes[idx].z = z; - vals[count++] = idx; + break; } } + + var dirs = new Direction[pathCount]; + + while (pathCount > 0) + { + dirs[backtrack++] = path[--pathCount]; + } + + _openQueue.Clear(); + return dirs; + } + } + + _openQueue.Clear(); + return null; + } + + private static int GetIndex(int x, int y, int z) + { + x -= _xOffset; + y -= _yOffset; + z += PlaneOffset; + z /= PlaneHeight; + + return x + y * AreaSize + z * AreaSize * AreaSize; + } + + private static int GetSuccessors(int p, Mobile m, Map map) + { + var px = p % AreaSize; + var py = p / AreaSize % AreaSize; + var pz = _nodes[p].z; + + var p3D = new Point3D(px + _xOffset, py + _yOffset, pz); + + var vals = _successors; + var count = 0; + + for (var i = 0; i < 8; ++i) + { + int x; + int y; + switch (i) + { + default: // 0 + x = 0; + y = -1; + break; + case 1: + x = 1; + y = -1; + break; + case 2: + x = 1; + y = 0; + break; + case 3: + x = 1; + y = 1; + break; + case 4: + x = 0; + y = 1; + break; + case 5: + x = -1; + y = 1; + break; + case 6: + x = -1; + y = 0; + break; + case 7: + x = -1; + y = -1; + break; } - return count; + x += px; + y += py; + + if (x is < 0 or >= AreaSize || y is < 0 or >= AreaSize) + { + continue; + } + + if (CalcMoves.CheckMovement(m, map, p3D, (Direction)i, out var z)) + { + var idx = GetIndex(x + _xOffset, y + _yOffset, z); + + if (idx >= 0 && idx < NodeCount) + { + _nodes[idx].z = z; + vals[count++] = idx; + } + } } + + return count; } } diff --git a/Projects/UOContent/Engines/Plants/MiscItems/FertileDirt.cs b/Projects/UOContent/Engines/Plants/MiscItems/FertileDirt.cs index 311eae3f6..32a4d0cb1 100644 --- a/Projects/UOContent/Engines/Plants/MiscItems/FertileDirt.cs +++ b/Projects/UOContent/Engines/Plants/MiscItems/FertileDirt.cs @@ -9,7 +9,8 @@ public partial class FertileDirt : Item public FertileDirt(int amount = 1) : base(0xF81) { Stackable = true; - Weight = 1.0; Amount = amount; } + + public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Engines/Plants/MiscItems/GreenThorns.cs b/Projects/UOContent/Engines/Plants/MiscItems/GreenThorns.cs index 846f8d1dd..90e8cc1ea 100644 --- a/Projects/UOContent/Engines/Plants/MiscItems/GreenThorns.cs +++ b/Projects/UOContent/Engines/Plants/MiscItems/GreenThorns.cs @@ -12,11 +12,12 @@ public partial class GreenThorns : Item public GreenThorns(int amount = 1) : base(0xF42) { Stackable = true; - Weight = 1.0; Hue = 0x42; Amount = amount; } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1060837; // green thorns public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Engines/Plants/PlantBowl.cs b/Projects/UOContent/Engines/Plants/PlantBowl.cs index 65489d385..5028b6db8 100644 --- a/Projects/UOContent/Engines/Plants/PlantBowl.cs +++ b/Projects/UOContent/Engines/Plants/PlantBowl.cs @@ -56,7 +56,11 @@ public partial class PlantBowl : Item }; [Constructible] - public PlantBowl() : base(0x15FD) => Weight = 1.0; + public PlantBowl() : base(0x15FD) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1060834; // a plant bowl diff --git a/Projects/UOContent/Engines/Plants/PlantItem.cs b/Projects/UOContent/Engines/Plants/PlantItem.cs index 53266f2b4..959bb031b 100644 --- a/Projects/UOContent/Engines/Plants/PlantItem.cs +++ b/Projects/UOContent/Engines/Plants/PlantItem.cs @@ -56,8 +56,6 @@ public partial class PlantItem : Item, ISecurable [Constructible] public PlantItem(bool fertileDirt = false) : base(0x1602) { - Weight = 1.0; - _plantStatus = PlantStatus.BowlOfDirt; _plantSystem = new PlantSystem(this) { @@ -69,6 +67,8 @@ public partial class PlantItem : Item, ISecurable Plants.Add(this); } + public override double DefaultWeight => 1.0; + public ObjectPropertyList OldClientPropertyList { get diff --git a/Projects/UOContent/Engines/Plants/PlantSystem.cs b/Projects/UOContent/Engines/Plants/PlantSystem.cs index 61d017c0f..cbbd0cb9c 100644 --- a/Projects/UOContent/Engines/Plants/PlantSystem.cs +++ b/Projects/UOContent/Engines/Plants/PlantSystem.cs @@ -320,7 +320,10 @@ namespace Server.Engines.Plants } [SerializableFieldSaveFlag(17)] - private bool ShouldSerializeLeftSeeds() => _leftSeeds != 0; + private bool ShouldSerializeLeftSeeds() => _leftSeeds != 8; + + [SerializableFieldDefault(17)] + private int LeftSeedsDefaultValue() => 8; [SerializableProperty(18)] public int AvailableResources @@ -340,7 +343,10 @@ namespace Server.Engines.Plants } [SerializableFieldSaveFlag(19)] - private bool ShouldSerializeLeftResources() => _leftResources != 0; + private bool ShouldSerializeLeftResources() => _leftResources != 8; + + [SerializableFieldDefault(19)] + private int LeftResourcesDefaultValue() => 8; public void Reset(bool potions) { diff --git a/Projects/UOContent/Engines/Plants/Seed.cs b/Projects/UOContent/Engines/Plants/Seed.cs index 043ad445b..24a290f0b 100644 --- a/Projects/UOContent/Engines/Plants/Seed.cs +++ b/Projects/UOContent/Engines/Plants/Seed.cs @@ -24,7 +24,6 @@ public partial class Seed : Item [Constructible] public Seed(PlantType plantType, PlantHue plantHue, bool showType = false) : base(0xDCF) { - Weight = 1.0; Stackable = Core.SA; _plantType = plantType; @@ -34,6 +33,8 @@ public partial class Seed : Item Hue = PlantHueInfo.GetInfo(plantHue).Hue; } + public override double DefaultWeight => 1.0; + [CommandProperty(AccessLevel.GameMaster)] [SerializableProperty(1)] public PlantHue PlantHue diff --git a/Projects/UOContent/Engines/Quests/Collector/Items/EnchantedPaints.cs b/Projects/UOContent/Engines/Quests/Collector/Items/EnchantedPaints.cs index 5b6ab0bb2..d9e13d5b9 100644 --- a/Projects/UOContent/Engines/Quests/Collector/Items/EnchantedPaints.cs +++ b/Projects/UOContent/Engines/Quests/Collector/Items/EnchantedPaints.cs @@ -8,11 +8,9 @@ namespace Server.Engines.Quests.Collector; public partial class EnchantedPaints : QuestItem { [Constructible] - public EnchantedPaints() : base(0xFC1) - { - LootType = LootType.Blessed; - Weight = 1.0; - } + public EnchantedPaints() : base(0xFC1) => LootType = LootType.Blessed; + + public override double DefaultWeight => 1.0; public override bool CanDrop(PlayerMobile player) => player.Quest is not CollectorQuest; diff --git a/Projects/UOContent/Engines/Quests/Collector/Items/PaintedImage.cs b/Projects/UOContent/Engines/Quests/Collector/Items/PaintedImage.cs index 7c9aa4da8..937424e9b 100644 --- a/Projects/UOContent/Engines/Quests/Collector/Items/PaintedImage.cs +++ b/Projects/UOContent/Engines/Quests/Collector/Items/PaintedImage.cs @@ -14,12 +14,12 @@ public partial class PaintedImage : Item [Constructible] public PaintedImage(ImageType image) : base(0xFF3) { - Weight = 1.0; Hue = 0x8FD; - _image = image; } + public override double DefaultWeight => 1.0; + public override void AddNameProperty(IPropertyList list) { var info = ImageTypeInfo.Get(_image); diff --git a/Projects/UOContent/Engines/Quests/Core/Items/EnchantedSextant.cs b/Projects/UOContent/Engines/Quests/Core/Items/EnchantedSextant.cs index 142cb42ab..f224d8747 100644 --- a/Projects/UOContent/Engines/Quests/Core/Items/EnchantedSextant.cs +++ b/Projects/UOContent/Engines/Quests/Core/Items/EnchantedSextant.cs @@ -73,7 +73,11 @@ public partial class EnchantedSextant : Item }; [Constructible] - public EnchantedSextant() : base(0x1058) => Weight = 2.0; + public EnchantedSextant() : base(0x1058) + { + } + + public override double DefaultWeight => 2.0; public override int LabelNumber => 1046226; // an enchanted sextant diff --git a/Projects/UOContent/Engines/Quests/Core/Items/HornOfRetreat.cs b/Projects/UOContent/Engines/Quests/Core/Items/HornOfRetreat.cs index 8bada24bd..2fdfe7146 100644 --- a/Projects/UOContent/Engines/Quests/Core/Items/HornOfRetreat.cs +++ b/Projects/UOContent/Engines/Quests/Core/Items/HornOfRetreat.cs @@ -27,10 +27,11 @@ public partial class HornOfRetreat : Item public HornOfRetreat() : base(0xFC4) { Hue = 0x482; - Weight = 1.0; _charges = 10; } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1049117; // Horn of Retreat public virtual bool ValidateUse(Mobile from) => true; diff --git a/Projects/UOContent/Engines/Quests/Core/QuestSystem.cs b/Projects/UOContent/Engines/Quests/Core/QuestSystem.cs index 2b0ace340..9670f8975 100644 --- a/Projects/UOContent/Engines/Quests/Core/QuestSystem.cs +++ b/Projects/UOContent/Engines/Quests/Core/QuestSystem.cs @@ -693,7 +693,7 @@ namespace Server.Engines.Quests } else { - AddHtml(x, y, width, height, message.ToString().Color(color.C16216()), back, scroll); + AddHtml(x, y, width, height, Html.Color($"{message}", color.C16216()), back, scroll); } } } diff --git a/Projects/UOContent/Engines/Quests/Dark Tides/Items/KronusScroll.cs b/Projects/UOContent/Engines/Quests/Dark Tides/Items/KronusScroll.cs index 76b349706..2b2fe9f3f 100644 --- a/Projects/UOContent/Engines/Quests/Dark Tides/Items/KronusScroll.cs +++ b/Projects/UOContent/Engines/Quests/Dark Tides/Items/KronusScroll.cs @@ -12,11 +12,9 @@ public partial class KronusScroll : QuestItem private static readonly Map m_WellOfTearsMap = Map.Malas; [Constructible] - public KronusScroll() : base(0x227A) - { - Weight = 1.0; - Hue = 0x44E; - } + public KronusScroll() : base(0x227A) => Hue = 0x44E; + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1060149; // Calling of Kronus diff --git a/Projects/UOContent/Engines/Quests/Dark Tides/Items/ScrollOfAbraxus.cs b/Projects/UOContent/Engines/Quests/Dark Tides/Items/ScrollOfAbraxus.cs index 29bbf37e3..a0c90e910 100644 --- a/Projects/UOContent/Engines/Quests/Dark Tides/Items/ScrollOfAbraxus.cs +++ b/Projects/UOContent/Engines/Quests/Dark Tides/Items/ScrollOfAbraxus.cs @@ -8,7 +8,11 @@ namespace Server.Engines.Quests.Necro; public partial class ScrollOfAbraxus : QuestItem { [Constructible] - public ScrollOfAbraxus() : base(0x227B) => Weight = 1.0; + public ScrollOfAbraxus() : base(0x227B) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1028827; // Scroll of Abraxus diff --git a/Projects/UOContent/Engines/Quests/Dark Tides/Mobiles/SummonedPaladin.cs b/Projects/UOContent/Engines/Quests/Dark Tides/Mobiles/SummonedPaladin.cs index b0ac5d41c..6199f3046 100644 --- a/Projects/UOContent/Engines/Quests/Dark Tides/Mobiles/SummonedPaladin.cs +++ b/Projects/UOContent/Engines/Quests/Dark Tides/Mobiles/SummonedPaladin.cs @@ -86,7 +86,7 @@ public partial class SummonedPaladin : BaseCreature Timer.StartTimer(TimeSpan.FromSeconds(5.0), Delete); } - else if (_necromancer.Map != Map || GetDistanceToSqrt(_necromancer) > RangePerception + 1) + else if (_necromancer.Map != Map || this.GetDistanceToSqrt(_necromancer) > RangePerception + 1) { Effects.SendLocationParticles( EffectItem.Create(Location, Map, EffectItem.DefaultDuration), diff --git a/Projects/UOContent/Engines/Quests/Emino's Undertaking/Items/EminosKatana.cs b/Projects/UOContent/Engines/Quests/Emino's Undertaking/Items/EminosKatana.cs index decb00f71..c8ac8f799 100644 --- a/Projects/UOContent/Engines/Quests/Emino's Undertaking/Items/EminosKatana.cs +++ b/Projects/UOContent/Engines/Quests/Emino's Undertaking/Items/EminosKatana.cs @@ -7,7 +7,11 @@ namespace Server.Engines.Quests.Ninja; public partial class EminosKatana : QuestItem { [Constructible] - public EminosKatana() : base(0x13FF) => Weight = 1.0; + public EminosKatana() : base(0x13FF) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1063214; // Daimyo Emino's Katana diff --git a/Projects/UOContent/Engines/Quests/Emino's Undertaking/Items/NoteForZoel.cs b/Projects/UOContent/Engines/Quests/Emino's Undertaking/Items/NoteForZoel.cs index f40372617..6f21e2f76 100644 --- a/Projects/UOContent/Engines/Quests/Emino's Undertaking/Items/NoteForZoel.cs +++ b/Projects/UOContent/Engines/Quests/Emino's Undertaking/Items/NoteForZoel.cs @@ -7,11 +7,9 @@ namespace Server.Engines.Quests.Ninja; public partial class NoteForZoel : QuestItem { [Constructible] - public NoteForZoel() : base(0x14EF) - { - Weight = 1.0; - Hue = 0x6B9; - } + public NoteForZoel() : base(0x14EF) => Hue = 0x6B9; + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1063186; // A Note for Zoel diff --git a/Projects/UOContent/Engines/Quests/Haochi's Trials/Items/HaochisKatana.cs b/Projects/UOContent/Engines/Quests/Haochi's Trials/Items/HaochisKatana.cs index 900e6e0a5..db023a3da 100644 --- a/Projects/UOContent/Engines/Quests/Haochi's Trials/Items/HaochisKatana.cs +++ b/Projects/UOContent/Engines/Quests/Haochi's Trials/Items/HaochisKatana.cs @@ -7,7 +7,11 @@ namespace Server.Engines.Quests.Samurai; public partial class HaochisKatana : QuestItem { [Constructible] - public HaochisKatana() : base(0x13FF) => Weight = 1.0; + public HaochisKatana() : base(0x13FF) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1063165; // Daimyo Haochi's Katana diff --git a/Projects/UOContent/Engines/Quests/The Summoning/Items/GoldenSkull.cs b/Projects/UOContent/Engines/Quests/The Summoning/Items/GoldenSkull.cs index 0fff2f1b1..1a899f766 100644 --- a/Projects/UOContent/Engines/Quests/The Summoning/Items/GoldenSkull.cs +++ b/Projects/UOContent/Engines/Quests/The Summoning/Items/GoldenSkull.cs @@ -8,10 +8,11 @@ public partial class GoldenSkull : Item [Constructible] public GoldenSkull() : base(Utility.Random(0x1AE2, 3)) { - Weight = 1.0; Hue = 0x8A5; LootType = LootType.Blessed; } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1061619; // a golden skull } diff --git a/Projects/UOContent/Engines/Quests/The Summoning/Items/GrandGrimoire.cs b/Projects/UOContent/Engines/Quests/The Summoning/Items/GrandGrimoire.cs index b646070d5..c7ac9bc3b 100644 --- a/Projects/UOContent/Engines/Quests/The Summoning/Items/GrandGrimoire.cs +++ b/Projects/UOContent/Engines/Quests/The Summoning/Items/GrandGrimoire.cs @@ -8,11 +8,12 @@ public partial class GrandGrimoire : Item [Constructible] public GrandGrimoire() : base(0xEFA) { - Weight = 1.0; Hue = 0x835; Layer = Layer.OneHanded; LootType = LootType.Blessed; } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1060801; // The Grand Grimoire } diff --git a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/QuestDaemonBlood.cs b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/QuestDaemonBlood.cs index 0e4b6c729..a65a50073 100644 --- a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/QuestDaemonBlood.cs +++ b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/QuestDaemonBlood.cs @@ -7,7 +7,11 @@ namespace Server.Engines.Quests.Haven; public partial class QuestDaemonBlood : QuestItem { [Constructible] - public QuestDaemonBlood() : base(0xF7D) => Weight = 1.0; + public QuestDaemonBlood() : base(0xF7D) + { + } + + public override double DefaultWeight => 1.0; public override bool CanDrop(PlayerMobile player) => player.Quest is not UzeraanTurmoilQuest; } diff --git a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/QuestDaemonBone.cs b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/QuestDaemonBone.cs index 72fdff2f7..9da029d1c 100644 --- a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/QuestDaemonBone.cs +++ b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/QuestDaemonBone.cs @@ -7,7 +7,11 @@ namespace Server.Engines.Quests.Haven; public partial class QuestDaemonBone : QuestItem { [Constructible] - public QuestDaemonBone() : base(0xF80) => Weight = 1.0; + public QuestDaemonBone() : base(0xF80) + { + } + + public override double DefaultWeight => 1.0; public override bool CanDrop(PlayerMobile player) => player.Quest is not UzeraanTurmoilQuest; } diff --git a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/QuestFertileDirt.cs b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/QuestFertileDirt.cs index e29e91f82..c51e6a9dc 100644 --- a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/QuestFertileDirt.cs +++ b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/QuestFertileDirt.cs @@ -7,7 +7,11 @@ namespace Server.Engines.Quests.Haven; public partial class QuestFertileDirt : QuestItem { [Constructible] - public QuestFertileDirt() : base(0xF81) => Weight = 1.0; + public QuestFertileDirt() : base(0xF81) + { + } + + public override double DefaultWeight => 1.0; public override bool CanDrop(PlayerMobile player) => player.Quest is not UzeraanTurmoilQuest; } diff --git a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/SchmendrickScrollOfPower.cs b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/SchmendrickScrollOfPower.cs index ba69b74b5..87eb5c8af 100644 --- a/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/SchmendrickScrollOfPower.cs +++ b/Projects/UOContent/Engines/Quests/Uzeraan Turmoil/Items/SchmendrickScrollOfPower.cs @@ -6,11 +6,9 @@ namespace Server.Engines.Quests.Haven; [SerializationGenerator(0, false)] public partial class SchmendrickScrollOfPower : QuestItem { - public SchmendrickScrollOfPower() : base(0xE34) - { - Weight = 1.0; - Hue = 0x34D; - } + public SchmendrickScrollOfPower() : base(0xE34) => Hue = 0x34D; + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1049118; // a scroll with ancient markings diff --git a/Projects/UOContent/Engines/Quests/Witch Apprentice/Items/Cauldron.cs b/Projects/UOContent/Engines/Quests/Witch Apprentice/Items/Cauldron.cs index 443ab6a69..2658b7070 100644 --- a/Projects/UOContent/Engines/Quests/Witch Apprentice/Items/Cauldron.cs +++ b/Projects/UOContent/Engines/Quests/Witch Apprentice/Items/Cauldron.cs @@ -6,7 +6,11 @@ namespace Server.Items; public partial class Cauldron : Item { [Constructible] - public Cauldron() : base(0x9ED) => Weight = 1.0; + public Cauldron() : base(0x9ED) + { + } + + public override double DefaultWeight => 1.0; public override string DefaultName => "a cauldron"; } diff --git a/Projects/UOContent/Engines/Quests/Witch Apprentice/Items/HangoverCure.cs b/Projects/UOContent/Engines/Quests/Witch Apprentice/Items/HangoverCure.cs index 633607aa8..edb838167 100644 --- a/Projects/UOContent/Engines/Quests/Witch Apprentice/Items/HangoverCure.cs +++ b/Projects/UOContent/Engines/Quests/Witch Apprentice/Items/HangoverCure.cs @@ -13,12 +13,13 @@ public partial class HangoverCure : Item [Constructible] public HangoverCure() : base(0xE2B) { - Weight = 1.0; Hue = 0x2D; _uses = 20; } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1055060; // Grizelda's Extra Strength Hangover Cure public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Engines/Quests/Witch Apprentice/Items/MoonfireBrew.cs b/Projects/UOContent/Engines/Quests/Witch Apprentice/Items/MoonfireBrew.cs index 0a72e067d..4040cb35c 100644 --- a/Projects/UOContent/Engines/Quests/Witch Apprentice/Items/MoonfireBrew.cs +++ b/Projects/UOContent/Engines/Quests/Witch Apprentice/Items/MoonfireBrew.cs @@ -6,7 +6,11 @@ namespace Server.Engines.Quests.Hag; public partial class MoonfireBrew : Item { [Constructible] - public MoonfireBrew() : base(0xF04) => Weight = 1.0; + public MoonfireBrew() : base(0xF04) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1055065; // a bottle of magical moonfire brew } diff --git a/Projects/UOContent/Engines/Spawners/SpawnerGump.cs b/Projects/UOContent/Engines/Spawners/SpawnerGump.cs index e7f9cf1be..a589c6f76 100644 --- a/Projects/UOContent/Engines/Spawners/SpawnerGump.cs +++ b/Projects/UOContent/Engines/Spawners/SpawnerGump.cs @@ -167,8 +167,8 @@ public class SpawnerGump : Gump totalWeight += spawnerEntry.SpawnedProbability; } - AddHtml(270, 308 + offset, 35, 20, totalSpawns.ToString().Center(0xF4F4F4)); - AddHtml(308, 308 + offset, 35, 20, totalWeight.ToString().Center(0xF4F4F4)); + AddHtml(270, 308 + offset, 35, 20, Html.Center($"{totalSpawns}", 0xF4F4F4)); + AddHtml(308, 308 + offset, 35, 20, Html.Center($"{totalWeight}",0xF4F4F4)); AddHtml(5, 1, 161, 20, $"{spawner.Name} ({totalSpawned}/{spawner.Count})"); diff --git a/Projects/UOContent/Engines/Treasures of Tokuno/BasePigmentsOfTokuno.cs b/Projects/UOContent/Engines/Treasures of Tokuno/BasePigmentsOfTokuno.cs index 9f63e9722..803a88a5d 100644 --- a/Projects/UOContent/Engines/Treasures of Tokuno/BasePigmentsOfTokuno.cs +++ b/Projects/UOContent/Engines/Treasures of Tokuno/BasePigmentsOfTokuno.cs @@ -72,13 +72,11 @@ public abstract partial class BasePigmentsOfTokuno : Item, IUsesRemaining public BasePigmentsOfTokuno() : base(0xEFF) { - Weight = 1.0; _usesRemaining = 1; } public BasePigmentsOfTokuno(int uses) : base(0xEFF) { - Weight = 1.0; _usesRemaining = uses; } @@ -86,6 +84,8 @@ public abstract partial class BasePigmentsOfTokuno : Item, IUsesRemaining { } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1070933; // Pigments of Tokuno protected TextDefinition Label diff --git a/Projects/UOContent/Engines/Treasures of Tokuno/GreaterArtifacts.cs b/Projects/UOContent/Engines/Treasures of Tokuno/GreaterArtifacts.cs index a9fa2d7ca..005688e1e 100644 --- a/Projects/UOContent/Engines/Treasures of Tokuno/GreaterArtifacts.cs +++ b/Projects/UOContent/Engines/Treasures of Tokuno/GreaterArtifacts.cs @@ -330,11 +330,7 @@ public partial class PigmentsOfTokuno : BasePigmentsOfTokuno } [Constructible] - public PigmentsOfTokuno(PigmentType type, int uses) : base(uses) - { - Weight = 1.0; - Type = type; - } + public PigmentsOfTokuno(PigmentType type, int uses) : base(uses) => Type = type; [SerializableProperty(0)] [CommandProperty(AccessLevel.GameMaster)] diff --git a/Projects/UOContent/Engines/Treasures of Tokuno/LesserArtifacts.cs b/Projects/UOContent/Engines/Treasures of Tokuno/LesserArtifacts.cs index 19ea73319..54d026df4 100644 --- a/Projects/UOContent/Engines/Treasures of Tokuno/LesserArtifacts.cs +++ b/Projects/UOContent/Engines/Treasures of Tokuno/LesserArtifacts.cs @@ -354,11 +354,9 @@ public partial class AncientUrn : Item } [Constructible] - public AncientUrn(string urnName) : base(0x241D) - { - _urnName = urnName; - Weight = 1.0; - } + public AncientUrn(string urnName) : base(0x241D) => _urnName = urnName; + + public override double DefaultWeight => 1.0; public static string[] Names { get; } = { @@ -432,12 +430,9 @@ public partial class HonorableSwords : Item } [Constructible] - public HonorableSwords(string swordsName) : base(0x2853) - { - _swordsName = swordsName; + public HonorableSwords(string swordsName) : base(0x2853) => _swordsName = swordsName; - Weight = 5.0; - } + public override double DefaultWeight => 5.0; public override int LabelNumber => 1071015; // Honorable Swords @@ -620,11 +615,7 @@ public partial class LesserPigmentsOfTokuno : BasePigmentsOfTokuno } [Constructible] - public LesserPigmentsOfTokuno(LesserPigmentType type) : base(1) - { - Weight = 1.0; - Type = type; - } + public LesserPigmentsOfTokuno(LesserPigmentType type) : base(1) => Type = type; [SerializableProperty(0)] [CommandProperty(AccessLevel.GameMaster)] diff --git a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatue.cs b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatue.cs index 5a8b128ab..fb08fb590 100644 --- a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatue.cs +++ b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatue.cs @@ -419,9 +419,10 @@ public partial class CharacterStatueDeed : Item, IRewardItem } LootType = LootType.Blessed; - Weight = 1.0; } + public override double DefaultWeight => 1.0; + public override int LabelNumber { get diff --git a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatueMaker.cs b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatueMaker.cs index 0e8ea37b6..56536d4d7 100644 --- a/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatueMaker.cs +++ b/Projects/UOContent/Engines/Veteran Rewards/Character Statue Maker/CharacterStatueMaker.cs @@ -19,9 +19,10 @@ public partial class CharacterStatueMaker : Item, IRewardItem InvalidateHue(); LootType = LootType.Blessed; - Weight = 5.0; } + public override double DefaultWeight => 5.0; + public override int LabelNumber => 1076173; // Character Statue Maker [SerializableProperty(1)] diff --git a/Projects/UOContent/Gumps/AdminGump.cs b/Projects/UOContent/Gumps/AdminGump.cs index fa22ceca6..7dc944e82 100644 --- a/Projects/UOContent/Gumps/AdminGump.cs +++ b/Projects/UOContent/Gumps/AdminGump.cs @@ -92,12 +92,12 @@ namespace Server.Gumps AddPage(0); - AddBackground(0, 0, 420, 440, 5054); + AddBackground(0, 0, 420, 480, 5054); AddBlackAlpha(10, 10, 170, 100); AddBlackAlpha(190, 10, 220, 100); - AddBlackAlpha(10, 120, 400, 260); - AddBlackAlpha(10, 390, 400, 40); + AddBlackAlpha(10, 120, 400, 300); + AddBlackAlpha(10, 430, 400, 40); AddPageButton( 10, @@ -141,7 +141,7 @@ namespace Server.Gumps if (notice != null) { - AddHtml(12, 392, 396, 36, notice.Color(LabelColor32)); + AddHtml(20, 440, 396, 36, notice.Color(LabelColor32)); } switch (pageType) @@ -619,6 +619,14 @@ namespace Server.Gumps AddButtonLabeled(20, y, GetButtonID(7, 12), "Kill"); AddButtonLabeled(200, y, GetButtonID(7, 13), "Resurrect"); + y += 20; + + AddButtonLabeled(20, y, GetButtonID(7, 15), "Jail"); + AddButtonLabeled(200, y, GetButtonID(7, 16), "Unjail"); + y += 25; + + AddLabel(20, y, LabelHue, "Jail Reason:"); + AddTextField(100, y, 300, 20, 1); break; } @@ -1027,7 +1035,7 @@ namespace Server.Gumps for (int i = 0, index = listPage * 6; i < 6 && index >= 0 && index < m_List.Count; ++i, ++index) { - AddHtml(18, 243 + i * 22, 114, 20, m_List[index].ToString().Color(LabelColor32)); + AddHtml(18, 243 + i * 22, 114, 20, Html.Color($"{m_List[index]}", LabelColor32)); AddButton(130, 242 + i * 22, 0xFA2, 0xFA4, GetButtonID(8, index)); AddButton(160, 242 + i * 22, 0xFA8, 0xFAA, GetButtonID(9, index)); AddButton(190, 242 + i * 22, 0xFB1, 0xFB3, GetButtonID(10, index)); @@ -1229,7 +1237,7 @@ namespace Server.Gumps break; } - AddHtml(10, 125, 400, 20, firewallEntry.ToString().Center(LabelColor32)); + AddHtml(10, 125, 400, 20, Html.Center($"{firewallEntry}", LabelColor32)); AddButtonLabeled(20, 150, GetButtonID(6, 3), "Remove"); @@ -3801,6 +3809,33 @@ namespace Server.Gumps sendGump = false; break; } + case 15: + { + var reason = info.GetTextEntry(1)?.Trim(); + + if (string.IsNullOrWhiteSpace(reason)) + { + reason = ""; + } + + CommandLogging.WriteLine( + from, + $"{from.AccessLevel} {CommandLogging.Format(from)} jailing {CommandLogging.Format(m)} - Reason: {reason}" + ); + InvokeCommand($"Jail {m.Name} \"{reason}\""); + notice = $"Player has been sent to jail. Reason: {reason}"; + break; + } + case 16: + { + CommandLogging.WriteLine( + from, + $"{from.AccessLevel} {CommandLogging.Format(from)} unjailing {CommandLogging.Format(m)}" + ); + InvokeCommand($"Unjail {m.Name}"); + notice = "Player has been unjailed."; + break; + } } if (sendGump) @@ -4060,15 +4095,15 @@ namespace Server.Gumps { if (x is not KeyValuePair> a) { - return -1; + return 1; } if (y is not KeyValuePair> b) { - return 1; + return -1; } - return a.Value.Count - b.Value.Count; + return b.Value.Count - a.Value.Count; } } diff --git a/Projects/UOContent/Gumps/Base/DynamicGumpBuilder.cs b/Projects/UOContent/Gumps/Base/DynamicGumpBuilder.cs index afe345bb9..198ac16fa 100644 --- a/Projects/UOContent/Gumps/Base/DynamicGumpBuilder.cs +++ b/Projects/UOContent/Gumps/Base/DynamicGumpBuilder.cs @@ -78,114 +78,33 @@ public ref struct DynamicGumpBuilder [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddHtml( - int x, - int y, - int width, - int height, - ReadOnlySpan text, - bool background = false, - bool scrollbar = false + int x, int y, int width, int height, ReadOnlySpan text, ReadOnlySpan color = default, int size = -1, byte fontStyle = 0, + TextAlignment align = TextAlignment.Left, bool background = false, bool scrollbar = false ) { - WriteInternalizedString(text); + if (color.Length > 0 || size != -1 || fontStyle != 0 || align != TextAlignment.Left) + { + var arr = STArrayPool.Shared.Rent(Html.BuildCharCount(text, color)); + var bytesWritten = Html.Build(text, arr.AsSpan(), color, size, fontStyle, align); + + WriteInternalizedString(arr.AsSpan(0, bytesWritten)); + STArrayPool.Shared.Return(arr); + } + else + { + WriteInternalizedString(text); + } + _gumpBuilder.AddHtml(x, y, width, height, _stringsCount++, background, scrollbar); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddHtml( - int x, int y, int width, int height, ref RawInterpolatedStringHandler handler, - bool background = false, bool scrollbar = false + int x, int y, int width, int height, ref RawInterpolatedStringHandler handler, ReadOnlySpan color = default, int size = -1, + byte fontStyle = 0, TextAlignment align = TextAlignment.Left, bool background = false, bool scrollbar = false ) { - AddHtml(x, y, width, height, handler.Text, background, scrollbar); - handler.Clear(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddHtml( - int x, - int y, - int width, - int height, - int color, - ReadOnlySpan text, - bool background = false, - bool scrollbar = false - ) - { - var handler = text.Color(color); - AddHtml(x, y, width, height, ref handler, background, scrollbar); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddHtml( - int x, - int y, - int width, - int height, - int color, - ref RawInterpolatedStringHandler handler, - bool background = false, - bool scrollbar = false - ) - { - AddHtml(x, y, width, height, color, handler.Text, background, scrollbar); - handler.Clear(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddHtmlCentered( - int x, int y, int width, int height, ReadOnlySpan text, bool background = false, bool scrollbar = false - ) - { - var handler = text.Center(); - AddHtml(x, y, width, height, ref handler, background, scrollbar); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddHtmlCentered( - int x, - int y, - int width, - int height, - ref RawInterpolatedStringHandler handler, - bool background = false, - bool scrollbar = false - ) - { - AddHtmlCentered(x, y, width, height, handler.Text, background, scrollbar); - handler.Clear(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddHtmlCentered( - int x, - int y, - int width, - int height, - int color, - ReadOnlySpan text, - bool background = false, - bool scrollbar = false - ) - { - var handler = text.Center(color); - AddHtml(x, y, width, height, ref handler, background, scrollbar); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddHtmlCentered( - int x, - int y, - int width, - int height, - int color, - ref RawInterpolatedStringHandler handler, - bool background = false, - bool scrollbar = false - ) - { - AddHtmlCentered(x, y, width, height, color, handler.Text, background, scrollbar); + AddHtml(x, y, width, height, handler.Text, color, size, fontStyle, align, background, scrollbar); handler.Clear(); } @@ -201,14 +120,7 @@ public ref struct DynamicGumpBuilder [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddHtmlLocalized( - int x, - int y, - int width, - int height, - int number, - ReadOnlySpan args, - int color, - bool background = false, + int x, int y, int width, int height, int number, ReadOnlySpan args, int color, bool background = false, bool scrollbar = false ) => _gumpBuilder.AddHtmlLocalized(x, y, width, height, number, args, color, background, scrollbar); @@ -232,18 +144,8 @@ public ref struct DynamicGumpBuilder [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddImageTiledButton( - int x, - int y, - int normalId, - int pressedId, - int buttonId, - GumpButtonType type, - int param, - int itemId, - int hue, - int width, - int height, - int localizedTooltip = -1 + int x, int y, int normalId, int pressedId, int buttonId, GumpButtonType type, int param, int itemId, int hue, + int width, int height, int localizedTooltip = -1 ) => _gumpBuilder.AddImageTiledButton( x, y, normalId, pressedId, buttonId, type, param, itemId, hue, width, height, localizedTooltip ); diff --git a/Projects/UOContent/Gumps/Base/Grid/Grid.cs b/Projects/UOContent/Gumps/Base/Grid/Grid.cs deleted file mode 100644 index 7dd7a5372..000000000 --- a/Projects/UOContent/Gumps/Base/Grid/Grid.cs +++ /dev/null @@ -1,77 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2023 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: Grid.cs * - * * - * This program is free software: you can redistribute it and/or modify * - * it under the terms of the GNU General Public License as published by * - * the Free Software Foundation, either version 3 of the License, or * - * (at your option) any later version. * - * * - * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ - -using System.Collections.Generic; - -namespace Server.Gumps; - -public enum GridHues -{ - White = 2049, // Change to 0x480 if you have old hues.mul - Black = 0, -} - -public static class GridColors -{ - public const string Gold = "#ffc959"; - public const string White = "#ffffff"; - public const string Blue = "#1D63DC"; - public const string LightBlue = "#7799EE"; - public const string Green = "#80E732"; - public const string Orange = "#F28B00"; - public const string Purple = "#F3ACF6"; - public const string Red = "#B52B2D"; - public const string LightGreen = "#E6FFC0"; - public const string Yellow = "#FFFFBB"; -} - -public class ListItem -{ - public int X { get; set; } - public int Y { get; set; } - public int Index { get; set; } - public Col[] Cols { get; set; } -} - -public class Col -{ - public int X { get; set; } - public int Width { get; set; } - public int HCenter => X + Width / 2; - public int HEnd => X + Width; -} - -public class Row -{ - public int Y { get; set; } - public int Height { get; set; } - public int VCenter => Y + Height / 2; - public int VEnd => Y + Height; -} - -public class Grid -{ - public string Name { get; set; } - public int Height { get; set; } - public int Width { get; set; } - public List Columns { get; set; } = new(); - public List Rows { get; set; } = new(); -} - -public class Swap -{ - public int Index { get; set; } - public int Size { get; set; } -} diff --git a/Projects/UOContent/Gumps/Base/GumpStringsBuilder.cs b/Projects/UOContent/Gumps/Base/GumpStringsBuilder.cs index 0e818f161..a5d8de052 100644 --- a/Projects/UOContent/Gumps/Base/GumpStringsBuilder.cs +++ b/Projects/UOContent/Gumps/Base/GumpStringsBuilder.cs @@ -66,78 +66,46 @@ public ref struct GumpStringsBuilder [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SetHtmlText( - ref RawInterpolatedStringHandler slotKeyHandler, ref RawInterpolatedStringHandler handler, int color, int size = -1, - byte fontStyle = 0 + ref RawInterpolatedStringHandler slotKeyHandler, ref RawInterpolatedStringHandler handler, ReadOnlySpan color = default, int size = -1, + byte fontStyle = 0, TextAlignment align = TextAlignment.Left ) { - SetHtmlText(ref slotKeyHandler, handler.Text, color, size, fontStyle); + SetHtmlText(slotKeyHandler.Text, handler.Text, color, size, fontStyle, align); + slotKeyHandler.Clear(); handler.Clear(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SetHtmlText( - ref RawInterpolatedStringHandler slotKeyHandler, ReadOnlySpan value, int color, int size = -1, byte fontStyle = 0 + ref RawInterpolatedStringHandler slotKeyHandler, ReadOnlySpan value, ReadOnlySpan color = default, int size = -1, byte fontStyle = 0, + TextAlignment align = TextAlignment.Left ) { - SetHtmlText(slotKeyHandler.Text, value, color, size, fontStyle); + SetHtmlText(slotKeyHandler.Text, value, color, size, fontStyle, align); slotKeyHandler.Clear(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SetHtmlText( - ReadOnlySpan slotKey, ref RawInterpolatedStringHandler handler, int color, int size = -1, byte fontStyle = 0 + ReadOnlySpan slotKey, ref RawInterpolatedStringHandler handler, ReadOnlySpan color = default, int size = -1, byte fontStyle = 0, + TextAlignment align = TextAlignment.Left ) { - SetHtmlText(slotKey, handler.Text, color, size, fontStyle); + SetHtmlText(slotKey, handler.Text, color, size, fontStyle, align); handler.Clear(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void SetHtmlText( - ReadOnlySpan slotKey, ReadOnlySpan value, int color, int size = -1, byte fontStyle = 0 + ReadOnlySpan slotKey, ReadOnlySpan value, ReadOnlySpan color = default, int size = -1, byte fontStyle = 0, + TextAlignment align = TextAlignment.Left ) { - var coloredTextHandler = value.Color(color, size, fontStyle); - SetStringSlot(slotKey, ref coloredTextHandler); - } + var arr = STArrayPool.Shared.Rent(Html.BuildCharCount(value, color)); + var bytesWritten = Html.Build(value, arr.AsSpan(), color, size, fontStyle, align); - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetHtmlTextCentered( - ref RawInterpolatedStringHandler slotKeyHandler, ref RawInterpolatedStringHandler handler, int color = -1, - int size = -1, byte fontStyle = 0 - ) - { - SetHtmlTextCentered(ref slotKeyHandler, handler.Text, color, size, fontStyle); - handler.Clear(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetHtmlTextCentered( - ref RawInterpolatedStringHandler slotKeyHandler, ReadOnlySpan value, int color = -1, int size = -1, - byte fontStyle = 0 - ) - { - SetHtmlTextCentered(slotKeyHandler.Text, value, color, size, fontStyle); - slotKeyHandler.Clear(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetHtmlTextCentered( - ReadOnlySpan slotKey, ref RawInterpolatedStringHandler handler, int color = -1, int size = -1, - byte fontStyle = 0 - ) - { - SetHtmlTextCentered(slotKey, handler.Text, color, size, fontStyle); - handler.Clear(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void SetHtmlTextCentered( - ReadOnlySpan slotKey, ReadOnlySpan value, int color = -1, int size = -1, byte fontStyle = 0 - ) - { - var coloredTextHandler = value.Center(color, size, fontStyle); - SetStringSlot(slotKey, ref coloredTextHandler); + SetStringSlot(slotKey, arr.AsSpan(0, bytesWritten)); + STArrayPool.Shared.Return(arr); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/Projects/UOContent/Gumps/Base/Legacy/Gump.cs b/Projects/UOContent/Gumps/Base/Legacy/Gump.cs index 0bcd9dd3c..bf6928744 100644 --- a/Projects/UOContent/Gumps/Base/Legacy/Gump.cs +++ b/Projects/UOContent/Gumps/Base/Legacy/Gump.cs @@ -169,7 +169,7 @@ public class Gump : BaseGump public void AddLabelHtml(int x, int y, int width, int height, string text, string hue, int size = 4, bool center = true) { - AddHtml(x, y, width, height, center ? text.Center(hue, size) : text.Color(hue, size)); + AddHtml(x, y, width, height, Html.Build(text, hue, size, align: center ? TextAlignment.Center : TextAlignment.Left)); } public void AddLabelCropped(int x, int y, int width, int height, int hue, string text) diff --git a/Projects/UOContent/Gumps/Base/Grid/GumpGrid.cs b/Projects/UOContent/Gumps/Base/Legacy/GumpGrid.cs similarity index 90% rename from Projects/UOContent/Gumps/Base/Grid/GumpGrid.cs rename to Projects/UOContent/Gumps/Base/Legacy/GumpGrid.cs index 6b5e80b05..dfa0dcc78 100644 --- a/Projects/UOContent/Gumps/Base/Grid/GumpGrid.cs +++ b/Projects/UOContent/Gumps/Base/Legacy/GumpGrid.cs @@ -1,6 +1,6 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2023 - ModernUO Development Team * + * Copyright 2019-2025 - ModernUO Development Team * * Email: hi@modernuo.com * * File: GumpGrid.cs * * * @@ -523,3 +523,63 @@ public class GumpGrid : Gump return null; } } + +public enum GridHues +{ + White = 2049, // Change to 0x480 if you have old hues.mul + Black = 0, +} + +public static class GridColors +{ + public const string Gold = "#ffc959"; + public const string White = "#ffffff"; + public const string Blue = "#1D63DC"; + public const string LightBlue = "#7799EE"; + public const string Green = "#80E732"; + public const string Orange = "#F28B00"; + public const string Purple = "#F3ACF6"; + public const string Red = "#B52B2D"; + public const string LightGreen = "#E6FFC0"; + public const string Yellow = "#FFFFBB"; +} + +public class ListItem +{ + public int X { get; set; } + public int Y { get; set; } + public int Index { get; set; } + public Col[] Cols { get; set; } +} + +public class Col +{ + public int X { get; set; } + public int Width { get; set; } + public int HCenter => X + Width / 2; + public int HEnd => X + Width; +} + +public class Row +{ + public int Y { get; set; } + public int Height { get; set; } + public int VCenter => Y + Height / 2; + public int VEnd => Y + Height; +} + +public class Grid +{ + public string Name { get; set; } + public int Height { get; set; } + public int Width { get; set; } + public List Columns { get; set; } = new(); + public List Rows { get; set; } = new(); +} + +public class Swap +{ + public int Index { get; set; } + public int Size { get; set; } +} + diff --git a/Projects/UOContent/Gumps/Base/StaticGumpBuilder.cs b/Projects/UOContent/Gumps/Base/StaticGumpBuilder.cs index 5e7702dab..00439ceda 100644 --- a/Projects/UOContent/Gumps/Base/StaticGumpBuilder.cs +++ b/Projects/UOContent/Gumps/Base/StaticGumpBuilder.cs @@ -91,13 +91,7 @@ public ref struct StaticGumpBuilder [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddHtmlPlaceholder( - int x, - int y, - int width, - int height, - ReadOnlySpan slotKey, - bool background = false, - bool scrollbar = false + int x, int y, int width, int height, ReadOnlySpan slotKey, bool background = false, bool scrollbar = false ) { var index = _gumpBuilder.AddHtmlPlaceholder(x, y, width, height, background, scrollbar); @@ -106,12 +100,7 @@ public ref struct StaticGumpBuilder [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddHtmlPlaceholder( - int x, - int y, - int width, - int height, - ref RawInterpolatedStringHandler handler, - bool background = false, + int x, int y, int width, int height, ref RawInterpolatedStringHandler handler, bool background = false, bool scrollbar = false ) { @@ -121,115 +110,33 @@ public ref struct StaticGumpBuilder [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddHtml( - int x, - int y, - int width, - int height, - ReadOnlySpan text, - bool background = false, - bool scrollbar = false + int x, int y, int width, int height, ReadOnlySpan text, ReadOnlySpan color = default, int size = -1, byte fontStyle = 0, + TextAlignment align = TextAlignment.Left, bool background = false, bool scrollbar = false ) { - WriteInternalizedString(text); + if (color.Length > 0 || size != -1 || fontStyle != 0 || align != TextAlignment.Left) + { + var arr = STArrayPool.Shared.Rent(Html.BuildCharCount(text, color)); + var bytesWritten = Html.Build(text, arr.AsSpan(), color, size, fontStyle, align); + + WriteInternalizedString(arr.AsSpan(0, bytesWritten)); + STArrayPool.Shared.Return(arr); + } + else + { + WriteInternalizedString(text); + } + _gumpBuilder.AddHtml(x, y, width, height, _stringsCount++, background, scrollbar); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddHtml( - int x, int y, int width, int height, ref RawInterpolatedStringHandler handler, - bool background = false, bool scrollbar = false + int x, int y, int width, int height, ref RawInterpolatedStringHandler handler, ReadOnlySpan color = default, int size = -1, + byte fontStyle = 0, TextAlignment align = TextAlignment.Left, bool background = false, bool scrollbar = false ) { - AddHtml(x, y, width, height, handler.Text, background, scrollbar); - handler.Clear(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddHtml( - int x, - int y, - int width, - int height, - int color, - ReadOnlySpan text, - bool background = false, - bool scrollbar = false - ) - { - var handler = text.Color(color); - AddHtml(x, y, width, height, ref handler, background, scrollbar); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddHtml( - int x, - int y, - int width, - int height, - int color, - ref RawInterpolatedStringHandler handler, - bool background = false, - bool scrollbar = false - ) - { - AddHtml(x, y, width, height, color, handler.Text, background, scrollbar); - handler.Clear(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddHtmlCentered( - int x, int y, int width, int height, ReadOnlySpan text, bool background = false, bool scrollbar = false - ) - { - var handler = text.Center(); - AddHtml(x, y, width, height, ref handler, background, scrollbar); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddHtmlCentered( - int x, - int y, - int width, - int height, - ref RawInterpolatedStringHandler handler, - bool background = false, - bool scrollbar = false - ) - { - var centerHandler = handler.Text.Center(); - AddHtml(x, y, width, height, ref centerHandler, background, scrollbar); - handler.Clear(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddHtmlCentered( - int x, - int y, - int width, - int height, - int color, - ReadOnlySpan text, - bool background = false, - bool scrollbar = false - ) - { - var handler = text.Center(color); - AddHtml(x, y, width, height, ref handler, background, scrollbar); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void AddHtmlCentered( - int x, - int y, - int width, - int height, - int color, - ref RawInterpolatedStringHandler handler, - bool background = false, - bool scrollbar = false - ) - { - AddHtmlCentered(x, y, width, height, color, handler.Text, background, scrollbar); + AddHtml(x, y, width, height, handler.Text, color, size, fontStyle, align, background, scrollbar); handler.Clear(); } diff --git a/Projects/UOContent/Gumps/ClientGump.cs b/Projects/UOContent/Gumps/ClientGump.cs index 6fdd0a84e..d2974f6cf 100644 --- a/Projects/UOContent/Gumps/ClientGump.cs +++ b/Projects/UOContent/Gumps/ClientGump.cs @@ -36,7 +36,7 @@ namespace Server.Gumps var line = 0; AddHtml(14, 36 + line * 20, 200, 20, "Address:".Color(LabelColor32)); - AddHtml(70, 36 + line++ * 20, 200, 20, state.ToString().Color(LabelColor32)); + AddHtml(70, 36 + line++ * 20, 200, 20, Html.Color($"{state}", LabelColor32)); AddHtml(14, 36 + line * 20, 200, 20, "Client:".Color(LabelColor32)); AddHtml( @@ -75,10 +75,10 @@ namespace Server.Gumps if (m != null) { AddHtml(14, 36 + line * 20, 200, 20, "Mobile:".Color(LabelColor32)); - AddHtml(72, 36 + line++ * 20, 200, 20, $"{m.Name} ({m.Serial})".Color(LabelColor32)); + AddHtml(72, 36 + line++ * 20, 200, 20, Html.Color($"{m.Name} ({m.Serial})", LabelColor32)); AddHtml(14, 36 + line * 20, 200, 20, "Location:".Color(LabelColor32)); - AddHtml(72, 36 + line++ * 20, 200, 20, $"{m.Location} [{m.Map}]".Color(LabelColor32)); + AddHtml(72, 36 + line++ * 20, 200, 20, Html.Color($"{m.Location} [{m.Map}]", LabelColor32)); AddButton(13, 197, 0xFAB, 0xFAD, 1); AddHtml(48, 198, 200, 20, "Send Message".Color(LabelColor32)); diff --git a/Projects/UOContent/Gumps/Guilds/New Guild System/OtherGuildInfo.cs b/Projects/UOContent/Gumps/Guilds/New Guild System/OtherGuildInfo.cs index efccd97cd..df1b8a36e 100644 --- a/Projects/UOContent/Gumps/Guilds/New Guild System/OtherGuildInfo.cs +++ b/Projects/UOContent/Gumps/Guilds/New Guild System/OtherGuildInfo.cs @@ -85,13 +85,13 @@ namespace Server.Guilds } else if (PendingWar) { - kills = $"{war.Kills}/{war.MaxKills}".Color(0x990000); - time = $"{war.WarLength.Hours:D2}:{DateTime.MinValue + war.WarLength:mm}".Color(0x990000); + kills = Html.Color($"{war.Kills}/{war.MaxKills}", 0x990000); + time = Html.Color($"{war.WarLength.Hours:D2}:{DateTime.MinValue + war.WarLength:mm}", 0x990000); otherWar = m_Other.FindPendingWar(guild); if (otherWar != null) { - otherKills = $"{otherWar.Kills}/{otherWar.MaxKills}".Color(0x990000); + otherKills = Html.Color($"{otherWar.Kills}/{otherWar.MaxKills}", 0x990000); } } diff --git a/Projects/UOContent/Gumps/Houses/HouseGumpAOS.cs b/Projects/UOContent/Gumps/Houses/HouseGumpAOS.cs index 3a159286f..bd4a6ce7b 100644 --- a/Projects/UOContent/Gumps/Houses/HouseGumpAOS.cs +++ b/Projects/UOContent/Gumps/Houses/HouseGumpAOS.cs @@ -870,7 +870,7 @@ namespace Server.Gumps } var items = house.GetItems(); - var mobiles = house.GetMobiles(); + using var mobiles = house.GetMobilesPooled(); newHouse.MoveToWorld( new Point3D( @@ -1216,7 +1216,7 @@ namespace Server.Gumps ); var r = m_House.Region; - var list = r.GetMobiles(); + using var list = r.GetMobilesPooled(); for (var i = 0; i < list.Count; ++i) { @@ -1258,7 +1258,7 @@ namespace Server.Gumps } var r = m_House.Region; - var list = r.GetMobiles(); + using var list = r.GetMobilesPooled(); for (var i = 0; i < list.Count; ++i) { diff --git a/Projects/UOContent/Gumps/PetResurrectGump.cs b/Projects/UOContent/Gumps/PetResurrectGump.cs index 103ed8255..75400b58d 100644 --- a/Projects/UOContent/Gumps/PetResurrectGump.cs +++ b/Projects/UOContent/Gumps/PetResurrectGump.cs @@ -39,7 +39,7 @@ public class PetResurrectGump : StaticGump protected override void BuildStrings(ref GumpStringsBuilder builder) { - builder.SetHtmlTextCentered("petName", _pet.Name); + builder.SetHtmlText("petName", _pet.Name, align: TextAlignment.Center); } public override void OnResponse(NetState state, in RelayInfo info) diff --git a/Projects/UOContent/Gumps/ResurrectGump.cs b/Projects/UOContent/Gumps/ResurrectGump.cs index 6612db956..44af971d0 100644 --- a/Projects/UOContent/Gumps/ResurrectGump.cs +++ b/Projects/UOContent/Gumps/ResurrectGump.cs @@ -176,7 +176,14 @@ public class ResurrectGump : DynamicGump public override void OnResponse(NetState state, in RelayInfo info) { - if (info.ButtonID is not 1 and not 2) + if (_price > 0) + { + if (info.ButtonID != 2 || info.Switches[0] != 1) + { + return; + } + } + else if (info.ButtonID != 1) { return; } diff --git a/Projects/UOContent/Gumps/StaticWarningGump.cs b/Projects/UOContent/Gumps/StaticWarningGump.cs index 1129fb36b..ee5fc020e 100644 --- a/Projects/UOContent/Gumps/StaticWarningGump.cs +++ b/Projects/UOContent/Gumps/StaticWarningGump.cs @@ -7,13 +7,14 @@ public abstract class StaticWarningGump : StaticGump where T : StaticWarni { public virtual int Header => 1060635; //
WARNING
public virtual int HeaderColor => 0x7800; - public virtual int ContentColor => StaticLocalizedContent > 0 ? 0x7F00 : 0xFFC000; + public virtual string ContentColor => "#FFC000"; public abstract int Width { get; } public abstract int Height { get; } public virtual bool CancelButton => true; // If this is overridden, then the content will be localized and cached. public virtual int StaticLocalizedContent => 0; + public virtual int StaticLocalizedContentColor => 0x7F00; public virtual string Content => null; @@ -55,7 +56,7 @@ public abstract class StaticWarningGump : StaticGump where T : StaticWarni width - 20, height - 80, StaticLocalizedContent, - (short)ContentColor, + StaticLocalizedContentColor, false, true ); diff --git a/Projects/UOContent/Gumps/ViewHousesGump.cs b/Projects/UOContent/Gumps/ViewHousesGump.cs index 12082c6b1..41861645b 100644 --- a/Projects/UOContent/Gumps/ViewHousesGump.cs +++ b/Projects/UOContent/Gumps/ViewHousesGump.cs @@ -64,7 +64,7 @@ namespace Server.Gumps var name = FindHouseName(list[i]); - AddHtml(15, 40 + i % 15 * 20, 20, 20, $"{i + 1}.".Color(White)); + AddHtml(15, 40 + i % 15 * 20, 20, 20, Html.Color($"{i + 1}.", White)); if (name.Number > 0) { diff --git a/Projects/UOContent/Gumps/VirtualCheckGump.cs b/Projects/UOContent/Gumps/VirtualCheckGump.cs index 11baa1896..e12b86d74 100644 --- a/Projects/UOContent/Gumps/VirtualCheckGump.cs +++ b/Projects/UOContent/Gumps/VirtualCheckGump.cs @@ -131,7 +131,7 @@ public sealed class VirtualCheckGump : Gump, IVirtualCheckGump AddImage(10, 8, 113); AddImage(360, 8, 113); - AddHtml(40, 15, 320, 20, $"BANK OF {User.RawName.ToUpper()}".Center(0x2F4F4F)); + AddHtml(40, 15, 320, 20, Html.Center($"BANK OF {User.RawName.ToUpper()}", 0x2F4F4F)); // Platinum Row AddBackground(15, 60, 175, 20, 9300); diff --git a/Projects/UOContent/Holiday Stuff/Christmas/2004/DecorativeTopiary.cs b/Projects/UOContent/Holiday Stuff/Christmas/2004/DecorativeTopiary.cs index b6a89ee3d..54431f0ad 100644 --- a/Projects/UOContent/Holiday Stuff/Christmas/2004/DecorativeTopiary.cs +++ b/Projects/UOContent/Holiday Stuff/Christmas/2004/DecorativeTopiary.cs @@ -6,11 +6,9 @@ namespace Server.Items; public partial class DecorativeTopiary : Item { [Constructible] - public DecorativeTopiary() : base(0x2378) - { - Weight = 1.0; - LootType = LootType.Blessed; - } + public DecorativeTopiary() : base(0x2378) => LootType = LootType.Blessed; + + public override double DefaultWeight => 1.0; public override void OnSingleClick(Mobile from) { diff --git a/Projects/UOContent/Holiday Stuff/Christmas/2004/FestiveCactus.cs b/Projects/UOContent/Holiday Stuff/Christmas/2004/FestiveCactus.cs index 2ceb3f9e1..4fba63754 100644 --- a/Projects/UOContent/Holiday Stuff/Christmas/2004/FestiveCactus.cs +++ b/Projects/UOContent/Holiday Stuff/Christmas/2004/FestiveCactus.cs @@ -6,11 +6,9 @@ namespace Server.Items; public partial class FestiveCactus : Item { [Constructible] - public FestiveCactus() : base(0x2376) - { - Weight = 1.0; - LootType = LootType.Blessed; - } + public FestiveCactus() : base(0x2376) => LootType = LootType.Blessed; + + public override double DefaultWeight => 1.0; public override void OnSingleClick(Mobile from) { diff --git a/Projects/UOContent/Holiday Stuff/Christmas/2004/LightOfTheWinterSolstice.cs b/Projects/UOContent/Holiday Stuff/Christmas/2004/LightOfTheWinterSolstice.cs index 4e03f72cc..d41019847 100644 --- a/Projects/UOContent/Holiday Stuff/Christmas/2004/LightOfTheWinterSolstice.cs +++ b/Projects/UOContent/Holiday Stuff/Christmas/2004/LightOfTheWinterSolstice.cs @@ -18,12 +18,13 @@ public partial class LightOfTheWinterSolstice : Item { _dipper = dipper?.Intern() ?? StaffInfo.GetRandomStaff(); - Weight = 1.0; LootType = LootType.Blessed; Light = LightType.Circle300; Hue = Utility.RandomDyedHue(); } + public override double DefaultWeight => 1.0; + public override void OnSingleClick(Mobile from) { base.OnSingleClick(from); diff --git a/Projects/UOContent/Holiday Stuff/Christmas/2004/Mistletoe.cs b/Projects/UOContent/Holiday Stuff/Christmas/2004/Mistletoe.cs index c5927a129..c59a4a5a9 100644 --- a/Projects/UOContent/Holiday Stuff/Christmas/2004/Mistletoe.cs +++ b/Projects/UOContent/Holiday Stuff/Christmas/2004/Mistletoe.cs @@ -163,10 +163,11 @@ public partial class MistletoeDeed : Item public MistletoeDeed(int hue = 0) : base(0x14F0) { Hue = hue; - Weight = 1.0; LootType = LootType.Blessed; } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1070882; // Mistletoe Deed public override void OnSingleClick(Mobile from) diff --git a/Projects/UOContent/Holiday Stuff/Christmas/2004/PileOfGlacialSnow.cs b/Projects/UOContent/Holiday Stuff/Christmas/2004/PileOfGlacialSnow.cs index 54cf71449..136ee9f47 100644 --- a/Projects/UOContent/Holiday Stuff/Christmas/2004/PileOfGlacialSnow.cs +++ b/Projects/UOContent/Holiday Stuff/Christmas/2004/PileOfGlacialSnow.cs @@ -9,10 +9,11 @@ public partial class PileOfGlacialSnow : Item public PileOfGlacialSnow() : base(0x913) { Hue = 0x480; - Weight = 1.0; LootType = LootType.Blessed; } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1070874; // a Pile of Glacial Snow public override void OnSingleClick(Mobile from) diff --git a/Projects/UOContent/Holiday Stuff/Christmas/2004/SnowPile.cs b/Projects/UOContent/Holiday Stuff/Christmas/2004/SnowPile.cs index 2b092f187..2f7624dad 100644 --- a/Projects/UOContent/Holiday Stuff/Christmas/2004/SnowPile.cs +++ b/Projects/UOContent/Holiday Stuff/Christmas/2004/SnowPile.cs @@ -9,10 +9,11 @@ public partial class SnowPile : Item public SnowPile() : base(0x913) { Hue = 0x481; - Weight = 1.0; LootType = LootType.Blessed; } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1005578; // a pile of snow public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Holiday Stuff/Christmas/2004/SnowyTree.cs b/Projects/UOContent/Holiday Stuff/Christmas/2004/SnowyTree.cs index ab233720e..e1a231d85 100644 --- a/Projects/UOContent/Holiday Stuff/Christmas/2004/SnowyTree.cs +++ b/Projects/UOContent/Holiday Stuff/Christmas/2004/SnowyTree.cs @@ -6,11 +6,9 @@ namespace Server.Items; public partial class SnowyTree : Item { [Constructible] - public SnowyTree() : base(0x2377) - { - Weight = 1.0; - LootType = LootType.Blessed; - } + public SnowyTree() : base(0x2377) => LootType = LootType.Blessed; + + public override double DefaultWeight => 1.0; public override void OnSingleClick(Mobile from) { diff --git a/Projects/UOContent/Holiday Stuff/Christmas/2010/Addons/FireFliesDeed.cs b/Projects/UOContent/Holiday Stuff/Christmas/2010/Addons/FireFliesDeed.cs index c930a18cb..acbc1ec48 100644 --- a/Projects/UOContent/Holiday Stuff/Christmas/2010/Addons/FireFliesDeed.cs +++ b/Projects/UOContent/Holiday Stuff/Christmas/2010/Addons/FireFliesDeed.cs @@ -61,11 +61,9 @@ public partial class Fireflies : Item, IAddon public partial class FirefliesDeed : Item { [Constructible] - public FirefliesDeed() : base(0x14F0) - { - LootType = LootType.Blessed; - Weight = 1.0; - } + public FirefliesDeed() : base(0x14F0) => LootType = LootType.Blessed; + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1150061; diff --git a/Projects/UOContent/Holiday Stuff/Christmas/2010/Items/AngelDecoration.cs b/Projects/UOContent/Holiday Stuff/Christmas/2010/Items/AngelDecoration.cs index 4b6d4969c..06b662922 100644 --- a/Projects/UOContent/Holiday Stuff/Christmas/2010/Items/AngelDecoration.cs +++ b/Projects/UOContent/Holiday Stuff/Christmas/2010/Items/AngelDecoration.cs @@ -1,16 +1,13 @@ using ModernUO.Serialization; -namespace Server.Items.Holiday -{ - [SerializationGenerator(0, false)] - [TypeAlias("Server.Items.AngelDecoration"), Flippable(0x46FA, 0x46FB)] - public partial class AngelDecoration : Item - { - public AngelDecoration() : base(0x46FA) - { - LootType = LootType.Blessed; +namespace Server.Items.Holiday; - Weight = 30; - } - } +[Flippable(0x46FA, 0x46FB)] +[TypeAlias("Server.Items.AngelDecoration")] +[SerializationGenerator(0, false)] +public partial class AngelDecoration : Item +{ + public AngelDecoration() : base(0x46FA) => LootType = LootType.Blessed; + + public override double DefaultWeight => 30.0; } diff --git a/Projects/UOContent/Holiday Stuff/Christmas/2010/Items/RockingHorse.cs b/Projects/UOContent/Holiday Stuff/Christmas/2010/Items/RockingHorse.cs index f2588cf46..7b5684f57 100644 --- a/Projects/UOContent/Holiday Stuff/Christmas/2010/Items/RockingHorse.cs +++ b/Projects/UOContent/Holiday Stuff/Christmas/2010/Items/RockingHorse.cs @@ -1,16 +1,13 @@ using ModernUO.Serialization; -namespace Server.Items.Holiday -{ - [SerializationGenerator(0, false)] - [TypeAlias("Server.Items.RockingHorse"), Flippable(0x4214, 0x4215)] - public partial class RockingHorse : Item - { - public RockingHorse() : base(0x4214) - { - LootType = LootType.Blessed; +namespace Server.Items.Holiday; - Weight = 30; - } - } +[Flippable(0x4214, 0x4215)] +[TypeAlias("Server.Items.RockingHorse")] +[SerializationGenerator(0, false)] +public partial class RockingHorse : Item +{ + public RockingHorse() : base(0x4214) => LootType = LootType.Blessed; + + public override double DefaultWeight => 30.0; } diff --git a/Projects/UOContent/Holiday Stuff/Halloween/2009/Foods/MrPlainsCookies.cs b/Projects/UOContent/Holiday Stuff/Halloween/2009/Foods/MrPlainsCookies.cs index 9e2c2b670..ab1f44561 100644 --- a/Projects/UOContent/Holiday Stuff/Halloween/2009/Foods/MrPlainsCookies.cs +++ b/Projects/UOContent/Holiday Stuff/Halloween/2009/Foods/MrPlainsCookies.cs @@ -1,18 +1,18 @@ using ModernUO.Serialization; -namespace Server.Items -{ - [SerializationGenerator(0, false)] - public partial class MrPlainsCookies : Food - { - [Constructible] - public MrPlainsCookies() : base(0x160C) - { - Weight = 1.0; - FillFactor = 4; - Hue = 0xF4; - } +namespace Server.Items; - public override string DefaultName => "Mr Plain's Cookies"; +[SerializationGenerator(0, false)] +public partial class MrPlainsCookies : Food +{ + [Constructible] + public MrPlainsCookies() : base(0x160C) + { + FillFactor = 4; + Hue = 0xF4; } + + public override double DefaultWeight => 1.0; + + public override string DefaultName => "Mr Plain's Cookies"; } diff --git a/Projects/UOContent/Items/Addons/BaseAddonContainerDeed.cs b/Projects/UOContent/Items/Addons/BaseAddonContainerDeed.cs index 17c9fba16..ca4913176 100644 --- a/Projects/UOContent/Items/Addons/BaseAddonContainerDeed.cs +++ b/Projects/UOContent/Items/Addons/BaseAddonContainerDeed.cs @@ -13,8 +13,6 @@ namespace Server.Items public BaseAddonContainerDeed() : base(0x14F0) { - Weight = 1.0; - if (!Core.AOS) { LootType = LootType.Newbied; @@ -25,6 +23,8 @@ namespace Server.Items { } + public override double DefaultWeight => 1.0; + public abstract BaseAddonContainer Addon { get; } [CommandProperty(AccessLevel.GameMaster)] diff --git a/Projects/UOContent/Items/Addons/BaseAddonDeed.cs b/Projects/UOContent/Items/Addons/BaseAddonDeed.cs index 49977289b..d6ca03ac3 100644 --- a/Projects/UOContent/Items/Addons/BaseAddonDeed.cs +++ b/Projects/UOContent/Items/Addons/BaseAddonDeed.cs @@ -13,14 +13,14 @@ namespace Server.Items public BaseAddonDeed() : base(0x14F0) { - Weight = 1.0; - if (!Core.AOS) { LootType = LootType.Newbied; } } + public override double DefaultWeight => 1.0; + public abstract BaseAddon Addon { get; } [CommandProperty(AccessLevel.GameMaster)] diff --git a/Projects/UOContent/Items/Aquarium/AquariumGump.cs b/Projects/UOContent/Items/Aquarium/AquariumGump.cs index d35690a32..569ca29e5 100644 --- a/Projects/UOContent/Items/Aquarium/AquariumGump.cs +++ b/Projects/UOContent/Items/Aquarium/AquariumGump.cs @@ -57,7 +57,7 @@ namespace Server.Items AddItem(150, 80, item.ItemID, item.Hue); // item number / all items - AddHtml(20, 195, 250, 20, $"{page}/{m_Aquarium.Items.Count}".Color(0xFFFFFF)); + AddHtml(20, 195, 250, 20, Html.Color($"{page}/{m_Aquarium.Items.Count}", 0xFFFFFF)); // remove item if (edit) diff --git a/Projects/UOContent/Items/Aquarium/Rewards/WaterloggedBoots.cs b/Projects/UOContent/Items/Aquarium/Rewards/WaterloggedBoots.cs index ae50b2e81..3336ebd0b 100644 --- a/Projects/UOContent/Items/Aquarium/Rewards/WaterloggedBoots.cs +++ b/Projects/UOContent/Items/Aquarium/Rewards/WaterloggedBoots.cs @@ -8,20 +8,15 @@ namespace Server.Items [Constructible] public WaterloggedBoots() : base(0x1711) { - if (Utility.RandomBool()) - { + ItemID = Utility.RandomBool() // thigh boots - ItemID = 0x1711; - Weight = 4.0; - } - else - { + ? 0x1711 // boots - ItemID = 0x170B; - Weight = 3.0; - } + : 0x170B; } + public override double DefaultWeight => ItemID == 0x1711 ? 4.0 : 3.0; + public override int LabelNumber => 1074364; // Waterlogged boots public override void AddNameProperties(IPropertyList list) diff --git a/Projects/UOContent/Items/Armor/Bone/BoneArms.cs b/Projects/UOContent/Items/Armor/Bone/BoneArms.cs index a5a41d647..63b817a69 100644 --- a/Projects/UOContent/Items/Armor/Bone/BoneArms.cs +++ b/Projects/UOContent/Items/Armor/Bone/BoneArms.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class BoneArms : BaseArmor { [Constructible] - public BoneArms() : base(0x144E) => Weight = 2.0; + public BoneArms() : base(0x144E) + { + } + + public override double DefaultWeight => 2.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Bone/BoneChest.cs b/Projects/UOContent/Items/Armor/Bone/BoneChest.cs index edbb82f10..10416ddbe 100644 --- a/Projects/UOContent/Items/Armor/Bone/BoneChest.cs +++ b/Projects/UOContent/Items/Armor/Bone/BoneChest.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class BoneChest : BaseArmor { [Constructible] - public BoneChest() : base(0x144F) => Weight = 6.0; + public BoneChest() : base(0x144F) + { + } + + public override double DefaultWeight => 6.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Bone/BoneGloves.cs b/Projects/UOContent/Items/Armor/Bone/BoneGloves.cs index 5958198ad..707caa759 100644 --- a/Projects/UOContent/Items/Armor/Bone/BoneGloves.cs +++ b/Projects/UOContent/Items/Armor/Bone/BoneGloves.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class BoneGloves : BaseArmor { [Constructible] - public BoneGloves() : base(0x1450) => Weight = 2.0; + public BoneGloves() : base(0x1450) + { + } + + public override double DefaultWeight => 2.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Bone/BoneLegs.cs b/Projects/UOContent/Items/Armor/Bone/BoneLegs.cs index 67c3f9653..6ed0e9ab2 100644 --- a/Projects/UOContent/Items/Armor/Bone/BoneLegs.cs +++ b/Projects/UOContent/Items/Armor/Bone/BoneLegs.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class BoneLegs : BaseArmor { [Constructible] - public BoneLegs() : base(0x1452) => Weight = 3.0; + public BoneLegs() : base(0x1452) + { + } + + public override double DefaultWeight => 3.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Chain/ChainChest.cs b/Projects/UOContent/Items/Armor/Chain/ChainChest.cs index 9d4536d80..ec10c90bc 100644 --- a/Projects/UOContent/Items/Armor/Chain/ChainChest.cs +++ b/Projects/UOContent/Items/Armor/Chain/ChainChest.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class ChainChest : BaseArmor { [Constructible] - public ChainChest() : base(0x13BF) => Weight = 7.0; + public ChainChest() : base(0x13BF) + { + } + + public override double DefaultWeight => 7.0; public override int BasePhysicalResistance => 4; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Chain/ChainHatsuburi.cs b/Projects/UOContent/Items/Armor/Chain/ChainHatsuburi.cs index b5fd1431f..c381990b0 100644 --- a/Projects/UOContent/Items/Armor/Chain/ChainHatsuburi.cs +++ b/Projects/UOContent/Items/Armor/Chain/ChainHatsuburi.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class ChainHatsuburi : BaseArmor { [Constructible] - public ChainHatsuburi() : base(0x2774) => Weight = 7.0; + public ChainHatsuburi() : base(0x2774) + { + } + + public override double DefaultWeight => 7.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 2; diff --git a/Projects/UOContent/Items/Armor/Chain/ChainLegs.cs b/Projects/UOContent/Items/Armor/Chain/ChainLegs.cs index edfbea1e5..a91777876 100644 --- a/Projects/UOContent/Items/Armor/Chain/ChainLegs.cs +++ b/Projects/UOContent/Items/Armor/Chain/ChainLegs.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class ChainLegs : BaseArmor { [Constructible] - public ChainLegs() : base(0x13BE) => Weight = 7.0; + public ChainLegs() : base(0x13BE) + { + } + + public override double DefaultWeight => 7.0; public override int BasePhysicalResistance => 4; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType1.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType1.cs index 0d124f8f4..6d9d14721 100644 --- a/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType1.cs +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType1.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishClothArmsType1 : BaseArmor { [Constructible] - public GargishClothArmsType1() : base(0x404) => Weight = 2.0; + public GargishClothArmsType1() : base(0x404) + { + } + + public override double DefaultWeight => 2.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 7; diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType2.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType2.cs index 96027ec54..0df21c0f1 100644 --- a/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType2.cs +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothArmsType2.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishClothArmsType2 : BaseArmor { [Constructible] - public GargishClothArmsType2() : base(0x403) => Weight = 2.0; + public GargishClothArmsType2() : base(0x403) + { + } + + public override double DefaultWeight => 2.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 7; diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType1.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType1.cs index 76f8ac65c..728acca2f 100644 --- a/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType1.cs +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType1.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishClothChestType1 : BaseArmor { [Constructible] - public GargishClothChestType1() : base(0x406) => Weight = 2.0; + public GargishClothChestType1() : base(0x406) + { + } + + public override double DefaultWeight => 2.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 7; diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType2.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType2.cs index 0867ec5c7..af79906ae 100644 --- a/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType2.cs +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothChestType2.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishClothChestType2 : BaseArmor { [Constructible] - public GargishClothChestType2() : base(0x405) => Weight = 2.0; + public GargishClothChestType2() : base(0x405) + { + } + + public override double DefaultWeight => 2.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 7; diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType1.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType1.cs index 3200848bc..e3f6e91b1 100644 --- a/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType1.cs +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType1.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishClothKiltType1 : BaseArmor { [Constructible] - public GargishClothKiltType1() : base(0x408) => Weight = 2.0; + public GargishClothKiltType1() : base(0x408) + { + } + + public override double DefaultWeight => 2.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 7; diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType2.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType2.cs index 8f9e4a486..fef197852 100644 --- a/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType2.cs +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothKiltType2.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class GargishClothKiltType2 : BaseArmor { [Constructible] - public GargishClothKiltType2() : base(0x407) => Weight = 2.0; + public GargishClothKiltType2() : base(0x407) + { + } + + public override double DefaultWeight => 2.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 7; diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType1.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType1.cs index 53e724d47..152bfb2df 100644 --- a/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType1.cs +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType1.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishClothLegsType1 : BaseArmor { [Constructible] - public GargishClothLegsType1() : base(0x40A) => Weight = 2.0; + public GargishClothLegsType1() : base(0x40A) + { + } + + public override double DefaultWeight => 2.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 7; diff --git a/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType2.cs b/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType2.cs index 0016b886f..7f930a0aa 100644 --- a/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType2.cs +++ b/Projects/UOContent/Items/Armor/Cloth/GargishClothLegsType2.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishClothLegsType2 : BaseArmor { [Constructible] - public GargishClothLegsType2() : base(0x409) => Weight = 2.0; + public GargishClothLegsType2() : base(0x409) + { + } + + public override double DefaultWeight => 2.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 7; diff --git a/Projects/UOContent/Items/Armor/DaemonBone/DaemonArms.cs b/Projects/UOContent/Items/Armor/DaemonBone/DaemonArms.cs index 8c22af849..1133c8410 100644 --- a/Projects/UOContent/Items/Armor/DaemonBone/DaemonArms.cs +++ b/Projects/UOContent/Items/Armor/DaemonBone/DaemonArms.cs @@ -1,39 +1,39 @@ using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +[Flippable(0x144e, 0x1453)] +public partial class DaemonArms : BaseArmor { - [SerializationGenerator(0, false)] - [Flippable(0x144e, 0x1453)] - public partial class DaemonArms : BaseArmor + [Constructible] + public DaemonArms() : base(0x144E) { - [Constructible] - public DaemonArms() : base(0x144E) - { - Weight = 2.0; - Hue = 0x648; + Hue = 0x648; - ArmorAttributes.SelfRepair = 1; - } - - public override int BasePhysicalResistance => 6; - public override int BaseFireResistance => 6; - public override int BaseColdResistance => 7; - public override int BasePoisonResistance => 5; - public override int BaseEnergyResistance => 7; - - public override int InitMinHits => 255; - public override int InitMaxHits => 255; - - public override int AosStrReq => 55; - public override int OldStrReq => 40; - - public override int OldDexBonus => -2; - - public override int ArmorBase => 46; - - public override ArmorMaterialType MaterialType => ArmorMaterialType.Bone; - public override CraftResource DefaultResource => CraftResource.RegularLeather; - - public override int LabelNumber => 1041371; // daemon bone arms + ArmorAttributes.SelfRepair = 1; } + + public override double DefaultWeight => 2.0; + + public override int BasePhysicalResistance => 6; + public override int BaseFireResistance => 6; + public override int BaseColdResistance => 7; + public override int BasePoisonResistance => 5; + public override int BaseEnergyResistance => 7; + + public override int InitMinHits => 255; + public override int InitMaxHits => 255; + + public override int AosStrReq => 55; + public override int OldStrReq => 40; + + public override int OldDexBonus => -2; + + public override int ArmorBase => 46; + + public override ArmorMaterialType MaterialType => ArmorMaterialType.Bone; + public override CraftResource DefaultResource => CraftResource.RegularLeather; + + public override int LabelNumber => 1041371; // daemon bone arms } diff --git a/Projects/UOContent/Items/Armor/DaemonBone/DaemonChest.cs b/Projects/UOContent/Items/Armor/DaemonBone/DaemonChest.cs index e2a0ec586..bff469f7a 100644 --- a/Projects/UOContent/Items/Armor/DaemonBone/DaemonChest.cs +++ b/Projects/UOContent/Items/Armor/DaemonBone/DaemonChest.cs @@ -1,39 +1,39 @@ using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +[Flippable(0x144f, 0x1454)] +public partial class DaemonChest : BaseArmor { - [SerializationGenerator(0, false)] - [Flippable(0x144f, 0x1454)] - public partial class DaemonChest : BaseArmor + [Constructible] + public DaemonChest() : base(0x144F) { - [Constructible] - public DaemonChest() : base(0x144F) - { - Weight = 6.0; - Hue = 0x648; + Hue = 0x648; - ArmorAttributes.SelfRepair = 1; - } - - public override int BasePhysicalResistance => 6; - public override int BaseFireResistance => 6; - public override int BaseColdResistance => 7; - public override int BasePoisonResistance => 5; - public override int BaseEnergyResistance => 7; - - public override int InitMinHits => 255; - public override int InitMaxHits => 255; - - public override int AosStrReq => 60; - public override int OldStrReq => 40; - - public override int OldDexBonus => -6; - - public override int ArmorBase => 46; - - public override ArmorMaterialType MaterialType => ArmorMaterialType.Bone; - public override CraftResource DefaultResource => CraftResource.RegularLeather; - - public override int LabelNumber => 1041372; // daemon bone armor + ArmorAttributes.SelfRepair = 1; } + + public override double DefaultWeight => 6.0; + + public override int BasePhysicalResistance => 6; + public override int BaseFireResistance => 6; + public override int BaseColdResistance => 7; + public override int BasePoisonResistance => 5; + public override int BaseEnergyResistance => 7; + + public override int InitMinHits => 255; + public override int InitMaxHits => 255; + + public override int AosStrReq => 60; + public override int OldStrReq => 40; + + public override int OldDexBonus => -6; + + public override int ArmorBase => 46; + + public override ArmorMaterialType MaterialType => ArmorMaterialType.Bone; + public override CraftResource DefaultResource => CraftResource.RegularLeather; + + public override int LabelNumber => 1041372; // daemon bone armor } diff --git a/Projects/UOContent/Items/Armor/DaemonBone/DaemonGloves.cs b/Projects/UOContent/Items/Armor/DaemonBone/DaemonGloves.cs index e7d0793cd..2791feba6 100644 --- a/Projects/UOContent/Items/Armor/DaemonBone/DaemonGloves.cs +++ b/Projects/UOContent/Items/Armor/DaemonBone/DaemonGloves.cs @@ -1,39 +1,39 @@ using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +[Flippable(0x1450, 0x1455)] +public partial class DaemonGloves : BaseArmor { - [SerializationGenerator(0, false)] - [Flippable(0x1450, 0x1455)] - public partial class DaemonGloves : BaseArmor + [Constructible] + public DaemonGloves() : base(0x1450) { - [Constructible] - public DaemonGloves() : base(0x1450) - { - Weight = 2.0; - Hue = 0x648; + Hue = 0x648; - ArmorAttributes.SelfRepair = 1; - } - - public override int BasePhysicalResistance => 6; - public override int BaseFireResistance => 6; - public override int BaseColdResistance => 7; - public override int BasePoisonResistance => 5; - public override int BaseEnergyResistance => 7; - - public override int InitMinHits => 255; - public override int InitMaxHits => 255; - - public override int AosStrReq => 55; - public override int OldStrReq => 40; - - public override int OldDexBonus => -1; - - public override int ArmorBase => 46; - - public override ArmorMaterialType MaterialType => ArmorMaterialType.Bone; - public override CraftResource DefaultResource => CraftResource.RegularLeather; - - public override int LabelNumber => 1041373; // daemon bone gloves + ArmorAttributes.SelfRepair = 1; } + + public override double DefaultWeight => 2.0; + + public override int BasePhysicalResistance => 6; + public override int BaseFireResistance => 6; + public override int BaseColdResistance => 7; + public override int BasePoisonResistance => 5; + public override int BaseEnergyResistance => 7; + + public override int InitMinHits => 255; + public override int InitMaxHits => 255; + + public override int AosStrReq => 55; + public override int OldStrReq => 40; + + public override int OldDexBonus => -1; + + public override int ArmorBase => 46; + + public override ArmorMaterialType MaterialType => ArmorMaterialType.Bone; + public override CraftResource DefaultResource => CraftResource.RegularLeather; + + public override int LabelNumber => 1041373; // daemon bone gloves } diff --git a/Projects/UOContent/Items/Armor/DaemonBone/DaemonLegs.cs b/Projects/UOContent/Items/Armor/DaemonBone/DaemonLegs.cs index f07aeeae6..2fee9b456 100644 --- a/Projects/UOContent/Items/Armor/DaemonBone/DaemonLegs.cs +++ b/Projects/UOContent/Items/Armor/DaemonBone/DaemonLegs.cs @@ -1,39 +1,39 @@ using ModernUO.Serialization; -namespace Server.Items +namespace Server.Items; + +[SerializationGenerator(0, false)] +[Flippable(0x1452, 0x1457)] +public partial class DaemonLegs : BaseArmor { - [SerializationGenerator(0, false)] - [Flippable(0x1452, 0x1457)] - public partial class DaemonLegs : BaseArmor + [Constructible] + public DaemonLegs() : base(0x1452) { - [Constructible] - public DaemonLegs() : base(0x1452) - { - Weight = 3.0; - Hue = 0x648; + Hue = 0x648; - ArmorAttributes.SelfRepair = 1; - } - - public override int BasePhysicalResistance => 6; - public override int BaseFireResistance => 6; - public override int BaseColdResistance => 7; - public override int BasePoisonResistance => 5; - public override int BaseEnergyResistance => 7; - - public override int InitMinHits => 255; - public override int InitMaxHits => 255; - - public override int AosStrReq => 55; - public override int OldStrReq => 40; - - public override int OldDexBonus => -4; - - public override int ArmorBase => 46; - - public override ArmorMaterialType MaterialType => ArmorMaterialType.Bone; - public override CraftResource DefaultResource => CraftResource.RegularLeather; - - public override int LabelNumber => 1041375; // daemon bone leggings + ArmorAttributes.SelfRepair = 1; } + + public override double DefaultWeight => 3.0; + + public override int BasePhysicalResistance => 6; + public override int BaseFireResistance => 6; + public override int BaseColdResistance => 7; + public override int BasePoisonResistance => 5; + public override int BaseEnergyResistance => 7; + + public override int InitMinHits => 255; + public override int InitMaxHits => 255; + + public override int AosStrReq => 55; + public override int OldStrReq => 40; + + public override int OldDexBonus => -4; + + public override int ArmorBase => 46; + + public override ArmorMaterialType MaterialType => ArmorMaterialType.Bone; + public override CraftResource DefaultResource => CraftResource.RegularLeather; + + public override int LabelNumber => 1041375; // daemon bone leggings } diff --git a/Projects/UOContent/Items/Armor/Dragon/DragonArms.cs b/Projects/UOContent/Items/Armor/Dragon/DragonArms.cs index 970be0af2..9322b2114 100644 --- a/Projects/UOContent/Items/Armor/Dragon/DragonArms.cs +++ b/Projects/UOContent/Items/Armor/Dragon/DragonArms.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class DragonArms : BaseArmor { [Constructible] - public DragonArms() : base(0x2657) => Weight = 5.0; + public DragonArms() : base(0x2657) + { + } + + public override double DefaultWeight => 5.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Dragon/DragonChest.cs b/Projects/UOContent/Items/Armor/Dragon/DragonChest.cs index 7e012296e..594834c7d 100644 --- a/Projects/UOContent/Items/Armor/Dragon/DragonChest.cs +++ b/Projects/UOContent/Items/Armor/Dragon/DragonChest.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class DragonChest : BaseArmor { [Constructible] - public DragonChest() : base(0x2641) => Weight = 10.0; + public DragonChest() : base(0x2641) + { + } + + public override double DefaultWeight => 10.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Dragon/DragonGloves.cs b/Projects/UOContent/Items/Armor/Dragon/DragonGloves.cs index 4d2ec9c8d..0ce52ed2a 100644 --- a/Projects/UOContent/Items/Armor/Dragon/DragonGloves.cs +++ b/Projects/UOContent/Items/Armor/Dragon/DragonGloves.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class DragonGloves : BaseArmor { [Constructible] - public DragonGloves() : base(0x2643) => Weight = 2.0; + public DragonGloves() : base(0x2643) + { + } + + public override double DefaultWeight => 2.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Dragon/DragonHelm.cs b/Projects/UOContent/Items/Armor/Dragon/DragonHelm.cs index 79190e074..0bb900903 100644 --- a/Projects/UOContent/Items/Armor/Dragon/DragonHelm.cs +++ b/Projects/UOContent/Items/Armor/Dragon/DragonHelm.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class DragonHelm : BaseArmor { [Constructible] - public DragonHelm() : base(0x2645) => Weight = 5.0; + public DragonHelm() : base(0x2645) + { + } + + public override double DefaultWeight => 5.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Dragon/DragonLegs.cs b/Projects/UOContent/Items/Armor/Dragon/DragonLegs.cs index 2f02eea46..b50c99f01 100644 --- a/Projects/UOContent/Items/Armor/Dragon/DragonLegs.cs +++ b/Projects/UOContent/Items/Armor/Dragon/DragonLegs.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class DragonLegs : BaseArmor { [Constructible] - public DragonLegs() : base(0x2647) => Weight = 6.0; + public DragonLegs() : base(0x2647) + { + } + + public override double DefaultWeight => 6.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Glasses/ElvenGlasses.cs b/Projects/UOContent/Items/Armor/Glasses/ElvenGlasses.cs index 583f22713..4b0fb791f 100644 --- a/Projects/UOContent/Items/Armor/Glasses/ElvenGlasses.cs +++ b/Projects/UOContent/Items/Armor/Glasses/ElvenGlasses.cs @@ -6,11 +6,9 @@ namespace Server.Items public partial class ElvenGlasses : BaseArmor { [Constructible] - public ElvenGlasses() : base(0x2FB8) - { - Weight = 2; - _weaponAttributes = new AosWeaponAttributes(this); - } + public ElvenGlasses() : base(0x2FB8) => _weaponAttributes = new AosWeaponAttributes(this); + + public override double DefaultWeight => 2.0; public override int LabelNumber => 1032216; // elven glasses diff --git a/Projects/UOContent/Items/Armor/Helmets/Bascinet.cs b/Projects/UOContent/Items/Armor/Helmets/Bascinet.cs index 3885e397a..1fffb21ae 100644 --- a/Projects/UOContent/Items/Armor/Helmets/Bascinet.cs +++ b/Projects/UOContent/Items/Armor/Helmets/Bascinet.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class Bascinet : BaseArmor { [Constructible] - public Bascinet() : base(0x140C) => Weight = 5.0; + public Bascinet() : base(0x140C) + { + } + + public override double DefaultWeight => 5.0; public override int BasePhysicalResistance => 7; public override int BaseFireResistance => 2; diff --git a/Projects/UOContent/Items/Armor/Helmets/BoneHelm.cs b/Projects/UOContent/Items/Armor/Helmets/BoneHelm.cs index 38c8b1c2f..8d8327706 100644 --- a/Projects/UOContent/Items/Armor/Helmets/BoneHelm.cs +++ b/Projects/UOContent/Items/Armor/Helmets/BoneHelm.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class BoneHelm : BaseArmor { [Constructible] - public BoneHelm() : base(0x1451) => Weight = 3.0; + public BoneHelm() : base(0x1451) + { + } + + public override double DefaultWeight => 3.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Helmets/ChainCoif.cs b/Projects/UOContent/Items/Armor/Helmets/ChainCoif.cs index d1277dd77..6e736bacc 100644 --- a/Projects/UOContent/Items/Armor/Helmets/ChainCoif.cs +++ b/Projects/UOContent/Items/Armor/Helmets/ChainCoif.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class ChainCoif : BaseArmor { [Constructible] - public ChainCoif() : base(0x13BB) => Weight = 1.0; + public ChainCoif() : base(0x13BB) + { + } + + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 4; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Helmets/Circlet.cs b/Projects/UOContent/Items/Armor/Helmets/Circlet.cs index 7544eb134..8c3baeae2 100644 --- a/Projects/UOContent/Items/Armor/Helmets/Circlet.cs +++ b/Projects/UOContent/Items/Armor/Helmets/Circlet.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class Circlet : BaseArmor { [Constructible] - public Circlet() : base(0x2B6E) => Weight = 2.0; + public Circlet() : base(0x2B6E) + { + } + + public override double DefaultWeight => 2.0; public override int BasePhysicalResistance => 1; public override int BaseFireResistance => 5; diff --git a/Projects/UOContent/Items/Armor/Helmets/CloseHelm.cs b/Projects/UOContent/Items/Armor/Helmets/CloseHelm.cs index b7a5197a4..4f8c3b4fd 100644 --- a/Projects/UOContent/Items/Armor/Helmets/CloseHelm.cs +++ b/Projects/UOContent/Items/Armor/Helmets/CloseHelm.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class CloseHelm : BaseArmor { [Constructible] - public CloseHelm() : base(0x1408) => Weight = 5.0; + public CloseHelm() : base(0x1408) + { + } + + public override double DefaultWeight => 5.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Helmets/DaemonHelm.cs b/Projects/UOContent/Items/Armor/Helmets/DaemonHelm.cs index 80f2de0b5..9ae3a8753 100644 --- a/Projects/UOContent/Items/Armor/Helmets/DaemonHelm.cs +++ b/Projects/UOContent/Items/Armor/Helmets/DaemonHelm.cs @@ -10,11 +10,12 @@ namespace Server.Items public DaemonHelm() : base(0x1451) { Hue = 0x648; - Weight = 3.0; ArmorAttributes.SelfRepair = 1; } + public override double DefaultWeight => 3.0; + public override int BasePhysicalResistance => 6; public override int BaseFireResistance => 6; public override int BaseColdResistance => 7; diff --git a/Projects/UOContent/Items/Armor/Helmets/GemmedCirclet.cs b/Projects/UOContent/Items/Armor/Helmets/GemmedCirclet.cs index 3df8b821a..62a8e99d6 100644 --- a/Projects/UOContent/Items/Armor/Helmets/GemmedCirclet.cs +++ b/Projects/UOContent/Items/Armor/Helmets/GemmedCirclet.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GemmedCirclet : BaseArmor { [Constructible] - public GemmedCirclet() : base(0x2B70) => Weight = 2.0; + public GemmedCirclet() : base(0x2B70) + { + } + + public override double DefaultWeight => 2.0; public override int RequiredRaces => Race.AllowElvesOnly; diff --git a/Projects/UOContent/Items/Armor/Helmets/Helmet.cs b/Projects/UOContent/Items/Armor/Helmets/Helmet.cs index 01b138abb..2e3a22369 100644 --- a/Projects/UOContent/Items/Armor/Helmets/Helmet.cs +++ b/Projects/UOContent/Items/Armor/Helmets/Helmet.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class Helmet : BaseArmor { [Constructible] - public Helmet() : base(0x140A) => Weight = 5.0; + public Helmet() : base(0x140A) + { + } + + public override double DefaultWeight => 5.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Helmets/LeatherCap.cs b/Projects/UOContent/Items/Armor/Helmets/LeatherCap.cs index dfeafd35b..84b6952b0 100644 --- a/Projects/UOContent/Items/Armor/Helmets/LeatherCap.cs +++ b/Projects/UOContent/Items/Armor/Helmets/LeatherCap.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class LeatherCap : BaseArmor { [Constructible] - public LeatherCap() : base(0x1DB9) => Weight = 2.0; + public LeatherCap() : base(0x1DB9) + { + } + + public override double DefaultWeight => 2.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Helmets/NorseHelm.cs b/Projects/UOContent/Items/Armor/Helmets/NorseHelm.cs index 84926b157..c759b2789 100644 --- a/Projects/UOContent/Items/Armor/Helmets/NorseHelm.cs +++ b/Projects/UOContent/Items/Armor/Helmets/NorseHelm.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class NorseHelm : BaseArmor { [Constructible] - public NorseHelm() : base(0x140E) => Weight = 5.0; + public NorseHelm() : base(0x140E) + { + } + + public override double DefaultWeight => 5.0; public override int BasePhysicalResistance => 4; public override int BaseFireResistance => 1; diff --git a/Projects/UOContent/Items/Armor/Helmets/PlateHelm.cs b/Projects/UOContent/Items/Armor/Helmets/PlateHelm.cs index c4e2060cf..317f6bf0c 100644 --- a/Projects/UOContent/Items/Armor/Helmets/PlateHelm.cs +++ b/Projects/UOContent/Items/Armor/Helmets/PlateHelm.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class PlateHelm : BaseArmor { [Constructible] - public PlateHelm() : base(0x1412) => Weight = 5.0; + public PlateHelm() : base(0x1412) + { + } + + public override double DefaultWeight => 5.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Helmets/RavenHelm.cs b/Projects/UOContent/Items/Armor/Helmets/RavenHelm.cs index 5cd85bb25..7c3115d08 100644 --- a/Projects/UOContent/Items/Armor/Helmets/RavenHelm.cs +++ b/Projects/UOContent/Items/Armor/Helmets/RavenHelm.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class RavenHelm : BaseArmor { [Constructible] - public RavenHelm() : base(0x2B71) => Weight = 5.0; + public RavenHelm() : base(0x2B71) + { + } + + public override double DefaultWeight => 5.0; public override int RequiredRaces => Race.AllowElvesOnly; diff --git a/Projects/UOContent/Items/Armor/Helmets/RoyalCirclet.cs b/Projects/UOContent/Items/Armor/Helmets/RoyalCirclet.cs index ed2b78800..1ac6d467d 100644 --- a/Projects/UOContent/Items/Armor/Helmets/RoyalCirclet.cs +++ b/Projects/UOContent/Items/Armor/Helmets/RoyalCirclet.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class RoyalCirclet : BaseArmor { [Constructible] - public RoyalCirclet() : base(0x2B6F) => Weight = 2.0; + public RoyalCirclet() : base(0x2B6F) + { + } + + public override double DefaultWeight => 2.0; public override int RequiredRaces => Race.AllowElvesOnly; diff --git a/Projects/UOContent/Items/Armor/Helmets/VultureHelm.cs b/Projects/UOContent/Items/Armor/Helmets/VultureHelm.cs index 8d5e89a25..195ec4782 100644 --- a/Projects/UOContent/Items/Armor/Helmets/VultureHelm.cs +++ b/Projects/UOContent/Items/Armor/Helmets/VultureHelm.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class VultureHelm : BaseArmor { [Constructible] - public VultureHelm() : base(0x2B72) => Weight = 5.0; + public VultureHelm() : base(0x2B72) + { + } + + public override double DefaultWeight => 5.0; public override int RequiredRaces => Race.AllowElvesOnly; diff --git a/Projects/UOContent/Items/Armor/Helmets/WingedHelm.cs b/Projects/UOContent/Items/Armor/Helmets/WingedHelm.cs index 91c2f3b59..f4aca8eb8 100644 --- a/Projects/UOContent/Items/Armor/Helmets/WingedHelm.cs +++ b/Projects/UOContent/Items/Armor/Helmets/WingedHelm.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class WingedHelm : BaseArmor { [Constructible] - public WingedHelm() : base(0x2B73) => Weight = 5.0; + public WingedHelm() : base(0x2B73) + { + } + + public override double DefaultWeight => 5.0; public override int RequiredRaces => Race.AllowElvesOnly; diff --git a/Projects/UOContent/Items/Armor/Leather/FemaleLeafChest.cs b/Projects/UOContent/Items/Armor/Leather/FemaleLeafChest.cs index 83b7bb84c..1e870939b 100644 --- a/Projects/UOContent/Items/Armor/Leather/FemaleLeafChest.cs +++ b/Projects/UOContent/Items/Armor/Leather/FemaleLeafChest.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class FemaleLeafChest : BaseArmor { [Constructible] - public FemaleLeafChest() : base(0x2FCB) => Weight = 2.0; + public FemaleLeafChest() : base(0x2FCB) + { + } + + public override double DefaultWeight => 2.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Leather/FemaleLeatherChest.cs b/Projects/UOContent/Items/Armor/Leather/FemaleLeatherChest.cs index 0a6433ff9..323311f20 100644 --- a/Projects/UOContent/Items/Armor/Leather/FemaleLeatherChest.cs +++ b/Projects/UOContent/Items/Armor/Leather/FemaleLeatherChest.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class FemaleLeatherChest : BaseArmor { [Constructible] - public FemaleLeatherChest() : base(0x1C06) => Weight = 1.0; + public FemaleLeatherChest() : base(0x1C06) + { + } + + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/GargishLeatherArmsType1.cs b/Projects/UOContent/Items/Armor/Leather/GargishLeatherArmsType1.cs index 007d64209..23d72aaf6 100644 --- a/Projects/UOContent/Items/Armor/Leather/GargishLeatherArmsType1.cs +++ b/Projects/UOContent/Items/Armor/Leather/GargishLeatherArmsType1.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishLeatherArmsType1 : BaseArmor { [Constructible] - public GargishLeatherArmsType1() : base(0x301) => Weight = 4.0; + public GargishLeatherArmsType1() : base(0x301) + { + } + + public override double DefaultWeight => 4.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 5; diff --git a/Projects/UOContent/Items/Armor/Leather/GargishLeatherArmsType2.cs b/Projects/UOContent/Items/Armor/Leather/GargishLeatherArmsType2.cs index 5167c8e15..760cc12cf 100644 --- a/Projects/UOContent/Items/Armor/Leather/GargishLeatherArmsType2.cs +++ b/Projects/UOContent/Items/Armor/Leather/GargishLeatherArmsType2.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishLeatherArmsType2 : BaseArmor { [Constructible] - public GargishLeatherArmsType2() : base(0x302) => Weight = 4.0; + public GargishLeatherArmsType2() : base(0x302) + { + } + + public override double DefaultWeight => 4.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 5; diff --git a/Projects/UOContent/Items/Armor/Leather/GargishLeatherChestType1.cs b/Projects/UOContent/Items/Armor/Leather/GargishLeatherChestType1.cs index 21a38d83c..df7249273 100644 --- a/Projects/UOContent/Items/Armor/Leather/GargishLeatherChestType1.cs +++ b/Projects/UOContent/Items/Armor/Leather/GargishLeatherChestType1.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishLeatherChestType1 : BaseArmor { [Constructible] - public GargishLeatherChestType1() : base(0x0304) => Weight = 4.0; + public GargishLeatherChestType1() : base(0x0304) + { + } + + public override double DefaultWeight => 4.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 5; diff --git a/Projects/UOContent/Items/Armor/Leather/GargishLeatherChestType2.cs b/Projects/UOContent/Items/Armor/Leather/GargishLeatherChestType2.cs index 5e91ef3a0..856d5906c 100644 --- a/Projects/UOContent/Items/Armor/Leather/GargishLeatherChestType2.cs +++ b/Projects/UOContent/Items/Armor/Leather/GargishLeatherChestType2.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishLeatherChestType2 : BaseArmor { [Constructible] - public GargishLeatherChestType2() : base(0x303) => Weight = 4.0; + public GargishLeatherChestType2() : base(0x303) + { + } + + public override double DefaultWeight => 4.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 5; diff --git a/Projects/UOContent/Items/Armor/Leather/GargishLeatherKiltType1.cs b/Projects/UOContent/Items/Armor/Leather/GargishLeatherKiltType1.cs index 98d468165..a585b1a6a 100644 --- a/Projects/UOContent/Items/Armor/Leather/GargishLeatherKiltType1.cs +++ b/Projects/UOContent/Items/Armor/Leather/GargishLeatherKiltType1.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishLeatherKiltType1 : BaseArmor { [Constructible] - public GargishLeatherKiltType1() : base(0x311) => Weight = 4.0; + public GargishLeatherKiltType1() : base(0x311) + { + } + + public override double DefaultWeight => 4.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 5; diff --git a/Projects/UOContent/Items/Armor/Leather/GargishLeatherKiltType2.cs b/Projects/UOContent/Items/Armor/Leather/GargishLeatherKiltType2.cs index ef7e3491c..175047183 100644 --- a/Projects/UOContent/Items/Armor/Leather/GargishLeatherKiltType2.cs +++ b/Projects/UOContent/Items/Armor/Leather/GargishLeatherKiltType2.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishLeatherKiltType2 : BaseArmor { [Constructible] - public GargishLeatherKiltType2() : base(0x310) => Weight = 4.0; + public GargishLeatherKiltType2() : base(0x310) + { + } + + public override double DefaultWeight => 4.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 5; diff --git a/Projects/UOContent/Items/Armor/Leather/GargishLeatherLegsType1.cs b/Projects/UOContent/Items/Armor/Leather/GargishLeatherLegsType1.cs index 6decd20a6..4ccec8a26 100644 --- a/Projects/UOContent/Items/Armor/Leather/GargishLeatherLegsType1.cs +++ b/Projects/UOContent/Items/Armor/Leather/GargishLeatherLegsType1.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishLeatherLegsType1 : BaseArmor { [Constructible] - public GargishLeatherLegsType1() : base(0x305) => Weight = 4.0; + public GargishLeatherLegsType1() : base(0x305) + { + } + + public override double DefaultWeight => 4.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 5; diff --git a/Projects/UOContent/Items/Armor/Leather/GargishLeatherLegsType2.cs b/Projects/UOContent/Items/Armor/Leather/GargishLeatherLegsType2.cs index be0e8b395..1e5f7fe68 100644 --- a/Projects/UOContent/Items/Armor/Leather/GargishLeatherLegsType2.cs +++ b/Projects/UOContent/Items/Armor/Leather/GargishLeatherLegsType2.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishLeatherLegsType2 : BaseArmor { [Constructible] - public GargishLeatherLegsType2() : base(0x306) => Weight = 4.0; + public GargishLeatherLegsType2() : base(0x306) + { + } + + public override double DefaultWeight => 4.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 5; diff --git a/Projects/UOContent/Items/Armor/Leather/GargishLeatherWingArmor.cs b/Projects/UOContent/Items/Armor/Leather/GargishLeatherWingArmor.cs index 52505040e..07aa2994e 100644 --- a/Projects/UOContent/Items/Armor/Leather/GargishLeatherWingArmor.cs +++ b/Projects/UOContent/Items/Armor/Leather/GargishLeatherWingArmor.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishLeatherWingArmor : BaseArmor { [Constructible] - public GargishLeatherWingArmor() : base(0x457E) => Weight = 2.0; + public GargishLeatherWingArmor() : base(0x457E) + { + } + + public override double DefaultWeight => 2.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int PhysicalResistance => 0; diff --git a/Projects/UOContent/Items/Armor/Leather/LeafArms.cs b/Projects/UOContent/Items/Armor/Leather/LeafArms.cs index 2eb21fbfd..42c50d89b 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeafArms.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeafArms.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class LeafArms : BaseArmor { [Constructible] - public LeafArms() : base(0x2FC8) => Weight = 2.0; + public LeafArms() : base(0x2FC8) + { + } + + public override double DefaultWeight => 2.0; public override int RequiredRaces => Race.AllowElvesOnly; public override int BasePhysicalResistance => 2; diff --git a/Projects/UOContent/Items/Armor/Leather/LeafChest.cs b/Projects/UOContent/Items/Armor/Leather/LeafChest.cs index 853aad33d..08347cbbe 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeafChest.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeafChest.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class LeafChest : BaseArmor { [Constructible] - public LeafChest() : base(0x2FC5) => Weight = 2.0; + public LeafChest() : base(0x2FC5) + { + } + + public override double DefaultWeight => 2.0; public override int RequiredRaces => Race.AllowElvesOnly; public override int BasePhysicalResistance => 2; diff --git a/Projects/UOContent/Items/Armor/Leather/LeafGloves.cs b/Projects/UOContent/Items/Armor/Leather/LeafGloves.cs index 36fb38c6e..f9abd485b 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeafGloves.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeafGloves.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class LeafGloves : BaseArmor, IArcaneEquip { [Constructible] - public LeafGloves() : base(0x2FC6) => Weight = 2.0; + public LeafGloves() : base(0x2FC6) + { + } + + public override double DefaultWeight => 2.0; public override int RequiredRaces => Race.AllowElvesOnly; public override int BasePhysicalResistance => 2; diff --git a/Projects/UOContent/Items/Armor/Leather/LeafGorget.cs b/Projects/UOContent/Items/Armor/Leather/LeafGorget.cs index 7361479ce..93753a9ad 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeafGorget.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeafGorget.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class LeafGorget : BaseArmor { [Constructible] - public LeafGorget() : base(0x2FC7) => Weight = 2.0; + public LeafGorget() : base(0x2FC7) + { + } + + public override double DefaultWeight => 2.0; public override int RequiredRaces => Race.AllowElvesOnly; public override int BasePhysicalResistance => 2; diff --git a/Projects/UOContent/Items/Armor/Leather/LeafLegs.cs b/Projects/UOContent/Items/Armor/Leather/LeafLegs.cs index 6ba3d3593..54a810421 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeafLegs.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeafLegs.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class LeafLegs : BaseArmor { [Constructible] - public LeafLegs() : base(0x2FC9) => Weight = 2.0; + public LeafLegs() : base(0x2FC9) + { + } + + public override double DefaultWeight => 2.0; public override int RequiredRaces => Race.AllowElvesOnly; diff --git a/Projects/UOContent/Items/Armor/Leather/LeafTonlet.cs b/Projects/UOContent/Items/Armor/Leather/LeafTonlet.cs index b627757e7..6067109aa 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeafTonlet.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeafTonlet.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class LeafTonlet : BaseArmor { [Constructible] - public LeafTonlet() : base(0x2FCA) => Weight = 2.0; + public LeafTonlet() : base(0x2FCA) + { + } + + public override double DefaultWeight => 2.0; public override int RequiredRaces => Race.AllowElvesOnly; public override int BasePhysicalResistance => 2; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherArms.cs b/Projects/UOContent/Items/Armor/Leather/LeatherArms.cs index f092fb371..7fd2e9ce7 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherArms.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherArms.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class LeatherArms : BaseArmor { [Constructible] - public LeatherArms() : base(0x13CD) => Weight = 2.0; + public LeatherArms() : base(0x13CD) + { + } + + public override double DefaultWeight => 2.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherBustierArms.cs b/Projects/UOContent/Items/Armor/Leather/LeatherBustierArms.cs index 5e7bd8e9a..062c54aad 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherBustierArms.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherBustierArms.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class LeatherBustierArms : BaseArmor { [Constructible] - public LeatherBustierArms() : base(0x1C0A) => Weight = 1.0; + public LeatherBustierArms() : base(0x1C0A) + { + } + + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherChest.cs b/Projects/UOContent/Items/Armor/Leather/LeatherChest.cs index e66f20ef1..23f53c223 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherChest.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherChest.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class LeatherChest : BaseArmor { [Constructible] - public LeatherChest() : base(0x13CC) => Weight = 6.0; + public LeatherChest() : base(0x13CC) + { + } + + public override double DefaultWeight => 6.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherDo.cs b/Projects/UOContent/Items/Armor/Leather/LeatherDo.cs index a0450165e..b453c4a77 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherDo.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherDo.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class LeatherDo : BaseArmor { [Constructible] - public LeatherDo() : base(0x27C6) => Weight = 6.0; + public LeatherDo() : base(0x27C6) + { + } + + public override double DefaultWeight => 6.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherGloves.cs b/Projects/UOContent/Items/Armor/Leather/LeatherGloves.cs index cab91adcc..a1ded151b 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherGloves.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherGloves.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class LeatherGloves : BaseArmor, IArcaneEquip { [Constructible] - public LeatherGloves() : base(0x13C6) => Weight = 1.0; + public LeatherGloves() : base(0x13C6) + { + } + + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherGorget.cs b/Projects/UOContent/Items/Armor/Leather/LeatherGorget.cs index 42eb16daf..0e7aca13b 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherGorget.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherGorget.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class LeatherGorget : BaseArmor { [Constructible] - public LeatherGorget() : base(0x13C7) => Weight = 1.0; + public LeatherGorget() : base(0x13C7) + { + } + + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherHaidate.cs b/Projects/UOContent/Items/Armor/Leather/LeatherHaidate.cs index a4509020d..83ed60c9b 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherHaidate.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherHaidate.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class LeatherHaidate : BaseArmor { [Constructible] - public LeatherHaidate() : base(0x278A) => Weight = 4.0; + public LeatherHaidate() : base(0x278A) + { + } + + public override double DefaultWeight => 4.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherHiroSode.cs b/Projects/UOContent/Items/Armor/Leather/LeatherHiroSode.cs index 6b54d1695..b698cab6a 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherHiroSode.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherHiroSode.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class LeatherHiroSode : BaseArmor { [Constructible] - public LeatherHiroSode() : base(0x277E) => Weight = 1.0; + public LeatherHiroSode() : base(0x277E) + { + } + + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherJingasa.cs b/Projects/UOContent/Items/Armor/Leather/LeatherJingasa.cs index a37ed89c0..31030cb6c 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherJingasa.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherJingasa.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class LeatherJingasa : BaseArmor { [Constructible] - public LeatherJingasa() : base(0x2776) => Weight = 3.0; + public LeatherJingasa() : base(0x2776) + { + } + + public override double DefaultWeight => 3.0; public override int BasePhysicalResistance => 4; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherLegs.cs b/Projects/UOContent/Items/Armor/Leather/LeatherLegs.cs index 2d83b9148..bc4d50771 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherLegs.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherLegs.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class LeatherLegs : BaseArmor { [Constructible] - public LeatherLegs() : base(0x13CB) => Weight = 4.0; + public LeatherLegs() : base(0x13CB) + { + } + + public override double DefaultWeight => 4.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherMempo.cs b/Projects/UOContent/Items/Armor/Leather/LeatherMempo.cs index 5887692ab..4ee5720fc 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherMempo.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherMempo.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class LeatherMempo : BaseArmor { [Constructible] - public LeatherMempo() : base(0x277A) => Weight = 2.0; + public LeatherMempo() : base(0x277A) + { + } + + public override double DefaultWeight => 2.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherNinjaHood.cs b/Projects/UOContent/Items/Armor/Leather/LeatherNinjaHood.cs index 19d7cd049..c07104ed7 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherNinjaHood.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherNinjaHood.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class LeatherNinjaHood : BaseArmor { [Constructible] - public LeatherNinjaHood() : base(0x278E) => Weight = 2.0; + public LeatherNinjaHood() : base(0x278E) + { + } + + public override double DefaultWeight => 2.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherNinjaJacket.cs b/Projects/UOContent/Items/Armor/Leather/LeatherNinjaJacket.cs index c27194003..f84520e4a 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherNinjaJacket.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherNinjaJacket.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class LeatherNinjaJacket : BaseArmor { [Constructible] - public LeatherNinjaJacket() : base(0x2793) => Weight = 5.0; + public LeatherNinjaJacket() : base(0x2793) + { + } + + public override double DefaultWeight => 5.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherNinjaMitts.cs b/Projects/UOContent/Items/Armor/Leather/LeatherNinjaMitts.cs index 0112fafa3..b12d00935 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherNinjaMitts.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherNinjaMitts.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class LeatherNinjaMitts : BaseArmor { [Constructible] - public LeatherNinjaMitts() : base(0x2792) => Weight = 2.0; + public LeatherNinjaMitts() : base(0x2792) + { + } + + public override double DefaultWeight => 2.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherNinjaPants.cs b/Projects/UOContent/Items/Armor/Leather/LeatherNinjaPants.cs index b21807d32..8feb71366 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherNinjaPants.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherNinjaPants.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class LeatherNinjaPants : BaseArmor { [Constructible] - public LeatherNinjaPants() : base(0x2791) => Weight = 3.0; + public LeatherNinjaPants() : base(0x2791) + { + } + + public override double DefaultWeight => 3.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherShorts.cs b/Projects/UOContent/Items/Armor/Leather/LeatherShorts.cs index b18307d99..7742344de 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherShorts.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherShorts.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class LeatherShorts : BaseArmor { [Constructible] - public LeatherShorts() : base(0x1C00) => Weight = 3.0; + public LeatherShorts() : base(0x1C00) + { + } + + public override double DefaultWeight => 3.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherSkirt.cs b/Projects/UOContent/Items/Armor/Leather/LeatherSkirt.cs index acaaa09fb..827dcabaa 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherSkirt.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherSkirt.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class LeatherSkirt : BaseArmor { [Constructible] - public LeatherSkirt() : base(0x1C08) => Weight = 1.0; + public LeatherSkirt() : base(0x1C08) + { + } + + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Leather/LeatherSuneate.cs b/Projects/UOContent/Items/Armor/Leather/LeatherSuneate.cs index 31df6ddeb..b56df8973 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherSuneate.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherSuneate.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class LeatherSuneate : BaseArmor { [Constructible] - public LeatherSuneate() : base(0x2786) => Weight = 4.0; + public LeatherSuneate() : base(0x2786) + { + } + + public override double DefaultWeight => 4.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Plate/DecorativePlateKabuto.cs b/Projects/UOContent/Items/Armor/Plate/DecorativePlateKabuto.cs index e8d99a675..02ea392b8 100644 --- a/Projects/UOContent/Items/Armor/Plate/DecorativePlateKabuto.cs +++ b/Projects/UOContent/Items/Armor/Plate/DecorativePlateKabuto.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class DecorativePlateKabuto : BaseArmor { [Constructible] - public DecorativePlateKabuto() : base(0x2778) => Weight = 6.0; + public DecorativePlateKabuto() : base(0x2778) + { + } + + public override double DefaultWeight => 6.0; public override int BasePhysicalResistance => 6; public override int BaseFireResistance => 2; diff --git a/Projects/UOContent/Items/Armor/Plate/FemalePlateChest.cs b/Projects/UOContent/Items/Armor/Plate/FemalePlateChest.cs index 76bd975b4..21d10ab58 100644 --- a/Projects/UOContent/Items/Armor/Plate/FemalePlateChest.cs +++ b/Projects/UOContent/Items/Armor/Plate/FemalePlateChest.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class FemalePlateChest : BaseArmor { [Constructible] - public FemalePlateChest() : base(0x1C04) => Weight = 4.0; + public FemalePlateChest() : base(0x1C04) + { + } + + public override double DefaultWeight => 4.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/FemaleWoodlandChest.cs b/Projects/UOContent/Items/Armor/Plate/FemaleWoodlandChest.cs index 5ac5a42ab..538efde5c 100644 --- a/Projects/UOContent/Items/Armor/Plate/FemaleWoodlandChest.cs +++ b/Projects/UOContent/Items/Armor/Plate/FemaleWoodlandChest.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class FemaleElvenPlateChest : BaseArmor { [Constructible] - public FemaleElvenPlateChest() : base(0x2B6D) => Weight = 8.0; + public FemaleElvenPlateChest() : base(0x2B6D) + { + } + + public override double DefaultWeight => 8.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/HeavyPlateJingasa.cs b/Projects/UOContent/Items/Armor/Plate/HeavyPlateJingasa.cs index b35003750..a1594b314 100644 --- a/Projects/UOContent/Items/Armor/Plate/HeavyPlateJingasa.cs +++ b/Projects/UOContent/Items/Armor/Plate/HeavyPlateJingasa.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class HeavyPlateJingasa : BaseArmor { [Constructible] - public HeavyPlateJingasa() : base(0x2777) => Weight = 5.0; + public HeavyPlateJingasa() : base(0x2777) + { + } + + public override double DefaultWeight => 5.0; public override int BasePhysicalResistance => 7; public override int BaseFireResistance => 2; diff --git a/Projects/UOContent/Items/Armor/Plate/LightPlateJingasa.cs b/Projects/UOContent/Items/Armor/Plate/LightPlateJingasa.cs index 1f35c50ae..860dbc513 100644 --- a/Projects/UOContent/Items/Armor/Plate/LightPlateJingasa.cs +++ b/Projects/UOContent/Items/Armor/Plate/LightPlateJingasa.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class LightPlateJingasa : BaseArmor { [Constructible] - public LightPlateJingasa() : base(0x2781) => Weight = 5.0; + public LightPlateJingasa() : base(0x2781) + { + } + + public override double DefaultWeight => 5.0; public override int BasePhysicalResistance => 7; public override int BaseFireResistance => 2; diff --git a/Projects/UOContent/Items/Armor/Plate/PlateArms.cs b/Projects/UOContent/Items/Armor/Plate/PlateArms.cs index 1e4505195..b5428ae5b 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateArms.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateArms.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class PlateArms : BaseArmor { [Constructible] - public PlateArms() : base(0x1410) => Weight = 5.0; + public PlateArms() : base(0x1410) + { + } + + public override double DefaultWeight => 5.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/PlateBattleKabuto.cs b/Projects/UOContent/Items/Armor/Plate/PlateBattleKabuto.cs index cc882ec5b..7452cb074 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateBattleKabuto.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateBattleKabuto.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class PlateBattleKabuto : BaseArmor { [Constructible] - public PlateBattleKabuto() : base(0x2785) => Weight = 6.0; + public PlateBattleKabuto() : base(0x2785) + { + } + + public override double DefaultWeight => 6.0; public override int BasePhysicalResistance => 6; public override int BaseFireResistance => 2; diff --git a/Projects/UOContent/Items/Armor/Plate/PlateChest.cs b/Projects/UOContent/Items/Armor/Plate/PlateChest.cs index 60375189a..d7efd690f 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateChest.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateChest.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class PlateChest : BaseArmor { [Constructible] - public PlateChest() : base(0x1415) => Weight = 10.0; + public PlateChest() : base(0x1415) + { + } + + public override double DefaultWeight => 10.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/PlateDo.cs b/Projects/UOContent/Items/Armor/Plate/PlateDo.cs index de8ecd5f1..29587a22e 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateDo.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateDo.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class PlateDo : BaseArmor { [Constructible] - public PlateDo() : base(0x277D) => Weight = 10.0; + public PlateDo() : base(0x277D) + { + } + + public override double DefaultWeight => 10.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/PlateGloves.cs b/Projects/UOContent/Items/Armor/Plate/PlateGloves.cs index 2303f2a83..828fb7d09 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateGloves.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateGloves.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class PlateGloves : BaseArmor { [Constructible] - public PlateGloves() : base(0x1414) => Weight = 2.0; + public PlateGloves() : base(0x1414) + { + } + + public override double DefaultWeight => 2.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/PlateGorget.cs b/Projects/UOContent/Items/Armor/Plate/PlateGorget.cs index 27ea17e0f..612949e1d 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateGorget.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateGorget.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class PlateGorget : BaseArmor { [Constructible] - public PlateGorget() : base(0x1413) => Weight = 2.0; + public PlateGorget() : base(0x1413) + { + } + + public override double DefaultWeight => 2.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/PlateHaidate.cs b/Projects/UOContent/Items/Armor/Plate/PlateHaidate.cs index ae238cf3b..bbb6ca326 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateHaidate.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateHaidate.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class PlateHaidate : BaseArmor { [Constructible] - public PlateHaidate() : base(0x278D) => Weight = 7.0; + public PlateHaidate() : base(0x278D) + { + } + + public override double DefaultWeight => 7.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/PlateHatsuburi.cs b/Projects/UOContent/Items/Armor/Plate/PlateHatsuburi.cs index cb629b4ce..b9a872c6f 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateHatsuburi.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateHatsuburi.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class PlateHatsuburi : BaseArmor { [Constructible] - public PlateHatsuburi() : base(0x2775) => Weight = 5.0; + public PlateHatsuburi() : base(0x2775) + { + } + + public override double DefaultWeight => 5.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/PlateHiroSode.cs b/Projects/UOContent/Items/Armor/Plate/PlateHiroSode.cs index 2f272a4a7..c26890a37 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateHiroSode.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateHiroSode.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class PlateHiroSode : BaseArmor { [Constructible] - public PlateHiroSode() : base(0x2780) => Weight = 3.0; + public PlateHiroSode() : base(0x2780) + { + } + + public override double DefaultWeight => 3.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/PlateLegs.cs b/Projects/UOContent/Items/Armor/Plate/PlateLegs.cs index cc69bfce6..9a437f311 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateLegs.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateLegs.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class PlateLegs : BaseArmor { [Constructible] - public PlateLegs() : base(0x1411) => Weight = 7.0; + public PlateLegs() : base(0x1411) + { + } + + public override double DefaultWeight => 7.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/PlateMempo.cs b/Projects/UOContent/Items/Armor/Plate/PlateMempo.cs index 257dcce97..83b730e06 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateMempo.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateMempo.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class PlateMempo : BaseArmor { [Constructible] - public PlateMempo() : base(0x2779) => Weight = 3.0; + public PlateMempo() : base(0x2779) + { + } + + public override double DefaultWeight => 3.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/PlateSuneate.cs b/Projects/UOContent/Items/Armor/Plate/PlateSuneate.cs index dce3c24fa..227407e5f 100644 --- a/Projects/UOContent/Items/Armor/Plate/PlateSuneate.cs +++ b/Projects/UOContent/Items/Armor/Plate/PlateSuneate.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class PlateSuneate : BaseArmor { [Constructible] - public PlateSuneate() : base(0x2788) => Weight = 7.0; + public PlateSuneate() : base(0x2788) + { + } + + public override double DefaultWeight => 7.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/SmallPlateJingasa.cs b/Projects/UOContent/Items/Armor/Plate/SmallPlateJingasa.cs index 3d3112286..ad6cd96a2 100644 --- a/Projects/UOContent/Items/Armor/Plate/SmallPlateJingasa.cs +++ b/Projects/UOContent/Items/Armor/Plate/SmallPlateJingasa.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class SmallPlateJingasa : BaseArmor { [Constructible] - public SmallPlateJingasa() : base(0x2784) => Weight = 5.0; + public SmallPlateJingasa() : base(0x2784) + { + } + + public override double DefaultWeight => 5.0; public override int BasePhysicalResistance => 7; public override int BaseFireResistance => 2; diff --git a/Projects/UOContent/Items/Armor/Plate/StandardPlateKabuto.cs b/Projects/UOContent/Items/Armor/Plate/StandardPlateKabuto.cs index be98d3a65..301f620c6 100644 --- a/Projects/UOContent/Items/Armor/Plate/StandardPlateKabuto.cs +++ b/Projects/UOContent/Items/Armor/Plate/StandardPlateKabuto.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class StandardPlateKabuto : BaseArmor { [Constructible] - public StandardPlateKabuto() : base(0x2789) => Weight = 6.0; + public StandardPlateKabuto() : base(0x2789) + { + } + + public override double DefaultWeight => 6.0; public override int BasePhysicalResistance => 6; public override int BaseFireResistance => 2; diff --git a/Projects/UOContent/Items/Armor/Plate/WoodlandArms.cs b/Projects/UOContent/Items/Armor/Plate/WoodlandArms.cs index 582bfed08..2f37cf73d 100644 --- a/Projects/UOContent/Items/Armor/Plate/WoodlandArms.cs +++ b/Projects/UOContent/Items/Armor/Plate/WoodlandArms.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class WoodlandArms : BaseArmor { [Constructible] - public WoodlandArms() : base(0x2B6C) => Weight = 5.0; + public WoodlandArms() : base(0x2B6C) + { + } + + public override double DefaultWeight => 5.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/WoodlandChest.cs b/Projects/UOContent/Items/Armor/Plate/WoodlandChest.cs index 9fb9e6567..81608a75a 100644 --- a/Projects/UOContent/Items/Armor/Plate/WoodlandChest.cs +++ b/Projects/UOContent/Items/Armor/Plate/WoodlandChest.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class WoodlandChest : BaseArmor { [Constructible] - public WoodlandChest() : base(0x2B67) => Weight = 8.0; + public WoodlandChest() : base(0x2B67) + { + } + + public override double DefaultWeight => 8.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/WoodlandGloves.cs b/Projects/UOContent/Items/Armor/Plate/WoodlandGloves.cs index 40455c089..836419d06 100644 --- a/Projects/UOContent/Items/Armor/Plate/WoodlandGloves.cs +++ b/Projects/UOContent/Items/Armor/Plate/WoodlandGloves.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class WoodlandGloves : BaseArmor { [Constructible] - public WoodlandGloves() : base(0x2B6A) => Weight = 2.0; + public WoodlandGloves() : base(0x2B6A) + { + } + + public override double DefaultWeight => 2.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Plate/WoodlandLegs.cs b/Projects/UOContent/Items/Armor/Plate/WoodlandLegs.cs index ec16588aa..08fb63eb2 100644 --- a/Projects/UOContent/Items/Armor/Plate/WoodlandLegs.cs +++ b/Projects/UOContent/Items/Armor/Plate/WoodlandLegs.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class WoodlandLegs : BaseArmor { [Constructible] - public WoodlandLegs() : base(0x2B6B) => Weight = 8.0; + public WoodlandLegs() : base(0x2B6B) + { + } + + public override double DefaultWeight => 8.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Ranger/RangerArms.cs b/Projects/UOContent/Items/Armor/Ranger/RangerArms.cs index 6eb5864db..1987140ec 100644 --- a/Projects/UOContent/Items/Armor/Ranger/RangerArms.cs +++ b/Projects/UOContent/Items/Armor/Ranger/RangerArms.cs @@ -9,10 +9,11 @@ namespace Server.Items [Constructible] public RangerArms() : base(0x13DC) { - Weight = 4.0; Hue = 0x59C; } + public override double DefaultWeight => 4.0; + public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; public override int BaseColdResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Ranger/RangerChest.cs b/Projects/UOContent/Items/Armor/Ranger/RangerChest.cs index e2a6054f5..d345767ef 100644 --- a/Projects/UOContent/Items/Armor/Ranger/RangerChest.cs +++ b/Projects/UOContent/Items/Armor/Ranger/RangerChest.cs @@ -9,10 +9,11 @@ namespace Server.Items [Constructible] public RangerChest() : base(0x13DB) { - Weight = 8.0; Hue = 0x59C; } + public override double DefaultWeight => 8.0; + public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; public override int BaseColdResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Ranger/RangerGloves.cs b/Projects/UOContent/Items/Armor/Ranger/RangerGloves.cs index b51b766e6..be4573cbc 100644 --- a/Projects/UOContent/Items/Armor/Ranger/RangerGloves.cs +++ b/Projects/UOContent/Items/Armor/Ranger/RangerGloves.cs @@ -9,10 +9,11 @@ namespace Server.Items [Constructible] public RangerGloves() : base(0x13D5) { - Weight = 1.0; Hue = 0x59C; } + public override double DefaultWeight => 1.0; + public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; public override int BaseColdResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Ranger/RangerGorget.cs b/Projects/UOContent/Items/Armor/Ranger/RangerGorget.cs index e9a4a6949..b6cb4c0c5 100644 --- a/Projects/UOContent/Items/Armor/Ranger/RangerGorget.cs +++ b/Projects/UOContent/Items/Armor/Ranger/RangerGorget.cs @@ -8,10 +8,11 @@ namespace Server.Items [Constructible] public RangerGorget() : base(0x13D6) { - Weight = 1.0; Hue = 0x59C; } + public override double DefaultWeight => 1.0; + public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; public override int BaseColdResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Ranger/RangerLegs.cs b/Projects/UOContent/Items/Armor/Ranger/RangerLegs.cs index 7fae0424a..4a728332b 100644 --- a/Projects/UOContent/Items/Armor/Ranger/RangerLegs.cs +++ b/Projects/UOContent/Items/Armor/Ranger/RangerLegs.cs @@ -9,10 +9,11 @@ namespace Server.Items [Constructible] public RangerLegs() : base(0x13DA) { - Weight = 3.0; Hue = 0x59C; } + public override double DefaultWeight => 3.0; + public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; public override int BaseColdResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Ring/RingmailArms.cs b/Projects/UOContent/Items/Armor/Ring/RingmailArms.cs index c9d1061e0..dfef77e71 100644 --- a/Projects/UOContent/Items/Armor/Ring/RingmailArms.cs +++ b/Projects/UOContent/Items/Armor/Ring/RingmailArms.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class RingmailArms : BaseArmor { [Constructible] - public RingmailArms() : base(0x13EE) => Weight = 15.0; + public RingmailArms() : base(0x13EE) + { + } + + public override double DefaultWeight => 15.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Ring/RingmailChest.cs b/Projects/UOContent/Items/Armor/Ring/RingmailChest.cs index e01dd908e..ac8275fb8 100644 --- a/Projects/UOContent/Items/Armor/Ring/RingmailChest.cs +++ b/Projects/UOContent/Items/Armor/Ring/RingmailChest.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class RingmailChest : BaseArmor { [Constructible] - public RingmailChest() : base(0x13EC) => Weight = 15.0; + public RingmailChest() : base(0x13EC) + { + } + + public override double DefaultWeight => 15.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Ring/RingmailGloves.cs b/Projects/UOContent/Items/Armor/Ring/RingmailGloves.cs index b57887f80..450c5fe76 100644 --- a/Projects/UOContent/Items/Armor/Ring/RingmailGloves.cs +++ b/Projects/UOContent/Items/Armor/Ring/RingmailGloves.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class RingmailGloves : BaseArmor { [Constructible] - public RingmailGloves() : base(0x13EB) => Weight = 2.0; + public RingmailGloves() : base(0x13EB) + { + } + + public override double DefaultWeight => 2.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Ring/RingmailLegs.cs b/Projects/UOContent/Items/Armor/Ring/RingmailLegs.cs index 189a8597d..d81cb142f 100644 --- a/Projects/UOContent/Items/Armor/Ring/RingmailLegs.cs +++ b/Projects/UOContent/Items/Armor/Ring/RingmailLegs.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class RingmailLegs : BaseArmor { [Constructible] - public RingmailLegs() : base(0x13F0) => Weight = 15.0; + public RingmailLegs() : base(0x13F0) + { + } + + public override double DefaultWeight => 15.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType1.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType1.cs index 8e28498d0..dcd45a9b7 100644 --- a/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType1.cs +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType1.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishStoneArmsType1 : BaseArmor { [Constructible] - public GargishStoneArmsType1() : base(0x284) => Weight = 10.0; + public GargishStoneArmsType1() : base(0x284) + { + } + + public override double DefaultWeight => 10.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType2.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType2.cs index 9198bf879..9d3385ffc 100644 --- a/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType2.cs +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneArmsType2.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishStoneArmsType2 : BaseArmor { [Constructible] - public GargishStoneArmsType2() : base(0x283) => Weight = 10.0; + public GargishStoneArmsType2() : base(0x283) + { + } + + public override double DefaultWeight => 10.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType1.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType1.cs index 25b39d0eb..39a356d53 100644 --- a/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType1.cs +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType1.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishStoneChestType1 : BaseArmor { [Constructible] - public GargishStoneChestType1() : base(0x286) => Weight = 15.0; + public GargishStoneChestType1() : base(0x286) + { + } + + public override double DefaultWeight => 15.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType2.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType2.cs index 81bb3b928..61a6f9571 100644 --- a/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType2.cs +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneChestType2.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishStoneChestType2 : BaseArmor { [Constructible] - public GargishStoneChestType2() : base(0x285) => Weight = 15.0; + public GargishStoneChestType2() : base(0x285) + { + } + + public override double DefaultWeight => 15.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType1.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType1.cs index 501a9cbf2..981394574 100644 --- a/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType1.cs +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType1.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishStoneKiltType1 : BaseArmor { [Constructible] - public GargishStoneKiltType1() : base(0x288) => Weight = 10.0; + public GargishStoneKiltType1() : base(0x288) + { + } + + public override double DefaultWeight => 10.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType2.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType2.cs index 43103cd72..953073882 100644 --- a/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType2.cs +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneKiltType2.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishStoneKiltType2 : BaseArmor { [Constructible] - public GargishStoneKiltType2() : base(0x287) => Weight = 10.0; + public GargishStoneKiltType2() : base(0x287) + { + } + + public override double DefaultWeight => 10.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType1.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType1.cs index c8a683f91..74f91f340 100644 --- a/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType1.cs +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType1.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishStoneLegsType1 : BaseArmor { [Constructible] - public GargishStoneLegsType1() : base(0x28A) => Weight = 15.0; + public GargishStoneLegsType1() : base(0x28A) + { + } + + public override double DefaultWeight => 15.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType2.cs b/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType2.cs index f5ab12bbf..3f67f9fb7 100644 --- a/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType2.cs +++ b/Projects/UOContent/Items/Armor/Stone/GargishStoneLegsType2.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishStoneLegsType2 : BaseArmor { [Constructible] - public GargishStoneLegsType2() : base(0x289) => Weight = 15.0; + public GargishStoneLegsType2() : base(0x289) + { + } + + public override double DefaultWeight => 15.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Studded/FemaleStuddedChest.cs b/Projects/UOContent/Items/Armor/Studded/FemaleStuddedChest.cs index 9034f797a..0011b1b47 100644 --- a/Projects/UOContent/Items/Armor/Studded/FemaleStuddedChest.cs +++ b/Projects/UOContent/Items/Armor/Studded/FemaleStuddedChest.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class FemaleStuddedChest : BaseArmor { [Constructible] - public FemaleStuddedChest() : base(0x1C02) => Weight = 6.0; + public FemaleStuddedChest() : base(0x1C02) + { + } + + public override double DefaultWeight => 6.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Studded/GargishStuddedArmsType1.cs b/Projects/UOContent/Items/Armor/Studded/GargishStuddedArmsType1.cs index 9d3279ae2..2bbf105db 100644 --- a/Projects/UOContent/Items/Armor/Studded/GargishStuddedArmsType1.cs +++ b/Projects/UOContent/Items/Armor/Studded/GargishStuddedArmsType1.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishStuddedArmsType1 : BaseArmor { [Constructible] - public GargishStuddedArmsType1() : base(0x284) => Weight = 10.0; + public GargishStuddedArmsType1() : base(0x284) + { + } + + public override double DefaultWeight => 10.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Studded/GargishStuddedArmsType2.cs b/Projects/UOContent/Items/Armor/Studded/GargishStuddedArmsType2.cs index c30396f45..aae9d4429 100644 --- a/Projects/UOContent/Items/Armor/Studded/GargishStuddedArmsType2.cs +++ b/Projects/UOContent/Items/Armor/Studded/GargishStuddedArmsType2.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishStuddedArmsType2 : BaseArmor { [Constructible] - public GargishStuddedArmsType2() : base(0x283) => Weight = 10.0; + public GargishStuddedArmsType2() : base(0x283) + { + } + + public override double DefaultWeight => 10.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Studded/GargishStuddedChestType1.cs b/Projects/UOContent/Items/Armor/Studded/GargishStuddedChestType1.cs index 6d3305544..091e8ee30 100644 --- a/Projects/UOContent/Items/Armor/Studded/GargishStuddedChestType1.cs +++ b/Projects/UOContent/Items/Armor/Studded/GargishStuddedChestType1.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishStuddedChestType1 : BaseArmor { [Constructible] - public GargishStuddedChestType1() : base(0x286) => Weight = 15.0; + public GargishStuddedChestType1() : base(0x286) + { + } + + public override double DefaultWeight => 15.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Studded/GargishStuddedChestType2.cs b/Projects/UOContent/Items/Armor/Studded/GargishStuddedChestType2.cs index ef80c6847..8420a9a46 100644 --- a/Projects/UOContent/Items/Armor/Studded/GargishStuddedChestType2.cs +++ b/Projects/UOContent/Items/Armor/Studded/GargishStuddedChestType2.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishStuddedChestType2 : BaseArmor { [Constructible] - public GargishStuddedChestType2() : base(0x285) => Weight = 15.0; + public GargishStuddedChestType2() : base(0x285) + { + } + + public override double DefaultWeight => 15.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Studded/GargishStuddedKiltType1.cs b/Projects/UOContent/Items/Armor/Studded/GargishStuddedKiltType1.cs index 2b61adb4d..037190b79 100644 --- a/Projects/UOContent/Items/Armor/Studded/GargishStuddedKiltType1.cs +++ b/Projects/UOContent/Items/Armor/Studded/GargishStuddedKiltType1.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishStuddedKiltType1 : BaseArmor { [Constructible] - public GargishStuddedKiltType1() : base(0x288) => Weight = 10.0; + public GargishStuddedKiltType1() : base(0x288) + { + } + + public override double DefaultWeight => 10.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Studded/GargishStuddedKiltType2.cs b/Projects/UOContent/Items/Armor/Studded/GargishStuddedKiltType2.cs index a5d60040f..862df4243 100644 --- a/Projects/UOContent/Items/Armor/Studded/GargishStuddedKiltType2.cs +++ b/Projects/UOContent/Items/Armor/Studded/GargishStuddedKiltType2.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishStuddedKiltType2 : BaseArmor { [Constructible] - public GargishStuddedKiltType2() : base(0x287) => Weight = 10.0; + public GargishStuddedKiltType2() : base(0x287) + { + } + + public override double DefaultWeight => 10.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Studded/GargishStuddedLegsType1.cs b/Projects/UOContent/Items/Armor/Studded/GargishStuddedLegsType1.cs index 8b98f861b..86215b118 100644 --- a/Projects/UOContent/Items/Armor/Studded/GargishStuddedLegsType1.cs +++ b/Projects/UOContent/Items/Armor/Studded/GargishStuddedLegsType1.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishStuddedLegsType1 : BaseArmor { [Constructible] - public GargishStuddedLegsType1() : base(0x28A) => Weight = 15.0; + public GargishStuddedLegsType1() : base(0x28A) + { + } + + public override double DefaultWeight => 15.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Studded/GargishStuddedLegsType2.cs b/Projects/UOContent/Items/Armor/Studded/GargishStuddedLegsType2.cs index 3984ad6eb..fc94f830d 100644 --- a/Projects/UOContent/Items/Armor/Studded/GargishStuddedLegsType2.cs +++ b/Projects/UOContent/Items/Armor/Studded/GargishStuddedLegsType2.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GargishStuddedLegsType2 : BaseArmor { [Constructible] - public GargishStuddedLegsType2() : base(0x289) => Weight = 15.0; + public GargishStuddedLegsType2() : base(0x289) + { + } + + public override double DefaultWeight => 15.0; public override int RequiredRaces => Race.AllowGargoylesOnly; public override int BasePhysicalResistance => 6; diff --git a/Projects/UOContent/Items/Armor/Studded/HideChest.cs b/Projects/UOContent/Items/Armor/Studded/HideChest.cs index b853279ba..35a66edf1 100644 --- a/Projects/UOContent/Items/Armor/Studded/HideChest.cs +++ b/Projects/UOContent/Items/Armor/Studded/HideChest.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class HideChest : BaseArmor { [Constructible] - public HideChest() : base(0x2B74) => Weight = 6.0; + public HideChest() : base(0x2B74) + { + } + + public override double DefaultWeight => 6.0; public override int RequiredRaces => Race.AllowElvesOnly; diff --git a/Projects/UOContent/Items/Armor/Studded/HideFemaleChest.cs b/Projects/UOContent/Items/Armor/Studded/HideFemaleChest.cs index acafa6b7f..3a134e729 100644 --- a/Projects/UOContent/Items/Armor/Studded/HideFemaleChest.cs +++ b/Projects/UOContent/Items/Armor/Studded/HideFemaleChest.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class HideFemaleChest : BaseArmor { [Constructible] - public HideFemaleChest() : base(0x2B79) => Weight = 6.0; + public HideFemaleChest() : base(0x2B79) + { + } + + public override double DefaultWeight => 6.0; public override int RequiredRaces => Race.AllowElvesOnly; diff --git a/Projects/UOContent/Items/Armor/Studded/HideGloves.cs b/Projects/UOContent/Items/Armor/Studded/HideGloves.cs index cc9bde92a..5162e15ae 100644 --- a/Projects/UOContent/Items/Armor/Studded/HideGloves.cs +++ b/Projects/UOContent/Items/Armor/Studded/HideGloves.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class HideGloves : BaseArmor { [Constructible] - public HideGloves() : base(0x2B75) => Weight = 2.0; + public HideGloves() : base(0x2B75) + { + } + + public override double DefaultWeight => 2.0; public override int RequiredRaces => Race.AllowElvesOnly; diff --git a/Projects/UOContent/Items/Armor/Studded/HideGorget.cs b/Projects/UOContent/Items/Armor/Studded/HideGorget.cs index 831c816da..d47c9bc4a 100644 --- a/Projects/UOContent/Items/Armor/Studded/HideGorget.cs +++ b/Projects/UOContent/Items/Armor/Studded/HideGorget.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class HideGorget : BaseArmor { [Constructible] - public HideGorget() : base(0x2B76) => Weight = 3.0; + public HideGorget() : base(0x2B76) + { + } + + public override double DefaultWeight => 3.0; public override int RequiredRaces => Race.AllowElvesOnly; diff --git a/Projects/UOContent/Items/Armor/Studded/HidePants.cs b/Projects/UOContent/Items/Armor/Studded/HidePants.cs index 3b952d3bc..2c24640c6 100644 --- a/Projects/UOContent/Items/Armor/Studded/HidePants.cs +++ b/Projects/UOContent/Items/Armor/Studded/HidePants.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class HidePants : BaseArmor { [Constructible] - public HidePants() : base(0x2B78) => Weight = 5.0; + public HidePants() : base(0x2B78) + { + } + + public override double DefaultWeight => 5.0; public override int RequiredRaces => Race.AllowElvesOnly; diff --git a/Projects/UOContent/Items/Armor/Studded/HidePauldrons.cs b/Projects/UOContent/Items/Armor/Studded/HidePauldrons.cs index 0eb6b8b3d..3fb25160a 100644 --- a/Projects/UOContent/Items/Armor/Studded/HidePauldrons.cs +++ b/Projects/UOContent/Items/Armor/Studded/HidePauldrons.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class HidePauldrons : BaseArmor { [Constructible] - public HidePauldrons() : base(0x2B77) => Weight = 4.0; + public HidePauldrons() : base(0x2B77) + { + } + + public override double DefaultWeight => 4.0; public override int RequiredRaces => Race.AllowElvesOnly; diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedArms.cs b/Projects/UOContent/Items/Armor/Studded/StuddedArms.cs index 4fa9de780..641e989f6 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedArms.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedArms.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class StuddedArms : BaseArmor { [Constructible] - public StuddedArms() : base(0x13DC) => Weight = 4.0; + public StuddedArms() : base(0x13DC) + { + } + + public override double DefaultWeight => 4.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedBustierArms.cs b/Projects/UOContent/Items/Armor/Studded/StuddedBustierArms.cs index 3dd466c49..9d0411665 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedBustierArms.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedBustierArms.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class StuddedBustierArms : BaseArmor { [Constructible] - public StuddedBustierArms() : base(0x1C0C) => Weight = 1.0; + public StuddedBustierArms() : base(0x1C0C) + { + } + + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedChest.cs b/Projects/UOContent/Items/Armor/Studded/StuddedChest.cs index 2698f5b09..ff67d1e09 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedChest.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedChest.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class StuddedChest : BaseArmor { [Constructible] - public StuddedChest() : base(0x13DB) => Weight = 8.0; + public StuddedChest() : base(0x13DB) + { + } + + public override double DefaultWeight => 8.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedDo.cs b/Projects/UOContent/Items/Armor/Studded/StuddedDo.cs index 692076af7..44f788b84 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedDo.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedDo.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class StuddedDo : BaseArmor { [Constructible] - public StuddedDo() : base(0x27C7) => Weight = 8.0; + public StuddedDo() : base(0x27C7) + { + } + + public override double DefaultWeight => 8.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedGloves.cs b/Projects/UOContent/Items/Armor/Studded/StuddedGloves.cs index aa2d585ea..f6a6052d2 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedGloves.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedGloves.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class StuddedGloves : BaseArmor { [Constructible] - public StuddedGloves() : base(0x13D5) => Weight = 1.0; + public StuddedGloves() : base(0x13D5) + { + } + + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedGorget.cs b/Projects/UOContent/Items/Armor/Studded/StuddedGorget.cs index 079d99e3a..13d395382 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedGorget.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedGorget.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class StuddedGorget : BaseArmor { [Constructible] - public StuddedGorget() : base(0x13D6) => Weight = 1.0; + public StuddedGorget() : base(0x13D6) + { + } + + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedHaidate.cs b/Projects/UOContent/Items/Armor/Studded/StuddedHaidate.cs index 26b41dd45..789793aec 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedHaidate.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedHaidate.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class StuddedHaidate : BaseArmor { [Constructible] - public StuddedHaidate() : base(0x278B) => Weight = 5.0; + public StuddedHaidate() : base(0x278B) + { + } + + public override double DefaultWeight => 5.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedHiroSode.cs b/Projects/UOContent/Items/Armor/Studded/StuddedHiroSode.cs index 0b0afd5bd..a8677a31f 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedHiroSode.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedHiroSode.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class StuddedHiroSode : BaseArmor { [Constructible] - public StuddedHiroSode() : base(0x277F) => Weight = 1.0; + public StuddedHiroSode() : base(0x277F) + { + } + + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedLegs.cs b/Projects/UOContent/Items/Armor/Studded/StuddedLegs.cs index 85863125b..92b63098f 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedLegs.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedLegs.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class StuddedLegs : BaseArmor { [Constructible] - public StuddedLegs() : base(0x13DA) => Weight = 5.0; + public StuddedLegs() : base(0x13DA) + { + } + + public override double DefaultWeight => 5.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedMempo.cs b/Projects/UOContent/Items/Armor/Studded/StuddedMempo.cs index 31b0b9a82..ab783139a 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedMempo.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedMempo.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class StuddedMempo : BaseArmor { [Constructible] - public StuddedMempo() : base(0x279D) => Weight = 2.0; + public StuddedMempo() : base(0x279D) + { + } + + public override double DefaultWeight => 2.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Armor/Studded/StuddedSuneate.cs b/Projects/UOContent/Items/Armor/Studded/StuddedSuneate.cs index f57939ea1..36a7a2cf6 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedSuneate.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedSuneate.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class StuddedSuneate : BaseArmor { [Constructible] - public StuddedSuneate() : base(0x27D2) => Weight = 5.0; + public StuddedSuneate() : base(0x27D2) + { + } + + public override double DefaultWeight => 5.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; diff --git a/Projects/UOContent/Items/Body Parts/BonePile.cs b/Projects/UOContent/Items/Body Parts/BonePile.cs index 04fcadcda..9b65e61d7 100644 --- a/Projects/UOContent/Items/Body Parts/BonePile.cs +++ b/Projects/UOContent/Items/Body Parts/BonePile.cs @@ -7,11 +7,9 @@ namespace Server.Items public partial class BonePile : Item, IScissorable { [Constructible] - public BonePile() : base(0x1B09 + Utility.Random(8)) - { - Stackable = false; - Weight = 10.0; - } + public BonePile() : base(0x1B09 + Utility.Random(8)) => Stackable = false; + + public override double DefaultWeight => 10.0; public bool Scissor(Mobile from, Scissors scissors) { diff --git a/Projects/UOContent/Items/Body Parts/RibCage.cs b/Projects/UOContent/Items/Body Parts/RibCage.cs index 0b02dfce0..ecfac5be3 100644 --- a/Projects/UOContent/Items/Body Parts/RibCage.cs +++ b/Projects/UOContent/Items/Body Parts/RibCage.cs @@ -7,11 +7,9 @@ namespace Server.Items public partial class RibCage : Item, IScissorable { [Constructible] - public RibCage() : base(0x1B17 + Utility.Random(2)) - { - Stackable = false; - Weight = 5.0; - } + public RibCage() : base(0x1B17 + Utility.Random(2)) => Stackable = false; + + public override double DefaultWeight => 5.0; public bool Scissor(Mobile from, Scissors scissors) { diff --git a/Projects/UOContent/Items/Body Parts/Torso.cs b/Projects/UOContent/Items/Body Parts/Torso.cs index 8a26dbecf..3ca8e5415 100644 --- a/Projects/UOContent/Items/Body Parts/Torso.cs +++ b/Projects/UOContent/Items/Body Parts/Torso.cs @@ -6,6 +6,10 @@ namespace Server.Items public partial class Torso : Item { [Constructible] - public Torso() : base(0x1D9F) => Weight = 2.0; + public Torso() : base(0x1D9F) + { + } + + public override double DefaultWeight => 2.0; } } diff --git a/Projects/UOContent/Items/Books/BaseBook.cs b/Projects/UOContent/Items/Books/BaseBook.cs index b70dcf854..47f368754 100644 --- a/Projects/UOContent/Items/Books/BaseBook.cs +++ b/Projects/UOContent/Items/Books/BaseBook.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using ModernUO.Serialization; using Server.Collections; using Server.ContextMenus; @@ -141,11 +140,14 @@ namespace Server.Items { get { - var lines = new List(); - - foreach (var bpi in Pages) + using var lines = PooledRefQueue.Create(256); + for (var i = 0; i < Pages.Length; i++) { - lines.AddRange(bpi.Lines); + var bpi = Pages[i]; + for (var j = 0; j < bpi.Lines.Length; j++) + { + lines.Enqueue(bpi.Lines[j]); + } } return lines.ToArray(); diff --git a/Projects/UOContent/Items/Bulletin Boards/BulletinMessage.cs b/Projects/UOContent/Items/Bulletin Boards/BulletinMessage.cs index 2e4500d10..8fa3ddd05 100644 --- a/Projects/UOContent/Items/Bulletin Boards/BulletinMessage.cs +++ b/Projects/UOContent/Items/Bulletin Boards/BulletinMessage.cs @@ -1,6 +1,6 @@ using System; -using System.Collections.Generic; using ModernUO.Serialization; +using Server.Collections; using Server.Targeting; namespace Server.Items @@ -22,7 +22,7 @@ namespace Server.Items PostedHue = Poster.Hue; Lines = lines; - var list = new List(); + using var list = PooledRefQueue.Create(poster.Items.Count); for (var i = 0; i < poster.Items.Count; ++i) { @@ -30,7 +30,7 @@ namespace Server.Items if (item.Layer >= Layer.OneHanded && item.Layer <= Layer.Mount) { - list.Add(new BulletinEquip(item.ItemID, item.Hue)); + list.Enqueue(new BulletinEquip(item.ItemID, item.Hue)); } } diff --git a/Projects/UOContent/Items/Champion Artifacts/Decorative/SkullPole.cs b/Projects/UOContent/Items/Champion Artifacts/Decorative/SkullPole.cs index 5834239ec..708bb6688 100644 --- a/Projects/UOContent/Items/Champion Artifacts/Decorative/SkullPole.cs +++ b/Projects/UOContent/Items/Champion Artifacts/Decorative/SkullPole.cs @@ -6,6 +6,10 @@ namespace Server.Items public partial class SkullPole : Item { [Constructible] - public SkullPole() : base(0x2204) => Weight = 5; + public SkullPole() : base(0x2204) + { + } + + public override double DefaultWeight => 5; } } diff --git a/Projects/UOContent/Items/Clothing/Cloaks.cs b/Projects/UOContent/Items/Clothing/Cloaks.cs index e18e5a510..67f572d9d 100644 --- a/Projects/UOContent/Items/Clothing/Cloaks.cs +++ b/Projects/UOContent/Items/Clothing/Cloaks.cs @@ -16,7 +16,11 @@ namespace Server.Items public partial class Cloak : BaseCloak, IArcaneEquip { [Constructible] - public Cloak(int hue = 0) : base(0x1515, hue) => Weight = 5.0; + public Cloak(int hue = 0) : base(0x1515, hue) + { + } + + public override double DefaultWeight => 5.0; [EncodedInt] [SerializableProperty(0)] @@ -124,12 +128,12 @@ namespace Server.Items [Constructible] public RewardCloak(int hue = 0, int labelNumber = 0) : base(0x1515, hue) { - Weight = 5.0; LootType = LootType.Blessed; - _number = labelNumber; } + public override double DefaultWeight => 5.0; + public override int LabelNumber => _number > 0 ? _number : base.LabelNumber; public override int BasePhysicalResistance => 3; @@ -190,6 +194,10 @@ namespace Server.Items public partial class FurCape : BaseCloak { [Constructible] - public FurCape(int hue = 0) : base(0x230A, hue) => Weight = 4.0; + public FurCape(int hue = 0) : base(0x230A, hue) + { + } + + public override double DefaultWeight => 4.0; } } diff --git a/Projects/UOContent/Items/Clothing/Hats.cs b/Projects/UOContent/Items/Clothing/Hats.cs index d6bec6723..1d5f31643 100644 --- a/Projects/UOContent/Items/Clothing/Hats.cs +++ b/Projects/UOContent/Items/Clothing/Hats.cs @@ -62,7 +62,11 @@ namespace Server.Items public partial class Kasa : BaseHat { [Constructible] - public Kasa(int hue = 0) : base(0x2798, hue) => Weight = 3.0; + public Kasa(int hue = 0) : base(0x2798, hue) + { + } + + public override double DefaultWeight => 3.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -79,7 +83,11 @@ namespace Server.Items public partial class ClothNinjaHood : BaseHat { [Constructible] - public ClothNinjaHood(int hue = 0) : base(0x278F, hue) => Weight = 2.0; + public ClothNinjaHood(int hue = 0) : base(0x278F, hue) + { + } + + public override double DefaultWeight => 2.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; @@ -96,7 +104,11 @@ namespace Server.Items public partial class FlowerGarland : BaseHat { [Constructible] - public FlowerGarland(int hue = 0) : base(0x2306, hue) => Weight = 1.0; + public FlowerGarland(int hue = 0) : base(0x2306, hue) + { + } + + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; @@ -112,7 +124,11 @@ namespace Server.Items public partial class FloppyHat : BaseHat { [Constructible] - public FloppyHat(int hue = 0) : base(0x1713, hue) => Weight = 1.0; + public FloppyHat(int hue = 0) : base(0x1713, hue) + { + } + + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -128,7 +144,11 @@ namespace Server.Items public partial class WideBrimHat : BaseHat { [Constructible] - public WideBrimHat(int hue = 0) : base(0x1714, hue) => Weight = 1.0; + public WideBrimHat(int hue = 0) : base(0x1714, hue) + { + } + + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -144,7 +164,11 @@ namespace Server.Items public partial class Cap : BaseHat { [Constructible] - public Cap(int hue = 0) : base(0x1715, hue) => Weight = 1.0; + public Cap(int hue = 0) : base(0x1715, hue) + { + } + + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -160,7 +184,11 @@ namespace Server.Items public partial class SkullCap : BaseHat { [Constructible] - public SkullCap(int hue = 0) : base(0x1544, hue) => Weight = 1.0; + public SkullCap(int hue = 0) : base(0x1544, hue) + { + } + + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 3; @@ -176,7 +204,11 @@ namespace Server.Items public partial class Bandana : BaseHat { [Constructible] - public Bandana(int hue = 0) : base(0x1540, hue) => Weight = 1.0; + public Bandana(int hue = 0) : base(0x1540, hue) + { + } + + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 3; @@ -192,7 +224,11 @@ namespace Server.Items public partial class BearMask : BaseHat { [Constructible] - public BearMask(int hue = 0) : base(0x1545, hue) => Weight = 5.0; + public BearMask(int hue = 0) : base(0x1545, hue) + { + } + + public override double DefaultWeight => 5.0; public override int BasePhysicalResistance => 5; public override int BaseFireResistance => 3; @@ -214,7 +250,11 @@ namespace Server.Items public partial class DeerMask : BaseHat { [Constructible] - public DeerMask(int hue = 0) : base(0x1547, hue) => Weight = 4.0; + public DeerMask(int hue = 0) : base(0x1547, hue) + { + } + + public override double DefaultWeight => 4.0; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 6; @@ -236,7 +276,11 @@ namespace Server.Items public partial class HornedTribalMask : BaseHat { [Constructible] - public HornedTribalMask(int hue = 0) : base(0x1549, hue) => Weight = 2.0; + public HornedTribalMask(int hue = 0) : base(0x1549, hue) + { + } + + public override double DefaultWeight => 2.0; public override int BasePhysicalResistance => 6; public override int BaseFireResistance => 9; @@ -258,7 +302,11 @@ namespace Server.Items public partial class TribalMask : BaseHat { [Constructible] - public TribalMask(int hue = 0) : base(0x154B, hue) => Weight = 2.0; + public TribalMask(int hue = 0) : base(0x154B, hue) + { + } + + public override double DefaultWeight => 2.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 0; @@ -280,7 +328,11 @@ namespace Server.Items public partial class TallStrawHat : BaseHat { [Constructible] - public TallStrawHat(int hue = 0) : base(0x1716, hue) => Weight = 1.0; + public TallStrawHat(int hue = 0) : base(0x1716, hue) + { + } + + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -296,7 +348,11 @@ namespace Server.Items public partial class StrawHat : BaseHat { [Constructible] - public StrawHat(int hue = 0) : base(0x1717, hue) => Weight = 1.0; + public StrawHat(int hue = 0) : base(0x1717, hue) + { + } + + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -312,7 +368,11 @@ namespace Server.Items public partial class OrcishKinMask : BaseHat { [Constructible] - public OrcishKinMask(int hue = 0x8A4) : base(0x141B, hue) => Weight = 2.0; + public OrcishKinMask(int hue = 0x8A4) : base(0x141B, hue) + { + } + + public override double DefaultWeight => 2.0; public override int BasePhysicalResistance => 1; public override int BaseFireResistance => 1; @@ -367,7 +427,11 @@ namespace Server.Items } [Constructible] - public SavageMask(int hue) : base(0x154B, hue) => Weight = 2.0; + public SavageMask(int hue) : base(0x154B, hue) + { + } + + public override double DefaultWeight => 2.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 0; @@ -401,7 +465,11 @@ namespace Server.Items public partial class WizardsHat : BaseHat { [Constructible] - public WizardsHat(int hue = 0) : base(0x1718, hue) => Weight = 1.0; + public WizardsHat(int hue = 0) : base(0x1718, hue) + { + } + + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -417,7 +485,11 @@ namespace Server.Items public partial class MagicWizardsHat : BaseHat { [Constructible] - public MagicWizardsHat(int hue = 0) : base(0x1718, hue) => Weight = 1.0; + public MagicWizardsHat(int hue = 0) : base(0x1718, hue) + { + } + + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -439,7 +511,11 @@ namespace Server.Items public partial class Bonnet : BaseHat { [Constructible] - public Bonnet(int hue = 0) : base(0x1719, hue) => Weight = 1.0; + public Bonnet(int hue = 0) : base(0x1719, hue) + { + } + + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -455,7 +531,11 @@ namespace Server.Items public partial class FeatheredHat : BaseHat { [Constructible] - public FeatheredHat(int hue = 0) : base(0x171A, hue) => Weight = 1.0; + public FeatheredHat(int hue = 0) : base(0x171A, hue) + { + } + + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -471,7 +551,11 @@ namespace Server.Items public partial class TricorneHat : BaseHat { [Constructible] - public TricorneHat(int hue = 0) : base(0x171B, hue) => Weight = 1.0; + public TricorneHat(int hue = 0) : base(0x171B, hue) + { + } + + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -487,7 +571,11 @@ namespace Server.Items public partial class JesterHat : BaseHat { [Constructible] - public JesterHat(int hue = 0) : base(0x171C, hue) => Weight = 1.0; + public JesterHat(int hue = 0) : base(0x171C, hue) + { + } + + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; diff --git a/Projects/UOContent/Items/Clothing/MiddleTorso.cs b/Projects/UOContent/Items/Clothing/MiddleTorso.cs index 27e3bc4b6..31299b316 100644 --- a/Projects/UOContent/Items/Clothing/MiddleTorso.cs +++ b/Projects/UOContent/Items/Clothing/MiddleTorso.cs @@ -15,7 +15,11 @@ namespace Server.Items public partial class BodySash : BaseMiddleTorso { [Constructible] - public BodySash(int hue = 0) : base(0x1541, hue) => Weight = 1.0; + public BodySash(int hue = 0) : base(0x1541, hue) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -23,7 +27,11 @@ namespace Server.Items public partial class FullApron : BaseMiddleTorso { [Constructible] - public FullApron(int hue = 0) : base(0x153d, hue) => Weight = 4.0; + public FullApron(int hue = 0) : base(0x153d, hue) + { + } + + public override double DefaultWeight => 4.0; } [SerializationGenerator(0, false)] @@ -31,7 +39,11 @@ namespace Server.Items public partial class Doublet : BaseMiddleTorso { [Constructible] - public Doublet(int hue = 0) : base(0x1F7B, hue) => Weight = 2.0; + public Doublet(int hue = 0) : base(0x1F7B, hue) + { + } + + public override double DefaultWeight => 2.0; } [SerializationGenerator(0, false)] @@ -39,7 +51,11 @@ namespace Server.Items public partial class Surcoat : BaseMiddleTorso { [Constructible] - public Surcoat(int hue = 0) : base(0x1FFD, hue) => Weight = 6.0; + public Surcoat(int hue = 0) : base(0x1FFD, hue) + { + } + + public override double DefaultWeight => 6.0; } [SerializationGenerator(0, false)] @@ -47,7 +63,11 @@ namespace Server.Items public partial class Tunic : BaseMiddleTorso { [Constructible] - public Tunic(int hue = 0) : base(0x1FA1, hue) => Weight = 5.0; + public Tunic(int hue = 0) : base(0x1FA1, hue) + { + } + + public override double DefaultWeight => 5.0; } [SerializationGenerator(0, false)] @@ -55,7 +75,11 @@ namespace Server.Items public partial class FormalShirt : BaseMiddleTorso { [Constructible] - public FormalShirt(int hue = 0) : base(0x2310, hue) => Weight = 1.0; + public FormalShirt(int hue = 0) : base(0x2310, hue) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -63,7 +87,11 @@ namespace Server.Items public partial class JesterSuit : BaseMiddleTorso { [Constructible] - public JesterSuit(int hue = 0) : base(0x1F9F, hue) => Weight = 4.0; + public JesterSuit(int hue = 0) : base(0x1F9F, hue) + { + } + + public override double DefaultWeight => 4.0; } [SerializationGenerator(0, false)] @@ -71,6 +99,10 @@ namespace Server.Items public partial class JinBaori : BaseMiddleTorso { [Constructible] - public JinBaori(int hue = 0) : base(0x27A1, hue) => Weight = 3.0; + public JinBaori(int hue = 0) : base(0x27A1, hue) + { + } + + public override double DefaultWeight => 3.0; } } diff --git a/Projects/UOContent/Items/Clothing/OuterLegs.cs b/Projects/UOContent/Items/Clothing/OuterLegs.cs index d060c306f..a3b3ba4b8 100644 --- a/Projects/UOContent/Items/Clothing/OuterLegs.cs +++ b/Projects/UOContent/Items/Clothing/OuterLegs.cs @@ -15,7 +15,11 @@ namespace Server.Items public partial class FurSarong : BaseOuterLegs { [Constructible] - public FurSarong(int hue = 0) : base(0x230C, hue) => Weight = 3.0; + public FurSarong(int hue = 0) : base(0x230C, hue) + { + } + + public override double DefaultWeight => 3.0; } [SerializationGenerator(0, false)] @@ -23,7 +27,11 @@ namespace Server.Items public partial class Skirt : BaseOuterLegs { [Constructible] - public Skirt(int hue = 0) : base(0x1516, hue) => Weight = 4.0; + public Skirt(int hue = 0) : base(0x1516, hue) + { + } + + public override double DefaultWeight => 4.0; } [SerializationGenerator(0, false)] @@ -31,7 +39,11 @@ namespace Server.Items public partial class Kilt : BaseOuterLegs { [Constructible] - public Kilt(int hue = 0) : base(0x1537, hue) => Weight = 2.0; + public Kilt(int hue = 0) : base(0x1537, hue) + { + } + + public override double DefaultWeight => 2.0; } [SerializationGenerator(0, false)] @@ -39,6 +51,10 @@ namespace Server.Items public partial class Hakama : BaseOuterLegs { [Constructible] - public Hakama(int hue = 0) : base(0x279A, hue) => Weight = 2.0; + public Hakama(int hue = 0) : base(0x279A, hue) + { + } + + public override double DefaultWeight => 2.0; } } diff --git a/Projects/UOContent/Items/Clothing/OuterTorso.cs b/Projects/UOContent/Items/Clothing/OuterTorso.cs index 5f147fcb2..4fda45bb5 100644 --- a/Projects/UOContent/Items/Clothing/OuterTorso.cs +++ b/Projects/UOContent/Items/Clothing/OuterTorso.cs @@ -18,7 +18,11 @@ namespace Server.Items public partial class GildedDress : BaseOuterTorso { [Constructible] - public GildedDress(int hue = 0) : base(0x230E, hue) => Weight = 3.0; + public GildedDress(int hue = 0) : base(0x230E, hue) + { + } + + public override double DefaultWeight => 3.0; } [SerializationGenerator(0, false)] @@ -26,7 +30,11 @@ namespace Server.Items public partial class FancyDress : BaseOuterTorso { [Constructible] - public FancyDress(int hue = 0) : base(0x1F00, hue) => Weight = 3.0; + public FancyDress(int hue = 0) : base(0x1F00, hue) + { + } + + public override double DefaultWeight => 3.0; } [SerializationGenerator(3, false)] @@ -157,12 +165,12 @@ namespace Server.Items [Constructible] public RewardRobe(int hue = 0, int labelNumber = 0) : base(0x1F03, hue) { - Weight = 3.0; LootType = LootType.Blessed; - _number = labelNumber; } + public override double DefaultWeight => 3.0; + public override int LabelNumber => _number > 0 ? _number : base.LabelNumber; public override int BasePhysicalResistance => 3; @@ -239,12 +247,12 @@ namespace Server.Items [Constructible] public RewardDress(int hue = 0, int labelNumber = 0) : base(0x1F01, hue) { - Weight = 2.0; LootType = LootType.Blessed; - _number = labelNumber; } + public override double DefaultWeight => 2.0; + public override int LabelNumber => _number > 0 ? _number : base.LabelNumber; public override int BasePhysicalResistance => 3; @@ -310,7 +318,11 @@ namespace Server.Items public partial class Robe : BaseOuterTorso, IArcaneEquip { [Constructible] - public Robe(int hue = 0) : base(0x1F03, hue) => Weight = 3.0; + public Robe(int hue = 0) : base(0x1F03, hue) + { + } + + public override double DefaultWeight => 3.0; [EncodedInt] [SerializableProperty(0)] @@ -406,11 +418,9 @@ namespace Server.Items public partial class MonkRobe : BaseOuterTorso { [Constructible] - public MonkRobe(int hue = 0x21E) : base(0x2687, hue) - { - Weight = 1.0; - StrRequirement = 0; - } + public MonkRobe(int hue = 0x21E) : base(0x2687, hue) => StrRequirement = 0; + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1076584; // A monk's robe public override bool CanBeBlessed => false; @@ -427,7 +437,11 @@ namespace Server.Items public partial class PlainDress : BaseOuterTorso { [Constructible] - public PlainDress(int hue = 0) : base(0x1F01, hue) => Weight = 2.0; + public PlainDress(int hue = 0) : base(0x1F01, hue) + { + } + + public override double DefaultWeight => 2.0; } [Flippable(0x2799, 0x27E4)] @@ -435,7 +449,11 @@ namespace Server.Items public partial class Kamishimo : BaseOuterTorso { [Constructible] - public Kamishimo(int hue = 0) : base(0x2799, hue) => Weight = 3.0; + public Kamishimo(int hue = 0) : base(0x2799, hue) + { + } + + public override double DefaultWeight => 3.0; } [Flippable(0x279C, 0x27E7)] @@ -443,7 +461,11 @@ namespace Server.Items public partial class HakamaShita : BaseOuterTorso { [Constructible] - public HakamaShita(int hue = 0) : base(0x279C, hue) => Weight = 3.0; + public HakamaShita(int hue = 0) : base(0x279C, hue) + { + } + + public override double DefaultWeight => 3.0; } [Flippable(0x2782, 0x27CD)] @@ -451,7 +473,11 @@ namespace Server.Items public partial class MaleKimono : BaseOuterTorso { [Constructible] - public MaleKimono(int hue = 0) : base(0x2782, hue) => Weight = 3.0; + public MaleKimono(int hue = 0) : base(0x2782, hue) + { + } + + public override double DefaultWeight => 3.0; } [Flippable(0x2783, 0x27CE)] @@ -459,7 +485,11 @@ namespace Server.Items public partial class FemaleKimono : BaseOuterTorso { [Constructible] - public FemaleKimono(int hue = 0) : base(0x2783, hue) => Weight = 3.0; + public FemaleKimono(int hue = 0) : base(0x2783, hue) + { + } + + public override double DefaultWeight => 3.0; } [Flippable(0x2FB9, 0x3173)] @@ -467,7 +497,11 @@ namespace Server.Items public partial class MaleElvenRobe : BaseOuterTorso { [Constructible] - public MaleElvenRobe(int hue = 0) : base(0x2FB9, hue) => Weight = 2.0; + public MaleElvenRobe(int hue = 0) : base(0x2FB9, hue) + { + } + + public override double DefaultWeight => 2.0; } [Flippable(0x2FBA, 0x3174)] @@ -475,7 +509,11 @@ namespace Server.Items public partial class FemaleElvenRobe : BaseOuterTorso { [Constructible] - public FemaleElvenRobe(int hue = 0) : base(0x2FBA, hue) => Weight = 2.0; + public FemaleElvenRobe(int hue = 0) : base(0x2FBA, hue) + { + } + + public override double DefaultWeight => 2.0; public override int RequiredRaces => Race.AllowElvesOnly; diff --git a/Projects/UOContent/Items/Clothing/Pants.cs b/Projects/UOContent/Items/Clothing/Pants.cs index efee4e379..69fe21011 100644 --- a/Projects/UOContent/Items/Clothing/Pants.cs +++ b/Projects/UOContent/Items/Clothing/Pants.cs @@ -15,7 +15,11 @@ namespace Server.Items public partial class ShortPants : BasePants { [Constructible] - public ShortPants(int hue = 0) : base(0x152E, hue) => Weight = 2.0; + public ShortPants(int hue = 0) : base(0x152E, hue) + { + } + + public override double DefaultWeight => 2.0; } [Flippable(0x1539, 0x153a)] @@ -23,7 +27,11 @@ namespace Server.Items public partial class LongPants : BasePants { [Constructible] - public LongPants(int hue = 0) : base(0x1539, hue) => Weight = 2.0; + public LongPants(int hue = 0) : base(0x1539, hue) + { + } + + public override double DefaultWeight => 2.0; } [Flippable(0x279B, 0x27E6)] @@ -31,7 +39,11 @@ namespace Server.Items public partial class TattsukeHakama : BasePants { [Constructible] - public TattsukeHakama(int hue = 0) : base(0x279B, hue) => Weight = 2.0; + public TattsukeHakama(int hue = 0) : base(0x279B, hue) + { + } + + public override double DefaultWeight => 2.0; } [Flippable(0x2FC3, 0x3179)] @@ -39,7 +51,11 @@ namespace Server.Items public partial class ElvenPants : BasePants { [Constructible] - public ElvenPants(int hue = 0) : base(0x2FC3, hue) => Weight = 2.0; + public ElvenPants(int hue = 0) : base(0x2FC3, hue) + { + } + + public override double DefaultWeight => 2.0; public override int RequiredRaces => Race.AllowElvesOnly; } diff --git a/Projects/UOContent/Items/Clothing/Shirts.cs b/Projects/UOContent/Items/Clothing/Shirts.cs index 6ba43a870..bd174067e 100644 --- a/Projects/UOContent/Items/Clothing/Shirts.cs +++ b/Projects/UOContent/Items/Clothing/Shirts.cs @@ -15,7 +15,11 @@ namespace Server.Items public partial class FancyShirt : BaseShirt { [Constructible] - public FancyShirt(int hue = 0) : base(0x1EFD, hue) => Weight = 2.0; + public FancyShirt(int hue = 0) : base(0x1EFD, hue) + { + } + + public override double DefaultWeight => 2.0; } [Flippable(0x1517, 0x1518)] @@ -23,7 +27,11 @@ namespace Server.Items public partial class Shirt : BaseShirt { [Constructible] - public Shirt(int hue = 0) : base(0x1517, hue) => Weight = 1.0; + public Shirt(int hue = 0) : base(0x1517, hue) + { + } + + public override double DefaultWeight => 1.0; } [Flippable(0x2794, 0x27DF)] @@ -31,18 +39,20 @@ namespace Server.Items public partial class ClothNinjaJacket : BaseShirt { [Constructible] - public ClothNinjaJacket(int hue = 0) : base(0x2794, hue) - { - Weight = 5.0; - Layer = Layer.InnerTorso; - } + public ClothNinjaJacket(int hue = 0) : base(0x2794, hue) => Layer = Layer.InnerTorso; + + public override double DefaultWeight => 5.0; } [SerializationGenerator(0)] public partial class ElvenShirt : BaseShirt { [Constructible] - public ElvenShirt(int hue = 0) : base(0x3175, hue) => Weight = 2.0; + public ElvenShirt(int hue = 0) : base(0x3175, hue) + { + } + + public override double DefaultWeight => 2.0; public override int RequiredRaces => Race.AllowElvesOnly; } @@ -51,6 +61,10 @@ namespace Server.Items public partial class ElvenDarkShirt : BaseShirt { [Constructible] - public ElvenDarkShirt(int hue = 0) : base(0x3176, hue) => Weight = 2.0; + public ElvenDarkShirt(int hue = 0) : base(0x3176, hue) + { + } + + public override double DefaultWeight => 2.0; } } diff --git a/Projects/UOContent/Items/Clothing/Shoes.cs b/Projects/UOContent/Items/Clothing/Shoes.cs index 8e6cb3d7a..bae7ec9fa 100644 --- a/Projects/UOContent/Items/Clothing/Shoes.cs +++ b/Projects/UOContent/Items/Clothing/Shoes.cs @@ -26,7 +26,11 @@ namespace Server.Items public partial class FurBoots : BaseShoes { [Constructible] - public FurBoots(int hue = 0) : base(0x2307, hue) => Weight = 3.0; + public FurBoots(int hue = 0) : base(0x2307, hue) + { + } + + public override double DefaultWeight => 3.0; } [Flippable(0x170b, 0x170c)] @@ -34,7 +38,11 @@ namespace Server.Items public partial class Boots : BaseShoes { [Constructible] - public Boots(int hue = 0) : base(0x170B, hue) => Weight = 3.0; + public Boots(int hue = 0) : base(0x170B, hue) + { + } + + public override double DefaultWeight => 3.0; public override CraftResource DefaultResource => CraftResource.RegularLeather; } @@ -44,7 +52,11 @@ namespace Server.Items public partial class ThighBoots : BaseShoes, IArcaneEquip { [Constructible] - public ThighBoots(int hue = 0) : base(0x1711, hue) => Weight = 4.0; + public ThighBoots(int hue = 0) : base(0x1711, hue) + { + } + + public override double DefaultWeight => 4.0; public override CraftResource DefaultResource => CraftResource.RegularLeather; @@ -143,7 +155,11 @@ namespace Server.Items public partial class Shoes : BaseShoes { [Constructible] - public Shoes(int hue = 0) : base(0x170F, hue) => Weight = 2.0; + public Shoes(int hue = 0) : base(0x170F, hue) + { + } + + public override double DefaultWeight => 2.0; public override CraftResource DefaultResource => CraftResource.RegularLeather; } @@ -153,7 +169,11 @@ namespace Server.Items public partial class Sandals : BaseShoes { [Constructible] - public Sandals(int hue = 0) : base(0x170D, hue) => Weight = 1.0; + public Sandals(int hue = 0) : base(0x170D, hue) + { + } + + public override double DefaultWeight => 1.0; public override CraftResource DefaultResource => CraftResource.RegularLeather; @@ -165,7 +185,11 @@ namespace Server.Items public partial class NinjaTabi : BaseShoes { [Constructible] - public NinjaTabi(int hue = 0) : base(0x2797, hue) => Weight = 2.0; + public NinjaTabi(int hue = 0) : base(0x2797, hue) + { + } + + public override double DefaultWeight => 2.0; } [Flippable(0x2796, 0x27E1)] @@ -173,7 +197,11 @@ namespace Server.Items public partial class SamuraiTabi : BaseShoes { [Constructible] - public SamuraiTabi(int hue = 0) : base(0x2796, hue) => Weight = 2.0; + public SamuraiTabi(int hue = 0) : base(0x2796, hue) + { + } + + public override double DefaultWeight => 2.0; } [Flippable(0x2796, 0x27E1)] @@ -181,7 +209,11 @@ namespace Server.Items public partial class Waraji : BaseShoes { [Constructible] - public Waraji(int hue = 0) : base(0x2796, hue) => Weight = 2.0; + public Waraji(int hue = 0) : base(0x2796, hue) + { + } + + public override double DefaultWeight => 2.0; } [Flippable(0x2FC4, 0x317A)] @@ -189,7 +221,11 @@ namespace Server.Items public partial class ElvenBoots : BaseShoes { [Constructible] - public ElvenBoots(int hue = 0) : base(0x2FC4, hue) => Weight = 2.0; + public ElvenBoots(int hue = 0) : base(0x2FC4, hue) + { + } + + public override double DefaultWeight => 2.0; public override CraftResource DefaultResource => CraftResource.RegularLeather; diff --git a/Projects/UOContent/Items/Clothing/Waist.cs b/Projects/UOContent/Items/Clothing/Waist.cs index 45f34d768..10dd9dab8 100644 --- a/Projects/UOContent/Items/Clothing/Waist.cs +++ b/Projects/UOContent/Items/Clothing/Waist.cs @@ -15,7 +15,11 @@ namespace Server.Items public partial class HalfApron : BaseWaist { [Constructible] - public HalfApron(int hue = 0) : base(0x153b, hue) => Weight = 2.0; + public HalfApron(int hue = 0) : base(0x153b, hue) + { + } + + public override double DefaultWeight => 2.0; } [Flippable(0x27A0, 0x27EB)] @@ -23,7 +27,11 @@ namespace Server.Items public partial class Obi : BaseWaist { [Constructible] - public Obi(int hue = 0) : base(0x27A0, hue) => Weight = 1.0; + public Obi(int hue = 0) : base(0x27A0, hue) + { + } + + public override double DefaultWeight => 1.0; } [Flippable(0x2B68, 0x315F)] @@ -31,7 +39,11 @@ namespace Server.Items public partial class WoodlandBelt : BaseWaist { [Constructible] - public WoodlandBelt(int hue = 0) : base(0x2B68, hue) => Weight = 4.0; + public WoodlandBelt(int hue = 0) : base(0x2B68, hue) + { + } + + public override double DefaultWeight => 4.0; public override int RequiredRaces => Race.AllowElvesOnly; diff --git a/Projects/UOContent/Items/Construction/Chairs/Benchs.cs b/Projects/UOContent/Items/Construction/Chairs/Benchs.cs index f37deba0b..e3ba5b2f3 100644 --- a/Projects/UOContent/Items/Construction/Chairs/Benchs.cs +++ b/Projects/UOContent/Items/Construction/Chairs/Benchs.cs @@ -8,6 +8,10 @@ namespace Server.Items public partial class WoodenBench : Item { [Constructible] - public WoodenBench() : base(0xB2D) => Weight = 6; + public WoodenBench() : base(0xB2D) + { + } + + public override double DefaultWeight => 6; } } diff --git a/Projects/UOContent/Items/Construction/Chairs/Chairs.cs b/Projects/UOContent/Items/Construction/Chairs/Chairs.cs index 12b2b9ad8..01f288d17 100644 --- a/Projects/UOContent/Items/Construction/Chairs/Chairs.cs +++ b/Projects/UOContent/Items/Construction/Chairs/Chairs.cs @@ -8,7 +8,11 @@ namespace Server.Items public partial class FancyWoodenChairCushion : Item { [Constructible] - public FancyWoodenChairCushion() : base(0xB4F) => Weight = 20.0; + public FancyWoodenChairCushion() : base(0xB4F) + { + } + + public override double DefaultWeight => 20.0; } [Furniture] @@ -17,7 +21,11 @@ namespace Server.Items public partial class WoodenChairCushion : Item { [Constructible] - public WoodenChairCushion() : base(0xB53) => Weight = 20.0; + public WoodenChairCushion() : base(0xB53) + { + } + + public override double DefaultWeight => 20.0; } [Furniture] @@ -26,7 +34,11 @@ namespace Server.Items public partial class WoodenChair : Item { [Constructible] - public WoodenChair() : base(0xB57) => Weight = 20.0; + public WoodenChair() : base(0xB57) + { + } + + public override double DefaultWeight => 20.0; } [Furniture] @@ -35,7 +47,11 @@ namespace Server.Items public partial class BambooChair : Item { [Constructible] - public BambooChair() : base(0xB5B) => Weight = 20.0; + public BambooChair() : base(0xB5B) + { + } + + public override double DefaultWeight => 20.0; } [DynamicFlipping] @@ -44,7 +60,11 @@ namespace Server.Items public partial class StoneChair : Item { [Constructible] - public StoneChair() : base(0x1218) => Weight = 20; + public StoneChair() : base(0x1218) + { + } + + public override double DefaultWeight => 20; } [DynamicFlipping] @@ -53,7 +73,11 @@ namespace Server.Items public partial class OrnateElvenChair : Item { [Constructible] - public OrnateElvenChair() : base(0x2DE3) => Weight = 1.0; + public OrnateElvenChair() : base(0x2DE3) + { + } + + public override double DefaultWeight => 1.0; } [DynamicFlipping] diff --git a/Projects/UOContent/Items/Construction/Chairs/Stools.cs b/Projects/UOContent/Items/Construction/Chairs/Stools.cs index 837b36972..30566f429 100644 --- a/Projects/UOContent/Items/Construction/Chairs/Stools.cs +++ b/Projects/UOContent/Items/Construction/Chairs/Stools.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class Stool : Item { [Constructible] - public Stool() : base(0xA2A) => Weight = 10.0; + public Stool() : base(0xA2A) + { + } + + public override double DefaultWeight => 10.0; } [Furniture] @@ -15,6 +19,10 @@ namespace Server.Items public partial class FootStool : Item { [Constructible] - public FootStool() : base(0xB5E) => Weight = 6.0; + public FootStool() : base(0xB5E) + { + } + + public override double DefaultWeight => 6.0; } } diff --git a/Projects/UOContent/Items/Construction/Chairs/Thrones.cs b/Projects/UOContent/Items/Construction/Chairs/Thrones.cs index de189504f..5bb86a2d2 100644 --- a/Projects/UOContent/Items/Construction/Chairs/Thrones.cs +++ b/Projects/UOContent/Items/Construction/Chairs/Thrones.cs @@ -8,7 +8,11 @@ namespace Server.Items public partial class Throne : Item { [Constructible] - public Throne() : base(0xB33) => Weight = 1.0; + public Throne() : base(0xB33) + { + } + + public override double DefaultWeight => 1.0; } [Furniture] @@ -17,6 +21,10 @@ namespace Server.Items public partial class WoodenThrone : Item { [Constructible] - public WoodenThrone() : base(0xB2E) => Weight = 15.0; + public WoodenThrone() : base(0xB2E) + { + } + + public override double DefaultWeight => 15.0; } } diff --git a/Projects/UOContent/Items/Construction/Decorative/GiantReplicaAcorn.cs b/Projects/UOContent/Items/Construction/Decorative/GiantReplicaAcorn.cs index d0a5c7a3e..4382653fc 100644 --- a/Projects/UOContent/Items/Construction/Decorative/GiantReplicaAcorn.cs +++ b/Projects/UOContent/Items/Construction/Decorative/GiantReplicaAcorn.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GiantReplicaAcorn : Item { [Constructible] - public GiantReplicaAcorn() : base(0x2D4A) => Weight = 1.0; + public GiantReplicaAcorn() : base(0x2D4A) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1072889; // giant replica acorn } diff --git a/Projects/UOContent/Items/Construction/Decorative/MountedDreadHorn.cs b/Projects/UOContent/Items/Construction/Decorative/MountedDreadHorn.cs index c3126fff4..4c3f11433 100644 --- a/Projects/UOContent/Items/Construction/Decorative/MountedDreadHorn.cs +++ b/Projects/UOContent/Items/Construction/Decorative/MountedDreadHorn.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class MountedDreadHorn : Item { [Constructible] - public MountedDreadHorn() : base(0x3158) => Weight = 1.0; + public MountedDreadHorn() : base(0x3158) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1074464; // mounted Dread Horn } diff --git a/Projects/UOContent/Items/Construction/Misc/BarrelParts.cs b/Projects/UOContent/Items/Construction/Misc/BarrelParts.cs index 79cc7b8ce..f3505c680 100644 --- a/Projects/UOContent/Items/Construction/Misc/BarrelParts.cs +++ b/Projects/UOContent/Items/Construction/Misc/BarrelParts.cs @@ -6,7 +6,11 @@ namespace Server.Items; public partial class BarrelLid : Item { [Constructible] - public BarrelLid() : base(0x1DB8) => Weight = 2; + public BarrelLid() : base(0x1DB8) + { + } + + public override double DefaultWeight => 2; } [Flippable(0x1EB1, 0x1EB2, 0x1EB3, 0x1EB4)] @@ -14,14 +18,22 @@ public partial class BarrelLid : Item public partial class BarrelStaves : Item { [Constructible] - public BarrelStaves() : base(0x1EB1) => Weight = 1; + public BarrelStaves() : base(0x1EB1) + { + } + + public override double DefaultWeight => 1; } [SerializationGenerator(0, false)] public partial class BarrelHoops : Item { [Constructible] - public BarrelHoops() : base(0x1DB7) => Weight = 5; + public BarrelHoops() : base(0x1DB7) + { + } + + public override double DefaultWeight => 5; public override int LabelNumber => 1011228; // Barrel hoops } @@ -30,5 +42,9 @@ public partial class BarrelHoops : Item public partial class BarrelTap : Item { [Constructible] - public BarrelTap() : base(0x1004) => Weight = 1; + public BarrelTap() : base(0x1004) + { + } + + public override double DefaultWeight => 1; } diff --git a/Projects/UOContent/Items/Construction/Misc/Easel.cs b/Projects/UOContent/Items/Construction/Misc/Easel.cs index 18b3af63c..238febe3a 100644 --- a/Projects/UOContent/Items/Construction/Misc/Easel.cs +++ b/Projects/UOContent/Items/Construction/Misc/Easel.cs @@ -9,5 +9,9 @@ namespace Server.Items; public partial class Easel : Item { [Constructible] - public Easel() : base(0xF65) => Weight = 25.0; + public Easel() : base(0xF65) + { + } + + public override double DefaultWeight => 25.0; } diff --git a/Projects/UOContent/Items/Construction/Misc/MusicStand.cs b/Projects/UOContent/Items/Construction/Misc/MusicStand.cs index b605dc5e1..d323e1aac 100644 --- a/Projects/UOContent/Items/Construction/Misc/MusicStand.cs +++ b/Projects/UOContent/Items/Construction/Misc/MusicStand.cs @@ -8,7 +8,11 @@ namespace Server.Items; public partial class TallMusicStand : Item { [Constructible] - public TallMusicStand() : base(0xEBB) => Weight = 10.0; + public TallMusicStand() : base(0xEBB) + { + } + + public override double DefaultWeight => 10.0; } [Furniture] @@ -17,5 +21,9 @@ public partial class TallMusicStand : Item public partial class ShortMusicStand : Item { [Constructible] - public ShortMusicStand() : base(0xEB6) => Weight = 10.0; + public ShortMusicStand() : base(0xEB6) + { + } + + public override double DefaultWeight => 10.0; } diff --git a/Projects/UOContent/Items/Construction/Misc/Screens.cs b/Projects/UOContent/Items/Construction/Misc/Screens.cs index 7855f8a64..5afffb6f1 100644 --- a/Projects/UOContent/Items/Construction/Misc/Screens.cs +++ b/Projects/UOContent/Items/Construction/Misc/Screens.cs @@ -8,7 +8,11 @@ namespace Server.Items; public partial class BambooScreen : Item { [Constructible] - public BambooScreen() : base(0x24D0) => Weight = 20.0; + public BambooScreen() : base(0x24D0) + { + } + + public override double DefaultWeight => 20.0; } [Furniture] @@ -17,5 +21,9 @@ public partial class BambooScreen : Item public partial class ShojiScreen : Item { [Constructible] - public ShojiScreen() : base(0x24CB) => Weight = 20.0; + public ShojiScreen() : base(0x24CB) + { + } + + public override double DefaultWeight => 20.0; } diff --git a/Projects/UOContent/Items/Construction/Misc/Statues.cs b/Projects/UOContent/Items/Construction/Misc/Statues.cs index 9288bc827..80de53dc6 100644 --- a/Projects/UOContent/Items/Construction/Misc/Statues.cs +++ b/Projects/UOContent/Items/Construction/Misc/Statues.cs @@ -6,7 +6,11 @@ namespace Server.Items; public partial class StatueSouth : Item { [Constructible] - public StatueSouth() : base(0x139A) => Weight = 10; + public StatueSouth() : base(0x139A) + { + } + + public override double DefaultWeight => 10; public override bool ForceShowProperties => ObjectPropertyList.Enabled; } @@ -15,7 +19,11 @@ public partial class StatueSouth : Item public partial class StatueSouth2 : Item { [Constructible] - public StatueSouth2() : base(0x1227) => Weight = 10; + public StatueSouth2() : base(0x1227) + { + } + + public override double DefaultWeight => 10; public override bool ForceShowProperties => ObjectPropertyList.Enabled; } @@ -24,7 +32,11 @@ public partial class StatueSouth2 : Item public partial class StatueNorth : Item { [Constructible] - public StatueNorth() : base(0x139B) => Weight = 10; + public StatueNorth() : base(0x139B) + { + } + + public override double DefaultWeight => 10; public override bool ForceShowProperties => ObjectPropertyList.Enabled; } @@ -33,7 +45,11 @@ public partial class StatueNorth : Item public partial class StatueWest : Item { [Constructible] - public StatueWest() : base(0x1226) => Weight = 10; + public StatueWest() : base(0x1226) + { + } + + public override double DefaultWeight => 10; public override bool ForceShowProperties => ObjectPropertyList.Enabled; } @@ -42,7 +58,11 @@ public partial class StatueWest : Item public partial class StatueEast : Item { [Constructible] - public StatueEast() : base(0x139C) => Weight = 10; + public StatueEast() : base(0x139C) + { + } + + public override double DefaultWeight => 10; public override bool ForceShowProperties => ObjectPropertyList.Enabled; } @@ -51,7 +71,11 @@ public partial class StatueEast : Item public partial class StatueEast2 : Item { [Constructible] - public StatueEast2() : base(0x1224) => Weight = 10; + public StatueEast2() : base(0x1224) + { + } + + public override double DefaultWeight => 10; public override bool ForceShowProperties => ObjectPropertyList.Enabled; } @@ -60,7 +84,11 @@ public partial class StatueEast2 : Item public partial class StatueSouthEast : Item { [Constructible] - public StatueSouthEast() : base(0x1225) => Weight = 10; + public StatueSouthEast() : base(0x1225) + { + } + + public override double DefaultWeight => 10; public override bool ForceShowProperties => ObjectPropertyList.Enabled; } @@ -69,7 +97,11 @@ public partial class StatueSouthEast : Item public partial class BustSouth : Item { [Constructible] - public BustSouth() : base(0x12CB) => Weight = 10; + public BustSouth() : base(0x12CB) + { + } + + public override double DefaultWeight => 10; public override bool ForceShowProperties => ObjectPropertyList.Enabled; } @@ -78,7 +110,11 @@ public partial class BustSouth : Item public partial class BustEast : Item { [Constructible] - public BustEast() : base(0x12CA) => Weight = 10; + public BustEast() : base(0x12CA) + { + } + + public override double DefaultWeight => 10; public override bool ForceShowProperties => ObjectPropertyList.Enabled; } @@ -87,7 +123,11 @@ public partial class BustEast : Item public partial class StatuePegasus : Item { [Constructible] - public StatuePegasus() : base(0x139D) => Weight = 10; + public StatuePegasus() : base(0x139D) + { + } + + public override double DefaultWeight => 10; public override bool ForceShowProperties => ObjectPropertyList.Enabled; } @@ -96,7 +136,11 @@ public partial class StatuePegasus : Item public partial class StatuePegasus2 : Item { [Constructible] - public StatuePegasus2() : base(0x1228) => Weight = 10; + public StatuePegasus2() : base(0x1228) + { + } + + public override double DefaultWeight => 10; public override bool ForceShowProperties => ObjectPropertyList.Enabled; } @@ -105,5 +149,9 @@ public partial class StatuePegasus2 : Item public partial class SmallTowerSculpture : Item { [Constructible] - public SmallTowerSculpture() : base(0x241A) => Weight = 20.0; + public SmallTowerSculpture() : base(0x241A) + { + } + + public override double DefaultWeight => 20.0; } diff --git a/Projects/UOContent/Items/Construction/Misc/Vase.cs b/Projects/UOContent/Items/Construction/Misc/Vase.cs index e4e3e2637..9ec2571ea 100644 --- a/Projects/UOContent/Items/Construction/Misc/Vase.cs +++ b/Projects/UOContent/Items/Construction/Misc/Vase.cs @@ -6,19 +6,31 @@ namespace Server.Items; public partial class Vase : Item { [Constructible] - public Vase() : base(0xB46) => Weight = 10; + public Vase() : base(0xB46) + { + } + + public override double DefaultWeight => 10; } [SerializationGenerator(0, false)] public partial class LargeVase : Item { [Constructible] - public LargeVase() : base(0xB45) => Weight = 15; + public LargeVase() : base(0xB45) + { + } + + public override double DefaultWeight => 15; } [SerializationGenerator(0, false)] public partial class SmallUrn : Item { [Constructible] - public SmallUrn() : base(0x241C) => Weight = 20.0; + public SmallUrn() : base(0x241C) + { + } + + public override double DefaultWeight => 20.0; } diff --git a/Projects/UOContent/Items/Construction/Misc/Vines.cs b/Projects/UOContent/Items/Construction/Misc/Vines.cs index 2c6a20e28..a00fee548 100644 --- a/Projects/UOContent/Items/Construction/Misc/Vines.cs +++ b/Projects/UOContent/Items/Construction/Misc/Vines.cs @@ -1,3 +1,4 @@ +using System; using ModernUO.Serialization; namespace Server.Items; @@ -11,16 +12,11 @@ public partial class Vines : Item } [Constructible] - public Vines(int v) : base(0xCEB) + public Vines(int v) : base(0xCEB + Math.Clamp(v, 0, 7)) { - if (v is < 0 or > 7) - { - v = 0; - } - - ItemID += v; - Weight = 1.0; } + public override double DefaultWeight => 1.0; + public override bool ForceShowProperties => ObjectPropertyList.Enabled; } diff --git a/Projects/UOContent/Items/Construction/Tables/ElvenPodium.cs b/Projects/UOContent/Items/Construction/Tables/ElvenPodium.cs index c17fbe287..774e222fd 100644 --- a/Projects/UOContent/Items/Construction/Tables/ElvenPodium.cs +++ b/Projects/UOContent/Items/Construction/Tables/ElvenPodium.cs @@ -8,6 +8,10 @@ namespace Server.Items public partial class ElvenPodium : Item { [Constructible] - public ElvenPodium() : base(0x2DDD) => Weight = 2.0; + public ElvenPodium() : base(0x2DDD) + { + } + + public override double DefaultWeight => 2.0; } } diff --git a/Projects/UOContent/Items/Construction/Tables/Tables.cs b/Projects/UOContent/Items/Construction/Tables/Tables.cs index b7602efaa..0b2f17f95 100644 --- a/Projects/UOContent/Items/Construction/Tables/Tables.cs +++ b/Projects/UOContent/Items/Construction/Tables/Tables.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class ElegantLowTable : Item { [Constructible] - public ElegantLowTable() : base(0x2819) => Weight = 1.0; + public ElegantLowTable() : base(0x2819) + { + } + + public override double DefaultWeight => 1.0; } [Furniture] @@ -15,7 +19,11 @@ namespace Server.Items public partial class PlainLowTable : Item { [Constructible] - public PlainLowTable() : base(0x281A) => Weight = 1.0; + public PlainLowTable() : base(0x281A) + { + } + + public override double DefaultWeight => 1.0; } [Furniture] @@ -24,7 +32,11 @@ namespace Server.Items public partial class LargeTable : Item { [Constructible] - public LargeTable() : base(0xB90) => Weight = 1.0; + public LargeTable() : base(0xB90) + { + } + + public override double DefaultWeight => 1.0; } [Furniture] @@ -33,7 +45,11 @@ namespace Server.Items public partial class Nightstand : Item { [Constructible] - public Nightstand() : base(0xB35) => Weight = 1.0; + public Nightstand() : base(0xB35) + { + } + + public override double DefaultWeight => 1.0; } [Furniture] @@ -42,6 +58,10 @@ namespace Server.Items public partial class YewWoodTable : Item { [Constructible] - public YewWoodTable() : base(0xB8F) => Weight = 1.0; + public YewWoodTable() : base(0xB8F) + { + } + + public override double DefaultWeight => 1.0; } } diff --git a/Projects/UOContent/Items/Construction/Tables/WritingTable.cs b/Projects/UOContent/Items/Construction/Tables/WritingTable.cs index 077978a8d..822f145a2 100644 --- a/Projects/UOContent/Items/Construction/Tables/WritingTable.cs +++ b/Projects/UOContent/Items/Construction/Tables/WritingTable.cs @@ -8,6 +8,10 @@ namespace Server.Items public partial class WritingTable : Item { [Constructible] - public WritingTable() : base(0xB4A) => Weight = 1.0; + public WritingTable() : base(0xB4A) + { + } + + public override double DefaultWeight => 1.0; } } diff --git a/Projects/UOContent/Items/Containers/Container.cs b/Projects/UOContent/Items/Containers/Container.cs index 6afb9105f..e4dd27669 100644 --- a/Projects/UOContent/Items/Containers/Container.cs +++ b/Projects/UOContent/Items/Containers/Container.cs @@ -168,9 +168,10 @@ public partial class CreatureBackpack : Backpack // Used on BaseCreature Name = name; Layer = Layer.Backpack; Hue = 5; - Weight = 3.0; } + public override double DefaultWeight => 3.0; + public override void AddNameProperty(IPropertyList list) { if (Name != null) @@ -213,11 +214,9 @@ public partial class CreatureBackpack : Backpack // Used on BaseCreature public partial class StrongBackpack : Backpack // Used on Pack animals { [Constructible] - public StrongBackpack() - { - Layer = Layer.Backpack; - Weight = 13.0; - } + public StrongBackpack() => Layer = Layer.Backpack; + + public override double DefaultWeight => 13.0; public override int DefaultMaxWeight => 1600; @@ -233,11 +232,9 @@ public partial class StrongBackpack : Backpack // Used on Pack animals public partial class Backpack : BaseContainer, IDyable { [Constructible] - public Backpack() : base(0xE75) - { - Layer = Layer.Backpack; - Weight = 3.0; - } + public Backpack() : base(0xE75) => Layer = Layer.Backpack; + + public override double DefaultWeight => 3.0; public override int DefaultMaxWeight { @@ -269,13 +266,21 @@ public partial class Backpack : BaseContainer, IDyable public partial class Pouch : TrappableContainer { [Constructible] - public Pouch() : base(0xE79) => Weight = 1.0; + public Pouch() : base(0xE79) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public abstract partial class BaseBagBall : BaseContainer, IDyable { - public BaseBagBall(int itemID) : base(itemID) => Weight = 1.0; + public BaseBagBall(int itemID) : base(itemID) + { + } + + public override double DefaultWeight => 1.0; public bool Dye(Mobile from, DyeTub sender) { @@ -312,7 +317,11 @@ public partial class LargeBagBall : BaseBagBall public partial class Bag : BaseContainer, IDyable { [Constructible] - public Bag() : base(0xE76) => Weight = 2.0; + public Bag() : base(0xE76) + { + } + + public override double DefaultWeight => 2.0; public bool Dye(Mobile from, DyeTub sender) { @@ -331,28 +340,44 @@ public partial class Bag : BaseContainer, IDyable public partial class Barrel : BaseContainer { [Constructible] - public Barrel() : base(0xE77) => Weight = 25.0; + public Barrel() : base(0xE77) + { + } + + public override double DefaultWeight => 25.0; } [SerializationGenerator(0, false)] public partial class Keg : BaseContainer { [Constructible] - public Keg() : base(0xE7F) => Weight = 15.0; + public Keg() : base(0xE7F) + { + } + + public override double DefaultWeight => 15.0; } [SerializationGenerator(0, false)] public partial class PicnicBasket : BaseContainer { [Constructible] - public PicnicBasket() : base(0xE7A) => Weight = 2.0; + public PicnicBasket() : base(0xE7A) + { + } + + public override double DefaultWeight => 2.0; } [SerializationGenerator(0, false)] public partial class Basket : BaseContainer { [Constructible] - public Basket() : base(0x990) => Weight = 1.0; + public Basket() : base(0x990) + { + } + + public override double DefaultWeight => 1.0; } [Furniture] @@ -361,7 +386,11 @@ public partial class Basket : BaseContainer public partial class WoodenBox : LockableContainer { [Constructible] - public WoodenBox() : base(0x9AA) => Weight = 4.0; + public WoodenBox() : base(0x9AA) + { + } + + public override double DefaultWeight => 4.0; } [Furniture] @@ -370,7 +399,11 @@ public partial class WoodenBox : LockableContainer public partial class SmallCrate : LockableContainer { [Constructible] - public SmallCrate() : base(0x9A9) => Weight = 2.0; + public SmallCrate() : base(0x9A9) + { + } + + public override double DefaultWeight => 2.0; } [Furniture] @@ -379,7 +412,11 @@ public partial class SmallCrate : LockableContainer public partial class MediumCrate : LockableContainer { [Constructible] - public MediumCrate() : base(0xE3F) => Weight = 2.0; + public MediumCrate() : base(0xE3F) + { + } + + public override double DefaultWeight => 2.0; } [Furniture] @@ -388,7 +425,11 @@ public partial class MediumCrate : LockableContainer public partial class LargeCrate : LockableContainer { [Constructible] - public LargeCrate() : base(0xE3D) => Weight = 1.0; + public LargeCrate() : base(0xE3D) + { + } + + public override double DefaultWeight => 1.0; } [DynamicFlipping] @@ -429,7 +470,11 @@ public partial class MetalGoldenChest : LockableContainer public partial class WoodenChest : LockableContainer { [Constructible] - public WoodenChest() : base(0xe43) => Weight = 2.0; + public WoodenChest() : base(0xe43) + { + } + + public override double DefaultWeight => 2.0; } [Furniture] diff --git a/Projects/UOContent/Items/Containers/FurnitureContainer.cs b/Projects/UOContent/Items/Containers/FurnitureContainer.cs index e638ab2d7..0b19f4979 100644 --- a/Projects/UOContent/Items/Containers/FurnitureContainer.cs +++ b/Projects/UOContent/Items/Containers/FurnitureContainer.cs @@ -10,7 +10,11 @@ namespace Server.Items; public partial class TallCabinet : BaseContainer { [Constructible] - public TallCabinet() : base(0x2815) => Weight = 1.0; + public TallCabinet() : base(0x2815) + { + } + + public override double DefaultWeight => 1.0; } [Furniture] @@ -19,7 +23,11 @@ public partial class TallCabinet : BaseContainer public partial class ShortCabinet : BaseContainer { [Constructible] - public ShortCabinet() : base(0x2817) => Weight = 1.0; + public ShortCabinet() : base(0x2817) + { + } + + public override double DefaultWeight => 1.0; } [Furniture] @@ -28,7 +36,11 @@ public partial class ShortCabinet : BaseContainer public partial class RedArmoire : BaseContainer { [Constructible] - public RedArmoire() : base(0x2857) => Weight = 1.0; + public RedArmoire() : base(0x2857) + { + } + + public override double DefaultWeight => 1.0; } [Furniture] @@ -37,7 +49,11 @@ public partial class RedArmoire : BaseContainer public partial class CherryArmoire : BaseContainer { [Constructible] - public CherryArmoire() : base(0x285D) => Weight = 1.0; + public CherryArmoire() : base(0x285D) + { + } + + public override double DefaultWeight => 1.0; } [Furniture] @@ -46,7 +62,11 @@ public partial class CherryArmoire : BaseContainer public partial class MapleArmoire : BaseContainer { [Constructible] - public MapleArmoire() : base(0x285B) => Weight = 1.0; + public MapleArmoire() : base(0x285B) + { + } + + public override double DefaultWeight => 1.0; } [Furniture] @@ -55,7 +75,11 @@ public partial class MapleArmoire : BaseContainer public partial class ElegantArmoire : BaseContainer { [Constructible] - public ElegantArmoire() : base(0x2859) => Weight = 1.0; + public ElegantArmoire() : base(0x2859) + { + } + + public override double DefaultWeight => 1.0; } [Furniture] @@ -64,7 +88,11 @@ public partial class ElegantArmoire : BaseContainer public partial class FancyElvenArmoire : BaseContainer { [Constructible] - public FancyElvenArmoire() : base(0x2D07) => Weight = 1.0; + public FancyElvenArmoire() : base(0x2D07) + { + } + + public override double DefaultWeight => 1.0; public override int DefaultGumpID => 0x4E; public override int DefaultDropSound => 0x42; } @@ -75,7 +103,11 @@ public partial class FancyElvenArmoire : BaseContainer public partial class SimpleElvenArmoire : BaseContainer { [Constructible] - public SimpleElvenArmoire() : base(0x2D05) => Weight = 1.0; + public SimpleElvenArmoire() : base(0x2D05) + { + } + + public override double DefaultWeight => 1.0; public override int DefaultGumpID => 0x4F; public override int DefaultDropSound => 0x42; } @@ -86,7 +118,11 @@ public partial class SimpleElvenArmoire : BaseContainer public partial class FullBookcase : BaseContainer { [Constructible] - public FullBookcase() : base(0xA97) => Weight = 1.0; + public FullBookcase() : base(0xA97) + { + } + + public override double DefaultWeight => 1.0; } [Furniture] @@ -106,7 +142,11 @@ public partial class EmptyBookcase : BaseContainer public partial class Drawer : BaseContainer { [Constructible] - public Drawer() : base(0xA2C) => Weight = 1.0; + public Drawer() : base(0xA2C) + { + } + + public override double DefaultWeight => 1.0; } [Furniture] @@ -115,7 +155,11 @@ public partial class Drawer : BaseContainer public partial class FancyDrawer : BaseContainer { [Constructible] - public FancyDrawer() : base(0xA30) => Weight = 1.0; + public FancyDrawer() : base(0xA30) + { + } + + public override double DefaultWeight => 1.0; } [Furniture] @@ -124,7 +168,11 @@ public partial class FancyDrawer : BaseContainer public partial class Armoire : BaseContainer { [Constructible] - public Armoire() : base(0xA4F) => Weight = 1.0; + public Armoire() : base(0xA4F) + { + } + + public override double DefaultWeight => 1.0; public override void DisplayTo(Mobile m) { @@ -147,7 +195,11 @@ public partial class Armoire : BaseContainer public partial class FancyArmoire : BaseContainer { [Constructible] - public FancyArmoire() : base(0xA4D) => Weight = 1.0; + public FancyArmoire() : base(0xA4D) + { + } + + public override double DefaultWeight => 1.0; public override void DisplayTo(Mobile m) { diff --git a/Projects/UOContent/Items/Containers/SalvageBag.cs b/Projects/UOContent/Items/Containers/SalvageBag.cs index 173a4d2e6..9e2b79a72 100644 --- a/Projects/UOContent/Items/Containers/SalvageBag.cs +++ b/Projects/UOContent/Items/Containers/SalvageBag.cs @@ -19,11 +19,12 @@ public partial class SalvageBag : Bag [Constructible] public SalvageBag(int hue) { - Weight = 2.0; Hue = hue; m_Failure = false; } + public override double DefaultWeight => 2.0; + public override int LabelNumber => 1079931; // Salvage Bag public override void GetContextMenuEntries(Mobile from, ref PooledRefList list) diff --git a/Projects/UOContent/Items/Containers/Strongbox.cs b/Projects/UOContent/Items/Containers/Strongbox.cs index 6e937ff9c..aa5e63925 100644 --- a/Projects/UOContent/Items/Containers/Strongbox.cs +++ b/Projects/UOContent/Items/Containers/Strongbox.cs @@ -1,6 +1,6 @@ using System; -using System.Collections.Generic; using ModernUO.Serialization; +using Server.Collections; using Server.Multis; namespace Server.Items; @@ -102,15 +102,15 @@ public partial class StrongBox : BaseContainer, IChoppable public Container ConvertToStandardContainer() { var metalBox = new MetalBox(); - var subItems = new List(Items); + using var subItems = PooledRefList.Create(Items.Count); + subItems.AddRange(Items); - foreach (var subItem in subItems) + for (var i = 0; i < subItems.Count; i++) { - metalBox.AddItem(subItem); + metalBox.AddItem(subItems[i]); } Delete(); - return metalBox; } } diff --git a/Projects/UOContent/Items/Containers/TreasureMapChest.cs b/Projects/UOContent/Items/Containers/TreasureMapChest.cs index 22c5101cf..fffd85db8 100644 --- a/Projects/UOContent/Items/Containers/TreasureMapChest.cs +++ b/Projects/UOContent/Items/Containers/TreasureMapChest.cs @@ -12,6 +12,7 @@ namespace Server.Items; [SerializationGenerator(2, false)] public partial class TreasureMapChest : LockableContainer { + [Tidy] [SerializableField(0, setter: "private")] private List _guardians; @@ -57,7 +58,7 @@ public partial class TreasureMapChest : LockableContainer _level = level; _temporary = temporary; - _guardians = new List(); + _guardians = []; _expireTimer = Timer.DelayCall(TimeSpan.FromHours(3.0), Delete); Fill(this, level); @@ -66,7 +67,7 @@ public partial class TreasureMapChest : LockableContainer public override int LabelNumber => 3000541; public static Type[] Artifacts { get; } = - { + [ typeof(CandelabraOfSouls), typeof(GoldBricks), typeof(PhillipsWoodenSteed), typeof(ArcticDeathDealer), typeof(BlazeOfDeath), typeof(BurglarsBandana), typeof(CavortingClub), typeof(DreadPirateHat), @@ -74,7 +75,7 @@ public partial class TreasureMapChest : LockableContainer typeof(LunaLance), typeof(NightsKiss), typeof(NoxRangersHeavyCrossbow), typeof(PolarBearMask), typeof(VioletCourage), typeof(HeartOfTheLion), typeof(ColdBlood), typeof(AlchemistsBauble) - }; + ]; [CommandProperty(AccessLevel.GameMaster)] public DateTime DeleteTime => _expireTimer.Next; @@ -299,8 +300,9 @@ public partial class TreasureMapChest : LockableContainer if (_level == 0 && from.AccessLevel < AccessLevel.GameMaster) { - foreach (var m in _guardians) + for (var i = 0; i < _guardians.Count; i++) { + var m = _guardians[i]; if (m.Alive) { // You must first kill the guardians before you may open this chest. @@ -366,7 +368,7 @@ public partial class TreasureMapChest : LockableContainer if (notYetLifted) { - _lifted ??= new HashSet(); + _lifted ??= []; _lifted.Add(item); if (Utility.RandomDouble() < 0.1) // 10% chance to spawn a new monster @@ -391,7 +393,7 @@ public partial class TreasureMapChest : LockableContainer private void Deserialize(IGenericReader reader, int version) { - _guardians = new List(); + _guardians = []; _owner = reader.ReadEntity(); _level = reader.ReadInt(); diff --git a/Projects/UOContent/Items/Decoration Artifacts/BaseDecorationArtifact.cs b/Projects/UOContent/Items/Decoration Artifacts/BaseDecorationArtifact.cs index 366a256e7..9b3e32c81 100644 --- a/Projects/UOContent/Items/Decoration Artifacts/BaseDecorationArtifact.cs +++ b/Projects/UOContent/Items/Decoration Artifacts/BaseDecorationArtifact.cs @@ -5,7 +5,11 @@ namespace Server.Items; [SerializationGenerator(0)] public abstract partial class BaseDecorationArtifact : Item { - public BaseDecorationArtifact(int itemID) : base(itemID) => Weight = 10.0; + public BaseDecorationArtifact(int itemID) : base(itemID) + { + } + + public override double DefaultWeight => 10.0; public abstract int ArtifactRarity { get; } @@ -22,7 +26,11 @@ public abstract partial class BaseDecorationArtifact : Item [SerializationGenerator(0)] public abstract partial class BaseDecorationContainerArtifact : BaseContainer { - public BaseDecorationContainerArtifact(int itemID) : base(itemID) => Weight = 10.0; + public BaseDecorationContainerArtifact(int itemID) : base(itemID) + { + } + + public override double DefaultWeight => 10.0; public abstract int ArtifactRarity { get; } diff --git a/Projects/UOContent/Items/Deeds/BarkeepContract.cs b/Projects/UOContent/Items/Deeds/BarkeepContract.cs index 3db975f11..b5bd7b4ab 100644 --- a/Projects/UOContent/Items/Deeds/BarkeepContract.cs +++ b/Projects/UOContent/Items/Deeds/BarkeepContract.cs @@ -10,10 +10,11 @@ public partial class BarkeepContract : Item [Constructible] public BarkeepContract() : base(0x14F0) { - Weight = 1.0; LootType = LootType.Blessed; } + public override double DefaultWeight => 1.0; + public override string DefaultName => "a barkeep contract"; public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Items/Deeds/ClothingBlessDeed.cs b/Projects/UOContent/Items/Deeds/ClothingBlessDeed.cs index 816a0cc0c..20f8f9fc6 100644 --- a/Projects/UOContent/Items/Deeds/ClothingBlessDeed.cs +++ b/Projects/UOContent/Items/Deeds/ClothingBlessDeed.cs @@ -58,10 +58,11 @@ public partial class ClothingBlessDeed : Item // Create the item class which is [Constructible] public ClothingBlessDeed() : base(0x14F0) { - Weight = 1.0; LootType = LootType.Blessed; } + public override double DefaultWeight => 1.0; + public override string DefaultName => "a clothing bless deed"; public override bool DisplayLootType => false; diff --git a/Projects/UOContent/Items/Deeds/CommodityDeed.cs b/Projects/UOContent/Items/Deeds/CommodityDeed.cs index d073bf90b..163ea68cf 100644 --- a/Projects/UOContent/Items/Deeds/CommodityDeed.cs +++ b/Projects/UOContent/Items/Deeds/CommodityDeed.cs @@ -19,7 +19,6 @@ public partial class CommodityDeed : Item [Constructible] public CommodityDeed(Item commodity = null) : base(0x14F0) { - Weight = 1.0; Hue = 0x47; Commodity = commodity; @@ -27,6 +26,8 @@ public partial class CommodityDeed : Item LootType = LootType.Blessed; } + public override double DefaultWeight => 1.0; + public override int LabelNumber => Commodity == null ? 1047016 : 1047017; public bool SetCommodity(Item item) diff --git a/Projects/UOContent/Items/Deeds/DragonBardingDeed.cs b/Projects/UOContent/Items/Deeds/DragonBardingDeed.cs index a7ac745dc..6599e6c56 100644 --- a/Projects/UOContent/Items/Deeds/DragonBardingDeed.cs +++ b/Projects/UOContent/Items/Deeds/DragonBardingDeed.cs @@ -20,7 +20,11 @@ public partial class DragonBardingDeed : Item, ICraftable [SerializedCommandProperty(AccessLevel.GameMaster)] private bool _exceptional; - public DragonBardingDeed() : base(0x14F0) => Weight = 1.0; + public DragonBardingDeed() : base(0x14F0) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => _exceptional ? 1053181 : 1053012; // dragon barding deed diff --git a/Projects/UOContent/Items/Deeds/HairRestylingDeed.cs b/Projects/UOContent/Items/Deeds/HairRestylingDeed.cs index d9256bf8a..460f337cc 100644 --- a/Projects/UOContent/Items/Deeds/HairRestylingDeed.cs +++ b/Projects/UOContent/Items/Deeds/HairRestylingDeed.cs @@ -11,10 +11,11 @@ public partial class HairRestylingDeed : Item [Constructible] public HairRestylingDeed() : base(0x14F0) { - Weight = 1.0; LootType = LootType.Blessed; } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1041061; // a coupon for a free hair restyling public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Items/Deeds/HolidayTreeDeed.cs b/Projects/UOContent/Items/Deeds/HolidayTreeDeed.cs index dcb8585e1..2a8a6fa63 100644 --- a/Projects/UOContent/Items/Deeds/HolidayTreeDeed.cs +++ b/Projects/UOContent/Items/Deeds/HolidayTreeDeed.cs @@ -13,10 +13,11 @@ public partial class HolidayTreeDeed : Item public HolidayTreeDeed() : base(0x14F0) { Hue = 0x488; - Weight = 1.0; LootType = LootType.Blessed; } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1041116; // a deed for a holiday tree public bool ValidatePlacement(Mobile from, Point3D loc) diff --git a/Projects/UOContent/Items/Deeds/NewPlayerTicket.cs b/Projects/UOContent/Items/Deeds/NewPlayerTicket.cs index ed988dd30..14f7abb4c 100644 --- a/Projects/UOContent/Items/Deeds/NewPlayerTicket.cs +++ b/Projects/UOContent/Items/Deeds/NewPlayerTicket.cs @@ -15,10 +15,11 @@ public partial class NewPlayerTicket : Item [Constructible] public NewPlayerTicket() : base(0x14EF) { - Weight = 1.0; LootType = LootType.Blessed; } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1062094; // a young player ticket public override bool DisplayLootType => false; diff --git a/Projects/UOContent/Items/Deeds/VendorRentalContract.cs b/Projects/UOContent/Items/Deeds/VendorRentalContract.cs index f2f7898c8..d97d1f26a 100644 --- a/Projects/UOContent/Items/Deeds/VendorRentalContract.cs +++ b/Projects/UOContent/Items/Deeds/VendorRentalContract.cs @@ -31,13 +31,13 @@ public partial class VendorRentalContract : Item [Constructible] public VendorRentalContract() : base(0x14F0) { - Weight = 1.0; Hue = 0x672; - _duration = VendorRentalDuration.Instances[0]; Price = 1500; } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1062332; // a vendor rental contract public VendorRentalDuration Duration diff --git a/Projects/UOContent/Items/Food/Asian.cs b/Projects/UOContent/Items/Food/Asian.cs index b80e5f2d9..282f35c03 100644 --- a/Projects/UOContent/Items/Food/Asian.cs +++ b/Projects/UOContent/Items/Food/Asian.cs @@ -6,7 +6,11 @@ namespace Server.Items; public partial class Wasabi : Food { [Constructible] - public Wasabi() : base(0x24E8) => Weight = 1.0; + public Wasabi() : base(0x24E8) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -16,17 +20,21 @@ public partial class WasabiClumps : Food public WasabiClumps() : base(0x24EB) { Stackable = false; - Weight = 1.0; FillFactor = 2; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class EmptyBentoBox : Item { [Constructible] - public EmptyBentoBox() : base(0x2834) => Weight = 5.0; + public EmptyBentoBox() : base(0x2834) + { + } + public override double DefaultWeight => 5.0; } [SerializationGenerator(0, false)] @@ -36,20 +44,10 @@ public partial class BentoBox : Food public BentoBox() : base(0x2836) { Stackable = false; - Weight = 5.0; FillFactor = 2; } - public override bool Eat(Mobile from) - { - if (!base.Eat(from)) - { - return false; - } - - from.AddToBackpack(new EmptyBentoBox()); - return true; - } + public override double DefaultWeight => 5.0; } [SerializationGenerator(0, false)] @@ -59,9 +57,10 @@ public partial class SushiRolls : Food public SushiRolls() : base(0x283E) { Stackable = false; - Weight = 3.0; FillFactor = 2; } + + public override double DefaultWeight => 3.0; } [SerializationGenerator(0, false)] @@ -71,16 +70,21 @@ public partial class SushiPlatter : Food public SushiPlatter() : base(0x2840) { Stackable = Core.ML; - Weight = 3.0; FillFactor = 2; } + + public override double DefaultWeight => 3.0; } [SerializationGenerator(0, false)] public partial class GreenTeaBasket : Item { [Constructible] - public GreenTeaBasket() : base(0x284B) => Weight = 10.0; + public GreenTeaBasket() : base(0x284B) + { + } + + public override double DefaultWeight => 10.0; } [SerializationGenerator(0, false)] @@ -90,9 +94,10 @@ public partial class GreenTea : Food public GreenTea() : base(0x284C) { Stackable = false; - Weight = 4.0; FillFactor = 2; } + + public override double DefaultWeight => 4.0; } [SerializationGenerator(0, false)] @@ -102,9 +107,10 @@ public partial class MisoSoup : Food public MisoSoup() : base(0x284D) { Stackable = false; - Weight = 4.0; FillFactor = 2; } + + public override double DefaultWeight => 4.0; } [SerializationGenerator(0, false)] @@ -114,9 +120,10 @@ public partial class WhiteMisoSoup : Food public WhiteMisoSoup() : base(0x284E) { Stackable = false; - Weight = 4.0; FillFactor = 2; } + + public override double DefaultWeight => 4.0; } [SerializationGenerator(0, false)] @@ -126,9 +133,10 @@ public partial class RedMisoSoup : Food public RedMisoSoup() : base(0x284F) { Stackable = false; - Weight = 4.0; FillFactor = 2; } + + public override double DefaultWeight => 4.0; } [SerializationGenerator(0, false)] @@ -138,7 +146,8 @@ public partial class AwaseMisoSoup : Food public AwaseMisoSoup() : base(0x2850) { Stackable = false; - Weight = 4.0; FillFactor = 2; } + + public override double DefaultWeight => 4.0; } diff --git a/Projects/UOContent/Items/Food/Beverage.cs b/Projects/UOContent/Items/Food/Beverage.cs index 6398fffc3..79b7f62fe 100644 --- a/Projects/UOContent/Items/Food/Beverage.cs +++ b/Projects/UOContent/Items/Food/Beverage.cs @@ -38,7 +38,11 @@ public interface IWaterSource : IHasQuantity public partial class BeverageBottle : BaseBeverage { [Constructible] - public BeverageBottle(BeverageType type) : base(type) => Weight = 1.0; + public BeverageBottle(BeverageType type) : base(type) + { + } + + public override double DefaultWeight => 1.0; public override int BaseLabelNumber => 1042959; // a bottle of Ale public override int MaxQuantity => 5; @@ -68,7 +72,11 @@ public partial class BeverageBottle : BaseBeverage public partial class Jug : BaseBeverage { [Constructible] - public Jug(BeverageType type) : base(type) => Weight = 1.0; + public Jug(BeverageType type) : base(type) + { + } + + public override double DefaultWeight => 1.0; public override int BaseLabelNumber => 1042965; // a jug of Ale public override int MaxQuantity => 10; @@ -81,10 +89,16 @@ public partial class Jug : BaseBeverage public partial class CeramicMug : BaseBeverage { [Constructible] - public CeramicMug() => Weight = 1.0; + public CeramicMug() + { + } [Constructible] - public CeramicMug(BeverageType type) : base(type) => Weight = 1.0; + public CeramicMug(BeverageType type) : base(type) + { + } + + public override double DefaultWeight => 1.0; public override int BaseLabelNumber => 1042982; // a ceramic mug of Ale public override int MaxQuantity => 1; @@ -96,10 +110,16 @@ public partial class CeramicMug : BaseBeverage public partial class PewterMug : BaseBeverage { [Constructible] - public PewterMug() => Weight = 1.0; + public PewterMug() + { + } [Constructible] - public PewterMug(BeverageType type) : base(type) => Weight = 1.0; + public PewterMug(BeverageType type) : base(type) + { + } + + public override double DefaultWeight => 1.0; public override int BaseLabelNumber => 1042994; // a pewter mug with Ale public override int MaxQuantity => 1; @@ -111,10 +131,16 @@ public partial class PewterMug : BaseBeverage public partial class Goblet : BaseBeverage { [Constructible] - public Goblet() => Weight = 1.0; + public Goblet() + { + } [Constructible] - public Goblet(BeverageType type) : base(type) => Weight = 1.0; + public Goblet(BeverageType type) : base(type) + { + } + + public override double DefaultWeight => 1.0; public override int BaseLabelNumber => 1043000; // a goblet of Ale public override int MaxQuantity => 1; @@ -134,10 +160,16 @@ public partial class Goblet : BaseBeverage public partial class GlassMug : BaseBeverage { [Constructible] - public GlassMug() => Weight = 1.0; + public GlassMug() + { + } [Constructible] - public GlassMug(BeverageType type) : base(type) => Weight = 1.0; + public GlassMug(BeverageType type) : base(type) + { + } + + public override double DefaultWeight => 1.0; public override int EmptyLabelNumber => 1022456; // mug public override int BaseLabelNumber => 1042976; // a mug of Ale @@ -176,10 +208,16 @@ public partial class GlassMug : BaseBeverage public partial class Pitcher : BaseBeverage { [Constructible] - public Pitcher() => Weight = 2.0; + public Pitcher() + { + } [Constructible] - public Pitcher(BeverageType type) : base(type) => Weight = 2.0; + public Pitcher(BeverageType type) : base(type) + { + } + + public override double DefaultWeight => 2.0; public override int BaseLabelNumber => 1048128; // a Pitcher of Ale public override int MaxQuantity => 5; diff --git a/Projects/UOContent/Items/Food/BeverageEmpty.cs b/Projects/UOContent/Items/Food/BeverageEmpty.cs index 317635af0..6c10580f1 100644 --- a/Projects/UOContent/Items/Food/BeverageEmpty.cs +++ b/Projects/UOContent/Items/Food/BeverageEmpty.cs @@ -7,12 +7,20 @@ namespace Server.Items; public partial class Glass : Item { [Constructible] - public Glass() : base(0x1f81) => Weight = 0.1; + public Glass() : base(0x1f81) + { + } + + public override double DefaultWeight => 0.1; } [SerializationGenerator(0, false)] public partial class GlassBottle : Item { [Constructible] - public GlassBottle() : base(0xe2b) => Weight = 0.3; + public GlassBottle() : base(0xe2b) + { + } + + public override double DefaultWeight => 0.3; } diff --git a/Projects/UOContent/Items/Food/Bowls.cs b/Projects/UOContent/Items/Food/Bowls.cs index f080b6d62..08b7bb11f 100644 --- a/Projects/UOContent/Items/Food/Bowls.cs +++ b/Projects/UOContent/Items/Food/Bowls.cs @@ -6,14 +6,22 @@ namespace Server.Items; public partial class EmptyWoodenBowl : Item { [Constructible] - public EmptyWoodenBowl() : base(0x15F8) => Weight = 1.0; + public EmptyWoodenBowl() : base(0x15F8) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class EmptyPewterBowl : Item { [Constructible] - public EmptyPewterBowl() : base(0x15FD) => Weight = 1.0; + public EmptyPewterBowl() : base(0x15FD) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -23,10 +31,11 @@ public partial class WoodenBowlOfCarrots : Food public WoodenBowlOfCarrots() : base(0x15F9) { Stackable = false; - Weight = 1.0; FillFactor = 2; } + public override double DefaultWeight => 1.0; + public override bool Eat(Mobile from) { if (!base.Eat(from)) @@ -46,10 +55,11 @@ public partial class WoodenBowlOfCorn : Food public WoodenBowlOfCorn() : base(0x15FA) { Stackable = false; - Weight = 1.0; FillFactor = 2; } + public override double DefaultWeight => 1.0; + public override bool Eat(Mobile from) { if (!base.Eat(from)) @@ -69,10 +79,11 @@ public partial class WoodenBowlOfLettuce : Food public WoodenBowlOfLettuce() : base(0x15FB) { Stackable = false; - Weight = 1.0; FillFactor = 2; } + public override double DefaultWeight => 1.0; + public override bool Eat(Mobile from) { if (!base.Eat(from)) @@ -92,10 +103,11 @@ public partial class WoodenBowlOfPeas : Food public WoodenBowlOfPeas() : base(0x15FC) { Stackable = false; - Weight = 1.0; FillFactor = 2; } + public override double DefaultWeight => 1.0; + public override bool Eat(Mobile from) { if (!base.Eat(from)) @@ -115,10 +127,11 @@ public partial class PewterBowlOfCarrots : Food public PewterBowlOfCarrots() : base(0x15FE) { Stackable = false; - Weight = 1.0; FillFactor = 2; } + public override double DefaultWeight => 1.0; + public override bool Eat(Mobile from) { if (!base.Eat(from)) @@ -138,10 +151,11 @@ public partial class PewterBowlOfCorn : Food public PewterBowlOfCorn() : base(0x15FF) { Stackable = false; - Weight = 1.0; FillFactor = 2; } + public override double DefaultWeight => 1.0; + public override bool Eat(Mobile from) { if (!base.Eat(from)) @@ -161,10 +175,11 @@ public partial class PewterBowlOfLettuce : Food public PewterBowlOfLettuce() : base(0x1600) { Stackable = false; - Weight = 1.0; FillFactor = 2; } + public override double DefaultWeight => 1.0; + public override bool Eat(Mobile from) { if (!base.Eat(from)) @@ -184,10 +199,11 @@ public partial class PewterBowlOfPeas : Food public PewterBowlOfPeas() : base(0x1601) { Stackable = false; - Weight = 1.0; FillFactor = 2; } + public override double DefaultWeight => 1.0; + public override bool Eat(Mobile from) { if (!base.Eat(from)) @@ -207,10 +223,11 @@ public partial class PewterBowlOfPotatos : Food public PewterBowlOfPotatos() : base(0x1602) { Stackable = false; - Weight = 1.0; FillFactor = 2; } + public override double DefaultWeight => 1.0; + public override bool Eat(Mobile from) { if (!base.Eat(from)) @@ -228,7 +245,11 @@ public partial class PewterBowlOfPotatos : Food public partial class EmptyWoodenTub : Item { [Constructible] - public EmptyWoodenTub() : base(0x1605) => Weight = 2.0; + public EmptyWoodenTub() : base(0x1605) + { + } + + public override double DefaultWeight => 2.0; } [TypeAlias("Server.Items.EmptyLargePewterBowl")] @@ -236,7 +257,11 @@ public partial class EmptyWoodenTub : Item public partial class EmptyPewterTub : Item { [Constructible] - public EmptyPewterTub() : base(0x1603) => Weight = 2.0; + public EmptyPewterTub() : base(0x1603) + { + } + + public override double DefaultWeight => 2.0; } [SerializationGenerator(0, false)] @@ -246,10 +271,11 @@ public partial class WoodenBowlOfStew : Food public WoodenBowlOfStew() : base(0x1604) { Stackable = false; - Weight = 2.0; FillFactor = 2; } + public override double DefaultWeight => 2.0; + public override bool Eat(Mobile from) { if (!base.Eat(from)) @@ -269,10 +295,11 @@ public partial class WoodenBowlOfTomatoSoup : Food public WoodenBowlOfTomatoSoup() : base(0x1606) { Stackable = false; - Weight = 2.0; FillFactor = 2; } + public override double DefaultWeight => 2.0; + public override bool Eat(Mobile from) { if (!base.Eat(from)) diff --git a/Projects/UOContent/Items/Food/CookableFood.cs b/Projects/UOContent/Items/Food/CookableFood.cs index a1515173c..e9597fd03 100644 --- a/Projects/UOContent/Items/Food/CookableFood.cs +++ b/Projects/UOContent/Items/Food/CookableFood.cs @@ -76,11 +76,12 @@ public partial class RawRibs : CookableFood [Constructible] public RawRibs(int amount = 1) : base(0x9F1, 10) { - Weight = 1.0; Stackable = true; Amount = amount; } + public override double DefaultWeight => 1.0; + public override Food Cook() => new Ribs(); } @@ -103,10 +104,11 @@ public partial class RawChickenLeg : CookableFood [Constructible] public RawChickenLeg() : base(0x1607, 10) { - Weight = 1.0; Stackable = true; } + public override double DefaultWeight => 1.0; + public override Food Cook() => new ChickenLeg(); } @@ -116,11 +118,12 @@ public partial class RawBird : CookableFood [Constructible] public RawBird(int amount = 1) : base(0x9B9, 10) { - Weight = 1.0; Stackable = true; Amount = amount; } + public override double DefaultWeight => 1.0; + public override Food Cook() => new CookedBird(); } @@ -128,7 +131,11 @@ public partial class RawBird : CookableFood public partial class UnbakedPeachCobbler : CookableFood { [Constructible] - public UnbakedPeachCobbler() : base(0x1042, 25) => Weight = 1.0; + public UnbakedPeachCobbler() : base(0x1042, 25) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1041335; // unbaked peach cobbler @@ -139,7 +146,11 @@ public partial class UnbakedPeachCobbler : CookableFood public partial class UnbakedFruitPie : CookableFood { [Constructible] - public UnbakedFruitPie() : base(0x1042, 25) => Weight = 1.0; + public UnbakedFruitPie() : base(0x1042, 25) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1041334; // unbaked fruit pie @@ -150,7 +161,11 @@ public partial class UnbakedFruitPie : CookableFood public partial class UnbakedMeatPie : CookableFood { [Constructible] - public UnbakedMeatPie() : base(0x1042, 25) => Weight = 1.0; + public UnbakedMeatPie() : base(0x1042, 25) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1041338; // unbaked meat pie @@ -161,7 +176,11 @@ public partial class UnbakedMeatPie : CookableFood public partial class UnbakedPumpkinPie : CookableFood { [Constructible] - public UnbakedPumpkinPie() : base(0x1042, 25) => Weight = 1.0; + public UnbakedPumpkinPie() : base(0x1042, 25) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1041342; // unbaked pumpkin pie @@ -172,7 +191,11 @@ public partial class UnbakedPumpkinPie : CookableFood public partial class UnbakedApplePie : CookableFood { [Constructible] - public UnbakedApplePie() : base(0x1042, 25) => Weight = 1.0; + public UnbakedApplePie() : base(0x1042, 25) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1041336; // unbaked apple pie @@ -184,7 +207,11 @@ public partial class UnbakedApplePie : CookableFood public partial class UncookedCheesePizza : CookableFood { [Constructible] - public UncookedCheesePizza() : base(0x1083, 20) => Weight = 1.0; + public UncookedCheesePizza() : base(0x1083, 20) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1041341; // uncooked cheese pizza @@ -195,7 +222,11 @@ public partial class UncookedCheesePizza : CookableFood public partial class UncookedSausagePizza : CookableFood { [Constructible] - public UncookedSausagePizza() : base(0x1083, 20) => Weight = 1.0; + public UncookedSausagePizza() : base(0x1083, 20) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1041337; // uncooked sausage pizza @@ -206,7 +237,11 @@ public partial class UncookedSausagePizza : CookableFood public partial class UnbakedQuiche : CookableFood { [Constructible] - public UnbakedQuiche() : base(0x1042, 25) => Weight = 1.0; + public UnbakedQuiche() : base(0x1042, 25) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1041339; // unbaked quiche @@ -219,11 +254,12 @@ public partial class Eggs : CookableFood [Constructible] public Eggs(int amount = 1) : base(0x9B5, 15) { - Weight = 1.0; Stackable = true; Amount = amount; } + public override double DefaultWeight => 1.0; + public override Food Cook() => new FriedEggs(); } @@ -233,12 +269,11 @@ public partial class BrightlyColoredEggs : CookableFood [Constructible] public BrightlyColoredEggs() : base(0x9B5, 15) { - Weight = 0.5; Hue = 3 + Utility.Random(20) * 5; } + public override double DefaultWeight => 0.5; public override string DefaultName => "brightly colored eggs"; - public override Food Cook() => new FriedEggs(); } @@ -248,12 +283,11 @@ public partial class EasterEggs : CookableFood [Constructible] public EasterEggs() : base(0x9B5, 15) { - Weight = 0.5; Hue = 3 + Utility.Random(20) * 5; } + public override double DefaultWeight => 0.5; public override int LabelNumber => 1016105; // Easter Eggs - public override Food Cook() => new FriedEggs(); } @@ -261,7 +295,11 @@ public partial class EasterEggs : CookableFood public partial class CookieMix : CookableFood { [Constructible] - public CookieMix() : base(0x103F, 20) => Weight = 1.0; + public CookieMix() : base(0x103F, 20) + { + } + + public override double DefaultWeight => 1.0; public override Food Cook() => new Cookies(); } @@ -270,7 +308,11 @@ public partial class CookieMix : CookableFood public partial class CakeMix : CookableFood { [Constructible] - public CakeMix() : base(0x103F, 40) => Weight = 1.0; + public CakeMix() : base(0x103F, 40) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1041002; // cake mix diff --git a/Projects/UOContent/Items/Food/Cooking.cs b/Projects/UOContent/Items/Food/Cooking.cs index 9701772dd..6cff16d72 100644 --- a/Projects/UOContent/Items/Food/Cooking.cs +++ b/Projects/UOContent/Items/Food/Cooking.cs @@ -11,8 +11,9 @@ public partial class Dough : Item public Dough() : base(0x103d) { Stackable = Core.ML; - Weight = 1.0; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -22,10 +23,10 @@ public partial class SweetDough : Item public SweetDough() : base(0x103d) { Stackable = Core.ML; - Weight = 1.0; Hue = 150; } + public override double DefaultWeight => 1.0; public override int LabelNumber => 1041340; // sweet dough } @@ -35,23 +36,32 @@ public partial class JarHoney : Item [Constructible] public JarHoney() : base(0x9ec) { - Weight = 1.0; Stackable = true; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class BowlFlour : Item { [Constructible] - public BowlFlour() : base(0xa1e) => Weight = 1.0; + public BowlFlour() : base(0xa1e) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class WoodenBowl : Item { [Constructible] - public WoodenBowl() : base(0x15f8) => Weight = 1.0; + public WoodenBowl() : base(0x15f8) + { + } + + public override double DefaultWeight => 1.0; } [TypeAlias("Server.Items.SackFlourOpen")] @@ -61,10 +71,11 @@ public partial class SackFlour : Item, IHasQuantity [Constructible] public SackFlour() : base(0x1039) { - Weight = 5.0; _quantity = 20; } + public override double DefaultWeight => 5.0; + [SerializableProperty(0)] [CommandProperty(AccessLevel.GameMaster)] public int Quantity @@ -100,7 +111,11 @@ public partial class SackFlour : Item, IHasQuantity public partial class Eggshells : Item { [Constructible] - public Eggshells() : base(0x9b4) => Weight = 0.5; + public Eggshells() : base(0x9b4) + { + } + + public override double DefaultWeight => 0.5; } [SerializationGenerator(0, false)] @@ -109,11 +124,12 @@ public partial class WheatSheaf : Item [Constructible] public WheatSheaf(int amount = 1) : base(7869) { - Weight = 1.0; Stackable = true; Amount = amount; } + public override double DefaultWeight => 1.0; + public override void OnDoubleClick(Mobile from) { if (Movable) diff --git a/Projects/UOContent/Items/Food/Food.cs b/Projects/UOContent/Items/Food/Food.cs index 35c3b4e5b..9824adc3f 100644 --- a/Projects/UOContent/Items/Food/Food.cs +++ b/Projects/UOContent/Items/Food/Food.cs @@ -133,9 +133,10 @@ public partial class BreadLoaf : Food [Constructible] public BreadLoaf(int amount = 1) : base(0x103B, amount) { - Weight = 1.0; FillFactor = 3; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -144,9 +145,10 @@ public partial class Bacon : Food [Constructible] public Bacon(int amount = 1) : base(0x979, amount) { - Weight = 1.0; FillFactor = 1; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -155,9 +157,10 @@ public partial class SlabOfBacon : Food [Constructible] public SlabOfBacon(int amount = 1) : base(0x976, amount) { - Weight = 1.0; FillFactor = 3; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -202,9 +205,10 @@ public partial class FrenchBread : Food [Constructible] public FrenchBread(int amount = 1) : base(0x98C, amount) { - Weight = 2.0; FillFactor = 3; } + + public override double DefaultWeight => 2.0; } [SerializationGenerator(0, false)] @@ -213,9 +217,10 @@ public partial class FriedEggs : Food [Constructible] public FriedEggs(int amount = 1) : base(0x9B6, amount) { - Weight = 1.0; FillFactor = 4; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -224,9 +229,10 @@ public partial class CookedBird : Food [Constructible] public CookedBird(int amount = 1) : base(0x9B7, amount) { - Weight = 1.0; FillFactor = 5; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -235,9 +241,10 @@ public partial class RoastPig : Food [Constructible] public RoastPig(int amount = 1) : base(0x9BB, amount) { - Weight = 45.0; FillFactor = 20; } + + public override double DefaultWeight => 45.0; } [SerializationGenerator(0, false)] @@ -246,9 +253,10 @@ public partial class Sausage : Food [Constructible] public Sausage(int amount = 1) : base(0x9C0, amount) { - Weight = 1.0; FillFactor = 4; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -257,9 +265,10 @@ public partial class Ham : Food [Constructible] public Ham(int amount = 1) : base(0x9C9, amount) { - Weight = 1.0; FillFactor = 5; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -269,9 +278,10 @@ public partial class Cake : Food public Cake() : base(0x9E9) { Stackable = false; - Weight = 1.0; FillFactor = 10; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -280,9 +290,10 @@ public partial class Ribs : Food [Constructible] public Ribs(int amount = 1) : base(0x9F2, amount) { - Weight = 1.0; FillFactor = 5; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -292,9 +303,10 @@ public partial class Cookies : Food public Cookies() : base(0x160b) { Stackable = Core.ML; - Weight = 1.0; FillFactor = 4; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -304,9 +316,10 @@ public partial class Muffins : Food public Muffins() : base(0x9eb) { Stackable = false; - Weight = 1.0; FillFactor = 4; } + + public override double DefaultWeight => 1.0; } [TypeAlias("Server.Items.Pizza")] @@ -317,10 +330,10 @@ public partial class CheesePizza : Food public CheesePizza() : base(0x1040) { Stackable = false; - Weight = 1.0; FillFactor = 6; } + public override double DefaultWeight => 1.0; public override int LabelNumber => 1044516; // cheese pizza } @@ -331,10 +344,10 @@ public partial class SausagePizza : Food public SausagePizza() : base(0x1040) { Stackable = false; - Weight = 1.0; FillFactor = 6; } + public override double DefaultWeight => 1.0; public override int LabelNumber => 1044517; // sausage pizza } @@ -345,10 +358,10 @@ public partial class FruitPie : Food public FruitPie() : base(0x1041) { Stackable = false; - Weight = 1.0; FillFactor = 5; } + public override double DefaultWeight => 1.0; public override int LabelNumber => 1041346; // baked fruit pie } @@ -359,10 +372,10 @@ public partial class MeatPie : Food public MeatPie() : base(0x1041) { Stackable = false; - Weight = 1.0; FillFactor = 5; } + public override double DefaultWeight => 1.0; public override int LabelNumber => 1041347; // baked meat pie } @@ -373,10 +386,10 @@ public partial class PumpkinPie : Food public PumpkinPie() : base(0x1041) { Stackable = false; - Weight = 1.0; FillFactor = 5; } + public override double DefaultWeight => 1.0; public override int LabelNumber => 1041348; // baked pumpkin pie } @@ -387,10 +400,10 @@ public partial class ApplePie : Food public ApplePie() : base(0x1041) { Stackable = false; - Weight = 1.0; FillFactor = 5; } + public override double DefaultWeight => 1.0; public override int LabelNumber => 1041343; // baked apple pie } @@ -401,10 +414,10 @@ public partial class PeachCobbler : Food public PeachCobbler() : base(0x1041) { Stackable = false; - Weight = 1.0; FillFactor = 5; } + public override double DefaultWeight => 1.0; public override int LabelNumber => 1041344; // baked peach cobbler } @@ -415,10 +428,10 @@ public partial class Quiche : Food public Quiche() : base(0x1041) { Stackable = Core.ML; - Weight = 1.0; FillFactor = 5; } + public override double DefaultWeight => 1.0; public override int LabelNumber => 1041345; // baked quiche } @@ -428,9 +441,10 @@ public partial class LambLeg : Food [Constructible] public LambLeg(int amount = 1) : base(0x160a, amount) { - Weight = 2.0; FillFactor = 5; } + + public override double DefaultWeight => 2.0; } [SerializationGenerator(0, false)] @@ -439,9 +453,10 @@ public partial class ChickenLeg : Food [Constructible] public ChickenLeg(int amount = 1) : base(0x1608, amount) { - Weight = 1.0; FillFactor = 4; } + + public override double DefaultWeight => 1.0; } [Flippable(0xC74, 0xC75)] @@ -451,9 +466,10 @@ public partial class HoneydewMelon : Food [Constructible] public HoneydewMelon(int amount = 1) : base(0xC74, amount) { - Weight = 1.0; FillFactor = 1; } + + public override double DefaultWeight => 1.0; } [Flippable(0xC64, 0xC65)] @@ -463,9 +479,10 @@ public partial class YellowGourd : Food [Constructible] public YellowGourd(int amount = 1) : base(0xC64, amount) { - Weight = 1.0; FillFactor = 1; } + + public override double DefaultWeight => 1.0; } [Flippable(0xC66, 0xC67)] @@ -475,9 +492,10 @@ public partial class GreenGourd : Food [Constructible] public GreenGourd(int amount = 1) : base(0xC66, amount) { - Weight = 1.0; FillFactor = 1; } + + public override double DefaultWeight => 1.0; } [Flippable(0xC7F, 0xC81)] @@ -487,9 +505,10 @@ public partial class EarOfCorn : Food [Constructible] public EarOfCorn(int amount = 1) : base(0xC81, amount) { - Weight = 1.0; FillFactor = 1; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -498,14 +517,19 @@ public partial class Turnip : Food [Constructible] public Turnip(int amount = 1) : base(0xD3A, amount) { - Weight = 1.0; FillFactor = 1; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class SheafOfHay : Item { [Constructible] - public SheafOfHay() : base(0xF36) => Weight = 10.0; + public SheafOfHay() : base(0xF36) + { + } + + public override double DefaultWeight => 10.0; } diff --git a/Projects/UOContent/Items/Food/Fruits.cs b/Projects/UOContent/Items/Food/Fruits.cs index 9971f1d62..2a5ea409d 100644 --- a/Projects/UOContent/Items/Food/Fruits.cs +++ b/Projects/UOContent/Items/Food/Fruits.cs @@ -8,11 +8,12 @@ public partial class FruitBasket : Food [Constructible] public FruitBasket() : base(0x993) { - Weight = 2.0; FillFactor = 5; Stackable = false; } + public override double DefaultWeight => 2.0; + public override bool Eat(Mobile from) { if (!base.Eat(from)) @@ -32,9 +33,10 @@ public partial class Banana : Food [Constructible] public Banana(int amount = 1) : base(0x171f, amount) { - Weight = 1.0; FillFactor = 1; } + + public override double DefaultWeight => 1.0; } [Flippable(0x1721, 0x1722)] @@ -44,9 +46,10 @@ public partial class Bananas : Food [Constructible] public Bananas(int amount = 1) : base(0x1721, amount) { - Weight = 1.0; FillFactor = 1; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -55,9 +58,10 @@ public partial class SplitCoconut : Food [Constructible] public SplitCoconut(int amount = 1) : base(0x1725, amount) { - Weight = 1.0; FillFactor = 1; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -66,9 +70,10 @@ public partial class Lemon : Food [Constructible] public Lemon(int amount = 1) : base(0x1728, amount) { - Weight = 1.0; FillFactor = 1; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -77,9 +82,10 @@ public partial class Lemons : Food [Constructible] public Lemons(int amount = 1) : base(0x1729, amount) { - Weight = 1.0; FillFactor = 1; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -88,9 +94,10 @@ public partial class Lime : Food [Constructible] public Lime(int amount = 1) : base(0x172a, amount) { - Weight = 1.0; FillFactor = 1; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -99,9 +106,10 @@ public partial class Limes : Food [Constructible] public Limes(int amount = 1) : base(0x172B, amount) { - Weight = 1.0; FillFactor = 1; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -110,9 +118,10 @@ public partial class Coconut : Food [Constructible] public Coconut(int amount = 1) : base(0x1726, amount) { - Weight = 1.0; FillFactor = 1; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -121,9 +130,10 @@ public partial class OpenCoconut : Food [Constructible] public OpenCoconut(int amount = 1) : base(0x1723, amount) { - Weight = 1.0; FillFactor = 1; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -132,9 +142,10 @@ public partial class Dates : Food [Constructible] public Dates(int amount = 1) : base(0x1727, amount) { - Weight = 1.0; FillFactor = 1; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -143,9 +154,10 @@ public partial class Grapes : Food [Constructible] public Grapes(int amount = 1) : base(0x9D1, amount) { - Weight = 1.0; FillFactor = 1; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -154,9 +166,10 @@ public partial class Peach : Food [Constructible] public Peach(int amount = 1) : base(0x9D2, amount) { - Weight = 1.0; FillFactor = 1; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -165,9 +178,10 @@ public partial class Pear : Food [Constructible] public Pear(int amount = 1) : base(0x994, amount) { - Weight = 1.0; FillFactor = 1; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -176,9 +190,10 @@ public partial class Apple : Food [Constructible] public Apple(int amount = 1) : base(0x9D0, amount) { - Weight = 1.0; FillFactor = 1; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -187,9 +202,10 @@ public partial class Watermelon : Food [Constructible] public Watermelon(int amount = 1) : base(0xC5C, amount) { - Weight = 5.0; FillFactor = 5; } + + public override double DefaultWeight => 5.0; } [SerializationGenerator(0, false)] @@ -198,9 +214,10 @@ public partial class SmallWatermelon : Food [Constructible] public SmallWatermelon(int amount = 1) : base(0xC5D, amount) { - Weight = 5.0; FillFactor = 5; } + + public override double DefaultWeight => 5.0; } [Flippable(0xc72, 0xc73)] @@ -210,9 +227,10 @@ public partial class Squash : Food [Constructible] public Squash(int amount = 1) : base(0xc72, amount) { - Weight = 1.0; FillFactor = 1; } + + public override double DefaultWeight => 1.0; } [Flippable(0xc79, 0xc7a)] @@ -222,7 +240,8 @@ public partial class Cantaloupe : Food [Constructible] public Cantaloupe(int amount = 1) : base(0xc79, amount) { - Weight = 1.0; FillFactor = 1; } + + public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Food/Vegetables.cs b/Projects/UOContent/Items/Food/Vegetables.cs index 9824d23b3..49cd1fcb5 100644 --- a/Projects/UOContent/Items/Food/Vegetables.cs +++ b/Projects/UOContent/Items/Food/Vegetables.cs @@ -7,11 +7,9 @@ namespace Server.Items; public partial class Carrot : Food { [Constructible] - public Carrot(int amount = 1) : base(0xc78, amount) - { - Weight = 1.0; - FillFactor = 1; - } + public Carrot(int amount = 1) : base(0xc78, amount) => FillFactor = 1; + + public override double DefaultWeight => 1.0; } [Flippable(0xc7b, 0xc7c)] @@ -19,11 +17,9 @@ public partial class Carrot : Food public partial class Cabbage : Food { [Constructible] - public Cabbage(int amount = 1) : base(0xc7b, amount) - { - Weight = 1.0; - FillFactor = 1; - } + public Cabbage(int amount = 1) : base(0xc7b, amount) => FillFactor = 1; + + public override double DefaultWeight => 1.0; } [Flippable(0xc6d, 0xc6e)] @@ -31,11 +27,9 @@ public partial class Cabbage : Food public partial class Onion : Food { [Constructible] - public Onion(int amount = 1) : base(0xc6d, amount) - { - Weight = 1.0; - FillFactor = 1; - } + public Onion(int amount = 1) : base(0xc6d, amount) => FillFactor = 1; + + public override double DefaultWeight => 1.0; } [Flippable(0xc70, 0xc71)] @@ -43,11 +37,9 @@ public partial class Onion : Food public partial class Lettuce : Food { [Constructible] - public Lettuce(int amount = 1) : base(0xc70, amount) - { - Weight = 1.0; - FillFactor = 1; - } + public Lettuce(int amount = 1) : base(0xc70, amount) => FillFactor = 1; + + public override double DefaultWeight => 1.0; } [Flippable(0xC6A, 0xC6B)] @@ -55,20 +47,16 @@ public partial class Lettuce : Food public partial class Pumpkin : Food { [Constructible] - public Pumpkin(int amount = 1) : base(0xC6A, amount) - { - Weight = 1.0; - FillFactor = 8; - } + public Pumpkin(int amount = 1) : base(0xC6A, amount) => FillFactor = 8; + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class SmallPumpkin : Food { [Constructible] - public SmallPumpkin(int amount = 1) : base(0xC6C, amount) - { - Weight = 1.0; - FillFactor = 8; - } + public SmallPumpkin(int amount = 1) : base(0xC6C, amount) => FillFactor = 8; + + public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Games/BaseBoard.cs b/Projects/UOContent/Items/Games/BaseBoard.cs index 123f700dc..d18cbc60d 100644 --- a/Projects/UOContent/Items/Games/BaseBoard.cs +++ b/Projects/UOContent/Items/Games/BaseBoard.cs @@ -19,10 +19,10 @@ public abstract partial class BaseBoard : Container, ISecurable public BaseBoard(int itemID) : base(itemID) { CreatePieces(); - - Weight = 5.0; } + public override double DefaultWeight => 5.0; + public override bool DisplaysContent => false; // Do not display (x items, y stones) public override bool IsDecoContainer => false; diff --git a/Projects/UOContent/Items/Games/Dices.cs b/Projects/UOContent/Items/Games/Dices.cs index 37346703d..ce92158c2 100644 --- a/Projects/UOContent/Items/Games/Dices.cs +++ b/Projects/UOContent/Items/Games/Dices.cs @@ -6,7 +6,11 @@ namespace Server.Items; public partial class Dices : Item, ITelekinesisable { [Constructible] - public Dices() : base(0xFA7) => Weight = 1.0; + public Dices() : base(0xFA7) + { + } + + public override double DefaultWeight => 1.0; public void OnTelekinesis(Mobile from) { diff --git a/Projects/UOContent/Items/Games/Mahjong/MahjongGame.cs b/Projects/UOContent/Items/Games/Mahjong/MahjongGame.cs index a4be5c613..de7e663b7 100644 --- a/Projects/UOContent/Items/Games/Mahjong/MahjongGame.cs +++ b/Projects/UOContent/Items/Games/Mahjong/MahjongGame.cs @@ -43,8 +43,6 @@ public partial class MahjongGame : Item, ISecurable [Constructible] public MahjongGame() : base(0xFAA) { - Weight = 5.0; - BuildWalls(); _dealerIndicator = new MahjongDealerIndicator(this, new Point2D(300, 300), MahjongPieceDirection.Up, MahjongWind.North); @@ -55,6 +53,8 @@ public partial class MahjongGame : Item, ISecurable _level = SecureLevel.CoOwners; } + public override double DefaultWeight => 5.0; + [CommandProperty(AccessLevel.GameMaster)] [SerializableProperty(6)] public bool ShowScores diff --git a/Projects/UOContent/Items/Guilds/GuildDeed.cs b/Projects/UOContent/Items/Guilds/GuildDeed.cs index ac0869a71..1c1d6f61a 100644 --- a/Projects/UOContent/Items/Guilds/GuildDeed.cs +++ b/Projects/UOContent/Items/Guilds/GuildDeed.cs @@ -9,7 +9,11 @@ namespace Server.Items; public partial class GuildDeed : Item { [Constructible] - public GuildDeed() : base(0x14F0) => Weight = 1.0; + public GuildDeed() : base(0x14F0) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1041055; // a guild deed diff --git a/Projects/UOContent/Items/Guilds/GuildTeleporter.cs b/Projects/UOContent/Items/Guilds/GuildTeleporter.cs index dc471b0ad..44103e998 100644 --- a/Projects/UOContent/Items/Guilds/GuildTeleporter.cs +++ b/Projects/UOContent/Items/Guilds/GuildTeleporter.cs @@ -13,11 +13,12 @@ public partial class GuildTeleporter : Item [Constructible] public GuildTeleporter(Item stone = null) : base(0x1869) { - Weight = 1.0; LootType = LootType.Blessed; _stone = stone; } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1041054; // guildstone teleporter public override bool DisplayLootType => false; diff --git a/Projects/UOContent/Items/Guilds/Guildstone.cs b/Projects/UOContent/Items/Guilds/Guildstone.cs index 6c0603984..3faaeb192 100644 --- a/Projects/UOContent/Items/Guilds/Guildstone.cs +++ b/Projects/UOContent/Items/Guilds/Guildstone.cs @@ -228,10 +228,10 @@ public partial class GuildstoneDeed : Item _guild = g; _guildName = guildName; _guildAbbrev = abbrev; - - Weight = 1.0; } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1041233; // deed to a guildstone public override void GetProperties(IPropertyList list) diff --git a/Projects/UOContent/Items/Jewels/Beads.cs b/Projects/UOContent/Items/Jewels/Beads.cs index e60572be2..0558cb0d2 100644 --- a/Projects/UOContent/Items/Jewels/Beads.cs +++ b/Projects/UOContent/Items/Jewels/Beads.cs @@ -6,5 +6,9 @@ namespace Server.Items; public partial class Beads : Item { [Constructible] - public Beads() : base(0x108B) => Weight = 1.0; + public Beads() : base(0x108B) + { + } + + public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Jewels/Bracelet.cs b/Projects/UOContent/Items/Jewels/Bracelet.cs index 7a5aa44f8..90b79cb96 100644 --- a/Projects/UOContent/Items/Jewels/Bracelet.cs +++ b/Projects/UOContent/Items/Jewels/Bracelet.cs @@ -16,12 +16,20 @@ public abstract partial class BaseBracelet : BaseJewel public partial class GoldBracelet : BaseBracelet { [Constructible] - public GoldBracelet() : base(0x1086) => Weight = 0.1; + public GoldBracelet() : base(0x1086) + { + } + + public override double DefaultWeight => 0.1; } [SerializationGenerator(0, false)] public partial class SilverBracelet : BaseBracelet { [Constructible] - public SilverBracelet() : base(0x1F06) => Weight = 0.1; + public SilverBracelet() : base(0x1F06) + { + } + + public override double DefaultWeight => 0.1; } diff --git a/Projects/UOContent/Items/Jewels/Earrings.cs b/Projects/UOContent/Items/Jewels/Earrings.cs index 73a6ccd09..1596a26ef 100644 --- a/Projects/UOContent/Items/Jewels/Earrings.cs +++ b/Projects/UOContent/Items/Jewels/Earrings.cs @@ -16,12 +16,20 @@ public abstract partial class BaseEarrings : BaseJewel public partial class GoldEarrings : BaseEarrings { [Constructible] - public GoldEarrings() : base(0x1087) => Weight = 0.1; + public GoldEarrings() : base(0x1087) + { + } + + public override double DefaultWeight => 0.1; } [SerializationGenerator(0, false)] public partial class SilverEarrings : BaseEarrings { [Constructible] - public SilverEarrings() : base(0x1F07) => Weight = 0.1; + public SilverEarrings() : base(0x1F07) + { + } + + public override double DefaultWeight => 0.1; } diff --git a/Projects/UOContent/Items/Jewels/Necklace.cs b/Projects/UOContent/Items/Jewels/Necklace.cs index d4cd1eb9e..12a02144a 100644 --- a/Projects/UOContent/Items/Jewels/Necklace.cs +++ b/Projects/UOContent/Items/Jewels/Necklace.cs @@ -16,33 +16,53 @@ public abstract partial class BaseNecklace : BaseJewel public partial class Necklace : BaseNecklace { [Constructible] - public Necklace() : base(0x1085) => Weight = 0.1; + public Necklace() : base(0x1085) + { + } + + public override double DefaultWeight => 0.1; } [SerializationGenerator(0, false)] public partial class GoldNecklace : BaseNecklace { [Constructible] - public GoldNecklace() : base(0x1088) => Weight = 0.1; + public GoldNecklace() : base(0x1088) + { + } + + public override double DefaultWeight => 0.1; } [SerializationGenerator(0, false)] public partial class GoldBeadNecklace : BaseNecklace { [Constructible] - public GoldBeadNecklace() : base(0x1089) => Weight = 0.1; + public GoldBeadNecklace() : base(0x1089) + { + } + + public override double DefaultWeight => 0.1; } [SerializationGenerator(0, false)] public partial class SilverNecklace : BaseNecklace { [Constructible] - public SilverNecklace() : base(0x1F08) => Weight = 0.1; + public SilverNecklace() : base(0x1F08) + { + } + + public override double DefaultWeight => 0.1; } [SerializationGenerator(0, false)] public partial class SilverBeadNecklace : BaseNecklace { [Constructible] - public SilverBeadNecklace() : base(0x1F05) => Weight = 0.1; + public SilverBeadNecklace() : base(0x1F05) + { + } + + public override double DefaultWeight => 0.1; } diff --git a/Projects/UOContent/Items/Jewels/Ring.cs b/Projects/UOContent/Items/Jewels/Ring.cs index 9c142f099..856f1ccfe 100644 --- a/Projects/UOContent/Items/Jewels/Ring.cs +++ b/Projects/UOContent/Items/Jewels/Ring.cs @@ -16,12 +16,20 @@ public abstract partial class BaseRing : BaseJewel public partial class GoldRing : BaseRing { [Constructible] - public GoldRing() : base(0x108a) => Weight = 0.1; + public GoldRing() : base(0x108a) + { + } + + public override double DefaultWeight => 0.1; } [SerializationGenerator(0, false)] public partial class SilverRing : BaseRing { [Constructible] - public SilverRing() : base(0x1F09) => Weight = 0.1; + public SilverRing() : base(0x1F09) + { + } + + public override double DefaultWeight => 0.1; } diff --git a/Projects/UOContent/Items/Lights/Brazier.cs b/Projects/UOContent/Items/Lights/Brazier.cs index 56f834c3b..dd0080a29 100644 --- a/Projects/UOContent/Items/Lights/Brazier.cs +++ b/Projects/UOContent/Items/Lights/Brazier.cs @@ -13,8 +13,9 @@ public partial class Brazier : BaseLight Duration = TimeSpan.Zero; // Never burnt out Burning = true; Light = LightType.Circle225; - Weight = 20.0; } + public override double DefaultWeight => 20.0; + public override int LitItemID => 0xE31; } diff --git a/Projects/UOContent/Items/Lights/BrazierTall.cs b/Projects/UOContent/Items/Lights/BrazierTall.cs index c23945c50..17f444fc4 100644 --- a/Projects/UOContent/Items/Lights/BrazierTall.cs +++ b/Projects/UOContent/Items/Lights/BrazierTall.cs @@ -13,8 +13,9 @@ public partial class BrazierTall : BaseLight Duration = TimeSpan.Zero; // Never burnt out Burning = true; Light = LightType.Circle300; - Weight = 25.0; } + public override double DefaultWeight => 25.0; + public override int LitItemID => 0x19AA; } diff --git a/Projects/UOContent/Items/Lights/Candelabra.cs b/Projects/UOContent/Items/Lights/Candelabra.cs index 1558417bd..c2d2c80aa 100644 --- a/Projects/UOContent/Items/Lights/Candelabra.cs +++ b/Projects/UOContent/Items/Lights/Candelabra.cs @@ -16,9 +16,10 @@ public partial class Candelabra : BaseLight, IShipwreckedItem Duration = TimeSpan.Zero; // Never burnt out Burning = false; Light = LightType.Circle225; - Weight = 3.0; } + public override double DefaultWeight => 3.0; + public override int LitItemID => 0xB1D; public override int UnlitItemID => 0xA27; diff --git a/Projects/UOContent/Items/Lights/CandelabraStand.cs b/Projects/UOContent/Items/Lights/CandelabraStand.cs index 00e49e521..b546947eb 100644 --- a/Projects/UOContent/Items/Lights/CandelabraStand.cs +++ b/Projects/UOContent/Items/Lights/CandelabraStand.cs @@ -12,9 +12,9 @@ public partial class CandelabraStand : BaseLight Duration = TimeSpan.Zero; // Never burnt out Burning = false; Light = LightType.Circle225; - Weight = 20.0; } + public override double DefaultWeight => 20.0; public override int LitItemID => 0xB26; public override int UnlitItemID => 0xA29; } diff --git a/Projects/UOContent/Items/Lights/Candle.cs b/Projects/UOContent/Items/Lights/Candle.cs index f0d5bd42e..283bccb03 100644 --- a/Projects/UOContent/Items/Lights/Candle.cs +++ b/Projects/UOContent/Items/Lights/Candle.cs @@ -14,9 +14,9 @@ public partial class Candle : BaseEquipableLight Stackable = true; Light = LightType.Circle150; - Weight = 1.0; } + public override double DefaultWeight => 1.0; public override int LitItemID => 0xA0F; public override int UnlitItemID => 0xA28; } diff --git a/Projects/UOContent/Items/Lights/CandleLarge.cs b/Projects/UOContent/Items/Lights/CandleLarge.cs index 12dfdf188..7981e6e4c 100644 --- a/Projects/UOContent/Items/Lights/CandleLarge.cs +++ b/Projects/UOContent/Items/Lights/CandleLarge.cs @@ -10,12 +10,11 @@ public partial class CandleLarge : BaseLight public CandleLarge() : base(0xA26) { Duration = Burnout ? TimeSpan.FromMinutes(25) : TimeSpan.Zero; - Burning = false; Light = LightType.Circle150; - Weight = 2.0; } + public override double DefaultWeight => 2.0; public override int LitItemID => 0xB1A; public override int UnlitItemID => 0xA26; } diff --git a/Projects/UOContent/Items/Lights/CandleLong.cs b/Projects/UOContent/Items/Lights/CandleLong.cs index 39daca623..4548f95d0 100644 --- a/Projects/UOContent/Items/Lights/CandleLong.cs +++ b/Projects/UOContent/Items/Lights/CandleLong.cs @@ -10,12 +10,12 @@ public partial class CandleLong : BaseLight public CandleLong() : base(0x1433) { Duration = Burnout ? TimeSpan.FromMinutes(30) : TimeSpan.Zero; - Burning = false; Light = LightType.Circle150; - Weight = 1.0; } + public override double DefaultWeight => 1.0; + public override int LitItemID => 0x1430; public override int UnlitItemID => 0x1433; } diff --git a/Projects/UOContent/Items/Lights/CandleShort.cs b/Projects/UOContent/Items/Lights/CandleShort.cs index 942478f35..4ad2758f2 100644 --- a/Projects/UOContent/Items/Lights/CandleShort.cs +++ b/Projects/UOContent/Items/Lights/CandleShort.cs @@ -10,12 +10,12 @@ public partial class CandleShort : BaseLight public CandleShort() : base(0x142F) { Duration = Burnout ? TimeSpan.FromMinutes(25) : TimeSpan.Zero; - Burning = false; Light = LightType.Circle150; - Weight = 1.0; } + public override double DefaultWeight => 1.0; + public override int LitItemID => 0x142C; public override int UnlitItemID => 0x142F; } diff --git a/Projects/UOContent/Items/Lights/CandleSkull.cs b/Projects/UOContent/Items/Lights/CandleSkull.cs index 8cd3a9f21..7236f4fbc 100644 --- a/Projects/UOContent/Items/Lights/CandleSkull.cs +++ b/Projects/UOContent/Items/Lights/CandleSkull.cs @@ -13,9 +13,10 @@ public partial class CandleSkull : BaseLight Burning = false; Light = LightType.Circle150; - Weight = 5.0; } + public override double DefaultWeight => 5.0; + public override int LitItemID => ItemID is 0x1583 or 0x1854 ? 0x1854 : 0x1858; public override int UnlitItemID => ItemID is 0x1853 or 0x1584 ? 0x1853 : 0x1857; diff --git a/Projects/UOContent/Items/Lights/HangingLantern.cs b/Projects/UOContent/Items/Lights/HangingLantern.cs index 1ae33dcf9..942635a02 100644 --- a/Projects/UOContent/Items/Lights/HangingLantern.cs +++ b/Projects/UOContent/Items/Lights/HangingLantern.cs @@ -13,9 +13,9 @@ public partial class HangingLantern : BaseLight Duration = TimeSpan.Zero; // Never burnt out Burning = false; Light = LightType.Circle300; - Weight = 40.0; } + public override double DefaultWeight => 40.0; public override int LitItemID => 0xA1A; public override int UnlitItemID => 0xA1D; } diff --git a/Projects/UOContent/Items/Lights/HeatingStand.cs b/Projects/UOContent/Items/Lights/HeatingStand.cs index 1b1abe044..ec9f6359c 100644 --- a/Projects/UOContent/Items/Lights/HeatingStand.cs +++ b/Projects/UOContent/Items/Lights/HeatingStand.cs @@ -10,12 +10,12 @@ public partial class HeatingStand : BaseLight public HeatingStand() : base(0x1849) { Duration = Burnout ? TimeSpan.FromMinutes(25) : TimeSpan.Zero; - Burning = false; Light = LightType.Empty; - Weight = 1.0; } + public override double DefaultWeight => 1.0; + public override int LitItemID => 0x184A; public override int UnlitItemID => 0x1849; diff --git a/Projects/UOContent/Items/Lights/LampPost1.cs b/Projects/UOContent/Items/Lights/LampPost1.cs index 985e6e7b1..3f6884618 100644 --- a/Projects/UOContent/Items/Lights/LampPost1.cs +++ b/Projects/UOContent/Items/Lights/LampPost1.cs @@ -13,9 +13,9 @@ public partial class LampPost1 : BaseLight Duration = TimeSpan.Zero; // Never burnt out Burning = false; Light = LightType.Circle300; - Weight = 40.0; } + public override double DefaultWeight => 40.0; public override int LitItemID => 0xB20; public override int UnlitItemID => 0xB21; } diff --git a/Projects/UOContent/Items/Lights/LampPost2.cs b/Projects/UOContent/Items/Lights/LampPost2.cs index f36bdf4db..d0f630fe4 100644 --- a/Projects/UOContent/Items/Lights/LampPost2.cs +++ b/Projects/UOContent/Items/Lights/LampPost2.cs @@ -13,9 +13,9 @@ public partial class LampPost2 : BaseLight Duration = TimeSpan.Zero; // Never burnt out Burning = false; Light = LightType.Circle300; - Weight = 40.0; } + public override double DefaultWeight => 40.0; public override int LitItemID => 0xB22; public override int UnlitItemID => 0xB23; } diff --git a/Projects/UOContent/Items/Lights/LampPost3.cs b/Projects/UOContent/Items/Lights/LampPost3.cs index c9cb237a0..24ffa44a5 100644 --- a/Projects/UOContent/Items/Lights/LampPost3.cs +++ b/Projects/UOContent/Items/Lights/LampPost3.cs @@ -13,9 +13,9 @@ public partial class LampPost3 : BaseLight Duration = TimeSpan.Zero; // Never burnt out Burning = false; Light = LightType.Circle300; - Weight = 40.0; } + public override double DefaultWeight => 40.0; public override int LitItemID => 0xB24; public override int UnlitItemID => 0xB25; } diff --git a/Projects/UOContent/Items/Lights/Lantern.cs b/Projects/UOContent/Items/Lights/Lantern.cs index b7cddd0cc..366728e95 100644 --- a/Projects/UOContent/Items/Lights/Lantern.cs +++ b/Projects/UOContent/Items/Lights/Lantern.cs @@ -15,9 +15,10 @@ public partial class Lantern : BaseEquipableLight Burning = false; Light = LightType.Circle300; - Weight = 2.0; } + public override double DefaultWeight => 2.0; + public override int LitItemID => ItemID is 0xA15 or 0xA17 ? ItemID : 0xA22; public override int UnlitItemID => ItemID == 0xA18 ? ItemID : 0xA25; diff --git a/Projects/UOContent/Items/Lights/PaperLantern.cs b/Projects/UOContent/Items/Lights/PaperLantern.cs index 681bf7705..fde95fb3e 100644 --- a/Projects/UOContent/Items/Lights/PaperLantern.cs +++ b/Projects/UOContent/Items/Lights/PaperLantern.cs @@ -14,9 +14,9 @@ public partial class PaperLantern : BaseLight Duration = TimeSpan.Zero; // Never burnt out Burning = false; Light = LightType.Circle150; - Weight = 3.0; } + public override double DefaultWeight => 3.0; public override int LitItemID => 0x24BD; public override int UnlitItemID => 0x24BE; } diff --git a/Projects/UOContent/Items/Lights/RedHangingLantern.cs b/Projects/UOContent/Items/Lights/RedHangingLantern.cs index 3643c2e12..173a2e082 100644 --- a/Projects/UOContent/Items/Lights/RedHangingLantern.cs +++ b/Projects/UOContent/Items/Lights/RedHangingLantern.cs @@ -14,9 +14,10 @@ public partial class RedHangingLantern : BaseLight Duration = TimeSpan.Zero; // Never burnt out Burning = false; Light = LightType.Circle300; - Weight = 3.0; } + public override double DefaultWeight => 3.0; + public override int LitItemID => ItemID == 0x24C2 ? 0x24C1 : 0x24C3; public override int UnlitItemID => ItemID == 0x24C1 ? 0x24C2 : 0x24C4; diff --git a/Projects/UOContent/Items/Lights/RoundPaperLantern.cs b/Projects/UOContent/Items/Lights/RoundPaperLantern.cs index 3b028a0d3..d1bd3ae1e 100644 --- a/Projects/UOContent/Items/Lights/RoundPaperLantern.cs +++ b/Projects/UOContent/Items/Lights/RoundPaperLantern.cs @@ -14,9 +14,10 @@ public partial class RoundPaperLantern : BaseLight Duration = TimeSpan.Zero; // Never burnt out Burning = false; Light = LightType.Circle150; - Weight = 3.0; } + public override double DefaultWeight => 3.0; + public override int LitItemID => 0x24C9; public override int UnlitItemID => 0x24CA; } diff --git a/Projects/UOContent/Items/Lights/ShojiLantern.cs b/Projects/UOContent/Items/Lights/ShojiLantern.cs index 56aa5d81e..f1482eff8 100644 --- a/Projects/UOContent/Items/Lights/ShojiLantern.cs +++ b/Projects/UOContent/Items/Lights/ShojiLantern.cs @@ -14,9 +14,9 @@ public partial class ShojiLantern : BaseLight Duration = TimeSpan.Zero; // Never burnt out Burning = false; Light = LightType.Circle150; - Weight = 3.0; } + public override double DefaultWeight => 3.0; public override int LitItemID => 0x24BB; public override int UnlitItemID => 0x24BC; } diff --git a/Projects/UOContent/Items/Lights/Torch.cs b/Projects/UOContent/Items/Lights/Torch.cs index 75c5f0967..a2f527070 100644 --- a/Projects/UOContent/Items/Lights/Torch.cs +++ b/Projects/UOContent/Items/Lights/Torch.cs @@ -15,9 +15,10 @@ public partial class Torch : BaseEquipableLight Stackable = true; Light = LightType.Circle300; - Weight = 1.0; } + public override double DefaultWeight => 1.0; + public override int LitItemID => 0xA12; public override int UnlitItemID => 0xF6B; diff --git a/Projects/UOContent/Items/Lights/WallSconce.cs b/Projects/UOContent/Items/Lights/WallSconce.cs index 3268efcaa..bf1c2576b 100644 --- a/Projects/UOContent/Items/Lights/WallSconce.cs +++ b/Projects/UOContent/Items/Lights/WallSconce.cs @@ -14,9 +14,10 @@ public partial class WallSconce : BaseLight Duration = TimeSpan.Zero; // Never burnt out Burning = false; Light = LightType.WestBig; - Weight = 3.0; } + public override double DefaultWeight => 3.0; + public override int LitItemID => ItemID == 0x9FB ? 0x9FD : 0xA02; public override int UnlitItemID => ItemID == 0x9FD ? 0x9FB : 0xA00; diff --git a/Projects/UOContent/Items/Lights/WallTorch.cs b/Projects/UOContent/Items/Lights/WallTorch.cs index 968b0bbae..9ed6944ec 100644 --- a/Projects/UOContent/Items/Lights/WallTorch.cs +++ b/Projects/UOContent/Items/Lights/WallTorch.cs @@ -14,9 +14,10 @@ public partial class WallTorch : BaseLight Duration = TimeSpan.Zero; // Never burnt out Burning = false; Light = LightType.WestBig; - Weight = 3.0; } + public override double DefaultWeight => 3.0; + public override int LitItemID => ItemID == 0xA05 ? 0xA07 : 0xA0C; public override int UnlitItemID => ItemID == 0xA07 ? 0xA05 : 0xA0A; diff --git a/Projects/UOContent/Items/Lights/WhiteHangingLantern.cs b/Projects/UOContent/Items/Lights/WhiteHangingLantern.cs index 7d62bde96..20837465e 100644 --- a/Projects/UOContent/Items/Lights/WhiteHangingLantern.cs +++ b/Projects/UOContent/Items/Lights/WhiteHangingLantern.cs @@ -14,9 +14,10 @@ public partial class WhiteHangingLantern : BaseLight Duration = TimeSpan.Zero; // Never burnt out Burning = false; Light = LightType.Circle300; - Weight = 3.0; } + public override double DefaultWeight => 3.0; + public override int LitItemID => ItemID == 0x24C6 ? 0x24C5 : 0x24C7; public override int UnlitItemID => ItemID == 0x24C5 ? 0x24C6 : 0x24C8; diff --git a/Projects/UOContent/Items/Maps/MapItem.cs b/Projects/UOContent/Items/Maps/MapItem.cs index b9ee21e53..0467d8220 100644 --- a/Projects/UOContent/Items/Maps/MapItem.cs +++ b/Projects/UOContent/Items/Maps/MapItem.cs @@ -42,13 +42,14 @@ public partial class MapItem : Item, ICraftable [Constructible] public MapItem(Map facet = null) : base(0x14EC) { - Weight = 1.0; _width = 200; _height = 200; _facet = facet; _pins = new List(); } + public override double DefaultWeight => 1.0; + public int OnCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, CraftItem craftItem, int resHue diff --git a/Projects/UOContent/Items/Minor Artifacts/ML/RobeOfTheEclipse.cs b/Projects/UOContent/Items/Minor Artifacts/ML/RobeOfTheEclipse.cs index 29caacff8..b2d48ed0f 100644 --- a/Projects/UOContent/Items/Minor Artifacts/ML/RobeOfTheEclipse.cs +++ b/Projects/UOContent/Items/Minor Artifacts/ML/RobeOfTheEclipse.cs @@ -9,12 +9,12 @@ public partial class RobeOfTheEclipse : BaseOuterTorso [Constructible] public RobeOfTheEclipse() : base(0x1F03, 0x486) { - Weight = 3.0; - Attributes.Luck = 95; // TODO: Supports arcane? } + public override double DefaultWeight => 3.0; + public override int LabelNumber => 1075082; // Robe of the Eclipse } diff --git a/Projects/UOContent/Items/Minor Artifacts/ML/RobeOfTheEquinox.cs b/Projects/UOContent/Items/Minor Artifacts/ML/RobeOfTheEquinox.cs index 3bfc6a283..54b4c4fe7 100644 --- a/Projects/UOContent/Items/Minor Artifacts/ML/RobeOfTheEquinox.cs +++ b/Projects/UOContent/Items/Minor Artifacts/ML/RobeOfTheEquinox.cs @@ -9,13 +9,13 @@ public partial class RobeOfTheEquinox : BaseOuterTorso [Constructible] public RobeOfTheEquinox() : base(0x1F04, 0xD6) { - Weight = 3.0; - Attributes.Luck = 95; // TODO: Supports arcane? // TODO: Elves Only } + public override double DefaultWeight => 3.0; + public override int LabelNumber => 1075042; // Robe of the Equinox } diff --git a/Projects/UOContent/Items/Misc/ArcaneGem.cs b/Projects/UOContent/Items/Misc/ArcaneGem.cs index ea474d086..4150deb87 100644 --- a/Projects/UOContent/Items/Misc/ArcaneGem.cs +++ b/Projects/UOContent/Items/Misc/ArcaneGem.cs @@ -13,9 +13,10 @@ public partial class ArcaneGem : Item public ArcaneGem() : base(0x1EA7) { Stackable = Core.ML; - Weight = 1.0; } + public override double DefaultWeight => 1.0; + public override string DefaultName => "arcane gem"; public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Items/Misc/BankCheck.cs b/Projects/UOContent/Items/Misc/BankCheck.cs index 4408d9f4f..1f42e6383 100644 --- a/Projects/UOContent/Items/Misc/BankCheck.cs +++ b/Projects/UOContent/Items/Misc/BankCheck.cs @@ -20,13 +20,14 @@ public partial class BankCheck : Item [Constructible] public BankCheck(int worth) : base(0x14F0) { - Weight = 1.0; Hue = 0x34; LootType = LootType.Blessed; _worth = worth; } + public override double DefaultWeight => 1.0; + public override bool DisplayLootType => Core.AOS; public override int LabelNumber => 1041361; // A bank check diff --git a/Projects/UOContent/Items/Misc/Beeswax.cs b/Projects/UOContent/Items/Misc/Beeswax.cs index 81349d043..46354510a 100644 --- a/Projects/UOContent/Items/Misc/Beeswax.cs +++ b/Projects/UOContent/Items/Misc/Beeswax.cs @@ -8,8 +8,9 @@ public partial class Beeswax : Item [Constructible] public Beeswax(int amount = 1) : base(0x1422) { - Weight = 1.0; Stackable = true; Amount = amount; } + + public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Misc/Bola.cs b/Projects/UOContent/Items/Misc/Bola.cs index 716a8e613..a2943c108 100644 --- a/Projects/UOContent/Items/Misc/Bola.cs +++ b/Projects/UOContent/Items/Misc/Bola.cs @@ -12,11 +12,12 @@ public partial class Bola : Item [Constructible] public Bola(int amount = 1) : base(0x26AC) { - Weight = 4.0; Stackable = true; Amount = amount; } + public override double DefaultWeight => 4.0; + public override void OnDoubleClick(Mobile from) { if (!IsChildOf(from.Backpack)) diff --git a/Projects/UOContent/Items/Misc/BolaBall.cs b/Projects/UOContent/Items/Misc/BolaBall.cs index b372b3abf..b4737a2cb 100644 --- a/Projects/UOContent/Items/Misc/BolaBall.cs +++ b/Projects/UOContent/Items/Misc/BolaBall.cs @@ -8,9 +8,10 @@ public partial class BolaBall : Item [Constructible] public BolaBall(int amount = 1) : base(0xE73) { - Weight = 4.0; Stackable = true; Amount = amount; Hue = 0x8AC; } + + public override double DefaultWeight => 4.0; } diff --git a/Projects/UOContent/Items/Misc/ClockworkAssembly.cs b/Projects/UOContent/Items/Misc/ClockworkAssembly.cs index 7348b6469..9c72aa240 100644 --- a/Projects/UOContent/Items/Misc/ClockworkAssembly.cs +++ b/Projects/UOContent/Items/Misc/ClockworkAssembly.cs @@ -34,10 +34,11 @@ public partial class ClockworkAssembly : Item [Constructible] public ClockworkAssembly() : base(0x1EA8) { - Weight = 5.0; Hue = 1102; } + public override double DefaultWeight => 5.0; + public override int LabelNumber => 1073426; // Clockwork Assembly public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Items/Misc/Corpses/Corpse.cs b/Projects/UOContent/Items/Misc/Corpses/Corpse.cs index 4c2f1ff26..92504888e 100644 --- a/Projects/UOContent/Items/Misc/Corpses/Corpse.cs +++ b/Projects/UOContent/Items/Misc/Corpses/Corpse.cs @@ -824,25 +824,28 @@ public partial class Corpse : Container, ICarvable } var pack = from.Backpack; + using var items = PooledRefList.Create(128); if (RestoreEquip != null && pack != null) { - var packItems = new List(pack.Items); // Only items in the top-level pack are re-equipped + items.AddRange(pack.Items); - for (var i = 0; i < packItems.Count; i++) + // Only items in the top-level pack are re-equipped + for (var i = 0; i < items.Count; i++) { - var packItem = packItems[i]; + var packItem = items[i]; if (RestoreEquip.Contains(packItem) && packItem.Movable) { from.EquipItem(packItem); } } + + items.Clear(); } - var items = new List(Items); - var didntFit = false; + items.AddRange(Items); for (var i = 0; !didntFit && i < items.Count; ++i) { diff --git a/Projects/UOContent/Items/Misc/ExecutionersCap.cs b/Projects/UOContent/Items/Misc/ExecutionersCap.cs index 69ba5ffe1..d2e1ae395 100644 --- a/Projects/UOContent/Items/Misc/ExecutionersCap.cs +++ b/Projects/UOContent/Items/Misc/ExecutionersCap.cs @@ -6,5 +6,9 @@ namespace Server.Items; public partial class ExecutionersCap : Item { [Constructible] - public ExecutionersCap() : base(0xF83) => Weight = 1.0; + public ExecutionersCap() : base(0xF83) + { + } + + public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Misc/Firebomb.cs b/Projects/UOContent/Items/Misc/Firebomb.cs index 89545a8ad..fe8f8a656 100644 --- a/Projects/UOContent/Items/Misc/Firebomb.cs +++ b/Projects/UOContent/Items/Misc/Firebomb.cs @@ -18,11 +18,9 @@ public partial class Firebomb : Item private HashSet _users; [Constructible] - public Firebomb(int itemID = 0x99B) : base(itemID) - { - Weight = 2.0; - Hue = 1260; - } + public Firebomb(int itemID = 0x99B) : base(itemID) => Hue = 1260; + + public override double DefaultWeight => 2.0; public override void OnDoubleClick(Mobile from) { diff --git a/Projects/UOContent/Items/Misc/GlassItems.cs b/Projects/UOContent/Items/Misc/GlassItems.cs index 9f3bbf070..6b86a8de2 100644 --- a/Projects/UOContent/Items/Misc/GlassItems.cs +++ b/Projects/UOContent/Items/Misc/GlassItems.cs @@ -7,7 +7,11 @@ namespace Server.Items; public partial class SmallFlask : Item { [Constructible] - public SmallFlask() : base(0x182E) => Weight = 1.0; + public SmallFlask() : base(0x182E) + { + } + + public override double DefaultWeight => 1.0; } [Flippable(0x182A, 0x182B, 0x182C, 0x182D)] @@ -15,7 +19,11 @@ public partial class SmallFlask : Item public partial class MediumFlask : Item { [Constructible] - public MediumFlask() : base(0x182A) => Weight = 1.0; + public MediumFlask() : base(0x182A) + { + } + + public override double DefaultWeight => 1.0; } [Flippable(0x183B, 0x183C, 0x183D)] @@ -23,7 +31,11 @@ public partial class MediumFlask : Item public partial class LargeFlask : Item { [Constructible] - public LargeFlask() : base(0x183B) => Weight = 1.0; + public LargeFlask() : base(0x183B) + { + } + + public override double DefaultWeight => 1.0; } [Flippable(0x1832, 0x1833, 0x1834, 0x1835, 0x1836, 0x1837)] @@ -31,7 +43,11 @@ public partial class LargeFlask : Item public partial class CurvedFlask : Item { [Constructible] - public CurvedFlask() : base(0x1832) => Weight = 1.0; + public CurvedFlask() : base(0x1832) + { + } + + public override double DefaultWeight => 1.0; } [Flippable(0x1838, 0x1839, 0x183A)] @@ -39,7 +55,11 @@ public partial class CurvedFlask : Item public partial class LongFlask : Item { [Constructible] - public LongFlask() : base(0x1838) => Weight = 1.0; + public LongFlask() : base(0x1838) + { + } + + public override double DefaultWeight => 1.0; } [Flippable(0x1810, 0x1811)] @@ -47,49 +67,77 @@ public partial class LongFlask : Item public partial class SpinningHourglass : Item { [Constructible] - public SpinningHourglass() : base(0x1810) => Weight = 1.0; + public SpinningHourglass() : base(0x1810) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class GreenBottle : Item { [Constructible] - public GreenBottle() : base(0x0EFB) => Weight = 1.0; + public GreenBottle() : base(0x0EFB) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class RedBottle : Item { [Constructible] - public RedBottle() : base(0x0EFC) => Weight = 1.0; + public RedBottle() : base(0x0EFC) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class SmallBrownBottle : Item { [Constructible] - public SmallBrownBottle() : base(0x0EFD) => Weight = 1.0; + public SmallBrownBottle() : base(0x0EFD) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class SmallGreenBottle : Item { [Constructible] - public SmallGreenBottle() : base(0x0F01) => Weight = 1.0; + public SmallGreenBottle() : base(0x0F01) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class SmallVioletBottle : Item { [Constructible] - public SmallVioletBottle() : base(0x0F02) => Weight = 1.0; + public SmallVioletBottle() : base(0x0F02) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class TinyYellowBottle : Item { [Constructible] - public TinyYellowBottle() : base(0x0F03) => Weight = 1.0; + public TinyYellowBottle() : base(0x0F03) + { + } + + public override double DefaultWeight => 1.0; } // remove @@ -97,175 +145,275 @@ public partial class TinyYellowBottle : Item public partial class SmallBlueFlask : Item { [Constructible] - public SmallBlueFlask() : base(0x182A) => Weight = 1.0; + public SmallBlueFlask() : base(0x182A) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class SmallYellowFlask : Item { [Constructible] - public SmallYellowFlask() : base(0x182B) => Weight = 1.0; + public SmallYellowFlask() : base(0x182B) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class SmallRedFlask : Item { [Constructible] - public SmallRedFlask() : base(0x182C) => Weight = 1.0; + public SmallRedFlask() : base(0x182C) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class SmallEmptyFlask : Item { [Constructible] - public SmallEmptyFlask() : base(0x182D) => Weight = 1.0; + public SmallEmptyFlask() : base(0x182D) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class YellowBeaker : Item { [Constructible] - public YellowBeaker() : base(0x182E) => Weight = 1.0; + public YellowBeaker() : base(0x182E) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class RedBeaker : Item { [Constructible] - public RedBeaker() : base(0x182F) => Weight = 1.0; + public RedBeaker() : base(0x182F) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class BlueBeaker : Item { [Constructible] - public BlueBeaker() : base(0x1830) => Weight = 1.0; + public BlueBeaker() : base(0x1830) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class GreenBeaker : Item { [Constructible] - public GreenBeaker() : base(0x1831) => Weight = 1.0; + public GreenBeaker() : base(0x1831) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class EmptyCurvedFlaskW : Item { [Constructible] - public EmptyCurvedFlaskW() : base(0x1832) => Weight = 1.0; + public EmptyCurvedFlaskW() : base(0x1832) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class RedCurvedFlask : Item { [Constructible] - public RedCurvedFlask() : base(0x1833) => Weight = 1.0; + public RedCurvedFlask() : base(0x1833) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class LtBlueCurvedFlask : Item { [Constructible] - public LtBlueCurvedFlask() : base(0x1834) => Weight = 1.0; + public LtBlueCurvedFlask() : base(0x1834) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class EmptyCurvedFlaskE : Item { [Constructible] - public EmptyCurvedFlaskE() : base(0x1835) => Weight = 1.0; + public EmptyCurvedFlaskE() : base(0x1835) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class BlueCurvedFlask : Item { [Constructible] - public BlueCurvedFlask() : base(0x1836) => Weight = 1.0; + public BlueCurvedFlask() : base(0x1836) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class GreenCurvedFlask : Item { [Constructible] - public GreenCurvedFlask() : base(0x1837) => Weight = 1.0; + public GreenCurvedFlask() : base(0x1837) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class RedRibbedFlask : Item { [Constructible] - public RedRibbedFlask() : base(0x1838) => Weight = 1.0; + public RedRibbedFlask() : base(0x1838) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class VioletRibbedFlask : Item { [Constructible] - public VioletRibbedFlask() : base(0x1839) => Weight = 1.0; + public VioletRibbedFlask() : base(0x1839) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class EmptyRibbedFlask : Item { [Constructible] - public EmptyRibbedFlask() : base(0x183A) => Weight = 1.0; + public EmptyRibbedFlask() : base(0x183A) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class LargeYellowFlask : Item { [Constructible] - public LargeYellowFlask() : base(0x183B) => Weight = 1.0; + public LargeYellowFlask() : base(0x183B) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class LargeVioletFlask : Item { [Constructible] - public LargeVioletFlask() : base(0x183C) => Weight = 1.0; + public LargeVioletFlask() : base(0x183C) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class LargeEmptyFlask : Item { [Constructible] - public LargeEmptyFlask() : base(0x183D) => Weight = 1.0; + public LargeEmptyFlask() : base(0x183D) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class AniRedRibbedFlask : Item { [Constructible] - public AniRedRibbedFlask() : base(0x183E) => Weight = 1.0; + public AniRedRibbedFlask() : base(0x183E) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class AniLargeVioletFlask : Item { [Constructible] - public AniLargeVioletFlask() : base(0x1841) => Weight = 1.0; + public AniLargeVioletFlask() : base(0x1841) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class AniSmallBlueFlask : Item { [Constructible] - public AniSmallBlueFlask() : base(0x1844) => Weight = 1.0; + public AniSmallBlueFlask() : base(0x1844) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class SmallBlueBottle : Item { [Constructible] - public SmallBlueBottle() : base(0x1847) => Weight = 1.0; + public SmallBlueBottle() : base(0x1847) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class SmallGreenBottle2 : Item { [Constructible] - public SmallGreenBottle2() : base(0x1848) => Weight = 1.0; + public SmallGreenBottle2() : base(0x1848) + { + } + + public override double DefaultWeight => 1.0; } [Flippable(0x185B, 0x185C)] @@ -273,7 +421,11 @@ public partial class SmallGreenBottle2 : Item public partial class EmptyVialsWRack : Item { [Constructible] - public EmptyVialsWRack() : base(0x185B) => Weight = 1.0; + public EmptyVialsWRack() : base(0x185B) + { + } + + public override double DefaultWeight => 1.0; } [Flippable(0x185D, 0x185E)] @@ -281,33 +433,53 @@ public partial class EmptyVialsWRack : Item public partial class FullVialsWRack : Item { [Constructible] - public FullVialsWRack() : base(0x185D) => Weight = 1.0; + public FullVialsWRack() : base(0x185D) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class EmptyVial : Item { [Constructible] - public EmptyVial() : base(0x0E24) => Weight = 1.0; + public EmptyVial() : base(0x0E24) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class HourglassAni : Item { [Constructible] - public HourglassAni() : base(0x1811) => Weight = 1.0; + public HourglassAni() : base(0x1811) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class Hourglass : Item { [Constructible] - public Hourglass() : base(0x1810) => Weight = 1.0; + public Hourglass() : base(0x1810) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class TinyRedBottle : Item { [Constructible] - public TinyRedBottle() : base(0x0F04) => Weight = 1.0; + public TinyRedBottle() : base(0x0F04) + { + } + + public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Misc/HairDye.cs b/Projects/UOContent/Items/Misc/HairDye.cs index 2693250a0..c32e1aa4d 100644 --- a/Projects/UOContent/Items/Misc/HairDye.cs +++ b/Projects/UOContent/Items/Misc/HairDye.cs @@ -9,7 +9,11 @@ namespace Server.Items; public partial class HairDye : Item { [Constructible] - public HairDye() : base(0xEFF) => Weight = 1.0; + public HairDye() : base(0xEFF) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1041060; // Hair Dye diff --git a/Projects/UOContent/Items/Misc/InteriorDecorator.cs b/Projects/UOContent/Items/Misc/InteriorDecorator.cs index 9c197685e..6af30b95f 100644 --- a/Projects/UOContent/Items/Misc/InteriorDecorator.cs +++ b/Projects/UOContent/Items/Misc/InteriorDecorator.cs @@ -22,10 +22,11 @@ public partial class InteriorDecorator : Item [Constructible] public InteriorDecorator() : base(0xFC1) { - Weight = 1.0; LootType = LootType.Blessed; } + public override double DefaultWeight => 1.0; + [CommandProperty(AccessLevel.GameMaster)] public DecorateCommand Command { diff --git a/Projects/UOContent/Items/Misc/Key.cs b/Projects/UOContent/Items/Misc/Key.cs index 4e16c55c1..931ccecba 100644 --- a/Projects/UOContent/Items/Misc/Key.cs +++ b/Projects/UOContent/Items/Misc/Key.cs @@ -47,13 +47,13 @@ public partial class Key : Item public Key(KeyType type, uint val = 0, Item link = null) : base((int)type) { - Weight = 1.0; - _maxRange = 3; _keyValue = val; _link = link; } + public override double DefaultWeight => 1.0; + public static uint RandomValue() => (uint)(0xFFFFFFFE * Utility.RandomDouble()) + 1; public static void RemoveKeys(Mobile m, uint keyValue) diff --git a/Projects/UOContent/Items/Misc/KeyRing.cs b/Projects/UOContent/Items/Misc/KeyRing.cs index 2a3c39759..bae7d8d77 100644 --- a/Projects/UOContent/Items/Misc/KeyRing.cs +++ b/Projects/UOContent/Items/Misc/KeyRing.cs @@ -15,10 +15,11 @@ public partial class KeyRing : Item [Constructible] public KeyRing() : base(0x1011) { - Weight = 1.0; // They seem to have no weight on OSI ?! _keys = new List(); } + public override double DefaultWeight => 1.0; + public override bool OnDragDrop(Mobile from, Item dropped) { if (!IsChildOf(from.Backpack)) diff --git a/Projects/UOContent/Items/Misc/Labyrinth/MinotaurArtifact.cs b/Projects/UOContent/Items/Misc/Labyrinth/MinotaurArtifact.cs index 07b6824cf..f807a7d61 100644 --- a/Projects/UOContent/Items/Misc/Labyrinth/MinotaurArtifact.cs +++ b/Projects/UOContent/Items/Misc/Labyrinth/MinotaurArtifact.cs @@ -8,15 +8,10 @@ public partial class MinotaurArtifact : Item [Constructible] public MinotaurArtifact() : base(Utility.RandomList(0xB46, 0xB48, 0x9ED)) { - if (ItemID == 0x9ED) - { - Weight = 30; - } - LootType = LootType.Blessed; Hue = 0x100; } public override int LabelNumber => 1074826; // Minotaur Artifact - public override double DefaultWeight => 5.0; + public override double DefaultWeight => ItemID == 0x9ED ? 30.0 : 5.0; } diff --git a/Projects/UOContent/Items/Misc/Moonstone.cs b/Projects/UOContent/Items/Misc/Moonstone.cs index 0a9a90903..4d08947c8 100644 --- a/Projects/UOContent/Items/Misc/Moonstone.cs +++ b/Projects/UOContent/Items/Misc/Moonstone.cs @@ -20,11 +20,9 @@ public partial class Moonstone : Item private MoonstoneType _type; [Constructible] - public Moonstone(MoonstoneType type) : base(0xF8B) - { - Weight = 1.0; - _type = type; - } + public Moonstone(MoonstoneType type) : base(0xF8B) => _type = type; + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1041490 + (int)_type; diff --git a/Projects/UOContent/Items/Misc/PlayerBulletinBoards.cs b/Projects/UOContent/Items/Misc/PlayerBulletinBoards.cs index 1a0c3d3bc..88983c81b 100644 --- a/Projects/UOContent/Items/Misc/PlayerBulletinBoards.cs +++ b/Projects/UOContent/Items/Misc/PlayerBulletinBoards.cs @@ -15,7 +15,11 @@ namespace Server.Items; public partial class PlayerBBSouth : BasePlayerBB { [Constructible] - public PlayerBBSouth() : base(0x2311) => Weight = 15.0; + public PlayerBBSouth() : base(0x2311) + { + } + + public override double DefaultWeight => 15.0; public override int LabelNumber => 1062421; // bulletin board (south) } @@ -24,7 +28,11 @@ public partial class PlayerBBSouth : BasePlayerBB public partial class PlayerBBEast : BasePlayerBB { [Constructible] - public PlayerBBEast() : base(0x2312) => Weight = 15.0; + public PlayerBBEast() : base(0x2312) + { + } + + public override double DefaultWeight => 15.0; public override int LabelNumber => 1062420; // bulletin board (east) } diff --git a/Projects/UOContent/Items/Misc/PlayerVendorDeed.cs b/Projects/UOContent/Items/Misc/PlayerVendorDeed.cs index cf864bce6..fb5b0a4e9 100644 --- a/Projects/UOContent/Items/Misc/PlayerVendorDeed.cs +++ b/Projects/UOContent/Items/Misc/PlayerVendorDeed.cs @@ -8,7 +8,11 @@ namespace Server.Items; public partial class ContractOfEmployment : Item { [Constructible] - public ContractOfEmployment() : base(0x14F0) => Weight = 1.0; + public ContractOfEmployment() : base(0x14F0) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1041243; // a contract of employment diff --git a/Projects/UOContent/Items/Misc/PowerCrystal.cs b/Projects/UOContent/Items/Misc/PowerCrystal.cs index 7d43ed5af..650a512ee 100644 --- a/Projects/UOContent/Items/Misc/PowerCrystal.cs +++ b/Projects/UOContent/Items/Misc/PowerCrystal.cs @@ -6,7 +6,11 @@ namespace Server.Items; public partial class PowerCrystal : Item { [Constructible] - public PowerCrystal() : base(0x1F1C) => Weight = 1.0; + public PowerCrystal() : base(0x1F1C) + { + } + + public override double DefaultWeight => 1.0; public override string DefaultName => "power crystal"; diff --git a/Projects/UOContent/Items/Misc/PromotionalToken.cs b/Projects/UOContent/Items/Misc/PromotionalToken.cs index 90547b8e0..ac572a7da 100644 --- a/Projects/UOContent/Items/Misc/PromotionalToken.cs +++ b/Projects/UOContent/Items/Misc/PromotionalToken.cs @@ -11,9 +11,10 @@ public abstract partial class PromotionalToken : Item { LootType = LootType.Blessed; Light = LightType.Circle300; - Weight = 5.0; } + public override double DefaultWeight => 5.0; + public abstract TextDefinition ItemName { get; } public abstract TextDefinition ItemReceiveMessage { get; } public abstract TextDefinition ItemGumpName { get; } diff --git a/Projects/UOContent/Items/Misc/Rares.cs b/Projects/UOContent/Items/Misc/Rares.cs index 2a23a7cc7..699006a7c 100644 --- a/Projects/UOContent/Items/Misc/Rares.cs +++ b/Projects/UOContent/Items/Misc/Rares.cs @@ -9,9 +9,10 @@ public partial class Rope : Item public Rope(int amount = 1) : base(0x14F8) { Stackable = true; - Weight = 1.0; Amount = amount; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -21,9 +22,10 @@ public partial class IronWire : Item public IronWire(int amount = 1) : base(0x1876) { Stackable = true; - Weight = 5.0; Amount = amount; } + + public override double DefaultWeight => 5.0; } [SerializationGenerator(0, false)] @@ -33,9 +35,10 @@ public partial class SilverWire : Item public SilverWire(int amount = 1) : base(0x1877) { Stackable = true; - Weight = 5.0; Amount = amount; } + + public override double DefaultWeight => 5.0; } [SerializationGenerator(0, false)] @@ -45,9 +48,10 @@ public partial class GoldWire : Item public GoldWire(int amount = 1) : base(0x1878) { Stackable = true; - Weight = 5.0; Amount = amount; } + + public override double DefaultWeight => 5.0; } [SerializationGenerator(0, false)] @@ -57,9 +61,10 @@ public partial class CopperWire : Item public CopperWire(int amount = 1) : base(0x1879) { Stackable = true; - Weight = 5.0; Amount = amount; } + + public override double DefaultWeight => 5.0; } [SerializationGenerator(0, false)] @@ -69,9 +74,10 @@ public partial class WhiteDriedFlowers : Item public WhiteDriedFlowers(int amount = 1) : base(0xC3C) { Stackable = true; - Weight = 1.0; Amount = amount; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -81,9 +87,10 @@ public partial class GreenDriedFlowers : Item public GreenDriedFlowers(int amount = 1) : base(0xC3E) { Stackable = true; - Weight = 1.0; Amount = amount; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -93,9 +100,10 @@ public partial class DriedOnions : Item public DriedOnions(int amount = 1) : base(0xC40) { Stackable = true; - Weight = 1.0; Amount = amount; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -105,105 +113,162 @@ public partial class DriedHerbs : Item public DriedHerbs(int amount = 1) : base(0xC42) { Stackable = true; - Weight = 1.0; Amount = amount; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class HorseShoes : Item { [Constructible] - public HorseShoes() : base(0xFB6) => Weight = 3.0; + public HorseShoes() : base(0xFB6) + { + } + + public override double DefaultWeight => 3.0; } [SerializationGenerator(0, false)] public partial class ForgedMetal : Item { [Constructible] - public ForgedMetal() : base(0xFB8) => Weight = 5.0; + public ForgedMetal() : base(0xFB8) + { + } + + public override double DefaultWeight => 5.0; } [SerializationGenerator(0, false)] public partial class Whip : Item { [Constructible] - public Whip() : base(0x166E) => Weight = 1.0; + public Whip() : base(0x166E) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class PaintsAndBrush : Item { [Constructible] - public PaintsAndBrush() : base(0xFC1) => Weight = 1.0; + public PaintsAndBrush() : base(0xFC1) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class PenAndInk : Item { [Constructible] - public PenAndInk() : base(0xFBF) => Weight = 1.0; + public PenAndInk() : base(0xFBF) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0)] public partial class ChiselsNorth : Item { [Constructible] - public ChiselsNorth() : base(0x1026) => Weight = 1.0; + public ChiselsNorth() : base(0x1026) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0)] public partial class ChiselsWest : Item { [Constructible] - public ChiselsWest() : base(0x1027) => Weight = 1.0; + public ChiselsWest() : base(0x1027) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0)] public partial class DirtyPan : Item { [Constructible] - public DirtyPan() : base(0x9E8) => Weight = 1.0; + public DirtyPan() : base(0x9E8) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0)] public partial class DirtySmallRoundPot : Item { [Constructible] - public DirtySmallRoundPot() : base(0x9E7) => Weight = 1.0; + public DirtySmallRoundPot() : base(0x9E7) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0)] public partial class DirtyPot : Item { [Constructible] - public DirtyPot() : base(0x9E6) => Weight = 1.0; + public DirtyPot() : base(0x9E6) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0)] public partial class DirtyRoundPot : Item { [Constructible] - public DirtyRoundPot() : base(0x9DF) => Weight = 1.0; + public DirtyRoundPot() : base(0x9DF) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0)] public partial class DirtyFrypan : Item { [Constructible] - public DirtyFrypan() : base(0x9DE) => Weight = 1.0; + public DirtyFrypan() : base(0x9DE) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0)] public partial class DirtySmallPot : Item { [Constructible] - public DirtySmallPot() : base(0x9DD) => Weight = 1.0; + public DirtySmallPot() : base(0x9DD) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0)] public partial class DirtyKettle : Item { [Constructible] - public DirtyKettle() : base(0x9DC) => Weight = 1.0; + public DirtyKettle() : base(0x9DC) + { + } + + public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Misc/Scales.cs b/Projects/UOContent/Items/Misc/Scales.cs index 3fe4937df..bd2c4003e 100644 --- a/Projects/UOContent/Items/Misc/Scales.cs +++ b/Projects/UOContent/Items/Misc/Scales.cs @@ -7,7 +7,11 @@ namespace Server.Items; public partial class Scales : Item { [Constructible] - public Scales() : base(0x1852) => Weight = 4.0; + public Scales() : base(0x1852) + { + } + + public override double DefaultWeight => 4.0; public override void OnDoubleClick(Mobile from) { diff --git a/Projects/UOContent/Items/Misc/SpecialBeardDye.cs b/Projects/UOContent/Items/Misc/SpecialBeardDye.cs index ee402923f..7687a2870 100644 --- a/Projects/UOContent/Items/Misc/SpecialBeardDye.cs +++ b/Projects/UOContent/Items/Misc/SpecialBeardDye.cs @@ -9,11 +9,9 @@ namespace Server.Items; public partial class SpecialBeardDye : Item { [Constructible] - public SpecialBeardDye() : base(0xE26) - { - Weight = 1.0; - LootType = LootType.Newbied; - } + public SpecialBeardDye() : base(0xE26) => LootType = LootType.Newbied; + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1041087; // Special Beard Dye diff --git a/Projects/UOContent/Items/Misc/SpecialHairDye.cs b/Projects/UOContent/Items/Misc/SpecialHairDye.cs index 09a8289b6..cd1131111 100644 --- a/Projects/UOContent/Items/Misc/SpecialHairDye.cs +++ b/Projects/UOContent/Items/Misc/SpecialHairDye.cs @@ -9,11 +9,9 @@ namespace Server.Items; public partial class SpecialHairDye : Item { [Constructible] - public SpecialHairDye() : base(0xE26) - { - Weight = 1.0; - LootType = LootType.Newbied; - } + public SpecialHairDye() : base(0xE26) => LootType = LootType.Newbied; + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1074402; diff --git a/Projects/UOContent/Items/Misc/TribalBerry.cs b/Projects/UOContent/Items/Misc/TribalBerry.cs index 4d6f1eb8a..313bff659 100644 --- a/Projects/UOContent/Items/Misc/TribalBerry.cs +++ b/Projects/UOContent/Items/Misc/TribalBerry.cs @@ -8,11 +8,12 @@ public partial class TribalBerry : Item [Constructible] public TribalBerry(int amount = 1) : base(0x9D0) { - Weight = 1.0; Stackable = true; Amount = amount; Hue = 6; } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1040001; // tribal berry } diff --git a/Projects/UOContent/Items/Misc/TribalPaint.cs b/Projects/UOContent/Items/Misc/TribalPaint.cs index 8bad846bb..be4ef1ef0 100644 --- a/Projects/UOContent/Items/Misc/TribalPaint.cs +++ b/Projects/UOContent/Items/Misc/TribalPaint.cs @@ -16,10 +16,11 @@ public partial class TribalPaint : Item public TribalPaint() : base(0x9EC) { Hue = 2101; - Weight = 2.0; Stackable = Core.ML; } + public override double DefaultWeight => 2.0; + public override int LabelNumber => 1040000; // savage kin paint public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Items/PlantsFlowers/Potted/EmptyPots.cs b/Projects/UOContent/Items/PlantsFlowers/Potted/EmptyPots.cs index 14a6c6e87..45505a91e 100644 --- a/Projects/UOContent/Items/PlantsFlowers/Potted/EmptyPots.cs +++ b/Projects/UOContent/Items/PlantsFlowers/Potted/EmptyPots.cs @@ -6,12 +6,20 @@ namespace Server.Items; public partial class SmallEmptyPot : Item { [Constructible] - public SmallEmptyPot() : base(0x11C6) => Weight = 100; + public SmallEmptyPot() : base(0x11C6) + { + } + + public override double DefaultWeight => 100; } [SerializationGenerator(0, false)] public partial class LargeEmptyPot : Item { [Constructible] - public LargeEmptyPot() : base(0x11C7) => Weight = 6; + public LargeEmptyPot() : base(0x11C7) + { + } + + public override double DefaultWeight => 6; } diff --git a/Projects/UOContent/Items/PlantsFlowers/Potted/PottedCactus.cs b/Projects/UOContent/Items/PlantsFlowers/Potted/PottedCactus.cs index 4c0f9c91e..0143f6ac3 100644 --- a/Projects/UOContent/Items/PlantsFlowers/Potted/PottedCactus.cs +++ b/Projects/UOContent/Items/PlantsFlowers/Potted/PottedCactus.cs @@ -6,40 +6,64 @@ namespace Server.Items; public partial class PottedCactus : Item { [Constructible] - public PottedCactus() : base(0x1E0F) => Weight = 100; + public PottedCactus() : base(0x1E0F) + { + } + + public override double DefaultWeight => 100; } [SerializationGenerator(0, false)] public partial class PottedCactus1 : Item { [Constructible] - public PottedCactus1() : base(0x1E10) => Weight = 100; + public PottedCactus1() : base(0x1E10) + { + } + + public override double DefaultWeight => 100; } [SerializationGenerator(0, false)] public partial class PottedCactus2 : Item { [Constructible] - public PottedCactus2() : base(0x1E11) => Weight = 100; + public PottedCactus2() : base(0x1E11) + { + } + + public override double DefaultWeight => 100; } [SerializationGenerator(0, false)] public partial class PottedCactus3 : Item { [Constructible] - public PottedCactus3() : base(0x1E12) => Weight = 100; + public PottedCactus3() : base(0x1E12) + { + } + + public override double DefaultWeight => 100; } [SerializationGenerator(0, false)] public partial class PottedCactus4 : Item { [Constructible] - public PottedCactus4() : base(0x1E13) => Weight = 100; + public PottedCactus4() : base(0x1E13) + { + } + + public override double DefaultWeight => 100; } [SerializationGenerator(0, false)] public partial class PottedCactus5 : Item { [Constructible] - public PottedCactus5() : base(0x1E14) => Weight = 100; + public PottedCactus5() : base(0x1E14) + { + } + + public override double DefaultWeight => 100; } diff --git a/Projects/UOContent/Items/PlantsFlowers/Potted/PottedPlants.cs b/Projects/UOContent/Items/PlantsFlowers/Potted/PottedPlants.cs index b6d98773d..503d5b310 100644 --- a/Projects/UOContent/Items/PlantsFlowers/Potted/PottedPlants.cs +++ b/Projects/UOContent/Items/PlantsFlowers/Potted/PottedPlants.cs @@ -6,19 +6,31 @@ namespace Server.Items; public partial class PottedPlant : Item { [Constructible] - public PottedPlant() : base(0x11CA) => Weight = 100; + public PottedPlant() : base(0x11CA) + { + } + + public override double DefaultWeight => 100; } [SerializationGenerator(0, false)] public partial class PottedPlant1 : Item { [Constructible] - public PottedPlant1() : base(0x11CB) => Weight = 100; + public PottedPlant1() : base(0x11CB) + { + } + + public override double DefaultWeight => 100; } [SerializationGenerator(0, false)] public partial class PottedPlant2 : Item { [Constructible] - public PottedPlant2() : base(0x11CC) => Weight = 100; + public PottedPlant2() : base(0x11CC) + { + } + + public override double DefaultWeight => 100; } diff --git a/Projects/UOContent/Items/PlantsFlowers/Potted/PottedTrees.cs b/Projects/UOContent/Items/PlantsFlowers/Potted/PottedTrees.cs index b04240614..916fa6f51 100644 --- a/Projects/UOContent/Items/PlantsFlowers/Potted/PottedTrees.cs +++ b/Projects/UOContent/Items/PlantsFlowers/Potted/PottedTrees.cs @@ -6,12 +6,20 @@ namespace Server.Items; public partial class PottedTree : Item { [Constructible] - public PottedTree() : base(0x11C8) => Weight = 100; + public PottedTree() : base(0x11C8) + { + } + + public override double DefaultWeight => 100; } [SerializationGenerator(0, false)] public partial class PottedTree1 : Item { [Constructible] - public PottedTree1() : base(0x11C9) => Weight = 100; + public PottedTree1() : base(0x11C9) + { + } + + public override double DefaultWeight => 100; } diff --git a/Projects/UOContent/Items/Quivers/BaseQuiver.cs b/Projects/UOContent/Items/Quivers/BaseQuiver.cs index dfdc6c5f8..e392ffdcb 100644 --- a/Projects/UOContent/Items/Quivers/BaseQuiver.cs +++ b/Projects/UOContent/Items/Quivers/BaseQuiver.cs @@ -73,7 +73,6 @@ public partial class BaseQuiver : Container, ICraftable, IAosItem public BaseQuiver(int itemID = 0x2FB7) : base(itemID) { - Weight = 2.0; Capacity = 500; Layer = Layer.Cloak; diff --git a/Projects/UOContent/Items/Resources/Fishing/Fish.cs b/Projects/UOContent/Items/Resources/Fishing/Fish.cs index 49f93eedd..d7e8e4498 100644 --- a/Projects/UOContent/Items/Resources/Fishing/Fish.cs +++ b/Projects/UOContent/Items/Resources/Fishing/Fish.cs @@ -9,12 +9,13 @@ public partial class Fish : Item, ICarvable public Fish(int amount = 1) : base(Utility.Random(0x09CC, 4)) { Stackable = true; - Weight = 1.0; Amount = amount; } + public override double DefaultWeight => 1.0; + public void Carve(Mobile from, Item item) { ScissorHelper(from, new RawFishSteak(), 4); } -} \ No newline at end of file +} diff --git a/Projects/UOContent/Items/Resources/Tailor/BoltOfCloth.cs b/Projects/UOContent/Items/Resources/Tailor/BoltOfCloth.cs index 6d3638145..685996cba 100644 --- a/Projects/UOContent/Items/Resources/Tailor/BoltOfCloth.cs +++ b/Projects/UOContent/Items/Resources/Tailor/BoltOfCloth.cs @@ -11,10 +11,11 @@ public partial class BoltOfCloth : Item, IScissorable, IDyable, ICommodity public BoltOfCloth(int amount = 1) : base(0xF95) { Stackable = true; - Weight = 5.0; Amount = amount; } + public override double DefaultWeight => 5.0; + int ICommodity.DescriptionNumber => LabelNumber; bool ICommodity.IsDeedable => true; @@ -48,4 +49,4 @@ public partial class BoltOfCloth : Item, IScissorable, IDyable, ICommodity var amount = (Amount * 50).ToString(); from.NetState.SendMessageLocalized(Serial, ItemID, MessageType.Label, 0x3B2, 3, number, "", amount); } -} \ No newline at end of file +} diff --git a/Projects/UOContent/Items/Resources/Tailor/Bone.cs b/Projects/UOContent/Items/Resources/Tailor/Bone.cs index 590b71a37..b2cb47788 100644 --- a/Projects/UOContent/Items/Resources/Tailor/Bone.cs +++ b/Projects/UOContent/Items/Resources/Tailor/Bone.cs @@ -10,9 +10,10 @@ public partial class Bone : Item, ICommodity { Stackable = true; Amount = amount; - Weight = 1.0; } + public override double DefaultWeight => 1.0; + int ICommodity.DescriptionNumber => LabelNumber; bool ICommodity.IsDeedable => true; -} \ No newline at end of file +} diff --git a/Projects/UOContent/Items/Resources/Tailor/Cotton.cs b/Projects/UOContent/Items/Resources/Tailor/Cotton.cs index ea6d9da29..9d8ce0ef6 100644 --- a/Projects/UOContent/Items/Resources/Tailor/Cotton.cs +++ b/Projects/UOContent/Items/Resources/Tailor/Cotton.cs @@ -10,10 +10,11 @@ public partial class Cotton : Item, IDyable public Cotton(int amount = 1) : base(0xDF9) { Stackable = true; - Weight = 4.0; Amount = amount; } + public override double DefaultWeight => 4.0; + public bool Dye(Mobile from, DyeTub sender) { if (Deleted) diff --git a/Projects/UOContent/Items/Resources/Tailor/Hides.cs b/Projects/UOContent/Items/Resources/Tailor/Hides.cs index f9a6014c9..df6423fb2 100644 --- a/Projects/UOContent/Items/Resources/Tailor/Hides.cs +++ b/Projects/UOContent/Items/Resources/Tailor/Hides.cs @@ -8,13 +8,13 @@ public abstract partial class BaseHides : Item, ICommodity public BaseHides(CraftResource resource, int amount = 1) : base(0x1079) { Stackable = true; - Weight = 5.0; Amount = amount; Hue = CraftResources.GetHue(resource); - _resource = resource; } + public override double DefaultWeight => 5.0; + [SerializableProperty(0)] [CommandProperty(AccessLevel.GameMaster)] public CraftResource Resource diff --git a/Projects/UOContent/Items/Resources/Tailor/Leathers.cs b/Projects/UOContent/Items/Resources/Tailor/Leathers.cs index 86d1861ec..173e2dae0 100644 --- a/Projects/UOContent/Items/Resources/Tailor/Leathers.cs +++ b/Projects/UOContent/Items/Resources/Tailor/Leathers.cs @@ -8,13 +8,13 @@ public abstract partial class BaseLeather : Item, ICommodity public BaseLeather(CraftResource resource, int amount = 1) : base(0x1081) { Stackable = true; - Weight = 1.0; Amount = amount; Hue = CraftResources.GetHue(resource); - _resource = resource; } + public override double DefaultWeight => 1.0; + [SerializableProperty(0)] [CommandProperty(AccessLevel.GameMaster)] public CraftResource Resource diff --git a/Projects/UOContent/Items/Resources/Tailor/Wool.cs b/Projects/UOContent/Items/Resources/Tailor/Wool.cs index 5320cbd8d..2ef0ddbf7 100644 --- a/Projects/UOContent/Items/Resources/Tailor/Wool.cs +++ b/Projects/UOContent/Items/Resources/Tailor/Wool.cs @@ -10,10 +10,11 @@ public partial class Wool : Item, IDyable public Wool(int amount = 1) : base(0xDF8) { Stackable = true; - Weight = 4.0; Amount = amount; } + public override double DefaultWeight => 4.0; + public bool Dye(Mobile from, DyeTub sender) { if (Deleted) @@ -99,10 +100,11 @@ public partial class TaintedWool : Wool public TaintedWool(int amount = 1) : base(0x101F) { Stackable = true; - Weight = 4.0; Amount = amount; } + public override double DefaultWeight => 4.0; + public override void OnSpun(ISpinningWheel wheel, Mobile from, int hue) { from.AddToBackpack(new DarkYarn diff --git a/Projects/UOContent/Items/Resources/Tailor/YarnsAndThreads.cs b/Projects/UOContent/Items/Resources/Tailor/YarnsAndThreads.cs index abce822ec..ea5265b6d 100644 --- a/Projects/UOContent/Items/Resources/Tailor/YarnsAndThreads.cs +++ b/Projects/UOContent/Items/Resources/Tailor/YarnsAndThreads.cs @@ -9,10 +9,11 @@ public abstract partial class BaseClothMaterial : Item, IDyable public BaseClothMaterial(int itemID, int amount = 1) : base(itemID) { Stackable = true; - Weight = 1.0; Amount = amount; } + public override double DefaultWeight => 1.0; + public bool Dye(Mobile from, DyeTub sender) { if (Deleted) diff --git a/Projects/UOContent/Items/Shields/BronzeShield.cs b/Projects/UOContent/Items/Shields/BronzeShield.cs index 8261d28c0..a13442682 100644 --- a/Projects/UOContent/Items/Shields/BronzeShield.cs +++ b/Projects/UOContent/Items/Shields/BronzeShield.cs @@ -6,7 +6,11 @@ namespace Server.Items; public partial class BronzeShield : BaseShield { [Constructible] - public BronzeShield() : base(0x1B72) => Weight = 6.0; + public BronzeShield() : base(0x1B72) + { + } + + public override double DefaultWeight => 6.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 0; diff --git a/Projects/UOContent/Items/Shields/Buckler.cs b/Projects/UOContent/Items/Shields/Buckler.cs index ea720c0b8..df3da2861 100644 --- a/Projects/UOContent/Items/Shields/Buckler.cs +++ b/Projects/UOContent/Items/Shields/Buckler.cs @@ -6,7 +6,11 @@ namespace Server.Items; public partial class Buckler : BaseShield { [Constructible] - public Buckler() : base(0x1B73) => Weight = 5.0; + public Buckler() : base(0x1B73) + { + } + + public override double DefaultWeight => 5.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 0; diff --git a/Projects/UOContent/Items/Shields/ChaosShield.cs b/Projects/UOContent/Items/Shields/ChaosShield.cs index dd7707c95..7878dc25b 100644 --- a/Projects/UOContent/Items/Shields/ChaosShield.cs +++ b/Projects/UOContent/Items/Shields/ChaosShield.cs @@ -13,10 +13,10 @@ public partial class ChaosShield : BaseShield { LootType = LootType.Newbied; } - - Weight = 5.0; } + public override double DefaultWeight => 4.0; + public override int BasePhysicalResistance => 1; public override int BaseFireResistance => 0; public override int BaseColdResistance => 0; diff --git a/Projects/UOContent/Items/Shields/GargishWoodenShield.cs b/Projects/UOContent/Items/Shields/GargishWoodenShield.cs index 5bdf1157c..cc4b3d038 100644 --- a/Projects/UOContent/Items/Shields/GargishWoodenShield.cs +++ b/Projects/UOContent/Items/Shields/GargishWoodenShield.cs @@ -7,7 +7,11 @@ namespace Server.Items; public partial class GargishWoodenShield : BaseShield { [Constructible] - public GargishWoodenShield() : base(0x4200) => Weight = 5.0; + public GargishWoodenShield() : base(0x4200) + { + } + + public override double DefaultWeight => 5.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 0; diff --git a/Projects/UOContent/Items/Shields/HeaterShield.cs b/Projects/UOContent/Items/Shields/HeaterShield.cs index 40dccdeb3..3e17ec22a 100644 --- a/Projects/UOContent/Items/Shields/HeaterShield.cs +++ b/Projects/UOContent/Items/Shields/HeaterShield.cs @@ -6,7 +6,11 @@ namespace Server.Items; public partial class HeaterShield : BaseShield { [Constructible] - public HeaterShield() : base(0x1B76) => Weight = 8.0; + public HeaterShield() : base(0x1B76) + { + } + + public override double DefaultWeight => 8.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 1; diff --git a/Projects/UOContent/Items/Shields/MetalKiteShield.cs b/Projects/UOContent/Items/Shields/MetalKiteShield.cs index 855b9ecff..ef46ddcfa 100644 --- a/Projects/UOContent/Items/Shields/MetalKiteShield.cs +++ b/Projects/UOContent/Items/Shields/MetalKiteShield.cs @@ -6,7 +6,11 @@ namespace Server.Items; public partial class MetalKiteShield : BaseShield, IDyable { [Constructible] - public MetalKiteShield() : base(0x1B74) => Weight = 7.0; + public MetalKiteShield() : base(0x1B74) + { + } + + public override double DefaultWeight => 7.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 0; diff --git a/Projects/UOContent/Items/Shields/MetalShield.cs b/Projects/UOContent/Items/Shields/MetalShield.cs index 5c9a5a148..6b4a8001e 100644 --- a/Projects/UOContent/Items/Shields/MetalShield.cs +++ b/Projects/UOContent/Items/Shields/MetalShield.cs @@ -6,7 +6,11 @@ namespace Server.Items; public partial class MetalShield : BaseShield { [Constructible] - public MetalShield() : base(0x1B7B) => Weight = 6.0; + public MetalShield() : base(0x1B7B) + { + } + + public override double DefaultWeight => 6.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 1; diff --git a/Projects/UOContent/Items/Shields/OrderShield.cs b/Projects/UOContent/Items/Shields/OrderShield.cs index 04ba88909..a85a5e6a0 100644 --- a/Projects/UOContent/Items/Shields/OrderShield.cs +++ b/Projects/UOContent/Items/Shields/OrderShield.cs @@ -13,10 +13,10 @@ public partial class OrderShield : BaseShield { LootType = LootType.Newbied; } - - Weight = 7.0; } + public override double DefaultWeight => 7.0; + public override int BasePhysicalResistance => 1; public override int BaseFireResistance => 0; public override int BaseColdResistance => 0; diff --git a/Projects/UOContent/Items/Shields/WoodenKiteShield.cs b/Projects/UOContent/Items/Shields/WoodenKiteShield.cs index 3a8874839..fbbea1213 100644 --- a/Projects/UOContent/Items/Shields/WoodenKiteShield.cs +++ b/Projects/UOContent/Items/Shields/WoodenKiteShield.cs @@ -6,7 +6,11 @@ namespace Server.Items; public partial class WoodenKiteShield : BaseShield { [Constructible] - public WoodenKiteShield() : base(0x1B79) => Weight = 5.0; + public WoodenKiteShield() : base(0x1B79) + { + } + + public override double DefaultWeight => 5.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 0; diff --git a/Projects/UOContent/Items/Shields/WoodenShield.cs b/Projects/UOContent/Items/Shields/WoodenShield.cs index 46b78874d..013f908be 100644 --- a/Projects/UOContent/Items/Shields/WoodenShield.cs +++ b/Projects/UOContent/Items/Shields/WoodenShield.cs @@ -6,7 +6,11 @@ namespace Server.Items; public partial class WoodenShield : BaseShield { [Constructible] - public WoodenShield() : base(0x1B7A) => Weight = 5.0; + public WoodenShield() : base(0x1B7A) + { + } + + public override double DefaultWeight => 5.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 0; diff --git a/Projects/UOContent/Items/Skill Items/Camping/Bedroll.cs b/Projects/UOContent/Items/Skill Items/Camping/Bedroll.cs index 33aebc3ce..0b3ecca0b 100644 --- a/Projects/UOContent/Items/Skill Items/Camping/Bedroll.cs +++ b/Projects/UOContent/Items/Skill Items/Camping/Bedroll.cs @@ -11,7 +11,11 @@ namespace Server.Items; public partial class Bedroll : Item { [Constructible] - public Bedroll() : base(0xA57) => Weight = 5.0; + public Bedroll() : base(0xA57) + { + } + + public override double DefaultWeight => 5.0; public override void OnDoubleClick(Mobile from) { diff --git a/Projects/UOContent/Items/Skill Items/Camping/Kindling.cs b/Projects/UOContent/Items/Skill Items/Camping/Kindling.cs index 348cdbb6d..aef7d0acf 100644 --- a/Projects/UOContent/Items/Skill Items/Camping/Kindling.cs +++ b/Projects/UOContent/Items/Skill Items/Camping/Kindling.cs @@ -11,10 +11,11 @@ public partial class Kindling : Item public Kindling(int amount = 1) : base(0xDE1) { Stackable = true; - Weight = 5.0; Amount = amount; } + public override double DefaultWeight => 5.0; + public override void OnDoubleClick(Mobile from) { if (!VerifyMove(from)) diff --git a/Projects/UOContent/Items/Skill Items/Carpenter Items/TaxidermyKit.cs b/Projects/UOContent/Items/Skill Items/Carpenter Items/TaxidermyKit.cs index c33cf6980..8915c06f1 100644 --- a/Projects/UOContent/Items/Skill Items/Carpenter Items/TaxidermyKit.cs +++ b/Projects/UOContent/Items/Skill Items/Carpenter Items/TaxidermyKit.cs @@ -22,7 +22,11 @@ public partial class TaxidermyKit : Item }; [Constructible] - public TaxidermyKit() : base(0x1EBA) => Weight = 1.0; + public TaxidermyKit() : base(0x1EBA) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1041279; // a taxidermy kit diff --git a/Projects/UOContent/Items/Skill Items/Fishing/FishingPole.cs b/Projects/UOContent/Items/Skill Items/Fishing/FishingPole.cs index 3cecb25d2..3914f28ff 100644 --- a/Projects/UOContent/Items/Skill Items/Fishing/FishingPole.cs +++ b/Projects/UOContent/Items/Skill Items/Fishing/FishingPole.cs @@ -9,11 +9,9 @@ namespace Server.Items; public partial class FishingPole : Item { [Constructible] - public FishingPole() : base(0x0DC0) - { - Layer = Layer.TwoHanded; - Weight = 8.0; - } + public FishingPole() : base(0x0DC0) => Layer = Layer.TwoHanded; + + public override double DefaultWeight => 8.0; public override void OnDoubleClick(Mobile from) { diff --git a/Projects/UOContent/Items/Skill Items/Fishing/Misc/MessageInABottle.cs b/Projects/UOContent/Items/Skill Items/Fishing/Misc/MessageInABottle.cs index 8f5603a5c..5d6062099 100644 --- a/Projects/UOContent/Items/Skill Items/Fishing/Misc/MessageInABottle.cs +++ b/Projects/UOContent/Items/Skill Items/Fishing/Misc/MessageInABottle.cs @@ -17,11 +17,12 @@ public partial class MessageInABottle : Item [Constructible] public MessageInABottle(Map map, int level) : base(0x099F) { - Weight = 1.0; TargetMap = map ?? Map.Trammel; _level = level; } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1041080; // a message in a bottle [SerializableProperty(0)] diff --git a/Projects/UOContent/Items/Skill Items/Fishing/Misc/SOS.cs b/Projects/UOContent/Items/Skill Items/Fishing/Misc/SOS.cs index 0ae8f7956..e5e97ab3e 100644 --- a/Projects/UOContent/Items/Skill Items/Fishing/Misc/SOS.cs +++ b/Projects/UOContent/Items/Skill Items/Fishing/Misc/SOS.cs @@ -77,8 +77,6 @@ public partial class SOS : Item [Constructible] public SOS(Map map, int level) : base(0x14EE) { - Weight = 1.0; - _level = level; MessageIndex = Utility.Random(MessageEntries.Length); TargetMap = map ?? Map.Trammel; @@ -87,6 +85,8 @@ public partial class SOS : Item UpdateHue(); } + public override double DefaultWeight => 1.0; + public override int LabelNumber { get diff --git a/Projects/UOContent/Items/Skill Items/Fishing/Misc/Sextant.cs b/Projects/UOContent/Items/Skill Items/Fishing/Misc/Sextant.cs index a9a7d4060..4b17d808f 100644 --- a/Projects/UOContent/Items/Skill Items/Fishing/Misc/Sextant.cs +++ b/Projects/UOContent/Items/Skill Items/Fishing/Misc/Sextant.cs @@ -6,7 +6,11 @@ namespace Server.Items; public partial class Sextant : Item { [Constructible] - public Sextant() : base(0x1058) => Weight = 2.0; + public Sextant() : base(0x1058) + { + } + + public override double DefaultWeight => 2.0; public override void OnDoubleClick(Mobile from) { diff --git a/Projects/UOContent/Items/Skill Items/Fishing/Misc/ShipwreckedItem.cs b/Projects/UOContent/Items/Skill Items/Fishing/Misc/ShipwreckedItem.cs index d812f761a..962b8752d 100644 --- a/Projects/UOContent/Items/Skill Items/Fishing/Misc/ShipwreckedItem.cs +++ b/Projects/UOContent/Items/Skill Items/Fishing/Misc/ShipwreckedItem.cs @@ -12,16 +12,10 @@ public partial class ShipwreckedItem : Item, IDyable, IShipwreckedItem { public ShipwreckedItem(int itemID) : base(itemID) { - var weight = ItemData.Weight; - - if (weight >= 255) - { - weight = 1; - } - - Weight = weight; } + public override double DefaultWeight => ItemData.Weight >= 255 ? 1.0 : ItemData.Weight; + public bool Dye(Mobile from, DyeTub sender) { if (Deleted) diff --git a/Projects/UOContent/Items/Skill Items/Fishing/Misc/SpecialFishingNet.cs b/Projects/UOContent/Items/Skill Items/Fishing/Misc/SpecialFishingNet.cs index 98530468e..7017af81f 100644 --- a/Projects/UOContent/Items/Skill Items/Fishing/Misc/SpecialFishingNet.cs +++ b/Projects/UOContent/Items/Skill Items/Fishing/Misc/SpecialFishingNet.cs @@ -44,11 +44,9 @@ public partial class SpecialFishingNet : Item private bool _inUse; [Constructible] - public SpecialFishingNet() : base(0x0DCA) - { - Weight = 1.0; - Hue = Utility.RandomDouble() < 0.01 ? _hues.RandomElement() : 0x8A0; - } + public SpecialFishingNet() : base(0x0DCA) => Hue = Utility.RandomDouble() < 0.01 ? _hues.RandomElement() : 0x8A0; + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1041079; // a special fishing net diff --git a/Projects/UOContent/Items/Skill Items/Harvest Tools/GargoylesPickaxe.cs b/Projects/UOContent/Items/Skill Items/Harvest Tools/GargoylesPickaxe.cs index 52335a4d1..1cf680952 100644 --- a/Projects/UOContent/Items/Skill Items/Harvest Tools/GargoylesPickaxe.cs +++ b/Projects/UOContent/Items/Skill Items/Harvest Tools/GargoylesPickaxe.cs @@ -14,11 +14,12 @@ public partial class GargoylesPickaxe : BaseAxe, IUsesRemaining [Constructible] public GargoylesPickaxe(int uses) : base(0xE85 + Utility.Random(2)) { - Weight = 11.0; UsesRemaining = uses; ShowUsesRemaining = true; } + public override double DefaultWeight => 11.0; + public override int LabelNumber => 1041281; // a gargoyle's pickaxe public override HarvestSystem HarvestSystem => Mining.System; diff --git a/Projects/UOContent/Items/Skill Items/Harvest Tools/ProspectorsTool.cs b/Projects/UOContent/Items/Skill Items/Harvest Tools/ProspectorsTool.cs index c2372223b..a5c770206 100644 --- a/Projects/UOContent/Items/Skill Items/Harvest Tools/ProspectorsTool.cs +++ b/Projects/UOContent/Items/Skill Items/Harvest Tools/ProspectorsTool.cs @@ -14,11 +14,9 @@ public partial class ProspectorsTool : BaseBashing, IUsesRemaining private int _usesRemaining; [Constructible] - public ProspectorsTool() : base(0xFB4) - { - Weight = 9.0; - UsesRemaining = 50; - } + public ProspectorsTool() : base(0xFB4) => UsesRemaining = 50; + + public override double DefaultWeight => 9.0; public override int LabelNumber => 1049065; // prospector's tool diff --git a/Projects/UOContent/Items/Skill Items/Harvest Tools/Shovel.cs b/Projects/UOContent/Items/Skill Items/Harvest Tools/Shovel.cs index 73cb310bd..c2fc94199 100644 --- a/Projects/UOContent/Items/Skill Items/Harvest Tools/Shovel.cs +++ b/Projects/UOContent/Items/Skill Items/Harvest Tools/Shovel.cs @@ -7,7 +7,11 @@ namespace Server.Items; public partial class Shovel : BaseHarvestTool { [Constructible] - public Shovel(int uses = 50) : base(0xF39, uses) => Weight = 5.0; + public Shovel(int uses = 50) : base(0xF39, uses) + { + } + + public override double DefaultWeight => 5.0; public override HarvestSystem HarvestSystem => Mining.System; } diff --git a/Projects/UOContent/Items/Skill Items/Harvest Tools/SturdyPickaxe.cs b/Projects/UOContent/Items/Skill Items/Harvest Tools/SturdyPickaxe.cs index 7b8424940..d3d42ef41 100644 --- a/Projects/UOContent/Items/Skill Items/Harvest Tools/SturdyPickaxe.cs +++ b/Projects/UOContent/Items/Skill Items/Harvest Tools/SturdyPickaxe.cs @@ -9,12 +9,13 @@ public partial class SturdyPickaxe : BaseAxe, IUsesRemaining [Constructible] public SturdyPickaxe(int uses = 180) : base(0xE86) { - Weight = 11.0; Hue = 0x973; UsesRemaining = uses; ShowUsesRemaining = true; } + public override double DefaultWeight => 11.0; + public override int LabelNumber => 1045126; // sturdy pickaxe public override HarvestSystem HarvestSystem => Mining.System; diff --git a/Projects/UOContent/Items/Skill Items/Harvest Tools/SturdyShovel.cs b/Projects/UOContent/Items/Skill Items/Harvest Tools/SturdyShovel.cs index 94c149ea9..acf576f22 100644 --- a/Projects/UOContent/Items/Skill Items/Harvest Tools/SturdyShovel.cs +++ b/Projects/UOContent/Items/Skill Items/Harvest Tools/SturdyShovel.cs @@ -7,12 +7,9 @@ namespace Server.Items; public partial class SturdyShovel : BaseHarvestTool { [Constructible] - public SturdyShovel(int uses = 180) : base(0xF39, uses) - { - Weight = 5.0; - Hue = 0x973; - } + public SturdyShovel(int uses = 180) : base(0xF39, uses) => Hue = 0x973; + public override double DefaultWeight => 5.0; public override int LabelNumber => 1045125; // sturdy shovel public override HarvestSystem HarvestSystem => Mining.System; } diff --git a/Projects/UOContent/Items/Skill Items/Lumberjack/Log.cs b/Projects/UOContent/Items/Skill Items/Lumberjack/Log.cs index 5b372abd5..2873957d5 100644 --- a/Projects/UOContent/Items/Skill Items/Lumberjack/Log.cs +++ b/Projects/UOContent/Items/Skill Items/Lumberjack/Log.cs @@ -20,13 +20,14 @@ public partial class Log : Item, ICommodity, IAxe public Log(CraftResource resource, int amount) : base(0x1BDD) { Stackable = true; - Weight = 2.0; Amount = amount; _resource = resource; Hue = CraftResources.GetHue(resource); } + public override double DefaultWeight => 2.0; + [SerializableProperty(0)] [CommandProperty(AccessLevel.GameMaster)] public CraftResource Resource diff --git a/Projects/UOContent/Items/Skill Items/Magical/Misc/BlankScroll.cs b/Projects/UOContent/Items/Skill Items/Magical/Misc/BlankScroll.cs index f5e0117cb..7f5876308 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Misc/BlankScroll.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Misc/BlankScroll.cs @@ -9,10 +9,11 @@ public partial class BlankScroll : Item, ICommodity public BlankScroll(int amount = 1) : base(0xEF3) { Stackable = true; - Weight = 1.0; Amount = amount; } + public override double DefaultWeight => 1.0; + int ICommodity.DescriptionNumber => LabelNumber; bool ICommodity.IsDeedable => Core.ML; } diff --git a/Projects/UOContent/Items/Skill Items/Magical/Misc/Bottle.cs b/Projects/UOContent/Items/Skill Items/Magical/Misc/Bottle.cs index 7a7ccbd30..d12dc873b 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Misc/Bottle.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Misc/Bottle.cs @@ -9,10 +9,11 @@ public partial class Bottle : Item, ICommodity public Bottle(int amount = 1) : base(0xF0E) { Stackable = true; - Weight = 1.0; Amount = amount; } + public override double DefaultWeight => 1.0; + int ICommodity.DescriptionNumber => LabelNumber; bool ICommodity.IsDeedable => Core.ML; } diff --git a/Projects/UOContent/Items/Skill Items/Magical/Misc/PotionKeg.cs b/Projects/UOContent/Items/Skill Items/Magical/Misc/PotionKeg.cs index 1a6239322..221984a5f 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Misc/PotionKeg.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Misc/PotionKeg.cs @@ -16,7 +16,11 @@ public partial class PotionKeg : Item private PotionEffect _type; [Constructible] - public PotionKeg() : base(0x1940) => UpdateWeight(); + public PotionKeg() : base(0x1940) + { + } + + public override double DefaultWeight => 20 + Math.Clamp(_held, 0, 100) * 0.8; [SerializableProperty(1)] [CommandProperty(AccessLevel.GameMaster)] @@ -28,7 +32,6 @@ public partial class PotionKeg : Item if (_held != value) { _held = value; - UpdateWeight(); InvalidateProperties(); this.MarkDirty(); } @@ -48,13 +51,6 @@ public partial class PotionKeg : Item } } - public virtual void UpdateWeight() - { - var held = Math.Max(0, Math.Min(_held, 100)); - - Weight = 20 + held * 80 / 100; - } - private void Deserialize(IGenericReader reader, int version) { _type = (PotionEffect)reader.ReadInt(); diff --git a/Projects/UOContent/Items/Skill Items/Magical/Misc/RecallRune.cs b/Projects/UOContent/Items/Skill Items/Magical/Misc/RecallRune.cs index d33d5ce7a..68b6f324a 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Misc/RecallRune.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Misc/RecallRune.cs @@ -21,10 +21,11 @@ public partial class RecallRune : Item [Constructible] public RecallRune() : base(0x1F14) { - Weight = 1.0; CalculateHue(); } + public override double DefaultWeight => 1.0; + [SerializableProperty(0)] [CommandProperty(AccessLevel.Counselor, AccessLevel.GameMaster)] public BaseHouse House diff --git a/Projects/UOContent/Items/Skill Items/Magical/Potions/BasePotion.cs b/Projects/UOContent/Items/Skill Items/Magical/Potions/BasePotion.cs index 4c62b3771..ea39645a7 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Potions/BasePotion.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Potions/BasePotion.cs @@ -50,9 +50,10 @@ public abstract partial class BasePotion : Item, ICraftable, ICommodity _potionEffect = effect; Stackable = Core.ML; - Weight = 1.0; } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1041314 + (int)_potionEffect; public virtual bool RequireFreeHand => true; diff --git a/Projects/UOContent/Items/Skill Items/Magical/Runebook.cs b/Projects/UOContent/Items/Skill Items/Magical/Runebook.cs index a56389a03..0a5a0f308 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Runebook.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Runebook.cs @@ -60,7 +60,6 @@ public partial class Runebook : Item, ISecurable, ICraftable [Constructible] public Runebook(int maxCharges) : base(Core.AOS ? 0x22C5 : 0xEFA) { - Weight = Core.SE ? 1.0 : 3.0; LootType = LootType.Blessed; Hue = 0x461; @@ -72,6 +71,8 @@ public partial class Runebook : Item, ISecurable, ICraftable _level = SecureLevel.CoOwners; } + public override double DefaultWeight => Core.SE ? 1.0 : 3.0; + [CommandProperty(AccessLevel.GameMaster)] public DateTime NextUse { get; set; } diff --git a/Projects/UOContent/Items/Skill Items/Magical/Scrolls/SpellScroll.cs b/Projects/UOContent/Items/Skill Items/Magical/Scrolls/SpellScroll.cs index 097a0a22d..818864edd 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Scrolls/SpellScroll.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Scrolls/SpellScroll.cs @@ -16,12 +16,13 @@ public partial class SpellScroll : Item, ICommodity public SpellScroll(int spellID, int itemID, int amount = 1) : base(itemID) { Stackable = true; - Weight = 1.0; Amount = amount; _spellID = spellID; } + public override double DefaultWeight => 1.0; + int ICommodity.DescriptionNumber => LabelNumber; bool ICommodity.IsDeedable => Core.ML; diff --git a/Projects/UOContent/Items/Skill Items/Magical/Spellbook.cs b/Projects/UOContent/Items/Skill Items/Magical/Spellbook.cs index 901a9d82e..cdd60bd51 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Spellbook.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Spellbook.cs @@ -116,7 +116,6 @@ public partial class Spellbook : Item, ICraftable, ISlayer, IAosItem _attributes = new AosAttributes(this); _skillBonuses = new AosSkillBonuses(this); - Weight = 3.0; Layer = Layer.OneHanded; LootType = LootType.Blessed; @@ -124,6 +123,8 @@ public partial class Spellbook : Item, ICraftable, ISlayer, IAosItem Content = content; } + public override double DefaultWeight => 3.0; + public override bool DisplayWeight => false; public virtual SpellbookType SpellbookType => SpellbookType.Regular; @@ -496,7 +497,7 @@ public partial class Spellbook : Item, ICraftable, ISlayer, IAosItem public override bool OnDragDrop(Mobile from, Item dropped) { - if (dropped is not SpellScroll { Amount: 1 } scroll) + if (dropped is not SpellScroll scroll) { return false; } @@ -523,10 +524,10 @@ public partial class Spellbook : Item, ICraftable, ISlayer, IAosItem InvalidateProperties(); - scroll.Delete(); + scroll.Consume(); from.SendSound(0x249, GetWorldLocation()); - return true; + return scroll.Deleted; } return false; diff --git a/Projects/UOContent/Items/Skill Items/Misc/FireHorn.cs b/Projects/UOContent/Items/Skill Items/Misc/FireHorn.cs index 68d293405..b2c6f0c6e 100644 --- a/Projects/UOContent/Items/Skill Items/Misc/FireHorn.cs +++ b/Projects/UOContent/Items/Skill Items/Misc/FireHorn.cs @@ -10,11 +10,9 @@ namespace Server.Items; public partial class FireHorn : Item { [Constructible] - public FireHorn() : base(0xFC7) - { - Hue = 0x466; - Weight = 1.0; - } + public FireHorn() : base(0xFC7) => Hue = 0x466; + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1060456; // fire horn diff --git a/Projects/UOContent/Items/Skill Items/Musical Instruments/BambooFlute.cs b/Projects/UOContent/Items/Skill Items/Musical Instruments/BambooFlute.cs index 7bf75926b..43d6ad26f 100644 --- a/Projects/UOContent/Items/Skill Items/Musical Instruments/BambooFlute.cs +++ b/Projects/UOContent/Items/Skill Items/Musical Instruments/BambooFlute.cs @@ -6,5 +6,9 @@ namespace Server.Items; public partial class BambooFlute : BaseInstrument { [Constructible] - public BambooFlute() : base(0x2805, 0x504, 0x503) => Weight = 2.0; + public BambooFlute() : base(0x2805, 0x504, 0x503) + { + } + + public override double DefaultWeight => 2.0; } diff --git a/Projects/UOContent/Items/Skill Items/Musical Instruments/Drums.cs b/Projects/UOContent/Items/Skill Items/Musical Instruments/Drums.cs index 893768a5a..e1021ae5a 100644 --- a/Projects/UOContent/Items/Skill Items/Musical Instruments/Drums.cs +++ b/Projects/UOContent/Items/Skill Items/Musical Instruments/Drums.cs @@ -6,5 +6,9 @@ namespace Server.Items; public partial class Drums : BaseInstrument { [Constructible] - public Drums() : base(0xE9C, 0x38, 0x39) => Weight = 4.0; + public Drums() : base(0xE9C, 0x38, 0x39) + { + } + + public override double DefaultWeight => 4.0; } diff --git a/Projects/UOContent/Items/Skill Items/Musical Instruments/Harp.cs b/Projects/UOContent/Items/Skill Items/Musical Instruments/Harp.cs index 69e5609dc..2af28f827 100644 --- a/Projects/UOContent/Items/Skill Items/Musical Instruments/Harp.cs +++ b/Projects/UOContent/Items/Skill Items/Musical Instruments/Harp.cs @@ -6,5 +6,9 @@ namespace Server.Items; public partial class Harp : BaseInstrument { [Constructible] - public Harp() : base(0xEB1, 0x43, 0x44) => Weight = 35.0; + public Harp() : base(0xEB1, 0x43, 0x44) + { + } + + public override double DefaultWeight => 35.0; } diff --git a/Projects/UOContent/Items/Skill Items/Musical Instruments/LapHarp.cs b/Projects/UOContent/Items/Skill Items/Musical Instruments/LapHarp.cs index ebd96445c..60881f059 100644 --- a/Projects/UOContent/Items/Skill Items/Musical Instruments/LapHarp.cs +++ b/Projects/UOContent/Items/Skill Items/Musical Instruments/LapHarp.cs @@ -6,5 +6,9 @@ namespace Server.Items; public partial class LapHarp : BaseInstrument { [Constructible] - public LapHarp() : base(0xEB2, 0x45, 0x46) => Weight = 10.0; + public LapHarp() : base(0xEB2, 0x45, 0x46) + { + } + + public override double DefaultWeight => 10.0; } diff --git a/Projects/UOContent/Items/Skill Items/Musical Instruments/Lute.cs b/Projects/UOContent/Items/Skill Items/Musical Instruments/Lute.cs index 308394492..1a74cac3b 100644 --- a/Projects/UOContent/Items/Skill Items/Musical Instruments/Lute.cs +++ b/Projects/UOContent/Items/Skill Items/Musical Instruments/Lute.cs @@ -6,5 +6,9 @@ namespace Server.Items; public partial class Lute : BaseInstrument { [Constructible] - public Lute() : base(0xEB3, 0x4C, 0x4D) => Weight = 5.0; + public Lute() : base(0xEB3, 0x4C, 0x4D) + { + } + + public override double DefaultWeight => 5.0; } diff --git a/Projects/UOContent/Items/Skill Items/Musical Instruments/Tambourine.cs b/Projects/UOContent/Items/Skill Items/Musical Instruments/Tambourine.cs index 3e768b929..e2d77530a 100644 --- a/Projects/UOContent/Items/Skill Items/Musical Instruments/Tambourine.cs +++ b/Projects/UOContent/Items/Skill Items/Musical Instruments/Tambourine.cs @@ -6,5 +6,9 @@ namespace Server.Items; public partial class Tambourine : BaseInstrument { [Constructible] - public Tambourine() : base(0xE9D, 0x52, 0x53) => Weight = 1.0; + public Tambourine() : base(0xE9D, 0x52, 0x53) + { + } + + public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Skill Items/Musical Instruments/TambourineTassel.cs b/Projects/UOContent/Items/Skill Items/Musical Instruments/TambourineTassel.cs index 7f4a5a951..ed54c07c9 100644 --- a/Projects/UOContent/Items/Skill Items/Musical Instruments/TambourineTassel.cs +++ b/Projects/UOContent/Items/Skill Items/Musical Instruments/TambourineTassel.cs @@ -6,5 +6,9 @@ namespace Server.Items; public partial class TambourineTassel : BaseInstrument { [Constructible] - public TambourineTassel() : base(0xE9E, 0x52, 0x53) => Weight = 1.0; + public TambourineTassel() : base(0xE9E, 0x52, 0x53) + { + } + + public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Skill Items/Ninjitsu/EggBomb.cs b/Projects/UOContent/Items/Skill Items/Ninjitsu/EggBomb.cs index d766555bb..31170713b 100644 --- a/Projects/UOContent/Items/Skill Items/Ninjitsu/EggBomb.cs +++ b/Projects/UOContent/Items/Skill Items/Ninjitsu/EggBomb.cs @@ -11,9 +11,10 @@ public partial class EggBomb : Item { // Item ID should be 0x2809 - Temporary solution for clients 7.0.0.0 and up Stackable = Core.ML; - Weight = 1.0; } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1030249; public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Items/Skill Items/Ninjitsu/Fukiya.cs b/Projects/UOContent/Items/Skill Items/Ninjitsu/Fukiya.cs index 6b80d40da..5cd7270ff 100644 --- a/Projects/UOContent/Items/Skill Items/Ninjitsu/Fukiya.cs +++ b/Projects/UOContent/Items/Skill Items/Ninjitsu/Fukiya.cs @@ -26,11 +26,9 @@ public partial class Fukiya : Item, INinjaWeapon private int _poisonCharges; [Constructible] - public Fukiya() : base(0x27AA) - { - Weight = 4.0; - Layer = Layer.OneHanded; - } + public Fukiya() : base(0x27AA) => Layer = Layer.OneHanded; + + public override double DefaultWeight => 1.0; public virtual int WrongAmmoMessage => 1063329; // You can only load fukiya darts public virtual int NoFreeHandMessage => 1063327; // You must have a free hand to use a fukiya. diff --git a/Projects/UOContent/Items/Skill Items/Ninjitsu/FukiyaDarts.cs b/Projects/UOContent/Items/Skill Items/Ninjitsu/FukiyaDarts.cs index 82a7950ee..ad5158eff 100644 --- a/Projects/UOContent/Items/Skill Items/Ninjitsu/FukiyaDarts.cs +++ b/Projects/UOContent/Items/Skill Items/Ninjitsu/FukiyaDarts.cs @@ -23,15 +23,9 @@ public partial class FukiyaDarts : Item, ICraftable, INinjaAmmo private int _poisonCharges; [Constructible] - public FukiyaDarts(int amount = 1) : base(0x2806) - { - Weight = 1.0; - _usesRemaining = amount; - } + public FukiyaDarts(int amount = 1) : base(0x2806) => _usesRemaining = amount; - public FukiyaDarts(Serial serial) : base(serial) - { - } + public override double DefaultWeight => 1.0; public int OnCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, diff --git a/Projects/UOContent/Items/Skill Items/Ninjitsu/LeatherNinjaBelt.cs b/Projects/UOContent/Items/Skill Items/Ninjitsu/LeatherNinjaBelt.cs index a62d7ad51..7cfd9ef05 100644 --- a/Projects/UOContent/Items/Skill Items/Ninjitsu/LeatherNinjaBelt.cs +++ b/Projects/UOContent/Items/Skill Items/Ninjitsu/LeatherNinjaBelt.cs @@ -26,11 +26,9 @@ public partial class LeatherNinjaBelt : BaseWaist, INinjaWeapon private int _poisonCharges; [Constructible] - public LeatherNinjaBelt() : base(0x2790) - { - Weight = 1.0; - Layer = Layer.Waist; - } + public LeatherNinjaBelt() : base(0x2790) => Layer = Layer.Waist; + + public override double DefaultWeight => 1.0; public override CraftResource DefaultResource => CraftResource.RegularLeather; diff --git a/Projects/UOContent/Items/Skill Items/Ninjitsu/Shuriken.cs b/Projects/UOContent/Items/Skill Items/Ninjitsu/Shuriken.cs index 7739d03d8..82bfc59e9 100644 --- a/Projects/UOContent/Items/Skill Items/Ninjitsu/Shuriken.cs +++ b/Projects/UOContent/Items/Skill Items/Ninjitsu/Shuriken.cs @@ -24,11 +24,9 @@ public partial class Shuriken : Item, ICraftable, INinjaAmmo private int _poisonCharges; [Constructible] - public Shuriken(int amount = 1) : base(0x27AC) - { - Weight = 1.0; - _usesRemaining = amount; - } + public Shuriken(int amount = 1) : base(0x27AC) => _usesRemaining = amount; + + public override double DefaultWeight => 1.0; public int OnCraft( int quality, bool makersMark, Mobile from, CraftSystem craftSystem, Type typeRes, BaseTool tool, diff --git a/Projects/UOContent/Items/Skill Items/Ninjitsu/SmokeBomb.cs b/Projects/UOContent/Items/Skill Items/Ninjitsu/SmokeBomb.cs index 7ea8c07bc..d2adb24a1 100644 --- a/Projects/UOContent/Items/Skill Items/Ninjitsu/SmokeBomb.cs +++ b/Projects/UOContent/Items/Skill Items/Ninjitsu/SmokeBomb.cs @@ -7,11 +7,9 @@ namespace Server.Items; public partial class SmokeBomb : Item { [Constructible] - public SmokeBomb() : base(0x2808) - { - Stackable = Core.ML; - Weight = 1.0; - } + public SmokeBomb() : base(0x2808) => Stackable = Core.ML; + + public override double DefaultWeight => 1.0; public override void OnDoubleClick(Mobile from) { diff --git a/Projects/UOContent/Items/Skill Items/Specialized/GlassblowingBook.cs b/Projects/UOContent/Items/Skill Items/Specialized/GlassblowingBook.cs index 4ce98103d..4584259dc 100644 --- a/Projects/UOContent/Items/Skill Items/Specialized/GlassblowingBook.cs +++ b/Projects/UOContent/Items/Skill Items/Specialized/GlassblowingBook.cs @@ -7,7 +7,11 @@ namespace Server.Items; public partial class GlassblowingBook : Item { [Constructible] - public GlassblowingBook() : base(0xFF4) => Weight = 1.0; + public GlassblowingBook() : base(0xFF4) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1153528; // Crafting glass with Glassblowing diff --git a/Projects/UOContent/Items/Skill Items/Specialized/MasonryBook.cs b/Projects/UOContent/Items/Skill Items/Specialized/MasonryBook.cs index 57425ad2a..28de22d89 100644 --- a/Projects/UOContent/Items/Skill Items/Specialized/MasonryBook.cs +++ b/Projects/UOContent/Items/Skill Items/Specialized/MasonryBook.cs @@ -7,7 +7,11 @@ namespace Server.Items; public partial class MasonryBook : Item { [Constructible] - public MasonryBook() : base(0xFBE) => Weight = 1.0; + public MasonryBook() : base(0xFBE) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1153527; // Making valuables with Stonecrafting diff --git a/Projects/UOContent/Items/Skill Items/Specialized/Sand.cs b/Projects/UOContent/Items/Skill Items/Specialized/Sand.cs index 5824f8bb3..f8983ef69 100644 --- a/Projects/UOContent/Items/Skill Items/Specialized/Sand.cs +++ b/Projects/UOContent/Items/Skill Items/Specialized/Sand.cs @@ -7,11 +7,9 @@ namespace Server.Items; public partial class Sand : Item, ICommodity { [Constructible] - public Sand(int amount = 1) : base(0x11EA) - { - Stackable = Core.ML; - Weight = 1.0; - } + public Sand(int amount = 1) : base(0x11EA) => Stackable = Core.ML; + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1044626; // sand int ICommodity.DescriptionNumber => LabelNumber; diff --git a/Projects/UOContent/Items/Skill Items/Specialized/SandMiningBook.cs b/Projects/UOContent/Items/Skill Items/Specialized/SandMiningBook.cs index ec18441ea..5a7642f83 100644 --- a/Projects/UOContent/Items/Skill Items/Specialized/SandMiningBook.cs +++ b/Projects/UOContent/Items/Skill Items/Specialized/SandMiningBook.cs @@ -7,7 +7,11 @@ namespace Server.Items; public partial class SandMiningBook : Item { [Constructible] - public SandMiningBook() : base(0xFF4) => Weight = 1.0; + public SandMiningBook() : base(0xFF4) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1153531; // Find Glass-Quality Sand public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Items/Skill Items/Specialized/StoneMiningBook.cs b/Projects/UOContent/Items/Skill Items/Specialized/StoneMiningBook.cs index 5af88288b..4955c8306 100644 --- a/Projects/UOContent/Items/Skill Items/Specialized/StoneMiningBook.cs +++ b/Projects/UOContent/Items/Skill Items/Specialized/StoneMiningBook.cs @@ -7,7 +7,11 @@ namespace Server.Items; public partial class StoneMiningBook : Item { [Constructible] - public StoneMiningBook() : base(0xFBE) => Weight = 1.0; + public StoneMiningBook() : base(0xFBE) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1153530; // Mining For Quality Stone diff --git a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/DyeTub.cs b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/DyeTub.cs index 6ee0aa0cf..6b557b7be 100644 --- a/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/DyeTub.cs +++ b/Projects/UOContent/Items/Skill Items/Tailor Items/Dyetubs/DyeTub.cs @@ -25,11 +25,9 @@ namespace Server.Items private bool _redyable; [Constructible] - public DyeTub() : base(0xFAB) - { - Weight = 10.0; - _redyable = true; - } + public DyeTub() : base(0xFAB) => _redyable = true; + + public override double DefaultWeight => 10.0; public virtual CustomHuePicker CustomHuePicker => null; diff --git a/Projects/UOContent/Items/Skill Items/Tailor Items/Misc/Dressform.cs b/Projects/UOContent/Items/Skill Items/Tailor Items/Misc/Dressform.cs index a4b77facb..a288b762c 100644 --- a/Projects/UOContent/Items/Skill Items/Tailor Items/Misc/Dressform.cs +++ b/Projects/UOContent/Items/Skill Items/Tailor Items/Misc/Dressform.cs @@ -7,5 +7,9 @@ namespace Server.Items; public partial class Dressform : Item { [Constructible] - public Dressform() : base(0xec6) => Weight = 10; + public Dressform() : base(0xec6) + { + } + + public override double DefaultWeight => 10; } diff --git a/Projects/UOContent/Items/Skill Items/Tailor Items/Misc/Dyes.cs b/Projects/UOContent/Items/Skill Items/Tailor Items/Misc/Dyes.cs index 54fe45c94..16fdd914e 100644 --- a/Projects/UOContent/Items/Skill Items/Tailor Items/Misc/Dyes.cs +++ b/Projects/UOContent/Items/Skill Items/Tailor Items/Misc/Dyes.cs @@ -11,7 +11,11 @@ public partial class Dyes : Item // TODO: Add uses remaining [Constructible] - public Dyes() : base(0xFA9) => Weight = 3.0; + public Dyes() : base(0xFA9) + { + } + + public override double DefaultWeight => 3.0; public override void OnDoubleClick(Mobile from) { diff --git a/Projects/UOContent/Items/Skill Items/Tailor Items/Misc/Scissors.cs b/Projects/UOContent/Items/Skill Items/Tailor Items/Misc/Scissors.cs index afd5b9e23..1d0863bab 100644 --- a/Projects/UOContent/Items/Skill Items/Tailor Items/Misc/Scissors.cs +++ b/Projects/UOContent/Items/Skill Items/Tailor Items/Misc/Scissors.cs @@ -13,7 +13,11 @@ public interface IScissorable public partial class Scissors : Item { [Constructible] - public Scissors() : base(0xF9F) => Weight = 1.0; + public Scissors() : base(0xF9F) + { + } + + public override double DefaultWeight => 1.0; public override void OnDoubleClick(Mobile from) { diff --git a/Projects/UOContent/Items/Skill Items/Thief/DisguiseKit.cs b/Projects/UOContent/Items/Skill Items/Thief/DisguiseKit.cs index 1de601118..ef4435bf6 100644 --- a/Projects/UOContent/Items/Skill Items/Thief/DisguiseKit.cs +++ b/Projects/UOContent/Items/Skill Items/Thief/DisguiseKit.cs @@ -17,7 +17,11 @@ public partial class DisguiseKit : Item public override int LabelNumber => 1041078; // a disguise kit [Constructible] - public DisguiseKit() : base(0xE05) => Weight = 1.0; + public DisguiseKit() : base(0xE05) + { + } + + public override double DefaultWeight => 1.0; public bool ValidateUse(Mobile from) { diff --git a/Projects/UOContent/Items/Skill Items/Tinkering/Axle.cs b/Projects/UOContent/Items/Skill Items/Tinkering/Axle.cs index 0f9a592ea..44f0914b0 100644 --- a/Projects/UOContent/Items/Skill Items/Tinkering/Axle.cs +++ b/Projects/UOContent/Items/Skill Items/Tinkering/Axle.cs @@ -11,6 +11,7 @@ public partial class Axle : Item { Stackable = true; Amount = amount; - Weight = 1.0; } + + public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Skill Items/Tinkering/AxleGears.cs b/Projects/UOContent/Items/Skill Items/Tinkering/AxleGears.cs index 113c1392f..0e303c76e 100644 --- a/Projects/UOContent/Items/Skill Items/Tinkering/AxleGears.cs +++ b/Projects/UOContent/Items/Skill Items/Tinkering/AxleGears.cs @@ -11,6 +11,7 @@ public partial class AxleGears : Item { Stackable = true; Amount = amount; - Weight = 1.0; } + + public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Skill Items/Tinkering/ClockFrame.cs b/Projects/UOContent/Items/Skill Items/Tinkering/ClockFrame.cs index df30cab2c..544ca7797 100644 --- a/Projects/UOContent/Items/Skill Items/Tinkering/ClockFrame.cs +++ b/Projects/UOContent/Items/Skill Items/Tinkering/ClockFrame.cs @@ -11,6 +11,7 @@ public partial class ClockFrame : Item { Stackable = true; Amount = amount; - Weight = 2.0; } + + public override double DefaultWeight => 2.0; } diff --git a/Projects/UOContent/Items/Skill Items/Tinkering/ClockParts.cs b/Projects/UOContent/Items/Skill Items/Tinkering/ClockParts.cs index 68f0dde40..f53f098e7 100644 --- a/Projects/UOContent/Items/Skill Items/Tinkering/ClockParts.cs +++ b/Projects/UOContent/Items/Skill Items/Tinkering/ClockParts.cs @@ -11,6 +11,7 @@ public partial class ClockParts : Item { Stackable = true; Amount = amount; - Weight = 1.0; } + + public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Skill Items/Tinkering/Clocks.cs b/Projects/UOContent/Items/Skill Items/Tinkering/Clocks.cs index 46b1dfd5b..82ca1e0d6 100644 --- a/Projects/UOContent/Items/Skill Items/Tinkering/Clocks.cs +++ b/Projects/UOContent/Items/Skill Items/Tinkering/Clocks.cs @@ -25,7 +25,11 @@ public partial class Clock : Item private static readonly DateTime WorldStart = new(1997, 9, 1); [Constructible] - public Clock(int itemID = 0x104B) : base(itemID) => Weight = 3.0; + public Clock(int itemID = 0x104B) : base(itemID) + { + } + + public override double DefaultWeight => 3.0; public static DateTime ServerStart { get; private set; } diff --git a/Projects/UOContent/Items/Skill Items/Tinkering/Gears.cs b/Projects/UOContent/Items/Skill Items/Tinkering/Gears.cs index d4c382740..3468477ed 100644 --- a/Projects/UOContent/Items/Skill Items/Tinkering/Gears.cs +++ b/Projects/UOContent/Items/Skill Items/Tinkering/Gears.cs @@ -11,6 +11,7 @@ public partial class Gears : Item { Stackable = true; Amount = amount; - Weight = 1.0; } + + public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Skill Items/Tinkering/Globe.cs b/Projects/UOContent/Items/Skill Items/Tinkering/Globe.cs index 6f1d8559c..34e4e49c2 100644 --- a/Projects/UOContent/Items/Skill Items/Tinkering/Globe.cs +++ b/Projects/UOContent/Items/Skill Items/Tinkering/Globe.cs @@ -7,5 +7,9 @@ namespace Server.Items; public partial class Globe : Item { [Constructible] - public Globe() : base(0x1047) => Weight = 3.0; + public Globe() : base(0x1047) + { + } + + public override double DefaultWeight => 3.0; } diff --git a/Projects/UOContent/Items/Skill Items/Tinkering/Hinge.cs b/Projects/UOContent/Items/Skill Items/Tinkering/Hinge.cs index b602afa32..3045bd73b 100644 --- a/Projects/UOContent/Items/Skill Items/Tinkering/Hinge.cs +++ b/Projects/UOContent/Items/Skill Items/Tinkering/Hinge.cs @@ -11,6 +11,7 @@ public partial class Hinge : Item { Stackable = true; Amount = amount; - Weight = 1.0; } + + public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Skill Items/Tinkering/SextantParts.cs b/Projects/UOContent/Items/Skill Items/Tinkering/SextantParts.cs index 069ced6ff..316ba1a9d 100644 --- a/Projects/UOContent/Items/Skill Items/Tinkering/SextantParts.cs +++ b/Projects/UOContent/Items/Skill Items/Tinkering/SextantParts.cs @@ -11,6 +11,7 @@ public partial class SextantParts : Item { Stackable = true; Amount = amount; - Weight = 2.0; } + + public override double DefaultWeight => 2.0; } diff --git a/Projects/UOContent/Items/Skill Items/Tinkering/Springs.cs b/Projects/UOContent/Items/Skill Items/Tinkering/Springs.cs index 451706d78..92179ccf4 100644 --- a/Projects/UOContent/Items/Skill Items/Tinkering/Springs.cs +++ b/Projects/UOContent/Items/Skill Items/Tinkering/Springs.cs @@ -11,6 +11,7 @@ public partial class Springs : Item { Stackable = true; Amount = amount; - Weight = 1.0; } + + public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Skill Items/Tinkering/Spyglass.cs b/Projects/UOContent/Items/Skill Items/Tinkering/Spyglass.cs index 773714837..f7673a060 100644 --- a/Projects/UOContent/Items/Skill Items/Tinkering/Spyglass.cs +++ b/Projects/UOContent/Items/Skill Items/Tinkering/Spyglass.cs @@ -10,7 +10,11 @@ namespace Server.Items; public partial class Spyglass : Item { [Constructible] - public Spyglass() : base(0x14F5) => Weight = 3.0; + public Spyglass() : base(0x14F5) + { + } + + public override double DefaultWeight => 3.0; public override void OnDoubleClick(Mobile from) { diff --git a/Projects/UOContent/Items/Skill Items/Tinkering/Utensils.cs b/Projects/UOContent/Items/Skill Items/Tinkering/Utensils.cs index c9449a6ed..0f26e6b73 100644 --- a/Projects/UOContent/Items/Skill Items/Tinkering/Utensils.cs +++ b/Projects/UOContent/Items/Skill Items/Tinkering/Utensils.cs @@ -7,21 +7,33 @@ namespace Server.Items; public partial class Fork : Item { [Constructible] - public Fork() : base(0x9F4) => Weight = 1.0; + public Fork() : base(0x9F4) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class ForkLeft : Item { [Constructible] - public ForkLeft() : base(0x9F4) => Weight = 1.0; + public ForkLeft() : base(0x9F4) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class ForkRight : Item { [Constructible] - public ForkRight() : base(0x9F5) => Weight = 1.0; + public ForkRight() : base(0x9F5) + { + } + + public override double DefaultWeight => 1.0; } [Flippable(0x9F8, 0x9F9, 0x9C2, 0x9C3)] @@ -29,21 +41,33 @@ public partial class ForkRight : Item public partial class Spoon : Item { [Constructible] - public Spoon() : base(0x9F8) => Weight = 1.0; + public Spoon() : base(0x9F8) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class SpoonLeft : Item { [Constructible] - public SpoonLeft() : base(0x9F8) => Weight = 1.0; + public SpoonLeft() : base(0x9F8) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class SpoonRight : Item { [Constructible] - public SpoonRight() : base(0x9F9) => Weight = 1.0; + public SpoonRight() : base(0x9F9) + { + } + + public override double DefaultWeight => 1.0; } [Flippable(0x9F6, 0x9F7, 0x9A5, 0x9A6)] @@ -51,26 +75,42 @@ public partial class SpoonRight : Item public partial class Knife : Item { [Constructible] - public Knife() : base(0x9F6) => Weight = 1.0; + public Knife() : base(0x9F6) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class KnifeLeft : Item { [Constructible] - public KnifeLeft() : base(0x9F6) => Weight = 1.0; + public KnifeLeft() : base(0x9F6) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class KnifeRight : Item { [Constructible] - public KnifeRight() : base(0x9F7) => Weight = 1.0; + public KnifeRight() : base(0x9F7) + { + } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] public partial class Plate : Item { [Constructible] - public Plate() : base(0x9D7) => Weight = 1.0; + public Plate() : base(0x9D7) + { + } + + public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/Blowpipe.cs b/Projects/UOContent/Items/Skill Items/Tools/Blowpipe.cs index 7107ffbaf..ddba32852 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/Blowpipe.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/Blowpipe.cs @@ -8,18 +8,12 @@ namespace Server.Items; public partial class Blowpipe : BaseTool { [Constructible] - public Blowpipe() : base(0xE8A) - { - Weight = 4.0; - Hue = 0x3B9; - } + public Blowpipe() : base(0xE8A) => Hue = 0x3B9; [Constructible] - public Blowpipe(int uses) : base(uses, 0xE8A) - { - Weight = 4.0; - Hue = 0x3B9; - } + public Blowpipe(int uses) : base(uses, 0xE8A) => Hue = 0x3B9; + + public override double DefaultWeight => 4.0; public override CraftSystem CraftSystem => DefGlassblowing.CraftSystem; diff --git a/Projects/UOContent/Items/Skill Items/Tools/DovetailSaw.cs b/Projects/UOContent/Items/Skill Items/Tools/DovetailSaw.cs index c95302be5..b84101d4b 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/DovetailSaw.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/DovetailSaw.cs @@ -8,10 +8,16 @@ namespace Server.Items; public partial class DovetailSaw : BaseTool { [Constructible] - public DovetailSaw() : base(0x1028) => Weight = 2.0; + public DovetailSaw() : base(0x1028) + { + } [Constructible] - public DovetailSaw(int uses) : base(uses, 0x1028) => Weight = 2.0; + public DovetailSaw(int uses) : base(uses, 0x1028) + { + } + + public override double DefaultWeight => 2.0; public override CraftSystem CraftSystem => DefCarpentry.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/DrawKnife.cs b/Projects/UOContent/Items/Skill Items/Tools/DrawKnife.cs index 397d92975..574861433 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/DrawKnife.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/DrawKnife.cs @@ -7,10 +7,16 @@ namespace Server.Items; public partial class DrawKnife : BaseTool { [Constructible] - public DrawKnife() : base(0x10E4) => Weight = 1.0; + public DrawKnife() : base(0x10E4) + { + } [Constructible] - public DrawKnife(int uses) : base(uses, 0x10E4) => Weight = 1.0; + public DrawKnife(int uses) : base(uses, 0x10E4) + { + } + + public override double DefaultWeight => 1.0; public override CraftSystem CraftSystem => DefCarpentry.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/FletcherTools.cs b/Projects/UOContent/Items/Skill Items/Tools/FletcherTools.cs index 775a0bb38..faa6f412d 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/FletcherTools.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/FletcherTools.cs @@ -8,10 +8,16 @@ namespace Server.Items; public partial class FletcherTools : BaseTool { [Constructible] - public FletcherTools() : base(0x1022) => Weight = 2.0; + public FletcherTools() : base(0x1022) + { + } [Constructible] - public FletcherTools(int uses) : base(uses, 0x1022) => Weight = 2.0; + public FletcherTools(int uses) : base(uses, 0x1022) + { + } + + public override double DefaultWeight => 2.0; public override CraftSystem CraftSystem => DefBowFletching.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/FlourSifter.cs b/Projects/UOContent/Items/Skill Items/Tools/FlourSifter.cs index 0794a4aef..33149c8f7 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/FlourSifter.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/FlourSifter.cs @@ -7,10 +7,16 @@ namespace Server.Items; public partial class FlourSifter : BaseTool { [Constructible] - public FlourSifter() : base(0x103E) => Weight = 1.0; + public FlourSifter() : base(0x103E) + { + } [Constructible] - public FlourSifter(int uses) : base(uses, 0x103E) => Weight = 1.0; + public FlourSifter(int uses) : base(uses, 0x103E) + { + } + + public override double DefaultWeight => 1.0; public override CraftSystem CraftSystem => DefCooking.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/Froe.cs b/Projects/UOContent/Items/Skill Items/Tools/Froe.cs index 3658db1f2..4ecb00594 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/Froe.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/Froe.cs @@ -7,10 +7,16 @@ namespace Server.Items; public partial class Froe : BaseTool { [Constructible] - public Froe() : base(0x10E5) => Weight = 1.0; + public Froe() : base(0x10E5) + { + } [Constructible] - public Froe(int uses) : base(uses, 0x10E5) => Weight = 1.0; + public Froe(int uses) : base(uses, 0x10E5) + { + } + + public override double DefaultWeight => 1.0; public override CraftSystem CraftSystem => DefCarpentry.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/Hammer.cs b/Projects/UOContent/Items/Skill Items/Tools/Hammer.cs index 732648691..23f4e6bd5 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/Hammer.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/Hammer.cs @@ -7,10 +7,16 @@ namespace Server.Items; public partial class Hammer : BaseTool { [Constructible] - public Hammer() : base(0x102A) => Weight = 2.0; + public Hammer() : base(0x102A) + { + } [Constructible] - public Hammer(int uses) : base(uses, 0x102A) => Weight = 2.0; + public Hammer(int uses) : base(uses, 0x102A) + { + } + + public override double DefaultWeight => 2.0; public override CraftSystem CraftSystem => DefCarpentry.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/Inshave.cs b/Projects/UOContent/Items/Skill Items/Tools/Inshave.cs index 48631a9d3..32e5eed36 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/Inshave.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/Inshave.cs @@ -7,10 +7,16 @@ namespace Server.Items; public partial class Inshave : BaseTool { [Constructible] - public Inshave() : base(0x10E6) => Weight = 1.0; + public Inshave() : base(0x10E6) + { + } [Constructible] - public Inshave(int uses) : base(uses, 0x10E6) => Weight = 1.0; + public Inshave(int uses) : base(uses, 0x10E6) + { + } + + public override double DefaultWeight => 1.0; public override CraftSystem CraftSystem => DefCarpentry.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/JointingPlane.cs b/Projects/UOContent/Items/Skill Items/Tools/JointingPlane.cs index 741840f1a..3e1143bef 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/JointingPlane.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/JointingPlane.cs @@ -8,10 +8,16 @@ namespace Server.Items; public partial class JointingPlane : BaseTool { [Constructible] - public JointingPlane() : base(0x1030) => Weight = 2.0; + public JointingPlane() : base(0x1030) + { + } [Constructible] - public JointingPlane(int uses) : base(uses, 0x1030) => Weight = 2.0; + public JointingPlane(int uses) : base(uses, 0x1030) + { + } + + public override double DefaultWeight => 2.0; public override CraftSystem CraftSystem => DefCarpentry.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/MalletAndChisel.cs b/Projects/UOContent/Items/Skill Items/Tools/MalletAndChisel.cs index 5a890e07b..1e35ff142 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/MalletAndChisel.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/MalletAndChisel.cs @@ -7,10 +7,16 @@ namespace Server.Items; public partial class MalletAndChisel : BaseTool { [Constructible] - public MalletAndChisel() : base(0x12B3) => Weight = 1.0; + public MalletAndChisel() : base(0x12B3) + { + } [Constructible] - public MalletAndChisel(int uses) : base(uses, 0x12B3) => Weight = 1.0; + public MalletAndChisel(int uses) : base(uses, 0x12B3) + { + } + + public override double DefaultWeight => 1.0; public override CraftSystem CraftSystem => DefMasonry.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/MapmakersPen.cs b/Projects/UOContent/Items/Skill Items/Tools/MapmakersPen.cs index edf1e2b6c..dd263e7d5 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/MapmakersPen.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/MapmakersPen.cs @@ -8,10 +8,16 @@ namespace Server.Items; public partial class MapmakersPen : BaseTool { [Constructible] - public MapmakersPen() : base(0x0FBF) => Weight = 1.0; + public MapmakersPen() : base(0x0FBF) + { + } [Constructible] - public MapmakersPen(int uses) : base(uses, 0x0FBF) => Weight = 1.0; + public MapmakersPen(int uses) : base(uses, 0x0FBF) + { + } + + public override double DefaultWeight => 1.0; public override CraftSystem CraftSystem => DefCartography.CraftSystem; diff --git a/Projects/UOContent/Items/Skill Items/Tools/MortarPestle.cs b/Projects/UOContent/Items/Skill Items/Tools/MortarPestle.cs index 9ed61e088..8c13f724e 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/MortarPestle.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/MortarPestle.cs @@ -7,10 +7,16 @@ namespace Server.Items; public partial class MortarPestle : BaseTool { [Constructible] - public MortarPestle() : base(0xE9B) => Weight = 1.0; + public MortarPestle() : base(0xE9B) + { + } [Constructible] - public MortarPestle(int uses) : base(uses, 0xE9B) => Weight = 1.0; + public MortarPestle(int uses) : base(uses, 0xE9B) + { + } + + public override double DefaultWeight => 1.0; public override CraftSystem CraftSystem => DefAlchemy.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/MouldingPlane.cs b/Projects/UOContent/Items/Skill Items/Tools/MouldingPlane.cs index c04c8d3c0..8a7e3e965 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/MouldingPlane.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/MouldingPlane.cs @@ -8,10 +8,16 @@ namespace Server.Items; public partial class MouldingPlane : BaseTool { [Constructible] - public MouldingPlane() : base(0x102C) => Weight = 2.0; + public MouldingPlane() : base(0x102C) + { + } [Constructible] - public MouldingPlane(int uses) : base(uses, 0x102C) => Weight = 2.0; + public MouldingPlane(int uses) : base(uses, 0x102C) + { + } + + public override double DefaultWeight => 2.0; public override CraftSystem CraftSystem => DefCarpentry.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/Nails.cs b/Projects/UOContent/Items/Skill Items/Tools/Nails.cs index 7df848c14..c1de93d5a 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/Nails.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/Nails.cs @@ -8,10 +8,16 @@ namespace Server.Items; public partial class Nails : BaseTool { [Constructible] - public Nails() : base(0x102E) => Weight = 2.0; + public Nails() : base(0x102E) + { + } [Constructible] - public Nails(int uses) : base(uses, 0x102E) => Weight = 2.0; + public Nails(int uses) : base(uses, 0x102E) + { + } + + public override double DefaultWeight => 2.0; public override CraftSystem CraftSystem => DefCarpentry.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/RollingPin.cs b/Projects/UOContent/Items/Skill Items/Tools/RollingPin.cs index 794e85522..a74bd6ac3 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/RollingPin.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/RollingPin.cs @@ -7,10 +7,16 @@ namespace Server.Items; public partial class RollingPin : BaseTool { [Constructible] - public RollingPin() : base(0x1043) => Weight = 1.0; + public RollingPin() : base(0x1043) + { + } [Constructible] - public RollingPin(int uses) : base(uses, 0x1043) => Weight = 1.0; + public RollingPin(int uses) : base(uses, 0x1043) + { + } + + public override double DefaultWeight => 1.0; public override CraftSystem CraftSystem => DefCooking.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/RunicDovetailSaw.cs b/Projects/UOContent/Items/Skill Items/Tools/RunicDovetailSaw.cs index 6cdb49049..0de55eaba 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/RunicDovetailSaw.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/RunicDovetailSaw.cs @@ -7,18 +7,12 @@ namespace Server.Items; public partial class RunicDovetailSaw : BaseRunicTool { [Constructible] - public RunicDovetailSaw(CraftResource resource) : base(resource, 0x1028) - { - Weight = 2.0; - Hue = CraftResources.GetHue(resource); - } + public RunicDovetailSaw(CraftResource resource) : base(resource, 0x1028) => Hue = CraftResources.GetHue(resource); [Constructible] - public RunicDovetailSaw(CraftResource resource, int uses) : base(resource, uses, 0x1028) - { - Weight = 2.0; - Hue = CraftResources.GetHue(resource); - } + public RunicDovetailSaw(CraftResource resource, int uses) : base(resource, uses, 0x1028) => Hue = CraftResources.GetHue(resource); + + public override double DefaultWeight => 2.0; public override CraftSystem CraftSystem => DefCarpentry.CraftSystem; diff --git a/Projects/UOContent/Items/Skill Items/Tools/RunicFletcherTool.cs b/Projects/UOContent/Items/Skill Items/Tools/RunicFletcherTool.cs index 595d4e10d..04045b4bb 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/RunicFletcherTool.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/RunicFletcherTool.cs @@ -7,18 +7,12 @@ namespace Server.Items; public partial class RunicFletcherTool : BaseRunicTool { [Constructible] - public RunicFletcherTool(CraftResource resource) : base(resource, 0x1022) - { - Weight = 2.0; - Hue = CraftResources.GetHue(resource); - } + public RunicFletcherTool(CraftResource resource) : base(resource, 0x1022) => Hue = CraftResources.GetHue(resource); [Constructible] - public RunicFletcherTool(CraftResource resource, int uses) : base(resource, uses, 0x1022) - { - Weight = 2.0; - Hue = CraftResources.GetHue(resource); - } + public RunicFletcherTool(CraftResource resource, int uses) : base(resource, uses, 0x1022) => Hue = CraftResources.GetHue(resource); + + public override double DefaultWeight => 2.0; public override CraftSystem CraftSystem => DefBowFletching.CraftSystem; diff --git a/Projects/UOContent/Items/Skill Items/Tools/RunicHammer.cs b/Projects/UOContent/Items/Skill Items/Tools/RunicHammer.cs index bb2581258..e2e569bee 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/RunicHammer.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/RunicHammer.cs @@ -10,7 +10,6 @@ public partial class RunicHammer : BaseRunicTool [Constructible] public RunicHammer(CraftResource resource) : base(resource, 0x13E3) { - Weight = 8.0; Layer = Layer.OneHanded; Hue = CraftResources.GetHue(resource); } @@ -18,11 +17,12 @@ public partial class RunicHammer : BaseRunicTool [Constructible] public RunicHammer(CraftResource resource, int uses) : base(resource, uses, 0x13E3) { - Weight = 8.0; Layer = Layer.OneHanded; Hue = CraftResources.GetHue(resource); } + public override double DefaultWeight => 8.0; + public override CraftSystem CraftSystem => DefBlacksmithy.CraftSystem; public override int LabelNumber diff --git a/Projects/UOContent/Items/Skill Items/Tools/RunicSewingKit.cs b/Projects/UOContent/Items/Skill Items/Tools/RunicSewingKit.cs index 42c9cbf0b..7cb39925c 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/RunicSewingKit.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/RunicSewingKit.cs @@ -7,18 +7,12 @@ namespace Server.Items; public partial class RunicSewingKit : BaseRunicTool { [Constructible] - public RunicSewingKit(CraftResource resource) : base(resource, 0xF9D) - { - Weight = 2.0; - Hue = CraftResources.GetHue(resource); - } + public RunicSewingKit(CraftResource resource) : base(resource, 0xF9D) => Hue = CraftResources.GetHue(resource); [Constructible] - public RunicSewingKit(CraftResource resource, int uses) : base(resource, uses, 0xF9D) - { - Weight = 2.0; - Hue = CraftResources.GetHue(resource); - } + public RunicSewingKit(CraftResource resource, int uses) : base(resource, uses, 0xF9D) => Hue = CraftResources.GetHue(resource); + + public override double DefaultWeight => 2.0; public override CraftSystem CraftSystem => DefTailoring.CraftSystem; diff --git a/Projects/UOContent/Items/Skill Items/Tools/Saw.cs b/Projects/UOContent/Items/Skill Items/Tools/Saw.cs index ba44b25f7..01649b210 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/Saw.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/Saw.cs @@ -8,10 +8,16 @@ namespace Server.Items; public partial class Saw : BaseTool { [Constructible] - public Saw() : base(0x1034) => Weight = 2.0; + public Saw() : base(0x1034) + { + } [Constructible] - public Saw(int uses) : base(uses, 0x1034) => Weight = 2.0; + public Saw(int uses) : base(uses, 0x1034) + { + } + + public override double DefaultWeight => 2.0; public override CraftSystem CraftSystem => DefCarpentry.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/Scorp.cs b/Projects/UOContent/Items/Skill Items/Tools/Scorp.cs index d6678a2c9..d495d9b82 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/Scorp.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/Scorp.cs @@ -7,10 +7,16 @@ namespace Server.Items; public partial class Scorp : BaseTool { [Constructible] - public Scorp() : base(0x10E7) => Weight = 1.0; + public Scorp() : base(0x10E7) + { + } [Constructible] - public Scorp(int uses) : base(uses, 0x10E7) => Weight = 1.0; + public Scorp(int uses) : base(uses, 0x10E7) + { + } + + public override double DefaultWeight => 1.0; public override CraftSystem CraftSystem => DefCarpentry.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/ScribesPen.cs b/Projects/UOContent/Items/Skill Items/Tools/ScribesPen.cs index 2eb9c2ae4..224632da2 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/ScribesPen.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/ScribesPen.cs @@ -8,10 +8,16 @@ namespace Server.Items; public partial class ScribesPen : BaseTool { [Constructible] - public ScribesPen() : base(0x0FBF) => Weight = 1.0; + public ScribesPen() : base(0x0FBF) + { + } [Constructible] - public ScribesPen(int uses) : base(uses, 0x0FBF) => Weight = 1.0; + public ScribesPen(int uses) : base(uses, 0x0FBF) + { + } + + public override double DefaultWeight => 1.0; public override CraftSystem CraftSystem => DefInscription.CraftSystem; diff --git a/Projects/UOContent/Items/Skill Items/Tools/SewingKit.cs b/Projects/UOContent/Items/Skill Items/Tools/SewingKit.cs index 97a1d2912..13436d1de 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/SewingKit.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/SewingKit.cs @@ -7,10 +7,16 @@ namespace Server.Items; public partial class SewingKit : BaseTool { [Constructible] - public SewingKit() : base(0xF9D) => Weight = 2.0; + public SewingKit() : base(0xF9D) + { + } [Constructible] - public SewingKit(int uses) : base(uses, 0xF9D) => Weight = 2.0; + public SewingKit(int uses) : base(uses, 0xF9D) + { + } + + public override double DefaultWeight => 2.0; public override CraftSystem CraftSystem => DefTailoring.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/Skillet.cs b/Projects/UOContent/Items/Skill Items/Tools/Skillet.cs index f9278ce38..851e0a726 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/Skillet.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/Skillet.cs @@ -7,10 +7,16 @@ namespace Server.Items; public partial class Skillet : BaseTool { [Constructible] - public Skillet() : base(0x97F) => Weight = 1.0; + public Skillet() : base(0x97F) + { + } [Constructible] - public Skillet(int uses) : base(uses, 0x97F) => Weight = 1.0; + public Skillet(int uses) : base(uses, 0x97F) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1044567; // skillet diff --git a/Projects/UOContent/Items/Skill Items/Tools/SmithHammer.cs b/Projects/UOContent/Items/Skill Items/Tools/SmithHammer.cs index d068457c9..33d17eacc 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/SmithHammer.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/SmithHammer.cs @@ -8,18 +8,12 @@ namespace Server.Items; public partial class SmithHammer : BaseTool { [Constructible] - public SmithHammer() : base(0x13E3) - { - Weight = 8.0; - Layer = Layer.OneHanded; - } + public SmithHammer() : base(0x13E3) => Layer = Layer.OneHanded; [Constructible] - public SmithHammer(int uses) : base(uses, 0x13E3) - { - Weight = 8.0; - Layer = Layer.OneHanded; - } + public SmithHammer(int uses) : base(uses, 0x13E3) => Layer = Layer.OneHanded; + + public override double DefaultWeight => 8.0; public override CraftSystem CraftSystem => DefBlacksmithy.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/SmoothingPlane.cs b/Projects/UOContent/Items/Skill Items/Tools/SmoothingPlane.cs index 13487dffe..6131b86a9 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/SmoothingPlane.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/SmoothingPlane.cs @@ -8,10 +8,16 @@ namespace Server.Items; public partial class SmoothingPlane : BaseTool { [Constructible] - public SmoothingPlane() : base(0x1032) => Weight = 1.0; + public SmoothingPlane() : base(0x1032) + { + } [Constructible] - public SmoothingPlane(int uses) : base(uses, 0x1032) => Weight = 1.0; + public SmoothingPlane(int uses) : base(uses, 0x1032) + { + } + + public override double DefaultWeight => 1.0; public override CraftSystem CraftSystem => DefCarpentry.CraftSystem; } diff --git a/Projects/UOContent/Items/Skill Items/Tools/TinkerTools.cs b/Projects/UOContent/Items/Skill Items/Tools/TinkerTools.cs index d6f9fc4b8..bd85b875c 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/TinkerTools.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/TinkerTools.cs @@ -8,10 +8,16 @@ namespace Server.Items; public partial class TinkerTools : BaseTool { [Constructible] - public TinkerTools() : base(0x1EB8) => Weight = 1.0; + public TinkerTools() : base(0x1EB8) + { + } [Constructible] - public TinkerTools(int uses) : base(uses, 0x1EB8) => Weight = 1.0; + public TinkerTools(int uses) : base(uses, 0x1EB8) + { + } + + public override double DefaultWeight => 1.0; public override CraftSystem CraftSystem => DefTinkering.CraftSystem; } @@ -20,10 +26,16 @@ public partial class TinkerTools : BaseTool public partial class TinkersTools : BaseTool { [Constructible] - public TinkersTools() : base(0x1EBC) => Weight = 1.0; + public TinkersTools() : base(0x1EBC) + { + } [Constructible] - public TinkersTools(int uses) : base(uses, 0x1EBC) => Weight = 1.0; + public TinkersTools(int uses) : base(uses, 0x1EBC) + { + } + + public override double DefaultWeight => 1.0; public override CraftSystem CraftSystem => DefTinkering.CraftSystem; -} \ No newline at end of file +} diff --git a/Projects/UOContent/Items/Skill Items/Tools/Tongs.cs b/Projects/UOContent/Items/Skill Items/Tools/Tongs.cs index 4c76847f1..b6371b4a3 100644 --- a/Projects/UOContent/Items/Skill Items/Tools/Tongs.cs +++ b/Projects/UOContent/Items/Skill Items/Tools/Tongs.cs @@ -8,10 +8,16 @@ namespace Server.Items; public partial class Tongs : BaseTool { [Constructible] - public Tongs() : base(0xFBB) => Weight = 2.0; + public Tongs() : base(0xFBB) + { + } [Constructible] - public Tongs(int uses) : base(uses, 0xFBB) => Weight = 2.0; + public Tongs(int uses) : base(uses, 0xFBB) + { + } + + public override double DefaultWeight => 2.0; public override CraftSystem CraftSystem => DefBlacksmithy.CraftSystem; } diff --git a/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicBox.cs b/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicBox.cs index f20097f7a..0874ff9c3 100644 --- a/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicBox.cs +++ b/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicBox.cs @@ -118,8 +118,6 @@ public partial class DawnsMusicBox : Item, ISecurable [Constructible] public DawnsMusicBox() : base(0x2AF9) { - Weight = 1.0; - var shuffledTracks = GetTracks(DawnsMusicRarity.Common); shuffledTracks.Shuffle(); @@ -132,6 +130,8 @@ public partial class DawnsMusicBox : Item, ISecurable ]; } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1075198; // Dawn's Music Box public override void OnAfterDuped(Item newItem) diff --git a/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicGear.cs b/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicGear.cs index a9eb5c16d..e7eb79894 100644 --- a/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicGear.cs +++ b/Projects/UOContent/Items/Special/8th Anniversary Items/Dawn's Music Box/DawnsMusicGear.cs @@ -17,11 +17,9 @@ public partial class DawnsMusicGear : Item } [Constructible] - public DawnsMusicGear(MusicName music) : base(0x1053) - { - _music = music; - Weight = 1.0; - } + public DawnsMusicGear(MusicName music) : base(0x1053) => _music = music; + + public override double DefaultWeight => 1.0; public static DawnsMusicGear RandomCommon => new(DawnsMusicBox.RandomTrack(DawnsMusicRarity.Common)); diff --git a/Projects/UOContent/Items/Special/8th Anniversary Items/DupresShield.cs b/Projects/UOContent/Items/Special/8th Anniversary Items/DupresShield.cs index 459b14c23..bbeee47d9 100644 --- a/Projects/UOContent/Items/Special/8th Anniversary Items/DupresShield.cs +++ b/Projects/UOContent/Items/Special/8th Anniversary Items/DupresShield.cs @@ -9,7 +9,6 @@ public partial class DupresShield : BaseShield public DupresShield() : base(0x2B01) { LootType = LootType.Blessed; - Weight = 6.0; Attributes.BonusHits = 5; Attributes.RegenHits = 1; @@ -17,6 +16,8 @@ public partial class DupresShield : BaseShield SkillBonuses.SetValues(0, SkillName.Parry, 5); } + public override double DefaultWeight => 6.0; + public override int LabelNumber => 1075196; // Dupre's Shield public override int BasePhysicalResistance => 1; diff --git a/Projects/UOContent/Items/Special/8th Anniversary Items/QuiverOfInfinity.cs b/Projects/UOContent/Items/Special/8th Anniversary Items/QuiverOfInfinity.cs index a13175dc4..0b1377d24 100644 --- a/Projects/UOContent/Items/Special/8th Anniversary Items/QuiverOfInfinity.cs +++ b/Projects/UOContent/Items/Special/8th Anniversary Items/QuiverOfInfinity.cs @@ -9,7 +9,6 @@ public partial class QuiverOfInfinity : BaseQuiver public QuiverOfInfinity() : base(0x2B02) { LootType = LootType.Blessed; - Weight = 8.0; WeightReduction = 30; LowerAmmoCost = 20; @@ -17,5 +16,7 @@ public partial class QuiverOfInfinity : BaseQuiver Attributes.DefendChance = 5; } + public override double DefaultWeight => 8.0; + public override int LabelNumber => 1075201; // Quiver of Infinity } diff --git a/Projects/UOContent/Items/Special/8th Anniversary Items/Talismans.cs b/Projects/UOContent/Items/Special/8th Anniversary Items/Talismans.cs index 091cc242b..e0a25fbf9 100644 --- a/Projects/UOContent/Items/Special/8th Anniversary Items/Talismans.cs +++ b/Projects/UOContent/Items/Special/8th Anniversary Items/Talismans.cs @@ -20,9 +20,10 @@ public partial class BaseFormTalisman : Item { LootType = LootType.Blessed; Layer = Layer.Talisman; - Weight = 1.0; } + public override double DefaultWeight => 1.0; + public virtual TalismanForm Form => TalismanForm.Squirrel; public override void AddNameProperty(IPropertyList list) diff --git a/Projects/UOContent/Items/Special/AoS Promotional/HoodedShroudOfShadows.cs b/Projects/UOContent/Items/Special/AoS Promotional/HoodedShroudOfShadows.cs index caefb44c2..bac7674a5 100644 --- a/Projects/UOContent/Items/Special/AoS Promotional/HoodedShroudOfShadows.cs +++ b/Projects/UOContent/Items/Special/AoS Promotional/HoodedShroudOfShadows.cs @@ -7,11 +7,9 @@ namespace Server.Items; public partial class HoodedShroudOfShadows : BaseOuterTorso { [Constructible] - public HoodedShroudOfShadows(int hue = 0x455) : base(0x2684, hue) - { - LootType = LootType.Blessed; - Weight = 3.0; - } + public HoodedShroudOfShadows(int hue = 0x455) : base(0x2684, hue) => LootType = LootType.Blessed; + + public override double DefaultWeight => 3.0; public override bool Dye(Mobile from, DyeTub sender) { diff --git a/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/AncientSmithyHammer.cs b/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/AncientSmithyHammer.cs index 0b7a0a89e..1f5d80eaf 100644 --- a/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/AncientSmithyHammer.cs +++ b/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/AncientSmithyHammer.cs @@ -13,11 +13,12 @@ public partial class AncientSmithyHammer : BaseTool public AncientSmithyHammer(int bonus, int uses = 600) : base(uses, 0x13E4) { _bonus = bonus; - Weight = 8.0; Layer = Layer.OneHanded; Hue = 0x482; } + public override double DefaultWeight => 8.0; + [SerializableProperty(0)] [CommandProperty(AccessLevel.GameMaster)] public int Bonus diff --git a/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/ColoredAnvil.cs b/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/ColoredAnvil.cs index 991175beb..ab0bf4fbd 100644 --- a/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/ColoredAnvil.cs +++ b/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/ColoredAnvil.cs @@ -13,6 +13,7 @@ public partial class ColoredAnvil : Item { // TODO: Color weighted by rarity? Hue = CraftResources.GetRandomResource(CraftResource.DullCopper, CraftResource.Valorite)?.Hue ?? 0; - Weight = 20; } + + public override double DefaultWeight => 20.0; } diff --git a/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/GlovesOfMining.cs b/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/GlovesOfMining.cs index 91dcc09bd..c2104259a 100644 --- a/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/GlovesOfMining.cs +++ b/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/GlovesOfMining.cs @@ -7,7 +7,11 @@ namespace Server.Items; public partial class LeatherGlovesOfMining : BaseGlovesOfMining { [Constructible] - public LeatherGlovesOfMining(int bonus) : base(bonus, 0x13C6) => Weight = 1; + public LeatherGlovesOfMining(int bonus) : base(bonus, 0x13C6) + { + } + + public override double DefaultWeight => 1; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; @@ -36,7 +40,11 @@ public partial class LeatherGlovesOfMining : BaseGlovesOfMining public partial class StuddedGlovesOfMining : BaseGlovesOfMining { [Constructible] - public StuddedGlovesOfMining(int bonus) : base(bonus, 0x13D5) => Weight = 2; + public StuddedGlovesOfMining(int bonus) : base(bonus, 0x13D5) + { + } + + public override double DefaultWeight => 2; public override int BasePhysicalResistance => 2; public override int BaseFireResistance => 4; @@ -63,7 +71,11 @@ public partial class StuddedGlovesOfMining : BaseGlovesOfMining public partial class RingmailGlovesOfMining : BaseGlovesOfMining { [Constructible] - public RingmailGlovesOfMining(int bonus) : base(bonus, 0x13EB) => Weight = 1; + public RingmailGlovesOfMining(int bonus) : base(bonus, 0x13EB) + { + } + + public override double DefaultWeight => 1; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; diff --git a/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/PowderOfTemperament.cs b/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/PowderOfTemperament.cs index fc898fd20..5a474ebe1 100644 --- a/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/PowderOfTemperament.cs +++ b/Projects/UOContent/Items/Special/Bulk Order Rewards/Blacksmithy/PowderOfTemperament.cs @@ -15,11 +15,12 @@ public partial class PowderOfTemperament : Item, IUsesRemaining [Constructible] public PowderOfTemperament(int charges = 10) : base(4102) { - Weight = 1.0; Hue = 2419; UsesRemaining = charges; } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1049082; // powder of fortifying bool IUsesRemaining.ShowUsesRemaining diff --git a/Projects/UOContent/Items/Special/Community Collection/JesterHatofChuckles.cs b/Projects/UOContent/Items/Special/Community Collection/JesterHatofChuckles.cs index d731396ee..3cae57aec 100644 --- a/Projects/UOContent/Items/Special/Community Collection/JesterHatofChuckles.cs +++ b/Projects/UOContent/Items/Special/Community Collection/JesterHatofChuckles.cs @@ -11,11 +11,9 @@ public partial class JesterHatofChuckles : BaseHat } [Constructible] - public JesterHatofChuckles(int hue) : base(0x171C, hue) - { - Attributes.Luck = 150; - Weight = 1.0; - } + public JesterHatofChuckles(int hue) : base(0x171C, hue) => Attributes.Luck = 150; + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1073256; // Jester Hat of Chuckles - Museum of Vesper Replica 1073256 diff --git a/Projects/UOContent/Items/Special/Gifts/RoseOfTrinsic.cs b/Projects/UOContent/Items/Special/Gifts/RoseOfTrinsic.cs index 2faee7f5f..82e0a2da2 100644 --- a/Projects/UOContent/Items/Special/Gifts/RoseOfTrinsic.cs +++ b/Projects/UOContent/Items/Special/Gifts/RoseOfTrinsic.cs @@ -26,13 +26,13 @@ public partial class RoseOfTrinsic : Item, ISecurable [Constructible] public RoseOfTrinsic() : base(0x234D) { - Weight = 1.0; LootType = LootType.Blessed; - _petals = 0; StartSpawnTimer(TimeSpan.FromMinutes(1.0)); } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1062913; // Rose of Trinsic public override void OnAfterDuped(Item newItem) @@ -156,11 +156,11 @@ public partial class RoseOfTrinsicPetal : Item { Stackable = true; Amount = amount; - - Weight = 1.0; Hue = 0xE; } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1062926; // Petal of the Rose of Trinsic public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Items/Special/Gifts/SamuraiHelm.cs b/Projects/UOContent/Items/Special/Gifts/SamuraiHelm.cs index 57b23ccf7..403dd2b51 100644 --- a/Projects/UOContent/Items/Special/Gifts/SamuraiHelm.cs +++ b/Projects/UOContent/Items/Special/Gifts/SamuraiHelm.cs @@ -9,7 +9,6 @@ public partial class SamuraiHelm : BaseArmor [Constructible] public SamuraiHelm() : base(0x236C) { - Weight = 5.0; LootType = LootType.Blessed; Attributes.DefendChance = 15; @@ -18,6 +17,8 @@ public partial class SamuraiHelm : BaseArmor ArmorAttributes.MageArmor = 1; } + public override double DefaultWeight => 5.0; + public override int LabelNumber => 1062923; // Ancient Samurai Helm public override int BasePhysicalResistance => 15; diff --git a/Projects/UOContent/Items/Special/Gifts/TapestryOfSosaria.cs b/Projects/UOContent/Items/Special/Gifts/TapestryOfSosaria.cs index 9a04138bf..11549a025 100644 --- a/Projects/UOContent/Items/Special/Gifts/TapestryOfSosaria.cs +++ b/Projects/UOContent/Items/Special/Gifts/TapestryOfSosaria.cs @@ -18,10 +18,11 @@ public partial class TapestryOfSosaria : Item, ISecurable [Constructible] public TapestryOfSosaria() : base(0x234E) { - Weight = 1.0; LootType = LootType.Blessed; } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1062917; // The Tapestry of Sosaria public override void GetContextMenuEntries(Mobile from, ref PooledRefList list) diff --git a/Projects/UOContent/Items/Special/HeritageToken.cs b/Projects/UOContent/Items/Special/HeritageToken.cs index eb5aae65a..99ea737af 100644 --- a/Projects/UOContent/Items/Special/HeritageToken.cs +++ b/Projects/UOContent/Items/Special/HeritageToken.cs @@ -7,12 +7,9 @@ namespace Server.Items; public partial class HeritageToken : Item { [Constructible] - public HeritageToken() : base(0x367A) - { - LootType = LootType.Blessed; - Weight = 5.0; - } + public HeritageToken() : base(0x367A) => LootType = LootType.Blessed; + public override double DefaultWeight => 5.0; public override int LabelNumber => 1076596; // A Heritage Token public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Items/Special/Holiday/GiftBox.cs b/Projects/UOContent/Items/Special/Holiday/GiftBox.cs index d7081d1e9..a45702c03 100644 --- a/Projects/UOContent/Items/Special/Holiday/GiftBox.cs +++ b/Projects/UOContent/Items/Special/Holiday/GiftBox.cs @@ -13,9 +13,7 @@ public partial class GiftBox : BaseContainer } [Constructible] - public GiftBox(int hue) : base(Utility.Random(0x232A, 2)) - { - Weight = 2.0; - Hue = hue; - } + public GiftBox(int hue) : base(Utility.Random(0x232A, 2)) => Hue = hue; + + public override double DefaultWeight => 2.0; } diff --git a/Projects/UOContent/Items/Special/Holiday/GingerBreadHouseDeed.cs b/Projects/UOContent/Items/Special/Holiday/GingerBreadHouseDeed.cs index 2121800fd..4150d8d16 100644 --- a/Projects/UOContent/Items/Special/Holiday/GingerBreadHouseDeed.cs +++ b/Projects/UOContent/Items/Special/Holiday/GingerBreadHouseDeed.cs @@ -24,10 +24,11 @@ public partial class GingerBreadHouseDeed : BaseAddonDeed [Constructible] public GingerBreadHouseDeed() { - Weight = 1.0; LootType = LootType.Blessed; } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1077394; // a Gingerbread House Deed public override BaseAddon Addon => new GingerBreadHouseAddon(); } diff --git a/Projects/UOContent/Items/Special/Holiday/HolidayTimepiece.cs b/Projects/UOContent/Items/Special/Holiday/HolidayTimepiece.cs index e7b6d76b3..085e60e94 100644 --- a/Projects/UOContent/Items/Special/Holiday/HolidayTimepiece.cs +++ b/Projects/UOContent/Items/Special/Holiday/HolidayTimepiece.cs @@ -8,7 +8,6 @@ public partial class HolidayTimepiece : Clock [Constructible] public HolidayTimepiece() : base(0x1086) { - Weight = DefaultWeight; LootType = LootType.Blessed; Layer = Layer.Bracelet; } diff --git a/Projects/UOContent/Items/Special/Holiday/Poinsettias.cs b/Projects/UOContent/Items/Special/Holiday/Poinsettias.cs index 013814d79..756b0a297 100644 --- a/Projects/UOContent/Items/Special/Holiday/Poinsettias.cs +++ b/Projects/UOContent/Items/Special/Holiday/Poinsettias.cs @@ -8,9 +8,10 @@ public partial class RedPoinsettia : Item [Constructible] public RedPoinsettia() : base(0x2330) { - Weight = 1.0; LootType = LootType.Blessed; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -19,7 +20,8 @@ public partial class WhitePoinsettia : Item [Constructible] public WhitePoinsettia() : base(0x2331) { - Weight = 1.0; LootType = LootType.Blessed; } + + public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Special/Holiday/Snowflakes.cs b/Projects/UOContent/Items/Special/Holiday/Snowflakes.cs index e08fcdf7e..f6f897b40 100644 --- a/Projects/UOContent/Items/Special/Holiday/Snowflakes.cs +++ b/Projects/UOContent/Items/Special/Holiday/Snowflakes.cs @@ -8,9 +8,10 @@ public partial class BlueSnowflake : Item [Constructible] public BlueSnowflake() : base(0x232E) { - Weight = 1.0; LootType = LootType.Blessed; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -19,7 +20,8 @@ public partial class WhiteSnowflake : Item [Constructible] public WhiteSnowflake() : base(0x232F) { - Weight = 1.0; LootType = LootType.Blessed; } + + public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Special/Holiday/Snowman.cs b/Projects/UOContent/Items/Special/Holiday/Snowman.cs index ded511569..0f6d26871 100644 --- a/Projects/UOContent/Items/Special/Holiday/Snowman.cs +++ b/Projects/UOContent/Items/Special/Holiday/Snowman.cs @@ -87,13 +87,14 @@ public partial class Snowman : Item, IDyable [Constructible] public Snowman(int hue, string title) : base(Utility.Random(0x2328, 2)) { - Weight = 10.0; Hue = hue; LootType = LootType.Blessed; _title = title; } + public override double DefaultWeight => 10.0; + public bool Dye(Mobile from, DyeTub sender) { if (Deleted) diff --git a/Projects/UOContent/Items/Special/Holiday/Wreath.cs b/Projects/UOContent/Items/Special/Holiday/Wreath.cs index 0d0333017..ce0bae397 100644 --- a/Projects/UOContent/Items/Special/Holiday/Wreath.cs +++ b/Projects/UOContent/Items/Special/Holiday/Wreath.cs @@ -165,11 +165,12 @@ public partial class WreathDeed : Item [Constructible] public WreathDeed(int hue) : base(0x14F0) { - Weight = 1.0; Hue = hue; LootType = LootType.Blessed; } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1062837; // holiday wreath deed public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Items/Special/Holiday/WristWatch.cs b/Projects/UOContent/Items/Special/Holiday/WristWatch.cs index 62dc9e1d0..6df024187 100644 --- a/Projects/UOContent/Items/Special/Holiday/WristWatch.cs +++ b/Projects/UOContent/Items/Special/Holiday/WristWatch.cs @@ -8,7 +8,6 @@ public partial class WristWatch : Clock [Constructible] public WristWatch() : base(0x1086) { - Weight = DefaultWeight; LootType = LootType.Blessed; Layer = Layer.Bracelet; } diff --git a/Projects/UOContent/Items/Special/House Raffle/HouseRaffleManagementGump.cs b/Projects/UOContent/Items/Special/House Raffle/HouseRaffleManagementGump.cs index dd39c9ba7..1f43cbcf8 100644 --- a/Projects/UOContent/Items/Special/House Raffle/HouseRaffleManagementGump.cs +++ b/Projects/UOContent/Items/Special/House Raffle/HouseRaffleManagementGump.cs @@ -68,7 +68,7 @@ public class HouseRaffleManagementGump : Gump AddHtml(145, 55, 250, 20, HouseRaffleStone.FormatPrice(stone.TicketPrice).Color(LabelColor)); AddHtml(45, 75, 100, 20, "Total Entries:".Color(LabelColor)); - AddHtml(145, 75, 250, 20, _stone.Entries.Count.ToString().Color(LabelColor)); + AddHtml(145, 75, 250, 20, Html.Color($"{_stone.Entries.Count}", LabelColor)); AddButton(440, 33, 0xFA5, 0xFA7, 3); AddHtml(474, 35, 120, 20, "Sort by name".Color(LabelColor)); @@ -130,7 +130,7 @@ public class HouseRaffleManagementGump : Gump { if (entry.From.Account is Account acc) { - AddHtml(x + 2, 140 + idx * 20, 250, 20, $"{entry.From.RawName} ({acc})".Color(color)); + AddHtml(x + 2, 140 + idx * 20, 250, 20, Html.Color($"{entry.From.RawName} ({acc})", color)); } else { diff --git a/Projects/UOContent/Items/Special/House Raffle/HouseRaffleStone.cs b/Projects/UOContent/Items/Special/House Raffle/HouseRaffleStone.cs index 628d09457..f94d27aa5 100644 --- a/Projects/UOContent/Items/Special/House Raffle/HouseRaffleStone.cs +++ b/Projects/UOContent/Items/Special/House Raffle/HouseRaffleStone.cs @@ -647,10 +647,9 @@ public partial class HouseRaffleStone : Item { public override int Header => 1150470; // CONFIRM TICKET PURCHASE public override int HeaderColor => 0x7F00; - public override int ContentColor => 0xFFFFFF; + public override string ContentColor => "#FFFFFF"; public override int Width => 420; public override int Height => 280; - public override string Content { get; } public ConfirmTicketPurchaseGump(Rectangle2D bounds, Point3D center, Map map, int price, Action callback) : base(callback) diff --git a/Projects/UOContent/Items/Special/ML/GrizzledMareStatuette.cs b/Projects/UOContent/Items/Special/ML/GrizzledMareStatuette.cs index 171ef0649..32ed9eda0 100644 --- a/Projects/UOContent/Items/Special/ML/GrizzledMareStatuette.cs +++ b/Projects/UOContent/Items/Special/ML/GrizzledMareStatuette.cs @@ -7,7 +7,11 @@ namespace Server.Items; public partial class GrizzledMareStatuette : BaseImprisonedMobile { [Constructible] - public GrizzledMareStatuette() : base(0x2617) => Weight = 1.0; + public GrizzledMareStatuette() : base(0x2617) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1074475; // Grizzled Mare Statuette public override BaseCreature Summon => new GrizzledMare(); diff --git a/Projects/UOContent/Items/Special/ML/MinotaurHedge.cs b/Projects/UOContent/Items/Special/ML/MinotaurHedge.cs index 593a6586d..fb25317f0 100644 --- a/Projects/UOContent/Items/Special/ML/MinotaurHedge.cs +++ b/Projects/UOContent/Items/Special/ML/MinotaurHedge.cs @@ -6,7 +6,11 @@ namespace Server.Items; public partial class MinotaurHedge : Item { [Constructible] - public MinotaurHedge() : base(Utility.Random(3215, 4)) => Weight = 1.0; + public MinotaurHedge() : base(Utility.Random(3215, 4)) + { + } + + public override double DefaultWeight => 1.0; public override string DefaultName => "minotaur hedge"; } diff --git a/Projects/UOContent/Items/Special/ML/TormentedChains.cs b/Projects/UOContent/Items/Special/ML/TormentedChains.cs index a8d8a5f00..784da5d2e 100644 --- a/Projects/UOContent/Items/Special/ML/TormentedChains.cs +++ b/Projects/UOContent/Items/Special/ML/TormentedChains.cs @@ -6,5 +6,9 @@ namespace Server.Items; public partial class TormentedChains : Item { [Constructible] - public TormentedChains() : base(Utility.Random(6663, 2)) => Weight = 1.0; + public TormentedChains() : base(Utility.Random(6663, 2)) + { + } + + public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Items/Special/MiniHouses.cs b/Projects/UOContent/Items/Special/MiniHouses.cs index 5104dad33..48ed9470c 100644 --- a/Projects/UOContent/Items/Special/MiniHouses.cs +++ b/Projects/UOContent/Items/Special/MiniHouses.cs @@ -74,11 +74,11 @@ public partial class MiniHouseDeed : BaseAddonDeed public MiniHouseDeed(MiniHouseType type = MiniHouseType.StoneAndPlaster) { _type = type; - - Weight = 1.0; LootType = LootType.Blessed; } + public override double DefaultWeight => 1.0; + public override BaseAddon Addon => new MiniHouseAddon(_type); public override int LabelNumber => 1062096; // a mini house deed diff --git a/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastGland.cs b/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastGland.cs index ab81044db..a5817fbfc 100644 --- a/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastGland.cs +++ b/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastGland.cs @@ -6,11 +6,9 @@ namespace Server.Items; public partial class PlagueBeastGland : Item { [Constructible] - public PlagueBeastGland() : base(0x1CEF) - { - Weight = 1.0; - Hue = 0x6; - } + public PlagueBeastGland() : base(0x1CEF) => Hue = 0x6; + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1153759; // a healthy gland } diff --git a/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastInnard.cs b/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastInnard.cs index bead7bdd0..e40497416 100644 --- a/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastInnard.cs +++ b/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastInnard.cs @@ -10,9 +10,10 @@ public partial class PlagueBeastInnard : Item, IScissorable, ICarvable { Hue = hue; Movable = false; - Weight = 1.0; } + public override double DefaultWeight => 1.0; + public PlagueBeastLord Owner => RootParent as PlagueBeastLord; public override string DefaultName => "plague beast innards"; diff --git a/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastMutationCore.cs b/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastMutationCore.cs index 4149b6131..4865f804b 100644 --- a/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastMutationCore.cs +++ b/Projects/UOContent/Items/Special/Mutation Core/PlagueBeastMutationCore.cs @@ -15,10 +15,11 @@ public partial class PlagueBeastMutationCore : Item, IScissorable public PlagueBeastMutationCore() : base(0x1CF0) { _cut = true; - Weight = 1.0; Hue = 0x480; } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1153760; // a plague beast mutation core public virtual bool Scissor(Mobile from, Scissors scissors) diff --git a/Projects/UOContent/Items/Special/RewardCake.cs b/Projects/UOContent/Items/Special/RewardCake.cs index 88d7f3f95..94dbe81a5 100644 --- a/Projects/UOContent/Items/Special/RewardCake.cs +++ b/Projects/UOContent/Items/Special/RewardCake.cs @@ -9,7 +9,6 @@ public partial class RewardCake : Item public RewardCake() : base(0x9e9) { Stackable = false; - Weight = 1.0; Hue = Utility.RandomList( 0x135, 0xcd, @@ -31,6 +30,8 @@ public partial class RewardCake : Item LootType = LootType.Blessed; } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1049786; // Happy Birthday! ... public override bool DisplayLootType => false; diff --git a/Projects/UOContent/Items/Special/Solen Items/BagOfSending.cs b/Projects/UOContent/Items/Special/Solen Items/BagOfSending.cs index a57c2d282..d81470d5d 100644 --- a/Projects/UOContent/Items/Special/Solen Items/BagOfSending.cs +++ b/Projects/UOContent/Items/Special/Solen Items/BagOfSending.cs @@ -27,12 +27,12 @@ public partial class BagOfSending : Item, TranslocationItem [Constructible] public BagOfSending(BagOfSendingHue hue) : base(0xE76) { - Weight = 2.0; - BagOfSendingHue = hue; _charges = Utility.RandomMinMax(3, 9); } + public override double DefaultWeight => 2.0; + public override int LabelNumber => 1054104; // a bag of sending [SerializableProperty(0)] diff --git a/Projects/UOContent/Items/Special/Solen Items/BallOfSummoning.cs b/Projects/UOContent/Items/Special/Solen Items/BallOfSummoning.cs index a9db9ec33..a76833e30 100644 --- a/Projects/UOContent/Items/Special/Solen Items/BallOfSummoning.cs +++ b/Projects/UOContent/Items/Special/Solen Items/BallOfSummoning.cs @@ -21,13 +21,14 @@ public partial class BallOfSummoning : Item, TranslocationItem [Constructible] public BallOfSummoning() : base(0xE2E) { - Weight = 10.0; Light = LightType.Circle150; _charges = Utility.RandomMinMax(3, 9); _petName = null; } + public override double DefaultWeight => 10.0; + [SerializableProperty(0)] [CommandProperty(AccessLevel.GameMaster)] public int Recharges diff --git a/Projects/UOContent/Items/Special/Solen Items/BraceletOfBinding.cs b/Projects/UOContent/Items/Special/Solen Items/BraceletOfBinding.cs index 8a8ce5d32..33cbe485c 100644 --- a/Projects/UOContent/Items/Special/Solen Items/BraceletOfBinding.cs +++ b/Projects/UOContent/Items/Special/Solen Items/BraceletOfBinding.cs @@ -23,11 +23,9 @@ public partial class BraceletOfBinding : BaseBracelet, TranslocationItem private TransportTimer _timer; [Constructible] - public BraceletOfBinding() : base(0x1086) - { - Hue = 0x489; - Weight = 1.0; - } + public BraceletOfBinding() : base(0x1086) => Hue = 0x489; + + public override double DefaultWeight => 1.0; [SerializableProperty(0)] [CommandProperty(AccessLevel.GameMaster)] diff --git a/Projects/UOContent/Items/Special/Solen Items/PowderOfTranslocation.cs b/Projects/UOContent/Items/Special/Solen Items/PowderOfTranslocation.cs index 47618e53d..5b0fbb08b 100644 --- a/Projects/UOContent/Items/Special/Solen Items/PowderOfTranslocation.cs +++ b/Projects/UOContent/Items/Special/Solen Items/PowderOfTranslocation.cs @@ -19,10 +19,11 @@ public partial class PowderOfTranslocation : Item public PowderOfTranslocation(int amount = 1) : base(0x26B8) { Stackable = true; - Weight = 0.1; Amount = amount; } + public override double DefaultWeight => 0.1; + public override void OnDoubleClick(Mobile from) { if (from.InRange(GetWorldLocation(), 2)) diff --git a/Projects/UOContent/Items/Special/Solen Items/ZoogiFungus.cs b/Projects/UOContent/Items/Special/Solen Items/ZoogiFungus.cs index d01c22198..33465c040 100644 --- a/Projects/UOContent/Items/Special/Solen Items/ZoogiFungus.cs +++ b/Projects/UOContent/Items/Special/Solen Items/ZoogiFungus.cs @@ -9,10 +9,11 @@ public partial class ZoogiFungus : Item, ICommodity public ZoogiFungus(int amount = 1) : base(0x26B7) { Stackable = true; - Weight = 0.1; Amount = amount; } + public override double DefaultWeight => 0.1; + int ICommodity.DescriptionNumber => LabelNumber; bool ICommodity.IsDeedable => Core.ML; } diff --git a/Projects/UOContent/Items/Special/Special Scrolls/PowerScroll.cs b/Projects/UOContent/Items/Special/Special Scrolls/PowerScroll.cs index 267f8e971..0accc3b50 100644 --- a/Projects/UOContent/Items/Special/Special Scrolls/PowerScroll.cs +++ b/Projects/UOContent/Items/Special/Special Scrolls/PowerScroll.cs @@ -110,7 +110,7 @@ public partial class PowerScroll : SpecialScroll } } - public override string DefaultTitle => $"Power Scroll ({Math.Floor(Value * 10) / 10:0.#} Skill):".Color(0xFFFFFF); + public override string DefaultTitle => Html.Color($"Power Scroll ({Math.Floor(Value * 10) / 10:0.#} Skill):", 0xFFFFFF); public static SkillName[] Skills { diff --git a/Projects/UOContent/Items/Special/Special Scrolls/ScrollofTranscendence.cs b/Projects/UOContent/Items/Special/Special Scrolls/ScrollofTranscendence.cs index 7ad9b72fd..6f74356e9 100644 --- a/Projects/UOContent/Items/Special/Special Scrolls/ScrollofTranscendence.cs +++ b/Projects/UOContent/Items/Special/Special Scrolls/ScrollofTranscendence.cs @@ -25,7 +25,7 @@ public partial class ScrollofTranscendence : SpecialScroll public override int Message => 1094933; public override string DefaultTitle => - $"Scroll of Transcendence ({Math.Floor(Value * 10) / 10:0.#} Skill):".Color(0xFFFFFF); + Html.Color( $"Scroll of Transcendence ({Math.Floor(Value * 10) / 10:0.#} Skill):", 0xFFFFFF); public static ScrollofTranscendence CreateRandom(int min, int max) => new(SkillsInfo.RandomSkill(), Utility.RandomMinMax(min, max) / 10.0); diff --git a/Projects/UOContent/Items/Special/Special Scrolls/SpecialScroll.cs b/Projects/UOContent/Items/Special/Special Scrolls/SpecialScroll.cs index 3bb4ca068..7612d1970 100644 --- a/Projects/UOContent/Items/Special/Special Scrolls/SpecialScroll.cs +++ b/Projects/UOContent/Items/Special/Special Scrolls/SpecialScroll.cs @@ -20,12 +20,13 @@ public abstract partial class SpecialScroll : Item public SpecialScroll(SkillName skill, double value) : base(0x14F0) { LootType = LootType.Cursed; - Weight = 1.0; _skill = skill; _value = value; } + public override double DefaultWeight => 1.0; + public abstract int Message { get; } public virtual int Title => 0; public abstract string DefaultTitle { get; } diff --git a/Projects/UOContent/Items/Special/Special Scrolls/StatCapScroll.cs b/Projects/UOContent/Items/Special/Special Scrolls/StatCapScroll.cs index f4e1a7d71..92dcd54de 100644 --- a/Projects/UOContent/Items/Special/Special Scrolls/StatCapScroll.cs +++ b/Projects/UOContent/Items/Special/Special Scrolls/StatCapScroll.cs @@ -42,7 +42,7 @@ public partial class StatCapScroll : SpecialScroll } public override string DefaultTitle => - $"Power Scroll ({((int)Value - 225 >= 0 ? "+" : "")}{(int)Value - 225} Maximum Stats):".Color(0xFFFFFF); + Html.Color($"Power Scroll ({((int)Value - 225 >= 0 ? "+" : "")}{(int)Value - 225} Maximum Stats):", 0xFFFFFF); public override void AddNameProperty(IPropertyList list) { diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/Banner.cs b/Projects/UOContent/Items/Special/Veteran Rewards/Banner.cs index 09cca1a32..8559b4af1 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/Banner.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/Banner.cs @@ -100,12 +100,9 @@ public partial class BannerDeed : Item, IRewardItem private bool _isRewardItem; [Constructible] - public BannerDeed() : base(0x14F0) - { - LootType = LootType.Blessed; - Weight = 1.0; - } + public BannerDeed() : base(0x14F0) => LootType = LootType.Blessed; + public override double DefaultWeight => 1.0; public override int LabelNumber => 1041007; // a banner deed public override void GetProperties(IPropertyList list) diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/Brazier.cs b/Projects/UOContent/Items/Special/Veteran Rewards/Brazier.cs index 437a8de63..93fd5c64c 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/Brazier.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/Brazier.cs @@ -25,11 +25,9 @@ public partial class RewardBrazier : Item, IRewardItem } [Constructible] - public RewardBrazier(int itemID) : base(itemID) - { - LootType = LootType.Blessed; - Weight = 10.0; - } + public RewardBrazier(int itemID) : base(itemID) => LootType = LootType.Blessed; + + public override double DefaultWeight => 10.0; public override bool ForceShowProperties => ObjectPropertyList.Enabled; @@ -115,11 +113,9 @@ public partial class RewardBrazierDeed : Item, IRewardItem private bool _isRewardItem; [Constructible] - public RewardBrazierDeed() : base(0x14F0) - { - LootType = LootType.Blessed; - Weight = 1.0; - } + public RewardBrazierDeed() : base(0x14F0) => LootType = LootType.Blessed; + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1080527; // Brazier Deed diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/CommodityDeedBox.cs b/Projects/UOContent/Items/Special/Veteran Rewards/CommodityDeedBox.cs index 78292c651..ac29c2cca 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/CommodityDeedBox.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/CommodityDeedBox.cs @@ -13,12 +13,9 @@ public partial class CommodityDeedBox : BaseContainer, IRewardItem private bool _isRewardItem; [Constructible] - public CommodityDeedBox() : base(0x9AA) - { - Hue = 0x47; - Weight = 4.0; - } + public CommodityDeedBox() : base(0x9AA) => Hue = 0x47; + public override double DefaultWeight => 4.0; public override int LabelNumber => 1080523; // Commodity Deed Box public override int DefaultGumpID => 0x43; diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/DecorativeShield.cs b/Projects/UOContent/Items/Special/Veteran Rewards/DecorativeShield.cs index e5d298296..a68b10fd3 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/DecorativeShield.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/DecorativeShield.cs @@ -84,12 +84,9 @@ public partial class DecorativeShieldDeed : Item, IRewardItem private bool _isRewardItem; [Constructible] - public DecorativeShieldDeed() : base(0x14F0) - { - LootType = LootType.Blessed; - Weight = 1.0; - } + public DecorativeShieldDeed() : base(0x14F0) => LootType = LootType.Blessed; + public override double DefaultWeight => 1.0; public override int LabelNumber => 1049771; // deed for a decorative shield wall hanging public override void GetProperties(IPropertyList list) diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/FlamingHead.cs b/Projects/UOContent/Items/Special/Veteran Rewards/FlamingHead.cs index 1407ad549..cddfe5c64 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/FlamingHead.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/FlamingHead.cs @@ -98,12 +98,9 @@ public partial class FlamingHeadDeed : Item, IRewardItem private bool _isRewardItem; [Constructible] - public FlamingHeadDeed() : base(0x14F0) - { - LootType = LootType.Blessed; - Weight = 1.0; - } + public FlamingHeadDeed() : base(0x14F0) => LootType = LootType.Blessed; + public override double DefaultWeight => 1.0; public override int LabelNumber => 1041050; // a flaming head deed public override void GetProperties(IPropertyList list) diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/HangingSkeleton.cs b/Projects/UOContent/Items/Special/Veteran Rewards/HangingSkeleton.cs index 29dca292e..e78189116 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/HangingSkeleton.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/HangingSkeleton.cs @@ -88,12 +88,9 @@ public partial class HangingSkeletonDeed : Item, IRewardItem private bool _isRewardItem; [Constructible] - public HangingSkeletonDeed() : base(0x14F0) - { - LootType = LootType.Blessed; - Weight = 1.0; - } + public HangingSkeletonDeed() : base(0x14F0) => LootType = LootType.Blessed; + public override double DefaultWeight => 1.0; public override int LabelNumber => 1049772; // deed for a hanging skeleton decoration public override void GetProperties(IPropertyList list) diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/PottedCactus.cs b/Projects/UOContent/Items/Special/Veteran Rewards/PottedCactus.cs index cbafbc7b0..51b59a361 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/PottedCactus.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/PottedCactus.cs @@ -19,7 +19,11 @@ public partial class RewardPottedCactus : Item, IRewardItem } [Constructible] - public RewardPottedCactus(int itemID) : base(itemID) => Weight = 5.0; + public RewardPottedCactus(int itemID) : base(itemID) + { + } + + public override double DefaultWeight => 5.0; public override bool ForceShowProperties => ObjectPropertyList.Enabled; } @@ -33,12 +37,9 @@ public partial class PottedCactusDeed : Item, IRewardItem private bool _isRewardItem; [Constructible] - public PottedCactusDeed() : base(0x14F0) - { - LootType = LootType.Blessed; - Weight = 1.0; - } + public PottedCactusDeed() : base(0x14F0) => LootType = LootType.Blessed; + public override double DefaultWeight => 1.0; public override int LabelNumber => 1080407; // Potted Cactus Deed public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/StoneAnkh.cs b/Projects/UOContent/Items/Special/Veteran Rewards/StoneAnkh.cs index 0cee61cf5..7d2508903 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/StoneAnkh.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/StoneAnkh.cs @@ -9,7 +9,11 @@ namespace Server.Items; [SerializationGenerator(0)] public partial class StoneAnkhComponent : AddonComponent { - public StoneAnkhComponent(int itemID) : base(itemID) => Weight = 1.0; + public StoneAnkhComponent(int itemID) : base(itemID) + { + } + + public override double DefaultWeight => 1.0; public override bool ForceShowProperties => ObjectPropertyList.Enabled; diff --git a/Projects/UOContent/Items/Special/Veteran Rewards/WeaponEngravingTool.cs b/Projects/UOContent/Items/Special/Veteran Rewards/WeaponEngravingTool.cs index 95af438d8..9a5d483cb 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/WeaponEngravingTool.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/WeaponEngravingTool.cs @@ -24,11 +24,11 @@ public partial class WeaponEngravingTool : Item, IUsesRemaining, IRewardItem public WeaponEngravingTool(int uses = 10) : base(0x32F8) { LootType = LootType.Blessed; - Weight = 1.0; - _usesRemaining = uses; } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1076158; // Weapon Engraving Tool public bool ShowUsesRemaining diff --git a/Projects/UOContent/Items/Suits/BaseSuit.cs b/Projects/UOContent/Items/Suits/BaseSuit.cs index ee7bf3897..018c010a2 100644 --- a/Projects/UOContent/Items/Suits/BaseSuit.cs +++ b/Projects/UOContent/Items/Suits/BaseSuit.cs @@ -8,7 +8,6 @@ public abstract partial class BaseSuit : Item public BaseSuit(AccessLevel level, int hue, int itemID) : base(itemID) { Hue = hue; - Weight = 1.0; Movable = false; LootType = LootType.Newbied; Layer = Layer.OuterTorso; @@ -16,6 +15,8 @@ public abstract partial class BaseSuit : Item _accessLevel = level; } + public override double DefaultWeight => 1.0; + [SerializableProperty(0)] public AccessLevel AccessLevel { diff --git a/Projects/UOContent/Items/Talismans/BaseTalisman.cs b/Projects/UOContent/Items/Talismans/BaseTalisman.cs index 67cd8b9a5..ff3f867c6 100644 --- a/Projects/UOContent/Items/Talismans/BaseTalisman.cs +++ b/Projects/UOContent/Items/Talismans/BaseTalisman.cs @@ -271,7 +271,6 @@ public partial class BaseTalisman : Item, IAosItem public BaseTalisman(int itemID) : base(itemID) { Layer = Layer.Talisman; - Weight = 1.0; _protection = new TalismanAttribute(); _killer = new TalismanAttribute(); @@ -280,6 +279,8 @@ public partial class BaseTalisman : Item, IAosItem SkillBonuses = new AosSkillBonuses(this); } + public override double DefaultWeight => 1.0; + public override int LabelNumber => 1071023; // Talisman public virtual bool ForceShowName => false; // used to override default summoner/removal name diff --git a/Projects/UOContent/Items/Talismans/Items/EnchantedSwitch.cs b/Projects/UOContent/Items/Talismans/Items/EnchantedSwitch.cs index 0258a5feb..b7ed6829b 100644 --- a/Projects/UOContent/Items/Talismans/Items/EnchantedSwitch.cs +++ b/Projects/UOContent/Items/Talismans/Items/EnchantedSwitch.cs @@ -6,7 +6,11 @@ namespace Server.Items; public partial class EnchantedSwitch : Item { [Constructible] - public EnchantedSwitch() : base(0x2F5C) => Weight = 1.0; + public EnchantedSwitch() : base(0x2F5C) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1072893; // enchanted switch } diff --git a/Projects/UOContent/Items/Talismans/Items/HollowPrism.cs b/Projects/UOContent/Items/Talismans/Items/HollowPrism.cs index 7f58dcfa5..723afa349 100644 --- a/Projects/UOContent/Items/Talismans/Items/HollowPrism.cs +++ b/Projects/UOContent/Items/Talismans/Items/HollowPrism.cs @@ -6,7 +6,11 @@ namespace Server.Items; public partial class HollowPrism : Item { [Constructible] - public HollowPrism() : base(0x2F5D) => Weight = 1.0; + public HollowPrism() : base(0x2F5D) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1072895; // hollow prism } diff --git a/Projects/UOContent/Items/Talismans/Items/JeweledFiligree.cs b/Projects/UOContent/Items/Talismans/Items/JeweledFiligree.cs index 8164dc22e..042ffa032 100644 --- a/Projects/UOContent/Items/Talismans/Items/JeweledFiligree.cs +++ b/Projects/UOContent/Items/Talismans/Items/JeweledFiligree.cs @@ -6,7 +6,11 @@ namespace Server.Items; public partial class JeweledFiligree : Item { [Constructible] - public JeweledFiligree() : base(0x2F5E) => Weight = 1.0; + public JeweledFiligree() : base(0x2F5E) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1072894; // jeweled filigree } diff --git a/Projects/UOContent/Items/Talismans/Items/RunedPrism.cs b/Projects/UOContent/Items/Talismans/Items/RunedPrism.cs index af7429967..dc8791966 100644 --- a/Projects/UOContent/Items/Talismans/Items/RunedPrism.cs +++ b/Projects/UOContent/Items/Talismans/Items/RunedPrism.cs @@ -6,7 +6,11 @@ namespace Server.Items; public partial class RunedPrism : Item { [Constructible] - public RunedPrism() : base(0x2F57) => Weight = 1.0; + public RunedPrism() : base(0x2F57) + { + } + + public override double DefaultWeight => 1.0; public override int LabelNumber => 1073465; // runed prism } diff --git a/Projects/UOContent/Items/Talismans/Items/RunedSwitch.cs b/Projects/UOContent/Items/Talismans/Items/RunedSwitch.cs index bc1c741c4..0c4bdec4d 100644 --- a/Projects/UOContent/Items/Talismans/Items/RunedSwitch.cs +++ b/Projects/UOContent/Items/Talismans/Items/RunedSwitch.cs @@ -7,7 +7,11 @@ namespace Server.Items; public partial class RunedSwitch : Item { [Constructible] - public RunedSwitch() : base(0x2F61) => Weight = 1.0; + public RunedSwitch() : base(0x2F61) + { + } + + public override double DefaultWeight => 1.0; public RunedSwitch(Serial serial) : base(serial) { diff --git a/Projects/UOContent/Items/Wands/BaseWand.cs b/Projects/UOContent/Items/Wands/BaseWand.cs index 4a0127192..8b0098275 100644 --- a/Projects/UOContent/Items/Wands/BaseWand.cs +++ b/Projects/UOContent/Items/Wands/BaseWand.cs @@ -38,7 +38,6 @@ public abstract partial class BaseWand : BaseBashing public BaseWand(WandEffect effect, int minCharges, int maxCharges) : base(0xDF2 + Utility.Random(4)) { - Weight = 1.0; _wandEffect = effect; _charges = Utility.RandomMinMax(minCharges, maxCharges); Attributes.SpellChanneling = 1; @@ -47,6 +46,8 @@ public abstract partial class BaseWand : BaseBashing Resource = CraftResource.None; } + public override double DefaultWeight => 1.0; + public override WeaponAbility PrimaryAbility => WeaponAbility.Dismount; public override WeaponAbility SecondaryAbility => WeaponAbility.Disarm; diff --git a/Projects/UOContent/Items/Weapons/Axes/Axe.cs b/Projects/UOContent/Items/Weapons/Axes/Axe.cs index 71f47b3b5..84b5f0366 100644 --- a/Projects/UOContent/Items/Weapons/Axes/Axe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/Axe.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class Axe : BaseAxe { [Constructible] - public Axe() : base(0xF49) => Weight = 4.0; + public Axe() : base(0xF49) + { + } + + public override double DefaultWeight => 4.0; public override WeaponAbility PrimaryAbility => WeaponAbility.CrushingBlow; public override WeaponAbility SecondaryAbility => WeaponAbility.Dismount; diff --git a/Projects/UOContent/Items/Weapons/Axes/BattleAxe.cs b/Projects/UOContent/Items/Weapons/Axes/BattleAxe.cs index 00352dbd3..0fa0b7ba9 100644 --- a/Projects/UOContent/Items/Weapons/Axes/BattleAxe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/BattleAxe.cs @@ -7,12 +7,9 @@ namespace Server.Items public partial class BattleAxe : BaseAxe { [Constructible] - public BattleAxe() : base(0xF47) - { - Weight = 4.0; - Layer = Layer.TwoHanded; - } + public BattleAxe() : base(0xF47) => Layer = Layer.TwoHanded; + public override double DefaultWeight => 4.0; public override WeaponAbility PrimaryAbility => WeaponAbility.BleedAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.ConcussionBlow; diff --git a/Projects/UOContent/Items/Weapons/Axes/DoubleAxe.cs b/Projects/UOContent/Items/Weapons/Axes/DoubleAxe.cs index fea645c8e..2c9ba8a9f 100644 --- a/Projects/UOContent/Items/Weapons/Axes/DoubleAxe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/DoubleAxe.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class DoubleAxe : BaseAxe { [Constructible] - public DoubleAxe() : base(0xF4B) => Weight = 8.0; + public DoubleAxe() : base(0xF4B) + { + } + + public override double DefaultWeight => 8.0; public override WeaponAbility PrimaryAbility => WeaponAbility.DoubleStrike; public override WeaponAbility SecondaryAbility => WeaponAbility.WhirlwindAttack; diff --git a/Projects/UOContent/Items/Weapons/Axes/ExecutionersAxe.cs b/Projects/UOContent/Items/Weapons/Axes/ExecutionersAxe.cs index 6302a26c0..bfee386c1 100644 --- a/Projects/UOContent/Items/Weapons/Axes/ExecutionersAxe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/ExecutionersAxe.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class ExecutionersAxe : BaseAxe { [Constructible] - public ExecutionersAxe() : base(0xF45) => Weight = 8.0; + public ExecutionersAxe() : base(0xF45) + { + } + + public override double DefaultWeight => 8.0; public override WeaponAbility PrimaryAbility => WeaponAbility.BleedAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.MortalStrike; diff --git a/Projects/UOContent/Items/Weapons/Axes/Hatchet.cs b/Projects/UOContent/Items/Weapons/Axes/Hatchet.cs index 924a54891..4509e28e1 100644 --- a/Projects/UOContent/Items/Weapons/Axes/Hatchet.cs +++ b/Projects/UOContent/Items/Weapons/Axes/Hatchet.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class Hatchet : BaseAxe { [Constructible] - public Hatchet() : base(0xF43) => Weight = 4.0; + public Hatchet() : base(0xF43) + { + } + + public override double DefaultWeight => 4.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ArmorIgnore; public override WeaponAbility SecondaryAbility => WeaponAbility.Disarm; diff --git a/Projects/UOContent/Items/Weapons/Axes/LargeBattleAxe.cs b/Projects/UOContent/Items/Weapons/Axes/LargeBattleAxe.cs index 37f6668f6..613af57cc 100644 --- a/Projects/UOContent/Items/Weapons/Axes/LargeBattleAxe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/LargeBattleAxe.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class LargeBattleAxe : BaseAxe { [Constructible] - public LargeBattleAxe() : base(0x13FB) => Weight = 6.0; + public LargeBattleAxe() : base(0x13FB) + { + } + + public override double DefaultWeight => 6.0; public override WeaponAbility PrimaryAbility => WeaponAbility.WhirlwindAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.BleedAttack; diff --git a/Projects/UOContent/Items/Weapons/Axes/Pickaxe.cs b/Projects/UOContent/Items/Weapons/Axes/Pickaxe.cs index 474712308..b78851d78 100644 --- a/Projects/UOContent/Items/Weapons/Axes/Pickaxe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/Pickaxe.cs @@ -12,11 +12,11 @@ namespace Server.Items [Constructible] public Pickaxe() : base(0xE86) { - Weight = 11.0; UsesRemaining = 50; ShowUsesRemaining = true; } + public override double DefaultWeight => 11.0; public override HarvestSystem HarvestSystem => Mining.System; public override WeaponAbility PrimaryAbility => WeaponAbility.DoubleStrike; diff --git a/Projects/UOContent/Items/Weapons/Axes/TwoHandedAxe.cs b/Projects/UOContent/Items/Weapons/Axes/TwoHandedAxe.cs index 1a87091b2..b1ea91e5e 100644 --- a/Projects/UOContent/Items/Weapons/Axes/TwoHandedAxe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/TwoHandedAxe.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class TwoHandedAxe : BaseAxe { [Constructible] - public TwoHandedAxe() : base(0x1443) => Weight = 8.0; + public TwoHandedAxe() : base(0x1443) + { + } + + public override double DefaultWeight => 8.0; public override WeaponAbility PrimaryAbility => WeaponAbility.DoubleStrike; public override WeaponAbility SecondaryAbility => WeaponAbility.ShadowStrike; diff --git a/Projects/UOContent/Items/Weapons/Axes/WarAxe.cs b/Projects/UOContent/Items/Weapons/Axes/WarAxe.cs index b11db81e0..5c006da82 100644 --- a/Projects/UOContent/Items/Weapons/Axes/WarAxe.cs +++ b/Projects/UOContent/Items/Weapons/Axes/WarAxe.cs @@ -8,7 +8,11 @@ namespace Server.Items public partial class WarAxe : BaseAxe { [Constructible] - public WarAxe() : base(0x13B0) => Weight = 8.0; + public WarAxe() : base(0x13B0) + { + } + + public override double DefaultWeight => 8.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ArmorIgnore; public override WeaponAbility SecondaryAbility => WeaponAbility.BleedAttack; diff --git a/Projects/UOContent/Items/Weapons/Knives/ButcherKnife.cs b/Projects/UOContent/Items/Weapons/Knives/ButcherKnife.cs index 19743996b..4cfb3fe40 100644 --- a/Projects/UOContent/Items/Weapons/Knives/ButcherKnife.cs +++ b/Projects/UOContent/Items/Weapons/Knives/ButcherKnife.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class ButcherKnife : BaseKnife { [Constructible] - public ButcherKnife() : base(0x13F6) => Weight = 1.0; + public ButcherKnife() : base(0x13F6) + { + } + + public override double DefaultWeight => 1.0; public override WeaponAbility PrimaryAbility => WeaponAbility.InfectiousStrike; public override WeaponAbility SecondaryAbility => WeaponAbility.Disarm; diff --git a/Projects/UOContent/Items/Weapons/Knives/Cleaver.cs b/Projects/UOContent/Items/Weapons/Knives/Cleaver.cs index 1738cf81c..5c70fc6ec 100644 --- a/Projects/UOContent/Items/Weapons/Knives/Cleaver.cs +++ b/Projects/UOContent/Items/Weapons/Knives/Cleaver.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class Cleaver : BaseKnife { [Constructible] - public Cleaver() : base(0xEC3) => Weight = 2.0; + public Cleaver() : base(0xEC3) + { + } + + public override double DefaultWeight => 2.0; public override WeaponAbility PrimaryAbility => WeaponAbility.BleedAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.InfectiousStrike; diff --git a/Projects/UOContent/Items/Weapons/Knives/Dagger.cs b/Projects/UOContent/Items/Weapons/Knives/Dagger.cs index fadcb7daf..baa4e87b8 100644 --- a/Projects/UOContent/Items/Weapons/Knives/Dagger.cs +++ b/Projects/UOContent/Items/Weapons/Knives/Dagger.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class Dagger : BaseKnife { [Constructible] - public Dagger() : base(0xF52) => Weight = 1.0; + public Dagger() : base(0xF52) + { + } + + public override double DefaultWeight => 1.0; public override WeaponAbility PrimaryAbility => WeaponAbility.InfectiousStrike; public override WeaponAbility SecondaryAbility => WeaponAbility.ShadowStrike; diff --git a/Projects/UOContent/Items/Weapons/Knives/SkinningKnife.cs b/Projects/UOContent/Items/Weapons/Knives/SkinningKnife.cs index dedd24265..5d72aca1d 100644 --- a/Projects/UOContent/Items/Weapons/Knives/SkinningKnife.cs +++ b/Projects/UOContent/Items/Weapons/Knives/SkinningKnife.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class SkinningKnife : BaseKnife { [Constructible] - public SkinningKnife() : base(0xEC4) => Weight = 1.0; + public SkinningKnife() : base(0xEC4) + { + } + + public override double DefaultWeight => 1.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ShadowStrike; public override WeaponAbility SecondaryAbility => WeaponAbility.Disarm; diff --git a/Projects/UOContent/Items/Weapons/Knives/ThrowingDagger.cs b/Projects/UOContent/Items/Weapons/Knives/ThrowingDagger.cs index f6a00ddbc..3e618435e 100644 --- a/Projects/UOContent/Items/Weapons/Knives/ThrowingDagger.cs +++ b/Projects/UOContent/Items/Weapons/Knives/ThrowingDagger.cs @@ -10,11 +10,9 @@ namespace Server.Items; public partial class ThrowingDagger : Item { [Constructible] - public ThrowingDagger() : base(0xF52) - { - Weight = 1.0; - Layer = Layer.OneHanded; - } + public ThrowingDagger() : base(0xF52) => Layer = Layer.OneHanded; + + public override double DefaultWeight => 1.0; public override string DefaultName => "a throwing dagger"; diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/AssassinSpike.cs b/Projects/UOContent/Items/Weapons/ML Weapons/AssassinSpike.cs index f0560945e..6d6ecfdff 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/AssassinSpike.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/AssassinSpike.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class AssassinSpike : BaseKnife { [Constructible] - public AssassinSpike() : base(0x2D21) => Weight = 4.0; + public AssassinSpike() : base(0x2D21) + { + } + + public override double DefaultWeight => 4.0; public override WeaponAbility PrimaryAbility => WeaponAbility.InfectiousStrike; public override WeaponAbility SecondaryAbility => WeaponAbility.ShadowStrike; diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/DiamondMace.cs b/Projects/UOContent/Items/Weapons/ML Weapons/DiamondMace.cs index 453e67e19..038682c33 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/DiamondMace.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/DiamondMace.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class DiamondMace : BaseBashing { [Constructible] - public DiamondMace() : base(0x2D24) => Weight = 10.0; + public DiamondMace() : base(0x2D24) + { + } + + public override double DefaultWeight => 10.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ConcussionBlow; public override WeaponAbility SecondaryAbility => WeaponAbility.CrushingBlow; diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/ElvenCompositeLongbow.cs b/Projects/UOContent/Items/Weapons/ML Weapons/ElvenCompositeLongbow.cs index 43a972d92..a0d866129 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/ElvenCompositeLongbow.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/ElvenCompositeLongbow.cs @@ -8,11 +8,9 @@ namespace Server.Items public partial class ElvenCompositeLongbow : BaseRanged { [Constructible] - public ElvenCompositeLongbow() : base(0x2D1E) - { - Weight = 8.0; - Resource = CraftResource.RegularWood; - } + public ElvenCompositeLongbow() : base(0x2D1E) => Resource = CraftResource.RegularWood; + + public override double DefaultWeight => 8.0; public override int EffectID => 0xF42; public override Type AmmoType => typeof(Arrow); diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/ElvenMachete.cs b/Projects/UOContent/Items/Weapons/ML Weapons/ElvenMachete.cs index 459c76a66..7132d8d92 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/ElvenMachete.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/ElvenMachete.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class ElvenMachete : BaseSword { [Constructible] - public ElvenMachete() : base(0x2D35) => Weight = 6.0; + public ElvenMachete() : base(0x2D35) + { + } + + public override double DefaultWeight => 6.0; public override WeaponAbility PrimaryAbility => WeaponAbility.DefenseMastery; public override WeaponAbility SecondaryAbility => WeaponAbility.Bladeweave; diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/ElvenSpellblade.cs b/Projects/UOContent/Items/Weapons/ML Weapons/ElvenSpellblade.cs index ebe89b1a5..be8bb7a58 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/ElvenSpellblade.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/ElvenSpellblade.cs @@ -7,12 +7,9 @@ namespace Server.Items public partial class ElvenSpellblade : BaseKnife { [Constructible] - public ElvenSpellblade() : base(0x2D20) - { - Weight = 5.0; - Layer = Layer.TwoHanded; - } + public ElvenSpellblade() : base(0x2D20) => Layer = Layer.TwoHanded; + public override double DefaultWeight => 5.0; public override WeaponAbility PrimaryAbility => WeaponAbility.PsychicAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.BleedAttack; diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/Leafblade.cs b/Projects/UOContent/Items/Weapons/ML Weapons/Leafblade.cs index 79fe3730f..8562eab28 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/Leafblade.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/Leafblade.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class Leafblade : BaseKnife { [Constructible] - public Leafblade() : base(0x2D22) => Weight = 8.0; + public Leafblade() : base(0x2D22) + { + } + + public override double DefaultWeight => 8.0; public override WeaponAbility PrimaryAbility => WeaponAbility.Feint; public override WeaponAbility SecondaryAbility => WeaponAbility.ArmorIgnore; diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/MagicalShortbow.cs b/Projects/UOContent/Items/Weapons/ML Weapons/MagicalShortbow.cs index 5a078290f..449dbc3a7 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/MagicalShortbow.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/MagicalShortbow.cs @@ -8,12 +8,9 @@ namespace Server.Items public partial class MagicalShortbow : BaseRanged { [Constructible] - public MagicalShortbow() : base(0x2D2B) - { - Weight = 6.0; - Resource = CraftResource.RegularWood; - } + public MagicalShortbow() : base(0x2D2B) => Resource = CraftResource.RegularWood; + public override double DefaultWeight => 6.0; public override int EffectID => 0xF42; public override Type AmmoType => typeof(Arrow); public override Item Ammo => new Arrow(); diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/OrnateAxe.cs b/Projects/UOContent/Items/Weapons/ML Weapons/OrnateAxe.cs index 1c4f89602..c871908d6 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/OrnateAxe.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/OrnateAxe.cs @@ -7,12 +7,9 @@ namespace Server.Items public partial class OrnateAxe : BaseAxe { [Constructible] - public OrnateAxe() : base(0x2D28) - { - Weight = 12.0; - Layer = Layer.TwoHanded; - } + public OrnateAxe() : base(0x2D28) => Layer = Layer.TwoHanded; + public override double DefaultWeight => 12.0; public override WeaponAbility PrimaryAbility => WeaponAbility.Disarm; public override WeaponAbility SecondaryAbility => WeaponAbility.CrushingBlow; diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/RadiantScimitar.cs b/Projects/UOContent/Items/Weapons/ML Weapons/RadiantScimitar.cs index 6783f36d3..a89915a89 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/RadiantScimitar.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/RadiantScimitar.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class RadiantScimitar : BaseSword { [Constructible] - public RadiantScimitar() : base(0x2D33) => Weight = 9.0; + public RadiantScimitar() : base(0x2D33) + { + } + + public override double DefaultWeight => 9.0; public override WeaponAbility PrimaryAbility => WeaponAbility.WhirlwindAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.Bladeweave; diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/RuneBlade.cs b/Projects/UOContent/Items/Weapons/ML Weapons/RuneBlade.cs index 1adda77d6..905a349dc 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/RuneBlade.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/RuneBlade.cs @@ -7,12 +7,9 @@ namespace Server.Items public partial class RuneBlade : BaseSword { [Constructible] - public RuneBlade() : base(0x2D32) - { - Weight = 7.0; - Layer = Layer.TwoHanded; - } + public RuneBlade() : base(0x2D32) => Layer = Layer.TwoHanded; + public override double DefaultWeight => 7.0; public override WeaponAbility PrimaryAbility => WeaponAbility.Disarm; public override WeaponAbility SecondaryAbility => WeaponAbility.Bladeweave; diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/WarCleaver.cs b/Projects/UOContent/Items/Weapons/ML Weapons/WarCleaver.cs index 153bf26b7..7b96ea256 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/WarCleaver.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/WarCleaver.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class WarCleaver : BaseKnife { [Constructible] - public WarCleaver() : base(0x2D2F) => Weight = 10.0; + public WarCleaver() : base(0x2D2F) + { + } + + public override double DefaultWeight => 10.0; public override WeaponAbility PrimaryAbility => WeaponAbility.Disarm; public override WeaponAbility SecondaryAbility => WeaponAbility.Bladeweave; diff --git a/Projects/UOContent/Items/Weapons/ML Weapons/WildStaff.cs b/Projects/UOContent/Items/Weapons/ML Weapons/WildStaff.cs index e253665f1..7689cad06 100644 --- a/Projects/UOContent/Items/Weapons/ML Weapons/WildStaff.cs +++ b/Projects/UOContent/Items/Weapons/ML Weapons/WildStaff.cs @@ -7,12 +7,9 @@ namespace Server.Items public partial class WildStaff : BaseStaff { [Constructible] - public WildStaff() : base(0x2D25) - { - Weight = 8.0; - Resource = CraftResource.RegularWood; - } + public WildStaff() : base(0x2D25) => Resource = CraftResource.RegularWood; + public override double DefaultWeight => 8.0; public override WeaponAbility PrimaryAbility => WeaponAbility.Block; public override WeaponAbility SecondaryAbility => WeaponAbility.ForceOfNature; diff --git a/Projects/UOContent/Items/Weapons/Maces/Club.cs b/Projects/UOContent/Items/Weapons/Maces/Club.cs index a4b3333e3..e7619c44c 100644 --- a/Projects/UOContent/Items/Weapons/Maces/Club.cs +++ b/Projects/UOContent/Items/Weapons/Maces/Club.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class Club : BaseBashing { [Constructible] - public Club() : base(0x13B4) => Weight = 9.0; + public Club() : base(0x13B4) + { + } + + public override double DefaultWeight => 9.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ShadowStrike; public override WeaponAbility SecondaryAbility => WeaponAbility.Dismount; diff --git a/Projects/UOContent/Items/Weapons/Maces/HammerPick.cs b/Projects/UOContent/Items/Weapons/Maces/HammerPick.cs index 88a74b2aa..3b27eb897 100644 --- a/Projects/UOContent/Items/Weapons/Maces/HammerPick.cs +++ b/Projects/UOContent/Items/Weapons/Maces/HammerPick.cs @@ -7,12 +7,9 @@ namespace Server.Items public partial class HammerPick : BaseBashing { [Constructible] - public HammerPick() : base(0x143D) - { - Weight = 9.0; - Layer = Layer.OneHanded; - } + public HammerPick() : base(0x143D) => Layer = Layer.OneHanded; + public override double DefaultWeight => 9.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ArmorIgnore; public override WeaponAbility SecondaryAbility => WeaponAbility.MortalStrike; diff --git a/Projects/UOContent/Items/Weapons/Maces/Mace.cs b/Projects/UOContent/Items/Weapons/Maces/Mace.cs index b362f04fe..530ad4c07 100644 --- a/Projects/UOContent/Items/Weapons/Maces/Mace.cs +++ b/Projects/UOContent/Items/Weapons/Maces/Mace.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class Mace : BaseBashing { [Constructible] - public Mace() : base(0xF5C) => Weight = 14.0; + public Mace() : base(0xF5C) + { + } + + public override double DefaultWeight => 14.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ConcussionBlow; public override WeaponAbility SecondaryAbility => WeaponAbility.Disarm; diff --git a/Projects/UOContent/Items/Weapons/Maces/MagicWand.cs b/Projects/UOContent/Items/Weapons/Maces/MagicWand.cs index 31e92e79f..c88baa93f 100644 --- a/Projects/UOContent/Items/Weapons/Maces/MagicWand.cs +++ b/Projects/UOContent/Items/Weapons/Maces/MagicWand.cs @@ -6,7 +6,11 @@ namespace Server.Items public partial class MagicWand : BaseBashing { [Constructible] - public MagicWand() : base(0xDF2) => Weight = 1.0; + public MagicWand() : base(0xDF2) + { + } + + public override double DefaultWeight => 1.0; public override WeaponAbility PrimaryAbility => WeaponAbility.Dismount; public override WeaponAbility SecondaryAbility => WeaponAbility.Disarm; diff --git a/Projects/UOContent/Items/Weapons/Maces/Maul.cs b/Projects/UOContent/Items/Weapons/Maces/Maul.cs index ee3054c76..69e44f833 100644 --- a/Projects/UOContent/Items/Weapons/Maces/Maul.cs +++ b/Projects/UOContent/Items/Weapons/Maces/Maul.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class Maul : BaseBashing { [Constructible] - public Maul() : base(0x143B) => Weight = 10.0; + public Maul() : base(0x143B) + { + } + + public override double DefaultWeight => 10.0; public override WeaponAbility PrimaryAbility => WeaponAbility.CrushingBlow; public override WeaponAbility SecondaryAbility => WeaponAbility.ConcussionBlow; diff --git a/Projects/UOContent/Items/Weapons/Maces/Scepter.cs b/Projects/UOContent/Items/Weapons/Maces/Scepter.cs index d48793e15..6b33fd211 100644 --- a/Projects/UOContent/Items/Weapons/Maces/Scepter.cs +++ b/Projects/UOContent/Items/Weapons/Maces/Scepter.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class Scepter : BaseBashing { [Constructible] - public Scepter() : base(0x26BC) => Weight = 8.0; + public Scepter() : base(0x26BC) + { + } + + public override double DefaultWeight => 8.0; public override WeaponAbility PrimaryAbility => WeaponAbility.CrushingBlow; public override WeaponAbility SecondaryAbility => WeaponAbility.MortalStrike; diff --git a/Projects/UOContent/Items/Weapons/Maces/WarHammer.cs b/Projects/UOContent/Items/Weapons/Maces/WarHammer.cs index dd5ab6944..b3944be74 100644 --- a/Projects/UOContent/Items/Weapons/Maces/WarHammer.cs +++ b/Projects/UOContent/Items/Weapons/Maces/WarHammer.cs @@ -7,12 +7,9 @@ namespace Server.Items public partial class WarHammer : BaseBashing { [Constructible] - public WarHammer() : base(0x1439) - { - Weight = 10.0; - Layer = Layer.TwoHanded; - } + public WarHammer() : base(0x1439) => Layer = Layer.TwoHanded; + public override double DefaultWeight => 10.0; public override WeaponAbility PrimaryAbility => WeaponAbility.WhirlwindAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.CrushingBlow; diff --git a/Projects/UOContent/Items/Weapons/Maces/WarMace.cs b/Projects/UOContent/Items/Weapons/Maces/WarMace.cs index 0ed10402c..cd6a18651 100644 --- a/Projects/UOContent/Items/Weapons/Maces/WarMace.cs +++ b/Projects/UOContent/Items/Weapons/Maces/WarMace.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class WarMace : BaseBashing { [Constructible] - public WarMace() : base(0x1407) => Weight = 17.0; + public WarMace() : base(0x1407) + { + } + + public override double DefaultWeight => 17.0; public override WeaponAbility PrimaryAbility => WeaponAbility.CrushingBlow; public override WeaponAbility SecondaryAbility => WeaponAbility.MortalStrike; diff --git a/Projects/UOContent/Items/Weapons/PoleArms/Bardiche.cs b/Projects/UOContent/Items/Weapons/PoleArms/Bardiche.cs index a19bc33c5..25d99ccd1 100644 --- a/Projects/UOContent/Items/Weapons/PoleArms/Bardiche.cs +++ b/Projects/UOContent/Items/Weapons/PoleArms/Bardiche.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class Bardiche : BasePoleArm { [Constructible] - public Bardiche() : base(0xF4D) => Weight = 7.0; + public Bardiche() : base(0xF4D) + { + } + + public override double DefaultWeight => 7.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ParalyzingBlow; public override WeaponAbility SecondaryAbility => WeaponAbility.Dismount; diff --git a/Projects/UOContent/Items/Weapons/PoleArms/Halberd.cs b/Projects/UOContent/Items/Weapons/PoleArms/Halberd.cs index 9356842a5..6b41a685c 100644 --- a/Projects/UOContent/Items/Weapons/PoleArms/Halberd.cs +++ b/Projects/UOContent/Items/Weapons/PoleArms/Halberd.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class Halberd : BasePoleArm { [Constructible] - public Halberd() : base(0x143E) => Weight = 16.0; + public Halberd() : base(0x143E) + { + } + + public override double DefaultWeight => 16.0; public override WeaponAbility PrimaryAbility => WeaponAbility.WhirlwindAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.ConcussionBlow; diff --git a/Projects/UOContent/Items/Weapons/PoleArms/Scythe.cs b/Projects/UOContent/Items/Weapons/PoleArms/Scythe.cs index 62038c4d4..031de09f0 100644 --- a/Projects/UOContent/Items/Weapons/PoleArms/Scythe.cs +++ b/Projects/UOContent/Items/Weapons/PoleArms/Scythe.cs @@ -8,7 +8,11 @@ namespace Server.Items public partial class Scythe : BasePoleArm { [Constructible] - public Scythe() : base(0x26BA) => Weight = 5.0; + public Scythe() : base(0x26BA) + { + } + + public override double DefaultWeight => 5.0; public override WeaponAbility PrimaryAbility => WeaponAbility.BleedAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.ParalyzingBlow; diff --git a/Projects/UOContent/Items/Weapons/Ranged/Bow.cs b/Projects/UOContent/Items/Weapons/Ranged/Bow.cs index 0953163d2..f873c6c42 100644 --- a/Projects/UOContent/Items/Weapons/Ranged/Bow.cs +++ b/Projects/UOContent/Items/Weapons/Ranged/Bow.cs @@ -10,11 +10,11 @@ namespace Server.Items [Constructible] public Bow() : base(0x13B2) { - Weight = 6.0; Layer = Layer.TwoHanded; Resource = CraftResource.RegularWood; } + public override double DefaultWeight => 6.0; public override int EffectID => 0xF42; public override Type AmmoType => typeof(Arrow); public override Item Ammo => new Arrow(); diff --git a/Projects/UOContent/Items/Weapons/Ranged/CompositeBow.cs b/Projects/UOContent/Items/Weapons/Ranged/CompositeBow.cs index bc1e26185..875eb005f 100644 --- a/Projects/UOContent/Items/Weapons/Ranged/CompositeBow.cs +++ b/Projects/UOContent/Items/Weapons/Ranged/CompositeBow.cs @@ -8,12 +8,9 @@ namespace Server.Items public partial class CompositeBow : BaseRanged { [Constructible] - public CompositeBow() : base(0x26C2) - { - Weight = 5.0; - Resource = CraftResource.RegularWood; - } + public CompositeBow() : base(0x26C2) => Resource = CraftResource.RegularWood; + public override double DefaultWeight => 5.0; public override int EffectID => 0xF42; public override Type AmmoType => typeof(Arrow); public override Item Ammo => new Arrow(); diff --git a/Projects/UOContent/Items/Weapons/Ranged/Crossbow.cs b/Projects/UOContent/Items/Weapons/Ranged/Crossbow.cs index 12c823bba..6abd968fa 100644 --- a/Projects/UOContent/Items/Weapons/Ranged/Crossbow.cs +++ b/Projects/UOContent/Items/Weapons/Ranged/Crossbow.cs @@ -10,11 +10,11 @@ namespace Server.Items [Constructible] public Crossbow() : base(0xF50) { - Weight = 7.0; Layer = Layer.TwoHanded; Resource = CraftResource.RegularWood; } + public override double DefaultWeight => 7.0; public override int EffectID => 0x1BFE; public override Type AmmoType => typeof(Bolt); public override Item Ammo => new Bolt(); diff --git a/Projects/UOContent/Items/Weapons/Ranged/HeavyCrossbow.cs b/Projects/UOContent/Items/Weapons/Ranged/HeavyCrossbow.cs index 947762bd5..62c2d5251 100644 --- a/Projects/UOContent/Items/Weapons/Ranged/HeavyCrossbow.cs +++ b/Projects/UOContent/Items/Weapons/Ranged/HeavyCrossbow.cs @@ -10,11 +10,11 @@ namespace Server.Items [Constructible] public HeavyCrossbow() : base(0x13FD) { - Weight = 9.0; Layer = Layer.TwoHanded; Resource = CraftResource.RegularWood; } + public override double DefaultWeight => 9.0; public override int EffectID => 0x1BFE; public override Type AmmoType => typeof(Bolt); public override Item Ammo => new Bolt(); diff --git a/Projects/UOContent/Items/Weapons/Ranged/RepeatingCrossbow.cs b/Projects/UOContent/Items/Weapons/Ranged/RepeatingCrossbow.cs index 92af83a9c..14c38ed0c 100644 --- a/Projects/UOContent/Items/Weapons/Ranged/RepeatingCrossbow.cs +++ b/Projects/UOContent/Items/Weapons/Ranged/RepeatingCrossbow.cs @@ -8,12 +8,9 @@ namespace Server.Items public partial class RepeatingCrossbow : BaseRanged { [Constructible] - public RepeatingCrossbow() : base(0x26C3) - { - Weight = 6.0; - Resource = CraftResource.RegularWood; - } + public RepeatingCrossbow() : base(0x26C3) => Resource = CraftResource.RegularWood; + public override double DefaultWeight => 6.0; public override int EffectID => 0x1BFE; public override Type AmmoType => typeof(Bolt); public override Item Ammo => new Bolt(); diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Bokuto.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Bokuto.cs index 1f3f9db30..c975bd3ce 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Bokuto.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Bokuto.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class Bokuto : BaseSword { [Constructible] - public Bokuto() : base(0x27A8) => Weight = 7.0; + public Bokuto() : base(0x27A8) + { + } + + public override double DefaultWeight => 7.0; public override WeaponAbility PrimaryAbility => WeaponAbility.Feint; public override WeaponAbility SecondaryAbility => WeaponAbility.NerveStrike; diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Daisho.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Daisho.cs index 136897fdf..4ce963ff5 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Daisho.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Daisho.cs @@ -7,12 +7,9 @@ namespace Server.Items public partial class Daisho : BaseSword { [Constructible] - public Daisho() : base(0x27A9) - { - Weight = 8.0; - Layer = Layer.TwoHanded; - } + public Daisho() : base(0x27A9) => Layer = Layer.TwoHanded; + public override double DefaultWeight => 8.0; public override WeaponAbility PrimaryAbility => WeaponAbility.Feint; public override WeaponAbility SecondaryAbility => WeaponAbility.DoubleStrike; diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Kama.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Kama.cs index 23d054bb6..4bbd1b090 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Kama.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Kama.cs @@ -7,12 +7,9 @@ namespace Server.Items public partial class Kama : BaseKnife { [Constructible] - public Kama() : base(0x27AD) - { - Weight = 7.0; - Layer = Layer.TwoHanded; - } + public Kama() : base(0x27AD) => Layer = Layer.TwoHanded; + public override double DefaultWeight => 7.0; public override WeaponAbility PrimaryAbility => WeaponAbility.WhirlwindAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.DefenseMastery; diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Lajatang.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Lajatang.cs index 963b1219f..f28b67961 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Lajatang.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Lajatang.cs @@ -7,12 +7,9 @@ namespace Server.Items public partial class Lajatang : BaseKnife { [Constructible] - public Lajatang() : base(0x27A7) - { - Weight = 12.0; - Layer = Layer.TwoHanded; - } + public Lajatang() : base(0x27A7) => Layer = Layer.TwoHanded; + public override double DefaultWeight => 12.0; public override WeaponAbility PrimaryAbility => WeaponAbility.DefenseMastery; public override WeaponAbility SecondaryAbility => WeaponAbility.FrenziedWhirlwind; diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/NoDachi.cs b/Projects/UOContent/Items/Weapons/SE Weapons/NoDachi.cs index 7d61ea419..64e6a6a92 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/NoDachi.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/NoDachi.cs @@ -7,12 +7,9 @@ namespace Server.Items public partial class NoDachi : BaseSword { [Constructible] - public NoDachi() : base(0x27A2) - { - Weight = 10.0; - Layer = Layer.TwoHanded; - } + public NoDachi() : base(0x27A2) => Layer = Layer.TwoHanded; + public override double DefaultWeight => 10.0; public override WeaponAbility PrimaryAbility => WeaponAbility.CrushingBlow; public override WeaponAbility SecondaryAbility => WeaponAbility.RidingSwipe; diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Nunchaku.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Nunchaku.cs index 2e5bf689c..990f86df6 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Nunchaku.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Nunchaku.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class Nunchaku : BaseBashing { [Constructible] - public Nunchaku() : base(0x27AE) => Weight = 5.0; + public Nunchaku() : base(0x27AE) + { + } + + public override double DefaultWeight => 5.0; public override WeaponAbility PrimaryAbility => WeaponAbility.Block; public override WeaponAbility SecondaryAbility => WeaponAbility.Feint; diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Sai.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Sai.cs index df60344a2..bfd9e9146 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Sai.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Sai.cs @@ -7,11 +7,9 @@ namespace Server.Items public partial class Sai : BaseKnife { [Constructible] - public Sai() : base(0x27AF) - { - Weight = 7.0; - Layer = Layer.TwoHanded; - } + public Sai() : base(0x27AF) => Layer = Layer.TwoHanded; + + public override double DefaultWeight => 7.0; public override WeaponAbility PrimaryAbility => WeaponAbility.Block; public override WeaponAbility SecondaryAbility => WeaponAbility.ArmorPierce; diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Tekagi.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Tekagi.cs index 388186818..9fc3ed171 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Tekagi.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Tekagi.cs @@ -7,12 +7,9 @@ namespace Server.Items public partial class Tekagi : BaseKnife { [Constructible] - public Tekagi() : base(0x27AB) - { - Weight = 5.0; - Layer = Layer.TwoHanded; - } + public Tekagi() : base(0x27AB) => Layer = Layer.TwoHanded; + public override double DefaultWeight => 5.0; public override WeaponAbility PrimaryAbility => WeaponAbility.DualWield; public override WeaponAbility SecondaryAbility => WeaponAbility.TalonStrike; diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Tessen.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Tessen.cs index 442ae9e55..ba4de594f 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Tessen.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Tessen.cs @@ -7,11 +7,9 @@ namespace Server.Items public partial class Tessen : BaseBashing { [Constructible] - public Tessen() : base(0x27A3) - { - Weight = 6.0; - Layer = Layer.TwoHanded; - } + public Tessen() : base(0x27A3) => Layer = Layer.TwoHanded; + + public override double DefaultWeight => 6.0; public override WeaponAbility PrimaryAbility => WeaponAbility.Feint; public override WeaponAbility SecondaryAbility => WeaponAbility.Block; diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Tetsubo.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Tetsubo.cs index ab32c41a4..7ca5402b3 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Tetsubo.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Tetsubo.cs @@ -7,12 +7,9 @@ namespace Server.Items public partial class Tetsubo : BaseBashing { [Constructible] - public Tetsubo() : base(0x27A6) - { - Weight = 8.0; - Layer = Layer.TwoHanded; - } + public Tetsubo() : base(0x27A6) => Layer = Layer.TwoHanded; + public override double DefaultWeight => 8.0; public override WeaponAbility PrimaryAbility => WeaponAbility.FrenziedWhirlwind; public override WeaponAbility SecondaryAbility => WeaponAbility.CrushingBlow; diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Wakizashi.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Wakizashi.cs index 5bb6b0fd4..3f3d06ca2 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Wakizashi.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Wakizashi.cs @@ -7,12 +7,9 @@ namespace Server.Items public partial class Wakizashi : BaseSword { [Constructible] - public Wakizashi() : base(0x27A4) - { - Weight = 5.0; - Layer = Layer.OneHanded; - } + public Wakizashi() : base(0x27A4) => Layer = Layer.OneHanded; + public override double DefaultWeight => 5.0; public override WeaponAbility PrimaryAbility => WeaponAbility.FrenziedWhirlwind; public override WeaponAbility SecondaryAbility => WeaponAbility.DoubleStrike; diff --git a/Projects/UOContent/Items/Weapons/SE Weapons/Yumi.cs b/Projects/UOContent/Items/Weapons/SE Weapons/Yumi.cs index 024f51657..83cef50ad 100644 --- a/Projects/UOContent/Items/Weapons/SE Weapons/Yumi.cs +++ b/Projects/UOContent/Items/Weapons/SE Weapons/Yumi.cs @@ -10,11 +10,11 @@ namespace Server.Items [Constructible] public Yumi() : base(0x27A5) { - Weight = 9.0; Layer = Layer.TwoHanded; Resource = CraftResource.RegularWood; } + public override double DefaultWeight => 9.0; public override int EffectID => 0xF42; public override Type AmmoType => typeof(Arrow); public override Item Ammo => new Arrow(); diff --git a/Projects/UOContent/Items/Weapons/SpearsAndForks/BladedStaff.cs b/Projects/UOContent/Items/Weapons/SpearsAndForks/BladedStaff.cs index 102ceb361..0116c525f 100644 --- a/Projects/UOContent/Items/Weapons/SpearsAndForks/BladedStaff.cs +++ b/Projects/UOContent/Items/Weapons/SpearsAndForks/BladedStaff.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class BladedStaff : BaseSpear { [Constructible] - public BladedStaff() : base(0x26BD) => Weight = 4.0; + public BladedStaff() : base(0x26BD) + { + } + + public override double DefaultWeight => 4.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ArmorIgnore; public override WeaponAbility SecondaryAbility => WeaponAbility.Dismount; diff --git a/Projects/UOContent/Items/Weapons/SpearsAndForks/DoubleBladedStaff.cs b/Projects/UOContent/Items/Weapons/SpearsAndForks/DoubleBladedStaff.cs index 7d7bfc05a..b0b758afb 100644 --- a/Projects/UOContent/Items/Weapons/SpearsAndForks/DoubleBladedStaff.cs +++ b/Projects/UOContent/Items/Weapons/SpearsAndForks/DoubleBladedStaff.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class DoubleBladedStaff : BaseSpear { [Constructible] - public DoubleBladedStaff() : base(0x26BF) => Weight = 2.0; + public DoubleBladedStaff() : base(0x26BF) + { + } + + public override double DefaultWeight => 2.0; public override WeaponAbility PrimaryAbility => WeaponAbility.DoubleStrike; public override WeaponAbility SecondaryAbility => WeaponAbility.InfectiousStrike; diff --git a/Projects/UOContent/Items/Weapons/SpearsAndForks/Pike.cs b/Projects/UOContent/Items/Weapons/SpearsAndForks/Pike.cs index 6c8b724c9..06289fdeb 100644 --- a/Projects/UOContent/Items/Weapons/SpearsAndForks/Pike.cs +++ b/Projects/UOContent/Items/Weapons/SpearsAndForks/Pike.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class Pike : BaseSpear { [Constructible] - public Pike() : base(0x26BE) => Weight = 8.0; + public Pike() : base(0x26BE) + { + } + + public override double DefaultWeight => 8.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ParalyzingBlow; public override WeaponAbility SecondaryAbility => WeaponAbility.InfectiousStrike; diff --git a/Projects/UOContent/Items/Weapons/SpearsAndForks/Pitchfork.cs b/Projects/UOContent/Items/Weapons/SpearsAndForks/Pitchfork.cs index fc3ac6a21..77267916e 100644 --- a/Projects/UOContent/Items/Weapons/SpearsAndForks/Pitchfork.cs +++ b/Projects/UOContent/Items/Weapons/SpearsAndForks/Pitchfork.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class Pitchfork : BaseSpear { [Constructible] - public Pitchfork() : base(0xE87) => Weight = 11.0; + public Pitchfork() : base(0xE87) + { + } + + public override double DefaultWeight => 11.0; public override WeaponAbility PrimaryAbility => WeaponAbility.BleedAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.Dismount; diff --git a/Projects/UOContent/Items/Weapons/SpearsAndForks/ShortSpear.cs b/Projects/UOContent/Items/Weapons/SpearsAndForks/ShortSpear.cs index 32b566a0b..4bb826dee 100644 --- a/Projects/UOContent/Items/Weapons/SpearsAndForks/ShortSpear.cs +++ b/Projects/UOContent/Items/Weapons/SpearsAndForks/ShortSpear.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class ShortSpear : BaseSpear { [Constructible] - public ShortSpear() : base(0x1403) => Weight = 4.0; + public ShortSpear() : base(0x1403) + { + } + + public override double DefaultWeight => 4.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ShadowStrike; public override WeaponAbility SecondaryAbility => WeaponAbility.MortalStrike; diff --git a/Projects/UOContent/Items/Weapons/SpearsAndForks/Spear.cs b/Projects/UOContent/Items/Weapons/SpearsAndForks/Spear.cs index 26d3e2636..6a72f030b 100644 --- a/Projects/UOContent/Items/Weapons/SpearsAndForks/Spear.cs +++ b/Projects/UOContent/Items/Weapons/SpearsAndForks/Spear.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class Spear : BaseSpear { [Constructible] - public Spear() : base(0xF62) => Weight = 7.0; + public Spear() : base(0xF62) + { + } + + public override double DefaultWeight => 7.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ArmorIgnore; public override WeaponAbility SecondaryAbility => WeaponAbility.ParalyzingBlow; diff --git a/Projects/UOContent/Items/Weapons/SpearsAndForks/TribalSpear.cs b/Projects/UOContent/Items/Weapons/SpearsAndForks/TribalSpear.cs index 3f3f17ecc..7aaac31b5 100644 --- a/Projects/UOContent/Items/Weapons/SpearsAndForks/TribalSpear.cs +++ b/Projects/UOContent/Items/Weapons/SpearsAndForks/TribalSpear.cs @@ -7,11 +7,9 @@ namespace Server.Items public partial class TribalSpear : BaseSpear { [Constructible] - public TribalSpear() : base(0xF62) - { - Weight = 7.0; - Hue = 837; - } + public TribalSpear() : base(0xF62) => Hue = 837; + + public override double DefaultWeight => 7.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ArmorIgnore; public override WeaponAbility SecondaryAbility => WeaponAbility.ParalyzingBlow; diff --git a/Projects/UOContent/Items/Weapons/SpearsAndForks/WarFork.cs b/Projects/UOContent/Items/Weapons/SpearsAndForks/WarFork.cs index 94b672b63..fa28136cd 100644 --- a/Projects/UOContent/Items/Weapons/SpearsAndForks/WarFork.cs +++ b/Projects/UOContent/Items/Weapons/SpearsAndForks/WarFork.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class WarFork : BaseSpear { [Constructible] - public WarFork() : base(0x1405) => Weight = 9.0; + public WarFork() : base(0x1405) + { + } + + public override double DefaultWeight => 9.0; public override WeaponAbility PrimaryAbility => WeaponAbility.BleedAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.Disarm; diff --git a/Projects/UOContent/Items/Weapons/Staves/BlackStaff.cs b/Projects/UOContent/Items/Weapons/Staves/BlackStaff.cs index a4508680c..a8525393e 100644 --- a/Projects/UOContent/Items/Weapons/Staves/BlackStaff.cs +++ b/Projects/UOContent/Items/Weapons/Staves/BlackStaff.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class BlackStaff : BaseStaff { [Constructible] - public BlackStaff() : base(0xDF0) => Weight = 6.0; + public BlackStaff() : base(0xDF0) + { + } + + public override double DefaultWeight => 6.0; public override WeaponAbility PrimaryAbility => WeaponAbility.WhirlwindAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.ParalyzingBlow; diff --git a/Projects/UOContent/Items/Weapons/Staves/GlassStaff.cs b/Projects/UOContent/Items/Weapons/Staves/GlassStaff.cs index 22719d53e..fc852bdc9 100644 --- a/Projects/UOContent/Items/Weapons/Staves/GlassStaff.cs +++ b/Projects/UOContent/Items/Weapons/Staves/GlassStaff.cs @@ -7,12 +7,9 @@ namespace Server.Items public partial class GlassStaff : BaseStaff { [Constructible] - public GlassStaff() : base(0x905) - { - Weight = 4.0; - Resource = CraftResource.None; - } + public GlassStaff() : base(0x905) => Resource = CraftResource.None; + public override double DefaultWeight => 4.0; public override WeaponAbility PrimaryAbility => WeaponAbility.DoubleStrike; public override WeaponAbility SecondaryAbility => WeaponAbility.MortalStrike; public override int AosStrengthReq => 20; diff --git a/Projects/UOContent/Items/Weapons/Staves/GnarledStaff.cs b/Projects/UOContent/Items/Weapons/Staves/GnarledStaff.cs index cc05d6a12..05cf5bd2a 100644 --- a/Projects/UOContent/Items/Weapons/Staves/GnarledStaff.cs +++ b/Projects/UOContent/Items/Weapons/Staves/GnarledStaff.cs @@ -7,12 +7,9 @@ namespace Server.Items public partial class GnarledStaff : BaseStaff { [Constructible] - public GnarledStaff() : base(0x13F8) - { - Weight = 3.0; - Resource = CraftResource.RegularWood; - } + public GnarledStaff() : base(0x13F8) => Resource = CraftResource.RegularWood; + public override double DefaultWeight => 3.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ConcussionBlow; public override WeaponAbility SecondaryAbility => WeaponAbility.ForceOfNature; diff --git a/Projects/UOContent/Items/Weapons/Staves/QuarterStaff.cs b/Projects/UOContent/Items/Weapons/Staves/QuarterStaff.cs index a6ee04d97..b05648f2f 100644 --- a/Projects/UOContent/Items/Weapons/Staves/QuarterStaff.cs +++ b/Projects/UOContent/Items/Weapons/Staves/QuarterStaff.cs @@ -7,12 +7,9 @@ namespace Server.Items public partial class QuarterStaff : BaseStaff { [Constructible] - public QuarterStaff() : base(0xE89) - { - Weight = 4.0; - Resource = CraftResource.RegularWood; - } + public QuarterStaff() : base(0xE89) => Resource = CraftResource.RegularWood; + public override double DefaultWeight => 4.0; public override WeaponAbility PrimaryAbility => WeaponAbility.DoubleStrike; public override WeaponAbility SecondaryAbility => WeaponAbility.ConcussionBlow; diff --git a/Projects/UOContent/Items/Weapons/Staves/ShepherdsCrook.cs b/Projects/UOContent/Items/Weapons/Staves/ShepherdsCrook.cs index 3378f14b6..cd47ac8f0 100644 --- a/Projects/UOContent/Items/Weapons/Staves/ShepherdsCrook.cs +++ b/Projects/UOContent/Items/Weapons/Staves/ShepherdsCrook.cs @@ -11,12 +11,9 @@ namespace Server.Items public partial class ShepherdsCrook : BaseStaff { [Constructible] - public ShepherdsCrook() : base(0xE81) - { - Weight = 4.0; - Resource = CraftResource.RegularWood; - } + public ShepherdsCrook() : base(0xE81) => Resource = CraftResource.RegularWood; + public override double DefaultWeight => 4.0; public override WeaponAbility PrimaryAbility => WeaponAbility.CrushingBlow; public override WeaponAbility SecondaryAbility => WeaponAbility.Disarm; diff --git a/Projects/UOContent/Items/Weapons/Swords/BoneHarvester.cs b/Projects/UOContent/Items/Weapons/Swords/BoneHarvester.cs index 8c3d9fbd2..a08813ad6 100644 --- a/Projects/UOContent/Items/Weapons/Swords/BoneHarvester.cs +++ b/Projects/UOContent/Items/Weapons/Swords/BoneHarvester.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class BoneHarvester : BaseSword { [Constructible] - public BoneHarvester() : base(0x26BB) => Weight = 3.0; + public BoneHarvester() : base(0x26BB) + { + } + + public override double DefaultWeight => 3.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ParalyzingBlow; public override WeaponAbility SecondaryAbility => WeaponAbility.MortalStrike; diff --git a/Projects/UOContent/Items/Weapons/Swords/Broadsword.cs b/Projects/UOContent/Items/Weapons/Swords/Broadsword.cs index e980b11ad..e4b42f359 100644 --- a/Projects/UOContent/Items/Weapons/Swords/Broadsword.cs +++ b/Projects/UOContent/Items/Weapons/Swords/Broadsword.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class Broadsword : BaseSword { [Constructible] - public Broadsword() : base(0xF5E) => Weight = 6.0; + public Broadsword() : base(0xF5E) + { + } + + public override double DefaultWeight => 6.0; public override WeaponAbility PrimaryAbility => WeaponAbility.CrushingBlow; public override WeaponAbility SecondaryAbility => WeaponAbility.ArmorIgnore; diff --git a/Projects/UOContent/Items/Weapons/Swords/CrescentBlade.cs b/Projects/UOContent/Items/Weapons/Swords/CrescentBlade.cs index 7bb7f1470..2a12d7c9b 100644 --- a/Projects/UOContent/Items/Weapons/Swords/CrescentBlade.cs +++ b/Projects/UOContent/Items/Weapons/Swords/CrescentBlade.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class CrescentBlade : BaseSword { [Constructible] - public CrescentBlade() : base(0x26C1) => Weight = 1.0; + public CrescentBlade() : base(0x26C1) + { + } + + public override double DefaultWeight => 1.0; public override WeaponAbility PrimaryAbility => WeaponAbility.DoubleStrike; public override WeaponAbility SecondaryAbility => WeaponAbility.MortalStrike; diff --git a/Projects/UOContent/Items/Weapons/Swords/Cutlass.cs b/Projects/UOContent/Items/Weapons/Swords/Cutlass.cs index 922c1fd04..5892dbfb7 100644 --- a/Projects/UOContent/Items/Weapons/Swords/Cutlass.cs +++ b/Projects/UOContent/Items/Weapons/Swords/Cutlass.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class Cutlass : BaseSword { [Constructible] - public Cutlass() : base(0x1441) => Weight = 8.0; + public Cutlass() : base(0x1441) + { + } + + public override double DefaultWeight => 8.0; public override WeaponAbility PrimaryAbility => WeaponAbility.BleedAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.ShadowStrike; diff --git a/Projects/UOContent/Items/Weapons/Swords/GlassSword.cs b/Projects/UOContent/Items/Weapons/Swords/GlassSword.cs index e821588ab..b277ab125 100644 --- a/Projects/UOContent/Items/Weapons/Swords/GlassSword.cs +++ b/Projects/UOContent/Items/Weapons/Swords/GlassSword.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class GlassSword : BaseSword { [Constructible] - public GlassSword() : base(0x90C) => Weight = 6.0; + public GlassSword() : base(0x90C) + { + } + + public override double DefaultWeight => 6.0; public override WeaponAbility PrimaryAbility => WeaponAbility.BleedAttack; public override WeaponAbility SecondaryAbility => WeaponAbility.MortalStrike; diff --git a/Projects/UOContent/Items/Weapons/Swords/Katana.cs b/Projects/UOContent/Items/Weapons/Swords/Katana.cs index 34e4110eb..f59ae28ed 100644 --- a/Projects/UOContent/Items/Weapons/Swords/Katana.cs +++ b/Projects/UOContent/Items/Weapons/Swords/Katana.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class Katana : BaseSword { [Constructible] - public Katana() : base(0x13FF) => Weight = 6.0; + public Katana() : base(0x13FF) + { + } + + public override double DefaultWeight => 6.0; public override WeaponAbility PrimaryAbility => WeaponAbility.DoubleStrike; public override WeaponAbility SecondaryAbility => WeaponAbility.ArmorIgnore; diff --git a/Projects/UOContent/Items/Weapons/Swords/Kryss.cs b/Projects/UOContent/Items/Weapons/Swords/Kryss.cs index e6ae7a663..00dfa36de 100644 --- a/Projects/UOContent/Items/Weapons/Swords/Kryss.cs +++ b/Projects/UOContent/Items/Weapons/Swords/Kryss.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class Kryss : BaseSword { [Constructible] - public Kryss() : base(0x1401) => Weight = 2.0; + public Kryss() : base(0x1401) + { + } + + public override double DefaultWeight => 2.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ArmorIgnore; public override WeaponAbility SecondaryAbility => WeaponAbility.InfectiousStrike; diff --git a/Projects/UOContent/Items/Weapons/Swords/Lance.cs b/Projects/UOContent/Items/Weapons/Swords/Lance.cs index 3520855ca..215e43ca4 100644 --- a/Projects/UOContent/Items/Weapons/Swords/Lance.cs +++ b/Projects/UOContent/Items/Weapons/Swords/Lance.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class Lance : BaseSword { [Constructible] - public Lance() : base(0x26C0) => Weight = 12.0; + public Lance() : base(0x26C0) + { + } + + public override double DefaultWeight => 12.0; public override WeaponAbility PrimaryAbility => WeaponAbility.Dismount; public override WeaponAbility SecondaryAbility => WeaponAbility.ConcussionBlow; diff --git a/Projects/UOContent/Items/Weapons/Swords/Longsword.cs b/Projects/UOContent/Items/Weapons/Swords/Longsword.cs index df3310606..fbff01e81 100644 --- a/Projects/UOContent/Items/Weapons/Swords/Longsword.cs +++ b/Projects/UOContent/Items/Weapons/Swords/Longsword.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class Longsword : BaseSword { [Constructible] - public Longsword() : base(0xF61) => Weight = 7.0; + public Longsword() : base(0xF61) + { + } + + public override double DefaultWeight => 7.0; public override WeaponAbility PrimaryAbility => WeaponAbility.ArmorIgnore; public override WeaponAbility SecondaryAbility => WeaponAbility.ConcussionBlow; diff --git a/Projects/UOContent/Items/Weapons/Swords/Scimitar.cs b/Projects/UOContent/Items/Weapons/Swords/Scimitar.cs index d49112d38..c8f8cc012 100644 --- a/Projects/UOContent/Items/Weapons/Swords/Scimitar.cs +++ b/Projects/UOContent/Items/Weapons/Swords/Scimitar.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class Scimitar : BaseSword { [Constructible] - public Scimitar() : base(0x13B6) => Weight = 5.0; + public Scimitar() : base(0x13B6) + { + } + + public override double DefaultWeight => 5.0; public override WeaponAbility PrimaryAbility => WeaponAbility.DoubleStrike; public override WeaponAbility SecondaryAbility => WeaponAbility.ParalyzingBlow; diff --git a/Projects/UOContent/Items/Weapons/Swords/ThinLongsword.cs b/Projects/UOContent/Items/Weapons/Swords/ThinLongsword.cs index 0e17985ca..b7338ccc9 100644 --- a/Projects/UOContent/Items/Weapons/Swords/ThinLongsword.cs +++ b/Projects/UOContent/Items/Weapons/Swords/ThinLongsword.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class ThinLongsword : BaseSword { [Constructible] - public ThinLongsword() : base(0x13B8) => Weight = 1.0; + public ThinLongsword() : base(0x13B8) + { + } + + public override double DefaultWeight => 1.0; public override int AosStrengthReq => 35; public override int AosMinDamage => 15; diff --git a/Projects/UOContent/Items/Weapons/Swords/VikingSword.cs b/Projects/UOContent/Items/Weapons/Swords/VikingSword.cs index 83c6f4de9..81b7c39b6 100644 --- a/Projects/UOContent/Items/Weapons/Swords/VikingSword.cs +++ b/Projects/UOContent/Items/Weapons/Swords/VikingSword.cs @@ -7,7 +7,11 @@ namespace Server.Items public partial class VikingSword : BaseSword { [Constructible] - public VikingSword() : base(0x13B9) => Weight = 6.0; + public VikingSword() : base(0x13B9) + { + } + + public override double DefaultWeight => 6.0; public override WeaponAbility PrimaryAbility => WeaponAbility.CrushingBlow; public override WeaponAbility SecondaryAbility => WeaponAbility.ParalyzingBlow; diff --git a/Projects/UOContent/Items/Weapons/Throwing/Boomerang.cs b/Projects/UOContent/Items/Weapons/Throwing/Boomerang.cs index 67650361b..bae88c893 100644 --- a/Projects/UOContent/Items/Weapons/Throwing/Boomerang.cs +++ b/Projects/UOContent/Items/Weapons/Throwing/Boomerang.cs @@ -6,12 +6,9 @@ namespace Server.Items; public partial class Boomerang : BaseThrown { [Constructible] - public Boomerang() : base(0x8FF) - { - Weight = 4.0; - Layer = Layer.OneHanded; - } + public Boomerang() : base(0x8FF) => Layer = Layer.OneHanded; + public override double DefaultWeight => 4.0; public override int MinThrowRange => 4; public override WeaponAbility PrimaryAbility => WeaponAbility.MysticArc; diff --git a/Projects/UOContent/Items/Weapons/Throwing/Cyclone.cs b/Projects/UOContent/Items/Weapons/Throwing/Cyclone.cs index 889d1e1fb..f2b6812c1 100644 --- a/Projects/UOContent/Items/Weapons/Throwing/Cyclone.cs +++ b/Projects/UOContent/Items/Weapons/Throwing/Cyclone.cs @@ -6,12 +6,9 @@ namespace Server.Items; public partial class Cyclone : BaseThrown { [Constructible] - public Cyclone() : base(0x901) - { - Weight = 6.0; - Layer = Layer.OneHanded; - } + public Cyclone() : base(0x901) => Layer = Layer.OneHanded; + public override double DefaultWeight => 6.0; public override int MinThrowRange => 6; public override WeaponAbility PrimaryAbility => WeaponAbility.MovingShot; diff --git a/Projects/UOContent/Items/Weapons/Throwing/SoulGlaive.cs b/Projects/UOContent/Items/Weapons/Throwing/SoulGlaive.cs index 724053a39..434d0c166 100644 --- a/Projects/UOContent/Items/Weapons/Throwing/SoulGlaive.cs +++ b/Projects/UOContent/Items/Weapons/Throwing/SoulGlaive.cs @@ -6,12 +6,9 @@ namespace Server.Items; public partial class SoulGlaive : BaseThrown { [Constructible] - public SoulGlaive() : base(0x090A) - { - Weight = 8.0; - Layer = Layer.OneHanded; - } + public SoulGlaive() : base(0x090A) => Layer = Layer.OneHanded; + public override double DefaultWeight => 8.0; public override int MinThrowRange => 8; public override WeaponAbility PrimaryAbility => WeaponAbility.ArmorIgnore; diff --git a/Projects/UOContent/Migrations/Server.Items.TreasureMapChest.v2.json b/Projects/UOContent/Migrations/Server.Items.TreasureMapChest.v2.json index 6510e836b..e44d7e19a 100644 --- a/Projects/UOContent/Migrations/Server.Items.TreasureMapChest.v2.json +++ b/Projects/UOContent/Migrations/Server.Items.TreasureMapChest.v2.json @@ -7,6 +7,7 @@ "type": "System.Collections.Generic.List\u003CServer.Mobile\u003E", "rule": "ListMigrationRule", "ruleArguments": [ + "@Tidy", "Server.Mobile", "SerializableInterfaceMigrationRule" ] diff --git a/Projects/UOContent/Migrations/Server.Mobiles.HarrowerTentacles.v0.json b/Projects/UOContent/Migrations/Server.Mobiles.HarrowerTentacles.v0.json index 0866656e3..3c76d4e00 100644 --- a/Projects/UOContent/Migrations/Server.Mobiles.HarrowerTentacles.v0.json +++ b/Projects/UOContent/Migrations/Server.Mobiles.HarrowerTentacles.v0.json @@ -4,7 +4,7 @@ "properties": [ { "name": "Harrower", - "type": "Server.Mobile", + "type": "Server.Mobiles.Harrower", "rule": "SerializableInterfaceMigrationRule" } ] diff --git a/Projects/UOContent/Migrations/Server.Systems.JailSystem.JailRecord.v0.json b/Projects/UOContent/Migrations/Server.Systems.JailSystem.JailRecord.v0.json new file mode 100644 index 000000000..acbdc3297 --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Systems.JailSystem.JailRecord.v0.json @@ -0,0 +1,43 @@ +{ + "version": 0, + "type": "Server.Systems.JailSystem.JailRecord", + "properties": [ + { + "name": "JailCount", + "type": "int", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "LastJailed", + "type": "System.DateTime", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "JailEndTime", + "type": "System.DateTime", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "LastJailReason", + "type": "string", + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "JailedBy", + "type": "Server.Mobile", + "rule": "SerializableInterfaceMigrationRule" + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Misc/ShardPoller.cs b/Projects/UOContent/Misc/ShardPoller.cs index 682003c58..0fb924ab9 100644 --- a/Projects/UOContent/Misc/ShardPoller.cs +++ b/Projects/UOContent/Misc/ShardPoller.cs @@ -397,7 +397,7 @@ public class ShardPollGump : Gump if (editing) { - AddHtml(22, 22, 294, 20, $"{totalVotes} total".Color(LabelColor32)); + AddHtml(22, 22, 294, 20, Html.Color($"{totalVotes} total", LabelColor32)); AddButton(287, 23, 0x2622, 0x2623, 2); } diff --git a/Projects/UOContent/Mobiles/AI/AIControlMobileTarget.cs b/Projects/UOContent/Mobiles/AI/AIControlMobileTarget.cs index 063fc5221..f14b46792 100644 --- a/Projects/UOContent/Mobiles/AI/AIControlMobileTarget.cs +++ b/Projects/UOContent/Mobiles/AI/AIControlMobileTarget.cs @@ -6,7 +6,7 @@ namespace Server.Targets; public class AIControlMobileTarget : Target { - private readonly List m_List; + private readonly List _list; public AIControlMobileTarget(BaseAI ai, OrderType order) : base( -1, @@ -14,19 +14,17 @@ public class AIControlMobileTarget : Target order == OrderType.Attack ? TargetFlags.Harmful : TargetFlags.None ) { - m_List = new List(); + _list = [ai]; Order = order; - - AddAI(ai); } public OrderType Order { get; } public void AddAI(BaseAI ai) { - if (!m_List.Contains(ai)) + if (!_list.Contains(ai)) { - m_List.Add(ai); + _list.Add(ai); } } @@ -34,10 +32,10 @@ public class AIControlMobileTarget : Target { if (o is Mobile m) { - for (var i = 0; i < m_List.Count; ++i) + for (var i = 0; i < _list.Count; ++i) { - m_List[i].EndPickTarget(from, m, Order); + _list[i].EndPickTarget(from, m, Order); } } } -} \ No newline at end of file +} diff --git a/Projects/UOContent/Mobiles/AI/AnimalAI.cs b/Projects/UOContent/Mobiles/AI/AnimalAI.cs index 8e3da75df..5107b57fe 100644 --- a/Projects/UOContent/Mobiles/AI/AnimalAI.cs +++ b/Projects/UOContent/Mobiles/AI/AnimalAI.cs @@ -19,25 +19,19 @@ public class AnimalAI : BaseAI { // New, only flee @ 10% - var hitPercent = (double)m_Mobile.Hits / m_Mobile.HitsMax; + var hitPercent = (double)Mobile.Hits / Mobile.HitsMax; - if (!m_Mobile.Summoned && !m_Mobile.Controlled && hitPercent < 0.1 && m_Mobile.CanFlee) // Less than 10% health + if (!Mobile.Summoned && !Mobile.Controlled && hitPercent < 0.1 && Mobile.CanFlee) // Less than 10% health { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I am low on health!"); - } + DebugSay("I am low on health!"); Action = ActionType.Flee; } - else if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + else if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name}, attacking"); - } + this.DebugSayFormatted($"I have detected {Mobile.FocusMob.Name}, attacking"); - m_Mobile.Combatant = m_Mobile.FocusMob; + Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; } else @@ -50,65 +44,50 @@ public class AnimalAI : BaseAI public override bool DoActionCombat() { - var combatant = m_Mobile.Combatant; + var combatant = Mobile.Combatant; - if (combatant == null || combatant.Deleted || combatant.Map != m_Mobile.Map || !combatant.Alive || + if (combatant == null || combatant.Deleted || combatant.Map != Mobile.Map || !combatant.Alive || combatant.IsDeadBondedPet) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("My combatant is gone!"); - } + DebugSay("My combatant is gone!"); Action = ActionType.Wander; return true; } - if (!WalkMobileRange(combatant, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight)) + if (!WalkMobileRange(combatant, 1, false, Mobile.RangeFight, Mobile.RangeFight)) { - if (m_Mobile.GetDistanceToSqrt(combatant) > m_Mobile.RangePerception + 1) + if (Mobile.GetDistanceToSqrt(combatant) > Mobile.RangePerception + 1) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I cannot find {combatant.Name}"); - } + this.DebugSayFormatted($"I cannot find {combatant.Name}"); Action = ActionType.Wander; return true; } - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I should be closer to {combatant.Name}"); - } + this.DebugSayFormatted($"I should be closer to {combatant.Name}"); } - else if (Core.TickCount - m_Mobile.LastMoveTime > 400) + else if (Core.TickCount - Mobile.LastMoveTime > 400) { - m_Mobile.Direction = m_Mobile.GetDirectionTo(combatant); + Mobile.Direction = Mobile.GetDirectionTo(combatant); } - if (!m_Mobile.Controlled && !m_Mobile.Summoned && m_Mobile.CanFlee) + if (!Mobile.Controlled && !Mobile.Summoned && Mobile.CanFlee) { - var hitPercent = (double)m_Mobile.Hits / m_Mobile.HitsMax; + var hitPercent = (double)Mobile.Hits / Mobile.HitsMax; if (hitPercent <= 0.1) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I am low on health!"); - } + DebugSay("I am low on health!"); Action = ActionType.Flee; return true; } } - if (m_Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) + if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I used my abilities on {combatant.Name}!"); - } + this.DebugSayFormatted($"I used my abilities on {combatant.Name}!"); } return true; @@ -116,30 +95,24 @@ public class AnimalAI : BaseAI public override bool DoActionBackoff() { - var hitPercent = (double)m_Mobile.Hits / m_Mobile.HitsMax; + var hitPercent = (double)Mobile.Hits / Mobile.HitsMax; - if (!m_Mobile.Summoned && !m_Mobile.Controlled && hitPercent < 0.1 && m_Mobile.CanFlee) // Less than 10% health + if (!Mobile.Summoned && !Mobile.Controlled && hitPercent < 0.1 && Mobile.CanFlee) // Less than 10% health { Action = ActionType.Flee; } - else if (AcquireFocusMob(m_Mobile.RangePerception * 2, FightMode.Closest, true, false, true)) + else if (AcquireFocusMob(Mobile.RangePerception * 2, FightMode.Closest, true, false, true)) { - if (WalkMobileRange(m_Mobile.FocusMob, 1, false, m_Mobile.RangePerception, m_Mobile.RangePerception * 2)) + if (WalkMobileRange(Mobile.FocusMob, 1, false, Mobile.RangePerception, Mobile.RangePerception * 2)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("Well, here I am safe"); - } + DebugSay("Well, here I am safe"); Action = ActionType.Wander; } } else { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I have lost my focus, lets relax"); - } + DebugSay("I have lost my focus, lets relax"); Action = ActionType.Wander; } @@ -149,9 +122,9 @@ public class AnimalAI : BaseAI public override bool DoActionFlee() { - AcquireFocusMob(m_Mobile.RangePerception * 2, m_Mobile.FightMode, true, false, true); + AcquireFocusMob(Mobile.RangePerception * 2, Mobile.FightMode, true, false, true); - m_Mobile.FocusMob ??= m_Mobile.Combatant; + Mobile.FocusMob ??= Mobile.Combatant; return base.DoActionFlee(); } diff --git a/Projects/UOContent/Mobiles/AI/ArcherAI.cs b/Projects/UOContent/Mobiles/AI/ArcherAI.cs index ad6788952..19e0375c4 100644 --- a/Projects/UOContent/Mobiles/AI/ArcherAI.cs +++ b/Projects/UOContent/Mobiles/AI/ArcherAI.cs @@ -11,19 +11,13 @@ public class ArcherAI : BaseAI public override bool DoActionWander() { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I have no combatant"); - } + DebugSay("I have no combatant"); - if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name} and I will attack"); - } + this.DebugSayFormatted($"I have detected {Mobile.FocusMob.Name} and I will attack"); - m_Mobile.Combatant = m_Mobile.FocusMob; + Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; } else @@ -36,62 +30,44 @@ public class ArcherAI : BaseAI public override bool DoActionCombat() { - var combatant = m_Mobile.Combatant; + var combatant = Mobile.Combatant; - if (combatant == null || combatant.Deleted || combatant.Map != m_Mobile.Map || !combatant.Alive || + if (combatant == null || combatant.Deleted || combatant.Map != Mobile.Map || !combatant.Alive || combatant.IsDeadBondedPet) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("My combatant is gone, so my guard is up"); - } + DebugSay("My combatant is gone, so my guard is up"); Action = ActionType.Guard; return true; } - if (Core.TickCount - m_Mobile.LastMoveTime > 1000 && !WalkMobileRange( - combatant, - 1, - true, - m_Mobile.RangeFight, - m_Mobile.Weapon.MaxRange - )) + if (!WalkMobileRange(combatant, 1, false, Mobile.RangeFight, Mobile.Weapon.MaxRange)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I am still not in range of {combatant.Name}"); - } + this.DebugSayFormatted($"I am still not in range of {combatant.Name}"); - if ((int)m_Mobile.GetDistanceToSqrt(combatant) > m_Mobile.RangePerception + 1) + if ((int)Mobile.GetDistanceToSqrt(combatant) > Mobile.RangePerception + 1) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I have lost {combatant.Name}"); - } + this.DebugSayFormatted($"I have lost {combatant.Name}"); - m_Mobile.Combatant = null; + Mobile.Combatant = null; Action = ActionType.Guard; return true; } } - else if (Core.TickCount - m_Mobile.LastMoveTime > 400) + else if (Core.TickCount - Mobile.LastMoveTime > 400) { - m_Mobile.Direction = m_Mobile.GetDirectionTo(combatant); + Mobile.Direction = Mobile.GetDirectionTo(combatant); } - if (m_Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) + if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I used my abilities on {combatant.Name}!"); - } + this.DebugSayFormatted($"I used my abilities on {combatant.Name}!"); return true; } // When we have no ammo, we flee - var pack = m_Mobile.Backpack; + var pack = Mobile.Backpack; if (pack?.FindItemByType() == null) { @@ -100,10 +76,10 @@ public class ArcherAI : BaseAI } // At 20% we should check if we must leave - if (m_Mobile.Combatant != null && m_Mobile.Hits < m_Mobile.HitsMax * 20 / 100 && m_Mobile.CanFlee) + if (Mobile.Combatant != null && Mobile.Hits < Mobile.HitsMax * 20 / 100 && Mobile.CanFlee) { // 10% to flee + the diff of hits - var fleeChance = 10 + Math.Max(0, m_Mobile.Combatant.Hits - m_Mobile.Hits); + var fleeChance = 10 + Math.Max(0, Mobile.Combatant.Hits - Mobile.Hits); if (Utility.Random(0, 100) > fleeChance) { @@ -116,14 +92,11 @@ public class ArcherAI : BaseAI public override bool DoActionGuard() { - if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name}, attacking"); - } + this.DebugSayFormatted($"I have detected {Mobile.FocusMob.Name}, attacking"); - m_Mobile.Combatant = m_Mobile.FocusMob; + Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; } else diff --git a/Projects/UOContent/Mobiles/AI/BaseAI.cs b/Projects/UOContent/Mobiles/AI/BaseAI.cs deleted file mode 100644 index a77e36d8f..000000000 --- a/Projects/UOContent/Mobiles/AI/BaseAI.cs +++ /dev/null @@ -1,3308 +0,0 @@ -using System; -using System.Collections.Generic; -using Server.Collections; -using Server.ContextMenus; -using Server.Engines.Quests.Necro; -using Server.Engines.Spawners; -using Server.Engines.Virtues; -using Server.Factions; -using Server.Gumps; -using Server.Items; -using Server.Network; -using Server.Spells; -using Server.Spells.Spellweaving; -using Server.Targets; -using MoveImpl = Server.Movement.MovementImpl; - -namespace Server.Mobiles; - -public enum AIType -{ - AI_Use_Default, - AI_Melee, - AI_Animal, - AI_Archer, - AI_Healer, - AI_Vendor, - AI_Mage, - AI_Berserk, - AI_Predator, - AI_Thief -} - -public enum ActionType -{ - Wander, - Combat, - Guard, - Flee, - Backoff, - Interact -} - -public abstract class BaseAI -{ - // How many milliseconds until our next move can we consider it ok to move without deferring/blocking. - private const int FuzzyTimeUntilNextMove = 24; - private static readonly SkillName[] m_KeywordTable = - { - SkillName.Parry, - SkillName.Healing, - SkillName.Hiding, - SkillName.Stealing, - SkillName.Alchemy, - SkillName.AnimalLore, - SkillName.ItemID, - SkillName.ArmsLore, - SkillName.Begging, - SkillName.Blacksmith, - SkillName.Fletching, - SkillName.Peacemaking, - SkillName.Camping, - SkillName.Carpentry, - SkillName.Cartography, - SkillName.Cooking, - SkillName.DetectHidden, - SkillName.Discordance, // ?? - SkillName.EvalInt, - SkillName.Fishing, - SkillName.Provocation, - SkillName.Lockpicking, - SkillName.Magery, - SkillName.MagicResist, - SkillName.Tactics, - SkillName.Snooping, - SkillName.RemoveTrap, - SkillName.Musicianship, - SkillName.Poisoning, - SkillName.Archery, - SkillName.SpiritSpeak, - SkillName.Tailoring, - SkillName.AnimalTaming, - SkillName.TasteID, - SkillName.Tinkering, - SkillName.Veterinary, - SkillName.Forensics, - SkillName.Herding, - SkillName.Tracking, - SkillName.Stealth, - SkillName.Inscribe, - SkillName.Swords, - SkillName.Macing, - SkillName.Fencing, - SkillName.Wrestling, - SkillName.Lumberjacking, - SkillName.Mining, - SkillName.Meditation - }; - - protected ActionType m_Action; - - public readonly BaseCreature m_Mobile; - - private long m_NextDetectHidden; - private long m_NextStopGuard; - - protected PathFollower m_Path; - public Timer m_Timer; - - public BaseAI(BaseCreature m) - { - m_Mobile = m; - - m_Timer = new AITimer(this); - - bool activate; - - if (!m.PlayerRangeSensitive) - { - activate = true; - } - else if (World.Loading) - { - activate = false; - } - else if (m.Map == null || m.Map == Map.Internal || !m.Map.GetSector(m.Location).Active) - { - activate = false; - } - else - { - activate = true; - } - - if (activate) - { - m_Timer.Start(); - } - - Action = ActionType.Wander; - } - - public ActionType Action - { - get => m_Action; - set - { - m_Action = value; - OnActionChanged(); - } - } - - public long NextMove { get; set; } - - public virtual bool CanDetectHidden => m_Mobile.Skills.DetectHidden.Value > 0; - - public virtual bool WasNamed(string speech) - { - var name = m_Mobile.Name; - - return name != null && speech.InsensitiveStartsWith(name); - } - - public virtual void GetContextMenuEntries(Mobile from, ref PooledRefList list) - { - if (!from.Alive || !m_Mobile.Controlled || !from.InRange(m_Mobile, 14)) - { - return; - } - - var isDeadPet = m_Mobile.IsDeadPet; - - if (from == m_Mobile.ControlMaster) - { - list.Add(new InternalEntry(6107, 14, OrderType.Guard, !isDeadPet)); // Command: Guard - list.Add(new InternalEntry(6108, 14, OrderType.Follow, true)); // Command: Follow - - if (m_Mobile.CanDrop) - { - list.Add(new InternalEntry(6109, 14, OrderType.Drop, !isDeadPet)); // Command: Drop - } - - list.Add(new InternalEntry(6111, 14, OrderType.Attack, !isDeadPet)); // Command: Kill - - list.Add(new InternalEntry(6112, 14, OrderType.Stop, true)); // Command: Stop - list.Add(new InternalEntry(6114, 14, OrderType.Stay, true)); // Command: Stay - - if (!m_Mobile.Summoned && m_Mobile is not GrizzledMare) - { - list.Add(new InternalEntry(6110, 14, OrderType.Friend, true)); // Add Friend - list.Add(new InternalEntry(6099, 14, OrderType.Unfriend, true)); // Remove Friend - list.Add(new InternalEntry(6113, 14, OrderType.Transfer, !isDeadPet)); // Transfer - } - - list.Add(new InternalEntry(6118, 14, OrderType.Release, true)); // Release - } - else if (m_Mobile.IsPetFriend(from)) - { - list.Add(new InternalEntry(6108, 14, OrderType.Follow, true)); // Command: Follow - list.Add(new InternalEntry(6112, 14, OrderType.Stop, !isDeadPet)); // Command: Stop - list.Add(new InternalEntry(6114, 14, OrderType.Stay, true)); // Command: Stay - } - } - - public virtual void BeginPickTarget(Mobile from, OrderType order) - { - if (m_Mobile.Deleted || !m_Mobile.Controlled || !from.InRange(m_Mobile, 14) || from.Map != m_Mobile.Map) - { - return; - } - - var isOwner = from == m_Mobile.ControlMaster; - var isFriend = !isOwner && m_Mobile.IsPetFriend(from); - - if (!isOwner && !isFriend) - { - return; - } - - if (isFriend && order != OrderType.Follow && order != OrderType.Stay && order != OrderType.Stop) - { - return; - } - - if (from.Target == null) - { - if (order == OrderType.Transfer) - { - from.SendLocalizedMessage(502038); // Click on the person to transfer ownership to. - } - else if (order == OrderType.Friend) - { - from.SendLocalizedMessage(502020); // Click on the player whom you wish to make a co-owner. - } - else if (order == OrderType.Unfriend) - { - from.SendLocalizedMessage(1070948); // Click on the player whom you wish to remove as a co-owner. - } - - from.Target = new AIControlMobileTarget(this, order); - } - else if (from.Target is AIControlMobileTarget t) - { - if (t.Order == order) - { - t.AddAI(this); - } - } - } - - public virtual void OnAggressiveAction(Mobile aggressor) - { - var currentCombat = m_Mobile.Combatant; - - if (currentCombat != null && !aggressor.Hidden && currentCombat != aggressor && - m_Mobile.GetDistanceToSqrt(currentCombat) > m_Mobile.GetDistanceToSqrt(aggressor)) - { - m_Mobile.Combatant = aggressor; - } - } - - public virtual void EndPickTarget(Mobile from, Mobile target, OrderType order) - { - if (m_Mobile.Deleted || !m_Mobile.Controlled || !from.InRange(m_Mobile, 14) || from.Map != m_Mobile.Map || - !from.CheckAlive()) - { - return; - } - - var isOwner = from == m_Mobile.ControlMaster; - var isFriend = !isOwner && m_Mobile.IsPetFriend(from); - - if (!isOwner && !isFriend) - { - return; - } - - if (isFriend && order != OrderType.Follow && order != OrderType.Stay && order != OrderType.Stop) - { - return; - } - - if (order == OrderType.Attack) - { - if (target is BaseCreature creature && creature.IsScaryToPets && m_Mobile.IsScaredOfScaryThings) - { - m_Mobile.SayTo(from, "Your pet refuses to attack this creature!"); - return; - } - - if (SolenHelper.CheckRedFriendship(from) && - target is RedSolenInfiltratorQueen or RedSolenInfiltratorWarrior or RedSolenQueen or RedSolenWarrior or RedSolenWorker - || SolenHelper.CheckBlackFriendship(from) && - target is BlackSolenInfiltratorQueen or BlackSolenInfiltratorWarrior or BlackSolenQueen or BlackSolenWarrior or BlackSolenWorker) - { - from.SendAsciiMessage("You can not force your pet to attack a creature you are protected from."); - return; - } - - if (target is BaseFactionGuard) - { - m_Mobile.SayTo(from, "Your pet refuses to attack the guard."); - return; - } - } - - if (m_Mobile.CheckControlChance(from)) - { - m_Mobile.ControlTarget = target; - m_Mobile.ControlOrder = order; - } - } - - public virtual bool HandlesOnSpeech(Mobile from) - { - if (from.AccessLevel >= AccessLevel.GameMaster) - { - return true; - } - - if (from.Alive && m_Mobile.Controlled && m_Mobile.Commandable && - (from == m_Mobile.ControlMaster || m_Mobile.IsPetFriend(from))) - { - return true; - } - - return from.Alive && from.InRange(m_Mobile.Location, 3) && m_Mobile.IsHumanInTown(); - } - - public virtual void OnSpeech(SpeechEventArgs e) - { - if (e.Mobile.Alive && e.Mobile.InRange(m_Mobile.Location, 3) && m_Mobile.IsHumanInTown()) - { - if (e.HasKeyword(0x9D) && WasNamed(e.Speech)) // *move* - { - if (m_Mobile.Combatant != null) - { - // I am too busy fighting to deal with thee! - m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501482); - } - else - { - // Excuse me? - m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501516); - WalkRandomInHome(2, 2, 1); - } - } - else if (e.HasKeyword(0x9E) && WasNamed(e.Speech)) // *time* - { - if (m_Mobile.Combatant != null) - { - // I am too busy fighting to deal with thee! - m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501482); - } - else - { - Clock.GetTime(m_Mobile, out var generalNumber, out _); - - m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, generalNumber); - } - } - else if (e.HasKeyword(0x6C) && WasNamed(e.Speech)) // *train - { - if (m_Mobile.Combatant != null) - { - // I am too busy fighting to deal with thee! - m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501482); - } - else - { - var foundSomething = false; - - var ourSkills = m_Mobile.Skills; - var theirSkills = e.Mobile.Skills; - - for (var i = 0; i < ourSkills.Length && i < theirSkills.Length; ++i) - { - var skill = ourSkills[i]; - var theirSkill = theirSkills[i]; - - if (skill?.Base >= 60.0 && m_Mobile.CheckTeach(skill.SkillName, e.Mobile)) - { - var toTeach = skill.Base / 3.0; - - if (toTeach > 42.0) - { - toTeach = 42.0; - } - - if (toTeach > theirSkill.Base) - { - var number = 1043059 + i; - - if (number > 1043107) - { - continue; - } - - if (!foundSomething) - { - m_Mobile.Say(1043058); // I can train the following: - } - - m_Mobile.Say(number); - - foundSomething = true; - } - } - } - - if (!foundSomething) - { - m_Mobile.Say(501505); // Alas, I cannot teach thee anything. - } - } - } - else - { - var toTrain = (SkillName)(-1); - - for (var i = 0; toTrain == (SkillName)(-1) && i < e.Keywords.Length; ++i) - { - var keyword = e.Keywords[i]; - - if (keyword == 0x154) - { - toTrain = SkillName.Anatomy; - } - else if (keyword >= 0x6D && keyword <= 0x9C) - { - var index = keyword - 0x6D; - - if (index >= 0 && index < m_KeywordTable.Length) - { - toTrain = m_KeywordTable[index]; - } - } - } - - if (toTrain != (SkillName)(-1) && WasNamed(e.Speech)) - { - if (m_Mobile.Combatant != null) - { - // I am too busy fighting to deal with thee! - m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501482); - } - else - { - var skills = m_Mobile.Skills; - var skill = skills[toTrain]; - - if (skill == null || skill.Base < 60.0 || !m_Mobile.CheckTeach(toTrain, e.Mobile)) - { - m_Mobile.Say(501507); // 'Tis not something I can teach thee of. - } - else - { - m_Mobile.Teach(toTrain, e.Mobile, 0, false); - } - } - } - } - } - - if (m_Mobile.Controlled && m_Mobile.Commandable) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("Listening..."); - } - - var isOwner = e.Mobile == m_Mobile.ControlMaster; - var isFriend = !isOwner && m_Mobile.IsPetFriend(e.Mobile); - - if (e.Mobile.Alive && (isOwner || isFriend)) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("It's from my master"); - } - - var keywords = e.Keywords; - var speech = e.Speech; - - // First, check the all* - for (var i = 0; i < keywords.Length; ++i) - { - var keyword = keywords[i]; - - switch (keyword) - { - case 0x164: // all come - { - if (!isOwner) - { - break; - } - - if (m_Mobile.CheckControlChance(e.Mobile)) - { - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.Come; - } - - return; - } - case 0x165: // all follow - { - BeginPickTarget(e.Mobile, OrderType.Follow); - return; - } - case 0x166: // all guard - case 0x16B: // all guard me - { - if (!isOwner) - { - break; - } - - if (m_Mobile.CheckControlChance(e.Mobile)) - { - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.Guard; - } - - return; - } - case 0x167: // all stop - { - if (m_Mobile.CheckControlChance(e.Mobile)) - { - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.Stop; - } - - return; - } - case 0x168: // all kill - case 0x169: // all attack - { - if (!isOwner) - { - break; - } - - BeginPickTarget(e.Mobile, OrderType.Attack); - return; - } - case 0x16C: // all follow me - { - if (m_Mobile.CheckControlChance(e.Mobile)) - { - m_Mobile.ControlTarget = e.Mobile; - m_Mobile.ControlOrder = OrderType.Follow; - } - - return; - } - case 0x170: // all stay - { - if (m_Mobile.CheckControlChance(e.Mobile)) - { - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.Stay; - } - - return; - } - } - } - - // No all*, so check *command - for (var i = 0; i < keywords.Length; ++i) - { - var keyword = keywords[i]; - - switch (keyword) - { - case 0x155: // *come - { - if (!isOwner) - { - break; - } - - if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) - { - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.Come; - } - - return; - } - case 0x156: // *drop - { - if (!isOwner) - { - break; - } - - if (!m_Mobile.IsDeadPet && !m_Mobile.Summoned && WasNamed(speech) && - m_Mobile.CheckControlChance(e.Mobile)) - { - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.Drop; - } - - return; - } - case 0x15A: // *follow - { - if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) - { - BeginPickTarget(e.Mobile, OrderType.Follow); - } - - return; - } - case 0x15B: // *friend - { - if (!isOwner) - { - break; - } - - if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) - { - if (m_Mobile.Summoned || m_Mobile is GrizzledMare) - { - // Summoned creatures are loyal only to their summoners. - e.Mobile.SendLocalizedMessage(1005481); - } - else if (e.Mobile.HasTrade) - { - // You cannot friend a pet with a trade pending - e.Mobile.SendLocalizedMessage(1070947); - } - else - { - BeginPickTarget(e.Mobile, OrderType.Friend); - } - } - - return; - } - case 0x15C: // *guard - { - if (!isOwner) - { - break; - } - - if (!m_Mobile.IsDeadPet && WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) - { - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.Guard; - } - - return; - } - case 0x15D: // *kill - case 0x15E: // *attack - { - if (!isOwner) - { - break; - } - - if (!m_Mobile.IsDeadPet && WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) - { - BeginPickTarget(e.Mobile, OrderType.Attack); - } - - return; - } - case 0x15F: // *patrol - { - if (!isOwner) - { - break; - } - - if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) - { - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.Patrol; - } - - return; - } - case 0x161: // *stop - { - if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) - { - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.Stop; - } - - return; - } - case 0x163: // *follow me - { - if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) - { - m_Mobile.ControlTarget = e.Mobile; - m_Mobile.ControlOrder = OrderType.Follow; - } - - return; - } - case 0x16D: // *release - { - if (!isOwner) - { - break; - } - - if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) - { - if (!m_Mobile.Summoned) - { - e.Mobile.SendGump(new ConfirmReleaseGump(e.Mobile, m_Mobile)); - } - else - { - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.Release; - } - } - - return; - } - case 0x16E: // *transfer - { - if (!isOwner) - { - break; - } - - if (!m_Mobile.IsDeadPet && WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) - { - if (m_Mobile.Summoned || m_Mobile is GrizzledMare) - { - // You cannot transfer ownership of a summoned creature. - e.Mobile.SendLocalizedMessage(1005487); - } - else if (e.Mobile.HasTrade) - { - // You cannot transfer a pet with a trade pending - e.Mobile.SendLocalizedMessage(1010507); - } - else - { - BeginPickTarget(e.Mobile, OrderType.Transfer); - } - } - - return; - } - case 0x16F: // *stay - { - if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) - { - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.Stay; - } - - return; - } - } - } - } - } - else - { - if (e.Mobile.AccessLevel >= AccessLevel.GameMaster) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("It's from a GM"); - } - - if (m_Mobile.FindMyName(e.Speech, true)) - { - var str = e.Speech.Split(' '); - int i; - - for (i = 0; i < str.Length; i++) - { - var word = str[i]; - - if (word.InsensitiveEquals("obey")) - { - m_Mobile.SetControlMaster(e.Mobile); - - if (m_Mobile.Summoned) - { - m_Mobile.SummonMaster = e.Mobile; - } - - return; - } - } - } - } - } - } - - public virtual bool Think() - { - if (m_Mobile.Deleted) - { - return false; - } - - if (CheckFlee()) - { - return true; - } - - switch (Action) - { - case ActionType.Wander: - { - m_Mobile.OnActionWander(); - return DoActionWander(); - } - - case ActionType.Combat: - { - m_Mobile.OnActionCombat(); - return DoActionCombat(); - } - - case ActionType.Guard: - { - m_Mobile.OnActionGuard(); - return DoActionGuard(); - } - - case ActionType.Flee: - { - m_Mobile.OnActionFlee(); - return DoActionFlee(); - } - - case ActionType.Interact: - { - m_Mobile.OnActionInteract(); - return DoActionInteract(); - } - - case ActionType.Backoff: - { - m_Mobile.OnActionBackoff(); - return DoActionBackoff(); - } - - default: - { - return false; - } - } - } - - public virtual void OnActionChanged() - { - switch (Action) - { - case ActionType.Wander: - { - m_Mobile.Warmode = false; - m_Mobile.Combatant = null; - m_Mobile.FocusMob = null; - m_Mobile.SetCurrentSpeedToPassive(); - break; - } - - case ActionType.Combat: - { - m_Mobile.Warmode = true; - m_Mobile.FocusMob = null; - m_Mobile.SetCurrentSpeedToActive(); - break; - } - - case ActionType.Guard: - { - m_Mobile.Warmode = true; - m_Mobile.FocusMob = null; - m_Mobile.Combatant = null; - m_NextStopGuard = Core.TickCount + (int)TimeSpan.FromSeconds(10).TotalMilliseconds; - m_Mobile.SetCurrentSpeedToActive(); - break; - } - - case ActionType.Flee: - { - m_Mobile.Warmode = true; - m_Mobile.FocusMob = null; - m_Mobile.SetCurrentSpeedToActive(); - break; - } - - case ActionType.Interact: - { - m_Mobile.Warmode = false; - m_Mobile.SetCurrentSpeedToPassive(); - break; - } - - case ActionType.Backoff: - { - m_Mobile.Warmode = false; - m_Mobile.SetCurrentSpeedToPassive(); - break; - } - } - } - - public virtual bool OnAtWayPoint() => true; - - public virtual bool DoActionWander() - { - if (CheckHerding()) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("Praise the shepherd!"); - } - } - else if (m_Mobile.CurrentWayPoint != null) - { - var point = m_Mobile.CurrentWayPoint; - if ((point.X != m_Mobile.Location.X || point.Y != m_Mobile.Location.Y) && point.Map == m_Mobile.Map && - point.Parent == null && !point.Deleted) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I will move towards my waypoint."); - } - - DoMove(m_Mobile.GetDirectionTo(m_Mobile.CurrentWayPoint)); - } - else if (OnAtWayPoint()) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I will go to the next waypoint"); - } - - m_Mobile.CurrentWayPoint = point.NextPoint; - if (point.NextPoint?.Deleted == true) - { - m_Mobile.CurrentWayPoint = point.NextPoint = point.NextPoint.NextPoint; - } - } - } - else if (m_Mobile.IsAnimatedDead) - { - // animated dead follow their master - var master = m_Mobile.SummonMaster; - - if (master != null && master.Map == m_Mobile.Map && master.InRange(m_Mobile, m_Mobile.RangePerception)) - { - MoveTo(master, false, 1); - } - else - { - WalkRandomInHome(2, 2, 1); - } - } - else if (CheckMove() && CanMoveNow(out _) && !m_Mobile.CheckIdle()) - { - WalkRandomInHome(2, 2, 1); - } - - if (m_Mobile.Combatant?.Deleted == false && m_Mobile.Combatant.Alive && - !m_Mobile.Combatant.IsDeadBondedPet) - { - m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant); - } - - return true; - } - - public virtual bool DoActionCombat() - { - if (Core.AOS && CheckHerding()) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("Praise the shepherd!"); - } - - return true; - } - - var combatant = m_Mobile.Combatant; - - if (combatant == null || combatant.Deleted || combatant.Map != m_Mobile.Map || !combatant.Alive || - combatant.IsDeadBondedPet) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("My combatant is gone!"); - } - - Action = ActionType.Wander; - return true; - } - - m_Mobile.Direction = m_Mobile.GetDirectionTo(combatant); - if (m_Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I used my abilities on {combatant.Name}!"); - } - } - - return true; - } - - public virtual bool DoActionGuard() - { - if (Core.AOS && CheckHerding()) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("Praise the shepherd!"); - } - } - else if (Core.TickCount - m_NextStopGuard < 0) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I am on guard"); - } - // m_Mobile.Turn( Utility.Random(0, 2) - 1 ); - } - else - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I stopped being on guard"); - } - - Action = ActionType.Wander; - } - - return true; - } - - public virtual bool DoActionFlee() - { - var from = m_Mobile.FocusMob; - - if (from?.Deleted != false || from.Map != m_Mobile.Map) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I have lost him"); - } - - Action = ActionType.Guard; - return true; - } - - if (WalkMobileRange(from, 1, true, m_Mobile.RangePerception * 2, m_Mobile.RangePerception * 3)) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I have fled"); - } - - Action = ActionType.Guard; - return true; - } - - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I am fleeing!"); - } - - return true; - } - - public virtual bool DoActionInteract() => true; - - public virtual bool DoActionBackoff() => true; - - public virtual bool Obey() => - !m_Mobile.Deleted && m_Mobile.ControlOrder switch - { - OrderType.None => DoOrderNone(), - OrderType.Come => DoOrderCome(), - OrderType.Drop => DoOrderDrop(), - OrderType.Friend => DoOrderFriend(), - OrderType.Unfriend => DoOrderUnfriend(), - OrderType.Guard => DoOrderGuard(), - OrderType.Attack => DoOrderAttack(), - OrderType.Patrol => DoOrderPatrol(), - OrderType.Release => DoOrderRelease(), - OrderType.Stay => DoOrderStay(), - OrderType.Stop => DoOrderStop(), - OrderType.Follow => DoOrderFollow(), - OrderType.Transfer => DoOrderTransfer(), - _ => false - }; - - public virtual void OnCurrentOrderChanged() - { - if (m_Mobile.Deleted || m_Mobile.ControlMaster?.Deleted != false) - { - return; - } - - switch (m_Mobile.ControlOrder) - { - case OrderType.None: - { - m_Mobile.ControlMaster.RevealingAction(); - m_Mobile.Home = m_Mobile.Location; - m_Mobile.SetCurrentSpeedToPassive(); - m_Mobile.PlaySound(m_Mobile.GetIdleSound()); - m_Mobile.Warmode = false; - m_Mobile.Combatant = null; - break; - } - - case OrderType.Come: - { - m_Mobile.ControlMaster.RevealingAction(); - m_Mobile.SetCurrentSpeedToActive(); - m_Mobile.PlaySound(m_Mobile.GetIdleSound()); - m_Mobile.Warmode = false; - m_Mobile.Combatant = null; - break; - } - - case OrderType.Drop: - { - m_Mobile.ControlMaster.RevealingAction(); - m_Mobile.SetCurrentSpeedToPassive(); - m_Mobile.PlaySound(m_Mobile.GetIdleSound()); - m_Mobile.Warmode = false; - m_Mobile.Combatant = null; - break; - } - - case OrderType.Friend: - case OrderType.Unfriend: - { - m_Mobile.ControlMaster.RevealingAction(); - break; - } - - case OrderType.Guard: - { - m_Mobile.ControlMaster.RevealingAction(); - m_Mobile.SetCurrentSpeedToActive(); - m_Mobile.PlaySound(m_Mobile.GetIdleSound()); - m_Mobile.Warmode = true; - m_Mobile.Combatant = null; - m_Mobile.ControlMaster.SendLocalizedMessage(1049671, m_Mobile.Name); // ~1_PETNAME~ is now guarding you. - break; - } - - case OrderType.Attack: - { - m_Mobile.ControlMaster.RevealingAction(); - m_Mobile.SetCurrentSpeedToActive(); - m_Mobile.PlaySound(m_Mobile.GetIdleSound()); - - m_Mobile.Warmode = true; - m_Mobile.Combatant = null; - break; - } - - case OrderType.Patrol: - { - m_Mobile.ControlMaster.RevealingAction(); - m_Mobile.SetCurrentSpeedToActive(); - m_Mobile.PlaySound(m_Mobile.GetIdleSound()); - m_Mobile.Warmode = false; - m_Mobile.Combatant = null; - break; - } - - case OrderType.Release: - { - m_Mobile.ControlMaster.RevealingAction(); - m_Mobile.SetCurrentSpeedToPassive(); - m_Mobile.PlaySound(m_Mobile.GetIdleSound()); - m_Mobile.Warmode = false; - m_Mobile.Combatant = null; - break; - } - - case OrderType.Stay: - { - m_Mobile.ControlMaster.RevealingAction(); - m_Mobile.SetCurrentSpeedToPassive(); - m_Mobile.PlaySound(m_Mobile.GetIdleSound()); - m_Mobile.Warmode = false; - m_Mobile.Combatant = null; - break; - } - - case OrderType.Stop: - { - m_Mobile.ControlMaster.RevealingAction(); - m_Mobile.Home = m_Mobile.Location; - m_Mobile.SetCurrentSpeedToPassive(); - m_Mobile.PlaySound(m_Mobile.GetIdleSound()); - m_Mobile.Warmode = false; - m_Mobile.Combatant = null; - break; - } - - case OrderType.Follow: - { - m_Mobile.ControlMaster.RevealingAction(); - m_Mobile.SetCurrentSpeedToActive(); - m_Mobile.PlaySound(m_Mobile.GetIdleSound()); - - m_Mobile.Warmode = false; - m_Mobile.Combatant = null; - break; - } - - case OrderType.Transfer: - { - m_Mobile.ControlMaster.RevealingAction(); - m_Mobile.SetCurrentSpeedToPassive(); - m_Mobile.PlaySound(m_Mobile.GetIdleSound()); - - m_Mobile.Warmode = false; - m_Mobile.Combatant = null; - break; - } - } - } - - public virtual bool DoOrderNone() - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I have no order"); - } - - WalkRandomInHome(3, 2, 1); - - if (m_Mobile.Combatant?.Deleted == false && m_Mobile.Combatant.Alive && - !m_Mobile.Combatant.IsDeadBondedPet) - { - m_Mobile.Warmode = true; - m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant); - } - else - { - m_Mobile.Warmode = false; - } - - return true; - } - - public virtual bool DoOrderCome() - { - if (m_Mobile.ControlMaster?.Deleted != false) - { - return true; - } - - var iCurrDist = (int)m_Mobile.GetDistanceToSqrt(m_Mobile.ControlMaster); - - if (iCurrDist > m_Mobile.RangePerception) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I have lost my master. I stay here"); - } - - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.None; - } - else - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("My master told me come"); - } - - // Not exactly OSI style, but better than nothing. - var bRun = iCurrDist > 5; - - if (WalkMobileRange(m_Mobile.ControlMaster, 1, bRun, 0, 1)) - { - if (m_Mobile.Combatant?.Deleted == false && m_Mobile.Combatant.Alive && - !m_Mobile.Combatant.IsDeadBondedPet) - { - m_Mobile.Warmode = true; - // m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant, bRun); - } - else - { - m_Mobile.Warmode = false; - } - } - } - - return true; - } - - public virtual bool DoOrderDrop() - { - if (m_Mobile.IsDeadPet || !m_Mobile.CanDrop) - { - return true; - } - - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I drop my stuff for my master"); - } - - var pack = m_Mobile.Backpack; - - if (pack != null) - { - var list = pack.Items; - - for (var i = list.Count - 1; i >= 0; --i) - { - if (i < list.Count) - { - list[i].MoveToWorld(m_Mobile.Location, m_Mobile.Map); - } - } - } - - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.None; - - return true; - } - - public virtual bool CheckHerding() - { - var target = m_Mobile.TargetLocation; - - if (target == null) - { - return false; // Creature is not being herded - } - - var distance = m_Mobile.GetDistanceToSqrt(target); - - if (distance is not (< 1 or > 15)) - { - DoMove(m_Mobile.GetDirectionTo(target)); - return true; - } - - if (distance < 1 && target.X == 1076 && target.Y == 450 && m_Mobile is HordeMinionFamiliar) - { - if (m_Mobile.ControlMaster is PlayerMobile pm) - { - var qs = pm.Quest; - - if (qs is DarkTidesQuest) - { - var obj = qs.FindObjective(); - - if (obj?.Completed == false) - { - m_Mobile.AddToBackpack(new ScrollOfAbraxus()); - obj.Complete(); - } - } - } - } - - m_Mobile.TargetLocation = null; - return false; // At the target or too far away - } - - public virtual bool DoOrderFollow() - { - if (CheckHerding()) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("Praise the shepherd!"); - } - } - else if (m_Mobile.ControlTarget?.Deleted == false && m_Mobile.ControlTarget != m_Mobile) - { - var iCurrDist = (int)m_Mobile.GetDistanceToSqrt(m_Mobile.ControlTarget); - - if (iCurrDist > m_Mobile.RangePerception) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I have lost the one to follow. I stay here"); - } - - if (m_Mobile.Combatant?.Deleted == false && m_Mobile.Combatant.Alive && - !m_Mobile.Combatant.IsDeadBondedPet) - { - m_Mobile.Warmode = true; - m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant); - } - else - { - m_Mobile.Warmode = false; - } - } - else - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"My master told me to follow: {m_Mobile.ControlTarget.Name}"); - } - - // Not exactly OSI style, but better than nothing. - var bRun = iCurrDist > 5; - - if (WalkMobileRange(m_Mobile.ControlTarget, 1, bRun, 0, 1)) - { - if (m_Mobile.Combatant?.Deleted == false && m_Mobile.Combatant.Alive && - !m_Mobile.Combatant.IsDeadBondedPet) - { - m_Mobile.Warmode = true; - // m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant, bRun); - } - else - { - m_Mobile.Warmode = false; - if (Core.AOS) - { - m_Mobile.CurrentSpeed = 0.1; - } - } - } - } - } - else - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I have nobody to follow"); - } - - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.None; - } - - return true; - } - - public virtual bool DoOrderFriend() - { - var from = m_Mobile.ControlMaster; - var to = m_Mobile.ControlTarget; - - if (from?.Deleted != false || to?.Deleted != false || from == to || !to.Player) - { - m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 502039); // *looks confused* - } - else - { - var youngFrom = from is PlayerMobile mobile && mobile.Young; - var youngTo = to is PlayerMobile playerMobile && playerMobile.Young; - - if (youngFrom && !youngTo) - { - from.SendLocalizedMessage(502040); // As a young player, you may not friend pets to older players. - } - else if (!youngFrom && youngTo) - { - from.SendLocalizedMessage(502041); // As an older player, you may not friend pets to young players. - } - else if (from.CanBeBeneficial(to, true)) - { - NetState fromState = from.NetState, toState = to.NetState; - - if (fromState != null && toState != null) - { - if (from.HasTrade) - { - from.SendLocalizedMessage(1070947); // You cannot friend a pet with a trade pending - } - else if (to.HasTrade) - { - to.SendLocalizedMessage(1070947); // You cannot friend a pet with a trade pending - } - else if (m_Mobile.IsPetFriend(to)) - { - from.SendLocalizedMessage(1049691); // That person is already a friend. - } - else if (!m_Mobile.AllowNewPetFriend) - { - from.SendLocalizedMessage( - 1005482 - ); // Your pet does not seem to be interested in making new friends right now. - } - else - { - // ~1_NAME~ will now accept movement commands from ~2_NAME~. - from.SendLocalizedMessage(1049676, $"{m_Mobile.Name}\t{to.Name}"); - - /* ~1_NAME~ has granted you the ability to give orders to their pet ~2_PET_NAME~. - * This creature will now consider you as a friend. - */ - to.SendLocalizedMessage(1043246, $"{from.Name}\t{m_Mobile.Name}"); - - m_Mobile.AddPetFriend(to); - - m_Mobile.ControlTarget = to; - m_Mobile.ControlOrder = OrderType.Follow; - - return true; - } - } - } - } - - m_Mobile.ControlTarget = from; - m_Mobile.ControlOrder = OrderType.Follow; - - return true; - } - - public virtual bool DoOrderUnfriend() - { - var from = m_Mobile.ControlMaster; - var to = m_Mobile.ControlTarget; - - if (from?.Deleted != false || to?.Deleted != false || from == to || !to.Player) - { - m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 502039); // *looks confused* - } - else if (!m_Mobile.IsPetFriend(to)) - { - from.SendLocalizedMessage(1070953); // That person is not a friend. - } - else - { - // ~1_NAME~ will no longer accept movement commands from ~2_NAME~. - from.SendLocalizedMessage(1070951, $"{m_Mobile.Name}\t{to.Name}"); - - /* ~1_NAME~ has no longer granted you the ability to give orders to their pet ~2_PET_NAME~. - * This creature will no longer consider you as a friend. - */ - to.SendLocalizedMessage(1070952, $"{from.Name}\t{m_Mobile.Name}"); - - m_Mobile.RemovePetFriend(to); - } - - m_Mobile.ControlTarget = from; - m_Mobile.ControlOrder = OrderType.Follow; - - return true; - } - - public virtual bool DoOrderGuard() - { - if (m_Mobile.IsDeadPet) - { - return true; - } - - var controlMaster = m_Mobile.ControlMaster; - - if (controlMaster?.Deleted != false) - { - return true; - } - - var combatant = m_Mobile.Combatant; - - var aggressors = controlMaster.Aggressors; - - if (aggressors.Count > 0) - { - for (var i = 0; i < aggressors.Count; ++i) - { - var info = aggressors[i]; - var attacker = info.Attacker; - - if (attacker?.Deleted == false && - attacker.GetDistanceToSqrt(m_Mobile) <= m_Mobile.RangePerception) - { - if (combatant == null || attacker.GetDistanceToSqrt(controlMaster) < - combatant.GetDistanceToSqrt(controlMaster)) - { - combatant = attacker; - } - } - } - - if (combatant != null && m_Mobile.Debug) - { - m_Mobile.DebugSay("Crap, my master has been attacked! I will attack one of those bastards!"); - } - } - - if (combatant?.Deleted == false && combatant != m_Mobile && combatant != m_Mobile.ControlMaster && - combatant.Alive && !combatant.IsDeadBondedPet && m_Mobile.CanSee(combatant) && - m_Mobile.CanBeHarmful(combatant, false) && combatant.Map == m_Mobile.Map) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("Guarding from target..."); - } - - m_Mobile.Combatant = combatant; - m_Mobile.FocusMob = combatant; - Action = ActionType.Combat; - - /* - * We need to call Think() here or spell casting monsters will not use - * spells when guarding because their target is never processed. - */ - Think(); - } - else - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("Nothing to guard from"); - } - - m_Mobile.Warmode = false; - if (Core.AOS) - { - m_Mobile.CurrentSpeed = 0.1; - } - - WalkMobileRange(controlMaster, 1, false, 0, 1); - } - - return true; - } - - public virtual bool DoOrderAttack() - { - if (m_Mobile.IsDeadPet) - { - return true; - } - - if (m_Mobile.ControlTarget?.Deleted != false || m_Mobile.ControlTarget.Map != m_Mobile.Map || - !m_Mobile.ControlTarget.Alive || m_Mobile.ControlTarget.IsDeadBondedPet) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay( - "I think he might be dead. He's not anywhere around here at least. That's cool. I'm glad he's dead." - ); - } - - if (Core.AOS) - { - m_Mobile.ControlTarget = m_Mobile.ControlMaster; - m_Mobile.ControlOrder = OrderType.Follow; - } - else - { - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.None; - } - - if (m_Mobile.FightMode is FightMode.Closest or FightMode.Aggressor) - { - Mobile newCombatant = null; - var newScore = 0.0; - - foreach (var aggr in m_Mobile.GetMobilesInRange(m_Mobile.RangePerception)) - { - if (!m_Mobile.CanSee(aggr) || aggr.Combatant != m_Mobile) - { - continue; - } - - if (aggr.IsDeadBondedPet || !aggr.Alive) - { - continue; - } - - var aggrScore = m_Mobile.GetFightModeRanking(aggr, FightMode.Closest, false); - - if ((newCombatant == null || aggrScore > newScore) && m_Mobile.InLOS(aggr)) - { - newCombatant = aggr; - newScore = aggrScore; - } - } - - if (newCombatant != null) - { - m_Mobile.ControlTarget = newCombatant; - m_Mobile.ControlOrder = OrderType.Attack; - m_Mobile.Combatant = newCombatant; - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("But -that- is not dead. Here we go again..."); - } - - Think(); - } - } - } - else - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("Attacking target..."); - } - - Think(); - } - - return true; - } - - public virtual bool DoOrderPatrol() - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("This order is not yet coded"); - } - - return true; - } - - public virtual bool DoOrderRelease() - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I have been released"); - } - - m_Mobile.PlaySound(m_Mobile.GetAngerSound()); - - m_Mobile.SetControlMaster(null); - m_Mobile.SummonMaster = null; - - m_Mobile.BondingBegin = DateTime.MinValue; - m_Mobile.OwnerAbandonTime = DateTime.MinValue; - m_Mobile.IsBonded = false; - - var spawner = m_Mobile.Spawner; - - if (spawner != null && spawner.HomeLocation != Point3D.Zero) - { - m_Mobile.Home = spawner.HomeLocation; - m_Mobile.RangeHome = spawner.HomeRange; - } - - if (m_Mobile.DeleteOnRelease || m_Mobile.IsDeadPet) - { - m_Mobile.Delete(); - } - - m_Mobile.BeginDeleteTimer(); - - if (m_Mobile.CanDrop) - { - m_Mobile.DropBackpack(); - } - - return true; - } - - public virtual bool DoOrderStay() - { - if (m_Mobile.Debug) - { - if (CheckHerding()) - { - m_Mobile.DebugSay("Praise the shepherd!"); - } - else - { - m_Mobile.DebugSay("My master told me to stay"); - } - } - - return true; - } - - public virtual bool DoOrderStop() - { - if (m_Mobile.ControlMaster?.Deleted != false) - { - return true; - } - - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("My master told me to stop."); - } - - m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.ControlMaster); - m_Mobile.Home = m_Mobile.Location; - - m_Mobile.ControlTarget = null; - - if (Core.ML) - { - WalkRandomInHome(3, 2, 1); - } - else - { - m_Mobile.ControlOrder = OrderType.None; - } - - return true; - } - - public virtual bool DoOrderTransfer() - { - if (m_Mobile.IsDeadPet) - { - return true; - } - - var from = m_Mobile.ControlMaster; - var to = m_Mobile.ControlTarget; - - if (from?.Deleted == false && to?.Deleted == false && from != to && to.Player) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"Begin transfer with {to.Name}"); - } - - var youngFrom = from is PlayerMobile mobile && mobile.Young; - var youngTo = to is PlayerMobile playerMobile && playerMobile.Young; - - if (youngFrom && !youngTo) - { - from.SendLocalizedMessage(502051); // As a young player, you may not transfer pets to older players. - } - else if (!youngFrom && youngTo) - { - from.SendLocalizedMessage(502052); // As an older player, you may not transfer pets to young players. - } - else if (!m_Mobile.CanBeControlledBy(to)) - { - var args = $"{to.Name}\t{from.Name}\t "; - - from.SendLocalizedMessage( - 1043248, - args - ); // The pet refuses to be transferred because it will not obey ~1_NAME~.~3_BLANK~ - to.SendLocalizedMessage( - 1043249, - args - ); // The pet will not accept you as a master because it does not trust you.~3_BLANK~ - } - else if (!m_Mobile.CanBeControlledBy(from)) - { - var args = $"{to.Name}\t{from.Name}\t "; - - from.SendLocalizedMessage( - 1043250, - args - ); // The pet refuses to be transferred because it will not obey you sufficiently.~3_BLANK~ - to.SendLocalizedMessage( - 1043251, - args - ); // The pet will not accept you as a master because it does not trust ~2_NAME~.~3_BLANK~ - } - else if (TransferItem.IsInCombat(m_Mobile)) - { - from.SendMessage("You may not transfer a pet that has recently been in combat."); - to.SendMessage("The pet may not be transferred to you because it has recently been in combat."); - } - else - { - NetState fromState = from.NetState, toState = to.NetState; - - if (fromState != null && toState != null) - { - if (from.HasTrade) - { - from.SendLocalizedMessage(1010507); // You cannot transfer a pet with a trade pending - } - else if (to.HasTrade) - { - to.SendLocalizedMessage(1010507); // You cannot transfer a pet with a trade pending - } - else - { - fromState.AddTrade(toState).DropItem(new TransferItem(m_Mobile)); - } - } - } - } - - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.Stay; - - return true; - } - - public virtual bool DoBardPacified() - { - if (Core.Now < m_Mobile.BardEndTime) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I am pacified, I wait"); - } - - m_Mobile.Combatant = null; - m_Mobile.Warmode = false; - } - else - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I'm not pacified any longer"); - } - - m_Mobile.BardPacified = false; - } - - return true; - } - - public virtual bool DoBardProvoked() - { - if (Core.Now >= m_Mobile.BardEndTime && - (m_Mobile.BardMaster?.Deleted != false || - m_Mobile.BardMaster.Map != m_Mobile.Map || m_Mobile.GetDistanceToSqrt(m_Mobile.BardMaster) > - m_Mobile.RangePerception)) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I have lost my provoker"); - } - - m_Mobile.BardProvoked = false; - m_Mobile.BardMaster = null; - m_Mobile.BardTarget = null; - - m_Mobile.Combatant = null; - m_Mobile.Warmode = false; - } - else if (m_Mobile.BardTarget?.Deleted != false || m_Mobile.BardTarget.Map != m_Mobile.Map || - m_Mobile.GetDistanceToSqrt(m_Mobile.BardTarget) > m_Mobile.RangePerception) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I have lost my provoke target"); - } - - m_Mobile.BardProvoked = false; - m_Mobile.BardMaster = null; - m_Mobile.BardTarget = null; - - m_Mobile.Combatant = null; - m_Mobile.Warmode = false; - } - else - { - m_Mobile.Combatant = m_Mobile.BardTarget; - m_Action = ActionType.Combat; - - m_Mobile.OnThink(); - Think(); - } - - return true; - } - - public virtual void WalkRandom(int iChanceToNotMove, int iChanceToDir, int iSteps) - { - if (m_Mobile.Deleted || m_Mobile.DisallowAllMoves) - { - return; - } - - if (iChanceToNotMove <= 0) - { - return; - } - - for (var i = 0; i < iSteps; i++) - { - if (Utility.Random(1 + iChanceToNotMove) == 0) - { - // iChanceToDir = 2, total weight is 18 - // 7/26 chance of other direction - var iRndMove = Utility.Random(8 * (iChanceToDir + 1)); - Direction direction = iRndMove < 8 ? (Direction)iRndMove : m_Mobile.Direction; - DoMove(direction); - } - } - } - - public static double TransformMoveDelay(BaseCreature bc, bool isPassive = false) - { - if (bc == null) - { - return 0.1; - } - - if (bc.MoveSpeedMod > 0) - { - return bc.MoveSpeedMod; - } - - var moveSpeed = bc.CurrentSpeed; - var isControlled = bc.Controlled || bc.Summoned; - - /* - * Movement Speed Table based on RunUO - * <0.075 -> 0.0375 - * 0.075 -> 0.05 - * 0.1 -> 0.125 - * 0.2 -> 0.3 - * 0.25 -> 0.45 - * 0.3 -> 0.6 - * 0.4 -> 0.85 - * 0.5 -> 1.05 - * 0.6 -> 1.2, - * 0.8 -> 1.5 - * 1.0 -> 1.8 - * Above 1.0 is linear - */ - - // Linear interpolated - var movementSpeed = moveSpeed switch - { - >= 1.0 => 1.8 * moveSpeed, - >= 0.5 => 1.05 + (moveSpeed - 0.5) * 1.5, - >= 0.4 => 0.85 + (moveSpeed - 0.4) * 2, - >= 0.3 => 0.6 + (moveSpeed - 0.3) * 2.5, - >= 0.2 => 0.3 + (moveSpeed - 0.2) * 3, - >= 0.1 => 0.125 + (moveSpeed - 0.1) * 1.75, - >= 0.075 => 0.05 + (moveSpeed - 0.075) * 3, - _ => 0.0375 // 30 ticks, 37.5ms - }; - - if (isPassive) - { - movementSpeed += 0.2; - } - - if (!isControlled) - { - movementSpeed += 0.1; - } - else if (!bc.Summoned) - { - if (bc.ControlOrder == OrderType.Follow && bc.ControlTarget == bc.ControlMaster) - { - movementSpeed *= 0.5; - } - - movementSpeed -= 0.075; - } - - if (!bc.IsDeadPet && (bc.ReduceSpeedWithDamage || bc.IsSubdued)) - { - int stats, statsMax; - if (Core.HS) - { - stats = bc.Stam; - statsMax = bc.StamMax; - } - else - { - stats = bc.Hits; - statsMax = bc.HitsMax; - } - - var offset = statsMax <= 0 ? 1.0 : Math.Max(0, stats) / (double)statsMax; - - movementSpeed += (1.0 - offset) * 0.8; - } - - return movementSpeed; - } - - public bool CanMoveNow(out long delay) => (delay = NextMove - Core.TickCount) <= FuzzyTimeUntilNextMove; - - public virtual bool CheckMove() => true; - - public virtual bool DoMove(Direction d, bool badStateOk = false) - { - var res = DoMoveImpl(d, badStateOk); - - return res is MoveResult.Success or MoveResult.SuccessAutoTurn || badStateOk && res == MoveResult.BadState; - } - - public virtual MoveResult DoMoveImpl(Direction d, bool badStateOk) - { - if (m_Mobile == null || m_Mobile.Deleted || m_Mobile.Frozen || m_Mobile.Paralyzed || - m_Mobile.Spell?.IsCasting == true || m_Mobile.DisallowAllMoves) - { - return MoveResult.BadState; - } - - if (!CheckMove()) - { - return MoveResult.BadState; - } - - var delay = (int)(TransformMoveDelay(m_Mobile) * 1000); - - if (!CanMoveNow(out var timeUntilMove)) - { - // When bad state is ok, we can still move if the time until move is less than the fuzzy time - if (badStateOk) - { - AIMovementTimerPool.GetTimer(TimeSpan.FromMilliseconds(timeUntilMove), this, d).Start(); - } - - return MoveResult.BadState; - } - - // This makes them always move one step, never any direction changes - // TODO: This is firing off deltas which aren't needed. Look into replacing/removing this - m_Mobile.Direction = d; - NextMove += delay; - - if (Core.TickCount - NextMove > 0) - { - NextMove = Core.TickCount; - } - - m_Mobile.Pushing = false; - var mobDirection = m_Mobile.Direction; - - // Do the actual move - MoveImpl.IgnoreMovableImpassables = m_Mobile.CanMoveOverObstacles && !m_Mobile.CanDestroyObstacles; - var moveResult = m_Mobile.Move(d); - MoveImpl.IgnoreMovableImpassables = false; - - if (moveResult) - { - // If we don't delay combat, then a direction change will happen and cause a glitchy sliding effect. - if (m_Mobile.Warmode && m_Mobile.Combatant != null) - { - var remaining = m_Mobile.NextCombatTime - Core.TickCount; - var maxWait = Math.Min(delay, 400); - if (remaining < maxWait) - { - m_Mobile.NextCombatTime = Core.TickCount + maxWait; - } - } - return MoveResult.Success; - } - - if ((mobDirection & Direction.Mask) != (d & Direction.Mask)) - { - return MoveResult.Blocked; - } - - var wasPushing = m_Mobile.Pushing; - - var blocked = true; - - var canOpenDoors = m_Mobile.CanOpenDoors; - var canDestroyObstacles = m_Mobile.CanDestroyObstacles; - - if (canOpenDoors || canDestroyObstacles) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("My movement was blocked, I will try to clear some obstacles."); - } - - var map = m_Mobile.Map; - - if (map != null) - { - var x = m_Mobile.X; - var y = m_Mobile.Y; - Movement.Movement.Offset(d, ref x, ref y); - - using var queue = PooledRefQueue.Create(); - var destroyables = 0; - foreach (var item in map.GetItemsInRange(new Point2D(x, y), 1)) - { - if (canOpenDoors && item is BaseDoor door && door.Z + door.ItemData.Height > m_Mobile.Z && - m_Mobile.Z + 16 > door.Z) - { - if (door.X != x || door.Y != y) - { - continue; - } - - if (!door.Locked || !door.UseLocks()) - { - queue.Enqueue(door); - } - - if (!canDestroyObstacles) - { - break; - } - } - else if (canDestroyObstacles && item.Movable && item.ItemData.Impassable && - item.Z + item.ItemData.Height > m_Mobile.Z && m_Mobile.Z + 16 > item.Z) - { - if (!m_Mobile.InRange(item.GetWorldLocation(), 1)) - { - continue; - } - - queue.Enqueue(item); - ++destroyables; - } - } - - if (destroyables > 0) - { - Effects.PlaySound(new Point3D(x, y, m_Mobile.Z), m_Mobile.Map, 0x3B3); - } - - if (queue.Count > 0) - { - blocked = false; // retry movement - } - - while (queue.Count > 0) - { - var item = queue.Dequeue(); - - if (item is BaseDoor door) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay( - "Little do they expect, I've learned how to open doors. Didn't they read the script??" - ); - } - - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("*twist*"); - } - - door.Use(m_Mobile); - } - else - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay( - $"Ugabooga. I'm so big and tough I can destroy it: {item.GetType().Name}" - ); - } - - if (item is Container cont) - { - for (var i = 0; i < cont.Items.Count; ++i) - { - var check = cont.Items[i]; - - if (check.Movable && check.ItemData.Impassable && - cont.Z + check.ItemData.Height > m_Mobile.Z) - { - queue.Enqueue(check); - } - } - - cont.Destroy(); - } - else - { - item.Delete(); - } - } - } - - if (!blocked) - { - blocked = !m_Mobile.Move(d); - } - } - } - - if (blocked) - { - var offset = Utility.RandomDouble() < 0.4 ? 1 : -1; - - for (var i = 0; i < 2; ++i) - { - m_Mobile.TurnInternal(offset); - - if (m_Mobile.Move(m_Mobile.Direction)) - { - return MoveResult.SuccessAutoTurn; - } - } - - return wasPushing ? MoveResult.BadState : MoveResult.Blocked; - } - - return MoveResult.Success; - } - - public virtual void WalkRandomInHome(int iChanceToNotMove, int iChanceToDir, int iSteps) - { - if (m_Mobile.Deleted || m_Mobile.DisallowAllMoves) - { - return; - } - - if (m_Mobile.Home == Point3D.Zero) - { - if (m_Mobile.Spawner is RegionSpawner rs) - { - Region region = rs.SpawnRegion; - - if (m_Mobile.Region.AcceptsSpawnsFrom(region)) - { - m_Mobile.WalkRegion = region; - WalkRandom(iChanceToNotMove, iChanceToDir, iSteps); - m_Mobile.WalkRegion = null; - } - else - { - if (region.GoLocation != Point3D.Zero && Utility.RandomBool()) - { - DoMove(m_Mobile.GetDirectionTo(region.GoLocation)); - } - else - { - WalkRandom(iChanceToNotMove, iChanceToDir, 1); - } - } - } - else - { - WalkRandom(iChanceToNotMove, iChanceToDir, iSteps); - } - } - else - { - for (var i = 0; i < iSteps; i++) - { - if (m_Mobile.RangeHome != 0) - { - var iCurrDist = (int)m_Mobile.GetDistanceToSqrt(m_Mobile.Home); - - if (iCurrDist < m_Mobile.RangeHome * 2 / 3) - { - WalkRandom(iChanceToNotMove, iChanceToDir, 1); - } - else if (iCurrDist > m_Mobile.RangeHome) - { - DoMove(m_Mobile.GetDirectionTo(m_Mobile.Home)); - } - else if (Utility.Random(10) > 5) - { - DoMove(m_Mobile.GetDirectionTo(m_Mobile.Home)); - } - else - { - WalkRandom(iChanceToNotMove, iChanceToDir, 1); - } - } - else - { - if (m_Mobile.Location != m_Mobile.Home) - { - DoMove(m_Mobile.GetDirectionTo(m_Mobile.Home)); - } - } - } - } - } - - public virtual bool CheckFlee() - { - if (m_Mobile.CheckFlee()) - { - var combatant = m_Mobile.Combatant; - - if (combatant == null) - { - WalkRandom(1, 2, 1); - } - else - { - var d = combatant.GetDirectionTo(m_Mobile); - - d = (Direction)((int)d + Utility.RandomMinMax(-1, +1)); - - m_Mobile.Direction = d; - m_Mobile.Move(d); - } - - return true; - } - - return false; - } - - public virtual void OnTeleported() - { - if (m_Path != null) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("Teleported; repathing"); - } - - m_Path.ForceRepath(); - } - } - - public virtual bool MoveTo(Mobile m, bool run, int range) - { - if (m_Mobile.Deleted || m_Mobile.DisallowAllMoves || m?.Deleted != false) - { - return false; - } - - if (m_Mobile.InRange(m, range)) - { - m_Path = null; - return true; - } - - if (m_Path?.Goal == m) - { - if (m_Path.Follow(run, 1)) - { - m_Path = null; - return true; - } - } - else if (!DoMove(m_Mobile.GetDirectionTo(m), true)) - { - m_Path = new PathFollower(m_Mobile, m) { Mover = DoMoveImpl }; - - if (m_Path.Follow(run, 1)) - { - m_Path = null; - return true; - } - } - else - { - m_Path = null; - return true; - } - - return false; - } - - /* - * Walk at range distance from mobile - * - * iSteps : Number of steps - * bRun : Do we run - * iWantDistMin : The minimum distance we want to be - * iWantDistMax : The maximum distance we want to be - * - */ - public virtual bool WalkMobileRange(Mobile m, int iSteps, bool run, int iWantDistMin, int iWantDistMax) - { - if (m_Mobile.Deleted || m_Mobile.DisallowAllMoves) - { - return false; - } - - if (m == null) - { - return false; - } - - for (var i = 0; i < iSteps; i++) - { - // Get the current distance - var iCurrDist = (int)m_Mobile.GetDistanceToSqrt(m); - - if (iCurrDist < iWantDistMin || iCurrDist > iWantDistMax) - { - var needCloser = iCurrDist > iWantDistMax; - - if (needCloser && m_Path != null && m_Path.Goal == m) - { - if (m_Path.Follow(run, 1)) - { - m_Path = null; - } - } - else - { - var dirTo = iCurrDist > iWantDistMax ? - m_Mobile.GetDirectionTo(m, run) : m.GetDirectionTo(m_Mobile, run); - - if (!DoMove(dirTo, true) && needCloser) - { - m_Path = new PathFollower(m_Mobile, m) { Mover = DoMoveImpl }; - - if (m_Path.Follow(run, 1)) - { - m_Path = null; - } - } - else - { - m_Path = null; - } - } - } - else - { - return true; - } - } - - // Get the current distance - var iNewDist = (int)m_Mobile.GetDistanceToSqrt(m); - - return iNewDist >= iWantDistMin && iNewDist <= iWantDistMax; - } - - /* - * Here we check to acquire a target from our surrounding - * - * iRange : The range - * acqType : A type of acquire we want (closest, strongest, etc) - * bPlayerOnly : Don't bother with other creatures or NPCs, want a player - * bFacFriend : Check people in my faction - * bFacFoe : Check people in other factions - * - */ - public virtual bool AcquireFocusMob(int iRange, FightMode acqType, bool bPlayerOnly, bool bFacFriend, bool bFacFoe) - { - if (m_Mobile.Deleted) - { - return false; - } - - if (m_Mobile.BardProvoked) - { - if (m_Mobile.BardTarget?.Deleted != false) - { - m_Mobile.FocusMob = null; - return false; - } - - m_Mobile.FocusMob = m_Mobile.BardTarget; - return m_Mobile.FocusMob != null; - } - - if (m_Mobile.Controlled) - { - if (m_Mobile.ControlTarget?.Deleted != false || m_Mobile.ControlTarget.Hidden || - !m_Mobile.ControlTarget.Alive || m_Mobile.ControlTarget.IsDeadBondedPet || - !m_Mobile.InRange(m_Mobile.ControlTarget, m_Mobile.RangePerception * 2)) - { - if (m_Mobile.ControlTarget != null && m_Mobile.ControlTarget != m_Mobile.ControlMaster) - { - m_Mobile.ControlTarget = null; - } - - m_Mobile.FocusMob = null; - return false; - } - - m_Mobile.FocusMob = m_Mobile.ControlTarget; - return m_Mobile.FocusMob != null; - } - - if (m_Mobile.ConstantFocus != null) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("Acquired my constant focus"); - } - - m_Mobile.FocusMob = m_Mobile.ConstantFocus; - return true; - } - - if (acqType == FightMode.None) - { - m_Mobile.FocusMob = null; - return false; - } - - if (acqType == FightMode.Aggressor && m_Mobile.Aggressors.Count == 0 && m_Mobile.Aggressed.Count == 0 && - m_Mobile.FactionAllegiance == null && m_Mobile.EthicAllegiance == null) - { - m_Mobile.FocusMob = null; - return false; - } - - if (Core.TickCount - m_Mobile.NextReacquireTime < 0) - { - m_Mobile.FocusMob = null; - return false; - } - - m_Mobile.NextReacquireTime = Core.TickCount + (int)m_Mobile.ReacquireDelay.TotalMilliseconds; - - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("Acquiring..."); - } - - var map = m_Mobile.Map; - - if (map == null) - { - // TODO: Is this correct? Maybe it should return false? - return m_Mobile.FocusMob != null; - } - - Mobile newFocusMob = null; - var val = double.MinValue; - Mobile enemySummonMob = null; - var enemySummonVal = double.MinValue; - - foreach (var m in map.GetMobilesInRange(m_Mobile.Location, iRange)) - { - if (m.Deleted || m.Blessed) - { - continue; - } - - // Let's not target ourselves... - if (m == m_Mobile || m is BaseFamiliar) - { - continue; - } - - // Dead targets are invalid. - if (!m.Alive || m.IsDeadBondedPet) - { - continue; - } - - // Staff members cannot be targeted. - if (m.AccessLevel > AccessLevel.Player) - { - continue; - } - - // Does it have to be a player? - if (bPlayerOnly && !m.Player) - { - continue; - } - - // Can't acquire a target we can't see. - if (!m_Mobile.CanSee(m)) - { - continue; - } - - var bc = m as BaseCreature; - var pm = m as PlayerMobile; - - // Monster don't attack it's own summon or the summon of another monster - if (Core.AOS && bc != null && bc.Summoned && (bc.SummonMaster == m_Mobile || !bc.SummonMaster.Player && IsHostile(bc.SummonMaster))) - { - continue; - } - - if (m_Mobile.Summoned && m_Mobile.SummonMaster != null) - { - // Animated creatures cannot attack players directly. - if (pm != null && m_Mobile.IsAnimatedDead) - { - continue; - } - - // Animated creatures cannot attack other animated creatures or pets of other players - if (m_Mobile.IsAnimatedDead && bc != null && (bc.IsAnimatedDead || bc.Controlled)) - { - continue; - } - - // If this is a summon, it can't target its controller or invalid targets. - if (m_Mobile.FollowsAcquireRules && (m == m_Mobile.SummonMaster || !SpellHelper.ValidIndirectTarget(m_Mobile, m))) - { - continue; - } - } - - // If we only want faction friends, make sure it's one. - if (bFacFriend && !m_Mobile.IsFriend(m)) - { - continue; - } - - // Ignore anyone under EtherealVoyage - if (TransformationSpellHelper.UnderTransformation(m, typeof(EtherealVoyageSpell))) - { - continue; - } - - // Ignore players with activated honor - if (m_Mobile.Combatant != m && VirtueSystem.GetVirtues(pm)?.HonorActive == true) - { - continue; - } - - if (acqType is FightMode.Aggressor or FightMode.Evil) - { - var bValid = IsHostile(m); - - if (!bValid) - { - bValid = m_Mobile.GetFactionAllegiance(m) == BaseCreature.Allegiance.Enemy || - m_Mobile.GetEthicAllegiance(m) == BaseCreature.Allegiance.Enemy; - } - - if (acqType == FightMode.Evil && !bValid) - { - if (bc?.Controlled == true && bc?.ControlMaster != null) - { - bValid = bc.ControlMaster.Karma < 0; - } - else - { - bValid = m.Karma < 0; - } - } - - if (!bValid) - { - continue; - } - } - else - { - // Same goes for faction enemies. - if (bFacFoe && !m_Mobile.IsEnemy(m)) - { - continue; - } - - // If it's an enemy factioned mobile, make sure we can be harmful to it. - if (bFacFoe && !bFacFriend && !m_Mobile.CanBeHarmful(m, false)) - { - continue; - } - } - - var theirVal = m_Mobile.GetFightModeRanking(m, acqType, bPlayerOnly); - - // Always prefer someone else over the summon master (EV/BS) - if ((theirVal > val || newFocusMob == m_Mobile.SummonMaster) && m_Mobile.InLOS(m)) - { - newFocusMob = m; - val = theirVal; - } - // The summon is targeted when nothing else around. Otherwise this monster enters idle mode. - // Do a check for this edge case so players cannot abuse by casting EVs offscreen to kill an idle monster. - else if (Core.AOS && theirVal > enemySummonVal && m_Mobile.InLOS(m) && bc?.Summoned == true && bc?.Controlled != true) - { - enemySummonMob = m; - enemySummonVal = theirVal; - } - } - - m_Mobile.FocusMob = newFocusMob ?? enemySummonMob; - return m_Mobile.FocusMob != null; - } - - private bool IsHostile(Mobile from) - { - if (m_Mobile.Combatant == from || from.Combatant == m_Mobile) - { - return true; - } - - var count = Math.Max(m_Mobile.Aggressors.Count, m_Mobile.Aggressed.Count); - - for (var a = 0; a < count; ++a) - { - if (a < m_Mobile.Aggressed.Count && m_Mobile.Aggressed[a].Attacker == from) - { - return true; - } - - if (a < m_Mobile.Aggressors.Count && m_Mobile.Aggressors[a].Defender == from) - { - return true; - } - } - - return false; - } - - public virtual void DetectHidden() - { - if (m_Mobile.Deleted || m_Mobile.Map == null) - { - return; - } - - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("Checking for hidden players"); - } - - var srcSkill = m_Mobile.Skills.DetectHidden.Value; - - if (srcSkill <= 0) - { - return; - } - - foreach (var trg in m_Mobile.GetMobilesInRange(m_Mobile.RangePerception)) - { - if (trg != m_Mobile && trg.Player && trg.Alive && trg.Hidden && trg.AccessLevel == AccessLevel.Player && - m_Mobile.InLOS(trg)) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"Trying to detect {trg.Name}"); - } - - var trgHiding = trg.Skills.Hiding.Value / 2.9; - var trgStealth = trg.Skills.Stealth.Value / 1.8; - - var chance = srcSkill / 1.2 - Math.Min(trgHiding, trgStealth); - - if (chance < srcSkill / 10) - { - chance = srcSkill / 10; - } - - chance /= 100; - - if (chance > Utility.RandomDouble()) - { - trg.RevealingAction(); - trg.SendLocalizedMessage(500814); // You have been revealed! - } - } - } - } - - public virtual void Deactivate() - { - if (m_Mobile.PlayerRangeSensitive) - { - m_Timer.Stop(); - - var spawner = m_Mobile.Spawner; - - if (spawner?.ReturnOnDeactivate == true && !m_Mobile.Controlled && ( - spawner.HomeLocation == Point3D.Zero && !m_Mobile.Region.AcceptsSpawnsFrom(spawner.Region) || - !m_Mobile.InRange(spawner.HomeLocation, spawner.HomeRange) - )) - { - Timer.StartTimer(ReturnToHome); - } - } - } - - private void ReturnToHome() - { - if (m_Mobile.Spawner != null) - { - var loc = m_Mobile.Spawner.GetSpawnPosition(m_Mobile, m_Mobile.Spawner.Map); - - if (loc != Point3D.Zero) - { - m_Mobile.MoveToWorld(loc, m_Mobile.Spawner.Map); - } - } - } - - public virtual void Activate() - { - if (!m_Timer.Running) - { - // We want to randomize the time at which the AI activates. - // This triggers when a mob is first created since it moves from the internal map to it's added location - // If we spawn lots of mobs, we don't want their AI synchronized exactly. - m_Timer.Delay = TimeSpan.FromMilliseconds(Utility.Random(48) * 8); - m_Timer.Start(); - } - } - - public virtual void OnCurrentSpeedChanged() - { - m_Timer.Interval = TimeSpan.FromSeconds(Math.Max(0.008, m_Mobile.CurrentSpeed)); - } - - private class InternalEntry : ContextMenuEntry - { - private readonly OrderType _order; - - public InternalEntry(int number, int range, OrderType order, bool enabled) : base(number, range) - { - _order = order; - Enabled = enabled; - } - - public override void OnClick(Mobile from, IEntity target) - { - if (!from.CheckAlive() || target is not BaseCreature { Deleted: not true, Controlled: true } bc) - { - return; - } - - // Just in case - if (bc.IsDeadPet && _order is OrderType.Guard or OrderType.Attack or OrderType.Transfer or OrderType.Drop) - { - return; - } - - var isOwner = from == bc.ControlMaster; - var isFriend = !isOwner && bc.IsPetFriend(from); - - if (!isOwner && !isFriend) - { - return; - } - - if (isFriend && _order != OrderType.Follow && _order != OrderType.Stay && _order != OrderType.Stop) - { - return; - } - - switch (_order) - { - case OrderType.Follow: - case OrderType.Attack: - case OrderType.Transfer: - case OrderType.Friend: - case OrderType.Unfriend: - { - if (_order == OrderType.Transfer && from.HasTrade) - { - from.SendLocalizedMessage(1010507); // You cannot transfer a pet with a trade pending - } - else if (_order == OrderType.Friend && from.HasTrade) - { - from.SendLocalizedMessage(1070947); // You cannot friend a pet with a trade pending - } - else - { - bc.AIObject.BeginPickTarget(from, _order); - } - - break; - } - case OrderType.Release: - { - if (bc.Summoned) - { - goto default; - } - - from.SendGump(new ConfirmReleaseGump(from, bc)); - - break; - } - default: - { - if (bc.CheckControlChance(from)) - { - bc.ControlTarget = null; - bc.ControlOrder = _order; - } - - break; - } - } - } - } - - private class TransferItem : Item - { - private readonly BaseCreature m_Creature; - - public TransferItem(BaseCreature creature) - : base(ShrinkTable.Lookup(creature)) - { - m_Creature = creature; - - Movable = false; - - if (!Core.AOS) - { - Name = creature.Name; - } - else if (ItemID == ShrinkTable.DefaultItemID || - creature.GetType().IsDefined(typeof(FriendlyNameAttribute), false) || creature is Reptalon) - { - Name = FriendlyNameAttribute.GetFriendlyNameFor(creature.GetType()).ToString(); - } - - // (As Per OSI)No name. Normally, set by the ItemID of the Shrink Item unless we either explicitly set it with an Attribute, or, no lookup found - - Hue = creature.Hue & 0x0FFF; - } - - public TransferItem(Serial serial) - : base(serial) - { - } - - public static bool IsInCombat(BaseCreature creature) => - creature != null && (creature.Aggressors.Count > 0 || creature.Aggressed.Count > 0); - - public override void Serialize(IGenericWriter writer) - { - base.Serialize(writer); - - writer.Write(0); // version - } - - public override void Deserialize(IGenericReader reader) - { - base.Deserialize(reader); - - var version = reader.ReadInt(); - - Delete(); - } - - public override void GetProperties(IPropertyList list) - { - base.GetProperties(list); - - list.Add(1041603); // This item represents a pet currently in consideration for trade - list.Add(1041601, m_Creature.Name); // Pet Name: ~1_val~ - - if (m_Creature.ControlMaster != null) - { - list.Add(1041602, m_Creature.ControlMaster.Name); // Owner: ~1_val~ - } - } - - public override bool AllowSecureTrade(Mobile from, Mobile to, Mobile newOwner, bool accepted) - { - if (!base.AllowSecureTrade(from, to, newOwner, accepted)) - { - return false; - } - - if (Deleted || m_Creature?.Deleted != false || m_Creature.ControlMaster != from || - !from.CheckAlive() || !to.CheckAlive()) - { - return false; - } - - if (from.Map != m_Creature.Map || !from.InRange(m_Creature, 14)) - { - return false; - } - - var youngFrom = from is PlayerMobile mobile && mobile.Young; - var youngTo = to is PlayerMobile playerMobile && playerMobile.Young; - - if (accepted && youngFrom && !youngTo) - { - from.SendLocalizedMessage(502051); // As a young player, you may not transfer pets to older players. - } - else if (accepted && !youngFrom && youngTo) - { - from.SendLocalizedMessage(502052); // As an older player, you may not transfer pets to young players. - } - else if (accepted && !m_Creature.CanBeControlledBy(to)) - { - var args = $"{to.Name}\t{from.Name}\t "; - - // The pet refuses to be transferred because it will not obey ~1_NAME~.~3_BLANK~ - from.SendLocalizedMessage(1043248, args); - // The pet will not accept you as a master because it does not trust you.~3_BLANK~ - to.SendLocalizedMessage(1043249, args); - - return false; - } - else if (accepted && !m_Creature.CanBeControlledBy(from)) - { - var args = $"{to.Name}\t{from.Name}\t "; - - // The pet refuses to be transferred because it will not obey you sufficiently.~3_BLANK~ - from.SendLocalizedMessage(1043250, args); - // The pet will not accept you as a master because it does not trust ~2_NAME~.~3_BLANK~ - to.SendLocalizedMessage(1043251, args); - } - else if (accepted && to.Followers + m_Creature.ControlSlots > to.FollowersMax) - { - to.SendLocalizedMessage(1049607); // You have too many followers to control that creature. - - return false; - } - else if (accepted && IsInCombat(m_Creature)) - { - from.SendMessage("You may not transfer a pet that has recently been in combat."); - to.SendMessage("The pet may not be transferred to you because it has recently been in combat."); - - return false; - } - - return true; - } - - public override void OnSecureTrade(Mobile from, Mobile to, Mobile newOwner, bool accepted) - { - if (Deleted) - { - return; - } - - Delete(); - - if (m_Creature?.Deleted != false || m_Creature.ControlMaster != from || !from.CheckAlive() || - !to.CheckAlive()) - { - return; - } - - if (from.Map != m_Creature.Map || !from.InRange(m_Creature, 14)) - { - return; - } - - if (accepted && m_Creature.SetControlMaster(to)) - { - if (m_Creature.Summoned) - { - m_Creature.SummonMaster = to; - } - - m_Creature.ControlTarget = to; - m_Creature.ControlOrder = OrderType.Follow; - - m_Creature.BondingBegin = DateTime.MinValue; - m_Creature.OwnerAbandonTime = DateTime.MinValue; - m_Creature.IsBonded = false; - - m_Creature.PlaySound(m_Creature.GetIdleSound()); - - var args = $"{from.Name}\t{m_Creature.Name}\t{to.Name}"; - - from.SendLocalizedMessage(1043253, args); // You have transferred your pet to ~3_GETTER~. - // ~1_NAME~ has transferred the allegiance of ~2_PET_NAME~ to you. - to.SendLocalizedMessage(1043252, args); - } - } - } - - /* - * The Timer object - */ - private class AITimer : Timer - { - private readonly BaseAI m_Owner; - - public AITimer(BaseAI owner) : base( - TimeSpan.FromMilliseconds(Utility.Random(96) * 8), - TimeSpan.FromSeconds(Math.Max(0.0, owner.m_Mobile.CurrentSpeed)) - ) - { - m_Owner = owner; - m_Owner.m_NextDetectHidden = Core.TickCount; - } - - protected override void OnTick() - { - if (m_Owner.m_Mobile.Deleted) - { - Stop(); - return; - } - - if (m_Owner.m_Mobile.Map == null || m_Owner.m_Mobile.Map == Map.Internal) - { - m_Owner.Deactivate(); - return; - } - - if (m_Owner.m_Mobile.PlayerRangeSensitive) // have to check this in the timer.... - { - var sect = m_Owner.m_Mobile.Map.GetSector(m_Owner.m_Mobile.Location); - if (!sect.Active) - { - m_Owner.Deactivate(); - return; - } - } - - m_Owner.m_Mobile.OnThink(); - - if (m_Owner.m_Mobile.Deleted) - { - Stop(); - return; - } - - if (m_Owner.m_Mobile.Map == null || m_Owner.m_Mobile.Map == Map.Internal) - { - m_Owner.Deactivate(); - return; - } - - if (m_Owner.m_Mobile.BardPacified) - { - m_Owner.DoBardPacified(); - } - else if (m_Owner.m_Mobile.BardProvoked) - { - m_Owner.DoBardProvoked(); - } - else if (!m_Owner.m_Mobile.Controlled) - { - if (!m_Owner.Think()) - { - Stop(); - return; - } - } - else if (!m_Owner.Obey()) - { - Stop(); - return; - } - - if (m_Owner.CanDetectHidden && Core.TickCount - m_Owner.m_NextDetectHidden >= 0) - { - m_Owner.DetectHidden(); - - // Not exactly OSI style, approximation. - var delay = Math.Min(15000 / m_Owner.m_Mobile.Int, 60); - - var min = delay * 900; // 13s at 1000 int, 33s at 400 int, 54s at <250 int - var max = delay * 1100; // 16s at 1000 int, 41s at 400 int, 66s at <250 int - - m_Owner.m_NextDetectHidden = Core.TickCount + Utility.RandomMinMax(min, max); - } - } - } - - public static class AIMovementTimerPool - { - private const int _poolSize = 1024; - private static readonly Queue _pool = new (_poolSize); - - public static void Configure() - { - var i = 0; - while (i++ < _poolSize) - { - _pool.Enqueue(new AIMovementTimer()); - } - } - - public static AIMovementTimer GetTimer(TimeSpan delay, BaseAI ai, Direction direction) - { - AIMovementTimer timer; - if (_pool.Count > 0) - { - timer = _pool.Dequeue(); - } - else - { - timer = new AIMovementTimer(); - } - - timer.Set(delay, ai, direction); - return timer; - } - - public class AIMovementTimer : Timer - { - public BaseAI AI { get; private set; } - public Direction Direction { get; private set; } - - public AIMovementTimer() : base(TimeSpan.Zero) - { - } - - public void Set(TimeSpan delay, BaseAI ai, Direction direction) - { - if (Running) - { - return; - } - - Delay = delay; - AI = ai; - Direction = direction; - } - - protected override void OnTick() - { - AI?.DoMove(Direction); - AI = null; - _pool.Enqueue(this); - } - } - } -} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/AIGroupMovement.cs b/Projects/UOContent/Mobiles/AI/BaseAI/AIGroupMovement.cs new file mode 100644 index 000000000..c1564c9d7 --- /dev/null +++ b/Projects/UOContent/Mobiles/AI/BaseAI/AIGroupMovement.cs @@ -0,0 +1,215 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2025 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: AIGroupMovement.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using Server.Collections; + +namespace Server.Mobiles; + +public abstract partial class BaseAI +{ + private static readonly Dictionary _reservedPositions = new(); + private static long _lastGroupUpdateTime; + + private static void CleanupReservedPositions() + { + using var toRemove = PooledRefQueue.Create(); + + foreach (var (m, p) in _reservedPositions) + { + if (m?.Deleted != false || m.GetDistanceToSqrt(p) < 1) + { + toRemove.Enqueue(m); + } + } + + while (toRemove.Count > 0) + { + _reservedPositions.Remove(toRemove.Dequeue()); + } + } + + private bool UseGroupMovement(Mobile target) => + Mobile.Combatant == target + && !Mobile.Controlled + && CountNearbyAllies(target) > 0; + + public static bool MoveToWithGroup(BaseAI ai, Mobile target, bool run, int range) + { + if (Core.TickCount - _lastGroupUpdateTime > 1000) + { + CleanupReservedPositions(); + _lastGroupUpdateTime = Core.TickCount; + } + + var mobile = ai.Mobile; + var allies = ai.GetNearbyAllies(target); + var optimalPosition = ai.CalculateOptimalPosition(target, ref allies, range); + try + { + if (optimalPosition == Point3D.Zero) + { + return ai.MoveToWithCollisionAvoidance(target, run, range); + } + + _reservedPositions[mobile] = optimalPosition; + + var direction = mobile.GetDirectionTo(optimalPosition); + + if (Utility.Random(3) == 0) + { + direction = GetAdjustedDirection(direction); + } + + return ai.DoMove(direction, true); + } + finally + { + allies.Dispose(); + } + } + + private int CountNearbyAllies(Mobile target) + { + var allies = 0; + foreach (var m in Mobile.GetMobilesInRange(8)) + { + if (m != Mobile && m.Combatant == target && m is BaseCreature { Controlled: false } bc + && bc.Team == Mobile.Team) + { + allies++; + } + } + + return allies; + } + + private PooledRefList GetNearbyAllies(Mobile target) + { + var allies = PooledRefList.Create(); + + foreach (var m in Mobile.GetMobilesInRange(8)) + { + if (m != Mobile && m.Combatant == target && m is BaseCreature { Controlled: false } bc + && bc.Team == Mobile.Team) + { + allies.Add(bc); + } + } + + return allies; + } + + private Point3D CalculateOptimalPosition(Mobile target, ref PooledRefList allies, int range) + { + var targetLoc = target.Location; + var bestPosition = Point3D.Zero; + var bestScore = -1.0; + + for (var x = -range; x <= range; x++) + { + for (var y = -range; y <= range; y++) + { + if (Math.Abs(x) + Math.Abs(y) != range) + { + continue; + } + + var testLoc = new Point3D(targetLoc.X + x, targetLoc.Y + y, targetLoc.Z); + var distance = Mobile.GetDistanceToSqrt(testLoc); + + if (distance < range || + distance > range + 3 || !CanMoveTo(testLoc)) + { + continue; + } + + var score = ScorePosition(testLoc, distance, target, ref allies); + + if (score > bestScore) + { + bestScore = score; + bestPosition = testLoc; + + if (score > 20) + { + break; + } + } + } + } + + return bestPosition; + } + + private double ScorePosition(Point3D position, double currentDistance, Mobile target, ref PooledRefList allies) + { + var score = -(currentDistance * 2); + + for (var i = 0; i < allies.Count; i++) + { + var ally = allies[i]; + var allyDistance = ally.GetDistanceToSqrt(position); + + if (allyDistance < 2) + { + score -= 50; + } + else if (allyDistance < 3) + { + score -= 20; + } + } + + foreach (var (m, p) in _reservedPositions) + { + if (m != Mobile && p.GetDistanceToSqrt(position) < 2) + { + score -= 30; + } + } + + if (Mobile.Map != null && Mobile.Map.LineOfSight(position, target.Location)) + { + score += 10; + } + + return score + Utility.RandomDouble() * 5; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool CanMoveTo(Point3D location) => + Mobile.Map?.CanFit(location.X, location.Y, location.Z, 16, false, false) == true; + + private static Direction GetAdjustedDirection(Direction original) + { + var adjustment = Utility.Random(3) - 1; + var newDir = (int)original + adjustment; + + if (newDir < 0) + { + return (Direction)(newDir + 8); + } + + if (newDir >= 8) + { + return (Direction)(newDir - 8); + } + + return (Direction)newDir; + } +} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/AIMovement.cs b/Projects/UOContent/Mobiles/AI/BaseAI/AIMovement.cs new file mode 100644 index 000000000..9909024bf --- /dev/null +++ b/Projects/UOContent/Mobiles/AI/BaseAI/AIMovement.cs @@ -0,0 +1,429 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2025 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: AIMovement.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ************************************************************************/ + +using System.Runtime.CompilerServices; +using Server.Collections; +using Server.Items; +using MoveImpl = Server.Movement.MovementImpl; + +namespace Server.Mobiles; + +public abstract partial class BaseAI +{ + public static double BadlyHurtMoveDelay(BaseCreature bc) + { + var statMin = Core.HS ? bc.Stam : bc.Hits; + var statMax = Core.HS ? bc.StamMax : bc.HitsMax; + + if (!bc.IsDeadPet && (bc.ReduceSpeedWithDamage || bc.IsSubdued) + && statMax > 0 && statMin < statMax * 0.3) + { + var hits = (double)statMin / statMax; + + if (hits < 0.1) { return bc.CurrentSpeed + 0.15; } + if (hits < 0.2) { return bc.CurrentSpeed + 0.1; } + if (hits < 0.3) { return bc.CurrentSpeed + 0.05; } + } + + return bc.CurrentSpeed; + } + + public bool CanMoveNow(out double delay) + { + delay = 0.0; + return Core.TickCount >= NextMove; + } + + public virtual bool CheckMove() => !(Mobile.Deleted || Mobile.DisallowAllMoves); + + public virtual bool DoMove(Direction d, bool badStateOk = false) => IsMoveSuccessful(DoMoveImpl(d, badStateOk), badStateOk); + + private static bool IsMoveSuccessful(MoveResult res, bool badStateOk) => + res is MoveResult.Success or MoveResult.SuccessAutoTurn + || badStateOk && res == MoveResult.BadState; + + public virtual MoveResult DoMoveImpl(Direction d, bool badStateOk) + { + if (IsInBadState() || !CanMoveNow(out _)) + { + return MoveResult.BadState; + } + + if ((Mobile.Direction & Direction.Mask) != (d & Direction.Mask)) + { + Mobile.Direction = d; + } + + Mobile.Pushing = false; + var mobDirection = Mobile.Direction; + + if (TryMove(d)) + { + Mobile.CurrentSpeed = Mobile.Hits < Mobile.HitsMax * 0.3 + ? BadlyHurtMoveDelay(Mobile) + : Mobile.Warmode || Mobile.Combatant != null ? Mobile.ActiveSpeed : Mobile.PassiveSpeed; + + return MoveResult.Success; + } + + if ((mobDirection & Direction.Mask) != (d & Direction.Mask)) + { + Mobile.Direction = d; + return MoveResult.SuccessAutoTurn; + } + + return HandleBlockedMovement(d); + } + + private bool TryMove(Direction d) + { + MoveImpl.IgnoreMovableImpassables = Mobile.CanMoveOverObstacles && !Mobile.CanDestroyObstacles; + + var result = Mobile.Move(d); + + MoveImpl.IgnoreMovableImpassables = false; + return result; + } + + private bool IsInBadState() => + Mobile == null || Mobile.Deleted || Mobile.Frozen || Mobile.Paralyzed || + Mobile.Spell?.IsCasting == true || Mobile.DisallowAllMoves; + + private MoveResult HandleBlockedMovement(Direction d) + { + var wasPushing = Mobile.Pushing; + + if ((Mobile.CanOpenDoors || Mobile.CanDestroyObstacles) && !TryClearObstacles(d)) + { + return MoveResult.Success; + } + + return TryAlternateMovement(wasPushing); + } + + private MoveResult TryAlternateMovement(bool wasPushing) + { + var offset = Utility.Random(2) == 0 ? 1 : -1; + + for (var i = 0; i < 2; ++i) + { + Mobile.TurnInternal(offset); + + if (Mobile.Move(Mobile.Direction)) + { + return MoveResult.SuccessAutoTurn; + } + } + + return wasPushing ? MoveResult.BadState : MoveResult.Blocked; + } + + private bool TryClearObstacles(Direction d) + { + DebugSay("My movement is blocked. Trying to push through."); + + var map = Mobile.Map; + + if (map == null) { return true; } + + var (x, y) = GetOffsetLocation(d); + + var queue = GatherObstacles(x, y, out var destroyables); + + if (destroyables > 0) + { + Effects.PlaySound(new Point3D(x, y, Mobile.Z), Mobile.Map, 0x3B3); + } + + try + { + return ProcessObstacles(ref queue, d); + } + finally + { + queue.Dispose(); + } + } + + private (int x, int y) GetOffsetLocation(Direction d) + { + var x = Mobile.X; + var y = Mobile.Y; + Movement.Movement.Offset(d, ref x, ref y); + return (x, y); + } + + private PooledRefQueue GatherObstacles(int x, int y, out int destroyables) + { + var queue = PooledRefQueue.Create(); + destroyables = 0; + + foreach (var item in Mobile.Map.GetItemsInRange(new Point2D(x, y), 1)) + { + if (IsValidDoor(item, x, y) || IsValidDestroyableItem(item)) + { + queue.Enqueue(item); + if (item is not BaseDoor) + { + destroyables++; + } + } + } + + return queue; + } + + private bool IsValidDoor(Item item, int x, int y) + { + if (!Mobile.CanOpenDoors || item is not BaseDoor door) + { + return false; + } + + if (door.Z + door.ItemData.Height <= Mobile.Z || Mobile.Z + 16 <= door.Z) + { + return false; + } + + if (door.X != x || door.Y != y) + { + return false; + } + + return !door.Locked || !door.UseLocks(); + } + + private bool IsValidDestroyableItem(Item item) + { + if (!Mobile.CanDestroyObstacles || !item.Movable || !item.ItemData.Impassable) + { + return false; + } + + if (item.Z + item.ItemData.Height <= Mobile.Z || Mobile.Z + 16 <= item.Z) + { + return false; + } + + return Mobile.InRange(item.GetWorldLocation(), 1); + } + + private bool ProcessObstacles(ref PooledRefQueue queue, Direction d) + { + if (queue.Count == 0) { return true; } + + while (queue.Count > 0) + { + ProcessObstacle(queue.Dequeue(), ref queue); + } + + return !Mobile.Move(d); + } + + private void ProcessObstacle(Item item, ref PooledRefQueue queue) + { + if (item is BaseDoor door) + { + DebugSay("Opening the door."); + door.Use(Mobile); + } + else + { + this.DebugSayFormatted($"Destroying item: {item.GetType().Name}"); + + if (item is Container cont) + { + ProcessContainer(cont, ref queue); + cont.Destroy(); + } + else + { + item.Delete(); + } + } + } + + private void ProcessContainer(Container cont, ref PooledRefQueue queue) + { + foreach (var check in cont.Items) + { + if (check.Movable && check.ItemData.Impassable && cont.Z + check.ItemData.Height > Mobile.Z) + { + queue.Enqueue(check); + } + } + } + + public virtual bool MoveTo(Mobile m, bool run, int range) + { + if (Mobile.Deleted || Mobile.DisallowAllMoves || m?.Deleted != false) + { + return false; + } + + var distance = (int)Mobile.GetDistanceToSqrt(m); + var distanceThreshold = Core.AOS && IsFollowingMaster() ? 1 : 5; + + var shouldRun = run && distance > distanceThreshold; + + if (Mobile.InRange(m, range)) + { + Path = null; + return true; + } + + if (UseGroupMovement(m)) + { + return MoveToWithGroup(this, m, shouldRun, range); + } + + if (Path == null && Mobile.InLOS(m) && DoMove(Mobile.GetDirectionTo(m), true)) + { + return true; + } + + if (Path?.Goal != m) + { + Path = new PathFollower(Mobile, m) { Mover = DoMoveImpl }; + } + + if (Path.Follow(shouldRun, 1)) + { + Path = null; + return true; + } + + return false; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool IsFollowingMaster() => + Mobile.Controlled && + Mobile.ControlOrder == OrderType.Follow && + Mobile.ControlTarget == Mobile.ControlMaster && + Mobile.Combatant == null; + + private bool MoveToWithCollisionAvoidance(Mobile target, bool run, int range) + { + var distance = (int)Mobile.GetDistanceToSqrt(target); + + var shouldRun = run && distance > 5; + + var direction = Mobile.GetDirectionTo(target); + + if (DoMove(direction, true)) + { + return true; + } + + for (var i = 1; i <= 3; i++) + { + var clockwise = (Direction)(((int)direction + i) % 8); + + if (DoMove(clockwise, true)) + { + return true; + } + + var counterclockwise = (Direction)(((int)direction - i + 8) % 8); + + if (DoMove(counterclockwise, true)) + { + return true; + } + } + + if (Path?.Goal != target) + { + Path = new PathFollower(Mobile, target) { Mover = DoMoveImpl }; + } + + if (Path.Follow(shouldRun, 1)) + { + Path = null; + return true; + } + + return false; + } + + public virtual bool WalkMobileRange(Mobile m, int iSteps, bool run, int iWantDistMin, int iWantDistMax) + { + if (Mobile.Deleted || Mobile.DisallowAllMoves || m == null) + { + return false; + } + + for (var i = 0; i < iSteps; i++) + { + var iCurrDist = (int)Mobile.GetDistanceToSqrt(m); + + var shouldRun = run && iCurrDist > 5; + + if (iCurrDist >= iWantDistMin && iCurrDist <= iWantDistMax) + { + return true; + } + + if (!MoveTowardsOrAwayFrom(m, shouldRun, iCurrDist, iWantDistMax)) + { + return false; + } + } + + var dist = Mobile.GetDistanceToSqrt(m); + + return dist >= iWantDistMin && dist <= iWantDistMax; + } + + private bool MoveTowardsOrAwayFrom(Mobile m, bool run, int iCurrDist, int iWantDistMax) + { + var shouldRun = run && iCurrDist > 5; + + var needCloser = iCurrDist > iWantDistMax; + + if (needCloser && m != null && Path?.Goal == m) + { + if (Path.Follow(shouldRun, 1)) + { + Path = null; + return true; + } + } + else + { + var dirTo = needCloser ? Mobile.GetDirectionTo(m, shouldRun) : m.GetDirectionTo(Mobile, shouldRun); + + if (DoMove(dirTo, true)) + { + Path = null; + return true; + } + + if (needCloser) + { + Path = new PathFollower(Mobile, m) { Mover = DoMoveImpl }; + + if (Path.Follow(shouldRun, 1)) + { + Path = null; + return true; + } + } + } + + return false; + } +} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/AITimer.cs b/Projects/UOContent/Mobiles/AI/BaseAI/AITimer.cs new file mode 100644 index 000000000..3ad69a627 --- /dev/null +++ b/Projects/UOContent/Mobiles/AI/BaseAI/AITimer.cs @@ -0,0 +1,134 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2025 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: AITimer.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ************************************************************************/ + +using System; + +namespace Server.Mobiles; + +internal sealed class AITimer : Timer +{ + private readonly BaseAI _owner; + private int _detectHiddenMinDelay; + private int _detectHiddenMaxDelay; + + public AITimer(BaseAI owner) : base(TimeSpan.FromMilliseconds(Utility.Random(3000)), + TimeSpan.FromMilliseconds(GetBaseInterval(owner))) + { + _owner = owner; + _owner._nextDetectHidden = Core.TickCount; + } + + private static double GetBaseInterval(BaseAI owner) + { + double interval; + + if (owner.IsFollowingMaster()) + { + interval = owner.Mobile.CurrentSpeed * (Core.AOS ? 100 : 400); + } + else if (owner.Mobile.CurrentSpeed <= 0.4) + { + interval = owner.Mobile.CurrentSpeed * 1000; + } + else + { + interval = owner.Mobile.CurrentSpeed * 3000; + } + + return Math.Max(interval, Core.AOS ? 100 : 200); + } + + protected override void OnTick() + { + if (ShouldStop()) + { + Stop(); + return; + } + + Interval = TimeSpan.FromMilliseconds(GetBaseInterval(_owner)); + + _owner.Mobile.OnThink(); + + if (ShouldStop()) + { + Stop(); + return; + } + + HandleBardEffects(); + + if (_owner.Mobile.Controlled ? !_owner.Obey() : !_owner.Think()) + { + Stop(); + return; + } + + HandleDetectHidden(); + } + + private bool ShouldStop() + { + if (_owner.Mobile.Deleted) + { + return true; + } + + if (_owner.Mobile.Map != null && _owner.Mobile.Map != Map.Internal && + (!_owner.Mobile.PlayerRangeSensitive || _owner.Mobile.Map.GetSector(_owner.Mobile.Location).Active)) + { + return false; + } + + _owner.Deactivate(); + return true; + } + + private void HandleBardEffects() + { + if (_owner.Mobile.BardPacified) + { + _owner.DoBardPacified(); + } + else if (_owner.Mobile.BardProvoked) + { + _owner.DoBardProvoked(); + } + } + + private void CacheDetectHiddenDelays() + { + var delay = Math.Min(30000 / _owner.Mobile.Int, 120); + _detectHiddenMinDelay = delay * 900; // 26s to 108s + _detectHiddenMaxDelay = delay * 1100; // 32s to 132s + } + + private void HandleDetectHidden() + { + if (!_owner.CanDetectHidden || Core.TickCount - _owner._nextDetectHidden < 0) + { + return; + } + + _owner.DetectHidden(); + + if (_detectHiddenMinDelay == 0 || _detectHiddenMaxDelay == 0) + { + CacheDetectHiddenDelays(); + } + + _owner._nextDetectHidden = Core.TickCount + Utility.RandomMinMax(_detectHiddenMinDelay, _detectHiddenMaxDelay); + } +} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/AIType.cs b/Projects/UOContent/Mobiles/AI/BaseAI/AIType.cs new file mode 100644 index 000000000..e04c0133d --- /dev/null +++ b/Projects/UOContent/Mobiles/AI/BaseAI/AIType.cs @@ -0,0 +1,30 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2025 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: AIType.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ************************************************************************/ + +namespace Server.Mobiles; + +public enum AIType +{ + AI_Use_Default, + AI_Melee, + AI_Animal, + AI_Archer, + AI_Healer, + AI_Vendor, + AI_Mage, + AI_Berserk, + AI_Predator, + AI_Thief +} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/ActionType.cs b/Projects/UOContent/Mobiles/AI/BaseAI/ActionType.cs new file mode 100644 index 000000000..f34e5a63e --- /dev/null +++ b/Projects/UOContent/Mobiles/AI/BaseAI/ActionType.cs @@ -0,0 +1,26 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2025 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: ActionType.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ************************************************************************/ + +namespace Server.Mobiles; + +public enum ActionType +{ + Wander, + Combat, + Guard, + Flee, + Backoff, + Interact +} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/BaseAI.cs b/Projects/UOContent/Mobiles/AI/BaseAI/BaseAI.cs new file mode 100644 index 000000000..1797cfca6 --- /dev/null +++ b/Projects/UOContent/Mobiles/AI/BaseAI/BaseAI.cs @@ -0,0 +1,921 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2025 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: BaseAI.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ************************************************************************/ + +using System; +using Server.Engines.Quests.Necro; +using Server.Engines.Spawners; +using Server.Engines.Virtues; +using Server.Factions; +using Server.Spells; +using Server.Spells.Spellweaving; +using Server.Targets; + +namespace Server.Mobiles; + +public abstract partial class BaseAI +{ + private ActionType _action; + public long _nextDetectHidden; + public PathFollower Path { get; protected set; } + public readonly Timer _timer; + public DateTime _lastOrder = DateTime.MinValue; + public Mobile _commandIssuer; + public long NextMove { get; set; } + + public BaseCreature Mobile { get; } + + public long NextDebugMessage { get; set; } + + public virtual bool CanDetectHidden => Mobile.Skills.DetectHidden.Value > 0; + + public BaseAI(BaseCreature m) + { + Mobile = m; + _timer = new AITimer(this); + + if (!m.PlayerRangeSensitive || !World.Loading && m.Map != null && m.Map != Map.Internal && m.Map.GetSector(m.Location).Active) + { + _timer.Start(); + } + + if (Action != ActionType.Wander) + { + Action = ActionType.Wander; + } + } + + public ActionType Action + { + get => _action; + set + { + if (_action != value) + { + _action = value; + OnActionChanged(); + } + } + } + + public virtual bool WasNamed(string speech) => !string.IsNullOrEmpty(Mobile.Name) && speech.InsensitiveStartsWith(Mobile.Name); + + public virtual void BeginPickTarget(Mobile from, OrderType order) + { + if (!IsValidTarget(from, order)) + { + return; + } + + if (from.Target == null) + { + SendOrderMessage(from, order); + from.Target = new AIControlMobileTarget(this, order); + } + else if (from.Target is AIControlMobileTarget t && t.Order == order) + { + t.AddAI(this); + } + } + + private static void SendOrderMessage(Mobile from, OrderType order) + { + switch (order) + { + case OrderType.Transfer: + { + from.SendLocalizedMessage(502038); + // Click on the person to transfer ownership to. + break; + } + case OrderType.Friend: + { + from.SendLocalizedMessage(502020); + // Click on the player whom you wish to make a co-owner. + break; + } + case OrderType.Unfriend: + { + from.SendLocalizedMessage(1070948); + // Click on the player whom you wish to remove as a co-owner. + break; + } + } + } + + public virtual void OnAggressiveAction(Mobile aggressor) + { + if (aggressor.Hidden) + { + return; + } + + var currentCombat = Mobile.Combatant; + + if (currentCombat == null || currentCombat == aggressor) + { + return; + } + + if (Mobile.GetDistanceToSqrt(aggressor) < Mobile.GetDistanceToSqrt(currentCombat)) + { + Mobile.Combatant = aggressor; + } + } + + public virtual void EndPickTarget(Mobile from, Mobile target, OrderType order) + { + if (!IsValidTarget(from, order) || order == OrderType.Attack && !CanAttackTarget(from, target)) + { + return; + } + + if (Mobile.CheckControlChance(from)) + { + Mobile.ControlTarget = target; + Mobile.ControlOrder = order; + + if (order == OrderType.Attack) + { + Mobile.FocusMob = target; + Mobile.Combatant = target; + Action = ActionType.Combat; + } + } + } + + private bool IsValidTarget(Mobile from, OrderType order) + { + if (Mobile.Deleted || !Mobile.Controlled || !from.InRange(Mobile, 14) + || from.Map != Mobile.Map || !from.CheckAlive()) + { + return false; + } + + var isOwner = from == Mobile.ControlMaster; + var isFriend = !isOwner && Mobile.IsPetFriend(from); + + if (!isOwner && !isFriend) + { + return false; + } + + if (isFriend && order is not (OrderType.Follow or OrderType.Stay or OrderType.Stop)) + { + return false; + } + + return true; + } + + private bool CanAttackTarget(Mobile from, Mobile target) + { + if (target is BaseCreature creature && creature.IsScaryToPets && Mobile.IsScaredOfScaryThings) + { + Mobile.SayTo(from, "Your pet refuses to attack this creature!"); + return false; + } + + if (SolenHelper.CheckRedFriendship(from) && + target is RedSolenInfiltratorQueen or RedSolenInfiltratorWarrior or RedSolenQueen or RedSolenWarrior or RedSolenWorker || + SolenHelper.CheckBlackFriendship(from) && + target is BlackSolenInfiltratorQueen or BlackSolenInfiltratorWarrior or BlackSolenQueen or BlackSolenWarrior or BlackSolenWorker) + { + from.SendLocalizedMessage(1063106); + // You can not force your pet to attack a creature you are protected from. + return false; + } + + if (target is BaseFactionGuard) + { + Mobile.SayTo(from, "Your pet refuses to attack the guard."); + return false; + } + + return true; + } + + public void DebugSay(string message, int cooldownMs = 5000) + { + if (Mobile.Debug && NextDebugMessage - Core.TickCount <= 0) + { + Mobile.PublicOverheadMessage(MessageType.Regular, 41, false, message); + NextDebugMessage = Core.TickCount + cooldownMs; + } + } + + public virtual bool Think() + { + if (Mobile.Deleted || Mobile.Map == null) + { + return false; + } + + if (CheckFlee()) + { + return true; + } + + switch (Action) + { + case ActionType.Wander: + { + Mobile.OnActionWander(); + return DoActionWander(); + } + case ActionType.Combat: + { + Mobile.OnActionCombat(); + return DoActionCombat(); + } + case ActionType.Guard: + { + Mobile.OnActionGuard(); + return DoActionGuard(); + } + case ActionType.Flee: + { + Mobile.OnActionFlee(); + return DoActionFlee(); + } + case ActionType.Interact: + { + Mobile.OnActionInteract(); + return DoActionInteract(); + } + case ActionType.Backoff: + { + Mobile.OnActionBackoff(); + return DoActionBackoff(); + } + } + + return false; + } + + public virtual void OnActionChanged() + { + switch (Action) + { + case ActionType.Wander: + { + HandleWanderAction(); + break; + } + case ActionType.Combat: + { + HandleCombatAction(); + break; + } + case ActionType.Guard: + { + HandleGuardAction(); + break; + } + case ActionType.Flee: + { + HandleFleeAction(); + break; + } + case ActionType.Interact: + { + HandleInteractAction(); + break; + } + case ActionType.Backoff: + { + HandleBackoffAction(); + break; + } + } + } + + private void HandleWanderAction() + { + Mobile.FocusMob = null; + Mobile.Warmode = false; + Mobile.Combatant = null; + } + + private void HandleCombatAction() + { + Mobile.Warmode = true; + } + + private void HandleGuardAction() + { + Mobile.Warmode = true; + Mobile.Combatant = null; + } + + private void HandleFleeAction() + { + Mobile.FocusMob = null; + Mobile.Warmode = true; + } + + private void HandleInteractAction() + { + Mobile.Warmode = false; + } + + private void HandleBackoffAction() + { + Mobile.Warmode = false; + } + + public virtual bool DoActionWander() + { + if (CheckHerding()) + { + this.DebugSayFormatted($"I am being herded by {Mobile.ControlTarget?.Name ?? "Unknown"}."); + } + else if (Mobile.CurrentWayPoint != null) + { + HandleWayPoint(); + } + else if (Mobile.IsAnimatedDead) + { + FollowMaster(); + } + else if (CheckMove() && CanMoveNow(out _) && !Mobile.CheckIdle()) + { + WalkRandomInHome(3, 2, 1); + } + + return true; + } + + public virtual bool OnAtWayPoint() => true; + + private void HandleWayPoint() + { + var point = Mobile.CurrentWayPoint; + + if ((point.X != Mobile.Location.X || point.Y != Mobile.Location.Y) + && point.Map == Mobile.Map && point.Parent == null && !point.Deleted) + { + this.DebugSayFormatted($"Moving towards waypoint {point.X}, {point.Y}."); + + DoMove(Mobile.GetDirectionTo(point)); + } + else if (OnAtWayPoint()) + { + this.DebugSayFormatted($"I have reached waypoint {point.X}, {point.Y}."); + + Mobile.CurrentWayPoint = point.NextPoint; + if (point.NextPoint?.Deleted == true) + { + Mobile.CurrentWayPoint = point.NextPoint = point.NextPoint.NextPoint; + } + } + } + + private void FollowMaster() + { + var master = Mobile.SummonMaster; + if (master != null && master.Map == Mobile.Map && master.InRange(Mobile, Mobile.RangePerception)) + { + MoveTo(master, false, 1); + } + else + { + WalkRandomInHome(3, 2, 1); + } + } + + public virtual bool DoActionCombat() + { + if (Core.AOS && CheckHerding()) + { + this.DebugSayFormatted($"I am being herded by {Mobile.ControlTarget?.Name ?? "Unknown"}."); + return true; + } + + var combatant = Mobile.Combatant; + if (!IsValidCombatant(combatant)) + { + DebugSay("My combatant is missing. Returning home..."); + + Mobile.FocusMob = null; + Mobile.Warmode = false; + Mobile.Combatant = null; + + WalkRandomInHome(3, 2, 1); + return true; + } + + if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) + { + this.DebugSayFormatted($"I used my abilities on {combatant.Name}!"); + } + + return true; + } + + public bool IsValidCombatant(Mobile combatant) => + IsValidFocusMob(combatant) && Mobile.InLOS(combatant); + + public bool IsValidFocusMob(Mobile focusMob) => + focusMob != null + && !focusMob.Deleted + && focusMob.Map == Mobile.Map + && focusMob.Alive + && (focusMob is not BaseCreature bc || !bc.IsDeadPet) + && focusMob.AccessLevel == AccessLevel.Player + && Mobile.CanSee(focusMob) + && Mobile.InRange(focusMob, Mobile.RangePerception); + + public virtual bool DoActionGuard() + { + if (Mobile.Combatant == null) + { + DebugSay("No threats found. Going home..."); + Action = ActionType.Wander; + } + + DebugSay("I stopped being on guard."); + Action = ActionType.Wander; + + return true; + } + + public virtual bool DoActionFlee() + { + var from = Mobile.FocusMob; + + if (!IsValidFocusMob(from)) + { + DebugSay("Focus target is missing."); + + WalkRandomInHome(3, 2, 1); + return true; + } + + DebugSay("I am fleeing!"); + + DoMove(from.GetDirectionTo(Mobile)); + return true; + } + + public virtual bool DoActionInteract() => true; + + public virtual bool DoActionBackoff() => true; + + public virtual bool CheckHerding() + { + var target = Mobile.TargetLocation; + + if (target == null) + { + return false; + } + + var distance = Mobile.GetDistanceToSqrt(target); + + if (distance >= 1 && distance <= 15) + { + DoMove(Mobile.GetDirectionTo(target)); + return true; + } + + if (distance < 1 && IsSpecialHerdingCase(target)) + { + HandleSpecialHerdingCase(); + } + + Mobile.TargetLocation = null; + return false; + } + + private bool IsSpecialHerdingCase(IPoint2D target) => target.X == 1076 && target.Y == 450 && Mobile is HordeMinionFamiliar; + + private void HandleSpecialHerdingCase() + { + if (Mobile.ControlMaster is PlayerMobile pm && pm.Quest is DarkTidesQuest qs) + { + var obj = qs.FindObjective(); + + if (obj?.Completed == false) + { + Mobile.AddToBackpack(new ScrollOfAbraxus()); + obj.Complete(); + } + } + } + + public virtual void DoBardPacified() + { + if (Core.Now < Mobile.BardEndTime) + { + DebugSay("I am pacified. Can not fight."); + + Mobile.Warmode = false; + Mobile.Combatant = null; + } + else + { + DebugSay("I am free from pacification."); + + Mobile.BardPacified = false; + } + } + + public virtual void DoBardProvoked() + { + if (Core.Now >= Mobile.BardEndTime && IsProvokerLost()) + { + DebugSay("Provoker missing."); + + Mobile.BardProvoked = false; + Mobile.BardMaster = null; + Mobile.BardTarget = null; + Mobile.Warmode = false; + Mobile.Combatant = null; + } + else if (IsProvokeTargetLost()) + { + DebugSay("Provoke target missing."); + + Mobile.BardProvoked = false; + Mobile.BardMaster = null; + Mobile.BardTarget = null; + Mobile.Warmode = false; + Mobile.Combatant = null; + } + else + { + Mobile.Combatant = Mobile.BardTarget; + Action = ActionType.Combat; + } + } + + private bool IsProvokerLost() => + Mobile.BardMaster?.Deleted != false + || Mobile.BardMaster.Map != Mobile.Map + || Mobile.GetDistanceToSqrt(Mobile.BardMaster) > Mobile.RangePerception; + + private bool IsProvokeTargetLost() => + Mobile.BardTarget?.Deleted != false + || Mobile.BardTarget.Map != Mobile.Map + || Mobile.GetDistanceToSqrt(Mobile.BardTarget) > Mobile.RangePerception; + + public virtual bool CheckFlee() + { + if (!Mobile.CheckFlee()) + { + return false; + } + + if (Mobile.Combatant == null) + { + WalkRandomInHome(3, 2, 1); + } + + return true; + } + + public virtual void OnTeleported() + { + DebugSay("Teleported; recalculating path..."); + + Path?.ForceRepath(); + } + + public virtual bool AcquireFocusMob(int iRange, FightMode acqType, bool bPlayerOnly, bool bFacFriend, bool bFacFoe) + { + if (Mobile.Deleted || Mobile.Map == null) + { + return false; + } + + if (HandleBardProvoked() || HandleControlled() || HandleConstantFocus()) + { + return true; + } + + if (acqType == FightMode.None) + { + Mobile.FocusMob = null; + return false; + } + + if (HandleAggressor(acqType)) + { + return false; + } + + if (Core.TickCount - Mobile.NextReacquireTime < 0) + { + Mobile.FocusMob = null; + return false; + } + + Mobile.NextReacquireTime = Core.TickCount + (int)Mobile.ReacquireDelay.TotalMilliseconds; + + DebugSay("Acquiring new target..."); + + if (Mobile.Map == null) + { + return Mobile.FocusMob != null; + } + + return AcquireNewFocusMob(Mobile.Map, iRange, acqType, bPlayerOnly, bFacFriend, bFacFoe); + } + + private bool HandleBardProvoked() + { + if (!Mobile.BardProvoked) + { + return false; + } + + if (Mobile.BardTarget?.Deleted != false) + { + Mobile.FocusMob = null; + return false; + } + + Mobile.FocusMob = Mobile.BardTarget; + return true; + } + + private bool HandleControlled() + { + if (!Mobile.Controlled) + { + return false; + } + + if (Mobile.ControlTarget?.Deleted == false && + Mobile.ControlTarget?.Hidden != true && + Mobile.ControlTarget?.Alive == true && + Mobile.ControlTarget?.IsDeadBondedPet != true && + Mobile.InRange(Mobile.ControlTarget, Mobile.RangePerception * 2)) + { + Mobile.FocusMob = Mobile.ControlTarget; + return true; + } + + if (Mobile.ControlTarget != null && Mobile.ControlTarget != Mobile.ControlMaster) + { + Mobile.ControlTarget = null; + } + + Mobile.FocusMob = null; + return false; + } + + private bool HandleConstantFocus() + { + if (Mobile.ConstantFocus == null) + { + return false; + } + + this.DebugSayFormatted($"Acquired focused target: {Mobile.ConstantFocus.Name}."); + + Mobile.FocusMob = Mobile.ConstantFocus; + return true; + } + + private bool HandleAggressor(FightMode acqType) + { + if (acqType != FightMode.Aggressor || + Mobile.Aggressors.Count > 0 || + Mobile.Aggressed.Count > 0 || + Mobile.FactionAllegiance != null || + Mobile.EthicAllegiance != null) + { + return false; + } + + Mobile.FocusMob = null; + return true; + } + + private bool AcquireNewFocusMob(Map map, int iRange, FightMode acqType, bool bPlayerOnly, bool bFacFriend, bool bFacFoe) + { + Mobile newFocusMob = null, enemySummonMob = null; + double val = double.MinValue, enemySummonVal = double.MinValue; + + foreach (var m in map.GetMobilesInRange(Mobile.Location, iRange)) + { + if (IsInvalidTarget(m, bPlayerOnly)) + { + continue; + } + + var bc = m as BaseCreature; + var pm = m as PlayerMobile; + + if (IsInvalidSummonTarget(m, bc, pm) || IsInvalidFactionTarget(m, bFacFriend, bFacFoe) + || IsInvalidFightModeTarget(m, acqType, bc)) + { + continue; + } + + var theirVal = Mobile.GetFightModeRanking(m, acqType, bPlayerOnly); + + if (theirVal > val && Mobile.InLOS(m)) + { + newFocusMob = m; + val = theirVal; + } + else if (Core.AOS && theirVal > enemySummonVal + && Mobile.InLOS(m) && bc?.Summoned == true && bc.Controlled != true) + { + enemySummonMob = m; + enemySummonVal = theirVal; + } + } + + Mobile.FocusMob = newFocusMob ?? enemySummonMob; + return Mobile.FocusMob != null; + } + + private bool IsInvalidTarget(Mobile m, bool bPlayerOnly) => + m.Deleted || m.Blessed || m == Mobile || m is BaseFamiliar || !m.Alive || m.IsDeadBondedPet || + m.AccessLevel > AccessLevel.Player || bPlayerOnly && !m.Player || !Mobile.CanSee(m); + + private bool IsInvalidSummonTarget(Mobile m, BaseCreature bc, PlayerMobile pm) + { + if (Core.AOS && bc?.Summoned == true && + (bc.SummonMaster == Mobile || !bc.SummonMaster.Player && IsHostile(bc.SummonMaster))) + { + return true; + } + + if (!Mobile.Summoned || Mobile.SummonMaster == null) + { + return false; + } + + return m == Mobile.SummonMaster || !SpellHelper.ValidIndirectTarget(Mobile.SummonMaster, m) || + Mobile.IsAnimatedDead && (pm != null || bc?.IsAnimatedDead == true || bc?.Controlled == true); + } + + private bool IsInvalidFactionTarget(Mobile m, bool bFacFriend, bool bFacFoe) + { + if (bFacFriend && !Mobile.IsFriend(m)) + { + return true; + } + + if (TransformationSpellHelper.UnderTransformation(m, typeof(EtherealVoyageSpell)) || + Mobile.Combatant != m && VirtueSystem.GetVirtues(m as PlayerMobile)?.HonorActive == true) + { + return true; + } + + return bFacFoe && (!Mobile.IsEnemy(m) || !bFacFriend && !Mobile.CanBeHarmful(m, false)); + } + + private bool IsInvalidFightModeTarget(Mobile m, FightMode acqType, BaseCreature bc) + { + if (acqType is not (FightMode.Aggressor or FightMode.Evil)) + { + return false; + } + + var valid = IsHostile(m) || Mobile.GetFactionAllegiance(m) == BaseCreature.Allegiance.Enemy + || Mobile.GetEthicAllegiance(m) == BaseCreature.Allegiance.Enemy; + + // Valid if FightMode is Evil and the target's karma is negative + return !valid && acqType != FightMode.Evil || (bc?.GetMaster()?.Karma ?? m.Karma) >= 0; + } + + private bool IsHostile(Mobile from) => Mobile.Combatant == from || from.Combatant == Mobile || IsAggressor(from) || IsAggressed(from); + + private bool IsAggressor(Mobile from) + { + foreach (var aggressor in Mobile.Aggressors) + { + if (aggressor.Defender == from) + { + return true; + } + } + return false; + } + + private bool IsAggressed(Mobile from) + { + foreach (var aggressed in Mobile.Aggressed) + { + if (aggressed.Attacker == from) + { + return true; + } + } + + return false; + } + + public virtual void DetectHidden() + { + if (Mobile.Deleted || Mobile.Map == null || !CanDetectHidden) + { + return; + } + + DebugSay("Checking for hidden entities..."); + + var srcSkill = Mobile.Skills.DetectHidden.Value; + + if (srcSkill <= 0) + { + return; + } + + foreach (var trg in Mobile.GetMobilesInRange(Mobile.RangePerception)) + { + if (IsValidTargetCombatTarget(trg)) + { + TryDetectHidden(trg, srcSkill); + } + } + } + + private bool IsValidTargetCombatTarget(Mobile trg) => trg != Mobile && trg.Player && trg.Alive && trg.Hidden && + trg.AccessLevel == AccessLevel.Player && Mobile.InLOS(trg); + + private void TryDetectHidden(Mobile trg, double srcSkill) + { + this.DebugSayFormatted($"Trying to detect: {trg.Name}"); + + var trgHiding = trg.Skills.Hiding.Value / 2.9; + var trgStealth = trg.Skills.Stealth.Value / 1.8; + var chance = Math.Max(srcSkill / 10, srcSkill / 1.2 - Math.Min(trgHiding, trgStealth)) / 100; + + if (chance > Utility.RandomDouble()) + { + trg.RevealingAction(); + trg.SendLocalizedMessage(500814); + // You have been revealed! + } + } + + public virtual void Deactivate() + { + if (!Mobile.PlayerRangeSensitive) + { + return; + } + + if (Mobile.Map == Map.Internal || !Mobile.Controlled && !Mobile.Map.GetSector(Mobile.Location).Active) + { + _timer.Stop(); + } + + if (ShouldReturnToHome(Mobile.Spawner as Spawner)) + { + Timer.StartTimer(ReturnToHome); + } + } + + private bool ShouldReturnToHome(Spawner spawner) => + spawner?.ReturnOnDeactivate == true && !Mobile.Controlled && + (spawner.HomeLocation == Point3D.Zero || !Mobile.InRange(spawner.HomeLocation, spawner.HomeRange)); + + private void ReturnToHome() + { + if (Mobile.Spawner is not Spawner spawner) + { + return; + } + + var loc = spawner.GetSpawnPosition(Mobile, spawner.Map); + + if (loc != Point3D.Zero) + { + Mobile.MoveToWorld(loc, spawner.Map); + } + + _timer.Start(); + } + + public virtual void Activate() + { + if (!_timer.Running) + { + _timer.Start(); + } + } + + public virtual void OnCurrentSpeedChanged() + { + _timer.Interval = TimeSpan.FromMilliseconds(Mobile.CurrentSpeed * 1000); + } +} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/BaseAIExtensions.cs b/Projects/UOContent/Mobiles/AI/BaseAI/BaseAIExtensions.cs new file mode 100644 index 000000000..e46181c00 --- /dev/null +++ b/Projects/UOContent/Mobiles/AI/BaseAI/BaseAIExtensions.cs @@ -0,0 +1,20 @@ +using System.Runtime.CompilerServices; +using Server.Mobiles.AI.BaseAI; + +namespace Server.Mobiles; + +public static class BaseAIExtensions +{ + public static void DebugSayFormatted(this BaseAI ai, + [InterpolatedStringHandlerArgument("ai")] ref DebugInterpolatedStringHandler handler, int cooldownMs = 5000) + { + var message = handler.Text; + if (message.Length > 0) + { + ai.Mobile.PublicOverheadMessage(MessageType.Regular, 41, false, message.ToString()); + ai.NextDebugMessage = Core.TickCount + cooldownMs; + } + + handler.Clear(); + } +} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/ContextMenu.cs b/Projects/UOContent/Mobiles/AI/BaseAI/ContextMenu.cs new file mode 100644 index 000000000..ca562bce7 --- /dev/null +++ b/Projects/UOContent/Mobiles/AI/BaseAI/ContextMenu.cs @@ -0,0 +1,75 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2025 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: ContextMenu.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ************************************************************************/ + +using Server.Collections; +using Server.ContextMenus; + +namespace Server.Mobiles; + +public abstract partial class BaseAI +{ + public virtual void GetContextMenuEntries(Mobile from, ref PooledRefList list) + { + if (!from.Alive || !Mobile.Controlled || !from.InRange(Mobile, 16)) + { + return; + } + + if (from == Mobile.ControlMaster) + { + AddControlMasterEntries(ref list); + } + else if (Mobile.IsPetFriend(from)) + { + AddPetFriendEntries(ref list); + } + } + + private void AddControlMasterEntries(ref PooledRefList list) + { + var isDeadPet = Mobile.IsDeadPet; + + list.Add(new InternalEntry(3006111, 14, OrderType.Attack, !isDeadPet)); // Command: Kill + list.Add(new InternalEntry(3006108, 14, OrderType.Follow, true)); // Command: Follow + list.Add(new InternalEntry(3006107, 14, OrderType.Guard, !isDeadPet)); // Command: Guard + list.Add(new InternalEntry(3006112, 14, OrderType.Stop, true)); // Command: Stop + list.Add(new InternalEntry(3006114, 14, OrderType.Stay, true)); // Command: Stay + + if (Mobile.CanDrop) + { + list.Add(new InternalEntry(3006109, 14, OrderType.Drop, !isDeadPet)); // Command: Drop + } + + list.Add(new InternalEntry(3006098, 14, OrderType.Rename, true)); // Rename + + if (!Mobile.Summoned && Mobile is not GrizzledMare) + { + list.Add(new InternalEntry(3006110, 14, OrderType.Friend, true)); // Add Friend + list.Add(new InternalEntry(3006099, 14, OrderType.Unfriend, true)); // Remove Friend + list.Add(new InternalEntry(3006113, 14, OrderType.Transfer, !isDeadPet)); // Transfer + } + + list.Add(new InternalEntry(3006118, 14, OrderType.Release, true)); // Release + } + + private void AddPetFriendEntries(ref PooledRefList list) + { + var isDeadPet = Mobile.IsDeadPet; + + list.Add(new InternalEntry(3006108, 14, OrderType.Follow, true)); // Command: Follow + list.Add(new InternalEntry(3006112, 14, OrderType.Stop, !isDeadPet)); // Command: Stop + list.Add(new InternalEntry(3006114, 14, OrderType.Stay, true)); // Command: Stay + } +} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/DebugInterpolatedStringHandler.cs b/Projects/UOContent/Mobiles/AI/BaseAI/DebugInterpolatedStringHandler.cs new file mode 100644 index 000000000..da1f80019 --- /dev/null +++ b/Projects/UOContent/Mobiles/AI/BaseAI/DebugInterpolatedStringHandler.cs @@ -0,0 +1,663 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.CompilerServices; +using Server.Buffers; + +namespace Server.Mobiles.AI.BaseAI; + +[InterpolatedStringHandler] +public ref struct DebugInterpolatedStringHandler +{ + // Implementation note: + // As this type lives in CompilerServices and is only intended to be targeted by the compiler, + // public APIs eschew argument validation logic in a variety of places, e.g. allowing a null input + // when one isn't expected to produce a NullReferenceException rather than an ArgumentNullException. + + /// Expected average length of formatted data used for an individual interpolation expression result. + /// + /// This is inherited from string.Format, and could be changed based on further data. + /// string.Format actually uses `format.Length + args.Length * 8`, but format.Length + /// includes the format items themselves, e.g. "{0}", and since it's rare to have double-digit + /// numbers of items, we bump the 8 up to 11 to account for the three extra characters in "{d}", + /// since the compiler-provided base length won't include the equivalent character count. + /// + private const int GuessedLengthPerHole = 11; + /// Minimum size array to rent from the pool. + /// Same as stack-allocation size used today by string.Format. + private const int MinimumArrayPoolLength = 256; + + /// Optional provider to pass to IFormattable.ToString or ISpanFormattable.TryFormat calls. + private readonly IFormatProvider? _provider; + /// Array rented from the array pool and used to back . + private char[]? _arrayToReturnToPool; + /// The span to write into. + private Span _chars; + /// Position at which to write the next character. + private int _pos; + /// Whether provides an ICustomFormatter. + /// + /// Custom formatters are very rare. We want to support them, but it's ok if we make them more expensive + /// in order to make them as pay-for-play as possible. So, we avoid adding another reference type field + /// to reduce the size of the handler and to reduce required zero'ing, by only storing whether the provider + /// provides a formatter, rather than actually storing the formatter. This in turn means, if there is a + /// formatter, we pay for the extra interface call on each AppendFormatted that needs it. + /// + private readonly bool _hasCustomFormatter; + + private readonly bool _debugActive; + + /// Creates a handler used to translate an interpolated string into a . + /// The number of constant characters outside of interpolation expressions in the interpolated string. + /// The number of interpolation expressions in the interpolated string. + /// This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly. + public DebugInterpolatedStringHandler(int literalLength, int formattedCount, Mobiles.BaseAI ai) + { + _debugActive = ai.Mobile?.Debug == true && Core.TickCount >= ai.NextDebugMessage; + + if (_debugActive) + { + _chars = _arrayToReturnToPool = STArrayPool.Shared.Rent(GetDefaultLength(literalLength, formattedCount)); + } + else + { + _chars = _arrayToReturnToPool = null; + } + + _provider = null; + _pos = 0; + _hasCustomFormatter = false; + } + + /// Creates a handler used to translate an interpolated string into a . + /// The number of constant characters outside of interpolation expressions in the interpolated string. + /// The number of interpolation expressions in the interpolated string. + /// An object that supplies culture-specific formatting information. + /// This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly. + public DebugInterpolatedStringHandler(int literalLength, int formattedCount, IFormatProvider? provider, Mobiles.BaseAI ai) + { + _debugActive = ai.Mobile?.Debug == true && Core.TickCount >= ai.NextDebugMessage; + + if (_debugActive) + { + _chars = _arrayToReturnToPool = STArrayPool.Shared.Rent(GetDefaultLength(literalLength, formattedCount)); + } + else + { + _chars = _arrayToReturnToPool = null; + } + + _provider = provider; + _pos = 0; + _hasCustomFormatter = provider is not null && HasCustomFormatter(provider); + } + + /// Derives a default length with which to seed the handler. + /// The number of constant characters outside of interpolation expressions in the interpolated string. + /// The number of interpolation expressions in the interpolated string. + [MethodImpl(MethodImplOptions.AggressiveInlining)] // becomes a constant when inputs are constant + internal static int GetDefaultLength(int literalLength, int formattedCount) => + Math.Max(MinimumArrayPoolLength, literalLength + formattedCount * GuessedLengthPerHole); + + /// Clears the handler, returning any rented array to the pool. + [MethodImpl(MethodImplOptions.AggressiveInlining)] // used only on a few hot paths + public void Clear() + { + char[]? toReturn = _arrayToReturnToPool; + this = default; // defensive clear + if (toReturn is not null) + { + STArrayPool.Shared.Return(toReturn); + } + } + + /// Gets a span of the written characters thus far. + public ReadOnlySpan Text => _chars[.._pos]; + + /// Writes the specified string to the handler. + /// The string to write. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AppendLiteral(string value) + { + if (!_debugActive) + { + return; + } + + if (value.Length == 1) + { + Span chars = _chars; + int pos = _pos; + if ((uint)pos < (uint)chars.Length) + { + chars[pos] = value[0]; + _pos = pos + 1; + } + else + { + GrowThenCopyString(value); + } + return; + } + + AppendStringDirect(value); + } + + /// Writes the specified string to the handler. + /// The string to write. + private void AppendStringDirect(string value) + { + if (value.TryCopyTo(_chars[_pos..])) + { + _pos += value.Length; + } + else + { + GrowThenCopyString(value); + } + } + + #region AppendFormatted + // Design note: + // The compiler requires a AppendFormatted overload for anything that might be within an interpolation expression; + // if it can't find an appropriate overload, for handlers in general it'll simply fail to compile. + // (For target-typing to string where it uses DefaultInterpolatedStringHandler implicitly, it'll instead fall back to + // its other mechanisms, e.g. using string.Format. This fallback has the benefit that if we miss a case, + // interpolated strings will still work, but it has the downside that a developer generally won't know + // if the fallback is happening and they're paying more.) + // + // At a minimum, then, we would need an overload that accepts: + // (object value, int alignment = 0, string? format = null) + // Such an overload would provide the same expressiveness as string.Format. However, this has several + // shortcomings: + // - Every value type in an interpolation expression would be boxed. + // - ReadOnlySpan could not be used in interpolation expressions. + // - Every AppendFormatted call would have three arguments at the call site, bloating the IL further. + // - Every invocation would be more expensive, due to lack of specialization, every call needing to account + // for alignment and format, etc. + // + // To address that, we could just have overloads for T and ReadOnlySpan: + // (T) + // (T, int alignment) + // (T, string? format) + // (T, int alignment, string? format) + // (ReadOnlySpan) + // (ReadOnlySpan, int alignment) + // (ReadOnlySpan, string? format) + // (ReadOnlySpan, int alignment, string? format) + // but this also has shortcomings: + // - Some expressions that would have worked with an object overload will now force a fallback to string.Format + // (or fail to compile if the handler is used in places where the fallback isn't provided), because the compiler + // can't always target type to T, e.g. `b switch { true => 1, false => null }` where `b` is a bool can successfully + // be passed as an argument of type `object` but not of type `T`. + // - Reference types get no benefit from going through the generic code paths, and actually incur some overheads + // from doing so. + // - Nullable value types also pay a heavy price, in particular around interface checks that would generally evaporate + // at compile time for value types but don't (currently) if the Nullable goes through the same code paths + // (see https://github.com/dotnet/runtime/issues/50915). + // + // We could try to take a more elaborate approach for DefaultInterpolatedStringHandler, since it is the most common handler + // and we want to minimize overheads both at runtime and in IL size, e.g. have a complete set of overloads for each of: + // (T, ...) where T : struct + // (T?, ...) where T : struct + // (object, ...) + // (ReadOnlySpan, ...) + // (string, ...) + // but this also has shortcomings, most importantly: + // - If you have an unconstrained T that happens to be a value type, it'll now end up getting boxed to use the object overload. + // This also necessitates the T? overload, since nullable value types don't meet a T : struct constraint, so without those + // they'd all map to the object overloads as well. + // - Any reference type with an implicit cast to ROS will fail to compile due to ambiguities between the overloads. string + // is one such type, hence needing dedicated overloads for it that can be bound to more tightly. + // + // A middle ground we've settled on, which is likely to be the right approach for most other handlers as well, would be the set: + // (T, ...) with no constraint + // (ReadOnlySpan) and (ReadOnlySpan, int) + // (object, int alignment = 0, string? format = null) + // (string) and (string, int) + // This would address most of the concerns, at the expense of: + // - Most reference types going through the generic code paths and so being a bit more expensive. + // - Nullable types being more expensive until https://github.com/dotnet/runtime/issues/50915 is addressed. + // We could choose to add a T? where T : struct set of overloads if necessary. + // Strings don't require their own overloads here, but as they're expected to be very common and as we can + // optimize them in several ways (can copy the contents directly, don't need to do any interface checks, don't + // need to pay the shared generic overheads, etc.) we can add overloads specifically to optimize for them. + // + // Hole values are formatted according to the following policy: + // 1. If an IFormatProvider was supplied and it provides an ICustomFormatter, use ICustomFormatter.Format (even if the value is null). + // 2. If the type implements ISpanFormattable, use ISpanFormattable.TryFormat. + // 3. If the type implements IFormattable, use IFormattable.ToString. + // 4. Otherwise, use object.ToString. + // This matches the behavior of string.Format, StringBuilder.AppendFormat, etc. The only overloads for which this doesn't + // apply is ReadOnlySpan, which isn't supported by either string.Format nor StringBuilder.AppendFormat, but more + // importantly which can't be boxed to be passed to ICustomFormatter.Format. + + #region AppendFormatted T + /// Writes the specified value to the handler. + /// The value to write. + public void AppendFormatted(T value) + { + if (!_debugActive) + { + return; + } + + // This method could delegate to AppendFormatted with a null format, but explicitly passing + // default as the format to TryFormat helps to improve code quality in some cases when TryFormat is inlined, + // e.g. for Int32 it enables the JIT to eliminate code in the inlined method based on a length check on the format. + + // If there's a custom formatter, always use it. + if (_hasCustomFormatter) + { + AppendCustomFormatter(value, format: null); + return; + } + + // Check first for IFormattable, even though we'll prefer to use ISpanFormattable, as the latter + // requires the former. For value types, it won't matter as the type checks devolve into + // JIT-time constants. For reference types, they're more likely to implement IFormattable + // than they are to implement ISpanFormattable: if they don't implement either, we save an + // interface check over first checking for ISpanFormattable and then for IFormattable, and + // if it only implements IFormattable, we come out even: only if it implements both do we + // end up paying for an extra interface check. + string? s; + if (value is IFormattable) + { + // If the value can format itself directly into our buffer, do so. + if (value is ISpanFormattable) + { + int charsWritten; + while (!((ISpanFormattable)value).TryFormat(_chars[_pos..], out charsWritten, default, _provider)) // constrained call avoiding boxing for value types + { + Grow(); + } + + _pos += charsWritten; + return; + } + + s = ((IFormattable)value).ToString(format: null, _provider); // constrained call avoiding boxing for value types + } + else + { + s = value?.ToString(); + } + + if (s is not null) + { + AppendStringDirect(s); + } + } + /// Writes the specified value to the handler. + /// The value to write. + /// The format string. + public void AppendFormatted(T value, string? format) + { + if (!_debugActive) + { + return; + } + + // If there's a custom formatter, always use it. + if (_hasCustomFormatter) + { + AppendCustomFormatter(value, format); + return; + } + + // Check first for IFormattable, even though we'll prefer to use ISpanFormattable, as the latter + // requires the former. For value types, it won't matter as the type checks devolve into + // JIT-time constants. For reference types, they're more likely to implement IFormattable + // than they are to implement ISpanFormattable: if they don't implement either, we save an + // interface check over first checking for ISpanFormattable and then for IFormattable, and + // if it only implements IFormattable, we come out even: only if it implements both do we + // end up paying for an extra interface check. + string? s; + if (value is IFormattable) + { + // If the value can format itself directly into our buffer, do so. + if (value is ISpanFormattable) + { + int charsWritten; + while (!((ISpanFormattable)value).TryFormat(_chars[_pos..], out charsWritten, format, _provider)) // constrained call avoiding boxing for value types + { + Grow(); + } + + _pos += charsWritten; + return; + } + + s = ((IFormattable)value).ToString(format, _provider); // constrained call avoiding boxing for value types + } + else + { + s = value?.ToString(); + } + + if (s is not null) + { + AppendStringDirect(s); + } + } + + /// Writes the specified value to the handler. + /// The value to write. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + public void AppendFormatted(T value, int alignment) + { + if (!_debugActive) + { + return; + } + + int startingPos = _pos; + AppendFormatted(value); + if (alignment != 0) + { + AppendOrInsertAlignmentIfNeeded(startingPos, alignment); + } + } + + /// Writes the specified value to the handler. + /// The value to write. + /// The format string. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + public void AppendFormatted(T value, int alignment, string? format) + { + if (!_debugActive) + { + return; + } + + int startingPos = _pos; + AppendFormatted(value, format); + if (alignment != 0) + { + AppendOrInsertAlignmentIfNeeded(startingPos, alignment); + } + } + #endregion + + #region AppendFormatted ReadOnlySpan + /// Writes the specified character span to the handler. + /// The span to write. + public void AppendFormatted(ReadOnlySpan value) + { + if (!_debugActive) + { + return; + } + + // Fast path for when the value fits in the current buffer + if (value.TryCopyTo(_chars[_pos..])) + { + _pos += value.Length; + } + else + { + GrowThenCopySpan(value); + } + } + + /// Writes the specified string of chars to the handler. + /// The span to write. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + /// The format string. + public void AppendFormatted(ReadOnlySpan value, int alignment = 0, string? format = null) + { + if (!_debugActive) + { + return; + } + + bool leftAlign = false; + if (alignment < 0) + { + leftAlign = true; + alignment = -alignment; + } + + int paddingRequired = alignment - value.Length; + if (paddingRequired <= 0) + { + // The value is as large or larger than the required amount of padding, + // so just write the value. + AppendFormatted(value); + return; + } + + // Write the value along with the appropriate padding. + EnsureCapacityForAdditionalChars(value.Length + paddingRequired); + if (leftAlign) + { + value.CopyTo(_chars[_pos..]); + _pos += value.Length; + _chars.Slice(_pos, paddingRequired).Fill(' '); + _pos += paddingRequired; + } + else + { + _chars.Slice(_pos, paddingRequired).Fill(' '); + _pos += paddingRequired; + value.CopyTo(_chars[_pos..]); + _pos += value.Length; + } + } + #endregion + + #region AppendFormatted string + /// Writes the specified value to the handler. + /// The value to write. + public void AppendFormatted(string? value) + { + if (!_debugActive) + { + return; + } + + // Fast-path for no custom formatter and a non-null string that fits in the current destination buffer. + if (!_hasCustomFormatter && value?.TryCopyTo(_chars[_pos..]) == true) + { + _pos += value.Length; + } + else + { + AppendFormattedSlow(value); + } + } + + /// Writes the specified value to the handler. + /// The value to write. + /// + /// Slow path to handle a custom formatter, potentially null value, + /// or a string that doesn't fit in the current buffer. + /// + [MethodImpl(MethodImplOptions.NoInlining)] + private void AppendFormattedSlow(string? value) + { + if (_hasCustomFormatter) + { + AppendCustomFormatter(value, format: null); + } + else if (value is not null) + { + EnsureCapacityForAdditionalChars(value.Length); + value.CopyTo(_chars[_pos..]); + _pos += value.Length; + } + } + + /// Writes the specified value to the handler. + /// The value to write. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + /// The format string. + public void AppendFormatted(string? value, int alignment = 0, string? format = null) => + // Format is meaningless for strings and doesn't make sense for someone to specify. We have the overload + // simply to disambiguate between ROS and object, just in case someone does specify a format, as + // string is implicitly convertible to both. Just delegate to the T-based implementation. + AppendFormatted(value, alignment, format); + #endregion + + #region AppendFormatted object + /// Writes the specified value to the handler. + /// The value to write. + /// Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + /// The format string. + public void AppendFormatted(object? value, int alignment = 0, string? format = null) => + // This overload is expected to be used rarely, only if either a) something strongly typed as object is + // formatted with both an alignment and a format, or b) the compiler is unable to target type to T. It + // exists purely to help make cases from (b) compile. Just delegate to the T-based implementation. + AppendFormatted(value, alignment, format); + #endregion + #endregion + + /// Gets whether the provider provides a custom formatter. + [MethodImpl(MethodImplOptions.AggressiveInlining)] // only used in a few hot path call sites + internal static bool HasCustomFormatter(IFormatProvider provider) + { + Debug.Assert(provider is not null); + Debug.Assert(provider is not CultureInfo || provider.GetFormat(typeof(ICustomFormatter)) is null, "Expected CultureInfo to not provide a custom formatter"); + return + provider.GetType() != typeof(CultureInfo) && // optimization to avoid GetFormat in the majority case + provider.GetFormat(typeof(ICustomFormatter)) != null; + } + + /// Formats the value using the custom formatter from the provider. + /// The value to write. + /// The format string. + [MethodImpl(MethodImplOptions.NoInlining)] + private void AppendCustomFormatter(T value, string? format) + { + // This case is very rare, but we need to handle it prior to the other checks in case + // a provider was used that supplied an ICustomFormatter which wanted to intercept the particular value. + // We do the cast here rather than in the ctor, even though this could be executed multiple times per + // formatting, to make the cast pay for play. + Debug.Assert(_hasCustomFormatter); + Debug.Assert(_provider != null); + + ICustomFormatter? formatter = (ICustomFormatter?)_provider.GetFormat(typeof(ICustomFormatter)); + Debug.Assert(formatter != null, "An incorrectly written provider said it implemented ICustomFormatter, and then didn't"); + + if (formatter?.Format(format, value, _provider) is string customFormatted) + { + AppendStringDirect(customFormatted); + } + } + + /// Handles adding any padding required for aligning a formatted value in an interpolation expression. + /// The position at which the written value started. + /// Non-zero minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value. + private void AppendOrInsertAlignmentIfNeeded(int startingPos, int alignment) + { + Debug.Assert(startingPos >= 0 && startingPos <= _pos); + Debug.Assert(alignment != 0); + + int charsWritten = _pos - startingPos; + + bool leftAlign = false; + if (alignment < 0) + { + leftAlign = true; + alignment = -alignment; + } + + int paddingNeeded = alignment - charsWritten; + if (paddingNeeded > 0) + { + EnsureCapacityForAdditionalChars(paddingNeeded); + + if (leftAlign) + { + _chars.Slice(_pos, paddingNeeded).Fill(' '); + } + else + { + _chars.Slice(startingPos, charsWritten).CopyTo(_chars[(startingPos + paddingNeeded)..]); + _chars.Slice(startingPos, paddingNeeded).Fill(' '); + } + + _pos += paddingNeeded; + } + } + + /// Ensures has the capacity to store beyond . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void EnsureCapacityForAdditionalChars(int additionalChars) + { + if (_chars.Length - _pos < additionalChars) + { + Grow(additionalChars); + } + } + + /// Fallback for fast path in when there's not enough space in the destination. + /// The string to write. + [MethodImpl(MethodImplOptions.NoInlining)] + private void GrowThenCopyString(string value) + { + Grow(value.Length); + value.CopyTo(_chars[_pos..]); + _pos += value.Length; + } + + /// Fallback for for when not enough space exists in the current buffer. + /// The span to write. + [MethodImpl(MethodImplOptions.NoInlining)] + private void GrowThenCopySpan(ReadOnlySpan value) + { + Grow(value.Length); + value.CopyTo(_chars[_pos..]); + _pos += value.Length; + } + + /// Grows to have the capacity to store at least beyond . + [MethodImpl(MethodImplOptions.NoInlining)] // keep consumers as streamlined as possible + private void Grow(int additionalChars) + { + // This method is called when the remaining space (_chars.Length - _pos) is + // insufficient to store a specific number of additional characters. Thus, we + // need to grow to at least that new total. GrowCore will handle growing by more + // than that if possible. + Debug.Assert(additionalChars > _chars.Length - _pos); + GrowCore((uint)_pos + (uint)additionalChars); + } + + /// Grows the size of . + [MethodImpl(MethodImplOptions.NoInlining)] // keep consumers as streamlined as possible + private void Grow() + { + // This method is called when the remaining space in _chars isn't sufficient to continue + // the operation. Thus, we need at least one character beyond _chars.Length. GrowCore + // will handle growing by more than that if possible. + GrowCore((uint)_chars.Length + 1); + } + + /// Grow the size of to at least the specified . + [MethodImpl(MethodImplOptions.AggressiveInlining)] // but reuse this grow logic directly in both of the above grow routines + private void GrowCore(uint requiredMinCapacity) + { + // We want the max of how much space we actually required and doubling our capacity (without going beyond the max allowed length). We + // also want to avoid asking for small arrays, to reduce the number of times we need to grow, and since we're working with unsigned + // ints that could technically overflow if someone tried to, for example, append a huge string to a huge string, we also clamp to int.MaxValue. + // Even if the array creation fails in such a case, we may later fail in ToStringAndClear. + + uint newCapacity = Math.Max(requiredMinCapacity, Math.Min((uint)_chars.Length * 2, 0x3FFFFFDF)); + int arraySize = (int)Math.Clamp(newCapacity, MinimumArrayPoolLength, int.MaxValue); + + char[] newArray = STArrayPool.Shared.Rent(arraySize); + _chars[.._pos].CopyTo(newArray); + + char[]? toReturn = _arrayToReturnToPool; + _chars = _arrayToReturnToPool = newArray; + + if (toReturn is not null) + { + STArrayPool.Shared.Return(toReturn); + } + } +} + diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/InternalEntry.cs b/Projects/UOContent/Mobiles/AI/BaseAI/InternalEntry.cs new file mode 100644 index 000000000..bf8dd029a --- /dev/null +++ b/Projects/UOContent/Mobiles/AI/BaseAI/InternalEntry.cs @@ -0,0 +1,119 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2025 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: InternalEntry.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ************************************************************************/ + +using Server.ContextMenus; +using Server.Gumps; + +namespace Server.Mobiles; + +internal sealed class InternalEntry : ContextMenuEntry +{ + private readonly OrderType _order; + + public InternalEntry(int number, int range, OrderType order, bool enabled) : base(number, range) + { + _order = order; + Enabled = enabled; + } + + public override void OnClick(Mobile from, IEntity target) + { + if (!IsValidClick(from, target, out var bc) || + IsInvalidOrderForDeadPet(bc) || + !IsOwnerOrFriend(from, bc, out var isFriend) || + IsInvalidOrderForFriend(isFriend)) + { + return; + } + + HandleOrder(from, bc); + } + + private static bool IsValidClick(Mobile from, IEntity target, out BaseCreature bc) + { + bc = target as BaseCreature; + return from.CheckAlive() && bc != null && !bc.Deleted && bc.Controlled; + } + + private bool IsInvalidOrderForDeadPet(BaseCreature bc) => bc.IsDeadPet && _order is OrderType.Guard or OrderType.Attack or OrderType.Transfer or OrderType.Drop; + + private static bool IsOwnerOrFriend(Mobile from, BaseCreature bc, out bool isFriend) + { + var isOwner = from == bc.ControlMaster; + isFriend = !isOwner && bc.IsPetFriend(from); + return isOwner || isFriend; + } + + private bool IsInvalidOrderForFriend(bool isFriend) => isFriend && _order is not (OrderType.Follow or OrderType.Stay or OrderType.Stop); + + private void HandleOrder(Mobile from, BaseCreature bc) + { + switch (_order) + { + case OrderType.Follow: + case OrderType.Attack: + case OrderType.Transfer: + case OrderType.Friend: + case OrderType.Unfriend: + { + HandleTargetOrder(from, bc); + break; + } + case OrderType.Release: + { + HandleReleaseOrder(from, bc); + break; + } + default: + { + HandleDefaultOrder(from, bc); + break; + } + } + } + + private void HandleTargetOrder(Mobile from, BaseCreature bc) + { + if (_order is OrderType.Transfer or OrderType.Friend && from.HasTrade) + { + from.SendLocalizedMessage(_order == OrderType.Transfer ? 1010507 : 1070947); + // 1010507: You cannot transfer a pet with a trade pending + // 1070947: You cannot friend a pet with a trade pending + return; + } + + bc.AIObject.BeginPickTarget(from, _order); + } + + private void HandleReleaseOrder(Mobile from, BaseCreature bc) + { + if (bc.Summoned) + { + HandleDefaultOrder(from, bc); + return; + } + + from.SendGump(new ConfirmReleaseGump(from, bc)); + } + + private void HandleDefaultOrder(Mobile from, BaseCreature bc) + { + if (bc.CheckControlChance(from)) + { + bc.ControlTarget = null; + bc.ControlOrder = _order; + } + } +} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/OnSpeech.cs b/Projects/UOContent/Mobiles/AI/BaseAI/OnSpeech.cs new file mode 100644 index 000000000..32c0356b1 --- /dev/null +++ b/Projects/UOContent/Mobiles/AI/BaseAI/OnSpeech.cs @@ -0,0 +1,457 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2025 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: OnSpeech.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ************************************************************************/ + +using System; +using Server.Items; +using Server.Gumps; + +namespace Server.Mobiles; + +public abstract partial class BaseAI +{ + public virtual bool HandlesOnSpeech(Mobile from) + { + if (from.AccessLevel >= AccessLevel.GameMaster) + { + return true; + } + + if (from.Alive && Mobile.Controlled && Mobile.Commandable && + (from == Mobile.ControlMaster || Mobile.IsPetFriend(from))) + { + return true; + } + + return from.Alive && from.InRange(Mobile.Location, 3) && Mobile.IsHumanInTown(); + } + + public virtual void OnSpeech(SpeechEventArgs e) + { + if (WasNamed(e.Speech) && e.Mobile.Alive && + e.Mobile.InRange(Mobile.Location, 3) && Mobile.IsHumanInTown()) + { + if (HandleMoveCommand(e) || HandleTimeCommand(e) || HandleTrainCommand(e)) + { + return; + } + } + + if (Mobile.Controlled && Mobile.Commandable) + { + AllOnSpeechPet(e); + NamedOnSpeechPet(e); + return; + } + + if (e.Mobile.AccessLevel >= AccessLevel.GameMaster) + { + HandleGMCommands(e); + } + } + + private bool HandleMoveCommand(SpeechEventArgs e) + { + if (!e.HasKeyword(0x9D)) // *move* + { + return false; + } + + if ((Core.Now - _lastOrder).TotalSeconds < 5) + { + return true; + } + + _lastOrder = Core.Now; + + var map = Mobile.Map; + var currentLoc = Mobile.Location; + + var newX = currentLoc.X + Utility.RandomMinMax(-1, 1); + var newY = currentLoc.Y + Utility.RandomMinMax(-1, 1); + var newZ = currentLoc.Z; + + if (map != null && map.CanFit(newX, newY, newZ, 16, false, false)) + { + Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501516); // Excuse me? + Mobile.Location = new Point3D(newX, newY, newZ); + } + else + { + Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501487); + // You're standing too close, go away. + } + + return true; + } + + private bool HandleTimeCommand(SpeechEventArgs e) + { + if (!e.HasKeyword(0x9E)) // *time* + { + return false; + } + + if ((Core.Now - _lastOrder).TotalSeconds < 5) + { + return true; + } + + _lastOrder = Core.Now; + + Clock.GetTime(Mobile, out var generalNumber, out _); + Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, generalNumber); + + return true; + } + + private bool HandleTrainCommand(SpeechEventArgs e) + { + if (!e.HasKeyword(0x6C)) // *train* + { + return false; + } + + HandleTraining(e.Mobile); + return true; + } + + public virtual void AllOnSpeechPet(SpeechEventArgs e) + { + if (!e.Mobile.InRange(Mobile.Location, 14)) + { + return; + } + + var isOwner = e.Mobile == Mobile.ControlMaster; + var isPetFriend = !isOwner && Mobile.IsPetFriend(e.Mobile); + + if (!isOwner && !isPetFriend) + { + return; + } + + var keyword = e.GetFirstKeyword( + 0x164, // all come + 0x165, // all follow + 0x166, // all guard + 0x167, // all stop + 0x168, // all kill + 0x169, // all attack + 0x16B, // all guard me + 0x16C, // all follow me + 0x170 // all stay + ); + + switch (keyword) + { + case 0x164: // all come + { + HandleComeCommand(e.Mobile, true); + break; + } + case 0x165: // all follow + { + BeginPickTarget(e.Mobile, OrderType.Follow); + break; + } + case 0x166: // all guard + case 0x16B: // all guard me + { + HandleGuardCommand(e.Mobile, true); + break; + } + case 0x167: // all stop + { + HandleStayStopFollowCommand(e.Mobile, OrderType.Stop); + break; + } + case 0x168: // all kill + case 0x169: // all attack + { + HandleAttackCommand(e.Mobile, true); + break; + } + case 0x16C: // all follow me + { + HandleStayStopFollowCommand(e.Mobile, OrderType.Follow, e.Mobile); + break; + } + case 0x170: // all stay + { + HandleStayStopFollowCommand(e.Mobile, OrderType.Stay); + break; + } + } + } + + public virtual void NamedOnSpeechPet(SpeechEventArgs e) + { + if (!e.Mobile.InRange(Mobile.Location, 14)) + { + return; + } + + var isOwner = e.Mobile == Mobile.ControlMaster; + var isPetFriend = !isOwner && Mobile.IsPetFriend(e.Mobile); + + if (!isOwner && !isPetFriend) + { + return; + } + + var keyword = e.GetFirstKeyword( + 0x155, // *come + 0x156, // *drop + 0x15A, // *follow + 0x15B, // *friend + 0x15C, // *guard + 0x15D, // *kill + 0x15E, // *attack + 0x161, // *stop + 0x163, // *follow me + 0x16D, // *release + 0x16E, // *transfer + 0x16F // *stay + ); + + switch (keyword) + { + case 0x155: // *come + { + HandleComeCommand(e.Mobile, true); + break; + } + case 0x156: // *drop + { + HandleDropCommand(e.Mobile, true, e.Speech); + break; + } + case 0x15A: // *follow + { + BeginPickTarget(e.Mobile, OrderType.Follow); + break; + } + case 0x15B: // *friend + { + HandleFriendCommand(e.Mobile, true, e.Speech); + break; + } + case 0x15C: // *guard + { + HandleGuardCommand(e.Mobile, true); + break; + } + case 0x15D: // *kill + case 0x15E: // *attack + { + HandleAttackCommand(e.Mobile, true); + break; + } + case 0x161: // *stop + { + HandleStayStopFollowCommand(e.Mobile, OrderType.Stop); + break; + } + case 0x163: // *follow me + { + HandleStayStopFollowCommand(e.Mobile, OrderType.Follow, e.Mobile); + break; + } + case 0x16D: // *release + { + HandleReleaseCommand(e.Mobile, true, e.Speech); + break; + } + case 0x16E: // *transfer + { + HandleTransferCommand(e.Mobile, true, e.Speech); + break; + } + case 0x16F: // *stay + { + HandleStayStopFollowCommand(e.Mobile, OrderType.Stay); + break; + } + } + } + + private void HandleTraining(Mobile from) + { + var foundSomething = false; + + foreach (var skill in Mobile.Skills) + { + if (skill.Base < 60.0 || !Mobile.CheckTeach(skill.SkillName, from)) + { + continue; + } + + var toTeach = Math.Min(skill.Base / 3.0, 42.0); + + if (toTeach <= from.Skills[skill.SkillName].Base) + { + continue; + } + + var number = 1043059 + (int)skill.SkillName; // alchemy + if (number > 1043107) // disarming traps + { + continue; + } + + if (!foundSomething) + { + Mobile.Say(1043058); // I can train the following: + foundSomething = true; + } + + Mobile.Say(number); + } + + if (!foundSomething) + { + Mobile.Say(501505); // Alas, I cannot teach thee anything. + } + } + + private void HandleComeCommand(Mobile from, bool isOwner) + { + if (isOwner && Mobile.CheckControlChance(from)) + { + _commandIssuer = from; + Mobile.ControlTarget = null; + Mobile.ControlOrder = OrderType.Come; + } + } + + private void HandleGuardCommand(Mobile from, bool isOwner) + { + if (isOwner && Mobile.CheckControlChance(from)) + { + _commandIssuer = from; + Mobile.ControlTarget = null; + Mobile.ControlOrder = OrderType.Guard; + } + } + + private void HandleStayStopFollowCommand(Mobile from, OrderType order, Mobile target = null) + { + if (Mobile.CheckControlChance(from)) + { + _commandIssuer = from; + Mobile.ControlTarget = target; + Mobile.ControlOrder = order; + } + } + + private void HandleAttackCommand(Mobile from, bool isOwner) + { + if (isOwner) + { + _commandIssuer = from; + BeginPickTarget(from, OrderType.Attack); + } + } + + private void HandleDropCommand(Mobile from, bool isOwner, string speech) + { + if (isOwner && !Mobile.IsDeadPet && !Mobile.Summoned && WasNamed(speech) + && Mobile.CheckControlChance(from)) + { + _commandIssuer = from; + Mobile.ControlTarget = null; + Mobile.ControlOrder = OrderType.Drop; + } + } + + private void HandleFriendCommand(Mobile from, bool isOwner, string speech) + { + if (isOwner && WasNamed(speech) && Mobile.CheckControlChance(from)) + { + if (Mobile.Summoned || Mobile is GrizzledMare) + { + from.SendLocalizedMessage(1005481); + // Summoned creatures are loyal only to their summoners. + return; + } + + if (from.HasTrade) + { + from.SendLocalizedMessage(1070947); + // You cannot friend a pet with a trade pending + return; + } + + BeginPickTarget(from, OrderType.Friend); + } + } + + private void HandleReleaseCommand(Mobile from, bool isOwner, string speech) + { + if (!isOwner) + { + return; + } + + if (WasNamed(speech) && Mobile.CheckControlChance(from)) + { + if (!Mobile.Summoned) + { + from.SendGump(new ConfirmReleaseGump(from, Mobile)); + } + else + { + Mobile.ControlOrder = OrderType.Release; + } + } + } + + private void HandleTransferCommand(Mobile from, bool isOwner, string speech) + { + if (isOwner && !Mobile.IsDeadPet && WasNamed(speech) && Mobile.CheckControlChance(from)) + { + if (Mobile.Summoned || Mobile is GrizzledMare) + { + from.SendLocalizedMessage(1005487); + // You cannot transfer ownership of a summoned creature. + return; + } + + if (from.HasTrade) + { + from.SendLocalizedMessage(1010507); + // You cannot transfer a pet with a trade pending + return; + } + + BeginPickTarget(from, OrderType.Transfer); + } + } + + private void HandleGMCommands(SpeechEventArgs e) + { + this.DebugSayFormatted($"Command is from GM: {e.Mobile.Name}, Target: {Mobile.ControlTarget?.Name ?? "None or Unknown"}"); + + if (Mobile.FindMyName(e.Speech, true) && e.Speech.InsensitiveContains("obey")) + { + Mobile.SetControlMaster(e.Mobile); + + if (Mobile.Summoned) + { + Mobile.SummonMaster = e.Mobile; + } + } + } +} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/PetOrderHandlers.cs b/Projects/UOContent/Mobiles/AI/BaseAI/PetOrderHandlers.cs new file mode 100644 index 000000000..08debb256 --- /dev/null +++ b/Projects/UOContent/Mobiles/AI/BaseAI/PetOrderHandlers.cs @@ -0,0 +1,241 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2025 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: PetOrderHandlers.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ************************************************************************/ + +using System; + +namespace Server.Mobiles; + +public abstract partial class BaseAI +{ + public virtual void OnCurrentOrderChanged() + { + if (Mobile.Deleted || Mobile.ControlMaster?.Deleted != false) + { + return; + } + + switch (Mobile.ControlOrder) + { + case OrderType.None: + { + HandleNoOrder(); + break; + } + case OrderType.Come: + case OrderType.Drop: + case OrderType.Friend: + case OrderType.Unfriend: + { + break; + } + case OrderType.Release: + { + HandleReleaseOrder(); + break; + } + case OrderType.Stop: + { + HandleStopOrder(); + break; + } + case OrderType.Transfer: + { + HandleTransferOrder(); + break; + } + case OrderType.Stay: + { + HandleStayOrder(); + break; + } + case OrderType.Guard: + { + HandleGuardOrder(); + break; + } + case OrderType.Attack: + { + HandleAttackOrder(); + break; + } + case OrderType.Follow: + { + HandleFollowOrder(); + break; + } + case OrderType.Rename: + { + HandleRenameOrder(); + break; + } + } + } + + private void HandleNoOrder() + { + Mobile.ControlTarget = null; + Mobile.FocusMob = null; + Mobile.Warmode = false; + Mobile.Combatant = null; + } + + private void HandleTransferOrder() + { + if (Mobile.ControlMaster?.Alive != true) + { + return; + } + + _commandIssuer?.RevealingAction(); + Mobile.FocusMob = null; + Mobile.Warmode = false; + Mobile.Combatant = null; + Mobile.PlaySound(Mobile.GetIdleSound()); + _commandIssuer = null; + } + + private void HandleGuardOrder() + { + if (Mobile.ControlMaster?.Alive != true) + { + return; + } + + _commandIssuer?.RevealingAction(); + Mobile.FocusMob = null; + Mobile.Warmode = true; + Mobile.PlaySound(Mobile.GetAttackSound()); + Mobile.ControlMaster?.SendLocalizedMessage(1049671, Mobile.Name); + // ~1_NAME~ is now guarding you. + _commandIssuer = null; + } + + private void HandleAttackOrder() + { + if (Mobile.ControlMaster?.Alive != true) + { + return; + } + + _commandIssuer?.RevealingAction(); + + if (Mobile.ControlTarget != null && + !Mobile.ControlTarget.Deleted && + Mobile.ControlTarget.Alive) + { + Mobile.FocusMob = Mobile.ControlTarget; + Mobile.Combatant = Mobile.ControlTarget; + } + else + { + Mobile.FocusMob = null; + Mobile.Combatant = null; + } + + Mobile.Warmode = true; + Mobile.PlaySound(Mobile.GetAttackSound()); + _commandIssuer = null; + } + + private void HandleFollowOrder() + { + if (Mobile.ControlMaster?.Alive != true) + { + return; + } + + _commandIssuer?.RevealingAction(); + Mobile.FocusMob = null; + Mobile.Warmode = false; + Mobile.Combatant = null; + Mobile.PlaySound(Mobile.GetIdleSound()); + _commandIssuer = null; + } + + private void HandleStayOrder() + { + if (Mobile.ControlMaster?.Alive != true) + { + return; + } + + _commandIssuer?.RevealingAction(); + Mobile.FocusMob = null; + Mobile.Warmode = false; + Mobile.Combatant = null; + Mobile.PlaySound(Mobile.GetIdleSound()); + Mobile.Home = Mobile.Location; + _commandIssuer = null; + } + + private void HandleStopOrder() + { + if (Mobile.ControlMaster?.Alive != true) + { + return; + } + + _commandIssuer?.RevealingAction(); + Mobile.ControlTarget = null; + Mobile.FocusMob = null; + Mobile.Warmode = false; + Mobile.Combatant = null; + Mobile.PlaySound(Mobile.GetIdleSound()); + _commandIssuer = null; + } + + private void HandleReleaseOrder() + { + if (Mobile.ControlMaster?.Alive != true) + { + return; + } + + if (Mobile.Summoned) + { + Mobile.Kill(); + return; + } + + if (!string.IsNullOrEmpty(Mobile.Name)) + { + Mobile.Name = null; + } + + _commandIssuer?.RevealingAction(); + Mobile.ControlTarget = null; + Mobile.FocusMob = null; + Mobile.Warmode = false; + Mobile.Combatant = null; + Mobile.PlaySound(Mobile.GetIdleSound()); + Mobile.BondingBegin = DateTime.MinValue; + Mobile.OwnerAbandonTime = DateTime.MinValue; + Mobile.IsBonded = false; + Mobile.SetControlMaster(null); + _commandIssuer = null; + } + + public virtual void HandleRenameOrder() + { + if (Mobile.Summoned) + { + Mobile.ControlMaster?.SendMessage("You cannot rename a summoned creature."); + } + else + { + Mobile.ControlMaster?.SendMessage("Change name on pet health bar."); + } + } +} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/PetOrders.cs b/Projects/UOContent/Mobiles/AI/BaseAI/PetOrders.cs new file mode 100644 index 000000000..f5919ee6b --- /dev/null +++ b/Projects/UOContent/Mobiles/AI/BaseAI/PetOrders.cs @@ -0,0 +1,518 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2025 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: PetOrders.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ************************************************************************/ + +namespace Server.Mobiles; + +public abstract partial class BaseAI +{ + public virtual bool Obey() => + !Mobile.Deleted && Mobile.ControlOrder switch + { + OrderType.None => DoOrderNone(), + OrderType.Come => DoOrderCome(), + OrderType.Drop => DoOrderDrop(), + OrderType.Friend => DoOrderFriend(), + OrderType.Unfriend => DoOrderUnfriend(), + OrderType.Guard => DoOrderGuard(), + OrderType.Attack => DoOrderAttack(), + OrderType.Release => DoOrderRelease(), + OrderType.Stay => DoOrderStay(), + OrderType.Stop => DoOrderStop(), + OrderType.Follow => DoOrderFollow(), + OrderType.Transfer => DoOrderTransfer(), + _ => false + }; + + public virtual bool DoOrderNone() + { + DebugSay("I currently have no orders."); + + Mobile.Warmode = IsValidCombatant(Mobile.Combatant); + + WalkRandom(3, 2, 1); + return true; + } + + public virtual bool DoOrderCome() + { + if (CheckHerding()) + { + this.DebugSayFormatted($"I am being herded by {Mobile.ControlTarget?.Name ?? "Unknown"}."); + return true; + } + + if (Mobile.ControlMaster?.Deleted != false) + { + return true; + } + + WalkMobileRange(Mobile.ControlMaster, 1, false, 1, 2); + + if (Mobile.GetDistanceToSqrt(Mobile.ControlMaster) <= 2) + { + Mobile.ControlOrder = OrderType.Stay; + } + + return true; + } + + public virtual bool DoOrderFollow() + { + if (CheckHerding()) + { + this.DebugSayFormatted($"I am being herded by {Mobile.ControlTarget?.Name ?? "Unknown"}."); + return true; + } + + if (Mobile.ControlTarget?.Deleted == false && Mobile.ControlTarget != Mobile) + { + FollowTarget(); + } + else + { + DebugSay("I have no one to follow."); + + Mobile.ControlOrder = OrderType.None; + } + + return true; + } + + private void FollowTarget() + { + var currentDistance = (int)Mobile.GetDistanceToSqrt(Mobile.ControlTarget); + + if (currentDistance > Mobile.RangePerception) + { + this.DebugSayFormatted($"Master {Mobile.ControlMaster?.Name ?? "Unknown"} is missing. Staying put."); + return; + } + + this.DebugSayFormatted($"I am ordered to follow {Mobile.ControlTarget?.Name}."); + + if (currentDistance > 1) + { + WalkMobileRange(Mobile.ControlTarget, 1, currentDistance > 2, 1, 2); + } + } + + public virtual bool DoOrderDrop() + { + if (Mobile.IsDeadPet || !Mobile.CanDrop) + { + return true; + } + + this.DebugSayFormatted($"I am ordered to drop my items by {Mobile.ControlMaster?.Name ?? "Unknown"}."); + + Mobile.ControlOrder = OrderType.None; + + DropItems(); + return true; + } + + private void DropItems() + { + var pack = Mobile.Backpack; + + if (pack == null) + { + return; + } + + var items = pack.Items; + + for (var i = items.Count - 1; i >= 0; --i) + { + if (i < items.Count) + { + items[i].MoveToWorld(Mobile.Location, Mobile.Map); + } + } + } + + public virtual bool DoOrderFriend() + { + var from = Mobile.ControlMaster; + var to = Mobile.ControlTarget; + + HandleFriendRequest(from, to); + return true; + } + + private void HandleFriendRequest(Mobile from, Mobile to) + { + var youngFrom = from is PlayerMobile mobile && mobile.Young; + var youngTo = to is PlayerMobile playerMobile && playerMobile.Young; + + if (youngFrom && !youngTo) + { + from.SendLocalizedMessage(502040); + // As a young player, you may not friend pets to older players. + return; + } + + if (!youngFrom && youngTo) + { + from.SendLocalizedMessage(502041); + // As an older player, you may not friend pets to young players. + return; + } + + if (!from.CanBeBeneficial(to, true)) + { + return; + } + + if (to?.Deleted != false || from == to || !to.Player) + { + Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 502039); + // *looks confused* + return; + } + + if (from.HasTrade || to.HasTrade) + { + (from.HasTrade ? from : to).SendLocalizedMessage(1070947); + // You cannot friend a pet with a trade pending + return; + } + + if (Mobile.IsPetFriend(to)) + { + from.SendLocalizedMessage(1049691); + // That person is already a friend. + Mobile.ControlOrder = OrderType.None; + return; + } + + if (!Mobile.AllowNewPetFriend) + { + from.SendLocalizedMessage(1005482); + // Your pet does not seem to be interested in making new friends right now. + return; + } + + from.SendLocalizedMessage(1049676, $"{Mobile.Name}\t{to.Name}"); + // ~1_NAME~ will now accept movement commands from ~2_NAME~. + + to.SendLocalizedMessage(1043246, $"{from.Name}\t{Mobile.Name}"); + // ~1_NAME~ has granted you the ability to give orders to their pet ~2_PET_NAME~. + // This creature will now consider you as a friend. + + Mobile.AddPetFriend(to); + + Mobile.ControlTarget = to; + Mobile.ControlOrder = OrderType.Follow; + } + + public virtual bool DoOrderUnfriend() + { + var from = Mobile.ControlMaster; + var to = Mobile.ControlTarget; + + HandleUnfriendRequest(from, to); + return true; + } + + private void HandleUnfriendRequest(Mobile from, Mobile to) + { + if (from?.Deleted != false || to?.Deleted != false || from == to || !to.Player) + { + Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 502039); + // *looks confused* + return; + } + + if (!Mobile.IsPetFriend(to)) + { + from.SendLocalizedMessage(1070953); + // That person is not a friend. + Mobile.ControlOrder = OrderType.None; + return; + } + + from.SendLocalizedMessage(1070951, $"{Mobile.Name}\t{to.Name}"); + // ~1_NAME~ will no longer accept movement commands from ~2_NAME~. + + to.SendLocalizedMessage(1070952, $"{from.Name}\t{Mobile.Name}"); + // ~1_NAME~ has no longer granted you the ability to give orders to their pet ~2_PET_NAME~. + // This creature will no longer consider you as a friend. + + Mobile.RemovePetFriend(to); + + Mobile.ControlTarget = from; + Mobile.ControlOrder = OrderType.Follow; + } + + public virtual bool DoOrderGuard() + { + var controlMaster = Mobile.ControlMaster; + + if (Mobile.IsDeadPet || controlMaster?.Deleted != false) + { + return true; + } + + FindCombatant(); + + if (IsValidCombatant(Mobile.Combatant)) + { + var combatant = Mobile.Combatant; + + this.DebugSayFormatted($"Attacking target: {combatant.Name}"); + + Mobile.Combatant = combatant; + Mobile.FocusMob = combatant; + Action = ActionType.Combat; + + Think(); + } + else + { + this.DebugSayFormatted($"Guarding my master, {controlMaster.Name}."); + + var guardLocation = controlMaster.Location; + + var distance = (int)Mobile.GetDistanceToSqrt(guardLocation); + + if (distance > 3) + { + DoMove(Mobile.GetDirectionTo(guardLocation)); + } + else + { + WalkRandom(3, 1, 1); + } + } + + return true; + } + + public virtual bool DoOrderAttack() + { + if (Mobile.IsDeadPet) + { + return false; + } + + if (IsInvalidControlTarget(Mobile.ControlTarget)) + { + HandleInvalidControlTarget(); + } + else + { + Mobile.Combatant = Mobile.ControlTarget; + + this.DebugSayFormatted($"Attacking target: {Mobile.ControlTarget?.Name}"); + + Think(); + } + + return true; + } + + private bool IsInvalidControlTarget(Mobile target) => target?.Deleted != false || target.Map != Mobile.Map || !target.Alive || target.IsDeadBondedPet; + + private void HandleInvalidControlTarget() + { + DebugSay("Target is either dead, hidden, or out of range."); + + Mobile.ControlOrder = Core.AOS || Mobile.IsBonded ? OrderType.Follow : OrderType.None; + + if (Mobile.FightMode is FightMode.Closest or FightMode.Aggressor) + { + FindCombatant(); + } + } + + private void FindCombatant() + { + foreach (var aggr in Mobile.GetMobilesInRange(Mobile.RangePerception)) + { + if (!Mobile.CanSee(aggr) || aggr.Combatant != Mobile || aggr.IsDeadBondedPet || !aggr.Alive) + { + continue; + } + + if (Mobile.InLOS(aggr)) + { + Mobile.ControlTarget = aggr; + Mobile.ControlOrder = OrderType.Attack; + Mobile.Combatant = aggr; + + this.DebugSayFormatted($"{aggr.Name} is still alive. Resuming attacks..."); + + Think(); + break; + } + } + } + + public virtual bool DoOrderRelease() + { + DebugSay("I have been released to the wild."); + + var spawner = Mobile.Spawner; + + if (spawner != null && spawner.HomeLocation != Point3D.Zero) + { + Mobile.Home = spawner.HomeLocation; + Mobile.RangeHome = spawner.HomeRange; + } + else + { + Action = ActionType.Wander; + } + + if (Mobile.DeleteOnRelease || Mobile.IsDeadPet) + { + Mobile.Delete(); + } + else + { + Mobile.BeginDeleteTimer(); + + if (Mobile.CanDrop) + { + Mobile.DropBackpack(); + } + } + + return true; + } + + public virtual bool DoOrderStay() + { + if (CheckHerding()) + { + this.DebugSayFormatted($"I am being herded by {Mobile.ControlTarget?.Name ?? "Unknown"}."); + } + else + { + this.DebugSayFormatted($"I have been ordered to stay by {Mobile.ControlMaster?.Name ?? "Unknown"}."); + } + + WalkRandomInHome(3, 2, 1); + return true; + } + + public virtual bool DoOrderStop() + { + if (CheckHerding()) + { + this.DebugSayFormatted($"I am being herded by {Mobile.ControlTarget?.Name ?? "Unknown"}."); + } + else + { + this.DebugSayFormatted($"I have been ordered to stop by {Mobile.ControlMaster?.Name ?? "Unknown"}."); + } + + if (Core.ML) + { + WalkRandomInHome(5, 2, 1); + } + + return true; + } + + public virtual bool DoOrderTransfer() + { + if (Mobile.IsDeadPet) + { + return true; + } + + var from = Mobile.ControlMaster; + var to = Mobile.ControlTarget; + + if (from?.Deleted == false && to?.Deleted == false && from != to && to.Player) + { + this.DebugSayFormatted($"Beginning transfer with {to.Name}"); + + var youngFrom = from is PlayerMobile mobile && mobile.Young; + var youngTo = to is PlayerMobile playerMobile && playerMobile.Young; + + if (youngFrom && !youngTo) + { + from.SendLocalizedMessage(502040); + // As a young player, you may not friend pets to older players. + return true; + } + + if (!youngFrom && youngTo) + { + from.SendLocalizedMessage(502041); + // As an older player, you may not friend pets to young players. + return true; + } + + if (!Mobile.CanBeControlledBy(to)) + { + SendTransferRefusalMessages(from, to, 1043248, 1043249); + // 1043248: The pet refuses to be transferred because it will not obey ~1_NAME~.~3_BLANK~ + // 1043249: The pet will not accept you as a master because it does not trust you.~3_BLANK~ + return false; + } + + if (!Mobile.CanBeControlledBy(from)) + { + SendTransferRefusalMessages(from, to, 1043250, 1043251); + // 1043250: The pet refuses to be transferred because it will not obey you sufficiently.~3_BLANK~ + // 1043251: The pet will not accept you as a master because it does not trust ~2_NAME~.~3_BLANK~ + return false; + } + + if (Mobile.Combatant != null || Mobile.Aggressors.Count > 0 || + Mobile.Aggressed.Count > 0 || Core.TickCount < Mobile.NextCombatTime) + { + from.SendMessage("You can not transfer a pet while in combat."); + to.SendMessage("You can not transfer a pet while in combat."); + return false; + } + + var fromState = from.NetState; + var toState = to.NetState; + + if (fromState == null || toState == null) + { + return false; + } + + if (from.HasTrade || to.HasTrade) + { + from.SendLocalizedMessage(1010507); + // You cannot transfer a pet with a trade pending + to.SendLocalizedMessage(1010507); + // You cannot transfer a pet with a trade pending + return false; + } + + var container = fromState.AddTrade(toState); + container.DropItem(new TransferItem(Mobile)); + } + + Mobile.ControlOrder = OrderType.Stay; + return true; + } + + private static void SendTransferRefusalMessages(Mobile from, Mobile to, int fromMessage, int toMessage) + { + var args = $"{to.Name}\t{from.Name}\t "; + + from.SendLocalizedMessage(fromMessage, args); + to.SendLocalizedMessage(toMessage, args); + } +} diff --git a/Projects/Server/Random/BuiltInSecureRng.cs b/Projects/UOContent/Mobiles/AI/BaseAI/SpeechEventArgsExt.cs similarity index 59% rename from Projects/Server/Random/BuiltInSecureRng.cs rename to Projects/UOContent/Mobiles/AI/BaseAI/SpeechEventArgsExt.cs index dec408338..5090f235a 100644 --- a/Projects/Server/Random/BuiltInSecureRng.cs +++ b/Projects/UOContent/Mobiles/AI/BaseAI/SpeechEventArgsExt.cs @@ -1,8 +1,8 @@ /************************************************************************* * ModernUO * - * Copyright 2019-2023 - ModernUO Development Team * + * Copyright 2019-2025 - ModernUO Development Team * * Email: hi@modernuo.com * - * File: SecureRandom.cs * + * File: SpeechEventArgsExt.cs * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * @@ -10,19 +10,26 @@ * (at your option) any later version. * * * * You should have received a copy of the GNU General Public License * - * along with this program. If not, see . * - *************************************************************************/ + * along with this program. If not, see . * + ************************************************************************/ using System; -using System.Runtime.CompilerServices; -using System.Security.Cryptography; namespace Server; -public static class BuiltInSecureRng +public static class SpeechEventArgsExt { - public static RandomNumberGenerator Generator { get; } = RandomNumberGenerator.Create(); + public static int GetFirstKeyword(this SpeechEventArgs e, params ReadOnlySpan keywords) + { + for (var i = 0; i < keywords.Length; i++) + { + var keyword = keywords[i]; + if (e.HasKeyword(keyword)) + { + return keyword; + } + } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void NextBytes(Span buffer) => Generator.GetBytes(buffer); + return 0; + } } diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/TransferItem.cs b/Projects/UOContent/Mobiles/AI/BaseAI/TransferItem.cs new file mode 100644 index 000000000..1a8f0bd74 --- /dev/null +++ b/Projects/UOContent/Mobiles/AI/BaseAI/TransferItem.cs @@ -0,0 +1,190 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2025 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: TransferItem.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ************************************************************************/ + +using System; +using System.Runtime.CompilerServices; + +namespace Server.Mobiles; + +internal sealed class TransferItem : Item +{ + private readonly BaseCreature _creature; + + public override string DefaultName => _creature.GetType().Name; + + public TransferItem(BaseCreature creature) : base(ShrinkTable.Lookup(creature)) + { + _creature = creature; + Movable = false; + + Hue = creature.Hue & 0x0FFF; + } + + public TransferItem(Serial serial) : base(serial) + { + } + + public static bool IsInCombat(BaseCreature creature) => creature?.Aggressors.Count > 0 || creature?.Aggressed.Count > 0; + + public override void Serialize(IGenericWriter writer) + { + base.Serialize(writer); + writer.Write(0); // version + } + + public override void Deserialize(IGenericReader reader) + { + base.Deserialize(reader); + reader.ReadInt(); // version + Delete(); + } + + public override void GetProperties(IPropertyList list) + { + base.GetProperties(list); + list.Add(1041603); // This item represents a pet currently in consideration for trade + list.Add(1041601, _creature.Name); // Pet Name: ~1_val~ + + if (_creature.ControlMaster != null) + { + list.Add(1041602, _creature.ControlMaster.Name); // Owner: ~1_val~ + } + } + + public override bool AllowSecureTrade(Mobile from, Mobile to, Mobile newOwner, bool accepted) + { + if (!base.AllowSecureTrade(from, to, newOwner, accepted) || IsInvalidTrade(from, to)) + { + return false; + } + + return !accepted || HandleAcceptedTrade(from, to); + } + + private bool IsInvalidTrade(Mobile from, Mobile to) => + Deleted + || _creature?.Deleted != false + || _creature.ControlMaster != from + || !from.CheckAlive() + || !to.CheckAlive() + || from.Map != _creature.Map + || !from.InRange(_creature, 14); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool HandleAcceptedTrade(Mobile from, Mobile to) => + ValidateYoungStatus(from, to) && ValidateControlStatus(from, to) && ValidateFollowerLimit(to) && + !IsInCombat(_creature); + + private bool ValidateFollowerLimit(Mobile to) + { + if (to.Followers + _creature.ControlSlots > to.FollowersMax) + { + to.SendLocalizedMessage(1049607); + // You have too many followers to control that creature. + return false; + } + + return true; + } + + private static bool ValidateYoungStatus(Mobile from, Mobile to) + { + var youngFrom = from is PlayerMobile mobile && mobile.Young; + var youngTo = to is PlayerMobile playerMobile && playerMobile.Young; + + if (youngFrom && !youngTo) + { + from.SendLocalizedMessage(502051); + // As a young player, you may not transfer pets to older players. + return false; + } + + if (!youngFrom && youngTo) + { + from.SendLocalizedMessage(502052); + // As an older player, you may not transfer pets to young players. + return false; + } + + return true; + } + + private bool ValidateControlStatus(Mobile from, Mobile to) + { + if (!_creature.CanBeControlledBy(to)) + { + SendTransferRefusalMessages(from, to, 1043248, 1043249); + // The pet refuses to be transferred because it will not obey ~1_NAME~.~3_BLANK~ + // The pet will not accept you as a master because it does not trust you.~3_BLANK~ + return false; + } + + if (!_creature.CanBeControlledBy(from)) + { + SendTransferRefusalMessages(from, to, 1043250, 1043251); + // The pet refuses to be transferred because it will not obey you sufficiently.~3_BLANK~ + // The pet will not accept you as a master because it does not trust ~2_NAME~.~3_BLANK~ + return false; + } + + return true; + } + + private static void SendTransferRefusalMessages(Mobile from, Mobile to, int fromMessage, int toMessage) + { + var args = $"{to.Name}\t{from.Name}\t "; + from.SendLocalizedMessage(fromMessage, args); + to.SendLocalizedMessage(toMessage, args); + } + + public override void OnSecureTrade(Mobile from, Mobile to, Mobile newOwner, bool accepted) + { + if (Deleted || IsInvalidTrade(from, to)) + { + Delete(); + return; + } + + Delete(); + + if (!accepted || !_creature.SetControlMaster(to)) + { + return; + } + + TransferPetOwnership(from, to); + } + + private void TransferPetOwnership(Mobile from, Mobile to) + { + if (_creature.Summoned) + { + _creature.SummonMaster = to; + } + + _creature.ControlTarget = to; + _creature.ControlOrder = OrderType.Follow; + _creature.BondingBegin = DateTime.MinValue; + _creature.OwnerAbandonTime = DateTime.MinValue; + _creature.IsBonded = false; + _creature.PlaySound(_creature.GetIdleSound()); + + var args = $"{from.Name}\t{_creature.Name}\t{to.Name}"; + from.SendLocalizedMessage(1043253, args); + // You have transferred your pet to ~3_GETTER~. + to.SendLocalizedMessage(1043252, args); + // ~1_NAME~ has transferred the allegiance of ~2_PET_NAME~ to you. + } +} diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/WalkRandomLogic.cs b/Projects/UOContent/Mobiles/AI/BaseAI/WalkRandomLogic.cs new file mode 100644 index 000000000..e7d1af42f --- /dev/null +++ b/Projects/UOContent/Mobiles/AI/BaseAI/WalkRandomLogic.cs @@ -0,0 +1,125 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2025 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: WalkRandomLogic.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + ************************************************************************/ + +using System; +using Server.Engines.Spawners; + +namespace Server.Mobiles; + +public abstract partial class BaseAI +{ + public virtual void WalkRandom(int chanceToNotMove, int chanceToDir, int steps) + { + if (Mobile.Deleted || Mobile.DisallowAllMoves || chanceToNotMove <= 0) + { + return; + } + + var maxSteps = Math.Min(steps, 3); + + for (var i = 0; i < maxSteps; i++) + { + if (Utility.Random(1 + chanceToNotMove) == 0) + { + DoMove(GetRandomDirection(chanceToDir)); + } + } + } + + public virtual void WalkRandomInHome(int chanceToNotMove, int chanceToDir, int steps) + { + if (Mobile.Deleted || Mobile.DisallowAllMoves) + { + return; + } + + if (Mobile.Home == Point3D.Zero) + { + WalkRandomNoHome(chanceToNotMove, chanceToDir, steps); + } + else + { + WalkRandomWithHome(chanceToNotMove, chanceToDir, steps); + } + } + + private void WalkRandomNoHome(int chanceToNotMove, int chanceToDir, int steps) + { + if (Mobile.Spawner is RegionSpawner rs) + { + var region = rs.SpawnRegion; + + if (Mobile.Region.AcceptsSpawnsFrom(region)) + { + Mobile.WalkRegion = region; + + WalkRandom(chanceToNotMove, chanceToDir, steps); + + Mobile.WalkRegion = null; + } + else if (region.GoLocation != Point3D.Zero && Utility.RandomBool()) + { + DoMove(Mobile.GetDirectionTo(region.GoLocation)); + } + else + { + WalkRandom(chanceToNotMove, chanceToDir, 1); + } + } + else + { + WalkRandom(chanceToNotMove, chanceToDir, steps); + } + } + + private void WalkRandomWithHome(int chanceToNotMove, int chanceToDir, int steps) + { + if (Mobile.RangeHome == 0 && Mobile.Location != Mobile.Home) + { + DoMove(Mobile.GetDirectionTo(Mobile.Home)); + return; + } + + for (var i = 0; i < steps; i++) + { + var currDist = (int)Mobile.GetDistanceToSqrt(Mobile.Home); + + if (currDist > Mobile.RangeHome) + { + DoMove(Mobile.GetDirectionTo(Mobile.Home)); + } + else if (currDist < Mobile.RangeHome * 2 / 3 || Utility.Random(10) <= 5) + { + WalkRandom(chanceToNotMove, chanceToDir, 1); + } + else + { + DoMove(Mobile.GetDirectionTo(Mobile.Home)); + } + } + } + + private Direction GetRandomDirection(int chanceToDir) + { + var randomMove = Utility.Random(8 * (chanceToDir + 1)); + + if (randomMove < 8) + { + return (Direction)randomMove; + } + + return Mobile.Direction; + } +} diff --git a/Projects/UOContent/Mobiles/AI/BerserkAI.cs b/Projects/UOContent/Mobiles/AI/BerserkAI.cs index db7a994ba..4663ae8d0 100644 --- a/Projects/UOContent/Mobiles/AI/BerserkAI.cs +++ b/Projects/UOContent/Mobiles/AI/BerserkAI.cs @@ -8,19 +8,13 @@ public class BerserkAI : BaseAI public override bool DoActionWander() { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I have no combatant"); - } + DebugSay("I have no combatant"); - if (AcquireFocusMob(m_Mobile.RangePerception, FightMode.Closest, false, true, true)) + if (AcquireFocusMob(Mobile.RangePerception, FightMode.Closest, false, true, true)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name} and I will attack"); - } + this.DebugSayFormatted($"I have detected {Mobile.FocusMob.Name} and I will attack"); - m_Mobile.Combatant = m_Mobile.FocusMob; + Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; } else @@ -33,49 +27,37 @@ public class BerserkAI : BaseAI public override bool DoActionCombat() { - var combatant = m_Mobile.Combatant; + var combatant = Mobile.Combatant; - if (combatant == null || combatant.Deleted || combatant.Map != m_Mobile.Map || !combatant.Alive || + if (combatant == null || combatant.Deleted || combatant.Map != Mobile.Map || !combatant.Alive || combatant.IsDeadBondedPet) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("My combatant is gone, so my guard is up"); - } + DebugSay("My combatant is gone, so my guard is up"); Action = ActionType.Guard; return true; } - if (!WalkMobileRange(combatant, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight)) + if (!WalkMobileRange(combatant, 1, false, Mobile.RangeFight, Mobile.RangeFight)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I am still not in range of {combatant.Name}"); - } + this.DebugSayFormatted($"I am still not in range of {combatant.Name}"); - if ((int)m_Mobile.GetDistanceToSqrt(combatant) > m_Mobile.RangePerception + 1) + if ((int)Mobile.GetDistanceToSqrt(combatant) > Mobile.RangePerception + 1) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I have lost {combatant.Name}"); - } + this.DebugSayFormatted($"I have lost {combatant.Name}"); Action = ActionType.Guard; return true; } } - else if (Core.TickCount - m_Mobile.LastMoveTime > 400) + else if (Core.TickCount - Mobile.LastMoveTime > 400) { - m_Mobile.Direction = m_Mobile.GetDirectionTo(combatant); + Mobile.Direction = Mobile.GetDirectionTo(combatant); } - if (m_Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) + if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I used my abilities on {combatant.Name}!"); - } + this.DebugSayFormatted($"I used my abilities on {combatant.Name}!"); } return true; @@ -83,14 +65,11 @@ public class BerserkAI : BaseAI public override bool DoActionGuard() { - if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, true, true)) + if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, true, true)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name}, attacking"); - } + this.DebugSayFormatted($"I have detected {Mobile.FocusMob.Name}, attacking"); - m_Mobile.Combatant = m_Mobile.FocusMob; + Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; } else diff --git a/Projects/UOContent/Mobiles/AI/HealerAI.cs b/Projects/UOContent/Mobiles/AI/HealerAI.cs index 4afa17e3b..2a1127b43 100644 --- a/Projects/UOContent/Mobiles/AI/HealerAI.cs +++ b/Projects/UOContent/Mobiles/AI/HealerAI.cs @@ -9,13 +9,10 @@ namespace Server.Mobiles; public class HealerAI : BaseAI { - private static readonly NeedDelegate m_Cure = NeedCure; - private static readonly NeedDelegate m_GHeal = NeedGHeal; - private static readonly NeedDelegate m_LHeal = NeedLHeal; - private static readonly NeedDelegate[] m_ACure = { m_Cure }; - private static readonly NeedDelegate[] m_AGHeal = { m_GHeal }; - private static readonly NeedDelegate[] m_ALHeal = { m_LHeal }; - private static readonly NeedDelegate[] m_All = { m_Cure, m_GHeal, m_LHeal }; + private static readonly NeedDelegate[] Cure = [NeedCure]; + private static readonly NeedDelegate[] GHeal = [NeedGHeal]; + private static readonly NeedDelegate[] LHeal = [NeedLHeal]; + private static readonly NeedDelegate[] AllHeal = [NeedCure, NeedGHeal, NeedLHeal]; public HealerAI(BaseCreature m) : base(m) { @@ -23,93 +20,73 @@ public class HealerAI : BaseAI public override bool Think() { - if (m_Mobile.Deleted) + if (Mobile.Deleted) { return false; } - var targ = m_Mobile.Target; + var targ = Mobile.Target; if (targ != null) { var spellTarg = targ as ISpellTarget; - if (spellTarg?.Spell is CureSpell) + var funcs = spellTarg?.Spell switch { - ProcessTarget(targ, m_ACure); - } - else if (spellTarg?.Spell is GreaterHealSpell) - { - ProcessTarget(targ, m_AGHeal); - } - else if (spellTarg?.Spell is HealSpell) - { - ProcessTarget(targ, m_ALHeal); - } - else - { - targ.Cancel(m_Mobile, TargetCancelType.Canceled); - } + CureSpell => Cure, + GreaterHealSpell => GHeal, + HealSpell => LHeal, + _ => null + }; + ProcessTarget(targ, funcs); return true; } - var toHelp = Find(m_All); + var toHelp = Find(AllHeal); if (toHelp != null) { if (NeedCure(toHelp)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"{toHelp.Name} needs a cure"); - } + this.DebugSayFormatted($"{toHelp.Name} needs a cure"); - if (!new CureSpell(m_Mobile).Cast()) + if (!new CureSpell(Mobile).Cast()) { - new CureSpell(m_Mobile).Cast(); + new CureSpell(Mobile).Cast(); } } else if (NeedGHeal(toHelp)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"{toHelp.Name} needs a greater heal"); - } + this.DebugSayFormatted($"{toHelp.Name} needs a greater heal"); - if (!new GreaterHealSpell(m_Mobile).Cast()) + if (!new GreaterHealSpell(Mobile).Cast()) { - new HealSpell(m_Mobile).Cast(); + new HealSpell(Mobile).Cast(); } } else if (NeedLHeal(toHelp)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"{toHelp.Name} needs a lesser heal"); - } + this.DebugSayFormatted($"{toHelp.Name} needs a lesser heal"); - new HealSpell(m_Mobile).Cast(); + new HealSpell(Mobile).Cast(); } return true; } - if (!AcquireFocusMob(m_Mobile.RangePerception, FightMode.Weakest, false, true, false)) + if (!AcquireFocusMob(Mobile.RangePerception, FightMode.Weakest, false, true, false)) { WalkRandomInHome(3, 2, 1); return true; } - WalkMobileRange(m_Mobile.FocusMob, 1, false, 4, 7); + WalkMobileRange(Mobile.FocusMob, 1, false, 4, 7); // TODO: Should it be able to do this? - if (m_Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, m_Mobile.Combatant)) + if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, Mobile.Combatant)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I used my abilities on {m_Mobile.Combatant.Name}!"); - } + this.DebugSayFormatted($"I used my abilities on {Mobile.Combatant.Name}!"); } return true; @@ -117,30 +94,36 @@ public class HealerAI : BaseAI private void ProcessTarget(Target targ, NeedDelegate[] func) { + if (func == null || func.Length == 0) + { + targ.Cancel(Mobile, TargetCancelType.Canceled); + return; + } + var toHelp = Find(func); if (toHelp == null) { - targ.Cancel(m_Mobile, TargetCancelType.Canceled); + targ.Cancel(Mobile, TargetCancelType.Canceled); } - else if (targ.Range != -1 && !m_Mobile.InRange(toHelp, targ.Range)) + else if (targ.Range != -1 && !Mobile.InRange(toHelp, targ.Range)) { - DoMove(m_Mobile.GetDirectionTo(toHelp) | Direction.Running); + DoMove(Mobile.GetDirectionTo(toHelp) | Direction.Running); } else { - targ.Invoke(m_Mobile, toHelp); + targ.Invoke(Mobile, toHelp); } } private Mobile Find(params ReadOnlySpan funcs) { - if (m_Mobile.Deleted) + if (Mobile.Deleted) { return null; } - var map = m_Mobile.Map; + var map = Mobile.Map; if (map == null) { @@ -150,9 +133,9 @@ public class HealerAI : BaseAI var prio = 0.0; Mobile found = null; - foreach (var m in m_Mobile.GetMobilesInRange(m_Mobile.RangePerception)) + foreach (var m in Mobile.GetMobilesInRange(Mobile.RangePerception)) { - if (!m_Mobile.CanSee(m) || m is not BaseCreature bc || bc.Team != m_Mobile.Team) + if (!Mobile.CanSee(m) || m is not BaseCreature bc || bc.Team != Mobile.Team) { continue; } @@ -161,7 +144,7 @@ public class HealerAI : BaseAI { if (funcs[i](bc)) { - var val = -m_Mobile.GetDistanceToSqrt(bc); + var val = -Mobile.GetDistanceToSqrt(bc); if (found == null || val > prio) { diff --git a/Projects/UOContent/Mobiles/AI/MageAI.cs b/Projects/UOContent/Mobiles/AI/MageAI.cs index 9a323f812..0ca4893e5 100644 --- a/Projects/UOContent/Mobiles/AI/MageAI.cs +++ b/Projects/UOContent/Mobiles/AI/MageAI.cs @@ -20,8 +20,8 @@ public class MageAI : BaseAI private const double DispelChance = 0.75; // 75% chance to dispel at gm magery private const double InvisChance = 0.50; // 50% chance to invis at gm magery - private static readonly int[] m_Offsets = - { + private static readonly int[] Offsets = + [ -1, -1, -1, 0, -1, 1, @@ -47,29 +47,28 @@ public class MageAI : BaseAI 2, 0, 2, 1, 2, 2 - }; + ]; - protected int m_Combo = -1; + protected int _combo = -1; - private Mobile m_LastTarget; - private Point3D m_LastTargetLoc; - private long m_NextCastTime; - private long m_NextHealTime; + private Mobile _lastTarget; + private Point3D _lastTargetLoc; + private long _nextCastTime; + private long _nextHealTime; - private LandTarget m_RevealTarget; + private LandTarget _revealTarget; - public MageAI(BaseCreature m) - : base(m) + public MageAI(BaseCreature m) : base(m) { } - public virtual bool SmartAI => m_Mobile is BaseVendor or BaseEscortable or Changeling; + public virtual bool SmartAI => Mobile is BaseVendor or BaseEscortable or Changeling; - public virtual bool IsNecromancer => Core.AOS && m_Mobile.Skills.Necromancy.Value > 50; + public virtual bool IsNecromancer => Core.AOS && Mobile.Skills.Necromancy.Value > 50; public override bool Think() { - if (m_Mobile.Deleted) + if (Mobile.Deleted) { return false; } @@ -77,38 +76,29 @@ public class MageAI : BaseAI return ProcessTarget() || base.Think(); } - public virtual double ScaleBySkill(double v, SkillName skill) => v * m_Mobile.Skills[skill].Value / 100; + public virtual double ScaleBySkill(double v, SkillName skill) => v * Mobile.Skills[skill].Value / 100; public override bool DoActionWander() { - if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I am going to attack {m_Mobile.FocusMob.Name}"); - } + this.DebugSayFormatted($"I am going to attack {Mobile.FocusMob.Name}"); - m_Mobile.Combatant = m_Mobile.FocusMob; + Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; - m_NextCastTime = Core.TickCount; + _nextCastTime = Core.TickCount; } - else if (SmartAI && m_Mobile.Mana < m_Mobile.ManaMax && !m_Mobile.Meditating) + else if (SmartAI && Mobile.Mana < Mobile.ManaMax && !Mobile.Meditating) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I am going to meditate"); - } + DebugSay("I am going to meditate"); - m_Mobile.UseSkill(SkillName.Meditation); + Mobile.UseSkill(SkillName.Meditation); } else { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I am wandering"); - } + DebugSay("I am wandering"); - m_Mobile.Warmode = false; + Mobile.Warmode = false; base.DoActionWander(); @@ -126,13 +116,13 @@ public class MageAI : BaseAI private Spell CheckCastHealingSpell() { // If I'm poisoned, always attempt to cure. - if (m_Mobile.Poisoned) + if (Mobile.Poisoned) { - return new CureSpell(m_Mobile); + return new CureSpell(Mobile); } // Summoned creatures never heal themselves. - if (m_Mobile.Summoned || m_Mobile.Controlled && Core.TickCount - m_NextHealTime < 0) + if (Mobile.Summoned || Mobile.Controlled && Core.TickCount - _nextHealTime < 0) { return null; } @@ -144,32 +134,32 @@ public class MageAI : BaseAI return null; } } - else if (Utility.Random(0, 4 + (m_Mobile.Hits == 0 ? m_Mobile.HitsMax : m_Mobile.HitsMax / m_Mobile.Hits)) < 3) + else if (Utility.Random(0, 4 + (Mobile.Hits == 0 ? Mobile.HitsMax : Mobile.HitsMax / Mobile.Hits)) < 3) { return null; } Spell spell = null; - if (m_Mobile.Hits < m_Mobile.HitsMax - 50) + if (Mobile.Hits < Mobile.HitsMax - 50) { if (UseNecromancy()) { - m_Mobile.UseSkill(SkillName.SpiritSpeak); + Mobile.UseSkill(SkillName.SpiritSpeak); } else { - spell = new GreaterHealSpell(m_Mobile); + spell = new GreaterHealSpell(Mobile); } } - else if (m_Mobile.Hits < m_Mobile.HitsMax - 10) + else if (Mobile.Hits < Mobile.HitsMax - 10) { - spell = new HealSpell(m_Mobile); + spell = new HealSpell(Mobile); } - var delay = m_Mobile.Int >= 500 ? Utility.RandomMinMax(7, 10) : Math.Sqrt(600 - m_Mobile.Int); + var delay = Mobile.Int >= 500 ? Utility.RandomMinMax(7, 10) : Math.Sqrt(600 - Mobile.Int); - m_NextHealTime = Core.TickCount + (int)TimeSpan.FromSeconds(delay).TotalMilliseconds; + _nextHealTime = Core.TickCount + (int)TimeSpan.FromSeconds(delay).TotalMilliseconds; return spell; } @@ -178,7 +168,7 @@ public class MageAI : BaseAI { if (!SmartAI) { - if (!MoveTo(m, true, m_Mobile.RangeFight)) + if (!MoveTo(m, false, Mobile.RangeFight)) { OnFailedMove(); } @@ -188,23 +178,23 @@ public class MageAI : BaseAI if (m.Paralyzed || m.Frozen) { - if (m_Mobile.InRange(m, 1)) + if (Mobile.InRange(m, 1)) { RunFrom(m); } - else if (!m_Mobile.InRange(m, Math.Max(m_Mobile.RangeFight, 2)) && !MoveTo(m, true, 1)) + else if (!Mobile.InRange(m, Math.Max(Mobile.RangeFight, 2)) && !MoveTo(m, false, 1)) { OnFailedMove(); } } - else if (!m_Mobile.InRange(m, m_Mobile.RangeFight)) + else if (!Mobile.InRange(m, Mobile.RangeFight)) { - if (!MoveTo(m, true, 1)) + if (!MoveTo(m, false, 1)) { OnFailedMove(); } } - else if (m_Mobile.InRange(m, m_Mobile.RangeFight - 1)) + else if (Mobile.InRange(m, Mobile.RangeFight - 1)) { RunFrom(m); } @@ -212,51 +202,45 @@ public class MageAI : BaseAI public void RunFrom(Mobile m) { - Run((m_Mobile.GetDirectionTo(m) - 4) & Direction.Mask); + Run((Mobile.GetDirectionTo(m) - 4) & Direction.Mask); } public void OnFailedMove() { - if (!m_Mobile.DisallowAllMoves && (SmartAI + if (!Mobile.DisallowAllMoves && (SmartAI ? Utility.Random(4) == 0 : TeleportChance > 0 && ScaleBySkill(TeleportChance, SkillName.Magery) > Utility.RandomDouble())) { - m_Mobile.Target?.Cancel(m_Mobile, TargetCancelType.Canceled); + Mobile.Target?.Cancel(Mobile, TargetCancelType.Canceled); - new TeleportSpell(m_Mobile).Cast(); + new TeleportSpell(Mobile).Cast(); - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I am stuck, I'm going to try teleporting away"); - } + DebugSay("I am stuck, I'm going to try teleporting away"); } - else if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + else if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"My move is blocked, so I am going to attack {m_Mobile.FocusMob.Name}"); - } + this.DebugSayFormatted($"My move is blocked, so I am going to attack {Mobile.FocusMob.Name}"); - m_Mobile.Combatant = m_Mobile.FocusMob; + Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; } - else if (m_Mobile.Debug) + else { - m_Mobile.DebugSay("I am stuck"); + DebugSay("I am stuck"); } } public void Run(Direction d) { - if (m_Mobile.Spell?.IsCasting == true || m_Mobile.Paralyzed || m_Mobile.Frozen || - m_Mobile.DisallowAllMoves) + if (Mobile.Spell?.IsCasting == true || Mobile.Paralyzed || Mobile.Frozen || + Mobile.DisallowAllMoves) { return; } - m_Mobile.Direction = d | Direction.Running; + Mobile.Direction = d | Direction.Running; - if (!DoMove(m_Mobile.Direction, true)) + if (!DoMove(Mobile.Direction, true)) { OnFailedMove(); } @@ -264,8 +248,8 @@ public class MageAI : BaseAI public virtual bool UseNecromancy() { - var magery = m_Mobile.Skills.Magery.BaseFixedPoint; - var necro = m_Mobile.Skills.Necromancy.BaseFixedPoint; + var magery = Mobile.Skills.Magery.BaseFixedPoint; + var necro = Mobile.Skills.Necromancy.BaseFixedPoint; return IsNecromancer && Utility.Random(magery + necro) >= magery; } @@ -274,37 +258,37 @@ public class MageAI : BaseAI public virtual Spell GetRandomDamageSpellNecro() { - var bound = m_Mobile.Skills.Necromancy.Value >= 100 ? 5 : 3; + var bound = Mobile.Skills.Necromancy.Value >= 100 ? 5 : 3; return Utility.Random(bound) switch { - 0 => new PainSpikeSpell(m_Mobile), - 1 => new PoisonStrikeSpell(m_Mobile), - 2 => new StrangleSpell(m_Mobile), - 3 => new WitherSpell(m_Mobile), - _ => new VengefulSpiritSpell(m_Mobile) + 0 => new PainSpikeSpell(Mobile), + 1 => new PoisonStrikeSpell(Mobile), + 2 => new StrangleSpell(Mobile), + 3 => new WitherSpell(Mobile), + _ => new VengefulSpiritSpell(Mobile) }; } public virtual Spell GetRandomDamageSpellMage() { - var maxCircle = Math.Clamp((int)((m_Mobile.Skills.Magery.Value + 20.0) / (100.0 / 7.0)), 1, 8); + var maxCircle = Math.Clamp((int)((Mobile.Skills.Magery.Value + 20.0) / (100.0 / 7.0)), 1, 8); return Utility.Random(maxCircle * 2) switch { - 0 => new MagicArrowSpell(m_Mobile), - 1 => new MagicArrowSpell(m_Mobile), - 2 => new HarmSpell(m_Mobile), - 3 => new HarmSpell(m_Mobile), - 4 => new FireballSpell(m_Mobile), - 5 => new FireballSpell(m_Mobile), - 6 => new LightningSpell(m_Mobile), - 7 => new LightningSpell(m_Mobile), - 8 => new MindBlastSpell(m_Mobile), - 9 => new MindBlastSpell(m_Mobile), - 10 => new EnergyBoltSpell(m_Mobile), - 11 => new ExplosionSpell(m_Mobile), - _ => new FlameStrikeSpell(m_Mobile) + 0 => new MagicArrowSpell(Mobile), + 1 => new MagicArrowSpell(Mobile), + 2 => new HarmSpell(Mobile), + 3 => new HarmSpell(Mobile), + 4 => new FireballSpell(Mobile), + 5 => new FireballSpell(Mobile), + 6 => new LightningSpell(Mobile), + 7 => new LightningSpell(Mobile), + 8 => new MindBlastSpell(Mobile), + 9 => new MindBlastSpell(Mobile), + 10 => new EnergyBoltSpell(Mobile), + 11 => new ExplosionSpell(Mobile), + _ => new FlameStrikeSpell(Mobile) }; } @@ -315,36 +299,36 @@ public class MageAI : BaseAI { return Utility.Random(4) switch { - 0 => new BloodOathSpell(m_Mobile), - 1 => new CorpseSkinSpell(m_Mobile), - 2 => new EvilOmenSpell(m_Mobile), - _ => new MindRotSpell(m_Mobile) + 0 => new BloodOathSpell(Mobile), + 1 => new CorpseSkinSpell(Mobile), + 2 => new EvilOmenSpell(Mobile), + _ => new MindRotSpell(Mobile) }; } public virtual Spell GetRandomCurseSpellMage() { - if (m_Mobile.Skills.Magery.Value >= 40.0 && Utility.Random(4) == 0) + if (Mobile.Skills.Magery.Value >= 40.0 && Utility.Random(4) == 0) { - return new CurseSpell(m_Mobile); + return new CurseSpell(Mobile); } return Utility.Random(3) switch { - 0 => new WeakenSpell(m_Mobile), - 1 => new ClumsySpell(m_Mobile), - _ => new FeeblemindSpell(m_Mobile) + 0 => new WeakenSpell(Mobile), + 1 => new ClumsySpell(Mobile), + _ => new FeeblemindSpell(Mobile) }; } public virtual Spell GetRandomManaDrainSpell() { - if (m_Mobile.Skills.Magery.Value >= 80.0 && Utility.RandomBool()) + if (Mobile.Skills.Magery.Value >= 80.0 && Utility.RandomBool()) { - return new ManaVampireSpell(m_Mobile); + return new ManaVampireSpell(Mobile); } - return new ManaDrainSpell(m_Mobile); + return new ManaDrainSpell(Mobile); } public virtual Spell DoDispel(Mobile toDispel) @@ -353,7 +337,7 @@ public class MageAI : BaseAI { if (DispelChance > 0 && ScaleBySkill(DispelChance, SkillName.Magery) > Utility.RandomDouble()) { - return new DispelSpell(m_Mobile); + return new DispelSpell(Mobile); } return null; @@ -366,18 +350,18 @@ public class MageAI : BaseAI return spell; } - var distance = (int)m_Mobile.GetDistanceToSqrt(toDispel); - if (!m_Mobile.DisallowAllMoves && distance > 0 && Utility.Random(distance) == 0) + var distance = (int)Mobile.GetDistanceToSqrt(toDispel); + if (!Mobile.DisallowAllMoves && distance > 0 && Utility.Random(distance) == 0) { - return new TeleportSpell(m_Mobile); + return new TeleportSpell(Mobile); } - if (Utility.Random(3) == 0 && !m_Mobile.InRange(toDispel, 3) && !toDispel.Paralyzed && !toDispel.Frozen) + if (Utility.Random(3) == 0 && !Mobile.InRange(toDispel, 3) && !toDispel.Paralyzed && !toDispel.Frozen) { - return new ParalyzeSpell(m_Mobile); + return new ParalyzeSpell(Mobile); } - return new DispelSpell(m_Mobile); + return new DispelSpell(Mobile); } public virtual Spell ChooseSpell(Mobile c) @@ -396,12 +380,12 @@ public class MageAI : BaseAI if (IsNecromancer) { var psDamage = - (m_Mobile.Skills.SpiritSpeak.Value - c.Skills.MagicResist.Value) / 10 + + (Mobile.Skills.SpiritSpeak.Value - c.Skills.MagicResist.Value) / 10 + (c.Player ? 18 : 30); if (psDamage > c.Hits) { - return new PainSpikeSpell(m_Mobile); + return new PainSpikeSpell(Mobile); } } @@ -415,56 +399,41 @@ public class MageAI : BaseAI goto default; } - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("Attempting to poison"); - } + DebugSay("Attempting to poison"); - spell = new PoisonSpell(m_Mobile); + spell = new PoisonSpell(Mobile); break; } case 2: // Bless ourselves { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("Blessing myself"); - } + DebugSay("Blessing myself"); - spell = new BlessSpell(m_Mobile); + spell = new BlessSpell(Mobile); break; } case 3: case 4: // Curse them { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("Attempting to curse"); - } + DebugSay("Attempting to curse"); spell = GetRandomCurseSpell(); break; } case 5: // Paralyze them { - if (c.Paralyzed || m_Mobile.Skills.Magery.Value <= 50.0) + if (c.Paralyzed || Mobile.Skills.Magery.Value <= 50.0) { goto default; } - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("Attempting to paralyze"); - } + DebugSay("Attempting to paralyze"); - spell = new ParalyzeSpell(m_Mobile); + spell = new ParalyzeSpell(Mobile); break; } case 6: // Drain mana { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("Attempting to drain mana"); - } + DebugSay("Attempting to drain mana"); spell = GetRandomManaDrainSpell(); break; @@ -476,20 +445,14 @@ public class MageAI : BaseAI goto default; } - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("Attempting to invis myself"); - } + DebugSay("Attempting to invis myself"); - spell = new InvisibilitySpell(m_Mobile); + spell = new InvisibilitySpell(Mobile); break; } default: // Damage them { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("Just doing damage"); - } + DebugSay("Just doing damage"); spell = GetRandomDamageSpell(); break; @@ -511,7 +474,7 @@ public class MageAI : BaseAI goto case 1; } - spell = new PoisonSpell(m_Mobile); + spell = new PoisonSpell(Mobile); break; } case 1: // Deal some damage @@ -521,33 +484,30 @@ public class MageAI : BaseAI } default: // Set up a combo { - if (m_Mobile.Mana is > 15 and < 40) + if (Mobile.Mana is > 15 and < 40) { - if (c.Paralyzed && !c.Poisoned && !m_Mobile.Meditating) + if (c.Paralyzed && !c.Poisoned && !Mobile.Meditating) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I am going to meditate"); - } + DebugSay("I am going to meditate"); - m_Mobile.UseSkill(SkillName.Meditation); + Mobile.UseSkill(SkillName.Meditation); } else if (!c.Poisoned) { - spell = new ParalyzeSpell(m_Mobile); + spell = new ParalyzeSpell(Mobile); } } - else if (m_Mobile.Mana > 60) + else if (Mobile.Mana > 60) { if (Utility.RandomBool() && !c.Paralyzed && !c.Frozen && !c.Poisoned) { - m_Combo = 0; - spell = new ParalyzeSpell(m_Mobile); + _combo = 0; + spell = new ParalyzeSpell(Mobile); } else { - m_Combo = 1; - spell = new ExplosionSpell(m_Mobile); + _combo = 1; + spell = new ExplosionSpell(Mobile); } } @@ -557,16 +517,13 @@ public class MageAI : BaseAI } } - if (m_Mobile.Debug) + if (spell != null) { - if (spell != null) - { - m_Mobile.DebugSay($"Casting {spell.Name}"); - } - else - { - m_Mobile.DebugSay("I don't have a spell to use!"); - } + this.DebugSayFormatted($"Casting {spell.Name}"); + } + else + { + DebugSay("I don't have a spell to use!"); } return spell; @@ -576,31 +533,31 @@ public class MageAI : BaseAI { Spell spell = null; - if (m_Combo == 0) + if (_combo == 0) { - spell = new ExplosionSpell(m_Mobile); - ++m_Combo; // Move to next spell + spell = new ExplosionSpell(Mobile); + ++_combo; // Move to next spell } - else if (m_Combo == 1) + else if (_combo == 1) { - spell = new WeakenSpell(m_Mobile); - ++m_Combo; // Move to next spell + spell = new WeakenSpell(Mobile); + ++_combo; // Move to next spell } - else if (m_Combo == 2) + else if (_combo == 2) { if (!c.Poisoned) { - spell = new PoisonSpell(m_Mobile); + spell = new PoisonSpell(Mobile); } else if (IsNecromancer) { - spell = new StrangleSpell(m_Mobile); + spell = new StrangleSpell(Mobile); } - ++m_Combo; // Move to next spell + ++_combo; // Move to next spell } - if (m_Combo == 3 && spell == null) + if (_combo == 3 && spell == null) { switch (Utility.Random(IsNecromancer ? 4 : 3)) { @@ -608,41 +565,41 @@ public class MageAI : BaseAI { if (c.Int < c.Dex) { - spell = new FeeblemindSpell(m_Mobile); + spell = new FeeblemindSpell(Mobile); } else { - spell = new ClumsySpell(m_Mobile); + spell = new ClumsySpell(Mobile); } - ++m_Combo; // Move to next spell + ++_combo; // Move to next spell break; } case 1: { - spell = new EnergyBoltSpell(m_Mobile); - m_Combo = -1; // Reset combo state + spell = new EnergyBoltSpell(Mobile); + _combo = -1; // Reset combo state break; } case 2: { - spell = new FlameStrikeSpell(m_Mobile); - m_Combo = -1; // Reset combo state + spell = new FlameStrikeSpell(Mobile); + _combo = -1; // Reset combo state break; } default: { - spell = new PainSpikeSpell(m_Mobile); - m_Combo = -1; // Reset combo state + spell = new PainSpikeSpell(Mobile); + _combo = -1; // Reset combo state break; } } } - else if (m_Combo == 4 && spell == null) + else if (_combo == 4 && spell == null) { - spell = new MindBlastSpell(m_Mobile); - m_Combo = -1; + spell = new MindBlastSpell(Mobile); + _combo = -1; } return spell; @@ -652,7 +609,7 @@ public class MageAI : BaseAI { if (SmartAI || spell is DispelSpell) { - return TimeSpan.FromSeconds(m_Mobile.ActiveSpeed); + return TimeSpan.FromSeconds(Mobile.ActiveSpeed); } var del = ScaleBySkill(3.0, SkillName.Magery); @@ -664,140 +621,119 @@ public class MageAI : BaseAI public override bool DoActionCombat() { - var c = m_Mobile.Combatant; - m_Mobile.Warmode = true; + var c = Mobile.Combatant; + Mobile.Warmode = true; - if (c?.Deleted != false || !c.Alive || c.IsDeadBondedPet || !m_Mobile.CanSee(c) || - !m_Mobile.CanBeHarmful(c, false) || c.Map != m_Mobile.Map) + if (c?.Deleted != false || !c.Alive || c.IsDeadBondedPet || !Mobile.CanSee(c) || + !Mobile.CanBeHarmful(c, false) || c.Map != Mobile.Map) { // Our combatant is deleted, dead, hidden, or we cannot hurt them // Try to find another combatant - if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - m_Mobile.Combatant = c = m_Mobile.FocusMob!; + Mobile.Combatant = c = Mobile.FocusMob!; - if (m_Mobile.Debug) - { - m_Mobile.DebugSay( - $"Something happened to my combatant, so I am going to fight {c.Name}" - ); - } + this.DebugSayFormatted($"Something happened to my combatant, so I am going to fight {c.Name}"); - m_Mobile.FocusMob = null; + Mobile.FocusMob = null; } else { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("Something happened to my combatant, and nothing is around. I am on guard."); - } + DebugSay("Something happened to my combatant, and nothing is around. I am on guard."); Action = ActionType.Guard; return true; } } - if (!m_Mobile.InLOS(c)) + if (!Mobile.InLOS(c)) { - if (m_Mobile.Debug) + DebugSay("I can't see my target"); + + if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - m_Mobile.DebugSay("I can't see my target"); - } + Mobile.Combatant = c = Mobile.FocusMob!; - if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) - { - m_Mobile.Combatant = c = m_Mobile.FocusMob!; + this.DebugSayFormatted($"I will switch to {c.Name}"); - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I will switch to {c.Name}"); - } - - m_Mobile.FocusMob = null; + Mobile.FocusMob = null; } } - if (!Core.AOS && SmartAI && !m_Mobile.StunReady && m_Mobile.Skills.Wrestling.Value >= 80.0 && - m_Mobile.Skills.Anatomy.Value >= 80.0) + if (!Core.AOS && SmartAI && !Mobile.StunReady && Mobile.Skills.Wrestling.Value >= 80.0 && + Mobile.Skills.Anatomy.Value >= 80.0) { - Fists.StunRequest(m_Mobile); + Fists.StunRequest(Mobile); } - if (!m_Mobile.InRange(c, m_Mobile.RangePerception)) + if (!Mobile.InRange(c, Mobile.RangePerception)) { // They are somewhat far away, can we find something else? - if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - m_Mobile.Combatant = m_Mobile.FocusMob; - m_Mobile.FocusMob = null; + Mobile.Combatant = Mobile.FocusMob; + Mobile.FocusMob = null; } - else if (!m_Mobile.InRange(c, m_Mobile.RangePerception * 3)) + else if (!Mobile.InRange(c, Mobile.RangePerception * 3)) { - m_Mobile.Combatant = null; + Mobile.Combatant = null; } - c = m_Mobile.Combatant; + c = Mobile.Combatant; if (c == null) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("My combatant has fled, so I am on guard"); - } + DebugSay("My combatant has fled, so I am on guard"); Action = ActionType.Guard; return true; } } - if (!m_Mobile.Controlled && !m_Mobile.Summoned && m_Mobile.CanFlee && m_Mobile.Hits < m_Mobile.HitsMax * 20 / 100) + if (!Mobile.Controlled && !Mobile.Summoned && Mobile.CanFlee && Mobile.Hits < Mobile.HitsMax * 20 / 100) { // We are low on health, should we flee? // (10 + diff)% chance to flee - var fleeChance = 10 + Math.Max(0, c.Hits - m_Mobile.Hits); + var fleeChance = 10 + Math.Max(0, c.Hits - Mobile.Hits); if (Utility.Random(0, 100) > fleeChance) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I am going to flee from {c.Name}"); - } + this.DebugSayFormatted($"I am going to flee from {c.Name}"); Action = ActionType.Flee; return true; } } - if (m_Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, c)) + if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, c)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I used my abilities!"); - } + DebugSay("I used my abilities!"); } - else if (m_Mobile.Spell == null && Core.TickCount - m_NextCastTime >= 0) + else if (Mobile.Spell == null && Core.TickCount - _nextCastTime >= 0) { + if (Mobile.Controlled && c == Mobile) + { + DebugSay("I should not attack myself!"); + Mobile.Combatant = null; + Action = ActionType.Guard; + return true; + } + // We are ready to cast a spell Spell spell; var toDispel = FindDispelTarget(true); - if (m_Mobile.Poisoned) // Top cast priority is cure + if (Mobile.Poisoned) // Top cast priority is cure { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I am going to cure myself"); - } + DebugSay("I am going to cure myself"); - spell = new CureSpell(m_Mobile); + spell = new CureSpell(Mobile); } else if (toDispel != null) // Something dispellable is attacking us { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I am going to dispel {toDispel}"); - } + this.DebugSayFormatted($"I am going to dispel {toDispel}"); spell = DoDispel(toDispel); // May return null if dumb AI and doesn't have enough skill } @@ -809,96 +745,90 @@ public class MageAI : BaseAI spell ??= SmartAI switch { // We are doing a spell combo - true when m_Combo != -1 => DoCombo(c), + true when _combo != -1 => DoCombo(c), // They have a heal spell out - true when !c.Poisoned && c.Spell is HealSpell or GreaterHealSpell => new PoisonSpell(m_Mobile), + true when !c.Poisoned && c.Spell is HealSpell or GreaterHealSpell => new PoisonSpell(Mobile), _ => ChooseSpell(c) }; // Now we have a spell picked - var range = (spell as IRangedSpell)?.TargetRange ?? m_Mobile.RangePerception; + var range = (spell as IRangedSpell)?.TargetRange ?? Mobile.RangePerception; // Move first before casting if (!SmartAI || toDispel == null) { RunTo(c); } - else if (m_Mobile.InRange(toDispel, Math.Min(10, range))) + else if (Mobile.InRange(toDispel, Math.Min(10, range))) { RunFrom(toDispel); } - else if (!m_Mobile.InRange(toDispel, range)) + else if (!Mobile.InRange(toDispel, range)) { RunTo(toDispel); } // After running, make sure we are still in range - if (spell == null || m_Mobile.InRange(c, range)) + if (spell == null || Mobile.InRange(c, range)) { spell?.Cast(); // Even if we don't have a spell, delay the next potential cast - m_NextCastTime = Core.TickCount + (int)GetDelay(spell).TotalMilliseconds; + _nextCastTime = Core.TickCount + (int)GetDelay(spell).TotalMilliseconds; } } - else if (m_Mobile.Spell?.IsCasting != true) + else if (Mobile.Spell?.IsCasting != true) { RunTo(c); } - if (m_Mobile.Spell != null || !m_Mobile.InRange(c, 1) || Core.TickCount - m_Mobile.LastMoveTime > 800) + if (Mobile.InRange(c, 1) || Mobile.Spell?.IsCasting == true || Core.TickCount - Mobile.LastMoveTime > 400) { - m_Mobile.Direction = m_Mobile.GetDirectionTo(c); + Mobile.Direction = Mobile.GetDirectionTo(c); } - m_LastTarget = c; - m_LastTargetLoc = c.Location; + _lastTarget = c; + _lastTargetLoc = c.Location; return true; } public override bool DoActionGuard() { - if (m_LastTarget?.Hidden == true) + if (_lastTarget?.Hidden == true) { - var map = m_Mobile.Map; + var map = Mobile.Map; - if (map == null || !m_Mobile.InRange(m_LastTargetLoc, m_Mobile.RangePerception)) + if (map == null || !Mobile.InRange(_lastTargetLoc, Mobile.RangePerception)) { - m_LastTarget = null; + _lastTarget = null; } - else if (m_Mobile.Spell == null && Core.TickCount - m_NextCastTime >= 0) + else if (Mobile.Spell == null && Core.TickCount - _nextCastTime >= 0) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I am going to reveal my last target"); - } + DebugSay("I am going to reveal my last target"); - m_RevealTarget = new LandTarget(m_LastTargetLoc, map); - Spell spell = new RevealSpell(m_Mobile); + _revealTarget = new LandTarget(_lastTargetLoc, map); + Spell spell = new RevealSpell(Mobile); if (spell.Cast()) { - m_LastTarget = null; // only do it once + _lastTarget = null; // only do it once } - m_NextCastTime = Core.TickCount + (int)GetDelay(spell).TotalMilliseconds; + _nextCastTime = Core.TickCount + (int)GetDelay(spell).TotalMilliseconds; } } - if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I am going to attack {m_Mobile.FocusMob.Name}"); - } + this.DebugSayFormatted($"I am going to attack {Mobile.FocusMob.Name}"); - m_Mobile.Combatant = m_Mobile.FocusMob; + Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; } else { - if (!m_Mobile.Controlled) + if (!Mobile.Controlled) { ProcessTarget(); @@ -915,41 +845,30 @@ public class MageAI : BaseAI public override bool DoActionFlee() { - // Mobile c = m_Mobile.Combatant; - - if ((m_Mobile.Mana > 20 || m_Mobile.Mana == m_Mobile.ManaMax) && m_Mobile.Hits > m_Mobile.HitsMax / 2) + if ((Mobile.Mana > 20 || Mobile.Mana == Mobile.ManaMax) && Mobile.Hits > Mobile.HitsMax / 2) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I am stronger now, my guard is up"); - } + DebugSay("I am stronger now, my guard is up"); Action = ActionType.Guard; } - else if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + else if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I am scared of {m_Mobile.FocusMob.Name}"); - } + this.DebugSayFormatted($"I am scared of {Mobile.FocusMob.Name}"); - RunFrom(m_Mobile.FocusMob); - m_Mobile.FocusMob = null; + RunFrom(Mobile.FocusMob); + Mobile.FocusMob = null; - if (m_Mobile.Poisoned && Utility.Random(0, 5) == 0) + if (Mobile.Poisoned && Utility.Random(0, 5) == 0) { - new CureSpell(m_Mobile).Cast(); + new CureSpell(Mobile).Cast(); } } else { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("Area seems clear, but my guard is up"); - } + DebugSay("Area seems clear, but my guard is up"); Action = ActionType.Guard; - m_Mobile.Warmode = true; + Mobile.Warmode = true; } return true; @@ -957,26 +876,26 @@ public class MageAI : BaseAI public Mobile FindDispelTarget(bool activeOnly) { - if (m_Mobile.Deleted || m_Mobile.Int < 95 || CanDispel(m_Mobile) || m_Mobile.AutoDispel) + if (Mobile.Deleted || Mobile.Int < 95 || CanDispel(Mobile) || Mobile.AutoDispel) { return null; } if (activeOnly) { - var aggressed = m_Mobile.Aggressed; - var aggressors = m_Mobile.Aggressors; + var aggressed = Mobile.Aggressed; + var aggressors = Mobile.Aggressors; Mobile active = null; var activePrio = 0.0; - var comb = m_Mobile.Combatant; + var comb = Mobile.Combatant; if (comb?.Deleted == false && comb.Alive && !comb.IsDeadBondedPet && - m_Mobile.InRange(comb, m_Mobile.RangePerception) && CanDispel(comb)) + Mobile.InRange(comb, Mobile.RangePerception) && CanDispel(comb)) { active = comb; - activePrio = m_Mobile.GetDistanceToSqrt(comb); + activePrio = Mobile.GetDistanceToSqrt(comb); if (activePrio <= 2) { @@ -989,9 +908,9 @@ public class MageAI : BaseAI var info = aggressed[i]; var m = info.Defender; - if (m != comb && m.Combatant == m_Mobile && m_Mobile.InRange(m, m_Mobile.RangePerception) && CanDispel(m)) + if (m != comb && m.Combatant == Mobile && Mobile.InRange(m, Mobile.RangePerception) && CanDispel(m)) { - var prio = m_Mobile.GetDistanceToSqrt(m); + var prio = Mobile.GetDistanceToSqrt(m); if (active == null || prio < activePrio) { @@ -1011,9 +930,9 @@ public class MageAI : BaseAI var info = aggressors[i]; var m = info.Attacker; - if (m != comb && m.Combatant == m_Mobile && m_Mobile.InRange(m, m_Mobile.RangePerception) && CanDispel(m)) + if (m != comb && m.Combatant == Mobile && Mobile.InRange(m, Mobile.RangePerception) && CanDispel(m)) { - var prio = m_Mobile.GetDistanceToSqrt(m); + var prio = Mobile.GetDistanceToSqrt(m); if (active == null || prio < activePrio) { @@ -1031,26 +950,26 @@ public class MageAI : BaseAI return active; } - var map = m_Mobile.Map; + var map = Mobile.Map; if (map != null) { Mobile active = null, inactive = null; double actPrio = 0.0, inactPrio = 0.0; - var comb = m_Mobile.Combatant; + var comb = Mobile.Combatant; if (comb?.Deleted == false && comb.Alive && !comb.IsDeadBondedPet && CanDispel(comb)) { active = inactive = comb; - actPrio = inactPrio = m_Mobile.GetDistanceToSqrt(comb); + actPrio = inactPrio = Mobile.GetDistanceToSqrt(comb); } - foreach (var m in m_Mobile.GetMobilesInRange(m_Mobile.RangePerception)) + foreach (var m in Mobile.GetMobilesInRange(Mobile.RangePerception)) { - if (m != m_Mobile && CanDispel(m)) + if (m != Mobile && CanDispel(m)) { - var prio = m_Mobile.GetDistanceToSqrt(m); + var prio = Mobile.GetDistanceToSqrt(m); if (inactive == null || prio < inactPrio) { @@ -1058,7 +977,7 @@ public class MageAI : BaseAI inactPrio = prio; } - if ((m_Mobile.Combatant == m || m.Combatant == m_Mobile) && (active == null || prio < actPrio)) + if ((Mobile.Combatant == m || m.Combatant == Mobile) && (active == null || prio < actPrio)) { active = m; actPrio = prio; @@ -1073,12 +992,12 @@ public class MageAI : BaseAI } public bool CanDispel(Mobile m) => - m is BaseCreature creature && creature.Summoned && creature.SummonMaster != m_Mobile && - m_Mobile.CanBeHarmful(creature, false) && !creature.IsAnimatedDead; + m is BaseCreature creature && creature.Summoned && creature.SummonMaster != Mobile && + Mobile.CanBeHarmful(creature, false) && !creature.IsAnimatedDead; private bool ProcessTarget() { - var targ = m_Mobile.Target; + var targ = Mobile.Target; if (targ == null) { @@ -1098,7 +1017,7 @@ public class MageAI : BaseAI if (isInvisible) { - toTarget = m_Mobile; + toTarget = Mobile; } else if (isDispel) { @@ -1108,7 +1027,7 @@ public class MageAI : BaseAI { RunTo(toTarget); } - else if (toTarget != null && m_Mobile.InRange(toTarget, 10)) + else if (toTarget != null && Mobile.InRange(toTarget, 10)) { RunFrom(toTarget); } @@ -1119,14 +1038,14 @@ public class MageAI : BaseAI if (toTarget == null) { - toTarget = m_Mobile.Combatant; + toTarget = Mobile.Combatant; if (toTarget != null) { RunTo(toTarget); } } - else if (m_Mobile.InRange(toTarget, 10)) + else if (Mobile.InRange(toTarget, 10)) { RunFrom(toTarget); teleportAway = true; @@ -1138,7 +1057,7 @@ public class MageAI : BaseAI } else { - toTarget = m_Mobile.Combatant; + toTarget = Mobile.Combatant; if (toTarget != null) { @@ -1148,27 +1067,27 @@ public class MageAI : BaseAI if ((targ.Flags & TargetFlags.Harmful) != 0 && toTarget != null) { - if ((targ.Range == -1 || m_Mobile.InRange(toTarget, targ.Range)) && m_Mobile.CanSee(toTarget) && - m_Mobile.InLOS(toTarget)) + if ((targ.Range == -1 || Mobile.InRange(toTarget, targ.Range)) && Mobile.CanSee(toTarget) && + Mobile.InLOS(toTarget)) { - targ.Invoke(m_Mobile, toTarget); + targ.Invoke(Mobile, toTarget); } else if (isDispel) { - targ.Cancel(m_Mobile, TargetCancelType.Canceled); + targ.Cancel(Mobile, TargetCancelType.Canceled); } } else if ((targ.Flags & TargetFlags.Beneficial) != 0) { - targ.Invoke(m_Mobile, m_Mobile); + targ.Invoke(Mobile, Mobile); } - else if (isReveal && m_RevealTarget != null) + else if (isReveal && _revealTarget != null) { - targ.Invoke(m_Mobile, m_RevealTarget); + targ.Invoke(Mobile, _revealTarget); } else { - var map = m_Mobile.Map; + var map = Mobile.Map; if (map != null && isTeleport && toTarget != null) { @@ -1179,10 +1098,10 @@ public class MageAI : BaseAI if (teleportAway) { - var rx = m_Mobile.X - toTarget.X; - var ry = m_Mobile.Y - toTarget.Y; + var rx = Mobile.X - toTarget.X; + var ry = Mobile.Y - toTarget.Y; - var d = m_Mobile.GetDistanceToSqrt(toTarget); + var d = Mobile.GetDistanceToSqrt(toTarget); px = toTarget.X + (int)(rx * (10 / d)); py = toTarget.Y + (int)(ry * (10 / d)); @@ -1193,18 +1112,18 @@ public class MageAI : BaseAI py = toTarget.Y; } - for (var i = 0; i < m_Offsets.Length; i += 2) + for (var i = 0; i < Offsets.Length; i += 2) { - int x = m_Offsets[i], y = m_Offsets[i + 1]; + int x = Offsets[i], y = Offsets[i + 1]; var p = new Point3D(px + x, py + y, 0); var lt = new LandTarget(p, map); - if ((targ.Range == -1 || m_Mobile.InRange(p, targ.Range)) && m_Mobile.InLOS(lt) && + if ((targ.Range == -1 || Mobile.InRange(p, targ.Range)) && Mobile.InLOS(lt) && map.CanSpawnMobile(px + x, py + y, lt.Z) && !SpellHelper.CheckMulti(p, map)) { - targ.Invoke(m_Mobile, lt); + targ.Invoke(Mobile, lt); return true; } } @@ -1212,23 +1131,23 @@ public class MageAI : BaseAI for (var i = 0; i < 10; ++i) { var randomPoint = new Point3D( - m_Mobile.X - teleRange + Utility.Random(teleRange * 2 + 1), - m_Mobile.Y - teleRange + Utility.Random(teleRange * 2 + 1), + Mobile.X - teleRange + Utility.Random(teleRange * 2 + 1), + Mobile.Y - teleRange + Utility.Random(teleRange * 2 + 1), 0 ); var lt = new LandTarget(randomPoint, map); - if (m_Mobile.InLOS(lt) && map.CanSpawnMobile(lt.X, lt.Y, lt.Z) && + if (Mobile.InLOS(lt) && map.CanSpawnMobile(lt.X, lt.Y, lt.Z) && !SpellHelper.CheckMulti(randomPoint, map)) { - targ.Invoke(m_Mobile, new LandTarget(randomPoint, map)); + targ.Invoke(Mobile, new LandTarget(randomPoint, map)); return true; } } } - targ.Cancel(m_Mobile, TargetCancelType.Canceled); + targ.Cancel(Mobile, TargetCancelType.Canceled); } return true; diff --git a/Projects/UOContent/Mobiles/AI/MeleeAI.cs b/Projects/UOContent/Mobiles/AI/MeleeAI.cs index 5bb697a1b..01ef3c06f 100644 --- a/Projects/UOContent/Mobiles/AI/MeleeAI.cs +++ b/Projects/UOContent/Mobiles/AI/MeleeAI.cs @@ -10,24 +10,18 @@ public class MeleeAI : BaseAI public override bool DoActionWander() { - if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name}, attacking"); - } + this.DebugSayFormatted($"I have detected {Mobile.FocusMob.Name}, attacking"); - m_Mobile.Combatant = m_Mobile.FocusMob; + Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; } else { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I am wandering"); - } + DebugSay("I am wandering"); - m_Mobile.Warmode = false; + Mobile.Warmode = false; base.DoActionWander(); } @@ -37,110 +31,84 @@ public class MeleeAI : BaseAI public override bool DoActionCombat() { - var combatant = m_Mobile.Combatant; + var combatant = Mobile.Combatant; - if (combatant == null || combatant.Deleted || combatant.Map != m_Mobile.Map || !combatant.Alive || + if (combatant == null || combatant.Deleted || combatant.Map != Mobile.Map || !combatant.Alive || combatant.IsDeadBondedPet) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("My combatant is gone, so my guard is up"); - } + DebugSay("My combatant is gone, so my guard is up"); Action = ActionType.Guard; return true; } - if (!m_Mobile.InRange(combatant, m_Mobile.RangePerception)) + if (!Mobile.InRange(combatant, Mobile.RangePerception)) { // They are somewhat far away, can we find something else? - if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - m_Mobile.Combatant = m_Mobile.FocusMob; - m_Mobile.FocusMob = null; + Mobile.Combatant = Mobile.FocusMob; + Mobile.FocusMob = null; } - else if (!m_Mobile.InRange(combatant, m_Mobile.RangePerception * 3)) + else if (!Mobile.InRange(combatant, Mobile.RangePerception * 3)) { - m_Mobile.Combatant = null; + Mobile.Combatant = null; } - combatant = m_Mobile.Combatant; + combatant = Mobile.Combatant; if (combatant == null) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("My combatant has fled, so I am on guard"); - } + DebugSay("My combatant has fled, so I am on guard"); Action = ActionType.Guard; return true; } } - if (!MoveTo(combatant, true, m_Mobile.RangeFight)) + if (!MoveTo(combatant, false, Mobile.RangeFight)) { - m_Mobile.Direction = m_Mobile.GetDirectionTo(combatant); - if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"My move is blocked, so I am going to attack {m_Mobile.FocusMob!.Name}"); - } + this.DebugSayFormatted($"My move is blocked, so I am going to attack {Mobile.FocusMob!.Name}"); - m_Mobile.Combatant = m_Mobile.FocusMob; + Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; return true; } - if (m_Mobile.GetDistanceToSqrt(combatant) > m_Mobile.RangePerception + 1) + if (Mobile.GetDistanceToSqrt(combatant) > Mobile.RangePerception + 1) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I cannot find {combatant.Name}, so my guard is up"); - } + this.DebugSayFormatted($"I cannot find {combatant.Name}, so my guard is up"); Action = ActionType.Guard; return true; } - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I cannot find {combatant.Name}, so my guard is up"); - } + this.DebugSayFormatted($"I cannot find {combatant.Name}, so my guard is up"); } - else if (Core.TickCount - m_Mobile.LastMoveTime > 400) + else if (Core.TickCount - Mobile.LastMoveTime > 200) { - m_Mobile.Direction = m_Mobile.GetDirectionTo(combatant); + Mobile.Direction = Mobile.GetDirectionTo(combatant); } - if (!m_Mobile.Controlled && !m_Mobile.Summoned && m_Mobile.CanFlee) + // We are low on health, should we flee? + if (!Mobile.Controlled && !Mobile.Summoned && Mobile.CanFlee && Mobile.Hits < Mobile.HitsMax * 20 / 100) { - if (m_Mobile.Hits < m_Mobile.HitsMax * 20 / 100) + var fleeChance = 10 + Math.Max(0, combatant.Hits - Mobile.Hits); // (10 + diff)% chance to flee; + if (Utility.Random(0, 100) < fleeChance) { - // We are low on health, should we flee? + this.DebugSayFormatted($"I am going to flee from {combatant.Name}"); - var fleeChance = 10 + Math.Max(0, combatant.Hits - m_Mobile.Hits); // (10 + diff)% chance to flee; - if (Utility.Random(0, 100) < fleeChance) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I am going to flee from {combatant.Name}"); - } - - Action = ActionType.Flee; - return true; - } + Action = ActionType.Flee; + return true; } } - if (m_Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) + if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I used my abilities!"); - } + DebugSay("I used my abilities!"); } return true; @@ -148,14 +116,11 @@ public class MeleeAI : BaseAI public override bool DoActionGuard() { - if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name}, attacking"); - } + this.DebugSayFormatted($"I have detected {Mobile.FocusMob.Name}, attacking"); - m_Mobile.Combatant = m_Mobile.FocusMob; + Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; } else @@ -168,18 +133,15 @@ public class MeleeAI : BaseAI public override bool DoActionFlee() { - if (m_Mobile.Hits > m_Mobile.HitsMax / 2) + if (Mobile.Hits > Mobile.HitsMax / 2) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I am stronger now, so I will continue fighting"); - } + DebugSay("I am stronger now, so I will continue fighting"); Action = ActionType.Combat; } else { - m_Mobile.FocusMob = m_Mobile.Combatant; + Mobile.FocusMob = Mobile.Combatant; base.DoActionFlee(); } diff --git a/Projects/UOContent/Mobiles/AI/OppositionGroup.cs b/Projects/UOContent/Mobiles/AI/OppositionGroup.cs index 9c7dafb97..344b65eba 100644 --- a/Projects/UOContent/Mobiles/AI/OppositionGroup.cs +++ b/Projects/UOContent/Mobiles/AI/OppositionGroup.cs @@ -1,135 +1,125 @@ using System; using Server.Mobiles; -namespace Server +namespace Server; + +public class OppositionGroup { - public class OppositionGroup + private readonly Type[][] _types; + + public OppositionGroup(Type[][] types) => _types = types; + + public static OppositionGroup TerathansAndOphidians { get; } = new( + [ + [ + typeof(TerathanAvenger), + typeof(TerathanDrone), + typeof(TerathanMatriarch), + typeof(TerathanWarrior) + ], + [ + typeof(OphidianArchmage), + typeof(OphidianKnight), + typeof(OphidianMage), + typeof(OphidianMatriarch), + typeof(OphidianWarrior) + ] + ] + ); + + public static OppositionGroup SavagesAndOrcs { get; } = new( + [ + [ + typeof(Orc), + typeof(OrcBomber), + typeof(OrcBrute), + typeof(OrcCaptain), + typeof(OrcishLord), + typeof(OrcishMage), + typeof(SpawnedOrcishLord) + ], + [ + typeof(Savage), + typeof(SavageRider), + typeof(SavageRidgeback), + typeof(SavageShaman) + ] + ] + ); + + public static OppositionGroup FeyAndUndead { get; } = new( + [ + [ + typeof(Centaur), + typeof(EtherealWarrior), + typeof(Kirin), + typeof(LordOaks), + typeof(Pixie), + typeof(Silvani), + typeof(Unicorn), + typeof(Wisp), + typeof(Treefellow), + typeof(MLDryad), + typeof(Satyr) + ], + [ + typeof(AncientLich), + typeof(Bogle), + typeof(LichLord), + typeof(Shade), + typeof(Spectre), + typeof(Wraith), + typeof(BoneKnight), + typeof(Ghoul), + typeof(Mummy), + typeof(SkeletalKnight), + typeof(Skeleton), + typeof(Zombie), + typeof(ShadowKnight), + typeof(DarknightCreeper), + typeof(RevenantLion), + typeof(LadyOfTheSnow), + typeof(RottingCorpse), + typeof(SkeletalDragon), + typeof(Lich) + ] + ] + ); + + public bool IsEnemy(object from, object target) { - private readonly Type[][] m_Types; + var fromGroup = IndexOf(from); + var targGroup = IndexOf(target); - public OppositionGroup(Type[][] types) => m_Types = types; + return fromGroup != -1 && targGroup != -1 && fromGroup != targGroup; + } - public static OppositionGroup TerathansAndOphidians { get; } = new( - new[] - { - new[] - { - typeof(TerathanAvenger), - typeof(TerathanDrone), - typeof(TerathanMatriarch), - typeof(TerathanWarrior) - }, - new[] - { - typeof(OphidianArchmage), - typeof(OphidianKnight), - typeof(OphidianMage), - typeof(OphidianMatriarch), - typeof(OphidianWarrior) - } - } - ); - - public static OppositionGroup SavagesAndOrcs { get; } = new( - new[] - { - new[] - { - typeof(Orc), - typeof(OrcBomber), - typeof(OrcBrute), - typeof(OrcCaptain), - typeof(OrcishLord), - typeof(OrcishMage), - typeof(SpawnedOrcishLord) - }, - new[] - { - typeof(Savage), - typeof(SavageRider), - typeof(SavageRidgeback), - typeof(SavageShaman) - } - } - ); - - public static OppositionGroup FeyAndUndead { get; } = new( - new[] - { - new[] - { - typeof(Centaur), - typeof(EtherealWarrior), - typeof(Kirin), - typeof(LordOaks), - typeof(Pixie), - typeof(Silvani), - typeof(Unicorn), - typeof(Wisp), - typeof(Treefellow), - typeof(MLDryad), - typeof(Satyr) - }, - new[] - { - typeof(AncientLich), - typeof(Bogle), - typeof(LichLord), - typeof(Shade), - typeof(Spectre), - typeof(Wraith), - typeof(BoneKnight), - typeof(Ghoul), - typeof(Mummy), - typeof(SkeletalKnight), - typeof(Skeleton), - typeof(Zombie), - typeof(ShadowKnight), - typeof(DarknightCreeper), - typeof(RevenantLion), - typeof(LadyOfTheSnow), - typeof(RottingCorpse), - typeof(SkeletalDragon), - typeof(Lich) - } - } - ); - - public bool IsEnemy(object from, object target) + public int IndexOf(object obj) + { + if (obj == null) { - var fromGroup = IndexOf(from); - var targGroup = IndexOf(target); - - return fromGroup != -1 && targGroup != -1 && fromGroup != targGroup; - } - - public int IndexOf(object obj) - { - if (obj == null) - { - return -1; - } - - var type = obj.GetType(); - - for (var i = 0; i < m_Types.Length; ++i) - { - var group = m_Types[i]; - - var contains = false; - - for (var j = 0; !contains && j < group.Length; ++j) - { - contains = group[j].IsAssignableFrom(type); - } - - if (contains) - { - return i; - } - } - return -1; } + + var type = obj.GetType(); + + for (var i = 0; i < _types.Length; ++i) + { + var group = _types[i]; + + var contains = false; + + for (var j = 0; !contains && j < group.Length; ++j) + { + contains = group[j].IsAssignableFrom(type); + } + + if (contains) + { + return i; + } + } + + return -1; } } diff --git a/Projects/UOContent/Mobiles/AI/PredatorAI.cs b/Projects/UOContent/Mobiles/AI/PredatorAI.cs index 7532595df..5e0e01520 100644 --- a/Projects/UOContent/Mobiles/AI/PredatorAI.cs +++ b/Projects/UOContent/Mobiles/AI/PredatorAI.cs @@ -8,21 +8,15 @@ public class PredatorAI : BaseAI public override bool DoActionWander() { - if (m_Mobile.Combatant != null) + if (Mobile.Combatant != null) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I am hurt or being attacked, I kill him"); - } + DebugSay("I am hurt or being attacked, I kill him"); Action = ActionType.Combat; } - else if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, true, false, true)) + else if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, true, false, true)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("There is something near, I go away"); - } + DebugSay("There is something near, I go away"); Action = ActionType.Backoff; } @@ -36,45 +30,33 @@ public class PredatorAI : BaseAI public override bool DoActionCombat() { - var combatant = m_Mobile.Combatant; + var combatant = Mobile.Combatant; - if (combatant == null || combatant.Deleted || combatant.Map != m_Mobile.Map || !combatant.Alive || + if (combatant == null || combatant.Deleted || combatant.Map != Mobile.Map || !combatant.Alive || combatant.IsDeadBondedPet) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("My combatant is gone, so my guard is up"); - } + DebugSay("My combatant is gone, so my guard is up"); Action = ActionType.Guard; return true; } - if (!WalkMobileRange(combatant, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight)) + if (!WalkMobileRange(combatant, 1, false, Mobile.RangeFight, Mobile.RangeFight)) { - if (m_Mobile.GetDistanceToSqrt(combatant) > m_Mobile.RangePerception + 1) + if (Mobile.GetDistanceToSqrt(combatant) > Mobile.RangePerception + 1) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I cannot find {combatant.Name}"); - } + this.DebugSayFormatted($"I cannot find {combatant.Name}"); Action = ActionType.Wander; return true; } - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I should be closer to {combatant.Name}"); - } + this.DebugSayFormatted($"I should be closer to {combatant.Name}"); } - if (m_Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) + if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I used my abilities on {combatant.Name}!"); - } + this.DebugSayFormatted($"I used my abilities on {combatant.Name}!"); } return true; @@ -82,28 +64,22 @@ public class PredatorAI : BaseAI public override bool DoActionBackoff() { - if (m_Mobile.IsHurt() || m_Mobile.Combatant != null) + if (Mobile.IsHurt() || Mobile.Combatant != null) { Action = ActionType.Combat; } - else if (AcquireFocusMob(m_Mobile.RangePerception * 2, FightMode.Closest, true, false, true)) + else if (AcquireFocusMob(Mobile.RangePerception * 2, FightMode.Closest, true, false, true)) { - if (WalkMobileRange(m_Mobile.FocusMob, 1, false, m_Mobile.RangePerception, m_Mobile.RangePerception * 2)) + if (WalkMobileRange(Mobile.FocusMob, 1, false, Mobile.RangePerception, Mobile.RangePerception * 2)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("Well, here I am safe"); - } + DebugSay("Well, here I am safe"); Action = ActionType.Wander; } } else { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I have lost my focus, lets relax"); - } + DebugSay("I have lost my focus, lets relax"); Action = ActionType.Wander; } diff --git a/Projects/UOContent/Mobiles/AI/ThiefAI.cs b/Projects/UOContent/Mobiles/AI/ThiefAI.cs index c81e5be6e..2d67de801 100644 --- a/Projects/UOContent/Mobiles/AI/ThiefAI.cs +++ b/Projects/UOContent/Mobiles/AI/ThiefAI.cs @@ -5,7 +5,7 @@ namespace Server.Mobiles; public class ThiefAI : BaseAI { - private Item m_toDisarm; + private Item _toDisarm; public ThiefAI(BaseCreature m) : base(m) { @@ -13,19 +13,13 @@ public class ThiefAI : BaseAI public override bool DoActionWander() { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I have no combatant"); - } + DebugSay("I have no combatant"); - if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name}, attacking"); - } + this.DebugSayFormatted($"I have detected {Mobile.FocusMob.Name}, attacking"); - m_Mobile.Combatant = m_Mobile.FocusMob; + Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; } else @@ -38,58 +32,46 @@ public class ThiefAI : BaseAI public override bool DoActionCombat() { - var combatant = m_Mobile.Combatant; + var combatant = Mobile.Combatant; - if (combatant == null || combatant.Deleted || combatant.Map != m_Mobile.Map || !combatant.Alive || + if (combatant == null || combatant.Deleted || combatant.Map != Mobile.Map || !combatant.Alive || combatant.IsDeadBondedPet) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("My combatant is gone, so my guard is up"); - } + DebugSay("My combatant is gone, so my guard is up"); Action = ActionType.Guard; return true; } - if (!WalkMobileRange(combatant, 1, true, m_Mobile.RangeFight, m_Mobile.RangeFight)) + if (!WalkMobileRange(combatant, 1, false, Mobile.RangeFight, Mobile.RangeFight)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I should be closer to {combatant.Name}"); - } + this.DebugSayFormatted($"I should be closer to {combatant.Name}"); } else { - if (m_toDisarm?.IsChildOf(m_Mobile.Backpack) != false) + if (_toDisarm?.IsChildOf(Mobile.Backpack) != false) { - m_toDisarm = combatant.FindItemOnLayer(Layer.OneHanded) ?? combatant.FindItemOnLayer(Layer.TwoHanded); + _toDisarm = combatant.FindItemOnLayer(Layer.OneHanded) ?? combatant.FindItemOnLayer(Layer.TwoHanded); } - if (!Core.AOS && !m_Mobile.DisarmReady && m_Mobile.Skills.Wrestling.Value >= 80.0 && - m_Mobile.Skills.ArmsLore.Value >= 80.0 && m_toDisarm != null) + if (!Core.AOS && !Mobile.DisarmReady && Mobile.Skills.Wrestling.Value >= 80.0 && + Mobile.Skills.ArmsLore.Value >= 80.0 && _toDisarm != null) { - Fists.DisarmRequest(m_Mobile); + Fists.DisarmRequest(Mobile); } - if (m_toDisarm?.IsChildOf(combatant.Backpack) == true && - Core.TickCount - m_Mobile.NextSkillTime >= 0 && m_toDisarm.LootType != LootType.Blessed && - m_toDisarm.LootType != LootType.Newbied) + if (_toDisarm?.IsChildOf(combatant.Backpack) == true && + Core.TickCount - Mobile.NextSkillTime >= 0 && _toDisarm.LootType != LootType.Blessed && + _toDisarm.LootType != LootType.Newbied) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("Trying to steal from combatant."); - } + DebugSay("Trying to steal from combatant."); - m_Mobile.UseSkill(SkillName.Stealing); - m_Mobile.Target?.Invoke(m_Mobile, m_toDisarm); + Mobile.UseSkill(SkillName.Stealing); + Mobile.Target?.Invoke(Mobile, _toDisarm); } - else if (m_toDisarm == null && Core.TickCount - m_Mobile.NextSkillTime >= 0) + else if (_toDisarm == null && Core.TickCount - Mobile.NextSkillTime >= 0) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"Trying to steal from {combatant.Name}."); - } + this.DebugSayFormatted($"Trying to steal from {combatant.Name}."); bool didSteal = TryStealFrom(combatant); didSteal = TryStealFrom(combatant) || didSteal; @@ -98,10 +80,7 @@ public class ThiefAI : BaseAI if (!didSteal) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I am going to flee from {combatant.Name}"); - } + this.DebugSayFormatted($"I am going to flee from {combatant.Name}"); Action = ActionType.Flee; return true; @@ -111,16 +90,13 @@ public class ThiefAI : BaseAI // We are low on health, should we flee? // (10 + diff)% chance to flee - if (m_Mobile.Hits < m_Mobile.HitsMax * 20 / 100 && m_Mobile.CanFlee) + if (Mobile.Hits < Mobile.HitsMax * 20 / 100 && Mobile.CanFlee) { - var fleeChance = 10 + Math.Max(0, combatant.Hits - m_Mobile.Hits); + var fleeChance = 10 + Math.Max(0, combatant.Hits - Mobile.Hits); if (Utility.Random(0, 100) > fleeChance) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I am going to flee from {combatant.Name}"); - } + this.DebugSayFormatted($"I am going to flee from {combatant.Name}"); Action = ActionType.Flee; } @@ -128,12 +104,9 @@ public class ThiefAI : BaseAI return true; } - if (m_Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, m_Mobile.Combatant)) + if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, Mobile.Combatant)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I used my abilities on {m_Mobile.Combatant.Name}!"); - } + this.DebugSayFormatted($"I used my abilities on {Mobile.Combatant.Name}!"); } return true; } @@ -143,8 +116,8 @@ public class ThiefAI : BaseAI Item steal = combatant.Backpack?.FindItemByType(); if (steal != null) { - m_Mobile.UseSkill(SkillName.Stealing); - m_Mobile.Target?.Invoke(m_Mobile, steal); + Mobile.UseSkill(SkillName.Stealing); + Mobile.Target?.Invoke(Mobile, steal); return true; } @@ -153,14 +126,11 @@ public class ThiefAI : BaseAI public override bool DoActionGuard() { - if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) + if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name}, attacking"); - } + this.DebugSayFormatted($"I have detected {Mobile.FocusMob.Name}, attacking"); - m_Mobile.Combatant = m_Mobile.FocusMob; + Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; } else @@ -173,18 +143,15 @@ public class ThiefAI : BaseAI public override bool DoActionFlee() { - if (m_Mobile.Hits > m_Mobile.HitsMax / 2) + if (Mobile.Hits > Mobile.HitsMax / 2) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I am stronger now, so I will continue fighting"); - } + DebugSay("I am stronger now, so I will continue fighting"); Action = ActionType.Combat; } else { - m_Mobile.FocusMob = m_Mobile.Combatant; + Mobile.FocusMob = Mobile.Combatant; base.DoActionFlee(); } diff --git a/Projects/UOContent/Mobiles/AI/VendorAI.cs b/Projects/UOContent/Mobiles/AI/VendorAI.cs index 3540655b6..a835ef61f 100644 --- a/Projects/UOContent/Mobiles/AI/VendorAI.cs +++ b/Projects/UOContent/Mobiles/AI/VendorAI.cs @@ -15,38 +15,26 @@ public class VendorAI : BaseAI public override bool DoActionWander() { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("I'm fine"); - } + DebugSay("I'm fine"); - if (m_Mobile.Combatant != null) + if (Mobile.Combatant != null) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"{m_Mobile.Combatant.Name} is attacking me"); - } + this.DebugSayFormatted($"{Mobile.Combatant.Name} is attacking me"); - m_Mobile.Say(GetRandomGuardMessage()); + Mobile.Say(GetRandomGuardMessage()); Action = ActionType.Flee; } + else if (Mobile.FocusMob != null) + { + this.DebugSayFormatted($"{Mobile.FocusMob.Name} has talked to me"); + + Action = ActionType.Interact; + } else { - if (m_Mobile.FocusMob != null) - { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"{m_Mobile.FocusMob.Name} has talked to me"); - } + Mobile.Warmode = false; - Action = ActionType.Interact; - } - else - { - m_Mobile.Warmode = false; - - base.DoActionWander(); - } + base.DoActionWander(); } return true; @@ -54,50 +42,38 @@ public class VendorAI : BaseAI public override bool DoActionInteract() { - var customer = m_Mobile.FocusMob; + var customer = Mobile.FocusMob; - if (m_Mobile.Combatant != null) + if (Mobile.Combatant != null) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"{m_Mobile.Combatant.Name} is attacking me"); - } + this.DebugSayFormatted($"{Mobile.Combatant.Name} is attacking me"); - m_Mobile.Say(GetRandomGuardMessage()); + Mobile.Say(GetRandomGuardMessage()); Action = ActionType.Flee; return true; } - if (customer?.Deleted != false || customer.Map != m_Mobile.Map) + if (customer?.Deleted != false || customer.Map != Mobile.Map) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay("My customer have disapeared"); - } + DebugSay("My customer has disappeared"); - m_Mobile.FocusMob = null; + Mobile.FocusMob = null; Action = ActionType.Wander; } - else if (customer.InRange(m_Mobile, m_Mobile.RangeFight)) + else if (customer.InRange(Mobile, Mobile.RangeFight)) { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"I am with {customer.Name}"); - } + this.DebugSayFormatted($"I am with {customer.Name}"); - m_Mobile.Direction = m_Mobile.GetDirectionTo(customer); + Mobile.Direction = Mobile.GetDirectionTo(customer); } else { - if (m_Mobile.Debug) - { - m_Mobile.DebugSay($"{customer.Name} is gone"); - } + this.DebugSayFormatted($"{customer.Name} is gone"); - m_Mobile.FocusMob = null; + Mobile.FocusMob = null; Action = ActionType.Wander; } @@ -106,13 +82,13 @@ public class VendorAI : BaseAI public override bool DoActionGuard() { - m_Mobile.FocusMob = m_Mobile.Combatant; + Mobile.FocusMob = Mobile.Combatant; return base.DoActionGuard(); } public override bool HandlesOnSpeech(Mobile from) { - if (from.InRange(m_Mobile, 4)) + if (from.InRange(Mobile, 4)) { return true; } @@ -127,7 +103,7 @@ public class VendorAI : BaseAI var from = e.Mobile; - if (m_Mobile is BaseVendor vendor && from.InRange(m_Mobile, Core.AOS ? 1 : 4) && !e.Handled) + if (Mobile is BaseVendor vendor && from.InRange(Mobile, Core.AOS ? 1 : 4) && !e.Handled) { if (e.HasKeyword(0x14D)) // *vendor sell* { diff --git a/Projects/UOContent/Mobiles/BaseCreature.cs b/Projects/UOContent/Mobiles/BaseCreature.cs index 22c43ebc4..eb70e8454 100644 --- a/Projects/UOContent/Mobiles/BaseCreature.cs +++ b/Projects/UOContent/Mobiles/BaseCreature.cs @@ -56,11 +56,12 @@ namespace Server.Mobiles Attack, // "(All/Name) kill", // "(All/Name) attack" All or the specified pet(s) currently under your control attack the target. - Patrol, // "(Name) patrol" Roves between two or more guarded targets. - Release, // "(Name) release" Releases pet back into the wild (removes "tame" status). - Stay, // "(All/Name) stay" All or the specified pet(s) will stop and stay in current spot. - Stop, // "(All/Name) stop Cancels any current orders to attack, guard or follow. - Transfer // "(Name) transfer" Transfers complete ownership to targeted player. + Patrol, // "(Name) patrol" Roves between two or more guarded targets. + Release, // "(Name) release" Releases pet back into the wild (removes "tame" status). + Stay, // "(All/Name) stay" All or the specified pet(s) will stop and stay in current spot. + Stop, // "(All/Name) stop Cancels any current orders to attack, guard or follow. + Transfer, // "(Name) transfer" Transfers complete ownership to targeted player. + Rename // "(Name) rename" Changes the name of the pet. } [Flags] @@ -132,30 +133,6 @@ namespace Server.Mobiles public int CompareTo(DamageStore ds) => (ds?.m_Damage ?? 0).CompareTo(m_Damage); } - [AttributeUsage(AttributeTargets.Class)] - public class FriendlyNameAttribute : Attribute - { - public FriendlyNameAttribute(TextDefinition friendlyName) => FriendlyName = friendlyName; - // future use: Talisman 'Protection/Bonus vs. Specific Creature - - public TextDefinition FriendlyName { get; } - - public static TextDefinition GetFriendlyNameFor(Type t) - { - if (t.IsDefined(typeof(FriendlyNameAttribute), false)) - { - var objs = t.GetCustomAttributes(typeof(FriendlyNameAttribute), false); - - if (objs.Length > 0) - { - return (objs[0] as FriendlyNameAttribute)?.FriendlyName ?? ""; - } - } - - return t.Name; - } - } - public abstract partial class BaseCreature : Mobile, IHonorTarget, IQuestGiver { public enum Allegiance @@ -364,7 +341,11 @@ namespace Server.Mobiles FightMode = mode; - ResetSpeeds(); + GetSpeeds(out var activeSpeed, out var passiveSpeed); + + ActiveSpeed = activeSpeed; + PassiveSpeed = passiveSpeed; + CurrentSpeed = passiveSpeed; m_Team = 0; @@ -926,8 +907,6 @@ namespace Server.Mobiles public virtual bool ReturnsToHome => SeeksHome && Home != Point3D.Zero && !m_ReturnQueued && !Controlled && !Summoned; - public virtual bool ScaleSpeedByDex => NPCSpeeds.ScaleSpeedByDex && !IsMonster; - // used for deleting untamed creatures [in houses] [CommandProperty(AccessLevel.GameMaster)] public bool RemoveIfUntamed { get; set; } @@ -1517,15 +1496,6 @@ namespace Server.Mobiles } } - public override void OnRawDexChange(int oldValue) - { - // This only really happens for pets or when a GM modifies a mob. - if (oldValue != RawDex && ScaleSpeedByDex) - { - ResetSpeeds(); - } - } - public override void OnBeforeSpawn(Point3D location, Map m) { if (Paragon.CheckConvert(this, location, m)) @@ -2258,7 +2228,7 @@ namespace Server.Mobiles public void ChangeAIType(AIType newAI) { - AIObject?.m_Timer.Stop(); + AIObject?._timer.Stop(); if (ForcedAI != null) { @@ -2381,7 +2351,7 @@ namespace Server.Mobiles { if (AIObject != null) { - AIObject.m_Timer?.Stop(); + AIObject._timer?.Stop(); AIObject = null; } @@ -2414,13 +2384,6 @@ namespace Server.Mobiles base.OnAfterDelete(); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void DebugSay(string text) - { - // Moved the debug check to implementation layer so we can avoid string formatting when we do not need it - PublicOverheadMessage(MessageType.Regular, 41, false, text); - } - /* * This function can be overridden.. so a "Strongest" mobile, can have a different definition depending * on who check for value @@ -2439,7 +2402,7 @@ namespace Server.Mobiles { FightMode.Strongest => m.Skills.Tactics.Value + m.Str, // returns strongest mobile FightMode.Weakest => -m.Hits, // returns weakest mobile - _ => -GetDistanceToSqrt(m) + _ => -this.GetDistanceToSqrt(m) }; } @@ -2461,7 +2424,7 @@ namespace Server.Mobiles public bool IsHurt() => Hits != HitsMax; - public double GetHomeDistance() => GetDistanceToSqrt(m_Home); + public double GetHomeDistance() => this.GetDistanceToSqrt(m_Home); public virtual int GetTeamSize(int iRange) { @@ -2498,7 +2461,7 @@ namespace Server.Mobiles } else { - DebugSay("I'm being attacked but my master told me not to fight."); + AIObject.DebugSay("I'm being attacked but my master told me not to fight."); Warmode = false; return; } @@ -2663,13 +2626,10 @@ namespace Server.Mobiles if (ai.Defender == target) { - if (m_ControlMaster?.Player == true && m_ControlMaster.CanBeHarmful(target, false)) + var master = GetMaster(); + if (master?.Player == true && master.CanBeHarmful(target, false)) { - m_ControlMaster.DoHarmful(target, true); - } - else if (m_SummonMaster?.Player == true && m_SummonMaster.CanBeHarmful(target, false)) - { - m_SummonMaster.DoHarmful(target, true); + master.DoHarmful(target, true); } return; @@ -2716,19 +2676,7 @@ namespace Server.Mobiles if (Body.IsHuman) { - switch (Utility.Random(2)) - { - case 0: - { - CheckedAnimate(5, 5, 1, true, true, 1); - break; - } - case 1: - { - CheckedAnimate(6, 5, 1, true, false, 1); - break; - } - } + CheckedAnimate(Utility.RandomBool() ? 5 : 6, 5, 1, true, false, 1); } else if (Body.IsAnimal) { @@ -2753,19 +2701,7 @@ namespace Server.Mobiles } else if (Body.IsMonster) { - switch (Utility.Random(2)) - { - case 0: - { - CheckedAnimate(17, 5, 1, true, false, 1); - break; - } - case 1: - { - CheckedAnimate(18, 5, 1, true, false, 1); - break; - } - } + CheckedAnimate(Utility.RandomBool() ? 17 : 18, 5, 1, true, false, 1); } PlaySound(GetIdleSound()); @@ -3543,7 +3479,6 @@ namespace Server.Mobiles } Guild = null; - ResetSpeeds(); Delta(MobileDelta.Noto); @@ -4933,15 +4868,6 @@ namespace Server.Mobiles NPCSpeeds.GetSpeeds(this, out activeSpeed, out passiveSpeed); } - public void ResetSpeeds(bool currentUseActive = false) - { - GetSpeeds(out var activeSpeed, out var passiveSpeed); - - ActiveSpeed = activeSpeed; - PassiveSpeed = passiveSpeed; - CurrentSpeed = currentUseActive ? activeSpeed : passiveSpeed; - } - public virtual void DropBackpack() { if (Backpack?.Items.Count > 0) diff --git a/Projects/UOContent/Mobiles/Familiars/BaseFamiliar.cs b/Projects/UOContent/Mobiles/Familiars/BaseFamiliar.cs index 3997a3856..3ecc94db2 100644 --- a/Projects/UOContent/Mobiles/Familiars/BaseFamiliar.cs +++ b/Projects/UOContent/Mobiles/Familiars/BaseFamiliar.cs @@ -93,7 +93,7 @@ public abstract partial class BaseFamiliar : BaseCreature Hidden = m_LastHidden = master.Hidden; } - if (AIObject?.WalkMobileRange(master, 5, true, 1, 1) == true) + if (AIObject?.WalkMobileRange(master, 5, false, 1, 1) == true) { Warmode = master.Warmode; Combatant = master.Combatant; diff --git a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/SavageShaman.cs b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/SavageShaman.cs index d1994f82d..f25f03497 100644 --- a/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/SavageShaman.cs +++ b/Projects/UOContent/Mobiles/Monsters/Humanoid/Magic/SavageShaman.cs @@ -228,7 +228,7 @@ namespace Server.Mobiles var total = Skills.Magery.Value + Skills.Poisoning.Value; - var dist = GetDistanceToSqrt(m); + var dist = this.GetDistanceToSqrt(m); if (dist >= 3.0) { diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/AnimatedWeapon.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/AnimatedWeapon.cs index 9f486c679..6edf6927b 100644 --- a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/AnimatedWeapon.cs +++ b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/AnimatedWeapon.cs @@ -86,7 +86,7 @@ namespace Server.Mobiles public override Poison PoisonImmune => Poison.Lethal; public override double GetFightModeRanking(Mobile m, FightMode acqType, bool bPlayerOnly) => - m.Str / Math.Max(GetDistanceToSqrt(m), 1.0); + m.Str / Math.Max(this.GetDistanceToSqrt(m), 1.0); public override int GetAngerSound() => 0x23A; diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/BladeSpirits.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/BladeSpirits.cs index 100e115d7..a0966eaf4 100644 --- a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/BladeSpirits.cs +++ b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/BladeSpirits.cs @@ -58,7 +58,7 @@ namespace Server.Mobiles public override bool FollowsAcquireRules => Core.AOS || !Summoned || SummonMaster?.Player != true || Map != Map.Felucca; public override double GetFightModeRanking(Mobile m, FightMode acqType, bool bPlayerOnly) => - (m.Str + m.Skills.Tactics.Value) / Math.Max(GetDistanceToSqrt(m), 1.0); + (m.Str + m.Skills.Tactics.Value) / Math.Max(this.GetDistanceToSqrt(m), 1.0); public override int GetAngerSound() => 0x23A; diff --git a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/EnergyVortex.cs b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/EnergyVortex.cs index b94d2650c..3bb2e69fa 100644 --- a/Projects/UOContent/Mobiles/Monsters/Misc/Melee/EnergyVortex.cs +++ b/Projects/UOContent/Mobiles/Monsters/Misc/Melee/EnergyVortex.cs @@ -66,7 +66,7 @@ namespace Server.Mobiles public override bool FollowsAcquireRules => Core.AOS || !Summoned || SummonMaster?.Player != true || Map != Map.Felucca; public override double GetFightModeRanking(Mobile m, FightMode acqType, bool bPlayerOnly) => - (m.Int + m.Skills.Magery.Value) / Math.Max(GetDistanceToSqrt(m), 1.0); + (m.Int + m.Skills.Magery.Value) / Math.Max(this.GetDistanceToSqrt(m), 1.0); public override int GetAngerSound() => 0x15; diff --git a/Projects/UOContent/Mobiles/NPCSpeeds.cs b/Projects/UOContent/Mobiles/NPCSpeeds.cs index 3a2bc201e..a61ce70c6 100644 --- a/Projects/UOContent/Mobiles/NPCSpeeds.cs +++ b/Projects/UOContent/Mobiles/NPCSpeeds.cs @@ -22,32 +22,12 @@ public static class NPCSpeeds private static readonly Dictionary _speedsByType = new(); private static readonly Dictionary _speedsByLevel = new(); - // Enabled for pets on HS+ - public static bool ScaleSpeedByDex { get; private set; } - public static double MinDelay { get; private set; } - public static double MaxDelay { get; private set; } - public static int MinDex { get; private set; } - public static int MaxDex { get; private set; } - // Time period to lock NPCs into idling public static int MinIdleSeconds { get; private set; } public static int MaxIdleSeconds { get; private set; } public static void GetSpeeds(BaseCreature bc, out double activeSpeed, out double passiveSpeed) { - // Used for scaling pet's speed by dex in HS+ - if (bc.ScaleSpeedByDex) - { - var maxDex = MaxDex; - double min = MinDelay; - double max = MaxDelay; - var dex = Math.Clamp(bc.Dex, MinDex, maxDex); - - activeSpeed = Math.Max(max - (max - min) * ((double)dex / maxDex), min); - passiveSpeed = activeSpeed * 2; - return; - } - if ((bc.SpeedClass == SpeedLevel.None || !_speedsByLevel.TryGetValue(bc.SpeedClass, out var sp)) && !_speedsByType.TryGetValue(bc.GetType(), out sp)) { @@ -70,11 +50,6 @@ public static class NPCSpeeds public static void Configure() { - ScaleSpeedByDex = ServerConfiguration.GetSetting("movement.delay.scaleSpeedByDex", Core.HS); - MinDelay = ServerConfiguration.GetSetting("movement.delay.npcMinDelay", 0.1); - MaxDelay = ServerConfiguration.GetSetting("movement.delay.npcMaxDelay", 0.4); - MaxDex = ServerConfiguration.GetSetting("movement.delay.npcMinDex", 50); - MaxDex = ServerConfiguration.GetSetting("movement.delay.npcMaxDex", 200); MinIdleSeconds = ServerConfiguration.GetSetting("movement.delay.npcMinIdle", 15); MaxIdleSeconds = ServerConfiguration.GetSetting("movement.delay.npcMaxIdle", 25); diff --git a/Projects/UOContent/Mobiles/PlayerMobile.cs b/Projects/UOContent/Mobiles/PlayerMobile.cs index fe2e6a05b..1da9656cd 100644 --- a/Projects/UOContent/Mobiles/PlayerMobile.cs +++ b/Projects/UOContent/Mobiles/PlayerMobile.cs @@ -986,67 +986,71 @@ namespace Server.Mobiles from.TargetLocked = false; } - public static void EquipMacro(Mobile m, List list) + public static void EquipMacro(Mobile m, ref PooledRefList list) { - if (m is PlayerMobile { Alive: true } pm && pm.Backpack != null) + if (m is not PlayerMobile { Alive: true } pm || pm.Backpack == null) { - var pack = pm.Backpack; + return; + } - foreach (var serial in list) + var pack = pm.Backpack; + + foreach (var serial in list) + { + Item item = null; + foreach (var i in pack.Items) { - Item item = null; - foreach (var i in pack.Items) + if (i.Serial == serial) { - if (i.Serial == serial) - { - item = i; - break; - } + item = i; + break; } + } - if (item == null) + if (item == null) + { + continue; + } + + var toMove = pm.FindItemOnLayer(item.Layer); + + if (toMove != null) + { + // pack.DropItem(toMove); + toMove.Internalize(); + + if (!pm.EquipItem(item)) { - continue; - } - - var toMove = pm.FindItemOnLayer(item.Layer); - - if (toMove != null) - { - // pack.DropItem(toMove); - toMove.Internalize(); - - if (!pm.EquipItem(item)) - { - pm.EquipItem(toMove); - } - else - { - pack.DropItem(toMove); - } + pm.EquipItem(toMove); } else { - pm.EquipItem(item); + pack.DropItem(toMove); } } + else + { + pm.EquipItem(item); + } } } - public static void UnequipMacro(Mobile m, List layers) + public static void UnequipMacro(Mobile m, ref PooledRefList layers) { - if (m is PlayerMobile { Alive: true } pm && pm.Backpack != null) + if (m is not PlayerMobile { Alive: true } pm || pm.Backpack == null) { - var pack = pm.Backpack; - var eq = m.Items; + return; + } - for (var i = eq.Count - 1; i >= 0; i--) + var pack = pm.Backpack; + var eq = m.Items; + + for (var i = eq.Count - 1; i >= 0; i--) + { + var item = eq[i]; + if (layers.Contains(item.Layer)) { - var item = eq[i]; - if (layers.Contains(item.Layer)) - { - pack.TryDropItem(pm, item, false); - } + pack.TryDropItem(pm, item, false); } } } @@ -1547,13 +1551,17 @@ namespace Server.Mobiles // Eject all from house from.RevealingAction(); - foreach (var item in context.Foundation.GetItems()) + var list = context.Foundation.GetItems(); + for (var i = 0; i < list.Count; i++) { + var item = list[i]; item.Location = context.Foundation.BanLocation; } - foreach (var mobile in context.Foundation.GetMobiles()) + using var mobiles = context.Foundation.GetMobilesPooled(); + for (var i = 0; i < mobiles.Count; i++) { + var mobile = mobiles[i]; mobile.Location = context.Foundation.BanLocation; } diff --git a/Projects/UOContent/Mobiles/Special/HarrowerTentacles.cs b/Projects/UOContent/Mobiles/Special/HarrowerTentacles.cs index 20877aa6a..04a8b79d7 100644 --- a/Projects/UOContent/Mobiles/Special/HarrowerTentacles.cs +++ b/Projects/UOContent/Mobiles/Special/HarrowerTentacles.cs @@ -11,10 +11,10 @@ public partial class HarrowerTentacles : BaseCreature [SerializableField(0)] [SerializedCommandProperty(AccessLevel.GameMaster)] - private Mobile _harrower; + private Harrower _harrower; [Constructible] - public HarrowerTentacles(Mobile harrower = null) : base(AIType.AI_Melee) + public HarrowerTentacles(Harrower harrower = null) : base(AIType.AI_Melee) { _harrower = harrower; Body = 129; @@ -101,6 +101,7 @@ public partial class HarrowerTentacles : BaseCreature _timer = null; base.OnAfterDelete(); + Harrower?.RemoveFromTentacles(this); } private class DrainTimer : Timer diff --git a/Projects/UOContent/Mobiles/Vendors/PlayerBarkeeper.cs b/Projects/UOContent/Mobiles/Vendors/PlayerBarkeeper.cs index a3af3d31b..3b63a8cfe 100644 --- a/Projects/UOContent/Mobiles/Vendors/PlayerBarkeeper.cs +++ b/Projects/UOContent/Mobiles/Vendors/PlayerBarkeeper.cs @@ -984,9 +984,9 @@ namespace Server.Mobiles var rumor = rumors[i]; builder.AddHtml(100, 70 + i * 120, 50, 20, "Message"); - builder.AddHtml(100, 90 + i * 120, 450, 40, rumor == null ? "No current message" : rumor.Message, true); + builder.AddHtml(100, 90 + i * 120, 450, 40, rumor?.Message ?? "No current message", background: true); builder.AddHtmlLocalized(100, 130 + i * 120, 50, 20, 1078361); // Keyword - builder.AddHtml(100, 150 + i * 120, 450, 40, rumor == null ? "None" : rumor.Keyword, true); + builder.AddHtml(100, 150 + i * 120, 450, 40, rumor?.Keyword ?? "None", background: true); builder.AddButton(60, 90 + i * 120, 4005, 4007, GetButtonID(1, i)); } @@ -1010,9 +1010,9 @@ namespace Server.Mobiles var rumor = rumors[i]; builder.AddHtml(100, 70 + i * 120, 50, 20, "Message"); - builder.AddHtml(100, 90 + i * 120, 450, 40, rumor == null ? "No current message" : rumor.Message, true); + builder.AddHtml(100, 90 + i * 120, 450, 40, rumor?.Message ?? "No current message", background: true); builder.AddHtmlLocalized(100, 130 + i * 120, 50, 20, 1078361); // Keyword - builder.AddHtml(100, 150 + i * 120, 450, 40, rumor == null ? "None" : rumor.Keyword, true); + builder.AddHtml(100, 150 + i * 120, 450, 40, rumor?.Keyword ?? "None", background: true); builder.AddButton(60, 90 + i * 120, 4005, 4007, GetButtonID(2, i)); } @@ -1031,7 +1031,7 @@ namespace Server.Mobiles builder.AddHtmlLocalized(250, 95, 500, 20, 1078363); // Change this tip message builder.AddHtml(100, 190, 50, 20, "Message"); - builder.AddHtml(100, 210, 450, 40, _barkeeper.TipMessage ?? "No current message", true); + builder.AddHtml(100, 210, 450, 40, _barkeeper.TipMessage ?? "No current message", background: true); builder.AddButton(60, 210, 4005, 4007, GetButtonID(3, 0)); @@ -1047,7 +1047,7 @@ namespace Server.Mobiles builder.AddHtmlLocalized(250, 95, 500, 20, 1078364); // Remove this tip message builder.AddHtml(100, 190, 50, 20, "Message"); - builder.AddHtml(100, 210, 450, 40, _barkeeper.TipMessage ?? "No current message", true); + builder.AddHtml(100, 210, 450, 40, _barkeeper.TipMessage ?? "No current message", background: true); builder.AddButton(60, 210, 4005, 4007, GetButtonID(4, 0)); diff --git a/Projects/UOContent/Mobiles/Vendors/VendorBackpack.cs b/Projects/UOContent/Mobiles/Vendors/VendorBackpack.cs index 267fbcd1a..06f0e6b62 100644 --- a/Projects/UOContent/Mobiles/Vendors/VendorBackpack.cs +++ b/Projects/UOContent/Mobiles/Vendors/VendorBackpack.cs @@ -12,12 +12,9 @@ namespace Server.Mobiles; [SerializationGenerator(0, false)] public partial class VendorBackpack : Backpack { - public VendorBackpack() - { - Layer = Layer.Backpack; - Weight = 1.0; - } + public VendorBackpack() => Layer = Layer.Backpack; + public override double DefaultWeight => 1.0; public override int DefaultMaxWeight => 0; public override bool CheckHold(Mobile m, Item item, bool message, bool checkItems, int plusItems, int plusWeight) diff --git a/Projects/UOContent/Multis/Boats/BaseBoatDeed.cs b/Projects/UOContent/Multis/Boats/BaseBoatDeed.cs index 6838ec755..9bd31c0e7 100644 --- a/Projects/UOContent/Multis/Boats/BaseBoatDeed.cs +++ b/Projects/UOContent/Multis/Boats/BaseBoatDeed.cs @@ -18,8 +18,6 @@ public abstract partial class BaseBoatDeed : Item public BaseBoatDeed(int id, Point3D offset) : base(0x14F2) { - Weight = 1.0; - if (!Core.AOS) { LootType = LootType.Newbied; @@ -29,6 +27,8 @@ public abstract partial class BaseBoatDeed : Item Offset = offset; } + public override double DefaultWeight => 1.0; + public abstract BaseBoat Boat { get; } public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Multis/Boats/BaseDockedBoat.cs b/Projects/UOContent/Multis/Boats/BaseDockedBoat.cs index 7b638801a..69ddadd88 100644 --- a/Projects/UOContent/Multis/Boats/BaseDockedBoat.cs +++ b/Projects/UOContent/Multis/Boats/BaseDockedBoat.cs @@ -23,7 +23,6 @@ public abstract partial class BaseDockedBoat : Item public BaseDockedBoat(int id, Point3D offset, BaseBoat boat) : base(0x14F4) { - Weight = 1.0; LootType = LootType.Blessed; _multiId = id; @@ -32,6 +31,8 @@ public abstract partial class BaseDockedBoat : Item _shipName = boat.ShipName; } + public override double DefaultWeight => 1.0; + public abstract BaseBoat Boat { get; } public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Multis/Camps/BaseCamp.cs b/Projects/UOContent/Multis/Camps/BaseCamp.cs index cd0354859..e3d10dd0b 100644 --- a/Projects/UOContent/Multis/Camps/BaseCamp.cs +++ b/Projects/UOContent/Multis/Camps/BaseCamp.cs @@ -23,6 +23,7 @@ public abstract partial class BaseCamp : BaseMulti private TimeSpan _decayDelay; private Timer _decayTimer; + private Timer _initTimer; public BaseCamp(int multiID) : base(multiID) { @@ -31,7 +32,7 @@ public abstract partial class BaseCamp : BaseMulti _decayDelay = TimeSpan.FromMinutes(30.0); RefreshDecay(true); - Timer.StartTimer(CheckAddComponents); + _initTimer = Timer.DelayCall(TimeSpan.Zero, CheckAddComponents); } public virtual int EventRange => 10; @@ -50,6 +51,8 @@ public abstract partial class BaseCamp : BaseMulti public void CheckAddComponents() { + _initTimer = null; + if (Deleted) { return; @@ -138,16 +141,16 @@ public abstract partial class BaseCamp : BaseMulti for (var i = 0; i < _items.Count; ++i) { - _items[i].Delete(); + _items[i]?.Delete(); } for (var i = 0; i < _mobiles.Count; ++i) { var mob = _mobiles[i]; - if (mob.CantWalk || (mob as BaseCreature)?.IsPrisoner == false) + if (mob != null && (mob.CantWalk || (mob as BaseCreature)?.IsPrisoner == false)) { - _mobiles[i].Delete(); + mob.Delete(); } } @@ -156,6 +159,9 @@ public abstract partial class BaseCamp : BaseMulti _decayTimer?.Stop(); _decayTimer = null; + + _initTimer?.Stop(); + _initTimer = null; } private void Deserialize(IGenericReader reader, int version) @@ -168,7 +174,20 @@ public abstract partial class BaseCamp : BaseMulti [AfterDeserialization] private void AfterDeserialization() { - RefreshDecay(false); + var remaining = _decayTime - Core.Now; + + if (remaining > TimeSpan.Zero) + { + _decayDelay = remaining; + RefreshDecay(false); + } + else + { + Timer.DelayCall(TimeSpan.Zero, Delete); + return; + } + + _initTimer = Timer.DelayCall(TimeSpan.Zero, CheckAddComponents); } } @@ -176,5 +195,9 @@ public abstract partial class BaseCamp : BaseMulti public partial class LockableBarrel : LockableContainer { [Constructible] - public LockableBarrel() : base(0xE77) => Weight = 1.0; + public LockableBarrel() : base(0xE77) + { + } + + public override double DefaultWeight => 1.0; } diff --git a/Projects/UOContent/Multis/Deeds.cs b/Projects/UOContent/Multis/Deeds.cs index 1120a0b55..e4ed5c202 100644 --- a/Projects/UOContent/Multis/Deeds.cs +++ b/Projects/UOContent/Multis/Deeds.cs @@ -54,7 +54,6 @@ namespace Server.Multis.Deeds { public HouseDeed(int id, Point3D offset) : base(0x14F0) { - Weight = 1.0; LootType = LootType.Newbied; MultiID = id; @@ -75,6 +74,8 @@ namespace Server.Multis.Deeds public abstract Rectangle2D[] Area { get; } + public override double DefaultWeight => 1.0; + public override void Serialize(IGenericWriter writer) { base.Serialize(writer); diff --git a/Projects/UOContent/Multis/Houses/BaseHouse.cs b/Projects/UOContent/Multis/Houses/BaseHouse.cs index f1d3954d9..4be71dc51 100644 --- a/Projects/UOContent/Multis/Houses/BaseHouse.cs +++ b/Projects/UOContent/Multis/Houses/BaseHouse.cs @@ -1235,24 +1235,24 @@ namespace Server.Multis return list; } - public List GetMobiles() + public PooledRefList GetMobilesPooled() { if (Map == null || Map == Map.Internal) { - return new List(); + return PooledRefList.Create(0); } - var list = new List(); - - foreach (var mobile in Region.GetMobiles()) + var mobileList = Region.GetMobilesPooled(); + for (var i = mobileList.Count - 1; i >= 0; i--) { - if (IsInside(mobile)) + var mobile = mobileList[i]; + if (!IsInside(mobile)) { - list.Add(mobile); + mobileList.Remove(mobile); } } - return list; + return mobileList; } public virtual bool CheckAosLockdowns(int need) => GetAosCurLockdowns() + need <= GetAosMaxLockdowns(); diff --git a/Projects/UOContent/Multis/Houses/HouseFoundation.cs b/Projects/UOContent/Multis/Houses/HouseFoundation.cs index 46be9fdfa..2c12373e0 100644 --- a/Projects/UOContent/Multis/Houses/HouseFoundation.cs +++ b/Projects/UOContent/Multis/Houses/HouseFoundation.cs @@ -901,8 +901,10 @@ namespace Server.Multis item.Location = BanLocation; } - foreach (var mobile in GetMobiles()) + using var mobiles = GetMobilesPooled(); + for (var i = 0; i < mobiles.Count; i++) { + var mobile = mobiles[i]; if (mobile != m) { mobile.Location = BanLocation; @@ -1294,13 +1296,18 @@ namespace Server.Multis // Eject all from house from.RevealingAction(); - foreach (var item in GetItems()) + var items = GetItems(); + for (var i = 0; i < items.Count; i++) { + var item = items[i]; item.Location = BanLocation; } - foreach (var mobile in GetMobiles()) + using var mobiles = GetMobilesPooled(); + var list = GetMobilesPooled(); + for (var i = 0; i < list.Count; i++) { + var mobile = list[i]; mobile.Location = BanLocation; } @@ -1760,13 +1767,17 @@ namespace Server.Multis // Eject all from house from.RevealingAction(); - foreach (var item in context.Foundation.GetItems()) + var list = context.Foundation.GetItems(); + for (var i = 0; i < list.Count; i++) { + var item = list[i]; item.Location = context.Foundation.BanLocation; } - foreach (var mobile in context.Foundation.GetMobiles()) + using var mobiles = context.Foundation.GetMobilesPooled(); + for (var i = 0; i < mobiles.Count; i++) { + var mobile = mobiles[i]; mobile.Location = context.Foundation.BanLocation; } diff --git a/Projects/UOContent/Multis/Houses/HousePlacementTool.cs b/Projects/UOContent/Multis/Houses/HousePlacementTool.cs index be254b01c..31007b322 100644 --- a/Projects/UOContent/Multis/Houses/HousePlacementTool.cs +++ b/Projects/UOContent/Multis/Houses/HousePlacementTool.cs @@ -14,12 +14,9 @@ namespace Server.Items; public partial class HousePlacementTool : Item { [Constructible] - public HousePlacementTool() : base(0x14F6) - { - Weight = 3.0; - LootType = LootType.Blessed; - } + public HousePlacementTool() : base(0x14F6) => LootType = LootType.Blessed; + public override double DefaultWeight => 3.0; public override int LabelNumber => 1060651; // a house placement tool public override void OnDoubleClick(Mobile from) diff --git a/Projects/UOContent/Network/Packets/IncomingItemPackets.cs b/Projects/UOContent/Network/Packets/IncomingItemPackets.cs index f2ad7fd8f..db47dbb31 100644 --- a/Projects/UOContent/Network/Packets/IncomingItemPackets.cs +++ b/Projects/UOContent/Network/Packets/IncomingItemPackets.cs @@ -14,8 +14,8 @@ *************************************************************************/ using System.Buffers; -using System.Collections.Generic; using System.IO; +using Server.Collections; using Server.Items; using Server.Mobiles; @@ -112,24 +112,25 @@ public static class IncomingItemPackets public static void EquipMacro(NetState state, SpanReader reader) { int count = reader.ReadByte(); - var serialList = new List(count); + var serialList = PooledRefList.Create(count); for (var i = 0; i < count; ++i) { serialList.Add((Serial)reader.ReadUInt32()); } - PlayerMobile.EquipMacro(state.Mobile, serialList); + PlayerMobile.EquipMacro(state.Mobile, ref serialList); + serialList.Dispose(); } public static void UnequipMacro(NetState state, SpanReader reader) { int count = reader.ReadByte(); - var layers = new List(count); + var layers = PooledRefList.Create(count); for (var i = 0; i < count; ++i) { layers.Add((Layer)reader.ReadUInt16()); } - PlayerMobile.UnequipMacro(state.Mobile, layers); + PlayerMobile.UnequipMacro(state.Mobile, ref layers); } } diff --git a/Projects/UOContent/Skills/Tracking/Tracking.cs b/Projects/UOContent/Skills/Tracking/Tracking.cs index 0cb1f792a..4825f8bc4 100644 --- a/Projects/UOContent/Skills/Tracking/Tracking.cs +++ b/Projects/UOContent/Skills/Tracking/Tracking.cs @@ -51,7 +51,7 @@ public static class Tracking public static void AddInfo(Mobile tracker, Mobile target) { - var info = new TrackingInfo(tracker, target); + var info = new TrackingInfo(target); _table[tracker] = info; } @@ -80,11 +80,9 @@ public static class Tracking public Point2D _location; public readonly Map _map; public readonly Mobile _target; - public Mobile _tracker; - public TrackingInfo(Mobile tracker, Mobile target) + public TrackingInfo(Mobile target) { - _tracker = tracker; _target = target; _location = new Point2D(target); _map = target.Map; @@ -164,7 +162,8 @@ public class TrackWhoGump : DynamicGump from.CheckSkill(SkillName.Tracking, 21.1, 100.0); // Passive gain - var range = 10 + (int)(from.Skills.Tracking.Value / 10); + // 10 tiles base + 10 tiles per 10 skill + var range = 10 + (int)from.Skills.Tracking.Value / 10 * 10; var mobs = GetClosestMobs(from, range, type); @@ -231,13 +230,19 @@ public class TrackWhoGump : DynamicGump var loc = from.Location; // We only track the closest 12 + // Using a simple array with count is sufficient since we're single-threaded var mobs = new Mobile[MaxClosest]; Span distances = stackalloc double[MaxClosest]; - distances.Fill(double.MaxValue); // Fill with max values - var total = 0; + var maxDistance = double.MaxValue; + var count = 0; - foreach (var m in from.GetMobilesInRange(range)) + foreach (var (m, minDistance) in from.GetMobilesInRangeByDistance(range)) { + if (count == MaxClosest && minDistance > maxDistance) + { + break; + } + if (m == from || Core.AOS && !m.Alive || m.Hidden && m.AccessLevel != AccessLevel.Player && from.AccessLevel <= m.AccessLevel || !IsValidMobileType(m, type) || !CheckDifficulty(from, m)) @@ -245,30 +250,51 @@ public class TrackWhoGump : DynamicGump continue; } - total++; - var distance = m.GetDistanceToSqrt(loc); - for (var i = 0; i < MaxClosest; i++) + + if (count >= MaxClosest && distance >= maxDistance) + { + continue; + } + + var searchLimit = Math.Min(count, MaxClosest - 1); + var insertIndex = searchLimit; + + for (var i = 0; i < searchLimit; i++) { if (distance < distances[i]) { - // Shift down the rest - for (int j = MaxClosest - 1; j > i; j--) - { - mobs[j] = mobs[j - 1]; - distances[j] = distances[j - 1]; - } - - mobs[i] = m; - distances[i] = distance; + insertIndex = i; break; } } + + // Shift elements to make room for insertion + if (insertIndex < searchLimit) + { + var shiftCount = searchLimit - insertIndex; + Array.Copy(mobs, insertIndex, mobs, insertIndex + 1, shiftCount); + distances.Slice(insertIndex, shiftCount).CopyTo(distances[(insertIndex + 1)..]); + } + + mobs[insertIndex] = m; + distances[insertIndex] = distance; + + if (count < MaxClosest) + { + count++; + } + + if (count == MaxClosest) + { + maxDistance = distances[MaxClosest - 1]; + } } - if (total < MaxClosest) + // Only resize if we found fewer than MaxClosest + if (count < MaxClosest) { - Array.Resize(ref mobs, total); + Array.Resize(ref mobs, count); } return mobs; diff --git a/Projects/UOContent/Spells/Eighth/Resurrection.cs b/Projects/UOContent/Spells/Eighth/Resurrection.cs index f4c6b686e..689ddc3d5 100644 --- a/Projects/UOContent/Spells/Eighth/Resurrection.cs +++ b/Projects/UOContent/Spells/Eighth/Resurrection.cs @@ -1,6 +1,7 @@ using Server.Engines.ConPVP; using Server.Gumps; using Server.Targeting; +using Server.Mobiles; namespace Server.Spells.Eighth { @@ -30,22 +31,22 @@ namespace Server.Spells.Eighth { Caster.SendLocalizedMessage(501039); // Thou can not resurrect thyself. } - else if (!Caster.Alive) + else if (m is BaseCreature { IsDeadBondedPet: true }) { - Caster.SendLocalizedMessage(501040); // The resurrecter must be alive. + Caster.SendMessage("Target can not be revived this way."); + } + else if (!m.Player) + { + Caster.SendLocalizedMessage(501043); // Target is not a being. } else if (m.Alive) { Caster.SendLocalizedMessage(501041); // Target is not dead. } - else if (!Caster.InRange(m, 1)) + else if (!Caster.InRange(m, TargetRange)) { Caster.SendLocalizedMessage(501042); // Target is not close enough. } - else if (!m.Player) - { - Caster.SendLocalizedMessage(501043); // Target is not a being. - } else if (m.Map?.CanFit(m.Location, 16, false, false) != true) { Caster.SendLocalizedMessage(501042); // Target can not be resurrected at that location. diff --git a/Projects/UOContent/Spells/Ninjitsu/MirrorImage.cs b/Projects/UOContent/Spells/Ninjitsu/MirrorImage.cs index d5a088123..6d85b9d73 100644 --- a/Projects/UOContent/Spells/Ninjitsu/MirrorImage.cs +++ b/Projects/UOContent/Spells/Ninjitsu/MirrorImage.cs @@ -234,11 +234,11 @@ namespace Server.Mobiles public override bool Think() { // Clones only follow their owners - var master = m_Mobile.SummonMaster; + var master = Mobile.SummonMaster; - if (master?.Map == m_Mobile.Map && master?.InRange(m_Mobile, m_Mobile.RangePerception) == true) + if (master?.Map == Mobile.Map && master?.InRange(Mobile, Mobile.RangePerception) == true) { - var iCurrDist = (int)m_Mobile.GetDistanceToSqrt(master); + var iCurrDist = (int)Mobile.GetDistanceToSqrt(master); var bRun = iCurrDist > 5; WalkMobileRange(master, 2, bRun, 0, 1); diff --git a/Projects/UOContent/Systems/JailSystem/JailRecord.cs b/Projects/UOContent/Systems/JailSystem/JailRecord.cs new file mode 100644 index 000000000..c57ef8622 --- /dev/null +++ b/Projects/UOContent/Systems/JailSystem/JailRecord.cs @@ -0,0 +1,25 @@ +using System; +using ModernUO.Serialization; + +namespace Server.Systems.JailSystem; + +[SerializationGenerator(0)] +public partial class JailRecord +{ + [SerializableField(0)] + private int _jailCount; + + [SerializableField(1)] + private DateTime _lastJailed; + + [SerializableField(2)] + private DateTime _jailEndTime; + + [SerializableField(3)] + private string _lastJailReason; + + [SerializableField(4)] + private Mobile _jailedBy; + + public bool IsCurrentlyJailed => JailEndTime > Core.Now; +} diff --git a/Projects/UOContent/Systems/JailSystem/JailRecordGump.cs b/Projects/UOContent/Systems/JailSystem/JailRecordGump.cs new file mode 100644 index 000000000..2ffe7a9f8 --- /dev/null +++ b/Projects/UOContent/Systems/JailSystem/JailRecordGump.cs @@ -0,0 +1,90 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2025 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: JailRecordGump.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using Server.Gumps; +using Server.Mobiles; + +namespace Server.Systems.JailSystem; + +public class JailRecordGump : StaticGump +{ + private readonly PlayerMobile _player; + private readonly JailRecord _record; + + public JailRecordGump(PlayerMobile player, JailRecord record) : base(0, 0) + { + _player = player; + _record = record; + } + + protected override void BuildLayout(ref StaticGumpBuilder builder) + { + builder.SetNoResize(); + + builder.AddPage(); + builder.AddBackground(0, 0, 330, 300, 9200); + builder.AddAlphaRegion(10, 10, 310, 280); + + builder.AddHtml(20, 20, 360, 30, "JAIL RECORD"); + builder.AddHtmlPlaceholder(20, 60, 360, 25, "playerName"); + builder.AddHtmlPlaceholder(20, 80, 360, 25, "jailCount"); + builder.AddHtmlPlaceholder(20, 100, 360, 25, "lastJailed"); + builder.AddHtmlPlaceholder(20, 120, 290, 25, "jailReason"); + + builder.AddHtmlPlaceholder(20, 150, 360, 25, "jailStatus"); + builder.AddHtmlPlaceholder(20, 170, 360, 25, "jailTime"); + + builder.AddHtml(20, 200, 360, 25, "If you believe you were jailed in error,"); + builder.AddHtml(20, 220, 360, 25, "please contact staff through normal channels."); + builder.AddHtml(20, 240, 360, 25, "Each time you are jailed, the wait increases."); + builder.AddHtml(20, 260, 360, 25, "Follow all shard rules to avoid future jail time."); + } + + protected override void BuildStrings(ref GumpStringsBuilder builder) + { + builder.SetHtmlText("playerName", $"Player: {_player.Name}", "#FFFF00", 5); + builder.SetHtmlText("jailCount", $"Jail Count: {_record.JailCount}", "#FFFFFF", 5); + + if (_record.LastJailed > DateTime.MinValue) + { + builder.SetHtmlText("lastJailed", $"Last Jailed: {_record.LastJailed:yyyy/MM/dd HH:mm}", "#FFFFFF", 5); + } + else + { + builder.SetHtmlText("lastJailed", "Last Jailed: Never", "#FFFFFF", 5); + } + + if (string.IsNullOrWhiteSpace(_record.LastJailReason)) + { + builder.SetHtmlText("jailReason", "Jail Reason: None given.", "#FFFFFF", 4); + } + else + { + builder.SetHtmlText("jailReason", $"Jail Reason: {_record.LastJailReason}", "#FFFFFF", 4); + } + + if (_record.IsCurrentlyJailed) + { + builder.SetHtmlText("jailStatus", "Status: Currently Jailed", "#FF6666", 5); + builder.SetHtmlText("jailTime", $"Jail Time: {(_record.JailEndTime - Core.Now).FormatTimeCompact()}", "#FF6666", 5); + } + else + { + builder.SetHtmlText("jailStatus", "Status: Not Jailed", "#00FF00", 5); + builder.SetStringSlot("jailTime", ""); + } + } +} diff --git a/Projects/UOContent/Regions/JailRegion.cs b/Projects/UOContent/Systems/JailSystem/JailRegion.cs similarity index 100% rename from Projects/UOContent/Regions/JailRegion.cs rename to Projects/UOContent/Systems/JailSystem/JailRegion.cs diff --git a/Projects/UOContent/Systems/JailSystem/JailSystem.cs b/Projects/UOContent/Systems/JailSystem/JailSystem.cs new file mode 100644 index 000000000..a2592470e --- /dev/null +++ b/Projects/UOContent/Systems/JailSystem/JailSystem.cs @@ -0,0 +1,427 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2025 - ModernUO Development Team * + * Email: hi@modernuo.com * + * File: JailSystem.cs * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation, either version 3 of the License, or * + * (at your option) any later version. * + * * + * You should have received a copy of the GNU General Public License * + * along with this program. If not, see . * + *************************************************************************/ + +using System; +using System.Collections.Generic; +using Server.Mobiles; +using Server.Network; +using Server.Commands; +using Server.Gumps; + +namespace Server.Systems.JailSystem; + +public class JailSystem : GenericPersistence +{ + // Jail locations (modify if using custom maps) + // felucca void + private static readonly Point3D[] JailLocations = + [ + new(5276, 1164, 0), + new(5286, 1164, 0), + new(5296, 1164, 0), + new(5306, 1164, 0), + new(5276, 1174, 0), + new(5286, 1174, 0), + new(5296, 1174, 0), + new(5306, 1174, 0), + new(5283, 1184, 0), + new(5304, 1184, 0) + ]; + + // Jail map, change this for custom maps + public static readonly Map JailMap = Map.Felucca; + private static readonly JailRecord EmptyRecord = new(); + + private static readonly HashSet CurrentlyBeingJailed = []; + private static readonly Dictionary PlayerJailRecords = []; + private static readonly Dictionary JailTimers = []; + + // Jail time scales from 5 minutes to 12 hours based on the number of offenses + private static readonly TimeSpan MinJailTime = TimeSpan.FromMinutes(5); + private static readonly TimeSpan MaxJailTime = TimeSpan.FromHours(12); + + // Release location, change this for custom maps + private static readonly Point3D ReleaseLocation = new(1444, 1697, 10); // Britain Bank + public static readonly Map ReleaseMap = Map.Felucca; + + private static JailSystem Instance; + + // [jail [reason] - Jail with time escalation per offense (GM only) + // [unjail - Manual release from jail regardless of time (GM only) + // [jailinfo - Check jail status and history (GM only) + // [jailrecord - Checks their own jail record stats (player access) + + public static void Configure() + { + Instance = new JailSystem(); + CommandSystem.Register("Jail", AccessLevel.GameMaster, Jail_OnCommand); + CommandSystem.Register("Unjail", AccessLevel.GameMaster, Unjail_OnCommand); + CommandSystem.Register("JailInfo", AccessLevel.Counselor, JailInfo_OnCommand); + CommandSystem.Register("JailRecord", AccessLevel.Player, MyJailInfo_OnCommand); + } + + public JailSystem() : base("Jail", 3) + { + } + + // Can be used by other systems to check player jail status + public static bool IsPlayerJailed(PlayerMobile player) => + PlayerJailRecords.GetValueOrDefault(player)?.IsCurrentlyJailed == true; + + private static TimeSpan CalculateJailTime(int jailCount) + { + var totalMinutes = MinJailTime + (jailCount - 1) * (MaxJailTime - MinJailTime) / 9.0; + + return totalMinutes.Clamp(MinJailTime, MaxJailTime); + } + + public static void JailPlayer(Mobile from, PlayerMobile player, string reason = "") + { + if (!CurrentlyBeingJailed.Add(player)) + { + return; + } + + if (!PlayerJailRecords.TryGetValue(player, out var record)) + { + PlayerJailRecords[player] = record = new JailRecord(); + } + + record.JailCount++; + record.LastJailed = Core.Now; + record.LastJailReason = reason; + record.JailedBy = from; + + var jailTime = CalculateJailTime(record.JailCount); + record.JailEndTime = Core.Now + jailTime; + + player.Frozen = true; + player.SendMessage(0x35, "You are being sent to jail!"); + player.PlaySound(0x204); + + CommandLogging.WriteLine(from, $"Player {player.Name} jailed for: {reason} (Offense #{record.JailCount}, {jailTime.TotalMinutes} minutes)"); + + foreach (var ns in NetState.Instances) + { + if (ns.Mobile is PlayerMobile staff && staff.AccessLevel >= AccessLevel.Counselor) + { + staff.SendMessage(0x35, $"Player {player.Name} has been jailed for {jailTime.TotalMinutes} minutes. Reason: {reason} (Offense #{record.JailCount})"); + } + } + + Timer.DelayCall(TimeSpan.FromSeconds(2.0), DismountPlayer, from, player, jailTime); + } + + private static void DismountPlayer(Mobile from, PlayerMobile player, TimeSpan jailTime) + { + if (player.Mount != null) + { + var mount = player.Mount; + mount.Rider = null; + player.SendMessage(0x35, "You have been dismounted."); + if (mount is BaseCreature bc) + { + if (bc.Summoned) + { + bc.Dispel(bc); + } + else // Stable the pet + { + bc.ControlTarget = null; + bc.ControlOrder = OrderType.Stay; + bc.Internalize(); + + bc.SetControlMaster(null); + + bc.IsStabled = true; + bc.StabledBy = from; + player.AddStabled(bc); + } + } + } + + CommandLogging.WriteLine(from, $"Player {player.Name} dismounted before jail teleport"); + + Timer.DelayCall(TimeSpan.FromSeconds(3.0), TeleportToJail, from, player, jailTime); + } + + private static void TeleportToJail(Mobile from, PlayerMobile player, TimeSpan jailTime) + { + var jailLocation = JailLocations.RandomElement(); + + player.MoveToWorld(jailLocation, JailMap); + + player.SendMessage(0x35, "Use [jailrecord to pull up your record."); + player.SendMessage(0x35, "Please contact staff if you believe this was a mistake."); + + CommandLogging.WriteLine(from, $"Player {player.Name} teleported to jail at {jailLocation}"); + + Timer.DelayCall(TimeSpan.FromSeconds(5.0), UnfreezePlayer, from, player, jailTime); + } + + private static void UnfreezePlayer(Mobile from, PlayerMobile player, TimeSpan jailTime) + { + player.Frozen = false; + + CommandLogging.WriteLine(from, $"Player {player.Name} unfrozen in jail"); + + CurrentlyBeingJailed.Remove(player); + + var releaseTimer = Timer.DelayCall(jailTime, ReleasePlayer, from, player); + JailTimers[player] = releaseTimer; + } + + private static void ReleasePlayer(Mobile from, PlayerMobile player) + { + if (PlayerJailRecords.TryGetValue(player, out var record)) + { + record.JailEndTime = Core.Now; + } + + JailTimers.Remove(player); + + // Freeze player for release sequence + player.Frozen = true; + player.SendMessage(0x35, "You have been released from jail!"); + player.PlaySound(0x1FF); + + if (from != null) + { + CommandLogging.WriteLine(from, $"Player {player.Name} released from jail, starting teleport sequence"); + } + + foreach (var ns in NetState.Instances) + { + if (ns.Mobile is PlayerMobile staff && staff.AccessLevel >= AccessLevel.Counselor) + { + staff.SendMessage(0x35, $"Player {player.Name} has been released from jail."); + } + } + + Timer.DelayCall(TimeSpan.FromSeconds(5.0), TeleportFromJail, from, player); + } + + private static void TeleportFromJail(Mobile from, PlayerMobile player) + { + player.MoveToWorld(ReleaseLocation, ReleaseMap); + + if (from != null) + { + CommandLogging.WriteLine(player, $"Player {player.Name} teleported from jail to {ReleaseLocation}"); + } + + Timer.DelayCall(TimeSpan.FromSeconds(5.0), UnfreezeFromRelease, from, player); + } + + private static void UnfreezeFromRelease(Mobile from, PlayerMobile player) + { + player.Frozen = false; + player.SendMessage(0x35, "Welcome back!"); + player.SendMessage(0x35, "Please follow the shard rules."); + player.SendMessage(0x35, "Have a nice day!"); + + if (from != null) + { + CommandLogging.WriteLine(from, $"Player {player.Name} unfrozen after jail release"); + } + } + + [Usage("Jail [reason]")] + [Description("Jails a player with freeze/teleport/unfreeze sequence.")] + private static void Jail_OnCommand(CommandEventArgs e) + { + if (e.Length < 1) + { + e.Mobile.SendMessage(0x35, "Usage: [jail [reason]"); + return; + } + + var reason = e.GetString(1); + var playerName = e.GetString(0); + var playerSerial = Serial.TryParse(playerName, null, out var serial) ? serial : Serial.MinusOne; + + PlayerMobile player = null; + + foreach (var m in World.Mobiles.Values) + { + if (m is PlayerMobile pm && (pm.Serial == playerSerial || m.RawName.InsensitiveEquals(playerName))) + { + player = pm; + break; + } + } + + if (player == null) + { + e.Mobile.SendMessage(0x35, $"Player '{playerName}' not found."); + return; + } + + if (player.AccessLevel > AccessLevel.Player) + { + e.Mobile.SendMessage(0x35, "You cannot jail staff members."); + return; + } + + if (IsPlayerJailed(player)) + { + e.Mobile.SendMessage(0x35, $"Player {player.Name} is already jailed."); + return; + } + + JailPlayer(e.Mobile, player, reason); + e.Mobile.SendMessage(0x35, $"Player {player.Name} is being jailed. Reason: {reason}"); + } + + [Usage("Unjail ")] + [Description("Manually releases a player from jail.")] + private static void Unjail_OnCommand(CommandEventArgs e) + { + var from = e.Mobile; + if (e.Length < 1) + { + from.SendMessage(0x35, "Usage: [unjail "); + return; + } + + var playerName = e.GetString(0); + var playerSerial = Serial.TryParse(playerName, null, out var serial) ? serial : Serial.MinusOne; + + PlayerMobile player = null; + + foreach (var m in World.Mobiles.Values) + { + if (m is PlayerMobile pm && (pm.Serial == playerSerial || m.RawName.InsensitiveEquals(playerName))) + { + player = pm; + break; + } + } + + if (player == null) + { + from.SendMessage(0x35, $"Player '{playerName}' not found."); + return; + } + + if (!IsPlayerJailed(player)) + { + from.SendMessage(0x35, $"Player {player.Name} is not currently jailed."); + return; + } + + if (JailTimers.Remove(player, out var value)) + { + value.Stop(); + } + + ReleasePlayer(from, player); + from.SendMessage(0x35, $"Player {player.Name} has been manually released from jail."); + } + + [Usage("JailInfo ")] + [Description("Shows jail information for a player.")] + private static void JailInfo_OnCommand(CommandEventArgs e) + { + if (e.Length < 1) + { + e.Mobile.SendMessage(0x35, "Usage: [jailinfo "); + return; + } + + var playerName = e.GetString(0); + var playerSerial = Serial.TryParse(playerName, null, out var serial) ? serial : Serial.MinusOne; + + PlayerMobile player = null; + + foreach (var m in World.Mobiles.Values) + { + if (m is PlayerMobile pm && (pm.Serial == playerSerial || m.RawName.InsensitiveEquals(playerName))) + { + player = pm; + break; + } + } + + if (player == null) + { + e.Mobile.SendMessage(0x35, $"Player '{playerName}' not found."); + return; + } + + e.Mobile.SendGump(new JailRecordGump(player, PlayerJailRecords.GetValueOrDefault(player, EmptyRecord))); + } + + private static readonly Dictionary JailRecordCooldowns = new(); + private static readonly TimeSpan JailRecordCooldown = TimeSpan.FromSeconds(30); + + [Usage("JailRecord")] + [Description("Shows your own jail information.")] + private static void MyJailInfo_OnCommand(CommandEventArgs e) + { + var player = (PlayerMobile)e.Mobile; + + if (JailRecordCooldowns.TryGetValue(player, out var cooldown)) + { + var timeSinceLastUse = Core.Now - cooldown; + + if (timeSinceLastUse < JailRecordCooldown) + { + var remaining = JailRecordCooldown - timeSinceLastUse; + e.Mobile.SendMessage(0x35, $"You must wait {remaining.TotalSeconds:F0} seconds."); + return; + } + } + + JailRecordCooldowns[player] = Core.Now; + + player.SendGump(new JailRecordGump(player, PlayerJailRecords.GetValueOrDefault(player, EmptyRecord))); + } + + public override void Serialize(IGenericWriter writer) + { + writer.WriteEncodedInt(0); // version + writer.WriteEncodedInt(PlayerJailRecords.Count); + foreach (var (m, record) in PlayerJailRecords) + { + writer.Write(m); + record.Serialize(writer); + } + } + + public override void Deserialize(IGenericReader reader) + { + var version = reader.ReadEncodedInt(); + + var count = reader.ReadEncodedInt(); + for (var i = 0; i < count; i++) + { + var player = reader.ReadEntity(); + var record = new JailRecord(); + record.Deserialize(reader); + + if (player != null) + { + PlayerJailRecords[player] = record; + if (record.IsCurrentlyJailed) + { + CurrentlyBeingJailed.Add(player); + var jailTime = record.JailEndTime - Core.Now; + JailTimers[player] = Timer.DelayCall(jailTime, ReleasePlayer, record.JailedBy, player); + } + } + } + } +} diff --git a/Projects/UOContent/Text/TextDefinitionExtensions.cs b/Projects/UOContent/Text/TextDefinitionExtensions.cs index 7e463b78a..8cb8b743b 100644 --- a/Projects/UOContent/Text/TextDefinitionExtensions.cs +++ b/Projects/UOContent/Text/TextDefinitionExtensions.cs @@ -32,7 +32,7 @@ public static class TextDefinitionExtensions int stringColor = -1 ) { - if (def == null) + if (def?.IsEmpty != false) { return; } @@ -48,16 +48,28 @@ public static class TextDefinitionExtensions builder.AddHtmlLocalized(x, y, width, height, def.Number, back, scroll); } } - else if (def.String != null) + else if (stringColor != -1) { builder.AddHtml( x, y, width, height, - stringColor >= 0 ? def.String.Color(stringColor) : def.String, // 8 bits per RGB component (24 bit RGB) - back, - scroll + $"{def.String}", + background: back, + scrollbar: scroll + ); + } + else + { + builder.AddHtml( + x, + y, + width, + height, + def.String, + background: back, + scrollbar: scroll ); } } @@ -75,7 +87,7 @@ public static class TextDefinitionExtensions int stringColor = -1 ) { - if (def == null) + if (def?.IsEmpty != false) { return; } @@ -91,16 +103,28 @@ public static class TextDefinitionExtensions builder.AddHtmlLocalized(x, y, width, height, def.Number, back, scroll); } } - else if (def.String != null) + else if (stringColor != -1) { builder.AddHtml( x, y, width, height, - stringColor >= 0 ? def.String.Color(stringColor) : def.String, // 8 bits per RGB component (24 bit RGB) - back, - scroll + $"{def.String}", + background: back, + scrollbar: scroll + ); + } + else + { + builder.AddHtml( + x, + y, + width, + height, + def.String, + background: back, + scrollbar: scroll ); } } @@ -118,23 +142,12 @@ public static class TextDefinitionExtensions int stringColor = -1 ) { - if (def == null) + if (def?.IsEmpty != false) { return; } - if (def.Number > 0) - { - if (numberColor >= 0) // 5 bits per RGB component (15 bit RGB) - { - g.AddHtmlLocalized(x, y, width, height, def.Number, numberColor, back, scroll); - } - else - { - g.AddHtmlLocalized(x, y, width, height, def.Number, back, scroll); - } - } - else if (def.String != null) + if (def.Number <= 0) { g.AddHtml( x, @@ -146,6 +159,14 @@ public static class TextDefinitionExtensions scroll ); } + else if (numberColor >= 0) // 5 bits per RGB component (15 bit RGB) + { + g.AddHtmlLocalized(x, y, width, height, def.Number, numberColor, back, scroll); + } + else + { + g.AddHtmlLocalized(x, y, width, height, def.Number, back, scroll); + } } public static void AddHtmlText( @@ -162,7 +183,7 @@ public static class TextDefinitionExtensions int stringColor = -1 ) { - if (def == null) + if (def?.IsEmpty != false) { return; } @@ -172,7 +193,7 @@ public static class TextDefinitionExtensions // 5 bits per RGB component (15 bit RGB) g.AddHtmlLocalized(x, y, width, height, def.Number, args, numberColor >= 0 ? numberColor : 0x7FFF, back, scroll); } - else if (def.String != null) + else { g.AddHtml( x, diff --git a/Projects/UOContent/UOContent.csproj b/Projects/UOContent/UOContent.csproj index a1146db1c..4ca0561f8 100644 --- a/Projects/UOContent/UOContent.csproj +++ b/Projects/UOContent/UOContent.csproj @@ -39,16 +39,16 @@ false - - + + - - + +
diff --git a/README.md b/README.md index 2184b2181..41f54b14a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- + ModernUO - Ultima Online Server Emulator for the modern era!

ModernUO [![Discord](https://img.shields.io/discord/751317910504603701?logo=discord&style=social)](https://muo.gg/discord) [![Subreddit subscribers](https://img.shields.io/reddit/subreddit-subscribers/modernuo?style=social&label=/r/modernuo)](https://muo.gg/reddit/) [![Twitter Follow](https://img.shields.io/twitter/follow/modernuo?label=@modernuo&style=social)](https://muo.gg/twitter) @@ -9,48 +9,45 @@ ModernUO [![Discord](https://img.shields.io/discord/751317910504603701?logo=disc [![GitHub license](https://img.shields.io/github/license/modernuo/ModernUO?color=blue)](https://github.com/modernuo/ModernUO/blob/master/LICENSE) [![GitHub stars](https://img.shields.io/github/stars/modernuo/ModernUO?logo=github&style=flat)](https://github.com/modernuo/ModernUO/stargazers) [![GitHub issues](https://img.shields.io/github/issues/modernuo/ModernUO?logo=github)](https://github.com/modernuo/ModernUO/issues) -
+
[![GitHub build](https://img.shields.io/github/actions/workflow/status/modernuo/ModernUO/build-test.yml?branch=main&logo=github)](https://github.com/modernuo/ModernUO/actions) [![Azure Pipelines build](https://dev.azure.com/modernuo/modernuo/_apis/build/status/Build?branchName=main)](https://dev.azure.com/modernuo/modernuo/_build/latest?definitionId=1&branchName=main) ## Requirements #### Supported Operating Systems -[![Windows 10/11/2019/2022](https://img.shields.io/badge/-server%202022-3c78d5?labelColor=222222&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHJvbGU9ImltZyIgdmlld0JveD0iMCAwIDI0IDI0Ij48dGl0bGU+V2luZG93czwvdGl0bGU+PHBhdGggZD0iTTAsMEgxMS4zNzdWMTEuMzcySDBaTTEyLjYyMywwSDI0VjExLjM3MkgxMi42MjNaTTAsMTIuNjIzSDExLjM3N1YyNEgwWm0xMi42MjMsMEgyNFYyNEgxMi42MjMiIGZpbGw9IiMzYzc4ZDUiLz48L3N2Zz4=)](https://www.microsoft.com/en-US/evalcenter/evaluate-windows-server-2022) -![MacOS 13+](https://img.shields.io/badge/-sonoma-222222?logo=apple&logoColor=white&labelColor=222222) -[![Debian 11+](https://img.shields.io/badge/-bookworm-A81D33?logo=debian&logoColor=A81D33&labelColor=222222)](https://www.debian.org/distrib/) -[![Ubuntu 20+ LTS](https://img.shields.io/badge/-22LTS-E95420?logo=ubuntu&logoColor=E95420&labelColor=222222)](https://ubuntu.com/download/server) -
-[![Alpine 3.18+](https://img.shields.io/badge/-3.18-0D597F?logo=alpinelinux&logoColor=0D597F&labelColor=222222)](https://alpinelinux.org/downloads/) -[![Fedora 40+](https://img.shields.io/badge/-40-51a2da?logo=fedora&logoColor=51a2da&labelColor=222222)](https://getfedora.org/en/server/download/) -[![RedHat 8+](https://img.shields.io/badge/-8-BE0000?logo=redhat&logoColor=BE0000&labelColor=222222)](https://access.redhat.com/downloads) -[![CentOS 9](https://img.shields.io/badge/-9-262577?logo=centos&logoColor=white&labelColor=222222)](https://www.centos.org/download/) -[![openSUSE 15+](https://img.shields.io/badge/-15-73BA25?logo=openSUSE&logoColor=73BA25&labelColor=222222)](https://get.opensuse.org/) -[![SUSE Enterprise 12 SP2+](https://img.shields.io/badge/-12%20SP2-0C322C?logo=suse&logoColor=30BA78&labelColor=222222)](https://www.suse.com/download/sles/) -[![Linux Mint 17+](https://img.shields.io/badge/-20-87CF3E?logo=linux%20mint&logoColor=87CF3E&labelColor=222222)](https://linuxmint.com/download.php) +[![Windows 10/11/2012/2016/2019/2022/2025](https://img.shields.io/badge/-server%202025-3c78d5?labelColor=222222&logo=data:image/svg%2bxml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHJvbGU9ImltZyIgdmlld0JveD0iMCAwIDI0IDI0Ij48dGl0bGU+V2luZG93czwvdGl0bGU+PHBhdGggZD0iTTAsMEgxMS4zNzdWMTEuMzcySDBaTTEyLjYyMywwSDI0VjExLjM3MkgxMi42MjNaTTAsMTIuNjIzSDExLjM3N1YyNEgwWm0xMi42MjMsMEgyNFYyNEgxMi42MjMiIGZpbGw9IiMzYzc4ZDUiLz48L3N2Zz4=)](https://www.microsoft.com/en-US/evalcenter/evaluate-windows-server-2022) +![MacOS 14+](https://img.shields.io/badge/-sonoma-222222?logo=apple&logoColor=white&labelColor=222222) +[![Debian 12+](https://img.shields.io/badge/-trixie-A81D33?logo=debian&logoColor=A81D33&labelColor=222222)](https://www.debian.org/distrib/) +[![Ubuntu 22+ LTS](https://img.shields.io/badge/-24LTS-E95420?logo=ubuntu&logoColor=E95420&labelColor=222222)](https://ubuntu.com/download/server) +
+[![Alpine 3.22+](https://img.shields.io/badge/-3.22-0D597F?logo=alpinelinux&logoColor=0D597F&labelColor=222222)](https://alpinelinux.org/downloads/) +[![Fedora 42+](https://img.shields.io/badge/-42-51a2da?logo=fedora&logoColor=51a2da&labelColor=222222)](https://getfedora.org/en/server/download/) +[![RedHat 9+](https://img.shields.io/badge/-9-BE0000?logo=redhat&logoColor=BE0000&labelColor=222222)](https://access.redhat.com/downloads) +[![CentOS Stream 9+](https://img.shields.io/badge/-stream_9-262577?logo=centos&logoColor=white&labelColor=222222)](https://www.centos.org/download/) +[![openSUSE 15.6+](https://img.shields.io/badge/-15.6-73BA25?logo=openSUSE&logoColor=73BA25&labelColor=222222)](https://get.opensuse.org/) +[![SUSE Enterprise 15 SP6](https://img.shields.io/badge/-15%20SP6-0C322C?logo=suse&logoColor=30BA78&labelColor=222222)](https://www.suse.com/download/sles/) +[![Linux Mint 21+](https://img.shields.io/badge/-21-87CF3E?logo=linux%20mint&logoColor=87CF3E&labelColor=222222)](https://linuxmint.com/download.php) [![Arch](https://img.shields.io/badge/-Arch-1793D1?logo=archlinux&logoColor=1793D1&labelColor=222222)](https://archlinux.org/download/) #### Required Frameworks ##### All Operating Systems -[![.NET](https://img.shields.io/badge/-9.0.0-5C2D91?logo=.NET&logoColor=white&labelColor=222222)](https://dotnet.microsoft.com/download/dotnet/9.0) +[![.NET](https://img.shields.io/badge/-10.0.0-5C2D91?logo=.NET&logoColor=white&labelColor=222222)](https://dotnet.microsoft.com/download/dotnet/10.0) ##### Windows -[![VC++ Redistributable 17](https://img.shields.io/badge/-Redist%2017-00599C?logo=cplusplus&logoColor=white&labelColor=222222)](https://learn.microsoft.com/en-us/cpp/windows/latest-supported-vc-redist?view=msvc-170#visual-studio-2015-2017-2019-and-2022) +[![VC++ Redistributable v14](https://img.shields.io/badge/-Redist%20v14-00599C?logo=cplusplus&logoColor=white&labelColor=222222)](https://aka.ms/vc14/vc_redist.x64.exe) #### Development [![git](https://img.shields.io/badge/-git-F05032?logo=git&logoColor=F05032&labelColor=222222)](https://git-scm.com/downloads) -[![.NET](https://img.shields.io/badge/-%209.0.101%20SDK-5C2D91?logo=.NET&logoColor=white&labelColor=222222)](https://dotnet.microsoft.com/download/dotnet/9.0) +[![.NET](https://img.shields.io/badge/-%2010.0.100%20SDK-5C2D91?logo=.NET&logoColor=white&labelColor=222222)](https://dotnet.microsoft.com/download/dotnet/10.0) #### Supported IDEs - -

Jetbrains Rider 2024.3+spaceVSCodespaceVisual Studio 2022 v17.12+

+

+ Jetbrains Rider 2025.3+ + space + VSCode + space + Visual Studio 2026 +

## Getting Started - Install prerequisite [requirements](https://github.com/modernuo/ModernUO#requirements) @@ -60,13 +57,13 @@ ModernUO [![Discord](https://img.shields.io/discord/751317910504603701?logo=disc ## Building/Publishing - Run `./publish.cmd [release|debug (default: release)] [os] [arch (default: x64)]` - - `os` - [Supported operating systems](https://github.com/dotnet/core/blob/main/release-notes/7.0/supported-os.md) - - `win` - Windows 10/11/2019/2022 - - `osx` - MacOS 12/13/14 (Sonoma, Big Sur, Monterey) - - `linux` - Linux + - `os` - [Supported operating systems](https://github.com/dotnet/core/blob/main/release-notes/10.0/supported-os.md) + - `win` - [Windows](https://learn.microsoft.com/en-us/dotnet/core/install/windows) + - `osx` - [MacOS](https://learn.microsoft.com/en-us/dotnet/core/install/macos) + - `linux` - [Linux](https://learn.microsoft.com/en-us/dotnet/core/install/linux) - `arch` - - `x64` - Intel 64-bit - - `arm64` - ARM 64-bit (Windows Arm64 not supported) + - `x64` - Intel/AMD 64-bit + - `arm64` - ARM 64-bit (Windows not supported) ## Linux Prerequisites ### Fedora, CentOS, RHEL, etc @@ -89,8 +86,8 @@ brew install icu4c libdeflate zstd argon2 ``` ## Running the Server -- Follow the [publish](https://github.com/modernuo/ModernUO#publishing-builds) instructions -- Run `ModernUO.exe` or `dotnet ModernUO.dll` from the `Distribution` directory on the +- Follow the [publish](https://github.com/modernuo/ModernUO#buildingpublishing) instructions +- Run `ModernUO.exe` or `dotnet ModernUO.dll` from the `Distribution` directory ## Troubleshooting / FAQ - See [FAQ](./FAQ.md) @@ -107,7 +104,6 @@ Thank you for supporting us! You can find out how by visiting the [sponsors](./S - [Voxpire](https://github.com/Voxpire), the ServUO Team & Community - [Karasho](https://github.com/andreakarasho), [Jaedan](https://github.com/jaedan) and the ClassicUO Community -

-

Development Tools & Plugins provided with ♥ by
JetBrains
-Material Theme -

+

+

Development Tools & Plugins provided with ♥ by

JetBrains
+Material Theme

diff --git a/RUNUO_TO_MODERNUO.md b/RUNUO_TO_MODERNUO.md deleted file mode 100644 index 6c553f563..000000000 --- a/RUNUO_TO_MODERNUO.md +++ /dev/null @@ -1,121 +0,0 @@ -# From RunUO to ModernUO -RunUO was built using C# from .NET 1.1 in 2002. There have been massive changes to technology in the past 20+ years. -We believe it is time for Ultima Online to take advantage of this technology so a server can provide a richer experience with never before seen scale. - -While it is possible (and many have done it) to migrate an _active server_ from RunUO to ModernUO, it is a daunting task. -Please ask for help in our discord! - -## Technology -| | RunUO | ModernUO | -|:-------------|:------------------------------------------------------------------------------------------------------|:------------------------------------------------------------| -| Language | C# 4 | C# 11 (.NET 8) | -| Supported OS | 32 & 64bit Windows or Mono | 64bit Windows, MacOS & Linux | -| IDEs | [VS](https://visualstudio.microsoft.com/downloads/), [VSCode](https://code.visualstudio.com/download) | VS 2022+ or [Rider 2023+](https://www.jetbrains.com/rider/) | - -## Code API Changes -* **ModernUO can use source generators to [serialization/deserialization](https://github.com/modernuo/SerializationGenerator#basic-usage) automatically.** - * This is the biggest change to the API! While intimidating and different, it unlocks the ability for ModernUO to only serialize _data that has changed_. - * We estimate a world save with 10mill objects on a busy server will take less than 1 second. - * This feature is _optional and will remain optional indefinitely_. -* `Serialize(GenericWriter writer)` and equiv deserialize was changed to `Serialize(IGenericWriter writer)`. -* The following now use generics, e.g. `BeginAction(typeof(X))` is now `BeginAction`. - * CanBeginAction, BeginAction and EndAction - * FindRegion and IsPartOf - * FindGump, HasGump, and CloseGump -* Functions such as `OnAdded(object)` for both Items/Mobiles are now `OnAdded(IEntity)`. -* Most `delegate` have been changed to `Action`. - * Example: `EventSink.PlayerDeath += new PlayerDeathEventHandler(EventSink_PlayerDeath);` is now `EventSink.PlayerDeath += EventSink_PlayerDeath;`. -* `ObjectPropertyList` is now `IPropertyList`, - * Example: `GetProperties(ObjectPropertyList list)` is now `GetProperties(IPropertyList list)`. -* `[Constructable]` attribute is now `[Constructible]`. - -### Object Property List API Changes -ObjectPropertyList has been drastically optimized, which means the API has been modernized: - -❌ _Not valid_ -```cs -list.Add(1061837, "{0}\t{1}", m_CurArcaneCharges, m_MaxArcaneCharges); -``` -✅ -```cs -list.Add(1061837, $"{_curArcaneCharges}\t{_maxArcaneCharges}"); -``` - -Clilocs that use arguments: - -❌ _Not valid_ -```cs -list.Add(1060659, "Level\t{1}", m_Level); -``` -✅ - _Note the string as an argument, this is mandatory!_ -```cs -list.Add(1060659, $"{"Level"}\t{Level}"); // ~1_val~: ~2_val~ -``` - -Clilocs that use other clilocs as an argument: - -❌ _Do not prepend #_ -```cs -list.Add(1060830, $"#{dirt.ToString()}"); -``` -✅ _Use the new custom cliloc argument formatter_ -```cs -list.Add(1060830, $"{dirt:#}"); -``` - -## Core Changes -* ModernUO is not inherently thread safe. Overall infrastructure improved with CPU and minimizing memory garbage in mind. -* Timer system improved drastically by using [Timer Wheels](http://www.cs.columbia.edu/~nahum/w6998/papers/sosp87-timing-wheels.pdf). -* Networking is 5-10x faster by using fixed [Circular Buffers](https://en.wikipedia.org/wiki/Circular_buffer). -* Network packets are no longer objects and write directly to the network buffer, improving performance by 10x. -* World saves are 30x faster by saving to memory and flushing to disk in the background. -* Improved RNG accuracy and performance by 5x using [Xoshiro256++](https://prng.di.unimi.it/) -* Converted quite a bit of configuration to JSON with a central settings file. -* Logging changed from Console.WriteLine to Serilog (still work in progress). -* Eliminated calling `DateTime.Now` which had a huge performance penalty. -* Accounts are now saved to a binary file (will eventually change to a databse) to improve world save performance by eliminating XML. -* Strings are now built using the new interpolated string syntax, and highly performant string builders. - -## New Features -* IPv6 support. -* Generic serialization that is automatically done in parallel with the world save. - * The [faction system](https://github.com/modernuo/ModernUO/blob/7adf52ef48df7ae2b034c27e67b0c332b37fb053/Projects/UOContent/Engines/Factions/Core/FactionSystem.cs#L15) is no longer powered by a single item and instead uses generic persistence. -* Timezone support for custom scripts that might need it. -* Hourly/daily/weekly/monthly backups & archiving using [Z-Standard](https://facebook.github.io/zstd). -* Client version detection (including CUO) for easy configuration. -* Localization (Cliloc) support. -* Better encryption for passwords using [Argon2](https://en.wikipedia.org/wiki/Argon2). -* Packet throttling that is configurable per packet and per connection. -* Enable packet logging per connection. -* Owner accounts can be protected from being locked out. The first account created is automatically added to this list. -* Use optional arguments from constructors for Add command. -* Captures Razor version and displays it in client gump -* Spawners can be exported/imported using commands and use a GUID for replacement. - -## Major Feature Changes -* By default, world saves are now every 5th minute of a real world hour. - * E.g. 5:00, 5:05, 5:10, regardless of when the server is booted. -* Some items have their constructor arguments rearranged. - -## Changes to UO Mechanics -* Min/max skill requirements for magery adjusted to be accurate to OSI -* Buffs/Curses now apply appropriately - -## Removed Features -* Reporting -* Remote Admin -* My RunUO -* Event Log -* DocsGen command - -## New Development Features & Changes -* Added a shared list/queue for temporary processing such as accumulating players to damage/kill. - * [PooledRefList](https://github.com/modernuo/ModernUO/blob/main/Projects/Server/Collections/PooledRefList.cs) - * [PooledRefQueue](https://github.com/modernuo/ModernUO/blob/main/Projects/Server/Collections/PooledRefQueue.cs) -* Adds HashSet that is ordered by insertion time - * [OrderedHashSet](https://github.com/modernuo/ModernUO/blob/main/Projects/Server/Collections/OrderedHashSet.cs) - * [PooledOrderedHashSet](https://github.com/modernuo/ModernUO/blob/main/Projects/Server/Collections/PooledOrderedHashSet.cs) -* Adds [StringBuilder](https://github.com/modernuo/ModernUO/blob/main/Projects/Server/Buffers/ValueStringBuilder.cs) that is fast and does not impose garbage collection. -* Adds performant [JSON](https://github.com/modernuo/ModernUO/blob/main/Projects/Server/Json/JsonConfig.cs) support with simple API. -* Adds performant, thread _unsafe_, [ArrayPool](https://github.com/modernuo/ModernUO/blob/main/Projects/Server/Buffers/STArrayPool.cs) -* Adds highly performant and easy to use converters for [HexString](https://github.com/modernuo/ModernUO/blob/main/Projects/Server/Text/HexStringConverter.cs) representation of data diff --git a/SPONSORS.md b/SPONSORS.md index 4264670ca..f832b6a72 100644 --- a/SPONSORS.md +++ b/SPONSORS.md @@ -5,6 +5,7 @@ Thank you to all of our generous sponsors that make ModernUO possible. **A special thank you to the following sponsors for their considerable contributions:** * [Age of Shadows](https://ageofshadows.gg) * [UO Outlands](https://uooutlands.com) +* [UO Sagas](https://uosagas.com) * Prayer ([MagnUm-Opus](https://discord.gg/CzDEq3vv2N)) ### Looking to sponsor ModernUO? @@ -31,6 +32,5 @@ xch1vzk93t538m4xukg955v3ltjtgz6rapev3evnmnxdzf4hjj5y7cuq7tkf6z #### Bitcoin 37QmRWTCjVoNWycMpo1t5JnYVDmJTGSJ9W - #### Ethereum 0x7A9D76F497d4Ee150Ada0ea0455fF3f6e8F3b6b8 diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 798907fc1..0a0fb6a25 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -14,7 +14,7 @@ jobs: steps: - task: UseDotNet@2 - displayName: 'Install .NET 9' + displayName: 'Install .NET' inputs: useGlobalJson: true - task: NuGetAuthenticate@1 diff --git a/docs/commands/commands.7z b/docs/commands/commands.7z index 0da10ed6d..6db187201 100644 Binary files a/docs/commands/commands.7z and b/docs/commands/commands.7z differ diff --git a/docs/installation.md b/docs/installation.md index ff6d90c8b..2fefc1d58 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -6,7 +6,7 @@ title: Installation === "Windows" ### Prerequisites - 1. Download and install the latest [.NET 8 SDK](https://dotnet.microsoft.com/download/dotnet/8.0) + 1. Download and install the latest [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0) 1. Download and install from [here](https://git-scm.com/download/win) !!! Tip @@ -22,7 +22,7 @@ title: Installation === "OSX" ### Prerequisites - 1. Download and install the latest [.NET 8 SDK](https://dotnet.microsoft.com/download/dotnet/8.0). + 1. Download and install the latest [.NET 10 SDK](https://dotnet.microsoft.com/download/dotnet/10.0). 1. Using _terminal_, install [homebrew](https://brew.sh) and git: ```bash /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install.sh)" @@ -37,7 +37,7 @@ title: Installation === "Linux" ### Prerequisites - 1. Download and install the latest [.NET 8 SDK](https://docs.microsoft.com/en-us/dotnet/core/install/linux). + 1. Download and install the latest [.NET 10 SDK](https://docs.microsoft.com/en-us/dotnet/core/install/linux). 1. Using _bash_, install git: ```bash sudo apt update && sudo apt install git diff --git a/global.json b/global.json index 1739913f7..6a288505a 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.101", + "version": "10.0.100", "rollForward": "latestMajor", "allowPrerelease": false } diff --git a/version.json b/version.json index 3bb3f304b..c752fe966 100644 --- a/version.json +++ b/version.json @@ -1,4 +1,4 @@ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/master/src/NerdBank.GitVersioning/version.schema.json", - "version": "0.14.0" + "version": "0.15.3" }