using System; namespace PKHeX.Core; /// /// Value result object of parsing a stat string. /// public record struct StatParseResult() { private const uint MaxStatCount = 6; // Number of stats in the game public const sbyte NoStatAmp = -1; /// /// Count of parsed stats. /// public byte CountParsed { get; private set; } = 0; // could potentially make this a computed value (popcnt), but it's not worth it /// /// Bitflag indexes of parsed stats, indexed in visual order. /// public byte IndexesParsed { get; private set; } = 0; /// /// Stat index of increased stat, indexed in visual order. /// public sbyte Plus { get; set; } = NoStatAmp; /// /// Stat index of decreased stat, indexed in visual order. /// public sbyte Minus { get; set; } = NoStatAmp; /// /// Indicates if the parsing was clean (no un-parsed text). /// public bool IsParseClean { get; private set; } = true; /// /// Indicates if all stat indexes available were parsed. /// public bool IsParsedAllStats { get; private set; } /// /// Marks the stat index as parsed, and updates the count of parsed stats. /// /// Visual index of the stat to mark as parsed. /// True if the stat had not been parsed before, false if it was already parsed. public bool MarkParsed(int statIndex) { // Check if the stat index is valid (0-5) ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)statIndex, MaxStatCount); if (WasParsed(statIndex)) return false; // Mark the stat index as parsed IndexesParsed |= (byte)(1 << statIndex); ++CountParsed; return true; } /// /// Checks if the stat index was parsed. /// /// Visual index of the stat to check. /// True if the stat was parsed, false otherwise. public readonly bool WasParsed(int statIndex) { // Check if the stat index is valid (0-5) ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual((uint)statIndex, MaxStatCount); return (IndexesParsed & (1 << statIndex)) != 0; } /// /// Marks the parsing as finished, and updates the internal state to indicate if all stats were parsed. /// /// /// This is used when not all stats are required to be parsed. /// /// public void FinishParse(int expect) { if (CountParsed == 0 && !HasAmps) MarkDirty(); IsParsedAllStats = CountParsed == expect || IsParseClean; } /// /// Marks the parsing as finished, and updates the internal state to indicate if all stats were parsed. /// /// /// This is used when a specific number of stats is expected. /// /// public void FinishParseOnly(int expect) => IsParsedAllStats = CountParsed == expect; /// /// Marks the parsing as dirty, indicating that the string was not a clean input string (user modified or the syntax doesn't match the spec). /// public void MarkDirty() => IsParseClean = false; /// /// Indicates if any stat has any amplified (+/-) requested, indicative of nature. /// public readonly bool HasAmps => Plus != NoStatAmp || Minus != NoStatAmp; /// /// Reorders the speed stat to be in the middle of the stats. /// /// /// Speed is visually represented as the last stat in the list, but it is actually the 3rd stat stored. /// public void TreatAmpsAsSpeedNotLast() { Plus = GetSpeedMiddleIndex(Plus); Minus = GetSpeedMiddleIndex(Minus); } /// /// Adjusts stat indexes from visual to stored, and ignoring HP's index. /// /// Visual index of the stat to get the adjusted value for. /// Stored index of the stat. private static sbyte GetSpeedMiddleIndex(sbyte amp) => amp switch { // 0 => NoStatAmp -- handle via default case 1 => 0, // Atk 2 => 1, // Def 3 => 3, // SpA 4 => 4, // SpD 5 => 2, // Spe _ => NoStatAmp, }; }