From ea92545b0d447e11a4c8da450cc0cb0e023038a3 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Mon, 22 Jun 2026 07:59:05 -0700 Subject: [PATCH] feat(aos): add AosAttributes.GetProperties(IPropertyList) with computed-bonus params --- .../AosAttributesPropertiesTests.cs | 75 +++++++++++ Projects/UOContent/Misc/AOS.cs | 125 ++++++++++++++++++ 2 files changed, 200 insertions(+) create mode 100644 Projects/UOContent.Tests/Tests/PropertyList/AosAttributesPropertiesTests.cs diff --git a/Projects/UOContent.Tests/Tests/PropertyList/AosAttributesPropertiesTests.cs b/Projects/UOContent.Tests/Tests/PropertyList/AosAttributesPropertiesTests.cs new file mode 100644 index 000000000..6571c1312 --- /dev/null +++ b/Projects/UOContent.Tests/Tests/PropertyList/AosAttributesPropertiesTests.cs @@ -0,0 +1,75 @@ +using System; +using System.Buffers.Binary; +using System.Collections.Generic; +using System.Text; +using Server; +using Server.Items; +using Xunit; + +namespace UOContent.Tests; + +[Collection("Sequential UOContent Tests")] +public class AosAttributesPropertiesTests +{ + private static Dictionary Decode(ObjectPropertyList opl) + { + opl.Terminate(); + var buffer = opl.Buffer; + var map = new Dictionary(); + var pos = 15; + while (true) + { + var cliloc = BinaryPrimitives.ReadInt32BigEndian(buffer.AsSpan(pos)); + pos += 4; + if (cliloc == 0) + { + break; + } + + var byteLen = BinaryPrimitives.ReadUInt16BigEndian(buffer.AsSpan(pos)); + pos += 2; + map[cliloc] = Encoding.Unicode.GetString(buffer, pos, byteLen); + pos += byteLen; + } + + return map; + } + + [Fact] + public void EmitsRawAttributesInCanonicalOrder() + { + var attrs = new AosAttributes(null) + { + DefendChance = 5, + BonusStr = 10, + NightSight = 1, + SpellChanneling = 1, + WeaponSpeed = 7 + }; + + var opl = new ObjectPropertyList(null); + attrs.GetProperties(opl); + var map = Decode(opl); + + Assert.Equal("5", map[1060408]); // DefendChance + Assert.Equal("10", map[1060485]); // BonusStr + Assert.Equal("", map[1060441]); // NightSight (no-arg) + Assert.Equal("", map[1060482]); // SpellChanneling (no-arg) + Assert.Equal("7", map[1060486]); // WeaponSpeed + Assert.False(map.ContainsKey(1060401)); // WeaponDamage not set + } + + [Fact] + public void AppliesComputedBonuses() + { + var attrs = new AosAttributes(null) { WeaponDamage = 10, AttackChance = 4, Luck = 100 }; + + var opl = new ObjectPropertyList(null); + attrs.GetProperties(opl, damageBonus: 5, hitChanceBonus: 3, luckBonus: 50); + var map = Decode(opl); + + Assert.Equal("15", map[1060401]); // WeaponDamage + damageBonus + Assert.Equal("7", map[1060415]); // AttackChance + hitChanceBonus + Assert.Equal("150", map[1060436]); // Luck + luckBonus + } +} diff --git a/Projects/UOContent/Misc/AOS.cs b/Projects/UOContent/Misc/AOS.cs index ec8c09b84..48f081634 100644 --- a/Projects/UOContent/Misc/AOS.cs +++ b/Projects/UOContent/Misc/AOS.cs @@ -493,6 +493,131 @@ namespace Server set => this[AosAttribute.IncreasedKarmaLoss] = value; } + public void GetProperties(IPropertyList list, int damageBonus = 0, int hitChanceBonus = 0, int luckBonus = 0) + { + int prop; + + if ((prop = WeaponDamage + damageBonus) != 0) + { + list.Add(1060401, prop); // damage increase ~1_val~% + } + + if ((prop = DefendChance) != 0) + { + list.Add(1060408, prop); // defense chance increase ~1_val~% + } + + if ((prop = BonusDex) != 0) + { + list.Add(1060409, prop); // dexterity bonus ~1_val~ + } + + if ((prop = EnhancePotions) != 0) + { + list.Add(1060411, prop); // enhance potions ~1_val~% + } + + if ((prop = CastRecovery) != 0) + { + list.Add(1060412, prop); // faster cast recovery ~1_val~ + } + + if ((prop = CastSpeed) != 0) + { + list.Add(1060413, prop); // faster casting ~1_val~ + } + + if ((prop = AttackChance + hitChanceBonus) != 0) + { + list.Add(1060415, prop); // hit chance increase ~1_val~% + } + + if ((prop = BonusHits) != 0) + { + list.Add(1060431, prop); // hit point increase ~1_val~ + } + + if ((prop = BonusInt) != 0) + { + list.Add(1060432, prop); // intelligence bonus ~1_val~ + } + + if ((prop = LowerManaCost) != 0) + { + list.Add(1060433, prop); // lower mana cost ~1_val~% + } + + if ((prop = LowerRegCost) != 0) + { + list.Add(1060434, prop); // lower reagent cost ~1_val~% + } + + if ((prop = Luck + luckBonus) != 0) + { + list.Add(1060436, prop); // luck ~1_val~ + } + + if ((prop = BonusMana) != 0) + { + list.Add(1060439, prop); // mana increase ~1_val~ + } + + if ((prop = RegenMana) != 0) + { + list.Add(1060440, prop); // mana regeneration ~1_val~ + } + + if (NightSight != 0) + { + list.Add(1060441); // night sight + } + + if ((prop = ReflectPhysical) != 0) + { + list.Add(1060442, prop); // reflect physical damage ~1_val~% + } + + if ((prop = RegenStam) != 0) + { + list.Add(1060443, prop); // stamina regeneration ~1_val~ + } + + if ((prop = RegenHits) != 0) + { + list.Add(1060444, prop); // hit point regeneration ~1_val~ + } + + if (SpellChanneling != 0) + { + list.Add(1060482); // spell channeling + } + + if ((prop = SpellDamage) != 0) + { + list.Add(1060483, prop); // spell damage increase ~1_val~% + } + + if ((prop = BonusStam) != 0) + { + list.Add(1060484, prop); // stamina increase ~1_val~ + } + + if ((prop = BonusStr) != 0) + { + list.Add(1060485, prop); // strength bonus ~1_val~ + } + + if ((prop = WeaponSpeed) != 0) + { + list.Add(1060486, prop); // swing speed increase ~1_val~% + } + + if (Core.ML && (prop = IncreasedKarmaLoss) != 0) + { + list.Add(1075210, prop); // Increased Karma Loss ~1val~% + } + } + public static int GetValue(Mobile m, AosAttribute attribute) { if (!Core.AOS)