diff --git a/Projects/UOContent/Skills/Stealing.cs b/Projects/UOContent/Skills/Stealing.cs index f2f270b29..6d4c4226b 100644 --- a/Projects/UOContent/Skills/Stealing.cs +++ b/Projects/UOContent/Skills/Stealing.cs @@ -12,510 +12,496 @@ using Server.Spells.Ninjitsu; using Server.Spells.Seventh; using Server.Targeting; -namespace Server.SkillHandlers +namespace Server.SkillHandlers; + +public static class Stealing { - public static class Stealing + public static bool ClassicMode { get; private set; } + + public static bool SuspendOnMurder { get; private set; } + + public static int MaxWeightToSteal { get; private set; } + + public static bool CanStealContainers { get; private set; } + + public static void Configure() { - public static readonly bool ClassicMode = false; - public static readonly bool SuspendOnMurder = false; - private const int MaxWeightToSteal = 10; + ClassicMode = ServerConfiguration.GetSetting("stealing.classicMode", !Core.AOS); + SuspendOnMurder = ServerConfiguration.GetSetting("stealing.suspendOnMurder", !Core.AOS); + CanStealContainers = ServerConfiguration.GetSetting("stealing.canStealContainers", !Core.AOS); + MaxWeightToSteal = ServerConfiguration.GetSetting("stealing.maxWeightToSteal", 10); + } - public static void Initialize() + public static void Initialize() + { + SkillInfo.Table[33].Callback = OnUse; + } + + public static bool IsInGuild(Mobile m) => m is PlayerMobile mobile && mobile.NpcGuild == NpcGuild.ThievesGuild; + + public static bool IsInnocentTo(Mobile from, Mobile to) => Notoriety.Compute(from, to) == Notoriety.Innocent; + + public static bool IsEmptyHanded(Mobile from) => + from.FindItemOnLayer(Layer.OneHanded) == null && from.FindItemOnLayer(Layer.TwoHanded) == null; + + public static TimeSpan OnUse(Mobile m) + { + if (!IsEmptyHanded(m)) { - SkillInfo.Table[33].Callback = OnUse; + m.SendLocalizedMessage(1005584); // Both hands must be free to steal. + } + else if (m.Region.IsPartOf()) + { + m.SendMessage("You may not steal in this area."); + } + else + { + m.Target = new StealingTarget(m); + m.RevealingAction(); + + m.SendLocalizedMessage(502698); // Which item do you want to steal? } - public static bool IsInGuild(Mobile m) => m is PlayerMobile mobile && mobile.NpcGuild == NpcGuild.ThievesGuild; + return TimeSpan.FromSeconds(10.0); + } - public static bool IsInnocentTo(Mobile from, Mobile to) => Notoriety.Compute(from, to) == Notoriety.Innocent; + private class StealingTarget : Target + { + private readonly Mobile _thief; - public static bool IsEmptyHanded(Mobile from) + public StealingTarget(Mobile thief) : base(1, false, TargetFlags.None) { - if (from.FindItemOnLayer(Layer.OneHanded) != null) - { - return false; - } - - if (from.FindItemOnLayer(Layer.TwoHanded) != null) - { - return false; - } - - return true; + _thief = thief; + AllowNonlocal = true; } - public static TimeSpan OnUse(Mobile m) + private Item TryStealItem(Item toSteal, ref bool caught) { - if (!IsEmptyHanded(m)) + Item stolen = null; + + var root = toSteal.RootParent; + var mobRoot = root as Mobile; + var rootIsPlayer = mobRoot?.Player == true; + + StealableArtifacts.StealableInstance si = toSteal.Parent == null || !toSteal.Movable + ? StealableArtifacts.GetStealableInstance(toSteal) + : null; + + if (!IsEmptyHanded(_thief)) { - m.SendLocalizedMessage(1005584); // Both hands must be free to steal. + _thief.SendLocalizedMessage(1005584); // Both hands must be free to steal. } - else if (m.Region.IsPartOf()) + else if (_thief.Region.IsPartOf()) { - m.SendMessage("You may not steal in this area."); + _thief.SendMessage("You may not steal in this area."); } - else + else if ((_thief as PlayerMobile)?.Young == true && (rootIsPlayer || mobRoot is BaseCreature)) { - m.Target = new StealingTarget(m); - m.RevealingAction(); - - m.SendLocalizedMessage(502698); // Which item do you want to steal? + _thief.SendLocalizedMessage(502700); // You cannot steal from people or monsters right now. Practice on chests and barrels. } - - return TimeSpan.FromSeconds(10.0); - } - - private class StealingTarget : Target - { - private readonly Mobile m_Thief; - - public StealingTarget(Mobile thief) : base(1, false, TargetFlags.None) + else if (rootIsPlayer && !IsInGuild(_thief)) { - m_Thief = thief; - AllowNonlocal = true; + _thief.SendLocalizedMessage(1005596); // You must be in the thieves guild to steal from other players. } - - private Item TryStealItem(Item toSteal, ref bool caught) + else if (SuspendOnMurder && rootIsPlayer && IsInGuild(_thief) && _thief.Kills > 0) { - Item stolen = null; + _thief.SendLocalizedMessage(502706); // You are currently suspended from the thieves guild. + } + else if ((mobRoot as PlayerMobile)?.Young == true) + { + _thief.SendLocalizedMessage(502699); // You cannot steal from the Young. + } + else if ((root as BaseVendor)?.IsInvulnerable == true) + { + _thief.SendLocalizedMessage(1005598); // You can't steal from shopkeepers. + } + else if (root is PlayerVendor) + { + _thief.SendLocalizedMessage(502709); // You can't steal from vendors. + } + else if (!_thief.CanSee(toSteal)) + { + _thief.SendLocalizedMessage(500237); // Target can not be seen. + } + else if (toSteal is Sigil sig) + { + var pl = PlayerState.Find(_thief); + var faction = pl?.Faction; - var root = toSteal.RootParent; - var mobRoot = root as Mobile; - var rootIsPlayer = mobRoot?.Player == true; - - StealableArtifacts.StealableInstance si = toSteal.Parent == null || !toSteal.Movable - ? StealableArtifacts.GetStealableInstance(toSteal) - : null; - - if (!IsEmptyHanded(m_Thief)) + if (!_thief.InRange(sig.GetWorldLocation(), 1)) { - m_Thief.SendLocalizedMessage(1005584); // Both hands must be free to steal. + _thief.SendLocalizedMessage(502703); // You must be standing next to an item to steal it. } - else if (m_Thief.Region.IsPartOf()) + else if (root != null) // not on the ground { - m_Thief.SendMessage("You may not steal in this area."); + _thief.SendLocalizedMessage(502710); // You can't steal that! } - else if ((m_Thief as PlayerMobile)?.Young == true && (rootIsPlayer || mobRoot is BaseCreature)) + else if (faction == null) { - m_Thief.SendLocalizedMessage(502700); // You cannot steal from people or monsters right now. Practice on chests and barrels. + _thief.SendLocalizedMessage(1005588); // You must join a faction to do that } - else if (rootIsPlayer && !IsInGuild(m_Thief)) + else if (!_thief.CanBeginAction()) { - m_Thief.SendLocalizedMessage(1005596); // You must be in the thieves guild to steal from other players. + _thief.SendLocalizedMessage(1010581); // You cannot steal the sigil when you are incognito } - else if (SuspendOnMurder && rootIsPlayer && IsInGuild(m_Thief) && m_Thief.Kills > 0) + else if (DisguisePersistence.IsDisguised(_thief)) { - m_Thief.SendLocalizedMessage(502706); // You are currently suspended from the thieves guild. + _thief.SendLocalizedMessage(1010583); // You cannot steal the sigil while disguised } - else if ((mobRoot as PlayerMobile)?.Young == true) + else if (!_thief.CanBeginAction()) { - m_Thief.SendLocalizedMessage(502699); // You cannot steal from the Young. + _thief.SendLocalizedMessage(1010582); // You cannot steal the sigil while polymorphed } - else if ((root as BaseVendor)?.IsInvulnerable == true) + else if (TransformationSpellHelper.UnderTransformation(_thief)) { - m_Thief.SendLocalizedMessage(1005598); // You can't steal from shopkeepers. + _thief.SendLocalizedMessage(1061622); // You cannot steal the sigil while in that form. } - else if (root is PlayerVendor) + else if (AnimalForm.UnderTransformation(_thief)) { - m_Thief.SendLocalizedMessage(502709); // You can't steal from vendors. + _thief.SendLocalizedMessage(1063222); // You cannot steal the sigil while mimicking an animal. } - else if (!m_Thief.CanSee(toSteal)) + else if (pl.IsLeaving) { - m_Thief.SendLocalizedMessage(500237); // Target can not be seen. + // You are currently quitting a faction and cannot steal the town sigil + _thief.SendLocalizedMessage(1005589); } - else if (toSteal is Sigil sig) + else if (sig.IsBeingCorrupted && sig.LastMonolith.Faction == faction) { - var pl = PlayerState.Find(m_Thief); - var faction = pl?.Faction; - - if (!m_Thief.InRange(sig.GetWorldLocation(), 1)) - { - m_Thief.SendLocalizedMessage(502703); // You must be standing next to an item to steal it. - } - else if (root != null) // not on the ground - { - m_Thief.SendLocalizedMessage(502710); // You can't steal that! - } - else if (faction != null) - { - if (!m_Thief.CanBeginAction()) - { - m_Thief.SendLocalizedMessage(1010581); // You cannot steal the sigil when you are incognito - } - else if (DisguisePersistence.IsDisguised(m_Thief)) - { - m_Thief.SendLocalizedMessage(1010583); // You cannot steal the sigil while disguised - } - else if (!m_Thief.CanBeginAction()) - { - m_Thief.SendLocalizedMessage(1010582); // You cannot steal the sigil while polymorphed - } - else if (TransformationSpellHelper.UnderTransformation(m_Thief)) - { - m_Thief.SendLocalizedMessage(1061622); // You cannot steal the sigil while in that form. - } - else if (AnimalForm.UnderTransformation(m_Thief)) - { - m_Thief.SendLocalizedMessage(1063222); // You cannot steal the sigil while mimicking an animal. - } - else if (pl.IsLeaving) - { - // You are currently quitting a faction and cannot steal the town sigil - m_Thief.SendLocalizedMessage(1005589); - } - else if (sig.IsBeingCorrupted && sig.LastMonolith.Faction == faction) - { - m_Thief.SendLocalizedMessage(1005590); // You cannot steal your own sigil - } - else if (sig.IsPurifying) - { - m_Thief.SendLocalizedMessage(1005592); // You cannot steal this sigil until it has been purified - } - else if (m_Thief.CheckTargetSkill(SkillName.Stealing, toSteal, 80.0, 80.0)) - { - if (Sigil.ExistsOn(m_Thief)) - { - // The sigil has gone back to its home location because you already have a sigil. - m_Thief.SendLocalizedMessage(1010258); - } - else if (m_Thief.Backpack?.CheckHold(m_Thief, sig, false, true) != true) - { - // The sigil has gone home because your backpack is full - m_Thief.SendLocalizedMessage(1010259); - } - else - { - if (sig.IsBeingCorrupted) - { - sig.GraceStart = Core.Now; // begin grace period - } - - m_Thief.SendLocalizedMessage(1010586); // YOU STOLE THE SIGIL!!! (woah, calm down now) - - if (sig.LastMonolith?.Sigil != null) - { - sig.LastMonolith.Sigil = null; - sig.LastStolen = Core.Now; - } - - return sig; - } - } - else - { - m_Thief.SendLocalizedMessage(1005594); // You do not have enough skill to steal the sigil - } - } - else - { - m_Thief.SendLocalizedMessage(1005588); // You must join a faction to do that - } + _thief.SendLocalizedMessage(1005590); // You cannot steal your own sigil } - else if (m_Thief.Backpack?.CheckHold(m_Thief, toSteal, false, true) != true) + else if (sig.IsPurifying) { - m_Thief.SendLocalizedMessage(1048147); // Your backpack can't hold anything else. + _thief.SendLocalizedMessage(1005592); // You cannot steal this sigil until it has been purified } - else if (si == null && (toSteal.Parent == null || !toSteal.Movable)) + else if (!_thief.CheckTargetSkill(SkillName.Stealing, toSteal, 80.0, 80.0)) { - m_Thief.SendLocalizedMessage(502710); // You can't steal that! + _thief.SendLocalizedMessage(1005594); // You do not have enough skill to steal the sigil } - else if (toSteal.LootType == LootType.Newbied || toSteal.CheckBlessed(mobRoot)) + else if (Sigil.ExistsOn(_thief)) { - m_Thief.SendLocalizedMessage(502710); // You can't steal that! + // The sigil has gone back to its home location because you already have a sigil. + _thief.SendLocalizedMessage(1010258); } - else if (Core.AOS && si == null && toSteal is Container) + else if (_thief.Backpack?.CheckHold(_thief, sig, false, true) != true) { - m_Thief.SendLocalizedMessage(502710); // You can't steal that! - } - else if (!m_Thief.InRange(toSteal.GetWorldLocation(), 1)) - { - m_Thief.SendLocalizedMessage(502703); // You must be standing next to an item to steal it. - } - else if (si != null && m_Thief.Skills.Stealing.Value < 100.0) - { - // You're not skilled enough to attempt the theft of this item. - m_Thief.SendLocalizedMessage(1060025, "", 0x66D); - } - else if (toSteal.Parent is Mobile) - { - m_Thief.SendLocalizedMessage(1005585); // You cannot steal items which are equipped. - } - else if (root == m_Thief) - { - m_Thief.SendLocalizedMessage(502704); // You catch yourself red-handed. - } - else if (mobRoot?.AccessLevel > AccessLevel.Player) - { - m_Thief.SendLocalizedMessage(502710); // You can't steal that! - } - else if (mobRoot != null && !m_Thief.CanBeHarmful(mobRoot)) - { - } - else if (root is Corpse) - { - m_Thief.SendLocalizedMessage(502710); // You can't steal that! + // The sigil has gone home because your backpack is full + _thief.SendLocalizedMessage(1010259); } else { - var w = toSteal.Weight + toSteal.TotalWeight; - - if (w > MaxWeightToSteal) + if (sig.IsBeingCorrupted) { - // This item is too heavy to steal from someone's backpack. - m_Thief.SendLocalizedMessage(502722); + sig.GraceStart = Core.Now; // begin grace period } - else + + _thief.SendLocalizedMessage(1010586); // YOU STOLE THE SIGIL!!! (woah, calm down now) + + if (sig.LastMonolith?.Sigil != null) { - if (toSteal.Stackable && toSteal.Amount > 1) + sig.LastMonolith.Sigil = null; + sig.LastStolen = Core.Now; + } + + return sig; + } + } + else if (_thief.Backpack?.CheckHold(_thief, toSteal, false, true) != true) + { + _thief.SendLocalizedMessage(1048147); // Your backpack can't hold anything else. + } + else if (si == null && (toSteal.Parent == null || !toSteal.Movable) || toSteal.LootType == LootType.Newbied || + toSteal.CheckBlessed(mobRoot) || !CanStealContainers && si == null && toSteal is Container) + { + _thief.SendLocalizedMessage(502710); // You can't steal that! + } + else if (!_thief.InRange(toSteal.GetWorldLocation(), 1)) + { + _thief.SendLocalizedMessage(502703); // You must be standing next to an item to steal it. + } + else if (si != null && _thief.Skills.Stealing.Value < 100.0) + { + // You're not skilled enough to attempt the theft of this item. + _thief.SendLocalizedMessage(1060025, "", 0x66D); + } + else if (toSteal.Parent is Mobile) + { + _thief.SendLocalizedMessage(1005585); // You cannot steal items which are equipped. + } + else if (root == _thief) + { + _thief.SendLocalizedMessage(502704); // You catch yourself red-handed. + } + else if (mobRoot?.AccessLevel > AccessLevel.Player) + { + _thief.SendLocalizedMessage(502710); // You can't steal that! + } + else if (mobRoot != null && !_thief.CanBeHarmful(mobRoot)) + { + } + else if (root is Corpse) + { + _thief.SendLocalizedMessage(502710); // You can't steal that! + } + else + { + var w = toSteal.Weight + toSteal.TotalWeight; + + if (w > MaxWeightToSteal) + { + // This item is too heavy to steal from someone's backpack. + _thief.SendLocalizedMessage(502722); + } + else + { + if (toSteal.Stackable && toSteal.Amount > 1) + { + var maxAmount = Math.Clamp( + (int)(_thief.Skills.Stealing.Value / 10.0 / toSteal.Weight), + 1, + toSteal.Amount + ); + + var amount = Utility.RandomMinMax(1, maxAmount); + + if (amount >= toSteal.Amount) { - var maxAmount = Math.Clamp( - (int)(m_Thief.Skills.Stealing.Value / 10.0 / toSteal.Weight), - 1, - toSteal.Amount - ); + var pileWeight = (int)Math.Ceiling(toSteal.Weight * toSteal.Amount); + pileWeight *= 10; - var amount = Utility.RandomMinMax(1, maxAmount); - - if (amount >= toSteal.Amount) - { - var pileWeight = (int)Math.Ceiling(toSteal.Weight * toSteal.Amount); - pileWeight *= 10; - - if (m_Thief.CheckTargetSkill( + if (_thief.CheckTargetSkill( SkillName.Stealing, toSteal, pileWeight - 22.5, pileWeight + 27.5 )) - { - stolen = toSteal; - } - } - else - { - var pileWeight = (int)Math.Ceiling(toSteal.Weight * amount); - pileWeight *= 10; - - if (m_Thief.CheckTargetSkill( - SkillName.Stealing, - toSteal, - pileWeight - 22.5, - pileWeight + 27.5 - )) - { - stolen = Mobile.LiftItemDupe(toSteal, toSteal.Amount - amount) ?? toSteal; - } - } - } - else - { - var iw = (int)Math.Ceiling(w); - iw *= 10; - - if (m_Thief.CheckTargetSkill(SkillName.Stealing, toSteal, iw - 22.5, iw + 27.5)) { stolen = toSteal; } } - - if (stolen != null) - { - m_Thief.SendLocalizedMessage(502724); // You successfully steal the item. - - if (si != null) - { - toSteal.Movable = true; - si.Item = null; - } - } else { - m_Thief.SendLocalizedMessage(502723); // You fail to steal the item. - } + var pileWeight = (int)Math.Ceiling(toSteal.Weight * amount); + pileWeight *= 10; - caught = m_Thief.Skills.Stealing.Value < Utility.Random(150); - } - } - - return stolen; - } - - protected override void OnTarget(Mobile from, object target) - { - from.RevealingAction(); - - Item stolen = null; - IEntity root = null; - var caught = false; - - if (target is Item item) - { - root = item.RootParent; - stolen = TryStealItem(item, ref caught); - } - else if (target is Mobile mobile) - { - var pack = mobile.Backpack; - - if (pack?.Items.Count > 0) - { - root = mobile; - stolen = TryStealItem(pack.Items.RandomElement(), ref caught); - } - } - else - { - m_Thief.SendLocalizedMessage(502710); // You can't steal that! - } - - var mobRoot = root as Mobile; - - if (stolen != null) - { - from.AddToBackpack(stolen); - - if (!(stolen is Container || stolen.Stackable)) - { - StolenItem.Add(stolen, m_Thief, mobRoot); - } - } - - var corpse = root as Corpse; - - if (caught) - { - if (root == null || corpse?.IsCriminalAction(m_Thief) == true) - { - m_Thief.CriminalAction(false); - } - else if (mobRoot != null) - { - if (!IsInGuild(mobRoot) && IsInnocentTo(m_Thief, mobRoot)) - { - m_Thief.CriminalAction(false); - } - - var message = $"You notice {m_Thief.Name} trying to steal from {mobRoot.Name}."; - - foreach (var ns in m_Thief.GetClientsInRange(8)) - { - if (ns.Mobile != m_Thief) + if (_thief.CheckTargetSkill( + SkillName.Stealing, + toSteal, + pileWeight - 22.5, + pileWeight + 27.5 + )) { - ns.Mobile.SendMessage(message); + stolen = Mobile.LiftItemDupe(toSteal, toSteal.Amount - amount) ?? toSteal; } } } - } - else if (corpse?.IsCriminalAction(m_Thief) == true) - { - m_Thief.CriminalAction(false); - } - - if (mobRoot?.Player == true && m_Thief is PlayerMobile pm && - IsInnocentTo(pm, mobRoot) && !IsInGuild(mobRoot)) - { - pm.PermaFlags.Add(mobRoot); - pm.Delta(MobileDelta.Noto); - } - } - } - } - - public class StolenItem - { - public static readonly TimeSpan StealTime = TimeSpan.FromMinutes(2.0); - - private static readonly Queue m_Queue = new(); - - public StolenItem(Item stolen, Mobile thief, Mobile victim) - { - Stolen = stolen; - Thief = thief; - Victim = victim; - - Expires = Core.Now + StealTime; - } - - public Item Stolen { get; } - - public Mobile Thief { get; } - - public Mobile Victim { get; } - - public DateTime Expires { get; private set; } - - public bool IsExpired => Core.Now >= Expires; - - public static void Add(Item item, Mobile thief, Mobile victim) - { - Clean(); - - m_Queue.Enqueue(new StolenItem(item, thief, victim)); - } - - public static bool IsStolen(Item item) - { - Mobile victim = null; - - return IsStolen(item, ref victim); - } - - public static bool IsStolen(Item item, ref Mobile victim) - { - Clean(); - - foreach (var si in m_Queue) - { - if (si.Stolen == item && !si.IsExpired) - { - victim = si.Victim; - return true; - } - } - - return false; - } - - [OnEvent(nameof(PlayerMobile.PlayerDeathEvent))] - public static void ReturnOnDeath(Mobile killed) - { - Clean(); - - var corpse = killed.Corpse; - - foreach (var si in m_Queue) - { - if (si.Stolen.RootParent == corpse && si.Victim != null && !si.IsExpired) - { - if (si.Victim.AddToBackpack(si.Stolen)) - { - si.Victim.SendLocalizedMessage(1010464); // the item that was stolen is returned to you. - } else { - si.Victim.SendLocalizedMessage(1010463); // the item that was stolen from you falls to the ground. + var iw = (int)Math.Ceiling(w); + iw *= 10; + + if (_thief.CheckTargetSkill(SkillName.Stealing, toSteal, iw - 22.5, iw + 27.5)) + { + stolen = toSteal; + } } - si.Expires = Core.Now; // such a hack + if (stolen != null) + { + _thief.SendLocalizedMessage(502724); // You successfully steal the item. + + if (si != null) + { + toSteal.Movable = true; + si.Item = null; + } + } + else + { + _thief.SendLocalizedMessage(502723); // You fail to steal the item. + } + + caught = _thief.Skills.Stealing.Value < Utility.Random(150); } } + + return stolen; } - public static void Clean() + protected override void OnTarget(Mobile from, object target) { - while (m_Queue.Count > 0) - { - var si = m_Queue.Peek(); + from.RevealingAction(); - if (si.IsExpired) + Item stolen = null; + IEntity root = null; + var caught = false; + + if (target is Item item) + { + root = item.RootParent; + stolen = TryStealItem(item, ref caught); + } + else if (target is Mobile mobile) + { + var pack = mobile.Backpack; + + if (pack?.Items.Count > 0) { - m_Queue.Dequeue(); + root = mobile; + stolen = TryStealItem(pack.Items.RandomElement(), ref caught); } - else + } + else + { + _thief.SendLocalizedMessage(502710); // You can't steal that! + } + + var mobRoot = root as Mobile; + + if (stolen != null) + { + from.AddToBackpack(stolen); + + if (!(stolen is Container || stolen.Stackable)) { - break; + StolenItem.Add(stolen, _thief, mobRoot); } } + + var corpse = root as Corpse; + + if (caught) + { + if (root == null || corpse?.IsCriminalAction(_thief) == true) + { + _thief.CriminalAction(false); + } + else if (mobRoot != null) + { + if (!IsInGuild(mobRoot) && IsInnocentTo(_thief, mobRoot)) + { + _thief.CriminalAction(false); + } + + var message = $"You notice {_thief.Name} trying to steal from {mobRoot.Name}."; + + foreach (var ns in _thief.GetClientsInRange(8)) + { + if (ns.Mobile != _thief) + { + ns.Mobile.SendMessage(message); + } + } + } + } + else if (corpse?.IsCriminalAction(_thief) == true) + { + _thief.CriminalAction(false); + } + + if (mobRoot?.Player == true && _thief is PlayerMobile pm && + IsInnocentTo(pm, mobRoot) && !IsInGuild(mobRoot)) + { + pm.PermaFlags.Add(mobRoot); + pm.Delta(MobileDelta.Noto); + } + } + } +} + +public class StolenItem +{ + public static readonly TimeSpan StealTime = TimeSpan.FromMinutes(2.0); + + private static readonly Queue _queue = []; + + public StolenItem(Item stolen, Mobile thief, Mobile victim) + { + Stolen = stolen; + Thief = thief; + Victim = victim; + + Expires = Core.Now + StealTime; + } + + public Item Stolen { get; } + + public Mobile Thief { get; } + + public Mobile Victim { get; } + + public DateTime Expires { get; private set; } + + public bool IsExpired => Core.Now >= Expires; + + public static void Add(Item item, Mobile thief, Mobile victim) + { + Clean(); + + _queue.Enqueue(new StolenItem(item, thief, victim)); + } + + public static bool IsStolen(Item item) + { + Mobile victim = null; + + return IsStolen(item, ref victim); + } + + public static bool IsStolen(Item item, ref Mobile victim) + { + Clean(); + + foreach (var si in _queue) + { + if (si.Stolen == item && !si.IsExpired) + { + victim = si.Victim; + return true; + } + } + + return false; + } + + [OnEvent(nameof(PlayerMobile.PlayerDeathEvent))] + public static void ReturnOnDeath(Mobile killed) + { + Clean(); + + var corpse = killed.Corpse; + + foreach (var si in _queue) + { + if (si.Stolen.RootParent != corpse || si.Victim == null || si.IsExpired) + { + continue; + } + + if (si.Victim.AddToBackpack(si.Stolen)) + { + si.Victim.SendLocalizedMessage(1010464); // the item that was stolen is returned to you. + } + else + { + si.Victim.SendLocalizedMessage(1010463); // the item that was stolen from you falls to the ground. + } + + si.Expires = Core.Now; // such a hack + } + } + + public static void Clean() + { + while (_queue.Count > 0) + { + var si = _queue.Peek(); + + if (!si.IsExpired) + { + break; + } + + _queue.Dequeue(); } } }