diff --git a/Heck/Animation/PointDefinition/PointDefinition.cs b/Heck/Animation/PointDefinition/PointDefinition.cs index 6e2e1a8..f61a8d5 100644 --- a/Heck/Animation/PointDefinition/PointDefinition.cs +++ b/Heck/Animation/PointDefinition/PointDefinition.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using Heck.BaseProvider; using ModestTree; namespace Heck.Animation; @@ -170,7 +171,7 @@ private static IEnumerable> Group(IEnumerable GroupType.Flag, + string s when !BaseProviderManager.Instance.IsProviderString(s) => GroupType.Flag, List => GroupType.Modifier, _ => GroupType.Value }; diff --git a/Heck/Animation/PointDefinition/Values.cs b/Heck/Animation/PointDefinition/Values.cs index e652f3b..3670418 100644 --- a/Heck/Animation/PointDefinition/Values.cs +++ b/Heck/Animation/PointDefinition/Values.cs @@ -6,12 +6,12 @@ namespace Heck.Animation; -internal interface IValues +public interface IValues { public float[] Values { get; } } -internal interface IRotationValues +public interface IRotationValues { public Quaternion Rotation { get; } } @@ -36,7 +36,7 @@ internal BaseProviderValues(float[] values) public float[] Values { get; } } -internal abstract record UpdateableValues : IValues +public abstract record UpdateableValues : IValues { public abstract float[] Values { get; } diff --git a/Heck/Services/BaseProvider/BaseProviderManager.cs b/Heck/Services/BaseProvider/BaseProviderManager.cs index 0218c1d..6d79f4e 100644 --- a/Heck/Services/BaseProvider/BaseProviderManager.cs +++ b/Heck/Services/BaseProvider/BaseProviderManager.cs @@ -10,11 +10,15 @@ namespace Heck.BaseProvider; -internal class BaseProviderManager : ITickable +public class BaseProviderManager : ITickable { private readonly Dictionary _baseProviders = new(); private readonly HashSet _updateableBaseProviders = []; private readonly Dictionary _updateableProviders = new(); + private readonly Dictionary _dynamicProviders = new(); + private readonly Dictionary _dynamicSeperators = new(); + private readonly Dictionary _updateableDynamicProviders = new(); + private readonly List _dynamicModifiers = new(); [UsedImplicitly] private BaseProviderManager( @@ -22,6 +26,7 @@ private BaseProviderManager( IEnumerable baseProviders) { Instance = this; + AddModifierHandler(DefaultModifier); foreach (IBaseProvider baseProvider in baseProviders) { foreach (PropertyInfo propertyInfo in baseProvider.GetType().GetProperties(AccessTools.allDeclared)) @@ -50,6 +55,12 @@ private BaseProviderManager( } } + public delegate UpdateableValues? DynamicModifier(string key, IValues provider, out string rest); + + public delegate IValues? DynamicProvider(string key); + + public delegate string DynamicSeperator(string key, out string rest); + // I couldnt think of a way to di this thing internal static BaseProviderManager Instance { get; private set; } = null!; @@ -60,69 +71,159 @@ public void Tick() updatableProvidersValue.Update(); } + foreach (UpdateableValues updatableDynamicProvidersValue in _updateableDynamicProviders.Values) + { + updatableDynamicProvidersValue.Update(); + } + foreach (UpdateableValues updateableBaseProvider in _updateableBaseProviders) { updateableBaseProvider.Update(); } } - // Clear cache when song ends to not keep updating unneeded values - public void Clear() + [PublicAPI] + public void AddModifierHandler(DynamicModifier modifier) { - _updateableProviders.Clear(); + _dynamicModifiers.Add(modifier); } - internal IValues GetProviderValues(string key) + [PublicAPI] + public void AddDynamicProvider(string startsWith, DynamicProvider dynamicProvider) { - string[] splits = key.Split('.'); - IValues result = _baseProviders[splits[0]]; + _dynamicProviders.Add(startsWith, dynamicProvider); + } - if (splits.Length == 1) - { - return result; - } + [PublicAPI] + public void AddDynamicSeperator(string startsWith, DynamicSeperator dynamicProvider) + { + _dynamicSeperators.Add(startsWith, dynamicProvider); + } + + [PublicAPI] + public IValues GetProviderValues(string key) + { + string rest; + IValues result = GetProvider(key, out rest); if (_updateableProviders.TryGetValue(key, out UpdateableValues cachedValues)) { return cachedValues; } - for (int i = 1; i < splits.Length; i++) + + while (rest != String.Empty) { - string split = splits[i]; - string subKey = string.Join(".", splits.Take(i)); - if (!_updateableProviders.TryGetValue(subKey, out UpdateableValues updateableValues)) + UpdateableValues updateableValues = ApplyModifiers(rest, result, out rest); + string subkey = key.Substring(0, key.Length - rest.Length - 1); + if (!_updateableProviders.ContainsKey(subkey)) { - if (split.StartsWith("s")) - { - float mult = float.Parse(split.Substring(1).Replace('_', '.')); - updateableValues = result is IRotationValues rotationValues - ? new SmoothRotationProvidersValues(rotationValues, mult) - : new SmoothProvidersValues(result.Values, mult); - } - else - { - int[] parts = split - .Select( - n => n switch - { - 'x' => 0, - 'y' => 1, - 'z' => 2, - 'w' => 3, - _ => throw new ArgumentOutOfRangeException(nameof(n), n, null) - }) - .ToArray(); - - updateableValues = new PartialProviderValues(result.Values, parts); - } - - _updateableProviders[subKey] = updateableValues; + _updateableProviders.Add(subkey, updateableValues); } - result = updateableValues; + result = _updateableProviders[subkey]; } return result; } + + internal bool IsProviderString(string key) + { + return key.StartsWith("base") + || _dynamicProviders.Keys.Any(key.StartsWith); + } + + // Clear cache when song ends to not keep updating unneeded values + internal void Clear() + { + _updateableProviders.Clear(); + _updateableDynamicProviders.Clear(); + } + + private IValues GetProvider(string key, out string modifiers) + { + var splits = key.Split(['.'], 2); + var start = splits[0]; + modifiers = splits.Length > 1 ? splits[1] : string.Empty; + if (_baseProviders.TryGetValue(splits[0], out var baseProvider)) + { + return baseProvider; + } + + foreach (var providerKey in _dynamicProviders.Keys.Where(key.StartsWith)) + { + if (_dynamicSeperators.TryGetValue(providerKey, out var seperator)) + { + start = seperator.Invoke(key, out modifiers); + } + + if (_updateableDynamicProviders.TryGetValue(start, out var dynamicProvider)) + { + return dynamicProvider; + } + + var provider = _dynamicProviders[providerKey].Invoke(start); + + if (provider is null) + { + continue; + } + + if (provider is UpdateableValues updateableProvider) + { + _updateableDynamicProviders.Add(start, updateableProvider); + } + + return provider; + } + + throw new Exception($"No provider found for: \"{key}\""); + } + + private UpdateableValues ApplyModifiers(string modifiers, IValues baseValue, out string rest) + { + foreach (var modifier in _dynamicModifiers) + { + var modifiersValue = modifier(modifiers, baseValue, out rest); + if (modifiersValue is not null) + { + return modifiersValue; + } + } + + throw new Exception($"No modifier found for: \"{modifiers}\""); + } + + private UpdateableValues? DefaultModifier(string modifiers, IValues baseValue, out string rest) + { + var splits = modifiers.Split(['.'], 2); + var modifier = splits[0]; + rest = splits.Length > 1 ? splits[1] : string.Empty; + + switch (modifier[0]) + { + case 's': + float mult = float.Parse(modifier.Substring(1).Replace('_', '.')); + return baseValue is IRotationValues rotationValues + ? new SmoothRotationProvidersValues(rotationValues, mult) + : new SmoothProvidersValues(baseValue.Values, mult); + + case 'x' or 'y' or 'z' or 'w': + int[] parts = modifier + .Select( + n => n switch + { + 'x' => 0, + 'y' => 1, + 'z' => 2, + 'w' => 3, + _ => throw new ArgumentOutOfRangeException(nameof(n), n, null) + }) + .ToArray(); + + return new PartialProviderValues(baseValue.Values, parts); + } + + return null; + } }