@pkmn/sim 0.5.22 → 0.5.25
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/LICENSE +1 -1
- package/build/config/formats.js +581 -481
- package/build/config/formats.js.map +1 -1
- package/build/data/aliases.js +9 -5
- package/build/data/aliases.js.map +1 -1
- package/build/data/conditions.js +1 -1
- package/build/data/conditions.js.map +1 -1
- package/build/data/formats-data.js +830 -28
- package/build/data/formats-data.js.map +1 -1
- package/build/data/learnsets.js +64 -39
- 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 +5 -0
- package/build/data/mods/gen1/moves.js.map +1 -1
- package/build/data/mods/gen1/scripts.js +4 -2
- package/build/data/mods/gen1/scripts.js.map +1 -1
- package/build/data/mods/gen2/moves.js +0 -5
- package/build/data/mods/gen2/moves.js.map +1 -1
- package/build/data/mods/gen2/rulesets.js +7 -1
- package/build/data/mods/gen2/rulesets.js.map +1 -1
- package/build/data/mods/gen3/formats-data.js +3 -3
- package/build/data/mods/gen3/formats-data.js.map +1 -1
- package/build/data/mods/gen3/rulesets.js +1 -1
- package/build/data/mods/gen3/rulesets.js.map +1 -1
- package/build/data/mods/gen4/rulesets.js +1 -1
- package/build/data/mods/gen4/rulesets.js.map +1 -1
- package/build/data/mods/gen5/rulesets.js +1 -1
- package/build/data/mods/gen5/rulesets.js.map +1 -1
- package/build/data/mods/gen6/formats-data.js +64 -64
- package/build/data/mods/gen6/formats-data.js.map +1 -1
- package/build/data/mods/gen7/rulesets.js +1 -1
- package/build/data/mods/gen7/rulesets.js.map +1 -1
- package/build/data/pokedex.js +1 -1
- package/build/data/rulesets.js +232 -3
- package/build/data/rulesets.js.map +1 -1
- package/build/data/tags.js +26 -9
- package/build/data/tags.js.map +1 -1
- package/build/lib/utils.d.ts +4 -0
- package/build/lib/utils.js +20 -1
- package/build/lib/utils.js.map +1 -1
- package/build/sim/battle-stream.js +3 -0
- package/build/sim/battle-stream.js.map +1 -1
- package/build/sim/battle.d.ts +1 -1
- 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-species.d.ts +5 -0
- package/build/sim/dex-species.js +11 -3
- package/build/sim/dex-species.js.map +1 -1
- package/build/sim/exported-global-types.d.ts +4 -0
- package/build/sim/global-types.d.ts +4 -0
- package/build/sim/pokemon.d.ts +1 -0
- package/build/sim/pokemon.js +1 -0
- package/build/sim/pokemon.js.map +1 -1
- package/build/sim/team-validator.js +5 -1
- package/build/sim/team-validator.js.map +1 -1
- package/config/formats.ts +575 -466
- package/data/aliases.ts +9 -5
- package/data/conditions.ts +1 -1
- package/data/formats-data.ts +830 -28
- package/data/learnsets.ts +64 -39
- package/data/mods/gen1/formats-data.ts +1 -1
- package/data/mods/gen1/moves.ts +5 -0
- package/data/mods/gen1/scripts.ts +3 -2
- package/data/mods/gen2/moves.ts +0 -5
- package/data/mods/gen2/rulesets.ts +8 -1
- package/data/mods/gen3/formats-data.ts +3 -3
- package/data/mods/gen3/rulesets.ts +1 -1
- package/data/mods/gen4/rulesets.ts +1 -1
- package/data/mods/gen5/rulesets.ts +1 -1
- package/data/mods/gen6/formats-data.ts +64 -64
- package/data/mods/gen7/rulesets.ts +1 -1
- package/data/pokedex.ts +1 -1
- package/data/rulesets.ts +212 -3
- package/data/tags.ts +27 -9
- package/lib/utils.ts +16 -0
- package/package.json +2 -2
- package/sim/battle-stream.ts +3 -0
- package/sim/battle.ts +1 -1
- package/sim/dex-conditions.ts +1 -0
- package/sim/dex-species.ts +15 -3
- package/sim/exported-global-types.ts +4 -0
- package/sim/global-types.ts +4 -0
- package/sim/pokemon.ts +2 -0
- package/sim/team-validator.ts +6 -1
package/data/rulesets.ts
CHANGED
|
@@ -14,7 +14,7 @@ export const Rulesets: {[k: string]: FormatData} = {
|
|
|
14
14
|
name: 'Standard',
|
|
15
15
|
desc: "The standard ruleset for all offical Smogon singles tiers (Ubers, OU, etc.)",
|
|
16
16
|
ruleset: [
|
|
17
|
-
'Obtainable', 'Team Preview', 'Sleep Clause Mod', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Moves Clause', 'Endless Battle Clause', 'HP Percentage Mod', 'Cancel Mod',
|
|
17
|
+
'Obtainable', 'Team Preview', 'Sleep Clause Mod', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Items Clause', 'Evasion Moves Clause', 'Endless Battle Clause', 'HP Percentage Mod', 'Cancel Mod',
|
|
18
18
|
],
|
|
19
19
|
},
|
|
20
20
|
standardnext: {
|
|
@@ -71,6 +71,14 @@ export const Rulesets: {[k: string]: FormatData} = {
|
|
|
71
71
|
'Obtainable', 'Team Preview', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Moves Clause', 'Gravity Sleep Clause', 'Endless Battle Clause', 'HP Percentage Mod', 'Cancel Mod',
|
|
72
72
|
],
|
|
73
73
|
},
|
|
74
|
+
standardoms: {
|
|
75
|
+
effectType: 'ValidatorRule',
|
|
76
|
+
name: 'Standard OMs',
|
|
77
|
+
desc: "The standard ruleset for all Smogon OMs (Almost Any Ability, STABmons, etc.)",
|
|
78
|
+
ruleset: [
|
|
79
|
+
'Obtainable', 'Team Preview', 'Species Clause', 'Nickname Clause', 'OHKO Clause', 'Evasion Moves Clause', 'Endless Battle Clause', 'Dynamax Clause', 'HP Percentage Mod', 'Cancel Mod', 'Overflow Stat Mod',
|
|
80
|
+
],
|
|
81
|
+
},
|
|
74
82
|
standardnatdex: {
|
|
75
83
|
effectType: 'ValidatorRule',
|
|
76
84
|
name: 'Standard NatDex',
|
|
@@ -643,6 +651,15 @@ export const Rulesets: {[k: string]: FormatData} = {
|
|
|
643
651
|
return problems;
|
|
644
652
|
},
|
|
645
653
|
},
|
|
654
|
+
evasionclause: {
|
|
655
|
+
effectType: 'ValidatorRule',
|
|
656
|
+
name: 'Evasion Clause',
|
|
657
|
+
desc: "Bans abilities, items, and moves that boost Evasion",
|
|
658
|
+
ruleset: ['Evasion Abilities Clause', 'Evasion Items Clause', 'Evasion Moves Clause'],
|
|
659
|
+
onBegin() {
|
|
660
|
+
this.add('rule', 'Evasion Clause: Evasion abilities, items, and moves are banned');
|
|
661
|
+
},
|
|
662
|
+
},
|
|
646
663
|
evasionabilitiesclause: {
|
|
647
664
|
effectType: 'ValidatorRule',
|
|
648
665
|
name: 'Evasion Abilities Clause',
|
|
@@ -652,6 +669,15 @@ export const Rulesets: {[k: string]: FormatData} = {
|
|
|
652
669
|
this.add('rule', 'Evasion Abilities Clause: Evasion abilities are banned');
|
|
653
670
|
},
|
|
654
671
|
},
|
|
672
|
+
evasionitemsclause: {
|
|
673
|
+
effectType: 'ValidatorRule',
|
|
674
|
+
name: 'Evasion Items Clause',
|
|
675
|
+
desc: "Bans moves that lower the accuracy of moves used against the user",
|
|
676
|
+
banlist: ['Bright Powder', 'Lax Incense'],
|
|
677
|
+
onBegin() {
|
|
678
|
+
this.add('rule', 'Evasion Items Clause: Evasion items are banned');
|
|
679
|
+
},
|
|
680
|
+
},
|
|
655
681
|
evasionmovesclause: {
|
|
656
682
|
effectType: 'ValidatorRule',
|
|
657
683
|
name: 'Evasion Moves Clause',
|
|
@@ -1409,6 +1435,7 @@ export const Rulesets: {[k: string]: FormatData} = {
|
|
|
1409
1435
|
name: 'Team Type Preview',
|
|
1410
1436
|
desc: "Allows each player to see the Pokémon on their opponent's team and those Pokémon's types before they choose their lead Pokémon",
|
|
1411
1437
|
onTeamPreview() {
|
|
1438
|
+
this.add('clearpoke');
|
|
1412
1439
|
for (const side of this.sides) {
|
|
1413
1440
|
for (const pokemon of side.pokemon) {
|
|
1414
1441
|
const details = pokemon.details.replace(', shiny', '')
|
|
@@ -1869,8 +1896,7 @@ export const Rulesets: {[k: string]: FormatData} = {
|
|
|
1869
1896
|
reevolutionmod: {
|
|
1870
1897
|
effectType: "Rule",
|
|
1871
1898
|
name: "Re-Evolution Mod",
|
|
1872
|
-
desc: "Pokémon gain the
|
|
1873
|
-
ruleset: ['Overflow Stat Mod'],
|
|
1899
|
+
desc: "Pokémon gain the stat changes they would gain from evolving again.",
|
|
1874
1900
|
onBegin() {
|
|
1875
1901
|
this.add('rule', 'Re-Evolution Mod: Pok\u00e9mon gain the boosts they would gain from evolving again');
|
|
1876
1902
|
},
|
|
@@ -1888,4 +1914,187 @@ export const Rulesets: {[k: string]: FormatData} = {
|
|
|
1888
1914
|
return newSpecies;
|
|
1889
1915
|
},
|
|
1890
1916
|
},
|
|
1917
|
+
brokenrecordmod: {
|
|
1918
|
+
effectType: "Rule",
|
|
1919
|
+
name: "Broken Record Mod",
|
|
1920
|
+
desc: `Pokémon can hold a TR to use that move in battle.`,
|
|
1921
|
+
onValidateSet(set) {
|
|
1922
|
+
if (!set.item) return;
|
|
1923
|
+
const item = this.dex.items.get(set.item);
|
|
1924
|
+
if (!/^tr\d\d/i.test(item.name)) return;
|
|
1925
|
+
const moveName = item.desc.split('move ')[1].split('.')[0];
|
|
1926
|
+
if (set.moves.map(this.toID).includes(this.toID(moveName))) {
|
|
1927
|
+
return [
|
|
1928
|
+
`${set.species} can't run ${item.name} (${moveName}) as its item because it already has that move in its moveset.`,
|
|
1929
|
+
];
|
|
1930
|
+
}
|
|
1931
|
+
},
|
|
1932
|
+
onValidateTeam(team) {
|
|
1933
|
+
const trs = new Set<string>();
|
|
1934
|
+
for (const set of team) {
|
|
1935
|
+
if (!set.item) continue;
|
|
1936
|
+
const item = this.dex.items.get(set.item).name;
|
|
1937
|
+
if (!/^tr\d\d/i.test(item)) continue;
|
|
1938
|
+
if (trs.has(item)) {
|
|
1939
|
+
return [`Your team already has a Pok\u00e9mon with ${item}.`];
|
|
1940
|
+
}
|
|
1941
|
+
trs.add(item);
|
|
1942
|
+
}
|
|
1943
|
+
},
|
|
1944
|
+
onTakeItem(item) {
|
|
1945
|
+
return !/^tr\d\d/i.test(item.name);
|
|
1946
|
+
},
|
|
1947
|
+
onModifyMove(move) {
|
|
1948
|
+
if (move.id === 'knockoff') {
|
|
1949
|
+
move.onBasePower = function (basePower, source, target, m) {
|
|
1950
|
+
const item = target.getItem();
|
|
1951
|
+
if (!this.singleEvent('TakeItem', item, target.itemState, target, target, m, item)) return;
|
|
1952
|
+
// Very hardcode but I'd prefer to not make a mod for one damage calculation change
|
|
1953
|
+
if (item.id && !/^tr\d\d/i.test(item.id)) {
|
|
1954
|
+
return this.chainModify(1.5);
|
|
1955
|
+
}
|
|
1956
|
+
};
|
|
1957
|
+
} else if (move.id === 'fling') {
|
|
1958
|
+
move.onPrepareHit = function (target, source, m) {
|
|
1959
|
+
if (source.ignoringItem()) return false;
|
|
1960
|
+
const item = source.getItem();
|
|
1961
|
+
if (!this.singleEvent('TakeItem', item, source.itemState, source, source, m, item)) return false;
|
|
1962
|
+
if (!item.fling) return false;
|
|
1963
|
+
if (/^tr\d\d/i.test(item.id)) return false;
|
|
1964
|
+
m.basePower = item.fling.basePower;
|
|
1965
|
+
if (item.isBerry) {
|
|
1966
|
+
m.onHit = function (foe) {
|
|
1967
|
+
if (this.singleEvent('Eat', item, null, foe, null, null)) {
|
|
1968
|
+
this.runEvent('EatItem', foe, null, null, item);
|
|
1969
|
+
if (item.id === 'leppaberry') foe.staleness = 'external';
|
|
1970
|
+
}
|
|
1971
|
+
if (item.onEat) foe.ateBerry = true;
|
|
1972
|
+
};
|
|
1973
|
+
} else if (item.fling.effect) {
|
|
1974
|
+
m.onHit = item.fling.effect;
|
|
1975
|
+
} else {
|
|
1976
|
+
if (!m.secondaries) m.secondaries = [];
|
|
1977
|
+
if (item.fling.status) {
|
|
1978
|
+
m.secondaries.push({status: item.fling.status});
|
|
1979
|
+
} else if (item.fling.volatileStatus) {
|
|
1980
|
+
m.secondaries.push({volatileStatus: item.fling.volatileStatus});
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
source.addVolatile('fling');
|
|
1984
|
+
};
|
|
1985
|
+
}
|
|
1986
|
+
},
|
|
1987
|
+
onBegin() {
|
|
1988
|
+
for (const pokemon of this.getAllPokemon()) {
|
|
1989
|
+
const item = pokemon.getItem();
|
|
1990
|
+
if (/^tr\d\d/i.test(item.name)) {
|
|
1991
|
+
const move = this.dex.moves.get(item.desc.split('move ')[1].split('.')[0]);
|
|
1992
|
+
pokemon.moveSlots = (pokemon as any).baseMoveSlots = [
|
|
1993
|
+
...pokemon.baseMoveSlots, {
|
|
1994
|
+
id: move.id,
|
|
1995
|
+
move: move.name,
|
|
1996
|
+
pp: move.pp * 8 / 5,
|
|
1997
|
+
maxpp: move.pp * 8 / 5,
|
|
1998
|
+
target: move.target,
|
|
1999
|
+
disabled: false,
|
|
2000
|
+
disabledSource: '',
|
|
2001
|
+
used: false,
|
|
2002
|
+
},
|
|
2003
|
+
];
|
|
2004
|
+
}
|
|
2005
|
+
}
|
|
2006
|
+
},
|
|
2007
|
+
},
|
|
2008
|
+
categoryswapmod: {
|
|
2009
|
+
effectType: 'Rule',
|
|
2010
|
+
name: 'Category Swap Mod',
|
|
2011
|
+
desc: `All physical moves become special, and all special moves become physical.`,
|
|
2012
|
+
onBegin() {
|
|
2013
|
+
this.add('rule', 'Category Swap Mod: All physical moves become special, and vice versa');
|
|
2014
|
+
},
|
|
2015
|
+
onModifyMove(move, pokemon, target) {
|
|
2016
|
+
if (move.category === "Status") return;
|
|
2017
|
+
|
|
2018
|
+
if (move.category === "Physical") {
|
|
2019
|
+
move.category = "Special";
|
|
2020
|
+
} else if (move.category === "Special") {
|
|
2021
|
+
move.category = "Physical";
|
|
2022
|
+
}
|
|
2023
|
+
|
|
2024
|
+
switch (move.id) {
|
|
2025
|
+
case 'doomdesire': {
|
|
2026
|
+
move.onTry = function (source, subtarget) {
|
|
2027
|
+
if (!subtarget.side.addSlotCondition(subtarget, 'futuremove')) return false;
|
|
2028
|
+
Object.assign(subtarget.side.slotConditions[subtarget.position]['futuremove'], {
|
|
2029
|
+
move: 'doomdesire',
|
|
2030
|
+
source: source,
|
|
2031
|
+
moveData: {
|
|
2032
|
+
id: 'doomdesire',
|
|
2033
|
+
name: "Doom Desire",
|
|
2034
|
+
accuracy: 100,
|
|
2035
|
+
basePower: 140,
|
|
2036
|
+
category: "Physical",
|
|
2037
|
+
priority: 0,
|
|
2038
|
+
flags: {},
|
|
2039
|
+
effectType: 'Move',
|
|
2040
|
+
isFutureMove: true,
|
|
2041
|
+
type: 'Steel',
|
|
2042
|
+
},
|
|
2043
|
+
});
|
|
2044
|
+
this.add('-start', source, 'Doom Desire');
|
|
2045
|
+
return this.NOT_FAIL;
|
|
2046
|
+
};
|
|
2047
|
+
break;
|
|
2048
|
+
}
|
|
2049
|
+
case 'futuresight': {
|
|
2050
|
+
move.onTry = function (source, subtarget) {
|
|
2051
|
+
if (!subtarget.side.addSlotCondition(subtarget, 'futuremove')) return false;
|
|
2052
|
+
Object.assign(subtarget.side.slotConditions[subtarget.position]['futuremove'], {
|
|
2053
|
+
duration: 3,
|
|
2054
|
+
move: 'futuresight',
|
|
2055
|
+
source: source,
|
|
2056
|
+
moveData: {
|
|
2057
|
+
id: 'futuresight',
|
|
2058
|
+
name: "Future Sight",
|
|
2059
|
+
accuracy: 100,
|
|
2060
|
+
basePower: 120,
|
|
2061
|
+
category: "Physical",
|
|
2062
|
+
priority: 0,
|
|
2063
|
+
flags: {},
|
|
2064
|
+
ignoreImmunity: false,
|
|
2065
|
+
effectType: 'Move',
|
|
2066
|
+
isFutureMove: true,
|
|
2067
|
+
type: 'Psychic',
|
|
2068
|
+
},
|
|
2069
|
+
});
|
|
2070
|
+
this.add('-start', source, 'move: Future Sight');
|
|
2071
|
+
return this.NOT_FAIL;
|
|
2072
|
+
};
|
|
2073
|
+
break;
|
|
2074
|
+
}
|
|
2075
|
+
// Moves with dynamic categories will always be physical if not special-cased
|
|
2076
|
+
case 'lightthatburnsthesky':
|
|
2077
|
+
case 'photongeyser': {
|
|
2078
|
+
move.category = 'Special';
|
|
2079
|
+
if (pokemon.getStat('atk', false, true) > pokemon.getStat('spa', false, true)) move.category = 'Physical';
|
|
2080
|
+
break;
|
|
2081
|
+
}
|
|
2082
|
+
case 'shellsidearm': {
|
|
2083
|
+
if (!target) return;
|
|
2084
|
+
move.category = 'Special';
|
|
2085
|
+
const atk = pokemon.getStat('atk', false, true);
|
|
2086
|
+
const spa = pokemon.getStat('spa', false, true);
|
|
2087
|
+
const def = target.getStat('def', false, true);
|
|
2088
|
+
const spd = target.getStat('spd', false, true);
|
|
2089
|
+
const physical = Math.floor(Math.floor(Math.floor(Math.floor(2 * pokemon.level / 5 + 2) * 90 * atk) / def) / 50);
|
|
2090
|
+
const special = Math.floor(Math.floor(Math.floor(Math.floor(2 * pokemon.level / 5 + 2) * 90 * spa) / spd) / 50);
|
|
2091
|
+
if (physical > special || (physical === special && this.random(2) === 0)) {
|
|
2092
|
+
move.category = 'Physical';
|
|
2093
|
+
move.flags.contact = 1;
|
|
2094
|
+
}
|
|
2095
|
+
break;
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
},
|
|
2099
|
+
},
|
|
1891
2100
|
};
|
package/data/tags.ts
CHANGED
|
@@ -194,15 +194,6 @@ export const Tags: {[id: string]: TagData} = {
|
|
|
194
194
|
name: "AG",
|
|
195
195
|
speciesFilter: species => species.tier === 'AG',
|
|
196
196
|
},
|
|
197
|
-
nduubl: {
|
|
198
|
-
name: "ND UUBL",
|
|
199
|
-
speciesFilter: species => [
|
|
200
|
-
'Aerodactyl-Mega', 'Alakazam', 'Blacephalon', 'Blaziken', 'Diancie-Mega', 'Gallade-Mega', 'Gardevoir-Mega', 'Gengar', 'Gyarados',
|
|
201
|
-
'Gyarados-Mega', 'Hawlucha', 'Heracross-Mega', 'Hoopa-Unbound', 'Hydreigon', 'Jirachi', 'Latias', 'Latias-Mega', 'Latios',
|
|
202
|
-
'Latios-Mega', 'Manaphy', 'Medicham-Mega', 'Melmetal', 'Mew', 'Moltres-Galar', 'Pinsir-Mega', 'Sableye-Mega', 'Slowbro-Mega',
|
|
203
|
-
'Slowking-Galar', 'Thundurus', 'Venusaur-Mega', 'Xurkitree', 'Zapdos-Galar',
|
|
204
|
-
].includes(species.name),
|
|
205
|
-
},
|
|
206
197
|
|
|
207
198
|
// Doubles tiers
|
|
208
199
|
// -------------
|
|
@@ -227,6 +218,33 @@ export const Tags: {[id: string]: TagData} = {
|
|
|
227
218
|
speciesFilter: species => species.doublesTier === '(DUU)',
|
|
228
219
|
},
|
|
229
220
|
|
|
221
|
+
// Nat Dex tiers
|
|
222
|
+
// -------------
|
|
223
|
+
nduber: {
|
|
224
|
+
name: "ND Uber",
|
|
225
|
+
speciesFilter: species => species.natDexTier === 'Uber' || species.natDexTier === '(Uber)',
|
|
226
|
+
},
|
|
227
|
+
ndou: {
|
|
228
|
+
name: "ND OU",
|
|
229
|
+
speciesFilter: species => species.natDexTier === 'OU' || species.natDexTier === '(OU)',
|
|
230
|
+
},
|
|
231
|
+
nduubl: {
|
|
232
|
+
name: "ND UUBL",
|
|
233
|
+
speciesFilter: species => species.natDexTier === 'UUBL',
|
|
234
|
+
},
|
|
235
|
+
nduu: {
|
|
236
|
+
name: "ND UU",
|
|
237
|
+
speciesFilter: species => species.natDexTier === 'UU',
|
|
238
|
+
},
|
|
239
|
+
ndrubl: {
|
|
240
|
+
name: "ND RUBL",
|
|
241
|
+
speciesFilter: species => species.natDexTier === 'RUBL',
|
|
242
|
+
},
|
|
243
|
+
ndru: {
|
|
244
|
+
name: "ND RU",
|
|
245
|
+
speciesFilter: species => species.natDexTier === 'RU',
|
|
246
|
+
},
|
|
247
|
+
|
|
230
248
|
// Legality tags
|
|
231
249
|
past: {
|
|
232
250
|
name: "Past",
|
package/lib/utils.ts
CHANGED
|
@@ -63,6 +63,22 @@ export function stripHTML(htmlContent: string) {
|
|
|
63
63
|
return htmlContent.replace(/<[^>]*>/g, '');
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Maps numbers to their ordinal string.
|
|
68
|
+
*/
|
|
69
|
+
export function formatOrder(place: number) {
|
|
70
|
+
// anything between 10 and 20 should always end with -th
|
|
71
|
+
let remainder = place % 100;
|
|
72
|
+
if (remainder >= 10 && remainder <= 20) return place + 'th';
|
|
73
|
+
|
|
74
|
+
// follow standard rules with -st, -nd, -rd, and -th
|
|
75
|
+
remainder = place % 10;
|
|
76
|
+
if (remainder === 1) return place + 'st';
|
|
77
|
+
if (remainder === 2) return place + 'nd';
|
|
78
|
+
if (remainder === 3) return place + 'rd';
|
|
79
|
+
return place + 'th';
|
|
80
|
+
}
|
|
81
|
+
|
|
66
82
|
/**
|
|
67
83
|
* Visualizes eval output in a slightly more readable form
|
|
68
84
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pkmn/sim",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.25",
|
|
4
4
|
"description": "An automatically generated extraction of just the simulator portion of Pokémon Showdown",
|
|
5
5
|
"homepage": "https://psim.us",
|
|
6
6
|
"main": "build/sim/index.js",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"sim"
|
|
28
28
|
],
|
|
29
29
|
"dependencies": {
|
|
30
|
-
"@pkmn/sets": "^3.
|
|
30
|
+
"@pkmn/sets": "^3.1.1",
|
|
31
31
|
"@pkmn/streams": "^1.0.0"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
package/sim/battle-stream.ts
CHANGED
|
@@ -211,6 +211,9 @@ export class BattleStream extends Streams.ObjectReadWriteStream<string> {
|
|
|
211
211
|
case 'requestlog':
|
|
212
212
|
this.push(`requesteddata\n${this.battle!.inputLog.join('\n')}`);
|
|
213
213
|
break;
|
|
214
|
+
case 'requestexport':
|
|
215
|
+
this.push(`requesteddata\n${this.battle!.prngSeed}\n${this.battle!.inputLog.join('\n')}`);
|
|
216
|
+
break;
|
|
214
217
|
case 'requestteam':
|
|
215
218
|
message = message.trim();
|
|
216
219
|
const slotNum = parseInt(message.slice(1)) - 1;
|
package/sim/battle.ts
CHANGED
|
@@ -399,7 +399,7 @@ export class Battle {
|
|
|
399
399
|
}
|
|
400
400
|
|
|
401
401
|
/** Sort a list, resolving speed ties the way the games do. */
|
|
402
|
-
speedSort<T>(list: T[], comparator: (a: T, b: T) => number = this.comparePriority) {
|
|
402
|
+
speedSort<T extends AnyObject>(list: T[], comparator: (a: T, b: T) => number = this.comparePriority) {
|
|
403
403
|
if (list.length < 2) return;
|
|
404
404
|
let sorted = 0;
|
|
405
405
|
// This is a Selection Sort - not the fastest sort in general, but
|
package/sim/dex-conditions.ts
CHANGED
|
@@ -473,6 +473,7 @@ export interface EventMethods {
|
|
|
473
473
|
onModifyPriorityPriority?: number;
|
|
474
474
|
onModifySpAPriority?: number;
|
|
475
475
|
onModifySpDPriority?: number;
|
|
476
|
+
onModifySpePriority?: number;
|
|
476
477
|
onModifyTypePriority?: number;
|
|
477
478
|
onModifyWeightPriority?: number;
|
|
478
479
|
onRedirectTargetPriority?: number;
|
package/sim/dex-species.ts
CHANGED
|
@@ -42,6 +42,7 @@ export interface SpeciesFormatsData {
|
|
|
42
42
|
exclusiveMoves?: readonly string[];
|
|
43
43
|
gmaxUnreleased?: boolean;
|
|
44
44
|
isNonstandard?: Nonstandard | null;
|
|
45
|
+
natDexTier?: TierTypes.Singles | TierTypes.Other;
|
|
45
46
|
randomBattleMoves?: readonly string[];
|
|
46
47
|
randomBattleLevel?: number;
|
|
47
48
|
randomDoubleBattleMoves?: readonly string[];
|
|
@@ -231,6 +232,10 @@ export class Species extends BasicEffect implements Readonly<BasicEffect & Speci
|
|
|
231
232
|
* Doubles Tier. The Pokemon's location in the Smogon doubles tier system.
|
|
232
233
|
*/
|
|
233
234
|
readonly doublesTier: TierTypes.Doubles | TierTypes.Other;
|
|
235
|
+
/**
|
|
236
|
+
* National Dex Tier. The Pokemon's location in the Smogon National Dex tier system.
|
|
237
|
+
*/
|
|
238
|
+
readonly natDexTier: TierTypes.Singles | TierTypes.Other;
|
|
234
239
|
declare readonly randomBattleMoves?: readonly ID[];
|
|
235
240
|
declare readonly randomBattleLevel?: number;
|
|
236
241
|
declare readonly randomDoubleBattleMoves?: readonly ID[];
|
|
@@ -261,6 +266,7 @@ export class Species extends BasicEffect implements Readonly<BasicEffect & Speci
|
|
|
261
266
|
this.prevo = data.prevo || '';
|
|
262
267
|
this.tier = data.tier || '';
|
|
263
268
|
this.doublesTier = data.doublesTier || '';
|
|
269
|
+
this.natDexTier = data.natDexTier || '';
|
|
264
270
|
this.evos = data.evos || [];
|
|
265
271
|
this.evoType = data.evoType || undefined;
|
|
266
272
|
this.evoMove = data.evoMove || undefined;
|
|
@@ -451,16 +457,19 @@ export class DexSpecies {
|
|
|
451
457
|
if (!(key in species)) (species as any)[key] = baseSpeciesStatuses[key];
|
|
452
458
|
}
|
|
453
459
|
}
|
|
454
|
-
if (!species.tier && !species.doublesTier && species.baseSpecies !== species.name) {
|
|
460
|
+
if (!species.tier && !species.doublesTier && !species.natDexTier && species.baseSpecies !== species.name) {
|
|
455
461
|
if (species.baseSpecies === 'Mimikyu') {
|
|
456
462
|
species.tier = this.dex.data.FormatsData[toID(species.baseSpecies)].tier || 'Illegal';
|
|
457
463
|
species.doublesTier = this.dex.data.FormatsData[toID(species.baseSpecies)].doublesTier || 'Illegal';
|
|
464
|
+
species.natDexTier = this.dex.data.FormatsData[toID(species.baseSpecies)].natDexTier || 'Illegal';
|
|
458
465
|
} else if (species.id.endsWith('totem')) {
|
|
459
466
|
species.tier = this.dex.data.FormatsData[species.id.slice(0, -5)].tier || 'Illegal';
|
|
460
467
|
species.doublesTier = this.dex.data.FormatsData[species.id.slice(0, -5)].doublesTier || 'Illegal';
|
|
468
|
+
species.natDexTier = this.dex.data.FormatsData[species.id.slice(0, -5)].natDexTier || 'Illegal';
|
|
461
469
|
} else if (species.battleOnly) {
|
|
462
470
|
species.tier = this.dex.data.FormatsData[toID(species.battleOnly)].tier || 'Illegal';
|
|
463
471
|
species.doublesTier = this.dex.data.FormatsData[toID(species.battleOnly)].doublesTier || 'Illegal';
|
|
472
|
+
species.natDexTier = this.dex.data.FormatsData[toID(species.battleOnly)].natDexTier || 'Illegal';
|
|
464
473
|
} else {
|
|
465
474
|
const baseFormatsData = this.dex.data.FormatsData[toID(species.baseSpecies)];
|
|
466
475
|
if (!baseFormatsData) {
|
|
@@ -468,13 +477,16 @@ export class DexSpecies {
|
|
|
468
477
|
}
|
|
469
478
|
species.tier = baseFormatsData.tier || 'Illegal';
|
|
470
479
|
species.doublesTier = baseFormatsData.doublesTier || 'Illegal';
|
|
480
|
+
species.natDexTier = baseFormatsData.natDexTier || 'Illegal';
|
|
471
481
|
}
|
|
472
482
|
}
|
|
473
483
|
if (!species.tier) species.tier = 'Illegal';
|
|
474
484
|
if (!species.doublesTier) species.doublesTier = species.tier as any;
|
|
485
|
+
if (!species.natDexTier) species.natDexTier = species.tier;
|
|
475
486
|
if (species.gen > this.dex.gen) {
|
|
476
487
|
species.tier = 'Illegal';
|
|
477
488
|
species.doublesTier = 'Illegal';
|
|
489
|
+
species.natDexTier = 'Illegal';
|
|
478
490
|
species.isNonstandard = 'Future';
|
|
479
491
|
}
|
|
480
492
|
if (this.dex.currentMod === 'gen7letsgo' && !species.isNonstandard) {
|
|
@@ -490,7 +502,7 @@ export class DexSpecies {
|
|
|
490
502
|
if (species.gen > 4 || (species.num < 1 && species.isNonstandard !== 'CAP') ||
|
|
491
503
|
species.id === 'pichuspikyeared') {
|
|
492
504
|
species.isNonstandard = 'Future';
|
|
493
|
-
species.tier = species.doublesTier = 'Illegal';
|
|
505
|
+
species.tier = species.doublesTier = species.natDexTier = 'Illegal';
|
|
494
506
|
}
|
|
495
507
|
}
|
|
496
508
|
species.nfe = species.evos.some(evo => {
|
|
@@ -505,7 +517,7 @@ export class DexSpecies {
|
|
|
505
517
|
} else {
|
|
506
518
|
species = new Species({
|
|
507
519
|
id, name: id,
|
|
508
|
-
exists: false, tier: 'Illegal', doublesTier: 'Illegal', isNonstandard: 'Custom',
|
|
520
|
+
exists: false, tier: 'Illegal', doublesTier: 'Illegal', natDexTier: 'Illegal', isNonstandard: 'Custom',
|
|
509
521
|
});
|
|
510
522
|
}
|
|
511
523
|
(species as any).kind = 'Species';
|
|
@@ -312,6 +312,7 @@ 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
|
+
eatItem?: (this: Pokemon, force?: boolean, source?: Pokemon, sourceEffect?: Effect) => boolean;
|
|
315
316
|
formeChange?: (
|
|
316
317
|
this: Pokemon, speciesId: string | Species, source: Effect, isPermanent?: boolean, message?: string
|
|
317
318
|
) => boolean;
|
|
@@ -344,6 +345,7 @@ export interface ModdedBattlePokemon {
|
|
|
344
345
|
) => boolean;
|
|
345
346
|
takeItem?: (this: Pokemon, source: Pokemon | undefined) => boolean | Item;
|
|
346
347
|
transformInto?: (this: Pokemon, pokemon: Pokemon, effect: Effect | null) => boolean;
|
|
348
|
+
useItem?: (this: Pokemon, source?: Pokemon, sourceEffect?: Effect) => boolean;
|
|
347
349
|
ignoringAbility?: (this: Pokemon) => boolean;
|
|
348
350
|
ignoringItem?: (this: Pokemon) => boolean;
|
|
349
351
|
|
|
@@ -515,6 +517,7 @@ export namespace RandomTeamsTypes {
|
|
|
515
517
|
shiny: boolean;
|
|
516
518
|
nature?: string;
|
|
517
519
|
happiness?: number;
|
|
520
|
+
dynamaxLevel?: number;
|
|
518
521
|
gigantamax?: boolean;
|
|
519
522
|
}
|
|
520
523
|
export interface RandomFactorySet {
|
|
@@ -530,6 +533,7 @@ export namespace RandomTeamsTypes {
|
|
|
530
533
|
ivs: SparseStatsTable;
|
|
531
534
|
nature: string;
|
|
532
535
|
moves: string[];
|
|
536
|
+
dynamaxLevel?: number;
|
|
533
537
|
gigantamax?: boolean;
|
|
534
538
|
}
|
|
535
539
|
}
|
package/sim/global-types.ts
CHANGED
|
@@ -312,6 +312,7 @@ 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
|
+
eatItem?: (this: Pokemon, force?: boolean, source?: Pokemon, sourceEffect?: Effect) => boolean;
|
|
315
316
|
formeChange?: (
|
|
316
317
|
this: Pokemon, speciesId: string | Species, source: Effect, isPermanent?: boolean, message?: string
|
|
317
318
|
) => boolean;
|
|
@@ -344,6 +345,7 @@ interface ModdedBattlePokemon {
|
|
|
344
345
|
) => boolean;
|
|
345
346
|
takeItem?: (this: Pokemon, source: Pokemon | undefined) => boolean | Item;
|
|
346
347
|
transformInto?: (this: Pokemon, pokemon: Pokemon, effect: Effect | null) => boolean;
|
|
348
|
+
useItem?: (this: Pokemon, source?: Pokemon, sourceEffect?: Effect) => boolean;
|
|
347
349
|
ignoringAbility?: (this: Pokemon) => boolean;
|
|
348
350
|
ignoringItem?: (this: Pokemon) => boolean;
|
|
349
351
|
|
|
@@ -515,6 +517,7 @@ namespace RandomTeamsTypes {
|
|
|
515
517
|
shiny: boolean;
|
|
516
518
|
nature?: string;
|
|
517
519
|
happiness?: number;
|
|
520
|
+
dynamaxLevel?: number;
|
|
518
521
|
gigantamax?: boolean;
|
|
519
522
|
}
|
|
520
523
|
export interface RandomFactorySet {
|
|
@@ -530,6 +533,7 @@ namespace RandomTeamsTypes {
|
|
|
530
533
|
ivs: SparseStatsTable;
|
|
531
534
|
nature: string;
|
|
532
535
|
moves: string[];
|
|
536
|
+
dynamaxLevel?: number;
|
|
533
537
|
gigantamax?: boolean;
|
|
534
538
|
}
|
|
535
539
|
}
|
package/sim/pokemon.ts
CHANGED
|
@@ -77,6 +77,7 @@ export class Pokemon {
|
|
|
77
77
|
readonly gender: GenderName;
|
|
78
78
|
readonly happiness: number;
|
|
79
79
|
readonly pokeball: string;
|
|
80
|
+
readonly dynamaxLevel: number;
|
|
80
81
|
readonly gigantamax: boolean;
|
|
81
82
|
|
|
82
83
|
/** Transform keeps the original pre-transformed Hidden Power in Gen 2-4. */
|
|
@@ -326,6 +327,7 @@ export class Pokemon {
|
|
|
326
327
|
if (this.gender === 'N') this.gender = '';
|
|
327
328
|
this.happiness = typeof set.happiness === 'number' ? this.battle.clampIntRange(set.happiness, 0, 255) : 255;
|
|
328
329
|
this.pokeball = this.set.pokeball || 'pokeball';
|
|
330
|
+
this.dynamaxLevel = typeof set.dynamaxLevel === 'number' ? this.battle.clampIntRange(set.dynamaxLevel, 0, 10) : 10;
|
|
329
331
|
this.gigantamax = this.set.gigantamax || false;
|
|
330
332
|
|
|
331
333
|
this.baseMoveSlots = [];
|
package/sim/team-validator.ts
CHANGED
|
@@ -1366,6 +1366,11 @@ export class TeamValidator {
|
|
|
1366
1366
|
const doublesTierTag = 'pokemontag:' + toID(doublesTier);
|
|
1367
1367
|
setHas[doublesTierTag] = true;
|
|
1368
1368
|
|
|
1369
|
+
const ndTier = tierSpecies.natDexTier === '(PU)' ? 'ZU' :
|
|
1370
|
+
tierSpecies.natDexTier === '(NU)' ? 'PU' : tierSpecies.natDexTier;
|
|
1371
|
+
const ndTierTag = 'pokemontag:nd' + toID(ndTier);
|
|
1372
|
+
setHas[ndTierTag] = true;
|
|
1373
|
+
|
|
1369
1374
|
// Only pokemon that can gigantamax should have the Gmax flag
|
|
1370
1375
|
if (!tierSpecies.canGigantamax && set.gigantamax) {
|
|
1371
1376
|
return `${tierSpecies.name} cannot Gigantamax but is flagged as being able to.`;
|
|
@@ -2206,7 +2211,7 @@ export class TeamValidator {
|
|
|
2206
2211
|
// different learnsets. To prevent a leak, we make them show up as their
|
|
2207
2212
|
// base forme, but hardcode their learnsets into Rockruff-Dusk and
|
|
2208
2213
|
// Greninja-Ash
|
|
2209
|
-
if (
|
|
2214
|
+
if (['Gastrodon', 'Pumpkaboo', 'Sinistea'].includes(species.baseSpecies) && species.forme) {
|
|
2210
2215
|
return this.dex.species.get(species.baseSpecies);
|
|
2211
2216
|
} else if (species.name === 'Lycanroc-Dusk') {
|
|
2212
2217
|
return this.dex.species.get('Rockruff-Dusk');
|