From de8bbd23299c4cdbe32116f9436d606a553ea7b2 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 12 Nov 2026 23:00:25 +0000 Subject: [PATCH] Add the perfume cost of taking over a bound item The client's perfume window computes the perfume and gold it asks for from the item's level and rarity, and for heroic items from the level plus 40 per rarity. ShellService now gives the same numbers. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_014gSmkVtYup7KTZ63CJcxZT --- ...ntationTest.ShellDocumentation.approved.md | 27 ++++++++++++ .../ShellService/IShellService.cs | 16 +++++++ .../ShellService/ShellService.cs | 42 +++++++++++++++++++ .../DocumentationTest.cs | 14 +++++++ 4 files changed, 99 insertions(+) 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")); }