using System; using System.Collections.Generic; namespace PKHeX.Core { /// /// Stat/misc data for individual species or their associated alternate form data. /// public abstract class PersonalInfo { /// /// Raw Data /// protected readonly byte[] Data; protected PersonalInfo(byte[] data) => Data = data; /// /// Writes entry to raw bytes. /// /// public abstract byte[] Write(); /// /// Base HP /// public abstract int HP { get; set; } /// /// Base Attack /// public abstract int ATK { get; set; } /// /// Base Defense /// public abstract int DEF { get; set; } /// /// Base Speed /// public abstract int SPE { get; set; } /// /// Base Special Attack /// public abstract int SPA { get; set; } /// /// Base Special Defense /// public abstract int SPD { get; set; } /// /// Base Stat values /// public IReadOnlyList Stats => new[] { HP, ATK, DEF, SPE, SPA, SPD }; /// /// Amount of HP Effort Values to yield when defeating this entry. /// public abstract int EV_HP { get; set; } /// /// Amount of Attack Effort Values to yield when defeating this entry. /// public abstract int EV_ATK { get; set; } /// /// Amount of Defense Effort Values to yield when defeating this entry. /// public abstract int EV_DEF { get; set; } /// /// Amount of Speed Effort Values to yield when defeating this entry. /// public abstract int EV_SPE { get; set; } /// /// Amount of Special Attack Effort Values to yield when defeating this entry. /// public abstract int EV_SPA { get; set; } /// /// Amount of Special Defense Effort Values to yield when defeating this entry. /// public abstract int EV_SPD { get; set; } /// /// Primary Type /// public abstract int Type1 { get; set; } /// /// Secondary Type /// public abstract int Type2 { get; set; } /// /// First Egg Group /// public abstract int EggGroup1 { get; set; } /// /// Second Egg Group /// public abstract int EggGroup2 { get; set; } /// /// Catch Rate /// public abstract int CatchRate { get; set; } /// /// Evolution Stage value (or equivalent for un-evolved). /// public virtual int EvoStage { get; set; } /// /// Held Items the entry can be randomly encountered with. /// public abstract IReadOnlyList Items { get; set; } /// /// Gender Ratio value determining if the entry is a fixed gender or bi-gendered. /// public abstract int Gender { get; set; } /// /// Amount of Hatching Step Cycles required to hatch if in an egg. /// public abstract int HatchCycles { get; set; } /// /// Initial Friendship when captured or received. /// public abstract int BaseFriendship { get; set; } /// /// Experience-Level Growth Rate type /// public abstract int EXPGrowth { get; set; } /// /// Full list of values the entry can have. /// public abstract IReadOnlyList Abilities { get; set; } /// /// Gets the ability index without creating an array and looking through it. /// /// Ability ID /// Ability Index public abstract int GetAbilityIndex(int abilityID); /// /// Escape factor used for fleeing the Safari Zone or calling for help in SOS Battles. /// public abstract int EscapeRate { get; set; } /// /// Count of values the entry can have. /// public virtual int FormCount { get; set; } = 1; /// /// Pointer to the first index /// protected internal virtual int FormStatsIndex { get; set; } /// /// Pointer to the sprite index. /// public virtual int FormSprite { get; set; } /// /// Base Experience Yield factor /// public abstract int BaseEXP { get; set; } /// /// Main color ID of the entry. The majority of the Pokémon's color is of this color, usually. /// public abstract int Color { get; set; } /// /// Height of the entry in meters (m). /// public virtual int Height { get; set; } = 0; /// /// Mass of the entry in kilograms (kg). /// public virtual int Weight { get; set; } = 0; /// /// TM/HM learn compatibility flags for individual moves. /// public bool[] TMHM = Array.Empty(); /// /// Grass-Fire-Water-Etc typed learn compatibility flags for individual moves. /// public bool[] TypeTutors = Array.Empty(); /// /// Special tutor learn compatibility flags for individual moves. /// public bool[][] SpecialTutors = Array.Empty(); protected static bool[] GetBits(ReadOnlySpan data) { bool[] result = new bool[data.Length << 3]; for (int i = result.Length - 1; i >= 0; i--) result[i] = (data[i >> 3] >> (i & 7) & 0x1) == 1; return result; } protected static void SetBits(bool[] bits, Span data) { for (int i = bits.Length - 1; i >= 0; i--) data[i>>3] |= (byte)(bits[i] ? 1 << (i&0x7) : 0); } /// /// Injects supplementary TM/HM compatibility which is not present in the generation specific format. /// /// Data to read from internal void AddTMHM(ReadOnlySpan data) => TMHM = GetBits(data); /// /// Injects supplementary Type Tutor compatibility which is not present in the generation specific format. /// /// Data to read from internal void AddTypeTutors(ReadOnlySpan data) => TypeTutors = GetBits(data); /// /// Gets the entry index for the input criteria, with fallback for the original species entry. /// /// to retrieve for /// to retrieve for /// Index the exists as in the . public int FormIndex(int species, int form) { if (!HasForm(form)) return species; return FormStatsIndex + form - 1; } /// /// Checks if the has the requested entry index available. /// /// to retrieve for public bool HasForm(int form) { if (form <= 0) // no form requested return false; if (FormStatsIndex <= 0) // no forms present return false; if (form >= FormCount) // beyond range of species' forms return false; return true; } /// /// Gets a random valid gender for the entry. /// public int RandomGender() { var fix = FixedGender; return fix >= 0 ? fix : Util.Rand.Next(2); } /// /// Gets a gender value. Returns -1 if the entry . /// public int FixedGender { get { if (Genderless) return 2; if (OnlyFemale) return 1; if (OnlyMale) return 0; return -1; } } public const int RatioMagicGenderless = 255; public const int RatioMagicFemale = 254; public const int RatioMagicMale = 0; public static bool IsSingleGender(int gt) => (uint)(gt - 1) >= 253; /// /// Indicates that the entry has two genders. /// public bool IsDualGender => (uint)(Gender - 1) < 253; /// /// Indicates that the entry is exclusively Genderless. /// public bool Genderless => Gender == RatioMagicGenderless; /// /// Indicates that the entry is exclusively Female gendered. /// public bool OnlyFemale => Gender == RatioMagicFemale; /// /// Indicates that the entry is exclusively Male gendered. /// public bool OnlyMale => Gender == RatioMagicMale; /// /// Indicates if the entry has forms or not. /// public bool HasForms => FormCount > 1; /// /// Base Stat Total sum of all stats. /// public int BST => HP + ATK + DEF + SPE + SPA + SPD; /// /// Checks to see if the is valid within the /// /// public bool IsFormWithinRange(int form) { if (form == 0) return true; return form < FormCount; } /// /// Checks to see if the provided Types match the entry's types. /// /// Input order matters! If input order does not matter, use instead. /// First type /// Second type /// Typing is an exact match public bool IsValidTypeCombination(int type1, int type2) => Type1 == type1 && Type2 == type2; /// /// Checks if the entry has either type equal to the input type. /// /// Type /// Typing is present in entry public bool IsType(int type1) => Type1 == type1 || Type2 == type1; /// /// Checks if the entry has either type equal to both input types. /// /// Input order does not matter. /// Type 1 /// Type 2 /// Typing is present in entry public bool IsType(int type1, int type2) => (Type1 == type1 || Type2 == type1) && (Type1 == type2 || Type2 == type2); /// /// Checks if the entry has either egg group equal to the input type. /// /// Egg group /// Egg is present in entry public bool IsEggGroup(int group) => EggGroup1 == group || EggGroup2 == group; } }