@pkmn/sim 0.5.15 → 0.5.18
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/config/formats.js +39 -57
- package/build/config/formats.js.map +1 -1
- package/build/data/formats-data.js +1 -1
- package/build/data/formats-data.js.map +1 -1
- package/build/data/items.js +1 -3
- package/build/data/items.js.map +1 -1
- package/build/data/learnsets.js +2 -2
- package/build/data/learnsets.js.map +1 -1
- package/build/data/mods/gen1/formats-data.js +1 -1
- package/build/data/mods/gen1/formats-data.js.map +1 -1
- package/build/data/mods/gen1/moves.js +1 -4
- package/build/data/mods/gen1/moves.js.map +1 -1
- package/build/data/mods/gen2/moves.js +0 -3
- package/build/data/mods/gen2/moves.js.map +1 -1
- package/build/data/mods/gen3/moves.js +0 -10
- package/build/data/mods/gen3/moves.js.map +1 -1
- package/build/data/mods/gen3/scripts.js +2 -2
- package/build/data/mods/gen3/scripts.js.map +1 -1
- package/build/data/mods/gen4/moves.js +4 -5
- package/build/data/mods/gen4/moves.js.map +1 -1
- package/build/data/mods/gen4/rulesets.js +2 -1
- package/build/data/mods/gen4/rulesets.js.map +1 -1
- package/build/data/mods/gen5/rulesets.js +2 -1
- package/build/data/mods/gen5/rulesets.js.map +1 -1
- package/build/data/mods/gen7/formats-data.js +4 -4
- package/build/data/mods/gen7/formats-data.js.map +1 -1
- package/build/data/mods/gen7/rulesets.js +2 -1
- package/build/data/mods/gen7/rulesets.js.map +1 -1
- package/build/data/moves.js +59 -79
- package/build/data/moves.js.map +1 -1
- package/build/data/pokedex.js +2 -2
- package/build/data/pokedex.js.map +1 -1
- package/build/data/rulesets.js +3 -4
- package/build/data/rulesets.js.map +1 -1
- package/build/data/text/items.js +1 -0
- package/build/data/text/items.js.map +1 -1
- package/build/data/text/moves.js +5 -3
- package/build/data/text/moves.js.map +1 -1
- package/build/sim/battle-actions.js +12 -4
- package/build/sim/battle-actions.js.map +1 -1
- package/build/sim/battle.js +43 -5
- package/build/sim/battle.js.map +1 -1
- package/build/sim/dex-conditions.d.ts +1 -0
- package/build/sim/dex-conditions.js.map +1 -1
- package/build/sim/dex-moves.d.ts +1 -1
- package/build/sim/exported-global-types.d.ts +1 -0
- package/build/sim/global-types.d.ts +1 -0
- package/build/sim/team-validator.js +15 -4
- package/build/sim/team-validator.js.map +1 -1
- package/build/sim/tools/exhaustive-runner.d.ts +9 -0
- package/build/sim/tools/exhaustive-runner.js +18 -7
- package/build/sim/tools/exhaustive-runner.js.map +1 -1
- package/config/formats.ts +39 -57
- package/data/formats-data.ts +1 -1
- package/data/items.ts +1 -3
- package/data/learnsets.ts +2 -2
- package/data/mods/gen1/formats-data.ts +1 -1
- package/data/mods/gen1/moves.ts +1 -4
- package/data/mods/gen2/moves.ts +0 -3
- package/data/mods/gen3/moves.ts +0 -10
- package/data/mods/gen3/scripts.ts +3 -3
- package/data/mods/gen4/moves.ts +3 -5
- package/data/mods/gen4/rulesets.ts +2 -1
- package/data/mods/gen5/rulesets.ts +2 -1
- package/data/mods/gen7/formats-data.ts +4 -4
- package/data/mods/gen7/rulesets.ts +2 -1
- package/data/moves.ts +59 -70
- package/data/pokedex.ts +2 -2
- package/data/rulesets.ts +3 -6
- package/data/text/items.ts +2 -0
- package/data/text/moves.ts +6 -3
- package/package.json +1 -1
- package/sim/battle-actions.ts +16 -5
- package/sim/battle.ts +42 -5
- package/sim/dex-conditions.ts +1 -0
- package/sim/dex-moves.ts +1 -1
- package/sim/exported-global-types.ts +3 -0
- package/sim/global-types.ts +3 -0
- package/sim/team-validator.ts +16 -4
- package/sim/tools/exhaustive-runner.ts +30 -11
package/data/moves.ts
CHANGED
|
@@ -2689,53 +2689,50 @@ export const Moves: {[moveid: string]: MoveData} = {
|
|
|
2689
2689
|
const offset = this.random(3) + 1;
|
|
2690
2690
|
// the list of all sides in counterclockwise order
|
|
2691
2691
|
const sides = [this.sides[0], this.sides[2]!, this.sides[1], this.sides[3]!];
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
const
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
const
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
this.add('-sideend', sourceSide, effectName, '[silent]');
|
|
2702
|
-
someCondition = true;
|
|
2703
|
-
}
|
|
2692
|
+
const temp: {[k: number]: typeof source.side.sideConditions} = {0: {}, 1: {}, 2: {}, 3: {}};
|
|
2693
|
+
for (const side of sides) {
|
|
2694
|
+
for (const id in side.sideConditions) {
|
|
2695
|
+
if (!sideConditions.includes(id)) continue;
|
|
2696
|
+
temp[side.n][id] = side.sideConditions[id];
|
|
2697
|
+
delete side.sideConditions[id];
|
|
2698
|
+
const effectName = this.dex.conditions.get(id).name;
|
|
2699
|
+
this.add('-sideend', side, effectName, '[silent]');
|
|
2700
|
+
success = true;
|
|
2704
2701
|
}
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
} else {
|
|
2715
|
-
delete side.sideConditions[id];
|
|
2716
|
-
}
|
|
2702
|
+
}
|
|
2703
|
+
for (let i = 0; i < 4; i++) {
|
|
2704
|
+
const sourceSideConditions = temp[sides[i].n];
|
|
2705
|
+
const targetSide = sides[(i + offset) % 4]; // the next side in rotation
|
|
2706
|
+
for (const id in sourceSideConditions) {
|
|
2707
|
+
targetSide.sideConditions[id] = sourceSideConditions[id];
|
|
2708
|
+
const effectName = this.dex.conditions.get(id).name;
|
|
2709
|
+
let layers = sourceSideConditions[id].layers || 1;
|
|
2710
|
+
for (; layers > 0; layers--) this.add('-sidestart', targetSide, effectName, '[silent]');
|
|
2717
2711
|
}
|
|
2718
|
-
success = true;
|
|
2719
2712
|
}
|
|
2720
2713
|
} else {
|
|
2721
|
-
const
|
|
2722
|
-
const
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2731
|
-
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
continue;
|
|
2736
|
-
}
|
|
2714
|
+
const sourceSideConditions = source.side.sideConditions;
|
|
2715
|
+
const targetSideConditions = source.side.foe.sideConditions;
|
|
2716
|
+
const sourceTemp: typeof sourceSideConditions = {};
|
|
2717
|
+
const targetTemp: typeof targetSideConditions = {};
|
|
2718
|
+
for (const id in sourceSideConditions) {
|
|
2719
|
+
if (!sideConditions.includes(id)) continue;
|
|
2720
|
+
sourceTemp[id] = sourceSideConditions[id];
|
|
2721
|
+
delete sourceSideConditions[id];
|
|
2722
|
+
success = true;
|
|
2723
|
+
}
|
|
2724
|
+
for (const id in targetSideConditions) {
|
|
2725
|
+
if (!sideConditions.includes(id)) continue;
|
|
2726
|
+
targetTemp[id] = targetSideConditions[id];
|
|
2727
|
+
delete targetSideConditions[id];
|
|
2737
2728
|
success = true;
|
|
2738
2729
|
}
|
|
2730
|
+
for (const id in sourceTemp) {
|
|
2731
|
+
targetSideConditions[id] = sourceTemp[id];
|
|
2732
|
+
}
|
|
2733
|
+
for (const id in targetTemp) {
|
|
2734
|
+
sourceSideConditions[id] = targetTemp[id];
|
|
2735
|
+
}
|
|
2739
2736
|
this.add('-swapsideconditions');
|
|
2740
2737
|
}
|
|
2741
2738
|
if (!success) return false;
|
|
@@ -4799,7 +4796,6 @@ export const Moves: {[moveid: string]: MoveData} = {
|
|
|
4799
4796
|
pp: 5,
|
|
4800
4797
|
priority: 0,
|
|
4801
4798
|
flags: {protect: 1},
|
|
4802
|
-
selfdestruct: "ifHit",
|
|
4803
4799
|
secondary: null,
|
|
4804
4800
|
target: "normal",
|
|
4805
4801
|
type: "Fighting",
|
|
@@ -6720,7 +6716,7 @@ export const Moves: {[moveid: string]: MoveData} = {
|
|
|
6720
6716
|
onSideStart(side) {
|
|
6721
6717
|
this.add('-sidestart', side, 'move: G-Max Steelsurge');
|
|
6722
6718
|
},
|
|
6723
|
-
|
|
6719
|
+
onEntryHazard(pokemon) {
|
|
6724
6720
|
if (pokemon.hasItem('heavydutyboots')) return;
|
|
6725
6721
|
// Ice Face and Disguise correctly get typed damage from Stealth Rock
|
|
6726
6722
|
// because Stealth Rock bypasses Substitute.
|
|
@@ -7820,10 +7816,10 @@ export const Moves: {[moveid: string]: MoveData} = {
|
|
|
7820
7816
|
priority: 0,
|
|
7821
7817
|
flags: {snatch: 1, heal: 1},
|
|
7822
7818
|
onHit(target, source, move) {
|
|
7823
|
-
if (!this.canSwitch(
|
|
7819
|
+
if (!this.canSwitch(source.side)) {
|
|
7824
7820
|
delete move.selfdestruct;
|
|
7825
7821
|
this.attrLastMove('[still]');
|
|
7826
|
-
this.add('-fail',
|
|
7822
|
+
this.add('-fail', source);
|
|
7827
7823
|
return this.NOT_FAIL;
|
|
7828
7824
|
}
|
|
7829
7825
|
},
|
|
@@ -9916,10 +9912,10 @@ export const Moves: {[moveid: string]: MoveData} = {
|
|
|
9916
9912
|
priority: 0,
|
|
9917
9913
|
flags: {snatch: 1, heal: 1, dance: 1},
|
|
9918
9914
|
onHit(target, source, move) {
|
|
9919
|
-
if (!this.canSwitch(
|
|
9915
|
+
if (!this.canSwitch(source.side)) {
|
|
9920
9916
|
delete move.selfdestruct;
|
|
9921
9917
|
this.attrLastMove('[still]');
|
|
9922
|
-
this.add('-fail',
|
|
9918
|
+
this.add('-fail', source);
|
|
9923
9919
|
return this.NOT_FAIL;
|
|
9924
9920
|
}
|
|
9925
9921
|
},
|
|
@@ -11063,26 +11059,21 @@ export const Moves: {[moveid: string]: MoveData} = {
|
|
|
11063
11059
|
priority: 0,
|
|
11064
11060
|
flags: {},
|
|
11065
11061
|
noMetronome: [
|
|
11066
|
-
"After You", "Apple Acid", "Assist", "Astral Barrage", "Aura Wheel", "Baneful Bunker", "Beak Blast", "Behemoth Bash", "Behemoth Blade", "Belch", "Bestow", "Body Press", "Branch Poke", "Breaking Swipe", "Celebrate", "Chatter", "Clangorous Soul", "Copycat", "Counter", "Covet", "Crafty Shield", "Decorate", "Destiny Bond", "Detect", "Diamond Storm", "Double Iron Bash", "Dragon Ascent", "Dragon Energy", "Drum Beating", "Dynamax Cannon", "Endure", "Eternabeam", "False Surrender", "Feint", "Fiery Wrath", "Fleur Cannon", "Focus Punch", "Follow Me", "Freeze Shock", "Freezing Glare", "Glacial Lance", "Grav Apple", "Helping Hand", "Hold Hands", "Hyperspace Fury", "Hyperspace Hole", "Ice Burn", "Instruct", "Jungle Healing", "King's Shield", "Life Dew", "Light of Ruin", "Mat Block", "Me First", "Meteor Assault", "Metronome", "Mimic", "Mind Blown", "Mirror Coat", "Mirror Move", "Moongeist Beam", "Nature Power", "Nature's Madness", "Obstruct", "Origin Pulse", "Overdrive", "Photon Geyser", "Plasma Fists", "Precipice Blades", "Protect", "Pyro Ball", "Quash", "Quick Guard", "Rage Powder", "Relic Song", "Secret Sword", "Shell Trap", "Sketch", "Sleep Talk", "Snap Trap", "Snarl", "Snatch", "Snore", "Spectral Thief", "Spiky Shield", "Spirit Break", "Spotlight", "Steam Eruption", "Steel Beam", "Strange Steam", "Struggle", "Sunsteel Strike", "Surging Strikes", "Switcheroo", "Techno Blast", "Thief", "Thousand Arrows", "Thousand Waves", "Thunder Cage", "Thunderous Kick", "Transform", "Trick", "V-create", "Wicked Blow", "Wide Guard",
|
|
11062
|
+
"After You", "Apple Acid", "Assist", "Astral Barrage", "Aura Wheel", "Baneful Bunker", "Beak Blast", "Behemoth Bash", "Behemoth Blade", "Belch", "Bestow", "Body Press", "Branch Poke", "Breaking Swipe", "Celebrate", "Chatter", "Clangorous Soul", "Copycat", "Counter", "Covet", "Crafty Shield", "Decorate", "Destiny Bond", "Detect", "Diamond Storm", "Double Iron Bash", "Dragon Ascent", "Dragon Energy", "Dragon Hammer", "Drum Beating", "Dynamax Cannon", "Endure", "Eternabeam", "False Surrender", "Feint", "Fiery Wrath", "Fleur Cannon", "Focus Punch", "Follow Me", "Freeze Shock", "Freezing Glare", "Glacial Lance", "Grav Apple", "Helping Hand", "Hold Hands", "Hyperspace Fury", "Hyperspace Hole", "Ice Burn", "Instruct", "Jungle Healing", "King's Shield", "Life Dew", "Light of Ruin", "Mat Block", "Me First", "Meteor Assault", "Metronome", "Mimic", "Mind Blown", "Mirror Coat", "Mirror Move", "Moongeist Beam", "Nature Power", "Nature's Madness", "Obstruct", "Origin Pulse", "Overdrive", "Photon Geyser", "Plasma Fists", "Precipice Blades", "Protect", "Pyro Ball", "Quash", "Quick Guard", "Rage Powder", "Relic Song", "Secret Sword", "Shell Trap", "Sketch", "Sleep Talk", "Snap Trap", "Snarl", "Snatch", "Snore", "Spectral Thief", "Spiky Shield", "Spirit Break", "Spotlight", "Steam Eruption", "Steel Beam", "Strange Steam", "Struggle", "Sunsteel Strike", "Surging Strikes", "Switcheroo", "Techno Blast", "Thief", "Thousand Arrows", "Thousand Waves", "Thunder Cage", "Thunderous Kick", "Transform", "Trick", "V-create", "Wicked Blow", "Wide Guard",
|
|
11067
11063
|
],
|
|
11068
11064
|
onHit(target, source, effect) {
|
|
11069
|
-
const moves
|
|
11070
|
-
|
|
11071
|
-
|
|
11072
|
-
|
|
11073
|
-
|
|
11074
|
-
|
|
11075
|
-
if (this.dex.moves.get(id).gen > this.gen) continue;
|
|
11076
|
-
moves.push(move);
|
|
11077
|
-
}
|
|
11065
|
+
const moves = this.dex.moves.all().filter(move => (
|
|
11066
|
+
(![2, 4].includes(this.gen) || !source.moves.includes(move.id)) &&
|
|
11067
|
+
!move.realMove && !move.isZ && !move.isMax &&
|
|
11068
|
+
(!move.isNonstandard || move.isNonstandard === 'Unobtainable') &&
|
|
11069
|
+
!effect.noMetronome!.includes(move.name)
|
|
11070
|
+
));
|
|
11078
11071
|
let randomMove = '';
|
|
11079
11072
|
if (moves.length) {
|
|
11080
|
-
moves.sort((a, b) => a.num
|
|
11081
|
-
randomMove = this.sample(moves).
|
|
11082
|
-
}
|
|
11083
|
-
if (!randomMove) {
|
|
11084
|
-
return false;
|
|
11073
|
+
moves.sort((a, b) => a.num - b.num);
|
|
11074
|
+
randomMove = this.sample(moves).id;
|
|
11085
11075
|
}
|
|
11076
|
+
if (!randomMove) return false;
|
|
11086
11077
|
this.actions.useMove(randomMove, target);
|
|
11087
11078
|
},
|
|
11088
11079
|
secondary: null,
|
|
@@ -16412,9 +16403,8 @@ export const Moves: {[moveid: string]: MoveData} = {
|
|
|
16412
16403
|
this.add('-sidestart', side, 'Spikes');
|
|
16413
16404
|
this.effectState.layers++;
|
|
16414
16405
|
},
|
|
16415
|
-
|
|
16416
|
-
if (!pokemon.isGrounded()) return;
|
|
16417
|
-
if (pokemon.hasItem('heavydutyboots')) return;
|
|
16406
|
+
onEntryHazard(pokemon) {
|
|
16407
|
+
if (!pokemon.isGrounded() || pokemon.hasItem('heavydutyboots')) return;
|
|
16418
16408
|
const damageAmounts = [0, 3, 4, 6]; // 1/8, 1/6, 1/4
|
|
16419
16409
|
this.damage(damageAmounts[this.effectState.layers] * pokemon.maxhp / 24);
|
|
16420
16410
|
},
|
|
@@ -16697,7 +16687,7 @@ export const Moves: {[moveid: string]: MoveData} = {
|
|
|
16697
16687
|
onSideStart(side) {
|
|
16698
16688
|
this.add('-sidestart', side, 'move: Stealth Rock');
|
|
16699
16689
|
},
|
|
16700
|
-
|
|
16690
|
+
onEntryHazard(pokemon) {
|
|
16701
16691
|
if (pokemon.hasItem('heavydutyboots')) return;
|
|
16702
16692
|
const typeMod = this.clampIntRange(pokemon.runEffectiveness(this.dex.getActiveMove('stealthrock')), -6, 6);
|
|
16703
16693
|
this.damage(pokemon.maxhp * Math.pow(2, typeMod) / 8);
|
|
@@ -16821,9 +16811,8 @@ export const Moves: {[moveid: string]: MoveData} = {
|
|
|
16821
16811
|
onSideStart(side) {
|
|
16822
16812
|
this.add('-sidestart', side, 'move: Sticky Web');
|
|
16823
16813
|
},
|
|
16824
|
-
|
|
16825
|
-
if (!pokemon.isGrounded()) return;
|
|
16826
|
-
if (pokemon.hasItem('heavydutyboots')) return;
|
|
16814
|
+
onEntryHazard(pokemon) {
|
|
16815
|
+
if (!pokemon.isGrounded() || pokemon.hasItem('heavydutyboots')) return;
|
|
16827
16816
|
this.add('-activate', pokemon, 'move: Sticky Web');
|
|
16828
16817
|
this.boost({spe: -1}, pokemon, this.effectState.source, this.dex.getActiveMove('stickyweb'));
|
|
16829
16818
|
},
|
|
@@ -18427,7 +18416,7 @@ export const Moves: {[moveid: string]: MoveData} = {
|
|
|
18427
18416
|
this.add('-sidestart', side, 'move: Toxic Spikes');
|
|
18428
18417
|
this.effectState.layers++;
|
|
18429
18418
|
},
|
|
18430
|
-
|
|
18419
|
+
onEntryHazard(pokemon) {
|
|
18431
18420
|
if (!pokemon.isGrounded()) return;
|
|
18432
18421
|
if (pokemon.hasType('Poison')) {
|
|
18433
18422
|
this.add('-sideend', pokemon.side, 'move: Toxic Spikes', '[of] ' + pokemon);
|
package/data/pokedex.ts
CHANGED
|
@@ -16034,7 +16034,7 @@ export const Pokedex: {[speciesid: string]: SpeciesData} = {
|
|
|
16034
16034
|
color: "Blue",
|
|
16035
16035
|
eggGroups: ["Undiscovered"],
|
|
16036
16036
|
requiredItem: "Rusted Sword",
|
|
16037
|
-
|
|
16037
|
+
battleOnly: "Zacian",
|
|
16038
16038
|
cannotDynamax: true,
|
|
16039
16039
|
},
|
|
16040
16040
|
zamazenta: {
|
|
@@ -16068,7 +16068,7 @@ export const Pokedex: {[speciesid: string]: SpeciesData} = {
|
|
|
16068
16068
|
color: "Red",
|
|
16069
16069
|
eggGroups: ["Undiscovered"],
|
|
16070
16070
|
requiredItem: "Rusted Shield",
|
|
16071
|
-
|
|
16071
|
+
battleOnly: "Zamazenta",
|
|
16072
16072
|
cannotDynamax: true,
|
|
16073
16073
|
},
|
|
16074
16074
|
eternatus: {
|
package/data/rulesets.ts
CHANGED
|
@@ -446,7 +446,8 @@ export const Rulesets: {[k: string]: FormatData} = {
|
|
|
446
446
|
this.add('clearpoke');
|
|
447
447
|
for (const pokemon of this.getAllPokemon()) {
|
|
448
448
|
const details = pokemon.details.replace(', shiny', '')
|
|
449
|
-
.replace(/(Arceus|Gourgeist|Pumpkaboo|Xerneas|Silvally|
|
|
449
|
+
.replace(/(Arceus|Gourgeist|Pumpkaboo|Xerneas|Silvally|Urshifu)(-[a-zA-Z?-]+)?/g, '$1-*')
|
|
450
|
+
.replace(/(Zacian|Zamazenta)(?!-Crowned)/g, '$1-*'); // Hacked-in Crowned formes will be revealed
|
|
450
451
|
this.add('poke', pokemon.side.id, details, '');
|
|
451
452
|
}
|
|
452
453
|
this.makeRequest('teampreview');
|
|
@@ -1146,11 +1147,7 @@ export const Rulesets: {[k: string]: FormatData} = {
|
|
|
1146
1147
|
if (prevo.evos.includes(formeName)) continue;
|
|
1147
1148
|
}
|
|
1148
1149
|
const forme = dex.species.get(formeName);
|
|
1149
|
-
if (
|
|
1150
|
-
forme.changesFrom === originalForme.name && !forme.battleOnly &&
|
|
1151
|
-
// Temporary workaround
|
|
1152
|
-
forme.forme !== 'Crowned'
|
|
1153
|
-
) {
|
|
1150
|
+
if (forme.changesFrom === originalForme.name && !forme.battleOnly) {
|
|
1154
1151
|
speciesTypes.push(...forme.types);
|
|
1155
1152
|
}
|
|
1156
1153
|
}
|
package/data/text/items.ts
CHANGED
|
@@ -429,6 +429,8 @@ export const ItemsText: {[k: string]: ItemText} = {
|
|
|
429
429
|
ejectpack: {
|
|
430
430
|
name: "Eject Pack",
|
|
431
431
|
desc: "If the holder's stat stages are lowered, it switches to a chosen ally. Single use.",
|
|
432
|
+
|
|
433
|
+
end: " [POKEMON] is switched out by the Eject Pack!",
|
|
432
434
|
},
|
|
433
435
|
electirizer: {
|
|
434
436
|
name: "Electirizer",
|
package/data/text/moves.ts
CHANGED
|
@@ -1934,6 +1934,8 @@ export const MovesText: {[k: string]: MoveText} = {
|
|
|
1934
1934
|
name: "Flip Turn",
|
|
1935
1935
|
desc: "If this move is successful and the user has not fainted, the user switches out even if it is trapped and is replaced immediately by a selected party member. The user does not switch out if there are no unfainted party members, or if the target switched out using an Eject Button or through the effect of the Emergency Exit or Wimp Out Abilities.",
|
|
1936
1936
|
shortDesc: "User switches out after damaging the target.",
|
|
1937
|
+
|
|
1938
|
+
switchOut: "#uturn",
|
|
1937
1939
|
},
|
|
1938
1940
|
floatyfall: {
|
|
1939
1941
|
name: "Floaty Fall",
|
|
@@ -3695,7 +3697,7 @@ export const MovesText: {[k: string]: MoveText} = {
|
|
|
3695
3697
|
},
|
|
3696
3698
|
metronome: {
|
|
3697
3699
|
name: "Metronome",
|
|
3698
|
-
desc: "A random move is selected for use, other than After You, Apple Acid, Assist, Astral Barrage, Aura Wheel, Baneful Bunker, Beak Blast, Behemoth Bash, Behemoth Blade, Belch, Bestow, Body Press, Branch Poke, Breaking Swipe, Celebrate, Chatter, Clangorous Soul, Copycat, Counter, Covet, Crafty Shield, Decorate, Destiny Bond, Detect, Diamond Storm, Double Iron Bash, Dragon Ascent, Dragon Energy, Drum Beating, Dynamax Cannon, Endure, Eternabeam, False Surrender, Feint, Fiery Wrath, Fleur Cannon, Focus Punch, Follow Me, Freeze Shock, Freezing Glare, Glacial Lance, Grav Apple, Helping Hand, Hold Hands, Hyperspace Fury, Hyperspace Hole, Ice Burn, Instruct, Jungle Healing, King's Shield, Life Dew, Light of Ruin, Mat Block, Me First, Meteor Assault, Metronome, Mimic, Mind Blown, Mirror Coat, Mirror Move, Moongeist Beam, Nature Power, Nature's Madness, Obstruct, Origin Pulse, Overdrive, Photon Geyser, Plasma Fists, Precipice Blades, Protect, Pyro Ball, Quash, Quick Guard, Rage Powder, Relic Song, Secret Sword, Shell Trap, Sketch, Sleep Talk, Snap Trap, Snarl, Snatch, Snore, Spectral Thief, Spiky Shield, Spirit Break, Spotlight, Steam Eruption, Steel Beam, Strange Steam, Struggle, Sunsteel Strike, Surging Strikes, Switcheroo, Techno Blast, Thief, Thousand Arrows, Thousand Waves, Thunder Cage, Thunderous Kick, Transform, Trick, V-create, Wicked Blow, or Wide Guard.",
|
|
3700
|
+
desc: "A random move is selected for use, other than After You, Apple Acid, Assist, Astral Barrage, Aura Wheel, Baneful Bunker, Beak Blast, Behemoth Bash, Behemoth Blade, Belch, Bestow, Body Press, Branch Poke, Breaking Swipe, Celebrate, Chatter, Clangorous Soul, Copycat, Counter, Covet, Crafty Shield, Decorate, Destiny Bond, Detect, Diamond Storm, Double Iron Bash, Dragon Ascent, Dragon Energy, Dragon Hammer, Drum Beating, Dynamax Cannon, Endure, Eternabeam, False Surrender, Feint, Fiery Wrath, Fleur Cannon, Focus Punch, Follow Me, Freeze Shock, Freezing Glare, Glacial Lance, Grav Apple, Helping Hand, Hold Hands, Hyperspace Fury, Hyperspace Hole, Ice Burn, Instruct, Jungle Healing, King's Shield, Life Dew, Light of Ruin, Mat Block, Me First, Meteor Assault, Metronome, Mimic, Mind Blown, Mirror Coat, Mirror Move, Moongeist Beam, Nature Power, Nature's Madness, Obstruct, Origin Pulse, Overdrive, Photon Geyser, Plasma Fists, Precipice Blades, Protect, Pyro Ball, Quash, Quick Guard, Rage Powder, Relic Song, Secret Sword, Shell Trap, Sketch, Sleep Talk, Snap Trap, Snarl, Snatch, Snore, Spectral Thief, Spiky Shield, Spirit Break, Spotlight, Steam Eruption, Steel Beam, Strange Steam, Struggle, Sunsteel Strike, Surging Strikes, Switcheroo, Techno Blast, Thief, Thousand Arrows, Thousand Waves, Thunder Cage, Thunderous Kick, Transform, Trick, V-create, Wicked Blow, or Wide Guard.",
|
|
3699
3701
|
shortDesc: "Picks a random move.",
|
|
3700
3702
|
gen7: {
|
|
3701
3703
|
desc: "A random move is selected for use, other than After You, Assist, Baneful Bunker, Beak Blast, Belch, Bestow, Celebrate, Chatter, Copycat, Counter, Covet, Crafty Shield, Destiny Bond, Detect, Diamond Storm, Dragon Ascent, Endure, Feint, Fleur Cannon, Focus Punch, Follow Me, Freeze Shock, Helping Hand, Hold Hands, Hyperspace Fury, Hyperspace Hole, Ice Burn, Instruct, King's Shield, Light of Ruin, Mat Block, Me First, Metronome, Mimic, Mind Blown, Mirror Coat, Mirror Move, Nature Power, Origin Pulse, Photon Geyser, Plasma Fists, Precipice Blades, Protect, Quash, Quick Guard, Rage Powder, Relic Song, Secret Sword, Shell Trap, Sketch, Sleep Talk, Snarl, Snatch, Snore, Spectral Thief, Spiky Shield, Spotlight, Steam Eruption, Struggle, Switcheroo, Techno Blast, Thief, Thousand Arrows, Thousand Waves, Transform, Trick, V-create, or Wide Guard.",
|
|
@@ -3707,13 +3709,13 @@ export const MovesText: {[k: string]: MoveText} = {
|
|
|
3707
3709
|
desc: "A random move is selected for use, other than After You, Assist, Bestow, Chatter, Copycat, Counter, Covet, Destiny Bond, Detect, Endure, Feint, Focus Punch, Follow Me, Freeze Shock, Helping Hand, Ice Burn, Me First, Metronome, Mimic, Mirror Coat, Mirror Move, Nature Power, Protect, Quash, Quick Guard, Rage Powder, Relic Song, Secret Sword, Sketch, Sleep Talk, Snarl, Snatch, Snore, Struggle, Switcheroo, Techno Blast, Thief, Transform, Trick, V-create, or Wide Guard.",
|
|
3708
3710
|
},
|
|
3709
3711
|
gen4: {
|
|
3710
|
-
desc: "A random move is selected for use, other than Assist, Chatter, Copycat, Counter, Covet, Destiny Bond, Detect, Endure, Feint, Focus Punch, Follow Me, Helping Hand, Me First, Metronome, Mimic, Mirror Coat, Mirror Move, Protect, Sketch, Sleep Talk, Snatch, Struggle, Switcheroo, Thief, or
|
|
3712
|
+
desc: "A random move is selected for use, other than Assist, Chatter, Copycat, Counter, Covet, Destiny Bond, Detect, Endure, Feint, Focus Punch, Follow Me, Helping Hand, Me First, Metronome, Mimic, Mirror Coat, Mirror Move, Protect, Sketch, Sleep Talk, Snatch, Struggle, Switcheroo, Thief, Trick, or any move the user already knows.",
|
|
3711
3713
|
},
|
|
3712
3714
|
gen3: {
|
|
3713
3715
|
desc: "A random move is selected for use, other than Counter, Covet, Destiny Bond, Detect, Endure, Focus Punch, Follow Me, Helping Hand, Metronome, Mimic, Mirror Coat, Protect, Sketch, Sleep Talk, Snatch, Struggle, Thief, or Trick.",
|
|
3714
3716
|
},
|
|
3715
3717
|
gen2: {
|
|
3716
|
-
desc: "A random move is selected for use, other than Counter, Destiny Bond, Detect, Endure, Metronome, Mimic, Mirror Coat, Protect, Sketch, Sleep Talk, Struggle, or
|
|
3718
|
+
desc: "A random move is selected for use, other than Counter, Destiny Bond, Detect, Endure, Metronome, Mimic, Mirror Coat, Protect, Sketch, Sleep Talk, Struggle, Thief, or any move the user already knows.",
|
|
3717
3719
|
},
|
|
3718
3720
|
gen1: {
|
|
3719
3721
|
desc: "A random move is selected for use, other than Metronome or Struggle.",
|
|
@@ -4167,6 +4169,7 @@ export const MovesText: {[k: string]: MoveText} = {
|
|
|
4167
4169
|
},
|
|
4168
4170
|
|
|
4169
4171
|
heal: "#memento",
|
|
4172
|
+
switchOut: "#uturn",
|
|
4170
4173
|
},
|
|
4171
4174
|
payback: {
|
|
4172
4175
|
name: "Payback",
|
package/package.json
CHANGED
package/sim/battle-actions.ts
CHANGED
|
@@ -133,6 +133,8 @@ export class BattleActions {
|
|
|
133
133
|
oldActive.isActive = false;
|
|
134
134
|
oldActive.isStarted = false;
|
|
135
135
|
oldActive.usedItemThisTurn = false;
|
|
136
|
+
oldActive.statsRaisedThisTurn = false;
|
|
137
|
+
oldActive.statsLoweredThisTurn = false;
|
|
136
138
|
oldActive.position = pokemon.position;
|
|
137
139
|
pokemon.position = pos;
|
|
138
140
|
side.pokemon[pokemon.position] = pokemon;
|
|
@@ -178,7 +180,17 @@ export class BattleActions {
|
|
|
178
180
|
}
|
|
179
181
|
runSwitch(pokemon: Pokemon) {
|
|
180
182
|
this.battle.runEvent('Swap', pokemon);
|
|
181
|
-
|
|
183
|
+
|
|
184
|
+
if (this.battle.gen >= 5) {
|
|
185
|
+
this.battle.runEvent('SwitchIn', pokemon);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
this.battle.runEvent('EntryHazard', pokemon);
|
|
189
|
+
|
|
190
|
+
if (this.battle.gen <= 4) {
|
|
191
|
+
this.battle.runEvent('SwitchIn', pokemon);
|
|
192
|
+
}
|
|
193
|
+
|
|
182
194
|
if (this.battle.gen <= 2 && !pokemon.side.faintedThisTurn && pokemon.draggedIn !== this.battle.turn) {
|
|
183
195
|
this.battle.runEvent('AfterSwitchInSelf', pokemon);
|
|
184
196
|
}
|
|
@@ -1124,9 +1136,6 @@ export class BattleActions {
|
|
|
1124
1136
|
continue;
|
|
1125
1137
|
}
|
|
1126
1138
|
damage[i] = curDamage;
|
|
1127
|
-
if (move.selfdestruct === 'ifHit') {
|
|
1128
|
-
this.battle.faint(source, source, move);
|
|
1129
|
-
}
|
|
1130
1139
|
}
|
|
1131
1140
|
return damage;
|
|
1132
1141
|
}
|
|
@@ -1230,6 +1239,9 @@ export class BattleActions {
|
|
|
1230
1239
|
}
|
|
1231
1240
|
}
|
|
1232
1241
|
}
|
|
1242
|
+
if (moveData.selfdestruct === 'ifHit' && damage[i] !== false) {
|
|
1243
|
+
this.battle.faint(source, source, move);
|
|
1244
|
+
}
|
|
1233
1245
|
if (moveData.selfSwitch) {
|
|
1234
1246
|
if (this.battle.canSwitch(source.side)) {
|
|
1235
1247
|
didSomething = true;
|
|
@@ -1243,7 +1255,6 @@ export class BattleActions {
|
|
|
1243
1255
|
didAnything = this.combineResults(didAnything, didSomething);
|
|
1244
1256
|
}
|
|
1245
1257
|
|
|
1246
|
-
|
|
1247
1258
|
if (!didAnything && didAnything !== 0 && !moveData.self && !moveData.selfdestruct) {
|
|
1248
1259
|
if (!isSelf && !isSecondary) {
|
|
1249
1260
|
if (didAnything === false) {
|
package/sim/battle.ts
CHANGED
|
@@ -711,7 +711,7 @@ export class Battle {
|
|
|
711
711
|
}
|
|
712
712
|
}
|
|
713
713
|
|
|
714
|
-
if (
|
|
714
|
+
if (['Invulnerability', 'TryHit', 'DamagingHit', 'EntryHazard'].includes(eventid)) {
|
|
715
715
|
handlers.sort(Battle.compareLeftToRightOrder);
|
|
716
716
|
} else if (fastExit) {
|
|
717
717
|
handlers.sort(Battle.compareRedirectOrder);
|
|
@@ -2263,11 +2263,11 @@ export class Battle {
|
|
|
2263
2263
|
if (this.activePerHalf > 2) {
|
|
2264
2264
|
if (move.target === 'adjacentFoe' || move.target === 'normal' || move.target === 'randomNormal') {
|
|
2265
2265
|
// even if a move can target an ally, auto-resolution will never make it target an ally
|
|
2266
|
-
// i.e. if both your opponents faint before you use Flamethrower, it will fail instead of targeting your
|
|
2266
|
+
// i.e. if both your opponents faint before you use Flamethrower, it will fail instead of targeting your ally
|
|
2267
2267
|
const adjacentFoes = pokemon.adjacentFoes();
|
|
2268
2268
|
if (adjacentFoes.length) return this.sample(adjacentFoes);
|
|
2269
|
-
// no valid target at all, return
|
|
2270
|
-
return pokemon.side.foe.active[
|
|
2269
|
+
// no valid target at all, return slot directly across for any possible redirection
|
|
2270
|
+
return pokemon.side.foe.active[pokemon.side.foe.active.length - 1 - pokemon.position];
|
|
2271
2271
|
}
|
|
2272
2272
|
}
|
|
2273
2273
|
return pokemon.side.randomFoe() || pokemon.side.foe.active[0];
|
|
@@ -2413,6 +2413,43 @@ export class Battle {
|
|
|
2413
2413
|
|
|
2414
2414
|
this.add('start');
|
|
2415
2415
|
|
|
2416
|
+
// Change Zacian/Zamazenta into their Crowned formes
|
|
2417
|
+
for (const pokemon of this.getAllPokemon()) {
|
|
2418
|
+
let rawSpecies: Species | null = null;
|
|
2419
|
+
if (pokemon.species.id === 'zacian' && pokemon.item === 'rustedsword') {
|
|
2420
|
+
rawSpecies = this.dex.species.get('Zacian-Crowned');
|
|
2421
|
+
} else if (pokemon.species.id === 'zamazenta' && pokemon.item === 'rustedshield') {
|
|
2422
|
+
rawSpecies = this.dex.species.get('Zamazenta-Crowned');
|
|
2423
|
+
}
|
|
2424
|
+
if (!rawSpecies) continue;
|
|
2425
|
+
const species = pokemon.setSpecies(rawSpecies);
|
|
2426
|
+
if (!species) continue;
|
|
2427
|
+
pokemon.baseSpecies = rawSpecies;
|
|
2428
|
+
pokemon.details = species.name + (pokemon.level === 100 ? '' : ', L' + pokemon.level) +
|
|
2429
|
+
(pokemon.gender === '' ? '' : ', ' + pokemon.gender) + (pokemon.set.shiny ? ', shiny' : '');
|
|
2430
|
+
pokemon.setAbility(species.abilities['0'], null, true);
|
|
2431
|
+
pokemon.baseAbility = pokemon.ability;
|
|
2432
|
+
|
|
2433
|
+
const behemothMove: {[k: string]: string} = {
|
|
2434
|
+
'Zacian-Crowned': 'behemothblade', 'Zamazenta-Crowned': 'behemothbash',
|
|
2435
|
+
};
|
|
2436
|
+
const ironHead = pokemon.baseMoves.indexOf('ironhead');
|
|
2437
|
+
if (ironHead >= 0) {
|
|
2438
|
+
const move = this.dex.moves.get(behemothMove[rawSpecies.name]);
|
|
2439
|
+
pokemon.baseMoveSlots[ironHead] = {
|
|
2440
|
+
move: move.name,
|
|
2441
|
+
id: move.id,
|
|
2442
|
+
pp: (move.noPPBoosts || move.isZ) ? move.pp : move.pp * 8 / 5,
|
|
2443
|
+
maxpp: (move.noPPBoosts || move.isZ) ? move.pp : move.pp * 8 / 5,
|
|
2444
|
+
target: move.target,
|
|
2445
|
+
disabled: false,
|
|
2446
|
+
disabledSource: '',
|
|
2447
|
+
used: false,
|
|
2448
|
+
};
|
|
2449
|
+
pokemon.moveSlots = pokemon.baseMoveSlots.slice();
|
|
2450
|
+
}
|
|
2451
|
+
}
|
|
2452
|
+
|
|
2416
2453
|
if (this.format.onBattleStart) this.format.onBattleStart.call(this);
|
|
2417
2454
|
for (const rule of this.ruleTable.keys()) {
|
|
2418
2455
|
if ('+*-!'.includes(rule.charAt(0))) continue;
|
|
@@ -2624,7 +2661,7 @@ export class Battle {
|
|
|
2624
2661
|
|
|
2625
2662
|
if (this.gen < 5) this.eachEvent('Update');
|
|
2626
2663
|
|
|
2627
|
-
if (this.gen >= 8 && this.queue.peek()?.choice === 'move') {
|
|
2664
|
+
if (this.gen >= 8 && (this.queue.peek()?.choice === 'move' || this.queue.peek()?.choice === 'runDynamax')) {
|
|
2628
2665
|
// In gen 8, speed is updated dynamically so update the queue's speed properties and sort it.
|
|
2629
2666
|
this.updateSpeed();
|
|
2630
2667
|
for (const queueAction of this.queue.list) {
|
package/sim/dex-conditions.ts
CHANGED
|
@@ -53,6 +53,7 @@ export interface EventMethods {
|
|
|
53
53
|
onDragOut?: (this: Battle, pokemon: Pokemon, source?: Pokemon, move?: ActiveMove) => void;
|
|
54
54
|
onEatItem?: (this: Battle, item: Item, pokemon: Pokemon) => void;
|
|
55
55
|
onEffectiveness?: MoveEventMethods['onEffectiveness'];
|
|
56
|
+
onEntryHazard?: (this: Battle, pokemon: Pokemon) => void;
|
|
56
57
|
onFaint?: CommonHandlers['VoidEffect'];
|
|
57
58
|
onFlinch?: ((this: Battle, pokemon: Pokemon) => boolean | void) | boolean;
|
|
58
59
|
onFractionalPriority?: CommonHandlers['ModifierSourceMove'] | -0.1;
|
package/sim/dex-moves.ts
CHANGED
|
@@ -196,7 +196,7 @@ export interface MoveData extends EffectData, MoveEventMethods, HitEffect {
|
|
|
196
196
|
forceSwitch?: boolean;
|
|
197
197
|
selfSwitch?: string | boolean;
|
|
198
198
|
selfBoost?: {boosts?: SparseBoostsTable};
|
|
199
|
-
selfdestruct?:
|
|
199
|
+
selfdestruct?: 'always' | 'ifHit' | boolean;
|
|
200
200
|
breaksProtect?: boolean;
|
|
201
201
|
/**
|
|
202
202
|
* Note that this is only "true" recoil. Other self-damage, like Struggle,
|
|
@@ -312,6 +312,9 @@ export interface ModdedBattlePokemon {
|
|
|
312
312
|
clearBoosts?: (this: Pokemon) => void;
|
|
313
313
|
calculateStat?: (this: Pokemon, statName: StatIDExceptHP, boost: number, modifier?: number) => number;
|
|
314
314
|
cureStatus?: (this: Pokemon, silent?: boolean) => boolean;
|
|
315
|
+
formeChange?: (
|
|
316
|
+
this: Pokemon, speciesId: string | Species, source: Effect, isPermanent?: boolean, message?: string
|
|
317
|
+
) => boolean;
|
|
315
318
|
getAbility?: (this: Pokemon) => Ability;
|
|
316
319
|
getActionSpeed?: (this: Pokemon) => number;
|
|
317
320
|
getItem?: (this: Pokemon) => Item;
|
package/sim/global-types.ts
CHANGED
|
@@ -312,6 +312,9 @@ interface ModdedBattlePokemon {
|
|
|
312
312
|
clearBoosts?: (this: Pokemon) => void;
|
|
313
313
|
calculateStat?: (this: Pokemon, statName: StatIDExceptHP, boost: number, modifier?: number) => number;
|
|
314
314
|
cureStatus?: (this: Pokemon, silent?: boolean) => boolean;
|
|
315
|
+
formeChange?: (
|
|
316
|
+
this: Pokemon, speciesId: string | Species, source: Effect, isPermanent?: boolean, message?: string
|
|
317
|
+
) => boolean;
|
|
315
318
|
getAbility?: (this: Pokemon) => Ability;
|
|
316
319
|
getActionSpeed?: (this: Pokemon) => number;
|
|
317
320
|
getItem?: (this: Pokemon) => Item;
|
package/sim/team-validator.ts
CHANGED
|
@@ -543,6 +543,10 @@ export class TeamValidator {
|
|
|
543
543
|
tierSpecies = dex.species.get('Kyogre-Primal');
|
|
544
544
|
} else if (canMegaEvo && species.id === 'rayquaza' && set.moves.map(toID).includes('dragonascent' as ID)) {
|
|
545
545
|
tierSpecies = dex.species.get('Rayquaza-Mega');
|
|
546
|
+
} else if (item.id === 'rustedsword' && species.id === 'zacian') {
|
|
547
|
+
tierSpecies = dex.species.get('Zacian-Crowned');
|
|
548
|
+
} else if (item.id === 'rustedshield' && species.id === 'zamazenta') {
|
|
549
|
+
tierSpecies = dex.species.get('Zamazenta-Crowned');
|
|
546
550
|
}
|
|
547
551
|
}
|
|
548
552
|
|
|
@@ -1315,10 +1319,10 @@ export class TeamValidator {
|
|
|
1315
1319
|
const crowned: {[k: string]: string} = {
|
|
1316
1320
|
'Zacian-Crowned': 'behemothblade', 'Zamazenta-Crowned': 'behemothbash',
|
|
1317
1321
|
};
|
|
1318
|
-
if (
|
|
1319
|
-
const
|
|
1320
|
-
if (
|
|
1321
|
-
set.moves[
|
|
1322
|
+
if (species.name in crowned) {
|
|
1323
|
+
const behemothMove = set.moves.map(toID).indexOf(crowned[species.name] as ID);
|
|
1324
|
+
if (behemothMove >= 0) {
|
|
1325
|
+
set.moves[behemothMove] = 'ironhead';
|
|
1322
1326
|
}
|
|
1323
1327
|
}
|
|
1324
1328
|
return problems;
|
|
@@ -1328,6 +1332,14 @@ export class TeamValidator {
|
|
|
1328
1332
|
const dex = this.dex;
|
|
1329
1333
|
const ruleTable = this.ruleTable;
|
|
1330
1334
|
|
|
1335
|
+
// https://www.smogon.com/forums/posts/8659168
|
|
1336
|
+
if (
|
|
1337
|
+
(tierSpecies.id === 'zamazentacrowned' && species.id === 'zamazenta') ||
|
|
1338
|
+
(tierSpecies.id === 'zaciancrowned' && species.id === 'zacian')
|
|
1339
|
+
) {
|
|
1340
|
+
species = tierSpecies;
|
|
1341
|
+
}
|
|
1342
|
+
|
|
1331
1343
|
setHas['pokemon:' + species.id] = true;
|
|
1332
1344
|
setHas['basepokemon:' + toID(species.baseSpecies)] = true;
|
|
1333
1345
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {ObjectReadWriteStream} from '../../lib/streams';
|
|
2
|
+
import {ID} from '../exported-global-types';
|
|
2
3
|
import {Dex, toID} from '../dex';
|
|
3
4
|
import {PRNG, PRNGSeed} from '../prng';
|
|
4
5
|
import {RandomPlayerAI} from './random-player-ai';
|
|
@@ -19,10 +20,18 @@ export interface ExhaustiveRunnerOptions {
|
|
|
19
20
|
maxGames?: number;
|
|
20
21
|
maxFailures?: number;
|
|
21
22
|
dual?: boolean | 'debug';
|
|
23
|
+
possible?: ExhaustiveRunnerPossibilites;
|
|
22
24
|
runner?: (options: RunnerOptions) => Promise<void>;
|
|
23
25
|
cmd?: (cycles: number, format: string, seed: string) => string;
|
|
24
26
|
}
|
|
25
27
|
|
|
28
|
+
export interface ExhaustiveRunnerPossibilites {
|
|
29
|
+
pokemon?: ID[];
|
|
30
|
+
items?: ID[];
|
|
31
|
+
abilities?: ID[];
|
|
32
|
+
moves?: ID[];
|
|
33
|
+
}
|
|
34
|
+
|
|
26
35
|
export class ExhaustiveRunner {
|
|
27
36
|
static readonly DEFAULT_CYCLES = 1;
|
|
28
37
|
static readonly MAX_FAILURES = 10;
|
|
@@ -46,6 +55,7 @@ export class ExhaustiveRunner {
|
|
|
46
55
|
private readonly maxGames?: number;
|
|
47
56
|
private readonly maxFailures?: number;
|
|
48
57
|
private readonly dual: boolean | 'debug';
|
|
58
|
+
private readonly possible?: ExhaustiveRunnerPossibilites;
|
|
49
59
|
|
|
50
60
|
private readonly runner: (options: RunnerOptions) => Promise<void>;
|
|
51
61
|
private readonly cmd: (cycles: number, format: string, seed: string) => string;
|
|
@@ -63,6 +73,7 @@ export class ExhaustiveRunner {
|
|
|
63
73
|
this.maxFailures = options.maxFailures || ExhaustiveRunner.MAX_FAILURES;
|
|
64
74
|
this.dual = options.dual || false;
|
|
65
75
|
this.runner = options.runner || ((o: RunnerOptions) => new Runner(o).run());
|
|
76
|
+
this.possible = options.possible;
|
|
66
77
|
this.cmd = options.cmd || ((cycles: number, format: string, seed: string) =>
|
|
67
78
|
`node tools/simulate exhaustive --cycles=${cycles} --format=${format} --seed=${seed}`);
|
|
68
79
|
|
|
@@ -93,7 +104,7 @@ export class ExhaustiveRunner {
|
|
|
93
104
|
error: true,
|
|
94
105
|
});
|
|
95
106
|
|
|
96
|
-
if (this.log) this.logProgress(pools);
|
|
107
|
+
if (this.log) this.logProgress(dex, pools);
|
|
97
108
|
} catch (err) {
|
|
98
109
|
this.failures++;
|
|
99
110
|
console.error(
|
|
@@ -110,23 +121,31 @@ export class ExhaustiveRunner {
|
|
|
110
121
|
|
|
111
122
|
private createPools(dex: typeof Dex): Pools {
|
|
112
123
|
return {
|
|
113
|
-
pokemon: new Pool(
|
|
114
|
-
(
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
124
|
+
pokemon: new Pool(this.possible?.pokemon ??
|
|
125
|
+
ExhaustiveRunner.onlyValid(dex.gen, dex.data.Pokedex, p => dex.species.get(p), (_, p) =>
|
|
126
|
+
(p.name !== 'Pichu-Spiky-eared' && p.name.substr(0, 8) !== 'Pikachu-')), this.prng),
|
|
127
|
+
items: new Pool(this.possible?.items ??
|
|
128
|
+
ExhaustiveRunner.onlyValid(dex.gen, dex.data.Items, i => dex.items.get(i)), this.prng),
|
|
129
|
+
abilities: new Pool(this.possible?.abilities ??
|
|
130
|
+
ExhaustiveRunner.onlyValid(dex.gen, dex.data.Abilities, a => dex.abilities.get(a)), this.prng),
|
|
131
|
+
moves: new Pool(this.possible?.moves ??
|
|
132
|
+
ExhaustiveRunner.onlyValid(dex.gen, dex.data.Moves, m => dex.moves.get(m), m =>
|
|
133
|
+
(m !== 'struggle' && (m === 'hiddenpower' || m.substr(0, 11) !== 'hiddenpower'))), this.prng),
|
|
119
134
|
};
|
|
120
135
|
}
|
|
121
136
|
|
|
122
|
-
private logProgress(p: Pools) {
|
|
137
|
+
private logProgress(dex: typeof Dex, p: Pools) {
|
|
123
138
|
// `\r` = return to the beginning of the line
|
|
124
139
|
// `\x1b[k` (`\e[K`) = clear all characters from cursor position to EOL
|
|
125
140
|
if (this.games) process.stdout.write('\r\x1b[K');
|
|
141
|
+
|
|
142
|
+
const msg = [`[${this.format}] P:${p.pokemon}`];
|
|
143
|
+
if (dex.gen >= 2) msg.push(`I:${p.items}`);
|
|
144
|
+
if (dex.gen >= 3) msg.push(`A:${p.abilities}`);
|
|
145
|
+
msg.push(`M:${p.moves} = ${this.games}`);
|
|
146
|
+
|
|
126
147
|
// Deliberately don't print a `\n` character so that we can overwrite
|
|
127
|
-
process.stdout.write(
|
|
128
|
-
`[${this.format}] P:${p.pokemon} I:${p.items} A:${p.abilities} M:${p.moves} = ${this.games}`
|
|
129
|
-
);
|
|
148
|
+
process.stdout.write(msg.join(' '));
|
|
130
149
|
}
|
|
131
150
|
|
|
132
151
|
private static getSignatures(dex: typeof Dex, pools: Pools): Map<string, {item: string, move?: string}[]> {
|