using System.Collections.Generic; using System.Linq; using pk3DS.Core.Structures.PersonalInfo; namespace pk3DS.Core.Randomizers { public class SpeciesRandomizer { private readonly GameConfig Game; private readonly PersonalInfo[] SpeciesStat; private readonly int MaxSpeciesID; public SpeciesRandomizer(GameConfig config) { Game = config; MaxSpeciesID = Game.Info.MaxSpeciesID; SpeciesStat = Game.Personal.Table; } /// /// To be called after the allowed species are provided. /// public void Initialize() { var list = InitializeSpeciesList(); RandSpec = new GenericRandomizer(list); } #region Randomizer Settings public bool G1 = true; public bool G2 = true; public bool G3 = true; public bool G4 = true; public bool G5 = true; public bool G6 = true; public bool G7 = false; public bool L = false; public bool E = false; public bool Shedinja = false; public bool rEXP = false; public bool rBST = true; public bool rType = false; #endregion #region Random Species Filtering Parameters private GenericRandomizer RandSpec; private int loopctr; private const int l = 10; // tweakable scalars private const int h = 11; #endregion internal int GetRandomSpecies(int oldSpecies, int bannedSpecies) { // Get a new random species PersonalInfo oldpkm = SpeciesStat[oldSpecies]; loopctr = 0; // altering calculations to prevent infinite loops int newSpecies; while (!GetNewSpecies(bannedSpecies, oldpkm, out newSpecies)) loopctr++; return newSpecies; } public int GetRandomSpeciesType(int oldSpecies, int type) { // Get a new random species PersonalInfo oldpkm = SpeciesStat[oldSpecies]; loopctr = 0; // altering calculations to prevent infinite loops int newSpecies; while (!GetNewSpecies(oldSpecies, oldpkm, out newSpecies) || !GetIsTypeMatch(newSpecies, type)) loopctr++; return newSpecies; } private bool GetIsTypeMatch(int newSpecies, int type) => type == -1 || SpeciesStat[newSpecies].Types.Any(z => z == type) || loopctr > 9000; public int GetRandomSpecies(int oldSpecies) { // Get a new random species PersonalInfo oldpkm = SpeciesStat[oldSpecies]; loopctr = 0; // altering calculations to prevent infinite loops int newSpecies; while (!GetNewSpecies(oldSpecies, oldpkm, out newSpecies)) { if (loopctr > 0x0001_0000) { PersonalInfo pkm = SpeciesStat[newSpecies]; if (IsSpeciesBSTBad(oldpkm, pkm) && loopctr > 0x0001_1000) // keep trying for at minimum BST continue; return newSpecies; // failed to find any match based on criteria, return random species that may or may not match criteria } loopctr++; } return newSpecies; } private bool IsSpeciesReplacementBad(int newSpecies, int currentSpecies) { return newSpecies == currentSpecies && loopctr < MaxSpeciesID * 10; } private bool IsSpeciesEXPRateBad(PersonalInfo oldpkm, PersonalInfo pkm) { if (!rEXP) return false; // Experience Growth Rate matches return oldpkm.EXPGrowth != pkm.EXPGrowth; } private bool IsSpeciesTypeBad(PersonalInfo oldpkm, PersonalInfo pkm) { if (!rType) return false; // Type has to be somewhat similar return !oldpkm.Types.Any(z => pkm.Types.Contains(z)); } private bool IsSpeciesBSTBad(PersonalInfo oldpkm, PersonalInfo pkm) { if (!rBST) return false; // Base stat total has to be close to original BST int expand = loopctr / MaxSpeciesID; int lo = oldpkm.BST * l / (h + expand); int hi = oldpkm.BST * (h + expand) / l; return lo > pkm.BST || pkm.BST > hi; } private int[] InitializeSpeciesList() { List list = new List(); if (G1) AddGen1Species(list); if (G2) AddGen2Species(list); if (G3) AddGen3Species(list); if (G4) AddGen4Species(list); if (G5) AddGen5Species(list); if (G6) AddGen6Species(list); if (G7) AddGen7Species(list); return list.Count == 0 ? RandomSpeciesList : list.ToArray(); } private void AddGen1Species(List list) { list.AddRange(Enumerable.Range(1, 143)); // Bulbasaur - Snorlax list.AddRange(Enumerable.Range(147, 3)); // Dratini - Dragonite if (L) { list.AddRange(Enumerable.Range(144, 3)); // Birds list.Add(150); // Mewtwo } if (E) list.Add(151); // Mew } private void AddGen2Species(List list) { list.AddRange(Enumerable.Range(152, 91)); // Chikorita - Blissey list.AddRange(Enumerable.Range(246, 3)); // Larvitar - Tyranitar if (L) { list.AddRange(Enumerable.Range(243, 3)); // Beasts list.AddRange(Enumerable.Range(249, 2)); // Lugia & Ho-Oh } if (E) list.Add(251); // Celebi } private void AddGen3Species(List list) { list.AddRange(Enumerable.Range(252, 40)); // Treecko - Ninjask list.AddRange(Enumerable.Range(293, 84)); // Whismur - Metagross if (Shedinja) list.Add(292); // Shedinja if (L) list.AddRange(Enumerable.Range(377, 8)); // Regi, Lati, Mascot if (E) list.AddRange(Enumerable.Range(385, 2)); // Jirachi/Deoxys } private void AddGen4Species(List list) { list.AddRange(Enumerable.Range(387, 93)); // Turtwig - Rotom if (L) list.AddRange(Enumerable.Range(480, 9)); // Sinnoh Legends if (E) list.AddRange(Enumerable.Range(489, 5)); // Phione, Manaphy, Darkrai, Shaymin, Arceus } private void AddGen5Species(List list) { list.AddRange(Enumerable.Range(495, 143)); // Snivy - Volcarona if (L) list.AddRange(Enumerable.Range(638, 9)); // Unova Legends if (E) list.Add(494); list.AddRange(Enumerable.Range(647, 3)); // Victini, Keldeo, Meloetta, Genesect } private void AddGen6Species(List list) { list.AddRange(Enumerable.Range(650, 66)); // Chespin - Noivern if (L) list.AddRange(Enumerable.Range(716, 3)); // Kalos Legends if (E) list.AddRange(Enumerable.Range(719, 3)); // Diancie, Hoopa, Volcanion } private void AddGen7Species(List list) { list.AddRange(Enumerable.Range(722, 67)); // Rowlet - Kommo-o if (L) list.AddRange(Enumerable.Range(785, 16)); // Tapus, Legends, UBs if (E) list.AddRange(Enumerable.Range(801, 2)); // Magearna, Marshadow if (MaxSpeciesID == 807) // USUM { if (L) list.AddRange(Enumerable.Range(803, 4)); // Poipole, Naganadel, Stakataka, Blacephalon if (E) list.Add(807); // Zeraora } } public int[] RandomSpeciesList => Enumerable.Range(1, MaxSpeciesID).ToArray(); private bool GetNewSpecies(int currentSpecies, PersonalInfo oldpkm, out int newSpecies) { newSpecies = RandSpec.Next(); PersonalInfo pkm = SpeciesStat[newSpecies]; // Verify it meets specifications if (IsSpeciesReplacementBad(newSpecies, currentSpecies)) // no A->A randomization return false; if (IsSpeciesEXPRateBad(oldpkm, pkm)) return false; if (IsSpeciesTypeBad(oldpkm, pkm)) return false; if (IsSpeciesBSTBad(oldpkm, pkm)) return false; return true; } } }