init
This commit is contained in:
@@ -0,0 +1,177 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using Sirenix.OdinInspector;
|
||||
using Sirenix.Utilities;
|
||||
using Sirenix.Utilities.Editor;
|
||||
using UnityEngine;
|
||||
using VOID.Generic.Dict;
|
||||
using VOID.Generic.Objects.Unit.DataCalculator;
|
||||
|
||||
namespace VOID.Generic.Objects.Unit
|
||||
{
|
||||
[Serializable]
|
||||
public class UnitObjectDataCalculator : ObjectComponent<UnitObject>
|
||||
{
|
||||
#region Editor
|
||||
|
||||
[ShowInInspector]
|
||||
[NonSerialized]
|
||||
[ListDrawerSettings(
|
||||
OnTitleBarGUI = "CreateMarkersInfoBox",
|
||||
DraggableItems = false,
|
||||
HideRemoveButton = true,
|
||||
HideAddButton = true,
|
||||
DefaultExpandedState = false)]
|
||||
private List<string> _availableMarkers = new();
|
||||
|
||||
private void CreateMarkersInfoBox()
|
||||
{
|
||||
if (!SirenixEditorGUI.ToolbarButton(EditorIcons.Refresh)) return;
|
||||
Debug.Log("Marker list refreshed!", parent);
|
||||
CreateCalcGroups();
|
||||
_availableMarkers = new List<string>();
|
||||
_calcGroups.ForEach(calcGroup =>
|
||||
calcGroup.GetMarkerItems()
|
||||
.Select(item => $"{{{calcGroup.GetMarkerGroup()}.{item}}}")
|
||||
.ForEach(marker => _availableMarkers.Add(marker)));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Initialize
|
||||
|
||||
protected override void SelfInitialize()
|
||||
{
|
||||
MergeFormulas();
|
||||
CreateCalcGroups();
|
||||
CreateDependencyMap();
|
||||
}
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
CalculateAllInstant();
|
||||
}
|
||||
|
||||
private void MergeFormulas()
|
||||
{
|
||||
_mainAttributeFormulas.ForEach(formula => _formulas.Add(formula.Key, formula.Value));
|
||||
_subAttributeFormulas.ForEach(formula => _formulas.Add(formula.Key, formula.Value));
|
||||
_basicResourceFormulas.ForEach(formula => _formulas.Add(formula.Key, formula.Value));
|
||||
_unitResourceFormulas.ForEach(formula => _formulas.Add(formula.Key, formula.Value));
|
||||
}
|
||||
|
||||
private void CreateCalcGroups()
|
||||
{
|
||||
_calcGroups = AppDomain.CurrentDomain.GetAssemblies()
|
||||
.SelectMany(assembly => assembly.GetTypes())
|
||||
.Where(type => typeof(ICalcGroup).IsAssignableFrom(type) && type.IsClass)
|
||||
.Select(Activator.CreateInstance)
|
||||
.Cast<ICalcGroup>()
|
||||
.ToList();
|
||||
}
|
||||
|
||||
private void CreateDependencyMap()
|
||||
{
|
||||
_formulas.ForEach(formulaPair =>
|
||||
{
|
||||
var forEnum = formulaPair.Key;
|
||||
var formula = formulaPair.Value;
|
||||
var markers = ParseFormulaMarkers(formula);
|
||||
markers.ForEach(marker => GetMarkerValue(marker));
|
||||
_dependencies.Add(forEnum, markers.Select(GetMarkerEnum).ToList());
|
||||
});
|
||||
}
|
||||
|
||||
private void CalculateAllInstant()
|
||||
{
|
||||
// Force to calculate main attributes (all other should be queued to calculate by dependencies)
|
||||
Enum.GetValues(typeof(MainAttributeType)).Cast<Enum>().ForEach(Queue);
|
||||
|
||||
while (!_calcQueue.IsNullOrEmpty()) ExecuteQueue();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
[Serializable] private class MainAttributeFormulaDict : SerializedDictionary<MainAttributeType, string> {}
|
||||
[Serializable] private class SubAttributeFormulaDict : SerializedDictionary<SubAttributeType, string> {}
|
||||
[Serializable] private class BasicResourceFormulaDict : SerializedDictionary<BasicResourceType, string> {}
|
||||
[Serializable] private class UnitResourceFormulaDict : SerializedDictionary<UnitResourceType, string> {}
|
||||
|
||||
[Title("Formulas used to calculate")]
|
||||
[SerializeField, EnableIn(PrefabKind.PrefabAsset)] private MainAttributeFormulaDict _mainAttributeFormulas = new();
|
||||
[SerializeField, EnableIn(PrefabKind.PrefabAsset)] private SubAttributeFormulaDict _subAttributeFormulas = new();
|
||||
[SerializeField, EnableIn(PrefabKind.PrefabAsset)] private BasicResourceFormulaDict _basicResourceFormulas = new();
|
||||
[SerializeField, EnableIn(PrefabKind.PrefabAsset)] private UnitResourceFormulaDict _unitResourceFormulas = new();
|
||||
|
||||
private Dictionary<Enum, string> _formulas = new();
|
||||
private Dictionary<Enum, List<Enum>> _dependencies = new();
|
||||
private List<Enum> _calcQueue = new();
|
||||
|
||||
private List<ICalcGroup> _calcGroups;
|
||||
|
||||
public void Queue(Enum @enum)
|
||||
{
|
||||
if (_calcGroups.All(calcGroup => calcGroup.GetEnumType() != @enum.GetType()))
|
||||
throw new Exception($"Enum '{@enum}' is not acceptable in {nameof(UnitObjectDataCalculator)}.");
|
||||
|
||||
if (_calcQueue.Contains(@enum)) return;
|
||||
_calcQueue.Add(@enum);
|
||||
parent.basicEvents.onFixedUpdate.AddListener(ExecuteQueue);
|
||||
}
|
||||
|
||||
private void ExecuteQueue()
|
||||
{
|
||||
var tempCalcQueue = new List<Enum>(_calcQueue);
|
||||
_calcQueue.Clear();
|
||||
parent.basicEvents.onFixedUpdate.RemoveListener(ExecuteQueue);
|
||||
tempCalcQueue.ForEach(Calculate);
|
||||
}
|
||||
|
||||
private void Calculate(Enum @enum)
|
||||
{
|
||||
var calcGroup = _calcGroups.FirstOrDefault(calcGroup => calcGroup.CheckEnum(@enum));
|
||||
if (calcGroup == null) throw new Exception($"Marker '{@enum}' does not exists!");
|
||||
calcGroup.UpdateWithValue(parent, @enum.ToString(), GetFormulaValue(@enum));
|
||||
_dependencies.Where(pair => pair.Value.Contains(@enum)).ForEach(pair => Queue(pair.Key));
|
||||
}
|
||||
|
||||
private float GetFormulaValue(Enum @enum)
|
||||
{
|
||||
var formula = _formulas.GetValueOrDefault(@enum, "");
|
||||
ParseFormulaMarkers(formula).ForEach(marker => formula = formula.Replace(marker, GetMarkerValue(marker)));
|
||||
var isValid = ExpressionEvaluator.Evaluate(formula, out float result);
|
||||
if (!isValid) Debug.LogError($"Formula `{formula}` is invalid and can't be calculated! Using zero!");
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<string> ParseFormulaMarkers(string formula)
|
||||
{
|
||||
return Regex.Matches(formula, "{(.*?)}").Select(match => match.Value).Distinct().ToList();
|
||||
}
|
||||
|
||||
private string GetMarkerValue(string marker)
|
||||
{
|
||||
var parsedMarker = ParseMarker(marker);
|
||||
var calcGroup = _calcGroups.FirstOrDefault(cg => cg.CheckMarker(parsedMarker.group, parsedMarker.item));
|
||||
if (calcGroup == null) throw new Exception($"Marker '{marker}' does not exists!");
|
||||
return calcGroup.GetValue(parent, parsedMarker.item).ToString("F2", CultureInfo.InvariantCulture);
|
||||
}
|
||||
|
||||
private Enum GetMarkerEnum(string marker)
|
||||
{
|
||||
var parsedMarker = ParseMarker(marker);
|
||||
var calcGroup = _calcGroups.FirstOrDefault(cg => cg.CheckMarker(parsedMarker.group, parsedMarker.item));
|
||||
if (calcGroup == null) throw new Exception($"Marker '{marker}' does not exists!");
|
||||
return calcGroup.GetEnum(parsedMarker.item);
|
||||
}
|
||||
|
||||
private (string group, string item) ParseMarker(string marker)
|
||||
{
|
||||
var arrayMarker = marker.Trim('{', '}').Split('.');
|
||||
return new ValueTuple<string, string>(arrayMarker[0], arrayMarker[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user