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:
mdodkins 2024-08-08 04:10:23 +01:00 committed by GitHub
parent 883d873c1c
commit 91e37fb8d4
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 548 additions and 357 deletions

View file

@ -1,6 +1,7 @@
using System;
using System.Buffers;
using System.Runtime.CompilerServices;
using ModernUO.Serialization;
using Server.Network;
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();
switch (version)
{
case 0:
{
ItemID = reader.ReadInt();
Hue = reader.ReadInt();
break;
}
}
_itemId = itemid;
_hue = hue;
VirtualSerial = World.NewVirtual;
}
[CommandProperty(AccessLevel.GameMaster)]
public int ItemID { get; set; }
[CommandProperty(AccessLevel.GameMaster)]
public int Hue { get; set; }
public virtual void Serialize(IGenericWriter writer)
[AfterDeserialization]
private void AfterDeserialization()
{
writer.Write(0); // version
writer.Write(ItemID);
writer.Write(Hue);
VirtualSerial = World.NewVirtual;
}
}
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;
public Serial VirtualSerial { get; private set; }
}