diff --git a/appveyor.yml b/appveyor.yml index 2575913..1e21d8d 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,4 +1,4 @@ -version: 0.18.{build} +version: 0.19.{build} branches: only: - master @@ -27,7 +27,7 @@ deploy: APPVEYOR_REPO_TAG: true server: api_key: - secure: 0yJPCmw0EEE6ea73EEvaB8+TjIhUs0A8wG0dR60FEAu+B/i6x33b0GweDBVyz0PE + secure: ZBJLq3pN+Q0n1I99/r9rVbdWuCfAiv7bobV9WYDiMR3ebjIx/3d/AVcYK27zC3vm skip_symbols: true symbol_server: artifact: /.*\.nupkg/ diff --git a/src/OpenRpg.Core/Utils/DefaultRandomizer.cs b/src/OpenRpg.Core/Utils/DefaultRandomizer.cs index 2c68fa6..435db65 100644 --- a/src/OpenRpg.Core/Utils/DefaultRandomizer.cs +++ b/src/OpenRpg.Core/Utils/DefaultRandomizer.cs @@ -2,15 +2,15 @@ namespace OpenRpg.Core.Utils { public class DefaultRandomizer : IRandomizer { - private System.Random _random; + public System.Random NativeRandomizer { get; } public DefaultRandomizer(System.Random random) - { _random = random; } + { NativeRandomizer = random; } public int Random(int min, int max) - { return _random.Next(min, max + 1); } + { return NativeRandomizer.Next(min, max + 1); } public float Random(float min, float max) - { return (float)_random.NextDouble() * (max - min) + min; } + { return (float)NativeRandomizer.NextDouble() * (max - min) + min; } } } \ No newline at end of file diff --git a/src/OpenRpg.Core/Utils/IRandomizer.cs b/src/OpenRpg.Core/Utils/IRandomizer.cs index f8b355c..4b14c7e 100644 --- a/src/OpenRpg.Core/Utils/IRandomizer.cs +++ b/src/OpenRpg.Core/Utils/IRandomizer.cs @@ -3,6 +3,6 @@ namespace OpenRpg.Core.Utils public interface IRandomizer { int Random(int min, int max); - float Random(float min, float max); + float Random(float min = 0, float max = 1.0f); } } \ No newline at end of file diff --git a/src/OpenRpg.CurveFunctions/Curves/PassThroughlCurveFunction.cs b/src/OpenRpg.CurveFunctions/Curves/PassThroughCurveFunction.cs similarity index 66% rename from src/OpenRpg.CurveFunctions/Curves/PassThroughlCurveFunction.cs rename to src/OpenRpg.CurveFunctions/Curves/PassThroughCurveFunction.cs index 7f7b392..a3de47d 100644 --- a/src/OpenRpg.CurveFunctions/Curves/PassThroughlCurveFunction.cs +++ b/src/OpenRpg.CurveFunctions/Curves/PassThroughCurveFunction.cs @@ -1,6 +1,6 @@ namespace OpenRpg.CurveFunctions.Curves { - public class PassThroughlCurveFunction : ICurveFunction + public class PassThroughCurveFunction : ICurveFunction { public float Plot(float value) { return value; } diff --git a/src/OpenRpg.CurveFunctions/Extensions/ICurveFunctionExtensions.cs b/src/OpenRpg.CurveFunctions/Extensions/ICurveFunctionExtensions.cs index 0743602..275510f 100644 --- a/src/OpenRpg.CurveFunctions/Extensions/ICurveFunctionExtensions.cs +++ b/src/OpenRpg.CurveFunctions/Extensions/ICurveFunctionExtensions.cs @@ -16,8 +16,7 @@ public static float SanitizeValue(this ICurveFunction curve, float value) /// The denormalized value from the curve public static float ScaledPlot(this ICurveFunction curve, float value, float maxValue) { - if (value == 0) { return 0; } - var normalizedValue = value / maxValue; + var normalizedValue = (value + float.Epsilon) / maxValue; var normalizedOutput = curve.Plot(normalizedValue); return normalizedOutput * maxValue; } diff --git a/src/OpenRpg.CurveFunctions/Extensions/IRandomizerExtensions.cs b/src/OpenRpg.CurveFunctions/Extensions/IRandomizerExtensions.cs new file mode 100644 index 0000000..9c8f230 --- /dev/null +++ b/src/OpenRpg.CurveFunctions/Extensions/IRandomizerExtensions.cs @@ -0,0 +1,31 @@ +using System; +using OpenRpg.Core.Utils; + +namespace OpenRpg.CurveFunctions.Extensions +{ + public static class IRandomizerExtensions + { + public static float Random(this IRandomizer randomizer, ICurveFunction curve) + { + var randomNumber = randomizer.Random(); + return curve.Plot(randomNumber); + } + + public static float Random(this IRandomizer randomizer, ICurveFunction curve, float minValue, float maxValue) + { + var randomNumber = randomizer.Random(minValue, maxValue); + var isNegative = minValue < 0; + if (isNegative) + { + randomNumber = Math.Abs(randomNumber); + maxValue = Math.Abs(minValue); + } + + var result = curve.ScaledPlot(randomNumber, maxValue); + return isNegative ? -result : result; + } + + public static int Random(this IRandomizer randomizer, ICurveFunction curve, int minValue, int maxValue) + { return (int)Random(randomizer, curve, minValue, (float)maxValue); } + } +} \ No newline at end of file diff --git a/src/OpenRpg.CurveFunctions/OpenRpg.CurveFunctions.csproj b/src/OpenRpg.CurveFunctions/OpenRpg.CurveFunctions.csproj index 2cdc0f0..2a9b06b 100644 --- a/src/OpenRpg.CurveFunctions/OpenRpg.CurveFunctions.csproj +++ b/src/OpenRpg.CurveFunctions/OpenRpg.CurveFunctions.csproj @@ -11,4 +11,8 @@ rpg game-development xna monogame unity godot + + + + diff --git a/src/OpenRpg.CurveFunctions/PresetCurves.cs b/src/OpenRpg.CurveFunctions/PresetCurves.cs index a227582..8bbb5c6 100644 --- a/src/OpenRpg.CurveFunctions/PresetCurves.cs +++ b/src/OpenRpg.CurveFunctions/PresetCurves.cs @@ -23,6 +23,6 @@ public class PresetCurves public static SineCurveFunction InverseSineWave = new SineCurveFunction(-1.0f, 0, 0); public static StepCurveFunction GreaterThanHalf = new StepCurveFunction(0.5f); public static StepCurveFunction LessThanHalf = new StepCurveFunction(0.5f, 1.0f, 0.0f); - public static PassThroughlCurveFunction PassThrough = new PassThroughlCurveFunction(); + public static PassThroughCurveFunction PassThrough = new PassThroughCurveFunction(); } } \ No newline at end of file diff --git a/src/OpenRpg.Data.Database/DatabaseDataSource.cs b/src/OpenRpg.Data.Database/DatabaseDataSource.cs new file mode 100644 index 0000000..f90a060 --- /dev/null +++ b/src/OpenRpg.Data.Database/DatabaseDataSource.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using System.Data; +using Dapper; + +namespace OpenRpg.Data.Database +{ + public class DatabaseDataSource : IDatabaseDataSource + { + public IDbConnection Connection { get; } + + public object NativeSource => Connection; + + public DatabaseDataSource(IDbConnection connection) + { Connection = connection; } + + public T Get(object id) => Connection.Get(id); + public IEnumerable GetAll() => Connection.GetList(); + public void Create(T data, object id = null) => Connection.Insert(data); + public void Update(T data, object id) => Connection.Update(data); + public bool Delete(object id) => Connection.Delete(id) > 0; + public bool Exists(object id) => Get(id) != null; + } +} \ No newline at end of file diff --git a/src/OpenRpg.Data.Database/IDatabaseDataSource.cs b/src/OpenRpg.Data.Database/IDatabaseDataSource.cs new file mode 100644 index 0000000..fdd5e7c --- /dev/null +++ b/src/OpenRpg.Data.Database/IDatabaseDataSource.cs @@ -0,0 +1,7 @@ +namespace OpenRpg.Data.Database +{ + public interface IDatabaseDataSource : IDataSource + { + + } +} \ No newline at end of file diff --git a/src/OpenRpg.Data.Database/OpenRpg.Data.Database.csproj b/src/OpenRpg.Data.Database/OpenRpg.Data.Database.csproj new file mode 100644 index 0000000..ad38ce9 --- /dev/null +++ b/src/OpenRpg.Data.Database/OpenRpg.Data.Database.csproj @@ -0,0 +1,24 @@ + + + + 0.0.0 + net461;netstandard2.0 + OpenRpg.Data.Database + Grofit (LP) + https://github.com/openrpg/OpenRpg/blob/master/LICENSE + https://github.com/openrpg/OpenRpg + An SQL database implementation of the OpenRpg data layer using Dapper + rpg game-development repository data xna monogame unity godot + + + + + + + + + + + + + diff --git a/src/OpenRpg.Data.InMemory/Builder/InMemoryDataSourceBuilder.cs b/src/OpenRpg.Data.InMemory/Builder/InMemoryDataSourceBuilder.cs new file mode 100644 index 0000000..db5130d --- /dev/null +++ b/src/OpenRpg.Data.InMemory/Builder/InMemoryDataSourceBuilder.cs @@ -0,0 +1,40 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace OpenRpg.Data.InMemory.Builder +{ + public class InMemoryDataSourceBuilder + { + private readonly Dictionary> Database; + + protected InMemoryDataSourceBuilder(Dictionary> database) + { + Database = database; + } + + public static InMemoryDataSourceBuilder Create() + { return new InMemoryDataSourceBuilder(new Dictionary>()); } + + public InMemoryDataSourceBuilder WithData(IEnumerable data, Func keySelector) + { + var typeOfT = typeof(T); + if (!Database.ContainsKey(typeOfT)) + { + var contents = data.ToDictionary(keySelector, x => (object)x); + Database.Add(typeOfT, contents); + return this; + } + + var typeContainer = Database[typeOfT]; + + foreach(var element in data) + { typeContainer.Add(keySelector(element), element); } + + return this; + } + + public IDataSource Build() + { return new InMemoryDataSource(Database); } + } +} \ No newline at end of file diff --git a/src/OpenRpg.Data.InMemory/IInMemoryDataSource.cs b/src/OpenRpg.Data.InMemory/IInMemoryDataSource.cs new file mode 100644 index 0000000..3638f3d --- /dev/null +++ b/src/OpenRpg.Data.InMemory/IInMemoryDataSource.cs @@ -0,0 +1,7 @@ +namespace OpenRpg.Data.InMemory +{ + public interface IInMemoryDataSource : IDataSource + { + + } +} \ No newline at end of file diff --git a/src/OpenRpg.Data.InMemory/InMemoryDataSource.cs b/src/OpenRpg.Data.InMemory/InMemoryDataSource.cs new file mode 100644 index 0000000..9a8476a --- /dev/null +++ b/src/OpenRpg.Data.InMemory/InMemoryDataSource.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace OpenRpg.Data.InMemory +{ + public class InMemoryDataSource : IInMemoryDataSource + { + public Dictionary> Database { get; } + + public object NativeSource => Database; + + public InMemoryDataSource(Dictionary> database = null) + { Database = database ?? new Dictionary>(); } + + public T Get(object id) => (T)Database[typeof(T)][id]; + public IEnumerable GetAll() => Database[typeof(T)].Values.Cast(); + public void Update(T data, object id) => Database[typeof(T)][id] = data; + public bool Delete(object id) => Database[typeof(T)].Remove(id); + public bool Exists(object id) => Database[typeof(T)].ContainsKey(id); + + public void Create(T data, object id = null) + { + if(id == null) { throw new ArgumentNullException(nameof(id), "In Memory DB Requires explicit keys on creation"); } + Database[typeof(T)].Add(id, data); + } + } +} \ No newline at end of file diff --git a/src/OpenRpg.Data.InMemory/OpenRpg.Data.InMemory.csproj b/src/OpenRpg.Data.InMemory/OpenRpg.Data.InMemory.csproj new file mode 100644 index 0000000..2cf977d --- /dev/null +++ b/src/OpenRpg.Data.InMemory/OpenRpg.Data.InMemory.csproj @@ -0,0 +1,19 @@ + + + + 0.0.0 + netstandard2.0;net46 + OpenRpg.Data.InMemory + Grofit (LP) + https://github.com/openrpg/OpenRpg/blob/master/LICENSE + https://github.com/openrpg/OpenRpg + An in memory implementation of the OpenRpg data layer + rpg game-development repository data xna monogame unity godot + + + + + + + + diff --git a/src/OpenRpg.Data/Conventions/Extensions/RepositoryDataExtensions.cs b/src/OpenRpg.Data/Conventions/Extensions/RepositoryDataExtensions.cs new file mode 100644 index 0000000..1f34705 --- /dev/null +++ b/src/OpenRpg.Data/Conventions/Extensions/RepositoryDataExtensions.cs @@ -0,0 +1,20 @@ +using OpenRpg.Core.Common; +using OpenRpg.Data.Conventions.Queries; + +namespace OpenRpg.Data.Conventions.Extensions +{ + public static class RepositoryDataExtensions + { + public static T Create(this IRepository repository, T entity) where T : class, IHasDataId + { return repository.Query(new CreateEntityQuery(entity, entity.Id)); } + + public static T Update(this IRepository repository, T entity) where T : class, IHasDataId + { return repository.Query(new UpdateEntityQuery(entity, entity.Id)); } + + public static bool Delete(this IRepository repository, T entity) where T : class, IHasDataId + { return repository.Query(new DeleteEntityQuery(entity.Id)); } + + public static bool Exists(this IRepository repository, T entity) where T : class, IHasDataId + { return repository.Query(new EntityExistsQuery(entity.Id)); } + } +} \ No newline at end of file diff --git a/src/OpenRpg.Data/Conventions/Extensions/RepositoryExtensions.cs b/src/OpenRpg.Data/Conventions/Extensions/RepositoryExtensions.cs new file mode 100644 index 0000000..25b7dde --- /dev/null +++ b/src/OpenRpg.Data/Conventions/Extensions/RepositoryExtensions.cs @@ -0,0 +1,22 @@ +using OpenRpg.Data.Conventions.Queries; + +namespace OpenRpg.Data.Conventions.Extensions +{ + public static class RepositoryExtensions + { + public static T Create(this IRepository repository, T entity, object id = null) where T : class + { return repository.Query(new CreateEntityQuery(entity, id)); } + + public static T Get(this IRepository repository, object id) where T : class + { return repository.Query(new GetEntityQuery(id)); } + + public static T Update(this IRepository repository, T entity, object id) where T : class + { return repository.Query(new UpdateEntityQuery(entity, id)); } + + public static bool Delete(this IRepository repository, object id) where T : class + { return repository.Query(new DeleteEntityQuery(id)); } + + public static bool Exists(this IRepository repository, object id) where T : class + { return repository.Query(new EntityExistsQuery(id)); } + } +} \ No newline at end of file diff --git a/src/OpenRpg.Data/Conventions/Queries/CreateEntityQuery.cs b/src/OpenRpg.Data/Conventions/Queries/CreateEntityQuery.cs new file mode 100644 index 0000000..1067527 --- /dev/null +++ b/src/OpenRpg.Data/Conventions/Queries/CreateEntityQuery.cs @@ -0,0 +1,20 @@ +namespace OpenRpg.Data.Conventions.Queries +{ + public class CreateEntityQuery : IQuery + { + public T Entity { get; } + public object Id { get; } + + public CreateEntityQuery(T entity, object id = null) + { + Entity = entity; + Id = id; + } + + public T Execute(IDataSource dataSource) + { + dataSource.Create(Entity, Id); + return Entity; + } + } +} \ No newline at end of file diff --git a/src/OpenRpg.Data/Conventions/Queries/DeleteEntityQuery.cs b/src/OpenRpg.Data/Conventions/Queries/DeleteEntityQuery.cs new file mode 100644 index 0000000..182e52f --- /dev/null +++ b/src/OpenRpg.Data/Conventions/Queries/DeleteEntityQuery.cs @@ -0,0 +1,13 @@ +namespace OpenRpg.Data.Conventions.Queries +{ + public class DeleteEntityQuery : IQuery + { + public object Id { get; } + + public DeleteEntityQuery(object id) + { Id = id; } + + public bool Execute(IDataSource dataSource) + { return dataSource.Delete(Id); } + } +} \ No newline at end of file diff --git a/src/OpenRpg.Data/Conventions/Queries/EntityExistsQuery.cs b/src/OpenRpg.Data/Conventions/Queries/EntityExistsQuery.cs new file mode 100644 index 0000000..fa49b58 --- /dev/null +++ b/src/OpenRpg.Data/Conventions/Queries/EntityExistsQuery.cs @@ -0,0 +1,13 @@ +namespace OpenRpg.Data.Conventions.Queries +{ + public class EntityExistsQuery : IQuery + { + public object Id { get; } + + public EntityExistsQuery(object id) + { Id = id; } + + public bool Execute(IDataSource dataSource) + { return dataSource.Exists(Id); } + } +} \ No newline at end of file diff --git a/src/OpenRpg.Data/Conventions/Queries/GetEntityQuery.cs b/src/OpenRpg.Data/Conventions/Queries/GetEntityQuery.cs new file mode 100644 index 0000000..62d54a5 --- /dev/null +++ b/src/OpenRpg.Data/Conventions/Queries/GetEntityQuery.cs @@ -0,0 +1,13 @@ +namespace OpenRpg.Data.Conventions.Queries +{ + public class GetEntityQuery : IQuery + { + public object Id { get; } + + public GetEntityQuery(object id) + { Id = id; } + + public T Execute(IDataSource dataSource) + { return dataSource.Get(Id); } + } +} \ No newline at end of file diff --git a/src/OpenRpg.Data/Queries/IFindQuery.cs b/src/OpenRpg.Data/Conventions/Queries/IFindQuery.cs similarity index 60% rename from src/OpenRpg.Data/Queries/IFindQuery.cs rename to src/OpenRpg.Data/Conventions/Queries/IFindQuery.cs index 2ae3a2e..15f8f8a 100644 --- a/src/OpenRpg.Data/Queries/IFindQuery.cs +++ b/src/OpenRpg.Data/Conventions/Queries/IFindQuery.cs @@ -1,12 +1,13 @@ -namespace OpenRpg.Data.Queries +using System.Collections.Generic; + +namespace OpenRpg.Data.Conventions.Queries { /// - /// The find query represents a query that finds something specific from the database, it a specific + /// The find query represents a query that finds something specific from the data source, it a specific /// /// Specific Type to return /// This is meant as a way to return types different to the repository type - public interface IFindQuery + public interface IFindQuery : IQuery> { - T Execute(object dataSource); } } \ No newline at end of file diff --git a/src/OpenRpg.Data/Conventions/Queries/UpdateEntityQuery.cs b/src/OpenRpg.Data/Conventions/Queries/UpdateEntityQuery.cs new file mode 100644 index 0000000..1cbca40 --- /dev/null +++ b/src/OpenRpg.Data/Conventions/Queries/UpdateEntityQuery.cs @@ -0,0 +1,20 @@ +namespace OpenRpg.Data.Conventions.Queries +{ + public class UpdateEntityQuery : IQuery + { + public T Entity { get; } + public object Id { get; } + + public UpdateEntityQuery(T entity, object id) + { + Entity = entity; + Id = id; + } + + public T Execute(IDataSource dataSource) + { + dataSource.Update(Entity, Id); + return Entity; + } + } +} \ No newline at end of file diff --git a/src/OpenRpg.Data/DefaultRepository.cs b/src/OpenRpg.Data/DefaultRepository.cs new file mode 100644 index 0000000..72ee5bb --- /dev/null +++ b/src/OpenRpg.Data/DefaultRepository.cs @@ -0,0 +1,12 @@ +namespace OpenRpg.Data +{ + public class DefaultRepository : IRepository + { + public IDataSource DataSource { get; } + + public DefaultRepository(IDataSource dataSource) + { DataSource = dataSource; } + + public T Query(IQuery query) => query.Execute(DataSource); + } +} \ No newline at end of file diff --git a/src/OpenRpg.Data/Defaults/InMemoryDataRepository.cs b/src/OpenRpg.Data/Defaults/InMemoryDataRepository.cs deleted file mode 100644 index f7c8fdc..0000000 --- a/src/OpenRpg.Data/Defaults/InMemoryDataRepository.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Collections.Generic; -using OpenRpg.Core.Common; -using OpenRpg.Data.Repositories; - -namespace OpenRpg.Data.Defaults -{ - /// - /// This is a layer on top of the InMemoryRepository that has awareness of the IHasDataId for Id lookup purposes - /// - /// - public class InMemoryDataRepository : InMemoryRepository, IDataRepository - where T : IHasDataId - { - protected override int GetKeyFromEntity(T entity) => entity.Id; - - public InMemoryDataRepository(IEnumerable data) : base(data) - {} - - protected InMemoryDataRepository() - {} - } -} \ No newline at end of file diff --git a/src/OpenRpg.Data/Defaults/InMemoryRepository.cs b/src/OpenRpg.Data/Defaults/InMemoryRepository.cs deleted file mode 100644 index f3283ba..0000000 --- a/src/OpenRpg.Data/Defaults/InMemoryRepository.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using OpenRpg.Data.Queries; -using OpenRpg.Data.Repositories; - -namespace OpenRpg.Data.Defaults -{ - /// - /// A simple implementation of a repository - /// - /// - /// It is expected that you would provide a populated enumerable to the constructor for most use cases here as the - /// data source, however you can extend it and built up the data internally if required for hard coded scenarios. - /// - /// Entity type - public abstract class InMemoryRepository : IRepository - { - public List Data { get; protected set; } - - public InMemoryRepository(IEnumerable data) - { Data = data.ToList(); } - - protected InMemoryRepository() - { Data = new List(); } - - protected abstract K GetKeyFromEntity(T entity); - - public void Create(T entry) => Data.Add(entry); - public T Retrieve(K id) => Data.SingleOrDefault(x => GetKeyFromEntity(x).Equals(id)); - public void Update(T entry) {} - public void Delete(T entry) => Data.Remove(entry); - - public IEnumerable FindAll(IFindAllQuery dataQuery) => dataQuery.Execute(Data); - public T2 Find(IFindQuery dataQuery) => dataQuery.Execute(Data); - public object Execute(IExecuteQuery query) => query.Execute(Data); - } -} \ No newline at end of file diff --git a/src/OpenRpg.Data/Defaults/Queries/Common/DynamicGetAllQuery.cs b/src/OpenRpg.Data/Defaults/Queries/Common/DynamicGetAllQuery.cs deleted file mode 100644 index 66e968e..0000000 --- a/src/OpenRpg.Data/Defaults/Queries/Common/DynamicGetAllQuery.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace OpenRpg.Data.Defaults.Queries.Common -{ - public class DynamicGetAllQuery : FindAllInMemoryQuery - { - public Func,IEnumerable> Filter { get; } - - public DynamicGetAllQuery(Func,IEnumerable> filter) - { Filter = filter; } - - public override IEnumerable Execute(List dataSource) - { return Filter(dataSource); } - } -} \ No newline at end of file diff --git a/src/OpenRpg.Data/Defaults/Queries/Common/GetAllQuery.cs b/src/OpenRpg.Data/Defaults/Queries/Common/GetAllQuery.cs deleted file mode 100644 index 7487b70..0000000 --- a/src/OpenRpg.Data/Defaults/Queries/Common/GetAllQuery.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; - -namespace OpenRpg.Data.Defaults.Queries.Common -{ - public class GetAllQuery : FindAllInMemoryQuery - { - public override IEnumerable Execute(List dataSource) - { return dataSource; } - } -} \ No newline at end of file diff --git a/src/OpenRpg.Data/Defaults/Queries/Common/GetCountQuery.cs b/src/OpenRpg.Data/Defaults/Queries/Common/GetCountQuery.cs deleted file mode 100644 index f03eb6a..0000000 --- a/src/OpenRpg.Data/Defaults/Queries/Common/GetCountQuery.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; - -namespace OpenRpg.Data.Defaults.Queries.Common -{ - public class GetCountQuery : FindInMemoryQuery - { - public override int Execute(List dataSource) - { return dataSource.Count; } - } -} \ No newline at end of file diff --git a/src/OpenRpg.Data/Defaults/Queries/ExecuteInMemoryQuery.cs b/src/OpenRpg.Data/Defaults/Queries/ExecuteInMemoryQuery.cs deleted file mode 100644 index 2383763..0000000 --- a/src/OpenRpg.Data/Defaults/Queries/ExecuteInMemoryQuery.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.Generic; -using OpenRpg.Data.Queries; - -namespace OpenRpg.Data.Defaults.Queries -{ - public abstract class ExecuteInMemoryQuery : IExecuteQuery - { - public object Execute(object dataSource) - { return Execute((List) dataSource); } - - public abstract object Execute(List dataSource); - } -} \ No newline at end of file diff --git a/src/OpenRpg.Data/Defaults/Queries/FindAllInMemoryQuery.cs b/src/OpenRpg.Data/Defaults/Queries/FindAllInMemoryQuery.cs deleted file mode 100644 index f821fa7..0000000 --- a/src/OpenRpg.Data/Defaults/Queries/FindAllInMemoryQuery.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.Generic; -using OpenRpg.Data.Queries; - -namespace OpenRpg.Data.Defaults.Queries -{ - public abstract class FindAllInMemoryQuery : IFindAllQuery - { - public IEnumerable Execute(object dataSource) - { return Execute((List) dataSource); } - - public abstract IEnumerable Execute(List dataSource); - } -} \ No newline at end of file diff --git a/src/OpenRpg.Data/Defaults/Queries/FindInMemoryQuery.cs b/src/OpenRpg.Data/Defaults/Queries/FindInMemoryQuery.cs deleted file mode 100644 index a883588..0000000 --- a/src/OpenRpg.Data/Defaults/Queries/FindInMemoryQuery.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.Generic; -using OpenRpg.Data.Queries; - -namespace OpenRpg.Data.Defaults.Queries -{ - public abstract class FindInMemoryQuery : IFindQuery - { - public T Execute(object dataSource) - { return Execute((List) dataSource); } - - public abstract T Execute(List dataSource); - } -} \ No newline at end of file diff --git a/src/OpenRpg.Data/IDataSource.cs b/src/OpenRpg.Data/IDataSource.cs new file mode 100644 index 0000000..33dc99c --- /dev/null +++ b/src/OpenRpg.Data/IDataSource.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace OpenRpg.Data +{ + public interface IDataSource + { + object NativeSource { get; } + + T Get(object id); + IEnumerable GetAll(); + void Create(T data, object id = null); + void Update(T data, object id); + bool Delete(object id); + bool Exists(object id); + } +} \ No newline at end of file diff --git a/src/OpenRpg.Data/IQuery.cs b/src/OpenRpg.Data/IQuery.cs new file mode 100644 index 0000000..8279544 --- /dev/null +++ b/src/OpenRpg.Data/IQuery.cs @@ -0,0 +1,7 @@ +namespace OpenRpg.Data +{ + public interface IQuery + { + T Execute(IDataSource dataSource); + } +} \ No newline at end of file diff --git a/src/OpenRpg.Data/IRepository.cs b/src/OpenRpg.Data/IRepository.cs new file mode 100644 index 0000000..7a1542b --- /dev/null +++ b/src/OpenRpg.Data/IRepository.cs @@ -0,0 +1,9 @@ +namespace OpenRpg.Data +{ + public interface IRepository + { + IDataSource DataSource { get; } + + T Query(IQuery query); + } +} \ No newline at end of file diff --git a/src/OpenRpg.Data/Queries/IExecuteQuery.cs b/src/OpenRpg.Data/Queries/IExecuteQuery.cs deleted file mode 100644 index d771c2a..0000000 --- a/src/OpenRpg.Data/Queries/IExecuteQuery.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace OpenRpg.Data.Queries -{ - /// - /// Execute queries are for altering data in the repository such as mass updates or deletions - /// - public interface IExecuteQuery - { - object Execute(object dataSource); - } -} \ No newline at end of file diff --git a/src/OpenRpg.Data/Queries/IFindAllQuery.cs b/src/OpenRpg.Data/Queries/IFindAllQuery.cs deleted file mode 100644 index 07297a1..0000000 --- a/src/OpenRpg.Data/Queries/IFindAllQuery.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; - -namespace OpenRpg.Data.Queries -{ - /// - /// The FindAll Query is meant to represent a way to get a list of results back from the repository - /// - /// The data type to return - public interface IFindAllQuery : IFindQuery> - { } -} \ No newline at end of file diff --git a/src/OpenRpg.Data/Repositories/IDataRepository.cs b/src/OpenRpg.Data/Repositories/IDataRepository.cs deleted file mode 100644 index a080818..0000000 --- a/src/OpenRpg.Data/Repositories/IDataRepository.cs +++ /dev/null @@ -1,7 +0,0 @@ -using OpenRpg.Core.Common; - -namespace OpenRpg.Data.Repositories -{ - public interface IDataRepository : IReadDataRepository, IWriteDataRepository where T : IHasDataId - {} -} \ No newline at end of file diff --git a/src/OpenRpg.Data/Repositories/IReadDataRepository.cs b/src/OpenRpg.Data/Repositories/IReadDataRepository.cs deleted file mode 100644 index fb4f638..0000000 --- a/src/OpenRpg.Data/Repositories/IReadDataRepository.cs +++ /dev/null @@ -1,7 +0,0 @@ -using OpenRpg.Core.Common; - -namespace OpenRpg.Data.Repositories -{ - public interface IReadDataRepository : IReadRepository where T : IHasDataId - {} -} \ No newline at end of file diff --git a/src/OpenRpg.Data/Repositories/IReadRepository.cs b/src/OpenRpg.Data/Repositories/IReadRepository.cs deleted file mode 100644 index f57c518..0000000 --- a/src/OpenRpg.Data/Repositories/IReadRepository.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Collections.Generic; -using OpenRpg.Data.Queries; - -namespace OpenRpg.Data.Repositories -{ - public interface IReadRepository - { - T Retrieve(K id); - - IEnumerable FindAll(IFindAllQuery query); - T2 Find(IFindQuery query); - } -} \ No newline at end of file diff --git a/src/OpenRpg.Data/Repositories/IRepository.cs b/src/OpenRpg.Data/Repositories/IRepository.cs deleted file mode 100644 index e24cd0b..0000000 --- a/src/OpenRpg.Data/Repositories/IRepository.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace OpenRpg.Data.Repositories -{ - public interface IRepository : IReadRepository, IWriteRepository - {} -} \ No newline at end of file diff --git a/src/OpenRpg.Data/Repositories/IWriteDataRepository.cs b/src/OpenRpg.Data/Repositories/IWriteDataRepository.cs deleted file mode 100644 index b6b8a8e..0000000 --- a/src/OpenRpg.Data/Repositories/IWriteDataRepository.cs +++ /dev/null @@ -1,7 +0,0 @@ -using OpenRpg.Core.Common; - -namespace OpenRpg.Data.Repositories -{ - public interface IWriteDataRepository : IWriteRepository where T : IHasDataId - {} -} \ No newline at end of file diff --git a/src/OpenRpg.Data/Repositories/IWriteRepository.cs b/src/OpenRpg.Data/Repositories/IWriteRepository.cs deleted file mode 100644 index 04879c7..0000000 --- a/src/OpenRpg.Data/Repositories/IWriteRepository.cs +++ /dev/null @@ -1,13 +0,0 @@ -using OpenRpg.Data.Queries; - -namespace OpenRpg.Data.Repositories -{ - public interface IWriteRepository - { - object Execute(IExecuteQuery query); - - void Create(T entry); - void Update(T entry); - void Delete(T entry); - } -} \ No newline at end of file diff --git a/src/OpenRpg.Localization/Data/DataSources/ILocaleDataSource.cs b/src/OpenRpg.Localization/Data/DataSources/ILocaleDataSource.cs new file mode 100644 index 0000000..67fbbb9 --- /dev/null +++ b/src/OpenRpg.Localization/Data/DataSources/ILocaleDataSource.cs @@ -0,0 +1,12 @@ +namespace OpenRpg.Localization.Data.DataSources +{ + public interface ILocaleDataSource + { + object NativeSource { get; } + string Get(string localeCode, string key); + void Create(string localeCode, string text, string key); + void Update(string localeCode, string text, string key); + bool Delete(string localeCode, string key); + bool Exists(string localeCode, string key); + } +} \ No newline at end of file diff --git a/src/OpenRpg.Localization/Data/DataSources/InMemoryLocaleDataSource.cs b/src/OpenRpg.Localization/Data/DataSources/InMemoryLocaleDataSource.cs new file mode 100644 index 0000000..6239de2 --- /dev/null +++ b/src/OpenRpg.Localization/Data/DataSources/InMemoryLocaleDataSource.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using System.Linq; + +namespace OpenRpg.Localization.Data.DataSources +{ + public class InMemoryLocaleDataSource : ILocaleDataSource + { + public Dictionary LocaleDatasets { get; } + public object NativeSource => LocaleDatasets; + + public InMemoryLocaleDataSource(IEnumerable localeDatasets = null) + { + LocaleDatasets = localeDatasets?.ToDictionary(x => x.LocaleCode, x => x) ?? + new Dictionary(); + } + + public string Get(string localeCode, string key) => LocaleDatasets[localeCode].LocaleData[key]; + public void Create(string localeCode, string text, string key) => LocaleDatasets[localeCode].LocaleData.Add(key, text); + public void Update(string localeCode, string text, string key) => LocaleDatasets[localeCode].LocaleData[key] = text; + public bool Delete(string localeCode, string key) => LocaleDatasets[localeCode].LocaleData.Remove(key); + public bool Exists(string localeCode, string key) => LocaleDatasets[localeCode].LocaleData.ContainsKey(key); + } +} \ No newline at end of file diff --git a/src/OpenRpg.Localization/Data/Extensions/RepositoryExtensions.cs b/src/OpenRpg.Localization/Data/Extensions/RepositoryExtensions.cs new file mode 100644 index 0000000..a33b996 --- /dev/null +++ b/src/OpenRpg.Localization/Data/Extensions/RepositoryExtensions.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using OpenRpg.Localization.Data.Queries.Conventions; +using OpenRpg.Localization.Data.Repositories; + +namespace OpenRpg.Localization.Data.Extensions +{ + public static class LocaleRepositoryExtensions + { + public static string Create(this ILocaleRepository repository, string text, string id) + { return repository.Query(new CreateLocaleQuery(text, id)); } + + public static string Get(this ILocaleRepository repository, string id) + { return repository.Query(new GetLocaleQuery(id)); } + + public static IEnumerable GetAll(this ILocaleRepository repository, params string[] ids) + { return repository.Query(new GetAllLocalesQuery(ids)); } + + public static string Update(this ILocaleRepository repository, string text, string id) + { return repository.Query(new UpdateLocaleQuery(text, id)); } + + public static bool Delete(this ILocaleRepository repository, string id) + { return repository.Query(new DeleteLocaleQuery(id)); } + + public static bool Exists(this ILocaleRepository repository, string id) + { return repository.Query(new LocaleExistsQuery(id)); } + } +} \ No newline at end of file diff --git a/src/OpenRpg.Localization/Data/Queries/Conventions/CreateLocaleQuery.cs b/src/OpenRpg.Localization/Data/Queries/Conventions/CreateLocaleQuery.cs new file mode 100644 index 0000000..ce2716b --- /dev/null +++ b/src/OpenRpg.Localization/Data/Queries/Conventions/CreateLocaleQuery.cs @@ -0,0 +1,22 @@ +using OpenRpg.Localization.Data.DataSources; + +namespace OpenRpg.Localization.Data.Queries.Conventions +{ + public class CreateLocaleQuery : ILocaleQuery + { + public string Text { get; } + public string Id { get; } + + public CreateLocaleQuery(string text, string id) + { + Text = text; + Id = id; + } + + public string Execute(string localeCode, ILocaleDataSource dataSource) + { + dataSource.Create(localeCode, Text, Id); + return Id; + } + } +} \ No newline at end of file diff --git a/src/OpenRpg.Localization/Data/Queries/Conventions/DeleteLocaleQuery.cs b/src/OpenRpg.Localization/Data/Queries/Conventions/DeleteLocaleQuery.cs new file mode 100644 index 0000000..8792d69 --- /dev/null +++ b/src/OpenRpg.Localization/Data/Queries/Conventions/DeleteLocaleQuery.cs @@ -0,0 +1,18 @@ +using OpenRpg.Localization.Data.DataSources; + +namespace OpenRpg.Localization.Data.Queries.Conventions +{ + public class DeleteLocaleQuery : ILocaleQuery + { + public string Id { get; } + + public DeleteLocaleQuery(string id) + { Id = id; } + + public bool Execute(string localeCode, ILocaleDataSource dataSource) + { + dataSource.Delete(localeCode, Id); + return true; + } + } +} \ No newline at end of file diff --git a/src/OpenRpg.Localization/Data/Queries/Conventions/GetAllLocalesQuery.cs b/src/OpenRpg.Localization/Data/Queries/Conventions/GetAllLocalesQuery.cs new file mode 100644 index 0000000..291868d --- /dev/null +++ b/src/OpenRpg.Localization/Data/Queries/Conventions/GetAllLocalesQuery.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using System.Linq; +using OpenRpg.Localization.Data.DataSources; + +namespace OpenRpg.Localization.Data.Queries.Conventions +{ + public class GetAllLocalesQuery : ILocaleQuery> + { + public string[] Ids { get; } + + public GetAllLocalesQuery(params string[] ids) + { Ids = ids; } + + public IEnumerable Execute(string locale, ILocaleDataSource dataSource) + { return Ids.Select(x => dataSource.Get(locale, x)); } + } +} \ No newline at end of file diff --git a/src/OpenRpg.Localization/Data/Queries/Conventions/GetLocaleQuery.cs b/src/OpenRpg.Localization/Data/Queries/Conventions/GetLocaleQuery.cs new file mode 100644 index 0000000..aebd983 --- /dev/null +++ b/src/OpenRpg.Localization/Data/Queries/Conventions/GetLocaleQuery.cs @@ -0,0 +1,15 @@ +using OpenRpg.Localization.Data.DataSources; + +namespace OpenRpg.Localization.Data.Queries.Conventions +{ + public class GetLocaleQuery : ILocaleQuery + { + public string Id { get; } + + public GetLocaleQuery(string id) + { Id = id; } + + public string Execute(string locale, ILocaleDataSource dataSource) + { return dataSource.Get(locale, Id); } + } +} \ No newline at end of file diff --git a/src/OpenRpg.Localization/Data/Queries/Conventions/LocaleExistsQuery.cs b/src/OpenRpg.Localization/Data/Queries/Conventions/LocaleExistsQuery.cs new file mode 100644 index 0000000..015d635 --- /dev/null +++ b/src/OpenRpg.Localization/Data/Queries/Conventions/LocaleExistsQuery.cs @@ -0,0 +1,15 @@ +using OpenRpg.Localization.Data.DataSources; + +namespace OpenRpg.Localization.Data.Queries.Conventions +{ + public class LocaleExistsQuery : ILocaleQuery + { + public string Id { get; } + + public LocaleExistsQuery(string id) + { Id = id; } + + public bool Execute(string locale, ILocaleDataSource dataSource) + { return dataSource.Exists(locale, Id); } + } +} \ No newline at end of file diff --git a/src/OpenRpg.Localization/Data/Queries/Conventions/UpdateLocaleQuery.cs b/src/OpenRpg.Localization/Data/Queries/Conventions/UpdateLocaleQuery.cs new file mode 100644 index 0000000..77531b9 --- /dev/null +++ b/src/OpenRpg.Localization/Data/Queries/Conventions/UpdateLocaleQuery.cs @@ -0,0 +1,22 @@ +using OpenRpg.Localization.Data.DataSources; + +namespace OpenRpg.Localization.Data.Queries.Conventions +{ + public class UpdateLocaleQuery : ILocaleQuery + { + public string Text { get; } + public string Key { get; } + + public UpdateLocaleQuery(string text, string key) + { + Text = text; + Key = key; + } + + public string Execute(string locale, ILocaleDataSource dataSource) + { + dataSource.Update(locale, Text, Key); + return Key; + } + } +} \ No newline at end of file diff --git a/src/OpenRpg.Localization/Data/Queries/ILocaleQuery.cs b/src/OpenRpg.Localization/Data/Queries/ILocaleQuery.cs new file mode 100644 index 0000000..7bf82e2 --- /dev/null +++ b/src/OpenRpg.Localization/Data/Queries/ILocaleQuery.cs @@ -0,0 +1,9 @@ +using OpenRpg.Localization.Data.DataSources; + +namespace OpenRpg.Localization.Data.Queries +{ + public interface ILocaleQuery + { + T Execute(string locale, ILocaleDataSource dataSource); + } +} \ No newline at end of file diff --git a/src/OpenRpg.Localization/Data/Repositories/ILocaleRepository.cs b/src/OpenRpg.Localization/Data/Repositories/ILocaleRepository.cs new file mode 100644 index 0000000..8d1b702 --- /dev/null +++ b/src/OpenRpg.Localization/Data/Repositories/ILocaleRepository.cs @@ -0,0 +1,15 @@ +using OpenRpg.Localization.Data.DataSources; +using OpenRpg.Localization.Data.Queries; + +namespace OpenRpg.Localization.Data.Repositories +{ + public interface ILocaleRepository + { + string CurrentLocaleCode { get; } + void ChangeLocale(string localeCode); + + ILocaleDataSource DataSource { get; } + + T Query(ILocaleQuery query); + } +} \ No newline at end of file diff --git a/src/OpenRpg.Localization/Data/Repositories/LocaleRepository.cs b/src/OpenRpg.Localization/Data/Repositories/LocaleRepository.cs new file mode 100644 index 0000000..7ba6ebb --- /dev/null +++ b/src/OpenRpg.Localization/Data/Repositories/LocaleRepository.cs @@ -0,0 +1,22 @@ +using OpenRpg.Localization.Data.DataSources; +using OpenRpg.Localization.Data.Queries; + +namespace OpenRpg.Localization.Data.Repositories +{ + public class LocaleRepository : ILocaleRepository + { + public string CurrentLocaleCode { get; private set; } + public ILocaleDataSource DataSource { get; } + + public LocaleRepository(ILocaleDataSource dataSource, string localeCodeToUse) + { + DataSource = dataSource; + CurrentLocaleCode = localeCodeToUse; + } + + public void ChangeLocale(string localeCode) + { CurrentLocaleCode = localeCode; } + + public T Query(ILocaleQuery query) => query.Execute(CurrentLocaleCode, DataSource); + } +} \ No newline at end of file diff --git a/src/OpenRpg.Localization/Queries/GetBatchLocalesQuery.cs b/src/OpenRpg.Localization/Queries/GetBatchLocalesQuery.cs deleted file mode 100644 index abcaabe..0000000 --- a/src/OpenRpg.Localization/Queries/GetBatchLocalesQuery.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Collections.Generic; -using OpenRpg.Data.Queries; - -namespace OpenRpg.Localization.Queries -{ - public class GetBatchLocalesQuery : IFindAllQuery - { - public IReadOnlyList Ids { get;} - - public GetBatchLocalesQuery(IReadOnlyList ids) - { Ids = ids; } - - public IEnumerable Execute(object dataSource) - { - var localeStore = dataSource as LocaleDataset; - - for (var i = 0; i < Ids.Count; i++) - { yield return localeStore.LocaleData[Ids[i]]; } - } - } -} \ No newline at end of file diff --git a/src/OpenRpg.Localization/Repositories/ILocaleRepository.cs b/src/OpenRpg.Localization/Repositories/ILocaleRepository.cs deleted file mode 100644 index 29aeb40..0000000 --- a/src/OpenRpg.Localization/Repositories/ILocaleRepository.cs +++ /dev/null @@ -1,15 +0,0 @@ -using OpenRpg.Data.Repositories; - -namespace OpenRpg.Localization.Repositories -{ - public interface ILocaleRepository : IReadRepository - { - string LocaleCode { get; } - - void ChangeLocale(LocaleDataset dataset); - bool Has(string id); - void Create(string id, string text); - void Update(string id, string text); - void Delete(string id); - } -} \ No newline at end of file diff --git a/src/OpenRpg.Localization/Repositories/LocaleRepository.cs b/src/OpenRpg.Localization/Repositories/LocaleRepository.cs deleted file mode 100644 index 46108ac..0000000 --- a/src/OpenRpg.Localization/Repositories/LocaleRepository.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Collections.Generic; -using OpenRpg.Data.Queries; - -namespace OpenRpg.Localization.Repositories -{ - public class LocaleRepository : ILocaleRepository - { - public string LocaleCode => LocaleDataset.LocaleCode; - public LocaleDataset LocaleDataset { get; protected set; } - - public LocaleRepository(LocaleDataset localeDataset) - { LocaleDataset = localeDataset; } - - public string Retrieve(string id) - { return LocaleDataset.LocaleData[id]; } - - public void ChangeLocale(LocaleDataset dataset) - { LocaleDataset = dataset; } - - public bool Has(string id) - { return LocaleDataset.LocaleData.ContainsKey(id); } - - public IEnumerable FindAll(IFindAllQuery query) - { return query.Execute(LocaleDataset); } - - public T2 Find(IFindQuery query) - { return query.Execute(LocaleDataset); } - - public void Create(string id, string text) - { LocaleDataset.LocaleData.Add(id, text); } - - public void Update(string id, string text) - { LocaleDataset.LocaleData[id] = text; } - - public void Delete(string id) - { LocaleDataset.LocaleData.Remove(id); } - } -} \ No newline at end of file diff --git a/src/OpenRpg.UnitTests/Framework/DefaultKeyedVariablesTests.cs b/src/OpenRpg.UnitTests/Framework/DefaultKeyedVariablesTests.cs new file mode 100644 index 0000000..350e2cc --- /dev/null +++ b/src/OpenRpg.UnitTests/Framework/DefaultKeyedVariablesTests.cs @@ -0,0 +1,93 @@ +using System.Collections.Generic; +using OpenRpg.Core.Variables; +using Xunit; + +namespace OpenRpg.UnitTests.Framework; + +public class DefaultKeyedVariablesTests +{ + [Fact] + public void should_correctly_raise_add_event_on_adding_variables() + { + var expectedArgs = new VariableEventArgs(1, default, 10); + VariableEventArgs actualArgs = null; + + var variables = new DefaultKeyedVariables(); + variables.OnAdded += (sender, args) => actualArgs = args; + + variables.AddVariable(expectedArgs.VariableType, expectedArgs.NewValue); + + Assert.NotNull(actualArgs); + Assert.Equal(expectedArgs.VariableType, actualArgs.VariableType); + Assert.Equal(expectedArgs.OldValue, actualArgs.OldValue); + Assert.Equal(expectedArgs.NewValue, actualArgs.NewValue); + + Assert.Equal(expectedArgs.NewValue, variables.InternalVariables[expectedArgs.VariableType]); + } + + [Fact] + public void should_correctly_raise_change_event_on_updating_variables() + { + var expectedArgs = new VariableEventArgs(1, 10, 100); + VariableEventArgs actualArgs = null; + + var variables = new DefaultKeyedVariables(new Dictionary { {1, 10} }); + variables.OnChanged += (sender, args) => actualArgs = args; + + variables[expectedArgs.VariableType] = expectedArgs.NewValue; + + Assert.NotNull(actualArgs); + Assert.Equal(expectedArgs.VariableType, actualArgs.VariableType); + Assert.Equal(expectedArgs.OldValue, actualArgs.OldValue); + Assert.Equal(expectedArgs.NewValue, actualArgs.NewValue); + + Assert.Equal(expectedArgs.NewValue, variables.InternalVariables[expectedArgs.VariableType]); + } + + [Fact] + public void should_correctly_raise_remove_event_on_removing_variables() + { + var expectedArgs = new VariableEventArgs(1, 10, default); + VariableEventArgs actualArgs = null; + + var variables = new DefaultKeyedVariables(new Dictionary { {1, 10} }); + variables.OnRemoved += (sender, args) => actualArgs = args; + + variables.Remove(expectedArgs.VariableType); + + Assert.NotNull(actualArgs); + Assert.Equal(expectedArgs.VariableType, actualArgs.VariableType); + Assert.Equal(expectedArgs.OldValue, actualArgs.OldValue); + Assert.Equal(expectedArgs.NewValue, actualArgs.NewValue); + + Assert.False(variables.InternalVariables.ContainsKey(expectedArgs.VariableType)); + } + + [Fact] + public void should_do_insert_on_key_update_if_key_doesnt_exist() + { + var expectedAddArgs = new VariableEventArgs(1, default, 10); + var expectedChangeArgs = new VariableEventArgs(1, 10, 100); + VariableEventArgs actualAddArgs = null; + VariableEventArgs actualChangedArgs = null; + + var variables = new DefaultKeyedVariables(); + variables.OnAdded += (sender, args) => actualAddArgs = args; + variables.OnChanged += (sender, args) => actualChangedArgs = args; + + variables[expectedAddArgs.VariableType] = expectedAddArgs.NewValue; + variables[expectedChangeArgs.VariableType] = expectedChangeArgs.NewValue; + + Assert.NotNull(actualAddArgs); + Assert.Equal(expectedAddArgs.VariableType, actualAddArgs.VariableType); + Assert.Equal(expectedAddArgs.OldValue, actualAddArgs.OldValue); + Assert.Equal(expectedAddArgs.NewValue, actualAddArgs.NewValue); + + Assert.NotNull(actualChangedArgs); + Assert.Equal(expectedChangeArgs.VariableType, actualChangedArgs.VariableType); + Assert.Equal(expectedChangeArgs.OldValue, actualChangedArgs.OldValue); + Assert.Equal(expectedChangeArgs.NewValue, actualChangedArgs.NewValue); + + Assert.Equal(expectedChangeArgs.NewValue, variables.InternalVariables[expectedChangeArgs.VariableType]); + } +} \ No newline at end of file diff --git a/src/OpenRpg.UnitTests/OpenRpg.UnitTests.csproj b/src/OpenRpg.UnitTests/OpenRpg.UnitTests.csproj new file mode 100644 index 0000000..14075f4 --- /dev/null +++ b/src/OpenRpg.UnitTests/OpenRpg.UnitTests.csproj @@ -0,0 +1,28 @@ + + + + net6.0 + enable + + false + + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + diff --git a/src/OpenRpg.UnitTests/Scaling/CurveExtensionTests.cs b/src/OpenRpg.UnitTests/Scaling/CurveExtensionTests.cs new file mode 100644 index 0000000..7ea1dd1 --- /dev/null +++ b/src/OpenRpg.UnitTests/Scaling/CurveExtensionTests.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using OpenRpg.CurveFunctions; +using OpenRpg.CurveFunctions.Extensions; +using Xunit; + +namespace OpenRpg.UnitTests.Scaling; + +public class CurveExtensionTests +{ + public static IEnumerable TestData() + { + yield return new object[] { PresetCurves.Linear, 10, 100, 10 }; + yield return new object[] { PresetCurves.Linear, 50, 100, 50 }; + yield return new object[] { PresetCurves.InverseLinear, 50, 100, 50 }; + yield return new object[] { PresetCurves.InverseLinear, 10, 100, 90 }; + yield return new object[] { PresetCurves.InverseLinear, 10, 100000, 99990 }; + yield return new object[] { PresetCurves.InverseLinear, 0, 10, 10 }; + yield return new object[] { PresetCurves.BellCurve, 5, 10, 10 }; + } + + [Theory] + [MemberData(nameof(TestData))] + public void should_scale_plot_correctly(ICurveFunction curve, float inputValue, float maxValue, float expectedValue) + { + var actualValue = curve.ScaledPlot(inputValue, maxValue); + Assert.Equal(expectedValue, actualValue); + } +} \ No newline at end of file diff --git a/src/OpenRpg.UnitTests/Scaling/CurveFunctionTests.cs b/src/OpenRpg.UnitTests/Scaling/CurveFunctionTests.cs new file mode 100644 index 0000000..92bccf1 --- /dev/null +++ b/src/OpenRpg.UnitTests/Scaling/CurveFunctionTests.cs @@ -0,0 +1,90 @@ +using OpenRpg.CurveFunctions.Curves; +using Xunit; + +namespace OpenRpg.UnitTests.Scaling; + +public class CurveFunctionTests +{ + [Theory] + [InlineData(1.0f, 0f, 0.0f, 1.0f, 5.0f, 1.0f)] // Doesnt go over 1 + [InlineData(1.0f, 0f, 0.0f, 1.0f, -10.0f, 0.0f)] // Doesnt go below 0 + [InlineData(1.0f, 0f, 0.0f, 1.0f, 0.4f, 0.27f)] + [InlineData(1.0f, 0f, 0.0f, 1.0f, 1.0f, 0.99f)] + [InlineData(1.0f, 0f, 1.0f, 1.0f, 1.0f, 1.0f)] + public void should_correctly_calculate_logistic_curve_value(float slope, float xShift, float yShift, float verticalSize, float inputValue, float expectedValue) + { + var curve = new LogisticCurveFunction(slope, xShift, yShift, verticalSize); + var actualValue = curve.Plot(inputValue); + Assert.Equal(expectedValue.ToString("F"), actualValue.ToString("F")); + } + + [Theory] + [InlineData(2.0f, 0f, 2.0f, 0.5f, 1.0f)] // Doesnt go over 1 + [InlineData(1.0f, 0f, 0.0f, -10.0f, 0.0f)] // Doesnt go below 0 + [InlineData(1.0f, 0f, 0.0f, 0.4f, 0.42f)] + [InlineData(1.0f, 0f, 0.0f, 1.0f, 0.0f)] + [InlineData(1.0f, 0f, 1.0f, 1.0f, 0.0f)] + public void should_correctly_calculate_logit_curve_value(float slope, float xShift, float yShift, float inputValue, float expectedValue) + { + var curve = new LogitCurveFunction(slope, xShift, yShift); + var actualValue = curve.Plot(inputValue); + Assert.Equal(expectedValue.ToString("F"), actualValue.ToString("F")); + } + + [Theory] + [InlineData(2.0f, 0.2f, 2.0f, 2.0f, 0.5f, 1.0f)] // Doesnt go over 1 + [InlineData(1.0f, 0f, 0.0f, 1.0f, -10.0f, 0.0f)] // Doesnt go below 0 + [InlineData(1.0f, 0f, 0.0f, 1.0f, 0.4f, 0.74f)] + [InlineData(1.0f, 0f, 0.0f, 1.0f, 1.0f, 0)] + [InlineData(1.0f, 0.2f, 1.0f, 1.0f, 1.0f, 1.0f)] + public void should_correctly_calculate_normal_curve_value(float slope, float xShift, float yShift, float verticalSize, float inputValue, float expectedValue) + { + var curve = new NormalCurveFunction(slope, xShift, yShift, verticalSize); + var actualValue = curve.Plot(inputValue); + Assert.Equal(expectedValue.ToString("F"), actualValue.ToString("F")); + } + + [Theory] + [InlineData(1.0f, 0f, 0.0f, 1.0f, 5.0f, 1.0f)] // Doesnt go over 1 + [InlineData(1.0f, 0f, 0.0f, 1.0f, -10.0f, 0.0f)] // Doesnt go below 0 + [InlineData(1.0f, 0f, 0.0f, 1.0f, 0.4f, 0.4f)] + [InlineData(-1.0f, 0f, 1.0f, 4.0f, 0.5f, 0.94f)] + [InlineData(1.0f, 0f, 1.0f, 1.0f, 1.0f, 1.0f)] + public void should_correctly_calculate_polynomial_curve_value(float slope, float xShift, float yShift, float verticalSize, float inputValue, float expectedValue) + { + var curve = new PolynomialCurveFunction(slope, xShift, yShift, verticalSize); + var actualValue = curve.Plot(inputValue); + Assert.Equal(expectedValue.ToString("F"), actualValue.ToString("F")); + } + + [Theory] + [InlineData(1.0f, 0, 2.0f, 0.5f, 1.0f)] // Doesnt go over 1 + [InlineData(1.0f, 0, -0.1f, 0.8f, 0.0f)] // Doesnt go below 0 + [InlineData(1.0f, 0, 0, 0.4f, 0.79f)] + public void should_correctly_calculate_sine_curve_value(float slope, float xShift, float yShift, float inputValue, float expectedValue) + { + var curve = new SineCurveFunction(slope, xShift, yShift); + var actualValue = curve.Plot(inputValue); + Assert.Equal(expectedValue.ToString("F"), actualValue.ToString("F")); + } + + [Theory] + [InlineData(1.0f, 0, 2.0f, 1.0f, 1.0f)] // Doesnt go over 1 + [InlineData(1.0f, 0, -0.1f, 0.8f, 0.0f)] // Doesnt go below 0 + [InlineData(0.5f, 1.0f, 0.0f, 0.45f, 1.0f)] + public void should_correctly_calculate_step_curve_value(float slope, float xShift, float yShift, float inputValue, float expectedValue) + { + var curve = new StepCurveFunction(slope, xShift, yShift); + var actualValue = curve.Plot(inputValue); + Assert.Equal(expectedValue.ToString("F"), actualValue.ToString("F")); + } + + [Theory] + [InlineData(0.5f, 0.5f)] // Passes through + public void should_correctly_pass_through_curve_value(float inputValue, float expectedValue) + { + var curve = new PassThroughCurveFunction(); + var actualValue = curve.Plot(inputValue); + Assert.Equal(expectedValue.ToString("F"), actualValue.ToString("F")); + } +} \ No newline at end of file diff --git a/src/OpenRpg.UnitTests/Scaling/RandomizerExtensionTests.cs b/src/OpenRpg.UnitTests/Scaling/RandomizerExtensionTests.cs new file mode 100644 index 0000000..98dc643 --- /dev/null +++ b/src/OpenRpg.UnitTests/Scaling/RandomizerExtensionTests.cs @@ -0,0 +1,46 @@ +using Moq; +using OpenRpg.Core.Utils; +using OpenRpg.CurveFunctions; +using OpenRpg.CurveFunctions.Extensions; +using Xunit; + +namespace OpenRpg.UnitTests.Scaling; + +public class RandomizerExtensionTests +{ + [Fact] + public void should_correctly_plot_random_number_using_float_curve_extension() + { + var expectedResult = 1f; + + var mockRandomizer = new Mock(); + mockRandomizer.Setup(x => x.Random(0.0f, 1.0f)).Returns(0.5f); + var actualResult = mockRandomizer.Object.Random(PresetCurves.BellCurve); + + Assert.Equal(expectedResult, actualResult); + } + + [Fact] + public void should_correctly_plot_random_number_using_curve_extension_with_min_max_floats() + { + var expectedResult = 10.0f; + + var mockRandomizer = new Mock(); + mockRandomizer.Setup(x => x.Random(0.0f, 10.0f)).Returns(5.0f); + var actualResult = mockRandomizer.Object.Random(PresetCurves.BellCurve, 0.0f, 10.0f); + + Assert.Equal(expectedResult, actualResult); + } + + [Fact] + public void should_correctly_plot_random_number_using_curve_extension_with_negative_min_max_floats() + { + var expectedResult = -10.0f; + + var mockRandomizer = new Mock(); + mockRandomizer.Setup(x => x.Random(-10.0f, 0.0f)).Returns(-5.0f); + var actualResult = mockRandomizer.Object.Random(PresetCurves.BellCurve, -10.0f, 0.0f); + + Assert.Equal(expectedResult, actualResult); + } +} \ No newline at end of file diff --git a/src/OpenRpg.sln b/src/OpenRpg.sln index e0522d1..48e9709 100644 --- a/src/OpenRpg.sln +++ b/src/OpenRpg.sln @@ -28,6 +28,18 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Scaling", "Scaling", "{1566 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenRpg.CurveFunctions", "OpenRpg.CurveFunctions\OpenRpg.CurveFunctions.csproj", "{91C35205-4EB6-4204-964E-D94427D362AA}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Data", "Data", "{8468364A-996C-4E7C-AB45-FEE8AEA6ABDE}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenRpg.Data.InMemory", "OpenRpg.Data.InMemory\OpenRpg.Data.InMemory.csproj", "{ADD47427-9A25-46AB-9414-100D29A3C74E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenRpg.Data.Database", "OpenRpg.Data.Database\OpenRpg.Data.Database.csproj", "{BAB3772D-236C-4872-BA89-469866F56CBB}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{B374DC71-CEDE-42CC-A6E2-5D35577B08BA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenRpg.UnitTests", "OpenRpg.UnitTests\OpenRpg.UnitTests.csproj", "{A3AACDFF-0D4B-4B4C-801E-6369BA9C8B6B}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Localization", "Localization", "{167FBCA2-476D-4BA4-818A-FA4ED80E6197}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -66,15 +78,30 @@ Global {91C35205-4EB6-4204-964E-D94427D362AA}.Debug|Any CPU.Build.0 = Debug|Any CPU {91C35205-4EB6-4204-964E-D94427D362AA}.Release|Any CPU.ActiveCfg = Release|Any CPU {91C35205-4EB6-4204-964E-D94427D362AA}.Release|Any CPU.Build.0 = Release|Any CPU + {ADD47427-9A25-46AB-9414-100D29A3C74E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ADD47427-9A25-46AB-9414-100D29A3C74E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ADD47427-9A25-46AB-9414-100D29A3C74E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ADD47427-9A25-46AB-9414-100D29A3C74E}.Release|Any CPU.Build.0 = Release|Any CPU + {BAB3772D-236C-4872-BA89-469866F56CBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BAB3772D-236C-4872-BA89-469866F56CBB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BAB3772D-236C-4872-BA89-469866F56CBB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BAB3772D-236C-4872-BA89-469866F56CBB}.Release|Any CPU.Build.0 = Release|Any CPU + {A3AACDFF-0D4B-4B4C-801E-6369BA9C8B6B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A3AACDFF-0D4B-4B4C-801E-6369BA9C8B6B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A3AACDFF-0D4B-4B4C-801E-6369BA9C8B6B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A3AACDFF-0D4B-4B4C-801E-6369BA9C8B6B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(NestedProjects) = preSolution {86FD715B-0B33-40EB-B8AC-25EE62398A66} = {6BE3D2A4-F53A-4879-868E-A7522A445FE9} - {BB417DEC-B2C9-4F60-9E87-2E674ACAD06F} = {6BE3D2A4-F53A-4879-868E-A7522A445FE9} - {5C2FF42D-8E29-4EDE-84D0-47D24FFFFBB9} = {6BE3D2A4-F53A-4879-868E-A7522A445FE9} {AB5563B9-CE48-49A4-BE3C-793351C7377F} = {7C187634-FF6B-4A6E-AAD2-FCA3C97C1E5A} {8D5C52B0-B68B-4539-9D2F-DA22C825B923} = {56A39779-8FA4-499E-8EF9-4757765CC230} {754A79DD-AC13-4EC1-9DDA-AF5D5F6DABCE} = {98CD79A9-0E41-49CE-8246-3A32C1A2E004} {2A8FA13E-2E89-457B-9457-46C345768495} = {C5E5EB5E-7F08-4CF4-B4DF-F20434DE2930} {91C35205-4EB6-4204-964E-D94427D362AA} = {1566BC2E-78DC-4998-BEC8-65CEC3EC9A88} + {BB417DEC-B2C9-4F60-9E87-2E674ACAD06F} = {8468364A-996C-4E7C-AB45-FEE8AEA6ABDE} + {ADD47427-9A25-46AB-9414-100D29A3C74E} = {8468364A-996C-4E7C-AB45-FEE8AEA6ABDE} + {BAB3772D-236C-4872-BA89-469866F56CBB} = {8468364A-996C-4E7C-AB45-FEE8AEA6ABDE} + {A3AACDFF-0D4B-4B4C-801E-6369BA9C8B6B} = {B374DC71-CEDE-42CC-A6E2-5D35577B08BA} + {5C2FF42D-8E29-4EDE-84D0-47D24FFFFBB9} = {167FBCA2-476D-4BA4-818A-FA4ED80E6197} EndGlobalSection EndGlobal