Add project files.
This commit is contained in:
32
RPGLibrary.Implementation/AttackCommand.cs
Normal file
32
RPGLibrary.Implementation/AttackCommand.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
using RPGLibrary.Abstraction.Character;
|
||||
using RPGLibrary.Abstraction.Services;
|
||||
using RPGLibrary.Command;
|
||||
|
||||
namespace RPGLibrary.Implementation.Command
|
||||
{
|
||||
public class AttackCommand : ICommand
|
||||
{
|
||||
private readonly Character[] _target;
|
||||
private readonly StrengthAttackData _attackData;
|
||||
private readonly IDamageCalculator<StrengthAttackData> _damageCalculator;
|
||||
|
||||
public AttackCommand(Character[] target, StrengthAttackData attackData, IDamageCalculator<StrengthAttackData> damageCalculator)
|
||||
{
|
||||
_target = target;
|
||||
_attackData = attackData;
|
||||
_damageCalculator = damageCalculator;
|
||||
}
|
||||
|
||||
|
||||
public async IAsyncEnumerable<ICharacter> Execute()
|
||||
{
|
||||
foreach (var character in _target)
|
||||
{
|
||||
var damage = _damageCalculator.Calculate(character, (dynamic)_attackData);
|
||||
var newCurrentHP = new HP(character.HP.Value - damage, character.HP.Maximum);
|
||||
yield return character.With(newCurrentHP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
20
RPGLibrary.Implementation/Attribute/DefenseAttribute.cs
Normal file
20
RPGLibrary.Implementation/Attribute/DefenseAttribute.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using RPGLibrary.Abstraction.Attribute;
|
||||
|
||||
namespace RPGLibrary.Implementation
|
||||
{
|
||||
public class DefenseAttribute : IAttribute
|
||||
{
|
||||
public DefenseAttribute(double value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public double Value { get; }
|
||||
|
||||
public override string? ToString()
|
||||
{
|
||||
return $"Defense: {Value}";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
18
RPGLibrary.Implementation/Attribute/HP.cs
Normal file
18
RPGLibrary.Implementation/Attribute/HP.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using RPGLibrary.Attribute;
|
||||
|
||||
namespace RPGLibrary.Implementation
|
||||
{
|
||||
public class HP : PoolAttribute
|
||||
{
|
||||
public HP(double current, double maximum)
|
||||
: base(current, maximum)
|
||||
{
|
||||
}
|
||||
|
||||
public override string? ToString()
|
||||
{
|
||||
return $"HP: ({Value}/{Maximum})";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
13
RPGLibrary.Implementation/Attribute/Speed.cs
Normal file
13
RPGLibrary.Implementation/Attribute/Speed.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace RPGLibrary.Implementation
|
||||
{
|
||||
public class Speed
|
||||
{
|
||||
public Speed(double value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public double Value { get; }
|
||||
}
|
||||
|
||||
}
|
||||
20
RPGLibrary.Implementation/Attribute/StrengthAttribute.cs
Normal file
20
RPGLibrary.Implementation/Attribute/StrengthAttribute.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using RPGLibrary.Abstraction.Attribute;
|
||||
|
||||
namespace RPGLibrary.Implementation
|
||||
{
|
||||
public class StrengthAttribute : IAttribute
|
||||
{
|
||||
public StrengthAttribute(double value)
|
||||
{
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public double Value { get; }
|
||||
|
||||
public override string? ToString()
|
||||
{
|
||||
return $"Strength: {Value}";
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
31
RPGLibrary.Implementation/Character.cs
Normal file
31
RPGLibrary.Implementation/Character.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using RPGLibrary.Abstraction.Character;
|
||||
|
||||
namespace RPGLibrary.Implementation
|
||||
{
|
||||
public class Character : ICharacter
|
||||
{
|
||||
public Character(string name, HP hp, StrengthAttribute strength, DefenseAttribute defense)
|
||||
{
|
||||
Name = name;
|
||||
HP = hp;
|
||||
Strength = strength;
|
||||
Defense = defense;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
public HP HP { get; private set; }
|
||||
|
||||
public StrengthAttribute Strength { get; }
|
||||
|
||||
public DefenseAttribute Defense { get; }
|
||||
|
||||
public override string? ToString()
|
||||
{
|
||||
return $"{HP}, {Strength}, {Defense}";
|
||||
}
|
||||
|
||||
public ICharacter With(HP hp) => new Character(Name, hp, Strength, Defense);
|
||||
}
|
||||
|
||||
}
|
||||
13
RPGLibrary.Implementation/RPGLibrary.Implementation.csproj
Normal file
13
RPGLibrary.Implementation/RPGLibrary.Implementation.csproj
Normal file
@@ -0,0 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\RPGLibrary\RPGLibrary.Abstraction.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
8
RPGLibrary.Implementation/StrengthAttackData.cs
Normal file
8
RPGLibrary.Implementation/StrengthAttackData.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using RPGLibrary.Abstraction.Character;
|
||||
|
||||
namespace RPGLibrary.Implementation
|
||||
{
|
||||
public record StrengthAttackData(ICharacter Source, StrengthAttribute Strength)
|
||||
: AttackData(Source, Strength);
|
||||
|
||||
}
|
||||
16
RPGLibrary.Implementation/StrengthBasedDamageCalculator.cs
Normal file
16
RPGLibrary.Implementation/StrengthBasedDamageCalculator.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using RPGLibrary.Abstraction.Character;
|
||||
using RPGLibrary.Abstraction.Services;
|
||||
|
||||
namespace RPGLibrary.Implementation
|
||||
{
|
||||
public class StrengthBasedDamageCalculator : IDamageCalculator<StrengthAttackData>
|
||||
{
|
||||
public double Calculate(ICharacter target, StrengthAttackData attackData)
|
||||
=> CalculateInternal((dynamic)target, attackData);
|
||||
|
||||
private double CalculateInternal(Character target, StrengthAttackData attackData)
|
||||
=> Math.Clamp(attackData.Strength.Value - target.Defense.Value, RPGConstants.MinDamage, RPGConstants.MaxDamage);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
6
RPGLibrary.Test/BattleSimulationTest.cs
Normal file
6
RPGLibrary.Test/BattleSimulationTest.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace RPGLibrary.Test
|
||||
{
|
||||
internal class BattleSimulationTest
|
||||
{
|
||||
}
|
||||
}
|
||||
97
RPGLibrary.Test/CommandExecuterTest.cs
Normal file
97
RPGLibrary.Test/CommandExecuterTest.cs
Normal file
@@ -0,0 +1,97 @@
|
||||
using RPGLibrary.Command;
|
||||
using RPGLibrary.Implementation;
|
||||
using RPGLibrary.Implementation.Command;
|
||||
|
||||
namespace RPGLibrary.Test
|
||||
{
|
||||
public class CommandExecuterTest
|
||||
{
|
||||
[Test]
|
||||
public async Task GivenAttackExpectHPIsReduced()
|
||||
{
|
||||
var source = new Character("A", new HP(38, 120), new StrengthAttribute(7), new DefenseAttribute(5));
|
||||
var target = new Character("B", new HP(95, 100), new StrengthAttribute(7), new DefenseAttribute(5));
|
||||
var damageCalculator = new StrengthBasedDamageCalculator();
|
||||
|
||||
var attackData = new StrengthAttackData(source, source.Strength);
|
||||
var command = new AttackCommand(new[] { target }, attackData, damageCalculator);
|
||||
var commands = new[] { command };
|
||||
var results = CommandExecuter.Singleton.Execute(commands, new CancellationToken());
|
||||
var actual = new List<Character>();
|
||||
|
||||
await foreach (var resultOfCommand in results)
|
||||
{
|
||||
await foreach (var resultOfAttack in resultOfCommand)
|
||||
actual.Add((Character)resultOfAttack);
|
||||
|
||||
}
|
||||
|
||||
Assert.That(actual[0].HP.Value, Is.EqualTo(93));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task MultiHitAttackTest()
|
||||
{
|
||||
var source = new Character("A", new HP(100, 100), new StrengthAttribute(10), new DefenseAttribute(4));
|
||||
var target = new[]
|
||||
{
|
||||
new Character("B", new HP(38, 100), new StrengthAttribute(12), new DefenseAttribute(4)),
|
||||
new Character("C", new HP(47, 100), new StrengthAttribute(18), new DefenseAttribute(6)),
|
||||
new Character("D", new HP(99, 100), new StrengthAttribute(11), new DefenseAttribute(2))
|
||||
};
|
||||
|
||||
var damageCalculator = new StrengthBasedDamageCalculator();
|
||||
var attackData = new StrengthAttackData(source, source.Strength);
|
||||
var command = new AttackCommand(target, attackData, damageCalculator);
|
||||
|
||||
var commands = new[] { command };
|
||||
var results = CommandExecuter.Singleton.Execute(commands, new CancellationToken());
|
||||
var actual = new List<Character>();
|
||||
|
||||
await foreach (var resultOfCommand in results)
|
||||
{
|
||||
await foreach (var resultOfAttack in resultOfCommand)
|
||||
actual.Add((Character)resultOfAttack);
|
||||
|
||||
}
|
||||
|
||||
Assert.That(actual[0].HP.Value, Is.EqualTo(32));
|
||||
Assert.That(actual[1].HP.Value, Is.EqualTo(43));
|
||||
Assert.That(actual[2].HP.Value, Is.EqualTo(91));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task QueueAttacks()
|
||||
{
|
||||
var source = new Character("A", new HP(100, 100), new StrengthAttribute(10), new DefenseAttribute(4));
|
||||
var target = new[]
|
||||
{
|
||||
new Character("B", new HP(38, 100), new StrengthAttribute(12), new DefenseAttribute(4)),
|
||||
new Character("C", new HP(47, 100), new StrengthAttribute(18), new DefenseAttribute(6)),
|
||||
new Character("D", new HP(99, 100), new StrengthAttribute(11), new DefenseAttribute(2))
|
||||
};
|
||||
|
||||
var damageCalculator = new StrengthBasedDamageCalculator();
|
||||
var attackData = new StrengthAttackData(source, source.Strength);
|
||||
var commandA = new AttackCommand(target, attackData, damageCalculator);
|
||||
var commandB = new AttackCommand(target, attackData, damageCalculator);
|
||||
var commandC = new AttackCommand(target, attackData, damageCalculator);
|
||||
|
||||
var commands = new[] { commandA, commandB, commandC };
|
||||
var results = CommandExecuter.Singleton.Execute(commands, new CancellationToken());
|
||||
|
||||
await foreach (var resultOfCommand in results)
|
||||
{
|
||||
var characterList = new List<Character>();
|
||||
await foreach (var r in resultOfCommand)
|
||||
{
|
||||
characterList.Add((Character)r);
|
||||
}
|
||||
|
||||
Assert.That(characterList[0].HP.Value, Is.EqualTo(32));
|
||||
Assert.That(characterList[1].HP.Value, Is.EqualTo(43));
|
||||
Assert.That(characterList[2].HP.Value, Is.EqualTo(91));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
23
RPGLibrary.Test/RPGLibrary.Test.csproj
Normal file
23
RPGLibrary.Test/RPGLibrary.Test.csproj
Normal file
@@ -0,0 +1,23 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
|
||||
<PackageReference Include="NUnit" Version="3.13.3" />
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.2.1" />
|
||||
<PackageReference Include="NUnit.Analyzers" Version="3.3.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\RPGLibrary.Implementation\RPGLibrary.Implementation.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
1
RPGLibrary.Test/Usings.cs
Normal file
1
RPGLibrary.Test/Usings.cs
Normal file
@@ -0,0 +1 @@
|
||||
global using NUnit.Framework;
|
||||
37
RPGLibrary.sln
Normal file
37
RPGLibrary.sln
Normal file
@@ -0,0 +1,37 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.2.32526.322
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RPGLibrary.Abstraction", "RPGLibrary\RPGLibrary.Abstraction.csproj", "{E5EBC955-9F1E-4179-BFC4-26D21B85799E}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RPGLibrary.Test", "RPGLibrary.Test\RPGLibrary.Test.csproj", "{B8EDB7DF-0160-4BB7-BA55-DE6FEB858327}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RPGLibrary.Implementation", "RPGLibrary.Implementation\RPGLibrary.Implementation.csproj", "{BCE26FFD-0AF9-4C20-BADC-FE7070F41772}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{E5EBC955-9F1E-4179-BFC4-26D21B85799E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E5EBC955-9F1E-4179-BFC4-26D21B85799E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E5EBC955-9F1E-4179-BFC4-26D21B85799E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E5EBC955-9F1E-4179-BFC4-26D21B85799E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B8EDB7DF-0160-4BB7-BA55-DE6FEB858327}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B8EDB7DF-0160-4BB7-BA55-DE6FEB858327}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B8EDB7DF-0160-4BB7-BA55-DE6FEB858327}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B8EDB7DF-0160-4BB7-BA55-DE6FEB858327}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{BCE26FFD-0AF9-4C20-BADC-FE7070F41772}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{BCE26FFD-0AF9-4C20-BADC-FE7070F41772}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{BCE26FFD-0AF9-4C20-BADC-FE7070F41772}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{BCE26FFD-0AF9-4C20-BADC-FE7070F41772}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {779F151F-ADC1-4D34-8D53-586743ADED93}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
8
RPGLibrary/AttackData/AttackData.cs
Normal file
8
RPGLibrary/AttackData/AttackData.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using RPGLibrary.Abstraction.Attribute;
|
||||
using RPGLibrary.Abstraction.Character;
|
||||
|
||||
namespace RPGLibrary
|
||||
{
|
||||
public abstract record AttackData(ICharacter Source, IAttribute DamageModifier);
|
||||
|
||||
}
|
||||
8
RPGLibrary/Attribute/IAttribute.cs
Normal file
8
RPGLibrary/Attribute/IAttribute.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace RPGLibrary.Abstraction.Attribute
|
||||
{
|
||||
public interface IAttribute
|
||||
{
|
||||
double Value { get; }
|
||||
}
|
||||
|
||||
}
|
||||
18
RPGLibrary/Attribute/PoolAttribute.cs
Normal file
18
RPGLibrary/Attribute/PoolAttribute.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
using RPGLibrary.Abstraction.Attribute;
|
||||
|
||||
namespace RPGLibrary.Attribute
|
||||
{
|
||||
public abstract class PoolAttribute : IAttribute
|
||||
{
|
||||
protected PoolAttribute(double value, double maximum)
|
||||
{
|
||||
Value = value;
|
||||
Maximum = maximum;
|
||||
}
|
||||
|
||||
public double Value { get; protected set; }
|
||||
|
||||
public double Maximum { get; protected set; }
|
||||
}
|
||||
|
||||
}
|
||||
8
RPGLibrary/Character/ICharacter.cs
Normal file
8
RPGLibrary/Character/ICharacter.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace RPGLibrary.Abstraction.Character
|
||||
{
|
||||
public interface ICharacter
|
||||
{
|
||||
string Name { get; }
|
||||
}
|
||||
|
||||
}
|
||||
28
RPGLibrary/Command/CommandExecuter.cs
Normal file
28
RPGLibrary/Command/CommandExecuter.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using RPGLibrary.Abstraction.Character;
|
||||
|
||||
namespace RPGLibrary.Command
|
||||
{
|
||||
|
||||
public sealed class CommandExecuter
|
||||
{
|
||||
private static readonly Lazy<CommandExecuter> _instance = new Lazy<CommandExecuter>(() => new CommandExecuter());
|
||||
|
||||
public static CommandExecuter Singleton => _instance.Value;
|
||||
|
||||
private CommandExecuter() { }
|
||||
|
||||
public async IAsyncEnumerable<IAsyncEnumerable<ICharacter>> Execute(
|
||||
IEnumerable<ICommand> commands,
|
||||
[EnumeratorCancellation] CancellationToken ct)
|
||||
{
|
||||
foreach (var command in commands)
|
||||
{
|
||||
if (ct.IsCancellationRequested)
|
||||
break;
|
||||
yield return command.Execute();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
10
RPGLibrary/Command/ICommand.cs
Normal file
10
RPGLibrary/Command/ICommand.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using RPGLibrary.Abstraction.Character;
|
||||
|
||||
namespace RPGLibrary.Command
|
||||
{
|
||||
public interface ICommand
|
||||
{
|
||||
public IAsyncEnumerable<ICharacter> Execute();
|
||||
}
|
||||
|
||||
}
|
||||
10
RPGLibrary/RPGConstants.cs
Normal file
10
RPGLibrary/RPGConstants.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace RPGLibrary
|
||||
{
|
||||
public struct RPGConstants
|
||||
{
|
||||
public static double MinDamage => 0;
|
||||
|
||||
public static double MaxDamage => 9999;
|
||||
}
|
||||
|
||||
}
|
||||
9
RPGLibrary/RPGLibrary.Abstraction.csproj
Normal file
9
RPGLibrary/RPGLibrary.Abstraction.csproj
Normal file
@@ -0,0 +1,9 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
</Project>
|
||||
11
RPGLibrary/Services/IDamageCalculator.cs
Normal file
11
RPGLibrary/Services/IDamageCalculator.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using RPGLibrary.Abstraction.Character;
|
||||
|
||||
namespace RPGLibrary.Abstraction.Services
|
||||
{
|
||||
public interface IDamageCalculator<TAttackData>
|
||||
where TAttackData : AttackData
|
||||
{
|
||||
double Calculate(ICharacter target, TAttackData attackData);
|
||||
}
|
||||
|
||||
}
|
||||
14
RPGTestGUI/RPGTestGUI.csproj
Normal file
14
RPGTestGUI/RPGTestGUI.csproj
Normal file
@@ -0,0 +1,14 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\RPGLibrary\RPGLibrary.Abstraction.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
Reference in New Issue
Block a user