diff --git a/documentation/DocumentationTest.SkillPointDocumentation.approved.md b/documentation/DocumentationTest.SkillPointDocumentation.approved.md
new file mode 100644
index 0000000..c1e59c3
--- /dev/null
+++ b/documentation/DocumentationTest.SkillPointDocumentation.approved.md
@@ -0,0 +1,350 @@
+# Skill Point (CP) Table
+## Class Adventurer
+- Job level 1 - CP: 2
+- Job level 2 - CP: 4
+- Job level 3 - CP: 6
+- Job level 4 - CP: 8
+- Job level 5 - CP: 10
+- Job level 6 - CP: 12
+- Job level 7 - CP: 14
+- Job level 8 - CP: 16
+- Job level 9 - CP: 18
+- Job level 10 - CP: 20
+- Job level 11 - CP: 22
+- Job level 12 - CP: 24
+- Job level 13 - CP: 26
+- Job level 14 - CP: 28
+- Job level 15 - CP: 30
+- Job level 16 - CP: 32
+- Job level 17 - CP: 34
+- Job level 18 - CP: 36
+- Job level 19 - CP: 38
+- Job level 20 - CP: 40
+
+## Class Swordsman
+- Job level 1 - CP: 42
+- Job level 2 - CP: 44
+- Job level 3 - CP: 46
+- Job level 4 - CP: 48
+- Job level 5 - CP: 50
+- Job level 6 - CP: 52
+- Job level 7 - CP: 54
+- Job level 8 - CP: 56
+- Job level 9 - CP: 58
+- Job level 10 - CP: 60
+- Job level 11 - CP: 62
+- Job level 12 - CP: 64
+- Job level 13 - CP: 66
+- Job level 14 - CP: 68
+- Job level 15 - CP: 70
+- Job level 16 - CP: 72
+- Job level 17 - CP: 74
+- Job level 18 - CP: 76
+- Job level 19 - CP: 78
+- Job level 20 - CP: 80
+- Job level 21 - CP: 82
+- Job level 22 - CP: 84
+- Job level 23 - CP: 86
+- Job level 24 - CP: 88
+- Job level 25 - CP: 90
+- Job level 26 - CP: 92
+- Job level 27 - CP: 94
+- Job level 28 - CP: 96
+- Job level 29 - CP: 98
+- Job level 30 - CP: 100
+- Job level 31 - CP: 102
+- Job level 32 - CP: 104
+- Job level 33 - CP: 106
+- Job level 34 - CP: 108
+- Job level 35 - CP: 110
+- Job level 36 - CP: 112
+- Job level 37 - CP: 114
+- Job level 38 - CP: 116
+- Job level 39 - CP: 118
+- Job level 40 - CP: 120
+- Job level 41 - CP: 122
+- Job level 42 - CP: 124
+- Job level 43 - CP: 126
+- Job level 44 - CP: 128
+- Job level 45 - CP: 130
+- Job level 46 - CP: 132
+- Job level 47 - CP: 134
+- Job level 48 - CP: 136
+- Job level 49 - CP: 138
+- Job level 50 - CP: 140
+- Job level 51 - CP: 142
+- Job level 52 - CP: 144
+- Job level 53 - CP: 146
+- Job level 54 - CP: 148
+- Job level 55 - CP: 150
+- Job level 56 - CP: 152
+- Job level 57 - CP: 154
+- Job level 58 - CP: 156
+- Job level 59 - CP: 158
+- Job level 60 - CP: 160
+- Job level 61 - CP: 162
+- Job level 62 - CP: 164
+- Job level 63 - CP: 166
+- Job level 64 - CP: 168
+- Job level 65 - CP: 170
+- Job level 66 - CP: 172
+- Job level 67 - CP: 174
+- Job level 68 - CP: 176
+- Job level 69 - CP: 178
+- Job level 70 - CP: 180
+- Job level 71 - CP: 182
+- Job level 72 - CP: 184
+- Job level 73 - CP: 186
+- Job level 74 - CP: 188
+- Job level 75 - CP: 190
+- Job level 76 - CP: 192
+- Job level 77 - CP: 194
+- Job level 78 - CP: 196
+- Job level 79 - CP: 198
+- Job level 80 - CP: 200
+
+## Class Archer
+- Job level 1 - CP: 42
+- Job level 2 - CP: 44
+- Job level 3 - CP: 46
+- Job level 4 - CP: 48
+- Job level 5 - CP: 50
+- Job level 6 - CP: 52
+- Job level 7 - CP: 54
+- Job level 8 - CP: 56
+- Job level 9 - CP: 58
+- Job level 10 - CP: 60
+- Job level 11 - CP: 62
+- Job level 12 - CP: 64
+- Job level 13 - CP: 66
+- Job level 14 - CP: 68
+- Job level 15 - CP: 70
+- Job level 16 - CP: 72
+- Job level 17 - CP: 74
+- Job level 18 - CP: 76
+- Job level 19 - CP: 78
+- Job level 20 - CP: 80
+- Job level 21 - CP: 82
+- Job level 22 - CP: 84
+- Job level 23 - CP: 86
+- Job level 24 - CP: 88
+- Job level 25 - CP: 90
+- Job level 26 - CP: 92
+- Job level 27 - CP: 94
+- Job level 28 - CP: 96
+- Job level 29 - CP: 98
+- Job level 30 - CP: 100
+- Job level 31 - CP: 102
+- Job level 32 - CP: 104
+- Job level 33 - CP: 106
+- Job level 34 - CP: 108
+- Job level 35 - CP: 110
+- Job level 36 - CP: 112
+- Job level 37 - CP: 114
+- Job level 38 - CP: 116
+- Job level 39 - CP: 118
+- Job level 40 - CP: 120
+- Job level 41 - CP: 122
+- Job level 42 - CP: 124
+- Job level 43 - CP: 126
+- Job level 44 - CP: 128
+- Job level 45 - CP: 130
+- Job level 46 - CP: 132
+- Job level 47 - CP: 134
+- Job level 48 - CP: 136
+- Job level 49 - CP: 138
+- Job level 50 - CP: 140
+- Job level 51 - CP: 142
+- Job level 52 - CP: 144
+- Job level 53 - CP: 146
+- Job level 54 - CP: 148
+- Job level 55 - CP: 150
+- Job level 56 - CP: 152
+- Job level 57 - CP: 154
+- Job level 58 - CP: 156
+- Job level 59 - CP: 158
+- Job level 60 - CP: 160
+- Job level 61 - CP: 162
+- Job level 62 - CP: 164
+- Job level 63 - CP: 166
+- Job level 64 - CP: 168
+- Job level 65 - CP: 170
+- Job level 66 - CP: 172
+- Job level 67 - CP: 174
+- Job level 68 - CP: 176
+- Job level 69 - CP: 178
+- Job level 70 - CP: 180
+- Job level 71 - CP: 182
+- Job level 72 - CP: 184
+- Job level 73 - CP: 186
+- Job level 74 - CP: 188
+- Job level 75 - CP: 190
+- Job level 76 - CP: 192
+- Job level 77 - CP: 194
+- Job level 78 - CP: 196
+- Job level 79 - CP: 198
+- Job level 80 - CP: 200
+
+## Class Mage
+- Job level 1 - CP: 42
+- Job level 2 - CP: 44
+- Job level 3 - CP: 46
+- Job level 4 - CP: 48
+- Job level 5 - CP: 50
+- Job level 6 - CP: 52
+- Job level 7 - CP: 54
+- Job level 8 - CP: 56
+- Job level 9 - CP: 58
+- Job level 10 - CP: 60
+- Job level 11 - CP: 62
+- Job level 12 - CP: 64
+- Job level 13 - CP: 66
+- Job level 14 - CP: 68
+- Job level 15 - CP: 70
+- Job level 16 - CP: 72
+- Job level 17 - CP: 74
+- Job level 18 - CP: 76
+- Job level 19 - CP: 78
+- Job level 20 - CP: 80
+- Job level 21 - CP: 82
+- Job level 22 - CP: 84
+- Job level 23 - CP: 86
+- Job level 24 - CP: 88
+- Job level 25 - CP: 90
+- Job level 26 - CP: 92
+- Job level 27 - CP: 94
+- Job level 28 - CP: 96
+- Job level 29 - CP: 98
+- Job level 30 - CP: 100
+- Job level 31 - CP: 102
+- Job level 32 - CP: 104
+- Job level 33 - CP: 106
+- Job level 34 - CP: 108
+- Job level 35 - CP: 110
+- Job level 36 - CP: 112
+- Job level 37 - CP: 114
+- Job level 38 - CP: 116
+- Job level 39 - CP: 118
+- Job level 40 - CP: 120
+- Job level 41 - CP: 122
+- Job level 42 - CP: 124
+- Job level 43 - CP: 126
+- Job level 44 - CP: 128
+- Job level 45 - CP: 130
+- Job level 46 - CP: 132
+- Job level 47 - CP: 134
+- Job level 48 - CP: 136
+- Job level 49 - CP: 138
+- Job level 50 - CP: 140
+- Job level 51 - CP: 142
+- Job level 52 - CP: 144
+- Job level 53 - CP: 146
+- Job level 54 - CP: 148
+- Job level 55 - CP: 150
+- Job level 56 - CP: 152
+- Job level 57 - CP: 154
+- Job level 58 - CP: 156
+- Job level 59 - CP: 158
+- Job level 60 - CP: 160
+- Job level 61 - CP: 162
+- Job level 62 - CP: 164
+- Job level 63 - CP: 166
+- Job level 64 - CP: 168
+- Job level 65 - CP: 170
+- Job level 66 - CP: 172
+- Job level 67 - CP: 174
+- Job level 68 - CP: 176
+- Job level 69 - CP: 178
+- Job level 70 - CP: 180
+- Job level 71 - CP: 182
+- Job level 72 - CP: 184
+- Job level 73 - CP: 186
+- Job level 74 - CP: 188
+- Job level 75 - CP: 190
+- Job level 76 - CP: 192
+- Job level 77 - CP: 194
+- Job level 78 - CP: 196
+- Job level 79 - CP: 198
+- Job level 80 - CP: 200
+
+## Class MartialArtist
+- Job level 1 - CP: 42
+- Job level 2 - CP: 44
+- Job level 3 - CP: 46
+- Job level 4 - CP: 48
+- Job level 5 - CP: 50
+- Job level 6 - CP: 52
+- Job level 7 - CP: 54
+- Job level 8 - CP: 56
+- Job level 9 - CP: 58
+- Job level 10 - CP: 60
+- Job level 11 - CP: 62
+- Job level 12 - CP: 64
+- Job level 13 - CP: 66
+- Job level 14 - CP: 68
+- Job level 15 - CP: 70
+- Job level 16 - CP: 72
+- Job level 17 - CP: 74
+- Job level 18 - CP: 76
+- Job level 19 - CP: 78
+- Job level 20 - CP: 80
+- Job level 21 - CP: 82
+- Job level 22 - CP: 84
+- Job level 23 - CP: 86
+- Job level 24 - CP: 88
+- Job level 25 - CP: 90
+- Job level 26 - CP: 92
+- Job level 27 - CP: 94
+- Job level 28 - CP: 96
+- Job level 29 - CP: 98
+- Job level 30 - CP: 100
+- Job level 31 - CP: 102
+- Job level 32 - CP: 104
+- Job level 33 - CP: 106
+- Job level 34 - CP: 108
+- Job level 35 - CP: 110
+- Job level 36 - CP: 112
+- Job level 37 - CP: 114
+- Job level 38 - CP: 116
+- Job level 39 - CP: 118
+- Job level 40 - CP: 120
+- Job level 41 - CP: 122
+- Job level 42 - CP: 124
+- Job level 43 - CP: 126
+- Job level 44 - CP: 128
+- Job level 45 - CP: 130
+- Job level 46 - CP: 132
+- Job level 47 - CP: 134
+- Job level 48 - CP: 136
+- Job level 49 - CP: 138
+- Job level 50 - CP: 140
+- Job level 51 - CP: 142
+- Job level 52 - CP: 144
+- Job level 53 - CP: 146
+- Job level 54 - CP: 148
+- Job level 55 - CP: 150
+- Job level 56 - CP: 152
+- Job level 57 - CP: 154
+- Job level 58 - CP: 156
+- Job level 59 - CP: 158
+- Job level 60 - CP: 160
+- Job level 61 - CP: 162
+- Job level 62 - CP: 164
+- Job level 63 - CP: 166
+- Job level 64 - CP: 168
+- Job level 65 - CP: 170
+- Job level 66 - CP: 172
+- Job level 67 - CP: 174
+- Job level 68 - CP: 176
+- Job level 69 - CP: 178
+- Job level 70 - CP: 180
+- Job level 71 - CP: 182
+- Job level 72 - CP: 184
+- Job level 73 - CP: 186
+- Job level 74 - CP: 188
+- Job level 75 - CP: 190
+- Job level 76 - CP: 192
+- Job level 77 - CP: 194
+- Job level 78 - CP: 196
+- Job level 79 - CP: 198
+- Job level 80 - CP: 200
diff --git a/src/NosCore.Algorithm/SkillPointService/ISkillPointService.cs b/src/NosCore.Algorithm/SkillPointService/ISkillPointService.cs
new file mode 100644
index 0000000..ae8cbfc
--- /dev/null
+++ b/src/NosCore.Algorithm/SkillPointService/ISkillPointService.cs
@@ -0,0 +1,24 @@
+// __ _ __ __ ___ __ ___ ___
+// | \| |/__\ /' _/ / _//__\| _ \ __|
+// | | ' | \/ |`._`.| \_| \/ | v / _|
+// |_|\__|\__/ |___/ \__/\__/|_|_\___|
+// -----------------------------------
+
+using NosCore.Shared.Enumerations;
+
+namespace NosCore.Algorithm.SkillPointService
+{
+ ///
+ /// Service for the skill points (CP) a character spends on class skills
+ ///
+ public interface ISkillPointService
+ {
+ ///
+ /// Gets the CP a character has before any skill is learned
+ ///
+ /// The character's class
+ /// The character's job level
+ /// The CP pool
+ int GetSkillPoints(CharacterClassType @class, byte jobLevel);
+ }
+}
diff --git a/src/NosCore.Algorithm/SkillPointService/SkillPointService.cs b/src/NosCore.Algorithm/SkillPointService/SkillPointService.cs
new file mode 100644
index 0000000..5a2cb21
--- /dev/null
+++ b/src/NosCore.Algorithm/SkillPointService/SkillPointService.cs
@@ -0,0 +1,27 @@
+// __ _ __ __ ___ __ ___ ___
+// | \| |/__\ /' _/ / _//__\| _ \ __|
+// | | ' | \/ |`._`.| \_| \/ | v / _|
+// |_|\__|\__/ |___/ \__/\__/|_|_\___|
+// -----------------------------------
+
+using NosCore.Shared.Enumerations;
+
+namespace NosCore.Algorithm.SkillPointService
+{
+ ///
+ /// Provides the CP pool: two per job level, plus forty once the character has a class
+ ///
+ public class SkillPointService : ISkillPointService
+ {
+ private const int ClassBonus = 40;
+ private const int PerJobLevel = 2;
+
+ // Login traces of job level 80 characters show 200 CP once their active skills are
+ // paid for, which fits this curve at its cap. No trace below the cap has been checked.
+ ///
+ public int GetSkillPoints(CharacterClassType @class, byte jobLevel)
+ {
+ return (@class == CharacterClassType.Adventurer ? 0 : ClassBonus) + PerJobLevel * jobLevel;
+ }
+ }
+}
diff --git a/test/NosCore.Algorithm.Tests/DocumentationTest.cs b/test/NosCore.Algorithm.Tests/DocumentationTest.cs
index 639bcde..19ccd2f 100644
--- a/test/NosCore.Algorithm.Tests/DocumentationTest.cs
+++ b/test/NosCore.Algorithm.Tests/DocumentationTest.cs
@@ -290,6 +290,26 @@ public void JobExperienceDocumentation()
Approvals.Verify(WriterFactory.CreateTextWriter(resultBuilder.ToString(), "md"));
}
+ [TestMethod]
+ public void SkillPointDocumentation()
+ {
+ var skillPointService = new SkillPointService.SkillPointService();
+
+ var resultBuilder = new StringBuilder("# Skill Point (CP) Table");
+ foreach (var @class in Enum.GetValues(typeof(CharacterClassType)).Cast())
+ {
+ resultBuilder.AppendLine();
+ resultBuilder.AppendLine($"## Class {@class}");
+ for (byte level = 1; level < (@class == CharacterClassType.Adventurer ? 21 : 81); level++)
+ {
+ resultBuilder.AppendLine(
+ $"- Job level {level,2} - CP: {skillPointService.GetSkillPoints(@class, level)}");
+ }
+ }
+
+ Approvals.Verify(WriterFactory.CreateTextWriter(resultBuilder.ToString(), "md"));
+ }
+
[TestMethod]
public void HitDodgeDocumentation()
{