[#14] rozbudowa stats + dodanie modyfikatorów

This commit is contained in:
2026-06-30 20:28:51 +02:00
parent 86b4a8a93f
commit 8326c4b279
33 changed files with 444 additions and 129 deletions
@@ -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;
@@ -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>
/// 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; 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));
}
} }
} }
@@ -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);
}
}
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); 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();
} }
} }
+1 -2
View File
@@ -5,8 +5,7 @@
"RPGCore", "RPGCore",
"RPGCoreCommon.Settings", "RPGCoreCommon.Settings",
"RPGCoreCommon.Helpers", "RPGCoreCommon.Helpers",
"RPGCoreCommon.DynamicValues", "RPGCoreCommon.DynamicValues"
"Unity.InputSystem"
], ],
"includePlatforms": [], "includePlatforms": [],
"excludePlatforms": [], "excludePlatforms": [],
+3
View File
@@ -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
}
@@ -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