fix: Hair and facial hair "teleporting" when mobile dies several times (#1901)
### Summary * Added World.NewVirtual for creating virtual serial numbers * Reserved range 0x7EEEEEEE to 0x7FFFFFFF for virtual serials * Hair and Facial hair (for mobiles) now use virtual serials instead of FakeSerial() functions * Consolidated virtual hair to a single `VirtualHairInfo` class. Corpse hair and facial hair now persists across save/load and hair and facial hair no longer teleport to newest corpse.
This commit is contained in:
parent
883d873c1c
commit
91e37fb8d4
15 changed files with 548 additions and 357 deletions
|
|
@ -504,13 +504,16 @@ namespace Server.Tests.Network
|
||||||
|
|
||||||
var eq = beheld.Items;
|
var eq = beheld.Items;
|
||||||
var count = eq.Count;
|
var count = eq.Count;
|
||||||
|
var hair = beheld.Hair;
|
||||||
|
var facialHair = beheld.FacialHair;
|
||||||
|
|
||||||
if (beheld.HairItemID > 0)
|
|
||||||
|
if (hair != null)
|
||||||
{
|
{
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (beheld.FacialHairItemID > 0)
|
if (facialHair != null)
|
||||||
{
|
{
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
@ -570,19 +573,19 @@ namespace Server.Tests.Network
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (beheld.HairItemID > 0)
|
if (hair?.ItemId > 0)
|
||||||
{
|
{
|
||||||
if (m_DupedLayers![(int)Layer.Hair] != m_Version)
|
if (m_DupedLayers![(int)Layer.Hair] != m_Version)
|
||||||
{
|
{
|
||||||
m_DupedLayers[(int)Layer.Hair] = m_Version;
|
m_DupedLayers[(int)Layer.Hair] = m_Version;
|
||||||
hue = beheld.HairHue;
|
hue = hair.Hue;
|
||||||
|
|
||||||
if (beheld.SolidHueOverride >= 0)
|
if (beheld.SolidHueOverride >= 0)
|
||||||
{
|
{
|
||||||
hue = beheld.SolidHueOverride;
|
hue = beheld.SolidHueOverride;
|
||||||
}
|
}
|
||||||
|
|
||||||
var itemID = beheld.HairItemID & itemIdMask;
|
var itemID = hair.ItemId & itemIdMask;
|
||||||
var writeHue = newPacket || hue != 0;
|
var writeHue = newPacket || hue != 0;
|
||||||
|
|
||||||
if (!newPacket && writeHue)
|
if (!newPacket && writeHue)
|
||||||
|
|
@ -590,7 +593,7 @@ namespace Server.Tests.Network
|
||||||
itemID |= 0x8000;
|
itemID |= 0x8000;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream.Write(HairInfo.FakeSerial(beheld.Serial));
|
Stream.Write(hair.VirtualSerial);
|
||||||
Stream.Write((ushort)itemID);
|
Stream.Write((ushort)itemID);
|
||||||
Stream.Write((byte)Layer.Hair);
|
Stream.Write((byte)Layer.Hair);
|
||||||
|
|
||||||
|
|
@ -601,19 +604,19 @@ namespace Server.Tests.Network
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (beheld.FacialHairItemID > 0)
|
if (facialHair?.ItemId > 0)
|
||||||
{
|
{
|
||||||
if (m_DupedLayers![(int)Layer.FacialHair] != m_Version)
|
if (m_DupedLayers![(int)Layer.FacialHair] != m_Version)
|
||||||
{
|
{
|
||||||
m_DupedLayers[(int)Layer.FacialHair] = m_Version;
|
m_DupedLayers[(int)Layer.FacialHair] = m_Version;
|
||||||
hue = beheld.FacialHairHue;
|
hue = facialHair.Hue;
|
||||||
|
|
||||||
if (beheld.SolidHueOverride >= 0)
|
if (beheld.SolidHueOverride >= 0)
|
||||||
{
|
{
|
||||||
hue = beheld.SolidHueOverride;
|
hue = beheld.SolidHueOverride;
|
||||||
}
|
}
|
||||||
|
|
||||||
var itemID = beheld.FacialHairItemID & itemIdMask;
|
var itemID = facialHair.ItemId & itemIdMask;
|
||||||
var writeHue = newPacket || hue != 0;
|
var writeHue = newPacket || hue != 0;
|
||||||
|
|
||||||
if (!newPacket && writeHue)
|
if (!newPacket && writeHue)
|
||||||
|
|
@ -621,7 +624,7 @@ namespace Server.Tests.Network
|
||||||
itemID |= 0x8000;
|
itemID |= 0x8000;
|
||||||
}
|
}
|
||||||
|
|
||||||
Stream.Write(FacialHairInfo.FakeSerial(beheld.Serial));
|
Stream.Write(facialHair.VirtualSerial);
|
||||||
Stream.Write((ushort)itemID);
|
Stream.Write((ushort)itemID);
|
||||||
Stream.Write((byte)Layer.FacialHair);
|
Stream.Write((byte)Layer.FacialHair);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ namespace Server.Tests
|
||||||
var expected = new HairEquipUpdate(m).Compile();
|
var expected = new HairEquipUpdate(m).Compile();
|
||||||
|
|
||||||
var ns = PacketTestUtilities.CreateTestNetState();
|
var ns = PacketTestUtilities.CreateTestNetState();
|
||||||
ns.SendHairEquipUpdatePacket(m, HairInfo.FakeSerial(m.Serial), m.HairItemID, m.HairHue, Layer.Hair);
|
ns.SendHairEquipUpdatePacket(m, (uint)m.Hair.VirtualSerial, m.Hair.ItemId, m.Hair.Hue, Layer.Hair);
|
||||||
|
|
||||||
var result = ns.SendPipe.Reader.AvailableToRead();
|
var result = ns.SendPipe.Reader.AvailableToRead();
|
||||||
AssertThat.Equal(result, expected);
|
AssertThat.Equal(result, expected);
|
||||||
|
|
@ -32,7 +32,7 @@ namespace Server.Tests
|
||||||
var expected = new RemoveHair(m).Compile();
|
var expected = new RemoveHair(m).Compile();
|
||||||
|
|
||||||
var ns = PacketTestUtilities.CreateTestNetState();
|
var ns = PacketTestUtilities.CreateTestNetState();
|
||||||
ns.SendRemoveHairPacket(HairInfo.FakeSerial(m.Serial));
|
ns.SendRemoveHairPacket((uint) m.Hair.VirtualSerial);
|
||||||
|
|
||||||
var result = ns.SendPipe.Reader.AvailableToRead();
|
var result = ns.SendPipe.Reader.AvailableToRead();
|
||||||
AssertThat.Equal(result, expected);
|
AssertThat.Equal(result, expected);
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ namespace Server.Network
|
||||||
{
|
{
|
||||||
var hue = parent.SolidHueOverride >= 0 ? parent.SolidHueOverride : parent.HairHue;
|
var hue = parent.SolidHueOverride >= 0 ? parent.SolidHueOverride : parent.HairHue;
|
||||||
|
|
||||||
Stream.Write(HairInfo.FakeSerial(parent.Serial));
|
Stream.Write(parent.Hair.VirtualSerial);
|
||||||
Stream.Write((short)parent.HairItemID);
|
Stream.Write((short)parent.HairItemID);
|
||||||
Stream.Write((byte)0);
|
Stream.Write((byte)0);
|
||||||
Stream.Write((byte)Layer.Hair);
|
Stream.Write((byte)Layer.Hair);
|
||||||
|
|
@ -23,7 +23,7 @@ namespace Server.Network
|
||||||
{
|
{
|
||||||
var hue = parent.SolidHueOverride >= 0 ? parent.SolidHueOverride : parent.FacialHairHue;
|
var hue = parent.SolidHueOverride >= 0 ? parent.SolidHueOverride : parent.FacialHairHue;
|
||||||
|
|
||||||
Stream.Write(FacialHairInfo.FakeSerial(parent.Serial));
|
Stream.Write(parent.FacialHair.VirtualSerial);
|
||||||
Stream.Write((short)parent.FacialHairItemID);
|
Stream.Write((short)parent.FacialHairItemID);
|
||||||
Stream.Write((byte)0);
|
Stream.Write((byte)0);
|
||||||
Stream.Write((byte)Layer.FacialHair);
|
Stream.Write((byte)Layer.FacialHair);
|
||||||
|
|
@ -37,7 +37,7 @@ namespace Server.Network
|
||||||
public RemoveHair(Mobile parent)
|
public RemoveHair(Mobile parent)
|
||||||
: base(0x1D, 5)
|
: base(0x1D, 5)
|
||||||
{
|
{
|
||||||
Stream.Write(HairInfo.FakeSerial(parent.Serial));
|
Stream.Write(parent.Hair.VirtualSerial);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -46,7 +46,7 @@ namespace Server.Network
|
||||||
public RemoveFacialHair(Mobile parent)
|
public RemoveFacialHair(Mobile parent)
|
||||||
: base(0x1D, 5)
|
: base(0x1D, 5)
|
||||||
{
|
{
|
||||||
Stream.Write(FacialHairInfo.FakeSerial(parent.Serial));
|
Stream.Write(parent.FacialHair.VirtualSerial);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
24
Projects/Server.Tests/Tests/World/VirtualSerialTests.cs
Normal file
24
Projects/Server.Tests/Tests/World/VirtualSerialTests.cs
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Server.Tests;
|
||||||
|
|
||||||
|
[Collection("Sequential Tests")]
|
||||||
|
public class VirtualSerialTests : IClassFixture<ServerFixture>
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void TestNewVirtualGetsAndRollover()
|
||||||
|
{
|
||||||
|
// Acquire virtual serials until we hit the max
|
||||||
|
Serial lastSerial = World.NewVirtual;
|
||||||
|
do
|
||||||
|
{
|
||||||
|
Serial virtualSerial = World.NewVirtual;
|
||||||
|
Assert.Equal(lastSerial + 1, virtualSerial);
|
||||||
|
lastSerial = (Serial) (uint) virtualSerial;
|
||||||
|
} while (lastSerial != World.MaxVirtualSerial);
|
||||||
|
|
||||||
|
// Next one should be MaxItemSerial (the first VirtualSerial) due to rollover
|
||||||
|
var nextSerial = World.NewVirtual;
|
||||||
|
Assert.Equal(nextSerial, (Serial)World.ResetVirtualSerial);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Buffers;
|
using System.Buffers;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using ModernUO.Serialization;
|
||||||
using Server.Network;
|
using Server.Network;
|
||||||
|
|
||||||
namespace Server;
|
namespace Server;
|
||||||
|
|
@ -65,83 +66,33 @@ public static class OutgoingVirtualHairPackets
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public abstract class BaseHairInfo
|
[SerializationGenerator(0, false)]
|
||||||
|
public partial class VirtualHairInfo
|
||||||
{
|
{
|
||||||
protected BaseHairInfo(int itemid, int hue = 0)
|
[SerializableField(0)]
|
||||||
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
|
private int _itemId;
|
||||||
|
|
||||||
|
[SerializableField(1)]
|
||||||
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
|
private int _hue;
|
||||||
|
|
||||||
|
public VirtualHairInfo() : this(0)
|
||||||
{
|
{
|
||||||
ItemID = itemid;
|
|
||||||
Hue = hue;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected BaseHairInfo(IGenericReader reader)
|
public VirtualHairInfo(int itemid, int hue = 0)
|
||||||
{
|
{
|
||||||
var version = reader.ReadInt();
|
_itemId = itemid;
|
||||||
|
_hue = hue;
|
||||||
switch (version)
|
VirtualSerial = World.NewVirtual;
|
||||||
{
|
|
||||||
case 0:
|
|
||||||
{
|
|
||||||
ItemID = reader.ReadInt();
|
|
||||||
Hue = reader.ReadInt();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[CommandProperty(AccessLevel.GameMaster)]
|
[AfterDeserialization]
|
||||||
public int ItemID { get; set; }
|
private void AfterDeserialization()
|
||||||
|
|
||||||
[CommandProperty(AccessLevel.GameMaster)]
|
|
||||||
public int Hue { get; set; }
|
|
||||||
|
|
||||||
public virtual void Serialize(IGenericWriter writer)
|
|
||||||
{
|
{
|
||||||
writer.Write(0); // version
|
VirtualSerial = World.NewVirtual;
|
||||||
writer.Write(ItemID);
|
|
||||||
writer.Write(Hue);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
public Serial VirtualSerial { get; private set; }
|
||||||
public class HairInfo : BaseHairInfo
|
|
||||||
{
|
|
||||||
public HairInfo(int itemid)
|
|
||||||
: base(itemid)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public HairInfo(int itemid, int hue)
|
|
||||||
: base(itemid, hue)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public HairInfo(IGenericReader reader)
|
|
||||||
: base(reader)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Can we make this higher for newer clients?
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static uint FakeSerial(Serial m) => 0x7FFFFFFF - 0x400 - m.Value * 4;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class FacialHairInfo : BaseHairInfo
|
|
||||||
{
|
|
||||||
public FacialHairInfo(int itemid)
|
|
||||||
: base(itemid)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public FacialHairInfo(int itemid, int hue)
|
|
||||||
: base(itemid, hue)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public FacialHairInfo(IGenericReader reader)
|
|
||||||
: base(reader)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Can we make this higher for newer clients?
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static uint FakeSerial(Serial m) => 0x7FFFFFFF - 0x400 - 1 - m.Value* 4;
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
22
Projects/Server/Migrations/Server.VirtualHairInfo.v0.json
generated
Normal file
22
Projects/Server/Migrations/Server.VirtualHairInfo.v0.json
generated
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"version": 0,
|
||||||
|
"type": "Server.VirtualHairInfo",
|
||||||
|
"properties": [
|
||||||
|
{
|
||||||
|
"name": "ItemId",
|
||||||
|
"type": "int",
|
||||||
|
"rule": "PrimitiveTypeMigrationRule",
|
||||||
|
"ruleArguments": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hue",
|
||||||
|
"type": "int",
|
||||||
|
"rule": "PrimitiveTypeMigrationRule",
|
||||||
|
"ruleArguments": [
|
||||||
|
""
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
@ -187,7 +187,7 @@ public delegate bool AllowBeneficialHandler(Mobile from, Mobile target);
|
||||||
public delegate bool AllowHarmfulHandler(Mobile from, Mobile target);
|
public delegate bool AllowHarmfulHandler(Mobile from, Mobile target);
|
||||||
|
|
||||||
public delegate Container CreateCorpseHandler(
|
public delegate Container CreateCorpseHandler(
|
||||||
Mobile from, HairInfo hair, FacialHairInfo facialhair, List<Item> initialContent, List<Item> equippedItems
|
Mobile from, List<Item> initialContent, List<Item> equippedItems
|
||||||
);
|
);
|
||||||
|
|
||||||
public delegate int AOSStatusHandler(Mobile from, int index);
|
public delegate int AOSStatusHandler(Mobile from, int index);
|
||||||
|
|
@ -273,7 +273,9 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
|
||||||
private TimerExecutionToken _expireAggrTimerToken;
|
private TimerExecutionToken _expireAggrTimerToken;
|
||||||
private TimerExecutionToken _expireCombatantTimerToken;
|
private TimerExecutionToken _expireCombatantTimerToken;
|
||||||
private TimerExecutionToken _expireCriminalTimerToken;
|
private TimerExecutionToken _expireCriminalTimerToken;
|
||||||
private FacialHairInfo m_FacialHair;
|
private VirtualHairInfo _facialHair;
|
||||||
|
public VirtualHairInfo FacialHair => _facialHair ??= new VirtualHairInfo(FacialHairItemID, FacialHairHue);
|
||||||
|
|
||||||
private int m_Fame, m_Karma;
|
private int m_Fame, m_Karma;
|
||||||
private bool m_Female, m_Warmode, m_Hidden, m_Blessed, m_Flying;
|
private bool m_Female, m_Warmode, m_Hidden, m_Blessed, m_Flying;
|
||||||
private int m_Followers, m_FollowersMax;
|
private int m_Followers, m_FollowersMax;
|
||||||
|
|
@ -282,7 +284,9 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
|
||||||
private BaseGuild m_Guild;
|
private BaseGuild m_Guild;
|
||||||
private string m_GuildTitle;
|
private string m_GuildTitle;
|
||||||
|
|
||||||
private HairInfo m_Hair;
|
private VirtualHairInfo _hair;
|
||||||
|
public VirtualHairInfo Hair => _hair ??= new VirtualHairInfo(HairItemID, HairHue);
|
||||||
|
|
||||||
private int m_Hits, m_Stam, m_Mana;
|
private int m_Hits, m_Stam, m_Mana;
|
||||||
|
|
||||||
private Item m_Holding;
|
private Item m_Holding;
|
||||||
|
|
@ -2165,59 +2169,62 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
|
||||||
[CommandProperty(AccessLevel.GameMaster)]
|
[CommandProperty(AccessLevel.GameMaster)]
|
||||||
public int HairItemID
|
public int HairItemID
|
||||||
{
|
{
|
||||||
get => m_Hair?.ItemID ?? 0;
|
get => _hair?.ItemId ?? 0;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (m_Hair == null && value > 0)
|
if (_hair == null && value > 0)
|
||||||
{
|
{
|
||||||
m_Hair = new HairInfo(value);
|
_hair = new VirtualHairInfo(value);
|
||||||
}
|
}
|
||||||
else if (value <= 0)
|
else if (value <= 0)
|
||||||
{
|
{
|
||||||
m_Hair = null;
|
_hair = null;
|
||||||
}
|
}
|
||||||
else if (m_Hair != null)
|
else if (_hair != null)
|
||||||
{
|
{
|
||||||
m_Hair.ItemID = value;
|
_hair.ItemId = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Delta(MobileDelta.Hair);
|
Delta(MobileDelta.Hair);
|
||||||
|
this.MarkDirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[CommandProperty(AccessLevel.GameMaster)]
|
[CommandProperty(AccessLevel.GameMaster)]
|
||||||
public int FacialHairItemID
|
public int FacialHairItemID
|
||||||
{
|
{
|
||||||
get => m_FacialHair?.ItemID ?? 0;
|
get => _facialHair?.ItemId ?? 0;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (m_FacialHair == null && value > 0)
|
if (_facialHair == null && value > 0)
|
||||||
{
|
{
|
||||||
m_FacialHair = new FacialHairInfo(value);
|
_facialHair = new VirtualHairInfo(value);
|
||||||
}
|
}
|
||||||
else if (value <= 0)
|
else if (value <= 0)
|
||||||
{
|
{
|
||||||
m_FacialHair = null;
|
_facialHair = null;
|
||||||
}
|
}
|
||||||
else if (m_FacialHair != null)
|
else if (_facialHair != null)
|
||||||
{
|
{
|
||||||
m_FacialHair.ItemID = value;
|
_facialHair.ItemId = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
Delta(MobileDelta.FacialHair);
|
Delta(MobileDelta.FacialHair);
|
||||||
|
this.MarkDirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[CommandProperty(AccessLevel.GameMaster)]
|
[CommandProperty(AccessLevel.GameMaster)]
|
||||||
public int HairHue
|
public int HairHue
|
||||||
{
|
{
|
||||||
get => m_Hair?.Hue ?? 0;
|
get => _hair?.Hue ?? 0;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (m_Hair != null)
|
if (_hair != null)
|
||||||
{
|
{
|
||||||
m_Hair.Hue = value;
|
_hair.Hue = value;
|
||||||
Delta(MobileDelta.Hair);
|
Delta(MobileDelta.Hair);
|
||||||
|
this.MarkDirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2225,13 +2232,14 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
|
||||||
[CommandProperty(AccessLevel.GameMaster)]
|
[CommandProperty(AccessLevel.GameMaster)]
|
||||||
public int FacialHairHue
|
public int FacialHairHue
|
||||||
{
|
{
|
||||||
get => m_FacialHair?.Hue ?? 0;
|
get => _facialHair?.Hue ?? 0;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
if (m_FacialHair != null)
|
if (_facialHair != null)
|
||||||
{
|
{
|
||||||
m_FacialHair.Hue = value;
|
_facialHair.Hue = value;
|
||||||
Delta(MobileDelta.FacialHair);
|
Delta(MobileDelta.FacialHair);
|
||||||
|
this.MarkDirty();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2280,12 +2288,12 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
|
||||||
|
|
||||||
byte hairflag = 0x00;
|
byte hairflag = 0x00;
|
||||||
|
|
||||||
if (m_Hair != null)
|
if (_hair != null)
|
||||||
{
|
{
|
||||||
hairflag |= 0x01;
|
hairflag |= 0x01;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_FacialHair != null)
|
if (_facialHair != null)
|
||||||
{
|
{
|
||||||
hairflag |= 0x02;
|
hairflag |= 0x02;
|
||||||
}
|
}
|
||||||
|
|
@ -2294,12 +2302,12 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
|
||||||
|
|
||||||
if ((hairflag & 0x01) != 0)
|
if ((hairflag & 0x01) != 0)
|
||||||
{
|
{
|
||||||
m_Hair?.Serialize(writer);
|
_hair?.Serialize(writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((hairflag & 0x02) != 0)
|
if ((hairflag & 0x02) != 0)
|
||||||
{
|
{
|
||||||
m_FacialHair?.Serialize(writer);
|
_facialHair?.Serialize(writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.Write(Race);
|
writer.Write(Race);
|
||||||
|
|
@ -2434,8 +2442,8 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
|
||||||
m_Map?.OnLeave(this);
|
m_Map?.OnLeave(this);
|
||||||
m_Map = null;
|
m_Map = null;
|
||||||
|
|
||||||
m_Hair = null;
|
_hair = null;
|
||||||
m_FacialHair = null;
|
_facialHair = null;
|
||||||
m_MountItem = null;
|
m_MountItem = null;
|
||||||
|
|
||||||
World.RemoveEntity(this);
|
World.RemoveEntity(this);
|
||||||
|
|
@ -2708,14 +2716,14 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
|
||||||
sendFacialHair = true;
|
sendFacialHair = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var hairSerial = HairInfo.FakeSerial(Serial);
|
var hairSerial = Hair.VirtualSerial;
|
||||||
var hairLength = removeHair
|
var hairLength = removeHair
|
||||||
? OutgoingVirtualHairPackets.RemovePacketLength
|
? OutgoingVirtualHairPackets.RemovePacketLength
|
||||||
: OutgoingVirtualHairPackets.EquipUpdatePacketLength;
|
: OutgoingVirtualHairPackets.EquipUpdatePacketLength;
|
||||||
|
|
||||||
Span<byte> hairPacket = stackalloc byte[hairLength].InitializePacket();
|
Span<byte> hairPacket = stackalloc byte[hairLength].InitializePacket();
|
||||||
|
|
||||||
var facialHairSerial = FacialHairInfo.FakeSerial(Serial);
|
var facialHairSerial = FacialHair.VirtualSerial;
|
||||||
var facialHairLength = removeFacialHair
|
var facialHairLength = removeFacialHair
|
||||||
? OutgoingVirtualHairPackets.RemovePacketLength
|
? OutgoingVirtualHairPackets.RemovePacketLength
|
||||||
: OutgoingVirtualHairPackets.EquipUpdatePacketLength;
|
: OutgoingVirtualHairPackets.EquipUpdatePacketLength;
|
||||||
|
|
@ -2806,14 +2814,14 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
|
||||||
{
|
{
|
||||||
if (removeHair)
|
if (removeHair)
|
||||||
{
|
{
|
||||||
OutgoingVirtualHairPackets.CreateRemoveHairPacket(hairPacket, hairSerial);
|
OutgoingVirtualHairPackets.CreateRemoveHairPacket(hairPacket, (uint)hairSerial);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
OutgoingVirtualHairPackets.CreateHairEquipUpdatePacket(
|
OutgoingVirtualHairPackets.CreateHairEquipUpdatePacket(
|
||||||
hairPacket,
|
hairPacket,
|
||||||
this,
|
this,
|
||||||
hairSerial,
|
(uint)hairSerial,
|
||||||
HairItemID,
|
HairItemID,
|
||||||
HairHue,
|
HairHue,
|
||||||
Layer.Hair
|
Layer.Hair
|
||||||
|
|
@ -2827,14 +2835,14 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
|
||||||
{
|
{
|
||||||
if (removeFacialHair)
|
if (removeFacialHair)
|
||||||
{
|
{
|
||||||
OutgoingVirtualHairPackets.CreateRemoveHairPacket(facialHairPacket, facialHairSerial);
|
OutgoingVirtualHairPackets.CreateRemoveHairPacket(facialHairPacket, (uint)facialHairSerial);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
OutgoingVirtualHairPackets.CreateHairEquipUpdatePacket(
|
OutgoingVirtualHairPackets.CreateHairEquipUpdatePacket(
|
||||||
facialHairPacket,
|
facialHairPacket,
|
||||||
this,
|
this,
|
||||||
facialHairSerial,
|
(uint)facialHairSerial,
|
||||||
FacialHairItemID,
|
FacialHairItemID,
|
||||||
FacialHairHue,
|
FacialHairHue,
|
||||||
Layer.FacialHair
|
Layer.FacialHair
|
||||||
|
|
@ -2943,14 +2951,14 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
|
||||||
{
|
{
|
||||||
if (removeHair)
|
if (removeHair)
|
||||||
{
|
{
|
||||||
OutgoingVirtualHairPackets.CreateRemoveHairPacket(hairPacket, hairSerial);
|
OutgoingVirtualHairPackets.CreateRemoveHairPacket(hairPacket, (uint)hairSerial);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
OutgoingVirtualHairPackets.CreateHairEquipUpdatePacket(
|
OutgoingVirtualHairPackets.CreateHairEquipUpdatePacket(
|
||||||
hairPacket,
|
hairPacket,
|
||||||
this,
|
this,
|
||||||
hairSerial,
|
(uint)hairSerial,
|
||||||
HairItemID,
|
HairItemID,
|
||||||
HairHue,
|
HairHue,
|
||||||
Layer.Hair
|
Layer.Hair
|
||||||
|
|
@ -2964,14 +2972,14 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
|
||||||
{
|
{
|
||||||
if (removeFacialHair)
|
if (removeFacialHair)
|
||||||
{
|
{
|
||||||
OutgoingVirtualHairPackets.CreateRemoveHairPacket(facialHairPacket, facialHairSerial);
|
OutgoingVirtualHairPackets.CreateRemoveHairPacket(facialHairPacket, (uint)facialHairSerial);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
OutgoingVirtualHairPackets.CreateHairEquipUpdatePacket(
|
OutgoingVirtualHairPackets.CreateHairEquipUpdatePacket(
|
||||||
facialHairPacket,
|
facialHairPacket,
|
||||||
this,
|
this,
|
||||||
facialHairSerial,
|
(uint)facialHairSerial,
|
||||||
FacialHairItemID,
|
FacialHairItemID,
|
||||||
FacialHairHue,
|
FacialHairHue,
|
||||||
Layer.FacialHair
|
Layer.FacialHair
|
||||||
|
|
@ -4764,19 +4772,7 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
HairInfo hair = null;
|
var c = CreateCorpseHandler?.Invoke(this, content, equip);
|
||||||
if (m_Hair != null)
|
|
||||||
{
|
|
||||||
hair = new HairInfo(m_Hair.ItemID, m_Hair.Hue);
|
|
||||||
}
|
|
||||||
|
|
||||||
FacialHairInfo facialhair = null;
|
|
||||||
if (m_FacialHair != null)
|
|
||||||
{
|
|
||||||
facialhair = new FacialHairInfo(m_FacialHair.ItemID, m_FacialHair.Hue);
|
|
||||||
}
|
|
||||||
|
|
||||||
var c = CreateCorpseHandler?.Invoke(this, hair, facialhair, content, equip);
|
|
||||||
|
|
||||||
if (m_Map != null)
|
if (m_Map != null)
|
||||||
{
|
{
|
||||||
|
|
@ -6092,12 +6088,14 @@ public partial class Mobile : IHued, IComparable<Mobile>, ISpawnable, IObjectPro
|
||||||
|
|
||||||
if ((hairflag & 0x01) != 0)
|
if ((hairflag & 0x01) != 0)
|
||||||
{
|
{
|
||||||
m_Hair = new HairInfo(reader);
|
_hair = new VirtualHairInfo();
|
||||||
|
_hair.Deserialize(reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((hairflag & 0x02) != 0)
|
if ((hairflag & 0x02) != 0)
|
||||||
{
|
{
|
||||||
m_FacialHair = new FacialHairInfo(reader);
|
_facialHair = new VirtualHairInfo();
|
||||||
|
_facialHair.Deserialize(reader);
|
||||||
}
|
}
|
||||||
|
|
||||||
goto case 29;
|
goto case 29;
|
||||||
|
|
|
||||||
|
|
@ -672,7 +672,7 @@ public static class OutgoingMobilePackets
|
||||||
itemID |= 0x8000;
|
itemID |= 0x8000;
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.Write(HairInfo.FakeSerial(beheld.Serial));
|
writer.Write(beheld.Hair.VirtualSerial);
|
||||||
writer.Write((ushort)itemID);
|
writer.Write((ushort)itemID);
|
||||||
writer.Write((byte)Layer.Hair);
|
writer.Write((byte)Layer.Hair);
|
||||||
|
|
||||||
|
|
@ -695,7 +695,7 @@ public static class OutgoingMobilePackets
|
||||||
itemID |= 0x8000;
|
itemID |= 0x8000;
|
||||||
}
|
}
|
||||||
|
|
||||||
writer.Write(FacialHairInfo.FakeSerial(beheld.Serial));
|
writer.Write(beheld.FacialHair.VirtualSerial);
|
||||||
writer.Write((ushort)itemID);
|
writer.Write((ushort)itemID);
|
||||||
writer.Write((byte)Layer.FacialHair);
|
writer.Write((byte)Layer.FacialHair);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -53,13 +53,37 @@ public static class World
|
||||||
|
|
||||||
public const bool DirtyTrackingEnabled = false;
|
public const bool DirtyTrackingEnabled = false;
|
||||||
public const uint ItemOffset = 0x40000000;
|
public const uint ItemOffset = 0x40000000;
|
||||||
public const uint MaxItemSerial = 0x7FFFFFFF;
|
public const uint MaxItemSerial = 0x7EEEEEEE;
|
||||||
public const uint MaxMobileSerial = ItemOffset - 1;
|
public const uint MaxMobileSerial = ItemOffset - 1;
|
||||||
|
|
||||||
|
public const uint ResetVirtualSerial = MaxItemSerial;
|
||||||
|
public const uint MaxVirtualSerial = 0x7FFFFFFF;
|
||||||
|
private static uint _nextVirtualSerial = ResetVirtualSerial;
|
||||||
|
|
||||||
public static Serial NewMobile => _mobilePersistence.NewEntity;
|
public static Serial NewMobile => _mobilePersistence.NewEntity;
|
||||||
public static Serial NewItem => _itemPersistence.NewEntity;
|
public static Serial NewItem => _itemPersistence.NewEntity;
|
||||||
public static Serial NewGuild => _guildPersistence.NewEntity;
|
public static Serial NewGuild => _guildPersistence.NewEntity;
|
||||||
|
|
||||||
|
// Virtual things don't persist across saves
|
||||||
|
public static Serial NewVirtual
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
#if THREADGUARD
|
||||||
|
if (Thread.CurrentThread != Core.Thread)
|
||||||
|
{
|
||||||
|
logger.Error(
|
||||||
|
"Attempted to get a new virtual serial from the wrong thread!\n{StackTrace}",
|
||||||
|
new StackTrace()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
var value = _nextVirtualSerial > MaxVirtualSerial ? ResetVirtualSerial : _nextVirtualSerial;
|
||||||
|
_nextVirtualSerial = value + 1;
|
||||||
|
return (Serial)value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static Dictionary<Serial, Item> Items => _itemPersistence.EntitiesBySerial;
|
public static Dictionary<Serial, Item> Items => _itemPersistence.EntitiesBySerial;
|
||||||
public static Dictionary<Serial, Mobile> Mobiles => _mobilePersistence.EntitiesBySerial;
|
public static Dictionary<Serial, Mobile> Mobiles => _mobilePersistence.EntitiesBySerial;
|
||||||
public static Dictionary<Serial, BaseGuild> Guilds => _guildPersistence.EntitiesBySerial;
|
public static Dictionary<Serial, BaseGuild> Guilds => _guildPersistence.EntitiesBySerial;
|
||||||
|
|
|
||||||
|
|
@ -7,48 +7,51 @@ public sealed class CorpseEquip : Packet
|
||||||
{
|
{
|
||||||
public CorpseEquip(Mobile beholder, Corpse beheld) : base(0x89)
|
public CorpseEquip(Mobile beholder, Corpse beheld) : base(0x89)
|
||||||
{
|
{
|
||||||
var list = beheld.EquipItems;
|
var list = beheld.EquipItems;
|
||||||
|
|
||||||
var count = list.Count;
|
var count = list.Count;
|
||||||
if (beheld.Hair?.ItemID > 0)
|
var hair = beheld.Hair;
|
||||||
{
|
var facialHair = beheld.FacialHair;
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (beheld.FacialHair?.ItemID > 0)
|
if (hair != null)
|
||||||
{
|
{
|
||||||
count++;
|
count++;
|
||||||
}
|
|
||||||
|
|
||||||
EnsureCapacity(8 + count * 5);
|
|
||||||
|
|
||||||
Stream.Write(beheld.Serial);
|
|
||||||
|
|
||||||
for (var i = 0; i < list.Count; ++i)
|
|
||||||
{
|
|
||||||
var item = list[i];
|
|
||||||
|
|
||||||
if (!item.Deleted && beholder.CanSee(item) && item.Parent == beheld)
|
|
||||||
{
|
|
||||||
Stream.Write((byte)(item.Layer + 1));
|
|
||||||
Stream.Write(item.Serial);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (beheld.Hair?.ItemID > 0)
|
|
||||||
{
|
|
||||||
Stream.Write((byte)(Layer.Hair + 1));
|
|
||||||
Stream.Write(HairInfo.FakeSerial(beheld.Owner.Serial) - 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (beheld.FacialHair?.ItemID > 0)
|
|
||||||
{
|
|
||||||
Stream.Write((byte)(Layer.FacialHair + 1));
|
|
||||||
Stream.Write(FacialHairInfo.FakeSerial(beheld.Owner.Serial) - 2);
|
|
||||||
}
|
|
||||||
|
|
||||||
Stream.Write((byte)Layer.Invalid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (facialHair != null)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnsureCapacity(8 + count * 5);
|
||||||
|
|
||||||
|
Stream.Write(beheld.Serial);
|
||||||
|
|
||||||
|
for (var i = 0; i < list.Count; ++i)
|
||||||
|
{
|
||||||
|
var item = list[i];
|
||||||
|
|
||||||
|
if (!item.Deleted && beholder.CanSee(item) && item.Parent == beheld)
|
||||||
|
{
|
||||||
|
Stream.Write((byte)(item.Layer + 1));
|
||||||
|
Stream.Write(item.Serial);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hair?.ItemId > 0)
|
||||||
|
{
|
||||||
|
Stream.Write((byte)(Layer.Hair + 1));
|
||||||
|
Stream.Write(hair.VirtualSerial);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (facialHair?.ItemId > 0)
|
||||||
|
{
|
||||||
|
Stream.Write((byte)(Layer.FacialHair + 1));
|
||||||
|
Stream.Write(facialHair.VirtualSerial);
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream.Write((byte)Layer.Invalid);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class CorpseContent : Packet
|
public sealed class CorpseContent : Packet
|
||||||
|
|
@ -56,77 +59,79 @@ public sealed class CorpseContent : Packet
|
||||||
public CorpseContent(Mobile beholder, Corpse beheld)
|
public CorpseContent(Mobile beholder, Corpse beheld)
|
||||||
: base(0x3C)
|
: base(0x3C)
|
||||||
{
|
{
|
||||||
var items = beheld.EquipItems;
|
var items = beheld.EquipItems;
|
||||||
var count = items.Count;
|
var count = items.Count;
|
||||||
|
var hair = beheld.Hair;
|
||||||
|
var facialHair = beheld.FacialHair;
|
||||||
|
|
||||||
if (beheld.Hair?.ItemID > 0)
|
if (hair != null)
|
||||||
{
|
{
|
||||||
count++;
|
count++;
|
||||||
}
|
|
||||||
|
|
||||||
if (beheld.FacialHair?.ItemID > 0)
|
|
||||||
{
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
EnsureCapacity(5 + count * 19);
|
|
||||||
|
|
||||||
var pos = Stream.Position;
|
|
||||||
|
|
||||||
var written = 0;
|
|
||||||
|
|
||||||
Stream.Write((ushort)0);
|
|
||||||
|
|
||||||
for (var i = 0; i < items.Count; ++i)
|
|
||||||
{
|
|
||||||
var child = items[i];
|
|
||||||
|
|
||||||
if (!child.Deleted && child.Parent == beheld && beholder.CanSee(child))
|
|
||||||
{
|
|
||||||
Stream.Write(child.Serial);
|
|
||||||
Stream.Write((ushort)child.ItemID);
|
|
||||||
Stream.Write((byte)0); // signed, itemID offset
|
|
||||||
Stream.Write((ushort)child.Amount);
|
|
||||||
Stream.Write((short)child.X);
|
|
||||||
Stream.Write((short)child.Y);
|
|
||||||
Stream.Write(beheld.Serial);
|
|
||||||
Stream.Write((ushort)child.Hue);
|
|
||||||
|
|
||||||
++written;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (beheld.Hair?.ItemID > 0)
|
|
||||||
{
|
|
||||||
Stream.Write(HairInfo.FakeSerial(beheld.Owner.Serial) - 2);
|
|
||||||
Stream.Write((ushort)beheld.Hair.ItemID);
|
|
||||||
Stream.Write((byte)0); // signed, itemID offset
|
|
||||||
Stream.Write((ushort)1);
|
|
||||||
Stream.Write((short)0);
|
|
||||||
Stream.Write((short)0);
|
|
||||||
Stream.Write(beheld.Serial);
|
|
||||||
Stream.Write((ushort)beheld.Hair.Hue);
|
|
||||||
|
|
||||||
++written;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (beheld.FacialHair?.ItemID > 0)
|
|
||||||
{
|
|
||||||
Stream.Write(FacialHairInfo.FakeSerial(beheld.Owner.Serial) - 2);
|
|
||||||
Stream.Write((ushort)beheld.FacialHair.ItemID);
|
|
||||||
Stream.Write((byte)0); // signed, itemID offset
|
|
||||||
Stream.Write((ushort)1);
|
|
||||||
Stream.Write((short)0);
|
|
||||||
Stream.Write((short)0);
|
|
||||||
Stream.Write(beheld.Serial);
|
|
||||||
Stream.Write((ushort)beheld.FacialHair.Hue);
|
|
||||||
|
|
||||||
++written;
|
|
||||||
}
|
|
||||||
|
|
||||||
Stream.Seek(pos, SeekOrigin.Begin);
|
|
||||||
Stream.Write((ushort)written);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (facialHair != null)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnsureCapacity(5 + count * 19);
|
||||||
|
|
||||||
|
var pos = Stream.Position;
|
||||||
|
|
||||||
|
var written = 0;
|
||||||
|
|
||||||
|
Stream.Write((ushort)0);
|
||||||
|
|
||||||
|
for (var i = 0; i < items.Count; ++i)
|
||||||
|
{
|
||||||
|
var child = items[i];
|
||||||
|
|
||||||
|
if (!child.Deleted && child.Parent == beheld && beholder.CanSee(child))
|
||||||
|
{
|
||||||
|
Stream.Write(child.Serial);
|
||||||
|
Stream.Write((ushort)child.ItemID);
|
||||||
|
Stream.Write((byte)0); // signed, itemID offset
|
||||||
|
Stream.Write((ushort)child.Amount);
|
||||||
|
Stream.Write((short)child.X);
|
||||||
|
Stream.Write((short)child.Y);
|
||||||
|
Stream.Write(beheld.Serial);
|
||||||
|
Stream.Write((ushort)child.Hue);
|
||||||
|
|
||||||
|
++written;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hair?.ItemId > 0)
|
||||||
|
{
|
||||||
|
Stream.Write(hair.VirtualSerial);
|
||||||
|
Stream.Write((ushort)hair.ItemId);
|
||||||
|
Stream.Write((byte)0); // signed, itemID offset
|
||||||
|
Stream.Write((ushort)1);
|
||||||
|
Stream.Write((short)0);
|
||||||
|
Stream.Write((short)0);
|
||||||
|
Stream.Write(beheld.Serial);
|
||||||
|
Stream.Write((ushort)hair.Hue);
|
||||||
|
|
||||||
|
++written;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (facialHair?.ItemId > 0)
|
||||||
|
{
|
||||||
|
Stream.Write(facialHair.VirtualSerial);
|
||||||
|
Stream.Write((ushort)facialHair.ItemId);
|
||||||
|
Stream.Write((byte)0); // signed, itemID offset
|
||||||
|
Stream.Write((ushort)1);
|
||||||
|
Stream.Write((short)0);
|
||||||
|
Stream.Write((short)0);
|
||||||
|
Stream.Write(beheld.Serial);
|
||||||
|
Stream.Write((ushort)facialHair.Hue);
|
||||||
|
|
||||||
|
++written;
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream.Seek(pos, SeekOrigin.Begin);
|
||||||
|
Stream.Write((ushort)written);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed class CorpseContent6017 : Packet
|
public sealed class CorpseContent6017 : Packet
|
||||||
|
|
@ -134,78 +139,80 @@ public sealed class CorpseContent6017 : Packet
|
||||||
public CorpseContent6017(Mobile beholder, Corpse beheld)
|
public CorpseContent6017(Mobile beholder, Corpse beheld)
|
||||||
: base(0x3C)
|
: base(0x3C)
|
||||||
{
|
{
|
||||||
var items = beheld.EquipItems;
|
var items = beheld.EquipItems;
|
||||||
var count = items.Count;
|
var count = items.Count;
|
||||||
|
var hair = beheld.Hair;
|
||||||
|
var facialHair = beheld.FacialHair;
|
||||||
|
|
||||||
if (beheld.Hair?.ItemID > 0)
|
if (hair != null)
|
||||||
{
|
{
|
||||||
count++;
|
count++;
|
||||||
}
|
|
||||||
|
|
||||||
if (beheld.FacialHair?.ItemID > 0)
|
|
||||||
{
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
EnsureCapacity(5 + count * 20);
|
|
||||||
|
|
||||||
var pos = Stream.Position;
|
|
||||||
|
|
||||||
var written = 0;
|
|
||||||
|
|
||||||
Stream.Write((ushort)0);
|
|
||||||
|
|
||||||
for (var i = 0; i < items.Count; ++i)
|
|
||||||
{
|
|
||||||
var child = items[i];
|
|
||||||
|
|
||||||
if (!child.Deleted && child.Parent == beheld && beholder.CanSee(child))
|
|
||||||
{
|
|
||||||
Stream.Write(child.Serial);
|
|
||||||
Stream.Write((ushort)child.ItemID);
|
|
||||||
Stream.Write((byte)0); // signed, itemID offset
|
|
||||||
Stream.Write((ushort)child.Amount);
|
|
||||||
Stream.Write((short)child.X);
|
|
||||||
Stream.Write((short)child.Y);
|
|
||||||
Stream.Write((byte)0); // Grid Location?
|
|
||||||
Stream.Write(beheld.Serial);
|
|
||||||
Stream.Write((ushort)child.Hue);
|
|
||||||
|
|
||||||
++written;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (beheld.Hair?.ItemID > 0)
|
|
||||||
{
|
|
||||||
Stream.Write(HairInfo.FakeSerial(beheld.Owner.Serial) - 2);
|
|
||||||
Stream.Write((ushort)beheld.Hair.ItemID);
|
|
||||||
Stream.Write((byte)0); // signed, itemID offset
|
|
||||||
Stream.Write((ushort)1);
|
|
||||||
Stream.Write((short)0);
|
|
||||||
Stream.Write((short)0);
|
|
||||||
Stream.Write((byte)0); // Grid Location?
|
|
||||||
Stream.Write(beheld.Serial);
|
|
||||||
Stream.Write((ushort)beheld.Hair.Hue);
|
|
||||||
|
|
||||||
++written;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (beheld.FacialHair?.ItemID > 0)
|
|
||||||
{
|
|
||||||
Stream.Write(FacialHairInfo.FakeSerial(beheld.Owner.Serial) - 2);
|
|
||||||
Stream.Write((ushort)beheld.FacialHair.ItemID);
|
|
||||||
Stream.Write((byte)0); // signed, itemID offset
|
|
||||||
Stream.Write((ushort)1);
|
|
||||||
Stream.Write((short)0);
|
|
||||||
Stream.Write((short)0);
|
|
||||||
Stream.Write((byte)0); // Grid Location?
|
|
||||||
Stream.Write(beheld.Serial);
|
|
||||||
Stream.Write((ushort)beheld.FacialHair.Hue);
|
|
||||||
|
|
||||||
++written;
|
|
||||||
}
|
|
||||||
|
|
||||||
Stream.Seek(pos, SeekOrigin.Begin);
|
|
||||||
Stream.Write((ushort)written);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
if (facialHair != null)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnsureCapacity(5 + count * 20);
|
||||||
|
|
||||||
|
var pos = Stream.Position;
|
||||||
|
|
||||||
|
var written = 0;
|
||||||
|
|
||||||
|
Stream.Write((ushort)0);
|
||||||
|
|
||||||
|
for (var i = 0; i < items.Count; ++i)
|
||||||
|
{
|
||||||
|
var child = items[i];
|
||||||
|
|
||||||
|
if (!child.Deleted && child.Parent == beheld && beholder.CanSee(child))
|
||||||
|
{
|
||||||
|
Stream.Write(child.Serial);
|
||||||
|
Stream.Write((ushort)child.ItemID);
|
||||||
|
Stream.Write((byte)0); // signed, itemID offset
|
||||||
|
Stream.Write((ushort)child.Amount);
|
||||||
|
Stream.Write((short)child.X);
|
||||||
|
Stream.Write((short)child.Y);
|
||||||
|
Stream.Write((byte)0); // Grid Location?
|
||||||
|
Stream.Write(beheld.Serial);
|
||||||
|
Stream.Write((ushort)child.Hue);
|
||||||
|
|
||||||
|
++written;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hair?.ItemId > 0)
|
||||||
|
{
|
||||||
|
Stream.Write(hair.VirtualSerial);
|
||||||
|
Stream.Write((ushort)hair.ItemId);
|
||||||
|
Stream.Write((byte)0); // signed, itemID offset
|
||||||
|
Stream.Write((ushort)1);
|
||||||
|
Stream.Write((short)0);
|
||||||
|
Stream.Write((short)0);
|
||||||
|
Stream.Write((byte)0); // Grid Location?
|
||||||
|
Stream.Write(beheld.Serial);
|
||||||
|
Stream.Write((ushort)hair.Hue);
|
||||||
|
|
||||||
|
++written;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (facialHair?.ItemId > 0)
|
||||||
|
{
|
||||||
|
Stream.Write(facialHair.VirtualSerial);
|
||||||
|
Stream.Write((ushort)facialHair.ItemId);
|
||||||
|
Stream.Write((byte)0); // signed, itemID offset
|
||||||
|
Stream.Write((ushort)1);
|
||||||
|
Stream.Write((short)0);
|
||||||
|
Stream.Write((short)0);
|
||||||
|
Stream.Write((byte)0); // Grid Location?
|
||||||
|
Stream.Write(beheld.Serial);
|
||||||
|
Stream.Write((ushort)facialHair.Hue);
|
||||||
|
|
||||||
|
++written;
|
||||||
|
}
|
||||||
|
|
||||||
|
Stream.Seek(pos, SeekOrigin.Begin);
|
||||||
|
Stream.Write((ushort)written);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -51,17 +51,17 @@ public partial class SchmendrickApprenticeCorpse : Corpse
|
||||||
new Spellbook()
|
new Spellbook()
|
||||||
];
|
];
|
||||||
|
|
||||||
private static HairInfo GetHair()
|
private static VirtualHairInfo GetHair()
|
||||||
{
|
{
|
||||||
_hairHue = Race.Human.RandomHairHue();
|
_hairHue = Race.Human.RandomHairHue();
|
||||||
return new HairInfo(Race.Human.RandomHair(false), _hairHue);
|
return new VirtualHairInfo(Race.Human.RandomHair(false), _hairHue);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static FacialHairInfo GetFacialHair()
|
private static VirtualHairInfo GetFacialHair()
|
||||||
{
|
{
|
||||||
_hairHue = Race.Human.RandomHairHue();
|
_hairHue = Race.Human.RandomHairHue();
|
||||||
|
|
||||||
return new FacialHairInfo(Race.Human.RandomFacialHair(false), _hairHue);
|
return new VirtualHairInfo(Race.Human.RandomFacialHair(false), _hairHue);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void AddNameProperty(IPropertyList list)
|
public override void AddNameProperty(IPropertyList list)
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,7 @@ public partial class MilitiaFighter : BaseCreature
|
||||||
[SerializationGenerator(0, false)]
|
[SerializationGenerator(0, false)]
|
||||||
public partial class MilitiaFighterCorpse : Corpse
|
public partial class MilitiaFighterCorpse : Corpse
|
||||||
{
|
{
|
||||||
public MilitiaFighterCorpse(Mobile owner, HairInfo hair, FacialHairInfo facialhair, List<Item> equipItems) : base(
|
public MilitiaFighterCorpse(Mobile owner, VirtualHairInfo hair, VirtualHairInfo facialhair, List<Item> equipItems) : base(
|
||||||
owner,
|
owner,
|
||||||
hair,
|
hair,
|
||||||
facialhair,
|
facialhair,
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ public enum CorpseFlag
|
||||||
SelfLooted = 0x00000080
|
SelfLooted = 0x00000080
|
||||||
}
|
}
|
||||||
|
|
||||||
[SerializationGenerator(13, false)]
|
[SerializationGenerator(14, false)]
|
||||||
public partial class Corpse : Container, ICarvable
|
public partial class Corpse : Container, ICarvable
|
||||||
{
|
{
|
||||||
public static readonly TimeSpan MonsterLootRightSacrifice = TimeSpan.FromMinutes(2.0);
|
public static readonly TimeSpan MonsterLootRightSacrifice = TimeSpan.FromMinutes(2.0);
|
||||||
|
|
@ -132,6 +132,16 @@ public partial class Corpse : Container, ICarvable
|
||||||
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
private List<Item> _equipItems;
|
private List<Item> _equipItems;
|
||||||
|
|
||||||
|
[CanBeNull]
|
||||||
|
[SerializableField(14, setter: "private")]
|
||||||
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
|
private VirtualHairInfo _hair;
|
||||||
|
|
||||||
|
[CanBeNull]
|
||||||
|
[SerializableField(15, setter: "private")]
|
||||||
|
[SerializedCommandProperty(AccessLevel.GameMaster)]
|
||||||
|
private VirtualHairInfo _facialHair;
|
||||||
|
|
||||||
// Why was this public?
|
// Why was this public?
|
||||||
// public override bool IsPublicContainer => true;
|
// public override bool IsPublicContainer => true;
|
||||||
|
|
||||||
|
|
@ -139,7 +149,7 @@ public partial class Corpse : Container, ICarvable
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
public Corpse(Mobile owner, HairInfo hair, FacialHairInfo facialhair, List<Item> equipItems)
|
public Corpse(Mobile owner, VirtualHairInfo hair, VirtualHairInfo facialHair, List<Item> equipItems)
|
||||||
: base(0x2006)
|
: base(0x2006)
|
||||||
{
|
{
|
||||||
// To suppress console warnings, stackable must be true
|
// To suppress console warnings, stackable must be true
|
||||||
|
|
@ -163,8 +173,15 @@ public partial class Corpse : Container, ICarvable
|
||||||
_kills = owner.Kills;
|
_kills = owner.Kills;
|
||||||
SetFlag(CorpseFlag.Criminal, owner.Criminal);
|
SetFlag(CorpseFlag.Criminal, owner.Criminal);
|
||||||
|
|
||||||
Hair = hair;
|
if (hair?.ItemId > 0)
|
||||||
FacialHair = facialhair;
|
{
|
||||||
|
_hair = new VirtualHairInfo(hair.ItemId, hair.Hue);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (facialHair?.ItemId > 0)
|
||||||
|
{
|
||||||
|
_facialHair = new VirtualHairInfo(facialHair.ItemId, facialHair.Hue);
|
||||||
|
}
|
||||||
|
|
||||||
// This corpse does not turn to bones if: the owner is not a player
|
// This corpse does not turn to bones if: the owner is not a player
|
||||||
SetFlag(CorpseFlag.NoBones, !owner.Player);
|
SetFlag(CorpseFlag.NoBones, !owner.Player);
|
||||||
|
|
@ -237,6 +254,26 @@ public partial class Corpse : Container, ICarvable
|
||||||
DevourCorpse();
|
DevourCorpse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Added corpse hair and corpse facial hair
|
||||||
|
private void MigrateFrom(V13Content content)
|
||||||
|
{
|
||||||
|
_restoreEquip = content.RestoreEquip;
|
||||||
|
_flags = content.Flags;
|
||||||
|
_timeOfDeath = content.TimeOfDeath;
|
||||||
|
_restoreTable = content.RestoreTable;
|
||||||
|
_decayTimer = new InternalTimer(this, content.DecayTimerDelay);
|
||||||
|
_decayTimer.Start();
|
||||||
|
_looters = content.Looters;
|
||||||
|
_killer = content.Killer;
|
||||||
|
_aggressors = content.Aggressors;
|
||||||
|
_owner = content.Owner;
|
||||||
|
_corpseName = content.CorpseName;
|
||||||
|
_accessLevel = content.AccessLevel;
|
||||||
|
_guild = content.Guild;
|
||||||
|
_kills = content.Kills;
|
||||||
|
_equipItems = content.EquipItems;
|
||||||
|
}
|
||||||
|
|
||||||
[CommandProperty(AccessLevel.GameMaster)]
|
[CommandProperty(AccessLevel.GameMaster)]
|
||||||
public virtual bool InstancedCorpse => Core.SE && Core.Now < TimeOfDeath + InstancedCorpseTime;
|
public virtual bool InstancedCorpse => Core.SE && Core.Now < TimeOfDeath + InstancedCorpseTime;
|
||||||
|
|
||||||
|
|
@ -248,10 +285,6 @@ public partial class Corpse : Container, ICarvable
|
||||||
[CommandProperty(AccessLevel.GameMaster)]
|
[CommandProperty(AccessLevel.GameMaster)]
|
||||||
public string Forensicist { get; set; }
|
public string Forensicist { get; set; }
|
||||||
|
|
||||||
public HairInfo Hair { get; }
|
|
||||||
|
|
||||||
public FacialHairInfo FacialHair { get; }
|
|
||||||
|
|
||||||
[CommandProperty(AccessLevel.GameMaster)]
|
[CommandProperty(AccessLevel.GameMaster)]
|
||||||
public bool IsBones => GetFlag(CorpseFlag.IsBones);
|
public bool IsBones => GetFlag(CorpseFlag.IsBones);
|
||||||
|
|
||||||
|
|
@ -492,14 +525,11 @@ public partial class Corpse : Container, ICarvable
|
||||||
Mobile.CreateCorpseHandler += Mobile_CreateCorpseHandler;
|
Mobile.CreateCorpseHandler += Mobile_CreateCorpseHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Container Mobile_CreateCorpseHandler(
|
public static Container Mobile_CreateCorpseHandler(Mobile owner, List<Item> initialContent, List<Item> equipItems)
|
||||||
Mobile owner, HairInfo hair, FacialHairInfo facialhair,
|
|
||||||
List<Item> initialContent, List<Item> equipItems
|
|
||||||
)
|
|
||||||
{
|
{
|
||||||
var c = owner is MilitiaFighter
|
var c = owner is MilitiaFighter
|
||||||
? new MilitiaFighterCorpse(owner, hair, facialhair, equipItems)
|
? new MilitiaFighterCorpse(owner, owner.Hair, owner.FacialHair, equipItems)
|
||||||
: new Corpse(owner, hair, facialhair, equipItems);
|
: new Corpse(owner, owner.Hair, owner.FacialHair, equipItems);
|
||||||
|
|
||||||
owner.Corpse = c;
|
owner.Corpse = c;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -49,16 +49,16 @@ public static class CorpsePackets
|
||||||
|
|
||||||
if (beheld.Owner != null)
|
if (beheld.Owner != null)
|
||||||
{
|
{
|
||||||
if (beheld.Hair?.ItemID > 0)
|
if (beheld.Hair?.ItemId > 0)
|
||||||
{
|
{
|
||||||
writer.Write((byte)(Layer.Hair + 1));
|
writer.Write((byte)(Layer.Hair + 1));
|
||||||
writer.Write(HairInfo.FakeSerial(beheld.Owner.Serial) - 2);
|
writer.Write(beheld.Hair.VirtualSerial);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (beheld.FacialHair?.ItemID > 0)
|
if (beheld.FacialHair?.ItemId > 0)
|
||||||
{
|
{
|
||||||
writer.Write((byte)(Layer.FacialHair + 1));
|
writer.Write((byte)(Layer.FacialHair + 1));
|
||||||
writer.Write(FacialHairInfo.FakeSerial(beheld.Owner.Serial) - 2);
|
writer.Write(beheld.FacialHair.VirtualSerial);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -76,15 +76,16 @@ public static class CorpsePackets
|
||||||
}
|
}
|
||||||
|
|
||||||
var list = beheld.EquipItems;
|
var list = beheld.EquipItems;
|
||||||
var hairItemID = beheld.Hair?.ItemID ?? 0;
|
var hair = beheld.Hair;
|
||||||
var facialHairItemID = beheld.FacialHair?.ItemID ?? 0;
|
var facialHair = beheld.FacialHair;
|
||||||
|
|
||||||
var count = list.Count;
|
var count = list.Count;
|
||||||
if (hairItemID > 0)
|
if (hair != null)
|
||||||
{
|
{
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (facialHairItemID > 0)
|
if (facialHair != null)
|
||||||
{
|
{
|
||||||
count++;
|
count++;
|
||||||
}
|
}
|
||||||
|
|
@ -120,10 +121,10 @@ public static class CorpsePackets
|
||||||
|
|
||||||
if (beheld.Owner != null)
|
if (beheld.Owner != null)
|
||||||
{
|
{
|
||||||
if (hairItemID > 0)
|
if (hair?.ItemId > 0)
|
||||||
{
|
{
|
||||||
writer.Write(HairInfo.FakeSerial(beheld.Owner.Serial) - 2);
|
writer.Write(hair.VirtualSerial);
|
||||||
writer.Write((ushort)hairItemID);
|
writer.Write((ushort)hair.ItemId);
|
||||||
writer.Write((byte)0); // signed, itemID offset
|
writer.Write((byte)0); // signed, itemID offset
|
||||||
writer.Write((ushort)1);
|
writer.Write((ushort)1);
|
||||||
writer.Write(0); // X/Y
|
writer.Write(0); // X/Y
|
||||||
|
|
@ -132,15 +133,15 @@ public static class CorpsePackets
|
||||||
writer.Write((byte)0); // Grid Location?
|
writer.Write((byte)0); // Grid Location?
|
||||||
}
|
}
|
||||||
writer.Write(beheld.Serial);
|
writer.Write(beheld.Serial);
|
||||||
writer.Write((ushort)beheld.Hair!.Hue);
|
writer.Write((ushort)hair.Hue);
|
||||||
|
|
||||||
++written;
|
++written;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (facialHairItemID > 0)
|
if (facialHair?.ItemId > 0)
|
||||||
{
|
{
|
||||||
writer.Write(FacialHairInfo.FakeSerial(beheld.Owner.Serial) - 2);
|
writer.Write(facialHair.VirtualSerial);
|
||||||
writer.Write((ushort)facialHairItemID);
|
writer.Write((ushort)facialHair.ItemId);
|
||||||
writer.Write((byte)0); // signed, itemID offset
|
writer.Write((byte)0); // signed, itemID offset
|
||||||
writer.Write((ushort)1);
|
writer.Write((ushort)1);
|
||||||
writer.Write(0); // X/Y
|
writer.Write(0); // X/Y
|
||||||
|
|
@ -149,7 +150,7 @@ public static class CorpsePackets
|
||||||
writer.Write((byte)0); // Grid Location?
|
writer.Write((byte)0); // Grid Location?
|
||||||
}
|
}
|
||||||
writer.Write(beheld.Serial);
|
writer.Write(beheld.Serial);
|
||||||
writer.Write((ushort)beheld.FacialHair!.Hue);
|
writer.Write((ushort)facialHair.Hue);
|
||||||
|
|
||||||
++written;
|
++written;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
131
Projects/UOContent/Migrations/Server.Items.Corpse.v14.json
generated
Normal file
131
Projects/UOContent/Migrations/Server.Items.Corpse.v14.json
generated
Normal file
|
|
@ -0,0 +1,131 @@
|
||||||
|
{
|
||||||
|
"version": 14,
|
||||||
|
"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"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Hair",
|
||||||
|
"type": "Server.VirtualHairInfo",
|
||||||
|
"rule": "RawSerializableMigrationRule",
|
||||||
|
"ruleArguments": [
|
||||||
|
"",
|
||||||
|
"@CanBeNull"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "FacialHair",
|
||||||
|
"type": "Server.VirtualHairInfo",
|
||||||
|
"rule": "RawSerializableMigrationRule",
|
||||||
|
"ruleArguments": [
|
||||||
|
"",
|
||||||
|
"@CanBeNull"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue