From 8326c4b2796ab72a2315a552c8dcef2ee7fa30d4 Mon Sep 17 00:00:00 2001 From: TheVVaS Date: Tue, 30 Jun 2026 20:28:51 +0200 Subject: [PATCH] =?UTF-8?q?[#14]=20rozbudowa=20stats=20+=20dodanie=20modyf?= =?UTF-8?q?ikator=C3=B3w?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Events/StatResourceChangeEvent.cs | 2 +- .../Events/StatValueChangeEvent.cs | 2 +- .../StatsObjectModule/StatModifier.cs | 18 ++ .../StatsObjectModule/StatModifier.cs.meta | 3 + .../StatsObjectModule/StatModifierContext.cs | 30 ++++ .../StatModifierContext.cs.meta | 3 + .../StatsObjectModule/StatModifierType.cs | 9 + .../StatModifierType.cs.meta | 3 + .../StatsObjectModule/StatResource.cs | 64 +++++++ .../StatsObjectModule/StatResource.cs.meta | 3 + .../StatResourceDefinitionSO.cs | 10 ++ .../StatResourceDefinitionSO.cs.meta | 3 + .../StatsObjectModule/StatType.cs | 8 - .../StatsObjectModule/StatType.cs.meta | 3 - .../StatsObjectModule/StatValue.cs | 72 ++------ ...finitionSO.cs => StatValueDefinitionSO.cs} | 33 ++-- ....cs.meta => StatValueDefinitionSO.cs.meta} | 0 .../StatsObjectModule/StatsModule.cs | 160 +++++++++++++----- .../StatsObjectModule/StatsSettings.cs | 3 +- RPGCore.Stats/Runtime/RPGCore.Stats.asmdef | 3 +- RPGCore.Stats/_integrations.meta | 3 + RPGCore.Stats/_integrations/Stats2Status.meta | 3 + .../_integrations/Stats2Status/Editor.meta | 3 + .../Editor/RPGCore.Stats2Status.Editor.asmdef | 21 +++ .../RPGCore.Stats2Status.Editor.asmdef.meta | 3 + .../_integrations/Stats2Status/Runtime.meta | 3 + .../Stats2Status/Runtime/Effects.meta | 3 + .../Runtime/Effects/ResourceChangeEffect.cs | 45 +++++ .../Effects/ResourceChangeEffect.cs.meta | 3 + .../Runtime/Effects/ValueChangeEffect.cs | 30 ++++ .../Runtime/Effects/ValueChangeEffect.cs.meta | 3 + .../Runtime/RPGCore.Stats2Status.asmdef | 18 ++ .../Runtime/RPGCore.Stats2Status.asmdef.meta | 3 + 33 files changed, 444 insertions(+), 129 deletions(-) create mode 100644 RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatModifier.cs create mode 100644 RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatModifier.cs.meta create mode 100644 RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatModifierContext.cs create mode 100644 RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatModifierContext.cs.meta create mode 100644 RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatModifierType.cs create mode 100644 RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatModifierType.cs.meta create mode 100644 RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatResource.cs create mode 100644 RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatResource.cs.meta create mode 100644 RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatResourceDefinitionSO.cs create mode 100644 RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatResourceDefinitionSO.cs.meta delete mode 100644 RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatType.cs delete mode 100644 RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatType.cs.meta rename RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/{StatDefinitionSO.cs => StatValueDefinitionSO.cs} (82%) rename RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/{StatDefinitionSO.cs.meta => StatValueDefinitionSO.cs.meta} (100%) create mode 100644 RPGCore.Stats/_integrations.meta create mode 100644 RPGCore.Stats/_integrations/Stats2Status.meta create mode 100644 RPGCore.Stats/_integrations/Stats2Status/Editor.meta create mode 100644 RPGCore.Stats/_integrations/Stats2Status/Editor/RPGCore.Stats2Status.Editor.asmdef create mode 100644 RPGCore.Stats/_integrations/Stats2Status/Editor/RPGCore.Stats2Status.Editor.asmdef.meta create mode 100644 RPGCore.Stats/_integrations/Stats2Status/Runtime.meta create mode 100644 RPGCore.Stats/_integrations/Stats2Status/Runtime/Effects.meta create mode 100644 RPGCore.Stats/_integrations/Stats2Status/Runtime/Effects/ResourceChangeEffect.cs create mode 100644 RPGCore.Stats/_integrations/Stats2Status/Runtime/Effects/ResourceChangeEffect.cs.meta create mode 100644 RPGCore.Stats/_integrations/Stats2Status/Runtime/Effects/ValueChangeEffect.cs create mode 100644 RPGCore.Stats/_integrations/Stats2Status/Runtime/Effects/ValueChangeEffect.cs.meta create mode 100644 RPGCore.Stats/_integrations/Stats2Status/Runtime/RPGCore.Stats2Status.asmdef create mode 100644 RPGCore.Stats/_integrations/Stats2Status/Runtime/RPGCore.Stats2Status.asmdef.meta diff --git a/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/Events/StatResourceChangeEvent.cs b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/Events/StatResourceChangeEvent.cs index 32ef9e5..9966d81 100644 --- a/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/Events/StatResourceChangeEvent.cs +++ b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/Events/StatResourceChangeEvent.cs @@ -6,7 +6,7 @@ namespace RPGCore.Stats.ObjectModules.StatsObjectModule.Events public class StatResourceChangeEvent : BaseEvent { public BaseObject target; - public StatDefinitionSO statDefinition; + public StatResourceDefinitionSO resourceDefinition; public int delta; public int before; public int after; diff --git a/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/Events/StatValueChangeEvent.cs b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/Events/StatValueChangeEvent.cs index 40d87c1..cb2615a 100644 --- a/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/Events/StatValueChangeEvent.cs +++ b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/Events/StatValueChangeEvent.cs @@ -6,7 +6,7 @@ namespace RPGCore.Stats.ObjectModules.StatsObjectModule.Events public class StatValueChangeEvent : BaseEvent { public BaseObject target; - public StatDefinitionSO statDefinition; + public StatValueDefinitionSO valueDefinition; public int before; public int after; } diff --git a/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatModifier.cs b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatModifier.cs new file mode 100644 index 0000000..ae30f59 --- /dev/null +++ b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatModifier.cs @@ -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; + } + } +} \ No newline at end of file diff --git a/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatModifier.cs.meta b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatModifier.cs.meta new file mode 100644 index 0000000..e688d4f --- /dev/null +++ b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatModifier.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 1347462f6c9444359615c903e5749bd3 +timeCreated: 1782736826 \ No newline at end of file diff --git a/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatModifierContext.cs b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatModifierContext.cs new file mode 100644 index 0000000..7ffee34 --- /dev/null +++ b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatModifierContext.cs @@ -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 _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); + } + } +} \ No newline at end of file diff --git a/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatModifierContext.cs.meta b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatModifierContext.cs.meta new file mode 100644 index 0000000..dce6534 --- /dev/null +++ b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatModifierContext.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 53790325b2504e468bd5452be43d9ac8 +timeCreated: 1782738303 \ No newline at end of file diff --git a/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatModifierType.cs b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatModifierType.cs new file mode 100644 index 0000000..1f608c5 --- /dev/null +++ b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatModifierType.cs @@ -0,0 +1,9 @@ +namespace RPGCore.Stats.ObjectModules.StatsObjectModule +{ + public enum StatModifierType + { + Add, + Multiply, + Override, + } +} \ No newline at end of file diff --git a/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatModifierType.cs.meta b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatModifierType.cs.meta new file mode 100644 index 0000000..0da537f --- /dev/null +++ b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatModifierType.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: ba280aecb8764285be02cd44827226cb +timeCreated: 1782736851 \ No newline at end of file diff --git a/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatResource.cs b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatResource.cs new file mode 100644 index 0000000..b2a14cd --- /dev/null +++ b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatResource.cs @@ -0,0 +1,64 @@ +using RPGCore.Stats.ObjectModules.StatsObjectModule.Events; +using UnityEngine; + +namespace RPGCore.Stats.ObjectModules.StatsObjectModule +{ + /// + /// You should never instantiate this by yourself! + /// Instantiated automatically by for every available . + /// + public sealed class StatResource + { + public StatResourceDefinitionSO definition { get; internal set; } + public StatValue statValue { get; internal set; } + + /// + /// This value represent current resource of this stat. Always between and + /// + public int resource { get; private set; } + + internal StatResource(StatResourceDefinitionSO definition, int defaultValue, StatValue statValue) + { + this.definition = definition; + resource = defaultValue; + this.statValue = statValue; + } + + /// + /// Changes (or simpler - uses) of this stat value by given amount. + /// It just calls with: + + /// + /// Amount that will be added or subtracted from + public void ChangeResource(int amount) + { + SetResource(resource + amount); + } + + /// + /// Sets to given amount. Amount will be clamped between ZERO and . + /// + /// New amount + 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 + }); + } + + /// + /// Sets to given percentage. Percentage will be clamped between ZERO and ONE. + /// + /// New percentage + public void SetResource(float percentage) + { + SetResource(Mathf.CeilToInt(Mathf.Clamp01(percentage) * statValue.value)); + } + } +} \ No newline at end of file diff --git a/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatResource.cs.meta b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatResource.cs.meta new file mode 100644 index 0000000..bdcd798 --- /dev/null +++ b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatResource.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 4fc9e797821a4084bcb2af5d6667a176 +timeCreated: 1782589814 \ No newline at end of file diff --git a/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatResourceDefinitionSO.cs b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatResourceDefinitionSO.cs new file mode 100644 index 0000000..2689d29 --- /dev/null +++ b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatResourceDefinitionSO.cs @@ -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; + } +} \ No newline at end of file diff --git a/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatResourceDefinitionSO.cs.meta b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatResourceDefinitionSO.cs.meta new file mode 100644 index 0000000..4f59127 --- /dev/null +++ b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatResourceDefinitionSO.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 068afee00fe449318bcbb2fce622822c +timeCreated: 1782588829 \ No newline at end of file diff --git a/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatType.cs b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatType.cs deleted file mode 100644 index e260ca3..0000000 --- a/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatType.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace RPGCore.Stats.ObjectModules.StatsObjectModule -{ - public enum StatType - { - Value, - Resource - } -} \ No newline at end of file diff --git a/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatType.cs.meta b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatType.cs.meta deleted file mode 100644 index c9c45cd..0000000 --- a/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatType.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 1800df1b40e6455abd599f5bb6808735 -timeCreated: 1762771772 \ No newline at end of file diff --git a/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatValue.cs b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatValue.cs index f092e85..2ea7f97 100644 --- a/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatValue.cs +++ b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatValue.cs @@ -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 { /// /// You should never instantiate this by yourself! - /// Instantiated automatically by for every available . + /// Instantiated automatically by for every available . /// 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(); /// /// Base value defined in attached to . @@ -24,22 +23,14 @@ namespace RPGCore.Stats.ObjectModules.StatsObjectModule public int baseValue { get; private set; } /// - /// This value is calculated by in and can have two meanings:
- /// 1. If - just simple value stat
- /// 2. If - this is maximum value for that resource + /// This value is calculated by in ///
public int value { get; private set; } - - /// - /// Usable only when . is .
- /// This value represent current resource of this stat. Always between and - ///
- 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 /// New base value public void SetBase(int amount) { + if (baseValue == amount) return; baseValue = amount; Refresh(); } - - /// - /// Changes (or simpler - uses) of this stat value by given amount. - /// It just calls with: + - /// - /// Amount that will be added or subtracted from - public void ChangeResource(int amount) - { - SetResource(resource + amount); - } - - /// - /// Sets to given amount. Amount will be clamped between ZERO and . - /// - /// New amount - 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 - }); - } - - /// - /// Sets to given percentage. Percentage will be clamped between ZERO and ONE. - /// - /// New percentage - public void SetResource(float percentage) - { - SetResource(Mathf.CeilToInt(Mathf.Clamp01(percentage) * value)); - } } } \ No newline at end of file diff --git a/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatDefinitionSO.cs b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatValueDefinitionSO.cs similarity index 82% rename from RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatDefinitionSO.cs rename to RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatValueDefinitionSO.cs index 6ca0767..f6a4f49 100644 --- a/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatDefinitionSO.cs +++ b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatValueDefinitionSO.cs @@ -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; } /// /// has custom value provider that matches stats by their name. - /// We will match these names with marker's parameter to create list of other that this one uses. + /// We will match these names with marker's parameter to create list of other that this one uses. /// Main purpose of dependencies is caching, specially when there is a lot of definitions and dependencies. /// private void FixDependencies() { - var tempDependencies = new List(); - var allDefinitions = SettingsManager.Get().allStats; + var tempDependencies = new List(); + var allDefinitions = SettingsManager.Get().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! ///
/// - private void CheckDependenciesLooping(List definitionsChain = null) + private void CheckDependenciesLooping(List definitionsChain = null) { - definitionsChain ??= new List(); + definitionsChain ??= new List(); if (definitionsChain.Contains(this)) { - definitionsChain[0].dependencies = Array.Empty(); + definitionsChain[0].dependencies = Array.Empty(); 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. /// /// Last overriden definition - public StatDefinitionSO GetBaseDefinition() + public StatValueDefinitionSO GetBaseDefinition() { var tempDefinition = this; while (tempDefinition.overrides) diff --git a/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatDefinitionSO.cs.meta b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatValueDefinitionSO.cs.meta similarity index 100% rename from RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatDefinitionSO.cs.meta rename to RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatValueDefinitionSO.cs.meta diff --git a/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatsModule.cs b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatsModule.cs index a76b76c..2397eaa 100644 --- a/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatsModule.cs +++ b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatsModule.cs @@ -12,57 +12,109 @@ using UnityEngine; namespace RPGCore.Stats.ObjectModules.StatsObjectModule { [Serializable] + [ObjectModule( + name: "[Stats] Statistics", + description: "Attached to any implementation of BaseObject. " + + "Create scriptable object "+nameof(StatValueDefinitionSO)+" to define new stats. " + + "Stats will be automatically created in any matching implementation of BaseObject. " + + "Remember to add all definitions to generated "+nameof(StatsSettings)+"." + )] public class StatsModule : ObjectModule { [SerializeField] - [SerializableDictionary("Stat Definition", "Base Value", isKeyEditable: false)] - private SerializableDictionary _serializedStats; + [SerializableDictionary("Value Definition", "Base Value", isKeyEditable: false)] + private SerializableDictionary _serializedValues; + [SerializeField] + [SerializableDictionary("Resource Definition", "Default Value", isKeyEditable: false)] + private SerializableDictionary _serializedResources; - private Dictionary _stats = new(); - private List _queue = new(); + private Dictionary _values = new(); + private Dictionary _resources = new(); + private List _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().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().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().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().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 } /// - /// Really important part - we create stats for runtime usage. - /// can also override another one, in that case - /// uses main definition, but it is visible as that overriden one. + /// Really important part - we create values for runtime usage. /// 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); + }); + } + + /// + /// Really important part - we create resources for runtime usage. + /// + private void CreateResources() + { + _serializedResources.DictForEach((definition, startingResource) => + { + _resources.Add( + definition, + new StatResource(definition, startingResource, Get(definition.valueDefinitionSO)) + ); }); } /// /// Returns runtime values for given stat definition. /// - /// Runtime stats will be found by this definition (or overriden definition if given) - /// Stat values (baseValue, value, resource) - public StatValue Get(StatDefinitionSO statDefinitionSO) + /// Runtime stats will be found by this definition (or overriden definition if given) + /// Stat value (baseValue, value) + public StatValue Get(StatValueDefinitionSO valueDefinitionSO) { #if UNITY_EDITOR if (!UnityEditor.EditorApplication.isPlaying) CreateStats(); #endif - return _stats.GetValueOrDefault(statDefinitionSO.GetBaseDefinition()); + return _values.GetValueOrDefault(valueDefinitionSO.GetBaseDefinition()); + } + + /// + /// Stat resource (baseValue, value, resource) + public StatResource Get(StatResourceDefinitionSO resourceDefinitionSO) + { +#if UNITY_EDITOR + if (!UnityEditor.EditorApplication.isPlaying) CreateResources(); +#endif + return _resources.GetValueOrDefault(resourceDefinitionSO); } /// @@ -136,9 +212,9 @@ namespace RPGCore.Stats.ObjectModules.StatsObjectModule } /// - /// Select along with its dependencies to refresh next frame. + /// Select along with its dependencies to refresh next frame. /// - 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); } diff --git a/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatsSettings.cs b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatsSettings.cs index 795db6a..4bde9e1 100644 --- a/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatsSettings.cs +++ b/RPGCore.Stats/Runtime/ObjectModules/StatsObjectModule/StatsSettings.cs @@ -6,6 +6,7 @@ namespace RPGCore.Stats.ObjectModules.StatsObjectModule [CustomSettings("RPG Core/Statistics")] public class StatsSettings : CustomSettingsSO { - public List allStats = new(); + public List allValues = new(); + public List allResources = new(); } } \ No newline at end of file diff --git a/RPGCore.Stats/Runtime/RPGCore.Stats.asmdef b/RPGCore.Stats/Runtime/RPGCore.Stats.asmdef index 9e8a995..4c4c994 100644 --- a/RPGCore.Stats/Runtime/RPGCore.Stats.asmdef +++ b/RPGCore.Stats/Runtime/RPGCore.Stats.asmdef @@ -5,8 +5,7 @@ "RPGCore", "RPGCoreCommon.Settings", "RPGCoreCommon.Helpers", - "RPGCoreCommon.DynamicValues", - "Unity.InputSystem" + "RPGCoreCommon.DynamicValues" ], "includePlatforms": [], "excludePlatforms": [], diff --git a/RPGCore.Stats/_integrations.meta b/RPGCore.Stats/_integrations.meta new file mode 100644 index 0000000..492bca9 --- /dev/null +++ b/RPGCore.Stats/_integrations.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: c7b1ac9d0b5844eb9b100a90eb31a295 +timeCreated: 1782147016 \ No newline at end of file diff --git a/RPGCore.Stats/_integrations/Stats2Status.meta b/RPGCore.Stats/_integrations/Stats2Status.meta new file mode 100644 index 0000000..0f5cf65 --- /dev/null +++ b/RPGCore.Stats/_integrations/Stats2Status.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7f0cd35538794fcbae68ff51704ff0bc +timeCreated: 1782147025 \ No newline at end of file diff --git a/RPGCore.Stats/_integrations/Stats2Status/Editor.meta b/RPGCore.Stats/_integrations/Stats2Status/Editor.meta new file mode 100644 index 0000000..09720c0 --- /dev/null +++ b/RPGCore.Stats/_integrations/Stats2Status/Editor.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 8e8a8415cb82457080457ed37759632f +timeCreated: 1782147041 \ No newline at end of file diff --git a/RPGCore.Stats/_integrations/Stats2Status/Editor/RPGCore.Stats2Status.Editor.asmdef b/RPGCore.Stats/_integrations/Stats2Status/Editor/RPGCore.Stats2Status.Editor.asmdef new file mode 100644 index 0000000..0bce80c --- /dev/null +++ b/RPGCore.Stats/_integrations/Stats2Status/Editor/RPGCore.Stats2Status.Editor.asmdef @@ -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 +} \ No newline at end of file diff --git a/RPGCore.Stats/_integrations/Stats2Status/Editor/RPGCore.Stats2Status.Editor.asmdef.meta b/RPGCore.Stats/_integrations/Stats2Status/Editor/RPGCore.Stats2Status.Editor.asmdef.meta new file mode 100644 index 0000000..4a6c8e4 --- /dev/null +++ b/RPGCore.Stats/_integrations/Stats2Status/Editor/RPGCore.Stats2Status.Editor.asmdef.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 3fcd02a1357a446aa9f506aa36f731c6 +timeCreated: 1782147041 \ No newline at end of file diff --git a/RPGCore.Stats/_integrations/Stats2Status/Runtime.meta b/RPGCore.Stats/_integrations/Stats2Status/Runtime.meta new file mode 100644 index 0000000..6ab53c7 --- /dev/null +++ b/RPGCore.Stats/_integrations/Stats2Status/Runtime.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2ea364d5fbbb4f649d4ea50f61b060d9 +timeCreated: 1782147025 \ No newline at end of file diff --git a/RPGCore.Stats/_integrations/Stats2Status/Runtime/Effects.meta b/RPGCore.Stats/_integrations/Stats2Status/Runtime/Effects.meta new file mode 100644 index 0000000..7a64065 --- /dev/null +++ b/RPGCore.Stats/_integrations/Stats2Status/Runtime/Effects.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: b1c1cd6791c74c87b717977a72381acc +timeCreated: 1782151180 \ No newline at end of file diff --git a/RPGCore.Stats/_integrations/Stats2Status/Runtime/Effects/ResourceChangeEffect.cs b/RPGCore.Stats/_integrations/Stats2Status/Runtime/Effects/ResourceChangeEffect.cs new file mode 100644 index 0000000..c8e8b6e --- /dev/null +++ b/RPGCore.Stats/_integrations/Stats2Status/Runtime/Effects/ResourceChangeEffect.cs @@ -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().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() + { + } + } +} \ No newline at end of file diff --git a/RPGCore.Stats/_integrations/Stats2Status/Runtime/Effects/ResourceChangeEffect.cs.meta b/RPGCore.Stats/_integrations/Stats2Status/Runtime/Effects/ResourceChangeEffect.cs.meta new file mode 100644 index 0000000..0a87b31 --- /dev/null +++ b/RPGCore.Stats/_integrations/Stats2Status/Runtime/Effects/ResourceChangeEffect.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 19741604c0ef4f87a5d7b27a7e62c538 +timeCreated: 1782332472 \ No newline at end of file diff --git a/RPGCore.Stats/_integrations/Stats2Status/Runtime/Effects/ValueChangeEffect.cs b/RPGCore.Stats/_integrations/Stats2Status/Runtime/Effects/ValueChangeEffect.cs new file mode 100644 index 0000000..184f5a5 --- /dev/null +++ b/RPGCore.Stats/_integrations/Stats2Status/Runtime/Effects/ValueChangeEffect.cs @@ -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().Get(valueDefinitionSO); + _modifier = new StatModifier(status.target, amount, amountType); + _value.modifiers.Add(_modifier); + } + + protected override void OnEnd() + { + _value.modifiers.Remove(_modifier); + } + } +} \ No newline at end of file diff --git a/RPGCore.Stats/_integrations/Stats2Status/Runtime/Effects/ValueChangeEffect.cs.meta b/RPGCore.Stats/_integrations/Stats2Status/Runtime/Effects/ValueChangeEffect.cs.meta new file mode 100644 index 0000000..dc4c0df --- /dev/null +++ b/RPGCore.Stats/_integrations/Stats2Status/Runtime/Effects/ValueChangeEffect.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 473957f15aa24486b03bd37fa8ef922d +timeCreated: 1782150941 \ No newline at end of file diff --git a/RPGCore.Stats/_integrations/Stats2Status/Runtime/RPGCore.Stats2Status.asmdef b/RPGCore.Stats/_integrations/Stats2Status/Runtime/RPGCore.Stats2Status.asmdef new file mode 100644 index 0000000..f9de879 --- /dev/null +++ b/RPGCore.Stats/_integrations/Stats2Status/Runtime/RPGCore.Stats2Status.asmdef @@ -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 +} \ No newline at end of file diff --git a/RPGCore.Stats/_integrations/Stats2Status/Runtime/RPGCore.Stats2Status.asmdef.meta b/RPGCore.Stats/_integrations/Stats2Status/Runtime/RPGCore.Stats2Status.asmdef.meta new file mode 100644 index 0000000..8d31543 --- /dev/null +++ b/RPGCore.Stats/_integrations/Stats2Status/Runtime/RPGCore.Stats2Status.asmdef.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 7b55415f0e2b4b3a9545c2390bd012f2 +timeCreated: 1782147025 \ No newline at end of file