@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
|
@@ -45,6 +45,9 @@ export class PokemonSources {
|
|
|
45
45
|
add(source, limitedEggMove) {
|
|
46
46
|
if (this.sources[this.sources.length - 1] !== source)
|
|
47
47
|
this.sources.push(source);
|
|
48
|
+
if (limitedEggMove && source.substr(0, 3) === '1ET') {
|
|
49
|
+
this.possiblyLimitedEggMoves = [limitedEggMove];
|
|
50
|
+
}
|
|
48
51
|
if (limitedEggMove && this.limitedEggMoves !== null) {
|
|
49
52
|
this.limitedEggMoves = [limitedEggMove];
|
|
50
53
|
}
|
|
@@ -79,6 +82,24 @@ export class PokemonSources {
|
|
|
79
82
|
return max;
|
|
80
83
|
}
|
|
81
84
|
intersectWith(other) {
|
|
85
|
+
if (this.pomegEventEgg && other.pomegEggMoves) {
|
|
86
|
+
const newSources = [];
|
|
87
|
+
for (const source of other.sources) {
|
|
88
|
+
newSources.push(source.substr(0, 2) === '3E' ? this.pomegEventEgg : source);
|
|
89
|
+
}
|
|
90
|
+
other.sources = newSources;
|
|
91
|
+
}
|
|
92
|
+
else if (other.pomegEventEgg && this.pomegEventEgg !== null) {
|
|
93
|
+
const newSources = [];
|
|
94
|
+
for (const source of this.sources) {
|
|
95
|
+
newSources.push(source.substr(0, 2) === '3E' ? other.pomegEventEgg : source);
|
|
96
|
+
}
|
|
97
|
+
this.sources = newSources;
|
|
98
|
+
this.pomegEventEgg = other.pomegEventEgg;
|
|
99
|
+
}
|
|
100
|
+
else if (!other.pomegEggMoves && !other.sourcesBefore) {
|
|
101
|
+
this.pomegEventEgg = null;
|
|
102
|
+
}
|
|
82
103
|
if (other.sourcesBefore || this.sourcesBefore) {
|
|
83
104
|
// having sourcesBefore is the equivalent of having everything before that gen
|
|
84
105
|
// in sources, so we fill the other array in preparation for intersection
|
|
@@ -128,6 +149,37 @@ export class PokemonSources {
|
|
|
128
149
|
this.limitedEggMoves.push(...other.limitedEggMoves);
|
|
129
150
|
}
|
|
130
151
|
}
|
|
152
|
+
if (other.possiblyLimitedEggMoves) {
|
|
153
|
+
if (!this.possiblyLimitedEggMoves) {
|
|
154
|
+
this.possiblyLimitedEggMoves = other.possiblyLimitedEggMoves;
|
|
155
|
+
}
|
|
156
|
+
else {
|
|
157
|
+
this.possiblyLimitedEggMoves.push(...other.possiblyLimitedEggMoves);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (other.pomegEggMoves) {
|
|
161
|
+
if (!this.pomegEggMoves) {
|
|
162
|
+
this.pomegEggMoves = other.pomegEggMoves;
|
|
163
|
+
}
|
|
164
|
+
else {
|
|
165
|
+
this.pomegEggMoves.push(...other.pomegEggMoves);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
let eggTradebackLegal = false;
|
|
169
|
+
for (const source of this.sources) {
|
|
170
|
+
if (source.substr(0, 3) === '1ET') {
|
|
171
|
+
eggTradebackLegal = true;
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (!eggTradebackLegal && this.possiblyLimitedEggMoves) {
|
|
176
|
+
for (const eggMove of this.possiblyLimitedEggMoves) {
|
|
177
|
+
if (!this.limitedEggMoves)
|
|
178
|
+
this.limitedEggMoves = [];
|
|
179
|
+
if (!this.limitedEggMoves.includes(eggMove))
|
|
180
|
+
this.limitedEggMoves.push(eggMove);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
131
183
|
this.moveEvoCarryCount += other.moveEvoCarryCount;
|
|
132
184
|
this.dreamWorldMoveCount += other.dreamWorldMoveCount;
|
|
133
185
|
if (other.sourcesAfter > this.sourcesAfter)
|
|
@@ -571,9 +623,6 @@ export class TeamValidator {
|
|
|
571
623
|
problems.push(`${name} has ${set.moves.length} moves, which is more than the limit of ${ruleTable.maxMoveCount}.`);
|
|
572
624
|
return problems;
|
|
573
625
|
}
|
|
574
|
-
if (ruleTable.isBanned('nonexistent')) {
|
|
575
|
-
problems.push(...this.validateStats(set, species, setSources));
|
|
576
|
-
}
|
|
577
626
|
const moveLegalityWhitelist = {};
|
|
578
627
|
for (const moveName of set.moves) {
|
|
579
628
|
if (!moveName)
|
|
@@ -597,20 +646,69 @@ export class TeamValidator {
|
|
|
597
646
|
}
|
|
598
647
|
}
|
|
599
648
|
}
|
|
649
|
+
const pokemonGoProblems = this.validatePokemonGo(outOfBattleSpecies, set, setSources);
|
|
650
|
+
const learnsetSpecies = dex.species.getLearnsetData(outOfBattleSpecies.id);
|
|
651
|
+
let isFromRBYEncounter = false;
|
|
652
|
+
if (this.gen === 1 && ruleTable.has('obtainablemisc') && !this.ruleTable.has('allowtradeback')) {
|
|
653
|
+
let lowestEncounterLevel;
|
|
654
|
+
for (const encounter of learnsetSpecies.encounters || []) {
|
|
655
|
+
if (encounter.generation !== 1)
|
|
656
|
+
continue;
|
|
657
|
+
if (!encounter.level)
|
|
658
|
+
continue;
|
|
659
|
+
if (lowestEncounterLevel && encounter.level > lowestEncounterLevel)
|
|
660
|
+
continue;
|
|
661
|
+
lowestEncounterLevel = encounter.level;
|
|
662
|
+
}
|
|
663
|
+
if (lowestEncounterLevel) {
|
|
664
|
+
if (set.level < lowestEncounterLevel) {
|
|
665
|
+
problems.push(`${name} is not obtainable at levels below ${lowestEncounterLevel} in Gen 1.`);
|
|
666
|
+
}
|
|
667
|
+
isFromRBYEncounter = true;
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
if (!isFromRBYEncounter && ruleTable.has('obtainablemisc')) {
|
|
671
|
+
// FIXME: Event pokemon given at a level under what it normally can be attained at gives a false positive
|
|
672
|
+
let evoSpecies = species;
|
|
673
|
+
while (evoSpecies.prevo) {
|
|
674
|
+
if (set.level < (evoSpecies.evoLevel || 0)) {
|
|
675
|
+
if (!pokemonGoProblems || (pokemonGoProblems && pokemonGoProblems.length)) {
|
|
676
|
+
problems.push(`${name} must be at least level ${evoSpecies.evoLevel} to be evolved.`);
|
|
677
|
+
if (pokemonGoProblems && pokemonGoProblems.length) {
|
|
678
|
+
problems.push(`It failed to validate as a Pokemon from Pokemon GO because:`);
|
|
679
|
+
for (const pokemonGoProblem of pokemonGoProblems) {
|
|
680
|
+
problems.push(pokemonGoProblem);
|
|
681
|
+
}
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
else {
|
|
685
|
+
// Pokemon from Pokemon GO can be transferred to LGPE
|
|
686
|
+
setSources.isFromPokemonGo = true;
|
|
687
|
+
setSources.sources.push('8V');
|
|
688
|
+
setSources.sourcesBefore = 0;
|
|
689
|
+
}
|
|
690
|
+
break;
|
|
691
|
+
}
|
|
692
|
+
evoSpecies = dex.species.get(evoSpecies.prevo);
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
let moveProblems;
|
|
600
696
|
if (ruleTable.has('obtainablemoves')) {
|
|
601
|
-
|
|
697
|
+
moveProblems = this.validateMoves(outOfBattleSpecies, set.moves, setSources, set, name, moveLegalityWhitelist);
|
|
698
|
+
problems.push(...moveProblems);
|
|
602
699
|
}
|
|
603
|
-
const learnsetSpecies = dex.species.getLearnsetData(outOfBattleSpecies.id);
|
|
604
700
|
let eventOnlyData;
|
|
605
701
|
if (!setSources.sourcesBefore && setSources.sources.length) {
|
|
606
|
-
|
|
702
|
+
const legalSources = [];
|
|
607
703
|
for (const source of setSources.sources) {
|
|
608
704
|
if (this.validateSource(set, source, setSources, outOfBattleSpecies))
|
|
609
705
|
continue;
|
|
610
|
-
|
|
611
|
-
break;
|
|
706
|
+
legalSources.push(source);
|
|
612
707
|
}
|
|
613
|
-
if (
|
|
708
|
+
if (legalSources.length) {
|
|
709
|
+
setSources.sources = legalSources;
|
|
710
|
+
}
|
|
711
|
+
else {
|
|
614
712
|
let nonEggSource = null;
|
|
615
713
|
for (const source of setSources.sources) {
|
|
616
714
|
if (source.charAt(1) !== 'E') {
|
|
@@ -624,12 +722,24 @@ export class TeamValidator {
|
|
|
624
722
|
problems.push(`(Is this incorrect? If so, post the chainbreeding instructions in Bug Reports)`);
|
|
625
723
|
}
|
|
626
724
|
else {
|
|
627
|
-
if (
|
|
628
|
-
|
|
725
|
+
if (species.id === 'mew' && pokemonGoProblems && !pokemonGoProblems.length) {
|
|
726
|
+
// Whitelist Pokemon GO Mew, which cannot be sent to Let's Go
|
|
727
|
+
setSources.isFromPokemonGo = true;
|
|
728
|
+
}
|
|
729
|
+
else {
|
|
730
|
+
if (setSources.sources.length > 1) {
|
|
731
|
+
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):`);
|
|
732
|
+
}
|
|
733
|
+
const eventProblems = this.validateSource(set, nonEggSource, setSources, outOfBattleSpecies, ` because it has a move only available`);
|
|
734
|
+
if (eventProblems)
|
|
735
|
+
problems.push(...eventProblems);
|
|
736
|
+
if (species.id === 'mew' && pokemonGoProblems && pokemonGoProblems.length) {
|
|
737
|
+
problems.push(`Additionally, it failed to validate as a Pokemon from Pokemon GO because:`);
|
|
738
|
+
for (const pokemonGoProblem of pokemonGoProblems) {
|
|
739
|
+
problems.push(pokemonGoProblem);
|
|
740
|
+
}
|
|
741
|
+
}
|
|
629
742
|
}
|
|
630
|
-
const eventProblems = this.validateSource(set, nonEggSource, setSources, outOfBattleSpecies, ` because it has a move only available`);
|
|
631
|
-
if (eventProblems)
|
|
632
|
-
problems.push(...eventProblems);
|
|
633
743
|
}
|
|
634
744
|
}
|
|
635
745
|
}
|
|
@@ -646,52 +756,56 @@ export class TeamValidator {
|
|
|
646
756
|
legal = true;
|
|
647
757
|
}
|
|
648
758
|
if (!legal) {
|
|
649
|
-
if (
|
|
650
|
-
|
|
759
|
+
if (!pokemonGoProblems || (pokemonGoProblems && pokemonGoProblems.length)) {
|
|
760
|
+
if (eventData.length === 1) {
|
|
761
|
+
problems.push(`${species.name} is only obtainable from an event - it needs to match its event:`);
|
|
762
|
+
}
|
|
763
|
+
else {
|
|
764
|
+
problems.push(`${species.name} is only obtainable from events - it needs to match one of its events:`);
|
|
765
|
+
}
|
|
766
|
+
for (const [i, event] of eventData.entries()) {
|
|
767
|
+
if (event.generation <= dex.gen && event.generation >= this.minSourceGen) {
|
|
768
|
+
const eventInfo = event;
|
|
769
|
+
const eventNum = i + 1;
|
|
770
|
+
const eventName = eventData.length > 1 ? ` #${eventNum}` : ``;
|
|
771
|
+
const eventProblems = this.validateEvent(set, setSources, eventInfo, eventSpecies, ` to be`, `from its event${eventName}`);
|
|
772
|
+
if (eventProblems)
|
|
773
|
+
problems.push(...eventProblems);
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
if (pokemonGoProblems && pokemonGoProblems.length) {
|
|
777
|
+
problems.push(`Additionally, it failed to validate as a Pokemon from Pokemon GO because:`);
|
|
778
|
+
for (const pokemonGoProblem of pokemonGoProblems) {
|
|
779
|
+
problems.push(pokemonGoProblem);
|
|
780
|
+
}
|
|
781
|
+
}
|
|
651
782
|
}
|
|
652
783
|
else {
|
|
653
|
-
|
|
654
|
-
}
|
|
655
|
-
for (const [i, event] of eventData.entries()) {
|
|
656
|
-
if (event.generation <= dex.gen && event.generation >= this.minSourceGen) {
|
|
657
|
-
const eventInfo = event;
|
|
658
|
-
const eventNum = i + 1;
|
|
659
|
-
const eventName = eventData.length > 1 ? ` #${eventNum}` : ``;
|
|
660
|
-
const eventProblems = this.validateEvent(set, setSources, eventInfo, eventSpecies, ` to be`, `from its event${eventName}`);
|
|
661
|
-
if (eventProblems)
|
|
662
|
-
problems.push(...eventProblems);
|
|
663
|
-
}
|
|
784
|
+
setSources.isFromPokemonGo = true;
|
|
664
785
|
}
|
|
665
786
|
}
|
|
666
787
|
}
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
if (lowestEncounterLevel && encounter.level > lowestEncounterLevel)
|
|
676
|
-
continue;
|
|
677
|
-
lowestEncounterLevel = encounter.level;
|
|
678
|
-
}
|
|
679
|
-
if (lowestEncounterLevel) {
|
|
680
|
-
if (set.level < lowestEncounterLevel) {
|
|
681
|
-
problems.push(`${name} is not obtainable at levels below ${lowestEncounterLevel} in Gen 1.`);
|
|
788
|
+
// Hardcoded forced validation for Pokemon GO
|
|
789
|
+
const pokemonGoOnlySpecies = ['meltan', 'melmetal', 'gimmighoulroaming'];
|
|
790
|
+
if (ruleTable.has('obtainablemisc') && (pokemonGoOnlySpecies.includes(species.id))) {
|
|
791
|
+
setSources.isFromPokemonGo = true;
|
|
792
|
+
if (pokemonGoProblems && pokemonGoProblems.length) {
|
|
793
|
+
problems.push(`${name} is only obtainable from Pokemon GO, and failed to validate because:`);
|
|
794
|
+
for (const pokemonGoProblem of pokemonGoProblems) {
|
|
795
|
+
problems.push(pokemonGoProblem);
|
|
682
796
|
}
|
|
683
|
-
isFromRBYEncounter = true;
|
|
684
797
|
}
|
|
685
798
|
}
|
|
686
|
-
if (
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
799
|
+
if (ruleTable.isBanned('nonexistent')) {
|
|
800
|
+
problems.push(...this.validateStats(set, species, setSources, pokemonGoProblems));
|
|
801
|
+
}
|
|
802
|
+
// Attempt move validation again after verifying Pokemon GO origin
|
|
803
|
+
if (ruleTable.has('obtainablemoves') && setSources.isFromPokemonGo) {
|
|
804
|
+
setSources.restrictiveMoves = [];
|
|
805
|
+
setSources.sources = ['8V'];
|
|
806
|
+
setSources.sourcesBefore = 0;
|
|
807
|
+
if (moveProblems && !moveProblems.length) {
|
|
808
|
+
problems.push(...this.validateMoves(outOfBattleSpecies, set.moves, setSources, set, name, moveLegalityWhitelist));
|
|
695
809
|
}
|
|
696
810
|
}
|
|
697
811
|
if (ruleTable.has('obtainablemoves')) {
|
|
@@ -759,7 +873,8 @@ export class TeamValidator {
|
|
|
759
873
|
if (nameSpecies.baseSpecies === species.baseSpecies) {
|
|
760
874
|
set.name = species.baseSpecies;
|
|
761
875
|
}
|
|
762
|
-
else if (nameSpecies.name !== species.name &&
|
|
876
|
+
else if (nameSpecies.name !== species.name &&
|
|
877
|
+
nameSpecies.name !== species.baseSpecies && ruleTable.has('nicknameclause')) {
|
|
763
878
|
// nickname species doesn't match actual species
|
|
764
879
|
// Nickname Clause
|
|
765
880
|
problems.push(`${name} must not be nicknamed a different Pokémon species than what it actually is.`);
|
|
@@ -772,7 +887,7 @@ export class TeamValidator {
|
|
|
772
887
|
}
|
|
773
888
|
return problems;
|
|
774
889
|
}
|
|
775
|
-
validateStats(set, species, setSources) {
|
|
890
|
+
validateStats(set, species, setSources, pokemonGoProblems) {
|
|
776
891
|
const ruleTable = this.ruleTable;
|
|
777
892
|
const dex = this.dex;
|
|
778
893
|
const allowAVs = ruleTable.has('allowavs');
|
|
@@ -831,8 +946,19 @@ export class TeamValidator {
|
|
|
831
946
|
perfectIVs++;
|
|
832
947
|
}
|
|
833
948
|
if (perfectIVs < 3) {
|
|
834
|
-
|
|
835
|
-
|
|
949
|
+
if (!pokemonGoProblems || (pokemonGoProblems && pokemonGoProblems.length)) {
|
|
950
|
+
const reason = (this.minSourceGen === 6 ? ` and this format requires Gen ${dex.gen} Pokémon` : ` in Gen 6 or later`);
|
|
951
|
+
problems.push(`${name} must have at least three perfect IVs because it's a legendary${reason}.`);
|
|
952
|
+
if (pokemonGoProblems && pokemonGoProblems.length) {
|
|
953
|
+
problems.push(`Additionally, it failed to validate as a Pokemon from Pokemon GO because:`);
|
|
954
|
+
for (const pokemonGoProblem of pokemonGoProblems) {
|
|
955
|
+
problems.push(pokemonGoProblem);
|
|
956
|
+
}
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
else {
|
|
960
|
+
setSources.isFromPokemonGo = true;
|
|
961
|
+
}
|
|
836
962
|
}
|
|
837
963
|
}
|
|
838
964
|
if (set.hpType && !canBottleCap) {
|
|
@@ -846,6 +972,24 @@ export class TeamValidator {
|
|
|
846
972
|
problems.push(`${name} has Hidden Power ${set.hpType}, but its IVs don't allow this even with (Bottle Cap) Hyper Training.`);
|
|
847
973
|
}
|
|
848
974
|
}
|
|
975
|
+
if (setSources.isFromPokemonGo) {
|
|
976
|
+
// Pokemon from Pokemon GO must have odd IVs in non-Spe stats
|
|
977
|
+
// Since the set can be fixed while making minimal changes, it does not force the IVs to be manually fixed
|
|
978
|
+
for (const stat in set.ivs) {
|
|
979
|
+
if (set.ivs[stat] % 2 === 0 && stat !== 'spe') {
|
|
980
|
+
set.ivs[stat]++;
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
if (set.ivs.atk !== set.ivs.spa && !(canBottleCap && (set.ivs.atk === 31 || set.ivs.spa === 31))) {
|
|
984
|
+
problems.push(`${name}'s Atk and Spa IVs must match because it is from Pokemon GO.`);
|
|
985
|
+
}
|
|
986
|
+
if (set.ivs.def !== set.ivs.spd && !(canBottleCap && (set.ivs.def === 31 || set.ivs.spd === 31))) {
|
|
987
|
+
problems.push(`${name}'s Def and Spd IVs must match because it is from Pokemon GO.`);
|
|
988
|
+
}
|
|
989
|
+
if (set.hpType && set.hpType !== 'Dark' && set.hpType !== 'Ice') {
|
|
990
|
+
problems.push(`${name} must have Hidden Power Dark or Ice because it is from Pokemon GO.`);
|
|
991
|
+
}
|
|
992
|
+
}
|
|
849
993
|
if (dex.gen <= 2) {
|
|
850
994
|
// validate DVs
|
|
851
995
|
const ivs = set.ivs;
|
|
@@ -1057,16 +1201,18 @@ export class TeamValidator {
|
|
|
1057
1201
|
const fathers = [];
|
|
1058
1202
|
// Gen 6+ don't have egg move incompatibilities
|
|
1059
1203
|
// (except for certain cases with baby Pokemon not handled here)
|
|
1060
|
-
if (!getAll && eggGen >= 6)
|
|
1204
|
+
if (!getAll && eggGen >= 6 && species.gender !== 'F')
|
|
1061
1205
|
return true;
|
|
1062
|
-
|
|
1206
|
+
let eggMoves = setSources.limitedEggMoves;
|
|
1207
|
+
if (eggGen === 3)
|
|
1208
|
+
eggMoves = eggMoves?.filter(eggMove => !setSources.pomegEggMoves?.includes(eggMove));
|
|
1063
1209
|
// must have 2 or more egg moves to have egg move incompatibilities
|
|
1064
1210
|
if (!eggMoves) {
|
|
1065
1211
|
// happens often in gen 1-6 LC if your only egg moves are level-up moves,
|
|
1066
1212
|
// which aren't limited and so aren't in `limitedEggMoves`
|
|
1067
1213
|
return getAll ? ['*'] : true;
|
|
1068
1214
|
}
|
|
1069
|
-
if (!getAll && eggMoves.length <= 1)
|
|
1215
|
+
if (!getAll && eggMoves.length <= 1 && species.gender !== 'F')
|
|
1070
1216
|
return true;
|
|
1071
1217
|
// gen 1 eggs come from gen 2 breeding
|
|
1072
1218
|
const dex = this.dex.gen === 1 ? this.dex.mod('gen2') : this.dex;
|
|
@@ -1157,6 +1303,8 @@ export class TeamValidator {
|
|
|
1157
1303
|
learnset = this.dex.species.getLearnset(curSpecies.id);
|
|
1158
1304
|
if (learnset && learnset[move]) {
|
|
1159
1305
|
for (const moveSource of learnset[move]) {
|
|
1306
|
+
if (eggGen > 8 && parseInt(moveSource.charAt(0)) <= 8)
|
|
1307
|
+
continue;
|
|
1160
1308
|
if (parseInt(moveSource.charAt(0)) > eggGen)
|
|
1161
1309
|
continue;
|
|
1162
1310
|
const canLearnFromSmeargle = moveSource.charAt(1) === 'E' && canBreedWithSmeargle;
|
|
@@ -1871,6 +2019,114 @@ export class TeamValidator {
|
|
|
1871
2019
|
}
|
|
1872
2020
|
return problems;
|
|
1873
2021
|
}
|
|
2022
|
+
/**
|
|
2023
|
+
* Returns a list of problems regarding a Pokemon's avilability in Pokemon GO (empty list if no problems)
|
|
2024
|
+
* If the Pokemon cannot be obtained from Pokemon GO, returns null
|
|
2025
|
+
*/
|
|
2026
|
+
validatePokemonGo(species, set, setSources, name = species.name) {
|
|
2027
|
+
let problems = [];
|
|
2028
|
+
let minLevel = 50; // maximum level a Pokemon can be in Pokemon GO
|
|
2029
|
+
let minIVs = 15; // IVs range from 0 to 15 in Pokemon GO
|
|
2030
|
+
const dex = this.dex;
|
|
2031
|
+
const pokemonGoData = dex.species.getPokemonGoData(species.id);
|
|
2032
|
+
if (dex.gen < 8 || this.format.mod === 'gen8dlc1')
|
|
2033
|
+
return null;
|
|
2034
|
+
if (!pokemonGoData) {
|
|
2035
|
+
// Handles forms and evolutions not obtainable from Pokemon GO
|
|
2036
|
+
const otherSpecies = this.learnsetParent(species);
|
|
2037
|
+
// If a Pokemon is somehow not obtainable from Pokemon GO and it must be leveled up to be evolved,
|
|
2038
|
+
// validation for the game should stop because it's more optimal to get the Pokemon outside of the game
|
|
2039
|
+
if (otherSpecies && !species.evoLevel) {
|
|
2040
|
+
const otherProblems = this.validatePokemonGo(otherSpecies, set, setSources, name);
|
|
2041
|
+
if (otherProblems) {
|
|
2042
|
+
problems = otherProblems;
|
|
2043
|
+
}
|
|
2044
|
+
else {
|
|
2045
|
+
return null;
|
|
2046
|
+
}
|
|
2047
|
+
}
|
|
2048
|
+
else {
|
|
2049
|
+
return null;
|
|
2050
|
+
}
|
|
2051
|
+
}
|
|
2052
|
+
else {
|
|
2053
|
+
const pokemonGoSources = pokemonGoData.encounters;
|
|
2054
|
+
// should never happen
|
|
2055
|
+
if (!pokemonGoSources)
|
|
2056
|
+
throw new Error(`Species with no Pokemon GO data: ${species.id}`);
|
|
2057
|
+
if (set.shiny)
|
|
2058
|
+
name = "Shiny " + name;
|
|
2059
|
+
if (set.shiny && pokemonGoSources.includes('noshiny')) {
|
|
2060
|
+
problems.push(`${name} is not obtainable from Pokemon GO.`);
|
|
2061
|
+
}
|
|
2062
|
+
else {
|
|
2063
|
+
if (pokemonGoSources.includes('wild') && !((set.shiny && pokemonGoSources.includes('nowildshiny')))) {
|
|
2064
|
+
minLevel = 1;
|
|
2065
|
+
minIVs = 0;
|
|
2066
|
+
}
|
|
2067
|
+
if (pokemonGoSources.includes('egg')) {
|
|
2068
|
+
/**
|
|
2069
|
+
* A Pokemon's level when hatched is determined by the trainer's level when it is obtained
|
|
2070
|
+
* It is no longer possible for new accounts to obtain eggs at level 1 because they will have reached
|
|
2071
|
+
* level 2 by the time they can spin a PokeStop. However, it might be possible for a sleeper account
|
|
2072
|
+
* from before XP changes to get a level 1 egg from spinning a PokeStop that sends the account to
|
|
2073
|
+
* level 2, but this needs research
|
|
2074
|
+
*/
|
|
2075
|
+
minLevel = Math.min(minLevel, 2);
|
|
2076
|
+
minIVs = Math.min(minIVs, 10);
|
|
2077
|
+
}
|
|
2078
|
+
if (pokemonGoSources.includes('12kmegg')) {
|
|
2079
|
+
minLevel = Math.min(minLevel, 8);
|
|
2080
|
+
minIVs = Math.min(minIVs, 10);
|
|
2081
|
+
}
|
|
2082
|
+
if (pokemonGoSources.includes('raid')) {
|
|
2083
|
+
minLevel = Math.min(minLevel, 20);
|
|
2084
|
+
minIVs = Math.min(minIVs, 10);
|
|
2085
|
+
}
|
|
2086
|
+
if (species.id === 'mewtwo' && set.level && set.level >= 20) {
|
|
2087
|
+
// A bug allowed Mewtwo to be encountered with an IV floor of 0 from GO Battle League
|
|
2088
|
+
minIVs = Math.min(minIVs, 0);
|
|
2089
|
+
}
|
|
2090
|
+
if (pokemonGoSources.includes('research')) {
|
|
2091
|
+
minLevel = Math.min(minLevel, 15);
|
|
2092
|
+
minIVs = Math.min(minIVs, 10);
|
|
2093
|
+
}
|
|
2094
|
+
if (pokemonGoSources.includes('giovanni') && !set.shiny) {
|
|
2095
|
+
/**
|
|
2096
|
+
* Purified Pokemon can be leveled down to level 8 after trading; they are forced to
|
|
2097
|
+
* special trades, but currently all Giovanni Shadow Pokemon are already forced special trades
|
|
2098
|
+
*/
|
|
2099
|
+
minLevel = Math.min(minLevel, 8);
|
|
2100
|
+
minIVs = Math.min(minIVs, 1);
|
|
2101
|
+
if (set.level && set.level < 12)
|
|
2102
|
+
setSources.pokemonGoSource = "purified";
|
|
2103
|
+
}
|
|
2104
|
+
// Attempt to trade the Pokemon to reduce level and IVs
|
|
2105
|
+
if (!pokemonGoSources.includes('notrade')) {
|
|
2106
|
+
// Special trades require a good friend
|
|
2107
|
+
// Trading with a friend of this level has an IV floor of 1
|
|
2108
|
+
// Note that (non-shiny) Deoxys could be traded for a short time when it was introduced
|
|
2109
|
+
if (!set.shiny || species.id !== 'deoxys') {
|
|
2110
|
+
const specialTrade = pokemonGoSources.includes('specialtrade') || set.shiny;
|
|
2111
|
+
minLevel = Math.min(minLevel, 12);
|
|
2112
|
+
minIVs = Math.min(minIVs, specialTrade ? 1 : 0);
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
if (set.level && set.level < minLevel) {
|
|
2116
|
+
problems.push(`${name} must be at least level ${minLevel} to be from Pokemon GO.`);
|
|
2117
|
+
}
|
|
2118
|
+
const ivs = set.ivs || TeamValidator.fillStats(null, 31);
|
|
2119
|
+
for (const stat in ivs) {
|
|
2120
|
+
if (Math.floor(ivs[stat] / 2) < minIVs && stat !== 'spe') {
|
|
2121
|
+
problems.push(`${name} must have at least ${minIVs} ` +
|
|
2122
|
+
(minIVs === 1 ? `IV` : `IVs`) + ` in non-Speed stats to be from Pokemon GO.`);
|
|
2123
|
+
break;
|
|
2124
|
+
}
|
|
2125
|
+
}
|
|
2126
|
+
}
|
|
2127
|
+
}
|
|
2128
|
+
return problems;
|
|
2129
|
+
}
|
|
1874
2130
|
omCheckCanLearn(move, s, setSources = this.allSources(s), set = {}, problem = `${set.name || s.name} can't learn ${move.name}`) {
|
|
1875
2131
|
if (!this.ruleTable.checkCanLearn?.[0])
|
|
1876
2132
|
return problem;
|
|
@@ -2000,7 +2256,8 @@ export class TeamValidator {
|
|
|
2000
2256
|
// redundant
|
|
2001
2257
|
if (learnedGen <= moveSources.sourcesBefore)
|
|
2002
2258
|
continue;
|
|
2003
|
-
if (baseSpecies.evoRegion === 'Alola' && checkingPrevo && learnedGen >= 8
|
|
2259
|
+
if (baseSpecies.evoRegion === 'Alola' && checkingPrevo && learnedGen >= 8 &&
|
|
2260
|
+
(dex.gen < 9 || learned.charAt(1) !== 'E')) {
|
|
2004
2261
|
cantLearnReason = `is from a ${species.name} that can't be transferred to USUM to evolve into ${baseSpecies.name}.`;
|
|
2005
2262
|
continue;
|
|
2006
2263
|
}
|
|
@@ -2037,8 +2294,10 @@ export class TeamValidator {
|
|
|
2037
2294
|
}
|
|
2038
2295
|
else if (level >= 5 && learnedGen === 3 && species.canHatch) {
|
|
2039
2296
|
// Pomeg Glitch
|
|
2297
|
+
learned = learnedGen + 'Epomeg';
|
|
2040
2298
|
}
|
|
2041
|
-
else if ((!species.gender || species.gender === 'F') &&
|
|
2299
|
+
else if ((!species.gender || species.gender === 'F') &&
|
|
2300
|
+
learnedGen >= 2 && species.canHatch && !setSources.isFromPokemonGo) {
|
|
2042
2301
|
// available as egg move
|
|
2043
2302
|
learned = learnedGen + 'Eany';
|
|
2044
2303
|
// falls through to E check below
|
|
@@ -2050,12 +2309,20 @@ export class TeamValidator {
|
|
|
2050
2309
|
}
|
|
2051
2310
|
}
|
|
2052
2311
|
// Gen 8+ egg moves can be taught to any pokemon from any source
|
|
2053
|
-
if (learnedGen >= 8 && learned.charAt(1) === 'E'
|
|
2312
|
+
if (learnedGen >= 8 && learned.charAt(1) === 'E' && learned.slice(1) !== 'Eany' &&
|
|
2313
|
+
learned.slice(1) !== 'Epomeg' || 'LMTR'.includes(learned.charAt(1))) {
|
|
2054
2314
|
if (learnedGen === dex.gen && learned.charAt(1) !== 'R') {
|
|
2055
2315
|
// current-gen level-up, TM or tutor moves:
|
|
2056
2316
|
// always available
|
|
2057
|
-
if (!(learnedGen >= 8 && learned.charAt(1) === 'E') && babyOnly)
|
|
2058
|
-
setSources.
|
|
2317
|
+
if (!(learnedGen >= 8 && learned.charAt(1) === 'E') && babyOnly) {
|
|
2318
|
+
if (setSources.isFromPokemonGo && species.evoLevel) {
|
|
2319
|
+
cantLearnReason = `is from a prevo, which is incompatible with its Pokemon GO origin.`;
|
|
2320
|
+
continue;
|
|
2321
|
+
}
|
|
2322
|
+
else {
|
|
2323
|
+
setSources.babyOnly = babyOnly;
|
|
2324
|
+
}
|
|
2325
|
+
}
|
|
2059
2326
|
if (!moveSources.moveEvoCarryCount)
|
|
2060
2327
|
return null;
|
|
2061
2328
|
}
|
|
@@ -2072,7 +2339,17 @@ export class TeamValidator {
|
|
|
2072
2339
|
// only if hatched from an egg
|
|
2073
2340
|
let limitedEggMove = undefined;
|
|
2074
2341
|
if (learned.slice(1) === 'Eany') {
|
|
2075
|
-
|
|
2342
|
+
if (species.gender === 'F') {
|
|
2343
|
+
limitedEggMove = move.id;
|
|
2344
|
+
}
|
|
2345
|
+
else {
|
|
2346
|
+
limitedEggMove = null;
|
|
2347
|
+
}
|
|
2348
|
+
}
|
|
2349
|
+
else if (learned.slice(1) === 'Epomeg') {
|
|
2350
|
+
// Pomeg glitched moves have to be from an egg but since they aren't true egg moves,
|
|
2351
|
+
// there should be no breeding restrictions
|
|
2352
|
+
moveSources.pomegEggMoves = [move.id];
|
|
2076
2353
|
}
|
|
2077
2354
|
else if (learnedGen < 6) {
|
|
2078
2355
|
limitedEggMove = move.id;
|
|
@@ -2080,7 +2357,7 @@ export class TeamValidator {
|
|
|
2080
2357
|
learned = learnedGen + 'E' + (species.prevo ? species.id : '');
|
|
2081
2358
|
if (tradebackEligible && learnedGen === 2 && move.gen <= 1) {
|
|
2082
2359
|
// can tradeback
|
|
2083
|
-
moveSources.add('1ET' + learned.slice(2));
|
|
2360
|
+
moveSources.add('1ET' + learned.slice(2), limitedEggMove);
|
|
2084
2361
|
}
|
|
2085
2362
|
moveSources.add(learned, limitedEggMove);
|
|
2086
2363
|
}
|
|
@@ -2094,6 +2371,10 @@ export class TeamValidator {
|
|
|
2094
2371
|
moveSources.add('1ST' + learned.slice(2) + ' ' + species.id);
|
|
2095
2372
|
}
|
|
2096
2373
|
moveSources.add(learned + ' ' + species.id);
|
|
2374
|
+
const eventLearnset = dex.species.getLearnsetData(species.id);
|
|
2375
|
+
if (eventLearnset.eventData?.[parseInt(learned.charAt(2))].emeraldEventEgg && learnedGen === 3) {
|
|
2376
|
+
moveSources.pomegEventEgg = learned + ' ' + species.id;
|
|
2377
|
+
}
|
|
2097
2378
|
}
|
|
2098
2379
|
else if (learned.charAt(1) === 'D') {
|
|
2099
2380
|
// DW moves:
|
|
@@ -2104,6 +2385,10 @@ export class TeamValidator {
|
|
|
2104
2385
|
else if (learned.charAt(1) === 'V' && this.minSourceGen < learnedGen) {
|
|
2105
2386
|
// Virtual Console or Let's Go transfer moves:
|
|
2106
2387
|
// only if that was the source
|
|
2388
|
+
if (learned === '8V' && setSources.isFromPokemonGo && babyOnly && species.evoLevel) {
|
|
2389
|
+
cantLearnReason = `is from a prevo, which is incompatible with its Pokemon GO origin.`;
|
|
2390
|
+
continue;
|
|
2391
|
+
}
|
|
2107
2392
|
moveSources.add(learned);
|
|
2108
2393
|
}
|
|
2109
2394
|
}
|
|
@@ -2153,6 +2438,41 @@ export class TeamValidator {
|
|
|
2153
2438
|
setSources.restrictiveMoves = [];
|
|
2154
2439
|
}
|
|
2155
2440
|
setSources.restrictiveMoves.push(move.name);
|
|
2441
|
+
const checkedSpecies = babyOnly ? species : baseSpecies;
|
|
2442
|
+
if (checkedSpecies && setSources.isFromPokemonGo &&
|
|
2443
|
+
(setSources.pokemonGoSource === 'purified' || checkedSpecies.id === 'mew')) {
|
|
2444
|
+
// Pokemon that cannot be sent from Pokemon GO to Let's Go can only access Let's Go moves through HOME
|
|
2445
|
+
// It can only obtain a chain of four level up moves and cannot have TM moves
|
|
2446
|
+
const pokemonGoData = dex.species.getPokemonGoData(checkedSpecies.id);
|
|
2447
|
+
if (pokemonGoData.LGPERestrictiveMoves) {
|
|
2448
|
+
let levelUpMoveCount = 0;
|
|
2449
|
+
const restrictiveMovesToID = [];
|
|
2450
|
+
for (const moveName of setSources.restrictiveMoves) {
|
|
2451
|
+
restrictiveMovesToID.push(toID(moveName));
|
|
2452
|
+
}
|
|
2453
|
+
for (const restrictiveMove in pokemonGoData.LGPERestrictiveMoves) {
|
|
2454
|
+
const moveLevel = pokemonGoData.LGPERestrictiveMoves[restrictiveMove];
|
|
2455
|
+
if (toID(move) === restrictiveMove) {
|
|
2456
|
+
if (!moveLevel) {
|
|
2457
|
+
return `'s move ${move.name} is incompatible with its Pokemon GO origin.`;
|
|
2458
|
+
}
|
|
2459
|
+
else if (set.level && set.level < moveLevel) {
|
|
2460
|
+
return ` must be at least level ${moveLevel} to learn ${move.name} due to its Pokemon GO origin.`;
|
|
2461
|
+
}
|
|
2462
|
+
}
|
|
2463
|
+
if (levelUpMoveCount)
|
|
2464
|
+
levelUpMoveCount++;
|
|
2465
|
+
if (restrictiveMovesToID.includes(restrictiveMove)) {
|
|
2466
|
+
if (!levelUpMoveCount) {
|
|
2467
|
+
levelUpMoveCount++;
|
|
2468
|
+
}
|
|
2469
|
+
else if (levelUpMoveCount > 4) {
|
|
2470
|
+
return `'s moves ${(setSources.restrictiveMoves || []).join(', ')} are incompatible with its Pokemon GO origin.`;
|
|
2471
|
+
}
|
|
2472
|
+
}
|
|
2473
|
+
}
|
|
2474
|
+
}
|
|
2475
|
+
}
|
|
2156
2476
|
// Now that we have our list of possible sources, intersect it with the current list
|
|
2157
2477
|
if (!moveSources.size()) {
|
|
2158
2478
|
if (cantLearnReason)
|
|
@@ -2167,6 +2487,8 @@ export class TeamValidator {
|
|
|
2167
2487
|
// prevents a crash if OMs override `checkCanLearn` to keep validating after an error
|
|
2168
2488
|
setSources.sources = backupSources;
|
|
2169
2489
|
setSources.sourcesBefore = backupSourcesBefore;
|
|
2490
|
+
if (setSources.isFromPokemonGo)
|
|
2491
|
+
return `'s move ${move.name} is incompatible with its Pokemon GO origin.`;
|
|
2170
2492
|
return `'s moves ${(setSources.restrictiveMoves || []).join(', ')} are incompatible.`;
|
|
2171
2493
|
}
|
|
2172
2494
|
if (babyOnly)
|
|
@@ -2179,7 +2501,7 @@ export class TeamValidator {
|
|
|
2179
2501
|
// different learnsets. To prevent a leak, we make them show up as their
|
|
2180
2502
|
// base forme, but hardcode their learnsets into Rockruff-Dusk and
|
|
2181
2503
|
// Greninja-Ash
|
|
2182
|
-
if (['Gastrodon', 'Pumpkaboo', 'Sinistea'].includes(species.baseSpecies) && species.forme) {
|
|
2504
|
+
if (['Gastrodon', 'Pumpkaboo', 'Sinistea', 'Tatsugiri'].includes(species.baseSpecies) && species.forme) {
|
|
2183
2505
|
return this.dex.species.get(species.baseSpecies);
|
|
2184
2506
|
}
|
|
2185
2507
|
else if (species.name === 'Lycanroc-Dusk') {
|