using System; using System.Linq; namespace PKHeX.Core { /// /// Base class for Pokédex logic operations. /// public abstract class ZukanBase { protected readonly SaveFile SAV; public readonly int PokeDex; protected ZukanBase(SaveFile sav, int dex) { SAV = sav; PokeDex = dex; } #region Overall Info /// Count of unique Species Seen public int SeenCount => Enumerable.Range(1, SAV.MaxSpeciesID).Count(GetSeen); /// Count of unique Species Caught (Owned) public int CaughtCount => Enumerable.Range(1, SAV.MaxSpeciesID).Count(GetCaught); public decimal PercentSeen => (decimal)SeenCount / SAV.MaxSpeciesID; public decimal PercentCaught => (decimal)CaughtCount / SAV.MaxSpeciesID; #endregion /// Gets if the Species has been Seen by the player. public abstract bool GetSeen(int species); /// Gets if the Species has been Caught (Owned) by the player. public abstract bool GetCaught(int species); /// Adds the Pokémon's information to the Pokédex. public abstract void SetDex(PKM pkm); #region Overall Manipulation public abstract void SeenNone(); public abstract void CaughtNone(); public abstract void SeenAll(bool shinyToo = false); public abstract void CompleteDex(bool shinyToo = false); public abstract void CaughtAll(bool shinyToo = false); public abstract void SetAllSeen(bool value = true, bool shinyToo = false); public abstract void SetDexEntryAll(int species, bool shinyToo = false); public abstract void ClearDexEntryAll(int species); #endregion } /// /// Base class for Pokédex operations, exposing the shared structure features used by Generations 5, 6, and 7. /// public abstract class Zukan : ZukanBase { protected readonly int PokeDexLanguageFlags; protected Zukan(SaveFile sav, int dex, int langflag) : base(sav, dex) { PokeDexLanguageFlags = langflag; if (langflag > dex) throw new ArgumentException(nameof(langflag)); } protected abstract int OFS_SEEN { get; } protected abstract int OFS_CAUGHT { get; } protected abstract int BitSeenSize { get; } protected abstract int DexLangFlagByteCount { get; } protected abstract int DexLangIDCount { get; } protected abstract int GetDexLangFlag(int lang); protected abstract bool GetSaneFormsToIterate(int species, out int formStart, out int formEnd, int formIn); protected virtual void SetSpindaDexData(PKM pkm, bool alreadySeen) { } protected abstract void SetAllDexFlagsLanguage(int bit, int lang, bool value = true); protected abstract void SetAllDexSeenFlags(int baseBit, int form, int gender, bool isShiny, bool value = true); protected bool GetFlag(int ofs, int bitIndex) => SAV.GetFlag(PokeDex + ofs + (bitIndex >> 3), bitIndex); protected void SetFlag(int ofs, int bitIndex, bool value = true) => SAV.SetFlag(PokeDex + ofs + (bitIndex >> 3), bitIndex, value); public override bool GetCaught(int species) => GetFlag(OFS_CAUGHT, species - 1); public virtual void SetCaught(int species, bool value = true) => SetFlag(OFS_CAUGHT, species - 1, value); public override bool GetSeen(int species) { // check all 4 seen flags (gender/shiny) for (int i = 0; i < 4; i++) { if (GetSeen(species, i)) return true; } return false; } public bool GetSeen(int species, int bitRegion) => GetFlag(OFS_SEEN + (bitRegion * BitSeenSize), species - 1); public void SetSeen(int species, int bitRegion, bool value) => SetFlag(OFS_SEEN + (bitRegion * BitSeenSize), species - 1, value); public bool GetDisplayed(int bit, int bitRegion) => GetFlag(OFS_SEEN + ((bitRegion + 4) * BitSeenSize), bit); public void SetDisplayed(int bit, int bitRegion, bool value) => SetFlag(OFS_SEEN + ((bitRegion + 4) * BitSeenSize), bit, value); public bool GetLanguageFlag(int bit, int lang) => GetFlag(PokeDexLanguageFlags, (bit * DexLangIDCount) + lang); public void SetLanguageFlag(int bit, int lang, bool value) => SetFlag(PokeDexLanguageFlags, (bit * DexLangIDCount) + lang, value); public virtual void SetSeen(int species, bool value = true) { if (!value) { ClearSeen(species); return; } // check all 4 seen flags (gender/shiny) for (int i = 0; i < 4; i++) { if (GetFlag(OFS_SEEN + (i * BitSeenSize), species - 1)) return; } var gender = SAV.Personal[species].RandomGender() & 1; SetAllDexSeenFlags(species - 1, 0, gender, false); } private void ClearSeen(int species) { SetCaught(species, false); for (int i = 0; i < 4; i++) SetFlag(OFS_SEEN + (i * BitSeenSize), species - 1, false); } public override void SetDex(PKM pkm) { if ((uint)(pkm.Species - 1) >= (uint)SAV.MaxSpeciesID) // out of range return; if (pkm.IsEgg) // do not add return; int species = pkm.Species; if (species == (int)Species.Spinda) SetSpindaDexData(pkm, GetSeen(species)); int bit = pkm.Species - 1; int form = pkm.Form; int gender = pkm.Gender & 1; bool shiny = pkm.IsShiny; int lang = pkm.Language; SetDex(species, bit, form, gender, shiny, lang); } protected virtual void SetDex(int species, int bit, int form, int gender, bool shiny, int lang) { SetCaught(species); // Set the Owned Flag SetAllDexSeenFlags(bit, form, gender, shiny); // genderless -> male SetAllDexFlagsLanguage(bit, lang); } protected void SetDexFlags(int baseBit, int formBit, int gender, int shiny, bool value = true) { int shift = (gender & 1) | (shiny << 1); // Set the [Species/Gender/Shiny] Seen Flag SetFlag(OFS_SEEN + (shift * BitSeenSize), baseBit, value); // Set the Display flag if none are set SetDisplayedFlag(baseBit, formBit, value, shift); } protected virtual void SetDisplayedFlag(int baseBit, int formBit, bool value, int shift) { var bit = formBit >= 0 ? formBit : baseBit; if (!value) { SetDisplayed(bit, shift, false); return; } bool displayed = GetIsSpeciesFormAnyDisplayed(baseBit, formBit); if (displayed) return; // no need to set another bit SetDisplayed(bit, shift, true); } private bool GetIsSpeciesFormAnyDisplayed(int baseBit, int formBit) { // Check Displayed Status for base form for (int i = 0; i < 4; i++) { if (GetDisplayed(baseBit, i)) return true; } if (baseBit == formBit) return false; // If form is not base form, check form too for (int i = 0; i < 4; i++) { if (GetDisplayed(formBit, i)) return true; } return false; } // Bulk Manipulation public override void SeenNone() => SetDexEntriesAll(false, shinyToo: true); public override void CaughtNone() => SetAllCaught(false, true); public override void SeenAll(bool shinyToo = false) => SetAllSeen(shinyToo); public override void CompleteDex(bool shinyToo = false) => SetDexEntriesAll(shinyToo: shinyToo); public override void CaughtAll(bool shinyToo = false) { SetAllSeen(true, shinyToo); SetAllCaught(true, shinyToo); } public void SetAllCaught(bool value = true, bool shinyToo = false) { for (int i = 0; i < SAV.MaxSpeciesID; i++) { int species = i + 1; SetCaught(species, value); // Set the Owned Flag SetSeenSingle(i + 1, value, shinyToo); } } public override void SetAllSeen(bool value = true, bool shinyToo = false) { for (int i = 0; i < SAV.MaxSpeciesID; i++) SetSeenSingle(i + 1, value, shinyToo); } public override void SetDexEntryAll(int species, bool shinyToo = false) { SetSeenSingle(species, true, shinyToo); SetCaughtSingle(species); } public override void ClearDexEntryAll(int species) { SetSeenSingle(species, false); SetCaughtSingle(species, false); } public void SetDexEntriesAll(bool value = true, int max = -1, bool shinyToo = false) { if (max <= 0) max = SAV.MaxSpeciesID; for (int i = 1; i <= max; i++) { SetSeenSingle(i, value, shinyToo); SetCaughtSingle(i, value); } } public void SetCaughtSingle(int species, bool value = true) { SetCaught(species, value); int baseBit = species - 1; SetAllDexFlagsLanguage(baseBit, value); } public void SetSeenSingle(int species, bool seen = true, bool shinyToo = false) { SetSeen(species, seen); var entry = SAV.Personal[species]; int baseBit = species - 1; int fc = entry.FormCount; for (int f = 0; f < fc; f++) { if (!entry.OnlyFemale) { SetAllDexSeenFlags(baseBit, f, 0, false, seen); if (shinyToo) SetAllDexSeenFlags(baseBit, f, 0, true, seen); } if (!entry.OnlyMale && !entry.Genderless) { SetAllDexSeenFlags(baseBit, f, 1, false, seen); if (shinyToo) SetAllDexSeenFlags(baseBit, f, 1, true, seen); } } } protected void SetAllDexFlagsLanguage(int bit, bool value = true) { for (int i = 1; i <= DexLangIDCount + 1; i++) SetAllDexFlagsLanguage(bit, i, value); } } }