diff --git a/documentation/DocumentationTest.ShellDocumentation.approved.md b/documentation/DocumentationTest.ShellDocumentation.approved.md index 2bb0e6e..c26edd0 100644 --- a/documentation/DocumentationTest.ShellDocumentation.approved.md +++ b/documentation/DocumentationTest.ShellDocumentation.approved.md @@ -209,3 +209,30 @@ - Level 70: 8 9 10 11 12 13 14 - Level 80: 9 10 11 12 13 14 15 - Level 90: 10 11 12 13 14 15 16 + +## Perfume cost (rarity 0 to 8, then gold) +- Level 0: 0 0 0 0 0 0 0 0 0 - 0 gold +- Level 1: 0 0 0 0 0 0 0 0 0 - 1000 gold +- Level 10: 0 0 0 0 0 0 0 0 0 - 1000 gold +- Level 11: 0 0 0 0 0 0 0 0 0 - 2000 gold +- Level 20: 0 0 0 0 0 0 0 0 0 - 2000 gold +- Level 21: 0 0 0 0 5 5 5 10 0 - 5000 gold +- Level 39: 0 0 0 0 5 5 5 10 0 - 5000 gold +- Level 40: 0 0 0 5 5 10 20 40 0 - 8000 gold +- Level 49: 0 0 0 5 5 10 20 40 0 - 8000 gold +- Level 50: 0 0 5 10 10 20 40 80 0 - 10000 gold +- Level 59: 0 0 5 10 10 20 40 80 0 - 10000 gold +- Level 60: 0 5 10 10 20 40 80 120 0 - 12500 gold +- Level 69: 0 5 10 10 20 40 80 120 0 - 12500 gold +- Level 70: 0 10 10 20 40 80 120 160 0 - 15000 gold +- Level 79: 0 10 10 20 40 80 120 160 0 - 15000 gold +- Level 80: 0 10 20 40 80 120 160 200 0 - 17500 gold +- Level 89: 0 10 20 40 80 120 160 200 0 - 17500 gold +- Level 90: 0 20 40 80 120 160 200 300 0 - 20000 gold +- Level 99: 0 20 40 80 120 160 200 300 0 - 20000 gold +- Level 100: 0 0 0 0 0 0 0 0 0 - 0 gold + +## Heroic perfume cost (rarity 0 to 8) +- Level 1: 1 41 81 121 161 201 241 281 321 +- Level 50: 50 90 130 170 210 250 290 330 370 +- Level 99: 99 139 179 219 259 299 339 379 419 diff --git a/src/NosCore.Algorithm/ShellService/IShellService.cs b/src/NosCore.Algorithm/ShellService/IShellService.cs index 95e61f7..557928c 100644 --- a/src/NosCore.Algorithm/ShellService/IShellService.cs +++ b/src/NosCore.Algorithm/ShellService/IShellService.cs @@ -51,5 +51,21 @@ public interface IShellService /// The shell rarity /// The pearl count int GetIdentificationCost(byte shellLevel, byte rarity); + + /// + /// Gets how many bottles of Perfume taking over an item bound to another player costs + /// + /// The item's level + /// The item's rarity, as the client holds it (a negative rarity wraps above 8) + /// Whether the item is a heroic item + /// The perfume count, 0 for a level or rarity the client gives no count + int GetPerfumeCost(byte itemLevel, byte rarity, bool heroic); + + /// + /// Gets the gold that taking over an item bound to another player costs on top of the Perfume + /// + /// The item's level + /// The gold, 0 for level 0 and from level 100 + int GetPerfumeGoldCost(byte itemLevel); } } diff --git a/src/NosCore.Algorithm/ShellService/ShellService.cs b/src/NosCore.Algorithm/ShellService/ShellService.cs index d5d36de..e26b7bc 100644 --- a/src/NosCore.Algorithm/ShellService/ShellService.cs +++ b/src/NosCore.Algorithm/ShellService/ShellService.cs @@ -144,5 +144,47 @@ public int GetIdentificationCost(byte shellLevel, byte rarity) { return shellLevel / 10 + rarity; } + + /// + public int GetPerfumeCost(byte itemLevel, byte rarity, bool heroic) + { + if (heroic) + { + return itemLevel + HeroicPerfumePerRarity * rarity; + } + + var bracket = Array.FindLastIndex(PerfumeLevels, l => l <= itemLevel); + return bracket < 0 || rarity >= PerfumeByRarity[bracket].Length ? 0 : PerfumeByRarity[bracket][rarity]; + } + + /// + public int GetPerfumeGoldCost(byte itemLevel) + { + var bracket = Array.FindLastIndex(PerfumeLevels, l => l <= itemLevel); + return bracket < 0 ? 0 : PerfumeGold[bracket]; + } + + private const int HeroicPerfumePerRarity = 40; + + // The client's perfume window computes both costs itself. Each entry is the first item level + // of a bracket; level 100 and above costs nothing, like level 0. + private static readonly byte[] PerfumeLevels = { 1, 11, 21, 40, 50, 60, 70, 80, 90, 100 }; + + private static readonly int[] PerfumeGold = { 1000, 2000, 5000, 8000, 10000, 12500, 15000, 17500, 20000, 0 }; + + // By rarity 0 to 7; rarity 8 and above costs no perfume. + private static readonly int[][] PerfumeByRarity = + { + new[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + new[] { 0, 0, 0, 0, 0, 0, 0, 0 }, + new[] { 0, 0, 0, 0, 5, 5, 5, 10 }, + new[] { 0, 0, 0, 5, 5, 10, 20, 40 }, + new[] { 0, 0, 5, 10, 10, 20, 40, 80 }, + new[] { 0, 5, 10, 10, 20, 40, 80, 120 }, + new[] { 0, 10, 10, 20, 40, 80, 120, 160 }, + new[] { 0, 10, 20, 40, 80, 120, 160, 200 }, + new[] { 0, 20, 40, 80, 120, 160, 200, 300 }, + Array.Empty(), + }; } } diff --git a/test/NosCore.Algorithm.Tests/DocumentationTest.cs b/test/NosCore.Algorithm.Tests/DocumentationTest.cs index 2766a1d..bb14111 100644 --- a/test/NosCore.Algorithm.Tests/DocumentationTest.cs +++ b/test/NosCore.Algorithm.Tests/DocumentationTest.cs @@ -563,6 +563,20 @@ public void ShellDocumentation() resultBuilder.AppendLine($"- Level {level,2}: {string.Join(" ", Enumerable.Range(1, 7).Select(r => shellService.GetIdentificationCost(level, (byte)r)))}"); } + resultBuilder.AppendLine(); + resultBuilder.AppendLine("## Perfume cost (rarity 0 to 8, then gold)"); + foreach (byte level in new byte[] { 0, 1, 10, 11, 20, 21, 39, 40, 49, 50, 59, 60, 69, 70, 79, 80, 89, 90, 99, 100 }) + { + resultBuilder.AppendLine($"- Level {level,3}: {string.Join(" ", Enumerable.Range(0, 9).Select(r => shellService.GetPerfumeCost(level, (byte)r, false)))} - {shellService.GetPerfumeGoldCost(level)} gold"); + } + + resultBuilder.AppendLine(); + resultBuilder.AppendLine("## Heroic perfume cost (rarity 0 to 8)"); + foreach (byte level in new byte[] { 1, 50, 99 }) + { + resultBuilder.AppendLine($"- Level {level,2}: {string.Join(" ", Enumerable.Range(0, 9).Select(r => shellService.GetPerfumeCost(level, (byte)r, true)))}"); + } + Approvals.Verify(WriterFactory.CreateTextWriter(resultBuilder.ToString(), "md")); }