@pkmn/sim 0.6.2 → 0.6.4
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 +98 -76
- package/build/config/formats.js.map +1 -1
- package/build/data/abilities.js +1 -1
- package/build/data/abilities.js.map +1 -1
- package/build/data/formats-data.js +3 -3
- package/build/data/formats-data.js.map +1 -1
- package/build/data/mods/gen1/moves.js +3 -2
- package/build/data/mods/gen1/moves.js.map +1 -1
- package/build/data/mods/gen1/scripts.js +75 -40
- package/build/data/mods/gen1/scripts.js.map +1 -1
- package/build/data/mods/gen2/scripts.js +1 -1
- package/build/data/moves.js +8 -6
- package/build/data/moves.js.map +1 -1
- package/build/data/pokedex.js +5 -0
- package/build/data/pokedex.js.map +1 -1
- package/build/data/rulesets.js +58 -0
- package/build/data/rulesets.js.map +1 -1
- package/build/sim/battle-queue.js +4 -3
- package/build/sim/battle-queue.js.map +1 -1
- package/build/sim/battle.js +8 -4
- package/build/sim/battle.js.map +1 -1
- package/build/sim/dex-species.d.ts +2 -0
- package/build/sim/dex-species.js.map +1 -1
- package/build/sim/team-validator.js +4 -0
- package/build/sim/team-validator.js.map +1 -1
- package/config/formats.ts +101 -73
- package/data/abilities.ts +1 -1
- package/data/formats-data.ts +3 -3
- package/data/mods/gen1/moves.ts +3 -2
- package/data/mods/gen1/scripts.ts +73 -39
- package/data/mods/gen2/scripts.ts +1 -1
- package/data/moves.ts +8 -6
- package/data/pokedex.ts +5 -0
- package/data/rulesets.ts +59 -0
- package/package.json +1 -1
- package/sim/battle-queue.ts +4 -3
- package/sim/battle.ts +6 -4
- package/sim/dex-species.ts +2 -0
- package/sim/team-validator.ts +5 -0
package/config/formats.ts
CHANGED
|
@@ -164,7 +164,7 @@ export const Formats: FormatList = [
|
|
|
164
164
|
mod: 'gen8',
|
|
165
165
|
ruleset: ['Little Cup', 'Standard', 'Dynamax Clause'],
|
|
166
166
|
banlist: [
|
|
167
|
-
'Corsola-Galar', 'Cutiefly', 'Drifloon', 'Gastly', 'Gothita', 'Rufflet', 'Scraggy', 'Scyther', 'Sneasel', 'Swirlix',
|
|
167
|
+
'Corsola-Galar', 'Cutiefly', 'Drifloon', 'Gastly', 'Gothita', 'Magby', 'Rufflet', 'Scraggy', 'Scyther', 'Sneasel', 'Swirlix',
|
|
168
168
|
'Tangela', 'Vullaby', 'Vulpix-Alola', 'Woobat', 'Zigzagoon-Base', 'Chlorophyll', 'Moody', 'Baton Pass', 'Sticky Web',
|
|
169
169
|
],
|
|
170
170
|
},
|
|
@@ -249,8 +249,8 @@ export const Formats: FormatList = [
|
|
|
249
249
|
banlist: [
|
|
250
250
|
// LC OU
|
|
251
251
|
'Abra', 'Carvanha', 'Diglett-Base', 'Dwebble', 'Ferroseed', 'Foongus', 'Frillish', 'Grookey', 'Koffing',
|
|
252
|
-
'Larvesta', '
|
|
253
|
-
'
|
|
252
|
+
'Larvesta', 'Magnemite', 'Mareanie', 'Mienfoo', 'Mudbray', 'Natu', 'Onix', 'Pawniard', 'Ponyta-Base',
|
|
253
|
+
'Porygon', 'Staryu', 'Timburr', 'Trapinch', 'Tyrunt',
|
|
254
254
|
// LC UUBL
|
|
255
255
|
'Archen', 'Farfetch\u2019d-Galar', 'Scorbunny', 'Shellder', 'Wingull',
|
|
256
256
|
],
|
|
@@ -517,6 +517,7 @@ export const Formats: FormatList = [
|
|
|
517
517
|
mod: 'gen8',
|
|
518
518
|
ruleset: ['[Gen 8] National Dex'],
|
|
519
519
|
banlist: ['ND OU', 'ND UUBL', 'Drizzle', 'Drought', 'Light Clay', 'Slowbronite'],
|
|
520
|
+
unbanlist: ['Hydreigon'],
|
|
520
521
|
},
|
|
521
522
|
{
|
|
522
523
|
name: "[Gen 8] National Dex RU",
|
|
@@ -527,7 +528,7 @@ export const Formats: FormatList = [
|
|
|
527
528
|
mod: 'gen8',
|
|
528
529
|
searchShow: false,
|
|
529
530
|
ruleset: ['[Gen 8] National Dex UU'],
|
|
530
|
-
banlist: ['ND UU', 'ND RUBL'],
|
|
531
|
+
banlist: ['ND UU', 'ND RUBL', 'Hydreigon'],
|
|
531
532
|
},
|
|
532
533
|
{
|
|
533
534
|
name: "[Gen 8] National Dex Monotype",
|
|
@@ -691,7 +692,7 @@ export const Formats: FormatList = [
|
|
|
691
692
|
'Yveltal', 'Zacian', 'Zacian-Crowned', 'Zamazenta-Base', 'Zekrom', 'Zeraora', 'Zygarde-Base', 'Arena Trap', 'Comatose', 'Contrary', 'Fluffy',
|
|
692
693
|
'Fur Coat', 'Gorilla Tactics', 'Huge Power', 'Ice Scales', 'Illusion', 'Imposter', 'Innards Out', 'Intrepid Sword', 'Libero', 'Magic Bounce',
|
|
693
694
|
'Magnet Pull', 'Moody', 'Neutralizing Gas', 'Parental Bond', 'Poison Heal', 'Protean', 'Pure Power', 'Shadow Tag', 'Simple', 'Stakeout',
|
|
694
|
-
'Speed Boost', 'Unburden', 'Water Bubble', 'Wonder Guard', 'King\'s Rock', 'Baton Pass',
|
|
695
|
+
'Speed Boost', 'Unburden', 'Water Bubble', 'Wonder Guard', 'King\'s Rock', 'Baton Pass', 'Electrify',
|
|
695
696
|
],
|
|
696
697
|
},
|
|
697
698
|
{
|
|
@@ -810,67 +811,12 @@ export const Formats: FormatList = [
|
|
|
810
811
|
],
|
|
811
812
|
|
|
812
813
|
mod: 'gen8',
|
|
813
|
-
ruleset: ['Standard OMs', 'Sleep Moves Clause'],
|
|
814
|
+
ruleset: ['Standard OMs', 'Sleep Moves Clause', 'Godly Gift Mod'],
|
|
814
815
|
banlist: [
|
|
815
816
|
'Blissey', 'Calyrex-Shadow', 'Chansey', 'Crawdaunt', 'Dragapult', 'Eternatus', 'Hawlucha', 'Kyogre', 'Marowak-Alola', 'Melmetal',
|
|
816
|
-
'Nidoking', 'Nidoqueen', 'Pikachu', 'Toxapex', 'Xerneas', 'Zacian', 'Zacian-Crowned', '
|
|
817
|
-
'
|
|
818
|
-
'Quick Claw', 'Baton Pass',
|
|
817
|
+
'Nidoking', 'Nidoqueen', 'Pikachu', 'Toxapex', 'Xerneas', 'Zacian', 'Zacian-Crowned', 'Arena Trap', 'Huge Power', 'Moody',
|
|
818
|
+
'Pure Power', 'Shadow Tag', 'Swift Swim', 'Bright Powder', 'Focus Band', 'King\'s Rock', 'Lax Incense', 'Quick Claw', 'Baton Pass',
|
|
819
819
|
],
|
|
820
|
-
onValidateTeam(team) {
|
|
821
|
-
const gods = new Set<string>();
|
|
822
|
-
for (const set of team) {
|
|
823
|
-
let species = this.dex.species.get(set.species);
|
|
824
|
-
if (typeof species.battleOnly === 'string') species = this.dex.species.get(species.battleOnly);
|
|
825
|
-
if (set.item && this.dex.items.get(set.item).megaStone) {
|
|
826
|
-
const item = this.dex.items.get(set.item);
|
|
827
|
-
if (item.megaEvolves === species.baseSpecies) {
|
|
828
|
-
species = this.dex.species.get(item.megaStone);
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
if (this.ruleTable.has('standardnatdex')) {
|
|
832
|
-
const format = this.dex.formats.getRuleTable(this.dex.formats.get('gen8nationaldex'));
|
|
833
|
-
if (format.isBannedSpecies(species)) gods.add(species.name);
|
|
834
|
-
} else {
|
|
835
|
-
if (['ag', 'uber'].includes(this.toID(species.tier)) || this.toID(set.ability) === 'powerconstruct') {
|
|
836
|
-
gods.add(species.name);
|
|
837
|
-
}
|
|
838
|
-
}
|
|
839
|
-
}
|
|
840
|
-
if (gods.size > 1) {
|
|
841
|
-
return [`You have too many Gods.`, `(${Array.from(gods).join(', ')} are Gods.)`];
|
|
842
|
-
}
|
|
843
|
-
},
|
|
844
|
-
onModifySpeciesPriority: 3,
|
|
845
|
-
onModifySpecies(species, target, source) {
|
|
846
|
-
if (source || !target?.side) return;
|
|
847
|
-
const god = target.side.team.find(set => {
|
|
848
|
-
let godSpecies = this.dex.species.get(set.species);
|
|
849
|
-
const isNatDex = this.format.ruleTable?.has('standardnatdex');
|
|
850
|
-
const validator = this.dex.formats.getRuleTable(
|
|
851
|
-
this.dex.formats.get(`gen${isNatDex && this.gen < 8 ? 8 : this.gen}${isNatDex ? 'nationaldex' : 'ou'}`)
|
|
852
|
-
);
|
|
853
|
-
if (this.toID(set.ability) === 'powerconstruct') {
|
|
854
|
-
return true;
|
|
855
|
-
}
|
|
856
|
-
if (set.item) {
|
|
857
|
-
const item = this.dex.items.get(set.item);
|
|
858
|
-
if (item.megaEvolves === set.species) godSpecies = this.dex.species.get(item.megaStone);
|
|
859
|
-
}
|
|
860
|
-
const isBanned = validator.isBannedSpecies(godSpecies);
|
|
861
|
-
return isBanned;
|
|
862
|
-
}) || target.side.team[0];
|
|
863
|
-
const stat = Dex.stats.ids()[target.side.team.indexOf(target.set)];
|
|
864
|
-
const newSpecies = this.dex.deepClone(species);
|
|
865
|
-
let godSpecies = this.dex.species.get(god.species);
|
|
866
|
-
if (typeof godSpecies.battleOnly === 'string') {
|
|
867
|
-
godSpecies = this.dex.species.get(godSpecies.battleOnly);
|
|
868
|
-
}
|
|
869
|
-
newSpecies.bst -= newSpecies.baseStats[stat];
|
|
870
|
-
newSpecies.baseStats[stat] = godSpecies.baseStats[stat];
|
|
871
|
-
newSpecies.bst += newSpecies.baseStats[stat];
|
|
872
|
-
return newSpecies;
|
|
873
|
-
},
|
|
874
820
|
},
|
|
875
821
|
{
|
|
876
822
|
name: "[Gen 8] Free-For-All",
|
|
@@ -1669,9 +1615,9 @@ export const Formats: FormatList = [
|
|
|
1669
1615
|
'Calyrex-Ice', 'Calyrex-Shadow', 'Dialga', 'Eternatus', 'Giratina', 'Giratina-Origin', 'Groudon', 'Ho-Oh',
|
|
1670
1616
|
'Jirachi', 'Kyogre', 'Kyurem-White', 'Lugia', 'Lunala', 'Magearna', 'Marshadow', 'Melmetal', 'Mewtwo',
|
|
1671
1617
|
'Necrozma-Dawn-Wings', 'Necrozma-Dusk-Mane', 'Palkia', 'Rayquaza', 'Reshiram', 'Solgaleo', 'Urshifu-Base',
|
|
1672
|
-
'Xerneas', 'Yveltal', 'Zacian', 'Zacian-Crowned', 'Zamazenta', 'Zamazenta-Crowned', 'Zekrom', '
|
|
1673
|
-
'
|
|
1674
|
-
'Shell Smash', 'Swagger',
|
|
1618
|
+
'Xerneas', 'Yveltal', 'Zacian', 'Zacian-Crowned', 'Zamazenta', 'Zamazenta-Crowned', 'Zekrom', 'Contrary',
|
|
1619
|
+
'Emergency Exit', 'Huge Power', 'Moody', 'Power Construct', 'Serene Grace', 'Shadow Tag', 'Wimp Out',
|
|
1620
|
+
'Wonder Guard', 'Ally Switch', 'Bolt Beak', 'Fishious Rend', 'Shell Smash', 'Swagger',
|
|
1675
1621
|
],
|
|
1676
1622
|
onBeforeSwitchIn(pokemon) {
|
|
1677
1623
|
pokemon.m.curMoves = this.dex.deepClone(pokemon.moves);
|
|
@@ -2173,12 +2119,94 @@ export const Formats: FormatList = [
|
|
|
2173
2119
|
column: 2,
|
|
2174
2120
|
},
|
|
2175
2121
|
{
|
|
2176
|
-
name: "[Gen
|
|
2122
|
+
name: "[Gen 4] Shared Power Random Battle",
|
|
2123
|
+
desc: `[Gen 4] Random Battle with aspects of [Gen 8] Shared Power`,
|
|
2177
2124
|
|
|
2178
|
-
mod: '
|
|
2125
|
+
mod: 'gen4',
|
|
2179
2126
|
team: 'random',
|
|
2180
|
-
ruleset: ['[Gen
|
|
2181
|
-
|
|
2127
|
+
ruleset: ['[Gen 4] Random Battle', 'Team Preview'],
|
|
2128
|
+
getSharedPower(pokemon) {
|
|
2129
|
+
const sharedPower = new Set<string>();
|
|
2130
|
+
for (const ally of pokemon.side.pokemon) {
|
|
2131
|
+
if (ally.previouslySwitchedIn > 0) {
|
|
2132
|
+
if (pokemon.battle.dex.currentMod !== 'sharedpower' && ['trace', 'mirrorarmor'].includes(ally.baseAbility)) {
|
|
2133
|
+
sharedPower.add('noability');
|
|
2134
|
+
continue;
|
|
2135
|
+
}
|
|
2136
|
+
sharedPower.add(ally.baseAbility);
|
|
2137
|
+
}
|
|
2138
|
+
}
|
|
2139
|
+
sharedPower.delete(pokemon.baseAbility);
|
|
2140
|
+
return sharedPower;
|
|
2141
|
+
},
|
|
2142
|
+
onBeforeSwitchIn(pokemon) {
|
|
2143
|
+
let format = this.format;
|
|
2144
|
+
if (!format.getSharedPower) format = this.dex.formats.get('gen8sharedpower');
|
|
2145
|
+
for (const ability of format.getSharedPower!(pokemon)) {
|
|
2146
|
+
const effect = 'ability:' + ability;
|
|
2147
|
+
pokemon.volatiles[effect] = {id: this.toID(effect), target: pokemon};
|
|
2148
|
+
if (!pokemon.m.abils) pokemon.m.abils = [];
|
|
2149
|
+
if (!pokemon.m.abils.includes(effect)) pokemon.m.abils.push(effect);
|
|
2150
|
+
}
|
|
2151
|
+
},
|
|
2152
|
+
onSwitchInPriority: 2,
|
|
2153
|
+
onSwitchIn(pokemon) {
|
|
2154
|
+
let format = this.format;
|
|
2155
|
+
if (!format.getSharedPower) format = this.dex.formats.get('gen8sharedpower');
|
|
2156
|
+
for (const ability of format.getSharedPower!(pokemon)) {
|
|
2157
|
+
if (ability === 'noability') {
|
|
2158
|
+
this.hint(`Mirror Armor and Trace break in Shared Power formats that don't use Shared Power as a base, so they get removed from non-base users.`);
|
|
2159
|
+
}
|
|
2160
|
+
const effect = 'ability:' + ability;
|
|
2161
|
+
delete pokemon.volatiles[effect];
|
|
2162
|
+
pokemon.addVolatile(effect);
|
|
2163
|
+
}
|
|
2164
|
+
},
|
|
2165
|
+
field: {
|
|
2166
|
+
suppressingWeather() {
|
|
2167
|
+
for (const pokemon of this.battle.getAllActive()) {
|
|
2168
|
+
const innates = Object.keys(pokemon.volatiles).filter(x => x.startsWith('ability:'));
|
|
2169
|
+
if (pokemon && !pokemon.ignoringAbility() &&
|
|
2170
|
+
(pokemon.getAbility().suppressWeather || innates.some(x => (
|
|
2171
|
+
this.battle.dex.abilities.get(x.replace('ability:', '')).suppressWeather
|
|
2172
|
+
)))) {
|
|
2173
|
+
return true;
|
|
2174
|
+
}
|
|
2175
|
+
}
|
|
2176
|
+
return false;
|
|
2177
|
+
},
|
|
2178
|
+
},
|
|
2179
|
+
pokemon: {
|
|
2180
|
+
hasAbility(ability) {
|
|
2181
|
+
if (this.ignoringAbility()) return false;
|
|
2182
|
+
if (Array.isArray(ability)) return ability.some(abil => this.hasAbility(abil));
|
|
2183
|
+
const abilityid = this.battle.toID(ability);
|
|
2184
|
+
return this.ability === abilityid || !!this.volatiles['ability:' + abilityid];
|
|
2185
|
+
},
|
|
2186
|
+
ignoringAbility() {
|
|
2187
|
+
// Check if any active pokemon have the ability Neutralizing Gas
|
|
2188
|
+
let neutralizinggas = false;
|
|
2189
|
+
for (const pokemon of this.battle.getAllActive()) {
|
|
2190
|
+
// can't use hasAbility because it would lead to infinite recursion
|
|
2191
|
+
if (
|
|
2192
|
+
(pokemon.ability === ('neutralizinggas' as ID) || pokemon.m.abils?.includes('ability:neutralizinggas')) &&
|
|
2193
|
+
!pokemon.volatiles['gastroacid'] && !pokemon.abilityState.ending
|
|
2194
|
+
) {
|
|
2195
|
+
neutralizinggas = true;
|
|
2196
|
+
break;
|
|
2197
|
+
}
|
|
2198
|
+
}
|
|
2199
|
+
|
|
2200
|
+
return !!(
|
|
2201
|
+
(this.battle.gen >= 5 && !this.isActive) ||
|
|
2202
|
+
((this.volatiles['gastroacid'] ||
|
|
2203
|
+
(neutralizinggas && (this.ability !== ('neutralizinggas' as ID) ||
|
|
2204
|
+
this.m.abils?.includes('ability:neutralizinggas'))
|
|
2205
|
+
)) && !this.getAbility().isPermanent
|
|
2206
|
+
)
|
|
2207
|
+
);
|
|
2208
|
+
},
|
|
2209
|
+
},
|
|
2182
2210
|
},
|
|
2183
2211
|
|
|
2184
2212
|
// Randomized Metas
|
|
@@ -2548,9 +2576,9 @@ export const Formats: FormatList = [
|
|
|
2548
2576
|
mod: 'gen3',
|
|
2549
2577
|
// searchShow: false,
|
|
2550
2578
|
gameType: 'doubles',
|
|
2551
|
-
ruleset: ['Standard', '!
|
|
2552
|
-
banlist: ['Uber'],
|
|
2553
|
-
unbanlist: ['Deoxys-
|
|
2579
|
+
ruleset: ['Standard', '!Switch Priority Clause Mod'],
|
|
2580
|
+
banlist: ['Uber', 'Soul Dew', 'Swagger'],
|
|
2581
|
+
unbanlist: ['Deoxys-Defense', 'Latias', 'Wobbuffet', 'Wynaut'],
|
|
2554
2582
|
},
|
|
2555
2583
|
{
|
|
2556
2584
|
name: "[Gen 3] UU",
|
package/data/abilities.ts
CHANGED
|
@@ -1234,7 +1234,7 @@ export const Abilities: {[abilityid: string]: AbilityData} = {
|
|
|
1234
1234
|
if (move?.type === 'Flying' && pokemon.hp === pokemon.maxhp) return priority + 1;
|
|
1235
1235
|
},
|
|
1236
1236
|
name: "Gale Wings",
|
|
1237
|
-
rating:
|
|
1237
|
+
rating: 2.5,
|
|
1238
1238
|
num: 177,
|
|
1239
1239
|
},
|
|
1240
1240
|
galvanize: {
|
package/data/formats-data.ts
CHANGED
|
@@ -1259,7 +1259,7 @@ export const FormatsData: {[k: string]: SpeciesFormatsData} = {
|
|
|
1259
1259
|
natDexTier: "RU",
|
|
1260
1260
|
},
|
|
1261
1261
|
magby: {
|
|
1262
|
-
tier: "
|
|
1262
|
+
tier: "NFE",
|
|
1263
1263
|
},
|
|
1264
1264
|
magmar: {
|
|
1265
1265
|
tier: "NFE",
|
|
@@ -1405,7 +1405,7 @@ export const FormatsData: {[k: string]: SpeciesFormatsData} = {
|
|
|
1405
1405
|
natDexTier: "RU",
|
|
1406
1406
|
},
|
|
1407
1407
|
glaceon: {
|
|
1408
|
-
randomBattleMoves: ["freezedry", "protect", "
|
|
1408
|
+
randomBattleMoves: ["freezedry", "protect", "toxic", "wish"],
|
|
1409
1409
|
randomBattleLevel: 88,
|
|
1410
1410
|
randomDoubleBattleMoves: ["blizzard", "freezedry", "helpinghand", "protect", "shadowball", "wish"],
|
|
1411
1411
|
randomDoubleBattleLevel: 88,
|
|
@@ -5841,7 +5841,7 @@ export const FormatsData: {[k: string]: SpeciesFormatsData} = {
|
|
|
5841
5841
|
natDexTier: "RU",
|
|
5842
5842
|
},
|
|
5843
5843
|
silvallyghost: {
|
|
5844
|
-
randomBattleMoves: ["multiattack", "partingshot", "swordsdance", "xscissor"],
|
|
5844
|
+
randomBattleMoves: ["flamecharge", "multiattack", "partingshot", "swordsdance", "xscissor"],
|
|
5845
5845
|
randomBattleLevel: 84,
|
|
5846
5846
|
randomDoubleBattleMoves: ["multiattack", "swordsdance", "tailwind", "xscissor"],
|
|
5847
5847
|
randomDoubleBattleLevel: 88,
|
package/data/mods/gen1/moves.ts
CHANGED
|
@@ -253,7 +253,7 @@ export const Moves: {[k: string]: ModdedMoveData} = {
|
|
|
253
253
|
duration: 2,
|
|
254
254
|
onLockMove: 'dig',
|
|
255
255
|
onInvulnerability(target, source, move) {
|
|
256
|
-
if (move.id === 'swift') return true;
|
|
256
|
+
if (move.id === 'swift' || move.id === 'transform') return true;
|
|
257
257
|
this.add('-message', 'The foe ' + target.name + ' can\'t be hit underground!');
|
|
258
258
|
return false;
|
|
259
259
|
},
|
|
@@ -372,7 +372,7 @@ export const Moves: {[k: string]: ModdedMoveData} = {
|
|
|
372
372
|
duration: 2,
|
|
373
373
|
onLockMove: 'fly',
|
|
374
374
|
onInvulnerability(target, source, move) {
|
|
375
|
-
if (move.id === 'swift') return true;
|
|
375
|
+
if (move.id === 'swift' || move.id === 'transform') return true;
|
|
376
376
|
this.add('-message', 'The foe ' + target.name + ' can\'t be hit while flying!');
|
|
377
377
|
return false;
|
|
378
378
|
},
|
|
@@ -835,6 +835,7 @@ export const Moves: {[k: string]: ModdedMoveData} = {
|
|
|
835
835
|
uncappedDamage = this.runEvent('SubDamage', target, source, move, uncappedDamage);
|
|
836
836
|
if (!uncappedDamage) return uncappedDamage;
|
|
837
837
|
source.lastDamage = uncappedDamage;
|
|
838
|
+
this.lastDamage = uncappedDamage;
|
|
838
839
|
target.volatiles['substitute'].hp -= uncappedDamage > target.volatiles['substitute'].hp ?
|
|
839
840
|
target.volatiles['substitute'].hp : uncappedDamage;
|
|
840
841
|
if (target.volatiles['substitute'].hp <= 0) {
|
|
@@ -67,8 +67,7 @@ export const Scripts: ModdedBattleScriptsData = {
|
|
|
67
67
|
},
|
|
68
68
|
actions: {
|
|
69
69
|
// This function is the main one when running a move.
|
|
70
|
-
// It deals with the beforeMove
|
|
71
|
-
// This leads with partial trapping moves shennanigans after the move has been used.
|
|
70
|
+
// It deals with the beforeMove event.
|
|
72
71
|
// It also deals with how PP reduction works on gen 1.
|
|
73
72
|
runMove(moveOrMoveName, pokemon, targetLoc, sourceEffect) {
|
|
74
73
|
const target = this.battle.getTarget(pokemon, moveOrMoveName, targetLoc);
|
|
@@ -115,46 +114,79 @@ export const Scripts: ModdedBattleScriptsData = {
|
|
|
115
114
|
}
|
|
116
115
|
}
|
|
117
116
|
this.useMove(move, pokemon, target, sourceEffect);
|
|
118
|
-
|
|
117
|
+
},
|
|
118
|
+
// This function deals with AfterMoveSelf events.
|
|
119
|
+
// This leads with partial trapping moves shenanigans after the move has been used.
|
|
120
|
+
useMove(moveOrMoveName, pokemon, target, sourceEffect) {
|
|
121
|
+
const moveResult = this.useMoveInner(moveOrMoveName, pokemon, target, sourceEffect);
|
|
119
122
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
123
|
+
if (!sourceEffect && this.battle.effect.id) sourceEffect = this.battle.effect;
|
|
124
|
+
const baseMove = this.battle.dex.moves.get(moveOrMoveName);
|
|
125
|
+
let move = this.battle.dex.getActiveMove(baseMove);
|
|
126
|
+
if (target === undefined) target = this.battle.getRandomTarget(pokemon, move);
|
|
127
|
+
if (move.target === 'self') {
|
|
128
|
+
target = pokemon;
|
|
129
|
+
}
|
|
130
|
+
if (sourceEffect) move.sourceEffect = sourceEffect.id;
|
|
131
|
+
|
|
132
|
+
this.battle.singleEvent('ModifyMove', move, null, pokemon, target, move, move);
|
|
133
|
+
if (baseMove.target !== move.target) {
|
|
134
|
+
// Target changed in ModifyMove, so we must adjust it here
|
|
135
|
+
target = this.battle.getRandomTarget(pokemon, move);
|
|
136
|
+
}
|
|
137
|
+
move = this.battle.runEvent('ModifyMove', pokemon, target, move, move);
|
|
138
|
+
if (baseMove.target !== move.target) {
|
|
139
|
+
// Check again, this shouldn't ever happen on Gen 1.
|
|
140
|
+
target = this.battle.getRandomTarget(pokemon, move);
|
|
131
141
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
targetVolatile.duration = 2;
|
|
142
|
+
|
|
143
|
+
if (move.id !== 'metronome') {
|
|
144
|
+
if (move.id !== 'mirrormove' ||
|
|
145
|
+
(!pokemon.side.foe.active[0]?.lastMove || pokemon.side.foe.active[0].lastMove?.id === 'mirrormove')) {
|
|
146
|
+
// The move is our 'final' move (a failed Mirror Move, or any move that isn't Metronome or Mirror Move).
|
|
147
|
+
this.battle.singleEvent('AfterMove', move, null, pokemon, target, move);
|
|
148
|
+
|
|
149
|
+
// If target fainted
|
|
150
|
+
if (target && target.hp <= 0) {
|
|
151
|
+
// We remove recharge
|
|
152
|
+
if (pokemon.volatiles['mustrecharge']) pokemon.removeVolatile('mustrecharge');
|
|
153
|
+
delete pokemon.volatiles['partialtrappinglock'];
|
|
154
|
+
// We remove screens
|
|
155
|
+
target.side.removeSideCondition('reflect');
|
|
156
|
+
target.side.removeSideCondition('lightscreen');
|
|
157
|
+
pokemon.removeVolatile('twoturnmove');
|
|
158
|
+
} else if (pokemon.hp) {
|
|
159
|
+
this.battle.runEvent('AfterMoveSelf', pokemon, target, move);
|
|
151
160
|
}
|
|
152
|
-
|
|
161
|
+
if (pokemon.volatiles['mustrecharge']) this.battle.add('-mustrecharge', pokemon);
|
|
162
|
+
|
|
163
|
+
// For partial trapping moves, we are saving the target
|
|
164
|
+
if (move.volatileStatus === 'partiallytrapped' && target && target.hp > 0) {
|
|
165
|
+
// Let's check if the lock exists
|
|
166
|
+
if (pokemon.volatiles['partialtrappinglock'] && target.volatiles['partiallytrapped']) {
|
|
167
|
+
// Here the partialtrappinglock volatile has been already applied
|
|
168
|
+
const sourceVolatile = pokemon.volatiles['partialtrappinglock'];
|
|
169
|
+
const targetVolatile = target.volatiles['partiallytrapped'];
|
|
170
|
+
if (!sourceVolatile.locked) {
|
|
171
|
+
// If it's the first hit, we save the target
|
|
172
|
+
sourceVolatile.locked = target;
|
|
173
|
+
} else if (target !== pokemon && target !== sourceVolatile.locked) {
|
|
174
|
+
// Our target switched out! Re-roll the duration, damage, and accuracy.
|
|
175
|
+
const duration = this.battle.sample([2, 2, 2, 3, 3, 3, 4, 5]);
|
|
176
|
+
sourceVolatile.duration = duration;
|
|
177
|
+
sourceVolatile.locked = target;
|
|
178
|
+
// Duration reset thus partially trapped at 2 always.
|
|
179
|
+
targetVolatile.duration = 2;
|
|
180
|
+
}
|
|
181
|
+
} // If we move to here, the move failed and there's no partial trapping lock.
|
|
182
|
+
}
|
|
183
|
+
}
|
|
153
184
|
}
|
|
185
|
+
return moveResult;
|
|
154
186
|
},
|
|
155
187
|
// This is the function that actually uses the move, running ModifyMove events.
|
|
156
188
|
// It uses the move and then deals with the effects after the move.
|
|
157
|
-
|
|
189
|
+
useMoveInner(moveOrMoveName, pokemon, target, sourceEffect) {
|
|
158
190
|
if (!sourceEffect && this.battle.effect.id) sourceEffect = this.battle.effect;
|
|
159
191
|
const baseMove = this.battle.dex.moves.get(moveOrMoveName);
|
|
160
192
|
let move = this.battle.dex.getActiveMove(baseMove);
|
|
@@ -210,13 +242,13 @@ export const Scripts: ModdedBattleScriptsData = {
|
|
|
210
242
|
}
|
|
211
243
|
damage = this.tryMoveHit(target, pokemon, move);
|
|
212
244
|
|
|
213
|
-
// Store 0 damage for last damage if move failed
|
|
245
|
+
// Store 0 damage for last damage if move failed.
|
|
214
246
|
// This only happens on moves that don't deal damage but call GetDamageVarsForPlayerAttack (disassembly).
|
|
215
247
|
const neverDamageMoves = [
|
|
216
248
|
'conversion', 'haze', 'mist', 'focusenergy', 'confuseray', 'supersonic', 'transform', 'lightscreen', 'reflect', 'substitute', 'mimic', 'leechseed', 'splash', 'softboiled', 'recover', 'rest',
|
|
217
249
|
];
|
|
218
250
|
if (
|
|
219
|
-
!damage &&
|
|
251
|
+
!damage && damage !== 0 &&
|
|
220
252
|
(move.category !== 'Status' || (move.status && !['psn', 'tox', 'par'].includes(move.status))) &&
|
|
221
253
|
!neverDamageMoves.includes(move.id)
|
|
222
254
|
) {
|
|
@@ -393,6 +425,7 @@ export const Scripts: ModdedBattleScriptsData = {
|
|
|
393
425
|
// We get the sub to the target to see if it existed
|
|
394
426
|
const targetSub = (target) ? target.volatiles['substitute'] : false;
|
|
395
427
|
const targetHadSub = (targetSub !== null && targetSub !== false && (typeof targetSub !== 'undefined'));
|
|
428
|
+
let targetHasSub: boolean | undefined = undefined;
|
|
396
429
|
|
|
397
430
|
if (target) {
|
|
398
431
|
hitResult = this.battle.singleEvent('TryHit', moveData, {}, target, pokemon, move);
|
|
@@ -425,6 +458,7 @@ export const Scripts: ModdedBattleScriptsData = {
|
|
|
425
458
|
}
|
|
426
459
|
|
|
427
460
|
if (hitResult === 0) {
|
|
461
|
+
targetHasSub = !!(target?.volatiles['substitute']);
|
|
428
462
|
target = null;
|
|
429
463
|
} else if (!hitResult) {
|
|
430
464
|
if (hitResult === false) this.battle.add('-fail', target);
|
|
@@ -554,7 +588,7 @@ export const Scripts: ModdedBattleScriptsData = {
|
|
|
554
588
|
return false;
|
|
555
589
|
}
|
|
556
590
|
}
|
|
557
|
-
|
|
591
|
+
if (targetHasSub === undefined) targetHasSub = !!(target?.volatiles['substitute']);
|
|
558
592
|
|
|
559
593
|
// Here's where self effects are applied.
|
|
560
594
|
const doSelf = (targetHadSub && targetHasSub) || !targetHadSub;
|
|
@@ -798,7 +832,7 @@ export const Scripts: ModdedBattleScriptsData = {
|
|
|
798
832
|
// This occurs when damage was either 2 or 3 prior to applying STAB/Type matchup, and target is 4x resistant to the move.
|
|
799
833
|
if (damage === 0) return damage;
|
|
800
834
|
|
|
801
|
-
// Apply random factor
|
|
835
|
+
// Apply random factor if damage is greater than 1
|
|
802
836
|
if (damage > 1) {
|
|
803
837
|
damage *= this.battle.random(217, 256);
|
|
804
838
|
damage = Math.floor(damage / 255);
|
|
@@ -706,7 +706,7 @@ export const Scripts: ModdedBattleScriptsData = {
|
|
|
706
706
|
}
|
|
707
707
|
}
|
|
708
708
|
|
|
709
|
-
// Apply random factor
|
|
709
|
+
// Apply random factor if damage is greater than 1, except for Flail and Reversal
|
|
710
710
|
if (!move.noDamageVariance && damage > 1) {
|
|
711
711
|
damage *= this.battle.random(217, 256);
|
|
712
712
|
damage = Math.floor(damage / 255);
|
package/data/moves.ts
CHANGED
|
@@ -11157,9 +11157,10 @@ export const Moves: {[moveid: string]: MoveData} = {
|
|
|
11157
11157
|
priority: 0,
|
|
11158
11158
|
flags: {protect: 1, mirror: 1},
|
|
11159
11159
|
mindBlownRecoil: true,
|
|
11160
|
-
|
|
11161
|
-
|
|
11162
|
-
|
|
11160
|
+
onAfterMoveSecondarySelf(pokemon, target, move) {
|
|
11161
|
+
const maxhp = pokemon.getUndynamaxedHP(pokemon.maxhp);
|
|
11162
|
+
if (pokemon.hp && pokemon.ability === "emergencyexit" && pokemon.getUndynamaxedHP() <= maxhp / 2) {
|
|
11163
|
+
this.runEvent('EmergencyExit', pokemon);
|
|
11163
11164
|
}
|
|
11164
11165
|
},
|
|
11165
11166
|
secondary: null,
|
|
@@ -16768,9 +16769,10 @@ export const Moves: {[moveid: string]: MoveData} = {
|
|
|
16768
16769
|
priority: 0,
|
|
16769
16770
|
flags: {protect: 1, mirror: 1},
|
|
16770
16771
|
mindBlownRecoil: true,
|
|
16771
|
-
|
|
16772
|
-
|
|
16773
|
-
|
|
16772
|
+
onAfterMoveSecondarySelf(pokemon, target, move) {
|
|
16773
|
+
const maxhp = pokemon.getUndynamaxedHP(pokemon.maxhp);
|
|
16774
|
+
if (pokemon.hp && pokemon.ability === "emergencyexit" && pokemon.getUndynamaxedHP() <= maxhp / 2) {
|
|
16775
|
+
this.runEvent('EmergencyExit', pokemon);
|
|
16774
16776
|
}
|
|
16775
16777
|
},
|
|
16776
16778
|
secondary: null,
|
package/data/pokedex.ts
CHANGED
|
@@ -820,6 +820,7 @@ export const Pokedex: {[speciesid: string]: SpeciesData} = {
|
|
|
820
820
|
prevo: "Pikachu",
|
|
821
821
|
evoType: "useItem",
|
|
822
822
|
evoItem: "Thunder Stone",
|
|
823
|
+
evoRegion: "Alola",
|
|
823
824
|
eggGroups: ["Field", "Fairy"],
|
|
824
825
|
},
|
|
825
826
|
sandshrew: {
|
|
@@ -2350,6 +2351,7 @@ export const Pokedex: {[speciesid: string]: SpeciesData} = {
|
|
|
2350
2351
|
prevo: "Exeggcute",
|
|
2351
2352
|
evoType: "useItem",
|
|
2352
2353
|
evoItem: "Leaf Stone",
|
|
2354
|
+
evoRegion: "Alola",
|
|
2353
2355
|
eggGroups: ["Grass"],
|
|
2354
2356
|
},
|
|
2355
2357
|
cubone: {
|
|
@@ -2393,6 +2395,7 @@ export const Pokedex: {[speciesid: string]: SpeciesData} = {
|
|
|
2393
2395
|
prevo: "Cubone",
|
|
2394
2396
|
evoLevel: 28,
|
|
2395
2397
|
evoCondition: "at night",
|
|
2398
|
+
evoRegion: "Alola",
|
|
2396
2399
|
eggGroups: ["Monster"],
|
|
2397
2400
|
},
|
|
2398
2401
|
marowakalolatotem: {
|
|
@@ -2490,6 +2493,7 @@ export const Pokedex: {[speciesid: string]: SpeciesData} = {
|
|
|
2490
2493
|
color: "Gray",
|
|
2491
2494
|
prevo: "Koffing",
|
|
2492
2495
|
evoLevel: 35,
|
|
2496
|
+
evoRegion: "Galar",
|
|
2493
2497
|
eggGroups: ["Amorphous"],
|
|
2494
2498
|
},
|
|
2495
2499
|
rhyhorn: {
|
|
@@ -2687,6 +2691,7 @@ export const Pokedex: {[speciesid: string]: SpeciesData} = {
|
|
|
2687
2691
|
prevo: "Mime Jr.",
|
|
2688
2692
|
evoType: "levelMove",
|
|
2689
2693
|
evoMove: "Mimic",
|
|
2694
|
+
evoRegion: "Galar",
|
|
2690
2695
|
evos: ["Mr. Rime"],
|
|
2691
2696
|
eggGroups: ["Human-Like"],
|
|
2692
2697
|
canHatch: true,
|
package/data/rulesets.ts
CHANGED
|
@@ -2097,4 +2097,63 @@ export const Rulesets: {[k: string]: FormatData} = {
|
|
|
2097
2097
|
}
|
|
2098
2098
|
},
|
|
2099
2099
|
},
|
|
2100
|
+
godlygiftmod: {
|
|
2101
|
+
effectType: 'Rule',
|
|
2102
|
+
name: "Godly Gift Mod",
|
|
2103
|
+
onValidateTeam(team) {
|
|
2104
|
+
const gods = new Set<string>();
|
|
2105
|
+
for (const set of team) {
|
|
2106
|
+
let species = this.dex.species.get(set.species);
|
|
2107
|
+
if (typeof species.battleOnly === 'string') species = this.dex.species.get(species.battleOnly);
|
|
2108
|
+
if (set.item && this.dex.items.get(set.item).megaStone) {
|
|
2109
|
+
const item = this.dex.items.get(set.item);
|
|
2110
|
+
if (item.megaEvolves === species.baseSpecies) {
|
|
2111
|
+
species = this.dex.species.get(item.megaStone);
|
|
2112
|
+
}
|
|
2113
|
+
}
|
|
2114
|
+
if (
|
|
2115
|
+
['ag', 'uber'].includes(this.toID(this.ruleTable.has('standardnatdex') ? species.natDexTier : species.tier)) ||
|
|
2116
|
+
this.toID(set.ability) === 'powerconstruct'
|
|
2117
|
+
) {
|
|
2118
|
+
gods.add(species.name);
|
|
2119
|
+
}
|
|
2120
|
+
}
|
|
2121
|
+
if (gods.size > 1) {
|
|
2122
|
+
return [`You have too many Gods.`, `(${Array.from(gods).join(', ')} are Gods.)`];
|
|
2123
|
+
}
|
|
2124
|
+
},
|
|
2125
|
+
onModifySpeciesPriority: 3,
|
|
2126
|
+
onModifySpecies(species, target, source) {
|
|
2127
|
+
if (source || !target?.side) return;
|
|
2128
|
+
const god = target.side.team.find(set => {
|
|
2129
|
+
let godSpecies = this.dex.species.get(set.species);
|
|
2130
|
+
const isNatDex = this.format.ruleTable?.has('standardnatdex');
|
|
2131
|
+
const validator = this.dex.formats.getRuleTable(
|
|
2132
|
+
this.dex.formats.get(`gen${this.gen}${isNatDex && this.gen >= 8 ? 'nationaldex' : 'ou'}`)
|
|
2133
|
+
);
|
|
2134
|
+
if (this.toID(set.ability) === 'powerconstruct') {
|
|
2135
|
+
return true;
|
|
2136
|
+
}
|
|
2137
|
+
if (set.item) {
|
|
2138
|
+
const item = this.dex.items.get(set.item);
|
|
2139
|
+
if (item.megaEvolves === set.species) godSpecies = this.dex.species.get(item.megaStone);
|
|
2140
|
+
}
|
|
2141
|
+
const isBanned = validator.isBannedSpecies(godSpecies);
|
|
2142
|
+
return isBanned;
|
|
2143
|
+
}) || target.side.team[0];
|
|
2144
|
+
const stat = Dex.stats.ids()[target.side.team.indexOf(target.set)];
|
|
2145
|
+
const newSpecies = this.dex.deepClone(species);
|
|
2146
|
+
let godSpecies = this.dex.species.get(god.species);
|
|
2147
|
+
if (typeof godSpecies.battleOnly === 'string') {
|
|
2148
|
+
godSpecies = this.dex.species.get(godSpecies.battleOnly);
|
|
2149
|
+
}
|
|
2150
|
+
newSpecies.bst -= newSpecies.baseStats[stat];
|
|
2151
|
+
newSpecies.baseStats[stat] = godSpecies.baseStats[stat];
|
|
2152
|
+
if (this.gen === 1 && (stat === 'spa' || stat === 'spd')) {
|
|
2153
|
+
newSpecies.baseStats['spa'] = newSpecies.baseStats['spd'] = godSpecies.baseStats[stat];
|
|
2154
|
+
}
|
|
2155
|
+
newSpecies.bst += newSpecies.baseStats[stat];
|
|
2156
|
+
return newSpecies;
|
|
2157
|
+
},
|
|
2158
|
+
},
|
|
2100
2159
|
};
|
package/package.json
CHANGED
package/sim/battle-queue.ts
CHANGED
|
@@ -281,9 +281,10 @@ export class BattleQueue {
|
|
|
281
281
|
for (const choice of choices) {
|
|
282
282
|
const resolvedChoices = this.resolveAction(choice);
|
|
283
283
|
this.list.push(...resolvedChoices);
|
|
284
|
-
const resolvedChoice
|
|
285
|
-
|
|
286
|
-
|
|
284
|
+
for (const resolvedChoice of resolvedChoices) {
|
|
285
|
+
if (resolvedChoice && resolvedChoice.choice === 'move' && resolvedChoice.move.id !== 'recharge') {
|
|
286
|
+
resolvedChoice.pokemon.side.lastSelectedMove = resolvedChoice.move.id;
|
|
287
|
+
}
|
|
287
288
|
}
|
|
288
289
|
}
|
|
289
290
|
}
|