// MIT License - Copyright (c) 2025 wallstop // Full license text: https://github.com/wallstop/unity-helpers/blob/main/LICENSE namespace WallstopStudios.UnityHelpers.Tags { using System; using WallstopStudios.UnityHelpers.Core.Helper; /// /// Key used to group effect handles for stacking decisions. /// /// /// /// Effect stacking is driven by . For reference-based grouping, the /// key stores the effect asset; for custom grouping it stores a string key. Equality and hashing are /// tuned to avoid allocations and rely on reference equality for effect assets and ordinal comparison /// for custom keys. /// /// /// Keys are created via the factory methods and are used by to bucket /// active instances when applying stacking policies. /// /// internal readonly struct EffectStackKey : IEquatable { private readonly EffectStackGroup _group; private readonly AttributeEffect _effect; private readonly string _customKey; private EffectStackKey(EffectStackGroup group, AttributeEffect effect, string customKey) { _group = group; _effect = effect; _customKey = customKey; } /// /// Creates a stack key that groups by effect asset reference. /// /// The effect asset to use for grouping. /// A key representing the reference-based stack group. /// /// /// EffectStackKey key = EffectStackKey.CreateReference(poisonEffect); /// // All handles from the same poisonEffect asset share a stack. /// /// public static EffectStackKey CreateReference(AttributeEffect effect) { return new EffectStackKey(EffectStackGroup.Reference, effect, null); } /// /// Creates a stack key that groups by a custom string identifier. /// /// Identifier used to group otherwise distinct effects. /// A key representing the custom stack group. /// /// /// EffectStackKey key = EffectStackKey.CreateCustom("DamageOverTime"); /// // Different assets can share the same custom key to stack together. /// /// public static EffectStackKey CreateCustom(string customKey) { return new EffectStackKey(EffectStackGroup.CustomKey, null, customKey); } /// /// Compares this key to another for value equality. /// /// The other key to compare. /// true when both keys represent the same stack group; otherwise, false. public bool Equals(EffectStackKey other) { if (_group != other._group) { return false; } return _group switch { EffectStackGroup.Reference => ReferenceEquals(_effect, other._effect), EffectStackGroup.CustomKey => string.Equals( _customKey, other._customKey, StringComparison.Ordinal ), _ => false, }; } /// /// Determines whether this key is equal to another object. /// /// The object to compare with. /// true if the object is an representing the same group; otherwise, false. public override bool Equals(object obj) { return obj is EffectStackKey other && Equals(other); } /// /// Generates a hash code consistent with . /// /// A hash code for use in dictionaries and sets. public override int GetHashCode() { return _group switch { EffectStackGroup.Reference => Objects.HashCode(_group, _effect), EffectStackGroup.CustomKey => Objects.HashCode( _group, _customKey != null ? StringComparer.Ordinal.GetHashCode(_customKey) : 0 ), _ => Objects.HashCode(_group), }; } /// /// Determines whether two keys are equal. /// /// First key to compare. /// Second key to compare. /// true if both keys represent the same stack group; otherwise, false. public static bool operator ==(EffectStackKey left, EffectStackKey right) { return left.Equals(right); } /// /// Determines whether two keys are not equal. /// /// First key to compare. /// Second key to compare. /// true if the keys differ; otherwise, false. public static bool operator !=(EffectStackKey left, EffectStackKey right) { return !(left == right); } } }