@pkmn/sim 0.7.46 → 0.7.48
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.
- package/build/cjs/config/formats.js +228 -134
- package/build/cjs/config/formats.js.map +1 -1
- package/build/cjs/data/abilities.js +9 -6
- package/build/cjs/data/abilities.js.map +1 -1
- package/build/cjs/data/aliases.js +3 -3
- package/build/cjs/data/aliases.js.map +1 -1
- package/build/cjs/data/formats-data.js +185 -174
- package/build/cjs/data/formats-data.js.map +1 -1
- package/build/cjs/data/items.js +25 -4
- package/build/cjs/data/items.js.map +1 -1
- package/build/cjs/data/learnsets.js +157 -4
- package/build/cjs/data/learnsets.js.map +1 -1
- package/build/cjs/data/legality.js +260 -50
- package/build/cjs/data/legality.js.map +1 -1
- package/build/cjs/data/mods/gen2/scripts.js +3 -3
- package/build/cjs/data/mods/gen2/scripts.js.map +1 -1
- package/build/cjs/data/mods/gen3/formats-data.js +2 -2
- package/build/cjs/data/mods/gen3/formats-data.js.map +1 -1
- package/build/cjs/data/mods/gen3/scripts.js +1 -1
- package/build/cjs/data/mods/gen3/scripts.js.map +1 -1
- package/build/cjs/data/mods/gen4/moves.js +4 -4
- package/build/cjs/data/mods/gen4/moves.js.map +1 -1
- package/build/cjs/data/mods/gen5/formats-data.js +1 -1
- package/build/cjs/data/mods/gen5/formats-data.js.map +1 -1
- package/build/cjs/data/mods/gen5/moves.js +1 -1
- package/build/cjs/data/mods/gen5/moves.js.map +1 -1
- package/build/cjs/data/moves.js +13 -18
- package/build/cjs/data/moves.js.map +1 -1
- package/build/cjs/data/pokedex.js +39 -3
- package/build/cjs/data/pokedex.js.map +1 -1
- package/build/cjs/data/pokemongo.js +987 -0
- package/build/cjs/data/pokemongo.js.map +1 -0
- package/build/cjs/data/rulesets.js +51 -0
- package/build/cjs/data/rulesets.js.map +1 -1
- package/build/cjs/sim/battle-actions.js +8 -7
- package/build/cjs/sim/battle-actions.js.map +1 -1
- package/build/cjs/sim/dex-species.js +4 -0
- package/build/cjs/sim/dex-species.js.map +1 -1
- package/build/cjs/sim/dex.js +3 -2
- package/build/cjs/sim/dex.js.map +1 -1
- package/build/cjs/sim/pokemon.js +3 -0
- package/build/cjs/sim/pokemon.js.map +1 -1
- package/build/cjs/sim/team-validator.js +389 -67
- package/build/cjs/sim/team-validator.js.map +1 -1
- package/build/esm/config/formats.mjs +228 -134
- package/build/esm/config/formats.mjs.map +1 -1
- package/build/esm/data/abilities.mjs +9 -6
- package/build/esm/data/abilities.mjs.map +1 -1
- package/build/esm/data/aliases.mjs +3 -3
- package/build/esm/data/aliases.mjs.map +1 -1
- package/build/esm/data/formats-data.mjs +185 -174
- package/build/esm/data/formats-data.mjs.map +1 -1
- package/build/esm/data/items.mjs +25 -4
- package/build/esm/data/items.mjs.map +1 -1
- package/build/esm/data/learnsets.mjs +157 -4
- package/build/esm/data/learnsets.mjs.map +1 -1
- package/build/esm/data/legality.mjs +260 -50
- package/build/esm/data/legality.mjs.map +1 -1
- package/build/esm/data/mods/gen2/scripts.mjs +3 -3
- package/build/esm/data/mods/gen2/scripts.mjs.map +1 -1
- package/build/esm/data/mods/gen3/formats-data.mjs +2 -2
- package/build/esm/data/mods/gen3/formats-data.mjs.map +1 -1
- package/build/esm/data/mods/gen3/scripts.mjs +1 -1
- package/build/esm/data/mods/gen3/scripts.mjs.map +1 -1
- package/build/esm/data/mods/gen4/moves.mjs +4 -4
- package/build/esm/data/mods/gen4/moves.mjs.map +1 -1
- package/build/esm/data/mods/gen5/formats-data.mjs +1 -1
- package/build/esm/data/mods/gen5/formats-data.mjs.map +1 -1
- package/build/esm/data/mods/gen5/moves.mjs +1 -1
- package/build/esm/data/mods/gen5/moves.mjs.map +1 -1
- package/build/esm/data/moves.mjs +13 -18
- package/build/esm/data/moves.mjs.map +1 -1
- package/build/esm/data/pokedex.mjs +39 -3
- package/build/esm/data/pokedex.mjs.map +1 -1
- package/build/esm/data/pokemongo.mjs +984 -0
- package/build/esm/data/pokemongo.mjs.map +1 -0
- package/build/esm/data/rulesets.mjs +51 -0
- package/build/esm/data/rulesets.mjs.map +1 -1
- package/build/esm/sim/battle-actions.mjs +8 -7
- package/build/esm/sim/battle-actions.mjs.map +1 -1
- package/build/esm/sim/dex-species.mjs +4 -0
- package/build/esm/sim/dex-species.mjs.map +1 -1
- package/build/esm/sim/dex.mjs +3 -2
- package/build/esm/sim/dex.mjs.map +1 -1
- package/build/esm/sim/pokemon.mjs +3 -0
- package/build/esm/sim/pokemon.mjs.map +1 -1
- package/build/esm/sim/team-validator.mjs +389 -67
- package/build/esm/sim/team-validator.mjs.map +1 -1
- package/build/types/data/pokemongo.d.ts +31 -0
- package/build/types/sim/battle-actions.d.ts +1 -1
- package/build/types/sim/dex-species.d.ts +12 -0
- package/build/types/sim/dex.d.ts +3 -2
- package/build/types/sim/exported-global-types.d.ts +16 -2
- package/build/types/sim/global-types.d.ts +16 -2
- package/build/types/sim/pokemon.d.ts +2 -0
- package/build/types/sim/team-validator.d.ts +24 -1
- package/package.json +1 -1
|
@@ -48,6 +48,9 @@ class PokemonSources {
|
|
|
48
48
|
add(source, limitedEggMove) {
|
|
49
49
|
if (this.sources[this.sources.length - 1] !== source)
|
|
50
50
|
this.sources.push(source);
|
|
51
|
+
if (limitedEggMove && source.substr(0, 3) === '1ET') {
|
|
52
|
+
this.possiblyLimitedEggMoves = [limitedEggMove];
|
|
53
|
+
}
|
|
51
54
|
if (limitedEggMove && this.limitedEggMoves !== null) {
|
|
52
55
|
this.limitedEggMoves = [limitedEggMove];
|
|
53
56
|
}
|
|
@@ -82,6 +85,24 @@ class PokemonSources {
|
|
|
82
85
|
return max;
|
|
83
86
|
}
|
|
84
87
|
intersectWith(other) {
|
|
88
|
+
if (this.pomegEventEgg && other.pomegEggMoves) {
|
|
89
|
+
const newSources = [];
|
|
90
|
+
for (const source of other.sources) {
|
|
91
|
+
newSources.push(source.substr(0, 2) === '3E' ? this.pomegEventEgg : source);
|
|
92
|
+
}
|
|
93
|
+
other.sources = newSources;
|
|
94
|
+
}
|
|
95
|
+
else if (other.pomegEventEgg && this.pomegEventEgg !== null) {
|
|
96
|
+
const newSources = [];
|
|
97
|
+
for (const source of this.sources) {
|
|
98
|
+
newSources.push(source.substr(0, 2) === '3E' ? other.pomegEventEgg : source);
|
|
99
|
+
}
|
|
100
|
+
this.sources = newSources;
|
|
101
|
+
this.pomegEventEgg = other.pomegEventEgg;
|
|
102
|
+
}
|
|
103
|
+
else if (!other.pomegEggMoves && !other.sourcesBefore) {
|
|
104
|
+
this.pomegEventEgg = null;
|
|
105
|
+
}
|
|
85
106
|
if (other.sourcesBefore || this.sourcesBefore) {
|
|
86
107
|
// having sourcesBefore is the equivalent of having everything before that gen
|
|
87
108
|
// in sources, so we fill the other array in preparation for intersection
|
|
@@ -131,6 +152,37 @@ class PokemonSources {
|
|
|
131
152
|
this.limitedEggMoves.push(...other.limitedEggMoves);
|
|
132
153
|
}
|
|
133
154
|
}
|
|
155
|
+
if (other.possiblyLimitedEggMoves) {
|
|
156
|
+
if (!this.possiblyLimitedEggMoves) {
|
|
157
|
+
this.possiblyLimitedEggMoves = other.possiblyLimitedEggMoves;
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
this.possiblyLimitedEggMoves.push(...other.possiblyLimitedEggMoves);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
if (other.pomegEggMoves) {
|
|
164
|
+
if (!this.pomegEggMoves) {
|
|
165
|
+
this.pomegEggMoves = other.pomegEggMoves;
|
|
166
|
+
}
|
|
167
|
+
else {
|
|
168
|
+
this.pomegEggMoves.push(...other.pomegEggMoves);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
let eggTradebackLegal = false;
|
|
172
|
+
for (const source of this.sources) {
|
|
173
|
+
if (source.substr(0, 3) === '1ET') {
|
|
174
|
+
eggTradebackLegal = true;
|
|
175
|
+
break;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
if (!eggTradebackLegal && this.possiblyLimitedEggMoves) {
|
|
179
|
+
for (const eggMove of this.possiblyLimitedEggMoves) {
|
|
180
|
+
if (!this.limitedEggMoves)
|
|
181
|
+
this.limitedEggMoves = [];
|
|
182
|
+
if (!this.limitedEggMoves.includes(eggMove))
|
|
183
|
+
this.limitedEggMoves.push(eggMove);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
134
186
|
this.moveEvoCarryCount += other.moveEvoCarryCount;
|
|
135
187
|
this.dreamWorldMoveCount += other.dreamWorldMoveCount;
|
|
136
188
|
if (other.sourcesAfter > this.sourcesAfter)
|
|
@@ -575,9 +627,6 @@ class TeamValidator {
|
|
|
575
627
|
problems.push(`${name} has ${set.moves.length} moves, which is more than the limit of ${ruleTable.maxMoveCount}.`);
|
|
576
628
|
return problems;
|
|
577
629
|
}
|
|
578
|
-
if (ruleTable.isBanned('nonexistent')) {
|
|
579
|
-
problems.push(...this.validateStats(set, species, setSources));
|
|
580
|
-
}
|
|
581
630
|
const moveLegalityWhitelist = {};
|
|
582
631
|
for (const moveName of set.moves) {
|
|
583
632
|
if (!moveName)
|
|
@@ -601,20 +650,69 @@ class TeamValidator {
|
|
|
601
650
|
}
|
|
602
651
|
}
|
|
603
652
|
}
|
|
653
|
+
const pokemonGoProblems = this.validatePokemonGo(outOfBattleSpecies, set, setSources);
|
|
654
|
+
const learnsetSpecies = dex.species.getLearnsetData(outOfBattleSpecies.id);
|
|
655
|
+
let isFromRBYEncounter = false;
|
|
656
|
+
if (this.gen === 1 && ruleTable.has('obtainablemisc') && !this.ruleTable.has('allowtradeback')) {
|
|
657
|
+
let lowestEncounterLevel;
|
|
658
|
+
for (const encounter of learnsetSpecies.encounters || []) {
|
|
659
|
+
if (encounter.generation !== 1)
|
|
660
|
+
continue;
|
|
661
|
+
if (!encounter.level)
|
|
662
|
+
continue;
|
|
663
|
+
if (lowestEncounterLevel && encounter.level > lowestEncounterLevel)
|
|
664
|
+
continue;
|
|
665
|
+
lowestEncounterLevel = encounter.level;
|
|
666
|
+
}
|
|
667
|
+
if (lowestEncounterLevel) {
|
|
668
|
+
if (set.level < lowestEncounterLevel) {
|
|
669
|
+
problems.push(`${name} is not obtainable at levels below ${lowestEncounterLevel} in Gen 1.`);
|
|
670
|
+
}
|
|
671
|
+
isFromRBYEncounter = true;
|
|
672
|
+
}
|
|
673
|
+
}
|
|
674
|
+
if (!isFromRBYEncounter && ruleTable.has('obtainablemisc')) {
|
|
675
|
+
// FIXME: Event pokemon given at a level under what it normally can be attained at gives a false positive
|
|
676
|
+
let evoSpecies = species;
|
|
677
|
+
while (evoSpecies.prevo) {
|
|
678
|
+
if (set.level < (evoSpecies.evoLevel || 0)) {
|
|
679
|
+
if (!pokemonGoProblems || (pokemonGoProblems && pokemonGoProblems.length)) {
|
|
680
|
+
problems.push(`${name} must be at least level ${evoSpecies.evoLevel} to be evolved.`);
|
|
681
|
+
if (pokemonGoProblems && pokemonGoProblems.length) {
|
|
682
|
+
problems.push(`It failed to validate as a Pokemon from Pokemon GO because:`);
|
|
683
|
+
for (const pokemonGoProblem of pokemonGoProblems) {
|
|
684
|
+
problems.push(pokemonGoProblem);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
else {
|
|
689
|
+
// Pokemon from Pokemon GO can be transferred to LGPE
|
|
690
|
+
setSources.isFromPokemonGo = true;
|
|
691
|
+
setSources.sources.push('8V');
|
|
692
|
+
setSources.sourcesBefore = 0;
|
|
693
|
+
}
|
|
694
|
+
break;
|
|
695
|
+
}
|
|
696
|
+
evoSpecies = dex.species.get(evoSpecies.prevo);
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
let moveProblems;
|
|
604
700
|
if (ruleTable.has('obtainablemoves')) {
|
|
605
|
-
|
|
701
|
+
moveProblems = this.validateMoves(outOfBattleSpecies, set.moves, setSources, set, name, moveLegalityWhitelist);
|
|
702
|
+
problems.push(...moveProblems);
|
|
606
703
|
}
|
|
607
|
-
const learnsetSpecies = dex.species.getLearnsetData(outOfBattleSpecies.id);
|
|
608
704
|
let eventOnlyData;
|
|
609
705
|
if (!setSources.sourcesBefore && setSources.sources.length) {
|
|
610
|
-
|
|
706
|
+
const legalSources = [];
|
|
611
707
|
for (const source of setSources.sources) {
|
|
612
708
|
if (this.validateSource(set, source, setSources, outOfBattleSpecies))
|
|
613
709
|
continue;
|
|
614
|
-
|
|
615
|
-
break;
|
|
710
|
+
legalSources.push(source);
|
|
616
711
|
}
|
|
617
|
-
if (
|
|
712
|
+
if (legalSources.length) {
|
|
713
|
+
setSources.sources = legalSources;
|
|
714
|
+
}
|
|
715
|
+
else {
|
|
618
716
|
let nonEggSource = null;
|
|
619
717
|
for (const source of setSources.sources) {
|
|
620
718
|
if (source.charAt(1) !== 'E') {
|
|
@@ -628,12 +726,24 @@ class TeamValidator {
|
|
|
628
726
|
problems.push(`(Is this incorrect? If so, post the chainbreeding instructions in Bug Reports)`);
|
|
629
727
|
}
|
|
630
728
|
else {
|
|
631
|
-
if (
|
|
632
|
-
|
|
729
|
+
if (species.id === 'mew' && pokemonGoProblems && !pokemonGoProblems.length) {
|
|
730
|
+
// Whitelist Pokemon GO Mew, which cannot be sent to Let's Go
|
|
731
|
+
setSources.isFromPokemonGo = true;
|
|
732
|
+
}
|
|
733
|
+
else {
|
|
734
|
+
if (setSources.sources.length > 1) {
|
|
735
|
+
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):`);
|
|
736
|
+
}
|
|
737
|
+
const eventProblems = this.validateSource(set, nonEggSource, setSources, outOfBattleSpecies, ` because it has a move only available`);
|
|
738
|
+
if (eventProblems)
|
|
739
|
+
problems.push(...eventProblems);
|
|
740
|
+
if (species.id === 'mew' && pokemonGoProblems && pokemonGoProblems.length) {
|
|
741
|
+
problems.push(`Additionally, it failed to validate as a Pokemon from Pokemon GO because:`);
|
|
742
|
+
for (const pokemonGoProblem of pokemonGoProblems) {
|
|
743
|
+
problems.push(pokemonGoProblem);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
633
746
|
}
|
|
634
|
-
const eventProblems = this.validateSource(set, nonEggSource, setSources, outOfBattleSpecies, ` because it has a move only available`);
|
|
635
|
-
if (eventProblems)
|
|
636
|
-
problems.push(...eventProblems);
|
|
637
747
|
}
|
|
638
748
|
}
|
|
639
749
|
}
|
|
@@ -650,52 +760,56 @@ class TeamValidator {
|
|
|
650
760
|
legal = true;
|
|
651
761
|
}
|
|
652
762
|
if (!legal) {
|
|
653
|
-
if (
|
|
654
|
-
|
|
763
|
+
if (!pokemonGoProblems || (pokemonGoProblems && pokemonGoProblems.length)) {
|
|
764
|
+
if (eventData.length === 1) {
|
|
765
|
+
problems.push(`${species.name} is only obtainable from an event - it needs to match its event:`);
|
|
766
|
+
}
|
|
767
|
+
else {
|
|
768
|
+
problems.push(`${species.name} is only obtainable from events - it needs to match one of its events:`);
|
|
769
|
+
}
|
|
770
|
+
for (const [i, event] of eventData.entries()) {
|
|
771
|
+
if (event.generation <= dex.gen && event.generation >= this.minSourceGen) {
|
|
772
|
+
const eventInfo = event;
|
|
773
|
+
const eventNum = i + 1;
|
|
774
|
+
const eventName = eventData.length > 1 ? ` #${eventNum}` : ``;
|
|
775
|
+
const eventProblems = this.validateEvent(set, setSources, eventInfo, eventSpecies, ` to be`, `from its event${eventName}`);
|
|
776
|
+
if (eventProblems)
|
|
777
|
+
problems.push(...eventProblems);
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
if (pokemonGoProblems && pokemonGoProblems.length) {
|
|
781
|
+
problems.push(`Additionally, it failed to validate as a Pokemon from Pokemon GO because:`);
|
|
782
|
+
for (const pokemonGoProblem of pokemonGoProblems) {
|
|
783
|
+
problems.push(pokemonGoProblem);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
655
786
|
}
|
|
656
787
|
else {
|
|
657
|
-
|
|
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
|
-
}
|
|
788
|
+
setSources.isFromPokemonGo = true;
|
|
668
789
|
}
|
|
669
790
|
}
|
|
670
791
|
}
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
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.`);
|
|
792
|
+
// Hardcoded forced validation for Pokemon GO
|
|
793
|
+
const pokemonGoOnlySpecies = ['meltan', 'melmetal', 'gimmighoulroaming'];
|
|
794
|
+
if (ruleTable.has('obtainablemisc') && (pokemonGoOnlySpecies.includes(species.id))) {
|
|
795
|
+
setSources.isFromPokemonGo = true;
|
|
796
|
+
if (pokemonGoProblems && pokemonGoProblems.length) {
|
|
797
|
+
problems.push(`${name} is only obtainable from Pokemon GO, and failed to validate because:`);
|
|
798
|
+
for (const pokemonGoProblem of pokemonGoProblems) {
|
|
799
|
+
problems.push(pokemonGoProblem);
|
|
686
800
|
}
|
|
687
|
-
isFromRBYEncounter = true;
|
|
688
801
|
}
|
|
689
802
|
}
|
|
690
|
-
if (
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
803
|
+
if (ruleTable.isBanned('nonexistent')) {
|
|
804
|
+
problems.push(...this.validateStats(set, species, setSources, pokemonGoProblems));
|
|
805
|
+
}
|
|
806
|
+
// Attempt move validation again after verifying Pokemon GO origin
|
|
807
|
+
if (ruleTable.has('obtainablemoves') && setSources.isFromPokemonGo) {
|
|
808
|
+
setSources.restrictiveMoves = [];
|
|
809
|
+
setSources.sources = ['8V'];
|
|
810
|
+
setSources.sourcesBefore = 0;
|
|
811
|
+
if (moveProblems && !moveProblems.length) {
|
|
812
|
+
problems.push(...this.validateMoves(outOfBattleSpecies, set.moves, setSources, set, name, moveLegalityWhitelist));
|
|
699
813
|
}
|
|
700
814
|
}
|
|
701
815
|
if (ruleTable.has('obtainablemoves')) {
|
|
@@ -763,7 +877,8 @@ class TeamValidator {
|
|
|
763
877
|
if (nameSpecies.baseSpecies === species.baseSpecies) {
|
|
764
878
|
set.name = species.baseSpecies;
|
|
765
879
|
}
|
|
766
|
-
else if (nameSpecies.name !== species.name &&
|
|
880
|
+
else if (nameSpecies.name !== species.name &&
|
|
881
|
+
nameSpecies.name !== species.baseSpecies && ruleTable.has('nicknameclause')) {
|
|
767
882
|
// nickname species doesn't match actual species
|
|
768
883
|
// Nickname Clause
|
|
769
884
|
problems.push(`${name} must not be nicknamed a different Pokémon species than what it actually is.`);
|
|
@@ -776,7 +891,7 @@ class TeamValidator {
|
|
|
776
891
|
}
|
|
777
892
|
return problems;
|
|
778
893
|
}
|
|
779
|
-
validateStats(set, species, setSources) {
|
|
894
|
+
validateStats(set, species, setSources, pokemonGoProblems) {
|
|
780
895
|
const ruleTable = this.ruleTable;
|
|
781
896
|
const dex = this.dex;
|
|
782
897
|
const allowAVs = ruleTable.has('allowavs');
|
|
@@ -835,8 +950,19 @@ class TeamValidator {
|
|
|
835
950
|
perfectIVs++;
|
|
836
951
|
}
|
|
837
952
|
if (perfectIVs < 3) {
|
|
838
|
-
|
|
839
|
-
|
|
953
|
+
if (!pokemonGoProblems || (pokemonGoProblems && pokemonGoProblems.length)) {
|
|
954
|
+
const reason = (this.minSourceGen === 6 ? ` and this format requires Gen ${dex.gen} Pokémon` : ` in Gen 6 or later`);
|
|
955
|
+
problems.push(`${name} must have at least three perfect IVs because it's a legendary${reason}.`);
|
|
956
|
+
if (pokemonGoProblems && pokemonGoProblems.length) {
|
|
957
|
+
problems.push(`Additionally, it failed to validate as a Pokemon from Pokemon GO because:`);
|
|
958
|
+
for (const pokemonGoProblem of pokemonGoProblems) {
|
|
959
|
+
problems.push(pokemonGoProblem);
|
|
960
|
+
}
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
else {
|
|
964
|
+
setSources.isFromPokemonGo = true;
|
|
965
|
+
}
|
|
840
966
|
}
|
|
841
967
|
}
|
|
842
968
|
if (set.hpType && !canBottleCap) {
|
|
@@ -850,6 +976,24 @@ class TeamValidator {
|
|
|
850
976
|
problems.push(`${name} has Hidden Power ${set.hpType}, but its IVs don't allow this even with (Bottle Cap) Hyper Training.`);
|
|
851
977
|
}
|
|
852
978
|
}
|
|
979
|
+
if (setSources.isFromPokemonGo) {
|
|
980
|
+
// Pokemon from Pokemon GO must have odd IVs in non-Spe stats
|
|
981
|
+
// Since the set can be fixed while making minimal changes, it does not force the IVs to be manually fixed
|
|
982
|
+
for (const stat in set.ivs) {
|
|
983
|
+
if (set.ivs[stat] % 2 === 0 && stat !== 'spe') {
|
|
984
|
+
set.ivs[stat]++;
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
if (set.ivs.atk !== set.ivs.spa && !(canBottleCap && (set.ivs.atk === 31 || set.ivs.spa === 31))) {
|
|
988
|
+
problems.push(`${name}'s Atk and Spa IVs must match because it is from Pokemon GO.`);
|
|
989
|
+
}
|
|
990
|
+
if (set.ivs.def !== set.ivs.spd && !(canBottleCap && (set.ivs.def === 31 || set.ivs.spd === 31))) {
|
|
991
|
+
problems.push(`${name}'s Def and Spd IVs must match because it is from Pokemon GO.`);
|
|
992
|
+
}
|
|
993
|
+
if (set.hpType && set.hpType !== 'Dark' && set.hpType !== 'Ice') {
|
|
994
|
+
problems.push(`${name} must have Hidden Power Dark or Ice because it is from Pokemon GO.`);
|
|
995
|
+
}
|
|
996
|
+
}
|
|
853
997
|
if (dex.gen <= 2) {
|
|
854
998
|
// validate DVs
|
|
855
999
|
const ivs = set.ivs;
|
|
@@ -1061,16 +1205,18 @@ class TeamValidator {
|
|
|
1061
1205
|
const fathers = [];
|
|
1062
1206
|
// Gen 6+ don't have egg move incompatibilities
|
|
1063
1207
|
// (except for certain cases with baby Pokemon not handled here)
|
|
1064
|
-
if (!getAll && eggGen >= 6)
|
|
1208
|
+
if (!getAll && eggGen >= 6 && species.gender !== 'F')
|
|
1065
1209
|
return true;
|
|
1066
|
-
|
|
1210
|
+
let eggMoves = setSources.limitedEggMoves;
|
|
1211
|
+
if (eggGen === 3)
|
|
1212
|
+
eggMoves = eggMoves?.filter(eggMove => !setSources.pomegEggMoves?.includes(eggMove));
|
|
1067
1213
|
// must have 2 or more egg moves to have egg move incompatibilities
|
|
1068
1214
|
if (!eggMoves) {
|
|
1069
1215
|
// happens often in gen 1-6 LC if your only egg moves are level-up moves,
|
|
1070
1216
|
// which aren't limited and so aren't in `limitedEggMoves`
|
|
1071
1217
|
return getAll ? ['*'] : true;
|
|
1072
1218
|
}
|
|
1073
|
-
if (!getAll && eggMoves.length <= 1)
|
|
1219
|
+
if (!getAll && eggMoves.length <= 1 && species.gender !== 'F')
|
|
1074
1220
|
return true;
|
|
1075
1221
|
// gen 1 eggs come from gen 2 breeding
|
|
1076
1222
|
const dex = this.dex.gen === 1 ? this.dex.mod('gen2') : this.dex;
|
|
@@ -1161,6 +1307,8 @@ class TeamValidator {
|
|
|
1161
1307
|
learnset = this.dex.species.getLearnset(curSpecies.id);
|
|
1162
1308
|
if (learnset && learnset[move]) {
|
|
1163
1309
|
for (const moveSource of learnset[move]) {
|
|
1310
|
+
if (eggGen > 8 && parseInt(moveSource.charAt(0)) <= 8)
|
|
1311
|
+
continue;
|
|
1164
1312
|
if (parseInt(moveSource.charAt(0)) > eggGen)
|
|
1165
1313
|
continue;
|
|
1166
1314
|
const canLearnFromSmeargle = moveSource.charAt(1) === 'E' && canBreedWithSmeargle;
|
|
@@ -1875,6 +2023,114 @@ class TeamValidator {
|
|
|
1875
2023
|
}
|
|
1876
2024
|
return problems;
|
|
1877
2025
|
}
|
|
2026
|
+
/**
|
|
2027
|
+
* Returns a list of problems regarding a Pokemon's avilability in Pokemon GO (empty list if no problems)
|
|
2028
|
+
* If the Pokemon cannot be obtained from Pokemon GO, returns null
|
|
2029
|
+
*/
|
|
2030
|
+
validatePokemonGo(species, set, setSources, name = species.name) {
|
|
2031
|
+
let problems = [];
|
|
2032
|
+
let minLevel = 50; // maximum level a Pokemon can be in Pokemon GO
|
|
2033
|
+
let minIVs = 15; // IVs range from 0 to 15 in Pokemon GO
|
|
2034
|
+
const dex = this.dex;
|
|
2035
|
+
const pokemonGoData = dex.species.getPokemonGoData(species.id);
|
|
2036
|
+
if (dex.gen < 8 || this.format.mod === 'gen8dlc1')
|
|
2037
|
+
return null;
|
|
2038
|
+
if (!pokemonGoData) {
|
|
2039
|
+
// Handles forms and evolutions not obtainable from Pokemon GO
|
|
2040
|
+
const otherSpecies = this.learnsetParent(species);
|
|
2041
|
+
// If a Pokemon is somehow not obtainable from Pokemon GO and it must be leveled up to be evolved,
|
|
2042
|
+
// validation for the game should stop because it's more optimal to get the Pokemon outside of the game
|
|
2043
|
+
if (otherSpecies && !species.evoLevel) {
|
|
2044
|
+
const otherProblems = this.validatePokemonGo(otherSpecies, set, setSources, name);
|
|
2045
|
+
if (otherProblems) {
|
|
2046
|
+
problems = otherProblems;
|
|
2047
|
+
}
|
|
2048
|
+
else {
|
|
2049
|
+
return null;
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
else {
|
|
2053
|
+
return null;
|
|
2054
|
+
}
|
|
2055
|
+
}
|
|
2056
|
+
else {
|
|
2057
|
+
const pokemonGoSources = pokemonGoData.encounters;
|
|
2058
|
+
// should never happen
|
|
2059
|
+
if (!pokemonGoSources)
|
|
2060
|
+
throw new Error(`Species with no Pokemon GO data: ${species.id}`);
|
|
2061
|
+
if (set.shiny)
|
|
2062
|
+
name = "Shiny " + name;
|
|
2063
|
+
if (set.shiny && pokemonGoSources.includes('noshiny')) {
|
|
2064
|
+
problems.push(`${name} is not obtainable from Pokemon GO.`);
|
|
2065
|
+
}
|
|
2066
|
+
else {
|
|
2067
|
+
if (pokemonGoSources.includes('wild') && !((set.shiny && pokemonGoSources.includes('nowildshiny')))) {
|
|
2068
|
+
minLevel = 1;
|
|
2069
|
+
minIVs = 0;
|
|
2070
|
+
}
|
|
2071
|
+
if (pokemonGoSources.includes('egg')) {
|
|
2072
|
+
/**
|
|
2073
|
+
* A Pokemon's level when hatched is determined by the trainer's level when it is obtained
|
|
2074
|
+
* It is no longer possible for new accounts to obtain eggs at level 1 because they will have reached
|
|
2075
|
+
* level 2 by the time they can spin a PokeStop. However, it might be possible for a sleeper account
|
|
2076
|
+
* from before XP changes to get a level 1 egg from spinning a PokeStop that sends the account to
|
|
2077
|
+
* level 2, but this needs research
|
|
2078
|
+
*/
|
|
2079
|
+
minLevel = Math.min(minLevel, 2);
|
|
2080
|
+
minIVs = Math.min(minIVs, 10);
|
|
2081
|
+
}
|
|
2082
|
+
if (pokemonGoSources.includes('12kmegg')) {
|
|
2083
|
+
minLevel = Math.min(minLevel, 8);
|
|
2084
|
+
minIVs = Math.min(minIVs, 10);
|
|
2085
|
+
}
|
|
2086
|
+
if (pokemonGoSources.includes('raid')) {
|
|
2087
|
+
minLevel = Math.min(minLevel, 20);
|
|
2088
|
+
minIVs = Math.min(minIVs, 10);
|
|
2089
|
+
}
|
|
2090
|
+
if (species.id === 'mewtwo' && set.level && set.level >= 20) {
|
|
2091
|
+
// A bug allowed Mewtwo to be encountered with an IV floor of 0 from GO Battle League
|
|
2092
|
+
minIVs = Math.min(minIVs, 0);
|
|
2093
|
+
}
|
|
2094
|
+
if (pokemonGoSources.includes('research')) {
|
|
2095
|
+
minLevel = Math.min(minLevel, 15);
|
|
2096
|
+
minIVs = Math.min(minIVs, 10);
|
|
2097
|
+
}
|
|
2098
|
+
if (pokemonGoSources.includes('giovanni') && !set.shiny) {
|
|
2099
|
+
/**
|
|
2100
|
+
* Purified Pokemon can be leveled down to level 8 after trading; they are forced to
|
|
2101
|
+
* special trades, but currently all Giovanni Shadow Pokemon are already forced special trades
|
|
2102
|
+
*/
|
|
2103
|
+
minLevel = Math.min(minLevel, 8);
|
|
2104
|
+
minIVs = Math.min(minIVs, 1);
|
|
2105
|
+
if (set.level && set.level < 12)
|
|
2106
|
+
setSources.pokemonGoSource = "purified";
|
|
2107
|
+
}
|
|
2108
|
+
// Attempt to trade the Pokemon to reduce level and IVs
|
|
2109
|
+
if (!pokemonGoSources.includes('notrade')) {
|
|
2110
|
+
// Special trades require a good friend
|
|
2111
|
+
// Trading with a friend of this level has an IV floor of 1
|
|
2112
|
+
// Note that (non-shiny) Deoxys could be traded for a short time when it was introduced
|
|
2113
|
+
if (!set.shiny || species.id !== 'deoxys') {
|
|
2114
|
+
const specialTrade = pokemonGoSources.includes('specialtrade') || set.shiny;
|
|
2115
|
+
minLevel = Math.min(minLevel, 12);
|
|
2116
|
+
minIVs = Math.min(minIVs, specialTrade ? 1 : 0);
|
|
2117
|
+
}
|
|
2118
|
+
}
|
|
2119
|
+
if (set.level && set.level < minLevel) {
|
|
2120
|
+
problems.push(`${name} must be at least level ${minLevel} to be from Pokemon GO.`);
|
|
2121
|
+
}
|
|
2122
|
+
const ivs = set.ivs || TeamValidator.fillStats(null, 31);
|
|
2123
|
+
for (const stat in ivs) {
|
|
2124
|
+
if (Math.floor(ivs[stat] / 2) < minIVs && stat !== 'spe') {
|
|
2125
|
+
problems.push(`${name} must have at least ${minIVs} ` +
|
|
2126
|
+
(minIVs === 1 ? `IV` : `IVs`) + ` in non-Speed stats to be from Pokemon GO.`);
|
|
2127
|
+
break;
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
return problems;
|
|
2133
|
+
}
|
|
1878
2134
|
omCheckCanLearn(move, s, setSources = this.allSources(s), set = {}, problem = `${set.name || s.name} can't learn ${move.name}`) {
|
|
1879
2135
|
if (!this.ruleTable.checkCanLearn?.[0])
|
|
1880
2136
|
return problem;
|
|
@@ -2004,7 +2260,8 @@ class TeamValidator {
|
|
|
2004
2260
|
// redundant
|
|
2005
2261
|
if (learnedGen <= moveSources.sourcesBefore)
|
|
2006
2262
|
continue;
|
|
2007
|
-
if (baseSpecies.evoRegion === 'Alola' && checkingPrevo && learnedGen >= 8
|
|
2263
|
+
if (baseSpecies.evoRegion === 'Alola' && checkingPrevo && learnedGen >= 8 &&
|
|
2264
|
+
(dex.gen < 9 || learned.charAt(1) !== 'E')) {
|
|
2008
2265
|
cantLearnReason = `is from a ${species.name} that can't be transferred to USUM to evolve into ${baseSpecies.name}.`;
|
|
2009
2266
|
continue;
|
|
2010
2267
|
}
|
|
@@ -2041,8 +2298,10 @@ class TeamValidator {
|
|
|
2041
2298
|
}
|
|
2042
2299
|
else if (level >= 5 && learnedGen === 3 && species.canHatch) {
|
|
2043
2300
|
// Pomeg Glitch
|
|
2301
|
+
learned = learnedGen + 'Epomeg';
|
|
2044
2302
|
}
|
|
2045
|
-
else if ((!species.gender || species.gender === 'F') &&
|
|
2303
|
+
else if ((!species.gender || species.gender === 'F') &&
|
|
2304
|
+
learnedGen >= 2 && species.canHatch && !setSources.isFromPokemonGo) {
|
|
2046
2305
|
// available as egg move
|
|
2047
2306
|
learned = learnedGen + 'Eany';
|
|
2048
2307
|
// falls through to E check below
|
|
@@ -2054,12 +2313,20 @@ class TeamValidator {
|
|
|
2054
2313
|
}
|
|
2055
2314
|
}
|
|
2056
2315
|
// Gen 8+ egg moves can be taught to any pokemon from any source
|
|
2057
|
-
if (learnedGen >= 8 && learned.charAt(1) === 'E'
|
|
2316
|
+
if (learnedGen >= 8 && learned.charAt(1) === 'E' && learned.slice(1) !== 'Eany' &&
|
|
2317
|
+
learned.slice(1) !== 'Epomeg' || 'LMTR'.includes(learned.charAt(1))) {
|
|
2058
2318
|
if (learnedGen === dex.gen && learned.charAt(1) !== 'R') {
|
|
2059
2319
|
// current-gen level-up, TM or tutor moves:
|
|
2060
2320
|
// always available
|
|
2061
|
-
if (!(learnedGen >= 8 && learned.charAt(1) === 'E') && babyOnly)
|
|
2062
|
-
setSources.
|
|
2321
|
+
if (!(learnedGen >= 8 && learned.charAt(1) === 'E') && babyOnly) {
|
|
2322
|
+
if (setSources.isFromPokemonGo && species.evoLevel) {
|
|
2323
|
+
cantLearnReason = `is from a prevo, which is incompatible with its Pokemon GO origin.`;
|
|
2324
|
+
continue;
|
|
2325
|
+
}
|
|
2326
|
+
else {
|
|
2327
|
+
setSources.babyOnly = babyOnly;
|
|
2328
|
+
}
|
|
2329
|
+
}
|
|
2063
2330
|
if (!moveSources.moveEvoCarryCount)
|
|
2064
2331
|
return null;
|
|
2065
2332
|
}
|
|
@@ -2076,7 +2343,17 @@ class TeamValidator {
|
|
|
2076
2343
|
// only if hatched from an egg
|
|
2077
2344
|
let limitedEggMove = undefined;
|
|
2078
2345
|
if (learned.slice(1) === 'Eany') {
|
|
2079
|
-
|
|
2346
|
+
if (species.gender === 'F') {
|
|
2347
|
+
limitedEggMove = move.id;
|
|
2348
|
+
}
|
|
2349
|
+
else {
|
|
2350
|
+
limitedEggMove = null;
|
|
2351
|
+
}
|
|
2352
|
+
}
|
|
2353
|
+
else if (learned.slice(1) === 'Epomeg') {
|
|
2354
|
+
// Pomeg glitched moves have to be from an egg but since they aren't true egg moves,
|
|
2355
|
+
// there should be no breeding restrictions
|
|
2356
|
+
moveSources.pomegEggMoves = [move.id];
|
|
2080
2357
|
}
|
|
2081
2358
|
else if (learnedGen < 6) {
|
|
2082
2359
|
limitedEggMove = move.id;
|
|
@@ -2084,7 +2361,7 @@ class TeamValidator {
|
|
|
2084
2361
|
learned = learnedGen + 'E' + (species.prevo ? species.id : '');
|
|
2085
2362
|
if (tradebackEligible && learnedGen === 2 && move.gen <= 1) {
|
|
2086
2363
|
// can tradeback
|
|
2087
|
-
moveSources.add('1ET' + learned.slice(2));
|
|
2364
|
+
moveSources.add('1ET' + learned.slice(2), limitedEggMove);
|
|
2088
2365
|
}
|
|
2089
2366
|
moveSources.add(learned, limitedEggMove);
|
|
2090
2367
|
}
|
|
@@ -2098,6 +2375,10 @@ class TeamValidator {
|
|
|
2098
2375
|
moveSources.add('1ST' + learned.slice(2) + ' ' + species.id);
|
|
2099
2376
|
}
|
|
2100
2377
|
moveSources.add(learned + ' ' + species.id);
|
|
2378
|
+
const eventLearnset = dex.species.getLearnsetData(species.id);
|
|
2379
|
+
if (eventLearnset.eventData?.[parseInt(learned.charAt(2))].emeraldEventEgg && learnedGen === 3) {
|
|
2380
|
+
moveSources.pomegEventEgg = learned + ' ' + species.id;
|
|
2381
|
+
}
|
|
2101
2382
|
}
|
|
2102
2383
|
else if (learned.charAt(1) === 'D') {
|
|
2103
2384
|
// DW moves:
|
|
@@ -2108,6 +2389,10 @@ class TeamValidator {
|
|
|
2108
2389
|
else if (learned.charAt(1) === 'V' && this.minSourceGen < learnedGen) {
|
|
2109
2390
|
// Virtual Console or Let's Go transfer moves:
|
|
2110
2391
|
// only if that was the source
|
|
2392
|
+
if (learned === '8V' && setSources.isFromPokemonGo && babyOnly && species.evoLevel) {
|
|
2393
|
+
cantLearnReason = `is from a prevo, which is incompatible with its Pokemon GO origin.`;
|
|
2394
|
+
continue;
|
|
2395
|
+
}
|
|
2111
2396
|
moveSources.add(learned);
|
|
2112
2397
|
}
|
|
2113
2398
|
}
|
|
@@ -2157,6 +2442,41 @@ class TeamValidator {
|
|
|
2157
2442
|
setSources.restrictiveMoves = [];
|
|
2158
2443
|
}
|
|
2159
2444
|
setSources.restrictiveMoves.push(move.name);
|
|
2445
|
+
const checkedSpecies = babyOnly ? species : baseSpecies;
|
|
2446
|
+
if (checkedSpecies && setSources.isFromPokemonGo &&
|
|
2447
|
+
(setSources.pokemonGoSource === 'purified' || checkedSpecies.id === 'mew')) {
|
|
2448
|
+
// Pokemon that cannot be sent from Pokemon GO to Let's Go can only access Let's Go moves through HOME
|
|
2449
|
+
// It can only obtain a chain of four level up moves and cannot have TM moves
|
|
2450
|
+
const pokemonGoData = dex.species.getPokemonGoData(checkedSpecies.id);
|
|
2451
|
+
if (pokemonGoData.LGPERestrictiveMoves) {
|
|
2452
|
+
let levelUpMoveCount = 0;
|
|
2453
|
+
const restrictiveMovesToID = [];
|
|
2454
|
+
for (const moveName of setSources.restrictiveMoves) {
|
|
2455
|
+
restrictiveMovesToID.push((0, dex_1.toID)(moveName));
|
|
2456
|
+
}
|
|
2457
|
+
for (const restrictiveMove in pokemonGoData.LGPERestrictiveMoves) {
|
|
2458
|
+
const moveLevel = pokemonGoData.LGPERestrictiveMoves[restrictiveMove];
|
|
2459
|
+
if ((0, dex_1.toID)(move) === restrictiveMove) {
|
|
2460
|
+
if (!moveLevel) {
|
|
2461
|
+
return `'s move ${move.name} is incompatible with its Pokemon GO origin.`;
|
|
2462
|
+
}
|
|
2463
|
+
else if (set.level && set.level < moveLevel) {
|
|
2464
|
+
return ` must be at least level ${moveLevel} to learn ${move.name} due to its Pokemon GO origin.`;
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
if (levelUpMoveCount)
|
|
2468
|
+
levelUpMoveCount++;
|
|
2469
|
+
if (restrictiveMovesToID.includes(restrictiveMove)) {
|
|
2470
|
+
if (!levelUpMoveCount) {
|
|
2471
|
+
levelUpMoveCount++;
|
|
2472
|
+
}
|
|
2473
|
+
else if (levelUpMoveCount > 4) {
|
|
2474
|
+
return `'s moves ${(setSources.restrictiveMoves || []).join(', ')} are incompatible with its Pokemon GO origin.`;
|
|
2475
|
+
}
|
|
2476
|
+
}
|
|
2477
|
+
}
|
|
2478
|
+
}
|
|
2479
|
+
}
|
|
2160
2480
|
// Now that we have our list of possible sources, intersect it with the current list
|
|
2161
2481
|
if (!moveSources.size()) {
|
|
2162
2482
|
if (cantLearnReason)
|
|
@@ -2171,6 +2491,8 @@ class TeamValidator {
|
|
|
2171
2491
|
// prevents a crash if OMs override `checkCanLearn` to keep validating after an error
|
|
2172
2492
|
setSources.sources = backupSources;
|
|
2173
2493
|
setSources.sourcesBefore = backupSourcesBefore;
|
|
2494
|
+
if (setSources.isFromPokemonGo)
|
|
2495
|
+
return `'s move ${move.name} is incompatible with its Pokemon GO origin.`;
|
|
2174
2496
|
return `'s moves ${(setSources.restrictiveMoves || []).join(', ')} are incompatible.`;
|
|
2175
2497
|
}
|
|
2176
2498
|
if (babyOnly)
|
|
@@ -2183,7 +2505,7 @@ class TeamValidator {
|
|
|
2183
2505
|
// different learnsets. To prevent a leak, we make them show up as their
|
|
2184
2506
|
// base forme, but hardcode their learnsets into Rockruff-Dusk and
|
|
2185
2507
|
// Greninja-Ash
|
|
2186
|
-
if (['Gastrodon', 'Pumpkaboo', 'Sinistea'].includes(species.baseSpecies) && species.forme) {
|
|
2508
|
+
if (['Gastrodon', 'Pumpkaboo', 'Sinistea', 'Tatsugiri'].includes(species.baseSpecies) && species.forme) {
|
|
2187
2509
|
return this.dex.species.get(species.baseSpecies);
|
|
2188
2510
|
}
|
|
2189
2511
|
else if (species.name === 'Lycanroc-Dusk') {
|