mirror of
https://github.com/modernuo/ModernUO
synced 2026-08-11 22:23:06 -04:00
### Summary - Fixes issues with non-_ISerializable_ objects having bad serialization (Skill/Stat. Mods) - Fixes issues with MarkDirty and namespaces: https://github.com/modernuo/SerializationGenerator/issues/29 - Fixes missing dirty tracking for some data structures. - Fixes cascading MarkDirty for non-serializable types that have owners that are also non-serializable types. ### New Codegenned API When a data structure (Array, List, Dictionary, HashSet, etc) is source generated for serialization, new methods are added which will handle dirty tracking. Use these instead of the built in methods for that data structure. _All Data Structures_ ```cs public void Clear<PropertyName>(); ``` _Lists and Sets_ ```cs public void AddTo<PropertyName>(V value); public void RemoveFrom<PropertyName>(V value); ``` _Lists_ ```cs public void InsertInto<PropertyName>(int index, V value); public void RemoveFrom<PropertyName>At(int index); ``` _Dictionaries_ ```cs public void AddTo<PropertyName>(T key, V value); public void RemoveFrom<PropertyName>(T key); public void ReplaceIn<PropertyName>(T key, V value); ``` Over time, they will be cleaned up and more variants added such as `bool RemoveFrom<PropertyName>(T key, out V value)`
353 lines
9.8 KiB
C#
353 lines
9.8 KiB
C#
using System.Collections.Generic;
|
|
using ModernUO.Serialization;
|
|
using Server.Multis;
|
|
|
|
namespace Server.Items
|
|
{
|
|
[SerializationGenerator(2, false)]
|
|
public abstract partial class BaseAddonContainer : BaseContainer, IChoppable, IAddon
|
|
{
|
|
[SerializableField(0, setter: "private")]
|
|
private List<AddonContainerComponent> _components;
|
|
|
|
public BaseAddonContainer(int itemID) : base(itemID)
|
|
{
|
|
AddonComponent.ApplyLightTo(this);
|
|
|
|
_components = new List<AddonContainerComponent>();
|
|
}
|
|
|
|
public override bool DisplayWeight => false;
|
|
|
|
[Hue]
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public override int Hue
|
|
{
|
|
get => base.Hue;
|
|
set
|
|
{
|
|
if (base.Hue != value)
|
|
{
|
|
base.Hue = value;
|
|
|
|
if (!Deleted && ShareHue && Components != null)
|
|
{
|
|
foreach (var c in Components)
|
|
{
|
|
c.Hue = value;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
[SerializableProperty(1)]
|
|
[CommandProperty(AccessLevel.GameMaster)]
|
|
public CraftResource Resource
|
|
{
|
|
get => _resource;
|
|
set
|
|
{
|
|
if (_resource != value)
|
|
{
|
|
_resource = value;
|
|
Hue = CraftResources.GetHue(_resource);
|
|
|
|
InvalidateProperties();
|
|
this.MarkDirty();
|
|
}
|
|
}
|
|
}
|
|
|
|
public virtual bool RetainDeedHue => false;
|
|
public virtual bool NeedsWall => false;
|
|
public virtual bool ShareHue => true;
|
|
public virtual Point3D WallPosition => Point3D.Zero;
|
|
public virtual BaseAddonContainerDeed Deed => null;
|
|
|
|
Item IAddon.Deed => Deed;
|
|
|
|
public bool CouldFit(IPoint3D p, Map map)
|
|
{
|
|
BaseHouse house = null;
|
|
|
|
return CouldFit(p, map, null, ref house) == AddonFitResult.Valid;
|
|
}
|
|
|
|
public virtual void OnChop(Mobile from)
|
|
{
|
|
var house = BaseHouse.FindHouseAt(this);
|
|
|
|
if (house?.IsOwner(from) == true)
|
|
{
|
|
if (!IsSecure)
|
|
{
|
|
Effects.PlaySound(GetWorldLocation(), Map, 0x3B3);
|
|
from.SendLocalizedMessage(500461); // You destroy the item.
|
|
|
|
var hue = 0;
|
|
|
|
if (RetainDeedHue)
|
|
{
|
|
for (var i = 0; hue == 0 && i < Components.Count; ++i)
|
|
{
|
|
var c = Components[i];
|
|
|
|
if (c.Hue != 0)
|
|
{
|
|
hue = c.Hue;
|
|
}
|
|
}
|
|
}
|
|
|
|
DropItemsToGround();
|
|
|
|
Delete();
|
|
|
|
house.Addons.Remove(this);
|
|
|
|
var deed = Deed;
|
|
|
|
if (deed != null)
|
|
{
|
|
deed.Resource = Resource;
|
|
|
|
if (RetainDeedHue)
|
|
{
|
|
deed.Hue = hue;
|
|
}
|
|
|
|
from.AddToBackpack(deed);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
from.SendLocalizedMessage(1074870); // This item must be unlocked/unsecured before re-deeding it.
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void OnLocationChange(Point3D oldLoc)
|
|
{
|
|
base.OnLocationChange(oldLoc);
|
|
|
|
if (Deleted)
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach (var c in Components)
|
|
{
|
|
c.Location = new Point3D(X + c.Offset.X, Y + c.Offset.Y, Z + c.Offset.Z);
|
|
}
|
|
}
|
|
|
|
public override void OnMapChange()
|
|
{
|
|
base.OnMapChange();
|
|
|
|
if (Deleted)
|
|
{
|
|
return;
|
|
}
|
|
|
|
foreach (var c in Components)
|
|
{
|
|
c.Map = Map;
|
|
}
|
|
}
|
|
|
|
public override void OnDelete()
|
|
{
|
|
var house = BaseHouse.FindHouseAt(this);
|
|
|
|
house?.Addons.Remove(this);
|
|
|
|
base.OnDelete();
|
|
}
|
|
|
|
public override void GetProperties(IPropertyList list)
|
|
{
|
|
base.GetProperties(list);
|
|
|
|
if (!CraftResources.IsStandard(_resource))
|
|
{
|
|
list.Add(CraftResources.GetLocalizationNumber(_resource));
|
|
}
|
|
}
|
|
|
|
public override void OnAfterDelete()
|
|
{
|
|
base.OnAfterDelete();
|
|
|
|
foreach (var c in Components)
|
|
{
|
|
c.Delete();
|
|
}
|
|
}
|
|
|
|
public override void InvalidateProperties()
|
|
{
|
|
base.InvalidateProperties();
|
|
|
|
if (_components != null)
|
|
{
|
|
foreach (var component in _components)
|
|
{
|
|
component.InvalidateProperties();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Handles v0 with old Enum -> Int casting
|
|
private void Deserialize(IGenericReader reader, int version)
|
|
{
|
|
_components = reader.ReadEntityList<AddonContainerComponent>();
|
|
_resource = version == 1 ? reader.ReadEnum<CraftResource>() : (CraftResource)reader.ReadInt();
|
|
}
|
|
|
|
[AfterDeserialization]
|
|
private void AfterDeserialization()
|
|
{
|
|
AddonComponent.ApplyLightTo(this);
|
|
}
|
|
|
|
public virtual void DropItemsToGround()
|
|
{
|
|
for (var i = Items.Count - 1; i >= 0; i--)
|
|
{
|
|
Items[i].MoveToWorld(Location);
|
|
}
|
|
}
|
|
|
|
public void AddComponent(AddonContainerComponent c, int x, int y, int z)
|
|
{
|
|
if (Deleted)
|
|
{
|
|
return;
|
|
}
|
|
|
|
AddToComponents(c);
|
|
|
|
c.Addon = this;
|
|
c.Offset = new Point3D(x, y, z);
|
|
c.MoveToWorld(new Point3D(X + x, Y + y, Z + z), Map);
|
|
}
|
|
|
|
public AddonFitResult CouldFit(IPoint3D p, Map map, Mobile from, ref BaseHouse house)
|
|
{
|
|
if (Deleted)
|
|
{
|
|
return AddonFitResult.Blocked;
|
|
}
|
|
|
|
foreach (var c in Components)
|
|
{
|
|
var p3D = new Point3D(p.X + c.Offset.X, p.Y + c.Offset.Y, p.Z + c.Offset.Z);
|
|
|
|
if (!map.CanFit(p3D.X, p3D.Y, p3D.Z, c.ItemData.Height, false, true, c.Z == 0))
|
|
{
|
|
return AddonFitResult.Blocked;
|
|
}
|
|
|
|
if (!BaseAddon.CheckHouse(from, p3D, map, c.ItemData.Height, out house))
|
|
{
|
|
return AddonFitResult.NotInHouse;
|
|
}
|
|
|
|
if (c.NeedsWall)
|
|
{
|
|
var wall = c.WallPosition;
|
|
|
|
if (!BaseAddon.IsWall(p3D.X + wall.X, p3D.Y + wall.Y, p3D.Z + wall.Z, map))
|
|
{
|
|
return AddonFitResult.NoWall;
|
|
}
|
|
}
|
|
}
|
|
|
|
var p3 = new Point3D(p.X, p.Y, p.Z);
|
|
|
|
if (!map.CanFit(p3.X, p3.Y, p3.Z, ItemData.Height, false, true, Z == 0))
|
|
{
|
|
return AddonFitResult.Blocked;
|
|
}
|
|
|
|
if (!BaseAddon.CheckHouse(from, p3, map, ItemData.Height, out house))
|
|
{
|
|
return AddonFitResult.NotInHouse;
|
|
}
|
|
|
|
if (NeedsWall)
|
|
{
|
|
var wall = WallPosition;
|
|
|
|
if (!BaseAddon.IsWall(p3.X + wall.X, p3.Y + wall.Y, p3.Z + wall.Z, map))
|
|
{
|
|
return AddonFitResult.NoWall;
|
|
}
|
|
}
|
|
|
|
if (house != null)
|
|
{
|
|
var doors = house.Doors;
|
|
|
|
for (var i = 0; i < doors.Count; ++i)
|
|
{
|
|
var door = doors[i];
|
|
|
|
if (door == null)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (door.Open)
|
|
{
|
|
return AddonFitResult.DoorsNotClosed;
|
|
}
|
|
|
|
var doorLoc = door.GetWorldLocation();
|
|
var doorHeight = door.ItemData.CalcHeight;
|
|
int addonHeight;
|
|
|
|
foreach (var c in Components)
|
|
{
|
|
var addonLoc = new Point3D(p.X + c.Offset.X, p.Y + c.Offset.Y, p.Z + c.Offset.Z);
|
|
addonHeight = c.ItemData.CalcHeight;
|
|
|
|
if (Utility.InRange(doorLoc, addonLoc, 1) &&
|
|
(addonLoc.Z == doorLoc.Z ||
|
|
addonLoc.Z + addonHeight > doorLoc.Z && doorLoc.Z + doorHeight > addonLoc.Z))
|
|
{
|
|
return AddonFitResult.DoorTooClose;
|
|
}
|
|
}
|
|
|
|
var addonLo = new Point3D(p.X, p.Y, p.Z);
|
|
addonHeight = ItemData.CalcHeight;
|
|
|
|
if (Utility.InRange(doorLoc, addonLo, 1) &&
|
|
(addonLo.Z == doorLoc.Z ||
|
|
addonLo.Z + addonHeight > doorLoc.Z && doorLoc.Z + doorHeight > addonLo.Z))
|
|
{
|
|
return AddonFitResult.DoorTooClose;
|
|
}
|
|
}
|
|
}
|
|
|
|
return AddonFitResult.Valid;
|
|
}
|
|
|
|
public virtual void OnComponentLoaded(AddonContainerComponent c)
|
|
{
|
|
}
|
|
|
|
public virtual void OnComponentUsed(AddonContainerComponent c, Mobile from)
|
|
{
|
|
if (!Deleted)
|
|
{
|
|
OnDoubleClick(from);
|
|
}
|
|
}
|
|
}
|
|
}
|