Skip to content

Instantly share code, notes, and snippets.

@phosphoer
Created March 11, 2022 22:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save phosphoer/adb7e324e3d768720d1012660bf35f82 to your computer and use it in GitHub Desktop.
Save phosphoer/adb7e324e3d768720d1012660bf35f82 to your computer and use it in GitHub Desktop.
Stat Modifier Stack
using UnityEngine;
using System.Collections.Generic;
public enum StatModifierTrait
{
MaxHealth,
Damage,
Etc
}
public enum StatModifierOperation
{
Add,
Multiply,
}
[System.Serializable]
public class StatModifier
{
public string Name => GetName(Trait);
public float RemainingTime = Mathf.Infinity;
public float Value;
public float DisplayMultiplier;
public string Units;
public StatModifierTrait Trait;
public StatModifierOperation Operation;
public static string GetName(StatModifierTrait forTrait)
{
switch (forTrait)
{
case StatModifierTrait.MaxHealth:
return "Max Health";
case StatModifierTrait.Damage:
return "Damage";
}
return $"Unnamed stat {forTrait}";
}
}
public class StatModifierStack
{
public IReadOnlyList<StatModifier> Stats => _stats;
public event System.Action<StatModifier> ModifierAdded;
public event System.Action<StatModifier> ModifierRemoved;
private List<StatModifier> _stats = new List<StatModifier>();
private List<float> _statLifeTimes = new List<float>();
private List<StatModifier>[] _statsByOperation;
private CachedValue[] _moddedValuesCache = new CachedValue[System.Enum.GetValues(typeof(StatModifierTrait)).Length];
private struct CachedValue
{
public float Value;
public float BaseValue;
public bool IsValid;
}
public StatModifierStack()
{
_statsByOperation = new List<StatModifier>[System.Enum.GetValues(typeof(StatModifierOperation)).Length];
for (int i = 0; i < _statsByOperation.Length; ++i)
{
_statsByOperation[i] = new List<StatModifier>();
}
}
public bool HasModifier(StatModifier modifier)
{
for (int i = 0; i < Stats.Count; ++i)
{
if (Stats[i] == modifier)
return true;
}
return false;
}
public bool HasModifier(string modifierName)
{
for (int i = 0; i < Stats.Count; ++i)
{
if (Stats[i].Name == modifierName)
return true;
}
return false;
}
public void Update(float dt)
{
for (int i = 0; i < _stats.Count; ++i)
{
StatModifier stat = _stats[i];
_statLifeTimes[i] -= dt;
if (_statLifeTimes[i] <= 0)
{
RemoveModifier(i);
--i;
}
}
}
public IReadOnlyList<StatModifier> GetModifiersByOperation(StatModifierOperation operation)
{
return _statsByOperation[(int)operation];
}
public float GetModdedValue(StatModifierTrait trait, float baseValue)
{
// Get the cached value
CachedValue cache = _moddedValuesCache[(int)trait];
cache.IsValid &= cache.BaseValue == baseValue;
// Update the cached value
if (!cache.IsValid)
{
// Debug.Log($"Recalculating modded stat for trait {trait}");
IReadOnlyList<StatModifier> addMods = GetModifiersByOperation(StatModifierOperation.Add);
IReadOnlyList<StatModifier> multMods = GetModifiersByOperation(StatModifierOperation.Multiply);
cache.Value = baseValue;
for (int i = 0; i < addMods.Count; ++i)
{
StatModifier mod = addMods[i];
if (mod.Trait == trait)
cache.Value += mod.Value;
}
for (int i = 0; i < multMods.Count; ++i)
{
StatModifier mod = multMods[i];
if (mod.Trait == trait)
cache.Value *= mod.Value;
}
cache.IsValid = true;
cache.BaseValue = baseValue;
_moddedValuesCache[(int)trait] = cache;
// Debug.Log($"Modded trait value is {trait} is {cache.Value}");
}
return cache.Value;
}
public void AddModifiers(StatModifier[] modifiers)
{
foreach (StatModifier modifier in modifiers)
AddModifier(modifier);
}
public void RemoveModifiers(StatModifier[] modifiers)
{
foreach (StatModifier modifier in modifiers)
RemoveModifier(modifier);
}
public void AddModifier(StatModifier modifier)
{
_stats.Add(modifier);
_statsByOperation[(int)modifier.Operation].Add(modifier);
_statLifeTimes.Add(modifier.RemainingTime);
CachedValue cachedValue = _moddedValuesCache[(int)modifier.Trait];
cachedValue.IsValid = false;
_moddedValuesCache[(int)modifier.Trait] = cachedValue;
ModifierAdded?.Invoke(modifier);
}
public void RemoveModifier(int index)
{
StatModifier modifier = _stats[index];
_stats.RemoveAt(index);
_statLifeTimes.RemoveAt(index);
_statsByOperation[(int)modifier.Operation].Remove(modifier);
CachedValue cachedValue = _moddedValuesCache[(int)modifier.Trait];
cachedValue.IsValid = false;
_moddedValuesCache[(int)modifier.Trait] = cachedValue;
ModifierRemoved?.Invoke(modifier);
}
public void RemoveModifier(StatModifier modifier)
{
int index = _stats.IndexOf(modifier);
if (index >= 0)
RemoveModifier(index);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment