diff --git a/Projects/UOContent.Tests/Tests/Items/Weapons/BanePropertyTests.cs b/Projects/UOContent.Tests/Tests/Items/Weapons/BanePropertyTests.cs index d68c14b92..d0bf02574 100644 --- a/Projects/UOContent.Tests/Tests/Items/Weapons/BanePropertyTests.cs +++ b/Projects/UOContent.Tests/Tests/Items/Weapons/BanePropertyTests.cs @@ -14,19 +14,19 @@ public class BanePropertyTests private const int BaneCliloc = 1154671; [Fact] - public void WeaponAttributes_StoresAndDupesBane() + public void ExtendedWeaponAttributes_StoresAndDupesBane() { var weapon = new TestKatana(); var dupe = new TestKatana(); try { - weapon.WeaponAttributes.Bane = 1; + weapon.ExtendedWeaponAttributes.Bane = 1; weapon.Dupe(dupe); - Assert.Equal(1, weapon.WeaponAttributes.Bane); - Assert.Equal(1, dupe.WeaponAttributes.Bane); + Assert.Equal(1, weapon.ExtendedWeaponAttributes.Bane); + Assert.Equal(1, dupe.ExtendedWeaponAttributes.Bane); } finally { @@ -36,23 +36,23 @@ public class BanePropertyTests } [Fact] - public void WeaponAttributes_GetProperties_GatesBaneTooltipToHighSeas() + public void ExtendedWeaponAttributes_GetProperties_GatesBaneTooltipToHighSeas() { var previousExpansion = Core.Expansion; var weapon = new TestKatana(); try { - weapon.WeaponAttributes.Bane = 1; + weapon.ExtendedWeaponAttributes.Bane = 1; Core.Expansion = Expansion.ML; var preHighSeas = new RecordingPropertyList(); - weapon.WeaponAttributes.GetProperties(preHighSeas); + weapon.ExtendedWeaponAttributes.GetProperties(preHighSeas); Assert.DoesNotContain(preHighSeas.Numbers, number => number == BaneCliloc); Core.Expansion = Expansion.HS; var highSeas = new RecordingPropertyList(); - weapon.WeaponAttributes.GetProperties(highSeas); + weapon.ExtendedWeaponAttributes.GetProperties(highSeas); Assert.Contains(highSeas.Numbers, number => number == BaneCliloc); } finally @@ -77,7 +77,7 @@ public class BanePropertyTests try { Core.Expansion = Expansion.HS; - weapon.WeaponAttributes.Bane = 1; + weapon.ExtendedWeaponAttributes.Bane = 1; Assert.Equal(expected, weapon.GetBaneDamage(defender)); } @@ -100,7 +100,7 @@ public class BanePropertyTests try { Core.Expansion = Expansion.ML; - weapon.WeaponAttributes.Bane = 1; + weapon.ExtendedWeaponAttributes.Bane = 1; AddResistance(defender, ResistanceType.Physical, 70); var before = defender.Hits; @@ -128,7 +128,7 @@ public class BanePropertyTests try { Core.Expansion = Expansion.HS; - weapon.WeaponAttributes.Bane = 1; + weapon.ExtendedWeaponAttributes.Bane = 1; AddResistance(defender, ResistanceType.Physical, 70); var before = defender.Hits; @@ -156,7 +156,7 @@ public class BanePropertyTests try { Core.Expansion = Expansion.HS; - weapon.WeaponAttributes.Bane = 1; + weapon.ExtendedWeaponAttributes.Bane = 1; AddResistance(defender, ResistanceType.Physical, 70); var before = defender.Hits; @@ -184,7 +184,7 @@ public class BanePropertyTests try { Core.Expansion = Expansion.HS; - weapon.WeaponAttributes.Bane = 1; + weapon.ExtendedWeaponAttributes.Bane = 1; weapon.AosElementDamages.Fire = 100; AddResistance(defender, ResistanceType.Physical, 70); AddResistance(defender, ResistanceType.Fire, 0); @@ -214,7 +214,7 @@ public class BanePropertyTests try { Core.Expansion = Expansion.HS; - weapon.WeaponAttributes.Bane = 1; + weapon.ExtendedWeaponAttributes.Bane = 1; AddResistance(defender, ResistanceType.Physical, 70); var before = defender.Hits; @@ -242,7 +242,7 @@ public class BanePropertyTests try { Core.Expansion = Expansion.HS; - weapon.WeaponAttributes.Bane = 1; + weapon.ExtendedWeaponAttributes.Bane = 1; AddResistance(defender, ResistanceType.Physical, 70); WeaponAbility.SetCurrentAbility(attacker, WeaponAbility.ArmorIgnore); var before = defender.Hits; @@ -272,7 +272,7 @@ public class BanePropertyTests try { Core.Expansion = Expansion.HS; - weapon.WeaponAttributes.Bane = 1; + weapon.ExtendedWeaponAttributes.Bane = 1; AddResistance(defender, ResistanceType.Physical, 70); var before = defender.Hits; @@ -300,7 +300,7 @@ public class BanePropertyTests try { Core.Expansion = Expansion.HS; - weapon.WeaponAttributes.Bane = 1; + weapon.ExtendedWeaponAttributes.Bane = 1; var before = defender.Hits; weapon.OnSwing(attacker, defender); @@ -327,7 +327,7 @@ public class BanePropertyTests try { Core.Expansion = Expansion.HS; - weapon.WeaponAttributes.Bane = 1; + weapon.ExtendedWeaponAttributes.Bane = 1; AddResistance(defender, ResistanceType.Physical, 70); var before = defender.Hits; @@ -357,7 +357,7 @@ public class BanePropertyTests BaseRunicTool.ApplyAttributesTo(weapon, false, 0, 25, 100, 100); - Assert.Equal(0, weapon.WeaponAttributes.Bane); + Assert.Equal(0, weapon.ExtendedWeaponAttributes.Bane); } finally { diff --git a/Projects/UOContent.Tests/Tests/Items/Weapons/BattleLustPropertyTests.cs b/Projects/UOContent.Tests/Tests/Items/Weapons/BattleLustPropertyTests.cs index bd49f3bcb..999eebf0c 100644 --- a/Projects/UOContent.Tests/Tests/Items/Weapons/BattleLustPropertyTests.cs +++ b/Projects/UOContent.Tests/Tests/Items/Weapons/BattleLustPropertyTests.cs @@ -15,19 +15,19 @@ public class BattleLustPropertyTests private static readonly DateTime TestNow = new(2035, 1, 1, 0, 0, 0, DateTimeKind.Utc); [Fact] - public void WeaponAttributes_StoresAndDupesBattleLust() + public void ExtendedWeaponAttributes_StoresAndDupesBattleLust() { var weapon = new TestKatana(); var dupe = new TestKatana(); try { - weapon.WeaponAttributes.BattleLust = 1; + weapon.ExtendedWeaponAttributes.BattleLust = 1; weapon.Dupe(dupe); - Assert.Equal(1, weapon.WeaponAttributes.BattleLust); - Assert.Equal(1, dupe.WeaponAttributes.BattleLust); + Assert.Equal(1, weapon.ExtendedWeaponAttributes.BattleLust); + Assert.Equal(1, dupe.ExtendedWeaponAttributes.BattleLust); } finally { @@ -37,23 +37,23 @@ public class BattleLustPropertyTests } [Fact] - public void WeaponAttributes_GetProperties_GatesBattleLustTooltipToStygianAbyss() + public void ExtendedWeaponAttributes_GetProperties_GatesBattleLustTooltipToStygianAbyss() { var previousExpansion = Core.Expansion; var weapon = new TestKatana(); try { - weapon.WeaponAttributes.BattleLust = 1; + weapon.ExtendedWeaponAttributes.BattleLust = 1; Core.Expansion = Expansion.ML; var preStygianAbyss = new RecordingPropertyList(); - weapon.WeaponAttributes.GetProperties(preStygianAbyss); + weapon.ExtendedWeaponAttributes.GetProperties(preStygianAbyss); Assert.DoesNotContain(preStygianAbyss.Numbers, number => number == BattleLustCliloc); Core.Expansion = Expansion.SA; var stygianAbyss = new RecordingPropertyList(); - weapon.WeaponAttributes.GetProperties(stygianAbyss); + weapon.ExtendedWeaponAttributes.GetProperties(stygianAbyss); Assert.Contains(stygianAbyss.Numbers, number => number == BattleLustCliloc); } finally @@ -277,7 +277,7 @@ public class BattleLustPropertyTests ApplyDamage(disarmedWielder, source, BattleLust.DamageThreshold); Assert.Equal(1, BattleLust.GetPoints(disarmedWielder)); - disarmedWeapon.WeaponAttributes.BattleLust = 0; + disarmedWeapon.ExtendedWeaponAttributes.BattleLust = 0; Assert.Equal(0, BattleLust.GetDamageBonus(disarmedWielder, source)); Assert.Equal(0, BattleLust.GetPoints(disarmedWielder)); } @@ -345,8 +345,8 @@ public class BattleLustPropertyTests ApplyDamage(wielder, source, BattleLust.DamageThreshold); Assert.Equal(1, BattleLust.GetPoints(wielder)); - weapon.WeaponAttributes.BattleLust = 0; - weapon.WeaponAttributes.BattleLust = 1; + weapon.ExtendedWeaponAttributes.BattleLust = 0; + weapon.ExtendedWeaponAttributes.BattleLust = 1; Assert.Equal(0, BattleLust.GetPoints(wielder)); } @@ -488,7 +488,7 @@ public class BattleLustPropertyTests BaseRunicTool.ApplyAttributesTo(weapon, false, 0, 25, 100, 100); - Assert.Equal(0, weapon.WeaponAttributes.BattleLust); + Assert.Equal(0, weapon.ExtendedWeaponAttributes.BattleLust); } finally { @@ -500,7 +500,7 @@ public class BattleLustPropertyTests private static TestKatana EquipBattleLustWeapon(Mobile wielder) { var weapon = new TestKatana(); - weapon.WeaponAttributes.BattleLust = 1; + weapon.ExtendedWeaponAttributes.BattleLust = 1; wielder.AddItem(weapon); return weapon; } diff --git a/Projects/UOContent.Tests/Tests/Items/Weapons/ExtendedWeaponAttributesTests.cs b/Projects/UOContent.Tests/Tests/Items/Weapons/ExtendedWeaponAttributesTests.cs new file mode 100644 index 000000000..829e68995 --- /dev/null +++ b/Projects/UOContent.Tests/Tests/Items/Weapons/ExtendedWeaponAttributesTests.cs @@ -0,0 +1,127 @@ +using System; +using System.Reflection; +using Server; +using Server.Items; +using Server.Mobiles; +using Xunit; + +namespace UOContent.Tests; + +[Collection("Sequential UOContent Tests")] +public class ExtendedWeaponAttributesTests +{ + [Fact] + public void AosWeaponAttribute_DoesNotExposeBaneOrBattleLust() + { + var names = Enum.GetNames(); + + Assert.DoesNotContain("Bane", names); + Assert.DoesNotContain("BattleLust", names); + } + + [Fact] + public void ExtendedWeaponAttribute_UsesFreshLowBits() + { + Assert.Equal(0x00000001, (int)ExtendedWeaponAttribute.Bane); + Assert.Equal(0x00000002, (int)ExtendedWeaponAttribute.BattleLust); + } + + [Fact] + public void NewWeapon_DefaultsExtendedWeaponAttributesEmpty() + { + var weapon = new TestKatana(); + + try + { + Assert.NotNull(weapon.ExtendedWeaponAttributes); + Assert.True(weapon.ExtendedWeaponAttributes.IsEmpty); + Assert.Equal(0, weapon.ExtendedWeaponAttributes.Bane); + Assert.Equal(0, weapon.ExtendedWeaponAttributes.BattleLust); + } + finally + { + weapon.Delete(); + } + } + + [Fact] + public void StormCaller_UsesExtendedBattleLustProperty() + { + var weapon = new StormCaller(); + + try + { + Assert.Equal(1, weapon.ExtendedWeaponAttributes.BattleLust); + } + finally + { + weapon.Delete(); + } + } + + [Fact] + public void ExtendedWeaponAttributes_AreStaffEditableThroughCommandProperties() + { + var containerProperty = typeof(BaseWeapon).GetProperty(nameof(BaseWeapon.ExtendedWeaponAttributes)); + Assert.NotNull(containerProperty); + var containerCommandProperty = containerProperty.GetCustomAttribute(); + Assert.NotNull(containerCommandProperty); + Assert.True(containerCommandProperty.CanModify); + + AssertStaffCommandProperty(nameof(ExtendedWeaponAttributes.Bane)); + AssertStaffCommandProperty(nameof(ExtendedWeaponAttributes.BattleLust)); + } + + [Fact] + public void ExtendedWeaponAttributes_PersistBaneAndBattleLustThroughBaseWeaponSerialization() + { + var previousExpansion = Core.Expansion; + var weapon = new TestKatana(); + var deserialized = new TestKatana(); + + try + { + Core.Expansion = Expansion.HS; + weapon.ExtendedWeaponAttributes.Bane = 1; + weapon.ExtendedWeaponAttributes.BattleLust = 1; + + var writer = new BufferWriter(true); + weapon.Serialize(writer); + var buffer = new byte[writer.Position]; + writer.Buffer.AsSpan(0, (int)writer.Position).CopyTo(buffer); + + var reader = new BufferReader(buffer); + deserialized.Deserialize(reader); + + Assert.Equal(buffer.Length, reader.Position); + Assert.Equal(1, deserialized.ExtendedWeaponAttributes.Bane); + Assert.Equal(1, deserialized.ExtendedWeaponAttributes.BattleLust); + } + finally + { + Core.Expansion = previousExpansion; + weapon.Delete(); + deserialized.Delete(); + } + } + + private static void AssertStaffCommandProperty(string propertyName) + { + var property = typeof(ExtendedWeaponAttributes).GetProperty(propertyName); + Assert.NotNull(property); + + var attribute = property.GetCustomAttribute(); + Assert.NotNull(attribute); + Assert.Equal(AccessLevel.GameMaster, attribute.ReadLevel); + Assert.Equal(AccessLevel.GameMaster, attribute.WriteLevel); + } + + private class TestKatana : Katana + { + public override int ComputeDamage(Mobile attacker, Mobile defender) => 1; + public override int AbsorbDamage(Mobile attacker, Mobile defender, int damage) => damage; + public override void AddBlood(Mobile attacker, Mobile defender, int damage) + { + } + } +} diff --git a/Projects/UOContent/Items/Weapons/BaseWeapon.Migrations.cs b/Projects/UOContent/Items/Weapons/BaseWeapon.Migrations.cs new file mode 100644 index 000000000..af0adbf06 --- /dev/null +++ b/Projects/UOContent/Items/Weapons/BaseWeapon.Migrations.cs @@ -0,0 +1,40 @@ +namespace Server.Items; + +public abstract partial class BaseWeapon +{ + private void MigrateFrom(V10Content content) + { + _damageLevel = content.DamageLevel ?? WeaponDamageLevel.Regular; + _accuracyLevel = content.AccuracyLevel ?? WeaponAccuracyLevel.Regular; + _durabilityLevel = content.DurabilityLevel ?? WeaponDurabilityLevel.Regular; + _quality = content.Quality ?? WeaponQuality.Regular; + _hitPoints = content.HitPoints ?? 0; + _maxHitPoints = content.MaxHitPoints ?? 0; + _slayer = content.Slayer ?? SlayerName.None; + _poison = content.Poison; + _poisonCharges = content.PoisonCharges ?? 0; + _crafter = content.Crafter; + _identified = content.Identified; + _strRequirement = content.StrRequirement ?? -1; + _dexRequirement = content.DexRequirement ?? -1; + _intRequirement = content.IntRequirement ?? -1; + _minDamage = content.MinDamage ?? -1; + _maxDamage = content.MaxDamage ?? -1; + _hitSound = content.HitSound ?? -1; + _missSound = content.MissSound ?? -1; + _speed = content.Speed ?? -1; + _maxRange = content.MaxRange ?? -1; + _skill = content.Skill ?? (SkillName)(-1); + _type = content.Type ?? (WeaponType)(-1); + _animation = content.Animation ?? (WeaponAnimation)(-1); + _resource = content.Resource ?? CraftResource.Iron; + _attributes = content.Attributes ?? AttributesDefaultValue(); + _weaponAttributes = content.WeaponAttributes ?? WeaponAttributesDefaultValue(); + _playerConstructed = content.PlayerConstructed; + _skillBonuses = content.SkillBonuses ?? SkillBonusesDefaultValue(); + _slayer2 = content.Slayer2 ?? SlayerName.None; + _aosElementDamages = content.AosElementDamages ?? AosElementAttributesDefaultValue(); + _engravedText = content.EngravedText; + _extendedWeaponAttributes = ExtendedWeaponAttributesDefaultValue(); + } +} diff --git a/Projects/UOContent/Items/Weapons/BaseWeapon.cs b/Projects/UOContent/Items/Weapons/BaseWeapon.cs index 8087b0a2a..4f8acceb2 100644 --- a/Projects/UOContent/Items/Weapons/BaseWeapon.cs +++ b/Projects/UOContent/Items/Weapons/BaseWeapon.cs @@ -27,7 +27,7 @@ public interface ISlayer SlayerName Slayer2 { get; set; } } -[SerializationGenerator(10, false)] +[SerializationGenerator(11, false)] public abstract partial class BaseWeapon : Item, IWeapon, IFactionItem, ICraftable, ISlayer, IDurability, IAosItem, IIdentifiable { @@ -191,6 +191,15 @@ public abstract partial class BaseWeapon [MethodImpl(MethodImplOptions.AggressiveInlining)] private bool ShouldSerializeEngravedText() => !string.IsNullOrEmpty(_engravedText); + // Field 31 intentionally has no save flag; BaseWeapon's legacy save-flag mask is already at the high bit. + [SerializedIgnoreDupe] + [SerializableField(31, setter: "private")] + [SerializedCommandProperty(AccessLevel.GameMaster, canModify: true)] + private ExtendedWeaponAttributes _extendedWeaponAttributes; + + [SerializableFieldDefault(31)] + private ExtendedWeaponAttributes ExtendedWeaponAttributesDefaultValue() => new(this); + private FactionItem m_FactionState; private SkillMod m_SkillMod, m_MageMod; @@ -218,6 +227,7 @@ public abstract partial class BaseWeapon Attributes = new AosAttributes(this); WeaponAttributes = new AosWeaponAttributes(this); + ExtendedWeaponAttributes = new ExtendedWeaponAttributes(this); SkillBonuses = new AosSkillBonuses(this); AosElementDamages = new AosElementAttributes(this); } @@ -934,6 +944,7 @@ public abstract partial class BaseWeapon weap.Attributes = new AosAttributes(newItem, Attributes); weap.AosElementDamages = new AosElementAttributes(newItem, AosElementDamages); + weap.ExtendedWeaponAttributes = new ExtendedWeaponAttributes(newItem, ExtendedWeaponAttributes); weap.SkillBonuses = new AosSkillBonuses(newItem, SkillBonuses); weap.WeaponAttributes = new AosWeaponAttributes(newItem, WeaponAttributes); @@ -1140,7 +1151,7 @@ public abstract partial class BaseWeapon return; } - if (WeaponAttributes.BattleLust != 0) + if (ExtendedWeaponAttributes.BattleLust != 0) { BattleLust.Clear(m); } @@ -1185,7 +1196,7 @@ public abstract partial class BaseWeapon { base.OnMapChange(); - if ((Map == null || Map == Map.Internal) && Parent is Mobile m && WeaponAttributes.BattleLust != 0) + if ((Map == null || Map == Map.Internal) && Parent is Mobile m && ExtendedWeaponAttributes.BattleLust != 0) { BattleLust.Clear(m); } @@ -2395,7 +2406,7 @@ public abstract partial class BaseWeapon internal int GetBaneDamage(Mobile defender) { - if (!Core.HS || WeaponAttributes.Bane == 0 || defender?.Deleted != false || defender.HitsMax <= 0 || + if (!Core.HS || ExtendedWeaponAttributes.Bane == 0 || defender?.Deleted != false || defender.HitsMax <= 0 || defender.Hits / (double)defender.HitsMax >= 0.5) { return 0; @@ -3051,6 +3062,7 @@ public abstract partial class BaseWeapon list.Add(1072792); // Balanced } + ExtendedWeaponAttributes.GetProperties(list); WeaponAttributes.GetProperties(list); if (ImmolatingWeaponSpell.IsImmolating(this)) @@ -3820,6 +3832,8 @@ public abstract partial class BaseWeapon WeaponAttributes.Deserialize(reader); } + ExtendedWeaponAttributes = new ExtendedWeaponAttributes(this); + PlayerConstructed = GetSaveFlag(flags, OldSaveFlag.PlayerConstructed); SkillBonuses = new AosSkillBonuses(this); @@ -3850,6 +3864,8 @@ public abstract partial class BaseWeapon [AfterDeserialization] private void AfterDeserialization() { + _extendedWeaponAttributes ??= ExtendedWeaponAttributesDefaultValue(); + var parentMobile = Parent as Mobile; if (UseSkillMod && _accuracyLevel != WeaponAccuracyLevel.Regular && parentMobile != null) diff --git a/Projects/UOContent/Items/Weapons/BattleLust.cs b/Projects/UOContent/Items/Weapons/BattleLust.cs index 9555851ec..50d80ff7a 100644 --- a/Projects/UOContent/Items/Weapons/BattleLust.cs +++ b/Projects/UOContent/Items/Weapons/BattleLust.cs @@ -115,7 +115,7 @@ public static class BattleLust mobile.Map != null && mobile.Map != Map.Internal && mobile.Weapon is BaseWeapon weapon && - weapon.WeaponAttributes.BattleLust != 0; + weapon.ExtendedWeaponAttributes.BattleLust != 0; private static int GetActiveAggressedCount(Mobile mobile) { diff --git a/Projects/UOContent/Items/Weapons/Throwing/StormCaller.cs b/Projects/UOContent/Items/Weapons/Throwing/StormCaller.cs index afb568058..c095855df 100644 --- a/Projects/UOContent/Items/Weapons/Throwing/StormCaller.cs +++ b/Projects/UOContent/Items/Weapons/Throwing/StormCaller.cs @@ -9,8 +9,7 @@ public partial class StormCaller : Boomerang public StormCaller() { Hue = 456; - //TODO Implement BattleLust - //WeaponAttributes.BattleLust = 1; + ExtendedWeaponAttributes.BattleLust = 1; WeaponAttributes.HitLightning = 40; WeaponAttributes.HitLowerDefend = 30; Attributes.BonusStr = 5; diff --git a/Projects/UOContent/Migrations/Server.Items.BaseWeapon.v11.json b/Projects/UOContent/Migrations/Server.Items.BaseWeapon.v11.json new file mode 100644 index 000000000..c03ed306e --- /dev/null +++ b/Projects/UOContent/Migrations/Server.Items.BaseWeapon.v11.json @@ -0,0 +1,263 @@ +{ + "version": 11, + "type": "Server.Items.BaseWeapon", + "properties": [ + { + "name": "DamageLevel", + "type": "Server.Items.WeaponDamageLevel", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "AccuracyLevel", + "type": "Server.Items.WeaponAccuracyLevel", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "DurabilityLevel", + "type": "Server.Items.WeaponDurabilityLevel", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "Quality", + "type": "Server.Items.WeaponQuality", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "HitPoints", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "MaxHitPoints", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Slayer", + "type": "Server.Items.SlayerName", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "Poison", + "type": "Server.Poison", + "usesSaveFlag": true, + "rule": "PrimitiveUOTypeMigrationRule", + "ruleArguments": [ + "Poison" + ] + }, + { + "name": "PoisonCharges", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Crafter", + "type": "string", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Identified", + "type": "bool", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "StrRequirement", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "DexRequirement", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "IntRequirement", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "MinDamage", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "MaxDamage", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "HitSound", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "MissSound", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Speed", + "type": "float", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "MaxRange", + "type": "int", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "Skill", + "type": "Server.SkillName", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "Type", + "type": "Server.Items.WeaponType", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "Animation", + "type": "Server.Items.WeaponAnimation", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "Resource", + "type": "Server.Items.CraftResource", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "Attributes", + "type": "Server.AosAttributes", + "usesSaveFlag": true, + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + }, + { + "name": "WeaponAttributes", + "type": "Server.AosWeaponAttributes", + "usesSaveFlag": true, + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + }, + { + "name": "PlayerConstructed", + "type": "bool", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "SkillBonuses", + "type": "Server.AosSkillBonuses", + "usesSaveFlag": true, + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + }, + { + "name": "Slayer2", + "type": "Server.Items.SlayerName", + "usesSaveFlag": true, + "rule": "EnumMigrationRule" + }, + { + "name": "AosElementDamages", + "type": "Server.AosElementAttributes", + "usesSaveFlag": true, + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + }, + { + "name": "EngravedText", + "type": "string", + "usesSaveFlag": true, + "rule": "PrimitiveTypeMigrationRule", + "ruleArguments": [ + "" + ] + }, + { + "name": "ExtendedWeaponAttributes", + "type": "Server.ExtendedWeaponAttributes", + "rule": "RawSerializableMigrationRule", + "ruleArguments": [ + "DeserializationRequiresParent" + ] + } + ] +} \ No newline at end of file diff --git a/Projects/UOContent/Misc/AOS.cs b/Projects/UOContent/Misc/AOS.cs index 9a0e524af..a8ce1125b 100644 --- a/Projects/UOContent/Misc/AOS.cs +++ b/Projects/UOContent/Misc/AOS.cs @@ -781,9 +781,7 @@ namespace Server ResistEnergyBonus = 0x00200000, UseBestSkill = 0x00400000, MageWeapon = 0x00800000, - DurabilityBonus = 0x01000000, - Bane = 0x02000000, - BattleLust = 0x04000000 + DurabilityBonus = 0x01000000 } public sealed class AosWeaponAttributes : BaseAttributes @@ -977,30 +975,6 @@ namespace Server set => this[AosWeaponAttribute.DurabilityBonus] = value; } - [CommandProperty(AccessLevel.GameMaster)] - public int Bane - { - get => this[AosWeaponAttribute.Bane]; - set => this[AosWeaponAttribute.Bane] = value; - } - - [CommandProperty(AccessLevel.GameMaster)] - public int BattleLust - { - get => this[AosWeaponAttribute.BattleLust]; - set - { - var hadBattleLust = BattleLust != 0; - - this[AosWeaponAttribute.BattleLust] = value; - - if (hadBattleLust && value == 0 && Owner is BaseWeapon { Parent: Mobile m }) - { - Server.Items.BattleLust.Clear(m); - } - } - } - public static int GetValue(Mobile m, AosWeaponAttribute attribute) { if (!Core.AOS) @@ -1042,16 +1016,6 @@ namespace Server { int prop; - if (Core.HS && Bane != 0) - { - list.Add(1154671); // Bane - } - - if (Core.SA && BattleLust != 0) - { - list.Add(1113710); // Battle Lust - } - if (UseBestSkill != 0) { list.Add(1060400); // use best weapon skill @@ -1146,6 +1110,69 @@ namespace Server public override string ToString() => "..."; } + [Flags] + public enum ExtendedWeaponAttribute + { + Bane = 0x00000001, + BattleLust = 0x00000002 + } + + public sealed class ExtendedWeaponAttributes : BaseAttributes + { + public ExtendedWeaponAttributes(Item owner) : base(owner) + { + } + + public ExtendedWeaponAttributes(Item owner, ExtendedWeaponAttributes other) : base(owner, other) + { + } + + public int this[ExtendedWeaponAttribute attribute] + { + get => GetValue((int)attribute); + set => SetValue((int)attribute, value); + } + + [CommandProperty(AccessLevel.GameMaster)] + public int Bane + { + get => this[ExtendedWeaponAttribute.Bane]; + set => this[ExtendedWeaponAttribute.Bane] = value; + } + + [CommandProperty(AccessLevel.GameMaster)] + public int BattleLust + { + get => this[ExtendedWeaponAttribute.BattleLust]; + set + { + var hadBattleLust = BattleLust != 0; + + this[ExtendedWeaponAttribute.BattleLust] = value; + + if (hadBattleLust && value == 0 && Owner is BaseWeapon { Parent: Mobile m }) + { + Server.Items.BattleLust.Clear(m); + } + } + } + + public void GetProperties(IPropertyList list) + { + if (Core.HS && Bane != 0) + { + list.Add(1154671); // Bane + } + + if (Core.SA && BattleLust != 0) + { + list.Add(1113710); // Battle Lust + } + } + + public override string ToString() => "..."; + } + [Flags] public enum AosArmorAttribute {