diff --git a/CODEOWNERS b/CODEOWNERS
new file mode 100644
index 000000000..49b6cf396
--- /dev/null
+++ b/CODEOWNERS
@@ -0,0 +1,2 @@
+play.pokemonshowdown.com/src/battle-animations-moves.ts @KrisXV
+play.pokemonshowdown.com/src/battle-animations.ts @KrisXV
\ No newline at end of file
diff --git a/WEB-API.md b/WEB-API.md
index d9b371d22..64f9cd57e 100644
--- a/WEB-API.md
+++ b/WEB-API.md
@@ -15,6 +15,14 @@ https://replay.pokemonshowdown.com/gen8doublesubers-1097585496.json
https://replay.pokemonshowdown.com/gen8doublesubers-1097585496.log
+Getting a replay inputlog directly (only for formats where the team is autogenerated):
+
+https://replay.pokemonshowdown.com/gen8randombattle-2005209836.inputlog
+
+Replay logs and inputlogs are also available in the JSON, so the `.log` and `.inputlog` forms are provided only for convenience.
+
+Also for convenience: scrolling down in the source code for the replay page. Obviously don't _scrape_ it, but `ctrl`+`u` is way faster than futzing with URLs if you just wanted to take a look at it.
+
Replay search
-------------
@@ -41,11 +49,11 @@ https://replay.pokemonshowdown.com/search.json?user=zarel&user2=yuyuko&format=ge
Paginate searches:
-https://replay.pokemonshowdown.com/search.json?user=zarel&page=2
+https://replay.pokemonshowdown.com/search.json?user=zarel&before=1372221987
-Searches are limited to 51 results, and pages are offset by 50 each, so the existence of a 51st result means that there's at least one more page available.
+Searches are limited to 51 results, and pages are offset by 50 each, so the existence of a 51st result means that there's at least one more page available. For the timestamp, use the uploadtime of the last replay in the list.
-Pagination is not supported for the recent replays list, but is supported for everything else.
+You can also use `page=[number]`, but this is an older API that is poorly supported and should not be relied upon.
Users (including ladder information)
diff --git a/build-tools/build-indexes b/build-tools/build-indexes
index 27ec32057..af79779c5 100755
--- a/build-tools/build-indexes
+++ b/build-tools/build-indexes
@@ -108,7 +108,7 @@ function buildSearchIndex() {
index = index.concat(Object.keys(Dex.data.TypeChart).map(x => toID(x) + ' type'));
index = index.concat(['physical', 'special', 'status'].map(x => toID(x) + ' category'));
index = index.concat(['monster', 'water1', 'bug', 'flying', 'field', 'fairy', 'grass', 'humanlike', 'water3', 'mineral', 'amorphous', 'water2', 'ditto', 'dragon', 'undiscovered'].map(x => toID(x) + ' egggroup'));
- index = index.concat(['ou', 'uu', 'ru', 'nu', 'pu', 'lc', 'nfe', 'uber', 'uubl', 'rubl', 'nubl', 'publ', 'cap', 'caplc', 'capnfe'].map(x => toID(x) + ' tier'));
+ index = index.concat(['ou', 'uu', 'ru', 'nu', 'pu', 'zu', 'lc', 'nfe', 'uber', 'uubl', 'rubl', 'nubl', 'publ', 'zubl', 'cap', 'caplc', 'capnfe'].map(x => toID(x) + ' tier'));
let BattleArticleTitles = {};
@@ -400,7 +400,7 @@ function buildTeambuilderTables() {
const LC = GENS.map(num => num + 0.7);
const STADIUM = [2.04, 1.04];
const NATDEX = [9.1, 8.1];
- const OTHER = [9.9, 9.41, 9.4, 9.2, -9.4, 8.6, 8.4, 8.2, 8.1, -8.4, -8.6, 7.1];
+ const OTHER = [9.9, 9.411, 9.41, 9.401, 9.4, 9.2, -9.4, -9.401, 8.6, 8.4, 8.2, 8.1, -8.4, -8.6, 7.1];
// process.stdout.write("\n ");
for (const genIdent of [...GENS, ...DOUBLES, ...VGC, ...NFE, ...STADIUM, ...OTHER, ...NATDEX, ...LC]) {
@@ -411,6 +411,7 @@ function buildTeambuilderTables() {
const isLC = ('' + genIdent).endsWith('.7');
const isSSDLC1 = (genIdent === 8.4 || genIdent === -8.4);
const isPreDLC = (genIdent === 9.4 || genIdent === 9.41 || genIdent === -9.4);
+ const isSVDLC1 = (genIdent === 9.401 || genIdent === 9.411 || genIdent === -9.401);
const isNatDex = ('' + genIdent).endsWith('.1') && genIdent > 8;
const isStadium = ('' + genIdent).endsWith('.04');
const isDoubles = (genIdent < 0);
@@ -423,6 +424,7 @@ function buildTeambuilderTables() {
if (isLetsGo) genStr += 'letsgo';
if (isBDSP) genStr += 'bdsp';
if (isPreDLC) genStr += 'predlc';
+ if (isSVDLC1) genStr += 'dlc1';
if (isStadium) genStr += 'stadium' + (genNum > 1 ? genNum : '');
return genStr;
})();
@@ -431,7 +433,7 @@ function buildTeambuilderTables() {
pokemon.sort();
const tierTable = {};
const overrideTier = {};
- const zuBans = {};
+ const ubersUUBans = {};
const monotypeBans = {};
const nonstandardMoves = [];
for (const id of pokemon) {
@@ -521,7 +523,7 @@ function buildTeambuilderTables() {
if (isDoubles && genNum > 4) {
return species.doublesTier;
}
- if (isNatDex || (isPreDLC && genNum === 9.41)) {
+ if (isNatDex || (isPreDLC && genNum === 9.41) || (isSVDLC1 && genNum === 9.411)) {
return species.natDexTier;
}
return species.tier;
@@ -542,10 +544,9 @@ function buildTeambuilderTables() {
tierTable[tier].push(id);
if (genNum === 9) {
- const zu = Dex.formats.get(gen + 'zu');
- if (zu.exists && Dex.formats.getRuleTable(zu).isBannedSpecies(species) &&
- ["(PU)", "NFE", "LC"].includes(species.tier)) {
- zuBans[species.id] = 1;
+ const ubersUU = Dex.formats.get(gen + 'ubersuu');
+ if (ubersUU.exists && Dex.formats.getRuleTable(ubersUU).isBannedSpecies(species)) {
+ ubersUUBans[species.id] = 1;
}
const mono = Dex.formats.get(gen + (isNatDex ? 'nationaldex' : '') + 'monotype');
if (Dex.formats.getRuleTable(mono).isBannedSpecies(species)) {
@@ -555,7 +556,7 @@ function buildTeambuilderTables() {
}
nonstandardMoves.push(...Object.keys(Dex.data.Moves).filter(id => {
- const move = Dex.mod(isSSDLC1 ? 'gen8dlc1' : 'gen9predlc').moves.get(id);
+ const move = Dex.mod(isSSDLC1 ? 'gen8dlc1' : isPreDLC ? 'gen9predlc' : 'gen9dlc1').moves.get(id);
const bMove = Dex.mod(isSSDLC1 ? 'gen8' : 'gen9').moves.get(id);
return bMove.isNonstandard !== move.isNonstandard;
}));
@@ -564,7 +565,7 @@ function buildTeambuilderTables() {
const items = [];
const formatSlices = {};
- if (isNatDex || (isPreDLC && genNum === 9.41)) {
+ if (isNatDex || (isPreDLC && genNum === 9.41) || (isSVDLC1 && genNum === 9.411)) {
BattleTeambuilderTable['gen' + genNum + 'natdex'] = {};
BattleTeambuilderTable['gen' + genNum + 'natdex'].tiers = tiers;
BattleTeambuilderTable['gen' + genNum + 'natdex'].overrideTier = overrideTier;
@@ -618,7 +619,7 @@ function buildTeambuilderTables() {
BattleTeambuilderTable.tiers = tiers;
BattleTeambuilderTable.items = items;
BattleTeambuilderTable.overrideTier = overrideTier;
- BattleTeambuilderTable.zuBans = zuBans;
+ BattleTeambuilderTable.ubersUUBans = ubersUUBans;
BattleTeambuilderTable.monotypeBans = monotypeBans;
BattleTeambuilderTable.formatSlices = formatSlices;
} else {
@@ -630,7 +631,7 @@ function buildTeambuilderTables() {
if (genNum >= 5) {
BattleTeambuilderTable[gen].monotypeBans = monotypeBans;
}
- if (isSSDLC1 || isPreDLC) {
+ if (isSSDLC1 || isPreDLC || isSVDLC1) {
BattleTeambuilderTable[gen].nonstandardMoves = nonstandardMoves;
BattleTeambuilderTable[gen].learnsets = {};
}
@@ -646,13 +647,12 @@ function buildTeambuilderTables() {
if (gen === 'gen4') {
return ["CAP", "CAP NFE", "CAP LC", "AG", "Uber", "OU", "(OU)", "UUBL", "UU", "NUBL", "NU", "NFE", "LC"];
}
- return ["CAP", "CAP NFE", "CAP LC", "AG", "Uber", "(Uber)", "OU", "(OU)", "UUBL", "UU", "RUBL", "RU", "NUBL", "NU", "PUBL", "PU", "ZUBL", "ZU", "(PU)", "New", "NFE", "LC", "Unreleased"];
+ return ["CAP", "CAP NFE", "CAP LC", "AG", "Uber", "(Uber)", "OU", "(OU)", "UUBL", "UU", "RUBL", "RU", "NUBL", "NU", "PUBL", "PU", "ZUBL", "ZU", "New", "NFE", "LC", "Unreleased"];
})();
for (const tier of tierOrder) {
- if (tier in {OU:1, AG:1, Uber:1, UU:1, RU:1, NU:1, PU:1, ZU: 1, "(PU)":1, NFE:1, LC:1, DOU:1, DUU:1, "(DUU)":1, New:1, Legal:1, Regular:1, "Restricted Legendary":1, "CAP LC":1}) {
+ if (tier in {OU:1, AG:1, Uber:1, UU:1, RU:1, NU:1, PU:1, ZU: 1, NFE:1, LC:1, DOU:1, DUU:1, "(DUU)":1, New:1, Legal:1, Regular:1, "Restricted Legendary":1, "CAP LC":1}) {
let usedTier = tier;
- if (usedTier === "(PU)" && genNum === 9) usedTier = "ZU";
if (usedTier === "(DUU)") usedTier = "DNU";
formatSlices[usedTier] = tiers.length;
}
@@ -694,8 +694,13 @@ function buildTeambuilderTables() {
}
if (item.isNonstandard && !isMetBattle) {
if (isNatDex) {
- if (item.isNonstandard !== "Past") continue;
- if (!item.itemUser && !item.zMove) continue;
+ let curItem = item;
+ let curGen = genNum;
+ while (item.isNonstandard && curGen >= 7) {
+ curItem = Dex.forGen(curGen).items.get(item.id);
+ curGen--;
+ }
+ if (curItem.isNonstandard) continue;
} else if (genNum !== 2) {
continue;
}
@@ -961,6 +966,52 @@ function buildTeambuilderTables() {
}
}
}
+ const SVDLC1Learnsets = Dex.mod('gen9dlc1').data.Learnsets;
+ for (const id in SVDLC1Learnsets) {
+ const learnset = SVDLC1Learnsets[id].learnset;
+ if (!learnset) continue;
+ BattleTeambuilderTable['gen9dlc1'].learnsets[id] = {};
+ for (const moveid in learnset) {
+ const gens = learnset[moveid].map(x => Number(x[0]));
+ const minGen = Math.min(...gens);
+
+ if (minGen <= 4 && (gen3HMs.has(moveid) || gen4HMs.has(moveid))) {
+ let legalGens = '';
+ let available = false;
+
+ if (minGen === 3) {
+ legalGens += '3';
+ available = true;
+ }
+ if (available) available = !gen3HMs.has(moveid);
+
+ if (available || gens.includes(4)) {
+ legalGens += '4';
+ available = true;
+ }
+ if (available) available = !gen4HMs.has(moveid);
+
+ let minUpperGen = available ? 5 : Math.min(
+ ...gens.filter(gen => gen > 4)
+ );
+ legalGens += '0123456789'.slice(minUpperGen);
+ BattleTeambuilderTable['gen9dlc1'].learnsets[id][moveid] = legalGens;
+ } else {
+ BattleTeambuilderTable['gen9dlc1'].learnsets[id][moveid] = '0123456789'.slice(minGen);
+ }
+
+ if (gens.indexOf(6) >= 0) BattleTeambuilderTable['gen9dlc1'].learnsets[id][moveid] += 'p';
+ if (gens.indexOf(7) >= 0 && learnset[moveid].some(x => x[0] === '7' && x !== '7V')) {
+ BattleTeambuilderTable['gen9dlc1'].learnsets[id][moveid] += 'q';
+ }
+ if (gens.indexOf(8) >= 0 && learnset[moveid].some(x => x[0] === '8' && x !== '8V')) {
+ BattleTeambuilderTable['gen9dlc1'].learnsets[id][moveid] += 'g';
+ }
+ if (gens.indexOf(9) >= 0 && learnset[moveid].some(x => x[0] === '9' && x !== '9V')) {
+ BattleTeambuilderTable['gen9dlc1'].learnsets[id][moveid] += 'a';
+ }
+ }
+ }
// Client relevant data that should be overriden by past gens and mods
const overrideSpeciesKeys = ['abilities', 'baseStats', 'cosmeticFormes', 'isNonstandard', 'requiredItems', 'types', 'unreleasedHidden'];
diff --git a/play.pokemonshowdown.com/crossdomain.php b/play.pokemonshowdown.com/crossdomain.php
index 61f199d1f..1fd7d6d32 100644
--- a/play.pokemonshowdown.com/crossdomain.php
+++ b/play.pokemonshowdown.com/crossdomain.php
@@ -97,7 +97,7 @@
?>
-
+
+
-
+
diff --git a/play.pokemonshowdown.com/recoverteams.html b/play.pokemonshowdown.com/recoverteams.html
index 0a58fd591..a77a8b3d7 100644
--- a/play.pokemonshowdown.com/recoverteams.html
+++ b/play.pokemonshowdown.com/recoverteams.html
@@ -5,7 +5,7 @@
exports = window;
if (location.protocol === 'https:') location.replace('http://play.pokemonshowdown.com/recoverteams.html')
-
+
diff --git a/play.pokemonshowdown.com/src/battle-animations-moves.ts b/play.pokemonshowdown.com/src/battle-animations-moves.ts
index 3896f5671..699ba82c5 100644
--- a/play.pokemonshowdown.com/src/battle-animations-moves.ts
+++ b/play.pokemonshowdown.com/src/battle-animations-moves.ts
@@ -16826,7 +16826,7 @@ export const BattleMoveAnims: AnimTable = {
},
syrupbomb: {
anim(scene, [attacker, defender]) {
- const imageType = {filter: attacker.sp.shiny ? 'hue-rotate(-45deg)' : 'hue-rotate(30deg)'};
+ const imageType = {filter: !attacker.sp.shiny ? 'hue-rotate(-45deg)' : 'hue-rotate(30deg)'};
scene.showEffect('flareball', {
x: attacker.x,
y: attacker.y,
@@ -35887,6 +35887,7 @@ BattleMoveAnims['terablastpsychic'] = {anim: BattleMoveAnims['psychic'].anim};
BattleMoveAnims['terablastrock'] = {anim: BattleMoveAnims['powergem'].anim};
BattleMoveAnims['terablaststeel'] = {anim: BattleMoveAnims['flashcannon'].anim};
BattleMoveAnims['terablastwater'] = {anim: BattleMoveAnims['hydropump'].anim};
+BattleMoveAnims['terablaststellar'] = {anim: BattleMoveAnims['dracometeor'].anim};
BattleMoveAnims['tidyup'] = {anim: BattleMoveAnims['bulkup'].anim};
BattleMoveAnims['trailblaze'] = {anim: BattleMoveAnims['powerwhip'].anim};
BattleMoveAnims['tripledive'] = {anim: BattleMoveAnims['dive'].anim};
diff --git a/play.pokemonshowdown.com/src/battle-animations.ts b/play.pokemonshowdown.com/src/battle-animations.ts
index a42cf99fd..21056266b 100644
--- a/play.pokemonshowdown.com/src/battle-animations.ts
+++ b/play.pokemonshowdown.com/src/battle-animations.ts
@@ -1813,6 +1813,7 @@ export class PokemonSprite extends Sprite {
magnetrise: ['Magnet Rise', 'good'],
smackdown: ['Smack Down', 'bad'],
focusenergy: ['Critical Hit Boost', 'good'],
+ dragoncheer: ['Critical Hit Boost', 'good'],
slowstart: ['Slow Start', 'bad'],
protosynthesisatk: ['Protosynthesis: Atk', 'good'],
protosynthesisdef: ['Protosynthesis: Def', 'good'],
diff --git a/play.pokemonshowdown.com/src/battle-dex-data.ts b/play.pokemonshowdown.com/src/battle-dex-data.ts
index 86b787672..5d649ba5e 100644
--- a/play.pokemonshowdown.com/src/battle-dex-data.ts
+++ b/play.pokemonshowdown.com/src/battle-dex-data.ts
@@ -537,45 +537,44 @@ const BattlePokemonIconIndexes: {[id: string]: number} = {
venomiconepilogue: 1464 + 33,
saharaja: 1464 + 34,
hemogoblin: 1464 + 35,
-
- // CAP prevos
- syclar: 1500 + 0,
- embirch: 1500 + 1,
- flarelm: 1500 + 2,
- breezi: 1500 + 3,
- scratchet: 1500 + 4,
- necturine: 1500 + 5,
- cupra: 1500 + 6,
- argalis: 1500 + 7,
- brattler: 1500 + 8,
- cawdet: 1500 + 9,
- volkritter: 1500 + 10,
- snugglow: 1500 + 11,
- floatoy: 1500 + 12,
- caimanoe: 1500 + 13,
- pluffle: 1500 + 14,
- rebble: 1500 + 15,
- tactite: 1500 + 16,
- privatyke: 1500 + 17,
- nohface: 1500 + 18,
- monohm: 1500 + 19,
- duohm: 1500 + 20,
- protowatt: 1500 + 21,
- voodoll: 1500 + 22,
- mumbao: 1500 + 23,
- fawnifer: 1500 + 24,
- electrelk: 1500 + 25,
- smogecko: 1500 + 26,
- smoguana: 1500 + 27,
- swirlpool: 1500 + 28,
- coribalis: 1500 + 29,
- justyke: 1500 + 30,
- solotl: 1500 + 31,
- miasmite: 1500 + 32,
- dorsoil: 1500 + 33,
- saharascal: 1500 + 34,
- ababo: 1500 + 35,
- scattervein: 1500 + 36,
+ syclar: 1464 + 36,
+ embirch: 1464 + 37,
+ flarelm: 1464 + 38,
+ breezi: 1464 + 39,
+ scratchet: 1464 + 40,
+ necturine: 1464 + 41,
+ cupra: 1464 + 42,
+ argalis: 1464 + 43,
+ brattler: 1464 + 44,
+ cawdet: 1464 + 45,
+ volkritter: 1464 + 46,
+ snugglow: 1464 + 47,
+ floatoy: 1464 + 48,
+ caimanoe: 1464 + 49,
+ pluffle: 1464 + 50,
+ rebble: 1464 + 51,
+ tactite: 1464 + 52,
+ privatyke: 1464 + 53,
+ nohface: 1464 + 54,
+ monohm: 1464 + 55,
+ duohm: 1464 + 56,
+ protowatt: 1464 + 57,
+ voodoll: 1464 + 58,
+ mumbao: 1464 + 59,
+ fawnifer: 1464 + 60,
+ electrelk: 1464 + 61,
+ smogecko: 1464 + 62,
+ smoguana: 1464 + 63,
+ swirlpool: 1464 + 64,
+ coribalis: 1464 + 65,
+ justyke: 1464 + 66,
+ solotl: 1464 + 67,
+ miasmite: 1464 + 68,
+ dorsoil: 1464 + 69,
+ saharascal: 1464 + 70,
+ ababo: 1464 + 71,
+ scattervein: 1464 + 72,
+ cresceidon: 1464 + 73,
};
const BattlePokemonIconIndexesLeft: {[id: string]: number} = {
@@ -1200,6 +1199,7 @@ class Move implements Effect {
readonly heal: number[] | null;
readonly multihit: number[] | number | null;
readonly hasCrashDamage: boolean;
+ readonly basePowerCallback: boolean;
readonly noPPBoosts: boolean;
readonly secondaries: ReadonlyArray | null;
readonly noSketch: boolean;
@@ -1236,6 +1236,7 @@ class Move implements Effect {
this.heal = data.heal || null;
this.multihit = data.multihit || null;
this.hasCrashDamage = data.hasCrashDamage || false;
+ this.basePowerCallback = !!data.basePowerCallback;
this.noPPBoosts = data.noPPBoosts || false;
this.secondaries = data.secondaries || (data.secondary ? [data.secondary] : null);
this.noSketch = !!data.noSketch;
diff --git a/play.pokemonshowdown.com/src/battle-dex-search.ts b/play.pokemonshowdown.com/src/battle-dex-search.ts
index b01423dcf..5323dc6ee 100644
--- a/play.pokemonshowdown.com/src/battle-dex-search.ts
+++ b/play.pokemonshowdown.com/src/battle-dex-search.ts
@@ -362,7 +362,6 @@ class DexSearch {
// For pokemon queries, accept types/tier/abilities/moves/eggroups as filters
if (searchType === 'pokemon' && (typeIndex === 5 || typeIndex > 7)) continue;
- if (searchType === 'pokemon' && typeIndex === 3 && this.dex.gen < 9) continue;
// For move queries, accept types/categories as filters
if (searchType === 'move' && ((typeIndex !== 8 && typeIndex > 4) || typeIndex === 3)) continue;
// For move queries in the teambuilder, don't accept pokemon as filters
@@ -607,7 +606,8 @@ abstract class BattleTypedSearch {
mod = '';
protected formatType: 'doubles' | 'bdsp' | 'bdspdoubles' | 'letsgo' | 'metronome' | 'natdex' | 'nfe' |
- 'ssdlc1' | 'ssdlc1doubles' | 'predlc' | 'predlcdoubles' | 'predlcnatdex' | 'stadium' | 'lc' | null = null;
+ 'ssdlc1' | 'ssdlc1doubles' | 'predlc' | 'predlcdoubles' | 'predlcnatdex' | 'svdlc1' | 'svdlc1doubles' |
+ 'svdlc1natdex' | 'stadium' | 'lc' | null = null;
/**
* Cached copy of what the results list would be with only base filters
@@ -664,7 +664,8 @@ abstract class BattleTypedSearch {
} else if (!format) {
this.dex = Dex;
}
- if (format.startsWith('dlc1')) {
+
+ if (format.startsWith('dlc1') && this.dex.gen === 8) {
if (format.includes('doubles')) {
this.formatType = 'ssdlc1doubles';
} else {
@@ -682,6 +683,16 @@ abstract class BattleTypedSearch {
}
format = format.slice(6) as ID;
}
+ if (format.startsWith('dlc1') && this.dex.gen === 9) {
+ if (format.includes('doubles') && !format.includes('nationaldex')) {
+ this.formatType = 'svdlc1doubles';
+ } else if (format.includes('nationaldex')) {
+ this.formatType = 'svdlc1natdex';
+ } else {
+ this.formatType = 'svdlc1';
+ }
+ format = format.slice(4) as ID;
+ }
if (format.startsWith('stadium')) {
this.formatType = 'stadium';
format = format.slice(7) as ID;
@@ -690,6 +701,7 @@ abstract class BattleTypedSearch {
if (format.startsWith('vgc')) this.formatType = 'doubles';
if (format === 'vgc2020') this.formatType = 'ssdlc1doubles';
if (format === 'vgc2023regulationd') this.formatType = 'predlcdoubles';
+ if (format === 'vgc2023regulatione') this.formatType = 'svdlc1doubles';
if (format.includes('bdsp')) {
if (format.includes('doubles')) {
this.formatType = 'bdspdoubles';
@@ -853,6 +865,7 @@ abstract class BattleTypedSearch {
let genChar = `${gen}`;
if (
this.format.startsWith('vgc') ||
+ this.format.startsWith('bss') ||
this.format.startsWith('battlespot') ||
this.format.startsWith('battlestadium') ||
this.format.startsWith('battlefestival') ||
@@ -914,7 +927,10 @@ abstract class BattleTypedSearch {
this.formatType === 'ssdlc1doubles' ? 'gen8dlc1doubles' :
this.formatType === 'predlc' ? 'gen9predlc' :
this.formatType === 'predlcdoubles' ? 'gen9predlcdoubles' :
- this.formatType === 'predlcnatdex' ? 'gen9predlcnatdex' :
+ this.formatType === 'predlcnatdex' ? 'gen9predlcnatdex' :
+ this.formatType === 'svdlc1' ? 'gen9dlc1' :
+ this.formatType === 'svdlc1doubles' ? 'gen9dlc1doubles' :
+ this.formatType === 'svdlc1natdex' ? 'gen9dlc1natdex' :
this.formatType === 'natdex' ? `gen${gen}natdex` :
this.formatType === 'stadium' ? `gen${gen}stadium${gen > 1 ? gen : ''}` :
`gen${gen}`;
@@ -997,7 +1013,8 @@ class BattlePokemonSearch extends BattleTypedSearch<'pokemon'> {
getBaseResults(): SearchRow[] {
const format = this.format;
if (!format) return this.getDefaultResults();
- const isVGCOrBS = format.startsWith('battlespot') || format.startsWith('battlestadium') || format.startsWith('vgc');
+ const isVGCOrBS = format.startsWith('battlespot') || format.startsWith('bss') ||
+ format.startsWith('battlestadium') || format.startsWith('vgc');
const isHackmons = format.includes('hackmons') || format.endsWith('bh');
let isDoublesOrBS = isVGCOrBS || this.formatType?.includes('doubles');
const dex = this.dex;
@@ -1015,6 +1032,7 @@ class BattlePokemonSearch extends BattleTypedSearch<'pokemon'> {
table['gen' + dex.gen + 'doubles'] && dex.gen > 4 &&
this.formatType !== 'letsgo' && this.formatType !== 'bdspdoubles' &&
this.formatType !== 'ssdlc1doubles' && this.formatType !== 'predlcdoubles' &&
+ this.formatType !== 'svdlc1doubles' &&
(
format.includes('doubles') || format.includes('triples') ||
format === 'freeforall' || format.startsWith('ffa') ||
@@ -1037,7 +1055,7 @@ class BattlePokemonSearch extends BattleTypedSearch<'pokemon'> {
table = table['gen' + dex.gen + 'nfe'];
} else if (this.formatType === 'lc') {
table = table['gen' + dex.gen + 'lc'];
- } else if (this.formatType?.startsWith('dlc1')) {
+ } else if (this.formatType?.startsWith('ssdlc1')) {
if (this.formatType.includes('doubles')) {
table = table['gen8dlc1doubles'];
} else {
@@ -1051,6 +1069,14 @@ class BattlePokemonSearch extends BattleTypedSearch<'pokemon'> {
} else {
table = table['gen9predlc'];
}
+ } else if (this.formatType?.startsWith('svdlc1')) {
+ if (this.formatType.includes('doubles')) {
+ table = table['gen9dlc1doubles'];
+ } else if (this.formatType.includes('natdex')) {
+ table = table['gen9dlc1natdex'];
+ } else {
+ table = table['gen9dlc1'];
+ }
} else if (this.formatType === 'stadium') {
table = table['gen' + dex.gen + 'stadium' + (dex.gen > 1 ? dex.gen : '')];
}
@@ -1064,7 +1090,7 @@ class BattlePokemonSearch extends BattleTypedSearch<'pokemon'> {
}
let tierSet: SearchRow[] = table.tierSet;
let slices: {[k: string]: number} = table.formatSlices;
- if (format === 'ubers' || format === 'uber') tierSet = tierSet.slice(slices.Uber);
+ if (format === 'ubers' || format === 'uber' || format === 'ubersuu') tierSet = tierSet.slice(slices.Uber);
else if (isVGCOrBS || (isHackmons && dex.gen === 9 && !this.formatType)) {
if (format.endsWith('series13') || isHackmons) {
// Show Mythicals
@@ -1113,9 +1139,9 @@ class BattlePokemonSearch extends BattleTypedSearch<'pokemon'> {
];
}
- if (format === 'zu' && table.zuBans && dex.gen === 9) {
+ if (format === 'ubersuu' && table.ubersUUBans) {
tierSet = tierSet.filter(([type, id]) => {
- if (id in table.zuBans) return false;
+ if (id in table.ubersUUBans) return false;
return true;
});
}
@@ -1669,21 +1695,21 @@ class BattleMoveSearch extends BattleTypedSearch<'move'> {
const isSTABmons = (format.includes('stabmons') || format.includes('stylemons')|| format === 'staaabmons');
const isTradebacks = (format.includes('tradebacks') || this.mod === 'gen1expansionpack' || this.mod === 'gen1burgundy');
const regionBornLegality = dex.gen >= 6 &&
- (/^battle(spot|stadium|festival)/.test(format) || format.startsWith('vgc') ||
- (dex.gen === 9 && this.formatType !== 'natdex'));
+ (/^battle(spot|stadium|festival)/.test(format) || format.startsWith('bss') ||
+ format.startsWith('vgc') || (dex.gen === 9 && this.formatType !== 'natdex'));
- let hasOwnUsefulCheck = false;
- switch(typeof window.ModConfig[this.mod]?.moveIsNotUseless){
- case 'string':
- hasOwnUsefulCheck = true;
- const usefulCheck = JSON.parse(window.ModConfig[this.mod].moveIsNotUseless);
- const checkParameters = usefulCheck.substring(usefulCheck.indexOf('(')+1,usefulCheck.indexOf(')')).split(',');
- window.ModConfig[this.mod].moveIsNotUseless = new Function(...checkParameters, usefulCheck.substring(usefulCheck.indexOf('{')));
- break;
- case 'function':
- hasOwnUsefulCheck = true;
- break;
- }
+ let hasOwnUsefulCheck = false;
+ switch(typeof window.ModConfig[this.mod]?.moveIsNotUseless){
+ case 'string':
+ hasOwnUsefulCheck = true;
+ const usefulCheck = JSON.parse(window.ModConfig[this.mod].moveIsNotUseless);
+ const checkParameters = usefulCheck.substring(usefulCheck.indexOf('(')+1,usefulCheck.indexOf(')')).split(',');
+ window.ModConfig[this.mod].moveIsNotUseless = new Function(...checkParameters, usefulCheck.substring(usefulCheck.indexOf('{')));
+ break;
+ case 'function':
+ hasOwnUsefulCheck = true;
+ break;
+ }
let learnsetid = this.firstLearnsetid(species.id);
let moves: string[] = [];
@@ -1693,8 +1719,9 @@ class BattleMoveSearch extends BattleTypedSearch<'move'> {
let lsetTable = BattleTeambuilderTable;
if (this.formatType?.startsWith('bdsp')) lsetTable = lsetTable['gen8bdsp'];
if (this.formatType === 'letsgo') lsetTable = lsetTable['gen7letsgo'];
- if (this.formatType?.startsWith('dlc1')) lsetTable = lsetTable['gen8dlc1'];
+ if (this.formatType?.startsWith('ssdlc1')) lsetTable = lsetTable['gen8dlc1'];
if (this.formatType?.startsWith('predlc')) lsetTable = lsetTable['gen9predlc'];
+ if (this.formatType?.startsWith('svdlc1')) lsetTable = lsetTable['gen9dlc1'];
while (learnsetid) {
let learnset = lsetTable.learnsets[learnsetid];
if (this.mod) {
@@ -1738,6 +1765,12 @@ class BattleMoveSearch extends BattleTypedSearch<'move'> {
) {
continue;
}
+ if (
+ this.formatType?.includes('svdlc1') && this.formatType !== 'svdlc1natdex' &&
+ BattleTeambuilderTable['gen9dlc1']?.nonstandardMoves.includes(moveid)
+ ) {
+ continue;
+ }
if (moves.includes(moveid)) continue;
moves.push(moveid);
if (moveid === 'sketch') sketch = true;
diff --git a/play.pokemonshowdown.com/src/battle-dex.ts b/play.pokemonshowdown.com/src/battle-dex.ts
index 48d9e2f21..c53f5cfa9 100644
--- a/play.pokemonshowdown.com/src/battle-dex.ts
+++ b/play.pokemonshowdown.com/src/battle-dex.ts
@@ -794,7 +794,8 @@ const Dex = new class implements ModdedDex {
let species = window.BattlePokedexAltForms && window.BattlePokedexAltForms[id] ? window.BattlePokedexAltForms[id] : Dex.species.get(id);
mod = this.getSpriteMod(mod, id, 'icons', species.exists !== false);
if (mod) return `background:transparent url(${this.modResourcePrefix}${mod}/sprites/icons/${id}.png) no-repeat scroll -0px -0px${fainted}`;
- return `background:transparent url(${Dex.resourcePrefix}sprites/pokemonicons-sheet.png?v14) no-repeat scroll -${left}px -${top}px${fainted}`;
+ return `background:transparent url(${Dex.resourcePrefix}sprites/pokemonicons-sheet.png?v15) no-repeat scroll -${left}px -${top}px${fainted}`;
+
}
getTeambuilderSpriteData(pokemon: any, gen: number = 0, mod: string = ''): TeambuilderSpriteData {
diff --git a/play.pokemonshowdown.com/src/battle-text-parser.ts b/play.pokemonshowdown.com/src/battle-text-parser.ts
index 6ef36c092..af84ecb00 100644
--- a/play.pokemonshowdown.com/src/battle-text-parser.ts
+++ b/play.pokemonshowdown.com/src/battle-text-parser.ts
@@ -512,6 +512,7 @@ class BattleTextParser {
case 'minior': id = 'shieldsdown'; templateName = 'transformEnd'; break;
case 'eiscuenoice': id = 'iceface'; break;
case 'eiscue': id = 'iceface'; templateName = 'transformEnd'; break;
+ case 'terapagosterastal': id = 'terashift'; break;
}
} else if (newSpecies) {
id = 'transform';
diff --git a/play.pokemonshowdown.com/src/battle-tooltips.ts b/play.pokemonshowdown.com/src/battle-tooltips.ts
index 980e56053..6f2a3fe52 100644
--- a/play.pokemonshowdown.com/src/battle-tooltips.ts
+++ b/play.pokemonshowdown.com/src/battle-tooltips.ts
@@ -1464,6 +1464,9 @@ class BattleTooltips {
if (move.id === 'terablast' && pokemon.terastallized) {
moveType = pokemon.terastallized as TypeName;
}
+ if (move.id === 'terastarstorm' && pokemon.terastallized === 'Stellar') {
+ moveType = pokemon.terastallized as TypeName;
+ }
// Aura Wheel as Morpeko-Hangry changes the type to Dark
if (move.id === 'aurawheel' && pokemon.getSpeciesForme() === 'Morpeko-Hangry') {
@@ -1669,12 +1672,15 @@ class BattleTooltips {
value.modify(2, "Acrobatics + no item");
}
}
- if (['crushgrip', 'wringout'].includes(move.id) && target) {
+ if (['crushgrip', 'hardpress', 'wringout'].includes(move.id) && target) {
value.set(
Math.floor(Math.floor((120 * (100 * Math.floor(target.hp * 4096 / target.maxhp)) + 2048 - 1) / 4096) / 100) || 1,
'approximate'
);
}
+ if (['terablast'].includes(move.id) && pokemon.terastallized === 'Stellar') {
+ value.set(100, 'Tera Stellar boost');
+ }
if (move.id === 'brine' && target && target.hp * 2 <= target.maxhp) {
value.modify(2, 'Brine + target below half HP');
}
@@ -2003,8 +2009,10 @@ class BattleTooltips {
// Terastal base power floor
if (
- pokemon.terastallized && pokemon.terastallized === move.type && value.value < 60 && move.priority <= 0 &&
- !move.multihit && !((move.basePower === 0 || move.basePower === 150) && (move as any).basePowerCallback)
+ pokemon.terastallized && (pokemon.terastallized === move.type || pokemon.terastallized === 'Stellar') &&
+ value.value < 60 && move.priority <= 0 && !move.multihit && !(
+ (move.basePower === 0 || move.basePower === 150) && move.basePowerCallback
+ )
) {
value.set(60, 'Tera type BP minimum');
}
diff --git a/play.pokemonshowdown.com/src/battle.ts b/play.pokemonshowdown.com/src/battle.ts
index 9efb9f827..a71e270ee 100644
--- a/play.pokemonshowdown.com/src/battle.ts
+++ b/play.pokemonshowdown.com/src/battle.ts
@@ -1095,6 +1095,7 @@ export class Battle {
speciesClause = false;
tier = '';
gameType: 'singles' | 'doubles' | 'triples' | 'multi' | 'freeforall' = 'singles';
+ compatMode = true;
rated: string | boolean = false;
rules: {[ruleName: string]: 1 | 0} = {};
isBlitz = false;
@@ -3321,7 +3322,7 @@ export class Battle {
if (!isInactive && side.active[slot]) return side.active[slot];
for (const pokemon of side.pokemon) {
- if (isInactive && side.active.includes(pokemon)) continue;
+ if (isInactive && !this.compatMode && side.active.includes(pokemon)) continue;
if (faintedOnly && pokemon.hp) continue;
if (pokemon.ident === pokemonid) { // name matched, good enough
if (slot >= 0) pokemon.slot = slot;
@@ -3403,6 +3404,7 @@ export class Battle {
}
case 'gametype': {
this.gameType = args[1] as any;
+ this.compatMode = false;
switch (args[1]) {
case 'multi':
case 'freeforall':
diff --git a/play.pokemonshowdown.com/testclient-beta.html b/play.pokemonshowdown.com/testclient-beta.html
index 7db082b0f..7cf71d22b 100644
--- a/play.pokemonshowdown.com/testclient-beta.html
+++ b/play.pokemonshowdown.com/testclient-beta.html
@@ -115,7 +115,7 @@