Add resistances immunities and weaknesses and refactor in preparation for traits
All checks were successful
MANDATORY: Build project (Pflib Net) TeamCity build finished
All checks were successful
MANDATORY: Build project (Pflib Net) TeamCity build finished
This commit is contained in:
parent
cfa39e2a4a
commit
d7eaf4f848
@ -3,6 +3,7 @@ using NUnit.Framework;
|
|||||||
using pflib_net.characters;
|
using pflib_net.characters;
|
||||||
using pflib_net.characters.internals.proficiencies;
|
using pflib_net.characters.internals.proficiencies;
|
||||||
using pflib_net.characters.internals.stats;
|
using pflib_net.characters.internals.stats;
|
||||||
|
using pflib_net.Tests.damage;
|
||||||
|
|
||||||
namespace pflib_net.Tests.characters;
|
namespace pflib_net.Tests.characters;
|
||||||
|
|
||||||
@ -14,24 +15,85 @@ public class PlayerCharacterTest
|
|||||||
[Test]
|
[Test]
|
||||||
public void DamageReducesHp()
|
public void DamageReducesHp()
|
||||||
{
|
{
|
||||||
var stats = new Stats(0, 0, 0, 0, 0, 0);
|
var creature = CreateCharacter();
|
||||||
var armorValues = new Dictionary<ProficiencyType, ProficiencyValue>
|
var damage = DamageUtils.CreateDamage();
|
||||||
{
|
creature.ResolveAttack(20, damage);
|
||||||
{ 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));
|
Assert.That(creature.GetHp(), Is.LessThan(50));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void AttackFailsOnMiss()
|
public void AttackFailsOnMiss()
|
||||||
{
|
{
|
||||||
var stats = new Stats(0, 0, 0, 0, 0, 0);
|
var creature = CreateCharacter();
|
||||||
|
var damage = DamageUtils.CreateDamage();
|
||||||
|
creature.ResolveAttack(0, damage);
|
||||||
|
Assert.That(creature.GetHp(), Is.EqualTo(50));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ImmunitiesApplyNoDamage()
|
||||||
|
{
|
||||||
|
var immunities = new HashSet<string>
|
||||||
|
{
|
||||||
|
"fire"
|
||||||
|
};
|
||||||
|
var creature = CreateCharacter(immunities: immunities);
|
||||||
|
creature.ResolveAttack(1000, DamageUtils.CreateDamage());
|
||||||
|
Assert.That(creature.GetHp(), Is.Positive);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void ResistancesReduceDamage()
|
||||||
|
{
|
||||||
|
var resistances = new Dictionary<string, int>
|
||||||
|
{
|
||||||
|
{ "fire", 5 }
|
||||||
|
};
|
||||||
|
var creature1 = CreateCharacter();
|
||||||
|
creature1.ResolveAttack(20, DamageUtils.CreateDamage());
|
||||||
|
var creature2 = CreateCharacter(resistances: resistances);
|
||||||
|
creature2.ResolveAttack(20, DamageUtils.CreateDamage());
|
||||||
|
Assert.That(creature2.GetHp() - creature1.GetHp(), Is.EqualTo(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void WeaknessIncreaseDamage()
|
||||||
|
{
|
||||||
|
var weaknesses = new Dictionary<string, int>
|
||||||
|
{
|
||||||
|
{ "fire", 5 }
|
||||||
|
};
|
||||||
|
var creature1 = CreateCharacter();
|
||||||
|
creature1.ResolveAttack(20, DamageUtils.CreateDamage());
|
||||||
|
var creature2 = CreateCharacter(weaknesses: weaknesses);
|
||||||
|
creature2.ResolveAttack(20, DamageUtils.CreateDamage());
|
||||||
|
Assert.That(creature1.GetHp() - creature2.GetHp(), Is.EqualTo(5));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void CannotTakeNegativeAmountsOfDamage()
|
||||||
|
{
|
||||||
|
var resistances = new Dictionary<string, int>
|
||||||
|
{
|
||||||
|
{ "fire", 45 }
|
||||||
|
};
|
||||||
|
var creature = CreateCharacter(hp: 20, resistances: resistances);
|
||||||
|
var damage = DamageUtils.CreateDamage();
|
||||||
|
creature.ResolveAttack(20, damage);
|
||||||
|
Assert.That(creature.GetHp(), Is.EqualTo(20));
|
||||||
|
}
|
||||||
|
|
||||||
|
private ICharacter CreateCharacter(
|
||||||
|
int hp = 50,
|
||||||
|
Stats stats = null,
|
||||||
|
int level = 5,
|
||||||
|
ArmorProficiency armorProficiency = null,
|
||||||
|
ISet<string> immunities = null,
|
||||||
|
Dictionary<string, int> resistances = null,
|
||||||
|
Dictionary<string, int> weaknesses = null
|
||||||
|
)
|
||||||
|
{
|
||||||
|
stats ??= new Stats(0, 0, 0, 0, 0, 0);
|
||||||
var armorValues = new Dictionary<ProficiencyType, ProficiencyValue>
|
var armorValues = new Dictionary<ProficiencyType, ProficiencyValue>
|
||||||
{
|
{
|
||||||
{ ProficiencyType.Unarmored, ProficiencyValue.Expert },
|
{ ProficiencyType.Unarmored, ProficiencyValue.Expert },
|
||||||
@ -39,9 +101,19 @@ public class PlayerCharacterTest
|
|||||||
{ ProficiencyType.MediumArmor, ProficiencyValue.None },
|
{ ProficiencyType.MediumArmor, ProficiencyValue.None },
|
||||||
{ ProficiencyType.HeavyArmor, ProficiencyValue.None }
|
{ ProficiencyType.HeavyArmor, ProficiencyValue.None }
|
||||||
};
|
};
|
||||||
var armorProficiency = new ArmorProficiency(armorValues);
|
armorProficiency ??= new ArmorProficiency(armorValues);
|
||||||
var creature = new PlayerCharacter(50, stats, 5, armorProficiency);
|
immunities ??= new HashSet<string>();
|
||||||
creature.ResolveAttack(0, 50);
|
resistances ??= new Dictionary<string, int>();
|
||||||
Assert.That(creature.GetHp(), Is.EqualTo(50));
|
weaknesses ??= new Dictionary<string, int>();
|
||||||
|
|
||||||
|
return new PlayerCharacter(
|
||||||
|
hp,
|
||||||
|
stats,
|
||||||
|
level,
|
||||||
|
armorProficiency,
|
||||||
|
immunities,
|
||||||
|
resistances,
|
||||||
|
weaknesses
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -11,28 +11,28 @@ public class DamageTest
|
|||||||
[Test]
|
[Test]
|
||||||
public void GetDamageReturnsCorrectRegularAmount()
|
public void GetDamageReturnsCorrectRegularAmount()
|
||||||
{
|
{
|
||||||
var damage = new Damage(10, "fire");
|
var damage = DamageUtils.CreateDamage();
|
||||||
Assert.That(damage.GetDamage(false), Is.EqualTo(10));
|
Assert.That(damage.GetDamage(false), Is.EqualTo(20));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void GetDamageReturnsCriticalAmount()
|
public void GetDamageReturnsCriticalAmount()
|
||||||
{
|
{
|
||||||
var damage = new Damage(10, "fire", 30);
|
var damage = DamageUtils.CreateDamage(crit: 30);
|
||||||
Assert.That(damage.GetDamage(true), Is.EqualTo(30));
|
Assert.That(damage.GetDamage(true), Is.EqualTo(30));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void CriticalDamageIsDoubleRegular()
|
public void CriticalDamageIsDoubleRegular()
|
||||||
{
|
{
|
||||||
var damage = new Damage(10, "fire");
|
var damage = DamageUtils.CreateDamage();
|
||||||
Assert.That(damage.GetDamage(true), Is.EqualTo(20));
|
Assert.That(damage.GetDamage(true), Is.EqualTo(40));
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void GetDamageTypeReturnsCorrectType()
|
public void GetDamageTypeReturnsCorrectTraits()
|
||||||
{
|
{
|
||||||
var damage = new Damage(0, "fire");
|
var damage = DamageUtils.CreateDamage();
|
||||||
Assert.That(damage.GetDamageType(), Is.EqualTo("fire"));
|
Assert.That(damage.GetTraits(), Does.Contain("fire"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
20
pflib-net.Tests/damage/DamageUtils.cs
Normal file
20
pflib-net.Tests/damage/DamageUtils.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using pflib_net.damage;
|
||||||
|
|
||||||
|
namespace pflib_net.Tests.damage;
|
||||||
|
|
||||||
|
public static class DamageUtils
|
||||||
|
{
|
||||||
|
public static Damage CreateDamage(
|
||||||
|
int damage = 20,
|
||||||
|
ISet<string> traits = null,
|
||||||
|
int? crit = null
|
||||||
|
)
|
||||||
|
{
|
||||||
|
traits ??= new HashSet<string>
|
||||||
|
{
|
||||||
|
"fire"
|
||||||
|
};
|
||||||
|
return new Damage(damage, traits, crit);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,3 +1,5 @@
|
|||||||
|
using pflib_net.damage;
|
||||||
|
|
||||||
namespace pflib_net.characters;
|
namespace pflib_net.characters;
|
||||||
|
|
||||||
public interface ICharacter
|
public interface ICharacter
|
||||||
@ -7,7 +9,7 @@ public interface ICharacter
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="roll"></param>
|
/// <param name="roll"></param>
|
||||||
/// <param name="damage"></param>
|
/// <param name="damage"></param>
|
||||||
public void ResolveAttack(int roll, int damage);
|
public void ResolveAttack(int roll, Damage damage);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Return the HP of the character
|
/// Return the HP of the character
|
||||||
|
|||||||
@ -1,33 +1,64 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using pflib_net.characters.internals.proficiencies;
|
using pflib_net.characters.internals.proficiencies;
|
||||||
using pflib_net.characters.internals.stats;
|
using pflib_net.characters.internals.stats;
|
||||||
|
using pflib_net.damage;
|
||||||
|
|
||||||
namespace pflib_net.characters;
|
namespace pflib_net.characters;
|
||||||
|
|
||||||
public class PlayerCharacter : ICharacter
|
public class PlayerCharacter : ICharacter
|
||||||
{
|
{
|
||||||
private int Hp { get; set; }
|
private int Hp { get; set; }
|
||||||
private Stats Stats { get; set; }
|
private Stats Stats { get; }
|
||||||
private int Level { get; set; }
|
private int Level { get; }
|
||||||
private ArmorProficiency ArmorProficiency { get; }
|
private ArmorProficiency ArmorProficiency { get; }
|
||||||
|
private ISet<string> Immunities { get; }
|
||||||
|
private Dictionary<string, int> Resistances { get; }
|
||||||
|
private Dictionary<string, int> Weaknesses { get; }
|
||||||
|
|
||||||
public PlayerCharacter(int hp, Stats stats, int level, ArmorProficiency armorProficiency)
|
public PlayerCharacter(
|
||||||
|
int hp,
|
||||||
|
Stats stats,
|
||||||
|
int level,
|
||||||
|
ArmorProficiency armorProficiency,
|
||||||
|
ISet<string> immunities,
|
||||||
|
Dictionary<string, int> resistances,
|
||||||
|
Dictionary<string, int> weaknesses
|
||||||
|
)
|
||||||
{
|
{
|
||||||
Hp = hp;
|
Hp = hp;
|
||||||
Stats = stats;
|
Stats = stats;
|
||||||
Level = level;
|
Level = level;
|
||||||
ArmorProficiency = armorProficiency;
|
ArmorProficiency = armorProficiency;
|
||||||
|
Immunities = immunities;
|
||||||
|
Resistances = resistances;
|
||||||
|
Weaknesses = weaknesses;
|
||||||
}
|
}
|
||||||
|
public void ResolveAttack(int roll, Damage damage)
|
||||||
public void ResolveAttack(int roll, int damage)
|
|
||||||
{
|
{
|
||||||
if (roll < GetAc()) return;
|
if (roll < GetAc()) return;
|
||||||
Hp -= damage;
|
ApplyDamage(damage, (roll >= GetAc() + 10));
|
||||||
}
|
}
|
||||||
|
|
||||||
private int GetAc()
|
private int GetAc()
|
||||||
{
|
{
|
||||||
return 10 + Stats.GetStat(StatType.Dexterity) + ArmorProficiency.GetValue(ProficiencyType.Unarmored, Level);
|
return 10 + Stats.GetStat(StatType.Dexterity) +
|
||||||
|
ArmorProficiency.GetValue(ProficiencyType.Unarmored, Level);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyDamage(Damage damage, bool crit)
|
||||||
|
{
|
||||||
|
var value = damage.GetDamage(crit);
|
||||||
|
if (Immunities.Intersect(damage.GetTraits()).Any())
|
||||||
|
return;
|
||||||
|
foreach (var trait in damage.GetTraits())
|
||||||
|
{
|
||||||
|
if (Weaknesses.TryGetValue(trait, out var weakness))
|
||||||
|
value += weakness;
|
||||||
|
if (Resistances.TryGetValue(trait, out var resistance))
|
||||||
|
value -= resistance;
|
||||||
|
}
|
||||||
|
if (value > 0)
|
||||||
|
Hp -= value;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int GetHp()
|
public int GetHp()
|
||||||
|
|||||||
@ -2,14 +2,14 @@ namespace pflib_net.damage;
|
|||||||
|
|
||||||
public class Damage
|
public class Damage
|
||||||
{
|
{
|
||||||
private int Regular { get; set; }
|
private int Regular { get; }
|
||||||
private int Critical { get; set; }
|
private int Critical { get; }
|
||||||
private string Type { get; set; }
|
private ISet<string> Traits { get; }
|
||||||
|
|
||||||
public Damage(int regular, string type, int? critical = null)
|
public Damage(int regular, ISet<string> traits, int? critical = null)
|
||||||
{
|
{
|
||||||
Regular = regular;
|
Regular = regular;
|
||||||
Type = type;
|
Traits = traits;
|
||||||
if (critical.HasValue)
|
if (critical.HasValue)
|
||||||
Critical = critical.Value;
|
Critical = critical.Value;
|
||||||
else
|
else
|
||||||
@ -21,8 +21,8 @@ public class Damage
|
|||||||
return crit ? Critical : Regular;
|
return crit ? Critical : Regular;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string GetDamageType()
|
public ISet<string> GetTraits()
|
||||||
{
|
{
|
||||||
return Type;
|
return Traits;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user