diff --git a/documentation/DocumentationTest.CellonDocumentation.approved.md b/documentation/DocumentationTest.CellonDocumentation.approved.md
new file mode 100644
index 0000000..1c3e9e4
--- /dev/null
+++ b/documentation/DocumentationTest.CellonDocumentation.approved.md
@@ -0,0 +1,70 @@
+# Cellon Table
+## Cellon 1 - Cost: 700
+- Hp: 30 - 100
+- Mp: 50 - 120
+- HpRecovery: 5 - 10
+- MpRecovery: 8 - 15
+
+## Cellon 2 - Cost: 1400
+- Hp: 120 - 200
+- Mp: 150 - 250
+- HpRecovery: 14 - 20
+- MpRecovery: 16 - 25
+
+## Cellon 3 - Cost: 3000
+- Hp: 220 - 300
+- Mp: 280 - 330
+- HpRecovery: 22 - 28
+- MpRecovery: 28 - 35
+
+## Cellon 4 - Cost: 5000
+- Hp: 330 - 400
+- Mp: 350 - 420
+- HpRecovery: 30 - 38
+- MpRecovery: 38 - 45
+
+## Cellon 5 - Cost: 10000
+- Hp: 430 - 550
+- Mp: 450 - 550
+- HpRecovery: 40 - 50
+- MpRecovery: 50 - 60
+
+## Cellon 6 - Cost: 20000
+- Hp: 600 - 750
+- Mp: 600 - 750
+- HpRecovery: 55 - 70
+- MpRecovery: 65 - 80
+- MpConsumption: 1 - 7
+- CriticalDamageDecrease: 1 - 10
+
+## Cellon 7 - Cost: 32000
+- Hp: 800 - 1000
+- Mp: 800 - 1000
+- HpRecovery: 75 - 90
+- MpRecovery: 75 - 90
+- MpConsumption: 8 - 12
+- CriticalDamageDecrease: 11 - 20
+
+## Cellon 8 - Cost: 58000
+- Hp: 1000 - 1300
+- Mp: 1000 - 1300
+- HpRecovery: 100 - 120
+- MpRecovery: 100 - 120
+- MpConsumption: 13 - 17
+- CriticalDamageDecrease: 21 - 35
+
+## Cellon 9 - Cost: 95000
+- Hp: 1100 - 1500
+- Mp: 1100 - 1500
+- HpRecovery: 110 - 135
+- MpRecovery: 110 - 135
+- MpConsumption: 14 - 21
+- CriticalDamageDecrease: 22 - 45
+
+## Cellon 10 - Cost: 134900
+- Hp: 1200 - 1700
+- Mp: 1200 - 1700
+- HpRecovery: 120 - 150
+- MpRecovery: 120 - 150
+- MpConsumption: 15 - 25
+- CriticalDamageDecrease: 23 - 55
diff --git a/src/NosCore.Algorithm/CellonService/CellonOption.cs b/src/NosCore.Algorithm/CellonService/CellonOption.cs
new file mode 100644
index 0000000..e001a52
--- /dev/null
+++ b/src/NosCore.Algorithm/CellonService/CellonOption.cs
@@ -0,0 +1,18 @@
+// __ _ __ __ ___ __ ___ ___
+// | \| |/__\ /' _/ / _//__\| _ \ __|
+// | | ' | \/ |`._`.| \_| \/ | v / _|
+// |_|\__|\__/ |___/ \__/\__/|_|_\___|
+// -----------------------------------
+
+using NosCore.Shared.Enumerations;
+
+namespace NosCore.Algorithm.CellonService
+{
+ ///
+ /// An option a cellon can roll, with the inclusive range its value is drawn from
+ ///
+ /// The option type
+ /// The lowest value the option can roll
+ /// The highest value the option can roll
+ public sealed record CellonOption(CellonType Type, int Minimum, int Maximum);
+}
diff --git a/src/NosCore.Algorithm/CellonService/CellonService.cs b/src/NosCore.Algorithm/CellonService/CellonService.cs
new file mode 100644
index 0000000..83f956f
--- /dev/null
+++ b/src/NosCore.Algorithm/CellonService/CellonService.cs
@@ -0,0 +1,56 @@
+// __ _ __ __ ___ __ ___ ___
+// | \| |/__\ /' _/ / _//__\| _ \ __|
+// | | ' | \/ |`._`.| \_| \/ | v / _|
+// |_|\__|\__/ |___/ \__/\__/|_|_\___|
+// -----------------------------------
+
+using System;
+using System.Collections.Generic;
+using NosCore.Shared.Enumerations;
+
+namespace NosCore.Algorithm.CellonService
+{
+ ///
+ /// Provides the options a cellon can add to an accessory and what applying it costs
+ ///
+ public class CellonService : ICellonService
+ {
+ internal static readonly long[] Price =
+ { 0, 700, 1400, 3000, 5000, 10000, 20000, 32000, 58000, 95000, 134900 };
+
+ internal static readonly CellonOption[][] Options =
+ {
+ Array.Empty(),
+ new CellonOption[] { new(CellonType.Hp, 30, 100), new(CellonType.Mp, 50, 120), new(CellonType.HpRecovery, 5, 10), new(CellonType.MpRecovery, 8, 15) },
+ new CellonOption[] { new(CellonType.Hp, 120, 200), new(CellonType.Mp, 150, 250), new(CellonType.HpRecovery, 14, 20), new(CellonType.MpRecovery, 16, 25) },
+ new CellonOption[] { new(CellonType.Hp, 220, 300), new(CellonType.Mp, 280, 330), new(CellonType.HpRecovery, 22, 28), new(CellonType.MpRecovery, 28, 35) },
+ new CellonOption[] { new(CellonType.Hp, 330, 400), new(CellonType.Mp, 350, 420), new(CellonType.HpRecovery, 30, 38), new(CellonType.MpRecovery, 38, 45) },
+ new CellonOption[] { new(CellonType.Hp, 430, 550), new(CellonType.Mp, 450, 550), new(CellonType.HpRecovery, 40, 50), new(CellonType.MpRecovery, 50, 60) },
+ new CellonOption[] { new(CellonType.Hp, 600, 750), new(CellonType.Mp, 600, 750), new(CellonType.HpRecovery, 55, 70), new(CellonType.MpRecovery, 65, 80), new(CellonType.MpConsumption, 1, 7), new(CellonType.CriticalDamageDecrease, 1, 10) },
+ new CellonOption[] { new(CellonType.Hp, 800, 1000), new(CellonType.Mp, 800, 1000), new(CellonType.HpRecovery, 75, 90), new(CellonType.MpRecovery, 75, 90), new(CellonType.MpConsumption, 8, 12), new(CellonType.CriticalDamageDecrease, 11, 20) },
+ new CellonOption[] { new(CellonType.Hp, 1000, 1300), new(CellonType.Mp, 1000, 1300), new(CellonType.HpRecovery, 100, 120), new(CellonType.MpRecovery, 100, 120), new(CellonType.MpConsumption, 13, 17), new(CellonType.CriticalDamageDecrease, 21, 35) },
+ new CellonOption[] { new(CellonType.Hp, 1100, 1500), new(CellonType.Mp, 1100, 1500), new(CellonType.HpRecovery, 110, 135), new(CellonType.MpRecovery, 110, 135), new(CellonType.MpConsumption, 14, 21), new(CellonType.CriticalDamageDecrease, 22, 45) },
+ new CellonOption[] { new(CellonType.Hp, 1200, 1700), new(CellonType.Mp, 1200, 1700), new(CellonType.HpRecovery, 120, 150), new(CellonType.MpRecovery, 120, 150), new(CellonType.MpConsumption, 15, 25), new(CellonType.CriticalDamageDecrease, 23, 55) },
+ };
+
+ ///
+ /// Gets the options a cellon of a specific level can roll
+ ///
+ /// The cellon level
+ /// The options, or an empty list for a level no cellon has
+ public IReadOnlyList GetOptions(byte level)
+ {
+ return level <= Constants.MaxCellonLevel ? Array.AsReadOnly(Options[level]) : Array.Empty();
+ }
+
+ ///
+ /// Gets the gold cost of applying a cellon of a specific level
+ ///
+ /// The cellon level
+ /// The gold cost
+ public long GetPrice(byte level)
+ {
+ return Price[level];
+ }
+ }
+}
diff --git a/src/NosCore.Algorithm/CellonService/ICellonService.cs b/src/NosCore.Algorithm/CellonService/ICellonService.cs
new file mode 100644
index 0000000..5a21fb4
--- /dev/null
+++ b/src/NosCore.Algorithm/CellonService/ICellonService.cs
@@ -0,0 +1,30 @@
+// __ _ __ __ ___ __ ___ ___
+// | \| |/__\ /' _/ / _//__\| _ \ __|
+// | | ' | \/ |`._`.| \_| \/ | v / _|
+// |_|\__|\__/ |___/ \__/\__/|_|_\___|
+// -----------------------------------
+
+using System.Collections.Generic;
+
+namespace NosCore.Algorithm.CellonService
+{
+ ///
+ /// Service for retrieving the options a cellon can add to an accessory and what applying it costs
+ ///
+ public interface ICellonService
+ {
+ ///
+ /// Gets the options a cellon of a specific level can roll
+ ///
+ /// The cellon level
+ /// The options, or an empty list for a level no cellon has
+ IReadOnlyList GetOptions(byte level);
+
+ ///
+ /// Gets the gold cost of applying a cellon of a specific level
+ ///
+ /// The cellon level
+ /// The gold cost
+ long GetPrice(byte level);
+ }
+}
diff --git a/src/NosCore.Algorithm/Constants.cs b/src/NosCore.Algorithm/Constants.cs
index 1d90315..96566f9 100644
--- a/src/NosCore.Algorithm/Constants.cs
+++ b/src/NosCore.Algorithm/Constants.cs
@@ -12,6 +12,7 @@ internal class Constants
internal const byte MaxMateLevel = 99;
internal const byte MaxFamilyLevel = 30;
internal const byte MaxGroupSize = 3;
+ internal const byte MaxCellonLevel = 10;
internal static readonly int ClassCount = Enum.GetNames(typeof(CharacterClassType)).Length;
}
}
diff --git a/src/NosCore.Algorithm/NosCore.Algorithm.csproj b/src/NosCore.Algorithm/NosCore.Algorithm.csproj
index a9280f6..34b8530 100644
--- a/src/NosCore.Algorithm/NosCore.Algorithm.csproj
+++ b/src/NosCore.Algorithm/NosCore.Algorithm.csproj
@@ -13,7 +13,7 @@
https://github.com/NosCoreIO/NosCore.Algorithm.git
nostale, noscore, nostale private server source, nostale emulator
- 2.1.0
+ 2.3.0
false
NosCore's Algorithm
GPL-3.0-only
@@ -29,7 +29,7 @@
-
+
diff --git a/test/NosCore.Algorithm.Tests/DocumentationTest.cs b/test/NosCore.Algorithm.Tests/DocumentationTest.cs
index c32bc15..33a367c 100644
--- a/test/NosCore.Algorithm.Tests/DocumentationTest.cs
+++ b/test/NosCore.Algorithm.Tests/DocumentationTest.cs
@@ -406,6 +406,34 @@ public void SumDocumentation()
Approvals.Verify(WriterFactory.CreateTextWriter(resultBuilder.ToString(), "md"));
}
+ [TestMethod]
+ public void CellonDocumentation()
+ {
+ var cellonService = new CellonService.CellonService();
+
+ var resultBuilder = new StringBuilder("# Cellon Table");
+ for (byte level = 1; level <= 10; level++)
+ {
+ resultBuilder.AppendLine();
+ resultBuilder.AppendLine($"## Cellon {level} - Cost: {cellonService.GetPrice(level)}");
+ foreach (var option in cellonService.GetOptions(level))
+ {
+ resultBuilder.AppendLine($"- {option.Type}: {option.Minimum} - {option.Maximum}");
+ }
+ }
+
+ Approvals.Verify(WriterFactory.CreateTextWriter(resultBuilder.ToString(), "md"));
+ }
+
+ [TestMethod]
+ public void NoCellonLevelHasNoOptions()
+ {
+ var cellonService = new CellonService.CellonService();
+
+ Assert.AreEqual(0, cellonService.GetOptions(0).Count);
+ Assert.AreEqual(0, cellonService.GetOptions(11).Count);
+ }
+
[TestMethod]
public void MateExperienceDocumentation()
{