// 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 System.Text.Json.Serialization; using Core.Extension; using Core.Helper; using ProtoBuf; using WallstopStudios.UnityHelpers.Core.Attributes; /// /// Declarative change applied to an . /// Each instance represents a single operation (add, multiply, or override) referenced by an . /// /// /// Key properties: /// /// Non-destructive: temporary handles can add/remove modifications without mutating base values. /// Deterministic ordering: always processes Addition, then Multiplication, then Override. /// Flexible authoring: supports both instant (permanent) and duration-based effects. /// /// Stack processing order: /// /// Addition (value += x) /// Multiplication (value *= x) /// Override (value = x) /// /// /// The field must match an field on the target . /// The Attribute Metadata Cache generator can provide editor dropdowns to avoid typos. Unknown names are ignored at runtime. /// /// Sample definitions: /// /// // +50 flat health /// new AttributeModification { attribute = "Health", action = ModificationAction.Addition, value = 50f }; /// /// // +50% speed /// new AttributeModification { attribute = "Speed", action = ModificationAction.Multiplication, value = 1.5f }; /// /// // Hard-set defense to 0 /// new AttributeModification { attribute = "Defense", action = ModificationAction.Override, value = 0f }; /// /// Authoring tips: /// /// Use Addition for flat buffs/debuffs; Multiplication for percentage-style adjustments. /// Reserve Override for hard clamps (it always executes last). /// Negative Addition subtracts; Multiplication values below 1.0 reduce the attribute. /// /// [Serializable] [ProtoContract] public struct AttributeModification : IEquatable, IComparable, IComparable { /// /// The name of the attribute to modify. This should match a field name in an subclass. /// [StringInList(typeof(AttributeUtilities), nameof(AttributeUtilities.GetAllAttributeNames))] [ProtoMember(1)] public string attribute; /// /// The type of modification action to perform (Addition, Multiplication, or Override). /// [ProtoMember(2)] public ModificationAction action; /// /// The value to use for the modification. Interpretation depends on the : /// - Addition: The amount to add (can be negative for subtraction) /// - Multiplication: The multiplier to apply (e.g., 1.5 for +50%, 0.5 for -50%) /// - Override: The new absolute value to set /// [ProtoMember(3)] public float value; [JsonConstructor] public AttributeModification(string attribute, ModificationAction action, float value) { this.attribute = attribute; this.action = action; this.value = value; } /// /// Converts this modification to a JSON string representation. /// /// A JSON string representing this modification. public override string ToString() { return this.ToJson(); } public int CompareTo(object obj) { if (obj is AttributeModification other) { return CompareTo(other); } return -1; } public int CompareTo(AttributeModification other) { return ((int)action).CompareTo((int)other.action); } /// /// Determines whether two attribute modifications are not equal. /// /// The first modification to compare. /// The second modification to compare. /// true if the modifications are not equal; otherwise, false. public static bool operator !=(AttributeModification lhs, AttributeModification rhs) { return !(lhs == rhs); } /// /// Determines whether two attribute modifications are equal. /// /// The first modification to compare. /// The second modification to compare. /// true if the modifications are equal; otherwise, false. public static bool operator ==(AttributeModification lhs, AttributeModification rhs) { return lhs.Equals(rhs); } /// /// Determines whether this modification equals the specified object. /// /// The object to compare with. /// true if the object is an AttributeModification with equal values; otherwise, false. public override bool Equals(object obj) { return obj is AttributeModification other && Equals(other); } /// /// Determines whether this modification equals another modification by comparing all fields. /// /// The modification to compare with. /// true if all fields are equal; otherwise, false. public bool Equals(AttributeModification other) { return string.Equals(attribute, other.attribute, StringComparison.Ordinal) && action == other.action && value.Equals(other.value); } /// /// Returns the hash code for this modification. /// /// A hash code combining the attribute name, action, and value. public override int GetHashCode() { return Objects.HashCode(attribute, action, value); } } }