diff --git a/Projects/UOContent/Items/Misc/Corpses/Corpse.cs b/Projects/UOContent/Items/Misc/Corpses/Corpse.cs
index 514157fc5..641413096 100644
--- a/Projects/UOContent/Items/Misc/Corpses/Corpse.cs
+++ b/Projects/UOContent/Items/Misc/Corpses/Corpse.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using ModernUO.Serialization;
using Server.ContextMenus;
using Server.Engines.PartySystem;
using Server.Engines.Quests.Doom;
@@ -9,445 +10,331 @@ using Server.Misc;
using Server.Mobiles;
using Server.Network;
-namespace Server.Items
+namespace Server.Items;
+
+public interface IDevourer
{
- public interface IDevourer
+ bool Devour(Corpse corpse);
+}
+
+[Flags]
+public enum CorpseFlag
+{
+ None = 0x00000000,
+
+ ///
+ /// Has this corpse been carved?
+ ///
+ Carved = 0x00000001,
+
+ ///
+ /// If true, this corpse will not turn into bones
+ ///
+ NoBones = 0x00000002,
+
+ ///
+ /// If true, the corpse has turned into bones
+ ///
+ IsBones = 0x00000004,
+
+ ///
+ /// Has this corpse yet been visited by a taxidermist?
+ ///
+ VisitedByTaxidermist = 0x00000008,
+
+ ///
+ /// Has this corpse yet been used to channel spiritual energy? (AOS Spirit Speak)
+ ///
+ Channeled = 0x00000010,
+
+ ///
+ /// Was the owner criminal when he died?
+ ///
+ Criminal = 0x00000020,
+
+ ///
+ /// Has this corpse been animated?
+ ///
+ Animated = 0x00000040,
+
+ ///
+ /// Has this corpse been self looted?
+ ///
+ SelfLooted = 0x00000080
+}
+
+[SerializationGenerator(13, false)]
+public partial class Corpse : Container, ICarvable
+{
+ public static readonly TimeSpan MonsterLootRightSacrifice = TimeSpan.FromMinutes(2.0);
+
+ private static TimeSpan InstancedCorpseTime = TimeSpan.FromMinutes(3.0);
+
+ private static TimeSpan _defaultDecayTime = TimeSpan.FromMinutes(7.0);
+ private static TimeSpan _boneDecayTime = TimeSpan.FromMinutes(7.0);
+
+ private IDevourer _devourer; // The creature that devoured this corpse
+
+ private Dictionary- _instancedItems;
+
+ [SerializableField(0)]
+ private List
- _restoreEquip;
+
+ [SerializableField(1)]
+ private CorpseFlag _flags;
+
+ [DeltaDateTime]
+ [SerializableField(2)]
+ [SerializedCommandProperty(AccessLevel.GameMaster)]
+ private DateTime _timeOfDeath;
+
+ [SerializableField(3, getter: "private", setter: "private")]
+ private Dictionary
- _restoreTable;
+
+ [TimerDrift]
+ [SerializableField(4, getter: "private", setter: "private")]
+ private Timer _decayTimer;
+
+ [DeserializeTimerField(4)]
+ private void DeserializeDecayTimer(TimeSpan delay) => BeginDecay(delay);
+
+ [SerializableField(5, setter: "private")]
+ private HashSet _looters;
+
+ [SerializableField(6, setter: "private")]
+ [SerializedCommandProperty(AccessLevel.GameMaster)]
+ private Mobile _killer;
+
+ [SerializableField(7, setter: "private")]
+ private List _aggressors;
+
+ [SerializableField(8, setter: "private")]
+ [SerializedCommandProperty(AccessLevel.GameMaster)]
+ private Mobile _owner;
+
+ // Value of the CorpseNameAttribute attached to the owner when he died -or- null if the owner had no CorpseNameAttribute; use "the remains of ~name~"
+ [SerializableField(9, getter: "private", setter: "private")]
+ private string _corpseName;
+
+ [SerializableField(10, setter: "private")]
+ [SerializedCommandProperty(AccessLevel.GameMaster)]
+ private AccessLevel _accessLevel;
+
+ [SerializableField(11, setter: "private")]
+ private Guild _guild;
+
+ [SerializableField(12)]
+ [SerializedCommandProperty(AccessLevel.GameMaster)]
+ private int _kills;
+
+ [SerializableField(13, setter: "private")]
+ [SerializedCommandProperty(AccessLevel.GameMaster)]
+ private List
- _equipItems;
+
+ // Why was this public?
+ // public override bool IsPublicContainer => true;
+
+ public Corpse(Mobile owner, List
- equipItems) : this(owner, null, null, equipItems)
{
- bool Devour(Corpse corpse);
}
- [Flags]
- public enum CorpseFlag
+ public Corpse(Mobile owner, HairInfo hair, FacialHairInfo facialhair, List
- equipItems)
+ : base(0x2006)
{
- None = 0x00000000,
+ // To suppress console warnings, stackable must be true
+ Stackable = true;
+ Amount = owner.Body; // protocol defines that for itemid 0x2006, amount=body
+ Stackable = false;
- ///
- /// Has this corpse been carved?
- ///
- Carved = 0x00000001,
+ Movable = false;
+ Hue = owner.Hue;
+ Direction = owner.Direction;
+ Name = owner.Name;
- ///
- /// If true, this corpse will not turn into bones
- ///
- NoBones = 0x00000002,
+ _owner = owner;
- ///
- /// If true, the corpse has turned into bones
- ///
- IsBones = 0x00000004,
+ _corpseName = GetCorpseName(owner);
- ///
- /// Has this corpse yet been visited by a taxidermist?
- ///
- VisitedByTaxidermist = 0x00000008,
+ _timeOfDeath = Core.Now;
- ///
- /// Has this corpse yet been used to channel spiritual energy? (AOS Spirit Speak)
- ///
- Channeled = 0x00000010,
+ _accessLevel = owner.AccessLevel;
+ _guild = owner.Guild as Guild;
+ _kills = owner.Kills;
+ SetFlag(CorpseFlag.Criminal, owner.Criminal);
- ///
- /// Was the owner criminal when he died?
- ///
- Criminal = 0x00000020,
+ Hair = hair;
+ FacialHair = facialhair;
- ///
- /// Has this corpse been animated?
- ///
- Animated = 0x00000040,
+ // This corpse does not turn to bones if: the owner is not a player
+ SetFlag(CorpseFlag.NoBones, !owner.Player);
- ///
- /// Has this corpse been self looted?
- ///
- SelfLooted = 0x00000080
+ _looters = new HashSet();
+ _equipItems = equipItems;
+
+ _aggressors = new List(owner.Aggressors.Count + owner.Aggressed.Count);
+
+ var isBaseCreature = owner is BaseCreature;
+
+ var lastTime = TimeSpan.MaxValue;
+
+ for (var i = 0; i < owner.Aggressors.Count; ++i)
+ {
+ var info = owner.Aggressors[i];
+
+ if (Core.Now - info.LastCombatTime < lastTime)
+ {
+ _killer = info.Attacker;
+ lastTime = Core.Now - info.LastCombatTime;
+ }
+
+ if (!isBaseCreature && !info.CriminalAggression)
+ {
+ _aggressors.Add(info.Attacker);
+ }
+ }
+
+ for (var i = 0; i < owner.Aggressed.Count; ++i)
+ {
+ var info = owner.Aggressed[i];
+
+ if (Core.Now - info.LastCombatTime < lastTime)
+ {
+ _killer = info.Defender;
+ lastTime = Core.Now - info.LastCombatTime;
+ }
+
+ if (!isBaseCreature)
+ {
+ _aggressors.Add(info.Defender);
+ }
+ }
+
+ if (isBaseCreature)
+ {
+ var bc = (BaseCreature)owner;
+
+ var master = bc.GetMaster();
+ if (master != null)
+ {
+ _aggressors.Add(master);
+ }
+
+ var rights = BaseCreature.GetLootingRights(bc.DamageEntries, bc.HitsMax);
+ for (var i = 0; i < rights.Count; ++i)
+ {
+ var ds = rights[i];
+
+ if (ds.m_HasRight)
+ {
+ _aggressors.Add(ds.m_Mobile);
+ }
+ }
+ }
+
+ BeginDecay(_defaultDecayTime);
+
+ DevourCorpse();
}
- public class Corpse : Container, ICarvable
+ [CommandProperty(AccessLevel.GameMaster)]
+ public virtual bool InstancedCorpse => Core.SE && Core.Now < TimeOfDeath + InstancedCorpseTime;
+
+ public override bool IsDecoContainer => false;
+
+ public override bool DisplayWeight => false;
+
+ // Name of the first PlayerMobile who used Forensic Evaluation on the corpse
+ [CommandProperty(AccessLevel.GameMaster)]
+ public string Forensicist { get; set; }
+
+ public HairInfo Hair { get; }
+
+ public FacialHairInfo FacialHair { get; }
+
+ [CommandProperty(AccessLevel.GameMaster)]
+ public bool IsBones => GetFlag(CorpseFlag.IsBones);
+
+ [CommandProperty(AccessLevel.GameMaster)]
+ public bool Devoured => _devourer != null;
+
+ [CommandProperty(AccessLevel.GameMaster)]
+ public bool Carved
{
- public static readonly TimeSpan MonsterLootRightSacrifice = TimeSpan.FromMinutes(2.0);
+ get => GetFlag(CorpseFlag.Carved);
+ set => SetFlag(CorpseFlag.Carved, value);
+ }
- public static readonly TimeSpan InstancedCorpseTime = TimeSpan.FromMinutes(3.0);
+ [CommandProperty(AccessLevel.GameMaster)]
+ public bool VisitedByTaxidermist
+ {
+ get => GetFlag(CorpseFlag.VisitedByTaxidermist);
+ set => SetFlag(CorpseFlag.VisitedByTaxidermist, value);
+ }
- private static readonly TimeSpan m_DefaultDecayTime = TimeSpan.FromMinutes(7.0);
- private static readonly TimeSpan m_BoneDecayTime = TimeSpan.FromMinutes(7.0);
+ [CommandProperty(AccessLevel.GameMaster)]
+ public bool Channeled
+ {
+ get => GetFlag(CorpseFlag.Channeled);
+ set => SetFlag(CorpseFlag.Channeled, value);
+ }
- private string
- m_CorpseName; // Value of the CorpseNameAttribute attached to the owner when he died -or- null if the owner had no CorpseNameAttribute; use "the remains of ~name~"
+ [CommandProperty(AccessLevel.GameMaster)]
+ public bool Animated
+ {
+ get => GetFlag(CorpseFlag.Animated);
+ set => SetFlag(CorpseFlag.Animated, value);
+ }
- private DateTime m_DecayTime;
+ [CommandProperty(AccessLevel.GameMaster)]
+ public bool SelfLooted
+ {
+ get => GetFlag(CorpseFlag.SelfLooted);
+ set => SetFlag(CorpseFlag.SelfLooted, value);
+ }
- private Timer m_DecayTimer;
- private IDevourer m_Devourer; // The creature that devoured this corpse
- private CorpseFlag m_Flags; // @see CorpseFlag
+ [CommandProperty(AccessLevel.GameMaster)]
+ public bool Criminal
+ {
+ get => GetFlag(CorpseFlag.Criminal);
+ set => SetFlag(CorpseFlag.Criminal, value);
+ }
- // For notoriety:
+ public override bool DisplaysContent => false;
- // For Forensics Evaluation
- public string m_Forensicist; // Name of the first PlayerMobile who used Forensic Evaluation on the corpse
-
- private Dictionary
- m_InstancedItems;
-
- private Dictionary
- m_RestoreTable;
-
- // Why was this public?
- // public override bool IsPublicContainer => true;
-
- public Corpse(Mobile owner, List
- equipItems) : this(owner, null, null, equipItems)
+ public void Carve(Mobile from, Item item)
+ {
+ if (IsCriminalAction(from) && (Map?.Rules & MapRules.HarmfulRestrictions) != 0)
{
- }
-
- public Corpse(Mobile owner, HairInfo hair, FacialHairInfo facialhair, List
- equipItems)
- : base(0x2006)
- {
- // To suppress console warnings, stackable must be true
- Stackable = true;
- Amount = owner.Body; // protocol defines that for itemid 0x2006, amount=body
- Stackable = false;
-
- Movable = false;
- Hue = owner.Hue;
- Direction = owner.Direction;
- Name = owner.Name;
-
- Owner = owner;
-
- m_CorpseName = GetCorpseName(owner);
-
- TimeOfDeath = Core.Now;
-
- AccessLevel = owner.AccessLevel;
- Guild = owner.Guild as Guild;
- Kills = owner.Kills;
- SetFlag(CorpseFlag.Criminal, owner.Criminal);
-
- Hair = hair;
- FacialHair = facialhair;
-
- // This corpse does not turn to bones if: the owner is not a player
- SetFlag(CorpseFlag.NoBones, !owner.Player);
-
- Looters = new List();
- EquipItems = equipItems;
-
- Aggressors = new List(owner.Aggressors.Count + owner.Aggressed.Count);
- // bool addToAggressors = !( owner is BaseCreature );
-
- var isBaseCreature = owner is BaseCreature;
-
- var lastTime = TimeSpan.MaxValue;
-
- for (var i = 0; i < owner.Aggressors.Count; ++i)
+ if (Owner?.Player != true)
{
- var info = owner.Aggressors[i];
-
- if (Core.Now - info.LastCombatTime < lastTime)
- {
- Killer = info.Attacker;
- lastTime = Core.Now - info.LastCombatTime;
- }
-
- if (!isBaseCreature && !info.CriminalAggression)
- {
- Aggressors.Add(info.Attacker);
- }
- }
-
- for (var i = 0; i < owner.Aggressed.Count; ++i)
- {
- var info = owner.Aggressed[i];
-
- if (Core.Now - info.LastCombatTime < lastTime)
- {
- Killer = info.Defender;
- lastTime = Core.Now - info.LastCombatTime;
- }
-
- if (!isBaseCreature)
- {
- Aggressors.Add(info.Defender);
- }
- }
-
- if (isBaseCreature)
- {
- var bc = (BaseCreature)owner;
-
- var master = bc.GetMaster();
- if (master != null)
- {
- Aggressors.Add(master);
- }
-
- var rights = BaseCreature.GetLootingRights(bc.DamageEntries, bc.HitsMax);
- for (var i = 0; i < rights.Count; ++i)
- {
- var ds = rights[i];
-
- if (ds.m_HasRight)
- {
- Aggressors.Add(ds.m_Mobile);
- }
- }
- }
-
- BeginDecay(m_DefaultDecayTime);
-
- DevourCorpse();
- }
-
- public Corpse(Serial serial) : base(serial)
- {
- }
-
- [CommandProperty(AccessLevel.GameMaster)]
- public virtual bool InstancedCorpse => Core.SE && Core.Now < TimeOfDeath + InstancedCorpseTime;
-
- public override bool IsDecoContainer => false;
-
- [CommandProperty(AccessLevel.GameMaster)]
- public DateTime TimeOfDeath { get; set; }
-
- public override bool DisplayWeight => false;
-
- public HairInfo Hair { get; }
-
- public FacialHairInfo FacialHair { get; }
-
- [CommandProperty(AccessLevel.GameMaster)]
- public bool IsBones => GetFlag(CorpseFlag.IsBones);
-
- [CommandProperty(AccessLevel.GameMaster)]
- public bool Devoured => m_Devourer != null;
-
- [CommandProperty(AccessLevel.GameMaster)]
- public bool Carved
- {
- get => GetFlag(CorpseFlag.Carved);
- set => SetFlag(CorpseFlag.Carved, value);
- }
-
- [CommandProperty(AccessLevel.GameMaster)]
- public bool VisitedByTaxidermist
- {
- get => GetFlag(CorpseFlag.VisitedByTaxidermist);
- set => SetFlag(CorpseFlag.VisitedByTaxidermist, value);
- }
-
- [CommandProperty(AccessLevel.GameMaster)]
- public bool Channeled
- {
- get => GetFlag(CorpseFlag.Channeled);
- set => SetFlag(CorpseFlag.Channeled, value);
- }
-
- [CommandProperty(AccessLevel.GameMaster)]
- public bool Animated
- {
- get => GetFlag(CorpseFlag.Animated);
- set => SetFlag(CorpseFlag.Animated, value);
- }
-
- [CommandProperty(AccessLevel.GameMaster)]
- public bool SelfLooted
- {
- get => GetFlag(CorpseFlag.SelfLooted);
- set => SetFlag(CorpseFlag.SelfLooted, value);
- }
-
- [CommandProperty(AccessLevel.GameMaster)]
- public AccessLevel AccessLevel { get; private set; }
-
- public List Aggressors { get; private set; }
-
- public List Looters { get; private set; }
-
- [CommandProperty(AccessLevel.GameMaster)]
- public Mobile Killer { get; private set; }
-
- public List
- EquipItems { get; private set; }
-
- public List
- RestoreEquip { get; set; }
-
- public Guild Guild { get; }
-
- [CommandProperty(AccessLevel.GameMaster)]
- public int Kills { get; set; }
-
- [CommandProperty(AccessLevel.GameMaster)]
- public bool Criminal
- {
- get => GetFlag(CorpseFlag.Criminal);
- set => SetFlag(CorpseFlag.Criminal, value);
- }
-
- [CommandProperty(AccessLevel.GameMaster)]
- public Mobile Owner { get; private set; }
-
- public override bool DisplaysContent => false;
-
- public void Carve(Mobile from, Item item)
- {
- if (IsCriminalAction(from) && (Map?.Rules & MapRules.HarmfulRestrictions) != 0)
- {
- if (Owner?.Player != true)
- {
- from.SendLocalizedMessage(1005035); // You did not earn the right to loot this creature!
- }
- else
- {
- from.SendLocalizedMessage(1010049); // You may not loot this corpse.
- }
-
- return;
- }
-
- var dead = Owner;
-
- if (GetFlag(CorpseFlag.Carved) || dead == null)
- {
- from.SendLocalizedMessage(500485); // You see nothing useful to carve from the corpse.
- }
- else if (((Body)Amount).IsHuman && ItemID == 0x2006)
- {
- new Blood(0x122D).MoveToWorld(Location, Map);
-
- new Torso().MoveToWorld(Location, Map);
- new LeftLeg().MoveToWorld(Location, Map);
- new LeftArm().MoveToWorld(Location, Map);
- new RightLeg().MoveToWorld(Location, Map);
- new RightArm().MoveToWorld(Location, Map);
- new Head(dead.Name).MoveToWorld(Location, Map);
-
- SetFlag(CorpseFlag.Carved, true);
-
- ProcessDelta();
- SendRemovePacket();
- ItemID = Utility.Random(0xECA, 9); // bone graphic
- Hue = 0;
- ProcessDelta();
-
- if (IsCriminalAction(from))
- {
- from.CriminalAction(true);
- }
- }
- else if (dead is BaseCreature creature)
- {
- creature.OnCarve(from, this, item);
+ from.SendLocalizedMessage(1005035); // You did not earn the right to loot this creature!
}
else
{
- from.SendLocalizedMessage(500485); // You see nothing useful to carve from the corpse.
+ from.SendLocalizedMessage(1010049); // You may not loot this corpse.
}
+
+ return;
}
- public override bool IsChildVisibleTo(Mobile m, Item child) =>
- !m.Player || m.AccessLevel > AccessLevel.Player || m_InstancedItems == null ||
- !m_InstancedItems.TryGetValue(child, out var info) || !InstancedCorpse && !info.Perpetual
- || info.IsOwner(m);
+ var dead = Owner;
- private void AssignInstancedLoot()
+ if (GetFlag(CorpseFlag.Carved) || dead == null)
{
- if (Aggressors.Count == 0 || Items.Count == 0)
- {
- return;
- }
-
- m_InstancedItems ??= new Dictionary
- ();
-
- var m_Stackables = new List
- ();
- var m_Unstackables = new List
- ();
-
- for (var i = 0; i < Items.Count; i++)
- {
- var item = Items[i];
-
- if (item.LootType != LootType.Cursed) // Don't have cursed items take up someone's item spot.. (?)
- {
- if (item.Stackable)
- {
- m_Stackables.Add(item);
- }
- else
- {
- m_Unstackables.Add(item);
- }
- }
- }
-
- var attackers = new List(Aggressors);
-
- for (var i = 1; i < attackers.Count - 1; i++) // randomize
- {
- var rand = Utility.Random(i + 1);
-
- (attackers[rand], attackers[i]) = (attackers[i], attackers[rand]);
- }
-
- // stackables first, for the remaining stackables, have those be randomly added after
-
- for (var i = 0; i < m_Stackables.Count; i++)
- {
- var item = m_Stackables[i];
-
- if (item.Amount >= attackers.Count)
- {
- var amountPerAttacker = Math.DivRem(item.Amount, attackers.Count, out var remainder);
-
- for (var j = 0; j < (remainder == 0 ? attackers.Count - 1 : attackers.Count); j++)
- {
- // LiftItemDupe automagically adds it as a child item to the corpse
- var splitItem =
- Mobile.LiftItemDupe(item, item.Amount - amountPerAttacker);
-
- m_InstancedItems.Add(splitItem, new InstancedItemInfo(splitItem, attackers[j]));
-
- // What happens to the remaining portion? TEMP FOR NOW UNTIL OSI VERIFICATION: Treat as Single Item.
- }
-
- if (remainder == 0)
- {
- m_InstancedItems.Add(item, new InstancedItemInfo(item, attackers[^1]));
- }
- else
- {
- m_Unstackables.Add(item);
- }
- }
- else
- {
- // What happens in this case? TEMP FOR NOW UNTIL OSI VERIFICATION: Treat as Single Item.
- m_Unstackables.Add(item);
- }
- }
-
- for (var i = 0; i < m_Unstackables.Count; i++)
- {
- var m = attackers[i % attackers.Count];
- var item = m_Unstackables[i];
-
- m_InstancedItems.Add(item, new InstancedItemInfo(item, m));
- }
+ from.SendLocalizedMessage(500485); // You see nothing useful to carve from the corpse.
}
-
- public void AddCarvedItem(Item carved, Mobile carver)
+ else if (((Body)Amount).IsHuman && ItemID == 0x2006)
{
- DropItem(carved);
+ new Blood(0x122D).MoveToWorld(Location, Map);
- if (InstancedCorpse)
- {
- m_InstancedItems ??= new Dictionary
- ();
+ new Torso().MoveToWorld(Location, Map);
+ new LeftLeg().MoveToWorld(Location, Map);
+ new LeftArm().MoveToWorld(Location, Map);
+ new RightLeg().MoveToWorld(Location, Map);
+ new RightArm().MoveToWorld(Location, Map);
+ new Head(dead.Name).MoveToWorld(Location, Map);
- m_InstancedItems.Add(carved, new InstancedItemInfo(carved, carver));
- }
- }
-
- public void TurnToBones()
- {
- if (Deleted)
- {
- return;
- }
+ SetFlag(CorpseFlag.Carved, true);
ProcessDelta();
SendRemovePacket();
@@ -455,793 +342,723 @@ namespace Server.Items
Hue = 0;
ProcessDelta();
- SetFlag(CorpseFlag.NoBones, true);
- SetFlag(CorpseFlag.IsBones, true);
-
- BeginDecay(m_BoneDecayTime);
- }
-
- public void BeginDecay(TimeSpan delay)
- {
- m_DecayTimer?.Stop();
-
- m_DecayTime = Core.Now + delay;
-
- m_DecayTimer = new InternalTimer(this, delay);
- m_DecayTimer.Start();
- }
-
- public override void OnAfterDelete()
- {
- m_DecayTimer?.Stop();
-
- m_DecayTimer = null;
- }
-
- public static string GetCorpseName(Mobile m) => m is BaseCreature bc ? bc.CorpseNameOverride ?? bc.CorpseName : null;
-
- public static void Initialize()
- {
- Mobile.CreateCorpseHandler += Mobile_CreateCorpseHandler;
- }
-
- public static Container Mobile_CreateCorpseHandler(
- Mobile owner, HairInfo hair, FacialHairInfo facialhair,
- List
- initialContent, List
- equipItems
- )
- {
- var c = owner is MilitiaFighter
- ? new MilitiaFighterCorpse(owner, hair, facialhair, equipItems)
- : new Corpse(owner, hair, facialhair, equipItems);
-
- owner.Corpse = c;
-
- for (var i = 0; i < initialContent.Count; ++i)
+ if (IsCriminalAction(from))
{
- var item = initialContent[i];
+ from.CriminalAction(true);
+ }
+ }
+ else if (dead is BaseCreature creature)
+ {
+ creature.OnCarve(from, this, item);
+ }
+ else
+ {
+ from.SendLocalizedMessage(500485); // You see nothing useful to carve from the corpse.
+ }
+ }
- if (Core.AOS && owner.Player && item.Parent == owner.Backpack)
+ public override bool IsChildVisibleTo(Mobile m, Item child) =>
+ !m.Player || m.AccessLevel > AccessLevel.Player || _instancedItems == null ||
+ !_instancedItems.TryGetValue(child, out var info) || !InstancedCorpse && !info.Perpetual
+ || info.IsOwner(m);
+
+ private void AssignInstancedLoot()
+ {
+ if (_aggressors.Count == 0 || Items.Count == 0)
+ {
+ return;
+ }
+
+ _instancedItems ??= new Dictionary
- ();
+
+ var m_Stackables = new List
- ();
+ var m_Unstackables = new List
- ();
+
+ for (var i = 0; i < Items.Count; i++)
+ {
+ var item = Items[i];
+
+ if (item.LootType != LootType.Cursed) // Don't have cursed items take up someone's item spot.. (?)
+ {
+ if (item.Stackable)
{
- c.AddItem(item);
+ m_Stackables.Add(item);
}
else
{
- c.DropItem(item);
+ m_Unstackables.Add(item);
}
+ }
+ }
- if (owner.Player && Core.AOS)
+ var attackers = new List(_aggressors);
+
+ for (var i = 1; i < attackers.Count - 1; i++) // randomize
+ {
+ var rand = Utility.Random(i + 1);
+
+ (attackers[rand], attackers[i]) = (attackers[i], attackers[rand]);
+ }
+
+ // stackables first, for the remaining stackables, have those be randomly added after
+
+ for (var i = 0; i < m_Stackables.Count; i++)
+ {
+ var item = m_Stackables[i];
+
+ if (item.Amount >= attackers.Count)
+ {
+ var amountPerAttacker = Math.DivRem(item.Amount, attackers.Count, out var remainder);
+
+ for (var j = 0; j < (remainder == 0 ? attackers.Count - 1 : attackers.Count); j++)
{
- c.SetRestoreInfo(item, item.Location);
+ // LiftItemDupe automagically adds it as a child item to the corpse
+ var splitItem = Mobile.LiftItemDupe(item, item.Amount - amountPerAttacker);
+
+ _instancedItems.Add(splitItem, new InstancedItemInfo(splitItem, attackers[j]));
+
+ // What happens to the remaining portion? TEMP FOR NOW UNTIL OSI VERIFICATION: Treat as Single Item.
}
- }
- if (Core.SE && !owner.Player)
- {
- c.AssignInstancedLoot();
- }
- else if (Core.AOS && owner is PlayerMobile pm)
- {
- c.RestoreEquip = pm.EquipSnapshot;
- }
-
- var loc = owner.Location;
- var map = owner.Map;
-
- if (map == null || map == Map.Internal)
- {
- loc = owner.LogoutLocation;
- map = owner.LogoutMap;
- }
-
- c.MoveToWorld(loc, map);
-
- return c;
- }
-
- protected bool GetFlag(CorpseFlag flag) => (m_Flags & flag) != 0;
-
- protected void SetFlag(CorpseFlag flag, bool on)
- {
- m_Flags = on ? m_Flags | flag : m_Flags & ~flag;
- }
-
- public override void Serialize(IGenericWriter writer)
- {
- base.Serialize(writer);
-
- writer.Write(12); // version
-
- if (RestoreEquip == null)
- {
- writer.Write(false);
+ if (remainder == 0)
+ {
+ _instancedItems.Add(item, new InstancedItemInfo(item, attackers[^1]));
+ }
+ else
+ {
+ m_Unstackables.Add(item);
+ }
}
else
{
- writer.Write(true);
- writer.Write(RestoreEquip);
- }
-
- writer.Write((int)m_Flags);
-
- writer.WriteDeltaTime(TimeOfDeath);
-
- var count = m_RestoreTable?.Count ?? 0;
- writer.Write(count);
-
- if (m_RestoreTable != null)
- {
- foreach (var (item, loc) in m_RestoreTable)
- {
- writer.Write(item);
-
- if (item.Location == loc)
- {
- writer.Write(false);
- }
- else
- {
- writer.Write(true);
- writer.Write(loc);
- }
- }
- }
-
- writer.Write(m_DecayTimer != null);
-
- if (m_DecayTimer != null)
- {
- writer.WriteDeltaTime(m_DecayTime);
- }
-
- writer.Write(Looters);
- writer.Write(Killer);
-
- writer.Write(Aggressors);
-
- writer.Write(Owner);
-
- writer.Write(m_CorpseName);
-
- writer.Write((int)AccessLevel);
- writer.Write(Guild);
- writer.Write(Kills);
-
- writer.Write(EquipItems);
- }
-
- public override void Deserialize(IGenericReader reader)
- {
- base.Deserialize(reader);
-
- var version = reader.ReadInt();
-
- switch (version)
- {
- case 12:
- {
- if (reader.ReadBool())
- {
- RestoreEquip = reader.ReadEntityList
- ();
- }
-
- goto case 11;
- }
- case 11:
- {
- // Version 11, we move all bools to a CorpseFlag
- m_Flags = (CorpseFlag)reader.ReadInt();
-
- TimeOfDeath = reader.ReadDeltaTime();
-
- var count = reader.ReadInt();
-
- for (var i = 0; i < count; ++i)
- {
- var item = reader.ReadEntity
- ();
-
- if (reader.ReadBool())
- {
- SetRestoreInfo(item, reader.ReadPoint3D());
- }
- else if (item != null)
- {
- SetRestoreInfo(item, item.Location);
- }
- }
-
- if (reader.ReadBool())
- {
- BeginDecay(reader.ReadDeltaTime() - Core.Now);
- }
-
- Looters = reader.ReadEntityList();
- Killer = reader.ReadEntity();
-
- Aggressors = reader.ReadEntityList();
- Owner = reader.ReadEntity();
-
- m_CorpseName = reader.ReadString();
-
- AccessLevel = (AccessLevel)reader.ReadInt();
- reader.ReadInt(); // guild reserve
- Kills = reader.ReadInt();
-
- EquipItems = reader.ReadEntityList
- ();
- break;
- }
- case 10:
- {
- TimeOfDeath = reader.ReadDeltaTime();
-
- goto case 9;
- }
- case 9:
- {
- var count = reader.ReadInt();
-
- for (var i = 0; i < count; ++i)
- {
- var item = reader.ReadEntity
- ();
-
- if (reader.ReadBool())
- {
- SetRestoreInfo(item, reader.ReadPoint3D());
- }
- else if (item != null)
- {
- SetRestoreInfo(item, item.Location);
- }
- }
-
- goto case 8;
- }
- case 8:
- {
- SetFlag(CorpseFlag.VisitedByTaxidermist, reader.ReadBool());
-
- goto case 7;
- }
- case 7:
- {
- if (reader.ReadBool())
- {
- BeginDecay(reader.ReadDeltaTime() - Core.Now);
- }
-
- goto case 6;
- }
- case 6:
- {
- Looters = reader.ReadEntityList();
- Killer = reader.ReadEntity();
-
- goto case 5;
- }
- case 5:
- {
- SetFlag(CorpseFlag.Carved, reader.ReadBool());
-
- goto case 4;
- }
- case 4:
- {
- Aggressors = reader.ReadEntityList();
-
- goto case 3;
- }
- case 3:
- {
- Owner = reader.ReadEntity();
-
- goto case 2;
- }
- case 2:
- {
- SetFlag(CorpseFlag.NoBones, reader.ReadBool());
-
- goto case 1;
- }
- case 1:
- {
- m_CorpseName = reader.ReadString();
-
- goto case 0;
- }
- case 0:
- {
- if (version < 10)
- {
- TimeOfDeath = Core.Now;
- }
-
- if (version < 7)
- {
- BeginDecay(m_DefaultDecayTime);
- }
-
- if (version < 6)
- {
- Looters = new List();
- }
-
- if (version < 4)
- {
- Aggressors = new List();
- }
-
- AccessLevel = (AccessLevel)reader.ReadInt();
- reader.ReadInt(); // guild reserve
- Kills = reader.ReadInt();
- SetFlag(CorpseFlag.Criminal, reader.ReadBool());
-
- EquipItems = reader.ReadEntityList
- ();
-
- break;
- }
+ // What happens in this case? TEMP FOR NOW UNTIL OSI VERIFICATION: Treat as Single Item.
+ m_Unstackables.Add(item);
}
}
- public bool DevourCorpse()
+ for (var i = 0; i < m_Unstackables.Count; i++)
{
- if (Devoured || Deleted || Killer?.Deleted != false || !Killer.Alive || Killer is not IDevourer devourer ||
- Owner?.Deleted != false)
+ var m = attackers[i % attackers.Count];
+ var item = m_Unstackables[i];
+
+ _instancedItems.Add(item, new InstancedItemInfo(item, m));
+ }
+ }
+
+ public void AddCarvedItem(Item carved, Mobile carver)
+ {
+ DropItem(carved);
+
+ if (InstancedCorpse)
+ {
+ _instancedItems ??= new Dictionary
- ();
+
+ _instancedItems.Add(carved, new InstancedItemInfo(carved, carver));
+ }
+ }
+
+ public void TurnToBones()
+ {
+ if (Deleted)
+ {
+ return;
+ }
+
+ ProcessDelta();
+ SendRemovePacket();
+ ItemID = Utility.Random(0xECA, 9); // bone graphic
+ Hue = 0;
+ ProcessDelta();
+
+ SetFlag(CorpseFlag.NoBones, true);
+ SetFlag(CorpseFlag.IsBones, true);
+
+ BeginDecay(_boneDecayTime);
+ }
+
+ public void BeginDecay(TimeSpan delay)
+ {
+ _decayTimer?.Stop();
+ _decayTimer = new InternalTimer(this, delay);
+ _decayTimer.Start();
+ }
+
+ public override void OnAfterDelete()
+ {
+ _decayTimer?.Stop();
+ _decayTimer = null;
+ }
+
+ public static string GetCorpseName(Mobile m) => m is BaseCreature bc ? bc.CorpseNameOverride ?? bc.CorpseName : null;
+
+ public static void Initialize()
+ {
+ Mobile.CreateCorpseHandler += Mobile_CreateCorpseHandler;
+ }
+
+ public static Container Mobile_CreateCorpseHandler(
+ Mobile owner, HairInfo hair, FacialHairInfo facialhair,
+ List
- initialContent, List
- equipItems
+ )
+ {
+ var c = owner is MilitiaFighter
+ ? new MilitiaFighterCorpse(owner, hair, facialhair, equipItems)
+ : new Corpse(owner, hair, facialhair, equipItems);
+
+ owner.Corpse = c;
+
+ for (var i = 0; i < initialContent.Count; ++i)
+ {
+ var item = initialContent[i];
+
+ if (Core.AOS && owner.Player && item.Parent == owner.Backpack)
+ {
+ c.AddItem(item);
+ }
+ else
+ {
+ c.DropItem(item);
+ }
+
+ if (owner.Player && Core.AOS)
+ {
+ c.SetRestoreInfo(item, item.Location);
+ }
+ }
+
+ if (Core.SE && !owner.Player)
+ {
+ c.AssignInstancedLoot();
+ }
+ else if (Core.AOS && owner is PlayerMobile pm)
+ {
+ c.RestoreEquip = pm.EquipSnapshot;
+ }
+
+ var loc = owner.Location;
+ var map = owner.Map;
+
+ if (map == null || map == Map.Internal)
+ {
+ loc = owner.LogoutLocation;
+ map = owner.LogoutMap;
+ }
+
+ c.MoveToWorld(loc, map);
+
+ return c;
+ }
+
+ protected bool GetFlag(CorpseFlag flag) => (_flags & flag) != 0;
+
+ protected void SetFlag(CorpseFlag flag, bool value)
+ {
+ if (value)
+ {
+ Flags |= flag;
+ }
+ else
+ {
+ Flags &= ~flag;
+ }
+ }
+
+ private void Deserialize(IGenericReader reader, int version)
+ {
+ if (reader.ReadBool())
+ {
+ _restoreEquip = reader.ReadEntityList
- ();
+ }
+
+ _flags = (CorpseFlag)reader.ReadInt();
+
+ _timeOfDeath = reader.ReadDeltaTime();
+
+ var count = reader.ReadInt();
+
+ for (var i = 0; i < count; ++i)
+ {
+ var item = reader.ReadEntity
- ();
+
+ if (reader.ReadBool())
+ {
+ SetRestoreInfo(item, reader.ReadPoint3D());
+ }
+ else if (item != null)
+ {
+ SetRestoreInfo(item, item.Location);
+ }
+ }
+
+ if (reader.ReadBool())
+ {
+ BeginDecay(reader.ReadDeltaTime() - Core.Now);
+ }
+
+ _looters = reader.ReadEntitySet();
+ _killer = reader.ReadEntity();
+
+ _aggressors = reader.ReadEntityList();
+ _owner = reader.ReadEntity();
+
+ _corpseName = reader.ReadString();
+
+ _accessLevel = (AccessLevel)reader.ReadInt();
+ reader.ReadInt(); // guild reserve
+ _kills = reader.ReadInt();
+
+ _equipItems = reader.ReadEntityList
- ();
+ }
+
+ public bool DevourCorpse()
+ {
+ if (Devoured || Deleted || Killer?.Deleted != false || !Killer.Alive || Killer is not IDevourer devourer ||
+ Owner?.Deleted != false)
+ {
+ return false;
+ }
+
+ _devourer = devourer; // Set the devourer the killer
+ return _devourer.Devour(this); // Devour the corpse if it hasn't
+ }
+
+ public override void SendInfoTo(NetState ns, ReadOnlySpan world = default)
+ {
+ base.SendInfoTo(ns, world);
+
+ if (((Body)Amount).IsHuman && ItemID == 0x2006)
+ {
+ ns.SendCorpseContent(ns.Mobile, this);
+ ns.SendCorpseEquip(ns.Mobile, this);
+ }
+ }
+
+ public bool IsCriminalAction(Mobile from)
+ {
+ if (from == Owner || from.AccessLevel >= AccessLevel.GameMaster)
+ {
+ return false;
+ }
+
+ var p = Party.Get(Owner);
+
+ if (p?.Contains(from) == true)
+ {
+ var pmi = p[Owner];
+
+ if (pmi?.CanLoot == true)
{
return false;
}
-
- m_Devourer = devourer; // Set the devourer the killer
- return m_Devourer.Devour(this); // Devour the corpse if it hasn't
}
- public override void SendInfoTo(NetState ns, ReadOnlySpan world = default)
- {
- base.SendInfoTo(ns, world);
+ return NotorietyHandlers.CorpseNotoriety(from, this) == Notoriety.Innocent;
+ }
- if (((Body)Amount).IsHuman && ItemID == 0x2006)
+ public override bool CheckItemUse(Mobile from, Item item) =>
+ base.CheckItemUse(from, item) && (item == this || CanLoot(from, item));
+
+ public override bool CheckLift(Mobile from, Item item, ref LRReason reject) =>
+ base.CheckLift(from, item, ref reject) && CanLoot(from, item);
+
+ public override void OnItemUsed(Mobile from, Item item)
+ {
+ base.OnItemUsed(from, item);
+
+ if (item is Food)
+ {
+ from.RevealingAction();
+ }
+
+ if (item != this && IsCriminalAction(from))
+ {
+ from.CriminalAction(true);
+ }
+
+ this.Add(_looters, from);
+
+ _instancedItems?.Remove(item);
+
+ }
+
+ public override void OnItemLifted(Mobile from, Item item)
+ {
+ base.OnItemLifted(from, item);
+
+ if (item != this && from != Owner)
+ {
+ from.RevealingAction();
+ }
+
+ if (item != this && IsCriminalAction(from))
+ {
+ from.CriminalAction(true);
+ }
+
+ this.Add(_looters, from);
+
+ _instancedItems?.Remove(item);
+ }
+
+ public override void GetContextMenuEntries(Mobile from, List list)
+ {
+ base.GetContextMenuEntries(from, list);
+
+ if (Core.AOS && _owner == from && from.Alive)
+ {
+ list.Add(new OpenCorpseEntry());
+ }
+ }
+
+ public bool GetRestoreInfo(Item item, ref Point3D loc) =>
+ item != null && _restoreTable?.TryGetValue(item, out loc) == true;
+
+ public void SetRestoreInfo(Item item, Point3D loc)
+ {
+ if (item == null)
+ {
+ return;
+ }
+
+ _restoreTable ??= new Dictionary
- ();
+ _restoreTable[item] = loc;
+ this.MarkDirty();
+ }
+
+ public void ClearRestoreInfo(Item item)
+ {
+ if (_restoreTable == null || item == null)
+ {
+ return;
+ }
+
+ _restoreTable.Remove(item);
+ if (_restoreTable.Count == 0)
+ {
+ RestoreTable = null;
+ }
+ else
+ {
+ this.MarkDirty();
+ }
+ }
+
+ public bool CanLoot(Mobile from, Item item) =>
+ !IsCriminalAction(from) || (Map.Rules & MapRules.HarmfulRestrictions) == 0;
+
+ public bool CheckLoot(Mobile from, Item item)
+ {
+ if (!CanLoot(from, item))
+ {
+ if (Owner?.Player != true)
{
- ns.SendCorpseContent(ns.Mobile, this);
- ns.SendCorpseEquip(ns.Mobile, this);
+ from.SendLocalizedMessage(1005035); // You did not earn the right to loot this creature!
+ }
+ else
+ {
+ from.SendLocalizedMessage(1010049); // You may not loot this corpse.
+ }
+
+ return false;
+ }
+
+ if (IsCriminalAction(from))
+ {
+ if (Owner?.Player != true)
+ {
+ from.SendLocalizedMessage(1005036); // Looting this monster corpse will be a criminal act!
+ }
+ else
+ {
+ from.SendLocalizedMessage(1005038); // Looting this corpse will be a criminal act!
}
}
- public bool IsCriminalAction(Mobile from)
+ return true;
+ }
+
+ public virtual void Open(Mobile from, bool checkSelfLoot)
+ {
+ if (from.AccessLevel <= AccessLevel.Player && !from.InRange(GetWorldLocation(), 2))
{
- if (from == Owner || from.AccessLevel >= AccessLevel.GameMaster)
+ from.SendLocalizedMessage(500446); // That is too far away.
+ return;
+ }
+
+ if (checkSelfLoot && from == Owner && !GetFlag(CorpseFlag.SelfLooted) && Items.Count != 0)
+ {
+ if (from.FindItemOnLayer(Layer.OuterTorso) is DeathRobe robe)
{
- return false;
- }
+ var map = from.Map;
- var p = Party.Get(Owner);
-
- if (p?.Contains(from) == true)
- {
- var pmi = p[Owner];
-
- if (pmi?.CanLoot == true)
+ if (map != null && map != Map.Internal)
{
- return false;
+ robe.MoveToWorld(from.Location, map);
}
}
- return NotorietyHandlers.CorpseNotoriety(from, this) == Notoriety.Innocent;
- }
+ var pack = from.Backpack;
- public override bool CheckItemUse(Mobile from, Item item) =>
- base.CheckItemUse(from, item) && (item == this || CanLoot(from, item));
-
- public override bool CheckLift(Mobile from, Item item, ref LRReason reject) =>
- base.CheckLift(from, item, ref reject) && CanLoot(from, item);
-
- public override void OnItemUsed(Mobile from, Item item)
- {
- base.OnItemUsed(from, item);
-
- if (item is Food)
+ if (RestoreEquip != null && pack != null)
{
- from.RevealingAction();
- }
+ var packItems = new List
- (pack.Items); // Only items in the top-level pack are re-equipped
- if (item != this && IsCriminalAction(from))
- {
- from.CriminalAction(true);
- }
-
- if (!Looters.Contains(from))
- {
- Looters.Add(from);
- }
-
- m_InstancedItems?.Remove(item);
-
- }
-
- public override void OnItemLifted(Mobile from, Item item)
- {
- base.OnItemLifted(from, item);
-
- if (item != this && from != Owner)
- {
- from.RevealingAction();
- }
-
- if (item != this && IsCriminalAction(from))
- {
- from.CriminalAction(true);
- }
-
- if (!Looters.Contains(from))
- {
- Looters.Add(from);
- }
-
- m_InstancedItems?.Remove(item);
- }
-
- public override void GetContextMenuEntries(Mobile from, List list)
- {
- base.GetContextMenuEntries(from, list);
-
- if (Core.AOS && Owner == from && from.Alive)
- {
- list.Add(new OpenCorpseEntry());
- }
- }
-
- public bool GetRestoreInfo(Item item, ref Point3D loc) =>
- item != null && m_RestoreTable?.TryGetValue(item, out loc) == true;
-
- public void SetRestoreInfo(Item item, Point3D loc)
- {
- if (item == null)
- {
- return;
- }
-
- m_RestoreTable ??= new Dictionary
- ();
-
- m_RestoreTable[item] = loc;
- }
-
- public void ClearRestoreInfo(Item item)
- {
- if (m_RestoreTable == null || item == null)
- {
- return;
- }
-
- m_RestoreTable.Remove(item);
-
- if (m_RestoreTable.Count == 0)
- {
- m_RestoreTable = null;
- }
- }
-
- public bool CanLoot(Mobile from, Item item) =>
- !IsCriminalAction(from) || (Map.Rules & MapRules.HarmfulRestrictions) == 0;
-
- public bool CheckLoot(Mobile from, Item item)
- {
- if (!CanLoot(from, item))
- {
- if (Owner?.Player != true)
+ for (var i = 0; i < packItems.Count; i++)
{
- from.SendLocalizedMessage(1005035); // You did not earn the right to loot this creature!
+ var packItem = packItems[i];
+
+ if (RestoreEquip.Contains(packItem) && packItem.Movable)
+ {
+ from.EquipItem(packItem);
+ }
+ }
+ }
+
+ var items = new List
- (Items);
+
+ var didntFit = false;
+
+ for (var i = 0; !didntFit && i < items.Count; ++i)
+ {
+ var item = items[i];
+ var loc = item.Location;
+
+ if (item.Layer is Layer.Hair or Layer.FacialHair || !item.Movable || !GetRestoreInfo(item, ref loc))
+ {
+ continue;
+ }
+
+ if (pack?.CheckHold(from, item, false, true) == true)
+ {
+ item.Location = loc;
+ pack.AddItem(item);
+
+ if (RestoreEquip?.Contains(item) == true)
+ {
+ from.EquipItem(item);
+ }
}
else
{
- from.SendLocalizedMessage(1010049); // You may not loot this corpse.
+ didntFit = true;
}
-
- return false;
}
- if (IsCriminalAction(from))
+ from.PlaySound(0x3E3);
+
+ if (Items.Count != 0)
{
- if (Owner?.Player != true)
+ from.SendLocalizedMessage(1062472); // You gather some of your belongings. The rest remain on the corpse.
+ }
+ else
+ {
+ SetFlag(CorpseFlag.Carved, true);
+
+ if (ItemID == 0x2006)
{
- from.SendLocalizedMessage(1005036); // Looting this monster corpse will be a criminal act!
+ ProcessDelta();
+ SendRemovePacket();
+ ItemID = Utility.Random(0xECA, 9); // bone graphic
+ Hue = 0;
+ ProcessDelta();
+ }
+
+ from.SendLocalizedMessage(1062471); // You quickly gather all of your belongings.
+ }
+
+ SetFlag(CorpseFlag.SelfLooted, true);
+ }
+
+ if (!CheckLoot(from, null))
+ {
+ return;
+ }
+
+ if (from is not PlayerMobile player)
+ {
+ return;
+ }
+
+ var qs = player.Quest;
+
+ if (qs is UzeraanTurmoilQuest)
+ {
+ var obj = qs.FindObjective();
+ if (obj?.CorpseWithBone == this && (!obj.Completed || UzeraanTurmoilQuest.HasLostDaemonBone(player)))
+ {
+ Item bone = new QuestDaemonBone();
+
+ if (player.PlaceInBackpack(bone))
+ {
+ obj.CorpseWithBone = null;
+ // You rummage through the bones and find a Daemon Bone! You quickly place the item in your pack.
+ player.SendLocalizedMessage(1049341, "", 0x22);
+
+ if (!obj.Completed)
+ {
+ obj.Complete();
+ }
}
else
{
- from.SendLocalizedMessage(1005038); // Looting this corpse will be a criminal act!
+ bone.Delete();
+ // Rummaging through the bones you find a Daemon Bone, but can't pick it up because your pack is too full. Come back when you have more room in your pack.
+ player.SendLocalizedMessage(1049342, "", 0x22);
}
- }
- return true;
- }
-
- public virtual void Open(Mobile from, bool checkSelfLoot)
- {
- if (from.AccessLevel <= AccessLevel.Player && !from.InRange(GetWorldLocation(), 2))
- {
- from.SendLocalizedMessage(500446); // That is too far away.
return;
}
-
- if (checkSelfLoot && from == Owner && !GetFlag(CorpseFlag.SelfLooted) && Items.Count != 0)
+ }
+ else if (qs is TheSummoningQuest)
+ {
+ var obj = qs.FindObjective();
+ if (obj?.Completed == true && obj.CorpseWithSkull == this)
{
- if (from.FindItemOnLayer(Layer.OuterTorso) is DeathRobe robe)
+ var sk = new GoldenSkull();
+
+ if (player.PlaceInBackpack(sk))
{
- var map = from.Map;
-
- if (map != null && map != Map.Internal)
- {
- robe.MoveToWorld(from.Location, map);
- }
- }
-
- var pack = from.Backpack;
-
- if (RestoreEquip != null && pack != null)
- {
- var packItems = new List
- (pack.Items); // Only items in the top-level pack are re-equipped
-
- for (var i = 0; i < packItems.Count; i++)
- {
- var packItem = packItems[i];
-
- if (RestoreEquip.Contains(packItem) && packItem.Movable)
- {
- from.EquipItem(packItem);
- }
- }
- }
-
- var items = new List
- (Items);
-
- var didntFit = false;
-
- for (var i = 0; !didntFit && i < items.Count; ++i)
- {
- var item = items[i];
- var loc = item.Location;
-
- if (item.Layer is Layer.Hair or Layer.FacialHair || !item.Movable || !GetRestoreInfo(item, ref loc))
- {
- continue;
- }
-
- if (pack?.CheckHold(from, item, false, true) == true)
- {
- item.Location = loc;
- pack.AddItem(item);
-
- if (RestoreEquip?.Contains(item) == true)
- {
- from.EquipItem(item);
- }
- }
- else
- {
- didntFit = true;
- }
- }
-
- from.PlaySound(0x3E3);
-
- if (Items.Count != 0)
- {
- from.SendLocalizedMessage(1062472); // You gather some of your belongings. The rest remain on the corpse.
+ obj.CorpseWithSkull = null;
+ // For your valor in combating the devourer, you have been awarded a golden skull.
+ player.SendLocalizedMessage(1050022);
+ qs.Complete();
}
else
{
- SetFlag(CorpseFlag.Carved, true);
-
- if (ItemID == 0x2006)
- {
- ProcessDelta();
- SendRemovePacket();
- ItemID = Utility.Random(0xECA, 9); // bone graphic
- Hue = 0;
- ProcessDelta();
- }
-
- from.SendLocalizedMessage(1062471); // You quickly gather all of your belongings.
- }
-
- SetFlag(CorpseFlag.SelfLooted, true);
- }
-
- if (!CheckLoot(from, null))
- {
- return;
- }
-
- if (from is not PlayerMobile player)
- {
- return;
- }
-
- var qs = player.Quest;
-
- if (qs is UzeraanTurmoilQuest)
- {
- var obj = qs.FindObjective();
- if (obj?.CorpseWithBone == this && (!obj.Completed || UzeraanTurmoilQuest.HasLostDaemonBone(player)))
- {
- Item bone = new QuestDaemonBone();
-
- if (player.PlaceInBackpack(bone))
- {
- obj.CorpseWithBone = null;
- player.SendLocalizedMessage(
- 1049341,
- "",
- 0x22
- ); // You rummage through the bones and find a Daemon Bone! You quickly place the item in your pack.
-
- if (!obj.Completed)
- {
- obj.Complete();
- }
- }
- else
- {
- bone.Delete();
- player.SendLocalizedMessage(
- 1049342,
- "",
- 0x22
- ); // Rummaging through the bones you find a Daemon Bone, but can't pick it up because your pack is too full. Come back when you have more room in your pack.
- }
-
- return;
+ sk.Delete();
+ // You find a golden skull, but your backpack is too full to carry it.
+ player.SendLocalizedMessage(1050023);
}
}
- else if (qs is TheSummoningQuest)
- {
- var obj = qs.FindObjective();
- if (obj?.Completed == true && obj.CorpseWithSkull == this)
- {
- var sk = new GoldenSkull();
-
- if (player.PlaceInBackpack(sk))
- {
- obj.CorpseWithSkull = null;
- player.SendLocalizedMessage(
- 1050022
- ); // For your valor in combating the devourer, you have been awarded a golden skull.
- qs.Complete();
- }
- else
- {
- sk.Delete();
- player.SendLocalizedMessage(
- 1050023
- ); // You find a golden skull, but your backpack is too full to carry it.
- }
- }
- }
-
- base.OnDoubleClick(from);
}
- public override void OnDoubleClick(Mobile from)
- {
- Open(from, Core.AOS);
- }
+ base.OnDoubleClick(from);
+ }
- public override bool CheckContentDisplay(Mobile from) => false;
+ public override void OnDoubleClick(Mobile from)
+ {
+ Open(from, Core.AOS);
+ }
- public override void AddNameProperty(IPropertyList list)
+ public override bool CheckContentDisplay(Mobile from) => false;
+
+ public override void AddNameProperty(IPropertyList list)
+ {
+ if (ItemID == 0x2006) // Corpse form
{
- if (ItemID == 0x2006) // Corpse form
+ if (_corpseName != null)
{
- if (m_CorpseName != null)
- {
- list.Add(m_CorpseName);
- }
- else
- {
- list.Add(1046414, Name); // the remains of ~1_NAME~
- }
+ list.Add(_corpseName);
}
- else // Bone form
+ else
{
list.Add(1046414, Name); // the remains of ~1_NAME~
}
}
-
- public override void OnAosSingleClick(Mobile from)
+ else // Bone form
{
- var hue = Notoriety.GetHue(NotorietyHandlers.CorpseNotoriety(from, this));
- var opl = PropertyList;
-
- if (opl.Header > 0)
- {
- from.NetState.SendMessageLocalized(Serial, ItemID, MessageType.Label, hue, 3, opl.Header, Name, opl.HeaderArgs);
- }
+ list.Add(1046414, Name); // the remains of ~1_NAME~
}
+ }
- public override void OnSingleClick(Mobile from)
+ public override void OnAosSingleClick(Mobile from)
+ {
+ var hue = Notoriety.GetHue(NotorietyHandlers.CorpseNotoriety(from, this));
+ var opl = PropertyList;
+
+ if (opl.Header > 0)
{
- var hue = Notoriety.GetHue(NotorietyHandlers.CorpseNotoriety(from, this));
+ from.NetState.SendMessageLocalized(Serial, ItemID, MessageType.Label, hue, 3, opl.Header, Name, opl.HeaderArgs);
+ }
+ }
- if (ItemID == 0x2006) // Corpse form
+ public override void OnSingleClick(Mobile from)
+ {
+ var hue = Notoriety.GetHue(NotorietyHandlers.CorpseNotoriety(from, this));
+
+ if (ItemID == 0x2006) // Corpse form
+ {
+ if (_corpseName != null)
{
- if (m_CorpseName != null)
- {
- from.NetState.SendMessage(Serial, ItemID, MessageType.Label, hue, 3, true, null, "", m_CorpseName);
- }
- else
- {
- from.NetState.SendMessageLocalized(Serial, ItemID, MessageType.Label, hue, 3, 1046414, "", Name);
- }
+ from.NetState.SendMessage(Serial, ItemID, MessageType.Label, hue, 3, true, null, "", _corpseName);
}
- else // Bone form
+ else
{
from.NetState.SendMessageLocalized(Serial, ItemID, MessageType.Label, hue, 3, 1046414, "", Name);
}
}
-
- private class InstancedItemInfo
+ else // Bone form
{
- private readonly Item m_Item;
- private readonly Mobile m_Mobile;
+ from.NetState.SendMessageLocalized(Serial, ItemID, MessageType.Label, hue, 3, 1046414, "", Name);
+ }
+ }
- public InstancedItemInfo(Item i, Mobile m)
- {
- m_Item = i;
- m_Mobile = m;
- }
+ private class InstancedItemInfo
+ {
+ private Item _item;
+ private Mobile _mobile;
- public bool Perpetual { get; set; }
-
- public bool IsOwner(Mobile m)
- {
- if (m_Item.LootType == LootType.Cursed) // Cursed Items are part of everyone's instanced corpse... (?)
- {
- return true;
- }
-
- if (m == null)
- {
- return false; // sanity
- }
-
- if (m_Mobile == m)
- {
- return true;
- }
-
- var myParty = Party.Get(m_Mobile);
-
- return myParty != null && myParty == Party.Get(m);
- }
+ public InstancedItemInfo(Item i, Mobile m)
+ {
+ _item = i;
+ _mobile = m;
}
- private class InternalTimer : Timer
+ public bool Perpetual { get; set; }
+
+ public bool IsOwner(Mobile m)
{
- private readonly Corpse m_Corpse;
-
- public InternalTimer(Corpse c, TimeSpan delay) : base(delay) => m_Corpse = c;
-
- protected override void OnTick()
+ if (_item.LootType == LootType.Cursed) // Cursed Items are part of everyone's instanced corpse... (?)
{
- if (!m_Corpse.GetFlag(CorpseFlag.NoBones))
- {
- m_Corpse.TurnToBones();
- }
- else
- {
- m_Corpse.Delete();
- }
+ return true;
+ }
+
+ if (m == null)
+ {
+ return false; // sanity
+ }
+
+ if (_mobile == m)
+ {
+ return true;
+ }
+
+ var myParty = Party.Get(_mobile);
+
+ return myParty != null && myParty == Party.Get(m);
+ }
+ }
+
+ private class InternalTimer : Timer
+ {
+ private Corpse _corpse;
+
+ public InternalTimer(Corpse c, TimeSpan delay) : base(delay) => _corpse = c;
+
+ protected override void OnTick()
+ {
+ if (!_corpse.GetFlag(CorpseFlag.NoBones))
+ {
+ _corpse.TurnToBones();
+ }
+ else
+ {
+ _corpse.Delete();
}
}
+ }
- private class OpenCorpseEntry : ContextMenuEntry
+ private class OpenCorpseEntry : ContextMenuEntry
+ {
+ public OpenCorpseEntry() : base(6215, 2)
{
- public OpenCorpseEntry() : base(6215, 2)
- {
- }
+ }
- public override void OnClick()
+ public override void OnClick()
+ {
+ if (Owner.Target is Corpse corpse && Owner.From.CheckAlive())
{
- if (Owner.Target is Corpse corpse && Owner.From.CheckAlive())
- {
- corpse.Open(Owner.From, false);
- }
+ corpse.Open(Owner.From, false);
}
}
}
diff --git a/Projects/UOContent/Items/Misc/Corpses/CorpseNameAttribute.cs b/Projects/UOContent/Items/Misc/Corpses/CorpseNameAttribute.cs
index a923345fd..5ba3339f7 100644
--- a/Projects/UOContent/Items/Misc/Corpses/CorpseNameAttribute.cs
+++ b/Projects/UOContent/Items/Misc/Corpses/CorpseNameAttribute.cs
@@ -1,12 +1,11 @@
using System;
-namespace Server
-{
- [AttributeUsage(AttributeTargets.Class)]
- public class CorpseNameAttribute : Attribute
- {
- public CorpseNameAttribute(string name) => Name = name;
+namespace Server;
- public string Name { get; }
- }
+[AttributeUsage(AttributeTargets.Class)]
+public class CorpseNameAttribute : Attribute
+{
+ public CorpseNameAttribute(string name) => Name = name;
+
+ public string Name { get; }
}
diff --git a/Projects/UOContent/Items/Misc/Corpses/CorpsePackets.cs b/Projects/UOContent/Items/Misc/Corpses/CorpsePackets.cs
index 8299b372e..c6331876a 100644
--- a/Projects/UOContent/Items/Misc/Corpses/CorpsePackets.cs
+++ b/Projects/UOContent/Items/Misc/Corpses/CorpsePackets.cs
@@ -17,143 +17,142 @@ using System.Buffers;
using System.IO;
using Server.Items;
-namespace Server.Network
+namespace Server.Network;
+
+public static class CorpsePackets
{
- public static class CorpsePackets
+ public static void SendCorpseEquip(this NetState ns, Mobile beholder, Corpse beheld)
{
- public static void SendCorpseEquip(this NetState ns, Mobile beholder, Corpse beheld)
+ if (ns.CannotSendPackets())
{
- if (ns.CannotSendPackets())
+ return;
+ }
+
+ var list = beheld.EquipItems;
+
+ var maxLength = 8 + (list.Count + 2) * 5;
+ var writer = new SpanWriter(stackalloc byte[maxLength]);
+ writer.Write((byte)0x89);
+ writer.Seek(2, SeekOrigin.Current);
+ writer.Write(beheld.Serial);
+
+ for (var i = 0; i < list.Count; ++i)
+ {
+ var item = list[i];
+
+ if (!item.Deleted && beholder.CanSee(item) && item.Parent == beheld)
{
- return;
+ writer.Write((byte)(item.Layer + 1));
+ writer.Write(item.Serial);
}
+ }
- var list = beheld.EquipItems;
+ if (beheld.Hair?.ItemID > 0)
+ {
+ writer.Write((byte)(Layer.Hair + 1));
+ writer.Write(HairInfo.FakeSerial(beheld.Owner.Serial) - 2);
+ }
- var maxLength = 8 + (list.Count + 2) * 5;
- var writer = new SpanWriter(stackalloc byte[maxLength]);
- writer.Write((byte)0x89);
- writer.Seek(2, SeekOrigin.Current);
+ if (beheld.FacialHair?.ItemID > 0)
+ {
+ writer.Write((byte)(Layer.FacialHair + 1));
+ writer.Write(FacialHairInfo.FakeSerial(beheld.Owner.Serial) - 2);
+ }
+
+ writer.Write((byte)Layer.Invalid);
+
+ writer.WritePacketLength();
+ ns.Send(writer.Span);
+ }
+
+ public static void SendCorpseContent(this NetState ns, Mobile beholder, Corpse beheld)
+ {
+ if (ns.CannotSendPackets())
+ {
+ return;
+ }
+
+ var list = beheld.EquipItems;
+ var hairItemID = beheld.Hair?.ItemID ?? 0;
+ var facialHairItemID = beheld.FacialHair?.ItemID ?? 0;
+ var count = list.Count;
+ if (hairItemID > 0)
+ {
+ count++;
+ }
+
+ if (facialHairItemID > 0)
+ {
+ count++;
+ }
+
+ var maxLength = 5 + count * (ns.ContainerGridLines ? 20 : 19);
+ var writer = new SpanWriter(stackalloc byte[maxLength]);
+ writer.Write((byte)0x3C);
+ writer.Seek(4, SeekOrigin.Current); // Length and Count
+
+ var written = 0;
+ for (var i = 0; i < list.Count; ++i)
+ {
+ var child = list[i];
+
+ if (!child.Deleted && child.Parent == beheld && beholder.CanSee(child))
+ {
+ writer.Write(child.Serial);
+ writer.Write((ushort)child.ItemID);
+ writer.Write((byte)0); // signed, itemID offset
+ writer.Write((ushort)child.Amount);
+ writer.Write((short)child.X);
+ writer.Write((short)child.Y);
+ if (ns.ContainerGridLines)
+ {
+ writer.Write((byte)0); // Grid Location?
+ }
+ writer.Write(beheld.Serial);
+ writer.Write((ushort)child.Hue);
+
+ ++written;
+ }
+ }
+
+ if (hairItemID > 0)
+ {
+ writer.Write(HairInfo.FakeSerial(beheld.Owner.Serial) - 2);
+ writer.Write((ushort)hairItemID);
+ writer.Write((byte)0); // signed, itemID offset
+ writer.Write((ushort)1);
+ writer.Write(0); // X/Y
+ if (ns.ContainerGridLines)
+ {
+ writer.Write((byte)0); // Grid Location?
+ }
writer.Write(beheld.Serial);
+ writer.Write((ushort)beheld.Hair!.Hue);
- for (var i = 0; i < list.Count; ++i)
- {
- var item = list[i];
-
- if (!item.Deleted && beholder.CanSee(item) && item.Parent == beheld)
- {
- writer.Write((byte)(item.Layer + 1));
- writer.Write(item.Serial);
- }
- }
-
- if (beheld.Hair?.ItemID > 0)
- {
- writer.Write((byte)(Layer.Hair + 1));
- writer.Write(HairInfo.FakeSerial(beheld.Owner.Serial) - 2);
- }
-
- if (beheld.FacialHair?.ItemID > 0)
- {
- writer.Write((byte)(Layer.FacialHair + 1));
- writer.Write(FacialHairInfo.FakeSerial(beheld.Owner.Serial) - 2);
- }
-
- writer.Write((byte)Layer.Invalid);
-
- writer.WritePacketLength();
- ns.Send(writer.Span);
+ ++written;
}
- public static void SendCorpseContent(this NetState ns, Mobile beholder, Corpse beheld)
+ if (facialHairItemID > 0)
{
- if (ns.CannotSendPackets())
+ writer.Write(FacialHairInfo.FakeSerial(beheld.Owner.Serial) - 2);
+ writer.Write((ushort)facialHairItemID);
+ writer.Write((byte)0); // signed, itemID offset
+ writer.Write((ushort)1);
+ writer.Write(0); // X/Y
+ if (ns.ContainerGridLines)
{
- return;
+ writer.Write((byte)0); // Grid Location?
}
+ writer.Write(beheld.Serial);
+ writer.Write((ushort)beheld.FacialHair!.Hue);
- var list = beheld.EquipItems;
- var hairItemID = beheld.Hair?.ItemID ?? 0;
- var facialHairItemID = beheld.FacialHair?.ItemID ?? 0;
- var count = list.Count;
- if (hairItemID > 0)
- {
- count++;
- }
-
- if (facialHairItemID > 0)
- {
- count++;
- }
-
- var maxLength = 5 + count * (ns.ContainerGridLines ? 20 : 19);
- var writer = new SpanWriter(stackalloc byte[maxLength]);
- writer.Write((byte)0x3C);
- writer.Seek(4, SeekOrigin.Current); // Length and Count
-
- var written = 0;
- for (var i = 0; i < list.Count; ++i)
- {
- var child = list[i];
-
- if (!child.Deleted && child.Parent == beheld && beholder.CanSee(child))
- {
- writer.Write(child.Serial);
- writer.Write((ushort)child.ItemID);
- writer.Write((byte)0); // signed, itemID offset
- writer.Write((ushort)child.Amount);
- writer.Write((short)child.X);
- writer.Write((short)child.Y);
- if (ns.ContainerGridLines)
- {
- writer.Write((byte)0); // Grid Location?
- }
- writer.Write(beheld.Serial);
- writer.Write((ushort)child.Hue);
-
- ++written;
- }
- }
-
- if (hairItemID > 0)
- {
- writer.Write(HairInfo.FakeSerial(beheld.Owner.Serial) - 2);
- writer.Write((ushort)hairItemID);
- writer.Write((byte)0); // signed, itemID offset
- writer.Write((ushort)1);
- writer.Write(0); // X/Y
- if (ns.ContainerGridLines)
- {
- writer.Write((byte)0); // Grid Location?
- }
- writer.Write(beheld.Serial);
- writer.Write((ushort)beheld.Hair!.Hue);
-
- ++written;
- }
-
- if (facialHairItemID > 0)
- {
- writer.Write(FacialHairInfo.FakeSerial(beheld.Owner.Serial) - 2);
- writer.Write((ushort)facialHairItemID);
- writer.Write((byte)0); // signed, itemID offset
- writer.Write((ushort)1);
- writer.Write(0); // X/Y
- if (ns.ContainerGridLines)
- {
- writer.Write((byte)0); // Grid Location?
- }
- writer.Write(beheld.Serial);
- writer.Write((ushort)beheld.FacialHair!.Hue);
-
- ++written;
- }
-
- writer.Seek(1, SeekOrigin.Begin);
- writer.Write((ushort)writer.BytesWritten);
- writer.Write((ushort)written);
- writer.Seek(0, SeekOrigin.End);
- ns.Send(writer.Span);
+ ++written;
}
+
+ writer.Seek(1, SeekOrigin.Begin);
+ writer.Write((ushort)writer.BytesWritten);
+ writer.Write((ushort)written);
+ writer.Seek(0, SeekOrigin.End);
+ ns.Send(writer.Span);
}
}
diff --git a/Projects/UOContent/Items/Misc/Corpses/DecayedCorpse.cs b/Projects/UOContent/Items/Misc/Corpses/DecayedCorpse.cs
index a2a9f5f83..ead812e2f 100644
--- a/Projects/UOContent/Items/Misc/Corpses/DecayedCorpse.cs
+++ b/Projects/UOContent/Items/Misc/Corpses/DecayedCorpse.cs
@@ -1,108 +1,74 @@
using System;
+using ModernUO.Serialization;
-namespace Server.Items
+namespace Server.Items;
+
+[SerializationGenerator(2, false)]
+public partial class DecayedCorpse : Container
{
- public class DecayedCorpse : Container
+ private static TimeSpan _defaultDecayTime = TimeSpan.FromMinutes(7.0);
+
+ [TimerDrift]
+ [SerializableField(0, getter: "private", setter: "private")]
+ private Timer _decayTimer;
+
+ [DeserializeTimerField(0)]
+ private void DeserializeDecayTimer(TimeSpan delay) => BeginDecay(delay);
+
+ public DecayedCorpse(string name) : base(Utility.Random(0xECA, 9))
{
- private static readonly TimeSpan m_DefaultDecayTime = TimeSpan.FromMinutes(7.0);
- private DateTime m_DecayTime;
- private Timer m_DecayTimer;
+ Movable = false;
+ Name = name;
- public DecayedCorpse(string name) : base(Utility.Random(0xECA, 9))
+ BeginDecay(_defaultDecayTime);
+ }
+
+ // Do not display (x items, y stones)
+ public override bool DisplaysContent => false;
+
+ public void BeginDecay(TimeSpan delay)
+ {
+ _decayTimer?.Stop();
+ DecayTimer = new InternalTimer(this, delay);
+ DecayTimer.Start();
+ }
+
+ public override void OnAfterDelete()
+ {
+ _decayTimer?.Stop();
+ _decayTimer = null;
+ }
+
+ // Do not display (x items, y stones)
+ public override bool CheckContentDisplay(Mobile from) => false;
+
+ public override void AddNameProperty(IPropertyList list)
+ {
+ list.Add(1046414, Name); // the remains of ~1_NAME~
+ }
+
+ public override void OnSingleClick(Mobile from)
+ {
+ LabelTo(from, 1046414, Name); // the remains of ~1_NAME~
+ }
+
+ private void Deserialize(IGenericReader reader, int version)
+ {
+ if (reader.ReadBool())
{
- Movable = false;
- Name = name;
-
- BeginDecay(m_DefaultDecayTime);
+ BeginDecay(reader.ReadDeltaTime() - Core.Now);
}
+ }
- public DecayedCorpse(Serial serial) : base(serial)
+ private class InternalTimer : Timer
+ {
+ private DecayedCorpse _corpse;
+
+ public InternalTimer(DecayedCorpse c, TimeSpan delay) : base(delay) => _corpse = c;
+
+ protected override void OnTick()
{
- }
-
- // Do not display (x items, y stones)
- public override bool DisplaysContent => false;
-
- public void BeginDecay(TimeSpan delay)
- {
- m_DecayTimer?.Stop();
-
- m_DecayTime = Core.Now + delay;
-
- m_DecayTimer = new InternalTimer(this, delay);
- m_DecayTimer.Start();
- }
-
- public override void OnAfterDelete()
- {
- m_DecayTimer?.Stop();
-
- m_DecayTimer = null;
- }
-
- // Do not display (x items, y stones)
- public override bool CheckContentDisplay(Mobile from) => false;
-
- public override void AddNameProperty(IPropertyList list)
- {
- list.Add(1046414, Name); // the remains of ~1_NAME~
- }
-
- public override void OnSingleClick(Mobile from)
- {
- LabelTo(from, 1046414, Name); // the remains of ~1_NAME~
- }
-
- public override void Serialize(IGenericWriter writer)
- {
- base.Serialize(writer);
-
- writer.Write(1); // version
-
- writer.Write(m_DecayTimer != null);
-
- if (m_DecayTimer != null)
- {
- writer.WriteDeltaTime(m_DecayTime);
- }
- }
-
- public override void Deserialize(IGenericReader reader)
- {
- base.Deserialize(reader);
-
- var version = reader.ReadInt();
-
- switch (version)
- {
- case 0:
- {
- BeginDecay(m_DefaultDecayTime);
-
- break;
- }
- case 1:
- {
- if (reader.ReadBool())
- {
- BeginDecay(reader.ReadDeltaTime() - Core.Now);
- }
-
- break;
- }
- }
- }
-
- private class InternalTimer : Timer
- {
- private readonly DecayedCorpse m_Corpse;
-
- public InternalTimer(DecayedCorpse c, TimeSpan delay) : base(delay) => m_Corpse = c;
-
- protected override void OnTick()
- {
- m_Corpse.Delete();
- }
+ _corpse.Delete();
}
}
}
diff --git a/Projects/UOContent/Migrations/Server.Items.Corpse.v13.json b/Projects/UOContent/Migrations/Server.Items.Corpse.v13.json
new file mode 100644
index 000000000..1579dd24e
--- /dev/null
+++ b/Projects/UOContent/Migrations/Server.Items.Corpse.v13.json
@@ -0,0 +1,113 @@
+{
+ "version": 13,
+ "type": "Server.Items.Corpse",
+ "properties": [
+ {
+ "name": "RestoreEquip",
+ "type": "System.Collections.Generic.List\u003CServer.Item\u003E",
+ "rule": "ListMigrationRule",
+ "ruleArguments": [
+ "Server.Item",
+ "SerializableInterfaceMigrationRule"
+ ]
+ },
+ {
+ "name": "Flags",
+ "type": "Server.Items.CorpseFlag",
+ "rule": "EnumMigrationRule"
+ },
+ {
+ "name": "TimeOfDeath",
+ "type": "System.DateTime",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ "DeltaTime"
+ ]
+ },
+ {
+ "name": "RestoreTable",
+ "type": "System.Collections.Generic.Dictionary\u003CServer.Item, Server.Point3D\u003E",
+ "rule": "DictionaryMigrationRule",
+ "ruleArguments": [
+ "Server.Item",
+ "SerializableInterfaceMigrationRule",
+ "0",
+ "Server.Point3D",
+ "PrimitiveUOTypeMigrationRule",
+ "1",
+ "Point3D"
+ ]
+ },
+ {
+ "name": "DecayTimer",
+ "type": "Server.Timer",
+ "rule": "TimerMigrationRule",
+ "ruleArguments": [
+ "@TimerDrift"
+ ]
+ },
+ {
+ "name": "Looters",
+ "type": "System.Collections.Generic.HashSet\u003CServer.Mobile\u003E",
+ "rule": "HashSetMigrationRule",
+ "ruleArguments": [
+ "Server.Mobile",
+ "SerializableInterfaceMigrationRule"
+ ]
+ },
+ {
+ "name": "Killer",
+ "type": "Server.Mobile",
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "Aggressors",
+ "type": "System.Collections.Generic.List\u003CServer.Mobile\u003E",
+ "rule": "ListMigrationRule",
+ "ruleArguments": [
+ "Server.Mobile",
+ "SerializableInterfaceMigrationRule"
+ ]
+ },
+ {
+ "name": "Owner",
+ "type": "Server.Mobile",
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "CorpseName",
+ "type": "string",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "AccessLevel",
+ "type": "Server.AccessLevel",
+ "rule": "EnumMigrationRule"
+ },
+ {
+ "name": "Guild",
+ "type": "Server.Guilds.Guild",
+ "rule": "SerializableInterfaceMigrationRule"
+ },
+ {
+ "name": "Kills",
+ "type": "int",
+ "rule": "PrimitiveTypeMigrationRule",
+ "ruleArguments": [
+ ""
+ ]
+ },
+ {
+ "name": "EquipItems",
+ "type": "System.Collections.Generic.List\u003CServer.Item\u003E",
+ "rule": "ListMigrationRule",
+ "ruleArguments": [
+ "Server.Item",
+ "SerializableInterfaceMigrationRule"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Projects/UOContent/Migrations/Server.Items.DecayedCorpse.v2.json b/Projects/UOContent/Migrations/Server.Items.DecayedCorpse.v2.json
new file mode 100644
index 000000000..fbabaecdd
--- /dev/null
+++ b/Projects/UOContent/Migrations/Server.Items.DecayedCorpse.v2.json
@@ -0,0 +1,14 @@
+{
+ "version": 2,
+ "type": "Server.Items.DecayedCorpse",
+ "properties": [
+ {
+ "name": "DecayTimer",
+ "type": "Server.Timer",
+ "rule": "TimerMigrationRule",
+ "ruleArguments": [
+ "@TimerDrift"
+ ]
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Projects/UOContent/Skills/ForensicEval.cs b/Projects/UOContent/Skills/ForensicEval.cs
index 24ad06a9a..1b82311fa 100644
--- a/Projects/UOContent/Skills/ForensicEval.cs
+++ b/Projects/UOContent/Skills/ForensicEval.cs
@@ -46,50 +46,53 @@ namespace Server.SkillHandlers
}
else
{
- from.SendLocalizedMessage(501001); // You cannot determain anything useful.
+ from.SendLocalizedMessage(501001); // You cannot determine anything useful.
}
}
else if (target is Corpse c)
{
if (from.CheckTargetSkill(SkillName.Forensics, c, 0.0, 100.0))
{
- if (c.m_Forensicist != null)
+ if (c.Forensicist != null)
{
- from.SendLocalizedMessage(
- 1042750,
- c.m_Forensicist
- ); // The forensicist ~1_NAME~ has already discovered that:
+ // The forensicist ~1_NAME~ has already discovered that:
+ from.SendLocalizedMessage(1042750, c.Forensicist);
}
else
{
- c.m_Forensicist = from.Name;
+ c.Forensicist = from.Name;
}
if (((Body)c.Amount).IsHuman)
{
- from.SendLocalizedMessage(
- 1042751,
- c.Killer == null ? "no one" : c.Killer.Name
- ); // This person was killed by ~1_KILLER_NAME~
+ // This person was killed by ~1_KILLER_NAME~
+ from.SendLocalizedMessage(1042751, c.Killer == null ? "no one" : c.Killer.Name);
}
if (c.Looters.Count > 0)
{
using var sb = new ValueStringBuilder(stackalloc char[128]);
- for (var i = 0; i < c.Looters.Count; i++)
+ int i = 0;
+ foreach (var looter in c.Looters)
{
- if (i > 0)
+ if (i == c.Looters.Count - 1)
{
- sb.Append(i == c.Looters.Count - 1 ? ", and " : ", ");
+ sb.Append($", and {looter.Name}");
+ }
+ else if (i > 0)
+ {
+ sb.Append($", {looter.Name}");
+ }
+ else
+ {
+ sb.Append(looter.Name);
}
- sb.Append(c.Looters[i].Name);
+ i++;
}
- from.SendLocalizedMessage(
- 1042752,
- sb.ToString()
- ); // This body has been disturbed by ~1_PLAYER_NAMES~
+ // This body has been disturbed by ~1_PLAYER_NAMES~
+ from.SendLocalizedMessage(1042752, sb.ToString());
}
else
{
@@ -98,7 +101,7 @@ namespace Server.SkillHandlers
}
else
{
- from.SendLocalizedMessage(501001); // You cannot determain anything useful.
+ from.SendLocalizedMessage(501001); // You cannot determine anything useful.
}
}
else if (target is ILockpickable p)