Example #1
0
        private void VerifyHTMemoryTransferTo7(LegalityAnalysis data, PKM pkm, LegalInfo Info)
        {
            // Bank Transfer adds in the Link Trade Memory.
            // Trading 7<->7 between games (not Bank) clears this data.
            if (pkm.HT_Memory == 0)
            {
                VerifyHTMemoryNone(data, pkm);
                return;
            }

            // Transfer 6->7 & withdraw to same HT => keeps past gen memory
            // Don't require link trade memory for these past gen cases
            int gen = Info.Generation;

            if (3 <= gen && gen < 7 && pkm.CurrentHandler == 1)
            {
                return;
            }

            if (pkm.HT_Memory != 4)
            {
                data.AddLine(Severity.Invalid, LMemoryIndexLinkHT, CheckIdentifier.Memory);
            }
            if (pkm.HT_TextVar != 0)
            {
                data.AddLine(Severity.Invalid, LMemoryIndexArgHT, CheckIdentifier.Memory);
            }
            if (pkm.HT_Intensity != 1)
            {
                data.AddLine(Severity.Invalid, LMemoryIndexIntensityHT1, CheckIdentifier.Memory);
            }
            if (pkm.HT_Feeling > 10)
            {
                data.AddLine(Severity.Invalid, LMemoryIndexFeelHT09, CheckIdentifier.Memory);
            }
        }
 private CheckResult VerifyBallEquals(LegalityAnalysis data, int ball) => GetResult(ball == data.pkm.Ball);
        private CheckResult VerifyBallEggGen6(LegalityAnalysis data)
        {
            var pkm     = data.pkm;
            int species = data.EncounterMatch.Species;

            if (pkm.Gender == 2 || Legal.BreedMaleOnly.Contains(species)) // Genderless
            {
                return(VerifyBallEquals(data, (int)Poke));                // Must be Pokéball as ball can only pass via mother (not Ditto!)
            }
            Ball ball = (Ball)pkm.Ball;

            if (ball == Safari) // Safari Ball
            {
                if (!Legal.Inherit_Safari.Contains(species))
                {
                    return(GetInvalid(LBallSpecies));
                }
                if (pkm.AbilityNumber == 4)
                {
                    return(GetInvalid(LBallAbility));
                }
                return(GetValid(LBallSpeciesPass));
            }
            if (ball.IsApricornBall()) // Apricorn Ball
            {
                if (!Legal.Inherit_Apricorn6.Contains(species))
                {
                    return(GetInvalid(LBallSpecies));
                }
                if (pkm.AbilityNumber == 4)
                {
                    return(GetInvalid(LBallAbility));
                }
                return(GetValid(LBallSpeciesPass));
            }
            if (ball == Sport) // Sport Ball
            {
                if (!Legal.Inherit_Sport.Contains(species))
                {
                    return(GetInvalid(LBallSpecies));
                }
                if (pkm.AbilityNumber == 4)
                {
                    return(GetInvalid(LBallAbility));
                }
                return(GetValid(LBallSpeciesPass));
            }
            if (ball == Dream) // Dream Ball
            {
                if (pkm.AbilityNumber == 4 && Legal.Ban_DreamHidden.Contains(species))
                {
                    return(GetInvalid(LBallAbility));
                }
                if (Legal.Inherit_Dream.Contains(species))
                {
                    return(GetValid(LBallSpeciesPass));
                }
                return(GetInvalid(LBallSpecies));
            }
            if (Dusk <= ball && ball <= Quick) // Dusk Heal Quick
            {
                if (!Legal.Ban_Gen4Ball_6.Contains(species))
                {
                    return(GetValid(LBallSpeciesPass));
                }
                return(GetInvalid(LBallSpecies));
            }
            if (Ultra <= ball && ball <= Premier) // Don't worry, Safari was already checked.
            {
                if (Legal.Ban_Gen3Ball.Contains(species))
                {
                    return(GetInvalid(LBallSpecies));
                }
                if (pkm.AbilityNumber == 4 && Legal.Ban_Gen3BallHidden.Contains(pkm.SpecForm))
                {
                    return(GetInvalid(LBallAbility));
                }
                return(GetValid(LBallSpeciesPass));
            }

            if (species > 650 && species != 700) // Sylveon
            {
                if (Legal.WildPokeballs6.Contains(pkm.Ball))
                {
                    return(GetValid(LBallSpeciesPass));
                }
                return(GetInvalid(LBallSpecies));
            }

            if (ball >= Dream)
            {
                return(GetInvalid(LBallUnavailable));
            }

            return(NONE);
        }
Example #4
0
        public override void Verify(LegalityAnalysis data)
        {
            var result = VerifyAbility(data);

            data.AddLine(result);
        }
Example #5
0
        public override void Verify(LegalityAnalysis data)
        {
            var pkm = data.pkm;

            switch (data.EncounterMatch)
            {
            case EncounterTrade _:
            case MysteryGift g when !g.IsEgg:
            case EncounterStatic5N _:
                return;     // already verified
            }

            var ot = pkm.OT_Name;

            if (ot.Length == 0)
            {
                data.AddLine(GetInvalid(LOTShort));
            }

            if (pkm.TID == 0 && pkm.SID == 0)
            {
                data.AddLine(Get(LOT_IDs0, Severity.Fishy));
            }
            else if (pkm.VC)
            {
                if (pkm.SID != 0)
                {
                    data.AddLine(GetInvalid(LOT_SID0Invalid));
                }
            }
            else if (pkm.TID == pkm.SID)
            {
                data.AddLine(Get(LOT_IDEqual, Severity.Fishy));
            }
            else if (pkm.TID == 0)
            {
                data.AddLine(Get(LOT_TID0, Severity.Fishy));
            }
            else if (pkm.SID == 0)
            {
                data.AddLine(Get(LOT_SID0, Severity.Fishy));
            }
            else if ((pkm.TID == 12345 && pkm.SID == 54321) || IsOTNameSuspicious(ot))
            {
                data.AddLine(Get(LOTSuspicious, Severity.Fishy));
            }

            if (pkm.VC)
            {
                VerifyOTG1(data);
            }
            else if (ot.Length > Legal.GetMaxLengthOT(data.Info.Generation, (LanguageID)pkm.Language))
            {
                if (!IsEdgeCaseLength(pkm, data.EncounterOriginal, ot))
                {
                    data.AddLine(Get(LOTLong, Severity.Invalid));
                }
            }

            if (ParseSettings.CheckWordFilter)
            {
                if (WordFilter.IsFiltered(ot, out string bad))
                {
                    data.AddLine(GetInvalid($"Wordfilter: {bad}"));
                }
                if (WordFilter.IsFiltered(pkm.HT_Name, out bad))
                {
                    data.AddLine(GetInvalid($"Wordfilter: {bad}"));
                }
                if (ContainsTooManyNumbers(ot, data.Info.Generation))
                {
                    data.AddLine(GetInvalid($"Wordfilter: Too many numbers."));
                }
            }
        }
Example #6
0
        public override void Verify(LegalityAnalysis data)
        {
            var pkm            = data.pkm;
            var EncounterMatch = data.EncounterOriginal;

            // If the Pokémon is not nicknamed, it should match one of the language strings.
            if (pkm.Nickname.Length == 0)
            {
                data.AddLine(GetInvalid(LNickLengthShort));
                return;
            }
            if (pkm.Species > SpeciesName.SpeciesLang[0].Count)
            {
                data.AddLine(Get(LNickLengthShort, Severity.Indeterminate));
                return;
            }

            if (pkm.VC && pkm.IsNicknamed)
            {
                VerifyG1NicknameWithinBounds(data, pkm.Nickname);
            }
            else if (EncounterMatch is MysteryGift m)
            {
                if (pkm.IsNicknamed && !m.IsEgg)
                {
                    data.AddLine(Get(LEncGiftNicknamed, ParseSettings.NicknamedMysteryGift));
                }
            }

            if (EncounterMatch is EncounterTrade t)
            {
                VerifyNicknameTrade(data, t);
                return;
            }

            if (pkm.IsEgg)
            {
                VerifyNicknameEgg(data);
                return;
            }

            string nickname = pkm.Nickname.Replace('\'', '’');

            if (VerifyUnNicknamedEncounter(data, pkm, nickname))
            {
                return;
            }

            // Non-nicknamed strings have already been checked.
            if (ParseSettings.CheckWordFilter && pkm.IsNicknamed)
            {
                if (WordFilter.IsFiltered(nickname, out string bad))
                {
                    data.AddLine(GetInvalid($"Wordfilter: {bad}"));
                }
                if (TrainerNameVerifier.ContainsTooManyNumbers(nickname, data.Info.Generation))
                {
                    data.AddLine(GetInvalid("Wordfilter: Too many numbers."));
                }
            }
        }
Example #7
0
        public override void Verify(LegalityAnalysis data)
        {
            var pkm = data.pkm;

            if (pkm.IsEgg)
            {
                VerifyMiscEggCommon(data);

                if (pkm is IContestStats s && s.HasContestStats())
                {
                    data.AddLine(GetInvalid(LEggContest, Egg));
                }

                switch (pkm)
                {
                case PK5 pk5 when pk5.PokeStarFame != 0 && pk5.IsEgg:
                    data.AddLine(GetInvalid(LEggShinyPokeStar, Egg));
                    break;

                case PK4 pk4 when pk4.ShinyLeaf != 0:
                    data.AddLine(GetInvalid(LEggShinyLeaf, Egg));
                    break;

                case PK4 pk4 when pk4.PokéathlonStat != 0:
                    data.AddLine(GetInvalid(LEggPokeathlon, Egg));
                    break;

                case PK3 _ when pkm.Language != 1:      // All Eggs are Japanese and flagged specially for localized string
                    data.AddLine(GetInvalid(string.Format(LOTLanguage, LanguageID.Japanese, (LanguageID)pkm.Language), Egg));
                    break;
                }

                if (pkm is IHomeTrack home && home.Tracker != 0)
                {
                    data.AddLine(GetInvalid(LTransferTrackerShouldBeZero));
                }
            }
            else
            {
                VerifyMiscMovePP(data);
            }

            switch (pkm)
            {
            case PK7 pk7 when pk7.ResortEventStatus >= 20:
                data.AddLine(GetInvalid(LTransferBad));
                break;

            case PB7 pb7:
                VerifyBelugaStats(data, pb7);
                break;

            case PK8 pk8:
                VerifySWSHStats(data, pk8);
                break;
            }

            if (pkm.Format >= 6)
            {
                VerifyFullness(data, pkm);
            }

            VerifyMiscFatefulEncounter(data);
        }
Example #8
0
        private void VerifyMedalsRegular(LegalityAnalysis data)
        {
            var  pkm   = data.pkm;
            var  Info  = data.Info;
            uint value = BitConverter.ToUInt32(pkm.Data, 0x2C);

            if ((value & 3) != 0) // 2 unused flags
            {
                data.AddLine(GetInvalid(LSuperUnused));
            }
            int TrainCount = pkm.SuperTrainingMedalCount();

            if (pkm.IsEgg)
            {
                // Can't have any super training data as an egg.
                if (TrainCount > 0)
                {
                    data.AddLine(GetInvalid(LSuperEgg));
                }
                if (pkm.SecretSuperTrainingUnlocked)
                {
                    data.AddLine(GetInvalid(LSuperNoUnlocked));
                }
                if (pkm.SecretSuperTrainingComplete)
                {
                    data.AddLine(GetInvalid(LSuperNoComplete));
                }
                return;
            }

            if (Info.Generation >= 7 || Info.Generation <= 2)
            {
                // Can't have any super training data if it never visited Gen6.
                if (TrainCount > 0)
                {
                    data.AddLine(GetInvalid(LSuperUnavailable));
                }
                if (pkm.SecretSuperTrainingUnlocked)
                {
                    data.AddLine(GetInvalid(LSuperNoUnlocked));
                }
                if (pkm.SecretSuperTrainingComplete)
                {
                    data.AddLine(GetInvalid(LSuperNoComplete));
                }
                return;
            }

            if (pkm.Format >= 7)
            {
                // Gen6->Gen7 transfer wipes the two Secret flags.
                if (pkm.SecretSuperTrainingUnlocked)
                {
                    data.AddLine(GetInvalid(LSuperNoUnlocked));
                }
                if (pkm.SecretSuperTrainingComplete)
                {
                    data.AddLine(GetInvalid(LSuperNoComplete));
                }
                return;
            }

            // Only reach here if Format==6.
            if (TrainCount == 30 ^ pkm.SecretSuperTrainingComplete)
            {
                data.AddLine(GetInvalid(LSuperComplete));
            }
        }
Example #9
0
        public override void Verify(LegalityAnalysis data)
        {
            var pkm            = data.pkm;
            var EncounterMatch = data.EncounterOriginal;

            if (EncounterMatch is MysteryGift gift)
            {
                if (gift.Level != pkm.Met_Level && pkm.HasOriginalMetLocation)
                {
                    switch (gift)
                    {
                    case WC3 wc3 when wc3.Met_Level == pkm.Met_Level || wc3.IsEgg:
                        break;

                    case WC7 wc7 when wc7.MetLevel == pkm.Met_Level:
                        break;

                    default:
                        data.AddLine(GetInvalid(LLevelMetGift));
                        return;
                    }
                }
                if (gift.Level > pkm.CurrentLevel)
                {
                    data.AddLine(GetInvalid(LLevelMetGiftFail));
                    return;
                }
            }

            if (pkm.IsEgg)
            {
                int elvl = Legal.GetEggHatchLevel(pkm);
                if (elvl != pkm.CurrentLevel)
                {
                    data.AddLine(GetInvalid(string.Format(LEggFMetLevel_0, elvl)));
                    return;
                }

                var reqEXP = EncounterMatch is EncounterStatic s && s.Version == GameVersion.C
                    ? 125 // Gen2 Dizzy Punch gifts always have 125 EXP, even if it's more than the Lv5 exp required.
                    : Experience.GetEXP(elvl, pkm.Species, pkm.AltForm);
                if (reqEXP != pkm.EXP)
                {
                    data.AddLine(GetInvalid(LEggEXP));
                }
                return;
            }

            int lvl = pkm.CurrentLevel;

            if (lvl < pkm.Met_Level)
            {
                data.AddLine(GetInvalid(LLevelMetBelow));
            }
            else if (!EncounterMatch.IsWithinRange(pkm) && lvl != 100 && pkm.EXP == Experience.GetEXP(lvl, pkm.Species, pkm.AltForm))
            {
                data.AddLine(Get(LLevelEXPThreshold, Severity.Fishy));
            }
            else
            {
                data.AddLine(GetValid(LLevelMetSane));
            }
        }
Example #10
0
        private CheckResult VerifyBallEggGen7(LegalityAnalysis data)
        {
            var pkm     = data.pkm;
            int species = data.EncounterMatch.Species;

            if (722 <= species && species <= 730) // G7 Starters
            {
                return(VerifyBallEquals(data, 4));
            }

            int ball = pkm.Ball;

            if (ball == 0x05) // Safari Ball
            {
                if (!(Legal.Inherit_Safari.Contains(species) || Legal.Inherit_SafariMale.Contains(species)))
                {
                    return(GetInvalid(V121));
                }
                if (pkm.AbilityNumber == 4 && Legal.Ban_SafariBallHidden_7.Contains(species))
                {
                    return(GetInvalid(V122));
                }
                return(GetValid(V123));
            }
            if (0x10 < ball && ball < 0x18) // Apricorn Ball
            {
                if (!Legal.Inherit_Apricorn7.Contains(species))
                {
                    return(GetInvalid(V121));
                }
                if (pkm.AbilityNumber == 4 && Legal.Ban_NoHidden7Apricorn.Contains(species | pkm.AltForm << 11)) // lineage is 3->2->origin
                {
                    return(GetInvalid(V122));
                }
                return(GetValid(V123));
            }
            if (ball == 0x18) // Sport Ball
            {
                if (!Legal.Inherit_Sport.Contains(species))
                {
                    return(GetInvalid(V121));
                }
                if (pkm.AbilityNumber == 4 && (species == 313 || species == 314)) // Volbeat/Illumise
                {
                    return(GetInvalid(V122));
                }
                return(GetValid(V123));
            }
            if (ball == 0x19) // Dream Ball
            {
                if (Legal.Inherit_Dream.Contains(species) || Legal.Inherit_DreamMale.Contains(species))
                {
                    return(GetValid(V123));
                }
                return(GetInvalid(V121));
            }
            if (0x0D <= ball && ball <= 0x0F) // Dusk Heal Quick
            {
                if (!Legal.Ban_Gen4Ball_7.Contains(species))
                {
                    return(GetValid(V123));
                }
                return(GetInvalid(V121));
            }
            if (0x02 <= ball && ball <= 0x0C) // Don't worry, Ball # 0x05 was already checked.
            {
                if (!Legal.Ban_Gen3Ball_7.Contains(species))
                {
                    return(GetValid(V123));
                }
                return(GetInvalid(V121));
            }

            if (ball == 26)
            {
                if (species == 669 && pkm.AltForm == 3 && pkm.AbilityNumber == 4)
                {
                    return(GetInvalid(V122)); // Can't obtain Flabébé-Blue with Hidden Ability in wild
                }
                if ((species > 731 && species <= 785) || (Legal.PastGenAlolanNatives.Contains(species) && !Legal.PastGenAlolanNativesUncapturable.Contains(species)))
                {
                    return(GetValid(V123));
                }
                if (Legal.PastGenAlolanScans.Contains(species))
                {
                    return(GetValid(V123));
                }
                // next statement catches all new alolans
            }

            if (species > 721)
            {
                return(VerifyBallEquals(data, Legal.WildPokeballs7));
            }

            if (ball >= 27)
            {
                return(GetInvalid(V126));
            }

            return(NONE);
        }
Example #11
0
        private CheckResult VerifyBallEggGen6(LegalityAnalysis data)
        {
            var pkm     = data.pkm;
            int species = data.EncounterMatch.Species;

            if (pkm.Gender == 2 || Legal.BreedMaleOnly.Contains(species)) // Genderless
            {
                return(VerifyBallEquals(data, 4));                        // Must be Pokéball as ball can only pass via mother (not Ditto!)
            }
            int ball = pkm.Ball;

            if (ball >= 26)
            {
                return(GetInvalid(V126));
            }

            if (ball == 0x05) // Safari Ball
            {
                if (!Legal.Inherit_Safari.Contains(species))
                {
                    return(GetInvalid(V121));
                }
                if (pkm.AbilityNumber == 4)
                {
                    return(GetInvalid(V122));
                }
                return(GetValid(V123));
            }
            if (0x10 < ball && ball < 0x18) // Apricorn Ball
            {
                if (!Legal.Inherit_Apricorn6.Contains(species))
                {
                    return(GetInvalid(V121));
                }
                if (pkm.AbilityNumber == 4)
                {
                    return(GetInvalid(V122));
                }
                return(GetValid(V123));
            }
            if (ball == 0x18) // Sport Ball
            {
                if (!Legal.Inherit_Sport.Contains(species))
                {
                    return(GetInvalid(V121));
                }
                if (pkm.AbilityNumber == 4)
                {
                    return(GetInvalid(V122));
                }
                return(GetValid(V123));
            }
            if (ball == 0x19) // Dream Ball
            {
                if (pkm.AbilityNumber == 4 && Legal.Ban_DreamHidden.Contains(species))
                {
                    return(GetInvalid(V122));
                }
                if (Legal.Inherit_Dream.Contains(species))
                {
                    return(GetValid(V123));
                }
                return(GetInvalid(V121));
            }
            if (0x0D <= ball && ball <= 0x0F) // Dusk Heal Quick
            {
                if (!Legal.Ban_Gen4Ball_6.Contains(species))
                {
                    return(GetValid(V123));
                }
                return(GetInvalid(V121));
            }
            if (0x02 <= ball && ball <= 0x0C) // Don't worry, Ball # 0x05 was already checked.
            {
                if (Legal.Ban_Gen3Ball.Contains(species))
                {
                    return(GetInvalid(V121));
                }
                if (pkm.AbilityNumber == 4 && Legal.Ban_Gen3BallHidden.Contains(pkm.SpecForm))
                {
                    return(GetInvalid(V122));
                }
                return(GetValid(V123));
            }

            if (species > 650 && species != 700) // Sylveon
            {
                if (Legal.WildPokeballs6.Contains(pkm.Ball))
                {
                    return(GetValid(V123));
                }
                return(GetInvalid(V121));
            }

            return(NONE);
        }
Example #12
0
 private static bool UpdateIsValid(LegalityAnalysis la)
 {
     LegalityAnalyzers.Ribbon.Verify(la);
     return(la.Results.All(z => z.Valid));
 }
Example #13
0
 private static bool TryApplyRibbon(PKM pk, LegalityAnalysis la, string rib)
 {
     SetRibbonValue(pk, rib, 1);
     return(UpdateIsValid(la));
 }
Example #14
0
 private static bool TryRemoveRibbon(PKM pk, LegalityAnalysis la, string rib)
 {
     RemoveRibbon(pk, rib);
     return(UpdateIsValid(la));
 }
Example #15
0
File: Main.cs Project: kwsch/PKHeX
        private void updateLegality(LegalityAnalysis la = null, bool skipMoveRepop = false)
        {
            if (!fieldsLoaded)
                return;
            Legality = la ?? new LegalityAnalysis(pkm);
            if (!Legality.Parsed || HaX)
            {
                PB_Legal.Visible = false;
                return;
            }
            PB_Legal.Visible = true;

            PB_Legal.Image = Legality.Valid ? Resources.valid : Resources.warn;

            // Refresh Move Legality
            for (int i = 0; i < 4; i++)
                movePB[i].Visible = !Legality.vMoves[i].Valid && !HaX;
            
            for (int i = 0; i < 4; i++)
                relearnPB[i].Visible = !Legality.vRelearn[i].Valid && !HaX;

            if (skipMoveRepop)
                return;
            // Resort moves
            bool tmp = fieldsLoaded;
            fieldsLoaded = false;
            var cb = new[] {CB_Move1, CB_Move2, CB_Move3, CB_Move4 };
            var moves = Legality.AllSuggestedMovesAndRelearn;
            var moveList = GameInfo.MoveDataSource.OrderByDescending(m => moves.Contains(m.Value)).ToList();
            foreach (ComboBox c in cb)
            {
                var index = WinFormsUtil.getIndex(c);
                c.DataSource = new BindingSource(moveList, null);
                c.SelectedValue = index;
            }
            fieldsLoaded |= tmp;
        }
Example #16
0
File: Main.cs Project: kwsch/PKHeX
 private void showLegality(PKM pk, bool tabs, bool verbose, bool skipMoveRepop = false)
 {
     LegalityAnalysis la = new LegalityAnalysis(pk);
     if (!la.Parsed)
     {
         WinFormsUtil.Alert($"Checking legality of PK{pk.Format} files that originated from Gen{pk.GenNumber} is not supported.");
         return;
     }
     if (tabs)
         updateLegality(la, skipMoveRepop);
     WinFormsUtil.Alert(verbose ? la.VerboseReport : la.Report);
 }
Example #17
0
 public override void Verify(LegalityAnalysis data)
 {
     VerifyMedalsRegular(data);
     VerifyMedalsEvent(data);
 }
Example #18
0
        public override void Verify(LegalityAnalysis data)
        {
            var pkm = data.pkm;

            switch (data.EncounterMatch)
            {
            case EncounterTrade _:
            case MysteryGift g when !g.IsEgg:
            case EncounterStaticPID s when s.NSparkle:
                return;     // already verified
            }

            var ot = pkm.OT_Name;

            if (ot.Length == 0)
            {
                data.AddLine(GetInvalid(V106));
            }

            if (pkm.TID == 0 && pkm.SID == 0)
            {
                data.AddLine(Get(V33, Severity.Fishy));
            }
            else if (pkm.VC)
            {
                if (pkm.SID != 0)
                {
                    data.AddLine(GetInvalid(V34));
                }
            }
            else if (pkm.TID == pkm.SID)
            {
                data.AddLine(Get(V35, Severity.Fishy));
            }
            else if (pkm.TID == 0)
            {
                data.AddLine(Get(V36, Severity.Fishy));
            }
            else if (pkm.SID == 0)
            {
                data.AddLine(Get(V37, Severity.Fishy));
            }
            else if (pkm.TID == 12345 && pkm.SID == 54321 || SuspiciousOTNames.Any(z => ot.StartsWith(z)))
            {
                data.AddLine(Get(V417, Severity.Fishy));
            }

            if (pkm.VC)
            {
                VerifyOTG1(data);
            }

            if (Legal.CheckWordFilter)
            {
                if (WordFilter.IsFiltered(ot, out string bad))
                {
                    data.AddLine(GetInvalid($"Wordfilter: {bad}"));
                }
                if (WordFilter.IsFiltered(pkm.HT_Name, out bad))
                {
                    data.AddLine(GetInvalid($"Wordfilter: {bad}"));
                }
            }
        }
Example #19
0
 public override void Verify(LegalityAnalysis data)
 {
     throw new Exception("Don't call via this.");
 }
Example #20
0
 public override void Verify(LegalityAnalysis data)
 {
     VerifyOTMemory(data);
     VerifyHTMemory(data);
 }
Example #21
0
        private void VerifySWSHStats(LegalityAnalysis data, PK8 pk8)
        {
            if (pk8.Favorite)
            {
                data.AddLine(GetInvalid(LFavoriteMarkingUnavailable, Encounter));
            }

            var sn = pk8.StatNature;

            if (sn != pk8.Nature)
            {
                // Only allow Serious nature (12); disallow all other neutral natures.
                if (sn != 12 && (sn > 24 || sn % 6 == 0))
                {
                    data.AddLine(GetInvalid(LStatNatureInvalid));
                }
            }

            var bv = pk8.BattleVersion;

            if (bv != 0)
            {
                if ((bv != (int)GameVersion.SW && bv != (int)GameVersion.SH) || pk8.SWSH)
                {
                    data.AddLine(GetInvalid(LStatBattleVersionInvalid));
                }
            }

            var  enc        = data.EncounterMatch;
            bool originGMax = enc is IGigantamax g && g.CanGigantamax;

            if (originGMax != pk8.CanGigantamax)
            {
                bool ok  = !pk8.IsEgg && pk8.CanToggleGigantamax(pk8.Species, pk8.AltForm, enc.Species, enc.Form);
                var  chk = ok ? GetValid(LStatGigantamaxValid) : GetInvalid(LStatGigantamaxInvalid);
                data.AddLine(chk);
            }

            if (pk8.DynamaxLevel != 0)
            {
                if (!pk8.CanHaveDynamaxLevel(pk8) || pk8.DynamaxLevel > 10)
                {
                    data.AddLine(GetInvalid(LStatDynamaxInvalid));
                }
            }

            PersonalInfo?pi = null;

            for (int i = 0; i < 100; i++)
            {
                if (!pk8.GetMoveRecordFlag(i))
                {
                    continue;
                }
                if ((pi ??= pk8.PersonalInfo).TMHM[i + 100])
                {
                    continue;
                }

                // Calyrex-0 can have TR flags for Calyrex-1/2 after it has force unlearned them.
                // Re-fusing can be reacquire the move via relearner, rather than needing another TR.
                // Calyrex-0 cannot reacquire the move via relearner, even though the TR is checked off in the TR list.
                if (pk8.Species == (int)Species.Calyrex)
                {
                    var form = pk8.AltForm;
                    // Check if another alt form can learn the TR
                    if ((form != 1 && CanLearnTR((int)Species.Calyrex, 1, i)) || (form != 2 && CanLearnTR((int)Species.Calyrex, 2, i)))
                    {
                        continue;
                    }
                }

                data.AddLine(GetInvalid(string.Format(LMoveSourceTR, ParseSettings.MoveStrings[Legal.TMHM_SWSH[i + 100]])));
            }

            // weight/height scalars can be legally 0 (1:65536) so don't bother checking
        }
Example #22
0
        private void VerifyOTMemory(LegalityAnalysis data)
        {
            var pkm  = data.pkm;
            var mem  = (ITrainerMemories)pkm;
            var Info = data.Info;

            switch (data.EncounterMatch)
            {
            case WC6 {
                    IsEgg: false
            } g when g.OTGender != 3 :
                VerifyOTMemoryIs(data, g.OT_Memory, g.OT_Intensity, g.OT_TextVar, g.OT_Feeling);
                return;

            case WC7 {
                    IsEgg: false
            } g when g.OTGender != 3 :
                VerifyOTMemoryIs(data, g.OT_Memory, g.OT_Intensity, g.OT_TextVar, g.OT_Feeling);
                return;

            case WC8 {
                    IsEgg: false
            } g when g.OTGender != 3 :
                VerifyOTMemoryIs(data, g.OT_Memory, g.OT_Intensity, g.OT_TextVar, g.OT_Feeling);
                return;

            case IMemoryOT t when t is not MysteryGift:     // Ignore Mystery Gift cases (covered above)
                VerifyOTMemoryIs(data, t.OT_Memory, t.OT_Intensity, t.OT_TextVar, t.OT_Feeling);
                return;
            }

            int memoryGen = Info.Generation;
            int memory    = mem.OT_Memory;

            if (pkm.IsEgg)
            {
                // Traded unhatched eggs in Gen8 have OT link trade memory applied erroneously.
                // They can also have the box-inspect memory!
                if (memoryGen != 8 || !((pkm.Met_Location == Locations.LinkTrade6 && memory == 4) || memory == 85))
                {
                    VerifyOTMemoryIs(data, 0, 0, 0, 0); // empty
                    return;
                }
            }
            else if (!CanHaveMemoryForOT(pkm, memoryGen, memory))
            {
                VerifyOTMemoryIs(data, 0, 0, 0, 0); // empty
                return;
            }

            // Bounds checking
            switch (memoryGen)
            {
            case 6 when pkm.XY && (memory > Memories.MAX_MEMORY_ID_XY || Memories.Memory_NotXY.Contains(memory)):
            case 6 when pkm.AO && (memory > Memories.MAX_MEMORY_ID_AO || Memories.Memory_NotAO.Contains(memory)):
            case 8 when pkm.SWSH && (memory > Memories.MAX_MEMORY_ID_SWSH || Memories.Memory_NotSWSH.Contains(memory)):
                data.AddLine(GetInvalid(string.Format(LMemoryArgBadID, L_XOT)));
                break;
            }

            // Verify memory if specific to OT
            switch (memory)
            {
            // No Memory
            case 0:     // SWSH trades don't set HT memories immediately, which is hilarious.
                data.AddLine(Get(LMemoryMissingOT, memoryGen == 8 ? Severity.Fishy : Severity.Invalid));
                VerifyOTMemoryIs(data, 0, 0, 0, 0);
                return;

            // {0} hatched from an Egg and saw {1} for the first time at... {2}. {4} that {3}.
            case 2 when !Info.EncounterMatch.EggEncounter:
                data.AddLine(GetInvalid(string.Format(LMemoryArgBadHatch, L_XOT)));
                break;

            // {0} became {1}’s friend when it arrived via Link Trade at... {2}. {4} that {3}.
            case 4 when pkm.Gen6:
                data.AddLine(GetInvalid(string.Format(LMemoryArgBadOTEgg, L_XOT)));
                return;

            // {0} went to the Pokémon Center in {2} with {1} and had its tired body healed there. {4} that {3}.
            case 6 when memoryGen == 6 && !Memories.GetHasPokeCenterLocation((GameVersion)pkm.Version, mem.OT_TextVar):
                data.AddLine(GetInvalid(string.Format(LMemoryArgBadLocation, L_XOT)));
                return;

            case 6 when memoryGen == 8 && mem.OT_TextVar != 0:
                data.AddLine(Get(string.Format(LMemoryArgBadLocation, L_XOT), ParseSettings.Gen8MemoryLocationTextVariable));
                return;

            // {0} was with {1} when {1} caught {2}. {4} that {3}.
            case 14:
                var result = GetCanBeCaptured(mem.OT_TextVar, Info.Generation, (GameVersion)pkm.Version)     // Any Game in the Handling Trainer's generation
                        ? GetValid(string.Format(LMemoryArgSpecies, L_XOT))
                        : GetInvalid(string.Format(LMemoryArgBadSpecies, L_XOT));
                data.AddLine(result);
                return;
            }

            data.AddLine(VerifyCommonMemory(pkm, 0, Info.Generation, Info));
        }
Example #23
0
        private static void VerifyTradeOTOnly(LegalityAnalysis data, EncounterTrade t)
        {
            var result = CheckTradeOTOnly(data, t.TrainerNames);

            data.AddLine(result);
        }
Example #24
0
        private void VerifyHTMemory(LegalityAnalysis data)
        {
            var pkm  = data.pkm;
            var mem  = (ITrainerMemories)pkm;
            var Info = data.Info;

            var memory = mem.HT_Memory;

            if (pkm.IsUntraded)
            {
                if (memory == 4 && WasTradedSWSHEgg(pkm))
                {
                    // Untraded link trade eggs in Gen8 have HT link trade memory applied erroneously.
                    // Verify the link trade memory later.
                }
                else
                {
                    VerifyHTMemoryNone(data, mem);
                    return;
                }
            }

            if (pkm.Format == 7)
            {
                VerifyHTMemoryTransferTo7(data, pkm, Info);
                return;
            }

            var memoryGen = pkm.Format >= 8 ? 8 : 6;

            // Bounds checking
            switch (memoryGen)
            {
            case 6 when memory > Memories.MAX_MEMORY_ID_AO:
            case 8 when memory > Memories.MAX_MEMORY_ID_SWSH || Memories.Memory_NotSWSH.Contains(memory):
                data.AddLine(GetInvalid(string.Format(LMemoryArgBadID, L_XHT)));
                break;
            }

            // Verify memory if specific to HT
            switch (memory)
            {
            // No Memory
            case 0:     // SWSH trades don't set HT memories immediately, which is hilarious.
                data.AddLine(Get(LMemoryMissingHT, memoryGen == 8 ? Severity.Fishy : Severity.Invalid));
                VerifyHTMemoryNone(data, mem);
                return;

            // {0} met {1} at... {2}. {1} threw a Poké Ball at it, and they started to travel together. {4} that {3}.
            case 1:
                data.AddLine(GetInvalid(string.Format(LMemoryArgBadCatch, L_XHT)));
                return;

            // {0} hatched from an Egg and saw {1} for the first time at... {2}. {4} that {3}.
            case 2:
                data.AddLine(GetInvalid(string.Format(LMemoryArgBadHatch, L_XHT)));
                return;

            // {0} went to the Pokémon Center in {2} with {1} and had its tired body healed there. {4} that {3}.
            case 6 when memoryGen == 6 && !Memories.GetHasPokeCenterLocation(GameVersion.Gen6, mem.HT_TextVar):
                data.AddLine(GetInvalid(string.Format(LMemoryArgBadLocation, L_XOT)));
                return;

            case 6 when memoryGen == 8 && mem.HT_TextVar != 0:
                data.AddLine(Get(string.Format(LMemoryArgBadLocation, L_XOT), ParseSettings.Gen8MemoryLocationTextVariable));
                return;

            // {0} was with {1} when {1} caught {2}. {4} that {3}.
            case 14:
                var result = GetCanBeCaptured(mem.HT_TextVar, memoryGen, GameVersion.Any)     // Any Game in the Handling Trainer's generation
                        ? GetValid(string.Format(LMemoryArgSpecies, L_XHT))
                        : GetInvalid(string.Format(LMemoryArgBadSpecies, L_XHT));
                data.AddLine(result);
                return;
            }

            var commonResult = VerifyCommonMemory(pkm, 1, memoryGen, Info);

            data.AddLine(commonResult);
        }
Example #25
0
        public override void Verify(LegalityAnalysis data)
        {
            var pkm            = data.pkm;
            var Info           = data.Info;
            var EncounterMatch = data.EncounterMatch;

            int[] abilities = pkm.PersonalInfo.Abilities;
            int   abilval   = Array.IndexOf(abilities, pkm.Ability);

            if (abilval < 0)
            {
                data.AddLine(GetInvalid(V107));
                return;
            }

            bool?AbilityUnchanged = true;

            // 3 states flag: true for unchanged, false for changed, null for uncertain/allowing PID mismatch
            // if true, check encounter ability
            // if true or false, check PID/AbilityNumber
            if (3 <= pkm.Format && pkm.Format <= 5 && abilities[0] != abilities[1]) // 3-5 and have 2 distinct ability now
            {
                AbilityUnchanged = VerifyAbilityPreCapsule(data, abilities, abilval);
            }

            if (EncounterMatch is PCD d)
            {
                if (VerifyAbilityPCD(data, d, AbilityUnchanged, abilities))
                {
                    return;
                }
            }
            else // Check Ability Mismatches
            {
                int?EncounterAbility = (EncounterMatch as EncounterStatic)?.Ability ??
                                       (EncounterMatch as EncounterTrade)?.Ability ??
                                       (EncounterMatch as EncounterLink)?.Ability;

                if (EncounterAbility != null && VerifySetAbility(data, EncounterAbility, AbilityUnchanged, abilities, abilval))
                {
                    return; // result added via VerifySetAbility
                }
            }
            switch (Info.Generation)
            {
            case 5: VerifyAbility5(data, abilities); break;

            case 6: VerifyAbility6(data, abilities); break;

            case 7: VerifyAbility7(data, abilities); break;
            }

            if (3 <= Info.Generation && Info.Generation <= 4 && pkm.AbilityNumber == 4)
            {
                data.AddLine(GetInvalid(V112));
            }
            else if (AbilityUnchanged != null && abilities[pkm.AbilityNumber >> 1] != pkm.Ability)
            {
                data.AddLine(GetInvalid(pkm.Format < 6 ? V113 : V114));
            }
            else
            {
                data.AddLine(GetValid(V115));
            }
        }
Example #26
0
        public override void Verify(LegalityAnalysis data)
        {
            var pkm = data.pkm;

            if (pkm is IAwakened a)
            {
                VerifyAwakenedValues(data, a);
                return;
            }
            var enc = data.EncounterMatch;
            int sum = pkm.EVTotal;

            if (sum > 0 && pkm.IsEgg)
            {
                data.AddLine(GetInvalid(LEffortEgg));
            }

            // In Generations I and II, when a Pokémon is taken out of the Day Care, its experience will lower to the minimum value for its current level.
            int format = pkm.Format;

            if (format < 3) // can abuse daycare for EV training without EXP gain
            {
                return;
            }

            if (sum > 510) // format >= 3
            {
                data.AddLine(GetInvalid(LEffortAbove510));
            }
            var evs = pkm.EVs;

            if (format >= 6 && evs.Any(ev => ev > 252))
            {
                data.AddLine(GetInvalid(LEffortAbove252));
            }

            const int vitaMax = 100;     // Vitamin Max

            if (format < 5)              // 3/4
            {
                if (enc.LevelMin == 100) // only true for Gen4 and Format=4
                {
                    // Cannot EV train at level 100 -- Certain events are distributed at level 100.
                    if (evs.Any(ev => ev > vitaMax)) // EVs can only be increased by vitamins to a max of 100.
                    {
                        data.AddLine(GetInvalid(LEffortCap100));
                    }
                }
                else // check for gained EVs without gaining EXP -- don't check gen5+ which have wings to boost above 100.
                {
                    var growth  = PersonalTable.HGSS[enc.Species].EXPGrowth;
                    var baseEXP = Experience.GetEXP(enc.LevelMin, growth);
                    if (baseEXP == pkm.EXP && evs.Any(ev => ev > vitaMax))
                    {
                        data.AddLine(GetInvalid(string.Format(LEffortUntrainedCap, vitaMax)));
                    }
                }
            }

            // Only one of the following can be true: 0, 508, and x%6!=0
            if (sum == 0 && !enc.IsWithinRange(pkm))
            {
                data.AddLine(Get(LEffortEXPIncreased, Severity.Fishy));
            }
            else if (sum == 508)
            {
                data.AddLine(Get(LEffort2Remaining, Severity.Fishy));
            }
            else if (evs[0] != 0 && evs.All(ev => evs[0] == ev))
            {
                data.AddLine(Get(LEffortAllEqual, Severity.Fishy));
            }
        }
Example #27
0
        private CheckResult VerifyAbility(LegalityAnalysis data)
        {
            var pkm = data.pkm;
            var pi  = data.PersonalInfo;

            // Check ability is possible (within bounds)
            int ability   = pkm.Ability;
            int abilIndex = pi.GetAbilityIndex(ability);

            if (abilIndex < 0)
            {
                return(GetInvalid(LAbilityUnexpected));
            }

            var abilities = pi.Abilities;
            int format    = pkm.Format;

            if (format >= 6)
            {
                var num = pkm.AbilityNumber;
                if (!IsValidAbilityBits(num))
                {
                    return(INVALID);
                }

                // Check AbilityNumber points to ability
                int an = num >> 1;
                if (an >= abilities.Count || abilities[an] != ability)
                {
                    return(INVALID);
                }

                // Check AbilityNumber for transfers without unique abilities
                int gen = data.Info.Generation;
                if (gen is 3 or 4 or 5 && num != 4)
                {
                    // To determine AbilityNumber [PK5->PK6], check if the first ability in Personal matches the ability.
                    // It is not possible to flip it to the other index as capsule requires unique abilities.
                    if (abilities[0] == abilities[1] && num != 1)
                    {
                        // Check if any pre-evolution could have it flipped.
                        var evos = data.Info.EvoChainsAllGens[6];
                        var pt   = GameData.GetPersonal(GameUtil.GetVersion(format));
                        if (!GetWasDual(evos, pt, pkm))
                        {
                            return(INVALID);
                        }
                    }
                }
            }

            if (format >= 8) // Ability Patch
            {
                if (pkm.AbilityNumber == 4 && !pkm.LA)
                {
                    if (CanAbilityPatch(format, abilities, pkm.Species))
                    {
                        return(GetValid(LAbilityPatchUsed));
                    }

                    var e = data.EncounterOriginal;

                    if (e.Species != pkm.Species && CanAbilityPatch(format, PKX.Personal.GetFormEntry(e.Species, e.Form).Abilities, e.Species))
                    {
                        return(GetValid(LAbilityPatchUsed));
                    }

                    // Verify later, it may be encountered with its hidden ability without using an ability patch.
                }
            }

            var enc = data.EncounterMatch;

            if (enc is MysteryGift {
                Generation : >= 4
            } g)
            {
                return(VerifyAbilityMG(data, g, abilities));
            }

            if (format < 6)
            {
                return(VerifyAbility345(data, enc, abilities, abilIndex));
            }

            return(VerifyAbility(data, abilities, abilIndex));
        }
Example #28
0
        private void VerifySWSHStats(LegalityAnalysis data, PK8 pk8)
        {
            if (pk8.Favorite)
            {
                data.AddLine(GetInvalid(LFavoriteMarkingUnavailable, Encounter));
            }

            var sn = pk8.StatNature;

            if (sn != pk8.Nature)
            {
                // Only allow Serious nature (12); disallow all other neutral natures.
                if (sn != 12 && (sn > 24 || sn % 6 == 0))
                {
                    data.AddLine(GetInvalid(LStatNatureInvalid));
                }
            }

            var bv = pk8.BattleVersion;

            if (bv != 0)
            {
                if ((bv != (int)GameVersion.SW && bv != (int)GameVersion.SH) || pk8.SWSH)
                {
                    data.AddLine(GetInvalid(LStatBattleVersionInvalid));
                }
            }

            var  enc        = data.EncounterMatch;
            bool originGMax = enc is IGigantamax g && g.CanGigantamax;

            if (originGMax != pk8.CanGigantamax)
            {
                bool ok  = pk8.CanToggleGigantamax(pk8.Species, pk8.AltForm, enc.Species, enc.Form);
                var  chk = ok ? GetValid(LStatGigantamaxValid) : GetInvalid(LStatGigantamaxInvalid);
                data.AddLine(chk);
            }

            if (pk8.DynamaxLevel != 0)
            {
                if (!pk8.CanHaveDynamaxLevel(pk8) || pk8.DynamaxLevel > 10)
                {
                    data.AddLine(GetInvalid(LStatDynamaxInvalid));
                }
            }

            PersonalInfo?pi = null;

            for (int i = 0; i < 100; i++)
            {
                if (!pk8.GetMoveRecordFlag(i))
                {
                    continue;
                }
                if (!(pi ??= pk8.PersonalInfo).TMHM[i + 100])
                {
                    data.AddLine(GetInvalid(string.Format(LMoveSourceTR, LegalityAnalysis.MoveStrings[Legal.TMHM_SWSH[i + 100]])));
                }
            }

            // weight/height scalars can be legally 0 (1:65536) so don't bother checking
        }
        private CheckResult VerifyBallEggGen7(LegalityAnalysis data)
        {
            var pkm     = data.pkm;
            int species = data.EncounterMatch.Species;

            if (722 <= species && species <= 730) // G7 Starters
            {
                return(VerifyBallEquals(data, (int)Poke));
            }

            Ball ball = (Ball)pkm.Ball;

            if (ball == Safari)
            {
                if (!(Legal.Inherit_Safari.Contains(species) || Legal.Inherit_SafariMale.Contains(species)))
                {
                    return(GetInvalid(LBallSpecies));
                }
                if (pkm.AbilityNumber == 4 && Legal.Ban_SafariBallHidden_7.Contains(species))
                {
                    return(GetInvalid(LBallAbility));
                }
                return(GetValid(LBallSpeciesPass));
            }
            if (ball.IsApricornBall()) // Apricorn Ball
            {
                if (!Legal.Inherit_Apricorn7.Contains(species))
                {
                    return(GetInvalid(LBallSpecies));
                }
                if (pkm.AbilityNumber == 4 && Legal.Ban_NoHidden7Apricorn.Contains(species | pkm.AltForm << 11)) // lineage is 3->2->origin
                {
                    return(GetInvalid(LBallAbility));
                }
                return(GetValid(LBallSpeciesPass));
            }
            if (ball == Sport) // Sport Ball
            {
                if (!Legal.Inherit_Sport.Contains(species))
                {
                    return(GetInvalid(LBallSpecies));
                }
                if (pkm.AbilityNumber == 4 && (species == 313 || species == 314)) // Volbeat/Illumise
                {
                    return(GetInvalid(LBallAbility));
                }
                return(GetValid(LBallSpeciesPass));
            }
            if (ball == Dream) // Dream Ball
            {
                if (Legal.Inherit_Dream.Contains(species) || Legal.Inherit_DreamMale.Contains(species))
                {
                    return(GetValid(LBallSpeciesPass));
                }
                return(GetInvalid(LBallSpecies));
            }
            if (Dusk <= ball && ball <= Quick) // Dusk Heal Quick
            {
                if (!Legal.Ban_Gen4Ball_7.Contains(species))
                {
                    return(GetValid(LBallSpeciesPass));
                }
                return(GetInvalid(LBallSpecies));
            }
            if (Ultra <= ball && ball <= Premier) // Don't worry, Safari was already checked.
            {
                if (!Legal.Ban_Gen3Ball_7.Contains(species))
                {
                    return(GetValid(LBallSpeciesPass));
                }
                return(GetInvalid(LBallSpecies));
            }

            if (ball == Beast)
            {
                if (species == 669 && pkm.AltForm == 3 && pkm.AbilityNumber == 4)
                {
                    return(GetInvalid(LBallAbility)); // Can't obtain Flabébé-Blue with Hidden Ability in wild
                }
                if ((species > 731 && species <= 785) || (Legal.AlolanCaptureOffspring.Contains(species) && !Legal.PastGenAlolanNativesUncapturable.Contains(species)))
                {
                    return(GetValid(LBallSpeciesPass));
                }
                if (Legal.PastGenAlolanScans.Contains(species))
                {
                    return(GetValid(LBallSpeciesPass));
                }
                // next statement catches all new alolans
            }

            if (species > 721)
            {
                return(VerifyBallEquals(data, Legal.WildPokeballs7));
            }

            if (ball > Beast)
            {
                return(GetInvalid(LBallUnavailable));
            }

            return(NONE);
        }
Example #30
0
 public override void Verify(LegalityAnalysis data)
 {
     throw new NotImplementedException();
 }
 private CheckResult VerifyBallEquals(LegalityAnalysis data, ICollection <int> balls) => GetResult(balls.Contains(data.pkm.Ball));
Example #32
0
        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 ? 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));
                    }
                }
            }