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 @@

{this.result ?

- {this.result.format}: {this.result.p1} vs. {this.result.p2} + {this.result.format}: {this.result.players.join(' vs. ')}

:

Loading...

} diff --git a/replay.pokemonshowdown.com/src/replays.tsx b/replay.pokemonshowdown.com/src/replays.tsx index 5e488c78d..d222cb11c 100644 --- a/replay.pokemonshowdown.com/src/replays.tsx +++ b/replay.pokemonshowdown.com/src/replays.tsx @@ -8,9 +8,9 @@ interface ReplayResult { uploadtime: number; id: string; format: string; - p1: string; - p2: string; + players: string[]; password?: string; + private?: number; rating?: number; } @@ -153,7 +153,7 @@ class SearchPanel extends preact.Component<{id: string}> { this.submitForm(e); }; url(replay: ReplayResult) { - const viewpointSwitched = (toID(replay.p2) === toID(this.user)); + const viewpointSwitched = (toID(replay.players[1]) === toID(this.user)); return replay.id + (replay.password ? `-${replay.password}pw` : '') + (viewpointSwitched ? '?p2' : ''); } formatid(replay: ReplayResult) { @@ -181,8 +181,9 @@ class SearchPanel extends preact.Component<{id: string}> { ) || (results?.map(result =>
  • - [{this.formatid(result)}]{result.rating ? ` Rating: ${result.rating}` : ''}
    - {result.p1} vs. {result.p2} + {result.format}{result.rating ? ` (Rating: ${result.rating})` : ''}
    + {!!result.private && } {} + {result.players[0]} vs. {result.players[1]}
  • ))} ;