177 lines
7.3 KiB
C#
177 lines
7.3 KiB
C#
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]);
|
|
}
|
|
}
|
|
} |