// 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);
}
}
}