ModernUO/Projects/UOContent/Items/Misc/FlippableAddonAttribute.cs
Kamron Batman b7882df136
fix: Fixes serialization and dirty tracking conveniences (#1627)
### 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)`
2023-12-03 15:55:31 -08:00

140 lines
3.7 KiB
C#

using System;
using Server.Multis;
namespace Server.Items;
[AttributeUsage(AttributeTargets.Class)]
public class FlippableAddonAttribute : Attribute
{
private static readonly string m_MethodName = "Flip";
private static readonly Type[] m_Params =
{
typeof(Mobile), typeof(Direction)
};
public FlippableAddonAttribute(params Direction[] directions) => Directions = directions;
public Direction[] Directions { get; }
public virtual void Flip(Mobile from, Item addon)
{
if (!(Directions?.Length > 1))
{
return;
}
try
{
var flipMethod = addon.GetType().GetMethod(m_MethodName, m_Params);
if (flipMethod == null)
{
return;
}
var index = 0;
for (var i = 0; i < Directions.Length; i++)
{
if (addon.Direction == Directions[i])
{
index = i + 1;
break;
}
}
if (index >= Directions.Length)
{
index = 0;
}
ClearComponents(addon);
flipMethod.Invoke(addon, new object[] { from, Directions[index] });
BaseHouse house = null;
var result = AddonFitResult.Valid;
addon.Map = Map.Internal;
if (addon is BaseAddon baseAddon)
{
result = baseAddon.CouldFit(baseAddon.Location, from.Map, from, ref house);
}
else if (addon is BaseAddonContainer container)
{
result = container.CouldFit(container.Location, from.Map, from, ref house);
}
addon.Map = from.Map;
if (result != AddonFitResult.Valid)
{
if (index == 0)
{
index = Directions.Length - 1;
}
else
{
index -= 1;
}
ClearComponents(addon);
flipMethod.Invoke(addon, new object[] { from, Directions[index] });
if (result == AddonFitResult.Blocked)
{
from.SendLocalizedMessage(500269); // You cannot build that there.
}
else if (result == AddonFitResult.NotInHouse)
{
from.SendLocalizedMessage(500274); // You can only place this in a house that you own!
}
else if (result == AddonFitResult.DoorsNotClosed)
{
from.SendMessage("You must close all house doors before placing this.");
}
else if (result == AddonFitResult.DoorTooClose)
{
from.SendLocalizedMessage(500271); // You cannot build near the door.
}
else if (result == AddonFitResult.NoWall)
{
from.SendLocalizedMessage(500268); // This object needs to be mounted on something.
}
}
addon.Direction = Directions[index];
}
catch
{
// ignored
}
}
private void ClearComponents(Item item)
{
if (item is BaseAddon addon)
{
foreach (var c in addon.Components)
{
c.Addon = null;
c.Delete();
}
addon.ClearComponents();
}
else if (item is BaseAddonContainer addonContainer)
{
foreach (var c in addonContainer.Components)
{
c.Addon = null;
c.Delete();
}
addonContainer.ClearComponents();
}
}
}