diff --git a/CODEOWNERS b/CODEOWNERS index 1cfd691174..9862e91e5c 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -1,6 +1,6 @@ config/formats.ts @TheImmortal data/mods/*/random-teams.ts @TheImmortal -data/mods/ssb/ @HoeenCoder +data/mods/ssb/ @HoeenCoder @xInstruct data/random-teams.ts @TheImmortal data/text/ @Marty-D databases/ @monsanto diff --git a/build b/build index 04916e0955..56cf4da45f 100755 --- a/build +++ b/build @@ -96,7 +96,7 @@ sucrase('./lib', './.lib-dist'); if (sucrase('./server', './.server-dist')) { replace('.server-dist', [ - {regex: /(require\(.*?)(lib|sim)/g, replace: `$1.$2-dist`}, + {regex: /(require\(.*?)(data|lib|sim)/g, replace: `$1.$2-dist`}, ]); } diff --git a/config/formats.ts b/config/formats.ts index 70590e0761..9de7a98f29 100644 --- a/config/formats.ts +++ b/config/formats.ts @@ -1355,6 +1355,44 @@ export const Formats: FormatList = [ team: 'random', ruleset: ['Obtainable', 'Same Type Clause', 'HP Percentage Mod', 'Cancel Mod', 'Sleep Clause Mod'], }, + { + name: "[Gen 8] Super Staff Bros 4", + desc: "The fourth iteration of Super Staff Bros is here! Battle with a random team of pokemon created by the sim staff.", + threads: [ + `• Introduction & Roster`, + ], + + mod: 'ssb', + team: 'randomStaffBros', + ruleset: ['Dynamax Clause', 'HP Percentage Mod', 'Cancel Mod', 'Sleep Clause Mod'], + onBegin() { // TODO look into making an event to put this right after turn|1 + // https://discordapp.com/channels/630837856075513856/630845310033330206/716126469528485909 + // Requires client change + this.add(`raw|
Wondering what all these custom moves, abilities, and items do?
Check out the Super Staff Bros 4 Guide or use /ssb to find out!
`); + + this.add('message', [ + 'THE BATTLE FOR SURVIVAL BEGINS!', 'WHO WILL SURVIVE?', 'GET READY TO KEEP UP!', 'GET READY!', 'DARE TO BELIEVE YOU CAN SURVIVE!', 'THERE CAN BE ONLY ONE WINNER!', 'GET READY FOR THE FIGHT OF YOUR LIFE!', 'WHO WILL PREVAIL?', 'ONLY ONE TEAM WILL BE LEFT STANDING!', 'BATTLE WITHOUT LIMITS!', + ][this.random(10)]); + this.add('message', 'FIGHT!'); + }, + onSwitchInPriority: 100, + onSwitchIn(pokemon) { + let name: string = this.toID(pokemon.illusion ? pokemon.illusion.name : pokemon.name); + if (this.dex.getSpecies(name).exists || this.dex.getMove(name).exists || this.dex.getAbility(name).exists) { + // Certain pokemon have volatiles named after their id + // To prevent overwriting those, and to prevent accidentaly leaking + // that a pokemon is on a team through the onStart even triggering + // at the start of a match, users with pokemon names will need their + // statuses to end in "user". + name = name + 'user'; + } + // Add the mon's status effect to it as a volatile. + const status = this.dex.getEffect(name); + if (status?.exists) { + pokemon.addVolatile(name, pokemon); + } + }, + }, { name: "[Gen 8] Challenge Cup 1v1", @@ -1463,40 +1501,6 @@ export const Formats: FormatList = [ searchShow: false, ruleset: ['HP Percentage Mod', 'Cancel Mod'], }, - { - name: "[Gen 7] Super Staff Bros Brawl", - desc: "Super Staff Bros returns for another round! Battle with a random team of pokemon created by the sim staff.", - threads: [ - `• Introduction & Roster`, - ], - - mod: 'ssb', - team: 'randomStaffBros', - ruleset: ['HP Percentage Mod', 'Cancel Mod', 'Sleep Clause Mod'], - onBegin() { - this.add('raw|SUPER STAFF BROS BRAWL!!'); - this.add('message', 'GET READY FOR THE NEXT BATTLE!'); - if (this.teamGenerator.allXfix) this.add(`c|~HoeenHero|Oops I dropped my bag of xfix sets sorry!`); - this.add(`raw|
Wondering what all these custom moves, abilities, and items do?
Check out the Super Staff Bros Brawl Guide and find out!
`); - }, - onSwitchInPriority: 100, - onSwitchIn(pokemon) { - let name: string = this.toID(pokemon.illusion ? pokemon.illusion.name : pokemon.name); - if (this.dex.getSpecies(name).exists || name === 'rage') { - // Certain pokemon have volatiles named after their id - // To prevent overwriting those, and to prevent accidentaly leaking - // that a pokemon is on a team through the onStart even triggering - // at the start of a match, users with pokemon names will need their - // statuses to end in "user". - name = name + 'user'; - } - // Add the mon's status effect to it as a volatile. - const status = this.dex.getEffect(name); - if (status?.exists) { - pokemon.addVolatile(name, pokemon); - } - }, - }, { name: "[Gen 7 Let's Go] Random Battle", diff --git a/data/aliases.ts b/data/aliases.ts index 48cf630bc2..aa3280d335 100644 --- a/data/aliases.ts +++ b/data/aliases.ts @@ -32,7 +32,8 @@ export const Aliases: {[alias: string]: string} = { monorandom: "[Gen 8] Monotype Random Battle", bf: "[Gen 7] Battle Factory", bssf: "[Gen 7] BSS Factory", - ssb: "[Gen 7] Super Staff Bros Brawl", + ssb: "[Gen 8] Super Staff Bros 4", + ssb4: "[Gen 8] Super Staff Bros 4", lgrandom: "[Gen 7] Let's Go Random Battle", gen6bf: "[Gen 6] Battle Factory", gen7mono: "[Gen 7] Monotype", diff --git a/data/mods/ssb/abilities.ts b/data/mods/ssb/abilities.ts index 7c09fe8bbf..20f2710c2c 100644 --- a/data/mods/ssb/abilities.ts +++ b/data/mods/ssb/abilities.ts @@ -1,3 +1,96 @@ +import {SSBSet, ssbSets} from "./random-teams"; +import {getName} from './conditions'; + +// Used in many abilities, placed here to reduce the number of updates needed and to reduce the chance of errors +const STRONG_WEATHERS = ['desolateland', 'primordialsea', 'deltastream', 'heavyhailstorm', 'winterhail', 'turbulence']; + +/** + * Assigns a new set to a Pokémon + * @param pokemon the Pokemon to assign the set to + * @param newSet the SSBSet to assign + */ +export function changeSet(context: Battle, pokemon: Pokemon, newSet: SSBSet, changeAbility = false) { + const evs: StatsTable = { + hp: newSet.evs?.hp || 0, + atk: newSet.evs?.atk || 0, + def: newSet.evs?.def || 0, + spa: newSet.evs?.spa || 0, + spd: newSet.evs?.spd || 0, + spe: newSet.evs?.spe || 0, + }; + const ivs: StatsTable = { + hp: newSet.ivs?.hp || 31, + atk: newSet.ivs?.atk || 31, + def: newSet.ivs?.def || 31, + spa: newSet.ivs?.spa || 31, + spd: newSet.ivs?.spd || 31, + spe: newSet.ivs?.spe || 31, + }; + pokemon.set.evs = evs; + pokemon.set.ivs = ivs; + if (newSet.nature) pokemon.set.nature = Array.isArray(newSet.nature) ? context.sample(newSet.nature) : newSet.nature; + const oldShiny = pokemon.set.shiny; + pokemon.set.shiny = (typeof newSet.shiny === 'number') ? context.randomChance(1, newSet.shiny) : !!newSet.shiny; + let percent = (pokemon.hp / pokemon.baseMaxhp); + if (newSet.species === 'Shedinja') percent = 1; + pokemon.formeChange(newSet.species, context.effect, true); + const details = pokemon.species.name + (pokemon.level === 100 ? '' : ', L' + pokemon.level) + + (pokemon.gender === '' ? '' : ', ' + pokemon.gender) + (pokemon.set.shiny ? ', shiny' : ''); + if (oldShiny !== pokemon.set.shiny) context.add('replace', pokemon, details); + if (changeAbility) pokemon.setAbility(newSet.ability as string); + + pokemon.baseMaxhp = pokemon.species.name === 'Shedinja' ? 1 : Math.floor(Math.floor( + 2 * pokemon.species.baseStats.hp + pokemon.set.ivs.hp + Math.floor(pokemon.set.evs.hp / 4) + 100 + ) * pokemon.level / 100 + 10); + const newMaxHP = pokemon.baseMaxhp; + pokemon.hp = Math.round(newMaxHP * percent); + pokemon.maxhp = newMaxHP; + context.add('-heal', pokemon, pokemon.getHealth, '[silent]'); + let item = newSet.item; + if (typeof item !== 'string') item = item[context.random(item.length)]; + if (context.toID(item) !== (pokemon.item || pokemon.lastItem)) pokemon.setItem(item); + const newMoves = changeMoves(context, pokemon, newSet.moves.concat(newSet.signatureMove)); + pokemon.moveSlots = newMoves; + // @ts-ignore Necessary so pokemon doesn't get 8 moves + pokemon.baseMoveSlots = newMoves; + context.add('-ability', pokemon, `${pokemon.getAbility().name}`); + context.add('message', `${pokemon.name} changed form!`); +} + +/** + * Assigns new moves to a Pokemon + * @param pokemon The Pokemon whose moveset is to be modified + * @param newSet The set whose moves should be assigned + */ +export function changeMoves(context: Battle, pokemon: Pokemon, newMoves: (string | string[])[]) { + const carryOver = pokemon.moveSlots.slice().map(m => m.pp / m.maxpp); + // In case there are ever less than 4 moves + while (carryOver.length < 4) { + carryOver.push(1); + } + const result = []; + let slot = 0; + for (const newMove of newMoves) { + const moveName = Array.isArray(newMove) ? newMove[context.random(newMove.length)] : newMove; + const move = context.dex.getMove(context.toID(moveName)); + if (!move.id) continue; + const moveSlot = { + move: move.name, + id: move.id, + // eslint-disable-next-line max-len + pp: ((move.noPPBoosts || move.isZ) ? Math.floor(move.pp * carryOver[slot]) : Math.floor((move.pp * (8 / 5)) * carryOver[slot])), + maxpp: ((move.noPPBoosts || move.isZ) ? move.pp : move.pp * 8 / 5), + target: move.target, + disabled: false, + disabledSource: '', + used: false, + }; + result.push(moveSlot); + slot++; + } + return result; +} + export const Abilities: {[k: string]: ModdedAbilityData} = { /* // Example @@ -10,171 +103,210 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { }, */ // Please keep abilites organized alphabetically based on staff member name! - // 5gen - seasonsgift: { - desc: "If Sunny Day is active, this Pokemon's Attack is 1.5x and its Speed is doubled.", - shortDesc: "If Sunny Day is active, this Pokemon's Attack is 1.5x and its Speed is doubled.", - name: "Season's Gift", - isNonstandard: "Custom", - onModifyAtk(atk) { - if (this.field.isWeather(['sunnyday', 'desolateland'])) { - return this.chainModify(1.5); + // Aelita + scyphozoa: { + desc: "On switch-in, this Pokemon removes all field conditions, entry hazards, and stat boosts on both sides, gaining one random boost for every field condition, entry hazard, or boosted stat that gets cleared. This Pokemon's moves ignore abilities. If this Pokemon is a Zygarde in its 10% or 50% Forme, it changes to Complete Forme when it has 1/2 or less of its maximum HP at the end of the turn.", + shortDesc: "Power Construct + Mold Breaker. On switch-in, clears everything for random boosts.", + name: "Scyphozoa", + onSwitchIn(source) { + let successes = 0; + this.add('-ability', source, 'Scyphozoa'); + this.add('-clearallboost'); + for (const pokemon of this.getAllActive()) { + pokemon.clearBoosts(); + successes++; + if (pokemon.removeVolatile('substitute')) successes++; } - }, - onModifySpe(spe) { - if (this.field.isWeather(['sunnyday', 'desolateland'])) { - return this.chainModify(2); - } - }, - }, - // Aeonic - dummythicc: { - desc: "This ability gives the effects of the abilities Fur Coat, Magic Bounce, Infiltrator, and Sturdy.", - shortDesc: "Fur Coat + Magic Bounce + Infiltrator + Sturdy.", - name: "Dummy Thicc", - isNonstandard: "Custom", - onModifyDefPriority: 6, - onModifyDef(def) { - return this.chainModify(2); - }, - onModifyMove(move) { - move.infiltrates = true; - }, - onTryHitPriority: 1, - onTryHit(target, source, move) { - if (move.ohko) { - this.add('-immune', target, '[from] ability: Sturdy'); - return null; - } - if (target === source || move.hasBounced || !move.flags['reflectable']) { - return; - } - const newMove = this.dex.getActiveMove(move.id); - newMove.hasBounced = true; - newMove.pranksterBoosted = false; - this.useMove(newMove, target, source, this.dex.getAbility('magicbounce')); - return null; - }, - onAllyTryHitSide(target, source, move) { - if (target.side === source.side || move.hasBounced || !move.flags['reflectable']) { - return; - } - const newMove = this.dex.getActiveMove(move.id); - newMove.hasBounced = true; - newMove.pranksterBoosted = false; - this.useMove(newMove, this.effectData.target, source, this.dex.getAbility('magicbounce')); - return null; - }, - onDamagePriority: -100, - onDamage(damage, target, source, effect) { - if (target.hp === target.maxhp && damage >= target.hp && effect?.effectType === 'Move') { - this.add('-ability', target, 'Sturdy'); - return target.hp - 1; - } - }, - condition: { - duration: 1, - }, - }, - // Aethernum - awakening: { - desc: "On switch-in, Attack and Speed are lowered by three stages, while Defense and Special Defense are increased by three stages. At the end of each turn, Attack and Speed are increased by one stage while Defense and Special Defense are decreased by one stage.", - shortDesc: "Atk & Spe -3; Def & SpD +3; each turn: Atk & Spe +1; Def & SpD -1.", - name: "Awakening", - isNonstandard: "Custom", - onStart(pokemon) { - this.boost({atk: -3, spe: -3, def: 3, spd: 3}); - }, - onResidualOrder: 26, - onResidualSubOrder: 1, - onResidual(pokemon) { - this.boost({atk: 1, spe: 1, def: -1, spd: -1}); - }, - }, - // Akiamara - toxicswap: { - desc: "On switch-in, this Pokemon swaps all stat changes with the foe. Ignores abilities.", - shortDesc: "On switch-in, swaps all stat changes with foe. Ignores abilities.", - isNonstandard: "Custom", - name: "Toxic Swap", - onStart(pokemon) { - const target = pokemon.side.foe.active[0]; - if (!target) return; - const targetBoosts: SparseBoostsTable = {}; - const pokemonBoosts: SparseBoostsTable = {}; + const target = source.side.foe.active[0]; - let i: BoostName; - for (i in target.boosts) { - targetBoosts[i] = target.boosts[i]; - pokemonBoosts[i] = pokemon.boosts[i]; + const removeAll = [ + 'reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'gmaxsteelsurge', 'ferrofluid', + 'spikes', 'toxicspikes', 'stealthrock', 'shiftingrocks', 'stickyweb', + ]; + const silentRemove = ['reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'shiftingrocks', 'ferrofluid']; + for (const sideCondition of removeAll) { + if (target.side.removeSideCondition(sideCondition)) { + if (!silentRemove.includes(sideCondition)) { + this.add('-sideend', target.side, this.dex.getEffect(sideCondition).name, '[from] ability: Scyphozoa', '[of] ' + source); + } + successes++; + } + if (source.side.removeSideCondition(sideCondition)) { + if (!silentRemove.includes(sideCondition)) { + this.add('-sideend', source.side, this.dex.getEffect(sideCondition).name, '[from] ability: Scyphozoa', '[of] ' + source); + } + successes++; + } + } + for (const clear in this.field.pseudoWeather) { + if (clear.endsWith('mod') || clear.endsWith('clause')) continue; + this.field.removePseudoWeather(clear); + successes++; + } + if (this.field.clearWeather()) successes++; + if (this.field.clearTerrain()) successes++; + const stats: BoostName[] = []; + const exclude: string[] = ['accuracy', 'evasion']; + for (let x = 0; x < successes; x++) { + let stat: BoostName; + for (stat in source.boosts) { + if (source.boosts[stat] < 6 && !exclude.includes(stat)) { + stats.push(stat); + } + } + if (stats.length) { + const randomStat = this.sample(stats); + const boost: SparseBoostsTable = {}; + boost[randomStat] = 1; + this.boost(boost, source, source); + } } - target.setBoost(pokemonBoosts); - pokemon.setBoost(targetBoosts); - - this.add('-swapboost', pokemon, target, '[from] ability: Toxic Swap'); }, + isPermanent: true, onModifyMove(move) { move.ignoreAbility = true; }, - }, - // Akir - neutralizingspores: { - desc: "Nullifies all abilities while on the field.", - shortDesc: "Nullifies all abilities while on the field.", - name: "Neutralizing Spores", - isNonstandard: "Custom", - onStart(pokemon) { - this.add('-ability', pokemon, 'Neutralizing Spores'); - const abilities = [ - 'battlebond', 'comatose', 'disguise', 'multitype', 'powerconstruct', 'rkssystem', 'schooling', 'shieldsdown', 'stancechange', - ]; - for (const curMon of this.getAllActive()) { - if (curMon === pokemon) continue; - if (abilities.includes(curMon.ability)) continue; - this.singleEvent('End', curMon.getAbility(), curMon.abilityData, curMon, curMon, 'gastroacid'); - } - this.add('-message', `${pokemon.name} neutralized all abilities on the field!`); + onResidualOrder: 27, + onResidual(pokemon) { + if (pokemon.baseSpecies.baseSpecies !== 'Zygarde' || pokemon.transformed || !pokemon.hp) return; + if (pokemon.species.id === 'zygardecomplete' || pokemon.hp > pokemon.maxhp / 2) return; + this.add('-activate', pokemon, 'ability: Scyphozoa'); + pokemon.formeChange('Zygarde-Complete', this.effect, true); + pokemon.baseMaxhp = Math.floor(Math.floor( + 2 * pokemon.species.baseStats['hp'] + pokemon.set.ivs['hp'] + Math.floor(pokemon.set.evs['hp'] / 4) + 100 + ) * pokemon.level / 100 + 10); + const newMaxHP = pokemon.volatiles['dynamax'] ? (2 * pokemon.baseMaxhp) : pokemon.baseMaxhp; + pokemon.hp = newMaxHP - (pokemon.maxhp - pokemon.hp); + pokemon.maxhp = newMaxHP; + this.add('-heal', pokemon, pokemon.getHealth, '[silent]'); }, - // ability ignoring further located in scripts.js - }, - // Alpha - osolemio: { - desc: "If Sunny Day is active, this Pokemon restores 1/16 of its maximum HP, rounded down, at the end of each turn.", - shortDesc: "If Sunny Day is active, this Pokemon heals 1/16 of its max HP each turn.", - name: "O SOLE MIO", isNonstandard: "Custom", + gen: 8, + }, + + // aegii + setthestage: { + desc: "If this Pokemon is an Aegislash, it changes to Blade Forme before attempting to use an attacking move, and changes to Shield Forme before attempting to use King's Shield. This Pokemon's moves that match one of its types have a same-type attack bonus (STAB) of 2 instead of 1.5. On switch-in, this Pokemon selects a physical or special set.", + shortDesc: "Stance Change + Adaptability; on switch-in, selects physical or special set.", + isPermanent: true, + onSwitchIn(pokemon) { + if (pokemon.species.baseSpecies !== 'Aegislash') return; + const forme = this.randomChance(1, 2) ? 'aegii-Alt' : 'aegii'; + changeSet(this, pokemon, ssbSets[forme]); + const setType = pokemon.moves.includes('shadowball') ? 'specially' : 'physically'; + this.add('-message', `aegii currently has a ${setType} oriented set.`); + }, + onModifyMove(move, attacker, defender) { + move.stab = 2; + if (attacker.species.baseSpecies !== 'Aegislash' || attacker.transformed) return; + if (move.category === 'Status' && move.id !== 'kingsshield' && move.id !== 'reset') return; + const targetForme = (move.id === 'kingsshield' || move.id === 'reset' ? 'Aegislash' : 'Aegislash-Blade'); + if (attacker.species.name !== targetForme) attacker.formeChange(targetForme); + }, + name: "Set the Stage", + isNonstandard: "Custom", + gen: 8, + }, + + // Aeonic + arsene: { + desc: "On switch-in, this Pokemon summons Sandstorm. If Sandstorm is active, this Pokemon's Speed is doubled. This Pokemon takes no damage from Sandstorm.", + shortDesc: "Sand Stream + Sand Rush.", + name: "Arsene", + onStart(source) { + this.field.setWeather('sandstorm'); + }, + onModifySpe(spe, pokemon) { + if (this.field.isWeather('sandstorm')) { + return this.chainModify(2); + } + }, + onImmunity(type) { + if (type === 'sandstorm') return false; + }, + isNonstandard: "Custom", + gen: 8, + }, + + // Aethernum + rainyseason: { + desc: "On switch-in, this Pokemon summons Rain Dance. If Rain Dance or Heavy Rain is active, this Pokemon has doubled Speed, collects a raindrop, and restores 1/8 of its maximum HP, rounded down, at the end of each turn. If this Pokemon is holding Big Root, it will restore 1/6 of its maximum HP, rounded down, at the end of the turn. If this Pokemon is holding Utility Umbrella, its HP does not get restored and it does not collect raindrops. Each raindrop raises this Pokemon's Defense and Special Defense by 1 stage while it is collected.", + shortDesc: "Drizzle + Swift Swim. Restore HP if raining. Collect raindrops.", + name: "Rainy Season", + isPermanent: true, + onStart(source) { + for (const action of this.queue) { + if (action.choice === 'runPrimal' && action.pokemon === source && source.species.id === 'kyogre') return; + if (action.choice !== 'runSwitch' && action.choice !== 'runPrimal') break; + } + this.field.setWeather('raindance'); + }, onWeather(target, source, effect) { - if (effect.id === 'sunnyday') { - this.heal(target.baseMaxhp / 16); + if (target.hasItem('utilityumbrella')) return; + if (['raindance', 'primordialsea'].includes(effect.id)) { + this.heal(target.baseMaxhp / (target.hasItem('bigroot') ? 6 : 8)); + target.addVolatile('raindrop'); } }, - }, - // Andrew - volcanictempest: { - desc: "On switch-in, this Pokemon summons Lava Terrain.", - shortDesc: "On switch-in, this Pokemon summons Lava Terrain.", - name: "Volcanic Tempest", - isNonstandard: "Custom", - onStart() { - this.field.setTerrain('lavaterrain'); + onModifySpe(spe, pokemon) { + if (['raindance', 'primordialsea'].includes(pokemon.effectiveWeather())) { + return this.chainModify(2); + } }, - }, - // Anubis - distortionworld: { - desc: "For 5 turns, Speed becomes the same for all Pokemon. The power of Ghost-type attacks made by Pokemon is multiplied by 1.5.", - shortDesc: "5 turns. Speed ties. +Ghost power.", - name: "Distortion World", isNonstandard: "Custom", - onStart() { - this.field.addPseudoWeather('distortionworld'); - }, + gen: 8, }, + + // Akir + fortifications: { + desc: "Pokemon making contact with this Pokemon lose 1/8 of their maximum HP, rounded down. At the end of every turn, this Pokemon Restores 1/16 of its max HP.", + shortDesc: "Foe loses 1/8 HP if makes contact; Restores 1/16 of its max HP every turn.", + onDamagingHitOrder: 1, + onDamagingHit(damage, target, source, move) { + if (move.flags['contact']) { + this.damage(source.baseMaxhp / 8, source, target); + } + }, + onResidual(pokemon) { + this.heal(pokemon.baseMaxhp / 16); + }, + name: "Fortifications", + isNonstandard: "Custom", + gen: 8, + }, + + // Annika + overprotective: { + desc: "If this Pokemon is the last unfainted team member, its Speed is raised by 1 stage.", + shortDesc: "+1 Speed on switch-in if all other team members have fainted.", + onSwitchIn(pokemon) { + if (pokemon.side.pokemonLeft === 1) this.boost({spe: 1}); + }, + name: "Overprotective", + isNonstandard: "Custom", + gen: 8, + }, + // A Quag To The Past - careless: { - desc: "This Pokemon blocks certain status moves and instead uses them against the original user. This Pokemon also ignores other Pokemon's Attack, Special Attack, and accuracy stat stages when taking damage, and ignores other Pokemon's Defense, Special Defense, and evasiveness stat stages when dealing damage.", - shortDesc: "Bounces certain status moves and ignores other Pokemon's stat changes.", - name: "Careless", - isNonstandard: "Custom", + carefree: { + desc: "This Pokemon blocks certain status moves and instead uses the move against the original user. This Pokemon ignores other Pokemon's Attack, Special Attack, and accuracy stat stages when taking damage, and ignores other Pokemon's Defense, Special Defense, and evasiveness stat stages when dealing damage.", + shortDesc: "Magic Bounce + Unaware.", + onAnyModifyBoost(boosts, pokemon) { + const unawareUser = this.effectData.target; + if (unawareUser === pokemon) return; + if (unawareUser === this.activePokemon && pokemon === this.activeTarget) { + boosts['def'] = 0; + boosts['spd'] = 0; + boosts['evasion'] = 0; + } + if (pokemon === this.activePokemon && unawareUser === this.activeTarget) { + boosts['atk'] = 0; + boosts['def'] = 0; + boosts['spa'] = 0; + boosts['spd'] = 0; + boosts['accuracy'] = 0; + } + }, onTryHitPriority: 1, onTryHit(target, source, move) { if (target === source || move.hasBounced || !move.flags['reflectable']) { @@ -196,794 +328,1044 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { this.useMove(newMove, this.effectData.target, source); return null; }, - onAnyModifyBoost(boosts, target) { - const source = this.effectData.target; - if (source === target) return; - if (source === this.activePokemon && target === this.activeTarget) { - boosts['def'] = 0; - boosts['spd'] = 0; - boosts['evasion'] = 0; - } - if (target === this.activePokemon && source === this.activeTarget) { - boosts['atk'] = 0; - boosts['spa'] = 0; - boosts['accuracy'] = 0; - } - }, condition: { duration: 1, }, - }, - // Arsenal - logia: { - desc: "If this Pokemon is an Arceus, its type changes to match its held Plate or Z-Crystal, and it is immune to Normal and same-type moves.", - shortDesc: "Arceus: type matches held Plate or Z-Crystal; immunity to Normal and its own type.", - // Logia's type-changing itself is implemented in statuses.js - name: "Logia", + name: "Carefree", isNonstandard: "Custom", - onTryHit(target, source, move) { - const plateType = this.dex.getItem(target.item).onPlate; - if (target !== source && (move.type === 'Normal' || plateType === move.type)) { - this.add('-immune', target, '[from] ability: Logia'); + gen: 8, + }, + + // Arby + wavesurge: { + desc: "On switch-in, this Pokemon summons Wave Terrain for 5 turns. During the effect, the accuracy of Water-type moves is multiplied by 1.2, all current entry hazards are removed, and no entry hazards can be set.", + shortDesc: "On switch-in, 5 turns: no hazards; Water move acc 1.2x.", + onStart(source) { + this.field.setTerrain('waveterrain'); + }, + name: "Wave Surge", + isNonstandard: "Custom", + gen: 8, + }, + + // Archas + indomitable: { + desc: "This Pokemon cures itself if it is confused or has a major status condition. Single use.", + onTryAddVolatile(status, pokemon) { + if (status.id === 'confusion' && !this.effectData.indomitableActivated) { + this.effectData.indomitableActivated = true; return null; } }, - }, - // Birdy~! - arabesque: { - name: "Arabesque", - desc: "On switch-in, this Pokemon switches to a different Oricorio forme.", - shortDesc: "On switch-in, this Pokemon switches to a different Oricorio forme.", + onSetStatus(status, target, source, effect) { + if (!target.status) return; + if (this.effectData.indomitableActivated) return; + this.add('-immune', target, '[from] ability: Indomitable'); + this.effectData.indomitableActivated = true; + return false; + }, + onUpdate(pokemon) { + if ((pokemon.status || pokemon.volatiles['confusion']) && !this.effectData.indomitableActivated) { + this.add('-activate', pokemon, 'ability: Indomitable'); + pokemon.cureStatus(); + this.effectData.indomitableActivated = true; + } + }, + name: "Indomitable", isNonstandard: "Custom", - onStart(source) { - if (source.m.hasTransformed) { - // Pull the breaks before it infinitely swaps formes. - source.m.hasTransformed = false; - return; - } - const formes = ['oricorio', 'oricoriosensu', 'oricoriopompom', 'oricoriopau']; - if (formes.includes(this.toID(source.species.name))) { - formes.splice(formes.indexOf(this.toID(source.species.name)), 1); - this.add('-activate', source, 'ability: Arabesque'); - source.m.hasTransformed = true; - source.formeChange(formes[this.random(formes.length)], this.effect, true); - } - }, + gen: 8, }, - // Brandon - gracideamastery: { - desc: "If this Pokemon is Shaymin-Sky, it will transform into Shaymin before using a status move or upon being attacked. After using the move or taking attack damage, if this Pokemon was originally in its base forme, it will transform back into Shaymin-Sky.", - shortDesc: "Transforms into Shaymin when using status moves/being attacked.", - name: "Gracidea Mastery", - isNonstandard: "Custom", - onTryHit(target, source, move) { - if ( - (target === source || move.category === 'Status') && - target.species.id !== 'shayminsky' && target.transformed - ) return; - target.formeChange('Shaymin', this.effect); - }, - onDamagingHit(damage, target, source, move) { - if (target.species.id === 'shaymin') { - target.formeChange('Shaymin-Sky', this.effect); - } - }, - onPrepareHit(source, target, move) { - if (!target || !move) return; - if (source.species.baseSpecies !== 'Shaymin' || source.transformed) return; - if (move.category !== 'Status') return; - source.formeChange('Shaymin', this.effect); - }, - onAfterMove(pokemon) { - if (pokemon.species.id !== 'shaymin' || pokemon.transformed) return; - pokemon.formeChange('Shaymin-Sky', this.effect); - }, - }, - // Darth - seraphicregeneration: { - desc: "When this Pokemon switches out, it regains 33% of its HP, then its replacement recovers 33% of its HP.", - shortDesc: "Upon switching out, this Pokemon and its replacement regain 33% of their HP.", - onSwitchOut(pokemon) { - pokemon.heal(pokemon.baseMaxhp / 3); - pokemon.side.addSlotCondition(pokemon, 'seraphicregeneration'); - }, - name: "Seraphic Regeneration", - isNonstandard: "Custom", - condition: { - duration: 1, - onSwitchInPriority: -1, - onSwitchIn(pokemon) { - pokemon.heal(pokemon.baseMaxhp / 3); - this.add('-heal', pokemon, pokemon.getHealth, '[silent]'); - }, - }, - }, - // DaWoblefet - shadowartifice: { - desc: "Prevents adjacent opposing Pokemon from choosing to switch out unless they are immune to trapping or also have this ability or Shadow Tag. If this Pokemon is knocked out with an attack, that attack's user loses HP equal to the amount of damage inflicted on this Pokemon.", - shortDesc: "Prevents adjacent foes from switching. If KOed, that move's user loses equal HP.", - name: "Shadow Artifice", - onFoeTrapPokemon(pokemon) { - if ( - !pokemon.hasAbility('shadowartifice') && !pokemon.hasAbility('shadowtag') && - this.isAdjacent(pokemon, this.effectData.target) - ) { - pokemon.tryTrap(true); - } - }, - onFoeMaybeTrapPokemon(pokemon, source) { - if (!source) source = this.effectData.target; - if (!source || !this.isAdjacent(pokemon, source)) return; - if (!pokemon.hasAbility('shadowtag') && !pokemon.hasAbility('shadowartifice')) { - pokemon.maybeTrapped = true; - } - }, - onDamagingHitOrder: 1, - onDamagingHit(damage, target, source, move) { - if (!target.hp) { - this.damage(damage, source, target); - } - }, - }, - // Decem - miraclescale: { - desc: "This Pokemon's Dragon-type moves have their priority increased by 1.", - shortDesc: "+1 Priority to Dragon-type moves.", - name: "Miracle Scale", - isNonstandard: "Custom", - onModifyPriority(priority, pokemon, target, move) { - if (move && move.type === 'Dragon') return priority + 1; - }, - }, - // deetah - radioactive: { - desc: "If this Pokemon has a major status condition, its Attack is 1.5x; ignores burn halving physical damage. This Pokemon heals 1/8 of its max HP when poisoned.", - shortDesc: "1.5x Atk if statused. Heals 1/8 if poisoned.", - name: "Radioactive", - isNonstandard: "Custom", - onModifyAtk(atk, pokemon) { - if (pokemon.status) { - return this.chainModify(1.5); - } - }, - onDamagePriority: 1, + + // Averardo + magichat: { + desc: "This Pokemon can only be damaged by direct attacks. This Pokemon blocks certain status moves and instead uses the move against the original user.", + shortDesc: "Magic Guard + Magic Bounce.", onDamage(damage, target, source, effect) { - if (effect.id === 'psn' || effect.id === 'tox') { - this.heal(target.baseMaxhp / 8); + if (effect.id === 'heavyhailstorm') return; + if (effect.effectType !== 'Move') { + if (effect.effectType === 'Ability') this.add('-activate', source, 'ability: ' + effect.name); return false; } }, - }, - // Dragontite - iceabsorb: { - desc: "This Pokemon is immune to Ice-type moves and restores 1/4 of its maximum HP, rounded down, when hit by an Ice-type move.", - shortDesc: "This Pokemon heals 1/4 of its max HP when hit by Ice moves; Ice immunity.", - name: "Ice Absorb", - isNonstandard: "Custom", + onTryHitPriority: 1, onTryHit(target, source, move) { - if (target !== source && move.type === 'Ice') { - if (!this.heal(target.baseMaxhp / 4)) { - this.add('-immune', target, '[from] ability: Ice Absorb'); - } - return null; + if (target === source || move.hasBounced || !move.flags['reflectable']) { + return; } + const newMove = this.dex.getActiveMove(move.id); + newMove.hasBounced = true; + newMove.pranksterBoosted = false; + this.useMove(newMove, target, source); + return null; }, - }, - // E4 Flint - starkmountain: { - desc: "The user summons Sunny Day when it switches in. In addition, the Base Power of Water-type attacks is multiplied by 0.5 against this Pokemon.", - shortDesc: "Summons Sunny Day on switch-in. Base Power of foe's Water moves is halved.", - name: "Stark Mountain", + onAllyTryHitSide(target, source, move) { + if (target.side === source.side || move.hasBounced || !move.flags['reflectable']) { + return; + } + const newMove = this.dex.getActiveMove(move.id); + newMove.hasBounced = true; + newMove.pranksterBoosted = false; + this.useMove(newMove, this.effectData.target, source); + return null; + }, + condition: { + duration: 1, + }, + name: "Magic Hat", isNonstandard: "Custom", - onStart(pokemon) { - this.field.setWeather('sunnyday', pokemon); - }, - onSourceBasePower(basePower, attacker, defender, move) { - if (move.type === 'Water') { - return this.chainModify(0.5); + gen: 8, + }, + + // biggie + superarmor: { + desc: "Reduces damage taken from physical moves by 25% if the user has not yet attacked.", + onSourceModifyDamage(damage, source, target, move) { + if (this.queue.willMove(target) && move.category === 'Physical') { + return this.chainModify(0.75); } }, + name: "Super Armor", + isNonstandard: "Custom", + gen: 8, }, - // Elgino - giblovepls: { - desc: "After being damaged by a contact move, this Pokemon is healed by 20% of its maximum HP and has its Defense raised by one stage.", - shortDesc: "Defense +1 and heal 20% after hit by contact move.", + + // Billo + proofpolicy: { + desc: "Pokemon making contact with this Pokemon have the effects of Yawn, Taunt, and Torment applied to them.", + shortDesc: "Upon contact, opposing Pokemon is made drowsy and applies Taunt + Torment.", onDamagingHit(damage, target, source, move) { if (move.flags['contact']) { - this.boost({def: 1}, target); - this.heal(target.baseMaxhp / 5, target); + source.addVolatile('taunt', target); + source.addVolatile('yawn', target); + source.addVolatile('torment', target); } }, - name: "Gib love pls", - }, - // fart - risefromthegases: { - desc: "The power of Fire-type and Ground-type attacks against this Pokemon is halved.", - shortDesc: "The power of Fire-type and Ground-type attacks is halved against this Pokemon.", - name: "Rise from the Gases", + name: "Proof Policy", isNonstandard: "Custom", - onSourceBasePowerPriority: 7, - onSourceBasePower(basePower, attacker, defender, move) { - if (move.type === 'Fire') { - return this.chainModify(0.5); - } - if (move.type === 'Ground') { - return this.chainModify(0.5); - } - }, + gen: 8, }, - // Gallant Spear - trombe: { - desc: "On switch-in, this Pokemon summons Light Screen or Reflect and is guaranteed to move first for one turn.", - shortDesc: "On switch, 1 screen + priority.", - name: "Trombe!", - isNonstandard: "Custom", - onSwitchIn(pokemon) { - this.add('-activate', pokemon, 'ability: Trombe!'); - if (this.random(2)) { - pokemon.side.addSideCondition('lightscreen'); - } else { - pokemon.side.addSideCondition('reflect'); + + // Blaz + whyworry: { + desc: "This Pokemon receives 3/4 damage from supereffective attacks and does not take damage from poison.", + shortDesc: "This Pokemon takes 3/4 damage from supereffective moves. Not hurt by poison.", + onSourceModifyDamage(damage, source, target, move) { + if (target.getMoveHitData(move).typeMod > 0) { + this.debug('Why Worry neutralize'); + return this.chainModify(0.75); } }, - onModifyPriority(priority, pokemon, target, move) { - if (move && pokemon.activeTurns === 1) return priority + 1; + onDamage(damage, target, source, effect) { + if (effect.name === 'tox' || effect.name === 'psn') { + return false; + } }, + name: "Why Worry", + isNonstandard: "Custom", + gen: 8, }, - // Gimm1ck - russianrush: { - desc: "If Hail is active, this Pokemon's Speed and accuracy are doubled.", - shortDesc: "If Hail is active, this Pokemon's Speed and accuracy are doubled.", - onModifySpe(spe, pokemon) { - if (this.field.isWeather('hail')) { - return this.chainModify(2); - } - }, - onSourceModifyAccuracy(accuracy) { - if (this.field.isWeather('hail')) { - if (typeof accuracy !== 'number') return; - this.debug('russianrush - enhancing accuracy'); - return accuracy * 2; - } - }, - name: "Russian Rush", - isNonstandard: "Custom", - }, - // GMars - mysteryshell: { - desc: "If this Pokemon is a Minior in its Meteor forme, it cannot be afflicted by a status condition. This Pokemon cannot be hit with a critical hit.", - shortDesc: "Status immunity while in Meteor forme, crit immunity.", - name: "Mystery Shell", - isNonstandard: "Custom", - onCriticalHit: false, - onSetStatus(status, target, source, effect) { - if (target.species.id !== 'miniormeteor' || target.transformed) return; - if ((effect as Move)?.status) { - this.add('-immune', target, '[from] ability: Mystery Shell'); - } - return false; - }, - }, - // guishark - gzguishark: { - desc: "Boosts Attack by one stage upon switch-in or Mega Evolution.", - shortDesc: "Boosts Attack by 1 stage upon switch-in/Mega Evolution.", - name: "gz guishark", - isNonstandard: "Custom", - onStart(pokemon) { - this.boost({atk: 1}, pokemon); - }, - }, - // HoeenHero - scripter: { - desc: "If Scripted Terrain is active, this Pokemon's Speed is doubled and its moves deal 1.5x damage.", - shortDesc: "If Scripted Terrain is active, this Pokemon's Speed doubles and it deals 1.5x damage.", - name: "Scripter", - isNonstandard: "Custom", - onModifyDamage(damage, source, target, move) { - if (this.field.isTerrain('scriptedterrain')) { - this.debug('Scripter boost'); - return this.chainModify(1.5); - } - }, - onModifySpe(spe, pokemon) { - if (this.field.isTerrain('scriptedterrain')) { - return this.chainModify(2); - } - }, - }, - // inactive - souleater: { - desc: "Attacking moves heal the user by 33% of damage dealt.", - shortDesc: "Attacking moves heal the user 33% of damage dealt.", - name: "Soul Eater", - isNonstandard: "Custom", - onModifyMove(move) { - move.drain = [1, 3]; - }, - }, - // Kie - maelstrom: { - desc: "On switch-in, the weather becomes heavy rain that prevents damaging Fire-type moves from executing, in addition to all the effects of Rain Dance. This weather remains in effect until this Ability is no longer active for any Pokemon, or the weather is changed by Delta Stream or Desolate Land. If Rain Dance is active, this Pokemon's Speed is doubled.", - shortDesc: "Summons heavy rain, doubled speed in rain.", - name: "Maelstrom", - isNonstandard: "Custom", - onModifySpe(spe, pokemon) { - if (this.field.isWeather(['raindance', 'primordialsea'])) { - return this.chainModify(2); - } - }, + + // Brandon + banesurge: { + desc: "On switch-in, this Pokemon summons Bane Terrain for 5 turns. For the duration of the effect, all Pokemon use their weaker offensive stat for all attacks. The move category used does not change.", + shortDesc: "On switch-in, 5 turns: all Pokemon use weaker offensive stat.", onStart(source) { - this.field.setWeather('primordialsea'); + this.field.setTerrain('baneterrain'); + }, + name: "Bane Surge", + isNonstandard: "Custom", + gen: 8, + }, + + // brouha + turbulence: { + desc: "While this Pokemon is on the field, all entry hazards and terrains are removed at the end of each turn, non-Flying-type Pokemon lose 6% of their HP, rounded down, at the end of each turn, and Flying-type Pokemon have their Defense multiplied by 1.5x.", + shortDesc: "Flying get 1.5x Def. End of each turn: clears terrain/hazards, non-Flying lose 6% HP.", + onStart(source) { + this.field.setWeather('turbulence'); }, onAnySetWeather(target, source, weather) { - const strongWeathers = ['desolateland', 'primordialsea', 'deltastream']; - if (this.field.getWeather().id === 'primordialsea' && !strongWeathers.includes(weather.id)) return false; + if (this.field.getWeather().id === 'turbulence' && !STRONG_WEATHERS.includes(weather.id)) return false; }, onEnd(pokemon) { if (this.field.weatherData.source !== pokemon) return; for (const target of this.getAllActive()) { if (target === pokemon) continue; - if (target.hasAbility('primordialsea') || target.hasAbility('maelstrom')) { + if (target.hasAbility('turbulence')) { this.field.weatherData.source = target; return; } } this.field.clearWeather(); }, - }, - // kaori - flowershield: { - shortDesc: "This Pokemon's Special Defense is doubled.", - onModifySpDPriority: 6, - onModifySpD(spd) { - return this.chainModify(2); - }, - name: "Flower Shield", - }, - // KingSwordYT - kungfupanda: { - desc: "This Pokemon's punch-based attacks have their power multiplied by 1.2, and this Pokemon's Speed is raised by one stage after it is damaged by a contact move.", - shortDesc: "Punch-based moves have 1.2x power. +1 Spe when a foe makes contact.", - name: "Kung Fu Panda", + name: "Turbulence", isNonstandard: "Custom", - onBasePowerPriority: 8, + gen: 8, + }, + + // cant say + ragequit: { + desc: "If a Pokemon with this ability uses a move that misses or fails, the Pokemon faints and reduces the foe's Attack and Special Attack by 2 stages", + shortDesc: "If move misses or fails, use Memento.", + name: "Rage Quit", + onAfterMove(pokemon, target, move) { + if (pokemon.moveThisTurnResult === false) { + this.add('-ability', pokemon, 'Rage Quit'); + pokemon.faint(); + if (pokemon.side.foe.active[0]) { + this.boost({atk: -2, spa: -2}, pokemon.side.foe.active[0], pokemon, null, true); + } + } + }, + isNonstandard: "Custom", + gen: 8, + }, + + // Celine + guardianarmor: { + desc: "On switch-in, this Pokemon's Defense and Special Defense are raised by 2 stages.", + name: "Guardian Armor", + onStart(pokemon) { + this.boost({def: 2, spd: 2}, pokemon); + }, + isNonstandard: "Custom", + gen: 8, + }, + + // drampa's grandpa + oldmanpa: { + desc: "This Pokemon's sound-based moves have their power multiplied by 1.3. This Pokemon takes halved damage from sound-based moves. This Pokemon ignores other Pokemon's Attack, Special Attack, and accuracy stat stages when taking damage, and ignores other Pokemon's Defense, Special Defense, and evasiveness stat stages when dealing damage. Upon switching in, this Pokemon's Defense and Special Defense are raised by 1 stage.", + shortDesc: "Effects of Punk Rock + Unaware. On switch-in, boosts Def and Sp. Def by 1.", + name: "Old Manpa", + onBasePowerPriority: 7, onBasePower(basePower, attacker, defender, move) { - if (move.flags['punch']) { - this.debug('Kung Fu Panda boost'); - return this.chainModify([0x1333, 0x1000]); + if (move.flags['sound']) { + this.debug('Old Manpa boost'); + return this.chainModify([0x14CD, 0x1000]); } }, - onDamagingHit(damage, target, source, move) { - if (move.flags['contact']) { - this.boost({spe: 1}); + onSourceModifyDamage(damage, source, target, move) { + if (move.flags['sound']) { + this.debug('Old Manpa weaken'); + return this.chainModify(0.5); } }, - }, - // Mad Monty ¾° - minnesnowta: { - desc: "This Pokemon is immune to Ice-type moves. Its Ice- and Electric-type attacks have their power multiplied by 1.2x.", - shortDesc: "This Pokemon's Ice and Electric attacks have 1.2x power; Ice immunity.", - onTryHit(target, source, move) { - if (target !== source && move.type === 'Ice') { - this.add('-immune', target, '[from] ability: Minnesnowta'); - return null; + onAnyModifyBoost(boosts, pokemon) { + const unawareUser = this.effectData.target; + if (unawareUser === pokemon) return; + if (unawareUser === this.activePokemon && pokemon === this.activeTarget) { + boosts['def'] = 0; + boosts['spd'] = 0; + boosts['evasion'] = 0; + } + if (pokemon === this.activePokemon && unawareUser === this.activeTarget) { + boosts['atk'] = 0; + boosts['def'] = 0; + boosts['spa'] = 0; + boosts['spd'] = 0; + boosts['accuracy'] = 0; } }, - onBasePowerPriority: 8, - onBasePower(basePower, attacker, defender, move) { - if (['Electric', 'Ice'].includes(move.type)) { - this.debug('Minnesnowta boost'); - return this.chainModify([0x1333, 0x1000]); - } + onStart(pokemon) { + this.boost({def: 1, spd: 1}); }, - name: "Minnesnowta", - }, - // Marshmallon - sightseeing: { - desc: "If this Pokemon is a Castform, its type changes to the current weather condition's type, and its moveset changes to the one associated with the current weather, except during Sandstorm and Acid Rain. The user's Defense, Special Attack, Special Defense, Speed, and accuracy are all boosted 1.5x during weather.", - shortDesc: "Castform adapts to current weather; in weather, Def, SpA, SpD, Spe, accuracy 1.5x.", - name: "Sightseeing", isNonstandard: "Custom", - onModifyDef(def) { - if (!this.field.isWeather('')) { - return this.chainModify(1.5); - } - }, - onModifySpA(spa) { - if (!this.field.isWeather('')) { - return this.chainModify(1.5); - } - }, - onModifySpD(spd) { - if (!this.field.isWeather('')) { - return this.chainModify(1.5); - } - }, - onModifySpe(spe) { - if (!this.field.isWeather('')) { - return this.chainModify(1.5); - } - }, - onSourceModifyAccuracy(accuracy) { - if (!this.field.isWeather('')) { - return this.chainModify(1.5); - } - }, - onUpdate(pokemon) { - if (pokemon.baseSpecies.baseSpecies !== 'Castform' || pokemon.transformed) return; - let forme = null; - switch (this.field.effectiveWeather()) { - case 'sunnyday': - case 'desolateland': - if (pokemon.species.id !== 'castformsunny') forme = 'Castform-Sunny'; - break; - case 'raindance': - case 'primordialsea': - if (pokemon.species.id !== 'castformrainy') forme = 'Castform-Rainy'; - break; - case 'hail': - if (pokemon.species.id !== 'castformsnowy') forme = 'Castform-Snowy'; - break; - default: - if (pokemon.species.id !== 'castform') forme = 'Castform'; - break; - } - if (pokemon.isActive && forme) { - pokemon.formeChange(forme, this.effect, false, '[msg]'); - const sets: {[k: string]: string[]} = { - 'Castform-Sunny': ['Fire Blast', 'Solar Beam', 'Synthesis', 'Weather Forecast'], - 'Castform-Rainy': ['Hydro Pump', 'Hurricane', 'Thunder', 'Weather Forecast'], - 'Castform-Snowy': ['Blizzard', 'Thunder', 'Earth Power', 'Weather Forecast'], - Castform: ['Rain Dance', 'Sunny Day', 'Hail', 'Weather Forecast'], - }; - // Store percentage of PP left for each moveSlot - const carryOver = pokemon.moveSlots.map(m => m.pp / m.maxpp); - // Incase theres ever less than 4 moves - while (carryOver.length < 4) { - carryOver.push(1); + gen: 8, + }, + + // dream + greedpunisher: { + desc: "This Pokemon can only be damaged by direct attacks. On switch-in, this Pokemon's stats are boosted based on the number of hazards on the field. 1 random stat is raised if 1-2 hazards are up, and 2 random stats are raised if 3 or more hazards are up.", + shortDesc: "On switch-in, boosts stats based on the number of hazards on this Pokemon's side.", + name: "Greed Punisher", + onSwitchIn(pokemon) { + const side = pokemon.side; + const sideConditions = Object.keys(side.sideConditions); + const activeCount = sideConditions.length; + const stats: BoostName[] = []; + const exclude: string[] = ['accuracy', 'evasion']; + for (let x = 0; x < activeCount; x++) { + let stat: BoostName; + for (stat in pokemon.boosts) { + if (pokemon.boosts[stat] < 6 && !exclude.includes(stat)) { + stats.push(stat); + } } - const set = sets[forme]; - pokemon.moveSlots = []; - for (const [i, newMove] of set.entries()) { - const moveSpecies = this.dex.getMove(newMove); - pokemon.moveSlots.push({ - move: moveSpecies.name, - id: moveSpecies.id, - pp: ( - (moveSpecies.noPPBoosts || moveSpecies.isZ) ? - Math.floor(moveSpecies.pp * carryOver[i]) : - Math.floor((moveSpecies.pp * 8 / 5) * carryOver[i]) - ), - maxpp: ((moveSpecies.noPPBoosts || moveSpecies.isZ) ? moveSpecies.pp : moveSpecies.pp * 8 / 5), - target: moveSpecies.target, - disabled: false, - disabledSource: '', - used: false, - }); + if (stats.length) { + const randomStat = this.sample(stats); + const boost: SparseBoostsTable = {}; + boost[randomStat] = 1; + this.boost(boost, pokemon, pokemon); } } }, - }, - // Megazard - standuptall: { - desc: "This Pokemon's Attack, Defense, and Special Defense are raised by one stage at the end of each full turn it is on the field.", - shortDesc: "Raises Atk, Def, and SpD by 1 at the end of each full turn on the field.", - name: "Stand Up Tall", - isNonstandard: "Custom", - onResidualOrder: 26, - onResidualSubOrder: 1, - onResidual(pokemon) { - if (pokemon.activeTurns) { - this.boost({atk: 1, def: 1, spd: 1}); - } - }, - }, - // nui - prismaticsurge: { - desc: "On switch-in, this Pokemon summons Prismatic Terrain.", - shortDesc: "On switch-in, this Pokemon summons Prismatic Terrain.", - name: "Prismatic Surge", - isNonstandard: "Custom", - onStart() { - this.field.setTerrain('prismaticterrain'); - }, - }, - // Pablo - shellshocker: { - desc: "This Pokemon's Normal-type moves become Electric-type and have 1.2x power. In addition, this Pokemon is immune to Electric-type moves and heals 1/4 of its maximum HP, rounded down, when hit by an Electric-type move.", - shortDesc: "Normal-type moves become Electric with 1.2x power; Electric hits heal 1/4 max HP.", - name: "Shell Shocker", - isNonstandard: "Custom", - onModifyMovePriority: -1, - onModifyMove(move, pokemon) { - const noTypeChange = ['judgment', 'multiattack', 'naturalgift', 'revelationdance', 'technoblast', 'weatherball']; - if (move.type === 'Normal' && !noTypeChange.includes(move.id) && !(move.isZ && move.category !== 'Status')) { - move.type = 'Electric'; - move.galvanizeBoosted = true; - } - }, - onBasePowerPriority: 8, - onBasePower(basePower, pokemon, target, move) { - if (move.galvanizeBoosted) return this.chainModify([0x1333, 0x1000]); - }, - onTryHit(target, source, move) { - if (target !== source && move.type === 'Electric') { - if (!this.heal(target.baseMaxhp / 4)) { - this.add('-immune', target, '[from] ability: Shell Shocker'); - } - return null; - } - }, - }, - // Pirate Princess - acidrain: { - desc: "On switch-in, this Pokemon summons Acid Rain.", - shortDesc: "On switch-in, this Pokemon summons Acid Rain.", - onStart() { - this.field.setWeather('acidrain'); - }, - name: "Acid Rain", - }, - // pre - optimize: { - desc: "This Pokemon changes forme and sets depending on which attack it uses, before the attack takes place. If this Pokemon uses Psycho Boost, it first changes to its Attack forme. If this Pokemon uses Recover, it first changes to its Defense forme. If this Pokemon uses Extreme Speed, it first changes to its Speed forme. If this Pokemon uses Refactor, it first changes to its Base forme.", - shortDesc: "This Pokemon changes forme and set depending on which attack it uses.", - name: "Optimize", - isNonstandard: "Custom", - onBeforeMove(pokemon, target, move) { - switch (move.id) { - case 'psychoboost': - if (pokemon.species.name === 'Deoxys-Attack') return; - pokemon.set.nature = 'Modest'; - pokemon.set.ivs = {hp: 31, atk: 31, def: 31, spa: 31, spd: 31, spe: 31}; - pokemon.set.evs = {hp: 4, atk: 0, def: 0, spa: 252, spd: 0, spe: 252}; - pokemon.setItem('Life Orb'); - pokemon.formeChange('Deoxys-Attack', this.effect); - break; - case 'recover': - if (pokemon.species.name === 'Deoxys-Defense') return; - pokemon.set.nature = 'Bold'; - pokemon.set.ivs = {hp: 31, atk: 0, def: 31, spa: 31, spd: 31, spe: 31}; - pokemon.set.evs = {hp: 252, atk: 0, def: 128, spa: 0, spd: 128, spe: 0}; - pokemon.setItem('Leftovers'); - pokemon.formeChange('Deoxys-Defense', this.effect); - break; - case 'extremespeed': - if (pokemon.species.name === 'Deoxys-Speed') return; - pokemon.set.nature = 'Adamant'; - pokemon.set.ivs = {hp: 31, atk: 31, def: 31, spa: 31, spd: 31, spe: 31}; - pokemon.set.evs = {hp: 4, atk: 252, def: 0, spa: 0, spd: 0, spe: 252}; - pokemon.setItem('Focus Sash'); - pokemon.formeChange('Deoxys-Speed', this.effect); - break; - case 'refactor': - if (pokemon.species.name === 'Deoxys') return; - pokemon.set.nature = 'Bold'; - pokemon.set.ivs = {hp: 31, atk: 0, def: 31, spa: 31, spd: 31, spe: 31}; - pokemon.set.evs = {hp: 252, atk: 0, def: 4, spa: 0, spd: 0, spe: 252}; - pokemon.setItem('Rocky Helmet'); - pokemon.formeChange('Deoxys', this.effect); - break; - } - }, - }, - // ptoad - fatrain: { - desc: "This Pokemon summons Rain Dance when it switches in, and its Defense is 2x when Rain is active.", - shortDesc: "On switch-in, summons Rain Dance. This Pokemon's Defense is 2x during Rain.", - name: "Fat Rain", - isNonstandard: "Custom", - onStart(source) { - for (const action of this.queue) { - if (action.choice === 'runPrimal' && action.pokemon === source && source.species.id === 'kyogre') return; - if (action.choice !== 'runSwitch' && action.choice !== 'runPrimal') break; - } - this.field.setWeather('raindance'); - }, - onModifyDef(def, pokemon) { - if (this.field.isWeather(['raindance', 'primordialsea'])) { - return this.chainModify(2); - } - }, - }, - // Psynergy - wrath: { - desc: "This Pokemon has its critical hit ratio raised by 1 stage, and its moves have their accuracy multiplied by 1.1.", - shortDesc: "This Pokemon's critical hit ratio is raised by 1, and its moves have 1.1x accuracy.", - name: "Wrath", - isNonstandard: "Custom", - onModifyCritRatio(critRatio) { - return critRatio + 1; - }, - onModifyMove(move) { - if (typeof move.accuracy === 'number') { - move.accuracy *= 1.1; - } - }, - }, - // Ransei - superguarda: { - desc: "This user's Attack is doubled until it is hit by a super effective attack. If this Pokemon has a major status condition, its Attack is 1.5x; burn halving physical damage is ignored. This Pokemon can only be damaged by direct attacks.", - shortDesc: "Atk 2x until hit by SE move. 1.5x Atk if statused. Immune to indirect damage.", - name: "Superguarda", - isNonstandard: "Custom", onDamage(damage, target, source, effect) { + if (effect.id === 'heavyhailstorm') return; if (effect.effectType !== 'Move') { + if (effect.effectType === 'Ability') this.add('-activate', source, 'ability: ' + effect.name); return false; } }, - onDamagingHit(damage, target, source, move) { - if (target.getMoveHitData(move).typeMod > 0) { - if (target.m.heavilydamaged && !target.m.quoteplayed) { - this.add(`c|@Ransei|Yo really? Why do you keep hitting me with super effective moves?`); - target.m.quoteplayed = true; - } - if (!target.m.heavilydamaged) { - this.add('-message', `${target.name}'s attack was reduced after that super effective attack!`); - target.m.heavilydamaged = true; - } - } - }, - onModifyAtk(atk, pokemon) { - let atkmult = 1; - if (!pokemon.m.heavilydamaged) { - atkmult *= 2; - } - if (pokemon.status) { - atkmult *= 1.5; - } - return this.chainModify(atkmult); - }, - }, - // Rory Mercury - recharge: { - desc: "Upon switching out, this Pokemon has its major status condition cured and restores 1/3 of its maximum HP, rounded down. When this Pokemon switches in, if it uses an Electric-type attack on the next turn, that attack's power will be doubled.", - shortDesc: "Switch-out: cures status + 33% HP. Switch-in: 2x power on Electric move next turn.", - name: "Recharge", isNonstandard: "Custom", - onSwitchIn(pokemon) { - this.add('-activate', pokemon, 'ability: Recharge'); - pokemon.addVolatile('charge'); - }, - onSwitchOut(pokemon) { - pokemon.heal(pokemon.baseMaxhp / 3); + gen: 8, + }, - if (!pokemon.status) return; - this.add('-curestatus', pokemon, pokemon.status, '[from] ability: Recharge'); - pokemon.setStatus(''); + // Emeri + dracovoice: { + desc: "This Pokemon's sound-based moves become Dragon-type moves. This effect comes after other effects that change a move's type, but before Ion Deluge and Electrify's effects.", + shortDesc: "This Pokemon's sound-based moves become Dragon type.", + name: "Draco Voice", + onModifyTypePriority: -1, + onModifyType(move, pokemon) { + if (move.flags['sound'] && !pokemon.volatiles['dynamax']) { // hardcode + move.type = 'Dragon'; + } }, - }, - // SamJo - thiccerfat: { - desc: "If a Pokemon uses a Fire- or Ice-type attack against this Pokemon, that Pokemon's attacking stat is halved when calculating the damage to this Pokemon. This Pokemon cannot be burned. Gaining this Ability while burned cures it.", - shortDesc: "Fire/Ice damage against this Pokemon has a halved attacking stat. Burn immunity.", - name: "Thiccer Fat", isNonstandard: "Custom", - onSourceModifyAtkPriority: 6, - onSourceModifyAtk(atk, attacker, defender, move) { - if (move.type === 'Ice' || move.type === 'Fire') { - this.debug('Thiccer Fat weaken'); - return this.chainModify(0.5); - } - }, - onSourceModifySpAPriority: 5, - onSourceModifySpA(atk, attacker, defender, move) { - if (move.type === 'Ice' || move.type === 'Fire') { - this.debug('Thiccer Fat weaken'); - return this.chainModify(0.5); - } - }, + gen: 8, + }, + + // EpicNikolai + dragonheart: { + desc: "Once per battle, when this Pokemon's HP is at or below 25% of its max HP, this Pokemon heals 50% of its max HP.", + shortDesc: "Once per battle, heals 50% when 25% or lower.", + name: "Dragon Heart", onUpdate(pokemon) { - if (pokemon.status === 'brn') { - this.add('-activate', pokemon, 'ability: Thiccer Fat'); - pokemon.cureStatus(); + if (pokemon.hp > 0 && pokemon.hp < pokemon.maxhp / 4 && !this.effectData.dragonheart) { + this.effectData.dragonheart = true; + this.heal(pokemon.maxhp / 2); } }, + isNonstandard: "Custom", + gen: 8, + }, + + // estarossa + sandsoftime: { + desc: "On switch-in, this Pokemon summons Sandstorm. If Sandstorm is active, this Pokemon's Ground-, Rock-, and Steel-type attacks have their power multiplied by 1.3. This Pokemon takes no damage from Sandstorm.", + shortDesc: "Sand Stream + Sand Force.", + name: "Sands of Time", + onStart(source) { + this.field.setWeather('sandstorm'); + }, + onImmunity(type, pokemon) { + if (type === 'sandstorm') return false; + }, + onBasePower(basePower, attacker, defender, move) { + if (this.field.isWeather('sandstorm')) { + if (move.type === 'Rock' || move.type === 'Ground' || move.type === 'Steel') { + this.debug('Sands of Time boost'); + return this.chainModify([0x14CD, 0x1000]); + } + } + }, + isNonstandard: "Custom", + gen: 8, + }, + + // fart + bipolar: { + desc: "When this Pokemon switches in, it changes to two random types and gets corresponding STAB attacks.", + shortDesc: "This Pokemon has 2 random types and STAB moves on switch-in.", + name: "Bipolar", + isPermanent: true, + onSwitchIn(pokemon) { + const typeMap: {[key: string]: string} = { + Normal: "Return", + Fighting: "Sacred Sword", + Flying: "Drill Peck", + Poison: "Poison Jab", + Ground: "Earthquake", + Rock: "Stone Edge", + Bug: "Lunge", + Ghost: "Shadow Bone", + Steel: "Iron Head", + Electric: "Zing Zap", + Psychic: "Psychic Fangs", + Ice: "Icicle Crash", + Dragon: "Dual Chop", + Dark: "Jaw Lock", + Fairy: "Play Rough", + }; + const types = Object.keys(typeMap); + this.prng.shuffle(types); + const newTypes = [types[0], types[1]]; + this.add('-start', pokemon, 'typechange', newTypes.join('/')); + pokemon.setType(newTypes); + let move = this.dex.getMove(typeMap[newTypes[0]]); + pokemon.moveSlots[3] = pokemon.moveSlots[1]; + pokemon.moveSlots[1] = { + move: move.name, + id: move.id, + pp: move.pp, + maxpp: move.pp, + target: move.target, + disabled: false, + used: false, + virtual: true, + }; + move = this.dex.getMove(typeMap[newTypes[1]]); + pokemon.moveSlots[2] = { + move: move.name, + id: move.id, + pp: move.pp, + maxpp: move.pp, + target: move.target, + disabled: false, + used: false, + virtual: true, + }; + }, + isNonstandard: "Custom", + gen: 8, + }, + + // Finland + windingsong: { + desc: "If this Pokemon's species is Alcremie, it alternates one of its moves between two different options at the end of each turn, depending on the forme of Alcremie.", + shortDesc: "Alcremie: alternates between moves each turn.", + name: "Winding Song", + isPermanent: true, + onResidual(pokemon) { + if (pokemon.species.baseSpecies !== 'Alcremie') return; + let coolMoves = []; + if (pokemon.species.forme === 'Lemon-Cream') { + coolMoves = ['Reflect', 'Light Screen']; + } else if (pokemon.species.forme === 'Ruby-Swirl') { + coolMoves = ['Refresh', 'Destiny Bond']; + } else if (pokemon.species.forme === 'Mint-Cream') { + coolMoves = ['Light of Ruin', 'Sparkling Aria']; + } else { + coolMoves = ['Infestation', 'Whirlwind']; + } + let oldMove; + let move; + if (pokemon.moves.includes(this.toID(coolMoves[0]))) { + oldMove = this.toID(coolMoves[0]); + move = this.dex.getMove(coolMoves[1]); + } else if (pokemon.moves.includes(this.toID(coolMoves[1]))) { + oldMove = this.toID(coolMoves[1]); + move = this.dex.getMove(coolMoves[0]); + } else { + return; + } + if (!oldMove || !move) return; + const sketchIndex = pokemon.moves.indexOf(oldMove); + if (sketchIndex < 0) return false; + const sketchedMove = { + move: move.name, + id: move.id, + pp: (move.pp * 8 / 5), + maxpp: (move.pp * 8 / 5), + target: move.target, + disabled: false, + used: false, + }; + pokemon.moveSlots[sketchIndex] = sketchedMove; + pokemon.baseMoveSlots[sketchIndex] = sketchedMove; + this.add('-message', `Finland changed its move ${this.dex.getMove(oldMove).name} to ${move.name}!`); + }, + isNonstandard: "Custom", + gen: 8, + }, + + // frostyicelad + iceshield: { + desc: "This Pokemon can only be damaged by direct attacks. This Pokemon cannot lose its held item due to another Pokemon's attack.", + shortDesc: "Can only be damaged by direct attacks. Cannot lose its held item.", + name: "Ice Shield", + onDamage(damage, target, source, effect) { + if (effect.effectType !== 'Move') { + if (effect.effectType === 'Ability') this.add('-activate', source, 'ability: ' + effect.name); + return false; + } + }, + onTakeItem(item, pokemon, source) { + if (this.suppressingAttackEvents(pokemon) || !pokemon.hp || pokemon.item === 'stickybarb') return; + if (!this.activeMove) throw new Error("Battle.activeMove is null"); + if ((source && source !== pokemon) || this.activeMove.id === 'knockoff') { + this.add('-activate', pokemon, 'ability: Ice Shield'); + return false; + } + }, + isNonstandard: "Custom", + gen: 8, + }, + + // gallant's pear + armortime: { + name: "Armor Time", + desc: "If this Pokemon uses a status move or a custom move, it changes its typing and boosts one of its stats by 1 stage randomly between four options: Bug/Fire type with a Special Attack boost, Bug/Steel type with a Defense boost, Bug/Rock type with a Special Defense boost, and Bug/Electric type with a Speed boost.", + shortDesc: "On use of status or custom, this Pokemon changes type and gets a boost.", + isPermanent: true, + onPrepareHit(source, target, move) { + if (move.category !== "Status" && move.isNonstandard !== "Custom") return; + const types = ['Fire', 'Steel', 'Rock', 'Electric']; + const type = ['Bug', this.sample(types)]; + if (!source.setType(type)) return; + this.add('-start', source, 'typechange', type.join('/'), '[from] ability: Armor Time'); + switch (type[1]) { + case 'Fire': + this.add('-message', 'Armor Time: Fire Armor!'); + this.boost({spa: 1}, source); + break; + case 'Steel': + this.add('-message', 'Armor Time: Steel Armor!'); + this.boost({def: 1}, source); + break; + case 'Rock': + this.add('-message', 'Armor Time: Rock Armor!'); + this.boost({spd: 1}, source); + break; + case 'Electric': + this.add('-message', 'Armor Time: Electric Armor!'); + this.boost({spe: 1}, source); + break; + } + }, + isNonstandard: "Custom", + gen: 8, + }, + + // Gimmick + ic3peak: { + desc: "This Pokemon's Normal-type moves become Ice-type moves and have their power multiplied by 1.2. This Pokemon's moves, if they are not affected by Refrigerate, have their Base Power multiplied by the number of consecutive turns the move is used by this Pokemon.", + shortDesc: "Refrigerate; Echoed Voice modifier on non-Refrigerate moves.", + name: "IC3PEAK", + onModifyTypePriority: -1, + onModifyType(move, pokemon) { + const noModifyType = [ + 'judgment', 'multiattack', 'naturalgift', 'revelationdance', 'technoblast', 'terrainpulse', 'weatherball', + ]; + if (move.type === 'Normal' && !noModifyType.includes(move.id) && !(move.isZ && move.category !== 'Status')) { + move.type = 'Ice'; + move.refrigerateBoosted = true; + } + }, + onBasePowerPriority: 23, + onBasePower(basePower, pokemon, target, move) { + if (move.refrigerateBoosted) return this.chainModify([0x1333, 0x1000]); + }, + onStart(pokemon) { + pokemon.addVolatile('ic3peak'); + }, + condition: { + onStart(pokemon) { + this.effectData.numConsecutive = 0; + this.effectData.lastMove = ''; + }, + onTryMovePriority: -2, + onTryMove(pokemon, target, move) { + if (!pokemon.hasAbility('ic3peak')) { + pokemon.removeVolatile('ic3peak'); + return; + } + if (this.effectData.lastMove === move.id && pokemon.moveLastTurnResult) { + this.effectData.numConsecutive++; + } else { + this.effectData.numConsecutive = 0; + } + this.effectData.lastMove = move.id; + }, + onBasePowerPriority: 24, + onBasePower(basePower, pokemon, target, move) { + if (move.refrigerateBoosted) return; + const dmgMod = [1, 2, 3, 4, 5]; + const numConsecutive = this.effectData.numConsecutive > 4 ? 4 : this.effectData.numConsecutive; + return this.chainModify(dmgMod[numConsecutive]); + }, + }, + isNonstandard: "Custom", + gen: 8, + }, + + // GMars + capsulearmor: { + desc: "While in Minior-Meteor forme, this Pokemon cannot be affected by major status conditions and is immune to critical hits. This ability cannot be ignored by Moongeist Beam, Sunsteel Strike, Mold Breaker, Teravolt, or Turboblaze.", + shortDesc: "Minior-Meteor: Immune to crits and status", + name: "Capsule Armor", + isPermanent: true, + isUnbreakable: true, + onCriticalHit: false, onSetStatus(status, target, source, effect) { - if (status.id !== 'brn') return; + if (target.species.id !== 'miniormeteor' || target.transformed) return; if ((effect as Move)?.status) { - this.add('-immune', target, '[from] ability: Thiccer Fat'); + this.add('-immune', target, '[from] ability: Capsule Armor'); } return false; }, - }, - // Salamander - numbnumbjuice: { - desc: "This Pokemon is immune to volatile statuses.", - shortDesc: "This Pokemon is immune to volatile statuses.", onTryAddVolatile(status, target) { - if ([this.toID(target.name), 'furycutter', 'stockpile'].includes(status.id)) return; - this.add('-immune', target, '[from] ability: Numb Numb Juice'); + if (target.species.id !== 'miniormeteor' || target.transformed) return; + if (status.id !== 'yawn') return; + this.add('-immune', target, '[from] ability: Capsule Armor'); return null; }, - name: "Numb Numb Juice", }, - // Schiavetto - rvs: { - desc: "This Pokemon has two random stats except accuracy and evasion raised by one stage and another stat other than accuracy and evasion lowered by one stage at the end of each turn.", - shortDesc: "Raises a 2 random stats by 1 and lowers another stat by 1 at the end the turn.", - name: "RVS", - isNonstandard: "Custom", - onResidualOrder: 26, - onResidualSubOrder: 1, - onResidual(pokemon) { - let stats: BoostName[] = []; - const boost: SparseBoostsTable = {}; - let statPlus: BoostName; - for (statPlus in pokemon.boosts) { - if (statPlus === 'accuracy' || statPlus === 'evasion') continue; - if (pokemon.boosts[statPlus] < 6) { - stats.push(statPlus); + + // grimAuxiliatrix + biosteel: { + desc: "This Pokemon restores 1/3 of its maximum HP, rounded down, when it switches out, and other Pokemon cannot lower this Pokemon's stat stages.", + shortDesc: "Regenerator + Clear Body.", + name: "Bio-steel", + onSwitchOut(pokemon) { + pokemon.heal(pokemon.baseMaxhp / 3); + }, + onBoost(boost, target, source, effect) { + if (source && target === source) return; + let showMsg = false; + let i: BoostName; + for (i in boost) { + if (boost[i]! < 0) { + delete boost[i]; + showMsg = true; } } - let randomStat = stats.length ? this.sample(stats) : undefined; - if (randomStat) { - boost[randomStat] = 1; - // Prevent picking the same stat twice, sampleNoReplace dosen't exists on type Battle - stats.splice(stats.indexOf(randomStat), 1); + if (showMsg && !(effect as ActiveMove).secondaries && effect.id !== 'octolock') { + this.add("-fail", target, "unboost", "[from] ability: Bio-steel", "[of] " + target); } + }, + isNonstandard: "Custom", + gen: 8, + }, - randomStat = stats.length ? this.sample(stats) : undefined; - if (randomStat) boost[randomStat] = 1; + // HoeenHero + tropicalcyclone: { + desc: "On switch-in, this Pokemon summons Rain Dance. If Rain Dance or Heavy Rain is active, this Pokemon's Speed is doubled.", + shortDesc: "Summons Rain. 2x Speed while rain is active.", + name: "Tropical Cyclone", + onStart(source) { + this.field.setWeather('raindance'); + }, + onModifySpe(spe, pokemon) { + if (['raindance', 'primordialsea'].includes(pokemon.effectiveWeather())) { + return this.chainModify(2); + } + }, + isNonstandard: "Custom", + gen: 8, + }, - stats = []; - let statMinus: BoostName; - for (statMinus in pokemon.boosts) { - if (statMinus === 'accuracy' || statMinus === 'evasion') continue; - if (pokemon.boosts[statMinus] > -6 && !(statMinus in boost)) { - stats.push(statMinus); + // Hydro + hydrostatic: { + desc: "This Pokemon is immune to Water- and Electric-type moves and raises its Special Attack by 1 stage when hit by a Water- or Electric-type move. If this Pokemon is not the target of a single-target Water- or Electric-type move used by another Pokemon, this Pokemon redirects that move to itself if it is within the range of that move. This Pokemon's Water- and Electric-type moves have their accuracy multiplied by 1.3.", + shortDesc: "Storm Drain + Lightning Rod. This Pokemon's Water/Electric moves have 1.3x acc.", + onSourceModifyAccuracyPriority: 9, + onSourceModifyAccuracy(accuracy, source, target, move) { + if (typeof accuracy !== 'number') return; + if (!['Water', 'Electric'].includes(move.type)) return; + this.debug('hydrostatic - enhancing accuracy'); + return accuracy * 1.3; + }, + onTryHit(target, source, move) { + if (target !== source && ['Water', 'Electric'].includes(move.type)) { + if (!this.boost({spa: 1})) { + this.add('-immune', target, '[from] ability: Hydrostatic'); + } + return null; + } + }, + onAnyRedirectTarget(target, source, source2, move) { + if (!['Water', 'Electric'].includes(move.type) || + ['firepledge', 'grasspledge', 'waterpledge'].includes(move.id)) return; + const redirectTarget = ['randomNormal', 'adjacentFoe'].includes(move.target) ? 'normal' : move.target; + if (this.validTarget(this.effectData.target, source, redirectTarget)) { + if (move.smartTarget) move.smartTarget = false; + if (this.effectData.target !== target) { + this.add('-activate', this.effectData.target, 'ability: Hydrostatic'); + } + return this.effectData.target; + } + }, + name: "Hydrostatic", + isNonstandard: "Custom", + gen: 8, + }, + + // Inactive + dragonsfury: { + desc: "If this Pokemon has a non-volatile status condition, its Defense is multiplied by 1.5x and its HP is restored by 25% of damage it deals.", + shortDesc: "If this Pokemon is statused, its Def is 1.5x and it heals for 25% of dmg dealt.", + onModifyDefPriority: 6, + onModifyDef(def, pokemon) { + if (pokemon.status) { + return this.chainModify(1.5); + } + }, + onModifyMove(move, attacker) { + if (attacker.status) move.drain = [1, 4]; + }, + name: "Dragon's Fury", + isNonstandard: "Custom", + gen: 8, + }, + + // Iyarito + pollodiablo: { + desc: "This Pokemon's Special Attack is 1.5x, but it can only select the first move it executes.", + shortDesc: "This Pokemon's Sp. Atk is 1.5x, but it can only select the first move it executes.", + name: "Pollo Diablo", + onStart(pokemon) { + pokemon.abilityData.choiceLock = ""; + }, + onBeforeMove(pokemon, target, move) { + if (move.isZOrMaxPowered || move.id === 'struggle') return; + if (pokemon.abilityData.choiceLock && pokemon.abilityData.choiceLock !== move.id) { + this.addMove('move', pokemon, move.name); + this.attrLastMove('[still]'); + this.debug("Disabled by Pollo Diablo"); + this.add('-fail', pokemon); + return false; + } + }, + onModifyMove(move, pokemon) { + if (pokemon.abilityData.choiceLock || move.isZOrMaxPowered || move.id === 'struggle') return; + pokemon.abilityData.choiceLock = move.id; + }, + onModifySpAPriority: 1, + onModifySpA(spa, pokemon) { + if (pokemon.volatiles['dynamax']) return; + this.debug('Pollo Diablo Spa Boost'); + return this.chainModify(1.5); + }, + onDisableMove(pokemon) { + if (!pokemon.abilityData.choiceLock) return; + if (pokemon.volatiles['dynamax']) return; + for (const moveSlot of pokemon.moveSlots) { + if (moveSlot.id !== pokemon.abilityData.choiceLock) { + pokemon.disableMove(moveSlot.id, false, this.effectData.sourceEffect); } } - randomStat = stats.length ? this.sample(stats) : undefined; - if (randomStat) boost[randomStat] = -1; + }, + onEnd(pokemon) { + pokemon.abilityData.choiceLock = ""; + }, + isNonstandard: "Custom", + gen: 8, + }, - this.boost(boost); + // Jett + deceiver: { + desc: "This Pokemon's moves that match one of its types have a same-type attack bonus of 2 instead of 1.5. If this Pokemon is at full HP, it survives one hit with at least 1 HP.", + shortDesc: "Adaptability + Sturdy.", + onModifyMove(move) { + move.stab = 2; }, - }, - // Shiba - galewingsv1: { - desc: "This Pokemon's Flying-type moves have their priority increased by 1.", - shortDesc: "This Pokemon's Flying-type moves have their priority increased by 1.", - name: "Gale Wings v1", - isNonstandard: "Custom", - onModifyPriority(priority, pokemon, target, move) { - if (move && move.type === 'Flying') return priority + 1; + onTryHit(pokemon, target, move) { + if (move.ohko) { + this.add('-immune', pokemon, '[from] ability: Deceiver'); + return null; + } }, - }, - // Teremiare - notprankster: { - desc: "This Pokemon's status moves have their priority raised by 1.", - shortDesc: "This Pokemon's status moves have priority raised by 1.", - name: "Not Prankster", + onDamagePriority: -100, + onDamage(damage, target, source, effect) { + if (target.hp === target.maxhp && damage >= target.hp && effect && effect.effectType === 'Move') { + this.add('-ability', target, 'Deceiver'); + return target.hp - 1; + } + }, + name: "Deceiver", isNonstandard: "Custom", + gen: 8, + }, + + // Jho + venomize: { + desc: "This Pokemon's sound-based moves become Poison-type moves. This effect comes after other effects that change a move's type, but before Ion Deluge and Electrify's effects.", + shortDesc: "This Pokemon's sound-based moves become Poison type.", + onModifyTypePriority: -1, + onModifyType(move, pokemon) { + if (move.flags['sound'] && !pokemon.volatiles['dynamax']) { // hardcode + move.type = 'Poison'; + } + }, + name: "Venomize", + isNonstandard: "Custom", + gen: 8, + }, + + // Jordy + divinesandstorm: { + desc: "On switch-in, this Pokemon summons Sandstorm. This Pokemon does not take recoil damage besides Struggle/Life Orb/crash damage.", + shortDesc: "Sand Stream + Rock Head.", + name: "Divine Sandstorm", + onDamage(damage, target, source, effect) { + if (effect.id === 'recoil') { + if (!this.activeMove) return; + if (this.activeMove.id !== 'struggle') return null; + } + }, + onStart(pokemon) { + this.field.setWeather('sandstorm'); + }, + isNonstandard: "Custom", + gen: 8, + }, + + // Kaiju Bunny + secondwind: { + desc: "Once per battle, when this Pokemon's HP is at or below 25% of its max HP, this Pokemon heals 50% of its max HP.", + shortDesc: "Once per battle, heals 50% when 25% or lower.", + name: "Second Wind", + onUpdate(pokemon) { + if (pokemon.hp > 0 && pokemon.hp < pokemon.maxhp / 4 && !this.effectData.dragonheart) { + this.effectData.dragonheart = true; + this.heal(pokemon.maxhp / 2); + } + }, + isNonstandard: "Custom", + gen: 8, + }, + + // Kennedy + falsenine: { + desc: "This Pokemon's type changes to match the type of the move it is about to use. This effect comes after all effects that change a move's type. This Pokemon's critical hit ratio is raised by 1 stage.", + shortDesc: "Protean + Super Luck.", + onPrepareHit(source, target, move) { + if (move.hasBounced) return; + const type = move.type; + if (type && type !== '???' && source.getTypes().join() !== type) { + if (!source.setType(type)) return; + this.add('-start', source, 'typechange', type, '[from] ability: Libero'); + } + }, + onModifyCritRatio(critRatio) { + return critRatio + 1; + }, + name: "False Nine", + isNonstandard: "Custom", + gen: 8, + }, + + // Kev + kingofatlantis: { + desc: "On switch-in, this Pokemon summons Rain Dance for 5 turns, plus 1 additional turn for each Water-type teammate. This Pokemon also has the effects of Dry Skin.", + shortDesc: "Drizzle + Dry Skin; +1 turn of rain for each Water-type teammate.", + onStart(source) { + this.field.setWeather('raindance', source); + // See conditions.ts for weather modifications. + }, + onTryHit(target, source, move) { + if (target !== source && move.type === 'Water') { + if (!this.heal(target.baseMaxhp / 4)) { + this.add('-immune', target, '[from] ability: King of Atlantis'); + } + return null; + } + }, + onFoeBasePowerPriority: 17, + onFoeBasePower(basePower, attacker, defender, move) { + if (this.effectData.target !== defender) return; + if (move.type === 'Fire') { + return this.chainModify(1.25); + } + }, + onWeather(target, source, effect) { + if (target.hasItem('utilityumbrella')) return; + if (effect.id === 'raindance' || effect.id === 'primordialsea') { + this.heal(target.baseMaxhp / 8); + } else if (effect.id === 'sunnyday' || effect.id === 'desolateland') { + this.damage(target.baseMaxhp / 8, target, target); + } + }, + name: "King of Atlantis", + isNonstandard: "Custom", + gen: 8, + }, + + // KingSwordYT + bambookingdom: { + desc: "On switch-in, this Pokemon's Defense and Special Defense are raised by 1 stage. Pokemon using physical moves against this Pokemon lose 1/8 of their maximum HP. Pokemon using special moves against this Pokemon lose 1/16 of their maximum HP. Attacking moves used by this Pokemon have their priority set to -7.", + shortDesc: "+1 Def/SpD. -7 priority on attacks. 1/8 recoil hit by phys, 1/16 hit by spec.", + name: "Bamboo Kingdom", + onStart(pokemon) { + this.boost({def: 1, spd: 1}, pokemon); + }, onModifyPriority(priority, pokemon, target, move) { - if (move && move.category === 'Status') { - return priority + 1; + if (move?.category !== 'Status') return -7; + }, + onDamagingHit(damage, target, source, move) { + if (move.category === 'Physical') { + this.damage(source.baseMaxhp / 8, source, target); + } + if (move.category === 'Special') { + this.damage(source.baseMaxhp / 16, source, target); + } + }, + isNonstandard: "Custom", + gen: 8, + }, + + // Kipkluif + degenerator: { + desc: "While this Pokemon is active, foes that switch out lose 1/3 of their maximum HP, rounded down. This damage will never cause a Pokemon to faint, and will instead leave them at 1 HP.", + shortDesc: "While this Pokemon is active, foes that switch out lose 1/3 of their maximum HP.", + onStart(pokemon) { + pokemon.side.foe.addSideCondition('degenerator', pokemon); + const data = pokemon.side.foe.getSideConditionData('degenerator'); + if (!data.sources) { + data.sources = []; + } + data.sources.push(pokemon); + }, + onEnd(pokemon) { + pokemon.side.foe.removeSideCondition('degenerator'); + }, + condition: { + onBeforeSwitchOut(pokemon) { + let alreadyAdded = false; + for (const source of this.effectData.sources) { + if (!source.hp || source.volatiles['gastroacid']) continue; + if (!alreadyAdded) { + this.add('-activate', pokemon, 'ability: Degenerator'); + alreadyAdded = true; + } + this.damage((pokemon.baseMaxhp * 33) / 100, pokemon); + } + }, + }, + name: "Degenerator", + isNonstandard: "Custom", + gen: 8, + }, + + // Lamp + candlewax: { + desc: "This Pokemon's Special Attack is raised by 1 stage when another Pokemon faints. This Pokemon is immune to Ground-type attacks and the effects of Spikes, Toxic Spikes, Sticky Web, and the Arena Trap Ability. The effects of Gravity, Ingrain, Smack Down, Thousand Arrows, and Iron Ball nullify the immunity.", + shortDesc: "Soul-Heart + Levitate.", + onAnyFaintPriority: 1, + onAnyFaint() { + this.boost({spa: 1}, this.effectData.target); + }, + // airborneness implemented in scripts.ts:Pokemon#isGrounded + name: "Candlewax", + isNonstandard: "Custom", + gen: 8, + }, + + // Lionyx + tension: { + desc: "On switch-in, the Pokemon builds up tension, making the next attack always hit and always be a critical hit.", + shortDesc: "On switch-in, the Pokemon's next attack will always be a critical hit and will always hit.", + name: "Tension", + onStart(pokemon) { + this.add('-ability', pokemon, 'Tension'); + pokemon.addVolatile('tension'); + }, + condition: { + onStart(pokemon, source, effect) { + if (effect && (['imposter', 'psychup', 'transform'].includes(effect.id))) { + this.add('-start', pokemon, 'move: Tension', '[silent]'); + } else { + this.add('-start', pokemon, 'move: Tension'); + } + this.add("-message", `${pokemon.name} has built up tension!`); + }, + onModifyCritRatio(critRatio) { + return 5; + }, + onAnyInvulnerability(target, source, move) { + if (move && (source === this.effectData.target || target === this.effectData.target)) return 0; + }, + onSourceAccuracy(accuracy) { + return true; + }, + onAfterMove(pokemon, source) { + pokemon.removeVolatile('tension'); + }, + onEnd(pokemon) { + this.add('-end', pokemon, 'move: Tension', '[silent]'); + }, + }, + isNonstandard: "Custom", + gen: 8, + }, + + // LittEleven + darkroyalty: { + desc: "While this Pokemon is active, priority moves from opposing Pokemon targeted at allies are prevented from having an effect. Dark-type attacks used by this Pokemon have their power multiplied by 1.2.", + shortDesc: "Immune to priority. Dark-type attacks have 1.2x power.", + onFoeTryMove(target, source, move) { + const targetAllExceptions = ['perishsong', 'flowershield', 'rototiller']; + if (move.target === 'foeSide' || (move.target === 'all' && !targetAllExceptions.includes(move.id))) { + return; + } + + const dazzlingHolder = this.effectData.target; + if ((source.side === dazzlingHolder.side || move.target === 'all') && move.priority > 0.1) { + this.attrLastMove('[still]'); + this.add('-ability', dazzlingHolder, 'Dark Royalty'); + this.add('cant', target, move, '[of] ' + dazzlingHolder); + return false; + } + }, + onAllyBasePower(basePower, attacker, defender, move) { + if (move.type === 'Dark') { + this.debug('Dark Royalty boost'); + return this.chainModify(1.2); + } + }, + name: "Dark Royalty", + }, + + // Mad Monty ¾° + petrichor: { + desc: "On switch-in, this Pokemon summons Rain Dance. If Rain Dance or Heavy Rain is active, this Pokemon's Electric-type moves have 1.2x power.", + shortDesc: "Summons rain. Electric-type moves have 1.2x power in rain.", + name: "Petrichor", + onStart(source) { + this.field.setWeather('raindance'); + }, + onBasePowerPriority: 23, + onBasePower(basePower, pokemon, target, move) { + if (move.type === 'Electric' && this.field.getWeather().id === 'raindance') { + return this.chainModify([0x1333, 0x1000]); } }, }, - // The Immortal - beastboost2: { - desc: "This Pokemon's two highest stats are raised by one stage if it attacks and KOes another Pokemon.", - shortDesc: "The user's 2 highest stats are raised by 1 if it attacks and KOes another Pokemon.", - name: "Beast Boost 2", - isNonstandard: "Custom", - onSourceFaint(target, source, effect) { - if (effect?.effectType === 'Move') { - const statOrder = Object.keys(source.storedStats).sort( - (stat1, stat2) => source.storedStats[stat2 as StatNameExceptHP] - source.storedStats[stat1 as StatNameExceptHP] - ); - this.boost({[statOrder[0]]: 1, [statOrder[1]]: 1}, source); + + // Marshmallon + stubbornness: { + desc: "this Pokemon does not take recoil damage. The first time an opposing Pokemon boosts a stat each time this Pokemon is active, this Pokemon's Attack, Defense, and Special Defense are raised by 1 stage; each time the opponent boosts after this, this Pokemon's Attack is boosted by 1 stage. Activation of opposing Stubbornness will not activate Stubbornness.", + shortDesc: "Rock Head + when foe first boosts, Atk/Def/SpD+1. Further foe boosts=+1 Atk.", + name: "Stubbornness", + onDamage(damage, target, source, effect) { + if (effect.id === 'recoil') { + if (!this.activeMove) throw new Error("Battle.activeMove is null"); + if (this.activeMove.id !== 'struggle') return null; + } + }, + onSwitchOut(pokemon) { + if (this.effectData.happened) delete this.effectData.happened; + }, + onFoeAfterBoost(boost, target, source, effect) { + const pokemon = target.side.foe.active[0]; + let success = false; + let i: BoostName; + for (i in boost) { + if (boost[i]! > 0) { + success = true; + } + } + // Infinite Loop preventer + if (effect.id.includes('stubbornness')) return; + if (success) { + if (!this.effectData.happened) { + this.boost({atk: 1, def: 1, spd: 1}, pokemon); + this.effectData.happened = true; + } else { + this.boost({atk: 1}, pokemon); + } } }, - }, - // torkool - deflectiveshell: { - desc: "Summons Sunny Day as the user switches in. Non-contact moves deal 2/3 damage to this Pokemon.", - shortDesc: "On switch-in, summons Sunny Day. Receives 2/3 damage from non-contact moves.", - name: "Deflective Shell", isNonstandard: "Custom", + gen: 8, + }, + + // Mitsuki + photosynthesis: { + desc: "On switch-in, this Pokemon summons Sunny Day. If Sunny Day is active and this Pokemon is not holding Utility Umbrella, this Pokemon's Speed is doubled. If Sunny Day is active, this Pokemon's Attack is multiplied by 1.5 and it loses 1/8 of its maximum HP, rounded down, at the end of each turn. If this Pokemon is holding Utility Umbrella, its Attack remains the same and it does not lose any HP.", + shortDesc: "Drought + Chlorophyll + physical Solar Power", + name: "Photosynthesis", onStart(source) { for (const action of this.queue) { if (action.choice === 'runPrimal' && action.pokemon === source && source.species.id === 'groudon') return; @@ -991,92 +1373,785 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { } this.field.setWeather('sunnyday'); }, - onSourceModifyDamage(damage, source, target, move) { - let mod = 1; - if (!move.flags['contact']) mod = (mod / 3) * 2; // 2/3 - return this.chainModify(mod); + onModifySpe(spe, pokemon) { + if (['sunnyday', 'desolateland'].includes(pokemon.effectiveWeather())) { + return this.chainModify(2); + } + }, + onModifyAtk(atk, pokemon) { + if (['sunnyday', 'desolateland'].includes(pokemon.effectiveWeather())) { + return this.chainModify(1.5); + } + }, + onWeather(target, source, effect) { + if (target.hasItem('utilityumbrella')) return; + if (effect.id === 'sunnyday' || effect.id === 'desolateland') { + this.damage(target.baseMaxhp / 8, target, target); + } }, - }, - // Trickster - interdimensional: { - desc: "As it switches in, this Pokemon summons Gravity.", - shortDesc: "On switch-in, this Pokemon Summons Gravity.", - name: "Interdimensional", isNonstandard: "Custom", - onStart(pokemon) { - this.field.addPseudoWeather('gravity', pokemon); + gen: 8, + }, + + // n10siT + greedymagician: { + desc: "This Pokemon steals the item off a Pokemon it hits with an attack. If this Pokemon already has an item, it is replaced with the stolen item. This ability does not affect Doom Desire and Future Sight.", + shortDesc: "Steals item from foe on attack; replace current item with stolen item.", + name: "Greedy Magician", + onSourceHit(target, source, move) { + if (!move || !target) return; + if (target !== source && move.category !== 'Status') { + const yourItem = target.takeItem(source); + if (!yourItem) return; + if (!source.setItem(yourItem)) { + target.item = yourItem.id; + return; + } + this.add('-item', source, yourItem, '[from] ability: Greedy Magician', '[of] ' + target); + } + }, + isNonstandard: "Custom", + gen: 8, + }, + + // Nol + burningsoul: { + desc: "On switch-in, this Pokemon summons Sunny Day. If this Pokemon is at full HP, it survives one hit with at least 1 HP. OHKO moves fail when used against this Pokemon.", + shortDesc: "Drought + Sturdy.", + onStart(source) { + this.field.setWeather('sunnyday'); + }, + onTryHit(pokemon, target, move) { + if (move.ohko) { + this.add('-immune', pokemon, '[from] ability: Burning Soul'); + return null; + } + }, + onDamagePriority: -100, + onDamage(damage, target, source, effect) { + if (target.hp === target.maxhp && damage >= target.hp && effect && effect.effectType === 'Move') { + this.add('-ability', target, 'Burning Soul'); + return target.hp - 1; + } + }, + name: "Burning Soul", + isNonstandard: "Custom", + gen: 8, + }, + + // Notater517 + lastminutelag: { + desc: "This Pokemon applies the Recharge status to the opposing Pokemon if this Pokemon needs to recharge.", + shortDesc: "Applies Recharge volatile to the opposing Pokemon if this Pokemon has it.", + onModifyMove(move, pokemon, target) { + if (move.self?.volatileStatus === 'mustrecharge') { + if (!move.volatileStatus) { + move.volatileStatus = 'mustrecharge'; + } else { + if (!move.secondaries) move.secondaries = []; + move.secondaries.push({chance: 100, volatileStatus: 'mustrecharge'}); + } + } + }, + name: "Last-Minute Lag", + isNonstandard: "Custom", + gen: 8, + }, + + // nui + conditionoverride: { + desc: "This Pokemon can attract opponents regardless of gender. Pokemon that are attracted have their Special Defense stat reduced by 25%.", + shortDesc: "Attracts anyone. Attracted Pokemon have SpD reduced by 25%.", + // See conditions.ts for implementation + name: "Condition Override", + isNonstandard: "Custom", + gen: 8, + }, + + // pants + ghostspores: { + desc: "This Pokemon ignores the foe's stat boosts. On switch-out, this Pokemon regenerates 1/3 HP, rounded down. If this Pokemon is hit by an attack, Leech Seed is applied to the foe. If this Pokemon is KOed, Curse is applied to the foe.", + shortDesc: "Unaware + Regenerator. If hit, foe is Leech Seeded. If KOed, foe is Cursed.", + name: 'Ghost Spores', + onDamagingHit(damage, target, source, move) { + if (!target.hp) { + source.addVolatile('curse'); + } else { + source.addVolatile('leechseed', target); + } + }, + onAnyModifyBoost(boosts, pokemon) { + const unawareUser = this.effectData.target; + if (unawareUser === pokemon) return; + if (unawareUser === this.activePokemon && pokemon === this.activeTarget) { + boosts['def'] = 0; + boosts['spd'] = 0; + boosts['evasion'] = 0; + } + if (pokemon === this.activePokemon && unawareUser === this.activeTarget) { + boosts['atk'] = 0; + boosts['def'] = 0; + boosts['spa'] = 0; + boosts['accuracy'] = 0; + } + }, + onSwitchOut(pokemon) { + pokemon.heal(pokemon.baseMaxhp / 3); }, }, - // vivalospride - trashvivwebs: { - desc: "This Pokemon's attacking stat is doubled while it uses a Water-type attack. If a Pokemon uses a Fire-type attack against this Pokemon, that Pokemon's attacking stat is halved when calculating the damage to this Pokemon. This Pokemon cannot be burned. Gaining this Ability while burned cures it. Sets Sticky Web the first time it switches in.", - shortDesc: "Attack stat using Water 2x; burn immunity; foe's attack using Fire 0.5x; Sticky Web.", + + // PartMan + hecatomb: { + desc: "This Pokemon's Speed is raised by 1 stage if it attacks and knocks out another Pokemon. If the Pokemon is Chandelure and is not shiny, it changes its set.", + shortDesc: "Spe +1 on KOing foe. Chandelure: changes sets.", + name: 'Hecatomb', + onSourceAfterFaint(length, target, source, effect) { + if (effect && effect.effectType === 'Move') { + this.boost({spe: length}, source); + if (source.species.baseSpecies !== 'Chandelure') return; + if (source.set.shiny) return; + this.add(`c|${getName('PartMan')}|THE LIGHT! IT BURNS!`); + changeSet(this, source, ssbSets['PartMan-Shiny']); + } + }, + isNonstandard: "Custom", + gen: 8, + }, + + // peapod + stealthblack: { + desc: "No competitive use.", + name: 'Stealth Black', + isNonstandard: "Custom", + gen: 8, + }, + + // Perish Song + soupsipper: { + desc: "This Pokemon is immune to Grass- and Water-type moves, restores 1/4 of its maximum HP, rounded down, when hit by these types, and boosts its Attack by 1 stage when hit by these types.", + shortDesc: "Immune to Water and Grass moves, heals 1/4 HP and gains +1 Atk when hit by them.", + onTryHit(target, source, move) { + if (target !== source && ['Water', 'Grass'].includes(move.type)) { + if (!this.heal(target.baseMaxhp / 4) && !this.boost({atk: 1})) { + this.add('-immune', target, '[from] ability: Soup Sipper'); + } + return null; + } + }, + name: "Soup Sipper", + isNonstandard: "Custom", + gen: 8, + }, + + // phiwings99 + plausibledeniability: { + desc: "This Pokemon's Status moves have priority raised by 1, but Dark-types are immune. Additionally, This Pokemon ignores other Pokemon's Attack, Special Attack, and accuracy stat stages when taking damage, and ignores other Pokemon's Defense, Special Defense, and evasiveness stat stages when dealing damage.", + shortDesc: "Unaware + Prankster. Dark-types still immune to Prankster moves.", + name: "Plausible Deniability", + onAnyModifyBoost(boosts, pokemon) { + const unawareUser = this.effectData.target; + if (unawareUser === pokemon) return; + if (unawareUser === this.activePokemon && pokemon === this.activeTarget) { + boosts['def'] = 0; + boosts['spd'] = 0; + boosts['evasion'] = 0; + } + if (pokemon === this.activePokemon && unawareUser === this.activeTarget) { + boosts['atk'] = 0; + boosts['def'] = 0; + boosts['spa'] = 0; + boosts['spd'] = 0; + boosts['accuracy'] = 0; + } + }, + onModifyPriority(priority, pokemon, target, move) { + if (move?.category === 'Status') { + move.pranksterBoosted = true; + return priority + 1; + } + }, + isNonstandard: "Custom", + gen: 8, + }, + + // piloswine gripado + foreverwinternights: { + desc: "On switch-in, this Pokemon summons Winter Hail. Winter Hail is hail that also lowers the Speed of non-Ice-type Pokemon by 75%. This weather remains in effect until this Ability is no longer active for any Pokemon, or the weather is changed by Delta Stream, Desolate Land, or Primordial Sea.", + shortDesc: "Sets permahail until this Pokemon switches out. Non-Ice: 1/4 Speed", + onStart(source) { + this.field.setWeather('winterhail'); + }, + onAnySetWeather(target, source, weather) { + if (this.field.getWeather().id === 'winterhail' && !STRONG_WEATHERS.includes(weather.id)) return false; + }, + onEnd(pokemon) { + if (this.field.weatherData.source !== pokemon) return; + for (const target of this.getAllActive()) { + if (target === pokemon) continue; + if (target.hasAbility('winterhail')) { + this.field.weatherData.source = target; + return; + } + } + this.field.clearWeather(); + }, + name: "Forever Winter Nights", + isNonstandard: "Custom", + gen: 8, + }, + + // PiraTe Princess + wildmagicsurge: { + desc: "Randomly changes this Pokemon's type at the end of every turn to the type of one of its moves; same-type attack bonus (STAB) is 2 instead of 1.5.", + shortDesc: "Adaptability + Randomly changes to the type of one of its moves every turn.", + name: "Wild Magic Surge", + onModifyMove(move) { + move.stab = 2; + }, + onResidual(pokemon) { + if (!pokemon.hp) return; + const moves = Object.values(pokemon.getMoves()).map(move => move.id); + const types: string[] = []; + for (const move of moves) { + types.push(this.dex.getMove(move).type); + } + let type = this.sample(types); + while (!pokemon.setType(type)) { + type = this.sample(types); + } + this.add('-start', pokemon, 'typechange', type); + }, + isNonstandard: "Custom", + gen: 8, + }, + + // Psynergy + supernova: { + desc: "On switch-in, if total positive boosts - total negative boosts ≥ 8, both Pokemon faint.", + onStart(source) { + let result = 0; + const pokemon = this.getAllActive(); + for (const poke of pokemon) { + result += Object.values(poke.boosts).reduce((total, x) => total + x); + } + if (result < 8) return; + this.add('-ability', source, 'Supernova'); + for (const x of pokemon) { + this.add('-anim', x, 'Explosion', x); + x.faint(); + } + }, + name: "Supernova", + isNonstandard: "Custom", + gen: 8, + }, + + // ptoad + swampysurge: { + desc: "On switch-in, this Pokemon summons Swampy Terrain. Swampy Terrain halves the power of Electric-, Grass-, and Ice-type moves used by grounded Pokemon and heals grounded Water- and Ground-types by 1/16 of their maximum HP, rounded down, each turn.", + shortDesc: "5 turns: Grounded: 1/2 Elec/Grass/Ice power, +1/16 HP/turn for Water or Ground.", + onStart(source) { + this.field.setTerrain('swampyterrain'); + }, + name: "Swampy Surge", + isNonstandard: "Custom", + gen: 8, + }, + + // quadrophenic + extremeways: { + desc: "On switch-in, this Pokemon boosts a random stat by 2 stages.", + onStart(source) { + const stats: BoostName[] = []; + let stat: BoostName; + for (stat in source.boosts) { + if (stat !== 'accuracy' && stat !== 'evasion' && source.boosts[stat] < 6) { + stats.push(stat); + } + } + if (stats.length) { + const randomStat = this.sample(stats); + const boost: SparseBoostsTable = {}; + boost[randomStat] = 2; + this.boost(boost); + } else { + return; + } + }, + name: "Extreme Ways", + isNonstandard: "Custom", + gen: 8, + }, + + // Rach + burnitdown: { + desc: "On switch-in, this Pokemon lowers the foe's higher offensive stat. This Pokemon restores 1/3 of its maximum HP, rounded down, when it switches out.", + shortDesc: "Lower the foe's higher offensive stat. Regenerator.", + onSwitchOut(pokemon) { + pokemon.heal(pokemon.baseMaxhp / 3); + }, onStart(pokemon) { - if (!pokemon.m.stickyweb) { - this.useMove("stickyweb", pokemon); - pokemon.m.stickyweb = true; + let totalatk = 0; + let totalspa = 0; + for (const target of pokemon.side.foe.active) { + if (!target || target.fainted) continue; + totalatk += target.getStat('atk', false, true); + totalspa += target.getStat('spa', false, true); + } + for (const target of pokemon.side.foe.active) { + if (!target || target.fainted) continue; + this.add('-ability', pokemon, 'BURN IT DOWN!'); + if (totalatk && totalatk >= totalspa) { + this.boost({atk: -1}, target, pokemon, null, true); + } else if (totalspa) { + this.boost({spa: -1}, target, pokemon, null, true); + } } }, - onModifyAtkPriority: 5, - onSourceModifyAtk(atk, attacker, defender, move) { - if (move.type === 'Fire') { - return this.chainModify(0.5); - } + name: "BURN IT DOWN!", + isNonstandard: "Custom", + gen: 8, + }, + + // Rage + inversionsurge: { + desc: "On switch-in, this Pokemon summons Inversion Terrain. While Inversion Terrain is active, type effectiveness for all Pokemon on the field is inverted, and paralyzed Pokemon have doubled, instead of halved, Speed.", + shortDesc: "Summons Inversion Terrain; 5 turns: Inverse Battle, par: 2x Spe.", + onStart(source) { + this.field.setTerrain('inversionterrain'); }, - onModifySpAPriority: 5, - onSourceModifySpA(atk, attacker, defender, move) { - if (move.type === 'Fire') { - return this.chainModify(0.5); - } - }, - onModifyAtk(atk, attacker, defender, move) { - if (move.type === 'Water') { + name: "Inversion Surge", + isNonstandard: "Custom", + gen: 8, + }, + + // Raihan Kibana + royalcoat: { + desc: "If Sandstorm is active, this Pokemon's Speed is doubled and its Special Defense is multiplied by 1.5. This Pokemon takes no damage from Sandstorm.", + shortDesc: "If Sandstorm, Speed x2 and SpD x1.5; immunity to Sandstorm.", + name: "Royal Coat", + onModifySpe(spe, pokemon) { + if (this.field.isWeather('sandstorm')) { return this.chainModify(2); } }, - onModifySpA(atk, attacker, defender, move) { - if (move.type === 'Water') { - return this.chainModify(2); + onModifySpD(spd, pokemon) { + if (this.field.isWeather('sandstorm')) { + return this.chainModify(1.5); } }, + onImmunity(type, pokemon) { + if (type === 'sandstorm') return false; + }, + isNonstandard: "Custom", + gen: 8, + }, + + // RavioliQueen + phantomplane: { + desc: "On switch-in, this Pokemon summons Pitch Black Terrain. While Pitch Black Terrain is active, all non-Ghost-type Pokemon take damage equal to 1/16 of their max HP, rounded down, at the end of each turn.", + shortDesc: "Summons Pitch Black Terrain, which damages non-Ghosts by 1/16 per turn.", + onStart(source) { + this.field.setTerrain('pitchblackterrain'); + }, + name: "Phantom Plane", + isNonstandard: "Custom", + gen: 8, + }, + + // Robb576 + thenumbersgame: { + desc: "If this Pokemon is a forme of Necrozma, its forme changes on switch-in depending on the number of unfainted Pokemon on the user's team: Necrozma-Dusk-Mane if 3 or fewer Pokemon and Necrozma-Dawn-Wings was sent out already; Necrozma-Ultra if it is the last Pokemon left on the team and Necrozma-Dusk-Mane was sent out already.", + shortDesc: "Changes forme on switch-in depending on # of remaining Pokemon on user's team.", + name: "The Numbers Game", + isPermanent: true, + onStart(target) { + if (target.baseSpecies.baseSpecies !== 'Necrozma' || target.transformed) return; + if (target.side.pokemonLeft <= 3) { + if (target.species.name === 'Necrozma-Dusk-Mane' && target.side.pokemonLeft === 1 && target.m.flag2) { + changeSet(this, target, ssbSets['Robb576-Ultra']); + } else if (target.species.name === "Necrozma-Dawn-Wings" && target.m.flag1) { + changeSet(this, target, ssbSets['Robb576-Dusk-Mane']); + target.m.flag2 = true; + } + } + target.m.flag1 = true; + }, + isNonstandard: "Custom", + gen: 8, + }, + + // SectoniaServant + royalaura: { + desc: "If this Pokemon is the target of an opposing Pokemon's move, that move loses one additional PP. Moves used by this Pokemon only use 0.5 PP.", + shortDesc: "Pressure, and this Pokemon uses 0.5 PP per move.", + name: "Royal Aura", + onStart(pokemon) { + this.add('-ability', pokemon, 'Royal Aura'); + }, + onDeductPP(target, source) { + if (target.side === source.side) return; + return 1; + }, + onTryMove(pokemon, target, move) { + const moveData = pokemon.getMoveData(move.id); + if (!moveData) return; + // Lost 1 PP due to move usage, restore 0.5 PP to make it so that only 0.5 PP + // would be used. + moveData.pp = (Math.round(moveData.pp * 100) + 50) / 100; + }, + isNonstandard: "Custom", + gen: 8, + }, + + // Segmr + skilldrain: { + desc: "While this Pokemon is active, no moves will trigger their secondary effects, and moves that cause the user to switch out will no longer do so.", + shortDesc: "While active: no secondary effects, moves can't switch out.", + name: "Skill Drain", + onAnyModifyMove(move) { + delete move.secondaries; + }, + // afterSecondarySelf and switch nullifying handled in ssb/scripts.ts + isNonstandard: "Custom", + gen: 8, + }, + + // sejesensei + trashconsumer: { + desc: "This Pokemon is immune to Poison-type moves and restores 1/4 of its maximum HP, rounded down, when hit by a Poison-type move. Pokemon making contact with this Pokemon lose 1/8 of their maximum HP, rounded down.", + shortDesc: "Poison Absorb + Rough Skin", + name: "Trash Consumer", + onTryHit(target, source, move) { + if (target !== source && move.type === 'Poison') { + if (!this.heal(target.baseMaxhp / 4)) { + this.add('-immune', target, '[from] ability: Trash Consumer'); + } + return null; + } + }, + onDamagingHitOrder: 1, + onDamagingHit(damage, target, source, move) { + if (move.flags['contact']) { + this.damage(source.baseMaxhp / 8, source, target); + } + }, + isNonstandard: "Custom", + gen: 8, + }, + + // Shadecession + shadydeal: { + desc: "On switch-in, this Pokemon boosts a random stat other than Special Attack by 1 stage and gains 2 random type immunities that are displayed to the opponent.", + shortDesc: "On switch-in, gains random +1 to non-SpA, 2 random immunities.", + onStart(pokemon) { + const stats: BoostName[] = []; + let stat: BoostName; + for (stat in pokemon.boosts) { + const noBoost: string[] = ['accuracy', 'evasion', 'spa']; + if (!noBoost.includes(stat) && pokemon.boosts[stat] < 6) { + stats.push(stat); + } + } + if (stats.length) { + const randomStat = this.sample(stats); + const boost: SparseBoostsTable = {}; + boost[randomStat] = 1; + this.boost(boost); + } + if (this.effectData.immunities) return; + const typeList = Object.keys(this.dex.data.TypeChart); + const firstTypeIndex = this.random(typeList.length); + const secondType = this.sample(typeList.slice(0, firstTypeIndex).concat(typeList.slice(firstTypeIndex + 1))); + this.effectData.immunities = [typeList[firstTypeIndex], secondType]; + this.add('-start', pokemon, `${this.effectData.immunities[0]} Immunity`, '[silent]'); + this.add('-start', pokemon, `${this.effectData.immunities[1]} Immunity`, '[silent]'); + this.add("-message", `${pokemon.name} is now immune to ${this.effectData.immunities[0]} and ${this.effectData.immunities[1]} type attacks!`); + }, + onTryHit(target, source, move) { + if (target !== source && this.effectData.immunities?.includes(move.type)) { + this.add('-immune', target, '[from] ability: Shady Deal'); + return null; + } + }, + onEnd(pokemon) { + this.add('-end', pokemon, `${this.effectData.immunities[0]} Immunity`, '[silent]'); + this.add('-end', pokemon, `${this.effectData.immunities[1]} Immunity`, '[silent]'); + delete this.effectData.immunities; + }, + name: "Shady Deal", + isNonstandard: "Custom", + gen: 8, + }, + + // Soft Flex + eyeofthestorm: { + name: "Eye of the Storm", + desc: "On switch-in, this Pokemon summons Rain Dance and Tempest Terrain. While Tempest Terrain is active, Electric-type Pokemon are healed by 1/16 of their maximum HP, rounded down, at the end of each turn, and Flying- and Steel-type Pokemon lose 1/16 of their maximum HP, rounded down, at the end of each turn. If the Flying- or Steel-type Pokemon is also Electric-type, they only receive the healing.", + shortDesc: "5 turns: Rain, +1/16 HP/turn to Elec, -1/16/turn to Fly/Steel.", + onStart(source) { + this.field.setWeather('raindance', source); + this.field.setTerrain('tempestterrain', source); + }, + }, + // Spandan + hackedcorrosion: { + desc: "This Pokemon ignores other Pokemon's stat stages when taking or doing damage. This Pokemon can poison or badly poison Pokemon regardless of their typing.", + shortDesc: "Unaware + Corrosion.", + onAnyModifyBoost(boosts, pokemon) { + const unawareUser = this.effectData.target; + if (unawareUser === pokemon) return; + if (unawareUser === this.activePokemon && pokemon === this.activeTarget) { + boosts['def'] = 0; + boosts['spd'] = 0; + boosts['evasion'] = 0; + } + if (pokemon === this.activePokemon && unawareUser === this.activeTarget) { + boosts['atk'] = 0; + boosts['def'] = 0; + boosts['spa'] = 0; + boosts['spd'] = 0; + boosts['accuracy'] = 0; + } + }, + name: "Hacked Corrosion", + }, + + // Struchni + overaskedclause: { + desc: "If this Pokemon is an Aggron and is hit by a move that is not very effective, this Pokemon becomes Aggron-Mega and its Attack is boosted by 1 stage.", + shortDesc: "Aggron: If hit by resisted move, Mega Evolve and gain +1 Atk.", + name: "Overasked Clause", + isPermanent: true, + onHit(target, source, move) { + if (target.getMoveHitData(move).typeMod < 0) { + if (!target.hp) return; + if (target.species.id.includes('aggron') && !target.illusion && !target.transformed) { + this.boost({atk: 1}, target); + if (target.species.name !== 'Aggron') return; + this.runMegaEvo(target); + } + } + }, + isNonstandard: "Custom", + gen: 8, + }, + + // Teclis + fieryfur: { + name: "Fiery Fur", + desc: "If this Pokemon is at full HP, damage taken from attacks is halved.", + onSourceModifyDamage(damage, source, target, move) { + if (target.hp >= target.maxhp) { + this.debug('Fiery Fur weaken'); + return this.chainModify(0.5); + } + }, + }, + + // temp + chargedup: { + desc: "If this Pokemon has a negative stat boost at -2 or lower, this Pokemon's negative stat boosts are cleared.", + shortDesc: "Resets negative stat boosts if there is one at -2 or lower.", + name: "Charged Up", onUpdate(pokemon) { - if (pokemon.status === 'brn') { - this.add('-activate', pokemon, 'ability: Water Bubble'); - pokemon.cureStatus(); + let activate = false; + const boosts: SparseBoostsTable = {}; + let i: BoostName; + for (i in pokemon.boosts) { + if (pokemon.boosts[i] <= -2) { + activate = true; + boosts[i] = 0; + } + } + if (activate) { + pokemon.setBoost(boosts); + this.add('-activate', pokemon, 'ability: Charged Up'); + this.add('-clearnegativeboost', pokemon); } }, - onSetStatus(status, target, source, effect) { - if (status.id !== 'brn') return; - if ((effect as Move)?.status) { - this.add('-immune', target, '[from] ability: Water Bubble'); + isNonstandard: "Custom", + gen: 8, + }, + + // tiki + truegrit: { + desc: "This Pokemon receives 1/2 damage from special attacks. This Pokemon ignores other Pokemon's Attack, Special Attack, and accuracy stat stages when taking damage, and ignores other Pokemon's Defense, Special Defense, and evasiveness stat stages when dealing damage.", + shortDesc: "Takes 1/2 damage from special moves and ignores boosts.", + name: "True Grit", + onSourceModifyDamage(damage, source, target, move) { + if (move.category === 'Special') { + return this.chainModify(0.5); } + }, + onAnyModifyBoost(boosts, pokemon) { + const unawareUser = this.effectData.target; + if (unawareUser === pokemon) return; + if (unawareUser === this.activePokemon && pokemon === this.activeTarget) { + boosts['def'] = 0; + boosts['spd'] = 0; + boosts['evasion'] = 0; + } + if (pokemon === this.activePokemon && unawareUser === this.activeTarget) { + boosts['atk'] = 0; + boosts['def'] = 0; + boosts['spa'] = 0; + boosts['spd'] = 0; + boosts['accuracy'] = 0; + } + }, + isNonstandard: "Custom", + gen: 8, + }, + + // Trickster + trillionageroots: { + desc: "This Pokemon applies Leech Seed to the opposing Pokemon when hit with an attacking move. If this Pokemon is at full HP, it survives one hit with at least 1 HP. OHKO moves fail when used against this Pokemon.", + shortDesc: "Sturdy + apply Leech Seed when hit by foe.", + onTryHit(pokemon, target, move) { + if (move.ohko) { + this.add('-immune', pokemon, '[from] ability: Trillionage Roots'); + return null; + } + }, + onDamagePriority: -100, + onDamage(damage, target, source, effect) { + if (target.hp === target.maxhp && damage >= target.hp && effect && effect.effectType === 'Move') { + this.add('-ability', target, 'Trillionage Roots'); + return target.hp - 1; + } + }, + onDamagingHit(damage, target, source, move) { + if (source.volatiles['leechseed']) return; + if (!move.isFutureMove) { + source.addVolatile('leechseed', this.effectData.target); + } + }, + name: "Trillionage Roots", + isNonstandard: "Custom", + gen: 8, + }, + + // Volco + speedrunning: { + desc: "This Pokemon's Special Attack is raised by 1 stage when another Pokemon faints. Moves used by this Pokemon that are 60 Base Power or lower gain an additional 25 Base Power. No moves can defrost a frozen Pokemon while this Pokemon is active.", + shortDesc: "Soul Heart + Weak moves get +25 BP. Moves cannot defrost, only natural thaws.", + onAnyFaintPriority: 1, + onAnyFaint() { + this.boost({spa: 1}, this.effectData.target); + }, + onAnyModifyMove(move, pokemon) { + if (move.thawsTarget) { + delete move.thawsTarget; + } + if (move.flags["defrost"]) { + delete move.flags["defrost"]; + } + }, + onBasePowerPriority: 21, + onBasePower(basePower, pokemon, target, move) { + if (move.basePower <= 60) return basePower + 25; + }, + name: "Speedrunning", + isNonstandard: "Custom", + gen: 8, + }, + + // Vexen + aquilasblessing: { + desc: "This Pokemon's attacks with secondary effects have their power multiplied by 1.3, but the secondary effects are removed. If this Pokemon gets hit by a damaging Fire type move, its Defense and Special Defense get raised by 1 stage.", + shortDesc: "Sheer Force + when hit with Fire move: +1 Def/SpD.", + onModifyMove(move, pokemon) { + if (move.secondaries) { + delete move.secondaries; + // Technically not a secondary effect, but it is negated + if (move.id === 'clangoroussoulblaze') delete move.selfBoost; + // Actual negation of `AfterMoveSecondary` effects implemented in scripts.js + move.hasSheerForce = true; + } + }, + onBasePowerPriority: 21, + onBasePower(basePower, pokemon, target, move) { + if (move.hasSheerForce) return this.chainModify([0x14CD, 0x1000]); + }, + onDamagingHit(damage, target, source, move) { + if (move.type === 'Fire') { + this.boost({def: 1, spd: 1}); + } + }, + name: "Aquila's Blessing", + isNonstandard: "Custom", + gen: 8, + }, + + // vooper + qigong: { + desc: "This Pokemon's Defense is doubled, and it receives 1/2 damage from special attacks.", + onModifyDefPriority: 6, + onModifyDef(def) { + return this.chainModify(2); + }, + onSourceModifyDamage(damage, source, target, move) { + if (move.category === 'Special') { + return this.chainModify(0.5); + } + }, + name: "Qi-Gong", + isNonstandard: "Custom", + gen: 8, + }, + + // yuki + combattraining: { + desc: "If this Pokemon is a Cosplay Pikachu forme, the first hit it takes in battle deals 0 neutral damage. Confusion damage also breaks the immunity.", + shortDesc: "(Pikachu-Cosplay only) First hit deals 0 damage.", + isPermanent: true, + onDamagePriority: 1, + onDamage(damage, target, source, effect) { + const cosplayFormes = [ + 'pikachucosplay', 'pikachuphd', 'pikachulibre', 'pikachupopstar', 'pikachurockstar', 'pikachubelle', + ]; + if ( + effect?.effectType === 'Move' && + cosplayFormes.includes(target.species.id) && !target.transformed && + !this.effectData.busted + ) { + this.add('-activate', target, 'ability: Combat Training'); + this.effectData.busted = true; + return 0; + } + }, + onCriticalHit(target, source, move) { + if (!target) return; + const cosplayFormes = [ + 'pikachucosplay', 'pikachuphd', 'pikachulibre', 'pikachupopstar', 'pikachurockstar', 'pikachubelle', + ]; + if (!cosplayFormes.includes(target.species.id) || target.transformed) { + return; + } + const hitSub = target.volatiles['substitute'] && !move.flags['authentic'] && !(move.infiltrates && this.gen >= 6); + if (hitSub) return; + + if (!target.runImmunity(move.type)) return; return false; }, - name: "TRASH VIV WEBS", - }, - // xJoelituh - clubexpertise: { - desc: "This Pokemon's bone moves have their Base Power multiplied by 1.3.", - shortDesc: "This Pokemon's bone moves have their power multiplied by 1.3.", - name: "Club Expertise", - isNonstandard: "Custom", - onBasePowerPriority: 8, - onBasePower(basePower, attacker, defender, move) { - if (move.id.includes('bone')) { - return this.chainModify([0x14CD, 0x1000]); + onEffectiveness(typeMod, target, type, move) { + if (!target) return; + const cosplayFormes = [ + 'pikachucosplay', 'pikachuphd', 'pikachulibre', 'pikachupopstar', 'pikachurockstar', 'pikachubelle', + ]; + if (!cosplayFormes.includes(target.species.id) || target.transformed) { + return; } + const hitSub = target.volatiles['substitute'] && !move.flags['authentic'] && !(move.infiltrates && this.gen >= 6); + if (hitSub) return; + + if (!target.runImmunity(move.type)) return; + return 0; }, - }, - // Yuki - snowstorm: { - desc: "As it switches in, this Pokemon summons Hail that remains in effect until replaced by another weather or suppressed by the effects of Cloud Nine, Air Lock, or Delta Stream.", - shortDesc: "On switch-in, this Pokemon summons Hail which remains active until replaced.", - name: "Snow Storm", + name: "Combat Training", isNonstandard: "Custom", - onStart() { - const snowStorm = this.dex.getEffect('hail'); - this.field.setWeather(snowStorm); - }, + gen: 8, }, // Modified Illusion to support SSB volatiles illusion: { @@ -1090,7 +2165,7 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { (pokemon.gender === '' ? '' : ', ' + pokemon.gender) + (pokemon.set.shiny ? ', shiny' : ''); this.add('replace', pokemon, details); this.add('-end', pokemon, 'Illusion'); - // Handle hippopotas + // Handle users whose names match a species if (this.dex.getSpecies(disguisedAs).exists) disguisedAs += 'user'; if (pokemon.volatiles[disguisedAs]) { pokemon.removeVolatile(disguisedAs); @@ -1104,19 +2179,141 @@ export const Abilities: {[k: string]: ModdedAbilityData} = { } }, }, - // Modified Primordial Sea to not end if a pokemon with Maelstrom is out - primordialsea: { + + // Modified various abilities to support Alpha's move & pilo's abiility + deltastream: { inherit: true, - onEnd(pokemon) { - if (this.field.weatherData.source !== pokemon) return; - for (const target of this.getAllActive()) { - if (target === pokemon) continue; - if (target.hasAbility('primordialsea') || target.hasAbility('maelstrom')) { - this.field.weatherData.source = target; - return; - } + desc: "On switch-in, the weather becomes strong winds that remove the weaknesses of the Flying type from Flying-type Pokemon. This weather remains in effect until this Ability is no longer active for any Pokemon, or the weather is changed by Desolate Land, Heavy Hailstorm, or Primordial Sea.", + shortDesc: "On switch-in, strong winds begin until this Ability is not active in battle.", + onAnySetWeather(target, source, weather) { + if (this.field.getWeather().id === 'deltastream' && !STRONG_WEATHERS.includes(weather.id)) return false; + }, + }, + desolateland: { + inherit: true, + desc: "On switch-in, the weather becomes extremely harsh sunlight that prevents damaging Water-type moves from executing, in addition to all the effects of Sunny Day. This weather remains in effect until this Ability is no longer active for any Pokemon, or the weather is changed by Delta Stream, Heavy Hailstorm, or Primordial Sea.", + shortDesc: "On switch-in, extremely harsh sunlight begins until this Ability is not active in battle.", + onAnySetWeather(target, source, weather) { + if (this.field.getWeather().id === 'desolateland' && !STRONG_WEATHERS.includes(weather.id)) return false; + }, + }, + forecast: { + inherit: true, + onUpdate(pokemon) { + if (pokemon.baseSpecies.baseSpecies !== 'Castform' || pokemon.transformed) return; + let forme = null; + switch (pokemon.effectiveWeather()) { + case 'sunnyday': + case 'desolateland': + if (pokemon.species.id !== 'castformsunny') forme = 'Castform-Sunny'; + break; + case 'raindance': + case 'primordialsea': + if (pokemon.species.id !== 'castformrainy') forme = 'Castform-Rainy'; + break; + case 'winterhail': + case 'heavyhailstorm': + case 'hail': + if (pokemon.species.id !== 'castformsnowy') forme = 'Castform-Snowy'; + break; + default: + if (pokemon.species.id !== 'castform') forme = 'Castform'; + break; + } + if (pokemon.isActive && forme) { + pokemon.formeChange(forme, this.effect, false, '[msg]'); + } + }, + }, + icebody: { + inherit: true, + desc: "If Hail or Heavy Hailstorm is active, this Pokemon restores 1/16 of its maximum HP, rounded down, at the end of each turn. This Pokemon takes no damage from Hail or Heavy Hailstorm.", + shortDesc: "Hail-like weather active: heals 1/16 max HP each turn; immunity to Hail-like weather.", + onWeather(target, source, effect) { + if (['heavyhailstorm', 'hail', 'winterhail'].includes(effect.id)) { + this.heal(target.baseMaxhp / 16); + } + }, + onImmunity(type, pokemon) { + if (['heavyhailstorm', 'hail', 'winterhail'].includes(type)) return false; + }, + }, + iceface: { + inherit: true, + desc: "If this Pokemon is an Eiscue, the first physical hit it takes in battle deals 0 neutral damage. Its ice face is then broken and it changes forme to Noice Face. Eiscue regains its Ice Face forme when Hail or Heavy Hailstorm begins or when Eiscue switches in while Hail or Heavy Hailstorm is active. Confusion damage also breaks the ice face.", + shortDesc: "If Eiscue, first physical hit taken deals 0 damage. Effect is restored in Hail-like weather.", + onStart(pokemon) { + if (this.field.isWeather(['heavyhailstorm', 'hail', 'winterhail']) && + pokemon.species.id === 'eiscuenoice' && !pokemon.transformed) { + this.add('-activate', pokemon, 'ability: Ice Face'); + this.effectData.busted = false; + pokemon.formeChange('Eiscue', this.effect, true); + } + }, + onAnyWeatherStart() { + const pokemon = this.effectData.target; + if (this.field.isWeather(['heavyhailstorm', 'hail', 'winterhail']) && + pokemon.species.id === 'eiscuenoice' && !pokemon.transformed) { + this.add('-activate', pokemon, 'ability: Ice Face'); + this.effectData.busted = false; + pokemon.formeChange('Eiscue', this.effect, true); + } + }, + }, + slushrush: { + inherit: true, + shortDesc: "If a Hail-like weather is active, this Pokemon's Speed is doubled.", + onModifySpe(spe, pokemon) { + if (this.field.isWeather(['heavyhailstorm', 'hail', 'winterhail'])) { + return this.chainModify(2); + } + }, + }, + snowcloak: { + inherit: true, + desc: "If Heavy Hailstorm, Winter Hail, or Hail is active, this Pokemon's evasiveness is multiplied by 1.25. This Pokemon takes no damage from Heavy Hailstorm or Hail.", + shortDesc: "If a Hail-like weather is active, 1.25x evasion; immunity to Hail-like weathers.", + onImmunity(type, pokemon) { + if (['heavyhailstorm', 'hail', 'winterhail'].includes(type)) return false; + }, + onModifyAccuracy(accuracy) { + if (typeof accuracy !== 'number') return; + if (this.field.isWeather(['heavyhailstorm', 'hail', 'winterhail'])) { + this.debug('Snow Cloak - decreasing accuracy'); + return accuracy * 0.8; + } + }, + }, + // Modified Magic Guard for Alpha + magicguard: { + inherit: true, + shortDesc: "This Pokemon can only be damaged by direct attacks and Heavy Hailstorm.", + onDamage(damage, target, source, effect) { + if (effect.id === 'heavyhailstorm') return; + if (effect.effectType !== 'Move') { + if (effect.effectType === 'Ability') this.add('-activate', source, 'ability: ' + effect.name); + return false; + } + }, + }, + // Modified Unaware for Blaz's move + unaware: { + inherit: true, + onAnyModifyBoost(boosts, pokemon) { + const unawareUser = this.effectData.target; + if (unawareUser === pokemon) return; + if (unawareUser === this.activePokemon && pokemon === this.activeTarget) { + boosts['def'] = 0; + boosts['spd'] = 0; + boosts['evasion'] = 0; + } + if (pokemon === this.activePokemon && unawareUser === this.activeTarget) { + boosts['atk'] = 0; + boosts['def'] = 0; + boosts['spa'] = 0; + boosts['spd'] = 0; + boosts['accuracy'] = 0; } - this.field.clearWeather(); }, }, }; diff --git a/data/mods/ssb/conditions.ts b/data/mods/ssb/conditions.ts index 1b6f6fbd00..209f973a72 100644 --- a/data/mods/ssb/conditions.ts +++ b/data/mods/ssb/conditions.ts @@ -1,1854 +1,2254 @@ -export const Conditions: {[k: string]: ModdedConditionData} = { +import {FS} from '../../../lib/fs'; +import {toID} from '../../../sim/dex-data'; + +// Used in many abilities, placed here to reduce the number of updates needed and to reduce the chance of errors +const STRONG_WEATHERS = ['desolateland', 'primordialsea', 'deltastream', 'heavyhailstorm', 'winterhail']; + +// Similar to User.usergroups. Cannot import here due to users.ts requiring Chat +// This also acts as a cache, meaning ranks will only update when a hotpatch/restart occurs +const usergroups: {[userid: string]: string} = {}; +const usergroupData = FS('config/usergroups.csv').readIfExistsSync().split('\n'); +for (const row of usergroupData) { + if (!toID(row)) continue; + + const cells = row.split(','); + if (cells.length !== 2) throw new Error(`Invalid entry when parsing usergroups.csv`); + usergroups[toID(cells[0])] = cells[1].trim() || ' '; +} + +export function getName(name: string): string { + const userid = toID(name); + if (!userid) throw new Error('No/Invalid name passed to getSymbol'); + + const group = usergroups[userid] || ' '; + return group + name; +} + +export const Conditions: {[k: string]: ModdedConditionData & {innateName?: string}} = { /* // Example: userid: { noCopy: true, onStart() { - this.add(`c|+Username|Switch In Message`); + this.add(`c|${getName('Username')}|Switch In Message`); }, onSwitchOut() { - this.add(`c|+Username|Switch Out Message`); + this.add(`c|${getName('Username')}|Switch Out Message`); }, onFaint() { - this.add(`c|+Username|Faint Message`); + this.add(`c|${getName('Username')}|Faint Message`); }, // Innate effects go here }, + IMPORTANT: Obtain the username from getName */ // Please keep statuses organized alphabetically based on staff member name! - '2xthetap': { // No single quotes causes issues + abdelrahman: { noCopy: true, onStart() { - this.add(`c|+2xTheTap|Time for a heckin' battle.`); + this.add(`c|${getName('Abdelrahman')}|good morning, i'm town`); }, onSwitchOut() { - this.add(`c|+2xTheTap|Doin' me a heckin' concern.`); + this.add(`c|${getName('Abdelrahman')}|brb gonna go lynch scum`); }, onFaint() { - this.add(`c|+2xTheTap|Doin' me the final bamboozle.`); + this.add(`c|${getName('Abdelrahman')}|I CC COP TOWN FAILED`); }, }, - '5gen': { + adri: { noCopy: true, - onStart(source) { - this.add(`c|+5gen|Someone asked for extra sauce?`); - if (source.illusion) return; - this.field.setWeather('sunnyday', source); + onStart() { + this.add(`c|${getName('Adri')}|This time will definitely be the one !`); }, onSwitchOut() { - this.add(`c|+5gen|Need to get some from the back.`); + this.add(`c|${getName('Adri')}|//afk`); }, onFaint() { - this.add(`c|+5gen|I'm not dead yet, just changing formes.`); + this.add(`c|${getName('Adri')}|Until next time...`); }, }, aelita: { noCopy: true, onStart() { - this.add(`c|@Aelita|Transfer, Aelita! Scanner, Aelita! Virtualization!`); + this.add(`c|${getName('Aelita')}|The Scyphozoa's absorbing Aelita's memories!`); }, onSwitchOut() { - this.add(`c|@Aelita|I have a tower to deactivate. See ya!`); + this.add(`c|${getName('Aelita')}|We scared it away but it will be back. We can't let it get ahold of Aelita's memories.`); }, onFaint() { - this.add(`c|@Aelita|CODE: LYOKO . Tower deactivated... Return to the past, now!`); + this.add(`c|${getName('Aelita')}|X.A.N.A. is finally finished for good.`); + }, + }, + aegii: { + noCopy: true, + onStart() { + this.add(`c|${getName('aegii')}|shoot! take a pano~rama~ https://youtu.be/G8GaQdW2wHc`); + }, + onSwitchOut() { + this.add(`c|${getName('aegii')}|${[`brb, buying albums`, `brb, downloading fancams`, `brb, streaming mvs`, `brb, learning choreos`][this.random(4)]}`); + }, + onFaint() { + this.add(`c|${getName('aegii')}|i forgot to stan loona...`); }, }, aeonic: { noCopy: true, onStart() { - this.add(`c|&Aeonic|Guys the emoji movie wasn't __that__ bad`); + this.add(`c|${getName('Aeonic')}|What's bonkin?`); }, onSwitchOut() { - this.add(`c|&Aeonic|Still better than kie btw`); + this.add(`c|${getName('Aeonic')}|I am thou, thou art I`); }, onFaint() { - this.add(`c|&Aeonic|Don't forget me`); + this.add(`c|${getName('Aeonic')}|Guys the emoji movie wasn't __that bad__`); }, }, aethernum: { noCopy: true, onStart() { - this.add(`c|@Aethernum|I am __Eterno__, fear me! ...or not...but you should! ...or not...`); + this.add(`c|${getName('Aethernum')}|Hlelo ^_^ Lotad is so cute, don't you think? But don't underestimate him!`); }, onSwitchOut() { - this.add(`c|@Aethernum|I'm not defeated, i'm just afk :^)`); + this.add(`c|${getName('Aethernum')}|Sinking in this sea of possibilities for now...but i'll float back once again!`); }, onFaint() { - this.add(`c|@Aethernum| Has been fun! But i'm too lazy to keep fighting ^_^'`); - }, - }, - akiamara: { - noCopy: true, - onStart() { - this.add(`c|+Akiamara|alguem br?`); - }, - onSwitchOut() { - this.add(`c|+Akiamara|Pode me multar que já volto`); - }, - onFaint() { - this.add(`c|+Akiamara|I'm going to report you to my big fren Mitsuki`); + this.add(`c|${getName('Aethernum')}|Ok, ok, i have procrastinated enough here, time to go ^_^' See ya around!`); }, }, akir: { noCopy: true, onStart() { - this.add(`c|%Akir|hey whats up`); + this.add(`c|${getName('Akir')}|hey whats up`); }, - onSwitchOut(pokemon) { - this.add(`c|%Akir|sorry need to build more`); - if (pokemon.illusion) return; - pokemon.heal(pokemon.baseMaxhp / 3); + onSwitchOut() { + this.add(`c|${getName('Akir')}|let me get back to you`); }, onFaint() { - this.add(`c|%Akir|too sleepy, c ya`); - }, - onSourceModifyDamage(damage, source, target, move) { - if (target.getMoveHitData(move) && !target.illusion) { - this.debug('Mushroom Guard halves damage.'); - return this.chainModify(0.5); - } + this.add(`c|${getName('Akir')}|ah well maybe next time`); }, }, alpha: { noCopy: true, onStart() { - this.add(`c|@Alpha|MAMMA MIA! It's me, ALPHA!`); + this.add(`c|${getName('Alpha')}|eccomi dimmi`); }, onSwitchOut() { - this.add(`c|@Alpha|brb gonna eat some pizza`); + this.add(`c|${getName('Alpha')}|FRATM FACI FRIDDU`); }, onFaint() { - this.add(`c|@Alpha|PER LA PATRIA!`); + this.add(`c|${getName('Alpha')}|caio`); }, }, andrew: { noCopy: true, onStart() { - this.add(`c|@Andrew|Hah! You better have BURN HEAL!`); + this.add(`c|${getName('Andrew')}|/me vents in`); }, onSwitchOut() { - this.add(`c|@Andrew|brb kuncing`); + this.add(`c|${getName('Andrew')}|purple sus`); }, onFaint() { - this.add(`c|@Andrew|<_<`); + this.add(`c|${getName('Andrew')}|EasyOnTheHills is impostor! He vented in front of me in admin! Vote him out next!`); }, }, - anubis: { + annika: { noCopy: true, onStart() { - this.add(`c|+Anubis|hi ur qt`); - // In loving memory of the SSB programming team's sanity. - if (this.random(300) === 272) this.add(`c|&HoeenHero|Anubis's set is OP against programmer sanity.`); + this.add(`c|${getName('Annika')}|The circumstances of one's birth are irrelevant; it is what you do with the gift of life that determines who you are.`); }, onSwitchOut() { - this.add(`c|+Anubis|brb making coffee`); + this.add(`c|${getName('Annika')}|I'll be stronger when I'm back ^_^`); }, onFaint() { - this.add(`c|+Anubis|worthless evildoer :(`); + this.add(`c|${getName('Annika')}|oh, I crashed the server again...`); }, }, aquagtothepast: { noCopy: true, onStart() { - this.add(`c|+A Quag to The Past|The Goons are all here`); + this.add(`c|${getName('A Quag To The Past')}|Whatever happens, happens.`); }, onSwitchOut() { - this.add(`c|+A Quag to The Past|um no`); + this.add(`c|${getName('A Quag To The Past')}|See you space cowboy...`); }, onFaint() { - this.add(`c|+A Quag to The Past|Wait, this isn't the groupchat...`); + this.add(`c|${getName('A Quag To The Past')}|You're gonna carry that weight.`); + }, + }, + arby: { + noCopy: true, + onStart() { + this.add(`c|${getName('Arby')}|Time to win this :)`); + }, + onSwitchOut() { + this.add(`c|${getName('Arby')}|MSU need a sub`); + }, + onFaint() { + this.add(`c|${getName('Arby')}|Authhate is real.`); + }, + }, + archas: { + noCopy: true, + onStart() { + this.add(`c|${getName('Archas')}|Ready the main batteries, gentlemen! Hit ‘em hard and fast!`); + }, + onSwitchOut() { + this.add(`c|${getName('Archas')}|Helmsman, full reverse at speed!`); + }, + onFaint() { + this.add(`c|${getName('Archas')}|They say the captain always goes down with the ship...`); }, }, arcticblast: { noCopy: true, onStart() { - this.add(`c|%Arcticblast|My trash friend can do no wrong`); - }, - onModifyMove(move) { - if (move.id === 'knockoff') { - move.onAfterHit = function (target, source) { - if (source.hp) { - const item = target.takeItem(); - if (item) { - this.add('-enditem', target, item.name, '[from] move: Knock Off', '[of] ' + source); - this.add(`c|%Arcticblast|+20 ;)`); - } - } - }; - } - }, - onFaint(pokemon) { - const activeMon = pokemon.side.foe.active[0].species.id; - if (activeMon === 'greninja') { - this.add(`c|%Arcticblast|FRIENDS DON’T const FRIENDS PLAY FROGS`); - } else if (activeMon === 'pumpkaboosuper') { - this.add(`c|%Arcticblast|WHY IS MY KAREN PRIZED`); - } else { - this.add(`c|%Arcticblast|FREE SKYMIN`); - } - }, - onSourceFaint(target) { - if (target.species.id === 'greninja') { - this.add(`c|%Arcticblast|FRIENDS DON’T const FRIENDS PLAY FROGS`); - } - }, - }, - arsenal: { - noCopy: true, - onStart() { - this.add(`c|+Arsenal|Wenger In`); + this.add(`c|${getName('Arcticblast')}|words are difficult`); }, onSwitchOut() { - this.add(`c|+Arsenal|Time to watch anime`); + this.add(`c|${getName('Arcticblast')}|oh no`); }, onFaint() { - this.add(`c|+Arsenal|Wenger Out`); + if (this.randomChance(1, 100)) { + this.add(`c|${getName('Arcticblast')}|get **mished** kid`); + } else { + this.add(`c|${getName('Arcticblast')}|single battles are bad anyway, why am I here?`); + } }, }, averardo: { noCopy: true, onStart() { - this.add(`c|+Averardo|ECCOMI`); + this.add(`c|${getName('Averardo')}|o bella`); }, onSwitchOut() { - this.add(`c|+Averardo|Scillato (PA)`); + this.add(`c|${getName('Averardo')}|Condivido schermo cosi' guardiamo i tre porcellini?`); }, onFaint() { - this.add(`c|+Averardo|Non mi sporcare più i MP`); + this.add(`c|${getName('Averardo')}|BE... Ok mejo chiudere gioco... vedo documentario su Bibbia`); + }, + }, + awauser: { + noCopy: true, + onStart() { + this.add(`c|${getName('awa!')}|awa!`); + }, + onSwitchOut() { + this.add(`c|${getName('awa!')}|well, at least i didn't lose the game`); + this.add(`c|${getName('awa!')}|or did i?`); + }, + onFaint() { + this.add(`c|${getName('awa!')}|awawa?! awa awawawa awawa >:(`); }, }, beowulf: { noCopy: true, onStart() { - this.add(`c|%Beowulf|/me BUZZES LOUDLY`); + this.add(`c|${getName('Beowulf')}|:^)`); }, onSwitchOut() { - this.add(`c|%Beowulf|/me BUZZES LOUDLY`); + this.add(`c|${getName('Beowulf')}|/me buzzes`); }, onFaint() { - this.add(`c|%Beowulf|BUZZ BUZZ BUZZ BUZZ`); + this.add(`c|${getName('Beowulf')}|time for my own isekai`); + }, + onSourceFaint() { + this.add(`c|${getName('Beowulf')}|another one reincarnating into an isekai`); }, }, biggie: { noCopy: true, onStart() { - this.add(`c|@biggie|Gamin' on ya`); + this.add(`c|${getName('biggie')}|gonna take you for a ride`); }, onSwitchOut() { - this.add(`c|@biggie|Mission complete!`); + this.add(`c|${getName('biggie')}|mahvel baybee!`); }, onFaint() { - this.add(`c|@biggie|It was all a dream`); + this.add(`c|${getName('biggie')}|it was all a dream`); }, }, - birdy: { + billo: { noCopy: true, - onStart() { - this.add(`c|+Birdy~!|And I oop- uh, I mean, hi!`); + onStart(source) { + let activeMon = source.side.foe.active[0].species.name; + if (!activeMon) activeMon = "Pokemon"; + this.add(`c|${getName('Billo')}|Your ${activeMon} looks hacked.`); }, onSwitchOut() { - this.add(`c|+Birdy~!|Be like that, then. Don't blame ME if you lose.`); + this.add(`c|${getName('Billo')}|Let me inspect your Pokemon, brb`); }, onFaint() { - this.add(`c|+Birdy~!|My last words will always be that I don't spam.`); + this.add(`c|${getName('Billo')}|Yep, definitely hacked.`); }, - // Feather Tuft Innate - onModifyDefPriority: 6, - onModifyDef(def, pokemon) { - if (!pokemon.transformed && !pokemon.illusion) { - return this.chainModify(1.5); + innateName: "Unaware", + shortDesc: "This Pokemon ignores other Pokemon's stat stages when taking or doing damage.", + // Unaware innate + onAnyModifyBoost(boosts, pokemon) { + const unawareUser = this.effectData.target; + if (unawareUser.illusion) return; + if (unawareUser === pokemon) return; + if (unawareUser === this.activePokemon && pokemon === this.activeTarget) { + boosts['def'] = 0; + boosts['spd'] = 0; + boosts['evasion'] = 0; } - }, - onModifySpDPriority: 6, - onModifySpD(spd, pokemon) { - if (!pokemon.transformed && !pokemon.illusion) { - return this.chainModify(1.5); + if (pokemon === this.activePokemon && unawareUser === this.activeTarget) { + boosts['atk'] = 0; + boosts['def'] = 0; + boosts['spa'] = 0; + boosts['accuracy'] = 0; } }, }, - bobochan: { + blaz: { noCopy: true, onStart() { - this.add(`c|@bobochan|Pika Pika! Just kidding, it's just a knock off clone.`); + this.add(`c|${getName('Blaz')}|Give me, give me, give me the truth now oh oh oh oh`); }, onSwitchOut() { - this.add(`c|@bobochan|Time to skedaddle!`); + this.add(`c|${getName('Blaz')}|Tell me... why? Please tell me why do we worry? Why? Why do we worry at all?`); }, onFaint() { - this.add(`c|@bobochan|You cruel trainer for killing an innocent pokemon!`); + this.add(`c|${getName('Blaz')}|the game (lol u lost)`); }, }, brandon: { noCopy: true, onStart() { - this.add(`c|%Brandon|Let's put the petal to the medal!`); + this.add(`c|${getName('Brandon')}|I didn't come here to play. I came here to slay!`); }, onSwitchOut() { - this.add(`c|%Brandon|I gotta charge my phone brb`); + this.add(`c|${getName('Brandon')}|${[`I need to catch my breath`, `brb getting a snack`][this.random(2)]}`); + }, + onFaint(pokemon) { + const foeName = pokemon.side.foe.active[0].illusion ? + pokemon.side.foe.active[0].illusion.name : pokemon.side.foe.active[0].name; + this.add(`c|${getName('Brandon')}|${[`This battle was rigga morris!`, `At least I'll snag Miss Congeniality...`, `This battle was rigged for ${foeName} anyway >:(`][this.random(3)]}`); + }, + }, + brouha: { + noCopy: true, + onStart() { + this.add(`c|${getName('brouha')}|lmf`); + }, + onSwitchOut() { + this.add(`c|${getName('brouha')}|....`); }, onFaint() { - this.add(`c|%Brandon|I need a nap`); + this.add(`c|${getName('brouha')}|sobL`); }, }, cake: { noCopy: true, - onStart(source) { - this.add(`c|+Cake|h`); - if (source.illusion) return; - this.add('-start', source, 'typeadd', 'Ghost'); + innateName: "h", + shortDesc: "On switch-in and at the end of every turn, this Pokemon changes type randomly.", + onStart(target, pokemon) { + this.add(`c|${getName('Cake')}|AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA`); + // h innate + if (pokemon.illusion) return; + const typeList = Object.keys(this.dex.data.TypeChart); + this.prng.shuffle(typeList); + const firstType = typeList[0]; + this.prng.shuffle(typeList); + const secondType = typeList[0]; + const newTypes = [firstType]; + if (firstType !== secondType) newTypes.push(secondType); + this.add('html|h'); + this.add('-start', pokemon, 'typechange', newTypes.join('/'), '[silent]'); + pokemon.setType(newTypes); }, - onSwitchOut(source) { - this.add(`c|+Cake|${source.side.name} is a nerd`); + onSwitchOut(pokemon) { + this.add(`c|${getName('Cake')}|${pokemon.side.name} is a nerd`); }, onFaint() { - this.add(`c|+Cake|According to all known laws of aviation, there is no way that Dunsparce should be able to fly. Its wings are too small to get its fat little body off the ground. Dunsparce, of course, does not learn Fly for this reason. It does learn Roost, though. Cute li'l winged snake thing.`); + this.add(`c|${getName('Cake')}|Chowder was a good show`); }, - // Fat Snake Innate - onModifyDefPriority: 6, - onModifyDef(def, pokemon) { - if (!pokemon.transformed && !pokemon.illusion) { - return this.chainModify(1.5); - } - }, - onModifySpDPriority: 6, - onModifySpD(spd, pokemon) { - if (!pokemon.transformed && !pokemon.illusion) { - return this.chainModify(1.5); + onResidualOrder: 26, + onResidualSubOrder: 1, + onResidual(pokemon) { + if (pokemon.illusion) return; + if (pokemon.activeTurns) { + const typeList = Object.keys(this.dex.data.TypeChart); + this.prng.shuffle(typeList); + const firstType = typeList[0]; + this.prng.shuffle(typeList); + const secondType = typeList[0]; + const newTypes = [firstType]; + if (firstType !== secondType) newTypes.push(secondType); + this.add('html|h'); + this.add('-start', pokemon, 'typechange', newTypes.join('/'), '[silent]'); + pokemon.setType(newTypes); } }, }, cantsay: { noCopy: true, onStart() { - this.add(`c|@cant say|(´・ω・\`)`); + this.add(`c|${getName('cant say')}|haha volc go brrrr`); }, onSwitchOut() { - this.add(`c|@cant say|wow CTed lol`); + this.add(`c|${getName('cant say')}|lol CTed`); }, onFaint() { - this.add(`c|@cant say|bg haxor :(`); + this.add(`c|${getName('cant say')}|${['imagine taking pokemon seriously when you can just get haxed', '/me plays curb your enthusiasm theme', 'bad players always get lucky'][this.random(3)]}`); }, - }, - catalystic: { - noCopy: true, - onStart() { - this.add(`c|+Catalystic|The Birb is here!`); - }, - onSwitchOut() { - this.add(`c|+Catalystic|The Birb will be back!`); - }, - onFaint() { - this.add(`c|+Catalystic|The Birb is never lucky.`); + innateName: "Magic Guard", + shortDesc: "This Pokemon can only be damaged by direct attacks.", + // Magic Guard Innate + onDamage(damage, target, source, effect) { + if (target.illusion) return; + if (effect.effectType !== 'Move') { + if (effect.effectType === 'Ability') this.add('-activate', source, 'ability: ' + effect.name); + return false; + } }, }, celestial: { + noCopy: true, + // No quotes requested + }, + celine: { noCopy: true, onStart() { - this.add(`c|%Celestial|**Time to electrify you**`); + this.add(`c|${getName('Celine')}|Support has arrived!`); }, onSwitchOut() { - this.add(`c|%Celestial|__I'll be coming back for you!__`); + this.add(`c|${getName('Celine')}|Brb writing`); }, onFaint() { - this.add(`c|%Celestial|I'm shocked that you mere mortal actually defeated __me__`); - }, - }, - ceteris: { - noCopy: true, - onStart() { - this.add(`c|+Ceteris|Your loss is inevitable, you best forfeit the battle if you know what's good for you. ╰(⇀︿⇀)つ-]═───`); - }, - onSwitchOut(source) { - this.add(`c|+Ceteris|Saving the best for last, ${source.side.name}, a wise choice my friend.`); - }, - onFaint() { - this.add(`c|+Ceteris|IMPOSSIBLE!! THIS IS AN OUTRAGE!! I WILL EXACT MY REVENGE ON YOU ONE DAY (◣_◢)`); - }, - }, - chaos: { - noCopy: true, - // No phrases provided - }, - chloe: { - noCopy: true, - onStart() { - this.add(`c|@Chloe|hlelo`); - }, - onSwitchOut() { - this.add(`c|@Chloe|bubye`); - }, - onFaint() { - this.add(`c|@Chloe|aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`); + this.add(`c|${getName('Celine')}|'Tis only a flesh wound!`); }, }, ckilgannon: { noCopy: true, onStart() { - this.add(`c|%c.kilgannon|Hope you're not afraid of the dark!`); + this.add(`c|${getName('c.kilgannon')}|Take a look to the sky just before you die`); }, onSwitchOut() { - this.add(`c|%c.kilgannon|/me growls`); + this.add(`c|${getName('c.kilgannon')}|Death does wait; there's no debate.`); }, onFaint() { - this.add(`c|%c.kilgannon|Your time will come.`); + this.add(`c|${getName('c.kilgannon')}|Memento mori.`); }, }, - darth: { + coconut: { noCopy: true, - onStart() { - this.add(`c|+Darth|The angel of stall descends upon the battlefield.`); - }, - onSwitchOut() { - this.add(`c|+Darth|The angel of stall has decided upon a tactical retreat.`); - }, - onFaint() { - this.add(`c|+Darth|Regenerator is a perfectly balanced ability.`); - }, + // no quotes }, - dawoblefet: { + dogknees: { noCopy: true, - onStart() { - this.add(`c|&DaWoblefet|What's going on guys? This is DaWoblefet, and welcome to Mechanics Monday.`); + onStart(source) { + this.add(`c|${getName('dogknees')}|Your opinion is wrong if you think cats are better than dogs ૮・ﻌ・ა`); + if (source.illusion) return; + this.add('-start', source, 'typechange', source.types.join('/'), '[silent]'); }, onSwitchOut() { - this.add(`c|&DaWoblefet|Until next time, have a good one.`); + this.add(`c|${getName('dogknees')}|Yes, dogs do have knees. Stop asking me.`); }, onFaint() { - this.add(`c|&DaWoblefet|mished`); - }, - }, - decem: { - noCopy: true, - onStart() { - this.add(`c|%Decem|:D`); - }, - onSwitchOut() { - this.add(`c|%Decem|bye`); - }, - onFaint() { - this.add(`c|%Decem|>:(`); - }, - }, - deetah: { - noCopy: true, - onStart() { - this.add(`c|+deetah|I can see right through you.`); - }, - onSwitchOut() { - this.add(`c|+deetah|Meow :3`); - }, - onFaint() { - this.add(`c|+deetah|I have 8 lives left, you can't get rid of me that easily.`); + this.add(`c|${getName('dogknees')}|Nap time!`); }, }, dragonwhale: { noCopy: true, - onStart() { - this.add(`c|+DragonWhale|i would switch to chomper here`); - }, + // No quotes }, - e4flint: { - // Fire type when mega evolving implemented in scripts.js - noCopy: true, - onStart(target, source) { - this.add(`c|@E4 Flint|How many Fire-Types do I have now`); - if (source.species.id !== 'steelixmega' || source.illusion) return; - this.add('-start', source, 'typeadd', 'Fire'); - }, - onFaint() { - this.add(`c|@E4 Flint|lul ok`); - }, - }, - eien: { + drampasgrandpa: { noCopy: true, onStart() { - this.add(`c|+Eien|umu!`); + this.add(`c|${getName('drampa\'s grandpa')}|Where are my glasses?`); + }, + onSwitchOut() { + this.add(`c|${getName('drampa\'s grandpa')}|Darn kids...`); }, onFaint() { - this.add(`c|+Eien|This game is Bad Civilization...`); + this.add(`c|${getName('drampa\'s grandpa')}|Bah humbug!`); + }, + }, + dream: { + noCopy: true, + onStart() { + this.add(`c|${getName('dream')}|It's Prime Time`); + }, + onSwitchOut() { + this.add(`c|${getName('dream')}|oh no please god tell me we're dreaming`); + }, + onFaint() { + this.add(`c|${getName('dream')}|perdemos`); }, }, elgino: { noCopy: true, - onStart() { - this.add(`c|+Elgino|I'm about to solve this puzzle.`); + onStart(target, pokemon) { + this.add(`c|${getName('Elgino')}|Time to save Hyrule!`); + if (pokemon.illusion) return; + this.add('-start', pokemon, 'typechange', pokemon.types.join('/'), '[silent]'); }, onSwitchOut() { - this.add(`c|+Elgino|uh I need some hints for this one, I'll try again later`); + this.add(`c|${getName('Elgino')}|Hold on I need to stock up on ${this.sample(['Bombs', 'Arrows', 'Magic', 'Seeds'])}`); }, onFaint() { - this.add(`c|+Elgino|Frankly, I'm... ashamed.`); - }, - onDamagePriority: 1, - onDamage(damage, target, source, effect) { - if (effect?.effectType === 'Move' && ['mimikyu', 'mimikyutotem'].includes(target.species.id) && !target.transformed) { - this.add('-activate', target, 'ability: Disguise'); - this.effectData.busted = true; - return 0; - } - }, - onEffectiveness(typeMod, target, type, move) { - if (!target) return; - if ( - !['mimikyu', 'mimikyutotem'].includes(target.species.id) || target.transformed || - (target.volatiles['substitute'] && !(move.flags['authentic'] || move.infiltrates)) - ) { - return; - } - if (!target.runImmunity(move.type)) return; - return 0; - }, - onUpdate(pokemon) { - if (['mimikyu', 'mimikyutotem'].includes(pokemon.species.id) && this.effectData.busted) { - const speciesid = pokemon.species.id === 'mimikyutotem' ? 'Mimikyu-Busted-Totem' : 'Mimikyu-Busted'; - pokemon.formeChange(speciesid, this.effect, true); - } + this.add(`c|${getName('Elgino')}|I'm out of fairies D:!`); }, }, - eternally: { + emeri: { noCopy: true, onStart() { - this.add(`c|@eternally|quack`); + this.add(`c|${getName('Emeri')}|hey !`); + }, + onSwitchOut() { + this.add(`c|${getName('Emeri')}|//busy`); }, onFaint() { - this.add(`c|@eternally|quack`); + this.add(`c|${getName('Emeri')}|don't forget to chall SFG or Agarica in gen8ou`); }, - onTryHit(target, source, move) { - if (target.illusion) return; - if (target !== source && move.type === 'Water') { - if (!this.heal(target.baseMaxhp / 4)) { - this.add('-immune', target, '[from] ability: Water Absorb'); - } - return null; - } + }, + epicnikolai: { + noCopy: true, + onStart(source) { + this.add(`c|${getName('EpicNikolai')}|I never give up until I get something right, which means destroying you ☜(゚ヮ゚☜)`); + if (source.species.id !== 'garchompmega' || source.illusion) return; + this.add('-start', source, 'typechange', source.types.join('/'), '[silent]'); + }, + onSwitchOut() { + this.add(`c|${getName('EpicNikolai')}|This wasn't as fun as I thought it would be, I'm out ¯_( ͡~ ͜ʖ ͡°)_/¯`); // eslint-disable-line no-irregular-whitespace + }, + onFaint() { + this.add(`c|${getName('EpicNikolai')}|I like to keep a positive attitude even though it is hard sometimes <('o'<)~*/`); + }, + }, + estarossa: { + noCopy: true, + onStart() { + this.add(`c|${getName('estarossa')}|honestly best pairing for hazard coverage wtih molt is like molt + tsareena/dhelmise`); + }, + onSwitchOut() { + this.add(`c|${getName('estarossa')}|sand balance <333`); + }, + onFaint() { + this.add(`c|${getName('estarossa')}|*eurgh*`); }, }, explodingdaisies: { noCopy: true, onStart() { - this.add(`c|%explodingdaisies|For today's weather: DOOOOOM!`); + this.add(`c|${getName('explodingdaisies')}|Turn and run now, and I will mercifully pretend this never happened.`); }, onSwitchOut() { - this.add(`c|%explodingdaisies|I WILL DESTROY YOU.......BUT LATER!`); + this.add(`c|${getName('explodingdaisies')}|You are beneath me, and it shows.`); }, onFaint() { - this.add(`c|%explodingdaisies|MY PEOPLE NEED ME!`); - }, - }, - false: { - noCopy: true, - onStart() { - this.add(`c|+false|٩(•̤̀ᵕ•̤́๑)ᵒᵏᵎᵎᵎᵎ`); - }, - onSwitchOut() { - this.add(`c|+false|٩(๑•◡-๑)۶ⒽⓤⒼ❤`); - }, - onFaint() { - this.add(`c|+false|ɢ∞פ⋆ᖙᵒᵝ ٩꒰”̮*ू꒱`); + this.add(`c|${getName('explodingdaisies')}|Unacceptable!`); }, }, fart: { noCopy: true, - onStart(target, source) { - this.add('-start', source, 'typechange', `Fairy/Steel`); - const activeMon = this.toID( - source.side.foe.active[0].illusion ? source.side.foe.active[0].illusion.name : source.side.foe.active[0].name - ); - const family = [ - 'aethernum', 'ceteris', 'ransei', 'trickster', 'gimm1ck', 'zalm', 'aelita', 'biggie', 'deetah', 'birdy', 'sundar', 'celestial', - ]; - if (activeMon === 'hoeenhero' || activeMon === 'instruct') { - this.add(`c|%fart|what song should I sing?`); - } else if (activeMon === 'lifeisdank' || activeMon === 'nui' || activeMon === 'grimauxiliatrix') { - this.add(`c|%fart|the gang's all here!`); - } else if (family.includes(activeMon)) { - this.add(`c|%fart|what's cookin', good lookin'?`); + onStart(source) { + let activeMon; + activeMon = source.side.foe.active[0]; + activeMon = activeMon.illusion ? activeMon.illusion.name : activeMon.name; + const family = ['aethernum', 'trickster', 'celestial', 'gimmick', 'zalm', 'aelita', 'biggie']; + if (this.toID(activeMon) === 'hoeenhero') { + this.add(`c|${getName('fart')}|🎵 it's friday, friday, gotta get down on friday 🎵`); + } else if (this.toID(activeMon) === 'grimauxiliatrix') { + this.add(`c|${getName('fart')}|howdy ho, neighbor`); + } else if (this.toID(activeMon) === 'fart') { + this.add(`c|${getName('fart')}|How Can Mirrors Be Real If Our Eyes Aren't Real`); + } else if (family.includes(this.toID(activeMon))) { + this.add(`c|${getName('fart')}|hey, hey, hey. ${activeMon} is OK`); } else { - this.add(`c|%fart|it's fukken raw`); + this.add(`c|${getName('fart')}|rats, rats, we are the rats`); } }, onSwitchOut() { - this.add(`c|%fart|this boy is not correct. he is **flawed.**`); + this.add(`c|${getName('fart')}|if I can't win this game, then I'll make it boring for everyone.`); }, onFaint(pokemon) { - const activeMon = this.toID( - pokemon.side.foe.active[0].illusion ? pokemon.side.foe.active[0].illusion.name : pokemon.side.foe.active[0].name - ); - if (activeMon === 'felucia') { - this.add(`c|%fart|Felucia I'm deleting your mon`); + let activeMon; + activeMon = pokemon.side.foe.active[0]; + activeMon = this.toID(activeMon.illusion ? activeMon.illusion.name : activeMon.name); + const family = ['aethernum', 'trickster', 'celestial', 'gimmick', 'zalm', 'aelita', 'biggie']; + if (family.includes(activeMon)) { + this.add(`c|${getName('fart')}|at least I wasn't boring, right?`); } else { - this.add(`c|%fart|the things I do for love...`); + this.add(`c|${getName('fart')}|oy, I die`); } }, }, felucia: { noCopy: true, - onStart() { - this.add(`c|%Felucia|Hi I'm here to participate in a totally serious conversation`); + onStart(source) { + this.add(`c|${getName('Felucia')}|battlesignup! I dropped my dice somewhere and now all I can do is make you play with them (join using %join one)`); + if (source.illusion) return; + this.add('-start', source, 'typechange', source.types.join('/'), '[silent]'); }, onSwitchOut() { - this.add(`c|%Felucia|Okay that's enough shitposting for now`); + this.add(`c|${getName('Felucia')}|battlesignup: I lost connection to a player so I guess I'll get a new one (/me in to sub)`); }, onFaint() { - this.add(`c|%Felucia|Fine I'll go back to work...`); + this.add(`c|${getName('Felucia')}|%remp Felucia`); }, }, - fomg: { + finland: { noCopy: true, - onStart() { - this.add(`c|@FOMG|You got this, my friend!`); + onStart(source) { + const roll = this.random(100); + let message: string; + if (roll < 70) { + message = 'pog'; + } else if (roll < 80) { + message = 'very pog'; + } else if (roll < 90) { + message = 'poggaroo'; + } else if (roll < 95) { + message = 'PogU'; + } else { + message = 'poog'; + } + this.add(`c|${getName('Finland')}|${message}`); + if (source.illusion) return; + this.boost({spa: 1, spd: 1}, source); }, + onBeforeMovePriority: 0.5, + onBeforeMove(attacker, defender, move) { + if (attacker.illusion) return; + attacker.clearBoosts(); + this.add('-clearboost', attacker); + if (move.category === 'Status') { + this.boost({def: 1, spd: 1}, attacker); + } else { + this.boost({spa: 1, spe: 1}, attacker); + } + }, + innateName: "Fickle Decorator", + shortDesc: "Calm Mind on switch-in. Changes boosts depending on move used.", onSwitchOut() { - this.add(`c|@FOMG|/me rolls out`); + this.add(`c|${getName('Finland')}|i hope running away is safe on shield?`); }, onFaint() { - this.add(`c|@FOMG|Rock in peace...`); + if (this.randomChance(99, 100)) { + this.add(`c|${getName('Finland')}|FINLAND!!!`); + } else { + // personally i like young link from oot3d and mm3d - sp + this.add(`c|${getName('Finland')}|i hate young link. i hate you i hate you i hate you. i hate you. young link i hate you. i despise you. i loathe you. your existence is an affront to my person. to my own existence. it's an offense. a despicable crime. a wretched abomination. even worse than mega man. a cruel barbarity. an awful curse from capricious, pernicious fate. oh do i hate young link. i scorn you. i cast you away to ignominy and hatred even worse than mega man. you are shameful young link, and you should never show your face again`); + } + }, + }, + frostyicelad: { + noCopy: true, + onStart() { + this.add(`c|${getName('frostyicelad ❆')}|Oh i guess its my turn now! Time to sweep!`); + }, + onSwitchOut(source) { + this.add(`c|${getName('frostyicelad ❆')}|Hey! ${source.side.name} why dont you keep me in and let me sweep? Mean.`); + }, + onFaint() { + this.add(`c|${getName('frostyicelad ❆')}|So c-c-cold`); }, }, gallantspear: { noCopy: true, onStart() { - this.add(`c|+Gallant Spear|*Trombe! override intensifies*`); + this.add(`c|${getName('gallant\'s pear')}|**Rejoice! The one to inherit all Rider powers, the time king who will rule over the past and the future.**`); }, onSwitchOut() { - this.add(`c|+Gallant Spear|*neigh* *snort*`); + this.add(`c|${getName('gallant\'s pear')}|My Overlord..`); }, onFaint() { - this.add(`c|+Gallant Spear|UNIVAAAAAAAAAAAAAAASE!!`); + this.add(`c|${getName('gallant\'s pear')}|Damn you, Decade!!!`); }, }, - gimm1ck: { + gimmick: { noCopy: true, onStart() { - this.add(`c|%Gimm1ck|Давай!`); + this.add(`c|${getName('Gimmick')}|Mama, they say I'm a TRRST`); }, onSwitchOut() { - this.add(`c|%Gimm1ck|Must get more semechki`); + this.add(`c|${getName('Gimmick')}|Ic3peak to you later`); }, onFaint() { - this.add(`c|%Gimm1ck|Ran out of vodka`); + this.add(`c|${getName('Gimmick')}|I did nothing wrong (but I got on the blacklist)`); }, }, gmars: { noCopy: true, - onStart(target) { - this.add(`c|&GMars|It's ya boy GEEEEEEEEMARS`); - if (target.illusion || !target.m.miniorColor) return; - this.add('-formechange', target, `Minior${target.m.miniorColor}`); + onStart() { + this.add(`c|${getName('GMars')}|It's ya boy GEEEEEEEEMARS`); }, onSwitchOut() { - this.add(`c|&GMars|I like 'em crisp`); + this.add(`c|${getName('GMars')}|Who switches out a Minior in prime position?`); }, onFaint() { - this.add(`c|&GMars|Don't forget to check out my bandcamp`); + this.add(`c|${getName('GMars')}|Follow me on bandcamp`); }, }, grimauxiliatrix: { noCopy: true, onStart() { - this.add(`c|@grimAuxiliatrix|${['A BRAWL IS SURELY BREWING!', 'GOOD DAY FOR A SWELL BATTLE!', 'THIS MATCH WILL GET RED HOT!'][this.random(3)]}`); + this.add(`c|${getName('grimAuxiliatrix')}|${['THE JUICE IS LOOSE', 'TOOTHPASTE\'S OUT OF THE TUBE', 'PREPARE TO DISCORPORATE'][this.random(3)]}`); }, onFaint() { - this.add(`c|@grimAuxiliatrix|**KNOCKOUT!**`); - }, - }, - guishark: { - noCopy: true, - onStart() { - this.add(`c|+guishark|Let's go hunt doodoodoodoodoodoo!`); - }, - onSwitchOut() { - this.add(`c|+guishark|Run away doodoodoodoodoodoo!`); - }, - onFaint() { - this.add(`c|+guishark|It's the end doodoodoodoodoodoo!`); - }, - }, - // Cant use the exact name because its a pokemon's name - hippopotasuser: { - noCopy: true, - onStart() { - this.add(`c|+Hippopotas|Something broke. If you're seeing this message, please PM a staff member about it.`); - this.add(`c|&HoeenHero|No, its not a bug Hippopotas, stop telling people to PM staff, its annoying.`); - }, - onSwitchOut() { - this.add(`c|+Hippopotas|Something broke. If you're seeing this message, please PM a staff member about it.`); - this.add(`c|&HoeenHero|There's still no bug!`); - }, - onFaint() { - this.add(`c|+Hippopotas|Something broke. If you're seeing this message, please PM a staff member about it.`); - this.add(`c|&HoeenHero|My PMs are flooded with bug reports :(`); + this.add(`c|${getName('grimAuxiliatrix')}|${['NOT LIKE THIS', 'HALT - MODULE CORE HEMORRHAGE', 'AAAAAAAAAAAAAAAAAAA'][this.random(3)]}`); }, }, hoeenhero: { noCopy: true, onStart() { - this.add(`c|&HoeenHero|My scripts will lead me to victory!`); + this.add(`c|${getName('HoeenHero')}|A storm is brewing...`); }, onSwitchOut() { - this.add(`c|&HoeenHero|I need to look something up, hold on...`); + this.add(`c|${getName('HoeenHero')}|The eye of the hurricane provides a brief respite from the storm.`); }, onFaint() { - this.add(`c|&HoeenHero|There must have been a bug in my script ;-;`); + this.add(`c|${getName('HoeenHero')}|All storms eventually disipate.`); }, }, hubriz: { noCopy: true, onStart() { - this.add(`c|%Hubriz|Just a harmless flower...`); + this.add(`c|${getName('Hubriz')}|Free hugs!`); }, onSwitchOut() { - this.add(`c|%Hubriz|I'll bloom once more soon enough!`); + this.add(`c|${getName('Hubriz')}|The soil's pH level is too high. I'm out!`); }, onFaint() { - this.add(`c|%Hubriz|I burn, I pine, I perish.`); + this.add(`c|${getName('Hubriz')}|Delicate Flower Quest failed...`); + }, + }, + hydro: { + noCopy: true, + onStart(pokemon) { + this.add(`c|${getName('Hydro')}|Person reading this is a qt nerd and there is absolutely NOTHING u can do about it :)`); + if (pokemon.illusion) return; + this.add('-start', pokemon, 'typechange', pokemon.types.join('/'), '[silent]'); + }, + onSwitchOut() { + this.add(`c|${getName('Hydro')}|brb, taking a break from ur nerdiness`); + }, + onFaint() { + this.add(`c|${getName('Hydro')}|RUUUUUDEEE`); }, }, inactive: { noCopy: true, onStart() { - this.add(`c|+inactive|I just can't wait to face you.`); + this.add(`c|${getName('Inactive')}|Are you my nightmare? Or am I yours?`); }, onSwitchOut() { - this.add(`c|+inactive|I'll keep an eye out for you next time...`); + this.add(`c|${getName('Inactive')}|This is not the end...`); }, onFaint() { - this.add(`c|+inactive|/me turns to stone and crumbles`); + this.add(`c|${getName('Inactive')}|/me turns to stone and crumbles`); }, }, - instruct: { + instructuser: { noCopy: true, onStart() { - this.add(`c|+Instruct|I am here to bully Aroma Electra`); + this.add(`c|${getName('INStruct')}|lets drink to a great time!`); }, onSwitchOut() { - this.add(`c|+Instruct|Remember these two things: Lucario•1582 will always be cute, and Swagn will never be able to dab`); + this.add(`c|${getName('Swagn')}|Hey, Instruct. Here's those 15,000 walls of text you ordered. :3`); + this.add(`c|${getName('INStruct')}|ya know, why __do__ you always flood my dms?`); + this.add(`c|${getName('INStruct')}|whatever im just gonna go get some more coke`); }, onFaint() { - this.add(`c|+Instruct|Noctrine help this isn't working as well as we planned`); + this.add(`c|${getName('INStruct')}|wait did we run out of coca-cola?`); + this.add(`c|${getName('INStruct')}|laaaaaaaaaaame`); + this.add(`c|${getName('INStruct')}|yall suck im going home`); }, - }, - irritated: { - noCopy: true, - onStart() { - this.add(`c|+irritated|Glhf`); + innateName: "Last Laugh", + desc: "Upon fainting, this Pokemon deals damage to all Pokemon that have made contact with it equal to 50% of their max HP. This damage cannot KO Pokemon.", + shortDesc: "Upon fainting, deal 50% of their max HP to all foes that this Pokemon contacted.", + // Extinction Level Event Innate + onSourceHit(target, source, move) { + if (source.illusion) return; + if (!move || !target) return; + if (target !== source && move.category !== 'Status') { + if (move.flags['contact']) { + if (!target.m.marked) this.add('-message', `${target.name} was marked by an unknown being...`); + target.m.marked = true; + } + } }, - onFaint() { - this.add(`c|+irritated|wtf hax`); + onDamagingHit(damage, target, source, move) { + if (target.illusion) return; + if (move.flags['contact']) { + if (!source.m.marked) this.add('-message', `${source.name} was marked by an unknown being...`); + source.m.marked = true; + } + if (!target.hp) { + for (const foe of source.side.pokemon) { + if (foe.fainted || !foe.hp) continue; + if (!foe.m.marked) continue; + this.add('-activate', target, 'ability: Last Laugh'); + let collateral = this.clampIntRange(foe.baseMaxhp / 2, 1); + this.add('-message', `${foe.name} became insane and attacked themselves!`); + if (collateral >= foe.hp) collateral = foe.hp - 1; + this.directDamage(collateral, foe); + } + } }, }, iyarito: { noCopy: true, onStart() { - this.add(`c|&Iyarito|Iyarito is always right`); + this.add(`c|${getName('Iyarito')}|Madre de Dios, ¡es el Pollo Diablo!`); }, onSwitchOut() { - this.add(`c|&Iyarito|It's all Iyarito's fault`); + this.add(`c|${getName('Iyarito')}|Well, you're not taking me without a fight!`); }, onFaint() { - this.add(`c|&Iyarito|RIP Patrona`); + this.add(`c|${getName('Iyarito')}|RIP Patrona`); + }, + }, + jett: { + noCopy: true, + onStart() { + this.add(`c|${getName('Jett')}|It's a good day for a hunt.`); + }, + onSwitchOut() { + this.add(`c|${getName('Jett')}|I'll be back for more.`); + }, + onFaint() { + this.add(`c|${getName('Jett')}|They got lucky.`); + }, + }, + jho: { + noCopy: true, + onStart() { + this.add(`c|${getName('Jho')}|Hey there party people`); + }, + onSwitchOut() { + this.add(`c|${getName('Jho')}|The Terminator(1984), 00:57:10`); + }, + onFaint() { + this.add(`c|${getName('Jho')}|Unfortunately, CAP no longer accepts custom elements`); + }, + }, + jordy: { + noCopy: true, + onStart() { + this.add(`c|${getName('Jordy')}|I heard there's a badge here. Please give it to me immediately.`); + }, + onSwitchOut() { + this.add(`c|${getName('Jordy')}|Au Revoir. Was that right?`); + }, + onFaint() { + this.add(`c|${getName('Jordy')}|hjb`); }, }, kaijubunny: { noCopy: true, - onStart() { - this.add(`c|@Kaiju Bunny| ̄( ÒㅅÓ) ̄ Thump Thump Motherfucker`); + onStart(source) { + this.add(`c|${getName('Kaiju Bunny')}|I heard SOMEONE wasn't getting enough affection!  ̄( ÒㅅÓ) ̄`); + if (source.species.id !== 'lopunnymega' || source.illusion) return; + this.add('-start', source, 'typechange', source.types.join('/'), '[silent]'); }, onSwitchOut() { - this.add(`c|@Kaiju Bunny| ̄( >ㅅ>) ̄ Holding me back, I see how it is`); + this.add(`c|${getName('Kaiju Bunny')}|Brb, need more coffee  ̄( =ㅅ=) ̄`); }, onFaint() { - this.add(`c|@Kaiju Bunny| ̄( ‘xㅅx) ̄Time to take a 10 hour power nap`); - }, - // Kaiju Rage Innate - // onUpdate so toxic orb can activate after. Code mainly copied from Power Construct. - onUpdate(pokemon) { - if (pokemon.species.id !== 'gligar' || pokemon.transformed || pokemon.illusion || !pokemon.hp) return; - if (pokemon.hp > pokemon.maxhp / 2) return; - this.add('-activate', pokemon, 'ability: Kaiju Rage'); - pokemon.formeChange('Gliscor', this.effect, true); - const newHP = Math.floor( - Math.floor( - 2 * pokemon.species.baseStats['hp'] + pokemon.set.ivs['hp'] + Math.floor(pokemon.set.evs['hp'] / 4) + 100 - ) * pokemon.level / 100 + - 10 - ); - pokemon.hp = newHP - (pokemon.maxhp - pokemon.hp); - pokemon.maxhp = newHP; - pokemon.heal(pokemon.baseMaxhp / 4); - this.add('-heal', pokemon, pokemon.getHealth); - pokemon.takeItem(); - pokemon.setItem('toxicorb'); - this.add('-message', pokemon.name + '\'s item is now a Toxic Orb!'); - this.add('-message', pokemon.name + '\'s ability is now Poison Heal!'); - this.boost({atk: 2, spe: 1}, pokemon); + this.add(`c|${getName('Kaiju Bunny')}|Wow, okay, r00d  ̄(ಥㅅಥ) ̄`); }, }, kalalokki: { - noCopy: true, - onStart(target) { - this.add(`c|@Kalalokki|(•_•)`); - this.add(`c|@Kalalokki|( •_•)>⌐■-■`); - this.add(`c|@Kalalokki|(⌐■_■)`); - if (target.illusion) return; - this.field.setWeather('raindance'); - }, - onFaint() { - this.add(`c|@Kalalokki|(⌐■_■)`); - this.add(`c|@Kalalokki|( •_•)>⌐■-■`); - this.add(`c|@Kalalokki|(x_x)`); - }, - }, - kaori: { - noCopy: true, - onStart(pokemon) { - this.add(`c|+kaori|(~ ̄³ ̄)~`); - if (pokemon.illusion) return; - this.boost({spd: 2}, pokemon); - }, - onSwitchOut() { - this.add(`c|+kaori|ಠ_ಠ`); - }, - onFaint() { - this.add(`c|+kaori|(◕ ᥥ ◕✿)`); - }, - }, - kay: { noCopy: true, onStart() { - this.add(`c|+kay|Every kiss begins with Kay`); - }, - onSwitchOut() { - this.add(`c|+kay|くコ:彡`); + this.add(`c|${getName('Kalalokki')}|(•_•)`); + this.add(`c|${getName('Kalalokki')}|( •_•)>⌐■-■`); + this.add(`c|${getName('Kalalokki')}|(⌐■_■)`); }, onFaint() { - this.add(`c|+kay|'kay bye!くコ:彡`); + this.add(`c|${getName('Kalalokki')}|(⌐■_■)`); + this.add(`c|${getName('Kalalokki')}|( •_•)>⌐■-■`); + this.add(`c|${getName('Kalalokki')}|(x_x)`); }, - // Simple Innate - onBoost(boost, target, source, effect) { - if (target?.illusion) return; - if (effect?.id === 'zpower') return; - let i: BoostName; - for (i in boost) { - boost[i]! *= 2; + innateName: "Sturdy", + shortDesc: "If this Pokemon is at full HP, it survives one hit with at least 1 HP. Immune to OHKO.", + // Sturdy Innate + onTryHit(pokemon, target, move) { + if (target.illusion) return; + if (move.ohko) { + this.add('-immune', pokemon, '[from] ability: Sturdy'); + return null; + } + }, + onDamagePriority: -100, + onDamage(damage, target, source, effect) { + if (target.illusion) return; + if (target.hp === target.maxhp && damage >= target.hp && effect && effect.effectType === 'Move') { + this.add('-ability', target, 'Sturdy'); + return target.hp - 1; } }, }, - kie: { + kennedy: { noCopy: true, onStart() { - this.add(`c|%Kie|Wherever there is hope, there is most definitely despair.`); + this.add(`c|${getName('Kennedy')}|up the reds`); }, onSwitchOut() { - this.add(`c|%Kie|Still better than Aeonic, btw.`); + this.add(`c|${getName('Kennedy')}|brb Jayi is PMing me (again) -_-`); }, onFaint() { - this.add(`c|%Kie|https://www.youtube.com/watch?v=Hyw6kKMjp5A`); + this.add(`c|${getName('Kennedy')}|I'm not meant to score goals anyway, I'm a defensive striker.`); + }, + }, + kev: { + noCopy: true, + onStart() { + this.add(`c|${getName('Kev')}|Sorry for raining on your parade`); + }, + onSwitchOut() { + this.add(`c|${getName('Kev')}|Rain, rain, go away, come again another day`); + }, + onFaint() { + this.add(`c|${getName('Kev')}|I guess I'm all washed up...`); + }, + }, + kingbaruk: { + noCopy: true, + onStart() { + this.add(`c|${getName('Kingbaruk')}|:cute:`); + }, + onSwitchOut() { + this.add(`c|${getName('Kingbaruk')}|//none`); + }, + onFaint() { + this.add(`c|${getName('Kingbaruk')}|Fijne avond nog`); }, }, kingswordyt: { noCopy: true, onStart() { - this.add(`c|@KingSwordYT|You're the master of your destiny, take destiny by the horns and have fun!`); + this.add(`c|${getName('KingSwordYT')}|Mucho texto`); }, onSwitchOut() { - this.add(`c|@KingSwordYT|I eat when im upset, and i gotta eat`); + this.add(`c|${getName('KingSwordYT')}|Hasta la próximaaaa`); }, onFaint() { - this.add(`c|@KingSwordYT|BUAAAAAA IYA AYÚDAME :(`); + this.add(`c|${getName('KingSwordYT')}|**__Se anula el host__**`); }, }, kipkluif: { noCopy: true, onStart() { - this.add(`c|%Kipkluif|I like bacon.`); + this.add(`c|${getName('Kipkluif')}|Please play LCUU, it's fun`); }, onSwitchOut() { - this.add(`c|%Kipkluif|Run away da, run run away da`); + this.add(`c|${getName('Kipkluif')}| /teleport`); }, onFaint() { - this.add(`c|%Kipkluif|Aah! Well, at least I have chicken.`); - }, - // Footballer innate - onBasePowerPriority: 8, - onBasePower(basePower, pokemon, target, move) { - if (move.name.includes('Kick') && !pokemon.transformed && !pokemon.illusion) { - this.chainModify(1.75); - } + this.add(`c|${getName('Kipkluif')}|I've failed you.. I pray you hurry.. with those reinforcments.. you promised..`); }, }, kris: { + innateName: "phuck", + desc: "If this Pokemon is an Unown forme, it is immune to indirect damage and transforms into a different Unown letter forme, aside from ! and ?, at the end of each turn.", + shortDesc: "Unown: Magic Guard + change letter every turn.", noCopy: true, - onStart(pokemon) { - this.add(`c|+Kris|wjat poppin ;)))))`); - if (pokemon.illusion) return; - for (const target of pokemon.side.foe.active) { - if (!target || target.fainted) continue; - for (const moveSlot of target.moveSlots) { - const move = this.dex.getMove(moveSlot.move); - const moveType = move.id === 'hiddenpower' ? target.hpType : move.type; - if (move.category !== 'Status' && ( - this.dex.getImmunity(moveType, pokemon) && this.dex.getEffectiveness(moveType, pokemon) > 0 || move.ohko - )) { - this.add('-ability', pokemon, 'Anticipation'); - return; - } - } + onStart(source) { + const foeName = source.side.foe.active[0].illusion ? + source.side.foe.active[0].illusion.name : source.side.foe.active[0].name; + if (foeName === 'Aeonic' || source.side.foe.name === 'Aeonic') { + this.add(`c|${getName('Kris')}|HAPPY BIRTHDAY AEONIC!!!!`); + } else { + this.add(`c|${getName('Kris')}|hi ${foeName}`); } }, - onSwitchOut() { - this.add(`c|+Kris|vbye`); + onSwitchOut(source) { + const foeName = source.side.foe.active[0].illusion ? + source.side.foe.active[0].illusion.name : source.side.foe.active[0].name; + if (foeName === 'Aeonic' || source.side.foe.name === 'Aeonic') { + this.add(`c|${getName('Kris')}|HAPPY BIRTHDAY AEONIC!!!!`); + } else { + this.add(`c|${getName('Kris')}|bye ${foeName}`); + } }, - onFaint() { - this.add(`c|+Kris|thats weird but ok`); + onFaint(target) { + const foeName = target.illusion ? + target.illusion.name : target.name; + if (foeName === 'Aeonic' || target.side.name === 'Aeonic') { + this.add(`c|${getName('Kris')}|HAPPY BIRTHDAY AEONIC!!!!`); + } else { + this.add(`c|${getName('Kris')}|Fortnite Battle Royale`); + } }, - }, - level51: { - noCopy: true, - onStart() { - this.add(`c|+Level 51|Calculating chance of victory!`); - }, - onSwitchOut() { - this.add(`c|+Level 51|chance_victory < 1. Recalibrating...`); - }, - onFaint() { - this.add(`c|+Level 51|**IndexError**: list index out of range`); - }, - }, - lifeisdank: { - noCopy: true, - onStart(target) { - this.add(`c|+LifeisDANK|!!!ლ(⁰⊖⁰ლ) Peent Peent.`); + // phuck innate + onDamage(damage, target, source, effect) { // Magic Guard + if (effect.id === 'heavyhailstorm') return; if (target.illusion) return; - this.boost({spe: 2}, target); - }, - onSwitchOut() { - this.add(`c|+LifeisDANK|!(•⌔• ) Peent Peent.`); - }, - onFaint() { - this.add(`c|+LifeisDANK|(•⌔•. ) Peent.`); - }, - // Mountaineer innate - onDamage(damage, target, source, effect) { - if (effect?.id === 'stealthrock') { + if (!target.species.id.includes('unown')) return; + if (effect.effectType !== 'Move') { + if (effect.effectType === 'Ability') this.add('-activate', source, 'ability: ' + effect.name); return false; } }, - onTryHit(target, source, move) { - if (move.type === 'Rock' && !target.activeTurns) { - this.add('-immune', target, '[from] ability: Mountaineer'); - return null; + onResidual(pokemon) { + if (pokemon.illusion) return; + if (!pokemon.species.id.includes('unown')) return; + // So this doesn't activate upon switching in + if (pokemon.activeTurns < 1) return; + const unownLetters = 'abcdefghijklmnopgrstuvwxyz'.split(''); + const currentFormeID = this.toID(pokemon.set.species); + const currentLetter = currentFormeID.charAt(5) || 'a'; + const chosenLetter = this.sample(unownLetters.filter(letter => letter !== currentLetter)); + // Change is permanent so when you switch out you keep the letter + this.add(`c|${getName('Kris')}|watch this`); + if (chosenLetter === 'w') { + this.add('-activate', pokemon, 'ability: phuck'); + pokemon.formeChange(`unownw`, this.effect, true); + this.add(`c|${getName('Kris')}|W? More like L`); + this.add('-activate', pokemon, 'ability: phuck'); + pokemon.formeChange(`unownl`, this.effect, true); + this.hint(`There are no W Pokemon that work with Kris's signature move, so we're counting this as a loss`); + } else if (chosenLetter === 'u') { + this.add('-activate', pokemon, 'ability: phuck'); + pokemon.formeChange(`unownu`, this.effect, true); + this.add(`c|${getName('Kris')}|U? I'm already an Unown, no`); + this.add('-activate', pokemon, 'ability: phuck'); + const chosenLetter2 = this.sample(unownLetters.filter(letter => letter !== 'u' && letter !== 'w')); + pokemon.formeChange(`unown${chosenLetter2}`, this.effect, true); + this.hint(`There are no U Pokemon that work with Kris's signature move, so we're counting this as a loss`); + } else { + this.add('-activate', pokemon, 'ability: phuck'); + pokemon.formeChange(`unown${chosenLetter === 'a' ? '' : chosenLetter}`, this.effect, true); } }, }, - lostseso: { + lamp: { noCopy: true, onStart(pokemon) { - this.add(`c|+Lost Seso|<3 ( ͡° ͜ʖ ͡°) DANCE WITH ME ( ͡° ͜ʖ ͡°) <3`); - if (pokemon.illusion) return; - this.effectData.danceMultiplier = 0; + this.add(`c|${getName('Lamp')}|DUDE HI ${pokemon.side.foe.name} (:`); }, - onAfterMove(pokemon, target, move) { - if (pokemon.illusion) return; - if (move.flags.dance) this.effectData.danceMultiplier++; - }, - onSwitchOut() { - this.add(`c|+Lost Seso|Ran out of ramen, brb`); + onSwitchOut(pokemon) { + this.add(`c|${getName('Lamp')}|bye ${pokemon.side.foe.name} :)`); }, onFaint() { - this.add(`c|+Lost Seso|└[ ─ ಎ ─ ]┘ 0% Battery, feed me ramen please`); + this.add(`c|${getName('Lamp')}|no u`); }, }, - macchaeger: { + lionyx: { noCopy: true, onStart() { - this.add(`c|+MacChaeger|What are you gonna do with that big bat? Gonna hit me? Better make it count. Better make it hurt. Better kill me in one shot.`); + this.add(`c|${getName('Lionyx')}|Hi, this is ps-chan, how may I help you, user-kun? (。◕‿‿◕。)`); + }, + onSwitchOut() { + this.add(`c|${getName('Lionyx')}|Teclis au secours`); }, onFaint() { - this.add(`c|+MacChaeger|im gonna pyuk`); + this.add(`c|${getName('Lionyx')}|The cold never bothered me anyway...`); + }, + }, + litteleven: { + noCopy: true, + onStart() { + this.add(`c|${getName('Litt♥Eleven')}|The coin is flipped, what follows is destiny alone.`); + }, + onSwitchOut() { + this.add(`c|${getName('Litt♥Eleven')}|Looks like my business is finished here... for now.`); + }, + onFaint() { + this.add(`c|${getName('Litt♥Eleven')}|Perhaps, coin tossing isn't the optimal way to win a war...`); }, }, madmonty: { noCopy: true, - onStart(pokemon) { - this.add(`c|+Mad Monty ¾°|/me puts on a coat, to protect himself from the cold.`); - this.add(`c|+Mad Monty ¾°|Don't get eaten by llamas!`); + onStart() { + this.add(`c|${getName('Mad Monty ¾°')}|Ah, the sweet smell of rain... Oh! Hi there!`); }, onSwitchOut() { - this.add(`c|+Mad Monty ¾°|Oh, I see how it is. Ok, fine. Be that way. I'll just be over here, then.`); + this.add(`c|${getName('Mad Monty ¾°')}|Hey, I was enjoying the weather! Awww...`); }, onFaint() { - this.add(`c|+Mad Monty ¾°|I take it back- I hope you DO get eaten by llamas. Toodles!`); + this.add(`c|${getName('Mad Monty ¾°')}|Nooo, if I go, who will stop the llamas?`); }, }, majorbowman: { noCopy: true, onStart() { - this.add(`c|@MajorBowman|I'm MajorBowman and I'm here to make it clear.`); + this.add(`c|${getName('MajorBowman')}|Aaaand Cracktion!`); }, onSwitchOut() { - this.add(`c|@MajorBowman|Witty catch phrase, you know what I mean?`); + this.add(`c|${getName('MajorBowman')}|This isn't Maury Povich!`); }, onFaint() { - this.add(`c|@MajorBowman|THEY GOT ME, GAL`); + this.add(`c|${getName('MajorBowman')}|Never loved ya.`); }, }, marshmallon: { noCopy: true, onStart() { - this.add(`c|@Marshmallon|What a wonderful day at the beach!`); + this.add(`c|${getName('Marshmallon')}|I'm hungry. Are you edible? c:`); }, onSwitchOut() { - this.add(`c|@Marshmallon|__C..c...cooold >w<__`); + this.add(`c|${getName('Marshmallon')}|RAWWWR`); }, onFaint() { - this.add(`c|@Marshmallon|I got hit by a thunderbolt!`); + this.add(`c|${getName('Marshmallon')}|I'm still hungry. rawr. :c`); }, }, - martha: { - noCopy: true, - onStart() { - this.add(`c|+martha|in to lose r1`); - }, - onSwitchOut() { - this.add(`c|+martha|bad`); - }, - }, - marty: { - noCopy: true, - // No phrases provided - }, meicoo: { noCopy: true, onStart() { - this.add(`c|%Meicoo|/joinhunt`); + this.add(`c|${getName('Meicoo')}|cool quiz`); }, onSwitchOut() { - this.add(`c|%Meicoo|/leavehunt`); + this.add(`c|${getName('Meicoo')}|/leavehunt`); }, onFaint() { - this.add(`c|%Meicoo|"That is not the answer - try again!"`); - }, - }, - megazard: { - noCopy: true, - onStart() { - this.add(`c|@Megazard|Almond top of the world!`); - }, - onSwitchOut() { - this.add(`c|@Megazard|Change of plants`); - }, - onFaint() { - this.add(`c|@Megazard|Better luck next thyme`); + this.add(`c|${getName('Meicoo')}|/endhunt`); }, }, mitsuki: { noCopy: true, onStart() { - this.add(`c|@Mitsuki|SSSSSSSSSSSSS`); + this.add(`c|${getName('Mitsuki')}|alguem quer batalha?????`); }, - onSwitchOut() { - this.add(`c|@Mitsuki|sssssssssssss`); + onSwitchOut(source) { + this.add(`c|${getName('Mitsuki')}|You're weak, ${source.side.foe.name}. Why? Because you lack... hatred.`); }, onFaint() { - this.add(`c|@Mitsuki|sss`); + this.add(`c|${getName('Mitsuki')}|THIS WORLD SHALL KNOW P A I N`); }, }, - morfent: { + n10sit: { noCopy: true, onStart() { - this.add(`c|@Morfent ( _̀> ̀)|──────▀█████▄──────▲`); - this.add(`c|@Morfent ( _̀> ̀)|───▄███████████▄──◀█▶`); - this.add(`c|@Morfent ( _̀> ̀)|─────▄████▀█▄──────█`); - this.add(`c|@Morfent ( _̀> ̀)|───▄█████████████████▄ -I`); - this.add(`c|@Morfent ( _̀> ̀)|─▄█████.▼.▼.▼.▼.▼.▼.▼ -cast`); - this.add(`c|@Morfent ( _̀> ̀)|▄███████▄.▲.▲.▲.▲.▲.▲ -magic`); - this.add(`c|@Morfent ( _̀> ̀)|█████████████████████▀▀-shitpost`); + this.add(`c|${getName('n10siT')}|Heheheh... were you surprised?`); + }, + onSwitchOut() { + this.add(`c|${getName('n10siT')}|Heheheh... did I scare you?`); }, onFaint() { - // Morfent returns something so im doing it this way - const endquote = this.random(3); - if (endquote === 1) { - this.add(`c|@Morfent ( _̀> ̀)|Hacking claims the lives of over 2,000 registered laddering alts every day.`); - } else if (endquote === 2) { - this.add(`c|@Morfent ( _̀> ̀)|Every 60 seconds in Africa, a minute passes. Together we can stop this. Please spread the word.`); - } else { - this.add(`c|@Morfent ( _̀> ̀)|!dt morfent's husbando`); - this.add(`raw|`); - this.add(`raw|Dex#: 94 | Gen: 1 | Height: 1.5 m | Weight: 40.5 kg (60 BP) | Dex Colour: Purple | Egg Group(s): Amorphous | Does Not Evolve`); + this.add(`c|${getName('n10siT')}|Hoopa never saw one of those!`); + }, + }, + naziel: { + noCopy: true, + onStart() { + this.add(`c|${getName('Naziel')}|ay ola soy nasieeeeeeel`); + }, + onSwitchOut() { + this.add(`c|${getName('Naziel')}|YAY, I WILL NOT DIE THIS TIME`); + }, + onFaint() { + this.add(`c|${getName('Naziel')}|Toy xikito no puedo ;-;`); + }, + }, + nol: { + noCopy: true, + onStart() { + this.add(`c|${getName('Nol')}|What's up nerds`); + }, + onSwitchOut() { + this.add(`c|${getName('Nol')}|cya nerds later`); + }, + onFaint() { + this.add(`c|${getName('Nol')}|nerd`); + }, + innateName: "RSUA", + shortDesc: "+1 priority to status moves. 1.5x Defense and Special Defense.", + // Innate Prankster and Eviolite + onModifyPriority(priority, pokemon, target, move) { + if (move?.category === 'Status') { + move.pranksterBoosted = true; + return priority + 1; } }, + onModifyDefPriority: 2, + onModifyDef(def, pokemon) { + if (pokemon.illusion) return; + return this.chainModify(1.5); + }, + onModifySpDPriority: 2, + onModifySpD(spd, pokemon) { + if (pokemon.illusion) return; + return this.chainModify(1.5); + }, + }, + notater517: { + noCopy: true, + onStart() { + this.add(`c|${getName('Notater517')}|nyaa~... I mean, 'tis a swell day to twirl one's mustache, isn't it?!`); + }, + onSwitchOut() { + this.add(`c|${getName('Notater517')}|/me corrupt trivia noises`); + }, + onFaint() { + this.add(`c|${getName('Notater517')}|This is probably a good time to fix my sleep schedule`); + }, }, nui: { noCopy: true, onStart() { - this.add(`c|&nui|(*^3^)`); + this.add(`c|${getName('nui')}|/html `); }, onSwitchOut() { - this.add(`c|&nui|(´◔‸◔\`) **??+ !`); + this.add(`c|${getName('nui')}|/html `); }, onFaint() { - this.add(`c|&nui|(◕︿◕✿)`); - }, - }, - om: { - noCopy: true, - onStart(target, source) { - this.add(`c|@OM|use shift gear`); - if (source.illusion) return; - this.add('-start', source, 'typeadd', 'Fairy'); - }, - onSwitchOut() { - this.add(`c|@OM|Ok brb I'm gonna ${["ladder Mix and Mega", "roll battle some surv regs real quick", "sweep y'all in mafia let's get it"][this.random(3)]}`); - }, - onFaint() { - this.add(`c|@OM|${["Oh god I rolled a 1", "Killed Night 1, seriously?"][this.random(2)]}`); + this.add(`c|${getName('nui')}|/html `); }, }, overneat: { noCopy: true, onStart(source) { - this.add(`c|+Overneat|[muffled eurobeat playing in the distance]`); + this.add(`c|${getName('Overneat')}|Lets end this ${source.side.foe.name}!!`); if (source.species.id !== 'absolmega' || source.illusion) return; - this.add('-start', source, 'typeadd', 'Fairy'); + this.add('-start', source, 'typechange', source.types.join('/'), '[silent]'); }, onSwitchOut() { - this.add(`c|+Overneat|Time to take a siesta.`); + this.add(`c|${getName('Overneat')}|I can do better!`); }, onFaint() { - this.add(`c|+Overneat|I gotta go, but friendly reminder to drink water after this battle.`); + this.add(`c|${getName('Overneat')}|I was to cocky...`); }, }, - pablo: { + om: { noCopy: true, onStart() { - this.add(`c|+Pablo|Let's get this party started.`); + this.add(`c|${getName('OM~!')}|What's Up Gamers`); }, onSwitchOut() { - this.add(`c|+Pablo|I need a break, this is getting boring.`); + this.add(`c|${getName('OM~!')}|Let me just ${['host murder for the 100th time', 'clean out scum zzz', 'ladder mnm rq'][this.random(3)]}`); }, onFaint() { - this.add(`c|+Pablo|It's cool, I didn't wanna battle anyway.`); + this.add(`c|${getName('OM~!')}|ugh, I ${['rolled a 1, damnit.', 'got killed night 1, seriously?', 'got v-create\'d by fucking dragapult lmaoo'][this.random(3)]}`); + }, + }, + pants: { + noCopy: true, + onStart() { + this.add(`c|${getName('pants')}|neat`); + }, + onSwitchOut(source) { + if (source.side.sideConditions.givewistfulthinking) { + this.add(`c|${getName('pants')}|brb contemplating things`); + } else { + this.add(`c|${getName('pants')}|brb dying a little`); + } + }, + onFaint() { + this.add(`c|${getName('pants')}|how do you even knock out something that's already dead? i call bs`); }, }, paradise: { noCopy: true, onStart() { - this.add(`c|@Paradise|⠠⠺⠓⠁⠞⠀⠞⠓⠑⠀⠋⠥⠉⠅⠀⠙⠊⠙⠀⠽⠕⠥⠀⠚⠥⠎⠞⠀⠋⠥⠉⠅⠊⠝⠛⠀⠎⠁⠽⠀⠁⠃⠕⠥⠞⠀⠍⠑,⠀⠽⠕⠥⠀⠇⠊⠞⠞⠇⠑⠀⠃⠊⠞⠉⠓?`); + this.add(`c|${getName('Paradise ╱╲☼')}|You ever notice that the first thing a PS tryhard does is put their PS auth in their smogon signature?`); }, onSwitchOut() { - this.add(`c|@Paradise|Braille is the only language you need.`); + this.add(`c|${getName('Paradise ╱╲☼')}|Pokemon Showdown copypastas have to be among the worst I've seen on any website. People spam garbage over and over until eventually the mods get fed up and clamp down on spam. I don't blame them for it. Have you ever seen a copypasta fail as hard as the dead memes on this website? There are mods on here who still think that "Harambe" and "Damn Daniel" are the peak of comedy. Not to mention that there are rooms on here that don't even talk about pokemon lol. Yeah, I don't see this website lasting more than 2 years, I'd suggest becoming a mod somewhere else.`); + }, + onFaint(pokemon) { + this.add(`c|${getName('Paradise ╱╲☼')}|Paradise has been kicked, not banned, therefore you could still potentially invite them back. However, do not do this @${pokemon.side.name}, unless of course, you want to be banned too, because if you invite them back you and Paradise will both be banned.`); + }, + }, + partman: { + noCopy: true, + onStart(source) { + this.add(`c|${getName('PartMan')}|${[`OMA HI ${source.side.name.toUpperCase()} BIG FAN`, `HYDRO IS A NERD`][this.random(2)]}`); + }, + onSwitchOut() { + this.add(`c|${getName('PartMan')}|/me flees`); }, onFaint() { - this.add(`c|@Paradise|⠠⠽⠕⠥’⠗⠑⠀⠋⠥⠉⠅⠊⠝⠛⠀⠙⠑⠁⠙,⠀⠅⠊⠙⠙⠕.`); + this.add(`c|${getName('PartMan')}|B-booli. >.<`); + }, + }, + peapodc: { + noCopy: true, + onStart() { + this.add(`c|${getName('peapod c')}|/me sprints into the room`); + }, + onSwitchOut() { + this.add(`c|${getName('peapod c')}|Must maintain m o m e n t u m`); + }, + onFaint() { + this.add(`c|${getName('peapod c')}|They say sleep is the cousin of death — but even ghosts need to sleep!`); + }, + }, + perishsonguser: { + noCopy: true, + onStart() { + this.add(`c|${getName('Perish Song')}|(╯°□°)╯︵ ┻━┻`); + }, + onSwitchOut() { + this.add(`c|${getName('Perish Song')}|┬──┬◡ノ(° -°ノ)`); + }, + onFaint() { + this.add(`c|${getName('Perish Song')}|Thanks for coming to my TED talk.`); + }, + }, + phiwings99: { + noCopy: true, + onStart() { + this.add(`c|${getName('phiwings99')}|Pick.`); + }, + onSwitchOut() { + this.add(`c|${getName('phiwings99')}|I'm boated.`); + }, + onFaint() { + this.add(`c|${getName('phiwings99')}|God, Nalei is fucking terrible at this game.`); + }, + }, + piloswinegripado: { + noCopy: true, + onStart() { + this.add(`c|${getName('piloswine gripado')}|Suave?`); + }, + onSwitchOut() { + this.add(`c|${getName('piloswine gripado')}|cya frend :)`); + }, + onFaint() { + this.add(`c|${getName('piloswine gripado')}|This was lame :/`); }, }, pirateprincess: { - noCopy: true, - onStart() { - this.add(`c|%Pirate Princess|Ahoy!`); - }, - onSwitchOut() { - this.add(`c|%Pirate Princess|You will always remember this as the day that you almost caught Captain Ja- Pirate Princess!`); - }, - onFaint() { - this.add(`c|%Pirate Princess|Erm… Parley?`); - }, - }, - pluviometer: { - noCopy: true, - onStart() { - this.add(`c|+pluviometer|${["Need a GP check?", "I'm a switch-in and I'm switching in #hyphenation"][this.random(2)]}`); - }, - onSwitchOut() { - this.add(`c|+pluviometer|${["I wish this were a better matchup #subjunctive", "GP 1/2", "GP 2/2"][this.random(3)]}`); - }, - onFaint() { - this.add(`c|+pluviometer|${["Follow SmogonU on Facebook! https://www.facebook.com/SmogonU", "Follow SmogonU on Twitter! https://twitter.com/SmogonU"][this.random(2)]}`); - }, - }, - pohjis: { - noCopy: true, - onStart() { - this.add(`c|+Pohjis|thats pohjis`); - }, - onSwitchOut() { - this.add(`c|+Pohjis|ÖPÖOLÖL`); - }, - onFaint() { - this.add(`c|+Pohjis|rly enjoyed that`); - }, - }, - pre: { noCopy: true, onStart(source) { - this.add(`c|&pre|let's go, in and out, 20 minute adventure`); + this.add(`c|${getName('PiraTe Princess')}|Ahoy! o/`); + // Easter Egg const activeMon = this.toID( source.side.foe.active[0].illusion ? source.side.foe.active[0].illusion.name : source.side.foe.active[0].name ); - if (activeMon === 'anubis') { - this.add(`c|+Anubis|ohey it's pre`); - this.add(`c|+Anubis|!showimage https://pokemonshowdown.com/images/ssbkitten.jpg`); - this.add(`raw|`); - this.add(`c|&pre|<3`); + if (activeMon === 'kaijubunny') { + this.add(`c|${getName('PiraTe Princess')}|~shame`); + this.add(`raw|`); + this.add(`c|${getName('Kaiju Bunny')}|WHY MUST YOU DO THIS TO ME`); } }, onSwitchOut() { - this.add(`c|&pre|sometimes science is more art than science`); + this.add(`c|${getName('PiraTe Princess')}|brb making tea`); }, onFaint() { - this.add(`c|&pre|LAMBS TO THE COSMIC SLAUGHTER!!`); + this.add(`c|${getName('PiraTe Princess')}|I failed my death save`); + }, + onHit(target, source, move) { + if (move?.effectType === 'Move' && target.getMoveHitData(move).crit) { + this.add(`c|${getName('PiraTe Princess')}|NATURAL 20!!!`); + } }, }, psynergy: { noCopy: true, onStart() { - this.add(`c|%Psynergy|oh`); + this.add(`c|${getName('Psynergy')}|Will you survive?`); }, onSwitchOut() { - this.add(`c|%Psynergy|Joe doesn't pay me enough for this`); + this.add(`c|${getName('Psynergy')}|yadon moment`); }, onFaint() { - this.add(`c|%Psynergy|I'm going to be late...`); + this.add(`c|${getName('Psynergy')}|oh`); }, }, ptoad: { noCopy: true, onStart() { - this.add(`c|%ptoad⚬|Make it rain!`); + this.add(`c|${getName('ptoad')}|I'm ptoad.`); }, onSwitchOut() { - this.add(`c|%ptoad⚬|Oh. You're switching me out. No, it's fine, I "toad"ally get it.`); + this.add(`c|${getName('ptoad')}|Bye, ribbitch!`); }, onFaint() { - this.add(`c|%ptoad⚬|Wow. Way to rain on my parade.`); + this.add(`c|${getName('ptoad')}|OKKKK DUUUDE`); + }, + innateName: "Sticky Hold", + shortDesc: "This Pokemon cannot lose its held item due to another Pokemon's attack.", + // Sticky Hold Innate + onTakeItem(item, pokemon, source) { + if (this.suppressingAttackEvents(pokemon) || !pokemon.hp || pokemon.item === 'stickybarb') return; + if (!this.activeMove) throw new Error("Battle.activeMove is null"); + if ((source && source !== pokemon) || this.activeMove.id === 'knockoff') { + this.add('-activate', pokemon, 'ability: Sticky Hold'); + return false; + } }, }, - quitequiet: { + quadrophenic: { noCopy: true, onStart() { - this.add(`c|@Quite Quiet|spooky :d`); + this.add(`c|${getName('quadrophenic')}|Did you ever like it then?`); }, onFaint() { - this.add(`c|@Quite Quiet|:'(`); + this.add(`c|${getName('quadrophenic')}|It fell apart.`); }, - onModifyDefPriority: 6, - onModifyDef(def, pokemon) { - if (pokemon.illusion) return; - if (!pokemon.transformed) { - return this.chainModify(1.5); - } + }, + rabia: { + noCopy: true, + onStart() { + this.add(`c|${getName('Rabia')}|eternally`); }, - onModifySpDPriority: 6, - onModifySpD(spd, pokemon) { - if (pokemon.illusion) return; - if (!pokemon.transformed) { - return this.chainModify(1.5); - } + onSwitchOut() { + this.add(`c|${getName('Rabia')}|rabia`); + }, + onFaint() { + this.add(`c|${getName('Rabia')}|im top 500 in relevant tiers and lead gp, i have 8 badges, im fine, gg`); }, }, rach: { noCopy: true, - onStart(target, source) { - this.add(`c|%Rach|BURN IT DOWN!`); - if (source.illusion) return; - this.add('-start', source, 'typeadd', 'Fighting'); - this.boost({spe: 1}, source); + onStart() { + this.add(`c|${getName('Rach')}|Hel-lo`); }, onSwitchOut() { - this.add(`c|%Rach|Tag!`); + this.add(`c|${getName('Rach')}|I was doing better alone`); }, onFaint() { - this.add(`c|%Rach|I oversold your move`); + this.add(`c|${getName('Rach')}|I'm all good already, so moved on, it's scary`); }, }, rageuser: { noCopy: true, - onStart(pokemon) { - this.add(`c|%Rage|I'm about to ruin this mans whole career`); + onStart() { + this.add(`c|${getName('Rage')}|Hello there`); }, onSwitchOut() { - this.add(`c|%Rage|Ain't supposed to be like that chief, we out`); + this.add(`c|${getName('Rage')}|im off, cya lads`); }, onFaint() { - this.add(`c|%Rage|/me quits`); + this.add(`c|${getName('Rage')}|/me quits`); + }, + }, + raihankibana: { + noCopy: true, + onStart() { + this.add(`c|${getName('Raihan Kibana')}|Hi gm`); + }, + onSwitchOut() { + this.add(`c|${getName('Raihan Kibana')}|Ight Imma head out`); + }, + onFaint() { + this.add(`c|${getName('Raihan Kibana')}|Grr bork bork :(`); + }, + }, + rajshoot: { + noCopy: true, + onStart() { + this.add(`c|${getName('Raj.Shoot')}|Plaza Power!`); + }, + onSwitchOut() { + this.add(`c|${getName('Raj.Shoot')}|We'll be back!`); + }, + onFaint() { + this.add(`c|${getName('Raj.Shoot')}|You'll join me in the shadow realm soon....`); }, }, ransei: { noCopy: true, onStart() { - this.add(`c|@Ransei| Sup! I have been brought from the world of Hackmons to give you a preview of our characteristics. I’ve been genetically engineered to beat you at all costs. Expect to lose this fight!`); + this.add(`c|${getName('Ransei')}|Sup! This is Gen 8 so imma run an Eternamax set. Best of luck. You’ll need it :^)`); }, - onFaint() { - this.add(`c|@Ransei|ripsei`); - }, - }, - rorymercury: { - noCopy: true, - onStart(pokemon) { - this.add(`c|+Rory Mercury|guess I'm in charge now`); - if (pokemon.illusion) return; - this.boost({atk: 1}, pokemon); - }, - onSwitchOut() { - this.add(`c|+Rory Mercury|brb running low on battery`); - }, - onFaint() { - this.add(`c|+Rory Mercury|pressing charges for battery`); - }, - }, - samjo: { - noCopy: true, - onStart() { - this.add(`c|+SamJo|Heyo/ Hope your day's been fantasthicc! Woo!`); - }, - onSwitchOut() { - this.add(`c|+SamJo|Catch ya later, stay thicc my friends o/`); - }, - onFaint() { - this.add(`c|+SamJo|Oof, gotta get thiccer……. ;(`); - }, - }, - schiavetto: { - noCopy: true, - onStart() { - this.add(`c|%Schiavetto|Aight, sis, guess I'm parking.`); - }, - onSwitchOut() { - this.add(`c|%Schiavetto|Someone help me press this.`); - }, - onFaint() { - this.add(`c|%Schiavetto|Read my ISOs when I'm dead`); - }, - }, - scotteh: { - noCopy: true, - onStart() { - this.add(`c|@Scotteh|─────▄▄████▀█▄`); - this.add(`c|@Scotteh|───▄██████████████████▄`); - this.add(`c|@Scotteh|─▄█████.▼.▼.▼.▼.▼.▼.▼`); - }, - onSwitchOut() { - this.add(`c|@Scotteh|▄███████▄.▲.▲.▲.▲.▲.▲`); - this.add(`c|@Scotteh|█████████████████████▀▀`); - }, - onFaint() { - this.add(`c|@Scotteh|▄███████▄.▲.▲.▲.▲.▲.▲`); - this.add(`c|@Scotteh|█████████████████████▀▀`); - }, - }, - shiba: { - noCopy: true, - onStart() { - this.add(`c|+Shiba|LINDA IS INDA`); - }, - onSwitchOut() { - this.add(`c|+Shiba|gotta buy an alt rq brb`); - }, - }, - slowbroth: { - noCopy: true, - onStart() { - this.add(`c|+Slowbroth|DETECTING FOREIGN SPECIES...`); - }, - onSwitchOut() { - this.add(`c|+Slowbroth|TELEPORTING TO ALTERNATE DIMENSION...`); - }, - onFaint() { - this.add(`c|+Slowbroth|HARDWARE DAMAGE PERMANENT...`); - }, - }, - snaquaza: { - noCopy: true, - onStart() { - this.add(`c|+Snaquaza|Snaq is baq... with a vengeance!`); - }, - onSwitchOut(pokemon) { - this.add(`c|+Snaquaza|Lynch Hoeen while I'm away...`); - if (pokemon.m.claimHP) { - pokemon.hp = pokemon.m.claimHP; - pokemon.m.claimHP = null; + onFaint(pokemon) { + const target = pokemon.side.foe.active[0]; + if (!target || target.fainted || target.hp <= 0) { + this.add(`c|${getName('Ransei')}|Ahah yes you got rekt! Welcome to Hackmons! gg m8!`); + } else { + this.add(`c|${getName('Ransei')}|ripsei... Ok look you might’ve won this time but I kid you not you’re losing next game!`); } }, - onFaint() { - this.add(`c|+Snaquaza|How did you know I was scum?`); - }, - onDamage(damage, pokemon) { - // Hack for Snaquaza's Z move - if (!pokemon.m.claimHP) return; - // Prevent Snaquaza from fainting while using a fake claim to prevent visual bug - if (pokemon.hp - damage <= 0) return (pokemon.hp - 1); - }, - onDamagingHit(damage, pokemon) { - // Hack for Snaquaza's Z move - if (!pokemon.m.claimHP || pokemon.hp > 1) return; - // Now we handle the fake claim "fainting" - pokemon.hp = pokemon.m.claimHP; - pokemon.formeChange(pokemon.baseSpecies.id); - pokemon.moveSlots = pokemon.moveSlots.slice(0, 4); - this.add('message', `${pokemon.name}'s fake claim was uncovered!`); - pokemon.m.claimHP = null; - this.add('-heal', pokemon, pokemon.getHealth, '[silent]'); - }, }, - sparksblade: { + ravioliqueen: { noCopy: true, onStart() { - this.add(`c|%sparksblade|this team looks marsh weak`); + this.add(`c|${getName('RavioliQueen')}|The Noodle Noble has Arrived!`); }, onSwitchOut() { - this.add(`c|%sparksblade|we lose`); + this.add(`c|${getName('RavioliQueen')}|Time to spaghett out of here!`); }, onFaint() { - this.add(`c|%sparksblade|i don't even play this game`); + this.add(`c|${getName('RavioliQueen')}|This is impastable!`); + }, + innateName: "Pitch Black Witch", + desc: "When this Pokemon sets or switches into Pitch Black errain, its Special Attack and Special Defense are boosted by 1 stage. If this Pokemon gets hit while Pitch Black Terrain is up, it gets +1 speed", + shortDesc: "Pitch Black Terrain: Calm Mind on switch-in, +1 Spe when attacked.", + // Coded in the terrain itself + }, + robb576: { + noCopy: true, + onStart(target, pokemon) { + if (pokemon.side.pokemonLeft === 1) { + this.add(`c|${getName('Robb576')}|This is our last stand. Give it everything you got ${pokemon.side.name}!`); + } else { + this.add(`c|${getName('Robb576')}|1, 2, 3, 4, dunno how to count no more!`); + } + }, + onSwitchOut() { + this.add(`c|${getName('Robb576')}|5, 7, 6, I will be right back into the mix!`); + }, + onFaint(pokemon) { + if (pokemon.species.name === "Necrozma-Ultra") { + this.add(`c|${getName('Robb576')}|gg better luck next time. Sorry I couldn't handle them all :^(`); + } else { + this.add(`c|${getName('Robb576')}|8, 9, 10, it has been a pleasure man!`); + } }, }, - sundar: { + sectoniaservant: { noCopy: true, onStart() { - this.add(`c|+Sundar|Now, we are really going to have a bad time. >:)`); + this.add(`c|${getName('SectoniaServant')}|I love one (1) queen bee`); }, onSwitchOut() { - this.add(`c|+Sundar|I'll spare you for now.`); + this.add(`c|${getName('SectoniaServant')}|My search for my lost queen continues....`); }, onFaint() { - this.add(`c|+Sundar|..that's it, huh? Don't say I didn't warn you.`); + this.add(`c|${getName('SectoniaServant')}|NOOOOOO NOT THE JELLY BABY`); + }, + }, + segmr: { + noCopy: true, + onStart() { + this.add(`c|${getName('Segmr')}|*awakens conquerors haki* Greetings.`); + }, + onSwitchOut() { + this.add(`c|${getName('Segmr')}|Lemme show you this`); + this.add(`l|Segmr`); + }, + onFaint(pokemon) { + const name = pokemon.side.foe.active[0].illusion ? + pokemon.side.foe.active[0].illusion.name : pokemon.side.foe.active[0].name; + this.add(`c|${getName('Segmr')}|I'm sorry ${name} but could you please stop talking to me?`); + }, + }, + sejesensei: { + noCopy: true, + onStart() { + this.add(`c|${getName('sejesensei')}|yoyo, what’ve you been reading lately`); + }, + onSwitchOut() { + this.add(`c|${getName('sejesensei')}|bbl, gonna go read some manga`); + }, + onFaint() { + this.add(`c|${getName('sejesensei')}|B-but, this didn’t happen in the manga…`); + }, + }, + seso: { + noCopy: true, + onStart() { + this.add(`c|${getName('Seso')}|I have good spacial awareness, and I'm pretty comfortable with a sword.`); + }, + onSwitchOut() { + this.add(`c|${getName('Seso')}|In the blink of an eye.`); + }, + onFaint() { + this.add(`c|${getName('Seso')}|I feel just, you know, defeated.`); + }, + }, + shadecession: { + noCopy: true, + onStart() { + this.add(`c|${getName('Shadecession')}|Better put on my Shadecessions`); + }, + onSwitchOut() { + this.add(`c|${getName('Shadecession')}|⌐■_■`); + }, + onFaint() { + this.add(`c|${getName('Shadecession')}|ah, gg fam`); + }, + }, + softflex: { + noCopy: true, + onStart() { + this.add(`c|${getName('Soft Flex')}|:]`); + }, + onFaint() { + this.add(`c|${getName('Soft Flex')}|:[`); + }, + }, + spandan: { + noCopy: true, + onStart() { + this.add(`c|${getName('Spandan')}|Mareanie!`); + }, + onSwitchOut() { + this.add(`c|${getName('Spandan')}|You can't end this toxic relationship just like that!`); + }, + onFaint() { + this.add(`c|${getName('Spandan')}|You didnt do shit. I coded myself to faint.`); + }, + }, + struchni: { + noCopy: true, + onStart() { + this.add(`c|${getName('Struchni')}|~tt newgame`); + }, + onSwitchOut(source) { + this.add(`c|${getName('Struchni')}|~tt endgame`); + }, + onFaint() { + this.add(`c|${getName('Struchni')}|**selfveto**`); }, }, teclis: { noCopy: true, onStart() { - this.add(`c|@Teclis|The Emperor protects.`); + this.add(`c|${getName('Teclis')}|Fire at will!`); }, onSwitchOut() { - this.add(`c|@Teclis|Only in death does duty end.`); + this.add(`c|${getName('Teclis')}|A spark remains...`); }, onFaint() { - this.add(`c|@Teclis|Success is commemorated; Failure merely remembered.`); + this.add(`c|${getName('Teclis')}|You set my soul on fire!`); }, }, - tennisace: { + temp: { noCopy: true, onStart() { - this.add(`c|@tennisace|VIVA LOS TIGRES`); + this.add(`c|${getName('temp')}|hi, i'm here to drop dracos`); }, onSwitchOut() { - this.add(`c|@tennisace|wtf is this shit even`); + this.add(`c|${getName('temp')}|how did I not win yet`); }, onFaint() { - this.add(`c|@tennisace|maybe next season n_n`); - }, - }, - teremiare: { - noCopy: true, - onStart(source) { - this.add(`c|+Teremiare|<('o'<)`); - if (source.illusion) return; - const target = source.side.foe.active[0]; - - const removeAll = [ - 'reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'spikes', 'toxicspikes', 'stealthrock', 'stickyweb', - ]; - const silentRemove = ['reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist']; - for (const sideCondition of removeAll) { - if (target.side.removeSideCondition(sideCondition)) { - if (!(silentRemove.includes(sideCondition))) { - this.add('-sideend', target.side, this.dex.getEffect(sideCondition).name, '[from] move: No Fun Zone', '[of] ' + source); - } - } - if (source.side.removeSideCondition(sideCondition)) { - if (!(silentRemove.includes(sideCondition))) { - this.add('-sideend', source.side, this.dex.getEffect(sideCondition).name, '[from] move: No Fun Zone', '[of] ' + source); - } - } - } - this.add('-clearallboost'); - for (const pokemon of this.getAllActive()) { - pokemon.clearBoosts(); - } - for (const clear in this.field.pseudoWeather) { - if (clear.endsWith('mod') || clear.endsWith('clause')) continue; - this.field.removePseudoWeather(clear); - } - this.field.clearWeather(); - this.field.clearTerrain(); - }, - onFaint() { - this.add(`c|+Teremiare|(>'o')>`); + this.add(`c|${getName('temp')}|oh I died`); }, }, theimmortal: { noCopy: true, - onStart() { - this.add(`c|&The Immortal|h-hi`); + onStart(source) { + const foe = source.side.foe.active[0]; + const foeName = this.toID(foe.illusion ? foe.illusion.name : foe.name); + this.add(`c|${getName('The Immortal')}|${!foe || foe.fainted || foe.hp <= 0 ? 'hi' : foeName}`); }, onSwitchOut() { - this.add(`c|&The Immortal|ok`); + this.add(`c|${getName('The Immortal')}|ok`); }, onFaint() { - this.add(`c|&The Immortal|zzz`); + this.add(`c|${getName('The Immortal')}|ban stall`); }, }, - theleprechaun: { + tiki: { noCopy: true, onStart() { - this.add(`c|+The Leprechaun|Let's get this bread`); + this.add(`c|${getName('tiki')}|just tiki.`); }, onSwitchOut() { - this.add(`c|+The Leprechaun|I'm out this mutherfucker`); + this.add(`c|${getName('tiki')}|`); + this.add(`raw|`); }, onFaint() { - this.add(`c|+The Leprechaun|This ain't it chief`); + this.add(`c|${getName('tiki')}|aksfgkjag o k`); }, }, - tony: { + traceuser: { noCopy: true, onStart() { - this.add(`c|@Tony|Greed!`); + this.add(`c|${getName('trace')}|Daishouri!`); }, onSwitchOut() { - this.add(`c|@Tony|Greed!`); + this.add(`c|${getName('trace')}|¯\\_(ツ)_/¯`); }, onFaint() { - this.add(`c|@Tony|Greed..`); - }, - // Innate levitate implemented in data/mods/ssb/scripts.js#pokemon#isGrounded - }, - torkool: { - noCopy: true, - onStart() { - this.add(`c|+torkool|:peepodetective:`); - }, - onSwitchOut() { - this.add(`c|+torkool|i cba`); - }, - onFaint() { - this.add(`c|+torkool|I don't deserve this...`); + this.add(`c|${getName('trace')}|sucks to sucks`); }, }, trickster: { noCopy: true, onStart() { - this.add(`c|@Trickster|(◕‿◕✿)`); + this.add(`c|${getName('Trickster')}|(¤﹏¤).`); }, onSwitchOut() { - this.add(`c|@Trickster|(◠﹏◠✿)`); + this.add(`c|${getName('Trickster')}|(︶︹︺)`); }, onFaint() { - this.add(`c|@Trickster|(✖﹏✖✿)`); + this.add(`c|${getName('Trickster')}|(ಥ﹏ಥ)`); + }, + }, + vexen: { + noCopy: true, + onStart() { + this.add(`c|${getName('Vexen')}|Most unlucky for you!`); + }, + onSwitchOut() { + this.add(`c|${getName('Vexen')}|brb reading Bleach`); + }, + onFaint() { + this.add(`c|${getName('Vexen')}|Wait this wasn't supposed to happen`); }, }, vivalospride: { noCopy: true, onStart() { - this.add(`c|%vivalospride|bet`); + this.add(`c|${getName('vivalospride')}|hola mi amore`); }, onSwitchOut() { - this.add(`c|%vivalospride|tuh`); + this.add(`c|${getName('vivalospride')}|no hablo español`); }, onFaint() { - this.add(`c|%vivalospride|THERE IT IS!!`); + this.add(`c|${getName('vivalospride')}|classic honestly`); }, }, volco: { noCopy: true, onStart() { - this.add(`c|+Volco|Get Ready, I'm going to take you down!`); + this.add(`c|${getName('Volco')}|/me loud controller noises`); }, onSwitchOut() { - this.add(`c|+Volco|I've decided to spare you, lucky you.`); + this.add(`c|${getName('Volco')}|/me controller clicking fades`); }, - onFaint() { - this.add(`c|+Volco|Well, seems I was taken down instead.`); + onFaint(source, target, effect) { + if (effect?.id === 'glitchexploiting') { + this.add(`c|${getName('Volco')}|Dammit, time for a reset.`); + return; + } + this.add(`c|${getName('Volco')}|Looks like the game fro-`); + this.add(`raw|
This Pokemon Showdown battle has frozen!
Don't worry, we're working on fixing it, so just carry on like you never saw this.
(Do not report this, this is intended.)
`); }, }, - xayah: { + vooper: { noCopy: true, onStart() { - this.add(`c|+Xayah|Let's dance!`); + this.add(`c|${getName('vooper')}|${['Paws out, claws out!', 'Ready for the prowl!'][this.random(2)]}`); }, onSwitchOut() { - this.add(`c|+Xayah|Fine, I'll go.`); + this.add(`c|${getName('vooper')}|Must... eat... bamboo...`); }, onFaint() { - this.add(`c|+Xayah|All out of second dances...`); - }, - }, - xfix: { - noCopy: true, - onStart() { - this.add(`c|+xfix|destroy the world!`); - }, - onSwitchOut() { - this.add(`c|+xfix|brb i need to rethink my plan`); - }, - onFaint() { - this.add(`c|+xfix|but the world refused`); + this.add(`c|${getName('vooper')}|I guess Kung Fu isn't for everyone...`); }, }, xjoelituh: { - noCopy: true, - onStart(source) { - this.add(`c|@xJoelituh|h-hi, im joel, not joe, tyvm`); - // Terrifying Demeanor Innate - if (source.illusion) return; - const target = source.side.foe.active[0]; - if (target.getStat('spe', true, true) > source.getStat('spe', true, true)) this.boost({spe: -1}, target, source); - }, - onSwitchOut() { - this.add(`c|@xJoelituh|if that's what you want, s-sure`); - }, - onFaint() { - // Random Gibberish Generator - let gibberish = ''; - for (let j = 0; j < 10; j++) gibberish += String.fromCharCode(48 + this.random(79)); - this.add(`c|@xJoelituh|${gibberish}`); - }, - }, - xprienzo: { noCopy: true, onStart() { - this.add(`c|+XpRienzo ☑◡☑|Bleh`); + this.add(`c|${getName('xJoelituh')}|Hey, how can I help you?`); }, onSwitchOut() { - this.add(`c|+XpRienzo ☑◡☑|>.>`); + this.add(`c|${getName('xJoelituh')}|Hold on, I need a second opinion.`); }, onFaint() { - this.add(`c|+XpRienzo ☑◡☑|Wait what?`); + let str = ''; + for (let x = 0; x < 10; x++) str += String.fromCharCode(48 + this.random(79)); + this.add(`c|${getName('xJoelituh')}|${str} ok`); }, }, yuki: { noCopy: true, - onStart() { - this.add(`c|%Yuki|My ice may be a little __cold__, but your plan has been put completely on __hold__!`); + onStart(target, pokemon) { + let bst = 0; + for (const stat of Object.values(pokemon.species.baseStats)) { + bst += stat; + } + let targetBst = 0; + for (const stat of Object.values(target.species.baseStats)) { + targetBst += stat; + } + let message: string; + if (bst > targetBst) { + message = 'You dare challenge me!?'; + } else { + message = 'Sometimes, you go for it'; + } + this.add(`c|${getName('yuki')}|${message}`); }, onSwitchOut() { - this.add(`c|%Yuki|I-It's too hot in here!`); + this.add(`c|${getName('yuki')}|Catch me if you can!`); }, onFaint() { - this.add(`c|%Yuki|I'm melting...`); + this.add(`c|${getName('yuki')}|You'll never extinguish our hopes!`); }, }, zalm: { noCopy: true, onStart() { - this.add(`c|+Zalm|<(:O)000>`); + this.add(`c|${getName('Zalm')}|<(:O)000>`); }, onSwitchOut() { - this.add(`c|+Zalm|Woah`); + this.add(`c|${getName('Zalm')}|Run for the hills!`); }, onFaint() { - this.add(`c|+Zalm|Tfw still no mega weedle`); + this.add(`c|${getName('Zalm')}|Woah`); }, }, zarel: { noCopy: true, onStart() { - this.add(`c|&Zarel|Your mom`); + this.add(`c|${getName('Zarel')}|the melo-p represents PS's battles, and the melo-a represents PS's chatrooms`); + this.add(`c|${getName('Zarel')}|THIS melo-a represents kicking your ass, though`); }, - onFaint() { - this.add(`c|&Zarel|Your mom`); - // message is shown after the "Zarel Fainted!" message - this.add('message', 'Zarel used your mom!'); + }, + zodiax: { + noCopy: true, + onStart(source) { + this.add(`c|${getName('Zodiax')}|Zodiax is here to Zodihax`); + + // Easter Egg + const activeMon = this.toID( + source.side.foe.active[0].illusion ? source.side.foe.active[0].illusion.name : source.side.foe.active[0].name + ); + if (activeMon === 'aeonic') { + this.add(`c|${getName('Zodiax')}|Happy Birthday Aeonic`); + this.add(`c|${getName('Aeonic')}|THIS JOKE IS AS BORING AS YOU ARE`); + } + }, + onSwitchOut() { + this.add(`c|${getName('Zodiax')}|Don't worry I'll be back again`); + }, + onFaint(pokemon) { + const name = pokemon.side.foe.name; + this.add(`c|${getName('Zodiax')}|${name}, Why would you hurt this poor little pompombirb :(`); }, }, zyguser: { noCopy: true, onStart() { - this.add(`c|+Zyg|/me sighs`); + this.add(`c|${getName('Zyg')}|Free Swirlyder.`); }, onSwitchOut() { - this.add(`c|+Zyg|/me sighs`); + this.add(`c|${getName('Zyg')}|/me sighs... what is there to say?`); }, onFaint() { - this.add(`c|+Zyg|Brexit means Brexit`); + this.add(`c|${getName('Zyg')}|At least I have a tier.`); }, }, - // boostreplacement condition for nui's zmove - boostreplacement: { - // this is a side condition - name: 'boostreplacement', - onStart(side, source) { - this.effectData.position = source.position; + // Heavy Hailstorm status support for Alpha + heavyhailstorm: { + name: 'HeavyHailstorm', + effectType: 'Weather', + duration: 3, + onTryMovePriority: 1, + onTryMove(attacker, defender, move) { + if (move.type === 'Steel' && move.category !== 'Status') { + this.debug('Heavy Hailstorm Steel suppress'); + this.add('-message', 'The hail suppressed the move!'); + this.add('-fail', attacker, move, '[from] Heavy Hailstorm'); + this.attrLastMove('[still]'); + return null; + } }, - onSwitchInPriority: 1, - onSwitchIn(target) { - if (!target.fainted && target.position === this.effectData.position) { - this.boost({def: 1, spd: 1}); - target.side.removeSideCondition('boostreplacement'); + onWeatherModifyDamage(damage, attacker, defender, move) { + if (move.type === 'Ice') { + this.debug('Heavy Hailstorm ice boost'); + return this.chainModify(1.5); + } + }, + onStart(battle, source, effect) { + this.add('-weather', 'Heavy Hailstorm'); + this.effectData.source = source; + this.add('-message', 'The hail became extremely chilling!'); + }, + onModifyMove(move, pokemon, target) { + if (!this.field.isWeather('heavyhailstorm')) return; + if (move.category !== "Status") { + this.debug('Adding Heavy Hailstorm freeze'); + if (!move.secondaries) move.secondaries = []; + for (const secondary of move.secondaries) { + if (secondary.status === 'frz') return; + } + move.secondaries.push({ + chance: 10, + status: 'frz', + }); + } + }, + onAnySetWeather(target, source, weather) { + if (this.field.getWeather().id === 'heavyhailstorm' && !STRONG_WEATHERS.includes(weather.id)) return false; + }, + onResidualOrder: 1, + onResidual() { + this.add('-weather', 'Heavy Hailstorm', '[upkeep]'); + this.add('-message', 'Hail is crashing down.'); + if (this.field.isWeather('heavyhailstorm')) this.eachEvent('Weather'); + }, + onWeather(target, source, effect) { + if (target.side === this.effectData.source.side) return; + // Hail is stronger from Heavy Hailstorm + if (!target.hasType('Ice')) this.damage(target.baseMaxhp / 8); + }, + onEnd() { + this.add('-weather', 'none'); + this.add('-message', 'The Hail ended.'); + }, + }, + // Forever Winter Hail support for piloswine gripado + winterhail: { + name: 'Winter Hail', + effectType: 'Weather', + duration: 0, + onStart(battle, source, effect) { + if (effect?.effectType === 'Ability') { + this.add('-weather', 'Winter Hail', '[from] ability: ' + effect, '[of] ' + source); + } else { + this.add('-weather', 'Winter Hail'); + } + this.add('-message', 'It became winter!'); + }, + onModifySpe(spe, pokemon) { + if (!pokemon.hasType('Ice')) return this.chainModify(0.5); + }, + onResidualOrder: 1, + onResidual() { + this.add('-weather', 'Winter Hail', '[upkeep]'); + this.add('-message', 'Hail is crashing down.'); + if (this.field.isWeather('winterhail')) this.eachEvent('Weather'); + }, + onWeather(target) { + if (target.hasType('Ice')) return; + this.damage(target.baseMaxhp / 8); + }, + onEnd() { + this.add('-weather', 'none'); + this.add('-message', 'The Hail ended.'); + }, + }, + raindrop: { + name: 'Raindrop', + noCopy: true, + onStart(target) { + this.effectData.layers = 1; + this.effectData.def = 0; + this.effectData.spd = 0; + this.add('-start', target, 'Raindrop'); + this.add('-message', `${target.name} has ${this.effectData.layers} raindrop(s)!`); + const [curDef, curSpD] = [target.boosts.def, target.boosts.spd]; + this.boost({def: 1, spd: 1}, target, target); + if (curDef !== target.boosts.def) this.effectData.def--; + if (curSpD !== target.boosts.spd) this.effectData.spd--; + }, + onRestart(target) { + this.effectData.layers++; + this.add('-start', target, 'Raindrop'); + this.add('-message', `${target.name} has ${this.effectData.layers} raindrop(s)!`); + const curDef = target.boosts.def; + const curSpD = target.boosts.spd; + this.boost({def: 1, spd: 1}, target, target); + if (curDef !== target.boosts.def) this.effectData.def--; + if (curSpD !== target.boosts.spd) this.effectData.spd--; + }, + onEnd(target) { + if (this.effectData.def || this.effectData.spd) { + const boosts: SparseBoostsTable = {}; + if (this.effectData.def) boosts.def = this.effectData.def; + if (this.effectData.spd) boosts.spd = this.effectData.spd; + this.boost(boosts, target, target); + } + this.add('-end', target, 'Raindrop'); + if (this.effectData.def !== this.effectData.layers * -1 || this.effectData.spd !== this.effectData.layers * -1) { + this.hint("Raindrop keeps track of how many times it successfully altered each stat individually."); } }, }, - // Prevents glitch out from running more than once per turn per pokemon & boosts base power - glitchout: { - name: "Glitch Out", + // Custom status for A Quag To The Past's signature move + bounty: { + name: 'bounty', + effectType: 'Status', + onStart(target, source, sourceEffect) { + if (sourceEffect.effectType === 'Ability') { + this.add('-start', target, 'bounty', '[from] ability: ' + sourceEffect.name, '[of] ' + source); + } else { + this.add('-start', target, 'bounty'); + } + }, + onSwitchIn(pokemon) { + if (pokemon.status === 'bounty') { + this.add('-start', pokemon, 'bounty'); + } + }, + onFaint(target, source, effect) { + if (effect.effectType !== 'Move') return; + if (source) { + this.add('-activate', target, 'ability: Bounty'); + this.boost({atk: 1, def: 1, spa: 1, spd: 1, spe: 1}, source, target, effect); + } + }, + }, + // Brilliant Condition for Arcticblast + brilliant: { + name: 'Brilliant', + duration: 5, + onStart(pokemon) { + this.add('-start', pokemon, 'Brilliant'); + }, + onModifyAtk() { + return this.chainModify(1.5); + }, + onModifyDef() { + return this.chainModify(1.5); + }, + onModifySpA() { + return this.chainModify(1.5); + }, + onModifySpD() { + return this.chainModify(1.5); + }, + onModifySpe() { + return this.chainModify(1.5); + }, + onUpdate(pokemon) { + if (pokemon.volatiles['perishsong']) pokemon.removeVolatile('perishsong'); + }, + onTryAddVolatile(status) { + if (status.id === 'perishsong') return null; + }, + onResidualOrder: 7, + onResidual(pokemon) { + this.heal(pokemon.baseMaxhp / 16); + }, + onTrapPokemon(pokemon) { + pokemon.tryTrap(); + }, + onDragOut(pokemon) { + this.add('-activate', pokemon, 'move: Ingrain'); + return null; + }, + onEnd(pokemon) { + this.add('-end', pokemon, 'Brilliant'); + }, + }, + // Custom status for HoeenHero's move + stormsurge: { + name: "Storm Surge", + duration: 2, + durationCallback(target, source, effect) { + const windSpeeds = [65, 85, 95, 115, 140]; + return windSpeeds.indexOf((effect as ActiveMove).basePower) + 2; + }, + onStart(targetSide) { + this.add('-sidestart', targetSide, 'Storm Surge'); + this.add('-message', `Storm Surge flooded the afflicted side of the battlefield!`); + }, + onEnd(targetSide) { + this.add('-sideend', targetSide, 'Storm Surge'); + this.add('-message', 'The Storm Surge receded.'); + }, + onModifySpe() { + return this.chainModify(0.25); + }, + }, + // For ravioliqueen + haunting: { + name: 'Haunting', + onTrapPokemon(pokemon) { + pokemon.tryTrap(); + }, + onStart(target) { + this.add('-start', target, 'Haunting'); + }, + onResidualOrder: 11, + onResidual(pokemon) { + this.damage(pokemon.baseMaxhp / 8); + }, + onEnd(pokemon) { + this.add('-end', pokemon, 'Haunting'); + }, + }, + // for pants' move + givewistfulthinking: { duration: 1, - onTryHit(target, source, move) { - if (move.basePower) { - move.basePower += 20; - this.debug('glitch out base power boost'); + onSwitchInPriority: 1, + onSwitchIn(pokemon) { + pokemon.addVolatile('wistfulthinking'); + }, + }, + // boost for LittEleven's move + nexthuntcheck: { + duration: 1, + onStart(pokemon) { + this.add('-singleturn', pokemon, 'move: /nexthunt'); + }, + onHit(pokemon, source, move) { + if (move.category !== 'Status') { + pokemon.volatiles['nexthuntcheck'].lostFocus = true; } }, }, - // Modified type setup for arceus - arceus: { - inherit: true, - onType(types, pokemon) { - if (pokemon.transformed) return types; - let type: string | undefined = 'Normal'; - if (pokemon.ability === 'multitype' || pokemon.ability === 'logia') { - type = pokemon.getItem().onPlate; - if (!type) { - type = 'Normal'; + // For Gmars' Effects + minior: { + noCopy: true, + name: 'Minior', + // Special Forme Effects + onBeforeMove(pokemon) { + if (pokemon.set.shiny) return; + if (pokemon.species.id === "miniorviolet") { + this.add(`${getName("GMars")} is thinking...`); + if (this.randomChance(1, 3)) { + this.add('cant', pokemon, 'ability: Truant'); + return false; } } - return [type]; + }, + onSwitchIn(pokemon) { + if (pokemon.set.shiny) return; + if (pokemon.species.id === 'miniorindigo') { + this.boost({atk: 1, spa: 1}, pokemon.side.foe.active[0]); + } else if (pokemon.species.id === 'miniorgreen') { + this.boost({atk: 1}, pokemon); + } + }, + onBoost(boost, target, source, effect) { + if (target.set.shiny) return; + if (source && target === source) return; + if (target.species.id !== 'miniorblue') return; + let showMsg = false; + let i: BoostName; + for (i in boost) { + if (boost[i]! < 0) { + delete boost[i]; + showMsg = true; + } + } + if (showMsg && !(effect as ActiveMove).secondaries && effect.id !== 'octolock') { + this.add("-fail", target, "unboost", "[from] ability: Minior-Blue", "[of] " + target); + } + }, + onFoeTryMove(target, source, move) { + if (move.id === 'haze' && target.species.id === 'miniorblue' && !target.set.shiny) { + move.onHitField = function (this: Battle) { + this.add('-clearallboost'); + for (const pokemon of this.getAllActive()) { + if (pokemon.species.id === 'miniorblue') continue; + pokemon.clearBoosts(); + } + }.bind(this); + return; + } + const dazzlingHolder = this.effectData.target; + if (!dazzlingHolder.set.shiny && dazzlingHolder.species.id !== 'minior') return; + const targetAllExceptions = ['perishsong', 'flowershield', 'rototiller']; + if (move.target === 'foeSide' || (move.target === 'all' && !targetAllExceptions.includes(move.id))) { + return; + } + + if ((source.side === dazzlingHolder.side || move.target === 'all') && move.priority > 0.1) { + this.attrLastMove('[still]'); + this.add('message', 'Minior dazzles!'); + this.add('cant', target, move, '[of] ' + dazzlingHolder); + return false; + } }, }, - // weight doubling volatile for trickster - weightdoubler: { - name: "Weight Doubler", - noCopy: true, - onStart(pokemon) { - this.add('-message', `${pokemon.illusion ? pokemon.illusion.name : pokemon.name}'s weight has doubled.`); - pokemon.weighthg *= 2; + // modified paralysis for Inversion Terrain + par: { + name: 'par', + effectType: 'Status', + onStart(target, source, sourceEffect) { + if (sourceEffect && sourceEffect.effectType === 'Ability') { + this.add('-status', target, 'par', '[from] ability: ' + sourceEffect.name, '[of] ' + source); + } else { + this.add('-status', target, 'par'); + } + }, + onModifySpe(spe, pokemon) { + if (pokemon.hasAbility('quickfeet')) return; + if (this.field.isTerrain('inversionterrain') && pokemon.isGrounded()) { + return this.chainModify(2); + } + return this.chainModify(0.5); + }, + onBeforeMovePriority: 1, + onBeforeMove(pokemon) { + if (this.randomChance(1, 4)) { + this.add('cant', pokemon, 'par'); + return false; + } }, }, - // Gooey volatile for Decem's move - gooey: { - name: "Gooey", - onStart(pokemon, source) { - this.add('-start', pokemon, 'Gooey', '[of] ' + source); - this.add('-message', `${pokemon.illusion ? pokemon.illusion.name : pokemon.name} was covered in corrosive goo!`); - }, - onResidualOrder: 10, - onResidual(pokemon) { - this.damage(pokemon.baseMaxhp / 6); - this.add('-message', `${pokemon.illusion ? pokemon.illusion.name : pokemon.name} was damaged by the corrosive goo!`); + bigstormcomingmod: { + name: "Big Storm Coming Mod", + duration: 1, + onBasePower() { + return this.chainModify([0x4CC, 0x1000]); }, }, - // Custom Acid Rain weather for Pirate Princess - acidrain: { - name: 'Acid Rain', + + // condition used for brouha's ability + turbulence: { + name: 'Turbulence', + effectType: 'Weather', + duration: 0, + onModifyDefPriority: 10, + onModifyDef(def, pokemon) { + if (pokemon.hasType('Flying') && this.field.isWeather('turbulence')) { + return this.modify(def, 1.5); + } + }, + onStart(battle, source, effect) { + this.add('-weather', 'DeltaStream', '[from] ability: ' + effect, '[of] ' + source); + }, + onResidualOrder: 1, + onResidual() { + this.add('-weather', 'DeltaStream', '[upkeep]'); + this.eachEvent('Weather'); + }, + onWeather(target) { + if (!target.hasType('Flying')) this.damage(target.baseMaxhp * 0.06); + if (this.sides.some(side => Object.keys(side.sideConditions).length)) { + this.add(`-message`, 'The Turbulence blew away the hazards on both sides!'); + } + if (this.field.terrain) { + this.add(`-message`, 'The Turbulence blew away the terrain!'); + } + const silentRemove = ['reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'shiftingrocks', 'ferrofluid']; + for (const side of this.sides) { + const keys = Object.keys(side.sideConditions); + for (const key of keys) { + side.removeSideCondition(key); + if (!silentRemove.includes(key)) { + this.add('-sideend', target.side, this.dex.getEffect(key).name, '[from] ability: Turbulence'); + } + } + } + this.field.clearTerrain(); + }, + onEnd() { + this.add('-weather', 'none'); + }, + }, + // Modded rain dance for Kev's ability + raindance: { + name: 'RainDance', effectType: 'Weather', duration: 5, - onModifySpDPriority: 10, - onModifySpD(spd, pokemon) { - if (pokemon.hasType('Poison') && this.field.isWeather('acidrain')) { - return this.modify(spd, 1.5); + durationCallback(source) { + let newDuration = 5; + let boostNum = 0; + if (source?.hasItem('damprock')) { + newDuration = 8; + } + if (source?.hasAbility('kingofatlantis')) { + for (const teammate of source.side.pokemon) { + if (teammate.hasType('Water') && teammate !== source) { + boostNum++; + } + } + } + return newDuration + boostNum; + }, + onWeatherModifyDamage(damage, attacker, defender, move) { + if (defender.hasItem('utilityumbrella')) return; + if (move.type === 'Water') { + this.debug('rain water boost'); + return this.chainModify(1.5); + } + if (move.type === 'Fire') { + this.debug('rain fire suppress'); + return this.chainModify(0.5); } }, onStart(battle, source, effect) { if (effect?.effectType === 'Ability') { if (this.gen <= 5) this.effectData.duration = 0; - this.add('-weather', 'AcidRain', '[from] ability: ' + effect, '[of] ' + source); + this.add('-weather', 'RainDance', '[from] ability: ' + effect, '[of] ' + source); } else { - this.add('-weather', 'AcidRain'); + this.add('-weather', 'RainDance'); } - this.add('-message', 'Acid Rain began to fall.'); }, onResidualOrder: 1, onResidual() { - this.add('-weather', 'AcidRain', '[upkeep]'); - if (this.field.isWeather('acidrain')) this.eachEvent('Weather'); - }, - onWeather(target) { - if (target.hasType('Poison')) return; - this.damage(target.baseMaxhp / 16); - }, - onModifyMovePriority: -5, - onModifyMove(move) { - if (!move.ignoreImmunity) move.ignoreImmunity = {}; - if (move.ignoreImmunity !== true) { - move.ignoreImmunity['Steel'] = true; - } + this.add('-weather', 'RainDance', '[upkeep]'); + this.eachEvent('Weather'); }, onEnd() { this.add('-weather', 'none'); - this.add('-message', 'The Acid Rain subsided.'); }, }, - // Custom effect for Rage's multihit - enrageeeeed: { - name: "Enrageeeeed", - onStart(pokemon, source) { - this.add('-message', `${pokemon.illusion ? pokemon.illusion.name : pokemon.name}'s next attack will hit multiple times!`); - }, - onPrepareHit(source, target, move) { - // beat up relies on its multihit being the number of valid allies - if (move.category !== 'Status' && move.id !== 'beatup') { - move.multihit = [2, 5]; - move.basePower = 25; - this.effectData.usedup = true; - } - }, - onAfterMove(pokemon, source) { - if (this.effectData.usedup) pokemon.removeVolatile('enrageeeeed'); - }, - }, - // Custom effect for Yuki - cutietrap: { - name: "Cutie Trap", - duration: 5, - noCopy: true, - onStart(pokemon, source) { - if (!this.runEvent('Attract', pokemon, source)) { - this.debug('Attract event failed'); - return false; - } - this.add('-start', pokemon, 'Attract', '[from] move: Cutie Trap', '[of] ' + source); - this.add('-message', `${pokemon.illusion ? pokemon.illusion.name : pokemon.name} was trapped by love!`); - }, - onBeforeMovePriority: 2, - onBeforeMove(pokemon) { - this.add('-activate', pokemon, 'move: Attract', '[of] ' + this.effectData.source); - if (this.randomChance(1, 2)) { - this.add('cant', pokemon, 'Attract'); - return false; - } - }, - onTrapPokemon(pokemon) { - pokemon.tryTrap(); - }, - onEnd(pokemon) { - this.add('-end', pokemon, 'Attract', '[silent]'); - this.add('-message', `${pokemon.illusion ? pokemon.illusion.name : pokemon.name} is no longer trapped by love.`); - }, - }, - // Modified hail for Yuki - hail: { - inherit: true, - onStart(battle, source, effect) { - if (effect?.effectType === 'Ability') { - if (this.gen <= 5 || effect.id === 'snowstorm') this.effectData.duration = 0; - this.add('-weather', 'Hail', '[from] ability: ' + effect, '[of] ' + source); - } else { - this.add('-weather', 'Hail'); - } - }, - }, - // Special volatile that is applied to pokemon using a custom move with the effects of baton pass so that boosts/volatiles are shown on client. - batonpasshelper: { - name: "Baton Pass Helper", - duration: 1, - onSwitchInPriority: 1000, - onSwitchIn(target) { - let boost: BoostName; - for (boost in target.boosts) { - if (target.boosts[boost]) this.add('-boost', target, boost, target.boosts[boost], '[silent]'); - } - for (const v in target.volatiles) { - if (v !== this.toID(target.name) && v !== 'batonpasshelper') this.add('-start', target, target.volatiles[v].id); - } - }, - }, - // Modded hazard moves to fail when Prismatic terrain is active + // Modded hazard moves to fail when Wave terrain is active auroraveil: { name: "Aurora Veil", duration: 5, - durationCallback(target, source, effect) { + durationCallback(target, source) { if (source?.hasItem('lightclay')) { return 8; } @@ -1868,8 +2268,8 @@ export const Conditions: {[k: string]: ModdedConditionData} = { } }, onStart(side) { - if (this.field.isTerrain('prismaticterrain')) { - this.add('-message', `Prismatic Terrain prevented Aurora Veil from starting!`); + if (this.field.isTerrain('waveterrain')) { + this.add('-message', `Wave Terrain prevented Aurora Veil from starting!`); return null; } this.add('-sidestart', side, 'move: Aurora Veil'); @@ -1883,7 +2283,7 @@ export const Conditions: {[k: string]: ModdedConditionData} = { lightscreen: { name: "Light Screen", duration: 5, - durationCallback(target, source, effect) { + durationCallback(target, source) { if (source?.hasItem('lightclay')) { return 8; } @@ -1899,8 +2299,8 @@ export const Conditions: {[k: string]: ModdedConditionData} = { } }, onStart(side) { - if (this.field.isTerrain('prismaticterrain')) { - this.add('-message', `Prismatic Terrain prevented Light Screen from starting!`); + if (this.field.isTerrain('waveterrain')) { + this.add('-message', `Wave Terrain prevented Light Screen from starting!`); return null; } this.add('-sidestart', side, 'move: Light Screen'); @@ -1931,8 +2331,8 @@ export const Conditions: {[k: string]: ModdedConditionData} = { } }, onStart(side) { - if (this.field.isTerrain('prismaticterrain')) { - this.add('-message', `Prismatic Terrain prevented Mist from starting!`); + if (this.field.isTerrain('waveterrain')) { + this.add('-message', `Wave Terrain prevented Mist from starting!`); return null; } this.add('-sidestart', side, 'move: Mist'); @@ -1946,7 +2346,7 @@ export const Conditions: {[k: string]: ModdedConditionData} = { reflect: { name: "Reflect", duration: 5, - durationCallback(target, source, effect) { + durationCallback(target, source) { if (source?.hasItem('lightclay')) { return 8; } @@ -1962,8 +2362,8 @@ export const Conditions: {[k: string]: ModdedConditionData} = { } }, onStart(side) { - if (this.field.isTerrain('prismaticterrain')) { - this.add('-message', `Prismatic Terrain prevented Reflect from starting!`); + if (this.field.isTerrain('waveterrain')) { + this.add('-message', `Wave Terrain prevented Reflect from starting!`); return null; } this.add('-sidestart', side, 'Reflect'); @@ -2003,8 +2403,8 @@ export const Conditions: {[k: string]: ModdedConditionData} = { } }, onStart(side) { - if (this.field.isTerrain('prismaticterrain')) { - this.add('-message', `Prismatic Terrain prevented Safeguard from starting!`); + if (this.field.isTerrain('waveterrain')) { + this.add('-message', `Wave Terrain prevented Safeguard from starting!`); return null; } this.add('-sidestart', side, 'move: Safeguard'); @@ -2015,11 +2415,31 @@ export const Conditions: {[k: string]: ModdedConditionData} = { this.add('-sideend', side, 'Safeguard'); }, }, + gmaxsteelsurge: { + onStart(side) { + if (this.field.isTerrain('waveterrain')) { + this.add('-message', `Wave Terrain prevented Steel Spikes from starting!`); + return null; + } + this.add('-sidestart', side, 'move: G-Max Steelsurge'); + }, + onSwitchIn(pokemon) { + if (pokemon.hasItem('heavydutyboots')) return; + // Ice Face and Disguise correctly get typed damage from Stealth Rock + // because Stealth Rock bypasses Substitute. + // They don't get typed damage from Steelsurge because Steelsurge doesn't, + // so we're going to test the damage of a Steel-type Stealth Rock instead. + const steelHazard = this.dex.getActiveMove('Stealth Rock'); + steelHazard.type = 'Steel'; + const typeMod = this.clampIntRange(pokemon.runEffectiveness(steelHazard), -6, 6); + this.damage(pokemon.maxhp * Math.pow(2, typeMod) / 8); + }, + }, spikes: { name: "Spikes", onStart(side) { - if (this.field.isTerrain('prismaticterrain')) { - this.add('-message', `Prismatic Terrain prevented Spikes from starting!`); + if (this.field.isTerrain('waveterrain')) { + this.add('-message', `Wave Terrain prevented Spikes from starting!`); return null; } this.effectData.layers = 1; @@ -2032,6 +2452,7 @@ export const Conditions: {[k: string]: ModdedConditionData} = { }, onSwitchIn(pokemon) { if (!pokemon.isGrounded()) return; + if (pokemon.hasItem('heavydutyboots')) return; const damageAmounts = [0, 3, 4, 6]; // 1/8, 1/6, 1/4 this.damage(damageAmounts[this.effectData.layers] * pokemon.maxhp / 24); }, @@ -2039,13 +2460,14 @@ export const Conditions: {[k: string]: ModdedConditionData} = { stealthrock: { name: "Stealth Rock", onStart(side) { - if (this.field.isTerrain('prismaticterrain')) { - this.add('-message', `Prismatic Terrain prevented Stealth Rock from starting!`); + if (this.field.isTerrain('waveterrain')) { + this.add('-message', `Wave Terrain prevented Stealth Rock from starting!`); return null; } this.add('-sidestart', side, 'move: Stealth Rock'); }, onSwitchIn(pokemon) { + if (pokemon.hasItem('heavydutyboots')) return; const typeMod = this.clampIntRange(pokemon.runEffectiveness(this.dex.getActiveMove('stealthrock')), -6, 6); this.damage(pokemon.maxhp * Math.pow(2, typeMod) / 8); }, @@ -2053,14 +2475,15 @@ export const Conditions: {[k: string]: ModdedConditionData} = { stickyweb: { name: "Sticky Web", onStart(side) { - if (this.field.isTerrain('prismaticterrain')) { - this.add('-message', `Prismatic Terrain prevented Sticky Web from starting!`); + if (this.field.isTerrain('waveterrain')) { + this.add('-message', `Wave Terrain prevented Sticky Web from starting!`); return null; } this.add('-sidestart', side, 'move: Sticky Web'); }, onSwitchIn(pokemon) { if (!pokemon.isGrounded()) return; + if (pokemon.hasItem('heavydutyboots')) return; this.add('-activate', pokemon, 'move: Sticky Web'); this.boost({spe: -1}, pokemon, pokemon.side.foe.active[0], this.dex.getActiveMove('stickyweb')); }, @@ -2068,8 +2491,8 @@ export const Conditions: {[k: string]: ModdedConditionData} = { toxicspikes: { name: "Toxic Spikes", onStart(side) { - if (this.field.isTerrain('prismaticterrain')) { - this.add('-message', `Prismatic Terrain prevented Toxic Spikes from starting!`); + if (this.field.isTerrain('waveterrain')) { + this.add('-message', `Wave Terrain prevented Toxic Spikes from starting!`); return null; } this.add('-sidestart', side, 'move: Toxic Spikes'); @@ -2085,7 +2508,7 @@ export const Conditions: {[k: string]: ModdedConditionData} = { if (pokemon.hasType('Poison')) { this.add('-sideend', pokemon.side, 'move: Toxic Spikes', '[of] ' + pokemon); pokemon.side.removeSideCondition('toxicspikes'); - } else if (pokemon.hasType('Steel')) { + } else if (pokemon.hasType('Steel') || pokemon.hasItem('heavydutyboots')) { return; } else if (this.effectData.layers >= 2) { pokemon.trySetStatus('tox', pokemon.side.foe.active[0]); @@ -2094,4 +2517,46 @@ export const Conditions: {[k: string]: ModdedConditionData} = { } }, }, + frz: { + inherit: true, + onHit(target, source, move) { + if (move.thawsTarget || move.type === 'Fire' && move.category !== 'Status') { + target.cureStatus(); + if (move.id === 'randomscreaming') { + this.add(`c|${getName('Gimmick')}|Give me some more paaain, baaaby`); + } + } + }, + }, + // No, you're not dynamaxing. + dynamax: { + inherit: true, + onStart(pokemon) { + pokemon.removeVolatile('minimize'); + pokemon.removeVolatile('substitute'); + if (pokemon.volatiles['torment']) { + delete pokemon.volatiles['torment']; + this.add('-end', pokemon, 'Torment', '[silent]'); + } + if (['cramorantgulping', 'cramorantgorging'].includes(pokemon.species.id) && !pokemon.transformed) { + pokemon.formeChange('cramorant'); + } + this.add('-start', pokemon, 'Dynamax'); + if (pokemon.gigantamax) this.add('-formechange', pokemon, pokemon.species.name + '-Gmax'); + if (pokemon.baseSpecies.name !== 'Shedinja') { + // Changes based on dynamax level, 2 is max (at LVL 10) + const ratio = this.format.id.startsWith('gen8doublesou') ? 1.5 : 2; + + pokemon.maxhp = Math.floor(pokemon.maxhp * ratio); + pokemon.hp = Math.floor(pokemon.hp * ratio); + + this.add('-heal', pokemon, pokemon.getHealth, '[silent]'); + } + this.add('-message', 'Dynamax is not allowed in SSB4, and custom challenges or tournaments won\'t save you from that fact.'); + pokemon.removeVolatile('dynamax'); + this.queue.cancelMove(pokemon); + // Actually its to prvent the user from using a Max Move in case of a crash. But this is funnier. + this.hint(`Your move was aborted due to dynamax. Cheater.`); + }, + }, }; diff --git a/data/mods/ssb/items.ts b/data/mods/ssb/items.ts index 239aa14825..d077f98eae 100644 --- a/data/mods/ssb/items.ts +++ b/data/mods/ssb/items.ts @@ -1,147 +1,69 @@ export const Items: {[k: string]: ModdedItemData} = { - // Aeonic - noseiumz: { - name: "Noseium Z", + // Alpha + caioniumz: { + name: "Caionium Z", isNonstandard: "Custom", onTakeItem: false, - zMove: "Shitpost", - zMoveFrom: "Fissure", - itemUser: ["Nosepass"], - gen: 7, - desc: "If held by a Nosepass with Fissure, it can use Shitpost.", + zMove: "Blistering Ice Age", + zMoveFrom: "Blizzard", + itemUser: ["Aurorus"], + gen: 8, + desc: "If held by an Aurorus with Blizzard, it can use Blistering Ice Age.", }, - // E4 Flint - magmarizer: { - inherit: true, - megaStone: "Steelix-Mega", - megaEvolves: "Steelix", - onTakeItem(item, source) { - if (item.megaEvolves === source.baseSpecies.baseSpecies) return false; - return true; + + // A Quag To The Past + quagniumz: { + name: "Quagnium Z", + isNonstandard: "Custom", + onTakeItem: false, + zMove: "Bounty Place", + zMoveFrom: "Scorching Sands", + itemUser: ["Quagsire"], + gen: 8, + desc: "If held by a Quagsire with Scorching Sands, it can use Bounty Place.", + }, + + // Kalalokki + kalalokkiumz: { + name: "Kalalokkium Z", + isNonstandard: "Custom", + onTakeItem: false, + zMove: "Gaelstrom", + zMoveFrom: "Blackbird", + itemUser: ["Wingull"], + gen: 8, + desc: "If held by a Wingull with Blackbird, it can use Gaelstrom.", + }, + + // Robb576 + modium6z: { + name: "Modium-6 Z", + isNonstandard: "Custom", + onTakeItem: false, + zMove: "Integer Overflow", + zMoveFrom: "Photon Geyser", + itemUser: ["Necrozma-Ultra"], + gen: 8, + desc: "If held by a Robb576 with Photon Geyser, it can use Integer Overflow.", + }, + + // xJoelituh + rarebone: { + onModifyAtkPriority: 2, + onModifyAtk() { + return this.chainModify(1.5); }, - fling: undefined, // Cannot be flung now since its a mega stone - desc: "If held by a Steelix, this item allows it to Mega Evolve in battle.", - }, - // FOMG - astleyiumz: { - name: "Astleyium Z", - isNonstandard: "Custom", - onTakeItem: false, - zMove: "Rickrollout", - zMoveFrom: "Rock Slide", - itemUser: ["Golem"], - gen: 7, - desc: "If held by a Golem with Rock Slide, it can use Rickrollout.", - }, - // inactive - dusknoiriumz: { - name: "Dusknoirium Z", - isNonstandard: "Custom", - onTakeItem: false, - zMove: "Petrifying Gaze", - zMoveFrom: "Mean Look", - itemUser: ["Dusknoir"], - gen: 7, - desc: "If held by a Dusknoir with Mean Look, it can use Petrifying Gaze.", - }, - // Kris - thunderstone: { - inherit: true, - // @ts-ignore - megaStone: ["Rotom-Wash", "Rotom-Mow", "Rotom-Heat", "Rotom-Frost", "Rotom-Fan"], - megaEvolves: "Rotom", - onTakeItem(item, source) { - if (item.megaEvolves === source.baseSpecies.baseSpecies) return false; - return true; + onModifyDefPriority: 2, + onModifyDef() { + return this.chainModify(1.5); }, - fling: undefined, // Cannot be flung now since its a mega stone - desc: "If held by a Rotom, this item allows it to Mega Evolve in battle.", - shortDesc: "If held by a Rotom, this item allows it to Mega Evolve in battle.", - }, - // MajorBowman - victiniumz: { - name: "Victinium Z", - isNonstandard: "Custom", - onTakeItem: false, - zMove: "Blaze of Glory", - zMoveFrom: "V-create", - itemUser: ["Victini"], - gen: 7, - desc: "If held by a Victini with V-create, it can use Blaze of Glory.", - }, - // Pohjis - marowakiumz: { - name: "Marowakium Z", - isNonstandard: "Custom", - onTakeItem: false, - zMove: "Great Eqake", - zMoveFrom: "Earthquake", - itemUser: ["Marowak"], - gen: 7, - desc: "If held by a Marowak with Earthquake, it can use Great Eqake.", - }, - // SamJo - thicciniumz: { - name: "Thiccinium Z", - isNonstandard: "Custom", - onTakeItem: false, - zMove: "Extra T h i c c", - zMoveFrom: "Thicc", - itemUser: ["Mamoswine"], - gen: 7, - desc: "If held by a Mamoswine with Thicc, it can use Extra T h i c c.", - }, - // Schiavetto - mariahcariumz: { - name: "Mariahcarium Z", - isNonstandard: "Custom", - onTakeItem: false, - zMove: "Plurshift", - zMoveFrom: "Poison Jab", - itemUser: ["Scolipede"], - gen: 7, - desc: "If held by a Scolipede with Poison Jab, it can use Plurshift.", - }, - // Snaquaza - fakeclaimiumz: { - name: "Fakeclaimium Z", - isNonstandard: "Custom", - onTakeItem: false, - zMove: "Fake Claim", - // @ts-ignore hack for Snaquaza's Z move. - zMoveFrom: ["Brave Bird", "Superpower", "Sucker Punch", "Flamethrower", "Ice Beam", "Thunderbolt"], - itemUser: ["Honchkrow"], - gen: 7, - desc: "If held by a Honchkrow, it can use Fake Claim.", - }, - // The Immortal - buzzniumz: { - name: "Buzznium Z", - isNonstandard: "Custom", - onTakeItem: false, - zMove: "Ultra Succ", - zMoveFrom: "Drain Punch", - itemUser: ["Buzzwole"], - gen: 7, - desc: "If held by a Buzzwole with Drain Punch, it can use Ultra Succ.", - }, - // Teclis - darkrainiumz: { - name: "Darkrainium Z", - isNonstandard: "Custom", - onTakeItem: false, - zMove: "Absolute Configuration", - zMoveFrom: "Dark Void", - itemUser: ["Darkrai"], - gen: 7, - desc: "If held by a Darkrai with Dark Void, it can use Absolute Configuration.", - }, - // XpRienzo - charcoal: { - inherit: true, - zMove: "Bleh Flame", - zMoveFrom: "Blue Flare", - itemUser: ["Reshiram"], - desc: "Fire-type attacks have 1.2x power. Reshiram with Blue Flare can use Bleh Flame.", + onModifySpDPriority: 2, + onModifySpD() { + return this.chainModify(1.5); + }, + gen: 4, + name: "Rare Bone", + desc: "1.5x to Attack, Defense, and Special Defense.", + shortDesc: "1.5x to Attack, Defense, and Special Defense.", }, }; diff --git a/data/mods/ssb/moves.ts b/data/mods/ssb/moves.ts index 83bcbaa894..0579423d4e 100644 --- a/data/mods/ssb/moves.ts +++ b/data/mods/ssb/moves.ts @@ -1,20 +1,77 @@ -// Used Snaquaza's move -import {RandomStaffBrosTeams} from './random-teams'; -import {Pokemon} from '../../../sim/pokemon'; +import {getName} from './conditions'; +import {changeSet, changeMoves} from "./abilities"; +import {ssbSets} from "./random-teams"; + + +/************************************************** + * Generated by the following script: + Object.keys(Dex.data.Moves).filter(id => { + const move = Dex.data.Moves[id]; + if (!move || move.isZ || move.isNonstandard || move.isMax || move.noSketch) return false; + return [ + 'agility', 'aromatherapy', 'auroraveil', 'autotomize', 'banefulbunker', 'batonpass', 'bellydrum', 'bulkup', 'calmmind', 'clangoroussoul', 'coil', 'cottonguard', 'courtchange', 'curse', 'defog', 'destinybond', 'detect', 'disable', 'dragondance', 'drainingkiss', 'encore', 'extremeevoboost', 'geomancy', 'glare', 'haze', 'healbell', 'healingwish', 'healorder', 'heartswap', 'honeclaws', 'kingsshield', 'irondefense', 'leechseed', 'lightscreen', 'lovelykiss', 'magiccoat', 'maxguard', 'memento', 'milkdrink', 'moonlight', 'morningsun', 'nastyplot', 'naturesmadness', 'noretreat', 'obstruct', 'painsplit', 'partingshot', 'perishsong', 'protect', 'quiverdance', 'recover', 'reflect', 'reflecttype', 'rest', 'roar', 'rockpolish', 'roost', 'shellsmash', 'shiftgear', 'slackoff', 'sleeppowder', 'sleeptalk', 'softboiled', 'spikes', 'spikyshield', 'spore', 'stealthrock', 'stickyweb', 'strengthsap', 'substitute', 'switcheroo', 'swordsdance', 'synthesis', 'tailglow', 'tailwind', 'taunt', 'thunderwave', 'toxic', 'toxicspikes', 'transform', 'trick', 'whirlwind', 'willowisp', 'wish', 'yawn', + ].includes(id) || ( + move.basePower > 75 && ![ + 'accelerock', 'acrobatics', 'aquajet', 'avalanche', 'bonemerang', 'bouncybubble', 'bulletpunch', 'bulletseed', 'buzzybuzz', 'circlethrow', 'clearsmog', 'doubleironbash', 'dragondarts', 'dragontail', 'endeavor', 'facade', 'firefang', 'flipturn', 'freezedry', 'frustration', 'geargrind', 'grassknot', 'gyroball', 'hex', 'icefang', 'iceshard', 'iciclespear', 'knockoff', 'lowkick', 'machpunch', 'nightshade', 'nuzzle', 'pikapapow', 'psychocut', 'pursuit', 'quickattack', 'rapidspin', 'return', 'rockblast', 'scorchingsands', 'seismictoss', 'shadowclaw', 'shadowsneak', 'sizzlyslide', 'storedpower', 'stormthrow', 'suckerpunch', 'superfang', 'surgingstrikes', 'tailslap', 'tripleaxel', 'uturn', 'veeveevolley', 'voltswitch', 'watershuriken', 'weatherball', + ].includes(id) + ); + }); + **************************************************/ +const USEFUL_MOVES = [ + "agility", "anchorshot", "appleacid", "aquatail", "aromatherapy", "attackorder", "aurasphere", "aurawheel", "auroraveil", + "autotomize", "banefulbunker", "batonpass", "behemothbash", "behemothblade", "belch", "bellydrum", "blastburn", "blazekick", + "blizzard", "blueflare", "bodypress", "bodyslam", "boltbeak", "boltstrike", "boomburst", "bounce", "bravebird", "bugbuzz", + "bulkup", "burnup", "calmmind", "clangingscales", "clangoroussoul", "closecombat", "coil", "cottonguard", "courtchange", + "crabhammer", "crosschop", "crunch", "curse", "darkestlariat", "darkpulse", "dazzlinggleam", "defog", "destinybond", "detect", + "dig", "disable", "discharge", "dive", "doomdesire", "doubleedge", "dracometeor", "dragonclaw", "dragondance", "dragonhammer", + "dragonpulse", "dragonrush", "drainingkiss", "dreameater", "drillpeck", "drillrun", "drumbeating", "dynamaxcannon", + "dynamicpunch", "earthpower", "earthquake", "encore", "energyball", "eruption", "eternabeam", "expandingforce", "explosion", + "extrasensory", "extremespeed", "falsesurrender", "fierydance", "fireblast", "firelash", "firepledge", "firstimpression", + "fishiousrend", "flamethrower", "flareblitz", "flashcannon", "fleurcannon", "fly", "flyingpress", "focusblast", "focuspunch", + "foulplay", "freezeshock", "frenzyplant", "fusionbolt", "fusionflare", "futuresight", "gigaimpact", "glare", "grasspledge", + "gravapple", "gunkshot", "hammerarm", "haze", "headcharge", "headsmash", "healbell", "healingwish", "heatwave", + "highhorsepower", "highjumpkick", "honeclaws", "hurricane", "hydrocannon", "hydropump", "hyperbeam", "hypervoice", + "icebeam", "iceburn", "iciclecrash", "inferno", "irondefense", "ironhead", "irontail", "jawlock", "kingsshield", + "lastresort", "lavaplume", "leafblade", "leafstorm", "leechlife", "leechseed", "lightscreen", "liquidation", + "lunge", "magiccoat", "megahorn", "megakick", "megapunch", "memento", "meteorassault", "meteorbeam", "meteormash", + "milkdrink", "mistyexplosion", "moonblast", "moongeistbeam", "moonlight", "morningsun", "muddywater", "multiattack", + "nastyplot", "nightdaze", "noretreat", "obstruct", "outrage", "overdrive", "overheat", "painsplit", "partingshot", + "perishsong", "petalblizzard", "petaldance", "phantomforce", "photongeyser", "plasmafists", "playrough", "poisonjab", + "pollenpuff", "poltergeist", "powergem", "powerwhip", "prismaticlaser", "protect", "psychic", "psychicfangs", "psyshock", + "psystrike", "pyroball", "quiverdance", "recover", "reflect", "reflecttype", "rest", "roar", "rockpolish", "rockwrecker", + "roost", "sacredsword", "scald", "secretsword", "seedbomb", "selfdestruct", "shadowball", "shadowbone", "shellsidearm", + "shellsmash", "shelltrap", "shiftgear", "skullbash", "skyattack", "slackoff", "slam", "sleeppowder", "sleeptalk", + "sludgebomb", "sludgewave", "snipeshot", "softboiled", "solarbeam", "solarblade", "sparklingaria", "spectralthief", + "spikes", "spikyshield", "spiritshackle", "spore", "stealthrock", "steelbeam", "steelroller", "stickyweb", + "stoneedge", "strangesteam", "strength", "strengthsap", "submission", "substitute", "sunsteelstrike", "superpower", + "surf", "switcheroo", "swordsdance", "synthesis", "tailwind", "takedown", "taunt", "thrash", "throatchop", "thunder", + "thunderbolt", "thunderwave", "toxic", "toxicspikes", "transform", "triattack", "trick", "uproar", "vcreate", + "volttackle", "waterfall", "waterpledge", "waterspout", "whirlwind", "wickedblow", "wildcharge", "willowisp", "wish", + "woodhammer", "xscissor", "yawn", "zapcannon", "zenheadbutt", "zingzap", +]; export const Moves: {[k: string]: ModdedMoveData} = { /* // Example - "moveid": { + moveid: { accuracy: 100, // a number or true for always hits basePower: 100, // Not used for Status moves, base power of the move, number category: "Physical", // "Physical", "Special", or "Status" desc: "", // long description shortDesc: "", // short description, shows up in /dt name: "Move Name", + isNonstandard: "Custom", + gen: 8, pp: 10, // unboosted PP count priority: 0, // move priority, -6 -> 6 flags: {}, // Move flags https://github.com/smogon/pokemon-showdown/blob/master/data/moves.js#L1-L27 + onTryMove() { + this.attrLastMove('[still]'); // For custom animations + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Move Name 1', source); + this.add('-anim', source, 'Move Name 2', source); + }, // For custom animations secondary: { status: "tox", chance: 20, @@ -33,303 +90,151 @@ export const Moves: {[k: string]: ModdedMoveData} = { }, */ // Please keep sets organized alphabetically based on staff member name! - // 2xTheTap - noblehowl: { + // Abdelrahman + thetownoutplay: { accuracy: true, basePower: 0, category: "Status", - desc: "Raises the user's Attack by two stages and cures the user of burns, paralysis, and poison. Removes Reflect, Light Screen, Aurora Veil, Safeguard, and Mist from the opponent's side and removes Spikes, Toxic Spikes, Stealth Rock, and Sticky Web from both sides.", - shortDesc: "Raises Attack by 2, clears hazards/user status.", - name: "Noble Howl", + desc: "Sets Trick Room and has 10% chance to burn the opponent.", + shortDesc: "Sets Trick Room. 10% chance to burn.", + name: "The Town Outplay", isNonstandard: "Custom", - pp: 3, - noPPBoosts: true, - priority: 0, + gen: 8, + pp: 5, + priority: -5, + flags: {}, onTryMove() { this.attrLastMove('[still]'); }, onPrepareHit(target, source) { - this.add('-anim', source, 'Howl', source); - this.add('-anim', source, 'Boomburst', source); + this.add('-anim', source, 'Trick Room', target); }, onHit(target, source, move) { - this.boost({atk: 2}, source, source, this.dex.getActiveMove('Noble Howl')); - if (!(['', 'slp', 'frz'].includes(source.status))) { - source.cureStatus(); - } - const removeTarget = [ - 'reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'spikes', 'toxicspikes', 'stealthrock', 'stickyweb', - ]; - const removeAll = ['spikes', 'toxicspikes', 'stealthrock', 'stickyweb']; - for (const targetCondition of removeTarget) { - if (target.side.removeSideCondition(targetCondition)) { - if (!removeAll.includes(targetCondition)) continue; - this.add('-sideend', target.side, this.dex.getEffect(targetCondition).name, '[from] move: Noble Howl', '[of] ' + target); - } - } - for (const sideCondition of removeAll) { - if (source.side.removeSideCondition(sideCondition)) { - this.add('-sideend', source.side, this.dex.getEffect(sideCondition).name, '[from] move: Noble Howl', '[of] ' + source); + if (this.randomChance(1, 10)) { + for (const foe of source.side.foe.active) { + if (!foe || foe.fainted) continue; + foe.trySetStatus('brn', source); } } }, - flags: {mirror: 1, snatch: 1, authentic: 1}, + pseudoWeather: 'trickroom', secondary: null, - target: "normal", - type: "Normal", + target: "self", + type: "Fire", }, - // 5gen - toomuchsaws: { - accuracy: 100, - basePower: 85, - basePowerCallback(pokemon, target, move) { - if (target.newlySwitched) { - return move.basePower * 2; - } - return move.basePower; - }, - category: "Physical", - desc: "Base Power doubles if the foe switches out the turn this move is used.", - shortDesc: "Power doubles if foe switches out.", - name: "Too Much Saws", - isNonstandard: "Custom", - pp: 10, - priority: 0, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Headbutt', target); - }, - flags: {protect: 1, mirror: 1, contact: 1}, - secondary: null, - target: "normal", - type: "Grass", - }, - // Aelita - energyfield: { - accuracy: 100, - basePower: 140, - category: "Special", - desc: "Has a 40% chance to paralyze the target. Lowers the user's Special Attack, Special Defense, and Speed by one stage.", - shortDesc: "40% to paralyze. Lowers user's SpA, SpD, Spe.", - name: "Energy Field", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {protect: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Electro Ball", target); - this.add('-anim', source, "Ion Deluge", target); - }, - self: {boosts: {spa: -1, spd: -1, spe: -1}}, - secondary: { - chance: 40, - status: 'par', - }, - target: "normal", - type: "Electric", - zMove: {basePower: 200}, - }, - // Aeonic - shitpost: { - accuracy: true, - basePower: 0, - category: "Physical", - desc: "50% chance to OHKO the user, 50% chance to OHKO the target.", - shortDesc: "OHKOs user or target (50% chance each).", - name: "Shitpost", - isNonstandard: "Custom", - pp: 1, - priority: 0, - flags: {}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Skill Swap", target); - }, - onHit(target, source) { - let koed: Pokemon; - if (Math.round(this.random())) { - koed = target; - this.add(`c|@Aeonic|What a buncha jokers`); - } else { - koed = source; - this.add(`c|@Aeonic|haha yeah`); - } - this.add('-anim', koed, "Explosion", koed); - koed.faint(); - }, - isZ: "noseiumz", - secondary: null, - target: "normal", - type: "???", - }, - // Aethernum - cataclysm: { - accuracy: 90, - basePower: 140, - category: "Physical", - desc: "After dealing damage, resets all of the user's boosts to 0, then Attack, Defense, and Speed get lowered by one stage.", - shortDesc: "Clears user's boosts; lowers Atk, Def and Spe.", - name: "Cataclysm", - isNonstandard: "Custom", - pp: 5, - priority: 0, - flags: {contact: 1, protect: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Earth Power", target); - this.add('-anim', source, "Continental Crush", target); - this.add('-anim', source, "Giga Impact", target); - }, - onAfterMoveSecondarySelf(pokemon) { - pokemon.clearBoosts(); - this.add('-clearboost', pokemon); - this.boost({atk: -1, def: -1, spe: -1}, pokemon, pokemon, this.dex.getActiveMove('Cataclysm')); - }, - secondary: null, - target: "normal", - type: "Normal", - }, - // Akiamara - x1: { + // Adri + skystriker: { accuracy: 100, - basePower: 100, + basePower: 50, category: "Special", - desc: "This move becomes a physical attack if the user's Attack is greater than its Special Attack, including stat stage changes. This move and its effects ignore the abilities and stat changes of other Pokemon.", - shortDesc: "Phys if Atk > SpA; ignores boosts, abilities.", + desc: "If this move is successful and the user has not fainted, the effects of Leech Seed and binding moves end for the user, and all hazards are removed from the user's side of the field. Raises the user's Speed by 1 stage.", + shortDesc: "Free user from hazards/bind/Leech Seed; +1 Spe.", + name: "Skystriker", isNonstandard: "Custom", - name: "x1", - pp: 10, + gen: 8, + pp: 30, priority: 0, flags: {protect: 1, mirror: 1}, onTryMove() { this.attrLastMove('[still]'); }, - onModifyMove(move, pokemon) { - if (pokemon.getStat('atk', false, true) > pokemon.getStat('spa', false, true)) move.category = 'Physical'; - }, onPrepareHit(target, source) { - this.add('-anim', source, 'Focus Punch', target); + this.add('-anim', source, 'Aerial Ace', target); + }, + onAfterHit(target, pokemon) { + if (pokemon.hp && pokemon.removeVolatile('leechseed')) { + this.add('-end', pokemon, 'Leech Seed', '[from] move: Skystriker', '[of] ' + pokemon); + } + const sideConditions = [ + 'spikes', 'toxicspikes', 'stealthrock', 'shiftingrocks', 'stickyweb', 'ferrofluid', 'gmaxsteelsurge', + ]; + for (const condition of sideConditions) { + if (pokemon.hp && pokemon.side.removeSideCondition(condition)) { + this.add('-sideend', pokemon.side, this.dex.getEffect(condition).name, '[from] move: Skystriker', '[of] ' + pokemon); + } + } + if (pokemon.hp && pokemon.volatiles['partiallytrapped']) { + pokemon.removeVolatile('partiallytrapped'); + } + }, + onAfterSubDamage(damage, target, pokemon) { + if (pokemon.hp && pokemon.removeVolatile('leechseed')) { + this.add('-end', pokemon, 'Leech Seed', '[from] move: Skystriker', '[of] ' + pokemon); + } + const sideConditions = [ + 'spikes', 'toxicspikes', 'stealthrock', 'shiftingrocks', 'stickyweb', 'ferrofluid', 'gmaxsteelsurge', + ]; + for (const condition of sideConditions) { + if (pokemon.hp && pokemon.side.removeSideCondition(condition)) { + this.add('-sideend', pokemon.side, this.dex.getEffect(condition).name, '[from] move: Skystriker', '[of] ' + pokemon); + } + } + if (pokemon.hp && pokemon.volatiles['partiallytrapped']) { + pokemon.removeVolatile('partiallytrapped'); + } + }, + self: { + boosts: { + spe: 1, + }, }, - ignoreAbility: true, - ignoreEvasion: true, - ignoreDefensive: true, secondary: null, target: "normal", - type: "Fighting", + type: "Flying", }, - // Akir - compost: { + + // aegii + reset: { accuracy: true, basePower: 0, category: "Status", - desc: "The user recovers half its HP. If any Pokemon fainted the previous turn, this move heals the active Pokemon by 50% of the user's HP on the following turn. Cures the user's party of all status conditions.", - shortDesc: "Heal 50%, Heal Bell; any fainted: Wish.", - name: "Compost", + desc: "This move acts as King's Shield for the purpose of Stance Change. The user is protected from most attacks this turn, but not status moves. Reduces the opponent's relevant attacking stat by 1 if they attempt to use a Special or contact move. If the user is Aegislash, changes the user's set from Physical to Special or Special to Physical.", + shortDesc: "King's Shield; -1 offense stat on hit; change set.", + name: "Reset", isNonstandard: "Custom", - pp: 5, - priority: 0, - flags: {snatch: 1, heal: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Ingrain", target); - }, - onHit(target, source) { - let didSomething = false; - const side = source.side; - if (side.faintedLastTurn || side.foe.faintedLastTurn) { - this.add('-anim', source, "Wish", target); - side.addSlotCondition(source, 'wish', source); - this.add('-message', `${source.name} made a wish!`); - didSomething = true; - } - for (const ally of side.pokemon) { - if (ally.cureStatus()) didSomething = true; - } - if (this.heal(source.baseMaxhp / 2, source)) didSomething = true; - return didSomething; - }, - secondary: null, - target: "self", - type: "Ghost", - }, - // Alpha - nekoveil: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "The user sets up Light Screen, Reflect, and Sunny Day for 5 turns. The effects of Light Screen and reflect are extended to 8 turns if the user is holding Light Clay, and Sunny Day remains for 8 turns if the user is holding a Heat Rock.", - shortDesc: "Light Screen, Reflect, and Sunny Day for 5 turns.", - name: "Neko Veil", - isNonstandard: "Custom", - pp: 5, - priority: 0, - flags: {snatch: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Geomancy", source); - }, - onHit(target, source) { - source.side.addSideCondition('lightscreen', source); - source.side.addSideCondition('reflect', source); - }, - weather: 'sunnyday', - secondary: null, - target: "self", - type: "Psychic", - }, - // Andrew - backoffgrrr: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "The user is protected from most moves made by other Pokemon during this turn, and if targeted with a move, the opposing Pokemon is forced to switch to a random ally. Non-damaging moves go through this protection. This move has a 1/X chance of being successful, where X starts at 1 and triples each time this move is successfully used. X resets to 1 if this move fails, if the user's last move used is not Baneful Bunker, Detect, Endure, King's Shield, Protect, Quick Guard, Spiky Shield, Wide Guard, or this move, or if it was one of those moves and the user's protection was broken. Fails if the user moves last this turn.", - shortDesc: "Protects from moves. Targeted: Force switch foe.", - name: "Back Off! GRRR!", - isNonstandard: "Custom", - pp: 15, + gen: 8, + pp: 10, priority: 4, flags: {}, + stallingMove: true, + volatileStatus: 'reset', onTryMove() { this.attrLastMove('[still]'); }, onPrepareHit(target, source) { - this.add('-anim', source, "Protect", source); - this.add('-anim', source, "Defense Curl", source); + this .add('-anim', source, 'Petal Dance', target); + this .add('-anim', source, 'King\'s Shield', source); }, - stallingMove: true, - volatileStatus: 'backoffgrrr', - onTryHit(target, source, move) { - return !!this.queue.willAct() && this.runEvent('StallMove', target); + onTryHit(pokemon) { + return !!this.queue.willAct() && this.runEvent('StallMove', pokemon); }, onHit(pokemon) { pokemon.addVolatile('stall'); + if (pokemon.species.baseSpecies === 'Aegislash') { + let specialSet = pokemon.moves.includes('shadowball'); + changeSet(this, pokemon, ssbSets[specialSet ? 'aegii' : 'aegii-Alt']); + specialSet = pokemon.moves.includes('shadowball'); + const setType = specialSet ? 'specially' : 'physically'; + this.add('-message', `aegii now has a ${setType} oriented set.`); + } }, condition: { duration: 1, onStart(target) { - this.add('-singleturn', target, 'move: Protect'); + this.add('-singleturn', target, 'Protect'); }, onTryHitPriority: 3, onTryHit(target, source, move) { if (!move.flags['protect'] || move.category === 'Status') { - if (move.isZ || move.isMax) target.getMoveHitData(move).zBrokeProtect = true; + if (move.isZ || (move.isMax && !move.breaksProtect)) target.getMoveHitData(move).zBrokeProtect = true; return; } - this.add('-activate', target, 'move: Protect'); + if (move.smartTarget) { + move.smartTarget = false; + } else { + this.add('-activate', target, 'move: Protect'); + } const lockedmove = source.getVolatile('lockedmove'); if (lockedmove) { // Outrage counter is reset @@ -337,30 +242,355 @@ export const Moves: {[k: string]: ModdedMoveData} = { delete source.volatiles['lockedmove']; } } - this.add('-anim', target, "Scary Face", source); - this.add('-anim', target, "Roar", source); - source.forceSwitchFlag = true; - this.add('-message', `${source.name} was scared off!`); - return null; + if (move.category === "Special") { + this.boost({spa: -1}, source, target, this.dex.getActiveMove("Reset")); + } else if (move.category === "Physical" && move.flags["contact"]) { + this.boost({atk: -1}, source, target, this.dex.getActiveMove("Reset")); + } + return this.NOT_FAIL; }, }, secondary: null, target: "self", - type: "Fire", + type: "Steel", }, - // Used for Andrew's ability - lavaterrain: { + + // Aelita + xanaskeystolyoko: { + accuracy: 100, + basePower: 20, + basePowerCallback(pokemon, target, move) { + return move.basePower + 20 * pokemon.positiveBoosts(); + }, + category: "Physical", + desc: "Power is equal to 20+(X*20), where X is the user's total stat stage changes that are greater than 0. User raises a random stat if it has less than 5 positive stat changes.", + shortDesc: "+20 power/boost. +1 random stat if < 5 boosts.", + name: "XANA's Keys To Lyoko", + isNonstandard: "Custom", + gen: 8, + pp: 40, + priority: 0, + flags: {protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Draco Meteor', target); + }, + self: { + onHit(pokemon) { + if (pokemon.positiveBoosts() < 5) { + const stats: BoostName[] = []; + let stat: BoostName; + for (stat in pokemon.boosts) { + if (!['accuracy', 'evasion'].includes(stat) && pokemon.boosts[stat] < 6) { + stats.push(stat); + } + } + if (stats.length) { + const randomStat = this.sample(stats); + const boost: SparseBoostsTable = {}; + boost[randomStat] = 1; + this.boost(boost); + } + } + }, + }, + secondary: null, + target: "normal", + type: "Dragon", + }, + + // Aeonic + lookingcool: { accuracy: true, basePower: 0, category: "Status", - desc: "For 5 turns, the terrain becomes Lava Terrain. During the effect, the power of Fire-type attacks made by Pokemon is multiplied by 1.5 and the power of Water-type attacks made by Pokemon is halved. Quilava's defense is doubled under Lava Terrain. Fire-type Pokemon have 1/16 of their maximum HP restored at the end of each turn, and all other Pokemon lose 1/16 of their maximum HP at the end of each turn.", - shortDesc: "Damages non-Fire-types. +Fire, -Water power.", - name: "Lava Terrain", + desc: "Sets up Stealth Rock on the opposing side of the field and boosts the user's Attack by 2 stages. Can only be used once per the user's time on the field.", + shortDesc: "1 use per switch-in. +2 Atk + Stealth Rock.", + name: "Looking Cool", isNonstandard: "Custom", + gen: 8, + pp: 5, + priority: 0, + flags: {reflectable: 1, snatch: 1}, + volatileStatus: 'lookingcool', + onTryMove(target) { + if (target.volatiles['lookingcool']) return false; + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + const foe = source.side.foe.active[0]; + this.add('-anim', source, 'Smokescreen', source); + this.add('-anim', source, 'Stealth Rock', foe); + }, + onHit(target, source, move) { + const foe = source.side.foe; + if (!foe.getSideCondition('stealthrock')) { + foe.addSideCondition('stealthrock'); + } + }, + boosts: { + atk: 2, + }, + secondary: null, + target: "self", + type: "Dark", + }, + + // Aethernum + lilypadoverflow: { + accuracy: 100, + basePower: 60, + basePowerCallback(source, target, move) { + if (!source.volatiles['raindrop'] || !source.volatiles['raindrop'].layers) return move.basePower; + return move.basePower + (source.volatiles['raindrop'].layers * 20); + }, + category: "Special", + desc: "Power is equal to 60 + (Number of Raindrops collected * 20). Whether or not this move is successful, the user's Defense and Special Defense decrease by as many stages as Raindrop had increased them, and the user's Raindrop count resets to 0.", + shortDesc: "More power per Raindrop. Lose Raindrops.", + name: "Lilypad Overflow", + isNonstandard: "Custom", + gen: 8, + pp: 5, + priority: 0, + flags: {protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Water Spout', target); + this.add('-anim', source, 'Max Geyser', target); + }, + onAfterMove(pokemon) { + if (pokemon.volatiles['raindrop']) pokemon.removeVolatile('raindrop'); + }, + secondary: null, + target: "normal", + type: "Water", + }, + + // Akir + ravelin: { + accuracy: 100, + basePower: 70, + category: "Physical", + desc: "Heals 50% of the user's max HP; Sets up Light Screen for 5 turns on the user's side.", + shortDesc: "Recover + Light Screen.", + name: "Ravelin", + isNonstandard: "Custom", + gen: 8, + pp: 5, + priority: 0, + flags: {contact: 1, protect: 1, mirror: 1, heal: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Aura Sphere', target); + this.add('-anim', source, 'Protect', source); + }, + onAfterMoveSecondarySelf(pokemon, target, move) { + this.heal(pokemon.maxhp / 2, pokemon, pokemon, move); + if (pokemon.side.getSideCondition('lightscreen')) return; + pokemon.side.addSideCondition('lightscreen'); + }, + secondary: null, + target: "normal", + type: "Steel", + }, + + // Alpha + blisteringiceage: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "The weather becomes an extremely heavy hailstorm lasting for 3 turns that prevents damaging Steel-type moves from executing, causes Ice-type moves to be 50% stronger, causes all non-Ice-type Pokemon on the opposing side to take 1/8 damage from hail, and causes all moves to have a 10% chance to freeze. This weather bypasses Magic Guard and Overcoat. This weather remains in effect until the 3 turns are up, or the weather is changed by Delta Stream, Desolate Land, or Primordial Sea.", + shortDesc: "3 turns. Heavy Hailstorm. Steel fail. 1.5x Ice.", + name: "Blistering Ice Age", + isNonstandard: "Custom", + gen: 8, + pp: 1, + noPPBoosts: true, + priority: 0, + flags: {}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Hail', target); + this.add('-anim', target, 'Subzero Slammer', target); + this.add('-anim', source, 'Subzero Slammer', source); + }, + isZ: "caioniumz", + secondary: null, + weather: 'heavyhailstorm', + target: "all", + type: "Ice", + }, + + // Andrew + whammerjammer: { + accuracy: 100, + basePower: 60, + category: "Special", + desc: "If this move is successful, the user switches out and all field conditions (entry hazards, terrains, weathers, screens, etc.) are removed from both sides.", + shortDesc: "Removes field conditions, switches out.", + name: "Whammer Jammer", + pp: 15, + priority: 0, + flags: {protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Shadow Ball', target); + }, + onHit(target, source, move) { + const removeAll = [ + 'reflect', 'lightscreen', 'auroraveil', 'ferrofluid', 'safeguard', 'mist', + 'spikes', 'toxicspikes', 'stealthrock', 'shiftingrocks', 'stickyweb', + ]; + const silentRemove = ['reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'shiftingrocks', 'ferrofluid']; + for (const sideCondition of removeAll) { + if (target.side.removeSideCondition(sideCondition)) { + if (!silentRemove.includes(sideCondition)) { + this.add('-sideend', target.side, this.dex.getEffect(sideCondition).name, '[from] move: Whammer Jammer', '[of] ' + source); + } + } + if (source.side.removeSideCondition(sideCondition)) { + if (!silentRemove.includes(sideCondition)) { + this.add('-sideend', source.side, this.dex.getEffect(sideCondition).name, '[from] move: Whammer Jammer', '[of] ' + source); + } + } + } + this.field.clearWeather(); + this.field.clearTerrain(); + for (const clear in this.field.pseudoWeather) { + if (clear.endsWith('mod') || clear.endsWith('clause')) continue; + this.field.removePseudoWeather(clear); + } + }, + selfSwitch: true, + secondary: null, + target: "normal", + type: "Ghost", + }, + + // Annika + datacorruption: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "Replaces the target's moveset with four vaguely competitively viable moves. 100% chance to cause the target to flinch.", + shortDesc: "Gives foe 4 new moves; flinches. +3 priority.", + name: "Data Corruption", + isNonstandard: "Custom", + gen: 8, + pp: 1, + noPPBoosts: true, + flags: {authentic: 1, reflectable: 1}, + priority: 3, + onPrepareHit(target, source) { + this.add('-anim', target, 'Shift Gear', target); + this.add('-anim', source, 'Plasma Fists', target); + this.add('-anim', target, 'Nasty Plot', target); + }, + onHit(target, source) { + this.add('-message', `${source.name} corrupted the opposing ${target.name}'s data storage!`); + const possibleMoves = [...USEFUL_MOVES]; + const newMoves = []; + for (let i = 0; i < 4; i++) { + const moveIndex = this.random(possibleMoves.length); + newMoves.push(possibleMoves[moveIndex]); + possibleMoves.splice(moveIndex, 1); + } + const newMoveSlots = changeMoves(this, target, newMoves); + target.moveSlots = newMoveSlots; + // @ts-ignore + target.baseMoveSlots = newMoveSlots; + }, + secondary: { + chance: 100, + volatileStatus: 'flinch', + }, + target: "adjacentFoe", + type: "Psychic", + }, + + // A Quag To The Past + bountyplace: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "Puts a bounty on the target. If the target is KOed by a direct attack, the attacker will gain +1 Attack, Defense, Special Attack, Special Defense, and Speed. If the target has a major status condition, it cannot have a bounty placed on it.", + shortDesc: "If target is ever KOed, attacker omniboosts.", + name: "Bounty Place", + isNonstandard: "Custom", + gen: 8, + pp: 1, + noPPBoosts: true, + priority: 0, + flags: {authentic: 1, reflectable: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Pay Day', target); + this.add('-anim', source, 'Block', target); + }, + status: "bounty", + isZ: "quagniumz", + secondary: null, + target: "normal", + type: "Ground", + }, + + // Arby + quickhammer: { + accuracy: 100, + basePower: 40, + category: "Special", + desc: "Usually moves first (Priority +1). If this move KOes the opponent, the user gains +2 Special Attack. Otherwise, the user gains -1 Defense and Special Defense.", + shortDesc: "+1 Prio. +2 SpA if KO, -1 Def/SpD if not.", + name: "Quickhammer", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 1, + flags: {protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Crabhammer', target); + }, + onAfterMoveSecondarySelf(pokemon, target, move) { + if (!target || target.fainted || target.hp <= 0) { + this.boost({spa: 2}, pokemon, pokemon, move); + } else { + this.boost({def: -1, spd: -1}, pokemon, pokemon, move); + } + }, + secondary: null, + target: "normal", + type: "Water", + }, + + // used for Arby's ability + waveterrain: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "For 5 turns, the terrain becomes Wave Terrain. During the effect, the accuracy of Water type moves is multiplied by 1.2, even if the user is not grounded. Hazards and screens are removed and cannot be set while Wave Terrain is active. Fails if the current terrain is Inversion Terrain.", + shortDesc: "5 turns. Removes hazards. Water move acc 1.2x.", + name: "Wave Terrain", + isNonstandard: "Custom", + gen: 8, pp: 10, priority: 0, flags: {}, - terrain: 'lavaterrain', + terrain: 'waveterrain', condition: { duration: 5, durationCallback(source, effect) { @@ -369,961 +599,188 @@ export const Moves: {[k: string]: ModdedMoveData} = { } return 5; }, - onModifyDefPriority: 1, - onModifyDef(def, pokemon) { - if (pokemon.baseSpecies.baseSpecies === 'Quilava') { - return this.chainModify(2); - } - }, - onBasePower(basePower, attacker, defender, move) { - if (move.type === 'Fire') { - return this.chainModify(1.5); - } else if (move.type === 'Water') { - return this.chainModify(0.5); + onModifyAccuracy(accuracy, target, source, move) { + if (move.type === 'Water') { + return this.chainModify(1.2); } }, onStart(battle, source, effect) { if (effect && effect.effectType === 'Ability') { - this.add('-fieldstart', 'move: Lava Terrain', '[from] ability: ' + effect, '[of] ' + source); + this.add('-fieldstart', 'move: Wave Terrain', '[from] ability: ' + effect, '[of] ' + source); } else { - this.add('-fieldstart', 'move: Lava Terrain'); + this.add('-fieldstart', 'move: Wave Terrain'); } - this.add('-message', 'The battlefield was covered in Lava!'); + this.add('-message', 'The battlefield suddenly flooded!'); + const removeAll = [ + 'reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'spikes', 'shiftingrocks', + 'toxicspikes', 'stealthrock', 'stickyweb', 'ferrofluid', 'gmaxsteelsurge', + ]; + const silentRemove = ['reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'shiftingrocks', 'ferrofluid']; + for (const sideCondition of removeAll) { + if (source.side.foe.removeSideCondition(sideCondition)) { + if (!silentRemove.includes(sideCondition)) { + this.add('-sideend', source.side.foe, this.dex.getEffect(sideCondition).name, '[from] move: Wave Terrain', '[of] ' + source); + } + } + if (source.side.removeSideCondition(sideCondition)) { + if (!silentRemove.includes(sideCondition)) { + this.add('-sideend', source.side, this.dex.getEffect(sideCondition).name, '[from] move: Wave Terrain', '[of] ' + source); + } + } + } + this.add('-message', `Hazards were removed by the terrain!`); }, - onResidualOrder: 21, - onResidualSubOrder: 2, + onResidualOrder: 5, + onResidualSubOrder: 3, onResidual() { this.eachEvent('Terrain'); }, - onTerrain(pokemon) { - if (pokemon.hasType('Fire')) { - this.heal(pokemon.baseMaxhp / 16); - this.add('-message', `${pokemon.illusion ? pokemon.illusion.name : pokemon.name} was healed by the Lava Terrain!`); - } else { - this.damage(pokemon.baseMaxhp / 16); - this.add('-message', `${pokemon.illusion ? pokemon.illusion.name : pokemon.name} was hurt by Lava Terrain!`); - } - }, onEnd() { - this.add('-fieldend', 'move: Lava Terrain'); - this.add('-message', 'The battlefield is no longer covered in Lava.'); + if (!this.effectData.duration) this.eachEvent('Terrain'); + this.add('-fieldend', 'move: Wave Terrain'); }, }, secondary: null, target: "all", - type: "Fire", + type: "Water", }, - // Anubis - hereticsmark: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "The target is replaced with a new randomly generated Super Staff Bros Brawl set. The new Pokemon retains the old pokemon's HP percentage, power point percentages, and status condition.", - shortDesc: "Target is replaced with random SSBB set.", - name: "Heretic's Mark", - isNonstandard: "Custom", - pp: 2, - noPPBoosts: true, - priority: -7, - flags: {authentic: 1, protect: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Shadow Force', target); - this.add('-anim', target, 'Dark Void', target); - }, - onHit(target, source, move) { - const wouldMove = this.queue.cancelMove(target); - // Generate a new team - const team = this.teamGenerator.getTeam({name: target.side.name, inBattle: true}); - let set = team.shift(); - if (set.name === target.set.name) set = team.shift(); // Must be a new set - const oldName = target.name; - // Bit of a hack so client doesn't crash when formeChange is called for the new pokemon - const effect = this.effect; - this.effect = {id: ''} as Effect; - const pokemon = new Pokemon(set, target.side); - this.effect = effect; - - pokemon.hp = Math.floor(pokemon.maxhp * (target.hp / target.maxhp)) || 1; - pokemon.status = target.status; - delete target.volatiles[target.name]; - if (target.statusData) pokemon.statusData = target.statusData; - for (const [j, moveSlot] of pokemon.moveSlots.entries()) { - moveSlot.pp = Math.floor( - moveSlot.maxpp * (target.moveSlots[j] ? (target.moveSlots[j].pp / target.moveSlots[j].maxpp) : 1) - ); - } - this.add('faint', target); - pokemon.position = target.position; - pokemon.isActive = true; - target = pokemon; - target.side.pokemon[0] = pokemon; - target.side.active[0] = pokemon; - - this.add('replace', target, pokemon.getDetails, target.hp / target.maxhp); // name change - target.setAbility(set.ability); - - this.singleEvent('SwitchIn', this.format, this.formatData, target); - this.add('-message', `${oldName} was sent to the Distortion World and replaced with somebody else!`); - let stat: BoostName; - for (stat in target.boosts) { - // Iterate through stat changes to update client - if (target.boosts[stat] !== 0) { - // Iterate through stat changes to update client - this.add('-setboost', target, stat, target.boosts[stat], '[silent]'); - } - } - if (wouldMove) this.hint(`${oldName}'s move was aborted because its moves changed.`); - }, - target: "normal", - type: "Ghost", - }, - // Used for Anubis's ability - distortionworld: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "Sets Distortion World for 5 turns. The power of Ghost type moves is boosted by 1.5x, and all Pokemon on the field have an effective Speed of 0. This terrain affects floating Pokemon.", - shortDesc: "5 turns: +Ghost power, all Pokemon Speed tie.", - name: "Distortion World", - isNonstandard: "Custom", - pp: 5, - priority: 0, - flags: {nosky: true}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Dark Void', source); - this.add('-anim', target, 'Dark Void', target); - }, - pseudoWeather: 'distortionworld', - condition: { - duration: 5, - onBasePower(basePower, attacker, defender, move) { - if (move.type === 'Ghost') { - this.debug('distortion world boost'); - return this.chainModify(1.5); - } - }, - onStart(battle, source, effect) { - if (effect && effect.effectType === 'Ability') { - this.add('-fieldstart', 'move: Distortion World', '[from] ability: ' + effect, '[of] ' + source); - } else { - this.add('-fieldstart', 'move: Distortion World'); - } - this.add('-message', 'Distortion World has caused all speed to become the same!'); - }, - onEnd() { - this.add('-fieldend', 'move: Distortion World'); - this.add('-message', 'Speeds have returned to normal.'); - }, - }, - target: "all", - type: "Ghost", - }, - // A Quag to The Past - murkyambush: { - accuracy: true, - basePower: 150, - category: "Physical", - desc: "Fails unless the user is hit by a physical move from an opponent this turn before it can execute the move. The foe's move has its secondary effects suppressed and damage halved. If the user was hit and has not fainted, it attacks and the effect ends. This move can affect Flying-type Pokemon.", - shortDesc: "Prepares for foe's attack, then retaliates.", - name: "Murky Ambush", - isNonstandard: "Custom", - pp: 10, - priority: -3, - flags: {contact: 1, protect: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - if (source.volatiles['murkyambush'] && source.volatiles['murkyambush'].gotHit) { - this.add('-anim', source, "Dig", target); - } - }, - beforeTurnCallback(pokemon) { - pokemon.addVolatile('murkyambush'); - this.add('-message', `${pokemon.name} anticipates the opposing Pokémon's next move!`); - this.attrLastMove('[still]'); - this.add('-anim', pokemon, "Work Up", pokemon); - }, - beforeMoveCallback(pokemon) { - if (pokemon.volatiles['murkyambush'] && !pokemon.volatiles['murkyambush'].gotHit) { - this.add('cant', pokemon, 'Murky Ambush', 'Murky Ambush'); - this.add('-message', `${pokemon.name} eases up.`); - return true; - } - this.add('-message', `${pokemon.side.foe.active[0].name} was caught in the ambush!`); - this.add(`c|+A Quag to The Past|GOTCHA BITCH`); - }, - condition: { - duration: 1, - onStart(pokemon) { - this.add('-singleturn', pokemon, 'move: Murky Ambush'); - }, - onSourceBasePowerPriority: 7, - onSourceBasePower(basePower, attacker, defender, move) { - this.debug('Murky Ambush weaken'); - if (move.category === 'Physical') { - return this.chainModify(0.5); - } - }, - onFoeTryMove(target, source, move) { - if (move.secondaries && move.category !== 'Status') { - this.debug('Murky Ambush secondary effects suppression'); - delete move.secondaries; - } - }, - onHit(pokemon, source, move) { - if (pokemon.side !== source.side && move.category === 'Physical') { - pokemon.volatiles['murkyambush'].gotHit = true; - } - }, - }, - ignoreImmunity: {Ground: true}, - target: "normal", - type: "Ground", - }, - // Arcticblast - trashalanche: { - basePower: 80, - basePowerCallback(pokemon, target, move) { - let noitem = 0; - for (const foes of target.side.pokemon) { - if (!foes.item) noitem += 20; - } - return move.basePower + noitem; - }, - accuracy: 100, - category: "Physical", - desc: "This move's Base Power increases by 20 for every foe that is not holding an item.", - shortDesc: "+20 Base Power for each itemless foe.", - name: "Trashalanche", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {protect: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Gunk Shot", target); - }, - secondary: null, - target: "normal", - type: "Poison", - }, - // Arsenal - comeonyougunners: { - accuracy: 100, - basePower: 100, - category: "Special", - desc: "This move's type depends on the user's held Plate. If the target has the same type as this move, its Base Power is boosted by 1.5x.", - shortDesc: "Type = Plate. 1.5x power for same-type targets.", - name: "Come on you Gunners", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {protect: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source, move) { - this.add('-anim', source, 'Judgment', target); - this.add('-anim', target, 'Extreme Evoboost', target); - // Modifying BP here so it happens AFTER ModifyMove - if (target.types.includes(move.type)) { - this.debug('Come on you Gunners BP boost'); - move.basePower = move.basePower * 1.5; - } - }, - onModifyMove(move, pokemon) { - const item = pokemon.getItem(); - if (item.id && item.onPlate && !item.zMove) { - this.debug(`Come on you Gunners type changed to: ${item.onPlate}`); - move.type = item.onPlate; - } - }, - secondary: null, - target: "normal", - type: "Normal", - }, - // Averardo - dragonsmash: { + // Archas + broadsidebarrage: { accuracy: 90, - basePower: 150, + basePower: 30, category: "Physical", - desc: "This Pokemon takes 50% of the damage it deals as recoil.", - shortDesc: "50% recoil.", - name: "Dragon Smash", + desc: "Hits 4 times. If one hit braks the target's substitute, it will take damage for the remaining hits. This move is super effective against Steel-type Pokemon.", + shortDesc: "Hits 4 times. Super effective on Steel.", + name: "Broadside Barrage", isNonstandard: "Custom", + gen: 8, pp: 5, priority: 0, - flags: {protect: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Head Smash', target); - this.add('-anim', target, 'Blue Flare', target); - }, - recoil: [1, 2], - secondary: null, - target: "normal", - type: "Dragon", - }, - // Beowulf - buzzingoftheswarm: { - accuracy: 100, - basePower: 95, - category: "Physical", - desc: "Has a 20% chance to cause the target to flinch.", - shortDesc: "20% chance to flinch.", - name: "Buzzing of the Swarm", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {protect: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Bug Buzz', source); - }, - secondary: { - chance: 20, - volatileStatus: 'flinch', - }, - target: "normal", - type: "Bug", - }, - // biggie - foodrush: { - accuracy: 100, - basePower: 100, - category: "Physical", - desc: "If both the user and the target have not fainted, the target is forced to switch out to a random non-fainted ally. This effect fails if the target used Ingrain previously, has the Suction Cups ability, or is behind a Substitute.", - shortDesc: "Forces the target to switch to a random ally.", - name: "Food Rush", - isNonstandard: "Custom", - pp: 10, - priority: -6, - flags: {protect: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Stockpile', source); - this.add('-anim', source, 'Spit Up', target); - }, - forceSwitch: true, - secondary: null, - target: "normal", - type: "Normal", - }, - // Birdy~! - justdance: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "Uses a random dance move other than Lunar Dance twice in a row and then restores 1/3 of its max HP.", - shortDesc: "Uses random dance move twice; heals 1/3 HP.", - name: "Just Dance", - isNonstandard: "Custom", - pp: 5, - priority: 0, - flags: {dance: 1, heal: 1}, - onTryMove(pokemon) { - this.attrLastMove('[still]'); - }, - onHit(target, source, effect) { - this.heal(source.baseMaxhp / 3, source); - const dancemoves = [ - 'dragondance', 'featherdance', 'fierydance', 'petaldance', 'quiverdance', 'revelationdance', 'swordsdance', 'teeterdance', - ]; - const randomMove = dancemoves[this.random(dancemoves.length)]; - this.useMove(randomMove, target); - this.useMove(randomMove, target); - // Kill getting locked into petal dance - if (source.getVolatile('lockedmove')) { - source.removeVolatile('lockedmove'); - } - }, - secondary: null, - target: "self", - type: "Normal", - }, - // bobochan - thousandcircuitoverload: { - accuracy: 100, - basePower: 90, - category: "Physical", - desc: "If the target is a Ground-type and is immune to Electric due to its typing, this move deals neutral damage regardless of other types, and the target loses its type-based immunity to Electric.", - shortDesc: "First hit neutral on Ground; removes its immunity.", - name: "Thousand Circuit Overload", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {protect: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Plasma Fists', target); - }, - onEffectiveness(typeMod, target, type, move) { - if (move.type !== 'Electric') return; - if (!target) return; // avoid crashing when called from a chat plugin - if (!target.runImmunity('Electric')) { - if (target.hasType('Ground')) return 0; - } - }, - volatileStatus: 'thousandcircuitoverload', - condition: { - noCopy: true, - onStart(pokemon) { - this.add('-start', pokemon, 'Thousand Circuit Overload'); - }, - onNegateImmunity(pokemon, type) { - if (pokemon.hasType('Ground') && type === 'Electric') return false; - }, - }, - ignoreImmunity: {Electric: true}, - secondary: null, - target: "normal", - type: "Electric", - }, - // Brandon - blusterywinds: { - accuracy: 100, - basePower: 70, - category: "Special", - desc: "Removes Reflect, Light Screen, Aurora Veil, Safeguard, Mist, Spikes, Toxic Spikes, Stealth Rock, and Sticky Web from both sides, and it removes any active weather condition or terrain.", - shortDesc: "Removes all field conditions and hazards.", - name: "Blustery Winds", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {protect: 1, mirror: 1, authentic: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Defog", target); - }, - onHit(target, source, move) { - const removeAll = [ - 'reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'spikes', 'toxicspikes', 'stealthrock', 'stickyweb', - ]; - const silentRemove = ['reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist']; - for (const sideCondition of removeAll) { - if (target.side.removeSideCondition(sideCondition)) { - if (!(silentRemove.includes(sideCondition))) { - this.add('-sideend', target.side, this.dex.getEffect(sideCondition).name, '[from] move: Blustery Winds', '[of] ' + source); - } - } - if (source.side.removeSideCondition(sideCondition)) { - if (!(silentRemove.includes(sideCondition))) { - this.add('-sideend', source.side, this.dex.getEffect(sideCondition).name, '[from] move: Blustery Winds', '[of] ' + source); - } - } - } - this.field.clearWeather(); - this.field.clearTerrain(); - }, - secondary: null, - target: "normal", - type: "Flying", - }, - // Cake - sparcedance: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "Boosts the user's Attack, Defense, and Speed by one stage.", - shortDesc: "+1 Atk, Def, and Spe.", - name: "Sparce Dance", - isNonstandard: "Custom", - pp: 15, - priority: 0, - flags: {snatch: 1, mirror: 1, dance: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Quiver Dance", source); - }, - boosts: {atk: 1, def: 1, spe: 1}, - secondary: null, - target: "self", - type: "Normal", - }, - // c.kilgannon - insidiousassault: { - accuracy: 100, - basePower: 70, - category: "Special", - desc: "This move's category becomes physical if the opponent's Defense stat is lower than its Special Defense stat. This move's Base Power is 1.5x if this move is physical.", - shortDesc: "Physical and power x1.5 if foe's Def < SpD.", - name: "Insidious Assault", - isNonstandard: "Custom", - pp: 5, - priority: 0, - flags: {protect: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Black Hole Eclipse', target); - }, - onModifyMove(move, pokemon, target) { - if (!target) return; - if (target.getStat('def', false, true) < target.getStat('spd', false, true)) move.category = 'Physical'; - }, - onBasePower(basePower, source, target, move) { - if (move.category === 'Physical') { - return this.chainModify(1.5); - } - }, - target: "normal", - type: "Dark", - }, - // cant say - aesthetislash: { - accuracy: 100, - basePower: 100, - category: "Physical", - desc: "Summons Grassy Terrain. If the user is an Aegislash, it changes to Blade forme, attacks, then goes back to Shield forme.", - shortDesc: "Summons Grassy Terrain. Aegislash transforms.", - name: "a e s t h e t i s l a s h", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {protect: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Geomancy', source); - this.add('-anim', source, 'Swords Dance', source); - this.add('-anim', source, 'Bloom Doom', target); - }, - onAfterMoveSecondarySelf() { - this.field.setTerrain('grassyterrain'); - }, - onAfterMove(pokemon) { - if (pokemon.species.baseSpecies !== 'Aegislash' || pokemon.transformed) return; - if (pokemon.species.name !== 'Aegislash') pokemon.formeChange('Aegislash'); - }, - target: "normal", - type: "Steel", - }, - // Catalystic - birbtotherescue: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "Resets the stat stages of all active Pokemon to 0, then lowers target's evasion by one stage and removes hazards.", - shortDesc: "Resets stats; target's evasion -1; clears hazards.", - name: "Birb to the Rescue", - pp: 15, - priority: 1, - flags: {protect: 1, reflectable: 1, mirror: 1, authentic: 1}, - onHit(target, source, move) { - this.add('-clearallboost'); - for (const pokemon of this.getAllActive()) { - pokemon.clearBoosts(); - } - let success = false; - if (!target.volatiles['substitute'] || move.infiltrates) success = !!this.boost({evasion: -1}); - const removeTarget = [ - 'reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'spikes', 'toxicspikes', 'stealthrock', 'stickyweb', - ]; - const removeAll = ['spikes', 'toxicspikes', 'stealthrock', 'stickyweb']; - for (const targetCondition of removeTarget) { - if (target.side.removeSideCondition(targetCondition)) { - if (!removeAll.includes(targetCondition)) continue; - this.add('-sideend', target.side, this.dex.getEffect(targetCondition).name, '[from] move: Defog', '[of] ' + source); - success = true; - } - } - for (const sideCondition of removeAll) { - if (source.side.removeSideCondition(sideCondition)) { - this.add('-sideend', source.side, this.dex.getEffect(sideCondition).name, '[from] move: Defog', '[of] ' + source); - success = true; - } - } - return success; - }, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Haze", target); - this.add('-anim', source, "Defog", target); - }, - secondary: null, - target: "normal", - type: "Ice", - }, - // Ceteris - bringerofdarkness: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "Has a 50% chance to cause the target to fall asleep. Sets one layer of Spikes on the opponent's side of the field and randomly boosts the user's Speed or Special Attack by one stage.", - shortDesc: "50% sleep; sets Spikes; Spe or SpA +1.", - name: "Bringer of Darkness", - isNonstandard: "Custom", - pp: 5, - priority: 0, - flags: {reflectable: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Dark Void", target); - }, - onHit(target, source, move) { - this.add('-anim', source, "Spikes", target); - target.side.addSideCondition('spikes'); - if (this.random(2) === 0) { - this.boost({spa: 1}, source, source); - } else { - this.boost({spe: 1}, source, source); - } - if (this.random(2) === 0) target.trySetStatus('slp', source); - }, - secondary: null, - target: "normal", - type: "Dark", - }, - // chaos - forcewin: { - accuracy: 100, - basePower: 0, - category: "Status", - desc: "Confuses the target and subjects it to the effects of Taunt, Torment, Heal Block, and Embargo.", - shortDesc: "Ensures domination of the opponent.", - name: "Forcewin", - isNonstandard: "Custom", - pp: 15, - priority: 0, - flags: {protect: 1, reflectable: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Entrainment", target); - this.add('-anim', source, "Lock On", target); - }, - onHit(target, source) { - target.addVolatile('taunt', source); - target.addVolatile('embargo', source); - target.addVolatile('torment', source); - target.addVolatile('confusion', source); - target.addVolatile('healblock', source); - this.add(`c|~chaos|/forcewin chaos`); - if (this.random(1000) === 420) { - // Should almost never happen, but will be hilarious when it does. - // Basically, roll a 1000 sided die, if it lands on 420 forcibly give the user's trainer the win - this.add(`c|~chaos|Actually`); - this.add(`c|~chaos|/forcewin ${source.side.name}`); - this.win(source.side); - } - }, - secondary: null, - target: "normal", - type: "???", - }, - // Chloe - beskyttelsesnet: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "The user faints, sets Reflect, Light Screen, and Safeguard, and lowers the target's Attack and Special Attack by 2 stages.", - shortDesc: "Faint; +Screens & Safeguard; foe -2 Atk & SpA.", - name: "beskyttelsesnet", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {mirror: 1, protect: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Geomancy', source); - this.add('-anim', source, 'Memento', target); - }, - onHit(target, source) { - this.boost({atk: -2, spa: -2}, target, source); - source.side.addSideCondition('lightscreen', source); - source.side.addSideCondition('reflect', source); - source.side.addSideCondition('safeguard', source); - }, - selfdestruct: "ifHit", - secondary: null, - target: "normal", - type: "Dark", - }, - // Darth - leechswap: { - accuracy: 85, - basePower: 0, - category: "Status", - desc: "Applies Leech Seed to the foe, then switches out", - shortDesc: "Leech Seeds foe, then switches out.", - name: "Leech Swap", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {protect: 1, mirror: 1, reflectable: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Leech Seed", target); - }, - onHit(target, source) { - if (target.hasType('Grass') || target.volatiles['leechseed']) { - this.add('-fail', source); - return false; - } else { - target.addVolatile('leechseed'); - } - }, - selfSwitch: true, - secondary: null, - target: "normal", - type: "Grass", - }, - // DaWoblefet - superegoinflation: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "User heals 25% HP. The target's Attack and Special Attack increase by two stages, and the target becomes affected by Taunt.", - shortDesc: "User heals 25% HP; target Atk & SpA +2; Taunt.", - name: "Super Ego Inflation", - isNonstandard: "Custom", - pp: 5, - priority: -7, - flags: {mirror: 1, authentic: 1, protect: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Follow Me', source); - this.add('-anim', target, 'Swords Dance', target); - this.add('-anim', target, 'Nasty Plot', target); - }, - onHit(target, source, move) { - this.heal(source.baseMaxhp / 4, source, source, this.dex.getActiveMove('Super Ego Inflation')); - this.boost({atk: 2, spa: 2}, target, source, this.dex.getActiveMove('Super Ego Inflation')); - target.addVolatile('taunt', source, this.dex.getActiveMove('Super Ego Inflation')); - }, - secondary: null, - target: "normal", - type: "Normal", - }, - // Decem - hitandrun: { - accuracy: 100, - basePower: 70, - category: "Special", - desc: "If this move hits, the Pokemon that was hit is affected with the Gooey status. Gooey causes the affected Pokemon to lose 1/6 of its max HP until it switches out. The user switches out.", - shortDesc: "Target loses 1/6 of HP per turn, user switches.", - name: "Hit and Run", - isNonstandard: "Custom", - pp: 5, - priority: 0, - flags: {protect: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Outrage', target); - }, - onHit(target, source, move) { - target.addVolatile('Gooey', source, move); - }, - selfSwitch: true, - secondary: null, - target: "normal", - type: "Dragon", - }, - // deetah - galvanizedstrike: { - accuracy: 100, - basePower: 70, - category: "Physical", - desc: "Power doubles if the user is burned, paralyzed, or poisoned. The physical damage halving effect from the user's burn is ignored. Has a 30% chance to paralyze the target.", - shortDesc: "Power x2 if burn/poison/paralyze; 30% paralyze.", - name: "Galvanized Strike", - isNonstandard: "Custom", - pp: 20, - priority: 0, flags: {contact: 1, protect: 1, mirror: 1}, onTryMove() { this.attrLastMove('[still]'); }, onPrepareHit(target, source) { - this.add('-anim', source, 'Wild Charge', target); - this.add('-anim', source, 'Bolt Strike', target); + this.add('-anim', target, 'Close Combat', target); + this.add('-anim', target, 'Earthquake', target); }, - onBasePower(basePower, pokemon) { - if (pokemon.status && pokemon.status !== 'slp') { - return this.chainModify(2); - } + onHit() { + this.add(`c|${getName('Archas')}|Fire all guns! Fiiiiire!`); }, - secondary: { - chance: 30, - status: 'par', + onEffectiveness(typeMod, target, type) { + if (type === 'Steel') return 1; }, + multihit: 4, + secondary: null, target: "normal", - type: "Electric", + type: "Steel", }, - // Dragontite - hyperforcestrike: { + + // Arcticblast + radiantburst: { accuracy: 100, - basePower: 90, - category: "Physical", - desc: "Damages the target and restores user's HP by 15% of its total health.", - shortDesc: "Damages the target and heals 15% total HP.", - name: "Hyperforce Strike", + basePower: 180, + category: "Special", + desc: "User gains Brilliant if not Brilliant without attacking. User attacks and loses Brilliant if Brilliant. Being Brilliant multiplies all stats by 1.5 and grants Perish Song immunity and Ingrain.", + shortDesc: "Gain or lose Brilliant. Attack if Brilliant.", + name: "Radiant Burst", isNonstandard: "Custom", + gen: 8, pp: 10, - priority: 0, - flags: {protect: 1, mirror: 1, heal: 1}, + priority: 1, + flags: {protect: 1, snatch: 1}, onTryMove() { this.attrLastMove('[still]'); }, - onPrepareHit(target, source) { - this.add('-anim', source, "Draco Meteor", target); - }, - onAfterMoveSecondarySelf(pokemon, target, move) { - this.heal(pokemon.maxhp * 0.15, pokemon, pokemon, move); // 15% health recovered - }, - secondary: null, - target: "normal", - type: "Flying", - }, - // DragonWhale - earthsblessing: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "Sets Gravity, raises the user's Attack by 2 stages, and cures the user's burn, paralysis, or poison. Fails if Gravity is already in effect.", - shortDesc: "Sets Gravity, raises Attack by 2, cures status.", - name: "Earth's Blessing", - isNonstandard: "Custom", - pp: 5, - priority: 0, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Swords Dance', source); - this.add('-anim', source, 'Wood Hammer', source); - }, - onHit(pokemon, move) { - if (this.field.pseudoWeather.gravity) return false; - this.boost({atk: 2}, pokemon, pokemon, this.dex.getActiveMove('EarthsBlessing')); - this.field.addPseudoWeather('gravity'); - if (['', 'slp', 'frz'].includes(pokemon.status)) return; - pokemon.cureStatus(); - }, - flags: {mirror: 1, snatch: 1}, - secondary: null, - target: "self", - type: "Ground", - zMove: {effect: 'healhalf'}, - }, - // E4 Flint - fangofthefireking: { - accuracy: 90, - basePower: 0, - damage: 111, - category: "Physical", - desc: "Deals 111 HP of damage and burns the target. If the target already has a status ailment, it is replaced with a burn. Fails if the target is a Fire-type or if the user is not a Fire-type.", - shortDesc: "111 damage & target burned; fails on Fire-type.", - name: "Fang of the Fire King", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {mirror: 1, protect: 1, bite: 1}, - onTryMove(pokemon, target, move) { - this.attrLastMove('[still]'); - if (!pokemon.hasType('Fire') || target.hasType('Fire')) { - this.add('-fail', pokemon, 'move: Fang of the Fire King'); + onTry(source, target) { + if (!source.volatiles['brilliant']) { + this.add('-anim', source, 'Recover', source); + source.addVolatile('brilliant'); return null; } }, onPrepareHit(target, source) { - this.add('-anim', source, 'Crunch', target); - this.add('-anim', target, 'Searing Shot', target); + this.add('-anim', source, 'Diamond Storm', target); }, - onHit(target, source) { - target.setStatus('brn', source, null, true); - // Cringy message - if (this.random(5) === 1) this.add(`c|@E4 Flint|here's a __taste__ of my __firepower__ XD`); + onModifyPriority(priority, source, target, move) { + if (source.volatiles['brilliant']) return 0; + }, + onModifyMove(move, source) { + if (!source.volatiles['brilliant']) { + move.accuracy = true; + move.target = "self"; + move.flags.protect = 0; + } + }, + onHit(target, pokemon) { + this.add(`c|${getName('Arcticblast')}|YEET`); + if (pokemon.volatiles['brilliant']) pokemon.removeVolatile('brilliant'); }, secondary: null, + infiltrates: true, target: "normal", - type: "Fire", + type: "Fairy", }, - // Eien - ancestralpower: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "The user's Attack and Special Attack are raised by one stage, it transforms into a different Pokemon, and it uses two moves dependent on the Pokemon; Celebi (Future Sight and Recover), Jirachi (Doom Desire and Wish), Manaphy (Tail Glow and Surf), Shaymin (Seed Flare and Leech Seed), or Victini (V-create and Blue Flare). Reverts to Mew and loses the initial raises of one stage to Attack and Special Attack at the end of the turn.", - shortDesc: "For turn: transforms, boosts, uses linked moves.", - name: "Ancestral Power", + + // Averardo + hatofwisdom: { + accuracy: 100, + basePower: 110, + category: "Special", + desc: "The user switches out, and this move deals damage one turn after it is used. At the end of that turn, the damage is calculated at that time and dealt to the Pokemon at the position the target had when the move was used. If the user is no longer active at the time, damage is calculated based on the user's natural Special Attack stat, types, and level, with no boosts from its held item or Ability. Fails if this move, Future Sight, or Doom Desire is already in effect for the target's position.", + shortDesc: "Hits 1 turn after being used. User switches.", + name: "Hat of Wisdom", isNonstandard: "Custom", - pp: 5, + gen: 8, + pp: 15, priority: 0, - flags: {protect: 1, authentic: 1}, - onTryMove() { + flags: {}, + ignoreImmunity: true, + isFutureMove: true, + onTry(source, target) { this.attrLastMove('[still]'); - }, - onHit(target, source, move) { - const baseForme = source.species.id; - const formes: {[forme: string]: string[]} = { - celebi: ['Future Sight', 'Recover'], - jirachi: ['Doom Desire', 'Wish'], - manaphy: ['Tail Glow', 'Surf'], - shaymin: ['Seed Flare', 'Leech Seed'], - victini: ['V-create', 'Blue Flare'], - }; - const forme = Object.keys(formes)[this.random(5)]; - // Suppress Ability now to prevent starting new abilities when transforming - source.addVolatile('gastroacid', source); - source.formeChange(forme, this.dex.getAbility('psychicsurge'), true); - this.boost({atk: 1, spa: 1}, source, source, move); - this.useMove(formes[forme][0], source, target); - this.useMove(formes[forme][1], source, target); - this.boost({atk: -1, spa: -1}, source, source, move); - source.formeChange(baseForme, this.dex.getAbility('psychicsurge'), true); + if (!target.side.addSlotCondition(target, 'futuremove')) return false; + this.add('-anim', source, 'Calm Mind', target); + this.add('-anim', source, 'Teleport', target); + Object.assign(target.side.slotConditions[target.position]['futuremove'], { + duration: 2, + move: 'hatofwisdom', + source: source, + moveData: { + id: 'hatofwisdom', + name: "Hat of Wisdom", + accuracy: 100, + basePower: 110, + category: "Special", + priority: 0, + flags: {}, + ignoreImmunity: false, + effectType: 'Move', + isFutureMove: true, + type: 'Psychic', + }, + }); + this.add('-start', source, 'move: Hat of Wisdom'); + source.switchFlag = 'hatofwisdom' as ID; + return null; }, secondary: null, target: "normal", type: "Psychic", }, - // Elgino - roughsnuggle: { + + // awa + awa: { accuracy: 100, basePower: 90, category: "Physical", - desc: "Power doubles if the target is not fully evolved.", - shortDesc: "Power doubles if the target is NFE.", - name: "Rough Snuggle", + desc: "Sets up Sandstorm.", + shortDesc: "Sets up Sandstorm.", + name: "awa!", isNonstandard: "Custom", - pp: 10, + gen: 8, + pp: 15, priority: 0, flags: {protect: 1, mirror: 1}, onTryMove() { @@ -1332,107 +789,878 @@ export const Moves: {[k: string]: ModdedMoveData} = { onPrepareHit(target, source) { this.add('-anim', source, 'Let\'s Snuggle Forever', target); }, - onBasePower(basePower, pokemon, target) { - if (target.species.evos.length) { - return this.chainModify(2); + weather: 'sandstorm', + secondary: null, + target: "normal", + type: "Rock", + }, + + // Beowulf + buzzinspection: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "The user gains the ability Compound Eyes for the remainder of the battle and then switches out", + shortDesc: "Gains Compound Eyes and switches.", + name: "Buzz Inspection", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Night Shade', source); + }, + onHit(pokemon) { + pokemon.baseAbility = 'compoundeyes' as ID; + pokemon.setAbility('compoundeyes'); + this.add('-ability', pokemon, pokemon.getAbility().name, '[from] move: Buzz Inspection'); + }, + selfSwitch: true, + secondary: null, + target: "self", + type: "Bug", + }, + + // biggie + juggernautpunch: { + accuracy: 100, + basePower: 150, + category: "Physical", + desc: "The user loses its focus and does nothing if it is hit by a damaging attack this turn before it can execute the move.", + shortDesc: "Fails if the user takes damage before it hits.", + name: "Juggernaut Punch", + isNonstandard: "Custom", + gen: 8, + pp: 20, + priority: -3, + flags: {contact: 1, protect: 1, punch: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Focus Punch', target); + }, + beforeTurnCallback(pokemon) { + pokemon.addVolatile('juggernautpunch'); + }, + beforeMoveCallback(pokemon) { + if (pokemon.volatiles['juggernautpunch'] && pokemon.volatiles['juggernautpunch'].lostFocus) { + this.add('cant', pokemon, 'Juggernaut Punch', 'Juggernaut Punch'); + return true; } }, + condition: { + duration: 1, + onStart(pokemon) { + this.add('-singleturn', pokemon, 'move: Juggernaut Punch'); + }, + onDamagePriority: -101, + onDamage(damage, target, source, effect) { + if (effect.effectType !== 'Move') return; + if (damage > target.baseMaxhp / 5) { + target.volatiles['juggernautpunch'].lostFocus = true; + } + }, + }, secondary: null, target: "normal", type: "Fighting", - zMove: {basePower: 175}, }, - // eternally - quack: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "Boosts the user's Special Attack and accuracy by one stage.", - shortDesc: "Raises the user's SpA and accuracy by 1.", - name: "Quack", + + // Billo + fishingforhacks: { + accuracy: 100, + basePower: 80, + category: "Special", + desc: "Knocks off opponent's item and randomly sets Stealth Rocks, Spikes, or Toxic Spikes.", + shortDesc: "Knock off foe's item. Set random hazard.", + name: "Fishing for Hacks", isNonstandard: "Custom", - pp: 5, + gen: 8, + pp: 15, priority: 0, - flags: {mirror: 1, snatch: 1}, + flags: {protect: 1, mirror: 1}, onTryMove() { this.attrLastMove('[still]'); }, onPrepareHit(target, source) { - this.add('-anim', source, 'Feather Dance', source); - this.add('-anim', source, 'Aqua Ring', source); + this.add('-anim', source, 'Mist Ball', target); + }, + onAfterHit(target, source) { + if (source.hp) { + const item = target.takeItem(); + if (item) { + this.add('-enditem', target, item.name, '[from] move: Knock Off', '[of] ' + source); + } + } + const hazard = this.sample(['Stealth Rock', 'Spikes', 'Toxic Spikes']); + target.side.addSideCondition(hazard); + }, + secondary: null, + target: "normal", + type: "Fairy", + }, + + // Blaz + bleakdecember: { + accuracy: 100, + basePower: 80, + category: "Special", + desc: "Damage is calculated using the user's Special Defense stat as its Special Attack, including stat stage changes. Other effects that modify the Special Attack stat are used as normal.", + shortDesc: "Uses user's SpD stat as SpA in damage calculation.", + name: "Bleak December", + isNonstandard: "Custom", + gen: 8, + pp: 15, + priority: 0, + flags: {protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Spirit Break', target); + }, + useSourceDefensiveAsOffensive: true, + secondary: null, + target: "normal", + type: "Fairy", + }, + + // Brandon + flowershower: { + accuracy: 100, + basePower: 100, + category: "Special", + desc: "This move is physical if the target's Defense is lower than the target's Special Defense.", + shortDesc: "Physical if target Def < Sp. Def.", + name: "Flower Shower", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Petal Dance', target); + }, + onModifyMove(move, source, target) { + if (target && target.getStat('def') < target.getStat('spd')) { + move.category = "Physical"; + } + }, + secondary: null, + target: "normal", + type: "Grass", + }, + + // Used for Brandon's ability + baneterrain: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "For 5 turns, the terrain becomes Bane Terrain. During the effect, moves hit off of the Pokemon's weaker attacking stat. Fails if the current terrain is Bane Terrain.", + shortDesc: "5 turns. Moves hit off of weaker stat.", + name: "Bane Terrain", + pp: 10, + priority: 0, + flags: {nonsky: 1}, + terrain: 'baneterrain', + condition: { + duration: 5, + durationCallback(source, effect) { + if (source?.hasItem('terrainextender')) { + return 8; + } + return 5; + }, + // Stat modifying in scripts.ts + onStart(battle, source, effect) { + if (effect?.effectType === 'Ability') { + this.add('-fieldstart', 'move: Bane Terrain', '[from] ability: ' + effect, '[of] ' + source); + } else { + this.add('-fieldstart', 'move: Bane Terrain'); + } + this.add('-message', 'The battlefield suddenly became grim!'); + }, + onResidualOrder: 5, + onResidualSubOrder: 3, + onResidual() { + this.eachEvent('Terrain'); + }, + onEnd() { + if (!this.effectData.duration) this.eachEvent('Terrain'); + this.add('-fieldend', 'move: Bane Terrain'); + }, + }, + secondary: null, + target: "all", + type: "Grass", + zMove: {boost: {def: 1}}, + contestType: "Beautiful", + }, + + // brouha + kinetosis: { + accuracy: 100, + basePower: 70, + category: "Special", + desc: "Badly poisons the target. If it is the user's first turn out, this move has +3 priority.", + shortDesc: "First turn: +3 priority. Target: TOX.", + name: "Kinetosis", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Aeroblast', target); + this.add('-anim', source, 'Haze', target); + }, + onModifyPriority(priority, source) { + if (source.activeMoveActions < 1) return priority + 3; + }, + status: 'tox', + secondary: null, + target: 'normal', + type: 'Flying', + }, + + // Cake + kevin: { + accuracy: true, + basePower: 100, + category: "Physical", + desc: "This move combines the user's current typing in its type effectiveness against the target.", + shortDesc: "Combines current types in its type effectiveness.", + name: "Kevin", + isNonstandard: "Custom", + gen: 8, + pp: 10, + flags: {protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source, move) { + this.add('-anim', source, 'Brave Bird', target); + if (!this.randomChance(255, 256)) { + this.attrLastMove('[miss]'); + this.add('-activate', target, 'move: Celebrate'); + this.add('-miss', source); + this.hint("In Super Staff Bros, this move can still miss 1/256 of the time regardless of accuracy or evasion."); + return null; + } + }, + onModifyType(move, pokemon, target) { + move.type = pokemon.types[0]; + }, + onTryImmunity(target, pokemon) { + if (pokemon.types[1]) { + if (!target.runImmunity(pokemon.types[1])) return false; + } + return true; + }, + onEffectiveness(typeMod, target, type, move) { + if (!target) return; + const pokemon = target.side.foe.active[0]; + if (pokemon.types[1]) { + return typeMod + this.dex.getEffectiveness(pokemon.types[1], type); + } + return typeMod; + }, + priority: 0, + recoil: [1, 8], + secondary: null, + target: "normal", + type: "Bird", + }, + + // cant say + neverlucky: { + accuracy: 85, + basePower: 110, + category: "Special", + desc: "Doubles base power if statused. Has a 10% chance to boost every stat 1 stage. High Crit Ratio.", + shortDesc: "x2 power if statused. 10% omniboost. High crit.", + name: "Never Lucky", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Overheat', target); + }, + onBasePower(basePower, pokemon) { + if (pokemon.status && pokemon.status !== 'slp') { + return this.chainModify(2); + } + }, + secondary: { + chance: 10, + self: { + boosts: { + atk: 1, + def: 1, + spa: 1, + spd: 1, + spe: 1, + }, + }, + }, + critRatio: 2, + target: "normal", + type: "Fire", + }, + + // Celestial + pandorasbox: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "Gains Protean and replaces Swords Dance and Pandora's Box with two moves from two random types.", + shortDesc: "Gains Protean and some random moves.", + name: "Pandora's Box", + isNonstandard: "Custom", + gen: 8, + pp: 5, + priority: 1, + flags: {snatch: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Teeter Dance', target); + }, + volatileStatus: 'pandorasbox', + condition: { + onStart(target) { + const typeMovePair: {[key: string]: string} = { + Normal: 'Body Slam', + Fighting: 'Drain Punch', + Flying: 'Floaty Fall', + Poison: 'Baneful Bunker', + Ground: 'Shore Up', + Rock: 'Stealth Rock', + Bug: 'Sticky Web', + Ghost: 'Shadow Sneak', + Steel: 'Iron Defense', + Fire: 'Fire Fang', + Water: 'Life Dew', + Grass: 'Synthesis', + Electric: 'Thunder Fang', + Psychic: 'Psychic Fangs', + Ice: 'Icicle Crash', + Dragon: 'Dragon Darts', + Dark: 'Taunt', + Fairy: 'Play Rough', + }; + const newMoveTypes = Object.keys(typeMovePair); + this.prng.shuffle(newMoveTypes); + const moves = [typeMovePair[newMoveTypes[0]], typeMovePair[newMoveTypes[1]]]; + target.m.replacedMoves = moves; + for (const moveSlot of target.moveSlots) { + if (!(moveSlot.id === 'swordsdance' || moveSlot.id === 'pandorasbox')) continue; + if (!target.m.backupMoves) { + target.m.backupMoves = [this.dex.deepClone(moveSlot)]; + } else { + target.m.backupMoves.push(this.dex.deepClone(moveSlot)); + } + const moveData = this.dex.getMove(this.toID(moves.pop())); + if (!moveData.id) continue; + target.moveSlots[target.moveSlots.indexOf(moveSlot)] = { + move: moveData.name, + id: moveData.id, + pp: Math.floor(moveData.pp * (moveSlot.pp / moveSlot.maxpp)), + maxpp: ((moveData.noPPBoosts || moveData.isZ) ? moveData.pp : moveData.pp * 8 / 5), + target: moveData.target, + disabled: false, + disabledSource: '', + used: false, + }; + } + target.setAbility('protean'); + this.add('-ability', target, target.getAbility().name, '[from] move: Pandora\'s Box'); + this.add('-message', `${target.name} learned new moves!`); + }, + onEnd(pokemon) { + if (!pokemon.m.backupMoves) return; + for (const [index, moveSlot] of pokemon.moveSlots.entries()) { + if (!(pokemon.m.replacedMoves.includes(moveSlot.move))) continue; + pokemon.moveSlots[index] = pokemon.m.backupMoves.shift(); + pokemon.moveSlots[index].pp = Math.floor(pokemon.moveSlots[index].maxpp * (moveSlot.pp / moveSlot.maxpp)); + } + delete pokemon.m.backupMoves; + delete pokemon.m.replacedMoves; + }, + }, + target: "self", + type: "Dragon", + }, + + // Celine + statusguard: { + accuracy: 100, + basePower: 0, + category: "Status", + desc: "Protects from physical moves. If hit by physical move, opponent is either badly poisoned, burned, or paralyzed at random and is forced out. Special attacks and status moves go through this protect.", + shortDesc: "Protected from physical moves. Gives brn/par/tox.", + name: "Status Guard", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 4, + flags: {}, + stallingMove: true, + volatileStatus: 'statusguard', + onTryMove() { + this.attrLastMove('[still]'); + }, + onHit(pokemon) { + pokemon.addVolatile('stall'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Protect', source); + }, + onTryHit(pokemon) { + return !!this.queue.willAct() && this.runEvent('StallMove', pokemon); + }, + condition: { + duration: 1, + onStart(target) { + this.add('-singleturn', target, 'Protect'); + }, + onTryHitPriority: 3, + onTryHit(target, source, move) { + if (!move.flags['protect']) { + if (move.isZ || (move.isMax && !move.breaksProtect)) target.getMoveHitData(move).zBrokeProtect = true; + return; + } + if (move.category === 'Special' || move.category === 'Status') { + return; + } else if (move.smartTarget) { + move.smartTarget = false; + } else { + this.add('-activate', target, 'move: Protect'); + } + const lockedmove = source.getVolatile('lockedmove'); + if (lockedmove) { + // Outrage counter is reset + if (source.volatiles['lockedmove'].duration === 2) { + delete source.volatiles['lockedmove']; + } + } + if (move.category === 'Physical') { + const statuses = ['brn', 'par', 'tox']; + source.trySetStatus(this.sample(statuses), target); + source.forceSwitchFlag = true; + } + return this.NOT_FAIL; + }, + onHit(target, source, move) { + if (move.category === 'Physical') { + const statuses = ['brn', 'par', 'tox']; + source.trySetStatus(this.sample(statuses), target); + source.forceSwitchFlag = true; + } + }, + }, + secondary: null, + target: "self", + type: "Normal", + }, + + // c.kilgannon + soulsiphon: { + accuracy: 100, + basePower: 70, + category: "Physical", + desc: "Lowers the target's Attack by 1 stage. The user restores its HP equal to the target's Attack stat calculated with its stat stage before this move was used. If Big Root is held by the user, the HP recovered is 1.3x normal, rounded half down. Fails if the target's Attack stat stage is -6.", + shortDesc: "User heals HP=target's Atk stat. Lowers Atk by 1.", + name: "Soul Siphon", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {contact: 1, mirror: 1, protect: 1, heal: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Supersonic Skystrike', target); + }, + onHit(target, source) { + if (target.boosts.atk === -6) return false; + const atk = target.getStat('atk', false, true); + const success = this.boost({atk: -1}, target, source, null, false, true); + return !!(this.heal(atk, source, target) || success); + }, + secondary: null, + target: "normal", + type: "Flying", + }, + + // Coconut + devolutionbeam: { + accuracy: 100, + basePower: 80, + category: "Special", + desc: "If the target Pokemon is evolved, this move will reduce the target to its first-stage form. If the target Pokemon is single-stage or is already in its first-stage form, this move deals 1.5x damage. Hits Ghost types.", + shortDesc: "Devolves evolved mons; 1.5x dmg to LC.", + name: "Devolution Beam", + isNonstandard: "Custom", + gen: 8, + pp: 5, + priority: 0, + flags: {protect: 1}, + ignoreImmunity: {'Normal': true}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Psywave', target); + }, + onBasePower(damage, source, target) { + let species = target.species; + if (species.isMega) species = this.dex.getSpecies(species.baseSpecies); + const isSingleStage = (species.nfe && !species.prevo) || (!species.nfe && !species.prevo); + if (isSingleStage) return this.chainModify(1.5); + }, + onHit(target, source, move) { + let species = target.species; + if (species.isMega) species = this.dex.getSpecies(species.baseSpecies); + const ability = target.ability; + const isSingleStage = (species.nfe && !species.prevo) || (!species.nfe && !species.prevo); + if (!isSingleStage) { + let prevo = species.prevo; + if (this.dex.getSpecies(prevo).prevo) { + prevo = this.dex.getSpecies(prevo).prevo; + } + target.formeChange(prevo, this.effect); + target.canMegaEvo = null; + target.setAbility(ability); + } + }, + secondary: null, + target: "normal", + type: "Normal", + }, + + // dogknees + bellyrubs: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "Heals the user by 25% of their maximum HP. Boosts the user's Attack and Defense by 1 stage.", + shortDesc: "Heals 25% HP. Boosts Atk/Def by 1 stage.", + name: "Belly Rubs", + isNonstandard: "Custom", + gen: 8, + pp: 5, + priority: 0, + flags: {heal: 1, snatch: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Belly Drum', target); + }, + self: { + boosts: { + atk: 1, + def: 1, + }, + }, + onHit(pokemon, target, move) { + this.heal(pokemon.maxhp / 4, pokemon, pokemon, move); + }, + secondary: null, + zMove: {boost: {spe: 1}}, + target: "self", + type: "Normal", + }, + + // drampa's grandpa + getoffmylawn: { + accuracy: 100, + basePower: 78, + category: "Special", + desc: "The user switches out after damaging the target. The target is forced out after being damaged.", + shortDesc: "Phazes target and switches user out.", + name: "GET OFF MY LAWN!", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: -6, + flags: {protect: 1, sound: 1, authentic: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Boomburst', target); + }, + onHit() { + this.add(`c|${getName('drampa\'s grandpa')}|GET OFF MY LAWN!!!`); + }, + secondary: null, + forceSwitch: true, + selfSwitch: true, + target: "normal", + type: "Normal", + }, + + // DragonWhale + cloakdance: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "If Mimikyu's Disguise is intact, the user is not Mimikyu, or Mimikyu is the last remaining Pokemon, Attack goes up 2 stages. If Mimikyu's Disguise is busted and there are other Pokemon on Mimikyu's side, the Disguise will be repaired and Mimikyu will switch out.", + shortDesc: "Busted: Repair, switch. Last mon/else: +2 Atk.", + name: "Cloak Dance", + pp: 5, + priority: 0, + flags: {snatch: 1, dance: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + const moveAnim = (!source.abilityData.busted || source.side.pokemonLeft === 1) ? 'Swords Dance' : 'Teleport'; + this.add('-anim', source, moveAnim, target); + }, + onHit(target, source) { + if (!source.abilityData.busted || source.side.pokemonLeft === 1) { + this.boost({atk: 2}, target); + } else { + delete source.abilityData.busted; + if (source.species.baseSpecies === 'Mimikyu') source.formeChange('Mimikyu', this.effect, true); + source.switchFlag = true; + } + }, + secondary: null, + target: "self", + type: "Fairy", + }, + + // dream + lockandkey: { + accuracy: 100, + basePower: 0, + category: "Status", + desc: "Raises the user's Special Attack and Special Defense stats by 1 stage and prevents the target from switching out.", + shortDesc: "Raises user's SpA and SpD by 1. Traps foe.", + name: "Lock and Key", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {snatch: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Calm Mind', source); + this.add('-anim', target, 'Imprison', target); + }, + onHit(target, source, move) { + if (source.isActive) target.addVolatile('trapped', source, move, 'trapper'); + }, + self: { + boosts: { + spa: 1, + spd: 1, + }, + }, + secondary: null, + target: "allAdjacentFoes", + type: "Steel", + }, + + // Elgino + navisgrace: { + accuracy: 100, + basePower: 90, + category: "Special", + desc: "This move is super effective on Steel- and Poison-type Pokemon.", + shortDesc: "Super effective on Steel- and Poison-types.", + name: "Navi's Grace", + isNonstandard: "Custom", + gen: 8, + pp: 15, + priority: 0, + flags: {protect: 1}, + secondary: null, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Dazzling Gleam', target); + this.add('-anim', source, 'Earth Power', target); + }, + onEffectiveness(typeMod, target, type) { + if (type === 'Poison' || type === 'Steel') return 1; + }, + target: 'normal', + type: 'Fairy', + }, + + // Emeri + forcedlanding: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "The user restores 1/2 of its maximum HP, rounded half up. For 5 turns, the evasiveness of all active Pokemon is multiplied by 0.6. At the time of use, Bounce, Fly, Magnet Rise, Sky Drop, and Telekinesis end immediately for all active Pokemon. During the effect, Bounce, Fly, Flying Press, High Jump Kick, Jump Kick, Magnet Rise, Sky Drop, Splash, and Telekinesis are prevented from being used by all active Pokemon. Ground-type attacks, Spikes, Toxic Spikes, Sticky Web, and the Arena Trap Ability can affect Flying types or Pokemon with the Levitate Ability. Fails if this move is already in effect.", + shortDesc: "Restore 50% HP + set Gravity.", + name: "Forced Landing", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {heal: 1}, + onHit(pokemon, target, move) { + this.heal(pokemon.maxhp / 2, pokemon, pokemon, move); + }, + pseudoWeather: 'gravity', + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Roost', source); + this.add('-anim', source, 'Gravity', source); }, - boosts: {spa: 1, accuracy: 1}, secondary: null, target: "self", type: "Flying", }, - // explodingdaisies - doom: { - basePower: 100, - accuracy: 100, - category: "Special", - desc: "Summons Sunny Day after doing damage.", - shortDesc: "Summons Sunny Day after doing damage.", - name: "DOOM!", + + // EpicNikolai + epicrage: { + accuracy: 95, + basePower: 120, + category: "Physical", + desc: "Paralyzes target, and take 40% recoil. If the user is fire-type, it burns the target and take 33% recoil.", + shortDesc: "Par + 40% recoil. Fire: burn + 33% recoil.", + name: "Epic Rage", isNonstandard: "Custom", + gen: 8, pp: 5, priority: 0, - flags: {mirror: 1, protect: 1}, + flags: {contact: 1, protect: 1, mirror: 1}, onTryMove() { this.attrLastMove('[still]'); }, onPrepareHit(target, source) { - this.add('-anim', source, 'Eruption', target); - this.add('-anim', source, 'Sunny Day', source); + this.add('-anim', source, 'Draco Meteor', target); }, - onAfterMoveSecondarySelf() { - this.field.setWeather('sunnyday'); + onModifyMove(move, pokemon) { + if (!pokemon.types.includes('Fire')) return; + move.secondaries = [{ + chance: 100, + status: 'brn', + }]; + move.recoil = [33, 100]; + }, + recoil: [4, 10], + secondary: { + chance: 100, + status: "par", }, - secondary: null, target: "normal", type: "Fire", }, - // False - frck: { + + // estarossa + sandbalance: { accuracy: true, basePower: 0, - category: "Physical", - desc: "Does not check accuracy. KOes the foe. User faints afterwards if move hits.", - shortDesc: "KOes foe. Always hits. User faints on success.", - name: "fr*ck", + category: "Status", + desc: "The user uses Roar, then switches out after forcing out the opposing Pokemon.", + shortDesc: "Uses Roar, switches out after.", + name: "Sand Balance", isNonstandard: "Custom", - pp: 6, - noPPBoosts: true, - priority: 0, - flags: {protect: 1}, + gen: 8, + pp: 10, + priority: -6, + flags: {authentic: 1, protect: 1, mirror: 1, sound: 1, reflectable: 1}, onTryMove() { this.attrLastMove('[still]'); }, onPrepareHit(target, source) { - this.add('-activate', source, 'move: Celebrate'); - this.add('-anim', source, 'Searing Sunraze Smash', target); - this.add('-anim', source, 'Explosion', target); + this.add('-anim', source, 'Roar', target); + this.add('-anim', source, 'Parting Shot', target); + }, + forceSwitch: true, + selfSwitch: true, + secondary: null, + target: "normal", + type: "Ground", + }, + + // explodingdaisies + youhavenohope: { + accuracy: 100, + basePower: 0, + damageCallback(pokemon, target) { + return target.getUndynamaxedHP() - pokemon.hp; + }, + onTryImmunity(target, pokemon) { + return pokemon.hp < target.hp; + }, + category: "Physical", + desc: "Lowers the target's HP to the user's HP. This move bypasses the target's substitute.", + shortDesc: "Lowers the target's HP to the user's HP.", + name: "You Have No Hope!", + pp: 1, + noPPBoosts: true, + priority: 0, + flags: {authentic: 1, contact: 1, protect: 1, mirror: 1}, + isNonstandard: "Custom", + gen: 8, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Endeavor', target); }, onHit(target, source) { - target.faint(); - source.faint(); + this.add(`c|${getName('explodingdaisies')}|You have no hope ${target.name}!`); }, secondary: null, target: "normal", - type: "???", + type: "Normal", }, // fart - soupstealing7starstrike: { + soupstealing7starstrikeredux: { accuracy: 100, - basePower: 90, + basePower: 40, + basePowerCallback() { + if (this.field.pseudoWeather.soupstealing7starstrikeredux) { + return 40 * this.field.pseudoWeather.soupstealing7starstrikeredux.multiplier; + } + return 40; + }, category: "Physical", - desc: "This move is either a Water-, Fire-, or Grass-type move. The selected type is added to the user of this move.", - shortDesc: "Changes user/move type to Fire, Water, or Grass.", - name: "Soup-Stealing 7-Star Strike", + desc: "This move is either a Water, Fire, or Grass type move. The selected type is added to the user of this move. For every consecutive turn that this move is used by at least one Pokemon, this move's power is multiplied by the number of turns to pass, but not more than 5.", + shortDesc: "Change type to F/W/G. Power+ on repeat.", + name: "Soup-Stealing 7-Star Strike: Redux", isNonstandard: "Custom", + gen: 8, pp: 15, priority: 0, - flags: {protect: 1}, + flags: {contact: 1, protect: 1, mirror: 1}, + onTry() { + this.field.addPseudoWeather('soupstealing7starstrikeredux'); + }, onTryMove() { this.attrLastMove('[still]'); }, @@ -1448,2056 +1676,51 @@ export const Moves: {[k: string]: ModdedMoveData} = { }, onHit(target, source) { this.add('-anim', source, 'Spectral Thief', target); - if (Math.round(this.random())) { - this.add(`c|%fart|I hl on soup`); + if (this.randomChance(1, 2)) { + this.add(`c|${getName('fart')}|I hl on soup`); } else { - this.add(`c|%fart|did someone say soup?`); + this.add(`c|${getName('fart')}|I walk with purpose. bring me soup.`); } }, - secondary: null, - target: "normal", - type: "Normal", - }, - // Felucia - quickreload: { - accuracy: true, - basePower: 0, - category: "Physical", - desc: "Uses Defog and then attempts to use U-Turn.", - shortDesc: "Uses Defog, then U-Turn.", - name: "Quick Reload", - isNonstandard: "Custom", - pp: 15, - priority: 0, - flags: {mirror: 1, protect: 1, authentic: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onHit(target, source) { - this.useMove('Defog', source, target); - const move = this.dex.getActiveMove('uturn'); - move.basePower = 90; - this.useMove(move, source, target); - }, - secondary: null, - target: "normal", - type: "Bug", - }, - // FOMG - rickrollout: { - accuracy: true, - basePower: 140, - category: "Physical", - desc: "Raises the user's Speed by two stages and has a 30% chance to confuse the target.", - shortDesc: "Speed +2; 30% chance to confuse target.", - name: "Rickrollout", - isNonstandard: "Custom", - pp: 1, - priority: 0, - flags: {}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Rock Polish', source); - this.add('-anim', source, 'Let\'s Snuggle Forever', target); - }, - onHit() { - const messages = ["SPL players don't want you to know about this secret", - "North American player reveals the concerning secret how to make money with Pokemon that will crack you up", - "10 amazing facts about Zarel you have never heard of", - "Veteran player shared his best team with a beginner - here's what happened after", - "Use these 3 simple methods to gain 200+ rating in 10 minutes"][this.random(5)]; - - this.add(`raw|${messages}`); - }, - self: { - boosts: { - spe: 2, - }, - }, - secondary: { - chance: 30, - volatileStatus: 'confusion', - }, - isZ: "astleyiumz", - target: "normal", - type: "Rock", - }, - // Gallant Spear - stormassaultogs: { - accuracy: 90, - basePower: 25, - multihit: 3, - category: "Physical", - desc: "Hits three times. Each hit has a 10% chance to drop the target's Defense and a 10% chance to burn. Each hit is always a critical hit. If one of the hits breaks the target's Substitute, it will take damage for the remaining hits.", - shortDesc: "Hits thrice; 10% Def -1; 10% burn; always crits.", - isNonstandard: "Custom", - name: "Storm Assault OGs", - pp: 10, - priority: 0, - flags: {protect: 1, mirror: 1}, - willCrit: true, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Bullet Seed', target); - this.add('-anim', source, 'Magma Storm', target); - }, - secondaries: [ - { - chance: 10, - status: 'brn', - }, { - chance: 10, - boosts: { - def: -1, - }, - }, - ], - target: "normal", - type: "Fire", - }, - // Gimm1ck - slavsquat: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "Raises the user's Attack, Defense, and Special Defense by one stage. Weather becomes Hail.", - shortDesc: "Atk, Def, and SpD +1; weather becomes Hail.", - name: "Slav Squat", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {snatch: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Automotize', source); - this.add('-anim', source, 'Hail', source); - this.add('-anim', source, 'Gravity', source); - }, - boosts: { - atk: 1, - def: 1, - spd: 1, - }, - weather: 'hail', - secondary: null, - target: "self", - type: "Ice", - }, - // GMars - tastetherainbow: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "Increases this Pokemon's Attack, Special Attack, and Speed by two stages while also decreasing this Pokemon's Defense and Special Defense by one stage. If this Pokemon is a Minior in its Meteor forme, it will permanently transform into one of the Minior colors and have a special effect based on that color. Red burns the foe, orange confuses it, yellow paralyzes it, green applies Leech Seed, blue gives the user Aqua Ring status, indigo poisons the foe, and violet badly poisons it.", - shortDesc: "+2 Atk, SpA, Spe. -1 Def, SpD. Meteor -> Core.", - name: "Taste the Rainbow", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {protect: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Extreme Evoboost', source); - }, - onHit(target, source, move) { - // Set target to the foe, this is a self targeting move so it works even if the foe has a subsitute - target = source.side.foe.active[0]; - this.boost({atk: 2, spa: 2, spe: 2, def: -1, spd: -1}, source); - if (source.species.id !== 'miniormeteor' || source.transformed) return; - - const rainbow = ['', '-Orange', '-Yellow', '-Green', '-Blue', '-Indigo', '-Violet']; - const color = rainbow[this.random(rainbow.length)]; - source.formeChange(`Minior${color}`, move, true); - // Display correct color on client - if (color) { - this.add('-formechange', source, `Minior${color}`); - source.m.miniorColor = color; - } - - if (target.volatiles['substitute'] && color !== '-Blue') { - this.add('-fail', source); - return; - } - switch (color) { - case '': - if (!target.trySetStatus('brn', source)) this.add('-fail', target); - break; - case '-Orange': - if (!target.addVolatile('confusion', source)) this.add('-fail', target); - break; - case '-Yellow': - if (!target.trySetStatus('par', source)) this.add('-fail', target); - break; - case '-Green': - if (!target.hasType('Grass')) { - this.add('-anim', source, 'Leech Seed', target); - target.addVolatile('leechseed'); - } else { - this.add('-immune', target); - } - break; - case '-Blue': - if (!source.addVolatile('aquaring', source)) this.add('-fail', source); - break; - case '-Indigo': - if (!target.trySetStatus('psn', source)) this.add('-fail', target); - break; - case '-Violet': - if (!target.trySetStatus('tox', source)) this.add('-fail', target); - break; - default: - throw new Error(`Invalid color for Taste the Rainbow selected: ${color}`); - } - }, - target: "self", - type: "Normal", - }, - // grimAuxiliatrix - paintrain: { - accuracy: 100, - basePower: 0, - basePowerCallback(pokemon, target) { - const targetWeight = target.getWeight(); - const pokemonWeight = pokemon.getWeight(); - if (pokemonWeight > targetWeight * 5) { - return 120; - } - if (pokemonWeight > targetWeight * 4) { - return 100; - } - if (pokemonWeight > targetWeight * 3) { - return 80; - } - if (pokemonWeight > targetWeight * 2) { - return 60; - } - return 40; - }, - category: "Physical", - desc: "The power of this move depends on (user's weight / target's weight), rounded down. Power is equal to 120 if the result is 5 or more, 100 if 4, 80 if 3, 60 if 2, and 40 if 1 or less. The user recovers 1/2 the HP lost by the target, rounded half up. If Big Root is held by the user, the HP recovered is 1.3x normal, rounded half down.", - shortDesc: "Stronger if user is heavier. Heals 50% of damage.", - name: "Pain Train", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {contact: 1, protect: 1, mirror: 1, heal: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Meteor Mash', target); - }, - drain: [1, 2], - secondary: null, - target: "normal", - type: "Steel", - }, - // guishark - dadjoke: { - accuracy: 100, - basePower: 90, - category: "Physical", - desc: "50% chance to confuse the foe.", - shortDesc: "50% chance to confuse the foe.", - name: "Dad Joke", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {protect: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Boomburst", target); - }, - secondary: { - chance: 50, - volatileStatus: "confusion", - }, - target: "normal", - type: "Dark", - }, - // Hippopotas - hazardpass: { - accuracy: 100, - basePower: 0, - category: "Status", - pp: 20, - priority: 0, - desc: "The user sets two of Stealth Rock, Spikes (1 layer), Toxic Spikes (1 layer), and Sticky Web on the foe's side of the field and then switches out.", - shortDesc: "Sets 2 random hazards, then switches out.", - name: "Hazard Pass", - isNonstandard: "Custom", - flags: {reflectable: 1, mirror: 1, authentic: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onHitSide(target, source) { - // All possible hazards, and their maximum possible layer count - const hazards: {[k: string]: number} = {stealthrock: 1, spikes: 3, toxicspikes: 2, stickyweb: 1}; - // Check how many layers of each hazard can still be added to the foe's side - if (target.getSideCondition('stealthrock')) delete hazards.stealthrock; - if (target.getSideCondition('spikes')) { - hazards.spikes -= target.sideConditions['spikes'].layers; - if (!hazards.spikes) delete hazards.spikes; - } - if (target.getSideCondition('toxicspikes')) { - hazards.toxicspikes -= target.sideConditions['toxicspikes'].layers; - if (!hazards.toxicspikes) delete hazards.toxicspikes; - } - if (target.getSideCondition('stickyweb')) delete hazards.stickyweb; - // Create a list of hazards not yet at their maximum layer count - const hazardTypes = Object.keys(hazards); - // If there are no possible hazards, don't do anything - if (!hazardTypes.length) return false; - // Pick a random hazard, and set it - const hazard1 = this.sample(hazardTypes); - // Theoretically, this should always work - this.add('-anim', source, this.dex.getMove(hazard1).name, target); - target.addSideCondition(hazard1, source, this.effect); - // If that was the last possible layer of that hazard, remove it from our list of possible hazards - if (hazards[hazard1] === 1) { - hazardTypes.splice(hazardTypes.indexOf(hazard1), 1); - // If there are no more hazards we can set, end early on a success - if (!hazardTypes.length) return true; - } - // Set the last hazard and animate the switch - const hazard2 = this.sample(hazardTypes); - this.add('-anim', source, this.dex.getMove(hazard2).name, target); - target.addSideCondition(hazard2, source, this.effect); - this.add('-anim', source, "Baton Pass", target); - }, - selfSwitch: true, - secondary: null, - target: "foeSide", - type: "Normal", - zMove: {boost: {def: 1}}, - }, - // HoeenHero - scriptedterrain: { - accuracy: 100, - basePower: 0, - category: "Status", - desc: "Sets Scripted Terrain for 5 turns. The power of Bug-type moves is boosted by 1.5, and there is a 5% chance for every move used to become Glitch Out instead. At the end of a turn, every Pokemon has a 5% chance to transform into a MissingNo. with 3 random moves and Glitch Out. Switching out will restore the Pokemon to its normal state. This terrain affects floating Pokemon.", - shortDesc: "5 turns: +Bug power, glitchy effects.", - name: "Scripted Terrain", - isNonstandard: "Custom", - pp: 5, - priority: 0, - flags: {nonsky: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Calm Mind', source); - this.add('-anim', source, 'Geomancy', source); - }, - terrain: 'scriptedterrain', - condition: { - duration: 5, - durationCallback(source, effect) { - if (source?.hasItem('terrainextender')) { - return 8; - } - return 5; - }, - onBasePower(basePower, attacker, defender, move) { - if (move.type === 'Bug') { - this.debug('scripted terrain boost'); - return this.chainModify(1.5); - } - }, - onTryHitPriority: 4, - onTryHit(target, source, effect) { - if (!effect || effect.id === 'glitchout' || source.volatiles['glitchout']) return; - if (this.random(20) === 1) { - this.add('message', `${source.illusion ? source.illusion.name : source.name}'s move was glitched by the Scripted Terrain!`); - this.useMove('Glitch Out', source, source.side.foe.active[0]); - return null; - } - }, - onStart(battle, source, effect) { - if (effect && effect.effectType === 'Ability') { - this.add('-fieldstart', 'move: Scripted Terrain', '[from] ability: ' + effect, '[of] ' + source); - } else { - this.add('-fieldstart', 'move: Scripted Terrain'); - } - this.add('-message', 'The battlefield got Technical!'); - }, - onResidualOrder: 21, - onResidualSubOrder: 2, - onResidual() { - this.eachEvent('Terrain'); - }, - onTerrain(pokemon) { - if (pokemon.species.id === 'missingno') return; - if (pokemon.fainted || !pokemon.hp) return; - if (this.random(20) === 1) { - this.debug('Scripted terrain corrupt'); - this.add('message', `${pokemon.name} was corrupted by a bug in the Scripted Terrain!`); - // generate a movepool - const moves = []; - const pool = Object.keys(this.dex.data.Moves); - this.prng.shuffle(pool); - const metronome = this.dex.getMove('metronome'); - for (const id of pool) { - const move = this.dex.getMove(id); - if (move.realMove) continue; - if (move.isZ || move.isNonstandard) continue; - if (metronome.noMetronome && metronome.noMetronome.includes(move.name)) continue; - if (this.dex.getMove(id).gen > this.gen) continue; - moves.push(move); - if (moves.length >= 3) break; - } - moves.push('glitchout'); - if (this.toID(pokemon.ability).includes('illusion') && pokemon.illusion) { - this.singleEvent('End', this.dex.getAbility('Illusion'), pokemon.abilityData, pokemon, pokemon); - } - pokemon.formeChange('missingno'); - pokemon.moveSlots = []; - for (const moveid of moves) { - const move = this.dex.getMove(moveid); - if (!move.id) continue; - pokemon.moveSlots.push({ - move: move.name, - id: move.id, - pp: 5, - maxpp: 5, - target: move.target, - disabled: false, - used: false, - virtual: true, - }); - } - } - }, - onEnd() { - this.add('-fieldend', 'move: Scripted Terrain'); - this.add('-message', 'The battlefield is no longer Technical.'); - }, - }, - secondary: null, - target: "all", - type: "Psychic", - }, - // Used by HoeenHero's terrain - glitchout: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "A random move is selected for use, other than After You, Assist, Baneful Bunker, Beak Blast, Belch, Bestow, Celebrate, Chatter, Copycat, Counter, Covet, Crafty Shield, Destiny Bond, Detect, Diamond Storm, Endure, Feint, Fleur Cannon, Focus Punch, Follow Me, Freeze Shock, Helping Hand, Hold Hands, Hyperspace Hole, Ice Burn, Instruct, King's Shield, Light of Ruin, Mat Block, Me First, Metronome, Mimic, Mind Blown, Mirror Coat, Mirror Move, Nature Power, Photon Geyser, Plasma Fists, Protect, Quash, Quick Guard, Rage Powder, Relic Song, Secret Sword, Shell Trap, Sketch, Sleep Talk, Snarl, Snatch, Snore, Spectral Thief, Spiky Shield, Spotlight, Steam Eruption, Struggle, Switcheroo, Techno Blast, Thief, Thousand Arrows, Thousand Waves, Transform, Trick, Trump Card, V-create, or Wide Guard. The selected move's Base Power is increased by 20.", - shortDesc: "Uses a random move with Base Power +20.", - name: "Glitch Out", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {}, - noMetronome: [ - "After You", "Assist", "Baneful Bunker", "Beak Blast", "Belch", "Bestow", "Celebrate", "Chatter", "Copycat", "Counter", "Covet", "Crafty Shield", "Destiny Bond", "Detect", "Diamond Storm", "Dragon Ascent", "Endure", "Feint", "Fleur Cannon", "Focus Punch", "Follow Me", "Freeze Shock", "Helping Hand", "Hold Hands", "Hyperspace Fury", "Hyperspace Hole", "Ice Burn", "Instruct", "King's Shield", "Light of Ruin", "Mat Block", "Me First", "Metronome", "Mimic", "Mind Blown", "Mirror Coat", "Mirror Move", "Nature Power", "Origin Pulse", "Photon Geyser", "Plasma Fists", "Precipice Blades", "Protect", "Quash", "Quick Guard", "Rage Powder", "Relic Song", "Secret Sword", "Shell Trap", "Sketch", "Sleep Talk", "Snarl", "Snatch", "Snore", "Spectral Thief", "Spiky Shield", "Spotlight", "Steam Eruption", "Struggle", "Switcheroo", "Techno Blast", "Thief", "Thousand Arrows", "Thousand Waves", "Transform", "Trick", "Trump Card", "V-create", "Wide Guard", - ], - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Bug Buzz', source); - this.add('-anim', source, 'Metronome', source); - source.addVolatile('glitchout'); - }, - onHit(target, source, effect) { - const moves = []; - for (const id in this.dex.data.Moves) { - const move = this.dex.data.Moves[id]; - if (move.realMove) continue; - if (move.isZ || move.isNonstandard) continue; - if (effect.noMetronome && effect.noMetronome.includes(move.name)) continue; - if (this.dex.getMove(id).gen > this.gen) continue; - moves.push(move); - } - let randomMove = ''; - if (moves.length) { - moves.sort((a, b) => (a.num || 0) - (b.num || 0)); - randomMove = this.sample(moves).name; - } - if (!randomMove) { - return false; - } - this.useMove(randomMove, target); - }, - secondary: null, - target: "self", - type: "Bug", - }, - // Hubriz - flowertornado: { - accuracy: 90, - basePower: 95, - category: "Special", - desc: "Has a 20% chance to either poison the target or cause it to fall asleep.", - shortDesc: "20% chance to either poison or sleep target.", - name: "Flower Tornado", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {protect: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Petal Blizzard", target); - this.add('-anim', source, "Leaf Tornado", target); - }, - secondary: { - chance: 20, - onHit(target, source) { - const result = this.random(2); - if (result === 0) { - target.trySetStatus('psn', source); - } else { - target.trySetStatus('slp', source); - } - }, - }, - target: "normal", - type: "Grass", - }, - // inactive - petrifyinggaze: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "Raises the user's Attack by two stages. Traps and paralyzes the foe.", - shortDesc: "Raises user's Atk by 2; traps and paralyzes foe.", - name: "Petrifying Gaze", - isNonstandard: "Custom", - pp: 1, - priority: 0, - flags: {}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Mean Look", target); - }, - onHit(target, source, move) { - this.boost({atk: 2}, source, source); - target.trySetStatus('par', source); - return target.addVolatile('trapped', source, move, 'trapper'); - }, - isZ: "dusknoiriumz", - secondary: null, - target: "normal", - type: "Ghost", - }, - // irritated - pureskill: { - accuracy: 100, - basePower: 80, - category: "Physical", - desc: "Has a 30% chance to paralyze the target and a 10% chance to confuse it.", - shortDesc: "30% chance to paralyze. 10% chance to confuse.", - name: "Pure Skill", - isNonstandard: "Custom", - pp: 10, - priority: 0, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Heart Stamp', target); - }, - flags: {protect: 1, mirror: 1}, - secondaries: [ - { - chance: 30, - status: 'par', - }, { - chance: 10, - volatileStatus: 'confusion', - }, - ], - target: "normal", - type: "Psychic", - }, - // Iyarito - rosarosa: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "Cures the user's party of all status conditions, then poisons the user.", - shortDesc: "Cures party's statuses, then poisons self.", - name: "Rosa Rosa", - isNonstandard: "Custom", - pp: 5, - priority: 0, - flags: {mirror: 1, snatch: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Acid Armor', source); - }, - onHit(pokemon, source, move) { - // this.add('-activate', source, 'move: Víbora'); - let success = false; - for (const ally of pokemon.side.pokemon) { - if (ally.cureStatus()) success = true; - } - if (pokemon.trySetStatus('psn', pokemon)) success = true; - return success; - }, - secondary: null, - target: "allyTeam", - type: "Poison", - }, - // Kaiju Bunny - bestialstrike: { - accuracy: 100, - basePower: 150, - basePowerCallback(pokemon, target, move) { - return move.basePower * pokemon.hp / pokemon.maxhp; - }, - category: "Physical", - desc: "Power is equal to (user's current HP * 150 / user's maximum HP), rounded down, but not less than 1.", - shortDesc: "Less power as user's HP decreases.", - name: "Bestial Strike", - isNonstandard: "Custom", - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Outrage', target); - }, - pp: 5, - priority: 0, - flags: {protect: 1, mirror: 1, contact: 1}, - secondary: null, - target: "normal", - type: "Flying", - }, - // kalalokki - maelstrm: { - accuracy: 85, - basePower: 100, - category: "Special", - desc: "Prevents the target from switching for four or five turns (seven turns if the user is holding Grip Claw). Causes damage to the target equal to 1/8 of its maximum HP (1/6 if the user is holding Binding Band), rounded down, at the end of each turn during effect. Both of these effects persist for their normal duration even if the user switches out or faints. The target can still switch out if it is holding Shed Shell or uses Baton Pass, Parting Shot, U-turn, or Volt Switch. The effect ends if the target leaves the field or uses Rapid Spin or Substitute successfully. This effect is not stackable or reset by using this or another binding move.", - shortDesc: "Traps/damages 4-5 turns, even if user switches.", - name: "Maelström", - isNonstandard: "Custom", - pp: 5, - priority: 0, - flags: {protect: 1, mirror: 1}, - volatileStatus: 'maelstrm', - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Dark Void', target); - this.add('-anim', source, 'Surf', target); - }, condition: { - duration: 5, - durationCallback(target, source) { - if (source.hasItem('gripclaw')) { - this.debug('maelstrm grip claw duration boost'); - return 8; - } - return this.random(5, 7); - }, + duration: 2, onStart() { - this.add('-message', 'It became trapped in an enormous maelström!'); + this.effectData.multiplier = 1; }, - onResidualOrder: 11, - onResidual(pokemon) { - if (this.effectData.source.hasItem('bindingband')) { - this.debug('maelstrm binding band damage boost'); - this.damage(pokemon.baseMaxhp / 6); - } else { - this.damage(pokemon.baseMaxhp / 8); + onRestart() { + if (this.effectData.duration !== 2) { + this.effectData.duration = 2; + if (this.effectData.multiplier < 5) { + this.effectData.multiplier++; + } } }, - onEnd() { - this.add('-message', 'The maelström dissipated.'); - }, - onTrapPokemon(pokemon) { - pokemon.tryTrap(); - }, }, secondary: null, target: "normal", - type: "Water", - }, - // kaori - w: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "Raises the user's Special Attack, Special Defense, and Speed by one stage. Summons Sunny Day.", - shortDesc: "User's SpA, SpD, Spe +1. Sets Sunny Day.", - name: ">w<", - isNonstandard: "Custom", - pp: 5, - priority: 0, - flags: {snatch: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Petal Dance', source); - }, - onHit(target, source) { - this.field.setWeather('sunnyday', source); - }, - boosts: { - spa: 1, - spd: 1, - spe: 1, - }, - secondary: null, - target: "self", - type: "Grass", - }, - // Kay - inkzooka: { - accuracy: 100, - basePower: 80, - category: "Physical", - desc: "Lowers the user's Defense, Special Defense, and Speed by one stage.", - shortDesc: "Lowers the user's Def, Sp. Def, and Spe by 1.", - name: "Inkzooka", - isNonstandard: "Custom", - pp: 5, - priority: 0, - flags: {protect: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Never Ending Nightmare', target); - }, - self: { - boosts: { - def: -1, - spd: -1, - spe: -1, - }, - }, - secondary: null, - target: "normal", - type: "Psychic", - }, - // Kie - chaotic: { - accuracy: 100, - basePower: 70, - category: "Physical", - desc: "Before the user attacks, its Attack is increased by one stage. After the user attacks, one of its stats other than accuracy and evasion is boosted by one stage. This move has a 50% chance to confuse the target.", - shortDesc: "Atk +1, attacks. Random stat +1. 50% confuse.", - name: "Chaotic", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {protect: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.boost({atk: 1}, source); - this.add('-anim', source, "Dragon Dance", source); - this.add('-anim', source, "Liquidation", target); - }, - - onAfterHit(target, source) { - const boost: SparseBoostsTable = {}; - const stats: BoostName[] = ['atk', 'def', 'spa', 'spd', 'spe']; - const stat: BoostName = stats[this.random(5)]; - boost[stat] = 1; - this.boost(boost, source); - }, - secondary: { - chance: 50, - volatileStatus: "confusion", - }, - target: "normal", - type: "Water", - }, - // KingSwordYT - dragonwarriortouch: { - accuracy: 100, - basePower: 70, - category: "Physical", - desc: "The user recovers 1/2 the HP lost by the target, rounded half up. Raises the user's Attack by one stage.", - shortDesc: "User recovers 50% of the damage dealt; Atk +1.", - name: "Dragon Warrior Touch", - isNonstandard: "Custom", - pp: 5, - priority: 0, - flags: {protect: 1, mirror: 1, punch: 1, contact: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Outrage', target); - this.add('-anim', source, 'Drain Punch', target); - }, - self: { - boosts: { - atk: 1, - }, - }, - drain: [1, 2], - target: "normal", - type: "Fighting", - }, - // Kipkluif - salutethecolonel: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "The user is protected from most attacks made by other Pokemon during this turn, and Pokemon trying to make contact with the user have their Attack lowered by 2 stages. Non-damaging moves go through this protection. This move has a 1/X chance of being successful, where X starts at 1 and triples each time this move is successfully used. X resets to 1 if this move fails, if the user's last move used is not Baneful Bunker, Detect, Endure, King's Shield, Protect, Quick Guard, Spiky Shield, or Wide Guard, or if it was one of those moves and the user's protection was broken. The user also swaps its Defense and Special Defense stat stage changes with the target. Fails if the user moves last this turn.", - shortDesc: "Blocks attacks; contact: Atk -2; Guard Swap.", - isNonstandard: "Custom", - name: "Salute the Colonel", - pp: 15, - priority: 4, - flags: {}, - stallingMove: true, - volatileStatus: 'kingsshield', - onTryMove() { - this.attrLastMove('[still]'); - }, - onTryHit(pokemon) { - return !!this.queue.willAct() && this.runEvent('StallMove', pokemon); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "King's Shield", source); - this.add('-anim', source, "Guard Swap", target); - }, - onHit(pokemon) { - pokemon.addVolatile('stall'); - this.add(`c|+Kipkluif|o7`); - const target = pokemon.side.foe.active[0]; - if (!target) return; - const targetBoosts: SparseBoostsTable = {}; - const sourceBoosts: SparseBoostsTable = {}; - - const defSpd: BoostName[] = ['def', 'spd']; - let stat: BoostName; - for (stat of defSpd) { - targetBoosts[stat] = target.boosts[stat]; - sourceBoosts[stat] = pokemon.boosts[stat]; - } - - pokemon.setBoost(targetBoosts); - target.setBoost(sourceBoosts); - - this.add('-swapboost', pokemon, target, 'def, spd', '[from] move: Guard Swap'); - }, - secondary: null, - target: "self", - type: "Fighting", - }, - // Kris - ectoplasm: { - accuracy: 100, - basePower: 95, - category: "Special", - desc: "This move's type is equal to the user's secondary type. Has a 10% chance to lower the user's Special Defense by 1.", - shortDesc: "Attack is user's 2nd type. 10% SpD -1.", - isNonstandard: "Custom", - name: "Ectoplasm", - pp: 15, - priority: 0, - flags: {protect: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onTry(pokemon, target) { - if (pokemon.types[1] === undefined) { - this.add('-fail', pokemon); - return null; - } - }, - onModifyMove(move, pokemon) { - move.type = pokemon.types[1]; - }, - onPrepareHit(target, source) { - let move = 'Tri Attack'; - switch (source.types[1]) { - case 'Ghost': - move = 'Moongeist Beam'; - break; - case 'Flying': - move = 'Hurricane'; - break; - case 'Fire': - move = 'Blast Burn'; - break; - case 'Water': - move = 'Hydro Cannon'; - break; - case 'Grass': - move = 'Frenzy Plant'; - break; - case 'Ice': - move = 'Sheer Cold'; - break; - } - this.add('-anim', source, move, target); - }, - secondary: { - chance: 10, - boosts: { - spd: -1, - }, - }, - target: "normal", type: "Normal", }, - // Level 51 - nextlevelstrats: { + + // Felucia + riggeddice: { accuracy: true, basePower: 0, category: "Status", - desc: "The user gains 5 levels upon using this move, which persist upon switching out.", - shortDesc: "User gains 5 levels.", - name: "Next Level Strats", - isNonstandard: "Custom", - pp: 5, - priority: 0, - flags: {snatch: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Nasty Plot", target); - }, - onHit(pokemon) { - const species = pokemon.species; - const level = pokemon.level + 5; - (pokemon as any).level = level; - pokemon.set.level = level; - pokemon.formeChange(species); - - pokemon.details = species.name + (level === 100 ? '' : ', L' + level) + - (pokemon.gender === '' ? '' : ', ' + pokemon.gender) + (pokemon.set.shiny ? ', shiny' : ''); - this.add('detailschange', pokemon, pokemon.details); - - const newHP = Math.floor(Math.floor( - 2 * species.baseStats['hp'] + pokemon.set.ivs['hp'] + Math.floor(pokemon.set.evs['hp'] / 4) + 100 - ) * level / 100 + 10); - pokemon.hp = newHP - (pokemon.maxhp - pokemon.hp); - pokemon.maxhp = newHP; - this.add('-heal', pokemon, pokemon.getHealth, '[silent]'); - - this.add('-message', `${pokemon.name} advanced 5 levels! It is now level ${level}!`); - }, - secondary: null, - target: "self", - type: "Normal", - - }, - // LifeisDANK - barfight: { - accuracy: 100, - basePower: 10, - category: "Physical", - desc: "Raises both the user's and the target's Attack by three stages, lowers the Defense of both by three stages, confuses both Pokemon, and has a 100% chance to cause the target to flinch. Only works on the user's first turn on the field.", - shortDesc: "First turn: +3 Atk, -3 Def, flinch, both confused.", - name: "Bar Fight", - isNonstandard: "Custom", - pp: 10, - priority: 3, - flags: {protect: 1, mirror: 1, contact: 1}, - onTry(pokemon, target) { - if (pokemon.activeMoveActions > 1) { - this.attrLastMove('[still]'); - this.add('-fail', pokemon); - this.hint("Bar Fight only works on your first turn out."); - return null; - } - }, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Fake Out", target); - this.add('-anim', source, "Feather Dance", target); - }, - onHit(target, source) { - this.boost({atk: 3, def: -3}, target); - this.boost({atk: 3, def: -3}, source); - target.addVolatile('confusion'); - source.addVolatile('confusion'); - target.addVolatile('flinch'); - }, - secondary: null, - target: "normal", - type: "Flying", - }, - // Lost Seso - shuffleramendance: { - accuracy: 100, - basePower: 80, - category: "Special", - desc: "This move's type effectiveness is inverted, meaning that it's super effective on Water-types but not very effective on Grass-types, and so forth. 20% chance to paralyze the target.", - shortDesc: "Type effectiveness is inverted; 20% paralyze.", - name: "Shuffle Ramen Dance", + desc: "Inverts target's stat boosts if they have any; taunts otherwise. User then switches out.", + shortDesc: "If target has boosts, invert; else, taunt. Switch out.", + name: "Rigged Dice", isNonstandard: "Custom", + gen: 8, pp: 10, priority: 0, - flags: {protect: 1, mirror: 1, dance: 1}, + flags: {protect: 1, reflectable: 1}, onTryMove() { this.attrLastMove('[still]'); }, onPrepareHit(target, source) { - this.add('-anim', source, 'Outrage', target); - }, - onEffectiveness(typeMod, target) { - return -typeMod; - }, - secondary: { - status: 'par', - chance: 20, - }, - target: "normal", - type: "Fire", - zMove: {basePower: 160}, - }, - // MacChaeger - naptime: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "The user falls asleep for the next turn, restoring 50% of its HP and curing itself of any major status condition. If the user falls asleep in this way, all other active Pokemon that are not asleep or frozen also try to use Nap Time. Fails if the user has full HP, if the user is already asleep, or if another effect is preventing sleep.", - shortDesc: "Active Pokemon sleep 1 turn, restoring HP/status.", - name: "Nap Time", - isNonstandard: "Custom", - pp: 5, - priority: 0, - flags: {snatch: 1, heal: 1}, - onTryMove(pokemon) { - this.attrLastMove('[still]'); - if (pokemon.hp < pokemon.maxhp && pokemon.status !== 'slp' && !pokemon.hasAbility('comatose')) return; - this.add('-fail', pokemon); - this.hint("Nap Time fails if the user has full health, is already asleep, or has Comatose."); - return null; - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Rest", target); - this.add('-anim', source, "Aromatic Mist", target); + this.add('-anim', source, 'Smart Strike', source); }, onHit(target, source, move) { - const napWeather = this.field.pseudoWeather['naptime']; - // Trigger sleep clause if not the original user - if (target !== napWeather.source) { - for (const ally of target.side.pokemon) { - if (ally.status === 'slp') { - if (!(ally.statusData.source && ally.statusData.source.side === ally.side)) return false; - } - } - } - if (!target.setStatus('slp', napWeather.source, move)) return false; - target.statusData.time = 2; - target.statusData.startTime = 2; - this.heal(target.baseMaxhp / 2); // Aesthetic only as the healing happens after you fall asleep in-game - if (napWeather.source === target) { - for (const curMon of this.getAllActive()) { - if (curMon === source) continue; - if (curMon.status !== 'slp' && curMon.status !== 'frz' && !curMon.hasAbility('comatose')) { - this.add('-anim', source, "Yawn", curMon); - this.useMove(move, curMon, curMon, move); - } - } - } - this.field.removePseudoWeather('naptime'); - }, - pseudoWeather: 'naptime', - condition: { - duration: 1, - }, - target: "self", - type: "Fairy", - zMove: {effect: 'clearnegativeboosts'}, - }, - // Mad Monty ¾° - llamacide: { - accuracy: 100, - basePower: 80, - category: "Physical", - desc: "Has a 40% chance to lower target's Defense by one stage and a 10% chance to freeze it.", - shortDesc: "40% target's Def -1. 10% chance of freeze.", - name: "Llamacide", - pp: 10, - priority: 0, - flags: {protect: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Nasty Plot", source); - this.add('-anim', source, "Plasma Fists", target); - this.add('-anim', source, "Sheer Cold", target); - }, - secondaries: [ - { - status: "frz", - chance: 10, - }, - { - chance: 40, - boosts: { - def: -1, - }, - }, - ], - target: "normal", - type: "Ice", - }, - // MajorBowman - blazeofglory: { - accuracy: true, - basePower: 0, - damageCallback(pokemon, target) { - const damage = pokemon.hp; - pokemon.faint(); - if ( - target.volatiles['banefulbunker'] || target.volatiles['kingsshield'] || - target.side.sideConditions['matblock'] || target.volatiles['protect'] || target.volatiles['spikyshield'] || - target.volatiles['lilypadshield'] || target.volatiles['backoffgrrr'] - ) { - this.add('-zbroken', target); - return Math.floor(damage / 4); - } - return damage; - }, - category: "Physical", - desc: "The user's HP is restored to maximum, and the user then faints. The target then takes damage equal to the amount of HP the user lost. This move does not check accuracy.", - shortDesc: "Does damage equal to user's max HP. User faints.", - name: "Blaze of Glory", - isNonstandard: "Custom", - pp: 1, - priority: 0, - flags: {}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Recover", source); - this.heal(source.maxhp, source, source, this.dex.getActiveMove('Blaze of Glory')); - this.add('-anim', source, "Final Gambit", target); - }, - selfdestruct: "ifHit", - isZ: "victiniumz", - secondary: null, - target: "normal", - type: "Fire", - }, - // Marshmallon - weatherforecast: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "The user heals 1/4 of their HP rounded down, is protected from most attacks made by other Pokemon during this turn, and changes the weather. The selected weather depends on the current weather. Rain: Hail, Sun: Rain, Hail: Sun, Other: Randomly pick one of Rain, Sun, and Hail. This move has a 1/X chance of being successful, where X starts at 1 and triples each time this move is successfully used. X resets to 1 if this move fails, if the user's last move used is not Baneful Bunker, Detect, Endure, King's Shield, Protect, Quick Guard, Spiky Shield, or Wide Guard, or if it was one of those moves and the user's protection was broken. Fails if the user moves last this turn.", - shortDesc: "Protect + Heal 1/4 + change weather.", - name: "Weather Forecast", - isNonstandard: "Custom", - pp: 10, - priority: 2, - flags: {heal: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Protect", source); - this.add('-anim', source, "Quiver Dance", source); - const result = !!this.queue.willAct() && this.runEvent('StallMove', source); - return result; - }, - onHit(target, source) { - let didSomething = false; - switch (this.field.weather) { - case 'raindance': - if (this.field.setWeather('hail', source)) didSomething = true; - break; - case 'sunnyday': - if (this.field.setWeather('raindance', source)) didSomething = true; - break; - case 'hail': - if (this.field.setWeather('sunnyday', source)) didSomething = true; - break; - default: - if (this.field.setWeather(['raindance', 'sunnyday', 'hail'][this.random(3)], source)) didSomething = true; - } - if (this.heal(source.baseMaxhp / 4, source)) didSomething = true; - if (source.addVolatile('stall')) didSomething = true; - if (source.addVolatile('protect')) didSomething = true; - return didSomething; - }, - secondary: null, - target: "self", - type: "Normal", - }, - // martha - crystalboost: { - accuracy: 90, - basePower: 75, - category: "Special", - desc: "Has a 50% chance to raise the user's Special Attack by one stage.", - shortDesc: "50% chance to raise the user's SpA by 1.", - name: "Crystal Boost", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {protect: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Power Gem", target); - }, - secondary: { - chance: 50, - self: { - boosts: { - spa: 1, - }, - }, - }, - target: "normal", - type: "Rock", - }, - // Marty - typeanalysis: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "If the user is a Silvally, its item becomes a random Memory whose type matches one of the target's weaknesses, it changes forme, and it uses Multi-Attack. This move and its effects ignore the abilities of other Pokemon. Fails if the target has no weaknesses or if the user's species is not Silvally.", - shortDesc: "Changes user/move type to target's weakness.", - name: "Type Analysis", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {authentic: 1, protect: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Conversion", source); - }, - onHit(target, source) { - if (source.baseSpecies.baseSpecies !== 'Silvally') return false; - let targetTypes = target.getTypes(true).filter(type => type !== '???'); - if (!targetTypes.length) { - if (target.addedType) { - targetTypes = ['Normal']; - } else { - return false; - } - } - const weaknesses = []; - for (const type in this.dex.data.TypeChart) { - const typeMod = this.dex.getEffectiveness(type, targetTypes); - if (typeMod > 0 && this.dex.getImmunity(type, target)) weaknesses.push(type); - } - if (!weaknesses.length) { - return false; - } - const randomType = this.sample(weaknesses); - source.setItem(randomType + 'memory'); - this.add('-item', source, source.getItem(), '[from] move: Type Analysis'); - const species = this.dex.getSpecies('Silvally-' + randomType); - source.formeChange(species, this.dex.getAbility('rkssystem'), true); - const move = this.dex.getActiveMove('multiattack'); - move.basePower = 80; - this.useMove(move, source, target); - }, - secondary: null, - target: "normal", - type: "Normal", - zMove: {effect: 'heal'}, - }, - // Meicoo - scavengesu: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "Lowers the user's Attack and Special Attack by two stages and then swaps all of its stat changes with the target.", - shortDesc: "Atk/SpA -2, then swaps all stats with foe.", - name: "/scavenges u", - isNonstandard: "Custom", - pp: 5, - priority: 0, - flags: {mirror: 1, protect: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Imprison", source); - this.add('-anim', source, "Miracle Eye", target); - }, - onHit(target, source) { - this.boost({atk: -2, spa: -2}, source, source, this.dex.getActiveMove('/scavenges u')); - const targetBoosts: SparseBoostsTable = {}; - const sourceBoosts: SparseBoostsTable = {}; - - let i: BoostName; - for (i in target.boosts) { - targetBoosts[i] = target.boosts[i]; - sourceBoosts[i] = source.boosts[i]; - } - - target.setBoost(sourceBoosts); - source.setBoost(targetBoosts); - - this.add(`c|%Meicoo|cool quiz`); - - this.add('-swapboost', source, target, '[from] move: /scavenges u'); - }, - secondary: null, - target: "normal", - type: "Psychic", - }, - // Megazard - tippingover: { - accuracy: 100, - basePower: 20, - basePowerCallback(pokemon, target, move) { - return move.basePower + 20 * pokemon.positiveBoosts(); - }, - category: "Physical", - desc: "Base Power rises by 20 for each of the user's positive stat stage changes. The user loses any defensive boosts not from Stockpile.", - shortDesc: "+20 BP per boost. Lose non-Stockpile +Def/SpD.", - name: "Tipping Over", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {protect: 1, mirror: 1, contact: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Dragon Hammer", target); - this.add('-anim', target, "Earthquake", target); - }, - onAfterMoveSecondarySelf(pokemon) { - let stockpileLayers = 0; - if (pokemon.volatiles['stockpile']) stockpileLayers = pokemon.volatiles['stockpile'].layers; - const boosts: SparseBoostsTable = {}; - if (pokemon.boosts.def > stockpileLayers) boosts.def = stockpileLayers - pokemon.boosts.def; - if (pokemon.boosts.spd > stockpileLayers) boosts.spd = stockpileLayers - pokemon.boosts.spd; - if (boosts.def || boosts.spd) this.boost(boosts, pokemon, pokemon); - }, - secondary: null, - target: "normal", - type: "???", - }, - // Mitsuki - pythonivy: { - accuracy: 95, - basePower: 110, - category: "Special", - desc: "Lowers the user's Special Attack, Special Defense, and Speed by one stage.", - shortDesc: "Lowers the user's SpA, SpD, and Spe by 1.", - name: "Python Ivy", - isNonstandard: "Custom", - pp: 5, - priority: 0, - flags: {protect: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Leaf Tornado", target); - this.add('-anim', source, "Leaf Storm", target); - }, - self: { - boosts: { - spa: -1, - spd: -1, - spe: -1, - }, - }, - secondary: null, - target: "normal", - type: "Grass", - }, - // Morfent - e: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "If Trick Room is not already in play, sets Trick Room for 5 turns and raises the user's Attack by one stage.", - shortDesc: "User Attack +1; sets Trick Room.", - name: "E", - isNonstandard: "Custom", - pp: 5, - priority: -6, - onModifyMove(move) { - if (!this.field.pseudoWeather.trickroom) { - move.pseudoWeather = 'trickroom'; - } - }, - flags: {snatch: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Recover", source); - this.add('-anim', source, "Nasty Plot", source); - }, - boosts: { - atk: 1, - }, - secondary: null, - target: "self", - type: "Ghost", - }, - // Used for nui's ability - prismaticterrain: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "For 5 turns, the terrain becomes Prismatic Terrain. During the effect, the power of Ice-type attacks is multiplied by 0.5, even if the user is not grounded. Hazards and screens are removed and cannot be set while Prismatic Terrain is active. Fails if the current terrain is Prismatic Terrain.", - shortDesc: "5 turns. No hazards, -Ice power even if floating.", - name: "Prismatic Terrain", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {}, - terrain: 'prismaticterrain', - condition: { - duration: 5, - durationCallback(source, effect) { - if (source?.hasItem('terrainextender')) { - return 8; - } - return 5; - }, - onBasePower(basePower, attacker, defender, move) { - if (move.type === 'Ice') { - this.debug('prismatic terrain weaken'); - return this.chainModify(0.5); - } - }, - onStart(battle, source, effect) { - if (effect && effect.effectType === 'Ability') { - this.add('-fieldstart', 'move: Prismatic Terrain', '[from] ability: ' + effect, '[of] ' + source); - } else { - this.add('-fieldstart', 'move: Prismatic Terrain'); - } - this.add('-message', 'The battlefield suddenly got a refractive high poly count!'); - const removeAll = [ - 'reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'spikes', 'toxicspikes', 'stealthrock', 'stickyweb', - ]; - for (const sideCondition of removeAll) { - if (source.side.foe.removeSideCondition(sideCondition)) { - this.add('-sideend', source.side.foe, this.dex.getEffect(sideCondition).name, '[from] move: Prismatic Terrain', '[of] ' + source); - } - if (source.side.removeSideCondition(sideCondition)) { - this.add('-sideend', source.side, this.dex.getEffect(sideCondition).name, '[from] move: Prismatic Terrain', '[of] ' + source); - } - } - }, - onResidualOrder: 21, - onResidualSubOrder: 2, - onEnd() { - this.add('-fieldend', 'move: Prismatic Terrain'); - this.add('-message', 'The battlefield no longer has a refractive high poly count!'); - }, - }, - secondary: null, - target: "all", - type: "Fairy", - }, - // nui - pyramidingsong: { - accuracy: 100, - basePower: 0, - category: "Status", - desc: "If the target has not fainted, both the user and the target are forced to switch out and be replaced with a chosen unfainted ally. The target's replacement has its Speed lowered by 1 stage. Fails if either Pokemon is under the effect of Ingrain or Suction Cups.", - shortDesc: "Both Pokemon switch. Opp. replacement: Spe -1.", - name: "Pyramiding Song", - isNonstandard: "Custom", - pp: 20, - priority: -6, - flags: {mirror: 1, protect: 1, authentic: 1, sound: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Freeze Dry", target); - this.add('-anim', source, "Mist", target); - }, - onTryHit(target, source, move) { - target.side.addSlotCondition(target, 'pyramidingsong'); - }, - onHit(target, source, move) { - if (this.runEvent('DragOut', source, target, move)) { - source.forceSwitchFlag = true; - } - }, - condition: { - duration: 1, - onSwitchIn(pokemon) { - this.boost({spe: -1}, pokemon, pokemon.side.foe.active[0], this.dex.getActiveMove('pyramidingsong')); - }, - }, - forceSwitch: true, - secondary: null, - target: "normal", - type: "Water", - zMove: {effect: "boostreplacement"}, - }, - // OM - omboom: { - accuracy: 95, - basePower: 110, - category: "Physical", - desc: "Has a 50% chance to raise the user's Speed by two stages and a 5% chance to raise the user's Attack by one stage. Only one will ever activate on a single use", - shortDesc: "50% chance of Spe +2 or 5% chance of Atk +1.", - name: "OM Boom", - isNonstandard: "Custom", - pp: 15, - priority: 0, - flags: {mirror: 1, protect: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Fire Lash", target); - this.add('-anim', source, "Heat Crash", target); - }, - onHit() { - this.add(`c|@OM|Bang Bang`); - }, - secondary: { - chance: 100, - onHit(target, source) { - if (this.random(2) === 0) { - this.boost({spe: 2}, source); - } else if (this.random(20) === 0) { - this.boost({atk: 1}, source); - } - }, - }, - target: "normal", - type: "Fire", - }, - // Overneat - ultimateslash: { - accuracy: 100, - basePower: 80, - category: "Physical", - desc: "If this attack does not miss, the effects of Reflect, Light Screen, and Aurora Veil end for the target's side of the field before damage is calculated. If the user has not fainted, the target loses its held item.", - shortDesc: "Destroys screens. Removes foe's item.", - name: "Ultimate Slash", - isNonstandard: "Custom", - pp: 5, - priority: 0, - flags: {contact: 1, protect: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Night Slash", target); - }, - onTryHit(pokemon) { - // will shatter screens through sub, before you hit - if (pokemon.runImmunity('Dark')) { - pokemon.side.removeSideCondition('reflect'); - pokemon.side.removeSideCondition('lightscreen'); - pokemon.side.removeSideCondition('auroraveil'); - } - }, - onAfterHit(target, source) { - if (source.hp) { - const item = target.takeItem(); - if (item) { - this.add('-enditem', target, item.name, '[from] move: Ultimate Slash', '[of] ' + source); - } - } - }, - secondary: null, - target: "normal", - type: "Dark", - }, - // Pablo - jailshell: { - accuracy: 90, - basePower: 90, - category: "Special", - desc: "This move has a 50% change to paralyze the target and prevents the target from switching out or using any moves that the user also knows while the user is active.", - shortDesc: "50% chance to paralyze. Traps and imprisons.", - name: "Jail Shell", - isNonstandard: "Custom", - pp: 5, - priority: 0, - flags: {protect: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Anchor Shot", target); - }, - onHit(target, source, move) { - if (source.isActive) target.addVolatile('trapped', source, move, 'trapper'); - source.addVolatile('imprison', source, move); - }, - secondary: { - chance: 50, - status: 'par', - }, - target: "normal", - type: "Normal", - }, - // Paradise - corrosivetoxic: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "Badly poisons the target, even if they are Poison-type or Steel-type. This move does not check accuracy.", - shortDesc: "Badly poisons the target, regardless of type.", - name: "Corrosive Toxic", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {protect: 1, reflectable: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Toxic", target); - }, - // Innate corrosive implemented in Scripts#setStatus - status: 'tox', - secondary: null, - target: "normal", - type: "Poison", - }, - // Pirate Princess - teabreak: { - accuracy: 100, - basePower: 0, - category: "Status", - desc: "The user attempts to use Toxic followed by Venoshock, then Rest and Sleep Talk.", - shortDesc: "Toxic -> Venoshock -> Rest -> Sleep Talk.", - name: "Tea Break", - pp: 5, - priority: 0, - flags: {protect: 1, authentic: 1}, - sleepUsable: true, - onTryMove(pokemon) { - this.attrLastMove('[still]'); - }, - onHit(target, source) { - this.useMove('Toxic', source, target); - this.useMove('Venoshock', source, target); - if (source.hp !== source.maxhp) this.useMove('Rest', source, source); - if (source.status === 'slp' || source.hasAbility('comatose')) this.useMove('Sleep Talk', source, target); - }, - secondary: null, - target: "normal", - type: "Fairy", - }, - // Used for PiratePrincess's ability - acidrain: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "For 5 turns, the weather becomes Acid Rain. Pokemon that are not Poison-type take damage every turn. Special Defense of Poison-type pokemon is multiplied by 1.5.", - shortDesc: "5 turns: +Poison SpD, corrosive damage.", - name: "Acid Rain", - isNonstandard: "Custom", - pp: 5, - priority: 0, - flags: {}, - weather: 'acidrain', - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Rain Dance', source); - }, - secondary: null, - target: "all", - type: "Poison", - }, - // pluviometer - grammarhammer: { - accuracy: 100, - basePower: 90, - category: "Special", - desc: "100% chance to burn the target.", - shortDesc: "100% chance to burn the target.", - name: "Grammar Hammer", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {contact: 1, protect: 1, mirror: 1, punch: 1}, - onPrepareHit(target, source) { - this.attrLastMove('[still]'); - this.add('-anim', source, "Hammer Arm", target); - }, - onHit(target, source) { - if (target.name === 'HoeenHero') { - this.add(`c|@pluviometer|HoennHero*`); - this.add(`c|~HoeenHero|I can speel`); - } - }, - secondary: { - chance: 100, - status: 'brn', - }, - target: "normal", - type: "Ghost", - }, - // Pohjis - greateqake: { - accuracy: true, - basePower: 200, - category: "Physical", - desc: "No additional effect.", - shortDesc: "No additional effect.", - name: "Great Eqake", - isNonstandard: "Custom", - pp: 1, - priority: 0, - flags: {}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Tectonic Rage", target); - }, - isZ: "marowakiumz", - secondary: null, - target: "normal", - type: "Ground", - }, - // pre - refactor: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "The user swaps all its stat stage changes with the target, then takes 1/4 of its maximum HP, rounded down, and puts it into a substitute to take its place in battle.", - shortDesc: "Swaps all stat changes with target + Substitute.", - name: "Refactor", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {protect: 1, mirror: 1, authentic: 1, mystery: 1, snatch: 1, nonsky: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Skill Swap', target); - }, - onTryHit(target, source) { - if (source.volatiles['substitute']) { - this.add('-fail', source, 'move: Refactor'); - return null; - } - if (source.hp <= source.maxhp / 4) { - this.add('-fail', source, 'move: Refactor', '[weak]'); - return null; - } - }, - onHit(target, source) { - const targetBoosts: SparseBoostsTable = {}; - const sourceBoosts: SparseBoostsTable = {}; - - let i: BoostName; - for (i in target.boosts) { - targetBoosts[i] = target.boosts[i]; - sourceBoosts[i] = source.boosts[i]; - } - - target.setBoost(sourceBoosts); - source.setBoost(targetBoosts); - - this.add('-swapboost', source, target, '[from] move: Refactor'); - this.directDamage(source.maxhp / 4, source); - }, - self: { - volatileStatus: 'substitute', - }, - secondary: null, - target: "normal", - type: "Psychic", - }, - // ptoad - lilypadshield: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "The user is protected from most moves made by other Pokemon during this turn, and if a Pokemon makes contact with the user, the user restores 1/4 of its maximum HP, rounded half up. This move has a 1/X chance of being successful, where X starts at 1 and doubles each time this move is successfully used. X resets to 1 if this move fails, if the user's last move used is not Baneful Bunker, Detect, Endure, King's Shield, Protect, Quick Guard, Spiky Shield, Wide Guard, or this move, or if it was one of those moves and the user's protection was broken. Fails if the user moves last this turn.", - shortDesc: "Protects from moves. Contact: restores 25% HP.", - name: "Lilypad Shield", - isNonstandard: "Custom", - pp: 10, - priority: 4, - flags: {heal: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Spiky Shield", source); - }, - stallingMove: true, - volatileStatus: 'lilypadshield', - onTryHit(target, source, move) { - return !!this.queue.willAct() && this.runEvent('StallMove', target); - }, - onHit(pokemon) { - pokemon.addVolatile('stall'); - }, - condition: { - duration: 1, - onStart(target) { - this.add('-singleturn', target, 'move: Protect'); - }, - onTryHitPriority: 3, - onTryHit(target, source, move) { - if (!move.flags['protect']) { - if (move.isZ || move.isMax) target.getMoveHitData(move).zBrokeProtect = true; - return; - } - this.add('-activate', target, 'move: Protect'); - const lockedmove = source.getVolatile('lockedmove'); - if (lockedmove) { - // Outrage counter is reset - if (source.volatiles['lockedmove'].duration === 2) { - delete source.volatiles['lockedmove']; - } - } - if (move.flags['contact']) { - this.heal(target.baseMaxhp / 4, target, target); - } - return null; - }, - onHit(target, source, move) { - if (move.isZOrMaxPowered && move.flags['contact']) { - this.heal(target.baseMaxhp / 4, target, target); - } - }, - }, - secondary: null, - target: "self", - type: "Grass", - }, - // Psynergy - resolve: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "Raises the user's Speed by one stage. Gives Focus Energy.", - shortDesc: "Raises user's Speed by 1; Focus Energy.", - name: "Resolve", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {snatch: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Acupressure", source); - this.add('-anim', source, "Flare Blitz", source); - }, - onHit(target, source) { - source.addVolatile('focusenergy', source); - }, - boosts: { - spe: 1, - }, - target: "self", - type: "Fighting", - }, - // Quite Quiet - literallycheating: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "For seven turns, any Pokemon that has one of their stats boosted through any manner loses all PP on the last move they used.", - shortDesc: "7 turns: boosting stat: lose all PP from last move.", - name: "Literally Cheating", - isNonstandard: "Custom", - pp: 5, - priority: 0, - flags: {}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Genesis Supernova", source); - }, - pseudoWeather: 'literallycheating', - condition: { - duration: 7, - onBoost(boost, target, source, effect) { - let positiveBoost = false; - const values = Object.values(boost); - for (const i of values) { - if (i !== undefined && i > 0) { - positiveBoost = true; - break; - } - } - if (!positiveBoost || !target.lastMove) return; - for (const moveSlot of target.moveSlots) { - if (moveSlot.id === target.lastMove.id) { - target.deductPP(moveSlot.id, moveSlot.pp); - } - } - this.add('-activate', target, 'move: Literally Cheating', target.lastMove.name, target.lastMove.pp); - this.add('-message', `${target.name} lost all PP for the move ${target.lastMove.name}!`); - }, - onStart(battle, source, effect) { - this.add('-fieldstart', 'move: Literally Cheating'); - }, - onResidualOrder: 21, - onResidualSubOrder: 2, - onEnd() { - this.add('-fieldend', 'move: Literally Cheating'); - }, - }, - secondary: null, - target: "all", - type: "Ghost", - }, - // Rach - stunner: { - accuracy: 85, - basePower: 95, - category: "Physical", - desc: "Has a 50% chance to raise the user's Attack by one stage and a 20% chance to paralyze the foe or cause them to flinch.", - shortDesc: "50% user's Atk +1. 20% flinch or paralyze foe.", - name: "Stunner", - pp: 10, - priority: 0, - isNonstandard: "Custom", - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Zen Headbutt", target); - }, - flags: {protect: 1, mirror: 1, contact: 1}, - secondaries: [ - { - chance: 50, - self: { - boosts: {atk: 1}, - }, - }, { - chance: 20, - onHit(target, source) { - const result = this.random(2); - if (result === 0) { - target.trySetStatus('par', source); - } else { - target.addVolatile('flinch', source); - } - }, - }, - ], - target: "normal", - type: "Electric", - }, - // Rage - rageeeee: { - accuracy: 100, - basePower: 40, - category: "Physical", - desc: "The user becomes affected with the effects of Rage and Endure. The opponent's next attack will hit 2 to 5 times with a Base Power of 25.", - shortDesc: "Rage + Endure. Foe: next move 2-5 hits at 25 BP.", - name: "Rageeeee", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {protect: 1, mirror: 1, contact: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Rage", target); - }, - onHit(target, source) { - source.addVolatile('rage', source); - if (this.queue.willAct() && this.runEvent('StallMove', source)) { - this.debug('Rageeeee endure'); - source.addVolatile('endure', source); - source.addVolatile('stall'); - } - target.addVolatile('enrageeeeed', source); - }, - secondary: null, - target: "normal", - type: "Flying", - }, - // Ransei - mashupmotive: { - accuracy: 100, - basePower: 80, - category: "Physical", - desc: "Raises the user's accuracy by one stage. 50% chance to raise Attack by one stage.", - shortDesc: "Accuracy +1. 50% chance to raise Atk by 1.", - name: "Mashup Motive", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {protect: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Revelation Dance", target); - }, - secondaries: [ - { - chance: 50, - self: { - boosts: {atk: 1}, - }, - }, - { - chance: 100, - self: { - boosts: {accuracy: 1}, - }, - }, - ], - target: "normal", - type: "Normal", - }, - // Rory Mercury - switchoff: { - accuracy: 100, - basePower: 60, - category: "Physical", - desc: "Before doing damage, the target's stat boosts are inverted. The user switches out after damaging the target.", - shortDesc: "Inverts target's boosts, then switches.", - name: "Switch Off", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {mirror: 1, protect: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Topsy-Turvy", target); - this.add('-anim', source, "Zing Zap", target); - }, - onTryHit(target, source, move) { let success = false; let i: BoostName; for (i in target.boosts) { @@ -3505,515 +1728,1224 @@ export const Moves: {[k: string]: ModdedMoveData} = { target.boosts[i] = -target.boosts[i]; success = true; } - if (!success) return; - this.add('-invertboost', target, '[from] move: Switch Off'); + if (success) { + this.add('-invertboost', target, '[from] move: Rigged Dice'); + } else { + target.addVolatile("taunt"); + } }, selfSwitch: true, secondary: null, target: "normal", - type: "Electric", - }, - // SamJo - thicc: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "Raises the user's Attack and accuracy by one stage.", - shortDesc: "Raises the user's Attack and accuracy by 1.", - name: "Thicc", - isNonstandard: "Custom", - pp: 15, - priority: 0, - flags: {snatch: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Hone Claws", source); - }, - boosts: { - atk: 1, - accuracy: 1, - }, - secondary: null, - target: "self", type: "Ice", }, - // SamJo Z-Move - extrathicc: { + + // Finland + cradilychaos: { accuracy: true, basePower: 0, category: "Status", - desc: "Raises the user's Attack and accuracy by one stage. Summons Hail and Aurora Veil.", - shortDesc: "User's Atk and acc +1. Sets Hail and Aurora Veil.", - name: "Extra T h i c c", + desc: "All Pokemon on the field get a +1 boost to a random stat. The target is badly poisoned, regardless of typing. If the user is Alcremie, it changes to a non-Vanilla Cream forme.", + shortDesc: "Random boosts to all mons. Tox. Change forme.", + name: "Cradily Chaos", isNonstandard: "Custom", - pp: 1, + gen: 8, + pp: 10, priority: 0, - flags: {}, + flags: {protect: 1, reflectable: 1}, onTryMove() { this.attrLastMove('[still]'); }, onPrepareHit(target, source) { - this.add('-anim', source, "Hone Claws", source); - this.add('-anim', source, "Extreme Evoboost", source); - this.add('-anim', source, "Blizzard", source); + this.add('-anim', source, 'Psywave', target); }, - onHit(target, source) { - this.field.setWeather('hail'); - if (this.field.isWeather('hail')) source.side.addSideCondition('auroraveil', source); - this.add('-message', source.name + ' became extra thicc!'); - }, - boosts: { - atk: 1, - accuracy: 1, - }, - isZ: "thicciniumz", - secondary: null, - target: "self", - type: "Ice", - }, - // Salamander - expressyourself: { - accuracy: true, - basePower: 0, - category: "Status", - isNonstandard: "Custom", - desc: "The user is healed for 50% of its HP. All Pokemon in the team's party get healed by 12.5% of their maximum HP.", - shortDesc: "User heals 50% HP. User's team heals 12.5% HP.", - name: "Express Yourself", - pp: 5, - priority: 0, - flags: {snatch: 1, heal: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Tail Glow', source); - this.add('-anim', source, 'Discharge', source); - }, - onHit(target, source) { - this.heal(source.baseMaxhp / 2, source); - if (!this.canSwitch(source.side)) return; - for (const ally of source.side.pokemon) { - if (ally === source) continue; - if (ally.fainted || !ally.hp) continue; - ally.heal(ally.baseMaxhp / 8, ally); + onHit(target, source, move) { + const boosts: BoostName[] = ['atk', 'def', 'spa', 'spd', 'spe']; + const selfBoost: SparseBoostsTable = {}; + selfBoost[boosts[this.random(5)]] = 1; + const oppBoost: SparseBoostsTable = {}; + oppBoost[boosts[this.random(5)]] = 1; + this.boost(selfBoost, source); + this.boost(oppBoost, target); + target.trySetStatus('tox', source); + if (source.species.baseSpecies === 'Alcremie') { + const newSet = ['Finland', 'Finland-Tsikhe', 'Finland-Nezavisa', 'Finland-Järvilaulu'][this.random(4)]; + changeSet(this, source, ssbSets[newSet]); } - this.add('-message', `${source.name} restored everyone's HP.`); }, secondary: null, - target: "self", - type: "Fairy", - }, - // Schiavetto - plurshift: { - accuracy: 100, - basePower: 120, - category: "Physical", - desc: "Has a 10% chance, calculated separately per effect, to apply any of Taunt, confusion, flinch, Heal Block, or Focus Energy to the target. Afterwards, the user is switched out for another Pokemon in its party. The selected Pokemon has the user's stat stage changes, confusion, and certain move effects transferred to it. ", - shortDesc: "Multiple status effects, then uses Baton Pass.", - name: "Plurshift", - isNonstandard: "Custom", - pp: 1, - priority: 0, - flags: {contact: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "First Impression", target); - this.add('-anim', target, "Infestation", target); - }, - onHit(target, source) { - source.addVolatile('batonpasshelper'); - }, - secondaries: [ - { - volatileStatus: "taunt", - chance: 10, - }, - { - volatileStatus: "confusion", - chance: 10, - }, - { - volatileStatus: "flinch", - chance: 10, - }, - { - volatileStatus: "healblock", - chance: 10, - }, - { - volatileStatus: "focusenergy", - chance: 10, - }, - ], - selfSwitch: 'copyvolatile', - isZ: "mariahcariumz", target: "normal", type: "Poison", }, - // Scotteh - geomagneticstorm: { + + // frostyicelad + frostywave: { accuracy: 100, - basePower: 140, + basePower: 95, category: "Special", - desc: "No additional effect.", - shortDesc: "No additional effect.", - name: "Geomagnetic Storm", + desc: "This move and its effects ignore the Abilities of other Pokemon.", + shortDesc: "Ignores abilities.", + name: "Frosty Wave", isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {protect: 1, mirror: 1, sound: 1, authentic: 1}, + ignoreAbility: true, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Boomburst', target); + this.add('-anim', source, 'Frost Breath', target); + }, + secondary: { + chance: 20, + self: { + boosts: { + spa: 1, + }, + }, + }, + target: "allAdjacentFoes", + type: "Ice", + }, + + // gallant's pear + kinggirigirislash: { + accuracy: 100, + basePower: 100, + category: "Special", + desc: "Removes the opponent's Reflect, Light Screen, Aurora Veil, and Safeguard. Secondary effect depends on the user's secondary typing: Psychic: 100% chance to lower target's Speed by 1; Fire: 10% burn; Steel: 10% flinch; Rock: apply Smack Down; Electric: 10% paralyze; else: no additional effect.", + shortDesc: "Breaks screens. Secondary depends on type.", + name: "King Giri Giri Slash", + isNonstandard: "Custom", + gen: 8, pp: 10, priority: 0, flags: {protect: 1, mirror: 1}, onTryMove() { this.attrLastMove('[still]'); }, + onModifyMove(move, pokemon) { + move.type = pokemon.types[1]; + }, + onTryHit(pokemon, target, move) { + // will shatter screens through sub, before you hit + if (target.runImmunity(move.type)) { + target.side.removeSideCondition('reflect'); + target.side.removeSideCondition('lightscreen'); + target.side.removeSideCondition('auroraveil'); + target.side.removeSideCondition('safeguard'); + } + if (move.type === 'Rock') move.volatileStatus = 'smackdown'; + if (this.random(10)) { + if (move.type === 'Fire') move.status = 'brn' as ID; + if (move.type === 'Steel') move.volatileStatus = 'flinch'; + if (move.type === 'Electric') move.status = 'par' as ID; + } + }, onPrepareHit(target, source) { - this.add('-anim', source, "Discharge", target); + this.add('-anim', source, 'Solar Blade', target); }, secondary: null, - target: "allAdjacent", - type: "Electric", + target: "normal", + type: "Normal", }, - // Shiba - goinda: { + + // Gimmick + randomscreaming: { + accuracy: 100, + basePower: 50, + category: "Special", + desc: "Has a 10% chance to freeze the target. If the target is frozen, this move will deal double damage and thaw the target.", + shortDesc: "10% frz. FRZ: 2x damage then thaw.", + name: "Random Screaming", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {protect: 1, mirror: 1, sound: 1, authentic: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Hyper Voice', target); + this.add('-anim', source, 'Misty Terrain', target); + }, + onBasePower(basePower, source, target, move) { + if (target.status === 'frz') { + return this.chainModify(2); + } + }, + secondary: { + chance: 10, + status: 'frz', + onHit() { + this.add(`c|${getName('Gimmick')}|Show me some more paaain, baaaby`); + }, + }, + thawsTarget: true, + target: "normal", + type: "Fire", + }, + + // GMars + gacha: { accuracy: true, basePower: 0, category: "Status", - desc: "Raises the user's Attack by two stages and Speed by one stage.", - shortDesc: "Raises the user's Attack by 2 and Speed by 1.", - name: "GO INDA", - isNonstandard: "Custom", - pp: 5, + desc: "Lowers the user's Defense and Special Defense by 1 stage. Raises the user's Attack, Special Attack, and Speed by 2 stages. If the user is Minior-Meteor, its forme changes, with a different effect for each forme.", + shortDesc: "Shell Smash; Minior: change forme.", + name: "Gacha", + pp: 15, priority: 0, flags: {snatch: 1}, onTryMove() { this.attrLastMove('[still]'); }, onPrepareHit(target, source) { - this.add('-anim', source, "Swords Dance", source); - this.add('-anim', source, "Sacred Fire", source); + this.add('-anim', source, 'Brick Break', source); + }, + onHit(target, source, move) { + if (target.species.id !== 'miniormeteor') return; + let forme: string; + let message = ""; + const random = this.random(100); + let shiny = false; + if (random < 3) { + forme = "Minior-Violet"; + message = "Oof, Violet. Tough break. A Violet Minior is sluggish and won't always listen to your commands. Best of luck! Rating: ★ ☆ ☆ ☆ ☆ "; + } else if (random < 13) { + forme = "Minior-Indigo"; + message = "Uh oh, an Indigo Minior. Its inspiring color may have had some unintended effects and boosted your foe's attacking stats. Better hope you can take it down first! Rating: ★ ☆ ☆ ☆ ☆"; + } else if (random < 33) { + forme = "Minior"; + message = "Nice one, a Red Minior is hard for your opponent to ignore. They'll be goaded into attacking the first time they see this! Rating: ★ ★ ★ ☆ ☆ "; + } else if (random < 66) { + forme = "Minior-Orange"; + message = "Solid, you pulled an Orange Minior. Nothing too fancy, but it can definitely get the job done if you use it right. Rating: ★ ★ ☆ ☆ ☆"; + } else if (random < 86) { + forme = "Minior-Yellow"; + message = "Sweet, a Yellow Minior! This thing had a lot of static energy built up that released when you cracked it open, paralyzing the foe. Rating: ★ ★ ★ ☆ ☆ "; + } else if (random < 96) { + forme = "Minior-Blue"; + message = "Woah! You got a Blue Minior. This one's almost translucent; it looks like it'd be hard for an opponent to find a way to reduce its stats. Rating: ★ ★ ★ ★ ☆"; + } else if (random < 99) { + forme = "Minior-Green"; + message = "Nice! You cracked a Green Minior, that's definitely a rare one. This type of Minior packs an extra punch, and it's great for breaking through defensive teams without risking multiple turns of setup. Rating: ★ ★ ★ ★ ★"; + } else { + forme = "Minior"; + shiny = true; + target.set.shiny = true; + message = "YO!! I can't believe it, you cracked open a Shiny Minior! Its multicolored interior dazzles its opponents and throws off their priority moves. Big grats. Rating: ★ ★ ★ ★ ★ ★"; + } + target.formeChange(forme, move, true); + const details = target.species.name + (target.level === 100 ? '' : ', L' + target.level) + + (target.gender === '' ? '' : ', ' + target.gender) + (target.set.shiny ? ', shiny' : ''); + if (shiny) this.add('replace', target, details); + if (message) this.add(`c|${getName('GMars')}|${message}`); + target.setAbility('capsulearmor'); + target.baseAbility = target.ability; + if (target.set.shiny) return; + if (forme === 'Minior-Indigo') { + this.boost({atk: 1, spa: 1}, target.side.foe.active[0]); + } else if (forme === 'Minior') { + target.side.foe.active[0].addVolatile('taunt'); + } else if (forme === 'Minior-Yellow') { + target.side.foe.active[0].trySetStatus('par', target); + } else if (forme === 'Minior-Green') { + this.boost({atk: 1}, target); + } }, boosts: { + def: -1, + spd: -1, atk: 2, - spe: 1, - }, - target: "self", - type: "Flying", - }, - // Slowbroth - alienwave: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "For 5 turns, slower Pokemon move first. Psychic-type attacks can hit if the target is a Dark-type.", - shortDesc: "5 turns: Trick Room; Psychic hits Dark.", - name: "Alien Wave", - isNonstandard: "Custom", - pp: 10, - priority: -7, - flags: {}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Telekinesis", source); - this.add('-anim', source, "Trick Room", source); - }, - pseudoWeather: 'alienwave', - condition: { - duration: 5, - onStart(target, source) { - this.add('-fieldstart', 'move: Alien Wave'); - this.add('-message', `Psychic-type attacks can hit Dark-type Pokemon!`); - }, - onNegateImmunity(pokemon, type) { - if (pokemon.hasType('Dark') && type === 'Psychic') return false; - }, - // Speed modification is changed in Pokemon.getActionSpeed() in mods/seasonal/scripts.js - onResidualOrder: 23, - onEnd() { - this.add('-fieldend', 'move: Alien Wave'); - this.add('-message', `Psychic-type attacks can no longer hit Dark-type Pokemon.`); - }, + spa: 2, + spe: 2, }, secondary: null, - target: "all", + target: "self", type: "Normal", }, - // Snaquaza - fakeclaim: { + + // grimAuxiliatrix + fuelleak: { accuracy: true, - category: "Physical", - basePower: 1, - desc: "The user creates a substitute to take its place in battle. This substitute is a Pokemon selected from a broad set of Random Battle-eligible Pokemon able to learn the move chosen as this move's base move. Upon the substitute's creation, this Pokemon's ability is suppressed until it switches out. The substitute Pokemon is generated with a Random Battle moveset with maximum PP that is added (except for duplicates) to the user's moveset; these additions are removed when this substitute is no longer active. The substitute uses its species's base stats, types, Ability, and weight but retains the user's max HP, stat stages, gender, level, status conditions, trapping, binding, and pseudo-statuses such as confusion. Its HP is 100% of the user's maximum HP. When this substitute falls to zero HP, it breaks, and the user reverts to the state in which it used this move. This substitute absorbs indirect damage and authentic moves but does not reset the counter of Toxic poison when broken and cannot be transferred through Baton Pass. Transforming into this substitute will not fail. If the user switches out while the substitute is up, the substitute will be removed and the user will revert to the state in which it used this move. This move's properties are based on the move Fake Claim is inheriting from.", - shortDesc: "Uses a Random Battle Pokemon as a Substitute.", - name: "Fake Claim", - isNonstandard: "Custom", - pp: 1, - priority: 0, - flags: {}, - onModifyMove(move) { - move.type = move.baseMove ? this.dex.getMove(move.baseMove).type : move.type; - // Hack for Snaquaza's Z move - move.basePower = move.baseMove ? this.dex.getMove(move.baseMove).basePower : move.basePower; - // Hack for Snaquaza's Z move - move.category = move.baseMove ? this.dex.getMove(move.baseMove).category : move.category; - // @ts-ignore Hack for Snaquaza's Z move - this.claimMove = move.baseMove ? this.dex.getMove(move.baseMove) : this.dex.getMove('bravebird'); - }, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source, move) { - const zmove = this.dex.getMove(this.zMoveTable[move.type]); - this.add('-anim', source, zmove.name, target); - this.add('-anim', source, "Transform", source); - }, - onAfterMoveSecondarySelf(pokemon, move) { - const claims: {[move: string]: string[]} = { - bravebird: [ - 'Braviary', 'Crobat', 'Decidueye', 'Dodrio', 'Farfetch\u2019d', 'Golbat', 'Mandibuzz', 'Pidgeot', 'Skarmory', 'Staraptor', 'Swanna', 'Swellow', 'Talonflame', 'Tapu Koko', 'Toucannon', - ], - superpower: [ - 'Absol', 'Aggron', 'Armaldo', 'Avalugg', 'Azumarill', 'Barbaracle', 'Basculin', 'Beartic', 'Bewear', 'Bibarel', 'Bouffalant', 'Braviary', 'Breloom', 'Buzzwole', 'Cacturne', 'Carracosta', 'Celesteela', 'Chesnaught', 'Cobalion', 'Conkeldurr', 'Crabominable', 'Crawdaunt', 'Darmanitan', 'Diggersby', 'Donphan', 'Dragonite', 'Drampa', 'Druddigon', 'Durant', 'Eelektross', 'Emboar', 'Exeggutor-Alola', 'Feraligatr', 'Flareon', 'Flygon', 'Gigalith', 'Gogoat', 'Golem', 'Golurk', 'Goodra', 'Granbull', 'Gurdurr', 'Hariyama', 'Hawlucha', 'Haxorus', 'Heatmor', 'Hippowdon', 'Hitmonlee', 'Hydreigon', 'Incineroar', 'Kabutops', 'Keldeo', 'Kingler', 'Komala', 'Kommo-o', 'Krookodile', 'Landorus-Therian', 'Lurantis', 'Luxray', 'Machamp', 'Malamar', 'Mamoswine', 'Mew', 'Mudsdale', 'Nidoking', 'Nidoqueen', 'Pangoro', 'Passimian', 'Piloswine', 'Pinsir', 'Rampardos', 'Regice', 'Regigigas', 'Regirock', 'Registeel', 'Reuniclus', 'Rhydon', 'Rhyperior', 'Samurott', 'Sawk', 'Scizor', 'Scolipede', 'Simipour', 'Simisage', 'Simisear', 'Smeargle', 'Snorlax', 'Spinda', 'Stakataka', 'Stoutland', 'Swampert', 'Tapu Bulu', 'Terrakion', 'Throh', 'Thundurus', 'Torkoal', 'Tornadus', 'Torterra', 'Tyranitar', 'Tyrantrum', 'Ursaring', 'Virizion', 'Zeraora', - ], - suckerpunch: [ - 'Absol', 'Arbok', 'Ariados', 'Banette', 'Bisharp', 'Cacturne', 'Celebi', 'Corsola', 'Decidueye', 'Delcatty', 'Drifblim', 'Druddigon', 'Dugtrio', 'Dusknoir', 'Electrode', 'Emboar', 'Froslass', 'Furfrou', 'Furret', 'Galvantula', 'Gengar', 'Girafarig', 'Golem', 'Golisopod', 'Heatmor', 'Hitmonlee', 'Hitmontop', 'Houndoom', 'Huntail', 'Kangaskhan', 'Kecleon', 'Komala', 'Lanturn', 'Latias', 'Liepard', 'Lycanroc', 'Maractus', 'Mawile', 'Meowstic', 'Mew', 'Mightyena', 'Mismagius', 'Nidoking', 'Nidoqueen', 'Purugly', 'Raticate', 'Rotom', 'Sableye', 'Seviper', 'Shiftry', 'Skuntank', 'Slaking', 'Smeargle', 'Spinda', 'Spiritomb', 'Stantler', 'Sudowoodo', 'Toxicroak', 'Umbreon', 'Victreebel', 'Wormadam', 'Xatu', - ], - flamethrower: [ - 'Absol', 'Aerodactyl', 'Aggron', 'Altaria', 'Arcanine', 'Audino', 'Azelf', 'Bastiodon', 'Blacephalon', 'Blissey', 'Camerupt', 'Castform', 'Celesteela', 'Chandelure', 'Chansey', 'Charizard', 'Clefable', 'Clefairy', 'Darmanitan', 'Delphox', 'Dragonite', 'Drampa', 'Druddigon', 'Dunsparce', 'Eelektross', 'Electivire', 'Emboar', 'Entei', 'Exeggutor-Alola', 'Exploud', 'Flareon', 'Flygon', 'Furret', 'Garchomp', 'Golem', 'Goodra', 'Gourgeist', 'Granbull', 'Guzzlord', 'Gyarados', 'Heatmor', 'Heatran', 'Houndoom', 'Hydreigon', 'Incineroar', 'Infernape', 'Kangaskhan', 'Kecleon', 'Kommo-o', 'Lickilicky', 'Machamp', 'Magcargo', 'Magmortar', 'Malamar', 'Manectric', 'Marowak', 'Mawile', 'Mew', 'Moltres', 'Muk', 'Nidoking', 'Nidoqueen', 'Ninetales', 'Noivern', 'Octillery', 'Pyroar', 'Rampardos', 'Rapidash', 'Rhydon', 'Rhyperior', 'Salamence', 'Salazzle', 'Seviper', 'Silvally', 'Simisear', 'Skuntank', 'Slaking', 'Slowbro', 'Slowking', 'Slurpuff', 'Smeargle', 'Snorlax', 'Solrock', 'Talonflame', 'Tauros', 'Togekiss', 'Torkoal', 'Turtonator', 'Typhlosion', 'Tyranitar', 'Watchog', 'Weezing', 'Wigglytuff', 'Zangoose', - ], - thunderbolt: [ - 'Absol', 'Aggron', 'Ambipom', 'Ampharos', 'Aromatisse', 'Audino', 'Aurorus', 'Azelf', 'Banette', 'Bastiodon', 'Beheeyem', 'Bibarel', 'Blissey', 'Castform', 'Chansey', 'Cinccino', 'Clefable', 'Clefairy', 'Dedenne', 'Delcatty', 'Dragalge', 'Dragonite', 'Drampa', 'Drifblim', 'Dunsparce', 'Eelektross', 'Electivire', 'Electrode', 'Emolga', 'Ferroseed', 'Ferrothorn', 'Froslass', 'Furret', 'Gallade', 'Galvantula', 'Garbodor', 'Gardevoir', 'Gengar', 'Girafarig', 'Golem-Alola', 'Golurk', 'Goodra', 'Gothitelle', 'Granbull', 'Gyarados', 'Heliolisk', 'Illumise', 'Jirachi', 'Jolteon', 'Kangaskhan', 'Kecleon', 'Klinklang', 'Lanturn', 'Lapras', 'Latias', 'Latios', 'Lickilicky', 'Linoone', 'Lopunny', 'Luxray', 'Magearna', 'Magmortar', 'Magneton', 'Magnezone', 'Malamar', 'Manectric', 'Marowak-Alola', 'Marowak-Alola-Totem', 'Meloetta', 'Meowstic', 'Mesprit', 'Mew', 'Miltank', 'Mimikyu', 'Minun', 'Mismagius', 'Mr. Mime', 'Muk', 'Nidoking', 'Nidoqueen', 'Nihilego', 'Oranguru', 'Pachirisu', 'Persian', 'Plusle', 'Porygon-Z', 'Porygon2', 'Primeape', 'Probopass', 'Purugly', 'Raichu', 'Raikou', 'Rampardos', 'Raticate', 'Regice', 'Regigigas', 'Regirock', 'Registeel', 'Rhydon', 'Rhyperior', 'Rotom', 'Silvally', 'Slaking', 'Slurpuff', 'Smeargle', 'Snorlax', 'Stantler', 'Starmie', 'Stoutland', 'Stunfisk', 'Tapu Koko', 'Tapu Lele', 'Tauros', 'Thundurus', 'Togedemaru', 'Tyranitar', 'Uxie', 'Vikavolt', 'Volbeat', 'Watchog', 'Weezing', 'Wigglytuff', 'Xurkitree', 'Zangoose', 'Zapdos', 'Zebstrika', 'Zeraora', - ], - icebeam: [ - 'Abomasnow', 'Absol', 'Aggron', 'Alomomola', 'Altaria', 'Araquanid', 'Articuno', 'Audino', 'Aurorus', 'Avalugg', 'Azumarill', 'Barbaracle', 'Basculin', 'Bastiodon', 'Beartic', 'Bibarel', 'Blastoise', 'Blissey', 'Bruxish', 'Carracosta', 'Castform', 'Chansey', 'Clawitzer', 'Claydol', 'Clefable', 'Clefairy', 'Cloyster', 'Corsola', 'Crabominable', 'Crawdaunt', 'Cresselia', 'Cryogonal', 'Delcatty', 'Delibird', 'Dewgong', 'Dragonite', 'Drampa', 'Dunsparce', 'Dusknoir', 'Empoleon', 'Exploud', 'Feraligatr', 'Floatzel', 'Froslass', 'Furret', 'Gastrodon', 'Glaceon', 'Glalie', 'Golduck', 'Golisopod', 'Golurk', 'Goodra', 'Gorebyss', 'Greninja', 'Gyarados', 'Huntail', 'Jellicent', 'Jynx', 'Kabutops', 'Kangaskhan', 'Kecleon', 'Kingdra', 'Kingler', 'Kyurem', 'Lanturn', 'Lapras', 'Latias', 'Latios', 'Lickilicky', 'Linoone', 'Lopunny', 'Ludicolo', 'Lumineon', 'Lunatone', 'Luvdisc', 'Magearna', 'Mamoswine', 'Manaphy', 'Mantine', 'Marowak', 'Masquerain', 'Mawile', 'Mesprit', 'Mew', 'Milotic', 'Miltank', 'Nidoking', 'Nidoqueen', 'Ninetales-Alola', 'Octillery', 'Omastar', 'Pelipper', 'Phione', 'Piloswine', 'Politoed', 'Poliwrath', 'Porygon-Z', 'Porygon2', 'Primarina', 'Quagsire', 'Qwilfish', 'Rampardos', 'Raticate', 'Regice', 'Relicanth', 'Rhydon', 'Rhyperior', 'Samurott', 'Seaking', 'Sharpedo', 'Sigilyph', 'Silvally', 'Simipour', 'Slaking', 'Slowbro', 'Slowking', 'Smeargle', 'Sneasel', 'Snorlax', 'Starmie', 'Suicune', 'Swalot', 'Swampert', 'Swanna', 'Tapu Fini', 'Tauros', 'Tentacruel', 'Toxapex', 'Tyranitar', 'Vanilluxe', 'Vaporeon', 'Wailord', 'Walrein', 'Weavile', 'Whiscash', 'Wigglytuff', 'Wishiwashi', 'Zangoose', - ], - }; - // @ts-ignore Hack for Snaquaza's Z move - const baseMove = this.claimMove.id; - const pool = claims[baseMove]; - if (!pool) { - // Should never happen - throw new Error(`SSB: Unable to find fake claim movepool for the move: "${baseMove}".`); - } - const claim = claims[baseMove][this.random(pool.length)]; - // Generate new set - const generator = new RandomStaffBrosTeams('gen7randombattle', this.prng); - const set = generator.randomSet(claim); - // Suppress Ability now to prevent starting new abilities when transforming - pokemon.addVolatile('gastroacid', pokemon); - // Tranform into it - pokemon.formeChange(set.species); - for (const newMove of set.moves) { - const moveSpecies = this.dex.getMove(newMove); - if (pokemon.moves.includes(moveSpecies.id)) continue; - pokemon.moveSlots.push({ - move: moveSpecies.name, - id: moveSpecies.id, - pp: ((moveSpecies.noPPBoosts || moveSpecies.isZ) ? moveSpecies.pp : moveSpecies.pp * 8 / 5), - maxpp: ((moveSpecies.noPPBoosts || moveSpecies.isZ) ? moveSpecies.pp : moveSpecies.pp * 8 / 5), - target: moveSpecies.target, - disabled: false, - disabledSource: '', - used: false, - }); - } - // Update HP - // Hack for Snaquaza's Z Move - pokemon.m.claimHP = pokemon.hp; - pokemon.heal(pokemon.maxhp - pokemon.hp, pokemon); - this.add('-heal', pokemon, pokemon.getHealth, '[silent]'); - this.add('message', `${pokemon.name} claims to be a ${set.species}!`); - }, - isZ: "fakeclaimiumz", - secondary: null, - target: "normal", - type: "Dark", - }, - // sparksblade - kratosmana: { - accuracy: 100, - basePower: 250, - category: "Physical", - desc: "The user faints after using this move, even if this move fails for having no target. Has a 10% chance to paralyze the target.", - shortDesc: "The user faints. 10% chance to paralyze target.", - name: "Kratosmana", - isNonstandard: "Custom", - pp: 5, - priority: 0, - flags: {protect: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Explosion", target); - this.add('-anim', source, "Searing Shot", target); - this.add('-anim', target, "Poison Gas", target); - }, - selfdestruct: "always", - secondary: { - chance: 10, - status: 'par', - }, - target: "normal", - type: "Fire", - }, - // Sundar - leafblaster: { - accuracy: 100, - basePower: 90, + basePower: 50, category: "Special", - desc: "Lowers the user's accuracy by 2 stages.", - shortDesc: "Lowers the user's accuracy by 2.", - name: "Leaf Blaster", + desc: "Summons ferrofluid to the user's side, which is a positive entry hazard that heals Pokemon by 25% of their maximum HP when they switch in.", + shortDesc: "Sets Ferrofluid on user's side.", + name: "Fuel Leak", isNonstandard: "Custom", - pp: 15, + gen: 8, + pp: 10, priority: 0, flags: {protect: 1, mirror: 1}, - onTryMove() { + onTryMove(target) { this.attrLastMove('[still]'); }, onPrepareHit(target, source) { - this.add('-anim', source, "Leaf Storm", target); + this.add('-anim', source, 'Steel Beam', target); }, - self: { - boosts: { - accuracy: -2, - }, + onAfterMoveSecondarySelf(source, target) { + source.side.addSideCondition('ferrofluid'); }, secondary: null, target: "normal", - type: "Grass", + type: "Steel", }, - // Teclis - absoluteconfiguration: { + + // for grimAuxiliatrix's move + ferrofluid: { accuracy: true, basePower: 0, category: "Status", - desc: "Puts the foe to sleep. Summons a Nightmare Field with the effects of Nightmare for 4 turns.", - shortDesc: "Puts foe to sleep. Nightmare for 4 turns.", - name: "Absolute Configuration", + desc: "Positive entry hazard that heals Pokemon by 25% of their maximum HP when they switch in.", + shortDesc: "Heals 25% on switch in.", + name: "Ferrofluid", isNonstandard: "Custom", - pp: 1, + gen: 8, + pp: 15, priority: 0, flags: {reflectable: 1}, onTryMove() { this.attrLastMove('[still]'); }, onPrepareHit(target, source) { - this.add('-anim', source, 'Dark Pulse', target); - this.add('-anim', source, 'Dark Void', target); + this.add('-anim', source, 'Acid Downpour', target); }, - onHit(source) { - this.field.addPseudoWeather('nightmarefield', source); - }, - status: 'slp', - isZ: "darkrainiumz", - target: "normal", - type: "Dark", - }, - // Used for Teclis's z-move - nightmarefield: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "For 4 turns, Nightmare Field is active. During the effect, sleeping Pokemon suffer from the effects of Nightmare.", - shortDesc: "4 turns. Sleeping Pokemon suffer Nightmare.", - name: "Nightmare Field", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {}, - pseudoWeather: 'nightmarefield', + sideCondition: 'ferrofluid', condition: { - duration: 4, - onStart(battle, source, effect) { - if (effect && effect.effectType === 'Ability') { - this.add('-fieldstart', 'move: Nightmare Field', '[from] ability: ' + effect, '[of] ' + source); + // this is a side condition + onStart(side) { + if (this.field.isTerrain('waveterrain')) { + this.add('-message', `Wave Terrain prevented Ferrofluid from starting!`); + return null; + } + this.add('-sidestart', side, 'Ferrofluid'); + this.add("-message", `Ferrofluid was spilled!`); + this.effectData.layers = 1; + }, + onRestart(side) { + if (this.effectData.layers >= 3) return false; + this.add('-sidestart', side, 'Ferrofluid'); + this.effectData.layers++; + }, + onSwitchIn(pokemon) { + // Can be used by pokemon with boots. + if (this.heal(pokemon.baseMaxhp / 4)) { + this.add("-message", `Magnetized particles mend your wounds!`); } else { - this.add('-fieldstart', 'move: Nightmare Field'); + return; + } + this.effectData.layers--; + if (this.effectData.layers < 1) { + pokemon.side.removeSideCondition(`ferrofluid`); } }, - onResidualOrder: 21, - onResidualSubOrder: 2, - onResidual() { - for (const curMon of this.getAllActive()) { - if (curMon.status === 'slp' || curMon.hasAbility('comatose')) { - this.damage(curMon.baseMaxhp / 4, curMon); - } - } - }, - onEnd() { - this.add('-fieldend', 'move: Nightmare Field'); + onEnd(side) { + this.add('-sideend', side, 'move: Ferrofluid'); + this.add('-message', `The Ferrofuild disappeared.`); }, }, secondary: null, - target: "self", - type: "Dark", + target: "foeSide", + type: "Steel", }, - // tennisace - groundsurge: { + + // HoeenHero + landfall: { accuracy: 100, - basePower: 95, category: "Special", - desc: "This move's type effectiveness against Ground is changed to be super effective no matter what this move's type is.", - shortDesc: "Super effective on Ground.", - name: "Ground Surge", + basePower: 0, + basePowerCallback(target, source, move) { + const windSpeeds = [65, 85, 85, 95, 95, 95, 95, 115, 115, 140]; + move.basePower = windSpeeds[this.random(0, 10)]; + return move.basePower; + }, + desc: "The foe is hit with a hurricane with a Base Power that varies based on the strength (category) of the hurricane. Category 1 is 65, category 2 is 85, category 3 is 95, category 4 is 115, and category 5 is 140. In addition, the target's side of the field is covered in a storm surge. Storm surge applies a 1/4 Speed multiplier to pokemon on that side of the field. Storm surge will last for as many turns as the hurricane's category (not including the turn Landfall was used).", + shortDesc: "Higher category = +dmg, foe side speed 1/4.", + name: "Landfall", isNonstandard: "Custom", - pp: 15, + gen: 8, + pp: 5, priority: 0, flags: {protect: 1, mirror: 1}, onTryMove() { this.attrLastMove('[still]'); }, onPrepareHit(target, source) { - this.add('-anim', source, "Thunder", target); - this.add('-anim', source, "Fissure", target); + this.add('-anim', source, 'Hurricane', target); + this.add('-anim', source, 'Surf', target); }, - ignoreImmunity: {Electric: true}, - onEffectiveness(typeMod, target, type) { - if (type === 'Ground') return 1; + onHit(target, source, move) { + const windSpeeds = [65, 85, 95, 115, 140]; + const category = windSpeeds.indexOf(move.basePower) + 1; + this.add('-message', `A category ${category} hurricane made landfall!`); }, - secondary: null, + sideCondition: 'stormsurge', // Programmed in conditions.ts target: "normal", - type: "Electric", + type: "Water", }, - // Teremiare - rotate: { - accuracy: 100, + + // Hubriz + steroidanaphylaxia: { + accuracy: true, basePower: 0, category: "Status", - desc: "The user's replacement will switch out at the end of next turn if the replacement's move is successful.", - shortDesc: "User's replacement switches after using its move.", - name: "Rotate", + desc: "Inverts the target's stat stages.", + name: "Steroid Anaphylaxia", isNonstandard: "Custom", + gen: 8, + pp: 20, + priority: 1, + flags: {protect: 1, reflectable: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onHit(target) { + let success = false; + let i: BoostName; + for (i in target.boosts) { + if (target.boosts[i] === 0) continue; + target.boosts[i] = -target.boosts[i]; + success = true; + } + if (!success) return false; + this.add('-invertboost', target, '[from] move: Steroid Anaphylaxia'); + }, + target: "normal", + type: "Poison", + }, + + // Hydro + hydrostatics: { + accuracy: 100, + basePower: 50, + category: "Special", + desc: "Has a 70% chance to raise the user's Special Attack by 1 stage and a 50% chance to paralyze the target. This move combines Water in its type effectiveness against the target.", + shortDesc: "70% +1 SpA; 50% par; +Water in type effect.", + name: "Hydrostatics", + isNonstandard: "Custom", + gen: 8, pp: 10, - priority: 0, - flags: {snatch: 1, mirror: 1}, + priority: 2, + flags: {protect: 1, mirror: 1}, onTryMove() { this.attrLastMove('[still]'); }, onPrepareHit(target, source) { - this.add('-anim', source, "Celebrate", target); + this.add('-anim', source, 'Origin Pulse', target); + this.add('-anim', source, 'Charge Beam', target); }, - sideCondition: "rotate", + secondaries: [{ + chance: 70, + self: { + boosts: { + spa: 1, + }, + }, + }, { + chance: 50, + status: 'par', + }], + onEffectiveness(typeMod, target, type, move) { + return typeMod + this.dex.getEffectiveness('Water', type); + }, + target: "normal", + type: "Electric", + }, + + // Inactive + paranoia: { + accuracy: 90, + basePower: 100, + category: "Physical", + desc: "Has a 15% chance to burn the target.", + name: "Paranoia", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Max Flare', target); + }, + secondary: { + chance: 15, + status: 'brn', + }, + target: "normal", + type: "Dark", + }, + + // Instruct + sodabreak: { + accuracy: true, + basePower: 10, + category: "Physical", + desc: "Has a 100% chance to make the target flinch. Causes the user to switch out. Fails unless it is the user's first turn on the field.", + shortDesc: "First turn: Flinches the target then switches out.", + name: "Soda Break", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 3, + flags: {contact: 1, protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Milk Drink', source); + this.add('-anim', source, 'Fling', target); + this.add('-anim', source, 'U-turn', target); + }, + onTry(pokemon, target) { + if (pokemon.activeMoveActions > 1) { + this.attrLastMove('[still]'); + this.add('-fail', pokemon); + this.hint("Soda Break only works on your first turn out."); + return null; + } + }, + secondary: { + chance: 100, + volatileStatus: 'flinch', + }, + selfSwitch: true, + target: "normal", + type: "???", + }, + + // Iyarito + patronaattack: { + accuracy: 100, + basePower: 50, + category: "Special", + desc: "Usually goes first.", + name: "Patrona Attack", + isNonstandard: "Custom", + gen: 8, + pp: 20, + priority: 1, + flags: {protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Moongeist Beam', target); + }, + secondary: null, + target: "normal", + type: "Ghost", + }, + + // Jett + thehuntison: { + accuracy: 100, + basePower: 55, + basePowerCallback(pokemon, target, move) { + // You can't get here unless the pursuit effect succeeds + if (target.beingCalledBack) { + this.debug('The Hunt is On! damage boost'); + return move.basePower * 2; + } + return move.basePower; + }, + category: "Physical", + desc: "If an opposing Pokemon switches out this turn, this move hits that Pokemon before it leaves the field, even if it was not the original target. If the user moves after an opponent using Parting Shot, U-turn, or Volt Switch, but not Baton Pass, it will hit that opponent before it leaves the field. Power doubles and no accuracy check is done if the user hits an opponent switching out, and the user's turn is over; if an opponent faints from this, the replacement Pokemon does not become active until the end of the turn.", + shortDesc: "Foe: 2x power when switching.", + name: "The Hunt is On!", + isNonstandard: "Custom", + gen: 8, + pp: 15, + priority: 0, + flags: {contact: 1, protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Sucker Punch', target); + this.add('-anim', source, 'Pursuit', target); + }, + beforeTurnCallback(pokemon) { + for (const side of this.sides) { + if (side === pokemon.side) continue; + side.addSideCondition('thehuntison', pokemon); + const data = side.getSideConditionData('thehuntison'); + if (!data.sources) { + data.sources = []; + } + data.sources.push(pokemon); + } + }, + onModifyMove(move, source, target) { + if (target?.beingCalledBack) move.accuracy = true; + }, + onTryHit(target, pokemon) { + target.side.removeSideCondition('thehuntison'); + }, + onAfterMoveSecondarySelf(pokemon, target, move) { + if (!target || target.fainted || target.hp <= 0) { + this.add(`c|${getName('Jett')}|Owned!`); + } + }, + condition: { + duration: 1, + onBeforeSwitchOut(pokemon) { + this.debug('Thehuntison start'); + let alreadyAdded = false; + pokemon.removeVolatile('destinybond'); + for (const source of this.effectData.sources) { + if (!this.queue.cancelMove(source) || !source.hp) continue; + if (!alreadyAdded) { + this.add('-activate', pokemon, 'move: The Hunt is On!'); + alreadyAdded = true; + } + this.runMove('thehuntison', source, this.getTargetLoc(pokemon, source)); + } + }, + }, + secondary: null, + target: "normal", + type: "Dark", + }, + + // Jho + genrechange: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "If the user is a Toxtricity, it changes into its Low-Key forme and Nasty Plot and Overdrive change to Aura Sphere and Boomburst, respectively. If the user is a Toxtricity in its Low-Key forme, it changes into its Amped forme and Aura Sphere and Boomburst turn into Nasty Plot and Overdrive, respectively. Raises the user's Speed by 1 stage.", + shortDesc: "Toxtricity: +1 Speed. Changes forme.", + name: "Genre Change", + isNonstandard: "Custom", + gen: 8, + pp: 5, + priority: 0, + flags: {snatch: 1, sound: 1}, + onTryMove(pokemon, target, move) { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Screech', source); + // The transform animation is done via `formeChange` + }, + onHit(pokemon) { + if (pokemon.species.baseSpecies === 'Toxtricity') { + if (pokemon.species.forme === 'Low-Key') { + changeSet(this, pokemon, ssbSets['Jho']); + } else { + changeSet(this, pokemon, ssbSets['Jho-Low-Key']); + } + } + }, + boosts: { + spe: 1, + }, + secondary: null, + target: "self", + type: "Normal", + }, + + // Jordy + archeopssrage: { + accuracy: 85, + basePower: 90, + category: "Physical", + desc: "Upon damaging the target, the user gains +1 Speed.", + shortDesc: "+1 Speed upon hit.", + name: "Archeops's Rage", + isNonstandard: "Custom", + gen: 8, + pp: 5, + flags: {contact: 1, protect: 1, mirror: 1}, + priority: 0, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Sunsteel Strike', target); + }, + self: { + boosts: { + spe: 1, + }, + }, + secondary: null, + target: "normal", + type: "Flying", + }, + + // Kaiju Bunny + cozycuddle: { + accuracy: 95, + basePower: 0, + category: "Status", + desc: "Traps the target and lowers its Attack and Defense by 2 stages.", + shortDesc: "Target: trapped, Atk and Def lowered by 2.", + name: "Cozy Cuddle", + isNonstandard: "Custom", + gen: 8, + pp: 20, + priority: 0, + flags: {contact: 1, protect: 1, reflectable: 1}, + volatileStatus: 'cozycuddle', + onTryMove() { + this.attrLastMove('[still]'); + }, + onTryHit(target, source, move) { + if (target.volatiles['cozycuddle']) return false; + if (target.volatiles['trapped']) { + delete move.volatileStatus; + } + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Flatter', target); + this.add('-anim', source, 'Let\'s Snuggle Forever', target); + }, + onHit(target, source, move) { + this.boost({atk: -2, def: -2}, target, target); + }, + condition: { + onStart(pokemon, source) { + this.add('-start', pokemon, 'Cozy Cuddle'); + }, + onTrapPokemon(pokemon) { + if (this.effectData.source?.isActive) pokemon.tryTrap(); + }, + }, + secondary: null, + target: "normal", + type: "Fairy", + }, + + // Kalalokki + blackbird: { + accuracy: 100, + basePower: 70, + category: "Special", + desc: "If this move is successful and the user has not fainted, the user switches out even if it is trapped and is replaced immediately by a selected party member. The user does not switch out if there are no unfainted party members, or if the target switched out using an Eject Button or through the effect of the Emergency Exit or Wimp Out Abilities.", + shortDesc: "User switches out after damaging the target.", + name: "Blackbird", + isNonstandard: "Custom", + gen: 8, + pp: 20, + priority: 0, + flags: {protect: 1, mirror: 1}, + selfSwitch: true, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Gust', target); + this.add('-anim', source, 'Parting Shot', target); + }, + secondary: null, + target: "normal", + type: "Flying", + }, + gaelstrom: { + accuracy: true, + basePower: 140, + category: "Special", + desc: "Hits foe and phazes them out, phaze the next one out and then another one, set a random entry hazard at the end of the move.", + shortDesc: "Hits foe, phazes 3 times, sets random hazard.", + name: "Gaelstrom", + isNonstandard: "Custom", + gen: 8, + pp: 1, + noPPBoosts: true, + priority: 0, + flags: {}, + isZ: "kalalokkiumz", + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Hurricane', target); + }, + sideCondition: 'gaelstrom', + condition: { + duration: 1, + onSwitchIn(pokemon) { + if (!this.effectData.count) this.effectData.count = 1; + if (this.effectData.count < 3) { + pokemon.forceSwitchFlag = true; + this.effectData.count++; + return; + } + pokemon.side.removeSideCondition('gaelstrom'); + }, + onStart(side) { + side.addSideCondition(['spikes', 'toxicspikes', 'stealthrock', 'stickyweb'][this.random(4)]); + }, + }, + forceSwitch: true, + target: "normal", + type: "Flying", + }, + + // Kennedy + topbins: { + accuracy: 70, + basePower: 130, + category: "Physical", + desc: "Has a 20% chance to burn the target and a 10% chance to cause the target to flinch.", + shortDesc: "20% chance to burn. 10% chance to flinch.", + name: "Top Bins", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Pyro Ball', target); + this.add('-anim', source, 'Blaze Kick', target); + }, + secondaries: [{ + chance: 20, + status: 'brn', + }, { + chance: 10, + volatileStatus: 'flinch', + }], + target: "normal", + type: "Fire", + }, + + // Kev + kingstrident: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "Raises the user's Special Attack by 1 stage and Speed by 2 stages.", + shortDesc: "Gives user +1 SpA and +2 Spe.", + name: "King's Trident", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {snatch: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target) { + this.add('-anim', target, 'Dragon Dance', target); + }, + self: { + boosts: { + spa: 1, + spe: 2, + }, + }, + secondary: null, + target: "self", + type: "Water", + }, + + // Kingbaruk + leaveittotheteam: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "The user faints and the Pokemon brought out to replace it gets Healing Wish effects and has its Attack, Defense, Special Attack, and Special Defense boosted by 1 stage.", + shortDesc: "User faints. Next: healed & +1 Atk/Def/SpA/SpD.", + name: "Leave it to the team!", + isNonstandard: "Custom", + gen: 8, + pp: 5, + priority: 0, + flags: {snatch: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onTryHit(pokemon, target, move) { + if (!this.canSwitch(pokemon.side)) { + delete move.selfdestruct; + return false; + } + }, + selfdestruct: "ifHit", + sideCondition: 'leaveittotheteam', condition: { duration: 2, - onStart(source) { - this.add('-message', `${source.active[0].name}'s replacement is going to switch out next turn!`); + onStart(side, source) { + this.debug('Leave it to the team! started on ' + side.name); + this.effectData.positions = []; + for (const i of side.active.keys()) { + this.effectData.positions[i] = false; + } + this.effectData.positions[source.position] = true; }, - onBeforeTurn(pokemon) { - this.queue.insertChoice({choice: 'event', event: 'SSBRotate', pokemon: pokemon, priority: -69}); + onRestart(side, source) { + this.effectData.positions[source.position] = true; }, - // @ts-ignore unsupported custom event - onSSBRotate(pokemon: Pokemon) { - // @ts-ignore Unsupported custom event, this is refering to a battle - this.add('-message', `${pokemon.illusion ? pokemon.illusion.name : pokemon.name} is preparing to switch out!`); - pokemon.switchFlag = true; + onSwitchInPriority: 1, + onSwitchIn(target) { + const positions: boolean[] = this.effectData.positions; + if (target.position !== this.effectData.sourcePosition) { + return; + } + if (!target.fainted) { + target.heal(target.maxhp); + this.boost({atk: 1, def: 1, spa: 1, spd: 1}, target); + target.clearStatus(); + for (const moveSlot of target.moveSlots) { + moveSlot.pp = moveSlot.maxpp; + } + this.add('-heal', target, target.getHealth, '[from] move: Leave it to the team!'); + positions[target.position] = false; + } + if (!positions.some(affected => affected === true)) { + target.side.removeSideCondition('leaveittotheteam'); + } + }, + }, + secondary: null, + target: "self", + type: "Fairy", + }, + + // KingSwordYT + clashofpangoros: { + accuracy: 100, + basePower: 90, + category: "Physical", + desc: "The user restores 1/8 of its maximum HP. Target can't use status moves for its next 3 turns. Lowers the target's Attack by 1 stage. At the end of the move, the user switches out.", + shortDesc: "Heals 1/8, taunts, lowers Atk, switches out.", + name: "Clash of Pangoros", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {contact: 1, protect: 1, mirror: 1, heal: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Black Hole Eclipse', target); + }, + onAfterMoveSecondarySelf(pokemon, target, move) { + this.heal(pokemon.maxhp / 8, pokemon, pokemon, move); + }, + onHit(target, pokemon, move) { + this.boost({atk: -1}, target, target, move); + target.addVolatile('taunt', pokemon); + }, + selfSwitch: true, + secondary: null, + target: "normal", + type: "Dark", + }, + + // Kipkluif + kipup: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "When used, if hit by an attack on the same turn this move was used, this Pokemon boosts its Defense and Special Defense by 2 stages if the relevant stat is at 0 or lower, or 1 stage if the relevant stat is at +1 or higher, and increases priority of the next used move by 1.", + shortDesc: "If hit, +Def/SpD; next move +1 prio.", + name: "Kip Up", + pp: 10, + priority: 3, + flags: {}, + onTryMove(source) { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Focus Energy', source); + }, + onHit(target, pokemon, move) { + pokemon.addVolatile('kipup'); + }, + condition: { + duration: 1, + onStart(pokemon) { + this.add('-message', 'This Pokémon prepares itself to be knocked down!'); + }, + onHit(pokemon, source, move) { + if (this.effectData.gotHit) return; + if (pokemon.side !== source.side && move.category !== 'Status') { + this.effectData.gotHit = true; + this.add('-message', 'Gossifleur was prepared for the impact!'); + const boosts: {[k: string]: number} = {def: 2, spd: 2}; + if (pokemon.boosts.def >= 1) boosts.def--; + if (pokemon.boosts.spd >= 1) boosts.spd--; + this.boost(boosts, pokemon); + this.add('-message', "Gossifleur did a Kip Up and can jump right back into the action!"); + this.effectData.duration++; + } + }, + onModifyPriority(priority, pokemon, target, move) { + if (!this.effectData.gotHit) return priority; + return priority + 1; + }, + }, + secondary: null, + target: "self", + type: "Fighting", + }, + + // Kris + alphabetsoup: { + accuracy: true, + basePower: 100, + category: "Special", + desc: "The user changes into a random Pokemon with a first name letter that matches the forme Unown is currently in (A -> Alakazam, etc) that has base stats that would benefit from Unown's EV/IV/Nature spread and moves. Using it while in a forme that is not Unown will make it revert back to the Unown forme it transformed in (If an Unown transforms into Alakazam, it'll transform back to Unown-A when used again). Light of Ruin becomes Strange Steam, Psystrike becomes Psyshock, Secret Sword becomes Aura Sphere, Mind Blown becomes Flamethrower, and Seed Flare becomes Apple Acid while in a non-Unown forme.", + shortDesc: "Transform into Unown. Unown: Transform to mon.", + name: "Alphabet Soup", + isNonstandard: "Custom", + gen: 8, + pp: 20, + priority: 0, + flags: {protect: 1}, + onTryMove(source) { + this.attrLastMove('[still]'); + if (source.name !== 'Kris') { + this.add('-fail', source); + this.hint("Only Kris can use Alphabet Soup."); + return null; + } + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Dark Pulse', target); + this.add('-anim', source, 'Teleport', source); + }, + onModifyType(move, pokemon) { + let type = pokemon.types[0]; + if (type === "Bird") type = "???"; + move.type = type; + }, + onHit(target, source) { + if (!source) return; + if (source.species.id.includes('unown')) { + const monList = Object.keys(this.dex.data.Pokedex).filter(speciesid => { + const species = this.dex.getSpecies(speciesid); + if (species.id.startsWith('unown')) return false; + if (species.isNonstandard && ['Gigantamax', 'Unobtainable'].includes(species.isNonstandard)) return false; + if (['Arceus', 'Silvally'].includes(species.baseSpecies) && species.types[0] !== 'Normal') return false; + if (species.baseStats.spa < 80) return false; + if (species.baseStats.spe < 80) return false; + const unownLetter = source.species.id.charAt(5) || 'a'; + if (!species.id.startsWith(unownLetter.trim().toLowerCase())) return false; + return true; + }); + source.formeChange(this.sample(monList), this.effect); + source.setAbility('Protean'); + source.moveSlots = source.moveSlots.map(slot => { + const newMoves: {[k: string]: string} = { + lightofruin: 'strangesteam', + psystrike: 'psyshock', + secretsword: 'aurasphere', + mindblown: 'flamethrower', + seedflare: 'appleacid', + }; + if (slot.id in newMoves) { + const newMove = this.dex.getMove(newMoves[slot.id]); + const newSlot = { + id: newMove.id, + move: newMove.name, + pp: newMove.pp * 8 / 5, + maxpp: newMove.pp * 8 / 5, + disabled: slot.disabled, + used: false, + }; + return newSlot; + } + return slot; + }); + } else { + let transformingLetter = source.species.id[0]; + if (transformingLetter === 'a') transformingLetter = ''; + source.formeChange(`unown${transformingLetter}`, this.effect, true); + source.moveSlots = source.moveSlots.map(slot => { + const newMoves: {[k: string]: string} = { + strangesteam: 'lightofruin', + psyshock: 'psystrike', + aurasphere: 'secretsword', + flamethrower: 'mindblown', + appleacid: 'seedflare', + }; + if (slot.id in newMoves) { + const newMove = this.dex.getMove(newMoves[slot.id]); + const newSlot = { + id: newMove.id, + move: newMove.name, + pp: newMove.pp * 8 / 5, + maxpp: newMove.pp * 8 / 5, + disabled: slot.disabled, + used: false, + }; + return newSlot; + } + return slot; + }); + } + }, + secondary: null, + target: "normal", + type: "Dark", + }, + + // Lamp + soulswap: { + accuracy: 100, + basePower: 90, + category: "Special", + desc: "The user copies the target's positive stat stage changes and then inverts the target's stats.", + shortDesc: "Copies target's stat boosts then inverts.", + name: "Soul Swap", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Spectral Thief', target); + this.add('-anim', source, 'Teleport', source); + this.add('-anim', source, 'Topsy-Turvy', target); + }, + onHit(target, source) { + let i: BoostName; + const boosts: SparseBoostsTable = {}; + for (i in target.boosts) { + const stage = target.boosts[i]; + if (stage > 0) { + boosts[i] = stage; + } + if (target.boosts[i] !== 0) { + target.boosts[i] = -target.boosts[i]; + } + } + this.add('-message', `${source.name} stole ${target.name}'s boosts!`); + this.boost(boosts, source); + this.add('-invertboost', target, '[from] move: Soul Swap'); + }, + secondary: null, + target: "normal", + type: "Ghost", + }, + + // Lionyx + bigbang: { + accuracy: 100, + basePower: 120, + category: "Special", + desc: "The user loses HP equal to 33% of the damage dealt by this attack. Resets the field by clearing all hazards, terrains, screens, and weather.", + shortDesc: "33% recoil; removes field conditions.", + name: "Big Bang", + isNonstandard: "Custom", + gen: 8, + pp: 5, + priority: 0, + flags: {protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Extreme Evoboost', source); + this.add('-anim', source, 'Light of Ruin', target); + this.add('-anim', source, 'Dark Void', target); + }, + onHit(target, source, move) { + let success = false; + const removeAll = [ + 'reflect', 'lightscreen', 'auroraveil', 'ferrofluid', 'safeguard', 'mist', + 'spikes', 'toxicspikes', 'stealthrock', 'shiftingrocks', 'stickyweb', + ]; + const silentRemove = ['reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'shiftingrocks', 'ferrofluid']; + for (const sideCondition of removeAll) { + if (target.side.removeSideCondition(sideCondition)) { + if (!silentRemove.includes(sideCondition)) { + this.add('-sideend', target.side, this.dex.getEffect(sideCondition).name, '[from] move: Big Bang', '[of] ' + source); + } + success = true; + } + if (source.side.removeSideCondition(sideCondition)) { + if (!silentRemove.includes(sideCondition)) { + this.add('-sideend', source.side, this.dex.getEffect(sideCondition).name, '[from] move: Big Bang', '[of] ' + source); + } + success = true; + } + } + this.field.clearTerrain(); + this.field.clearWeather(); + return success; + }, + recoil: [33, 100], + secondary: null, + target: "normal", + type: "Fairy", + }, + + // LittEleven + nexthunt: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "If this Pokemon does not take damage this turn, it switches out to another Pokemon in the party and gives it the Download boost. Fails otherwise.", + shortDesc: "Focus: switch out, next Pokemon Downloads.", + name: "/nexthunt", + pp: 10, + priority: -6, + flags: {snatch: 1}, + beforeTurnCallback(pokemon) { + pokemon.addVolatile('nexthuntcheck'); + }, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Teleport', source); + }, + beforeMoveCallback(pokemon) { + if (pokemon.volatiles['nexthuntcheck'] && pokemon.volatiles['nexthuntcheck'].lostFocus) { + this.add('cant', pokemon, '/nexthunt', '/nexthunt'); + return true; + } + }, + onHit(target, source, move) { + this.add('-message', 'Time for the next hunt!'); + }, + sideCondition: 'nexthunt', + condition: { + duration: 1, + onStart(side, source) { + this.debug('/nexthunt started on ' + side.name); + this.effectData.positions = []; + for (const i of side.active.keys()) { + this.effectData.positions[i] = false; + } + this.effectData.positions[source.position] = true; + }, + onRestart(side, source) { + this.effectData.positions[source.position] = true; + }, + onSwitchInPriority: 1, + onSwitchIn(target) { + this.add('-activate', target, 'move: /nexthunt'); + let statName = 'atk'; + let bestStat = 0; + let s: StatNameExceptHP; + for (s in target.storedStats) { + if (target.storedStats[s] > bestStat) { + statName = s; + bestStat = target.storedStats[s]; + } + } + this.boost({[statName]: 1}, target, null, this.dex.getActiveMove('/nexthunt')); }, }, selfSwitch: true, @@ -4021,23 +2953,1542 @@ export const Moves: {[k: string]: ModdedMoveData} = { target: "self", type: "Normal", }, - // The Immortal - ultrasucc: { - accuracy: true, - basePower: 140, + + // Mad Monty ¾° + callamaty: { + accuracy: 100, + basePower: 75, category: "Physical", - desc: "Has a 100% chance to raise the user's Speed by one stage.", - shortDesc: "100% chance to raise the user's Speed by 1.", - name: "Ultra Succ", + desc: "30% chance to paralyze. Starts Rain Dance if not currently active.", + shortDesc: "30% paralyze. Sets Rain Dance.", + name: "Ca-LLAMA-ty", + pp: 10, + priority: 0, + flags: {contact: 1, protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Dark Void', target); + this.add('-anim', source, 'Plasma Fists', target); + }, + secondary: { + chance: 30, + status: 'par', + }, + self: { + onHit(source) { + this.field.setWeather('raindance'); + }, + }, + target: "normal", + type: "Electric", + }, + + // MajorBowman + corrosivecloud: { + accuracy: true, + basePower: 90, + category: "Special", + desc: "Has a 30% chance to burn the target. This move's type effectiveness against Steel is changed to be super effective no matter what this move's type is.", + shortDesc: "30% chance to burn. Super effective on Steel.", + name: "Corrosive Cloud", isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Poison Gas', target); + this.add('-anim', source, 'Fire Spin', target); + }, + onEffectiveness(typeMod, target, type) { + if (type === 'Steel') return 1; + }, + ignoreImmunity: {'Poison': true}, + secondary: { + chance: 30, + status: 'brn', + }, + target: "normal", + type: "Poison", + }, + + // Marshmallon + rawwwr: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "Heals the user by 50% of its max HP. Forces the target to switch to a random ally. User switches out after.", + shortDesc: "50% heal. Force out target, then switch.", + name: "RAWWWR", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {reflectable: 1, mirror: 1, sound: 1, authentic: 1, heal: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Slack Off', source); + this.add('-anim', source, 'Roar of Time', target); + this.add('-anim', source, 'Roar', target); + }, + onAfterMoveSecondarySelf(pokemon, target, move) { + this.heal(pokemon.maxhp / 2, pokemon, pokemon, move); + }, + forceSwitch: true, + selfSwitch: true, + secondary: null, + target: "normal", + type: "Dark", + }, + + // Meicoo + spamguess: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "Calls the following moves in order, each with their normal respective accuracy: Haze -> Worry Seed -> Poison Powder -> Stun Spore -> Leech Seed -> Struggle (150 BP)", + shortDesc: "Does many things then struggles.", + name: "spamguess", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {}, + onTryMove() { + this.attrLastMove('[still]'); + }, + // fruit this move. + onHit(target, source) { + for (const move of ['Haze', 'Worry Seed', 'Poison Powder', 'Stun Spore', 'Leech Seed']) { + this.useMove(move, source); + this.add(`c|${getName('Meicoo')}|That is not the answer - try again!`); + } + const strgl = this.dex.getActiveMove('Struggle'); + strgl.basePower = 150; + this.useMove(strgl, source); + this.add(`c|${getName('Meicoo')}|That is not the answer - try again!`); + }, + secondary: null, + target: "self", + type: "Fighting", + }, + + + // Mitsuki + terraforming: { + accuracy: 100, + basePower: 70, + category: "Physical", + desc: "Upon use, this move sets up Stealth Rock on the target's side of the field.", + shortDesc: "Sets up Stealth Rock.", + name: "Terraforming", + isNonstandard: "Custom", + gen: 8, + pp: 15, + priority: 0, + flags: {protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Rock Slide', target); + this.add('-anim', source, 'Ingrain', target); + this.add('-anim', source, 'Stealth Rock', target); + }, + sideCondition: 'stealthrock', + secondary: null, + target: "normal", + type: "Rock", + }, + + // n10siT + "unbind": { + accuracy: 100, + basePower: 60, + category: "Special", + desc: "Has a 100% chance to raise the user's Speed by 1 stage. If the user is a Hoopa in its Confined forme, this move is Psychic type, and Hoopa will change into its Unbound forme. If the user is a Hoopa in its Unbound forme, this move is Dark type, and Hoopa will change into its Confined forme. This move cannot be used successfully unless the user's current form, while considering Transform, is Confined or Unbound Hoopa.", + shortDesc: "Hoopa: Psychic; Unbound: Dark; 100% +1 Spe. Changes form.", + name: "Unbind", + isNonstandard: "Custom", + gen: 8, + pp: 15, + priority: 0, + flags: {protect: 1}, + onTryMove(pokemon, target, move) { + this.attrLastMove('[still]'); + if (pokemon.species.baseSpecies === 'Hoopa') { + return; + } + this.add('-fail', pokemon, 'move: Unbind'); + this.hint("Only a Pokemon whose form is Hoopa or Hoopa-Unbound can use this move."); + return null; + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Hyperspace Hole', target); + this.add('-anim', source, 'Hyperspace Fury', target); + }, + onHit(target, pokemon, move) { + if (pokemon.baseSpecies.baseSpecies === 'Hoopa') { + const forme = pokemon.species.forme === 'Unbound' ? '' : '-Unbound'; + pokemon.formeChange(`Hoopa${forme}`, this.effect, false, '[msg]'); + this.boost({spe: 1}, pokemon, pokemon, move); + } + }, + onModifyType(move, pokemon) { + if (pokemon.baseSpecies.baseSpecies !== 'Hoopa') return; + move.type = pokemon.species.name === 'Hoopa-Unbound' ? 'Dark' : 'Psychic'; + }, + secondary: null, + target: "normal", + type: "Psychic", + }, + + // naziel + notsoworthypirouette: { + accuracy: 100, + basePower: 0, + category: "Status", + desc: "55% chance to OHKO the target; otherwise, it OHKOs itself.", + shortDesc: "55% chance to OHKO target. 45% to OHKO user.", + name: "Not-so-worthy Pirouette", + isNonstandard: "Custom", + pp: 5, + priority: 0, + flags: {protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, "High Jump Kick", target); + }, + onHit(target, source) { + if (this.randomChance(11, 20)) { + target.faint(); + } else { + source.faint(); + } + }, + secondary: null, + target: "normal", + type: "Fairy", + }, + + // Nol + madhacks: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "Raises the user's Defense, Special Attack, and Special Defense by 1 stage.", + shortDesc: "Raises the user's Defense, Sp. Atk, Sp. Def by 1.", + name: "Mad Hacks", + isNonstandard: "Custom", + gen: 8, + pp: 5, + priority: 0, + flags: {snatch: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Acupressure', source); + }, + boosts: { + def: 1, + spa: 1, + spd: 1, + }, + secondary: null, + target: "self", + type: "Ghost", + }, + + // Notater517 + technotubertransmission: { + accuracy: 90, + basePower: 145, + category: "Special", + desc: "If this move is successful, the user must recharge on the following turn and cannot select a move.", + shortDesc: "User cannot move next turn.", + name: "Techno Tuber Transmission", + pp: 5, + priority: 0, + flags: {recharge: 1, protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Techno Blast', target); + this.add('-anim', source, 'Never-Ending Nightmare', target); + }, + onHit() { + this.add(`c|${getName('Notater517')}|For more phantasmic music, check out [[this link ]].`); + }, + self: { + volatileStatus: 'mustrecharge', + }, + secondary: null, + target: "normal", + type: "Ghost", + }, + + // nui + wincondition: { + accuracy: 100, + basePower: 0, + category: "Status", + desc: "Inflicts the opponent with random status of sleep, paralysis, burn, or toxic. Then uses Dream Eater, Iron Head, Fire Blast, or Venoshock, respectively.", + shortDesc: "Chooses one of four move combos at random.", + name: "Win Condition", + isNonstandard: "Custom", + pp: 10, + priority: 0, + flags: {protect: 1, reflectable: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, "Celebrate", target); + }, + onHit(target, source) { + const hax = this.sample(['slp', 'brn', 'par', 'tox']); + target.trySetStatus(hax, source); + if (hax === 'slp') { + this.useMove('Dream Eater', source); + } else if (hax === 'par') { + this.useMove('Iron Head', source); + } else if (hax === 'brn') { + this.useMove('Fire Blast', source); + } else if (hax === 'tox') { + this.useMove('Venoshock', source); + } + }, + secondary: null, + target: "normal", + type: "Fairy", + }, + + // OM~! + omzoom: { + accuracy: 100, + basePower: 70, + category: "Physical", + desc: "If this move is successful and the user has not fainted, the user switches out even if it is trapped and is replaced immediately by a selected party member. The user does not switch out if there are no unfainted party members, or if the target switched out using an Eject Button or through the effect of the Emergency Exit or Wimp Out Abilities.", + shortDesc: "User switches out after damaging the target.", + name: "OM Zoom", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Icicle Spear', target); + this.add('-anim', source, 'U-turn', target); + }, + onHit() { + this.add(`c|${getName('OM~!')}|Bang Bang`); + }, + flags: {protect: 1, mirror: 1}, + selfSwitch: true, + secondary: null, + target: "normal", + type: "Ice", + }, + + // Overneat + healingyou: { + accuracy: 100, + basePower: 115, + category: "Physical", + desc: "Heals the target by 50% of their maximum HP and eliminates any status problem before dealing damage, and lowers the target's Defense and Special Defense stat by 1 stage after dealing damage.", + shortDesc: "Foe: heal 50%HP & status, dmg, then -1 Def/SpD.", + name: "Healing you?", + isNonstandard: "Custom", + gen: 8, + pp: 5, + priority: 0, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Heal Pulse', target); + this.heal(Math.ceil(target.baseMaxhp * 0.5)); + target.cureStatus(); + this.add('-anim', source, 'Close Combat', target); + }, + flags: {contact: 1, mirror: 1, protect: 1}, + secondary: { + chance: 100, + boosts: { + def: -1, + spd: -1, + }, + }, + target: "normal", + type: "Dark", + }, + + // Pants + wistfulthinking: { + accuracy: 100, + basePower: 0, + category: "Status", + desc: "Burns the target and switches out. The next Pokemon on the user's side heals 1/16 of their maximum HP per turn until they switch out.", + shortDesc: "Burn foe; switch out. Heals replacement.", + name: "Wistful Thinking", + isNonstandard: "Custom", + pp: 10, + priority: 0, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Will-O-Wisp', target); + this.add('-anim', source, 'Parting Shot', target); + }, + onHit(target, source) { + target.setStatus('brn', source, null, true); + }, + self: { + sideCondition: 'givewistfulthinking', + }, + condition: { + onStart(pokemon) { + this.add('-singleturn', pokemon, 'move: Wistful Thinking'); + }, + onResidualOrder: 5, + onResidualSubOrder: 5, + onResidual(pokemon) { + this.heal(pokemon.baseMaxhp / 16); + }, + }, + flags: {protect: 1, reflectable: 1}, + selfSwitch: true, + secondary: null, + target: "normal", + type: "Ghost", + }, + + // Paradise + rapidturn: { + accuracy: 100, + basePower: 50, + category: "Physical", + desc: "Removes entry hazards, then user switches out after dealing damage", + shortDesc: "Removes hazards then switches out", + name: "Rapid Turn", + isNonstandard: "Custom", + gen: 8, + pp: 20, + priority: 0, + flags: {contact: 1, protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Rapid Spin', target); + this.add('-anim', source, 'U-turn', target); + }, + onAfterHit(target, pokemon) { + const sideConditions = [ + 'spikes', 'toxicspikes', 'stealthrock', 'shiftingrocks', 'stickyweb', 'ferrofluid', 'gmaxsteelsurge', + ]; + for (const condition of sideConditions) { + if (pokemon.hp && pokemon.side.removeSideCondition(condition)) { + this.add('-sideend', pokemon.side, this.dex.getEffect(condition).name, '[from] move: Rapid Turn', '[of] ' + pokemon); + } + } + if (pokemon.hp && pokemon.volatiles['partiallytrapped']) { + pokemon.removeVolatile('partiallytrapped'); + } + }, + onAfterSubDamage(damage, target, pokemon) { + const sideConditions = [ + 'spikes', 'toxicspikes', 'stealthrock', 'shiftingrocks', 'stickyweb', 'ferrofluid', 'gmaxsteelsurge', + ]; + for (const condition of sideConditions) { + if (pokemon.hp && pokemon.side.removeSideCondition(condition)) { + this.add('-sideend', pokemon.side, this.dex.getEffect(condition).name, '[from] move: Rapid Turn', '[of] ' + pokemon); + } + } + if (pokemon.hp && pokemon.volatiles['partiallytrapped']) { + pokemon.removeVolatile('partiallytrapped'); + } + }, + selfSwitch: true, + secondary: null, + target: "normal", + type: "Normal", + }, + + // PartMan + balefulblaze: { + accuracy: 100, + basePower: 75, + basePowerCallback(pokemon) { + if (pokemon.set.shiny) { + return 95; + } + return 75; + }, + category: "Special", + desc: "Raises the user's Special Attack by 1 stage if this move knocks out the target. If the user is shiny, the move's Base Power becomes 95.", + shortDesc: "+1 SpA if this KOes the target. Shiny: BP=95.", + name: "Baleful Blaze", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {protect: 1, mirror: 1, defrost: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Inferno', target); + this.add('-anim', source, 'Hex', target); + }, + onEffectiveness(typeMod, target, type, move) { + return typeMod + this.dex.getEffectiveness('Ghost', type); + }, + onAfterMoveSecondarySelf(pokemon, target, move) { + if (!target || target.fainted || target.hp <= 0) { + this.add(`c|${getName('PartMan')}|FOR SNOM!`); + this.boost({spa: 1}, pokemon, pokemon, move); + } + }, + secondary: null, + target: "normal", + type: "Fire", + }, + + // peapod c + submartingale: { + accuracy: 100, + basePower: 0, + category: "Status", + desc: "Inflicts the target with burn, toxic, or paralysis, then sets up a Substitute.", + shortDesc: "Inflicts burn/toxic/paralysis. Makes Substitute.", + name: "Submartingale", + isNonstandard: "Custom", + pp: 10, + priority: 0, + flags: {protect: 1, reflectable: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, "Dark Void", target); + this.add('-anim', source, "Celebrate", target); + }, + onTryHit(target, source) { + this.useMove('Substitute', source); + }, + onHit(target, source) { + target.trySetStatus('brn', source); + target.trySetStatus('tox', source); + target.trySetStatus('par', source); + }, + secondary: null, + target: "normal", + type: "Dark", + }, + + // Perish Song + shiftingrocks: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "Entry Hazard. The damage is static for all types, (doesn't factor type effectiveness of rock) and is equal to (7+n)%, n being the number of times a Pokemon takes damage from the hazard. Starts at 0 and caps at 10. After 10 turns, (when the damage reaches 17%) Shifting Rocks explode (disappear) and deal 80-Base Power Rock-type damage to the foe.", + shortDesc: "Hazard: ramps up damage then explodes.", + name: "Shifting Rocks", + isNonstandard: "Custom", + gen: 8, + pp: 15, + priority: 0, + flags: {reflectable: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Stealth Rock', target); + this.add('-anim', source, 'Stealth Rock', target); + }, + sideCondition: 'shiftingrocks', + condition: { + // this is a side condition + onStart(side) { + if (this.field.isTerrain('waveterrain')) { + this.add('-message', `Wave Terrain prevented Shifting Rocks from starting!`); + return null; + } + this.add('-sidestart', side, 'Shifting Rocks'); + this.add("-message", `Shifting Rocks were set!`); + this.effectData.damage = 7; + }, + onSwitchIn(pokemon) { + if (pokemon.hasItem('heavydutyboots')) return; + if (this.effectData.damage >= 17) { + const activeMove = { + id: 'rocks' as ID, + basePower: 80, + type: 'Rock', + category: 'Physical', + willCrit: false, + }; + const damage = this.getDamage(pokemon, pokemon, activeMove as ActiveMove); + if (typeof damage !== 'number') throw new Error("Shifting Rocks damage not dealt"); + if (this.damage(damage)) { + this.add('-message', `${pokemon.name} was hurt by the shifting rocks!`); + } + this.effectData.damage = 7; + pokemon.side.removeSideCondition(`shiftingrocks`); + return false; + } + if (this.damage(this.effectData.damage * pokemon.maxhp / 100)) { + this.add('-message', `${pokemon.name} was hurt by the shifting rocks!`); + } + this.effectData.damage++; + }, + onEnd(side) { + this.add('-sideend', side, 'move: Shifting Rocks'); + this.add("-message", `The Shifting Rocks were removed!`); + }, + }, + secondary: null, + target: "foeSide", + type: "Rock", + }, + + // phiwings99 + ghostof1v1past: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "Imprisons and traps the target, and then transforms into them. The user faints if the target faints.", + shortDesc: "Trap + ImprisonForm. Faints if the target faints.", + name: "Ghost of 1v1 Past", + isNonstandard: "Custom", + gen: 8, pp: 1, + noPPBoosts: true, + priority: 0, + flags: {protect: 1, reflectable: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Imprison', source); + this.add('-anim', source, 'Mean Look', target); + this.add('-anim', source, 'Transform', target); + }, + onHit(target, pokemon, move) { + target.addVolatile('trapped', pokemon, move, 'trapper'); + pokemon.addVolatile('imprison', pokemon, move); + if (!pokemon.transformInto(target)) { + return false; + } + pokemon.addVolatile('ghostof1v1past', pokemon); + pokemon.volatiles['ghostof1v1past'].targetPokemon = target; + }, + condition: { + onAnyFaint(target) { + if (target === this.effectData.targetPokemon) this.effectData.source.faint(); + }, + }, + secondary: null, + target: "normal", + type: "Ghost", + }, + + // piloswine gripado + iciclespirits: { + accuracy: 100, + basePower: 90, + category: "Physical", + desc: "The user recovers 1/2 the HP lost by the target, rounded half up. If Big Root is held by the user, the HP recovered is 1.3x normal, rounded half down.", + shortDesc: "User recovers 50% of the damage dealt.", + name: "Icicle Spirits", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {contact: 1, protect: 1, mirror: 1, heal: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Horn Leech', target); + }, + drain: [1, 2], + secondary: null, + target: "normal", + type: "Ice", + }, + + // PiraTe Princess + dungeonsdragons: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "Prevents the target from switching out and adds Dragon to the target's type. Has a 5% chance to either confuse the user or guarantee that the next attack is a critical hit, 15% chance to raise the user's Attack, Defense, Special Attack, Special Defense, or Speed by 1 stage, and a 15% chance to raise user's Special Attack and Speed by 1 stage.", + shortDesc: "Target: can't switch,+Dragon. Does other things.", + name: "Dungeons & Dragons", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {protect: 1, reflectable: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Imprison', target); + this.add('-anim', source, 'Trick-or-Treat', target); + this.add('-anim', source, 'Shell Smash', source); + }, + onHit(target, source, move) { + this.add(`c|${getName('PiraTe Princess')}|did someone say d&d?`); + target.addVolatile('trapped', source, move, 'trapper'); + if (!target.hasType('Dragon') && target.addType('Dragon')) { + this.add('-start', target, 'typeadd', 'Dragon', '[from] move: Dungeons & Dragons'); + } + const result = this.random(21); + if (result === 20) { + source.addVolatile('laserfocus'); + } else if (result >= 2 && result <= 16) { + const boost: SparseBoostsTable = {}; + const stats: BoostName[] = ['atk', 'def', 'spa', 'spd', 'spe']; + boost[stats[this.random(5)]] = 1; + this.boost(boost, source); + } else if (result >= 17 && result <= 19) { + this.boost({spa: 1, spe: 1}, source); + } else { + source.addVolatile('confusion'); + } + }, + target: "normal", + type: "Dragon", + }, + + // Psynergy + clearbreath: { + accuracy: 100, + basePower: 0, + basePowerCallback(pokemon, target) { + let power = 60 + 20 * target.positiveBoosts(); + if (power > 200) power = 200; + return power; + }, + category: "Special", + desc: "Power is equal to 60+(X*20), where X is the target's total stat stage changes that are greater than 0, but not more than 200 power.", + shortDesc: "60 power +20 for each of the target's stat boosts.", + isNonstandard: "Custom", + gen: 8, + name: "Clear Breath", + pp: 10, + priority: 0, + flags: {protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Dragon Breath', target); + this.add('-anim', source, 'Haze', target); + }, + secondary: null, + target: "normal", + type: "Flying", + }, + + // ptoad + croak: { + accuracy: 100, + basePower: 20, + basePowerCallback(pokemon, target, move) { + const bp = move.basePower + 20 * pokemon.positiveBoosts(); + if (bp >= 140) return 140; + return bp; + }, + category: "Special", + desc: "Randomly raises a stat (other than evasion and accuracy) by 1 before attacking. + 20 power for each of the user's stat boosts. Base Power maxes out at 140, regardless of boosts. Sound based move.", + shortDesc: "1 random boost, then attacks. +20 pow/boost.", + name: "Croak", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {protect: 1, mirror: 1, sound: 1, authentic: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source, move) { + this.add('-anim', source, 'Splash', source); + const stats: BoostName[] = []; + let stat: BoostName; + const exclude: string[] = ['accuracy', 'evasion']; + for (stat in source.boosts) { + if (source.boosts[stat] < 6 && !exclude.includes(stat)) { + stats.push(stat); + } + } + if (stats.length) { + const randomStat = this.sample(stats); + const boost: SparseBoostsTable = {}; + boost[randomStat] = 1; + this.boost(boost, source, source, move); + } + this.add('-anim', source, 'Hyper Voice', source); + }, + secondary: null, + target: "normal", + type: "Water", + }, + + // used for ptoad's ability + swampyterrain: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "For 5 turns, the terrain becomes Swampy Terrain. During the effect, the power of Electric-type, Grass-type, and Ice-type attacks made by grounded Pokemon are halved and Water and Ground types heal 1/16 at the end of each turn if grounded. Fails if the current terrain is Swampy Terrain.", + shortDesc: "5trn. Grounded:-Elec/Grs/Ice pow, Wtr/Grd:Lefts.", + name: "Swampy Terrain", + isNonstandard: "Custom", + pp: 10, + priority: 0, + flags: {nonsky: 1}, + terrain: 'swampyterrain', + condition: { + duration: 5, + durationCallback(source, effect) { + if (source?.hasItem('terrainextender')) { + return 8; + } + return 5; + }, + onBasePowerPriority: 6, + onBasePower(basePower, attacker, defender, move) { + if (['Electric', 'Grass', 'Ice'].includes(move.type) && attacker.isGrounded() && !attacker.isSemiInvulnerable()) { + this.debug('swampy terrain weaken'); + return this.chainModify(0.5); + } + }, + onStart(battle, source, effect) { + if (effect?.effectType === 'Ability') { + this.add('-fieldstart', 'move: Swampy Terrain', '[from] ability: ' + effect, '[of] ' + source); + } else { + this.add('-fieldstart', 'move: Swampy Terrain'); + } + this.add('-message', 'The battlefield became swamped!'); + }, + onResidualOrder: 5, + onResidualSubOrder: 3, + onResidual() { + this.eachEvent('Terrain'); + }, + onTerrain(pokemon) { + if ((pokemon.hasType('Water') || pokemon.hasType('Ground')) && pokemon.isGrounded() && !pokemon.isSemiInvulnerable()) { + this.debug('Pokemon is grounded and a Water or Ground type, healing through Swampy Terrain.'); + if (this.heal(pokemon.baseMaxhp / 16, pokemon, pokemon)) { + this.add('-message', `${pokemon.name} was healed by the terrain!`); + } + } + }, + onEnd() { + if (!this.effectData.duration) this.eachEvent('Terrain'); + this.add('-fieldend', 'move: Swampy Terrain'); + }, + }, + secondary: null, + target: "all", + type: "Ground", + }, + + // quadrophenic + triplethreat: { + accuracy: 100, + basePower: 40, + category: "Physical", + desc: "Has a 40, 60, or 100 Base Power and a 10%, 25%, or 40% chance to apply any non-volatile status aside from freeze; the status chance and base power increase for each consecutive hit up to 3.", + shortDesc: "Chance to random status. Chance&BP+ per use.", + name: "Triple Threat", + isNonstandard: "Custom", + gen: 8, + pp: 20, + priority: 0, + flags: {protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + beforeTurnCallback(pokemon) { + pokemon.addVolatile('triplethreat'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Tri Attack', target); + }, + secondary: { + chance: 10, + onHit(target, source) { + const hax = this.sample(['slp', 'brn', 'par', 'psn', 'tox']); + target.trySetStatus(hax, source); + }, + }, + condition: { + onStart(pokemon) { + this.effectData.numConsecutive = 0; + this.effectData.lastMove = 'triplethreat'; + }, + onTryMovePriority: -2, + onTryMove(pokemon, target, move) { + if (move.id !== 'triplethreat') { + pokemon.removeVolatile('triplethreat'); + return; + } + if (this.effectData.lastMove === move.id) { + this.effectData.numConsecutive++; + } else { + this.effectData.numConsecutive = 0; + } + if (this.effectData.numConsecutive >= 3) this.effectData.numConsecutive = 0; + this.effectData.lastMove = move.id; + }, + onModifyMove(move) { + if (move.secondaries && move.id === 'triplethreat') { + const bpModif = [40, 60, 100]; + const secModif = [10, 25, 40]; + const numConsecutive = this.effectData.numConsecutive > 2 ? 2 : this.effectData.numConsecutive; + move.basePower = bpModif[numConsecutive]; + for (const secondary of move.secondaries) { + if (secondary.chance) secondary.chance = secModif[numConsecutive]; + } + } + }, + }, + target: "normal", + type: "Normal", + }, + + // Rabia + psychodrive: { + accuracy: 100, + basePower: 80, + category: "Special", + desc: "Has a 30% chance to boost the user's Speed by 1 stage.", + shortDesc: "30% chance to boost the user's Spe by 1.", + name: "Psycho Drive", + isNonstandard: "Custom", + gen: 8, + pp: 15, + priority: 0, + flags: {protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Genesis Supernova', target); + }, + secondary: { + chance: 30, + self: { + boosts: {spe: 1}, + }, + }, + target: "normal", + type: "Psychic", + }, + + // Rach + spindawheel: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "The user uses a random hazard-setting move; burns, badly poisons, or paralyzes the target; and then switches out.", + shortDesc: "Sets random hazard; brn/tox/par; switches.", + name: "Spinda Wheel", + isNonstandard: "Custom", + gen: 8, + pp: 20, + priority: 0, + flags: {reflectable: 1, protect: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + target.m.spindaHazard = this.sample(['Sticky Web', 'Stealth Rock', 'Spikes', 'Toxic Spikes', 'G-Max Steelsurge']); + target.m.spindaStatus = this.sample(['Thunder Wave', 'Toxic', 'Will-O-Wisp']); + if (target.m.spindaHazard) { + this.add('-anim', source, target.m.spindaHazard, target); + } + if (target.m.spindaStatus) { + this.add('-anim', source, target.m.spindaStatus, target); + } + }, + onHit(target, source, move) { + if (target) { + if (target.m.spindaHazard) { + target.side.addSideCondition(target.m.spindaHazard); + } + if (target.m.spindaStatus) { + const s = target.m.spindaStatus; + target.trySetStatus(s === 'Toxic' ? 'tox' : s === 'Thunder Wave' ? 'par' : 'brn'); + } + } + }, + selfSwitch: true, + secondary: null, + target: "normal", + type: "Normal", + }, + + // Rage + shockedlapras: { + accuracy: 100, + basePower: 75, + category: "Special", + desc: "Has a 100% chance to paralyze the user.", + shortDesc: "100% chance to paralyze the user.", + name: ":shockedlapras:", + isNonstandard: "Custom", + gen: 8, + pp: 15, + priority: 0, + flags: {protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Thunder', target); + if (!source.status) this.add('-anim', source, 'Thunder Wave', source); + }, + onHit() { + this.add(`raw|`); + }, + secondary: { + chance: 100, + self: { + status: 'par', + }, + }, + target: "normal", + type: "Electric", + }, + + // used for Rage's ability + inversionterrain: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "For 5 turns, the terrain becomes Inversion Terrain. During the effect, the the type chart is inverted, and grounded, paralyzed Pokemon have their Speed doubled. Fails if the current terrain is Inversion Terrain.", + shortDesc: "5 turns. Type chart inverted. Par: 2x Spe.", + name: "Inversion Terrain", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {}, + terrain: 'inversionterrain', + condition: { + duration: 5, + durationCallback(source, effect) { + if (source?.hasItem('terrainextender')) { + return 8; + } + return 5; + }, + onNegateImmunity: false, + onEffectivenessPriority: 1, + onEffectiveness(typeMod, target, type, move) { + // The effectiveness of Freeze Dry on Water isn't reverted + if (move && move.id === 'freezedry' && type === 'Water') return; + if (move && !this.dex.getImmunity(move, type)) return 1; + return -typeMod; + }, + onStart(battle, source, effect) { + if (effect?.effectType === 'Ability') { + this.add('-fieldstart', 'move: Inversion Terrain', '[from] ability: ' + effect, '[of] ' + source); + } else { + this.add('-fieldstart', 'move: Inversion Terrain'); + } + this.add('-message', 'The battlefield became upside down!'); + }, + onResidualOrder: 5, + onResidualSubOrder: 3, + onResidual() { + this.eachEvent('Terrain'); + }, + onEnd() { + if (!this.effectData.duration) this.eachEvent('Terrain'); + this.add('-fieldend', 'move: Inversion Terrain'); + }, + }, + secondary: null, + target: "all", + type: "Psychic", + }, + + // Raihan Kibana + stonykibbles: { + accuracy: 100, + basePower: 90, + category: "Physical", + desc: "For 5 turns, the weather becomes Sandstorm. At the end of each turn except the last, all active Pokemon lose 1/16 of their maximum HP, rounded down, unless they are a Ground, Rock, or Steel type, or have the Magic Guard, Overcoat, Sand Force, Sand Rush, or Sand Veil Abilities. During the effect, the Special Defense of Rock-type Pokemon is multiplied by 1.5 when taking damage from a special attack. Lasts for 8 turns if the user is holding Smooth Rock. Fails if the current weather is Sandstorm.", + shortDesc: "Sets Sandstorm.", + name: "Stony Kibbles", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {contact: 1, protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onHit() { + this.add(`c|${getName('Raihan Kibana')}|Let the winds blow! Stream forward, Sandstorm!`); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Rock Slide', target); + this.add('-anim', source, 'Crunch', target); + this.add('-anim', source, 'Sandstorm', target); + }, + weather: 'Sandstorm', + target: "normal", + type: "Normal", + }, + + // Raj.Shoot + fanservice: { + accuracy: 100, + basePower: 90, + category: "Physical", + desc: "The user has its Attack and Speed raised by 1 stage after KOing a target. If the user is a Charizard in its base form, it will Mega Evolve into Mega Charizard X.", + shortDesc: "+1 Atk/Spe after KO. Mega evolves user.", + name: "Fan Service", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {contact: 1, protect: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source, move) { + this.add('-anim', source, 'Sacred Fire', target); + }, + onAfterMoveSecondarySelf(pokemon, target, move) { + if (!target || target.fainted || target.hp <= 0) { + this.boost({atk: 1, spe: 1}, pokemon, pokemon, move); + } + }, + onHit(target, source) { + if (source.species.id === 'charizard') { + this.runMegaEvo(source); + } + }, + secondary: null, + target: "normal", + type: "Grass", + }, + + // Ransei + ripsei: { + accuracy: 100, + basePower: 0, + damageCallback(pokemon) { + const damage = pokemon.hp; + return damage; + }, + category: "Special", + desc: "Deals damage to the target equal to the user's current HP. If this move is successful, the user faints.", + shortDesc: "Does damage equal to the user's HP. User faints.", + name: "ripsei", + isNonstandard: "Custom", + gen: 8, + pp: 5, + priority: 1, + flags: {contact: 1, protect: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Final Gambit', target); + }, + onAfterMove(pokemon, target, move) { + if (pokemon.moveThisTurnResult === true) { + pokemon.faint(); + } + }, + secondary: null, + target: "normal", + type: "Fighting", + }, + + // RavioliQueen + witchinghour: { + accuracy: 90, + basePower: 60, + category: "Special", + desc: "50% chance to trap the target, dealing 1/8th of their HP, rounded down, in damage each turn it is trapped.", + shortDesc: "50% to trap, dealing 1/8 each turn.", + name: "Witching Hour", + pp: 5, + priority: 0, + flags: {protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Spirit Shackle', target); + this.add('-anim', source, 'Curse', target); + }, + secondary: { + chance: 50, + volatileStatus: 'haunting', + }, + target: "normal", + type: "Ghost", + }, + + // for RavioliQueen's ability + pitchblackterrain: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "For 5 turns, Non Ghost types take 1/16th damage; Has boosting effects on Mismagius.", + shortDesc: "5 turns. Non Ghost types take 1/16th damage; Has boosting effects on Mismagius.", + name: "Pitch Black Terrain", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {}, + terrain: 'pitchblackterrain', + condition: { + duration: 5, + durationCallback(source, effect) { + if (source?.hasItem('terrainextender')) { + return 8; + } + return 5; + }, + onHit(target, source, move) { + if (!target.hp || target.species.name !== 'Mismagius') return; + if (move?.effectType === 'Move' && move.category !== 'Status') { + if (this.boost({spe: 1}, target)) { + this.add('-message', `${target.name} got a boost by the terrain!`); + } + } + }, + onSwitchInPriority: -1, + onSwitchIn(target) { + if (target?.species.name !== 'Mismagius') return; + if (this.boost({spa: 1, spd: 1}, target)) { + this.add('-message', `${target.name} got a boost by the terrain!`); + } + }, + onStart(battle, source, effect) { + if (effect?.effectType === 'Ability') { + this.add('-fieldstart', 'move: Pitch Black Terrain', '[from] ability: ' + effect, '[of] ' + source); + } else { + this.add('-fieldstart', 'move: Pitch Black Terrain'); + } + this.add('-message', 'The battlefield became dark!'); + if (source?.species.name !== 'Mismagius') return; + if (this.boost({spa: 1, spd: 1}, source)) { + this.add('-message', `${source.name} got a boost by the terrain!`); + } + }, + onResidualOrder: 5, + onResidualSubOrder: 3, + onResidual() { + this.eachEvent('Terrain'); + }, + onTerrain(pokemon) { + if (pokemon.isSemiInvulnerable()) return; + if (!pokemon || pokemon.hasType('Ghost')) return; + if (this.damage(pokemon.baseMaxhp / 16, pokemon)) { + this.add('-message', `${pokemon.name} was hurt by the terrain!`); + } + }, + onEnd() { + this.add('-fieldend', 'move: Pitch Black Terrain'); + }, + }, + secondary: null, + target: "all", + type: "Ghost", + }, + + // Robb576 + integeroverflow: { + accuracy: true, + basePower: 200, + category: "Special", + desc: "This move becomes a physical attack if the user's Attack is greater than its Special Attack, including stat stage changes. This move and its effects ignore the Abilities of other Pokemon.", + shortDesc: "Physical if user's Atk > Sp. Atk. Ignores Abilities.", + name: "Integer Overflow", + isNonstandard: "Custom", + gen: 8, + pp: 1, + noPPBoosts: true, priority: 0, flags: {}, onTryMove() { this.attrLastMove('[still]'); }, onPrepareHit(target, source) { - this.add('-anim', source, "Dragon Ascent", target); + this.add('-anim', source, 'Light That Burns The Sky', target); + }, + onModifyMove(move, pokemon) { + if (pokemon.getStat('atk', false, true) > pokemon.getStat('spa', false, true)) move.category = 'Physical'; + }, + ignoreAbility: true, + isZ: "modium6z", + secondary: null, + target: "normal", + type: "Psychic", + }, + + mode5offensive: { + accuracy: true, + basePower: 30, + category: "Special", + desc: "This move hits three times. Every hit has a 20% chance to drop the target's SpD by 1 stage.", + shortDesc: "3 hits. Each hit: 20% -1 SpD.", + name: "Mode [5: Offensive]", + isNonstandard: "Custom", + gen: 8, + pp: 15, + priority: 0, + flags: {protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Focus Blast', target); + this.add('-anim', source, 'Zap Cannon', target); + }, + secondary: { + chance: 20, + boosts: { + spd: -1, + }, + }, + multihit: 3, + target: "normal", + type: "Fighting", + }, + + mode7defensive: { + accuracy: 100, + basePower: 0, + category: "Status", + desc: "This move cures the user's party of all status conditions, and then forces the target to switch to a random ally.", + shortDesc: "Heal Bell + Whirlwind.", + name: "Mode [7: Defensive]", + isNonstandard: "Custom", + gen: 8, + pp: 15, + priority: -6, + flags: {reflectable: 1, protect: 1, sound: 1, authentic: 1}, + forceSwitch: true, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Heal Bell', source); + this.add('-anim', source, 'Roar', source); + }, + onHit(pokemon, source) { + this.add('-activate', source, 'move: Mode [7: Defensive]'); + const side = source.side; + let success = false; + for (const ally of side.pokemon) { + if (ally.hasAbility('soundproof')) continue; + if (ally.cureStatus()) success = true; + } + return success; + }, + target: "normal", + type: "Normal", + }, + + // SectoniaServant + homunculussvanity: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "Raises the Special Attack of the user, and either Defense or Special Defense randomly, by 1 stage.", + shortDesc: "Gives +1 SpA and +1 Def or SpD", + name: "Homunculus's Vanity", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {snatch: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Cosmic Power', source); + this.add('-anim', source, 'Psychic', target); + }, + self: { + onHit(source) { + const boosts: {[k: string]: number} = {}; + boosts['spa'] = 1; + boosts[['def', 'spd'][this.random(2)]] = 1; + this.boost(boosts, source); + this.add(`c|${getName('SectoniaServant')}|Jelly baby ;w;`); + }, + }, + secondary: null, + target: "self", + type: "Psychic", + zMove: {boost: {atk: 1}}, + }, + + // Segmr + tsukuyomi: { + accuracy: 100, + basePower: 0, + category: "Status", + desc: "If the user is not a Ghost type, lowers the user's Speed by 1 stage and raises the user's Attack and Defense by 1 stage. If the user is a Ghost type, the user loses 1/2 of its maximum HP, rounded down and even if it would cause fainting, in exchange for the target losing 1/4 of its maximum HP, rounded down, at the end of each turn while it is active. If the target uses Baton Pass, the replacement will continue to be affected. Fails if there is no target or if the target is already affected. Prevents the target from switching out. The target can still switch out if it is holding Shed Shell or uses Baton Pass, Parting Shot, Teleport, U-turn, or Volt Switch. If the target leaves the field using Baton Pass, the replacement will remain trapped. The effect ends if the user leaves the field.", + shortDesc: "Curse + Mean Look.", + name: "Tsukuyomi", + isNonstandard: "Custom", + gen: 8, + pp: 5, + priority: 0, + flags: {protect: 1, reflectable: 1}, + isFutureMove: true, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Dark Void', target); + if (source.hasType('Ghost')) { + this.add('-anim', source, 'Curse', target); + } else { + this.add('-anim', source, 'Curse', source); + } + }, + volatileStatus: 'curse', + onModifyMove(move, source, target) { + if (!source.hasType('Ghost')) { + move.target = move.nonGhostTarget as MoveTarget; + } + }, + onTryHit(target, source, move) { + if (!source.hasType('Ghost')) { + delete move.volatileStatus; + move.onHit = function (t, s, m) { + s.side.foe.active[0].addVolatile('trapped', s, m, 'trapper'); + }; + move.self = {boosts: {spe: -1, atk: 1, def: 1}}; + } else if (move.volatileStatus && target.volatiles['curse']) { + return false; + } + }, + onHit(target, source, move) { + this.directDamage(source.maxhp / 2, source, source); + source.side.foe.active[0].addVolatile('trapped', source, move, 'trapper'); + if (source.name === 'Segmr' && !source.illusion) { + this.add(`c|${getName('Segmr')}|I don't like naruto actually let someone else write this message plz.`); + } + }, + condition: { + onStart(pokemon, source) { + this.add('-start', pokemon, 'Curse', '[of] ' + source); + }, + onResidualOrder: 10, + onResidual(pokemon) { + this.damage(pokemon.baseMaxhp / 4); + }, + }, + secondary: null, + target: "normal", + type: "Dark", + nonGhostTarget: "self", + }, + + // sejesensei + badopinion: { + accuracy: 90, + basePower: 120, + category: "Physical", + desc: "Forces the opponent out. The user's Defense is raised by 1 stage upon hitting.", + shortDesc: "Forces the opponent out. +1 Def.", + name: "Bad Opinion", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: -6, + flags: {protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Hyper Voice', target); + this.add('-anim', source, 'Sludge Bomb', target); + }, + onHit() { + this.add(`c|${getName('sejesensei')}|Please go read To Love-Ru I swear its really good, wait... don’t leave…`); + }, + self: { + boosts: { + def: 1, + }, + }, + forceSwitch: true, + secondary: null, + target: "normal", + type: "Poison", + }, + + // Seso + legendaryswordsman: { + accuracy: 85, + basePower: 95, + onTry(source, target) { + this.attrLastMove('[still]'); + const action = this.queue.willMove(target); + const move = action?.choice === 'move' ? action.move : null; + if (!move || (move.category === 'Status' && move.id !== 'mefirst') || target.volatiles['mustrecharge']) { + if (move?.category === 'Status') { + this.add(`c|${getName('Seso')}|Irritating a better swordsman than yourself is always a good way to end up dead.`); + } else { + this.add(`c|${getName('Seso')}|Scars on the back are a swordsman's shame.`); + } + return false; + } + }, + category: "Physical", + desc: "If the move hits, the user gains +1 Speed. This move deals not very effective damage to Flying-type Pokemon. This move fails if the target does not intend to attack.", + shortDesc: "+1 Spe on hit. Fails if target doesnt attack.", + name: "Legendary Swordsman", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 1, + flags: {contact: 1, protect: 1}, + ignoreImmunity: {'Ground': true}, + onEffectiveness(typeMod, target, type) { + if (type === 'Flying') return -1; + }, + onTryMove(source, target, move) { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add(`c|${getName('Seso')}|FORWARD!`); + this.add('-anim', source, 'Gear Grind', target); + this.add('-anim', source, 'Thief', target); }, secondary: { chance: 100, @@ -4047,188 +4498,786 @@ export const Moves: {[k: string]: ModdedMoveData} = { }, }, }, - isZ: "buzzniumz", target: "normal", - type: "Fighting", - }, - // The Leprechaun - gyroballin: { - accuracy: 100, - basePower: 0, - basePowerCallback(pokemon, target) { - let power = (Math.floor(25 * target.getStat('spe') / pokemon.getStat('spe')) || 1); - if (power > 150) power = 150; - this.debug('' + power + ' bp'); - return power; - }, - category: "Physical", - desc: "Base Power is equal to (25 * target's current Speed / user's current Speed) + 1, rounded down, but not more than 150. If the user's current Speed is 0, this move's power is 1. Summons Trick Room unless Trick Room is already active.", - shortDesc: "More power if slower; sets Trick Room.", - name: "Gyro Ballin'", - isNonstandard: "Custom", - pp: 5, - priority: 0, - flags: {bullet: 1, contact: 1, protect: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Gyro Ball", target); - }, - onAfterMoveSecondarySelf(pokemon) { - if (!this.field.pseudoWeather.trickroom) { - this.field.addPseudoWeather('trickroom', pokemon); - } - this.add('-fieldactivate', 'move: Pay Day'); // Coins are scattered on the ground - }, - secondary: null, - target: "normal", - type: "Steel", - zMove: {basePower: 160}, - contestType: "Cool", - }, - // Tony - greed: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "If there is no Stealth Rock on the foe's side of the field, sets Stealth Rock and one layer of Spikes. Otherwise, it sets two layers of Spikes.", - shortDesc: "SR present: Spikes x2; otherwise: SR + Spikes.", - name: "Greed", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {reflectable: 1, nosky: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - if (!target.side.sideConditions['stealthrock']) { - this.add('-anim', source, "Stealth Rock", target); - } else { - this.add('-anim', source, "Spikes", target); - } - this.add('-anim', source, "Spikes", target); - }, - onHitSide(target, source) { - if (!target.sideConditions['stealthrock']) { - target.addSideCondition('stealthrock', source); - } else { - target.addSideCondition('spikes', source); - } - target.addSideCondition('spikes', source); - }, - secondary: null, - target: "foeSide", type: "Ground", }, - // torkool - smokebomb: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "Moves all hazards that are on the user's side of the field to the foe's side of the field. Sets Stealth Rock on the foe's side, after which the user switches out.", - shortDesc: "Hazards -> foe side. Set SR. User switches out.", - name: "Smoke Bomb", + + // Shadecession + shadeuppercut: { + accuracy: 100, + basePower: 90, + category: "Physical", + desc: "This move ignores type effectiveness, substitutes, and the opposing side's Reflect, Light Screen, Safeguard, Mist and Aurora Veil.", + shortDesc: "Ignores typing, sub, & screens.", + name: "Shade Uppercut", isNonstandard: "Custom", + gen: 8, pp: 10, priority: 0, - flags: {snatch: 1, mirror: 1, reflectable: 1, authentic: 1}, + flags: {contact: 1, protect: 1, mirror: 1}, onTryMove() { this.attrLastMove('[still]'); }, onPrepareHit(target, source) { - this.add('-anim', source, "Smokescreen", target); - this.add('-anim', source, "Parting Shot", target); + this.add('-anim', source, 'Sky Uppercut', target); + this.add('-anim', source, 'Shadow Sneak', target); }, - onHit(target, source) { - const sideConditions = {spikes: 1, toxicspikes: 1, stealthrock: 1, stickyweb: 1}; - for (const i in sideConditions) { - let layers = source.side.sideConditions[i] ? (source.side.sideConditions[i].layers || 1) : 1; - if (source.side.removeSideCondition(i)) { - this.add('-sideend', source.side, this.dex.getEffect(i).name, '[from] move: Smoke Bomb', '[of] ' + source); - for (layers; layers > 0; layers--) target.side.addSideCondition(i, source); - } - } - target.side.addSideCondition('stealthrock'); + onEffectiveness(typeMod, target, type) { + return 0; }, - selfSwitch: true, + infiltrates: true, secondary: null, target: "normal", - type: "Fire", + type: "Dark", }, - // Trickster - minisingularity: { - accuracy: 55, - basePower: 0, - basePowerCallback(pokemon, target) { - const targetWeight = target.getWeight(); - if (targetWeight >= 2000) { - this.debug('120 bp'); - return 120; - } - if (targetWeight >= 1000) { - this.debug('100 bp'); - return 100; - } - if (targetWeight >= 500) { - this.debug('80 bp'); - return 80; - } - if (targetWeight >= 250) { - this.debug('60 bp'); - return 60; - } - if (targetWeight >= 100) { - this.debug('40 bp'); - return 40; - } - this.debug('20 bp'); - return 20; - }, + + // Soft Flex + updraft: { + accuracy: 75, + basePower: 75, category: "Special", - desc: "This move's Base Power is 20 if the target weighs less than 10 kg, 40 if its weight is less than 25 kg, 60 if its weight is less than 50 kg, 80 if its weight is less than 100 kg, 100 if its weight is less than 200 kg, and 120 if its weight is greater than or equal to 200 kg. Before doing damage, the target's item is replaced with an Iron Ball, and the target's weight is doubled.", - shortDesc: "BP:weight; +foe weight; foe item = Iron Ball.", - name: "Mini Singularity", + desc: "Changes target's secondary typing to Flying for 2-5 turns unless the target is Ground-type or affected by Ingrain. This move cannot miss in rain.", + shortDesc: "Target: +Flying type. Rain: never misses.", + name: "Updraft", isNonstandard: "Custom", - pp: 5, + gen: 8, + pp: 10, + priority: 0, + flags: {protect: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Twister', target); + }, + onModifyMove(move, pokemon, target) { + if (target && ['raindance', 'primordialsea'].includes(target.effectiveWeather())) { + move.accuracy = true; + } + }, + condition: { + noCopy: true, + duration: 5, + durationCallback(target, source) { + return this.random(5, 7); + }, + onStart(target) { + this.effectData.origTypes = target.getTypes(); // store original types + if (target.getTypes().length === 1) { // single type mons + if (!target.addType('Flying')) return false; + this.add('-start', target, 'typeadd', 'Flying', '[from] move: Updraft'); + } else { // dual typed mons + const primary = target.getTypes()[0]; // take the first type + if (!target.setType([primary, 'Flying'])) return false; + this.add('-start', target, 'typechange', primary + '/Flying', '[from] move: Updraft'); + } + }, + onEnd(target) { + if (!target.setType(this.effectData.origTypes)) return false; // reset the types + this.add('-start', target, 'typechange', this.effectData.origTypes.join('/'), '[silent]'); + }, + }, + secondary: { + chance: 100, + onHit(target) { + if (target.hasType(['Flying', 'Ground']) || target.volatiles['ingrain'] || target.volatiles['brilliant']) return false; + target.addVolatile('updraft'); + }, + }, + target: "normal", + type: "Flying", + }, + + // used for Soft Flex's ability + tempestterrain: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "Heals Electric types for 1/16 of their maximum HP, rounded down, at the end of each turn. Causes Flying- and Steel-types and Levitate users to lose 1/16 of their maximum HP, rounded down, at the end of each turn; if the Pokemon is also Electric-type, they only get the healing effect.", + shortDesc: "Heals Electrics. Hurts Flyings and Steels.", + name: "Tempest Terrain", + pp: 10, + priority: 0, + flags: {nonsky: 1}, + terrain: 'tempestterrain', + condition: { + duration: 5, + durationCallback(source, effect) { + if (source?.hasItem('terrainextender')) { + return 8; + } + return 5; + }, + onResidualOrder: 5, + onResidualSubOrder: 3, + onResidual() { + this.eachEvent('Terrain'); + }, + onTerrain(pokemon) { + if (pokemon.hasType('Electric')) { + if (this.heal(pokemon.baseMaxhp / 8, pokemon)) { + this.add('-message', `${pokemon.name} was healed by the terrain!`); + } + } else if (!pokemon.hasType('Electric') && (pokemon.hasType(['Flying', 'Steel']) || pokemon.hasAbility('levitate'))) { + if (this.damage(pokemon.baseMaxhp / 8, pokemon)) { + this.add('-message', `${pokemon.name} was hurt by the terrain!`); + } + } + }, + onStart(battle, source, effect) { + if (effect?.effectType === 'Ability') { + this.add('-fieldstart', 'move: Tempest Terrain', '[from] ability: ' + effect, '[of] ' + source); + } else { + this.add('-fieldstart', 'move: Tempest Terrain'); + } + this.add('-message', 'The battlefield became stormy!'); + }, + onEnd() { + this.add('-fieldend', 'move: Tempest Terrain'); + }, + }, + secondary: null, + target: "all", + type: "Electric", + zMove: {boost: {spe: 1}}, + contestType: "Clever", + }, + + // Spandan + imtoxicyoureslippinunder: { + accuracy: true, + basePower: 110, + category: "Physical", + desc: "This move uses the target's Special Defense to calculate damage (like Foul Play). This move is neutrally effective against Steel-types.", + shortDesc: "Uses foe's SpD as user's Atk. Hits Steel.", + name: "I'm Toxic You're Slippin' Under", + isNonstandard: "Custom", + gen: 8, + pp: 10, priority: 0, flags: {protect: 1, mirror: 1}, onTryMove() { this.attrLastMove('[still]'); }, onPrepareHit(target, source) { - this.add('-anim', source, "Spacial Rend", target); - this.add('-anim', source, "Flash", target); + this.add('-anim', source, 'Sludge Bomb', target); + this.add('-anim', source, 'Sludge Wave', target); + }, + ignoreImmunity: {'Poison': true}, + secondary: null, + target: "normal", + type: "Poison", + }, - // Really feel like this could be done better (blocked by protect and alike moves.) - if (!( - target.volatiles['banefulbunker'] || target.volatiles['kingsshield'] || - target.side.sideConditions['matblock'] || target.volatiles['protect'] || target.volatiles['spikyshield'] || - target.volatiles['lilypadshield'] || target.volatiles['backoffgrrr'] - )) { - target.addVolatile('weightdoubler', source); + // Struchni + veto: { + accuracy: 100, + basePower: 80, + category: "Physical", + desc: "If the user's stats was raised on the previous turn, double power and gain +1 priority.", + shortDesc: "If stat raised last turn: x2 power, +1 prio.", + name: "Veto", + isNonstandard: "Custom", + gen: 8, + pp: 5, + priority: 0, + flags: {contact: 1, protect: 1}, + onTryMove(source) { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Head Smash', target); + }, + // Veto interactions located in formats.ts + onModifyPriority(priority, source, target, move) { + if (source.m.statsRaisedLastTurn) { + return priority + 1; + } + }, + basePowerCallback(pokemon, target, move) { + if (pokemon.m.statsRaisedLastTurn) { + return move.basePower * 2; + } + return move.basePower; + }, + onHit(target, source) { + if (source.m.statsRaisedLastTurn) { + this.add(`c|${getName('Struchni')}|**veto**`); + } + }, + target: "normal", + type: "Steel", + }, + + // Teclis + kaboom: { + accuracy: 100, + basePower: 150, + category: "Special", + desc: "This move's Base Power is equal to 70+(80*user's current HP/user's max HP). Sets Sunny Day.", + shortDesc: "Better Eruption. Sets Sun.", + name: "Kaboom", + pp: 5, + priority: 0, + flags: {protect: 1, mirror: 1}, + weather: 'sunnyday', + basePowerCallback(pokemon, target, move) { + return 70 + 80 * Math.floor(pokemon.hp / pokemon.maxhp); + }, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Eruption', target); + this.add('-anim', source, 'Earthquake', target); + }, + secondary: null, + target: "normal", + type: "Fire", + }, + + // temp + dropadraco: { + accuracy: 90, + basePower: 130, + category: "Special", + desc: "Lowers the user's Special Attack by 2 stages, then raises it by 1 stage.", + shortDesc: "Lowers user's Sp. Atk by 2, then raises by 1.", + name: "DROP A DRACO", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Draco Meteor', target); + }, + self: { + boosts: { + spa: -2, + }, + }, + onAfterMoveSecondarySelf(source, target) { + this.boost({spa: 1}, source, source, this.dex.getActiveMove('dropadraco')); + }, + secondary: null, + target: "normal", + type: "Dragon", + }, + + // The Immortal + wattup: { + accuracy: 100, + basePower: 73, + category: "Special", + desc: "Has a 100% chance to raise the user's Speed by 1 stage.", + shortDesc: "+1 Speed if successful.", + name: "Watt Up", + isNonstandard: "Custom", + gen: 8, + pp: 15, + priority: 0, + flags: {protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Volt Switch', target); + this.add('-anim', source, 'Nasty Plot', source); + }, + secondary: { + chance: 100, + self: { + boosts: { + spe: 1, + }, + }, + }, + target: "normal", + type: "Electric", + }, + + // tiki + rightoncue: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "25% chance of setting up a layer of spikes. 25% chance of using Heal Bell. 25% chance of using Leech Seed. 25% chance of using Tailwind. 25% chance of using Octolock.", + shortDesc: "5 independent chances of rolling different effects.", + name: "Right. On. Cue!", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {protect: 1, reflectable: 1}, + onHit(target, source) { + let effects = 0; + if (this.randomChance(4, 10)) { + this.useMove('Spikes', source, target); + effects++; + } + if (this.randomChance(4, 10)) { + this.useMove('Heal Bell', source); + effects++; + } + if (this.randomChance(4, 10)) { + this.useMove('Leech Seed', source, target); + effects++; + } + if (this.randomChance(4, 10)) { + this.useMove('Tailwind', source, target); + effects++; + } + if (this.randomChance(4, 10)) { + this.useMove('Octolock', source); + effects++; + } + if (effects <= 0) { + this.add(`c|${getName('tiki')}|truly a dumpster fire`); + } else if (effects >= 3) { + this.add(`c|${getName('tiki')}|whos ${source.side.foe.name}?`); + } + }, + secondary: null, + target: "normal", + type: "Normal", + }, + + // trace + herocreation: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "The user switches out and raises the incoming Pokemon's Attack and Special Attack by 1 stage.", + shortDesc: "User switches, +1 Atk/SpA to replacement.", + name: "Hero Creation", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: -6, + flags: {snatch: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Teleport', source); + this.add('-anim', source, 'Work Up', source); + }, + selfSwitch: true, + sideCondition: 'herocreation', + condition: { + duration: 1, + onStart(side, source) { + this.debug('Hero Creation started on ' + side.name); + this.effectData.positions = []; + for (const i of side.active.keys()) { + this.effectData.positions[i] = false; + } + this.effectData.positions[source.position] = true; + }, + onRestart(side, source) { + this.effectData.positions[source.position] = true; + }, + onSwitchInPriority: 1, + onSwitchIn(target) { + this.add('-activate', target, 'move: Hero Creation'); + this.boost({atk: 1, spa: 1}, target, null, this.dex.getActiveMove('herocreation')); + }, + }, + secondary: null, + target: "self", + type: "Psychic", + }, + + // Trickster + soulshatteringstare: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "Curses the target and blocks it from healing.", + shortDesc: "Curses the target and blocks it from healing.", + name: "Soul-Shattering Stare", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: -7, + flags: {protect: 1, reflectable: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Glare', target); + this.add('-anim', source, 'Trick-or-Treat', source); + }, + onHit(pokemon, source) { + pokemon.addVolatile('healblock'); + this.directDamage(source.maxhp / 2, source, source); + pokemon.addVolatile('curse'); + }, + secondary: null, + target: "randomNormal", + type: "Ghost", + contestType: "Tough", + }, + + // Vexen + asteriusstrike: { + accuracy: 85, + basePower: 100, + category: "Physical", + desc: "Has a 25% chance to confuse the target.", + shortDesc: "25% chance to confuse the target.", + name: "Asterius Strike", + isNonstandard: "Custom", + gen: 8, + pp: 5, + priority: 0, + flags: {protect: 1, contact: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Giga Impact', target); + }, + secondary: { + chance: 25, + volatileStatus: 'confusion', + }, + target: "normal", + type: "Normal", + }, + + // vivalospride + dripbayless: { + accuracy: true, + basePower: 85, + category: "Special", + desc: "This move's type effectiveness against Water is changed to be super effective no matter what this move's type is.", + shortDesc: "Super effective on Water.", + name: "DRIP BAYLESS", + isNonstandard: "Custom", + gen: 8, + pp: 20, + priority: 0, + flags: {protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Lava Plume', target); + this.add('-anim', source, 'Sunny Day', target); + }, + onEffectiveness(typeMod, target, type) { + if (type === 'Water') return 1; + }, + secondary: null, + target: "normal", + type: "Fire", + }, + + // Volco + glitchexploiting: { + accuracy: 100, + basePower: 60, + category: "Special", + desc: "1/4096 chance to KO the target and then the user, and a 1/1024 chance to force out the target and then the user; 20% chance to burn the target, and a 5% chance to freeze or paralyze a random Pokemon on the field; 30% chance to confuse the target.", + shortDesc: "Has a chance to do many things.", + name: "Glitch Exploiting", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Explosion', target); + this.add('-anim', source, 'Tackle', source); + this.add('-anim', source, 'Blue Flare', target); + }, + onHit(target, source, move) { + const random = this.random(4096); + if (random === 1) { + target.faint(source, move); + source.faint(source, move); + } else if ([1024, 2048, 3072, 4096].includes(random)) { + this.add(`c|${getName('Volco')}|haha memory corruption go brrr...`); + target.forceSwitchFlag = true; + source.forceSwitchFlag = true; + } else if (random === 69) { + this.add(`raw|
Pokemon Showdown has not crashed!
It just got sick of all the rng in Volco's Glitch Exploiting move and gave up.
(Do not report this, this is intended.)
`); + this.tie(); + } + }, + secondaries: [ + { + chance: 5, + onHit(target, source) { + const status = this.sample(['frz', 'par']); + this.add(`c|${getName('Volco')}|Ever just screw up the trick and corrupt the memory and cause the wrong thing to happen possibly ruining a run? No? Just me? okay...`); + if (this.randomChance(1, 2)) { + target.trySetStatus(status); + } else { + source.trySetStatus(status); + } + }, + }, + { + chance: 20, + status: 'brn', + }, + { + chance: 30, + volatileStatus: 'confusion', + }, + ], + target: "normal", + type: "Fire", + }, + + // vooper + pandaexpress: { + accuracy: 100, + basePower: 0, + category: "Status", + desc: "Lowers the target's Attack and Special Attack by 2 stages. If this move is successful, the user switches out even if it is trapped and is replaced immediately by a selected party member. The user does not switch out if the target's Attack and Special Attack stat stages were both unchanged, or if there are no unfainted party members.", + shortDesc: "Double strength Parting Shot.", + name: "Panda Express", + isNonstandard: "Custom", + gen: 8, + pp: 20, + priority: 0, + flags: {protect: 1, reflectable: 1, mirror: 1, sound: 1, authentic: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Parting Shot', target); + }, + onHit(target, source, move) { + const success = this.boost({atk: -2, spa: -2}, target, source); + if (!success && !target.hasAbility('mirrorarmor')) { + delete move.selfSwitch; + } + }, + selfSwitch: true, + secondary: null, + target: "normal", + type: "Dark", + }, + + // xJoelituh + burnbone: { + accuracy: 90, + basePower: 0, + category: "Status", + desc: "Burns the target, and the user heals 33% of their maximum HP, rounded down, if the move successfully burns.", + shortDesc: "Applies burn. Heals 33% HP if this move burns.", + name: "Burn Bone", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 1, + flags: {protect: 1, reflectable: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Will-O-Wisp', target); + this.add('-anim', source, 'Shadow Bone', target); + }, + onHit(target, source, move) { + if (target.trySetStatus('brn', source, move)) { + this.heal(source.baseMaxhp * 0.33, source); + return; + } + return false; + }, + secondary: null, + target: "normal", + type: "Fire", + }, + + // yuki + classchange: { + accuracy: true, + basePower: 0, + category: "Status", + desc: "If the user is a cosplay Pikachu forme, it randomly changes forme and has an effect depending on the forme chosen: Cleric uses Strength Sap, Ninja uses Confuse Ray, Dancer uses Feather Dance, Songstress uses Sing, and Jester uses Charm.", + shortDesc: "Pikachu: Random forme and effect.", + name: "Class Change", + isNonstandard: "Custom", + gen: 8, + pp: 6, + noPPBoosts: true, + priority: 0, + flags: {}, + onTryMove(source) { + this.attrLastMove('[still]'); + }, + onPrepareHit(foe, source, move) { + const formes = ['Cleric', 'Ninja', 'Dancer', 'Songstress', 'Jester']; + source.m.yukiCosplayForme = this.sample(formes); + switch (source.m.yukiCosplayForme) { + case 'Cleric': + this.useMove("Strength Sap", source); + break; + case 'Ninja': + this.useMove("Confuse Ray", source); + break; + case 'Dancer': + this.useMove("Feather Dance", source); + break; + case 'Songstress': + this.useMove("Sing", source); + break; + case 'Jester': + this.useMove("Charm", source); + break; + } + }, + onHit(target, source) { + if (source.baseSpecies.baseSpecies !== 'Pikachu') return; + switch (source.m.yukiCosplayForme) { + case 'Cleric': + changeSet(this, source, ssbSets['yuki-Cleric']); + this.add('-message', 'yuki patches up her wounds!'); + return; + case 'Ninja': + changeSet(this, source, ssbSets['yuki-Ninja']); + this.add('-message', `yuki's fast movements confuse ${target.name}!`); + return; + case 'Dancer': + changeSet(this, source, ssbSets['yuki-Dancer']); + this.add('-message', `yuki dazzles ${target.name} with her moves!`); + return; + case 'Songstress': + changeSet(this, source, ssbSets['yuki-Songstress']); + this.add('-message', `yuki sang an entrancing melody!`); + return; + case 'Jester': + changeSet(this, source, ssbSets['yuki-Jester']); + this.add('-message', `yuki tries her best to impress ${target.name}!`); + return; + } + }, + secondary: null, + target: "self", + type: "Normal", + }, + + // Zalm + ingredientforaging: { + accuracy: 100, + basePower: 70, + category: "Special", + desc: "Heals 50% of the user's max HP, rounded down, if the target is holding an item. Removes the target's item and enables Belch on the user.", + shortDesc: "If foe has item: Heal 50% and remove it.", + name: "Ingredient Foraging", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {protect: 1, mirror: 1}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onAfterHit(target, source) { + if (source.hp) { const item = target.takeItem(); - if (!target.item) { - if (item) this.add('-enditem', target, item.name, '[from] move: Mini Singularity', '[of] ' + source); - target.setItem('ironball'); - this.add('-message', target.name + ' obtained an Iron Ball.'); + if (item) { + this.add('-enditem', target, item.name, '[from] stealeat', '[move] Ingredient Foraging', '[of] ' + source); + this.heal(source.maxhp / 2, source); + this.add(`c|${getName('Zalm')}|Yum`); + source.ateBerry = true; } } }, secondary: null, target: "normal", + type: "Bug", + }, + + // Zarel + relicdance: { + accuracy: 100, + basePower: 80, + category: "Special", + desc: "+1 Special Attack and, if the user is a Meloetta forme, transforms into the other Meloetta forme with its accompanying moveset, regardless of the outcome of the move. The move becomes fighting if Meloetta-P uses the move. If the user is Meloetta-Pirouette, this move is Fighting-type.", + shortDesc: "+1 SpA. Meloetta transforms. Fighting type if Melo-P.", + name: "Relic Dance", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {protect: 1, mirror: 1, dance: 1}, + secondary: null, + onTryMove(pokemon, target, move) { + this.attrLastMove('[still]'); + }, + onPrepareHit(target, source) { + this.add('-anim', source, 'Relic Song', target); + }, + onAfterMove(source) { + this.boost({spa: 1}, source); + if (source.species.baseSpecies !== 'Meloetta') return; + if (source.species.name === "Meloetta-Pirouette") { + changeSet(this, source, ssbSets['Zarel']); + } else { + changeSet(this, source, ssbSets['Zarel-Pirouette']); + } + }, + onModifyMove(move, pokemon) { + if (pokemon.species.name === "Meloetta-Pirouette") move.type = "Fighting"; + }, + target: "allAdjacentFoes", type: "Psychic", }, - // vivalospride - ceilingsabsent: { + + // Zodiax + bigstormcoming: { + accuracy: 100, + basePower: 0, + category: "Special", + desc: "Uses Hurricane, Thunder, Blizzard, and then Weather Ball, each at 30% of their normal Base Power.", + shortDesc: "30% power: Hurricane, Thunder, Blizzard, W. Ball.", + name: "Big Storm Coming", + isNonstandard: "Custom", + gen: 8, + pp: 10, + priority: 0, + flags: {}, + onTryMove() { + this.attrLastMove('[still]'); + }, + onPrepareHit() { + this.add(`c|${getName('Zodiax')}|There is a hail no storm okayyyyyy`); + }, + onTry(pokemon, target) { + pokemon.addVolatile('bigstormcomingmod'); + this.useMove("Hurricane", pokemon); + this.useMove("Thunder", pokemon); + this.useMove("Blizzard", pokemon); + this.useMove("Weather Ball", pokemon); + }, + secondary: null, + target: "normal", + type: "Flying", + }, + // Zyg + luckofthedraw: { accuracy: true, basePower: 0, category: "Status", - desc: "Before the turn starts, this Pokemon uses Taunt against the foe. When this move hits, the user is healed by 50% of its max HP.", - shortDesc: "Foe is Taunted; user heals 50% of Max HP.", - name: "CEILINGS ABSENT", + desc: "Raises the user's Attack, Defense, and Speed by 1 stage.", + shortDesc: "Raises the user's Attack, Defense, Speed by 1.", + name: "Luck of the Draw", isNonstandard: "Custom", + gen: 8, pp: 10, priority: 0, flags: {snatch: 1}, @@ -4236,354 +5285,438 @@ export const Moves: {[k: string]: ModdedMoveData} = { this.attrLastMove('[still]'); }, onPrepareHit(target, source) { - this.add('-anim', source, "Recover", source); + this.add('-anim', source, 'Quiver Dance', source); }, - beforeTurnCallback(pokemon) { - if (pokemon.status === 'slp' || pokemon.status === 'frz') return; - this.useMove("taunt", pokemon); - }, - onHit(source) { - this.heal(source.baseMaxhp / 2, source); + boosts: { + atk: 1, + def: 1, + spe: 1, }, secondary: null, target: "self", - type: "Normal", - }, - // Volco - explosivedrain: { - basePower: 90, - accuracy: 100, - category: "Special", - desc: "The user recovers half the HP lost by the target, rounded half up. If Big Root is held, the user recovers 1.3x the normal amount of HP, rounded half down.", - shortDesc: "User recovers 50% of the damage dealt.", - name: "Explosive Drain", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {protect: 1, mirror: 1, heal: 1}, - drain: [1, 2], - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Fire Blast", target); - this.add('-anim', source, "Giga Drain", target); - }, - secondary: null, - target: "normal", - type: "Fire", - }, - // Xayah - feathersnare: { - accuracy: 100, - basePower: 95, - category: "Special", - desc: "Has a 20% chance to make the target flinch and a 50% chance to paralyze the target.", - shortDesc: "20% to flinch; 50% to paralyze.", - name: "Feather Snare", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {protect: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Revelation Dance", source); - this.add('-anim', source, "Air Slash", target); - this.add('-anim', source, "Air Slash", target); - }, - secondaries: [ - { - chance: 20, - volatileStatus: 'flinch', - }, - { - chance: 50, - status: 'par', - }, - ], - zMove: {basePower: 175}, - target: "normal", - type: "Flying", - }, - // xfix - glitzerpopping: { - accuracy: true, - basePower: 0, - category: "Status", - desc: "Uses 2-5 random moves. Does not include Z-Moves that have 1 Base Power or Glitzer Popping.", - shortDesc: "Uses 2-5 random moves; avoids 1 BP Z-Moves.", - name: "glitzer popping", - isNonstandard: "Custom", - pp: 3.14, - noPPBoosts: true, - priority: 0, - flags: {}, - onTryMove(pokemon) { - this.attrLastMove('[still]'); - const moveData = pokemon.getMoveData('glitzerpopping'); - if (!moveData) return; - // Lost 1 PP due to move usage, restore 0.9 PP to make it so that only 0.1 PP - // would be used. - moveData.pp = (Math.round(moveData.pp * 100) + 90) / 100; - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Metronome", source); - }, - onHit(target, source, effect) { - const moves = []; - for (const id in this.dex.data.Moves) { - const move = this.dex.getMove(id); - if (move.realMove || move.id === 'glitzerpopping') continue; - // Calling 1 BP move is somewhat lame and disappointing. However, - // signature Z moves are fine, as they actually have a base power. - if (move.isZ && move.basePower === 1) continue; - if (move.gen > this.gen) continue; - moves.push(move.name); - } - if (!moves.length) return false; - const randomMove = this.sample(moves); - this.useMove(randomMove, target); - }, - multihit: [2, 5], - secondary: null, - target: "self", - type: "???", - }, - // xJoelituh - lavabone: { - accuracy: 100, - basePower: 130, - category: "Physical", - desc: "Has a 35% chance to burn the target.", - shortDesc: "35% chance to burn the target.", - name: "Lava Bone", - isNonstandard: "Custom", - pp: 5, - priority: 0, - flags: {protect: 1, mirror: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Shadow Bone", target); - this.add('-anim', target, "Fire Blast", target); - }, - secondary: { - chance: 35, - status: 'brn', - }, - target: "normal", - type: "Fire", - }, - // XpRienzo ☑◡☑ - blehflame: { - accuracy: 100, - basePower: 130, - category: "Special", - desc: "No additional effect.", - shortDesc: "Usually goes first.", - name: "Bleh Flame", - isNonstandard: "Custom", - pp: 1, - priority: 1, - flags: {}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Focus Energy", source); - this.add('-anim', source, "Fusion Flare", target); - }, - secondary: null, - isZ: "charcoal", - target: "normal", - type: "Fire", - }, - // Yuki - cutieescape: { - accuracy: true, - category: "Status", - basePower: 0, - desc: "The user is replaced with another Pokemon in its party. The foe is confused, trapped, and infatuated regardless of the replacement's gender. This move fails unless the user already took damage this turn.", - shortDesc: "If hit; switches out + confuses, traps, infatuates.", - name: "Cutie Escape", - isNonstandard: "Custom", - pp: 10, - priority: -6, - flags: {mirror: 1}, - beforeTurnCallback(pokemon) { - pokemon.addVolatile('cutieescape'); - this.add('-message', `${pokemon.name} is preparing to flee!`); - }, - beforeMoveCallback(pokemon) { - if (!pokemon.volatiles['cutieescape'] || !pokemon.volatiles['cutieescape'].tookDamage) { - this.add('-fail', pokemon, 'move: Cutie Escape'); - this.add('-hint', 'Cutie Escape only works when Yuki is hit in the same turn the move is used.'); - return true; - } - }, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, "Baton Pass", source); - }, - onHit(target, source) { - target.addVolatile('confusion'); - target.addVolatile('cutietrap'); - }, - condition: { - duration: 1, - onStart(pokemon) { - this.add('-singleturn', pokemon, 'move: Cutie Escape'); - }, - onHit(pokemon, source, move) { - if (move.category !== 'Status') { - pokemon.volatiles['cutieescape'].tookDamage = true; - } - }, - }, - secondary: null, - selfSwitch: true, - target: "normal", - type: "Fairy", - }, - // Zalm - twinweedle: { - accuracy: 100, - basePower: 40, - multihit: 2, - category: "Physical", - desc: "Hits twice. Each hit has a 20% chance to poison the target and heals the user for 30% damage dealt. If one of the hits breaks the target's Substitute, it will take damage for the remaining hits.", - shortDesc: "2 hits, 20% poison each, heals 30% of damage.", - isNonstandard: "Custom", - name: "TwinWeedle", - pp: 25, - noPPBoosts: true, - priority: 0, - flags: {protect: 1, mirror: 1, heal: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onPrepareHit(target, source) { - this.add('-anim', source, 'Poison Sting', target); - }, - drain: [3, 10], - secondary: { - chance: 20, - status: 'psn', - }, - target: "normal", - type: "Poison", - }, - // Zarel - relicsongdance: { - accuracy: 100, - basePower: 60, - multihit: 2, - category: "Special", - desc: "Hits twice and ignores type immunities. Before the second hit, the user switches to its Pirouette forme, and this move's second hit deals physical Fighting-type damage. After the second hit, the user reverts to its Aria forme. Fails unless the user is Meloetta.", - shortDesc: "Attacks in Aria forme, then in Pirouette forme.", - name: "Relic Song Dance", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {protect: 1, mirror: 1, sound: 1, authentic: 1}, - ignoreImmunity: true, - onTryMove() { - this.attrLastMove('[still]'); - }, - onTryHit(target, pokemon) { - if (pokemon.name !== 'Zarel') { - this.add('-fail', pokemon); - this.hint("Only Zarel can use Relic Song Dance."); - return null; - } - this.attrLastMove('[still]'); - const move = pokemon.species.id === 'meloettapirouette' ? 'Brick Break' : 'Relic Song'; - this.add('-anim', pokemon, move, target); - }, - onHit(target, pokemon, move) { - if (pokemon.species.id === 'meloettapirouette') { - pokemon.formeChange('Meloetta'); - } else if (pokemon.formeChange('Meloetta-Pirouette')) { - move.category = 'Physical'; - move.type = 'Fighting'; - } - }, - onAfterMove(pokemon) { - // Ensure Meloetta goes back to standard form after using the move - if (pokemon.species.id === 'meloettapirouette') { - pokemon.formeChange('Meloetta'); - } - this.hint("Zarel still has the Serene Grace ability."); - }, - condition: { - duration: 1, - onAfterMoveSecondarySelf(pokemon, target, move) { - if (pokemon.species.id === 'meloettapirouette') { - pokemon.formeChange('Meloetta'); - } else { - pokemon.formeChange('Meloetta-Pirouette'); - } - pokemon.removeVolatile('relicsong'); - }, - }, - secondary: null, - target: "allAdjacentFoes", type: "Psychic", }, - // Zyg - thelifeofzyg: { - accuracy: 100, - basePower: 0, - category: "Status", - desc: "Both the user and target are badly poisoned.", - shortDesc: "Badly poisons the user and target.", - name: "The Life of Zyg", - isNonstandard: "Custom", - pp: 10, - priority: 0, - flags: {protect: 1}, - onTryMove() { - this.attrLastMove('[still]'); - }, - onTryHit(target, source) { - this.add('-anim', source, "Toxic", source); - this.add('-anim', source, "Toxic", target); - }, - onHit(target, source) { - source.trySetStatus('tox'); - target.trySetStatus('tox'); - }, - secondary: null, - target: "normal", - type: "Poison", - }, - // Modded Sleep Talk for pirate princess - sleeptalk: { + // These moves need modified to support Alpha's move + auroraveil: { inherit: true, + desc: "For 5 turns, the user and its party members take 0.5x damage from physical and special attacks, or 0.66x damage if in a Double Battle; does not reduce damage further with Reflect or Light Screen. Critical hits ignore this protection. It is removed from the user's side if the user or an ally is successfully hit by Brick Break, Psychic Fangs, or Defog. Brick Break and Psychic Fangs remove the effect before damage is calculated. Lasts for 8 turns if the user is holding Light Clay. Fails unless the weather is Heavy Hailstorm or Hail.", + shortDesc: "For 5 turns, damage to allies is halved. Hail-like weather only.", + onTryHitSide() { + if (!this.field.isWeather(['heavyhailstorm', 'hail'])) return false; + }, + }, + blizzard: { + inherit: true, + desc: "Has a 10% chance to freeze the target. If the weather is Heavy Hailstorm or Hail, this move does not check accuracy.", + shortDesc: "10% freeze foe(s). Can't miss in Hail-like weather.", + onModifyMove(move) { + if (this.field.isWeather(['heavyhailstorm', 'hail'])) move.accuracy = true; + }, + }, + dig: { + inherit: true, + condition: { + duration: 2, + onImmunity(type, pokemon) { + if (['sandstorm', 'heavyhailstorm', 'hail'].includes(type)) return false; + }, + onInvulnerability(target, source, move) { + if (['earthquake', 'magnitude'].includes(move.id)) { + return; + } + return false; + }, + onSourceModifyDamage(damage, source, target, move) { + if (move.id === 'earthquake' || move.id === 'magnitude') { + return this.chainModify(2); + } + }, + }, + }, + dive: { + inherit: true, + condition: { + duration: 2, + onImmunity(type, pokemon) { + if (['sandstorm', 'heavyhailstorm', 'hail'].includes(type)) return false; + }, + onInvulnerability(target, source, move) { + if (['surf', 'whirlpool'].includes(move.id)) { + return; + } + return false; + }, + onSourceModifyDamage(damage, source, target, move) { + if (move.id === 'surf' || move.id === 'whirlpool') { + return this.chainModify(2); + } + }, + }, + }, + moonlight: { + inherit: true, + desc: "The user restores 1/2 of its maximum HP if Delta Stream or no weather conditions are in effect or if the user is holding Utility Umbrella, 2/3 of its maximum HP if the weather is Desolate Land or Sunny Day, and 1/4 of its maximum HP if the weather is Heavy Hailstorm, Hail, Primordial Sea, Rain Dance, or Sandstorm, all rounded half down.", onHit(pokemon) { - const moves = []; - for (const moveSlot of pokemon.moveSlots) { - const move = this.dex.getMove(moveSlot.id); - const noSleepTalk = [ - 'assist', 'beakblast', 'belch', 'bide', 'celebrate', 'chatter', 'copycat', 'focuspunch', 'mefirst', 'metronome', 'mimic', 'mirrormove', 'naturepower', 'shelltrap', 'sketch', 'sleeptalk', 'uproar', - 'teabreak', 'glitzerpopping', // Modded banlist - ]; - if (noSleepTalk.includes(move.id) || move.flags['charge'] || (move.isZ && move.basePower !== 1)) { + let factor = 0.5; + switch (pokemon.effectiveWeather()) { + case 'sunnyday': + case 'desolateland': + factor = 0.667; + break; + case 'raindance': + case 'primordialsea': + case 'sandstorm': + case 'heavyhailstorm': + case 'winterhail': + case 'hail': + factor = 0.25; + break; + } + return !!this.heal(this.modify(pokemon.maxhp, factor)); + }, + }, + morningsun: { + inherit: true, + desc: "The user restores 1/2 of its maximum HP if Delta Stream or no weather conditions are in effect or if the user is holding Utility Umbrella, 2/3 of its maximum HP if the weather is Desolate Land or Sunny Day, and 1/4 of its maximum HP if the weather is Heavy Hailstorm, Hail, Primordial Sea, Rain Dance, or Sandstorm, all rounded half down.", + onHit(pokemon) { + let factor = 0.5; + switch (pokemon.effectiveWeather()) { + case 'sunnyday': + case 'desolateland': + factor = 0.667; + break; + case 'raindance': + case 'primordialsea': + case 'sandstorm': + case 'heavyhailstorm': + case 'winterhail': + case 'hail': + factor = 0.25; + break; + } + return !!this.heal(this.modify(pokemon.maxhp, factor)); + }, + }, + solarbeam: { + inherit: true, + desc: "This attack charges on the first turn and executes on the second. Power is halved if the weather is Heavy Hailstorm, Hail, Primordial Sea, Rain Dance, or Sandstorm and the user is not holding Utility Umbrella. If the user is holding a Power Herb or the weather is Desolate Land or Sunny Day, the move completes in one turn. If the user is holding Utility Umbrella and the weather is Desolate Land or Sunny Day, the move still requires a turn to charge.", + onBasePower(basePower, pokemon, target) { + const weathers = ['raindance', 'primordialsea', 'sandstorm', 'heavyhailstorm', 'hail']; + if (weathers.includes(pokemon.effectiveWeather())) { + this.debug('weakened by weather'); + return this.chainModify(0.5); + } + }, + }, + solarblade: { + inherit: true, + desc: "This attack charges on the first turn and executes on the second. Power is halved if the weather is Heavy Hailstorm, Hail, Primordial Sea, Rain Dance, or Sandstorm and the user is not holding Utility Umbrella. If the user is holding a Power Herb or the weather is Desolate Land or Sunny Day, the move completes in one turn. If the user is holding Utility Umbrella and the weather is Desolate Land or Sunny Day, the move still requires a turn to charge.", + onBasePower(basePower, pokemon, target) { + const weathers = ['raindance', 'primordialsea', 'sandstorm', 'heavyhailstorm', 'hail']; + if (weathers.includes(pokemon.effectiveWeather())) { + this.debug('weakened by weather'); + return this.chainModify(0.5); + } + }, + }, + synthesis: { + inherit: true, + desc: "The user restores 1/2 of its maximum HP if Delta Stream or no weather conditions are in effect or if the user is holding Utility Umbrella, 2/3 of its maximum HP if the weather is Desolate Land or Sunny Day, and 1/4 of its maximum HP if the weather is Heavy Hailstorm, Hail, Primordial Sea, Rain Dance, or Sandstorm, all rounded half down.", + onHit(pokemon) { + let factor = 0.5; + switch (pokemon.effectiveWeather()) { + case 'sunnyday': + case 'desolateland': + factor = 0.667; + break; + case 'raindance': + case 'primordialsea': + case 'sandstorm': + case 'heavyhailstorm': + case 'winterhail': + case 'hail': + factor = 0.25; + break; + } + return !!this.heal(this.modify(pokemon.maxhp, factor)); + }, + }, + weatherball: { + inherit: true, + desc: "Power doubles if a weather condition other than Delta Stream is active, and this move's type changes to match. Ice type during Heavy Hailstorm or Hail, Water type during Primordial Sea or Rain Dance, Rock type during Sandstorm, and Fire type during Desolate Land or Sunny Day. If the user is holding Utility Umbrella and uses Weather Ball during Primordial Sea, Rain Dance, Desolate Land, or Sunny Day, the move is still Normal-type and does not have a base power boost.", + onModifyType(move, pokemon) { + switch (pokemon.effectiveWeather()) { + case 'sunnyday': + case 'desolateland': + move.type = 'Fire'; + break; + case 'raindance': + case 'primordialsea': + move.type = 'Water'; + break; + case 'sandstorm': + move.type = 'Rock'; + break; + case 'heavyhailstorm': + case 'winterhail': + case 'hail': + move.type = 'Ice'; + break; + } + }, + onModifyMove(move, pokemon) { + switch (pokemon.effectiveWeather()) { + case 'sunnyday': + case 'desolateland': + move.basePower *= 2; + break; + case 'raindance': + case 'primordialsea': + move.basePower *= 2; + break; + case 'sandstorm': + move.basePower *= 2; + break; + case 'heavyhailstorm': + case 'winterhail': + case 'hail': + move.basePower *= 2; + break; + } + }, + }, + // Modified move descriptions for support of Segmr's move + doomdesire: { + inherit: true, + desc: "Deals damage two turns after this move is used. At the end of that turn, the damage is calculated at that time and dealt to the Pokemon at the position the target had when the move was used. If the user is no longer active at the time, damage is calculated based on the user's natural Special Attack stat, types, and level, with no boosts from its held item or Ability. Fails if this move, Disconnect, or Future Sight is already in effect for the target's position.", + }, + futuresight: { + inherit: true, + desc: "Deals damage two turns after this move is used. At the end of that turn, the damage is calculated at that time and dealt to the Pokemon at the position the target had when the move was used. If the user is no longer active at the time, damage is calculated based on the user's natural Special Attack stat, types, and level, with no boosts from its held item or Ability. Fails if this move, Doom Desire, or Disconnect is already in effect for the target's position.", + }, + // For shifting rocks compatibility + defog: { + inherit: true, + onHit(target, source, move) { + let success = false; + if (!target.volatiles['substitute'] || move.infiltrates) success = !!this.boost({evasion: -1}); + const removeTarget = [ + 'reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'spikes', 'toxicspikes', 'stealthrock', 'shiftingrocks', 'stickyweb', 'ferrofluid', 'gmaxsteelsurge', + ]; + const removeAll = [ + 'spikes', 'toxicspikes', 'stealthrock', 'shiftingrocks', 'stickyweb', 'ferrofluid', 'gmaxsteelsurge', + ]; + for (const targetCondition of removeTarget) { + if (target.side.removeSideCondition(targetCondition)) { + if (!removeAll.includes(targetCondition)) continue; + this.add('-sideend', target.side, this.dex.getEffect(targetCondition).name, '[from] move: Defog', '[of] ' + source); + success = true; + } + } + for (const sideCondition of removeAll) { + if (source.side.removeSideCondition(sideCondition)) { + this.add('-sideend', source.side, this.dex.getEffect(sideCondition).name, '[from] move: Defog', '[of] ' + source); + success = true; + } + } + this.field.clearTerrain(); + return success; + }, + }, + rapidspin: { + inherit: true, + onAfterHit(target, pokemon) { + if (pokemon.hp && pokemon.removeVolatile('leechseed')) { + this.add('-end', pokemon, 'Leech Seed', '[from] move: Rapid Spin', '[of] ' + pokemon); + } + const sideConditions = ['spikes', 'toxicspikes', 'stealthrock', + 'shiftingrocks', 'stickyweb', 'ferrofluid', 'gmaxsteelsurge']; + for (const condition of sideConditions) { + if (pokemon.hp && pokemon.side.removeSideCondition(condition)) { + this.add('-sideend', pokemon.side, this.dex.getEffect(condition).name, '[from] move: Rapid Spin', '[of] ' + pokemon); + } + } + if (pokemon.hp && pokemon.volatiles['partiallytrapped']) { + pokemon.removeVolatile('partiallytrapped'); + } + }, + onAfterSubDamage(damage, target, pokemon) { + if (pokemon.hp && pokemon.removeVolatile('leechseed')) { + this.add('-end', pokemon, 'Leech Seed', '[from] move: Rapid Spin', '[of] ' + pokemon); + } + const sideConditions = ['spikes', 'toxicspikes', 'stealthrock', + 'shiftingrocks', 'stickyweb', 'ferrofluid', 'gmaxsteelsurge']; + for (const condition of sideConditions) { + if (pokemon.hp && pokemon.side.removeSideCondition(condition)) { + this.add('-sideend', pokemon.side, this.dex.getEffect(condition).name, '[from] move: Rapid Spin', '[of] ' + pokemon); + } + } + if (pokemon.hp && pokemon.volatiles['partiallytrapped']) { + pokemon.removeVolatile('partiallytrapped'); + } + }, + }, + courtchange: { + inherit: true, + onHitField(target, source) { + const sourceSide = source.side; + const targetSide = source.side.foe; + const sideConditions = [ + 'mist', 'lightscreen', 'reflect', 'spikes', 'safeguard', 'tailwind', 'toxicspikes', 'stealthrock', 'shiftingrocks', 'waterpledge', 'firepledge', 'grasspledge', 'stickyweb', 'ferrofluid', 'auroraveil', 'gmaxsteelsurge', 'gmaxcannonade', 'gmaxvinelash', 'gmaxwildfire', + ]; + let success = false; + for (const id of sideConditions) { + const effectName = this.dex.getEffect(id).name; + if (sourceSide.sideConditions[id] && targetSide.sideConditions[id]) { + [sourceSide.sideConditions[id], targetSide.sideConditions[id]] = [ + targetSide.sideConditions[id], sourceSide.sideConditions[id], + ]; + this.add('-sideend', sourceSide, effectName, '[silent]'); + this.add('-sideend', targetSide, effectName, '[silent]'); + } else if (sourceSide.sideConditions[id] && !targetSide.sideConditions[id]) { + targetSide.sideConditions[id] = sourceSide.sideConditions[id]; + delete sourceSide.sideConditions[id]; + this.add('-sideend', sourceSide, effectName, '[silent]'); + } else if (targetSide.sideConditions[id] && !sourceSide.sideConditions[id]) { + sourceSide.sideConditions[id] = targetSide.sideConditions[id]; + delete targetSide.sideConditions[id]; + this.add('-sideend', targetSide, effectName, '[silent]'); + } else { continue; } - moves.push(move.id); + let sourceLayers = sourceSide.sideConditions[id] ? (sourceSide.sideConditions[id].layers || 1) : 0; + let targetLayers = targetSide.sideConditions[id] ? (targetSide.sideConditions[id].layers || 1) : 0; + for (; sourceLayers > 0; sourceLayers--) { + this.add('-sidestart', sourceSide, effectName, '[silent]'); + } + for (; targetLayers > 0; targetLayers--) { + this.add('-sidestart', targetSide, effectName, '[silent]'); + } + success = true; } - let randomMove = ''; - if (moves.length) randomMove = this.sample(moves); - if (!randomMove) { - return false; + if (!success) return false; + this.add('-activate', source, 'move: Court Change'); + }, + }, + gmaxwindrage: { + inherit: true, + self: { + onHit(source) { + let success = false; + const removeTarget = [ + 'reflect', 'lightscreen', 'auroraveil', 'safeguard', 'mist', 'spikes', 'toxicspikes', 'stealthrock', 'shiftingrocks', 'stickyweb', 'ferrofluid', 'gmaxsteelsurge', + ]; + const removeAll = [ + 'spikes', 'toxicspikes', 'stealthrock', 'shiftingrocks', 'stickyweb', 'ferrofluid', 'gmaxsteelsurge', + ]; + for (const targetCondition of removeTarget) { + if (source.side.foe.removeSideCondition(targetCondition)) { + if (!removeAll.includes(targetCondition)) continue; + this.add('-sideend', source.side.foe, this.dex.getEffect(targetCondition).name, '[from] move: G-Max Wind Rage', '[of] ' + source); + success = true; + } + } + for (const sideCondition of removeAll) { + if (source.side.removeSideCondition(sideCondition)) { + this.add('-sideend', source.side, this.dex.getEffect(sideCondition).name, '[from] move: G-Max Wind Rage', '[of] ' + source); + success = true; + } + } + this.field.clearTerrain(); + return success; + }, + }, + }, + // Terrain Pulse for consistency + terrainpulse: { + inherit: true, + onModifyType(move, pokemon) { + if (!pokemon.isGrounded()) return; + switch (this.field.terrain) { + case 'electricterrain': + move.type = 'Electric'; + break; + case 'grassyterrain': + move.type = 'Grass'; + break; + case 'mistyterrain': + move.type = 'Fairy'; + break; + case 'psychicterrain': + move.type = 'Psychic'; + break; + case 'baneterrain': + move.type = 'Ice'; + break; + case 'swampyterrain': + move.type = 'Ground'; + break; + case 'inversionterrain': + move.type = '???'; + break; + case 'pitchblack': + move.type = 'Ghost'; + break; + case 'waveterrain': + move.type = 'Water'; + break; + case 'tempestterrain': + move.type = 'Flying'; + break; } - this.useMove(randomMove, pokemon); + }, + }, + // genderless infatuation for nui's Condition Override + attract: { + inherit: true, + volatileStatus: 'attract', + condition: { + noCopy: true, // doesn't get copied by Baton Pass + onStart(pokemon, source, effect) { + if (!source.hasAbility('conditionoverride')) { + if (!(pokemon.gender === 'M' && source.gender === 'F') && !(pokemon.gender === 'F' && source.gender === 'M')) { + this.debug('incompatible gender'); + return false; + } + } + if (!this.runEvent('Attract', pokemon, source)) { + this.debug('Attract event failed'); + return false; + } + + if (effect.id === 'cutecharm') { + this.add('-start', pokemon, 'Attract', '[from] ability: Cute Charm', '[of] ' + source); + } else if (effect.id === 'destinyknot') { + this.add('-start', pokemon, 'Attract', '[from] item: Destiny Knot', '[of] ' + source); + } else { + this.add('-start', pokemon, 'Attract'); + } + }, + onUpdate(pokemon) { + if (this.effectData.source && !this.effectData.source.isActive && pokemon.volatiles['attract']) { + this.debug('Removing Attract volatile on ' + pokemon); + pokemon.removeVolatile('attract'); + } + }, + onModifySpDPriority: 1, + onModifySpD(spd, pokemon) { + for (const target of this.getAllActive()) { + if (target === pokemon) continue; + if (target.hasAbility('conditionoverride')) return this.chainModify(0.75); + } + return; + }, + onBeforeMovePriority: 2, + onBeforeMove(pokemon, target, move) { + this.add('-activate', pokemon, 'move: Attract', '[of] ' + this.effectData.source); + if (this.randomChance(1, 2)) { + this.add('cant', pokemon, 'Attract'); + return false; + } + }, + onEnd(pokemon) { + this.add('-end', pokemon, 'Attract', '[silent]'); + }, }, }, }; diff --git a/data/mods/ssb/pokedex.ts b/data/mods/ssb/pokedex.ts index 9cda7cbf42..6b0e525522 100644 --- a/data/mods/ssb/pokedex.ts +++ b/data/mods/ssb/pokedex.ts @@ -6,186 +6,254 @@ export const Pokedex: {[k: string]: ModdedSpeciesData} = { baseStats: {hp: 100, atk: 100, def: 100, spa: 100, spd: 100, spe: 100}, // the base stats for the pokemon }, */ - // ACakeWearingAHat - dunsparce: { + // Abdelrahman + cameruptmega: { inherit: true, - types: ['Normal', 'Ghost'], + abilities: {0: "Water Absorb"}, }, - // Akiamara - croagunk: { + // Aelita + zygardecomplete: { inherit: true, - baseStats: {hp: 83, atk: 106, def: 65, spa: 86, spd: 65, spe: 85}, + abilities: {0: "Scyphozoa"}, }, - // Arrested - blastoisemega: { + // aegii + aegislash: { inherit: true, - abilities: {0: "Shell Shocker"}, + abilities: {0: "Set the Stage"}, }, - // Birdy - oricorio: { + aegislashblade: { inherit: true, - abilities: {0: "Arabesque"}, + abilities: {0: "Set the Stage"}, }, - oricoriopau: { + // Aeonic + nosepass: { inherit: true, - abilities: {0: "Arabesque"}, + baseStats: {hp: 70, atk: 85, def: 135, spa: 45, spd: 90, spe: 70}, }, - oricoriopompom: { + // Aethernum + lotad: { inherit: true, - abilities: {0: "Arabesque"}, + baseStats: {hp: 40, atk: 70, def: 70, spa: 80, spd: 90, spe: 70}, }, - oricoriosensu: { + // Annika + mewtwomegay: { inherit: true, - abilities: {0: "Arabesque"}, + abilities: {0: "Overprotective"}, }, - // E4 Flint - steelixmega: { + // A Quag To The Past + quagsire: { inherit: true, - abilities: {0: 'Stark Mountain'}, - types: ['Steel', 'Ground', 'Fire'], + baseStats: {hp: 95, atk: 65, def: 85, spa: 65, spd: 85, spe: 35}, }, - // Eien - mew: { + // Billo + cosmog: { inherit: true, - abilities: {0: 'Psychic Surge'}, + baseStats: {hp: 86, atk: 58, def: 62, spa: 87, spd: 62, spe: 74}, + }, + // dogknees + furret: { + inherit: true, + types: ["Normal", "Ghost"], }, // Elgino - mimikyubusted: { + celebi: { inherit: true, - abilities: {0: 'Gib love pls'}, + types: ["Grass", "Fairy"], }, - // eternally - ducklett: { + // EpicNikolai + garchompmega: { inherit: true, - // Doubled def, spa, spd and spe - baseStats: {hp: 62, atk: 44, def: 100, spa: 88, spd: 100, spe: 110}, + abilities: {0: "Dragon Heart"}, + types: ["Dragon", "Fire"], }, - // fart - kartana: { + // Felucia + uxie: { inherit: true, - types: ['Fairy', 'Steel'], + types: ["Psychic", "Normal"], + }, + // frostyicelad + laprasgmax: { + inherit: true, + heightm: 2.5, + weightkg: 220, }, // GMars minior: { inherit: true, - abilities: {0: 'mysteryshell'}, + abilities: {0: "Capsule Armor"}, + }, + miniorviolet: { + inherit: true, + abilities: {0: "Capsule Armor"}, + }, + miniorindigo: { + inherit: true, + abilities: {0: "Capsule Armor"}, + }, + miniorblue: { + inherit: true, + abilities: {0: "Capsule Armor"}, + }, + miniorgreen: { + inherit: true, + abilities: {0: "Capsule Armor"}, + }, + minioryellow: { + inherit: true, + abilities: {0: "Capsule Armor"}, + }, + miniororange: { + inherit: true, + abilities: {0: "Capsule Armor"}, }, miniormeteor: { inherit: true, - abilities: {0: 'mysteryshell'}, + abilities: {0: "Capsule Armor"}, }, - // grimAuxiliatrix - aggronmega: { + // Hydro + pichu: { inherit: true, - abilities: {0: 'Intimidate'}, + types: ["Electric", "Water"], + baseStats: {hp: 70, atk: 80, def: 55, spa: 75, spd: 75, spe: 100}, }, - // guishark - sharpedomega: { + // Inactive + gyaradosmega: { inherit: true, - abilities: {0: 'gz guishark'}, + abilities: {0: "Dragon's Fury"}, + }, + // Jho + toxtricity: { + inherit: true, + abilities: {0: "Punk Rock"}, + }, + toxtricitylowkey: { + inherit: true, + abilities: {0: "Venomize"}, }, // Kaiju Bunny - gliscor: { + lopunnymega: { inherit: true, - abilities: {0: 'Poison Heal'}, // Ability change is permanent + abilities: {0: "Second Wind"}, + types: ["Normal", "Fairy"], }, // Kris - rotomfan: { + unown: { inherit: true, - abilities: {0: "Adaptability"}, - baseStats: {hp: 50, atk: 65, def: 127, spa: 145, spd: 127, spe: 106}, + baseStats: {hp: 100, atk: 100, def: 100, spa: 100, spd: 100, spe: 100}, + // For reverting back to an Unown forme + abilities: {0: "Protean"}, }, - rotomfrost: { + // Lamp + lampent: { inherit: true, - abilities: {0: "Adaptability"}, - baseStats: {hp: 50, atk: 65, def: 127, spa: 145, spd: 127, spe: 106}, + baseStats: {hp: 60, atk: 80, def: 100, spa: 135, spd: 100, spe: 95}, }, - rotomheat: { + // Meicoo + venusaurmega: { inherit: true, - abilities: {0: "Adaptability"}, - baseStats: {hp: 50, atk: 65, def: 127, spa: 145, spd: 127, spe: 106}, + abilities: {0: "Unaware"}, }, - rotommow: { + // nui + jigglypuff: { inherit: true, - abilities: {0: "Adaptability"}, - baseStats: {hp: 50, atk: 65, def: 127, spa: 145, spd: 127, spe: 106}, - }, - rotomwash: { - inherit: true, - abilities: {0: "Adaptability"}, - baseStats: {hp: 50, atk: 65, def: 127, spa: 145, spd: 127, spe: 106}, - }, - // Level 51 - porygon2: { - inherit: true, - abilities: {0: 'Stamina'}, - }, - // MacChaeger - mantyke: { - inherit: true, - baseStats: {hp: 90, atk: 40, def: 100, spa: 120, spd: 240, spe: 100}, - }, - // martha - dianciemega: { - inherit: true, - abilities: {0: 'Pixilate'}, - }, - // Morfent - banettemega: { - inherit: true, - abilities: {0: 'Comatose'}, - }, - // OM - flareon: { - inherit: true, - types: ['Fire', 'Fairy'], + baseStats: {hp: 115, atk: 128, def: 62, spa: 128, spd: 78, spe: 62}, }, // Overneat absolmega: { inherit: true, - abilities: {0: 'Filter'}, - types: ['Dark', 'Fairy'], + abilities: {0: "Fluffy"}, + types: ["Dark", "Fairy"], }, - // Pirate Princess - gulpin: { + // PartMan + chandelure: { inherit: true, - baseStats: {hp: 70, atk: 43, def: 53, spa: 86, spd: 53, spe: 40}, - }, - // pre - deoxys: { - inherit: true, - abilities: {0: 'Optimize'}, - }, - deoxysattack: { - inherit: true, - abilities: {0: 'Optimize'}, - }, - deoxysdefense: { - inherit: true, - abilities: {0: 'Optimize'}, - }, - deoxysspeed: { - inherit: true, - abilities: {0: 'Optimize'}, + abilities: {0: "Hecatomb"}, }, // Psynergy - blazikenmega: { + rayquaza: { inherit: true, - abilities: {0: 'Wrath'}, + abilities: {0: "Supernova"}, + }, + rayquazamega: { + inherit: true, + abilities: {0: "Supernova"}, + requiredMove: "Clear Breath", + }, + // Robb576 + necrozmadawnwings: { + inherit: true, + abilities: {0: "The Numbers Game"}, + }, + necrozmaduskmane: { + inherit: true, + abilities: {0: "The Numbers Game"}, + }, + necrozmaultra: { + inherit: true, + abilities: {0: "The Numbers Game"}, + }, + // Strucni + aggronmega: { + inherit: true, + abilities: {0: "Overasked Clause"}, + }, + // Finland + alcremie: { + inherit: true, + abilities: {0: "Winding Song"}, + }, + // vivalospride's interaction with Coconut's move + darumaka: { + inherit: true, + evos: ["Darmanitan", "Darmanitan-Zen"], + }, + darmanitanzen: { + inherit: true, + prevo: "Darumaka", + }, + // yuki + pikachucosplay: { + inherit: true, + baseStats: {hp: 60, atk: 85, def: 50, spa: 95, spd: 85, spe: 110}, + abilities: {0: "Combat Training"}, + }, + pikachuphd: { + inherit: true, + baseStats: {hp: 60, atk: 85, def: 50, spa: 95, spd: 85, spe: 110}, + abilities: {0: "Triage"}, }, - // Rach pikachulibre: { inherit: true, - types: ['Electric', 'Fighting'], + baseStats: {hp: 60, atk: 85, def: 50, spa: 95, spd: 85, spe: 110}, + abilities: {0: "White Smoke"}, }, - // Sunny - sceptilemega: { + pikachupopstar: { inherit: true, - abilities: {0: 'Contrary'}, + baseStats: {hp: 60, atk: 85, def: 50, spa: 95, spd: 85, spe: 110}, + abilities: {0: "Dancer"}, + }, + pikachurockstar: { + inherit: true, + baseStats: {hp: 60, atk: 85, def: 50, spa: 95, spd: 85, spe: 110}, + abilities: {0: "Punk Rock"}, + }, + pikachubelle: { + inherit: true, + baseStats: {hp: 60, atk: 85, def: 50, spa: 95, spd: 85, spe: 110}, + abilities: {0: "Tangled Feet"}, }, // Zalm weedle: { inherit: true, - baseStats: {hp: 85, atk: 100, def: 90, spa: 20, spd: 90, spe: 90}, + baseStats: {hp: 100, atk: 35, def: 100, spa: 90, spd: 90, spe: 100}, + }, + // Zarel + meloetta: { + inherit: true, + abilities: {0: "Dancer"}, + }, + meloettapirouette: { + inherit: true, + abilities: {0: "Serene Grace"}, }, }; diff --git a/data/mods/ssb/random-teams.ts b/data/mods/ssb/random-teams.ts index b90824ce72..72162fcab9 100644 --- a/data/mods/ssb/random-teams.ts +++ b/data/mods/ssb/random-teams.ts @@ -1,760 +1,888 @@ import RandomTeams from '../../random-teams'; -import {PRNG, PRNGSeed} from '../../../sim/prng'; -interface SSBSet { +export interface SSBSet { species: string; ability: string | string[]; item: string | string[]; gender: GenderName; moves: (string | string[])[]; signatureMove: string; - evs: {hp?: number, atk?: number, def?: number, spa?: number, spd?: number, spe?: number}; + evs?: {hp?: number, atk?: number, def?: number, spa?: number, spd?: number, spe?: number}; ivs?: {hp?: number, atk?: number, def?: number, spa?: number, spd?: number, spe?: number}; - nature: string | string[]; + nature?: string | string[]; shiny?: number | boolean; level?: number; happiness?: number; + skip?: string; } interface SSBSets {[k: string]: SSBSet} -export class RandomStaffBrosTeams extends RandomTeams { - allXfix: boolean; - constructor(format: Format | string, prng: PRNG | PRNGSeed | null = null) { - super(format, prng); - this.allXfix = (this.random(500) === 360); - } +export const ssbSets: SSBSets = { + /* + // Example: + Username: { + species: 'Species', ability: 'Ability', item: 'Item', gender: '', + moves: ['Move Name', ['Move Name', 'Move Name']], + signatureMove: 'Move Name', + evs: {stat: number}, ivs: {stat: number}, nature: 'Nature', level: 100, shiny: false, + }, + // Species, ability, and item need to be captialized properly ex: Ludicolo, Swift Swim, Life Orb + // Gender can be M, F, N, or left as an empty string + // each slot in moves needs to be a string (the move name, captialized properly ex: Hydro Pump), or an array of strings (also move names) + // signatureMove also needs to be capitalized properly ex: Scripting + // You can skip Evs (defaults to 82 all) and/or Ivs (defaults to 31 all), or just skip part of the Evs (skipped evs are 0) and/or Ivs (skipped Ivs are 31) + // You can also skip shiny, defaults to false. Level can be skipped (defaults to 100). + // Nature needs to be a valid nature with the first letter capitalized ex: Modest + */ + // Please keep sets organized alphabetically based on staff member name! + Abdelrahman: { + species: 'Camerupt', ability: 'Water Absorb', item: 'Cameruptite', gender: 'M', + moves: ['Eruption', 'Earth Power', 'Fire Blast'], + signatureMove: 'The Town Outplay', + evs: {hp: 252, spd: 172, spe: 84}, nature: 'Calm', + }, + Adri: { + species: 'Latios', ability: 'Psychic Surge', item: 'Leftovers', gender: 'M', + moves: ['Psyshock', 'Calm Mind', 'Aura Sphere'], + signatureMove: 'Skystriker', + evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Timid', + }, + Aelita: { + species: 'Zygarde', ability: 'Scyphozoa', item: 'Focus Sash', gender: 'F', + moves: ['Rest', 'Sleep Talk', 'Thousand Arrows'], + signatureMove: 'XANA\'s Keys To Lyoko', + evs: {hp: 252, atk: 4, spd: 252}, nature: 'Careful', + }, + aegii: { + species: 'Aegislash', ability: 'Set the Stage', item: 'Life Orb', gender: 'M', + moves: ['Shadow Claw', 'Iron Head', 'Shadow Sneak'], + signatureMove: 'Reset', + evs: {hp: 252, def: 192, spd: 64}, nature: 'Sassy', + }, + 'aegii-Alt': { + species: 'Aegislash', ability: 'Set the Stage', item: 'Life Orb', gender: 'M', + moves: ['Shadow Ball', 'Flash Cannon', 'Shadow Sneak'], + signatureMove: 'Reset', + evs: {hp: 252, def: 192, spd: 64}, nature: 'Sassy', + skip: 'aegii', + }, + Aeonic: { + species: 'Nosepass', ability: 'Arsene', item: 'Stone Plate', gender: 'M', + moves: ['Diamond Storm', 'Earthquake', 'Milk Drink'], + signatureMove: 'Looking Cool', + evs: {atk: 252, def: 4, spd: 252}, nature: 'Impish', + }, + Aethernum: { + species: 'Lotad', ability: 'Rainy Season', item: 'Big Root', gender: 'M', + moves: ['Giga Drain', 'Muddy Water', 'Hurricane'], + signatureMove: 'Lilypad Overflow', + evs: {spa: 252, spd: 4, spe: 252}, nature: 'Modest', + }, + Akir: { + species: 'Forretress', ability: 'Fortifications', item: 'Leftovers', gender: 'M', + moves: ['Rapid Spin', 'Stealth Rock', ['U-turn', 'Toxic']], + signatureMove: 'Ravelin', + evs: {hp: 248, def: 252, spe: 8}, ivs: {spa: 0}, nature: 'Impish', + }, + Alpha: { + species: 'Aurorus', ability: 'Snow Warning', item: 'Caionium Z', gender: 'M', + moves: ['Freeze-Dry', 'Ancient Power', 'Earth Power'], + signatureMove: 'Blizzard', + evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Timid', shiny: true, + }, + Andrew: { + species: 'Spectrier', ability: 'Neutralizing Gas', item: 'Choice Specs', gender: 'M', + moves: ['Astral Barrage', 'Moonblast', 'Aura Sphere'], + signatureMove: 'Whammer Jammer', + evs: {hp: 4, spa: 252, spe: 252}, ivs: {atk: 0}, nature: 'Timid', + }, + Annika: { + species: 'Mewtwo', ability: 'Overprotective', item: 'Mewtwonite Y', gender: 'F', + moves: [['Rising Voltage', 'Lava Plume'], ['Hex', 'Aurora Beam'], ['Psychic', 'Psyshock']], + signatureMove: 'Data Corruption', + evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Quirky', shiny: true, + }, + 'A Quag To The Past': { + species: 'Quagsire', ability: 'Carefree', item: 'Quagnium Z', gender: 'M', + moves: ['Shore Up', 'Flip Turn', ['Haze', 'Toxic']], + signatureMove: 'Scorching Sands', + evs: {hp: 252, def: 252, spd: 4}, ivs: {spe: 0}, nature: 'Relaxed', + }, + Arby: { + species: 'Keldeo-Resolute', ability: 'Wave Surge', item: 'Expert Belt', gender: '', + moves: ['Hydro Pump', 'Secret Sword', 'Ice Beam'], + signatureMove: 'Quickhammer', + evs: {def: 4, spa: 252, spe: 252}, ivs: {atk: 0}, nature: 'Timid', + }, + Arcticblast: { + species: 'Tapu Fini', ability: 'Misty Surge', item: 'Misty Seed', gender: '', + moves: ['Heal Order', 'Sparkling Aria', ['Clear Smog', 'Moonblast']], + signatureMove: 'Radiant Burst', + evs: {hp: 252, def: 252, spe: 4}, ivs: {atk: 0}, nature: 'Bold', + }, + Archas: { + species: 'Naviathan', ability: 'Indomitable', item: 'Iron Plate', gender: 'F', + moves: ['Waterfall', 'Icicle Crash', 'No Retreat'], + signatureMove: 'Broadside Barrage', + evs: {atk: 252, def: 4, spe: 252}, nature: 'Jolly', + }, + Averardo: { + species: 'Hattrem', ability: 'Magic Hat', item: 'Eviolite', gender: 'M', + moves: ['Nuzzle', 'Flamethrower', 'Healing Wish'], + signatureMove: 'Hat of Wisdom', + evs: {hp: 252, def: 4, spd: 252}, ivs: {atk: 0}, nature: 'Sassy', + }, + 'awa!': { + species: 'Lycanroc', ability: 'Sand Rush', item: 'Life Orb', gender: 'F', + moves: ['Earthquake', 'Close Combat', 'Swords Dance'], + signatureMove: 'awa!', + evs: {atk: 252, spd: 4, spe: 252}, nature: 'Adamant', + }, + Beowulf: { + species: 'Beedrill', ability: 'Intrepid Sword', item: 'Beedrillite', gender: '', + moves: ['Megahorn', 'Gunk Shot', ['Precipice Blades', 'Head Smash']], + signatureMove: 'Buzz Inspection', + evs: {hp: 4, atk: 252, spe: 252}, nature: 'Jolly', shiny: 2, + }, + biggie: { + species: 'Snorlax', ability: 'Super Armor', item: 'Leftovers', gender: 'M', + moves: ['Body Slam', 'Darkest Lariat', 'Assist'], + signatureMove: 'Juggernaut Punch', + evs: {hp: 4, def: 252, spd: 252}, nature: 'Brave', + }, + Billo: { + species: 'Cosmog', ability: 'Proof Policy', item: 'Eviolite', gender: 'N', + moves: ['Cosmic Power', 'Calm Mind', 'Stored Power'], + signatureMove: 'Fishing for Hacks', + evs: {hp: 252, spa: 252, spd: 4}, ivs: {atk: 0}, nature: 'Modest', shiny: true, + }, + Blaz: { + species: 'Carbink', ability: 'Why Worry', item: 'Leftovers', gender: 'N', + moves: ['Cosmic Power', 'Body Press', 'Recover'], + signatureMove: 'Bleak December', + evs: {hp: 4, def: 252, spd: 252}, ivs: {atk: 0}, nature: 'Careful', shiny: true, + }, + Brandon: { + species: 'Shaymin', ability: 'Bane Surge', item: ['Leftovers', 'Terrain Extender'], gender: 'M', + moves: [['Ice Beam', 'Paleo Wave'], ['Earthquake', 'Flamethrower'], 'Recover'], + signatureMove: 'Flower Shower', + evs: {hp: 84, atk: 84, def: 84, spa: 84, spd: 84, spe: 84}, nature: 'Quirky', + }, + brouha: { + species: 'Mantine', ability: 'Turbulence', item: 'Leftovers', gender: 'M', + moves: ['Scald', 'Recover', 'Haze'], + signatureMove: 'Kinetosis', + evs: {hp: 248, def: 8, spd: 252}, ivs: {atk: 0}, nature: 'Calm', + }, + Cake: { + species: 'Dunsparce', ability: 'Wonder Guard', item: 'Shell Bell', gender: 'M', + moves: ['Haze', 'Ingrain', ['Poison Gas', 'Corrosive Gas', 'Magic Powder', 'Speed Swap', 'Spite', 'Refresh', 'Screech', 'Trick Room', 'Heal Block', 'Geomancy']], + signatureMove: 'Kevin', + evs: {hp: 252, atk: 252, spd: 4}, nature: 'Adamant', + }, + 'cant say': { + species: 'Volcarona', ability: 'Rage Quit', item: 'Kee Berry', gender: 'M', + moves: ['Quiver Dance', 'Roost', 'Will-O-Wisp'], + signatureMove: 'Never Lucky', + evs: {hp: 248, def: 36, spe: 224}, ivs: {atk: 0}, nature: 'Timid', + }, + Celestial: { + species: 'Dragonite', ability: 'Speed Boost', item: 'Metal Coat', gender: '', + moves: ['Swords Dance', 'Thousand Arrows', 'Double Iron Bash'], + signatureMove: 'Pandora\'s Box', + evs: {atk: 252, def: 4, spe: 252}, nature: 'Jolly', shiny: 2, + }, + Celine: { + species: 'Lucario', ability: 'Guardian Armor', item: 'Leftovers', gender: 'F', + moves: ['Wish', 'Teleport', 'Drain Punch'], + signatureMove: 'Status Guard', + evs: {hp: 248, def: 252, spd: 8}, nature: 'Impish', + }, + 'c.kilgannon': { + species: 'Yveltal', ability: 'Infiltrator', item: 'Choice Scarf', gender: 'N', + moves: ['Knock Off', 'Steel Wing', 'U-turn'], + signatureMove: 'Soul Siphon', + evs: {atk: 252, spd: 4, spe: 252}, nature: 'Adamant', + }, + Coconut: { + species: 'Misdreavus', ability: 'Levitate', item: 'Focus Sash', gender: 'F', + moves: ['Dazzling Gleam', 'Shadow Ball', 'Snatch'], + signatureMove: 'Devolution Beam', + evs: {spa: 252, spd: 4, spe: 252}, nature: 'Timid', + }, + dogknees: { + species: 'Furret', ability: 'Adaptability', item: ['Normalium Z', 'Ghostium Z'], gender: 'M', + moves: ['Extreme Speed', 'Shadow Claw', 'Explosion'], + signatureMove: 'Belly Rubs', + evs: {hp: 4, atk: 252, spe: 252}, nature: 'Jolly', + }, + DragonWhale: { + species: 'Mimikyu', ability: 'Disguise', item: 'Life Orb', gender: 'M', + moves: ['Play Rough', 'Spectral Thief', 'Shadow Sneak'], + signatureMove: 'Cloak Dance', + evs: {hp: 4, atk: 252, spe: 252}, nature: 'Jolly', + }, + 'drampa\'s grandpa': { + species: 'Drampa', ability: 'Old Manpa', item: 'Wise Glasses', gender: 'M', + moves: [ + ['Spikes', 'Stealth Rock', 'Toxic Spikes'], 'Slack Off', ['Core Enforcer', 'Snarl', 'Lava Plume', 'Scorching Sands'], + ], + signatureMove: 'GET OFF MY LAWN!', + evs: {hp: 248, def: 8, spa: 252}, ivs: {atk: 0}, nature: 'Modest', + }, + dream: { + species: 'Klefki', ability: 'Greed Punisher', item: 'Life Orb', gender: 'N', + moves: ['Light of Ruin', 'Steel Beam', 'Mind Blown'], + signatureMove: 'Lock and Key', + evs: {hp: 4, spa: 252, spe: 252}, ivs: {atk: 0}, nature: 'Modest', + }, + Elgino: { + species: 'Celebi', ability: 'Magic Guard', item: 'Life Orb', gender: 'M', + moves: ['Leaf Storm', 'Nasty Plot', 'Power Gem'], + signatureMove: 'Navi\'s Grace', + evs: {hp: 4, spa: 252, spe: 252}, ivs: {atk: 0}, nature: 'Timid', shiny: true, + }, + Emeri: { + species: 'Flygon', ability: 'Draco Voice', item: 'Throat Spray', gender: 'M', + moves: ['Boomburst', 'Earth Power', 'Agility'], + signatureMove: 'Forced Landing', + evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Timid', + }, + estarossa: { + species: 'Hippowdon', ability: 'Sands of Time', item: 'Leftovers', gender: 'M', + moves: ['Earthquake', 'Stone Edge', 'Slack Off'], + signatureMove: 'Sand Balance', + evs: {hp: 252, atk: 252, def: 4}, nature: 'Adamant', + }, + EpicNikolai: { + species: 'Garchomp', ability: 'Dragon Heart', item: 'Garchompite', gender: 'M', + moves: ['Outrage', 'Earthquake', 'Swords Dance'], + signatureMove: 'Epic Rage', + evs: {atk: 252, def: 4, spe: 252}, nature: 'Jolly', + }, + explodingdaisies: { + species: 'Shedinja', ability: 'Wonder Guard', item: 'Heavy-Duty Boots', gender: 'M', + moves: ['Swords Dance', 'X-Scissor', 'Shadow Sneak'], + signatureMove: 'You Have No Hope!', + evs: {atk: 252, spd: 4, spe: 252}, nature: 'Adamant', + }, + fart: { + species: 'Kartana', ability: 'Bipolar', item: 'Metronome', gender: 'M', + moves: ['U-turn'], + signatureMove: 'Soup-Stealing 7-Star Strike: Redux', + evs: {atk: 252, def: 4, spe: 252}, nature: 'Jolly', level: 100, shiny: true, + }, + Felucia: { + species: 'Uxie', ability: 'Regenerator', item: 'Red Card', gender: 'F', + moves: ['Strength Sap', ['Psyshock', 'Night Shade'], ['Thief', 'Toxic']], + signatureMove: 'Rigged Dice', + evs: {hp: 252, def: 4, spd: 252}, nature: 'Calm', + }, + Finland: { + species: 'Alcremie', ability: 'Winding Song', item: 'Leftovers', gender: 'M', + moves: ['Shore Up', 'Moonblast', ['Infestation', 'Whirlwind']], + signatureMove: 'Cradily Chaos', + evs: {hp: 252, def: 64, spa: 64, spd: 64, spe: 64}, ivs: {atk: 0}, nature: 'Serious', + }, + 'Finland-Tsikhe': { + species: 'Alcremie-Lemon-Cream', ability: 'Winding Song', item: 'Leftovers', gender: 'M', + moves: ['Shore Up', 'Spiky Shield', ['Reflect', 'Light Screen']], + signatureMove: 'Cradily Chaos', + evs: {hp: 252, def: 64, spa: 64, spd: 64, spe: 64}, ivs: {atk: 0}, nature: 'Serious', + skip: 'Finland', + }, + 'Finland-Nezavisa': { + species: 'Alcremie-Ruby-Swirl', ability: 'Winding Song', item: 'Leftovers', gender: 'M', + moves: ['Lava Plume', 'Scorching Sands', ['Refresh', 'Destiny Bond']], + signatureMove: 'Cradily Chaos', + evs: {hp: 252, def: 64, spa: 64, spd: 64, spe: 64}, ivs: {atk: 0}, nature: 'Serious', + skip: 'Finland', + }, + 'Finland-Järvilaulu': { + species: 'Alcremie-Mint-Cream', ability: 'Winding Song', item: 'Leftovers', gender: 'M', + moves: ['Sticky Web', 'Parting Shot', ['Light of Ruin', 'Sparkling Aria']], + signatureMove: 'Cradily Chaos', + evs: {hp: 252, def: 64, spa: 64, spd: 64, spe: 64}, ivs: {atk: 0}, nature: 'Serious', + skip: 'Finland', + }, + 'frostyicelad ❆': { + species: 'Lapras-Gmax', ability: 'Ice Shield', item: 'Life Orb', gender: 'M', + moves: ['Quiver Dance', 'Sparkling Aria', 'Recover'], + signatureMove: 'Frosty Wave', + evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Timid', + }, + 'gallant\'s pear': { + species: 'Orbeetle', ability: 'Armor Time', item: ['Life Orb', 'Heavy-Duty Boots'], gender: 'M', + moves: ['Bug Buzz', 'Nasty Plot', 'Snipe Shot'], + signatureMove: 'King Giri Giri Slash', + evs: {hp: 252, def: 4, spe: 252}, nature: 'Timid', + }, + Gimmick: { + species: 'Grimmsnarl', ability: 'IC3PEAK', item: 'Throat Spray', gender: 'M', + moves: ['Boomburst', 'Disarming Voice', 'Snarl'], + signatureMove: 'Random Screaming', + evs: {hp: 4, spa: 252, spe: 252}, ivs: {atk: 0}, nature: 'Timid', shiny: true, + }, + GMars: { + species: 'Minior-Meteor', ability: 'Capsule Armor', item: 'White Herb', gender: 'N', + moves: ['Acrobatics', 'Earthquake', 'Diamond Storm'], + signatureMove: 'Gacha', + evs: {hp: 68, atk: 252, spe: 188}, nature: 'Adamant', + }, + grimAuxiliatrix: { + species: 'Duraludon', ability: 'Bio-steel', item: 'Assault Vest', gender: '', + moves: [['Core Enforcer', 'Draco Meteor'], 'Flash Cannon', ['Thunderbolt', 'Fire Blast']], + signatureMove: 'Fuel Leak', + evs: {spa: 252, spd: 4, spe: 252}, nature: 'Timid', + }, + HoeenHero: { + species: 'Ludicolo', ability: 'Tropical Cyclone', item: 'Life Orb', gender: 'M', + moves: ['Scald', 'Giga Drain', 'Hurricane'], + signatureMove: 'Landfall', + evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Modest', + }, + Hubriz: { + species: 'Roserade', ability: 'Stakeout', item: 'Rose Incense', gender: 'F', + moves: [['Toxic Spikes', 'Spikes'], 'Leaf Storm', 'Sludge Bomb'], + signatureMove: 'Steroid Anaphylaxia', + evs: {def: 4, spa: 252, spe: 252}, ivs: {atk: 0}, nature: 'Timid', + }, + Hydro: { + species: 'Pichu', ability: 'Hydrostatic', item: 'Eviolite', gender: 'M', + moves: ['Hydro Pump', 'Thunder', 'Ice Beam'], + signatureMove: 'Hydrostatics', + evs: {def: 4, spa: 252, spe: 252}, nature: 'Modest', + }, + Inactive: { + species: 'Gyarados', ability: 'Dragon\'s Fury', item: 'Gyaradosite', gender: '', + moves: ['Dragon Dance', 'Earthquake', 'Crabhammer'], + signatureMove: 'Paranoia', + evs: {atk: 252, def: 4, spe: 252}, nature: 'Jolly', + }, + INStruct: { + species: 'Riolu', ability: 'Truant', item: 'Heavy-Duty Boots', gender: '', + moves: ['Explosion', 'Lunar Dance', 'Memento'], + signatureMove: 'Soda Break', + evs: {hp: 252, atk: 4, spe: 252}, nature: 'Jolly', + }, + Iyarito: { + species: 'Gengar', ability: 'Pollo Diablo', item: 'Choice Specs', gender: 'F', + moves: ['Sludge Wave', 'Volt Switch', 'Fusion Flare'], + signatureMove: 'Patrona Attack', + evs: {def: 4, spa: 252, spe: 252}, nature: 'Timid', shiny: true, + }, + Jett: { + species: 'Sneasel', ability: 'Deceiver', item: 'Heavy Duty Boots', gender: 'F', + moves: ['Knock Off', 'Triple Axel', 'Counter'], + signatureMove: 'The Hunt is On!', + evs: {hp: 4, atk: 252, spe: 252}, nature: 'Jolly', + }, + Jho: { + species: 'Toxtricity', ability: 'Punk Rock', item: 'Throat Spray', gender: 'M', + moves: ['Nasty Plot', 'Overdrive', 'Volt Switch'], + signatureMove: 'Genre Change', + evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Timid', + }, + 'Jho-Low-Key': { + species: 'Toxtricity-Low-Key', ability: 'Venomize', item: 'Throat Spray', gender: 'M', + moves: ['Aura Sphere', 'Boomburst', 'Volt Switch'], + signatureMove: 'Genre Change', + evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Timid', + skip: 'Jho', + }, + Jordy: { + species: 'Archeops', ability: 'Divine Sandstorm', item: 'Life Orb', gender: 'M', + moves: ['Brave Bird', 'Head Smash', ['U-turn', 'Roost', 'Icicle Crash']], + signatureMove: 'Archeops\'s Rage', + evs: {atk: 252, spd: 4, spe: 252}, nature: 'Jolly', + }, + 'Kaiju Bunny': { + species: 'Lopunny', ability: 'Second Wind', item: 'Lopunnite', gender: 'F', + moves: ['Return', 'Play Rough', ['Drain Punch', 'High Jump Kick']], + signatureMove: 'Cozy Cuddle', + evs: {atk: 252, spd: 4, spe: 252}, nature: 'Jolly', shiny: true, + }, + Kalalokki: { + species: 'Wingull', ability: 'Magic Guard', item: 'Kalalokkium Z', gender: 'M', + moves: ['Tailwind', 'Encore', 'Healing Wish'], + signatureMove: 'Blackbird', + evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Timid', + }, + Kennedy: { + species: 'Cinderace', ability: 'False Nine', item: 'Choice Band', gender: 'M', + moves: ['High Jump Kick', 'Triple Axel', 'U-turn'], + signatureMove: 'Top Bins', + evs: {atk: 252, def: 4, spe: 252}, nature: 'Adamant', + }, + Kev: { + species: 'Kingdra', ability: 'King of Atlantis', item: 'Life Orb', gender: 'M', + moves: ['Hydro Pump', 'Core Enforcer', 'Hurricane'], + signatureMove: 'King\'s Trident', + evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Modest', + }, + Kingbaruk: { + species: 'Stonjourner', ability: 'Sturdy', item: 'Heavy Duty Boots', gender: 'M', + moves: ['Diamond Storm', ['Superpower', 'Earthquake'], 'King\'s Shield'], + signatureMove: 'Leave it to the team!', + evs: {atk: 252, def: 4, spe: 252}, nature: 'Jolly', + }, + KingSwordYT: { + species: 'Pangoro', ability: 'Bamboo Kingdom', item: 'Rocky Helmet', gender: 'M', + moves: ['Body Press', 'Spiky Shield', 'Shore Up'], + signatureMove: 'Clash of Pangoros', + evs: {hp: 252, atk: 4, def: 252}, nature: 'Impish', shiny: true, + }, + Kipkluif: { + species: 'Gossifleur', ability: 'Degenerator', item: 'Eviolite', gender: 'M', + moves: ['Strength Sap', 'Apple Acid', 'Court Change'], + signatureMove: 'Kip Up', + evs: {hp: 196, def: 116, spa: 36, spd: 116, spe: 36}, ivs: {atk: 0}, nature: 'Modest', shiny: true, + }, + Kris: { + species: 'Unown', ability: 'Protean', item: 'Life Orb', gender: 'N', + moves: ['Light of Ruin', 'Psystrike', ['Secret Sword', 'Mind Blown', 'Seed Flare']], + signatureMove: 'Alphabet Soup', + evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Timid', + }, + Lamp: { + species: 'Lampent', ability: 'Candlewax', item: 'Eviolite', gender: 'M', + moves: ['Nasty Plot', 'Searing Shot', 'Strength Sap'], + signatureMove: 'Soul Swap', + evs: {def: 4, spa: 252, spe: 252}, ivs: {atk: 0}, nature: 'Timid', + }, + Lionyx: { + species: 'Gardevoir', ability: 'Tension', item: 'Blunder Policy', gender: 'F', + moves: [ + ['Psychic', 'Psystrike'], 'Quiver Dance', [ + 'Blizzard', 'Focus Blast', 'Hurricane', 'Hydro Pump', 'Inferno', 'Zap Cannon', + ], + ], + signatureMove: 'Big Bang', + evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Timid', shiny: true, + }, + 'Litt♥Eleven': { + species: 'Bisharp', ability: 'Dark Royalty', item: 'Black Glasses', gender: 'M', + moves: ['Sucker Punch', 'Knock Off', 'Iron Head'], + signatureMove: '/nexthunt', + evs: {hp: 4, atk: 252, spe: 252}, nature: 'Adamant', shiny: true, + }, + 'Mad Monty ¾°': { + species: 'Zekrom', ability: 'Petrichor', item: 'Damp Rock', gender: 'N', + moves: ['Bolt Strike', 'Dragon Claw', 'Liquidation'], + signatureMove: 'Ca-LLAMA-ty', + evs: {atk: 252, def: 4, spe: 252}, ivs: {def: 0}, nature: 'Jolly', shiny: true, + }, + MajorBowman: { + species: 'Weezing-Galar', ability: 'Neutralizing Gas', item: 'Black Sludge', gender: 'M', + moves: ['Strange Steam', ['Toxic Spikes', 'Haze'], 'Recover'], + signatureMove: 'Corrosive Cloud', + evs: {hp: 252, def: 252, spd: 4}, nature: 'Bold', + }, + Marshmallon: { + species: 'Munchlax', ability: 'Stubbornness', item: 'Eviolite', gender: 'M', + moves: ['Head Charge', 'Flare Blitz', 'Wood Hammer', 'Head Smash'], + signatureMove: 'RAWWWR', + evs: {hp: 248, def: 252, spd: 8}, ivs: {spe: 0}, nature: 'Relaxed', + }, + Meicoo: { + species: 'Venusaur', ability: 'Regenerator', item: 'Venusaurite', gender: 'M', + moves: ['Sludge Bomb', ['Giga Drain', 'Knock Off', 'Flamethrower'], ['Recover', 'Strength Sap']], + signatureMove: 'spamguess', + evs: {hp: 252, def: 252, spd: 4}, nature: 'Bold', + }, + Mitsuki: { + species: 'Leafeon', ability: 'Photosynthesis', item: ['Life Orb', 'Miracle Seed'], gender: 'M', + moves: ['Leaf Blade', 'Attack Order', 'Thousand Arrows'], + signatureMove: 'Terraforming', + evs: {atk: 252, def: 4, spe: 252}, nature: 'Jolly', + }, + n10siT: { + species: 'Hoopa', ability: 'Greedy Magician', item: 'Focus Sash', gender: 'N', + moves: ['Hyperspace Hole', 'Shadow Ball', 'Aura Sphere'], + signatureMove: 'Unbind', + evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Timid', + }, + Naziel: { + species: 'Kirlia', ability: 'Prankster', item: 'Eviolite', gender: '', + moves: ['Glare', 'Defog', 'Swagger'], + signatureMove: 'Not-so-worthy Pirouette', + evs: {hp: 252, def: 200, spd: 56}, ivs: {atk: 0}, nature: 'Calm', shiny: true, + }, + Nol: { + species: 'Litwick', ability: 'Burning Soul', item: 'Spooky Plate', gender: 'F', + moves: ['Shadow Ball', 'Flamethrower', 'Trick Room'], + signatureMove: 'Mad Hacks', + evs: {hp: 252, spa: 252, spd: 4}, ivs: {atk: 0}, nature: 'Modest', shiny: true, + }, + Notater517: { + species: 'Jellicent', ability: 'Last-Minute Lag', item: 'Leftovers', gender: 'M', + moves: ['Hydro Cannon', 'Blast Burn', 'Toxic Spikes'], + signatureMove: 'Techno Tuber Transmission', + evs: {hp: 236, spa: 252, spe: 20}, ivs: {atk: 0}, nature: 'Modest', + }, + nui: { + species: 'Jigglypuff', ability: 'Condition Override', item: 'King\'s Rock', gender: 'M', + moves: ['Stealth Rock', 'Attract', 'Heal Order'], + signatureMove: 'Win Condition', + evs: {hp: 248, def: 92, spd: 168}, nature: 'Bold', shiny: true, + }, + 'OM~!': { + species: 'Glastrier', ability: 'Filter', item: 'Heavy Duty Boots', gender: 'M', + moves: ['Recover', 'Stealth Rock', 'Earthquake'], + signatureMove: 'OM Zoom', + evs: {hp: 252, def: 252, spd: 4}, ivs: {spe: 0}, nature: 'Relaxed', + }, + Overneat: { + species: 'Absol', ability: 'Intimidate', item: 'Absolite', gender: 'M', + moves: ['Play Rough', 'U-turn', 'Close Combat'], + signatureMove: 'Healing you?', + evs: {atk: 252, def: 4, spe: 252}, nature: 'Jolly', + }, + pants: { + species: 'Phantump', ability: 'Ghost Spores', item: 'Eviolite', gender: 'M', + moves: ['Taunt', 'Spirit Shackle', ['Horn Leech', 'U-turn', 'Flip Turn']], + signatureMove: 'Wistful Thinking', + evs: {hp: 252, def: 4, spd: 252}, nature: 'Impish', shiny: true, + }, + 'Paradise ╱╲☼': { + species: 'Slaking', ability: 'Unaware', item: 'Choice Scarf', gender: '', + moves: ['Sacred Fire', 'Spectral Thief', 'Icicle Crash'], + signatureMove: 'Rapid Turn', + evs: {atk: 252, def: 4, spe: 252}, nature: 'Jolly', + }, + PartMan: { + species: 'Chandelure', ability: 'Hecatomb', item: 'Focus Sash', gender: 'M', + moves: ['Nasty Plot', 'Draining Kiss', 'Dark Pulse'], + signatureMove: 'Baleful Blaze', + evs: {hp: 4, spa: 252, spe: 252}, ivs: {atk: 0}, nature: 'Timid', + }, + 'PartMan-Shiny': { + species: 'Chandelure', ability: 'Hecatomb', item: 'Focus Sash', gender: 'M', + moves: ['Nasty Plot', 'Light of Ruin', 'Fiery Wrath'], + signatureMove: 'Baleful Blaze', + evs: {hp: 4, spa: 252, spe: 252}, ivs: {atk: 0}, nature: 'Timid', shiny: true, + skip: 'PartMan', + }, + 'peapod c': { + species: 'Dragapult', ability: 'Stealth Black', item: 'Leftovers', gender: 'M', + moves: ['Hex', 'Dragon Darts', 'Work Up'], + signatureMove: 'Submartingale', + evs: {atk: 4, spa: 252, spe: 252}, nature: 'Mild', + }, + 'Perish Song': { + species: 'Rhydon', ability: 'Soup Sipper', item: 'Rocky Helmet', gender: 'M', + moves: ['Swords Dance', 'Rock Blast', 'Earthquake'], + signatureMove: 'Shifting Rocks', + evs: {hp: 252, atk: 4, def: 252}, nature: 'Impish', + }, + phiwings99: { + species: 'Froslass', ability: 'Plausible Deniability', item: 'Heavy Duty Boots', gender: 'M', + moves: ['Moongeist Beam', 'Spikes', 'Haze'], + signatureMove: 'Ghost of 1v1 Past', + evs: {hp: 252, spa: 4, spe: 252}, ivs: {atk: 0}, nature: 'Timid', + }, + 'piloswine gripado': { + species: 'Piloswine', ability: 'Forever Winter Nights', item: 'Eviolite', gender: 'M', + moves: ['Earthquake', 'Bulk Up', 'refresh'], + signatureMove: 'Icicle Spirits', + evs: {hp: 252, atk: 252, def: 4}, nature: 'Adamant', + }, + 'PiraTe Princess': { + species: 'Polteageist', ability: 'Wild Magic Surge', item: 'Expert Belt', gender: 'F', + moves: [ + 'Moongeist Beam', 'Spacial Rend', [ + 'Tri Attack', 'Fiery Dance', 'Scald', 'Discharge', 'Apple Acid', 'Ice Beam', + 'Aura Sphere', 'Sludge Bomb', 'Earth Power', 'Oblivion Wing', 'Psyshock', 'Bug Buzz', + 'Power Gem', 'Dark Pulse', 'Flash Cannon', 'Dazzling Gleam', + ], + ], + signatureMove: 'Dungeons & Dragons', + evs: {def: 4, spa: 252, spe: 252}, ivs: {atk: 0}, nature: 'Timid', + }, + Psynergy: { + species: 'Rayquaza', ability: 'Supernova', item: 'Wise Glasses', gender: 'M', + moves: ['Bouncy Bubble', 'Discharge', 'Lava Plume'], + signatureMove: 'Clear Breath', + evs: {spa: 252, spd: 4, spe: 252}, nature: 'Serious', shiny: true, + }, + ptoad: { + species: 'Palpitoad', ability: 'Swampy Surge', item: 'Eviolite', gender: 'M', + moves: ['Recover', 'Refresh', ['Sludge Bomb', 'Sludge Wave']], + signatureMove: 'Croak', + evs: {hp: 248, def: 8, spd: 252}, ivs: {atk: 0}, nature: 'Calm', + }, + quadrophenic: { + species: 'Dodrio', ability: 'Extreme Ways', item: 'Choice Band', gender: 'N', + moves: ['Dragon Ascent', 'Close Combat', 'U-turn'], + signatureMove: 'Triple Threat', + evs: {atk: 252, spd: 4, spe: 252}, nature: 'Jolly', + }, + Rabia: { + species: 'Mew', ability: 'Psychic Surge', item: 'Life Orb', gender: 'M', + moves: ['Nasty Plot', ['Flamethrower', 'Fire Blast'], 'Roost'], + signatureMove: 'Psycho Drive', + evs: {spa: 252, spd: 4, spe: 252}, nature: 'Timid', shiny: true, + }, + Rach: { + species: 'Spinda', ability: 'BURN IT DOWN!', item: 'Leftovers', gender: 'F', + moves: ['Extreme Speed', 'Recover', 'Knock Off'], + signatureMove: 'Spinda Wheel', + evs: {hp: 252, atk: 4, def: 252}, nature: 'Impish', + }, + Rage: { + species: 'Espeon', ability: 'Inversion Surge', item: 'Leftovers', gender: 'M', + moves: ['Psychic', 'Calm Mind', 'Hyper Voice'], + signatureMove: ':shockedlapras:', + evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Timid', + }, + 'Raihan Kibana': { + species: 'Stoutland', ability: 'Royal Coat', item: 'Leftovers', gender: 'M', + moves: ['Knock Off', 'Thousand Waves', ['Play Rough', 'Power Whip']], + signatureMove: 'Stony Kibbles', + evs: {atk: 128, spd: 252, spe: 128}, nature: 'Jolly', + }, + 'Raj.Shoot': { + species: 'Charizard', ability: 'Tough Claws', item: 'Heavy-Duty Boots', gender: 'N', + moves: ['Flare Blitz', 'Dragon Claw', 'Roost'], + signatureMove: 'Fan Service', + evs: {atk: 252, def: 4, spe: 252}, nature: 'Jolly', + }, + Ransei: { + species: 'Audino', ability: 'Neutralizing Gas', item: 'Choice Scarf', gender: 'M', + moves: ['Trick', 'Recover', 'Spectral Thief'], + signatureMove: 'ripsei', + evs: {hp: 252, atk: 4, spe: 252}, nature: 'Jolly', + }, + RavioliQueen: { + species: 'Mismagius', ability: 'Phantom Plane', item: 'Spell Tag', gender: '', + moves: ['Shadow Ball', 'Dark Pulse', 'Psychic'], + signatureMove: 'Witching Hour', + evs: {def: 4, spa: 52, spe: 252}, ivs: {atk: 0}, nature: 'Modest', + }, + Robb576: { + species: 'Necrozma-Dawn-Wings', ability: 'The Numbers Game', item: 'Metronome', gender: 'M', + moves: ['Moongeist Beam', 'Psystrike', 'Thunder Wave'], + signatureMove: 'Mode [5: Offensive]', + evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Modest', + }, + 'Robb576-Dusk-Mane': { + species: 'Necrozma-Dusk-Mane', ability: 'The Numbers Game', item: 'Leftovers', gender: 'M', + moves: ['Sunsteel Strike', 'Toxic', 'Rapid Spin'], + signatureMove: 'Mode [7: Defensive]', + evs: {hp: 252, atk: 4, spd: 252}, nature: 'Careful', + skip: 'Robb576', // This set is transformed into by The Numbers Game ability + }, + 'Robb576-Ultra': { + species: 'Necrozma-Ultra', ability: 'The Numbers Game', item: 'Modium-6 Z', gender: 'M', + moves: ['Earthquake', 'Dynamax Cannon', 'Fusion Flare'], + signatureMove: 'Photon Geyser', + evs: {atk: 204, spa: 200, spe: 104}, nature: 'Hasty', + skip: 'Robb576', // This set is transformed into by The Numbers Game ability + }, + SectoniaServant: { + species: 'Reuniclus', ability: 'Royal Aura', item: 'Leftovers', gender: 'M', + moves: ['Eerie Spell', 'Moonblast', 'Recover'], + signatureMove: 'Homunculus\'s Vanity', + evs: {hp: 252, def: 252, spd: 4}, ivs: {atk: 0, spe: 0}, nature: 'Relaxed', shiny: true, + }, + Segmr: { + species: 'Runerigus', ability: 'Skill Drain', item: 'Leftovers', gender: 'M', + moves: ['Recover', 'Will-O-Wisp', 'Protect'], + signatureMove: 'Tsukuyomi', + evs: {hp: 252, def: 4, spd: 252}, nature: 'Calm', shiny: true, + }, + sejesensei: { + species: 'Garbodor', ability: 'Trash Consumer', item: 'Red Card', gender: 'M', + moves: ['Toxic Spikes', 'Spikes', 'Thousand Waves'], + signatureMove: 'Bad Opinion', + evs: {hp: 252, atk: 56, def: 200}, nature: 'Impish', shiny: 2, + }, + Seso: { + species: 'Nidoking', ability: 'Intrepid Sword', item: 'Weakness Policy', gender: 'M', + moves: ['Sacred Sword', 'Leaf Blade', 'Behemoth Blade'], + signatureMove: 'Legendary Swordsman', + evs: {atk: 252, def: 4, spe: 252}, nature: 'Adamant', shiny: true, + }, + Shadecession: { + species: 'Honchkrow', ability: 'Shady Deal', item: 'Heavy Duty Boots', gender: 'M', + moves: ['Knock Off', 'Roost', 'Brave Bird'], + signatureMove: 'Shade Uppercut', + evs: {atk: 252, spd: 4, spe: 252}, nature: 'Jolly', shiny: true, + }, + 'Soft Flex': { + species: 'Zapdos', ability: 'Eye of the Storm', item: ['Leftovers', 'Damp Rock'], gender: '', + moves: ['Thunder', 'Roost', ['Defog', 'Toxic']], + signatureMove: 'Updraft', + evs: {hp: 252, def: 252, spe: 8}, ivs: {atk: 0}, nature: 'Bold', shiny: 1024, + }, + Spandan: { + species: 'Mareanie', ability: 'Hacked Corrosion', item: 'Eviolite', gender: 'M', + moves: ['Toxic', 'Recover', 'Spiky Shield'], + signatureMove: 'I\'m Toxic You\'re Slippin\' Under', + evs: {hp: 252, def: 4, spd: 252}, nature: 'Calm', + }, + Struchni: { + species: 'Aggron', ability: 'Overasked Clause', item: 'Choice Band', gender: 'M', + moves: ['Pursuit', 'U-turn', 'Fishious Rend'], + signatureMove: 'Veto', + evs: {hp: 251, atk: 5, def: 11, spd: 241}, nature: 'Careful', + }, + Teclis: { + species: 'Typhlosion', ability: 'Fiery Fur', item: 'Heavy Duty Boots', gender: 'M', + moves: ['Earth Power', 'Seed Flare', 'Spiky Shield'], + signatureMove: 'Kaboom', + evs: {def: 4, spa: 252, spe: 252}, ivs: {atk: 0}, nature: 'Modest', + }, + temp: { + species: 'Latias', ability: 'Charged Up', item: 'Dragon Fang', gender: 'F', + moves: ['Psychic', 'Surf', 'Roost'], + signatureMove: 'DROP A DRACO', + evs: {def: 4, spa: 252, spe: 252}, ivs: {atk: 0}, nature: 'Timid', shiny: true, + }, + 'The Immortal': { + species: 'Xurkitree', ability: 'Teravolt', item: 'Electrium Z', gender: '', + moves: ['Tail Glow', 'Freeze Dry', 'Secret Sword'], + signatureMove: 'Watt Up', + evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Timid', + }, + tiki: { + species: 'Snom', ability: 'True Grit', item: 'Eviolite', gender: 'M', + moves: ['Toxic', 'Strength Sap', 'U-turn'], + signatureMove: 'Right. On. Cue!', + evs: {hp: 128, def: 144, spd: 236}, ivs: {atk: 0}, nature: 'Bold', + }, + trace: { + species: 'Jirachi', ability: 'Trace', item: 'Leftovers', gender: '', + moves: ['Wish', 'Protect', 'Psychic'], + signatureMove: 'Hero Creation', + evs: {hp: 248, def: 8, spd: 252}, ivs: {atk: 0}, nature: 'Calm', + }, + Trickster: { + species: 'Shiinotic', ability: 'Trillionage Roots', item: 'Leftovers', gender: '', + moves: ['Strength Sap', 'Cosmic Power', 'Knock Off'], + signatureMove: 'Soul-Shattering Stare', + evs: {hp: 252, def: 252, spd: 4}, nature: 'Bold', shiny: true, + }, + Vexen: { + species: 'Tauros', ability: 'Aquila\'s Blessing', item: 'Life Orb', gender: 'M', + moves: ['Earthquake', 'Zen Headbutt', 'Rock Slide'], + signatureMove: 'Asterius Strike', + evs: {atk: 252, def: 4, spe: 252}, nature: 'Jolly', + }, + vivalospride: { + species: 'Darmanitan-Zen', ability: 'Regenerator', item: 'Heavy Duty Boots', gender: 'M', + moves: ['Teleport', 'Future Sight', 'Toxic'], + signatureMove: 'DRIP BAYLESS', + evs: {hp: 252, spa: 252, def: 4}, ivs: {atk: 0}, nature: 'Modest', + }, + Volco: { + species: 'Volcanion', ability: 'Speedrunning', item: 'Choice Scarf', + moves: ['Steam Eruption', ['Vacuum Wave', 'Secret Sword'], 'Overdrive'], + signatureMove: 'Glitch Exploiting', + evs: {hp: 4, spa: 252, spe: 252}, ivs: {atk: 0}, nature: 'Timid', gender: 'N', + }, + vooper: { + species: 'Pancham', ability: 'Qi-Gong', item: 'Eviolite', gender: 'M', + moves: ['Drain Punch', 'Knock Off', 'Swords Dance'], + signatureMove: 'Panda Express', + evs: {hp: 252, atk: 252, spd: 4}, ivs: {atk: 0}, nature: 'Adamant', + }, + xJoelituh: { + species: 'Marowak-Alola', ability: 'Mountaineer', item: 'Rare Bone', gender: 'M', + moves: ['Poltergeist', 'Fire Punch', 'Stomping Tantrum'], + signatureMove: 'Burn Bone', + evs: {hp: 248, atk: 8, spd: 252}, nature: 'Careful', shiny: true, + }, + yuki: { + species: 'Pikachu-Cosplay', ability: 'Combat Training', item: 'Light Ball', gender: 'F', + moves: ['Quick Attack'], + signatureMove: 'Class Change', + evs: {hp: 4, spa: 252, spe: 252}, ivs: {atk: 0}, nature: ['Modest', 'Timid'], + }, + 'yuki-Cleric': { + species: 'Pikachu-PhD', ability: 'Triage', item: 'Light Ball', gender: 'F', + moves: ['Parabolic Charge', 'Wish', 'Baton Pass'], + signatureMove: 'Class Change', + evs: {hp: 4, spa: 252, spe: 252}, ivs: {atk: 0}, + skip: 'yuki', + }, + 'yuki-Dancer': { + species: 'Pikachu-Pop-Star', ability: 'Dancer', item: 'Light Ball', gender: 'F', + moves: ['Fiery Dance', 'Revelation Dance', 'Lunar Dance'], + signatureMove: 'Class Change', + evs: {hp: 4, spa: 252, spe: 252}, ivs: {atk: 0}, + skip: 'yuki', + }, + 'yuki-Ninja': { + species: 'Pikachu-Libre', ability: 'White Smoke', item: 'Light Ball', gender: 'F', + moves: ['Water Shuriken', 'Acrobatics', 'Toxic'], + signatureMove: 'Class Change', + evs: {hp: 4, spa: 252, spe: 252}, ivs: {atk: 0}, + skip: 'yuki', + }, + 'yuki-Songstress': { + species: 'Pikachu-Rock-Star', ability: 'Punk Rock', item: 'Light Ball', gender: 'F', + moves: ['Hyper Voice', 'Overdrive', 'Sing'], + signatureMove: 'Class Change', + evs: {hp: 4, spa: 252, spe: 252}, ivs: {atk: 0}, + skip: 'yuki', + }, + 'yuki-Jester': { + species: 'Pikachu-Belle', ability: 'Tangled Feet', item: 'Light Ball', gender: 'F', + moves: ['Present', 'Metronome', 'Teeter Dance'], + signatureMove: 'Class Change', + evs: {hp: 4, spa: 252, spe: 252}, ivs: {atk: 0}, + skip: 'yuki', + }, + Zalm: { + species: 'Weedle', ability: 'Berserk', item: 'Sitrus Berry', gender: 'M', + moves: ['Quiver Dance', 'Belch', ['Snipe Shot', 'Power Gem']], + signatureMove: 'Ingredient Foraging', + evs: {hp: 252, spa: 252, spd: 4}, ivs: {atk: 0}, nature: 'Modest', + }, + Zarel: { + species: 'Meloetta', ability: 'Dancer', item: 'Leftovers', gender: 'N', + moves: ['Quiver Dance', 'Feather Dance', 'Lunar Dance'], + signatureMove: 'Relic Dance', + evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Modest', + }, + 'Zarel-Pirouette': { + species: 'Meloetta-Pirouette', ability: 'Serene Grace', item: 'Leftovers', gender: 'N', + moves: ['Revelation Dance', 'Fiery Dance', 'Petal Dance'], + signatureMove: 'Relic Dance', + evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Modest', + skip: 'Zarel', + }, + Zodiax: { + species: 'Oricorio-Pom-Pom', ability: 'Primordial Sea', item: 'Heavy-Duty Boots', gender: 'M', + moves: ['Quiver Dance', 'Hurricane', 'Thunder'], + signatureMove: 'Big Storm Coming', + evs: {hp: 4, spa: 252, spe: 252}, nature: 'Timid', + }, + Zyg: { + species: 'Azelf', ability: 'Magic Bounce', item: ['Life Orb', 'Expert Belt'], gender: 'M', + moves: ['Photon Geyser', 'Knock Off', ['U-turn', 'Play Rough', 'Close Combat']], + signatureMove: 'Luck of the Draw', + evs: {atk: 252, def: 4, spe: 252}, nature: 'Timid', + }, +}; +export class RandomStaffBrosTeams extends RandomTeams { randomStaffBrosTeam(options: {inBattle?: boolean} = {}) { const team: PokemonSet[] = []; - const sets: SSBSets = { - /* - // Example: - Username: { - species: 'Species', ability: 'Ability', item: 'Item', gender: '', - moves: ['Move Name', ['Move Name', 'Move Name']], - signatureMove: 'Move Name', - evs: {stat: number}, ivs: {stat: number}, nature: 'Nature', level: 100, shiny: false, - }, - // Species, ability, and item need to be captialized properly ex: Ludicolo, Swift Swim, Life Orb - // Gender can be M, F, N, or left as an empty string - // each slot in moves needs to be a string (the move name, captialized properly ex: Hydro Pump), or an array of strings (also move names) - // signatureMove also needs to be capitalized properly ex: Scripting - // You can skip Evs (defaults to 82 all) and/or Ivs (defaults to 31 all), or just skip part of the Evs (skipped evs are 0) and/or Ivs (skipped Ivs are 31) - // You can also skip shiny, defaults to false. Level can be skipped (defaults to 100). - // Nature needs to be a valid nature with the first letter capitalized ex: Modest - */ - // Please keep sets organized alphabetically based on staff member name! - '2xTheTap': { - species: 'Arcanine', ability: 'Mold Breaker', item: 'Life Orb', gender: 'M', - moves: ['Sacred Fire', 'Extreme Speed', 'Morning Sun'], - signatureMove: 'Noble Howl', - evs: {atk: 252, def: 4, spe: 252}, nature: 'Adamant', shiny: true, - }, - '5gen': { - species: 'Sawsbuck', ability: 'Season\'s Gift', item: 'Heat Rock', gender: 'M', - moves: ['Return', 'Knock Off', 'High Jump Kick'], - signatureMove: 'Too Much Saws', - evs: {atk: 252, def: 4, spe: 252}, nature: 'Jolly', - }, - Aelita: { - species: 'Porygon-Z', ability: 'Protean', item: 'Life Orb', gender: 'F', - moves: [['Boomburst', 'Moonblast'], 'Blue Flare', 'Chatter'], - signatureMove: 'Energy Field', - evs: {hp: 4, spa: 252, spe: 252}, ivs: {atk: 0}, nature: 'Modest', - }, - Aeonic: { - species: 'Nosepass', ability: 'Dummy Thicc', item: 'Noseium Z', gender: 'M', - moves: ['Stealth Rock', 'Thunder Wave', 'Milk Drink'], - signatureMove: 'Fissure', - evs: {hp: 248, def: 8, spd: 252}, nature: 'Careful', - }, - Aethernum: { - species: 'Regigigas', ability: 'Awakening', item: 'Leftovers', gender: 'N', - moves: ['Knock Off', 'Confuse Ray', 'Drain Punch'], - signatureMove: 'Cataclysm', - evs: {hp: 252, atk: 252, spe: 4}, ivs: {spa: 0}, nature: 'Adamant', - }, - Akiamara: { - species: 'Croagunk', ability: 'Toxic Swap', item: ['Life Orb', 'Black Sludge'], gender: '', - moves: [['Gunk Shot', 'Sludge Wave'], 'Taunt', 'Photon Geyser'], - signatureMove: 'x1', - evs: {hp: 252, spa: 4, spe: 252}, nature: 'Serious', - }, - Akir: { - species: 'Parasect', ability: 'Neutralizing Spores', item: 'Leftovers', gender: 'M', - moves: ['Toxic', 'Leech Life', ['Lunar Dance', 'Spore']], - signatureMove: 'Compost', - evs: {hp: 252, atk: 4, def: 252}, nature: 'Impish', - }, - Alpha: { - species: 'Espeon', ability: 'O SOLE MIO', item: 'Light Clay', gender: 'M', - moves: ['Psychic', 'Focus Blast', 'Morning Sun'], - signatureMove: 'Neko Veil', - evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Timid', - }, - Andrew: { - species: 'Quilava', ability: 'Volcanic Tempest', item: 'Eviolite', gender: 'M', - moves: ['Quiver Dance', 'Seed Flare', 'Fiery Dance'], - signatureMove: 'Back Off! GRRR!', - evs: {hp: 4, spa: 252, spe: 252}, ivs: {atk: 0}, nature: 'Timid', - }, - Anubis: { - species: 'Giratina-Origin', ability: 'Distortion World', item: 'Griseous Orb', gender: 'N', - moves: ['Shadow Force', 'Spacial Rend', 'Topsy-Turvy'], - signatureMove: 'Heretic\'s Mark', - evs: {def: 252, spa: 252, spd: 4}, ivs: {spe: 0}, shiny: true, nature: 'Relaxed', - }, - 'A Quag to The Past': { - species: 'Quagsire', ability: 'Careless', item: 'Leftovers', gender: 'M', - moves: ['Recover', 'Toxic', 'Scald'], - signatureMove: 'Murky Ambush', - evs: {hp: 252, def: 252, spd: 4}, ivs: {spe: 0}, nature: 'Relaxed', - }, - Arcticblast: { - species: 'Garbodor', ability: 'Analytic', item: 'Assault Vest', gender: 'M', - moves: ['Knock Off', 'Earthquake', ['Horn Leech', 'U-turn', 'Avalanche']], - signatureMove: 'Trashalanche', - evs: {hp: 252, atk: 252, def: 4}, ivs: {spe: 0}, nature: 'Brave', - }, - Arsenal: { - species: 'Arceus', ability: 'Logia', gender: 'M', - item: ["Draco Plate", "Dread Plate", "Earth Plate", "Fist Plate", "Flame Plate", "Icicle Plate", "Insect Plate", "Iron Plate", "Meadow Plate", "Mind Plate", "Pixie Plate", "Sky Plate", "Splash Plate", "Spooky Plate", "Stone Plate", "Toxic Plate", "Zap Plate"], - moves: ['Mimic', 'Stealth Rock', 'Memento'], - signatureMove: 'Come on you Gunners', - evs: {hp: 4, spa: 252, spe: 252}, ivs: {hp: 20, def: 20, spd: 20}, nature: 'Naive', - }, - Averardo: { - species: 'Tyrantrum', ability: 'Rock Head', item: 'Choice Scarf', gender: 'M', - moves: ['Head Smash', 'Flare Blitz', 'Photon Geyser'], - signatureMove: 'Dragon Smash', - evs: {hp: 4, atk: 252, spe: 252}, nature: 'Adamant', shiny: true, - }, - Beowulf: { - species: 'Beedrill', ability: ['Download', 'Speed Boost'], item: 'Beedrillite', gender: 'M', - moves: ['Spiky Shield', 'Gunk Shot', ['Bolt Strike', 'Diamond Storm', 'Thousand Arrows']], - signatureMove: 'Buzzing of the Swarm', - evs: {atk: 252, def: 4, spe: 252}, nature: 'Jolly', - }, - biggie: { - species: 'Snorlax', ability: 'Fur Coat', item: 'Leftovers', gender: 'M', - moves: ['Diamond Storm', 'Knock Off', ['Drain Punch', 'Precipice Blades']], - signatureMove: 'Food Rush', - evs: {hp: 4, atk: 252, spd: 252}, nature: 'Adamant', - }, - 'Birdy~!': { - species: 'Oricorio', ability: 'Arabesque', item: 'Wiki Berry', gender: 'F', - moves: ['Floaty Fall', 'Hurricane', 'Revelation Dance'], - signatureMove: 'Just Dance', - evs: {spa: 252, spd: 4, spe: 252}, nature: 'Hasty', shiny: true, - }, - bobochan: { - species: 'Emolga', ability: 'Huge Power', item: 'Choice Band', gender: 'M', - moves: ['Brave Bird', 'Knock Off', 'U-turn'], - signatureMove: 'Thousand Circuit Overload', - evs: {atk: 252, spd: 4, spe: 252}, nature: 'Jolly', - }, - Brandon: { - species: 'Shaymin-Sky', ability: 'Gracidea Mastery', item: ['Red Card', 'Life Orb', 'Grassium Z'], gender: 'N', - moves: ['Seed Flare', ['Earth Power', 'Aura Sphere', 'Psyshock'], ['Oblivion Wing', 'Synthesis']], - signatureMove: 'Blustery Winds', - evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Timid', - }, - Cake: { - species: 'Dunsparce', ability: 'Serene Grace', item: 'Leftovers', gender: 'M', - moves: ['Headbutt', 'Shadow Strike', 'Roost'], - signatureMove: 'Sparce Dance', - evs: {hp: 252, def: 4, spe: 252}, nature: 'Jolly', - }, - 'c.kilgannon': { - species: 'Yveltal', ability: 'Dark Aura', item: 'Leftovers', gender: 'N', - moves: ['Taunt', 'Oblivion Wing', 'Sucker Punch'], - signatureMove: 'Insidious Assault', - evs: {atk: 84, spa: 172, spe: 252}, nature: 'Naive', - }, - 'cant say': { - species: 'Aegislash', ability: 'Stance Change', item: ['Leftovers', 'Terrain Extender', 'Muscle Band'], gender: 'M', - moves: ['Shift Gear', 'Spectral Thief', 'Sacred Sword'], - signatureMove: 'a e s t h e t i s l a s h', - evs: {hp: 32, atk: 252, spd: 4, spe: 220}, nature: 'Jolly', - }, - Catalystic: { - species: 'Ho-Oh', ability: 'Magic Guard', item: ['Toxic Orb'], gender: 'M', - moves: ['Sacred Fire', 'Brave Bird', 'Strength Sap'], - signatureMove: 'Birb to the Rescue', - evs: {hp: 252, atk: 252, spe: 4}, nature: 'Adamant', - }, - Celestial: { - species: 'Dragonite', ability: 'Ice Absorb', item: 'Sky Plate', gender: 'M', - moves: ['Dragon Dance', 'Extreme Speed', 'Thousand Arrows'], - signatureMove: 'Hyperforce Strike', - evs: {atk: 252, spd: 4, spe: 252}, nature: 'Jolly', shiny: true, - }, - Ceteris: { - species: 'Greninja', ability: 'Protean', item: 'Expert Belt', gender: 'M', - moves: ['Dark Pulse', 'Origin Pulse', 'Gunk Shot', 'Shadow Sneak'], - signatureMove: 'Bringer of Darkness', - evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Timid', shiny: true, - }, - chaos: { - species: 'Bewear', ability: 'Fur Coat', item: 'Red Card', gender: 'M', - moves: ['Extreme Speed', 'Close Combat', 'Knock Off', ['Swords Dance', 'Recover']], - signatureMove: 'Forcewin', - evs: {atk: 252, def: 4, spe: 252}, nature: 'Adamant', - }, - Chloe: { - species: 'Tapu Fini', ability: 'Prankster', item: 'Light Clay', gender: 'F', - moves: ['Fleur Cannon', 'Parting Shot', ['Taunt', 'Topsy-Turvy']], - signatureMove: 'beskyttelsesnet', - evs: {hp: 248, def: 252, spa: 8}, ivs: {atk: 0}, nature: 'Bold', - }, - Darth: { - species: 'Tangela', ability: 'Seraphic Regeneration', item: 'Eviolite', gender: 'M', - moves: [['Knock Off', 'Power Whip'], ['Sludge Bomb', 'Toxic'], ['Recover', 'Protect']], - signatureMove: 'Leech Swap', - evs: {hp: 252, atk: 4, def: 252}, nature: 'Impish', - }, - DaWoblefet: { - species: 'Wobbuffet', ability: 'Shadow Artifice', item: 'Iapapa Berry', gender: 'M', - moves: ['Counter', 'Mirror Coat', 'Encore'], - signatureMove: 'Super Ego Inflation', - evs: {hp: 252, def: 252, spd: 4}, ivs: {spe: 0}, nature: 'Relaxed', - }, - Decem: { - species: 'Goodra', ability: 'Miracle Scale', item: 'Choice Scarf', gender: '', - moves: ['Draco Meteor', 'Fire Blast', 'Sludge Wave'], - signatureMove: 'Hit and Run', - evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Timid', - }, - deetah: { - species: 'Luxray', ability: 'Radioactive', item: 'Toxic Orb', gender: '', - moves: ['Extreme Speed', 'Icicle Crash', 'Thousand Arrows'], - signatureMove: 'Galvanized Strike', - evs: {hp: 28, atk: 244, def: 188, spe: 48}, nature: 'Adamant', shiny: true, - }, - DragonWhale: { - species: 'Garchomp', ability: 'Hustle', item: 'Groundium Z', gender: 'M', - moves: ['Earthquake', 'Dragon Rush', 'Diamond Storm'], - signatureMove: 'Earth\'s Blessing', - evs: {hp: 4, atk: 252, spe: 252}, nature: 'Jolly', - }, - 'E4 Flint': { - species: 'Steelix', ability: 'Sturdy', item: 'Magmarizer', gender: 'M', - moves: ['Sunsteel Strike', 'Thousand Arrows', ['Dragon Tail', 'Knock Off', 'Fire Lash', 'Fire Lash']], // Fire Lash listed twice for 50% chance to get it - signatureMove: 'Fang of the Fire King', - evs: {hp: 252, atk: 36, def: 100, spd: 120}, ivs: {spe: 0}, nature: 'Brave', - }, - explodingdaisies: { - species: 'Houndoom', ability: 'Flash Fire', item: 'Houndoominite', gender: 'M', - moves: ['Sludge Bomb', 'Nasty Plot', 'Dark Pulse'], - signatureMove: 'DOOM!', - evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Timid', - }, - Eien: { - species: 'Mew', ability: 'Psychic Surge', item: 'Terrain Extender', gender: 'N', - moves: ['Calm Mind', 'Psychic', 'Roost'], - signatureMove: 'Ancestral Power', - evs: {hp: 252, spd: 4, spe: 252}, nature: 'Timid', - }, - Elgino: { - species: 'Mimikyu', ability: 'Gib love pls', item: ['Mimikium Z', 'Ghostium Z', 'Fightinium Z'], gender: '', - moves: ['Spectral Thief', 'Play Rough', ['Shadow Sneak', 'Swords Dance']], - signatureMove: 'Rough Snuggle', - evs: {hp: 4, atk: 252, spe: 252}, nature: 'Adamant', shiny: true, - }, - eternally: { - species: 'Ducklett', ability: 'Primordial Sea', item: 'Eviolite', gender: 'M', - moves: ['Origin Pulse', 'Hurricane', 'Roost'], - signatureMove: 'Quack', - evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Timid', - }, - false: { - species: 'Rayquaza-Mega', ability: 'Infiltrator', item: 'Focus Band', gender: 'F', - moves: ['Celebrate'], - signatureMove: 'fr*ck', - evs: {atk: 252, spe: 252, def: 4}, nature: 'Jolly', shiny: true, - }, - fart: { - species: 'Kartana', ability: 'Rise from the Gases', item: 'Life Orb', gender: 'M', - moves: ['Iron Head', 'Play Rough', 'U-turn'], - signatureMove: 'Soup-Stealing 7-Star Strike', - evs: {atk: 252, def: 4, spe: 252}, nature: 'Jolly', shiny: true, - }, - Felucia: { - species: 'Durant', ability: 'Hustle', item: 'Choice Band', gender: 'F', - moves: ['Iron Head', ['Megahorn', 'Leech Life'], ['Wild Charge', 'Superpower']], - signatureMove: 'Quick Reload', - evs: {hp: 24, atk: 232, spe: 252}, nature: 'Jolly', - }, - FOMG: { - species: 'Golem', ability: 'Serene Grace', item: 'Astleyium Z', gender: 'M', - moves: ['Earthquake', 'Explosion', 'Iron Head'], - signatureMove: 'Rock Slide', - evs: {hp: 4, atk: 252, spe: 252}, nature: 'Jolly', - }, - 'Gallant Spear': { - species: 'Rapidash', ability: 'Trombe!', item: ['Eject Button', 'Choice Band'], gender: 'M', - moves: ['High Horsepower', 'U-turn', 'Recover'], - signatureMove: 'Storm Assault OGs', - evs: {atk: 252, spd: 4, spe: 252}, nature: 'Jolly', shiny: true, - }, - Gimm1ck: { - species: 'Beartic', ability: 'Russian Rush', item: 'Life Orb', gender: 'M', - moves: ['Icicle Crash', 'Earthquake', 'Close Combat'], - signatureMove: 'Slav Squat', - evs: {atk: 252, def: 4, spe: 252}, ivs: {spa: 0}, nature: 'Jolly', - }, - GMars: { - species: 'Minior-Meteor', ability: 'Mystery Shell', item: 'White Herb', gender: 'N', - moves: ['Acrobatics', 'Earthquake', 'Stone Edge'], - signatureMove: 'Taste the Rainbow', - evs: {hp: 68, atk: 252, spe: 188}, nature: 'Adamant', - }, - grimAuxiliatrix: { - species: 'Aggron', ability: 'Sturdy', item: 'Aggronite', gender: '', - moves: [['Toxic', 'Stealth Rock'], ['Stone Edge', 'Heat Crash'], 'Earthquake'], - signatureMove: 'Pain Train', - evs: {hp: 252, def: 4, spd: 252}, nature: 'Careful', - }, - guishark: { - species: 'Sharpedo', ability: 'Speed Boost', item: 'Sharpedonite', gender: 'M', - moves: ['Crabhammer', 'Icicle Crash', 'Protect'], - signatureMove: 'Dad Joke', - evs: {hp: 4, atk: 252, spe: 252}, nature: 'Jolly', - - }, - Hippopotas: { - species: 'Hippopotas', ability: 'Regenerator', item: 'Eviolite', gender: 'N', - moves: ['Stealth Rock', 'Spikes', 'Toxic Spikes', 'Sticky Web'], - signatureMove: 'Hazard Pass', - evs: {hp: 252, def: 252, spd: 4}, ivs: {atk: 0, spa: 0}, nature: 'Bold', - }, - HoeenHero: { - species: 'Ludicolo', ability: 'Scripter', item: 'Terrain Extender', gender: 'M', - moves: [['Hydro Pump', 'Scald'], 'Giga Drain', 'Ice Beam'], - signatureMove: 'Scripted Terrain', - evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Modest', - }, - Hubriz: { - species: 'Roserade', ability: 'Merciless', item: 'Focus Sash', gender: 'F', - moves: ['Toxic Spikes', 'Fleur Cannon', 'Sludge Bomb'], - signatureMove: 'Flower Tornado', - evs: {def: 4, spa: 252, spe: 252}, ivs: {atk: 0}, nature: 'Timid', - }, - inactive: { - species: 'Dusknoir', ability: 'Soul Eater', item: 'Dusknoirium Z', gender: '', - moves: ['Earthquake', 'Shadow Force', 'Shadow Sneak'], - signatureMove: 'Mean Look', - evs: {hp: 4, atk: 252, spe: 252}, nature: 'Jolly', - }, - Instruct: { - species: 'Magearna-Original', ability: 'Numb Numb Juice', item: 'Leftovers', gender: 'N', - moves: ['Sparkly Swirl', 'Defog', ['Toxic', 'Thunder Wave']], - signatureMove: 'Express Yourself', - evs: {hp: 252, def: 164, spd: 92}, ivs: {atk: 0}, nature: 'Calm', shiny: true, - }, - irritated: { - species: 'Jirachi', ability: 'Serene Grace', item: 'Leftovers', gender: 'F', - moves: ['Double Iron Bash', 'Rock Slide', 'Icicle Crash', 'Zing Zap'], - signatureMove: 'Pure Skill', - evs: {atk: 252, spd: 4, spe: 252}, nature: 'Jolly', - }, - Iyarito: { - species: 'Vaporeon', ability: 'Poison Heal', item: 'Leftovers', gender: 'F', - moves: ['Scald', 'Recover', 'Toxic'], - signatureMove: 'Rosa Rosa', - evs: {hp: 252, def: 220, spd: 36}, nature: 'Bold', shiny: true, - }, - 'Kaiju Bunny': { - species: 'Gligar', ability: 'Immunity', item: 'Eviolite', gender: 'F', - moves: ['Earthquake', 'Roost', ['Toxic', 'Stealth Rock']], - signatureMove: 'Bestial Strike', - evs: {hp: 252, def: 156, spd: 100}, nature: 'Impish', - }, - Kalalokki: { - species: 'Wingull', ability: 'Swift Swim', item: ['Waterium Z', 'Flyinium Z', 'Electrium Z'], gender: 'M', - moves: ['Water Spout', 'Hurricane', 'Thunder'], - signatureMove: 'Maelström', - evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Modest', - }, - kaori: { - species: 'Bellossom', ability: 'Flower Shield', item: 'Heat Rock', gender: 'F', - moves: ['Solarbeam', 'Fusion Flare', 'Moonlight'], - signatureMove: '>w<', - evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Modest', shiny: true, - }, - kay: { - species: 'Inkay', ability: 'Contrary', item: 'Eviolite', gender: 'M', - moves: ['Power Trip', 'Rest', 'Sleep Talk'], - signatureMove: 'Inkzooka', - evs: {hp: 252, atk: 144, spe: 112}, nature: 'Adamant', - }, - Kie: { - species: 'Totodile', ability: 'Maelstrom', item: 'Eviolite', gender: 'M', - moves: ['Aqua Jet', 'Icicle Crash', 'Horn Leech'], - signatureMove: 'Chaotic', - evs: {atk: 252, def: 4, spe: 252}, nature: 'Jolly', shiny: true, - }, - KingSwordYT: { - species: 'Pangoro', ability: 'Kung Fu Panda', item: 'Life Orb', gender: 'M', - moves: ['Ice Punch', 'Bullet Punch', 'Knock Off'], - signatureMove: 'Dragon Warrior Touch', - evs: {atk: 252, hp: 4, spe: 252}, nature: 'Jolly', - }, - Kipkluif: { - species: 'Combusken', ability: 'Speed Boost', item: 'Eviolite', gender: 'M', - moves: ['High Jump Kick', 'Blaze Kick', 'Trop Kick'], - signatureMove: 'Salute the Colonel', - evs: {hp: 252, atk: 252}, nature: 'Adamant', - }, - Kris: { - species: 'Rotom', ability: 'Adaptability', item: 'Thunder Stone', gender: 'N', - moves: [['Defog', 'Will-O-Wisp', 'Charge'], 'Thunderbolt', ['Recover', 'Volt Switch']], - signatureMove: 'Ectoplasm', - evs: {hp: 4, spa: 252, spe: 252}, nature: 'Timid', - }, - 'Level 51': { - species: 'Porygon2', ability: 'Stamina', item: 'Eviolite', gender: 'N', - moves: ['Recover', ['Seismic Toss', 'Night Shade'], 'Cosmic Power'], - signatureMove: 'Next Level Strats', - evs: {hp: 236, def: 220, spd: 48, spe: 4}, ivs: {atk: 0}, nature: 'Calm', - }, - LifeisDANK: { - species: 'Delibird', ability: 'Aerilate', item: 'Focus Sash', gender: 'F', - moves: ['Ice Shard', 'Return', 'Explosion'], - signatureMove: 'Bar Fight', - evs: {hp: 4, atk: 252, spe: 252}, nature: 'Jolly', - }, - 'Lost Seso': { - species: 'Typhlosion', ability: 'Dazzling', item: 'Firium Z', gender: 'M', - moves: ['Calm Mind', 'Petal Dance', 'Fiery Dance'], - signatureMove: 'Shuffle Ramen Dance', - evs: {def: 4, spa: 252, spe: 252}, ivs: {atk: 0}, nature: 'Timid', shiny: true, - }, - MacChaeger: { - species: 'Mantyke', ability: 'Water Veil', item: ['Life Orb', 'Normalium Z'], gender: 'F', - moves: ['Scald', 'Aeroblast', 'Sleep Talk'], - signatureMove: 'Nap Time', - evs: {hp: 252, spa: 80, spe: 176}, ivs: {atk: 0}, nature: 'Modest', - }, - 'Mad Monty ¾°': { - species: 'Zekrom', ability: 'Minnesnowta', item: 'Expert Belt', gender: 'N', - moves: ['Fusion Bolt', 'Avalanche', 'Hone Claws'], - signatureMove: 'Llamacide', - evs: {atk: 252, spa: 4, spe: 252}, ivs: {def: 0}, nature: 'Lonely', shiny: true, - }, - MajorBowman: { - species: 'Victini', ability: 'Victory Star', item: 'Victinium Z', gender: 'M', - moves: ['Bolt Strike', 'Zen Headbutt', 'U-turn'], - signatureMove: 'V-create', - evs: {hp: 252, atk: 4, spe: 252}, nature: 'Jolly', - }, - Marshmallon: { - species: 'Castform', ability: 'Sightseeing', item: 'Shell Bell', gender: 'M', - moves: ['Rain Dance', 'Sunny Day', 'Hail'], - signatureMove: 'Weather Forecast', - evs: {spa: 252, spd: 4, spe: 252}, nature: 'Modest', - }, - martha: { - species: 'Diancie', ability: 'Pixilate', item: 'Diancite', gender: 'F', - moves: ['Hyper Voice', ['Fire Blast', 'Earth Power'], 'Photon Geyser'], - signatureMove: 'Crystal Boost', - evs: {hp: 4, spa: 252, spe: 252}, ivs: {atk: 0}, nature: 'Timid', - }, - Marty: { - species: 'Silvally', ability: 'RKS System', item: 'Normal Gem', gender: 'N', - moves: ['Parting Shot', 'Explosion', 'Extreme Speed'], - signatureMove: 'Type Analysis', - evs: {hp: 4, atk: 252, spe: 252}, nature: 'Jolly', - }, - Meicoo: { - species: 'Pidgeot', ability: 'Prankster', item: 'Pidgeotite', gender: 'M', - moves: ['Hurricane', 'Inferno', 'Roost'], - signatureMove: '/scavenges u', - evs: {def: 4, spa: 252, spe: 252}, ivs: {atk: 0}, nature: 'Timid', - }, - Megazard: { - species: 'Exeggutor-Alola', ability: 'Stand Up Tall', item: 'Leftovers', gender: 'M', - moves: ['Strength Sap', 'Growth', 'Stockpile'], - signatureMove: 'Tipping Over', - evs: {hp: 252, atk: 252, def: 4}, ivs: {spe: 0}, nature: 'Adamant', - }, - Mitsuki: { - species: 'Serperior', ability: 'Contrary', item: 'Leftovers', gender: 'M', - moves: ['Leech Seed', 'Substitute', ['Earth Power', 'Flamethrower', 'Glare']], - signatureMove: 'Python Ivy', - evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Timid', - }, - 'Morfent ( _̀> ̀)': { - species: 'Banette', ability: 'Intimidate', item: 'Banettite', gender: 'M', - moves: ['Shadow Sneak', 'Spectral Thief', 'Drain Punch'], - signatureMove: 'E', - evs: {hp: 184, atk: 252, def: 68, spd: 4}, ivs: {spe: 0}, nature: 'Brave', - }, - nui: { - species: 'Milotic', ability: 'Prismatic Surge', item: 'Waterium Z', gender: 'N', - moves: ['Steam Eruption', 'Toxic', 'Recover'], - signatureMove: 'Pyramiding Song', - evs: {hp: 252, def: 252, spd: 4}, ivs: {atk: 0}, nature: 'Bold', shiny: true, - }, - OM: { - species: 'Flareon', ability: 'Pixilate', item: 'Metronome', gender: 'M', - moves: ['Leaf Blade', 'Thousand Arrows', 'Extreme Speed'], - signatureMove: 'OM Boom', - evs: {atk: 252, def: 4, spe: 252}, nature: 'Adamant', - }, - Overneat: { - species: 'Absol', ability: 'Intimidate', item: 'Absolite', gender: 'M', - moves: ['Play Rough', 'Close Combat', 'Extreme Speed'], - signatureMove: 'Ultimate Slash', - evs: {atk: 252, def: 4, spe: 252}, nature: 'Jolly', - }, - Pablo: { - species: 'Blastoise', ability: 'Torrent', item: 'Blastoisinite', gender: 'M', - moves: ['Muddy Water', 'Ice Beam', 'Slack Off'], - signatureMove: 'Jail Shell', - evs: {hp: 252, def: 4, spa: 252}, nature: 'Modest', shiny: true, - }, - Paradise: { - species: 'Muk', ability: 'Unaware', item: 'Black Sludge', gender: '', - moves: ['Wish', 'Knock Off', 'Protect'], - signatureMove: 'Corrosive Toxic', - evs: {hp: 252, def: 4, spd: 252}, nature: 'Careful', - }, - 'Pirate Princess': { - species: 'Gulpin', ability: 'Acid Rain', item: 'Eviolite', gender: 'F', - moves: ['Cosmic Power', 'Venom Drench', 'Leech Seed'], - signatureMove: 'Tea Break', - evs: {hp: 252, def: 236, spd: 20}, nature: 'Bold', shiny: true, - }, - pluviometer: { - species: 'Mismagius', ability: 'Sheer Force', item: 'Life Orb', gender: '', - moves: ['Taunt', 'Moonblast', ['Mystical Fire', 'Earth Power']], - signatureMove: 'Grammar Hammer', - evs: {hp: 4, spa: 252, spe: 252}, ivs: {atk: 0}, nature: 'Timid', - }, - Pohjis: { - species: 'Marowak', ability: 'Huge Power', item: 'Marowakium Z', gender: '', - moves: ['Fire Punch', 'Knock Off', 'Trick Room'], - signatureMove: 'Earthquake', - evs: {hp: 252, atk: 252, spd: 4}, ivs: {spe: 0}, nature: 'Brave', - }, - pre: { - species: 'Deoxys', ability: 'Optimize', item: 'Rocky Helmet', gender: 'N', - moves: ['Psycho Boost', 'Recover', 'Extreme Speed'], - signatureMove: 'Refactor', - evs: {hp: 252, def: 4, spd: 252}, ivs: {atk: 0}, nature: 'Bold', shiny: 64, - }, - ptoad: { - species: 'Politoed', ability: 'Fat Rain', item: 'Damp Rock', gender: 'M', - moves: ['Scald', 'Toxic', 'Ice Beam'], - signatureMove: 'Lilypad Shield', - evs: {hp: 252, def: 60, spd: 196}, ivs: {atk: 0}, nature: 'Calm', - }, - Psynergy: { - species: 'Blaziken', ability: 'Wrath', item: 'Blazikenite', gender: 'M', - moves: ['Flare Blitz', ['High Jump Kick', 'Superpower'], ['Wild Charge', 'Thunder Punch']], - signatureMove: 'Resolve', - evs: {atk: 252, spd: 4, spe: 252}, nature: 'Jolly', - }, - 'Quite Quiet': { - species: 'Misdreavus', ability: 'Levitate', item: 'Leftovers', gender: 'F', - moves: [['Moongeist Beam', 'Shadow Ball', 'Night Shade'], 'Recover', ['Flatter', 'Swagger']], - signatureMove: 'Literally Cheating', - evs: {hp: 252, def: 4, spe: 252}, ivs: {atk: 0}, nature: 'Timid', - }, - Rach: { - species: 'Pikachu-Libre', ability: 'Huge Power', item: 'Fightinium Z', gender: 'F', - moves: ['Flying Press', 'Throat Chop', 'Thousand Arrows'], - signatureMove: 'Stunner', - evs: {hp: 4, atk: 252, spe: 252}, nature: 'Jolly', - }, - Rage: { - species: 'Salamence', ability: 'Aerilate', item: 'Leftovers', gender: 'M', - moves: ['Roost', 'Thousand Arrows', 'Double-Edge'], - signatureMove: 'Rageeeee', - evs: {atk: 252, spd: 4, spe: 252}, nature: 'Jolly', - }, - Ransei: { - species: 'Audino-Mega', ability: 'Superguarda', item: 'Safety Goggles', gender: 'M', - moves: ['Photon Geyser', 'Sacred Fire', 'Play Rough'], - signatureMove: 'Mashup Motive', - evs: {hp: 248, atk: 252, spe: 8}, nature: 'Adamant', - }, - 'Rory Mercury': { - species: 'Charjabug', ability: 'Recharge', item: 'Eviolite', gender: 'M', - moves: ['First Impression', ['Leech Life', 'U-Turn'], 'Bolt Strike'], - signatureMove: 'Switch Off', - evs: {hp: 252, atk: 252, def: 4}, ivs: {spe: 0}, nature: 'Brave', - }, - SamJo: { - species: 'Mamoswine', ability: 'Thiccer Fat', item: 'Thiccinium Z', gender: '', - moves: ['Icicle Crash', 'Precipice Blades', ['Ice Shard', 'Superpower', 'Stone Edge', 'Knock Off']], - signatureMove: 'Thicc', - evs: {atk: 252, def: 4, spe: 252}, nature: 'Jolly', - }, - Schiavetto: { - species: 'Scolipede', ability: 'RVS', item: 'Mariahcarium Z', gender: 'M', - moves: ['Fell Stinger', 'Diamond Storm', ['Hammer Arm', 'Ice Hammer', 'Baneful Bunker']], - signatureMove: 'Poison Jab', - evs: {hp: 4, atk: 252, spe: 252}, nature: 'Jolly', - }, - Scotteh: { - species: 'Suicune', ability: 'Fur Coat', item: 'Leftovers', gender: 'M', - moves: ['Nasty Plot', 'Ice Beam', 'Scald', 'Recover'], - signatureMove: 'Geomagnetic Storm', - evs: {def: 252, spa: 4, spe: 252}, nature: 'Bold', - }, - Shiba: { - species: 'Fletchinder', ability: 'Gale Wings v1', item: 'Eviolite', gender: 'F', - moves: ['Dragon Ascent', 'Sacred Fire', 'Roost'], - signatureMove: 'GO INDA', - evs: {hp: 248, atk: 252, spe: 8}, nature: 'Adamant', - }, - Slowbroth: { - species: 'Beheeyem', ability: 'Psychic Surge', item: 'Psychium Z', gender: 'M', - moves: ['Nasty Plot', 'Psystrike', ['Aura Sphere', 'Earth Power', 'Shadow Ball']], - signatureMove: 'Alien Wave', - evs: {hp: 252, spa: 252, spd: 4}, ivs: {atk: 0, spe: 0}, nature: 'Quiet', - }, - Snaquaza: { - species: 'Honchkrow', ability: 'Illusion', item: 'Fakeclaimium Z', gender: 'M', - moves: ['Superpower', 'Sucker Punch', ['Flamethrower', 'Ice Beam', 'Thunderbolt']], - signatureMove: 'Brave Bird', - evs: {atk: 128, spa: 128, spe: 252}, nature: 'Naive', - }, - sparksblade: { - species: 'Blacephalon', ability: 'Magic Guard', item: 'Life Orb', gender: 'N', - moves: ['Mind Blown', 'Shadow Ball', 'Explosion'], - signatureMove: 'Kratosmana', - evs: {hp: 4, spa: 252, spe: 252}, nature: 'Naive', shiny: true, - }, - Sundar: { - species: 'Sceptile', ability: 'Overgrow', item: 'Sceptilite', gender: 'M', - moves: ['Sludge Wave', 'Draco Meteor', 'Hidden Power Fighting'], - signatureMove: 'Leaf Blaster', - evs: {def: 4, spa: 252, spe: 252}, nature: 'Serious', shiny: true, - }, - Teclis: { - species: 'Darkrai', ability: 'Dark Aura', item: 'Darkrainium Z', gender: 'N', - moves: ['Dark Pulse', 'Destiny Bond', 'Earth Power'], - signatureMove: 'Dark Void', - evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Timid', - }, - tennisace: { - species: 'Raikou', ability: 'Levitate', item: 'Life Orb', gender: 'M', - moves: ['Volt Switch', 'Ice Beam', ['Aura Sphere', 'Shadow Ball']], - signatureMove: 'Ground Surge', - evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Timid', - }, - Teremiare: { - species: 'Zorua', ability: 'Not Prankster', item: 'Eject Button', gender: 'N', - moves: ['Encore', 'Taunt', 'Lunar Dance'], - signatureMove: 'Rotate', - evs: {hp: 252, def: 136, spd: 120}, ivs: {atk: 0}, nature: 'Bold', shiny: true, - }, - 'The Immortal': { - species: 'Buzzwole', ability: 'Beast Boost 2', item: ['Buzznium Z', 'Choice Scarf'], gender: 'M', - moves: ['Leech Life', 'Plasma Fists', 'Ice Punch'], - signatureMove: 'Drain Punch', - evs: {hp: 4, atk: 252, spe: 252}, nature: 'Jolly', - }, - 'The Leprechaun': { - species: 'Bronzong', ability: 'Steelworker', item: 'Life Orb', gender: 'N', - moves: ['Photon Geyser', 'Precipice Blades', 'Diamond Storm'], - signatureMove: 'Gyro Ballin\'', - evs: {hp: 252, atk: 252, def: 4}, ivs: {spe: 0}, nature: 'Brave', shiny: true, - }, - Tony: { - species: 'Flygon', ability: 'Magic Bounce', item: 'Leftovers', gender: 'M', - moves: ['Thousand Arrows', 'Dragon Tail', 'Roost'], - signatureMove: 'Greed', - evs: {hp: 252, def: 40, spe: 216}, nature: 'Jolly', shiny: true, - }, - torkool: { - species: 'Torkoal', ability: 'Deflective Shell', item: 'Leftovers', gender: 'M', - moves: ['Morning Sun', ['Lava Plume', 'Magma Storm'], 'Toxic'], - signatureMove: 'Smoke Bomb', - evs: {hp: 248, spa: 8, spd: 252}, nature: 'Calm', - }, - Trickster: { - species: 'Hoopa', ability: 'Interdimensional', item: 'Life Orb', gender: 'M', - moves: ['Inferno', 'Zap Cannon', ['Roost', 'Grass Whistle']], - signatureMove: 'Mini Singularity', - evs: {hp: 4, spa: 252, spe: 252}, ivs: {atk: 0}, nature: 'Timid', - }, - vivalospride: { - species: 'Araquanid', ability: 'TRASH VIV WEBS', item: 'Wave Incense', gender: 'M', - moves: ['Liquidation', 'U-turn', 'Toxic'], - signatureMove: 'CEILINGS ABSENT', - evs: {hp: 64, atk: 252, spe: 192}, nature: 'Adamant', - }, - Volco: { - species: 'Volcanion', ability: 'Unaware', item: 'Assault Vest', gender: 'M', - moves: ["Steam Eruption", "Giga Drain", ["Ice Beam", "Ice Beam", "Earth Power"]], - signatureMove: 'Explosive Drain', - evs: {hp: 248, spa: 252, spd: 8}, ivs: {atk: 0}, nature: 'Modest', - }, - Xayah: { - species: 'Noivern', ability: 'Dancer', item: 'Flyinium Z', gender: 'F', - moves: ['Clanging Scales', 'Roost', 'Fiery Dance'], - signatureMove: 'Feather Snare', - evs: {spa: 252, spd: 4, spe: 252}, nature: 'Timid', - }, - xfix: { - species: 'Xatu', ability: 'Magic Bounce', item: 'Focus Sash', gender: 'M', - moves: ['Substitute', ['Roost', 'Strength Sap'], 'Thunder Wave'], - signatureMove: 'glitzer popping', - evs: {hp: 8, def: 252, spd: 248}, nature: 'Calm', - }, - xJoelituh: { - species: 'Marowak-Alola', ability: 'Club Expertise', item: 'Thick Club', gender: 'M', - moves: ['Shadow Bone', 'Bonemerang', 'Drain Punch'], - signatureMove: 'Lava Bone', - evs: {atk: 252, def: 4, spe: 252}, ivs: {spa: 0}, nature: 'Jolly', shiny: true, - }, - 'XpRienzo ☑◡☑': { - species: 'Reshiram', ability: 'Turboblaze', item: 'Charcoal', gender: 'M', - moves: ['Core Enforcer', 'Volt Switch', 'Psystrike'], - signatureMove: 'Blue Flare', - evs: {spa: 252, spd: 4, spe: 252}, ivs: {atk: 0}, nature: 'Timid', - }, - Yuki: { - species: 'Ninetales-Alola', ability: 'Snow Storm', item: 'Focus Sash', gender: 'F', - moves: ['Blizzard', 'Moonblast', 'Aurora Veil'], - signatureMove: 'Cutie Escape', - evs: {hp: 4, spa: 252, spe: 252}, nature: 'Timid', - }, - Zalm: { - species: 'Weedle', ability: 'Triage', item: 'Black Sludge', gender: 'M', - moves: ['Swords Dance', 'Leech Life', 'Horn Leech'], - signatureMove: 'TwinWeedle', - evs: {atk: 252, def: 128, spd: 128}, nature: 'Adamant', - }, - Zarel: { - species: 'Meloetta', ability: 'Serene Grace', item: '', gender: 'M', - moves: ['Lunar Dance', 'Fiery Dance', 'Perish Song', 'Petal Dance', 'Quiver Dance'], - signatureMove: 'Relic Song Dance', - evs: {hp: 4, atk: 252, spa: 252}, nature: 'Quiet', - }, - Zyg: { - species: 'Zygarde', ability: 'Poison Heal', item: 'Leftovers', gender: 'M', - moves: ['Thousand Arrows', 'Stone Edge', 'Coil'], - signatureMove: 'The Life of Zyg', - evs: {hp: 188, atk: 68, def: 252}, nature: 'Adamant', - }, - }; - const pool = Object.keys(sets); + const debug: string[] = []; // Set this to a list of SSB sets to override the normal pool for debugging. + const pool = debug.length ? debug : Object.keys(ssbSets); const typePool: {[k: string]: number} = {}; let depth = 0; - if (options.inBattle) this.allXfix = false; while (pool.length && team.length < 6) { if (depth >= 200) throw new Error(`Infinite loop in Super Staff Bros team generation.`); depth++; - const name = this.allXfix ? 'xfix' : this.sampleNoReplace(pool); - const ssbSet = sets[name]; + const name = this.sampleNoReplace(pool); + const ssbSet: SSBSet = this.dex.deepClone(ssbSets[name]); + if (ssbSet.skip) continue; - if (!this.allXfix) { - // Enforce typing limits + // Enforce typing limits + if (!debug.length) { // Type limits are ignored when debugging const types = this.dex.getSpecies(ssbSet.species).types; let rejected = false; for (const type of types) { @@ -765,57 +893,50 @@ export class RandomStaffBrosTeams extends RandomTeams { break; } } + if (ssbSet.ability === 'Wonder Guard') { + if (!typePool['wonderguard']) { + typePool['wonderguard'] = 1; + } else { + rejected = true; + } + } if (rejected) continue; // Update type counts for (const type of types) { typePool[type]++; } } + const set: PokemonSet = { name: name, species: ssbSet.species, item: Array.isArray(ssbSet.item) ? this.sampleNoReplace(ssbSet.item) : ssbSet.item, ability: Array.isArray(ssbSet.ability) ? this.sampleNoReplace(ssbSet.ability) : ssbSet.ability, moves: [], - nature: Array.isArray(ssbSet.nature) ? this.sampleNoReplace(ssbSet.nature) : ssbSet.nature, - gender: ssbSet.gender, - evs: {hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0}, - ivs: {hp: 31, atk: 31, def: 31, spa: 31, spd: 31, spe: 31}, + nature: ssbSet.nature ? Array.isArray(ssbSet.nature) ? this.sampleNoReplace(ssbSet.nature) : ssbSet.nature : 'Serious', + gender: ssbSet.gender || this.sample(['M', 'F', 'N']), + evs: ssbSet.evs ? {...{hp: 0, atk: 0, def: 0, spa: 0, spd: 0, spe: 0}, ...ssbSet.evs} : + {hp: 84, atk: 84, def: 84, spa: 84, spd: 84, spe: 84}, + ivs: {...{hp: 31, atk: 31, def: 31, spa: 31, spd: 31, spe: 31}, ...ssbSet.ivs}, level: ssbSet.level || 100, happiness: typeof ssbSet.happiness === 'number' ? ssbSet.happiness : 255, - shiny: typeof ssbSet.shiny === 'number' ? this.randomChance(1, ssbSet.shiny) : ssbSet.shiny, + shiny: typeof ssbSet.shiny === 'number' ? this.randomChance(1, ssbSet.shiny) : !!ssbSet.shiny, }; - if (ssbSet.ivs) { - let iv: StatName; - for (iv in ssbSet.ivs) { - // IVs from the set override the default of 31, assume the hardcoded IVs are legal - set.ivs[iv] = ssbSet.ivs[iv]!; - } - } - if (ssbSet.evs) { - let ev: StatName; - for (ev in ssbSet.evs) { - // EVs from the set override the default of 0, assume the hardcoded EVs are legal - set.evs[ev] = ssbSet.evs[ev]!; - } - } else { - set.evs = {hp: 84, atk: 84, def: 84, spa: 84, spd: 84, spe: 84}; - } while (set.moves.length < 3 && ssbSet.moves.length > 0) { let move = this.sampleNoReplace(ssbSet.moves); if (Array.isArray(move)) move = this.sampleNoReplace(move); set.moves.push(move); } set.moves.push(ssbSet.signatureMove); - if (name === 'Arsenal' && this.dex.getItem(set.item).onPlate) { - set.species = 'Arceus-' + this.dex.getItem(set.item).onPlate; - } - if (name === 'Gallant Spear' && set.item === 'Choice Band') set.moves[set.moves.indexOf('Recover')] = 'Aqua Tail'; - if (name === 'The Immortal' && set.item === 'Choice Scarf') set.moves[3] = 'Superpower'; - if (name === 'irritated' && !set.moves.includes('Double Iron Bash')) set.moves[this.random(3)] = 'Double Iron Bash'; + + // Any set specific tweaks occur here. + if (set.name === 'Marshmallon' && !set.moves.includes('Head Charge')) set.moves[this.random(3)] = 'Head Charge'; + team.push(set); - // Swap last and second to last sets if last set has an illusion variant - if (team.length === 6 && set.ability.includes("Illusion")) { + + // Team specific tweaks occur here + // Swap last and second to last sets if last set has Illusion + if (team.length === 6 && set.ability === 'Illusion') { team[5] = team[4]; team[4] = set; } diff --git a/data/mods/ssb/scripts.ts b/data/mods/ssb/scripts.ts index 2bacf10dac..dcf2ea3f2c 100644 --- a/data/mods/ssb/scripts.ts +++ b/data/mods/ssb/scripts.ts @@ -1,11 +1,115 @@ export const Scripts: ModdedBattleScriptsData = { - inherit: 'gen7', - runMove(moveOrMoveName, pokemon, targetLoc, sourceEffect, zMove, externalMove) { + inherit: 'gen8', + // 1 mega per pokemon + runMegaEvo(pokemon) { + if (pokemon.name === 'Struchni' && pokemon.species.name === 'Aggron') pokemon.canMegaEvo = 'Aggron-Mega'; + if (pokemon.name === 'Raj.Shoot' && pokemon.species.name === 'Charizard') pokemon.canMegaEvo = 'Charizard-Mega-X'; + const speciesid = pokemon.canMegaEvo || pokemon.canUltraBurst; + if (!speciesid) return false; + const side = pokemon.side; + + // Pokémon affected by Sky Drop cannot mega evolve. Enforce it here for now. + for (const foeActive of side.foe.active) { + if (foeActive.volatiles['skydrop'] && foeActive.volatiles['skydrop'].source === pokemon) { + return false; + } + } + + pokemon.formeChange(speciesid, pokemon.getItem(), true); + if (pokemon.canMegaEvo) { + pokemon.canMegaEvo = null; + } else { + pokemon.canUltraBurst = null; + } + + this.runEvent('AfterMega', pokemon); + + if (['Kaiju Bunny', 'Overneat', 'EpicNikolai'].includes(pokemon.name) && !pokemon.illusion) { + this.add('-start', pokemon, 'typechange', pokemon.types.join('/')); + } + + return true; + }, + + // Modded for Mega Rayquaza + canMegaEvo(pokemon) { + const species = pokemon.baseSpecies; + const altForme = species.otherFormes && this.dex.getSpecies(species.otherFormes[0]); + const item = pokemon.getItem(); + // Mega Rayquaza + if (altForme?.isMega && altForme?.requiredMove && + pokemon.baseMoves.includes(this.toID(altForme.requiredMove)) && !item.zMove) { + return altForme.name; + } + // a hacked-in Megazard X can mega evolve into Megazard Y, but not into Megazard X + if (item.megaEvolves === species.baseSpecies && item.megaStone !== species.name) { + return item.megaStone; + } + return null; + }, + + // 1 Z per pokemon + canZMove(pokemon) { + if (pokemon.m.zMoveUsed || + (pokemon.transformed && + (pokemon.species.isMega || pokemon.species.isPrimal || pokemon.species.forme === "Ultra")) + ) return; + const item = pokemon.getItem(); + if (!item.zMove) return; + if (item.itemUser && !item.itemUser.includes(pokemon.species.name)) return; + let atLeastOne = false; + let mustStruggle = true; + const zMoves: ZMoveOptions = []; + for (const moveSlot of pokemon.moveSlots) { + if (moveSlot.pp <= 0) { + zMoves.push(null); + continue; + } + if (!moveSlot.disabled) { + mustStruggle = false; + } + const move = this.dex.getMove(moveSlot.move); + let zMoveName = this.getZMove(move, pokemon, true) || ''; + if (zMoveName) { + const zMove = this.dex.getMove(zMoveName); + if (!zMove.isZ && zMove.category === 'Status') zMoveName = "Z-" + zMoveName; + zMoves.push({move: zMoveName, target: zMove.target}); + } else { + zMoves.push(null); + } + if (zMoveName) atLeastOne = true; + } + if (atLeastOne && !mustStruggle) return zMoves; + }, + + getZMove(move, pokemon, skipChecks) { + const item = pokemon.getItem(); + if (!skipChecks) { + if (pokemon.m.zMoveUsed) return; + if (!item.zMove) return; + if (item.itemUser && !item.itemUser.includes(pokemon.species.name)) return; + const moveData = pokemon.getMoveData(move); + // Draining the PP of the base move prevents the corresponding Z-move from being used. + if (!moveData?.pp) return; + } + + if (move.name === item.zMoveFrom) { + return item.zMove as string; + } else if (item.zMove === true && move.type === item.zMoveType) { + if (move.category === "Status") { + return move.name; + } else if (move.zMove?.basePower) { + return this.zMoveTable[move.type]; + } + } + }, + + runMove(moveOrMoveName, pokemon, targetLoc, sourceEffect, zMove, externalMove, maxMove, originalTarget) { pokemon.activeMoveActions++; - let target = this.getTarget(pokemon, zMove || moveOrMoveName, targetLoc); + let target = this.getTarget(pokemon, maxMove || zMove || moveOrMoveName, targetLoc, originalTarget); let baseMove = this.dex.getActiveMove(moveOrMoveName); const pranksterBoosted = baseMove.pranksterBoosted; - if (!sourceEffect && baseMove.id !== 'struggle' && !zMove) { + if (baseMove.id !== 'struggle' && !zMove && !maxMove && !externalMove) { const changedMove = this.runEvent('OverrideAction', pokemon, target, baseMove); if (changedMove && changedMove !== true) { baseMove = this.dex.getActiveMove(changedMove); @@ -13,7 +117,12 @@ export const Scripts: ModdedBattleScriptsData = { target = this.getRandomTarget(pokemon, baseMove); } } - let move = zMove ? this.getActiveZMove(baseMove, pokemon) : baseMove; + let move = baseMove; + if (zMove) { + move = this.getActiveZMove(baseMove, pokemon); + } else if (maxMove) { + move = this.getActiveMaxMove(baseMove, pokemon); + } move.isExternal = externalMove; @@ -75,9 +184,10 @@ export const Scripts: ModdedBattleScriptsData = { this.singleEvent('End', this.dex.getAbility('Illusion'), pokemon.abilityData, pokemon); } this.add('-zpower', pokemon); + // In SSB Z-Moves are limited to 1 per pokemon. pokemon.m.zMoveUsed = true; } - const moveDidSomething = this.useMove(baseMove, pokemon, target, sourceEffect, zMove); + const moveDidSomething = this.useMove(baseMove, pokemon, target, sourceEffect, zMove, maxMove); if (this.activeMove) move = this.activeMove; this.singleEvent('AfterMove', move, null, pokemon, target, move); this.runEvent('AfterMove', pokemon, target, move); @@ -100,309 +210,635 @@ export const Scripts: ModdedBattleScriptsData = { ); for (const dancer of dancers) { if (this.faintMessages()) break; + if (dancer.fainted) continue; this.add('-activate', dancer, 'ability: Dancer'); - this.runMove(move.id, dancer, 0, this.dex.getAbility('dancer'), undefined, true); + // @ts-ignore - the Dancer ability can't trigger on a move where target is null because it does not copy failed moves. + const dancersTarget = target.side !== dancer.side && pokemon.side === dancer.side ? target : pokemon; + // @ts-ignore + this.runMove(move.id, dancer, this.getTargetLoc(dancersTarget, dancer), this.dex.getAbility('dancer'), undefined, true); } } if (noLock && pokemon.volatiles['lockedmove']) delete pokemon.volatiles['lockedmove']; }, - // Modded to allow arrays as Mega Stone options - canMegaEvo(pokemon) { - const altForme = pokemon.baseSpecies.otherFormes && this.dex.getSpecies(pokemon.baseSpecies.otherFormes[0]); - const item = pokemon.getItem(); - if ( - altForme?.isMega && altForme?.requiredMove && - pokemon.baseMoves.includes(this.toID(altForme.requiredMove)) && !item.zMove - ) { - return altForme.name; - } - if ( - item.megaEvolves !== pokemon.baseSpecies.name || - (Array.isArray(item.megaStone) && item.megaStone.includes(pokemon.species.name)) || - (typeof item.megaStone === 'string' && item.megaStone === pokemon.species.name) - ) { - return null; - } - if (Array.isArray(item.megaStone)) { - return item.megaStone[this.random(item.megaStone.length)]; - } - return item.megaStone; - }, - // Modded to allow unlimited mega evos - runMegaEvo(pokemon) { - const speciesid = pokemon.canMegaEvo || pokemon.canUltraBurst; - if (!speciesid) return false; - const side = pokemon.side; - // Pokémon affected by Sky Drop cannot mega evolve. Enforce it here for now. - for (const foeActive of side.foe.active) { - if (foeActive.volatiles['skydrop'] && foeActive.volatiles['skydrop'].source === pokemon) { + // For Jett's The Hunt is On! + useMoveInner(moveOrMoveName, pokemon, target, sourceEffect, zMove, maxMove) { + if (!sourceEffect && this.effect.id) sourceEffect = this.effect; + if (sourceEffect && ['instruct', 'custapberry'].includes(sourceEffect.id)) sourceEffect = null; + + let move = this.dex.getActiveMove(moveOrMoveName); + if (move.id === 'weatherball' && zMove) { + // Z-Weather Ball only changes types if it's used directly, + // not if it's called by Z-Sleep Talk or something. + this.singleEvent('ModifyType', move, null, pokemon, target, move, move); + if (move.type !== 'Normal') sourceEffect = move; + } + if (zMove || (move.category !== 'Status' && sourceEffect && (sourceEffect as ActiveMove).isZ)) { + move = this.getActiveZMove(move, pokemon); + } + if (maxMove && move.category !== 'Status') { + // Max move outcome is dependent on the move type after type modifications from ability and the move itself + this.singleEvent('ModifyType', move, null, pokemon, target, move, move); + this.runEvent('ModifyType', pokemon, target, move, move); + } + if (maxMove || (move.category !== 'Status' && sourceEffect && (sourceEffect as ActiveMove).isMax)) { + move = this.getActiveMaxMove(move, pokemon); + } + + if (this.activeMove) { + move.priority = this.activeMove.priority; + if (!move.hasBounced) move.pranksterBoosted = this.activeMove.pranksterBoosted; + } + const baseTarget = move.target; + if (target === undefined) target = this.getRandomTarget(pokemon, move); + if (move.target === 'self' || move.target === 'allies') { + target = pokemon; + } + if (sourceEffect) { + move.sourceEffect = sourceEffect.id; + move.ignoreAbility = false; + } + let moveResult = false; + + this.setActiveMove(move, pokemon, target); + + this.singleEvent('ModifyType', move, null, pokemon, target, move, move); + this.singleEvent('ModifyMove', move, null, pokemon, target, move, move); + if (baseTarget !== move.target) { + // Target changed in ModifyMove, so we must adjust it here + // Adjust before the next event so the correct target is passed to the + // event + target = this.getRandomTarget(pokemon, move); + } + move = this.runEvent('ModifyType', pokemon, target, move, move); + move = this.runEvent('ModifyMove', pokemon, target, move, move); + if (baseTarget !== move.target) { + // Adjust again + target = this.getRandomTarget(pokemon, move); + } + if (!move || pokemon.fainted) { + return false; + } + + let attrs = ''; + + let movename = move.name; + if (move.id === 'hiddenpower') movename = 'Hidden Power'; + if (sourceEffect) attrs += '|[from]' + this.dex.getEffect(sourceEffect); + if (zMove && move.isZ === true) { + attrs = '|[anim]' + movename + attrs; + movename = 'Z-' + movename; + } + this.addMove('move', pokemon, movename, target + attrs); + + if (zMove) this.runZPower(move, pokemon); + + if (!target) { + this.attrLastMove('[notarget]'); + this.add(this.gen >= 5 ? '-fail' : '-notarget', pokemon); + return false; + } + + const {targets, pressureTargets} = pokemon.getMoveTargets(move, target); + if (targets.length) { + target = targets[targets.length - 1]; // in case of redirection + } + + if (!sourceEffect || sourceEffect.id === 'pursuit' || sourceEffect.id === 'thehuntison') { + let extraPP = 0; + for (const source of pressureTargets) { + const ppDrop = this.runEvent('DeductPP', source, pokemon, move); + if (ppDrop !== true) { + extraPP += ppDrop || 0; + } + } + if (extraPP > 0) { + pokemon.deductPP(move, extraPP); + } + } + + if (!this.singleEvent('TryMove', move, null, pokemon, target, move) || + !this.runEvent('TryMove', pokemon, target, move)) { + move.mindBlownRecoil = false; + return false; + } + + this.singleEvent('UseMoveMessage', move, null, pokemon, target, move); + + if (move.ignoreImmunity === undefined) { + move.ignoreImmunity = (move.category === 'Status'); + } + + if (this.gen !== 4 && move.selfdestruct === 'always') { + this.faint(pokemon, pokemon, move); + } + + let damage: number | false | undefined | '' = false; + if (move.target === 'all' || move.target === 'foeSide' || move.target === 'allySide' || move.target === 'allyTeam') { + damage = this.tryMoveHit(target, pokemon, move); + if (damage === this.NOT_FAIL) pokemon.moveThisTurnResult = null; + if (damage || damage === 0 || damage === undefined) moveResult = true; + } else { + if (!targets.length) { + this.attrLastMove('[notarget]'); + this.add(this.gen >= 5 ? '-fail' : '-notarget', pokemon); + return false; + } + if (this.gen === 4 && move.selfdestruct === 'always') { + this.faint(pokemon, pokemon, move); + } + moveResult = this.trySpreadMoveHit(targets, pokemon, move); + } + if (move.selfBoost && moveResult) this.moveHit(pokemon, pokemon, move, move.selfBoost, false, true); + if (!pokemon.hp) { + this.faint(pokemon, pokemon, move); + } + + if (!moveResult) { + this.singleEvent('MoveFail', move, null, target, pokemon, move); + return false; + } + + if ( + !move.negateSecondary && + !(move.hasSheerForce && pokemon.hasAbility(['sheerforce', 'aquilasblessing'])) && + !this.getAllActive().some(x => x.hasAbility('skilldrain')) + ) { + const originalHp = pokemon.hp; + this.singleEvent('AfterMoveSecondarySelf', move, null, pokemon, target, move); + this.runEvent('AfterMoveSecondarySelf', pokemon, target, move); + if (pokemon && pokemon !== target && move && move.category !== 'Status') { + if (pokemon.hp <= pokemon.maxhp / 2 && originalHp > pokemon.maxhp / 2) { + this.runEvent('EmergencyExit', pokemon, pokemon); + } + } + } + + if ((move.forceSwitch || move.selfSwitch) && this.getAllActive().some(x => x.hasAbility('skilldrain'))) { + this.hint(`Self-switching and force switch moves don't trigger when a Pokemon with Skill Drain is active.`); + } + + return true; + }, + afterMoveSecondaryEvent(targets, pokemon, move) { + // console.log(`${targets}, ${pokemon}, ${move}`) + if ( + !move.negateSecondary && + !(move.hasSheerForce && pokemon.hasAbility(['sheerforce', 'aquilasblessing'])) && + !this.getAllActive().some(x => x.hasAbility('skilldrain')) + ) { + this.singleEvent('AfterMoveSecondary', move, null, targets[0], pokemon, move); + this.runEvent('AfterMoveSecondary', targets, pokemon, move); + } + return undefined; + }, + hitStepMoveHitLoop(targets, pokemon, move) { // Temporary name + const damage: (number | boolean | undefined)[] = []; + for (const i of targets.keys()) { + damage[i] = 0; + } + move.totalDamage = 0; + pokemon.lastDamage = 0; + let targetHits = move.multihit || 1; + if (Array.isArray(targetHits)) { + // yes, it's hardcoded... meh + if (targetHits[0] === 2 && targetHits[1] === 5) { + if (this.gen >= 5) { + targetHits = this.sample([2, 2, 3, 3, 4, 5]); + } else { + targetHits = this.sample([2, 2, 2, 3, 3, 3, 4, 5]); + } + } else { + targetHits = this.random(targetHits[0], targetHits[1] + 1); + } + } + targetHits = Math.floor(targetHits); + let nullDamage = true; + let moveDamage: (number | boolean | undefined)[]; + // There is no need to recursively check the ´sleepUsable´ flag as Sleep Talk can only be used while asleep. + const isSleepUsable = move.sleepUsable || this.dex.getMove(move.sourceEffect).sleepUsable; + + let targetsCopy: (Pokemon | false | null)[] = targets.slice(0); + let hit: number; + for (hit = 1; hit <= targetHits; hit++) { + if (damage.includes(false)) break; + if (hit > 1 && pokemon.status === 'slp' && !isSleepUsable) break; + if (targets.every(target => !target || !target.hp)) break; + move.hit = hit; + if (move.smartTarget && targets.length > 1) { + targetsCopy = [targets[hit - 1]]; + } else { + targetsCopy = targets.slice(0); + } + const target = targetsCopy[0]; // some relevant-to-single-target-moves-only things are hardcoded + if (target && typeof move.smartTarget === 'boolean') { + if (hit > 1) { + this.addMove('-anim', pokemon, move.name, target); + } else { + this.retargetLastMove(target); + } + } + + // like this (Triple Kick) + if (target && move.multiaccuracy && hit > 1) { + let accuracy = move.accuracy; + const boostTable = [1, 4 / 3, 5 / 3, 2, 7 / 3, 8 / 3, 3]; + if (accuracy !== true) { + if (!move.ignoreAccuracy) { + const boosts = this.runEvent('ModifyBoost', pokemon, null, null, {...pokemon.boosts}); + const boost = this.clampIntRange(boosts['accuracy'], -6, 6); + if (boost > 0) { + accuracy *= boostTable[boost]; + } else { + accuracy /= boostTable[-boost]; + } + } + if (!move.ignoreEvasion) { + const boosts = this.runEvent('ModifyBoost', target, null, null, {...target.boosts}); + const boost = this.clampIntRange(boosts['evasion'], -6, 6); + if (boost > 0) { + accuracy /= boostTable[boost]; + } else if (boost < 0) { + accuracy *= boostTable[-boost]; + } + } + } + accuracy = this.runEvent('ModifyAccuracy', target, pokemon, move, accuracy); + if (!move.alwaysHit) { + accuracy = this.runEvent('Accuracy', target, pokemon, move, accuracy); + if (accuracy !== true && !this.randomChance(accuracy, 100)) break; + } + } + + const moveData = move; + if (!moveData.flags) moveData.flags = {}; + + // Modifies targetsCopy (which is why it's a copy) + [moveDamage, targetsCopy] = this.spreadMoveHit(targetsCopy, pokemon, move, moveData); + + if (!moveDamage.some(val => val !== false)) break; + nullDamage = false; + + for (const [i, md] of moveDamage.entries()) { + // Damage from each hit is individually counted for the + // purposes of Counter, Metal Burst, and Mirror Coat. + damage[i] = md === true || !md ? 0 : md; + // Total damage dealt is accumulated for the purposes of recoil (Parental Bond). + // @ts-ignore + move.totalDamage += damage[i]; + } + if (move.mindBlownRecoil) { + this.damage(Math.round(pokemon.maxhp / 2), pokemon, pokemon, this.dex.getEffect('Mind Blown'), true); + move.mindBlownRecoil = false; + } + this.eachEvent('Update'); + if (!pokemon.hp && targets.length === 1) { + hit++; // report the correct number of hits for multihit moves + break; + } + } + // hit is 1 higher than the actual hit count + if (hit === 1) return damage.fill(false); + if (nullDamage) damage.fill(false); + if (move.multihit && typeof move.smartTarget !== 'boolean') { + this.add('-hitcount', targets[0], hit - 1); + } + + if (move.recoil && move.totalDamage) { + this.damage(this.calcRecoilDamage(move.totalDamage, move), pokemon, pokemon, 'recoil'); + } + + if (move.struggleRecoil) { + let recoilDamage; + if (this.dex.gen >= 5) { + recoilDamage = this.clampIntRange(Math.round(pokemon.baseMaxhp / 4), 1); + } else { + recoilDamage = this.trunc(pokemon.maxhp / 4); + } + this.directDamage(recoilDamage, pokemon, pokemon, {id: 'strugglerecoil'} as Condition); + } + + // smartTarget messes up targetsCopy, but smartTarget should in theory ensure that targets will never fail, anyway + if (move.smartTarget) targetsCopy = targets.slice(0); + + for (const [i, target] of targetsCopy.entries()) { + if (target && pokemon !== target) { + target.gotAttacked(move, damage[i] as number | false | undefined, pokemon); + } + } + + if (move.ohko && !targets[0].hp) this.add('-ohko'); + + if (!damage.some(val => !!val || val === 0)) return damage; + + this.eachEvent('Update'); + + this.afterMoveSecondaryEvent(targetsCopy.filter(val => !!val) as Pokemon[], pokemon, move); + + if ( + !move.negateSecondary && + !(move.hasSheerForce && pokemon.hasAbility(['sheerforce', 'aquilasblessing'])) && + !this.getAllActive().some(x => x.hasAbility('skilldrain')) + ) { + for (const [i, d] of damage.entries()) { + // There are no multihit spread moves, so it's safe to use move.totalDamage for multihit moves + // The previous check was for `move.multihit`, but that fails for Dragon Darts + const curDamage = targets.length === 1 ? move.totalDamage : d; + if (typeof curDamage === 'number' && targets[i].hp) { + if (targets[i].hp <= targets[i].maxhp / 2 && targets[i].hp + curDamage > targets[i].maxhp / 2) { + this.runEvent('EmergencyExit', targets[i], pokemon); + } + } + } + } + + return damage; + }, + + // For Spandan's custom move and Brandon's ability + getDamage(pokemon, target, move, suppressMessages = false) { + if (typeof move === 'string') move = this.dex.getActiveMove(move); + + if (typeof move === 'number') { + const basePower = move; + move = new Dex.Move({ + basePower, + type: '???', + category: 'Physical', + willCrit: false, + }) as unknown as ActiveMove; + move.hit = 0; + } + + if (!move.ignoreImmunity || (move.ignoreImmunity !== true && !move.ignoreImmunity[move.type])) { + if (!target.runImmunity(move.type, !suppressMessages)) { return false; } } - pokemon.formeChange(speciesid, pokemon.getItem(), true); - - // Limit mega evolution to once-per-Pokemon - pokemon.canMegaEvo = null; - - this.runEvent('AfterMega', pokemon); - - // E4 flint gains fire type when mega evolving - if (pokemon.name === 'E4 Flint' && !pokemon.illusion) this.add('-start', pokemon, 'typeadd', 'Fire'); - // Overneat gains the fairy type when mega evolving - if (pokemon.name === 'Overneat' && !pokemon.illusion) this.add('-start', pokemon, 'typeadd', 'Fairy'); - - return true; - }, - getZMove(move, pokemon, skipChecks) { - const item = pokemon.getItem(); - if (!skipChecks) { - if (!item.zMove) return; - if (item.itemUser && !item.itemUser.includes(pokemon.species.name)) return; - const moveData = pokemon.getMoveData(move); - // Draining the PP of the base move prevents the corresponding Z-move from being used. - if (!moveData || !moveData.pp) return; + if (move.ohko) return target.maxhp; + if (move.damageCallback) return move.damageCallback.call(this, pokemon, target); + if (move.damage === 'level') { + return pokemon.level; + } else if (move.damage) { + return move.damage; } - if (item.zMoveFrom) { - if (Array.isArray(item.zMoveFrom)) { - if (item.zMoveFrom.includes(move.name)) return item.zMove as string; + const category = this.getCategory(move); + const defensiveCategory = move.defensiveCategory || category; + + let basePower: number | false | null = move.basePower; + if (move.basePowerCallback) { + basePower = move.basePowerCallback.call(this, pokemon, target, move); + } + if (!basePower) return basePower === 0 ? undefined : basePower; + basePower = this.clampIntRange(basePower, 1); + + let critMult; + let critRatio = this.runEvent('ModifyCritRatio', pokemon, target, move, move.critRatio || 0); + if (this.gen <= 5) { + critRatio = this.clampIntRange(critRatio, 0, 5); + critMult = [0, 16, 8, 4, 3, 2]; + } else { + critRatio = this.clampIntRange(critRatio, 0, 4); + if (this.gen === 6) { + critMult = [0, 16, 8, 2, 1]; } else { - if (move.name === item.zMoveFrom) return item.zMove as string; + critMult = [0, 24, 8, 2, 1]; } - } else if (item.zMove === true) { - if (move.type === item.zMoveType) { - if (move.category === "Status") { - return move.name; - } else if (move.zMove?.basePower) { - return this.zMoveTable[move.type]; + } + + const moveHit = target.getMoveHitData(move); + moveHit.crit = move.willCrit || false; + if (move.willCrit === undefined) { + if (critRatio) { + moveHit.crit = this.randomChance(1, critMult[critRatio]); + } + } + + if (moveHit.crit) { + moveHit.crit = this.runEvent('CriticalHit', target, null, move); + } + + // happens after crit calculation + basePower = this.runEvent('BasePower', pokemon, target, move, basePower, true); + + if (!basePower) return 0; + basePower = this.clampIntRange(basePower, 1); + + const level = pokemon.level; + + const attacker = pokemon; + const defender = target; + let attackStat: StatNameExceptHP = category === 'Physical' ? 'atk' : 'spa'; + const defenseStat: StatNameExceptHP = defensiveCategory === 'Physical' ? 'def' : 'spd'; + if (this.field.isTerrain('baneterrain')) { + if (attacker.getStat('atk') > attacker.getStat('spa')) { + attackStat = 'spa'; + } else { + attackStat = 'atk'; + } + } + if (move.useSourceDefensiveAsOffensive) { + attackStat = defenseStat; + // Body press really wants to use the def stat, + // so it switches stats to compensate for Wonder Room. + // Of course, the game thus miscalculates the boosts... + if ('wonderroom' in this.field.pseudoWeather) { + if (attackStat === 'def') { + attackStat = 'spd'; + } else if (attackStat === 'spd') { + attackStat = 'def'; + } + if (attacker.boosts['def'] || attacker.boosts['spd']) { + this.hint("Body Press uses Sp. Def boosts when Wonder Room is active."); } } } - }, - getActiveZMove(move, pokemon) { - let zMove; - if (pokemon) { - const item = pokemon.getItem(); - const zMoveFrom = Array.isArray(item.zMoveFrom) ? item.zMoveFrom : item.zMoveFrom ? [item.zMoveFrom] : null; - if (zMoveFrom?.includes(move.name)) { - zMove = this.dex.getActiveMove(item.zMove as string); - // Hack for Snaquaza's Z move - zMove.baseMove = move.id; - zMove.isZOrMaxPowered = true; - return zMove; - } + + const statTable = {atk: 'Atk', def: 'Def', spa: 'SpA', spd: 'SpD', spe: 'Spe'}; + let attack; + let defense; + + let atkBoosts = move.useTargetOffensive ? defender.boosts[attackStat] : attacker.boosts[attackStat]; + if (move.id === 'imtoxicyoureslippinunder') atkBoosts = defender.boosts['spd']; + let defBoosts = defender.boosts[defenseStat]; + + let ignoreNegativeOffensive = !!move.ignoreNegativeOffensive; + let ignorePositiveDefensive = !!move.ignorePositiveDefensive; + + if (moveHit.crit) { + ignoreNegativeOffensive = true; + ignorePositiveDefensive = true; + } + const ignoreOffensive = !!(move.ignoreOffensive || (ignoreNegativeOffensive && atkBoosts < 0)); + const ignoreDefensive = !!(move.ignoreDefensive || (ignorePositiveDefensive && defBoosts > 0)); + + if (ignoreOffensive) { + this.debug('Negating (sp)atk boost/penalty.'); + atkBoosts = 0; + } + if (ignoreDefensive) { + this.debug('Negating (sp)def boost/penalty.'); + defBoosts = 0; } - if (move.category === 'Status') { - zMove = this.dex.getActiveMove(move); - zMove.isZ = true; - zMove.isZOrMaxPowered = true; - return zMove; - } - zMove = this.dex.getActiveMove(this.zMoveTable[move.type]); - zMove.basePower = move.zMove!.basePower!; - zMove.category = move.category; - zMove.isZOrMaxPowered = true; - return zMove; - }, - // Modded to allow each Pokemon on a team to use a Z move once per battle - canZMove(pokemon) { - if ( - pokemon.m?.zMoveUsed || - (pokemon.transformed && (pokemon.species.isMega || pokemon.species.isPrimal || pokemon.species.forme === "Ultra")) - ) { - return; - } - const item = pokemon.getItem(); - if (!item.zMove) return; - if (item.itemUser && !item.itemUser.includes(pokemon.species.name)) return; - let atLeastOne = false; - let mustStruggle = true; - const zMoves: ZMoveOptions = []; - for (const moveSlot of pokemon.moveSlots) { - if (moveSlot.pp <= 0) { - zMoves.push(null); - continue; - } - if (!moveSlot.disabled) { - mustStruggle = false; - } - const move = this.dex.getMove(moveSlot.move); - let zMoveName = this.getZMove(move, pokemon, true) || ''; - if (zMoveName) { - const zMove = this.dex.getMove(zMoveName); - if (!zMove.isZ && zMove.category === 'Status') zMoveName = "Z-" + zMoveName; - zMoves.push({move: zMoveName, target: zMove.target}); - } else { - zMoves.push(null); - } - if (zMoveName) atLeastOne = true; - } - if (atLeastOne && !mustStruggle) return zMoves; - }, - runZPower(move, pokemon) { - const zPower = this.dex.getEffect('zpower'); - if (move.category !== 'Status') { - this.attrLastMove('[zeffect]'); - } else if (move.zMove?.boost) { - this.boost(move.zMove.boost, pokemon, pokemon, zPower); + if (move.useTargetOffensive) { + attack = defender.calculateStat(attackStat, atkBoosts); + } else if (move.id === 'imtoxicyoureslippinunder') { + attack = defender.calculateStat("spd", atkBoosts); } else { - switch (move.zMove?.effect) { - case 'heal': - this.heal(pokemon.maxhp, pokemon, pokemon, zPower); - break; - case 'healhalf': - // For DragonWhale - this.heal(pokemon.baseMaxhp / 2, pokemon, pokemon, zPower); - break; - case 'healreplacement': - move.self = {sideCondition: 'healreplacement'}; - break; - case 'boostreplacement': - // For nui - move.self = {sideCondition: 'boostreplacement'}; - break; - case 'clearnegativeboost': - const boosts: SparseBoostsTable = {}; - let i: BoostName; - for (i in pokemon.boosts) { - if (pokemon.boosts[i] < 0) { - boosts[i] = 0; + attack = attacker.calculateStat(attackStat, atkBoosts); + } + + attackStat = (category === 'Physical' ? 'atk' : 'spa'); + defense = defender.calculateStat(defenseStat, defBoosts); + + // Apply Stat Modifiers + attack = this.runEvent('Modify' + statTable[attackStat], attacker, defender, move, attack); + defense = this.runEvent('Modify' + statTable[defenseStat], defender, attacker, move, defense); + + if (this.gen <= 4 && ['explosion', 'selfdestruct'].includes(move.id) && defenseStat === 'def') { + defense = this.clampIntRange(Math.floor(defense / 2), 1); + } + + const tr = this.trunc; + + // int(int(int(2 * L / 5 + 2) * A * P / D) / 50); + const baseDamage = tr(tr(tr(tr(2 * level / 5 + 2) * basePower * attack) / defense) / 50); + + // Calculate damage modifiers separately (order differs between generations) + return this.modifyDamage(baseDamage, pokemon, target, move, suppressMessages); + }, + + runMoveEffects(damage, targets, pokemon, move, moveData, isSecondary, isSelf) { + let didAnything: number | boolean | null | undefined = damage.reduce(this.combineResults); + for (const [i, target] of targets.entries()) { + if (target === false) continue; + let hitResult; + let didSomething: number | boolean | null | undefined = undefined; + + if (target) { + if (moveData.boosts && !target.fainted) { + hitResult = this.boost(moveData.boosts, target, pokemon, move, isSecondary, isSelf); + didSomething = this.combineResults(didSomething, hitResult); + } + if (moveData.heal && !target.fainted) { + if (target.hp >= target.maxhp) { + this.add('-fail', target, 'heal'); + this.attrLastMove('[still]'); + damage[i] = this.combineResults(damage[i], false); + didAnything = this.combineResults(didAnything, null); + continue; + } + const amount = target.baseMaxhp * moveData.heal[0] / moveData.heal[1]; + const d = target.heal((this.gen < 5 ? Math.floor : Math.round)(amount)); + if (!d && d !== 0) { + this.add('-fail', pokemon); + this.attrLastMove('[still]'); + this.debug('heal interrupted'); + damage[i] = this.combineResults(damage[i], false); + didAnything = this.combineResults(didAnything, null); + continue; + } + this.add('-heal', target, target.getHealth); + didSomething = true; + } + if (moveData.status) { + hitResult = target.trySetStatus(moveData.status, pokemon, moveData.ability ? moveData.ability : move); + if (!hitResult && move.status) { + damage[i] = this.combineResults(damage[i], false); + didAnything = this.combineResults(didAnything, null); + continue; + } + didSomething = this.combineResults(didSomething, hitResult); + } + if (moveData.forceStatus) { + hitResult = target.setStatus(moveData.forceStatus, pokemon, move); + didSomething = this.combineResults(didSomething, hitResult); + } + if (moveData.volatileStatus) { + hitResult = target.addVolatile(moveData.volatileStatus, pokemon, move); + didSomething = this.combineResults(didSomething, hitResult); + } + if (moveData.sideCondition) { + hitResult = target.side.addSideCondition(moveData.sideCondition, pokemon, move); + didSomething = this.combineResults(didSomething, hitResult); + } + if (moveData.slotCondition) { + hitResult = target.side.addSlotCondition(target, moveData.slotCondition, pokemon, move); + didSomething = this.combineResults(didSomething, hitResult); + } + if (moveData.weather) { + hitResult = this.field.setWeather(moveData.weather, pokemon, move); + didSomething = this.combineResults(didSomething, hitResult); + } + if (moveData.terrain) { + hitResult = this.field.setTerrain(moveData.terrain, pokemon, move); + didSomething = this.combineResults(didSomething, hitResult); + } + if (moveData.pseudoWeather) { + hitResult = this.field.addPseudoWeather(moveData.pseudoWeather, pokemon, move); + didSomething = this.combineResults(didSomething, hitResult); + } + if (moveData.forceSwitch && !this.getAllActive().some(x => x.hasAbility('skilldrain'))) { + hitResult = !!this.canSwitch(target.side); + didSomething = this.combineResults(didSomething, hitResult); + } + // Hit events + // These are like the TryHit events, except we don't need a FieldHit event. + // Scroll up for the TryHit event documentation, and just ignore the "Try" part. ;) + if (move.target === 'all' && !isSelf) { + if (moveData.onHitField) { + hitResult = this.singleEvent('HitField', moveData, {}, target, pokemon, move); + didSomething = this.combineResults(didSomething, hitResult); + } + } else if ((move.target === 'foeSide' || move.target === 'allySide') && !isSelf) { + if (moveData.onHitSide) { + hitResult = this.singleEvent('HitSide', moveData, {}, target.side, pokemon, move); + didSomething = this.combineResults(didSomething, hitResult); + } + } else { + if (moveData.onHit) { + hitResult = this.singleEvent('Hit', moveData, {}, target, pokemon, move); + didSomething = this.combineResults(didSomething, hitResult); + } + if (!isSelf && !isSecondary) { + this.runEvent('Hit', target, pokemon, move); } } - pokemon.setBoost(boosts); - this.add('-clearnegativeboost', pokemon, '[zeffect]'); - break; - case 'redirect': - pokemon.addVolatile('followme', pokemon, zPower); - break; - case 'crit2': - pokemon.addVolatile('focusenergy', pokemon, zPower); - break; - case 'curse': - if (pokemon.hasType('Ghost')) { - this.heal(pokemon.maxhp, pokemon, pokemon, zPower); + } + if (moveData.selfSwitch && !this.getAllActive().some(x => x.hasAbility('skilldrain'))) { + if (this.canSwitch(pokemon.side)) { + didSomething = true; } else { - this.boost({atk: 1}, pokemon, pokemon, zPower); + didSomething = this.combineResults(didSomething, false); } } + // Move didn't fail because it didn't try to do anything + if (didSomething === undefined) didSomething = true; + damage[i] = this.combineResults(damage[i], didSomething === null ? false : didSomething); + didAnything = this.combineResults(didAnything, didSomething); } + + + if (!didAnything && didAnything !== 0 && !moveData.self && !moveData.selfdestruct) { + if (!isSelf && !isSecondary) { + if (didAnything === false) { + this.add('-fail', pokemon); + this.attrLastMove('[still]'); + } + } + this.debug('move failed because it did nothing'); + } else if (move.selfSwitch && pokemon.hp && !this.getAllActive().some(x => x.hasAbility('skilldrain'))) { + pokemon.switchFlag = move.id; + } + + return damage; }, - // Modded to account for guts clones - modifyDamage(baseDamage, pokemon, target, move, suppressMessages) { - const tr = this.trunc; - if (!move.type) move.type = '???'; - const type = move.type; - baseDamage += 2; - - // multi-target modifier (doubles only) - if (move.spreadHit) { - const spreadModifier = move.spreadModifier || (this.gameType === 'free-for-all' ? 0.5 : 0.75); - this.debug('Spread modifier: ' + spreadModifier); - baseDamage = this.modify(baseDamage, spreadModifier); - } - - // weather modifier - baseDamage = this.runEvent('WeatherModifyDamage', pokemon, target, move, baseDamage); - - // crit - not a modifier - const isCrit = target.getMoveHitData(move).crit; - if (isCrit) { - baseDamage = tr(baseDamage * (move.critModifier || (this.gen >= 6 ? 1.5 : 2))); - } - - // random factor - also not a modifier - baseDamage = this.randomizer(baseDamage); - - // STAB - if (move.forceSTAB || (type !== '???' && pokemon.hasType(type))) { - // The "???" type never gets STAB - // Not even if you Roost in Gen 4 and somehow manage to use - // Struggle in the same turn. - // (On second thought, it might be easier to get a MissingNo.) - baseDamage = this.modify(baseDamage, move.stab || 1.5); - } - // types - let typeMod = target.runEffectiveness(move); - typeMod = this.clampIntRange(typeMod, -6, 6); - target.getMoveHitData(move).typeMod = typeMod; - if (typeMod > 0) { - if (!suppressMessages) this.add('-supereffective', target); - - for (let i = 0; i < typeMod; i++) { - baseDamage *= 2; - } - } - if (typeMod < 0) { - if (!suppressMessages) this.add('-resisted', target); - - for (let i = 0; i > typeMod; i--) { - baseDamage = tr(baseDamage / 2); - } - } - - if (isCrit && !suppressMessages) this.add('-crit', target); - - if ( - pokemon.status === 'brn' && move.category === 'Physical' && !pokemon.hasAbility('guts') && - !pokemon.hasAbility('superguarda') && !pokemon.hasAbility('radioactive') - ) { - if (this.gen < 6 || move.id !== 'facade') { - baseDamage = this.modify(baseDamage, 0.5); - } - } - - // Generation 5, but nothing later, sets damage to 1 before the final damage modifiers - if (this.gen === 5 && !baseDamage) baseDamage = 1; - - // Final modifier. Modifiers that modify damage after min damage check, such as Life Orb. - baseDamage = this.runEvent('ModifyDamage', pokemon, target, move, baseDamage); - - if ((move.isZOrMaxPowered || move.isMax) && target.getMoveHitData(move).zBrokeProtect) { - baseDamage = this.modify(baseDamage, 0.25); - this.add('-zbroken', target); - } - - // Generation 6-7 moves the check for minimum 1 damage after the final modifier... - if (this.gen !== 5 && !baseDamage) return 1; - - // ...but 16-bit truncation happens even later, and can truncate to 0 - return tr(baseDamage, 16); - }, pokemon: { - ignoringAbility() { - const abilities = [ - 'battlebond', 'comatose', 'disguise', 'multitype', 'powerconstruct', 'rkssystem', 'schooling', 'shieldsdown', 'stancechange', - ]; - // Neutralizing Spores modded into ignoringAbility - let sporeEffect = false; - for (const foeActive of this.side.foe.active) { - // foeActive can be null when a pokemon isn't active - if (foeActive?.ability.includes('neutralizingspores') && !foeActive?.volatiles['gastroacid']) sporeEffect = true; - } - for (const allyActive of this.side.active) { - // allyActive can be null when a pokemon isn't active - if (allyActive?.ability.includes('neutralizingspores') && !allyActive?.volatiles['gastroacid']) sporeEffect = true; - } - return !!((this.battle.gen >= 5 && !this.isActive) || - (this.volatiles['gastroacid'] && !abilities.includes(this.ability)) || - (sporeEffect && !this.ability.includes('neutralizingspores'))); - }, - getActionSpeed() { - let speed = this.getStat('spe', false, false); - if ((this.battle.field.getPseudoWeather('trickroom') || this.battle.field.getPseudoWeather('alienwave')) && - !(this.battle.field.getPseudoWeather('trickroom') && this.battle.field.getPseudoWeather('alienwave'))) { - speed = 0x2710 - speed; - } - if (this.battle.field.getPseudoWeather('distortionworld')) { - speed = 0; // Anything times 0 is still 0 - } - return this.battle.trunc(speed, 13); - }, - isGrounded(negateImmunity = false) { + isGrounded(negateImmunity) { if ('gravity' in this.battle.field.pseudoWeather) return true; if ('ingrain' in this.volatiles && this.battle.gen >= 4) return true; if ('smackdown' in this.volatiles) return true; @@ -410,14 +846,14 @@ export const Scripts: ModdedBattleScriptsData = { if (item === 'ironball') return true; // If a Fire/Flying type uses Burn Up and Roost, it becomes ???/Flying-type, but it's still grounded. if (!negateImmunity && this.hasType('Flying') && !('roost' in this.volatiles)) return false; - if (this.hasAbility('levitate') && !this.battle.suppressingAttackEvents()) return null; + if (this.hasAbility(['levitate', 'candlewax']) && !this.battle.suppressingAttackEvents()) return null; // Innate levitate - if ((('tony' in this.volatiles) && !this.illusion) && !this.battle.suppressingAttackEvents()) return null; + if ((('gimmick' in this.volatiles) && !this.illusion) && !this.battle.suppressingAttackEvents()) return null; if ('magnetrise' in this.volatiles) return false; if ('telekinesis' in this.volatiles) return false; return item !== 'airballoon'; }, - setStatus(status, source = null, sourceEffect = null, ignoreImmunities = false) { + setStatus(status, source, sourceEffect, ignoreImmunities) { if (!this.hp) return false; status = this.battle.dex.getEffect(status); if (this.battle.event) { @@ -437,8 +873,8 @@ export const Scripts: ModdedBattleScriptsData = { } if (!ignoreImmunities && status.id && - !(source?.hasAbility('corrosion') && ['tox', 'psn'].includes(status.id)) && - !(sourceEffect?.id === 'corrosivetoxic')) { + !((source?.hasAbility('corrosion') || source?.hasAbility('hackedcorrosion') || sourceEffect?.id === 'cradilychaos') && + ['tox', 'psn'].includes(status.id))) { // the game currently never ignores immunities if (!this.runStatusImmunity(status.id === 'tox' ? 'psn' : status.id)) { this.battle.debug('immune to status'); @@ -479,4 +915,131 @@ export const Scripts: ModdedBattleScriptsData = { return true; }, }, + + // Modded to add a property to work with Struchni's move + nextTurn() { + this.turn++; + this.lastSuccessfulMoveThisTurn = null; + + const trappedBySide: boolean[] = []; + const stalenessBySide: ('internal' | 'external' | undefined)[] = []; + for (const side of this.sides) { + let sideTrapped = true; + let sideStaleness: 'internal' | 'external' | undefined; + for (const pokemon of side.active) { + if (!pokemon) continue; + pokemon.moveThisTurn = ''; + pokemon.newlySwitched = false; + pokemon.moveLastTurnResult = pokemon.moveThisTurnResult; + pokemon.moveThisTurnResult = undefined; + if (this.turn !== 1) { + pokemon.usedItemThisTurn = false; + // Used for Veto + pokemon.m.statsRaisedLastTurn = !!pokemon.statsRaisedThisTurn; + pokemon.statsRaisedThisTurn = false; + pokemon.statsLoweredThisTurn = false; + // It shouldn't be possible in a normal battle for a Pokemon to be damaged before turn 1's move selection + // However, this could be potentially relevant in certain OMs + pokemon.hurtThisTurn = null; + } + + pokemon.maybeDisabled = false; + for (const moveSlot of pokemon.moveSlots) { + moveSlot.disabled = false; + moveSlot.disabledSource = ''; + } + this.runEvent('DisableMove', pokemon); + if (!pokemon.ateBerry) pokemon.disableMove('belch'); + if (!pokemon.getItem().isBerry) pokemon.disableMove('stuffcheeks'); + + // If it was an illusion, it's not any more + if (pokemon.getLastAttackedBy() && this.gen >= 7) pokemon.knownType = true; + + for (let i = pokemon.attackedBy.length - 1; i >= 0; i--) { + const attack = pokemon.attackedBy[i]; + if (attack.source.isActive) { + attack.thisTurn = false; + } else { + pokemon.attackedBy.splice(pokemon.attackedBy.indexOf(attack), 1); + } + } + + if (this.gen >= 7) { + // In Gen 7, the real type of every Pokemon is visible to all players via the bottom screen while making choices + const seenPokemon = pokemon.illusion || pokemon; + const realTypeString = seenPokemon.getTypes(true).join('/'); + if (realTypeString !== seenPokemon.apparentType) { + this.add('-start', pokemon, 'typechange', realTypeString, '[silent]'); + seenPokemon.apparentType = realTypeString; + if (pokemon.addedType) { + // The typechange message removes the added type, so put it back + this.add('-start', pokemon, 'typeadd', pokemon.addedType, '[silent]'); + } + } + } + + pokemon.trapped = pokemon.maybeTrapped = false; + this.runEvent('TrapPokemon', pokemon); + if (!pokemon.knownType || this.dex.getImmunity('trapped', pokemon)) { + this.runEvent('MaybeTrapPokemon', pokemon); + } + // canceling switches would leak information + // if a foe might have a trapping ability + if (this.gen > 2) { + for (const source of pokemon.side.foe.active) { + if (!source || source.fainted) continue; + const species = (source.illusion || source).species; + if (!species.abilities) continue; + for (const abilitySlot in species.abilities) { + const abilityName = species.abilities[abilitySlot as keyof Species['abilities']]; + if (abilityName === source.ability) { + // pokemon event was already run above so we don't need + // to run it again. + continue; + } + const ruleTable = this.ruleTable; + if ((ruleTable.has('+hackmons') || !ruleTable.has('obtainableabilities')) && !this.format.team) { + // hackmons format + continue; + } else if (abilitySlot === 'H' && species.unreleasedHidden) { + // unreleased hidden ability + continue; + } + const ability = this.dex.getAbility(abilityName); + if (ruleTable.has('-ability:' + ability.id)) continue; + if (pokemon.knownType && !this.dex.getImmunity('trapped', pokemon)) continue; + this.singleEvent('FoeMaybeTrapPokemon', ability, {}, pokemon, source); + } + } + } + + if (pokemon.fainted) continue; + + sideTrapped = sideTrapped && pokemon.trapped; + const staleness = pokemon.volatileStaleness || pokemon.staleness; + if (staleness) sideStaleness = sideStaleness === 'external' ? sideStaleness : staleness; + pokemon.activeTurns++; + } + trappedBySide.push(sideTrapped); + stalenessBySide.push(sideStaleness); + side.faintedLastTurn = side.faintedThisTurn; + side.faintedThisTurn = null; + } + + if (this.maybeTriggerEndlessBattleClause(trappedBySide, stalenessBySide)) return; + + if (this.gameType === 'triples' && !this.sides.filter(side => side.pokemonLeft > 1).length) { + // If both sides have one Pokemon left in triples and they are not adjacent, they are both moved to the center. + const actives = this.getAllActive(); + if (actives.length > 1 && !this.isAdjacent(actives[0], actives[1])) { + this.swapPosition(actives[0], 1, '[silent]'); + this.swapPosition(actives[1], 1, '[silent]'); + this.add('-center'); + } + } + + this.add('turn', this.turn); + + this.makeRequest('move'); + }, }; diff --git a/server/chat-plugins/random-battles.ts b/server/chat-plugins/random-battles.ts index 0d77a23547..3abf4dc799 100644 --- a/server/chat-plugins/random-battles.ts +++ b/server/chat-plugins/random-battles.ts @@ -4,11 +4,17 @@ */ import {FS} from "../../lib/fs"; +import {SSBSet, ssbSets} from "../../data/mods/ssb/random-teams"; +import {Utils} from "../../lib/utils"; const GEN_NAMES: {[k: string]: string} = { gen1: '[Gen 1]', gen2: '[Gen 2]', gen3: '[Gen 3]', gen4: '[Gen 4]', gen5: '[Gen 5]', gen6: '[Gen 6]', gen7: '[Gen 7]', }; +const STAT_NAMES: {[k: string]: string} = { + hp: "HP", atk: "Atk", def: "Def", spa: "SpA", spd: "SpD", spe: "Spe", +}; + const TIERS: {[k: string]: string} = { uber: "Uber", ubers: "Uber", ou: "OU", uu: "UU", ru: "RU", nu: "NU", pu: "PU", @@ -95,9 +101,6 @@ function battleFactorySets(species: string | Species, tier: string | null, gen = ); if (!Object.keys(statsFile).length) return null; let buf = ``; - const statNames: {[k: string]: string} = { - hp: "HP", atk: "Atk", def: "Def", spa: "SpA", spd: "SpD", spe: "Spe", - }; if (!isBSS) { if (!tier) return {e: `Please provide a valid tier.`}; if (!(toID(tier) in TIERS)) return {e: `That tier isn't supported.`}; @@ -123,7 +126,7 @@ function battleFactorySets(species: string | Species, tier: string | null, gen = let ev: string; for (ev in set.evs) { if (set.evs[ev] === 0) continue; - evs.push(`${set.evs[ev]} ${statNames[ev]}`); + evs.push(`${set.evs[ev]} ${STAT_NAMES[ev]}`); } buf += `${evs.join(" / ")}`; } @@ -134,7 +137,7 @@ function battleFactorySets(species: string | Species, tier: string | null, gen = let iv: string; for (iv in set.ivs) { if (set.ivs[iv] === 31) continue; - ivs.push(`${set.ivs[iv]} ${statNames[iv]}`); + ivs.push(`${set.ivs[iv]} ${STAT_NAMES[iv]}`); } buf += `${ivs.join(" / ")}`; } @@ -163,7 +166,7 @@ function battleFactorySets(species: string | Species, tier: string | null, gen = let ev: string; for (ev in set.evs) { if (set.evs[ev] === 0) continue; - evs.push(`${set.evs[ev]} ${statNames[ev]}`); + evs.push(`${set.evs[ev]} ${STAT_NAMES[ev]}`); } buf += `${evs.join(" / ")}`; } @@ -174,7 +177,7 @@ function battleFactorySets(species: string | Species, tier: string | null, gen = let iv: string; for (iv in set.ivs) { if (set.ivs[iv] === 31) continue; - ivs.push(`${set.ivs[iv]} ${statNames[iv]}`); + ivs.push(`${set.ivs[iv]} ${STAT_NAMES[iv]}`); } buf += `${ivs.join(" / ")}`; } @@ -204,9 +207,6 @@ function CAP1v1Sets(species: string | Species) { return {e: `${species.name} doesn't have any sets in [Gen 8] CAP 1v1.`}; } let buf = `Sets for ${species.name} in [Gen 8] CAP 1v1:
`; - const statNames: {[k: string]: string} = { - hp: "HP", atk: "Atk", def: "Def", spa: "SpA", spd: "SpD", spe: "Spe", - }; for (const [i, set] of statsFile[species.name].entries()) { buf += `
Set ${i + 1}`; buf += `