private CheckResult VerifyForm(LegalityAnalysis data) { var pkm = data.pkm; var PersonalInfo = data.PersonalInfo; int count = PersonalInfo.FormCount; var form = pkm.Form; if (count <= 1 && form == 0) { return(VALID); // no forms to check } var species = pkm.Species; var enc = data.EncounterMatch; var Info = data.Info; if (!PersonalInfo.IsFormWithinRange(form) && !FormInfo.IsValidOutOfBoundsForm(species, form, Info.Generation)) { return(GetInvalid(string.Format(LFormInvalidRange, count - 1, form))); } switch (enc) { case EncounterSlot w when w.Area.Type == SlotType.FriendSafari: VerifyFormFriendSafari(data); break; case EncounterEgg e when FormInfo.IsTotemForm(species, form, e.Generation): return(GetInvalid(LFormInvalidGame)); } switch ((Species)species) { case Pikachu when Info.Generation == 6: // Cosplay bool isStatic = enc is EncounterStatic; bool validCosplay = form == (isStatic ? enc.Form : 0); if (!validCosplay) { return(GetInvalid(isStatic ? LFormPikachuCosplayInvalid : LFormPikachuCosplay)); } break; case Pikachu when Info.Generation >= 7: // Cap bool validCap = form == (enc is EncounterInvalid or EncounterEgg ? 0 : enc.Form); if (!validCap) { bool gift = enc is MysteryGift g && g.Form != form; var msg = gift ? LFormPikachuEventInvalid : LFormInvalidGame; return(GetInvalid(msg)); } break; case Unown when Info.Generation == 2 && form >= 26: return(GetInvalid(string.Format(LFormInvalidRange, "Z", form == 26 ? "!" : "?"))); case Giratina when form == 1 ^ pkm.HeldItem == 112: // Giratina, Origin form only with Griseous Orb return(GetInvalid(LFormItemInvalid)); case Arceus: { int arceus = GetArceusFormFromHeldItem(pkm.HeldItem, pkm.Format); return(arceus != form?GetInvalid(LFormItemInvalid) : GetValid(LFormItem)); } case Keldeo when pkm.Format < 8 && enc.Generation != 5: // can mismatch in gen5 via BW tutor and transfer up // can mismatch in gen8+ as the form activates in battle when knowing the move; outside of battle can be either state. bool hasSword = pkm.HasMove((int)Move.SecretSword); bool isSword = pkm.Form == 1; if (isSword != hasSword) { return(GetInvalid(LMoveKeldeoMismatch)); } break; case Genesect: { int genesect = GetGenesectFormFromHeldItem(pkm.HeldItem); return(genesect != form?GetInvalid(LFormItemInvalid) : GetValid(LFormItem)); } case Greninja: if (form > 1) // Ash Battle Bond active { return(GetInvalid(LFormBattle)); } if (form != 0 && enc is not MysteryGift) // Formes are not breedable, MysteryGift already checked { return(GetInvalid(string.Format(LFormInvalidRange, 0, form))); } break; case Scatterbug or Spewpa: if (form > 17) // Fancy & Pokéball { return(GetInvalid(LFormVivillonEventPre)); } if (pkm is not IRegionOrigin tr) { break; } if (!Vivillon3DS.IsPatternValid(form, (byte)tr.Country, (byte)tr.Region)) { data.AddLine(Get(LFormVivillonInvalid, Severity.Fishy)); } break; case Vivillon: if (form > 17) // Fancy & Pokéball { if (enc is not MysteryGift) { return(GetInvalid(LFormVivillonInvalid)); } return(GetValid(LFormVivillon)); } if (pkm is not IRegionOrigin trv) { break; } if (!Vivillon3DS.IsPatternValid(form, (byte)trv.Country, (byte)trv.Region)) { data.AddLine(Get(LFormVivillonInvalid, Severity.Fishy)); } break; case Floette when form == 5: // Floette Eternal Flower -- Never Released if (enc is not MysteryGift) { return(GetInvalid(LFormEternalInvalid)); } return(GetValid(LFormEternal)); case Meowstic when form != pkm.Gender: return(GetInvalid(LGenderInvalidNone)); case Silvally: { int silvally = GetSilvallyFormFromHeldItem(pkm.HeldItem); return(silvally != form?GetInvalid(LFormItemInvalid) : GetValid(LFormItem)); } // Form doesn't exist in SM; cannot originate from that game. case Lillipup when enc.Generation == 7 && form == 1 && pkm.SM: case Lycanroc when enc.Generation == 7 && form == 2 && pkm.SM: return(GetInvalid(LFormInvalidGame)); // Toxel encounters have already been checked for the nature-specific evolution criteria. case Toxtricity when enc.Species == (int)Toxtricity: { // The game enforces the Nature for Toxtricity encounters too! if (pkm.Form != EvolutionMethod.GetAmpLowKeyResult(pkm.Nature)) { return(GetInvalid(LFormInvalidNature)); } break; } // Impossible Egg forms case Rotom when pkm.IsEgg && form != 0: case Furfrou when pkm.IsEgg && form != 0: return(GetInvalid(LEggSpecies)); // Party Only Forms case Shaymin: case Furfrou: case Hoopa: if (form != 0 && pkm.Box > -1 && pkm.Format <= 6) // has form but stored in box { return(GetInvalid(LFormParty)); } break; } var format = pkm.Format; if (FormInfo.IsBattleOnlyForm(species, form, format)) { return(GetInvalid(LFormBattle)); } if (form == 0) { return(VALID); } // everything below here is not Form 0, so it has a form. if (format >= 7 && Info.Generation < 7) { if (species == 25 || Legal.AlolanOriginForms.Contains(species) || Legal.AlolanVariantEvolutions12.Contains(enc.Species)) { return(GetInvalid(LFormInvalidGame)); } } if (format >= 8 && Info.Generation < 8) { var orig = enc.Species; if (Legal.GalarOriginForms.Contains(species) || Legal.GalarVariantFormEvolutions.Contains(orig)) { if (species == (int)Meowth && enc.Form != 2) { // We're okay here. There's also Alolan Meowth... } else if (((Species)orig is MrMime or MimeJr) && pkm.CurrentLevel > enc.LevelMin && Info.Generation >= 4) { // We're okay with a Mime Jr. that has evolved via level up. } else if (enc.Version != GameVersion.GO) { return(GetInvalid(LFormInvalidGame)); } } }
public static IEnumerable <EncounterEgg> GenerateEggs(PKM pkm, IReadOnlyList <EvoCriteria> chain, int generation, bool all = false) { System.Diagnostics.Debug.Assert(generation >= 3); // if generating Gen2 eggs, use the other generator. int currentSpecies = pkm.Species; if (!Breeding.CanHatchAsEgg(currentSpecies)) { yield break; } var currentForm = pkm.Form; if (!Breeding.CanHatchAsEgg(currentSpecies, currentForm, generation)) { yield break; // can't originate from eggs } // version is a true indicator for all generation 3-5 origins var ver = (GameVersion)pkm.Version; if (!Breeding.CanGameGenerateEggs(ver)) { yield break; } int lvl = EggStateLegality.GetEggLevel(generation); int max = GetMaxSpeciesOrigin(generation); var(species, form) = GetBaseSpecies(chain, 0); if ((uint)species <= max) { // NOTE: THE SPLIT-BREED SECTION OF CODE SHOULD BE EXACTLY THE SAME AS THE BELOW SECTION if (FormInfo.IsBattleOnlyForm(species, form, generation)) { form = FormInfo.GetOutOfBattleForm(species, form, generation); } if (Breeding.CanHatchAsEgg(species, form, ver)) { yield return(new EncounterEgg(species, form, lvl, generation, ver)); if (generation > 5 && (pkm.WasTradedEgg || all) && HasOtherGamePair(ver)) { yield return(new EncounterEgg(species, form, lvl, generation, GetOtherTradePair(ver))); } } } if (!Breeding.GetSplitBreedGeneration(generation).Contains(currentSpecies)) { yield break; // no other possible species } var otherSplit = species; (species, form) = GetBaseSpecies(chain, 1); if ((uint)species == otherSplit) { yield break; } if (species <= max) { // NOTE: THIS SECTION OF CODE SHOULD BE EXACTLY THE SAME AS THE ABOVE SECTION if (FormInfo.IsBattleOnlyForm(species, form, generation)) { form = FormInfo.GetOutOfBattleForm(species, form, generation); } if (Breeding.CanHatchAsEgg(species, form, ver)) { yield return(new EncounterEgg(species, form, lvl, generation, ver)); if (generation > 5 && (pkm.WasTradedEgg || all) && HasOtherGamePair(ver)) { yield return(new EncounterEgg(species, form, lvl, generation, GetOtherTradePair(ver))); } } } }