Add resistances immunities and weaknesses and refactor in preparation for traits (#7)
All checks were successful
MANDATORY: Build project (Pflib Net) TeamCity build finished
All checks were successful
MANDATORY: Build project (Pflib Net) TeamCity build finished
Reviewed-on: #7 Co-authored-by: Malachy Byrne <malachybyrne1@gmail.com> Co-committed-by: Malachy Byrne <malachybyrne1@gmail.com>
This commit is contained in:
parent
cfa39e2a4a
commit
02740cfdf4
@ -3,6 +3,7 @@ using NUnit.Framework;
|
||||
using pflib_net.characters;
|
||||
using pflib_net.characters.internals.proficiencies;
|
||||
using pflib_net.characters.internals.stats;
|
||||
using pflib_net.Tests.damage;
|
||||
|
||||
namespace pflib_net.Tests.characters;
|
||||
|
||||
@ -14,24 +15,85 @@ public class PlayerCharacterTest
|
||||
[Test]
|
||||
public void DamageReducesHp()
|
||||
{
|
||||
var stats = new Stats(0, 0, 0, 0, 0, 0);
|
||||
var armorValues = new Dictionary<ProficiencyType, ProficiencyValue>
|
||||
{
|
||||
{ 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);
|
||||
var creature = CreateCharacter();
|
||||
var damage = DamageUtils.CreateDamage();
|
||||
creature.ResolveAttack(20, damage);
|
||||
Assert.That(creature.GetHp(), Is.LessThan(50));
|
||||
}
|
||||
|
||||
[Test]
|
||||
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>
|
||||
{
|
||||
{ ProficiencyType.Unarmored, ProficiencyValue.Expert },
|
||||
@ -39,9 +101,19 @@ public class PlayerCharacterTest
|
||||
{ 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));
|
||||
armorProficiency ??= new ArmorProficiency(armorValues);
|
||||
immunities ??= new HashSet<string>();
|
||||
resistances ??= new Dictionary<string, int>();
|
||||
weaknesses ??= new Dictionary<string, int>();
|
||||
|
||||
return new PlayerCharacter(
|
||||
hp,
|
||||
stats,
|
||||
level,
|
||||
armorProficiency,
|
||||
immunities,
|
||||
resistances,
|
||||
weaknesses
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -11,28 +11,28 @@ public class DamageTest
|
||||
[Test]
|
||||
public void GetDamageReturnsCorrectRegularAmount()
|
||||
{
|
||||
var damage = new Damage(10, "fire");
|
||||
Assert.That(damage.GetDamage(false), Is.EqualTo(10));
|
||||
var damage = DamageUtils.CreateDamage();
|
||||
Assert.That(damage.GetDamage(false), Is.EqualTo(20));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetDamageReturnsCriticalAmount()
|
||||
{
|
||||
var damage = new Damage(10, "fire", 30);
|
||||
var damage = DamageUtils.CreateDamage(crit: 30);
|
||||
Assert.That(damage.GetDamage(true), Is.EqualTo(30));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CriticalDamageIsDoubleRegular()
|
||||
{
|
||||
var damage = new Damage(10, "fire");
|
||||
Assert.That(damage.GetDamage(true), Is.EqualTo(20));
|
||||
var damage = DamageUtils.CreateDamage();
|
||||
Assert.That(damage.GetDamage(true), Is.EqualTo(40));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void GetDamageTypeReturnsCorrectType()
|
||||
public void GetDamageTypeReturnsCorrectTraits()
|
||||
{
|
||||
var damage = new Damage(0, "fire");
|
||||
Assert.That(damage.GetDamageType(), Is.EqualTo("fire"));
|
||||
var damage = DamageUtils.CreateDamage();
|
||||
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;
|
||||
|
||||
public interface ICharacter
|
||||
@ -7,7 +9,7 @@ public interface ICharacter
|
||||
/// </summary>
|
||||
/// <param name="roll"></param>
|
||||
/// <param name="damage"></param>
|
||||
public void ResolveAttack(int roll, int damage);
|
||||
public void ResolveAttack(int roll, Damage damage);
|
||||
|
||||
/// <summary>
|
||||
/// Return the HP of the character
|
||||
|
||||
@ -1,33 +1,64 @@
|
||||
using System.Collections.Generic;
|
||||
using pflib_net.characters.internals.proficiencies;
|
||||
using pflib_net.characters.internals.stats;
|
||||
using pflib_net.damage;
|
||||
|
||||
namespace pflib_net.characters;
|
||||
|
||||
public class PlayerCharacter : ICharacter
|
||||
{
|
||||
private int Hp { get; set; }
|
||||
private Stats Stats { get; set; }
|
||||
private int Level { get; set; }
|
||||
private Stats Stats { get; }
|
||||
private int Level { 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;
|
||||
Stats = stats;
|
||||
Level = level;
|
||||
ArmorProficiency = armorProficiency;
|
||||
Immunities = immunities;
|
||||
Resistances = resistances;
|
||||
Weaknesses = weaknesses;
|
||||
}
|
||||
|
||||
public void ResolveAttack(int roll, int damage)
|
||||
public void ResolveAttack(int roll, Damage damage)
|
||||
{
|
||||
if (roll < GetAc()) return;
|
||||
Hp -= damage;
|
||||
ApplyDamage(damage, (roll >= GetAc() + 10));
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
@ -2,14 +2,14 @@ namespace pflib_net.damage;
|
||||
|
||||
public class Damage
|
||||
{
|
||||
private int Regular { get; set; }
|
||||
private int Critical { get; set; }
|
||||
private string Type { get; set; }
|
||||
private int Regular { get; }
|
||||
private int Critical { get; }
|
||||
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;
|
||||
Type = type;
|
||||
Traits = traits;
|
||||
if (critical.HasValue)
|
||||
Critical = critical.Value;
|
||||
else
|
||||
@ -21,8 +21,8 @@ public class Damage
|
||||
return crit ? Critical : Regular;
|
||||
}
|
||||
|
||||
public string GetDamageType()
|
||||
public ISet<string> GetTraits()
|
||||
{
|
||||
return Type;
|
||||
return Traits;
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user