@pkmn/sim 0.7.45 → 0.7.47

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/build/cjs/config/formats.js +176 -116
  2. package/build/cjs/config/formats.js.map +1 -1
  3. package/build/cjs/data/abilities.js +5 -5
  4. package/build/cjs/data/abilities.js.map +1 -1
  5. package/build/cjs/data/aliases.js +3 -2
  6. package/build/cjs/data/aliases.js.map +1 -1
  7. package/build/cjs/data/formats-data.js +161 -163
  8. package/build/cjs/data/formats-data.js.map +1 -1
  9. package/build/cjs/data/items.js +35 -14
  10. package/build/cjs/data/items.js.map +1 -1
  11. package/build/cjs/data/mods/gen2/scripts.js +3 -3
  12. package/build/cjs/data/mods/gen2/scripts.js.map +1 -1
  13. package/build/cjs/data/mods/gen3/formats-data.js +2 -2
  14. package/build/cjs/data/mods/gen3/formats-data.js.map +1 -1
  15. package/build/cjs/data/mods/gen3/scripts.js +1 -1
  16. package/build/cjs/data/mods/gen3/scripts.js.map +1 -1
  17. package/build/cjs/data/mods/gen4/moves.js +1 -1
  18. package/build/cjs/data/mods/gen4/moves.js.map +1 -1
  19. package/build/cjs/data/mods/gen5/formats-data.js +1 -1
  20. package/build/cjs/data/mods/gen5/formats-data.js.map +1 -1
  21. package/build/cjs/data/mods/gen5/moves.js +1 -1
  22. package/build/cjs/data/mods/gen5/moves.js.map +1 -1
  23. package/build/cjs/data/moves.js +13 -18
  24. package/build/cjs/data/moves.js.map +1 -1
  25. package/build/cjs/data/pokedex.js +9 -9
  26. package/build/cjs/data/pokedex.js.map +1 -1
  27. package/build/cjs/data/pokemongo.js +987 -0
  28. package/build/cjs/data/pokemongo.js.map +1 -0
  29. package/build/cjs/data/rulesets.js +51 -0
  30. package/build/cjs/data/rulesets.js.map +1 -1
  31. package/build/cjs/sim/battle-actions.js +8 -7
  32. package/build/cjs/sim/battle-actions.js.map +1 -1
  33. package/build/cjs/sim/dex-species.js +4 -0
  34. package/build/cjs/sim/dex-species.js.map +1 -1
  35. package/build/cjs/sim/dex.js +3 -2
  36. package/build/cjs/sim/dex.js.map +1 -1
  37. package/build/cjs/sim/pokemon.js +7 -1
  38. package/build/cjs/sim/pokemon.js.map +1 -1
  39. package/build/cjs/sim/team-validator.js +299 -54
  40. package/build/cjs/sim/team-validator.js.map +1 -1
  41. package/build/esm/config/formats.mjs +176 -116
  42. package/build/esm/config/formats.mjs.map +1 -1
  43. package/build/esm/data/abilities.mjs +5 -5
  44. package/build/esm/data/abilities.mjs.map +1 -1
  45. package/build/esm/data/aliases.mjs +3 -2
  46. package/build/esm/data/aliases.mjs.map +1 -1
  47. package/build/esm/data/formats-data.mjs +161 -163
  48. package/build/esm/data/formats-data.mjs.map +1 -1
  49. package/build/esm/data/items.mjs +35 -14
  50. package/build/esm/data/items.mjs.map +1 -1
  51. package/build/esm/data/mods/gen2/scripts.mjs +3 -3
  52. package/build/esm/data/mods/gen2/scripts.mjs.map +1 -1
  53. package/build/esm/data/mods/gen3/formats-data.mjs +2 -2
  54. package/build/esm/data/mods/gen3/formats-data.mjs.map +1 -1
  55. package/build/esm/data/mods/gen3/scripts.mjs +1 -1
  56. package/build/esm/data/mods/gen3/scripts.mjs.map +1 -1
  57. package/build/esm/data/mods/gen4/moves.mjs +1 -1
  58. package/build/esm/data/mods/gen4/moves.mjs.map +1 -1
  59. package/build/esm/data/mods/gen5/formats-data.mjs +1 -1
  60. package/build/esm/data/mods/gen5/formats-data.mjs.map +1 -1
  61. package/build/esm/data/mods/gen5/moves.mjs +1 -1
  62. package/build/esm/data/mods/gen5/moves.mjs.map +1 -1
  63. package/build/esm/data/moves.mjs +13 -18
  64. package/build/esm/data/moves.mjs.map +1 -1
  65. package/build/esm/data/pokedex.mjs +9 -9
  66. package/build/esm/data/pokedex.mjs.map +1 -1
  67. package/build/esm/data/pokemongo.mjs +984 -0
  68. package/build/esm/data/pokemongo.mjs.map +1 -0
  69. package/build/esm/data/rulesets.mjs +51 -0
  70. package/build/esm/data/rulesets.mjs.map +1 -1
  71. package/build/esm/sim/battle-actions.mjs +8 -7
  72. package/build/esm/sim/battle-actions.mjs.map +1 -1
  73. package/build/esm/sim/dex-species.mjs +4 -0
  74. package/build/esm/sim/dex-species.mjs.map +1 -1
  75. package/build/esm/sim/dex.mjs +3 -2
  76. package/build/esm/sim/dex.mjs.map +1 -1
  77. package/build/esm/sim/pokemon.mjs +7 -1
  78. package/build/esm/sim/pokemon.mjs.map +1 -1
  79. package/build/esm/sim/team-validator.mjs +299 -54
  80. package/build/esm/sim/team-validator.mjs.map +1 -1
  81. package/build/types/data/pokemongo.d.ts +31 -0
  82. package/build/types/sim/battle-actions.d.ts +1 -1
  83. package/build/types/sim/dex-species.d.ts +12 -0
  84. package/build/types/sim/dex.d.ts +3 -2
  85. package/build/types/sim/exported-global-types.d.ts +2 -1
  86. package/build/types/sim/global-types.d.ts +2 -1
  87. package/build/types/sim/pokemon.d.ts +2 -0
  88. package/build/types/sim/team-validator.d.ts +8 -1
  89. package/package.json +1 -1
@@ -575,9 +575,6 @@ class TeamValidator {
575
575
  problems.push(`${name} has ${set.moves.length} moves, which is more than the limit of ${ruleTable.maxMoveCount}.`);
576
576
  return problems;
577
577
  }
578
- if (ruleTable.isBanned('nonexistent')) {
579
- problems.push(...this.validateStats(set, species, setSources));
580
- }
581
578
  const moveLegalityWhitelist = {};
582
579
  for (const moveName of set.moves) {
583
580
  if (!moveName)
@@ -601,10 +598,56 @@ class TeamValidator {
601
598
  }
602
599
  }
603
600
  }
601
+ const pokemonGoProblems = this.validatePokemonGo(outOfBattleSpecies, set, setSources);
602
+ const learnsetSpecies = dex.species.getLearnsetData(outOfBattleSpecies.id);
603
+ let isFromRBYEncounter = false;
604
+ if (this.gen === 1 && ruleTable.has('obtainablemisc') && !this.ruleTable.has('allowtradeback')) {
605
+ let lowestEncounterLevel;
606
+ for (const encounter of learnsetSpecies.encounters || []) {
607
+ if (encounter.generation !== 1)
608
+ continue;
609
+ if (!encounter.level)
610
+ continue;
611
+ if (lowestEncounterLevel && encounter.level > lowestEncounterLevel)
612
+ continue;
613
+ lowestEncounterLevel = encounter.level;
614
+ }
615
+ if (lowestEncounterLevel) {
616
+ if (set.level < lowestEncounterLevel) {
617
+ problems.push(`${name} is not obtainable at levels below ${lowestEncounterLevel} in Gen 1.`);
618
+ }
619
+ isFromRBYEncounter = true;
620
+ }
621
+ }
622
+ if (!isFromRBYEncounter && ruleTable.has('obtainablemisc')) {
623
+ // FIXME: Event pokemon given at a level under what it normally can be attained at gives a false positive
624
+ let evoSpecies = species;
625
+ while (evoSpecies.prevo) {
626
+ if (set.level < (evoSpecies.evoLevel || 0)) {
627
+ if (!pokemonGoProblems || (pokemonGoProblems && pokemonGoProblems.length)) {
628
+ problems.push(`${name} must be at least level ${evoSpecies.evoLevel} to be evolved.`);
629
+ if (pokemonGoProblems && pokemonGoProblems.length) {
630
+ problems.push(`It failed to validate as a Pokemon from Pokemon GO because:`);
631
+ for (const pokemonGoProblem of pokemonGoProblems) {
632
+ problems.push(pokemonGoProblem);
633
+ }
634
+ }
635
+ }
636
+ else {
637
+ // Pokemon from Pokemon GO can be transferred to LGPE
638
+ setSources.isFromPokemonGo = true;
639
+ setSources.sources.push('8V');
640
+ setSources.sourcesBefore = 0;
641
+ }
642
+ break;
643
+ }
644
+ evoSpecies = dex.species.get(evoSpecies.prevo);
645
+ }
646
+ }
647
+ const moveProblems = this.validateMoves(outOfBattleSpecies, set.moves, setSources, set, name, moveLegalityWhitelist);
604
648
  if (ruleTable.has('obtainablemoves')) {
605
- problems.push(...this.validateMoves(outOfBattleSpecies, set.moves, setSources, set, name, moveLegalityWhitelist));
649
+ problems.push(...moveProblems);
606
650
  }
607
- const learnsetSpecies = dex.species.getLearnsetData(outOfBattleSpecies.id);
608
651
  let eventOnlyData;
609
652
  if (!setSources.sourcesBefore && setSources.sources.length) {
610
653
  let legal = false;
@@ -628,12 +671,24 @@ class TeamValidator {
628
671
  problems.push(`(Is this incorrect? If so, post the chainbreeding instructions in Bug Reports)`);
629
672
  }
630
673
  else {
631
- if (setSources.sources.length > 1) {
632
- problems.push(`${name} has an event-exclusive move that it doesn't qualify for (only one of several ways to get the move will be listed):`);
674
+ if (species.id === 'mew' && pokemonGoProblems && !pokemonGoProblems.length) {
675
+ // Whitelist Pokemon GO Mew, which cannot be sent to Let's Go
676
+ setSources.isFromPokemonGo = true;
677
+ }
678
+ else {
679
+ if (setSources.sources.length > 1) {
680
+ problems.push(`${name} has an event-exclusive move that it doesn't qualify for (only one of several ways to get the move will be listed):`);
681
+ }
682
+ const eventProblems = this.validateSource(set, nonEggSource, setSources, outOfBattleSpecies, ` because it has a move only available`);
683
+ if (eventProblems)
684
+ problems.push(...eventProblems);
685
+ if (species.id === 'mew' && pokemonGoProblems && pokemonGoProblems.length) {
686
+ problems.push(`Additionally, it failed to validate as a Pokemon from Pokemon GO because:`);
687
+ for (const pokemonGoProblem of pokemonGoProblems) {
688
+ problems.push(pokemonGoProblem);
689
+ }
690
+ }
633
691
  }
634
- const eventProblems = this.validateSource(set, nonEggSource, setSources, outOfBattleSpecies, ` because it has a move only available`);
635
- if (eventProblems)
636
- problems.push(...eventProblems);
637
692
  }
638
693
  }
639
694
  }
@@ -650,54 +705,58 @@ class TeamValidator {
650
705
  legal = true;
651
706
  }
652
707
  if (!legal) {
653
- if (eventData.length === 1) {
654
- problems.push(`${species.name} is only obtainable from an event - it needs to match its event:`);
708
+ if (!pokemonGoProblems || (pokemonGoProblems && pokemonGoProblems.length)) {
709
+ if (eventData.length === 1) {
710
+ problems.push(`${species.name} is only obtainable from an event - it needs to match its event:`);
711
+ }
712
+ else {
713
+ problems.push(`${species.name} is only obtainable from events - it needs to match one of its events:`);
714
+ }
715
+ for (const [i, event] of eventData.entries()) {
716
+ if (event.generation <= dex.gen && event.generation >= this.minSourceGen) {
717
+ const eventInfo = event;
718
+ const eventNum = i + 1;
719
+ const eventName = eventData.length > 1 ? ` #${eventNum}` : ``;
720
+ const eventProblems = this.validateEvent(set, setSources, eventInfo, eventSpecies, ` to be`, `from its event${eventName}`);
721
+ if (eventProblems)
722
+ problems.push(...eventProblems);
723
+ }
724
+ }
725
+ if (pokemonGoProblems && pokemonGoProblems.length) {
726
+ problems.push(`Additionally, it failed to validate as a Pokemon from Pokemon GO because:`);
727
+ for (const pokemonGoProblem of pokemonGoProblems) {
728
+ problems.push(pokemonGoProblem);
729
+ }
730
+ }
655
731
  }
656
732
  else {
657
- problems.push(`${species.name} is only obtainable from events - it needs to match one of its events:`);
658
- }
659
- for (const [i, event] of eventData.entries()) {
660
- if (event.generation <= dex.gen && event.generation >= this.minSourceGen) {
661
- const eventInfo = event;
662
- const eventNum = i + 1;
663
- const eventName = eventData.length > 1 ? ` #${eventNum}` : ``;
664
- const eventProblems = this.validateEvent(set, setSources, eventInfo, eventSpecies, ` to be`, `from its event${eventName}`);
665
- if (eventProblems)
666
- problems.push(...eventProblems);
667
- }
733
+ setSources.isFromPokemonGo = true;
668
734
  }
669
735
  }
670
736
  }
671
- let isFromRBYEncounter = false;
672
- if (this.gen === 1 && ruleTable.has('obtainablemisc') && !this.ruleTable.has('allowtradeback')) {
673
- let lowestEncounterLevel;
674
- for (const encounter of learnsetSpecies.encounters || []) {
675
- if (encounter.generation !== 1)
676
- continue;
677
- if (!encounter.level)
678
- continue;
679
- if (lowestEncounterLevel && encounter.level > lowestEncounterLevel)
680
- continue;
681
- lowestEncounterLevel = encounter.level;
682
- }
683
- if (lowestEncounterLevel) {
684
- if (set.level < lowestEncounterLevel) {
685
- problems.push(`${name} is not obtainable at levels below ${lowestEncounterLevel} in Gen 1.`);
686
- }
687
- isFromRBYEncounter = true;
737
+ // Attempt move validation again after verifying Pokemon GO origin
738
+ if (setSources.isFromPokemonGo) {
739
+ setSources.restrictiveMoves = [];
740
+ setSources.sources = ['8V'];
741
+ setSources.sourcesBefore = 0;
742
+ if (!moveProblems.length && ruleTable.has('obtainablemoves')) {
743
+ problems.push(...this.validateMoves(outOfBattleSpecies, set.moves, setSources, set, name, moveLegalityWhitelist));
688
744
  }
689
745
  }
690
- if (!isFromRBYEncounter && ruleTable.has('obtainablemisc')) {
691
- // FIXME: Event pokemon given at a level under what it normally can be attained at gives a false positive
692
- let evoSpecies = species;
693
- while (evoSpecies.prevo) {
694
- if (set.level < (evoSpecies.evoLevel || 0)) {
695
- problems.push(`${name} must be at least level ${evoSpecies.evoLevel} to be evolved.`);
696
- break;
746
+ // Hardcoded forced validation for Pokemon GO
747
+ const pokemonGoOnlySpecies = ['meltan', 'melmetal', 'gimmighoulroaming'];
748
+ if (ruleTable.has('obtainablemisc') && (pokemonGoOnlySpecies.includes(species.id))) {
749
+ setSources.isFromPokemonGo = true;
750
+ if (pokemonGoProblems && pokemonGoProblems.length) {
751
+ problems.push(`${name} is only obtainable from Pokemon GO, and failed to validate because:`);
752
+ for (const pokemonGoProblem of pokemonGoProblems) {
753
+ problems.push(pokemonGoProblem);
697
754
  }
698
- evoSpecies = dex.species.get(evoSpecies.prevo);
699
755
  }
700
756
  }
757
+ if (ruleTable.isBanned('nonexistent')) {
758
+ problems.push(...this.validateStats(set, species, setSources, pokemonGoProblems));
759
+ }
701
760
  if (ruleTable.has('obtainablemoves')) {
702
761
  if (species.id === 'keldeo' && set.moves.includes('secretsword') && this.minSourceGen > 5 && dex.gen <= 7) {
703
762
  problems.push(`${name} has Secret Sword, which is only compatible with Keldeo-Ordinary obtained from Gen 5.`);
@@ -776,7 +835,7 @@ class TeamValidator {
776
835
  }
777
836
  return problems;
778
837
  }
779
- validateStats(set, species, setSources) {
838
+ validateStats(set, species, setSources, pokemonGoProblems) {
780
839
  const ruleTable = this.ruleTable;
781
840
  const dex = this.dex;
782
841
  const allowAVs = ruleTable.has('allowavs');
@@ -835,8 +894,19 @@ class TeamValidator {
835
894
  perfectIVs++;
836
895
  }
837
896
  if (perfectIVs < 3) {
838
- const reason = (this.minSourceGen === 6 ? ` and this format requires Gen ${dex.gen} Pokémon` : ` in Gen 6 or later`);
839
- problems.push(`${name} must have at least three perfect IVs because it's a legendary${reason}.`);
897
+ if (!pokemonGoProblems || (pokemonGoProblems && pokemonGoProblems.length)) {
898
+ const reason = (this.minSourceGen === 6 ? ` and this format requires Gen ${dex.gen} Pokémon` : ` in Gen 6 or later`);
899
+ problems.push(`${name} must have at least three perfect IVs because it's a legendary${reason}.`);
900
+ if (pokemonGoProblems && pokemonGoProblems.length) {
901
+ problems.push(`Additionally, it failed to validate as a Pokemon from Pokemon GO because:`);
902
+ for (const pokemonGoProblem of pokemonGoProblems) {
903
+ problems.push(pokemonGoProblem);
904
+ }
905
+ }
906
+ }
907
+ else {
908
+ setSources.isFromPokemonGo = true;
909
+ }
840
910
  }
841
911
  }
842
912
  if (set.hpType && !canBottleCap) {
@@ -850,6 +920,24 @@ class TeamValidator {
850
920
  problems.push(`${name} has Hidden Power ${set.hpType}, but its IVs don't allow this even with (Bottle Cap) Hyper Training.`);
851
921
  }
852
922
  }
923
+ if (setSources.isFromPokemonGo) {
924
+ // Pokemon from Pokemon GO must have odd IVs in non-Spe stats
925
+ // Since the set can be fixed while making minimal changes, it does not force the IVs to be manually fixed
926
+ for (const stat in set.ivs) {
927
+ if (set.ivs[stat] % 2 === 0 && stat !== 'spe') {
928
+ set.ivs[stat]++;
929
+ }
930
+ }
931
+ if (set.ivs.atk !== set.ivs.spa && !(canBottleCap && (set.ivs.atk === 31 || set.ivs.spa === 31))) {
932
+ problems.push(`${name}'s Atk and Spa IVs must match because it is from Pokemon GO.`);
933
+ }
934
+ if (set.ivs.def !== set.ivs.spd && !(canBottleCap && (set.ivs.def === 31 || set.ivs.spd === 31))) {
935
+ problems.push(`${name}'s Def and Spd IVs must match because it is from Pokemon GO.`);
936
+ }
937
+ if (set.hpType && set.hpType !== 'Dark' && set.hpType !== 'Ice') {
938
+ problems.push(`${name} must have Hidden Power Dark or Ice because it is from Pokemon GO.`);
939
+ }
940
+ }
853
941
  if (dex.gen <= 2) {
854
942
  // validate DVs
855
943
  const ivs = set.ivs;
@@ -1875,6 +1963,114 @@ class TeamValidator {
1875
1963
  }
1876
1964
  return problems;
1877
1965
  }
1966
+ /**
1967
+ * Returns a list of problems regarding a Pokemon's avilability in Pokemon GO (empty list if no problems)
1968
+ * If the Pokemon cannot be obtained from Pokemon GO, returns null
1969
+ */
1970
+ validatePokemonGo(species, set, setSources, name = species.name) {
1971
+ let problems = [];
1972
+ let minLevel = 50; // maximum level a Pokemon can be in Pokemon GO
1973
+ let minIVs = 15; // IVs range from 0 to 15 in Pokemon GO
1974
+ const dex = this.dex;
1975
+ const pokemonGoData = dex.species.getPokemonGoData(species.id);
1976
+ if (dex.gen < 8 || this.format.mod === 'gen8dlc1')
1977
+ return null;
1978
+ if (!pokemonGoData) {
1979
+ // Handles forms and evolutions not obtainable from Pokemon GO
1980
+ const otherSpecies = this.learnsetParent(species);
1981
+ // If a Pokemon is somehow not obtainable from Pokemon GO and it must be leveled up to be evolved,
1982
+ // validation for the game should stop because it's more optimal to get the Pokemon outside of the game
1983
+ if (otherSpecies && !species.evoLevel) {
1984
+ const otherProblems = this.validatePokemonGo(otherSpecies, set, setSources, name);
1985
+ if (otherProblems) {
1986
+ problems = otherProblems;
1987
+ }
1988
+ else {
1989
+ return null;
1990
+ }
1991
+ }
1992
+ else {
1993
+ return null;
1994
+ }
1995
+ }
1996
+ else {
1997
+ const pokemonGoSources = pokemonGoData.encounters;
1998
+ // should never happen
1999
+ if (!pokemonGoSources)
2000
+ throw new Error(`Species with no Pokemon GO data: ${species.id}`);
2001
+ if (set.shiny)
2002
+ name = "Shiny " + name;
2003
+ if (set.shiny && pokemonGoSources.includes('noshiny')) {
2004
+ problems.push(`${name} is not obtainable from Pokemon GO.`);
2005
+ }
2006
+ else {
2007
+ if (pokemonGoSources.includes('wild') && !((set.shiny && pokemonGoSources.includes('nowildshiny')))) {
2008
+ minLevel = 1;
2009
+ minIVs = 0;
2010
+ }
2011
+ if (pokemonGoSources.includes('egg')) {
2012
+ /**
2013
+ * A Pokemon's level when hatched is determined by the trainer's level when it is obtained
2014
+ * It is no longer possible for new accounts to obtain eggs at level 1 because they will have reached
2015
+ * level 2 by the time they can spin a PokeStop. However, it might be possible for a sleeper account
2016
+ * from before XP changes to get a level 1 egg from spinning a PokeStop that sends the account to
2017
+ * level 2, but this needs research
2018
+ */
2019
+ minLevel = Math.min(minLevel, 2);
2020
+ minIVs = Math.min(minIVs, 10);
2021
+ }
2022
+ if (pokemonGoSources.includes('12kmegg')) {
2023
+ minLevel = Math.min(minLevel, 8);
2024
+ minIVs = Math.min(minIVs, 10);
2025
+ }
2026
+ if (pokemonGoSources.includes('raid')) {
2027
+ minLevel = Math.min(minLevel, 20);
2028
+ minIVs = Math.min(minIVs, 10);
2029
+ }
2030
+ if (species.id === 'mewtwo' && set.level && set.level >= 20) {
2031
+ // A bug allowed Mewtwo to be encountered with an IV floor of 0 from GO Battle League
2032
+ minIVs = Math.min(minIVs, 0);
2033
+ }
2034
+ if (pokemonGoSources.includes('research')) {
2035
+ minLevel = Math.min(minLevel, 15);
2036
+ minIVs = Math.min(minIVs, 10);
2037
+ }
2038
+ if (pokemonGoSources.includes('giovanni') && !set.shiny) {
2039
+ /**
2040
+ * Purified Pokemon can be leveled down to level 8 after trading; they are forced to
2041
+ * special trades, but currently all Giovanni Shadow Pokemon are already forced special trades
2042
+ */
2043
+ minLevel = Math.min(minLevel, 8);
2044
+ minIVs = Math.min(minIVs, 1);
2045
+ if (set.level && set.level < 12)
2046
+ setSources.pokemonGoSource = "purified";
2047
+ }
2048
+ // Attempt to trade the Pokemon to reduce level and IVs
2049
+ if (!pokemonGoSources.includes('notrade')) {
2050
+ // Special trades require a good friend
2051
+ // Trading with a friend of this level has an IV floor of 1
2052
+ // Note that (non-shiny) Deoxys could be traded for a short time when it was introduced
2053
+ if (!set.shiny || species.id !== 'deoxys') {
2054
+ const specialTrade = pokemonGoSources.includes('specialtrade') || set.shiny;
2055
+ minLevel = Math.min(minLevel, 12);
2056
+ minIVs = Math.min(minIVs, specialTrade ? 1 : 0);
2057
+ }
2058
+ }
2059
+ if (set.level && set.level < minLevel) {
2060
+ problems.push(`${name} must be at least level ${minLevel} to be from Pokemon GO.`);
2061
+ }
2062
+ const ivs = set.ivs || TeamValidator.fillStats(null, 31);
2063
+ for (const stat in ivs) {
2064
+ if (Math.floor(ivs[stat] / 2) < minIVs && stat !== 'spe') {
2065
+ problems.push(`${name} must have at least ${minIVs} ` +
2066
+ (minIVs === 1 ? `IV` : `IVs`) + ` in non-Speed stats to be from Pokemon GO.`);
2067
+ break;
2068
+ }
2069
+ }
2070
+ }
2071
+ }
2072
+ return problems;
2073
+ }
1878
2074
  omCheckCanLearn(move, s, setSources = this.allSources(s), set = {}, problem = `${set.name || s.name} can't learn ${move.name}`) {
1879
2075
  if (!this.ruleTable.checkCanLearn?.[0])
1880
2076
  return problem;
@@ -2042,7 +2238,8 @@ class TeamValidator {
2042
2238
  else if (level >= 5 && learnedGen === 3 && species.canHatch) {
2043
2239
  // Pomeg Glitch
2044
2240
  }
2045
- else if ((!species.gender || species.gender === 'F') && learnedGen >= 2 && species.canHatch) {
2241
+ else if ((!species.gender || species.gender === 'F') &&
2242
+ learnedGen >= 2 && species.canHatch && !setSources.isFromPokemonGo) {
2046
2243
  // available as egg move
2047
2244
  learned = learnedGen + 'Eany';
2048
2245
  // falls through to E check below
@@ -2058,8 +2255,15 @@ class TeamValidator {
2058
2255
  if (learnedGen === dex.gen && learned.charAt(1) !== 'R') {
2059
2256
  // current-gen level-up, TM or tutor moves:
2060
2257
  // always available
2061
- if (!(learnedGen >= 8 && learned.charAt(1) === 'E') && babyOnly)
2062
- setSources.babyOnly = babyOnly;
2258
+ if (!(learnedGen >= 8 && learned.charAt(1) === 'E') && babyOnly) {
2259
+ if (setSources.isFromPokemonGo && species.evoLevel) {
2260
+ cantLearnReason = `is from a prevo, which is incompatible with its Pokemon GO origin.`;
2261
+ continue;
2262
+ }
2263
+ else {
2264
+ setSources.babyOnly = babyOnly;
2265
+ }
2266
+ }
2063
2267
  if (!moveSources.moveEvoCarryCount)
2064
2268
  return null;
2065
2269
  }
@@ -2108,6 +2312,10 @@ class TeamValidator {
2108
2312
  else if (learned.charAt(1) === 'V' && this.minSourceGen < learnedGen) {
2109
2313
  // Virtual Console or Let's Go transfer moves:
2110
2314
  // only if that was the source
2315
+ if (learned === '8V' && setSources.isFromPokemonGo && babyOnly && species.evoLevel) {
2316
+ cantLearnReason = `is from a prevo, which is incompatible with its Pokemon GO origin.`;
2317
+ continue;
2318
+ }
2111
2319
  moveSources.add(learned);
2112
2320
  }
2113
2321
  }
@@ -2157,6 +2365,41 @@ class TeamValidator {
2157
2365
  setSources.restrictiveMoves = [];
2158
2366
  }
2159
2367
  setSources.restrictiveMoves.push(move.name);
2368
+ const checkedSpecies = babyOnly ? species : baseSpecies;
2369
+ if (checkedSpecies && setSources.isFromPokemonGo &&
2370
+ (setSources.pokemonGoSource === 'purified' || checkedSpecies.id === 'mew')) {
2371
+ // Pokemon that cannot be sent from Pokemon GO to Let's Go can only access Let's Go moves through HOME
2372
+ // It can only obtain a chain of four level up moves and cannot have TM moves
2373
+ const pokemonGoData = dex.species.getPokemonGoData(checkedSpecies.id);
2374
+ if (pokemonGoData.LGPERestrictiveMoves) {
2375
+ let levelUpMoveCount = 0;
2376
+ const restrictiveMovesToID = [];
2377
+ for (const moveName of setSources.restrictiveMoves) {
2378
+ restrictiveMovesToID.push((0, dex_1.toID)(moveName));
2379
+ }
2380
+ for (const restrictiveMove in pokemonGoData.LGPERestrictiveMoves) {
2381
+ const moveLevel = pokemonGoData.LGPERestrictiveMoves[restrictiveMove];
2382
+ if ((0, dex_1.toID)(move) === restrictiveMove) {
2383
+ if (!moveLevel) {
2384
+ return `'s move ${move.name} is incompatible with its Pokemon GO origin.`;
2385
+ }
2386
+ else if (set.level && set.level < moveLevel) {
2387
+ return ` must be at least level ${moveLevel} to learn ${move.name} due to its Pokemon GO origin.`;
2388
+ }
2389
+ }
2390
+ if (levelUpMoveCount)
2391
+ levelUpMoveCount++;
2392
+ if (restrictiveMovesToID.includes(restrictiveMove)) {
2393
+ if (!levelUpMoveCount) {
2394
+ levelUpMoveCount++;
2395
+ }
2396
+ else if (levelUpMoveCount > 4) {
2397
+ return `'s moves ${(setSources.restrictiveMoves || []).join(', ')} are incompatible with its Pokemon GO origin.`;
2398
+ }
2399
+ }
2400
+ }
2401
+ }
2402
+ }
2160
2403
  // Now that we have our list of possible sources, intersect it with the current list
2161
2404
  if (!moveSources.size()) {
2162
2405
  if (cantLearnReason)
@@ -2171,6 +2414,8 @@ class TeamValidator {
2171
2414
  // prevents a crash if OMs override `checkCanLearn` to keep validating after an error
2172
2415
  setSources.sources = backupSources;
2173
2416
  setSources.sourcesBefore = backupSourcesBefore;
2417
+ if (setSources.isFromPokemonGo)
2418
+ return `'s move ${move.name} is incompatible with its Pokemon GO origin.`;
2174
2419
  return `'s moves ${(setSources.restrictiveMoves || []).join(', ')} are incompatible.`;
2175
2420
  }
2176
2421
  if (babyOnly)