using AcDream.App.UI.Layout; using AcDream.Core.Items; using AcDream.Core.Net.Messages; using AcDream.Core.Spells; namespace AcDream.App.Tests.UI.Layout; public sealed class ItemAppraisalTextFormatterTests { [Fact] public void WeaponAndMagic_AreProjectedInRetailOrderWithDatDescriptions() { var obj = new ClientObject { ObjectId = 0x50000001u, Name = "Flaming Sword", Type = ItemType.MeleeWeapon, ValidLocations = EquipMask.MeleeWeapon, Value = 12_500, Burden = 450, Workmanship = 8.4f, }; var properties = new PropertyBundle(); properties.Ints[353u] = 2; properties.Ints[204u] = 4; properties.Ints[106u] = 300; properties.Ints[107u] = 250; properties.Ints[108u] = 500; properties.Ints[117u] = 20; properties.Floats[29u] = 1.05d; properties.Strings[14u] = "Use this item to attack."; properties.Strings[16u] = "A finely balanced enchanted blade."; SpellMetadata flame = Spell(101u, "Flame Bolt VI", "Hurls a bolt of flame."); SpellMetadata armor = Spell(102u, "Impenetrability VI", "Increases the target's armor."); AppraiseInfoParser.Parsed appraisal = Parsed( properties, spells: [101u, 0x8000_0066u], weapon: new AppraiseInfoParser.WeaponProfile( DamageType: 1u, WeaponTime: 30u, WeaponSkill: 44u, Damage: 40u, DamageVariance: 0.25d, DamageMod: 1d, WeaponLength: 1d, MaxVelocity: 0d, WeaponOffense: 1.15d, MaxVelocityEstimated: 0u)); string report = ItemAppraisalTextFormatter.Build( obj, appraisal, id => id switch { 101u => flame, 102u => armor, _ => null, }); Assert.Contains("Value: 12,500", report); Assert.Contains("Workmanship: Utterly flawless (8.4)", report); Assert.Contains("Skill: Heavy Weapons (Sword)", report); Assert.Contains("Damage: 30 - 40, Slashing", report); Assert.Contains("Elemental Damage Bonus: 4, Slashing.", report); Assert.Contains("Speed: Fast (30)", report); Assert.Contains("Bonus to Attack Skill: +15%.", report); Assert.Contains("Bonus to Melee Defense: +5%.", report); Assert.Contains("Spells: Flame Bolt VI, Impenetrability VI", report); Assert.Contains("Spellcraft: 300.", report); Assert.Contains("Mana: 250 / 500.", report); Assert.Contains("Mana Cost: 20.", report); Assert.Contains("Spell Descriptions:", report); Assert.Contains("Flame Bolt VI\nHurls a bolt of flame.", report); Assert.Contains("Enchantments:", report); Assert.Contains("Impenetrability VI\nIncreases the target's armor.", report); Assert.True( report.IndexOf("Skill:", StringComparison.Ordinal) < report.IndexOf("Spells:", StringComparison.Ordinal)); Assert.True( report.IndexOf("Spells:", StringComparison.Ordinal) < report.IndexOf("Spell Descriptions:", StringComparison.Ordinal)); Assert.EndsWith("A finely balanced enchanted blade.", report); } [Fact] public void ArmorRequirementsAndUses_UseRetailLabelsAndResistanceBands() { var obj = new ClientObject { ObjectId = 0x50000002u, Name = "Platemail Hauberk", Type = ItemType.Armor, ValidLocations = EquipMask.ChestArmor, Structure = 8, MaxStructure = 10, }; var properties = new PropertyBundle(); properties.Ints[28u] = 200; properties.Ints[158u] = 1; properties.Ints[159u] = 6; properties.Ints[160u] = 250; properties.Ints[324u] = 4; properties.Ints[369u] = 50; properties.Ints[370u] = 3; properties.Bools[69u] = false; properties.Strings[25u] = "Tester"; AppraiseInfoParser.Parsed appraisal = Parsed( properties, armor: new AppraiseInfoParser.ArmorProfile( SlashingProtection: 1.2f, PiercingProtection: 0.8f, BludgeoningProtection: 0.4f, ColdProtection: 0f, FireProtection: 1.6f, AcidProtection: 2f, NetherProtection: 1f, LightningProtection: 0.2f)); string report = ItemAppraisalTextFormatter.Build( obj, appraisal, _ => null); Assert.Contains("Ratings: Dam +3", report); Assert.Contains("Armor Level: 200", report); Assert.Contains("Slashing: Above Average (240)", report); Assert.Contains("Piercing: Average (160)", report); Assert.Contains("Bludgeoning: Below Average (80)", report); Assert.Contains("Cold: None (0)", report); Assert.Contains("Fire: Excellent (320)", report); Assert.Contains("Acid: Unparalleled (400)", report); Assert.Contains("Electric: Poor (40)", report); Assert.Contains("Wield requires Viamontian", report); Assert.Contains("Wield requires Melee Defense 250", report); Assert.Contains("Use requires level 50.", report); Assert.Contains("Number of uses remaining: 8", report); Assert.Contains("Created by Tester.", report); Assert.Contains("This item cannot be sold.", report); } [Fact] public void Launcher_UsesDamageBonusModifierAndAmmunitionWording() { var obj = new ClientObject { ObjectId = 0x50000003u, Name = "Longbow", Type = ItemType.MissileWeapon, ValidLocations = EquipMask.MissileWeapon, AmmoType = 1, }; AppraiseInfoParser.Parsed appraisal = Parsed( new PropertyBundle(), weapon: new AppraiseInfoParser.WeaponProfile( DamageType: 2u, WeaponTime: 45u, WeaponSkill: 47u, Damage: 12u, DamageVariance: 0d, DamageMod: 1.13d, WeaponLength: 1d, MaxVelocity: 50d, WeaponOffense: 1d, MaxVelocityEstimated: 1u)); string report = ItemAppraisalTextFormatter.Build( obj, appraisal, _ => null); Assert.Contains("Skill: Missile Weapons", report); Assert.Contains("Damage Bonus: 12", report); Assert.DoesNotContain("Damage Bonus: 12, Piercing", report); Assert.Contains("Damage Modifier: +13%.", report); Assert.Contains("Speed: Average (45)", report); Assert.Contains("Range: 85 yds. (based on STRENGTH 100)", report); Assert.Contains("Uses arrows as ammunition.", report); } [Fact] public void SpecialProperties_AreCombinedUsingRetailNames() { var obj = new ClientObject { ObjectId = 0x50000004u, Name = "Imbued Katar", Type = ItemType.MeleeWeapon, }; var properties = new PropertyBundle(); properties.Ints[279u] = 3; properties.Ints[292u] = 4; properties.Ints[179u] = 0x0000_0481; properties.Ints[114u] = 1; properties.Ints[33u] = 1; properties.Floats[167u] = 90d; properties.Bools[99u] = true; properties.Bools[100u] = true; string report = ItemAppraisalTextFormatter.Build( obj, Parsed(properties), _ => null); Assert.Contains("You can only carry 3 of these items.", report); Assert.Contains("Cooldown When Used: 1m 30s ", report); Assert.Contains("Cleave: 4 enemies in front arc.", report); Assert.Contains( "Properties: Critical Strike, Cold Rending, +1 Melee Defense, Attuned, Bonded, Ivoryable, Dyeable", report); Assert.Contains("This item cannot be further imbued.", report); } private static AppraiseInfoParser.Parsed Parsed( PropertyBundle properties, uint[]? spells = null, AppraiseInfoParser.WeaponProfile? weapon = null, AppraiseInfoParser.ArmorProfile? armor = null) => new( Guid: 0x50000001u, Flags: AppraiseInfoParser.IdentifyResponseFlags.IntStatsTable, Success: true, Properties: properties, SpellBook: spells ?? [], ArmorProfile: armor, CreatureProfile: null, WeaponProfile: weapon, HookProfile: null, ArmorLevels: null, ArmorEnchantments: null, WeaponEnchantments: null, ResistEnchantments: null); private static SpellMetadata Spell( uint id, string name, string description) => new( SpellId: id, Name: name, School: "War Magic", Family: id, IconId: 0u, SpellWords: string.Empty, Duration: 0f, ManaCost: 10, IsDebuff: false, IsFellowship: false, Description: description, SortKey: 0, Difficulty: 0, Flags: 0u, Generation: 6, IsFastWindup: false, IsOffensive: true, IsUntargeted: false, Speed: 0f, CasterEffect: 0u, TargetEffect: 0u, TargetMask: 0u, SpellType: 0); }