[#14] rozbudowa stats + dodanie modyfikatorów
This commit is contained in:
+1
-1
@@ -6,7 +6,7 @@ namespace RPGCore.Stats.ObjectModules.StatsObjectModule.Events
|
||||
public class StatResourceChangeEvent : BaseEvent<BaseObject>
|
||||
{
|
||||
public BaseObject target;
|
||||
public StatDefinitionSO statDefinition;
|
||||
public StatResourceDefinitionSO resourceDefinition;
|
||||
public int delta;
|
||||
public int before;
|
||||
public int after;
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@ namespace RPGCore.Stats.ObjectModules.StatsObjectModule.Events
|
||||
public class StatValueChangeEvent : BaseEvent<BaseObject>
|
||||
{
|
||||
public BaseObject target;
|
||||
public StatDefinitionSO statDefinition;
|
||||
public StatValueDefinitionSO valueDefinition;
|
||||
public int before;
|
||||
public int after;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
using RPGCore.Core.Objects;
|
||||
|
||||
namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
||||
{
|
||||
public class StatModifier
|
||||
{
|
||||
public readonly BaseObject source;
|
||||
public readonly int value;
|
||||
public readonly StatModifierType type;
|
||||
|
||||
public StatModifier(BaseObject source, int value, StatModifierType type)
|
||||
{
|
||||
this.source = source;
|
||||
this.value = value;
|
||||
this.type = type;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1347462f6c9444359615c903e5749bd3
|
||||
timeCreated: 1782736826
|
||||
@@ -0,0 +1,30 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using RPGCore.Core.Objects;
|
||||
|
||||
namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
||||
{
|
||||
public class StatModifierContext
|
||||
{
|
||||
private readonly List<StatModifier> _modifiers = new();
|
||||
|
||||
public int add => _modifiers.Where(m => m.type is StatModifierType.Add).Sum(m => m.value);
|
||||
public int multiply => _modifiers.Where(m => m.type is StatModifierType.Multiply).Sum(m => m.value);
|
||||
public int @override => _modifiers.Where(m => m.type is StatModifierType.Override).Max(m => m.value);
|
||||
|
||||
public void Add(StatModifier modifier)
|
||||
{
|
||||
_modifiers.Add(modifier);
|
||||
}
|
||||
|
||||
public void Remove(StatModifier modifier)
|
||||
{
|
||||
_modifiers.Remove(modifier);
|
||||
}
|
||||
|
||||
public void RemoveBySource(BaseObject source)
|
||||
{
|
||||
_modifiers.RemoveAll(m => m.source == source);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 53790325b2504e468bd5452be43d9ac8
|
||||
timeCreated: 1782738303
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
||||
{
|
||||
public enum StatModifierType
|
||||
{
|
||||
Add,
|
||||
Multiply,
|
||||
Override,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ba280aecb8764285be02cd44827226cb
|
||||
timeCreated: 1782736851
|
||||
@@ -0,0 +1,64 @@
|
||||
using RPGCore.Stats.ObjectModules.StatsObjectModule.Events;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
||||
{
|
||||
/// <summary>
|
||||
/// <b>You should never instantiate this by yourself!</b>
|
||||
/// Instantiated automatically by <see cref="StatsModule"/> for every available <see cref="StatResourceDefinitionSO"/>.
|
||||
/// </summary>
|
||||
public sealed class StatResource
|
||||
{
|
||||
public StatResourceDefinitionSO definition { get; internal set; }
|
||||
public StatValue statValue { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// This value represent current resource of this stat. Always between <see cref="StatValue.baseValue"/> and <see cref="StatValue.value"/>
|
||||
/// </summary>
|
||||
public int resource { get; private set; }
|
||||
|
||||
internal StatResource(StatResourceDefinitionSO definition, int defaultValue, StatValue statValue)
|
||||
{
|
||||
this.definition = definition;
|
||||
resource = defaultValue;
|
||||
this.statValue = statValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes (or simpler - uses) <see cref="resource"/> of this stat value by given amount.
|
||||
/// It just calls <see cref="SetResource(int)"/> with: <see cref="resource"/> + <see cref="amount"/>
|
||||
/// </summary>
|
||||
/// <param name="amount">Amount that will be added or subtracted from <see cref="resource"/></param>
|
||||
public void ChangeResource(int amount)
|
||||
{
|
||||
SetResource(resource + amount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets <see cref="resource"/> to given amount. Amount will be clamped between ZERO and <see cref="StatValue.value"/>.
|
||||
/// </summary>
|
||||
/// <param name="amount">New amount</param>
|
||||
public void SetResource(int amount)
|
||||
{
|
||||
var old = resource;
|
||||
resource = Mathf.Clamp(amount, 0, statValue.value);
|
||||
statValue.obj.events.Invoke(new StatResourceChangeEvent
|
||||
{
|
||||
delta = resource - old,
|
||||
after = resource,
|
||||
before = old,
|
||||
resourceDefinition = definition,
|
||||
target = statValue.obj
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets <see cref="resource"/> to given percentage. Percentage will be clamped between ZERO and ONE.
|
||||
/// </summary>
|
||||
/// <param name="percentage">New percentage</param>
|
||||
public void SetResource(float percentage)
|
||||
{
|
||||
SetResource(Mathf.CeilToInt(Mathf.Clamp01(percentage) * statValue.value));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4fc9e797821a4084bcb2af5d6667a176
|
||||
timeCreated: 1782589814
|
||||
@@ -0,0 +1,10 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
||||
{
|
||||
[CreateAssetMenu(menuName = "RPG Core/Object Resource Definition")]
|
||||
public class StatResourceDefinitionSO : ScriptableObject
|
||||
{
|
||||
public StatValueDefinitionSO valueDefinitionSO;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 068afee00fe449318bcbb2fce622822c
|
||||
timeCreated: 1782588829
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
||||
{
|
||||
public enum StatType
|
||||
{
|
||||
Value,
|
||||
Resource
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1800df1b40e6455abd599f5bb6808735
|
||||
timeCreated: 1762771772
|
||||
@@ -1,20 +1,19 @@
|
||||
using RPGCore.Core.Objects;
|
||||
using RPGCore.ObjectModules.EventObjectModule;
|
||||
using RPGCore.Stats.ObjectModules.StatsObjectModule.Events;
|
||||
using RPGCoreCommon.DynamicValues;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
||||
{
|
||||
/// <summary>
|
||||
/// <b>You should never instantiate this by yourself!</b>
|
||||
/// Instantiated automatically by <see cref="StatsModule"/> for every available <see cref="StatDefinitionSO"/>.
|
||||
/// Instantiated automatically by <see cref="StatsModule"/> for every available <see cref="StatValueDefinitionSO"/>.
|
||||
/// </summary>
|
||||
public sealed class StatValue
|
||||
{
|
||||
public readonly StatDefinitionSO definition;
|
||||
private readonly BaseObject _parent;
|
||||
public readonly DynamicValueSourceContext sourceContext = new();
|
||||
public readonly StatValueDefinitionSO definition;
|
||||
public readonly BaseObject obj;
|
||||
public readonly DynamicValueSourceContext sources = new();
|
||||
public readonly StatModifierContext modifiers = new();
|
||||
|
||||
/// <summary>
|
||||
/// Base value defined in <see cref="StatsModule"/> attached to <see cref="BaseObject"/>.
|
||||
@@ -24,22 +23,14 @@ namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
||||
public int baseValue { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// This value is calculated by <see cref="DynamicValue"/> in <see cref="StatDefinitionSO"/> and can have two meanings:<br/>
|
||||
/// 1. If <see cref="StatType.Value"/> - just simple value stat<br/>
|
||||
/// 2. If <see cref="StatType.Resource"/> - this is maximum value for that resource
|
||||
/// This value is calculated by <see cref="DynamicValue"/> in <see cref="StatValueDefinitionSO"/>
|
||||
/// </summary>
|
||||
public int value { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Usable only when <see cref="StatDefinitionSO"/>.<see cref="StatType"/> is <see cref="StatType.Resource"/>.<br/>
|
||||
/// This value represent current resource of this stat. Always between <see cref="baseValue"/> and <see cref="value"/>
|
||||
/// </summary>
|
||||
public int resource { get; private set; }
|
||||
|
||||
internal StatValue(StatDefinitionSO definition, int baseValue, BaseObject parent)
|
||||
internal StatValue(StatValueDefinitionSO definition, int baseValue, BaseObject obj)
|
||||
{
|
||||
this.definition = definition;
|
||||
_parent = parent;
|
||||
this.obj = obj;
|
||||
this.baseValue = baseValue;
|
||||
}
|
||||
|
||||
@@ -49,13 +40,14 @@ namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
||||
internal void Refresh()
|
||||
{
|
||||
var previous = value;
|
||||
value = baseValue + definition.dynamicValue.GetValue(sourceContext);
|
||||
_parent.events.Invoke(new StatValueChangeEvent
|
||||
value = definition.dynamicValue.GetValue(sources);
|
||||
if (previous == value) return;
|
||||
obj.events.Invoke(new StatValueChangeEvent
|
||||
{
|
||||
after = value,
|
||||
before = previous,
|
||||
statDefinition = definition,
|
||||
target = _parent
|
||||
valueDefinition = definition,
|
||||
target = obj
|
||||
});
|
||||
}
|
||||
|
||||
@@ -65,45 +57,9 @@ namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
||||
/// <param name="amount">New base value</param>
|
||||
public void SetBase(int amount)
|
||||
{
|
||||
if (baseValue == amount) return;
|
||||
baseValue = amount;
|
||||
Refresh();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes (or simpler - uses) <see cref="resource"/> of this stat value by given amount.
|
||||
/// It just calls <see cref="SetResource(int)"/> with: <see cref="resource"/> + <see cref="amount"/>
|
||||
/// </summary>
|
||||
/// <param name="amount">Amount that will be added or subtracted from <see cref="resource"/></param>
|
||||
public void ChangeResource(int amount)
|
||||
{
|
||||
SetResource(resource + amount);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets <see cref="resource"/> to given amount. Amount will be clamped between ZERO and <see cref="value"/>.
|
||||
/// </summary>
|
||||
/// <param name="amount">New amount</param>
|
||||
public void SetResource(int amount)
|
||||
{
|
||||
var old = resource;
|
||||
resource = Mathf.Clamp(amount, 0, value);
|
||||
_parent.events.Invoke(new StatResourceChangeEvent
|
||||
{
|
||||
delta = resource - old,
|
||||
after = resource,
|
||||
before = old,
|
||||
statDefinition = definition,
|
||||
target = _parent
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets <see cref="resource"/> to given percentage. Percentage will be clamped between ZERO and ONE.
|
||||
/// </summary>
|
||||
/// <param name="percentage">New percentage</param>
|
||||
public void SetResource(float percentage)
|
||||
{
|
||||
SetResource(Mathf.CeilToInt(Mathf.Clamp01(percentage) * value));
|
||||
}
|
||||
}
|
||||
}
|
||||
+20
-13
@@ -11,13 +11,12 @@ using UnityEngine;
|
||||
|
||||
namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
||||
{
|
||||
[CreateAssetMenu(menuName = "RPG Core/Object Stat Definition")]
|
||||
public sealed class StatDefinitionSO : ScriptableObject
|
||||
[CreateAssetMenu(menuName = "RPG Core/Object Value Definition")]
|
||||
public class StatValueDefinitionSO : ScriptableObject
|
||||
{
|
||||
[SerializableType(typeof(BaseObject), allowAbstract: true)] public SerializableType attachedTo;
|
||||
public StatType type;
|
||||
public StatDefinitionSO overrides;
|
||||
[ReadOnly(true)] public StatDefinitionSO[] dependencies = {};
|
||||
public StatValueDefinitionSO overrides;
|
||||
[ReadOnly(true)] public StatValueDefinitionSO[] dependencies = {};
|
||||
|
||||
[DynamicValue(DynamicValueType.ByDynamicTypes)] public DynamicValue dynamicValue = new();
|
||||
|
||||
@@ -35,6 +34,7 @@ namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
||||
{
|
||||
dynamicValue.RemoveDynamicTypes();
|
||||
dynamicValue.SetDynamicType("object", attachedTo);
|
||||
dynamicValue.SetDynamicType("stats", typeof(StatsModule));
|
||||
}
|
||||
|
||||
private void FixOverride()
|
||||
@@ -58,19 +58,26 @@ namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
||||
return;
|
||||
}
|
||||
|
||||
// Overriding only for same types to prevent overriding VALUE <=> RESOURCE
|
||||
if (overrides.GetType() != GetType())
|
||||
{
|
||||
Debug.LogWarning("Only overriding of same stat types is allowed! VALUE and RESOURCE is not allowed!");
|
||||
overrides = null;
|
||||
return;
|
||||
}
|
||||
|
||||
name = overrides.name;
|
||||
type = overrides.type;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// <see cref="StatsModule"/> has custom value provider <see cref="StatsModule.StatValueProvider"/> that matches stats by their name.
|
||||
/// We will match these names with marker's parameter to create list of other <see cref="StatDefinitionSO"/> that this one uses.
|
||||
/// We will match these names with marker's parameter to create list of other <see cref="StatValueDefinitionSO"/> that this one uses.
|
||||
/// Main purpose of dependencies is caching, specially when there is a lot of definitions and dependencies.
|
||||
/// </summary>
|
||||
private void FixDependencies()
|
||||
{
|
||||
var tempDependencies = new List<StatDefinitionSO>();
|
||||
var allDefinitions = SettingsManager.Get<StatsSettings>().allStats;
|
||||
var tempDependencies = new List<StatValueDefinitionSO>();
|
||||
var allDefinitions = SettingsManager.Get<StatsSettings>().allValues;
|
||||
|
||||
foreach (var marker in dynamicValue.GetMarkers())
|
||||
{
|
||||
@@ -107,13 +114,13 @@ namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
||||
/// If looping is found then dependencies will be cleared, preventing application freeze!
|
||||
/// </summary>
|
||||
/// <param name="definitionsChain"></param>
|
||||
private void CheckDependenciesLooping(List<StatDefinitionSO> definitionsChain = null)
|
||||
private void CheckDependenciesLooping(List<StatValueDefinitionSO> definitionsChain = null)
|
||||
{
|
||||
definitionsChain ??= new List<StatDefinitionSO>();
|
||||
definitionsChain ??= new List<StatValueDefinitionSO>();
|
||||
|
||||
if (definitionsChain.Contains(this))
|
||||
{
|
||||
definitionsChain[0].dependencies = Array.Empty<StatDefinitionSO>();
|
||||
definitionsChain[0].dependencies = Array.Empty<StatValueDefinitionSO>();
|
||||
var definitionsChainString = definitionsChain.Append(this).Select(d => d.name).StringJoin(" -> ");
|
||||
Debug.LogError($"Looped dependencies found! {definitionsChainString}", this);
|
||||
return;
|
||||
@@ -129,7 +136,7 @@ namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
||||
/// Thanks to OnValidate its impossible to create endless overriding loop, it is possible to override only child object.
|
||||
/// </summary>
|
||||
/// <returns>Last overriden definition</returns>
|
||||
public StatDefinitionSO GetBaseDefinition()
|
||||
public StatValueDefinitionSO GetBaseDefinition()
|
||||
{
|
||||
var tempDefinition = this;
|
||||
while (tempDefinition.overrides)
|
||||
@@ -12,57 +12,109 @@ using UnityEngine;
|
||||
namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
||||
{
|
||||
[Serializable]
|
||||
[ObjectModule(
|
||||
name: "[Stats] Statistics",
|
||||
description: "Attached to any implementation of <b>BaseObject</b>. " +
|
||||
"Create scriptable object <b>"+nameof(StatValueDefinitionSO)+"</b> to define new stats. " +
|
||||
"Stats will be automatically created in any matching implementation of <b>BaseObject</b>. " +
|
||||
"Remember to add all definitions to generated <b>"+nameof(StatsSettings)+"</b>."
|
||||
)]
|
||||
public class StatsModule : ObjectModule<BaseObject>
|
||||
{
|
||||
[SerializeField]
|
||||
[SerializableDictionary("Stat Definition", "Base Value", isKeyEditable: false)]
|
||||
private SerializableDictionary<StatDefinitionSO, int> _serializedStats;
|
||||
[SerializableDictionary("Value Definition", "Base Value", isKeyEditable: false)]
|
||||
private SerializableDictionary<StatValueDefinitionSO, int> _serializedValues;
|
||||
[SerializeField]
|
||||
[SerializableDictionary("Resource Definition", "Default Value", isKeyEditable: false)]
|
||||
private SerializableDictionary<StatResourceDefinitionSO, int> _serializedResources;
|
||||
|
||||
private Dictionary<StatDefinitionSO, StatValue> _stats = new();
|
||||
private List<StatDefinitionSO> _queue = new();
|
||||
private Dictionary<StatValueDefinitionSO, StatValue> _values = new();
|
||||
private Dictionary<StatResourceDefinitionSO, StatResource> _resources = new();
|
||||
private List<StatValueDefinitionSO> _queue = new();
|
||||
|
||||
#if UNITY_EDITOR
|
||||
|
||||
private void OnValidate()
|
||||
{
|
||||
FixSerializedStats();
|
||||
FixSerializedStatValues();
|
||||
FixSerializedStatResources();
|
||||
}
|
||||
|
||||
private void FixSerializedStats()
|
||||
private void FixSerializedStatValues()
|
||||
{
|
||||
// fixing only when variable is serialized (sometimes OnValidate can be called before deserializing on project open)
|
||||
if (_serializedStats == null) return;
|
||||
if (_serializedValues == null) return;
|
||||
|
||||
// All stat definitions that should be added to this object...
|
||||
var correctStats = SettingsManager.Get<StatsSettings>().allStats
|
||||
.Where(stat => stat)
|
||||
.Where(stat => stat.attachedTo.type.IsAssignableFrom(parent.GetType()))
|
||||
// All value definitions that should be added to this object...
|
||||
var correctValues = SettingsManager.Get<StatsSettings>().allValues
|
||||
.Where(value => value)
|
||||
.Where(value => value.attachedTo.type.IsAssignableFrom(parent.GetType()))
|
||||
.ToList();
|
||||
|
||||
//... but also remember that some of them can override other's, overriden ones should not be here
|
||||
correctStats.Select(stat => stat.overrides)
|
||||
.Where(overridenStat => overridenStat)
|
||||
.ForEach(overridenStat => correctStats.Remove(overridenStat));
|
||||
correctValues.Select(value => value.overrides)
|
||||
.Where(overridenValue => overridenValue)
|
||||
.ForEach(overridenValue => correctValues.Remove(overridenValue));
|
||||
|
||||
// ADD MISSING
|
||||
var missingStats = correctStats.Except(_serializedStats.Keys).ToList();
|
||||
if (missingStats.Any())
|
||||
var missingValues = correctValues.Except(_serializedValues.Keys).ToList();
|
||||
if (missingValues.Any())
|
||||
{
|
||||
missingStats.ForEach(missingStat => _serializedStats.Add(missingStat, 0));
|
||||
var missingStatsString = string.Join(", ", missingStats.Select(s => s.name));
|
||||
Debug.LogWarning($"[StatsModule] automatically adding missing stats: {missingStatsString}", parent);
|
||||
missingValues.ForEach(missingValue => _serializedValues.Add(missingValue, 0));
|
||||
var missingValuesString = string.Join(", ", missingValues.Select(s => s.name));
|
||||
Debug.LogWarning($"[StatsModule] automatically adding missing stats: {missingValuesString}", parent);
|
||||
UnityEditor.EditorUtility.SetDirty(parent.gameObject);
|
||||
}
|
||||
|
||||
// REMOVE INVALID
|
||||
var invalidStats = _serializedStats.Keys.Except(correctStats).ToList();
|
||||
if (invalidStats.Any())
|
||||
var invalidValues = _serializedValues.Keys.Except(correctValues).ToList();
|
||||
if (invalidValues.Any())
|
||||
{
|
||||
invalidStats.ForEach(invalidStat => _serializedStats.Remove(invalidStat));
|
||||
var invalidStatsString = string.Join(", ", invalidStats.Select(missingStat => missingStat.name));
|
||||
Debug.LogWarning($"[StatsModule] automatically removing invalid stats: {invalidStatsString}", parent);
|
||||
invalidValues.ForEach(invalidValue => _serializedValues.Remove(invalidValue));
|
||||
var invalidValuesString = string.Join(", ", invalidValues.Select(missingValue => missingValue.name));
|
||||
Debug.LogWarning($"[StatsModule] automatically removing invalid values: {invalidValuesString}", parent);
|
||||
UnityEditor.EditorUtility.SetDirty(parent.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
private void FixSerializedStatResources()
|
||||
{
|
||||
// fixing only when variable is serialized (sometimes OnValidate can be called before deserializing on project open)
|
||||
if (_serializedResources == null) return;
|
||||
|
||||
// All stat definitions that should be added to this object...
|
||||
var correctResources = SettingsManager.Get<StatsSettings>().allResources
|
||||
.Where(resource => resource)
|
||||
.Where(resource => resource.valueDefinitionSO.attachedTo.type.IsAssignableFrom(parent.GetType()))
|
||||
.ToList();
|
||||
|
||||
//... but also remember that some of them can override other's, overriden ones should not be here
|
||||
var baseValues = SettingsManager.Get<StatsSettings>().allValues
|
||||
.Where(value => value)
|
||||
.Where(value => !value.overrides)
|
||||
.ToList();
|
||||
correctResources
|
||||
.Where(resource => !baseValues.Contains(resource.valueDefinitionSO))
|
||||
.ForEach(resource => correctResources.Remove(resource));
|
||||
|
||||
// ADD MISSING
|
||||
var missingResources = correctResources.Except(_serializedResources.Keys).ToList();
|
||||
if (missingResources.Any())
|
||||
{
|
||||
missingResources.ForEach(missingResource => _serializedResources.Add(missingResource, 0));
|
||||
var missingResourcesString = string.Join(", ", missingResources.Select(s => s.name));
|
||||
Debug.LogWarning($"[StatsModule] automatically adding missing resources: {missingResourcesString}", parent);
|
||||
UnityEditor.EditorUtility.SetDirty(parent.gameObject);
|
||||
}
|
||||
|
||||
// REMOVE INVALID
|
||||
var invalidResources = _serializedResources.Keys.Except(correctResources).ToList();
|
||||
if (invalidResources.Any())
|
||||
{
|
||||
invalidResources.ForEach(invalidResource => _serializedResources.Remove(invalidResource));
|
||||
var invalidResourcesString = string.Join(", ", invalidResources.Select(missingStat => missingStat.name));
|
||||
Debug.LogWarning($"[StatsModule] automatically removing invalid resources: {invalidResourcesString}", parent);
|
||||
UnityEditor.EditorUtility.SetDirty(parent.gameObject);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,19 +123,20 @@ namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
||||
[DynamicValueProvider]
|
||||
internal StatValue StatValueProvider(string name)
|
||||
{
|
||||
return _stats.GetValueOrDefault(_stats.Keys.FirstOrDefault(def => def.name == name));
|
||||
return _values.GetValueOrDefault(_values.Keys.FirstOrDefault(def => def.name == name));
|
||||
}
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
// Create instances of all stats that will be used runtime
|
||||
CreateStats();
|
||||
CreateResources();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
// First stats refresh
|
||||
_stats.Keys.ForEach(AddToQueueRefresh);
|
||||
_values.Keys.ForEach(AddToQueueRefresh);
|
||||
QueueRefresh();
|
||||
}
|
||||
|
||||
@@ -93,32 +146,55 @@ namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Really important part - we create stats for runtime usage.
|
||||
/// <see cref="StatDefinitionSO"/> can also override another one, in that case <see cref="StatValue"/>
|
||||
/// uses main definition, but it is visible as that overriden one.
|
||||
/// Really important part - we create values for runtime usage.
|
||||
/// </summary>
|
||||
private void CreateStats()
|
||||
{
|
||||
_serializedStats.DictForEach((statDefinition, baseValue) =>
|
||||
_serializedValues.DictForEach((definition, baseValue) =>
|
||||
{
|
||||
var baseDefinition = statDefinition.GetBaseDefinition();
|
||||
var statValue = new StatValue(statDefinition, baseValue, parent);
|
||||
statValue.sourceContext.SetSource("object", parent);
|
||||
_stats.Add(baseDefinition, statValue);
|
||||
var baseDefinition = definition.GetBaseDefinition();
|
||||
var value = new StatValue(definition, baseValue, parent);
|
||||
value.sources.SetSource("object", parent);
|
||||
value.sources.SetSource("stats", this);
|
||||
_values.Add(baseDefinition, value);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Really important part - we create resources for runtime usage.
|
||||
/// </summary>
|
||||
private void CreateResources()
|
||||
{
|
||||
_serializedResources.DictForEach((definition, startingResource) =>
|
||||
{
|
||||
_resources.Add(
|
||||
definition,
|
||||
new StatResource(definition, startingResource, Get(definition.valueDefinitionSO))
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns runtime values for given stat definition.
|
||||
/// </summary>
|
||||
/// <param name="statDefinitionSO">Runtime stats will be found by this definition (or overriden definition if given)</param>
|
||||
/// <returns>Stat values (baseValue, value, resource)</returns>
|
||||
public StatValue Get(StatDefinitionSO statDefinitionSO)
|
||||
/// <param name="valueDefinitionSO">Runtime stats will be found by this definition (or overriden definition if given)</param>
|
||||
/// <returns>Stat value (baseValue, value)</returns>
|
||||
public StatValue Get(StatValueDefinitionSO valueDefinitionSO)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!UnityEditor.EditorApplication.isPlaying) CreateStats();
|
||||
#endif
|
||||
return _stats.GetValueOrDefault(statDefinitionSO.GetBaseDefinition());
|
||||
return _values.GetValueOrDefault(valueDefinitionSO.GetBaseDefinition());
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="Get(StatValueDefinitionSO)"/>
|
||||
/// <returns>Stat resource (baseValue, value, resource)</returns>
|
||||
public StatResource Get(StatResourceDefinitionSO resourceDefinitionSO)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!UnityEditor.EditorApplication.isPlaying) CreateResources();
|
||||
#endif
|
||||
return _resources.GetValueOrDefault(resourceDefinitionSO);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -136,9 +212,9 @@ namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Select <see cref="StatDefinitionSO"/> along with its dependencies to refresh next frame.
|
||||
/// Select <see cref="StatValueDefinitionSO"/> along with its dependencies to refresh next frame.
|
||||
/// </summary>
|
||||
public void AddToQueueRefresh(StatDefinitionSO statDefinitionSO)
|
||||
public void AddToQueueRefresh(StatValueDefinitionSO statDefinitionSO)
|
||||
{
|
||||
statDefinitionSO = statDefinitionSO.GetBaseDefinition();
|
||||
|
||||
@@ -146,7 +222,7 @@ namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
||||
if (!_queue.Contains(statDefinitionSO)) _queue.Add(statDefinitionSO);
|
||||
|
||||
// All definitions that uses this one as dependency should be refreshed too
|
||||
_stats.Keys
|
||||
_values.Keys
|
||||
.Where(otherStatDefinition => otherStatDefinition.dependencies.Contains(statDefinitionSO))
|
||||
.ForEach(AddToQueueRefresh);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
||||
[CustomSettings("RPG Core/Statistics")]
|
||||
public class StatsSettings : CustomSettingsSO
|
||||
{
|
||||
public List<StatDefinitionSO> allStats = new();
|
||||
public List<StatValueDefinitionSO> allValues = new();
|
||||
public List<StatResourceDefinitionSO> allResources = new();
|
||||
}
|
||||
}
|
||||
@@ -5,8 +5,7 @@
|
||||
"RPGCore",
|
||||
"RPGCoreCommon.Settings",
|
||||
"RPGCoreCommon.Helpers",
|
||||
"RPGCoreCommon.DynamicValues",
|
||||
"Unity.InputSystem"
|
||||
"RPGCoreCommon.DynamicValues"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c7b1ac9d0b5844eb9b100a90eb31a295
|
||||
timeCreated: 1782147016
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7f0cd35538794fcbae68ff51704ff0bc
|
||||
timeCreated: 1782147025
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8e8a8415cb82457080457ed37759632f
|
||||
timeCreated: 1782147041
|
||||
@@ -0,0 +1,21 @@
|
||||
{
|
||||
"name": "RPGCore.Stats2Status.Editor",
|
||||
"rootNamespace": "RPGCore.Stats2Status.Editor",
|
||||
"references": [
|
||||
"RPGCore",
|
||||
"RPGCore.Editor",
|
||||
"RPGCore.Stats2Status",
|
||||
"RPGCoreCommon.Helpers"
|
||||
],
|
||||
"includePlatforms": [
|
||||
"Editor"
|
||||
],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3fcd02a1357a446aa9f506aa36f731c6
|
||||
timeCreated: 1782147041
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2ea364d5fbbb4f649d4ea50f61b060d9
|
||||
timeCreated: 1782147025
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b1c1cd6791c74c87b717977a72381acc
|
||||
timeCreated: 1782151180
|
||||
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using RPGCore.Stats.ObjectModules.StatsObjectModule;
|
||||
using RPGCore.StatusEffect.ObjectModules.StatusObjectModule;
|
||||
|
||||
namespace RPGCore.Stats2Status.Effects
|
||||
{
|
||||
public class ResourceChangeEffect : BaseEffect
|
||||
{
|
||||
public enum ResourceAmountType
|
||||
{
|
||||
Flat,
|
||||
PercentOfValue,
|
||||
PercentOfResource
|
||||
}
|
||||
|
||||
public StatResourceDefinitionSO resourceDefinitionSO;
|
||||
public ResourceAmountType amountType;
|
||||
public int amount;
|
||||
|
||||
protected override void OnApply()
|
||||
{
|
||||
var resource = status.target.GetComponent<StatsModule>().Get(resourceDefinitionSO);
|
||||
var value = resource.statValue;
|
||||
|
||||
switch (amountType)
|
||||
{
|
||||
case ResourceAmountType.Flat:
|
||||
resource.ChangeResource(amount);
|
||||
break;
|
||||
case ResourceAmountType.PercentOfValue:
|
||||
resource.ChangeResource(amount * value.value / 100);
|
||||
break;
|
||||
case ResourceAmountType.PercentOfResource:
|
||||
resource.ChangeResource(amount * resource.resource / 100);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnEnd()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 19741604c0ef4f87a5d7b27a7e62c538
|
||||
timeCreated: 1782332472
|
||||
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using RPGCore.Stats.ObjectModules.StatsObjectModule;
|
||||
using RPGCore.StatusEffect.ObjectModules.StatusObjectModule;
|
||||
using UnityEngine;
|
||||
|
||||
namespace RPGCore.Stats2Status.Effects
|
||||
{
|
||||
[Serializable]
|
||||
public class ValueChangeEffect : BaseEffect
|
||||
{
|
||||
[SerializeField] public StatValueDefinitionSO valueDefinitionSO;
|
||||
[SerializeField] public StatModifierType amountType;
|
||||
[SerializeField] public int amount;
|
||||
|
||||
private StatValue _value;
|
||||
private StatModifier _modifier;
|
||||
|
||||
protected override void OnApply()
|
||||
{
|
||||
_value = status.target.GetComponent<StatsModule>().Get(valueDefinitionSO);
|
||||
_modifier = new StatModifier(status.target, amount, amountType);
|
||||
_value.modifiers.Add(_modifier);
|
||||
}
|
||||
|
||||
protected override void OnEnd()
|
||||
{
|
||||
_value.modifiers.Remove(_modifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 473957f15aa24486b03bd37fa8ef922d
|
||||
timeCreated: 1782150941
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "RPGCore.Stats2Status",
|
||||
"rootNamespace": "RPGCore.Stats2Status",
|
||||
"references": [
|
||||
"RPGCore",
|
||||
"RPGCore.Stats",
|
||||
"RPGCore.StatusEffect"
|
||||
],
|
||||
"includePlatforms": [],
|
||||
"excludePlatforms": [],
|
||||
"allowUnsafeCode": false,
|
||||
"overrideReferences": false,
|
||||
"precompiledReferences": [],
|
||||
"autoReferenced": true,
|
||||
"defineConstraints": [],
|
||||
"versionDefines": [],
|
||||
"noEngineReferences": false
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7b55415f0e2b4b3a9545c2390bd012f2
|
||||
timeCreated: 1782147025
|
||||
Reference in New Issue
Block a user