[#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 class StatResourceChangeEvent : BaseEvent<BaseObject>
|
||||||
{
|
{
|
||||||
public BaseObject target;
|
public BaseObject target;
|
||||||
public StatDefinitionSO statDefinition;
|
public StatResourceDefinitionSO resourceDefinition;
|
||||||
public int delta;
|
public int delta;
|
||||||
public int before;
|
public int before;
|
||||||
public int after;
|
public int after;
|
||||||
|
|||||||
+1
-1
@@ -6,7 +6,7 @@ namespace RPGCore.Stats.ObjectModules.StatsObjectModule.Events
|
|||||||
public class StatValueChangeEvent : BaseEvent<BaseObject>
|
public class StatValueChangeEvent : BaseEvent<BaseObject>
|
||||||
{
|
{
|
||||||
public BaseObject target;
|
public BaseObject target;
|
||||||
public StatDefinitionSO statDefinition;
|
public StatValueDefinitionSO valueDefinition;
|
||||||
public int before;
|
public int before;
|
||||||
public int after;
|
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.Core.Objects;
|
||||||
using RPGCore.ObjectModules.EventObjectModule;
|
|
||||||
using RPGCore.Stats.ObjectModules.StatsObjectModule.Events;
|
using RPGCore.Stats.ObjectModules.StatsObjectModule.Events;
|
||||||
using RPGCoreCommon.DynamicValues;
|
using RPGCoreCommon.DynamicValues;
|
||||||
using UnityEngine;
|
|
||||||
|
|
||||||
namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <b>You should never instantiate this by yourself!</b>
|
/// <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>
|
/// </summary>
|
||||||
public sealed class StatValue
|
public sealed class StatValue
|
||||||
{
|
{
|
||||||
public readonly StatDefinitionSO definition;
|
public readonly StatValueDefinitionSO definition;
|
||||||
private readonly BaseObject _parent;
|
public readonly BaseObject obj;
|
||||||
public readonly DynamicValueSourceContext sourceContext = new();
|
public readonly DynamicValueSourceContext sources = new();
|
||||||
|
public readonly StatModifierContext modifiers = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Base value defined in <see cref="StatsModule"/> attached to <see cref="BaseObject"/>.
|
/// 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; }
|
public int baseValue { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// This value is calculated by <see cref="DynamicValue"/> in <see cref="StatDefinitionSO"/> and can have two meanings:<br/>
|
/// This value is calculated by <see cref="DynamicValue"/> in <see cref="StatValueDefinitionSO"/>
|
||||||
/// 1. If <see cref="StatType.Value"/> - just simple value stat<br/>
|
|
||||||
/// 2. If <see cref="StatType.Resource"/> - this is maximum value for that resource
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public int value { get; private set; }
|
public int value { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
internal StatValue(StatValueDefinitionSO definition, int baseValue, BaseObject obj)
|
||||||
/// 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)
|
|
||||||
{
|
{
|
||||||
this.definition = definition;
|
this.definition = definition;
|
||||||
_parent = parent;
|
this.obj = obj;
|
||||||
this.baseValue = baseValue;
|
this.baseValue = baseValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,13 +40,14 @@ namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
|||||||
internal void Refresh()
|
internal void Refresh()
|
||||||
{
|
{
|
||||||
var previous = value;
|
var previous = value;
|
||||||
value = baseValue + definition.dynamicValue.GetValue(sourceContext);
|
value = definition.dynamicValue.GetValue(sources);
|
||||||
_parent.events.Invoke(new StatValueChangeEvent
|
if (previous == value) return;
|
||||||
|
obj.events.Invoke(new StatValueChangeEvent
|
||||||
{
|
{
|
||||||
after = value,
|
after = value,
|
||||||
before = previous,
|
before = previous,
|
||||||
statDefinition = definition,
|
valueDefinition = definition,
|
||||||
target = _parent
|
target = obj
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -65,45 +57,9 @@ namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
|||||||
/// <param name="amount">New base value</param>
|
/// <param name="amount">New base value</param>
|
||||||
public void SetBase(int amount)
|
public void SetBase(int amount)
|
||||||
{
|
{
|
||||||
|
if (baseValue == amount) return;
|
||||||
baseValue = amount;
|
baseValue = amount;
|
||||||
Refresh();
|
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
|
namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
||||||
{
|
{
|
||||||
[CreateAssetMenu(menuName = "RPG Core/Object Stat Definition")]
|
[CreateAssetMenu(menuName = "RPG Core/Object Value Definition")]
|
||||||
public sealed class StatDefinitionSO : ScriptableObject
|
public class StatValueDefinitionSO : ScriptableObject
|
||||||
{
|
{
|
||||||
[SerializableType(typeof(BaseObject), allowAbstract: true)] public SerializableType attachedTo;
|
[SerializableType(typeof(BaseObject), allowAbstract: true)] public SerializableType attachedTo;
|
||||||
public StatType type;
|
public StatValueDefinitionSO overrides;
|
||||||
public StatDefinitionSO overrides;
|
[ReadOnly(true)] public StatValueDefinitionSO[] dependencies = {};
|
||||||
[ReadOnly(true)] public StatDefinitionSO[] dependencies = {};
|
|
||||||
|
|
||||||
[DynamicValue(DynamicValueType.ByDynamicTypes)] public DynamicValue dynamicValue = new();
|
[DynamicValue(DynamicValueType.ByDynamicTypes)] public DynamicValue dynamicValue = new();
|
||||||
|
|
||||||
@@ -35,6 +34,7 @@ namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
|||||||
{
|
{
|
||||||
dynamicValue.RemoveDynamicTypes();
|
dynamicValue.RemoveDynamicTypes();
|
||||||
dynamicValue.SetDynamicType("object", attachedTo);
|
dynamicValue.SetDynamicType("object", attachedTo);
|
||||||
|
dynamicValue.SetDynamicType("stats", typeof(StatsModule));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FixOverride()
|
private void FixOverride()
|
||||||
@@ -58,19 +58,26 @@ namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
|||||||
return;
|
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;
|
name = overrides.name;
|
||||||
type = overrides.type;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// <see cref="StatsModule"/> has custom value provider <see cref="StatsModule.StatValueProvider"/> that matches stats by their name.
|
/// <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.
|
/// Main purpose of dependencies is caching, specially when there is a lot of definitions and dependencies.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void FixDependencies()
|
private void FixDependencies()
|
||||||
{
|
{
|
||||||
var tempDependencies = new List<StatDefinitionSO>();
|
var tempDependencies = new List<StatValueDefinitionSO>();
|
||||||
var allDefinitions = SettingsManager.Get<StatsSettings>().allStats;
|
var allDefinitions = SettingsManager.Get<StatsSettings>().allValues;
|
||||||
|
|
||||||
foreach (var marker in dynamicValue.GetMarkers())
|
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!
|
/// If looping is found then dependencies will be cleared, preventing application freeze!
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="definitionsChain"></param>
|
/// <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))
|
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(" -> ");
|
var definitionsChainString = definitionsChain.Append(this).Select(d => d.name).StringJoin(" -> ");
|
||||||
Debug.LogError($"Looped dependencies found! {definitionsChainString}", this);
|
Debug.LogError($"Looped dependencies found! {definitionsChainString}", this);
|
||||||
return;
|
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.
|
/// Thanks to OnValidate its impossible to create endless overriding loop, it is possible to override only child object.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>Last overriden definition</returns>
|
/// <returns>Last overriden definition</returns>
|
||||||
public StatDefinitionSO GetBaseDefinition()
|
public StatValueDefinitionSO GetBaseDefinition()
|
||||||
{
|
{
|
||||||
var tempDefinition = this;
|
var tempDefinition = this;
|
||||||
while (tempDefinition.overrides)
|
while (tempDefinition.overrides)
|
||||||
@@ -12,57 +12,109 @@ using UnityEngine;
|
|||||||
namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
||||||
{
|
{
|
||||||
[Serializable]
|
[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>
|
public class StatsModule : ObjectModule<BaseObject>
|
||||||
{
|
{
|
||||||
[SerializeField]
|
[SerializeField]
|
||||||
[SerializableDictionary("Stat Definition", "Base Value", isKeyEditable: false)]
|
[SerializableDictionary("Value Definition", "Base Value", isKeyEditable: false)]
|
||||||
private SerializableDictionary<StatDefinitionSO, int> _serializedStats;
|
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 Dictionary<StatValueDefinitionSO, StatValue> _values = new();
|
||||||
private List<StatDefinitionSO> _queue = new();
|
private Dictionary<StatResourceDefinitionSO, StatResource> _resources = new();
|
||||||
|
private List<StatValueDefinitionSO> _queue = new();
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
|
|
||||||
private void OnValidate()
|
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)
|
// 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...
|
// All value definitions that should be added to this object...
|
||||||
var correctStats = SettingsManager.Get<StatsSettings>().allStats
|
var correctValues = SettingsManager.Get<StatsSettings>().allValues
|
||||||
.Where(stat => stat)
|
.Where(value => value)
|
||||||
.Where(stat => stat.attachedTo.type.IsAssignableFrom(parent.GetType()))
|
.Where(value => value.attachedTo.type.IsAssignableFrom(parent.GetType()))
|
||||||
.ToList();
|
.ToList();
|
||||||
|
|
||||||
//... but also remember that some of them can override other's, overriden ones should not be here
|
//... but also remember that some of them can override other's, overriden ones should not be here
|
||||||
correctStats.Select(stat => stat.overrides)
|
correctValues.Select(value => value.overrides)
|
||||||
.Where(overridenStat => overridenStat)
|
.Where(overridenValue => overridenValue)
|
||||||
.ForEach(overridenStat => correctStats.Remove(overridenStat));
|
.ForEach(overridenValue => correctValues.Remove(overridenValue));
|
||||||
|
|
||||||
// ADD MISSING
|
// ADD MISSING
|
||||||
var missingStats = correctStats.Except(_serializedStats.Keys).ToList();
|
var missingValues = correctValues.Except(_serializedValues.Keys).ToList();
|
||||||
if (missingStats.Any())
|
if (missingValues.Any())
|
||||||
{
|
{
|
||||||
missingStats.ForEach(missingStat => _serializedStats.Add(missingStat, 0));
|
missingValues.ForEach(missingValue => _serializedValues.Add(missingValue, 0));
|
||||||
var missingStatsString = string.Join(", ", missingStats.Select(s => s.name));
|
var missingValuesString = string.Join(", ", missingValues.Select(s => s.name));
|
||||||
Debug.LogWarning($"[StatsModule] automatically adding missing stats: {missingStatsString}", parent);
|
Debug.LogWarning($"[StatsModule] automatically adding missing stats: {missingValuesString}", parent);
|
||||||
UnityEditor.EditorUtility.SetDirty(parent.gameObject);
|
UnityEditor.EditorUtility.SetDirty(parent.gameObject);
|
||||||
}
|
}
|
||||||
|
|
||||||
// REMOVE INVALID
|
// REMOVE INVALID
|
||||||
var invalidStats = _serializedStats.Keys.Except(correctStats).ToList();
|
var invalidValues = _serializedValues.Keys.Except(correctValues).ToList();
|
||||||
if (invalidStats.Any())
|
if (invalidValues.Any())
|
||||||
{
|
{
|
||||||
invalidStats.ForEach(invalidStat => _serializedStats.Remove(invalidStat));
|
invalidValues.ForEach(invalidValue => _serializedValues.Remove(invalidValue));
|
||||||
var invalidStatsString = string.Join(", ", invalidStats.Select(missingStat => missingStat.name));
|
var invalidValuesString = string.Join(", ", invalidValues.Select(missingValue => missingValue.name));
|
||||||
Debug.LogWarning($"[StatsModule] automatically removing invalid stats: {invalidStatsString}", parent);
|
Debug.LogWarning($"[StatsModule] automatically removing invalid values: {invalidValuesString}", parent);
|
||||||
UnityEditor.EditorUtility.SetDirty(parent.gameObject);
|
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]
|
[DynamicValueProvider]
|
||||||
internal StatValue StatValueProvider(string name)
|
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()
|
private void Awake()
|
||||||
{
|
{
|
||||||
// Create instances of all stats that will be used runtime
|
// Create instances of all stats that will be used runtime
|
||||||
CreateStats();
|
CreateStats();
|
||||||
|
CreateResources();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Start()
|
private void Start()
|
||||||
{
|
{
|
||||||
// First stats refresh
|
// First stats refresh
|
||||||
_stats.Keys.ForEach(AddToQueueRefresh);
|
_values.Keys.ForEach(AddToQueueRefresh);
|
||||||
QueueRefresh();
|
QueueRefresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -93,32 +146,55 @@ namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Really important part - we create stats for runtime usage.
|
/// Really important part - we create values 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.
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void CreateStats()
|
private void CreateStats()
|
||||||
{
|
{
|
||||||
_serializedStats.DictForEach((statDefinition, baseValue) =>
|
_serializedValues.DictForEach((definition, baseValue) =>
|
||||||
{
|
{
|
||||||
var baseDefinition = statDefinition.GetBaseDefinition();
|
var baseDefinition = definition.GetBaseDefinition();
|
||||||
var statValue = new StatValue(statDefinition, baseValue, parent);
|
var value = new StatValue(definition, baseValue, parent);
|
||||||
statValue.sourceContext.SetSource("object", parent);
|
value.sources.SetSource("object", parent);
|
||||||
_stats.Add(baseDefinition, statValue);
|
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>
|
/// <summary>
|
||||||
/// Returns runtime values for given stat definition.
|
/// Returns runtime values for given stat definition.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="statDefinitionSO">Runtime stats will be found by this definition (or overriden definition if given)</param>
|
/// <param name="valueDefinitionSO">Runtime stats will be found by this definition (or overriden definition if given)</param>
|
||||||
/// <returns>Stat values (baseValue, value, resource)</returns>
|
/// <returns>Stat value (baseValue, value)</returns>
|
||||||
public StatValue Get(StatDefinitionSO statDefinitionSO)
|
public StatValue Get(StatValueDefinitionSO valueDefinitionSO)
|
||||||
{
|
{
|
||||||
#if UNITY_EDITOR
|
#if UNITY_EDITOR
|
||||||
if (!UnityEditor.EditorApplication.isPlaying) CreateStats();
|
if (!UnityEditor.EditorApplication.isPlaying) CreateStats();
|
||||||
#endif
|
#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>
|
/// <summary>
|
||||||
@@ -136,9 +212,9 @@ namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <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>
|
/// </summary>
|
||||||
public void AddToQueueRefresh(StatDefinitionSO statDefinitionSO)
|
public void AddToQueueRefresh(StatValueDefinitionSO statDefinitionSO)
|
||||||
{
|
{
|
||||||
statDefinitionSO = statDefinitionSO.GetBaseDefinition();
|
statDefinitionSO = statDefinitionSO.GetBaseDefinition();
|
||||||
|
|
||||||
@@ -146,7 +222,7 @@ namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
|||||||
if (!_queue.Contains(statDefinitionSO)) _queue.Add(statDefinitionSO);
|
if (!_queue.Contains(statDefinitionSO)) _queue.Add(statDefinitionSO);
|
||||||
|
|
||||||
// All definitions that uses this one as dependency should be refreshed too
|
// All definitions that uses this one as dependency should be refreshed too
|
||||||
_stats.Keys
|
_values.Keys
|
||||||
.Where(otherStatDefinition => otherStatDefinition.dependencies.Contains(statDefinitionSO))
|
.Where(otherStatDefinition => otherStatDefinition.dependencies.Contains(statDefinitionSO))
|
||||||
.ForEach(AddToQueueRefresh);
|
.ForEach(AddToQueueRefresh);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ namespace RPGCore.Stats.ObjectModules.StatsObjectModule
|
|||||||
[CustomSettings("RPG Core/Statistics")]
|
[CustomSettings("RPG Core/Statistics")]
|
||||||
public class StatsSettings : CustomSettingsSO
|
public class StatsSettings : CustomSettingsSO
|
||||||
{
|
{
|
||||||
public List<StatDefinitionSO> allStats = new();
|
public List<StatValueDefinitionSO> allValues = new();
|
||||||
|
public List<StatResourceDefinitionSO> allResources = new();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -5,8 +5,7 @@
|
|||||||
"RPGCore",
|
"RPGCore",
|
||||||
"RPGCoreCommon.Settings",
|
"RPGCoreCommon.Settings",
|
||||||
"RPGCoreCommon.Helpers",
|
"RPGCoreCommon.Helpers",
|
||||||
"RPGCoreCommon.DynamicValues",
|
"RPGCoreCommon.DynamicValues"
|
||||||
"Unity.InputSystem"
|
|
||||||
],
|
],
|
||||||
"includePlatforms": [],
|
"includePlatforms": [],
|
||||||
"excludePlatforms": [],
|
"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