using System; namespace PKHeX.Core { /// /// Stat/misc data for individual species or their associated alternate forme 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 int[] Stats { get => new[] { HP, ATK, DEF, SPE, SPA, SPD }; set { HP = value[0]; ATK = value[1]; DEF = value[2]; SPE = value[3]; SPA = value[4]; SPD = value[5]; } } /// /// 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 int[] 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 int [] Abilities { get; set; } /// /// 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 FormeCount { get; set; } = 1; /// /// Pointer to the first index /// protected internal virtual int FormStatsIndex { get; set; } /// /// Pointer to the sprite index. /// public virtual int FormeSprite { 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; /// /// Dual Type IDs used for same-type attack bonuses, weakness, etc. /// public int[] Types { get => new[] { Type1, Type2 }; set { if (value.Length != 2) return; Type1 = value[0]; Type2 = value[1]; } } /// /// Dual Egg Group IDs used to determine if an egg should be created as a result of both parents sharing at least one group ID. /// public int[] EggGroups { get => new[] { EggGroup1, EggGroup2 }; set { if (value.Length != 2) return; EggGroup1 = (byte)value[0]; EggGroup2 = (byte)value[1]; } } /// /// TM/HM learn compatibility flags for individual moves. /// public bool[] TMHM { get; protected set; } = Array.Empty(); /// /// Grass-Fire-Water-Etc typed learn compatibility flags for individual moves. /// public bool[] TypeTutors { get; protected set; } = Array.Empty(); /// /// Special tutor learn compatibility flags for individual moves. /// public bool[][] SpecialTutors { get; protected set; } = Array.Empty(); protected static bool[] GetBits(byte[] data, int start = 0, int length = -1) { if (length < 0) length = data.Length; bool[] result = new bool[length << 3]; for (int i = 0; i < result.Length; i++) result[i] = (data[start + (i >> 3)] >> (i & 7) & 0x1) == 1; return result; } protected static byte[] SetBits(bool[] bits) { byte[] data = new byte[bits.Length>>3]; for (int i = 0; i < bits.Length; i++) data[i>>3] |= (byte)(bits[i] ? 1 << (i&0x7) : 0); return data; } /// /// Injects supplementary TM/HM compatibility which is not present in the generation specific format. /// /// Data to read from /// Starting offset to read at /// Amount of bytes to decompose into bits internal void AddTMHM(byte[] data, int start = 0, int length = -1) => TMHM = GetBits(data, start, length); /// /// Injects supplementary Type Tutor compatibility which is not present in the generation specific format. /// /// Data to read from /// Starting offset to read at /// Amount of bytes to decompose into bits internal void AddTypeTutors(byte[] data, int start = 0, int length = -1) => TypeTutors = GetBits(data, start, length); /// /// 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 FormeIndex(int species, int forme) { if (forme <= 0) // no forme requested return species; if (FormStatsIndex <= 0) // no formes present return species; if (forme >= FormeCount) // beyond range of species' formes return species; return FormStatsIndex + forme - 1; } /// /// Gets a random valid gender for the entry. /// public int RandomGender() { var fix = FixedGender; return fix >= 0 ? fix : Util.Rand.Next(2); } public bool IsDualGender => FixedGender < 0; public int FixedGender { get { if (Genderless) return 2; if (OnlyFemale) return 1; if (OnlyMale) return 0; return -1; } } /// /// Indicates that the entry is exclusively Genderless. /// public bool Genderless => Gender == 255; /// /// Indicates that the entry is exclusively Female gendered. /// public bool OnlyFemale => Gender == 254; /// /// Indicates that the entry is exclusively Male gendered. /// public bool OnlyMale => Gender == 0; /// /// Indicates if the entry has Formes or not. /// public bool HasFormes => FormeCount > 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 IsFormeWithinRange(int forme) { if (forme == 0) return true; return forme < FormeCount; } /// /// Checks to see if the provided Types match the entry's types. /// /// Input order matters! If input order does not matter, use . /// 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; } }