using System.Collections; using System.Diagnostics.CodeAnalysis; using PkmnLib.Static.Utils; namespace PkmnLib.Static; /// /// A set of statistics that cannot be changed. /// /// The size of the integer to be used public record ImmutableStatisticSet where T : struct { /// /// The health points stat value. /// public T Hp { get; protected set; } /// /// The physical attack stat value. /// public T Attack { get; protected set; } /// /// The physical defense stat value. /// public T Defense { get; protected set; } /// /// The special attack stat value. /// public T SpecialAttack { get; protected set; } /// /// The special defense stat value. /// public T SpecialDefense { get; protected set; } /// /// The speed stat value. /// public T Speed { get; protected set; } /// public ImmutableStatisticSet(T hp, T attack, T defense, T specialAttack, T specialDefense, T speed) { Hp = hp; Attack = attack; Defense = defense; SpecialAttack = specialAttack; SpecialDefense = specialDefense; Speed = speed; } public ImmutableStatisticSet(ImmutableStatisticSet set) { Hp = set.Hp; Attack = set.Attack; Defense = set.Defense; SpecialAttack = set.SpecialAttack; SpecialDefense = set.SpecialDefense; Speed = set.Speed; } /// /// Gets a statistic from the set. /// public T GetStatistic(Statistic stat) { return stat switch { Statistic.Hp => Hp, Statistic.Attack => Attack, Statistic.Defense => Defense, Statistic.SpecialAttack => SpecialAttack, Statistic.SpecialDefense => SpecialDefense, Statistic.Speed => Speed, _ => throw new ArgumentException("Invalid statistic."), }; } } /// /// A set of statistics that can be changed. /// /// public record StatisticSet : ImmutableStatisticSet, IEnumerable, IDeepCloneable where T : struct { /// public StatisticSet() : base(default, default, default, default, default, default) { } /// public StatisticSet(T hp, T attack, T defense, T specialAttack, T specialDefense, T speed) : base(hp, attack, defense, specialAttack, specialDefense, speed) { } public StatisticSet(StatisticSet set) : base(set) { } /// /// Helper function to add two numerics together. /// protected T Add(T a, T b) => (T)Convert.ChangeType(Convert.ToInt32(a) + Convert.ToInt32(b), typeof(T)); /// /// Helper function to subtract two numerics. /// protected T Subtract(T a, T b) => (T)Convert.ChangeType(Convert.ToInt32(a) - Convert.ToInt32(b), typeof(T)); /// /// Modifies a statistic in the set. /// public virtual void SetStatistic(Statistic stat, T value) { switch (stat) { case Statistic.Hp: Hp = value; break; case Statistic.Attack: Attack = value; break; case Statistic.Defense: Defense = value; break; case Statistic.SpecialAttack: SpecialAttack = value; break; case Statistic.SpecialDefense: SpecialDefense = value; break; case Statistic.Speed: Speed = value; break; default: throw new ArgumentException("Invalid statistic."); } } /// /// Increases a statistic in the set by a value. /// public virtual bool IncreaseStatistic(Statistic stat, T value) { switch (stat) { case Statistic.Hp: Hp = Add(Hp, value); break; case Statistic.Attack: Attack = Add(Attack, value); break; case Statistic.Defense: Defense = Add(Defense, value); break; case Statistic.SpecialAttack: SpecialAttack = Add(SpecialAttack, value); break; case Statistic.SpecialDefense: SpecialDefense = Add(SpecialDefense, value); break; case Statistic.Speed: Speed = Add(Speed, value); break; default: SetUnknownStat(stat, Add(GetUnknownStat(stat), value)); break; } return true; } protected virtual T GetUnknownStat(Statistic stat) => throw new ArgumentException($"Invalid statistic {stat}"); protected virtual void SetUnknownStat(Statistic stat, T value) { throw new ArgumentException($"Invalid statistic {stat}"); } /// /// Decreases a statistic in the set by a value. /// public virtual bool DecreaseStatistic(Statistic stat, T value) { switch (stat) { case Statistic.Hp: Hp = Subtract(Hp, value); break; case Statistic.Attack: Attack = Subtract(Attack, value); break; case Statistic.Defense: Defense = Subtract(Defense, value); break; case Statistic.SpecialAttack: SpecialAttack = Subtract(SpecialAttack, value); break; case Statistic.SpecialDefense: SpecialDefense = Subtract(SpecialDefense, value); break; case Statistic.Speed: Speed = Subtract(Speed, value); break; default: SetUnknownStat(stat, Subtract(GetUnknownStat(stat), value)); break; } return true; } /// public virtual IEnumerator GetEnumerator() { yield return Hp; yield return Attack; yield return Defense; yield return SpecialAttack; yield return SpecialDefense; yield return Speed; } /// IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } /// /// A set of statistics that can be changed, but are clamped to a minimum and maximum value. /// public abstract record ClampedStatisticSet : StatisticSet where T : struct, IComparable { /// [SuppressMessage("ReSharper", "VirtualMemberCallInConstructor")] protected ClampedStatisticSet(T hp, T attack, T defense, T specialAttack, T specialDefense, T speed) : base(hp, attack, defense, specialAttack, specialDefense, speed) { if (Min.CompareTo(Max) > 0) throw new ArgumentException("Minimum value must be less than or equal to maximum value."); Hp = Clamp(Hp, Min, Max); Attack = Clamp(Attack, Min, Max); Defense = Clamp(Defense, Min, Max); SpecialAttack = Clamp(SpecialAttack, Min, Max); SpecialDefense = Clamp(SpecialDefense, Min, Max); Speed = Clamp(Speed, Min, Max); } protected ClampedStatisticSet(ClampedStatisticSet set) : base(set) { } protected static T Clamp(T value, T min, T max) { if (value.CompareTo(min) < 0) return min; if (value.CompareTo(max) > 0) return max; return value; } /// /// The minimum value for the statistics. /// protected abstract T Min { get; } /// /// The maximum value for the statistics. /// protected abstract T Max { get; } /// public override void SetStatistic(Statistic stat, T value) => base.SetStatistic(stat, Clamp(value, Min, Max)); /// public override bool IncreaseStatistic(Statistic stat, T value) { var current = GetStatistic(stat); var newValue = Add(current, value); if (newValue.CompareTo(Max) > 0) value = Subtract(Max, current); if (value.CompareTo(default) == 0) return false; return base.IncreaseStatistic(stat, value); } /// public override bool DecreaseStatistic(Statistic stat, T value) { var current = GetStatistic(stat); var newValue = Subtract(current, value); if (newValue.CompareTo(Min) < 0) value = Subtract(current, Min); if (value.CompareTo(default) == 0) return false; return base.DecreaseStatistic(stat, value); } } /// /// A set of statistics that can be changed, but are clamped to a minimum and maximum value of -6 and 6. /// public record StatBoostStatisticSet : ClampedStatisticSet { /// protected override sbyte Min => -6; /// protected override sbyte Max => 6; private sbyte _evasion; public sbyte Evasion { get => _evasion; set => _evasion = Clamp(value, Min, Max); } private sbyte _accuracy; public sbyte Accuracy { get => _accuracy; set => _accuracy = Clamp(value, Min, Max); } /// public StatBoostStatisticSet() : base(0, 0, 0, 0, 0, 0) { } /// public StatBoostStatisticSet(sbyte hp, sbyte attack, sbyte defense, sbyte specialAttack, sbyte specialDefense, sbyte speed) : base(hp, attack, defense, specialAttack, specialDefense, speed) { } /// protected override sbyte GetUnknownStat(Statistic stat) { return stat switch { Statistic.Evasion => Evasion, Statistic.Accuracy => Accuracy, _ => throw new ArgumentException($"Invalid statistic {stat}"), }; } /// /// protected override void SetUnknownStat(Statistic stat, sbyte value) { switch (stat) { case Statistic.Evasion: Evasion = value; break; case Statistic.Accuracy: Accuracy = value; break; default: throw new ArgumentException($"Invalid statistic {stat}"); } } /// public override IEnumerator GetEnumerator() { yield return Hp; yield return Attack; yield return Defense; yield return SpecialAttack; yield return SpecialDefense; yield return Speed; yield return Evasion; yield return Accuracy; } } /// /// A set of statistics that can be changed, but are clamped to a minimum and maximum value of 0 and 31. /// public record IndividualValueStatisticSet : ClampedStatisticSet { /// protected override byte Min => 0; /// protected override byte Max => 31; /// public IndividualValueStatisticSet() : base(0, 0, 0, 0, 0, 0) { } /// public IndividualValueStatisticSet(byte hp, byte attack, byte defense, byte specialAttack, byte specialDefense, byte speed) : base(hp, attack, defense, specialAttack, specialDefense, speed) { } public IndividualValueStatisticSet(IndividualValueStatisticSet ivs) : base(ivs) { } } /// /// A set of statistics that can be changed, but are clamped to a minimum and maximum value of 0 and 252. /// public record EffortValueStatisticSet : ClampedStatisticSet { /// protected override byte Min => 0; /// protected override byte Max => 252; /// public EffortValueStatisticSet() : base(0, 0, 0, 0, 0, 0) { } /// public EffortValueStatisticSet(byte hp, byte attack, byte defense, byte specialAttack, byte specialDefense, byte speed) : base(hp, attack, defense, specialAttack, specialDefense, speed) { } public EffortValueStatisticSet(EffortValueStatisticSet evs) : base(evs) { } }