diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5109f3d --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ +.idea/ + +*.user \ No newline at end of file diff --git a/pflib-net.Tests/characters/PlayerCharacterTest.cs b/pflib-net.Tests/characters/PlayerCharacterTest.cs new file mode 100644 index 0000000..0809a84 --- /dev/null +++ b/pflib-net.Tests/characters/PlayerCharacterTest.cs @@ -0,0 +1,47 @@ +using System.Collections.Generic; +using NUnit.Framework; +using pflib_net.characters; +using pflib_net.characters.internals.proficiencies; +using pflib_net.characters.internals.stats; + +namespace pflib_net.Tests.characters; + +[TestFixture] +[TestOf(typeof(PlayerCharacter))] +public class PlayerCharacterTest +{ + + [Test] + public void DamageReducesHp() + { + var stats = new Stats(0, 0, 0, 0, 0, 0); + var armorValues = new Dictionary + { + { ProficiencyType.Unarmored, ProficiencyValue.Expert }, + { ProficiencyType.LightArmor, ProficiencyValue.None }, + { ProficiencyType.MediumArmor, ProficiencyValue.None }, + { ProficiencyType.HeavyArmor, ProficiencyValue.None } + }; + var armorProficiency = new ArmorProficiency(armorValues); + var creature = new PlayerCharacter(50, stats, 5, armorProficiency); + creature.ResolveAttack(20, 20); + Assert.That(creature.GetHp(), Is.LessThan(50)); + } + + [Test] + public void AttackFailsOnMiss() + { + var stats = new Stats(0, 0, 0, 0, 0, 0); + var armorValues = new Dictionary + { + { ProficiencyType.Unarmored, ProficiencyValue.Expert }, + { ProficiencyType.LightArmor, ProficiencyValue.None }, + { ProficiencyType.MediumArmor, ProficiencyValue.None }, + { ProficiencyType.HeavyArmor, ProficiencyValue.None } + }; + var armorProficiency = new ArmorProficiency(armorValues); + var creature = new PlayerCharacter(50, stats, 5, armorProficiency); + creature.ResolveAttack(0, 50); + Assert.That(creature.GetHp(), Is.EqualTo(50)); + } +} \ No newline at end of file diff --git a/pflib-net.Tests/characters/internals/proficiencies/ArmorProficiencyTest.cs b/pflib-net.Tests/characters/internals/proficiencies/ArmorProficiencyTest.cs new file mode 100644 index 0000000..a905a1e --- /dev/null +++ b/pflib-net.Tests/characters/internals/proficiencies/ArmorProficiencyTest.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using NUnit.Framework; +using pflib_net.characters.internals.proficiencies; + +namespace pflib_net.Tests.characters.internals.proficiencies; + +[TestFixture] +[TestOf(typeof(ArmorProficiency))] +public class ArmorProficiencyTest +{ + private Dictionary _values = new() + { + { ProficiencyType.Unarmored, 0}, + { ProficiencyType.LightArmor, 5 }, + { ProficiencyType.MediumArmor, 7 }, + { ProficiencyType.HeavyArmor, 9 } + }; + + private Dictionary _proficiencyValues = + new() + { + { ProficiencyType.Unarmored, ProficiencyValue.None }, + { ProficiencyType.LightArmor, ProficiencyValue.Trained }, + { ProficiencyType.MediumArmor, ProficiencyValue.Expert }, + { ProficiencyType.HeavyArmor, ProficiencyValue.Master } + }; + + [Test] + [TestCase(ProficiencyType.Unarmored)] + [TestCase(ProficiencyType.LightArmor)] + [TestCase(ProficiencyType.MediumArmor)] + [TestCase(ProficiencyType.HeavyArmor)] + public void GetValueReturnsCorrectAc(ProficiencyType type) + { + var armorProficiency = new ArmorProficiency(_proficiencyValues); + Assert.That(armorProficiency.GetValue(type, 3), Is.EqualTo(_values[type])); + } +} \ No newline at end of file diff --git a/pflib-net.Tests/characters/internals/stats/StatsTest.cs b/pflib-net.Tests/characters/internals/stats/StatsTest.cs new file mode 100644 index 0000000..9563d92 --- /dev/null +++ b/pflib-net.Tests/characters/internals/stats/StatsTest.cs @@ -0,0 +1,40 @@ +using System.Collections.Generic; +using NUnit.Framework; +using pflib_net.characters.internals.stats; + +namespace pflib_net.Tests.characters.internals.stats; + +[TestFixture] +[TestOf(typeof(Stats))] +public class StatsTest +{ + private readonly Dictionary _values = new Dictionary() + { + { StatType.Strength, 1 }, + { StatType.Dexterity, 2 }, + { StatType.Constitution, 3 }, + { StatType.Intelligence, 4 }, + { StatType.Wisdom, 5 }, + { StatType.Charisma, 6 } + }; + + [Test] + [TestCase(StatType.Strength)] + [TestCase(StatType.Dexterity)] + [TestCase(StatType.Constitution)] + [TestCase(StatType.Intelligence)] + [TestCase(StatType.Wisdom)] + [TestCase(StatType.Charisma)] + public void GetStatsGivesCorrectValues(StatType type) + { + var stats = new Stats( + _values[StatType.Strength], + _values[StatType.Dexterity], + _values[StatType.Constitution], + _values[StatType.Intelligence], + _values[StatType.Wisdom], + _values[StatType.Charisma] + ); + Assert.That(stats.GetStat(type), Is.EqualTo(_values[type])); + } +} \ No newline at end of file diff --git a/pflib-net.Tests/pflib-net.Tests.csproj b/pflib-net.Tests/pflib-net.Tests.csproj new file mode 100644 index 0000000..c23782f --- /dev/null +++ b/pflib-net.Tests/pflib-net.Tests.csproj @@ -0,0 +1,20 @@ + + + + net6.0 + pflib_net.Tests + + false + + + + + + + + + + + + + \ No newline at end of file diff --git a/pflib-net.sln b/pflib-net.sln new file mode 100644 index 0000000..ca1eeb2 --- /dev/null +++ b/pflib-net.sln @@ -0,0 +1,22 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "pflib-net", "pflib-net\pflib-net.csproj", "{67894E72-B960-45B8-BE46-13B887EDE420}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "pflib-net.Tests", "pflib-net.Tests\pflib-net.Tests.csproj", "{C27B21AF-82FC-4AED-845E-DE27602063E0}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {67894E72-B960-45B8-BE46-13B887EDE420}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {67894E72-B960-45B8-BE46-13B887EDE420}.Debug|Any CPU.Build.0 = Debug|Any CPU + {67894E72-B960-45B8-BE46-13B887EDE420}.Release|Any CPU.ActiveCfg = Release|Any CPU + {67894E72-B960-45B8-BE46-13B887EDE420}.Release|Any CPU.Build.0 = Release|Any CPU + {C27B21AF-82FC-4AED-845E-DE27602063E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C27B21AF-82FC-4AED-845E-DE27602063E0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C27B21AF-82FC-4AED-845E-DE27602063E0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C27B21AF-82FC-4AED-845E-DE27602063E0}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/pflib-net/characters/ICharacter.cs b/pflib-net/characters/ICharacter.cs new file mode 100644 index 0000000..a8fbaf0 --- /dev/null +++ b/pflib-net/characters/ICharacter.cs @@ -0,0 +1,17 @@ +namespace pflib_net.characters; + +public interface ICharacter +{ + /// + /// Resolve an attack against the character's AC and deal damage appropriately + /// + /// + /// + public void ResolveAttack(int roll, int damage); + + /// + /// Return the HP of the character + /// + /// + public int GetHp(); +} \ No newline at end of file diff --git a/pflib-net/characters/PlayerCharacter.cs b/pflib-net/characters/PlayerCharacter.cs new file mode 100644 index 0000000..0c169bf --- /dev/null +++ b/pflib-net/characters/PlayerCharacter.cs @@ -0,0 +1,37 @@ +using System.Collections.Generic; +using pflib_net.characters.internals.proficiencies; +using pflib_net.characters.internals.stats; + +namespace pflib_net.characters; + +public class PlayerCharacter : ICharacter +{ + private int Hp { get; set; } + private Stats Stats { get; set; } + private int Level { get; set; } + private ArmorProficiency ArmorProficiency { get; } + + public PlayerCharacter(int hp, Stats stats, int level, ArmorProficiency armorProficiency) + { + Hp = hp; + Stats = stats; + Level = level; + ArmorProficiency = armorProficiency; + } + + public void ResolveAttack(int roll, int damage) + { + if (roll < GetAc()) return; + Hp -= damage; + } + + private int GetAc() + { + return 10 + Stats.GetStat(StatType.Dexterity) + ArmorProficiency.GetValue(ProficiencyType.Unarmored, Level); + } + + public int GetHp() + { + return Hp; + } +} \ No newline at end of file diff --git a/pflib-net/characters/internals/proficiencies/ArmorProficiency.cs b/pflib-net/characters/internals/proficiencies/ArmorProficiency.cs new file mode 100644 index 0000000..93cf5a2 --- /dev/null +++ b/pflib-net/characters/internals/proficiencies/ArmorProficiency.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; + +namespace pflib_net.characters.internals.proficiencies; + +public class ArmorProficiency : IProficiency +{ + private Dictionary Values { get; set; } + + public ArmorProficiency(Dictionary values) + { + Values = values; + } + + public int GetValue(ProficiencyType type, int level) + { + if (Values[type].Equals(ProficiencyValue.None)) + { + return 0; + } + else + { + return level + (int) Values[type]; + } + } +} \ No newline at end of file diff --git a/pflib-net/characters/internals/proficiencies/IProficiency.cs b/pflib-net/characters/internals/proficiencies/IProficiency.cs new file mode 100644 index 0000000..f6966e9 --- /dev/null +++ b/pflib-net/characters/internals/proficiencies/IProficiency.cs @@ -0,0 +1,6 @@ +namespace pflib_net.characters.internals.proficiencies; + +public interface IProficiency +{ + public int GetValue(ProficiencyType type, int level); +} \ No newline at end of file diff --git a/pflib-net/characters/internals/proficiencies/ProficiencyType.cs b/pflib-net/characters/internals/proficiencies/ProficiencyType.cs new file mode 100644 index 0000000..3de10e6 --- /dev/null +++ b/pflib-net/characters/internals/proficiencies/ProficiencyType.cs @@ -0,0 +1,35 @@ +namespace pflib_net.characters.internals.proficiencies; + +public enum ProficiencyType +{ + Unarmored, + LightArmor, + MediumArmor, + HeavyArmor, + + Unarmed, + SimpleWeapon, + MartialWeapon, + AdvancedWeapon, + + SkillAcrobatics, + SkillArcana, + SkillAthletics, + SkillCrafting, + SkillDeception, + SkillDiplomacy, + SkillIntimidation, + SkillMedicine, + SkillNature, + SkillOccultism, + SkillPerformance, + SkillReligion, + SkillSociety, + SkillStealth, + SkillSurvival, + SkillThievery, + + LoreBardic, + LoreLoremaster, + LoreOther +} \ No newline at end of file diff --git a/pflib-net/characters/internals/proficiencies/ProficiencyValue.cs b/pflib-net/characters/internals/proficiencies/ProficiencyValue.cs new file mode 100644 index 0000000..dba91d4 --- /dev/null +++ b/pflib-net/characters/internals/proficiencies/ProficiencyValue.cs @@ -0,0 +1,10 @@ +namespace pflib_net.characters.internals.proficiencies; + +public enum ProficiencyValue: ushort +{ + None = 0, + Trained = 2, + Expert = 4, + Master = 6, + Legendary = 8 +} \ No newline at end of file diff --git a/pflib-net/characters/internals/stats/StatType.cs b/pflib-net/characters/internals/stats/StatType.cs new file mode 100644 index 0000000..82dbb64 --- /dev/null +++ b/pflib-net/characters/internals/stats/StatType.cs @@ -0,0 +1,11 @@ +namespace pflib_net.characters.internals.stats; + +public enum StatType +{ + Strength, + Dexterity, + Constitution, + Intelligence, + Wisdom, + Charisma +} \ No newline at end of file diff --git a/pflib-net/characters/internals/stats/Stats.cs b/pflib-net/characters/internals/stats/Stats.cs new file mode 100644 index 0000000..1939ad4 --- /dev/null +++ b/pflib-net/characters/internals/stats/Stats.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; + +namespace pflib_net.characters.internals.stats; + +public class Stats +{ + private Dictionary StatDict { get; } = new(); + + public Stats(int strength, int dexterity, int constitution, int intelligence, int wisdom, int charisma) + { + StatDict[StatType.Strength] = strength; + StatDict[StatType.Dexterity] = dexterity; + StatDict[StatType.Constitution] = constitution; + StatDict[StatType.Intelligence] = intelligence; + StatDict[StatType.Wisdom] = wisdom; + StatDict[StatType.Charisma] = charisma; + } + + public int GetStat(StatType type) + { + return StatDict[type]; + } +} \ No newline at end of file diff --git a/pflib-net/pflib-net.csproj b/pflib-net/pflib-net.csproj new file mode 100644 index 0000000..decb98f --- /dev/null +++ b/pflib-net/pflib-net.csproj @@ -0,0 +1,14 @@ + + + + net6.0 + pflib_net + enable + enable + + + + + + +