From 5f0561b7fc0ac275b5151b306518d2be80815acf Mon Sep 17 00:00:00 2001 From: Bohica <53943479+Bohicatv@users.noreply.github.com> Date: Wed, 16 Jul 2025 20:41:21 -0700 Subject: [PATCH 01/47] feat: New Jail System (#2215) New Jail System for MUO ---------------------------- Commands: [jail [player] [reason] - Jail with time escalation per offense (5 to 120 minutes, GM only) [unjail [player] - Manual release from jail regardless of time (GM only) [jailinfo [player] - Check jail status and history (GM only) [jailrecord - Checks their own jail record stats (player access) Jail/Unjail can be found in the client view of a player in Admin Gump ![Screenshot 2025-06-12 030226](https://github.com/user-attachments/assets/a9f1a736-0316-464c-8958-c589ea0a1dcb) Jail record gump, invoked with [jailrecord (30 second cooldown) ![Screenshot 2025-06-12 030320](https://github.com/user-attachments/assets/c19b3e76-3042-48ae-a00d-c70ef564bc84) --- Projects/Server/Utilities/Utility.cs | 45 ++ Projects/UOContent/Gumps/AdminGump.cs | 43 +- ...rver.Systems.JailSystem.JailRecord.v0.json | 43 ++ .../Systems/JailSystem/JailRecord.cs | 25 + .../Systems/JailSystem/JailRecordGump.cs | 90 ++++ .../JailSystem}/JailRegion.cs | 0 .../Systems/JailSystem/JailSystem.cs | 427 ++++++++++++++++++ docs/commands/commands.7z | Bin 12531 -> 12652 bytes 8 files changed, 669 insertions(+), 4 deletions(-) create mode 100644 Projects/UOContent/Migrations/Server.Systems.JailSystem.JailRecord.v0.json create mode 100644 Projects/UOContent/Systems/JailSystem/JailRecord.cs create mode 100644 Projects/UOContent/Systems/JailSystem/JailRecordGump.cs rename Projects/UOContent/{Regions => Systems/JailSystem}/JailRegion.cs (100%) create mode 100644 Projects/UOContent/Systems/JailSystem/JailSystem.cs diff --git a/Projects/Server/Utilities/Utility.cs b/Projects/Server/Utilities/Utility.cs index 6ef4aa72c..448e9adf1 100644 --- a/Projects/Server/Utilities/Utility.cs +++ b/Projects/Server/Utilities/Utility.cs @@ -1479,4 +1479,49 @@ 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(); + } } diff --git a/Projects/UOContent/Gumps/AdminGump.cs b/Projects/UOContent/Gumps/AdminGump.cs index fa22ceca6..e9c91dba3 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; } @@ -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) 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/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..45b9ce172 --- /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}", 0xFFFF00, 5); + builder.SetHtmlText("jailCount", $"Jail Count: {_record.JailCount}", 0xFFFFFF, 5); + + if (_record.LastJailed > DateTime.MinValue) + { + builder.SetHtmlText("lastJailed", $"Last Jailed: {_record.LastJailed:yyyy/MM/dd HH:mm}", 0xFFFFFF, 5); + } + else + { + builder.SetHtmlText("lastJailed", "Last Jailed: Never", 0xFFFFFF, 5); + } + + if (string.IsNullOrWhiteSpace(_record.LastJailReason)) + { + builder.SetHtmlText("jailReason", "Jail Reason: None given.", 0xFFFFFF, 4); + } + else + { + builder.SetHtmlText("jailReason", $"Jail Reason: {_record.LastJailReason}", 0xFFFFFF, 4); + } + + if (_record.IsCurrentlyJailed) + { + builder.SetHtmlText("jailStatus", "Status: Currently Jailed", 0xFF6666, 5); + builder.SetHtmlText("jailTime", $"Jail Time: {(_record.JailEndTime - Core.Now).FormatTimeCompact()}", 0xFF6666, 5); + } + else + { + builder.SetHtmlText("jailStatus", "Status: Not Jailed", 0x00FF00, 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/docs/commands/commands.7z b/docs/commands/commands.7z index 0da10ed6dbbce289bc2c1f848a1a9f7b51288213..6db187201c68989111c3554f565e67943a071219 100644 GIT binary patch delta 6130 zcmVM#HR00000VgLXD00000Te<5s;gU8m;;|)$ z8Gl#RL=6JI&qzcwaZp&sx_4Y#z2YdrP?@h zSdeB8QU!G>(JV$c?xgR!WcSXFybY1wYjvu-WqoRY2V@*f0~S=2sG3Hn;~t!3aw0r zWdm#eQ!-$xE{%!6mFm!5m5#I(5G?kZf+G0hyxH#HNUlp&PM}>=38;EKiej&TYk!WL znwyPh@=pGcJ{T_1HoRXQxc6v(Y4SI>S@DVS0M1^6xuIPlpJDc^zxBHcEHdH>0XHkE zEH?%;^f z84Efc^=h}BV4d@2b?v{iA>d7m&hF96iQ?&;4}n* z@V?z-gZ^d7lgDZ@8@3pRs3Sma?ATG5mh}%NoQG(DXS=i)b??>uNs?b;RWz85pUQ4rAIN+I8cU8wdalS^qq`{)2XS!;#ba4D%`J)5LHA7p0CfrV zSQyj2pSGf$MnC0%B9#RWFqp-X`KqAjGqh-$Gh!{o%E?z_Qfq!40s4OO%&0o zIc}QWm!VaC^5A{;Dhh4WFHN?1YAK5r!UR53XErqPsCg2F30D26!+uEsyYtmwrYL0+ zQ;yW(9&#PmEr0u11U5g0PBHUAu?`qWu9)WdLOC6~YnU5M_ZbcGSVFiFMa^8|dFy&OLBfEJGD!=Tz>& zb}qv|v_rA;W@X5pC1zJ3=NU_G-2B*8#ua&Y-X-E!$fglyUm|K;1cDfzn;TyAr#K{Z`WAma$s*K9<@9YZTvRbD7SUigey^+)W5R&rS_{Ahp?%l1u+&+$Z( zEgfoHJAZgzlLTEW_}Qiwg8Qo%&+QWv4Vpo~&l(Y}`9~nx2BgVoW>r~m^GOfs$gb`3 zsEY6S)g~&@1+9o4K3QiR$Z`<@F)+#T58qB?0`GrvAY7$+_CEG##hA{`_2>e=x4dR} zYIG`?lnjpwU>TYJTpUL=N0$7oWzPvB=b4F+wk8oYQKF({8q?0blGdHx8g8ox!yz-A@BC~BMyd8)sb-cRG2i@c5J6WtDHdd zxt`dLxvj&6d@4*Q8`{9LnzN-e2M0|D^?#GmQh(}B)xn<{QZL=7rI+GkZCrf8z|VN$ z5Qiv!&%^CX7t2=sj$UVNRZ5tH)0uX?kYF{&fqm)|$;qm`&Ii>3}Xyjf9pj9=={qa(eKmx#&$%!faG zLKuPX17%?%RLh9T3U0)o71)gU9UAludoXWeH&8$L6=m3HG5u)5*>gUY<|anC7(jC) zVU6Z`G29(+fW==3Z{ciMQd%;qZGYtQ2>9g*P<>98sampE9flZ|POXu-t9s`I3T5qT z+BC|O=@1#h-f&DXM8r|_s6WPV5KB^bpyQW3Z>Qvp3TgF+)QT7~@O)dVE4YF)39P(T#FW&+L|xkTd_Xt$m!%;$zimfB z6`hmYiTIyzTE1F`OKmg9u*zqfY-jX;fD5gLLAi!xl?u{E9W{&(BNwm|v)MrU`gW-s zpXY9<=!OVxC@3G!+HH|ur+);;@F1Vabsr~7Af4>$QjPm?WEutdq!yDk-q`jJ4tL8e zL+myt$Tnc;9Z=4=5?XZ+g8=QPump2O@EID%-dTA-v#XK@qfHdVry8S@wZ*j$jl{(W zWO?aG;_;L>M|3CEY0}xr-f2SJioam>g7URFx91E}jH`cuW=5A3N`I6$xU)4iY(Tc$ z681fuXDiz~Y%>v&Plp_tt2nft9Q2MGdb=7559gW_Rqou;NBI4;VdmK_W!p+VTAU{K z_}a$LV@jFTlHiGEz3S04yn45+_GJq)w+_e^rV6B)cPVjM{E5b6uc_bjObsNLN%pGa zo;~-Dpd5#=!I}mNQ-4gO6DuI*Z^mWs^VfV-%-FgLte(F8yx=rI=HO9?a54Ss%xJgK z7{#WwAHsBF>CRvP271Q>A*2CwKv6@+I6?`yu6Bt(@&D`jXM!yn!Mgux=~l*yu|C>6gf-^ZNjE{%R^ir2g4mMQ(<~{q)A>6r-P;7HTE~E)?&P zr+E~OtOG;-<$oe~4;cTcl6Px43$+sMXEgQuuXZ~e)or_!1srka5v_^?s195d3TzgR z@G#_Nj{dcojR2=3icvNm`cPb|BskLjm(BMaUaj|>n3}lKs_}kkZWHOoaCnlS*#&IZ z37|&{2(Ppvn9a%>Xt5lrDNXPXX*pNLbbja(tS-Y7zJIJCkV=JX!Sb0!PY>Um3T;}w z%vs2hT|8y5ZX5fOVf7)67}^uJn&W`B4Cus(jQM+eJl1&i(=ObTM#p-&novw zK$E6xW%Y_|&p$07Wr>d)Gg1Av24B7B>9JfT=fnGQa6_AE$mvkX#R)L+B>(Wrws6_zE(Y45uck5kBU#l<|>yd3THoL6ICi|2T7aF|BXr zgd+6Paxe?V`;0DABwD>$6d`7dR4hD{!pljIXm!)~hpha13Z2M^Zas;aFB?kaFoxpj z)MU*g<(Oh3AipLFI?q)4ngEhi^s=WLZ`N4lVt@QZ!)KDB3cMW z*njoz=0$ov>Jo{yOTE=nr5 zj_3iE39H-iK(U9r`&p4z?{NU`zTNm$hd>qFbVfsEU_kgH8=`wBRMl}&UYzRSnI#Gq zDJ35@&LC*K4!|rPcUAsA6ZoonbM;IC$bUF<33?+rteqzzE8u;SN5KS@SO!)lHRZwE z9Y=&<+|22YN~_j0J-~@%jxpx+l>uBxk83Q{7(IZh=XGB5X`rNzM%TOoqFO363hC9hKQO;in?df#!M8@JN%OOl z>x|Jw-KJRwnmn{Qq|;g7^=|a7R|b&Gl-Yj#o#9>pIx4;Yv|sIpfxci24k`o5Nl0tt zu`Q8$O;9X?q4s(gkMH}VezS<26MvifaA;%G%}uX+W1u_#Jdwovi=Ef~e>NOOryTK< z3odNvZ%xO65t26BhI|l`sH7lEeOAP-`o&-$USkG!)fW(58!EUrDjIW0>-cXA(GJ~} z_T_?T)87g)p#uNyhEaPd=Wpxil?kXCNP)9E9bx_I>tc3@6HFhcD3?uEB?B1$GrW z!7xKk-GL3>D|Og77gd}r6^^Nrhq;6g=X<*yE@=91^_4vH_=pGrZHcg80i|HTHxBDTTlL;aB;(u=7z$-iWvUc-S zze^(%(N~LuDcvznR>#EEe(xZh z`)6jp9xr(2q3OxU>Lca9Zub#yzK%K5))K}8_Podrw64p=u5-cy!m_XaW?O#%RZP01 z67~$`t(Qd)gy3v$MSpOAlm)pYiOkM-7Fqp-SrC}2fnKJ~)vdon{nl~(+O-1Bt9A;* z9NVStZpk||4b3sITwhBbmogzF>Uo^@NR03?Rvz*)T25je3Orq!p*ovnP5ES}I)J z_^$H_)z;&N(P04wsu7%-UNnW2^q=z>;!#8Y-@r`tC0-^(ljVRMw*PY3$hV#2WqYA^ z1Im8@bSe4r=U}+}TTKgHG7acTI2})F%44-aZQf>S8;qXmeC=Rnqbw;qWJt?n%guyF zS2OfJyIB)xzkkk<10s3kD(RYeSZuA9xJpvYVWNM_KQ%Jl#208mTLnw@KK^g$-h2e= z3ec_7-}p54WcHB04H5ex599L(VvtGMfZ`MNiG>|l3-gER=HtGo>uTNOBrMW@A=n8C zsZuZ1PlPS8>m~=y30Y0<^@S5MLECzc&L~dJ|6Q}^a)0MIQG7?sH;=V4OV`1G`YN4c zhBY5veoqpO=x1?R5HiW=|gy3JD#EL%v6(~O1l!|oA9LkwHP_|A{A!;M8Z2|<4InNvZqzfJSAI-3wOfls? z+M4<6w10Ce3tHBouf+z3I^w8vaX5IF$IIn|GyHT;gfom1;sS15zY5p^}+tjqxWh1oX^8V7R zvPW%+E~ZM;)kt*ZbtIVJTd)$V?@c1R}l zv5!F_-$t3aDf+snl_Pp4&o)8A7&sVWE#T3zqi&9TWMSKKUXpih#*I7N<8*tyDKWfu z@h{DMK5Q{M5lDrgLmGRrxW}`_af6gKQbRosX>bllI7~C!Z*RL>`XF^8wfyH;DJU|^ z0e>@H?H6c>rj-VwqT9NxoX&jsomUi~pI7k$6m~Jd;I%Nl-IHtvmGFUN)o!2$ot(vE zoSytG%Qh9;q8q@-kq+foEyR z^@&bMM-1U5Wo%L%C}p&|#`;V;LMD4+Z-0O$V{}C%UudO9R^GGa1SK@Xc5R{1sl+24 z=hmsS9kIJ0V2DbZmVb>K%&;yUC9Q1nrZ=j&YF_40w)f>#a~BcqE}@iE z({l`b=v1b7srlS$N*Pe1z&%JyFwot*Yz;{gY1mTH_kss0peS|4oj zoA8_d=E*OJen8SrgUJ5TT}BNEj(rp^<*1oLgUH2@X$tynu7f>SlDb_14}TbI15hbm z`TVFhO3=)4L|KLZ(ntdbV-ogP|9h$q<^zA0JicoEwOcs5?I|Y?GP3eBntXp-y8no9 zLEyHsS&9Q#a>J^syO4N>3C=);jI@QwykE7tD=*8-`@v&}!*MSOh|%wyV-$wp!6on@ z;AN$VAQm9ayap0m$%d|*hks8n0oc^ibs`Zj4PaMcjmGCQfNOfm?~HNkEm5-^Xh_Rg z@XNUq6J9fN2Jhf5FVQVuXmH^cHE2UZrn^?~bJPCq0SQiOrnDfhOqz{G(P$;S2?FOI0Q?;&Y1ip=7naQF__#wHe}2x_4A2=#>{&r0MeMI(k_nu zDIRgMz!Icdic{PuF@J;k;00K?WGiLUEpvkpGVbPw>a+RJK9O|W3iyRl=g-&^=rhmD zRd7Nik1uw-<#tzH>dcHhtR~ezte9wPJ00AK(0SXMkHaPwFftM}DX62%_S*p!1_1yd00000 E0HkotY5)KL delta 6008 zcmV-;7l-KVV)J1oH+sCUCma9-y6NipaWDV?00000VgLXD00000V&zNJ;f@M0X|W}S z8Gl&YAFOW2$Nx1i=aS#~zNz6lOmDV^jL8dw!RYLlmG3gM)uVvOaKV0xBjyK9jVpd} z@GsjwK_J}vBv8Z$0p4Usl={&ls_t;#$8%i#)n>LTaXD(LAFBvoYE^vd0txiDB)9_& z^A+4b;HU99;^>XZ4CARd3J5EPM|Qtjjel0`U}5ofOwx0le&Le-pK`(r&o`mXKpzz% z)9=}+enViZ*z$_qs~LcEUwWn!FsGIWyYX0V$LfW`qW{;rV9A++@%Ndf-Wx6U%}erw z6i~;3I!?mcCn|BmP;Au)2bVCXg#dfh*%H3BV(8*1nRUl1x!t&szN;w0|h8 z*GV5?R{kwZo-oppkr_-7i_qF_&Ns=CSg1=YAdK~l>3HBmYqD0MFV~M=FVo6+sGeN) z4V~Xez((%WkNA5HfZN4tA1)j3xSgHkS@9`A8#ecy&*hwWQ-(V*Wko@OpP9L4Xz0+q zN3(=a2R%@XNN5$dJ?@e4|BP8{vVR0XP*yoRPJa}VYhXO#@5b>Hwj%Y+^+Z3~hR?#n_*q%lz#h{1a{eA_C^8aX+Wk~hZY08ltr6I0 zx5IuB=d#+!vuLHp8qO~Cn(-aWjmF*aWdS>zyfFGBNuJW3N{h}L(>eco7k`k^uHGJL zM?R=)dwp6yLz)y^pwp`ewbZN42SvQsP-)H&0$qe4$#1%^?=hT_f9&68-%bPD2gIc+ zh%`Yvu*#W}Z9)EG@PUVDte7b@)6qj5Fy|r$)wr9o2Yw zvA9t(QG#Dcm~)Cxq0IxKhJP8>8=J7_-pA>&!a0b~HkfuubL5`2l?>V#`~@V+wE$qP zvhhr?e{~Kv?!JUpAPs#^GnR4&$DDu1HW^)4A6+@fTb{(5w}XWi`Rh*!i(6oc-!%I5 zy$pb?ZfGzOfbP9w@4Us*?-JfUSp`62}Uhp}UzCh)eZ=p`XDZU?EQ z;7*(>G~~Xiz9E&ySHO}r*IuW4Z_ZHndc`%YSCpl9e zaHSe*FGyY^s}4SqvVVn&vIJ>MfDTW;8`eAFu@H~{FI`Lpx=WIgJQ19`h*(R(vc^%^ zZ-ex-Qs?9=k9$*4sQ{P^a8RHvS&!F!*b|$pV}Qi6dp(sdWCSd(xBXR!44T;wH5d91 zExB~_y?e2T!XX}Pg~NPx`*@!^iPe5cuQSvd^6tu7tC?@3D}ST5(WF}nLJ}`%$tO<^ zf}{e1mXT528jip#bgPc~eAiXI#YoXA$d`iCLq zls@F78Z!vy27e#su{f-A%otMx;_oGiw@=zdj&9jE=l3eOb#2MX4W;2RZ$&yt&82|Sj|f?sIev)IPnHYzZ(Ir54E)Z(@wb`taOfP-BAuh-I<(NU8?E=LUJ`b^81;H3{1H$*%ew90{NXrC z-8Oo)Dq*PvYGq`-B99G7tvHqT571i zoYFWjk>41chtUoB}g3o2u7y=&h|WHCfR4-j$U z01^5EC2tK1!iRu54-Q&GO?-oj(iY04RX^^a0qWPD*XmD3^c&AV*VDY~Q#0i7u1zu> zQTFZb!wrZ`3r@xk6U(wXVRp!FON!%LWh-{8q-?4|KpVXK#UB*V4ZI>cf#%J=gi?*?c^OlS*fE3XG4~6{7uftsga3x zhf*My$vb8M4IHIOYMqrzd04nxWN3}>!{5voE(3b>w720y24ic4W+&4xwQ~|6hu6RV z*1TrpyoqQ)o``HWw;1JgSJLeDd$#B6AAbu5mMKr%PAEtrY}KsHdH+EO&jB^=3vTFX zV!u&g6M1)%fzu6%7NdwH&u5G;wYXg0Ke3h28q$~Yq2Y|y|F+sGZ|fv-_0=+t@#$ks z`X+$pdrfYAF${?h;Z^c)#%!4E?@FD)W#JkK8&ReV$EkNXIo?Xb3529zUALZD4u3Po zwaAJwwL_3vQg;e#aES~Wx)s5QEkcp(Sk#}$RkOA0_DEpWrIhYfDW^1oy+&kVL2c~8 zKJa5IDRJu8vFnxEc&vv#mHd!(kWt@0;X$>qygw&9e&I!mPf=$rbPlBg-vATTG=T22 z4^qjs4>OhY`J1p9@|sG&A}|Q~VSm@NlZxBy@lANzp?isLyfmy>Qks<6}zgN^}h^fKeR(?!Q+Ev^ZH#+l#;D$+|7g?kPaeKgSk+E4tpO*uno)JTA zfaA}}SK7{lU6d~LnY~Q;{G4ADI=~TfkHQZNs+AJ*5`!4uFE&N=$?~Gda;Oc?9^Ztr z^r+WkEOZ;mQ;y2^(`LC~ntxpL#L^pP>M~C76pss(-291zB7Mh;>Apr*ZGS^(GsqY6 zdO5(Q+g1^K=i*FkDU(|u!*%Y`%7TVe2mV_$ctfBPIwc$QNcu*1t4_PI%TxkqUIkIB zaDgalXL@N$mJ(t0s4(n-Y&`$(f3^;yv?=-pKK!V-Qr;1rsMr`bHh+Vr;@j&_ixMVY zHWg7G!veC5!#-H&NE^+48rw}(uAcrE+L0|4qUeZ`rdr|<#Fkc*^RlRh*Ov_{T$G0C zD^$fHzwnEK1t9uNEL(5x^KdY{C}dzuieaI5g!JL!1t8t zv3C^TF6+QKFqWPh)K6>$4r|enZfm(SH=ADYDmel_aPKBqh(vkGBnrp?k<}wu?&?;d zHa#|xyFyk9mV(Z*2HDlCZWf*d*dV^~+=+G4TOL_3TP9u5v@yl1(Wa=pjFk?n0F5bg z=Vg5^xc>`opMMOZ)2kHP^Os0!fRjH341oFeO!Arb4{IpuxZ3BgDEZGTws{!=VR2 z?{q@x7g1wbg#*)jgyIQLYf!isAVO>JsPYm{TCWFbNPjB8bVptmSvxx9RxyN5e)uc_ zqxeTkc7|=#?EPc@6kTt-X9TG1G8lW}9mK_*xgQV79z>y|DfSH{7*L0a*A5}HN$v}O zD4s2=le_b+zNJ;Eh25^Dr|mnvTQF>kYf5i7AMVskI34h2X~RsR-iB>swTf zMrLT_Zhy<%sm35T!^?Bp1cUhIPBkhDvra5TJ}%)*ZEF|CLEW*B7S&~2UQ}?r5RI4+ zdV(eC(Wh8)baPyn7?CvqcRxi4N2wEcPqU*C>ZZz>rd9TTzA#FOX6_wn^|MP`I$d5+n!DOjpp%C*n6s4aR;cYQa(a%Wo`TJcc-1%GOe&xSC|g2|tCf!7K+#!u|I1_nQH zKW#uh;%OCRGEK?(AM>ELJQW*b1^mOslqBd@_~xlG)%r$;op}Vz!r5u zH-B&62$ZlI8QW-G4^>k$^Z3cd#{1PlyzdpueL#DnWe;4NlOn_IU*`)6ytit;kQXe5 zc%iQ@_-b?`lpvk#FZb!g2>5pUgCqY@hOTQO{sVsYj+eUR{)?f&jQC=E{G0XDVO0gF zv|5gNaY0(wSbM>*jFi08f!z`%VzSQuwSOW~OCO+-Gy=2Rsk<|}sPWfPyJH{QB&*$= zMs#cZ4Y`=gmJWw3xW5Lj#{J>S`LsSH9jA3PWtIU4esC6MwRQ@3H6|kw4zU&Sz|Vpp z4};83s8~m0KwGvkmT_Y`sFlaS%uD${aCRo2UsP|EQ6?OtvTgekiS{`hZWpWAp?^`s zmjlPL0DcHK=v5Sy0y04!J|%%Zr(8=?fQxUkYVyokSPN1i>0pPEP1tE6>}A1$9on8* zkKwJ{yb1N!96RSXBnvGf5E(3}TV6!tM?AEVK=KPyCJ${EY~3rdZz?Ht!r;^v5%QaO z)&>r#aDgl>rnYf-*+o!)@Q2xqVSkIMXOj+?fQaWvB%65RM-QqFrwM3=jp*|62}Lam zjGsbUN_|f>0t&s=S)kCC6D1f5F-<1Bm{**)?T5Tu7App>kvK z4_dh_=W!P+(Sn#(W{%$QKNI6%9;XKQhwpUmv)l<%DRb0sY=<87( zr`&m3zLltaga00nV*Si~wAsu#h8?iXFyA;ij|6YicjN<{D`0hFDC!B0fa;$G718Z?nxZnl6KB*8rO5FH6U!ZiF>1*0XCtA10oMZ#EJVcFA zE?A8p%i`cVd6zrZfba`)O^Zw%-#{b_jb`;$T1GV;a57ChiW;q^wng;l@etF`zc^xT zTn;nx0i!tm?SJDr@|Fh2nnji=OovoyKL$L9Gf0NWcvrwb&YoULVD4+ngpD#?uWRR~ zynRLF(qJOXB^+HD2v31jws%|-TXn+`j z>>6KudAGmkX_%rW3$cPFHx`Umk%yj10Wa*jr~>9acs~-TwtVN#&?=`eo0gADU)Ra{ zsB5@TiFXKtPGOqR*5NB}L$3eB)K}jG3GU}0Ucb%K{$p$dzY>D0*`1pDGmFQl;=u@N zC&dEintw4AN-TmEXN{T6b4F^|KhsP~`teI_QIsQ#g<SHO2|ww&ThU3+{|J;n_5m*;u%0rsW6}{JC~~|n}vPyRxix= zNtkaCq!DOK1KB2u-)9ZALQ!{<)AA3|;U3O;uz%1M^R_cmh<+OD1n!DhAq0cA&tX?F zx5#ZfgNZ&sq6x}LNk0IKvdZu^f>N4W)lF_B^JwJ)3fm74t4~tzFkF9Jfc*$tB3+-2 zrtC&mCt22F;86T}(ZgpYAgH2{C5qW$iynUoq%FN4H(Kz*ftoqd-oH`P2#%!kDw=IU z@PB_yRk90e2yukfNtdh?|EZLjp5H=NEo^oN6e3&AL&|B91m%p33pqDGnd$xd{FoFk zXa33RMI39I+)cQgUmHHx@C|#rV+cSlE+oF|pzUu}T)t=SHo?0SJoGar?zT6h9ibKv zCq6H%3*+Zt?hAd*_a$KE{s>oZ5TJuWGJjvmdvar?(%4MPWu8mQyQEG8bR$>B(@rzn z(Tg3fVnezD44lSsJ~0r!*k4n;=${u}`$GO7F2J}yY#SMFOL_c(o^0*DchNtZ1k|1$ ztsVhpW7L*bT|{JEM@7)-nL<6_cIy107e4mo=eT{v`e}0Pt#`J%9jSQ3=nuqulYjMy z3v+)NbZcjDUiV-p6<^g*RyBL(JR9WIoYslj<@z9 zT>@^^=(4O~sxVYPc6_6RQjM}Ri}-r@nrz1aCrYHw9C+{`sQub6El^(;WXQ@Fg~OFT z(FflWLb?oe14)Rd++0oK>#~fCOMgiDm5AY<&$LIeBP*q!=2VmGNvcWiV)Ibkk);X_ zL1J}fj|Vj~tm3|&)}VpiMXRJ9EE9x=hSAWRKs=q&5zfOfV%bL&h%>CV+MemN5HZxo zj1uH|5bM33fnL&@Vd#Gyhzyt^k$^`hihQ+~h6z~~+u2{D9mE_(o~|fNAudLk`Z@I zr#oFv%Ox+RKUbX23#kL-T`|I|-&eRweH3E)ITdkAJSDCt_s5ZQN58XNlMl~|3V8%! zhNQfNfZzHVd3~O9pZo210DrP)QSh)nT$#waWrudZI9*vY^=GOzx*x4hwDj!UX_?-e z0i~?zd&fFn>uVq%eXWAo)&kA37>^q06O}jMH%8b4_r09UObQ@hOn|9EUQ+z5R0}t2 zp1_3`hAj!-lUtjdwd5p&6c#g=JDPvQU{P~iF3S@(N zZA$YTLmf|*#Z&^UK9?iZ7q(0}@si$G|I?+$#~Hn=_T4Ki5o?q#|L|ZuWzesfj*2M< z3OU5*JVGW8*c0a(GcvSbu^4L~n*Qu<6OjK_D_de_000351^@vGuyFtf3jqKDAt3<@ m48aSI00;^J?D*@flPEDV5}D>$7VYla0Tl)T03ZMW0002`pO5kY From c1af2ade833581ff1008205db7050132d4b06cc1 Mon Sep 17 00:00:00 2001 From: Bohica <53943479+Bohicatv@users.noreply.github.com> Date: Wed, 16 Jul 2025 21:34:43 -0700 Subject: [PATCH 02/47] fix: Fixes pet validation for resurrection spell (#2197) --- Projects/UOContent/Spells/Eighth/Resurrection.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) 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. From 2086e9b292b1cd5484c9dc9a02b44c05da1170cf Mon Sep 17 00:00:00 2001 From: Bohica <53943479+Bohicatv@users.noreply.github.com> Date: Wed, 16 Jul 2025 21:55:47 -0700 Subject: [PATCH 03/47] fix: Fixes direction snapping when pathing to combatant for MeleeAI (#2187) * fix: Fixes direction snapping when pathing to combatant * Fixes speed when exiting out of flee action --- Projects/UOContent/Mobiles/AI/MeleeAI.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Projects/UOContent/Mobiles/AI/MeleeAI.cs b/Projects/UOContent/Mobiles/AI/MeleeAI.cs index 5bb697a1b..f431b1d23 100644 --- a/Projects/UOContent/Mobiles/AI/MeleeAI.cs +++ b/Projects/UOContent/Mobiles/AI/MeleeAI.cs @@ -81,7 +81,11 @@ public class MeleeAI : BaseAI if (!MoveTo(combatant, true, m_Mobile.RangeFight)) { - m_Mobile.Direction = m_Mobile.GetDirectionTo(combatant); + if (m_Mobile.InRange(combatant, 1)) + { + m_Mobile.Direction = m_Mobile.GetDirectionTo(combatant); + } + if (AcquireFocusMob(m_Mobile.RangePerception, m_Mobile.FightMode, false, false, true)) { if (m_Mobile.Debug) @@ -175,6 +179,8 @@ public class MeleeAI : BaseAI m_Mobile.DebugSay("I am stronger now, so I will continue fighting"); } + m_Mobile.PlaySound(m_Mobile.GetAttackSound()); + m_Mobile.CurrentSpeed = m_Mobile.ActiveSpeed; Action = ActionType.Combat; } else From 416e1be73539c5b063f257f2923eea2092d32aab Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Fri, 18 Jul 2025 09:24:41 -0700 Subject: [PATCH 04/47] fix: Adds GetDistanceToSqrt for Point3D and centralizes all the calls (#2233) --- Projects/Server/Mobiles/Mobile.cs | 32 ------------ Projects/Server/Utilities/Utility.cs | 49 +++++++++++++++++++ .../Dark Tides/Mobiles/SummonedPaladin.cs | 2 +- Projects/UOContent/Mobiles/BaseCreature.cs | 4 +- .../Monsters/Humanoid/Magic/SavageShaman.cs | 2 +- .../Monsters/Misc/Melee/AnimatedWeapon.cs | 2 +- .../Monsters/Misc/Melee/BladeSpirits.cs | 2 +- .../Monsters/Misc/Melee/EnergyVortex.cs | 2 +- 8 files changed, 56 insertions(+), 39 deletions(-) diff --git a/Projects/Server/Mobiles/Mobile.cs b/Projects/Server/Mobiles/Mobile.cs index f217b0fe1..48ef9a59c 100644 --- a/Projects/Server/Mobiles/Mobile.cs +++ b/Projects/Server/Mobiles/Mobile.cs @@ -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) diff --git a/Projects/Server/Utilities/Utility.cs b/Projects/Server/Utilities/Utility.cs index 448e9adf1..d405fabe8 100644 --- a/Projects/Server/Utilities/Utility.cs +++ b/Projects/Server/Utilities/Utility.cs @@ -1524,4 +1524,53 @@ public static partial class Utility 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/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/Mobiles/BaseCreature.cs b/Projects/UOContent/Mobiles/BaseCreature.cs index 22c43ebc4..0957a8c22 100644 --- a/Projects/UOContent/Mobiles/BaseCreature.cs +++ b/Projects/UOContent/Mobiles/BaseCreature.cs @@ -2439,7 +2439,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 +2461,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) { 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; From 8f88de68a7cd84c1a9b2b383dbcc7c4c5a2dd759 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Fri, 18 Jul 2025 17:50:32 -0700 Subject: [PATCH 05/47] fix: Standardizes the variables for AI (#2234) --- .../Factions/Mobiles/Guards/GuardAI.cs | 94 +- .../Mobiles/AI/AIControlMobileTarget.cs | 14 +- Projects/UOContent/Mobiles/AI/AnimalAI.cs | 74 +- Projects/UOContent/Mobiles/AI/ArcherAI.cs | 62 +- Projects/UOContent/Mobiles/AI/BaseAI.cs | 1298 ++++++++--------- Projects/UOContent/Mobiles/AI/BerserkAI.cs | 50 +- Projects/UOContent/Mobiles/AI/HealerAI.cs | 75 +- Projects/UOContent/Mobiles/AI/MageAI.cs | 574 ++++---- Projects/UOContent/Mobiles/AI/MeleeAI.cs | 106 +- .../UOContent/Mobiles/AI/OppositionGroup.cs | 8 +- Projects/UOContent/Mobiles/AI/PredatorAI.cs | 52 +- Projects/UOContent/Mobiles/AI/ThiefAI.cs | 96 +- Projects/UOContent/Mobiles/AI/VendorAI.cs | 58 +- Projects/UOContent/Mobiles/BaseCreature.cs | 4 +- .../UOContent/Spells/Ninjitsu/MirrorImage.cs | 6 +- 15 files changed, 1284 insertions(+), 1287 deletions(-) diff --git a/Projects/UOContent/Engines/Factions/Mobiles/Guards/GuardAI.cs b/Projects/UOContent/Engines/Factions/Mobiles/Guards/GuardAI.cs index 804ddbef7..85662a887 100644 --- a/Projects/UOContent/Engines/Factions/Mobiles/Guards/GuardAI.cs +++ b/Projects/UOContent/Engines/Factions/Mobiles/Guards/GuardAI.cs @@ -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() @@ -396,33 +396,33 @@ namespace Server.Factions m_Mobile.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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"My move is blocked, so I am going to attack {m_Mobile.FocusMob.Name}"); + Mobile.DebugSay($"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 if (Mobile.Debug) { - m_Mobile.DebugSay("I am stuck"); + Mobile.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 +430,7 @@ namespace Server.Factions public override bool Think() { - if (m_Mobile.Deleted) + if (Mobile.Deleted) { return false; } @@ -438,32 +438,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 +544,7 @@ namespace Server.Factions Action = ActionType.Combat; } - m_Mobile.SetCurrentSpeedToActive(); + Mobile.SetCurrentSpeedToActive(); m_Guard.Warmode = true; RunTo(toFollow); @@ -556,7 +556,7 @@ namespace Server.Factions Action = ActionType.Wander; } - m_Mobile.SetCurrentSpeedToPassive(); + Mobile.SetCurrentSpeedToPassive(); m_Guard.Warmode = false; WalkRandomInHome(2, 2, 1); @@ -582,9 +582,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; diff --git a/Projects/UOContent/Mobiles/AI/AIControlMobileTarget.cs b/Projects/UOContent/Mobiles/AI/AIControlMobileTarget.cs index 063fc5221..6e12837e4 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,7 +14,7 @@ public class AIControlMobileTarget : Target order == OrderType.Attack ? TargetFlags.Harmful : TargetFlags.None ) { - m_List = new List(); + _list = new List(); Order = order; AddAI(ai); @@ -24,9 +24,9 @@ public class AIControlMobileTarget : Target public void AddAI(BaseAI ai) { - if (!m_List.Contains(ai)) + if (!_list.Contains(ai)) { - m_List.Add(ai); + _list.Add(ai); } } @@ -34,10 +34,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..481b22747 100644 --- a/Projects/UOContent/Mobiles/AI/AnimalAI.cs +++ b/Projects/UOContent/Mobiles/AI/AnimalAI.cs @@ -19,25 +19,25 @@ 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) + if (Mobile.Debug) { - m_Mobile.DebugSay("I am low on health!"); + Mobile.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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name}, attacking"); + Mobile.DebugSay($"I have detected {Mobile.FocusMob.Name}, attacking"); } - m_Mobile.Combatant = m_Mobile.FocusMob; + Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; } else @@ -50,52 +50,52 @@ 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) + if (Mobile.Debug) { - m_Mobile.DebugSay("My combatant is gone!"); + Mobile.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, true, Mobile.RangeFight, Mobile.RangeFight)) { - if (m_Mobile.GetDistanceToSqrt(combatant) > m_Mobile.RangePerception + 1) + if (Mobile.GetDistanceToSqrt(combatant) > Mobile.RangePerception + 1) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I cannot find {combatant.Name}"); + Mobile.DebugSay($"I cannot find {combatant.Name}"); } Action = ActionType.Wander; return true; } - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I should be closer to {combatant.Name}"); + Mobile.DebugSay($"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) + if (Mobile.Debug) { - m_Mobile.DebugSay("I am low on health!"); + Mobile.DebugSay("I am low on health!"); } Action = ActionType.Flee; @@ -103,11 +103,11 @@ public class AnimalAI : BaseAI } } - if (m_Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) + if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I used my abilities on {combatant.Name}!"); + Mobile.DebugSay($"I used my abilities on {combatant.Name}!"); } } @@ -116,19 +116,19 @@ 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) + if (Mobile.Debug) { - m_Mobile.DebugSay("Well, here I am safe"); + Mobile.DebugSay("Well, here I am safe"); } Action = ActionType.Wander; @@ -136,9 +136,9 @@ public class AnimalAI : BaseAI } else { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("I have lost my focus, lets relax"); + Mobile.DebugSay("I have lost my focus, lets relax"); } Action = ActionType.Wander; @@ -149,9 +149,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..660ca702f 100644 --- a/Projects/UOContent/Mobiles/AI/ArcherAI.cs +++ b/Projects/UOContent/Mobiles/AI/ArcherAI.cs @@ -11,19 +11,19 @@ public class ArcherAI : BaseAI public override bool DoActionWander() { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("I have no combatant"); + Mobile.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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name} and I will attack"); + Mobile.DebugSay($"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 +36,62 @@ 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) + if (Mobile.Debug) { - m_Mobile.DebugSay("My combatant is gone, so my guard is up"); + Mobile.DebugSay("My combatant is gone, so my guard is up"); } Action = ActionType.Guard; return true; } - if (Core.TickCount - m_Mobile.LastMoveTime > 1000 && !WalkMobileRange( + if (Core.TickCount - Mobile.LastMoveTime > 1000 && !WalkMobileRange( combatant, 1, true, - m_Mobile.RangeFight, - m_Mobile.Weapon.MaxRange + Mobile.RangeFight, + Mobile.Weapon.MaxRange )) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I am still not in range of {combatant.Name}"); + Mobile.DebugSay($"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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I have lost {combatant.Name}"); + Mobile.DebugSay($"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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I used my abilities on {combatant.Name}!"); + Mobile.DebugSay($"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 +100,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 +116,14 @@ 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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name}, attacking"); + Mobile.DebugSay($"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 index a77e36d8f..8ddf00d32 100644 --- a/Projects/UOContent/Mobiles/AI/BaseAI.cs +++ b/Projects/UOContent/Mobiles/AI/BaseAI.cs @@ -44,7 +44,7 @@ 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 = + private static readonly SkillName[] _keywordTable = { SkillName.Parry, SkillName.Healing, @@ -96,21 +96,21 @@ public abstract class BaseAI SkillName.Meditation }; - protected ActionType m_Action; + protected ActionType _action; - public readonly BaseCreature m_Mobile; + public BaseCreature Mobile { get; } - private long m_NextDetectHidden; - private long m_NextStopGuard; + private long _nextDetectHidden; + private long _nextStopGuard; - protected PathFollower m_Path; - public Timer m_Timer; + protected PathFollower _path; + public Timer _timer; public BaseAI(BaseCreature m) { - m_Mobile = m; + Mobile = m; - m_Timer = new AITimer(this); + _timer = new AITimer(this); bool activate; @@ -133,7 +133,7 @@ public abstract class BaseAI if (activate) { - m_Timer.Start(); + _timer.Start(); } Action = ActionType.Wander; @@ -141,40 +141,40 @@ public abstract class BaseAI public ActionType Action { - get => m_Action; + get => _action; set { - m_Action = value; + _action = value; OnActionChanged(); } } public long NextMove { get; set; } - public virtual bool CanDetectHidden => m_Mobile.Skills.DetectHidden.Value > 0; + public virtual bool CanDetectHidden => Mobile.Skills.DetectHidden.Value > 0; public virtual bool WasNamed(string speech) { - var name = m_Mobile.Name; + var name = 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)) + if (!from.Alive || !Mobile.Controlled || !from.InRange(Mobile, 14)) { return; } - var isDeadPet = m_Mobile.IsDeadPet; + var isDeadPet = Mobile.IsDeadPet; - if (from == m_Mobile.ControlMaster) + if (from == 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) + if (Mobile.CanDrop) { list.Add(new InternalEntry(6109, 14, OrderType.Drop, !isDeadPet)); // Command: Drop } @@ -184,7 +184,7 @@ public abstract class BaseAI 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) + if (!Mobile.Summoned && 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 @@ -193,7 +193,7 @@ public abstract class BaseAI list.Add(new InternalEntry(6118, 14, OrderType.Release, true)); // Release } - else if (m_Mobile.IsPetFriend(from)) + else if (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 @@ -203,13 +203,13 @@ public abstract class BaseAI 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) + if (Mobile.Deleted || !Mobile.Controlled || !from.InRange(Mobile, 14) || from.Map != Mobile.Map) { return; } - var isOwner = from == m_Mobile.ControlMaster; - var isFriend = !isOwner && m_Mobile.IsPetFriend(from); + var isOwner = from == Mobile.ControlMaster; + var isFriend = !isOwner && Mobile.IsPetFriend(from); if (!isOwner && !isFriend) { @@ -249,25 +249,25 @@ public abstract class BaseAI public virtual void OnAggressiveAction(Mobile aggressor) { - var currentCombat = m_Mobile.Combatant; + var currentCombat = Mobile.Combatant; if (currentCombat != null && !aggressor.Hidden && currentCombat != aggressor && - m_Mobile.GetDistanceToSqrt(currentCombat) > m_Mobile.GetDistanceToSqrt(aggressor)) + Mobile.GetDistanceToSqrt(currentCombat) > Mobile.GetDistanceToSqrt(aggressor)) { - m_Mobile.Combatant = aggressor; + 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 || + if (Mobile.Deleted || !Mobile.Controlled || !from.InRange(Mobile, 14) || from.Map != Mobile.Map || !from.CheckAlive()) { return; } - var isOwner = from == m_Mobile.ControlMaster; - var isFriend = !isOwner && m_Mobile.IsPetFriend(from); + var isOwner = from == Mobile.ControlMaster; + var isFriend = !isOwner && Mobile.IsPetFriend(from); if (!isOwner && !isFriend) { @@ -281,9 +281,9 @@ public abstract class BaseAI if (order == OrderType.Attack) { - if (target is BaseCreature creature && creature.IsScaryToPets && m_Mobile.IsScaredOfScaryThings) + if (target is BaseCreature creature && creature.IsScaryToPets && Mobile.IsScaredOfScaryThings) { - m_Mobile.SayTo(from, "Your pet refuses to attack this creature!"); + Mobile.SayTo(from, "Your pet refuses to attack this creature!"); return; } @@ -298,15 +298,15 @@ public abstract class BaseAI if (target is BaseFactionGuard) { - m_Mobile.SayTo(from, "Your pet refuses to attack the guard."); + Mobile.SayTo(from, "Your pet refuses to attack the guard."); return; } } - if (m_Mobile.CheckControlChance(from)) + if (Mobile.CheckControlChance(from)) { - m_Mobile.ControlTarget = target; - m_Mobile.ControlOrder = order; + Mobile.ControlTarget = target; + Mobile.ControlOrder = order; } } @@ -317,59 +317,59 @@ public abstract class BaseAI return true; } - if (from.Alive && m_Mobile.Controlled && m_Mobile.Commandable && - (from == m_Mobile.ControlMaster || m_Mobile.IsPetFriend(from))) + if (from.Alive && Mobile.Controlled && Mobile.Commandable && + (from == Mobile.ControlMaster || Mobile.IsPetFriend(from))) { return true; } - return from.Alive && from.InRange(m_Mobile.Location, 3) && m_Mobile.IsHumanInTown(); + return from.Alive && from.InRange(Mobile.Location, 3) && Mobile.IsHumanInTown(); } public virtual void OnSpeech(SpeechEventArgs e) { - if (e.Mobile.Alive && e.Mobile.InRange(m_Mobile.Location, 3) && m_Mobile.IsHumanInTown()) + if (e.Mobile.Alive && e.Mobile.InRange(Mobile.Location, 3) && Mobile.IsHumanInTown()) { if (e.HasKeyword(0x9D) && WasNamed(e.Speech)) // *move* { - if (m_Mobile.Combatant != null) + if (Mobile.Combatant != null) { // I am too busy fighting to deal with thee! - m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501482); + Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501482); } else { // Excuse me? - m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501516); + Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501516); WalkRandomInHome(2, 2, 1); } } else if (e.HasKeyword(0x9E) && WasNamed(e.Speech)) // *time* { - if (m_Mobile.Combatant != null) + if (Mobile.Combatant != null) { // I am too busy fighting to deal with thee! - m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501482); + Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501482); } else { - Clock.GetTime(m_Mobile, out var generalNumber, out _); + Clock.GetTime(Mobile, out var generalNumber, out _); - m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, generalNumber); + Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, generalNumber); } } else if (e.HasKeyword(0x6C) && WasNamed(e.Speech)) // *train { - if (m_Mobile.Combatant != null) + if (Mobile.Combatant != null) { // I am too busy fighting to deal with thee! - m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501482); + Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501482); } else { var foundSomething = false; - var ourSkills = m_Mobile.Skills; + var ourSkills = Mobile.Skills; var theirSkills = e.Mobile.Skills; for (var i = 0; i < ourSkills.Length && i < theirSkills.Length; ++i) @@ -377,7 +377,7 @@ public abstract class BaseAI var skill = ourSkills[i]; var theirSkill = theirSkills[i]; - if (skill?.Base >= 60.0 && m_Mobile.CheckTeach(skill.SkillName, e.Mobile)) + if (skill?.Base >= 60.0 && Mobile.CheckTeach(skill.SkillName, e.Mobile)) { var toTeach = skill.Base / 3.0; @@ -397,10 +397,10 @@ public abstract class BaseAI if (!foundSomething) { - m_Mobile.Say(1043058); // I can train the following: + Mobile.Say(1043058); // I can train the following: } - m_Mobile.Say(number); + Mobile.Say(number); foundSomething = true; } @@ -409,7 +409,7 @@ public abstract class BaseAI if (!foundSomething) { - m_Mobile.Say(501505); // Alas, I cannot teach thee anything. + Mobile.Say(501505); // Alas, I cannot teach thee anything. } } } @@ -429,53 +429,53 @@ public abstract class BaseAI { var index = keyword - 0x6D; - if (index >= 0 && index < m_KeywordTable.Length) + if (index >= 0 && index < _keywordTable.Length) { - toTrain = m_KeywordTable[index]; + toTrain = _keywordTable[index]; } } } if (toTrain != (SkillName)(-1) && WasNamed(e.Speech)) { - if (m_Mobile.Combatant != null) + if (Mobile.Combatant != null) { // I am too busy fighting to deal with thee! - m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501482); + Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501482); } else { - var skills = m_Mobile.Skills; + var skills = Mobile.Skills; var skill = skills[toTrain]; - if (skill == null || skill.Base < 60.0 || !m_Mobile.CheckTeach(toTrain, e.Mobile)) + if (skill == null || skill.Base < 60.0 || !Mobile.CheckTeach(toTrain, e.Mobile)) { - m_Mobile.Say(501507); // 'Tis not something I can teach thee of. + Mobile.Say(501507); // 'Tis not something I can teach thee of. } else { - m_Mobile.Teach(toTrain, e.Mobile, 0, false); + Mobile.Teach(toTrain, e.Mobile, 0, false); } } } } } - if (m_Mobile.Controlled && m_Mobile.Commandable) + if (Mobile.Controlled && Mobile.Commandable) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("Listening..."); + Mobile.DebugSay("Listening..."); } - var isOwner = e.Mobile == m_Mobile.ControlMaster; - var isFriend = !isOwner && m_Mobile.IsPetFriend(e.Mobile); + var isOwner = e.Mobile == Mobile.ControlMaster; + var isFriend = !isOwner && Mobile.IsPetFriend(e.Mobile); if (e.Mobile.Alive && (isOwner || isFriend)) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("It's from my master"); + Mobile.DebugSay("It's from my master"); } var keywords = e.Keywords; @@ -495,10 +495,10 @@ public abstract class BaseAI break; } - if (m_Mobile.CheckControlChance(e.Mobile)) + if (Mobile.CheckControlChance(e.Mobile)) { - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.Come; + Mobile.ControlTarget = null; + Mobile.ControlOrder = OrderType.Come; } return; @@ -516,20 +516,20 @@ public abstract class BaseAI break; } - if (m_Mobile.CheckControlChance(e.Mobile)) + if (Mobile.CheckControlChance(e.Mobile)) { - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.Guard; + Mobile.ControlTarget = null; + Mobile.ControlOrder = OrderType.Guard; } return; } case 0x167: // all stop { - if (m_Mobile.CheckControlChance(e.Mobile)) + if (Mobile.CheckControlChance(e.Mobile)) { - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.Stop; + Mobile.ControlTarget = null; + Mobile.ControlOrder = OrderType.Stop; } return; @@ -547,20 +547,20 @@ public abstract class BaseAI } case 0x16C: // all follow me { - if (m_Mobile.CheckControlChance(e.Mobile)) + if (Mobile.CheckControlChance(e.Mobile)) { - m_Mobile.ControlTarget = e.Mobile; - m_Mobile.ControlOrder = OrderType.Follow; + Mobile.ControlTarget = e.Mobile; + Mobile.ControlOrder = OrderType.Follow; } return; } case 0x170: // all stay { - if (m_Mobile.CheckControlChance(e.Mobile)) + if (Mobile.CheckControlChance(e.Mobile)) { - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.Stay; + Mobile.ControlTarget = null; + Mobile.ControlOrder = OrderType.Stay; } return; @@ -582,10 +582,10 @@ public abstract class BaseAI break; } - if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) + if (WasNamed(speech) && Mobile.CheckControlChance(e.Mobile)) { - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.Come; + Mobile.ControlTarget = null; + Mobile.ControlOrder = OrderType.Come; } return; @@ -597,18 +597,18 @@ public abstract class BaseAI break; } - if (!m_Mobile.IsDeadPet && !m_Mobile.Summoned && WasNamed(speech) && - m_Mobile.CheckControlChance(e.Mobile)) + if (!Mobile.IsDeadPet && !Mobile.Summoned && WasNamed(speech) && + Mobile.CheckControlChance(e.Mobile)) { - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.Drop; + Mobile.ControlTarget = null; + Mobile.ControlOrder = OrderType.Drop; } return; } case 0x15A: // *follow { - if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) + if (WasNamed(speech) && Mobile.CheckControlChance(e.Mobile)) { BeginPickTarget(e.Mobile, OrderType.Follow); } @@ -622,9 +622,9 @@ public abstract class BaseAI break; } - if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) + if (WasNamed(speech) && Mobile.CheckControlChance(e.Mobile)) { - if (m_Mobile.Summoned || m_Mobile is GrizzledMare) + if (Mobile.Summoned || Mobile is GrizzledMare) { // Summoned creatures are loyal only to their summoners. e.Mobile.SendLocalizedMessage(1005481); @@ -649,10 +649,10 @@ public abstract class BaseAI break; } - if (!m_Mobile.IsDeadPet && WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) + if (!Mobile.IsDeadPet && WasNamed(speech) && Mobile.CheckControlChance(e.Mobile)) { - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.Guard; + Mobile.ControlTarget = null; + Mobile.ControlOrder = OrderType.Guard; } return; @@ -665,7 +665,7 @@ public abstract class BaseAI break; } - if (!m_Mobile.IsDeadPet && WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) + if (!Mobile.IsDeadPet && WasNamed(speech) && Mobile.CheckControlChance(e.Mobile)) { BeginPickTarget(e.Mobile, OrderType.Attack); } @@ -679,30 +679,30 @@ public abstract class BaseAI break; } - if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) + if (WasNamed(speech) && Mobile.CheckControlChance(e.Mobile)) { - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.Patrol; + Mobile.ControlTarget = null; + Mobile.ControlOrder = OrderType.Patrol; } return; } case 0x161: // *stop { - if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) + if (WasNamed(speech) && Mobile.CheckControlChance(e.Mobile)) { - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.Stop; + Mobile.ControlTarget = null; + Mobile.ControlOrder = OrderType.Stop; } return; } case 0x163: // *follow me { - if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) + if (WasNamed(speech) && Mobile.CheckControlChance(e.Mobile)) { - m_Mobile.ControlTarget = e.Mobile; - m_Mobile.ControlOrder = OrderType.Follow; + Mobile.ControlTarget = e.Mobile; + Mobile.ControlOrder = OrderType.Follow; } return; @@ -714,16 +714,16 @@ public abstract class BaseAI break; } - if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) + if (WasNamed(speech) && Mobile.CheckControlChance(e.Mobile)) { - if (!m_Mobile.Summoned) + if (!Mobile.Summoned) { - e.Mobile.SendGump(new ConfirmReleaseGump(e.Mobile, m_Mobile)); + e.Mobile.SendGump(new ConfirmReleaseGump(e.Mobile, Mobile)); } else { - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.Release; + Mobile.ControlTarget = null; + Mobile.ControlOrder = OrderType.Release; } } @@ -736,9 +736,9 @@ public abstract class BaseAI break; } - if (!m_Mobile.IsDeadPet && WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) + if (!Mobile.IsDeadPet && WasNamed(speech) && Mobile.CheckControlChance(e.Mobile)) { - if (m_Mobile.Summoned || m_Mobile is GrizzledMare) + if (Mobile.Summoned || Mobile is GrizzledMare) { // You cannot transfer ownership of a summoned creature. e.Mobile.SendLocalizedMessage(1005487); @@ -758,10 +758,10 @@ public abstract class BaseAI } case 0x16F: // *stay { - if (WasNamed(speech) && m_Mobile.CheckControlChance(e.Mobile)) + if (WasNamed(speech) && Mobile.CheckControlChance(e.Mobile)) { - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.Stay; + Mobile.ControlTarget = null; + Mobile.ControlOrder = OrderType.Stay; } return; @@ -774,12 +774,12 @@ public abstract class BaseAI { if (e.Mobile.AccessLevel >= AccessLevel.GameMaster) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("It's from a GM"); + Mobile.DebugSay("It's from a GM"); } - if (m_Mobile.FindMyName(e.Speech, true)) + if (Mobile.FindMyName(e.Speech, true)) { var str = e.Speech.Split(' '); int i; @@ -790,11 +790,11 @@ public abstract class BaseAI if (word.InsensitiveEquals("obey")) { - m_Mobile.SetControlMaster(e.Mobile); + Mobile.SetControlMaster(e.Mobile); - if (m_Mobile.Summoned) + if (Mobile.Summoned) { - m_Mobile.SummonMaster = e.Mobile; + Mobile.SummonMaster = e.Mobile; } return; @@ -807,7 +807,7 @@ public abstract class BaseAI public virtual bool Think() { - if (m_Mobile.Deleted) + if (Mobile.Deleted) { return false; } @@ -821,37 +821,37 @@ public abstract class BaseAI { case ActionType.Wander: { - m_Mobile.OnActionWander(); + Mobile.OnActionWander(); return DoActionWander(); } case ActionType.Combat: { - m_Mobile.OnActionCombat(); + Mobile.OnActionCombat(); return DoActionCombat(); } case ActionType.Guard: { - m_Mobile.OnActionGuard(); + Mobile.OnActionGuard(); return DoActionGuard(); } case ActionType.Flee: { - m_Mobile.OnActionFlee(); + Mobile.OnActionFlee(); return DoActionFlee(); } case ActionType.Interact: { - m_Mobile.OnActionInteract(); + Mobile.OnActionInteract(); return DoActionInteract(); } case ActionType.Backoff: { - m_Mobile.OnActionBackoff(); + Mobile.OnActionBackoff(); return DoActionBackoff(); } @@ -868,50 +868,50 @@ public abstract class BaseAI { case ActionType.Wander: { - m_Mobile.Warmode = false; - m_Mobile.Combatant = null; - m_Mobile.FocusMob = null; - m_Mobile.SetCurrentSpeedToPassive(); + Mobile.Warmode = false; + Mobile.Combatant = null; + Mobile.FocusMob = null; + Mobile.SetCurrentSpeedToPassive(); break; } case ActionType.Combat: { - m_Mobile.Warmode = true; - m_Mobile.FocusMob = null; - m_Mobile.SetCurrentSpeedToActive(); + Mobile.Warmode = true; + Mobile.FocusMob = null; + 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(); + Mobile.Warmode = true; + Mobile.FocusMob = null; + Mobile.Combatant = null; + _nextStopGuard = Core.TickCount + (int)TimeSpan.FromSeconds(10).TotalMilliseconds; + Mobile.SetCurrentSpeedToActive(); break; } case ActionType.Flee: { - m_Mobile.Warmode = true; - m_Mobile.FocusMob = null; - m_Mobile.SetCurrentSpeedToActive(); + Mobile.Warmode = true; + Mobile.FocusMob = null; + Mobile.SetCurrentSpeedToActive(); break; } case ActionType.Interact: { - m_Mobile.Warmode = false; - m_Mobile.SetCurrentSpeedToPassive(); + Mobile.Warmode = false; + Mobile.SetCurrentSpeedToPassive(); break; } case ActionType.Backoff: { - m_Mobile.Warmode = false; - m_Mobile.SetCurrentSpeedToPassive(); + Mobile.Warmode = false; + Mobile.SetCurrentSpeedToPassive(); break; } } @@ -923,44 +923,44 @@ public abstract class BaseAI { if (CheckHerding()) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("Praise the shepherd!"); + Mobile.DebugSay("Praise the shepherd!"); } } - else if (m_Mobile.CurrentWayPoint != null) + else if (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 && + var point = Mobile.CurrentWayPoint; + if ((point.X != Mobile.Location.X || point.Y != Mobile.Location.Y) && point.Map == Mobile.Map && point.Parent == null && !point.Deleted) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("I will move towards my waypoint."); + Mobile.DebugSay("I will move towards my waypoint."); } - DoMove(m_Mobile.GetDirectionTo(m_Mobile.CurrentWayPoint)); + DoMove(Mobile.GetDirectionTo(Mobile.CurrentWayPoint)); } else if (OnAtWayPoint()) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("I will go to the next waypoint"); + Mobile.DebugSay("I will go to the next waypoint"); } - m_Mobile.CurrentWayPoint = point.NextPoint; + Mobile.CurrentWayPoint = point.NextPoint; if (point.NextPoint?.Deleted == true) { - m_Mobile.CurrentWayPoint = point.NextPoint = point.NextPoint.NextPoint; + Mobile.CurrentWayPoint = point.NextPoint = point.NextPoint.NextPoint; } } } - else if (m_Mobile.IsAnimatedDead) + else if (Mobile.IsAnimatedDead) { // animated dead follow their master - var master = m_Mobile.SummonMaster; + var master = Mobile.SummonMaster; - if (master != null && master.Map == m_Mobile.Map && master.InRange(m_Mobile, m_Mobile.RangePerception)) + if (master != null && master.Map == Mobile.Map && master.InRange(Mobile, Mobile.RangePerception)) { MoveTo(master, false, 1); } @@ -969,15 +969,15 @@ public abstract class BaseAI WalkRandomInHome(2, 2, 1); } } - else if (CheckMove() && CanMoveNow(out _) && !m_Mobile.CheckIdle()) + else if (CheckMove() && CanMoveNow(out _) && !Mobile.CheckIdle()) { WalkRandomInHome(2, 2, 1); } - if (m_Mobile.Combatant?.Deleted == false && m_Mobile.Combatant.Alive && - !m_Mobile.Combatant.IsDeadBondedPet) + if (Mobile.Combatant?.Deleted == false && Mobile.Combatant.Alive && + !Mobile.Combatant.IsDeadBondedPet) { - m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant); + Mobile.Direction = Mobile.GetDirectionTo(Mobile.Combatant); } return true; @@ -987,34 +987,34 @@ public abstract class BaseAI { if (Core.AOS && CheckHerding()) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("Praise the shepherd!"); + Mobile.DebugSay("Praise the shepherd!"); } return true; } - 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) + if (Mobile.Debug) { - m_Mobile.DebugSay("My combatant is gone!"); + 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)) + Mobile.Direction = Mobile.GetDirectionTo(combatant); + if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I used my abilities on {combatant.Name}!"); + Mobile.DebugSay($"I used my abilities on {combatant.Name}!"); } } @@ -1025,24 +1025,24 @@ public abstract class BaseAI { if (Core.AOS && CheckHerding()) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("Praise the shepherd!"); + Mobile.DebugSay("Praise the shepherd!"); } } - else if (Core.TickCount - m_NextStopGuard < 0) + else if (Core.TickCount - _nextStopGuard < 0) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("I am on guard"); + Mobile.DebugSay("I am on guard"); } // m_Mobile.Turn( Utility.Random(0, 2) - 1 ); } else { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("I stopped being on guard"); + Mobile.DebugSay("I stopped being on guard"); } Action = ActionType.Wander; @@ -1053,33 +1053,33 @@ public abstract class BaseAI public virtual bool DoActionFlee() { - var from = m_Mobile.FocusMob; + var from = Mobile.FocusMob; - if (from?.Deleted != false || from.Map != m_Mobile.Map) + if (from?.Deleted != false || from.Map != Mobile.Map) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("I have lost him"); + 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 (WalkMobileRange(from, 1, true, Mobile.RangePerception * 2, Mobile.RangePerception * 3)) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("I have fled"); + Mobile.DebugSay("I have fled"); } Action = ActionType.Guard; return true; } - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("I am fleeing!"); + Mobile.DebugSay("I am fleeing!"); } return true; @@ -1090,7 +1090,7 @@ public abstract class BaseAI public virtual bool DoActionBackoff() => true; public virtual bool Obey() => - !m_Mobile.Deleted && m_Mobile.ControlOrder switch + !Mobile.Deleted && Mobile.ControlOrder switch { OrderType.None => DoOrderNone(), OrderType.Come => DoOrderCome(), @@ -1110,133 +1110,133 @@ public abstract class BaseAI public virtual void OnCurrentOrderChanged() { - if (m_Mobile.Deleted || m_Mobile.ControlMaster?.Deleted != false) + if (Mobile.Deleted || Mobile.ControlMaster?.Deleted != false) { return; } - switch (m_Mobile.ControlOrder) + switch (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; + Mobile.ControlMaster.RevealingAction(); + Mobile.Home = Mobile.Location; + Mobile.SetCurrentSpeedToPassive(); + Mobile.PlaySound(Mobile.GetIdleSound()); + Mobile.Warmode = false; + 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; + Mobile.ControlMaster.RevealingAction(); + Mobile.SetCurrentSpeedToActive(); + Mobile.PlaySound(Mobile.GetIdleSound()); + Mobile.Warmode = false; + 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; + Mobile.ControlMaster.RevealingAction(); + Mobile.SetCurrentSpeedToPassive(); + Mobile.PlaySound(Mobile.GetIdleSound()); + Mobile.Warmode = false; + Mobile.Combatant = null; break; } case OrderType.Friend: case OrderType.Unfriend: { - m_Mobile.ControlMaster.RevealingAction(); + 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. + Mobile.ControlMaster.RevealingAction(); + Mobile.SetCurrentSpeedToActive(); + Mobile.PlaySound(Mobile.GetIdleSound()); + Mobile.Warmode = true; + Mobile.Combatant = null; + Mobile.ControlMaster.SendLocalizedMessage(1049671, 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()); + Mobile.ControlMaster.RevealingAction(); + Mobile.SetCurrentSpeedToActive(); + Mobile.PlaySound(Mobile.GetIdleSound()); - m_Mobile.Warmode = true; - m_Mobile.Combatant = null; + Mobile.Warmode = true; + 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; + Mobile.ControlMaster.RevealingAction(); + Mobile.SetCurrentSpeedToActive(); + Mobile.PlaySound(Mobile.GetIdleSound()); + Mobile.Warmode = false; + 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; + Mobile.ControlMaster.RevealingAction(); + Mobile.SetCurrentSpeedToPassive(); + Mobile.PlaySound(Mobile.GetIdleSound()); + Mobile.Warmode = false; + 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; + Mobile.ControlMaster.RevealingAction(); + Mobile.SetCurrentSpeedToPassive(); + Mobile.PlaySound(Mobile.GetIdleSound()); + Mobile.Warmode = false; + 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; + Mobile.ControlMaster.RevealingAction(); + Mobile.Home = Mobile.Location; + Mobile.SetCurrentSpeedToPassive(); + Mobile.PlaySound(Mobile.GetIdleSound()); + Mobile.Warmode = false; + Mobile.Combatant = null; break; } case OrderType.Follow: { - m_Mobile.ControlMaster.RevealingAction(); - m_Mobile.SetCurrentSpeedToActive(); - m_Mobile.PlaySound(m_Mobile.GetIdleSound()); + Mobile.ControlMaster.RevealingAction(); + Mobile.SetCurrentSpeedToActive(); + Mobile.PlaySound(Mobile.GetIdleSound()); - m_Mobile.Warmode = false; - m_Mobile.Combatant = null; + Mobile.Warmode = false; + Mobile.Combatant = null; break; } case OrderType.Transfer: { - m_Mobile.ControlMaster.RevealingAction(); - m_Mobile.SetCurrentSpeedToPassive(); - m_Mobile.PlaySound(m_Mobile.GetIdleSound()); + Mobile.ControlMaster.RevealingAction(); + Mobile.SetCurrentSpeedToPassive(); + Mobile.PlaySound(Mobile.GetIdleSound()); - m_Mobile.Warmode = false; - m_Mobile.Combatant = null; + Mobile.Warmode = false; + Mobile.Combatant = null; break; } } @@ -1244,22 +1244,22 @@ public abstract class BaseAI public virtual bool DoOrderNone() { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("I have no order"); + Mobile.DebugSay("I have no order"); } WalkRandomInHome(3, 2, 1); - if (m_Mobile.Combatant?.Deleted == false && m_Mobile.Combatant.Alive && - !m_Mobile.Combatant.IsDeadBondedPet) + if (Mobile.Combatant?.Deleted == false && Mobile.Combatant.Alive && + !Mobile.Combatant.IsDeadBondedPet) { - m_Mobile.Warmode = true; - m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant); + Mobile.Warmode = true; + Mobile.Direction = Mobile.GetDirectionTo(Mobile.Combatant); } else { - m_Mobile.Warmode = false; + Mobile.Warmode = false; } return true; @@ -1267,44 +1267,44 @@ public abstract class BaseAI public virtual bool DoOrderCome() { - if (m_Mobile.ControlMaster?.Deleted != false) + if (Mobile.ControlMaster?.Deleted != false) { return true; } - var iCurrDist = (int)m_Mobile.GetDistanceToSqrt(m_Mobile.ControlMaster); + var iCurrDist = (int)Mobile.GetDistanceToSqrt(Mobile.ControlMaster); - if (iCurrDist > m_Mobile.RangePerception) + if (iCurrDist > Mobile.RangePerception) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("I have lost my master. I stay here"); + Mobile.DebugSay("I have lost my master. I stay here"); } - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.None; + Mobile.ControlTarget = null; + Mobile.ControlOrder = OrderType.None; } else { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("My master told me come"); + 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 (WalkMobileRange(Mobile.ControlMaster, 1, bRun, 0, 1)) { - if (m_Mobile.Combatant?.Deleted == false && m_Mobile.Combatant.Alive && - !m_Mobile.Combatant.IsDeadBondedPet) + if (Mobile.Combatant?.Deleted == false && Mobile.Combatant.Alive && + !Mobile.Combatant.IsDeadBondedPet) { - m_Mobile.Warmode = true; + Mobile.Warmode = true; // m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant, bRun); } else { - m_Mobile.Warmode = false; + Mobile.Warmode = false; } } } @@ -1314,17 +1314,17 @@ public abstract class BaseAI public virtual bool DoOrderDrop() { - if (m_Mobile.IsDeadPet || !m_Mobile.CanDrop) + if (Mobile.IsDeadPet || !Mobile.CanDrop) { return true; } - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("I drop my stuff for my master"); + Mobile.DebugSay("I drop my stuff for my master"); } - var pack = m_Mobile.Backpack; + var pack = Mobile.Backpack; if (pack != null) { @@ -1334,37 +1334,37 @@ public abstract class BaseAI { if (i < list.Count) { - list[i].MoveToWorld(m_Mobile.Location, m_Mobile.Map); + list[i].MoveToWorld(Mobile.Location, Mobile.Map); } } } - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.None; + Mobile.ControlTarget = null; + Mobile.ControlOrder = OrderType.None; return true; } public virtual bool CheckHerding() { - var target = m_Mobile.TargetLocation; + var target = Mobile.TargetLocation; if (target == null) { return false; // Creature is not being herded } - var distance = m_Mobile.GetDistanceToSqrt(target); + var distance = Mobile.GetDistanceToSqrt(target); if (distance is not (< 1 or > 15)) { - DoMove(m_Mobile.GetDirectionTo(target)); + DoMove(Mobile.GetDirectionTo(target)); return true; } - if (distance < 1 && target.X == 1076 && target.Y == 450 && m_Mobile is HordeMinionFamiliar) + if (distance < 1 && target.X == 1076 && target.Y == 450 && Mobile is HordeMinionFamiliar) { - if (m_Mobile.ControlMaster is PlayerMobile pm) + if (Mobile.ControlMaster is PlayerMobile pm) { var qs = pm.Quest; @@ -1374,14 +1374,14 @@ public abstract class BaseAI if (obj?.Completed == false) { - m_Mobile.AddToBackpack(new ScrollOfAbraxus()); + Mobile.AddToBackpack(new ScrollOfAbraxus()); obj.Complete(); } } } } - m_Mobile.TargetLocation = null; + Mobile.TargetLocation = null; return false; // At the target or too far away } @@ -1389,57 +1389,57 @@ public abstract class BaseAI { if (CheckHerding()) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("Praise the shepherd!"); + Mobile.DebugSay("Praise the shepherd!"); } } - else if (m_Mobile.ControlTarget?.Deleted == false && m_Mobile.ControlTarget != m_Mobile) + else if (Mobile.ControlTarget?.Deleted == false && Mobile.ControlTarget != Mobile) { - var iCurrDist = (int)m_Mobile.GetDistanceToSqrt(m_Mobile.ControlTarget); + var iCurrDist = (int)Mobile.GetDistanceToSqrt(Mobile.ControlTarget); - if (iCurrDist > m_Mobile.RangePerception) + if (iCurrDist > Mobile.RangePerception) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("I have lost the one to follow. I stay here"); + 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) + if (Mobile.Combatant?.Deleted == false && Mobile.Combatant.Alive && + !Mobile.Combatant.IsDeadBondedPet) { - m_Mobile.Warmode = true; - m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant); + Mobile.Warmode = true; + Mobile.Direction = Mobile.GetDirectionTo(Mobile.Combatant); } else { - m_Mobile.Warmode = false; + Mobile.Warmode = false; } } else { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay($"My master told me to follow: {m_Mobile.ControlTarget.Name}"); + Mobile.DebugSay($"My master told me to follow: {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 (WalkMobileRange(Mobile.ControlTarget, 1, bRun, 0, 1)) { - if (m_Mobile.Combatant?.Deleted == false && m_Mobile.Combatant.Alive && - !m_Mobile.Combatant.IsDeadBondedPet) + if (Mobile.Combatant?.Deleted == false && Mobile.Combatant.Alive && + !Mobile.Combatant.IsDeadBondedPet) { - m_Mobile.Warmode = true; + Mobile.Warmode = true; // m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant, bRun); } else { - m_Mobile.Warmode = false; + Mobile.Warmode = false; if (Core.AOS) { - m_Mobile.CurrentSpeed = 0.1; + Mobile.CurrentSpeed = 0.1; } } } @@ -1447,13 +1447,13 @@ public abstract class BaseAI } else { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("I have nobody to follow"); + Mobile.DebugSay("I have nobody to follow"); } - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.None; + Mobile.ControlTarget = null; + Mobile.ControlOrder = OrderType.None; } return true; @@ -1461,12 +1461,12 @@ public abstract class BaseAI public virtual bool DoOrderFriend() { - var from = m_Mobile.ControlMaster; - var to = m_Mobile.ControlTarget; + var from = Mobile.ControlMaster; + var to = Mobile.ControlTarget; if (from?.Deleted != false || to?.Deleted != false || from == to || !to.Player) { - m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 502039); // *looks confused* + Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 502039); // *looks confused* } else { @@ -1495,11 +1495,11 @@ public abstract class BaseAI { to.SendLocalizedMessage(1070947); // You cannot friend a pet with a trade pending } - else if (m_Mobile.IsPetFriend(to)) + else if (Mobile.IsPetFriend(to)) { from.SendLocalizedMessage(1049691); // That person is already a friend. } - else if (!m_Mobile.AllowNewPetFriend) + else if (!Mobile.AllowNewPetFriend) { from.SendLocalizedMessage( 1005482 @@ -1508,17 +1508,17 @@ public abstract class BaseAI else { // ~1_NAME~ will now accept movement commands from ~2_NAME~. - from.SendLocalizedMessage(1049676, $"{m_Mobile.Name}\t{to.Name}"); + from.SendLocalizedMessage(1049676, $"{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}"); + to.SendLocalizedMessage(1043246, $"{from.Name}\t{Mobile.Name}"); - m_Mobile.AddPetFriend(to); + Mobile.AddPetFriend(to); - m_Mobile.ControlTarget = to; - m_Mobile.ControlOrder = OrderType.Follow; + Mobile.ControlTarget = to; + Mobile.ControlOrder = OrderType.Follow; return true; } @@ -1526,59 +1526,59 @@ public abstract class BaseAI } } - m_Mobile.ControlTarget = from; - m_Mobile.ControlOrder = OrderType.Follow; + Mobile.ControlTarget = from; + Mobile.ControlOrder = OrderType.Follow; return true; } public virtual bool DoOrderUnfriend() { - var from = m_Mobile.ControlMaster; - var to = m_Mobile.ControlTarget; + var from = Mobile.ControlMaster; + var to = Mobile.ControlTarget; if (from?.Deleted != false || to?.Deleted != false || from == to || !to.Player) { - m_Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 502039); // *looks confused* + Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 502039); // *looks confused* } - else if (!m_Mobile.IsPetFriend(to)) + else if (!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}"); + from.SendLocalizedMessage(1070951, $"{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}"); + to.SendLocalizedMessage(1070952, $"{from.Name}\t{Mobile.Name}"); - m_Mobile.RemovePetFriend(to); + Mobile.RemovePetFriend(to); } - m_Mobile.ControlTarget = from; - m_Mobile.ControlOrder = OrderType.Follow; + Mobile.ControlTarget = from; + Mobile.ControlOrder = OrderType.Follow; return true; } public virtual bool DoOrderGuard() { - if (m_Mobile.IsDeadPet) + if (Mobile.IsDeadPet) { return true; } - var controlMaster = m_Mobile.ControlMaster; + var controlMaster = Mobile.ControlMaster; if (controlMaster?.Deleted != false) { return true; } - var combatant = m_Mobile.Combatant; + var combatant = Mobile.Combatant; var aggressors = controlMaster.Aggressors; @@ -1590,7 +1590,7 @@ public abstract class BaseAI var attacker = info.Attacker; if (attacker?.Deleted == false && - attacker.GetDistanceToSqrt(m_Mobile) <= m_Mobile.RangePerception) + attacker.GetDistanceToSqrt(Mobile) <= Mobile.RangePerception) { if (combatant == null || attacker.GetDistanceToSqrt(controlMaster) < combatant.GetDistanceToSqrt(controlMaster)) @@ -1600,23 +1600,23 @@ public abstract class BaseAI } } - if (combatant != null && m_Mobile.Debug) + if (combatant != null && Mobile.Debug) { - m_Mobile.DebugSay("Crap, my master has been attacked! I will attack one of those bastards!"); + 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 (combatant?.Deleted == false && combatant != Mobile && combatant != Mobile.ControlMaster && + combatant.Alive && !combatant.IsDeadBondedPet && Mobile.CanSee(combatant) && + Mobile.CanBeHarmful(combatant, false) && combatant.Map == Mobile.Map) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("Guarding from target..."); + Mobile.DebugSay("Guarding from target..."); } - m_Mobile.Combatant = combatant; - m_Mobile.FocusMob = combatant; + Mobile.Combatant = combatant; + Mobile.FocusMob = combatant; Action = ActionType.Combat; /* @@ -1627,15 +1627,15 @@ public abstract class BaseAI } else { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("Nothing to guard from"); + Mobile.DebugSay("Nothing to guard from"); } - m_Mobile.Warmode = false; + Mobile.Warmode = false; if (Core.AOS) { - m_Mobile.CurrentSpeed = 0.1; + Mobile.CurrentSpeed = 0.1; } WalkMobileRange(controlMaster, 1, false, 0, 1); @@ -1646,40 +1646,40 @@ public abstract class BaseAI public virtual bool DoOrderAttack() { - if (m_Mobile.IsDeadPet) + if (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 (Mobile.ControlTarget?.Deleted != false || Mobile.ControlTarget.Map != Mobile.Map || + !Mobile.ControlTarget.Alive || Mobile.ControlTarget.IsDeadBondedPet) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay( + 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; + Mobile.ControlTarget = Mobile.ControlMaster; + Mobile.ControlOrder = OrderType.Follow; } else { - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.None; + Mobile.ControlTarget = null; + Mobile.ControlOrder = OrderType.None; } - if (m_Mobile.FightMode is FightMode.Closest or FightMode.Aggressor) + if (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)) + foreach (var aggr in Mobile.GetMobilesInRange(Mobile.RangePerception)) { - if (!m_Mobile.CanSee(aggr) || aggr.Combatant != m_Mobile) + if (!Mobile.CanSee(aggr) || aggr.Combatant != Mobile) { continue; } @@ -1689,9 +1689,9 @@ public abstract class BaseAI continue; } - var aggrScore = m_Mobile.GetFightModeRanking(aggr, FightMode.Closest, false); + var aggrScore = Mobile.GetFightModeRanking(aggr, FightMode.Closest, false); - if ((newCombatant == null || aggrScore > newScore) && m_Mobile.InLOS(aggr)) + if ((newCombatant == null || aggrScore > newScore) && Mobile.InLOS(aggr)) { newCombatant = aggr; newScore = aggrScore; @@ -1700,12 +1700,12 @@ public abstract class BaseAI if (newCombatant != null) { - m_Mobile.ControlTarget = newCombatant; - m_Mobile.ControlOrder = OrderType.Attack; - m_Mobile.Combatant = newCombatant; - if (m_Mobile.Debug) + Mobile.ControlTarget = newCombatant; + Mobile.ControlOrder = OrderType.Attack; + Mobile.Combatant = newCombatant; + if (Mobile.Debug) { - m_Mobile.DebugSay("But -that- is not dead. Here we go again..."); + Mobile.DebugSay("But -that- is not dead. Here we go again..."); } Think(); @@ -1714,9 +1714,9 @@ public abstract class BaseAI } else { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("Attacking target..."); + Mobile.DebugSay("Attacking target..."); } Think(); @@ -1727,9 +1727,9 @@ public abstract class BaseAI public virtual bool DoOrderPatrol() { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("This order is not yet coded"); + Mobile.DebugSay("This order is not yet coded"); } return true; @@ -1737,38 +1737,38 @@ public abstract class BaseAI public virtual bool DoOrderRelease() { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("I have been released"); + Mobile.DebugSay("I have been released"); } - m_Mobile.PlaySound(m_Mobile.GetAngerSound()); + Mobile.PlaySound(Mobile.GetAngerSound()); - m_Mobile.SetControlMaster(null); - m_Mobile.SummonMaster = null; + Mobile.SetControlMaster(null); + Mobile.SummonMaster = null; - m_Mobile.BondingBegin = DateTime.MinValue; - m_Mobile.OwnerAbandonTime = DateTime.MinValue; - m_Mobile.IsBonded = false; + Mobile.BondingBegin = DateTime.MinValue; + Mobile.OwnerAbandonTime = DateTime.MinValue; + Mobile.IsBonded = false; - var spawner = m_Mobile.Spawner; + var spawner = Mobile.Spawner; if (spawner != null && spawner.HomeLocation != Point3D.Zero) { - m_Mobile.Home = spawner.HomeLocation; - m_Mobile.RangeHome = spawner.HomeRange; + Mobile.Home = spawner.HomeLocation; + Mobile.RangeHome = spawner.HomeRange; } - if (m_Mobile.DeleteOnRelease || m_Mobile.IsDeadPet) + if (Mobile.DeleteOnRelease || Mobile.IsDeadPet) { - m_Mobile.Delete(); + Mobile.Delete(); } - m_Mobile.BeginDeleteTimer(); + Mobile.BeginDeleteTimer(); - if (m_Mobile.CanDrop) + if (Mobile.CanDrop) { - m_Mobile.DropBackpack(); + Mobile.DropBackpack(); } return true; @@ -1776,15 +1776,15 @@ public abstract class BaseAI public virtual bool DoOrderStay() { - if (m_Mobile.Debug) + if (Mobile.Debug) { if (CheckHerding()) { - m_Mobile.DebugSay("Praise the shepherd!"); + Mobile.DebugSay("Praise the shepherd!"); } else { - m_Mobile.DebugSay("My master told me to stay"); + Mobile.DebugSay("My master told me to stay"); } } @@ -1793,20 +1793,20 @@ public abstract class BaseAI public virtual bool DoOrderStop() { - if (m_Mobile.ControlMaster?.Deleted != false) + if (Mobile.ControlMaster?.Deleted != false) { return true; } - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("My master told me to stop."); + Mobile.DebugSay("My master told me to stop."); } - m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.ControlMaster); - m_Mobile.Home = m_Mobile.Location; + Mobile.Direction = Mobile.GetDirectionTo(Mobile.ControlMaster); + Mobile.Home = Mobile.Location; - m_Mobile.ControlTarget = null; + Mobile.ControlTarget = null; if (Core.ML) { @@ -1814,7 +1814,7 @@ public abstract class BaseAI } else { - m_Mobile.ControlOrder = OrderType.None; + Mobile.ControlOrder = OrderType.None; } return true; @@ -1822,19 +1822,19 @@ public abstract class BaseAI public virtual bool DoOrderTransfer() { - if (m_Mobile.IsDeadPet) + if (Mobile.IsDeadPet) { return true; } - var from = m_Mobile.ControlMaster; - var to = m_Mobile.ControlTarget; + var from = Mobile.ControlMaster; + var to = Mobile.ControlTarget; if (from?.Deleted == false && to?.Deleted == false && from != to && to.Player) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay($"Begin transfer with {to.Name}"); + Mobile.DebugSay($"Begin transfer with {to.Name}"); } var youngFrom = from is PlayerMobile mobile && mobile.Young; @@ -1848,7 +1848,7 @@ public abstract class BaseAI { from.SendLocalizedMessage(502052); // As an older player, you may not transfer pets to young players. } - else if (!m_Mobile.CanBeControlledBy(to)) + else if (!Mobile.CanBeControlledBy(to)) { var args = $"{to.Name}\t{from.Name}\t "; @@ -1861,7 +1861,7 @@ public abstract class BaseAI args ); // The pet will not accept you as a master because it does not trust you.~3_BLANK~ } - else if (!m_Mobile.CanBeControlledBy(from)) + else if (!Mobile.CanBeControlledBy(from)) { var args = $"{to.Name}\t{from.Name}\t "; @@ -1874,7 +1874,7 @@ public abstract class BaseAI 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)) + else if (TransferItem.IsInCombat(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."); @@ -1895,38 +1895,38 @@ public abstract class BaseAI } else { - fromState.AddTrade(toState).DropItem(new TransferItem(m_Mobile)); + fromState.AddTrade(toState).DropItem(new TransferItem(Mobile)); } } } } - m_Mobile.ControlTarget = null; - m_Mobile.ControlOrder = OrderType.Stay; + Mobile.ControlTarget = null; + Mobile.ControlOrder = OrderType.Stay; return true; } public virtual bool DoBardPacified() { - if (Core.Now < m_Mobile.BardEndTime) + if (Core.Now < Mobile.BardEndTime) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("I am pacified, I wait"); + Mobile.DebugSay("I am pacified, I wait"); } - m_Mobile.Combatant = null; - m_Mobile.Warmode = false; + Mobile.Combatant = null; + Mobile.Warmode = false; } else { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("I'm not pacified any longer"); + Mobile.DebugSay("I'm not pacified any longer"); } - m_Mobile.BardPacified = false; + Mobile.BardPacified = false; } return true; @@ -1934,44 +1934,44 @@ public abstract class BaseAI 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 (Core.Now >= Mobile.BardEndTime && + (Mobile.BardMaster?.Deleted != false || + Mobile.BardMaster.Map != Mobile.Map || Mobile.GetDistanceToSqrt(Mobile.BardMaster) > + Mobile.RangePerception)) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("I have lost my provoker"); + Mobile.DebugSay("I have lost my provoker"); } - m_Mobile.BardProvoked = false; - m_Mobile.BardMaster = null; - m_Mobile.BardTarget = null; + Mobile.BardProvoked = false; + Mobile.BardMaster = null; + Mobile.BardTarget = null; - m_Mobile.Combatant = null; - m_Mobile.Warmode = false; + Mobile.Combatant = null; + 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) + else if (Mobile.BardTarget?.Deleted != false || Mobile.BardTarget.Map != Mobile.Map || + Mobile.GetDistanceToSqrt(Mobile.BardTarget) > Mobile.RangePerception) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("I have lost my provoke target"); + Mobile.DebugSay("I have lost my provoke target"); } - m_Mobile.BardProvoked = false; - m_Mobile.BardMaster = null; - m_Mobile.BardTarget = null; + Mobile.BardProvoked = false; + Mobile.BardMaster = null; + Mobile.BardTarget = null; - m_Mobile.Combatant = null; - m_Mobile.Warmode = false; + Mobile.Combatant = null; + Mobile.Warmode = false; } else { - m_Mobile.Combatant = m_Mobile.BardTarget; - m_Action = ActionType.Combat; + Mobile.Combatant = Mobile.BardTarget; + _action = ActionType.Combat; - m_Mobile.OnThink(); + Mobile.OnThink(); Think(); } @@ -1980,7 +1980,7 @@ public abstract class BaseAI public virtual void WalkRandom(int iChanceToNotMove, int iChanceToDir, int iSteps) { - if (m_Mobile.Deleted || m_Mobile.DisallowAllMoves) + if (Mobile.Deleted || Mobile.DisallowAllMoves) { return; } @@ -1997,7 +1997,7 @@ public abstract class BaseAI // 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; + Direction direction = iRndMove < 8 ? (Direction)iRndMove : Mobile.Direction; DoMove(direction); } } @@ -2101,8 +2101,8 @@ public abstract class BaseAI 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) + if (Mobile == null || Mobile.Deleted || Mobile.Frozen || Mobile.Paralyzed || + Mobile.Spell?.IsCasting == true || Mobile.DisallowAllMoves) { return MoveResult.BadState; } @@ -2112,7 +2112,7 @@ public abstract class BaseAI return MoveResult.BadState; } - var delay = (int)(TransformMoveDelay(m_Mobile) * 1000); + var delay = (int)(TransformMoveDelay(Mobile) * 1000); if (!CanMoveNow(out var timeUntilMove)) { @@ -2127,7 +2127,7 @@ public abstract class BaseAI // 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; + Mobile.Direction = d; NextMove += delay; if (Core.TickCount - NextMove > 0) @@ -2135,24 +2135,24 @@ public abstract class BaseAI NextMove = Core.TickCount; } - m_Mobile.Pushing = false; - var mobDirection = m_Mobile.Direction; + Mobile.Pushing = false; + var mobDirection = Mobile.Direction; // Do the actual move - MoveImpl.IgnoreMovableImpassables = m_Mobile.CanMoveOverObstacles && !m_Mobile.CanDestroyObstacles; - var moveResult = m_Mobile.Move(d); + MoveImpl.IgnoreMovableImpassables = Mobile.CanMoveOverObstacles && !Mobile.CanDestroyObstacles; + var moveResult = 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) + if (Mobile.Warmode && Mobile.Combatant != null) { - var remaining = m_Mobile.NextCombatTime - Core.TickCount; + var remaining = Mobile.NextCombatTime - Core.TickCount; var maxWait = Math.Min(delay, 400); if (remaining < maxWait) { - m_Mobile.NextCombatTime = Core.TickCount + maxWait; + Mobile.NextCombatTime = Core.TickCount + maxWait; } } return MoveResult.Success; @@ -2163,34 +2163,34 @@ public abstract class BaseAI return MoveResult.Blocked; } - var wasPushing = m_Mobile.Pushing; + var wasPushing = Mobile.Pushing; var blocked = true; - var canOpenDoors = m_Mobile.CanOpenDoors; - var canDestroyObstacles = m_Mobile.CanDestroyObstacles; + var canOpenDoors = Mobile.CanOpenDoors; + var canDestroyObstacles = Mobile.CanDestroyObstacles; if (canOpenDoors || canDestroyObstacles) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("My movement was blocked, I will try to clear some obstacles."); + Mobile.DebugSay("My movement was blocked, I will try to clear some obstacles."); } - var map = m_Mobile.Map; + var map = Mobile.Map; if (map != null) { - var x = m_Mobile.X; - var y = m_Mobile.Y; + var x = Mobile.X; + var y = 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 (canOpenDoors && item is BaseDoor door && door.Z + door.ItemData.Height > Mobile.Z && + Mobile.Z + 16 > door.Z) { if (door.X != x || door.Y != y) { @@ -2208,9 +2208,9 @@ public abstract class BaseAI } } else if (canDestroyObstacles && item.Movable && item.ItemData.Impassable && - item.Z + item.ItemData.Height > m_Mobile.Z && m_Mobile.Z + 16 > item.Z) + item.Z + item.ItemData.Height > Mobile.Z && Mobile.Z + 16 > item.Z) { - if (!m_Mobile.InRange(item.GetWorldLocation(), 1)) + if (!Mobile.InRange(item.GetWorldLocation(), 1)) { continue; } @@ -2222,7 +2222,7 @@ public abstract class BaseAI if (destroyables > 0) { - Effects.PlaySound(new Point3D(x, y, m_Mobile.Z), m_Mobile.Map, 0x3B3); + Effects.PlaySound(new Point3D(x, y, Mobile.Z), Mobile.Map, 0x3B3); } if (queue.Count > 0) @@ -2236,25 +2236,25 @@ public abstract class BaseAI if (item is BaseDoor door) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay( + Mobile.DebugSay( "Little do they expect, I've learned how to open doors. Didn't they read the script??" ); } - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("*twist*"); + Mobile.DebugSay("*twist*"); } - door.Use(m_Mobile); + door.Use(Mobile); } else { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay( + Mobile.DebugSay( $"Ugabooga. I'm so big and tough I can destroy it: {item.GetType().Name}" ); } @@ -2266,7 +2266,7 @@ public abstract class BaseAI var check = cont.Items[i]; if (check.Movable && check.ItemData.Impassable && - cont.Z + check.ItemData.Height > m_Mobile.Z) + cont.Z + check.ItemData.Height > Mobile.Z) { queue.Enqueue(check); } @@ -2283,7 +2283,7 @@ public abstract class BaseAI if (!blocked) { - blocked = !m_Mobile.Move(d); + blocked = !Mobile.Move(d); } } } @@ -2294,9 +2294,9 @@ public abstract class BaseAI for (var i = 0; i < 2; ++i) { - m_Mobile.TurnInternal(offset); + Mobile.TurnInternal(offset); - if (m_Mobile.Move(m_Mobile.Direction)) + if (Mobile.Move(Mobile.Direction)) { return MoveResult.SuccessAutoTurn; } @@ -2310,28 +2310,28 @@ public abstract class BaseAI public virtual void WalkRandomInHome(int iChanceToNotMove, int iChanceToDir, int iSteps) { - if (m_Mobile.Deleted || m_Mobile.DisallowAllMoves) + if (Mobile.Deleted || Mobile.DisallowAllMoves) { return; } - if (m_Mobile.Home == Point3D.Zero) + if (Mobile.Home == Point3D.Zero) { - if (m_Mobile.Spawner is RegionSpawner rs) + if (Mobile.Spawner is RegionSpawner rs) { Region region = rs.SpawnRegion; - if (m_Mobile.Region.AcceptsSpawnsFrom(region)) + if (Mobile.Region.AcceptsSpawnsFrom(region)) { - m_Mobile.WalkRegion = region; + Mobile.WalkRegion = region; WalkRandom(iChanceToNotMove, iChanceToDir, iSteps); - m_Mobile.WalkRegion = null; + Mobile.WalkRegion = null; } else { if (region.GoLocation != Point3D.Zero && Utility.RandomBool()) { - DoMove(m_Mobile.GetDirectionTo(region.GoLocation)); + DoMove(Mobile.GetDirectionTo(region.GoLocation)); } else { @@ -2348,21 +2348,21 @@ public abstract class BaseAI { for (var i = 0; i < iSteps; i++) { - if (m_Mobile.RangeHome != 0) + if (Mobile.RangeHome != 0) { - var iCurrDist = (int)m_Mobile.GetDistanceToSqrt(m_Mobile.Home); + var iCurrDist = (int)Mobile.GetDistanceToSqrt(Mobile.Home); - if (iCurrDist < m_Mobile.RangeHome * 2 / 3) + if (iCurrDist < Mobile.RangeHome * 2 / 3) { WalkRandom(iChanceToNotMove, iChanceToDir, 1); } - else if (iCurrDist > m_Mobile.RangeHome) + else if (iCurrDist > Mobile.RangeHome) { - DoMove(m_Mobile.GetDirectionTo(m_Mobile.Home)); + DoMove(Mobile.GetDirectionTo(Mobile.Home)); } else if (Utility.Random(10) > 5) { - DoMove(m_Mobile.GetDirectionTo(m_Mobile.Home)); + DoMove(Mobile.GetDirectionTo(Mobile.Home)); } else { @@ -2371,9 +2371,9 @@ public abstract class BaseAI } else { - if (m_Mobile.Location != m_Mobile.Home) + if (Mobile.Location != Mobile.Home) { - DoMove(m_Mobile.GetDirectionTo(m_Mobile.Home)); + DoMove(Mobile.GetDirectionTo(Mobile.Home)); } } } @@ -2382,9 +2382,9 @@ public abstract class BaseAI public virtual bool CheckFlee() { - if (m_Mobile.CheckFlee()) + if (Mobile.CheckFlee()) { - var combatant = m_Mobile.Combatant; + var combatant = Mobile.Combatant; if (combatant == null) { @@ -2392,12 +2392,12 @@ public abstract class BaseAI } else { - var d = combatant.GetDirectionTo(m_Mobile); + var d = combatant.GetDirectionTo(Mobile); d = (Direction)((int)d + Utility.RandomMinMax(-1, +1)); - m_Mobile.Direction = d; - m_Mobile.Move(d); + Mobile.Direction = d; + Mobile.Move(d); } return true; @@ -2408,51 +2408,51 @@ public abstract class BaseAI public virtual void OnTeleported() { - if (m_Path != null) + if (_path != null) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("Teleported; repathing"); + Mobile.DebugSay("Teleported; repathing"); } - m_Path.ForceRepath(); + _path.ForceRepath(); } } public virtual bool MoveTo(Mobile m, bool run, int range) { - if (m_Mobile.Deleted || m_Mobile.DisallowAllMoves || m?.Deleted != false) + if (Mobile.Deleted || Mobile.DisallowAllMoves || m?.Deleted != false) { return false; } - if (m_Mobile.InRange(m, range)) + if (Mobile.InRange(m, range)) { - m_Path = null; + _path = null; return true; } - if (m_Path?.Goal == m) + if (_path?.Goal == m) { - if (m_Path.Follow(run, 1)) + if (_path.Follow(run, 1)) { - m_Path = null; + _path = null; return true; } } - else if (!DoMove(m_Mobile.GetDirectionTo(m), true)) + else if (!DoMove(Mobile.GetDirectionTo(m), true)) { - m_Path = new PathFollower(m_Mobile, m) { Mover = DoMoveImpl }; + _path = new PathFollower(Mobile, m) { Mover = DoMoveImpl }; - if (m_Path.Follow(run, 1)) + if (_path.Follow(run, 1)) { - m_Path = null; + _path = null; return true; } } else { - m_Path = null; + _path = null; return true; } @@ -2470,7 +2470,7 @@ public abstract class BaseAI */ public virtual bool WalkMobileRange(Mobile m, int iSteps, bool run, int iWantDistMin, int iWantDistMax) { - if (m_Mobile.Deleted || m_Mobile.DisallowAllMoves) + if (Mobile.Deleted || Mobile.DisallowAllMoves) { return false; } @@ -2483,36 +2483,36 @@ public abstract class BaseAI for (var i = 0; i < iSteps; i++) { // Get the current distance - var iCurrDist = (int)m_Mobile.GetDistanceToSqrt(m); + var iCurrDist = (int)Mobile.GetDistanceToSqrt(m); if (iCurrDist < iWantDistMin || iCurrDist > iWantDistMax) { var needCloser = iCurrDist > iWantDistMax; - if (needCloser && m_Path != null && m_Path.Goal == m) + if (needCloser && _path != null && _path.Goal == m) { - if (m_Path.Follow(run, 1)) + if (_path.Follow(run, 1)) { - m_Path = null; + _path = null; } } else { var dirTo = iCurrDist > iWantDistMax ? - m_Mobile.GetDirectionTo(m, run) : m.GetDirectionTo(m_Mobile, run); + Mobile.GetDirectionTo(m, run) : m.GetDirectionTo(Mobile, run); if (!DoMove(dirTo, true) && needCloser) { - m_Path = new PathFollower(m_Mobile, m) { Mover = DoMoveImpl }; + _path = new PathFollower(Mobile, m) { Mover = DoMoveImpl }; - if (m_Path.Follow(run, 1)) + if (_path.Follow(run, 1)) { - m_Path = null; + _path = null; } } else { - m_Path = null; + _path = null; } } } @@ -2523,7 +2523,7 @@ public abstract class BaseAI } // Get the current distance - var iNewDist = (int)m_Mobile.GetDistanceToSqrt(m); + var iNewDist = (int)Mobile.GetDistanceToSqrt(m); return iNewDist >= iWantDistMin && iNewDist <= iWantDistMax; } @@ -2540,85 +2540,85 @@ public abstract class BaseAI */ public virtual bool AcquireFocusMob(int iRange, FightMode acqType, bool bPlayerOnly, bool bFacFriend, bool bFacFoe) { - if (m_Mobile.Deleted) + if (Mobile.Deleted) { return false; } - if (m_Mobile.BardProvoked) + if (Mobile.BardProvoked) { - if (m_Mobile.BardTarget?.Deleted != false) + if (Mobile.BardTarget?.Deleted != false) { - m_Mobile.FocusMob = null; + Mobile.FocusMob = null; return false; } - m_Mobile.FocusMob = m_Mobile.BardTarget; - return m_Mobile.FocusMob != null; + Mobile.FocusMob = Mobile.BardTarget; + return Mobile.FocusMob != null; } - if (m_Mobile.Controlled) + if (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 (Mobile.ControlTarget?.Deleted != false || Mobile.ControlTarget.Hidden || + !Mobile.ControlTarget.Alive || Mobile.ControlTarget.IsDeadBondedPet || + !Mobile.InRange(Mobile.ControlTarget, Mobile.RangePerception * 2)) { - if (m_Mobile.ControlTarget != null && m_Mobile.ControlTarget != m_Mobile.ControlMaster) + if (Mobile.ControlTarget != null && Mobile.ControlTarget != Mobile.ControlMaster) { - m_Mobile.ControlTarget = null; + Mobile.ControlTarget = null; } - m_Mobile.FocusMob = null; + Mobile.FocusMob = null; return false; } - m_Mobile.FocusMob = m_Mobile.ControlTarget; - return m_Mobile.FocusMob != null; + Mobile.FocusMob = Mobile.ControlTarget; + return Mobile.FocusMob != null; } - if (m_Mobile.ConstantFocus != null) + if (Mobile.ConstantFocus != null) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("Acquired my constant focus"); + Mobile.DebugSay("Acquired my constant focus"); } - m_Mobile.FocusMob = m_Mobile.ConstantFocus; + Mobile.FocusMob = Mobile.ConstantFocus; return true; } if (acqType == FightMode.None) { - m_Mobile.FocusMob = null; + 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) + if (acqType == FightMode.Aggressor && Mobile.Aggressors.Count == 0 && Mobile.Aggressed.Count == 0 && + Mobile.FactionAllegiance == null && Mobile.EthicAllegiance == null) { - m_Mobile.FocusMob = null; + Mobile.FocusMob = null; return false; } - if (Core.TickCount - m_Mobile.NextReacquireTime < 0) + if (Core.TickCount - Mobile.NextReacquireTime < 0) { - m_Mobile.FocusMob = null; + Mobile.FocusMob = null; return false; } - m_Mobile.NextReacquireTime = Core.TickCount + (int)m_Mobile.ReacquireDelay.TotalMilliseconds; + Mobile.NextReacquireTime = Core.TickCount + (int)Mobile.ReacquireDelay.TotalMilliseconds; - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("Acquiring..."); + Mobile.DebugSay("Acquiring..."); } - var map = m_Mobile.Map; + var map = Mobile.Map; if (map == null) { // TODO: Is this correct? Maybe it should return false? - return m_Mobile.FocusMob != null; + return Mobile.FocusMob != null; } Mobile newFocusMob = null; @@ -2626,7 +2626,7 @@ public abstract class BaseAI Mobile enemySummonMob = null; var enemySummonVal = double.MinValue; - foreach (var m in map.GetMobilesInRange(m_Mobile.Location, iRange)) + foreach (var m in map.GetMobilesInRange(Mobile.Location, iRange)) { if (m.Deleted || m.Blessed) { @@ -2634,7 +2634,7 @@ public abstract class BaseAI } // Let's not target ourselves... - if (m == m_Mobile || m is BaseFamiliar) + if (m == Mobile || m is BaseFamiliar) { continue; } @@ -2658,7 +2658,7 @@ public abstract class BaseAI } // Can't acquire a target we can't see. - if (!m_Mobile.CanSee(m)) + if (!Mobile.CanSee(m)) { continue; } @@ -2667,34 +2667,34 @@ public abstract class BaseAI 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))) + if (Core.AOS && bc != null && bc.Summoned && (bc.SummonMaster == Mobile || !bc.SummonMaster.Player && IsHostile(bc.SummonMaster))) { continue; } - if (m_Mobile.Summoned && m_Mobile.SummonMaster != null) + if (Mobile.Summoned && Mobile.SummonMaster != null) { // Animated creatures cannot attack players directly. - if (pm != null && m_Mobile.IsAnimatedDead) + if (pm != null && 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)) + if (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))) + if (Mobile.FollowsAcquireRules && (m == Mobile.SummonMaster || !SpellHelper.ValidIndirectTarget(Mobile, m))) { continue; } } // If we only want faction friends, make sure it's one. - if (bFacFriend && !m_Mobile.IsFriend(m)) + if (bFacFriend && !Mobile.IsFriend(m)) { continue; } @@ -2706,7 +2706,7 @@ public abstract class BaseAI } // Ignore players with activated honor - if (m_Mobile.Combatant != m && VirtueSystem.GetVirtues(pm)?.HonorActive == true) + if (Mobile.Combatant != m && VirtueSystem.GetVirtues(pm)?.HonorActive == true) { continue; } @@ -2717,8 +2717,8 @@ public abstract class BaseAI if (!bValid) { - bValid = m_Mobile.GetFactionAllegiance(m) == BaseCreature.Allegiance.Enemy || - m_Mobile.GetEthicAllegiance(m) == BaseCreature.Allegiance.Enemy; + bValid = Mobile.GetFactionAllegiance(m) == BaseCreature.Allegiance.Enemy || + Mobile.GetEthicAllegiance(m) == BaseCreature.Allegiance.Enemy; } if (acqType == FightMode.Evil && !bValid) @@ -2741,56 +2741,56 @@ public abstract class BaseAI else { // Same goes for faction enemies. - if (bFacFoe && !m_Mobile.IsEnemy(m)) + if (bFacFoe && !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)) + if (bFacFoe && !bFacFriend && !Mobile.CanBeHarmful(m, false)) { continue; } } - var theirVal = m_Mobile.GetFightModeRanking(m, acqType, bPlayerOnly); + var theirVal = 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)) + if ((theirVal > val || newFocusMob == Mobile.SummonMaster) && 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) + else if (Core.AOS && theirVal > enemySummonVal && Mobile.InLOS(m) && bc?.Summoned == true && bc?.Controlled != true) { enemySummonMob = m; enemySummonVal = theirVal; } } - m_Mobile.FocusMob = newFocusMob ?? enemySummonMob; - return m_Mobile.FocusMob != null; + Mobile.FocusMob = newFocusMob ?? enemySummonMob; + return Mobile.FocusMob != null; } private bool IsHostile(Mobile from) { - if (m_Mobile.Combatant == from || from.Combatant == m_Mobile) + if (Mobile.Combatant == from || from.Combatant == Mobile) { return true; } - var count = Math.Max(m_Mobile.Aggressors.Count, m_Mobile.Aggressed.Count); + var count = Math.Max(Mobile.Aggressors.Count, Mobile.Aggressed.Count); for (var a = 0; a < count; ++a) { - if (a < m_Mobile.Aggressed.Count && m_Mobile.Aggressed[a].Attacker == from) + if (a < Mobile.Aggressed.Count && Mobile.Aggressed[a].Attacker == from) { return true; } - if (a < m_Mobile.Aggressors.Count && m_Mobile.Aggressors[a].Defender == from) + if (a < Mobile.Aggressors.Count && Mobile.Aggressors[a].Defender == from) { return true; } @@ -2801,31 +2801,31 @@ public abstract class BaseAI public virtual void DetectHidden() { - if (m_Mobile.Deleted || m_Mobile.Map == null) + if (Mobile.Deleted || Mobile.Map == null) { return; } - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("Checking for hidden players"); + Mobile.DebugSay("Checking for hidden players"); } - var srcSkill = m_Mobile.Skills.DetectHidden.Value; + var srcSkill = Mobile.Skills.DetectHidden.Value; if (srcSkill <= 0) { return; } - foreach (var trg in m_Mobile.GetMobilesInRange(m_Mobile.RangePerception)) + foreach (var trg in Mobile.GetMobilesInRange(Mobile.RangePerception)) { - if (trg != m_Mobile && trg.Player && trg.Alive && trg.Hidden && trg.AccessLevel == AccessLevel.Player && - m_Mobile.InLOS(trg)) + if (trg != Mobile && trg.Player && trg.Alive && trg.Hidden && trg.AccessLevel == AccessLevel.Player && + Mobile.InLOS(trg)) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay($"Trying to detect {trg.Name}"); + Mobile.DebugSay($"Trying to detect {trg.Name}"); } var trgHiding = trg.Skills.Hiding.Value / 2.9; @@ -2851,15 +2851,15 @@ public abstract class BaseAI public virtual void Deactivate() { - if (m_Mobile.PlayerRangeSensitive) + if (Mobile.PlayerRangeSensitive) { - m_Timer.Stop(); + _timer.Stop(); - var spawner = m_Mobile.Spawner; + var spawner = 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) + if (spawner?.ReturnOnDeactivate == true && !Mobile.Controlled && ( + spawner.HomeLocation == Point3D.Zero && !Mobile.Region.AcceptsSpawnsFrom(spawner.Region) || + !Mobile.InRange(spawner.HomeLocation, spawner.HomeRange) )) { Timer.StartTimer(ReturnToHome); @@ -2869,32 +2869,32 @@ public abstract class BaseAI private void ReturnToHome() { - if (m_Mobile.Spawner != null) + if (Mobile.Spawner != null) { - var loc = m_Mobile.Spawner.GetSpawnPosition(m_Mobile, m_Mobile.Spawner.Map); + var loc = Mobile.Spawner.GetSpawnPosition(Mobile, Mobile.Spawner.Map); if (loc != Point3D.Zero) { - m_Mobile.MoveToWorld(loc, m_Mobile.Spawner.Map); + Mobile.MoveToWorld(loc, Mobile.Spawner.Map); } } } public virtual void Activate() { - if (!m_Timer.Running) + if (!_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(); + _timer.Delay = TimeSpan.FromMilliseconds(Utility.Random(48) * 8); + _timer.Start(); } } public virtual void OnCurrentSpeedChanged() { - m_Timer.Interval = TimeSpan.FromSeconds(Math.Max(0.008, m_Mobile.CurrentSpeed)); + _timer.Interval = TimeSpan.FromSeconds(Math.Max(0.008, Mobile.CurrentSpeed)); } private class InternalEntry : ContextMenuEntry @@ -2983,12 +2983,12 @@ public abstract class BaseAI private class TransferItem : Item { - private readonly BaseCreature m_Creature; + private readonly BaseCreature _creature; public TransferItem(BaseCreature creature) : base(ShrinkTable.Lookup(creature)) { - m_Creature = creature; + _creature = creature; Movable = false; @@ -3036,11 +3036,11 @@ public abstract class BaseAI 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~ + list.Add(1041601, _creature.Name); // Pet Name: ~1_val~ - if (m_Creature.ControlMaster != null) + if (_creature.ControlMaster != null) { - list.Add(1041602, m_Creature.ControlMaster.Name); // Owner: ~1_val~ + list.Add(1041602, _creature.ControlMaster.Name); // Owner: ~1_val~ } } @@ -3051,13 +3051,13 @@ public abstract class BaseAI return false; } - if (Deleted || m_Creature?.Deleted != false || m_Creature.ControlMaster != from || + if (Deleted || _creature?.Deleted != false || _creature.ControlMaster != from || !from.CheckAlive() || !to.CheckAlive()) { return false; } - if (from.Map != m_Creature.Map || !from.InRange(m_Creature, 14)) + if (from.Map != _creature.Map || !from.InRange(_creature, 14)) { return false; } @@ -3073,7 +3073,7 @@ public abstract class BaseAI { from.SendLocalizedMessage(502052); // As an older player, you may not transfer pets to young players. } - else if (accepted && !m_Creature.CanBeControlledBy(to)) + else if (accepted && !_creature.CanBeControlledBy(to)) { var args = $"{to.Name}\t{from.Name}\t "; @@ -3084,7 +3084,7 @@ public abstract class BaseAI return false; } - else if (accepted && !m_Creature.CanBeControlledBy(from)) + else if (accepted && !_creature.CanBeControlledBy(from)) { var args = $"{to.Name}\t{from.Name}\t "; @@ -3093,13 +3093,13 @@ public abstract class BaseAI // 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) + else if (accepted && to.Followers + _creature.ControlSlots > to.FollowersMax) { to.SendLocalizedMessage(1049607); // You have too many followers to control that creature. return false; } - else if (accepted && IsInCombat(m_Creature)) + else if (accepted && IsInCombat(_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."); @@ -3119,34 +3119,34 @@ public abstract class BaseAI Delete(); - if (m_Creature?.Deleted != false || m_Creature.ControlMaster != from || !from.CheckAlive() || + if (_creature?.Deleted != false || _creature.ControlMaster != from || !from.CheckAlive() || !to.CheckAlive()) { return; } - if (from.Map != m_Creature.Map || !from.InRange(m_Creature, 14)) + if (from.Map != _creature.Map || !from.InRange(_creature, 14)) { return; } - if (accepted && m_Creature.SetControlMaster(to)) + if (accepted && _creature.SetControlMaster(to)) { - if (m_Creature.Summoned) + if (_creature.Summoned) { - m_Creature.SummonMaster = to; + _creature.SummonMaster = to; } - m_Creature.ControlTarget = to; - m_Creature.ControlOrder = OrderType.Follow; + _creature.ControlTarget = to; + _creature.ControlOrder = OrderType.Follow; - m_Creature.BondingBegin = DateTime.MinValue; - m_Creature.OwnerAbandonTime = DateTime.MinValue; - m_Creature.IsBonded = false; + _creature.BondingBegin = DateTime.MinValue; + _creature.OwnerAbandonTime = DateTime.MinValue; + _creature.IsBonded = false; - m_Creature.PlaySound(m_Creature.GetIdleSound()); + _creature.PlaySound(_creature.GetIdleSound()); - var args = $"{from.Name}\t{m_Creature.Name}\t{to.Name}"; + var args = $"{from.Name}\t{_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. @@ -3160,88 +3160,88 @@ public abstract class BaseAI */ private class AITimer : Timer { - private readonly BaseAI m_Owner; + private readonly BaseAI _owner; public AITimer(BaseAI owner) : base( TimeSpan.FromMilliseconds(Utility.Random(96) * 8), - TimeSpan.FromSeconds(Math.Max(0.0, owner.m_Mobile.CurrentSpeed)) + TimeSpan.FromSeconds(Math.Max(0.0, owner.Mobile.CurrentSpeed)) ) { - m_Owner = owner; - m_Owner.m_NextDetectHidden = Core.TickCount; + _owner = owner; + _owner._nextDetectHidden = Core.TickCount; } protected override void OnTick() { - if (m_Owner.m_Mobile.Deleted) + if (_owner.Mobile.Deleted) { Stop(); return; } - if (m_Owner.m_Mobile.Map == null || m_Owner.m_Mobile.Map == Map.Internal) + if (_owner.Mobile.Map == null || _owner.Mobile.Map == Map.Internal) { - m_Owner.Deactivate(); + _owner.Deactivate(); return; } - if (m_Owner.m_Mobile.PlayerRangeSensitive) // have to check this in the timer.... + if (_owner.Mobile.PlayerRangeSensitive) // have to check this in the timer.... { - var sect = m_Owner.m_Mobile.Map.GetSector(m_Owner.m_Mobile.Location); + var sect = _owner.Mobile.Map.GetSector(_owner.Mobile.Location); if (!sect.Active) { - m_Owner.Deactivate(); + _owner.Deactivate(); return; } } - m_Owner.m_Mobile.OnThink(); + _owner.Mobile.OnThink(); - if (m_Owner.m_Mobile.Deleted) + if (_owner.Mobile.Deleted) { Stop(); return; } - if (m_Owner.m_Mobile.Map == null || m_Owner.m_Mobile.Map == Map.Internal) + if (_owner.Mobile.Map == null || _owner.Mobile.Map == Map.Internal) { - m_Owner.Deactivate(); + _owner.Deactivate(); return; } - if (m_Owner.m_Mobile.BardPacified) + if (_owner.Mobile.BardPacified) { - m_Owner.DoBardPacified(); + _owner.DoBardPacified(); } - else if (m_Owner.m_Mobile.BardProvoked) + else if (_owner.Mobile.BardProvoked) { - m_Owner.DoBardProvoked(); + _owner.DoBardProvoked(); } - else if (!m_Owner.m_Mobile.Controlled) + else if (!_owner.Mobile.Controlled) { - if (!m_Owner.Think()) + if (!_owner.Think()) { Stop(); return; } } - else if (!m_Owner.Obey()) + else if (!_owner.Obey()) { Stop(); return; } - if (m_Owner.CanDetectHidden && Core.TickCount - m_Owner.m_NextDetectHidden >= 0) + if (_owner.CanDetectHidden && Core.TickCount - _owner._nextDetectHidden >= 0) { - m_Owner.DetectHidden(); + _owner.DetectHidden(); // Not exactly OSI style, approximation. - var delay = Math.Min(15000 / m_Owner.m_Mobile.Int, 60); + var delay = Math.Min(15000 / _owner.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); + _owner._nextDetectHidden = Core.TickCount + Utility.RandomMinMax(min, max); } } } diff --git a/Projects/UOContent/Mobiles/AI/BerserkAI.cs b/Projects/UOContent/Mobiles/AI/BerserkAI.cs index db7a994ba..207d1f727 100644 --- a/Projects/UOContent/Mobiles/AI/BerserkAI.cs +++ b/Projects/UOContent/Mobiles/AI/BerserkAI.cs @@ -8,19 +8,19 @@ public class BerserkAI : BaseAI public override bool DoActionWander() { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("I have no combatant"); + Mobile.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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name} and I will attack"); + Mobile.DebugSay($"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,48 +33,48 @@ 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) + if (Mobile.Debug) { - m_Mobile.DebugSay("My combatant is gone, so my guard is up"); + Mobile.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, true, Mobile.RangeFight, Mobile.RangeFight)) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I am still not in range of {combatant.Name}"); + Mobile.DebugSay($"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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I have lost {combatant.Name}"); + Mobile.DebugSay($"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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I used my abilities on {combatant.Name}!"); + Mobile.DebugSay($"I used my abilities on {combatant.Name}!"); } } @@ -83,14 +83,14 @@ 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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name}, attacking"); + Mobile.DebugSay($"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..6b4dfa2ed 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,12 +20,12 @@ 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) { @@ -36,79 +33,79 @@ public class HealerAI : BaseAI if (spellTarg?.Spell is CureSpell) { - ProcessTarget(targ, m_ACure); + ProcessTarget(targ, Cure); } else if (spellTarg?.Spell is GreaterHealSpell) { - ProcessTarget(targ, m_AGHeal); + ProcessTarget(targ, GHeal); } else if (spellTarg?.Spell is HealSpell) { - ProcessTarget(targ, m_ALHeal); + ProcessTarget(targ, LHeal); } else { - targ.Cancel(m_Mobile, TargetCancelType.Canceled); + targ.Cancel(Mobile, TargetCancelType.Canceled); } return true; } - var toHelp = Find(m_All); + var toHelp = Find(AllHeal); if (toHelp != null) { if (NeedCure(toHelp)) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay($"{toHelp.Name} needs a cure"); + Mobile.DebugSay($"{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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"{toHelp.Name} needs a greater heal"); + Mobile.DebugSay($"{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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"{toHelp.Name} needs a lesser heal"); + Mobile.DebugSay($"{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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I used my abilities on {m_Mobile.Combatant.Name}!"); + Mobile.DebugSay($"I used my abilities on {Mobile.Combatant.Name}!"); } } @@ -121,26 +118,26 @@ public class HealerAI : BaseAI 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 +147,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 +158,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..d86aa67ad 100644 --- a/Projects/UOContent/Mobiles/AI/MageAI.cs +++ b/Projects/UOContent/Mobiles/AI/MageAI.cs @@ -20,7 +20,7 @@ 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, @@ -49,27 +49,27 @@ public class MageAI : BaseAI 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 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 +77,38 @@ 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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I am going to attack {m_Mobile.FocusMob.Name}"); + Mobile.DebugSay($"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) + if (Mobile.Debug) { - m_Mobile.DebugSay("I am going to meditate"); + Mobile.DebugSay("I am going to meditate"); } - m_Mobile.UseSkill(SkillName.Meditation); + Mobile.UseSkill(SkillName.Meditation); } else { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("I am wandering"); + Mobile.DebugSay("I am wandering"); } - m_Mobile.Warmode = false; + Mobile.Warmode = false; base.DoActionWander(); @@ -126,13 +126,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 +144,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 +178,7 @@ public class MageAI : BaseAI { if (!SmartAI) { - if (!MoveTo(m, true, m_Mobile.RangeFight)) + if (!MoveTo(m, true, Mobile.RangeFight)) { OnFailedMove(); } @@ -188,23 +188,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, true, 1)) { OnFailedMove(); } } - else if (!m_Mobile.InRange(m, m_Mobile.RangeFight)) + else 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); } @@ -212,51 +212,51 @@ 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) + if (Mobile.Debug) { - m_Mobile.DebugSay("I am stuck, I'm going to try teleporting away"); + Mobile.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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"My move is blocked, so I am going to attack {m_Mobile.FocusMob.Name}"); + Mobile.DebugSay($"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 if (Mobile.Debug) { - m_Mobile.DebugSay("I am stuck"); + Mobile.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 +264,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 +274,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 +315,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 +353,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 +366,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 +396,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,30 +415,30 @@ public class MageAI : BaseAI goto default; } - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("Attempting to poison"); + Mobile.DebugSay("Attempting to poison"); } - spell = new PoisonSpell(m_Mobile); + spell = new PoisonSpell(Mobile); break; } case 2: // Bless ourselves { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("Blessing myself"); + Mobile.DebugSay("Blessing myself"); } - spell = new BlessSpell(m_Mobile); + spell = new BlessSpell(Mobile); break; } case 3: case 4: // Curse them { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("Attempting to curse"); + Mobile.DebugSay("Attempting to curse"); } spell = GetRandomCurseSpell(); @@ -446,24 +446,24 @@ public class MageAI : BaseAI } 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) + if (Mobile.Debug) { - m_Mobile.DebugSay("Attempting to paralyze"); + Mobile.DebugSay("Attempting to paralyze"); } - spell = new ParalyzeSpell(m_Mobile); + spell = new ParalyzeSpell(Mobile); break; } case 6: // Drain mana { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("Attempting to drain mana"); + Mobile.DebugSay("Attempting to drain mana"); } spell = GetRandomManaDrainSpell(); @@ -476,19 +476,19 @@ public class MageAI : BaseAI goto default; } - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("Attempting to invis myself"); + Mobile.DebugSay("Attempting to invis myself"); } - spell = new InvisibilitySpell(m_Mobile); + spell = new InvisibilitySpell(Mobile); break; } default: // Damage them { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("Just doing damage"); + Mobile.DebugSay("Just doing damage"); } spell = GetRandomDamageSpell(); @@ -511,7 +511,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 +521,33 @@ 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) + if (Mobile.Debug) { - m_Mobile.DebugSay("I am going to meditate"); + Mobile.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,15 +557,15 @@ public class MageAI : BaseAI } } - if (m_Mobile.Debug) + if (Mobile.Debug) { if (spell != null) { - m_Mobile.DebugSay($"Casting {spell.Name}"); + Mobile.DebugSay($"Casting {spell.Name}"); } else { - m_Mobile.DebugSay("I don't have a spell to use!"); + Mobile.DebugSay("I don't have a spell to use!"); } } @@ -576,31 +576,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 +608,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 +652,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,33 +664,33 @@ 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) + if (Mobile.Debug) { - m_Mobile.DebugSay( + Mobile.DebugSay( $"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) + if (Mobile.Debug) { - m_Mobile.DebugSay("Something happened to my combatant, and nothing is around. I am on guard."); + Mobile.DebugSay("Something happened to my combatant, and nothing is around. I am on guard."); } Action = ActionType.Guard; @@ -698,53 +698,53 @@ public class MageAI : BaseAI } } - if (!m_Mobile.InLOS(c)) + if (!Mobile.InLOS(c)) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("I can't see my target"); + Mobile.DebugSay("I can't see my target"); } - 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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I will switch to {c.Name}"); + 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) + if (Mobile.Debug) { - m_Mobile.DebugSay("My combatant has fled, so I am on guard"); + Mobile.DebugSay("My combatant has fled, so I am on guard"); } Action = ActionType.Guard; @@ -752,17 +752,17 @@ public class MageAI : BaseAI } } - 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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I am going to flee from {c.Name}"); + Mobile.DebugSay($"I am going to flee from {c.Name}"); } Action = ActionType.Flee; @@ -770,33 +770,33 @@ public class MageAI : BaseAI } } - if (m_Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, c)) + if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, c)) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("I used my abilities!"); + Mobile.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) { // 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) + if (Mobile.Debug) { - m_Mobile.DebugSay("I am going to cure myself"); + Mobile.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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I am going to dispel {toDispel}"); + Mobile.DebugSay($"I am going to dispel {toDispel}"); } spell = DoDispel(toDispel); // May return null if dumb AI and doesn't have enough skill @@ -809,96 +809,96 @@ 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.Spell != null || !Mobile.InRange(c, 1) || Core.TickCount - Mobile.LastMoveTime > 800) { - 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) + if (Mobile.Debug) { - m_Mobile.DebugSay("I am going to reveal my last target"); + Mobile.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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I am going to attack {m_Mobile.FocusMob.Name}"); + Mobile.DebugSay($"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(); @@ -917,39 +917,39 @@ public class MageAI : BaseAI { // 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) + if (Mobile.Debug) { - m_Mobile.DebugSay("I am stronger now, my guard is up"); + Mobile.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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I am scared of {m_Mobile.FocusMob.Name}"); + Mobile.DebugSay($"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) + if (Mobile.Debug) { - m_Mobile.DebugSay("Area seems clear, but my guard is up"); + Mobile.DebugSay("Area seems clear, but my guard is up"); } Action = ActionType.Guard; - m_Mobile.Warmode = true; + Mobile.Warmode = true; } return true; @@ -957,26 +957,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 +989,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 +1011,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 +1031,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 +1058,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 +1073,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 +1098,7 @@ public class MageAI : BaseAI if (isInvisible) { - toTarget = m_Mobile; + toTarget = Mobile; } else if (isDispel) { @@ -1108,7 +1108,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 +1119,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 +1138,7 @@ public class MageAI : BaseAI } else { - toTarget = m_Mobile.Combatant; + toTarget = Mobile.Combatant; if (toTarget != null) { @@ -1148,27 +1148,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 +1179,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 +1193,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 +1212,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 f431b1d23..075b4f06c 100644 --- a/Projects/UOContent/Mobiles/AI/MeleeAI.cs +++ b/Projects/UOContent/Mobiles/AI/MeleeAI.cs @@ -10,24 +10,24 @@ 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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name}, attacking"); + Mobile.DebugSay($"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) + if (Mobile.Debug) { - m_Mobile.DebugSay("I am wandering"); + Mobile.DebugSay("I am wandering"); } - m_Mobile.Warmode = false; + Mobile.Warmode = false; base.DoActionWander(); } @@ -37,41 +37,41 @@ 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) + if (Mobile.Debug) { - m_Mobile.DebugSay("My combatant is gone, so my guard is up"); + Mobile.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) + if (Mobile.Debug) { - m_Mobile.DebugSay("My combatant has fled, so I am on guard"); + Mobile.DebugSay("My combatant has fled, so I am on guard"); } Action = ActionType.Guard; @@ -79,58 +79,58 @@ public class MeleeAI : BaseAI } } - if (!MoveTo(combatant, true, m_Mobile.RangeFight)) + if (!MoveTo(combatant, true, Mobile.RangeFight)) { - if (m_Mobile.InRange(combatant, 1)) + if (Mobile.InRange(combatant, 1)) { - m_Mobile.Direction = m_Mobile.GetDirectionTo(combatant); + Mobile.Direction = 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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"My move is blocked, so I am going to attack {m_Mobile.FocusMob!.Name}"); + Mobile.DebugSay($"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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I cannot find {combatant.Name}, so my guard is up"); + Mobile.DebugSay($"I cannot find {combatant.Name}, so my guard is up"); } Action = ActionType.Guard; return true; } - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I cannot find {combatant.Name}, so my guard is up"); + Mobile.DebugSay($"I cannot find {combatant.Name}, so my guard is up"); } } - 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) { - if (m_Mobile.Hits < m_Mobile.HitsMax * 20 / 100) + if (Mobile.Hits < Mobile.HitsMax * 20 / 100) { // We are low on health, should we flee? - var fleeChance = 10 + Math.Max(0, combatant.Hits - m_Mobile.Hits); // (10 + diff)% chance to flee; + var fleeChance = 10 + Math.Max(0, combatant.Hits - Mobile.Hits); // (10 + diff)% chance to flee; if (Utility.Random(0, 100) < fleeChance) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I am going to flee from {combatant.Name}"); + Mobile.DebugSay($"I am going to flee from {combatant.Name}"); } Action = ActionType.Flee; @@ -139,11 +139,11 @@ public class MeleeAI : BaseAI } } - if (m_Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) + if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("I used my abilities!"); + Mobile.DebugSay("I used my abilities!"); } } @@ -152,14 +152,14 @@ 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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name}, attacking"); + Mobile.DebugSay($"I have detected {Mobile.FocusMob.Name}, attacking"); } - m_Mobile.Combatant = m_Mobile.FocusMob; + Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; } else @@ -172,20 +172,20 @@ 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) + if (Mobile.Debug) { - m_Mobile.DebugSay("I am stronger now, so I will continue fighting"); + Mobile.DebugSay("I am stronger now, so I will continue fighting"); } - m_Mobile.PlaySound(m_Mobile.GetAttackSound()); - m_Mobile.CurrentSpeed = m_Mobile.ActiveSpeed; + Mobile.PlaySound(Mobile.GetAttackSound()); + Mobile.CurrentSpeed = Mobile.ActiveSpeed; 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..c087112db 100644 --- a/Projects/UOContent/Mobiles/AI/OppositionGroup.cs +++ b/Projects/UOContent/Mobiles/AI/OppositionGroup.cs @@ -5,9 +5,9 @@ namespace Server { public class OppositionGroup { - private readonly Type[][] m_Types; + private readonly Type[][] _types; - public OppositionGroup(Type[][] types) => m_Types = types; + public OppositionGroup(Type[][] types) => _types = types; public static OppositionGroup TerathansAndOphidians { get; } = new( new[] @@ -112,9 +112,9 @@ namespace Server var type = obj.GetType(); - for (var i = 0; i < m_Types.Length; ++i) + for (var i = 0; i < _types.Length; ++i) { - var group = m_Types[i]; + var group = _types[i]; var contains = false; diff --git a/Projects/UOContent/Mobiles/AI/PredatorAI.cs b/Projects/UOContent/Mobiles/AI/PredatorAI.cs index 7532595df..31a683ca5 100644 --- a/Projects/UOContent/Mobiles/AI/PredatorAI.cs +++ b/Projects/UOContent/Mobiles/AI/PredatorAI.cs @@ -8,20 +8,20 @@ public class PredatorAI : BaseAI public override bool DoActionWander() { - if (m_Mobile.Combatant != null) + if (Mobile.Combatant != null) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("I am hurt or being attacked, I kill him"); + Mobile.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) + if (Mobile.Debug) { - m_Mobile.DebugSay("There is something near, I go away"); + Mobile.DebugSay("There is something near, I go away"); } Action = ActionType.Backoff; @@ -36,44 +36,44 @@ 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) + if (Mobile.Debug) { - m_Mobile.DebugSay("My combatant is gone, so my guard is up"); + Mobile.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, true, Mobile.RangeFight, Mobile.RangeFight)) { - if (m_Mobile.GetDistanceToSqrt(combatant) > m_Mobile.RangePerception + 1) + if (Mobile.GetDistanceToSqrt(combatant) > Mobile.RangePerception + 1) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I cannot find {combatant.Name}"); + Mobile.DebugSay($"I cannot find {combatant.Name}"); } Action = ActionType.Wander; return true; } - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I should be closer to {combatant.Name}"); + Mobile.DebugSay($"I should be closer to {combatant.Name}"); } } - if (m_Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) + if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I used my abilities on {combatant.Name}!"); + Mobile.DebugSay($"I used my abilities on {combatant.Name}!"); } } @@ -82,17 +82,17 @@ 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) + if (Mobile.Debug) { - m_Mobile.DebugSay("Well, here I am safe"); + Mobile.DebugSay("Well, here I am safe"); } Action = ActionType.Wander; @@ -100,9 +100,9 @@ public class PredatorAI : BaseAI } else { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("I have lost my focus, lets relax"); + Mobile.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..30adec3cf 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,19 @@ public class ThiefAI : BaseAI public override bool DoActionWander() { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("I have no combatant"); + Mobile.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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name}, attacking"); + Mobile.DebugSay($"I have detected {Mobile.FocusMob.Name}, attacking"); } - m_Mobile.Combatant = m_Mobile.FocusMob; + Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; } else @@ -38,57 +38,57 @@ 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) + if (Mobile.Debug) { - m_Mobile.DebugSay("My combatant is gone, so my guard is up"); + Mobile.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, true, Mobile.RangeFight, Mobile.RangeFight)) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I should be closer to {combatant.Name}"); + Mobile.DebugSay($"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) + if (Mobile.Debug) { - m_Mobile.DebugSay("Trying to steal from combatant."); + Mobile.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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"Trying to steal from {combatant.Name}."); + Mobile.DebugSay($"Trying to steal from {combatant.Name}."); } bool didSteal = TryStealFrom(combatant); @@ -98,9 +98,9 @@ public class ThiefAI : BaseAI if (!didSteal) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I am going to flee from {combatant.Name}"); + Mobile.DebugSay($"I am going to flee from {combatant.Name}"); } Action = ActionType.Flee; @@ -111,15 +111,15 @@ 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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I am going to flee from {combatant.Name}"); + Mobile.DebugSay($"I am going to flee from {combatant.Name}"); } Action = ActionType.Flee; @@ -128,11 +128,11 @@ 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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I used my abilities on {m_Mobile.Combatant.Name}!"); + Mobile.DebugSay($"I used my abilities on {Mobile.Combatant.Name}!"); } } return true; @@ -143,8 +143,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 +153,14 @@ 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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I have detected {m_Mobile.FocusMob.Name}, attacking"); + Mobile.DebugSay($"I have detected {Mobile.FocusMob.Name}, attacking"); } - m_Mobile.Combatant = m_Mobile.FocusMob; + Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; } else @@ -173,18 +173,18 @@ 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) + if (Mobile.Debug) { - m_Mobile.DebugSay("I am stronger now, so I will continue fighting"); + Mobile.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..7b2e7950a 100644 --- a/Projects/UOContent/Mobiles/AI/VendorAI.cs +++ b/Projects/UOContent/Mobiles/AI/VendorAI.cs @@ -15,35 +15,35 @@ public class VendorAI : BaseAI public override bool DoActionWander() { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay("I'm fine"); + Mobile.DebugSay("I'm fine"); } - if (m_Mobile.Combatant != null) + if (Mobile.Combatant != null) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay($"{m_Mobile.Combatant.Name} is attacking me"); + Mobile.DebugSay($"{Mobile.Combatant.Name} is attacking me"); } - m_Mobile.Say(GetRandomGuardMessage()); + Mobile.Say(GetRandomGuardMessage()); Action = ActionType.Flee; } else { - if (m_Mobile.FocusMob != null) + if (Mobile.FocusMob != null) { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay($"{m_Mobile.FocusMob.Name} has talked to me"); + Mobile.DebugSay($"{Mobile.FocusMob.Name} has talked to me"); } Action = ActionType.Interact; } else { - m_Mobile.Warmode = false; + Mobile.Warmode = false; base.DoActionWander(); } @@ -54,50 +54,50 @@ 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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"{m_Mobile.Combatant.Name} is attacking me"); + Mobile.DebugSay($"{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) + if (Mobile.Debug) { - m_Mobile.DebugSay("My customer have disapeared"); + Mobile.DebugSay("My customer have disapeared"); } - 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) + if (Mobile.Debug) { - m_Mobile.DebugSay($"I am with {customer.Name}"); + Mobile.DebugSay($"I am with {customer.Name}"); } - m_Mobile.Direction = m_Mobile.GetDirectionTo(customer); + Mobile.Direction = Mobile.GetDirectionTo(customer); } else { - if (m_Mobile.Debug) + if (Mobile.Debug) { - m_Mobile.DebugSay($"{customer.Name} is gone"); + Mobile.DebugSay($"{customer.Name} is gone"); } - m_Mobile.FocusMob = null; + Mobile.FocusMob = null; Action = ActionType.Wander; } @@ -106,13 +106,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 +127,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 0957a8c22..d15bf8ef8 100644 --- a/Projects/UOContent/Mobiles/BaseCreature.cs +++ b/Projects/UOContent/Mobiles/BaseCreature.cs @@ -2258,7 +2258,7 @@ namespace Server.Mobiles public void ChangeAIType(AIType newAI) { - AIObject?.m_Timer.Stop(); + AIObject?._timer.Stop(); if (ForcedAI != null) { @@ -2381,7 +2381,7 @@ namespace Server.Mobiles { if (AIObject != null) { - AIObject.m_Timer?.Stop(); + AIObject._timer?.Stop(); AIObject = null; } 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); From f89150735e592522d37aec3de7bdece2db2804d3 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Fri, 18 Jul 2025 18:08:03 -0700 Subject: [PATCH 06/47] fix: Consolidates debug check messages for AI and adds debounce (#2235) --- .../Factions/Mobiles/Guards/GuardAI.cs | 9 +- Projects/UOContent/Mobiles/AI/AnimalAI.cs | 45 +- Projects/UOContent/Mobiles/AI/ArcherAI.cs | 35 +- Projects/UOContent/Mobiles/AI/BaseAI.cs | 257 ++----- .../Mobiles/AI/BaseAI/BaseAIExtensions.cs | 20 + .../BaseAI/DebugInterpolatedStringHandler.cs | 662 ++++++++++++++++++ Projects/UOContent/Mobiles/AI/BerserkAI.cs | 35 +- Projects/UOContent/Mobiles/AI/HealerAI.cs | 20 +- Projects/UOContent/Mobiles/AI/MageAI.cs | 155 +--- Projects/UOContent/Mobiles/AI/MeleeAI.cs | 55 +- Projects/UOContent/Mobiles/AI/PredatorAI.cs | 40 +- Projects/UOContent/Mobiles/AI/ThiefAI.cs | 55 +- Projects/UOContent/Mobiles/AI/VendorAI.cs | 35 +- Projects/UOContent/Mobiles/BaseCreature.cs | 9 +- 14 files changed, 838 insertions(+), 594 deletions(-) create mode 100644 Projects/UOContent/Mobiles/AI/BaseAI/BaseAIExtensions.cs create mode 100644 Projects/UOContent/Mobiles/AI/BaseAI/DebugInterpolatedStringHandler.cs diff --git a/Projects/UOContent/Engines/Factions/Mobiles/Guards/GuardAI.cs b/Projects/UOContent/Engines/Factions/Mobiles/Guards/GuardAI.cs index 85662a887..c89dd6e98 100644 --- a/Projects/UOContent/Engines/Factions/Mobiles/Guards/GuardAI.cs +++ b/Projects/UOContent/Engines/Factions/Mobiles/Guards/GuardAI.cs @@ -398,17 +398,14 @@ namespace Server.Factions else*/ if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - if (Mobile.Debug) - { - Mobile.DebugSay($"My move is blocked, so I am going to attack {Mobile.FocusMob.Name}"); - } + this.DebugSayFormatted($"My move is blocked, so I am going to attack {Mobile.FocusMob.Name}"); Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; } - else if (Mobile.Debug) + else { - Mobile.DebugSay("I am stuck"); + DebugSay("I am stuck"); } } diff --git a/Projects/UOContent/Mobiles/AI/AnimalAI.cs b/Projects/UOContent/Mobiles/AI/AnimalAI.cs index 481b22747..9e33414c7 100644 --- a/Projects/UOContent/Mobiles/AI/AnimalAI.cs +++ b/Projects/UOContent/Mobiles/AI/AnimalAI.cs @@ -23,19 +23,13 @@ public class AnimalAI : BaseAI if (!Mobile.Summoned && !Mobile.Controlled && hitPercent < 0.1 && Mobile.CanFlee) // Less than 10% health { - if (Mobile.Debug) - { - Mobile.DebugSay("I am low on health!"); - } + DebugSay("I am low on health!"); Action = ActionType.Flee; } else if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - if (Mobile.Debug) - { - Mobile.DebugSay($"I have detected {Mobile.FocusMob.Name}, attacking"); - } + this.DebugSayFormatted($"I have detected {Mobile.FocusMob.Name}, attacking"); Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; @@ -55,10 +49,7 @@ public class AnimalAI : BaseAI if (combatant == null || combatant.Deleted || combatant.Map != Mobile.Map || !combatant.Alive || combatant.IsDeadBondedPet) { - if (Mobile.Debug) - { - Mobile.DebugSay("My combatant is gone!"); - } + DebugSay("My combatant is gone!"); Action = ActionType.Wander; return true; @@ -68,19 +59,13 @@ public class AnimalAI : BaseAI { if (Mobile.GetDistanceToSqrt(combatant) > Mobile.RangePerception + 1) { - if (Mobile.Debug) - { - Mobile.DebugSay($"I cannot find {combatant.Name}"); - } + this.DebugSayFormatted($"I cannot find {combatant.Name}"); Action = ActionType.Wander; return true; } - if (Mobile.Debug) - { - Mobile.DebugSay($"I should be closer to {combatant.Name}"); - } + this.DebugSayFormatted($"I should be closer to {combatant.Name}"); } else if (Core.TickCount - Mobile.LastMoveTime > 400) { @@ -93,10 +78,7 @@ public class AnimalAI : BaseAI if (hitPercent <= 0.1) { - if (Mobile.Debug) - { - Mobile.DebugSay("I am low on health!"); - } + DebugSay("I am low on health!"); Action = ActionType.Flee; return true; @@ -105,10 +87,7 @@ public class AnimalAI : BaseAI if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) { - if (Mobile.Debug) - { - Mobile.DebugSay($"I used my abilities on {combatant.Name}!"); - } + this.DebugSayFormatted($"I used my abilities on {combatant.Name}!"); } return true; @@ -126,20 +105,14 @@ public class AnimalAI : BaseAI { if (WalkMobileRange(Mobile.FocusMob, 1, false, Mobile.RangePerception, Mobile.RangePerception * 2)) { - if (Mobile.Debug) - { - Mobile.DebugSay("Well, here I am safe"); - } + DebugSay("Well, here I am safe"); Action = ActionType.Wander; } } else { - if (Mobile.Debug) - { - 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/ArcherAI.cs b/Projects/UOContent/Mobiles/AI/ArcherAI.cs index 660ca702f..6491c42d5 100644 --- a/Projects/UOContent/Mobiles/AI/ArcherAI.cs +++ b/Projects/UOContent/Mobiles/AI/ArcherAI.cs @@ -11,17 +11,11 @@ public class ArcherAI : BaseAI public override bool DoActionWander() { - if (Mobile.Debug) - { - Mobile.DebugSay("I have no combatant"); - } + DebugSay("I have no combatant"); if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - if (Mobile.Debug) - { - Mobile.DebugSay($"I have detected {Mobile.FocusMob.Name} and I will attack"); - } + this.DebugSayFormatted($"I have detected {Mobile.FocusMob.Name} and I will attack"); Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; @@ -41,10 +35,7 @@ public class ArcherAI : BaseAI if (combatant == null || combatant.Deleted || combatant.Map != Mobile.Map || !combatant.Alive || combatant.IsDeadBondedPet) { - if (Mobile.Debug) - { - 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; @@ -58,17 +49,11 @@ public class ArcherAI : BaseAI Mobile.Weapon.MaxRange )) { - if (Mobile.Debug) - { - Mobile.DebugSay($"I am still not in range of {combatant.Name}"); - } + this.DebugSayFormatted($"I am still not in range of {combatant.Name}"); if ((int)Mobile.GetDistanceToSqrt(combatant) > Mobile.RangePerception + 1) { - if (Mobile.Debug) - { - Mobile.DebugSay($"I have lost {combatant.Name}"); - } + this.DebugSayFormatted($"I have lost {combatant.Name}"); Mobile.Combatant = null; Action = ActionType.Guard; @@ -82,10 +67,7 @@ public class ArcherAI : BaseAI if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) { - if (Mobile.Debug) - { - Mobile.DebugSay($"I used my abilities on {combatant.Name}!"); - } + this.DebugSayFormatted($"I used my abilities on {combatant.Name}!"); return true; } @@ -118,10 +100,7 @@ public class ArcherAI : BaseAI { if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - if (Mobile.Debug) - { - Mobile.DebugSay($"I have detected {Mobile.FocusMob.Name}, attacking"); - } + this.DebugSayFormatted($"I have detected {Mobile.FocusMob.Name}, attacking"); Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; diff --git a/Projects/UOContent/Mobiles/AI/BaseAI.cs b/Projects/UOContent/Mobiles/AI/BaseAI.cs index 8ddf00d32..cc8ee14c3 100644 --- a/Projects/UOContent/Mobiles/AI/BaseAI.cs +++ b/Projects/UOContent/Mobiles/AI/BaseAI.cs @@ -102,6 +102,7 @@ public abstract class BaseAI private long _nextDetectHidden; private long _nextStopGuard; + public long NextDebugMessage { get; set; } protected PathFollower _path; public Timer _timer; @@ -463,20 +464,14 @@ public abstract class BaseAI if (Mobile.Controlled && Mobile.Commandable) { - if (Mobile.Debug) - { - Mobile.DebugSay("Listening..."); - } + DebugSay("Listening..."); var isOwner = e.Mobile == Mobile.ControlMaster; var isFriend = !isOwner && Mobile.IsPetFriend(e.Mobile); if (e.Mobile.Alive && (isOwner || isFriend)) { - if (Mobile.Debug) - { - Mobile.DebugSay("It's from my master"); - } + DebugSay("It's from my master"); var keywords = e.Keywords; var speech = e.Speech; @@ -774,10 +769,7 @@ public abstract class BaseAI { if (e.Mobile.AccessLevel >= AccessLevel.GameMaster) { - if (Mobile.Debug) - { - Mobile.DebugSay("It's from a GM"); - } + DebugSay("It's from a GM"); if (Mobile.FindMyName(e.Speech, true)) { @@ -805,6 +797,15 @@ public abstract class BaseAI } } + public void DebugSay(string message, int cooldownMs = 5000) + { + if (Mobile.Debug && Core.TickCount >= NextDebugMessage) + { + Mobile.PublicOverheadMessage(MessageType.Regular, 41, false, message); + NextDebugMessage = Core.TickCount + cooldownMs; + } + } + public virtual bool Think() { if (Mobile.Deleted) @@ -923,10 +924,7 @@ public abstract class BaseAI { if (CheckHerding()) { - if (Mobile.Debug) - { - Mobile.DebugSay("Praise the shepherd!"); - } + DebugSay("Praise the shepherd!"); } else if (Mobile.CurrentWayPoint != null) { @@ -934,19 +932,13 @@ public abstract class BaseAI if ((point.X != Mobile.Location.X || point.Y != Mobile.Location.Y) && point.Map == Mobile.Map && point.Parent == null && !point.Deleted) { - if (Mobile.Debug) - { - Mobile.DebugSay("I will move towards my waypoint."); - } + DebugSay("I will move towards my waypoint."); DoMove(Mobile.GetDirectionTo(Mobile.CurrentWayPoint)); } else if (OnAtWayPoint()) { - if (Mobile.Debug) - { - Mobile.DebugSay("I will go to the next waypoint"); - } + DebugSay("I will go to the next waypoint"); Mobile.CurrentWayPoint = point.NextPoint; if (point.NextPoint?.Deleted == true) @@ -987,10 +979,7 @@ public abstract class BaseAI { if (Core.AOS && CheckHerding()) { - if (Mobile.Debug) - { - Mobile.DebugSay("Praise the shepherd!"); - } + DebugSay("Praise the shepherd!"); return true; } @@ -1000,10 +989,7 @@ public abstract class BaseAI if (combatant == null || combatant.Deleted || combatant.Map != Mobile.Map || !combatant.Alive || combatant.IsDeadBondedPet) { - if (Mobile.Debug) - { - Mobile.DebugSay("My combatant is gone!"); - } + DebugSay("My combatant is gone!"); Action = ActionType.Wander; return true; @@ -1012,10 +998,7 @@ public abstract class BaseAI Mobile.Direction = Mobile.GetDirectionTo(combatant); if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) { - if (Mobile.Debug) - { - Mobile.DebugSay($"I used my abilities on {combatant.Name}!"); - } + this.DebugSayFormatted($"I used my abilities on {combatant.Name}!"); } return true; @@ -1025,25 +1008,16 @@ public abstract class BaseAI { if (Core.AOS && CheckHerding()) { - if (Mobile.Debug) - { - Mobile.DebugSay("Praise the shepherd!"); - } + DebugSay("Praise the shepherd!"); } else if (Core.TickCount - _nextStopGuard < 0) { - if (Mobile.Debug) - { - Mobile.DebugSay("I am on guard"); - } + DebugSay("I am on guard"); // m_Mobile.Turn( Utility.Random(0, 2) - 1 ); } else { - if (Mobile.Debug) - { - Mobile.DebugSay("I stopped being on guard"); - } + DebugSay("I stopped being on guard"); Action = ActionType.Wander; } @@ -1057,10 +1031,7 @@ public abstract class BaseAI if (from?.Deleted != false || from.Map != Mobile.Map) { - if (Mobile.Debug) - { - Mobile.DebugSay("I have lost him"); - } + DebugSay("I have lost him"); Action = ActionType.Guard; return true; @@ -1068,19 +1039,13 @@ public abstract class BaseAI if (WalkMobileRange(from, 1, true, Mobile.RangePerception * 2, Mobile.RangePerception * 3)) { - if (Mobile.Debug) - { - Mobile.DebugSay("I have fled"); - } + DebugSay("I have fled"); Action = ActionType.Guard; return true; } - if (Mobile.Debug) - { - Mobile.DebugSay("I am fleeing!"); - } + DebugSay("I am fleeing!"); return true; } @@ -1244,10 +1209,7 @@ public abstract class BaseAI public virtual bool DoOrderNone() { - if (Mobile.Debug) - { - Mobile.DebugSay("I have no order"); - } + DebugSay("I have no order"); WalkRandomInHome(3, 2, 1); @@ -1276,20 +1238,14 @@ public abstract class BaseAI if (iCurrDist > Mobile.RangePerception) { - if (Mobile.Debug) - { - Mobile.DebugSay("I have lost my master. I stay here"); - } + DebugSay("I have lost my master. I stay here"); Mobile.ControlTarget = null; Mobile.ControlOrder = OrderType.None; } else { - if (Mobile.Debug) - { - Mobile.DebugSay("My master told me come"); - } + DebugSay("My master told me come"); // Not exactly OSI style, but better than nothing. var bRun = iCurrDist > 5; @@ -1319,10 +1275,7 @@ public abstract class BaseAI return true; } - if (Mobile.Debug) - { - Mobile.DebugSay("I drop my stuff for my master"); - } + DebugSay("I drop my stuff for my master"); var pack = Mobile.Backpack; @@ -1389,10 +1342,7 @@ public abstract class BaseAI { if (CheckHerding()) { - if (Mobile.Debug) - { - Mobile.DebugSay("Praise the shepherd!"); - } + DebugSay("Praise the shepherd!"); } else if (Mobile.ControlTarget?.Deleted == false && Mobile.ControlTarget != Mobile) { @@ -1400,10 +1350,7 @@ public abstract class BaseAI if (iCurrDist > Mobile.RangePerception) { - if (Mobile.Debug) - { - Mobile.DebugSay("I have lost the one to follow. I stay here"); - } + DebugSay("I have lost the one to follow. I stay here"); if (Mobile.Combatant?.Deleted == false && Mobile.Combatant.Alive && !Mobile.Combatant.IsDeadBondedPet) @@ -1418,10 +1365,7 @@ public abstract class BaseAI } else { - if (Mobile.Debug) - { - Mobile.DebugSay($"My master told me to follow: {Mobile.ControlTarget.Name}"); - } + this.DebugSayFormatted($"My master told me to follow: {Mobile.ControlTarget.Name}"); // Not exactly OSI style, but better than nothing. var bRun = iCurrDist > 5; @@ -1447,10 +1391,7 @@ public abstract class BaseAI } else { - if (Mobile.Debug) - { - Mobile.DebugSay("I have nobody to follow"); - } + DebugSay("I have nobody to follow"); Mobile.ControlTarget = null; Mobile.ControlOrder = OrderType.None; @@ -1600,9 +1541,9 @@ public abstract class BaseAI } } - if (combatant != null && Mobile.Debug) + if (combatant != null) { - Mobile.DebugSay("Crap, my master has been attacked! I will attack one of those bastards!"); + DebugSay("Crap, my master has been attacked! I will attack one of those bastards!"); } } @@ -1610,10 +1551,7 @@ public abstract class BaseAI combatant.Alive && !combatant.IsDeadBondedPet && Mobile.CanSee(combatant) && Mobile.CanBeHarmful(combatant, false) && combatant.Map == Mobile.Map) { - if (Mobile.Debug) - { - Mobile.DebugSay("Guarding from target..."); - } + DebugSay("Guarding from target..."); Mobile.Combatant = combatant; Mobile.FocusMob = combatant; @@ -1627,10 +1565,7 @@ public abstract class BaseAI } else { - if (Mobile.Debug) - { - Mobile.DebugSay("Nothing to guard from"); - } + DebugSay("Nothing to guard from"); Mobile.Warmode = false; if (Core.AOS) @@ -1654,12 +1589,7 @@ public abstract class BaseAI if (Mobile.ControlTarget?.Deleted != false || Mobile.ControlTarget.Map != Mobile.Map || !Mobile.ControlTarget.Alive || Mobile.ControlTarget.IsDeadBondedPet) { - if (Mobile.Debug) - { - 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." - ); - } + 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) { @@ -1703,10 +1633,7 @@ public abstract class BaseAI Mobile.ControlTarget = newCombatant; Mobile.ControlOrder = OrderType.Attack; Mobile.Combatant = newCombatant; - if (Mobile.Debug) - { - Mobile.DebugSay("But -that- is not dead. Here we go again..."); - } + DebugSay("But -that- is not dead. Here we go again..."); Think(); } @@ -1714,10 +1641,7 @@ public abstract class BaseAI } else { - if (Mobile.Debug) - { - Mobile.DebugSay("Attacking target..."); - } + DebugSay("Attacking target..."); Think(); } @@ -1727,20 +1651,14 @@ public abstract class BaseAI public virtual bool DoOrderPatrol() { - if (Mobile.Debug) - { - Mobile.DebugSay("This order is not yet coded"); - } + DebugSay("This order is not yet coded"); return true; } public virtual bool DoOrderRelease() { - if (Mobile.Debug) - { - Mobile.DebugSay("I have been released"); - } + DebugSay("I have been released"); Mobile.PlaySound(Mobile.GetAngerSound()); @@ -1776,17 +1694,7 @@ public abstract class BaseAI public virtual bool DoOrderStay() { - if (Mobile.Debug) - { - if (CheckHerding()) - { - Mobile.DebugSay("Praise the shepherd!"); - } - else - { - Mobile.DebugSay("My master told me to stay"); - } - } + DebugSay(CheckHerding() ? "Praise the shepherd!" : "My master told me to stay"); return true; } @@ -1798,10 +1706,7 @@ public abstract class BaseAI return true; } - if (Mobile.Debug) - { - Mobile.DebugSay("My master told me to stop."); - } + DebugSay("My master told me to stop."); Mobile.Direction = Mobile.GetDirectionTo(Mobile.ControlMaster); Mobile.Home = Mobile.Location; @@ -1832,10 +1737,7 @@ public abstract class BaseAI if (from?.Deleted == false && to?.Deleted == false && from != to && to.Player) { - if (Mobile.Debug) - { - Mobile.DebugSay($"Begin transfer with {to.Name}"); - } + this.DebugSayFormatted($"Begin transfer with {to.Name}"); var youngFrom = from is PlayerMobile mobile && mobile.Young; var youngTo = to is PlayerMobile playerMobile && playerMobile.Young; @@ -1911,20 +1813,14 @@ public abstract class BaseAI { if (Core.Now < Mobile.BardEndTime) { - if (Mobile.Debug) - { - Mobile.DebugSay("I am pacified, I wait"); - } + DebugSay("I am pacified, I wait"); Mobile.Combatant = null; Mobile.Warmode = false; } else { - if (Mobile.Debug) - { - Mobile.DebugSay("I'm not pacified any longer"); - } + DebugSay("I'm not pacified any longer"); Mobile.BardPacified = false; } @@ -1939,10 +1835,7 @@ public abstract class BaseAI Mobile.BardMaster.Map != Mobile.Map || Mobile.GetDistanceToSqrt(Mobile.BardMaster) > Mobile.RangePerception)) { - if (Mobile.Debug) - { - Mobile.DebugSay("I have lost my provoker"); - } + DebugSay("I have lost my provoker"); Mobile.BardProvoked = false; Mobile.BardMaster = null; @@ -1954,10 +1847,7 @@ public abstract class BaseAI else if (Mobile.BardTarget?.Deleted != false || Mobile.BardTarget.Map != Mobile.Map || Mobile.GetDistanceToSqrt(Mobile.BardTarget) > Mobile.RangePerception) { - if (Mobile.Debug) - { - Mobile.DebugSay("I have lost my provoke target"); - } + DebugSay("I have lost my provoke target"); Mobile.BardProvoked = false; Mobile.BardMaster = null; @@ -2172,10 +2062,7 @@ public abstract class BaseAI if (canOpenDoors || canDestroyObstacles) { - if (Mobile.Debug) - { - Mobile.DebugSay("My movement was blocked, I will try to clear some obstacles."); - } + DebugSay("My movement was blocked, I will try to clear some obstacles."); var map = Mobile.Map; @@ -2236,28 +2123,15 @@ public abstract class BaseAI if (item is BaseDoor door) { - if (Mobile.Debug) - { - Mobile.DebugSay( - "Little do they expect, I've learned how to open doors. Didn't they read the script??" - ); - } + DebugSay("Little do they expect, I've learned how to open doors. Didn't they read the script??"); - if (Mobile.Debug) - { - Mobile.DebugSay("*twist*"); - } + DebugSay("*twist*"); door.Use(Mobile); } else { - if (Mobile.Debug) - { - Mobile.DebugSay( - $"Ugabooga. I'm so big and tough I can destroy it: {item.GetType().Name}" - ); - } + this.DebugSayFormatted($"Ugabooga. I'm so big and tough I can destroy it: {item.GetType().Name}"); if (item is Container cont) { @@ -2410,10 +2284,7 @@ public abstract class BaseAI { if (_path != null) { - if (Mobile.Debug) - { - Mobile.DebugSay("Teleported; repathing"); - } + DebugSay("Teleported; repathing"); _path.ForceRepath(); } @@ -2578,10 +2449,7 @@ public abstract class BaseAI if (Mobile.ConstantFocus != null) { - if (Mobile.Debug) - { - Mobile.DebugSay("Acquired my constant focus"); - } + DebugSay("Acquired my constant focus"); Mobile.FocusMob = Mobile.ConstantFocus; return true; @@ -2608,10 +2476,7 @@ public abstract class BaseAI Mobile.NextReacquireTime = Core.TickCount + (int)Mobile.ReacquireDelay.TotalMilliseconds; - if (Mobile.Debug) - { - Mobile.DebugSay("Acquiring..."); - } + DebugSay("Acquiring..."); var map = Mobile.Map; @@ -2806,10 +2671,7 @@ public abstract class BaseAI return; } - if (Mobile.Debug) - { - Mobile.DebugSay("Checking for hidden players"); - } + DebugSay("Checking for hidden players"); var srcSkill = Mobile.Skills.DetectHidden.Value; @@ -2823,10 +2685,7 @@ public abstract class BaseAI if (trg != Mobile && trg.Player && trg.Alive && trg.Hidden && trg.AccessLevel == AccessLevel.Player && Mobile.InLOS(trg)) { - if (Mobile.Debug) - { - Mobile.DebugSay($"Trying to detect {trg.Name}"); - } + this.DebugSayFormatted($"Trying to detect {trg.Name}"); var trgHiding = trg.Skills.Hiding.Value / 2.9; var trgStealth = trg.Skills.Stealth.Value / 1.8; 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/DebugInterpolatedStringHandler.cs b/Projects/UOContent/Mobiles/AI/BaseAI/DebugInterpolatedStringHandler.cs new file mode 100644 index 000000000..c2d5dcf15 --- /dev/null +++ b/Projects/UOContent/Mobiles/AI/BaseAI/DebugInterpolatedStringHandler.cs @@ -0,0 +1,662 @@ +// 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 as BaseCreature)?.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/BerserkAI.cs b/Projects/UOContent/Mobiles/AI/BerserkAI.cs index 207d1f727..f882d6fb5 100644 --- a/Projects/UOContent/Mobiles/AI/BerserkAI.cs +++ b/Projects/UOContent/Mobiles/AI/BerserkAI.cs @@ -8,17 +8,11 @@ public class BerserkAI : BaseAI public override bool DoActionWander() { - if (Mobile.Debug) - { - Mobile.DebugSay("I have no combatant"); - } + DebugSay("I have no combatant"); if (AcquireFocusMob(Mobile.RangePerception, FightMode.Closest, false, true, true)) { - if (Mobile.Debug) - { - Mobile.DebugSay($"I have detected {Mobile.FocusMob.Name} and I will attack"); - } + this.DebugSayFormatted($"I have detected {Mobile.FocusMob.Name} and I will attack"); Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; @@ -38,10 +32,7 @@ public class BerserkAI : BaseAI if (combatant == null || combatant.Deleted || combatant.Map != Mobile.Map || !combatant.Alive || combatant.IsDeadBondedPet) { - if (Mobile.Debug) - { - 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; @@ -49,17 +40,11 @@ public class BerserkAI : BaseAI if (!WalkMobileRange(combatant, 1, true, Mobile.RangeFight, Mobile.RangeFight)) { - if (Mobile.Debug) - { - Mobile.DebugSay($"I am still not in range of {combatant.Name}"); - } + this.DebugSayFormatted($"I am still not in range of {combatant.Name}"); if ((int)Mobile.GetDistanceToSqrt(combatant) > Mobile.RangePerception + 1) { - if (Mobile.Debug) - { - Mobile.DebugSay($"I have lost {combatant.Name}"); - } + this.DebugSayFormatted($"I have lost {combatant.Name}"); Action = ActionType.Guard; return true; @@ -72,10 +57,7 @@ public class BerserkAI : BaseAI if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) { - if (Mobile.Debug) - { - Mobile.DebugSay($"I used my abilities on {combatant.Name}!"); - } + this.DebugSayFormatted($"I used my abilities on {combatant.Name}!"); } return true; @@ -85,10 +67,7 @@ public class BerserkAI : BaseAI { if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, true, true)) { - if (Mobile.Debug) - { - Mobile.DebugSay($"I have detected {Mobile.FocusMob.Name}, attacking"); - } + this.DebugSayFormatted($"I have detected {Mobile.FocusMob.Name}, attacking"); Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; diff --git a/Projects/UOContent/Mobiles/AI/HealerAI.cs b/Projects/UOContent/Mobiles/AI/HealerAI.cs index 6b4dfa2ed..e6189e033 100644 --- a/Projects/UOContent/Mobiles/AI/HealerAI.cs +++ b/Projects/UOContent/Mobiles/AI/HealerAI.cs @@ -57,10 +57,7 @@ public class HealerAI : BaseAI { if (NeedCure(toHelp)) { - if (Mobile.Debug) - { - Mobile.DebugSay($"{toHelp.Name} needs a cure"); - } + this.DebugSayFormatted($"{toHelp.Name} needs a cure"); if (!new CureSpell(Mobile).Cast()) { @@ -69,10 +66,7 @@ public class HealerAI : BaseAI } else if (NeedGHeal(toHelp)) { - if (Mobile.Debug) - { - Mobile.DebugSay($"{toHelp.Name} needs a greater heal"); - } + this.DebugSayFormatted($"{toHelp.Name} needs a greater heal"); if (!new GreaterHealSpell(Mobile).Cast()) { @@ -81,10 +75,7 @@ public class HealerAI : BaseAI } else if (NeedLHeal(toHelp)) { - if (Mobile.Debug) - { - Mobile.DebugSay($"{toHelp.Name} needs a lesser heal"); - } + this.DebugSayFormatted($"{toHelp.Name} needs a lesser heal"); new HealSpell(Mobile).Cast(); } @@ -103,10 +94,7 @@ public class HealerAI : BaseAI // TODO: Should it be able to do this? if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, Mobile.Combatant)) { - if (Mobile.Debug) - { - Mobile.DebugSay($"I used my abilities on {Mobile.Combatant.Name}!"); - } + this.DebugSayFormatted($"I used my abilities on {Mobile.Combatant.Name}!"); } return true; diff --git a/Projects/UOContent/Mobiles/AI/MageAI.cs b/Projects/UOContent/Mobiles/AI/MageAI.cs index d86aa67ad..f54326baf 100644 --- a/Projects/UOContent/Mobiles/AI/MageAI.cs +++ b/Projects/UOContent/Mobiles/AI/MageAI.cs @@ -83,10 +83,7 @@ public class MageAI : BaseAI { if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - if (Mobile.Debug) - { - Mobile.DebugSay($"I am going to attack {Mobile.FocusMob.Name}"); - } + this.DebugSayFormatted($"I am going to attack {Mobile.FocusMob.Name}"); Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; @@ -94,19 +91,13 @@ public class MageAI : BaseAI } else if (SmartAI && Mobile.Mana < Mobile.ManaMax && !Mobile.Meditating) { - if (Mobile.Debug) - { - Mobile.DebugSay("I am going to meditate"); - } + DebugSay("I am going to meditate"); Mobile.UseSkill(SkillName.Meditation); } else { - if (Mobile.Debug) - { - Mobile.DebugSay("I am wandering"); - } + DebugSay("I am wandering"); Mobile.Warmode = false; @@ -225,24 +216,18 @@ public class MageAI : BaseAI new TeleportSpell(Mobile).Cast(); - if (Mobile.Debug) - { - 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(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - if (Mobile.Debug) - { - Mobile.DebugSay($"My move is blocked, so I am going to attack {Mobile.FocusMob.Name}"); - } + this.DebugSayFormatted($"My move is blocked, so I am going to attack {Mobile.FocusMob.Name}"); Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; } - else if (Mobile.Debug) + else { - Mobile.DebugSay("I am stuck"); + DebugSay("I am stuck"); } } @@ -415,20 +400,14 @@ public class MageAI : BaseAI goto default; } - if (Mobile.Debug) - { - Mobile.DebugSay("Attempting to poison"); - } + DebugSay("Attempting to poison"); spell = new PoisonSpell(Mobile); break; } case 2: // Bless ourselves { - if (Mobile.Debug) - { - Mobile.DebugSay("Blessing myself"); - } + DebugSay("Blessing myself"); spell = new BlessSpell(Mobile); break; @@ -436,10 +415,7 @@ public class MageAI : BaseAI case 3: case 4: // Curse them { - if (Mobile.Debug) - { - Mobile.DebugSay("Attempting to curse"); - } + DebugSay("Attempting to curse"); spell = GetRandomCurseSpell(); break; @@ -451,20 +427,14 @@ public class MageAI : BaseAI goto default; } - if (Mobile.Debug) - { - Mobile.DebugSay("Attempting to paralyze"); - } + DebugSay("Attempting to paralyze"); spell = new ParalyzeSpell(Mobile); break; } case 6: // Drain mana { - if (Mobile.Debug) - { - Mobile.DebugSay("Attempting to drain mana"); - } + DebugSay("Attempting to drain mana"); spell = GetRandomManaDrainSpell(); break; @@ -476,20 +446,14 @@ public class MageAI : BaseAI goto default; } - if (Mobile.Debug) - { - Mobile.DebugSay("Attempting to invis myself"); - } + DebugSay("Attempting to invis myself"); spell = new InvisibilitySpell(Mobile); break; } default: // Damage them { - if (Mobile.Debug) - { - Mobile.DebugSay("Just doing damage"); - } + DebugSay("Just doing damage"); spell = GetRandomDamageSpell(); break; @@ -525,10 +489,7 @@ public class MageAI : BaseAI { if (c.Paralyzed && !c.Poisoned && !Mobile.Meditating) { - if (Mobile.Debug) - { - Mobile.DebugSay("I am going to meditate"); - } + DebugSay("I am going to meditate"); Mobile.UseSkill(SkillName.Meditation); } @@ -557,18 +518,7 @@ public class MageAI : BaseAI } } - if (Mobile.Debug) - { - if (spell != null) - { - Mobile.DebugSay($"Casting {spell.Name}"); - } - else - { - Mobile.DebugSay("I don't have a spell to use!"); - } - } - + DebugSay(spell != null ? $"Casting {spell.Name}" : "I don't have a spell to use!"); return spell; } @@ -677,21 +627,12 @@ public class MageAI : BaseAI { Mobile.Combatant = c = Mobile.FocusMob!; - if (Mobile.Debug) - { - 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}"); Mobile.FocusMob = null; } else { - if (Mobile.Debug) - { - 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; @@ -700,19 +641,13 @@ public class MageAI : BaseAI if (!Mobile.InLOS(c)) { - if (Mobile.Debug) - { - Mobile.DebugSay("I can't see my target"); - } + DebugSay("I can't see my target"); if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { Mobile.Combatant = c = Mobile.FocusMob!; - if (Mobile.Debug) - { - Mobile.DebugSay($"I will switch to {c.Name}"); - } + this.DebugSayFormatted($"I will switch to {c.Name}"); Mobile.FocusMob = null; } @@ -742,10 +677,7 @@ public class MageAI : BaseAI if (c == null) { - if (Mobile.Debug) - { - 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; @@ -760,10 +692,7 @@ public class MageAI : BaseAI if (Utility.Random(0, 100) > fleeChance) { - if (Mobile.Debug) - { - 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; @@ -772,10 +701,7 @@ public class MageAI : BaseAI if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, c)) { - if (Mobile.Debug) - { - Mobile.DebugSay("I used my abilities!"); - } + DebugSay("I used my abilities!"); } else if (Mobile.Spell == null && Core.TickCount - _nextCastTime >= 0) { @@ -785,19 +711,13 @@ public class MageAI : BaseAI if (Mobile.Poisoned) // Top cast priority is cure { - if (Mobile.Debug) - { - Mobile.DebugSay("I am going to cure myself"); - } + DebugSay("I am going to cure myself"); spell = new CureSpell(Mobile); } else if (toDispel != null) // Something dispellable is attacking us { - if (Mobile.Debug) - { - 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 } @@ -869,10 +789,7 @@ public class MageAI : BaseAI } else if (Mobile.Spell == null && Core.TickCount - _nextCastTime >= 0) { - if (Mobile.Debug) - { - Mobile.DebugSay("I am going to reveal my last target"); - } + DebugSay("I am going to reveal my last target"); _revealTarget = new LandTarget(_lastTargetLoc, map); Spell spell = new RevealSpell(Mobile); @@ -888,10 +805,7 @@ public class MageAI : BaseAI if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - if (Mobile.Debug) - { - Mobile.DebugSay($"I am going to attack {Mobile.FocusMob.Name}"); - } + this.DebugSayFormatted($"I am going to attack {Mobile.FocusMob.Name}"); Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; @@ -919,19 +833,13 @@ public class MageAI : BaseAI if ((Mobile.Mana > 20 || Mobile.Mana == Mobile.ManaMax) && Mobile.Hits > Mobile.HitsMax / 2) { - if (Mobile.Debug) - { - 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(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - if (Mobile.Debug) - { - Mobile.DebugSay($"I am scared of {Mobile.FocusMob.Name}"); - } + this.DebugSayFormatted($"I am scared of {Mobile.FocusMob.Name}"); RunFrom(Mobile.FocusMob); Mobile.FocusMob = null; @@ -943,10 +851,7 @@ public class MageAI : BaseAI } else { - if (Mobile.Debug) - { - Mobile.DebugSay("Area seems clear, but my guard is up"); - } + DebugSay("Area seems clear, but my guard is up"); Action = ActionType.Guard; Mobile.Warmode = true; diff --git a/Projects/UOContent/Mobiles/AI/MeleeAI.cs b/Projects/UOContent/Mobiles/AI/MeleeAI.cs index 075b4f06c..adc8d566b 100644 --- a/Projects/UOContent/Mobiles/AI/MeleeAI.cs +++ b/Projects/UOContent/Mobiles/AI/MeleeAI.cs @@ -12,20 +12,14 @@ public class MeleeAI : BaseAI { if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - if (Mobile.Debug) - { - Mobile.DebugSay($"I have detected {Mobile.FocusMob.Name}, attacking"); - } + this.DebugSayFormatted($"I have detected {Mobile.FocusMob.Name}, attacking"); Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; } else { - if (Mobile.Debug) - { - Mobile.DebugSay("I am wandering"); - } + DebugSay("I am wandering"); Mobile.Warmode = false; @@ -42,10 +36,7 @@ public class MeleeAI : BaseAI if (combatant == null || combatant.Deleted || combatant.Map != Mobile.Map || !combatant.Alive || combatant.IsDeadBondedPet) { - if (Mobile.Debug) - { - 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; @@ -69,10 +60,7 @@ public class MeleeAI : BaseAI if (combatant == null) { - if (Mobile.Debug) - { - 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; @@ -88,10 +76,7 @@ public class MeleeAI : BaseAI if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - if (Mobile.Debug) - { - Mobile.DebugSay($"My move is blocked, so I am going to attack {Mobile.FocusMob!.Name}"); - } + this.DebugSayFormatted($"My move is blocked, so I am going to attack {Mobile.FocusMob!.Name}"); Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; @@ -100,19 +85,13 @@ public class MeleeAI : BaseAI if (Mobile.GetDistanceToSqrt(combatant) > Mobile.RangePerception + 1) { - if (Mobile.Debug) - { - 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 (Mobile.Debug) - { - 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 - Mobile.LastMoveTime > 400) { @@ -128,10 +107,7 @@ public class MeleeAI : BaseAI var fleeChance = 10 + Math.Max(0, combatant.Hits - Mobile.Hits); // (10 + diff)% chance to flee; if (Utility.Random(0, 100) < fleeChance) { - if (Mobile.Debug) - { - 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; @@ -141,10 +117,7 @@ public class MeleeAI : BaseAI if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) { - if (Mobile.Debug) - { - Mobile.DebugSay("I used my abilities!"); - } + DebugSay("I used my abilities!"); } return true; @@ -154,10 +127,7 @@ public class MeleeAI : BaseAI { if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - if (Mobile.Debug) - { - Mobile.DebugSay($"I have detected {Mobile.FocusMob.Name}, attacking"); - } + this.DebugSayFormatted($"I have detected {Mobile.FocusMob.Name}, attacking"); Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; @@ -174,10 +144,7 @@ public class MeleeAI : BaseAI { if (Mobile.Hits > Mobile.HitsMax / 2) { - if (Mobile.Debug) - { - Mobile.DebugSay("I am stronger now, so I will continue fighting"); - } + DebugSay("I am stronger now, so I will continue fighting"); Mobile.PlaySound(Mobile.GetAttackSound()); Mobile.CurrentSpeed = Mobile.ActiveSpeed; diff --git a/Projects/UOContent/Mobiles/AI/PredatorAI.cs b/Projects/UOContent/Mobiles/AI/PredatorAI.cs index 31a683ca5..fe5b06a6f 100644 --- a/Projects/UOContent/Mobiles/AI/PredatorAI.cs +++ b/Projects/UOContent/Mobiles/AI/PredatorAI.cs @@ -10,19 +10,13 @@ public class PredatorAI : BaseAI { if (Mobile.Combatant != null) { - if (Mobile.Debug) - { - 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(Mobile.RangePerception, Mobile.FightMode, true, false, true)) { - if (Mobile.Debug) - { - Mobile.DebugSay("There is something near, I go away"); - } + DebugSay("There is something near, I go away"); Action = ActionType.Backoff; } @@ -41,10 +35,7 @@ public class PredatorAI : BaseAI if (combatant == null || combatant.Deleted || combatant.Map != Mobile.Map || !combatant.Alive || combatant.IsDeadBondedPet) { - if (Mobile.Debug) - { - 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; @@ -54,27 +45,18 @@ public class PredatorAI : BaseAI { if (Mobile.GetDistanceToSqrt(combatant) > Mobile.RangePerception + 1) { - if (Mobile.Debug) - { - Mobile.DebugSay($"I cannot find {combatant.Name}"); - } + this.DebugSayFormatted($"I cannot find {combatant.Name}"); Action = ActionType.Wander; return true; } - if (Mobile.Debug) - { - Mobile.DebugSay($"I should be closer to {combatant.Name}"); - } + this.DebugSayFormatted($"I should be closer to {combatant.Name}"); } if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) { - if (Mobile.Debug) - { - Mobile.DebugSay($"I used my abilities on {combatant.Name}!"); - } + this.DebugSayFormatted($"I used my abilities on {combatant.Name}!"); } return true; @@ -90,20 +72,14 @@ public class PredatorAI : BaseAI { if (WalkMobileRange(Mobile.FocusMob, 1, false, Mobile.RangePerception, Mobile.RangePerception * 2)) { - if (Mobile.Debug) - { - Mobile.DebugSay("Well, here I am safe"); - } + DebugSay("Well, here I am safe"); Action = ActionType.Wander; } } else { - if (Mobile.Debug) - { - 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 30adec3cf..ff707f3df 100644 --- a/Projects/UOContent/Mobiles/AI/ThiefAI.cs +++ b/Projects/UOContent/Mobiles/AI/ThiefAI.cs @@ -13,17 +13,11 @@ public class ThiefAI : BaseAI public override bool DoActionWander() { - if (Mobile.Debug) - { - Mobile.DebugSay("I have no combatant"); - } + DebugSay("I have no combatant"); if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - if (Mobile.Debug) - { - Mobile.DebugSay($"I have detected {Mobile.FocusMob.Name}, attacking"); - } + this.DebugSayFormatted($"I have detected {Mobile.FocusMob.Name}, attacking"); Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; @@ -43,10 +37,7 @@ public class ThiefAI : BaseAI if (combatant == null || combatant.Deleted || combatant.Map != Mobile.Map || !combatant.Alive || combatant.IsDeadBondedPet) { - if (Mobile.Debug) - { - 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; @@ -54,10 +45,7 @@ public class ThiefAI : BaseAI if (!WalkMobileRange(combatant, 1, true, Mobile.RangeFight, Mobile.RangeFight)) { - if (Mobile.Debug) - { - Mobile.DebugSay($"I should be closer to {combatant.Name}"); - } + this.DebugSayFormatted($"I should be closer to {combatant.Name}"); } else { @@ -76,20 +64,14 @@ public class ThiefAI : BaseAI Core.TickCount - Mobile.NextSkillTime >= 0 && _toDisarm.LootType != LootType.Blessed && _toDisarm.LootType != LootType.Newbied) { - if (Mobile.Debug) - { - Mobile.DebugSay("Trying to steal from combatant."); - } + DebugSay("Trying to steal from combatant."); Mobile.UseSkill(SkillName.Stealing); Mobile.Target?.Invoke(Mobile, _toDisarm); } else if (_toDisarm == null && Core.TickCount - Mobile.NextSkillTime >= 0) { - if (Mobile.Debug) - { - 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 (Mobile.Debug) - { - 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; @@ -117,10 +96,7 @@ public class ThiefAI : BaseAI if (Utility.Random(0, 100) > fleeChance) { - if (Mobile.Debug) - { - Mobile.DebugSay($"I am going to flee from {combatant.Name}"); - } + this.DebugSayFormatted($"I am going to flee from {combatant.Name}"); Action = ActionType.Flee; } @@ -130,10 +106,7 @@ public class ThiefAI : BaseAI if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, Mobile.Combatant)) { - if (Mobile.Debug) - { - Mobile.DebugSay($"I used my abilities on {Mobile.Combatant.Name}!"); - } + this.DebugSayFormatted($"I used my abilities on {Mobile.Combatant.Name}!"); } return true; } @@ -155,10 +128,7 @@ public class ThiefAI : BaseAI { if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { - if (Mobile.Debug) - { - Mobile.DebugSay($"I have detected {Mobile.FocusMob.Name}, attacking"); - } + this.DebugSayFormatted($"I have detected {Mobile.FocusMob.Name}, attacking"); Mobile.Combatant = Mobile.FocusMob; Action = ActionType.Combat; @@ -175,10 +145,7 @@ public class ThiefAI : BaseAI { if (Mobile.Hits > Mobile.HitsMax / 2) { - if (Mobile.Debug) - { - Mobile.DebugSay("I am stronger now, so I will continue fighting"); - } + DebugSay("I am stronger now, so I will continue fighting"); Action = ActionType.Combat; } diff --git a/Projects/UOContent/Mobiles/AI/VendorAI.cs b/Projects/UOContent/Mobiles/AI/VendorAI.cs index 7b2e7950a..30f63dd29 100644 --- a/Projects/UOContent/Mobiles/AI/VendorAI.cs +++ b/Projects/UOContent/Mobiles/AI/VendorAI.cs @@ -15,17 +15,11 @@ public class VendorAI : BaseAI public override bool DoActionWander() { - if (Mobile.Debug) - { - Mobile.DebugSay("I'm fine"); - } + DebugSay("I'm fine"); if (Mobile.Combatant != null) { - if (Mobile.Debug) - { - Mobile.DebugSay($"{Mobile.Combatant.Name} is attacking me"); - } + this.DebugSayFormatted($"{Mobile.Combatant.Name} is attacking me"); Mobile.Say(GetRandomGuardMessage()); Action = ActionType.Flee; @@ -34,10 +28,7 @@ public class VendorAI : BaseAI { if (Mobile.FocusMob != null) { - if (Mobile.Debug) - { - Mobile.DebugSay($"{Mobile.FocusMob.Name} has talked to me"); - } + this.DebugSayFormatted($"{Mobile.FocusMob.Name} has talked to me"); Action = ActionType.Interact; } @@ -58,10 +49,7 @@ public class VendorAI : BaseAI if (Mobile.Combatant != null) { - if (Mobile.Debug) - { - Mobile.DebugSay($"{Mobile.Combatant.Name} is attacking me"); - } + this.DebugSayFormatted($"{Mobile.Combatant.Name} is attacking me"); Mobile.Say(GetRandomGuardMessage()); @@ -72,10 +60,7 @@ public class VendorAI : BaseAI if (customer?.Deleted != false || customer.Map != Mobile.Map) { - if (Mobile.Debug) - { - Mobile.DebugSay("My customer have disapeared"); - } + DebugSay("My customer have disapeared"); Mobile.FocusMob = null; @@ -83,19 +68,13 @@ public class VendorAI : BaseAI } else if (customer.InRange(Mobile, Mobile.RangeFight)) { - if (Mobile.Debug) - { - Mobile.DebugSay($"I am with {customer.Name}"); - } + this.DebugSayFormatted($"I am with {customer.Name}"); Mobile.Direction = Mobile.GetDirectionTo(customer); } else { - if (Mobile.Debug) - { - Mobile.DebugSay($"{customer.Name} is gone"); - } + this.DebugSayFormatted($"{customer.Name} is gone"); Mobile.FocusMob = null; Action = ActionType.Wander; diff --git a/Projects/UOContent/Mobiles/BaseCreature.cs b/Projects/UOContent/Mobiles/BaseCreature.cs index d15bf8ef8..ecfbba73f 100644 --- a/Projects/UOContent/Mobiles/BaseCreature.cs +++ b/Projects/UOContent/Mobiles/BaseCreature.cs @@ -2414,13 +2414,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 @@ -2498,7 +2491,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; } From aab731ed96370d0dc4f8da88fcb4a5b166994064 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Fri, 18 Jul 2025 22:39:57 -0700 Subject: [PATCH 07/47] feat: Overhauls AI (Speech/Movement) (#2232) ### Summary * Refactors AI so it is easier to read and maintain * Fixes NPC speed issues * Fixes pet sector AI issue that was causing stuttering * Fixes direction snapping for Melee/Mage AI * Refactors pet orders * Refactors speech commands * Removes scale speed by dex for HS+ (it was a stupid feature anyways) --- .../Factions/Mobiles/Guards/GuardAI.cs | 2 +- .../Mobiles/AI/AIControlMobileTarget.cs | 4 +- Projects/UOContent/Mobiles/AI/BaseAI.cs | 3167 ----------------- .../Mobiles/AI/BaseAI/AIGroupMovement.cs | 215 ++ .../UOContent/Mobiles/AI/BaseAI/AIMovement.cs | 429 +++ .../UOContent/Mobiles/AI/BaseAI/AITimer.cs | 134 + .../UOContent/Mobiles/AI/BaseAI/AIType.cs | 30 + .../UOContent/Mobiles/AI/BaseAI/ActionType.cs | 26 + .../UOContent/Mobiles/AI/BaseAI/BaseAI.cs | 918 +++++ .../Mobiles/AI/BaseAI/ContextMenu.cs | 75 + .../BaseAI/DebugInterpolatedStringHandler.cs | 3 +- .../Mobiles/AI/BaseAI/InternalEntry.cs | 119 + .../UOContent/Mobiles/AI/BaseAI/OnSpeech.cs | 457 +++ .../Mobiles/AI/BaseAI/PetOrderHandlers.cs | 241 ++ .../UOContent/Mobiles/AI/BaseAI/PetOrders.cs | 518 +++ .../Mobiles/AI/BaseAI/SpeechEventArgsExt.cs | 35 + .../Mobiles/AI/BaseAI/TransferItem.cs | 190 + .../Mobiles/AI/BaseAI/WalkRandomLogic.cs | 125 + Projects/UOContent/Mobiles/AI/HealerAI.cs | 28 +- Projects/UOContent/Mobiles/AI/MageAI.cs | 28 +- Projects/UOContent/Mobiles/AI/MeleeAI.cs | 27 +- .../UOContent/Mobiles/AI/OppositionGroup.cs | 236 +- Projects/UOContent/Mobiles/AI/VendorAI.cs | 21 +- Projects/UOContent/Mobiles/BaseCreature.cs | 62 +- Projects/UOContent/Mobiles/NPCSpeeds.cs | 25 - version.json | 2 +- 26 files changed, 3688 insertions(+), 3429 deletions(-) delete mode 100644 Projects/UOContent/Mobiles/AI/BaseAI.cs create mode 100644 Projects/UOContent/Mobiles/AI/BaseAI/AIGroupMovement.cs create mode 100644 Projects/UOContent/Mobiles/AI/BaseAI/AIMovement.cs create mode 100644 Projects/UOContent/Mobiles/AI/BaseAI/AITimer.cs create mode 100644 Projects/UOContent/Mobiles/AI/BaseAI/AIType.cs create mode 100644 Projects/UOContent/Mobiles/AI/BaseAI/ActionType.cs create mode 100644 Projects/UOContent/Mobiles/AI/BaseAI/BaseAI.cs create mode 100644 Projects/UOContent/Mobiles/AI/BaseAI/ContextMenu.cs create mode 100644 Projects/UOContent/Mobiles/AI/BaseAI/InternalEntry.cs create mode 100644 Projects/UOContent/Mobiles/AI/BaseAI/OnSpeech.cs create mode 100644 Projects/UOContent/Mobiles/AI/BaseAI/PetOrderHandlers.cs create mode 100644 Projects/UOContent/Mobiles/AI/BaseAI/PetOrders.cs create mode 100644 Projects/UOContent/Mobiles/AI/BaseAI/SpeechEventArgsExt.cs create mode 100644 Projects/UOContent/Mobiles/AI/BaseAI/TransferItem.cs create mode 100644 Projects/UOContent/Mobiles/AI/BaseAI/WalkRandomLogic.cs diff --git a/Projects/UOContent/Engines/Factions/Mobiles/Guards/GuardAI.cs b/Projects/UOContent/Engines/Factions/Mobiles/Guards/GuardAI.cs index c89dd6e98..91bde7a24 100644 --- a/Projects/UOContent/Engines/Factions/Mobiles/Guards/GuardAI.cs +++ b/Projects/UOContent/Engines/Factions/Mobiles/Guards/GuardAI.cs @@ -393,7 +393,7 @@ 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(Mobile.RangePerception, Mobile.FightMode, false, false, true)) diff --git a/Projects/UOContent/Mobiles/AI/AIControlMobileTarget.cs b/Projects/UOContent/Mobiles/AI/AIControlMobileTarget.cs index 6e12837e4..f14b46792 100644 --- a/Projects/UOContent/Mobiles/AI/AIControlMobileTarget.cs +++ b/Projects/UOContent/Mobiles/AI/AIControlMobileTarget.cs @@ -14,10 +14,8 @@ public class AIControlMobileTarget : Target order == OrderType.Attack ? TargetFlags.Harmful : TargetFlags.None ) { - _list = new List(); + _list = [ai]; Order = order; - - AddAI(ai); } public OrderType Order { get; } diff --git a/Projects/UOContent/Mobiles/AI/BaseAI.cs b/Projects/UOContent/Mobiles/AI/BaseAI.cs deleted file mode 100644 index cc8ee14c3..000000000 --- a/Projects/UOContent/Mobiles/AI/BaseAI.cs +++ /dev/null @@ -1,3167 +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[] _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 _action; - - public BaseCreature Mobile { get; } - - private long _nextDetectHidden; - private long _nextStopGuard; - public long NextDebugMessage { get; set; } - - protected PathFollower _path; - public Timer _timer; - - public BaseAI(BaseCreature m) - { - Mobile = 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) - { - _timer.Start(); - } - - Action = ActionType.Wander; - } - - public ActionType Action - { - get => _action; - set - { - _action = value; - OnActionChanged(); - } - } - - public long NextMove { get; set; } - - public virtual bool CanDetectHidden => Mobile.Skills.DetectHidden.Value > 0; - - public virtual bool WasNamed(string speech) - { - var name = Mobile.Name; - - return name != null && speech.InsensitiveStartsWith(name); - } - - public virtual void GetContextMenuEntries(Mobile from, ref PooledRefList list) - { - if (!from.Alive || !Mobile.Controlled || !from.InRange(Mobile, 14)) - { - return; - } - - var isDeadPet = Mobile.IsDeadPet; - - if (from == 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 (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 (!Mobile.Summoned && 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 (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 (Mobile.Deleted || !Mobile.Controlled || !from.InRange(Mobile, 14) || from.Map != Mobile.Map) - { - return; - } - - var isOwner = from == Mobile.ControlMaster; - var isFriend = !isOwner && 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 = Mobile.Combatant; - - if (currentCombat != null && !aggressor.Hidden && currentCombat != aggressor && - Mobile.GetDistanceToSqrt(currentCombat) > Mobile.GetDistanceToSqrt(aggressor)) - { - Mobile.Combatant = aggressor; - } - } - - public virtual void EndPickTarget(Mobile from, Mobile target, OrderType order) - { - if (Mobile.Deleted || !Mobile.Controlled || !from.InRange(Mobile, 14) || from.Map != Mobile.Map || - !from.CheckAlive()) - { - return; - } - - var isOwner = from == Mobile.ControlMaster; - var isFriend = !isOwner && 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 && Mobile.IsScaredOfScaryThings) - { - 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) - { - Mobile.SayTo(from, "Your pet refuses to attack the guard."); - return; - } - } - - if (Mobile.CheckControlChance(from)) - { - Mobile.ControlTarget = target; - Mobile.ControlOrder = order; - } - } - - 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 (e.Mobile.Alive && e.Mobile.InRange(Mobile.Location, 3) && Mobile.IsHumanInTown()) - { - if (e.HasKeyword(0x9D) && WasNamed(e.Speech)) // *move* - { - if (Mobile.Combatant != null) - { - // I am too busy fighting to deal with thee! - Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501482); - } - else - { - // Excuse me? - Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501516); - WalkRandomInHome(2, 2, 1); - } - } - else if (e.HasKeyword(0x9E) && WasNamed(e.Speech)) // *time* - { - if (Mobile.Combatant != null) - { - // I am too busy fighting to deal with thee! - Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501482); - } - else - { - Clock.GetTime(Mobile, out var generalNumber, out _); - - Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, generalNumber); - } - } - else if (e.HasKeyword(0x6C) && WasNamed(e.Speech)) // *train - { - if (Mobile.Combatant != null) - { - // I am too busy fighting to deal with thee! - Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501482); - } - else - { - var foundSomething = false; - - var ourSkills = 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 && 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) - { - Mobile.Say(1043058); // I can train the following: - } - - Mobile.Say(number); - - foundSomething = true; - } - } - } - - if (!foundSomething) - { - 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 < _keywordTable.Length) - { - toTrain = _keywordTable[index]; - } - } - } - - if (toTrain != (SkillName)(-1) && WasNamed(e.Speech)) - { - if (Mobile.Combatant != null) - { - // I am too busy fighting to deal with thee! - Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 501482); - } - else - { - var skills = Mobile.Skills; - var skill = skills[toTrain]; - - if (skill == null || skill.Base < 60.0 || !Mobile.CheckTeach(toTrain, e.Mobile)) - { - Mobile.Say(501507); // 'Tis not something I can teach thee of. - } - else - { - Mobile.Teach(toTrain, e.Mobile, 0, false); - } - } - } - } - } - - if (Mobile.Controlled && Mobile.Commandable) - { - DebugSay("Listening..."); - - var isOwner = e.Mobile == Mobile.ControlMaster; - var isFriend = !isOwner && Mobile.IsPetFriend(e.Mobile); - - if (e.Mobile.Alive && (isOwner || isFriend)) - { - 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 (Mobile.CheckControlChance(e.Mobile)) - { - Mobile.ControlTarget = null; - 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 (Mobile.CheckControlChance(e.Mobile)) - { - Mobile.ControlTarget = null; - Mobile.ControlOrder = OrderType.Guard; - } - - return; - } - case 0x167: // all stop - { - if (Mobile.CheckControlChance(e.Mobile)) - { - Mobile.ControlTarget = null; - 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 (Mobile.CheckControlChance(e.Mobile)) - { - Mobile.ControlTarget = e.Mobile; - Mobile.ControlOrder = OrderType.Follow; - } - - return; - } - case 0x170: // all stay - { - if (Mobile.CheckControlChance(e.Mobile)) - { - Mobile.ControlTarget = null; - 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) && Mobile.CheckControlChance(e.Mobile)) - { - Mobile.ControlTarget = null; - Mobile.ControlOrder = OrderType.Come; - } - - return; - } - case 0x156: // *drop - { - if (!isOwner) - { - break; - } - - if (!Mobile.IsDeadPet && !Mobile.Summoned && WasNamed(speech) && - Mobile.CheckControlChance(e.Mobile)) - { - Mobile.ControlTarget = null; - Mobile.ControlOrder = OrderType.Drop; - } - - return; - } - case 0x15A: // *follow - { - if (WasNamed(speech) && Mobile.CheckControlChance(e.Mobile)) - { - BeginPickTarget(e.Mobile, OrderType.Follow); - } - - return; - } - case 0x15B: // *friend - { - if (!isOwner) - { - break; - } - - if (WasNamed(speech) && Mobile.CheckControlChance(e.Mobile)) - { - if (Mobile.Summoned || 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 (!Mobile.IsDeadPet && WasNamed(speech) && Mobile.CheckControlChance(e.Mobile)) - { - Mobile.ControlTarget = null; - Mobile.ControlOrder = OrderType.Guard; - } - - return; - } - case 0x15D: // *kill - case 0x15E: // *attack - { - if (!isOwner) - { - break; - } - - if (!Mobile.IsDeadPet && WasNamed(speech) && Mobile.CheckControlChance(e.Mobile)) - { - BeginPickTarget(e.Mobile, OrderType.Attack); - } - - return; - } - case 0x15F: // *patrol - { - if (!isOwner) - { - break; - } - - if (WasNamed(speech) && Mobile.CheckControlChance(e.Mobile)) - { - Mobile.ControlTarget = null; - Mobile.ControlOrder = OrderType.Patrol; - } - - return; - } - case 0x161: // *stop - { - if (WasNamed(speech) && Mobile.CheckControlChance(e.Mobile)) - { - Mobile.ControlTarget = null; - Mobile.ControlOrder = OrderType.Stop; - } - - return; - } - case 0x163: // *follow me - { - if (WasNamed(speech) && Mobile.CheckControlChance(e.Mobile)) - { - Mobile.ControlTarget = e.Mobile; - Mobile.ControlOrder = OrderType.Follow; - } - - return; - } - case 0x16D: // *release - { - if (!isOwner) - { - break; - } - - if (WasNamed(speech) && Mobile.CheckControlChance(e.Mobile)) - { - if (!Mobile.Summoned) - { - e.Mobile.SendGump(new ConfirmReleaseGump(e.Mobile, Mobile)); - } - else - { - Mobile.ControlTarget = null; - Mobile.ControlOrder = OrderType.Release; - } - } - - return; - } - case 0x16E: // *transfer - { - if (!isOwner) - { - break; - } - - if (!Mobile.IsDeadPet && WasNamed(speech) && Mobile.CheckControlChance(e.Mobile)) - { - if (Mobile.Summoned || 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) && Mobile.CheckControlChance(e.Mobile)) - { - Mobile.ControlTarget = null; - Mobile.ControlOrder = OrderType.Stay; - } - - return; - } - } - } - } - } - else - { - if (e.Mobile.AccessLevel >= AccessLevel.GameMaster) - { - DebugSay("It's from a GM"); - - if (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")) - { - Mobile.SetControlMaster(e.Mobile); - - if (Mobile.Summoned) - { - Mobile.SummonMaster = e.Mobile; - } - - return; - } - } - } - } - } - } - - public void DebugSay(string message, int cooldownMs = 5000) - { - if (Mobile.Debug && Core.TickCount >= NextDebugMessage) - { - Mobile.PublicOverheadMessage(MessageType.Regular, 41, false, message); - NextDebugMessage = Core.TickCount + cooldownMs; - } - } - - public virtual bool Think() - { - if (Mobile.Deleted) - { - 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(); - } - - default: - { - return false; - } - } - } - - public virtual void OnActionChanged() - { - switch (Action) - { - case ActionType.Wander: - { - Mobile.Warmode = false; - Mobile.Combatant = null; - Mobile.FocusMob = null; - Mobile.SetCurrentSpeedToPassive(); - break; - } - - case ActionType.Combat: - { - Mobile.Warmode = true; - Mobile.FocusMob = null; - Mobile.SetCurrentSpeedToActive(); - break; - } - - case ActionType.Guard: - { - Mobile.Warmode = true; - Mobile.FocusMob = null; - Mobile.Combatant = null; - _nextStopGuard = Core.TickCount + (int)TimeSpan.FromSeconds(10).TotalMilliseconds; - Mobile.SetCurrentSpeedToActive(); - break; - } - - case ActionType.Flee: - { - Mobile.Warmode = true; - Mobile.FocusMob = null; - Mobile.SetCurrentSpeedToActive(); - break; - } - - case ActionType.Interact: - { - Mobile.Warmode = false; - Mobile.SetCurrentSpeedToPassive(); - break; - } - - case ActionType.Backoff: - { - Mobile.Warmode = false; - Mobile.SetCurrentSpeedToPassive(); - break; - } - } - } - - public virtual bool OnAtWayPoint() => true; - - public virtual bool DoActionWander() - { - if (CheckHerding()) - { - DebugSay("Praise the shepherd!"); - } - else if (Mobile.CurrentWayPoint != null) - { - var point = Mobile.CurrentWayPoint; - if ((point.X != Mobile.Location.X || point.Y != Mobile.Location.Y) && point.Map == Mobile.Map && - point.Parent == null && !point.Deleted) - { - DebugSay("I will move towards my waypoint."); - - DoMove(Mobile.GetDirectionTo(Mobile.CurrentWayPoint)); - } - else if (OnAtWayPoint()) - { - DebugSay("I will go to the next waypoint"); - - Mobile.CurrentWayPoint = point.NextPoint; - if (point.NextPoint?.Deleted == true) - { - Mobile.CurrentWayPoint = point.NextPoint = point.NextPoint.NextPoint; - } - } - } - else if (Mobile.IsAnimatedDead) - { - // animated dead follow their master - var master = Mobile.SummonMaster; - - if (master != null && master.Map == Mobile.Map && master.InRange(Mobile, Mobile.RangePerception)) - { - MoveTo(master, false, 1); - } - else - { - WalkRandomInHome(2, 2, 1); - } - } - else if (CheckMove() && CanMoveNow(out _) && !Mobile.CheckIdle()) - { - WalkRandomInHome(2, 2, 1); - } - - if (Mobile.Combatant?.Deleted == false && Mobile.Combatant.Alive && - !Mobile.Combatant.IsDeadBondedPet) - { - Mobile.Direction = Mobile.GetDirectionTo(Mobile.Combatant); - } - - return true; - } - - public virtual bool DoActionCombat() - { - if (Core.AOS && CheckHerding()) - { - DebugSay("Praise the shepherd!"); - - return true; - } - - var combatant = Mobile.Combatant; - - if (combatant == null || combatant.Deleted || combatant.Map != Mobile.Map || !combatant.Alive || - combatant.IsDeadBondedPet) - { - DebugSay("My combatant is gone!"); - - Action = ActionType.Wander; - return true; - } - - Mobile.Direction = Mobile.GetDirectionTo(combatant); - if (Mobile.TriggerAbility(MonsterAbilityTrigger.CombatAction, combatant)) - { - this.DebugSayFormatted($"I used my abilities on {combatant.Name}!"); - } - - return true; - } - - public virtual bool DoActionGuard() - { - if (Core.AOS && CheckHerding()) - { - DebugSay("Praise the shepherd!"); - } - else if (Core.TickCount - _nextStopGuard < 0) - { - DebugSay("I am on guard"); - // m_Mobile.Turn( Utility.Random(0, 2) - 1 ); - } - else - { - DebugSay("I stopped being on guard"); - - Action = ActionType.Wander; - } - - return true; - } - - public virtual bool DoActionFlee() - { - var from = Mobile.FocusMob; - - if (from?.Deleted != false || from.Map != Mobile.Map) - { - DebugSay("I have lost him"); - - Action = ActionType.Guard; - return true; - } - - if (WalkMobileRange(from, 1, true, Mobile.RangePerception * 2, Mobile.RangePerception * 3)) - { - DebugSay("I have fled"); - - Action = ActionType.Guard; - return true; - } - - DebugSay("I am fleeing!"); - - return true; - } - - public virtual bool DoActionInteract() => true; - - public virtual bool DoActionBackoff() => true; - - 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.Patrol => DoOrderPatrol(), - OrderType.Release => DoOrderRelease(), - OrderType.Stay => DoOrderStay(), - OrderType.Stop => DoOrderStop(), - OrderType.Follow => DoOrderFollow(), - OrderType.Transfer => DoOrderTransfer(), - _ => false - }; - - public virtual void OnCurrentOrderChanged() - { - if (Mobile.Deleted || Mobile.ControlMaster?.Deleted != false) - { - return; - } - - switch (Mobile.ControlOrder) - { - case OrderType.None: - { - Mobile.ControlMaster.RevealingAction(); - Mobile.Home = Mobile.Location; - Mobile.SetCurrentSpeedToPassive(); - Mobile.PlaySound(Mobile.GetIdleSound()); - Mobile.Warmode = false; - Mobile.Combatant = null; - break; - } - - case OrderType.Come: - { - Mobile.ControlMaster.RevealingAction(); - Mobile.SetCurrentSpeedToActive(); - Mobile.PlaySound(Mobile.GetIdleSound()); - Mobile.Warmode = false; - Mobile.Combatant = null; - break; - } - - case OrderType.Drop: - { - Mobile.ControlMaster.RevealingAction(); - Mobile.SetCurrentSpeedToPassive(); - Mobile.PlaySound(Mobile.GetIdleSound()); - Mobile.Warmode = false; - Mobile.Combatant = null; - break; - } - - case OrderType.Friend: - case OrderType.Unfriend: - { - Mobile.ControlMaster.RevealingAction(); - break; - } - - case OrderType.Guard: - { - Mobile.ControlMaster.RevealingAction(); - Mobile.SetCurrentSpeedToActive(); - Mobile.PlaySound(Mobile.GetIdleSound()); - Mobile.Warmode = true; - Mobile.Combatant = null; - Mobile.ControlMaster.SendLocalizedMessage(1049671, Mobile.Name); // ~1_PETNAME~ is now guarding you. - break; - } - - case OrderType.Attack: - { - Mobile.ControlMaster.RevealingAction(); - Mobile.SetCurrentSpeedToActive(); - Mobile.PlaySound(Mobile.GetIdleSound()); - - Mobile.Warmode = true; - Mobile.Combatant = null; - break; - } - - case OrderType.Patrol: - { - Mobile.ControlMaster.RevealingAction(); - Mobile.SetCurrentSpeedToActive(); - Mobile.PlaySound(Mobile.GetIdleSound()); - Mobile.Warmode = false; - Mobile.Combatant = null; - break; - } - - case OrderType.Release: - { - Mobile.ControlMaster.RevealingAction(); - Mobile.SetCurrentSpeedToPassive(); - Mobile.PlaySound(Mobile.GetIdleSound()); - Mobile.Warmode = false; - Mobile.Combatant = null; - break; - } - - case OrderType.Stay: - { - Mobile.ControlMaster.RevealingAction(); - Mobile.SetCurrentSpeedToPassive(); - Mobile.PlaySound(Mobile.GetIdleSound()); - Mobile.Warmode = false; - Mobile.Combatant = null; - break; - } - - case OrderType.Stop: - { - Mobile.ControlMaster.RevealingAction(); - Mobile.Home = Mobile.Location; - Mobile.SetCurrentSpeedToPassive(); - Mobile.PlaySound(Mobile.GetIdleSound()); - Mobile.Warmode = false; - Mobile.Combatant = null; - break; - } - - case OrderType.Follow: - { - Mobile.ControlMaster.RevealingAction(); - Mobile.SetCurrentSpeedToActive(); - Mobile.PlaySound(Mobile.GetIdleSound()); - - Mobile.Warmode = false; - Mobile.Combatant = null; - break; - } - - case OrderType.Transfer: - { - Mobile.ControlMaster.RevealingAction(); - Mobile.SetCurrentSpeedToPassive(); - Mobile.PlaySound(Mobile.GetIdleSound()); - - Mobile.Warmode = false; - Mobile.Combatant = null; - break; - } - } - } - - public virtual bool DoOrderNone() - { - DebugSay("I have no order"); - - WalkRandomInHome(3, 2, 1); - - if (Mobile.Combatant?.Deleted == false && Mobile.Combatant.Alive && - !Mobile.Combatant.IsDeadBondedPet) - { - Mobile.Warmode = true; - Mobile.Direction = Mobile.GetDirectionTo(Mobile.Combatant); - } - else - { - Mobile.Warmode = false; - } - - return true; - } - - public virtual bool DoOrderCome() - { - if (Mobile.ControlMaster?.Deleted != false) - { - return true; - } - - var iCurrDist = (int)Mobile.GetDistanceToSqrt(Mobile.ControlMaster); - - if (iCurrDist > Mobile.RangePerception) - { - DebugSay("I have lost my master. I stay here"); - - Mobile.ControlTarget = null; - Mobile.ControlOrder = OrderType.None; - } - else - { - DebugSay("My master told me come"); - - // Not exactly OSI style, but better than nothing. - var bRun = iCurrDist > 5; - - if (WalkMobileRange(Mobile.ControlMaster, 1, bRun, 0, 1)) - { - if (Mobile.Combatant?.Deleted == false && Mobile.Combatant.Alive && - !Mobile.Combatant.IsDeadBondedPet) - { - Mobile.Warmode = true; - // m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant, bRun); - } - else - { - Mobile.Warmode = false; - } - } - } - - return true; - } - - public virtual bool DoOrderDrop() - { - if (Mobile.IsDeadPet || !Mobile.CanDrop) - { - return true; - } - - DebugSay("I drop my stuff for my master"); - - var pack = 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(Mobile.Location, Mobile.Map); - } - } - } - - Mobile.ControlTarget = null; - Mobile.ControlOrder = OrderType.None; - - return true; - } - - public virtual bool CheckHerding() - { - var target = Mobile.TargetLocation; - - if (target == null) - { - return false; // Creature is not being herded - } - - var distance = Mobile.GetDistanceToSqrt(target); - - if (distance is not (< 1 or > 15)) - { - DoMove(Mobile.GetDirectionTo(target)); - return true; - } - - if (distance < 1 && target.X == 1076 && target.Y == 450 && Mobile is HordeMinionFamiliar) - { - if (Mobile.ControlMaster is PlayerMobile pm) - { - var qs = pm.Quest; - - if (qs is DarkTidesQuest) - { - var obj = qs.FindObjective(); - - if (obj?.Completed == false) - { - Mobile.AddToBackpack(new ScrollOfAbraxus()); - obj.Complete(); - } - } - } - } - - Mobile.TargetLocation = null; - return false; // At the target or too far away - } - - public virtual bool DoOrderFollow() - { - if (CheckHerding()) - { - DebugSay("Praise the shepherd!"); - } - else if (Mobile.ControlTarget?.Deleted == false && Mobile.ControlTarget != Mobile) - { - var iCurrDist = (int)Mobile.GetDistanceToSqrt(Mobile.ControlTarget); - - if (iCurrDist > Mobile.RangePerception) - { - DebugSay("I have lost the one to follow. I stay here"); - - if (Mobile.Combatant?.Deleted == false && Mobile.Combatant.Alive && - !Mobile.Combatant.IsDeadBondedPet) - { - Mobile.Warmode = true; - Mobile.Direction = Mobile.GetDirectionTo(Mobile.Combatant); - } - else - { - Mobile.Warmode = false; - } - } - else - { - this.DebugSayFormatted($"My master told me to follow: {Mobile.ControlTarget.Name}"); - - // Not exactly OSI style, but better than nothing. - var bRun = iCurrDist > 5; - - if (WalkMobileRange(Mobile.ControlTarget, 1, bRun, 0, 1)) - { - if (Mobile.Combatant?.Deleted == false && Mobile.Combatant.Alive && - !Mobile.Combatant.IsDeadBondedPet) - { - Mobile.Warmode = true; - // m_Mobile.Direction = m_Mobile.GetDirectionTo(m_Mobile.Combatant, bRun); - } - else - { - Mobile.Warmode = false; - if (Core.AOS) - { - Mobile.CurrentSpeed = 0.1; - } - } - } - } - } - else - { - DebugSay("I have nobody to follow"); - - Mobile.ControlTarget = null; - Mobile.ControlOrder = OrderType.None; - } - - return true; - } - - public virtual bool DoOrderFriend() - { - var from = Mobile.ControlMaster; - var to = Mobile.ControlTarget; - - if (from?.Deleted != false || to?.Deleted != false || from == to || !to.Player) - { - 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 (Mobile.IsPetFriend(to)) - { - from.SendLocalizedMessage(1049691); // That person is already a friend. - } - else if (!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, $"{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{Mobile.Name}"); - - Mobile.AddPetFriend(to); - - Mobile.ControlTarget = to; - Mobile.ControlOrder = OrderType.Follow; - - return true; - } - } - } - } - - Mobile.ControlTarget = from; - Mobile.ControlOrder = OrderType.Follow; - - return true; - } - - public virtual bool DoOrderUnfriend() - { - var from = Mobile.ControlMaster; - var to = Mobile.ControlTarget; - - if (from?.Deleted != false || to?.Deleted != false || from == to || !to.Player) - { - Mobile.PublicOverheadMessage(MessageType.Regular, 0x3B2, 502039); // *looks confused* - } - else if (!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, $"{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{Mobile.Name}"); - - Mobile.RemovePetFriend(to); - } - - Mobile.ControlTarget = from; - Mobile.ControlOrder = OrderType.Follow; - - return true; - } - - public virtual bool DoOrderGuard() - { - if (Mobile.IsDeadPet) - { - return true; - } - - var controlMaster = Mobile.ControlMaster; - - if (controlMaster?.Deleted != false) - { - return true; - } - - var combatant = 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(Mobile) <= Mobile.RangePerception) - { - if (combatant == null || attacker.GetDistanceToSqrt(controlMaster) < - combatant.GetDistanceToSqrt(controlMaster)) - { - combatant = attacker; - } - } - } - - if (combatant != null) - { - DebugSay("Crap, my master has been attacked! I will attack one of those bastards!"); - } - } - - if (combatant?.Deleted == false && combatant != Mobile && combatant != Mobile.ControlMaster && - combatant.Alive && !combatant.IsDeadBondedPet && Mobile.CanSee(combatant) && - Mobile.CanBeHarmful(combatant, false) && combatant.Map == Mobile.Map) - { - DebugSay("Guarding from target..."); - - Mobile.Combatant = combatant; - 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 - { - DebugSay("Nothing to guard from"); - - Mobile.Warmode = false; - if (Core.AOS) - { - Mobile.CurrentSpeed = 0.1; - } - - WalkMobileRange(controlMaster, 1, false, 0, 1); - } - - return true; - } - - public virtual bool DoOrderAttack() - { - if (Mobile.IsDeadPet) - { - return true; - } - - if (Mobile.ControlTarget?.Deleted != false || Mobile.ControlTarget.Map != Mobile.Map || - !Mobile.ControlTarget.Alive || Mobile.ControlTarget.IsDeadBondedPet) - { - 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) - { - Mobile.ControlTarget = Mobile.ControlMaster; - Mobile.ControlOrder = OrderType.Follow; - } - else - { - Mobile.ControlTarget = null; - Mobile.ControlOrder = OrderType.None; - } - - if (Mobile.FightMode is FightMode.Closest or FightMode.Aggressor) - { - Mobile newCombatant = null; - var newScore = 0.0; - - foreach (var aggr in Mobile.GetMobilesInRange(Mobile.RangePerception)) - { - if (!Mobile.CanSee(aggr) || aggr.Combatant != Mobile) - { - continue; - } - - if (aggr.IsDeadBondedPet || !aggr.Alive) - { - continue; - } - - var aggrScore = Mobile.GetFightModeRanking(aggr, FightMode.Closest, false); - - if ((newCombatant == null || aggrScore > newScore) && Mobile.InLOS(aggr)) - { - newCombatant = aggr; - newScore = aggrScore; - } - } - - if (newCombatant != null) - { - Mobile.ControlTarget = newCombatant; - Mobile.ControlOrder = OrderType.Attack; - Mobile.Combatant = newCombatant; - DebugSay("But -that- is not dead. Here we go again..."); - - Think(); - } - } - } - else - { - DebugSay("Attacking target..."); - - Think(); - } - - return true; - } - - public virtual bool DoOrderPatrol() - { - DebugSay("This order is not yet coded"); - - return true; - } - - public virtual bool DoOrderRelease() - { - DebugSay("I have been released"); - - Mobile.PlaySound(Mobile.GetAngerSound()); - - Mobile.SetControlMaster(null); - Mobile.SummonMaster = null; - - Mobile.BondingBegin = DateTime.MinValue; - Mobile.OwnerAbandonTime = DateTime.MinValue; - Mobile.IsBonded = false; - - var spawner = Mobile.Spawner; - - if (spawner != null && spawner.HomeLocation != Point3D.Zero) - { - Mobile.Home = spawner.HomeLocation; - Mobile.RangeHome = spawner.HomeRange; - } - - if (Mobile.DeleteOnRelease || Mobile.IsDeadPet) - { - Mobile.Delete(); - } - - Mobile.BeginDeleteTimer(); - - if (Mobile.CanDrop) - { - Mobile.DropBackpack(); - } - - return true; - } - - public virtual bool DoOrderStay() - { - DebugSay(CheckHerding() ? "Praise the shepherd!" : "My master told me to stay"); - - return true; - } - - public virtual bool DoOrderStop() - { - if (Mobile.ControlMaster?.Deleted != false) - { - return true; - } - - DebugSay("My master told me to stop."); - - Mobile.Direction = Mobile.GetDirectionTo(Mobile.ControlMaster); - Mobile.Home = Mobile.Location; - - Mobile.ControlTarget = null; - - if (Core.ML) - { - WalkRandomInHome(3, 2, 1); - } - else - { - Mobile.ControlOrder = OrderType.None; - } - - 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($"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 (!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 (!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(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(Mobile)); - } - } - } - } - - Mobile.ControlTarget = null; - Mobile.ControlOrder = OrderType.Stay; - - return true; - } - - public virtual bool DoBardPacified() - { - if (Core.Now < Mobile.BardEndTime) - { - DebugSay("I am pacified, I wait"); - - Mobile.Combatant = null; - Mobile.Warmode = false; - } - else - { - DebugSay("I'm not pacified any longer"); - - Mobile.BardPacified = false; - } - - return true; - } - - public virtual bool DoBardProvoked() - { - if (Core.Now >= Mobile.BardEndTime && - (Mobile.BardMaster?.Deleted != false || - Mobile.BardMaster.Map != Mobile.Map || Mobile.GetDistanceToSqrt(Mobile.BardMaster) > - Mobile.RangePerception)) - { - DebugSay("I have lost my provoker"); - - Mobile.BardProvoked = false; - Mobile.BardMaster = null; - Mobile.BardTarget = null; - - Mobile.Combatant = null; - Mobile.Warmode = false; - } - else if (Mobile.BardTarget?.Deleted != false || Mobile.BardTarget.Map != Mobile.Map || - Mobile.GetDistanceToSqrt(Mobile.BardTarget) > Mobile.RangePerception) - { - DebugSay("I have lost my provoke target"); - - Mobile.BardProvoked = false; - Mobile.BardMaster = null; - Mobile.BardTarget = null; - - Mobile.Combatant = null; - Mobile.Warmode = false; - } - else - { - Mobile.Combatant = Mobile.BardTarget; - _action = ActionType.Combat; - - Mobile.OnThink(); - Think(); - } - - return true; - } - - public virtual void WalkRandom(int iChanceToNotMove, int iChanceToDir, int iSteps) - { - if (Mobile.Deleted || 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 : 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 (Mobile == null || Mobile.Deleted || Mobile.Frozen || Mobile.Paralyzed || - Mobile.Spell?.IsCasting == true || Mobile.DisallowAllMoves) - { - return MoveResult.BadState; - } - - if (!CheckMove()) - { - return MoveResult.BadState; - } - - var delay = (int)(TransformMoveDelay(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 - Mobile.Direction = d; - NextMove += delay; - - if (Core.TickCount - NextMove > 0) - { - NextMove = Core.TickCount; - } - - Mobile.Pushing = false; - var mobDirection = Mobile.Direction; - - // Do the actual move - MoveImpl.IgnoreMovableImpassables = Mobile.CanMoveOverObstacles && !Mobile.CanDestroyObstacles; - var moveResult = 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 (Mobile.Warmode && Mobile.Combatant != null) - { - var remaining = Mobile.NextCombatTime - Core.TickCount; - var maxWait = Math.Min(delay, 400); - if (remaining < maxWait) - { - Mobile.NextCombatTime = Core.TickCount + maxWait; - } - } - return MoveResult.Success; - } - - if ((mobDirection & Direction.Mask) != (d & Direction.Mask)) - { - return MoveResult.Blocked; - } - - var wasPushing = Mobile.Pushing; - - var blocked = true; - - var canOpenDoors = Mobile.CanOpenDoors; - var canDestroyObstacles = Mobile.CanDestroyObstacles; - - if (canOpenDoors || canDestroyObstacles) - { - DebugSay("My movement was blocked, I will try to clear some obstacles."); - - var map = Mobile.Map; - - if (map != null) - { - var x = Mobile.X; - var y = 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 > Mobile.Z && - 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 > Mobile.Z && Mobile.Z + 16 > item.Z) - { - if (!Mobile.InRange(item.GetWorldLocation(), 1)) - { - continue; - } - - queue.Enqueue(item); - ++destroyables; - } - } - - if (destroyables > 0) - { - Effects.PlaySound(new Point3D(x, y, Mobile.Z), Mobile.Map, 0x3B3); - } - - if (queue.Count > 0) - { - blocked = false; // retry movement - } - - while (queue.Count > 0) - { - var item = queue.Dequeue(); - - if (item is BaseDoor door) - { - DebugSay("Little do they expect, I've learned how to open doors. Didn't they read the script??"); - - DebugSay("*twist*"); - - door.Use(Mobile); - } - else - { - this.DebugSayFormatted($"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 > Mobile.Z) - { - queue.Enqueue(check); - } - } - - cont.Destroy(); - } - else - { - item.Delete(); - } - } - } - - if (!blocked) - { - blocked = !Mobile.Move(d); - } - } - } - - if (blocked) - { - var offset = Utility.RandomDouble() < 0.4 ? 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; - } - - return MoveResult.Success; - } - - public virtual void WalkRandomInHome(int iChanceToNotMove, int iChanceToDir, int iSteps) - { - if (Mobile.Deleted || Mobile.DisallowAllMoves) - { - return; - } - - if (Mobile.Home == Point3D.Zero) - { - if (Mobile.Spawner is RegionSpawner rs) - { - Region region = rs.SpawnRegion; - - if (Mobile.Region.AcceptsSpawnsFrom(region)) - { - Mobile.WalkRegion = region; - WalkRandom(iChanceToNotMove, iChanceToDir, iSteps); - Mobile.WalkRegion = null; - } - else - { - if (region.GoLocation != Point3D.Zero && Utility.RandomBool()) - { - DoMove(Mobile.GetDirectionTo(region.GoLocation)); - } - else - { - WalkRandom(iChanceToNotMove, iChanceToDir, 1); - } - } - } - else - { - WalkRandom(iChanceToNotMove, iChanceToDir, iSteps); - } - } - else - { - for (var i = 0; i < iSteps; i++) - { - if (Mobile.RangeHome != 0) - { - var iCurrDist = (int)Mobile.GetDistanceToSqrt(Mobile.Home); - - if (iCurrDist < Mobile.RangeHome * 2 / 3) - { - WalkRandom(iChanceToNotMove, iChanceToDir, 1); - } - else if (iCurrDist > Mobile.RangeHome) - { - DoMove(Mobile.GetDirectionTo(Mobile.Home)); - } - else if (Utility.Random(10) > 5) - { - DoMove(Mobile.GetDirectionTo(Mobile.Home)); - } - else - { - WalkRandom(iChanceToNotMove, iChanceToDir, 1); - } - } - else - { - if (Mobile.Location != Mobile.Home) - { - DoMove(Mobile.GetDirectionTo(Mobile.Home)); - } - } - } - } - } - - public virtual bool CheckFlee() - { - if (Mobile.CheckFlee()) - { - var combatant = Mobile.Combatant; - - if (combatant == null) - { - WalkRandom(1, 2, 1); - } - else - { - var d = combatant.GetDirectionTo(Mobile); - - d = (Direction)((int)d + Utility.RandomMinMax(-1, +1)); - - Mobile.Direction = d; - Mobile.Move(d); - } - - return true; - } - - return false; - } - - public virtual void OnTeleported() - { - if (_path != null) - { - DebugSay("Teleported; repathing"); - - _path.ForceRepath(); - } - } - - public virtual bool MoveTo(Mobile m, bool run, int range) - { - if (Mobile.Deleted || Mobile.DisallowAllMoves || m?.Deleted != false) - { - return false; - } - - if (Mobile.InRange(m, range)) - { - _path = null; - return true; - } - - if (_path?.Goal == m) - { - if (_path.Follow(run, 1)) - { - _path = null; - return true; - } - } - else if (!DoMove(Mobile.GetDirectionTo(m), true)) - { - _path = new PathFollower(Mobile, m) { Mover = DoMoveImpl }; - - if (_path.Follow(run, 1)) - { - _path = null; - return true; - } - } - else - { - _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 (Mobile.Deleted || Mobile.DisallowAllMoves) - { - return false; - } - - if (m == null) - { - return false; - } - - for (var i = 0; i < iSteps; i++) - { - // Get the current distance - var iCurrDist = (int)Mobile.GetDistanceToSqrt(m); - - if (iCurrDist < iWantDistMin || iCurrDist > iWantDistMax) - { - var needCloser = iCurrDist > iWantDistMax; - - if (needCloser && _path != null && _path.Goal == m) - { - if (_path.Follow(run, 1)) - { - _path = null; - } - } - else - { - var dirTo = iCurrDist > iWantDistMax ? - Mobile.GetDirectionTo(m, run) : m.GetDirectionTo(Mobile, run); - - if (!DoMove(dirTo, true) && needCloser) - { - _path = new PathFollower(Mobile, m) { Mover = DoMoveImpl }; - - if (_path.Follow(run, 1)) - { - _path = null; - } - } - else - { - _path = null; - } - } - } - else - { - return true; - } - } - - // Get the current distance - var iNewDist = (int)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 (Mobile.Deleted) - { - return false; - } - - if (Mobile.BardProvoked) - { - if (Mobile.BardTarget?.Deleted != false) - { - Mobile.FocusMob = null; - return false; - } - - Mobile.FocusMob = Mobile.BardTarget; - return Mobile.FocusMob != null; - } - - if (Mobile.Controlled) - { - if (Mobile.ControlTarget?.Deleted != false || Mobile.ControlTarget.Hidden || - !Mobile.ControlTarget.Alive || Mobile.ControlTarget.IsDeadBondedPet || - !Mobile.InRange(Mobile.ControlTarget, Mobile.RangePerception * 2)) - { - if (Mobile.ControlTarget != null && Mobile.ControlTarget != Mobile.ControlMaster) - { - Mobile.ControlTarget = null; - } - - Mobile.FocusMob = null; - return false; - } - - Mobile.FocusMob = Mobile.ControlTarget; - return Mobile.FocusMob != null; - } - - if (Mobile.ConstantFocus != null) - { - DebugSay("Acquired my constant focus"); - - Mobile.FocusMob = Mobile.ConstantFocus; - return true; - } - - if (acqType == FightMode.None) - { - Mobile.FocusMob = null; - return false; - } - - if (acqType == FightMode.Aggressor && Mobile.Aggressors.Count == 0 && Mobile.Aggressed.Count == 0 && - Mobile.FactionAllegiance == null && Mobile.EthicAllegiance == null) - { - Mobile.FocusMob = null; - return false; - } - - if (Core.TickCount - Mobile.NextReacquireTime < 0) - { - Mobile.FocusMob = null; - return false; - } - - Mobile.NextReacquireTime = Core.TickCount + (int)Mobile.ReacquireDelay.TotalMilliseconds; - - DebugSay("Acquiring..."); - - var map = Mobile.Map; - - if (map == null) - { - // TODO: Is this correct? Maybe it should return false? - return Mobile.FocusMob != null; - } - - Mobile newFocusMob = null; - var val = double.MinValue; - Mobile enemySummonMob = null; - var enemySummonVal = double.MinValue; - - foreach (var m in map.GetMobilesInRange(Mobile.Location, iRange)) - { - if (m.Deleted || m.Blessed) - { - continue; - } - - // Let's not target ourselves... - if (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 (!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 == Mobile || !bc.SummonMaster.Player && IsHostile(bc.SummonMaster))) - { - continue; - } - - if (Mobile.Summoned && Mobile.SummonMaster != null) - { - // Animated creatures cannot attack players directly. - if (pm != null && Mobile.IsAnimatedDead) - { - continue; - } - - // Animated creatures cannot attack other animated creatures or pets of other players - if (Mobile.IsAnimatedDead && bc != null && (bc.IsAnimatedDead || bc.Controlled)) - { - continue; - } - - // If this is a summon, it can't target its controller or invalid targets. - if (Mobile.FollowsAcquireRules && (m == Mobile.SummonMaster || !SpellHelper.ValidIndirectTarget(Mobile, m))) - { - continue; - } - } - - // If we only want faction friends, make sure it's one. - if (bFacFriend && !Mobile.IsFriend(m)) - { - continue; - } - - // Ignore anyone under EtherealVoyage - if (TransformationSpellHelper.UnderTransformation(m, typeof(EtherealVoyageSpell))) - { - continue; - } - - // Ignore players with activated honor - if (Mobile.Combatant != m && VirtueSystem.GetVirtues(pm)?.HonorActive == true) - { - continue; - } - - if (acqType is FightMode.Aggressor or FightMode.Evil) - { - var bValid = IsHostile(m); - - if (!bValid) - { - bValid = Mobile.GetFactionAllegiance(m) == BaseCreature.Allegiance.Enemy || - 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 && !Mobile.IsEnemy(m)) - { - continue; - } - - // If it's an enemy factioned mobile, make sure we can be harmful to it. - if (bFacFoe && !bFacFriend && !Mobile.CanBeHarmful(m, false)) - { - continue; - } - } - - var theirVal = Mobile.GetFightModeRanking(m, acqType, bPlayerOnly); - - // Always prefer someone else over the summon master (EV/BS) - if ((theirVal > val || newFocusMob == Mobile.SummonMaster) && 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 && Mobile.InLOS(m) && bc?.Summoned == true && bc?.Controlled != true) - { - enemySummonMob = m; - enemySummonVal = theirVal; - } - } - - Mobile.FocusMob = newFocusMob ?? enemySummonMob; - return Mobile.FocusMob != null; - } - - private bool IsHostile(Mobile from) - { - if (Mobile.Combatant == from || from.Combatant == Mobile) - { - return true; - } - - var count = Math.Max(Mobile.Aggressors.Count, Mobile.Aggressed.Count); - - for (var a = 0; a < count; ++a) - { - if (a < Mobile.Aggressed.Count && Mobile.Aggressed[a].Attacker == from) - { - return true; - } - - if (a < Mobile.Aggressors.Count && Mobile.Aggressors[a].Defender == from) - { - return true; - } - } - - return false; - } - - public virtual void DetectHidden() - { - if (Mobile.Deleted || Mobile.Map == null) - { - return; - } - - DebugSay("Checking for hidden players"); - - var srcSkill = Mobile.Skills.DetectHidden.Value; - - if (srcSkill <= 0) - { - return; - } - - foreach (var trg in Mobile.GetMobilesInRange(Mobile.RangePerception)) - { - if (trg != Mobile && trg.Player && trg.Alive && trg.Hidden && trg.AccessLevel == AccessLevel.Player && - Mobile.InLOS(trg)) - { - 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 = 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 (Mobile.PlayerRangeSensitive) - { - _timer.Stop(); - - var spawner = Mobile.Spawner; - - if (spawner?.ReturnOnDeactivate == true && !Mobile.Controlled && ( - spawner.HomeLocation == Point3D.Zero && !Mobile.Region.AcceptsSpawnsFrom(spawner.Region) || - !Mobile.InRange(spawner.HomeLocation, spawner.HomeRange) - )) - { - Timer.StartTimer(ReturnToHome); - } - } - } - - private void ReturnToHome() - { - if (Mobile.Spawner != null) - { - var loc = Mobile.Spawner.GetSpawnPosition(Mobile, Mobile.Spawner.Map); - - if (loc != Point3D.Zero) - { - Mobile.MoveToWorld(loc, Mobile.Spawner.Map); - } - } - } - - public virtual void Activate() - { - if (!_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. - _timer.Delay = TimeSpan.FromMilliseconds(Utility.Random(48) * 8); - _timer.Start(); - } - } - - public virtual void OnCurrentSpeedChanged() - { - _timer.Interval = TimeSpan.FromSeconds(Math.Max(0.008, 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 _creature; - - public TransferItem(BaseCreature creature) - : base(ShrinkTable.Lookup(creature)) - { - _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, _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)) - { - return false; - } - - if (Deleted || _creature?.Deleted != false || _creature.ControlMaster != from || - !from.CheckAlive() || !to.CheckAlive()) - { - return false; - } - - if (from.Map != _creature.Map || !from.InRange(_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 && !_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 && !_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 + _creature.ControlSlots > to.FollowersMax) - { - to.SendLocalizedMessage(1049607); // You have too many followers to control that creature. - - return false; - } - else if (accepted && IsInCombat(_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 (_creature?.Deleted != false || _creature.ControlMaster != from || !from.CheckAlive() || - !to.CheckAlive()) - { - return; - } - - if (from.Map != _creature.Map || !from.InRange(_creature, 14)) - { - return; - } - - if (accepted && _creature.SetControlMaster(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~. - // ~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 _owner; - - public AITimer(BaseAI owner) : base( - TimeSpan.FromMilliseconds(Utility.Random(96) * 8), - TimeSpan.FromSeconds(Math.Max(0.0, owner.Mobile.CurrentSpeed)) - ) - { - _owner = owner; - _owner._nextDetectHidden = Core.TickCount; - } - - protected override void OnTick() - { - if (_owner.Mobile.Deleted) - { - Stop(); - return; - } - - if (_owner.Mobile.Map == null || _owner.Mobile.Map == Map.Internal) - { - _owner.Deactivate(); - return; - } - - if (_owner.Mobile.PlayerRangeSensitive) // have to check this in the timer.... - { - var sect = _owner.Mobile.Map.GetSector(_owner.Mobile.Location); - if (!sect.Active) - { - _owner.Deactivate(); - return; - } - } - - _owner.Mobile.OnThink(); - - if (_owner.Mobile.Deleted) - { - Stop(); - return; - } - - if (_owner.Mobile.Map == null || _owner.Mobile.Map == Map.Internal) - { - _owner.Deactivate(); - return; - } - - if (_owner.Mobile.BardPacified) - { - _owner.DoBardPacified(); - } - else if (_owner.Mobile.BardProvoked) - { - _owner.DoBardProvoked(); - } - else if (!_owner.Mobile.Controlled) - { - if (!_owner.Think()) - { - Stop(); - return; - } - } - else if (!_owner.Obey()) - { - Stop(); - return; - } - - if (_owner.CanDetectHidden && Core.TickCount - _owner._nextDetectHidden >= 0) - { - _owner.DetectHidden(); - - // Not exactly OSI style, approximation. - var delay = Math.Min(15000 / _owner.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 - - _owner._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..d42e24409 --- /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 = Core.AOS ? 100 : owner.Mobile.CurrentSpeed * 400; + } + else if (owner.Mobile.CurrentSpeed <= 0.4) + { + interval = owner.Mobile.CurrentSpeed * 1000; + } + else + { + interval = owner.Mobile.CurrentSpeed * 3000; + } + + return Math.Max(interval, 100); + } + + 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..02b178dc2 --- /dev/null +++ b/Projects/UOContent/Mobiles/AI/BaseAI/BaseAI.cs @@ -0,0 +1,918 @@ +/************************************************************************* + * 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() + { + DebugSay("I am still on guard."); + if (Utility.Random(10) == 0) + { + Mobile.Turn(Utility.Random(0, 2) - 1); + } + + 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/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 index c2d5dcf15..da1f80019 100644 --- a/Projects/UOContent/Mobiles/AI/BaseAI/DebugInterpolatedStringHandler.cs +++ b/Projects/UOContent/Mobiles/AI/BaseAI/DebugInterpolatedStringHandler.cs @@ -79,7 +79,7 @@ public ref struct DebugInterpolatedStringHandler /// 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 as BaseCreature)?.Debug == true && Core.TickCount >= ai.NextDebugMessage; + _debugActive = ai.Mobile?.Debug == true && Core.TickCount >= ai.NextDebugMessage; if (_debugActive) { @@ -660,3 +660,4 @@ public ref struct DebugInterpolatedStringHandler } } } + 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/UOContent/Mobiles/AI/BaseAI/SpeechEventArgsExt.cs b/Projects/UOContent/Mobiles/AI/BaseAI/SpeechEventArgsExt.cs new file mode 100644 index 000000000..5090f235a --- /dev/null +++ b/Projects/UOContent/Mobiles/AI/BaseAI/SpeechEventArgsExt.cs @@ -0,0 +1,35 @@ +/************************************************************************* + * ModernUO * + * Copyright 2019-2025 - ModernUO Development Team * + * Email: hi@modernuo.com * + * 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 * + * 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; + +public static class SpeechEventArgsExt +{ + 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; + } + } + + 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/HealerAI.cs b/Projects/UOContent/Mobiles/AI/HealerAI.cs index e6189e033..2a1127b43 100644 --- a/Projects/UOContent/Mobiles/AI/HealerAI.cs +++ b/Projects/UOContent/Mobiles/AI/HealerAI.cs @@ -31,23 +31,15 @@ public class HealerAI : BaseAI { var spellTarg = targ as ISpellTarget; - if (spellTarg?.Spell is CureSpell) + var funcs = spellTarg?.Spell switch { - ProcessTarget(targ, Cure); - } - else if (spellTarg?.Spell is GreaterHealSpell) - { - ProcessTarget(targ, GHeal); - } - else if (spellTarg?.Spell is HealSpell) - { - ProcessTarget(targ, LHeal); - } - else - { - targ.Cancel(Mobile, TargetCancelType.Canceled); - } + CureSpell => Cure, + GreaterHealSpell => GHeal, + HealSpell => LHeal, + _ => null + }; + ProcessTarget(targ, funcs); return true; } @@ -102,6 +94,12 @@ 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) diff --git a/Projects/UOContent/Mobiles/AI/MageAI.cs b/Projects/UOContent/Mobiles/AI/MageAI.cs index f54326baf..03fe60a6a 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[] _offsets = - { + private static readonly int[] Offsets = + [ -1, -1, -1, 0, -1, 1, @@ -47,7 +47,7 @@ public class MageAI : BaseAI 2, 0, 2, 1, 2, 2 - }; + ]; protected int _combo = -1; @@ -58,8 +58,7 @@ public class MageAI : BaseAI private LandTarget _revealTarget; - public MageAI(BaseCreature m) - : base(m) + public MageAI(BaseCreature m) : base(m) { } @@ -518,7 +517,15 @@ public class MageAI : BaseAI } } - DebugSay(spell != null ? $"Casting {spell.Name}" : "I don't have a spell to use!"); + if (spell != null) + { + this.DebugSayFormatted($"Casting {spell.Name}"); + } + else + { + DebugSay("I don't have a spell to use!"); + } + return spell; } @@ -628,6 +635,7 @@ public class MageAI : BaseAI Mobile.Combatant = c = Mobile.FocusMob!; this.DebugSayFormatted($"Something happened to my combatant, so I am going to fight {c.Name}"); + Mobile.FocusMob = null; } else @@ -766,7 +774,7 @@ public class MageAI : BaseAI RunTo(c); } - if (Mobile.Spell != null || !Mobile.InRange(c, 1) || Core.TickCount - Mobile.LastMoveTime > 800) + if (Mobile.InRange(c, 1) || Mobile.Spell?.IsCasting == true || Core.TickCount - Mobile.LastMoveTime > 400) { Mobile.Direction = Mobile.GetDirectionTo(c); } @@ -829,8 +837,6 @@ public class MageAI : BaseAI public override bool DoActionFlee() { - // Mobile c = m_Mobile.Combatant; - if ((Mobile.Mana > 20 || Mobile.Mana == Mobile.ManaMax) && Mobile.Hits > Mobile.HitsMax / 2) { DebugSay("I am stronger now, my guard is up"); @@ -1098,9 +1104,9 @@ public class MageAI : BaseAI py = toTarget.Y; } - for (var i = 0; i < _offsets.Length; i += 2) + for (var i = 0; i < Offsets.Length; i += 2) { - int x = _offsets[i], y = _offsets[i + 1]; + int x = Offsets[i], y = Offsets[i + 1]; var p = new Point3D(px + x, py + y, 0); diff --git a/Projects/UOContent/Mobiles/AI/MeleeAI.cs b/Projects/UOContent/Mobiles/AI/MeleeAI.cs index adc8d566b..edf0b108e 100644 --- a/Projects/UOContent/Mobiles/AI/MeleeAI.cs +++ b/Projects/UOContent/Mobiles/AI/MeleeAI.cs @@ -69,11 +69,6 @@ public class MeleeAI : BaseAI if (!MoveTo(combatant, true, Mobile.RangeFight)) { - if (Mobile.InRange(combatant, 1)) - { - Mobile.Direction = Mobile.GetDirectionTo(combatant); - } - if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { this.DebugSayFormatted($"My move is blocked, so I am going to attack {Mobile.FocusMob!.Name}"); @@ -93,25 +88,21 @@ public class MeleeAI : BaseAI this.DebugSayFormatted($"I cannot find {combatant.Name}, so my guard is up"); } - else if (Core.TickCount - Mobile.LastMoveTime > 400) + else if (Core.TickCount - Mobile.LastMoveTime > 200) { Mobile.Direction = Mobile.GetDirectionTo(combatant); } - if (!Mobile.Controlled && !Mobile.Summoned && Mobile.CanFlee) + // We are low on health, should we flee? + if (!Mobile.Controlled && !Mobile.Summoned && Mobile.CanFlee && Mobile.Hits < Mobile.HitsMax * 20 / 100) { - if (Mobile.Hits < 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 - Mobile.Hits); // (10 + diff)% chance to flee; - if (Utility.Random(0, 100) < fleeChance) - { - this.DebugSayFormatted($"I am going to flee from {combatant.Name}"); - - Action = ActionType.Flee; - return true; - } + Action = ActionType.Flee; + return true; } } @@ -146,8 +137,6 @@ public class MeleeAI : BaseAI { DebugSay("I am stronger now, so I will continue fighting"); - Mobile.PlaySound(Mobile.GetAttackSound()); - Mobile.CurrentSpeed = Mobile.ActiveSpeed; Action = ActionType.Combat; } else diff --git a/Projects/UOContent/Mobiles/AI/OppositionGroup.cs b/Projects/UOContent/Mobiles/AI/OppositionGroup.cs index c087112db..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[][] _types; + var fromGroup = IndexOf(from); + var targGroup = IndexOf(target); - public OppositionGroup(Type[][] types) => _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 < _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; } + + 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/VendorAI.cs b/Projects/UOContent/Mobiles/AI/VendorAI.cs index 30f63dd29..a835ef61f 100644 --- a/Projects/UOContent/Mobiles/AI/VendorAI.cs +++ b/Projects/UOContent/Mobiles/AI/VendorAI.cs @@ -24,20 +24,17 @@ public class VendorAI : BaseAI 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 (Mobile.FocusMob != null) - { - this.DebugSayFormatted($"{Mobile.FocusMob.Name} has talked to me"); + Mobile.Warmode = false; - Action = ActionType.Interact; - } - else - { - Mobile.Warmode = false; - - base.DoActionWander(); - } + base.DoActionWander(); } return true; @@ -60,7 +57,7 @@ public class VendorAI : BaseAI if (customer?.Deleted != false || customer.Map != Mobile.Map) { - DebugSay("My customer have disapeared"); + DebugSay("My customer has disappeared"); Mobile.FocusMob = null; diff --git a/Projects/UOContent/Mobiles/BaseCreature.cs b/Projects/UOContent/Mobiles/BaseCreature.cs index ecfbba73f..c8aeeea18 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)) @@ -3536,7 +3506,6 @@ namespace Server.Mobiles } Guild = null; - ResetSpeeds(); Delta(MobileDelta.Noto); @@ -4926,15 +4895,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/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/version.json b/version.json index 3bb3f304b..227ebd3dd 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.0" } From 8a118170e7dc22dd3e1f1ff6360a1a2bb63fda49 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Tue, 22 Jul 2025 16:01:27 -0700 Subject: [PATCH 08/47] fix: Fixes treasure map chest crash from null guardians (#2236) --- .../UOContent/Items/Containers/TreasureMapChest.cs | 14 ++++++++------ .../Server.Items.TreasureMapChest.v2.json | 1 + 2 files changed, 9 insertions(+), 6 deletions(-) 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/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" ] From 173037ae77d3b872416e6de6652375c8a8ed0c49 Mon Sep 17 00:00:00 2001 From: Bohica <53943479+Bohicatv@users.noreply.github.com> Date: Tue, 22 Jul 2025 16:01:56 -0700 Subject: [PATCH 09/47] fix: Prevents recursion in BaseCreature.DoHarmful() (#2202) --- Projects/Server/Mobiles/Mobile.cs | 2 +- Projects/UOContent/Mobiles/BaseCreature.cs | 9 +++------ 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/Projects/Server/Mobiles/Mobile.cs b/Projects/Server/Mobiles/Mobile.cs index 48ef9a59c..7c998e887 100644 --- a/Projects/Server/Mobiles/Mobile.cs +++ b/Projects/Server/Mobiles/Mobile.cs @@ -8286,7 +8286,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/UOContent/Mobiles/BaseCreature.cs b/Projects/UOContent/Mobiles/BaseCreature.cs index c8aeeea18..f9898d6cd 100644 --- a/Projects/UOContent/Mobiles/BaseCreature.cs +++ b/Projects/UOContent/Mobiles/BaseCreature.cs @@ -2626,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; From 2df62fba5aa4075f3552b985065e25a79d6cfb36 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Tue, 22 Jul 2025 16:04:04 -0700 Subject: [PATCH 10/47] chore: Bumps various dependencies (#2237) --- Directory.Build.props | 2 +- Projects/Server.Tests/Server.Tests.csproj | 4 ++-- Projects/Server/Server.csproj | 2 +- Projects/UOContent.Tests/UOContent.Tests.csproj | 4 ++-- Projects/UOContent/UOContent.csproj | 4 ++-- 5 files changed, 8 insertions(+), 8 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index a9eae192f..ae71e92d4 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -62,7 +62,7 @@ latest - + diff --git a/Projects/Server.Tests/Server.Tests.csproj b/Projects/Server.Tests/Server.Tests.csproj index 8da175d26..038575c90 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/Server.csproj b/Projects/Server/Server.csproj index 9142059c6..5e16cde20 100644 --- a/Projects/Server/Server.csproj +++ b/Projects/Server/Server.csproj @@ -37,7 +37,7 @@ - + diff --git a/Projects/UOContent.Tests/UOContent.Tests.csproj b/Projects/UOContent.Tests/UOContent.Tests.csproj index 967f997cd..77064faa1 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/UOContent.csproj b/Projects/UOContent/UOContent.csproj index a1146db1c..bea34e002 100644 --- a/Projects/UOContent/UOContent.csproj +++ b/Projects/UOContent/UOContent.csproj @@ -39,8 +39,8 @@ false - - + + From 1e2f4b1171de799da9be30ab4a1429feb23a506a Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Thu, 24 Jul 2025 14:18:16 -0700 Subject: [PATCH 11/47] fix: Fixes tentacles not getting removed from harrower's list (#2239) --- .../Migrations/Server.Mobiles.HarrowerTentacles.v0.json | 2 +- Projects/UOContent/Mobiles/Special/HarrowerTentacles.cs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) 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/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 From c2e44a58f6e498e907a763f900e6af2ccd0e6aef Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Thu, 24 Jul 2025 14:26:35 -0700 Subject: [PATCH 12/47] fix: Moves container enumerables to Item. (#2238) --- .../Tests/Items/ContainerTests.cs | 46 ++++++++++------- Projects/Server/Items/Container.cs | 27 +++++----- ...ainer.Enumerable.cs => Item.Enumerable.cs} | 49 ++++++++++--------- Projects/Server/Items/Item.cs | 32 ++++++------ 4 files changed, 87 insertions(+), 67 deletions(-) rename Projects/Server/Items/{Container.Enumerable.cs => Item.Enumerable.cs} (85%) 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/Items/Container.cs b/Projects/Server/Items/Container.cs index 3481d182c..65b910e95 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() { 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] From 2eba2868a678654b6f2e7957010aa8eb66d04b8d Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Thu, 24 Jul 2025 14:44:39 -0700 Subject: [PATCH 13/47] fix: Optimizes items to use default weights. (Part 1) (#2240) --- .../UOContent/Engines/Bulk Orders/BaseBOD.cs | 8 +- .../Bulk Orders/Books/BulkOrderBook.cs | 3 +- .../Items/Traps/BaseFactionTrapDeed.cs | 5 +- .../Engines/Plants/MiscItems/FertileDirt.cs | 3 +- .../Engines/Plants/MiscItems/GreenThorns.cs | 3 +- .../UOContent/Engines/Plants/PlantBowl.cs | 6 +- .../UOContent/Engines/Plants/PlantItem.cs | 4 +- Projects/UOContent/Engines/Plants/Seed.cs | 3 +- .../Quests/Collector/Items/EnchantedPaints.cs | 8 +- .../Quests/Collector/Items/PaintedImage.cs | 4 +- .../Quests/Core/Items/EnchantedSextant.cs | 6 +- .../Quests/Core/Items/HornOfRetreat.cs | 3 +- .../Quests/Dark Tides/Items/KronusScroll.cs | 8 +- .../Dark Tides/Items/ScrollOfAbraxus.cs | 6 +- .../Emino's Undertaking/Items/EminosKatana.cs | 6 +- .../Emino's Undertaking/Items/NoteForZoel.cs | 8 +- .../Haochi's Trials/Items/HaochisKatana.cs | 6 +- .../Quests/The Summoning/Items/GoldenSkull.cs | 3 +- .../The Summoning/Items/GrandGrimoire.cs | 3 +- .../Uzeraan Turmoil/Items/QuestDaemonBlood.cs | 6 +- .../Uzeraan Turmoil/Items/QuestDaemonBone.cs | 6 +- .../Uzeraan Turmoil/Items/QuestFertileDirt.cs | 6 +- .../Items/SchmendrickScrollOfPower.cs | 8 +- .../Quests/Witch Apprentice/Items/Cauldron.cs | 6 +- .../Witch Apprentice/Items/HangoverCure.cs | 3 +- .../Witch Apprentice/Items/MoonfireBrew.cs | 6 +- .../BasePigmentsOfTokuno.cs | 4 +- .../Treasures of Tokuno/GreaterArtifacts.cs | 6 +- .../Treasures of Tokuno/LesserArtifacts.cs | 21 +- .../Character Statue Maker/CharacterStatue.cs | 3 +- .../CharacterStatueMaker.cs | 3 +- .../Christmas/2004/DecorativeTopiary.cs | 8 +- .../Christmas/2004/FestiveCactus.cs | 8 +- .../2004/LightOfTheWinterSolstice.cs | 3 +- .../Holiday Stuff/Christmas/2004/Mistletoe.cs | 3 +- .../Christmas/2004/PileOfGlacialSnow.cs | 3 +- .../Holiday Stuff/Christmas/2004/SnowPile.cs | 3 +- .../Holiday Stuff/Christmas/2004/SnowyTree.cs | 8 +- .../Christmas/2010/Addons/FireFliesDeed.cs | 8 +- .../Christmas/2010/Items/AngelDecoration.cs | 21 +- .../Christmas/2010/Items/RockingHorse.cs | 21 +- .../Halloween/2009/Foods/MrPlainsCookies.cs | 26 +- .../Items/Addons/BaseAddonContainerDeed.cs | 4 +- .../UOContent/Items/Addons/BaseAddonDeed.cs | 4 +- .../Items/Armor/Helmets/ChainCoif.cs | 6 +- .../Items/Armor/Leather/FemaleLeatherChest.cs | 6 +- .../Items/Armor/Leather/LeatherBustierArms.cs | 6 +- .../Items/Armor/Leather/LeatherGloves.cs | 6 +- .../Items/Armor/Leather/LeatherGorget.cs | 6 +- .../Items/Armor/Leather/LeatherHiroSode.cs | 6 +- .../Items/Armor/Leather/LeatherSkirt.cs | 6 +- .../Items/Armor/Studded/StuddedBustierArms.cs | 6 +- .../Items/Armor/Studded/StuddedGloves.cs | 6 +- .../Items/Armor/Studded/StuddedGorget.cs | 6 +- .../Items/Armor/Studded/StuddedHiroSode.cs | 6 +- Projects/UOContent/Items/Clothing/Hats.cs | 84 +++++- .../UOContent/Items/Clothing/MiddleTorso.cs | 12 +- Projects/UOContent/Items/Clothing/Shirts.cs | 6 +- Projects/UOContent/Items/Clothing/Shoes.cs | 6 +- Projects/UOContent/Items/Clothing/Waist.cs | 6 +- .../Items/Construction/Chairs/Chairs.cs | 6 +- .../Items/Construction/Chairs/Thrones.cs | 6 +- .../Decorative/GiantReplicaAcorn.cs | 6 +- .../Decorative/MountedDreadHorn.cs | 6 +- .../Items/Construction/Tables/Tables.cs | 30 +- .../Items/Construction/Tables/WritingTable.cs | 6 +- .../UOContent/Items/Containers/Container.cs | 24 +- .../Items/Containers/FurnitureContainer.cs | 78 +++++- .../Items/Deeds/DragonBardingDeed.cs | 6 +- Projects/UOContent/Items/Food/Asian.cs | 6 +- Projects/UOContent/Items/Food/Beverage.cs | 52 +++- Projects/UOContent/Items/Food/Bowls.cs | 12 +- Projects/UOContent/Items/Food/CookableFood.cs | 60 +++- Projects/UOContent/Items/Food/Cooking.cs | 12 +- Projects/UOContent/Items/Food/Vegetables.cs | 48 ++-- Projects/UOContent/Items/Games/Dices.cs | 6 +- Projects/UOContent/Items/Guilds/GuildDeed.cs | 6 +- Projects/UOContent/Items/Jewels/Beads.cs | 6 +- .../UOContent/Items/Misc/ExecutionersCap.cs | 6 +- Projects/UOContent/Items/Misc/GlassItems.cs | 258 +++++++++++++++--- Projects/UOContent/Items/Misc/HairDye.cs | 6 +- .../UOContent/Items/Misc/PlayerVendorDeed.cs | 6 +- Projects/UOContent/Items/Misc/PowerCrystal.cs | 6 +- Projects/UOContent/Items/Misc/Rares.cs | 72 ++++- .../Carpenter Items/TaxidermyKit.cs | 6 +- .../Musical Instruments/Tambourine.cs | 6 +- .../Musical Instruments/TambourineTassel.cs | 6 +- .../Specialized/GlassblowingBook.cs | 6 +- .../Skill Items/Specialized/MasonryBook.cs | 6 +- .../Skill Items/Specialized/SandMiningBook.cs | 6 +- .../Specialized/StoneMiningBook.cs | 6 +- .../Skill Items/Tailor Items/Misc/Scissors.cs | 6 +- .../Items/Skill Items/Thief/DisguiseKit.cs | 6 +- .../Items/Skill Items/Tinkering/Clocks.cs | 6 +- .../Items/Skill Items/Tinkering/Utensils.cs | 60 +++- .../Items/Skill Items/Tools/DrawKnife.cs | 10 +- .../Items/Skill Items/Tools/FlourSifter.cs | 10 +- .../UOContent/Items/Skill Items/Tools/Froe.cs | 10 +- .../Items/Skill Items/Tools/Inshave.cs | 10 +- .../Skill Items/Tools/MalletAndChisel.cs | 10 +- .../Items/Skill Items/Tools/MapmakersPen.cs | 10 +- .../Items/Skill Items/Tools/MortarPestle.cs | 10 +- .../Items/Skill Items/Tools/RollingPin.cs | 10 +- .../Items/Skill Items/Tools/Scorp.cs | 10 +- .../Items/Skill Items/Tools/ScribesPen.cs | 10 +- .../Items/Skill Items/Tools/Skillet.cs | 10 +- .../Items/Skill Items/Tools/SmoothingPlane.cs | 10 +- .../Items/Skill Items/Tools/TinkerTools.cs | 22 +- .../Items/Special/Holiday/HolidayTimepiece.cs | 1 - .../Items/Special/Holiday/WristWatch.cs | 1 - .../Items/Special/ML/GrizzledMareStatuette.cs | 6 +- .../Items/Special/ML/MinotaurHedge.cs | 6 +- .../Items/Special/ML/TormentedChains.cs | 6 +- .../Special/Veteran Rewards/StoneAnkh.cs | 6 +- .../Items/Talismans/Items/EnchantedSwitch.cs | 6 +- .../Items/Talismans/Items/HollowPrism.cs | 6 +- .../Items/Talismans/Items/JeweledFiligree.cs | 6 +- .../Items/Talismans/Items/RunedPrism.cs | 6 +- .../Items/Talismans/Items/RunedSwitch.cs | 6 +- .../Items/Weapons/Knives/ButcherKnife.cs | 6 +- .../UOContent/Items/Weapons/Knives/Dagger.cs | 6 +- .../Items/Weapons/Knives/SkinningKnife.cs | 6 +- .../Items/Weapons/Maces/MagicWand.cs | 6 +- .../Items/Weapons/Swords/CrescentBlade.cs | 6 +- .../Items/Weapons/Swords/ThinLongsword.cs | 6 +- Projects/UOContent/Multis/Camps/BaseCamp.cs | 6 +- 126 files changed, 1184 insertions(+), 380 deletions(-) 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/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/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/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/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/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/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/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/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/Armor/Helmets/ChainCoif.cs b/Projects/UOContent/Items/Armor/Helmets/ChainCoif.cs index d1277dd77..cb76d0e39 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/Leather/FemaleLeatherChest.cs b/Projects/UOContent/Items/Armor/Leather/FemaleLeatherChest.cs index 0a6433ff9..7b13c2075 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/LeatherBustierArms.cs b/Projects/UOContent/Items/Armor/Leather/LeatherBustierArms.cs index 5e7bd8e9a..4dc7c3dde 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/LeatherGloves.cs b/Projects/UOContent/Items/Armor/Leather/LeatherGloves.cs index cab91adcc..28517f5db 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..7e902ae60 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/LeatherHiroSode.cs b/Projects/UOContent/Items/Armor/Leather/LeatherHiroSode.cs index 6b54d1695..40fb5885f 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/LeatherSkirt.cs b/Projects/UOContent/Items/Armor/Leather/LeatherSkirt.cs index acaaa09fb..ea47347e2 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/Studded/StuddedBustierArms.cs b/Projects/UOContent/Items/Armor/Studded/StuddedBustierArms.cs index 3dd466c49..eff01f1f1 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/StuddedGloves.cs b/Projects/UOContent/Items/Armor/Studded/StuddedGloves.cs index aa2d585ea..e7e78da92 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..cd5af7c1b 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/StuddedHiroSode.cs b/Projects/UOContent/Items/Armor/Studded/StuddedHiroSode.cs index 0b0afd5bd..c934b728a 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/Clothing/Hats.cs b/Projects/UOContent/Items/Clothing/Hats.cs index d6bec6723..f6d5b0c49 100644 --- a/Projects/UOContent/Items/Clothing/Hats.cs +++ b/Projects/UOContent/Items/Clothing/Hats.cs @@ -96,7 +96,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 +116,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 +136,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 +156,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 +176,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 +196,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; @@ -280,7 +304,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 +324,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; @@ -401,7 +433,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 +453,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 +479,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 +499,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 +519,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 +539,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..0003b3625 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)] @@ -55,7 +59,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)] diff --git a/Projects/UOContent/Items/Clothing/Shirts.cs b/Projects/UOContent/Items/Clothing/Shirts.cs index 6ba43a870..899d307f1 100644 --- a/Projects/UOContent/Items/Clothing/Shirts.cs +++ b/Projects/UOContent/Items/Clothing/Shirts.cs @@ -23,7 +23,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)] diff --git a/Projects/UOContent/Items/Clothing/Shoes.cs b/Projects/UOContent/Items/Clothing/Shoes.cs index 8e6cb3d7a..167a3fbb7 100644 --- a/Projects/UOContent/Items/Clothing/Shoes.cs +++ b/Projects/UOContent/Items/Clothing/Shoes.cs @@ -153,7 +153,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; diff --git a/Projects/UOContent/Items/Clothing/Waist.cs b/Projects/UOContent/Items/Clothing/Waist.cs index 45f34d768..6920e1866 100644 --- a/Projects/UOContent/Items/Clothing/Waist.cs +++ b/Projects/UOContent/Items/Clothing/Waist.cs @@ -23,7 +23,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)] diff --git a/Projects/UOContent/Items/Construction/Chairs/Chairs.cs b/Projects/UOContent/Items/Construction/Chairs/Chairs.cs index 12b2b9ad8..f9034a1e8 100644 --- a/Projects/UOContent/Items/Construction/Chairs/Chairs.cs +++ b/Projects/UOContent/Items/Construction/Chairs/Chairs.cs @@ -53,7 +53,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/Thrones.cs b/Projects/UOContent/Items/Construction/Chairs/Thrones.cs index de189504f..7ecf175c6 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] diff --git a/Projects/UOContent/Items/Construction/Decorative/GiantReplicaAcorn.cs b/Projects/UOContent/Items/Construction/Decorative/GiantReplicaAcorn.cs index d0a5c7a3e..e644fe701 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..6e79f3f4c 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/Tables/Tables.cs b/Projects/UOContent/Items/Construction/Tables/Tables.cs index b7602efaa..ee670cced 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..351fbda2e 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..d1178ad79 100644 --- a/Projects/UOContent/Items/Containers/Container.cs +++ b/Projects/UOContent/Items/Containers/Container.cs @@ -269,13 +269,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) { @@ -352,7 +360,11 @@ public partial class PicnicBasket : BaseContainer public partial class Basket : BaseContainer { [Constructible] - public Basket() : base(0x990) => Weight = 1.0; + public Basket() : base(0x990) + { + } + + public override double DefaultWeight => 1.0; } [Furniture] @@ -388,7 +400,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] 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/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/Food/Asian.cs b/Projects/UOContent/Items/Food/Asian.cs index b80e5f2d9..e050450ab 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)] diff --git a/Projects/UOContent/Items/Food/Beverage.cs b/Projects/UOContent/Items/Food/Beverage.cs index 6398fffc3..f1a8cae29 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 diff --git a/Projects/UOContent/Items/Food/Bowls.cs b/Projects/UOContent/Items/Food/Bowls.cs index f080b6d62..0ececb679 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)] diff --git a/Projects/UOContent/Items/Food/CookableFood.cs b/Projects/UOContent/Items/Food/CookableFood.cs index a1515173c..7b2dbad62 100644 --- a/Projects/UOContent/Items/Food/CookableFood.cs +++ b/Projects/UOContent/Items/Food/CookableFood.cs @@ -128,7 +128,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 +143,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 +158,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 +173,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 +188,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 +204,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 +219,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 +234,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 @@ -261,7 +293,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 +306,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..71eac6dbd 100644 --- a/Projects/UOContent/Items/Food/Cooking.cs +++ b/Projects/UOContent/Items/Food/Cooking.cs @@ -44,14 +44,22 @@ public partial class JarHoney : Item 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")] 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/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/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/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/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/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/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/Rares.cs b/Projects/UOContent/Items/Misc/Rares.cs index 2a23a7cc7..a5a0c6201 100644 --- a/Projects/UOContent/Items/Misc/Rares.cs +++ b/Projects/UOContent/Items/Misc/Rares.cs @@ -128,82 +128,130 @@ public partial class ForgedMetal : Item 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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/Weapons/Knives/ButcherKnife.cs b/Projects/UOContent/Items/Weapons/Knives/ButcherKnife.cs index 19743996b..d9c9f957f 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/Dagger.cs b/Projects/UOContent/Items/Weapons/Knives/Dagger.cs index fadcb7daf..8daa9d216 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..c47c45a8c 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/Maces/MagicWand.cs b/Projects/UOContent/Items/Weapons/Maces/MagicWand.cs index 31e92e79f..76549be8f 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/Swords/CrescentBlade.cs b/Projects/UOContent/Items/Weapons/Swords/CrescentBlade.cs index 7bb7f1470..5853f4a48 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/ThinLongsword.cs b/Projects/UOContent/Items/Weapons/Swords/ThinLongsword.cs index 0e17985ca..6d0a4a97a 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/Multis/Camps/BaseCamp.cs b/Projects/UOContent/Multis/Camps/BaseCamp.cs index cd0354859..3b9cece2a 100644 --- a/Projects/UOContent/Multis/Camps/BaseCamp.cs +++ b/Projects/UOContent/Multis/Camps/BaseCamp.cs @@ -176,5 +176,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; } From 2c5441731ea46651e55df2c02a3e6e19541ee937 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Thu, 24 Jul 2025 15:41:44 -0700 Subject: [PATCH 14/47] fix: Optimizes items to use default weights. (Part 2) (#2241) --- .../Aquarium/Rewards/WaterloggedBoots.cs | 15 +- .../UOContent/Items/Armor/Bone/BoneArms.cs | 6 +- .../UOContent/Items/Armor/Bone/BoneChest.cs | 6 +- .../UOContent/Items/Armor/Bone/BoneGloves.cs | 6 +- .../UOContent/Items/Armor/Bone/BoneLegs.cs | 6 +- .../UOContent/Items/Armor/Chain/ChainChest.cs | 6 +- .../Items/Armor/Chain/ChainHatsuburi.cs | 6 +- .../UOContent/Items/Armor/Chain/ChainLegs.cs | 6 +- .../Armor/Cloth/GargishClothArmsType1.cs | 6 +- .../Armor/Cloth/GargishClothArmsType2.cs | 6 +- .../Armor/Cloth/GargishClothChestType1.cs | 6 +- .../Armor/Cloth/GargishClothChestType2.cs | 6 +- .../Armor/Cloth/GargishClothKiltType1.cs | 6 +- .../Armor/Cloth/GargishClothKiltType2.cs | 6 +- .../Armor/Cloth/GargishClothLegsType1.cs | 6 +- .../Armor/Cloth/GargishClothLegsType2.cs | 6 +- .../Items/Armor/Dragon/DragonArms.cs | 6 +- .../Items/Armor/Dragon/DragonChest.cs | 6 +- .../Items/Armor/Dragon/DragonGloves.cs | 6 +- .../Items/Armor/Dragon/DragonHelm.cs | 6 +- .../Items/Armor/Dragon/DragonLegs.cs | 6 +- .../UOContent/Items/Armor/Helmets/Bascinet.cs | 6 +- .../UOContent/Items/Armor/Helmets/BoneHelm.cs | 6 +- .../Items/Armor/Helmets/ChainCoif.cs | 6 +- .../UOContent/Items/Armor/Helmets/Circlet.cs | 6 +- .../Items/Armor/Helmets/CloseHelm.cs | 6 +- .../Items/Armor/Helmets/GemmedCirclet.cs | 6 +- .../UOContent/Items/Armor/Helmets/Helmet.cs | 6 +- .../Items/Armor/Helmets/LeatherCap.cs | 6 +- .../Items/Armor/Helmets/NorseHelm.cs | 6 +- .../Items/Armor/Helmets/PlateHelm.cs | 6 +- .../Items/Armor/Helmets/RavenHelm.cs | 6 +- .../Items/Armor/Helmets/RoyalCirclet.cs | 6 +- .../Items/Armor/Helmets/VultureHelm.cs | 6 +- .../Items/Armor/Helmets/WingedHelm.cs | 6 +- .../Items/Armor/Leather/FemaleLeafChest.cs | 6 +- .../Items/Armor/Leather/FemaleLeatherChest.cs | 6 +- .../Armor/Leather/GargishLeatherArmsType1.cs | 6 +- .../Armor/Leather/GargishLeatherArmsType2.cs | 6 +- .../Armor/Leather/GargishLeatherChestType1.cs | 6 +- .../Armor/Leather/GargishLeatherChestType2.cs | 6 +- .../Armor/Leather/GargishLeatherKiltType1.cs | 6 +- .../Armor/Leather/GargishLeatherKiltType2.cs | 6 +- .../Armor/Leather/GargishLeatherLegsType1.cs | 6 +- .../Armor/Leather/GargishLeatherLegsType2.cs | 6 +- .../Armor/Leather/GargishLeatherWingArmor.cs | 6 +- .../UOContent/Items/Armor/Leather/LeafArms.cs | 6 +- .../Items/Armor/Leather/LeafChest.cs | 6 +- .../Items/Armor/Leather/LeafGloves.cs | 6 +- .../Items/Armor/Leather/LeafGorget.cs | 6 +- .../UOContent/Items/Armor/Leather/LeafLegs.cs | 6 +- .../Items/Armor/Leather/LeafTonlet.cs | 6 +- .../Items/Armor/Leather/LeatherArms.cs | 6 +- .../Items/Armor/Leather/LeatherBustierArms.cs | 6 +- .../Items/Armor/Leather/LeatherChest.cs | 6 +- .../Items/Armor/Leather/LeatherDo.cs | 6 +- .../Items/Armor/Leather/LeatherGloves.cs | 6 +- .../Items/Armor/Leather/LeatherGorget.cs | 6 +- .../Items/Armor/Leather/LeatherHaidate.cs | 6 +- .../Items/Armor/Leather/LeatherHiroSode.cs | 6 +- .../Items/Armor/Leather/LeatherJingasa.cs | 6 +- .../Items/Armor/Leather/LeatherLegs.cs | 6 +- .../Items/Armor/Leather/LeatherMempo.cs | 6 +- .../Items/Armor/Leather/LeatherNinjaHood.cs | 6 +- .../Items/Armor/Leather/LeatherNinjaJacket.cs | 6 +- .../Items/Armor/Leather/LeatherNinjaMitts.cs | 6 +- .../Items/Armor/Leather/LeatherNinjaPants.cs | 6 +- .../Items/Armor/Leather/LeatherShorts.cs | 6 +- .../Items/Armor/Leather/LeatherSkirt.cs | 6 +- .../Items/Armor/Leather/LeatherSuneate.cs | 6 +- .../Armor/Plate/DecorativePlateKabuto.cs | 6 +- .../Items/Armor/Plate/FemalePlateChest.cs | 6 +- .../Items/Armor/Plate/FemaleWoodlandChest.cs | 6 +- .../Items/Armor/Plate/HeavyPlateJingasa.cs | 6 +- .../Items/Armor/Plate/LightPlateJingasa.cs | 6 +- .../UOContent/Items/Armor/Plate/PlateArms.cs | 6 +- .../Items/Armor/Plate/PlateBattleKabuto.cs | 6 +- .../UOContent/Items/Armor/Plate/PlateChest.cs | 6 +- .../UOContent/Items/Armor/Plate/PlateDo.cs | 6 +- .../Items/Armor/Plate/PlateGloves.cs | 6 +- .../Items/Armor/Plate/PlateGorget.cs | 6 +- .../Items/Armor/Plate/PlateHaidate.cs | 6 +- .../Items/Armor/Plate/PlateHatsuburi.cs | 6 +- .../Items/Armor/Plate/PlateHiroSode.cs | 6 +- .../UOContent/Items/Armor/Plate/PlateLegs.cs | 6 +- .../UOContent/Items/Armor/Plate/PlateMempo.cs | 6 +- .../Items/Armor/Plate/PlateSuneate.cs | 6 +- .../Items/Armor/Plate/SmallPlateJingasa.cs | 6 +- .../Items/Armor/Plate/StandardPlateKabuto.cs | 6 +- .../Items/Armor/Plate/WoodlandArms.cs | 6 +- .../Items/Armor/Plate/WoodlandChest.cs | 6 +- .../Items/Armor/Plate/WoodlandGloves.cs | 6 +- .../Items/Armor/Plate/WoodlandLegs.cs | 6 +- .../Items/Armor/Ring/RingmailArms.cs | 6 +- .../Items/Armor/Ring/RingmailChest.cs | 6 +- .../Items/Armor/Ring/RingmailGloves.cs | 6 +- .../Items/Armor/Ring/RingmailLegs.cs | 6 +- .../Armor/Stone/GargishStoneArmsType1.cs | 6 +- .../Armor/Stone/GargishStoneArmsType2.cs | 6 +- .../Armor/Stone/GargishStoneChestType1.cs | 6 +- .../Armor/Stone/GargishStoneChestType2.cs | 6 +- .../Armor/Stone/GargishStoneKiltType1.cs | 6 +- .../Armor/Stone/GargishStoneKiltType2.cs | 6 +- .../Armor/Stone/GargishStoneLegsType1.cs | 6 +- .../Armor/Stone/GargishStoneLegsType2.cs | 6 +- .../Items/Armor/Studded/FemaleStuddedChest.cs | 6 +- .../Armor/Studded/GargishStuddedArmsType1.cs | 6 +- .../Armor/Studded/GargishStuddedArmsType2.cs | 6 +- .../Armor/Studded/GargishStuddedChestType1.cs | 6 +- .../Armor/Studded/GargishStuddedChestType2.cs | 6 +- .../Armor/Studded/GargishStuddedKiltType1.cs | 6 +- .../Armor/Studded/GargishStuddedKiltType2.cs | 6 +- .../Armor/Studded/GargishStuddedLegsType1.cs | 6 +- .../Armor/Studded/GargishStuddedLegsType2.cs | 6 +- .../Items/Armor/Studded/HideChest.cs | 6 +- .../Items/Armor/Studded/HideFemaleChest.cs | 6 +- .../Items/Armor/Studded/HideGloves.cs | 6 +- .../Items/Armor/Studded/HideGorget.cs | 6 +- .../Items/Armor/Studded/HidePants.cs | 6 +- .../Items/Armor/Studded/HidePauldrons.cs | 6 +- .../Items/Armor/Studded/StuddedArms.cs | 6 +- .../Items/Armor/Studded/StuddedBustierArms.cs | 6 +- .../Items/Armor/Studded/StuddedChest.cs | 6 +- .../Items/Armor/Studded/StuddedDo.cs | 6 +- .../Items/Armor/Studded/StuddedGloves.cs | 6 +- .../Items/Armor/Studded/StuddedGorget.cs | 6 +- .../Items/Armor/Studded/StuddedHaidate.cs | 6 +- .../Items/Armor/Studded/StuddedHiroSode.cs | 6 +- .../Items/Armor/Studded/StuddedLegs.cs | 6 +- .../Items/Armor/Studded/StuddedMempo.cs | 6 +- .../Items/Armor/Studded/StuddedSuneate.cs | 6 +- Projects/UOContent/Items/Body Parts/Torso.cs | 6 +- .../Decorative/SkullPole.cs | 6 +- Projects/UOContent/Items/Clothing/Cloaks.cs | 12 +- Projects/UOContent/Items/Clothing/Hats.cs | 132 +++++++++++------- .../UOContent/Items/Clothing/MiddleTorso.cs | 48 +++++-- .../UOContent/Items/Clothing/OuterLegs.cs | 24 +++- .../UOContent/Items/Clothing/OuterTorso.cs | 60 ++++++-- Projects/UOContent/Items/Clothing/Pants.cs | 24 +++- Projects/UOContent/Items/Clothing/Shirts.cs | 24 +++- Projects/UOContent/Items/Clothing/Shoes.cs | 54 +++++-- Projects/UOContent/Items/Clothing/Waist.cs | 18 ++- .../Items/Construction/Chairs/Benchs.cs | 6 +- .../Items/Construction/Chairs/Chairs.cs | 36 +++-- .../Items/Construction/Chairs/Stools.cs | 12 +- .../Items/Construction/Chairs/Thrones.cs | 12 +- .../Decorative/GiantReplicaAcorn.cs | 6 +- .../Decorative/MountedDreadHorn.cs | 6 +- .../Items/Construction/Misc/BarrelParts.cs | 24 +++- .../Items/Construction/Misc/Easel.cs | 6 +- .../Items/Construction/Misc/MusicStand.cs | 12 +- .../Items/Construction/Misc/Screens.cs | 12 +- .../Items/Construction/Misc/Statues.cs | 72 ++++++++-- .../UOContent/Items/Construction/Misc/Vase.cs | 18 ++- .../Items/Construction/Tables/ElvenPodium.cs | 6 +- .../Items/Construction/Tables/Tables.cs | 30 ++-- .../Items/Construction/Tables/WritingTable.cs | 6 +- .../UOContent/Items/Containers/Container.cs | 48 +++++-- .../BaseDecorationArtifact.cs | 12 +- Projects/UOContent/Items/Food/Asian.cs | 12 +- Projects/UOContent/Items/Food/Beverage.cs | 10 +- .../UOContent/Items/Food/BeverageEmpty.cs | 12 +- Projects/UOContent/Items/Food/Bowls.cs | 12 +- Projects/UOContent/Items/Food/Cooking.cs | 6 +- Projects/UOContent/Items/Food/Food.cs | 6 +- Projects/UOContent/Items/Jewels/Bracelet.cs | 12 +- Projects/UOContent/Items/Jewels/Earrings.cs | 12 +- Projects/UOContent/Items/Jewels/Necklace.cs | 30 +++- Projects/UOContent/Items/Jewels/Ring.cs | 12 +- Projects/UOContent/Items/Misc/Firebomb.cs | 8 +- .../Items/Misc/PlayerBulletinBoards.cs | 12 +- Projects/UOContent/Items/Misc/Rares.cs | 12 +- Projects/UOContent/Items/Misc/Scales.cs | 6 +- .../Items/PlantsFlowers/Potted/EmptyPots.cs | 12 +- .../PlantsFlowers/Potted/PottedCactus.cs | 36 ++++- .../PlantsFlowers/Potted/PottedPlants.cs | 18 ++- .../Items/PlantsFlowers/Potted/PottedTrees.cs | 12 +- .../UOContent/Items/Shields/BronzeShield.cs | 6 +- Projects/UOContent/Items/Shields/Buckler.cs | 6 +- .../Items/Shields/GargishWoodenShield.cs | 6 +- .../UOContent/Items/Shields/HeaterShield.cs | 6 +- .../Items/Shields/MetalKiteShield.cs | 6 +- .../UOContent/Items/Shields/MetalShield.cs | 6 +- .../Items/Shields/WoodenKiteShield.cs | 6 +- .../UOContent/Items/Shields/WoodenShield.cs | 6 +- .../Items/Skill Items/Camping/Bedroll.cs | 6 +- .../Items/Skill Items/Fishing/Misc/Sextant.cs | 6 +- .../Items/Skill Items/Harvest Tools/Shovel.cs | 6 +- .../Musical Instruments/BambooFlute.cs | 6 +- .../Skill Items/Musical Instruments/Drums.cs | 6 +- .../Skill Items/Musical Instruments/Harp.cs | 6 +- .../Musical Instruments/LapHarp.cs | 6 +- .../Skill Items/Musical Instruments/Lute.cs | 6 +- .../Tailor Items/Misc/Dressform.cs | 6 +- .../Skill Items/Tailor Items/Misc/Dyes.cs | 6 +- .../Items/Skill Items/Tinkering/Globe.cs | 6 +- .../Items/Skill Items/Tinkering/Spyglass.cs | 6 +- .../Items/Skill Items/Tools/DovetailSaw.cs | 10 +- .../Items/Skill Items/Tools/FletcherTools.cs | 10 +- .../Items/Skill Items/Tools/Hammer.cs | 10 +- .../Items/Skill Items/Tools/JointingPlane.cs | 10 +- .../Items/Skill Items/Tools/MouldingPlane.cs | 10 +- .../Items/Skill Items/Tools/Nails.cs | 10 +- .../UOContent/Items/Skill Items/Tools/Saw.cs | 10 +- .../Items/Skill Items/Tools/SewingKit.cs | 10 +- .../Items/Skill Items/Tools/Tongs.cs | 10 +- .../Blacksmithy/GlovesOfMining.cs | 18 ++- .../Special/Veteran Rewards/PottedCactus.cs | 6 +- Projects/UOContent/Items/Weapons/Axes/Axe.cs | 6 +- .../UOContent/Items/Weapons/Axes/DoubleAxe.cs | 6 +- .../Items/Weapons/Axes/ExecutionersAxe.cs | 6 +- .../UOContent/Items/Weapons/Axes/Hatchet.cs | 6 +- .../Items/Weapons/Axes/LargeBattleAxe.cs | 6 +- .../Items/Weapons/Axes/TwoHandedAxe.cs | 6 +- .../UOContent/Items/Weapons/Axes/WarAxe.cs | 6 +- .../Items/Weapons/Knives/ButcherKnife.cs | 6 +- .../UOContent/Items/Weapons/Knives/Cleaver.cs | 6 +- .../UOContent/Items/Weapons/Knives/Dagger.cs | 6 +- .../Items/Weapons/Knives/SkinningKnife.cs | 6 +- .../Items/Weapons/ML Weapons/AssassinSpike.cs | 6 +- .../Items/Weapons/ML Weapons/DiamondMace.cs | 6 +- .../Items/Weapons/ML Weapons/ElvenMachete.cs | 6 +- .../Items/Weapons/ML Weapons/Leafblade.cs | 6 +- .../Weapons/ML Weapons/RadiantScimitar.cs | 6 +- .../Items/Weapons/ML Weapons/WarCleaver.cs | 6 +- .../UOContent/Items/Weapons/Maces/Club.cs | 6 +- .../UOContent/Items/Weapons/Maces/Mace.cs | 6 +- .../Items/Weapons/Maces/MagicWand.cs | 6 +- .../UOContent/Items/Weapons/Maces/Maul.cs | 6 +- .../UOContent/Items/Weapons/Maces/Scepter.cs | 6 +- .../UOContent/Items/Weapons/Maces/WarMace.cs | 6 +- .../Items/Weapons/PoleArms/Bardiche.cs | 6 +- .../Items/Weapons/PoleArms/Halberd.cs | 6 +- .../Items/Weapons/PoleArms/Scythe.cs | 6 +- .../Items/Weapons/SE Weapons/Bokuto.cs | 6 +- .../Items/Weapons/SE Weapons/Nunchaku.cs | 6 +- .../Weapons/SpearsAndForks/BladedStaff.cs | 6 +- .../SpearsAndForks/DoubleBladedStaff.cs | 6 +- .../Items/Weapons/SpearsAndForks/Pike.cs | 6 +- .../Items/Weapons/SpearsAndForks/Pitchfork.cs | 6 +- .../Weapons/SpearsAndForks/ShortSpear.cs | 6 +- .../Items/Weapons/SpearsAndForks/Spear.cs | 6 +- .../Items/Weapons/SpearsAndForks/WarFork.cs | 6 +- .../Items/Weapons/Staves/BlackStaff.cs | 6 +- .../Items/Weapons/Swords/BoneHarvester.cs | 6 +- .../Items/Weapons/Swords/Broadsword.cs | 6 +- .../Items/Weapons/Swords/CrescentBlade.cs | 6 +- .../UOContent/Items/Weapons/Swords/Cutlass.cs | 6 +- .../Items/Weapons/Swords/GlassSword.cs | 6 +- .../UOContent/Items/Weapons/Swords/Katana.cs | 6 +- .../UOContent/Items/Weapons/Swords/Kryss.cs | 6 +- .../UOContent/Items/Weapons/Swords/Lance.cs | 6 +- .../Items/Weapons/Swords/Longsword.cs | 6 +- .../Items/Weapons/Swords/Scimitar.cs | 6 +- .../Items/Weapons/Swords/ThinLongsword.cs | 6 +- .../Items/Weapons/Swords/VikingSword.cs | 6 +- 256 files changed, 1801 insertions(+), 488 deletions(-) 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/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/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 cb76d0e39..6e736bacc 100644 --- a/Projects/UOContent/Items/Armor/Helmets/ChainCoif.cs +++ b/Projects/UOContent/Items/Armor/Helmets/ChainCoif.cs @@ -8,10 +8,10 @@ namespace Server.Items { [Constructible] public ChainCoif() : base(0x13BB) - { - } + { + } - public override double DefaultWeight => 1.0; + 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/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 7b13c2075..323311f20 100644 --- a/Projects/UOContent/Items/Armor/Leather/FemaleLeatherChest.cs +++ b/Projects/UOContent/Items/Armor/Leather/FemaleLeatherChest.cs @@ -8,10 +8,10 @@ namespace Server.Items { [Constructible] public FemaleLeatherChest() : base(0x1C06) - { - } + { + } - public override double DefaultWeight => 1.0; + 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 4dc7c3dde..062c54aad 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherBustierArms.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherBustierArms.cs @@ -8,10 +8,10 @@ namespace Server.Items { [Constructible] public LeatherBustierArms() : base(0x1C0A) - { - } + { + } - public override double DefaultWeight => 1.0; + 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 28517f5db..a1ded151b 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherGloves.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherGloves.cs @@ -8,10 +8,10 @@ namespace Server.Items { [Constructible] public LeatherGloves() : base(0x13C6) - { - } + { + } - public override double DefaultWeight => 1.0; + 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 7e902ae60..0e7aca13b 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherGorget.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherGorget.cs @@ -7,10 +7,10 @@ namespace Server.Items { [Constructible] public LeatherGorget() : base(0x13C7) - { - } + { + } - public override double DefaultWeight => 1.0; + 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 40fb5885f..b698cab6a 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherHiroSode.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherHiroSode.cs @@ -7,10 +7,10 @@ namespace Server.Items { [Constructible] public LeatherHiroSode() : base(0x277E) - { - } + { + } - public override double DefaultWeight => 1.0; + 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 ea47347e2..827dcabaa 100644 --- a/Projects/UOContent/Items/Armor/Leather/LeatherSkirt.cs +++ b/Projects/UOContent/Items/Armor/Leather/LeatherSkirt.cs @@ -8,10 +8,10 @@ namespace Server.Items { [Constructible] public LeatherSkirt() : base(0x1C08) - { - } + { + } - public override double DefaultWeight => 1.0; + 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/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 eff01f1f1..9d0411665 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedBustierArms.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedBustierArms.cs @@ -8,10 +8,10 @@ namespace Server.Items { [Constructible] public StuddedBustierArms() : base(0x1C0C) - { - } + { + } - public override double DefaultWeight => 1.0; + 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 e7e78da92..f6a6052d2 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedGloves.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedGloves.cs @@ -8,10 +8,10 @@ namespace Server.Items { [Constructible] public StuddedGloves() : base(0x13D5) - { - } + { + } - public override double DefaultWeight => 1.0; + 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 cd5af7c1b..13d395382 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedGorget.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedGorget.cs @@ -7,10 +7,10 @@ namespace Server.Items { [Constructible] public StuddedGorget() : base(0x13D6) - { - } + { + } - public override double DefaultWeight => 1.0; + 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 c934b728a..a8677a31f 100644 --- a/Projects/UOContent/Items/Armor/Studded/StuddedHiroSode.cs +++ b/Projects/UOContent/Items/Armor/Studded/StuddedHiroSode.cs @@ -7,10 +7,10 @@ namespace Server.Items { [Constructible] public StuddedHiroSode() : base(0x277F) - { - } + { + } - public override double DefaultWeight => 1.0; + 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/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/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..caa255c72 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)] @@ -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 f6d5b0c49..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; @@ -97,10 +105,10 @@ namespace Server.Items { [Constructible] public FlowerGarland(int hue = 0) : base(0x2306, hue) - { - } + { + } - public override double DefaultWeight => 1.0; + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 3; public override int BaseFireResistance => 3; @@ -117,10 +125,10 @@ namespace Server.Items { [Constructible] public FloppyHat(int hue = 0) : base(0x1713, hue) - { - } + { + } - public override double DefaultWeight => 1.0; + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -137,10 +145,10 @@ namespace Server.Items { [Constructible] public WideBrimHat(int hue = 0) : base(0x1714, hue) - { - } + { + } - public override double DefaultWeight => 1.0; + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -157,10 +165,10 @@ namespace Server.Items { [Constructible] public Cap(int hue = 0) : base(0x1715, hue) - { - } + { + } - public override double DefaultWeight => 1.0; + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -177,10 +185,10 @@ namespace Server.Items { [Constructible] public SkullCap(int hue = 0) : base(0x1544, hue) - { - } + { + } - public override double DefaultWeight => 1.0; + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 3; @@ -197,10 +205,10 @@ namespace Server.Items { [Constructible] public Bandana(int hue = 0) : base(0x1540, hue) - { - } + { + } - public override double DefaultWeight => 1.0; + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 3; @@ -216,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; @@ -238,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; @@ -260,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; @@ -282,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; @@ -305,10 +329,10 @@ namespace Server.Items { [Constructible] public TallStrawHat(int hue = 0) : base(0x1716, hue) - { - } + { + } - public override double DefaultWeight => 1.0; + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -325,10 +349,10 @@ namespace Server.Items { [Constructible] public StrawHat(int hue = 0) : base(0x1717, hue) - { - } + { + } - public override double DefaultWeight => 1.0; + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -344,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; @@ -399,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; @@ -434,10 +466,10 @@ namespace Server.Items { [Constructible] public WizardsHat(int hue = 0) : base(0x1718, hue) - { - } + { + } - public override double DefaultWeight => 1.0; + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -454,10 +486,10 @@ namespace Server.Items { [Constructible] public MagicWizardsHat(int hue = 0) : base(0x1718, hue) - { - } + { + } - public override double DefaultWeight => 1.0; + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -480,10 +512,10 @@ namespace Server.Items { [Constructible] public Bonnet(int hue = 0) : base(0x1719, hue) - { - } + { + } - public override double DefaultWeight => 1.0; + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -500,10 +532,10 @@ namespace Server.Items { [Constructible] public FeatheredHat(int hue = 0) : base(0x171A, hue) - { - } + { + } - public override double DefaultWeight => 1.0; + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -520,10 +552,10 @@ namespace Server.Items { [Constructible] public TricorneHat(int hue = 0) : base(0x171B, hue) - { - } + { + } - public override double DefaultWeight => 1.0; + public override double DefaultWeight => 1.0; public override int BasePhysicalResistance => 0; public override int BaseFireResistance => 5; @@ -540,10 +572,10 @@ namespace Server.Items { [Constructible] public JesterHat(int hue = 0) : base(0x171C, hue) - { - } + { + } - public override double DefaultWeight => 1.0; + 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 0003b3625..31299b316 100644 --- a/Projects/UOContent/Items/Clothing/MiddleTorso.cs +++ b/Projects/UOContent/Items/Clothing/MiddleTorso.cs @@ -16,10 +16,10 @@ namespace Server.Items { [Constructible] public BodySash(int hue = 0) : base(0x1541, hue) - { - } + { + } - public override double DefaultWeight => 1.0; + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -27,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)] @@ -35,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)] @@ -43,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)] @@ -51,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)] @@ -60,10 +76,10 @@ namespace Server.Items { [Constructible] public FormalShirt(int hue = 0) : base(0x2310, hue) - { - } + { + } - public override double DefaultWeight => 1.0; + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -71,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)] @@ -79,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..9b4924b23 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)] @@ -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)] @@ -427,7 +439,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 +451,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 +463,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 +475,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 +487,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 +499,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 +511,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 899d307f1..9ebe7a73f 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)] @@ -24,10 +28,10 @@ namespace Server.Items { [Constructible] public Shirt(int hue = 0) : base(0x1517, hue) - { - } + { + } - public override double DefaultWeight => 1.0; + public override double DefaultWeight => 1.0; } [Flippable(0x2794, 0x27DF)] @@ -46,7 +50,11 @@ namespace Server.Items 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; } @@ -55,6 +63,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 167a3fbb7..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; } @@ -154,10 +170,10 @@ namespace Server.Items { [Constructible] public Sandals(int hue = 0) : base(0x170D, hue) - { - } + { + } - public override double DefaultWeight => 1.0; + public override double DefaultWeight => 1.0; public override CraftResource DefaultResource => CraftResource.RegularLeather; @@ -169,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)] @@ -177,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)] @@ -185,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)] @@ -193,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 6920e1866..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)] @@ -24,10 +28,10 @@ namespace Server.Items { [Constructible] public Obi(int hue = 0) : base(0x27A0, hue) - { - } + { + } - public override double DefaultWeight => 1.0; + public override double DefaultWeight => 1.0; } [Flippable(0x2B68, 0x315F)] @@ -35,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 f9034a1e8..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] @@ -54,10 +74,10 @@ namespace Server.Items { [Constructible] public OrnateElvenChair() : base(0x2DE3) - { - } + { + } - public override double DefaultWeight => 1.0; + 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 7ecf175c6..5bb86a2d2 100644 --- a/Projects/UOContent/Items/Construction/Chairs/Thrones.cs +++ b/Projects/UOContent/Items/Construction/Chairs/Thrones.cs @@ -9,10 +9,10 @@ namespace Server.Items { [Constructible] public Throne() : base(0xB33) - { - } + { + } - public override double DefaultWeight => 1.0; + public override double DefaultWeight => 1.0; } [Furniture] @@ -21,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 e644fe701..4382653fc 100644 --- a/Projects/UOContent/Items/Construction/Decorative/GiantReplicaAcorn.cs +++ b/Projects/UOContent/Items/Construction/Decorative/GiantReplicaAcorn.cs @@ -8,10 +8,10 @@ namespace Server.Items { [Constructible] public GiantReplicaAcorn() : base(0x2D4A) - { - } + { + } - public override double DefaultWeight => 1.0; + 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 6e79f3f4c..4c3f11433 100644 --- a/Projects/UOContent/Items/Construction/Decorative/MountedDreadHorn.cs +++ b/Projects/UOContent/Items/Construction/Decorative/MountedDreadHorn.cs @@ -8,10 +8,10 @@ namespace Server.Items { [Constructible] public MountedDreadHorn() : base(0x3158) - { - } + { + } - public override double DefaultWeight => 1.0; + 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/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 ee670cced..0b2f17f95 100644 --- a/Projects/UOContent/Items/Construction/Tables/Tables.cs +++ b/Projects/UOContent/Items/Construction/Tables/Tables.cs @@ -8,10 +8,10 @@ namespace Server.Items { [Constructible] public ElegantLowTable() : base(0x2819) - { - } + { + } - public override double DefaultWeight => 1.0; + public override double DefaultWeight => 1.0; } [Furniture] @@ -20,10 +20,10 @@ namespace Server.Items { [Constructible] public PlainLowTable() : base(0x281A) - { - } + { + } - public override double DefaultWeight => 1.0; + public override double DefaultWeight => 1.0; } [Furniture] @@ -33,10 +33,10 @@ namespace Server.Items { [Constructible] public LargeTable() : base(0xB90) - { - } + { + } - public override double DefaultWeight => 1.0; + public override double DefaultWeight => 1.0; } [Furniture] @@ -46,10 +46,10 @@ namespace Server.Items { [Constructible] public Nightstand() : base(0xB35) - { - } + { + } - public override double DefaultWeight => 1.0; + public override double DefaultWeight => 1.0; } [Furniture] @@ -59,9 +59,9 @@ namespace Server.Items { [Constructible] public YewWoodTable() : base(0xB8F) - { - } + { + } - public override double DefaultWeight => 1.0; + 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 351fbda2e..822f145a2 100644 --- a/Projects/UOContent/Items/Construction/Tables/WritingTable.cs +++ b/Projects/UOContent/Items/Construction/Tables/WritingTable.cs @@ -9,9 +9,9 @@ namespace Server.Items { [Constructible] public WritingTable() : base(0xB4A) - { - } + { + } - public override double DefaultWeight => 1.0; + public override double DefaultWeight => 1.0; } } diff --git a/Projects/UOContent/Items/Containers/Container.cs b/Projects/UOContent/Items/Containers/Container.cs index d1178ad79..2822d8b64 100644 --- a/Projects/UOContent/Items/Containers/Container.cs +++ b/Projects/UOContent/Items/Containers/Container.cs @@ -320,7 +320,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) { @@ -339,21 +343,33 @@ 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)] @@ -373,7 +389,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] @@ -382,7 +402,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] @@ -391,7 +415,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] @@ -445,7 +473,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/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/Food/Asian.cs b/Projects/UOContent/Items/Food/Asian.cs index e050450ab..2b9a66101 100644 --- a/Projects/UOContent/Items/Food/Asian.cs +++ b/Projects/UOContent/Items/Food/Asian.cs @@ -29,7 +29,11 @@ public partial class WasabiClumps : Food public partial class EmptyBentoBox : Item { [Constructible] - public EmptyBentoBox() : base(0x2834) => Weight = 5.0; + public EmptyBentoBox() : base(0x2834) + { + } + + public override double DefaultWeight => 5.0; } @@ -84,7 +88,11 @@ public partial class SushiPlatter : Food 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)] diff --git a/Projects/UOContent/Items/Food/Beverage.cs b/Projects/UOContent/Items/Food/Beverage.cs index f1a8cae29..79b7f62fe 100644 --- a/Projects/UOContent/Items/Food/Beverage.cs +++ b/Projects/UOContent/Items/Food/Beverage.cs @@ -208,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 0ececb679..e26c4b72b 100644 --- a/Projects/UOContent/Items/Food/Bowls.cs +++ b/Projects/UOContent/Items/Food/Bowls.cs @@ -236,7 +236,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")] @@ -244,7 +248,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)] diff --git a/Projects/UOContent/Items/Food/Cooking.cs b/Projects/UOContent/Items/Food/Cooking.cs index 71eac6dbd..d45db3b94 100644 --- a/Projects/UOContent/Items/Food/Cooking.cs +++ b/Projects/UOContent/Items/Food/Cooking.cs @@ -108,7 +108,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)] diff --git a/Projects/UOContent/Items/Food/Food.cs b/Projects/UOContent/Items/Food/Food.cs index 35c3b4e5b..130b137d9 100644 --- a/Projects/UOContent/Items/Food/Food.cs +++ b/Projects/UOContent/Items/Food/Food.cs @@ -507,5 +507,9 @@ public partial class Turnip : Food 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/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/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/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/Rares.cs b/Projects/UOContent/Items/Misc/Rares.cs index a5a0c6201..bd9cf00c9 100644 --- a/Projects/UOContent/Items/Misc/Rares.cs +++ b/Projects/UOContent/Items/Misc/Rares.cs @@ -114,14 +114,22 @@ public partial class DriedHerbs : Item 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)] 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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/Veteran Rewards/PottedCactus.cs b/Projects/UOContent/Items/Special/Veteran Rewards/PottedCactus.cs index cbafbc7b0..e208359a1 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; } 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/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/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 d9c9f957f..4cfb3fe40 100644 --- a/Projects/UOContent/Items/Weapons/Knives/ButcherKnife.cs +++ b/Projects/UOContent/Items/Weapons/Knives/ButcherKnife.cs @@ -8,10 +8,10 @@ namespace Server.Items { [Constructible] public ButcherKnife() : base(0x13F6) - { - } + { + } - public override double DefaultWeight => 1.0; + 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 8daa9d216..baa4e87b8 100644 --- a/Projects/UOContent/Items/Weapons/Knives/Dagger.cs +++ b/Projects/UOContent/Items/Weapons/Knives/Dagger.cs @@ -8,10 +8,10 @@ namespace Server.Items { [Constructible] public Dagger() : base(0xF52) - { - } + { + } - public override double DefaultWeight => 1.0; + 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 c47c45a8c..5d72aca1d 100644 --- a/Projects/UOContent/Items/Weapons/Knives/SkinningKnife.cs +++ b/Projects/UOContent/Items/Weapons/Knives/SkinningKnife.cs @@ -8,10 +8,10 @@ namespace Server.Items { [Constructible] public SkinningKnife() : base(0xEC4) - { - } + { + } - public override double DefaultWeight => 1.0; + 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/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/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/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/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/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/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/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 76549be8f..c88baa93f 100644 --- a/Projects/UOContent/Items/Weapons/Maces/MagicWand.cs +++ b/Projects/UOContent/Items/Weapons/Maces/MagicWand.cs @@ -7,10 +7,10 @@ namespace Server.Items { [Constructible] public MagicWand() : base(0xDF2) - { - } + { + } - public override double DefaultWeight => 1.0; + 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/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/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/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/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/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/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 5853f4a48..2a12d7c9b 100644 --- a/Projects/UOContent/Items/Weapons/Swords/CrescentBlade.cs +++ b/Projects/UOContent/Items/Weapons/Swords/CrescentBlade.cs @@ -8,10 +8,10 @@ namespace Server.Items { [Constructible] public CrescentBlade() : base(0x26C1) - { - } + { + } - public override double DefaultWeight => 1.0; + 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 6d0a4a97a..b7338ccc9 100644 --- a/Projects/UOContent/Items/Weapons/Swords/ThinLongsword.cs +++ b/Projects/UOContent/Items/Weapons/Swords/ThinLongsword.cs @@ -8,10 +8,10 @@ namespace Server.Items { [Constructible] public ThinLongsword() : base(0x13B8) - { - } + { + } - public override double DefaultWeight => 1.0; + 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; From 40ba85d7b8fd48d55891f615a4336754f17c95f5 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Thu, 24 Jul 2025 16:21:27 -0700 Subject: [PATCH 15/47] fix: Optimizes items to use default weights. (Part 3) (#2242) --- .../Items/Armor/DaemonBone/DaemonArms.cs | 64 ++++++++-------- .../Items/Armor/DaemonBone/DaemonChest.cs | 64 ++++++++-------- .../Items/Armor/DaemonBone/DaemonGloves.cs | 64 ++++++++-------- .../Items/Armor/DaemonBone/DaemonLegs.cs | 64 ++++++++-------- .../Items/Armor/Glasses/ElvenGlasses.cs | 8 +- .../Items/Armor/Helmets/DaemonHelm.cs | 3 +- .../Items/Armor/Ranger/RangerArms.cs | 3 +- .../Items/Armor/Ranger/RangerChest.cs | 3 +- .../Items/Armor/Ranger/RangerGloves.cs | 3 +- .../Items/Armor/Ranger/RangerGorget.cs | 3 +- .../Items/Armor/Ranger/RangerLegs.cs | 3 +- .../UOContent/Items/Body Parts/BonePile.cs | 8 +- .../UOContent/Items/Body Parts/RibCage.cs | 8 +- Projects/UOContent/Items/Clothing/Cloaks.cs | 4 +- .../UOContent/Items/Clothing/OuterTorso.cs | 16 ++-- Projects/UOContent/Items/Clothing/Shirts.cs | 8 +- .../Items/Construction/Misc/Vines.cs | 12 +-- .../UOContent/Items/Containers/Container.cs | 19 ++--- .../UOContent/Items/Containers/SalvageBag.cs | 3 +- .../UOContent/Items/Deeds/BarkeepContract.cs | 3 +- .../Items/Deeds/ClothingBlessDeed.cs | 3 +- .../UOContent/Items/Deeds/CommodityDeed.cs | 3 +- .../Items/Deeds/HairRestylingDeed.cs | 3 +- .../UOContent/Items/Deeds/HolidayTreeDeed.cs | 3 +- .../UOContent/Items/Deeds/NewPlayerTicket.cs | 3 +- .../Items/Deeds/VendorRentalContract.cs | 4 +- Projects/UOContent/Items/Food/Asian.cs | 37 +++++---- Projects/UOContent/Items/Food/Bowls.cs | 33 +++++--- Projects/UOContent/Items/Food/CookableFood.cs | 18 +++-- Projects/UOContent/Items/Food/Cooking.cs | 14 ++-- Projects/UOContent/Items/Food/Food.cs | 76 ++++++++++++------- Projects/UOContent/Items/Food/Fruits.cs | 57 +++++++++----- Projects/UOContent/Items/Games/BaseBoard.cs | 4 +- .../Items/Games/Mahjong/MahjongGame.cs | 4 +- .../UOContent/Items/Guilds/GuildTeleporter.cs | 3 +- Projects/UOContent/Items/Guilds/Guildstone.cs | 4 +- Projects/UOContent/Items/Lights/Brazier.cs | 3 +- .../UOContent/Items/Lights/BrazierTall.cs | 3 +- Projects/UOContent/Items/Lights/Candelabra.cs | 3 +- .../UOContent/Items/Lights/CandelabraStand.cs | 2 +- Projects/UOContent/Items/Lights/Candle.cs | 2 +- .../UOContent/Items/Lights/CandleLarge.cs | 3 +- Projects/UOContent/Items/Lights/CandleLong.cs | 4 +- .../UOContent/Items/Lights/CandleShort.cs | 4 +- .../UOContent/Items/Lights/CandleSkull.cs | 3 +- .../UOContent/Items/Lights/HangingLantern.cs | 2 +- .../UOContent/Items/Lights/HeatingStand.cs | 4 +- Projects/UOContent/Items/Lights/LampPost1.cs | 2 +- Projects/UOContent/Items/Lights/LampPost2.cs | 2 +- Projects/UOContent/Items/Lights/LampPost3.cs | 2 +- Projects/UOContent/Items/Lights/Lantern.cs | 3 +- .../UOContent/Items/Lights/PaperLantern.cs | 2 +- .../Items/Lights/RedHangingLantern.cs | 3 +- .../Items/Lights/RoundPaperLantern.cs | 3 +- .../UOContent/Items/Lights/ShojiLantern.cs | 2 +- Projects/UOContent/Items/Lights/Torch.cs | 3 +- Projects/UOContent/Items/Lights/WallSconce.cs | 3 +- Projects/UOContent/Items/Lights/WallTorch.cs | 3 +- .../Items/Lights/WhiteHangingLantern.cs | 3 +- Projects/UOContent/Items/Maps/MapItem.cs | 3 +- .../Minor Artifacts/ML/RobeOfTheEclipse.cs | 4 +- .../Minor Artifacts/ML/RobeOfTheEquinox.cs | 4 +- Projects/UOContent/Items/Misc/ArcaneGem.cs | 3 +- Projects/UOContent/Items/Misc/BankCheck.cs | 3 +- Projects/UOContent/Items/Misc/Beeswax.cs | 3 +- Projects/UOContent/Items/Misc/Bola.cs | 3 +- Projects/UOContent/Items/Misc/BolaBall.cs | 3 +- .../UOContent/Items/Misc/ClockworkAssembly.cs | 3 +- .../UOContent/Items/Misc/InteriorDecorator.cs | 3 +- Projects/UOContent/Items/Misc/Key.cs | 4 +- Projects/UOContent/Items/Misc/KeyRing.cs | 3 +- .../Items/Misc/Labyrinth/MinotaurArtifact.cs | 7 +- Projects/UOContent/Items/Misc/Moonstone.cs | 8 +- .../UOContent/Items/Misc/PromotionalToken.cs | 3 +- Projects/UOContent/Items/Misc/Rares.cs | 27 ++++--- .../UOContent/Items/Misc/SpecialBeardDye.cs | 8 +- .../UOContent/Items/Misc/SpecialHairDye.cs | 8 +- Projects/UOContent/Items/Misc/TribalBerry.cs | 3 +- Projects/UOContent/Items/Misc/TribalPaint.cs | 3 +- .../UOContent/Items/Quivers/BaseQuiver.cs | 1 - .../UOContent/Items/Resources/Fishing/Fish.cs | 5 +- .../Items/Resources/Tailor/BoltOfCloth.cs | 5 +- .../UOContent/Items/Resources/Tailor/Bone.cs | 5 +- .../Items/Resources/Tailor/Cotton.cs | 3 +- .../UOContent/Items/Resources/Tailor/Hides.cs | 4 +- .../Items/Resources/Tailor/Leathers.cs | 4 +- .../UOContent/Items/Resources/Tailor/Wool.cs | 6 +- .../Items/Resources/Tailor/YarnsAndThreads.cs | 3 +- .../UOContent/Items/Shields/ChaosShield.cs | 4 +- .../UOContent/Items/Shields/OrderShield.cs | 4 +- .../Items/Skill Items/Camping/Kindling.cs | 3 +- .../Items/Skill Items/Fishing/FishingPole.cs | 8 +- .../Fishing/Misc/MessageInABottle.cs | 3 +- .../Items/Skill Items/Fishing/Misc/SOS.cs | 4 +- .../Fishing/Misc/ShipwreckedItem.cs | 11 +-- .../Fishing/Misc/SpecialFishingNet.cs | 8 +- .../Harvest Tools/GargoylesPickaxe.cs | 3 +- .../Harvest Tools/ProspectorsTool.cs | 8 +- .../Harvest Tools/SturdyPickaxe.cs | 3 +- .../Skill Items/Harvest Tools/SturdyShovel.cs | 7 +- .../Items/Special/Gifts/TapestryOfSosaria.cs | 3 +- .../Special/Holiday/GingerBreadHouseDeed.cs | 3 +- .../Items/Special/Holiday/Poinsettias.cs | 6 +- .../Items/Special/Holiday/Snowflakes.cs | 6 +- 104 files changed, 497 insertions(+), 425 deletions(-) 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/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/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/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/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/Clothing/Cloaks.cs b/Projects/UOContent/Items/Clothing/Cloaks.cs index caa255c72..67f572d9d 100644 --- a/Projects/UOContent/Items/Clothing/Cloaks.cs +++ b/Projects/UOContent/Items/Clothing/Cloaks.cs @@ -128,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; diff --git a/Projects/UOContent/Items/Clothing/OuterTorso.cs b/Projects/UOContent/Items/Clothing/OuterTorso.cs index 9b4924b23..4fda45bb5 100644 --- a/Projects/UOContent/Items/Clothing/OuterTorso.cs +++ b/Projects/UOContent/Items/Clothing/OuterTorso.cs @@ -165,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; @@ -247,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; @@ -418,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; diff --git a/Projects/UOContent/Items/Clothing/Shirts.cs b/Projects/UOContent/Items/Clothing/Shirts.cs index 9ebe7a73f..bd174067e 100644 --- a/Projects/UOContent/Items/Clothing/Shirts.cs +++ b/Projects/UOContent/Items/Clothing/Shirts.cs @@ -39,11 +39,9 @@ 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)] 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/Containers/Container.cs b/Projects/UOContent/Items/Containers/Container.cs index 2822d8b64..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 { 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/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/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 2b9a66101..282f35c03 100644 --- a/Projects/UOContent/Items/Food/Asian.cs +++ b/Projects/UOContent/Items/Food/Asian.cs @@ -20,9 +20,10 @@ 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)] @@ -34,7 +35,6 @@ public partial class EmptyBentoBox : Item } public override double DefaultWeight => 5.0; - } [SerializationGenerator(0, false)] @@ -44,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)] @@ -67,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)] @@ -79,9 +70,10 @@ 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)] @@ -102,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)] @@ -114,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)] @@ -126,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)] @@ -138,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)] @@ -150,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/Bowls.cs b/Projects/UOContent/Items/Food/Bowls.cs index e26c4b72b..08b7bb11f 100644 --- a/Projects/UOContent/Items/Food/Bowls.cs +++ b/Projects/UOContent/Items/Food/Bowls.cs @@ -31,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)) @@ -54,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)) @@ -77,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)) @@ -100,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)) @@ -123,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)) @@ -146,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)) @@ -169,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)) @@ -192,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)) @@ -215,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)) @@ -262,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)) @@ -285,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 7b2dbad62..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(); } @@ -251,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(); } @@ -265,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(); } @@ -280,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(); } diff --git a/Projects/UOContent/Items/Food/Cooking.cs b/Projects/UOContent/Items/Food/Cooking.cs index d45db3b94..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,9 +36,10 @@ public partial class JarHoney : Item [Constructible] public JarHoney() : base(0x9ec) { - Weight = 1.0; Stackable = true; } + + public override double DefaultWeight => 1.0; } [SerializationGenerator(0, false)] @@ -69,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 @@ -121,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 130b137d9..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,9 +517,10 @@ 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)] 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/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/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/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/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/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/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 bd9cf00c9..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,9 +113,10 @@ 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)] 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/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/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/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/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/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/ShipwreckedItem.cs b/Projects/UOContent/Items/Skill Items/Fishing/Misc/ShipwreckedItem.cs index d812f761a..c4f7945ac 100644 --- a/Projects/UOContent/Items/Skill Items/Fishing/Misc/ShipwreckedItem.cs +++ b/Projects/UOContent/Items/Skill Items/Fishing/Misc/ShipwreckedItem.cs @@ -1,3 +1,4 @@ +using System; using ModernUO.Serialization; namespace Server.Items; @@ -12,16 +13,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/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/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/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/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; } From fa0b67ec29119f670e933a01159aedc3cd17f338 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Thu, 24 Jul 2025 23:29:29 -0700 Subject: [PATCH 16/47] fix: Optimizes items to use default weights. (Part 4) (#2243) --- .../Items/Skill Items/Lumberjack/Log.cs | 3 ++- .../Skill Items/Magical/Misc/BlankScroll.cs | 3 ++- .../Items/Skill Items/Magical/Misc/Bottle.cs | 3 ++- .../Items/Skill Items/Magical/Misc/PotionKeg.cs | 14 +++++--------- .../Items/Skill Items/Magical/Misc/RecallRune.cs | 3 ++- .../Skill Items/Magical/Potions/BasePotion.cs | 3 ++- .../Items/Skill Items/Magical/Runebook.cs | 3 ++- .../Skill Items/Magical/Scrolls/SpellScroll.cs | 3 ++- .../Items/Skill Items/Magical/Spellbook.cs | 3 ++- .../UOContent/Items/Skill Items/Misc/FireHorn.cs | 8 +++----- .../Items/Skill Items/Ninjitsu/EggBomb.cs | 3 ++- .../Items/Skill Items/Ninjitsu/Fukiya.cs | 8 +++----- .../Items/Skill Items/Ninjitsu/FukiyaDarts.cs | 10 ++-------- .../Skill Items/Ninjitsu/LeatherNinjaBelt.cs | 8 +++----- .../Items/Skill Items/Ninjitsu/Shuriken.cs | 8 +++----- .../Items/Skill Items/Ninjitsu/SmokeBomb.cs | 8 +++----- .../Items/Skill Items/Specialized/Sand.cs | 8 +++----- .../Skill Items/Tailor Items/Dyetubs/DyeTub.cs | 8 +++----- .../Items/Skill Items/Tinkering/Axle.cs | 3 ++- .../Items/Skill Items/Tinkering/AxleGears.cs | 3 ++- .../Items/Skill Items/Tinkering/ClockFrame.cs | 3 ++- .../Items/Skill Items/Tinkering/ClockParts.cs | 3 ++- .../Items/Skill Items/Tinkering/Gears.cs | 3 ++- .../Items/Skill Items/Tinkering/Hinge.cs | 3 ++- .../Items/Skill Items/Tinkering/SextantParts.cs | 3 ++- .../Items/Skill Items/Tinkering/Springs.cs | 3 ++- .../Items/Skill Items/Tools/Blowpipe.cs | 14 ++++---------- .../Items/Skill Items/Tools/RunicDovetailSaw.cs | 14 ++++---------- .../Items/Skill Items/Tools/RunicFletcherTool.cs | 14 ++++---------- .../Items/Skill Items/Tools/RunicHammer.cs | 4 ++-- .../Items/Skill Items/Tools/RunicSewingKit.cs | 14 ++++---------- .../Items/Skill Items/Tools/SmithHammer.cs | 14 ++++---------- .../Dawn's Music Box/DawnsMusicBox.cs | 4 ++-- .../Dawn's Music Box/DawnsMusicGear.cs | 8 +++----- .../8th Anniversary Items/DupresShield.cs | 3 ++- .../8th Anniversary Items/QuiverOfInfinity.cs | 3 ++- .../Special/8th Anniversary Items/Talismans.cs | 3 ++- .../AoS Promotional/HoodedShroudOfShadows.cs | 8 +++----- .../Blacksmithy/AncientSmithyHammer.cs | 3 ++- .../Blacksmithy/ColoredAnvil.cs | 3 ++- .../Blacksmithy/PowderOfTemperament.cs | 3 ++- .../Community Collection/JesterHatofChuckles.cs | 8 +++----- .../Items/Special/Gifts/RoseOfTrinsic.cs | 8 ++++---- .../UOContent/Items/Special/Gifts/SamuraiHelm.cs | 3 ++- .../UOContent/Items/Special/HeritageToken.cs | 7 ++----- .../UOContent/Items/Special/Holiday/GiftBox.cs | 8 +++----- .../UOContent/Items/Special/Holiday/Snowman.cs | 3 ++- .../UOContent/Items/Special/Holiday/Wreath.cs | 3 ++- Projects/UOContent/Items/Special/MiniHouses.cs | 4 ++-- .../Special/Mutation Core/PlagueBeastGland.cs | 8 +++----- .../Special/Mutation Core/PlagueBeastInnard.cs | 3 ++- .../Mutation Core/PlagueBeastMutationCore.cs | 3 ++- Projects/UOContent/Items/Special/RewardCake.cs | 3 ++- .../Items/Special/Solen Items/BagOfSending.cs | 4 ++-- .../Items/Special/Solen Items/BallOfSummoning.cs | 3 ++- .../Special/Solen Items/BraceletOfBinding.cs | 8 +++----- .../Special/Solen Items/PowderOfTranslocation.cs | 3 ++- .../Items/Special/Solen Items/ZoogiFungus.cs | 3 ++- .../Special/Special Scrolls/SpecialScroll.cs | 3 ++- .../Items/Special/Veteran Rewards/Banner.cs | 7 ++----- .../Items/Special/Veteran Rewards/Brazier.cs | 16 ++++++---------- .../Special/Veteran Rewards/CommodityDeedBox.cs | 7 ++----- .../Special/Veteran Rewards/DecorativeShield.cs | 7 ++----- .../Items/Special/Veteran Rewards/FlamingHead.cs | 7 ++----- .../Special/Veteran Rewards/HangingSkeleton.cs | 7 ++----- .../Special/Veteran Rewards/PottedCactus.cs | 7 ++----- .../Veteran Rewards/WeaponEngravingTool.cs | 4 ++-- Projects/UOContent/Items/Suits/BaseSuit.cs | 3 ++- .../UOContent/Items/Talismans/BaseTalisman.cs | 3 ++- Projects/UOContent/Items/Wands/BaseWand.cs | 3 ++- .../UOContent/Items/Weapons/Axes/BattleAxe.cs | 7 ++----- Projects/UOContent/Items/Weapons/Axes/Pickaxe.cs | 2 +- .../Items/Weapons/Knives/ThrowingDagger.cs | 8 +++----- .../Weapons/ML Weapons/ElvenCompositeLongbow.cs | 8 +++----- .../Items/Weapons/ML Weapons/ElvenSpellblade.cs | 7 ++----- .../Items/Weapons/ML Weapons/MagicalShortbow.cs | 7 ++----- .../Items/Weapons/ML Weapons/OrnateAxe.cs | 7 ++----- .../Items/Weapons/ML Weapons/RuneBlade.cs | 7 ++----- .../Items/Weapons/ML Weapons/WildStaff.cs | 7 ++----- .../UOContent/Items/Weapons/Maces/HammerPick.cs | 7 ++----- .../UOContent/Items/Weapons/Maces/WarHammer.cs | 7 ++----- Projects/UOContent/Items/Weapons/Ranged/Bow.cs | 2 +- .../Items/Weapons/Ranged/CompositeBow.cs | 7 ++----- .../UOContent/Items/Weapons/Ranged/Crossbow.cs | 2 +- .../Items/Weapons/Ranged/HeavyCrossbow.cs | 2 +- .../Items/Weapons/Ranged/RepeatingCrossbow.cs | 7 ++----- .../UOContent/Items/Weapons/SE Weapons/Daisho.cs | 7 ++----- .../UOContent/Items/Weapons/SE Weapons/Kama.cs | 7 ++----- .../Items/Weapons/SE Weapons/Lajatang.cs | 7 ++----- .../Items/Weapons/SE Weapons/NoDachi.cs | 7 ++----- .../UOContent/Items/Weapons/SE Weapons/Sai.cs | 8 +++----- .../UOContent/Items/Weapons/SE Weapons/Tekagi.cs | 7 ++----- .../UOContent/Items/Weapons/SE Weapons/Tessen.cs | 8 +++----- .../Items/Weapons/SE Weapons/Tetsubo.cs | 7 ++----- .../Items/Weapons/SE Weapons/Wakizashi.cs | 7 ++----- .../UOContent/Items/Weapons/SE Weapons/Yumi.cs | 2 +- .../Items/Weapons/SpearsAndForks/TribalSpear.cs | 8 +++----- .../UOContent/Items/Weapons/Staves/GlassStaff.cs | 7 ++----- .../Items/Weapons/Staves/GnarledStaff.cs | 7 ++----- .../Items/Weapons/Staves/QuarterStaff.cs | 7 ++----- .../Items/Weapons/Staves/ShepherdsCrook.cs | 7 ++----- .../Items/Weapons/Throwing/Boomerang.cs | 7 ++----- .../UOContent/Items/Weapons/Throwing/Cyclone.cs | 7 ++----- .../Items/Weapons/Throwing/SoulGlaive.cs | 7 ++----- .../UOContent/Mobiles/Vendors/VendorBackpack.cs | 7 ++----- Projects/UOContent/Multis/Boats/BaseBoatDeed.cs | 4 ++-- .../UOContent/Multis/Boats/BaseDockedBoat.cs | 3 ++- Projects/UOContent/Multis/Deeds.cs | 3 ++- .../Multis/Houses/HousePlacementTool.cs | 7 ++----- 109 files changed, 250 insertions(+), 391 deletions(-) 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..f96a8980e 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; 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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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 e208359a1..51b59a361 100644 --- a/Projects/UOContent/Items/Special/Veteran Rewards/PottedCactus.cs +++ b/Projects/UOContent/Items/Special/Veteran Rewards/PottedCactus.cs @@ -37,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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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/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) From f59605190cc21b0254546c47f83e801e82bd6d4b Mon Sep 17 00:00:00 2001 From: Bohica <53943479+Bohicatv@users.noreply.github.com> Date: Sat, 26 Jul 2025 10:11:31 -0700 Subject: [PATCH 17/47] fix: fixes mobiles pathing home after combat (#2246) --- Projects/UOContent/Mobiles/AI/BaseAI/BaseAI.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/BaseAI.cs b/Projects/UOContent/Mobiles/AI/BaseAI/BaseAI.cs index 02b178dc2..1797cfca6 100644 --- a/Projects/UOContent/Mobiles/AI/BaseAI/BaseAI.cs +++ b/Projects/UOContent/Mobiles/AI/BaseAI/BaseAI.cs @@ -439,11 +439,14 @@ public abstract partial class BaseAI public virtual bool DoActionGuard() { - DebugSay("I am still on guard."); - if (Utility.Random(10) == 0) + if (Mobile.Combatant == null) { - Mobile.Turn(Utility.Random(0, 2) - 1); + DebugSay("No threats found. Going home..."); + Action = ActionType.Wander; } + + DebugSay("I stopped being on guard."); + Action = ActionType.Wander; return true; } From 6938735113f1df22a3828dc6e69d251cf78fbd79 Mon Sep 17 00:00:00 2001 From: Bohica <53943479+Bohicatv@users.noreply.github.com> Date: Sat, 26 Jul 2025 10:21:36 -0700 Subject: [PATCH 18/47] fix: fixes movement jitter for non-AOS (#2245) --- Projects/UOContent/Mobiles/AI/BaseAI/AITimer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Projects/UOContent/Mobiles/AI/BaseAI/AITimer.cs b/Projects/UOContent/Mobiles/AI/BaseAI/AITimer.cs index d42e24409..3ad69a627 100644 --- a/Projects/UOContent/Mobiles/AI/BaseAI/AITimer.cs +++ b/Projects/UOContent/Mobiles/AI/BaseAI/AITimer.cs @@ -36,7 +36,7 @@ internal sealed class AITimer : Timer if (owner.IsFollowingMaster()) { - interval = Core.AOS ? 100 : owner.Mobile.CurrentSpeed * 400; + interval = owner.Mobile.CurrentSpeed * (Core.AOS ? 100 : 400); } else if (owner.Mobile.CurrentSpeed <= 0.4) { @@ -47,7 +47,7 @@ internal sealed class AITimer : Timer interval = owner.Mobile.CurrentSpeed * 3000; } - return Math.Max(interval, 100); + return Math.Max(interval, Core.AOS ? 100 : 200); } protected override void OnTick() From d6bb8316d009e9788e41e2cd9bb49a6fccba8f7a Mon Sep 17 00:00:00 2001 From: Bohica <53943479+Bohicatv@users.noreply.github.com> Date: Sat, 26 Jul 2025 15:53:06 -0700 Subject: [PATCH 19/47] feat: Refactors A* Movement to use PriorityQueue (#2244) ### Summary * Replaces the node chain with a PriorityQueue. * Performance difference is up to 3x faster. ### Benchmarks ```cs | Method | Mean | Error | StdDev | Gen0 | Allocated | |-------------------- |---------:|----------:|----------:|-------:|----------:| | FastAStar_PQueue | 2.112 us | 0.0186 us | 0.0165 us | 0.0038 | 64 B | | FastAStar_NodeChain | 6.430 us | 0.0800 us | 0.1807 us | - | 64 B | ``` ### Video https://github.com/user-attachments/assets/3edd2524-5146-4775-be45-3516f0b01c27 --- .../Engines/Pathing/FastAStarAlgorithm.cs | 559 ++++++++---------- version.json | 2 +- 2 files changed, 253 insertions(+), 308 deletions(-) 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/version.json b/version.json index 227ebd3dd..d36382b04 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.15.0" + "version": "0.15.1" } From 52e309ef95350e2465d7b5ff92c26cf7446ffd56 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Sun, 27 Jul 2025 11:04:32 -0700 Subject: [PATCH 20/47] chore: Updates readme files (#2247) --- README.md | 47 ++++++++--------- RUNUO_TO_MODERNUO.md | 121 ------------------------------------------- SPONSORS.md | 2 +- global.json | 2 +- 4 files changed, 24 insertions(+), 148 deletions(-) delete mode 100644 RUNUO_TO_MODERNUO.md diff --git a/README.md b/README.md index 2184b2181..4108f12c9 100644 --- a/README.md +++ b/README.md @@ -9,22 +9,22 @@ 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/) +[![Windows 11/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 13+](https://img.shields.io/badge/-sequoia-222222?logo=apple&logoColor=white&labelColor=222222) +[![Debian 12+](https://img.shields.io/badge/-bookworm-A81D33?logo=debian&logoColor=A81D33&labelColor=222222)](https://www.debian.org/distrib/) +[![Ubuntu 22+ LTS](https://img.shields.io/badge/-22LTS-E95420?logo=ubuntu&logoColor=E95420&labelColor=222222)](https://ubuntu.com/download/server) +
+[![Alpine 3.19+](https://img.shields.io/badge/-3.21-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/) +[![CentOS Stream 8+](https://img.shields.io/badge/-stream_8-262577?logo=centos&logoColor=white&labelColor=222222)](https://www.centos.org/download/) +[![openSUSE 15.5+](https://img.shields.io/badge/-15.5-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) [![Arch](https://img.shields.io/badge/-Arch-1793D1?logo=archlinux&logoColor=1793D1&labelColor=222222)](https://archlinux.org/download/) @@ -38,19 +38,16 @@ ModernUO [![Discord](https://img.shields.io/discord/751317910504603701?logo=disc #### 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/-%209.0.303%20SDK-5C2D91?logo=.NET&logoColor=white&labelColor=222222)](https://dotnet.microsoft.com/download/dotnet/9.0) #### Supported IDEs - -

Jetbrains Rider 2024.3+spaceVSCodespaceVisual Studio 2022 v17.12+

+

+ Jetbrains Rider 2025+ + space + VSCode + space + Visual Studio 2022 v17.12+ +

## Getting Started - Install prerequisite [requirements](https://github.com/modernuo/ModernUO#requirements) @@ -60,9 +57,9 @@ 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) + - `os` - [Supported operating systems](https://github.com/dotnet/core/blob/main/release-notes/9.0/supported-os.md) + - `win` - Windows 10/11/2019/2022/2025 + - `osx` - MacOS 13/14/15 (Sequoia, Sonoma, Big Sur) - `linux` - Linux - `arch` - `x64` - Intel 64-bit @@ -107,7 +104,7 @@ 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
+

+

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/global.json b/global.json index 1739913f7..733b653c1 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.101", + "version": "9.0.100", "rollForward": "latestMajor", "allowPrerelease": false } From bbb7dc2294d766928873b40ac8da52ab9e99d024 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Sat, 9 Aug 2025 20:55:36 -0700 Subject: [PATCH 21/47] fix: Fixes dropping a stack of scrolls on a spellbook (#2248) --- Projects/UOContent/Items/Skill Items/Magical/Spellbook.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Projects/UOContent/Items/Skill Items/Magical/Spellbook.cs b/Projects/UOContent/Items/Skill Items/Magical/Spellbook.cs index f96a8980e..cdd60bd51 100644 --- a/Projects/UOContent/Items/Skill Items/Magical/Spellbook.cs +++ b/Projects/UOContent/Items/Skill Items/Magical/Spellbook.cs @@ -497,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; } @@ -524,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; From 340acafcb3e56b960bcbb1e30200ec5683ea2993 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Wed, 20 Aug 2025 15:30:04 -0700 Subject: [PATCH 22/47] fix: Fixes mining/lumberjacking multi-harvest (#2249) --- Projects/UOContent/Engines/Harvest/Lumberjacking.cs | 2 ++ Projects/UOContent/Engines/Harvest/Mining.cs | 2 ++ 2 files changed, 4 insertions(+) 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)) From 641a3eb1f853b731884d9337d24c94eaeda4ee6a Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Sun, 7 Sep 2025 12:50:20 -0700 Subject: [PATCH 23/47] fix: Updates BOB/BOD gumps to the new API (#2250) ### Summary * Moves BOBGump to DynamicGump. * BOBGump no longer creates a whole new gump context object on every send. * Moves BODBuyGump to StaticGump. --- .../Engines/Bulk Orders/Books/BOBGump.cs | 1310 ++++++++--------- .../Engines/Bulk Orders/Books/BODBuyGump.cs | 209 +-- 2 files changed, 750 insertions(+), 769 deletions(-) diff --git a/Projects/UOContent/Engines/Bulk Orders/Books/BOBGump.cs b/Projects/UOContent/Engines/Bulk Orders/Books/BOBGump.cs index 28ee596c2..8dd3073f1 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(); + } } } From df171926bb06c90cc12d01a20032e90ee8fb4363 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Thu, 11 Sep 2025 06:32:48 -0700 Subject: [PATCH 24/47] fix: Fixes potential notoriety caching issue in Mobile.cs (#2251) Replaces the 2D packet cache with a simpler one outlining the packet flag changes --- Projects/Server/Mobiles/Mobile.cs | 13 ++--- .../Network/Packets/OutgoingMobilePackets.cs | 58 ++++++++++--------- 2 files changed, 35 insertions(+), 36 deletions(-) diff --git a/Projects/Server/Mobiles/Mobile.cs b/Projects/Server/Mobiles/Mobile.cs index 7c998e887..855244da6 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)) { @@ -4328,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) { @@ -6963,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) 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)] From fbf8497c59c63e633b7c118803c76c313773a692 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Fri, 31 Oct 2025 00:53:47 -0700 Subject: [PATCH 25/47] fix: Fix PlantSystem serialization of LeftSeeds and LeftResources at zero (#2255) * Initial plan * Add SerializableFieldDefault attributes for LeftSeeds and LeftResources This fixes the serialization bug where _leftSeeds and _leftResources were initialized to 8 in the constructor but didn't serialize when their value was 0. Upon deserialization, the constructor would run again and reset these values back to 8. The SerializableFieldDefault attributes tell the serialization system that the default value is 8, so it will properly serialize 0 values and maintain the correct state across server restarts. Co-authored-by: kamronbatman <3953314+kamronbatman@users.noreply.github.com> * Update ShouldSerialize methods to check against default value Changed ShouldSerializeLeftSeeds() and ShouldSerializeLeftResources() to check if the value is != 8 (the default) instead of != 0. This ensures that only non-default values are serialized, following the same pattern used in BaseWeapon.cs and other classes with SerializableFieldDefault attributes. Co-authored-by: kamronbatman <3953314+kamronbatman@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: kamronbatman <3953314+kamronbatman@users.noreply.github.com> --- Projects/UOContent/Engines/Plants/PlantSystem.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) 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) { From 3a3f5ee518ea37f1c23996ca7ffe87e8e3c5f725 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Tue, 11 Nov 2025 11:26:34 -0800 Subject: [PATCH 26/47] feat: Adds .NET 10 / C# 14 support. (#2258) ### Summary * Adds .NET 10 support * Bumps to C# 14 --- .config/dotnet-tools.json | 2 +- .github/workflows/build-test.yml | 4 +- .github/workflows/create-release.yml | 2 +- Directory.Build.props | 10 +- Projects/Server.Tests/Server.Tests.csproj | 4 +- Projects/Server/Random/BuiltInSecureRng.cs | 28 ------ Projects/Server/Server.csproj | 4 +- .../UOContent.Tests/UOContent.Tests.csproj | 4 +- .../Accounting/Security/AccountSecurity.cs | 91 +++++++++---------- .../Security/Argon2PasswordProtection.cs | 21 ++--- .../HashAlgorithmPasswordProtection.cs | 35 ++++--- .../Security/PBKDF2PasswordProtection.cs | 59 ++++++------ Projects/UOContent/UOContent.csproj | 8 +- azure-pipelines.yml | 2 +- global.json | 2 +- version.json | 2 +- 16 files changed, 124 insertions(+), 154 deletions(-) delete mode 100644 Projects/Server/Random/BuiltInSecureRng.cs 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..0a906a743 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 @@ -86,7 +86,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 ae71e92d4..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 @@ -64,9 +64,9 @@ - + - 3.7.115 + 3.9.50 all diff --git a/Projects/Server.Tests/Server.Tests.csproj b/Projects/Server.Tests/Server.Tests.csproj index 038575c90..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/Random/BuiltInSecureRng.cs b/Projects/Server/Random/BuiltInSecureRng.cs deleted file mode 100644 index dec408338..000000000 --- a/Projects/Server/Random/BuiltInSecureRng.cs +++ /dev/null @@ -1,28 +0,0 @@ -/************************************************************************* - * ModernUO * - * Copyright 2019-2023 - ModernUO Development Team * - * Email: hi@modernuo.com * - * File: SecureRandom.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 System.Security.Cryptography; - -namespace Server; - -public static class BuiltInSecureRng -{ - public static RandomNumberGenerator Generator { get; } = RandomNumberGenerator.Create(); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void NextBytes(Span buffer) => Generator.GetBytes(buffer); -} diff --git a/Projects/Server/Server.csproj b/Projects/Server/Server.csproj index 5e16cde20..d9da8166d 100644 --- a/Projects/Server/Server.csproj +++ b/Projects/Server/Server.csproj @@ -37,10 +37,10 @@ - + - + diff --git a/Projects/UOContent.Tests/UOContent.Tests.csproj b/Projects/UOContent.Tests/UOContent.Tests.csproj index 77064faa1..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/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/UOContent.csproj b/Projects/UOContent/UOContent.csproj index bea34e002..4ca0561f8 100644 --- a/Projects/UOContent/UOContent.csproj +++ b/Projects/UOContent/UOContent.csproj @@ -39,16 +39,16 @@ false - - + + - - + + 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/global.json b/global.json index 733b653c1..6a288505a 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "9.0.100", + "version": "10.0.100", "rollForward": "latestMajor", "allowPrerelease": false } diff --git a/version.json b/version.json index d36382b04..de4574c38 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.15.1" + "version": "0.15.2" } From c308a3baadb7c20ffe254917141a33cf2e759ef5 Mon Sep 17 00:00:00 2001 From: Bohica <53943479+Bohicatv@users.noreply.github.com> Date: Wed, 12 Nov 2025 09:49:35 -0800 Subject: [PATCH 27/47] fix: Fixes timer leak in BaseCamp (#2259) --- Projects/UOContent/Multis/Camps/BaseCamp.cs | 29 +++++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/Projects/UOContent/Multis/Camps/BaseCamp.cs b/Projects/UOContent/Multis/Camps/BaseCamp.cs index 3b9cece2a..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); } } From 5d920a25b1adaac85f7f3c40249d846815f6fbde Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Wed, 12 Nov 2025 23:03:14 -0800 Subject: [PATCH 28/47] chore: Updates .NET reference material to 10 (#2261) --- .github/workflows/build-test.yml | 13 ++-------- README.md | 44 ++++++++++++++++---------------- docs/installation.md | 6 ++--- 3 files changed, 27 insertions(+), 36 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 0a906a743..1c9ea98a9 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -54,20 +54,11 @@ jobs: - container: ubuntu:jammy name: Ubuntu 22 packageManager: apt - - container: ubuntu:focal - name: Ubuntu 20 - 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 diff --git a/README.md b/README.md index 4108f12c9..385219433 100644 --- a/README.md +++ b/README.md @@ -15,38 +15,38 @@ ModernUO [![Discord](https://img.shields.io/discord/751317910504603701?logo=disc ## Requirements #### Supported Operating Systems -[![Windows 11/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 13+](https://img.shields.io/badge/-sequoia-222222?logo=apple&logoColor=white&labelColor=222222) +[![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/-bookworm-A81D33?logo=debian&logoColor=A81D33&labelColor=222222)](https://www.debian.org/distrib/) [![Ubuntu 22+ LTS](https://img.shields.io/badge/-22LTS-E95420?logo=ubuntu&logoColor=E95420&labelColor=222222)](https://ubuntu.com/download/server)
-[![Alpine 3.19+](https://img.shields.io/badge/-3.21-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 Stream 8+](https://img.shields.io/badge/-stream_8-262577?logo=centos&logoColor=white&labelColor=222222)](https://www.centos.org/download/) -[![openSUSE 15.5+](https://img.shields.io/badge/-15.5-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) +[![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.303%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 2025+ + Jetbrains Rider 2025.3+ space VSCode space - Visual Studio 2022 v17.12+ + Visual Studio 2026

## Getting Started @@ -57,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/9.0/supported-os.md) - - `win` - Windows 10/11/2019/2022/2025 - - `osx` - MacOS 13/14/15 (Sequoia, Sonoma, Big Sur) - - `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 @@ -86,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) 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 From 68caaab92caa0bc0bb600ca7ba3189a5e8ad1de1 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Wed, 12 Nov 2025 23:34:13 -0800 Subject: [PATCH 29/47] fix: Makes SpanWriter/SpanReader exceptions clearer (#2262) --- Projects/Server/Buffers/SpanReader.cs | 75 ++++++++++---------------- Projects/Server/Buffers/SpanWriter.cs | 76 ++++++++++----------------- 2 files changed, 55 insertions(+), 96 deletions(-) diff --git a/Projects/Server/Buffers/SpanReader.cs b/Projects/Server/Buffers/SpanReader.cs index 31c5fc51d..290ba5a94 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,42 +254,24 @@ 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)] 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) { From 6f5b7f7a6b3e4265232663c7609acbe6301bbb42 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Thu, 13 Nov 2025 00:00:19 -0800 Subject: [PATCH 30/47] fix: Fixes SpanWriter/SpanReader tests (#2263) --- .../Tests/Buffers/SpanReaderTests.cs | 597 +++++++++++++++++ .../Tests/Buffers/SpanWriterTests.cs | 618 ++++++++++++++++-- Projects/Server/Buffers/SpanReader.cs | 2 +- 3 files changed, 1171 insertions(+), 46 deletions(-) create mode 100644 Projects/Server.Tests/Tests/Buffers/SpanReaderTests.cs 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/Buffers/SpanReader.cs b/Projects/Server/Buffers/SpanReader.cs index 290ba5a94..dd35e8702 100644 --- a/Projects/Server/Buffers/SpanReader.cs +++ b/Projects/Server/Buffers/SpanReader.cs @@ -275,7 +275,7 @@ public ref struct SpanReader } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int Read(Span bytes) + public int Read(scoped Span bytes) { if (bytes.Length == 0) { From 5667017874c4084b137b82bee48de3b6703c7f3e Mon Sep 17 00:00:00 2001 From: Bohica <53943479+Bohicatv@users.noreply.github.com> Date: Sat, 15 Nov 2025 10:15:10 -0800 Subject: [PATCH 31/47] fix: Fixes pets from attacking themselves (#2265) ### Summary Stops pets from attacking themselves. --- Projects/UOContent/Mobiles/AI/AnimalAI.cs | 8 ++++++++ Projects/UOContent/Mobiles/AI/MageAI.cs | 8 ++++++++ Projects/UOContent/Mobiles/AI/MeleeAI.cs | 8 ++++++++ 3 files changed, 24 insertions(+) diff --git a/Projects/UOContent/Mobiles/AI/AnimalAI.cs b/Projects/UOContent/Mobiles/AI/AnimalAI.cs index 9e33414c7..a25355078 100644 --- a/Projects/UOContent/Mobiles/AI/AnimalAI.cs +++ b/Projects/UOContent/Mobiles/AI/AnimalAI.cs @@ -46,6 +46,14 @@ public class AnimalAI : BaseAI { var combatant = Mobile.Combatant; + if (Mobile.Controlled && combatant == Mobile) + { + DebugSay("I should not attack myself!"); + Mobile.Combatant = null; + Action = ActionType.Guard; + return true; + } + if (combatant == null || combatant.Deleted || combatant.Map != Mobile.Map || !combatant.Alive || combatant.IsDeadBondedPet) { diff --git a/Projects/UOContent/Mobiles/AI/MageAI.cs b/Projects/UOContent/Mobiles/AI/MageAI.cs index 03fe60a6a..c1e6b70f4 100644 --- a/Projects/UOContent/Mobiles/AI/MageAI.cs +++ b/Projects/UOContent/Mobiles/AI/MageAI.cs @@ -624,6 +624,14 @@ public class MageAI : BaseAI var c = Mobile.Combatant; Mobile.Warmode = true; + if (Mobile.Controlled && c == Mobile) + { + DebugSay("I should not attack myself!"); + Mobile.Combatant = null; + Action = ActionType.Guard; + return true; + } + if (c?.Deleted != false || !c.Alive || c.IsDeadBondedPet || !Mobile.CanSee(c) || !Mobile.CanBeHarmful(c, false) || c.Map != Mobile.Map) { diff --git a/Projects/UOContent/Mobiles/AI/MeleeAI.cs b/Projects/UOContent/Mobiles/AI/MeleeAI.cs index edf0b108e..44fe40370 100644 --- a/Projects/UOContent/Mobiles/AI/MeleeAI.cs +++ b/Projects/UOContent/Mobiles/AI/MeleeAI.cs @@ -33,6 +33,14 @@ public class MeleeAI : BaseAI { var combatant = Mobile.Combatant; + if (Mobile.Controlled && combatant == Mobile) + { + DebugSay("I should not attack myself!"); + Mobile.Combatant = null; + Action = ActionType.Guard; + return true; + } + if (combatant == null || combatant.Deleted || combatant.Map != Mobile.Map || !combatant.Alive || combatant.IsDeadBondedPet) { From 6cabac5a4f1981f01403040003e82556f87c48f4 Mon Sep 17 00:00:00 2001 From: Bohica <53943479+Bohicatv@users.noreply.github.com> Date: Sat, 15 Nov 2025 14:21:18 -0800 Subject: [PATCH 32/47] fix: Fixes pets from attacking themselves (Again) (#2267) --- Projects/UOContent/Mobiles/AI/AnimalAI.cs | 8 -------- Projects/UOContent/Mobiles/AI/MageAI.cs | 16 ++++++++-------- Projects/UOContent/Mobiles/AI/MeleeAI.cs | 8 -------- 3 files changed, 8 insertions(+), 24 deletions(-) diff --git a/Projects/UOContent/Mobiles/AI/AnimalAI.cs b/Projects/UOContent/Mobiles/AI/AnimalAI.cs index a25355078..9e33414c7 100644 --- a/Projects/UOContent/Mobiles/AI/AnimalAI.cs +++ b/Projects/UOContent/Mobiles/AI/AnimalAI.cs @@ -46,14 +46,6 @@ public class AnimalAI : BaseAI { var combatant = Mobile.Combatant; - if (Mobile.Controlled && combatant == Mobile) - { - DebugSay("I should not attack myself!"); - Mobile.Combatant = null; - Action = ActionType.Guard; - return true; - } - if (combatant == null || combatant.Deleted || combatant.Map != Mobile.Map || !combatant.Alive || combatant.IsDeadBondedPet) { diff --git a/Projects/UOContent/Mobiles/AI/MageAI.cs b/Projects/UOContent/Mobiles/AI/MageAI.cs index c1e6b70f4..9ed02cd02 100644 --- a/Projects/UOContent/Mobiles/AI/MageAI.cs +++ b/Projects/UOContent/Mobiles/AI/MageAI.cs @@ -624,14 +624,6 @@ public class MageAI : BaseAI var c = Mobile.Combatant; Mobile.Warmode = true; - if (Mobile.Controlled && c == Mobile) - { - DebugSay("I should not attack myself!"); - Mobile.Combatant = null; - Action = ActionType.Guard; - return true; - } - if (c?.Deleted != false || !c.Alive || c.IsDeadBondedPet || !Mobile.CanSee(c) || !Mobile.CanBeHarmful(c, false) || c.Map != Mobile.Map) { @@ -721,6 +713,14 @@ public class MageAI : BaseAI } 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); diff --git a/Projects/UOContent/Mobiles/AI/MeleeAI.cs b/Projects/UOContent/Mobiles/AI/MeleeAI.cs index 44fe40370..edf0b108e 100644 --- a/Projects/UOContent/Mobiles/AI/MeleeAI.cs +++ b/Projects/UOContent/Mobiles/AI/MeleeAI.cs @@ -33,14 +33,6 @@ public class MeleeAI : BaseAI { var combatant = Mobile.Combatant; - if (Mobile.Controlled && combatant == Mobile) - { - DebugSay("I should not attack myself!"); - Mobile.Combatant = null; - Action = ActionType.Guard; - return true; - } - if (combatant == null || combatant.Deleted || combatant.Map != Mobile.Map || !combatant.Alive || combatant.IsDeadBondedPet) { From 5e65f9f0b24674d516015675f39541e878bacc72 Mon Sep 17 00:00:00 2001 From: Bohica <53943479+Bohicatv@users.noreply.github.com> Date: Sun, 16 Nov 2025 10:09:44 -0800 Subject: [PATCH 33/47] fix: Fixes descending comparer in admin gump (#2272) --- Projects/UOContent/Gumps/AdminGump.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Projects/UOContent/Gumps/AdminGump.cs b/Projects/UOContent/Gumps/AdminGump.cs index e9c91dba3..00e65887d 100644 --- a/Projects/UOContent/Gumps/AdminGump.cs +++ b/Projects/UOContent/Gumps/AdminGump.cs @@ -4095,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; } } From 051f22ff93f0dfdd009aa4f3c6edb233e8b63d81 Mon Sep 17 00:00:00 2001 From: Bohica <53943479+Bohicatv@users.noreply.github.com> Date: Sun, 16 Nov 2025 10:12:12 -0800 Subject: [PATCH 34/47] fix: Fixes vendor look spam (#2269) --- Projects/UOContent/Mobiles/BaseCreature.cs | 28 ++-------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/Projects/UOContent/Mobiles/BaseCreature.cs b/Projects/UOContent/Mobiles/BaseCreature.cs index f9898d6cd..eb70e8454 100644 --- a/Projects/UOContent/Mobiles/BaseCreature.cs +++ b/Projects/UOContent/Mobiles/BaseCreature.cs @@ -2676,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) { @@ -2713,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()); From ee1a40bb51fc467ae1e6795fd0c7efab71ae0a97 Mon Sep 17 00:00:00 2001 From: Bohica <53943479+Bohicatv@users.noreply.github.com> Date: Sun, 16 Nov 2025 10:14:06 -0800 Subject: [PATCH 35/47] fix: Fixes movement pathing while in combat (#2271) --- Projects/UOContent/Mobiles/AI/AnimalAI.cs | 2 +- Projects/UOContent/Mobiles/AI/ArcherAI.cs | 8 +------- Projects/UOContent/Mobiles/AI/BerserkAI.cs | 2 +- Projects/UOContent/Mobiles/AI/MageAI.cs | 6 +++--- Projects/UOContent/Mobiles/AI/MeleeAI.cs | 2 +- Projects/UOContent/Mobiles/AI/PredatorAI.cs | 2 +- Projects/UOContent/Mobiles/AI/ThiefAI.cs | 2 +- Projects/UOContent/Mobiles/Familiars/BaseFamiliar.cs | 2 +- 8 files changed, 10 insertions(+), 16 deletions(-) diff --git a/Projects/UOContent/Mobiles/AI/AnimalAI.cs b/Projects/UOContent/Mobiles/AI/AnimalAI.cs index 9e33414c7..5107b57fe 100644 --- a/Projects/UOContent/Mobiles/AI/AnimalAI.cs +++ b/Projects/UOContent/Mobiles/AI/AnimalAI.cs @@ -55,7 +55,7 @@ public class AnimalAI : BaseAI return true; } - if (!WalkMobileRange(combatant, 1, true, Mobile.RangeFight, Mobile.RangeFight)) + if (!WalkMobileRange(combatant, 1, false, Mobile.RangeFight, Mobile.RangeFight)) { if (Mobile.GetDistanceToSqrt(combatant) > Mobile.RangePerception + 1) { diff --git a/Projects/UOContent/Mobiles/AI/ArcherAI.cs b/Projects/UOContent/Mobiles/AI/ArcherAI.cs index 6491c42d5..19e0375c4 100644 --- a/Projects/UOContent/Mobiles/AI/ArcherAI.cs +++ b/Projects/UOContent/Mobiles/AI/ArcherAI.cs @@ -41,13 +41,7 @@ public class ArcherAI : BaseAI return true; } - if (Core.TickCount - Mobile.LastMoveTime > 1000 && !WalkMobileRange( - combatant, - 1, - true, - Mobile.RangeFight, - Mobile.Weapon.MaxRange - )) + if (!WalkMobileRange(combatant, 1, false, Mobile.RangeFight, Mobile.Weapon.MaxRange)) { this.DebugSayFormatted($"I am still not in range of {combatant.Name}"); diff --git a/Projects/UOContent/Mobiles/AI/BerserkAI.cs b/Projects/UOContent/Mobiles/AI/BerserkAI.cs index f882d6fb5..4663ae8d0 100644 --- a/Projects/UOContent/Mobiles/AI/BerserkAI.cs +++ b/Projects/UOContent/Mobiles/AI/BerserkAI.cs @@ -38,7 +38,7 @@ public class BerserkAI : BaseAI return true; } - if (!WalkMobileRange(combatant, 1, true, Mobile.RangeFight, Mobile.RangeFight)) + if (!WalkMobileRange(combatant, 1, false, Mobile.RangeFight, Mobile.RangeFight)) { this.DebugSayFormatted($"I am still not in range of {combatant.Name}"); diff --git a/Projects/UOContent/Mobiles/AI/MageAI.cs b/Projects/UOContent/Mobiles/AI/MageAI.cs index 9ed02cd02..0ca4893e5 100644 --- a/Projects/UOContent/Mobiles/AI/MageAI.cs +++ b/Projects/UOContent/Mobiles/AI/MageAI.cs @@ -168,7 +168,7 @@ public class MageAI : BaseAI { if (!SmartAI) { - if (!MoveTo(m, true, Mobile.RangeFight)) + if (!MoveTo(m, false, Mobile.RangeFight)) { OnFailedMove(); } @@ -182,14 +182,14 @@ public class MageAI : BaseAI { RunFrom(m); } - else if (!Mobile.InRange(m, Math.Max(Mobile.RangeFight, 2)) && !MoveTo(m, true, 1)) + else if (!Mobile.InRange(m, Math.Max(Mobile.RangeFight, 2)) && !MoveTo(m, false, 1)) { OnFailedMove(); } } else if (!Mobile.InRange(m, Mobile.RangeFight)) { - if (!MoveTo(m, true, 1)) + if (!MoveTo(m, false, 1)) { OnFailedMove(); } diff --git a/Projects/UOContent/Mobiles/AI/MeleeAI.cs b/Projects/UOContent/Mobiles/AI/MeleeAI.cs index edf0b108e..01ef3c06f 100644 --- a/Projects/UOContent/Mobiles/AI/MeleeAI.cs +++ b/Projects/UOContent/Mobiles/AI/MeleeAI.cs @@ -67,7 +67,7 @@ public class MeleeAI : BaseAI } } - if (!MoveTo(combatant, true, Mobile.RangeFight)) + if (!MoveTo(combatant, false, Mobile.RangeFight)) { if (AcquireFocusMob(Mobile.RangePerception, Mobile.FightMode, false, false, true)) { diff --git a/Projects/UOContent/Mobiles/AI/PredatorAI.cs b/Projects/UOContent/Mobiles/AI/PredatorAI.cs index fe5b06a6f..5e0e01520 100644 --- a/Projects/UOContent/Mobiles/AI/PredatorAI.cs +++ b/Projects/UOContent/Mobiles/AI/PredatorAI.cs @@ -41,7 +41,7 @@ public class PredatorAI : BaseAI return true; } - if (!WalkMobileRange(combatant, 1, true, Mobile.RangeFight, Mobile.RangeFight)) + if (!WalkMobileRange(combatant, 1, false, Mobile.RangeFight, Mobile.RangeFight)) { if (Mobile.GetDistanceToSqrt(combatant) > Mobile.RangePerception + 1) { diff --git a/Projects/UOContent/Mobiles/AI/ThiefAI.cs b/Projects/UOContent/Mobiles/AI/ThiefAI.cs index ff707f3df..2d67de801 100644 --- a/Projects/UOContent/Mobiles/AI/ThiefAI.cs +++ b/Projects/UOContent/Mobiles/AI/ThiefAI.cs @@ -43,7 +43,7 @@ public class ThiefAI : BaseAI return true; } - if (!WalkMobileRange(combatant, 1, true, Mobile.RangeFight, Mobile.RangeFight)) + if (!WalkMobileRange(combatant, 1, false, Mobile.RangeFight, Mobile.RangeFight)) { this.DebugSayFormatted($"I should be closer to {combatant.Name}"); } 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; From 6f64cddd0b94eddfe70c1ccca58bb7ea064ca160 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Sun, 16 Nov 2025 10:32:29 -0800 Subject: [PATCH 36/47] feat: Optimizes HTML Escape (#2273) ### Summary Optimizes HTML escaping by using a vectorized search. --- .../Tests/Utility/HtmlEscapeTests.cs | 242 ++++++++++++++++++ Projects/Server/Utilities/Html.cs | 75 +++++- 2 files changed, 309 insertions(+), 8 deletions(-) create mode 100644 Projects/Server.Tests/Tests/Utility/HtmlEscapeTests.cs 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/Utilities/Html.cs b/Projects/Server/Utilities/Html.cs index 91d2b6f3b..9377c7014 100644 --- a/Projects/Server/Utilities/Html.cs +++ b/Projects/Server/Utilities/Html.cs @@ -14,9 +14,11 @@ *************************************************************************/ using System; +using System.Buffers; using System.Runtime.CompilerServices; using System.Text; using Server.Buffers; +using Server.Text; namespace Server; @@ -237,13 +239,70 @@ public static class Html [MethodImpl(MethodImplOptions.AggressiveInlining)] public static RawInterpolatedStringHandler Right(this ReadOnlySpan text) => text.Right(-1); + private static readonly SearchValues _htmlSearchValues = SearchValues.Create('<', '>', '&', '"', '\''); + [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string EscapeHtml(this string input) => - new StringBuilder(input.Length).Append(input) - .Replace("<", "<") - .Replace(">", ">") - .Replace("&", "&") - .Replace("\"", """) - .Replace("'", "'") - .ToString(); + 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; + } } From 4836bff5ebf4088518b333c1d0f87b2376228868 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Sun, 16 Nov 2025 18:33:53 -0800 Subject: [PATCH 37/47] fix: Eliminates List allocations in various places. (#2158) --- Projects/Server/Items/Container.cs | 17 ++-- Projects/Server/Regions/Region.cs | 62 +++++++++++- Projects/Server/Utilities/Html.cs | 1 - Projects/UOContent/Accounting/Account.cs | 34 ------- .../Commands/Generic/Commands/DesignInsert.cs | 9 +- .../Implementors/RegionCommandImplementor.cs | 7 +- .../Engines/CannedEvil/ChampionSpawn.cs | 22 +++-- .../UOContent/Engines/CannedEvil/GenChamps.cs | 10 +- .../UOContent/Engines/Doom/GenGauntlet.cs | 15 ++- .../Doom/LeverPuzzle/LeverPuzzleController.cs | 46 ++++----- .../Factions/Mobiles/Guards/GuardAI.cs | 37 ++++---- .../ML Quests/Mobiles/BoonCollector.cs | 3 +- .../ML Quests/Objectives/DeliverObjective.cs | 10 +- .../UOContent/Gumps/Houses/HouseGumpAOS.cs | 6 +- Projects/UOContent/Items/Books/BaseBook.cs | 12 ++- .../Items/Bulletin Boards/BulletinMessage.cs | 6 +- .../UOContent/Items/Containers/Strongbox.cs | 10 +- .../UOContent/Items/Misc/Corpses/Corpse.cs | 13 ++- .../Fishing/Misc/ShipwreckedItem.cs | 1 - Projects/UOContent/Mobiles/PlayerMobile.cs | 94 ++++++++++--------- Projects/UOContent/Multis/Houses/BaseHouse.cs | 16 ++-- .../Multis/Houses/HouseFoundation.cs | 21 ++++- .../Network/Packets/IncomingItemPackets.cs | 11 ++- 23 files changed, 263 insertions(+), 200 deletions(-) diff --git a/Projects/Server/Items/Container.cs b/Projects/Server/Items/Container.cs index 65b910e95..d258d3e45 100644 --- a/Projects/Server/Items/Container.cs +++ b/Projects/Server/Items/Container.cs @@ -430,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; @@ -451,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; @@ -461,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; } @@ -469,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/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/Utilities/Html.cs b/Projects/Server/Utilities/Html.cs index 9377c7014..944e2e025 100644 --- a/Projects/Server/Utilities/Html.cs +++ b/Projects/Server/Utilities/Html.cs @@ -16,7 +16,6 @@ using System; using System.Buffers; using System.Runtime.CompilerServices; -using System.Text; using Server.Buffers; using Server.Text; 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/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/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/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/Mobiles/Guards/GuardAI.cs b/Projects/UOContent/Engines/Factions/Mobiles/Guards/GuardAI.cs index 91bde7a24..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; @@ -692,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)); } @@ -746,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/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/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/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/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/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/Skill Items/Fishing/Misc/ShipwreckedItem.cs b/Projects/UOContent/Items/Skill Items/Fishing/Misc/ShipwreckedItem.cs index c4f7945ac..962b8752d 100644 --- a/Projects/UOContent/Items/Skill Items/Fishing/Misc/ShipwreckedItem.cs +++ b/Projects/UOContent/Items/Skill Items/Fishing/Misc/ShipwreckedItem.cs @@ -1,4 +1,3 @@ -using System; using ModernUO.Serialization; namespace Server.Items; 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/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/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); } } From dfb83cda13fdaf272417fcb69b4e5b7781c360b9 Mon Sep 17 00:00:00 2001 From: Bohica <53943479+Bohicatv@users.noreply.github.com> Date: Sun, 23 Nov 2025 09:58:33 -0800 Subject: [PATCH 38/47] fix: Fixes priced healer resurrection when not choosing the correct option (#2276) --- Projects/UOContent/Gumps/ResurrectGump.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) 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; } From ea86d5b2a683136e08010be0bf618d5eb4d7daf3 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Sun, 23 Nov 2025 10:09:42 -0800 Subject: [PATCH 39/47] chore: Adds Debian 13 to CI/CD (#2277) --- .github/workflows/build-test.yml | 3 +++ README.md | 4 ++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml index 1c9ea98a9..15d42fceb 100644 --- a/.github/workflows/build-test.yml +++ b/.github/workflows/build-test.yml @@ -54,6 +54,9 @@ jobs: - container: ubuntu:jammy name: Ubuntu 22 packageManager: apt + - container: debian:trixie + name: Debian 13 + packageManager: apt - container: debian:bookworm name: Debian 12 packageManager: apt diff --git a/README.md b/README.md index 385219433..01f875145 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ ModernUO [![Discord](https://img.shields.io/discord/751317910504603701?logo=disc #### Supported Operating Systems [![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/-bookworm-A81D33?logo=debian&logoColor=A81D33&labelColor=222222)](https://www.debian.org/distrib/) -[![Ubuntu 22+ LTS](https://img.shields.io/badge/-22LTS-E95420?logo=ubuntu&logoColor=E95420&labelColor=222222)](https://ubuntu.com/download/server) +[![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/) From 7bd5a853a3c108cdbc1df862d173a15959b40506 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Sun, 23 Nov 2025 11:35:31 -0800 Subject: [PATCH 40/47] feat: Adds size/style support to gump builders, optimizes EscapeHtml (#2257) > [!IMPORTANT] > **Developer Note** > THIS IS A BREAKING CHANGE TO THE NEW API GUMP. > Please give us feedback in [discord ](https://muo.gg/discord) if you have issues, need help, or have ideas for a better API change! ### Summary * Adds support for size/style to dynamic/static builder. * Drastically simplifies the dynamic/static builder api for AddHtml. * Cleans up some legacy gump files. --- Projects/Server/Utilities/Html.cs | 322 ++++++++---------- .../Tests/Gumps/TestGumps/DynamicTestGump.cs | 2 +- .../Gumps/TestGumps/StaticLayoutTestGump.cs | 2 +- .../Tests/Gumps/TestGumps/StaticTestGump.cs | 2 +- Projects/UOContent/Commands/HelpInfo.cs | 4 +- .../Engines/Bulk Orders/Books/BOBGump.cs | 2 +- .../Engines/ConPVP/Gumps/AcceptTeamGump.cs | 8 +- .../Engines/ConPVP/Gumps/ArenaGump.cs | 2 +- .../Engines/ConPVP/Gumps/ConfirmSignupGump.cs | 8 +- .../Engines/ConPVP/Gumps/LadderGump.cs | 8 +- .../ConPVP/Gumps/TournamentBracketGump.cs | 14 +- .../Factions/Gumps/ElectionManagementGump.cs | 14 +- .../Engines/Help/PageResponseGump.cs | 2 +- .../UOContent/Engines/Help/SpeechLogGump.cs | 2 +- .../Engines/Quests/Core/QuestSystem.cs | 2 +- .../UOContent/Engines/Spawners/SpawnerGump.cs | 4 +- Projects/UOContent/Gumps/AdminGump.cs | 4 +- .../Gumps/Base/DynamicGumpBuilder.cs | 140 ++------ Projects/UOContent/Gumps/Base/Grid/Grid.cs | 77 ----- .../Gumps/Base/GumpStringsBuilder.cs | 64 +--- Projects/UOContent/Gumps/Base/Legacy/Gump.cs | 2 +- .../Gumps/Base/{Grid => Legacy}/GumpGrid.cs | 62 +++- .../UOContent/Gumps/Base/StaticGumpBuilder.cs | 133 ++------ Projects/UOContent/Gumps/ClientGump.cs | 6 +- .../Guilds/New Guild System/OtherGuildInfo.cs | 6 +- Projects/UOContent/Gumps/PetResurrectGump.cs | 2 +- Projects/UOContent/Gumps/StaticWarningGump.cs | 5 +- Projects/UOContent/Gumps/ViewHousesGump.cs | 2 +- Projects/UOContent/Gumps/VirtualCheckGump.cs | 2 +- .../UOContent/Items/Aquarium/AquariumGump.cs | 2 +- .../House Raffle/HouseRaffleManagementGump.cs | 4 +- .../Special/House Raffle/HouseRaffleStone.cs | 3 +- .../Special/Special Scrolls/PowerScroll.cs | 2 +- .../Special Scrolls/ScrollofTranscendence.cs | 2 +- .../Special/Special Scrolls/StatCapScroll.cs | 2 +- Projects/UOContent/Misc/ShardPoller.cs | 2 +- .../Mobiles/Vendors/PlayerBarkeeper.cs | 12 +- .../Systems/JailSystem/JailRecordGump.cs | 28 +- .../Text/TextDefinitionExtensions.cs | 71 ++-- version.json | 2 +- 40 files changed, 398 insertions(+), 635 deletions(-) delete mode 100644 Projects/UOContent/Gumps/Base/Grid/Grid.cs rename Projects/UOContent/Gumps/Base/{Grid => Legacy}/GumpGrid.cs (90%) diff --git a/Projects/Server/Utilities/Html.cs b/Projects/Server/Utilities/Html.cs index 944e2e025..907b90812 100644 --- a/Projects/Server/Utilities/Html.cs +++ b/Projects/Server/Utilities/Html.cs @@ -21,222 +21,131 @@ 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 - ) - { - var handler = textHandler.Text.Color(color, size, fontStyle); - textHandler.Clear(); - return handler; - } + public static string Center(this string input) => Center(input.AsSpan()); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RawInterpolatedStringHandler Color( - this ReadOnlySpan text, - ReadOnlySpan color, - int size = -1, - byte fontStyle = 0 - ) - { - if (color != Span.Empty) - { - if (size > -1) - { - if (fontStyle > 0) - { - return $"{text}"; - } - - return $"{text}"; - } - - if (fontStyle > 0) - { - return $"{text}"; - } - - return $"{text}"; - } - - if (size > -1) - { - if (fontStyle > 0) - { - return $"{text}"; - } - - return $"{text}"; - } - - if (fontStyle > 0) - { - return $"{text}"; - } - - return $"{text}"; - } + public static string Center(this ReadOnlySpan input) => $"
{input}
"; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RawInterpolatedStringHandler Color( - this ReadOnlySpan text, - int color, - int size = -1, - byte fontStyle = 0 - ) + public static string Center(ref RawInterpolatedStringHandler input) { - if (color > -1) - { - return text.Color($"#{color:X6}", size, fontStyle); - } - - return text.Color((ReadOnlySpan)default, size, fontStyle); - } - - [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(); + var str = input.Text.Center(); + input.Clear(); return str; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Color( - this string text, - int 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 textHandler = ((ReadOnlySpan)text).Color(color, size, fontStyle); - var str = textHandler.Text.ToString(); - textHandler.Clear(); + var str = input.Text.Center(color); + input.Clear(); return str; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static string Center( - this string text, int color, int size = -1, byte fontStyle = 0 - ) + public static string Center(ref RawInterpolatedStringHandler input, ReadOnlySpan color) { - var handler = Center((ReadOnlySpan)text, color, size, fontStyle); - var str = handler.Text.ToString(); - handler.Clear(); - + var str = input.Text.Center(color); + input.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(); + 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 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 - ) + public static string Color(ref RawInterpolatedStringHandler input, ReadOnlySpan color) { - 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(); - + var str = input.Text.Color(color); + input.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(); + 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 text) => text.Right(-1); + public static string Right(this string input, int color) => Right(input.AsSpan(), color); [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static RawInterpolatedStringHandler Right( - scoped ref RawInterpolatedStringHandler textHandler, int color = -1, int size = -1, byte fontStyle = 0 - ) + 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 handler = textHandler.Text.Right(color, size, fontStyle); - textHandler.Clear(); - return handler; + var str = input.Text.Right(color); + input.Clear(); + return str; } [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); + 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('<', '>', '&', '"', '\''); @@ -304,4 +213,71 @@ public static class Html builder.Dispose(); return result; } + + public static string Build( + ReadOnlySpan text, ReadOnlySpan color = default, int size = -1, byte fontStyle = 0, + TextAlignment align = TextAlignment.Left + ) + { + 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) + { + builder.Append($" SIZE={size}"); + } + + if (fontStyle > 0) + { + builder.Append($" STYLE={fontStyle}"); + } + builder.Append($">{text}"); + } + else + { + builder.Append(text); + } + + if (align == TextAlignment.Right) + { + builder.Append(""); + } + else if (align == TextAlignment.Center) + { + builder.Append("
"); + } + + return builder.Length; + } } 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/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/Books/BOBGump.cs b/Projects/UOContent/Engines/Bulk Orders/Books/BOBGump.cs index 8dd3073f1..7be389e49 100644 --- a/Projects/UOContent/Engines/Bulk Orders/Books/BOBGump.cs +++ b/Projects/UOContent/Engines/Bulk Orders/Books/BOBGump.cs @@ -157,7 +157,7 @@ public class BOBGump : DynamicGump builder.AddHtmlLocalized(576, 64, 200, 32, 1062227, LabelColor); // Set builder.AddButton(450, 416, 4005, 4007, 4); - builder.AddHtml(485, 416, 120, 20, "Price all"); + builder.AddHtml(485, 416, 120, 20, "Price all"); } } 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/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/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/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/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/Gumps/AdminGump.cs b/Projects/UOContent/Gumps/AdminGump.cs index 00e65887d..7dc944e82 100644 --- a/Projects/UOContent/Gumps/AdminGump.cs +++ b/Projects/UOContent/Gumps/AdminGump.cs @@ -1035,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)); @@ -1237,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"); 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/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/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/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/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/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/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/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/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/Systems/JailSystem/JailRecordGump.cs b/Projects/UOContent/Systems/JailSystem/JailRecordGump.cs index 45b9ce172..2ffe7a9f8 100644 --- a/Projects/UOContent/Systems/JailSystem/JailRecordGump.cs +++ b/Projects/UOContent/Systems/JailSystem/JailRecordGump.cs @@ -38,7 +38,7 @@ public class JailRecordGump : StaticGump builder.AddBackground(0, 0, 330, 300, 9200); builder.AddAlphaRegion(10, 10, 310, 280); - builder.AddHtml(20, 20, 360, 30, "JAIL RECORD"); + 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"); @@ -47,43 +47,43 @@ public class JailRecordGump : StaticGump 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."); + 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}", 0xFFFF00, 5); - builder.SetHtmlText("jailCount", $"Jail Count: {_record.JailCount}", 0xFFFFFF, 5); + 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}", 0xFFFFFF, 5); + builder.SetHtmlText("lastJailed", $"Last Jailed: {_record.LastJailed:yyyy/MM/dd HH:mm}", "#FFFFFF", 5); } else { - builder.SetHtmlText("lastJailed", "Last Jailed: Never", 0xFFFFFF, 5); + builder.SetHtmlText("lastJailed", "Last Jailed: Never", "#FFFFFF", 5); } if (string.IsNullOrWhiteSpace(_record.LastJailReason)) { - builder.SetHtmlText("jailReason", "Jail Reason: None given.", 0xFFFFFF, 4); + builder.SetHtmlText("jailReason", "Jail Reason: None given.", "#FFFFFF", 4); } else { - builder.SetHtmlText("jailReason", $"Jail Reason: {_record.LastJailReason}", 0xFFFFFF, 4); + builder.SetHtmlText("jailReason", $"Jail Reason: {_record.LastJailReason}", "#FFFFFF", 4); } if (_record.IsCurrentlyJailed) { - builder.SetHtmlText("jailStatus", "Status: Currently Jailed", 0xFF6666, 5); - builder.SetHtmlText("jailTime", $"Jail Time: {(_record.JailEndTime - Core.Now).FormatTimeCompact()}", 0xFF6666, 5); + 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", 0x00FF00, 5); + builder.SetHtmlText("jailStatus", "Status: Not Jailed", "#00FF00", 5); builder.SetStringSlot("jailTime", ""); } } 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/version.json b/version.json index de4574c38..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.15.2" + "version": "0.15.3" } From c5bbe0bc909d63b1bf20e00985f6a28852eb4c62 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Tue, 25 Nov 2025 17:35:22 -0800 Subject: [PATCH 41/47] chore: Updates README.md to use stable CDN links for images. --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 01f875145..ee750bb98 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@

- +

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) @@ -42,11 +42,11 @@ ModernUO [![Discord](https://img.shields.io/discord/751317910504603701?logo=disc #### Supported IDEs

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

## Getting Started @@ -105,6 +105,6 @@ Thank you for supporting us! You can find out how by visiting the [sponsors](./S - [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

From 5aed018232c2cf300b432d8a2ee85f873478dd03 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Tue, 25 Nov 2025 20:05:05 -0800 Subject: [PATCH 42/47] chore: Update README with logo link and image sources --- README.md | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index ee750bb98..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) @@ -43,9 +43,9 @@ ModernUO [![Discord](https://img.shields.io/discord/751317910504603701?logo=disc #### Supported IDEs

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

@@ -105,6 +105,5 @@ Thank you for supporting us! You can find out how by visiting the [sponsors](./S - [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

From d3fdb180b305218f4073753c7b24879ec4f92940 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Thu, 27 Nov 2025 11:59:47 -0800 Subject: [PATCH 43/47] fix: Fixes searching multis/clients. Adds missing map enumeration tests (#2278) > [!IMPORTANT] > **Dev Note:** This is an **important** patch as the bug could lead to major issues like: > * Multis/Players disappearing from view or not being counted during game logic. > * World processes (e.g., area checks, targeting) failing to detect entities correctly. > * General stability and correctness concerns for core map functionality. > > **Important Breaking Change**: Multis now properly use the map link list. This means modifying a multi while iterating will cause the server to crash. The crash _is expected_. Please modify/fix code accordingly to create a list using `PooledRefQueue` or `PooledRefList` instead of moving/deleting multis while inside the foreach. ### Summary * Fixes a bug where deleting/moving a multi (boat/house) in some circumstances can use undefined behavior due to unsafe changes to List * Fixes a bug where Multis may not be considered while searching due to a bug causing the sector search to end early. --- .../Tests/Maps/ClientEnumeratorTests.cs | 492 +++++++++++++++++ .../Tests/Maps/ItemEnumeratorTests.cs | 427 +++++++++++++++ .../Tests/Maps/MobileEnumeratorTests.cs | 206 +++++++ .../Tests/Maps/MultiEnumeratorTests.cs | 356 +++++++++++++ .../Tests/Maps/StaticTileEnumeratorTests.cs | 319 +++++++++++ .../Items/BaseMulti.SectorMultiLinkList.cs | 503 ------------------ Projects/Server/Maps/Map.ClientEnumerator.cs | 26 +- Projects/Server/Maps/Map.MultiEnumerator.cs | 146 +++-- Projects/Server/Maps/Map.cs | 19 +- 9 files changed, 1923 insertions(+), 571 deletions(-) create mode 100644 Projects/Server.Tests/Tests/Maps/ClientEnumeratorTests.cs create mode 100644 Projects/Server.Tests/Tests/Maps/ItemEnumeratorTests.cs create mode 100644 Projects/Server.Tests/Tests/Maps/MobileEnumeratorTests.cs create mode 100644 Projects/Server.Tests/Tests/Maps/MultiEnumeratorTests.cs create mode 100644 Projects/Server.Tests/Tests/Maps/StaticTileEnumeratorTests.cs delete mode 100644 Projects/Server/Items/BaseMulti.SectorMultiLinkList.cs diff --git a/Projects/Server.Tests/Tests/Maps/ClientEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/ClientEnumeratorTests.cs new file mode 100644 index 000000000..68a867728 --- /dev/null +++ b/Projects/Server.Tests/Tests/Maps/ClientEnumeratorTests.cs @@ -0,0 +1,492 @@ +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); + } + + 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/ItemEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/ItemEnumeratorTests.cs new file mode 100644 index 000000000..ef37ade80 --- /dev/null +++ b/Projects/Server.Tests/Tests/Maps/ItemEnumeratorTests.cs @@ -0,0 +1,427 @@ +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); + } + } + + 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/MobileEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/MobileEnumeratorTests.cs new file mode 100644 index 000000000..5c7786a92 --- /dev/null +++ b/Projects/Server.Tests/Tests/Maps/MobileEnumeratorTests.cs @@ -0,0 +1,206 @@ +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); + } + } + + private static Mobile CreateMobile(Map map, Point3D location) + { + var mobile = new Mobile((Serial)Utility.RandomMinMax(0x100u, 0xFFFu)); + 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..0f592ca83 --- /dev/null +++ b/Projects/Server.Tests/Tests/Maps/MultiEnumeratorTests.cs @@ -0,0 +1,356 @@ +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 (Exception e) + { + Assert.IsType(e); + 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); + } + } + + 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/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/Maps/Map.ClientEnumerator.cs b/Projects/Server/Maps/Map.ClientEnumerator.cs index ae53010a1..d4dea8e26 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 * * * @@ -76,6 +76,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 +86,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 +102,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 +117,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 +137,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 +175,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 +194,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; diff --git a/Projects/Server/Maps/Map.MultiEnumerator.cs b/Projects/Server/Maps/Map.MultiEnumerator.cs index bc1f7b018..807e351b1 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); @@ -83,6 +81,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 +98,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 +175,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 +214,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() From f2ce860c18867e047afdeff20bc8301cf5350973 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Fri, 28 Nov 2025 10:57:54 -0800 Subject: [PATCH 44/47] feat: Adds Map.GetXByDistance (for tracking skill). Fixes negative range checks. (#2252) ### Summary Adds `XInRangeByDistance` and `XInBoundsByDistance` methods to `Map.cs`: **Item Distance Enumeration:** ```cs ItemDistanceEnumerable GetItemsInRangeByDistance(Point3D p); ItemDistanceEnumerable GetItemsInRangeByDistance(Point3D p, int range); ItemDistanceEnumerable GetItemsInRangeByDistance(Point3D p) where T : Item; ItemDistanceEnumerable GetItemsInRangeByDistance(Point3D p, int range) where T : Item; ItemDistanceEnumerable GetItemsInRangeByDistance(Point2D p); ItemDistanceEnumerable GetItemsInRangeByDistance(Point2D p, int range); ItemDistanceEnumerable GetItemsInRangeByDistance(Point2D p) where T : Item; ItemDistanceEnumerable GetItemsInRangeByDistance(Point2D p, int range) where T : Item; ItemDistanceEnumerable GetItemsInRangeByDistance(int x, int y, int range); ItemDistanceEnumerable GetItemsInRangeByDistance(int x, int y, int range) where T : Item; ItemDistanceEnumerable GetItemsInBoundsByDistance(Rectangle2D bounds, , bool makeBoundsInclusive = false); ItemDistanceEnumerable GetItemsInBoundsByDistance(Rectangle2D bounds, bool makeBoundsInclusive = false) where T : Item; ``` **Mobile Distance Enumeration:** ```cs MobileDistanceEnumerable GetMobilesInRangeByDistance(Point3D p); MobileDistanceEnumerable GetMobilesInRangeByDistance(Point3D p, int range); MobileDistanceEnumerable GetMobilesInRangeByDistance(Point3D p) where T : Mobile; MobileDistanceEnumerable GetMobilesInRangeByDistance(Point3D p, int range) where T : Mobile; MobileDistanceEnumerable GetMobilesInRangeByDistance(Point2D p); MobileDistanceEnumerable GetMobilesInRangeByDistance(Point2D p, int range); MobileDistanceEnumerable GetMobilesInRangeByDistance(Point2D p) where T : Mobile; MobileDistanceEnumerable GetMobilesInRangeByDistance(Point2D p, int range) where T : Mobile; MobileDistanceEnumerable GetMobilesInRangeByDistance(int x, int y, int range); MobileDistanceEnumerable GetMobilesInRangeByDistance(int x, int y, int range) where T : Mobile; MobileDistanceEnumerable GetMobilesInBoundsByDistance(Rectangle2D bounds, bool makeBoundsInclusive = false); MobileDistanceEnumerable GetMobilesInBoundsByDistance(Rectangle2D bounds, bool makeBoundsInclusive = false) where T : Mobile; ``` **Client Distance Enumeration:** ```cs ClientDistanceEnumerable GetClientsInRangeByDistance(Point3D p); ClientDistanceEnumerable GetClientsInRangeByDistance(Point3D p, int range); ClientDistanceEnumerable GetClientsInRangeByDistance(Point2D p); ClientDistanceEnumerable GetClientsInRangeByDistance(Point2D p, int range); ClientDistanceEnumerable GetClientsInRangeByDistance(int x, int y, int range); ClientDistanceEnumerable GetClientsInBoundsByDistance(Rectangle2D bounds, bool makeBoundsInclusive = false); ``` **Example Usage:** How to use `minDistance` to terminate early when all subsequent mobiles in the iteration will be at an increasing min distance. ```csharp var playerLocation = player.Location; const int maxRange = 100; const int maxMobiles = 12; var closestMobiles = new SortedSet(Comparer.Create((x, y) => { var distX = x.GetDistanceToSqrt(playerLocation); var distY = y.GetDistanceToSqrt(playerLocation); int result = distX.CompareTo(distY); if (result == 0) { result = (x?.Serial ?? Serial.MinusOne).CompareTo(y?.Serial ?? Serial.MinusOne); } return result; })); int lastMinDistance = 0; foreach (var (mobile, minDistance) in map.GetMobilesInRangeByDistance(playerLocation, maxRange)) { // Stop if we have enough and distance starts increasing if (closestMobiles.Count >= maxMobiles && minDistance > lastMinDistance) { break; } closestMobiles.Add(mobile); lastMinDistance = minDistance; } // Results are already ordered by proximity foreach (var mobile in closestMobiles) { var actualDistance = mobile.GetDistanceToSqrt(playerLocation); Console.WriteLine($"{mobile.Name}: ActualDist={actualDistance:F2}"); } ``` --- .../Tests/Maps/ClientEnumeratorTests.cs | 57 ++ .../Maps/ItemByDistanceEnumeratorTests.cs | 605 +++++++++++++++++ .../Tests/Maps/ItemEnumeratorTests.cs | 57 ++ .../Maps/MobileByDistanceEnumeratorTests.cs | 608 ++++++++++++++++++ .../Tests/Maps/MobileEnumeratorTests.cs | 57 ++ .../Tests/Maps/MultiEnumeratorTests.cs | 60 +- .../Maps/Map.ClientByDistanceEnumerator.cs | 303 +++++++++ Projects/Server/Maps/Map.ClientEnumerator.cs | 11 +- .../Maps/Map.ItemByDistanceEnumerator.cs | 325 ++++++++++ Projects/Server/Maps/Map.ItemEnumerator.cs | 8 +- .../Maps/Map.MobileByDistanceEnumerator.cs | 320 +++++++++ Projects/Server/Maps/Map.MobileEnumerator.cs | 8 +- Projects/Server/Maps/Map.MultiEnumerator.cs | 8 +- 13 files changed, 2415 insertions(+), 12 deletions(-) create mode 100644 Projects/Server.Tests/Tests/Maps/ItemByDistanceEnumeratorTests.cs create mode 100644 Projects/Server.Tests/Tests/Maps/MobileByDistanceEnumeratorTests.cs create mode 100644 Projects/Server/Maps/Map.ClientByDistanceEnumerator.cs create mode 100644 Projects/Server/Maps/Map.ItemByDistanceEnumerator.cs create mode 100644 Projects/Server/Maps/Map.MobileByDistanceEnumerator.cs diff --git a/Projects/Server.Tests/Tests/Maps/ClientEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/ClientEnumeratorTests.cs index 68a867728..b2eb644ea 100644 --- a/Projects/Server.Tests/Tests/Maps/ClientEnumeratorTests.cs +++ b/Projects/Server.Tests/Tests/Maps/ClientEnumeratorTests.cs @@ -476,6 +476,63 @@ public class ClientEnumeratorTests 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++) diff --git a/Projects/Server.Tests/Tests/Maps/ItemByDistanceEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/ItemByDistanceEnumeratorTests.cs new file mode 100644 index 000000000..be259044f --- /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((Serial)Utility.RandomMinMax(0x100u, 0xFFFu)); + var testItem = new TestItem((Serial)Utility.RandomMinMax(0x100u, 0xFFFu)); + + 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((Serial)Utility.RandomMinMax(0x100u, 0xFFFu)); + 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 index ef37ade80..1a9495008 100644 --- a/Projects/Server.Tests/Tests/Maps/ItemEnumeratorTests.cs +++ b/Projects/Server.Tests/Tests/Maps/ItemEnumeratorTests.cs @@ -408,6 +408,63 @@ public class ItemEnumeratorTests } } + [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); diff --git a/Projects/Server.Tests/Tests/Maps/MobileByDistanceEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/MobileByDistanceEnumeratorTests.cs new file mode 100644 index 000000000..435a53cd8 --- /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((Serial)Utility.RandomMinMax(0x100u, 0xFFFu)); + var npc = new TestMobile((Serial)Utility.RandomMinMax(0x100u, 0xFFFu)); + + 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((Serial)Utility.RandomMinMax(0x100u, 0xFFFu)); + 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 index 5c7786a92..f1c3117eb 100644 --- a/Projects/Server.Tests/Tests/Maps/MobileEnumeratorTests.cs +++ b/Projects/Server.Tests/Tests/Maps/MobileEnumeratorTests.cs @@ -188,6 +188,63 @@ public class MobileEnumeratorTests } } + [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((Serial)Utility.RandomMinMax(0x100u, 0xFFFu)); diff --git a/Projects/Server.Tests/Tests/Maps/MultiEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/MultiEnumeratorTests.cs index 0f592ca83..d99f7bbad 100644 --- a/Projects/Server.Tests/Tests/Maps/MultiEnumeratorTests.cs +++ b/Projects/Server.Tests/Tests/Maps/MultiEnumeratorTests.cs @@ -120,9 +120,8 @@ public class MultiEnumeratorTests { enumerator.MoveNext(); } - catch (Exception e) + catch (InvalidOperationException) { - Assert.IsType(e); exceptionThrown = true; } @@ -330,6 +329,63 @@ public class MultiEnumeratorTests } } + [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(); diff --git a/Projects/Server/Maps/Map.ClientByDistanceEnumerator.cs b/Projects/Server/Maps/Map.ClientByDistanceEnumerator.cs new file mode 100644 index 000000000..6d324cbb3 --- /dev/null +++ b/Projects/Server/Maps/Map.ClientByDistanceEnumerator.cs @@ -0,0 +1,303 @@ +/************************************************************************* + * 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) => + GetClientsInBoundsByDistance(bounds, new Point2D(bounds.X + bounds.Width / 2, bounds.Y + bounds.Height / 2)); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ClientDistanceEnumerable GetClientsInBoundsByDistance(Rectangle2D bounds, bool makeBoundsInclusive) => + 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 = MinDistSqToSectorRect(_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 MinDistSqToSectorRect(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 dx * dx + dy * dy; + } + } +} diff --git a/Projects/Server/Maps/Map.ClientEnumerator.cs b/Projects/Server/Maps/Map.ClientEnumerator.cs index d4dea8e26..921affec9 100644 --- a/Projects/Server/Maps/Map.ClientEnumerator.cs +++ b/Projects/Server/Maps/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) => @@ -232,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; @@ -267,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..698fe8b89 --- /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) => + GetItemsInBoundsByDistance(bounds); + + [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 = MinDistSqToSectorRect(_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 MinDistSqToSectorRect(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 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..a8d42c693 --- /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) => + GetMobilesInBoundsByDistance(bounds); + + [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 = MinDistSqToSectorRect(_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 MinDistSqToSectorRect(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 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 807e351b1..2069d4c94 100644 --- a/Projects/Server/Maps/Map.MultiEnumerator.cs +++ b/Projects/Server/Maps/Map.MultiEnumerator.cs @@ -70,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) => From 06443ba0ca19d4f509cbca4c2a6cb04e5b04ab03 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Fri, 28 Nov 2025 11:01:20 -0800 Subject: [PATCH 45/47] fix: Cleans up the GetXDistance methods. (#2279) --- Projects/Server/Maps/Map.ClientByDistanceEnumerator.cs | 6 +----- Projects/Server/Maps/Map.ItemByDistanceEnumerator.cs | 4 ++-- Projects/Server/Maps/Map.MobileByDistanceEnumerator.cs | 4 ++-- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/Projects/Server/Maps/Map.ClientByDistanceEnumerator.cs b/Projects/Server/Maps/Map.ClientByDistanceEnumerator.cs index 6d324cbb3..18a443c61 100644 --- a/Projects/Server/Maps/Map.ClientByDistanceEnumerator.cs +++ b/Projects/Server/Maps/Map.ClientByDistanceEnumerator.cs @@ -50,11 +50,7 @@ public partial class Map } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ClientDistanceEnumerable GetClientsInBoundsByDistance(Rectangle2D bounds) => - GetClientsInBoundsByDistance(bounds, new Point2D(bounds.X + bounds.Width / 2, bounds.Y + bounds.Height / 2)); - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ClientDistanceEnumerable GetClientsInBoundsByDistance(Rectangle2D bounds, bool makeBoundsInclusive) => + 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)] diff --git a/Projects/Server/Maps/Map.ItemByDistanceEnumerator.cs b/Projects/Server/Maps/Map.ItemByDistanceEnumerator.cs index 698fe8b89..dd022e0c1 100644 --- a/Projects/Server/Maps/Map.ItemByDistanceEnumerator.cs +++ b/Projects/Server/Maps/Map.ItemByDistanceEnumerator.cs @@ -69,8 +69,8 @@ public partial class Map } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ItemDistanceEnumerable GetItemsInBoundsByDistance(Rectangle2D bounds) => - GetItemsInBoundsByDistance(bounds); + public ItemDistanceEnumerable GetItemsInBoundsByDistance(Rectangle2D bounds, bool makeBoundsInclusive = false) => + GetItemsInBoundsByDistance(bounds, makeBoundsInclusive); [MethodImpl(MethodImplOptions.AggressiveInlining)] public ItemDistanceEnumerable GetItemsInBoundsByDistance(Rectangle2D bounds, bool makeBoundsInclusive = false) diff --git a/Projects/Server/Maps/Map.MobileByDistanceEnumerator.cs b/Projects/Server/Maps/Map.MobileByDistanceEnumerator.cs index a8d42c693..d508cd8d0 100644 --- a/Projects/Server/Maps/Map.MobileByDistanceEnumerator.cs +++ b/Projects/Server/Maps/Map.MobileByDistanceEnumerator.cs @@ -69,8 +69,8 @@ public partial class Map } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public MobileDistanceEnumerable GetMobilesInBoundsByDistance(Rectangle2D bounds) => - GetMobilesInBoundsByDistance(bounds); + 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 => From ad26ab6260627b5550c3802de3abea10cf08b3b8 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Fri, 28 Nov 2025 11:56:06 -0800 Subject: [PATCH 46/47] fix: Fixes returning min distance from GetXInRangeByDistance (#2280) --- Projects/Server/Maps/Map.ClientByDistanceEnumerator.cs | 6 +++--- Projects/Server/Maps/Map.ItemByDistanceEnumerator.cs | 6 +++--- Projects/Server/Maps/Map.MobileByDistanceEnumerator.cs | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Projects/Server/Maps/Map.ClientByDistanceEnumerator.cs b/Projects/Server/Maps/Map.ClientByDistanceEnumerator.cs index 18a443c61..64c7b3851 100644 --- a/Projects/Server/Maps/Map.ClientByDistanceEnumerator.cs +++ b/Projects/Server/Maps/Map.ClientByDistanceEnumerator.cs @@ -173,7 +173,7 @@ public partial class Map if (current != null) { - _minDistance = MinDistSqToSectorRect(_center.m_X, _center.m_Y, _currentSectorX, _currentSectorY); + _minDistance = MinDistToSectorSqrt(_center.m_X, _center.m_Y, _currentSectorX, _currentSectorY); } } @@ -266,7 +266,7 @@ public partial class Map } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int MinDistSqToSectorRect(int cx, int cy, int sectorX, int sectorY) + private static int MinDistToSectorSqrt(int cx, int cy, int sectorX, int sectorY) { var x0 = sectorX * SectorSize; var y0 = sectorY * SectorSize; @@ -293,7 +293,7 @@ public partial class Map dy = cy - y1; } - return dx * dx + dy * dy; + return (int)Math.Sqrt(dx * dx + dy * dy); } } } diff --git a/Projects/Server/Maps/Map.ItemByDistanceEnumerator.cs b/Projects/Server/Maps/Map.ItemByDistanceEnumerator.cs index dd022e0c1..74174d9e0 100644 --- a/Projects/Server/Maps/Map.ItemByDistanceEnumerator.cs +++ b/Projects/Server/Maps/Map.ItemByDistanceEnumerator.cs @@ -201,7 +201,7 @@ public partial class Map if (current != null) { - _minDistance = MinDistSqToSectorRect(_center.m_X, _center.m_Y, _currentSectorX, _currentSectorY); + _minDistance = MinDistToSectorSqrt(_center.m_X, _center.m_Y, _currentSectorX, _currentSectorY); } } @@ -292,7 +292,7 @@ public partial class Map } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int MinDistSqToSectorRect(int cx, int cy, int sectorX, int sectorY) + private static int MinDistToSectorSqrt(int cx, int cy, int sectorX, int sectorY) { var x0 = sectorX * SectorSize; var y0 = sectorY * SectorSize; @@ -319,7 +319,7 @@ public partial class Map dy = cy - y1; } - return dx * dx + dy * dy; + return (int)Math.Sqrt(dx * dx + dy * dy); } } } diff --git a/Projects/Server/Maps/Map.MobileByDistanceEnumerator.cs b/Projects/Server/Maps/Map.MobileByDistanceEnumerator.cs index d508cd8d0..71449e550 100644 --- a/Projects/Server/Maps/Map.MobileByDistanceEnumerator.cs +++ b/Projects/Server/Maps/Map.MobileByDistanceEnumerator.cs @@ -196,7 +196,7 @@ public partial class Map if (current != null) { - _minDistance = MinDistSqToSectorRect(_center.m_X, _center.m_Y, _currentSectorX, _currentSectorY); + _minDistance = MinDistToSectorSqrt(_center.m_X, _center.m_Y, _currentSectorX, _currentSectorY); } } @@ -287,7 +287,7 @@ public partial class Map } [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static int MinDistSqToSectorRect(int cx, int cy, int sectorX, int sectorY) + private static int MinDistToSectorSqrt(int cx, int cy, int sectorX, int sectorY) { var x0 = sectorX * SectorSize; var y0 = sectorY * SectorSize; @@ -314,7 +314,7 @@ public partial class Map dy = cy - y1; } - return dx * dx + dy * dy; + return (int)Math.Sqrt(dx * dx + dy * dy); } } } From 5fb0e806c527cfdd19b74e37620b4cea2510da14 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Sat, 29 Nov 2025 09:55:52 -0800 Subject: [PATCH 47/47] feat: Adds proper OSI tracking up to 120 tiles. (#2281) ### Summary Fixes tracking skill to 10 tiles per 10% up to 120 tiles. --- .../Maps/ItemByDistanceEnumeratorTests.cs | 6 +- .../Maps/MobileByDistanceEnumeratorTests.cs | 6 +- .../Tests/Maps/MobileEnumeratorTests.cs | 2 +- Projects/Server/Mobiles/Mobile.Enumerators.cs | 63 ++++ Projects/Server/Mobiles/Mobile.cs | 37 --- .../Tests/Skills/TrackingTests.cs | 299 ++++++++++++++++++ .../UOContent/Skills/Tracking/Tracking.cs | 70 ++-- 7 files changed, 417 insertions(+), 66 deletions(-) create mode 100644 Projects/Server/Mobiles/Mobile.Enumerators.cs create mode 100644 Projects/UOContent.Tests/Tests/Skills/TrackingTests.cs diff --git a/Projects/Server.Tests/Tests/Maps/ItemByDistanceEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/ItemByDistanceEnumeratorTests.cs index be259044f..fe19c76fb 100644 --- a/Projects/Server.Tests/Tests/Maps/ItemByDistanceEnumeratorTests.cs +++ b/Projects/Server.Tests/Tests/Maps/ItemByDistanceEnumeratorTests.cs @@ -215,8 +215,8 @@ public class ItemByDistanceEnumeratorTests var center = new Point3D(700, 700, 0); const int range = 5; - var testItem2 = new TestItem2((Serial)Utility.RandomMinMax(0x100u, 0xFFFu)); - var testItem = new TestItem((Serial)Utility.RandomMinMax(0x100u, 0xFFFu)); + var testItem2 = new TestItem2(World.NewItem); + var testItem = new TestItem(World.NewItem); try { @@ -438,7 +438,7 @@ public class ItemByDistanceEnumeratorTests private static TestItem CreateItem(Map map, Point3D location) { - var Item = new TestItem((Serial)Utility.RandomMinMax(0x100u, 0xFFFu)); + var Item = new TestItem(World.NewItem); Item.MoveToWorld(location, map); return Item; } diff --git a/Projects/Server.Tests/Tests/Maps/MobileByDistanceEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/MobileByDistanceEnumeratorTests.cs index 435a53cd8..0a84da9e6 100644 --- a/Projects/Server.Tests/Tests/Maps/MobileByDistanceEnumeratorTests.cs +++ b/Projects/Server.Tests/Tests/Maps/MobileByDistanceEnumeratorTests.cs @@ -215,8 +215,8 @@ public class MobileByDistanceEnumeratorTests var center = new Point3D(700, 700, 0); const int range = 5; - var player = new TestPlayerMobile((Serial)Utility.RandomMinMax(0x100u, 0xFFFu)); - var npc = new TestMobile((Serial)Utility.RandomMinMax(0x100u, 0xFFFu)); + var player = new TestPlayerMobile(World.NewMobile); + var npc = new TestMobile(World.NewMobile); try { @@ -440,7 +440,7 @@ public class MobileByDistanceEnumeratorTests private static TestMobile CreateMobile(Map map, Point3D location) { - var mobile = new TestMobile((Serial)Utility.RandomMinMax(0x100u, 0xFFFu)); + var mobile = new TestMobile(World.NewMobile); mobile.DefaultMobileInit(); mobile.MoveToWorld(location, map); return mobile; diff --git a/Projects/Server.Tests/Tests/Maps/MobileEnumeratorTests.cs b/Projects/Server.Tests/Tests/Maps/MobileEnumeratorTests.cs index f1c3117eb..5df3d4b69 100644 --- a/Projects/Server.Tests/Tests/Maps/MobileEnumeratorTests.cs +++ b/Projects/Server.Tests/Tests/Maps/MobileEnumeratorTests.cs @@ -247,7 +247,7 @@ public class MobileEnumeratorTests private static Mobile CreateMobile(Map map, Point3D location) { - var mobile = new Mobile((Serial)Utility.RandomMinMax(0x100u, 0xFFFu)); + var mobile = new Mobile(World.NewMobile); mobile.DefaultMobileInit(); mobile.MoveToWorld(location, map); return mobile; 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 855244da6..035d96945 100644 --- a/Projects/Server/Mobiles/Mobile.cs +++ b/Projects/Server/Mobiles/Mobile.cs @@ -8032,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); 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/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;