Merge pull request #11 from RebirthUO/feat/issue-7-extended-weapon-attributes

feat(items): add extended weapon attributes
This commit is contained in:
Crome696 2026-07-08 22:53:05 +02:00 committed by GitHub
commit dde5fb6d49
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 548 additions and 76 deletions

View file

@ -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
{

View file

@ -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;
}

View file

@ -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<AosWeaponAttribute>();
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<CommandPropertyAttribute>();
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<CommandPropertyAttribute>();
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)
{
}
}
}

View file

@ -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();
}
}

View file

@ -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)

View file

@ -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)
{

View file

@ -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;

View file

@ -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"
]
}
]
}

View file

@ -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
{